Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -4179,10 +4179,18 @@ ** of run-time by skipping the initialization of those elements. */ void sqlite3BtreeCursorZero(BtCursor *p){ memset(p, 0, offsetof(BtCursor, iPage)); } + +/* +** Return the Btree object associated with a BtCursor. +*/ +Btree *sqlite3BtreeOfCursor(BtCursor *p){ + assert( p!=0 ); + return p->pBtree; +} /* ** Close a cursor. The read lock on the database file is released ** when the last cursor is closed. */ Index: src/btree.h ================================================================== --- src/btree.h +++ src/btree.h @@ -219,10 +219,11 @@ BtCursor *pCursor /* Space to write cursor structure */ ); int sqlite3BtreeCursorSize(void); void sqlite3BtreeCursorZero(BtCursor*); void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned); +Btree *sqlite3BtreeOfCursor(BtCursor*); #ifdef SQLITE_ENABLE_CURSOR_HINTS void sqlite3BtreeCursorHint(BtCursor*, int, ...); #endif int sqlite3BtreeCloseCursor(BtCursor*); Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -3439,10 +3439,11 @@ */ case OP_OpenAutoindex: case OP_OpenEphemeral: { VdbeCursor *pCx; KeyInfo *pKeyInfo; + Btree *pBt; static const int vfsFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | @@ -3451,39 +3452,43 @@ assert( pOp->p1>=0 ); assert( pOp->p2>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; - pCx->isEphemeral = 1; - rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBt, + rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pBt, BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeBeginTrans(pCx->pBt, 1); - } - if( rc==SQLITE_OK ){ - /* If a transient index is required, create it by calling - ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before - ** opening it. If a transient table is required, just use the - ** automatically created table with root-page 1 (an BLOB_INTKEY table). - */ - if( (pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ - int pgno; - assert( pOp->p4type==P4_KEYINFO ); - rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5); - if( rc==SQLITE_OK ){ - assert( pgno==MASTER_ROOT+1 ); - assert( pKeyInfo->db==db ); - assert( pKeyInfo->enc==ENC(db) ); - pCx->pKeyInfo = pKeyInfo; - rc = sqlite3BtreeCursor(pCx->pBt, pgno, BTREE_WRCSR, - pKeyInfo, pCx->uc.pCursor); - } - pCx->isTable = 0; - }else{ - rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, BTREE_WRCSR, - 0, pCx->uc.pCursor); - pCx->isTable = 1; + rc = sqlite3BtreeBeginTrans(pBt, 1); + if( rc==SQLITE_OK ){ + /* If a transient index is required, create it by calling + ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before + ** opening it. If a transient table is required, just use the + ** automatically created table with root-page 1 (an BLOB_INTKEY table). + */ + if( (pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ + int pgno; + assert( pOp->p4type==P4_KEYINFO ); + rc = sqlite3BtreeCreateTable(pBt, &pgno, BTREE_BLOBKEY | pOp->p5); + if( rc==SQLITE_OK ){ + assert( pgno==MASTER_ROOT+1 ); + assert( pKeyInfo->db==db ); + assert( pKeyInfo->enc==ENC(db) ); + pCx->pKeyInfo = pKeyInfo; + rc = sqlite3BtreeCursor(pBt, pgno, BTREE_WRCSR, + pKeyInfo, pCx->uc.pCursor); + } + pCx->isTable = 0; + }else{ + rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, BTREE_WRCSR, + 0, pCx->uc.pCursor); + pCx->isTable = 1; + } + } + if( rc==SQLITE_OK ){ + pCx->isEphemeral = 1; + }else{ + sqlite3BtreeClose(pBt); } } pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); break; } @@ -3522,11 +3527,12 @@ case OP_SequenceTest: { VdbeCursor *pC; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( isSorter(pC) ); - if( (pC->seqCount++)==0 ){ + assert( !pC->movetoUsed ); + if( (pC->ux.seqCount++)==0 ){ goto jump_to_p2; } break; } @@ -3752,11 +3758,12 @@ assert( (OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001) ); if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++; } } rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)iKey, 0, &res); - pC->movetoTarget = iKey; /* Used by OP_Delete */ + VVA_ONLY( pC->movetoUsed = 1; ) + pC->ux.movetoTarget = iKey; /* Used by OP_Delete */ if( rc!=SQLITE_OK ){ goto abort_due_to_error; } }else{ /* For a cursor with the BTREE_SEEK_EQ hint, only the OP_SeekGE and @@ -3860,15 +3867,17 @@ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->eCurType==CURTYPE_BTREE ); + assert( !pC->isEphemeral ); assert( pC->uc.pCursor!=0 ); assert( pC->isTable ); pC->nullRow = 0; pIn2 = &aMem[pOp->p2]; - pC->movetoTarget = sqlite3VdbeIntValue(pIn2); + VVA_ONLY( pC->movetoUsed = 1; ) + pC->ux.movetoTarget = sqlite3VdbeIntValue(pIn2); pC->deferredMoveto = 1; break; } @@ -4053,11 +4062,12 @@ assert( pCrsr!=0 ); res = 0; iKey = pIn3->u.i; rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res); assert( rc==SQLITE_OK || res==0 ); - pC->movetoTarget = iKey; /* Used by OP_Delete */ + VVA_ONLY( pC->movetoUsed = 1; ) + pC->ux.movetoTarget = iKey; /* Used by OP_Delete */ pC->nullRow = 0; pC->cacheStatus = CACHE_STALE; pC->deferredMoveto = 0; VdbeBranchTaken(res!=0,2); pC->seekResult = res; @@ -4081,13 +4091,15 @@ ** instruction. */ case OP_Sequence: { /* out2 */ assert( pOp->p1>=0 && pOp->p1nCursor ); assert( p->apCsr[pOp->p1]!=0 ); - assert( p->apCsr[pOp->p1]->eCurType!=CURTYPE_VTAB ); + assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE ); + assert( p->apCsr[pOp->p1]->isEphemeral ); + assert( !p->apCsr[pOp->p1]->movetoUsed ); pOut = out2Prerelease(p, pOp); - pOut->u.i = p->apCsr[pOp->p1]->seqCount++; + pOut->u.i = p->apCsr[pOp->p1]->ux.seqCount++; break; } /* Opcode: NewRowid P1 P2 P3 * * @@ -4368,31 +4380,31 @@ assert( pC->uc.pCursor!=0 ); assert( pC->deferredMoveto==0 ); hasUpdateCallback = db->xUpdateCallback && pOp->p4.z && pC->isTable; if( pOp->p5 && hasUpdateCallback ){ - sqlite3BtreeKeySize(pC->uc.pCursor, &pC->movetoTarget); + sqlite3BtreeKeySize(pC->uc.pCursor, &pC->ux.movetoTarget); } #ifdef SQLITE_DEBUG /* The seek operation that positioned the cursor prior to OP_Delete will - ** have also set the pC->movetoTarget field to the rowid of the row that + ** have also set the pC->ux.movetoTarget field to the rowid of the row that ** is being deleted */ if( pOp->p4.z && pC->isTable && pOp->p5==0 ){ i64 iKey = 0; sqlite3BtreeKeySize(pC->uc.pCursor, &iKey); - assert( pC->movetoTarget==iKey ); + assert( pC->ux.movetoTarget==iKey ); } #endif rc = sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5); pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ if( rc==SQLITE_OK && hasUpdateCallback ){ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, - db->aDb[pC->iDb].zName, pOp->p4.z, pC->movetoTarget); + db->aDb[pC->iDb].zName, pOp->p4.z, pC->ux.movetoTarget); assert( pC->iDb>=0 ); } if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++; break; } @@ -4579,11 +4591,11 @@ assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); if( pC->nullRow ){ pOut->flags = MEM_Null; break; }else if( pC->deferredMoveto ){ - v = pC->movetoTarget; + v = pC->ux.movetoTarget; #ifndef SQLITE_OMIT_VIRTUALTABLE }else if( pC->eCurType==CURTYPE_VTAB ){ assert( pC->uc.pVCur!=0 ); pVtab = pC->uc.pVCur->pVtab; pModule = pVtab->pModule; Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -75,33 +75,35 @@ ** * A one-row "pseudotable" stored in a single register */ struct VdbeCursor { u8 eCurType; /* One of the CURTYPE_* values above */ i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ - u8 nullRow; /* True if pointing to a row with no data */ - u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ - u8 isTable; /* True for rowid tables. False for indexes */ #ifdef SQLITE_DEBUG u8 seekOp; /* Most recent seek operation on this cursor */ + Bool movetoUsed:1; /* True if ux.movetoTarget has been used */ #endif + Bool nullRow:1; /* True if pointing to a row with no data */ + Bool deferredMoveto:1; /* A call to sqlite3BtreeMoveto() is needed */ + Bool isTable:1; /* True for rowid tables. False for indexes */ Bool isEphemeral:1; /* True for an ephemeral table */ Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */ Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */ Pgno pgnoRoot; /* Root page of the open btree cursor */ + int seekResult; /* Result of previous sqlite3BtreeMoveto() */ i16 nField; /* Number of fields in the header */ u16 nHdrParsed; /* Number of header fields parsed so far */ union { BtCursor *pCursor; /* CURTYPE_BTREE. Btree cursor */ sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */ int pseudoTableReg; /* CURTYPE_PSEUDO. Reg holding content. */ VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */ } uc; - Btree *pBt; /* Separate file holding temporary table */ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ - int seekResult; /* Result of previous sqlite3BtreeMoveto() */ - i64 seqCount; /* Sequence counter */ - i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ + union { + i64 seqCount; /* Sequence counter. Only valid if movetoUsed==0 */ + i64 movetoTarget; /* Rowid moved to. */ + } ux; #ifdef SQLITE_ENABLE_COLUMN_USED_MASK u64 maskUsed; /* Mask of columns used by this cursor */ #endif /* Cached information about the header for the data record that the Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -1923,23 +1923,23 @@ */ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ if( pCx==0 ){ return; } - assert( pCx->pBt==0 || pCx->eCurType==CURTYPE_BTREE ); + assert( pCx->isEphemeral==0 || pCx->eCurType==CURTYPE_BTREE ); switch( pCx->eCurType ){ case CURTYPE_SORTER: { sqlite3VdbeSorterClose(p->db, pCx); break; } case CURTYPE_BTREE: { - if( pCx->pBt ){ - sqlite3BtreeClose(pCx->pBt); + assert( pCx->uc.pCursor!=0 ); + if( pCx->isEphemeral ){ + sqlite3BtreeClose(sqlite3BtreeOfCursor(pCx->uc.pCursor)); /* The pCx->pCursor will be close automatically, if it exists, by ** the call above. */ }else{ - assert( pCx->uc.pCursor!=0 ); sqlite3BtreeCloseCursor(pCx->uc.pCursor); } break; } #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -2938,11 +2938,13 @@ extern int sqlite3_search_count; #endif assert( p->deferredMoveto ); assert( p->isTable ); assert( p->eCurType==CURTYPE_BTREE ); - rc = sqlite3BtreeMovetoUnpacked(p->uc.pCursor, 0, p->movetoTarget, 0, &res); + assert( !p->isEphemeral ); + assert( p->movetoUsed ); + rc = sqlite3BtreeMovetoUnpacked(p->uc.pCursor, 0, p->ux.movetoTarget, 0,&res); if( rc ) return rc; if( res!=0 ) return SQLITE_CORRUPT_BKPT; #ifdef SQLITE_TEST sqlite3_search_count++; #endif Index: src/vdbesort.c ================================================================== --- src/vdbesort.c +++ src/vdbesort.c @@ -958,11 +958,11 @@ if( nWorker>=SORTER_MAX_MERGE_COUNT ){ nWorker = SORTER_MAX_MERGE_COUNT-1; } #endif - assert( pCsr->pKeyInfo && pCsr->pBt==0 ); + assert( pCsr->pKeyInfo && !pCsr->isEphemeral ); assert( pCsr->eCurType==CURTYPE_SORTER ); szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nField-1)*sizeof(CollSeq*); sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);