Merge automatic index changes into the trunk.
FossilOrigin-Name: bf7b8d863665870a38f0b2335cc71de856a515b4
This commit is contained in:
@@ -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-----
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
a3540c6acf2bb6bdd44c101b63f17ca85e6b68ed
|
||||
bf7b8d863665870a38f0b2335cc71de856a515b4
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.</dd>
|
||||
**
|
||||
** <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt>
|
||||
** <dd>^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.</dd>
|
||||
**
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1
|
||||
#define SQLITE_STMTSTATUS_SORT 2
|
||||
#define SQLITE_STMTSTATUS_AUTOINDEX 3
|
||||
|
||||
/*
|
||||
** CAPI3REF: Custom Page Cache Object
|
||||
|
||||
+8
-1
@@ -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 */
|
||||
|
||||
+7
-3
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ){
|
||||
|
||||
+9
-1
@@ -1383,7 +1383,7 @@ case OP_Function: {
|
||||
for(i=0; i<n; i++, pArg++){
|
||||
apVal[i] = pArg;
|
||||
sqlite3VdbeMemStoreType(pArg);
|
||||
REGISTER_TRACE(pOp->p2, 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 =
|
||||
|
||||
+1
-1
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
+288
-14
@@ -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 /* x<EXPR or x<=EXPR constraint */
|
||||
#define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR 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; pTerm<pWCEnd; pTerm++){
|
||||
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
|
||||
WHERETRACE(("auto-index reduces cost from %.2f to %.2f\n",
|
||||
pCost->rCost, 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; pTerm<pWCEnd; pTerm++){
|
||||
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
|
||||
int iCol = pTerm->u.leftColumn;
|
||||
Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol;
|
||||
if( (idxCols & cMask)==0 ){
|
||||
nColumn++;
|
||||
idxCols |= cMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert( nColumn>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; i<mxBitCol; i++){
|
||||
if( extraCols & (1<<i) ) nColumn++;
|
||||
}
|
||||
if( pSrc->colUsed & (((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; pTerm<pWCEnd; pTerm++){
|
||||
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
|
||||
int iCol = pTerm->u.leftColumn;
|
||||
Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol;
|
||||
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;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert( n==pLevel->plan.nEq );
|
||||
|
||||
/* Add additional columns needed to make the automatic index into
|
||||
** a covering index */
|
||||
for(i=0; i<mxBitCol; i++){
|
||||
if( extraCols & (1<<i) ){
|
||||
pIdx->aiColumn[n] = i;
|
||||
pIdx->azColl[n] = "BINARY";
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){
|
||||
for(i=BMS-1; i<pTable->nCol; 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; i<nTabList; i++, pLevel++){
|
||||
Table *pTab; /* Table to open */
|
||||
int iDb; /* Index of database containing table/index */
|
||||
@@ -3869,7 +4123,9 @@ WhereInfo *sqlite3WhereBegin(
|
||||
if( pItem->zAlias ){
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
+2
-1
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user