Skip Menu |

This queue is for tickets about the DBD-SQLite CPAN distribution.

Report information
The Basics
Id: 44891
Status: resolved
Priority: 0/
Queue: DBD-SQLite

People
Owner: Nobody in particular
Requestors: GUIDO [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in:
  • 1.20
  • 1.21
Fixed in: (no value)



Subject: Strings looking like numbers corrupted
The attached script creates a file numbers.sqlite that showcases the bug. The output here (32 bit Linux on i686, Perl 5.8.8) is: $ perl sqlitebug.pl 2100000517|wanted: 0000002100000517 -2094966779|wanted: 0000002200000517 Inf|wanted: 0000001e00000517 Happened with DBD::SQLite 1.21. The bug is a real show-stopper. In 1.19_09 you started guessing the type of values, when the Perl function looks_like_number returns true. But obviously sqlite3 and Perl 5.8.8 have different notions of what a number is. I think the correct fix is to completely revert that change, because essentially the data gets corrupted by it. You can only do this guessing if you can somehow guarantee that strings looking like numbers are numerically equal after the cast. For that, you either have to read them back, or somehow check which numerical value the database engine /would/ write. The least thing you should do is roll your own looks_like_number() function, and that function must be a lot stricter than the Perl internal. The reason for that is, that Perl already called the function on the value, and it has already done the appropriate conversion. Consider: $sth = $dbh->prepare("INSERT INTO foo (bar) VALUES (?)"); $sth->execute(00002.000); # Your code sees a plain 2. $sth->execute("00002.000"); # Your code sees 00002.000, but why do you convert it into # a double? This is obviously not what the user wants. my $string = "00002.0000"; my $number = 00002.0000; $sth->execute($string); # Again, why convert this? $sth->execute($number); I think this guessing is dangerous and gives you more headaches than it cures. But the least needed is a check like $number == 1 * $number;
Subject: sqlitebug.pl
#! /usr/local/bin/perl -w use strict; use DBI; my $dbh = DBI->connect('dbi:SQLite:dbname=numbers.sqlite', '', '', { AutoCommit => 1, RaiseError => 1, PrintError => 1, }); my @strings = qw ( 0000002100000517 0000002200000517 0000001e00000517 ); $dbh->do(<<EOF); CREATE TABLE IF NOT EXISTS numbers(node_id TEXT, wanted TEXT) EOF $dbh->do("DELETE FROM numbers"); my $sth = $dbh->prepare("INSERT INTO numbers (node_id, wanted) VALUES (?, ?)"); foreach my $string (@strings) { $sth->execute($string, "wanted: $string"); } $sth->finish; $sth = $dbh->prepare("SELECT node_id, wanted FROM numbers"); $sth->execute; while (my @row = $sth->fetchrow_array) { my ($node_id, $wanted) = @row; print "$node_id|$wanted\n"; } $dbh->disconnect; print "Happened with DBD::SQLite $DBD::SQLite::VERSION.\n"; my $string = "0000002200000517"; my $number = 1 * $string; print "$number\n";
Subject: Re: [rt.cpan.org #44891] Strings looking like numbers corrupted
Date: Thu, 9 Apr 2009 22:27:03 +1000
To: bug-DBD-SQLite [...] rt.cpan.org
From: Adam Kennedy <adamkennedybackup [...] gmail.com>
The problem is we're using the Perl-based guess. If we can find a function from SQLite itself to do the guess, then we should be fine. Adam K 2009/4/9 GUIDO via RT <bug-DBD-SQLite@rt.cpan.org>: Show quoted text
> Thu Apr 09 06:26:53 2009: Request 44891 was acted upon. > Transaction: Ticket created by GUIDO >       Queue: DBD-SQLite >     Subject: Strings looking like numbers corrupted >   Broken in: 1.20, 1.21 >    Severity: (no value) >       Owner: Nobody >  Requestors: GUIDO@cpan.org >      Status: new >  Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=44891 > > > > The attached script creates a file numbers.sqlite that showcases the > bug. The output here (32 bit Linux on i686, Perl 5.8.8) is: > >    $ perl sqlitebug.pl >    2100000517|wanted: 0000002100000517 >    -2094966779|wanted: 0000002200000517 >    Inf|wanted: 0000001e00000517 >    Happened with DBD::SQLite 1.21. > > The bug is a real show-stopper. > > In 1.19_09 you started guessing the type of values, when the Perl > function looks_like_number returns true.  But obviously sqlite3 and Perl > 5.8.8 have different notions of what a number is. > > I think the correct fix is to completely revert that change, because > essentially the data gets corrupted by it.  You can only do this > guessing if you can somehow guarantee that strings looking like numbers > are numerically equal after the cast.  For that, you either have to read > them back, or somehow check which numerical value the database engine > /would/ write. > > The least thing you should do is roll your own looks_like_number() > function, and that function must be a lot stricter than the Perl > internal.  The reason for that is, that Perl already called the function > on the value, and it has already done the appropriate conversion.  Consider: > >    $sth = $dbh->prepare("INSERT INTO foo (bar) VALUES (?)"); > >    $sth->execute(00002.000); >    # Your code sees a plain 2. > >    $sth->execute("00002.000"); >    # Your code sees 00002.000, but why do you convert it into >    # a double?  This is obviously not what the user wants. > >    my $string = "00002.0000"; >    my $number = 00002.0000; > >    $sth->execute($string); >    # Again, why convert this? > >    $sth->execute($number); > > I think this guessing is dangerous and gives you more headaches than it > cures.  But the least needed is a check like $number == 1 * $number; >
On Thu Apr 09 08:27:22 2009, adamkennedybackup@gmail.com wrote: Show quoted text
> The problem is we're using the Perl-based guess. > > If we can find a function from SQLite itself to do the guess, then we > should be fine.
That's what I meant, and an additional check should be done that the resulting number is numerically identical to the cast. BTW, my test case only shows the behavior for 32 bit Perl. If you compile it with 64 bit integers, you have to use higher "numbers". If you really want to stick with the guessing (honestly, I don't understand why, when SQLite is a typeless database and Perl a typeless language), the following cases signify that string context should be used: - String starts with a 0 and the next character is a digit. That means, that the argument was a quoted string. Otherwise Perl would have eliminated the leading 0. - If it is an integer and greater than the maximum signed integer or smaller than the maximum unsigned integer that Perl can understand. - Likewise for doubles. - Both checks likewise for the limits of the database engine. Cheers, Guido
The reason why I implemented a wild guessing is to resolve tickets like #29058 and #29629 where we want to compare a number with a result of a function that returns a number (this hadn't worked well as the binded number was quoted), but I understand you and the previous implementation was not good enough. So we reverted the change in 1.22_04, maybe tentatively, or maybe permanently as there're at least two known workarounds for the issue. On Thu, Apr 09 08:52:01 2009, GUIDO wrote: Show quoted text
> On Thu Apr 09 08:27:22 2009, adamkennedybackup@gmail.com wrote:
> > The problem is we're using the Perl-based guess. > > > > If we can find a function from SQLite itself to do the guess, then
we Show quoted text
> > should be fine.
> > That's what I meant, and an additional check should be done that the > resulting number is numerically identical to the cast. > > BTW, my test case only shows the behavior for 32 bit Perl. If you > compile it with 64 bit integers, you have to use higher "numbers". > > If you really want to stick with the guessing (honestly, I don't > understand why, when SQLite is a typeless database and Perl a typeless > language), the following cases signify that string context should be
used: Show quoted text
> > - String starts with a 0 and the next character is a digit. That
means, Show quoted text
> that the argument was a quoted string. Otherwise Perl would have > eliminated the leading 0. > > - If it is an integer and greater than the maximum signed integer or > smaller than the maximum unsigned integer that Perl can understand. > > - Likewise for doubles. > > - Both checks likewise for the limits of the database engine. > > Cheers, > Guido
On Sat Apr 11 00:52:57 2009, ISHIGAKI wrote: Show quoted text
> The reason why I implemented a wild guessing is to resolve tickets like > #29058 and #29629 where we want to compare a number with a result of a > function that returns a number (this hadn't worked well as the binded > number was quoted), but I understand you and the previous > implementation was not good enough. So we reverted the change in > 1.22_04, maybe tentatively, or maybe permanently as there're at least > two known workarounds for the issue.
I think, no matter how smart your guessing will be, there will always be real-world data that outsmarts your guessing. A clean way to solve the other issues would be to extend the interface of prepare(). Currently, it takes only one argument. Plenty of room for specifying the type of place-holders, if it matters. Another way would be to extend the placeholder syntax: SELECT bar FROM foo HAVING COUNT(*) > ?[INT] Disadvantage: Not compatible with the other DBD drivers. My own problem is solved, I have a workaround. I simply prepend a character to my strings when writing, and discard it when reading. A little bit ugly but it works.
Resolved by removing the original implementation