Skip to content

Commit 43be0c2

Browse files
CaptTofudveeden
authored andcommitted
Fix for issue 251. Also fixed case of "e<single int>"
1 parent 9a1500f commit 43be0c2

File tree

5 files changed

+182
-26
lines changed

5 files changed

+182
-26
lines changed

Changes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ DBI/DBD community (4.049)
4141
DBI/DBD community (4.048)
4242
* Fix corrupted META.json so cpan installations work as expected.
4343
https://github.com/perl5-dbi/DBD-mysql/issues/263
44+
* Fix issue 251: unable to insert "." with bind_type_guessing on
4445

4546
2018-09-08 Daniël van Eeden, Patrick Galbraith, DBI/DBD community (4.047)
4647
* Add options needed for public key based security.

dbdimp.c

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4824,14 +4824,30 @@ int mysql_db_async_ready(SV* h)
48244824
}
48254825
}
48264826

4827+
/*
4828+
* this funtion tries to parse numbers from the passed string value
4829+
* for emulated prepared statements being bound
4830+
* * finds the beginning and end address of the string
4831+
* * loops through the string
4832+
* * skips past any spaces
4833+
* * if it sees a -, ., +, or e, sets a flag
4834+
* * if all subsequent values are numbers, is a digit
4835+
* * otherwise, a string
4836+
* * if any character that's not a digit is found, it's a string
4837+
* * if only numbers are found, it's a number
4838+
* * `end` is set to the address of the last member in the iteration
4839+
* and used in knowing where the value is when building the SQL
4840+
* statement
4841+
*
4842+
*/
48274843
static int parse_number(char *string, STRLEN len, char **end)
48284844
{
48294845
int seen_neg;
48304846
int seen_dec;
48314847
int seen_e;
48324848
int seen_plus;
48334849
int seen_digit;
4834-
char *cp;
4850+
char *cp,*str_end;
48354851

48364852
seen_neg= seen_dec= seen_e= seen_plus= seen_digit= 0;
48374853

@@ -4840,14 +4856,15 @@ static int parse_number(char *string, STRLEN len, char **end)
48404856
}
48414857

48424858
cp= string;
4859+
str_end= string + len-1;
48434860

48444861
/* Skip leading whitespace */
48454862
while (*cp && isspace(*cp))
48464863
cp++;
48474864

48484865
for ( ; *cp; cp++)
48494866
{
4850-
if ('-' == *cp)
4867+
if (*cp == '-')
48514868
{
48524869
if (seen_neg >= 2)
48534870
{
@@ -4858,27 +4875,35 @@ static int parse_number(char *string, STRLEN len, char **end)
48584875
}
48594876
seen_neg += 1;
48604877
}
4861-
else if ('.' == *cp)
4878+
else if (*cp == '.')
48624879
{
4863-
if (seen_dec)
4880+
if (seen_dec || cp == str_end)
48644881
{
48654882
/* second '.' */
48664883
break;
48674884
}
48684885
seen_dec= 1;
48694886
}
4870-
else if ('e' == *cp)
4887+
else if (*cp == 'e')
48714888
{
4872-
if (seen_e)
4889+
/*
4890+
* this is the case where the value for example, e2,
4891+
* which should be a varchar
4892+
*/
4893+
if (cp == str_end -1)
4894+
{
4895+
break;
4896+
}
4897+
if (seen_e || cp == str_end)
48734898
{
48744899
/* second 'e' */
48754900
break;
48764901
}
48774902
seen_e= 1;
48784903
}
4879-
else if ('+' == *cp)
4904+
else if (*cp == '+')
48804905
{
4881-
if (seen_plus)
4906+
if (seen_plus || cp == str_end)
48824907
{
48834908
/* second '+' */
48844909
break;
@@ -4891,6 +4916,7 @@ static int parse_number(char *string, STRLEN len, char **end)
48914916
/* seen_digit= 1; */
48924917
break;
48934918
}
4919+
/*printf("else: cp is a digit: %d\n", *cp); */
48944920
}
48954921

48964922
*end= cp;

dbdimp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
/*
1818
* Header files we use
1919
*/
20+
#include <ctype.h>
2021
#include <DBIXS.h> /* installed by the DBI module */
2122
#include <mysql.h> /* Comes with MySQL-devel */
2223
#include <mysqld_error.h> /* Comes MySQL */

t/35limit.t

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ require 'lib.pl';
1515

1616
my $dbh;
1717
eval {$dbh= DBI->connect($test_dsn, $test_user, $test_password,
18-
{ RaiseError => 1, PrintError => 1, AutoCommit => 0 });};
18+
{ mysql_bind_type_guessing => 1,
19+
RaiseError => 1,
20+
PrintError => 1,
21+
AutoCommit => 1 });};
22+
1923
if ($@) {
2024
plan skip_all => "no database connection";
2125
}

