aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/sqliteInt.h3
-rw-r--r--src/test1.c242
-rw-r--r--src/utf.c10
-rw-r--r--src/vdbe.c63
-rw-r--r--src/vdbeInt.h2
-rw-r--r--src/vdbeaux.c38
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",
diff --git a/src/utf.c b/src/utf.c
index 74c560986..461c641a3 100644
--- a/src/utf.c
+++ b/src/utf.c
@@ -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(