This patch depends/based on "[rfc][patch] Move parameter binding back to
(sqlite|dbd)_bind_ph".
Description in patch.
In addition to avoiding conversion to strings and back, it fixes passing some
special values (NAN, +-INF), test included (should fail before patch).

>From a0a270662c30db8ed4a17057dc400d2122364027 Mon Sep 17 00:00:00 2001
From: "Yuriy M. Kaminskiy" <yum...@gmail.com>
Date: Tue, 27 Mar 2012 16:33:47 +0400
Subject: [PATCH 2/3] Avoid converting numbers to strings and back

Don't coerce SV to numeric, but use numeric value if it is present.
---
 dbdimp.c         |   24 ++++++++++++++++++++++++
 t/19_bindparam.t |   53 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 76 insertions(+), 1 deletions(-)

diff --git a/dbdimp.c b/dbdimp.c
index 007638b..779ec18 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -1188,6 +1188,16 @@ sqlite_bind_ph(SV *sth, imp_sth_t *imp_sth,
         const char *data;
         int numtype = 0;
 
+        if ((imp_dbh->see_if_its_a_number || sqlite_type == SQLITE_INTEGER) && SvIOK(value)) {
+            numtype = -1;
+        }
+        else if ((imp_dbh->see_if_its_a_number || sqlite_type == SQLITE_FLOAT) && SvNOK(value) && (sizeof(NV) == sizeof(double) || SvNVX(value) == (double)SvNVX(value))) {
+            numtype = -2;
+        }
+        else if ((UV)(sqlite3_int64)UV_MAX == UV_MAX && SvIOK_UV(value)) {
+            numtype = -3;
+        }
+        else {
         if (imp_dbh->unicode) {
             sv_utf8_upgrade(value);
         }
@@ -1206,6 +1216,7 @@ sqlite_bind_ph(SV *sth, imp_sth_t *imp_sth,
         else if (sqlite_type == SQLITE_INTEGER || sqlite_type == SQLITE_FLOAT) {
             numtype = sqlite_is_number(aTHX_ data, FALSE);
         }
+        }
 
         if (numtype == 1) {
 #if defined(USE_64_BIT_INT)
@@ -1217,6 +1228,19 @@ sqlite_bind_ph(SV *sth, imp_sth_t *imp_sth,
         else if (numtype == 2 && sqlite_type != SQLITE_INTEGER) {
             rc = sqlite3_bind_double(imp_sth->stmt, pos, atof(data));
         }
+        else if (numtype == -1) {
+#if defined(USE_64_BIT_INT)
+            rc = sqlite3_bind_int64(imp_sth->stmt, pos, SvIVX(value));
+#else
+            rc = sqlite3_bind_int(imp_sth->stmt, pos, SvIVX(value));
+#endif
+        }
+        else if (numtype == -2) {
+            rc = sqlite3_bind_double(imp_sth->stmt, pos, SvNVX(value));
+        }
+        else if ((UV)(sqlite3_int64)UV_MAX == UV_MAX && numtype == -3) {
+            rc = sqlite3_bind_int64(imp_sth->stmt, pos, (sqlite3_int64)SvUVX(value));
+        }
         else {
             if (sqlite_type == SQLITE_INTEGER || sqlite_type == SQLITE_FLOAT) {
                 sqlite_error(sth, -2, form("datatype mismatch: bind %d type %d as %s", pos, sqlite_type, SvPV_nolen_undef_ok(value)));
diff --git a/t/19_bindparam.t b/t/19_bindparam.t
index 955663d..62d74d5 100644
--- a/t/19_bindparam.t
+++ b/t/19_bindparam.t
@@ -7,7 +7,7 @@ BEGIN {
 }
 
 use t::lib::Test;
-use Test::More tests => 33;
+use Test::More tests => 52;
 use Test::NoWarnings;
 use DBI ':sql_types';
 
@@ -22,6 +22,13 @@ CREATE TABLE one (
 )
 END_SQL
 
+ok( $dbh->do(<<'END_SQL'), 'CREATE TABLE' );
+CREATE TABLE two (
+    id INTEGER NOT NULL,
+    value REAL
+)
+END_SQL
+
 my $konig = "Andreas K\xf6nig";
 
 SCOPE: {
@@ -50,6 +57,26 @@ SCOPE: {
 	ok( $sth->execute, '->execute' );
 }
 
+# XXX NOT VERFIED on big-endian arches !
+# XXX maybe unpack("f",pack("L", 0x7f800000)) ?
+my $skip_float = pack("f", 1.0) ne "\x00\x00\x80\x3f";
+SKIP: {
+	skip "unusual float binary representation", 6 if $skip_float;
+	
+	my $sth = $dbh->prepare("INSERT INTO two VALUES ( ?, ? )");
+	isa_ok( $sth, 'DBI::st' );
+
+	ok( $sth->bind_param(1, 0, SQL_INTEGER), 'bind 5' );
+	ok( $sth->bind_param(2, 0, SQL_FLOAT), 'bind 6' );
+
+	# NAN
+	ok( $sth->execute(1, unpack("f", "\x00\x00\xc0\xff")), 'EXECUTE 4' );
+	# +INF
+	ok( $sth->execute(2, unpack("f", "\x00\x00\x80\x7f")), 'EXECUTE 5' );
+	# -INF
+	ok( $sth->execute(3, unpack("f", "\x00\x00\x80\xff")), 'EXECUTE 6' );
+}
+
 # Reconnect
 ok( $dbh->disconnect, '->disconnect' );
 $dbh = connect_ok( dbfile => 'foo' );
@@ -76,3 +103,27 @@ SCOPE: {
 	is( $id,   5,   'id = 5'   );
 	is( $name, undef, 'name = undef' );
 }
+
+SKIP: {
+	skip "unusual float binary representation", 12 if $skip_float;
+
+	my $sth = $dbh->prepare("SELECT id, typeof(value) FROM two ORDER BY id");
+	isa_ok( $sth, 'DBI::st' );
+	ok( $sth->execute, '->execute' );
+	my $id   = undef;
+	my $type = undef;
+	ok( $sth->bind_columns(undef, \$id, \$type), '->bind_columns' );
+
+	ok( $sth->fetch, '->fetch' );
+	is( $id,   1,   'id = 1'   );
+	# SQLite converts NAN to NULL (currently)
+	ok( $type eq 'null' || $type eq 'real', 'NAN is either null or real' );
+
+	ok( $sth->fetch, '->fetch' );
+	is( $id,   2,   'id = 2'   );
+	is( $type, 'real', '+INF is real' );
+
+	ok( $sth->fetch, '->fetch' );
+	is( $id,   3,   'id = 3'   );
+	is( $type, 'real', '-INF is real' );
+}
-- 
1.7.6.3


>From 40fc7503ec7dffa89548502461d1743dbf57aed2 Mon Sep 17 00:00:00 2001
From: "Yuriy M. Kaminskiy" <yum...@gmail.com>
Date: Tue, 3 Apr 2012 19:04:12 +0400
Subject: [PATCH 3/3] reindent

---
 dbdimp.c |   34 +++++++++++++++++-----------------
 1 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/dbdimp.c b/dbdimp.c
index 779ec18..86825fd 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -1198,24 +1198,24 @@ sqlite_bind_ph(SV *sth, imp_sth_t *imp_sth,
             numtype = -3;
         }
         else {
-        if (imp_dbh->unicode) {
-            sv_utf8_upgrade(value);
-        }
-        data = SvPV(value, len);
+            if (imp_dbh->unicode) {
+                sv_utf8_upgrade(value);
+            }
+            data = SvPV(value, len);
 
-        /*
-         *  XXX: For backward compatibility, it'd be better to
-         *  accept a value like " 4" as an integer for an integer
-         *  type column (see t/19_bindparam.t), at least when
-         *  we explicitly specify its type. However, we should
-         *  keep spaces when we just guess.
-         */
-        if (imp_dbh->see_if_its_a_number) {
-            numtype = sqlite_is_number(aTHX_ data, TRUE);
-        }
-        else if (sqlite_type == SQLITE_INTEGER || sqlite_type == SQLITE_FLOAT) {
-            numtype = sqlite_is_number(aTHX_ data, FALSE);
-        }
+            /*
+             *  XXX: For backward compatibility, it'd be better to
+             *  accept a value like " 4" as an integer for an integer
+             *  type column (see t/19_bindparam.t), at least when
+             *  we explicitly specify its type. However, we should
+             *  keep spaces when we just guess.
+             */
+            if (imp_dbh->see_if_its_a_number) {
+                numtype = sqlite_is_number(aTHX_ data, TRUE);
+            }
+            else if (sqlite_type == SQLITE_INTEGER || sqlite_type == SQLITE_FLOAT) {
+                numtype = sqlite_is_number(aTHX_ data, FALSE);
+            }
         }
 
         if (numtype == 1) {
-- 
1.7.6.3


_______________________________________________
DBD-SQLite mailing list
DBD-SQLite@lists.scsys.co.uk
http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbd-sqlite

Reply via email to