diff --git a/manifest b/manifest index b33caf688f..24215d2264 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Extra\stests\sfor\sincremental\svacuum.\s(CVS\s3879) -D 2007-04-27T07:55:38 +C Make\ssure\ssqlite3_value_bytes()\sdoes\snot\sreformat\sthe\scontent\safter\sa\ncall\sto\ssqlite3_value_blob().\s\sAdd\sdocumentation\sto\sexplain\sthis\shazard.\nAdd\smany\snew\stests.\s\sTicket\s#2321.\s(CVS\s3880) +D 2007-04-27T17:16:20 F Makefile.in 8cab54f7c9f5af8f22fd97ddf1ecfd1e1860de62 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -68,7 +68,7 @@ F src/date.c 94a6777df13d2aaacd19de080d9e8d3444364133 F src/delete.c 5c0d89b3ef7d48fe1f5124bfe8341f982747fe29 F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b F src/expr.c 2f0f9f89efe9170e5e6ca5d5e93a9d5896fff5ac -F src/func.c b989aa0ecc66a6d48b46f297299d52f12d84bce9 +F src/func.c 89d8547a9cb1c5ad8e0b86b3d74de56e5254254b F src/hash.c 67b23e14f0257b69a3e8aa663e4eeadc1a2b6fd5 F src/hash.h 1b3f7e2609141fd571f62199fc38687d262e9564 F src/insert.c 413cc06990cb3c401e64e596776c1e43934f8841 @@ -102,7 +102,7 @@ F src/sqlite3ext.h 7d0d363ea7327e817ef0dfe1b7eee1f171b72890 F src/sqliteInt.h 0b14d0eae083aafca0562d2261a404e5e5abc5f0 F src/table.c 6d0da66dde26ee75614ed8f584a1996467088d06 F src/tclsqlite.c ec69eb9ad56d03fbf7570ca1ca5ea947d1ec4b6f -F src/test1.c 53b7eb5cba0012f592b5860f6ad3b5a3f887eb1e +F src/test1.c f1271d41719d05348e6dc39722260e17b8d7ddc1 F src/test2.c 24458b17ab2f3c90cbc1c8446bd7ffe69be62f88 F src/test3.c 65f92247cf8592854e9bf5115b3fb711f8b33280 F src/test4.c 8b784cd82de158a2317cb4ac4bc86f91ad315e25 @@ -128,7 +128,7 @@ F src/vacuum.c 8bd895d29e7074e78d4e80f948e35ddc9cf2beef F src/vdbe.c a3cf3792fdbd382f756eb7eb50006b2f3f8d4283 F src/vdbe.h 0025259af1939fb264a545816c69e4b5b8d52691 F src/vdbeInt.h 4b19fd8febad3fd14c4c97adaefc06754d323132 -F src/vdbeapi.c 245263aa2d70d87b1201753cddc881996f219843 +F src/vdbeapi.c 37fc2818bec64b361af73f3935699107bab0e625 F src/vdbeaux.c ef59545f53f90394283f2fd003375d3ebbf0bd6e F src/vdbefifo.c 3ca8049c561d5d67cbcb94dc909ae9bb68c0bf8f F src/vdbemem.c 981a113405bd9b80aeb71fe246a2f01708e8a8f7 @@ -297,6 +297,7 @@ F test/pagesize.test e0a8b3fe80f8b8e808d94a00734c7a18c76c407e F test/pragma.test fecb7085f58d9fb5172a5c0b63fd3b25c7bfb414 F test/printf.test 483b9fe75ffae1fb27328bdce5560b452ba83577 F test/progress.test 8b22b4974b0a95272566385f8cb8c341c7130df8 x +F test/ptrchng.test 1c712dd6516e1377471744fa765e41c79a357da6 F test/quick.test 8e7ffe36a1c920cdcce5d641646abde2dafd764b F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6 F test/rdonly.test b34db316525440d3b42c32e83942c02c37d28ef0 @@ -418,8 +419,8 @@ F www/arch2b.fig d22a2c9642d584b89d4088b1e51e2bb0f7c04bed F www/audit.tcl 90e09d580f79c7efec0c7d6f447b7ec5c2dce5c0 F www/autoinc.tcl b357f5ba954b046ee35392ce0f884a2fcfcdea06 F www/c_interface.tcl b51b08591554c16a0c3ef718364a508ac25abc7e -F www/capi3.tcl 7a7cc225fe02eb7ab861a6019b08baa0014409e1 -F www/capi3ref.tcl 80178d2697e97236c208a2a6a507e82d121acc71 +F www/capi3.tcl 88884dd743039d1a95aa57f4a5eb369de7744716 +F www/capi3ref.tcl 0d742d6bd59bc51bbb4be014e7ea47e97786d318 F www/changes.tcl 550300b0ff00fc1b872f7802b2d5a1e7587d3e58 F www/common.tcl 2b793e5c31486c8a01dd27dc0a631ad93704438e F www/compile.tcl 276546d7eb445add5a867193bbd80f6919a6b084 @@ -463,7 +464,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5 -P 4d4180d6474d8d74460fb9333580b9b60c89f353 -R cb55629cd4ed6b5c3bd0582e6bc3a138 -U danielk1977 -Z f3e9134088bb4d98704fded975f43270 +P 40ba6493e9e8ba135552a2a0943ab499713ac001 +R ae6e82023418585801b4f01168703216 +U drh +Z e4fdef4577a3571a48a38a52b01018a7 diff --git a/manifest.uuid b/manifest.uuid index dfc8508b51..b27b176068 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -40ba6493e9e8ba135552a2a0943ab499713ac001 \ No newline at end of file +e92bd97a3726bbb7978489e2994747127c4aefcf \ No newline at end of file diff --git a/src/func.c b/src/func.c index ad1bb59155..86d5da6232 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.141 2007/04/27 01:18:03 drh Exp $ +** $Id: func.c,v 1.142 2007/04/27 17:16:20 drh Exp $ */ #include "sqliteInt.h" #include @@ -215,11 +215,12 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ char *z1; const char *z2; - int i; + int i, n; if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return; + n = sqlite3_value_bytes(argv[0]); z2 = (char*)sqlite3_value_text(argv[0]); if( z2 ){ - z1 = sqlite3_malloc(sqlite3_value_bytes(argv[0])+1); + z1 = sqlite3_malloc(n+1); if( z1 ){ strcpy(z1, z2); for(i=0; z1[i]; i++){ @@ -232,11 +233,12 @@ static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ char *z1; const char *z2; - int i; + int i, n; if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return; + n = sqlite3_value_bytes(argv[0]); z2 = (char*)sqlite3_value_text(argv[0]); if( z2 ){ - z1 = sqlite3_malloc(sqlite3_value_bytes(argv[0])+1); + z1 = sqlite3_malloc(n+1); if( z1 ){ strcpy(z1, z2); for(i=0; z1[i]; i++){ @@ -703,15 +705,15 @@ static void replaceFunc( int i, j; /* Loop counters */ assert( argc==3 ); + nStr = sqlite3_value_bytes(argv[0]); zStr = sqlite3_value_text(argv[0]); if( zStr==0 ) return; - nStr = sqlite3_value_bytes(argv[0]); + nPattern = sqlite3_value_bytes(argv[1]); zPattern = sqlite3_value_text(argv[1]); if( zPattern==0 || zPattern[0]==0 ) return; - nPattern = sqlite3_value_bytes(argv[1]); + nRep = sqlite3_value_bytes(argv[2]); zRep = sqlite3_value_text(argv[2]); if( zRep==0 ) return; - nRep = sqlite3_value_bytes(argv[2]); if( nPattern>=nRep ){ nOut = nStr; }else{ @@ -754,9 +756,9 @@ static void trimFunc( if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ return; } + nIn = sqlite3_value_bytes(argv[0]); zIn = sqlite3_value_text(argv[0]); if( zIn==0 ) return; - nIn = sqlite3_value_bytes(argv[0]); if( argc==1 ){ static const unsigned char zSpace[] = " "; zCharSet = zSpace; diff --git a/src/test1.c b/src/test1.c index 2454e7d292..1d765b1efc 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.239 2007/04/23 23:56:31 drh Exp $ +** $Id: test1.c,v 1.240 2007/04/27 17:16:20 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -557,8 +557,9 @@ static void t1_ifnullFunc( int i; for(i=0; iflags & (MEM_Blob|MEM_Str) ){ + if( (p->flags & MEM_Term)==0 ){ + p->flags &= ~MEM_Str; + } return p->z; }else{ return sqlite3_value_text(pVal); diff --git a/test/ptrchng.test b/test/ptrchng.test new file mode 100644 index 0000000000..a26d96f2e4 --- /dev/null +++ b/test/ptrchng.test @@ -0,0 +1,217 @@ +# 2007 April 27 +# +# 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. +# +# The focus of the tests in this file are to verify that the +# underlying TEXT or BLOB representation of an sqlite3_value +# changes appropriately when APIs from the following set are +# called: +# +# sqlite3_value_text() +# sqlite3_value_text16() +# sqlite3_value_blob() +# sqlite3_value_bytes() +# sqlite3_value_bytes16() +# +# $Id: ptrchng.test,v 1.1 2007/04/27 17:16:22 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Register the "pointer_change" SQL function. +# +sqlite3_create_function db + +do_test ptrchng-1.1 { + execsql { + CREATE TABLE t1(x INTEGER PRIMARY KEY, y BLOB); + INSERT INTO t1 VALUES(1, 'abc'); + INSERT INTO t1 VALUES(2, + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234356789'); + INSERT INTO t1 VALUES(3, x'626c6f62'); + INSERT INTO t1 VALUES(4, + x'000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324' + ); + SELECT count(*) FROM t1; + } +} {4} + +# For the short entries that fit in the Mem.zBuf[], the pointer should +# never change regardless of what type conversions occur. +# +do_test ptrchng-2.1 { + execsql { + SELECT pointer_change(y, 'text', 'noop', 'blob') FROM t1 WHERE x=1 + } +} {0} +do_test ptrchng-2.2 { + execsql { + SELECT pointer_change(y, 'blob', 'noop', 'text') FROM t1 WHERE x=1 + } +} {0} +ifcapable utf16 { + do_test ptrchng-2.3 { + execsql { + SELECT pointer_change(y, 'text', 'noop', 'text16') FROM t1 WHERE x=1 + } + } {0} + do_test ptrchng-2.4 { + execsql { + SELECT pointer_change(y, 'blob', 'noop', 'text16') FROM t1 WHERE x=1 + } + } {0} + do_test ptrchng-2.5 { + execsql { + SELECT pointer_change(y, 'text16', 'noop', 'blob') FROM t1 WHERE x=1 + } + } {0} + do_test ptrchng-2.6 { + execsql { + SELECT pointer_change(y, 'text16', 'noop', 'text') FROM t1 WHERE x=1 + } + } {0} +} +do_test ptrchng-2.11 { + execsql { + SELECT pointer_change(y, 'text', 'noop', 'blob') FROM t1 WHERE x=3 + } +} {0} +do_test ptrchng-2.12 { + execsql { + SELECT pointer_change(y, 'blob', 'noop', 'text') FROM t1 WHERE x=3 + } +} {0} +ifcapable utf16 { + do_test ptrchng-2.13 { + execsql { + SELECT pointer_change(y, 'text', 'noop', 'text16') FROM t1 WHERE x=3 + } + } {0} + do_test ptrchng-2.14 { + execsql { + SELECT pointer_change(y, 'blob', 'noop', 'text16') FROM t1 WHERE x=3 + } + } {0} + do_test ptrchng-2.15 { + execsql { + SELECT pointer_change(y, 'text16', 'noop', 'blob') FROM t1 WHERE x=3 + } + } {0} + do_test ptrchng-2.16 { +btree_breakpoint + execsql { + SELECT pointer_change(y, 'text16', 'noop', 'text') FROM t1 WHERE x=3 + } + } {0} +} + +# For the long entries that do not fit in the Mem.zBuf[], the pointer +# should change sometimes. +# +do_test ptrchng-3.1 { + execsql { + SELECT pointer_change(y, 'text', 'noop', 'blob') FROM t1 WHERE x=2 + } +} {0} +do_test ptrchng-3.2 { + execsql { + SELECT pointer_change(y, 'blob', 'noop', 'text') FROM t1 WHERE x=2 + } +} {0} +ifcapable utf16 { + do_test ptrchng-3.3 { + execsql { + SELECT pointer_change(y, 'text', 'noop', 'text16') FROM t1 WHERE x=2 + } + } {1} + do_test ptrchng-3.4 { + execsql { + SELECT pointer_change(y, 'blob', 'noop', 'text16') FROM t1 WHERE x=2 + } + } {1} + do_test ptrchng-3.5 { + execsql { + SELECT pointer_change(y, 'text16', 'noop', 'blob') FROM t1 WHERE x=2 + } + } {0} + do_test ptrchng-3.6 { + execsql { + SELECT pointer_change(y, 'text16', 'noop', 'text') FROM t1 WHERE x=2 + } + } {1} +} +do_test ptrchng-3.11 { + execsql { + SELECT pointer_change(y, 'text', 'noop', 'blob') FROM t1 WHERE x=4 + } +} {0} +do_test ptrchng-3.12 { + execsql { + SELECT pointer_change(y, 'blob', 'noop', 'text') FROM t1 WHERE x=4 + } +} {0} +ifcapable utf16 { + do_test ptrchng-3.13 { + execsql { + SELECT pointer_change(y, 'text', 'noop', 'text16') FROM t1 WHERE x=4 + } + } {1} + do_test ptrchng-3.14 { + execsql { + SELECT pointer_change(y, 'blob', 'noop', 'text16') FROM t1 WHERE x=4 + } + } {1} + do_test ptrchng-3.15 { + execsql { + SELECT pointer_change(y, 'text16', 'noop', 'blob') FROM t1 WHERE x=4 + } + } {0} + do_test ptrchng-3.16 { + execsql { + SELECT pointer_change(y, 'text16', 'noop', 'text') FROM t1 WHERE x=4 + } + } {1} +} + +# A call to _bytes() should never reformat a _text() or _blob(). +# +do_test ptrchng-4.1 { + execsql { + SELECT pointer_change(y, 'text', 'bytes', 'text') FROM t1 + } +} {0 0 0 0} +do_test ptrchng-4.2 { + execsql { + SELECT pointer_change(y, 'blob', 'bytes', 'blob') FROM t1 + } +} {0 0 0 0} + +# A call to _blob() should never trigger a reformat +# +do_test ptrchng-5.1 { + execsql { + SELECT pointer_change(y, 'text', 'bytes', 'blob') FROM t1 + } +} {0 0 0 0} +ifcapable utf16 { + do_test ptrchng-5.2 { + execsql { + SELECT pointer_change(y, 'text16', 'noop', 'blob') FROM t1 + } + } {0 0 0 0} + do_test ptrchng-5.3 { + execsql { + SELECT pointer_change(y, 'text16', 'bytes16', 'blob') FROM t1 + } + } {0 0 0 0} +} + +finish_test diff --git a/www/capi3.tcl b/www/capi3.tcl index 2e1e0ec180..149cf7f0ad 100644 --- a/www/capi3.tcl +++ b/www/capi3.tcl @@ -1,4 +1,4 @@ -set rcsid {$Id: capi3.tcl,v 1.9 2005/03/11 04:39:58 drh Exp $} +set rcsid {$Id: capi3.tcl,v 1.10 2007/04/27 17:16:22 drh Exp $} source common.tcl header {C/C++ Interface For SQLite Version 3} @@ -324,6 +324,56 @@ sqlite3_column_type(). If a different format is requested, the data is converted automatically.

