aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/auth.c2
-rw-r--r--src/btree.c8
-rw-r--r--src/select.c13
-rw-r--r--src/sqliteInt.h11
-rw-r--r--src/vdbe.c156
-rw-r--r--src/vdbeInt.h16
-rw-r--r--src/vdbeaux.c3
7 files changed, 88 insertions, 121 deletions
diff --git a/src/auth.c b/src/auth.c
index 4e5bd164f..ec3514257 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -128,7 +128,7 @@ void sqlite3AuthRead(
pTab = pParse->pTriggerTab;
}else{
assert( pTabList );
- for(iSrc=0; iSrc<pTabList->nSrc; iSrc++){
+ for(iSrc=0; ALWAYS(iSrc<pTabList->nSrc); iSrc++){
if( pExpr->iTable==pTabList->a[iSrc].iCursor ){
pTab = pTabList->a[iSrc].pTab;
break;
diff --git a/src/btree.c b/src/btree.c
index 903b782bc..dde289752 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -6420,8 +6420,10 @@ static int balance(BtCursor *pCur){
** a positive value if pCur points at an etry that is larger than
** (pKey, nKey)).
**
-** If the seekResult parameter is 0, then cursor pCur may point to any
-** entry or to no entry at all. In this case this function has to seek
+** If the seekResult parameter is non-zero, then the caller guarantees that
+** cursor pCur is pointing at the existing copy of a row that is to be
+** overwritten. If the seekResult parameter is 0, then cursor pCur may
+** point to any entry or to no entry at all and so this function has to seek
** the cursor before the new key can be inserted.
*/
int sqlite3BtreeInsert(
@@ -6433,7 +6435,7 @@ int sqlite3BtreeInsert(
int seekResult /* Result of prior MovetoUnpacked() call */
){
int rc;
- int loc = seekResult;
+ int loc = seekResult; /* -1: before desired location +1: after */
int szNew;
int idx;
MemPage *pPage;
diff --git a/src/select.c b/src/select.c
index ecdc63187..21bc2be23 100644
--- a/src/select.c
+++ b/src/select.c
@@ -766,14 +766,16 @@ static void generateSortTail(
int regRowid;
iTab = pOrderBy->iECursor;
+ regRow = sqlite3GetTempReg(pParse);
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
pseudoTab = pParse->nTab++;
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Output, nColumn);
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, regRow, nColumn);
+ regRowid = 0;
+ }else{
+ regRowid = sqlite3GetTempReg(pParse);
}
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
codeOffset(v, p, addrContinue);
- regRow = sqlite3GetTempReg(pParse);
- regRowid = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr + 1, regRow);
switch( eDest ){
case SRT_Table:
@@ -805,11 +807,12 @@ static void generateSortTail(
assert( eDest==SRT_Output || eDest==SRT_Coroutine );
testcase( eDest==SRT_Output );
testcase( eDest==SRT_Coroutine );
- sqlite3VdbeAddOp2(v, OP_Integer, 1, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, pseudoTab, regRow, regRowid);
for(i=0; i<nColumn; i++){
assert( regRow!=pDest->iMem+i );
sqlite3VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iMem+i);
+ if( i==0 ){
+ sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
+ }
}
if( eDest==SRT_Output ){
sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iMem, nColumn);
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 7c610060b..6cfe702e3 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -2161,11 +2161,12 @@ struct AuthContext {
/*
** Bitfield flags for P5 value in OP_Insert and OP_Delete
*/
-#define OPFLAG_NCHANGE 1 /* Set to update db->nChange */
-#define OPFLAG_LASTROWID 2 /* Set to update db->lastRowid */
-#define OPFLAG_ISUPDATE 4 /* This OP_Insert is an sql UPDATE */
-#define OPFLAG_APPEND 8 /* This is likely to be an append */
-#define OPFLAG_USESEEKRESULT 16 /* Try to avoid a seek in BtreeInsert() */
+#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */
+#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */
+#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */
+#define OPFLAG_APPEND 0x08 /* This is likely to be an append */
+#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */
+#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */
/*
* Each trigger present in the database schema is stored as an instance of
diff --git a/src/vdbe.c b/src/vdbe.c
index d3fd3050e..b16880270 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -189,7 +189,7 @@ static VdbeCursor *allocateCursor(
int iCur, /* Index of the new VdbeCursor */
int nField, /* Number of fields in the table or index */
int iDb, /* When database the cursor belongs to, or -1 */
- int isBtreeCursor /* True for B-Tree vs. pseudo-table or vtab */
+ int isBtreeCursor /* True for B-Tree. False for pseudo-table or vtab */
){
/* Find the memory cell that will be used to store the blob of memory
** required for this VdbeCursor structure. It is convenient to use a
@@ -1975,29 +1975,7 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
break;
}
-/* Opcode: SetNumColumns * P2 * * *
-**
-** This opcode sets the number of columns for the cursor opened by the
-** following instruction to P2.
-**
-** An OP_SetNumColumns is only useful if it occurs immediately before
-** one of the following opcodes:
-**
-** OpenRead
-** OpenWrite
-** OpenPseudo
-**
-** If the OP_Column opcode is to be executed on a cursor, then
-** this opcode must be present immediately before the opcode that
-** opens the cursor.
-*/
-#if 0
-case OP_SetNumColumns: {
- break;
-}
-#endif
-
-/* Opcode: Column P1 P2 P3 P4 *
+/* Opcode: Column P1 P2 P3 P4 P5
**
** Interpret the data that cursor P1 points to as a structure built using
** the MakeRecord instruction. (See the MakeRecord opcode for additional
@@ -2010,6 +1988,11 @@ case OP_SetNumColumns: {
** If the column contains fewer than P2 fields, then extract a NULL. Or,
** if the P4 argument is a P4_MEM use the value of the P4 argument as
** the result.
+**
+** If the OPFLAG_CLEARCACHE bit is set on P5 and P1 is a pseudo-table cursor,
+** then the cache of the cursor is reset prior to extracting the column.
+** The first OP_Column against a pseudo-table after the value of the content
+** register has changed should have this bit set.
*/
case OP_Column: {
u32 payloadSize; /* Number of bytes in the record */
@@ -2033,6 +2016,7 @@ case OP_Column: {
u64 offset64; /* 64-bit offset. 64 bits needed to catch overflow */
int szHdr; /* Size of the header size field at start of record */
int avail; /* Number of bytes of available data */
+ Mem *pReg; /* PseudoTable input register */
p1 = pOp->p1;
@@ -2086,11 +2070,12 @@ case OP_Column: {
rc = sqlite3BtreeDataSize(pCrsr, &payloadSize);
assert( rc==SQLITE_OK ); /* DataSize() cannot fail */
}
- }else if( pC->pseudoTable ){
- /* The record is the sole entry of a pseudo-table */
- payloadSize = pC->nData;
- zRec = pC->pData;
- pC->cacheStatus = CACHE_STALE;
+ }else if( pC->pseudoTableReg>0 ){
+ pReg = &p->aMem[pC->pseudoTableReg];
+ assert( pReg->flags & MEM_Blob );
+ payloadSize = pReg->n;
+ zRec = pReg->z;
+ pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr;
assert( payloadSize==0 || zRec!=0 );
}else{
/* Consider the row to be NULL */
@@ -3067,22 +3052,14 @@ case OP_OpenEphemeral: {
/* Opcode: OpenPseudo P1 P2 P3 * *
**
** 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.
+** row of data. The content of that one row in the content of memory
+** register P2. In other words, cursor P1 becomes an alias for the
+** MEM_Blob content contained in register P2.
**
-** A pseudo-table created by this opcode is useful for holding the
-** NEW or OLD tables in a trigger. Also used to hold the a single
+** A pseudo-table created by this opcode is used to hold the a single
** row output from the sorter so that the row can be decomposed into
-** individual columns using the OP_Column opcode.
-**
-** When OP_Insert is executed to insert a row in to the pseudo table,
-** the pseudo-table cursor may or may not make it's own copy of the
-** original row data. If P2 is 0, then the pseudo-table will copy the
-** original row data. Otherwise, a pointer to the original memory cell
-** is stored. In this case, the vdbe program must ensure that the
-** memory cell containing the row data is not overwritten until the
-** pseudo table is closed (or a new row is inserted into it).
+** individual columns using the OP_Column opcode. The OP_Column opcode
+** is the only cursor opcode that works with a pseudo-table.
**
** P3 is the number of fields in the records that will be stored by
** the pseudo-table.
@@ -3094,8 +3071,7 @@ case OP_OpenPseudo: {
pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0);
if( pCx==0 ) goto no_mem;
pCx->nullRow = 1;
- pCx->pseudoTable = 1;
- pCx->ephemPseudoTable = (u8)pOp->p2;
+ pCx->pseudoTableReg = pOp->p2;
pCx->isTable = 1;
pCx->isIndex = 0;
break;
@@ -3180,6 +3156,7 @@ case OP_SeekGt: { /* jump, in3 */
assert( pOp->p2!=0 );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
+ assert( pC->pseudoTableReg==0 );
if( pC->pCursor!=0 ){
oc = pOp->opcode;
pC->nullRow = 0;
@@ -3293,7 +3270,6 @@ case OP_SeekGt: { /* jump, in3 */
** for read access returns SQLITE_EMPTY. In this case always
** take the jump (since there are no records in the table).
*/
- assert( pC->pseudoTable==0 );
pc = pOp->p2 - 1;
}
break;
@@ -3504,6 +3480,7 @@ case OP_NotExists: { /* jump, in3 */
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->isTable );
+ assert( pC->pseudoTableReg==0 );
pCrsr = pC->pCursor;
if( pCrsr!=0 ){
res = 0;
@@ -3523,8 +3500,6 @@ case OP_NotExists: { /* jump, in3 */
/* This happens when an attempt to open a read cursor on the
** sqlite_master table returns SQLITE_EMPTY.
*/
- assert( !pC->pseudoTable );
- assert( pC->isTable );
pc = pOp->p2 - 1;
assert( pC->rowidIsValid==0 );
pC->seekResult = 0;
@@ -3685,15 +3660,28 @@ case OP_NewRowid: { /* out2-prerelease */
**
** Write an entry into the table of cursor P1. A new entry is
** created if it doesn't already exist or the data for an existing
-** entry is overwritten. The data is the value stored register
+** entry is overwritten. The data is the value MEM_Blob stored in register
** number P2. The key is stored in register P3. The key must
-** be an integer.
+** be a MEM_Int.
**
** If the OPFLAG_NCHANGE flag of P5 is set, then the row change count is
** incremented (otherwise not). If the OPFLAG_LASTROWID flag of P5 is set,
** then rowid is stored for subsequent return by the
** sqlite3_last_insert_rowid() function (otherwise it is unmodified).
**
+** If the OPFLAG_USESEEKRESULT flag of P5 is set and if the result of
+** the last seek operation (OP_NotExists) was a success, then this
+** operation will not attempt to find the appropriate row before doing
+** the insert but will instead overwrite the row that the cursor is
+** currently pointing to. Presumably, the prior OP_NotExists opcode
+** has already positioned the cursor correctly. This is an optimization
+** that boosts performance by avoiding redundant seeks.
+**
+** If the OPFLAG_ISUPDATE flag is set, then this opcode is part of an
+** UPDATE operation. Otherwise (if the flag is clear) then this opcode
+** is part of an INSERT operation. The difference is only important to
+** the update hook.
+**
** Parameter P4 may point to a string containing the table-name, or
** may be NULL. If it is not NULL, then the update-hook
** (sqlite3.xUpdateCallback) is invoked following a successful insert.
@@ -3708,22 +3696,23 @@ case OP_NewRowid: { /* out2-prerelease */
** for indices is OP_IdxInsert.
*/
case OP_Insert: {
- Mem *pData;
- Mem *pKey;
- i64 iKey; /* The integer ROWID or key for the record to be inserted */
- VdbeCursor *pC;
- int nZero;
- int seekResult;
- const char *zDb;
- const char *zTbl;
- int op;
+ Mem *pData; /* MEM cell holding data for the record to be inserted */
+ Mem *pKey; /* MEM cell holding key for the record */
+ i64 iKey; /* The integer ROWID or key for the record to be inserted */
+ VdbeCursor *pC; /* Cursor to table into which insert is written */
+ int nZero; /* Number of zero-bytes to append */
+ int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */
+ const char *zDb; /* database name - used by the update hook */
+ const char *zTbl; /* Table name - used by the opdate hook */
+ int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */
pData = &p->aMem[pOp->p2];
pKey = &p->aMem[pOp->p3];
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- assert( pC->pCursor!=0 || pC->pseudoTable );
+ assert( pC->pCursor!=0 );
+ assert( pC->pseudoTableReg==0 );
assert( pKey->flags & MEM_Int );
assert( pC->isTable );
REGISTER_TRACE(pOp->p2, pData);
@@ -3738,41 +3727,17 @@ case OP_Insert: {
}else{
assert( pData->flags & (MEM_Blob|MEM_Str) );
}
- if( pC->pseudoTable ){
- if( !pC->ephemPseudoTable ){
- sqlite3DbFree(db, pC->pData);
- }
- pC->iKey = iKey;
- pC->nData = pData->n;
- if( pC->ephemPseudoTable || pData->z==pData->zMalloc ){
- pC->pData = pData->z;
- if( !pC->ephemPseudoTable ){
- pData->flags &= ~MEM_Dyn;
- pData->flags |= MEM_Ephem;
- pData->zMalloc = 0;
- }
- }else{
- pC->pData = sqlite3Malloc( pC->nData+2 );
- if( !pC->pData ) goto no_mem;
- memcpy(pC->pData, pData->z, pC->nData);
- pC->pData[pC->nData] = 0;
- pC->pData[pC->nData+1] = 0;
- }
- pC->nullRow = 0;
+ seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0);
+ if( pData->flags & MEM_Zero ){
+ nZero = pData->u.nZero;
}else{
- seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0);
- if( pData->flags & MEM_Zero ){
- nZero = pData->u.nZero;
- }else{
- nZero = 0;
- }
- sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
- rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey,
- pData->z, pData->n, nZero,
- pOp->p5 & OPFLAG_APPEND, seekResult
- );
+ nZero = 0;
}
-
+ sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
+ rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey,
+ pData->z, pData->n, nZero,
+ pOp->p5 & OPFLAG_APPEND, seekResult
+ );
pC->rowidIsValid = 0;
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
@@ -3905,7 +3870,7 @@ case OP_RowData: {
assert( pC->isIndex || pOp->opcode==OP_RowData );
assert( pC!=0 );
assert( pC->nullRow==0 );
- assert( pC->pseudoTable==0 );
+ assert( pC->pseudoTableReg==0 );
assert( pC->pCursor!=0 );
pCrsr = pC->pCursor;
assert( sqlite3BtreeCursorIsValid(pCrsr) );
@@ -3967,13 +3932,12 @@ case OP_Rowid: { /* out2-prerelease */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
+ assert( pC->pseudoTableReg==0 );
if( pC->nullRow ){
/* Do nothing so that reg[P2] remains NULL */
break;
}else if( pC->deferredMoveto ){
v = pC->movetoTarget;
- }else if( pC->pseudoTable ){
- v = pC->iKey;
#ifndef SQLITE_OMIT_VIRTUALTABLE
}else if( pC->pVtabCursor ){
pVtab = pC->pVtabCursor->pVtab;
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 40aab1a39..ecf8dcd54 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -56,16 +56,12 @@ struct VdbeCursor {
Bool atFirst; /* True if pointing to first entry */
Bool useRandomRowid; /* Generate new record numbers semi-randomly */
Bool nullRow; /* True if pointing to a row with no data */
- Bool pseudoTable; /* This is a NEW or OLD pseudo-tables of a trigger */
- Bool ephemPseudoTable;
Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
Bool isTable; /* True if a table requiring integer keys */
Bool isIndex; /* True if an index containing keys only - no data */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
Btree *pBt; /* Separate file holding temporary table */
- int nData; /* Number of bytes in pData */
- char *pData; /* Data for a NEW or OLD pseudo-table */
- i64 iKey; /* Key for the NEW or OLD pseudo-table row */
+ int pseudoTableReg; /* Register holding pseudotable content. */
KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
int nField; /* Number of fields in the header */
i64 seqCount; /* Sequence counter */
@@ -77,11 +73,15 @@ struct VdbeCursor {
int seekResult;
/* Cached information about the header for the data record that the
- ** cursor is currently pointing to. Only valid if cacheValid is true.
+ ** cursor is currently pointing to. Only valid if cacheStatus matches
+ ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of
+ ** CACHE_STALE and so setting cacheStatus=CACHE_STALE guarantees that
+ ** the cache is out of date.
+ **
** aRow might point to (ephemeral) data for the current row, or it might
** be NULL.
*/
- int cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */
+ u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */
int payloadSize; /* Total number of bytes in the record */
u32 *aType; /* Type values for all entries in the record */
u32 *aOffset; /* Cached offsets to the start of each columns data */
@@ -296,7 +296,7 @@ struct Vdbe {
u32 magic; /* Magic number for sanity checking */
int nMem; /* Number of memory locations currently allocated */
Mem *aMem; /* The memory locations */
- int cacheCtr; /* VdbeCursor row cache generation counter */
+ u32 cacheCtr; /* VdbeCursor row cache generation counter */
int pc; /* The program counter */
int rc; /* Value to return */
char *zErrMsg; /* Error message written here */
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index a3975647d..35ae1e355 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -1365,9 +1365,6 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
p->inVtabMethod = 0;
}
#endif
- if( !pCx->ephemPseudoTable ){
- sqlite3DbFree(p->db, pCx->pData);
- }
}
/*