diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/alter.c | 4 | ||||
-rw-r--r-- | src/attach.c | 8 | ||||
-rw-r--r-- | src/backup.c | 7 | ||||
-rw-r--r-- | src/btree.c | 26 | ||||
-rw-r--r-- | src/btree.h | 3 | ||||
-rw-r--r-- | src/build.c | 39 | ||||
-rw-r--r-- | src/callback.c | 12 | ||||
-rw-r--r-- | src/delete.c | 2 | ||||
-rw-r--r-- | src/expr.c | 1 | ||||
-rw-r--r-- | src/insert.c | 29 | ||||
-rw-r--r-- | src/main.c | 2 | ||||
-rw-r--r-- | src/os_unix.c | 4 | ||||
-rw-r--r-- | src/os_win.c | 74 | ||||
-rw-r--r-- | src/pager.c | 33 | ||||
-rw-r--r-- | src/pager.h | 1 | ||||
-rw-r--r-- | src/pragma.c | 1 | ||||
-rw-r--r-- | src/select.c | 3 | ||||
-rw-r--r-- | src/sqlite.h.in | 12 | ||||
-rw-r--r-- | src/sqliteInt.h | 5 | ||||
-rw-r--r-- | src/tclsqlite.c | 54 | ||||
-rw-r--r-- | src/test1.c | 6 | ||||
-rw-r--r-- | src/test_intarray.c | 5 | ||||
-rw-r--r-- | src/test_quota.c | 9 | ||||
-rw-r--r-- | src/vdbeaux.c | 2 | ||||
-rw-r--r-- | src/vtab.c | 10 | ||||
-rw-r--r-- | src/wal.c | 2 | ||||
-rw-r--r-- | src/where.c | 731 |
27 files changed, 630 insertions, 455 deletions
diff --git a/src/alter.c b/src/alter.c index 8a96ab704..a49d3349d 100644 --- a/src/alter.c +++ b/src/alter.c @@ -414,7 +414,7 @@ void sqlite3AlterRenameTable( assert( pSrc->nSrc==1 ); assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); - pTab = sqlite3LocateTable(pParse, 0, pSrc->a[0].zName, pSrc->a[0].zDatabase); + pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); if( !pTab ) goto exit_rename_table; iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); zDb = db->aDb[iDb].zName; @@ -757,7 +757,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ assert( pParse->pNewTable==0 ); assert( sqlite3BtreeHoldsAllMutexes(db) ); if( db->mallocFailed ) goto exit_begin_add_column; - pTab = sqlite3LocateTable(pParse, 0, pSrc->a[0].zName, pSrc->a[0].zDatabase); + pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); if( !pTab ) goto exit_begin_add_column; #ifndef SQLITE_OMIT_VIRTUALTABLE diff --git a/src/attach.c b/src/attach.c index e295c3011..3e045aa36 100644 --- a/src/attach.c +++ b/src/attach.c @@ -434,6 +434,7 @@ int sqlite3FixInit( assert( db->nDb>iDb ); pFix->pParse = pParse; pFix->zDb = db->aDb[iDb].zName; + pFix->pSchema = db->aDb[iDb].pSchema; pFix->zType = zType; pFix->pName = pName; return 1; @@ -464,14 +465,15 @@ int sqlite3FixSrcList( if( NEVER(pList==0) ) return 0; zDb = pFix->zDb; for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){ - if( pItem->zDatabase==0 ){ - pItem->zDatabase = sqlite3DbStrDup(pFix->pParse->db, zDb); - }else if( sqlite3StrICmp(pItem->zDatabase,zDb)!=0 ){ + if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){ sqlite3ErrorMsg(pFix->pParse, "%s %T cannot reference objects in database %s", pFix->zType, pFix->pName, pItem->zDatabase); return 1; } + sqlite3_free(pItem->zDatabase); + pItem->zDatabase = 0; + pItem->pSchema = pFix->pSchema; #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1; if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1; diff --git a/src/backup.c b/src/backup.c index 4881215e9..6abc11c20 100644 --- a/src/backup.c +++ b/src/backup.c @@ -219,13 +219,16 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){ const int nCopy = MIN(nSrcPgsz, nDestPgsz); const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz; #ifdef SQLITE_HAS_CODEC - int nSrcReserve = sqlite3BtreeGetReserve(p->pSrc); + /* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is + ** guaranteed that the shared-mutex is held by this thread, handle + ** p->pSrc may not actually be the owner. */ + int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc); int nDestReserve = sqlite3BtreeGetReserve(p->pDest); #endif - int rc = SQLITE_OK; i64 iOff; + assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 ); assert( p->bDestLocked ); assert( !isFatalError(p->rc) ); assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); diff --git a/src/btree.c b/src/btree.c index 47dd7db99..4ee0c860a 100644 --- a/src/btree.c +++ b/src/btree.c @@ -2200,6 +2200,24 @@ int sqlite3BtreeGetPageSize(Btree *p){ return p->pBt->pageSize; } +#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG) +/* +** This function is similar to sqlite3BtreeGetReserve(), except that it +** may only be called if it is guaranteed that the b-tree mutex is already +** held. +** +** This is useful in one special case in the backup API code where it is +** known that the shared b-tree mutex is held, but the mutex on the +** database handle that owns *p is not. In this case if sqlite3BtreeEnter() +** were to be called, it might collide with some other operation on the +** database handle that owns *p, causing undefined behaviour. +*/ +int sqlite3BtreeGetReserveNoMutex(Btree *p){ + assert( sqlite3_mutex_held(p->pBt->mutex) ); + return p->pBt->pageSize - p->pBt->usableSize; +} +#endif /* SQLITE_HAS_CODEC || SQLITE_DEBUG */ + #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) /* ** Return the number of bytes of space at the end of every page that @@ -5256,7 +5274,7 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){ return SQLITE_OK; /* No overflow pages. Return without doing anything */ } if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){ - return SQLITE_CORRUPT; /* Cell extends past end of page */ + return SQLITE_CORRUPT_BKPT; /* Cell extends past end of page */ } ovflPgno = get4byte(&pCell[info.iOverflow]); assert( pBt->usableSize > 4 ); @@ -5922,6 +5940,9 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ ** If aOvflSpace is set to a null pointer, this function returns ** SQLITE_NOMEM. */ +#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM) +#pragma optimize("", off) +#endif static int balance_nonroot( MemPage *pParent, /* Parent page of siblings being balanced */ int iParentIdx, /* Index of "the page" in pParent */ @@ -6552,6 +6573,9 @@ balance_cleanup: return rc; } +#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM) +#pragma optimize("", on) +#endif /* diff --git a/src/btree.h b/src/btree.h index 95897d566..0efa0cdcd 100644 --- a/src/btree.h +++ b/src/btree.h @@ -71,6 +71,9 @@ int sqlite3BtreeMaxPageCount(Btree*,int); u32 sqlite3BtreeLastPage(Btree*); int sqlite3BtreeSecureDelete(Btree*,int); int sqlite3BtreeGetReserve(Btree*); +#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG) +int sqlite3BtreeGetReserveNoMutex(Btree *p); +#endif int sqlite3BtreeSetAutoVacuum(Btree *, int); int sqlite3BtreeGetAutoVacuum(Btree *); int sqlite3BtreeBeginTrans(Btree*,int); diff --git a/src/build.c b/src/build.c index 8910bbd98..00ff705f7 100644 --- a/src/build.c +++ b/src/build.c @@ -320,6 +320,31 @@ Table *sqlite3LocateTable( } /* +** Locate the table identified by *p. +** +** This is a wrapper around sqlite3LocateTable(). The difference between +** sqlite3LocateTable() and this function is that this function restricts +** the search to schema (p->pSchema) if it is not NULL. p->pSchema may be +** non-NULL if it is part of a view or trigger program definition. See +** sqlite3FixSrcList() for details. +*/ +Table *sqlite3LocateTableItem( + Parse *pParse, + int isView, + struct SrcList_item *p +){ + const char *zDb; + assert( p->pSchema==0 || p->zDatabase==0 ); + if( p->pSchema ){ + int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema); + zDb = pParse->db->aDb[iDb].zName; + }else{ + zDb = p->zDatabase; + } + return sqlite3LocateTable(pParse, isView, p->zName, zDb); +} + +/* ** Locate the in-memory structure that describes ** a particular index given the name of that index ** and the name of the database that contains the index. @@ -1295,10 +1320,7 @@ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){ pColl = sqlite3FindCollSeq(db, enc, zName, initbusy); if( !initbusy && (!pColl || !pColl->xCmp) ){ - pColl = sqlite3GetCollSeq(db, enc, pColl, zName); - if( !pColl ){ - sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName); - } + pColl = sqlite3GetCollSeq(pParse, enc, pColl, zName); } return pColl; @@ -2114,8 +2136,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ assert( pParse->nErr==0 ); assert( pName->nSrc==1 ); if( noErr ) db->suppressErr++; - pTab = sqlite3LocateTable(pParse, isView, - pName->a[0].zName, pName->a[0].zDatabase); + pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]); if( noErr ) db->suppressErr--; if( pTab==0 ){ @@ -2555,9 +2576,9 @@ Index *sqlite3CreateIndex( ** sqlite3FixSrcList can never fail. */ assert(0); } - pTab = sqlite3LocateTable(pParse, 0, pTblName->a[0].zName, - pTblName->a[0].zDatabase); - if( !pTab || db->mallocFailed ) goto exit_create_index; + pTab = sqlite3LocateTableItem(pParse, 0, &pTblName->a[0]); + assert( db->mallocFailed==0 || pTab==0 ); + if( pTab==0 ) goto exit_create_index; assert( db->aDb[iDb].pSchema==pTab->pSchema ); }else{ assert( pName==0 ); diff --git a/src/callback.c b/src/callback.c index a515e05e2..d40c65cb9 100644 --- a/src/callback.c +++ b/src/callback.c @@ -75,17 +75,18 @@ static int synthCollSeq(sqlite3 *db, CollSeq *pColl){ ** ** The return value is either the collation sequence to be used in database ** db for collation type name zName, length nName, or NULL, if no collation -** sequence can be found. +** sequence can be found. If no collation is found, leave an error message. ** ** See also: sqlite3LocateCollSeq(), sqlite3FindCollSeq() */ CollSeq *sqlite3GetCollSeq( - sqlite3* db, /* The database connection */ + Parse *pParse, /* Parsing context */ u8 enc, /* The desired encoding for the collating sequence */ CollSeq *pColl, /* Collating sequence with native encoding, or NULL */ const char *zName /* Collating sequence name */ ){ CollSeq *p; + sqlite3 *db = pParse->db; p = pColl; if( !p ){ @@ -102,6 +103,9 @@ CollSeq *sqlite3GetCollSeq( p = 0; } assert( !p || p->xCmp ); + if( p==0 ){ + sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName); + } return p; } @@ -120,10 +124,8 @@ int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){ if( pColl ){ const char *zName = pColl->zName; sqlite3 *db = pParse->db; - CollSeq *p = sqlite3GetCollSeq(db, ENC(db), pColl, zName); + CollSeq *p = sqlite3GetCollSeq(pParse, ENC(db), pColl, zName); if( !p ){ - sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName); - pParse->nErr++; return SQLITE_ERROR; } assert( p==pColl ); diff --git a/src/delete.c b/src/delete.c index a69a62902..1411b7c61 100644 --- a/src/delete.c +++ b/src/delete.c @@ -32,7 +32,7 @@ Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ struct SrcList_item *pItem = pSrc->a; Table *pTab; assert( pItem && pSrc->nSrc==1 ); - pTab = sqlite3LocateTable(pParse, 0, pItem->zName, pItem->zDatabase); + pTab = sqlite3LocateTableItem(pParse, 0, pItem); sqlite3DeleteTable(pParse->db, pItem->pTab); pItem->pTab = pTab; if( pTab ){ diff --git a/src/expr.c b/src/expr.c index 2d1fb38ed..04216b171 100644 --- a/src/expr.c +++ b/src/expr.c @@ -930,6 +930,7 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){ struct SrcList_item *pNewItem = &pNew->a[i]; struct SrcList_item *pOldItem = &p->a[i]; Table *pTab; + pNewItem->pSchema = pOldItem->pSchema; pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase); pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias); diff --git a/src/insert.c b/src/insert.c index 097b6fcad..3950dbd3b 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1274,20 +1274,25 @@ void sqlite3GenerateConstraintChecks( onError = overrideError!=OE_Default ? overrideError : OE_Abort; for(i=0; i<pCheck->nExpr; i++){ int allOk = sqlite3VdbeMakeLabel(v); - sqlite3ExprIfTrue(pParse, pCheck->a[i].pExpr, allOk, SQLITE_JUMPIFNULL); - if( onError==OE_Ignore ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); - }else{ - char *zConsName = pCheck->a[i].zName; - if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */ - if( zConsName ){ - zConsName = sqlite3MPrintf(db, "constraint %s failed", zConsName); + Expr *pDup = sqlite3ExprDup(db, pCheck->a[i].pExpr, 0); + if( !db->mallocFailed ){ + assert( pDup!=0 ); + sqlite3ExprIfTrue(pParse, pDup, allOk, SQLITE_JUMPIFNULL); + if( onError==OE_Ignore ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); }else{ - zConsName = 0; + char *zConsName = pCheck->a[i].zName; + if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */ + if( zConsName ){ + zConsName = sqlite3MPrintf(db, "constraint %s failed", zConsName); + }else{ + zConsName = 0; + } + sqlite3HaltConstraint(pParse, onError, zConsName, P4_DYNAMIC); } - sqlite3HaltConstraint(pParse, onError, zConsName, P4_DYNAMIC); + sqlite3VdbeResolveLabel(v, allOk); } - sqlite3VdbeResolveLabel(v, allOk); + sqlite3ExprDelete(db, pDup); } } #endif /* !defined(SQLITE_OMIT_CHECK) */ @@ -1753,7 +1758,7 @@ static int xferOptimization( ** we have to check the semantics. */ pItem = pSelect->pSrc->a; - pSrc = sqlite3LocateTable(pParse, 0, pItem->zName, pItem->zDatabase); + pSrc = sqlite3LocateTableItem(pParse, 0, pItem); if( pSrc==0 ){ return 0; /* FROM clause does not contain a real table */ } diff --git a/src/main.c b/src/main.c index 5778f8780..0d1e092c5 100644 --- a/src/main.c +++ b/src/main.c @@ -3040,7 +3040,7 @@ int sqlite3_test_control(int op, ...){ */ case SQLITE_TESTCTRL_OPTIMIZATIONS: { sqlite3 *db = va_arg(ap, sqlite3*); - db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff); + db->dbOptFlags = (u8)(va_arg(ap, int) & 0xff); break; } diff --git a/src/os_unix.c b/src/os_unix.c index c0df66e8e..0852eb1a8 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3010,6 +3010,8 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ i64 newOffset; #endif TIMER_START; + assert( cnt==(cnt&0x1ffff) ); + cnt &= 0x1ffff; do{ #if defined(USE_PREAD) got = osPread(id->h, pBuf, cnt, offset); @@ -3099,6 +3101,8 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ #if (!defined(USE_PREAD) && !defined(USE_PREAD64)) i64 newOffset; #endif + assert( cnt==(cnt&0x1ffff) ); + cnt &= 0x1ffff; TIMER_START; #if defined(USE_PREAD) do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); diff --git a/src/os_win.c b/src/os_win.c index c4d87b92c..2d4c74af9 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -34,6 +34,57 @@ #endif /* +** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions +** based on the sub-platform)? +*/ +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +# define SQLITE_WIN32_HAS_ANSI +#endif + +/* +** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions +** based on the sub-platform)? +*/ +#if SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT +# define SQLITE_WIN32_HAS_WIDE +#endif + +/* +** Do we need to manually define the Win32 file mapping APIs for use with WAL +** mode (e.g. these APIs are available in the Windows CE SDK; however, they +** are not present in the header file)? +*/ +#if SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL) +/* +** Two of the file mapping APIs are different under WinRT. Figure out which +** set we need. +*/ +#if SQLITE_OS_WINRT +WINBASEAPI HANDLE WINAPI CreateFileMappingFromApp(HANDLE, \ + LPSECURITY_ATTRIBUTES, ULONG, ULONG64, LPCWSTR); + +WINBASEAPI LPVOID WINAPI MapViewOfFileFromApp(HANDLE, ULONG, ULONG64, SIZE_T); +#else +#if defined(SQLITE_WIN32_HAS_ANSI) +WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, \ + DWORD, DWORD, DWORD, LPCSTR); +#endif /* defined(SQLITE_WIN32_HAS_ANSI) */ + +#if defined(SQLITE_WIN32_HAS_WIDE) +WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, \ + DWORD, DWORD, DWORD, LPCWSTR); +#endif /* defined(SQLITE_WIN32_HAS_WIDE) */ + +WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T); +#endif /* SQLITE_OS_WINRT */ + +/* +** This file mapping API is common to both Win32 and WinRT. +*/ +WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID); +#endif /* SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL) */ + +/* ** Macro to find the minimum of two numeric values. */ #ifndef MIN @@ -238,14 +289,6 @@ int sqlite3_os_type = 0; static int sqlite3_os_type = 0; #endif -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT -# define SQLITE_WIN32_HAS_ANSI -#endif - -#if SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT -# define SQLITE_WIN32_HAS_WIDE -#endif - #ifndef SYSCALL # define SYSCALL sqlite3_syscall_ptr #endif @@ -402,7 +445,11 @@ static struct win_syscall { #define osFormatMessageW ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPWSTR, \ DWORD,va_list*))aSyscall[15].pCurrent) +#if !defined(SQLITE_OMIT_LOAD_EXTENSION) { "FreeLibrary", (SYSCALL)FreeLibrary, 0 }, +#else + { "FreeLibrary", (SYSCALL)0, 0 }, +#endif #define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[16].pCurrent) @@ -483,6 +530,7 @@ static struct win_syscall { #define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent) +#if !defined(SQLITE_OMIT_LOAD_EXTENSION) #if SQLITE_OS_WINCE /* The GetProcAddressA() routine is only available on Windows CE. */ { "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 }, @@ -491,6 +539,9 @@ static struct win_syscall { ** an ANSI string regardless of the _UNICODE setting */ { "GetProcAddressA", (SYSCALL)GetProcAddress, 0 }, #endif +#else + { "GetProcAddressA", (SYSCALL)0, 0 }, +#endif #define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \ LPCSTR))aSyscall[27].pCurrent) @@ -594,7 +645,7 @@ static struct win_syscall { #define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \ LPCVOID))aSyscall[41].pCurrent) -#if defined(SQLITE_WIN32_HAS_ANSI) +#if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION) { "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 }, #else { "LoadLibraryA", (SYSCALL)0, 0 }, @@ -602,7 +653,8 @@ static struct win_syscall { #define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[42].pCurrent) -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ + !defined(SQLITE_OMIT_LOAD_EXTENSION) { "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 }, #else { "LoadLibraryW", (SYSCALL)0, 0 }, @@ -791,7 +843,7 @@ static struct win_syscall { #define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \ LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[66].pCurrent) -#if SQLITE_OS_WINRT +#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION) { "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 }, #else { "LoadPackagedLibrary", (SYSCALL)0, 0 }, diff --git a/src/pager.c b/src/pager.c index a094e0da2..a767d747f 100644 --- a/src/pager.c +++ b/src/pager.c @@ -2510,6 +2510,21 @@ static int pager_truncate(Pager *pPager, Pgno nPage){ } /* +** Return a sanitized version of the sector-size of OS file pFile. The +** return value is guaranteed to lie between 32 and MAX_SECTOR_SIZE. +*/ +int sqlite3SectorSize(sqlite3_file *pFile){ + int iRet = sqlite3OsSectorSize(pFile); + if( iRet<32 ){ + iRet = 512; + }else if( iRet>MAX_SECTOR_SIZE ){ + assert( MAX_SECTOR_SIZE>=512 ); + iRet = MAX_SECTOR_SIZE; + } + return iRet; +} + +/* ** Set the value of the Pager.sectorSize variable for the given ** pager based on the value returned by the xSectorSize method ** of the open database file. The sector size will be used used @@ -2544,14 +2559,7 @@ static void setSectorSize(Pager *pPager){ ** call will segfault. */ pPager->sectorSize = 512; }else{ - pPager->sectorSize = sqlite3OsSectorSize(pPager->fd); - if( pPager->sectorSize<32 ){ - pPager->sectorSize = 512; - } - if( pPager->sectorSize>MAX_SECTOR_SIZE ){ - assert( MAX_SECTOR_SIZE>=512 ); - pPager->sectorSize = MAX_SECTOR_SIZE; - } + pPager->sectorSize = sqlite3SectorSize(pPager->fd); } } @@ -3468,9 +3476,16 @@ void sqlite3PagerSetBusyhandler( Pager *pPager, /* Pager object */ int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ -){ +){ pPager->xBusyHandler = xBusyHandler; pPager->pBusyHandlerArg = pBusyHandlerArg; + + if( isOpen(pPager->fd) ){ + void **ap = (void **)&pPager->xBusyHandler; + assert( ((int(*)(void *))(ap[0]))==xBusyHandler ); + assert( ap[1]==pBusyHandlerArg ); + sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap); + } } /* diff --git a/src/pager.h b/src/pager.h index 2b60e058d..5fb0f0133 100644 --- a/src/pager.h +++ b/src/pager.h @@ -160,6 +160,7 @@ void *sqlite3PagerTempSpace(Pager*); int sqlite3PagerIsMemdb(Pager*); void sqlite3PagerCacheStat(Pager *, int, int, int *); void sqlite3PagerClearCache(Pager *); +int sqlite3SectorSize(sqlite3_file *); /* Functions used to truncate the database file. */ void sqlite3PagerTruncateImage(Pager*,Pgno); diff --git a/src/pragma.c b/src/pragma.c index f41f2db06..8eaec51ec 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -357,6 +357,7 @@ void sqlite3Pragma( aFcntl[1] = zLeft; aFcntl[2] = zRight; aFcntl[3] = 0; + db->busyHandler.nBusy = 0; rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl); if( rc==SQLITE_OK ){ if( aFcntl[0] ){ diff --git a/src/select.c b/src/select.c index 2da14d93e..51847ffbd 100644 --- a/src/select.c +++ b/src/select.c @@ -3332,8 +3332,7 @@ static int selectExpander(Walker *pWalker, Select *p){ }else{ /* An ordinary table or view name in the FROM clause */ assert( pFrom->pTab==0 ); - pFrom->pTab = pTab = - sqlite3LocateTable(pParse,0,pFrom->zName,pFrom->zDatabase); + pFrom->pTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom); if( pTab==0 ) return WRC_Abort; pTab->nRef++; #if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE) diff --git a/src/sqlite.h.in b/src/sqlite.h.in index b2edffa5a..1d5406f0f 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -852,6 +852,17 @@ struct sqlite3_io_methods { ** file control occurs at the beginning of pragma statement analysis and so ** it is able to override built-in [PRAGMA] statements. ** </ul> +** +** <li>[[SQLITE_FCNTL_BUSYHANDLER]] +** ^This file-control may be invoked by SQLite on the database file handle +** shortly after it is opened in order to provide a custom VFS with access +** to the connections busy-handler callback. The argument is of type (void **) +** - an array of two (void *) values. The first (void *) actually points +** to a function of type (int (*)(void *)). In order to invoke the connections +** busy-handler, this function should be invoked with the second (void *) in +** the array as the only argument. If it returns non-zero, then the operation +** should be retried. If it returns zero, the custom VFS should abandon the +** current operation. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 @@ -867,6 +878,7 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_VFSNAME 12 #define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 #define SQLITE_FCNTL_PRAGMA 14 +#define SQLITE_FCNTL_BUSYHANDLER 15 /* ** CAPI3REF: Mutex Handle diff --git a/src/sqliteInt.h b/src/sqliteInt.h index b8451aba2..b440f8497 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1877,6 +1877,7 @@ struct SrcList { i16 nSrc; /* Number of tables or subqueries in the FROM clause */ i16 nAlloc; /* Number of entries allocated in a[] below */ struct SrcList_item { + Schema *pSchema; /* Schema to which this item is fixed */ char *zDatabase; /* Name of database holding this table */ char *zName; /* Name of the table */ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ @@ -2449,6 +2450,7 @@ struct TriggerStep { typedef struct DbFixer DbFixer; struct DbFixer { Parse *pParse; /* The parsing context. Error messages written here */ + Schema *pSchema; /* Fix items to this schema */ const char *zDb; /* Make sure all objects are contained in this database */ const char *zType; /* Type of the container - used for error messages */ const Token *pName; /* Name of the container - used for error messages */ @@ -2859,6 +2861,7 @@ void sqlite3ExprIfTrue(Parse*, Expr*, int, int); void sqlite3ExprIfFalse(Parse*, Expr*, int, int); Table *sqlite3FindTable(sqlite3*,const char*, const char*); Table *sqlite3LocateTable(Parse*,int isView,const char*, const char*); +Table *sqlite3LocateTableItem(Parse*,int isView,struct SrcList_item *); Index *sqlite3FindIndex(sqlite3*,const char*, const char*); void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); @@ -3089,7 +3092,7 @@ int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); void sqlite3ColumnDefault(Vdbe *, Table *, int, int); void sqlite3AlterFinishAddColumn(Parse *, Token *); void sqlite3AlterBeginAddColumn(Parse *, SrcList *); -CollSeq *sqlite3GetCollSeq(sqlite3*, u8, CollSeq *, const char*); +CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); char sqlite3AffinityType(const char*); void sqlite3Analyze(Parse*, Token*, Token*); int sqlite3InvokeBusyHandler(BusyHandler*); diff --git a/src/tclsqlite.c b/src/tclsqlite.c index cad7ef7f1..f1a05c9a6 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -53,15 +53,8 @@ #define NUM_PREPARED_STMTS 10 #define MAX_PREPARED_STMTS 100 -/* -** If TCL uses UTF-8 and SQLite is configured to use iso8859, then we -** have to do a translation when going between the two. Set the -** UTF_TRANSLATION_NEEDED macro to indicate that we need to do -** this translation. -*/ -#if defined(TCL_UTF_MAX) && !defined(SQLITE_UTF8) -# define UTF_TRANSLATION_NEEDED 1 -#endif +/* Forward declaration */ +typedef struct SqliteDb SqliteDb; /* ** New SQL functions can be created as TCL scripts. Each such function @@ -71,6 +64,7 @@ typedef struct SqlFunc SqlFunc; struct SqlFunc { Tcl_Interp *interp; /* The TCL interpret to execute the function */ Tcl_Obj *pScript; /* The Tcl_Obj representation of the script */ + SqliteDb *pDb; /* Database connection that owns this function */ int useEvalObjv; /* True if it is safe to use Tcl_EvalObjv */ char *zName; /* Name of this function */ SqlFunc *pNext; /* Next function on the list of them all */ @@ -113,7 +107,6 @@ typedef struct IncrblobChannel IncrblobChannel; ** sqlite3_prepare_v2() or sqlite3_prepare() to prepare SQL statements. ** If SqliteDb.bLegacyPrepare is true, sqlite3_prepare() is used. */ -typedef struct SqliteDb SqliteDb; struct SqliteDb { sqlite3 *db; /* The "real" database structure. MUST BE FIRST */ Tcl_Interp *interp; /* The interpreter used for this database */ @@ -432,6 +425,7 @@ static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){ } } pNew->interp = pDb->interp; + pNew->pDb = pDb; pNew->pScript = 0; pNew->pNext = pDb->pFunc; pDb->pFunc = pNew; @@ -479,6 +473,7 @@ static void DbDeleteCmd(void *db){ while( pDb->pFunc ){ SqlFunc *pFunc = pDb->pFunc; pDb->pFunc = pFunc->pNext; + assert( pFunc->pDb==pDb ); Tcl_DecrRefCount(pFunc->pScript); Tcl_Free((char*)pFunc); } @@ -838,7 +833,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){ break; } case SQLITE_NULL: { - pVal = Tcl_NewStringObj("", 0); + pVal = Tcl_NewStringObj(p->pDb->zNull, -1); break; } default: { @@ -978,26 +973,6 @@ static int auth_callback( #endif /* SQLITE_OMIT_AUTHORIZATION */ /* -** zText is a pointer to text obtained via an sqlite3_result_text() -** or similar interface. This routine returns a Tcl string object, -** reference count set to 0, containing the text. If a translation -** between iso8859 and UTF-8 is required, it is preformed. -*/ -static Tcl_Obj *dbTextToObj(char const *zText){ - Tcl_Obj *pVal; -#ifdef UTF_TRANSLATION_NEEDED - Tcl_DString dCol; - Tcl_DStringInit(&dCol); - Tcl_ExternalToUtfDString(NULL, zText, -1, &dCol); - pVal = Tcl_NewStringObj(Tcl_DStringValue(&dCol), -1); - Tcl_DStringFree(&dCol); -#else - pVal = Tcl_NewStringObj(zText, -1); -#endif - return pVal; -} - -/* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() @@ -1184,13 +1159,13 @@ static int dbPrepareAndBind( int nByte; if( SQLITE_OK!=dbPrepare(pDb, zSql, &pStmt, pzOut) ){ - Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errmsg(pDb->db), -1)); return TCL_ERROR; } if( pStmt==0 ){ if( SQLITE_OK!=sqlite3_errcode(pDb->db) ){ /* A compile-time error in the statement. */ - Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errmsg(pDb->db), -1)); return TCL_ERROR; }else{ /* The statement was a no-op. Continue to the next statement @@ -1409,7 +1384,7 @@ static void dbEvalRowInfo( if( nCol>0 && (papColName || p->pArray) ){ apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol ); for(i=0; i<nCol; i++){ - apColName[i] = dbTextToObj(sqlite3_column_name(pStmt,i)); + apColName[i] = Tcl_NewStringObj(sqlite3_column_name(pStmt,i), -1); Tcl_IncrRefCount(apColName[i]); } p->apColName = apColName; @@ -1496,7 +1471,8 @@ static int dbEvalStep(DbEvalContext *p){ continue; } #endif - Tcl_SetObjResult(pDb->interp, dbTextToObj(sqlite3_errmsg(pDb->db))); + Tcl_SetObjResult(pDb->interp, + Tcl_NewStringObj(sqlite3_errmsg(pDb->db), -1)); return TCL_ERROR; }else{ dbReleaseStmt(pDb, pPreStmt, 0); @@ -1553,11 +1529,11 @@ static Tcl_Obj *dbEvalColumnValue(DbEvalContext *p, int iCol){ return Tcl_NewDoubleObj(sqlite3_column_double(pStmt, iCol)); } case SQLITE_NULL: { - return dbTextToObj(p->pDb->zNull); + return Tcl_NewStringObj(p->pDb->zNull, -1); } } - return dbTextToObj((char *)sqlite3_column_text(pStmt, iCol)); + return Tcl_NewStringObj((char*)sqlite3_column_text(pStmt, iCol), -1); } /* @@ -2452,7 +2428,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ const char *zDb = "main"; const char *zTable; const char *zColumn; - sqlite_int64 iRow; + Tcl_WideInt iRow; /* Check for the -readonly option */ if( objc>3 && strcmp(Tcl_GetString(objv[2]), "-readonly")==0 ){ @@ -2518,7 +2494,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ pDb->zNull = 0; } } - Tcl_SetObjResult(interp, dbTextToObj(pDb->zNull)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(pDb->zNull, -1)); break; } diff --git a/src/test1.c b/src/test1.c index 3d2fb0203..e7a224221 100644 --- a/src/test1.c +++ b/src/test1.c @@ -3067,7 +3067,7 @@ static int test_bind_int64( ){ sqlite3_stmt *pStmt; int idx; - i64 value; + Tcl_WideInt value; int rc; if( objc!=4 ){ @@ -4703,7 +4703,7 @@ static int test_soft_heap_limit( Tcl_Obj *CONST objv[] ){ sqlite3_int64 amt; - sqlite3_int64 N = -1; + Tcl_WideInt N = -1; if( objc!=1 && objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "?N?"); return TCL_ERROR; @@ -5096,7 +5096,7 @@ static int file_control_sizehint_test( int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ - sqlite3_int64 nSize; /* Hinted size */ + Tcl_WideInt nSize; /* Hinted size */ char *zDb; /* Db name ("main", "temp" etc.) */ sqlite3 *db; /* Database handle */ int rc; /* file_control() return code */ diff --git a/src/test_intarray.c b/src/test_intarray.c index 8651d01cf..6ea775617 100644 --- a/src/test_intarray.c +++ b/src/test_intarray.c @@ -346,8 +346,9 @@ static int test_intarray_bind( return TCL_ERROR; } for(i=0; i<n; i++){ - a[i] = 0; - Tcl_GetWideIntFromObj(0, objv[i+2], &a[i]); + Tcl_WideInt x = 0; + Tcl_GetWideIntFromObj(0, objv[i+2], &x); + a[i] = x; } rc = sqlite3_intarray_bind(pArray, n, a, sqlite3_free); if( rc!=SQLITE_OK ){ diff --git a/src/test_quota.c b/src/test_quota.c index 2ce46acdc..e1ec12d37 100644 --- a/src/test_quota.c +++ b/src/test_quota.c @@ -1073,7 +1073,7 @@ size_t sqlite3_quota_fwrite( /* If the write was incomplete, adjust the file size and group size ** downward */ if( rc<nmemb && pFile ){ - size_t nWritten = rc>=0 ? rc : 0; + size_t nWritten = rc; sqlite3_int64 iNewEnd = iOfst + size*nWritten; if( iNewEnd<iEnd ) iNewEnd = iEnd; quotaEnter(); @@ -1354,8 +1354,10 @@ static void tclQuotaCallback( rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); if( rc==TCL_OK ){ + Tcl_WideInt x; Tcl_Obj *pLimit = Tcl_ObjGetVar2(p->interp, pVarname, 0, 0); - rc = Tcl_GetWideIntFromObj(p->interp, pLimit, piLimit); + rc = Tcl_GetWideIntFromObj(p->interp, pLimit, &x); + *piLimit = x; Tcl_UnsetVar(p->interp, Tcl_GetString(pVarname), 0); } @@ -1437,7 +1439,7 @@ static int test_quota_set( Tcl_Obj *CONST objv[] ){ const char *zPattern; /* File pattern to configure */ - sqlite3_int64 iLimit; /* Initial quota in bytes */ + Tcl_WideInt iLimit; /* Initial quota in bytes */ Tcl_Obj *pScript; /* Tcl script to invoke to increase quota */ int rc; /* Value returned by quota_set() */ TclQuotaCallback *p; /* Callback object */ @@ -1613,7 +1615,6 @@ static int test_quota_fread( return TCL_ERROR; } got = sqlite3_quota_fread(zBuf, sz, nElem, p); - if( got<0 ) got = 0; zBuf[got*sz] = 0; Tcl_SetResult(interp, zBuf, TCL_VOLATILE); sqlite3_free(zBuf); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 731375ecc..c10e26a7f 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1771,7 +1771,9 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ if( sqlite3BtreeIsInTrans(pBt) ){ needXcommit = 1; if( i!=1 ) nTrans++; + sqlite3BtreeEnter(pBt); rc = sqlite3PagerExclusiveLock(sqlite3BtreePager(pBt)); + sqlite3BtreeLeave(pBt); } } if( rc!=SQLITE_OK ){ diff --git a/src/vtab.c b/src/vtab.c index 29f097da4..39fbacacb 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -264,7 +264,7 @@ void sqlite3VtabClear(sqlite3 *db, Table *p){ if( p->azModuleArg ){ int i; for(i=0; i<p->nModuleArg; i++){ - sqlite3DbFree(db, p->azModuleArg[i]); + if( i!=1 ) sqlite3DbFree(db, p->azModuleArg[i]); } sqlite3DbFree(db, p->azModuleArg); } @@ -324,7 +324,7 @@ void sqlite3VtabBeginParse( pTable->tabFlags |= TF_Virtual; pTable->nModuleArg = 0; addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName)); - addModuleArgument(db, pTable, sqlite3DbStrDup(db, db->aDb[iDb].zName)); + addModuleArgument(db, pTable, 0); addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName)); pParse->sNameToken.n = (int)(&pModuleName->z[pModuleName->n] - pName1->z); @@ -481,6 +481,7 @@ static int vtabCallConstructor( int nArg = pTab->nModuleArg; char *zErr = 0; char *zModuleName = sqlite3MPrintf(db, "%s", pTab->zName); + int iDb; if( !zModuleName ){ return SQLITE_NOMEM; @@ -494,6 +495,10 @@ static int vtabCallConstructor( pVTable->db = db; pVTable->pMod = pMod; + assert( pTab->azModuleArg[1]==0 ); + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + pTab->azModuleArg[1] = db->aDb[iDb].zName; + /* Invoke the virtual table constructor */ assert( &db->pVtabCtx ); assert( xConstruct ); @@ -504,6 +509,7 @@ static int vtabCallConstructor( rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); db->pVtabCtx = pPriorCtx; if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; + pTab->azModuleArg[1] = 0; if( SQLITE_OK!=rc ){ if( zErr==0 ){ @@ -2828,7 +2828,7 @@ int sqlite3WalFrames( */ if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){ if( pWal->padToSectorBoundary ){ - int sectorSize = sqlite3OsSectorSize(pWal->pWalFd); + int sectorSize = sqlite3SectorSize(pWal->pWalFd); w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize; while( iOffset<w.iSyncPoint ){ rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset); diff --git a/src/where.c b/src/where.c index 7f386289c..7a994943c 100644 --- a/src/where.c +++ b/src/where.c @@ -257,10 +257,11 @@ struct WhereCost { #define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */ #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */ #define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */ -#define WHERE_IDX_ONLY 0x00800000 /* Use index only - omit table */ -#define WHERE_ORDERBY 0x01000000 /* Output will appear in correct order */ -#define WHERE_REVERSE 0x02000000 /* Scan in reverse order */ -#define WHERE_UNIQUE 0x04000000 /* Selects no more than one row */ +#define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */ +#define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */ +#define WHERE_REVERSE 0x01000000 /* Scan in reverse order */ +#define WHERE_UNIQUE 0x02000000 /* Selects no more than one row */ +#define WHERE_ALL_UNIQUE 0x04000000 /* This and all prior have one row */ #define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */ #define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */ #define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */ @@ -290,6 +291,17 @@ struct WhereBestIdx { }; /* +** Return TRUE if the probe cost is less than the baseline cost +*/ +static int compareCost(const WhereCost *pProbe, const WhereCost *pBaseline){ + if( pProbe->rCost<pBaseline->rCost ) return 1; + if( pProbe->rCost>pBaseline->rCost ) return 0; + if( pProbe->plan.nOBSat>pBaseline->plan.nOBSat ) return 1; + if( pProbe->plan.nRow<pBaseline->plan.nRow ) return 1; + return 0; +} + +/* ** Initialize a preallocated WhereClause structure. */ static void whereClauseInit( @@ -1431,21 +1443,6 @@ static void exprAnalyze( } /* -** Return TRUE if the given index is UNIQUE and all columns past the -** first nSkip columns are NOT NULL. -*/ -static int indexIsUniqueNotNull(Index *pIdx, int nSkip){ - Table *pTab = pIdx->pTable; - int i; - if( pIdx->onError==OE_None ) return 0; - for(i=nSkip; i<pIdx->nColumn; i++){ - int j = pIdx->aiColumn[i]; - if( j>=0 && pTab->aCol[j].notNull==0 ) return 0; - } - return 1; -} - -/* ** This function searches the expression list passed as the second argument ** for an expression of type TK_COLUMN that refers to the same column and ** uses the same collation sequence as the iCol'th column of index pIdx. @@ -1504,7 +1501,8 @@ static int isDistinctIndex( Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */ int i; /* Iterator variable */ - if( pIdx->zName==0 || pDistinct==0 || pDistinct->nExpr>=BMS ) return 0; + assert( pDistinct!=0 ); + if( pIdx->zName==0 || pDistinct->nExpr>=BMS ) return 0; testcase( pDistinct->nExpr==BMS-1 ); /* Loop through all the expressions in the distinct list. If any of them @@ -1607,166 +1605,6 @@ static int isDistinctRedundant( } /* -** This routine decides if pIdx can be used to satisfy the ORDER BY -** clause, either in whole or in part. The return value is the -** cumulative number of terms in the ORDER BY clause that are satisfied -** by the index pIdx and other indices in outer loops. -** -** The table being queried has a cursor number of "base". pIdx is the -** index that is postulated for use to access the table. -** -** nEqCol is the number of columns of pIdx that are used as equality -** constraints and where the other side of the == is an ordered column -** or constant. An "order column" in the previous sentence means a column -** in table from an outer loop whose values will always appear in the -** correct order due to othre index, or because the outer loop generates -** a unique result. Any of the first nEqCol columns of pIdx may be missing -** from the ORDER BY clause and the match can still be a success. -** -** The *pbRev value is set to 0 order 1 depending on whether or not -** pIdx should be run in the forward order or in reverse order. -*/ -static int isSortingIndex( - WhereBestIdx *p, /* Best index search context */ - Index *pIdx, /* The index we are testing */ - int base, /* Cursor number for the table to be sorted */ - int nEqCol, /* Number of index columns with ordered == constraints */ - int wsFlags, /* Index usages flags */ - int bOuterRev, /* True if outer loops scan in reverse order */ - int *pbRev /* Set to 1 for reverse-order scan of pIdx */ -){ - int i; /* Number of pIdx terms used */ - int j; /* Number of ORDER BY terms satisfied */ - int sortOrder = 0; /* XOR of index and ORDER BY sort direction */ - int nTerm; /* Number of ORDER BY terms */ - struct ExprList_item *pTerm; /* A term of the ORDER BY clause */ - ExprList *pOrderBy; /* The ORDER BY clause */ - Parse *pParse = p->pParse; /* Parser context */ - sqlite3 *db = pParse->db; /* Database connection */ - int nPriorSat; /* ORDER BY terms satisfied by outer loops */ - int seenRowid = 0; /* True if an ORDER BY rowid term is seen */ - int nEqOneRow; /* Idx columns that ref unique values */ - - if( p->i==0 ){ - nPriorSat = 0; - nEqOneRow = nEqCol; - }else{ - if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return 0; - nPriorSat = p->aLevel[p->i-1].plan.nOBSat; - sortOrder = bOuterRev; - nEqOneRow = 0; - } - if( p->i>0 && nEqCol==0 /*&& !allOuterLoopsUnique(p)*/ ) return nPriorSat; - pOrderBy = p->pOrderBy; - if( !pOrderBy ) return nPriorSat; - if( wsFlags & WHERE_COLUMN_IN ) return nPriorSat; - if( pIdx->bUnordered ) return nPriorSat; - nTerm = pOrderBy->nExpr; - assert( nTerm>0 ); - - /* Argument pIdx must either point to a 'real' named index structure, - ** or an index structure allocated on the stack by bestBtreeIndex() to - ** represent the rowid index that is part of every table. */ - assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) ); - - /* Match terms of the ORDER BY clause against columns of - ** the index. - ** - ** Note that indices have pIdx->nColumn regular columns plus - ** one additional column containing the rowid. The rowid column - ** of the index is also allowed to match against the ORDER BY - ** clause. - */ - for(i=0,j=nPriorSat,pTerm=&pOrderBy->a[j]; j<nTerm && i<=pIdx->nColumn; i++){ - Expr *pExpr; /* The expression of the ORDER BY pTerm */ - CollSeq *pColl; /* The collating sequence of pExpr */ - int termSortOrder; /* Sort order for this term */ - int iColumn; /* The i-th column of the index. -1 for rowid */ - int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */ - const char *zColl; /* Name of the collating sequence for i-th index term */ - - pExpr = pTerm->pExpr; - if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ){ - /* Can not use an index sort on anything that is not a column in the - ** left-most table of the FROM clause */ - break; - } - pColl = sqlite3ExprCollSeq(pParse, pExpr); - if( !pColl ){ - pColl = db->pDfltColl; - } - if( pIdx->zName && i<pIdx->nColumn ){ - iColumn = pIdx->aiColumn[i]; - if( iColumn==pIdx->pTable->iPKey ){ - iColumn = -1; - } - iSortOrder = pIdx->aSortOrder[i]; - zColl = pIdx->azColl[i]; - }else{ - iColumn = -1; - iSortOrder = 0; - zColl = pColl->zName; - } - if( pExpr->iColumn!=iColumn || sqlite3StrICmp(pColl->zName, zColl) ){ - /* Term j of the ORDER BY clause does not match column i of the index */ - if( i<nEqCol ){ - /* If an index column that is constrained by == fails to match an - ** ORDER BY term, that is OK. Just ignore that column of the index - */ - continue; - }else if( i==pIdx->nColumn ){ - /* Index column i is the rowid. All other terms match. */ - break; - }else{ - /* If an index column fails to match and is not constrained by == - ** then the index cannot satisfy the ORDER BY constraint. - */ - return nPriorSat; - } - } - assert( pIdx->aSortOrder!=0 || iColumn==-1 ); - assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 ); - assert( iSortOrder==0 || iSortOrder==1 ); - termSortOrder = iSortOrder ^ pTerm->sortOrder; - if( i>nEqOneRow ){ - if( termSortOrder!=sortOrder ){ - /* Indices can only be used if all ORDER BY terms past the - ** equality constraints are all either DESC or ASC. */ - break; - } - }else{ - sortOrder = termSortOrder; - } - j++; - pTerm++; - if( iColumn<0 ){ - seenRowid = 1; - break; - } - } - *pbRev = sortOrder; - - /* If there was an "ORDER BY rowid" term that matched, or it is only - ** possible for a single row from this table to match, then skip over - ** any additional ORDER BY terms dealing with this table. - */ - if( seenRowid || - ( (wsFlags & WHERE_COLUMN_NULL)==0 - && i>=pIdx->nColumn - && indexIsUniqueNotNull(pIdx, nEqCol) - ) - ){ - /* Advance j over additional ORDER BY terms associated with base */ - WhereMaskSet *pMS = p->pWC->pMaskSet; - Bitmask m = ~getMask(pMS, base); - while( j<nTerm && (exprTableUsage(pMS, pOrderBy->a[j].pExpr)&m)==0 ){ - j++; - } - } - return j; -} - -/* ** Prepare a crude estimate of the logarithm of the input value. ** The results need not be exact. This is only used for estimating ** the total cost of performing operations with O(logN) or O(NlogN) @@ -1919,6 +1757,7 @@ static void bestOrClauseIndex(WhereBestIdx *p){ p->cost.rCost = rTotal; p->cost.used = used; p->cost.plan.nRow = nRow; + p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0; p->cost.plan.wsFlags = flags; p->cost.plan.u.pTerm = pTerm; } @@ -2461,7 +2300,10 @@ static void bestVirtualIndex(WhereBestIdx *p){ } p->cost.plan.u.pVtabIdx = pIdxInfo; if( pIdxInfo->orderByConsumed ){ - p->cost.plan.wsFlags |= WHERE_ORDERBY; + p->cost.plan.wsFlags |= WHERE_ORDERED; + p->cost.plan.nOBSat = nOrderBy; + }else{ + p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0; } p->cost.plan.nEq = 0; pIdxInfo->nOrderBy = nOrderBy; @@ -2557,10 +2399,8 @@ static int whereKeyStats( pColl = db->pDfltColl; assert( pColl->enc==SQLITE_UTF8 ); }else{ - pColl = sqlite3GetCollSeq(db, SQLITE_UTF8, 0, *pIdx->azColl); + pColl = sqlite3GetCollSeq(pParse, SQLITE_UTF8, 0, *pIdx->azColl); if( pColl==0 ){ - sqlite3ErrorMsg(pParse, "no such collation sequence: %s", - *pIdx->azColl); return SQLITE_ERROR; } z = (const u8 *)sqlite3ValueText(pVal, pColl->enc); @@ -2870,30 +2710,40 @@ static int whereInScanEst( /* ** Check to see if column iCol of the table with cursor iTab will appear -** in sorted order according to the current query plan. Return true if -** it will and false if not. +** in sorted order according to the current query plan. ** -** If *pbRev is initially 2 (meaning "unknown") then set *pbRev to the -** sort order of iTab.iCol. If *pbRev is 0 or 1 but does not match -** the sort order of iTab.iCol, then consider the column to be unordered. +** Return values: +** +** 0 iCol is not ordered +** 1 iCol has only a single value +** 2 iCol is in ASC order +** 3 iCol is in DESC order */ -static int isOrderedColumn(WhereBestIdx *p, int iTab, int iCol, int *pbRev){ +static int isOrderedColumn( + WhereBestIdx *p, + int iTab, + int iCol +){ int i, j; WhereLevel *pLevel = &p->aLevel[p->i-1]; Index *pIdx; u8 sortOrder; for(i=p->i-1; i>=0; i--, pLevel--){ if( pLevel->iTabCur!=iTab ) continue; - if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ - pIdx = pLevel->plan.u.pIdx; + if( (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ + return 1; + } + assert( (pLevel->plan.wsFlags & WHERE_ORDERED)!=0 ); + if( (pIdx = pLevel->plan.u.pIdx)!=0 ){ if( iCol<0 ){ sortOrder = 0; testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ); }else{ - for(j=0; j<pIdx->nColumn; j++){ + int n = pIdx->nColumn; + for(j=0; j<n; j++){ if( iCol==pIdx->aiColumn[j] ) break; } - if( j>=pIdx->nColumn ) return 0; + if( j>=n ) return 0; sortOrder = pIdx->aSortOrder[j]; testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ); } @@ -2907,41 +2757,222 @@ static int isOrderedColumn(WhereBestIdx *p, int iTab, int iCol, int *pbRev){ testcase( sortOrder==1 ); sortOrder = 1 - sortOrder; } - if( *pbRev==2 ){ - *pbRev = sortOrder; - return 1; - } - return (*pbRev==sortOrder); + return sortOrder+2; } return 0; } /* -** pTerm is an == constraint. Check to see if the other side of -** the == is a constant or a value that is guaranteed to be ordered -** by outer loops. Return 1 if pTerm is ordered, and 0 if not. +** This routine decides if pIdx can be used to satisfy the ORDER BY +** clause, either in whole or in part. The return value is the +** cumulative number of terms in the ORDER BY clause that are satisfied +** by the index pIdx and other indices in outer loops. +** +** The table being queried has a cursor number of "base". pIdx is the +** index that is postulated for use to access the table. +** +** The *pbRev value is set to 0 order 1 depending on whether or not +** pIdx should be run in the forward order or in reverse order. */ -static int isOrderedTerm(WhereBestIdx *p, WhereTerm *pTerm, int *pbRev){ - Expr *pExpr = pTerm->pExpr; - assert( pExpr->op==TK_EQ ); - assert( pExpr->pLeft!=0 && pExpr->pLeft->op==TK_COLUMN ); - assert( pExpr->pRight!=0 ); +static int isSortingIndex( + WhereBestIdx *p, /* Best index search context */ + Index *pIdx, /* The index we are testing */ + int base, /* Cursor number for the table to be sorted */ + int *pbRev /* Set to 1 for reverse-order scan of pIdx */ +){ + int i; /* Number of pIdx terms used */ + int j; /* Number of ORDER BY terms satisfied */ + int sortOrder = 2; /* 0: forward. 1: backward. 2: unknown */ + int nTerm; /* Number of ORDER BY terms */ + struct ExprList_item *pOBItem;/* A term of the ORDER BY clause */ + Table *pTab = pIdx->pTable; /* Table that owns index pIdx */ + ExprList *pOrderBy; /* The ORDER BY clause */ + Parse *pParse = p->pParse; /* Parser context */ + sqlite3 *db = pParse->db; /* Database connection */ + int nPriorSat; /* ORDER BY terms satisfied by outer loops */ + int seenRowid = 0; /* True if an ORDER BY rowid term is seen */ + int uniqueNotNull; /* pIdx is UNIQUE with all terms are NOT NULL */ + if( p->i==0 ){ - return 1; /* All == are ordered in the outer loop */ + nPriorSat = 0; + }else{ + nPriorSat = p->aLevel[p->i-1].plan.nOBSat; + if( (p->aLevel[p->i-1].plan.wsFlags & WHERE_ORDERED)==0 ){ + /* This loop cannot be ordered unless the next outer loop is + ** also ordered */ + return nPriorSat; + } + if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ){ + /* Only look at the outer-most loop if the OrderByIdxJoin + ** optimization is disabled */ + return nPriorSat; + } } - if( pTerm->prereqRight==0 ){ - return 1; /* RHS of the == is a constant */ + pOrderBy = p->pOrderBy; + assert( pOrderBy!=0 ); + if( pIdx->bUnordered ){ + /* Hash indices (indicated by the "unordered" tag on sqlite_stat1) cannot + ** be used for sorting */ + return nPriorSat; } - if( pExpr->pRight->op==TK_COLUMN - && isOrderedColumn(p, pExpr->pRight->iTable, pExpr->pRight->iColumn, pbRev) - ){ - return 1; + nTerm = pOrderBy->nExpr; + uniqueNotNull = pIdx->onError!=OE_None; + assert( nTerm>0 ); + + /* Argument pIdx must either point to a 'real' named index structure, + ** or an index structure allocated on the stack by bestBtreeIndex() to + ** represent the rowid index that is part of every table. */ + assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) ); + + /* Match terms of the ORDER BY clause against columns of + ** the index. + ** + ** Note that indices have pIdx->nColumn regular columns plus + ** one additional column containing the rowid. The rowid column + ** of the index is also allowed to match against the ORDER BY + ** clause. + */ + j = nPriorSat; + for(i=0,pOBItem=&pOrderBy->a[j]; j<nTerm && i<=pIdx->nColumn; i++){ + Expr *pOBExpr; /* The expression of the ORDER BY pOBItem */ + CollSeq *pColl; /* The collating sequence of pOBExpr */ + int termSortOrder; /* Sort order for this term */ + int iColumn; /* The i-th column of the index. -1 for rowid */ + int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */ + int isEq; /* Subject to an == or IS NULL constraint */ + int isMatch; /* ORDER BY term matches the index term */ + const char *zColl; /* Name of collating sequence for i-th index term */ + WhereTerm *pConstraint; /* A constraint in the WHERE clause */ + + /* If the next term of the ORDER BY clause refers to anything other than + ** a column in the "base" table, then this index will not be of any + ** further use in handling the ORDER BY. */ + pOBExpr = pOBItem->pExpr; + if( pOBExpr->op!=TK_COLUMN || pOBExpr->iTable!=base ){ + break; + } + + /* Find column number and collating sequence for the next entry + ** in the index */ + if( pIdx->zName && i<pIdx->nColumn ){ + iColumn = pIdx->aiColumn[i]; + if( iColumn==pIdx->pTable->iPKey ){ + iColumn = -1; + } + iSortOrder = pIdx->aSortOrder[i]; + zColl = pIdx->azColl[i]; + assert( zColl!=0 ); + }else{ + iColumn = -1; + iSortOrder = 0; + zColl = 0; + } + + /* Check to see if the column number and collating sequence of the + ** index match the column number and collating sequence of the ORDER BY + ** clause entry. Set isMatch to 1 if they both match. */ + if( pOBExpr->iColumn==iColumn ){ + if( zColl ){ + pColl = sqlite3ExprCollSeq(pParse, pOBExpr); + if( !pColl ) pColl = db->pDfltColl; + isMatch = sqlite3StrICmp(pColl->zName, zColl)==0; + }else{ + isMatch = 1; + } + }else{ + isMatch = 0; + } + + /* termSortOrder is 0 or 1 for whether or not the access loop should + ** run forward or backwards (respectively) in order to satisfy this + ** term of the ORDER BY clause. */ + assert( pOBItem->sortOrder==0 || pOBItem->sortOrder==1 ); + assert( iSortOrder==0 || iSortOrder==1 ); + termSortOrder = iSortOrder ^ pOBItem->sortOrder; + + /* If X is the column in the index and ORDER BY clause, check to see + ** if there are any X= or X IS NULL constraints in the WHERE clause. */ + pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, + WO_EQ|WO_ISNULL|WO_IN, pIdx); + if( pConstraint==0 ){ + isEq = 0; + }else if( pConstraint->eOperator==WO_IN ){ + /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY + ** because we do not know in what order the values on the RHS of the IN + ** operator will occur. */ + break; + }else if( pConstraint->eOperator==WO_ISNULL ){ + uniqueNotNull = 0; + isEq = 1; /* "X IS NULL" means X has only a single value */ + }else if( pConstraint->prereqRight==0 ){ + isEq = 1; /* Constraint "X=constant" means X has only a single value */ + }else{ + Expr *pRight = pConstraint->pExpr->pRight; + if( pRight->op==TK_COLUMN ){ + WHERETRACE((" .. isOrderedColumn(tab=%d,col=%d)", + pRight->iTable, pRight->iColumn)); + isEq = isOrderedColumn(p, pRight->iTable, pRight->iColumn); + WHERETRACE((" -> isEq=%d\n", isEq)); + + /* If the constraint is of the form X=Y where Y is an ordered value + ** in an outer loop, then make sure the sort order of Y matches the + ** sort order required for X. */ + if( isMatch && isEq>=2 && isEq!=pOBItem->sortOrder+2 ){ + testcase( isEq==2 ); + testcase( isEq==3 ); + break; + } + }else{ + isEq = 0; /* "X=expr" places no ordering constraints on X */ + } + } + if( !isMatch ){ + if( isEq==0 ){ + break; + }else{ + continue; + } + }else if( isEq!=1 ){ + if( sortOrder==2 ){ + sortOrder = termSortOrder; + }else if( termSortOrder!=sortOrder ){ + break; + } + } + j++; + pOBItem++; + if( iColumn<0 ){ + seenRowid = 1; + break; + }else if( pTab->aCol[iColumn].notNull==0 && isEq!=1 ){ + testcase( isEq==0 ); + testcase( isEq==2 ); + testcase( isEq==3 ); + uniqueNotNull = 0; + } } - /* If we cannot prove that the constraint is ordered, assume it is not */ - return 0; -} + /* If we have not found at least one ORDER BY term that matches the + ** index, then show no progress. */ + if( pOBItem==&pOrderBy->a[nPriorSat] ) return nPriorSat; + /* Return the necessary scan order back to the caller */ + *pbRev = sortOrder & 1; + + /* If there was an "ORDER BY rowid" term that matched, or it is only + ** possible for a single row from this table to match, then skip over + ** any additional ORDER BY terms dealing with this table. + */ + if( seenRowid || (uniqueNotNull && i>=pIdx->nColumn) ){ + /* Advance j over additional ORDER BY terms associated with base */ + WhereMaskSet *pMS = p->pWC->pMaskSet; + Bitmask m = ~getMask(pMS, base); + while( j<nTerm && (exprTableUsage(pMS, pOrderBy->a[j].pExpr)&m)==0 ){ + j++; + } + } + return j; +} /* ** Find the best query plan for accessing a particular table. Write the @@ -3036,18 +3067,14 @@ static void bestBtreeIndex(WhereBestIdx *p){ */ for(; pProbe; pIdx=pProbe=pProbe->pNext){ const tRowcnt * const aiRowEst = pProbe->aiRowEst; - double cost; /* Cost of using pProbe */ - double nRow; /* Estimated number of rows in result set */ + WhereCost pc; /* Cost of using pProbe */ double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */ - int bRev = 2; /* 0=forward scan. 1=reverse. 2=undecided */ - int wsFlags = 0; - Bitmask used = 0; /* The following variables are populated based on the properties of ** index being evaluated. They are then used to determine the expected ** cost and number of rows returned. ** - ** nEq: + ** pc.plan.nEq: ** Number of equality terms that can be implemented using the index. ** In other words, the number of initial fields in the index that ** are used in == or IN or NOT NULL constraints of the WHERE clause. @@ -3072,10 +3099,6 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** the sub-select is assumed to return 25 rows for the purposes of ** determining nInMul. ** - ** nOrdered: - ** The number of equality terms that are constrainted by outer loop - ** variables that are well-ordered. - ** ** bInEst: ** Set to true if there was at least one "x IN (SELECT ...)" term used ** in determining the value of nInMul. Note that the RHS of the @@ -3094,7 +3117,7 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** external sort (i.e. scanning the index being evaluated will not ** correctly order records). ** - ** bDistinct: + ** bDist: ** Boolean. True if there is a DISTINCT clause that will require an ** external btree. ** @@ -3113,8 +3136,6 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** SELECT a, b FROM tbl WHERE a = 1; ** SELECT a, b, c FROM tbl WHERE a = 1; */ - int nEq; /* Number of == or IN terms matching index */ - int nOrdered; /* Number of ordered terms matching index */ int bInEst = 0; /* True if "x IN (SELECT...)" seen */ int nInMul = 1; /* Number of distinct equalities to lookup */ double rangeDiv = (double)1; /* Estimated reduction in search space */ @@ -3122,27 +3143,39 @@ static void bestBtreeIndex(WhereBestIdx *p){ int bSort; /* True if external sort required */ int bDist; /* True if index cannot help with DISTINCT */ int bLookup = 0; /* True if not a covering index */ - int nOBSat = 0; /* Number of ORDER BY terms satisfied */ + int nPriorSat; /* ORDER BY terms satisfied by outer loops */ int nOrderBy; /* Number of ORDER BY terms */ WhereTerm *pTerm; /* A single term of the WHERE clause */ #ifdef SQLITE_ENABLE_STAT3 WhereTerm *pFirstTerm = 0; /* First term matching the index */ #endif + WHERETRACE(( + " %s(%s):\n", + pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk") + )); + memset(&pc, 0, sizeof(pc)); nOrderBy = p->pOrderBy ? p->pOrderBy->nExpr : 0; - bSort = nOrderBy>0 && (p->i==0 || p->aLevel[p->i-1].plan.nOBSat<nOrderBy); - bDist = p->i==0 && p->pDistinct!=0; + if( p->i ){ + nPriorSat = pc.plan.nOBSat = p->aLevel[p->i-1].plan.nOBSat; + bSort = nPriorSat<nOrderBy; + bDist = 0; + }else{ + nPriorSat = pc.plan.nOBSat = 0; + bSort = nOrderBy>0; + bDist = p->pDistinct!=0; + } - /* Determine the values of nEq and nInMul */ - for(nEq=nOrdered=0; nEq<pProbe->nColumn; nEq++){ - int j = pProbe->aiColumn[nEq]; + /* Determine the values of pc.plan.nEq and nInMul */ + for(pc.plan.nEq=0; pc.plan.nEq<pProbe->nColumn; pc.plan.nEq++){ + int j = pProbe->aiColumn[pc.plan.nEq]; pTerm = findTerm(pWC, iCur, j, p->notReady, eqTermMask, pIdx); if( pTerm==0 ) break; - wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); + pc.plan.wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); testcase( pTerm->pWC!=pWC ); if( pTerm->eOperator & WO_IN ){ Expr *pExpr = pTerm->pExpr; - wsFlags |= WHERE_COLUMN_IN; + pc.plan.wsFlags |= WHERE_COLUMN_IN; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */ nInMul *= 25; @@ -3152,15 +3185,12 @@ static void bestBtreeIndex(WhereBestIdx *p){ nInMul *= pExpr->x.pList->nExpr; } }else if( pTerm->eOperator & WO_ISNULL ){ - wsFlags |= WHERE_COLUMN_NULL; - if( nEq==nOrdered ) nOrdered++; - }else if( bSort && nEq==nOrdered && isOrderedTerm(p, pTerm, &bRev) ){ - nOrdered++; + pc.plan.wsFlags |= WHERE_COLUMN_NULL; } #ifdef SQLITE_ENABLE_STAT3 - if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm; + if( pc.plan.nEq==0 && pProbe->aSample ) pFirstTerm = pTerm; #endif - used |= pTerm->prereqRight; + pc.used |= pTerm->prereqRight; } /* If the index being considered is UNIQUE, and there is an equality @@ -3169,72 +3199,79 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** indicate this to the caller. ** ** Otherwise, if the search may find more than one row, test to see if - ** there is a range constraint on indexed column (nEq+1) that can be + ** there is a range constraint on indexed column (pc.plan.nEq+1) that can be ** optimized using the index. */ - if( nEq==pProbe->nColumn && pProbe->onError!=OE_None ){ - testcase( wsFlags & WHERE_COLUMN_IN ); - testcase( wsFlags & WHERE_COLUMN_NULL ); - if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ - wsFlags |= WHERE_UNIQUE; + if( pc.plan.nEq==pProbe->nColumn && pProbe->onError!=OE_None ){ + testcase( pc.plan.wsFlags & WHERE_COLUMN_IN ); + testcase( pc.plan.wsFlags & WHERE_COLUMN_NULL ); + if( (pc.plan.wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ + pc.plan.wsFlags |= WHERE_UNIQUE; + if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ + pc.plan.wsFlags |= WHERE_ALL_UNIQUE; + } } }else if( pProbe->bUnordered==0 ){ - int j = (nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[nEq]); + int j; + j = (pc.plan.nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[pc.plan.nEq]); if( findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ WhereTerm *pTop, *pBtm; pTop = findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE, pIdx); pBtm = findTerm(pWC, iCur, j, p->notReady, WO_GT|WO_GE, pIdx); - whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &rangeDiv); + whereRangeScanEst(pParse, pProbe, pc.plan.nEq, pBtm, pTop, &rangeDiv); if( pTop ){ nBound = 1; - wsFlags |= WHERE_TOP_LIMIT; - used |= pTop->prereqRight; + pc.plan.wsFlags |= WHERE_TOP_LIMIT; + pc.used |= pTop->prereqRight; testcase( pTop->pWC!=pWC ); } if( pBtm ){ nBound++; - wsFlags |= WHERE_BTM_LIMIT; - used |= pBtm->prereqRight; + pc.plan.wsFlags |= WHERE_BTM_LIMIT; + pc.used |= pBtm->prereqRight; testcase( pBtm->pWC!=pWC ); } - wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); + pc.plan.wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); } } /* If there is an ORDER BY clause and the index being considered will ** naturally scan rows in the required order, set the appropriate flags - ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index - ** will scan rows in a different order, set the bSort variable. */ - assert( bRev>=0 && bRev<=2 ); - if( bSort ){ - testcase( bRev==0 ); - testcase( bRev==1 ); - testcase( bRev==2 ); - nOBSat = isSortingIndex(p, pProbe, iCur, nOrdered, - wsFlags, bRev&1, &bRev); - if( nOrderBy==nOBSat ){ + ** in pc.plan.wsFlags. Otherwise, if there is an ORDER BY clause but + ** the index will scan rows in a different order, set the bSort + ** variable. */ + if( bSort && (pSrc->jointype & JT_LEFT)==0 ){ + int bRev = 2; + WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat)); + pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev); + WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n", + bRev, pc.plan.nOBSat)); + if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_UNIQUE)!=0 ){ + pc.plan.wsFlags |= WHERE_ORDERED; + } + if( nOrderBy==pc.plan.nOBSat ){ bSort = 0; - wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY; + pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE; } - if( bRev & 1 ) wsFlags |= WHERE_REVERSE; + if( bRev & 1 ) pc.plan.wsFlags |= WHERE_REVERSE; } /* If there is a DISTINCT qualifier and this index will scan rows in ** order of the DISTINCT expressions, clear bDist and set the appropriate - ** flags in wsFlags. */ + ** flags in pc.plan.wsFlags. */ if( bDist - && isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, nEq) - && (wsFlags & WHERE_COLUMN_IN)==0 + && isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, pc.plan.nEq) + && (pc.plan.wsFlags & WHERE_COLUMN_IN)==0 ){ bDist = 0; - wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT; + pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT; } /* If currently calculating the cost of using an index (not the IPK ** index), determine if all required column data may be obtained without ** using the main table (i.e. if the index is a covering ** index for this query). If it is, set the WHERE_IDX_ONLY flag in - ** wsFlags. Otherwise, set the bLookup variable to true. */ + ** pc.plan.wsFlags. Otherwise, set the bLookup variable to true. */ if( pIdx ){ Bitmask m = pSrc->colUsed; int j; @@ -3245,7 +3282,7 @@ static void bestBtreeIndex(WhereBestIdx *p){ } } if( m==0 ){ - wsFlags |= WHERE_IDX_ONLY; + pc.plan.wsFlags |= WHERE_IDX_ONLY; }else{ bLookup = 1; } @@ -3255,10 +3292,10 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** Estimate the number of rows of output. For an "x IN (SELECT...)" ** constraint, do not let the estimate exceed half the rows in the table. */ - nRow = (double)(aiRowEst[nEq] * nInMul); - if( bInEst && nRow*2>aiRowEst[0] ){ - nRow = aiRowEst[0]/2; - nInMul = (int)(nRow / aiRowEst[nEq]); + pc.plan.nRow = (double)(aiRowEst[pc.plan.nEq] * nInMul); + if( bInEst && pc.plan.nRow*2>aiRowEst[0] ){ + pc.plan.nRow = aiRowEst[0]/2; + nInMul = (int)(pc.plan.nRow / aiRowEst[pc.plan.nEq]); } #ifdef SQLITE_ENABLE_STAT3 @@ -3268,15 +3305,18 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** to get a better estimate on the number of rows based on ** VALUE and how common that value is according to the histogram. */ - if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 && aiRowEst[1]>1 ){ + if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 + && pFirstTerm!=0 && aiRowEst[1]>1 ){ assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ testcase( pFirstTerm->eOperator==WO_EQ ); testcase( pFirstTerm->eOperator==WO_ISNULL ); - whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow); + whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, + &pc.plan.nRow); }else if( bInEst==0 ){ assert( pFirstTerm->eOperator==WO_IN ); - whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow); + whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, + &pc.plan.nRow); } } #endif /* SQLITE_ENABLE_STAT3 */ @@ -3284,8 +3324,8 @@ static void bestBtreeIndex(WhereBestIdx *p){ /* Adjust the number of output rows and downward to reflect rows ** that are excluded by range constraints. */ - nRow = nRow/rangeDiv; - if( nRow<1 ) nRow = 1; + pc.plan.nRow = pc.plan.nRow/rangeDiv; + if( pc.plan.nRow<1 ) pc.plan.nRow = 1; /* Experiments run on real SQLite databases show that the time needed ** to do a binary search to locate a row in a table or index is roughly @@ -3300,7 +3340,7 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** So this computation assumes table records are about twice as big ** as index records */ - if( (wsFlags&~WHERE_REVERSE)==WHERE_IDX_ONLY + if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED))==WHERE_IDX_ONLY && (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 && sqlite3GlobalConfig.bUseCis && OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan) @@ -3309,9 +3349,9 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** A full-scan of the index might be a little faster than a full-scan ** of the table, so give this case a cost slightly less than a table ** scan. */ - cost = aiRowEst[0]*3 + pProbe->nColumn; - wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE; - }else if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){ + pc.rCost = aiRowEst[0]*3 + pProbe->nColumn; + pc.plan.wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE; + }else if( (pc.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ /* The cost of a full table scan is a number of move operations equal ** to the number of rows in the table. ** @@ -3321,11 +3361,15 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** decision and one which we expect to revisit in the future. But ** it seems to be working well enough at the moment. */ - cost = aiRowEst[0]*4; - wsFlags &= ~WHERE_IDX_ONLY; + pc.rCost = aiRowEst[0]*4; + pc.plan.wsFlags &= ~WHERE_IDX_ONLY; + if( pIdx ){ + pc.plan.wsFlags &= ~WHERE_ORDERED; + pc.plan.nOBSat = nPriorSat; + } }else{ log10N = estLog(aiRowEst[0]); - cost = nRow; + pc.rCost = pc.plan.nRow; if( pIdx ){ if( bLookup ){ /* For an index lookup followed by a table lookup: @@ -3333,20 +3377,20 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** + nRow steps through the index ** + nRow table searches to lookup the table entry using the rowid */ - cost += (nInMul + nRow)*log10N; + pc.rCost += (nInMul + pc.plan.nRow)*log10N; }else{ /* For a covering index: ** nInMul index searches to find the initial entry ** + nRow steps through the index */ - cost += nInMul*log10N; + pc.rCost += nInMul*log10N; } }else{ /* For a rowid primary key lookup: ** nInMult table searches to find the initial entry for each range ** + nRow steps through the table */ - cost += nInMul*log10N; + pc.rCost += nInMul*log10N; } } @@ -3357,10 +3401,12 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** difference and select C of 3.0. */ if( bSort ){ - cost += nRow*estLog(nRow*(nOrderBy - nOBSat)/nOrderBy)*3; + double m = estLog(pc.plan.nRow*(nOrderBy - pc.plan.nOBSat)/nOrderBy); + m *= (double)(pc.plan.nOBSat ? 2 : 3); + pc.rCost += pc.plan.nRow*m; } if( bDist ){ - cost += nRow*estLog(nRow)*3; + pc.rCost += pc.plan.nRow*estLog(pc.plan.nRow)*3; } /**** Cost of using this index has now been computed ****/ @@ -3381,25 +3427,25 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** might be selected even when there exists an optimal index that has ** no such dependency. */ - if( nRow>2 && cost<=p->cost.rCost ){ + if( pc.plan.nRow>2 && pc.rCost<=p->cost.rCost ){ int k; /* Loop counter */ - int nSkipEq = nEq; /* Number of == constraints to skip */ + int nSkipEq = pc.plan.nEq; /* Number of == constraints to skip */ int nSkipRange = nBound; /* Number of < constraints to skip */ Bitmask thisTab; /* Bitmap for pSrc */ thisTab = getMask(pWC->pMaskSet, iCur); - for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){ + for(pTerm=pWC->a, k=pWC->nTerm; pc.plan.nRow>2 && k; k--, pTerm++){ if( pTerm->wtFlags & TERM_VIRTUAL ) continue; if( (pTerm->prereqAll & p->notValid)!=thisTab ) continue; if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){ if( nSkipEq ){ - /* Ignore the first nEq equality matches since the index + /* Ignore the first pc.plan.nEq equality matches since the index ** has already accounted for these */ nSkipEq--; }else{ /* Assume each additional equality match reduces the result ** set size by a factor of 10 */ - nRow /= 10; + pc.plan.nRow /= 10; } }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){ if( nSkipRange ){ @@ -3413,39 +3459,32 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** more selective intentionally because of the subjective ** observation that indexed range constraints really are more ** selective in practice, on average. */ - nRow /= 3; + pc.plan.nRow /= 3; } }else if( pTerm->eOperator!=WO_NOOP ){ /* Any other expression lowers the output row count by half */ - nRow /= 2; + pc.plan.nRow /= 2; } } - if( nRow<2 ) nRow = 2; + if( pc.plan.nRow<2 ) pc.plan.nRow = 2; } WHERETRACE(( - "%s(%s):\n" - " nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n" - " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n" - " used=0x%llx nOrdered=%d nOBSat=%d\n", - pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"), - nEq, nInMul, (int)rangeDiv, bSort, bLookup, wsFlags, - p->notReady, log10N, nRow, cost, used, nOrdered, nOBSat + " nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n" + " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n" + " used=0x%llx nOBSat=%d\n", + pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags, + p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used, + pc.plan.nOBSat )); /* If this index is the best we have seen so far, then record this - ** index and its cost in the pCost structure. + ** index and its cost in the p->cost structure. */ - if( (!pIdx || wsFlags) - && (cost<p->cost.rCost || (cost<=p->cost.rCost && nRow<p->cost.plan.nRow)) - ){ - p->cost.rCost = cost; - p->cost.used = used; - p->cost.plan.nRow = nRow; - p->cost.plan.wsFlags = (wsFlags&wsFlagMask); - p->cost.plan.nEq = nEq; - p->cost.plan.nOBSat = nOBSat; + if( (!pIdx || pc.plan.wsFlags) && compareCost(&pc, &p->cost) ){ + p->cost = pc; + p->cost.plan.wsFlags &= wsFlagMask; p->cost.plan.u.pIdx = pIdx; } @@ -3467,17 +3506,15 @@ static void bestBtreeIndex(WhereBestIdx *p){ p->cost.plan.wsFlags |= WHERE_REVERSE; } - assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERBY)==0 ); + assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERED)==0 ); assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 ); assert( pSrc->pIndex==0 || p->cost.plan.u.pIdx==0 || p->cost.plan.u.pIdx==pSrc->pIndex ); - WHERETRACE(("best index is: %s\n", - ((p->cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" : - p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk") - )); + WHERETRACE((" best index is: %s\n", + p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk")); bestOrClauseIndex(p); bestAutomaticIndex(p); @@ -4205,7 +4242,7 @@ static Bitmask codeOneLoopStart( ** this requires some special handling. */ if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0 - && (pLevel->plan.wsFlags&WHERE_ORDERBY) + && (pLevel->plan.wsFlags&WHERE_ORDERED) && (pIdx->nColumn>nEq) ){ /* assert( pOrderBy->nExpr==1 ); */ @@ -5026,8 +5063,8 @@ WhereInfo *sqlite3WhereBegin( sWBI.notReady = (isOptimal ? m : sWBI.notValid); if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; - WHERETRACE(("=== trying table %d with isOptimal=%d ===\n", - j, isOptimal)); + WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", + j, sWBI.pSrc->pTab->zName, isOptimal)); assert( sWBI.pSrc->pTab ); #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(sWBI.pSrc->pTab) ){ @@ -5068,8 +5105,8 @@ WhereInfo *sqlite3WhereBegin( ** The NEVER() comes about because rule (2) above prevents ** An indexable full-table-scan from reaching rule (3). ** - ** (4) The plan cost must be lower than prior plans or else the - ** cost must be the same and the number of rows must be lower. + ** (4) The plan cost must be lower than prior plans, where "cost" + ** is defined by the compareCost() function above. */ if( (sWBI.cost.used&sWBI.notValid)==0 /* (1) */ && (bestJ<0 || (notIndexed&m)!=0 /* (2) */ @@ -5077,14 +5114,13 @@ WhereInfo *sqlite3WhereBegin( || (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0) && (nUnconstrained==0 || sWBI.pSrc->pIndex==0 /* (3) */ || NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) - && (bestJ<0 || sWBI.cost.rCost<bestPlan.rCost /* (4) */ - || (sWBI.cost.rCost<=bestPlan.rCost - && sWBI.cost.plan.nRow<bestPlan.plan.nRow)) + && (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */ ){ - WHERETRACE(("=== table %d is best so far" - " with cost=%.1f, nRow=%.1f, nOBSat=%d\n", - j, sWBI.cost.rCost, sWBI.cost.plan.nRow, - sWBI.cost.plan.nOBSat)); + WHERETRACE((" === table %d (%s) is best so far\n" + " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n", + j, sWBI.pSrc->pTab->zName, + sWBI.cost.rCost, sWBI.cost.plan.nRow, + sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); bestPlan = sWBI.cost; bestJ = j; } @@ -5093,19 +5129,18 @@ WhereInfo *sqlite3WhereBegin( } assert( bestJ>=0 ); assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); - WHERETRACE(("*** Optimizer selects table %d for loop %d with:\n" - " cost=%.1f, nRow=%.1f, nOBSat=%d wsFlags=0x%08x\n", - bestJ, pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, + WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" + " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", + bestJ, pTabList->a[bestJ].pTab->zName, + pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); - if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){ - pWInfo->nOBSat = pOrderBy->nExpr; - } if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){ assert( pWInfo->eDistinct==0 ); pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } andFlags &= bestPlan.plan.wsFlags; pLevel->plan = bestPlan.plan; + pLevel->iTabCur = pTabList->a[bestJ].iCursor; testcase( bestPlan.plan.wsFlags & WHERE_INDEXED ); testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX ); if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){ @@ -5147,11 +5182,18 @@ WhereInfo *sqlite3WhereBegin( if( pParse->nErr || db->mallocFailed ){ goto whereBeginError; } + if( nTabList ){ + pLevel--; + pWInfo->nOBSat = pLevel->plan.nOBSat; + }else{ + pWInfo->nOBSat = 0; + } /* If the total query only selects a single row, then the ORDER BY ** clause is irrelevant. */ if( (andFlags & WHERE_UNIQUE)!=0 && pOrderBy ){ + assert( nTabList==0 || (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ); pWInfo->nOBSat = pOrderBy->nExpr; } @@ -5179,7 +5221,6 @@ WhereInfo *sqlite3WhereBegin( pTabItem = &pTabList->a[pLevel->iFrom]; pTab = pTabItem->pTab; - pLevel->iTabCur = pTabItem->iCursor; pWInfo->nRowOut *= pLevel->plan.nRow; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){ |