]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix text-to-numeric type casting so that it works correctly on UTF16
authordrh <drh@noemail.net>
Wed, 20 Mar 2013 12:04:29 +0000 (12:04 +0000)
committerdrh <drh@noemail.net>
Wed, 20 Mar 2013 12:04:29 +0000 (12:04 +0000)
strings that contain characters where the LSB is numeric but the MSB
is non-zero.  Ticket [689137afb6da41]

FossilOrigin-Name: 5b22053f918d16f593227a432a5d5b4c195bb0b5

manifest
manifest.uuid
src/util.c
test/numcast.test [new file with mode: 0644]

index 4e0fb3f191ee7fecc36c33647f974c9b8092d75b..311e065ca2bac352aa3b4b82af0e8481f526a9d8 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Bring\smakefiles\sand\sbuild\sscripts\sinto\salignment\swith\sthe\ssessions\sbranch.\nNo\schanges\sto\scode.
-D 2013-03-19T16:12:40.639
+C Fix\stext-to-numeric\stype\scasting\sso\sthat\sit\sworks\scorrectly\son\sUTF16\nstrings\sthat\scontain\scharacters\swhere\sthe\sLSB\sis\snumeric\sbut\sthe\sMSB\nis\snon-zero.\s\sTicket\s[689137afb6da41]
+D 2013-03-20T12:04:29.291
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in df3e48659d80e1b7765785d8d66c86b320f72cc7
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -237,7 +237,7 @@ F src/tokenize.c 1e86210d3976717a19238ea7b047fac481fe8c12
 F src/trigger.c cd95ac64efa60e39faf9b5597443192ff27a22fa
 F src/update.c 28d2d098b43a2c70dae399896ea8a02f622410ef
 F src/utf.c 8d819e2e5104a430fc2005f018db14347c95a38f
-F src/util.c 0af2e515dc0dabacec931bca39525f6c3f1c5455
+F src/util.c 550f2b6a5c0085153a4d00462719fb17ee242792
 F src/vacuum.c 2727bdd08847fcb6b2d2da6d14f018910e8645d3
 F src/vdbe.c 292f8f7ced59c29c63fe17830cbe5f5a0230cdf0
 F src/vdbe.h b52887278cb173e66188da84dfab216bea61119d
@@ -641,6 +641,7 @@ F test/notify2.test 9503e51b9a272a5405c205ad61b7623d5a9ca489
 F test/notify3.test a86259abbfb923aa27d30f0fc038c88e5251488a
 F test/notnull.test 2afad748d18fd66d01f66463de73b3e2501fb226
 F test/null.test a8b09b8ed87852742343b33441a9240022108993
+F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1
 F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
 F test/orderby1.test f33968647da5c546528fe4d2bf86c6a6a2e5a7ae
 F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
@@ -1038,7 +1039,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
-P 4fe2db1d866c80fe51f7fddbf9ce6753fb55b5f6
-R 7758dd15e3a5f0387c3f17443ec025d8
+P d1f41089aba075eef45fd696599e5d3a74c84d0c
+R ccf3378e4accef01a0ea31a61ac073d2
 U drh
-Z 7c5d1dfbeba3270fc7d5841fae61579f
+Z 400c45055ef48421e523d8328d092a1b
index ac1d43b9fa2b60a814577adbf1577a354b4e3ace..f861049e0c30ddf3cffcb9e90a2249a4e2499ad3 100644 (file)
@@ -1 +1 @@
-d1f41089aba075eef45fd696599e5d3a74c84d0c
\ No newline at end of file
+5b22053f918d16f593227a432a5d5b4c195bb0b5
\ No newline at end of file
index 5cf8ebacb5db2d8489e192498d9d627be9201cbb..937067a06f9773306b0c62c57a7c6b98c28c5beb 100644 (file)
@@ -261,7 +261,7 @@ int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
 */
 int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
 #ifndef SQLITE_OMIT_FLOATING_POINT
-  int incr = (enc==SQLITE_UTF8?1:2);
+  int incr;
   const char *zEnd = z + length;
   /* sign * significand * (10 ^ (esign * exponent)) */
   int sign = 1;    /* sign of significand */
