Skip to content

Commit

Permalink
Fix for issue 251. Also fixed case of "e<single int>"
Browse files Browse the repository at this point in the history
  • Loading branch information
CaptTofu authored and dveeden committed Nov 16, 2023
1 parent 9a1500f commit 43be0c2
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 26 deletions.
1 change: 1 addition & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ DBI/DBD community (4.049)
DBI/DBD community (4.048)
* Fix corrupted META.json so cpan installations work as expected.
https://github.com/perl5-dbi/DBD-mysql/issues/263
* Fix issue 251: unable to insert "." with bind_type_guessing on

2018-09-08 Daniël van Eeden, Patrick Galbraith, DBI/DBD community (4.047)
* Add options needed for public key based security.
Expand Down
42 changes: 34 additions & 8 deletions dbdimp.c
Original file line number Diff line number Diff line change
Expand Up @@ -4824,14 +4824,30 @@ int mysql_db_async_ready(SV* h)
}
}

/*
* this funtion tries to parse numbers from the passed string value
* for emulated prepared statements being bound
* * finds the beginning and end address of the string
* * loops through the string
* * skips past any spaces
* * if it sees a -, ., +, or e, sets a flag
* * if all subsequent values are numbers, is a digit
* * otherwise, a string
* * if any character that's not a digit is found, it's a string
* * if only numbers are found, it's a number
* * `end` is set to the address of the last member in the iteration
* and used in knowing where the value is when building the SQL
* statement
*
*/
static int parse_number(char *string, STRLEN len, char **end)
{
int seen_neg;
int seen_dec;
int seen_e;
int seen_plus;
int seen_digit;
char *cp;
char *cp,*str_end;

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

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

cp= string;
str_end= string + len-1;

/* Skip leading whitespace */
while (*cp && isspace(*cp))
cp++;

for ( ; *cp; cp++)
{
if ('-' == *cp)
if (*cp == '-')
{
if (seen_neg >= 2)
{
Expand All @@ -4858,27 +4875,35 @@ static int parse_number(char *string, STRLEN len, char **end)
}
seen_neg += 1;
}
else if ('.' == *cp)
else if (*cp == '.')
{
if (seen_dec)
if (seen_dec || cp == str_end)
{
/* second '.' */
break;
}
seen_dec= 1;
}
else if ('e' == *cp)
else if (*cp == 'e')
{
if (seen_e)
/*
* this is the case where the value for example, e2,
* which should be a varchar
*/
if (cp == str_end -1)
{
break;
}
if (seen_e || cp == str_end)
{
/* second 'e' */
break;
}
seen_e= 1;
}
else if ('+' == *cp)
else if (*cp == '+')
{
if (seen_plus)
if (seen_plus || cp == str_end)
{
/* second '+' */
break;
Expand All @@ -4891,6 +4916,7 @@ static int parse_number(char *string, STRLEN len, char **end)
/* seen_digit= 1; */
break;
}
/*printf("else: cp is a digit: %d\n", *cp); */
}

*end= cp;
Expand Down
1 change: 1 addition & 0 deletions dbdimp.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
/*
* Header files we use
*/
#include <ctype.h>
#include <DBIXS.h> /* installed by the DBI module */
#include <mysql.h> /* Comes with MySQL-devel */
#include <mysqld_error.h> /* Comes MySQL */
Expand Down
6 changes: 5 additions & 1 deletion t/35limit.t
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ require 'lib.pl';

my $dbh;
eval {$dbh= DBI->connect($test_dsn, $test_user, $test_password,
{ RaiseError => 1, PrintError => 1, AutoCommit => 0 });};
{ mysql_bind_type_guessing => 1,
RaiseError => 1,
PrintError => 1,
AutoCommit => 1 });};

if ($@) {
plan skip_all => "no database connection";
}
Expand Down
158 changes: 141 additions & 17 deletions t/51bind_type_guessing.t
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,29 @@ use warnings;
use DBI;
use DBI::Const::GetInfoType;
use Test::More;
select(($|=1,select(STDERR),$|=1)[1]);
use lib 't', '.';
require 'lib.pl';

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

my $dbh;
my ($dbh, $t);
eval {$dbh= DBI->connect($test_dsn, $test_user, $test_password,
{ RaiseError => 1, PrintError => 1, AutoCommit => 0 });};
{ RaiseError => 1, PrintError => 1, AutoCommit => 1 });};
if ($@) {
plan skip_all => "no database connection";
}
plan tests => 26;
plan tests => 98;

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

my $create= <<"EOTABLE";
create table dbd_mysql_t51bind_type_guessing (
id bigint unsigned not null default 0
)
EOTABLE


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

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

my $retref;
ok $retref= $dbh->selectall_arrayref("select * from dbd_mysql_t51bind_type_guessing");
ok $retref= $dbh->selectall_arrayref(
"select * from dbd_mysql_t51bind_type_guessing");

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

# checking varchars/empty strings/misidentification:
$create= <<"EOTABLE";
create table dbd_mysql_t51bind_type_guessing (
id bigint default 0 not null,
nn bigint default 0,
dd double(12,4),
str varchar(80),
num bigint
)
primary key (id)
) engine=innodb
EOTABLE

