Index: src/analyze.c ================================================================== --- src/analyze.c +++ src/analyze.c @@ -111,11 +111,12 @@ int iIdxCur; /* Cursor open on index being analyzed */ Vdbe *v; /* The virtual machine being built up */ int i; /* Loop counter */ int topOfLoop; /* The top of the loop */ int endOfLoop; /* The end of the loop */ - int addr; /* The address of an instruction */ + int addr = 0; /* The address of an instruction */ + int jZeroRows = 0; /* Jump from here if number of rows is zero */ int iDb; /* Index of database containing pTab */ int regTabname = iMem++; /* Register containing table name */ int regIdxname = iMem++; /* Register containing index name */ int regSampleno = iMem++; /* Register containing next sample number */ int regCol = iMem++; /* Content of a column analyzed table */ @@ -130,12 +131,19 @@ int regLast = iMem++; /* Index of last sample to record */ int regFirst = iMem++; /* Index of first sample to record */ #endif v = sqlite3GetVdbe(pParse); - if( v==0 || NEVER(pTab==0) || pTab->pIndex==0 ){ - /* Do no analysis for tables that have no indices */ + if( v==0 || NEVER(pTab==0) ){ + return; + } + if( pTab->tnum==0 ){ + /* Do not gather statistics on views or virtual tables */ + return; + } + if( memcmp(pTab->zName, "sqlite_", 7)==0 ){ + /* Do not gather statistics on system tables */ return; } assert( sqlite3BtreeHoldsAllMutexes(db) ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 ); @@ -148,10 +156,11 @@ /* Establish a read-lock on the table at the shared-cache level. */ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); iIdxCur = pParse->nTab++; + sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int nCol = pIdx->nColumn; KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); if( iMem+1+(nCol*2)>pParse->nMem ){ @@ -162,14 +171,11 @@ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb, (char *)pKey, P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pIdx->zName)); - /* Populate the registers containing the table and index names. */ - if( pTab->pIndex==pIdx ){ - sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0); - } + /* Populate the register containing the index name. */ sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0); #ifdef SQLITE_ENABLE_STAT2 /* If this iteration of the loop is generating code to analyze the @@ -225,13 +231,14 @@ sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop); topOfLoop = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1); for(i=0; iazColl!=0 ); + assert( pIdx->azColl[i]!=0 ); + pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]); + sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1, + (char*)pColl, P4_COLLSEQ); + sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); } if( db->mallocFailed ){ /* If a malloc failure has occurred, then the result of the expression ** passed as the second argument to the call to sqlite3VdbeJumpHere() ** below may be negative. Which causes an assert() to fail (or an @@ -272,11 +284,15 @@ ** out-of-bounds write if SQLITE_DEBUG is not defined). */ return; } sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop); for(i=0; i0 then it is always the case the D>0 so division by zero ** is never possible. */ - addr = sqlite3VdbeAddOp1(v, OP_IfNot, iMem); sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno); + if( jZeroRows==0 ){ + jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem); + } for(i=0; ipIndex==0 ){ + sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb); + VdbeComment((v, "%s", pTab->zName)); + sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno); + sqlite3VdbeAddOp1(v, OP_Close, iIdxCur); + }else{ + assert( jZeroRows>0 ); + addr = sqlite3VdbeAddOp0(v, OP_Goto); + sqlite3VdbeJumpHere(v, jZeroRows); + } + sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname); + sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); + sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid); + sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid); + sqlite3VdbeChangeP5(v, OPFLAG_APPEND); + if( pParse->nMemnMem = regRec; + if( jZeroRows ){ sqlite3VdbeJumpHere(v, addr); } } /* ** Generate code that will cause the most recent index analysis to -** be laoded into internal hash tables where is can be used. +** be loaded into internal hash tables where is can be used. */ static void loadAnalysis(Parse *pParse, int iDb){ Vdbe *v = sqlite3GetVdbe(pParse); if( v ){ sqlite3VdbeAddOp1(v, OP_LoadAnalysis, iDb); @@ -451,39 +491,56 @@ /* ** This callback is invoked once for each index when reading the ** sqlite_stat1 table. ** -** argv[0] = name of the index -** argv[1] = results of analysis - on integer for each column +** argv[0] = name of the table +** argv[1] = name of the index (might be NULL) +** argv[2] = results of analysis - on integer for each column +** +** Entries for which argv[1]==NULL simply record the number of rows in +** the table. */ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ analysisInfo *pInfo = (analysisInfo*)pData; Index *pIndex; - int i, c; + Table *pTable; + int i, c, n; unsigned int v; const char *z; - assert( argc==2 ); + assert( argc==3 ); UNUSED_PARAMETER2(NotUsed, argc); - if( argv==0 || argv[0]==0 || argv[1]==0 ){ + if( argv==0 || argv[0]==0 || argv[2]==0 ){ return 0; } - pIndex = sqlite3FindIndex(pInfo->db, argv[0], pInfo->zDatabase); - if( pIndex==0 ){ + pTable = sqlite3FindTable(pInfo->db, argv[0], pInfo->zDatabase); + if( pTable==0 ){ return 0; } - z = argv[1]; - for(i=0; *z && i<=pIndex->nColumn; i++){ + if( argv[1] ){ + pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase); + }else{ + pIndex = 0; + } + n = pIndex ? pIndex->nColumn : 0; + z = argv[2]; + for(i=0; *z && i<=n; i++){ v = 0; while( (c=z[0])>='0' && c<='9' ){ v = v*10 + c - '0'; z++; } + if( i==0 ) pTable->nRowEst = v; + if( pIndex==0 ) break; pIndex->aiRowEst[i] = v; if( *z==' ' ) z++; + if( memcmp(z, "unordered", 10)==0 ){ + pIndex->bUnordered = 1; + break; + } } return 0; } /* @@ -553,11 +610,11 @@ return SQLITE_ERROR; } /* Load new statistics out of the sqlite_stat1 table */ zSql = sqlite3MPrintf(db, - "SELECT idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase); + "SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0); sqlite3DbFree(db, zSql); @@ -581,12 +638,15 @@ sqlite3DbFree(db, zSql); } if( rc==SQLITE_OK ){ while( sqlite3_step(pStmt)==SQLITE_ROW ){ - char *zIndex = (char *)sqlite3_column_text(pStmt, 0); - Index *pIdx = sqlite3FindIndex(db, zIndex, sInfo.zDatabase); + char *zIndex; /* Index name */ + Index *pIdx; /* Pointer to the index object */ + + zIndex = (char *)sqlite3_column_text(pStmt, 0); + pIdx = zIndex ? sqlite3FindIndex(db, zIndex, sInfo.zDatabase) : 0; if( pIdx ){ int iSample = sqlite3_column_int(pStmt, 1); if( iSample=0 ){ int eType = sqlite3_column_type(pStmt, 2); Index: src/attach.c ================================================================== --- src/attach.c +++ src/attach.c @@ -122,13 +122,12 @@ /* Open the database file. If the btree is successfully opened, use ** it to obtain the database schema. At this point the schema may ** or may not be initialised. */ - rc = sqlite3BtreeFactory(db, zFile, 0, SQLITE_DEFAULT_CACHE_SIZE, - db->openFlags | SQLITE_OPEN_MAIN_DB, - &aNew->pBt); + rc = sqlite3BtreeOpen(zFile, db, &aNew->pBt, 0, + db->openFlags | SQLITE_OPEN_MAIN_DB); db->nDb++; if( rc==SQLITE_CONSTRAINT ){ rc = SQLITE_ERROR; zErrDyn = sqlite3MPrintf(db, "database is already attached"); }else if( rc==SQLITE_OK ){ Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -1670,15 +1670,24 @@ /* ** Open a database file. ** ** zFilename is the name of the database file. If zFilename is NULL -** a new database with a random name is created. This randomly named -** database file will be deleted when sqlite3BtreeClose() is called. +** then an ephemeral database is created. The ephemeral database might +** be exclusively in memory, or it might use a disk-based memory cache. +** Either way, the ephemeral database will be automatically deleted +** when sqlite3BtreeClose() is called. +** ** If zFilename is ":memory:" then an in-memory database is created ** that is automatically destroyed when it is closed. ** +** The "flags" parameter is a bitmask that might contain bits +** BTREE_OMIT_JOURNAL and/or BTREE_NO_READLOCK. The BTREE_NO_READLOCK +** bit is also set if the SQLITE_NoReadlock flags is set in db->flags. +** These flags are passed through into sqlite3PagerOpen() and must +** be the same values as PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK. +** ** If the database is already opened in the same database connection ** and we are in shared cache mode, then the open will fail with an ** SQLITE_CONSTRAINT error. We cannot allow two or more BtShared ** objects in the same database connection since doing so will lead ** to problems with locking. @@ -1695,10 +1704,13 @@ Btree *p; /* Handle to return */ sqlite3_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */ int rc = SQLITE_OK; /* Result code from this function */ u8 nReserve; /* Byte of unused space on each page */ unsigned char zDbHeader[100]; /* Database header content */ + + /* True if opening an ephemeral, temporary database */ + const int isTempDb = zFilename==0 || zFilename[0]==0; /* Set the variable isMemdb to true for an in-memory database, or ** false for a file-based database. This symbol is only required if ** either of the shared-data or autovacuum features are compiled ** into the library. @@ -1705,17 +1717,34 @@ */ #if !defined(SQLITE_OMIT_SHARED_CACHE) || !defined(SQLITE_OMIT_AUTOVACUUM) #ifdef SQLITE_OMIT_MEMORYDB const int isMemdb = 0; #else - const int isMemdb = zFilename && !strcmp(zFilename, ":memory:"); + const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0) + || (isTempDb && sqlite3TempInMemory(db)); #endif #endif assert( db!=0 ); assert( sqlite3_mutex_held(db->mutex) ); + assert( (flags&0xff)==flags ); /* flags fit in 8 bits */ + /* Only a BTREE_SINGLE database can be BTREE_UNORDERED */ + assert( (flags & BTREE_UNORDERED)==0 || (flags & BTREE_SINGLE)!=0 ); + + /* A BTREE_SINGLE database is always a temporary and/or ephemeral */ + assert( (flags & BTREE_SINGLE)==0 || isTempDb ); + + if( db->flags & SQLITE_NoReadlock ){ + flags |= BTREE_NO_READLOCK; + } + if( isMemdb ){ + flags |= BTREE_MEMORY; + } + if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){ + vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB; + } pVfs = db->pVfs; p = sqlite3MallocZero(sizeof(Btree)); if( !p ){ return SQLITE_NOMEM; } @@ -1729,11 +1758,11 @@ #if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) /* ** If this Btree is a candidate for shared cache, try to find an ** existing BtShared object that we can share with */ - if( isMemdb==0 && zFilename && zFilename[0] ){ + if( isMemdb==0 && isTempDb==0 ){ if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){ int nFullPathname = pVfs->mxPathname+1; char *zFullPathname = sqlite3Malloc(nFullPathname); sqlite3_mutex *mutexShared; p->sharable = 1; @@ -1804,10 +1833,11 @@ rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); } if( rc!=SQLITE_OK ){ goto btree_open_out; } + pBt->openFlags = flags; pBt->db = db; sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); p->pBt = pBt; pBt->pCursor = 0; @@ -1908,10 +1938,18 @@ sqlite3PagerClose(pBt->pPager); } sqlite3_free(pBt); sqlite3_free(p); *ppBtree = 0; + }else{ + /* If the B-Tree was successfully opened, set the pager-cache size to the + ** default value. Except, when opening on an existing shared pager-cache, + ** do not change the pager-cache size. + */ + if( sqlite3BtreeSchema(p, 0, 0)==0 ){ + sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE); + } } if( mutexOpen ){ assert( sqlite3_mutex_held(mutexOpen) ); sqlite3_mutex_leave(mutexOpen); } @@ -4550,22 +4588,32 @@ /* ** Advance the cursor to the next entry in the database. If ** successful then set *pRes=0. If the cursor ** was already pointing to the last entry in the database before ** this routine was called, then set *pRes=1. +** +** The calling function will set *pRes to 0 or 1. The initial *pRes value +** will be 1 if the cursor being stepped corresponds to an SQL index and +** if this routine could have been skipped if that SQL index had been +** a unique index. Otherwise the caller will have set *pRes to zero. +** Zero is the common case. The btree implementation is free to use the +** initial *pRes value as a hint to improve performance, but the current +** SQLite btree implementation does not. (Note that the comdb2 btree +** implementation does use this hint, however.) */ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ int rc; int idx; MemPage *pPage; assert( cursorHoldsMutex(pCur) ); + assert( pRes!=0 ); + assert( *pRes==0 || *pRes==1 ); rc = restoreCursorPosition(pCur); if( rc!=SQLITE_OK ){ return rc; } - assert( pRes!=0 ); if( CURSOR_INVALID==pCur->eState ){ *pRes = 1; return SQLITE_OK; } if( pCur->skipNext>0 ){ @@ -4619,16 +4667,27 @@ /* ** Step the cursor to the back to the previous entry in the database. If ** successful then set *pRes=0. If the cursor ** was already pointing to the first entry in the database before ** this routine was called, then set *pRes=1. +** +** The calling function will set *pRes to 0 or 1. The initial *pRes value +** will be 1 if the cursor being stepped corresponds to an SQL index and +** if this routine could have been skipped if that SQL index had been +** a unique index. Otherwise the caller will have set *pRes to zero. +** Zero is the common case. The btree implementation is free to use the +** initial *pRes value as a hint to improve performance, but the current +** SQLite btree implementation does not. (Note that the comdb2 btree +** implementation does use this hint, however.) */ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ int rc; MemPage *pPage; assert( cursorHoldsMutex(pCur) ); + assert( pRes!=0 ); + assert( *pRes==0 || *pRes==1 ); rc = restoreCursorPosition(pCur); if( rc!=SQLITE_OK ){ return rc; } pCur->atLast = 0; @@ -6776,11 +6835,11 @@ ** from the internal node. The 'previous' entry is used for this instead ** of the 'next' entry, as the previous entry is always a part of the ** sub-tree headed by the child page of the cell being deleted. This makes ** balancing the tree following the delete operation easier. */ if( !pPage->leaf ){ - int notUsed; + int notUsed = 0; rc = sqlite3BtreePrevious(pCur, ¬Used); if( rc ) return rc; } /* Save the positions of any other cursors open on this table before @@ -6858,15 +6917,16 @@ ** flags might not work: ** ** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys ** BTREE_ZERODATA Used for SQL indices */ -static int btreeCreateTable(Btree *p, int *piTable, int flags){ +static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){ BtShared *pBt = p->pBt; MemPage *pRoot; Pgno pgnoRoot; int rc; + int ptfFlags; /* Page-type flage for the root page of new table */ assert( sqlite3BtreeHoldsMutex(p) ); assert( pBt->inTransaction==TRANS_WRITE ); assert( !pBt->readOnly ); @@ -6981,12 +7041,18 @@ rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); if( rc ) return rc; } #endif assert( sqlite3PagerIswriteable(pRoot->pDbPage) ); - zeroPage(pRoot, flags | PTF_LEAF); + if( createTabFlags & BTREE_INTKEY ){ + ptfFlags = PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF; + }else{ + ptfFlags = PTF_ZERODATA | PTF_LEAF; + } + zeroPage(pRoot, ptfFlags); sqlite3PagerUnref(pRoot->pDbPage); + assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 ); *piTable = (int)pgnoRoot; return SQLITE_OK; } int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){ int rc; Index: src/btree.h ================================================================== --- src/btree.h +++ src/btree.h @@ -65,16 +65,15 @@ ** following values. ** ** NOTE: These values must match the corresponding PAGER_ values in ** pager.h. */ -#define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */ +#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */ #define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */ -#define BTREE_MEMORY 4 /* In-memory DB. No argument */ -#define BTREE_READONLY 8 /* Open the database in read-only mode */ -#define BTREE_READWRITE 16 /* Open for both reading and writing */ -#define BTREE_CREATE 32 /* Create the database if it does not exist */ +#define BTREE_MEMORY 4 /* This is an in-memory DB */ +#define BTREE_SINGLE 8 /* The file contains at most 1 b-tree */ +#define BTREE_UNORDERED 16 /* Use of a hash implementation is OK */ int sqlite3BtreeClose(Btree*); int sqlite3BtreeSetCacheSize(Btree*,int); int sqlite3BtreeSetSafetyLevel(Btree*,int,int); int sqlite3BtreeSyncDisabled(Btree*); @@ -106,15 +105,21 @@ int sqlite3BtreeCopyFile(Btree *, Btree *); int sqlite3BtreeIncrVacuum(Btree *); /* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR -** of the following flags: +** of the flags shown below. +** +** Every SQLite table must have either BTREE_INTKEY or BTREE_BLOBKEY set. +** With BTREE_INTKEY, the table key is a 64-bit integer and arbitrary data +** is stored in the leaves. (BTREE_INTKEY is used for SQL tables.) With +** BTREE_BLOBKEY, the key is an arbitrary BLOB and no content is stored +** anywhere - the key is the content. (BTREE_BLOBKEY is used for SQL +** indices.) */ #define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ -#define BTREE_ZERODATA 2 /* Table has keys only - no data */ -#define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */ +#define BTREE_BLOBKEY 2 /* Table has keys only - no data */ int sqlite3BtreeDropTable(Btree*, int, int*); int sqlite3BtreeClearTable(Btree*, int, int*); void sqlite3BtreeTripAllCursors(Btree*, int); Index: src/btreeInt.h ================================================================== --- src/btreeInt.h +++ src/btreeInt.h @@ -407,10 +407,11 @@ MemPage *pPage1; /* First page of the database */ u8 readOnly; /* True if the underlying file is readonly */ u8 pageSizeFixed; /* True if the page size can no longer be changed */ u8 secureDelete; /* True if secure_delete is enabled */ u8 initiallyEmpty; /* Database is empty at start of transaction */ + u8 openFlags; /* Flags to sqlite3BtreeOpen() */ #ifndef SQLITE_OMIT_AUTOVACUUM u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */ #endif u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -800,10 +800,11 @@ } pTable->zName = zName; pTable->iPKey = -1; pTable->pSchema = db->aDb[iDb].pSchema; pTable->nRef = 1; + pTable->nRowEst = 1000000; assert( pParse->pNewTable==0 ); pParse->pNewTable = pTable; /* If this is the magic sqlite_sequence table used by autoincrement, ** then record a pointer to this table in the main database structure @@ -2830,18 +2831,18 @@ ** are based on typical values found in actual indices. */ void sqlite3DefaultRowEst(Index *pIdx){ unsigned *a = pIdx->aiRowEst; int i; + unsigned n; assert( a!=0 ); - a[0] = 1000000; - for(i=pIdx->nColumn; i>=5; i--){ - a[i] = 5; - } - while( i>=1 ){ - a[i] = 11 - i; - i--; + a[0] = pIdx->pTable->nRowEst; + if( a[0]<10 ) a[0] = 10; + n = 10; + for(i=1; i<=pIdx->nColumn; i++){ + a[i] = n; + if( n>5 ) n--; } if( pIdx->onError!=OE_None ){ a[pIdx->nColumn] = 1; } } @@ -3389,11 +3390,11 @@ SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TEMP_DB; - rc = sqlite3BtreeFactory(db, 0, 0, SQLITE_DEFAULT_CACHE_SIZE, flags, &pBt); + rc = sqlite3BtreeOpen(0, db, &pBt, 0, flags); if( rc!=SQLITE_OK ){ sqlite3ErrorMsg(pParse, "unable to open a temporary database " "file for storing temporary tables"); pParse->rc = rc; return 1; Index: src/ctime.c ================================================================== --- src/ctime.c +++ src/ctime.c @@ -296,10 +296,13 @@ #ifdef SQLITE_OMIT_TRIGGER "OMIT_TRIGGER", #endif #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION "OMIT_TRUNCATE_OPTIMIZATION", +#endif +#ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT + "OMIT_UNIQUE_ENFORCEMENT", #endif #ifdef SQLITE_OMIT_UTF16 "OMIT_UTF16", #endif #ifdef SQLITE_OMIT_VACUUM Index: src/delete.c ================================================================== --- src/delete.c +++ src/delete.c @@ -360,11 +360,13 @@ int regRowid; /* Actual register containing rowids */ /* Collect rowids of every row to be deleted. */ sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,WHERE_DUPLICATES_OK); + pWInfo = sqlite3WhereBegin( + pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK, 0 + ); if( pWInfo==0 ) goto delete_from_cleanup; regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid); sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid); if( db->flags & SQLITE_CountRows ){ sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -894,10 +894,11 @@ pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias); pNewItem->jointype = pOldItem->jointype; pNewItem->iCursor = pOldItem->iCursor; pNewItem->isPopulated = pOldItem->isPopulated; + pNewItem->isCorrelated = pOldItem->isCorrelated; pNewItem->zIndex = sqlite3DbStrDup(db, pOldItem->zIndex); pNewItem->notIndexed = pOldItem->notIndexed; pNewItem->pIndex = pOldItem->pIndex; pTab = pNewItem->pTab = pOldItem->pTab; if( pTab ){ @@ -1533,12 +1534,12 @@ return eType; } #endif /* -** Generate code for scalar subqueries used as an expression -** and IN operators. Examples: +** Generate code for scalar subqueries used as a subquery expression, EXISTS, +** or IN operators. Examples: ** ** (SELECT a FROM b) -- subquery ** EXISTS (SELECT a FROM b) -- EXISTS subquery ** x IN (4,5,11) -- IN operator with list on right-hand side ** x IN (SELECT a FROM b) -- IN operator with subquery on the right @@ -1597,14 +1598,14 @@ assert( testAddr>0 || pParse->db->mallocFailed ); } switch( pExpr->op ){ case TK_IN: { - char affinity; - KeyInfo keyInfo; - int addr; /* Address of OP_OpenEphemeral instruction */ - Expr *pLeft = pExpr->pLeft; + char affinity; /* Affinity of the LHS of the IN */ + KeyInfo keyInfo; /* Keyinfo for the generated table */ + int addr; /* Address of OP_OpenEphemeral instruction */ + Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */ if( rMayHaveNull ){ sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull); } @@ -1623,10 +1624,11 @@ ** 'x' nor the SELECT... statement are columns, then numeric affinity ** is used. */ pExpr->iTable = pParse->nTab++; addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid); + if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED); memset(&keyInfo, 0, sizeof(keyInfo)); keyInfo.nField = 1; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* Case 1: expr IN (SELECT ...) @@ -2231,77 +2233,10 @@ } return 0; } #endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */ -/* -** If the last instruction coded is an ephemeral copy of any of -** the registers in the nReg registers beginning with iReg, then -** convert the last instruction from OP_SCopy to OP_Copy. -*/ -void sqlite3ExprHardCopy(Parse *pParse, int iReg, int nReg){ - VdbeOp *pOp; - Vdbe *v; - - assert( pParse->db->mallocFailed==0 ); - v = pParse->pVdbe; - assert( v!=0 ); - pOp = sqlite3VdbeGetOp(v, -1); - assert( pOp!=0 ); - if( pOp->opcode==OP_SCopy && pOp->p1>=iReg && pOp->p1opcode = OP_Copy; - } -} - -/* -** Generate code to store the value of the iAlias-th alias in register -** target. The first time this is called, pExpr is evaluated to compute -** the value of the alias. The value is stored in an auxiliary register -** and the number of that register is returned. On subsequent calls, -** the register number is returned without generating any code. -** -** Note that in order for this to work, code must be generated in the -** same order that it is executed. -** -** Aliases are numbered starting with 1. So iAlias is in the range -** of 1 to pParse->nAlias inclusive. -** -** pParse->aAlias[iAlias-1] records the register number where the value -** of the iAlias-th alias is stored. If zero, that means that the -** alias has not yet been computed. -*/ -static int codeAlias(Parse *pParse, int iAlias, Expr *pExpr, int target){ -#if 0 - sqlite3 *db = pParse->db; - int iReg; - if( pParse->nAliasAllocnAlias ){ - pParse->aAlias = sqlite3DbReallocOrFree(db, pParse->aAlias, - sizeof(pParse->aAlias[0])*pParse->nAlias ); - testcase( db->mallocFailed && pParse->nAliasAlloc>0 ); - if( db->mallocFailed ) return 0; - memset(&pParse->aAlias[pParse->nAliasAlloc], 0, - (pParse->nAlias-pParse->nAliasAlloc)*sizeof(pParse->aAlias[0])); - pParse->nAliasAlloc = pParse->nAlias; - } - assert( iAlias>0 && iAlias<=pParse->nAlias ); - iReg = pParse->aAlias[iAlias-1]; - if( iReg==0 ){ - if( pParse->iCacheLevel>0 ){ - iReg = sqlite3ExprCodeTarget(pParse, pExpr, target); - }else{ - iReg = ++pParse->nMem; - sqlite3ExprCode(pParse, pExpr, iReg); - pParse->aAlias[iAlias-1] = iReg; - } - } - return iReg; -#else - UNUSED_PARAMETER(iAlias); - return sqlite3ExprCodeTarget(pParse, pExpr, target); -#endif -} - /* ** Generate code into the current Vdbe to evaluate the given ** expression. Attempt to store the results in register "target". ** Return the register where results are stored. ** @@ -2406,11 +2341,11 @@ case TK_REGISTER: { inReg = pExpr->iTable; break; } case TK_AS: { - inReg = codeAlias(pParse, pExpr->iTable, pExpr->pLeft, target); + inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); break; } #ifndef SQLITE_OMIT_CAST case TK_CAST: { /* Expressions of the form: CAST(pLeft AS token) */ @@ -2838,10 +2773,15 @@ testcase( regFree1==0 ); cacheX.op = TK_REGISTER; opCompare.op = TK_EQ; opCompare.pLeft = &cacheX; pTest = &opCompare; + /* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001: + ** The value in regFree1 might get SCopy-ed into the file result. + ** So make sure that the regFree1 register is not reused for other + ** purposes and possibly overwritten. */ + regFree1 = 0; } for(i=0; i0 && target<=pParse->nMem ); - inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); - assert( pParse->pVdbe || pParse->db->mallocFailed ); - if( inReg!=target && pParse->pVdbe ){ - sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target); + if( pExpr && pExpr->op==TK_REGISTER ){ + sqlite3VdbeAddOp2(pParse->pVdbe, OP_Copy, pExpr->iTable, target); + }else{ + inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); + assert( pParse->pVdbe || pParse->db->mallocFailed ); + if( inReg!=target && pParse->pVdbe ){ + sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target); + } } return target; } /* @@ -3107,23 +3051,18 @@ ){ struct ExprList_item *pItem; int i, n; assert( pList!=0 ); assert( target>0 ); + assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */ n = pList->nExpr; for(pItem=pList->a, i=0; iiAlias ){ - int iReg = codeAlias(pParse, pItem->iAlias, pItem->pExpr, target+i); - Vdbe *v = sqlite3GetVdbe(pParse); - if( iReg!=target+i ){ - sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target+i); - } - }else{ - sqlite3ExprCode(pParse, pItem->pExpr, target+i); - } - if( doHardCopy && !pParse->db->mallocFailed ){ - sqlite3ExprHardCopy(pParse, target, n); + Expr *pExpr = pItem->pExpr; + int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); + if( inReg!=target+i ){ + sqlite3VdbeAddOp2(pParse->pVdbe, doHardCopy ? OP_Copy : OP_SCopy, + inReg, target+i); } } return n; } Index: src/fkey.c ================================================================== --- src/fkey.c +++ src/fkey.c @@ -378,11 +378,11 @@ KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); for(i=0; i0 && pFKey->isDeferred==0 ){ sqlite3ParseToplevel(pParse)->mayAbort = 1; } sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); if( pWInfo ){ Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -1310,11 +1310,13 @@ ** index and making sure that duplicate entries do not already exist. ** Add the new records to the indices as we go. */ for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ int regIdx; +#ifndef SQLITE_OMIT_UNIQUE_ENFORCEMENT int regR; +#endif if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */ /* Create a key for accessing the index entry */ regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn+1); @@ -1328,10 +1330,15 @@ } sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]); sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); + +#ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT + sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); + continue; /* Treat pIdx as if it is not a UNIQUE index */ +#else /* Find out what action to take in case there is an indexing conflict */ onError = pIdx->onError; if( onError==OE_None ){ sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); @@ -1402,10 +1409,11 @@ break; } } sqlite3VdbeJumpHere(v, j3); sqlite3ReleaseTempReg(pParse, regR); +#endif } if( pbMayReplace ){ *pbMayReplace = seenReplace; } Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -1348,64 +1348,10 @@ #if SQLITE_TEMP_STORE<1 || SQLITE_TEMP_STORE>3 return 0; #endif } -/* -** This routine is called to create a connection to a database BTree -** driver. If zFilename is the name of a file, then that file is -** opened and used. If zFilename is the magic name ":memory:" then -** the database is stored in memory (and is thus forgotten as soon as -** the connection is closed.) If zFilename is NULL then the database -** is a "virtual" database for transient use only and is deleted as -** soon as the connection is closed. -** -** A virtual database can be either a disk file (that is automatically -** deleted when the file is closed) or it an be held entirely in memory. -** The sqlite3TempInMemory() function is used to determine which. -*/ -int sqlite3BtreeFactory( - sqlite3 *db, /* Main database when opening aux otherwise 0 */ - const char *zFilename, /* Name of the file containing the BTree database */ - int omitJournal, /* if TRUE then do not journal this file */ - int nCache, /* How many pages in the page cache */ - int vfsFlags, /* Flags passed through to vfsOpen */ - Btree **ppBtree /* Pointer to new Btree object written here */ -){ - int btFlags = 0; - int rc; - - assert( sqlite3_mutex_held(db->mutex) ); - assert( ppBtree != 0); - if( omitJournal ){ - btFlags |= BTREE_OMIT_JOURNAL; - } - if( db->flags & SQLITE_NoReadlock ){ - btFlags |= BTREE_NO_READLOCK; - } -#ifndef SQLITE_OMIT_MEMORYDB - if( zFilename==0 && sqlite3TempInMemory(db) ){ - zFilename = ":memory:"; - } -#endif - - if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (zFilename==0 || *zFilename==0) ){ - vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB; - } - rc = sqlite3BtreeOpen(zFilename, (sqlite3 *)db, ppBtree, btFlags, vfsFlags); - - /* If the B-Tree was successfully opened, set the pager-cache size to the - ** default value. Except, if the call to BtreeOpen() returned a handle - ** open on an existing shared pager-cache, do not change the pager-cache - ** size. - */ - if( rc==SQLITE_OK && 0==sqlite3BtreeSchema(*ppBtree, 0, 0) ){ - sqlite3BtreeSetCacheSize(*ppBtree, nCache); - } - return rc; -} - /* ** Return UTF-8 encoded English language explanation of the most recent ** error. */ const char *sqlite3_errmsg(sqlite3 *db){ @@ -1783,13 +1729,12 @@ createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0, nocaseCollatingFunc, 0); /* Open the backend database driver */ db->openFlags = flags; - rc = sqlite3BtreeFactory(db, zFilename, 0, SQLITE_DEFAULT_CACHE_SIZE, - flags | SQLITE_OPEN_MAIN_DB, - &db->aDb[0].pBt); + rc = sqlite3BtreeOpen(zFilename, db, &db->aDb[0].pBt, 0, + flags | SQLITE_OPEN_MAIN_DB); if( rc!=SQLITE_OK ){ if( rc==SQLITE_IOERR_NOMEM ){ rc = SQLITE_NOMEM; } sqlite3Error(db, rc, 0); Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -4223,10 +4223,17 @@ journalFileSize = ROUND8(sqlite3MemJournalSize()); } /* Set the output variable to NULL in case an error occurs. */ *ppPager = 0; + +#ifndef SQLITE_OMIT_MEMORYDB + if( flags & PAGER_MEMORY ){ + memDb = 1; + zFilename = 0; + } +#endif /* Compute and store the full pathname in an allocated buffer pointed ** to by zPathname, length nPathname. Or, if this is a temporary file, ** leave both nPathname and zPathname set to 0. */ @@ -4234,21 +4241,12 @@ nPathname = pVfs->mxPathname+1; zPathname = sqlite3Malloc(nPathname*2); if( zPathname==0 ){ return SQLITE_NOMEM; } -#ifndef SQLITE_OMIT_MEMORYDB - if( strcmp(zFilename,":memory:")==0 ){ - memDb = 1; - zPathname[0] = 0; - }else -#endif - { - zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ - rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); - } - + zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ + rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); nPathname = sqlite3Strlen30(zPathname); if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){ /* This branch is taken when the journal path required by ** the database being opened will be more than pVfs->mxPathname ** bytes in length. This means the database cannot be opened, Index: src/pager.h ================================================================== --- src/pager.h +++ src/pager.h @@ -57,10 +57,11 @@ ** ** NOTE: These values must match the corresponding BTREE_ values in btree.h. */ #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ #define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ +#define PAGER_MEMORY 0x0004 /* In-memory database */ /* ** Valid values for the second argument to sqlite3PagerLockingMode(). */ #define PAGER_LOCKINGMODE_QUERY -1 Index: src/prepare.c ================================================================== --- src/prepare.c +++ src/prepare.c @@ -626,17 +626,17 @@ #ifndef SQLITE_OMIT_EXPLAIN if( rc==SQLITE_OK && pParse->pVdbe && pParse->explain ){ static const char * const azColName[] = { "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", - "order", "from", "detail" + "selectid", "order", "from", "detail" }; int iFirst, mx; if( pParse->explain==2 ){ - sqlite3VdbeSetNumCols(pParse->pVdbe, 3); + sqlite3VdbeSetNumCols(pParse->pVdbe, 4); iFirst = 8; - mx = 11; + mx = 12; }else{ sqlite3VdbeSetNumCols(pParse->pVdbe, 8); iFirst = 0; mx = 8; } Index: src/resolve.c ================================================================== --- src/resolve.c +++ src/resolve.c @@ -994,15 +994,29 @@ /* Recursively resolve names in all subqueries */ for(i=0; ipSrc->nSrc; i++){ struct SrcList_item *pItem = &p->pSrc->a[i]; if( pItem->pSelect ){ + NameContext *pNC; /* Used to iterate name contexts */ + int nRef = 0; /* Refcount for pOuterNC and outer contexts */ const char *zSavedContext = pParse->zAuthContext; + + /* Count the total number of references to pOuterNC and all of its + ** parent contexts. After resolving references to expressions in + ** pItem->pSelect, check if this value has changed. If so, then + ** SELECT statement pItem->pSelect must be correlated. Set the + ** pItem->isCorrelated flag if this is the case. */ + for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef; + if( pItem->zName ) pParse->zAuthContext = pItem->zName; sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); pParse->zAuthContext = zSavedContext; if( pParse->nErr || db->mallocFailed ) return WRC_Abort; + + for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef; + assert( pItem->isCorrelated==0 && nRef<=0 ); + pItem->isCorrelated = (nRef!=0); } } /* If there are no aggregate functions in the result-set, and no GROUP BY ** expression, do not allow aggregates in any of the other expressions. Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -1300,10 +1300,11 @@ /* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside ** is disabled */ assert( db->lookaside.bEnabled==0 ); pTab->nRef = 1; pTab->zName = 0; + pTab->nRowEst = 1000000; selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSelect); pTab->iPKey = -1; if( db->mallocFailed ){ sqlite3DeleteTable(db, pTab); @@ -1370,10 +1371,12 @@ if( sqlite3ExprIsInteger(p->pLimit, &n) ){ sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit); VdbeComment((v, "LIMIT counter")); if( n==0 ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak); + }else{ + if( p->nSelectRow > (double)n ) p->nSelectRow = (double)n; } }else{ sqlite3ExprCode(pParse, p->pLimit, iLimit); sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); VdbeComment((v, "LIMIT counter")); @@ -1501,10 +1504,11 @@ /* Create the destination temporary table if necessary */ if( dest.eDest==SRT_EphemTab ){ assert( p->pEList ); sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iParm, p->pEList->nExpr); + sqlite3VdbeChangeP5(v, BTREE_UNORDERED); dest.eDest = SRT_Table; } /* Make sure all SELECTs in the statement have the same number of elements ** in their result sets. @@ -1526,10 +1530,11 @@ /* Generate code for the left and right SELECT statements. */ switch( p->op ){ case TK_ALL: { int addr = 0; + int nLimit; assert( !pPrior->pLimit ); pPrior->pLimit = p->pLimit; pPrior->pOffset = p->pOffset; rc = sqlite3Select(pParse, pPrior, &dest); p->pLimit = 0; @@ -1546,10 +1551,17 @@ } rc = sqlite3Select(pParse, p, &dest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; p->pPrior = pPrior; + p->nSelectRow += pPrior->nSelectRow; + if( pPrior->pLimit + && sqlite3ExprIsInteger(pPrior->pLimit, &nLimit) + && p->nSelectRow > (double)nLimit + ){ + p->nSelectRow = (double)nLimit; + } if( addr ){ sqlite3VdbeJumpHere(v, addr); } break; } @@ -1616,10 +1628,11 @@ ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */ sqlite3ExprListDelete(db, p->pOrderBy); pDelete = p->pPrior; p->pPrior = pPrior; p->pOrderBy = 0; + if( p->op==TK_UNION ) p->nSelectRow += pPrior->nSelectRow; sqlite3ExprDelete(db, p->pLimit); p->pLimit = pLimit; p->pOffset = pOffset; p->iLimit = 0; p->iOffset = 0; @@ -1693,10 +1706,11 @@ intersectdest.iParm = tab2; rc = sqlite3Select(pParse, p, &intersectdest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; p->pPrior = pPrior; + if( p->nSelectRow>pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow; sqlite3ExprDelete(db, p->pLimit); p->pLimit = pLimit; p->pOffset = pOffset; /* Generate code to take the intersection of the two temporary @@ -2272,17 +2286,19 @@ }else{ addrEofA = sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd); sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA); + p->nSelectRow += pPrior->nSelectRow; } /* Generate a subroutine to run when the results from select B ** are exhausted and only data in select A remains. */ if( op==TK_INTERSECT ){ addrEofB = addrEofA; + if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow; }else{ VdbeNoopComment((v, "eof-B subroutine")); addrEofB = sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd); sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); @@ -3099,10 +3115,11 @@ pTab->nRef = 1; pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab); while( pSel->pPrior ){ pSel = pSel->pPrior; } selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol); pTab->iPKey = -1; + pTab->nRowEst = 1000000; pTab->tabFlags |= TF_Ephemeral; #endif }else{ /* An ordinary table or view name in the FROM clause */ assert( pFrom->pTab==0 ); @@ -3463,11 +3480,11 @@ ExprList *pList = pF->pExpr->x.pList; assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); if( pList ){ nArg = pList->nExpr; regAgg = sqlite3GetTempRange(pParse, nArg); - sqlite3ExprCodeExprList(pParse, pList, regAgg, 0); + sqlite3ExprCodeExprList(pParse, pList, regAgg, 1); }else{ nArg = 0; regAgg = 0; } if( pF->iDistinct>=0 ){ @@ -3515,10 +3532,36 @@ } pAggInfo->directMode = 0; sqlite3ExprCacheClear(pParse); } +/* +** Add a single OP_Explain instruction to the VDBE to explain a simple +** count(*) query ("SELECT count(*) FROM pTab"). +*/ +#ifndef SQLITE_OMIT_EXPLAIN +static void explainSimpleCount( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table being queried */ + Index *pIdx /* Index used to optimize scan, or NULL */ +){ + if( pParse->explain==2 ){ + char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s %s%s(~%d rows)", + pTab->zName, + pIdx ? "USING COVERING INDEX " : "", + pIdx ? pIdx->zName : "", + pTab->nRowEst + ); + sqlite3VdbeAddOp4( + pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC + ); + } +} +#else +# define explainSimpleCount(a,b,c) +#endif + /* ** Generate code for the SELECT statement given in the p argument. ** ** The results are distributed in various ways depending on the ** contents of the SelectDest structure pointed to by argument pDest @@ -3588,10 +3631,11 @@ Expr *pHaving; /* The HAVING clause. May be NULL */ int isDistinct; /* True if the DISTINCT keyword is present */ int distinct; /* Table to use for the distinct set */ int rc = 1; /* Value to return from this function */ int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */ + int addrDistinctIndex; /* Address of an OP_OpenEphemeral instruction */ AggInfo sAggInfo; /* Information used by aggregate queries */ int iEnd; /* Address of the end of the query */ sqlite3 *db; /* The database connection */ db = pParse->db; @@ -3656,10 +3700,11 @@ }else{ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); assert( pItem->isPopulated==0 ); sqlite3Select(pParse, pSub, &dest); pItem->isPopulated = 1; + pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow; } if( /*pParse->nErr ||*/ db->mallocFailed ){ goto select_end; } pParse->nHeight -= sqlite3SelectExprHeight(p); @@ -3705,21 +3750,10 @@ if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){ goto select_end; } #endif - /* If possible, rewrite the query to use GROUP BY instead of DISTINCT. - ** GROUP BY might use an index, DISTINCT never does. - */ - assert( p->pGroupBy==0 || (p->selFlags & SF_Aggregate)!=0 ); - if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ){ - p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0); - pGroupBy = p->pGroupBy; - p->selFlags &= ~SF_Distinct; - isDistinct = 0; - } - /* If there is both a GROUP BY and an ORDER BY clause and they are ** identical, then disable the ORDER BY clause since the GROUP BY ** will cause elements to come out in the correct order. This is ** an optimization - the correct answer should result regardless. ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER @@ -3727,10 +3761,34 @@ */ if( sqlite3ExprListCompare(p->pGroupBy, pOrderBy)==0 && (db->flags & SQLITE_GroupByOrder)==0 ){ pOrderBy = 0; } + + /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and + ** if the select-list is the same as the ORDER BY list, then this query + ** can be rewritten as a GROUP BY. In other words, this: + ** + ** SELECT DISTINCT xyz FROM ... ORDER BY xyz + ** + ** is transformed to: + ** + ** SELECT xyz FROM ... GROUP BY xyz + ** + ** The second form is preferred as a single index (or temp-table) may be + ** used for both the ORDER BY and DISTINCT processing. As originally + ** written the query must use a temp-table for at least one of the ORDER + ** BY and DISTINCT, and an index or separate temp-table for the other. + */ + if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct + && sqlite3ExprListCompare(pOrderBy, p->pEList)==0 + ){ + p->selFlags &= ~SF_Distinct; + p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0); + pGroupBy = p->pGroupBy; + pOrderBy = 0; + } /* If there is an ORDER BY clause, then this sorting ** index might end up being unused if the data can be ** extracted in pre-sorted order. If that is the case, then the ** OP_OpenEphemeral instruction will be changed to an OP_Noop once @@ -3756,32 +3814,34 @@ } /* Set the limiter. */ iEnd = sqlite3VdbeMakeLabel(v); + p->nSelectRow = (double)LARGEST_INT64; computeLimitRegisters(pParse, p, iEnd); /* Open a virtual index to use for the distinct set. */ - if( isDistinct ){ + if( p->selFlags & SF_Distinct ){ KeyInfo *pKeyInfo; - assert( isAgg || pGroupBy ); distinct = pParse->nTab++; pKeyInfo = keyInfoFromExprList(pParse, p->pEList); - sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0, - (char*)pKeyInfo, P4_KEYINFO_HANDOFF); + addrDistinctIndex = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0, + (char*)pKeyInfo, P4_KEYINFO_HANDOFF); + sqlite3VdbeChangeP5(v, BTREE_UNORDERED); }else{ distinct = -1; } /* Aggregate and non-aggregate queries are handled differently */ if( !isAgg && pGroupBy==0 ){ - /* This case is for non-aggregate queries - ** Begin the database scan - */ - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0); + ExprList *pDist = (isDistinct ? p->pEList : 0); + + /* Begin the database scan. */ + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, pDist, 0,0); if( pWInfo==0 ) goto select_end; + if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut; /* If sorting index that was created by a prior OP_OpenEphemeral ** instruction ended up not being needed, then change the OP_OpenEphemeral ** into an OP_Noop. */ @@ -3788,14 +3848,55 @@ if( addrSortIndex>=0 && pOrderBy==0 ){ sqlite3VdbeChangeToNoop(v, addrSortIndex, 1); p->addrOpenEphm[2] = -1; } - /* Use the standard inner loop - */ - assert(!isDistinct); - selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, -1, pDest, + if( pWInfo->eDistinct ){ + VdbeOp *pOp; /* No longer required OpenEphemeral instr. */ + + pOp = sqlite3VdbeGetOp(v, addrDistinctIndex); + + assert( isDistinct ); + assert( pWInfo->eDistinct==WHERE_DISTINCT_ORDERED + || pWInfo->eDistinct==WHERE_DISTINCT_UNIQUE + ); + distinct = -1; + if( pWInfo->eDistinct==WHERE_DISTINCT_ORDERED ){ + int iJump; + int iExpr; + int iFlag = ++pParse->nMem; + int iBase = pParse->nMem+1; + int iBase2 = iBase + pEList->nExpr; + pParse->nMem += (pEList->nExpr*2); + + /* Change the OP_OpenEphemeral coded earlier to an OP_Integer. The + ** OP_Integer initializes the "first row" flag. */ + pOp->opcode = OP_Integer; + pOp->p1 = 1; + pOp->p2 = iFlag; + + sqlite3ExprCodeExprList(pParse, pEList, iBase, 1); + iJump = sqlite3VdbeCurrentAddr(v) + 1 + pEList->nExpr + 1 + 1; + sqlite3VdbeAddOp2(v, OP_If, iFlag, iJump-1); + for(iExpr=0; iExprnExpr; iExpr++){ + CollSeq *pColl = sqlite3ExprCollSeq(pParse, pEList->a[iExpr].pExpr); + sqlite3VdbeAddOp3(v, OP_Ne, iBase+iExpr, iJump, iBase2+iExpr); + sqlite3VdbeChangeP4(v, -1, (const char *)pColl, P4_COLLSEQ); + sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); + } + sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iContinue); + + sqlite3VdbeAddOp2(v, OP_Integer, 0, iFlag); + assert( sqlite3VdbeCurrentAddr(v)==iJump ); + sqlite3VdbeAddOp3(v, OP_Move, iBase, iBase2, pEList->nExpr); + }else{ + pOp->opcode = OP_Noop; + } + } + + /* Use the standard inner loop. */ + selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, pDest, pWInfo->iContinue, pWInfo->iBreak); /* End the database scan loop. */ sqlite3WhereEnd(pWInfo); @@ -3822,10 +3923,13 @@ pItem->iAlias = 0; } for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){ pItem->iAlias = 0; } + if( p->nSelectRow>(double)100 ) p->nSelectRow = (double)100; + }else{ + p->nSelectRow = (double)1; } /* Create a label to jump to when we want to abort the query */ addrEnd = sqlite3VdbeMakeLabel(v); @@ -3898,11 +4002,11 @@ ** This might involve two separate loops with an OP_Sort in between, or ** it might be a single loop that uses an index to extract information ** in the right order to begin with. */ sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0, 0); if( pWInfo==0 ) goto select_end; if( pGroupBy==0 ){ /* The optimizer is able to deliver rows in group by order so ** we do not have to sort. The OP_OpenEphemeral table will be ** cancelled later because we still need to use the pKeyInfo @@ -4088,16 +4192,18 @@ ** does, then we can assume that it consumes less space on disk and ** will therefore be cheaper to scan to determine the query result. ** In this case set iRoot to the root page number of the index b-tree ** and pKeyInfo to the KeyInfo structure required to navigate the ** index. + ** + ** (2011-04-15) Do not do a full scan of an unordered index. ** ** In practice the KeyInfo structure will not be used. It is only ** passed to keep OP_OpenRead happy. */ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( !pBest || pIdx->nColumnnColumn ){ + if( pIdx->bUnordered==0 && (!pBest || pIdx->nColumnnColumn) ){ pBest = pIdx; } } if( pBest && pBest->nColumnnCol ){ iRoot = pBest->tnum; @@ -4109,10 +4215,11 @@ if( pKeyInfo ){ sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO_HANDOFF); } sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); sqlite3VdbeAddOp1(v, OP_Close, iCsr); + explainSimpleCount(pParse, pTab, pBest); }else #endif /* SQLITE_OMIT_BTREECOUNT */ { /* Check if the query is of one of the following forms: ** @@ -4154,11 +4261,11 @@ /* This case runs if the aggregate has no GROUP BY clause. The ** processing is much simpler since there is only a single row ** of output. */ resetAccumulator(pParse, &sAggInfo); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax,0,flag,0); if( pWInfo==0 ){ sqlite3ExprListDelete(db, pDel); goto select_end; } updateAccumulator(pParse, &sAggInfo); Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -1222,10 +1222,11 @@ int iPKey; /* If not negative, use aCol[iPKey] as the primary key */ int nCol; /* Number of columns in this table */ Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ int tnum; /* Root BTree node for this table (see note above) */ + unsigned nRowEst; /* Estimated rows in table - from sqlite_stat1 table */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ u16 nRef; /* Number of pointers to this Table */ u8 tabFlags; /* Mask of TF_* values */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ FKey *pFKey; /* Linked list of all foreign keys in this table */ @@ -1425,10 +1426,11 @@ unsigned *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ Table *pTable; /* The SQL table being indexed */ int tnum; /* Page containing root of this index in database file */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ + u8 bUnordered; /* Use this index for == or IN queries only */ char *zColAff; /* String defining the affinity of each column */ Index *pNext; /* The next index associated with the same table */ Schema *pSchema; /* Schema containing this index */ u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ char **azColl; /* Array of collation sequence names for index */ @@ -1790,10 +1792,14 @@ Table *pTab; /* An SQL table corresponding to zName */ Select *pSelect; /* A SELECT statement used in place of a table name */ u8 isPopulated; /* Temporary table associated with SELECT is populated */ u8 jointype; /* Type of join between this able and the previous */ u8 notIndexed; /* True if there is a NOT INDEXED clause */ + u8 isCorrelated; /* True if sub-query is correlated */ +#ifndef SQLITE_OMIT_EXPLAIN + u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */ +#endif int iCursor; /* The VDBE cursor number used to access this table */ Expr *pOn; /* The ON clause of a join */ IdList *pUsing; /* The USING clause of a join */ Bitmask colUsed; /* Bit N (1<" clause */ @@ -1828,10 +1834,11 @@ ** case that more than one of these conditions is true. */ struct WherePlan { u32 wsFlags; /* WHERE_* flags that describe the strategy */ u32 nEq; /* Number of == constraints */ + double nRow; /* Estimated number of rows (for EQP) */ union { Index *pIdx; /* Index when WHERE_INDEXED is true */ struct WhereTerm *pTerm; /* WHERE clause term for OR-search */ sqlite3_index_info *pVtabIdx; /* Virtual table index to use */ } u; @@ -1858,20 +1865,21 @@ int addrBrk; /* Jump here to break out of the loop */ int addrNxt; /* Jump here to start the next IN combination */ int addrCont; /* Jump here to continue with the next loop cycle */ int addrFirst; /* First instruction of interior of the loop */ u8 iFrom; /* Which entry in the FROM clause */ - u8 op, p5; /* Opcode and P5 of the opcode that ends the loop */ - int p1, p2; /* Operands of the opcode used to ends the loop */ + u8 op, p3, p5; /* Opcode, P3, and P5 of the end-of-loop instruction */ + int p1, p2; /* P1 and P2 operands of the end-of-loop instruction */ union { /* Information that depends on plan.wsFlags */ struct { int nIn; /* Number of entries in aInLoop[] */ struct InLoop { int iCur; /* The VDBE cursor used by this IN operator */ int addrInTop; /* Top of the IN loop */ } *aInLoop; /* Information about each nested IN operator */ } in; /* Used when plan.wsFlags&WHERE_IN_ABLE */ + Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */ } u; /* The following field is really not part of the current level. But ** we need a place to cache virtual table index information for each ** virtual table in the FROM clause and the WhereLevel structure is @@ -1905,20 +1913,25 @@ struct WhereInfo { Parse *pParse; /* Parsing and code generating context */ u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */ u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE or DELETE */ u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */ + u8 eDistinct; SrcList *pTabList; /* List of tables in the join */ int iTop; /* The very beginning of the WHERE loop */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ int nLevel; /* Number of nested loop */ struct WhereClause *pWC; /* Decomposition of the WHERE clause */ double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */ + double nRowOut; /* Estimated number of output rows */ WhereLevel a[1]; /* Information about each nest loop in WHERE */ }; +#define WHERE_DISTINCT_UNIQUE 1 +#define WHERE_DISTINCT_ORDERED 2 + /* ** A NameContext defines a context in which to resolve table and column ** names. The context consists of a list of tables (the pSrcList) field and ** a list of named expression (pEList). The named expression list may ** be NULL. The pSrc corresponds to the FROM clause of a SELECT or @@ -1987,10 +2000,11 @@ Select *pRightmost; /* Right-most select in a compound select statement */ Expr *pLimit; /* LIMIT expression. NULL means not used. */ Expr *pOffset; /* OFFSET expression. NULL means not used. */ int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */ + double nSelectRow; /* Estimated number of result rows */ }; /* ** Allowed values for Select.selFlags. The "SF" prefix stands for ** "Select Flag". @@ -2182,10 +2196,14 @@ Table **apVtabLock; /* Pointer to virtual tables needing locking */ #endif int nHeight; /* Expression tree height of current sub-select */ Table *pZombieTab; /* List of Table objects to delete after code gen */ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ +#ifndef SQLITE_OMIT_EXPLAIN + int iSelectId; /* Subquery ID for query planning */ + int iNextSelectId; /* Next available subquery ID */ +#endif }; #ifdef SQLITE_OMIT_VIRTUALTABLE #define IN_DECLARE_VTAB 0 #else @@ -2659,11 +2677,12 @@ #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) Expr *sqlite3LimitWhere(Parse *, SrcList *, Expr *, ExprList *, Expr *, Expr *, char *); #endif void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); -WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u16); +WhereInfo *sqlite3WhereBegin( + Parse*,SrcList*,Expr*,ExprList**,ExprList*,u16,int); void sqlite3WhereEnd(WhereInfo*); int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int); void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); void sqlite3ExprCodeMove(Parse*, int, int, int); void sqlite3ExprCodeCopy(Parse*, int, int, int); @@ -2671,11 +2690,10 @@ void sqlite3ExprCachePush(Parse*); void sqlite3ExprCachePop(Parse*, int); void sqlite3ExprCacheRemove(Parse*, int, int); void sqlite3ExprCacheClear(Parse*); void sqlite3ExprCacheAffinityChange(Parse*, int, int); -void sqlite3ExprHardCopy(Parse*,int,int); int sqlite3ExprCode(Parse*, Expr*, int); int sqlite3ExprCodeTemp(Parse*, Expr*, int*); int sqlite3ExprCodeTarget(Parse*, Expr*, int); int sqlite3ExprCodeAndCache(Parse*, Expr*, int); void sqlite3ExprCodeConstants(Parse*, Expr*); @@ -2791,12 +2809,10 @@ # define sqlite3AuthContextPush(a,b,c) # define sqlite3AuthContextPop(a) ((void)(a)) #endif void sqlite3Attach(Parse*, Expr*, Expr*, Expr*); void sqlite3Detach(Parse*, Expr*); -int sqlite3BtreeFactory(sqlite3 *db, const char *zFilename, - int omitJournal, int nCache, int flags, Btree **ppBtree); int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*); int sqlite3FixSrcList(DbFixer*, SrcList*); int sqlite3FixSelect(DbFixer*, Select*); int sqlite3FixExpr(DbFixer*, Expr*); int sqlite3FixExprList(DbFixer*, ExprList*); Index: src/test3.c ================================================================== --- src/test3.c +++ src/test3.c @@ -51,11 +51,11 @@ */ static sqlite3 sDb; static int nRefSqlite3 = 0; /* -** Usage: btree_open FILENAME NCACHE FLAGS +** Usage: btree_open FILENAME NCACHE ** ** Open a new database */ static int btree_open( void *NotUsed, @@ -62,26 +62,25 @@ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ const char **argv /* Text of each argument */ ){ Btree *pBt; - int rc, nCache, flags; + int rc, nCache; char zBuf[100]; - if( argc!=4 ){ + if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FILENAME NCACHE FLAGS\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR; - if( Tcl_GetInt(interp, argv[3], &flags) ) return TCL_ERROR; nRefSqlite3++; if( nRefSqlite3==1 ){ sDb.pVfs = sqlite3_vfs_find(0); sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); sqlite3_mutex_enter(sDb.mutex); } - rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, flags, + rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, 0, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } Index: src/test_config.c ================================================================== --- src/test_config.c +++ src/test_config.c @@ -466,10 +466,16 @@ #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY); #endif + +#ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT + Tcl_SetVar2(interp, "sqlite_options", "unique_enforcement", "0", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "unique_enforcement", "1", TCL_GLOBAL_ONLY); +#endif #ifdef SQLITE_OMIT_UTF16 Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "utf16", "1", TCL_GLOBAL_ONLY); Index: src/test_vfs.c ================================================================== --- src/test_vfs.c +++ src/test_vfs.c @@ -79,12 +79,10 @@ char *zName; /* Name of this VFS */ sqlite3_vfs *pParent; /* The VFS to use for file IO */ sqlite3_vfs *pVfs; /* The testvfs registered with SQLite */ Tcl_Interp *interp; /* Interpreter to run script in */ Tcl_Obj *pScript; /* Script to execute */ - int nScript; /* Number of elements in array apScript */ - Tcl_Obj **apScript; /* Array version of pScript */ TestvfsBuffer *pBuffer; /* List of shared buffers */ int isNoshm; int mask; /* Mask controlling [script] and [ioerr] */ @@ -266,52 +264,30 @@ Tcl_Obj *arg1, Tcl_Obj *arg2, Tcl_Obj *arg3 ){ int rc; /* Return code from Tcl_EvalObj() */ - int nArg; /* Elements in eval'd list */ - int nScript; - Tcl_Obj ** ap; - + Tcl_Obj *pEval; assert( p->pScript ); - if( !p->apScript ){ - int nByte; - int i; - if( TCL_OK!=Tcl_ListObjGetElements(p->interp, p->pScript, &nScript, &ap) ){ - Tcl_BackgroundError(p->interp); - Tcl_ResetResult(p->interp); - return; - } - p->nScript = nScript; - nByte = (nScript+TESTVFS_MAX_ARGS)*sizeof(Tcl_Obj *); - p->apScript = (Tcl_Obj **)ckalloc(nByte); - memset(p->apScript, 0, nByte); - for(i=0; iapScript[i] = ap[i]; - } - } - - p->apScript[p->nScript] = Tcl_NewStringObj(zMethod, -1); - p->apScript[p->nScript+1] = arg1; - p->apScript[p->nScript+2] = arg2; - p->apScript[p->nScript+3] = arg3; - - for(nArg=p->nScript; p->apScript[nArg]; nArg++){ - Tcl_IncrRefCount(p->apScript[nArg]); - } - - rc = Tcl_EvalObjv(p->interp, nArg, p->apScript, TCL_EVAL_GLOBAL); + assert( zMethod ); + assert( p ); + assert( arg2==0 || arg1!=0 ); + assert( arg3==0 || arg2!=0 ); + + pEval = Tcl_DuplicateObj(p->pScript); + Tcl_IncrRefCount(p->pScript); + Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zMethod, -1)); + if( arg1 ) Tcl_ListObjAppendElement(p->interp, pEval, arg1); + if( arg2 ) Tcl_ListObjAppendElement(p->interp, pEval, arg2); + if( arg3 ) Tcl_ListObjAppendElement(p->interp, pEval, arg3); + + rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); if( rc!=TCL_OK ){ Tcl_BackgroundError(p->interp); Tcl_ResetResult(p->interp); } - - for(nArg=p->nScript; p->apScript[nArg]; nArg++){ - Tcl_DecrRefCount(p->apScript[nArg]); - p->apScript[nArg] = 0; - } } /* ** Close an tvfs-file. @@ -1072,13 +1048,10 @@ case CMD_SCRIPT: { if( objc==3 ){ int nByte; if( p->pScript ){ Tcl_DecrRefCount(p->pScript); - ckfree((char *)p->apScript); - p->apScript = 0; - p->nScript = 0; p->pScript = 0; } Tcl_GetStringFromObj(objv[2], &nByte); if( nByte>0 ){ p->pScript = Tcl_DuplicateObj(objv[2]); @@ -1228,11 +1201,10 @@ static void testvfs_obj_del(ClientData cd){ Testvfs *p = (Testvfs *)cd; if( p->pScript ) Tcl_DecrRefCount(p->pScript); sqlite3_vfs_unregister(p->pVfs); - ckfree((char *)p->apScript); ckfree((char *)p->pVfs); ckfree((char *)p); } /* Index: src/update.c ================================================================== --- src/update.c +++ src/update.c @@ -310,11 +310,13 @@ } /* Begin the database scan */ sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0, WHERE_ONEPASS_DESIRED); + pWInfo = sqlite3WhereBegin( + pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0 + ); if( pWInfo==0 ) goto update_cleanup; okOnePass = pWInfo->okOnePass; /* Remember the rowid of every item to be updated. */ @@ -636,10 +638,11 @@ ** be stored. */ assert( v ); ephemTab = pParse->nTab++; sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0)); + sqlite3VdbeChangeP5(v, BTREE_UNORDERED); /* fill the ephemeral table */ sqlite3SelectDestInit(&dest, SRT_Table, ephemTab); sqlite3Select(pParse, pSelect, &dest); Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -44,10 +44,21 @@ ** commenting and indentation practices when changing or adding code. */ #include "sqliteInt.h" #include "vdbeInt.h" +/* +** Invoke this macro on memory cells just prior to changing the +** value of the cell. This macro verifies that shallow copies are +** not misused. +*/ +#ifdef SQLITE_DEBUG +# define memAboutToChange(P,M) sqlite3VdbeMemPrepareToChange(P,M) +#else +# define memAboutToChange(P,M) +#endif + /* ** The following global variable is incremented every time a cursor ** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes. The test ** procedures use this information to make sure that indices are ** working correctly. This variable has no function other than to @@ -665,38 +676,44 @@ assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] ); if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); pOut = &aMem[pOp->p2]; + memAboutToChange(p, pOut); sqlite3VdbeMemReleaseExternal(pOut); pOut->flags = MEM_Int; } /* Sanity checking on other operands */ #ifdef SQLITE_DEBUG if( (pOp->opflags & OPFLG_IN1)!=0 ){ assert( pOp->p1>0 ); assert( pOp->p1<=p->nMem ); + assert( memIsValid(&aMem[pOp->p1]) ); REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]); } if( (pOp->opflags & OPFLG_IN2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); + assert( memIsValid(&aMem[pOp->p2]) ); REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]); } if( (pOp->opflags & OPFLG_IN3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=p->nMem ); + assert( memIsValid(&aMem[pOp->p3]) ); REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]); } if( (pOp->opflags & OPFLG_OUT2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); + memAboutToChange(p, &aMem[pOp->p2]); } if( (pOp->opflags & OPFLG_OUT3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=p->nMem ); + memAboutToChange(p, &aMem[pOp->p3]); } #endif switch( pOp->opcode ){ @@ -754,10 +771,11 @@ ** and then jump to address P2. */ case OP_Gosub: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( (pIn1->flags & MEM_Dyn)==0 ); + memAboutToChange(p, pIn1); pIn1->flags = MEM_Int; pIn1->u.i = pc; REGISTER_TRACE(pOp->p1, pIn1); pc = pOp->p2 - 1; break; @@ -1017,10 +1035,12 @@ pIn1 = &aMem[p1]; pOut = &aMem[p2]; while( n-- ){ assert( pOut<=&aMem[p->nMem] ); assert( pIn1<=&aMem[p->nMem] ); + assert( memIsValid(pIn1) ); + memAboutToChange(p, pOut); zMalloc = pOut->zMalloc; pOut->zMalloc = 0; sqlite3VdbeMemMove(pOut, pIn1); pIn1->zMalloc = zMalloc; REGISTER_TRACE(p2++, pOut); @@ -1062,10 +1082,13 @@ case OP_SCopy: { /* in1, out2 */ pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p2]; assert( pOut!=pIn1 ); sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); +#ifdef SQLITE_DEBUG + if( pOut->pScopyFrom==0 ) pOut->pScopyFrom = pIn1; +#endif REGISTER_TRACE(pOp->p2, pOut); break; } /* Opcode: ResultRow P1 P2 * * * @@ -1120,10 +1143,14 @@ ** and have an assigned type. The results are de-ephemeralized as ** as side effect. */ pMem = p->pResultSet = &aMem[pOp->p1]; for(i=0; ip2; i++){ + assert( memIsValid(&pMem[i]) ); + Deephemeralize(&pMem[i]); + assert( (pMem[i].flags & MEM_Ephem)==0 + || (pMem[i].flags & (MEM_Str|MEM_Blob))==0 ); sqlite3VdbeMemNulTerminate(&pMem[i]); sqlite3VdbeMemStoreType(&pMem[i]); REGISTER_TRACE(pOp->p1+i, &pMem[i]); } if( db->mallocFailed ) goto no_mem; @@ -1345,16 +1372,21 @@ int n; n = pOp->p5; apVal = p->apArg; assert( apVal || n==0 ); + assert( pOp->p3>0 && pOp->p3<=p->nMem ); + pOut = &aMem[pOp->p3]; + memAboutToChange(p, pOut); assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) ); assert( pOp->p3p2 || pOp->p3>=pOp->p2+n ); pArg = &aMem[pOp->p2]; for(i=0; ip2+i, pArg); } assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC ); @@ -1364,12 +1396,10 @@ }else{ ctx.pVdbeFunc = (VdbeFunc*)pOp->p4.pVdbeFunc; ctx.pFunc = ctx.pVdbeFunc->pFunc; } - assert( pOp->p3>0 && pOp->p3<=p->nMem ); - pOut = &aMem[pOp->p3]; ctx.s.flags = MEM_Null; ctx.s.db = db; ctx.s.xDel = 0; ctx.s.zMalloc = 0; @@ -1485,10 +1515,11 @@ ** ** To force any register to be an integer, just add 0. */ case OP_AddImm: { /* in1 */ pIn1 = &aMem[pOp->p1]; + memAboutToChange(p, pIn1); sqlite3VdbeMemIntegerify(pIn1); pIn1->u.i += pOp->p2; break; } @@ -1499,10 +1530,11 @@ ** without data loss, then jump immediately to P2, or if P2==0 ** raise an SQLITE_MISMATCH exception. */ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; + memAboutToChange(p, pIn1); applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); if( (pIn1->flags & MEM_Int)==0 ){ if( pOp->p2==0 ){ rc = SQLITE_MISMATCH; goto abort_due_to_error; @@ -1525,10 +1557,11 @@ ** integers, for space efficiency, but after extraction we want them ** to have only a real value. */ case OP_RealAffinity: { /* in1 */ pIn1 = &aMem[pOp->p1]; + memAboutToChange(p, pIn1); if( pIn1->flags & MEM_Int ){ sqlite3VdbeMemRealify(pIn1); } break; } @@ -1544,10 +1577,11 @@ ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToText: { /* same as TK_TO_TEXT, in1 */ pIn1 = &aMem[pOp->p1]; + memAboutToChange(p, pIn1); if( pIn1->flags & MEM_Null ) break; assert( MEM_Str==(MEM_Blob>>3) ); pIn1->flags |= (pIn1->flags&MEM_Blob)>>3; applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding); rc = ExpandBlob(pIn1); @@ -1566,10 +1600,11 @@ ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */ pIn1 = &aMem[pOp->p1]; + memAboutToChange(p, pIn1); if( pIn1->flags & MEM_Null ) break; if( (pIn1->flags & MEM_Blob)==0 ){ applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding); assert( pIn1->flags & MEM_Str || db->mallocFailed ); MemSetTypeFlag(pIn1, MEM_Blob); @@ -1590,10 +1625,11 @@ ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */ pIn1 = &aMem[pOp->p1]; + memAboutToChange(p, pIn1); if( (pIn1->flags & (MEM_Null|MEM_Int|MEM_Real))==0 ){ sqlite3VdbeMemNumerify(pIn1); } break; } @@ -1608,10 +1644,11 @@ ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToInt: { /* same as TK_TO_INT, in1 */ pIn1 = &aMem[pOp->p1]; + memAboutToChange(p, pIn1); if( (pIn1->flags & MEM_Null)==0 ){ sqlite3VdbeMemIntegerify(pIn1); } break; } @@ -1626,10 +1663,11 @@ ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ pIn1 = &aMem[pOp->p1]; + memAboutToChange(p, pIn1); if( (pIn1->flags & MEM_Null)==0 ){ sqlite3VdbeMemRealify(pIn1); } break; } @@ -1718,10 +1756,12 @@ u16 flags1; /* Copy of initial value of pIn1->flags */ u16 flags3; /* Copy of initial value of pIn3->flags */ pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; + memAboutToChange(p, pIn1); + memAboutToChange(p, pIn3); flags1 = pIn1->flags; flags3 = pIn3->flags; if( (pIn1->flags | pIn3->flags)&MEM_Null ){ /* One or both operands are NULL */ if( pOp->p5 & SQLITE_NULLEQ ){ @@ -1768,10 +1808,11 @@ default: res = res>=0; break; } if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; + memAboutToChange(p, pOut); MemSetTypeFlag(pOut, MEM_Int); pOut->u.i = res; REGISTER_TRACE(pOp->p2, pOut); }else if( res ){ pc = pOp->p2-1; @@ -1840,10 +1881,12 @@ assert( p2>0 && p2+n<=p->nMem+1 ); } #endif /* SQLITE_DEBUG */ for(i=0; inField ); pColl = pKeyInfo->aColl[i]; bRev = pKeyInfo->aSortOrder[i]; @@ -2065,10 +2108,11 @@ pC = 0; memset(&sMem, 0, sizeof(sMem)); assert( p1nCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); pDest = &aMem[pOp->p3]; + memAboutToChange(p, pDest); MemSetTypeFlag(pDest, MEM_Null); zRec = 0; /* This block sets the variable payloadSize to be the total number of ** bytes in the record. @@ -2112,10 +2156,11 @@ assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ } }else if( pC->pseudoTableReg>0 ){ pReg = &aMem[pC->pseudoTableReg]; assert( pReg->flags & MEM_Blob ); + assert( memIsValid(pReg) ); payloadSize = pReg->n; zRec = pReg->z; pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; assert( payloadSize==0 || zRec!=0 ); }else{ @@ -2334,10 +2379,12 @@ assert( zAffinity!=0 ); assert( zAffinity[pOp->p2]==0 ); pIn1 = &aMem[pOp->p1]; while( (cAff = *(zAffinity++))!=0 ){ assert( pIn1 <= &p->aMem[p->nMem] ); + assert( memIsValid(pIn1) ); + memAboutToChange(p, pIn1); ExpandBlob(pIn1); applyAffinity(pIn1, cAff, encoding); pIn1++; } break; @@ -2402,16 +2449,23 @@ assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem+1 ); pData0 = &aMem[nField]; nField = pOp->p2; pLast = &pData0[nField-1]; file_format = p->minWriteFileFormat; + + /* Identify the output register */ + assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); + pOut = &aMem[pOp->p3]; + memAboutToChange(p, pOut); /* Loop through the elements that will make up the record to figure ** out how much space is required for the new record. */ for(pRec=pData0; pRec<=pLast; pRec++){ + assert( memIsValid(pRec) ); if( zAffinity ){ + memAboutToChange(p, pRec); applyAffinity(pRec, zAffinity[pRec-pData0], encoding); } if( pRec->flags&MEM_Zero && pRec->n>0 ){ sqlite3VdbeMemExpandBlob(pRec); } @@ -2441,12 +2495,10 @@ /* Make sure the output register has a buffer large enough to store ** the new record. The output register (pOp->p3) is not allowed to ** be one of the input registers (because the following call to ** sqlite3VdbeMemGrow() could clobber the value before it is used). */ - assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); - pOut = &aMem[pOp->p3]; if( sqlite3VdbeMemGrow(pOut, (int)nByte, 0) ){ goto no_mem; } zNewRecord = (u8 *)pOut->z; @@ -2989,10 +3041,12 @@ } if( pOp->p5 ){ assert( p2>0 ); assert( p2<=p->nMem ); pIn2 = &aMem[p2]; + assert( memIsValid(pIn2) ); + assert( (pIn2->flags & MEM_Int)!=0 ); sqlite3VdbeMemIntegerify(pIn2); p2 = (int)pIn2->u.i; /* The p2 value always comes from a prior OP_CreateTable opcode and ** that opcode will always set the p2 value to 2 or more or else fail. ** If there were a failure, the prepared statement would have halted @@ -3011,10 +3065,11 @@ } assert( pOp->p1>=0 ); pCur = allocateCursor(p, pOp->p1, nField, iDb, 1); if( pCur==0 ) goto no_mem; pCur->nullRow = 1; + pCur->isOrdered = 1; rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor); pCur->pKeyInfo = pKeyInfo; /* Since it performs no memory allocation or IO, the only values that ** sqlite3BtreeCursor() may return are SQLITE_EMPTY and SQLITE_OK. @@ -3061,11 +3116,11 @@ ** indices in joins. */ case OP_OpenAutoindex: case OP_OpenEphemeral: { VdbeCursor *pCx; - static const int openFlags = + static const int vfsFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TRANSIENT_DB; @@ -3072,25 +3127,25 @@ assert( pOp->p1>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; - rc = sqlite3BtreeFactory(db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, openFlags, - &pCx->pBt); + rc = sqlite3BtreeOpen(0, db, &pCx->pBt, + BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); if( rc==SQLITE_OK ){ rc = sqlite3BtreeBeginTrans(pCx->pBt, 1); } if( rc==SQLITE_OK ){ /* If a transient index is required, create it by calling - ** sqlite3BtreeCreateTable() with the BTREE_ZERODATA flag before + ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before ** opening it. If a transient table is required, just use the - ** automatically created table with root-page 1 (an INTKEY table). + ** automatically created table with root-page 1 (an BLOB_INTKEY table). */ if( pOp->p4.pKeyInfo ){ int pgno; assert( pOp->p4type==P4_KEYINFO ); - rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_ZERODATA); + rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY); if( rc==SQLITE_OK ){ assert( pgno==MASTER_ROOT+1 ); rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, (KeyInfo*)pOp->p4.z, pCx->pCursor); pCx->pKeyInfo = pOp->p4.pKeyInfo; @@ -3100,10 +3155,11 @@ }else{ rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, pCx->pCursor); pCx->isTable = 1; } } + pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); pCx->isIndex = !pCx->isTable; break; } /* Opcode: OpenPseudo P1 P2 P3 * * @@ -3215,10 +3271,11 @@ assert( pC!=0 ); assert( pC->pseudoTableReg==0 ); assert( OP_SeekLe == OP_SeekLt+1 ); assert( OP_SeekGe == OP_SeekLt+2 ); assert( OP_SeekGt == OP_SeekLt+3 ); + assert( pC->isOrdered ); if( pC->pCursor!=0 ){ oc = pOp->opcode; pC->nullRow = 0; if( pC->isTable ){ /* The input value in P3 might be of any type: integer, real, string, @@ -3297,10 +3354,13 @@ assert( oc!=OP_SeekLe || r.flags==UNPACKED_INCRKEY ); assert( oc!=OP_SeekGe || r.flags==0 ); assert( oc!=OP_SeekLt || r.flags==0 ); r.aMem = &aMem[pOp->p3]; +#ifdef SQLITE_DEBUG + { int i; for(i=0; ipCursor, &r, 0, 0, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } @@ -3311,19 +3371,21 @@ #ifdef SQLITE_TEST sqlite3_search_count++; #endif if( oc>=OP_SeekGe ){ assert( oc==OP_SeekGe || oc==OP_SeekGt ); if( res<0 || (res==0 && oc==OP_SeekGt) ){ + res = 0; rc = sqlite3BtreeNext(pC->pCursor, &res); if( rc!=SQLITE_OK ) goto abort_due_to_error; pC->rowidIsValid = 0; }else{ res = 0; } }else{ assert( oc==OP_SeekLt || oc==OP_SeekLe ); if( res>0 || (res==0 && oc==OP_SeekLt) ){ + 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 @@ -3421,15 +3483,18 @@ assert( pC->isTable==0 ); if( pOp->p4.i>0 ){ r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p4.i; r.aMem = pIn3; +#ifdef SQLITE_DEBUG + { int i; for(i=0; iflags & MEM_Blob ); - ExpandBlob(pIn3); + assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */ pIdxKey = sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, aTempRec, sizeof(aTempRec)); if( pIdxKey==0 ){ goto no_mem; } @@ -3518,10 +3583,13 @@ /* Populate the index search key. */ r.pKeyInfo = pCx->pKeyInfo; r.nField = nField + 1; r.flags = UNPACKED_PREFIX_SEARCH; r.aMem = aMx; +#ifdef SQLITE_DEBUG + { int i; for(i=0; iu.i; @@ -3694,11 +3762,13 @@ pMem = &pFrame->aMem[pOp->p3]; }else{ /* Assert that P3 is a valid memory cell. */ assert( pOp->p3<=p->nMem ); pMem = &aMem[pOp->p3]; + memAboutToChange(p, pMem); } + assert( memIsValid(pMem) ); REGISTER_TRACE(pOp->p3, pMem); sqlite3VdbeMemIntegerify(pMem); assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){ @@ -3803,10 +3873,11 @@ const char *zTbl; /* Table name - used by the opdate hook */ int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ pData = &aMem[pOp->p2]; assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( memIsValid(pData) ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->pCursor!=0 ); assert( pC->pseudoTableReg==0 ); assert( pC->isTable ); @@ -3813,10 +3884,11 @@ REGISTER_TRACE(pOp->p2, pData); if( pOp->opcode==OP_Insert ){ pKey = &aMem[pOp->p3]; assert( pKey->flags & MEM_Int ); + assert( memIsValid(pKey) ); REGISTER_TRACE(pOp->p3, pKey); iKey = pKey->u.i; }else{ assert( pOp->opcode==OP_InsertInt ); iKey = pOp->p3; @@ -3960,10 +4032,11 @@ BtCursor *pCrsr; u32 n; i64 n64; pOut = &aMem[pOp->p2]; + memAboutToChange(p, pOut); /* Note that RowKey and RowData are really exactly the same instruction */ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC->isTable || pOp->opcode==OP_RowKey ); @@ -4164,11 +4237,11 @@ pc = pOp->p2 - 1; } break; } -/* Opcode: Next P1 P2 * * P5 +/* Opcode: Next P1 P2 P3 * P5 ** ** Advance cursor P1 so that it points to the next key/data pair in its ** table or index. If there are no more key/value pairs then fall through ** to the following instruction. But if the cursor advance was successful, ** jump immediately to P2. @@ -4176,13 +4249,18 @@ ** The P1 cursor must be for a real table, not a pseudo-table. ** ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. ** +** The P3 value is a hint to the btree implementation. If P3==1, that +** means P1 is an SQL index and that this instruction could have been +** omitted if that index had been unique. P3 is usually 0. P3 is +** always either 0 or 1. +** ** See also: Prev */ -/* Opcode: Prev P1 P2 * * P5 +/* Opcode: Prev P1 P2 P3 * P5 ** ** Back up cursor P1 so that it points to the previous key/data pair in its ** table or index. If there is no previous key/value pairs then fall through ** to the following instruction. But if the cursor backup was successful, ** jump immediately to P2. @@ -4189,10 +4267,15 @@ ** ** The P1 cursor must be for a real table, not a pseudo-table. ** ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. +** +** The P3 value is a hint to the btree implementation. If P3==1, that +** means P1 is an SQL index and that this instruction could have been +** omitted if that index had been unique. P3 is usually 0. P3 is +** always either 0 or 1. */ case OP_Prev: /* jump */ case OP_Next: { /* jump */ VdbeCursor *pC; BtCursor *pCrsr; @@ -4199,20 +4282,24 @@ int res; CHECK_FOR_INTERRUPT; assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p5<=ArraySize(p->aCounter) ); + assert( pOp->p3==0 || pOp->p3==1 ); pC = p->apCsr[pOp->p1]; if( pC==0 ){ break; /* See ticket #2273 */ } pCrsr = pC->pCursor; if( pCrsr==0 ){ pC->nullRow = 1; break; } - res = 1; + res = pOp->p3; + assert( res==0 || pC->isIndex==1 ); + testcase( res==1 ); + testcase( res==0 ); assert( pC->deferredMoveto==0 ); rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(pCrsr, &res) : sqlite3BtreePrevious(pCrsr, &res); pC->nullRow = (u8)res; pC->cacheStatus = CACHE_STALE; @@ -4288,10 +4375,13 @@ if( ALWAYS(pCrsr!=0) ){ r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p3; r.flags = 0; r.aMem = &aMem[pOp->p2]; +#ifdef SQLITE_DEBUG + { int i; for(i=0; ideferredMoveto==0 ); @@ -4368,10 +4458,11 @@ UnpackedRecord r; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); + assert( pC->isOrdered ); if( ALWAYS(pC->pCursor!=0) ){ assert( pC->deferredMoveto==0 ); assert( pOp->p5==0 || pOp->p5==1 ); assert( pOp->p4type==P4_INT32 ); r.pKeyInfo = pC->pKeyInfo; @@ -4380,10 +4471,13 @@ r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID; }else{ r.flags = UNPACKED_IGNORE_ROWID; } r.aMem = &aMem[pOp->p3]; +#ifdef SQLITE_DEBUG + { int i; for(i=0; iopcode==OP_IdxLT ){ res = -res; }else{ assert( pOp->opcode==OP_IdxGE ); @@ -4479,10 +4573,12 @@ db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) ); if( pOp->p3 ){ p->nChange += nChange; if( pOp->p3>0 ){ + assert( memIsValid(&aMem[pOp->p3]) ); + memAboutToChange(p, &aMem[pOp->p3]); aMem[pOp->p3].u.i += nChange; } } break; } @@ -4520,13 +4616,13 @@ assert( (p->btreeMask & (1<p1))!=0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ /* flags = BTREE_INTKEY; */ - flags = BTREE_LEAFDATA|BTREE_INTKEY; + flags = BTREE_INTKEY; }else{ - flags = BTREE_ZERODATA; + flags = BTREE_BLOBKEY; } rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, flags); pOut->u.i = pgno; break; } @@ -4841,10 +4937,11 @@ SubProgram *pProgram; /* Sub-program to execute */ void *t; /* Token identifying trigger */ pProgram = pOp->p4.pProgram; pRt = &aMem[pOp->p3]; + assert( memIsValid(pRt) ); assert( pProgram->nOp>0 ); /* If the p5 flag is clear, then recursive invocation of triggers is ** disabled for backwards compatibility (p5 is set if this sub-program ** is really a trigger, not a foreign key action, and the flag set @@ -5010,10 +5107,11 @@ for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); pIn1 = &pFrame->aMem[pOp->p1]; }else{ pIn1 = &aMem[pOp->p1]; } + assert( memIsValid(pIn1) ); sqlite3VdbeMemIntegerify(pIn1); pIn2 = &aMem[pOp->p2]; sqlite3VdbeMemIntegerify(pIn2); if( pIn1->u.iu.i){ pIn1->u.i = pIn2->u.i; @@ -5094,11 +5192,13 @@ assert( n>=0 ); pRec = &aMem[pOp->p2]; apVal = p->apArg; assert( apVal || n==0 ); for(i=0; ip4.pFunc; assert( pOp->p3>0 && pOp->p3<=p->nMem ); ctx.pMem = pMem = &aMem[pOp->p3]; @@ -5489,10 +5589,11 @@ Mem **apArg; pQuery = &aMem[pOp->p3]; pArgc = &pQuery[1]; pCur = p->apCsr[pOp->p1]; + assert( memIsValid(pQuery) ); REGISTER_TRACE(pOp->p3, pQuery); assert( pCur->pVtabCursor ); pVtabCursor = pCur->pVtabCursor; pVtab = pVtabCursor->pVtab; pModule = pVtab->pModule; @@ -5544,10 +5645,11 @@ VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur->pVtabCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); pDest = &aMem[pOp->p3]; + memAboutToChange(p, pDest); if( pCur->nullRow ){ sqlite3VdbeMemSetNull(pDest); break; } pVtab = pCur->pVtabCursor->pVtab; @@ -5642,10 +5744,11 @@ Mem *pName; pVtab = pOp->p4.pVtab->pVtab; pName = &aMem[pOp->p1]; assert( pVtab->pModule->xRename ); + assert( memIsValid(pName) ); REGISTER_TRACE(pOp->p1, pName); assert( pName->flags & MEM_Str ); rc = pVtab->pModule->xRename(pVtab, pName->z); importVtabErrMsg(p, pVtab); @@ -5692,10 +5795,12 @@ assert( pOp->p4type==P4_VTAB ); if( ALWAYS(pModule->xUpdate) ){ apArg = p->apArg; pX = &aMem[pOp->p3]; for(i=0; ixUpdate(pVtab, nArg, apArg, &rowid); Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -55,10 +55,11 @@ Bool useRandomRowid; /* Generate new record numbers semi-randomly */ Bool nullRow; /* True if pointing to a row with no data */ Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ Bool isTable; /* True if a table requiring integer keys */ Bool isIndex; /* True if an index containing keys only - no data */ + Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ Btree *pBt; /* Separate file holding temporary table */ int pseudoTableReg; /* Register holding pseudotable content. */ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ int nField; /* Number of fields in the header */ @@ -149,10 +150,14 @@ char *z; /* String or BLOB value */ int n; /* Number of characters in string value, excluding '\0' */ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */ u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ +#ifdef SQLITE_DEBUG + Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ + void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */ +#endif void (*xDel)(void *); /* If not null, call this function to delete Mem.z */ char *zMalloc; /* Dynamic buffer allocated by sqlite3_malloc() */ }; /* One or more of the following flags are set to indicate the validOK @@ -175,10 +180,11 @@ #define MEM_Int 0x0004 /* Value is an integer */ #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_RowSet 0x0020 /* Value is a RowSet object */ #define MEM_Frame 0x0040 /* Value is a VdbeFrame object */ +#define MEM_Invalid 0x0080 /* Value is undefined */ #define MEM_TypeMask 0x00ff /* Mask of type bits */ /* Whenever Mem contains a valid string or blob representation, one of ** the following flags must be set to determine the memory management ** policy for Mem.z. The MEM_Term flag tells us whether or not the @@ -188,23 +194,29 @@ #define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */ #define MEM_Static 0x0800 /* Mem.z points to a static string */ #define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ #define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ #define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */ - #ifdef SQLITE_OMIT_INCRBLOB #undef MEM_Zero #define MEM_Zero 0x0000 #endif - /* ** Clear any existing type flags from a Mem and replace them with f */ #define MemSetTypeFlag(p, f) \ ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) +/* +** Return true if a memory cell is not marked as invalid. This macro +** is for use inside assert() statements only. +*/ +#ifdef SQLITE_DEBUG +#define memIsValid(M) ((M)->flags & MEM_Invalid)==0 +#endif + /* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains ** additional information about auxiliary information bound to arguments ** of the function. This is used to implement the sqlite3_get_auxdata() ** and sqlite3_set_auxdata() APIs. The "auxdata" is some auxiliary data @@ -388,10 +400,14 @@ int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); int sqlite3VdbeCloseStatement(Vdbe *, int); void sqlite3VdbeFrameDelete(VdbeFrame*); int sqlite3VdbeFrameRestore(VdbeFrame *); void sqlite3VdbeMemStoreType(Mem *pMem); + +#ifdef SQLITE_DEBUG +void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*); +#endif #ifndef SQLITE_OMIT_FOREIGN_KEY int sqlite3VdbeCheckFk(Vdbe *, int); #else # define sqlite3VdbeCheckFk(p,i) 0 Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -1180,16 +1180,14 @@ pMem->flags = MEM_Int; pMem->u.i = pOp->p2; /* P2 */ pMem->type = SQLITE_INTEGER; pMem++; - if( p->explain==1 ){ - pMem->flags = MEM_Int; - pMem->u.i = pOp->p3; /* P3 */ - pMem->type = SQLITE_INTEGER; - pMem++; - } + pMem->flags = MEM_Int; + pMem->u.i = pOp->p3; /* P3 */ + pMem->type = SQLITE_INTEGER; + pMem++; if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */ assert( p->db->mallocFailed ); return SQLITE_ERROR; } @@ -1230,11 +1228,11 @@ pMem->flags = MEM_Null; /* Comment */ pMem->type = SQLITE_NULL; } } - p->nResColumn = 8 - 5*(p->explain-1); + p->nResColumn = 8 - 4*(p->explain-1); p->rc = SQLITE_OK; rc = SQLITE_ROW; } return rc; } Index: src/vdbemem.c ================================================================== --- src/vdbemem.c +++ src/vdbemem.c @@ -130,10 +130,13 @@ return SQLITE_NOMEM; } pMem->z[pMem->n] = 0; pMem->z[pMem->n+1] = 0; pMem->flags |= MEM_Term; +#ifdef SQLITE_DEBUG + pMem->pScopyFrom = 0; +#endif } return SQLITE_OK; } @@ -591,10 +594,32 @@ return n>p->db->aLimit[SQLITE_LIMIT_LENGTH]; } return 0; } +#ifdef SQLITE_DEBUG +/* +** This routine prepares a memory cell for modication by breaking +** its link to a shallow copy and by marking any current shallow +** copies of this cell as invalid. +** +** This is used for testing and debugging only - to make sure shallow +** copies are not misused. +*/ +void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){ + int i; + Mem *pX; + for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){ + if( pX->pScopyFrom==pMem ){ + pX->flags |= MEM_Invalid; + pX->pScopyFrom = 0; + } + } + pMem->pScopyFrom = 0; +} +#endif /* SQLITE_DEBUG */ + /* ** Size of struct Mem not including the Mem.zMalloc member. */ #define MEMCELLSIZE (size_t)(&(((Mem *)0)->zMalloc)) @@ -1049,10 +1074,13 @@ if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){ pVal->u.i = -1 * pVal->u.i; /* (double)-1 In case of SQLITE_OMIT_FLOATING_POINT... */ pVal->r = (double)-1 * pVal->r; } + }else if( op==TK_NULL ){ + pVal = sqlite3ValueNew(db); + if( pVal==0 ) goto no_mem; } #ifndef SQLITE_OMIT_BLOB_LITERAL else if( op==TK_BLOB ){ int nVal; assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -15,10 +15,11 @@ ** rows. Indices are selected and used to speed the search when doing ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". */ #include "sqliteInt.h" + /* ** Trace output macros */ #if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) @@ -115,10 +116,15 @@ #define TERM_CODED 0x04 /* This term is already coded */ #define TERM_COPIED 0x08 /* Has a child */ #define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */ #define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */ #define TERM_OR_OK 0x40 /* Used during OR-clause processing */ +#ifdef SQLITE_ENABLE_STAT2 +# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */ +#else +# define TERM_VNULL 0x00 /* Disabled if not using stat2 */ +#endif /* ** An instance of the following structure holds all information about a ** WHERE clause. Mostly this is a container for one or more WhereTerms. */ @@ -190,11 +196,10 @@ ** cost of pursuing that strategy. */ struct WhereCost { WherePlan plan; /* The lookup strategy */ double rCost; /* Overall cost of pursuing this search strategy */ - double nRow; /* Estimated number of output rows */ Bitmask used; /* Bitmask of cursors used by this plan */ }; /* ** Bitmasks for the operators that indices are able to exploit. An @@ -209,10 +214,11 @@ #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) #define WO_MATCH 0x040 #define WO_ISNULL 0x080 #define WO_OR 0x100 /* Two or more OR-connected terms */ #define WO_AND 0x200 /* Two or more AND-connected terms */ +#define WO_NOOP 0x800 /* This term does not restrict search space */ #define WO_ALL 0xfff /* Mask of all possible WO_* values */ #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ /* @@ -233,21 +239,24 @@ #define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */ #define WHERE_COLUMN_RANGE 0x00020000 /* xEXPR */ #define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */ #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ -#define WHERE_NOT_FULLSCAN 0x000f3000 /* Does not do a full table scan */ +#define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */ #define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */ #define WHERE_TOP_LIMIT 0x00100000 /* xEXPR or x>=EXPR constraint */ +#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and xop2; } if( op==TK_VARIABLE ){ Vdbe *pReprepare = pParse->pReprepare; - pVal = sqlite3VdbeGetValue(pReprepare, pRight->iColumn, SQLITE_AFF_NONE); + int iCol = pRight->iColumn; + pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE); if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ z = (char *)sqlite3_value_text(pVal); } - sqlite3VdbeSetVarmask(pParse->pVdbe, pRight->iColumn); + sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-23257-02778 */ assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); }else if( op==TK_STRING ){ z = pRight->u.zToken; } if( z ){ @@ -689,11 +699,11 @@ pPrefix = sqlite3Expr(db, TK_STRING, z); if( pPrefix ) pPrefix->u.zToken[cnt] = 0; *ppPrefix = pPrefix; if( op==TK_VARIABLE ){ Vdbe *v = pParse->pVdbe; - sqlite3VdbeSetVarmask(v, pRight->iColumn); + sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-23257-02778 */ if( *pisComplete && pRight->u.zToken[1] ){ /* If the rhs of the LIKE expression is a variable, and the current ** value of the variable means there is no need to invoke the LIKE ** function, then no OP_Variable will be added to the program. ** This causes problems for the sqlite3_bind_parameter_name() @@ -1057,11 +1067,11 @@ pWC->a[idxNew].iParent = idxTerm; pTerm->nChild = 1; }else{ sqlite3ExprListDelete(db, pList); } - pTerm->eOperator = 0; /* case 1 trumps case 2 */ + pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ } } } #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ @@ -1321,10 +1331,51 @@ pNewTerm->prereqAll = pTerm->prereqAll; } } #endif /* SQLITE_OMIT_VIRTUALTABLE */ +#ifdef SQLITE_ENABLE_STAT2 + /* When sqlite_stat2 histogram data is available an operator of the + ** form "x IS NOT NULL" can sometimes be evaluated more efficiently + ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a + ** virtual term of that form. + ** + ** Note that the virtual term must be tagged with TERM_VNULL. This + ** TERM_VNULL tag will suppress the not-null check at the beginning + ** of the loop. Without the TERM_VNULL flag, the not-null check at + ** the start of the loop will prevent any results from being returned. + */ + if( pExpr->op==TK_NOTNULL + && pExpr->pLeft->op==TK_COLUMN + && pExpr->pLeft->iColumn>=0 + ){ + Expr *pNewExpr; + Expr *pLeft = pExpr->pLeft; + int idxNew; + WhereTerm *pNewTerm; + + pNewExpr = sqlite3PExpr(pParse, TK_GT, + sqlite3ExprDup(db, pLeft, 0), + sqlite3PExpr(pParse, TK_NULL, 0, 0, 0), 0); + + idxNew = whereClauseInsert(pWC, pNewExpr, + TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); + if( idxNew ){ + pNewTerm = &pWC->a[idxNew]; + pNewTerm->prereqRight = 0; + pNewTerm->leftCursor = pLeft->iTable; + pNewTerm->u.leftColumn = pLeft->iColumn; + pNewTerm->eOperator = WO_GT; + pNewTerm->iParent = idxTerm; + pTerm = &pWC->a[idxTerm]; + pTerm->nChild = 1; + pTerm->wtFlags |= TERM_COPIED; + pNewTerm->prereqAll = pTerm->prereqAll; + } + } +#endif /* SQLITE_ENABLE_STAT2 */ + /* Prevent ON clause terms of a LEFT JOIN from being used to drive ** an index for tables to the left of the join. */ pTerm->prereqRight |= extraRight; } @@ -1346,10 +1397,162 @@ } } return 0; } +/* +** 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. +** Argument iBase is the cursor number used for the table that pIdx refers +** to. +** +** If such an expression is found, its index in pList->a[] is returned. If +** no expression is found, -1 is returned. +*/ +static int findIndexCol( + Parse *pParse, /* Parse context */ + ExprList *pList, /* Expression list to search */ + int iBase, /* Cursor for table associated with pIdx */ + Index *pIdx, /* Index to match column of */ + int iCol /* Column of index to match */ +){ + int i; + const char *zColl = pIdx->azColl[iCol]; + + for(i=0; inExpr; i++){ + Expr *p = pList->a[i].pExpr; + if( pIdx->aiColumn[iCol]==p->iColumn && iBase==p->iTable ){ + CollSeq *pColl = sqlite3ExprCollSeq(pParse, p); + if( pColl && 0==sqlite3StrICmp(pColl->zName, zColl) ){ + return i; + } + } + } + + return -1; +} + +/* +** This routine determines if pIdx can be used to assist in processing a +** DISTINCT qualifier. In other words, it tests whether or not using this +** index for the outer loop guarantees that rows with equal values for +** all expressions in the pDistinct list are delivered grouped together. +** +** For example, the query +** +** SELECT DISTINCT a, b, c FROM tbl WHERE a = ? +** +** can benefit from any index on columns "b" and "c". +*/ +static int isDistinctIndex( + Parse *pParse, /* Parsing context */ + WhereClause *pWC, /* The WHERE clause */ + Index *pIdx, /* The index being considered */ + int base, /* Cursor number for the table pIdx is on */ + ExprList *pDistinct, /* The DISTINCT expressions */ + int nEqCol /* Number of index columns with == */ +){ + Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */ + int i; /* Iterator variable */ + + if( pIdx->zName==0 || pDistinct==0 || pDistinct->nExpr>=BMS ) return 0; + + /* Loop through all the expressions in the distinct list. If any of them + ** are not simple column references, return early. Otherwise, test if the + ** WHERE clause contains a "col=X" clause. If it does, the expression + ** can be ignored. If it does not, and the column does not belong to the + ** same table as index pIdx, return early. Finally, if there is no + ** matching "col=X" expression and the column is on the same table as pIdx, + ** set the corresponding bit in variable mask. + */ + for(i=0; inExpr; i++){ + WhereTerm *pTerm; + Expr *p = pDistinct->a[i].pExpr; + if( p->op!=TK_COLUMN ) return 0; + pTerm = findTerm(pWC, p->iTable, p->iColumn, ~(Bitmask)0, WO_EQ, 0); + if( pTerm ){ + Expr *pX = pTerm->pExpr; + CollSeq *p1 = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); + CollSeq *p2 = sqlite3ExprCollSeq(pParse, p); + if( p1==p2 ) continue; + } + if( p->iTable!=base ) return 0; + mask |= (((Bitmask)1) << i); + } + + for(i=nEqCol; mask && inColumn; i++){ + int iExpr = findIndexCol(pParse, pDistinct, base, pIdx, i); + if( iExpr<0 ) break; + mask &= ~(((Bitmask)1) << iExpr); + } + + return (mask==0); +} + + +/* +** Return true if the DISTINCT expression-list passed as the third argument +** is redundant. A DISTINCT list is redundant if the database contains a +** UNIQUE index that guarantees that the result of the query will be distinct +** anyway. +*/ +static int isDistinctRedundant( + Parse *pParse, + SrcList *pTabList, + WhereClause *pWC, + ExprList *pDistinct +){ + Table *pTab; + Index *pIdx; + int i; + int iBase; + + /* If there is more than one table or sub-select in the FROM clause of + ** this query, then it will not be possible to show that the DISTINCT + ** clause is redundant. */ + if( pTabList->nSrc!=1 ) return 0; + iBase = pTabList->a[0].iCursor; + pTab = pTabList->a[0].pTab; + + /* If any of the expressions is an IPK column on table iBase, then return + ** true. Note: The (p->iTable==iBase) part of this test may be false if the + ** current SELECT is a correlated sub-query. + */ + for(i=0; inExpr; i++){ + Expr *p = pDistinct->a[i].pExpr; + if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1; + } + + /* Loop through all indices on the table, checking each to see if it makes + ** the DISTINCT qualifier redundant. It does so if: + ** + ** 1. The index is itself UNIQUE, and + ** + ** 2. All of the columns in the index are either part of the pDistinct + ** list, or else the WHERE clause contains a term of the form "col=X", + ** where X is a constant value. The collation sequences of the + ** comparison and select-list expressions must match those of the index. + */ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->onError==OE_None ) continue; + for(i=0; inColumn; i++){ + int iCol = pIdx->aiColumn[i]; + if( 0==findTerm(pWC, iBase, iCol, ~(Bitmask)0, WO_EQ, pIdx) + && 0>findIndexCol(pParse, pDistinct, iBase, pIdx, i) + ){ + break; + } + } + if( i==pIdx->nColumn ){ + /* This index implies that the DISTINCT qualifier is redundant. */ + return 1; + } + } + + return 0; +} /* ** This routine decides if pIdx can be used to satisfy the ORDER BY ** clause. If it can, it returns 1. If pIdx cannot satisfy the ** ORDER BY clause, this routine returns 0. @@ -1373,19 +1576,23 @@ WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmaps */ Index *pIdx, /* The index we are testing */ int base, /* Cursor number for the table to be sorted */ ExprList *pOrderBy, /* The ORDER BY clause */ int nEqCol, /* Number of index columns with == constraints */ + int wsFlags, /* Index usages flags */ int *pbRev /* Set to 1 if ORDER BY is DESC */ ){ int i, j; /* Loop counters */ 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 */ sqlite3 *db = pParse->db; - assert( pOrderBy!=0 ); + if( !pOrderBy ) return 0; + if( wsFlags & WHERE_COLUMN_IN ) return 0; + if( pIdx->bUnordered ) return 0; + 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 @@ -1471,22 +1678,25 @@ */ j = nTerm; } } - *pbRev = sortOrder!=0; + if( pbRev ) *pbRev = sortOrder!=0; if( j>=nTerm ){ /* All terms of the ORDER BY clause are covered by this index so ** this index can be used for sorting. */ return 1; } if( pIdx->onError!=OE_None && i==pIdx->nColumn + && (wsFlags & WHERE_COLUMN_NULL)==0 && !referencesOtherTables(pOrderBy, pMaskSet, j, base) ){ /* All terms of this index match some prefix of the ORDER BY clause ** and the index is UNIQUE and no terms on the tail of the ORDER BY ** clause reference other tables in a join. If this is all true then - ** the order by clause is superfluous. */ + ** the order by clause is superfluous. Not that if the matching + ** condition is IS NULL then the result is not necessarily unique + ** even on a UNIQUE index, so disallow those cases. */ return 1; } return 0; } @@ -1553,11 +1763,12 @@ /* ** Required because bestIndex() is called by bestOrClauseIndex() */ static void bestIndex( - Parse*, WhereClause*, struct SrcList_item*, Bitmask, ExprList*, WhereCost*); + Parse*, WhereClause*, struct SrcList_item*, + Bitmask, Bitmask, ExprList*, WhereCost*); /* ** This routine attempts to find an scanning strategy that can be used ** to optimize an 'OR' expression that is part of a WHERE clause. ** @@ -1566,22 +1777,24 @@ */ static void bestOrClauseIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ - Bitmask notReady, /* Mask of cursors that are not available */ + Bitmask notReady, /* Mask of cursors not available for indexing */ + Bitmask notValid, /* Cursors not available for any purpose */ ExprList *pOrderBy, /* The ORDER BY clause */ WhereCost *pCost /* Lowest cost query plan */ ){ #ifndef SQLITE_OMIT_OR_OPTIMIZATION const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */ WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */ WhereTerm *pTerm; /* A single term of the WHERE clause */ - /* No OR-clause optimization allowed if the NOT INDEXED clause is used */ - if( pSrc->notIndexed ){ + /* No OR-clause optimization allowed if the INDEXED BY or NOT INDEXED clauses + ** are used */ + if( pSrc->notIndexed || pSrc->pIndex!=0 ){ return; } /* Search the WHERE clause terms for a usable WO_OR term. */ for(pTerm=pWC->a; pTerma), (pTerm - pWC->a) )); if( pOrTerm->eOperator==WO_AND ){ WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc; - bestIndex(pParse, pAndWC, pSrc, notReady, 0, &sTermCost); + bestIndex(pParse, pAndWC, pSrc, notReady, notValid, 0, &sTermCost); }else if( pOrTerm->leftCursor==iCur ){ WhereClause tempWC; tempWC.pParse = pWC->pParse; tempWC.pMaskSet = pWC->pMaskSet; tempWC.op = TK_AND; tempWC.a = pOrTerm; tempWC.nTerm = 1; - bestIndex(pParse, &tempWC, pSrc, notReady, 0, &sTermCost); + bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost); }else{ continue; } rTotal += sTermCost.rCost; - nRow += sTermCost.nRow; + nRow += sTermCost.plan.nRow; used |= sTermCost.used; if( rTotal>=pCost->rCost ) break; } /* If there is an ORDER BY clause, increase the scan cost to account @@ -1634,12 +1847,12 @@ ** less than the current cost stored in pCost, replace the contents ** of pCost. */ WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow)); if( rTotalrCost ){ pCost->rCost = rTotal; - pCost->nRow = nRow; pCost->used = used; + pCost->plan.nRow = nRow; pCost->plan.wsFlags = flags; pCost->plan.u.pTerm = pTerm; } } } @@ -1688,10 +1901,14 @@ double costTempIdx; /* per-query cost of the transient index */ WhereTerm *pTerm; /* A single term of the WHERE clause */ WhereTerm *pWCEnd; /* End of pWC->a[] */ Table *pTable; /* Table tht might be indexed */ + if( pParse->nQueryLoop<=(double)1 ){ + /* There is no point in building an automatic index for a single scan */ + return; + } if( (pParse->db->flags & SQLITE_AutoIndex)==0 ){ /* Automatic indices are disabled at run-time */ return; } if( (pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)!=0 ){ @@ -1700,14 +1917,18 @@ } if( pSrc->notIndexed ){ /* The NOT INDEXED clause appears in the SQL. */ return; } + if( pSrc->isCorrelated ){ + /* The source is a correlated sub-query. No point in indexing it. */ + return; + } assert( pParse->nQueryLoop >= (double)1 ); pTable = pSrc->pTab; - nTableRow = pTable->pIndex ? pTable->pIndex->aiRowEst[0] : 1000000; + nTableRow = pTable->nRowEst; logN = estLog(nTableRow); costTempIdx = 2*logN*(nTableRow/pParse->nQueryLoop + 1); if( costTempIdx>=pCost->rCost ){ /* The cost of creating the transient table would be greater than ** doing the full table scan */ @@ -1716,14 +1937,14 @@ /* Search for any equality comparison term */ pWCEnd = &pWC->a[pWC->nTerm]; for(pTerm=pWC->a; pTermrCost, costTempIdx)); pCost->rCost = costTempIdx; - pCost->nRow = logN + 1; + pCost->plan.nRow = logN + 1; pCost->plan.wsFlags = WHERE_TEMP_INDEX; pCost->used = pTerm->prereqRight; break; } } @@ -1837,11 +2058,12 @@ if( (idxCols & cMask)==0 ){ Expr *pX = pTerm->pExpr; idxCols |= cMask; pIdx->aiColumn[n] = pTerm->u.leftColumn; pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - pIdx->azColl[n] = pColl->zName; + assert( pColl!=0 || pParse->nErr>0 ); + pIdx->azColl[n] = pColl ? pColl->zName : "BINARY"; n++; } } } assert( (u32)n==pLevel->plan.nEq ); @@ -2057,11 +2279,12 @@ */ static void bestVirtualIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ - Bitmask notReady, /* Mask of cursors that are not available */ + Bitmask notReady, /* Mask of cursors not available for index */ + Bitmask notValid, /* Cursors not valid for any purpose */ ExprList *pOrderBy, /* The order by clause */ WhereCost *pCost, /* Lowest cost query plan */ sqlite3_index_info **ppIdxInfo /* Index information passed to xBestIndex */ ){ Table *pTab = pSrc->pTab; @@ -2187,22 +2410,29 @@ pIdxInfo->nOrderBy = nOrderBy; /* Try to find a more efficient access pattern by using multiple indexes ** to optimize an OR expression within the WHERE clause. */ - bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); + bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); } #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* ** Argument pIdx is a pointer to an index structure that has an array of ** SQLITE_INDEX_SAMPLES evenly spaced samples of the first indexed column -** stored in Index.aSample. The domain of values stored in said column -** may be thought of as divided into (SQLITE_INDEX_SAMPLES+1) regions. -** Region 0 contains all values smaller than the first sample value. Region -** 1 contains values larger than or equal to the value of the first sample, -** but smaller than the value of the second. And so on. +** stored in Index.aSample. These samples divide the domain of values stored +** the index into (SQLITE_INDEX_SAMPLES+1) regions. +** Region 0 contains all values less than the first sample value. Region +** 1 contains values between the first and second samples. Region 2 contains +** values between samples 2 and 3. And so on. Region SQLITE_INDEX_SAMPLES +** contains values larger than the last sample. +** +** If the index contains many duplicates of a single value, then it is +** possible that two or more adjacent samples can hold the same value. +** When that is the case, the smallest possible region code is returned +** when roundUp is false and the largest possible region code is returned +** when roundUp is true. ** ** If successful, this function determines which of the regions value ** pVal lies in, sets *piRegion to the region index (a value between 0 ** and SQLITE_INDEX_SAMPLES+1, inclusive) and returns SQLITE_OK. ** Or, if an OOM occurs while converting text values between encodings, @@ -2211,22 +2441,34 @@ #ifdef SQLITE_ENABLE_STAT2 static int whereRangeRegion( Parse *pParse, /* Database connection */ Index *pIdx, /* Index to consider domain of */ sqlite3_value *pVal, /* Value to consider */ + int roundUp, /* Return largest valid region if true */ int *piRegion /* OUT: Region of domain in which value lies */ ){ + assert( roundUp==0 || roundUp==1 ); if( ALWAYS(pVal) ){ IndexSample *aSample = pIdx->aSample; int i = 0; int eType = sqlite3_value_type(pVal); if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ double r = sqlite3_value_double(pVal); for(i=0; i=SQLITE_TEXT || aSample[i].u.r>r ) break; + if( aSample[i].eType>=SQLITE_TEXT ) break; + if( roundUp ){ + if( aSample[i].u.r>r ) break; + }else{ + if( aSample[i].u.r>=r ) break; + } + } + }else if( eType==SQLITE_NULL ){ + i = 0; + if( roundUp ){ + while( idb; CollSeq *pColl; const u8 *z; @@ -2253,11 +2495,11 @@ assert( z && pColl && pColl->xCmp ); } n = sqlite3ValueBytes(pVal, pColl->enc); for(i=0; ienc!=SQLITE_UTF8 ){ @@ -2267,18 +2509,18 @@ ); if( !zSample ){ assert( db->mallocFailed ); return SQLITE_NOMEM; } - r = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); + c = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); sqlite3DbFree(db, zSample); }else #endif { - r = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); + c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); } - if( r>0 ) break; + if( c-roundUp>=0 ) break; } } assert( i>=0 && i<=SQLITE_INDEX_SAMPLES ); *piRegion = i; @@ -2308,16 +2550,15 @@ Parse *pParse, Expr *pExpr, u8 aff, sqlite3_value **pp ){ - /* The evalConstExpr() function will have already converted any TK_VARIABLE - ** expression involved in an comparison into a TK_REGISTER. */ - assert( pExpr->op!=TK_VARIABLE ); - if( pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE ){ + if( pExpr->op==TK_VARIABLE + || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) + ){ int iVar = pExpr->iColumn; - sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); + sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-23257-02778 */ *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); return SQLITE_OK; } return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); } @@ -2358,13 +2599,13 @@ ** constraints (if any). A return value of 100 indicates that it is expected ** that the range scan will visit every row (100%) selected by the equality ** constraints. ** ** In the absence of sqlite_stat2 ANALYZE data, each range inequality -** reduces the search space by 2/3rds. Hence a single constraint (x>?) -** results in a return of 33 and a range constraint (x>? AND x?) +** results in a return of 25 and a range constraint (x>? AND xaCol[] of the range-compared column */ @@ -2380,73 +2621,213 @@ sqlite3_value *pLowerVal = 0; sqlite3_value *pUpperVal = 0; int iEst; int iLower = 0; int iUpper = SQLITE_INDEX_SAMPLES; + int roundUpUpper; + int roundUpLower; u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; if( pLower ){ Expr *pExpr = pLower->pExpr->pRight; rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal); + assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); + roundUpLower = (pLower->eOperator==WO_GT) ?1:0; } if( rc==SQLITE_OK && pUpper ){ Expr *pExpr = pUpper->pExpr->pRight; rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal); + assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); + roundUpUpper = (pUpper->eOperator==WO_LE) ?1:0; } if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){ sqlite3ValueFree(pLowerVal); sqlite3ValueFree(pUpperVal); goto range_est_fallback; }else if( pLowerVal==0 ){ - rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper); + rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper); if( pLower ) iLower = iUpper/2; }else if( pUpperVal==0 ){ - rc = whereRangeRegion(pParse, p, pLowerVal, &iLower); + rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower); if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2; }else{ - rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper); + rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper); if( rc==SQLITE_OK ){ - rc = whereRangeRegion(pParse, p, pLowerVal, &iLower); + rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower); } } + WHERETRACE(("range scan regions: %d..%d\n", iLower, iUpper)); iEst = iUpper - iLower; testcase( iEst==SQLITE_INDEX_SAMPLES ); assert( iEst<=SQLITE_INDEX_SAMPLES ); if( iEst<1 ){ - iEst = 1; + *piEst = 50/SQLITE_INDEX_SAMPLES; + }else{ + *piEst = (iEst*100)/SQLITE_INDEX_SAMPLES; } - sqlite3ValueFree(pLowerVal); sqlite3ValueFree(pUpperVal); - *piEst = (iEst * 100)/SQLITE_INDEX_SAMPLES; return rc; } range_est_fallback: #else UNUSED_PARAMETER(pParse); UNUSED_PARAMETER(p); UNUSED_PARAMETER(nEq); #endif assert( pLower || pUpper ); - if( pLower && pUpper ){ - *piEst = 11; + *piEst = 100; + if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *piEst /= 4; + if( pUpper ) *piEst /= 4; + return rc; +} + +#ifdef SQLITE_ENABLE_STAT2 +/* +** Estimate the number of rows that will be returned based on +** an equality constraint x=VALUE and where that VALUE occurs in +** the histogram data. This only works when x is the left-most +** column of an index and sqlite_stat2 histogram data is available +** for that index. +** +** Write the estimated row count into *pnRow and return SQLITE_OK. +** If unable to make an estimate, leave *pnRow unchanged and return +** non-zero. +** +** This routine can fail if it is unable to load a collating sequence +** required for string comparison, or if unable to allocate memory +** for a UTF conversion required for comparison. The error is stored +** in the pParse structure. +*/ +int whereEqualScanEst( + Parse *pParse, /* Parsing & code generating context */ + Index *p, /* The index whose left-most column is pTerm */ + Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */ + double *pnRow /* Write the revised row estimate here */ +){ + sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */ + int iLower, iUpper; /* Range of histogram regions containing pRhs */ + u8 aff; /* Column affinity */ + int rc; /* Subfunction return code */ + double nRowEst; /* New estimate of the number of rows */ + + assert( p->aSample!=0 ); + aff = p->pTable->aCol[p->aiColumn[0]].affinity; + if( pExpr ){ + rc = valueFromExpr(pParse, pExpr, aff, &pRhs); + if( rc ) goto whereEqualScanEst_cancel; + }else{ + pRhs = sqlite3ValueNew(pParse->db); + } + if( pRhs==0 ) return SQLITE_NOTFOUND; + rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower); + if( rc ) goto whereEqualScanEst_cancel; + rc = whereRangeRegion(pParse, p, pRhs, 1, &iUpper); + if( rc ) goto whereEqualScanEst_cancel; + WHERETRACE(("equality scan regions: %d..%d\n", iLower, iUpper)); + if( iLower>=iUpper ){ + nRowEst = p->aiRowEst[0]/(SQLITE_INDEX_SAMPLES*2); + if( nRowEst<*pnRow ) *pnRow = nRowEst; }else{ - *piEst = 33; + nRowEst = (iUpper-iLower)*p->aiRowEst[0]/SQLITE_INDEX_SAMPLES; + *pnRow = nRowEst; + } + +whereEqualScanEst_cancel: + sqlite3ValueFree(pRhs); + return rc; +} +#endif /* defined(SQLITE_ENABLE_STAT2) */ + +#ifdef SQLITE_ENABLE_STAT2 +/* +** Estimate the number of rows that will be returned based on +** an IN constraint where the right-hand side of the IN operator +** is a list of values. Example: +** +** WHERE x IN (1,2,3,4) +** +** Write the estimated row count into *pnRow and return SQLITE_OK. +** If unable to make an estimate, leave *pnRow unchanged and return +** non-zero. +** +** This routine can fail if it is unable to load a collating sequence +** required for string comparison, or if unable to allocate memory +** for a UTF conversion required for comparison. The error is stored +** in the pParse structure. +*/ +int whereInScanEst( + Parse *pParse, /* Parsing & code generating context */ + Index *p, /* The index whose left-most column is pTerm */ + ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */ + double *pnRow /* Write the revised row estimate here */ +){ + sqlite3_value *pVal = 0; /* One value from list */ + int iLower, iUpper; /* Range of histogram regions containing pRhs */ + u8 aff; /* Column affinity */ + int rc = SQLITE_OK; /* Subfunction return code */ + double nRowEst; /* New estimate of the number of rows */ + int nSpan = 0; /* Number of histogram regions spanned */ + int nSingle = 0; /* Histogram regions hit by a single value */ + int nNotFound = 0; /* Count of values that are not constants */ + int i; /* Loop counter */ + u8 aSpan[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions that are spanned */ + u8 aSingle[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions hit once */ + + assert( p->aSample!=0 ); + aff = p->pTable->aCol[p->aiColumn[0]].affinity; + memset(aSpan, 0, sizeof(aSpan)); + memset(aSingle, 0, sizeof(aSingle)); + for(i=0; inExpr; i++){ + sqlite3ValueFree(pVal); + rc = valueFromExpr(pParse, pList->a[i].pExpr, aff, &pVal); + if( rc ) break; + if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){ + nNotFound++; + continue; + } + rc = whereRangeRegion(pParse, p, pVal, 0, &iLower); + if( rc ) break; + rc = whereRangeRegion(pParse, p, pVal, 1, &iUpper); + if( rc ) break; + if( iLower>=iUpper ){ + aSingle[iLower] = 1; + }else{ + assert( iLower>=0 && iUpper<=SQLITE_INDEX_SAMPLES ); + while( iLoweraiRowEst[0]/(2*SQLITE_INDEX_SAMPLES) + + nNotFound*p->aiRowEst[1]; + if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; + *pnRow = nRowEst; + WHERETRACE(("IN row estimate: nSpan=%d, nSingle=%d, nNotFound=%d, est=%g\n", + nSpan, nSingle, nNotFound, nRowEst)); } + sqlite3ValueFree(pVal); return rc; } +#endif /* defined(SQLITE_ENABLE_STAT2) */ /* -** Find the query plan for accessing a particular table. Write the +** Find the best query plan for accessing a particular table. Write the ** best query plan and its cost into the WhereCost object supplied as the ** last parameter. ** ** The lowest cost plan wins. The cost is an estimate of the amount of -** CPU and disk I/O need to process the request using the selected plan. +** CPU and disk I/O needed to process the requested result. ** Factors that influence cost include: ** ** * The estimated number of rows that will be retrieved. (The ** fewer the better.) ** @@ -2461,19 +2842,21 @@ ** SQLITE_BIG_DBL. If a plan is found that uses the named index, ** then the cost is calculated in the usual way. ** ** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table ** in the SELECT statement, then no indexes are considered. However, the -** selected plan may still take advantage of the tables built-in rowid +** selected plan may still take advantage of the built-in rowid primary key ** index. */ static void bestBtreeIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ - Bitmask notReady, /* Mask of cursors that are not available */ + Bitmask notReady, /* Mask of cursors not available for indexing */ + Bitmask notValid, /* Cursors not available for any purpose */ ExprList *pOrderBy, /* The ORDER BY clause */ + ExprList *pDistinct, /* The select-list if query is DISTINCT */ WhereCost *pCost /* Lowest cost query plan */ ){ int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ Index *pProbe; /* An index we are evaluating */ Index *pIdx; /* Copy of pProbe, or zero for IPK index */ @@ -2503,34 +2886,29 @@ /* An INDEXED BY clause specifies a particular index to use */ pIdx = pProbe = pSrc->pIndex; wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); eqTermMask = idxEqTermMask; }else{ - /* There is no INDEXED BY clause. Create a fake Index object to - ** represent the primary key */ - Index *pFirst; /* Any other index on the table */ + /* There is no INDEXED BY clause. Create a fake Index object in local + ** variable sPk to represent the rowid primary key index. Make this + ** fake index the first in a chain of Index objects with all of the real + ** indices to follow */ + Index *pFirst; /* First of real indices on the table */ memset(&sPk, 0, sizeof(Index)); sPk.nColumn = 1; sPk.aiColumn = &aiColumnPk; sPk.aiRowEst = aiRowEstPk; - aiRowEstPk[1] = 1; sPk.onError = OE_Replace; sPk.pTable = pSrc->pTab; + aiRowEstPk[0] = pSrc->pTab->nRowEst; + aiRowEstPk[1] = 1; pFirst = pSrc->pTab->pIndex; if( pSrc->notIndexed==0 ){ + /* The real indices of the table are only considered if the + ** NOT INDEXED qualifier is omitted from the FROM clause */ sPk.pNext = pFirst; } - /* The aiRowEstPk[0] is an estimate of the total number of rows in the - ** table. Get this information from the ANALYZE information if it is - ** available. If not available, assume the table 1 million rows in size. - */ - if( pFirst ){ - assert( pFirst->aiRowEst!=0 ); /* Allocated together with pFirst */ - aiRowEstPk[0] = pFirst->aiRowEst[0]; - }else{ - aiRowEstPk[0] = 1000000; - } pProbe = &sPk; wsFlagMask = ~( WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE ); eqTermMask = WO_EQ|WO_IN; @@ -2541,20 +2919,23 @@ */ for(; pProbe; pIdx=pProbe=pProbe->pNext){ const unsigned int * const aiRowEst = pProbe->aiRowEst; double cost; /* Cost of using pProbe */ double nRow; /* Estimated number of rows in result set */ + double log10N; /* base-10 logarithm of nRow (inexact) */ int rev; /* True to scan in reverse order */ int wsFlags = 0; Bitmask used = 0; /* The following variables are populated based on the properties of - ** scan being evaluated. They are then used to determine the expected + ** index being evaluated. They are then used to determine the expected ** cost and number of rows returned. ** ** 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. ** ** nInMul: ** The "in-multiplier". This is an estimate of how many seek operations ** SQLite must perform on the index in question. For example, if the ** WHERE clause is: @@ -2574,46 +2955,55 @@ ** the sub-select is assumed to return 25 rows for the purposes of ** determining nInMul. ** ** bInEst: ** Set to true if there was at least one "x IN (SELECT ...)" term used - ** in determining the value of nInMul. + ** in determining the value of nInMul. Note that the RHS of the + ** IN operator must be a SELECT, not a value list, for this variable + ** to be true. ** ** estBound: ** An estimate on the amount of the table that must be searched. A ** value of 100 means the entire table is searched. Range constraints ** might reduce this to a value less than 100 to indicate that only ** a fraction of the table needs searching. In the absence of ** sqlite_stat2 ANALYZE data, a single inequality reduces the search - ** space to 1/3rd its original size. So an x>? constraint reduces - ** estBound to 33. Two constraints (x>? AND x? constraint reduces + ** estBound to 25. Two constraints (x>? AND xnColumn; nEq++){ int j = pProbe->aiColumn[nEq]; pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pIdx); @@ -2621,23 +3011,28 @@ wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); if( pTerm->eOperator & WO_IN ){ Expr *pExpr = pTerm->pExpr; wsFlags |= WHERE_COLUMN_IN; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */ nInMul *= 25; bInEst = 1; - }else if( ALWAYS(pExpr->x.pList) ){ - nInMul *= pExpr->x.pList->nExpr + 1; + }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){ + /* "x IN (value, value, ...)" */ + nInMul *= pExpr->x.pList->nExpr; } }else if( pTerm->eOperator & WO_ISNULL ){ wsFlags |= WHERE_COLUMN_NULL; } +#ifdef SQLITE_ENABLE_STAT2 + if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm; +#endif used |= pTerm->prereqRight; } /* Determine the value of estBound. */ - if( nEqnColumn ){ + if( nEqnColumn && pProbe->bUnordered==0 ){ int j = pProbe->aiColumn[nEq]; if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx); WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx); whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &estBound); @@ -2651,31 +3046,36 @@ wsFlags |= WHERE_BTM_LIMIT; used |= pBtm->prereqRight; } wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); } - }else if( pProbe->onError!=OE_None ){ + }else{ testcase( wsFlags & WHERE_COLUMN_IN ); testcase( wsFlags & WHERE_COLUMN_NULL ); if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ - wsFlags |= WHERE_UNIQUE; + wsFlags |= (pProbe->onError!=OE_None) ? WHERE_UNIQUE : WHERE_UNQ_WANTED; } } /* 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. */ - if( pOrderBy ){ - if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 - && isSortingIndex(pParse,pWC->pMaskSet,pProbe,iCur,pOrderBy,nEq,&rev) - ){ - wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY; - wsFlags |= (rev ? WHERE_REVERSE : 0); - }else{ - bSort = 1; - } + if( isSortingIndex( + pParse, pWC->pMaskSet, pProbe, iCur, pOrderBy, nEq, wsFlags, &rev) + ){ + bSort = 0; + wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY; + wsFlags |= (rev ? WHERE_REVERSE : 0); + } + + /* 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. */ + if( isDistinctIndex(pParse, pWC, pProbe, iCur, pDistinct, nEq) ){ + bDist = 0; + 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 @@ -2696,72 +3096,135 @@ bLookup = 1; } } /* - ** Estimate the number of rows of output. For an IN operator, - ** do not let the estimate exceed half the rows in the table. + ** 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]); } - /* Assume constant cost to access a row and logarithmic cost to - ** do a binary search. Hence, the initial cost is the number of output - ** rows plus log2(table-size) times the number of binary searches. +#ifdef SQLITE_ENABLE_STAT2 + /* If the constraint is of the form x=VALUE or x IN (E1,E2,...) + ** and we do not think that values of x are unique and if histogram + ** data is available for column x, then it might be possible + ** to get a better estimate on the number of rows based on + ** VALUE and how common that value is according to the histogram. */ - cost = nRow + nInMul*estLog(aiRowEst[0]); + if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 && aiRowEst[1]>1 ){ + if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ + testcase( pFirstTerm->eOperator==WO_EQ ); + testcase( pFirstTerm->pOperator==WO_ISNULL ); + whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow); + }else if( pFirstTerm->eOperator==WO_IN && bInEst==0 ){ + whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow); + } + } +#endif /* SQLITE_ENABLE_STAT2 */ - /* Adjust the number of rows and the cost downward to reflect rows + /* Adjust the number of output rows and downward to reflect rows ** that are excluded by range constraints. */ nRow = (nRow * (double)estBound) / (double)100; - cost = (cost * (double)estBound) / (double)100; + if( nRow<1 ) nRow = 1; - /* Add in the estimated cost of sorting the result + /* 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 + ** log10(N) times the time to move from one row to the next row within + ** a table or index. The actual times can vary, with the size of + ** records being an important factor. Both moves and searches are + ** slower with larger records, presumably because fewer records fit + ** on one page and hence more pages have to be fetched. + ** + ** The ANALYZE command and the sqlite_stat1 and sqlite_stat2 tables do + ** not give us data on the relative sizes of table and index records. + ** So this computation assumes table records are about twice as big + ** as index records + */ + if( (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. + ** + ** We add an additional 4x penalty to full table scans. This causes + ** the cost function to err on the side of choosing an index over + ** choosing a full scan. This 4x full-scan penalty is an arguable + ** 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; + }else{ + log10N = estLog(aiRowEst[0]); + cost = nRow; + if( pIdx ){ + if( bLookup ){ + /* For an index lookup followed by a table lookup: + ** nInMul index searches to find the start of each index range + ** + nRow steps through the index + ** + nRow table searches to lookup the table entry using the rowid + */ + cost += (nInMul + nRow)*log10N; + }else{ + /* For a covering index: + ** nInMul index searches to find the initial entry + ** + nRow steps through the index + */ + cost += 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; + } + } + + /* Add in the estimated cost of sorting the result. Actual experimental + ** measurements of sorting performance in SQLite show that sorting time + ** adds C*N*log10(N) to the cost, where N is the number of rows to be + ** sorted and C is a factor between 1.95 and 4.3. We will split the + ** difference and select C of 3.0. */ if( bSort ){ - cost += cost*estLog(cost); + cost += nRow*estLog(nRow)*3; + } + if( bDist ){ + cost += nRow*estLog(nRow)*3; } - /* If all information can be taken directly from the index, we avoid - ** doing table lookups. This reduces the cost by half. (Not really - - ** this needs to be fixed.) - */ - if( pIdx && bLookup==0 ){ - cost /= (double)2; - } /**** Cost of using this index has now been computed ****/ /* If there are additional constraints on this table that cannot ** be used with the current index, but which might lower the number ** of output rows, adjust the nRow value accordingly. This only ** matters if the current index is the least costly, so do not bother ** with this step if we already know this index will not be chosen. ** Also, never reduce the output row count below 2 using this step. ** - ** Do not reduce the output row count if pSrc is the only table that - ** is notReady; if notReady is a power of two. This will be the case - ** when the main sqlite3WhereBegin() loop is scanning for a table with - ** and "optimal" index, and on such a scan the output row count - ** reduction is not valid because it does not update the "pCost->used" - ** bitmap. The notReady bitmap will also be a power of two when we - ** are scanning for the last table in a 64-way join. We are willing - ** to bypass this optimization in that corner case. + ** It is critical that the notValid mask be used here instead of + ** the notReady mask. When computing an "optimal" index, the notReady + ** mask will only have one bit set - the bit for the current table. + ** The notValid mask, on the other hand, always has all bits set for + ** tables that are not in outer loops. If notReady is used here instead + ** of notValid, then a optimal index that depends on inner joins loops + ** might be selected even when there exists an optimal index that has + ** no such dependency. */ - if( nRow>2 && cost<=pCost->rCost && (notReady & (notReady-1))!=0 ){ + if( nRow>2 && cost<=pCost->rCost ){ int k; /* Loop counter */ int nSkipEq = 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++){ if( pTerm->wtFlags & TERM_VIRTUAL ) continue; - if( (pTerm->prereqAll & notReady)!=thisTab ) continue; + if( (pTerm->prereqAll & notValid)!=thisTab ) continue; if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){ if( nSkipEq ){ /* Ignore the first nEq equality matches since the index ** has already accounted for these */ nSkipEq--; @@ -2770,19 +3233,23 @@ ** set size by a factor of 10 */ nRow /= 10; } }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){ if( nSkipRange ){ - /* Ignore the first nBound range constraints since the index + /* Ignore the first nSkipRange range constraints since the index ** has already accounted for these */ nSkipRange--; }else{ /* Assume each additional range constraint reduces the result - ** set size by a factor of 3 */ + ** set size by a factor of 3. Indexed range constraints reduce + ** the search space by a larger factor: 4. We make indexed range + ** more selective intentionally because of the subjective + ** observation that indexed range constraints really are more + ** selective in practice, on average. */ nRow /= 3; } - }else{ + }else if( pTerm->eOperator!=WO_NOOP ){ /* Any other expression lowers the output row count by half */ nRow /= 2; } } if( nRow<2 ) nRow = 2; @@ -2789,25 +3256,25 @@ } WHERETRACE(( "%s(%s): nEq=%d nInMul=%d estBound=%d bSort=%d bLookup=%d wsFlags=0x%x\n" - " notReady=0x%llx nRow=%.2f cost=%.2f used=0x%llx\n", + " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f used=0x%llx\n", pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"), nEq, nInMul, estBound, bSort, bLookup, wsFlags, - notReady, nRow, cost, used + notReady, log10N, nRow, cost, used )); /* If this index is the best we have seen so far, then record this ** index and its cost in the pCost structure. */ if( (!pIdx || wsFlags) - && (costrCost || (cost<=pCost->rCost && nRownRow)) + && (costrCost || (cost<=pCost->rCost && nRowplan.nRow)) ){ pCost->rCost = cost; - pCost->nRow = nRow; pCost->used = used; + pCost->plan.nRow = nRow; pCost->plan.wsFlags = (wsFlags&wsFlagMask); pCost->plan.nEq = nEq; pCost->plan.u.pIdx = pIdx; } @@ -2839,11 +3306,11 @@ WHERETRACE(("best index is: %s\n", ((pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" : pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk") )); - bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); + bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); bestAutomaticIndex(pParse, pWC, pSrc, notReady, pCost); pCost->plan.wsFlags |= eqTermMask; } /* @@ -2854,26 +3321,27 @@ */ static void bestIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ - Bitmask notReady, /* Mask of cursors that are not available */ + Bitmask notReady, /* Mask of cursors not available for indexing */ + Bitmask notValid, /* Cursors not available for any purpose */ ExprList *pOrderBy, /* The ORDER BY clause */ WhereCost *pCost /* Lowest cost query plan */ ){ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pSrc->pTab) ){ sqlite3_index_info *p = 0; - bestVirtualIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost, &p); + bestVirtualIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost,&p); if( p->needToFreeIdxStr ){ sqlite3_free(p->idxStr); } sqlite3DbFree(pParse->db, p); }else #endif { - bestBtreeIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); + bestBtreeIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, 0, pCost); } } /* ** Disable a term in the WHERE clause. Except, do not disable the term @@ -3098,11 +3566,11 @@ assert( pIdx->nColumn>=nEq ); for(j=0; jaiColumn[j]; pTerm = findTerm(pWC, iCur, k, notReady, pLevel->plan.wsFlags, pIdx); - if( NEVER(pTerm==0) ) break; + if( pTerm==0 ) break; /* The following true for indices with redundant columns. ** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */ testcase( (pTerm->wtFlags & TERM_CODED)!=0 ); testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ r1 = codeEqualityTerm(pParse, pTerm, pLevel, regBase+j); @@ -3130,10 +3598,165 @@ } } *pzAff = zAff; return regBase; } + +#ifndef SQLITE_OMIT_EXPLAIN +/* +** This routine is a helper for explainIndexRange() below +** +** pStr holds the text of an expression that we are building up one term +** at a time. This routine adds a new term to the end of the expression. +** Terms are separated by AND so add the "AND" text for second and subsequent +** terms only. +*/ +static void explainAppendTerm( + StrAccum *pStr, /* The text expression being built */ + int iTerm, /* Index of this term. First is zero */ + const char *zColumn, /* Name of the column */ + const char *zOp /* Name of the operator */ +){ + if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5); + sqlite3StrAccumAppend(pStr, zColumn, -1); + sqlite3StrAccumAppend(pStr, zOp, 1); + sqlite3StrAccumAppend(pStr, "?", 1); +} + +/* +** 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. +** +** For example, if the query: +** +** SELECT * FROM t1 WHERE a=1 AND b>2; +** +** is run and there is an index on (a, b), then this function returns a +** 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, WhereLevel *pLevel, Table *pTab){ + WherePlan *pPlan = &pLevel->plan; + Index *pIndex = pPlan->u.pIdx; + int nEq = pPlan->nEq; + int i, j; + Column *aCol = pTab->aCol; + int *aiColumn = pIndex->aiColumn; + StrAccum txt; + + if( nEq==0 && (pPlan->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){ + return 0; + } + sqlite3StrAccumInit(&txt, 0, 0, SQLITE_MAX_LENGTH); + txt.db = db; + sqlite3StrAccumAppend(&txt, " (", 2); + for(i=0; i"); + } + if( pPlan->wsFlags&WHERE_TOP_LIMIT ){ + explainAppendTerm(&txt, i, aCol[aiColumn[j]].zName, "<"); + } + sqlite3StrAccumAppend(&txt, ")", 1); + return sqlite3StrAccumFinish(&txt); +} + +/* +** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN +** command. If the query being compiled is an EXPLAIN QUERY PLAN, a single +** record is added to the output to describe the table scan strategy in +** pLevel. +*/ +static void explainOneScan( + Parse *pParse, /* Parse context */ + SrcList *pTabList, /* Table list this loop refers to */ + WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ + int iLevel, /* Value for "level" column of output */ + int iFrom, /* Value for "from" column of output */ + u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ +){ + if( pParse->explain==2 ){ + u32 flags = pLevel->plan.wsFlags; + 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 */ + sqlite3_int64 nRow; /* Expected number of rows visited by scan */ + int iId = pParse->iSelectId; /* Select id (left-most output column) */ + int isSearch; /* True for a SEARCH. False for SCAN. */ + + if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return; + + isSearch = (pLevel->plan.nEq>0) + || (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 + || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); + + zMsg = sqlite3MPrintf(db, "%s", isSearch?"SEARCH":"SCAN"); + if( pItem->pSelect ){ + zMsg = sqlite3MAppendf(db, zMsg, "%s SUBQUERY %d", zMsg,pItem->iSelectId); + }else{ + zMsg = sqlite3MAppendf(db, zMsg, "%s TABLE %s", zMsg, pItem->zName); + } + + if( pItem->zAlias ){ + zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias); + } + if( (flags & WHERE_INDEXED)!=0 ){ + char *zWhere = explainIndexRange(db, pLevel, pItem->pTab); + zMsg = sqlite3MAppendf(db, zMsg, "%s USING %s%sINDEX%s%s%s", zMsg, + ((flags & WHERE_TEMP_INDEX)?"AUTOMATIC ":""), + ((flags & WHERE_IDX_ONLY)?"COVERING ":""), + ((flags & WHERE_TEMP_INDEX)?"":" "), + ((flags & WHERE_TEMP_INDEX)?"": pLevel->plan.u.pIdx->zName), + zWhere + ); + sqlite3DbFree(db, zWhere); + }else if( flags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ + zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg); + + if( flags&WHERE_ROWID_EQ ){ + zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid=?)", zMsg); + }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){ + zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>? AND rowid?)", zMsg); + }else if( flags&WHERE_TOP_LIMIT ){ + zMsg = sqlite3MAppendf(db, zMsg, "%s (rowidplan.u.pVtabIdx; + zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg, + pVtabIdx->idxNum, pVtabIdx->idxStr); + } +#endif + if( wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX) ){ + testcase( wctrlFlags & WHERE_ORDERBY_MIN ); + nRow = 1; + }else{ + nRow = (sqlite3_int64)pLevel->plan.nRow; + } + zMsg = sqlite3MAppendf(db, zMsg, "%s (~%lld rows)", zMsg, nRow); + sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC); + } +} +#else +# define explainOneScan(u,v,w,x,y,z) +#endif /* SQLITE_OMIT_EXPLAIN */ + /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ @@ -3460,11 +4083,13 @@ /* Seek the index cursor to the start of the range. */ nConstraint = nEq; if( pRangeStart ){ Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); - sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + if( (pRangeStart->wtFlags & TERM_VNULL)==0 ){ + sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + } if( zStartAff ){ if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_NONE){ /* Since the comparison is to be performed with no conversions ** applied to the operands, set the affinity to apply to pRight to ** SQLITE_AFF_NONE. */ @@ -3499,11 +4124,13 @@ nConstraint = nEq; if( pRangeEnd ){ Expr *pRight = pRangeEnd->pExpr->pRight; sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCode(pParse, pRight, regBase+nEq); - sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){ + sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + } if( zEndAff ){ if( sqlite3CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){ /* Since the comparison is to be performed with no conversions ** applied to the operands, set the affinity to apply to pRight to ** SQLITE_AFF_NONE. */ @@ -3538,11 +4165,11 @@ ** If it is, jump to the next iteration of the loop. */ r1 = sqlite3GetTempReg(pParse); testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ); testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ); - if( pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT) ){ + if( (pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1); sqlite3VdbeAddOp2(v, OP_IsNull, r1, addrCont); } sqlite3ReleaseTempReg(pParse, r1); @@ -3557,12 +4184,20 @@ } /* Record the instruction used to terminate the loop. Disable ** WHERE clause terms made redundant by the index range scan. */ - pLevel->op = bRev ? OP_Prev : OP_Next; + if( pLevel->plan.wsFlags & WHERE_UNIQUE ){ + pLevel->op = OP_Noop; + }else if( bRev ){ + pLevel->op = OP_Prev; + }else{ + pLevel->op = OP_Next; + } pLevel->p1 = iIdxCur; + assert( (WHERE_UNQ_WANTED>>30)==1 ); + pLevel->p3 = (pLevel->plan.wsFlags>>30)&1; }else #ifndef SQLITE_OMIT_OR_OPTIMIZATION if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ /* Case 4: Two or more separately indexed terms connected by OR @@ -3605,10 +4240,12 @@ ** */ WhereClause *pOrWc; /* The OR-clause broken out into subterms */ WhereTerm *pFinal; /* Final subterm within the OR-clause. */ SrcList *pOrTab; /* Shortened table list or OR-clause generation */ + Index *pCov = 0; /* Potential covering index (or NULL) */ + int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */ int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */ int regRowset = 0; /* Register for RowSet object */ int regRowid = 0; /* Register holding rowid */ int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */ @@ -3623,11 +4260,11 @@ pOrWc = &pTerm->u.pOrInfo->wc; pFinal = &pOrWc->a[pOrWc->nTerm-1]; pLevel->op = OP_Return; pLevel->p1 = regReturn; - /* Set up a new SrcList ni pOrTab containing the table being scanned + /* Set up a new SrcList in pOrTab containing the table being scanned ** by this loop in the a[0] slot and all notReady tables in a[1..] slots. ** This becomes the SrcList in the recursive call to sqlite3WhereBegin(). */ if( pWInfo->nLevel>1 ){ int nNotReady; /* The number of notReady tables */ @@ -3668,14 +4305,19 @@ for(ii=0; iinTerm; ii++){ WhereTerm *pOrTerm = &pOrWc->a[ii]; if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ /* Loop through table entries that match term pOrTerm. */ - pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, + pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, 0, WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | - WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY); + WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY, iCovCur); + assert( pSubWInfo || pParse->nErr || pParse->db->mallocFailed ); if( pSubWInfo ){ + WhereLevel *pLvl; + explainOneScan( + pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0 + ); if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ int iSet = ((ii==pOrWc->nTerm-1)?-1:ii); int r; r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur, regRowid); @@ -3688,16 +4330,41 @@ ** contained one or more AND term from a notReady table. The ** terms from the notReady table could not be tested and will ** need to be tested later. */ if( pSubWInfo->untestedTerms ) untestedTerms = 1; + + /* If all of the OR-connected terms are optimized using the same + ** index, and the index is opened using the same cursor number + ** by each call to sqlite3WhereBegin() made by this loop, it may + ** be possible to use that index as a covering index. + ** + ** If the call to sqlite3WhereBegin() above resulted in a scan that + ** uses an index, and this is either the first OR-connected term + ** processed or the index is the same as that used by all previous + ** terms, set pCov to the candidate covering index. Otherwise, set + ** pCov to NULL to indicate that no candidate covering index will + ** be available. + */ + pLvl = &pSubWInfo->a[0]; + if( (pLvl->plan.wsFlags & WHERE_INDEXED)!=0 + && (pLvl->plan.wsFlags & WHERE_TEMP_INDEX)==0 + && (ii==0 || pLvl->plan.u.pIdx==pCov) + ){ + assert( pLvl->iIdxCur==iCovCur ); + pCov = pLvl->plan.u.pIdx; + }else{ + pCov = 0; + } /* Finish the loop through table entries that match term pOrTerm. */ sqlite3WhereEnd(pSubWInfo); } } } + pLevel->u.pCovidx = pCov; + pLevel->iIdxCur = iCovCur; sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v)); sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk); sqlite3VdbeResolveLabel(v, iLoopBody); if( pWInfo->nLevel>1 ) sqlite3StackFree(pParse->db, pOrTab); @@ -3908,11 +4575,13 @@ WhereInfo *sqlite3WhereBegin( Parse *pParse, /* The parser context */ SrcList *pTabList, /* A list of all tables to be scanned */ Expr *pWhere, /* The WHERE clause */ ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */ - u16 wctrlFlags /* One of the WHERE_* flags defined in sqliteInt.h */ + ExprList *pDistinct, /* The select-list for DISTINCT queries - or NULL */ + u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */ + int iIdxCur /* If WHERE_ONETABLE_ONLY is set, index cursor number */ ){ int i; /* Loop counter */ int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */ int nTabList; /* Number of elements in pTabList */ WhereInfo *pWInfo; /* Will become the return value of this function */ @@ -4034,10 +4703,19 @@ */ exprAnalyzeAll(pTabList, pWC); if( db->mallocFailed ){ goto whereBeginError; } + + /* Check if the DISTINCT qualifier, if there is one, is redundant. + ** If it is, then set pDistinct to NULL and WhereInfo.eDistinct to + ** WHERE_DISTINCT_UNIQUE to tell the caller to ignore the DISTINCT. + */ + if( pDistinct && isDistinctRedundant(pParse, pTabList, pWC, pDistinct) ){ + pDistinct = 0; + pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; + } /* Chose the best index to use for each table in the FROM clause. ** ** This loop fills in the following fields: ** @@ -4067,10 +4745,11 @@ int nUnconstrained; /* Number tables without INDEXED BY */ Bitmask notIndexed; /* Mask of tables that cannot use an index */ memset(&bestPlan, 0, sizeof(bestPlan)); bestPlan.rCost = SQLITE_BIG_DBL; + WHERETRACE(("*** Begin search for loop %d ***\n", i)); /* Loop through the remaining entries in the FROM clause to find the ** next nested loop. The loop tests all FROM clause entries ** either once or twice. ** @@ -4084,14 +4763,20 @@ ** by waiting for other tables to run first. This "optimal" test works ** by first assuming that the FROM clause is on the inner loop and finding ** its query plan, then checking to see if that query plan uses any ** other FROM clause terms that are notReady. If no notReady terms are ** used then the "optimal" query plan works. + ** + ** Note that the WhereCost.nRow parameter for an optimal scan might + ** not be as small as it would be if the table really were the innermost + ** join. The nRow value can be reduced by WHERE clause constraints + ** that do not use indices. But this nRow reduction only happens if the + ** table really is the innermost join. ** ** The second loop iteration is only performed if no optimal scan - ** strategies were found by the first loop. This 2nd iteration is used to - ** search for the lowest cost scan overall. + ** strategies were found by the first iteration. This second iteration + ** is used to search for the lowest cost scan overall. ** ** Previous versions of SQLite performed only the second iteration - ** the next outermost loop was always that with the lowest overall ** cost. However, this meant that SQLite could select the wrong plan ** for scripts such as the following: @@ -4100,23 +4785,24 @@ ** CREATE TABLE t2(c, d); ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; ** ** The best strategy is to iterate through table t1 first. However it ** is not possible to determine this with a simple greedy algorithm. - ** However, since the cost of a linear scan through table t2 is the same + ** Since the cost of a linear scan through table t2 is the same ** as the cost of a linear scan through table t1, a simple greedy ** algorithm may choose to use t2 for the outer loop, which is a much ** costlier approach. */ nUnconstrained = 0; notIndexed = 0; - for(isOptimal=(iFrom=0; isOptimal--){ + for(isOptimal=(iFrom=0 && bestJ<0; isOptimal--){ Bitmask mask; /* Mask of tables not yet ready */ for(j=iFrom, pTabItem=&pTabList->a[j]; jjointype & (JT_LEFT|JT_CROSS))!=0; if( j!=iFrom && doNotReorder ) break; m = getMask(pMaskSet, pTabItem->iCursor); if( (m & notReady)==0 ){ @@ -4123,21 +4809,26 @@ if( j==iFrom ) iFrom++; continue; } mask = (isOptimal ? m : notReady); pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); + pDist = (i==0 ? pDistinct : 0); if( pTabItem->pIndex==0 ) nUnconstrained++; + WHERETRACE(("=== trying table %d with isOptimal=%d ===\n", + j, isOptimal)); assert( pTabItem->pTab ); #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTabItem->pTab) ){ sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; - bestVirtualIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost, pp); + bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, + &sCost, pp); }else #endif { - bestBtreeIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost); + bestBtreeIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, + pDist, &sCost); } assert( isOptimal || (sCost.used¬Ready)==0 ); /* If an INDEXED BY clause is present, then the plan must use that ** index if it uses any index at all */ @@ -4152,12 +4843,12 @@ /* Conditions under which this table becomes the best so far: ** ** (1) The table must not depend on other tables that have not ** yet run. ** - ** (2) A full-table-scan plan cannot supercede another plan unless - ** it is an "optimal" plan as defined above. + ** (2) A full-table-scan plan cannot supercede indexed plan unless + ** the full-table-scan is an "optimal" plan as defined above. ** ** (3) All tables have an INDEXED BY clause or this table lacks an ** INDEXED BY clause or this table uses the specific ** index specified by its INDEXED BY clause. This rule ensures ** that a best-so-far is always selected even if an impossible @@ -4169,43 +4860,59 @@ ** (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. */ if( (sCost.used¬Ready)==0 /* (1) */ && (bestJ<0 || (notIndexed&m)!=0 /* (2) */ + || (bestPlan.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0) && (nUnconstrained==0 || pTabItem->pIndex==0 /* (3) */ || NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) && (bestJ<0 || sCost.rCost=0 ); assert( notReady & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); - WHERETRACE(("*** Optimizer selects table %d for loop %d\n", bestJ, - pLevel-pWInfo->a)); + WHERETRACE(("*** Optimizer selects table %d for loop %d" + " with cost=%g and nRow=%g\n", + bestJ, pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow)); if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){ *ppOrderBy = 0; + } + if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){ + assert( pWInfo->eDistinct==0 ); + pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } andFlags &= bestPlan.plan.wsFlags; pLevel->plan = bestPlan.plan; testcase( bestPlan.plan.wsFlags & WHERE_INDEXED ); testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX ); if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){ - pLevel->iIdxCur = pParse->nTab++; + if( (wctrlFlags & WHERE_ONETABLE_ONLY) + && (bestPlan.plan.wsFlags & WHERE_TEMP_INDEX)==0 + ){ + pLevel->iIdxCur = iIdxCur; + }else{ + pLevel->iIdxCur = pParse->nTab++; + } }else{ pLevel->iIdxCur = -1; } notReady &= ~getMask(pMaskSet, pTabList->a[bestJ].iCursor); pLevel->iFrom = (u8)bestJ; - if( bestPlan.nRow>=(double)1 ) pParse->nQueryLoop *= bestPlan.nRow; + if( bestPlan.plan.nRow>=(double)1 ){ + pParse->nQueryLoop *= bestPlan.plan.nRow; + } /* Check that if the table scanned by this loop iteration had an ** INDEXED BY clause attached to it, that the named index is being ** used for the scan. If not, then query compilation has failed. ** Return an error. @@ -4249,48 +4956,19 @@ /* Open all tables in the pTabList and any indices selected for ** searching those tables. */ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ notReady = ~(Bitmask)0; + pWInfo->nRowOut = (double)1; for(i=0, pLevel=pWInfo->a; iexplain==2 ){ - char *zMsg; - struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; - zMsg = sqlite3MPrintf(db, "TABLE %s", pItem->zName); - if( pItem->zAlias ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias); - } - if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s WITH AUTOMATIC INDEX", zMsg); - }else if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s WITH INDEX %s", - zMsg, pLevel->plan.u.pIdx->zName); - }else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s VIA MULTI-INDEX UNION", zMsg); - }else if( pLevel->plan.wsFlags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s USING PRIMARY KEY", zMsg); - } -#ifndef SQLITE_OMIT_VIRTUALTABLE - else if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ - sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx; - zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg, - pVtabIdx->idxNum, pVtabIdx->idxStr); - } -#endif - if( pLevel->plan.wsFlags & WHERE_ORDERBY ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s ORDER BY", zMsg); - } - sqlite3VdbeAddOp4(v, OP_Explain, i, pLevel->iFrom, 0, zMsg, P4_DYNAMIC); - } -#endif /* SQLITE_OMIT_EXPLAIN */ 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 ){ /* Do nothing */ }else #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -4342,12 +5020,14 @@ ** loop below generates code for a single nested loop of the VM ** program. */ notReady = ~(Bitmask)0; for(i=0; ia[i]; + explainOneScan(pParse, pTabList, pLevel, i, pLevel->iFrom, wctrlFlags); notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady); - pWInfo->iContinue = pWInfo->a[i].addrCont; + pWInfo->iContinue = pLevel->addrCont; } #ifdef SQLITE_TEST /* For testing and debugging use only */ /* Record in the query plan information about the current table ** and the index used to access it (if any). If the table itself @@ -4428,11 +5108,11 @@ sqlite3ExprCacheClear(pParse); for(i=pWInfo->nLevel-1; i>=0; i--){ pLevel = &pWInfo->a[i]; sqlite3VdbeResolveLabel(v, pLevel->addrCont); if( pLevel->op!=OP_Noop ){ - sqlite3VdbeAddOp2(v, pLevel->op, pLevel->p1, pLevel->p2); + sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); sqlite3VdbeChangeP5(v, pLevel->p5); } if( pLevel->plan.wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){ struct InLoop *pIn; int j; @@ -4472,10 +5152,11 @@ /* Close all of the cursors that were opened by sqlite3WhereBegin. */ assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc ); for(i=0, pLevel=pWInfo->a; inLevel; i++, pLevel++){ + Index *pIdx = 0; struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); if( (pTab->tabFlags & TF_Ephemeral)==0 && pTab->pSelect==0 @@ -4501,16 +5182,19 @@ ** sqlite3WhereEnd will have created code that references the table ** directly. This loop scans all that code looking for opcodes ** that reference the table and converts them into opcodes that ** reference the index. */ - if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 && !db->mallocFailed){ + if( pLevel->plan.wsFlags & WHERE_INDEXED ){ + pIdx = pLevel->plan.u.pIdx; + }else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ + pIdx = pLevel->u.pCovidx; + } + if( pIdx && !db->mallocFailed){ int k, j, last; VdbeOp *pOp; - Index *pIdx = pLevel->plan.u.pIdx; - assert( pIdx!=0 ); pOp = sqlite3VdbeGetOp(v, pWInfo->iTop); last = sqlite3VdbeCurrentAddr(v); for(k=pWInfo->iTop; kp1!=pLevel->iTabCur ) continue; if( pOp->opcode==OP_Column ){ Index: test/analyze.test ================================================================== --- test/analyze.test +++ test/analyze.test @@ -94,21 +94,21 @@ } {0 {}} do_test analyze-1.11 { execsql { SELECT * FROM sqlite_stat1 } -} {} +} {t1 {} 0} do_test analyze-1.12 { catchsql { ANALYZE t1; } } {0 {}} do_test analyze-1.13 { execsql { SELECT * FROM sqlite_stat1 } -} {} +} {t1 {} 0} # Create some indices that can be analyzed. But do not yet add # data. Without data in the tables, no analysis is done. # do_test analyze-2.1 { @@ -115,25 +115,25 @@ execsql { CREATE INDEX t1i1 ON t1(a); ANALYZE main.t1; SELECT * FROM sqlite_stat1 ORDER BY idx; } -} {} +} {t1 {} 0} do_test analyze-2.2 { execsql { CREATE INDEX t1i2 ON t1(b); ANALYZE t1; SELECT * FROM sqlite_stat1 ORDER BY idx; } -} {} +} {t1 {} 0} do_test analyze-2.3 { execsql { CREATE INDEX t1i3 ON t1(a,b); ANALYZE main; SELECT * FROM sqlite_stat1 ORDER BY idx; } -} {} +} {t1 {} 0} # Start adding data to the table. Verify that the analysis # is done correctly. # do_test analyze-3.1 { Index: test/analyze2.test ================================================================== --- test/analyze2.test +++ test/analyze2.test @@ -19,10 +19,12 @@ ifcapable !stat2 { finish_test return } + +set testprefix analyze2 # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec @@ -117,40 +119,60 @@ execsql { INSERT INTO t1 VALUES($i, $i) } } execsql COMMIT execsql ANALYZE } {} -do_test analyze2-2.2 { - eqp "SELECT * FROM t1 WHERE x>500 AND y>700" -} {0 0 {TABLE t1 WITH INDEX t1_y}} -do_test analyze2-2.3 { - eqp "SELECT * FROM t1 WHERE x>700 AND y>500" -} {0 0 {TABLE t1 WITH INDEX t1_x}} -do_test analyze2-2.3 { - eqp "SELECT * FROM t1 WHERE y>700 AND x>500" -} {0 0 {TABLE t1 WITH INDEX t1_y}} -do_test analyze2-2.4 { - eqp "SELECT * FROM t1 WHERE y>500 AND x>700" -} {0 0 {TABLE t1 WITH INDEX t1_x}} -do_test analyze2-2.5 { - eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 200 AND y BETWEEN 400 AND 700" -} {0 0 {TABLE t1 WITH INDEX t1_x}} -do_test analyze2-2.6 { - eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 400 AND 700" -} {0 0 {TABLE t1 WITH INDEX t1_y}} -do_test analyze2-2.7 { - eqp "SELECT * FROM t1 WHERE x BETWEEN -400 AND -300 AND y BETWEEN 100 AND 300" -} {0 0 {TABLE t1 WITH INDEX t1_x}} -do_test analyze2-2.8 { - eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN -400 AND -300" -} {0 0 {TABLE t1 WITH INDEX t1_y}} -do_test analyze2-2.9 { - eqp "SELECT * FROM t1 WHERE x BETWEEN 500 AND 100 AND y BETWEEN 100 AND 300" -} {0 0 {TABLE t1 WITH INDEX t1_x}} -do_test analyze2-2.10 { - eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN 500 AND 100" -} {0 0 {TABLE t1 WITH INDEX t1_y}} +do_eqp_test analyze2-2.2 { + SELECT * FROM t1 WHERE x>500 AND y>700 +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~100 rows)} +} +do_eqp_test analyze2-2.3 { + SELECT * FROM t1 WHERE x>700 AND y>500 +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>?) (~100 rows)} +} +do_eqp_test analyze2-2.3 { + SELECT * FROM t1 WHERE y>700 AND x>500 +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~100 rows)} +} +do_eqp_test analyze2-2.4 { + SELECT * FROM t1 WHERE y>500 AND x>700 +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>?) (~100 rows)} +} +do_eqp_test analyze2-2.5 { + SELECT * FROM t1 WHERE x BETWEEN 100 AND 200 AND y BETWEEN 400 AND 700 +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x? AND y? AND x? AND y? AND x? AND y'h'" -} {0 0 {TABLE t1 WITH INDEX t1_y}} -do_test analyze2-3.6 { - eqp "SELECT * FROM t1 WHERE x<444 AND y>'h'" -} {0 0 {TABLE t1 WITH INDEX t1_y}} -do_test analyze2-3.7 { - eqp "SELECT * FROM t1 WHERE x<221 AND y>'g'" -} {0 0 {TABLE t1 WITH INDEX t1_x}} +do_eqp_test analyze2-3.3 { + SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 'a' AND 'b' +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y? AND x'h' +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~66 rows)} +} +do_eqp_test analyze2-3.6 { + SELECT * FROM t1 WHERE x<444 AND y>'h' +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~66 rows)} +} +do_eqp_test analyze2-3.7 { + SELECT * FROM t1 WHERE x<221 AND y>'g' +} { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x 'A' AND a < 'C' AND b > 'A' AND b < 'C'" -} {0 0 {TABLE t3 WITH INDEX t3b}} -do_test analyze2-4.5 { - eqp "SELECT * FROM t3 WHERE a > 'A' AND a < 'c' AND b > 'A' AND b < 'c'" -} {0 0 {TABLE t3 WITH INDEX t3a}} +do_eqp_test analyze2-4.4 { + SELECT * FROM t3 WHERE a > 'A' AND a < 'C' AND b > 'A' AND b < 'C' +} { + 0 0 0 {SEARCH TABLE t3 USING INDEX t3b (b>? AND b 'A' AND a < 'c' AND b > 'A' AND b < 'c' +} { + 0 0 0 {SEARCH TABLE t3 USING INDEX t3a (a>? AND a'ccc'" - } {0 0 {TABLE t4 WITH INDEX t4x}} - do_test analyze2-5.4 { - eqp "SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ccc' AND t42.x>'ggg'" - } {0 1 {TABLE t4 AS t42 WITH INDEX t4x} 1 0 {TABLE t4 AS t41 WITH INDEX t4x}} - do_test analyze2-5.5 { - eqp "SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ddd' AND t42.x>'ccc'" - } {0 0 {TABLE t4 AS t41 WITH INDEX t4x} 1 1 {TABLE t4 AS t42 WITH INDEX t4x}} + do_eqp_test analyze2-5.3 { + SELECT * FROM t4 WHERE x>'ccc' + } {0 0 0 {SEARCH TABLE t4 USING COVERING INDEX t4x (x>?) (~800 rows)}} + do_eqp_test analyze2-5.4 { + SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ccc' AND t42.x>'ggg' + } { + 0 0 1 {SEARCH TABLE t4 AS t42 USING COVERING INDEX t4x (x>?) (~300 rows)} + 0 1 0 {SEARCH TABLE t4 AS t41 USING COVERING INDEX t4x (x>?) (~800 rows)} + } + do_eqp_test analyze2-5.5 { + SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ddd' AND t42.x>'ccc' + } { + 0 0 0 {SEARCH TABLE t4 AS t41 USING COVERING INDEX t4x (x>?) (~700 rows)} + 0 1 1 {SEARCH TABLE t4 AS t42 USING COVERING INDEX t4x (x>?) (~800 rows)} + } } #-------------------------------------------------------------------- # These tests, analyze2-6.*, verify that the library behaves correctly # when one of the sqlite_stat1 and sqlite_stat2 tables is missing. @@ -304,26 +348,26 @@ do_test analyze2-6.1.1 { eqp {SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } -} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a=? AND b=?) (~9 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.1.2 { db cache flush execsql ANALYZE eqp {SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } -} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.1.3 { sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } -} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.1.4 { execsql { PRAGMA writable_schema = 1; DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat2'; } @@ -330,11 +374,11 @@ sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } -} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.1.5 { execsql { PRAGMA writable_schema = 1; DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat1'; } @@ -341,11 +385,11 @@ sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } -} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a=? AND b=?) (~9 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.1.6 { execsql { PRAGMA writable_schema = 1; INSERT INTO sqlite_master SELECT * FROM master; } @@ -352,11 +396,11 @@ sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } -} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.1 { execsql { DELETE FROM sqlite_stat1; DELETE FROM sqlite_stat2; @@ -364,26 +408,26 @@ sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } -} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a1 AND t5.a<15 AND t6.a>1 } -} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.3 { sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } -} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.4 { execsql { PRAGMA writable_schema = 1; DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat1'; } @@ -390,11 +434,11 @@ sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } -} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a1 AND t5.a<15 AND t6.a>1 } -} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a1 AND t5.a<15 AND t6.a>1 } -} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} #-------------------------------------------------------------------- # These tests, analyze2-7.*, test that the sqlite_stat2 functionality # works in shared-cache mode. Note that these tests reuse the database # created for the analyze2-6.* tests. @@ -457,54 +501,54 @@ do_test analyze2-7.5 { eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db1 - } {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.6 { incr_schema_cookie test.db execsql { SELECT * FROM sqlite_master } db2 eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db2 - } {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.7 { incr_schema_cookie test.db execsql { SELECT * FROM sqlite_master } db1 eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db1 - } {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.8 { execsql { DELETE FROM sqlite_stat2 } db2 execsql { SELECT * FROM sqlite_master } db1 eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db1 - } {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.9 { execsql { SELECT * FROM sqlite_master } db2 eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db2 - } {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} + } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.10 { incr_schema_cookie test.db execsql { SELECT * FROM sqlite_master } db1 eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db1 - } {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} + } {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a200 AND x<300 } -} {0 0 {TABLE t1 WITH INDEX i1}} -do_test analyze3-1.1.3 { - eqp { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 } -} {0 0 {TABLE t1}} +do_eqp_test analyze3-1.1.2 { + SELECT sum(y) FROM t1 WHERE x>200 AND x<300 +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x0 AND x<1100 +} {0 0 0 {SCAN TABLE t1 (~111 rows)}} do_test analyze3-1.1.4 { sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 } } {199 0 14850} do_test analyze3-1.1.5 { @@ -142,16 +142,16 @@ CREATE INDEX i2 ON t2(x); COMMIT; ANALYZE; } } {} -do_test analyze3-1.2.2 { - eqp { SELECT sum(y) FROM t2 WHERE x>1 AND x<2 } -} {0 0 {TABLE t2 WITH INDEX i2}} -do_test analyze3-1.2.3 { - eqp { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 } -} {0 0 {TABLE t2}} +do_eqp_test analyze3-1.2.2 { + SELECT sum(y) FROM t2 WHERE x>1 AND x<2 +} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x0 AND x<99 +} {0 0 0 {SCAN TABLE t2 (~111 rows)}} do_test analyze3-1.2.4 { sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 } } {161 0 4760} do_test analyze3-1.2.5 { set l [string range "12" 0 end] @@ -189,16 +189,16 @@ CREATE INDEX i3 ON t3(x); COMMIT; ANALYZE; } } {} -do_test analyze3-1.3.2 { - eqp { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 } -} {0 0 {TABLE t3 WITH INDEX i3}} -do_test analyze3-1.3.3 { - eqp { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 } -} {0 0 {TABLE t3}} +do_eqp_test analyze3-1.3.2 { + SELECT sum(y) FROM t3 WHERE x>200 AND x<300 +} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x0 AND x<1100 +} {0 0 0 {SCAN TABLE t3 (~111 rows)}} do_test analyze3-1.3.4 { sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 } } {199 0 14850} do_test analyze3-1.3.5 { @@ -244,16 +244,16 @@ append t [lindex {a b c d e f g h i j} [expr ($i%10)]] execsql { INSERT INTO t1 VALUES($i, $t) } } execsql COMMIT } {} -do_test analyze3-2.2 { - eqp { SELECT count(a) FROM t1 WHERE b LIKE 'a%' } -} {0 0 {TABLE t1 WITH INDEX i1}} -do_test analyze3-2.3 { - eqp { SELECT count(a) FROM t1 WHERE b LIKE '%a' } -} {0 0 {TABLE t1}} +do_eqp_test analyze3-2.2 { + SELECT count(a) FROM t1 WHERE b LIKE 'a%' +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b>? AND b=25 && $i<=50}] + set z [expr {($i>=400) + ($i>=700) + ($i>=875)}] + set x $z + set w $z + set t [expr {$z+0.5}] + switch $z { + 0 {set u "alpha"; unset x} + 1 {set u "bravo"} + 2 {set u "charlie"} + 3 {set u "delta"; unset w} + } + if {$i%2} {set v $u} {set v [string toupper $u]} + db eval {INSERT INTO t1 VALUES($t,$u,$v,$w,$x,$y,$z)} + } + db eval { + CREATE INDEX t1t ON t1(t); -- 0.5, 1.5, 2.5, and 3.5 + CREATE INDEX t1u ON t1(u); -- text + CREATE INDEX t1v ON t1(v); -- mixed case text + CREATE INDEX t1w ON t1(w); -- integers 0, 1, 2 and a few NULLs + CREATE INDEX t1x ON t1(x); -- integers 1, 2, 3 and many NULLs + CREATE INDEX t1y ON t1(y); -- integers 0 and very few 1s + CREATE INDEX t1z ON t1(z); -- integers 0, 1, 2, and 3 + ANALYZE; + SELECT sample FROM sqlite_stat2 WHERE idx='t1u' ORDER BY sampleno; + } +} {alpha alpha alpha alpha bravo bravo bravo charlie charlie delta} +do_test analyze5-1.1 { + string tolower \ + [db eval {SELECT sample from sqlite_stat2 WHERE idx='t1v' ORDER BY sampleno}] +} {alpha alpha alpha alpha bravo bravo bravo charlie charlie delta} +do_test analyze5-1.2 { + db eval {SELECT sample from sqlite_stat2 WHERE idx='t1w' ORDER BY sampleno} +} {{} 0 0 0 0 1 1 1 2 2} +do_test analyze5-1.3 { + db eval {SELECT sample from sqlite_stat2 WHERE idx='t1x' ORDER BY sampleno} +} {{} {} {} {} 1 1 1 2 2 3} +do_test analyze5-1.4 { + db eval {SELECT sample from sqlite_stat2 WHERE idx='t1y' ORDER BY sampleno} +} {0 0 0 0 0 0 0 0 0 0} +do_test analyze5-1.5 { + db eval {SELECT sample from sqlite_stat2 WHERE idx='t1z' ORDER BY sampleno} +} {0 0 0 0 1 1 1 2 2 3} +do_test analyze5-1.6 { + db eval {SELECT sample from sqlite_stat2 WHERE idx='t1t' ORDER BY sampleno} +} {0.5 0.5 0.5 0.5 1.5 1.5 1.5 2.5 2.5 3.5} + + +# Verify that range queries generate the correct row count estimates +# +foreach {testid where index rows} { + 1 {z>=0 AND z<=0} t1z 400 + 2 {z>=1 AND z<=1} t1z 300 + 3 {z>=2 AND z<=2} t1z 200 + 4 {z>=3 AND z<=3} t1z 100 + 5 {z>=4 AND z<=4} t1z 50 + 6 {z>=-1 AND z<=-1} t1z 50 + 7 {z>1 AND z<3} t1z 200 + 8 {z>0 AND z<100} t1z 600 + 9 {z>=1 AND z<100} t1z 600 + 10 {z>1 AND z<100} t1z 300 + 11 {z>=2 AND z<100} t1z 300 + 12 {z>2 AND z<100} t1z 100 + 13 {z>=3 AND z<100} t1z 100 + 14 {z>3 AND z<100} t1z 50 + 15 {z>=4 AND z<100} t1z 50 + 16 {z>=-100 AND z<=-1} t1z 50 + 17 {z>=-100 AND z<=0} t1z 400 + 18 {z>=-100 AND z<0} t1z 50 + 19 {z>=-100 AND z<=1} t1z 700 + 20 {z>=-100 AND z<2} t1z 700 + 21 {z>=-100 AND z<=2} t1z 900 + 22 {z>=-100 AND z<3} t1z 900 + + 31 {z>=0.0 AND z<=0.0} t1z 400 + 32 {z>=1.0 AND z<=1.0} t1z 300 + 33 {z>=2.0 AND z<=2.0} t1z 200 + 34 {z>=3.0 AND z<=3.0} t1z 100 + 35 {z>=4.0 AND z<=4.0} t1z 50 + 36 {z>=-1.0 AND z<=-1.0} t1z 50 + 37 {z>1.5 AND z<3.0} t1z 200 + 38 {z>0.5 AND z<100} t1z 600 + 39 {z>=1.0 AND z<100} t1z 600 + 40 {z>1.5 AND z<100} t1z 300 + 41 {z>=2.0 AND z<100} t1z 300 + 42 {z>2.1 AND z<100} t1z 100 + 43 {z>=3.0 AND z<100} t1z 100 + 44 {z>3.2 AND z<100} t1z 50 + 45 {z>=4.0 AND z<100} t1z 50 + 46 {z>=-100 AND z<=-1.0} t1z 50 + 47 {z>=-100 AND z<=0.0} t1z 400 + 48 {z>=-100 AND z<0.0} t1z 50 + 49 {z>=-100 AND z<=1.0} t1z 700 + 50 {z>=-100 AND z<2.0} t1z 700 + 51 {z>=-100 AND z<=2.0} t1z 900 + 52 {z>=-100 AND z<3.0} t1z 900 + + 101 {z=-1} t1z 50 + 102 {z=0} t1z 400 + 103 {z=1} t1z 300 + 104 {z=2} t1z 200 + 105 {z=3} t1z 100 + 106 {z=4} t1z 50 + 107 {z=-10.0} t1z 50 + 108 {z=0.0} t1z 400 + 109 {z=1.0} t1z 300 + 110 {z=2.0} t1z 200 + 111 {z=3.0} t1z 100 + 112 {z=4.0} t1z 50 + 113 {z=1.5} t1z 50 + 114 {z=2.5} t1z 50 + + 201 {z IN (-1)} t1z 50 + 202 {z IN (0)} t1z 400 + 203 {z IN (1)} t1z 300 + 204 {z IN (2)} t1z 200 + 205 {z IN (3)} t1z 100 + 206 {z IN (4)} t1z 50 + 207 {z IN (0.5)} t1z 50 + 208 {z IN (0,1)} t1z 700 + 209 {z IN (0,1,2)} t1z 900 + 210 {z IN (0,1,2,3)} {} 100 + 211 {z IN (0,1,2,3,4,5)} {} 100 + 212 {z IN (1,2)} t1z 500 + 213 {z IN (2,3)} t1z 300 + 214 {z=3 OR z=2} t1z 300 + 215 {z IN (-1,3)} t1z 150 + 216 {z=-1 OR z=3} t1z 150 + + 300 {y=0} {} 100 + 301 {y=1} t1y 50 + 302 {y=0.1} t1y 50 + + 400 {x IS NULL} t1x 400 + +} { + # Verify that the expected index is used with the expected row count + do_test analyze5-1.${testid}a { + set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3] + set idx {} + regexp {INDEX (t1.) } $x all idx + regexp {~([0-9]+) rows} $x all nrow + list $idx $nrow + } [list $index $rows] + + # Verify that the same result is achieved regardless of whether or not + # the index is used + do_test analyze5-1.${testid}b { + set w2 [string map {y +y z +z} $where] + set a1 [db eval "SELECT rowid FROM t1 NOT INDEXED WHERE $w2\ + ORDER BY +rowid"] + set a2 [db eval "SELECT rowid FROM t1 WHERE $where ORDER BY +rowid"] + if {$a1==$a2} { + set res ok + } else { + set res "a1=\[$a1\] a2=\[$a2\]" + } + set res + } {ok} +} + +# Increase the number of NULLs in column x +# +db eval { + UPDATE t1 SET x=NULL; + UPDATE t1 SET x=rowid + WHERE rowid IN (SELECT rowid FROM t1 ORDER BY random() LIMIT 5); + ANALYZE; +} + +# Verify that range queries generate the correct row count estimates +# +foreach {testid where index rows} { + 500 {x IS NULL AND u='charlie'} t1u 20 + 501 {x=1 AND u='charlie'} t1x 5 + 502 {x IS NULL} {} 100 + 503 {x=1} t1x 50 + 504 {x IS NOT NULL} t1x 25 + 505 {+x IS NOT NULL} {} 500 + 506 {upper(x) IS NOT NULL} {} 500 + +} { + # Verify that the expected index is used with the expected row count + do_test analyze5-1.${testid}a { + set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3] + set idx {} + regexp {INDEX (t1.) } $x all idx + regexp {~([0-9]+) rows} $x all nrow + list $idx $nrow + } [list $index $rows] + + # Verify that the same result is achieved regardless of whether or not + # the index is used + do_test analyze5-1.${testid}b { + set w2 [string map {y +y z +z} $where] + set a1 [db eval "SELECT rowid FROM t1 NOT INDEXED WHERE $w2\ + ORDER BY +rowid"] + set a2 [db eval "SELECT rowid FROM t1 WHERE $where ORDER BY +rowid"] + if {$a1==$a2} { + set res ok + } else { + set res "a1=\[$a1\] a2=\[$a2\]" + } + set res + } {ok} +} + +finish_test ADDED test/analyze6.test Index: test/analyze6.test ================================================================== --- /dev/null +++ test/analyze6.test @@ -0,0 +1,74 @@ +# 2011 March 3 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file implements tests for SQLite library. The focus of the tests +# in this file a corner-case query planner optimization involving the +# join order of two tables of different sizes. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !stat2 { + finish_test + return +} + +set testprefix analyze6 + +proc eqp {sql {db db}} { + uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db +} + +do_test analyze6-1.0 { + db eval { + CREATE TABLE cat(x INT); + CREATE UNIQUE INDEX catx ON cat(x); + /* Give cat 16 unique integers */ + INSERT INTO cat VALUES(1); + INSERT INTO cat VALUES(2); + INSERT INTO cat SELECT x+2 FROM cat; + INSERT INTO cat SELECT x+4 FROM cat; + INSERT INTO cat SELECT x+8 FROM cat; + + CREATE TABLE ev(y INT); + CREATE INDEX evy ON ev(y); + /* ev will hold 32 copies of 16 integers found in cat */ + INSERT INTO ev SELECT x FROM cat; + INSERT INTO ev SELECT x FROM cat; + INSERT INTO ev SELECT y FROM ev; + INSERT INTO ev SELECT y FROM ev; + INSERT INTO ev SELECT y FROM ev; + INSERT INTO ev SELECT y FROM ev; + ANALYZE; + SELECT count(*) FROM cat; + SELECT count(*) FROM ev; + } +} {16 512} + +# The lowest cost plan is to scan CAT and for each integer there, do a single +# lookup of the first corresponding entry in EV then read off the equal values +# in EV. (Prior to the 2011-03-04 enhancement to where.c, this query would +# have used EV for the outer loop instead of CAT - which was about 3x slower.) +# +do_test analyze6-1.1 { + eqp {SELECT count(*) FROM ev, cat WHERE x=y} +} {0 0 1 {SCAN TABLE cat (~16 rows)} 0 1 0 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} + +# The same plan is chosen regardless of the order of the tables in the +# FROM clause. +# +do_test analyze6-1.2 { + eqp {SELECT count(*) FROM cat, ev WHERE x=y} +} {0 0 0 {SCAN TABLE cat (~16 rows)} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} + + +finish_test Index: test/auth.test ================================================================== --- test/auth.test +++ test/auth.test @@ -1974,16 +1974,16 @@ CREATE INDEX t4i2 ON t4(b,a,c); INSERT INTO t4 VALUES(1,2,3); ANALYZE; } set ::authargs - } {t4 {} main {}} + } {t4 {} main {} t2 {} main {}} do_test auth-1.295 { execsql { SELECT count(*) FROM sqlite_stat1; } - } 2 + } 3 proc auth {code args} { if {$code=="SQLITE_ANALYZE"} { set ::authargs [concat $::authargs $args] return SQLITE_DENY } @@ -1997,11 +1997,11 @@ } {1 {not authorized}} do_test auth-1.297 { execsql { SELECT count(*) FROM sqlite_stat1; } - } 2 + } 3 } ;# ifcapable analyze # Authorization for ALTER TABLE ADD COLUMN. # These tests are omitted if the library Index: test/autoindex1.test ================================================================== --- test/autoindex1.test +++ test/autoindex1.test @@ -146,24 +146,32 @@ CREATE TABLE t502(x INTEGER PRIMARY KEY, y); EXPLAIN QUERY PLAN SELECT b FROM t501 WHERE t501.a IN (SELECT x FROM t502 WHERE y=?); } -} {0 0 {TABLE t501 USING PRIMARY KEY} 0 0 {TABLE t502}} +} {0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?) (~25 rows)} 0 0 0 {SCAN TABLE t502 (~100000 rows)}} do_test autoindex1-501 { db eval { EXPLAIN QUERY PLAN SELECT b FROM t501 WHERE t501.a IN (SELECT x FROM t502 WHERE y=t501.b); } -} {0 0 {TABLE t501} 0 0 {TABLE t502 WITH AUTOMATIC INDEX}} +} {0 0 0 {SCAN TABLE t501 (~500000 rows)} 0 0 0 {SEARCH TABLE t502 USING AUTOMATIC COVERING INDEX (y=?) (~7 rows)}} do_test autoindex1-502 { db eval { EXPLAIN QUERY PLAN SELECT b FROM t501 WHERE t501.a=123 AND t501.a IN (SELECT x FROM t502 WHERE y=t501.b); } -} {0 0 {TABLE t501 USING PRIMARY KEY} 0 0 {TABLE t502}} - +} {0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 0 0 {SCAN TABLE t502 (~100000 rows)}} + + +do_execsql_test autoindex1-700 { + CREATE TABLE t5(a, b, c); + EXPLAIN QUERY PLAN SELECT a FROM t5 WHERE b=10 ORDER BY c; +} { + 0 0 0 {SCAN TABLE t5 (~100000 rows)} +} + finish_test Index: test/collate5.test ================================================================== --- test/collate5.test +++ test/collate5.test @@ -55,21 +55,21 @@ } {} do_test collate5-1.1 { execsql { SELECT DISTINCT a FROM collate5t1; } -} {A B N} +} {a b n} do_test collate5-1.2 { execsql { SELECT DISTINCT b FROM collate5t1; } -} {{} Apple apple banana} +} {apple Apple banana {}} do_test collate5-1.3 { execsql { SELECT DISTINCT a, b FROM collate5t1; } -} {A Apple a apple B banana N {}} +} {a apple A Apple b banana n {}} # Ticket #3376 # do_test collate5-1.11 { execsql { ADDED test/distinct.test Index: test/distinct.test ================================================================== --- /dev/null +++ test/distinct.test @@ -0,0 +1,170 @@ +# 2011 July 1 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this script is the DISTINCT modifier. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +set testprefix distinct + +proc do_execsql_test {testname sql {result {}}} { + uplevel do_test $testname [list "execsql {$sql}"] [list [list {*}$result]] +} + + +proc is_distinct_noop {sql} { + set sql1 $sql + set sql2 [string map {DISTINCT ""} $sql] + + set program1 [list] + set program2 [list] + db eval "EXPLAIN $sql1" { + if {$opcode != "Noop"} { lappend program1 $opcode } + } + db eval "EXPLAIN $sql2" { + if {$opcode != "Noop"} { lappend program2 $opcode } + } + + return [expr {$program1==$program2}] +} + +proc do_distinct_noop_test {tn sql} { + uplevel [list do_test $tn [list is_distinct_noop $sql] 1] +} +proc do_distinct_not_noop_test {tn sql} { + uplevel [list do_test $tn [list is_distinct_noop $sql] 0] +} + +proc do_temptables_test {tn sql temptables} { + uplevel [list do_test $tn [subst -novar { + set ret "" + db eval "EXPLAIN [set sql]" { + if {$opcode == "OpenEphemeral"} { + if {$p5 != "10" && $p5!="00"} { error "p5 = $p5" } + if {$p5 == "10"} { + lappend ret hash + } else { + lappend ret btree + } + } + } + set ret + }] $temptables] +} + + +#------------------------------------------------------------------------- +# The following tests - distinct-1.* - check that the planner correctly +# detects cases where a UNIQUE index means that a DISTINCT clause is +# redundant. Currently the planner only detects such cases when there +# is a single table in the FROM clause. +# +do_execsql_test 1.0 { + CREATE TABLE t1(a, b, c, d); + CREATE UNIQUE INDEX i1 ON t1(b, c); + CREATE UNIQUE INDEX i2 ON t1(d COLLATE nocase); + + CREATE TABLE t2(x INTEGER PRIMARY KEY, y); + + CREATE TABLE t3(c1 PRIMARY KEY, c2); + CREATE INDEX i3 ON t3(c2); +} +foreach {tn noop sql} { + + 1 1 "SELECT DISTINCT b, c FROM t1" + 2 1 "SELECT DISTINCT c FROM t1 WHERE b = ?" + 3 1 "SELECT DISTINCT rowid FROM t1" + 4 1 "SELECT DISTINCT rowid, a FROM t1" + 5 1 "SELECT DISTINCT x FROM t2" + 6 1 "SELECT DISTINCT * FROM t2" + 7 1 "SELECT DISTINCT * FROM (SELECT * FROM t2)" + + 8 1 "SELECT DISTINCT * FROM t1" + + 8 0 "SELECT DISTINCT a, b FROM t1" + + 9 0 "SELECT DISTINCT c FROM t1 WHERE b IN (1,2)" + 10 0 "SELECT DISTINCT c FROM t1" + 11 0 "SELECT DISTINCT b FROM t1" + + 12 0 "SELECT DISTINCT a, d FROM t1" + 13 0 "SELECT DISTINCT a, b, c COLLATE nocase FROM t1" + 14 1 "SELECT DISTINCT a, d COLLATE nocase FROM t1" + 15 0 "SELECT DISTINCT a, d COLLATE binary FROM t1" + 16 1 "SELECT DISTINCT a, b, c COLLATE binary FROM t1" + + 16 0 "SELECT DISTINCT t1.rowid FROM t1, t2" + 17 0 { /* Technically, it would be possible to detect that DISTINCT + ** is a no-op in cases like the following. But SQLite does not + ** do so. */ + SELECT DISTINCT t1.rowid FROM t1, t2 WHERE t1.rowid=t2.rowid } + + 18 1 "SELECT DISTINCT c1, c2 FROM t3" + 19 1 "SELECT DISTINCT c1 FROM t3" + 20 1 "SELECT DISTINCT * FROM t3" + 21 0 "SELECT DISTINCT c2 FROM t3" + + 22 0 "SELECT DISTINCT * FROM (SELECT 1, 2, 3 UNION SELECT 4, 5, 6)" + 23 1 "SELECT DISTINCT rowid FROM (SELECT 1, 2, 3 UNION SELECT 4, 5, 6)" + + 24 0 "SELECT DISTINCT rowid/2 FROM t1" + 25 1 "SELECT DISTINCT rowid/2, rowid FROM t1" + 26 1 "SELECT DISTINCT rowid/2, b FROM t1 WHERE c = ?" +} { + if {$noop} { + do_distinct_noop_test 1.$tn $sql + } else { + do_distinct_not_noop_test 1.$tn $sql + } +} + +#------------------------------------------------------------------------- +# The following tests - distinct-2.* - test cases where an index is +# used to deliver results in order of the DISTINCT expressions. +# +drop_all_tables +do_execsql_test 2.0 { + CREATE TABLE t1(a, b, c); + + CREATE INDEX i1 ON t1(a, b); + CREATE INDEX i2 ON t1(b COLLATE nocase, c COLLATE nocase); + + INSERT INTO t1 VALUES('a', 'b', 'c'); + INSERT INTO t1 VALUES('A', 'B', 'C'); + INSERT INTO t1 VALUES('a', 'b', 'c'); + INSERT INTO t1 VALUES('A', 'B', 'C'); +} + +foreach {tn sql temptables res} { + 1 "a, b FROM t1" {} {A B a b} + 2 "b, a FROM t1" {} {B A b a} + 3 "a, b, c FROM t1" {hash} {a b c A B C} + 4 "a, b, c FROM t1 ORDER BY a, b, c" {btree} {A B C a b c} + 5 "b FROM t1 WHERE a = 'a'" {} {b} + 6 "b FROM t1" {hash} {b B} + 7 "a FROM t1" {} {A a} + 8 "b COLLATE nocase FROM t1" {} {b} + 9 "b COLLATE nocase FROM t1 ORDER BY b COLLATE nocase" {} {B} +} { + do_execsql_test 2.$tn.1 "SELECT DISTINCT $sql" $res + do_temptables_test 2.$tn.2 "SELECT DISTINCT $sql" $temptables +} + +do_execsql_test 2.A { + SELECT (SELECT DISTINCT o.a FROM t1 AS i) FROM t1 AS o; +} {a A a A} + + + + +finish_test Index: test/e_fkey.test ================================================================== --- test/e_fkey.test +++ test/e_fkey.test @@ -972,17 +972,17 @@ execsql { PRAGMA foreign_keys = OFF; EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1; EXPLAIN QUERY PLAN SELECT rowid FROM track WHERE trackartist = ?; } -} {0 0 {TABLE artist} 0 0 {TABLE track}} +} {0 0 0 {SCAN TABLE artist (~1000000 rows)} 0 0 0 {SCAN TABLE track (~100000 rows)}} do_test e_fkey-25.3 { execsql { PRAGMA foreign_keys = ON; EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1; } -} {0 0 {TABLE artist} 0 0 {TABLE track}} +} {0 0 0 {SCAN TABLE artist (~1000000 rows)} 0 0 0 {SCAN TABLE track (~100000 rows)}} do_test e_fkey-25.4 { execsql { INSERT INTO artist VALUES(5, 'artist 5'); INSERT INTO artist VALUES(6, 'artist 6'); INSERT INTO artist VALUES(7, 'artist 7'); @@ -1094,19 +1094,19 @@ eqp { INSERT INTO artist VALUES(?, ?) } } {} do_test e_fkey-27.3 { eqp { UPDATE artist SET artistid = ?, artistname = ? } } [list \ - 0 0 {TABLE artist} \ - 0 0 {TABLE track WITH INDEX trackindex} \ - 0 0 {TABLE track WITH INDEX trackindex} + 0 0 0 {SCAN TABLE artist (~1000000 rows)} \ + 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)} \ + 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)} ] do_test e_fkey-27.4 { eqp { DELETE FROM artist } } [list \ - 0 0 {TABLE artist} \ - 0 0 {TABLE track WITH INDEX trackindex} + 0 0 0 {SCAN TABLE artist (~1000000 rows)} \ + 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)} ] ########################################################################### ### SECTION 4.1: Composite Foreign Key Constraints Index: test/fts3query.test ================================================================== --- test/fts3query.test +++ test/fts3query.test @@ -114,10 +114,11 @@ CREATE INDEX i1 ON t1(date); CREATE VIRTUAL TABLE ft USING fts3(title); CREATE TABLE bt(title); } } {} +if 0 { do_test fts3query-4.2 { eqp "SELECT t1.number FROM t1, ft WHERE t1.number=ft.rowid ORDER BY t1.date" } {0 0 {TABLE t1 WITH INDEX i1 ORDER BY} 1 1 {TABLE ft VIRTUAL TABLE INDEX 1:}} do_test fts3query-4.3 { eqp "SELECT t1.number FROM ft, t1 WHERE t1.number=ft.rowid ORDER BY t1.date" @@ -126,9 +127,8 @@ eqp "SELECT t1.number FROM t1, bt WHERE t1.number=bt.rowid ORDER BY t1.date" } {0 0 {TABLE t1 WITH INDEX i1 ORDER BY} 1 1 {TABLE bt USING PRIMARY KEY}} do_test fts3query-4.5 { eqp "SELECT t1.number FROM bt, t1 WHERE t1.number=bt.rowid ORDER BY t1.date" } {0 1 {TABLE t1 WITH INDEX i1 ORDER BY} 1 0 {TABLE bt USING PRIMARY KEY}} - +} finish_test - Index: test/index.test ================================================================== --- test/index.test +++ test/index.test @@ -352,11 +352,11 @@ for {set i 1} {$i<=50} {incr i} { execsql "INSERT INTO t3 VALUES('x${i}x',$i,0.$i)" } set sqlite_search_count 0 concat [execsql {SELECT c FROM t3 WHERE b==10}] $sqlite_search_count -} {0.1 3} +} {0.1 2} integrity_check index-11.2 # Numeric strings should compare as if they were numbers. So even if the # strings are not character-by-character the same, if they represent the Index: test/indexedby.test ================================================================== --- test/indexedby.test +++ test/indexedby.test @@ -38,19 +38,22 @@ uplevel "execsql {EXPLAIN QUERY PLAN $sql}" } # These tests are to check that "EXPLAIN QUERY PLAN" is working as expected. # -do_test indexedby-1.2 { - EQP { select * from t1 WHERE a = 10; } -} {0 0 {TABLE t1 WITH INDEX i1}} -do_test indexedby-1.3 { - EQP { select * from t1 ; } -} {0 0 {TABLE t1}} -do_test indexedby-1.4 { - EQP { select * from t1, t2 WHERE c = 10; } -} {0 1 {TABLE t2 WITH INDEX i3} 1 0 {TABLE t1}} +do_execsql_test indexedby-1.2 { + EXPLAIN QUERY PLAN select * from t1 WHERE a = 10; +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}} +do_execsql_test indexedby-1.3 { + EXPLAIN QUERY PLAN select * from t1 ; +} {0 0 0 {SCAN TABLE t1 (~1000000 rows)}} +do_execsql_test indexedby-1.4 { + EXPLAIN QUERY PLAN select * from t1, t2 WHERE c = 10; +} { + 0 0 1 {SEARCH TABLE t2 USING INDEX i3 (c=?) (~10 rows)} + 0 1 0 {SCAN TABLE t1 (~1000000 rows)} +} # Parser tests. Test that an INDEXED BY or NOT INDEX clause can be # attached to a table in the FROM clause, but not to a sub-select or # SQL view. Also test that specifying an index that does not exist or # is attached to a different table is detected as an error. @@ -78,19 +81,21 @@ catchsql { SELECT * FROM v1 INDEXED BY i1 WHERE a = 'one' } } {1 {no such index: i1}} # Tests for single table cases. # -do_test indexedby-3.1 { - EQP { SELECT * FROM t1 NOT INDEXED WHERE a = 'one' AND b = 'two'} -} {0 0 {TABLE t1}} -do_test indexedby-3.2 { - EQP { SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two'} -} {0 0 {TABLE t1 WITH INDEX i1}} -do_test indexedby-3.3 { - EQP { SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' AND b = 'two'} -} {0 0 {TABLE t1 WITH INDEX i2}} +do_execsql_test indexedby-3.1 { + EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE a = 'one' AND b = 'two' +} {0 0 0 {SCAN TABLE t1 (~10000 rows)}} +do_execsql_test indexedby-3.2 { + EXPLAIN QUERY PLAN + SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two' +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}} +do_execsql_test indexedby-3.3 { + EXPLAIN QUERY PLAN + SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' AND b = 'two' +} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}} do_test indexedby-3.4 { catchsql { SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' } } {1 {cannot use index: i2}} do_test indexedby-3.5 { catchsql { SELECT * FROM t1 INDEXED BY i2 ORDER BY a } @@ -100,31 +105,39 @@ } {0 {}} do_test indexedby-3.7 { catchsql { SELECT * FROM t1 INDEXED BY i1 ORDER BY a } } {0 {}} -do_test indexedby-3.8 { - EQP { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 ORDER BY e } -} {0 0 {TABLE t3 WITH INDEX sqlite_autoindex_t3_1 ORDER BY}} -do_test indexedby-3.9 { - EQP { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE e = 10 } -} {0 0 {TABLE t3 WITH INDEX sqlite_autoindex_t3_1}} +do_execsql_test indexedby-3.8 { + EXPLAIN QUERY PLAN + SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 ORDER BY e +} {0 0 0 {SCAN TABLE t3 USING INDEX sqlite_autoindex_t3_1 (~1000000 rows)}} +do_execsql_test indexedby-3.9 { + EXPLAIN QUERY PLAN + SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE e = 10 +} {0 0 0 {SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (e=?) (~1 rows)}} do_test indexedby-3.10 { catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE f = 10 } } {1 {cannot use index: sqlite_autoindex_t3_1}} do_test indexedby-3.11 { catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_2 WHERE f = 10 } } {1 {no such index: sqlite_autoindex_t3_2}} # Tests for multiple table cases. # -do_test indexedby-4.1 { - EQP { SELECT * FROM t1, t2 WHERE a = c } -} {0 0 {TABLE t1} 1 1 {TABLE t2 WITH INDEX i3}} -do_test indexedby-4.2 { - EQP { SELECT * FROM t1 INDEXED BY i1, t2 WHERE a = c } -} {0 1 {TABLE t2} 1 0 {TABLE t1 WITH INDEX i1}} +do_execsql_test indexedby-4.1 { + EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE a = c +} { + 0 0 0 {SCAN TABLE t1 (~1000000 rows)} + 0 1 1 {SEARCH TABLE t2 USING INDEX i3 (c=?) (~10 rows)} +} +do_execsql_test indexedby-4.2 { + EXPLAIN QUERY PLAN SELECT * FROM t1 INDEXED BY i1, t2 WHERE a = c +} { + 0 0 1 {SCAN TABLE t2 (~1000000 rows)} + 0 1 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)} +} do_test indexedby-4.3 { catchsql { SELECT * FROM t1 INDEXED BY i1, t2 INDEXED BY i3 WHERE a=c } } {1 {cannot use index: i1}} @@ -136,19 +149,17 @@ # Test embedding an INDEXED BY in a CREATE VIEW statement. This block # also tests that nothing bad happens if an index refered to by # a CREATE VIEW statement is dropped and recreated. # -do_test indexedby-5.1 { - execsql { - CREATE VIEW v2 AS SELECT * FROM t1 INDEXED BY i1 WHERE a > 5; - } - EQP { SELECT * FROM v2 } -} {0 0 {TABLE t1 WITH INDEX i1}} -do_test indexedby-5.2 { - EQP { SELECT * FROM v2 WHERE b = 10 } -} {0 0 {TABLE t1 WITH INDEX i1}} +do_execsql_test indexedby-5.1 { + CREATE VIEW v2 AS SELECT * FROM t1 INDEXED BY i1 WHERE a > 5; + EXPLAIN QUERY PLAN SELECT * FROM v2 +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~250000 rows)}} +do_execsql_test indexedby-5.2 { + EXPLAIN QUERY PLAN SELECT * FROM v2 WHERE b = 10 +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~25000 rows)}} do_test indexedby-5.3 { execsql { DROP INDEX i1 } catchsql { SELECT * FROM v2 } } {1 {no such index: i1}} do_test indexedby-5.4 { @@ -163,55 +174,57 @@ catchsql { SELECT * FROM v2 } } {0 {}} # Test that "NOT INDEXED" may use the rowid index, but not others. # -do_test indexedby-6.1 { - EQP { SELECT * FROM t1 WHERE b = 10 ORDER BY rowid } -} {0 0 {TABLE t1 WITH INDEX i2 ORDER BY}} -do_test indexedby-6.2 { - EQP { SELECT * FROM t1 NOT INDEXED WHERE b = 10 ORDER BY rowid } -} {0 0 {TABLE t1 USING PRIMARY KEY ORDER BY}} +do_execsql_test indexedby-6.1 { + EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b = 10 ORDER BY rowid +} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)}} +do_execsql_test indexedby-6.2 { + EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE b = 10 ORDER BY rowid +} {0 0 0 {SCAN TABLE t1 USING INTEGER PRIMARY KEY (~100000 rows)}} # Test that "INDEXED BY" can be used in a DELETE statement. # -do_test indexedby-7.1 { - EQP { DELETE FROM t1 WHERE a = 5 } -} {0 0 {TABLE t1 WITH INDEX i1}} -do_test indexedby-7.2 { - EQP { DELETE FROM t1 NOT INDEXED WHERE a = 5 } -} {0 0 {TABLE t1}} -do_test indexedby-7.3 { - EQP { DELETE FROM t1 INDEXED BY i1 WHERE a = 5 } -} {0 0 {TABLE t1 WITH INDEX i1}} -do_test indexedby-7.4 { - EQP { DELETE FROM t1 INDEXED BY i1 WHERE a = 5 AND b = 10} -} {0 0 {TABLE t1 WITH INDEX i1}} -do_test indexedby-7.5 { - EQP { DELETE FROM t1 INDEXED BY i2 WHERE a = 5 AND b = 10} -} {0 0 {TABLE t1 WITH INDEX i2}} +do_execsql_test indexedby-7.1 { + EXPLAIN QUERY PLAN DELETE FROM t1 WHERE a = 5 +} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}} +do_execsql_test indexedby-7.2 { + EXPLAIN QUERY PLAN DELETE FROM t1 NOT INDEXED WHERE a = 5 +} {0 0 0 {SCAN TABLE t1 (~100000 rows)}} +do_execsql_test indexedby-7.3 { + EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 +} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}} +do_execsql_test indexedby-7.4 { + EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 AND b = 10 +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}} +do_execsql_test indexedby-7.5 { + EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i2 WHERE a = 5 AND b = 10 +} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}} do_test indexedby-7.6 { catchsql { DELETE FROM t1 INDEXED BY i2 WHERE a = 5} } {1 {cannot use index: i2}} # Test that "INDEXED BY" can be used in an UPDATE statement. # -do_test indexedby-8.1 { - EQP { UPDATE t1 SET rowid=rowid+1 WHERE a = 5 } -} {0 0 {TABLE t1 WITH INDEX i1}} -do_test indexedby-8.2 { - EQP { UPDATE t1 NOT INDEXED SET rowid=rowid+1 WHERE a = 5 } -} {0 0 {TABLE t1}} -do_test indexedby-8.3 { - EQP { UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 } -} {0 0 {TABLE t1 WITH INDEX i1}} -do_test indexedby-8.4 { - EQP { UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 AND b = 10} -} {0 0 {TABLE t1 WITH INDEX i1}} -do_test indexedby-8.5 { - EQP { UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5 AND b = 10} -} {0 0 {TABLE t1 WITH INDEX i2}} +do_execsql_test indexedby-8.1 { + EXPLAIN QUERY PLAN UPDATE t1 SET rowid=rowid+1 WHERE a = 5 +} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}} +do_execsql_test indexedby-8.2 { + EXPLAIN QUERY PLAN UPDATE t1 NOT INDEXED SET rowid=rowid+1 WHERE a = 5 +} {0 0 0 {SCAN TABLE t1 (~100000 rows)}} +do_execsql_test indexedby-8.3 { + EXPLAIN QUERY PLAN UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 +} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}} +do_execsql_test indexedby-8.4 { + EXPLAIN QUERY PLAN + UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 AND b = 10 +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}} +do_execsql_test indexedby-8.5 { + EXPLAIN QUERY PLAN + UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5 AND b = 10 +} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}} do_test indexedby-8.6 { catchsql { UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5} } {1 {cannot use index: i2}} # Test that bug #3560 is fixed. Index: test/insert4.test ================================================================== --- test/insert4.test +++ test/insert4.test @@ -110,11 +110,11 @@ execsql { DELETE FROM t3; INSERT INTO t3 SELECT DISTINCT * FROM t2; SELECT * FROM t3; } -} {1 9 9 1} +} {9 1 1 9} xferopt_test insert4-2.4.2 0 do_test insert4-2.4.3 { catchsql { DELETE FROM t1; INSERT INTO t1 SELECT DISTINCT * FROM t2; Index: test/minmax3.test ================================================================== --- test/minmax3.test +++ test/minmax3.test @@ -50,10 +50,11 @@ INSERT INTO t1 VALUES('2', NULL, 'three'); INSERT INTO t1 VALUES('2', 'II', 'two'); INSERT INTO t1 VALUES('2', 'V', 'five'); INSERT INTO t1 VALUES('3', 'VI', 'six'); COMMIT; + PRAGMA automatic_index=OFF; } } {} do_test minmax3-1.1.1 { # Linear scan. count { SELECT max(y) FROM t1 WHERE x = '2'; } Index: test/misc4.test ================================================================== --- test/misc4.test +++ test/misc4.test @@ -149,11 +149,11 @@ insert into b values ('+1',3); insert into b values ('+1',4); select a.*, x.* from a, (select key,sum(period) from b group by key) as x - where a.key=x.key; + where a.key=x.key order by 1 desc; } } {01 data01 01 3 +1 data+1 +1 7} # This test case tests the same property as misc4-4.1, but it is # a bit smaller which makes it easier to work with while debugging. Index: test/misc5.test ================================================================== --- test/misc5.test +++ test/misc5.test @@ -503,11 +503,11 @@ ) ) ) ORDER BY LOWER(artist) ASC; } - } {one} + } {two} } # Ticket #1370. Do not overwrite small files (less than 1024 bytes) # when trying to open them as a database. # Index: test/nan.test ================================================================== --- test/nan.test +++ test/nan.test @@ -44,35 +44,35 @@ if {$tcl_platform(platform) != "symbian"} { do_test nan-1.1.2 { sqlite3_bind_double $::STMT 1 +Inf sqlite3_step $::STMT sqlite3_reset $::STMT - db eval {SELECT x, typeof(x) FROM t1} + string tolower [db eval {SELECT x, typeof(x) FROM t1}] } {{} null inf real} do_test nan-1.1.3 { sqlite3_bind_double $::STMT 1 -Inf sqlite3_step $::STMT sqlite3_reset $::STMT - db eval {SELECT x, typeof(x) FROM t1} + string tolower [db eval {SELECT x, typeof(x) FROM t1}] } {{} null inf real -inf real} do_test nan-1.1.4 { sqlite3_bind_double $::STMT 1 -NaN sqlite3_step $::STMT sqlite3_reset $::STMT - db eval {SELECT x, typeof(x) FROM t1} + string tolower [db eval {SELECT x, typeof(x) FROM t1}] } {{} null inf real -inf real {} null} do_test nan-1.1.5 { sqlite3_bind_double $::STMT 1 NaN0 sqlite3_step $::STMT sqlite3_reset $::STMT - db eval {SELECT x, typeof(x) FROM t1} + string tolower [db eval {SELECT x, typeof(x) FROM t1}] } {{} null inf real -inf real {} null {} null} do_test nan-1.1.6 { sqlite3_bind_double $::STMT 1 -NaN0 sqlite3_step $::STMT sqlite3_reset $::STMT - db eval {SELECT x, typeof(x) FROM t1} + string tolower [db eval {SELECT x, typeof(x) FROM t1}] } {{} null inf real -inf real {} null {} null {} null} do_test nan-1.1.7 { db eval { UPDATE t1 SET x=x-x; SELECT x, typeof(x) FROM t1; @@ -232,16 +232,16 @@ # convert from floating point value "-inf" to a string. # do_test nan-4.7 { db eval {DELETE FROM t1} db eval "INSERT INTO t1 VALUES([string repeat 9 309].0)" - db eval {SELECT x, typeof(x) FROM t1} + string tolower [db eval {SELECT x, typeof(x) FROM t1}] } {inf real} do_test nan-4.8 { db eval {DELETE FROM t1} db eval "INSERT INTO t1 VALUES(-[string repeat 9 309].0)" - db eval {SELECT x, typeof(x) FROM t1} + string tolower [db eval {SELECT x, typeof(x) FROM t1}] } {-inf real} } do_test nan-4.9 { db eval {DELETE FROM t1} db eval "INSERT INTO t1 VALUES([string repeat 9 309].0)" @@ -315,11 +315,11 @@ do_test nan-4.20 { db eval {DELETE FROM t1} set big [string repeat 9 10000].0e-9000 db eval "INSERT INTO t1 VALUES($big)" - db eval {SELECT x, typeof(x) FROM t1} + string tolower [db eval {SELECT x, typeof(x) FROM t1}] } {inf real} finish_test Index: test/select6.test ================================================================== --- test/select6.test +++ test/select6.test @@ -456,10 +456,11 @@ # Ticket #1634 # do_test select6-9.1 { execsql { SELECT a.x, b.x FROM t1 AS a, (SELECT x FROM t1 LIMIT 2) AS b + ORDER BY 1, 2 } } {1 1 1 2 2 1 2 2 3 1 3 2 4 1 4 2} do_test select6-9.2 { execsql { SELECT x FROM (SELECT x FROM t1 LIMIT 2); Index: test/selectB.test ================================================================== --- test/selectB.test +++ test/selectB.test @@ -353,11 +353,11 @@ execsql { SELECT * FROM ( SELECT DISTINCT (a/10) FROM t1 UNION ALL SELECT DISTINCT(d%2) FROM t2 ) } - } {0 1 0 1} + } {0 1 1 0} do_test selectB-$ii.20 { execsql { SELECT DISTINCT * FROM ( SELECT DISTINCT (a/10) FROM t1 UNION ALL SELECT DISTINCT(d%2) FROM t2 Index: test/tester.tcl ================================================================== --- test/tester.tcl +++ test/tester.tcl @@ -18,11 +18,11 @@ # test cases are as follows: # # Commands to manipulate the db and the file-system at a high level: # # copy_file FROM TO -# drop_all_table ?DB? +# drop_all_tables ?DB? # forcedelete FILENAME # # Test the capability of the SQLite version built into the interpreter to # determine if a specific test can be run: # @@ -331,15 +331,22 @@ } flush stdout } proc do_execsql_test {testname sql result} { - uplevel do_test $testname [list "execsql {$sql}"] [list $result] + set r {} + foreach x $result {lappend r $x} + uplevel do_test $testname [list "execsql {$sql}"] [list $r] } proc do_catchsql_test {testname sql result} { uplevel do_test $testname [list "catchsql {$sql}"] [list $result] } +proc do_eqp_test {name sql res} { + set r {} + foreach x $res {lappend r $x} + uplevel do_execsql_test $name [list "EXPLAIN QUERY PLAN $sql"] [list $r] +} # Run an SQL script. # Return the number of microseconds per statement. # ADDED test/tkt-54844eea3f.test Index: test/tkt-54844eea3f.test ================================================================== --- /dev/null +++ test/tkt-54844eea3f.test @@ -0,0 +1,67 @@ +# 2011 July 8 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing that bug [54844eea3f] has been fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +set ::testprefix tkt-54844eea3f + +do_test 1.0 { + execsql { + CREATE TABLE t1(a INTEGER PRIMARY KEY); + INSERT INTO t1 VALUES(1); + INSERT INTO t1 VALUES(4); + + CREATE TABLE t2(b INTEGER PRIMARY KEY); + INSERT INTO t2 VALUES(1); + INSERT INTO t2 VALUES(2); + INSERT INTO t2 SELECT b+2 FROM t2; + INSERT INTO t2 SELECT b+4 FROM t2; + INSERT INTO t2 SELECT b+8 FROM t2; + INSERT INTO t2 SELECT b+16 FROM t2; + + CREATE TABLE t3(c INTEGER PRIMARY KEY); + INSERT INTO t3 VALUES(1); + INSERT INTO t3 VALUES(2); + INSERT INTO t3 VALUES(3); + } +} {} + +do_test 1.1 { + execsql { + SELECT 'test-2', t3.c, ( + SELECT count(*) + FROM t1 JOIN (SELECT DISTINCT t3.c AS p FROM t2) AS x ON t1.a=x.p + ) + FROM t3; + } +} {test-2 1 1 test-2 2 0 test-2 3 0} + +do_test 1.2 { + execsql { + CREATE TABLE t4(a, b, c); + INSERT INTO t4 VALUES('a', 1, 'one'); + INSERT INTO t4 VALUES('a', 2, 'two'); + INSERT INTO t4 VALUES('b', 1, 'three'); + INSERT INTO t4 VALUES('b', 2, 'four'); + SELECT ( + SELECT c FROM ( + SELECT * FROM t4 WHERE a=out.a ORDER BY b LIMIT 10 OFFSET 1 + ) WHERE b=out.b + ) FROM t4 AS out; + } +} {{} two {} four} + + +finish_test Index: test/tkt-78e04e52ea.test ================================================================== --- test/tkt-78e04e52ea.test +++ test/tkt-78e04e52ea.test @@ -42,11 +42,11 @@ } {} do_test tkt-78e04-1.4 { execsql { EXPLAIN QUERY PLAN SELECT * FROM "" WHERE "" LIKE 'abc%'; } -} {0 0 {TABLE }} +} {0 0 0 {SCAN TABLE (~500000 rows)}} do_test tkt-78e04-1.5 { execsql { DROP TABLE ""; SELECT name FROM sqlite_master; } @@ -55,14 +55,14 @@ do_test tkt-78e04-2.1 { execsql { CREATE INDEX "" ON t2(x); EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE x=5; } -} {0 0 {TABLE t2 WITH INDEX }} +} {0 0 0 {SEARCH TABLE t2 USING COVERING INDEX (x=?) (~10 rows)}} do_test tkt-78e04-2.2 { execsql { DROP INDEX ""; EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE x=2; } -} {0 0 {TABLE t2}} +} {0 0 0 {SCAN TABLE t2 (~100000 rows)}} finish_test ADDED test/tkt-b351d95f9.test Index: test/tkt-b351d95f9.test ================================================================== --- /dev/null +++ test/tkt-b351d95f9.test @@ -0,0 +1,47 @@ +# 2010 September 28 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. Specifically, +# it tests that ticket [b351d95f9cd5ef17e9d9dbae18f5ca8611190001] has been +# resolved. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +source $testdir/malloc_common.tcl + +do_test tkt-b351d95.1 { + execsql { + CREATE table t1(a,b); + INSERT INTO t1 VALUES('name1','This is a test'); + INSERT INTO t1 VALUES('name2','xyz'); + CREATE TABLE t2(x,y); + INSERT INTO t2 SELECT a, CASE b WHEN 'xyz' THEN null ELSE b END FROM t1; + SELECT x, y FROM t2 ORDER BY x; + } +} {name1 {This is a test} name2 {}} + +do_test tkt-b351d95.2 { + execsql { + DELETE FROM t2; + INSERT INTO t2 SELECT a, coalesce(b,a) FROM t1; + SELECT x, y FROM t2 ORDER BY x; + } +} {name1 {This is a test} name2 xyz} +do_test tkt-b351d95.3 { + execsql { + DELETE FROM t2; + INSERT INTO t2 SELECT a, coalesce(b,a) FROM t1; + SELECT x, y BETWEEN 'xy' AND 'xz' FROM t2 ORDER BY x; + } +} {name1 0 name2 1} + +finish_test Index: test/tkt3442.test ================================================================== --- test/tkt3442.test +++ test/tkt3442.test @@ -47,23 +47,23 @@ # and verify that the query plan is the same. # ifcapable explain { do_test tkt3442-1.2 { EQP { SELECT node FROM listhash WHERE id='5000' LIMIT 1; } - } {0 0 {TABLE listhash WITH INDEX ididx}} + } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}} do_test tkt3442-1.3 { EQP { SELECT node FROM listhash WHERE id="5000" LIMIT 1; } - } {0 0 {TABLE listhash WITH INDEX ididx}} + } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}} } # Some extra tests testing other permutations of 5000. # ifcapable explain { do_test tkt3442-1.4 { EQP { SELECT node FROM listhash WHERE id=5000 LIMIT 1; } - } {0 0 {TABLE listhash WITH INDEX ididx}} + } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}} } do_test tkt3442-1.5 { catchsql { SELECT node FROM listhash WHERE id=[5000] LIMIT 1; } Index: test/tkt3757.test ================================================================== --- test/tkt3757.test +++ test/tkt3757.test @@ -33,13 +33,13 @@ CREATE INDEX t1i1 ON t1(y,z); INSERT INTO t1 VALUES(1,2,'three'); CREATE TABLE t2(a INTEGER, b TEXT); INSERT INTO t2 VALUES(2, 'two'); ANALYZE; - SELECT * FROM sqlite_stat1; + SELECT * FROM sqlite_stat1 ORDER BY 1, 2; } -} {t1 t1i1 {1 1 1}} +} {t1 t1i1 {1 1 1} t2 {} 1} # Modify statistics in order to make the optimizer then that: # # (1) Table T1 has about 250K entries # (2) There are only about 5 distinct values of T1. Index: test/tkt3824.test ================================================================== --- test/tkt3824.test +++ test/tkt3824.test @@ -70,11 +70,11 @@ } {5 9 sort} do_test tkt3824-2.3 { lsort [execsql_status { SELECT a FROM t2 WHERE b=2 AND c IS NULL ORDER BY b; }] -} {5 9 sort} +} {5 9 nosort} do_test tkt3824-3.1 { db eval { CREATE TABLE t3(x,y); INSERT INTO t3 SELECT a, b FROM t1; Index: test/types.test ================================================================== --- test/types.test +++ test/types.test @@ -133,11 +133,11 @@ # Open the table with root-page $rootpage at the btree # level. Return a list that is the length of each record # in the table, in the tables default scanning order. proc record_sizes {rootpage} { - set bt [btree_open test.db 10 0] + set bt [btree_open test.db 10] btree_begin_transaction $bt set c [btree_cursor $bt $rootpage 0] btree_first $c while 1 { lappend res [btree_payload_size $c] ADDED test/unordered.test Index: test/unordered.test ================================================================== --- /dev/null +++ test/unordered.test @@ -0,0 +1,67 @@ +# 2011 April 9 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +set testprefix unordered + +do_execsql_test 1.0 { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a); + INSERT INTO t1 VALUES(1, 'xxx'); + INSERT INTO t1 SELECT a+1, b FROM t1; + INSERT INTO t1 SELECT a+2, b FROM t1; + INSERT INTO t1 SELECT a+4, b FROM t1; + INSERT INTO t1 SELECT a+8, b FROM t1; + INSERT INTO t1 SELECT a+16, b FROM t1; + INSERT INTO t1 SELECT a+32, b FROM t1; + INSERT INTO t1 SELECT a+64, b FROM t1; + ANALYZE; +} {} + +foreach idxmode {ordered unordered} { + if {$idxmode == "unordered"} { + execsql { UPDATE sqlite_stat1 SET stat = stat || ' unordered' } + db close + sqlite3 db test.db + } + foreach {tn sql r(ordered) r(unordered)} { + 1 "SELECT * FROM t1 ORDER BY a" + {0 0 0 {SCAN TABLE t1 USING INDEX i1 (~128 rows)}} + {0 0 0 {SCAN TABLE t1 (~128 rows)}} + 2 "SELECT * FROM t1 WHERE a >?" + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~32 rows)}} + {0 0 0 {SCAN TABLE t1 (~42 rows)}} + 3 "SELECT * FROM t1 WHERE a = ? ORDER BY rowid" + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} + 4 "SELECT max(a) FROM t1" + {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 (~1 rows)}} + 5 "SELECT group_concat(b) FROM t1 GROUP BY a" + {0 0 0 {SCAN TABLE t1 USING INDEX i1 (~128 rows)}} + {0 0 0 {SCAN TABLE t1 (~128 rows)}} + + 6 "SELECT * FROM t1 WHERE a = ?" + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} + 7 "SELECT count(*) FROM t1" + {0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1(~128 rows)}} + {0 0 0 {SCAN TABLE t1 (~128 rows)}} + } { + do_eqp_test 1.$idxmode.$tn $sql $r($idxmode) + } +} + +finish_test Index: test/where3.test ================================================================== --- test/where3.test +++ test/where3.test @@ -225,17 +225,17 @@ CREATE TABLE t302(x, y); ANALYZE; explain query plan SELECT * FROM t302, t301 WHERE t302.x=5 AND t301.a=t302.y; } -} {0 0 {TABLE t302} 1 1 {TABLE t301 USING PRIMARY KEY}} +} {0 0 0 {SCAN TABLE t302 (~1 rows)} 0 1 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test where3-3.1 { execsql { explain query plan SELECT * FROM t301, t302 WHERE t302.x=5 AND t301.a=t302.y; } -} {0 1 {TABLE t302} 1 0 {TABLE t301 USING PRIMARY KEY}} +} {0 0 1 {SCAN TABLE t302 (~1 rows)} 0 1 0 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} # Verify that when there are multiple tables in a join which must be # full table scans that the query planner attempts put the table with # the fewest number of output rows as the outer loop. # @@ -245,20 +245,20 @@ CREATE TABLE t401(p INTEGER PRIMARY KEY, q, r); CREATE TABLE t402(x INTEGER PRIMARY KEY, y, z); EXPLAIN QUERY PLAN SELECT * FROM t400, t401, t402 WHERE t402.z GLOB 'abc*'; } -} {0 2 {TABLE t402} 1 0 {TABLE t400} 2 1 {TABLE t401}} +} {0 0 2 {SCAN TABLE t402 (~500000 rows)} 0 1 0 {SCAN TABLE t400 (~1000000 rows)} 0 2 1 {SCAN TABLE t401 (~1000000 rows)}} do_test where3-4.1 { execsql { EXPLAIN QUERY PLAN SELECT * FROM t400, t401, t402 WHERE t401.r GLOB 'abc*'; } -} {0 1 {TABLE t401} 1 0 {TABLE t400} 2 2 {TABLE t402}} +} {0 0 1 {SCAN TABLE t401 (~500000 rows)} 0 1 0 {SCAN TABLE t400 (~1000000 rows)} 0 2 2 {SCAN TABLE t402 (~1000000 rows)}} do_test where3-4.2 { execsql { EXPLAIN QUERY PLAN SELECT * FROM t400, t401, t402 WHERE t400.c GLOB 'abc*'; } -} {0 0 {TABLE t400} 1 1 {TABLE t401} 2 2 {TABLE t402}} +} {0 0 0 {SCAN TABLE t400 (~500000 rows)} 0 1 1 {SCAN TABLE t401 (~1000000 rows)} 0 2 2 {SCAN TABLE t402 (~1000000 rows)}} finish_test Index: test/where9.test ================================================================== --- test/where9.test +++ test/where9.test @@ -356,36 +356,29 @@ } } {1 80 2 1 80 28 1 80 54 1 80 80 2 80 2 2 80 28 2 80 54 2 80 80 scan 1 sort 1} ifcapable explain { - do_test where9-3.1 { - set r [db eval { - EXPLAIN QUERY PLAN - SELECT t2.a FROM t1, t2 - WHERE t1.a=80 - AND ((t1.c=t2.c AND t1.d=t2.d) OR t1.f=t2.f) - }] - set a [expr {[lsearch $r {TABLE t2 VIA MULTI-INDEX UNION}]>=0}] - set b [expr {[lsearch $r {TABLE t2 WITH INDEX t2f}]>=0}] - set c [expr {([lsearch $r {TABLE t2 WITH INDEX t2c}]>=0)+ - [lsearch $r {TABLE t2 WITH INDEX t2d}]>=0}] - concat $a $b $c - } {1 1 1} - do_test where9-3.2 { - set r [db eval { - EXPLAIN QUERY PLAN - SELECT coalesce(t2.a,9999) - FROM t1 LEFT JOIN t2 ON (t1.c+1=t2.c AND t1.d=t2.d) OR (t1.f||'x')=t2.f - WHERE t1.a=80 - }] - set a [expr {[lsearch $r {TABLE t2 VIA MULTI-INDEX UNION}]>=0}] - set b [expr {[lsearch $r {TABLE t2 WITH INDEX t2f}]>=0}] - set c [expr {([lsearch $r {TABLE t2 WITH INDEX t2c}]>=0)+ - [lsearch $r {TABLE t2 WITH INDEX t2d}]>=0}] - concat $a $b $c - } {1 1 1} + do_execsql_test where9-3.1 { + EXPLAIN QUERY PLAN + SELECT t2.a FROM t1, t2 + WHERE t1.a=80 AND ((t1.c=t2.c AND t1.d=t2.d) OR t1.f=t2.f) + } { + 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)} + 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)} + } + do_execsql_test where9-3.2 { + EXPLAIN QUERY PLAN + SELECT coalesce(t2.a,9999) + FROM t1 LEFT JOIN t2 ON (t1.c+1=t2.c AND t1.d=t2.d) OR (t1.f||'x')=t2.f + WHERE t1.a=80 + } { + 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)} + 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)} + } } # Make sure that INDEXED BY and multi-index OR clauses play well with # one another. # @@ -456,50 +449,33 @@ ifcapable explain { # The (c=31031 OR d IS NULL) clause is preferred over b>1000 because # the former is an equality test which is expected to return fewer rows. # - do_test where9-5.1 { - set r [db eval { - EXPLAIN QUERY PLAN - SELECT a FROM t1 - WHERE b>1000 - AND (c=31031 OR d IS NULL) - }] - set a [expr {[lsearch $r {TABLE t1 VIA MULTI-INDEX UNION}]>=0}] - set b [expr {[lsearch $r {TABLE t1 WITH INDEX t1b}]>=0}] - concat $a $b - } {1 0} + do_execsql_test where9-5.1 { + EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c=31031 OR d IS NULL) + } { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?) (~10 rows)} + 0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?) (~10 rows)} + } # In contrast, b=1000 is preferred over any OR-clause. # - do_test where9-5.2 { - set r [db eval { - EXPLAIN QUERY PLAN - SELECT a FROM t1 - WHERE b=1000 - AND (c=31031 OR d IS NULL) - }] - set a [expr {[lsearch $r {TABLE t1 VIA MULTI-INDEX UNION}]>=0}] - set b [expr {[lsearch $r {TABLE t1 WITH INDEX t1b}]>=0}] - concat $a $b - } {0 1} + do_execsql_test where9-5.2 { + EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b=1000 AND (c=31031 OR d IS NULL) + } { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~5 rows)} + } # Likewise, inequalities in an AND are preferred over inequalities in # an OR. # - do_test where9-5.3 { - set r [db eval { - EXPLAIN QUERY PLAN - SELECT a FROM t1 - WHERE b>1000 - AND (c>=31031 OR d IS NULL) - }] - set a [expr {[lsearch $r {TABLE t1 VIA MULTI-INDEX UNION}]>=0}] - set b [expr {[lsearch $r {TABLE t1 WITH INDEX t1b}]>=0}] - concat $a $b - } {0 1} + do_execsql_test where9-5.3 { + EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c>=31031 OR d IS NULL) + } { + 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>?) (~125000 rows)} + } } ############################################################################ # Make sure OR-clauses work correctly on UPDATE and DELETE statements. ADDED test/whereD.test Index: test/whereD.test ================================================================== --- /dev/null +++ test/whereD.test @@ -0,0 +1,164 @@ +# 2012 August 24 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing that an index may be used as a covering +# index when there are OR expressions in the WHERE clause. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix whereD + +do_execsql_test 1.1 { + CREATE TABLE t(i,j,k,m,n); + CREATE INDEX ijk ON t(i,j,k); + CREATE INDEX jmn ON t(j,m,n); + + INSERT INTO t VALUES(3, 3, 'three', 3, 'tres'); + INSERT INTO t VALUES(2, 2, 'two', 2, 'dos'); + INSERT INTO t VALUES(1, 1, 'one', 1, 'uno'); + INSERT INTO t VALUES(4, 4, 'four', 4, 'cuatro'); +} {} + +do_execsql_test 1.2 { + SELECT k FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2); +} {one two} +do_execsql_test 1.3 { + SELECT k FROM t WHERE (i=1 AND j=1) OR (+i=2 AND j=2); +} {one two} +do_execsql_test 1.4 { + SELECT n FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2); +} {uno dos} +do_execsql_test 1.5 { + SELECT k, n FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2); +} {one uno two dos} +do_execsql_test 1.6 { + SELECT k FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2) OR (i=3 AND j=3); +} {one two three} +do_execsql_test 1.7 { + SELECT n FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2) OR (i=3 AND j=3); +} {uno dos tres} +do_execsql_test 1.8 { + SELECT k FROM t WHERE (i=1 AND j=1) OR (j=2 AND m=2); +} {one two} +do_execsql_test 1.9 { + SELECT k FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2) OR (j=3 AND m=3); +} {one two three} +do_execsql_test 1.10 { + SELECT n FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2) OR (j=3 AND m=3); +} {uno dos tres} +do_execsql_test 1.11 { + SELECT k FROM t WHERE (i=1 AND j=1) OR (j=2 AND m=2) OR (i=3 AND j=3); +} {one two three} +do_execsql_test 1.12 { + SELECT n FROM t WHERE (i=1 AND j=1) OR (j=2 AND m=2) OR (i=3 AND j=3); +} {uno dos tres} +do_execsql_test 1.13 { + SELECT k FROM t WHERE (j=1 AND m=1) OR (i=2 AND j=2) OR (i=3 AND j=3); +} {one two three} +do_execsql_test 1.14 { + SELECT k FROM t WHERE (i=1 AND j=1) OR (j=2 AND i=2) OR (i=3 AND j=3); +} {one two three} +do_execsql_test 1.15 { + SELECT k FROM t WHERE (i=1 AND j=2) OR (i=2 AND j=1) OR (i=3 AND j=4); +} {} +do_execsql_test 1.16 { + SELECT k FROM t WHERE (i=1 AND (j=1 or j=2)) OR (i=3 AND j=3); +} {one three} + +do_execsql_test 2.0 { + CREATE TABLE t1(a,b,c,d); + CREATE INDEX t1b ON t1(b); + CREATE INDEX t1c ON t1(c); + CREATE INDEX t1d ON t1(d); + CREATE TABLE t2(x,y); + CREATE INDEX t2y ON t2(y); + + INSERT INTO t1 VALUES(1,2,3,4); + INSERT INTO t1 VALUES(5,6,7,8); + INSERT INTO t2 VALUES(1,2); + INSERT INTO t2 VALUES(2,7); + INSERT INTO t2 VALUES(3,4); +} {} +do_execsql_test 2.1 { + SELECT a, x FROM t1 JOIN t2 ON +y=d OR x=7 ORDER BY a, x; +} {1 3} +do_execsql_test 2.2 { + SELECT a, x FROM t1 JOIN t2 ON y=d OR x=7 ORDER BY a, x; +} {1 3} + + +# Similar to [do_execsql_test], except that two elements are appended +# to the result - the string "search" and the number of times test variable +# sqlite3_search_count is incremented by running the supplied SQL. e.g. +# +# do_searchcount_test 1.0 { SELECT * FROM t1 } {x y search 2} +# +proc do_searchcount_test {tn sql res} { + uplevel [subst -nocommands { + do_test $tn { + set ::sqlite_search_count 0 + concat [db eval {$sql}] search [set ::sqlite_search_count] + } [list $res] + }] +} + +do_execsql_test 3.0 { + CREATE TABLE t3(a, b, c); + CREATE UNIQUE INDEX i3 ON t3(a, b); + INSERT INTO t3 VALUES(1, 'one', 'i'); + INSERT INTO t3 VALUES(3, 'three', 'iii'); + INSERT INTO t3 VALUES(6, 'six', 'vi'); + INSERT INTO t3 VALUES(2, 'two', 'ii'); + INSERT INTO t3 VALUES(4, 'four', 'iv'); + INSERT INTO t3 VALUES(5, 'five', 'v'); + + CREATE TABLE t4(x PRIMARY KEY, y); + INSERT INTO t4 VALUES('a', 'one'); + INSERT INTO t4 VALUES('b', 'two'); +} {} + +do_searchcount_test 3.1 { + SELECT a, b FROM t3 WHERE (a=1 AND b='one') OR (a=2 AND b='two') +} {1 one 2 two search 2} + +do_searchcount_test 3.2 { + SELECT a, c FROM t3 WHERE (a=1 AND b='one') OR (a=2 AND b='two') +} {1 i 2 ii search 4} + +do_searchcount_test 3.4.1 { + SELECT y FROM t4 WHERE x='a' +} {one search 2} +do_searchcount_test 3.4.2 { + SELECT a, b FROM t3 WHERE + (a=1 AND b=(SELECT y FROM t4 WHERE x='a')) + OR (a=2 AND b='two') +} {1 one 2 two search 4} +do_searchcount_test 3.4.3 { + SELECT a, b FROM t3 WHERE + (a=2 AND b='two') + OR (a=1 AND b=(SELECT y FROM t4 WHERE x='a')) +} {2 two 1 one search 4} +do_searchcount_test 3.4.4 { + SELECT a, b FROM t3 WHERE + (a=2 AND b=(SELECT y FROM t4 WHERE x='b')) + OR (a=1 AND b=(SELECT y FROM t4 WHERE x='a')) +} {2 two 1 one search 6} + +do_searchcount_test 3.5.1 { + SELECT a, b FROM t3 WHERE (a=1 AND b='one') OR rowid=4 +} {1 one 2 two search 2} +do_searchcount_test 3.5.2 { + SELECT a, c FROM t3 WHERE (a=1 AND b='one') OR rowid=4 +} {1 i 2 ii search 2} + +finish_test Index: tool/mksqlite3h.tcl ================================================================== --- tool/mksqlite3h.tcl +++ tool/mksqlite3h.tcl @@ -51,11 +51,11 @@ # set in [open $TOP/manifest] set zDate {} while {![eof $in]} { set line [gets $in] - if {[regexp {^D (2.*[0-9])} $line all date]} { + if {[regexp {^D (2[-0-9T:]+)} $line all date]} { set zDate [string map {T { }} $date] break } } close $in Index: tool/omittest.tcl ================================================================== --- tool/omittest.tcl +++ tool/omittest.tcl @@ -153,11 +153,11 @@ SQLITE_OMIT_COMPLETE \ SQLITE_OMIT_COMPOUND_SELECT \ SQLITE_OMIT_CONFLICT_CLAUSE \ SQLITE_OMIT_DATETIME_FUNCS \ SQLITE_OMIT_DECLTYPE \ - off_SQLITE_OMIT_DISKIO \ + off_SQLITE_OMIT_DISKIO \ SQLITE_OMIT_EXPLAIN \ SQLITE_OMIT_FLAG_PRAGMAS \ SQLITE_OMIT_FLOATING_POINT \ SQLITE_OMIT_FOREIGN_KEY \ SQLITE_OMIT_GET_TABLE \ @@ -180,15 +180,19 @@ SQLITE_OMIT_SUBQUERY \ SQLITE_OMIT_TCL_VARIABLE \ SQLITE_OMIT_TEMPDB \ SQLITE_OMIT_TRACE \ SQLITE_OMIT_TRIGGER \ - SQLITE_OMIT_UTF16 \ - SQLITE_OMIT_VACUUM \ - SQLITE_OMIT_VIEW \ - SQLITE_OMIT_VIRTUALTABLE \ - SQLITE_OMIT_XFER_OPT \ + SQLITE_OMIT_TRUNCATE_OPTIMIZATION \ + SQLITE_OMIT_UNIQUE_ENFORCEMENT \ + SQLITE_OMIT_UTF16 \ + SQLITE_OMIT_VACUUM \ + SQLITE_OMIT_VIEW \ + SQLITE_OMIT_VIRTUALTABLE \ + SQLITE_OMIT_WAL \ + SQLITE_OMIT_WSD \ + SQLITE_OMIT_XFER_OPT \ ] # Process any command line options. process_options $argv