diff --git a/manifest b/manifest index dc0a0e8fea..9e4943d469 100644 --- a/manifest +++ b/manifest @@ -1,8 +1,8 @@ -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 -C When\srolling\sback\sa\ssavepoint\sto\sthe\sbeginning\sof\sthe\stransaction,\smake\ssure\nto\sinitialize\sthe\sdatabase\ssize\sin\sthe\sbtree\slayer\scorrectly\seven\sif\sthe\ndatabase\ssize\sfield\sof\sthe\sheader\sis\szeroed. -D 2010-04-07T20:29:56 +C Merge\sautomatic\sindex\schanges\sinto\sthe\strunk. +D 2010-04-08T01:16:20 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 4f2f967b7e58a35bb74fb7ec8ae90e0f4ca7868b F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -118,7 +118,7 @@ F src/btreeInt.h 22447d259639271774a931cbf66aa55112846681 F src/build.c 11100b66fb97638d2d874c1d34d8db90650bb1d7 F src/callback.c 908f3e0172c3d4058f4ca0acd42c637c52e9669f F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac -F src/ctime.c ceb247eb31620bba66a94c3f697db489a1652353 +F src/ctime.c 51553a859994d01d8bf3500747f66a890c459774 F src/date.c 485a4409a384310e6d93fd1104a9d0a8658becd9 F src/delete.c 610dc008e88a9599f905f5cbe9577ac9c36e0581 F src/expr.c 6baed2a0448d494233d9c0a610eea018ab386a32 @@ -134,7 +134,7 @@ F src/journal.c b0ea6b70b532961118ab70301c00a33089f9315c F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e F src/loadext.c 1c7a61ce1281041f437333f366a96aa0d29bb581 -F src/main.c 7d89bb6dcc6993a8d32f4f22dae3e57c50a41399 +F src/main.c c0e7192bad5b90544508b241eb2487ac661de890 F src/malloc.c a08f16d134f0bfab6b20c3cd142ebf3e58235a6a F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 89d4ea8d5cdd55635cbaa48ad53132af6294cbb2 @@ -161,22 +161,22 @@ F src/parse.y ace5c7a125d9f2a410e431ee3209034105045f7e F src/pcache.c ace8f6a5ecd4711cc66a1b23053be7109bd437cf F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050 F src/pcache1.c 6dc1871ce8ead9187161c370a58cd06c84221f76 -F src/pragma.c 56d95f76154a5f873c32eae485bb625f3c70be46 -F src/prepare.c 18292e5f365655cd5c5693e09508e90668f7d547 +F src/pragma.c e166ea41544f8e57a08db86dbe87212b7d378fe8 +F src/prepare.c e8164a925274cb7803b5f8ab3cf42f2a508ad33f F src/printf.c 5f5b65a83e63f2096a541a340722a509fa0240a7 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c a1648d98e869937b29f4f697461fe4d60f220a7b F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/select.c 5a08245cb18b7ddf2456274653599cbf738d3830 F src/shell.c c40427c7245535a04a9cb4a417b6cc05c022e6a4 -F src/sqlite.h.in 1b81828af38e040820577fb7a05e7f378add7e6f +F src/sqlite.h.in c5001b77dc0cb046136da65d8dbdf234048be21d F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89 -F src/sqliteInt.h f9b890585c644da05e86b772042ae2059e61fd27 +F src/sqliteInt.h bd7ff54663bdd5b57e0eb8b49aca5a3a3c60119a F src/sqliteLimit.h 3afab2291762b5d09ae20c18feb8e9fa935a60a6 F src/status.c 4df6fe7dce2d256130b905847c6c60055882bdbe F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e -F src/tclsqlite.c 1c7faac8372f1ec72d68b9fcce9d6de00f1b7b86 -F src/test1.c aa9b1e10e834330e7759afb639420117e2422ded +F src/tclsqlite.c ebf7a964f8311bc52266cf75afedae98cd743df3 +F src/test1.c 8b4c246c41e75c3ff033edb2e8c2cf15820861ae F src/test2.c b6b43413d495addd039a88b87d65c839f86b18cb F src/test3.c 4c21700c73a890a47fc685c1097bfb661346ac94 F src/test4.c ad03bb987ddedce928f4258c1e7fa4109a73497d @@ -189,7 +189,7 @@ F src/test_async.c c1656facbaf43cb2e71b62621e5b9eb080e2621c F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2 -F src/test_config.c 5844274bf6cec4af3e6461fb3e2d349082635e81 +F src/test_config.c 8c408fbffbe8082d1d3dc55044ddfd5580f3c9b9 F src/test_demovfs.c a3533803c92e659b80a6c9aaf60852ff0a891c8f F src/test_devsym.c de3c9af2bb9a8b1e44525c449e4ec3f88e3d4110 F src/test_func.c 13b582345fb1185a93e46c53310fae8547dcce20 @@ -210,22 +210,22 @@ F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa F src/test_thread.c 00fed80690ae7f1525483a35861511c48bc579f2 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/tokenize.c 25ceb0f0a746ea1d0f9553787f3f0a56853cfaeb -F src/trigger.c 340c9eca0fb24b1197468d96ba059f867c9834c7 +F src/trigger.c 8927588cb9e6d47f933b53bfe74200fbb504100d F src/update.c c0dc6b75ad28b76b619042d934f337b02acee208 F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685 F src/util.c 32aebf04c10e51ad3977a928b7416bed671b620b F src/vacuum.c b1d542c8919d4d11119f78069e1906a1ad07e0ee -F src/vdbe.c 011f41a5bd40181657322d24590b92874cf8c21e +F src/vdbe.c 2abd931ea2aec3eacc6426677f40cc5a1071d34e F src/vdbe.h 471f6a3dcec4817ca33596fe7f6654d56c0e75f3 -F src/vdbeInt.h ae1e6ba0dd3fb4a886898d2829d748be701b01f8 +F src/vdbeInt.h 19ebc8c2a2e938340051ee65af3f377fb99102d1 F src/vdbeapi.c 74c25680046a116b24b95393914d3669c23305dc F src/vdbeaux.c 3028b2d50df39697e21263685349b5ff1333a29b F src/vdbeblob.c 5327132a42a91e8b7acfb60b9d2c3b1c5c863e0e F src/vdbemem.c 2a82f455f6ca6f78b59fb312f96054c04ae0ead1 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2 -F src/vtab.c 606adf51cd6d4ba51a8c6dccede06a6f7b0dd72d +F src/vtab.c a0f8a40274e4261696ef57aa806de2776ab72cda F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f -F src/where.c 8bbb34e8a836165d92a7d60b80f60be2da9615f9 +F src/where.c 28c270dc93828e36392ddf85b9fd9400303c589c F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45 @@ -250,6 +250,7 @@ F test/auth.test 8f21c160a4562f54f27618e85bac869efcecbcaf F test/auth2.test 270baddc8b9c273682760cffba6739d907bd2882 F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5 F test/autoinc.test 85ef3180a737e6580086a018c09c6f1a52759b46 +F test/autoindex1.test 1ab878fa736b3cf8fa7b99f1d45e09ffa02a6c13 F test/autovacuum.test 25f891bc343a8bf5d9229e2e9ddab9f31a9ab5ec F test/autovacuum_ioerr2.test 598b0663074d3673a9c1bc9a16e80971313bafe6 F test/avtrans.test 1e901d8102706b63534dbd2bdd4d8f16c4082650 @@ -286,7 +287,7 @@ F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91 F test/collate1.test e3eaa48c21e150814be1a7b852d2a8af24458d04 F test/collate2.test 04cebe4a033be319d6ddbb3bbc69464e01700b49 F test/collate3.test d28d2cfab2c3a3d4628ae4b2b7afc9965daa3b4c -F test/collate4.test 4545554388daaa604e5b3def3aa2f7ed6d56e8da +F test/collate4.test 3d3f123f83fd8ccda6f48d617e44e661b9870c7d F test/collate5.test fe0f43c4740d7b71b959cac668d19e42f2e06e4d F test/collate6.test 8be65a182abaac8011a622131486dafb8076e907 F test/collate7.test fac8db7aac3978466c04ae892cc74dcf2bc031aa @@ -798,14 +799,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P af3b72d94a1b6513f02402af3ada5fb5dd390151 -R f78c9929d17e46d2aa7049123364c764 +P a3540c6acf2bb6bdd44c101b63f17ca85e6b68ed d067d9f7a9138e026c1018361127e34385928657 +R 6de3d923fcac21cdb23f5cf757fdd27b U drh -Z 4bb39b0a9403860754323008ca41f9d2 +Z 5a775b8d77bdb4424d535d7f42dbf68e -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) -iD8DBQFLvOtHoxKgR168RlERAr6UAJ9fo0g92PB14exEgBW5OrP0Aot84wCfZZHy -n7PVWjhCjUBrl1g3CQG0QF4= -=WMyU +iD8DBQFLvS5ooxKgR168RlERAsH0AJ9c22BUD9BphL4wVeIQE0+4NLYFKgCeInGI +fyTJFbPX8dMPpCpst7eJaVo= +=jJ9S -----END PGP SIGNATURE----- diff --git a/manifest.uuid b/manifest.uuid index 2798865464..fa334980d3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a3540c6acf2bb6bdd44c101b63f17ca85e6b68ed \ No newline at end of file +bf7b8d863665870a38f0b2335cc71de856a515b4 \ No newline at end of file diff --git a/src/ctime.c b/src/ctime.c index dacdc5883c..e6af8f173a 100644 --- a/src/ctime.c +++ b/src/ctime.c @@ -171,6 +171,9 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_OMIT_AUTOINIT "OMIT_AUTOINIT", #endif +#ifdef SQLITE_OMIT_AUTOMATIC_INDEX + "OMIT_AUTOMATIC_INDEX", +#endif #ifdef SQLITE_OMIT_AUTOVACUUM "OMIT_AUTOVACUUM", #endif diff --git a/src/main.c b/src/main.c index c80250791a..cc03d50ed3 100644 --- a/src/main.c +++ b/src/main.c @@ -1608,7 +1608,7 @@ static int openDatabase( db->autoCommit = 1; db->nextAutovac = -1; db->nextPagesize = 0; - db->flags |= SQLITE_ShortColNames + db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex #if SQLITE_DEFAULT_FILE_FORMAT<4 | SQLITE_LegacyFileFmt #endif diff --git a/src/pragma.c b/src/pragma.c index bd96e47c18..f03078f246 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -173,6 +173,9 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){ { "legacy_file_format", SQLITE_LegacyFileFmt }, { "fullfsync", SQLITE_FullFSync }, { "reverse_unordered_selects", SQLITE_ReverseOrder }, +#ifndef SQLITE_OMIT_AUTOMATIC_INDEX + { "automatic_index", SQLITE_AutoIndex }, +#endif #ifdef SQLITE_DEBUG { "sql_trace", SQLITE_SqlTrace }, { "vdbe_listing", SQLITE_VdbeListing }, diff --git a/src/prepare.c b/src/prepare.c index f1b1e00579..44b7775138 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -579,6 +579,7 @@ static int sqlite3Prepare( sqlite3VtabUnlockList(db); pParse->db = db; + pParse->nQueryLoop = (double)1; if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ char *zSqlCopy; int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; @@ -600,6 +601,7 @@ static int sqlite3Prepare( }else{ sqlite3RunParser(pParse, zSql, &zErrMsg); } + assert( 1==(int)pParse->nQueryLoop ); if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM; diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 4777a87ec7..24d4e8fbc5 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -5199,10 +5199,18 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); ** A non-zero value in this counter may indicate an opportunity to ** improvement performance through careful use of indices. ** +**
SQLITE_STMTSTATUS_AUTOINDEX
+**
^This is the number of rows inserted into transient indices that +** were created automatically in order to help joins run faster. +** A non-zero value in this counter may indicate an opportunity to +** improvement performance by adding permanent indices that do not +** need to be reinitialized each time the statement is run.
+** ** */ #define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 #define SQLITE_STMTSTATUS_SORT 2 +#define SQLITE_STMTSTATUS_AUTOINDEX 3 /* ** CAPI3REF: Custom Page Cache Object diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c674b71bd2..6007b8af99 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -301,6 +301,7 @@ */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 +# define float sqlite_int64 # define LONGDOUBLE_TYPE sqlite_int64 # ifndef SQLITE_BIG_DBL # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) @@ -911,6 +912,7 @@ struct sqlite3 { #define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */ #define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */ #define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */ +#define SQLITE_AutoIndex 0x08000000 /* Enable automatic indexes */ /* ** Bits of the sqlite3.flags field that are used by the @@ -1773,6 +1775,9 @@ typedef u64 Bitmask; ** and the next table on the list. The parser builds the list this way. ** But sqlite3SrcListShiftJoinType() later shifts the jointypes so that each ** jointype expresses the join between the table and the previous table. +** +** In the colUsed field, the high-order bit (bit 63) is set if the table +** contains more than 63 columns and the 64-th or later column is used. */ struct SrcList { i16 nSrc; /* Number of tables or subqueries in the FROM clause */ @@ -1884,7 +1889,7 @@ struct WhereLevel { #define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */ #define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */ #define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */ -#define WHERE_OMIT_OPEN 0x0010 /* Table cursor are already open */ +#define WHERE_OMIT_OPEN 0x0010 /* Table cursors are already open */ #define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */ #define WHERE_FORCE_TABLE 0x0040 /* Do not use an index-only search */ #define WHERE_ONETABLE_ONLY 0x0080 /* Only code the 1st table in pTabList */ @@ -1907,6 +1912,7 @@ struct WhereInfo { 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 */ WhereLevel a[1]; /* Information about each nest loop in WHERE */ }; @@ -2148,6 +2154,7 @@ struct Parse { u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ u8 disableTriggers; /* True to disable triggers */ + double nQueryLoop; /* Estimated number of iterations of a query */ /* Above is constant between recursions. Below is reset before and after ** each recursion */ diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 404a167c35..02d67c4798 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -132,7 +132,7 @@ struct SqliteDb { int maxStmt; /* The next maximum number of stmtList */ int nStmt; /* Number of statements in stmtList */ IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */ - int nStep, nSort; /* Statistics for most recent operation */ + int nStep, nSort, nIndex; /* Statistics for most recent operation */ int nTransaction; /* Number of nested [transaction] methods */ }; @@ -1351,6 +1351,7 @@ static int dbEvalStep(DbEvalContext *p){ pDb->nStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1); pDb->nSort = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1); + pDb->nIndex = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_AUTOINDEX,1); dbReleaseColumnNames(p); p->pPreStmt = 0; @@ -2528,7 +2529,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } /* - ** $db status (step|sort) + ** $db status (step|sort|autoindex) ** ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or ** SQLITE_STMTSTATUS_SORT for the most recent eval. @@ -2545,8 +2546,11 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ v = pDb->nStep; }else if( strcmp(zOp, "sort")==0 ){ v = pDb->nSort; + }else if( strcmp(zOp, "autoindex")==0 ){ + v = pDb->nIndex; }else{ - Tcl_AppendResult(interp, "bad argument: should be step or sort", + Tcl_AppendResult(interp, + "bad argument: should be autoindex, step, or sort", (char*)0); return TCL_ERROR; } diff --git a/src/test1.c b/src/test1.c index 7c5d497157..b5fda952c0 100644 --- a/src/test1.c +++ b/src/test1.c @@ -2025,6 +2025,7 @@ static int test_stmt_status( } aOp[] = { { "SQLITE_STMTSTATUS_FULLSCAN_STEP", SQLITE_STMTSTATUS_FULLSCAN_STEP }, { "SQLITE_STMTSTATUS_SORT", SQLITE_STMTSTATUS_SORT }, + { "SQLITE_STMTSTATUS_AUTOINDEX", SQLITE_STMTSTATUS_AUTOINDEX }, }; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT PARAMETER RESETFLAG"); diff --git a/src/test_config.c b/src/test_config.c index 7350392646..8544ee5356 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -127,6 +127,12 @@ static void set_options(Tcl_Interp *interp){ Tcl_SetVar2(interp, "sqlite_options", "autoinc", "1", TCL_GLOBAL_ONLY); #endif +#ifdef SQLITE_OMIT_AUTOMATIC_INDEX + Tcl_SetVar2(interp, "sqlite_options", "autoindex", "0", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "autoindex", "1", TCL_GLOBAL_ONLY); +#endif + #ifdef SQLITE_OMIT_AUTOVACUUM Tcl_SetVar2(interp, "sqlite_options", "autovacuum", "0", TCL_GLOBAL_ONLY); #else diff --git a/src/trigger.c b/src/trigger.c index f57d5600f2..66464bdae4 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -826,6 +826,7 @@ static TriggerPrg *codeRowTrigger( pSubParse->pToplevel = pTop; pSubParse->zAuthContext = pTrigger->zName; pSubParse->eTriggerOp = pTrigger->op; + pSubParse->nQueryLoop = pParse->nQueryLoop; v = sqlite3GetVdbe(pSubParse); if( v ){ diff --git a/src/vdbe.c b/src/vdbe.c index d5e4b4d197..c1b0eea313 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -1383,7 +1383,7 @@ case OP_Function: { for(i=0; ip2, pArg); + REGISTER_TRACE(pOp->p2+i, pArg); } assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC ); @@ -3082,6 +3082,14 @@ case OP_OpenWrite: { ** this opcode. Then this opcode was call OpenVirtual. But ** that created confusion with the whole virtual-table idea. */ +/* Opcode: OpenAutoindex P1 P2 * P4 * +** +** This opcode works the same as OP_OpenEphemeral. It has a +** different name to distinguish its use. Tables created using +** by this opcode will be used for automatically created transient +** indices in joins. +*/ +case OP_OpenAutoindex: case OP_OpenEphemeral: { VdbeCursor *pCx; static const int openFlags = diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 489e8da6a7..e8e1585449 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -311,7 +311,7 @@ struct Vdbe { int btreeMask; /* Bitmask of db->aDb[] entries referenced */ i64 startTime; /* Time when query started - used for profiling */ BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */ - int aCounter[2]; /* Counters used by sqlite3_stmt_status() */ + int aCounter[3]; /* Counters used by sqlite3_stmt_status() */ char *zSql; /* Text of the SQL statement that generated this */ void *pFree; /* Free this when deleting the vdbe */ i64 nFkConstraint; /* Number of imm. FK constraints this VM */ diff --git a/src/vtab.c b/src/vtab.c index cbb7523549..24e922e8dd 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -657,6 +657,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ }else{ pParse->declareVtab = 1; pParse->db = db; + pParse->nQueryLoop = 1; if( SQLITE_OK==sqlite3RunParser(pParse, zCreateTable, &zErr) && pParse->pNewTable diff --git a/src/where.c b/src/where.c index bd82bb01d3..81a74fa9a2 100644 --- a/src/where.c +++ b/src/where.c @@ -235,6 +235,7 @@ struct WhereCost { #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_IN_ABLE 0x000f1000 /* Able to support an IN operator */ #define WHERE_TOP_LIMIT 0x00100000 /* xEXPR or x>=EXPR constraint */ @@ -244,6 +245,7 @@ struct WhereCost { #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 */ /* ** Initialize a preallocated WhereClause structure. @@ -1635,6 +1637,243 @@ static void bestOrClauseIndex( #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ } +#ifndef SQLITE_OMIT_AUTOMATIC_INDEX +/* +** Return TRUE if the WHERE clause term pTerm is of a form where it +** could be used with an index to access pSrc, assuming an appropriate +** index existed. +*/ +static int termCanDriveIndex( + WhereTerm *pTerm, /* WHERE clause term to check */ + struct SrcList_item *pSrc, /* Table we are trying to access */ + Bitmask notReady /* Tables in outer loops of the join */ +){ + char aff; + if( pTerm->leftCursor!=pSrc->iCursor ) return 0; + if( pTerm->eOperator!=WO_EQ ) return 0; + if( (pTerm->prereqRight & notReady)!=0 ) return 0; + aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; + if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; + return 1; +} +#endif + +#ifndef SQLITE_OMIT_AUTOMATIC_INDEX +/* +** If the query plan for pSrc specified in pCost is a full table scan +** and indexing is allows (if there is no NOT INDEXED clause) and it +** possible to construct a transient index that would perform better +** than a full table scan even when the cost of constructing the index +** is taken into account, then alter the query plan to use the +** transient index. +*/ +static void bestAutomaticIndex( + 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 */ + WhereCost *pCost /* Lowest cost query plan */ +){ + double nTableRow; /* Rows in the input table */ + double logN; /* log(nTableRow) */ + 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->db->flags & SQLITE_AutoIndex)==0 ){ + /* Automatic indices are disabled at run-time */ + return; + } + if( (pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)!=0 ){ + /* We already have some kind of index in use for this query. */ + return; + } + if( pSrc->notIndexed ){ + /* The NOT INDEXED clause appears in the SQL. */ + return; + } + + assert( pParse->nQueryLoop >= (double)1 ); + nTableRow = pSrc->pIndex ? pSrc->pIndex->aiRowEst[0] : 1000000; + 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 */ + return; + } + + /* Search for any equality comparison term */ + pTable = pSrc->pTab; + pWCEnd = &pWC->a[pWC->nTerm]; + for(pTerm=pWC->a; pTermrCost, costTempIdx)); + pCost->rCost = costTempIdx; + pCost->nRow = logN + 1; + pCost->plan.wsFlags = WHERE_TEMP_INDEX; + pCost->used = pTerm->prereqRight; + break; + } + } +} +#else +# define bestAutomaticIndex(A,B,C,D,E) /* no-op */ +#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ + + +#ifndef SQLITE_OMIT_AUTOMATIC_INDEX +/* +** Generate code to construct the Index object for an automatic index +** and to set up the WhereLevel object pLevel so that the code generator +** makes use of the automatic index. +*/ +static void constructAutomaticIndex( + Parse *pParse, /* The parsing context */ + WhereClause *pWC, /* The WHERE clause */ + struct SrcList_item *pSrc, /* The FROM clause term to get the next index */ + Bitmask notReady, /* Mask of cursors that are not available */ + WhereLevel *pLevel /* Write new index here */ +){ + int nColumn; /* Number of columns in the constructed index */ + WhereTerm *pTerm; /* A single term of the WHERE clause */ + WhereTerm *pWCEnd; /* End of pWC->a[] */ + int nByte; /* Byte of memory needed for pIdx */ + Index *pIdx; /* Object describing the transient index */ + Vdbe *v; /* Prepared statement under construction */ + int regIsInit; /* Register set by initialization */ + int addrInit; /* Address of the initialization bypass jump */ + Table *pTable; /* The table being indexed */ + KeyInfo *pKeyinfo; /* Key information for the index */ + int addrTop; /* Top of the index fill loop */ + int regRecord; /* Register holding an index record */ + int n; /* Column counter */ + int i; /* Loop counter */ + int mxBitCol; /* Maximum column in pSrc->colUsed */ + CollSeq *pColl; /* Collating sequence to on a column */ + Bitmask idxCols; /* Bitmap of columns used for indexing */ + Bitmask extraCols; /* Bitmap of additional columns */ + + /* Generate code to skip over the creation and initialization of the + ** transient index on 2nd and subsequent iterations of the loop. */ + v = pParse->pVdbe; + assert( v!=0 ); + regIsInit = ++pParse->nMem; + addrInit = sqlite3VdbeAddOp1(v, OP_If, regIsInit); + sqlite3VdbeAddOp2(v, OP_Integer, 1, regIsInit); + + /* Count the number of columns that will be added to the index + ** and used to match WHERE clause constraints */ + nColumn = 0; + pTable = pSrc->pTab; + pWCEnd = &pWC->a[pWC->nTerm]; + idxCols = 0; + for(pTerm=pWC->a; pTermu.leftColumn; + Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<0 ); + pLevel->plan.nEq = nColumn; + + /* Count the number of additional columns needed to create a + ** covering index. A "covering index" is an index that contains all + ** columns that are needed by the query. With a covering index, the + ** original table never needs to be accessed. Automatic indices must + ** be a covering index because the index will not be updated if the + ** original table changes and the index and table cannot both be used + ** if they go out of sync. + */ + extraCols = pSrc->colUsed & (~idxCols | (((Bitmask)1)<<(BMS-1))); + mxBitCol = (pTable->nCol >= BMS-1) ? BMS-1 : pTable->nCol; + for(i=0; icolUsed & (((Bitmask)1)<<(BMS-1)) ){ + nColumn += pTable->nCol - BMS + 1; + } + pLevel->plan.wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WO_EQ; + + /* Construct the Index object to describe this index */ + nByte = sizeof(Index); + nByte += nColumn*sizeof(int); /* Index.aiColumn */ + nByte += nColumn*sizeof(char*); /* Index.azColl */ + nByte += nColumn; /* Index.aSortOrder */ + pIdx = sqlite3DbMallocZero(pParse->db, nByte); + if( pIdx==0 ) return; + pLevel->plan.u.pIdx = pIdx; + pIdx->azColl = (char**)&pIdx[1]; + pIdx->aiColumn = (int*)&pIdx->azColl[nColumn]; + pIdx->aSortOrder = (u8*)&pIdx->aiColumn[nColumn]; + pIdx->zName = "auto-index"; + pIdx->nColumn = nColumn; + pIdx->pTable = pTable; + n = 0; + idxCols = 0; + for(pTerm=pWC->a; pTermu.leftColumn; + Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<pExpr; + idxCols |= cMask; + pIdx->aiColumn[n] = pTerm->u.leftColumn; + pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); + pIdx->azColl[n] = pColl->zName; + n++; + } + } + } + assert( n==pLevel->plan.nEq ); + + /* Add additional columns needed to make the automatic index into + ** a covering index */ + for(i=0; iaiColumn[n] = i; + pIdx->azColl[n] = "BINARY"; + n++; + } + } + if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){ + for(i=BMS-1; inCol; i++){ + pIdx->aiColumn[n] = i; + pIdx->azColl[n] = "BINARY"; + n++; + } + } + assert( n==nColumn ); + + /* Create the automatic index */ + pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx); + assert( pLevel->iIdxCur>=0 ); + sqlite3VdbeAddOp4(v, OP_OpenAutoindex, pLevel->iIdxCur, nColumn+1, 0, + (char*)pKeyinfo, P4_KEYINFO_HANDOFF); + VdbeComment((v, "for %s", pTable->zName)); + + /* Fill the automatic index with content */ + addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); + regRecord = sqlite3GetTempReg(pParse); + sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 1); + sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); + sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); + sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); + sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); + sqlite3VdbeJumpHere(v, addrTop); + sqlite3ReleaseTempReg(pParse, regRecord); + + /* Jump here when skipping the initialization */ + sqlite3VdbeJumpHere(v, addrInit); +} +#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ + #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Allocate and populate an sqlite3_index_info structure. It is the @@ -2423,7 +2662,7 @@ static void bestBtreeIndex( /* If currently calculating the cost of using an index (not the IPK ** index), determine if all required column data may be obtained without - ** seeking to entries in the main table (i.e. if the index is a covering + ** 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 ){ @@ -2481,10 +2720,10 @@ static void bestBtreeIndex( /**** Cost of using this index has now been computed ****/ WHERETRACE(( - "tbl=%s idx=%s nEq=%d nInMul=%d nBound=%d bSort=%d bLookup=%d" - " wsFlags=%d (nRow=%.2f cost=%.2f)\n", + "%s(%s): nEq=%d nInMul=%d nBound=%d bSort=%d bLookup=%d wsFlags=0x%x\n" + " notReady=0x%llx nRow=%.2f cost=%.2f used=0x%llx\n", pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"), - nEq, nInMul, nBound, bSort, bLookup, wsFlags, nRow, cost + nEq, nInMul, nBound, bSort, bLookup, wsFlags, notReady, nRow, cost, used )); /* If this index is the best we have seen so far, then record this @@ -2529,6 +2768,7 @@ static void bestBtreeIndex( )); bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); + bestAutomaticIndex(pParse, pWC, pSrc, notReady, pCost); pCost->plan.wsFlags |= eqTermMask; } @@ -3471,6 +3711,13 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ } sqlite3DbFree(db, pInfo); } + if( pWInfo->a[i].plan.wsFlags & WHERE_TEMP_INDEX ){ + Index *pIdx = pWInfo->a[i].plan.u.pIdx; + if( pIdx ){ + sqlite3DbFree(db, pIdx->zColAff); + sqlite3DbFree(db, pIdx); + } + } } whereClauseClear(pWInfo->pWC); sqlite3DbFree(db, pWInfo); @@ -3617,6 +3864,8 @@ WhereInfo *sqlite3WhereBegin( sizeof(WhereMaskSet) ); if( db->mallocFailed ){ + sqlite3DbFree(db, pWInfo); + pWInfo = 0; goto whereBeginError; } pWInfo->nLevel = nTabList; @@ -3625,6 +3874,7 @@ WhereInfo *sqlite3WhereBegin( pWInfo->iBreak = sqlite3VdbeMakeLabel(v); pWInfo->pWC = pWC = (WhereClause *)&((u8 *)pWInfo)[nByteWInfo]; pWInfo->wctrlFlags = wctrlFlags; + pWInfo->savedNQueryLoop = pParse->nQueryLoop; pMaskSet = (WhereMaskSet*)&pWC[1]; /* Split the WHERE clause into separate subexpressions where each @@ -3804,13 +4054,16 @@ WhereInfo *sqlite3WhereBegin( } andFlags &= bestPlan.plan.wsFlags; pLevel->plan = bestPlan.plan; - if( bestPlan.plan.wsFlags & WHERE_INDEXED ){ + 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++; }else{ pLevel->iIdxCur = -1; } notReady &= ~getMask(pMaskSet, pTabList->a[bestJ].iCursor); pLevel->iFrom = (u8)bestJ; + if( bestPlan.nRow>=(double)1 ) pParse->nQueryLoop *= bestPlan.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 @@ -3857,6 +4110,7 @@ WhereInfo *sqlite3WhereBegin( ** searching those tables. */ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ + notReady = ~(Bitmask)0; for(i=0, pLevel=pWInfo->a; izAlias ){ zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias); } - if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ + 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 ){ @@ -3892,8 +4148,11 @@ WhereInfo *sqlite3WhereBegin( #endif /* SQLITE_OMIT_EXPLAIN */ pTabItem = &pTabList->a[pLevel->iFrom]; pTab = pTabItem->pTab; + pLevel->iTabCur = pTabItem->iCursor; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ) continue; + if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){ + /* Do nothing */ + }else #ifndef SQLITE_OMIT_VIRTUALTABLE if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); @@ -3916,7 +4175,11 @@ WhereInfo *sqlite3WhereBegin( }else{ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); } - pLevel->iTabCur = pTabItem->iCursor; +#ifndef SQLITE_OMIT_AUTOMATIC_INDEX + if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){ + constructAutomaticIndex(pParse, pWC, pTabItem, notReady, pLevel); + }else +#endif if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ Index *pIx = pLevel->plan.u.pIdx; KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx); @@ -3928,8 +4191,10 @@ WhereInfo *sqlite3WhereBegin( VdbeComment((v, "%s", pIx->zName)); } sqlite3CodeVerifySchema(pParse, iDb); + notReady &= ~getMask(pWC->pMaskSet, pTabItem->iCursor); } pWInfo->iTop = sqlite3VdbeCurrentAddr(v); + if( db->mallocFailed ) goto whereBeginError; /* Generate the code to do the search. Each iteration of the for ** loop below generates code for a single nested loop of the VM @@ -3997,7 +4262,10 @@ WhereInfo *sqlite3WhereBegin( /* Jump here if malloc fails */ whereBeginError: - whereInfoFree(db, pWInfo); + if( pWInfo ){ + pParse->nQueryLoop = pWInfo->savedNQueryLoop; + whereInfoFree(db, pWInfo); + } return 0; } @@ -4067,12 +4335,15 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); - if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ) continue; - if( (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0 ){ - if( !pWInfo->okOnePass && (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 ){ + if( (pTab->tabFlags & TF_Ephemeral)==0 + && pTab->pSelect==0 + && (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0 + ){ + int ws = pLevel->plan.wsFlags; + if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){ sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); } - if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ + if( (ws & (WHERE_INDEXED|WHERE_TEMP_INDEX)) == WHERE_INDEXED ){ sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); } } @@ -4120,6 +4391,9 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ /* Final cleanup */ - whereInfoFree(db, pWInfo); + if( pWInfo ){ + pParse->nQueryLoop = pWInfo->savedNQueryLoop; + whereInfoFree(db, pWInfo); + } return; } diff --git a/test/autoindex1.test b/test/autoindex1.test new file mode 100644 index 0000000000..1f8b9ef87f --- /dev/null +++ b/test/autoindex1.test @@ -0,0 +1,93 @@ +# 2010 April 07 +# +# 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 testing automatic index creation logic. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# If the library is not compiled with automatic index support then +# skip all tests in this file. +# +ifcapable {!autoindex} { + finish_test + return +} + +# With automatic index turned off, we do a full scan of the T2 table +do_test autoindex1-100 { + db eval { + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,11); + INSERT INTO t1 VALUES(2,22); + INSERT INTO t1 SELECT a+2, b+22 FROM t1; + INSERT INTO t1 SELECT a+4, b+44 FROM t1; + CREATE TABLE t2(c,d); + INSERT INTO t2 SELECT a, 900+b FROM t1; + } + db eval { + PRAGMA automatic_index=OFF; + SELECT b, d FROM t1 JOIN t2 ON a=c ORDER BY b; + } +} {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988} +do_test autoindex1-101 { + db status step +} {63} +do_test autoindex1-102 { + db status autoindex +} {0} + +# With autoindex turned on, we build an index once and then use that index +# to find T2 values. +do_test autoindex1-110 { + db eval { + PRAGMA automatic_index=ON; + SELECT b, d FROM t1 JOIN t2 ON a=c ORDER BY b; + } +} {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988} +do_test autoindex1-111 { + db status step +} {7} +do_test autoindex1-112 { + db status autoindex +} {7} + +# The same test as above, but this time the T2 query is a subquery rather +# than a join. +do_test autoindex1-200 { + db eval { + PRAGMA automatic_index=OFF; + SELECT b, (SELECT d FROM t2 WHERE c=a) FROM t1; + } +} {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988} +do_test autoindex1-201 { + db status step +} {35} +do_test autoindex1-202 { + db status autoindex +} {0} +do_test autoindex1-210 { + db eval { + PRAGMA automatic_index=ON; + SELECT b, (SELECT d FROM t2 WHERE c=a) FROM t1; + } +} {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988} +do_test autoindex1-211 { + db status step +} {7} +do_test autoindex1-212 { + db status autoindex +} {7} + + + +finish_test diff --git a/test/collate4.test b/test/collate4.test index 4db4c311b9..12bc16ef2e 100644 --- a/test/collate4.test +++ b/test/collate4.test @@ -321,6 +321,7 @@ do_test collate4-1.2.25 { # do_test collate4-2.1.0 { execsql { + PRAGMA automatic_index=OFF; CREATE TABLE collate4t1(a COLLATE NOCASE); CREATE TABLE collate4t2(b COLLATE TEXT); @@ -426,7 +427,7 @@ do_test collate4-2.2.0 { } {} do_test collate4-2.2.1 { count { - SELECT * FROM collate4t2 NATURAL JOIN collate4t1; + SELECT * FROM collate4t2 NOT INDEXED NATURAL JOIN collate4t1 NOT INDEXED; } } {0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 63} do_test collate4-2.2.1b {