aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2014-10-14 13:41:32 +0000
committerdrh <drh@noemail.net>2014-10-14 13:41:32 +0000
commit92fe38ece56c01929b8ad949c94a8b7732db496c (patch)
tree14273daa821b50e1da797883ca36de3a78329512 /src
parent78aad7cd1d026afa649460cf0ed6508c3d88a16e (diff)
parent8dd8362d6446e8d83fd621122987f048216197c2 (diff)
downloadsqlite-92fe38ece56c01929b8ad949c94a8b7732db496c.tar.gz
sqlite-92fe38ece56c01929b8ad949c94a8b7732db496c.zip
Merge recent trunk micro-optimizations and the DESC index GROUP BY ORDER BY
bug fix into the sessions branch. FossilOrigin-Name: 83d4114f2aa404e670ced33511183baacd813a01
Diffstat (limited to 'src')
-rw-r--r--src/btree.c12
-rw-r--r--src/build.c2
-rw-r--r--src/pcache1.c2
-rw-r--r--src/select.c9
-rw-r--r--src/shell.c1
-rw-r--r--src/sqliteInt.h3
-rw-r--r--src/threads.c4
-rw-r--r--src/vdbe.c150
-rw-r--r--src/vdbeInt.h6
-rw-r--r--src/vdbeaux.c24
-rw-r--r--src/where.c120
11 files changed, 174 insertions, 159 deletions
diff --git a/src/btree.c b/src/btree.c
index 12dcb44cb..3553924c0 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -776,7 +776,7 @@ static int btreeRestoreCursorPosition(BtCursor *pCur){
** back to where it ought to be if this routine returns true.
*/
int sqlite3BtreeCursorHasMoved(BtCursor *pCur){
- return pCur && pCur->eState!=CURSOR_VALID;
+ return pCur->eState!=CURSOR_VALID;
}
/*
@@ -5845,11 +5845,6 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
** in pTemp or the original pCell) and also record its index.
** Allocating a new entry in pPage->aCell[] implies that
** pPage->nOverflow is incremented.
-**
-** If nSkip is non-zero, then do not copy the first nSkip bytes of the
-** cell. The caller will overwrite them after this function returns. If
-** nSkip is non-zero, then pCell may not point to an invalid memory location
-** (but pCell+nSkip is always valid).
*/
static void insertCell(
MemPage *pPage, /* Page into which we are copying */
@@ -5866,7 +5861,6 @@ static void insertCell(
int ins; /* Index in data[] where new cell pointer is inserted */
int cellOffset; /* Address of first cell pointer in data[] */
u8 *data; /* The content of the whole page */
- int nSkip = (iChild ? 4 : 0);
if( *pRC ) return;
@@ -5884,7 +5878,7 @@ static void insertCell(
assert( sz==cellSizePtr(pPage, pCell) || (sz==8 && iChild>0) );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
- memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip);
+ memcpy(pTemp, pCell, sz);
pCell = pTemp;
}
if( iChild ){
@@ -5913,7 +5907,7 @@ static void insertCell(
assert( idx+sz <= (int)pPage->pBt->usableSize );
pPage->nCell++;
pPage->nFree -= (u16)(2 + sz);
- memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip);
+ memcpy(&data[idx], pCell, sz);
if( iChild ){
put4byte(&data[idx], iChild);
}
diff --git a/src/build.c b/src/build.c
index 14d8aab58..b897494db 100644
--- a/src/build.c
+++ b/src/build.c
@@ -2747,7 +2747,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
}else{
addr2 = sqlite3VdbeCurrentAddr(v);
}
- sqlite3VdbeAddOp2(v, OP_SorterData, iSorter, regRecord);
+ sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx);
sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
sqlite3ReleaseTempReg(pParse, regRecord);
diff --git a/src/pcache1.c b/src/pcache1.c
index 9d15e8514..a8c321738 100644
--- a/src/pcache1.c
+++ b/src/pcache1.c
@@ -688,7 +688,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
if( createFlag==1 && (
nPinned>=pGroup->mxPinned
|| nPinned>=pCache->n90pct
- || pcache1UnderMemoryPressure(pCache)
+ || (pcache1UnderMemoryPressure(pCache) && pCache->nRecyclable<nPinned)
)){
return 0;
}
diff --git a/src/select.c b/src/select.c
index 411bca0df..3b422f110 100644
--- a/src/select.c
+++ b/src/select.c
@@ -1181,7 +1181,6 @@ static void generateSortTail(
int nKey;
int iSortTab; /* Sorter cursor to read from */
int nSortData; /* Trailing values to read from sorter */
- u8 p5; /* p5 parameter for 1st OP_Column */
int i;
int bSeq; /* True if sorter record includes seq. no. */
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
@@ -1215,19 +1214,16 @@ static void generateSortTail(
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
VdbeCoverage(v);
codeOffset(v, p->iOffset, addrContinue);
- sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut);
- p5 = OPFLAG_CLEARCACHE;
+ sqlite3VdbeAddOp3(v, OP_SorterData, iTab, regSortOut, iSortTab);
bSeq = 0;
}else{
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v);
codeOffset(v, p->iOffset, addrContinue);
iSortTab = iTab;
- p5 = 0;
bSeq = 1;
}
for(i=0; i<nSortData; i++){
sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq+i, regRow+i);
- if( i==0 ) sqlite3VdbeChangeP5(v, p5);
VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
}
switch( eDest ){
@@ -5156,12 +5152,11 @@ int sqlite3Select(
addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
sqlite3ExprCacheClear(pParse);
if( groupBySort ){
- sqlite3VdbeAddOp2(v, OP_SorterData, sAggInfo.sortingIdx, sortOut);
+ sqlite3VdbeAddOp3(v, OP_SorterData, sAggInfo.sortingIdx, sortOut,sortPTab);
}
for(j=0; j<pGroupBy->nExpr; j++){
if( groupBySort ){
sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j);
- if( j==0 ) sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
}else{
sAggInfo.directMode = 1;
sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j);
diff --git a/src/shell.c b/src/shell.c
index a54db50e4..d77f9d9ef 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -4006,6 +4006,7 @@ static int process_input(ShellState *p, FILE *in){
if( nSql ){
if( !_all_whitespace(zSql) ){
fprintf(stderr, "Error: incomplete SQL: %s\n", zSql);
+ errCnt++;
}
free(zSql);
}
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 02ae089b1..081115b8a 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -159,7 +159,7 @@
*/
#if defined(__GNUC__)
# define SQLITE_NOINLINE __attribute__((noinline))
-#elif defined(_MSC_VER)
+#elif defined(_MSC_VER) && _MSC_VER>=1310
# define SQLITE_NOINLINE __declspec(noinline)
#else
# define SQLITE_NOINLINE
@@ -2677,7 +2677,6 @@ struct AuthContext {
#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 */
#define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */
#define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */
#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */
diff --git a/src/threads.c b/src/threads.c
index 213a129c9..6d39042fd 100644
--- a/src/threads.c
+++ b/src/threads.c
@@ -105,7 +105,7 @@ int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
/* A running thread */
struct SQLiteThread {
- uintptr_t tid; /* The thread handle */
+ void *tid; /* The thread handle */
unsigned id; /* The thread identifier */
void *(*xTask)(void*); /* The routine to run as a thread */
void *pIn; /* Argument to xTask */
@@ -153,7 +153,7 @@ int sqlite3ThreadCreate(
}else{
p->xTask = xTask;
p->pIn = pIn;
- p->tid = _beginthreadex(0, 0, sqlite3ThreadProc, p, 0, &p->id);
+ p->tid = (void*)_beginthreadex(0, 0, sqlite3ThreadProc, p, 0, &p->id);
if( p->tid==0 ){
memset(p, 0, sizeof(*p));
}
diff --git a/src/vdbe.c b/src/vdbe.c
index 185b0f9f6..059ce5860 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -224,6 +224,7 @@ static VdbeCursor *allocateCursor(
memset(pCx, 0, sizeof(VdbeCursor));
pCx->iDb = iDb;
pCx->nField = nField;
+ pCx->aOffset = &pCx->aType[nField];
if( isBtreeCursor ){
pCx->pCursor = (BtCursor*)
&pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
@@ -2286,7 +2287,7 @@ case OP_Column: {
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( p2<pC->nField );
- aOffset = pC->aType + pC->nField;
+ aOffset = pC->aOffset;
#ifndef SQLITE_OMIT_VIRTUALTABLE
assert( pC->pVtabCursor==0 ); /* OP_Column never called on virtual table */
#endif
@@ -2297,7 +2298,7 @@ case OP_Column: {
/* If the cursor cache is stale, bring it up-to-date */
rc = sqlite3VdbeCursorMoveto(pC);
if( rc ) goto abort_due_to_error;
- if( pC->cacheStatus!=p->cacheCtr || (pOp->p5&OPFLAG_CLEARCACHE)!=0 ){
+ if( pC->cacheStatus!=p->cacheCtr ){
if( pC->nullRow ){
if( pCrsr==0 ){
assert( pC->pseudoTableReg>0 );
@@ -2342,14 +2343,6 @@ case OP_Column: {
pC->iHdrOffset = getVarint32(pC->aRow, offset);
pC->nHdrParsed = 0;
aOffset[0] = offset;
- if( avail<offset ){
- /* pC->aRow does not have to hold the entire row, but it does at least
- ** need to cover the header of the record. If pC->aRow does not contain
- ** the complete header, then set it to zero, forcing the header to be
- ** dynamically allocated. */
- pC->aRow = 0;
- pC->szRow = 0;
- }
/* Make sure a corrupt database has not given us an oversize header.
** Do this now to avoid an oversize memory allocation.
@@ -2364,6 +2357,22 @@ case OP_Column: {
rc = SQLITE_CORRUPT_BKPT;
goto op_column_error;
}
+
+ if( avail<offset ){
+ /* pC->aRow does not have to hold the entire row, but it does at least
+ ** need to cover the header of the record. If pC->aRow does not contain
+ ** the complete header, then set it to zero, forcing the header to be
+ ** dynamically allocated. */
+ pC->aRow = 0;
+ pC->szRow = 0;
+ }
+
+ /* The following goto is an optimization. It can be omitted and
+ ** everything will still work. But OP_Column is measurably faster
+ ** by skipping the subsequent conditional, which is always true.
+ */
+ assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */
+ goto op_column_read_header;
}
/* Make sure at least the first p2+1 entries of the header have been
@@ -2373,6 +2382,7 @@ case OP_Column: {
/* If there is more header available for parsing in the record, try
** to extract additional fields up through the p2+1-th field
*/
+ op_column_read_header:
if( pC->iHdrOffset<aOffset[0] ){
/* Make sure zData points to enough of the record to cover the header. */
if( pC->aRow==0 ){
@@ -2417,15 +2427,16 @@ case OP_Column: {
sMem.flags = MEM_Null;
}
- /* If we have read more header data than was contained in the header,
- ** or if the end of the last field appears to be past the end of the
- ** record, or if the end of the last field appears to be before the end
- ** of the record (when all fields present), then we must be dealing
- ** with a corrupt database.
+ /* The record is corrupt if any of the following are true:
+ ** (1) the bytes of the header extend past the declared header size
+ ** (zHdr>zEndHdr)
+ ** (2) the entire header was used but not all data was used
+ ** (zHdr==zEndHdr && offset!=pC->payloadSize)
+ ** (3) the end of the data extends beyond the end of the record.
+ ** (offset > pC->payloadSize)
*/
- if( (zHdr > zEndHdr)
+ if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset!=pC->payloadSize))
|| (offset > pC->payloadSize)
- || (zHdr==zEndHdr && offset!=pC->payloadSize)
){
rc = SQLITE_CORRUPT_BKPT;
goto op_column_error;
@@ -2616,7 +2627,7 @@ case OP_MakeRecord: {
pRec = pLast;
do{
assert( memIsValid(pRec) );
- serial_type = sqlite3VdbeSerialType(pRec, file_format);
+ pRec->uTemp = serial_type = sqlite3VdbeSerialType(pRec, file_format);
len = sqlite3VdbeSerialTypeLen(serial_type);
if( pRec->flags & MEM_Zero ){
if( nData ){
@@ -2665,7 +2676,7 @@ case OP_MakeRecord: {
assert( pData0<=pLast );
pRec = pData0;
do{
- serial_type = sqlite3VdbeSerialType(pRec, file_format);
+ serial_type = pRec->uTemp;
i += putVarint32(&zNewRecord[i], serial_type); /* serial type */
j += sqlite3VdbeSerialPut(&zNewRecord[j], pRec, serial_type); /* content */
}while( (++pRec)<=pLast );
@@ -3564,7 +3575,6 @@ case OP_SeekGT: { /* jump, in3 */
applyNumericAffinity(pIn3, 0);
}
iKey = sqlite3VdbeIntValue(pIn3);
- pC->rowidIsValid = 0;
/* If the P3 value could not be converted into an integer without
** loss of information, then special processing is required... */
@@ -3600,13 +3610,10 @@ case OP_SeekGT: { /* jump, in3 */
}
}
rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)iKey, 0, &res);
+ pC->movetoTarget = iKey; /* Used by OP_Delete */
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
- if( res==0 ){
- pC->rowidIsValid = 1;
- pC->lastRowid = iKey;
- }
}else{
nField = pOp->p4.i;
assert( pOp->p4type==P4_INT32 );
@@ -3636,7 +3643,6 @@ case OP_SeekGT: { /* jump, in3 */
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
- pC->rowidIsValid = 0;
}
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
@@ -3648,7 +3654,6 @@ case OP_SeekGT: { /* jump, in3 */
res = 0;
rc = sqlite3BtreeNext(pC->pCursor, &res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
- pC->rowidIsValid = 0;
}else{
res = 0;
}
@@ -3658,7 +3663,6 @@ case OP_SeekGT: { /* jump, in3 */
res = 0;
rc = sqlite3BtreePrevious(pC->pCursor, &res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
- pC->rowidIsValid = 0;
}else{
/* res might be negative because the table is empty. Check to
** see if this is the case.
@@ -3695,7 +3699,6 @@ case OP_Seek: { /* in2 */
pC->nullRow = 0;
pIn2 = &aMem[pOp->p2];
pC->movetoTarget = sqlite3VdbeIntValue(pIn2);
- pC->rowidIsValid = 0;
pC->deferredMoveto = 1;
break;
}
@@ -3881,15 +3884,13 @@ case OP_NotExists: { /* jump, in3 */
res = 0;
iKey = pIn3->u.i;
rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
- pC->lastRowid = pIn3->u.i;
- pC->rowidIsValid = res==0 ?1:0;
+ pC->movetoTarget = iKey; /* Used by OP_Delete */
pC->nullRow = 0;
pC->cacheStatus = CACHE_STALE;
pC->deferredMoveto = 0;
VdbeBranchTaken(res!=0,2);
if( res!=0 ){
pc = pOp->p2 - 1;
- assert( pC->rowidIsValid==0 );
}
pC->seekResult = res;
break;
@@ -4037,7 +4038,6 @@ case OP_NewRowid: { /* out2-prerelease */
}
assert( v>0 ); /* EV: R-40812-03570 */
}
- pC->rowidIsValid = 0;
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
}
@@ -4137,7 +4137,7 @@ case OP_InsertInt: {
/* Invoke the pre-update hook, if any */
if( db->xPreUpdateCallback
&& pOp->p4type==P4_TABLE
- && (!(pOp->p5 & OPFLAG_ISUPDATE) || pC->rowidIsValid==0)
+ && !(pOp->p5 & OPFLAG_ISUPDATE)
&& HasRowid(pTab)
){
sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, iKey, pOp->p2);
@@ -4162,7 +4162,6 @@ case OP_InsertInt: {
pData->z, pData->n, nZero,
(pOp->p5 & OPFLAG_APPEND)!=0, seekResult
);
- pC->rowidIsValid = 0;
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
@@ -4200,7 +4199,6 @@ case OP_InsertInt: {
** be set to by the update.
*/
case OP_Delete: {
- i64 iKey;
VdbeCursor *pC;
const char *zDb;
Table *pTab;
@@ -4211,29 +4209,27 @@ case OP_Delete: {
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */
- iKey = pC->lastRowid; /* Only used for the update hook */
-
- /* The OP_Delete opcode always follows an OP_NotExists or OP_Last or
- ** OP_Column on the same table without any intervening operations that
- ** might move or invalidate the cursor. Hence cursor pC is always pointing
- ** to the row to be deleted and the sqlite3VdbeCursorMoveto() operation
- ** below is always a no-op and cannot fail. We will run it anyhow, though,
- ** to guard against future changes to the code generator.
- **/
assert( pC->deferredMoveto==0 );
- rc = sqlite3VdbeCursorMoveto(pC);
- if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
+
+#ifdef SQLITE_DEBUG
+ if( pOp->p4type==P4_TABLE && HasRowid(pOp->p4.pTab) ){
+ /* 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
+ ** is being deleted */
+ i64 iKey = 0;
+ sqlite3BtreeKeySize(pC->pCursor, &iKey);
+ assert( pC->movetoTarget==iKey );
+ }
+#endif
/* If the update-hook or pre-update-hook will be invoked, set iKey to
** the rowid of the row being deleted. Set zDb and zTab as well.
*/
if( pOp->p4.z && HAS_UPDATE_HOOK(db) ){
assert( pC->iDb>=0 );
- assert( pC->rowidIsValid || !HasRowid(pOp->p4.pTab) );
- iKey = pC->lastRowid;
zDb = db->aDb[pC->iDb].zName;
pTab = pOp->p4.pTab;
- }
+ }
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/* Invoke the pre-update-hook if required. */
@@ -4241,14 +4237,14 @@ case OP_Delete: {
assert( !(opflags & OPFLAG_ISUPDATE) || (aMem[pOp->p3].flags & MEM_Int) );
sqlite3VdbePreUpdateHook(p, pC,
(opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE,
- zDb, pTab, iKey,
+ zDb, pTab, pC->movetoTarget,
pOp->p3
);
}
#endif
if( opflags & OPFLAG_ISNOOP ) break;
-
+
rc = sqlite3BtreeDelete(pC->pCursor);
pC->cacheStatus = CACHE_STALE;
@@ -4257,7 +4253,8 @@ case OP_Delete: {
p->nChange++;
assert( pOp->p4.z );
if( rc==SQLITE_OK && db->xUpdateCallback && HasRowid(pTab) ){
- db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName,iKey);
+ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName,
+ pC->movetoTarget);
}
}
break;
@@ -4309,10 +4306,17 @@ case OP_SorterCompare: {
break;
};
-/* Opcode: SorterData P1 P2 * * *
+/* Opcode: SorterData P1 P2 P3 * *
** Synopsis: r[P2]=data
**
** Write into register P2 the current sorter data for sorter cursor P1.
+** Then clear the column header cache on cursor P3.
+**
+** This opcode is normally use to move a record out of the sorter and into
+** a register that is the source for a pseudo-table cursor created using
+** OpenPseudo. That pseudo-table cursor is the one that is identified by
+** parameter P3. Clearing the P3 column cache as part of this opcode saves
+** us from having to issue a separate NullRow instruction to clear that cache.
*/
case OP_SorterData: {
VdbeCursor *pC;
@@ -4322,6 +4326,8 @@ case OP_SorterData: {
assert( isSorter(pC) );
rc = sqlite3VdbeSorterRowkey(pC, pOut);
assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) );
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ p->apCsr[pOp->p3]->cacheStatus = CACHE_STALE;
break;
}
@@ -4368,16 +4374,20 @@ case OP_RowData: {
assert( pC->pseudoTableReg==0 );
assert( pC->pCursor!=0 );
pCrsr = pC->pCursor;
- assert( sqlite3BtreeCursorIsValid(pCrsr) );
/* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or
** OP_Rewind/Op_Next with no intervening instructions that might invalidate
- ** the cursor. Hence the following sqlite3VdbeCursorMoveto() call is always
- ** a no-op and can never fail. But we leave it in place as a safety.
+ ** the cursor. If this where not the case, on of the following assert()s
+ ** would fail. Should this ever change (because of changes in the code
+ ** generator) then the fix would be to insert a call to
+ ** sqlite3VdbeCursorMoveto().
*/
assert( pC->deferredMoveto==0 );
+ assert( sqlite3BtreeCursorIsValid(pCrsr) );
+#if 0 /* Not required due to the previous to assert() statements */
rc = sqlite3VdbeCursorMoveto(pC);
- if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+#endif
if( pC->isTable==0 ){
assert( !pC->isTable );
@@ -4446,14 +4456,10 @@ case OP_Rowid: { /* out2-prerelease */
#endif /* SQLITE_OMIT_VIRTUALTABLE */
}else{
assert( pC->pCursor!=0 );
- rc = sqlite3VdbeCursorMoveto(pC);
+ rc = sqlite3VdbeCursorRestore(pC);
if( rc ) goto abort_due_to_error;
- if( pC->rowidIsValid ){
- v = pC->lastRowid;
- }else{
- rc = sqlite3BtreeKeySize(pC->pCursor, &v);
- assert( rc==SQLITE_OK ); /* Always so because of CursorMoveto() above */
- }
+ rc = sqlite3BtreeKeySize(pC->pCursor, &v);
+ assert( rc==SQLITE_OK ); /* Always so because of CursorRestore() above */
}
pOut->u.i = v;
break;
@@ -4472,7 +4478,6 @@ case OP_NullRow: {
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
pC->nullRow = 1;
- pC->rowidIsValid = 0;
pC->cacheStatus = CACHE_STALE;
if( pC->pCursor ){
sqlite3BtreeClearCursor(pC->pCursor);
@@ -4506,7 +4511,6 @@ case OP_Last: { /* jump */
rc = sqlite3BtreeLast(pCrsr, &res);
pC->nullRow = (u8)res;
pC->deferredMoveto = 0;
- pC->rowidIsValid = 0;
pC->cacheStatus = CACHE_STALE;
#ifdef SQLITE_DEBUG
pC->seekOp = OP_Last;
@@ -4573,7 +4577,6 @@ case OP_Rewind: { /* jump */
rc = sqlite3BtreeFirst(pCrsr, &res);
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
- pC->rowidIsValid = 0;
}
pC->nullRow = (u8)res;
assert( pOp->p2>0 && pOp->p2<p->nOp );
@@ -4699,7 +4702,6 @@ next_tail:
}else{
pC->nullRow = 1;
}
- pC->rowidIsValid = 0;
goto check_for_interrupt;
}
@@ -4815,10 +4817,16 @@ case OP_IdxRowid: { /* out2-prerelease */
pCrsr = pC->pCursor;
assert( pCrsr!=0 );
pOut->flags = MEM_Null;
- rc = sqlite3VdbeCursorMoveto(pC);
- if( NEVER(rc) ) goto abort_due_to_error;
- assert( pC->deferredMoveto==0 );
assert( pC->isTable==0 );
+ assert( pC->deferredMoveto==0 );
+
+ /* sqlite3VbeCursorRestore() can only fail if the record has been deleted
+ ** out from under the cursor. That will never happend for an IdxRowid
+ ** opcode, hence the NEVER() arround the check of the return value.
+ */
+ rc = sqlite3VdbeCursorRestore(pC);
+ if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
+
if( !pC->nullRow ){
rowid = 0; /* Not needed. Only used to silence a warning. */
rc = sqlite3VdbeIdxRowid(db, pCrsr, &rowid);
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 4e8b2860f..5ef21320d 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -73,7 +73,6 @@ struct VdbeCursor {
#endif
i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */
u8 nullRow; /* True if pointing to a row with no data */
- u8 rowidIsValid; /* True if lastRowid is valid */
u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
Bool isEphemeral:1; /* True for an ephemeral table */
Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */
@@ -83,7 +82,6 @@ struct VdbeCursor {
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
- i64 lastRowid; /* Rowid being deleted by OP_Delete */
VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
/* Cached information about the header for the data record that the
@@ -100,6 +98,7 @@ struct VdbeCursor {
u32 szRow; /* Byte available in aRow */
u32 iHdrOffset; /* Offset to next unparsed byte of the header */
const u8 *aRow; /* Data for the current row, if all on one page */
+ u32 *aOffset; /* Pointer to aType[nField] */
u32 aType[1]; /* Type values for all entries in the record */
/* 2*nField extra array elements allocated for aType[], beyond the one
** static element declared in the structure. nField total array slots for
@@ -176,7 +175,7 @@ struct Mem {
/* ShallowCopy only needs to copy the information above */
char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */
int szMalloc; /* Size of the zMalloc allocation */
- int iPadding1; /* Padding for 8-byte alignment */
+ u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */
sqlite3 *db; /* The associated database connection */
void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */
#ifdef SQLITE_DEBUG
@@ -403,6 +402,7 @@ struct PreUpdate {
void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*);
void sqliteVdbePopStack(Vdbe*,int);
int sqlite3VdbeCursorMoveto(VdbeCursor*);
+int sqlite3VdbeCursorRestore(VdbeCursor*);
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
void sqlite3VdbePrintOp(FILE*, int, Op*);
#endif
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 33f17c7fd..876e525eb 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -1746,7 +1746,7 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
sqlite3BtreeCloseCursor(pCx->pCursor);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( pCx->pVtabCursor ){
+ else if( pCx->pVtabCursor ){
sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor;
const sqlite3_module *pModule = pVtabCursor->pVtab->pModule;
p->inVtabMethod = 1;
@@ -1789,9 +1789,10 @@ static void closeAllCursors(Vdbe *p){
VdbeFrame *pFrame;
for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
sqlite3VdbeFrameRestore(pFrame);
+ p->pFrame = 0;
+ p->nFrame = 0;
}
- p->pFrame = 0;
- p->nFrame = 0;
+ assert( p->nFrame==0 );
if( p->apCsr ){
int i;
@@ -1813,7 +1814,7 @@ static void closeAllCursors(Vdbe *p){
}
/* Delete any auxdata allocations made by the VM */
- sqlite3VdbeDeleteAuxData(p, -1, 0);
+ if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p, -1, 0);
assert( p->pAuxData==0 );
}
@@ -2719,9 +2720,7 @@ static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){
assert( p->isTable );
rc = sqlite3BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res);
if( rc ) return rc;
- p->lastRowid = p->movetoTarget;
if( res!=0 ) return SQLITE_CORRUPT_BKPT;
- p->rowidIsValid = 1;
#ifdef SQLITE_TEST
sqlite3_search_count++;
#endif
@@ -2748,6 +2747,17 @@ static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){
}
/*
+** Check to ensure that the cursor is valid. Restore the cursor
+** if need be. Return any I/O error from the restore operation.
+*/
+int sqlite3VdbeCursorRestore(VdbeCursor *p){
+ if( sqlite3BtreeCursorHasMoved(p->pCursor) ){
+ return handleMovedCursor(p);
+ }
+ return SQLITE_OK;
+}
+
+/*
** Make sure the cursor p is ready to read or write the row to which it
** was last positioned. Return an error code if an OOM fault or I/O error
** prevents us from positioning the cursor to its correct position.
@@ -2764,7 +2774,7 @@ int sqlite3VdbeCursorMoveto(VdbeCursor *p){
if( p->deferredMoveto ){
return handleDeferredMoveto(p);
}
- if( sqlite3BtreeCursorHasMoved(p->pCursor) ){
+ if( p->pCursor && sqlite3BtreeCursorHasMoved(p->pCursor) ){
return handleMovedCursor(p);
}
return SQLITE_OK;
diff --git a/src/where.c b/src/where.c
index 011ad66c0..bc0110779 100644
--- a/src/where.c
+++ b/src/where.c
@@ -2209,8 +2209,8 @@ static int whereRangeScanEst(
assert( pLower==0 || (pLower->eOperator & (WO_GT|WO_GE))!=0 );
assert( pUpper==0 || (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
- assert( p->pKeyInfo!=0 && p->pKeyInfo->aSortOrder!=0 );
- if( p->pKeyInfo->aSortOrder[nEq] ){
+ assert( p->aSortOrder!=0 );
+ if( p->aSortOrder[nEq] ){
/* The roles of pLower and pUpper are swapped for a DESC index */
SWAP(WhereTerm*, pLower, pUpper);
}
@@ -2737,9 +2737,8 @@ static void explainAppendTerm(
/*
** Argument pLevel describes a strategy for scanning table pTab. This
-** function returns a pointer to a string buffer containing a description
-** of the subset of table rows scanned by the strategy in the form of an
-** SQL expression. Or, if all rows are scanned, NULL is returned.
+** function appends text to pStr that describes the subset of table
+** rows scanned by the strategy in the form of an SQL expression.
**
** For example, if the query:
**
@@ -2749,49 +2748,37 @@ static void explainAppendTerm(
** string similar to:
**
** "a=? AND b>?"
-**
-** The returned pointer points to memory obtained from sqlite3DbMalloc().
-** It is the responsibility of the caller to free the buffer when it is
-** no longer required.
*/
-static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){
+static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop, Table *pTab){
Index *pIndex = pLoop->u.btree.pIndex;
u16 nEq = pLoop->u.btree.nEq;
u16 nSkip = pLoop->u.btree.nSkip;
int i, j;
Column *aCol = pTab->aCol;
i16 *aiColumn = pIndex->aiColumn;
- StrAccum txt;
- if( nEq==0 && (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){
- return 0;
- }
- sqlite3StrAccumInit(&txt, 0, 0, SQLITE_MAX_LENGTH);
- txt.db = db;
- sqlite3StrAccumAppend(&txt, " (", 2);
+ if( nEq==0 && (pLoop->wsFlags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ) return;
+ sqlite3StrAccumAppend(pStr, " (", 2);
for(i=0; i<nEq; i++){
char *z = aiColumn[i] < 0 ? "rowid" : aCol[aiColumn[i]].zName;
if( i>=nSkip ){
- explainAppendTerm(&txt, i, z, "=");
+ explainAppendTerm(pStr, i, z, "=");
}else{
- if( i ) sqlite3StrAccumAppend(&txt, " AND ", 5);
- sqlite3StrAccumAppend(&txt, "ANY(", 4);
- sqlite3StrAccumAppendAll(&txt, z);
- sqlite3StrAccumAppend(&txt, ")", 1);
+ if( i ) sqlite3StrAccumAppend(pStr, " AND ", 5);
+ sqlite3XPrintf(pStr, 0, "ANY(%s)", z);
}
}
j = i;
if( pLoop->wsFlags&WHERE_BTM_LIMIT ){
char *z = aiColumn[j] < 0 ? "rowid" : aCol[aiColumn[j]].zName;
- explainAppendTerm(&txt, i++, z, ">");
+ explainAppendTerm(pStr, i++, z, ">");
}
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
char *z = aiColumn[j] < 0 ? "rowid" : aCol[aiColumn[j]].zName;
- explainAppendTerm(&txt, i, z, "<");
+ explainAppendTerm(pStr, i, z, "<");
}
- sqlite3StrAccumAppend(&txt, ")", 1);
- return sqlite3StrAccumFinish(&txt);
+ sqlite3StrAccumAppend(pStr, ")", 1);
}
/*
@@ -2815,11 +2802,13 @@ static void explainOneScan(
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
Vdbe *v = pParse->pVdbe; /* VM being constructed */
sqlite3 *db = pParse->db; /* Database handle */
- char *zMsg; /* Text to add to EQP output */
int iId = pParse->iSelectId; /* Select id (left-most output column) */
int isSearch; /* True for a SEARCH. False for SCAN. */
WhereLoop *pLoop; /* The controlling WhereLoop object */
u32 flags; /* Flags that describe this loop */
+ char *zMsg; /* Text to add to EQP output */
+ StrAccum str; /* EQP output string */
+ char zBuf[100]; /* Initial space for EQP output string */
pLoop = pLevel->pWLoop;
flags = pLoop->wsFlags;
@@ -2829,54 +2818,70 @@ static void explainOneScan(
|| ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
- zMsg = sqlite3MPrintf(db, "%s", isSearch?"SEARCH":"SCAN");
+ sqlite3StrAccumInit(&str, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
+ str.db = db;
+ sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
if( pItem->pSelect ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s SUBQUERY %d", zMsg,pItem->iSelectId);
+ sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId);
}else{
- zMsg = sqlite3MAppendf(db, zMsg, "%s TABLE %s", zMsg, pItem->zName);
+ sqlite3XPrintf(&str, 0, " TABLE %s", pItem->zName);
}
if( pItem->zAlias ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias);
+ sqlite3XPrintf(&str, 0, " AS %s", pItem->zAlias);
}
- if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0
- && ALWAYS(pLoop->u.btree.pIndex!=0)
- ){
- const char *zFmt;
- Index *pIdx = pLoop->u.btree.pIndex;
- char *zWhere = explainIndexRange(db, pLoop, pItem->pTab);
+ if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
+ const char *zFmt = 0;
+ Index *pIdx;
+
+ assert( pLoop->u.btree.pIndex!=0 );
+ pIdx = pLoop->u.btree.pIndex;
assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) );
if( !HasRowid(pItem->pTab) && IsPrimaryKeyIndex(pIdx) ){
- zFmt = zWhere ? "%s USING PRIMARY KEY%.0s%s" : "%s%.0s%s";
+ if( isSearch ){
+ zFmt = "PRIMARY KEY";
+ }
}else if( flags & WHERE_AUTO_INDEX ){
- zFmt = "%s USING AUTOMATIC COVERING INDEX%.0s%s";
+ zFmt = "AUTOMATIC COVERING INDEX";
}else if( flags & WHERE_IDX_ONLY ){
- zFmt = "%s USING COVERING INDEX %s%s";
+ zFmt = "COVERING INDEX %s";
}else{
- zFmt = "%s USING INDEX %s%s";
+ zFmt = "INDEX %s";
+ }
+ if( zFmt ){
+ sqlite3StrAccumAppend(&str, " USING ", 7);
+ sqlite3XPrintf(&str, 0, zFmt, pIdx->zName);
+ explainIndexRange(&str, pLoop, pItem->pTab);
}
- zMsg = sqlite3MAppendf(db, zMsg, zFmt, zMsg, pIdx->zName, zWhere);
- sqlite3DbFree(db, zWhere);
}else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg);
-
+ const char *zRange;
if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid=?)", zMsg);
+ zRange = "(rowid=?)";
}else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>? AND rowid<?)", zMsg);
+ zRange = "(rowid>? AND rowid<?)";
}else if( flags&WHERE_BTM_LIMIT ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>?)", zMsg);
- }else if( ALWAYS(flags&WHERE_TOP_LIMIT) ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid<?)", zMsg);
+ zRange = "(rowid>?)";
+ }else{
+ assert( flags&WHERE_TOP_LIMIT);
+ zRange = "(rowid<?)";
}
+ sqlite3StrAccumAppendAll(&str, " USING INTEGER PRIMARY KEY ");
+ sqlite3StrAccumAppendAll(&str, zRange);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg,
+ sqlite3XPrintf(&str, 0, " VIRTUAL TABLE INDEX %d:%s",
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
}
#endif
- zMsg = sqlite3MAppendf(db, zMsg, "%s", zMsg);
+#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
+ if( pLoop->nOut>=10 ){
+ sqlite3XPrintf(&str, 0, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
+ }else{
+ sqlite3StrAccumAppend(&str, " (~1 row)", 9);
+ }
+#endif
+ zMsg = sqlite3StrAccumFinish(&str);
sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC);
}
}
@@ -5355,7 +5360,7 @@ static i8 wherePathSatisfiesOrderBy(
isMatch = 1;
break;
}
- if( isMatch && (pWInfo->wctrlFlags & WHERE_GROUPBY)==0 ){
+ if( isMatch && (wctrlFlags & WHERE_GROUPBY)==0 ){
/* Make sure the sort order is compatible in an ORDER BY clause.
** Sort order is irrelevant for a GROUP BY clause. */
if( revSet ){
@@ -5820,12 +5825,15 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP)
&& pWInfo->nOBSat==pWInfo->pOrderBy->nExpr
){
- Bitmask notUsed = 0;
+ Bitmask revMask = 0;
int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy,
- pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], &notUsed
+ pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], &revMask
);
assert( pWInfo->sorted==0 );
- pWInfo->sorted = (nOrder==pWInfo->pOrderBy->nExpr);
+ if( nOrder==pWInfo->pOrderBy->nExpr ){
+ pWInfo->sorted = 1;
+ pWInfo->revMask = revMask;
+ }
}
}