diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/sqliteInt.h | 3 | ||||
-rw-r--r-- | src/test1.c | 242 | ||||
-rw-r--r-- | src/utf.c | 10 | ||||
-rw-r--r-- | src/vdbe.c | 63 | ||||
-rw-r--r-- | src/vdbeInt.h | 2 | ||||
-rw-r--r-- | src/vdbeaux.c | 38 |
6 files changed, 337 insertions, 21 deletions
diff --git a/src/sqliteInt.h b/src/sqliteInt.h index afea0e465..f757899f4 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.238 2004/05/19 20:41:03 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.239 2004/05/20 01:12:34 danielk1977 Exp $ */ #include "config.h" #include "sqlite.h" @@ -1321,6 +1321,7 @@ void *sqlite3utf8to16be(const unsigned char *pIn, int N); void *sqlite3utf8to16le(const unsigned char *pIn, int N); void sqlite3utf16to16le(void *pData, int N); void sqlite3utf16to16be(void *pData, int N); +int sqlite3utf16ByteLen(const void *pData); int sqlite3PutVarint(unsigned char *, u64); int sqlite3GetVarint(const unsigned char *, u64 *); int sqlite3GetVarint32(const unsigned char *, u32 *); diff --git a/src/test1.c b/src/test1.c index 1fe133d76..1a55fd3b2 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.40 2004/05/13 11:34:16 danielk1977 Exp $ +** $Id: test1.c,v 1.41 2004/05/20 01:12:35 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -52,6 +52,21 @@ static int getVmPointer(Tcl_Interp *interp, const char *zArg, sqlite_vm **ppVm){ } /* +** Decode a pointer to an sqlite3_stmt object. +*/ +static int getStmtPointer( + Tcl_Interp *interp, + const char *zArg, + sqlite3_stmt **ppStmt +){ + if( sscanf(zArg, PTR_FMT, (void**)ppStmt)!=1 ){ + Tcl_AppendResult(interp, "\"", zArg, "\" is not a valid pointer value", 0); + return TCL_ERROR; + } + return TCL_OK; +} + +/* ** Generate a text representation of a pointer that can be understood ** by the getDbPointer and getVmPointer routines above. ** @@ -967,6 +982,216 @@ static int test_breakpoint( return TCL_OK; /* Do nothing */ } +static int test_bind_int32( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt; + int idx; + int value; + int rc; + + if( objc!=4 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.> <value>", 0); + return TCL_ERROR; + } + + if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[3], &value) ) return TCL_ERROR; + + rc = sqlite3_bind_int32(pStmt, idx, value); + if( rc!=SQLITE_OK ){ + return TCL_ERROR; + } + + return TCL_OK; +} + +static int test_bind_int64( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt; + int idx; + i64 value; + int rc; + + if( objc!=4 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.> <value>", 0); + return TCL_ERROR; + } + + if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; + if( Tcl_GetWideIntFromObj(interp, objv[3], &value) ) return TCL_ERROR; + + rc = sqlite3_bind_int64(pStmt, idx, value); + if( rc!=SQLITE_OK ){ + return TCL_ERROR; + } + + return TCL_OK; +} + +static int test_bind_double( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt; + int idx; + double value; + int rc; + + if( objc!=4 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.> <value>", 0); + return TCL_ERROR; + } + + if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; + if( Tcl_GetDoubleFromObj(interp, objv[3], &value) ) return TCL_ERROR; + + rc = sqlite3_bind_double(pStmt, idx, value); + if( rc!=SQLITE_OK ){ + return TCL_ERROR; + } + + return TCL_OK; +} + +static int test_bind_null( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt; + int idx; + int rc; + + if( objc!=3 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.>", 0); + return TCL_ERROR; + } + + if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; + + rc = sqlite3_bind_null(pStmt, idx); + if( rc!=SQLITE_OK ){ + return TCL_ERROR; + } + + return TCL_OK; +} + +static int test_bind_text( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt; + int idx; + int bytes; + char *value; + int rc; + + if( objc!=5 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.> <value>" + " <bytes>", 0); + return TCL_ERROR; + } + + if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; + value = Tcl_GetString(objv[3]); + if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR; + + rc = sqlite3_bind_text(pStmt, idx, value, bytes, 1); + if( rc!=SQLITE_OK ){ + return TCL_ERROR; + } + + return TCL_OK; +} + +static int test_bind_text16( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt; + int idx; + int bytes; + char *value; + int rc; + + if( objc!=5 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.> <value>" + " <bytes>", 0); + return TCL_ERROR; + } + + if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; + value = Tcl_GetByteArrayFromObj(objv[3], 0); + if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR; + + rc = sqlite3_bind_text16(pStmt, idx, (void *)value, bytes, 1); + if( rc!=SQLITE_OK ){ + return TCL_ERROR; + } + + return TCL_OK; +} + +static int test_bind_blob( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt; + int idx; + int bytes; + char *value; + int rc; + + if( objc!=5 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.> <value>" + " <bytes>", 0); + return TCL_ERROR; + } + + if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; + value = Tcl_GetString(objv[3]); + if( Tcl_GetIntFromObj(interp, objv[2], &bytes) ) return TCL_ERROR; + + rc = sqlite3_bind_blob(pStmt, idx, value, bytes, 1); + if( rc!=SQLITE_OK ){ + return TCL_ERROR; + } + + return TCL_OK; +} + /* ** Register commands with the TCL interpreter. */ @@ -1005,11 +1230,26 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite_reset", (Tcl_CmdProc*)test_reset }, { "breakpoint", (Tcl_CmdProc*)test_breakpoint }, }; + static struct { + char *zName; + Tcl_ObjCmdProc *xProc; + } aObjCmd[] = { + { "sqlite3_bind_int32", (Tcl_ObjCmdProc*)test_bind_int32 }, + { "sqlite3_bind_int64", (Tcl_ObjCmdProc*)test_bind_int64 }, + { "sqlite3_bind_double", (Tcl_ObjCmdProc*)test_bind_double }, + { "sqlite3_bind_null", (Tcl_ObjCmdProc*)test_bind_null }, + { "sqlite3_bind_text", (Tcl_ObjCmdProc*)test_bind_text }, + { "sqlite3_bind_text16", (Tcl_ObjCmdProc*)test_bind_text16 }, + { "sqlite3_bind_blob", (Tcl_ObjCmdProc*)test_bind_blob }, + }; int i; for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); } + for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ + Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0); + } Tcl_LinkVar(interp, "sqlite_search_count", (char*)&sqlite3_search_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_interrupt_count", @@ -12,7 +12,7 @@ ** This file contains routines used to translate between UTF-8, ** UTF-16, UTF-16BE, and UTF-16LE. ** -** $Id: utf.c,v 1.4 2004/05/19 10:34:57 danielk1977 Exp $ +** $Id: utf.c,v 1.5 2004/05/20 01:12:35 danielk1977 Exp $ ** ** Notes on UTF-8: ** @@ -327,14 +327,14 @@ static int writeUtf16(UtfString *pStr, int code, int big_endian){ ** Return the number of bytes up to (but not including) the first \u0000 ** character in *pStr. */ -static int utf16Bytelen(const unsigned char *pZ){ +int sqlite3utf16ByteLen(const void *pZ){ const unsigned char *pC1 = pZ; const unsigned char *pC2 = pZ+1; while( *pC1 || *pC2 ){ pC1 += 2; pC2 += 2; } - return pC1-pZ; + return pC1-(unsigned char *)pZ; } /* @@ -359,7 +359,7 @@ unsigned char *sqlite3utf16to8(const void *pData, int N){ in.c = 0; if( in.n<0 ){ - in.n = utf16Bytelen(in.pZ); + in.n = sqlite3utf16ByteLen(in.pZ); } /* A UTF-8 encoding of a unicode string can require at most 1.5 times as @@ -447,7 +447,7 @@ static void utf16to16(void *pData, int N, int big_endian){ inout.n = N; if( inout.n<0 ){ - inout.n = utf16Bytelen(inout.pZ); + inout.n = sqlite3utf16ByteLen(inout.pZ); } if( readUtf16Bom(&inout)!=big_endian ){ diff --git a/src/vdbe.c b/src/vdbe.c index 5bcaea930..be4b20d8b 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.305 2004/05/19 20:41:03 drh Exp $ +** $Id: vdbe.c,v 1.306 2004/05/20 01:12:35 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -189,6 +189,51 @@ static AggElem *_AggInFocus(Agg *p){ return pElem ? sqliteHashData(pElem) : 0; } +#define NulTermify(P) if(((P)->flags & MEM_Str)==0){hardStringify(P);} \ + else if(((P)->flags & MEM_Term)==0){hardNulTermify(P);} +static int hardNulTermify(Mem *pStack){ + int flags = pStack->flags; + + assert( !(flags&MEM_Term) && (flags&MEM_Str) ); + assert( flags&(MEM_Utf8|MEM_Utf16le|MEM_Utf16be) ); + + if( flags&MEM_Utf8 ){ + /* If the string is already dynamically allocated, use sqliteRealloc() + ** to allocate extra space for the terminator. + */ + if( flags&MEM_Dyn ){ + pStack->z = sqliteRealloc(pStack->z, pStack->n+1); + if( !pStack->z ){ + return 1; + } + } + + if( flags&(MEM_Static|MEM_Ephem|MEM_Short) ){ + if( pStack->n+1<NBFS ){ + if( flags&MEM_Short ){ + memcpy(pStack->zShort, pStack->z, pStack->n); + pStack->flags = MEM_Short|MEM_Str|MEM_Utf8|MEM_Term; + } + }else{ + char *z = sqliteMalloc(pStack->n+1); + if( !z ){ + return 1; + } + memcpy(z, pStack->z, pStack->n); + pStack->z = z; + pStack->flags = MEM_Dyn|MEM_Str|MEM_Utf8|MEM_Term; + } + } + + pStack->z[pStack->n] = '\0'; + pStack->n++; + }else{ + assert(0); + } + + return 0; +} + /* ** Convert the given stack entity into a string if it isn't one ** already. @@ -205,7 +250,7 @@ static int hardStringify(Mem *pStack){ } pStack->z = pStack->zShort; pStack->n = strlen(pStack->zShort)+1; - pStack->flags = MEM_Str | MEM_Short; + pStack->flags = MEM_Str | MEM_Short | MEM_Term; return 0; } @@ -834,7 +879,7 @@ case OP_Variable: { ** variable is used again, even after the virtual machine is reset, the ** conversion won't have to be done again. ** - ** TODO: This is where we need to support databases that use other than + ** FIX ME: This is where we need to support databases that use other than ** UTF-8 on disk. */ pVar = &p->apVar[j]; @@ -848,7 +893,17 @@ case OP_Variable: { Release(pVar); pVar->z = zUtf8; pVar->n = strlen(zUtf8)+1; - pVar->flags = MEM_Str|MEM_Dyn; + pVar->flags = MEM_Str|MEM_Dyn|MEM_Utf8|MEM_Term; + } + + /* Ensure that the variable value is nul terminated. Again, do this in + ** place. + ** + ** FIX ME: The rest of the vdbe will soon understand MEM_Term, making + ** this step unnecessary. + */ + if( pVar->flags&MEM_Str ){ + NulTermify(pVar); } /* Copy the value in pVar to the top of the stack. If pVar is a string or diff --git a/src/vdbeInt.h b/src/vdbeInt.h index f86a4aff3..4f5d0f714 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -146,6 +146,8 @@ typedef struct Mem Mem; #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ +#define MEM_Term 0x1000 /* String has a nul terminator character */ + #define MEM_Utf8 0x0020 /* String uses UTF-8 encoding */ #define MEM_Utf16be 0x0040 /* String uses UTF-16 big-endian */ #define MEM_Utf16le 0x0080 /* String uses UTF-16 little-endian */ diff --git a/src/vdbeaux.c b/src/vdbeaux.c index a8f04a5e9..03757e387 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1043,10 +1043,16 @@ int sqlite3_bind_text( int nData, int eCopy ){ - if( zData && nData<0 ){ - nData = strlen(zData)+1; + int flags = MEM_Str|MEM_Utf8; + if( zData ){ + if( nData<0 ){ + nData = strlen(zData)+1; + flags |= MEM_Term; + }else if( !zData[nData-1] ){ + flags |= MEM_Term; + } } - return vdbeBindBlob((Vdbe *)p, i, zData, nData, eCopy, MEM_Str|MEM_Utf8); + return vdbeBindBlob((Vdbe *)p, i, zData, nData, eCopy, flags); } int sqlite3_bind_text16( @@ -1056,14 +1062,26 @@ int sqlite3_bind_text16( int nData, int eCopy ){ - if( zData && nData<0 ){ - char *z = (char *)zData; - while( (*z)!=0 && (*(z+1))!=0 ) z+=2; - nData = (z - (char *)zData) + 2; - } + int flags = MEM_Str|MEM_Utf16le|MEM_Utf16be; + + if( zData ){ + /* If nData is less than zero, measure the length of the string. + ** manually. In this case the variable will always be null terminated. + */ + if( nData<0 ){ + nData = sqlite3utf16ByteLen(zData) + 2; + flags |= MEM_Term; + }else{ + /* If nData is greater than zero, check if the final character appears + ** to be a terminator. + */ + if( !(((u8 *)zData)[nData-1]) && !(((u8 *)zData)[nData-2]) ){ + flags |= MEM_Term; + } + } + } - /* FIX ME - MEM_Utf16le? */ - return vdbeBindBlob((Vdbe *)p, i, zData, nData, eCopy, MEM_Str|MEM_Utf16le); + return vdbeBindBlob((Vdbe *)p, i, zData, nData, eCopy, flags); } int sqlite3_bind_blob( |