mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-19 21:43:15 +03:00
Merge recent trunk micro-optimizations and the DESC index GROUP BY ORDER BY
bug fix into the sessions branch. FossilOrigin-Name: 83d4114f2aa404e670ced33511183baacd813a01
This commit is contained in:
12
src/btree.c
12
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() */
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
150
src/vdbe.c
150
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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -2747,6 +2746,17 @@ static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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
|
||||
@@ -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;
|
||||
|
||||
120
src/where.c
120
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], ¬Used
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user