Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for issue 251. Also fixed case of "e<single int>" #273

Merged
merged 1 commit into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
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 @@ -5665,14 +5665,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 @@ -5681,14 +5697,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 @@ -5699,27 +5716,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 @@ -5732,6 +5757,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