diff options
Diffstat (limited to 'ext/sqlite/libsqlite/src/vdbe.c')
-rw-r--r-- | ext/sqlite/libsqlite/src/vdbe.c | 528 |
1 files changed, 318 insertions, 210 deletions
diff --git a/ext/sqlite/libsqlite/src/vdbe.c b/ext/sqlite/libsqlite/src/vdbe.c index f9b94824c4..3ac4a3acc8 100644 --- a/ext/sqlite/libsqlite/src/vdbe.c +++ b/ext/sqlite/libsqlite/src/vdbe.c @@ -79,6 +79,11 @@ typedef unsigned char Bool; ** ** Every cursor that the virtual machine has open is represented by an ** instance of the following structure. +** +** If the Cursor.isTriggerRow flag is set it means that this cursor is +** really a single row that represents the NEW or OLD pseudo-table of +** a row trigger. The data for the row is stored in Cursor.pData and +** the rowid is in Cursor.iKey. */ struct Cursor { BtCursor *pCursor; /* The cursor structure of the backend */ @@ -90,7 +95,11 @@ struct Cursor { Bool useRandomRowid; /* Generate new record numbers semi-randomly */ Bool nullRow; /* True if pointing to a row with no data */ Bool nextRowidValid; /* True if the nextRowid field is valid */ + Bool pseudoTable; /* This is a NEW or OLD pseudo-tables of a trigger */ Btree *pBt; /* Separate file holding temporary table */ + int nData; /* Number of bytes in pData */ + char *pData; /* Data for a NEW or OLD pseudo-table */ + int iKey; /* Key for the NEW or OLD pseudo-table row */ }; typedef struct Cursor Cursor; @@ -243,7 +252,6 @@ struct Keylist { struct Vdbe { sqlite *db; /* The whole database */ Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ - Btree *pBt; /* Opaque context structure used by DB backend */ FILE *trace; /* Write an execution trace here, if not NULL */ int nOp; /* Number of instructions in the program */ int nOpAlloc; /* Number of slots allocated for aOp[] */ @@ -315,7 +323,6 @@ Vdbe *sqliteVdbeCreate(sqlite *db){ Vdbe *p; p = sqliteMalloc( sizeof(Vdbe) ); if( p==0 ) return 0; - p->pBt = db->pBe; p->db = db; if( db->pVdbe ){ db->pVdbe->pPrev = p; @@ -478,6 +485,7 @@ int sqliteVdbeAddOpList(Vdbe *p, int nOp, VdbeOp const *aOp){ return addr; } +#if 0 /* NOT USED */ /* ** Change the value of the P1 operand for a specific instruction. ** This routine is useful when a large program is loaded from a @@ -490,6 +498,7 @@ void sqliteVdbeChangeP1(Vdbe *p, int addr, int val){ p->aOp[addr].p1 = val; } } +#endif /* NOT USED */ /* ** Change the value of the P2 operand for a specific instruction. @@ -605,18 +614,27 @@ void sqliteVdbeCompressSpace(Vdbe *p, int addr){ /* ** Search for the current program for the given opcode and P2 -** value. Return 1 if found and 0 if not found. +** value. Return the address plus 1 if found and 0 if not found. */ int sqliteVdbeFindOp(Vdbe *p, int op, int p2){ int i; assert( p->magic==VDBE_MAGIC_INIT ); for(i=0; i<p->nOp; i++){ - if( p->aOp[i].opcode==op && p->aOp[i].p2==p2 ) return 1; + if( p->aOp[i].opcode==op && p->aOp[i].p2==p2 ) return i+1; } return 0; } /* +** Return the opcode for a given address. +*/ +VdbeOp *sqliteVdbeGetOp(Vdbe *p, int addr){ + assert( p->magic==VDBE_MAGIC_INIT ); + assert( addr>=0 && addr<p->nOp ); + return &p->aOp[addr]; +} + +/* ** The following group or routines are employed by installable functions ** to return their results. ** @@ -994,6 +1012,7 @@ static int toInt(const char *zNum, int *pNum){ }else{ neg = 0; } + if( *zNum==0 ) return 0; while( isdigit(*zNum) ){ v = v*10 + *zNum - '0'; zNum++; @@ -1073,27 +1092,6 @@ static void PopStack(Vdbe *p, int N){ p->tos--; /* -** Return TRUE if zNum is a floating-point or integer number. -*/ -static int isNumber(const char *zNum){ - if( *zNum=='-' || *zNum=='+' ) zNum++; - if( !isdigit(*zNum) ) return 0; - while( isdigit(*zNum) ) zNum++; - if( *zNum==0 ) return 1; - if( *zNum!='.' ) return 0; - zNum++; - if( !isdigit(*zNum) ) return 0; - while( isdigit(*zNum) ) zNum++; - if( *zNum==0 ) return 1; - if( *zNum!='e' && *zNum!='E' ) return 0; - zNum++; - if( *zNum=='-' || *zNum=='+' ) zNum++; - if( !isdigit(*zNum) ) return 0; - while( isdigit(*zNum) ) zNum++; - return *zNum==0; -} - -/* ** Delete a keylist */ static void KeylistFree(Keylist *p){ @@ -1115,6 +1113,7 @@ static void cleanupCursor(Cursor *pCx){ if( pCx->pBt ){ sqliteBtreeClose(pCx->pBt); } + sqliteFree(pCx->pData); memset(pCx, 0, sizeof(Cursor)); } @@ -1300,7 +1299,7 @@ int sqliteVdbeList( p->rc = SQLITE_MISUSE; } } - return p->rc==SQLITE_OK ? SQLITE_OK : SQLITE_ERROR; + return p->rc==SQLITE_OK ? SQLITE_DONE : SQLITE_ERROR; } /* @@ -1498,6 +1497,9 @@ void sqliteVdbeMakeReady( int isExplain /* True if the EXPLAIN keywords is present */ ){ int n; +#ifdef MEMORY_DEBUG + extern int access(const char*,int); +#endif assert( p!=0 ); assert( p->aStack==0 ); @@ -1569,8 +1571,8 @@ void sqliteVdbeMakeReady( ** immediately. There will be no error message but the p->rc field is ** set to SQLITE_ABORT and this routine will return SQLITE_ERROR. ** -** A memory allocation error causes p->rc to be set SQLITE_NOMEM and this -** routien to return SQLITE_ERROR. +** A memory allocation error causes p->rc to be set to SQLITE_NOMEM and this +** routine to return SQLITE_ERROR. ** ** Other fatal errors return SQLITE_ERROR. ** @@ -1583,7 +1585,6 @@ int sqliteVdbeExec( int pc; /* The program counter */ Op *pOp; /* Current operation */ int rc = SQLITE_OK; /* Value to return */ - Btree *pBt = p->pBt; /* The backend driver */ sqlite *db = p->db; /* The database */ char **zStack = p->zStack; /* Text stack */ Stack *aStack = p->aStack; /* Additional stack information */ @@ -1893,6 +1894,7 @@ case OP_Push: { ** to all column names is passed as the 4th parameter to the callback. */ case OP_ColumnName: { + assert( pOp->p1>=0 && pOp->p1<p->nOp ); p->azColName[pOp->p1] = pOp->p3; p->nCallback = 0; break; @@ -3061,7 +3063,7 @@ case OP_MakeKey: { Stringify(p, i); aStack[i].flags &= ~(STK_Int|STK_Real); nByte += aStack[i].n+1; - }else if( (flags & (STK_Real|STK_Int))!=0 || isNumber(zStack[i]) ){ + }else if( (flags & (STK_Real|STK_Int))!=0 || sqliteIsNumber(zStack[i]) ){ if( (flags & (STK_Real|STK_Int))==STK_Int ){ aStack[i].r = aStack[i].i; }else if( (flags & (STK_Real|STK_Int))==0 ){ @@ -3153,17 +3155,21 @@ case OP_IncrKey: { break; } -/* Opcode: Checkpoint * * * +/* Opcode: Checkpoint P1 * * ** ** Begin a checkpoint. A checkpoint is the beginning of a operation that ** is part of a larger transaction but which might need to be rolled back ** itself without effecting the containing transaction. A checkpoint will ** be automatically committed or rollback when the VDBE halts. +** +** The checkpoint is begun on the database file with index P1. The main +** database file has an index of 0 and the file used for temporary tables +** has an index of 1. */ case OP_Checkpoint: { - rc = sqliteBtreeBeginCkpt(pBt); - if( rc==SQLITE_OK && db->pBeTemp ){ - rc = sqliteBtreeBeginCkpt(db->pBeTemp); + int i = pOp->p1; + if( i>=0 && i<db->nDb && db->aDb[i].pBt ){ + rc = sqliteBtreeBeginCkpt(db->aDb[i].pBt); } break; } @@ -3174,10 +3180,9 @@ case OP_Checkpoint: { ** opcode is encountered. Depending on the ON CONFLICT setting, the ** transaction might also be rolled back if an error is encountered. ** -** If P1 is true, then the transaction is started on the temporary -** tables of the database only. The main database file is not write -** locked and other processes can continue to read the main database -** file. +** P1 is the index of the database file on which the transaction is +** started. Index 0 is the main database file and index 1 is the +** file used for temporary tables. ** ** A write lock is obtained on the database file when a transaction is ** started. No other process can read or write the file while the @@ -3187,15 +3192,11 @@ case OP_Checkpoint: { */ case OP_Transaction: { int busy = 1; - if( db->pBeTemp && !p->inTempTrans ){ - rc = sqliteBtreeBeginTrans(db->pBeTemp); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - p->inTempTrans = 1; - } - while( pOp->p1==0 && busy ){ - rc = sqliteBtreeBeginTrans(pBt); + int i = pOp->p1; + assert( i>=0 && i<db->nDb ); + if( db->aDb[i].inTrans ) break; + while( db->aDb[i].pBt!=0 && busy ){ + rc = sqliteBtreeBeginTrans(db->aDb[i].pBt); switch( rc ){ case SQLITE_BUSY: { if( db->xBusyCallback==0 ){ @@ -3223,6 +3224,7 @@ case OP_Transaction: { } } } + db->aDb[i].inTrans = 1; p->undoTransOnError = 1; break; } @@ -3236,53 +3238,47 @@ case OP_Transaction: { ** A read lock continues to be held if there are still cursors open. */ case OP_Commit: { - if( db->pBeTemp==0 || (rc = sqliteBtreeCommit(db->pBeTemp))==SQLITE_OK ){ - rc = p->inTempTrans ? SQLITE_OK : sqliteBtreeCommit(pBt); + int i; + for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ + if( db->aDb[i].inTrans ){ + rc = sqliteBtreeCommit(db->aDb[i].pBt); + db->aDb[i].inTrans = 0; + } } if( rc==SQLITE_OK ){ sqliteCommitInternalChanges(db); }else{ - if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp); - sqliteBtreeRollback(pBt); - sqliteRollbackInternalChanges(db); + sqliteRollbackAll(db); } - p->inTempTrans = 0; break; } -/* Opcode: Rollback * * * +/* Opcode: Rollback P1 * * ** ** Cause all modifications to the database that have been made since the ** last Transaction to be undone. The database is restored to its state ** before the Transaction opcode was executed. No additional modifications ** are allowed until another transaction is started. ** +** P1 is the index of the database file that is committed. An index of 0 +** is used for the main database and an index of 1 is used for the file used +** to hold temporary tables. +** ** This instruction automatically closes all cursors and releases both -** the read and write locks on the database. +** the read and write locks on the indicated database. */ case OP_Rollback: { - if( db->pBeTemp ){ - sqliteBtreeRollback(db->pBeTemp); - } - rc = sqliteBtreeRollback(pBt); - sqliteRollbackInternalChanges(db); + sqliteRollbackAll(db); break; } -/* Opcode: ReadCookie * P2 * -** -** When P2==0, -** read the schema cookie from the database file and push it onto the -** stack. The schema cookie is an integer that is used like a version -** number for the database schema. Everytime the schema changes, the -** cookie changes to a new random value. This opcode is used during -** initialization to read the initial cookie value so that subsequent -** database accesses can verify that the cookie has not changed. +/* Opcode: ReadCookie P1 P2 * ** -** If P2>0, then read global database parameter number P2. There is -** a small fixed number of global database parameters. P2==1 is the -** database version number. P2==2 is the recommended pager cache size. -** Other parameters are currently unused. +** Read cookie number P2 from database P1 and push it onto the stack. +** P2==0 is the schema version. P2==1 is the database format. +** P2==2 is the recommended pager cache size, and so forth. P1==0 is +** the main database file and P1==1 is the database file used to store +** temporary tables. ** ** There must be a read-lock on the database (either a transaction ** must be started or there must be an open cursor) before @@ -3292,36 +3288,35 @@ case OP_ReadCookie: { int i = ++p->tos; int aMeta[SQLITE_N_BTREE_META]; assert( pOp->p2<SQLITE_N_BTREE_META ); - rc = sqliteBtreeGetMeta(pBt, aMeta); + assert( pOp->p1>=0 && pOp->p1<db->nDb ); + assert( db->aDb[pOp->p1].pBt!=0 ); + rc = sqliteBtreeGetMeta(db->aDb[pOp->p1].pBt, aMeta); aStack[i].i = aMeta[1+pOp->p2]; aStack[i].flags = STK_Int; break; } -/* Opcode: SetCookie * P2 * +/* Opcode: SetCookie P1 P2 * ** -** When P2==0, -** this operation changes the value of the schema cookie on the database. -** The new value is top of the stack. -** When P2>0, the value of global database parameter -** number P2 is changed. See ReadCookie for more information about -** global database parametes. -** -** The schema cookie changes its value whenever the database schema changes. -** That way, other processes can recognize when the schema has changed -** and reread it. +** Write the top of the stack into cookie number P2 of database P1. +** P2==0 is the schema version. P2==1 is the database format. +** P2==2 is the recommended pager cache size, and so forth. P1==0 is +** the main database file and P1==1 is the database file used to store +** temporary tables. ** ** A transaction must be started before executing this opcode. */ case OP_SetCookie: { int aMeta[SQLITE_N_BTREE_META]; assert( pOp->p2<SQLITE_N_BTREE_META ); + assert( pOp->p1>=0 && pOp->p1<db->nDb ); + assert( db->aDb[pOp->p1].pBt!=0 ); VERIFY( if( p->tos<0 ) goto not_enough_stack; ) Integerify(p, p->tos) - rc = sqliteBtreeGetMeta(pBt, aMeta); + rc = sqliteBtreeGetMeta(db->aDb[pOp->p1].pBt, aMeta); if( rc==SQLITE_OK ){ aMeta[1+pOp->p2] = aStack[p->tos].i; - rc = sqliteBtreeUpdateMeta(pBt, aMeta); + rc = sqliteBtreeUpdateMeta(db->aDb[pOp->p1].pBt, aMeta); } POPSTACK; break; @@ -3329,10 +3324,11 @@ case OP_SetCookie: { /* Opcode: VerifyCookie P1 P2 * ** -** Check the value of global database parameter number P2 and make -** sure it is equal to P1. P2==0 is the schema cookie. P1==1 is -** the database version. If the values do not match, abort with -** an SQLITE_SCHEMA error. +** Check the value of global database parameter number 0 (the +** schema version) and make sure it is equal to P2. +** P1 is the database number which is 0 for the main database file +** and 1 for the file holding temporary tables and some higher number +** for auxiliary databases. ** ** The cookie changes its value whenever the database schema changes. ** This operation is used to detect when that the cookie has changed @@ -3344,23 +3340,26 @@ case OP_SetCookie: { */ case OP_VerifyCookie: { int aMeta[SQLITE_N_BTREE_META]; - assert( pOp->p2<SQLITE_N_BTREE_META ); - rc = sqliteBtreeGetMeta(pBt, aMeta); - if( rc==SQLITE_OK && aMeta[1+pOp->p2]!=pOp->p1 ){ + assert( pOp->p1>=0 && pOp->p1<db->nDb ); + rc = sqliteBtreeGetMeta(db->aDb[pOp->p1].pBt, aMeta); + if( rc==SQLITE_OK && aMeta[1]!=pOp->p2 ){ sqliteSetString(&p->zErrMsg, "database schema has changed", 0); rc = SQLITE_SCHEMA; } break; } -/* Opcode: Open P1 P2 P3 +/* Opcode: OpenRead P1 P2 P3 ** ** Open a read-only cursor for the database table whose root page is -** P2 in the main database file. Give the new cursor an identifier -** of P1. The P1 values need not be contiguous but all P1 values -** should be small integers. It is an error for P1 to be negative. +** P2 in a database file. The database file is determined by an +** integer from the top of the stack. 0 means the main database and +** 1 means the database used for temporary tables. Give the new +** cursor an identifier of P1. The P1 values need not be contiguous +** but all P1 values should be small integers. It is an error for +** P1 to be negative. ** -** If P2==0 then take the root page number from the top of the stack. +** If P2==0 then take the root page number from the next of the stack. ** ** There will be a read lock on the database whenever there is an ** open cursor. If the database was unlocked prior to this instruction @@ -3376,52 +3375,39 @@ case OP_VerifyCookie: { ** omitted. But the code generator usually inserts the index or ** table name into P3 to make the code easier to read. ** -** See also OpenAux and OpenWrite. -*/ -/* Opcode: OpenAux P1 P2 P3 -** -** Open a read-only cursor in the auxiliary table set. This opcode -** works exactly like OP_Open except that it opens the cursor on the -** auxiliary table set (the file used to store tables created using -** CREATE TEMPORARY TABLE) instead of in the main database file. -** See OP_Open for additional information. +** See also OpenWrite. */ /* Opcode: OpenWrite P1 P2 P3 ** ** Open a read/write cursor named P1 on the table or index whose root ** page is P2. If P2==0 then take the root page number from the stack. ** -** This instruction works just like Open except that it opens the cursor +** This instruction works just like OpenRead except that it opens the cursor ** in read/write mode. For a given table, there can be one or more read-only ** cursors or a single read/write cursor but not both. ** -** See also OpWrAux. +** See also OpenRead. */ -/* Opcode: OpenWrAux P1 P2 P3 -** -** Open a read/write cursor in the auxiliary table set. This opcode works -** just like OpenWrite except that the auxiliary table set (the file used -** to store tables created using CREATE TEMPORARY TABLE) is used in place -** of the main database file. -*/ -case OP_OpenAux: -case OP_OpenWrAux: -case OP_OpenWrite: -case OP_Open: { +case OP_OpenRead: +case OP_OpenWrite: { int busy = 0; int i = pOp->p1; int tos = p->tos; int p2 = pOp->p2; int wrFlag; Btree *pX; - switch( pOp->opcode ){ - case OP_Open: wrFlag = 0; pX = pBt; break; - case OP_OpenWrite: wrFlag = 1; pX = pBt; break; - case OP_OpenAux: wrFlag = 0; pX = db->pBeTemp; break; - case OP_OpenWrAux: wrFlag = 1; pX = db->pBeTemp; break; - } + int iDb; + + VERIFY( if( tos<0 ) goto not_enough_stack; ); + Integerify(p, tos); + iDb = p->aStack[tos].i; + tos--; + VERIFY( if( iDb<0 || iDb>=db->nDb ) goto bad_instruction; ); + VERIFY( if( db->aDb[iDb].pBt==0 ) goto bad_instruction; ); + pX = db->aDb[iDb].pBt; + wrFlag = pOp->opcode==OP_OpenWrite; if( p2<=0 ){ - if( tos<0 ) goto not_enough_stack; + VERIFY( if( tos<0 ) goto not_enough_stack; ); Integerify(p, tos); p2 = p->aStack[tos].i; POPSTACK; @@ -3463,15 +3449,16 @@ case OP_Open: { if( p2<=0 ){ POPSTACK; } + POPSTACK; break; } /* Opcode: OpenTemp P1 P2 * ** -** Open a new cursor that points to a table or index in a temporary -** database file. The temporary file is opened read/write even if -** the main database is read-only. The temporary file is deleted -** when the cursor is closed. +** Open a new cursor to a transient table. +** The transient cursor is always opened read/write even if +** the main database is read-only. The transient table is deleted +** automatically when the cursor is closed. ** ** The cursor points to a BTree table if P2==0 and to a BTree index ** if P2==1. A BTree table must have an integer key and can have arbitrary @@ -3479,7 +3466,7 @@ case OP_Open: { ** ** This opcode is used for tables that exist for the duration of a single ** SQL statement only. Tables created using CREATE TEMPORARY TABLE -** are opened using OP_OpenAux or OP_OpenWrAux. "Temporary" in the +** are opened using OP_OpenRead or OP_OpenWrite. "Temporary" in the ** context of this opcode means for the duration of a single SQL statement ** whereas "Temporary" in the context of CREATE TABLE means for the duration ** of the connection to the database. Same word; different meanings. @@ -3493,7 +3480,8 @@ case OP_OpenTemp: { cleanupCursor(pCx); memset(pCx, 0, sizeof(*pCx)); pCx->nullRow = 1; - rc = sqliteBtreeOpen(0, 1, TEMP_PAGES, &pCx->pBt); + rc = sqliteBtreeFactory(db, 0, 1, TEMP_PAGES, &pCx->pBt); + if( rc==SQLITE_OK ){ rc = sqliteBtreeBeginTrans(pCx->pBt); } @@ -3511,24 +3499,26 @@ case OP_OpenTemp: { break; } -/* -** Opcode: RenameCursor P1 P2 * +/* Opcode: OpenPseudo P1 * * ** -** Rename cursor number P1 as cursor number P2. If P2 was previously -** opened is is closed before the renaming occurs. +** Open a new cursor that points to a fake table that contains a single +** row of data. Any attempt to write a second row of data causes the +** first row to be deleted. All data is deleted when the cursor is +** closed. +** +** A pseudo-table created by this opcode is useful for holding the +** NEW or OLD tables in a trigger. */ -case OP_RenameCursor: { - int from = pOp->p1; - int to = pOp->p2; - VERIFY( if( from<0 || to<0 ) goto bad_instruction; ) - if( to<p->nCursor && p->aCsr[to].pCursor ){ - cleanupCursor(&p->aCsr[to]); - } - expandCursorArraySize(p, to); - if( from<p->nCursor ){ - memcpy(&p->aCsr[to], &p->aCsr[from], sizeof(p->aCsr[0])); - memset(&p->aCsr[from], 0, sizeof(p->aCsr[0])); - } +case OP_OpenPseudo: { + int i = pOp->p1; + Cursor *pCx; + VERIFY( if( i<0 ) goto bad_instruction; ) + if( expandCursorArraySize(p, i) ) goto no_mem; + pCx = &p->aCsr[i]; + cleanupCursor(pCx); + memset(pCx, 0, sizeof(*pCx)); + pCx->nullRow = 1; + pCx->pseudoTable = 1; break; } @@ -3539,7 +3529,7 @@ case OP_RenameCursor: { */ case OP_Close: { int i = pOp->p1; - if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor ){ + if( i>=0 && i<p->nCursor ){ cleanupCursor(&p->aCsr[i]); } break; @@ -3573,7 +3563,9 @@ case OP_MoveTo: { Cursor *pC; VERIFY( if( tos<0 ) goto not_enough_stack; ) - if( i>=0 && i<p->nCursor && (pC = &p->aCsr[i])->pCursor!=0 ){ + assert( i>=0 && i<p->nCursor ); + pC = &p->aCsr[i]; + if( pC->pCursor!=0 ){ int res, oc; if( aStack[tos].flags & STK_Int ){ int iKey = intToKey(aStack[tos].i); @@ -3905,7 +3897,8 @@ case OP_NewRecno: { ** be an integer. The stack is popped twice by this instruction. ** ** If P2==1 then the row change count is incremented. If P2==0 the -** row change count is unmodified. +** row change count is unmodified. The rowid is stored for subsequent +** return by the sqlite_last_insert_rowid() function if P2 is 1. */ /* Opcode: PutStrKey P1 * * ** @@ -3914,6 +3907,8 @@ case OP_NewRecno: { ** entry is overwritten. The data is the value on the top of the ** stack. The key is the next value down on the stack. The key must ** be a string. The stack is popped twice by this instruction. +** +** P1 may not be a pseudo-table opened using the OpenPseudo opcode. */ case OP_PutIntKey: case OP_PutStrKey: { @@ -3922,7 +3917,8 @@ case OP_PutStrKey: { int i = pOp->p1; Cursor *pC; VERIFY( if( nos<0 ) goto not_enough_stack; ) - if( VERIFY( i>=0 && i<p->nCursor && ) (pC = &p->aCsr[i])->pCursor!=0 ){ + if( VERIFY( i>=0 && i<p->nCursor && ) + ((pC = &p->aCsr[i])->pCursor!=0 || pC->pseudoTable) ){ char *zKey; int nKey, iKey; if( pOp->opcode==OP_PutStrKey ){ @@ -3934,14 +3930,38 @@ case OP_PutStrKey: { nKey = sizeof(int); iKey = intToKey(aStack[nos].i); zKey = (char*)&iKey; - db->lastRowid = aStack[nos].i; - if( pOp->p2 ) db->nChange++; + if( pOp->p2 ){ + db->nChange++; + db->lastRowid = aStack[nos].i; + } if( pC->nextRowidValid && aStack[nos].i>=pC->nextRowid ){ pC->nextRowidValid = 0; } } - rc = sqliteBtreeInsert(pC->pCursor, zKey, nKey, - zStack[tos], aStack[tos].n); + if( pC->pseudoTable ){ + /* PutStrKey does not work for pseudo-tables. + ** The following assert makes sure we are not trying to use + ** PutStrKey on a pseudo-table + */ + assert( pOp->opcode==OP_PutIntKey ); + sqliteFree(pC->pData); + pC->iKey = iKey; + pC->nData = aStack[tos].n; + if( aStack[tos].flags & STK_Dyn ){ + pC->pData = zStack[tos]; + zStack[tos] = 0; + aStack[tos].flags = STK_Null; + }else{ + pC->pData = sqliteMallocRaw( pC->nData ); + if( pC->pData ){ + memcpy(pC->pData, zStack[tos], pC->nData); + } + } + pC->nullRow = 0; + }else{ + rc = sqliteBtreeInsert(pC->pCursor, zKey, nKey, + zStack[tos], aStack[tos].n); + } pC->recnoIsValid = 0; } POPSTACK; @@ -3960,11 +3980,15 @@ case OP_PutStrKey: { ** ** The row change counter is incremented if P2==1 and is unmodified ** if P2==0. +** +** If P1 is a pseudo-table, then this instruction is a no-op. */ case OP_Delete: { int i = pOp->p1; Cursor *pC; - if( VERIFY( i>=0 && i<p->nCursor && ) (pC = &p->aCsr[i])->pCursor!=0 ){ + assert( i>=0 && i<p->nCursor ); + pC = &p->aCsr[i]; + if( pC->pCursor!=0 ){ rc = sqliteBtreeDelete(pC->pCursor); pC->nextRowidValid = 0; } @@ -3975,14 +3999,67 @@ case OP_Delete: { /* Opcode: KeyAsData P1 P2 * ** ** Turn the key-as-data mode for cursor P1 either on (if P2==1) or -** off (if P2==0). In key-as-data mode, the Field opcode pulls -** data off of the key rather than the data. This is useful for +** off (if P2==0). In key-as-data mode, the OP_Column opcode pulls +** data off of the key rather than the data. This is used for ** processing compound selects. */ case OP_KeyAsData: { int i = pOp->p1; - if( VERIFY( i>=0 && i<p->nCursor && ) p->aCsr[i].pCursor!=0 ){ - p->aCsr[i].keyAsData = pOp->p2; + assert( i>=0 && i<p->nCursor ); + p->aCsr[i].keyAsData = pOp->p2; + break; +} + +/* Opcode: RowData P1 * * +** +** Push onto the stack the complete row data for cursor P1. +** There is no interpretation of the data. It is just copied +** onto the stack exactly as it is found in the database file. +** +** If the cursor is not pointing to a valid row, a NULL is pushed +** onto the stack. +*/ +case OP_RowData: { + int i = pOp->p1; + int tos = ++p->tos; + Cursor *pC; + int n; + + assert( i>=0 && i<p->nCursor ); + pC = &p->aCsr[i]; + if( pC->nullRow ){ + aStack[tos].flags = STK_Null; + }else if( pC->pCursor!=0 ){ + BtCursor *pCrsr = pC->pCursor; + if( pC->nullRow ){ + aStack[tos].flags = STK_Null; + break; + }else if( pC->keyAsData ){ + sqliteBtreeKeySize(pCrsr, &n); + }else{ + sqliteBtreeDataSize(pCrsr, &n); + } + aStack[tos].n = n; + if( n<=NBFS ){ + aStack[tos].flags = STK_Str; + zStack[tos] = aStack[tos].z; + }else{ + char *z = sqliteMallocRaw( n ); + if( z==0 ) goto no_mem; + aStack[tos].flags = STK_Str | STK_Dyn; + zStack[tos] = z; + } + if( pC->keyAsData ){ + sqliteBtreeKey(pCrsr, 0, n, zStack[tos]); + }else{ + sqliteBtreeData(pCrsr, 0, n, zStack[tos]); + } + }else if( pC->pseudoTable ){ + aStack[tos].n = pC->nData; + zStack[tos] = pC->pData; + aStack[tos].flags = STK_Str|STK_Ephem; + }else{ + aStack[tos].flags = STK_Null; } break; } @@ -4017,12 +4094,13 @@ case OP_Column: { int idxWidth; unsigned char aHdr[10]; + assert( i<p->nCursor ); if( i<0 ){ VERIFY( if( tos+i<0 ) goto bad_instruction; ) VERIFY( if( (aStack[tos+i].flags & STK_Str)==0 ) goto bad_instruction; ) zRec = zStack[tos+i]; payloadSize = aStack[tos+i].n; - }else if( VERIFY( i>=0 && i<p->nCursor && ) (pC = &p->aCsr[i])->pCursor!=0 ){ + }else if( (pC = &p->aCsr[i])->pCursor!=0 ){ zRec = 0; pCrsr = pC->pCursor; if( pC->nullRow ){ @@ -4032,6 +4110,10 @@ case OP_Column: { }else{ sqliteBtreeDataSize(pCrsr, &payloadSize); } + }else if( pC->pseudoTable ){ + payloadSize = pC->nData; + zRec = pC->pData; + assert( payloadSize==0 || zRec!=0 ); }else{ payloadSize = 0; } @@ -4121,22 +4203,24 @@ case OP_Column: { case OP_Recno: { int i = pOp->p1; int tos = ++p->tos; - BtCursor *pCrsr; + Cursor *pC; + int v; - if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ - int v; - if( p->aCsr[i].recnoIsValid ){ - v = p->aCsr[i].lastRecno; - }else if( p->aCsr[i].nullRow ){ - aStack[tos].flags = STK_Null; - break; - }else{ - sqliteBtreeKey(pCrsr, 0, sizeof(u32), (char*)&v); - v = keyToInt(v); - } - aStack[tos].i = v; - aStack[tos].flags = STK_Int; + assert( i>=0 && i<p->nCursor ); + if( (pC = &p->aCsr[i])->recnoIsValid ){ + v = pC->lastRecno; + }else if( pC->nullRow ){ + aStack[tos].flags = STK_Null; + break; + }else if( pC->pseudoTable ){ + v = keyToInt(pC->iKey); + }else{ + assert( pC->pCursor!=0 ); + sqliteBtreeKey(pC->pCursor, 0, sizeof(u32), (char*)&v); + v = keyToInt(v); } + aStack[tos].i = v; + aStack[tos].flags = STK_Int; break; } @@ -4148,6 +4232,8 @@ case OP_Recno: { ** Compare this opcode to Recno. The Recno opcode extracts the first ** 4 bytes of the key and pushes those bytes onto the stack as an ** integer. This instruction pushes the entire key as a string. +** +** This opcode may not be used on a pseudo-table. */ case OP_FullKey: { int i = pOp->p1; @@ -4155,6 +4241,7 @@ case OP_FullKey: { BtCursor *pCrsr; VERIFY( if( !p->aCsr[i].keyAsData ) goto bad_instruction; ) + VERIFY( if( p->aCsr[i].pseudoTable ) goto bad_instruction; ) if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ int amt; char *z; @@ -4187,11 +4274,10 @@ case OP_FullKey: { */ case OP_NullRow: { int i = pOp->p1; - BtCursor *pCrsr; - if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ - p->aCsr[i].nullRow = 1; - } + assert( i>=0 && i<p->nCursor ); + p->aCsr[i].nullRow = 1; + p->aCsr[i].recnoIsValid = 0; break; } @@ -4205,15 +4291,20 @@ case OP_NullRow: { */ case OP_Last: { int i = pOp->p1; + Cursor *pC; BtCursor *pCrsr; - if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ + assert( i>=0 && i<p->nCursor ); + pC = &p->aCsr[i]; + if( (pCrsr = pC->pCursor)!=0 ){ int res; - sqliteBtreeLast(pCrsr, &res); + rc = sqliteBtreeLast(pCrsr, &res); p->aCsr[i].nullRow = res; if( res && pOp->p2>0 ){ pc = pOp->p2 - 1; } + }else{ + pC->nullRow = 0; } break; } @@ -4228,16 +4319,21 @@ case OP_Last: { */ case OP_Rewind: { int i = pOp->p1; + Cursor *pC; BtCursor *pCrsr; - if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ + assert( i>=0 && i<p->nCursor ); + pC = &p->aCsr[i]; + if( (pCrsr = pC->pCursor)!=0 ){ int res; - sqliteBtreeFirst(pCrsr, &res); - p->aCsr[i].atFirst = res==0; - p->aCsr[i].nullRow = res; + rc = sqliteBtreeFirst(pCrsr, &res); + pC->atFirst = res==0; + pC->nullRow = res; if( res && pOp->p2>0 ){ pc = pOp->p2 - 1; } + }else{ + pC->nullRow = 0; } break; } @@ -4264,8 +4360,9 @@ case OP_Next: { BtCursor *pCrsr; CHECK_FOR_INTERRUPT; - if( VERIFY( pOp->p1>=0 && pOp->p1<p->nCursor && ) - (pCrsr = (pC = &p->aCsr[pOp->p1])->pCursor)!=0 ){ + assert( pOp->p1>=0 && pOp->p1<p->nCursor ); + pC = &p->aCsr[pOp->p1]; + if( (pCrsr = pC->pCursor)!=0 ){ int res; if( pC->nullRow ){ res = 1; @@ -4278,8 +4375,10 @@ case OP_Next: { pc = pOp->p2 - 1; sqlite_search_count++; } - pC->recnoIsValid = 0; + }else{ + pC->nullRow = 1; } + pC->recnoIsValid = 0; break; } @@ -4373,10 +4472,14 @@ case OP_IdxRecno: { int v; int sz; sqliteBtreeKeySize(pCrsr, &sz); - sqliteBtreeKey(pCrsr, sz - sizeof(u32), sizeof(u32), (char*)&v); - v = keyToInt(v); - aStack[tos].i = v; - aStack[tos].flags = STK_Int; + if( sz<sizeof(u32) ){ + aStack[tos].flags = STK_Null; + }else{ + sqliteBtreeKey(pCrsr, sz - sizeof(u32), sizeof(u32), (char*)&v); + v = keyToInt(v); + aStack[tos].i = v; + aStack[tos].flags = STK_Int; + } } break; } @@ -4446,7 +4549,7 @@ case OP_IdxGE: { ** See also: Clear */ case OP_Destroy: { - sqliteBtreeDropTable(pOp->p2 ? db->pBeTemp : pBt, pOp->p1); + rc = sqliteBtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1); break; } @@ -4463,7 +4566,7 @@ case OP_Destroy: { ** See also: Destroy */ case OP_Clear: { - sqliteBtreeClearTable(pOp->p2 ? db->pBeTemp : pBt, pOp->p1); + rc = sqliteBtreeClearTable(db->aDb[pOp->p2].pBt, pOp->p1); break; } @@ -4497,10 +4600,12 @@ case OP_CreateTable: { int i = ++p->tos; int pgno; assert( pOp->p3!=0 && pOp->p3type==P3_POINTER ); + assert( pOp->p2>=0 && pOp->p2<db->nDb ); + assert( db->aDb[pOp->p2].pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ - rc = sqliteBtreeCreateTable(pOp->p2 ? db->pBeTemp : pBt, &pgno); + rc = sqliteBtreeCreateTable(db->aDb[pOp->p2].pBt, &pgno); }else{ - rc = sqliteBtreeCreateIndex(pOp->p2 ? db->pBeTemp : pBt, &pgno); + rc = sqliteBtreeCreateIndex(db->aDb[pOp->p2].pBt, &pgno); } if( rc==SQLITE_OK ){ aStack[i].i = pgno; @@ -4544,7 +4649,7 @@ case OP_IntegrityCk: { toInt((char*)sqliteHashKey(i), &aRoot[j]); } aRoot[j] = 0; - z = sqliteBtreeIntegrityCheck(pOp->p2 ? db->pBeTemp : pBt, aRoot, nRoot); + z = sqliteBtreeIntegrityCheck(db->aDb[pOp->p2].pBt, aRoot, nRoot); if( z==0 || z[0]==0 ){ if( z ) sqliteFree(z); zStack[tos] = "ok"; @@ -5669,8 +5774,7 @@ bad_instruction: */ int sqliteVdbeFinalize(Vdbe *p, char **pzErrMsg){ sqlite *db = p->db; - Btree *pBt = p->pBt; - int rc; + int i, rc; if( p->magic!=VDBE_MAGIC_RUN && p->magic!=VDBE_MAGIC_HALT ){ sqliteSetString(pzErrMsg, sqlite_error_string(SQLITE_MISUSE), 0); @@ -5689,23 +5793,24 @@ int sqliteVdbeFinalize(Vdbe *p, char **pzErrMsg){ switch( p->errorAction ){ case OE_Abort: { if( !p->undoTransOnError ){ - sqliteBtreeRollbackCkpt(pBt); - if( db->pBeTemp ) sqliteBtreeRollbackCkpt(db->pBeTemp); + for(i=0; i<db->nDb; i++){ + if( db->aDb[i].pBt ){ + sqliteBtreeRollbackCkpt(db->aDb[i].pBt); + } + } break; } /* Fall through to ROLLBACK */ } case OE_Rollback: { - sqliteBtreeRollback(pBt); - if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp); + sqliteRollbackAll(db); db->flags &= ~SQLITE_InTrans; db->onError = OE_Default; break; } default: { if( p->undoTransOnError ){ - sqliteBtreeCommit(pBt); - if( db->pBeTemp ) sqliteBtreeCommit(db->pBeTemp); + sqliteRollbackAll(db); db->flags &= ~SQLITE_InTrans; db->onError = OE_Default; } @@ -5714,8 +5819,11 @@ int sqliteVdbeFinalize(Vdbe *p, char **pzErrMsg){ } sqliteRollbackInternalChanges(db); } - sqliteBtreeCommitCkpt(pBt); - if( db->pBeTemp ) sqliteBtreeCommitCkpt(db->pBeTemp); + for(i=0; i<db->nDb; i++){ + if( db->aDb[i].pBt ){ + sqliteBtreeCommitCkpt(db->aDb[i].pBt); + } + } assert( p->tos<p->pc || sqlite_malloc_failed==1 ); #ifdef VDBE_PROFILE { |