Subject: | Negative values for SQL_INTEGER rejected by bind_param() |
Hi DBD::mysql maintainers,
the latest DBD::mysql on CPAN, 3.0002_5, has the following problem:
If you specify a negative integer on a column marked as SQL_INTEGER, as in
$sth->bind_param(1,undef,SQL_INTEGER);
$sth->execute(-1) or die $DBI::errstr;
then the integer doesn't make it into the resulting SQL, resulting in a
syntax error. It works fine for positive integers. Negative integers
used to work in the 2.9008 series, and stopped working in 3.2.x.
Reason is that dbdimp.c only accepts digits as numbers ignoring minus
signs (-) and floating points (.).
I've attached a patch that puts the number parser, employing some of the
logic of a previous patch by dragonchild, into a subroutine.
I've also added a test case.
Would be great if you could apply it. Thanks!
-- Mike Schilli
Subject: | patch.txt |
diff -Naur DBD-mysql-3.0002_5/dbdimp.c DBD-mysql-3.0002_5.patched/dbdimp.c
--- DBD-mysql-3.0002_5/dbdimp.c Wed Feb 1 14:49:02 2006
+++ DBD-mysql-3.0002_5.patched/dbdimp.c Fri Apr 28 13:30:49 2006
@@ -418,9 +418,10 @@
bool bind_type_guessing)
{
- bool seen_neg, seen_dec;
+ int rc;
char *salloc, *statement_ptr;
char *statement_ptr_end, testchar, *ptr, *valbuf;
+ char *cp, *end;
int alen, i, j;
int slen= *slen_ptr;
int limit_flag= 0;
@@ -467,40 +468,9 @@
valbuf= SvPV(ph->value, vallen);
ph->type= SQL_INTEGER;
- /* patch from Dragonchild */
- seen_neg= 0;
- seen_dec= 0;
- for (j= 0; j < (int)vallen; ++j)
+ if(parse_number(valbuf, vallen, &end) != 0)
{
- testchar= *(valbuf+j);
- if ('-' == testchar)
- {
- if (seen_neg)
- {
- ph->type= SQL_VARCHAR;
- break;
- }
- else if (j)
- {
- ph->type= SQL_VARCHAR;
- break;
- }
- seen_neg= 1;
- }
- else if ('.' == testchar)
- {
- if (seen_dec)
- {
- ph->type= SQL_VARCHAR;
- break;
- }
- seen_dec= 1;
- }
- else if (!isdigit(testchar))
- {
ph->type= SQL_VARCHAR;
- break;
- }
}
}
else if (bind_type_guessing)
@@ -616,13 +586,9 @@
}
else
{
- while (vallen--)
- {
- c = *valbuf++;
- if ((c < '0' || c > '9') && c != ' ')
- break;
- *ptr++= c;
- }
+ parse_number(valbuf, vallen, &end);
+ for(cp = valbuf; cp < end; cp++)
+ *ptr++ = *cp;
}
}
}
@@ -4277,3 +4243,61 @@
return sv_2mortal(my_ulonglong2str(mysql_insert_id(&((imp_dbh_t*)imp_dbh)->mysql)));
}
#endif
+
+int parse_number(
+ char *string,
+ STRLEN len,
+ char **end
+) {
+ int seen_neg;
+ int seen_dec;
+ char *cp;
+
+ seen_neg= 0;
+ seen_dec= 0;
+
+ if(len <= 0) {
+ len = strlen(string);
+ }
+
+ cp = string;
+
+ for(cp = string; *cp; cp++)
+ {
+ if ('-' == *cp)
+ {
+ if (seen_neg)
+ {
+ /* second '-' */
+ break;
+ }
+ else if (cp > string)
+ {
+ /* '-' after digit(s) */
+ break;
+ }
+ seen_neg= 1;
+ }
+ else if ('.' == *cp)
+ {
+ if (seen_dec)
+ {
+ /* second '.' */
+ break;
+ }
+ seen_dec= 1;
+ }
+ else if (!isdigit(*cp))
+ {
+ break;
+ }
+ }
+
+ *end = cp;
+
+ if(cp - string < len) {
+ return -1;
+ }
+
+ return 0;
+}
diff -Naur DBD-mysql-3.0002_5/t/40bindparam.t DBD-mysql-3.0002_5.patched/t/40bindparam.t
--- DBD-mysql-3.0002_5/t/40bindparam.t Wed Feb 1 10:52:23 2006
+++ DBD-mysql-3.0002_5.patched/t/40bindparam.t Fri Apr 28 13:32:50 2006
@@ -130,6 +130,14 @@
or DbiError($dbh->err, $dbh->errstr);
}
+ # Testing bugfix for negative integers
+ Test($state or $cursor->bind_param(1, undef, SQL_INTEGER()))
+ or DbiError($dbh->err, $dbh->errstr);
+ Test($state or $cursor->bind_param(2, undef))
+ or DbiError($dbh->err, $dbh->errstr);
+ Test($state or $cursor->execute(-1, "abc"))
+ or DbiError($dbh->err, $dbh->errstr);
+
Test($state or undef $cursor || 1);
#
@@ -144,6 +152,11 @@
Test($state or $cursor->bind_columns(undef, \$id, \$name))
or DbiError($dbh->err, $dbh->errstr);
+
+ Test($state or ($ref = $cursor->fetch) && $id == -1 &&
+ $name eq 'abc')
+ or printf("Query returned id = %s, name = %s, ref = %s, %d\n",
+ $id, $name, $ref, scalar(@$ref));
Test($state or ($ref = $cursor->fetch) && $id == 1 &&
$name eq 'Alligator Descartes')