** 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.461 2005/03/29 03:11:00 danielk1977 Exp $
+** $Id: vdbe.c,v 1.462 2005/03/29 08:26:13 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
int nProgressOps = 0; /* Opcodes executed since progress callback. */
#endif
+#ifndef NDEBUG
+ Mem *pStackLimit;
+#endif
if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE;
assert( db->magic==SQLITE_MAGIC_BUSY );
}
#endif
+#ifndef NDEBUG
+ /* This is to check that the return value of static function
+ ** opcodeUsesStack() (see vdbeaux.c) returns values that match the
+ ** implementation of the virtual machine in this file. If
+ ** opcodeUsesStack() returns non-zero, then the stack is guarenteed
+ ** not to grow when the opcode is executed. If it returns zero, then
+ ** the stack may grow by at most 1.
+ **
+ ** The global wrapper function sqlite3VdbeOpcodeUsesStack() is not
+ ** available if NDEBUG is defined at build time.
+ */
+ pStackLimit = pTos;
+ if( !sqlite3VdbeOpcodeUsesStack(pOp->opcode) ){
+ pStackLimit++;
+ }
+#endif
+
switch( pOp->opcode ){
/*****************************************************************************
** case statement is followed by a comment of the form "/# same as ... #/"
** that comment is used to determine the particular value of the opcode.
**
+** If a comment on the same line as the "case OP_" construction contains
+** the word "stack", then the opcode is guarenteed not to grow the
+** vdbe stack when it is executed. See function opcodeUsesStack() in
+** vdbeaux.c for details.
+**
** Documentation about VDBE opcodes is generated by scanning this file
** for lines of that contain "Opcode:". That line and all subsequent
** comment lines are used in the generation of the opcode.html documentation
** the one at index P2 from the beginning of
** the program.
*/
-case OP_Goto: {
+case OP_Goto: { /* no stack growth */
CHECK_FOR_INTERRUPT;
pc = pOp->p2 - 1;
break;
** the return address stack will fill up and processing will abort
** with a fatal error.
*/
-case OP_Gosub: {
+case OP_Gosub: { /* no stack growth */
assert( p->returnDepth<sizeof(p->returnStack)/sizeof(p->returnStack[0]) );
p->returnStack[p->returnDepth++] = pc+1;
pc = pOp->p2 - 1;
** OP_Gosub. If an OP_Return has occurred for all OP_Gosubs, then
** processing aborts with a fatal error.
*/
-case OP_Return: {
+case OP_Return: { /* no stack growth */
assert( p->returnDepth>0 );
p->returnDepth--;
pc = p->returnStack[p->returnDepth] - 1;
** every program. So a jump past the last instruction of the program
** is the same as executing Halt.
*/
-case OP_Halt: {
+case OP_Halt: { /* no stack growth */
p->pTos = pTos;
p->rc = pOp->p1;
p->pc = pc;
**
** The string value P3 is converted to a real and pushed on to the stack.
*/
-case OP_Real: { /* same as TK_FLOAT */
+case OP_Real: { /* same as TK_FLOAT, */
pTos++;
pTos->flags = MEM_Str|MEM_Static|MEM_Term;
pTos->z = pOp->p3;
**
** P1 elements are popped off of the top of stack and discarded.
*/
-case OP_Pop: {
+case OP_Pop: { /* no stack growth */
assert( pOp->p1>=0 );
popStack(&pTos, pOp->p1);
assert( pTos>=&p->aStack[-1] );
**
** See also the Dup instruction.
*/
-case OP_Pull: {
+case OP_Pull: { /* no stack growth */
Mem *pFrom = &pTos[-pOp->p1];
int i;
Mem ts;
** stack (P1==0 is the top of the stack) with the value
** of the top of the stack. Then pop the top of the stack.
*/
-case OP_Push: {
+case OP_Push: { /* no stack growth */
Mem *pTo = &pTos[-pOp->p1];
assert( pTo>=p->aStack );
** invoke the callback function using the newly formed array as the
** 3rd parameter.
*/
-case OP_Callback: {
+case OP_Callback: { /* no stack growth */
int i;
assert( p->nResColumn==pOp->p1 );
** function before the division. Division by zero returns NULL.
** If either operand is NULL, the result is NULL.
*/
-case OP_Add: /* same as TK_PLUS */
-case OP_Subtract: /* same as TK_MINUS */
-case OP_Multiply: /* same as TK_STAR */
-case OP_Divide: /* same as TK_SLASH */
-case OP_Remainder: { /* same as TK_REM */
+case OP_Add: /* same as TK_PLUS, no stack growth */
+case OP_Subtract: /* same as TK_MINUS, no stack growth */
+case OP_Multiply: /* same as TK_STAR, no stack growth */
+case OP_Divide: /* same as TK_SLASH, no stack growth */
+case OP_Remainder: { /* same as TK_REM, no stack growth */
Mem *pNos = &pTos[-1];
assert( pNos>=p->aStack );
if( ((pTos->flags | pNos->flags) & MEM_Null)!=0 ){
** to retrieve the collation sequence set by this opcode is not available
** publicly, only to user functions defined in func.c.
*/
-case OP_CollSeq: {
+case OP_CollSeq: { /* no stack growth */
assert( pOp->p3type==P3_COLLSEQ );
break;
}
** right by N bits where N is the top element on the stack.
** If either operand is NULL, the result is NULL.
*/
-case OP_BitAnd: /* same as TK_BITAND */
-case OP_BitOr: /* same as TK_BITOR */
-case OP_ShiftLeft: /* same as TK_LSHIFT */
-case OP_ShiftRight: { /* same as TK_RSHIFT */
+case OP_BitAnd: /* same as TK_BITAND, no stack growth */
+case OP_BitOr: /* same as TK_BITOR, no stack growth */
+case OP_ShiftLeft: /* same as TK_LSHIFT, no stack growth */
+case OP_ShiftRight: { /* same as TK_RSHIFT, no stack growth */
Mem *pNos = &pTos[-1];
int a, b;
**
** To force the top of the stack to be an integer, just add 0.
*/
-case OP_AddImm: {
+case OP_AddImm: { /* no stack growth */
assert( pTos>=p->aStack );
Integerify(pTos);
pTos->i += pOp->p1;
** current value if P1==0, or to the least integer that is strictly
** greater than its current value if P1==1.
*/
-case OP_ForceInt: {
+case OP_ForceInt: { /* no stack growth */
int v;
assert( pTos>=p->aStack );
applyAffinity(pTos, SQLITE_AFF_INTEGER, db->enc);
** P1 is 1, then the stack is popped. In all other cases, the depth
** of the stack is unchanged.
*/
-case OP_MustBeInt: {
+case OP_MustBeInt: { /* no stack growth */
assert( pTos>=p->aStack );
applyAffinity(pTos, SQLITE_AFF_INTEGER, db->enc);
if( (pTos->flags & MEM_Int)==0 ){
** the 2nd element down on the stack is greater than or equal to the
** top of the stack. See the Eq opcode for additional information.
*/
-case OP_Eq: /* same as TK_EQ */
-case OP_Ne: /* same as TK_NE */
-case OP_Lt: /* same as TK_LT */
-case OP_Le: /* same as TK_LE */
-case OP_Gt: /* same as TK_GT */
-case OP_Ge: { /* same as TK_GE */
+case OP_Eq: /* same as TK_EQ, no stack growth */
+case OP_Ne: /* same as TK_NE, no stack growth */
+case OP_Lt: /* same as TK_LT, no stack growth */
+case OP_Le: /* same as TK_LE, no stack growth */
+case OP_Gt: /* same as TK_GT, no stack growth */
+case OP_Ge: { /* same as TK_GE, no stack growth */
Mem *pNos;
int flags;
int res;
** two values and push the resulting boolean value back onto the
** stack.
*/
-case OP_And: /* same as TK_AND */
-case OP_Or: { /* same as TK_OR */
+case OP_And: /* same as TK_AND, no stack growth */
+case OP_Or: { /* same as TK_OR, no stack growth */
Mem *pNos = &pTos[-1];
int v1, v2; /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */
** with its absolute value. If the top of the stack is NULL
** its value is unchanged.
*/
-case OP_Negative: /* same as TK_UMINUS */
+case OP_Negative: /* same as TK_UMINUS, no stack growth */
case OP_AbsValue: {
assert( pTos>=p->aStack );
if( pTos->flags & MEM_Real ){
** with its complement. If the top of the stack is NULL its value
** is unchanged.
*/
-case OP_Not: { /* same as TK_NOT */
+case OP_Not: { /* same as TK_NOT, no stack growth */
assert( pTos>=p->aStack );
if( pTos->flags & MEM_Null ) break; /* Do nothing to NULLs */
Integerify(pTos);
** with its ones-complement. If the top of the stack is NULL its
** value is unchanged.
*/
-case OP_BitNot: { /* same as TK_BITNOT */
+case OP_BitNot: { /* same as TK_BITNOT, no stack growth */
assert( pTos>=p->aStack );
if( pTos->flags & MEM_Null ) break; /* Do nothing to NULLs */
Integerify(pTos);
** Do nothing. This instruction is often useful as a jump
** destination.
*/
-case OP_Noop: {
+case OP_Noop: { /* no stack growth */
break;
}
** If the value popped of the stack is NULL, then take the jump if P1
** is true and fall through if P1 is false.
*/
-case OP_If:
-case OP_IfNot: {
+case OP_If: /* no stack growth */
+case OP_IfNot: { /* no stack growth */
int c;
assert( pTos>=p->aStack );
if( pTos->flags & MEM_Null ){
** to P2. Pop the stack P1 times if P1>0. If P1<0 leave the stack
** unchanged.
*/
-case OP_IsNull: { /* same as TK_ISNULL */
+case OP_IsNull: { /* same as TK_ISNULL, no stack growth */
int i, cnt;
Mem *pTerm;
cnt = pOp->p1;
** stack if P1 times if P1 is greater than zero. If P1 is less than
** zero then leave the stack unchanged.
*/
-case OP_NotNull: { /* same as TK_NOTNULL */
+case OP_NotNull: { /* same as TK_NOTNULL, no stack growth */
int i, cnt;
cnt = pOp->p1;
if( cnt<0 ) cnt = -cnt;
** If OP_KeyAsData is to be applied to cursor P1, it must be executed
** before this op-code.
*/
-case OP_SetNumColumns: {
+case OP_SetNumColumns: { /* no stack growth */
Cursor *pC;
assert( (pOp->p1)<p->nCursor );
assert( p->apCsr[pOp->p1]!=0 );
** database file has an index of 0 and the file used for temporary tables
** has an index of 1.
*/
-case OP_Statement: {
+case OP_Statement: { /* no stack growth */
int i = pOp->p1;
Btree *pBt;
if( i>=0 && i<db->nDb && (pBt = db->aDb[i].pBt) && !(db->autoCommit) ){
**
** This instruction causes the VM to halt.
*/
-case OP_AutoCommit: {
+case OP_AutoCommit: { /* no stack growth */
u8 i = pOp->p1;
u8 rollback = pOp->p2;
**
** If P2 is zero, then a read-lock is obtained on the database file.
*/
-case OP_Transaction: {
+case OP_Transaction: { /* no stack growth */
int i = pOp->p1;
Btree *pBt;
**
** A transaction must be started before executing this opcode.
*/
-case OP_SetCookie: {
+case OP_SetCookie: { /* no stack growth */
Db *pDb;
assert( pOp->p2<SQLITE_N_BTREE_META );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
** to be executed (to establish a read lock) before this opcode is
** invoked.
*/
-case OP_VerifyCookie: {
+case OP_VerifyCookie: { /* no stack growth */
int iMeta;
Btree *pBt;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
**
** See also OpenRead.
*/
-case OP_OpenRead:
-case OP_OpenWrite: {
+case OP_OpenRead: /* no stack growth */
+case OP_OpenWrite: { /* no stack growth */
int i = pOp->p1;
int p2 = pOp->p2;
int wrFlag;
** whereas "Temporary" in the context of CREATE TABLE means for the duration
** of the connection to the database. Same word; different meanings.
*/
-case OP_OpenTemp: {
+case OP_OpenTemp: { /* no stack growth */
int i = pOp->p1;
Cursor *pCx;
assert( i>=0 );
** A pseudo-table created by this opcode is useful for holding the
** NEW or OLD tables in a trigger.
*/
-case OP_OpenPseudo: {
+case OP_OpenPseudo: { /* no stack growth */
int i = pOp->p1;
Cursor *pCx;
assert( i>=0 );
** Close a cursor previously opened as P1. If P1 is not
** currently open, this instruction is a no-op.
*/
-case OP_Close: {
+case OP_Close: { /* no stack growth */
int i = pOp->p1;
if( i>=0 && i<p->nCursor ){
sqlite3VdbeFreeCursor(p->apCsr[i]);
**
** See also: Found, NotFound, Distinct, MoveGt, MoveGe, MoveLt
*/
-case OP_MoveLt:
-case OP_MoveLe:
-case OP_MoveGe:
-case OP_MoveGt: {
+case OP_MoveLt: /* no stack growth */
+case OP_MoveLe: /* no stack growth */
+case OP_MoveGe: /* no stack growth */
+case OP_MoveGt: { /* no stack growth */
int i = pOp->p1;
Cursor *pC;
**
** See also: Distinct, Found, MoveTo, NotExists, IsUnique
*/
-case OP_Distinct:
-case OP_NotFound:
-case OP_Found: {
+case OP_Distinct: /* no stack growth */
+case OP_NotFound: /* no stack growth */
+case OP_Found: { /* no stack growth */
int i = pOp->p1;
int alreadyExists = 0;
Cursor *pC;
**
** See also: Distinct, NotFound, NotExists, Found
*/
-case OP_IsUnique: {
+case OP_IsUnique: { /* no stack growth */
int i = pOp->p1;
Mem *pNos = &pTos[-1];
Cursor *pCx;
**
** See also: Distinct, Found, MoveTo, NotFound, IsUnique
*/
-case OP_NotExists: {
+case OP_NotExists: { /* no stack growth */
int i = pOp->p1;
Cursor *pC;
BtCursor *pCrsr;
**
** P1 may not be a pseudo-table opened using the OpenPseudo opcode.
*/
-case OP_PutIntKey:
-case OP_PutStrKey: {
+case OP_PutIntKey: /* no stack growth */
+case OP_PutStrKey: { /* no stack growth */
Mem *pNos = &pTos[-1];
int i = pOp->p1;
Cursor *pC;
**
** If P1 is a pseudo-table, then this instruction is a no-op.
*/
-case OP_Delete: {
+case OP_Delete: { /* no stack growth */
int i = pOp->p1;
Cursor *pC;
assert( i>=0 && i<p->nCursor );
** change counter (returned by subsequent calls to sqlite3_changes())
** before it is reset. This is used by trigger programs.
*/
-case OP_ResetCount: {
+case OP_ResetCount: { /* no stack growth */
if( pOp->p1 ){
sqlite3VdbeSetChanges(db, p->nChange);
}
** data off of the key rather than the data. This is used for
** processing compound selects.
*/
-case OP_KeyAsData: {
+case OP_KeyAsData: { /* no stack growth */
int i = pOp->p1;
Cursor *pC;
assert( i>=0 && i<p->nCursor );
** that occur while the cursor is on the null row will always push
** a NULL onto the stack.
*/
-case OP_NullRow: {
+case OP_NullRow: { /* no stack growth */
int i = pOp->p1;
Cursor *pC;
** If P2 is 0 or if the table or index is not empty, fall through
** to the following instruction.
*/
-case OP_Last: {
+case OP_Last: { /* no stack growth */
int i = pOp->p1;
Cursor *pC;
BtCursor *pCrsr;
** If P2 is 0 or if the table or index is not empty, fall through
** to the following instruction.
*/
-case OP_Rewind: {
+case OP_Rewind: { /* no stack growth */
int i = pOp->p1;
Cursor *pC;
BtCursor *pCrsr;
** to the following instruction. But if the cursor backup was successful,
** jump immediately to P2.
*/
-case OP_Prev:
-case OP_Next: {
+case OP_Prev: /* no stack growth */
+case OP_Next: { /* no stack growth */
Cursor *pC;
BtCursor *pCrsr;
** is rolled back. If P3 is not null, then it becomes part of the
** error message returned with the SQLITE_CONSTRAINT.
*/
-case OP_IdxPut: {
+case OP_IdxPut: { /* no stack growth */
int i = pOp->p1;
Cursor *pC;
BtCursor *pCrsr;
** The top of the stack is an index key built using the MakeIdxKey opcode.
** This opcode removes that entry from the index.
*/
-case OP_IdxDelete: {
+case OP_IdxDelete: { /* no stack growth */
int i = pOp->p1;
Cursor *pC;
BtCursor *pCrsr;
** an epsilon prior to the comparison. This makes the opcode work
** like IdxLE.
*/
-case OP_IdxLT:
-case OP_IdxGT:
-case OP_IdxGE: {
+case OP_IdxLT: /* no stack growth */
+case OP_IdxGT: /* no stack growth */
+case OP_IdxGE: { /* no stack growth */
int i= pOp->p1;
BtCursor *pCrsr;
Cursor *pC;
**
** The index entry is always popped from the stack.
*/
-case OP_IdxIsNull: {
+case OP_IdxIsNull: { /* no stack growth */
int i = pOp->p1;
int k, n;
const char *z;
**
** See also: Destroy
*/
-case OP_Clear: {
+case OP_Clear: { /* no stack growth */
rc = sqlite3BtreeClearTable(db->aDb[pOp->p2].pBt, pOp->p1);
break;
}
** This opcode invokes the parser to create a new virtual machine,
** then runs the new virtual machine. It is thus a reentrant opcode.
*/
-case OP_ParseSchema: {
+case OP_ParseSchema: { /* no stack growth */
char *zSql;
int iDb = pOp->p1;
const char *zMaster;
** is dropped in order to keep the internal representation of the
** schema consistent with what is on disk.
*/
-case OP_DropTable: {
+case OP_DropTable: { /* no stack growth */
sqlite3UnlinkAndDeleteTable(db, pOp->p1, pOp->p3);
break;
}
** is dropped in order to keep the internal representation of the
** schema consistent with what is on disk.
*/
-case OP_DropIndex: {
+case OP_DropIndex: { /* no stack growth */
sqlite3UnlinkAndDeleteIndex(db, pOp->p1, pOp->p3);
break;
}
** is dropped in order to keep the internal representation of the
** schema consistent with what is on disk.
*/
-case OP_DropTrigger: {
+case OP_DropTrigger: { /* no stack growth */
sqlite3UnlinkAndDeleteTrigger(db, pOp->p1, pOp->p3);
break;
}
** Write the integer on the top of the stack
** into the temporary storage list.
*/
-case OP_ListWrite: {
+case OP_ListWrite: { /* no stack growth */
Keylist *pKeylist;
assert( pTos>=p->aStack );
pKeylist = p->pList;
**
** Rewind the temporary buffer back to the beginning.
*/
-case OP_ListRewind: {
+case OP_ListRewind: { /* no stack growth */
/* What this opcode codes, really, is reverse the order of the
** linked list of Keylist structures so that they are read out
** in the same order that they were read in. */
**
** Reset the temporary storage buffer so that it holds nothing.
*/
-case OP_ListReset: {
+case OP_ListReset: { /* no stack growth */
if( p->pList ){
sqlite3VdbeKeylistFree(p->pList);
p->pList = 0;
** AggContextPop opcode.
**
*/
-case OP_AggContextPush: {
+case OP_AggContextPush: { /* no stack growth */
p->pAgg++;
assert( p->pAgg<&p->apAgg[p->nAgg] );
break;
** Restore the aggregator to the state it was in when AggContextPush
** was last called. Any data in the current aggregator is deleted.
*/
-case OP_AggContextPop: {
+case OP_AggContextPop: { /* no stack growth */
p->pAgg--;
assert( p->pAgg>=p->apAgg );
break;
** opcode. The context stores the last insert row id, the last statement change
** count, and the current statement change count.
*/
-case OP_ContextPush: {
+case OP_ContextPush: { /* no stack growth */
int i = p->contextStackTop++;
Context *pContext;
** executed. The context stores the last insert row id, the last statement
** change count, and the current statement change count.
*/
-case OP_ContextPop: {
+case OP_ContextPop: { /* no stack growth */
Context *pContext = &p->contextStack[--p->contextStackTop];
assert( p->contextStackTop>=0 );
db->lastRowid = pContext->lastRowid;
** and put them on the sorter. The key and data should have been
** made using the MakeRecord opcode.
*/
-case OP_SortPut: {
+case OP_SortPut: { /* no stack growth */
Mem *pNos = &pTos[-1];
Sorter *pSorter;
assert( pNos>=p->aStack );
** mergesort. The P3 argument is a pointer to a KeyInfo structure
** that describes the keys to be sorted.
*/
-case OP_Sort: {
+case OP_Sort: { /* no stack growth */
int i;
KeyInfo *pKeyInfo = (KeyInfo*)pOp->p3;
Sorter *pElem;
**
** Remove any elements that remain on the sorter.
*/
-case OP_SortReset: {
+case OP_SortReset: { /* no stack growth */
sqlite3VdbeSorterReset(p);
break;
}
** stack is popped once if P2 is 1. If P2 is zero, then
** the original data remains on the stack.
*/
-case OP_MemStore: {
+case OP_MemStore: { /* no stack growth */
assert( pTos>=p->aStack );
assert( pOp->p1>=0 && pOp->p1<p->nMem );
rc = sqlite3VdbeMemMove(&p->aMem[pOp->p1], pTos);
** This instruction throws an error if the memory cell is not initially
** an integer.
*/
-case OP_MemMax: {
+case OP_MemMax: { /* no stack growth */
int i = pOp->p1;
Mem *pMem;
assert( pTos>=p->aStack );
** This instruction throws an error if the memory cell is not initially
** an integer.
*/
-case OP_MemIncr: {
+case OP_MemIncr: { /* no stack growth */
int i = pOp->p1;
Mem *pMem;
assert( i>=0 && i<p->nMem );
** If the value of memory cell P1 is 1 or greater, jump to P2. This
** opcode assumes that memory cell P1 holds an integer value.
*/
-case OP_IfMemPos: {
+case OP_IfMemPos: { /* no stack growth */
int i = pOp->p1;
Mem *pMem;
assert( i>=0 && i<p->nMem );
** there is no GROUP BY expression). In this case it is illegal to invoke
** OP_AggFocus.
*/
-case OP_AggReset: {
+case OP_AggReset: { /* no stack growth */
assert( !pOp->p3 || pOp->p3type==P3_KEYINFO );
if( pOp->p1 ){
rc = sqlite3VdbeAggReset(0, p->pAgg, (KeyInfo *)pOp->p3);
** The aggregate will operate out of aggregate column P2.
** P3 is a pointer to the FuncDef structure for the function.
*/
-case OP_AggInit: {
+case OP_AggInit: { /* no stack growth */
int i = pOp->p2;
assert( i>=0 && i<p->pAgg->nMem );
p->pAgg->apFunc[i] = (FuncDef*)pOp->p3;
** Ideally, this index would be another parameter, but there are
** no free parameters left. The integer is popped from the stack.
*/
-case OP_AggFunc: {
+case OP_AggFunc: { /* no stack growth */
int n = pOp->p2;
int i;
Mem *pMem, *pRec;
** zero or more AggNext operations. You must not execute an AggFocus
** in between an AggNext and an AggReset.
*/
-case OP_AggFocus: {
+case OP_AggFocus: { /* no stack growth */
char *zKey;
int nKey;
int res;
** Move the top of the stack into the P2-th field of the current
** aggregate. String values are duplicated into new memory.
*/
-case OP_AggSet: {
+case OP_AggSet: { /* no stack growth */
AggElem *pFocus;
int i = pOp->p2;
pFocus = p->pAgg->pCurrent;
** zero or more AggNext operations. You must not execute an AggFocus
** in between an AggNext and an AggReset.
*/
-case OP_AggNext: {
+case OP_AggNext: { /* no stack growth */
int res;
assert( rc==SQLITE_OK );
CHECK_FOR_INTERRUPT;
** machines to be created and run. It may not be called from within
** a transaction.
*/
-case OP_Vacuum: {
+case OP_Vacuum: { /* no stack growth */
if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
rc = sqlite3RunVacuum(&p->zErrMsg, db);
if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
** If P1 is 0, then all SQL statements become expired. If P1 is non-zero,
** then only the currently executing statement is affected.
*/
-case OP_Expire: {
+case OP_Expire: { /* no stack growth */
if( !pOp->p1 ){
sqlite3ExpirePreparedStatements(db);
}else{
*****************************************************************************/
}
+ /* Make sure the stack limit was not exceeded */
+ assert( pTos<=pStackLimit );
+
#ifdef VDBE_PROFILE
{
long long elapse = hwtime() - start;
}
}
+/*
+** Return non-zero if opcode 'op' is guarenteed not to push more values
+** onto the VDBE stack than it pops off.
+*/
+static int opcodeUsesStack(u8 op){
+ /* The 10 STACK_MASK_n constants are defined in the automatically
+ ** generated header file opcodes.h. Each is a 16-bit bitmask, one
+ ** bit corresponding to each opcode implemented by the virtual
+ ** machine in vdbe.c. The bit is true if the word "stack" appears
+ ** in a comment on the same line as the "case OP_XXX:" in
+ ** sqlite3VdbeExec() in vdbe.c.
+ **
+ ** If the bit is true, then the corresponding opcode is guarenteed not
+ ** to grow the stack when it is executed. Otherwise, it may grow the
+ ** stack by at most one entry.
+ **
+ ** STACK_MASK_0 corresponds to opcodes 0 to 15. STACK_MASK_1 contains
+ ** one bit for opcodes 16 to 31, and so on.
+ **
+ ** 16-bit bitmasks (rather than 32-bit) are specified in opcodes.h
+ ** because the file is generated by an awk program. Awk manipulates
+ ** all numbers as floating-point and we don't want to risk a rounding
+ ** error if someone builds with an awk that uses (for example) 32-bit
+ ** IEEE floats.
+ */
+ static u32 masks[5] = {
+ STACK_MASK_0 + (STACK_MASK_1<<16),
+ STACK_MASK_2 + (STACK_MASK_3<<16),
+ STACK_MASK_4 + (STACK_MASK_5<<16),
+ STACK_MASK_6 + (STACK_MASK_7<<16),
+ STACK_MASK_8 + (STACK_MASK_9<<16)
+ };
+ return (masks[op>>5] & (1<<(op&0x1F)));
+}
+
+#ifndef NDEBUG
+int sqlite3VdbeOpcodeUsesStack(u8 op){
+ return opcodeUsesStack(op);
+}
+#endif
+
/*
** Loop through the program looking for P2 values that are negative.
** Each such value is a label. Resolve the label by setting the P2
** Variable *pMaxFuncArgs is set to the maximum value of any P1 argument
** to an OP_Function or P2 to an OP_AggFunc opcode. This is used by
** sqlite3VdbeMakeReady() to size the Vdbe.apArg[] array.
+**
+** The integer *pMaxStack is set to the maximum number of vdbe stack
+** entries that static analysis reveals this program might need.
*/
-static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
+static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs, int *pMaxStack){
int i;
- int nMax = 0;
+ int nMaxArgs = 0;
+ int nMaxStack = p->nOp;
Op *pOp;
int *aLabel = p->aLabel;
- if( aLabel==0 ) return;
for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
u8 opcode = pOp->opcode;
/* Todo: Maybe OP_AggFunc should change to use P1 in the same
- * way as OP_Function. */
+ * way as OP_Function.
+ */
if( opcode==OP_Function ){
- if( pOp->p1>nMax ) nMax = pOp->p1;
+ if( pOp->p1>nMaxArgs ) nMaxArgs = pOp->p1;
}else if( opcode==OP_AggFunc ){
- if( pOp->p2>nMax ) nMax = pOp->p2;
+ if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
+ }
+
+ if( opcodeUsesStack(opcode) ){
+ nMaxStack--;
}
if( pOp->p2>=0 ) continue;
}
sqliteFree(p->aLabel);
p->aLabel = 0;
- *pMaxFuncArgs = nMax;
+
+ *pMaxFuncArgs = nMaxArgs;
+ *pMaxStack = nMaxStack;
}
/*
*/
if( p->aStack==0 ){
int nArg; /* Maximum number of args passed to a user function. */
- resolveP2Values(p, &nArg);
+ int nStack; /* Maximum number of stack entries required */
+ resolveP2Values(p, &nArg, &nStack);
resizeOpArray(p, p->nOp);
assert( nVar>=0 );
- n = isExplain ? 10 : p->nOp;
+ assert( nStack<p->nOp );
+ nStack = isExplain ? 10 : nStack;
p->aStack = sqliteMalloc(
- n*sizeof(p->aStack[0]) /* aStack */
+ nStack*sizeof(p->aStack[0]) /* aStack */
+ nArg*sizeof(Mem*) /* apArg */
+ nVar*sizeof(Mem) /* aVar */
+ nVar*sizeof(char*) /* azVar */
+ nAgg*sizeof(Agg) /* Aggregate contexts */
);
if( !sqlite3_malloc_failed ){
- p->aMem = &p->aStack[n];
+ p->aMem = &p->aStack[nStack];
p->nMem = nMem;
p->aVar = &p->aMem[nMem];
p->nVar = nVar;