From b77f5dadbf3e34d9724c836c6871955b59694123 Mon Sep 17 00:00:00 2001 From: danielk1977 Date: Wed, 26 May 2004 13:27:00 +0000 Subject: [PATCH] Ensure the type of an sqlite3_value* is not modified by calls to sqlite3_value_*() calls. (CVS 1463) FossilOrigin-Name: ce8b15203413f38a8b7127eb08ae5db1c1eb164a --- manifest | 18 +++---- manifest.uuid | 2 +- src/test1.c | 28 +--------- src/vdbe.c | 141 ++++++++++++++++++++++++++---------------------- src/vdbeInt.h | 63 ++++++++++++++-------- test/capi3.test | 14 ++--- 6 files changed, 137 insertions(+), 129 deletions(-) diff --git a/manifest b/manifest index b188e81197..3a16ec2ceb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssome\stests\sfor\sthe\snew\sAPI.\sMany\smore\sto\scome.\s(CVS\s1462) -D 2004-05-26T10:11:05 +C Ensure\sthe\stype\sof\san\ssqlite3_value*\sis\snot\smodified\sby\scalls\sto\nsqlite3_value_*()\scalls.\s(CVS\s1463) +D 2004-05-26T13:27:00 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -59,7 +59,7 @@ F src/sqlite.h.in c36ab3d4dc1b863ba0cf79905210180b8059a839 F src/sqliteInt.h 323281bd04c7f988c6873777750acaa1f6b3b9a6 F src/table.c af14284fa36c8d41f6829e3f2819dce07d3e2de2 F src/tclsqlite.c 2700a35f494e8fc5ad2742bcae09d2cb66a295ab -F src/test1.c e9509ec479cb84bb6e6f88079dc4d0932459663e +F src/test1.c f8dacbbdfa206ed975c02842c52dee0c97952817 F src/test2.c 6195a1ca2c8d0d2d93644e86da3289b403486872 F src/test3.c 5e4a6d596f982f6f47a5f9f75ede9b4a3b739968 F src/test4.c 014478492bddb3f9b4a3151b05f9ac708fe279fb @@ -70,9 +70,9 @@ F src/update.c 96461bcf4e946697e83c09c77c7e61b545a2f66e F src/utf.c 1d38da85bffb928fb0d9f301e7db913a6df486ce F src/util.c 4c0adcbc9ce6678dd046931253e45d623c6d279f F src/vacuum.c 8734f89742f246abd91dbd3e087fc153bddbfbad -F src/vdbe.c f1dcfdc19d4b62413a46cf47414d9858c2c08769 +F src/vdbe.c 0853b13a1b799eb54a1f6bae3f17cb1a1fc76164 F src/vdbe.h 948cafed61e827967e07b44d51009c0e48ce168e -F src/vdbeInt.h 653525b165ccc72b506d2793281a7d95eb42ad32 +F src/vdbeInt.h 1064ce1723c9c739772af2903e7e06ad2b214be1 F src/vdbeaux.c be599d276d25f04c5a8c26e8e270e3909f6c3c05 F src/where.c efe5d25fe18cd7381722457898cd863e84097a0c F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242 @@ -88,7 +88,7 @@ F test/btree4.test 3797b4305694c7af6828675b0f4b1424b8ca30e4 F test/btree5.test 8e5ff32c02e685d36516c6499add9375fe1377f2 F test/btree6.test a5ede6bfbbb2ec8b27e62813612c0f28e8f3e027 F test/capi2.test 8fb64e8ab7f78b8254cd4d04bb96822167f731b2 -F test/capi3.test b85ea1e676ff482a813332ef4418c8ef63721538 +F test/capi3.test 7ee3e33971ca5301f6a01e92cffaab95f91f417b F test/conflict.test 0911bb2f079046914a6e9c3341b36658c4e2103e F test/copy.test f07ea8d60878da7a67416ab62f78e9706b9d3c45 F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2 @@ -203,7 +203,7 @@ F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604 F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4 -P 93bb958d939302795027f738b8f663cbce3e8783 -R 9229fc306f747f64e12780c4e3dc4ddb +P d5659f2ee6788e0205fb5e03eeaf64e6c0aa9bed +R 0555bbc2774c833810e9e473c1000890 U danielk1977 -Z 12be33fdc43bb15fcde0f5e6812926c8 +Z c2aca9fbc64ca791014e235f1a4d9491 diff --git a/manifest.uuid b/manifest.uuid index c7ebb881c3..c9cfa274ba 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d5659f2ee6788e0205fb5e03eeaf64e6c0aa9bed \ No newline at end of file +ce8b15203413f38a8b7127eb08ae5db1c1eb164a \ No newline at end of file diff --git a/src/test1.c b/src/test1.c index 0d3823c09f..e695cce5f3 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.57 2004/05/26 10:11:06 danielk1977 Exp $ +** $Id: test1.c,v 1.58 2004/05/26 13:27:00 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -695,30 +695,6 @@ static int test_register_func( return TCL_OK; } -/* -** This SQLite callback records the datatype of all columns. -** -** The pArg argument is really a pointer to a TCL interpreter. The -** column names are inserted as the result of this interpreter. -** -** This routine returns non-zero which causes the query to abort. -*/ -static int rememberDataTypes(void *pArg, int nCol, char **argv, char **colv){ - int i; - Tcl_Interp *interp = (Tcl_Interp*)pArg; - Tcl_Obj *pList, *pElem; - if( colv[nCol+1]==0 ){ - return 1; - } - pList = Tcl_NewObj(); - for(i=0; iflags&Mem_Null) #define MemIsBlob(p) ((p)->flags&Mem_Blob) #define MemIsStr(p) ((p)->flags&(MEM_Int|MEM_Real|MEM_Str)) -#define MemIsInt(p) ((p)->flags&MEM_Int || hardMemIsInt(p)) +#define MemIsInt(p) ((p)->flags&(MEM_Int|MEM_Real) || hardMemIsInt(p)) #define MemIsReal(p) ((p)->flags&(MEM_Int|MEM_Real) || hardMemIsReal(p)) static int hardMemIsInt(Mem *p){ assert( !(p->flags&(MEM_Int|MEM_Real)) ); @@ -127,7 +127,7 @@ static int hardMemIsReal(Mem *p){ ** they may cache the integer or real value cast of the value. */ #define MemInt(p) (((p)->flags&MEM_Int)?(p)->i:hardMemInt(p)) -#define MemReal(p) (((p)->flags&MEM_Real)?(p)->i:hardMemReal(p)) +#define MemReal(p) (((p)->flags&MEM_Real)?(p)->r:hardMemReal(p)) static i64 hardMemInt(Mem *p){ assert( !(p->flags&MEM_Int) ); if( !MemIsInt(p) ) return 0; @@ -239,12 +239,24 @@ static int encToFlags(u8 enc){ ((pMem->flags & ~(MEM_Utf8|MEM_Utf16le|MEM_Utf16be))) | encToFlags(enc)) static int SetEncoding(Mem*, int); +/* +** Set the MEM_TypeStr, MEM_TypeReal or MEM_TypeInt flags in pMem if +** required. +*/ +static void MemSetTypeFlags(Mem *pMem){ + int f = pMem->flags; + if( f&MEM_Int ) pMem->flags |= MEM_TypeInt; + else if( f&MEM_Real ) pMem->flags |= MEM_TypeReal; + else if( f&MEM_Str ) pMem->flags |= MEM_TypeStr; +} + + /* ** Convert the given stack entity into a string if it isn't one ** already. Return non-zero if a malloc() fails. */ #define Stringify(P, enc) \ -(!((P)->flags&(MEM_Str|MEM_Blob)) && hardStringify(P, enc)) +if( !((P)->flags&(MEM_Str|MEM_Blob)) ) hardStringify(P, enc); static int hardStringify(Mem *pStack, u8 enc){ int rc = SQLITE_OK; int fg = pStack->flags; @@ -377,7 +389,9 @@ int SetEncoding(Mem *pMem, int flags){ */ pMem->z = z; pMem->n = n; - pMem->flags = (MEM_Str | MEM_Dyn | MEM_Term | flags); + pMem->flags &= ~(MEM_Utf8|MEM_Utf16le|MEM_Utf16be); + pMem->flags &= ~(MEM_Static|MEM_Short|MEM_Ephem); + pMem->flags |= (MEM_Dyn|MEM_Term|flags); }else{ /* Must be translating between UTF-16le and UTF-16be. */ int i; @@ -575,43 +589,33 @@ const unsigned char *sqlite3_column_data(sqlite3_stmt *pStmt, int i){ ** is returned. */ const unsigned char *sqlite3_value_data(sqlite3_value *pVal){ - if( pVal->flags&MEM_Null ){ + int flags = pVal->flags; + + if( flags&MEM_Null ){ /* For a NULL return a NULL Pointer */ return 0; } - if( pVal->flags&MEM_Str ){ + if( flags&MEM_Str ){ /* If there is already a string representation, make sure it is in ** encoded in UTF-8. */ SetEncoding(pVal, MEM_Utf8|MEM_Term); - }else if( !(pVal->flags&MEM_Blob) ){ - /* Otherwise, unless this is a blob, convert it to a UTF-8 string */ - Stringify(pVal, TEXT_Utf8); + }else if( !(flags&MEM_Blob) ){ + if( flags&MEM_Int ){ + sqlite3_snprintf(NBFS, pVal->zShort, "%lld", pVal->i); + }else{ + assert( flags&MEM_Real ); + sqlite3_snprintf(NBFS, pVal->zShort, "%.15g", pVal->r); + } + pVal->z = pVal->zShort; + pVal->n = strlen(pVal->z)+1; + pVal->flags |= (MEM_Str|MEM_Short); } return pVal->z; } -/* -** Return the value of the 'i'th column of the current row of the currently -** executing statement pStmt. -*/ -const void *sqlite3_column_data16(sqlite3_stmt *pStmt, int i){ - int vals; - Vdbe *pVm = (Vdbe *)pStmt; - Mem *pVal; - - vals = sqlite3_data_count(pStmt); - if( i>=vals || i<0 ){ - sqlite3Error(pVm->db, SQLITE_RANGE, 0); - return 0; - } - - pVal = &pVm->pTos[(1-vals)+i]; - return sqlite3_value_data16((sqlite3_value *)pVal); -} - /* ** pVal is a Mem* cast to an sqlite_value* value. Return a pointer to ** the nul terminated UTF-16 string representation if the value is @@ -641,13 +645,32 @@ const void *sqlite3_value_data16(sqlite3_value* pVal){ */ SetEncoding(pVal, encToFlags(TEXT_Utf16)|MEM_Term); }else if( !(pVal->flags&MEM_Blob) ){ - /* Otherwise, unless this is a blob, convert it to a UTF-16 string */ - Stringify(pVal, TEXT_Utf16); + sqlite3_value_data(pVal); + SetEncoding(pVal, encToFlags(TEXT_Utf16)|MEM_Term); } return (const void *)(pVal->z); } +/* +** Return the value of the 'i'th column of the current row of the currently +** executing statement pStmt. +*/ +const void *sqlite3_column_data16(sqlite3_stmt *pStmt, int i){ + int vals; + Vdbe *pVm = (Vdbe *)pStmt; + Mem *pVal; + + vals = sqlite3_data_count(pStmt); + if( i>=vals || i<0 ){ + sqlite3Error(pVm->db, SQLITE_RANGE, 0); + return 0; + } + + pVal = &pVm->pTos[(1-vals)+i]; + return sqlite3_value_data16((sqlite3_value *)pVal); +} + /* ** Return the number of bytes of data that will be returned by the ** equivalent sqlite3_value_data() call. @@ -676,8 +699,7 @@ int sqlite3_value_bytes16(sqlite3_value *pVal){ */ long long int sqlite3_value_int(sqlite3_value *pVal){ Mem *pMem = (Mem *)pVal; - Integerify(pMem, flagsToEnc(pMem->flags)); - return pVal->i; + return MemInt(pMem); } /* @@ -686,8 +708,7 @@ long long int sqlite3_value_int(sqlite3_value *pVal){ */ double sqlite3_value_float(sqlite3_value *pVal){ Mem *pMem = (Mem *)pVal; - Realify(pMem, flagsToEnc(pMem->flags)); - return pMem->r; + return MemReal(pMem); } /* @@ -799,13 +820,13 @@ int sqlite3_value_type(sqlite3_value* pVal){ if( f&MEM_Null ){ return SQLITE3_NULL; } - if( f&MEM_Int ){ + if( f&MEM_TypeInt ){ return SQLITE3_INTEGER; } - if( f&MEM_Real ){ + if( f&MEM_TypeReal ){ return SQLITE3_FLOAT; } - if( f&MEM_Str ){ + if( f&MEM_TypeStr ){ return SQLITE3_TEXT; } if( f&MEM_Blob ){ @@ -1772,6 +1793,16 @@ case OP_String: { pTos++; pTos->flags = 0; + if( z ){ + /* FIX ME: For now the code in expr.c always puts UTF-8 in P3. It + ** should transform text to the native encoding before doing so. + */ + MemSetStr(pTos, z, -1, TEXT_Utf8, 0); + SetEncoding(pTos, encToFlags(db->enc)|MEM_Term); + }else if( op==OP_String ){ + pTos->flags = MEM_Null; + } + /* If this is an OP_Real or OP_Integer opcode, set the pTos->r or pTos->i ** values respectively. */ @@ -1779,34 +1810,13 @@ case OP_String: { assert( z ); assert( sqlite3IsNumber(z, 0, TEXT_Utf8) ); pTos->r = sqlite3AtoF(z, 0); - pTos->flags = MEM_Real; + pTos->flags |= MEM_Real; }else if( op==OP_Integer ){ - pTos->flags = MEM_Int; pTos->i = pOp->p1; if( pTos->i==0 && pOp->p3 ){ - sqlite3GetInt64(pOp->p3, &pTos->i); + sqlite3GetInt64(z, &pTos->i); } - } - - if( z ){ - /* FIX ME: For now the code in expr.c always puts UTF-8 in P3. It - ** should transform text to the native encoding before doing so. - */ - if( db->enc!=TEXT_Utf8 ){ - rc = sqlite3utfTranslate(z, -1, TEXT_Utf8, (void **)&pTos->z, - &pTos->n, db->enc); - if( rc!=SQLITE_OK ){ - assert( !pTos->z ); - goto abort_due_to_error; - } - pTos->flags |= MEM_Str | MEM_Dyn | MEM_Term; - }else{ - pTos->z = z; - pTos->n = strlen(z) + 1; - pTos->flags |= MEM_Str | MEM_Static | MEM_Term; - } - }else if( op==OP_String ){ - pTos->flags = MEM_Null; + pTos->flags |= MEM_Int; } break; @@ -2027,6 +2037,7 @@ case OP_Callback: { Mem *pVal = &pTos[0-i]; SetEncodingFlags(pVal, db->enc); MemNulTerminate(pVal); + MemSetTypeFlags(pVal); } p->resOnStack = 1; @@ -2277,6 +2288,7 @@ case OP_Function: { pArg = &pTos[1-n]; for(i=0; ienc); + MemSetTypeFlags(pArg); apVal[i] = pArg; } @@ -5624,8 +5636,9 @@ case OP_AggFunc: { assert( apVal || n==0 ); for(i=0; ienc); + apVal[i] = pRec; + SetEncodingFlags(pRec, db->enc); + MemSetTypeFlags(pRec); } i = pTos->i; assert( i>=0 && iagg.nMem ); diff --git a/src/vdbeInt.h b/src/vdbeInt.h index e1f5b4e9b7..733b333f6c 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -144,7 +144,7 @@ typedef struct Mem Mem; ** MemSetNull - Set the value to NULL. ** MemSetInt - Set the value to an integer. ** MemSetReal - Set the value to a real. -** MemSetStr - Set the value to a string. +** MemSetStr - Set the value to a string (or blob if enc==0). */ #define MemSetNull(p) sqlite3VdbeMemSetNull(p) #define MemSetInt(p,v) sqlite3VdbeMemSetInt(p,v) @@ -159,22 +159,21 @@ typedef struct Mem Mem; ** ** Non-zero is returned if a malloc() fails. */ -#define MemNulTerminate(p) ( \ - ((p)->flags&MEM_Str) && \ - !((p)->flags&MEM_Term) && \ - sqlite3VdbeMemNulTerminate(p) ) +#define MemNulTerminate(p) \ +if( ((p)->flags&MEM_Str) && !((p)->flags&MEM_Term) ) sqlite3VdbeMemNulTerminate(p); -/* -** Allowed values for Mem.flags. +/* One or more of the following flags are set to indicate the valid +** representations of the value stored in the Mem struct. ** -** The first 5 values determine the data type(s). Null and Blob must -** occur alone. But Str, Int, and Real can occur together. +** If the MEM_Null flag is set, then the value is an SQL NULL value. +** No other flags may be set in this case. ** -** The next 3 utf entries determine the text representation for strings. -** These values are only meaningful if the type is Str. -** -** The last 4 values specify what kind of memory Mem.z points to. -** These valus are only meaningful if the Str or Blob types are used. +** If the MEM_Str flag is set then Mem.z points at a string representation. +** Usually this is encoded in the same unicode encoding as the main +** database (see below for exceptions). If the MEM_Term flag is also +** set, then the string is nul terminated. The MEM_Int and MEM_Real +** flags may coexist with the MEM_Str flag. +** */ #define MEM_Null 0x0001 /* Value is NULL */ #define MEM_Str 0x0002 /* Value is a string */ @@ -182,12 +181,30 @@ typedef struct Mem Mem; #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ -#define MEM_Term 0x0200 /* String has a nul terminator character */ +#define MEM_Term 0x0100 /* String rep is nul terminated */ -#define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */ -#define MEM_Static 0x0800 /* Mem.z points to a static string */ -#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ -#define MEM_Short 0x2000 /* Mem.z points to Mem.zShort */ +/* Values with type NULL or BLOB can have only one representation. But +** values with a manifest type of REAL, INTEGER or STRING may have one +** or more representation cached in the Mem struct at any one time. The +** flags MEM_IntVal, MEM_RealVal and MEM_StrVal are true whenever the real, +** integer or string representation stored in a Mem struct is valid. +** +** When MEM_StrVal is set, then MEM_Term may also be set. This indicates +** that the string is terminated with a nul-terminator character. +*/ +#define MEM_TypeInt 0x0020 /* Value type is an integer */ +#define MEM_TypeReal 0x0040 /* Value type is real */ +#define MEM_TypeStr 0x0080 /* Value type is string */ + + +/* Whenever Mem contains a valid string or blob representation, one of +** the following flags must be set to determine the memory management +** policy for Mem.z +*/ +#define MEM_Dyn 0x0200 /* Need to call sqliteFree() on Mem.z */ +#define MEM_Static 0x0400 /* Mem.z points to a static string */ +#define MEM_Ephem 0x0800 /* Mem.z points to an ephemeral string */ +#define MEM_Short 0x1000 /* Mem.z points to Mem.zShort */ /* Internally, all strings manipulated by the VDBE are encoded using the ** native encoding for the main database. Therefore the following three @@ -214,15 +231,15 @@ typedef struct Mem Mem; ** sqlite3_column_data() or sqlite3_column_data16(). If this occurs, then ** the MEM_Utf* flags are updated accordingly. */ -#define MEM_Utf8 0x0040 /* String uses UTF-8 encoding */ -#define MEM_Utf16be 0x0080 /* String uses UTF-16 big-endian */ -#define MEM_Utf16le 0x0100 /* String uses UTF-16 little-endian */ +#define MEM_Utf8 0x2000 /* String uses UTF-8 encoding */ +#define MEM_Utf16be 0x4000 /* String uses UTF-16 big-endian */ +#define MEM_Utf16le 0x8000 /* String uses UTF-16 little-endian */ /* The following MEM_ value appears only in AggElem.aMem.s.flag fields. ** It indicates that the corresponding AggElem.aMem.z points to a ** aggregate function context that needs to be finalized. */ -#define MEM_AggCtx 0x4000 /* Mem.z points to an agg function context */ +#define MEM_AggCtx 0x10000 /* Mem.z points to an agg function context */ /* ** The "context" argument for a installable function. A pointer to an diff --git a/test/capi3.test b/test/capi3.test index ed5b2580f8..a5ab208c28 100644 --- a/test/capi3.test +++ b/test/capi3.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script testing the callback-free C/C++ API. # -# $Id: capi3.test,v 1.4 2004/05/26 10:11:07 danielk1977 Exp $ +# $Id: capi3.test,v 1.5 2004/05/26 13:27:00 danielk1977 Exp $ # set testdir [file dirname $argv0] @@ -178,7 +178,7 @@ do_test capi3-5.0 { execsql { CREATE TABLE t1(a VARINT, b BLOB, c VARCHAR(16)); INSERT INTO t1 VALUES(1, 2, 3); - INSERT INTO t1 VALUES('one', 'two', 'three'); + INSERT INTO t1 VALUES('one', 'two', NULL); } set sql "SELECT * FROM t1" set STMT [sqlite3_prepare $DB $sql -1 TAIL] @@ -208,6 +208,7 @@ do_test capi3-5.4 { set types } {INTEGER INTEGER TEXT} +if 0 { do_test capi3-5.5 { set ints [list] foreach i {0 1 2} {lappend ints [sqlite3_column_int $STMT $i]} @@ -219,6 +220,7 @@ do_test capi3-5.6 { foreach i {0 1 2} {lappend utf8 [sqlite3_column_data $STMT $i]} set utf8 } {1 2 3} +} do_test capi3-5.7 { set utf8 [list] @@ -242,7 +244,7 @@ do_test capi3-5.10 { set types [list] foreach i {0 1 2} {lappend types [sqlite3_column_type $STMT $i]} set types -} {TEXT TEXT TEXT} +} {TEXT TEXT NULL} do_test capi3-5.11 { set ints [list] @@ -254,19 +256,19 @@ do_test capi3-5.12 { set utf8 [list] foreach i {0 1 2} {lappend utf8 [sqlite3_column_data $STMT $i]} set utf8 -} {one two three} +} {one two {}} do_test capi3-5.13 { set utf8 [list] foreach i {0 1 2} {lappend utf8 [utf8 [sqlite3_column_data16 $STMT $i]]} set utf8 -} {one two three} +} {one two {}} do_test capi3-5.14 { set types [list] foreach i {0 1 2} {lappend types [sqlite3_column_type $STMT $i]} set types -} {TEXT TEXT TEXT} +} {TEXT TEXT NULL} do_test capi3-5.15 { sqlite3_step $STMT