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:
+
+
+
+The initial content is a BLOB and sqlite3_column_text()
+or sqlite3_column_text16()
+is called. A zero-terminator might need to be added to the string.
+
+
+The initial content is UTF-8 text and sqlite3_column_bytes16() or
+sqlite3_column_text16() is called. The content must be converted to UTF-16.
+
+
+The initial content is UTF-16 text and sqlite3_column_bytes() or
+sqlite3_column_text() is called. The content must be converted to UTF-8.
+
+
+
+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
+
+- sqlite3_column_blob(),
+- sqlite3_column_text(), or
+- sqlite3_column_text16()
+
+is invalided by subsequent calls to
+
+- sqlite3_column_bytes(),
+- sqlite3_column_bytes16(),
+- sqlite3_column_text(), or
+- sqlite3_column_text16().
+
+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:
+
+
+
+ The initial content is a BLOB and sqlite3_column_text()
+ or sqlite3_column_text16()
+ is called. A zero-terminator might need to be added to the string.
+
+
+ The initial content is UTF-8 text and sqlite3_column_bytes16() or
+ sqlite3_column_text16() is called. The content must be converted to UTF-16.
+
+
+ The initial content is UTF-16 text and sqlite3_column_bytes() or
+ sqlite3_column_text() is called. The content must be converted to UTF-8.
+
+
+
+ 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
+
+ - sqlite3_column_blob(),
+ - sqlite3_column_text(), or
+ - sqlite3_column_text16()
+
+ is invalided by subsequent calls to
+
+ - sqlite3_column_bytes(),
+ - sqlite3_column_bytes16(),
+ - sqlite3_column_text(), or
+ - sqlite3_column_text16().
+
+ 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 {} {