t/51bind_type_guessing.t

Lines changed: 141 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,29 @@ use warnings;
44
use DBI;
55
use DBI::Const::GetInfoType;
66
use Test::More;
7+
select(($|=1,select(STDERR),$|=1)[1]);
78
use lib 't', '.';
89
require 'lib.pl';
910

1011
use vars qw($test_dsn $test_user $test_password);
1112

12-
my $dbh;
13+
my ($dbh, $t);
1314
eval {$dbh= DBI->connect($test_dsn, $test_user, $test_password,
14-
{ RaiseError => 1, PrintError => 1, AutoCommit => 0 });};
15+
{ RaiseError => 1, PrintError => 1, AutoCommit => 1 });};
1516
if ($@) {
1617
plan skip_all => "no database connection";
1718
}
18-
plan tests => 26;
19+
plan tests => 98;
1920

20-
ok $dbh->do("DROP TABLE IF EXISTS dbd_mysql_t51bind_type_guessing"), "drop table if exists dbd_mysql_t51bind_type_guessing";
21+
ok $dbh->do("DROP TABLE IF EXISTS dbd_mysql_t51bind_type_guessing"),
22+
"drop table if exists dbd_mysql_t51bind_type_guessing";
2123

2224
my $create= <<"EOTABLE";
2325
create table dbd_mysql_t51bind_type_guessing (
2426
id bigint unsigned not null default 0
2527
)
2628
EOTABLE
2729

28-
2930
ok $dbh->do($create), "creating table";
3031

3132
my $statement= "insert into dbd_mysql_t51bind_type_guessing (id) values (?)";
@@ -54,29 +55,152 @@ ok $sth2= $dbh->prepare($statement);
5455
ok $rows= $sth2->execute('9999999999999996', '9999999999999997');
5556

5657
my $retref;
57-
ok $retref= $dbh->selectall_arrayref("select * from dbd_mysql_t51bind_type_guessing");
58+
ok $retref= $dbh->selectall_arrayref(
59+
"select * from dbd_mysql_t51bind_type_guessing");
5860

5961
cmp_ok $retref->[0][0], '==', 9999999999999998;
6062
cmp_ok $retref->[1][0], '==', 9999999999999996;
6163