ok $dbh->do("DROP TABLE IF EXISTS dbd_mysql_t51bind_type_guessing"), "drop table if exists dbd_mysql_t51bind_type_guessing";
ok $dbh->do($create), "creating table w/ varchar";

ok $dbh->do($create), "creating table with int, double, and varchar";

my @sts;
$t= "prepare insert integer col nn into dbd_mysql_t51bind_type_guessing";
ok $sts[0] = $dbh->prepare("insert into dbd_mysql_t51bind_type_guessing (id,nn) values (?,?)"), $t;
$t= "prepare update double col dd dbd_mysql_t51bind_type_guessing";
ok $sts[1] = $dbh->prepare("update dbd_mysql_t51bind_type_guessing set dd = ? where id = ?"), $t;
$t= "prepare update string col str dbd_mysql_t51bind_type_guessing";
ok $sts[2] = $dbh->prepare("update dbd_mysql_t51bind_type_guessing set str = ? where id = ?"), $t;

# various values to try including issue 251
my @vals = ( 52.3,
' 77.7777',
'.1',
'5e3',
+1,
-1,
undef,
'5e',
'1+',
'+',
'.',
'e5',
);

my $val;
# the tests for 'like' are when values fail to be inserted/updated
for my $i (0 .. 11) {
$val = $vals[$i];
if (defined $val) {
$t= "insert int val $val id $i"
}
else {
$t= "insert undef into int id $i";
}
if ($i >= 8) {
eval {
$rows= $sts[0]->execute($i, $val);
};
if ($i == 8) {
like ($@, qr{Data truncated for column}, $t);
}
else {
like ($@, qr{Incorrect integer value}, $t);
}
$rows= $sts[0]->execute($i, 0);
}
else {
ok $rows= $sts[0]->execute($i, $val),$t;
}

if (defined $val) {
$t= "update double val $val id $i";
}
else {
$t= "update double val undefined id $i";
}
if ($i >= 7) {
eval {
$rows = $sts[1]->execute($val, $i);
};
like ($@, qr{Data truncated for column}, $t);
$rows= $sts[1]->execute(0, $i);
}
else {
ok $rows= $sts[1]->execute($val,$i),$t;
}

if (defined $val) {
$t= "update string val $val id $i";
}
else {
$t= "update string val undef id $i";
}
ok $rows = $sts[2]->execute($val,$i),$t;
}

for my $i (0 .. 2) {
$sts[$i]->finish();
}

# expected results
my $res= [
[ 0, 52, '52.3', '52.3' ],
[ 1, 78, '77.7777', '77.7777' ],
[ 2, 0, '0.1', '0.1' ],
[ 3, 5000, '5000', '5e3' ],
[ 4, 1, '1', '1' ],
[ 5, -1, '-1', '-1' ],
[ 6, undef, undef, undef ],
[ 7, 5, '0', '5e' ],
[ 8, 0, '0', '1+' ],
[ 9, 0, '0', '+' ],
[ 10, 0, '0', '.' ],
[ 11, 0, '0', 'e5' ]
];

$t= "Select all values";
my $query= "select * from dbd_mysql_t51bind_type_guessing";

ok $retref = $dbh->selectall_arrayref($query), $t;

for my $i (0 .. $#$res) {
if ($i == 6) {
is($retref->[$i][1], undef, "$i: nn undefined as expected");
is($retref->[$i][2], undef, "$i: dd undefined as expected");
is($retref->[$i][3], undef, "$i: str undefined as expected");
}
else {
cmp_ok $retref->[$i][1], '==', $res->[$i][1],
"test: " . "$retref->[$i][1], '==', $res->[$i][1]";
cmp_ok $retref->[$i][2], 'eq', $res->[$i][2],
"test: " . "$retref->[$i][2], '==', $res->[$i][2]";
cmp_ok $retref->[$i][3], 'eq', $res->[$i][3],
"test: " . "$retref->[$i][2], '==', $res->[$i][2]";
}
}

my $sth3;
ok $sth3= $dbh->prepare("insert into dbd_mysql_t51bind_type_guessing (str, num) values (?, ?)");
ok $rows= $sth3->execute(52.3, 44);
ok $rows= $sth3->execute('', ' 77');
ok $rows= $sth3->execute(undef, undef);

ok $sth3= $dbh->prepare("select * from dbd_mysql_t51bind_type_guessing limit ?");
ok $rows= $sth3->execute(1);
ok $rows= $sth3->execute(' 1');
$t = "Prepare limit statement";
ok $sth3= $dbh->prepare("select * from dbd_mysql_t51bind_type_guessing limit ?"), $t;
$val = 1;
$t = "select with limit $val statement";
ok $rows= $sth3->execute($val), $t;
$val = ' 1';
$t = "select with limit $val statement";
ok $rows= $sth3->execute($val), $t;
$sth3->finish();

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

0 comments on commit 43be0c2

Please sign in to comment.