Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(205)

Unified Diff: third_party/sqlite/src/src/vdbeaux.c

Issue 5626002: Update sqlite to 3.7.3. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/third_party/sqlite/src
Patch Set: Remove misc change. Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/sqlite/src/src/vdbeapi.c ('k') | third_party/sqlite/src/src/vdbeblob.c » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/sqlite/src/src/vdbeaux.c
diff --git a/third_party/sqlite/src/src/vdbeaux.c b/third_party/sqlite/src/src/vdbeaux.c
index c169b3727261eed76267837fa533e748f82df178..c0227d596f1518741223b46579b6f2b3fbe8979d 100644
--- a/third_party/sqlite/src/src/vdbeaux.c
+++ b/third_party/sqlite/src/src/vdbeaux.c
@@ -13,8 +13,6 @@
** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.) Prior
** to version 2.8.7, all this code was combined into the vdbe.c source file.
** But that file was getting too big so this subroutines were split out.
-**
-** $Id: vdbeaux.c,v 1.480 2009/08/08 18:01:08 drh Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
@@ -53,13 +51,14 @@ Vdbe *sqlite3VdbeCreate(sqlite3 *db){
** Remember the SQL string for a prepared statement.
*/
void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){
+ assert( isPrepareV2==1 || isPrepareV2==0 );
if( p==0 ) return;
#ifdef SQLITE_OMIT_TRACE
if( !isPrepareV2 ) return;
#endif
assert( p->zSql==0 );
p->zSql = sqlite3DbStrNDup(p->db, z, n);
- p->isPrepareV2 = isPrepareV2 ? 1 : 0;
+ p->isPrepareV2 = (u8)isPrepareV2;
}
/*
@@ -67,7 +66,7 @@ void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){
*/
const char *sqlite3_sql(sqlite3_stmt *pStmt){
Vdbe *p = (Vdbe *)pStmt;
- return (p->isPrepareV2 ? p->zSql : 0);
+ return (p && p->isPrepareV2) ? p->zSql : 0;
}
/*
@@ -197,6 +196,22 @@ int sqlite3VdbeAddOp4(
}
/*
+** Add an opcode that includes the p4 value as an integer.
+*/
+int sqlite3VdbeAddOp4Int(
+ Vdbe *p, /* Add the opcode to this VM */
+ int op, /* The new opcode */
+ int p1, /* The P1 operand */
+ int p2, /* The P2 operand */
+ int p3, /* The P3 operand */
+ int p4 /* The P4 operand as an integer */
+){
+ int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3);
+ sqlite3VdbeChangeP4(p, addr, SQLITE_INT_TO_PTR(p4), P4_INT32);
+ return addr;
+}
+
+/*
** Create a new symbolic label for an instruction that has yet to be
** coded. The symbolic label is really just a negative number. The
** label can be used as the P2 value of an operation. Later, when
@@ -240,7 +255,14 @@ void sqlite3VdbeResolveLabel(Vdbe *p, int x){
}
}
-#ifdef SQLITE_DEBUG
+/*
+** Mark the VDBE as one that can only be run one time.
+*/
+void sqlite3VdbeRunOnlyOnce(Vdbe *p){
+ p->runOnlyOnce = 1;
+}
+
+#ifdef SQLITE_DEBUG /* sqlite3AssertMayAbort() logic */
/*
** The following type and function are used to iterate through all opcodes
@@ -312,7 +334,7 @@ static Op *opIterNext(VdbeOpIter *p){
/*
** Check if the program stored in the VM associated with pParse may
-** throw an ABORT exception (causing the statement, but not transaction
+** throw an ABORT exception (causing the statement, but not entire transaction
** to be rolled back). This condition is true if the main program or any
** sub-programs contains any of the following:
**
@@ -321,6 +343,7 @@ static Op *opIterNext(VdbeOpIter *p){
** * OP_Destroy
** * OP_VUpdate
** * OP_VRename
+** * OP_FkCounter with P2==0 (immediate foreign key constraint)
**
** Then check that the value of Parse.mayAbort is true if an
** ABORT may be thrown, or false otherwise. Return true if it does
@@ -339,6 +362,9 @@ int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
while( (pOp = opIterNext(&sIter))!=0 ){
int opcode = pOp->opcode;
if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename
+#ifndef SQLITE_OMIT_FOREIGN_KEY
+ || (opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1)
+#endif
|| ((opcode==OP_Halt || opcode==OP_HaltIfNull)
&& (pOp->p1==SQLITE_CONSTRAINT && pOp->p2==OE_Abort))
){
@@ -355,7 +381,7 @@ int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
** from failing. */
return ( v->db->mallocFailed || hasAbort==mayAbort );
}
-#endif
+#endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */
/*
** Loop through the program looking for P2 values that are negative
@@ -367,6 +393,8 @@ int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
** Variable *pMaxFuncArgs is set to the maximum value of any P2 argument
** to an OP_Function, OP_AggStep or OP_VFilter opcode. This is used by
** sqlite3VdbeMakeReady() to size the Vdbe.apArg[] array.
+**
+** The Op.opflags field is set on all opcodes.
*/
static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
int i;
@@ -377,15 +405,14 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
u8 opcode = pOp->opcode;
+ pOp->opflags = sqlite3OpcodeProperty[opcode];
if( opcode==OP_Function || opcode==OP_AggStep ){
if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- }else if( opcode==OP_VUpdate ){
- if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
-#endif
}else if( opcode==OP_Transaction && pOp->p2!=0 ){
p->readOnly = 0;
#ifndef SQLITE_OMIT_VIRTUALTABLE
+ }else if( opcode==OP_VUpdate ){
+ if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
}else if( opcode==OP_VFilter ){
int n;
assert( p->nOp - i >= 3 );
@@ -395,7 +422,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
#endif
}
- if( sqlite3VdbeOpcodeHasProperty(opcode, OPFLG_JUMP) && pOp->p2<0 ){
+ if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){
assert( -1-pOp->p2<p->nLabel );
pOp->p2 = aLabel[-1-pOp->p2];
}
@@ -457,7 +484,7 @@ int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
VdbeOp *pOut = &p->aOp[i+addr];
pOut->opcode = pIn->opcode;
pOut->p1 = pIn->p1;
- if( p2<0 && sqlite3VdbeOpcodeHasProperty(pOut->opcode, OPFLG_JUMP) ){
+ if( p2<0 && (sqlite3OpcodeProperty[pOut->opcode] & OPFLG_JUMP)!=0 ){
pOut->p2 = addr + ADDR(p2);
}else{
pOut->p2 = p2;
@@ -546,15 +573,17 @@ static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){
}
}
+static void vdbeFreeOpArray(sqlite3 *, Op *, int);
+
/*
** Delete a P4 value if necessary.
*/
static void freeP4(sqlite3 *db, int p4type, void *p4){
if( p4 ){
+ assert( db );
switch( p4type ){
case P4_REAL:
case P4_INT64:
- case P4_MPRINTF:
case P4_DYNAMIC:
case P4_KEYINFO:
case P4_INTARRAY:
@@ -562,10 +591,14 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
sqlite3DbFree(db, p4);
break;
}
+ case P4_MPRINTF: {
+ if( db->pnBytesFreed==0 ) sqlite3_free(p4);
+ break;
+ }
case P4_VDBEFUNC: {
VdbeFunc *pVdbeFunc = (VdbeFunc *)p4;
freeEphemeralFunction(db, pVdbeFunc->pFunc);
- sqlite3VdbeDeleteAuxData(pVdbeFunc, 0);
+ if( db->pnBytesFreed==0 ) sqlite3VdbeDeleteAuxData(pVdbeFunc, 0);
sqlite3DbFree(db, pVdbeFunc);
break;
}
@@ -574,15 +607,17 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
break;
}
case P4_MEM: {
- sqlite3ValueFree((sqlite3_value*)p4);
+ if( db->pnBytesFreed==0 ){
+ sqlite3ValueFree((sqlite3_value*)p4);
+ }else{
+ Mem *p = (Mem*)p4;
+ sqlite3DbFree(db, p->zMalloc);
+ sqlite3DbFree(db, p);
+ }
break;
}
case P4_VTAB : {
- sqlite3VtabUnlock((VTable *)p4);
- break;
- }
- case P4_SUBPROGRAM : {
- sqlite3VdbeProgramDelete(db, (SubProgram *)p4, 1);
+ if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
break;
}
}
@@ -608,35 +643,15 @@ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){
}
/*
-** Decrement the ref-count on the SubProgram structure passed as the
-** second argument. If the ref-count reaches zero, free the structure.
-**
-** The array of VDBE opcodes stored as SubProgram.aOp is freed if
-** either the ref-count reaches zero or parameter freeop is non-zero.
-**
-** Since the array of opcodes pointed to by SubProgram.aOp may directly
-** or indirectly contain a reference to the SubProgram structure itself.
-** By passing a non-zero freeop parameter, the caller may ensure that all
-** SubProgram structures and their aOp arrays are freed, even when there
-** are such circular references.
+** Link the SubProgram object passed as the second argument into the linked
+** list at Vdbe.pSubProgram. This list is used to delete all sub-program
+** objects when the VM is no longer required.
*/
-void sqlite3VdbeProgramDelete(sqlite3 *db, SubProgram *p, int freeop){
- if( p ){
- assert( p->nRef>0 );
- if( freeop || p->nRef==1 ){
- Op *aOp = p->aOp;
- p->aOp = 0;
- vdbeFreeOpArray(db, aOp, p->nOp);
- p->nOp = 0;
- }
- p->nRef--;
- if( p->nRef==0 ){
- sqlite3DbFree(db, p);
- }
- }
+void sqlite3VdbeLinkSubProgram(Vdbe *pVdbe, SubProgram *p){
+ p->pNext = pVdbe->pProgram;
+ pVdbe->pProgram = p;
}
-
/*
** Change N opcodes starting at addr to No-ops.
*/
@@ -712,11 +727,11 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
nField = ((KeyInfo*)zP4)->nField;
nByte = sizeof(*pKeyInfo) + (nField-1)*sizeof(pKeyInfo->aColl[0]) + nField;
- pKeyInfo = sqlite3Malloc( nByte );
+ pKeyInfo = sqlite3DbMallocRaw(0, nByte);
pOp->p4.pKeyInfo = pKeyInfo;
if( pKeyInfo ){
u8 *aSortOrder;
- memcpy(pKeyInfo, zP4, nByte);
+ memcpy((char*)pKeyInfo, zP4, nByte - nField);
aSortOrder = pKeyInfo->aSortOrder;
if( aSortOrder ){
pKeyInfo->aSortOrder = (unsigned char*)&pKeyInfo->aColl[nField];
@@ -787,9 +802,12 @@ void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
**
** If a memory allocation error has occurred prior to the calling of this
** routine, then a pointer to a dummy VdbeOp will be returned. That opcode
-** is readable and writable, but it has no effect. The return of a dummy
-** opcode allows the call to continue functioning after a OOM fault without
-** having to check to see if the return from this routine is a valid pointer.
+** is readable but not writable, though it is cast to a writable value.
+** The return of a dummy opcode allows the call to continue functioning
+** after a OOM fault without having to check to see if the return from
+** this routine is a valid pointer. But because the dummy.opcode is 0,
+** dummy will never be written to. This is verified by code inspection and
+** by running with Valgrind.
**
** About the #ifdef SQLITE_OMIT_TRACE: Normally, this routine is never called
** unless p->nOp>0. This is because in the absense of SQLITE_OMIT_TRACE,
@@ -800,17 +818,19 @@ void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
** check the value of p->nOp-1 before continuing.
*/
VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
- static VdbeOp dummy;
+ /* C89 specifies that the constant "dummy" will be initialized to all
+ ** zeros, which is correct. MSVC generates a warning, nevertheless. */
+ static const VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
assert( p->magic==VDBE_MAGIC_INIT );
if( addr<0 ){
#ifdef SQLITE_OMIT_TRACE
- if( p->nOp==0 ) return &dummy;
+ if( p->nOp==0 ) return (VdbeOp*)&dummy;
#endif
addr = p->nOp - 1;
}
assert( (addr>=0 && addr<p->nOp) || p->db->mallocFailed );
if( p->db->mallocFailed ){
- return &dummy;
+ return (VdbeOp*)&dummy;
}else{
return &p->aOp[addr];
}
@@ -923,6 +943,11 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
/*
** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
+**
+** The prepared statement has to know in advance which Btree objects
+** will be used so that it can acquire mutexes on them all in sorted
+** order (via sqlite3VdbeMutexArrayEnter(). Mutexes are acquired
+** in order (and released in reverse order) to avoid deadlocks.
*/
void sqlite3VdbeUsesBtree(Vdbe *p, int i){
int mask;
@@ -966,6 +991,12 @@ static void releaseMemArray(Mem *p, int N){
Mem *pEnd;
sqlite3 *db = p->db;
u8 malloc_failed = db->mallocFailed;
+ if( db->pnBytesFreed ){
+ for(pEnd=&p[N]; p<pEnd; p++){
+ sqlite3DbFree(db, p->zMalloc);
+ }
+ return;
+ }
for(pEnd=&p[N]; p<pEnd; p++){
assert( (&p[1])==pEnd || p[0].db==p[1].db );
@@ -1009,27 +1040,6 @@ void sqlite3VdbeFrameDelete(VdbeFrame *p){
sqlite3DbFree(p->v->db, p);
}
-
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
-int sqlite3VdbeReleaseBuffers(Vdbe *p){
- int ii;
- int nFree = 0;
- assert( sqlite3_mutex_held(p->db->mutex) );
- for(ii=1; ii<=p->nMem; ii++){
- Mem *pMem = &p->aMem[ii];
- if( pMem->flags & MEM_RowSet ){
- sqlite3RowSetClear(pMem->u.pRowSet);
- }
- if( pMem->z && pMem->flags&MEM_Dyn ){
- assert( !pMem->xDel );
- nFree += sqlite3DbMallocSize(pMem->db, pMem->z);
- sqlite3VdbeMemRelease(pMem);
- }
- }
- return nFree;
-}
-#endif
-
#ifndef SQLITE_OMIT_EXPLAIN
/*
** Give a listing of the program in the virtual machine.
@@ -1042,22 +1052,24 @@ int sqlite3VdbeReleaseBuffers(Vdbe *p){
** p->explain==2, only OP_Explain instructions are listed and these
** are shown in a different format. p->explain==2 is used to implement
** EXPLAIN QUERY PLAN.
+**
+** When p->explain==1, first the main program is listed, then each of
+** the trigger subprograms are listed one by one.
*/
int sqlite3VdbeList(
Vdbe *p /* The VDBE */
){
- int nRow; /* Total number of rows to return */
+ int nRow; /* Stop when row count reaches this */
int nSub = 0; /* Number of sub-vdbes seen so far */
SubProgram **apSub = 0; /* Array of sub-vdbes */
- Mem *pSub = 0;
- sqlite3 *db = p->db;
- int i;
- int rc = SQLITE_OK;
- Mem *pMem = p->pResultSet = &p->aMem[1];
+ Mem *pSub = 0; /* Memory cell hold array of subprogs */
+ sqlite3 *db = p->db; /* The database connection */
+ int i; /* Loop counter */
+ int rc = SQLITE_OK; /* Return code */
+ Mem *pMem = p->pResultSet = &p->aMem[1]; /* First Mem of result set */
assert( p->explain );
assert( p->magic==VDBE_MAGIC_RUN );
- assert( db->magic==SQLITE_MAGIC_BUSY );
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM );
/* Even though this opcode does not use dynamic strings for
@@ -1073,12 +1085,24 @@ int sqlite3VdbeList(
return SQLITE_ERROR;
}
- /* Figure out total number of rows that will be returned by this
- ** EXPLAIN program. */
+ /* When the number of output rows reaches nRow, that means the
+ ** listing has finished and sqlite3_step() should return SQLITE_DONE.
+ ** nRow is the sum of the number of rows in the main program, plus
+ ** the sum of the number of rows in all trigger subprograms encountered
+ ** so far. The nRow value will increase as new trigger subprograms are
+ ** encountered, but p->pc will eventually catch up to nRow.
+ */
nRow = p->nOp;
if( p->explain==1 ){
+ /* The first 8 memory cells are used for the result set. So we will
+ ** commandeer the 9th cell to use as storage for an array of pointers
+ ** to trigger subprograms. The VDBE is guaranteed to have at least 9
+ ** cells. */
+ assert( p->nMem>9 );
pSub = &p->aMem[9];
if( pSub->flags&MEM_Blob ){
+ /* On the first call to sqlite3_step(), pSub will hold a NULL. It is
+ ** initialized to a BLOB by the P4_SUBPROGRAM processing logic below */
nSub = pSub->n/sizeof(Vdbe*);
apSub = (SubProgram **)pSub->z;
}
@@ -1101,8 +1125,12 @@ int sqlite3VdbeList(
char *z;
Op *pOp;
if( i<p->nOp ){
+ /* The output line number is small enough that we are still in the
+ ** main program. */
pOp = &p->aOp[i];
}else{
+ /* We are currently listing subprograms. Figure out which one and
+ ** pick up the appropriate opcode. */
int j;
i -= p->nOp;
for(j=0; i>=apSub[j]->nOp; j++){
@@ -1124,6 +1152,11 @@ int sqlite3VdbeList(
pMem->enc = SQLITE_UTF8;
pMem++;
+ /* When an OP_Program opcode is encounter (the only opcode that has
+ ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
+ ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
+ ** has not already been seen.
+ */
if( pOp->p4type==P4_SUBPROGRAM ){
int nByte = (nSub+1)*sizeof(SubProgram*);
int j;
@@ -1255,38 +1288,43 @@ void sqlite3VdbeIOTraceSql(Vdbe *p){
#endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */
/*
-** Allocate space from a fixed size buffer. Make *pp point to the
-** allocated space. (Note: pp is a char* rather than a void** to
-** work around the pointer aliasing rules of C.) *pp should initially
-** be zero. If *pp is not zero, that means that the space has already
-** been allocated and this routine is a noop.
+** Allocate space from a fixed size buffer and return a pointer to
+** that space. If insufficient space is available, return NULL.
+**
+** The pBuf parameter is the initial value of a pointer which will
+** receive the new memory. pBuf is normally NULL. If pBuf is not
+** NULL, it means that memory space has already been allocated and that
+** this routine should not allocate any new memory. When pBuf is not
+** NULL simply return pBuf. Only allocate new memory space when pBuf
+** is NULL.
**
** nByte is the number of bytes of space needed.
**
-** *ppFrom point to available space and pEnd points to the end of the
-** available space.
+** *ppFrom points to available space and pEnd points to the end of the
+** available space. When space is allocated, *ppFrom is advanced past
+** the end of the allocated space.
**
** *pnByte is a counter of the number of bytes of space that have failed
** to allocate. If there is insufficient space in *ppFrom to satisfy the
** request, then increment *pnByte by the amount of the request.
*/
-static void allocSpace(
- char *pp, /* IN/OUT: Set *pp to point to allocated buffer */
+static void *allocSpace(
+ void *pBuf, /* Where return pointer will be stored */
int nByte, /* Number of bytes to allocate */
u8 **ppFrom, /* IN/OUT: Allocate from *ppFrom */
u8 *pEnd, /* Pointer to 1 byte past the end of *ppFrom buffer */
int *pnByte /* If allocation cannot be made, increment *pnByte */
){
assert( EIGHT_BYTE_ALIGNMENT(*ppFrom) );
- if( (*(void**)pp)==0 ){
- nByte = ROUND8(nByte);
- if( &(*ppFrom)[nByte] <= pEnd ){
- *(void**)pp = (void *)*ppFrom;
- *ppFrom += nByte;
- }else{
- *pnByte += nByte;
- }
+ if( pBuf ) return pBuf;
+ nByte = ROUND8(nByte);
+ if( &(*ppFrom)[nByte] <= pEnd ){
+ pBuf = (void*)*ppFrom;
+ *ppFrom += nByte;
+ }else{
+ *pnByte += nByte;
}
+ return pBuf;
}
/*
@@ -1345,9 +1383,10 @@ void sqlite3VdbeMakeReady(
** being called from sqlite3_reset() to reset the virtual machine.
*/
if( nVar>=0 && ALWAYS(db->mallocFailed==0) ){
- u8 *zCsr = (u8 *)&p->aOp[p->nOp];
- u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc];
- int nByte;
+ u8 *zCsr = (u8 *)&p->aOp[p->nOp]; /* Memory avaliable for alloation */
+ u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc]; /* First byte past available mem */
+ int nByte; /* How much extra memory needed */
+
resolveP2Values(p, &nArg);
p->usesStmtJournal = (u8)usesStmtJournal;
if( isExplain && nMem<10 ){
@@ -1357,15 +1396,24 @@ void sqlite3VdbeMakeReady(
zCsr += (zCsr - (u8*)0)&7;
assert( EIGHT_BYTE_ALIGNMENT(zCsr) );
+ /* Memory for registers, parameters, cursor, etc, is allocated in two
+ ** passes. On the first pass, we try to reuse unused space at the
+ ** end of the opcode array. If we are unable to satisfy all memory
+ ** requirements by reusing the opcode array tail, then the second
+ ** pass will fill in the rest using a fresh allocation.
+ **
+ ** This two-pass approach that reuses as much memory as possible from
+ ** the leftover space at the end of the opcode array can significantly
+ ** reduce the amount of memory held by a prepared statement.
+ */
do {
nByte = 0;
- allocSpace((char*)&p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte);
- allocSpace((char*)&p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte);
- allocSpace((char*)&p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte);
- allocSpace((char*)&p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
- allocSpace((char*)&p->apCsr,
- nCursor*sizeof(VdbeCursor*), &zCsr, zEnd, &nByte
- );
+ p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte);
+ p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte);
+ p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte);
+ p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
+ p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
+ &zCsr, zEnd, &nByte);
if( nByte ){
p->pFree = sqlite3DbMallocZero(db, nByte);
}
@@ -1375,7 +1423,7 @@ void sqlite3VdbeMakeReady(
p->nCursor = (u16)nCursor;
if( p->aVar ){
- p->nVar = (u16)nVar;
+ p->nVar = (ynVar)nVar;
for(n=0; n<nVar; n++){
p->aVar[n].flags = MEM_Null;
p->aVar[n].db = db;
@@ -1405,6 +1453,7 @@ void sqlite3VdbeMakeReady(
p->cacheCtr = 1;
p->minWriteFileFormat = 255;
p->iStatement = 0;
+ p->nFkConstraint = 0;
#ifdef VDBE_PROFILE
{
int i;
@@ -1436,9 +1485,7 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor;
const sqlite3_module *pModule = pCx->pModule;
p->inVtabMethod = 1;
- (void)sqlite3SafetyOff(p->db);
pModule->xClose(pVtabCursor);
- (void)sqlite3SafetyOn(p->db);
p->inVtabMethod = 0;
}
#endif
@@ -1599,9 +1646,6 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
** to the transaction.
*/
rc = sqlite3VtabSync(db, &p->zErrMsg);
- if( rc!=SQLITE_OK ){
- return rc;
- }
/* This loop determines (a) if the commit hook should be invoked and
** (b) how many database files have open write transactions, not
@@ -1609,19 +1653,21 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
** one database file has an open write transaction, a master journal
** file is required for an atomic commit.
*/
- for(i=0; i<db->nDb; i++){
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( sqlite3BtreeIsInTrans(pBt) ){
needXcommit = 1;
if( i!=1 ) nTrans++;
+ rc = sqlite3PagerExclusiveLock(sqlite3BtreePager(pBt));
}
}
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
/* If there are any write-transactions at all, invoke the commit hook */
if( needXcommit && db->xCommitCallback ){
- (void)sqlite3SafetyOff(db);
rc = db->xCommitCallback(db->pCommitArg);
- (void)sqlite3SafetyOn(db);
if( rc ){
return SQLITE_CONSTRAINT;
}
@@ -1707,10 +1753,12 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
*/
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
- if( i==1 ) continue; /* Ignore the TEMP database */
if( sqlite3BtreeIsInTrans(pBt) ){
char const *zFile = sqlite3BtreeGetJournalname(pBt);
- if( zFile[0]==0 ) continue; /* Ignore :memory: databases */
+ if( zFile==0 ){
+ continue; /* Ignore TEMP and :memory: databases */
+ }
+ assert( zFile[0]!=0 );
if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){
needSync = 1;
}
@@ -1755,6 +1803,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
}
}
sqlite3OsCloseFree(pMaster);
+ assert( rc!=SQLITE_BUSY );
if( rc!=SQLITE_OK ){
sqlite3DbFree(db, zMaster);
return rc;
@@ -1895,6 +1944,13 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
}
db->nStatement--;
p->iStatement = 0;
+
+ /* If the statement transaction is being rolled back, also restore the
+ ** database handles deferred constraint counter to the value it had when
+ ** the statement transaction was opened. */
+ if( eOp==SAVEPOINT_ROLLBACK ){
+ db->nDeferredCons = p->nStmtDefCons;
+ }
}
return rc;
}
@@ -1927,6 +1983,29 @@ void sqlite3VdbeMutexArrayEnter(Vdbe *p){
#endif
/*
+** This function is called when a transaction opened by the database
+** handle associated with the VM passed as an argument is about to be
+** committed. If there are outstanding deferred foreign key constraint
+** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK.
+**
+** If there are outstanding FK violations and this function returns
+** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT and write
+** an error message to it. Then return SQLITE_ERROR.
+*/
+#ifndef SQLITE_OMIT_FOREIGN_KEY
+int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
+ sqlite3 *db = p->db;
+ if( (deferred && db->nDeferredCons>0) || (!deferred && p->nFkConstraint>0) ){
+ p->rc = SQLITE_CONSTRAINT;
+ p->errorAction = OE_Abort;
+ sqlite3SetString(&p->zErrMsg, db, "foreign key constraint failed");
+ return SQLITE_ERROR;
+ }
+ return SQLITE_OK;
+}
+#endif
+
+/*
** This routine is called the when a VDBE tries to halt. If the VDBE
** has made changes and is in autocommit mode, then commit those
** changes. If a rollback is needed, then do the rollback.
@@ -1983,8 +2062,17 @@ int sqlite3VdbeHalt(Vdbe *p){
isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
|| mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
if( isSpecialError ){
- /* If the query was read-only, we need do no rollback at all. Otherwise,
- ** proceed with the special handling.
+ /* If the query was read-only and the error code is SQLITE_INTERRUPT,
+ ** no rollback is necessary. Otherwise, at least a savepoint
+ ** transaction must be rolled back to restore the database to a
+ ** consistent state.
+ **
+ ** Even if the statement is read-only, it is important to perform
+ ** a statement or transaction rollback operation. If the error
+ ** occured while writing to the journal, sub-journal or database
+ ** file as part of an effort to free up cache space (see function
+ ** pagerStress() in pager.c), the rollback is required to restore
+ ** the pager to a consistent state.
*/
if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){
@@ -2000,6 +2088,11 @@ int sqlite3VdbeHalt(Vdbe *p){
}
}
}
+
+ /* Check for immediate foreign key violations. */
+ if( p->rc==SQLITE_OK ){
+ sqlite3VdbeCheckFk(p, 0);
+ }
/* If the auto-commit flag is set and this is the only active writer
** VM, then we do either a commit or rollback of the current transaction.
@@ -2012,10 +2105,14 @@ int sqlite3VdbeHalt(Vdbe *p){
&& db->writeVdbeCnt==(p->readOnly==0)
){
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
- /* The auto-commit flag is true, and the vdbe program was
- ** successful or hit an 'OR FAIL' constraint. This means a commit
- ** is required.
- */
+ if( sqlite3VdbeCheckFk(p, 1) ){
+ sqlite3BtreeMutexArrayLeave(&p->aMutex);
+ return SQLITE_ERROR;
+ }
+ /* The auto-commit flag is true, the vdbe program was successful
+ ** or hit an 'OR FAIL' constraint and there are no deferred foreign
+ ** key constraints to hold up the transaction. This means a commit
+ ** is required. */
rc = vdbeCommit(db, p);
if( rc==SQLITE_BUSY ){
sqlite3BtreeMutexArrayLeave(&p->aMutex);
@@ -2024,6 +2121,7 @@ int sqlite3VdbeHalt(Vdbe *p){
p->rc = rc;
sqlite3RollbackAll(db);
}else{
+ db->nDeferredCons = 0;
sqlite3CommitInternalChanges(db);
}
}else{
@@ -2046,15 +2144,27 @@ int sqlite3VdbeHalt(Vdbe *p){
/* If eStatementOp is non-zero, then a statement transaction needs to
** be committed or rolled back. Call sqlite3VdbeCloseStatement() to
** do so. If this operation returns an error, and the current statement
- ** error code is SQLITE_OK or SQLITE_CONSTRAINT, then set the error
- ** code to the new value.
+ ** error code is SQLITE_OK or SQLITE_CONSTRAINT, then promote the
+ ** current statement error code.
+ **
+ ** Note that sqlite3VdbeCloseStatement() can only fail if eStatementOp
+ ** is SAVEPOINT_ROLLBACK. But if p->rc==SQLITE_OK then eStatementOp
+ ** must be SAVEPOINT_RELEASE. Hence the NEVER(p->rc==SQLITE_OK) in
+ ** the following code.
*/
if( eStatementOp ){
rc = sqlite3VdbeCloseStatement(p, eStatementOp);
- if( rc && (p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT) ){
- p->rc = rc;
- sqlite3DbFree(db, p->zErrMsg);
- p->zErrMsg = 0;
+ if( rc ){
+ assert( eStatementOp==SAVEPOINT_ROLLBACK );
+ if( NEVER(p->rc==SQLITE_OK) || p->rc==SQLITE_CONSTRAINT ){
+ p->rc = rc;
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = 0;
+ }
+ invalidateCursorsOnModifiedBtrees(db);
+ sqlite3RollbackAll(db);
+ sqlite3CloseSavepoints(db);
+ db->autoCommit = 1;
}
}
@@ -2134,9 +2244,7 @@ int sqlite3VdbeReset(Vdbe *p){
** error, then it might not have been halted properly. So halt
** it now.
*/
- (void)sqlite3SafetyOn(db);
sqlite3VdbeHalt(p);
- (void)sqlite3SafetyOff(db);
/* If the VDBE has be run even partially, then transfer the error code
** and error message from the VDBE into the main database structure. But
@@ -2156,6 +2264,7 @@ int sqlite3VdbeReset(Vdbe *p){
}else{
sqlite3Error(db, SQLITE_OK, 0);
}
+ if( p->runOnlyOnce ) p->expired = 1;
}else if( p->rc && p->expired ){
/* The expired flag was set on the VDBE before the first call
** to sqlite3_step(). For consistency (since sqlite3_step() was
@@ -2233,6 +2342,30 @@ void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
}
/*
+** Free all memory associated with the Vdbe passed as the second argument.
+** The difference between this function and sqlite3VdbeDelete() is that
+** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with
+** the database connection.
+*/
+void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
+ SubProgram *pSub, *pNext;
+ assert( p->db==0 || p->db==db );
+ releaseMemArray(p->aVar, p->nVar);
+ releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
+ for(pSub=p->pProgram; pSub; pSub=pNext){
+ pNext = pSub->pNext;
+ vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
+ sqlite3DbFree(db, pSub);
+ }
+ vdbeFreeOpArray(db, p->aOp, p->nOp);
+ sqlite3DbFree(db, p->aLabel);
+ sqlite3DbFree(db, p->aColName);
+ sqlite3DbFree(db, p->zSql);
+ sqlite3DbFree(db, p->pFree);
+ sqlite3DbFree(db, p);
+}
+
+/*
** Delete an entire VDBE.
*/
void sqlite3VdbeDelete(Vdbe *p){
@@ -2249,15 +2382,9 @@ void sqlite3VdbeDelete(Vdbe *p){
if( p->pNext ){
p->pNext->pPrev = p->pPrev;
}
- releaseMemArray(p->aVar, p->nVar);
- releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
- vdbeFreeOpArray(db, p->aOp, p->nOp);
- sqlite3DbFree(db, p->aLabel);
- sqlite3DbFree(db, p->aColName);
- sqlite3DbFree(db, p->zSql);
p->magic = VDBE_MAGIC_DEAD;
- sqlite3DbFree(db, p->pFree);
- sqlite3DbFree(db, p);
+ p->db = 0;
+ sqlite3VdbeDeleteObject(db, p);
}
/*
@@ -2283,11 +2410,8 @@ int sqlite3VdbeCursorMoveto(VdbeCursor *p){
rc = sqlite3BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res);
if( rc ) return rc;
p->lastRowid = p->movetoTarget;
- p->rowidIsValid = ALWAYS(res==0) ?1:0;
- if( NEVER(res<0) ){
- rc = sqlite3BtreeNext(p->pCursor, &res);
- if( rc ) return rc;
- }
+ if( res!=0 ) return SQLITE_CORRUPT_BKPT;
+ p->rowidIsValid = 1;
#ifdef SQLITE_TEST
sqlite3_search_count++;
#endif
@@ -2748,9 +2872,17 @@ int sqlite3VdbeRecordCompare(
pKeyInfo = pPKey2->pKeyInfo;
mem1.enc = pKeyInfo->enc;
mem1.db = pKeyInfo->db;
- mem1.flags = 0;
- mem1.u.i = 0; /* not needed, here to silence compiler warning */
- mem1.zMalloc = 0;
+ /* mem1.flags = 0; // Will be initialized by sqlite3VdbeSerialGet() */
+ VVA_ONLY( mem1.zMalloc = 0; ) /* Only needed by assert() statements */
+
+ /* Compilers may complain that mem1.u.i is potentially uninitialized.
+ ** We could initialize it, as shown here, to silence those complaints.
+ ** But in fact, mem1.u.i will never actually be used initialized, and doing
+ ** the unnecessary initialization has a measurable negative performance
+ ** impact, since this routine is a very high runner. And so, we choose
+ ** to ignore the compiler warnings and leave this variable uninitialized.
+ */
+ /* mem1.u.i = 0; // not needed, here to silence compiler warning */
idx1 = getVarint32(aKey1, szHdr1);
d1 = szHdr1;
@@ -2774,47 +2906,52 @@ int sqlite3VdbeRecordCompare(
rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i],
i<nField ? pKeyInfo->aColl[i] : 0);
if( rc!=0 ){
- break;
+ assert( mem1.zMalloc==0 ); /* See comment below */
+
+ /* Invert the result if we are using DESC sort order. */
+ if( pKeyInfo->aSortOrder && i<nField && pKeyInfo->aSortOrder[i] ){
+ rc = -rc;
+ }
+
+ /* If the PREFIX_SEARCH flag is set and all fields except the final
+ ** rowid field were equal, then clear the PREFIX_SEARCH flag and set
+ ** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1).
+ ** This is used by the OP_IsUnique opcode.
+ */
+ if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){
+ assert( idx1==szHdr1 && rc );
+ assert( mem1.flags & MEM_Int );
+ pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH;
+ pPKey2->rowid = mem1.u.i;
+ }
+
+ return rc;
}
i++;
}
- /* No memory allocation is ever used on mem1. */
- if( NEVER(mem1.zMalloc) ) sqlite3VdbeMemRelease(&mem1);
-
- /* If the PREFIX_SEARCH flag is set and all fields except the final
- ** rowid field were equal, then clear the PREFIX_SEARCH flag and set
- ** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1).
- ** This is used by the OP_IsUnique opcode.
+ /* No memory allocation is ever used on mem1. Prove this using
+ ** the following assert(). If the assert() fails, it indicates a
+ ** memory leak and a need to call sqlite3VdbeMemRelease(&mem1).
*/
- if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){
- assert( idx1==szHdr1 && rc );
- assert( mem1.flags & MEM_Int );
- pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH;
- pPKey2->rowid = mem1.u.i;
- }
-
- if( rc==0 ){
- /* rc==0 here means that one of the keys ran out of fields and
- ** all the fields up to that point were equal. If the UNPACKED_INCRKEY
- ** flag is set, then break the tie by treating key2 as larger.
- ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes
- ** are considered to be equal. Otherwise, the longer key is the
- ** larger. As it happens, the pPKey2 will always be the longer
- ** if there is a difference.
- */
- if( pPKey2->flags & UNPACKED_INCRKEY ){
- rc = -1;
- }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){
- /* Leave rc==0 */
- }else if( idx1<szHdr1 ){
- rc = 1;
- }
- }else if( pKeyInfo->aSortOrder && i<pKeyInfo->nField
- && pKeyInfo->aSortOrder[i] ){
- rc = -rc;
+ assert( mem1.zMalloc==0 );
+
+ /* rc==0 here means that one of the keys ran out of fields and
+ ** all the fields up to that point were equal. If the UNPACKED_INCRKEY
+ ** flag is set, then break the tie by treating key2 as larger.
+ ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes
+ ** are considered to be equal. Otherwise, the longer key is the
+ ** larger. As it happens, the pPKey2 will always be the longer
+ ** if there is a difference.
+ */
+ assert( rc==0 );
+ if( pPKey2->flags & UNPACKED_INCRKEY ){
+ rc = -1;
+ }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){
+ /* Leave rc==0 */
+ }else if( idx1<szHdr1 ){
+ rc = 1;
}
-
return rc;
}
@@ -2924,7 +3061,7 @@ int sqlite3VdbeIdxKeyCompare(
** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */
if( nCellKey<=0 || nCellKey>0x7fffffff ){
*res = 0;
- return SQLITE_CORRUPT;
+ return SQLITE_CORRUPT_BKPT;
}
memset(&m, 0, sizeof(m));
rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (int)nCellKey, 1, &m);
@@ -2978,3 +3115,42 @@ void sqlite3ExpirePreparedStatements(sqlite3 *db){
sqlite3 *sqlite3VdbeDb(Vdbe *v){
return v->db;
}
+
+/*
+** Return a pointer to an sqlite3_value structure containing the value bound
+** parameter iVar of VM v. Except, if the value is an SQL NULL, return
+** 0 instead. Unless it is NULL, apply affinity aff (one of the SQLITE_AFF_*
+** constants) to the value before returning it.
+**
+** The returned value must be freed by the caller using sqlite3ValueFree().
+*/
+sqlite3_value *sqlite3VdbeGetValue(Vdbe *v, int iVar, u8 aff){
+ assert( iVar>0 );
+ if( v ){
+ Mem *pMem = &v->aVar[iVar-1];
+ if( 0==(pMem->flags & MEM_Null) ){
+ sqlite3_value *pRet = sqlite3ValueNew(v->db);
+ if( pRet ){
+ sqlite3VdbeMemCopy((Mem *)pRet, pMem);
+ sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8);
+ sqlite3VdbeMemStoreType((Mem *)pRet);
+ }
+ return pRet;
+ }
+ }
+ return 0;
+}
+
+/*
+** Configure SQL variable iVar so that binding a new value to it signals
+** to sqlite3_reoptimize() that re-preparing the statement may result
+** in a better query plan.
+*/
+void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
+ assert( iVar>0 );
+ if( iVar>32 ){
+ v->expmask = 0xffffffff;
+ }else{
+ v->expmask |= ((u32)1 << (iVar-1));
+ }
+}
« no previous file with comments | « third_party/sqlite/src/src/vdbeapi.c ('k') | third_party/sqlite/src/src/vdbeblob.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698