+

+Data format conversions can invalidate the pointer returned by +prior calls to sqlite3_column_blob(), sqlite3_column_text(), and/or +sqlite3_column_text16(). Pointers might be invalided in the following +cases: +

+ +

+Note that conversions between UTF-16be and UTF-16le +are always done in place and do +not invalidate a prior pointer, though of course the content of the buffer +that the prior pointer points to will have been modified. Other kinds +of conversion are done in place when it is possible, but sometime it is +not possible and in those cases prior pointers are invalidated. +

+ +

+The safest and easiest to remember policy is this: assume that any +result from +

+is invalided by subsequent calls to + +This means that you should always call sqlite3_column_bytes() or +sqlite3_column_bytes16() before calling sqlite3_column_blob(), +sqlite3_column_text(), or sqlite3_column_text16(). +

+

2.3 User-defined functions

diff --git a/www/capi3ref.tcl b/www/capi3ref.tcl index b175ada5d5..aaa88ea1ed 100644 --- a/www/capi3ref.tcl +++ b/www/capi3ref.tcl @@ -1,4 +1,4 @@ -set rcsid {$Id: capi3ref.tcl,v 1.55 2007/04/16 15:35:24 drh Exp $} +set rcsid {$Id: capi3ref.tcl,v 1.56 2007/04/27 17:16:22 drh Exp $} source common.tcl header {C/C++ Interface For SQLite Version 3} puts { @@ -432,15 +432,51 @@ int sqlite3_column_type(sqlite3_stmt*, int iCol); Note that when type conversions occur, pointers returned by prior calls to sqlite3_column_blob(), sqlite3_column_text(), and/or - sqlite3_column_text16() may be invalidated. So, for example, if - you initially call sqlite3_column_text() and get back a pointer to - a UTF-8 string, then you call sqlite3_column_text16(), after the - call to sqlite3_column_text16() the pointer returned by the prior - call to sqlite3_column_text() will likely point to deallocated memory. - Attempting to use the original pointer might lead to heap corruption - or a segfault. Note also that calls to sqlite3_column_bytes() - and sqlite3_column_bytes16() can also cause type conversion that - and deallocate prior buffers. Use these routines carefully. + sqlite3_column_text16() may be invalidated. + Type conversions and pointer invalidations might occur + in the following cases: + +

+ + Conversions between UTF-16be and UTF-16le + are always done in place and do + not invalidate a prior pointer, though of course the content of the buffer + that the prior pointer points to will have been modified. Other kinds + of conversion are done in place when it is possible, but sometime it is + not possible and in those cases prior pointers are invalidated. + + The safest and easiest to remember policy is this: assume that any + result from + + is invalided by subsequent calls to + + This means that you should always call sqlite3_column_bytes() or + sqlite3_column_bytes16() before calling sqlite3_column_blob(), + sqlite3_column_text(), or sqlite3_column_text16(). + } api {} { @@ -1491,6 +1527,12 @@ int sqlite3_value_type(sqlite3_value*); See the documentation under sqlite3_column_blob for additional information. + + Please pay particular attention to the fact that the pointer that + is returned from sqlite3_value_blob(), sqlite3_value_text(), or + sqlite3_value_text16() can be invalidated by a subsequent call to + sqlite3_value_bytes(), sqlite3_value_bytes16(), sqlite_value_text(), + or sqlite3_value_text16(). } api {} {