@@ -272,10 +272,22 @@ int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
   int eValid = 1;  /* True exponent is either not used or is well-formed */
   double result;
   int nDigits = 0;
+  int nonNum = 0;
 
+  assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
   *pResult = 0.0;   /* Default return value, in case of an error */
 
-  if( enc==SQLITE_UTF16BE ) z++;
+  if( enc==SQLITE_UTF8 ){
+    incr = 1;
+  }else{
+    int i;
+    incr = 2;
+    assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
+    for(i=3-enc; i<length && z[i]==0; i+=2){}
+    nonNum = i<length;
+    zEnd = z+i+enc-3;
+    z += (enc&1);
+  }
 
   /* skip leading spaces */
   while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
@@ -408,7 +420,7 @@ do_atof_calc:
   *pResult = result;
 
   /* return true if number and no extra non-whitespace chracters after */
-  return z>=zEnd && nDigits>0 && eValid;
+  return z>=zEnd && nDigits>0 && eValid && nonNum==0;
 #else
   return !sqlite3Atoi64(z, pResult, length, enc);
 #endif /* SQLITE_OMIT_FLOATING_POINT */
@@ -457,21 +469,33 @@ static int compare2pow63(const char *zNum, int incr){
 ** signed 64-bit integer, its negative -9223372036854665808 can be.
 **
 ** If zNum is too big for a 64-bit integer and is not
-** 9223372036854665808 then return 1.
+** 9223372036854665808  or if zNum contains any non-numeric text,
+** then return 1.
 **
 ** length is the number of bytes in the string (bytes, not characters).
 ** The string is not necessarily zero-terminated.  The encoding is
 ** given by enc.
 */
 int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
-  int incr = (enc==SQLITE_UTF8?1:2);
+  int incr;
   u64 u = 0;
   int neg = 0; /* assume positive */
   int i;
   int c = 0;
+  int nonNum = 0;
   const char *zStart;
   const char *zEnd = zNum + length;
-  if( enc==SQLITE_UTF16BE ) zNum++;
+  assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
+  if( enc==SQLITE_UTF8 ){
+    incr = 1;
+  }else{
+    incr = 2;
+    assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
+    for(i=3-enc; i<length && zNum[i]==0; i+=2){}
+    nonNum = i<length;
+    zEnd = zNum+i+enc-3;
+    zNum += (enc&1);
+  }
   while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr;
   if( zNum<zEnd ){
     if( *zNum=='-' ){
@@ -496,7 +520,7 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
   testcase( i==18 );
   testcase( i==19 );
   testcase( i==20 );
-  if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr ){
+  if( (c+nonNum!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr ){
     /* zNum is empty or contains non-numeric text or is longer
     ** than 19 digits (thus guaranteeing that it is too large) */
     return 1;
diff --git a/test/numcast.test b/test/numcast.test
new file mode 100644 (file)
index 0000000..926bbe4
--- /dev/null
@@ -0,0 +1,46 @@
+# 2013 March 20
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. 
+# This particular file does testing of casting strings into numeric
+# values.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+foreach enc {utf8 utf16le utf16be} {
+  do_test numcast-$enc.0 {
+    db close
+    sqlite3 db :memory:
+    db eval "PRAGMA encoding='$enc'"
+    set x [db eval {PRAGMA encoding}]
+    string map {- {}} [string tolower $x]
+  } $enc
+  foreach {idx str rval ival} {
+     1 12345.0       12345.0    12345
+     2 12345.0e0     12345.0    12345
+     3 -12345.0e0   -12345.0   -12345
+     4 -12345.25    -12345.25  -12345
+     5 { -12345.0}  -12345.0   -12345
+     6 { 876xyz}       876.0      876
+     7 { 456ķ89}       456.0      456
+     8 { Ġ 321.5}        0.0        0
+  } {
+    do_test numcast-$enc.$idx.1 {
+      db eval {SELECT CAST($str AS real)}
+    } $rval
+    do_test numcast-$enc.$idx.2 {
+      db eval {SELECT CAST($str AS integer)}
+    } $ival
+  }
+}
+
+finish_test