6264
# checking varchars/empty strings/misidentification:
6365
$create= <<"EOTABLE";
6466
create table dbd_mysql_t51bind_type_guessing (
67+
id bigint default 0 not null,
68+
nn bigint default 0,
69+
dd double(12,4),
6570
str varchar(80),
66-
num bigint
67-
)
71+
primary key (id)
72+
) engine=innodb
6873
EOTABLE
74+
6975
ok $dbh->do("DROP TABLE IF EXISTS dbd_mysql_t51bind_type_guessing"), "drop table if exists dbd_mysql_t51bind_type_guessing";
70-
ok $dbh->do($create), "creating table w/ varchar";
76+
77+
ok $dbh->do($create), "creating table with int, double, and varchar";
78+
79+
my @sts;
80+
$t= "prepare insert integer col nn into dbd_mysql_t51bind_type_guessing";
81+
ok $sts[0] = $dbh->prepare("insert into dbd_mysql_t51bind_type_guessing (id,nn) values (?,?)"), $t;
82+
$t= "prepare update double col dd dbd_mysql_t51bind_type_guessing";
83+
ok $sts[1] = $dbh->prepare("update dbd_mysql_t51bind_type_guessing set dd = ? where id = ?"), $t;
84+
$t= "prepare update string col str dbd_mysql_t51bind_type_guessing";
85+
ok $sts[2] = $dbh->prepare("update dbd_mysql_t51bind_type_guessing set str = ? where id = ?"), $t;
86+
87+
# various values to try including issue 251
88+
my @vals = ( 52.3,
89+
' 77.7777',
90+
'.1',
91+
'5e3',
92+
+1,
93+
-1,
94+
undef,
95+
'5e',
96+
'1+',
97+
'+',
98+
'.',
99+
'e5',
100+
);
101+
102+
my $val;
103+
# the tests for 'like' are when values fail to be inserted/updated
104+
for my $i (0 .. 11) {
105+
$val = $vals[$i];
106+
if (defined $val) {
107+
$t= "insert int val $val id $i"
108+
}
109+
else {
110+
$t= "insert undef into int id $i";
111+
}
112+
if ($i >= 8) {
113+
eval {
114+
$rows= $sts[0]->execute($i, $val);
115+
};
116+
if ($i == 8) {
117+
like ($@, qr{Data truncated for column}, $t);
118+
}
119+
else {
120+
like ($@, qr{Incorrect integer value}, $t);
121+
}
122+
$rows= $sts[0]->execute($i, 0);
123+
}
124+
else {
125+
ok $rows= $sts[0]->execute($i, $val),$t;
126+
}
127+
128+
if (defined $val) {
129+
$t= "update double val $val id $i";
130+
}
131+
else {
132+
$t= "update double val undefined id $i";
133+
}
134+
if ($i >= 7) {
135+
eval {
136+
$rows = $sts[1]->execute($val, $i);
137+
};
138+
like ($@, qr{Data truncated for column}, $t);
139+
$rows= $sts[1]->execute(0, $i);
140+
}
141+
else {
142+
ok $rows= $sts[1]->execute($val,$i),$t;
143+
}
144+
145+
if (defined $val) {
146+
$t= "update string val $val id $i";
147+
}
148+
else {
149+
$t= "update string val undef id $i";
150+
}
151+
ok $rows = $sts[2]->execute($val,$i),$t;
152+
}
153+
154+
for my $i (0 .. 2) {
155+
$sts[$i]->finish();
156+
}
157+
158+
# expected results
159+
my $res= [
160+
[ 0, 52, '52.3', '52.3' ],
161+
[ 1, 78, '77.7777', '77.7777' ],
162+
[ 2, 0, '0.1', '0.1' ],
163+
[ 3, 5000, '5000', '5e3' ],
164+
[ 4, 1, '1', '1' ],
165+
[ 5, -1, '-1', '-1' ],
166+
[ 6, undef, undef, undef ],
167+
[ 7, 5, '0', '5e' ],
168+
[ 8, 0, '0', '1+' ],
169+
[ 9, 0, '0', '+' ],
170+
[ 10, 0, '0', '.' ],
171+
[ 11, 0, '0', 'e5' ]
172+
];
173+
174+
$t= "Select all values";
175+
my $query= "select * from dbd_mysql_t51bind_type_guessing";
176+
177+
ok $retref = $dbh->selectall_arrayref($query), $t;
178+
179+
for my $i (0 .. $#$res) {
180+
if ($i == 6) {
181+
is($retref->[$i][1], undef, "$i: nn undefined as expected");
182+
is($retref->[$i][2], undef, "$i: dd undefined as expected");
183+
is($retref->[$i][3], undef, "$i: str undefined as expected");
184+
}
185+
else {
186+
cmp_ok $retref->[$i][1], '==', $res->[$i][1],
187+
"test: " . "$retref->[$i][1], '==', $res->[$i][1]";
188+
cmp_ok $retref->[$i][2], 'eq', $res->[$i][2],
189+
"test: " . "$retref->[$i][2], '==', $res->[$i][2]";
190+
cmp_ok $retref->[$i][3], 'eq', $res->[$i][3],
191+
"test: " . "$retref->[$i][2], '==', $res->[$i][2]";
192+
}
193+
}
194+
71195
my $sth3;
72-
ok $sth3= $dbh->prepare("insert into dbd_mysql_t51bind_type_guessing (str, num) values (?, ?)");
73-
ok $rows= $sth3->execute(52.3, 44);
74-
ok $rows= $sth3->execute('', ' 77');
75-
ok $rows= $sth3->execute(undef, undef);
76-
77-
ok $sth3= $dbh->prepare("select * from dbd_mysql_t51bind_type_guessing limit ?");
78-
ok $rows= $sth3->execute(1);
79-
ok $rows= $sth3->execute(' 1');
196+
$t = "Prepare limit statement";
197+
ok $sth3= $dbh->prepare("select * from dbd_mysql_t51bind_type_guessing limit ?"), $t;
198+
$val = 1;
199+
$t = "select with limit $val statement";
200+
ok $rows= $sth3->execute($val), $t;
201+
$val = ' 1';
202+
$t = "select with limit $val statement";
203+
ok $rows= $sth3->execute($val), $t;
80204
$sth3->finish();
81205

82206
ok $dbh->do("DROP TABLE dbd_mysql_t51bind_type_guessing");

0 commit comments

Comments
 (0)