diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/btree.c | 30 | ||||
-rw-r--r-- | src/btree.h | 5 | ||||
-rw-r--r-- | src/sqlite.h.in | 4 | ||||
-rw-r--r-- | src/test1.c | 40 | ||||
-rw-r--r-- | src/test3.c | 18 | ||||
-rw-r--r-- | src/vdbe.c | 12 | ||||
-rw-r--r-- | src/vdbeInt.h | 2 | ||||
-rw-r--r-- | src/vdbeapi.c | 13 | ||||
-rw-r--r-- | src/vdbemem.c | 58 |
9 files changed, 150 insertions, 32 deletions
diff --git a/src/btree.c b/src/btree.c index b253395d9..2ad735805 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.363 2007/05/01 17:49:49 danielk1977 Exp $ +** $Id: btree.c,v 1.364 2007/05/02 01:34:31 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -4151,6 +4151,7 @@ static int fillInCell( unsigned char *pCell, /* Complete text of the cell */ const void *pKey, i64 nKey, /* The key */ const void *pData,int nData, /* The data */ + int nZero, /* Extra zero bytes to append to pData */ int *pnSize /* Write cell size here */ ){ int nPayload; @@ -4172,18 +4173,18 @@ static int fillInCell( nHeader += 4; } if( pPage->hasData ){ - nHeader += putVarint(&pCell[nHeader], nData); + nHeader += putVarint(&pCell[nHeader], nData+nZero); }else{ - nData = 0; + nData = nZero = 0; } nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey); parseCellPtr(pPage, pCell, &info); assert( info.nHeader==nHeader ); assert( info.nKey==nKey ); - assert( info.nData==nData ); + assert( info.nData==nData+nZero ); /* Fill in the payload */ - nPayload = nData; + nPayload = nData + nZero; if( pPage->intKey ){ pSrc = pData; nSrc = nData; @@ -4228,9 +4229,13 @@ static int fillInCell( } n = nPayload; if( n>spaceLeft ) n = spaceLeft; - if( n>nSrc ) n = nSrc; - assert( pSrc ); - memcpy(pPayload, pSrc, n); + if( nSrc>0 ){ + if( n>nSrc ) n = nSrc; + assert( pSrc ); + memcpy(pPayload, pSrc, n); + }else{ + memset(pPayload, 0, n); + } nPayload -= n; pPayload += n; pSrc += n; @@ -4555,7 +4560,7 @@ static int balance_quick(MemPage *pPage, MemPage *pParent){ */ assert( pPage->nCell>0 ); parseCellPtr(pPage, findCell(pPage, pPage->nCell-1), &info); - rc = fillInCell(pParent, parentCell, 0, info.nKey, 0, 0, &parentSize); + rc = fillInCell(pParent, parentCell, 0, info.nKey, 0, 0, 0, &parentSize); if( rc!=SQLITE_OK ){ return rc; } @@ -5101,7 +5106,7 @@ static int balance_nonroot(MemPage *pPage){ j--; parseCellPtr(pNew, apCell[j], &info); pCell = &aSpace[iSpace]; - fillInCell(pParent, pCell, 0, info.nKey, 0, 0, &sz); + fillInCell(pParent, pCell, 0, info.nKey, 0, 0, 0, &sz); iSpace += sz; assert( iSpace<=pBt->pageSize*5 ); pTemp = 0; @@ -5415,6 +5420,7 @@ int sqlite3BtreeInsert( BtCursor *pCur, /* Insert data into the table of this cursor */ const void *pKey, i64 nKey, /* The key of the new record */ const void *pData, int nData, /* The data of the new record */ + int nZero, /* Number of extra 0 bytes to append to data */ int appendBias /* True if this is likely an append */ ){ int rc; @@ -5457,7 +5463,7 @@ int sqlite3BtreeInsert( if( rc ) return rc; newCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) ); if( newCell==0 ) return SQLITE_NOMEM; - rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, &szNew); + rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew); if( rc ) goto end_insert; assert( szNew==cellSizePtr(pPage, newCell) ); assert( szNew<=MX_CELL_SIZE(pBt) ); @@ -6857,7 +6863,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, const void *z){ } if( nCopy>0 ){ memcpy(&zData[offset], z, amt); - rc = sqlite3BtreeInsert(pCsr, 0, iKey, zData, nData, 0); + rc = sqlite3BtreeInsert(pCsr, 0, iKey, zData, nData, 0, 0); } sqliteFree(zData); diff --git a/src/btree.h b/src/btree.h index 40a885f51..b3cd58564 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,7 +13,7 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.76 2007/05/01 17:49:49 danielk1977 Exp $ +** @(#) $Id: btree.h,v 1.77 2007/05/02 01:34:31 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -124,7 +124,8 @@ int sqlite3BtreeCloseCursor(BtCursor*); int sqlite3BtreeMoveto(BtCursor*,const void *pKey,i64 nKey,int bias,int *pRes); int sqlite3BtreeDelete(BtCursor*); int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey, - const void *pData, int nData, int bias); + const void *pData, int nData, + int nZero, int bias); int sqlite3BtreeFirst(BtCursor*, int *pRes); int sqlite3BtreeLast(BtCursor*, int *pRes); int sqlite3BtreeNext(BtCursor*, int *pRes); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index c229a1043..17a0a14d5 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.202 2007/05/01 17:49:49 danielk1977 Exp $ +** @(#) $Id: sqlite.h.in,v 1.203 2007/05/02 01:34:31 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -790,6 +790,7 @@ int sqlite3_bind_null(sqlite3_stmt*, int); int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*)); int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); +int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); /* ** Return the number of host parameters in a compiled SQL statement. This @@ -1222,6 +1223,7 @@ void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); void sqlite3_result_value(sqlite3_context*, sqlite3_value*); +void sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** These are the allowed values for the eTextRep argument to diff --git a/src/test1.c b/src/test1.c index 1d765b1ef..409f4c212 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.240 2007/04/27 17:16:20 drh Exp $ +** $Id: test1.c,v 1.241 2007/05/02 01:34:31 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -2258,6 +2258,43 @@ static int test_breakpoint( } /* +** Usage: sqlite3_bind_zeroblob STMT IDX N +** +** Test the sqlite3_bind_zeroblob interface. STMT is a prepared statement. +** IDX is the index of a wildcard in the prepared statement. This command +** binds a N-byte zero-filled BLOB to the wildcard. +*/ +static int test_bind_zeroblob( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt; + int idx; + int n; + int rc; + + if( objc!=4 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " STMT N 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], &n) ) return TCL_ERROR; + + rc = sqlite3_bind_zeroblob(pStmt, idx, n); + if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; + if( rc!=SQLITE_OK ){ + return TCL_ERROR; + } + + return TCL_OK; +} + +/* ** Usage: sqlite3_bind_int STMT N VALUE ** ** Test the sqlite3_bind_int interface. STMT is a prepared statement. @@ -4361,6 +4398,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ } aObjCmd[] = { { "sqlite3_connection_pointer", get_sqlite_pointer, 0 }, { "sqlite3_bind_int", test_bind_int, 0 }, + { "sqlite3_bind_zeroblob", test_bind_zeroblob, 0 }, { "sqlite3_bind_int64", test_bind_int64, 0 }, { "sqlite3_bind_double", test_bind_double, 0 }, { "sqlite3_bind_null", test_bind_null ,0 }, diff --git a/src/test3.c b/src/test3.c index fba3fa2ec..4fb878fb5 100644 --- a/src/test3.c +++ b/src/test3.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test3.c,v 1.73 2007/03/29 05:51:49 drh Exp $ +** $Id: test3.c,v 1.74 2007/05/02 01:34:32 drh Exp $ */ #include "sqliteInt.h" #include "pager.h" @@ -750,7 +750,7 @@ static int btree_delete( } /* -** Usage: btree_insert ID KEY DATA +** Usage: btree_insert ID KEY DATA ?NZERO? ** ** Create a new entry with the given key and data. If an entry already ** exists with the same key the old entry is overwritten. @@ -763,19 +763,25 @@ static int btree_insert( ){ BtCursor *pCur; int rc; + int nZero; - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "ID KEY DATA"); + if( objc!=4 && objc!=5 ){ + Tcl_WrongNumArgs(interp, 1, objv, "ID KEY DATA ?NZERO?"); return TCL_ERROR; } pCur = sqlite3TextToPtr(Tcl_GetString(objv[1])); + if( objc==5 ){ + if( Tcl_GetIntFromObj(interp, objv[4], &nZero) ) return TCL_ERROR; + }else{ + nZero = 0; + } if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ i64 iKey; int len; unsigned char *pBuf; if( Tcl_GetWideIntFromObj(interp, objv[2], &iKey) ) return TCL_ERROR; pBuf = Tcl_GetByteArrayFromObj(objv[3], &len); - rc = sqlite3BtreeInsert(pCur, 0, iKey, pBuf, len, 0); + rc = sqlite3BtreeInsert(pCur, 0, iKey, pBuf, len, nZero, 0); }else{ int keylen; int dlen; @@ -783,7 +789,7 @@ static int btree_insert( unsigned char *pDBuf; pKBuf = Tcl_GetByteArrayFromObj(objv[2], &keylen); pDBuf = Tcl_GetByteArrayFromObj(objv[3], &dlen); - rc = sqlite3BtreeInsert(pCur, pKBuf, keylen, pDBuf, dlen, 0); + rc = sqlite3BtreeInsert(pCur, pKBuf, keylen, pDBuf, dlen, nZero, 0); } if( rc ){ Tcl_AppendResult(interp, errorName(rc), 0); diff --git a/src/vdbe.c b/src/vdbe.c index eeb0847fd..4207cf3e3 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.602 2007/04/26 14:42:36 danielk1977 Exp $ +** $Id: vdbe.c,v 1.603 2007/05/02 01:34:32 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -3372,8 +3372,14 @@ case OP_Insert: { /* no-push */ } pC->nullRow = 0; }else{ + int nZero; + if( pTos->flags & MEM_Zero ){ + nZero = pTos->u.i; + }else{ + nZero = 0; + } rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey, - pTos->z, pTos->n, + pTos->z, pTos->n, nZero, pOp->p2 & OPFLAG_APPEND); } @@ -3752,7 +3758,7 @@ case OP_IdxInsert: { /* no-push */ int nKey = pTos->n; const char *zKey = pTos->z; assert( pC->isTable==0 ); - rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, pOp->p2); + rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p2); assert( pC->deferredMoveto==0 ); pC->cacheStatus = CACHE_STALE; } diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 11c906100..88ea6c72d 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -172,6 +172,7 @@ typedef struct Mem Mem; #define MEM_Ephem 0x0100 /* Mem.z points to an ephemeral string */ #define MEM_Short 0x0200 /* Mem.z points to Mem.zShort */ #define MEM_Agg 0x0400 /* Mem.z points to an agg function context */ +#define MEM_Zero 0x0800 /* Mem.i contains count of 0s appended to blob */ /* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains @@ -385,6 +386,7 @@ int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*)); void sqlite3VdbeMemSetInt64(Mem*, i64); void sqlite3VdbeMemSetDouble(Mem*, double); void sqlite3VdbeMemSetNull(Mem*); +void sqlite3VdbeMemSetZeroBlob(Mem*,int); int sqlite3VdbeMemMakeWriteable(Mem*); int sqlite3VdbeMemDynamicify(Mem*); int sqlite3VdbeMemStringify(Mem*, int); diff --git a/src/vdbeapi.c b/src/vdbeapi.c index bd6d1d410..4e121059f 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -37,6 +37,7 @@ int sqlite3_expired(sqlite3_stmt *pStmt){ const void *sqlite3_value_blob(sqlite3_value *pVal){ Mem *p = (Mem*)pVal; if( p->flags & (MEM_Blob|MEM_Str) ){ + sqlite3VdbeMemExpandBlob(p); if( (p->flags & MEM_Term)==0 ){ p->flags &= ~MEM_Str; } @@ -151,6 +152,9 @@ void sqlite3_result_text16le( void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){ sqlite3VdbeMemCopy(&pCtx->s, pValue); } +void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){ + sqlite3VdbeMemSetZeroBlob(&pCtx->s, n); +} /* @@ -790,6 +794,15 @@ int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){ } return rc; } +int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ + int rc; + Vdbe *p = (Vdbe *)pStmt; + rc = vdbeUnbind(p, i); + if( rc==SQLITE_OK ){ + sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); + } + return rc; +} /* ** Return the number of wildcards that can be potentially bound to. diff --git a/src/vdbemem.c b/src/vdbemem.c index 5aed1df3d..52b4a5feb 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -60,12 +60,14 @@ int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ ** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails. */ int sqlite3VdbeMemDynamicify(Mem *pMem){ - int n = pMem->n; + int n; u8 *z; + sqlite3VdbeMemExpandBlob(pMem); if( (pMem->flags & (MEM_Ephem|MEM_Static|MEM_Short))==0 ){ return SQLITE_OK; } assert( (pMem->flags & MEM_Dyn)==0 ); + n = pMem->n; assert( pMem->flags & (MEM_Str|MEM_Blob) ); z = sqliteMallocRaw( n+2 ); if( z==0 ){ @@ -82,6 +84,30 @@ int sqlite3VdbeMemDynamicify(Mem *pMem){ } /* +** If the given Mem* is a zero-filled blob, 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); + if( pNew==0 ){ + return SQLITE_NOMEM; + } + memcpy(pNew, pMem->z, pMem->n); + memset(&pNew[pMem->n], 0, pMem->u.i+1); + sqlite3VdbeMemRelease(pMem); + pMem->z = pNew; + pMem->u.i = 0; + pMem->flags &= MEM_Zero|MEM_Static|MEM_Ephem|MEM_Short; + pMem->flags |= MEM_Term|MEM_Dyn; + } + return SQLITE_OK; +} + + +/* ** Make the given Mem object either MEM_Short or MEM_Dyn so that bytes ** of the Mem.z[] array can be modified. ** @@ -90,6 +116,7 @@ int sqlite3VdbeMemDynamicify(Mem *pMem){ int sqlite3VdbeMemMakeWriteable(Mem *pMem){ int n; u8 *z; + sqlite3VdbeMemExpandBlob(pMem); if( (pMem->flags & (MEM_Ephem|MEM_Static))==0 ){ return SQLITE_OK; } @@ -348,6 +375,18 @@ void sqlite3VdbeMemSetNull(Mem *pMem){ } /* +** 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->type = SQLITE_BLOB; + pMem->n = 0; + pMem->u.i = n; +} + +/* ** Delete any previous value and set the value stored in *pMem to val, ** manifest type INTEGER. */ @@ -699,14 +738,14 @@ int sqlite3VdbeMemFromBtree( void sqlite3VdbeMemSanity(Mem *pMem){ int flags = pMem->flags; assert( flags!=0 ); /* Must define some type */ - if( pMem->flags & (MEM_Str|MEM_Blob) ){ - int x = pMem->flags & (MEM_Static|MEM_Dyn|MEM_Ephem|MEM_Short); + 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 ); /* Strings must have a value */ + assert( pMem->z!=0 || x==MEM_Zero ); /* Strings must have a value */ /* Mem.z points to Mem.zShort iff the subtype is MEM_Short */ - assert( (pMem->flags & MEM_Short)==0 || pMem->z==pMem->zShort ); - assert( (pMem->flags & MEM_Short)!=0 || pMem->z!=pMem->zShort ); + 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 ); @@ -759,6 +798,7 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){ } assert( (MEM_Blob>>3) == MEM_Str ); pVal->flags |= (pVal->flags & MEM_Blob)>>3; + sqlite3VdbeMemExpandBlob(pVal); if( pVal->flags&MEM_Str ){ sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED); if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&(int)pVal->z) ){ @@ -888,7 +928,11 @@ void sqlite3ValueFree(sqlite3_value *v){ int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){ Mem *p = (Mem*)pVal; if( (p->flags & MEM_Blob)!=0 || sqlite3ValueText(pVal, enc) ){ - return p->n; + if( p->flags & MEM_Zero ){ + return p->n+p->u.i; + }else{ + return p->n; + } } return 0; } |