aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/btree.c30
-rw-r--r--src/btree.h5
-rw-r--r--src/sqlite.h.in4
-rw-r--r--src/test1.c40
-rw-r--r--src/test3.c18
-rw-r--r--src/vdbe.c12
-rw-r--r--src/vdbeInt.h2
-rw-r--r--src/vdbeapi.c13
-rw-r--r--src/vdbemem.c58
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;
}