Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -11,11 +11,11 @@ ************************************************************************* ** Code for testing all sorts of SQLite interfaces. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.241 2007/05/02 01:34:31 drh Exp $ +** $Id: test1.c,v 1.242 2007/05/02 16:51:59 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include "os.h" #include @@ -4347,10 +4347,11 @@ extern int sqlite3_search_count; extern int sqlite3_interrupt_count; extern int sqlite3_open_file_count; extern int sqlite3_sort_count; extern int sqlite3_current_time; + extern int sqlite3_max_blobsize; static struct { char *zName; Tcl_CmdProc *xProc; } aCmd[] = { { "sqlite3_mprintf_int", (Tcl_CmdProc*)sqlite3_mprintf_int }, @@ -4540,10 +4541,12 @@ } Tcl_LinkVar(interp, "sqlite_search_count", (char*)&sqlite3_search_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_sort_count", (char*)&sqlite3_sort_count, TCL_LINK_INT); + Tcl_LinkVar(interp, "sqlite3_max_blobsize", + (char*)&sqlite3_max_blobsize, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_like_count", (char*)&sqlite3_like_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_interrupt_count", (char*)&sqlite3_interrupt_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_open_file_count", Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -41,11 +41,11 @@ ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** 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.604 2007/05/02 13:30:27 drh Exp $ +** $Id: vdbe.c,v 1.605 2007/05/02 16:51:59 drh Exp $ */ #include "sqliteInt.h" #include "os.h" #include #include "vdbeInt.h" @@ -82,10 +82,21 @@ */ #ifdef SQLITE_TEST int sqlite3_sort_count = 0; #endif +/* +** The next global variable records the size of the largest MEM_Blob +** or MEM_Str that has appeared on the VDBE stack. The test procedures +** use this information to make sure that the zero-blob functionality +** is working correctly. This variable has no function other than to +** help verify the correct operation of the library. +*/ +#ifdef SQLITE_TEST +int sqlite3_max_blobsize = 0; +#endif + /* ** Release the memory associated with the given stack level. This ** leaves the Mem.flags field in an inconsistent state. */ #define Release(P) if((P)->flags&MEM_Dyn){ sqlite3VdbeMemRelease(P); } @@ -1631,10 +1642,12 @@ applyAffinity(pNos, affinity, encoding); applyAffinity(pTos, affinity, encoding); } assert( pOp->p3type==P3_COLLSEQ || pOp->p3==0 ); + sqlite3VdbeMemExpandBlob(pNos); + sqlite3VdbeMemExpandBlob(pTos); res = sqlite3MemCompare(pNos, pTos, (CollSeq*)pOp->p3); switch( pOp->opcode ){ case OP_Eq: res = res==0; break; case OP_Ne: res = res!=0; break; case OP_Lt: res = res<0; break; @@ -2221,25 +2234,27 @@ /* Loop through the elements that will make up the record to figure ** out how much space is required for the new record. */ for(pRec=pData0; pRec<=pTos; pRec++){ + int len; if( zAffinity ){ applyAffinity(pRec, zAffinity[pRec-pData0], encoding); } if( pRec->flags&MEM_Null ){ containsNull = 1; } serial_type = sqlite3VdbeSerialType(pRec, file_format); - nData += sqlite3VdbeSerialTypeLen(serial_type); + len = sqlite3VdbeSerialTypeLen(serial_type); + nData += len; nHdr += sqlite3VarintLen(serial_type); if( pRec->flags & MEM_Zero ){ /* Only pure zero-filled BLOBs can be input to this Opcode. ** We do not allow blobs with a prefix and a zero-filled tail. */ assert( pRec->n==0 ); nZero += pRec->u.i; - }else{ + }else if( len ){ nZero = 0; } } /* If we have to append a varint rowid to this record, set pRowid @@ -2875,11 +2890,11 @@ } pC->lastRowid = pTos->u.i; pC->rowidIsValid = res==0; }else{ assert( pTos->flags & MEM_Blob ); - /* Stringify(pTos, encoding); */ + sqlite3VdbeMemExpandBlob(pTos); rc = sqlite3BtreeMoveto(pC->pCursor, pTos->z, pTos->n, 0, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } pC->rowidIsValid = 0; @@ -2982,10 +2997,11 @@ assert( i>=0 && inCursor ); assert( p->apCsr[i]!=0 ); if( (pC = p->apCsr[i])->pCursor!=0 ){ int res, rx; assert( pC->isTable==0 ); + assert( pTos->flags & MEM_Blob ); Stringify(pTos, encoding); rx = sqlite3BtreeMoveto(pC->pCursor, pTos->z, pTos->n, 0, &res); alreadyExists = rx==SQLITE_OK && res==0; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; @@ -3049,10 +3065,11 @@ int len; /* Number of bytes in K without the rowid at the end */ int szRowid; /* Size of the rowid column at the end of zKey */ /* Make sure K is a string and make zKey point to K */ + assert( pNos->flags & MEM_Blob ); Stringify(pNos, encoding); zKey = pNos->z; nKey = pNos->n; szRowid = sqlite3VdbeIdxRowidLen((u8*)zKey); @@ -3900,13 +3917,13 @@ assert( p->apCsr[i]!=0 ); assert( pTos>=p->aStack ); if( (pC = p->apCsr[i])->pCursor!=0 ){ int res; - assert( pTos->flags & MEM_Blob ); /* Created using OP_Make*Key */ - Stringify(pTos, encoding); + assert( pTos->flags & MEM_Blob ); /* Created using OP_MakeRecord */ assert( pC->deferredMoveto==0 ); + sqlite3VdbeMemExpandBlob(pTos); *pC->pIncrKey = pOp->p3!=0; assert( pOp->p3==0 || pOp->opcode!=OP_IdxGT ); rc = sqlite3VdbeIdxKeyCompare(pC, pTos->n, (u8*)pTos->z, &res); *pC->pIncrKey = 0; if( rc!=SQLITE_OK ){ @@ -4979,10 +4996,20 @@ fprintf(stdout, "%10lld ", elapse); sqlite3VdbePrintOp(stdout, origPc, &p->aOp[origPc]); #endif } #endif + +#ifdef SQLITE_TEST + /* Keep track of the size of the largest BLOB or STR that has appeared + ** on the top of the VDBE stack. + */ + if( pTos>=p->aStack && (pTos->flags & (MEM_Blob|MEM_Str))!=0 + && pTos->n>sqlite3_max_blobsize ){ + sqlite3_max_blobsize = pTos->n; + } +#endif /* The following code adds nothing to the actual functionality ** of the program. It is only here for testing and debugging. ** On the other hand, it does burn CPU cycles every time through ** the evaluator loop. So we can leave it out when NDEBUG is defined. @@ -4996,10 +5023,11 @@ if( pTos>=p->aStack && pTos->flags ){ sqlite3VdbeMemSanity(pTos); } assert( pc>=-1 && pcnOp ); #ifdef SQLITE_DEBUG + /* Code for tracing the vdbe stack. */ if( p->trace && pTos>=p->aStack ){ int i; fprintf(p->trace, "Stack:"); for(i=0; i>-5 && &pTos[i]>=p->aStack; i--){ Index: test/zeroblob.test ================================================================== --- test/zeroblob.test +++ test/zeroblob.test @@ -11,48 +11,109 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing of the zero-filled blob functionality # including the sqlite3_bind_zeroblob(), sqlite3_result_zeroblob(), # and the built-in zeroblob() SQL function. # -# $Id: zeroblob.test,v 1.1 2007/05/02 13:30:27 drh Exp $ +# $Id: zeroblob.test,v 1.2 2007/05/02 16:51:59 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl -# Create the database +# When zeroblob() is used for the last field of a column, then the +# content of the zeroblob is never instantiated on the VDBE stack. +# But it does get inserted into the database correctly. # do_test zeroblob-1.1 { execsql { CREATE TABLE t1(a,b,c,d); - INSERT INTO t1 VALUES(1,2,3,zeroblob(10000)); - SELECT count(*) FROM t1; + } + set ::sqlite3_max_blobsize 0 + execsql { + INSERT INTO t1 VALUES(2,3,4,zeroblob(10000)); } -} {1} + set ::sqlite3_max_blobsize +} {10} do_test zeroblob-1.2 { execsql { SELECT length(d) FROM t1 } } {10000} + +# If a non-NULL column follows the zeroblob, then the content of +# the zeroblob must be instantiated. +# do_test zeroblob-1.3 { + set ::sqlite3_max_blobsize 0 execsql { - INSERT INTO t1 VALUES(2,3,zeroblob(10000),4); - SELECT count(*) FROM t1; + INSERT INTO t1 VALUES(3,4,zeroblob(10000),5); } -} {2} + set ::sqlite3_max_blobsize +} {10010} do_test zeroblob-1.4 { execsql { SELECT length(c), length(d) FROM t1 } } {1 10000 10000 1} + +# Multiple zeroblobs can appear at the end of record. No instantiation +# of the blob content occurs on the stack. +# do_test zeroblob-1.5 { + set ::sqlite3_max_blobsize 0 execsql { - INSERT INTO t1 VALUES(3,4,zeroblob(10000),zeroblob(10000)); - SELECT count(*) FROM t1; + INSERT INTO t1 VALUES(4,5,zeroblob(10000),zeroblob(10000)); } -} {3} + set ::sqlite3_max_blobsize +} {11} do_test zeroblob-1.6 { execsql { SELECT length(c), length(d) FROM t1 } } {1 10000 10000 1 10000 10000} + +# NULLs can follow the zeroblob() or be intermixed with zeroblobs and +# no instantiation of the zeroblobs occurs on the stack. +# +do_test zeroblob-1.7 { + set ::sqlite3_max_blobsize 0 + execsql { + INSERT INTO t1 VALUES(5,zeroblob(10000),NULL,zeroblob(10000)); + } + set ::sqlite3_max_blobsize +} {10} +do_test zeroblob-1.8 { + execsql { + SELECT length(b), length(d) FROM t1 WHERE a=5 + } +} {10000 10000} + +# Comparisons against zeroblobs work. +# +do_test zeroblob-2.1 { + execsql { + SELECT a FROM t1 WHERE b=zeroblob(10000) + } +} {5} + +# Comparisons against zeroblobs work even when indexed. +# +do_test zeroblob-2.2 { + execsql { + CREATE INDEX i1_1 ON t1(b); + SELECT a FROM t1 WHERE b=zeroblob(10000); + } +} {5} + +# DISTINCT works for zeroblobs +# +do_test zeroblob-3.1 { + execsql { + SELECT count(DISTINCT a) FROM ( + SELECT x'00000000000000000000' AS a + UNION ALL + SELECT zeroblob(10) AS a + ) + } +} {1} + finish_test