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.603 2007/05/02 01:34:32 drh Exp $ +** $Id: vdbe.c,v 1.604 2007/05/02 13:30:27 drh Exp $ */ #include "sqliteInt.h" #include "os.h" #include #include "vdbeInt.h" @@ -309,19 +309,22 @@ } zCsr += sprintf(zCsr, "%c", c); zCsr += sprintf(zCsr, "%d[", pMem->n); for(i=0; i<16 && in; i++){ - zCsr += sprintf(zCsr, "%02X ", ((int)pMem->z[i] & 0xFF)); + zCsr += sprintf(zCsr, "%02X", ((int)pMem->z[i] & 0xFF)); } for(i=0; i<16 && in; i++){ char z = pMem->z[i]; if( z<32 || z>126 ) *zCsr++ = '.'; else *zCsr++ = z; } zCsr += sprintf(zCsr, "]"); + if( f & MEM_Zero ){ + zCsr += sprintf(zCsr,"+%lldz",pMem->u.i); + } *zCsr = '\0'; }else if( f & MEM_Str ){ int j, k; zBuf[0] = ' '; if( f & MEM_Dyn ){ @@ -2183,29 +2186,29 @@ ** Each type field is a varint representing the serial type of the ** corresponding data element (see sqlite3VdbeSerialType()). The ** hdr-size field is also a varint which is the offset from the beginning ** of the record to data0. */ - unsigned char *zNewRecord; - unsigned char *zCsr; - Mem *pRec; - Mem *pRowid = 0; + u8 *zNewRecord; /* A buffer to hold the data for the new record */ + Mem *pRec; /* The new record */ + Mem *pRowid = 0; /* Rowid appended to the new record */ int nData = 0; /* Number of bytes of data space */ int nHdr = 0; /* Number of bytes of header space */ - int nByte = 0; /* Space required for this record */ + int nByte = 0; /* Data space required for this record */ + int nZero = 0; /* Number of zero bytes at the end of the record */ int nVarint; /* Number of bytes in a varint */ u32 serial_type; /* Type field */ int containsNull = 0; /* True if any of the data fields are NULL */ - char zTemp[NBFS]; /* Space to hold small records */ - Mem *pData0; - + Mem *pData0; /* Bottom of the stack */ int leaveOnStack; /* If true, leave the entries on the stack */ int nField; /* Number of fields in the record */ int jumpIfNull; /* Jump here if non-zero and any entries are NULL. */ int addRowid; /* True to append a rowid column at the end */ char *zAffinity; /* The affinity string for the record */ int file_format; /* File format to use for encoding */ + int i; /* Space used in zNewRecord[] */ + char zTemp[NBFS]; /* Space to hold small records */ leaveOnStack = ((pOp->p1<0)?1:0); nField = pOp->p1 * (leaveOnStack?-1:1); jumpIfNull = pOp->p2; addRowid = pOp->opcode==OP_MakeIdxRec; @@ -2227,31 +2230,40 @@ containsNull = 1; } serial_type = sqlite3VdbeSerialType(pRec, file_format); nData += sqlite3VdbeSerialTypeLen(serial_type); 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{ + nZero = 0; + } } - /* If we have to append a varint rowid to this record, set 'rowid' + /* If we have to append a varint rowid to this record, set pRowid ** to the value of the rowid and increase nByte by the amount of space - ** required to store it and the 0x00 seperator byte. + ** required to store it. */ if( addRowid ){ pRowid = &pTos[0-nField]; assert( pRowid>=p->aStack ); sqlite3VdbeMemIntegerify(pRowid); serial_type = sqlite3VdbeSerialType(pRowid, 0); nData += sqlite3VdbeSerialTypeLen(serial_type); nHdr += sqlite3VarintLen(serial_type); + nZero = 0; } /* Add the initial header varint and total the size */ nHdr += nVarint = sqlite3VarintLen(nHdr); if( nVarintsizeof(zTemp) ){ zNewRecord = sqliteMallocRaw(nByte); if( !zNewRecord ){ @@ -2260,26 +2272,25 @@ }else{ zNewRecord = (u8*)zTemp; } /* Write the record */ - zCsr = zNewRecord; - zCsr += sqlite3PutVarint(zCsr, nHdr); - for(pRec=pData0; pRec<=pTos; pRec++){ - serial_type = sqlite3VdbeSerialType(pRec, file_format); - zCsr += sqlite3PutVarint(zCsr, serial_type); /* serial type */ - } - if( addRowid ){ - zCsr += sqlite3PutVarint(zCsr, sqlite3VdbeSerialType(pRowid, 0)); - } - for(pRec=pData0; pRec<=pTos; pRec++){ - zCsr += sqlite3VdbeSerialPut(zCsr, pRec, file_format); /* serial data */ - } - if( addRowid ){ - zCsr += sqlite3VdbeSerialPut(zCsr, pRowid, 0); - } - assert( zCsr==(zNewRecord+nByte) ); + i = sqlite3PutVarint(zNewRecord, nHdr); + for(pRec=pData0; pRec<=pTos; pRec++){ + serial_type = sqlite3VdbeSerialType(pRec, file_format); + i += sqlite3PutVarint(&zNewRecord[i], serial_type); /* serial type */ + } + if( addRowid ){ + i += sqlite3PutVarint(&zNewRecord[i], sqlite3VdbeSerialType(pRowid, 0)); + } + for(pRec=pData0; pRec<=pTos; pRec++){ /* serial data */ + i += sqlite3VdbeSerialPut(&zNewRecord[i], nByte-i, pRec, file_format); + } + if( addRowid ){ + i += sqlite3VdbeSerialPut(&zNewRecord[i], nByte-i, pRowid, 0); + } + assert( i==nByte ); /* Pop entries off the stack if required. Push the new record on. */ if( !leaveOnStack ){ popStack(&pTos, nField+addRowid); } @@ -2294,10 +2305,14 @@ assert( zNewRecord!=(unsigned char *)zTemp ); pTos->z = (char*)zNewRecord; pTos->flags = MEM_Blob | MEM_Dyn; pTos->xDel = 0; } + if( nZero ){ + pTos->u.i = nZero; + pTos->flags |= MEM_Zero; + } pTos->enc = SQLITE_UTF8; /* In case the blob is ever converted to text */ /* If a NULL was encountered and jumpIfNull is non-zero, take the jump. */ if( jumpIfNull && containsNull ){ pc = jumpIfNull - 1; @@ -4995,11 +5010,11 @@ }else if( pTos[i].flags & MEM_Int ){ fprintf(p->trace, " i:%lld", pTos[i].u.i); }else if( pTos[i].flags & MEM_Real ){ fprintf(p->trace, " r:%g", pTos[i].r); }else{ - char zBuf[100]; + char zBuf[200]; sqlite3VdbeMemPrettyPrint(&pTos[i], zBuf); fprintf(p->trace, " "); fprintf(p->trace, "%s", zBuf); } } Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -131,12 +131,12 @@ } u; double r; /* Real value */ char *z; /* String or BLOB value */ int n; /* Number of characters in string value, including '\0' */ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ - u8 type; /* One of MEM_Null, MEM_Str, etc. */ - u8 enc; /* TEXT_Utf8, TEXT_Utf16le, or TEXT_Utf16be */ + u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */ + u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ void (*xDel)(void *); /* If not null, call this function to delete Mem.z */ char zShort[NBFS]; /* Space for short strings */ }; typedef struct Mem Mem; @@ -362,11 +362,11 @@ #ifdef SQLITE_DEBUG void sqlite3VdbePrintSql(Vdbe*); #endif int sqlite3VdbeSerialTypeLen(u32); u32 sqlite3VdbeSerialType(Mem*, int); -int sqlite3VdbeSerialPut(unsigned char*, Mem*, int); +int sqlite3VdbeSerialPut(unsigned char*, int, Mem*, int); int sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); void sqlite3VdbeDeleteAuxData(VdbeFunc*, int); int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); int sqlite3VdbeIdxKeyCompare(Cursor*, int , const unsigned char*, int*); Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -1720,10 +1720,11 @@ /* ** Return the serial-type for the value stored in pMem. */ u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){ int flags = pMem->flags; + int n; if( flags&MEM_Null ){ return 0; } if( flags&MEM_Int ){ @@ -1743,17 +1744,17 @@ return 6; } if( flags&MEM_Real ){ return 7; } - if( flags&MEM_Str ){ - int n = pMem->n; - assert( n>=0 ); - return ((n*2) + 13); + assert( flags&(MEM_Str|MEM_Blob) ); + n = pMem->n; + if( flags & MEM_Zero ){ + n += pMem->u.i; } - assert( (flags & MEM_Blob)!=0 ); - return (pMem->n*2 + 12); + assert( n>=0 ); + return ((n*2) + 12 + ((flags&MEM_Str)!=0)); } /* ** Return the length of the data corresponding to the supplied serial-type. */ @@ -1768,12 +1769,25 @@ /* ** Write the serialized data blob for the value stored in pMem into ** buf. It is assumed that the caller has allocated sufficient space. ** Return the number of bytes written. +** +** nBuf is the amount of space left in buf[]. nBuf must always be +** large enough to hold the entire field. Except, if the field is +** a blob with a zero-filled tail, then buf[] might be just the right +** size to hold everything except for the zero-filled tail. If buf[] +** is only big enough to hold the non-zero prefix, then only write that +** prefix into buf[]. But if buf[] is large enough to hold both the +** prefix and the tail then write the prefix and set the tail to all +** zeros. +** +** Return the number of bytes actually written into buf[]. The number +** of bytes in the zero-filled tail is included in the return value only +** if those bytes were zeroed in buf[]. */ -int sqlite3VdbeSerialPut(unsigned char *buf, Mem *pMem, int file_format){ +int sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_format){ u32 serial_type = sqlite3VdbeSerialType(pMem, file_format); int len; /* Integer and Real */ if( serial_type<=7 && serial_type>0 ){ @@ -1784,21 +1798,32 @@ memcpy(&v, &pMem->r, sizeof(v)); }else{ v = pMem->u.i; } len = i = sqlite3VdbeSerialTypeLen(serial_type); + assert( len<=nBuf ); while( i-- ){ buf[i] = (v&0xFF); v >>= 8; } return len; } /* String or blob */ if( serial_type>=12 ){ - len = sqlite3VdbeSerialTypeLen(serial_type); + assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.i:0) + == sqlite3VdbeSerialTypeLen(serial_type) ); + assert( pMem->n<=nBuf ); + len = pMem->n; memcpy(buf, pMem->z, len); + if( pMem->flags & MEM_Zero ){ + len += pMem->u.i; + if( len>nBuf ){ + len = nBuf; + } + memset(&buf[pMem->n], 0, len-pMem->n); + } return len; } /* NULL or constants 0 or 1 */ return 0; Index: src/vdbemem.c ================================================================== --- src/vdbemem.c +++ src/vdbemem.c @@ -82,25 +82,26 @@ pMem->flags &= ~(MEM_Ephem|MEM_Static|MEM_Short); return SQLITE_OK; } /* -** If the given Mem* is a zero-filled blob, turn it into an ordinary +** If the given Mem* has a zero-filled tail, turn it into an ordinary ** blob stored in dynamically allocated space. */ int sqlite3VdbeMemExpandBlob(Mem *pMem){ if( pMem->flags & MEM_Zero ){ char *pNew; assert( (pMem->flags & MEM_Blob)!=0 ); - pNew = sqliteMalloc(pMem->n+pMem->u.i+1); + pNew = sqliteMalloc(pMem->n+pMem->u.i); if( pNew==0 ){ return SQLITE_NOMEM; } memcpy(pNew, pMem->z, pMem->n); - memset(&pNew[pMem->n], 0, pMem->u.i+1); + memset(&pNew[pMem->n], 0, pMem->u.i); sqlite3VdbeMemRelease(pMem); pMem->z = pNew; + pMem->n += pMem->u.i; pMem->u.i = 0; pMem->flags &= MEM_Zero|MEM_Static|MEM_Ephem|MEM_Short; pMem->flags |= MEM_Term|MEM_Dyn; } return SQLITE_OK; @@ -378,14 +379,15 @@ ** Delete any previous value and set the value to be a BLOB of length ** n containing all zeros. */ void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ sqlite3VdbeMemRelease(pMem); - pMem->flags = MEM_Blob|MEM_Zero; + pMem->flags = MEM_Blob|MEM_Zero|MEM_Short; pMem->type = SQLITE_BLOB; pMem->n = 0; pMem->u.i = n; + pMem->z = pMem->zShort; } /* ** Delete any previous value and set the value stored in *pMem to val, ** manifest type INTEGER. @@ -740,11 +742,11 @@ assert( flags!=0 ); /* Must define some type */ if( flags & (MEM_Str|MEM_Blob) ){ int x = flags & (MEM_Static|MEM_Dyn|MEM_Ephem|MEM_Short); assert( x!=0 ); /* Strings must define a string subtype */ assert( (x & (x-1))==0 ); /* Only one string subtype can be defined */ - assert( pMem->z!=0 || x==MEM_Zero ); /* Strings must have a value */ + assert( pMem->z!=0 ); /* Strings must have a value */ /* Mem.z points to Mem.zShort iff the subtype is MEM_Short */ assert( (x & MEM_Short)==0 || pMem->z==pMem->zShort ); assert( (x & MEM_Short)!=0 || pMem->z!=pMem->zShort ); /* No destructor unless there is MEM_Dyn */ assert( pMem->xDel==0 || (pMem->flags & MEM_Dyn)!=0 ); ADDED test/zeroblob.test Index: test/zeroblob.test ================================================================== --- /dev/null +++ test/zeroblob.test @@ -0,0 +1,58 @@ +# 2007 May 02 +# +# 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 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 $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Create the database +# +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; + } +} {1} +do_test zeroblob-1.2 { + execsql { + SELECT length(d) FROM t1 + } +} {10000} +do_test zeroblob-1.3 { + execsql { + INSERT INTO t1 VALUES(2,3,zeroblob(10000),4); + SELECT count(*) FROM t1; + } +} {2} +do_test zeroblob-1.4 { + execsql { + SELECT length(c), length(d) FROM t1 + } +} {1 10000 10000 1} +do_test zeroblob-1.5 { + execsql { + INSERT INTO t1 VALUES(3,4,zeroblob(10000),zeroblob(10000)); + SELECT count(*) FROM t1; + } +} {3} +do_test zeroblob-1.6 { + execsql { + SELECT length(c), length(d) FROM t1 + } +} {1 10000 10000 1 10000 10000} + +finish_test