Index: src/global.c ================================================================== --- src/global.c +++ src/global.c @@ -131,19 +131,24 @@ #ifndef SQLITE_USE_URI # define SQLITE_USE_URI 0 #endif +#ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN +# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 +#endif + /* ** The following singleton contains the global configuration for ** the SQLite library. */ SQLITE_WSD struct Sqlite3Config sqlite3Config = { SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ 1, /* bCoreMutex */ SQLITE_THREADSAFE==1, /* bFullMutex */ SQLITE_USE_URI, /* bOpenUri */ + SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ 0x7ffffffe, /* mxStrlen */ 128, /* szLookaside */ 500, /* nLookaside */ {0,0,0,0,0,0,0,0}, /* m */ {0,0,0,0,0,0,0,0,0}, /* mutex */ Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -472,10 +472,15 @@ case SQLITE_CONFIG_URI: { sqlite3GlobalConfig.bOpenUri = va_arg(ap, int); break; } + + case SQLITE_CONFIG_COVERING_INDEX_SCAN: { + sqlite3GlobalConfig.bUseCis = va_arg(ap, int); + break; + } default: { rc = SQLITE_ERROR; break; } Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -1560,10 +1560,22 @@ ** connection is opened. If it is globally disabled, filenames are ** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the ** database connection is opened. By default, URI handling is globally ** disabled. The default value may be changed by compiling with the ** [SQLITE_USE_URI] symbol defined. +** +** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]]
SQLITE_CONFIG_COVERING_INDEX_SCAN +**
This option taks a single integer argument which is interpreted as +** a boolean in order to enable or disable the use of covering indices for +** full table scans in the query optimizer. The default setting is determined +** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on" +** if that compile-time option is omitted. +** The ability to disable the use of covering indices for full table scans +** is because some incorrectly coded legacy applications might malfunction +** malfunction when the optimization is enabled. Providing the ability to +** disable the optimization allows the older, buggy application code to work +** without change even with newer versions of SQLite. ** ** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]] **
SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE **
These options are obsolete and should not be used by new code. ** They are retained for backwards compatibility but are now no-ops. @@ -1586,10 +1598,11 @@ #define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ #define SQLITE_CONFIG_URI 17 /* int */ #define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -966,10 +966,11 @@ #define SQLITE_ColumnCache 0x02 /* Disable the column cache */ #define SQLITE_GroupByOrder 0x04 /* Disable GROUPBY cover of ORDERBY */ #define SQLITE_FactorOutConst 0x08 /* Disable factoring out constants */ #define SQLITE_IdxRealAsInt 0x10 /* Store REAL as INT in indices */ #define SQLITE_DistinctOpt 0x20 /* DISTINCT using indexes */ +#define SQLITE_CoverIdxScan 0x40 /* Disable covering index scans */ #define SQLITE_OptMask 0xff /* Mask of all disablable opts */ /* ** Possible values for the sqlite.magic field. ** The numbers are obtained at random and have no special meaning, other @@ -2459,10 +2460,11 @@ struct Sqlite3Config { int bMemstat; /* True to enable memory status */ int bCoreMutex; /* True to enable core mutexing */ int bFullMutex; /* True to enable full mutexing */ int bOpenUri; /* True to interpret filenames as URIs */ + int bUseCis; /* Use covering indices for full-scans */ int mxStrlen; /* Maximum string length */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ sqlite3_mem_methods m; /* Low-level memory allocation interface */ sqlite3_mutex_methods mutex; /* Low-level mutex interface */ Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -5938,10 +5938,11 @@ { "column-cache", SQLITE_ColumnCache }, { "groupby-order", SQLITE_GroupByOrder }, { "factor-constants", SQLITE_FactorOutConst }, { "real-as-int", SQLITE_IdxRealAsInt }, { "distinct-opt", SQLITE_DistinctOpt }, + { "cover-idx-scan", SQLITE_CoverIdxScan }, }; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB OPT BOOLEAN"); return TCL_ERROR; Index: src/test_malloc.c ================================================================== --- src/test_malloc.c +++ src/test_malloc.c @@ -1194,10 +1194,39 @@ rc = sqlite3_config(SQLITE_CONFIG_URI, bOpenUri); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); return TCL_OK; } + +/* +** Usage: sqlite3_config_cis BOOLEAN +** +** Enables or disables the use of the covering-index scan optimization. +** SQLITE_CONFIG_COVERING_INDEX_SCAN. +*/ +static int test_config_cis( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int rc; + int bUseCis; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "BOOL"); + return TCL_ERROR; + } + if( Tcl_GetBooleanFromObj(interp, objv[1], &bUseCis) ){ + return TCL_ERROR; + } + + rc = sqlite3_config(SQLITE_CONFIG_COVERING_INDEX_SCAN, bUseCis); + Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + + return TCL_OK; +} /* ** Usage: sqlite3_dump_memsys3 FILENAME ** sqlite3_dump_memsys5 FILENAME ** @@ -1445,10 +1474,11 @@ { "sqlite3_config_heap", test_config_heap ,0 }, { "sqlite3_config_memstatus", test_config_memstatus ,0 }, { "sqlite3_config_lookaside", test_config_lookaside ,0 }, { "sqlite3_config_error", test_config_error ,0 }, { "sqlite3_config_uri", test_config_uri ,0 }, + { "sqlite3_config_cis", test_config_cis ,0 }, { "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 }, { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 }, { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 }, { "sqlite3_install_memsys3", test_install_memsys3 ,0 }, { "sqlite3_memdebug_vfs_oom_test", test_vfs_oom_test ,0 }, Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -262,10 +262,11 @@ #define WHERE_UNIQUE 0x04000000 /* Selects no more than one row */ #define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */ #define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */ #define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */ #define WHERE_DISTINCT 0x40000000 /* Correct order for DISTINCT */ +#define WHERE_COVER_SCAN 0x80000000 /* Full scan of a covering index */ /* ** Initialize a preallocated WhereClause structure. */ static void whereClauseInit( @@ -3131,11 +3132,11 @@ /* If currently calculating the cost of using an index (not the IPK ** index), determine if all required column data may be obtained without ** using the main table (i.e. if the index is a covering ** index for this query). If it is, set the WHERE_IDX_ONLY flag in ** wsFlags. Otherwise, set the bLookup variable to true. */ - if( pIdx && wsFlags ){ + if( pIdx ){ Bitmask m = pSrc->colUsed; int j; for(j=0; jnColumn; j++){ int x = pIdx->aiColumn[j]; if( xwctrlFlags & WHERE_ONEPASS_DESIRED)==0 + && sqlite3GlobalConfig.bUseCis +#ifndef SQLITE_OMIT_BUILTIN_TEST + && (pParse->db->flags & SQLITE_CoverIdxScan)==0 +#endif + ){ + /* This index is not useful for indexing, but it is a covering index. + ** A full-scan of the index might be a little faster than a full-scan + ** of the table, so give this case a cost slightly less than a table + ** scan. */ + cost = aiRowEst[0]*3 + pProbe->nColumn; + wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE; + }else if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){ /* 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 @@ -3207,10 +3221,11 @@ ** 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; + wsFlags &= ~WHERE_IDX_ONLY; }else{ log10N = estLog(aiRowEst[0]); cost = nRow; if( pIdx ){ if( bLookup ){ @@ -4250,10 +4265,15 @@ pLevel->op = OP_Prev; }else{ pLevel->op = OP_Next; } pLevel->p1 = iIdxCur; + if( pLevel->plan.wsFlags & WHERE_COVER_SCAN ){ + pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; + }else{ + assert( pLevel->p5==0 ); + } }else #ifndef SQLITE_OMIT_OR_OPTIMIZATION if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ /* Case 4: Two or more separately indexed terms connected by OR @@ -5126,31 +5146,33 @@ ** index name is '*'. */ for(i=0; ia[i]; + w = pLevel->plan.wsFlags; pTabItem = &pTabList->a[pLevel->iFrom]; z = pTabItem->zAlias; if( z==0 ) z = pTabItem->pTab->zName; n = sqlite3Strlen30(z); if( n+nQPlan < sizeof(sqlite3_query_plan)-10 ){ - if( pLevel->plan.wsFlags & WHERE_IDX_ONLY ){ + if( (w & WHERE_IDX_ONLY)!=0 && (w & WHERE_COVER_SCAN)==0 ){ memcpy(&sqlite3_query_plan[nQPlan], "{}", 2); nQPlan += 2; }else{ memcpy(&sqlite3_query_plan[nQPlan], z, n); nQPlan += n; } sqlite3_query_plan[nQPlan++] = ' '; } - testcase( pLevel->plan.wsFlags & WHERE_ROWID_EQ ); - testcase( pLevel->plan.wsFlags & WHERE_ROWID_RANGE ); - if( pLevel->plan.wsFlags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ + testcase( w & WHERE_ROWID_EQ ); + testcase( w & WHERE_ROWID_RANGE ); + if( w & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ memcpy(&sqlite3_query_plan[nQPlan], "* ", 2); nQPlan += 2; - }else if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ + }else if( (w & WHERE_INDEXED)!=0 && (w & WHERE_COVER_SCAN)==0 ){ n = sqlite3Strlen30(pLevel->plan.u.pIdx->zName); if( n+nQPlan < sizeof(sqlite3_query_plan)-2 ){ memcpy(&sqlite3_query_plan[nQPlan], pLevel->plan.u.pIdx->zName, n); nQPlan += n; sqlite3_query_plan[nQPlan++] = ' '; Index: test/analyze6.test ================================================================== --- test/analyze6.test +++ test/analyze6.test @@ -59,18 +59,18 @@ # 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)}} +} {0 0 1 {SCAN TABLE cat USING COVERING INDEX catx (~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)}} +} {0 0 0 {SCAN TABLE cat USING COVERING INDEX catx (~16 rows)} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} # Ticket [83ea97620bd3101645138b7b0e71c12c5498fe3d] 2011-03-30 # If ANALYZE is run on an empty table, make sure indices are used # on the table. Index: test/autovacuum.test ================================================================== --- test/autovacuum.test +++ test/autovacuum.test @@ -112,11 +112,11 @@ set idx [lsearch $::tbl_data [make_str $d $ENTRY_LEN]] set ::tbl_data [lreplace $::tbl_data $idx $idx] } do_test autovacuum-1.$tn.($delete).3 { execsql { - select a from av1 + select a from av1 order by rowid } } $::tbl_data } # All rows have been deleted. Ensure the file has shrunk to 4 pages. Index: test/collate4.test ================================================================== --- test/collate4.test +++ test/collate4.test @@ -92,11 +92,11 @@ } {{} A B a b nosort} do_test collate4-1.1.5 { cksort {SELECT b FROM collate4t1 ORDER BY b COLLATE TEXT} } {{} A B a b nosort} do_test collate4-1.1.6 { - cksort {SELECT b FROM collate4t1 ORDER BY b COLLATE NOCASE} + cksort {SELECT b FROM collate4t1 ORDER BY b COLLATE NOCASE, rowid} } {{} a A b B sort} do_test collate4-1.1.7 { execsql { CREATE TABLE collate4t2( @@ -169,17 +169,17 @@ CREATE INDEX collate4i3 ON collate4t4(a COLLATE TEXT); CREATE INDEX collate4i4 ON collate4t4(b COLLATE NOCASE); } } {} do_test collate4-1.1.22 { - cksort {SELECT a FROM collate4t4 ORDER BY a} + cksort {SELECT a FROM collate4t4 ORDER BY a, rowid} } {{} a A b B sort} do_test collate4-1.1.23 { - cksort {SELECT a FROM collate4t4 ORDER BY a COLLATE NOCASE} + cksort {SELECT a FROM collate4t4 ORDER BY a COLLATE NOCASE, rowid} } {{} a A b B sort} do_test collate4-1.1.24 { - cksort {SELECT a FROM collate4t4 ORDER BY a COLLATE TEXT} + cksort {SELECT a FROM collate4t4 ORDER BY a COLLATE TEXT, rowid} } {{} A B a b nosort} do_test collate4-1.1.25 { cksort {SELECT b FROM collate4t4 ORDER BY b} } {{} A B a b sort} do_test collate4-1.1.26 { @@ -220,11 +220,11 @@ } {{} A B a b sort} do_test collate4-1.2.4 { cksort {SELECT a FROM collate4t1 ORDER BY a, b} } {{} A a B b nosort} do_test collate4-1.2.5 { - cksort {SELECT a FROM collate4t1 ORDER BY a, b COLLATE nocase} + cksort {SELECT a FROM collate4t1 ORDER BY a, b COLLATE nocase, rowid} } {{} a A b B sort} do_test collate4-1.2.6 { cksort {SELECT a FROM collate4t1 ORDER BY a, b COLLATE text} } {{} A a B b nosort} @@ -269,14 +269,14 @@ INSERT INTO collate4t3 VALUES( 'A', 'A' ); CREATE INDEX collate4i2 ON collate4t3(a COLLATE TEXT, b COLLATE NOCASE); } } {} do_test collate4-1.2.15 { - cksort {SELECT a FROM collate4t3 ORDER BY a} + cksort {SELECT a FROM collate4t3 ORDER BY a, rowid} } {{} a A b B sort} do_test collate4-1.2.16 { - cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE nocase} + cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE nocase, rowid} } {{} a A b B sort} do_test collate4-1.2.17 { cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text} } {{} A B a b nosort} do_test collate4-1.2.18 { @@ -362,11 +362,12 @@ execsql { DROP INDEX collate4i1; CREATE INDEX collate4i1 ON collate4t1(a COLLATE TEXT); } count { - SELECT * FROM collate4t2, collate4t1 WHERE a = b; + SELECT * FROM collate4t2, collate4t1 WHERE a = b + ORDER BY collate4t2.rowid, collate4t1.rowid } } {A a A A 19} do_test collate4-2.1.5 { count { SELECT * FROM collate4t2, collate4t1 WHERE b = a; @@ -373,20 +374,22 @@ } } {A A 4} ifcapable subquery { do_test collate4-2.1.6 { count { - SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2); + SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2) + ORDER BY rowid } } {a A 10} do_test collate4-2.1.7 { execsql { DROP INDEX collate4i1; CREATE INDEX collate4i1 ON collate4t1(a); } count { - SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2); + SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2) + ORDER BY rowid } } {a A 6} do_test collate4-2.1.8 { count { SELECT a FROM collate4t1 WHERE a IN ('z', 'a'); @@ -396,11 +399,11 @@ execsql { DROP INDEX collate4i1; CREATE INDEX collate4i1 ON collate4t1(a COLLATE TEXT); } count { - SELECT a FROM collate4t1 WHERE a IN ('z', 'a'); + SELECT a FROM collate4t1 WHERE a IN ('z', 'a') ORDER BY rowid; } } {a A 9} } do_test collate4-2.1.10 { execsql { Index: test/corruptD.test ================================================================== --- test/corruptD.test +++ test/corruptD.test @@ -105,16 +105,16 @@ # containing the offset of the first free block in a page. # do_test corruptD-1.1.1 { incr_change_counter hexio_write test.db [expr 1024+1] FFFF - catchsql { SELECT * FROM t1 } + catchsql { SELECT * FROM t1 ORDER BY rowid } } {1 {database disk image is malformed}} do_test corruptD-1.1.2 { incr_change_counter hexio_write test.db [expr 1024+1] [hexio_render_int32 1021] - catchsql { SELECT * FROM t1 } + catchsql { SELECT * FROM t1 ORDER BY rowid } } {1 {database disk image is malformed}} #------------------------------------------------------------------------- # The following tests, corruptD-1.2.*, focus on the offsets contained # in the first 2 byte of each free-block on the free-list. Index: test/corruptE.test ================================================================== --- test/corruptE.test +++ test/corruptE.test @@ -47,11 +47,11 @@ INSERT OR IGNORE INTO t1 SELECT x*11,y FROM t1; INSERT OR IGNORE INTO t1 SELECT x*13,y FROM t1; INSERT OR IGNORE INTO t1 SELECT x*17,y FROM t1; INSERT OR IGNORE INTO t1 SELECT x*19,y FROM t1; CREATE INDEX t1i1 ON t1(x); - CREATE TABLE t2 AS SELECT x,2 as y FROM t1 WHERE rowid%5!=0; + CREATE TABLE t2 AS SELECT x,2 as y FROM t1 WHERE rowid%5!=0 ORDER BY rowid; COMMIT; } } {} ifcapable {integrityck} { ADDED test/coveridxscan.test Index: test/coveridxscan.test ================================================================== --- /dev/null +++ test/coveridxscan.test @@ -0,0 +1,93 @@ +# 2012 September 17 +# +# 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. +# +#*********************************************************************** +# +# Tests for the optimization which attempts to use a covering index +# for a full-table scan (under the theory that the index will be smaller +# and require less I/O and hence will run faster.) +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +set testprefix coveridxscan + +do_test 1.1 { + db eval { + CREATE TABLE t1(a,b,c); + INSERT INTO t1 VALUES(5,4,3), (4,8,2), (3,2,1); + CREATE INDEX t1ab ON t1(a,b); + CREATE INDEX t1b ON t1(b); + SELECT a FROM t1; + } + # covering index used for the scan, hence values are increasing +} {3 4 5} + +do_test 1.2 { + db eval { + SELECT a, c FROM t1; + } + # There is no covering index, hence the values are in rowid order +} {5 3 4 2 3 1} + +do_test 1.3 { + db eval { + SELECT b FROM t1; + } + # Choice of two indices: use the one with fewest columns +} {2 4 8} + +do_test 2.1 { + optimization_control db cover-idx-scan 0 + db eval {SELECT a FROM t1} + # With the optimization turned off, output in rowid order +} {5 4 3} +do_test 2.2 { + db eval {SELECT a, c FROM t1} +} {5 3 4 2 3 1} +do_test 2.3 { + db eval {SELECT b FROM t1} +} {4 8 2} + +db close +sqlite3_shutdown +sqlite3_config_cis 0 +sqlite3 db test.db + +do_test 3.1 { + db eval {SELECT a FROM t1} + # With the optimization configured off, output in rowid order +} {5 4 3} +do_test 3.2 { + db eval {SELECT a, c FROM t1} +} {5 3 4 2 3 1} +do_test 3.3 { + db eval {SELECT b FROM t1} +} {4 8 2} + +db close +sqlite3_shutdown +sqlite3_config_cis 1 +sqlite3 db test.db + +# The CIS optimization is enabled again. Covering indices are once again +# used for all table scans. +do_test 4.1 { + db eval {SELECT a FROM t1} +} {3 4 5} +do_test 4.2 { + db eval {SELECT a, c FROM t1} +} {5 3 4 2 3 1} +do_test 4.3 { + db eval {SELECT b FROM t1} +} {2 4 8} + + +finish_test Index: test/distinct.test ================================================================== --- test/distinct.test +++ test/distinct.test @@ -173,11 +173,11 @@ 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; + SELECT (SELECT DISTINCT o.a FROM t1 AS i) FROM t1 AS o ORDER BY rowid; } {a A a A} Index: test/e_createtable.test ================================================================== --- test/e_createtable.test +++ test/e_createtable.test @@ -1589,11 +1589,11 @@ do_catchsql_test 4.17.$tn.2 " INSERT INTO $tbl SELECT ((a%2)*a+3), 'three' FROM $tbl " $res do_test e_createtable-4.17.$tn.3 { sqlite3_get_autocommit db } $ac - do_execsql_test 4.17.$tn.4 "SELECT * FROM $tbl" $data + do_execsql_test 4.17.$tn.4 "SELECT * FROM $tbl ORDER BY rowid" $data } catchsql COMMIT # EVIDENCE-OF: R-12645-39772 Or, if a constraint definition does not # include a conflict-clause or it is a CHECK constraint, the default Index: test/e_fkey.test ================================================================== --- test/e_fkey.test +++ test/e_fkey.test @@ -2058,20 +2058,20 @@ } } {} do_test e_fkey-45.2 { execsql { DELETE FROM pA WHERE rowid = 3; - SELECT quote(x) FROM pA; + SELECT quote(x) FROM pA ORDER BY rowid; } } {X'0000' X'9999' X'1234'} do_test e_fkey-45.3 { execsql { SELECT quote(c) FROM cA } } {X'0000'} do_test e_fkey-45.4 { execsql { UPDATE pA SET x = X'8765' WHERE rowid = 4; - SELECT quote(x) FROM pA; + SELECT quote(x) FROM pA ORDER BY rowid; } } {X'0000' X'9999' X'8765'} do_test e_fkey-45.5 { execsql { SELECT quote(c) FROM cB } } {X'9999'} @@ -2323,21 +2323,21 @@ } } {} do_test e_fkey-51.2 { execsql { UPDATE parent SET x = 22; - SELECT * FROM parent ; SELECT 'xxx' ; SELECT a FROM child; + SELECT * FROM parent ORDER BY rowid; SELECT 'xxx' ; SELECT a FROM child; } } {22 21 23 xxx 22} do_test e_fkey-51.3 { execsql { DELETE FROM child; DELETE FROM parent; INSERT INTO parent VALUES(-1); INSERT INTO child VALUES(-1); UPDATE parent SET x = 22; - SELECT * FROM parent ; SELECT 'xxx' ; SELECT a FROM child; + SELECT * FROM parent ORDER BY rowid; SELECT 'xxx' ; SELECT a FROM child; } } {22 23 21 xxx 23} #------------------------------------------------------------------------- Index: test/e_select.test ================================================================== --- test/e_select.test +++ test/e_select.test @@ -1024,11 +1024,11 @@ do_select_tests e_select-4.9 { 1 "SELECT group_concat(one), two FROM b1 GROUP BY two" { 4,5 f 1 o 7,6 s 3,2 t } 2 "SELECT group_concat(one), sum(one) FROM b1 GROUP BY (one>4)" { - 1,4,3,2 10 5,7,6 18 + 1,2,3,4 10 5,6,7 18 } 3 "SELECT group_concat(one) FROM b1 GROUP BY (two>'o'), one%2" { 4 1,5 2,6 3,7 } 4 "SELECT group_concat(one) FROM b1 GROUP BY (one==2 OR two=='o')" { Index: test/eqp.test ================================================================== --- test/eqp.test +++ test/eqp.test @@ -60,11 +60,11 @@ 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} } do_eqp_test 1.4 { SELECT a FROM t1 ORDER BY +a } { - 0 0 0 {SCAN TABLE t1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_eqp_test 1.5 { SELECT a FROM t1 WHERE a=4 } { @@ -164,11 +164,11 @@ } det 2.3.2 "SELECT min(x) FROM t2" { 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1 (~1 rows)} } det 2.3.3 "SELECT min(x), max(x) FROM t2" { - 0 0 0 {SCAN TABLE t2 (~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} } det 2.4.1 "SELECT * FROM t1 WHERE rowid=?" { 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} } @@ -337,19 +337,19 @@ do_eqp_test 4.3.1 { SELECT x FROM t1 UNION SELECT x FROM t2 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 2 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)} } do_eqp_test 4.3.2 { SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1 } { 2 0 0 {SCAN TABLE t1 (~1000000 rows)} - 3 0 0 {SCAN TABLE t2 (~1000000 rows)} + 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)} 4 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 0 0 {COMPOUND SUBQUERIES 1 AND 4 USING TEMP B-TREE (UNION)} } do_eqp_test 4.3.3 { @@ -445,11 +445,11 @@ # 0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 2 2|0|0|SEARCH TABLE t1 USING # INDEX i3 (b=?) (~10 rows) det 5.9 { SELECT (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2 } { - 0 0 0 {SCAN TABLE t2 (~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i4 (~1000000 rows)} 0 0 0 {EXECUTE SCALAR SUBQUERY 1} 1 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)} 0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 2} 2 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows)} } @@ -469,20 +469,20 @@ # EVIDENCE-OF: R-18544-33103 sqlite> EXPLAIN QUERY PLAN SELECT * FROM # (SELECT * FROM t2 WHERE c=1), t1; 0|0|0|SEARCH TABLE t2 USING INDEX i4 # (c=?) (~10 rows) 0|1|1|SCAN TABLE t1 (~1000000 rows) det 5.11 "SELECT * FROM (SELECT * FROM t2 WHERE c=1), t1" { 0 0 0 {SEARCH TABLE t2 USING INDEX i4 (c=?) (~10 rows)} - 0 1 1 {SCAN TABLE t1 (~1000000 rows)} + 0 1 1 {SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)} } # EVIDENCE-OF: R-40701-42164 sqlite> EXPLAIN QUERY PLAN SELECT a FROM # t1 UNION SELECT c FROM t2; 1|0|0|SCAN TABLE t1 (~1000000 rows) # 2|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|COMPOUND SUBQUERIES 1 AND 2 # USING TEMP B-TREE (UNION) det 5.12 "SELECT a FROM t1 UNION SELECT c FROM t2" { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} + 2 0 0 {SCAN TABLE t2 USING COVERING INDEX i4 (~1000000 rows)} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)} } # EVIDENCE-OF: R-61538-24748 sqlite> EXPLAIN QUERY PLAN SELECT a FROM # t1 EXCEPT SELECT d FROM t2 ORDER BY 1; 1|0|0|SCAN TABLE t1 USING Index: test/incrblob.test ================================================================== --- test/incrblob.test +++ test/incrblob.test @@ -435,11 +435,11 @@ INSERT INTO blobs(k, v, i) VALUES('a', 'different', 'connection'); } db2 } {} do_test incrblob-6.2 { execsql { - SELECT rowid FROM blobs + SELECT rowid FROM blobs ORDER BY rowid } } {1 2 3} do_test incrblob-6.3 { set rc [catch { db incrblob blobs v 1 Index: test/intpkey.test ================================================================== --- test/intpkey.test +++ test/intpkey.test @@ -374,11 +374,11 @@ SELECT * FROM t1 WHERE a=0; } } {0 zero entry 0} do_test intpkey-5.2 { execsql { - SELECT rowid, a FROM t1 + SELECT rowid, a FROM t1 ORDER BY rowid } } {-4 -4 0 0 5 5 6 6 11 11} # Test the ability of the COPY command to put data into a # table that contains an integer primary key. Index: test/like.test ================================================================== --- test/like.test +++ test/like.test @@ -404,11 +404,11 @@ set sqlite_like_count } 12 do_test like-5.3 { execsql { CREATE TABLE t2(x TEXT COLLATE NOCASE); - INSERT INTO t2 SELECT * FROM t1; + INSERT INTO t2 SELECT * FROM t1 ORDER BY rowid; CREATE INDEX i2 ON t2(x COLLATE NOCASE); } set sqlite_like_count 0 queryplan { SELECT x FROM t2 WHERE x LIKE 'abc%' ORDER BY 1 @@ -660,12 +660,12 @@ ifcapable explain { do_test like-9.4.3 { set res [sqlite3_exec_hex db { EXPLAIN QUERY PLAN SELECT x FROM t2 WHERE x LIKE '%ff%25' }] - regexp {INDEX i2} $res - } {0} + regexp {SCAN TABLE t2} $res + } {1} } do_test like-9.5.1 { set res [sqlite3_exec_hex db { SELECT x FROM t2 WHERE x LIKE '%fe%25' }] Index: test/stat.test ================================================================== --- test/stat.test +++ test/stat.test @@ -74,15 +74,20 @@ } {} do_execsql_test stat-2.1 { CREATE TABLE t3(a PRIMARY KEY, b); INSERT INTO t3(rowid, a, b) VALUES(2, a_string(111), a_string(222)); - INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; - INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; - INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; - INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; - INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; + INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3 + ORDER BY rowid; + INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3 + ORDER BY rowid; + INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3 + ORDER BY rowid; + INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3 + ORDER BY rowid; + INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3 + ORDER BY rowid; SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload FROM stat WHERE name != 'sqlite_master'; } [list \ sqlite_autoindex_t3_1 / 3 internal 3 368 623 125 \ sqlite_autoindex_t3_1 /000/ 8 leaf 8 946 46 123 \ Index: test/tkt-385a5b56b9.test ================================================================== --- test/tkt-385a5b56b9.test +++ test/tkt-385a5b56b9.test @@ -37,11 +37,11 @@ do_eqp_test 2.1 { SELECT DISTINCT x FROM t2 } { 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2x (~1000000 rows)} } do_eqp_test 2.2 { SELECT DISTINCT y FROM t2 } { - 0 0 0 {SCAN TABLE t2 (~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2y (~1000000 rows)} } do_eqp_test 2.3 { SELECT DISTINCT x, y FROM t2 WHERE y=10 } { 0 0 0 {SEARCH TABLE t2 USING INDEX t2y (y=?) (~1 rows)} } @@ -49,6 +49,5 @@ do_eqp_test 2.4 { SELECT DISTINCT x, y FROM t2 WHERE x=10 } { 0 0 0 {SEARCH TABLE t2 USING INDEX t2x (x=?) (~1 rows)} } 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 0 {SCAN TABLE (~500000 rows)}} +} {0 0 0 {SCAN TABLE USING COVERING INDEX i1 (~500000 rows)}} do_test tkt-78e04-1.5 { execsql { DROP TABLE ""; SELECT name FROM sqlite_master; } Index: test/triggerC.test ================================================================== --- test/triggerC.test +++ test/triggerC.test @@ -220,11 +220,11 @@ catchsql { DROP TRIGGER t2_trig } execsql { DELETE FROM t2 } execsql $tdefn catchsql { INSERT INTO t2 VALUES(10); - SELECT * FROM t2; + SELECT * FROM t2 ORDER BY rowid; } } $rc } do_test triggerC-2.2 { @@ -545,11 +545,11 @@ } { do_test triggerC-4.1.$n { eval concat [execsql " DELETE FROM log; $insert ; - SELECT * FROM log; + SELECT * FROM log ORDER BY rowid; "] } [join $log " "] } #------------------------------------------------------------------------- @@ -582,12 +582,12 @@ } { do_test triggerC-5.1.$n { execsql " BEGIN; $dml ; - SELECT * FROM t5g; - SELECT * FROM t5; + SELECT * FROM t5g ORDER BY rowid; + SELECT * FROM t5 ORDER BY rowid; ROLLBACK; " } [concat $t5g $t5] } do_test triggerC-5.2.0 { @@ -609,12 +609,12 @@ } { do_test triggerC-5.2.$n { execsql " BEGIN; $dml ; - SELECT * FROM t5g; - SELECT * FROM t5; + SELECT * FROM t5g ORDER BY rowid; + SELECT * FROM t5 ORDER BY rowid; ROLLBACK; " } [concat $t5g $t5] } do_test triggerC-5.3.0 { @@ -631,12 +631,12 @@ } { do_test triggerC-5.3.$n { execsql " BEGIN; $dml ; - SELECT * FROM t5g; - SELECT * FROM t5; + SELECT * FROM t5g ORDER BY rowid; + SELECT * FROM t5 ORDER BY rowid; ROLLBACK; " } [concat $t5g $t5] } do_test triggerC-5.3.8 { Index: test/unordered.test ================================================================== --- test/unordered.test +++ test/unordered.test @@ -49,11 +49,11 @@ {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY}} 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)}} + {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (~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)} 0 0 0 {USE TEMP B-TREE FOR GROUP BY}} 6 "SELECT * FROM t1 WHERE a = ?" Index: test/where.test ================================================================== --- test/where.test +++ test/where.test @@ -1096,28 +1096,28 @@ } {1/1 1/4 4/1 4/4 sort} do_test where-14.3 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, x.b } -} {1/1 1/4 4/1 4/4 nosort} +} {1/4 1/1 4/4 4/1 nosort} do_test where-14.4 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, x.b DESC } -} {1/1 1/4 4/1 4/4 nosort} +} {1/4 1/1 4/4 4/1 nosort} do_test where-14.5 { # This test case changed from "nosort" to "sort". See ticket 2a5629202f. cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a||x.b } -} {4/1 4/4 1/1 1/4 sort} +} {4/4 4/1 1/4 1/1 sort} do_test where-14.6 { # This test case changed from "nosort" to "sort". See ticket 2a5629202f. cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a||x.b DESC } -} {4/1 4/4 1/1 1/4 sort} +} {4/4 4/1 1/4 1/1 sort} do_test where-14.7 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, y.a||y.b } } {4/1 4/4 1/1 1/4 sort} @@ -1128,11 +1128,11 @@ } {4/1 4/4 1/1 1/4 sort} do_test where-14.7.2 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a, x.a||x.b } -} {4/1 4/4 1/1 1/4 nosort} +} {4/4 4/1 1/4 1/1 nosort} do_test where-14.8 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, y.a||y.b DESC } } {4/4 4/1 1/4 1/1 sort} Index: test/where9.test ================================================================== --- test/where9.test +++ test/where9.test @@ -690,11 +690,11 @@ } } {scan 0 sort 0} ;# Add 100 to rowids 5 31 57 82 83 84 85 86 87 do_test where9-6.5.4 { db eval { SELECT count(*) FROM t1 UNION ALL - SELECT a FROM t1 WHERE a%100 IN (5,31,57,82,83,84,85,86,87); + SELECT a FROM t1 WHERE a%100 IN (5,31,57,82,83,84,85,86,87) ORDER BY rowid; ROLLBACK; } } {99 105 131 157 182 183 184 185 186 187} do_test where9-6.6.1 {