mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Enhancements to the query planner to exploit transitive relationships in the
WHERE clause, and other minor changes to bring the sessions branch into alignment with the trunk. FossilOrigin-Name: 82d3d1ae824e1fbc7958657be79231590ec17ace
This commit is contained in:
@@ -683,6 +683,7 @@ TESTSRC = \
|
||||
$(TOP)\src\test_config.c \
|
||||
$(TOP)\src\test_demovfs.c \
|
||||
$(TOP)\src\test_devsym.c \
|
||||
$(TOP)\src\test_fs.c \
|
||||
$(TOP)\src\test_func.c \
|
||||
$(TOP)\src\test_fuzzer.c \
|
||||
$(TOP)\src\test_hexio.c \
|
||||
|
@@ -3049,7 +3049,8 @@ static int getIntFromStmt(sqlite3 *db, const char *zSql, int *piVal){
|
||||
static int getNodeSize(
|
||||
sqlite3 *db, /* Database handle */
|
||||
Rtree *pRtree, /* Rtree handle */
|
||||
int isCreate /* True for xCreate, false for xConnect */
|
||||
int isCreate, /* True for xCreate, false for xConnect */
|
||||
char **pzErr /* OUT: Error message, if any */
|
||||
){
|
||||
int rc;
|
||||
char *zSql;
|
||||
@@ -3062,6 +3063,8 @@ static int getNodeSize(
|
||||
if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){
|
||||
pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS;
|
||||
}
|
||||
}else{
|
||||
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
}
|
||||
}else{
|
||||
zSql = sqlite3_mprintf(
|
||||
@@ -3069,6 +3072,9 @@ static int getNodeSize(
|
||||
pRtree->zDb, pRtree->zName
|
||||
);
|
||||
rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize);
|
||||
if( rc!=SQLITE_OK ){
|
||||
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_free(zSql);
|
||||
@@ -3132,7 +3138,7 @@ static int rtreeInit(
|
||||
memcpy(pRtree->zName, argv[2], nName);
|
||||
|
||||
/* Figure out the node size to use. */
|
||||
rc = getNodeSize(db, pRtree, isCreate);
|
||||
rc = getNodeSize(db, pRtree, isCreate, pzErr);
|
||||
|
||||
/* Create/Connect to the underlying relational database schema. If
|
||||
** that is successful, call sqlite3_declare_vtab() to configure
|
||||
|
1
main.mk
1
main.mk
@@ -241,6 +241,7 @@ TESTSRC = \
|
||||
$(TOP)/src/test_config.c \
|
||||
$(TOP)/src/test_demovfs.c \
|
||||
$(TOP)/src/test_devsym.c \
|
||||
$(TOP)/src/test_fs.c \
|
||||
$(TOP)/src/test_func.c \
|
||||
$(TOP)/src/test_fuzzer.c \
|
||||
$(TOP)/src/test_hexio.c \
|
||||
|
40
manifest
40
manifest
@@ -1,9 +1,9 @@
|
||||
C Merge\slatest\strunk\schanges\sinto\sthe\ssessions\sbranch,\sespecially\sthe\nORDER\sBY\sbug\sfix\sof\s3.7.15.2.
|
||||
D 2013-01-09T14:49:37.244
|
||||
C Enhancements\sto\sthe\squery\splanner\sto\sexploit\stransitive\srelationships\sin\sthe\nWHERE\sclause,\sand\sother\sminor\schanges\sto\sbring\sthe\ssessions\sbranch\sinto\nalignment\swith\sthe\strunk.
|
||||
D 2013-01-25T02:10:06.424
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc f74e5635d39e882c915c8b988848a744b3fb3a6a
|
||||
F Makefile.msc 5de508f802789aae3e96d86261c5cf633d67ce00
|
||||
F Makefile.vxworks b18ad88e9a8c6a001f5cf4a389116a4f1a7ab45f
|
||||
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
|
||||
F VERSION 6d4f66eaebabc42ef8c2a4d2d0caf4ce7ee81137
|
||||
@@ -83,7 +83,7 @@ F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
|
||||
F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
|
||||
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c 47064ee2995a396bfb626337d2b43f12cc0af687
|
||||
F ext/rtree/rtree.c ebd07d0f06dc167f1424ff3940a5711a3a039982
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
F ext/rtree/rtree1.test e474a2b5eff231496dbd073fe67e5fbaf7f444c9
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
@@ -115,7 +115,7 @@ F ext/session/sqlite3session.h f374c9c4c96e08f67ac418871c29d423245c7673
|
||||
F ext/session/test_session.c ea4dc9b4a1895c8e6bddcbfe3838d7eb57df2d99
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F main.mk f2fd62730fb5c7771f565f1ea9da70f481eb9577
|
||||
F main.mk 787401c56823d6cf0d2fa01540d04bd438b11ee5
|
||||
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
|
||||
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
|
||||
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
|
||||
@@ -180,22 +180,22 @@ F src/parse.y 5d5e12772845805fdfeb889163516b84fbb9ae95
|
||||
F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
|
||||
F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c
|
||||
F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9
|
||||
F src/pragma.c 8907c559d3127729d3bcedb1fe5c59fc196d3a17
|
||||
F src/pragma.c b7ef175454106000fae966b3948b19e807bffc89
|
||||
F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c
|
||||
F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f
|
||||
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
|
||||
F src/resolve.c 0bca3bf694f14f96a13873d87f62d6a6f38f913f
|
||||
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
||||
F src/select.c 395e458a6dc611cbe1179f424753f0c344957607
|
||||
F src/shell.c 11c9611580bb2ffce3a232f31f7f8cc310df0843
|
||||
F src/shell.c af0309c2491a0d82ded1c2af3f64fcdb29d26ad5
|
||||
F src/sqlite.h.in 6a7a592aacc98674f39cb520cb7a7af87c2c2438
|
||||
F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
|
||||
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
|
||||
F src/sqliteInt.h f3f74ba8e76a9a850bfc38a529e7d7ad8227d0be
|
||||
F src/sqliteInt.h aa9282588b320654c0efa4379737a05e8f6616e4
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
F src/tclsqlite.c 0faa4b56ab352368b0bffa0874de5cf5c9d89c7e
|
||||
F src/tclsqlite.c bc021495cad081c13ccdcebd524857aedd831e11
|
||||
F src/test1.c f62769c989146149590662ab02de4a813813a9c5
|
||||
F src/test2.c 4178056dd1e7d70f954ad8a1e3edb71a2a784daf
|
||||
F src/test3.c 3c3c2407fa6ec7a19e24ae23f7cb439d0275a60d
|
||||
@@ -212,6 +212,7 @@ F src/test_btree.c 5b89601dcb42a33ba8b820a6b763cc9cb48bac16
|
||||
F src/test_config.c 1ffddfdfa5a73e7fb497e8c89c22754bbc949c81
|
||||
F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094
|
||||
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_fs.c 1c51e203b2c20235d8c3739f8c1fb13a7502915b
|
||||
F src/test_func.c 3a8dd37c08ab43b76d38eea2836e34a3897bf170
|
||||
F src/test_fuzzer.c 1d26aa965120420bc14807da29d4d4541bfa6148
|
||||
F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd
|
||||
@@ -229,7 +230,7 @@ F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba
|
||||
F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
|
||||
F src/test_quota.c 0e0e2e3bf6766b101ecccd8c042b66e44e9be8f5
|
||||
F src/test_quota.h 8761e463b25e75ebc078bd67d70e39b9c817a0cb
|
||||
F src/test_regexp.c 935a1bfb48c7a6857514aa9cedf6df048f8b9928
|
||||
F src/test_regexp.c 58e0349f155bc307dfa209df4b03add0a7749866
|
||||
F src/test_rtree.c aba603c949766c4193f1068b91c787f57274e0d9
|
||||
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
|
||||
F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
|
||||
@@ -254,7 +255,7 @@ F src/vdbe.c 4cf34269ba3a2f405eb4eb966c793baa07d863c0
|
||||
F src/vdbe.h 1223e2548e0970cf96f573ff6b99f804a36ad683
|
||||
F src/vdbeInt.h 2de43968dc47f1961d5bc76aa3cb68eacf433a7c
|
||||
F src/vdbeapi.c 58fdcd56109c05876f69c25d47a138ef370d3647
|
||||
F src/vdbeaux.c 0ce759dca1d1662f45f60a9336bf84b5cd15debf
|
||||
F src/vdbeaux.c 570714c7e2440da1b04689171124d84ef3015cd4
|
||||
F src/vdbeblob.c 11248c6362389569764682eb0f59ce910f3cc381
|
||||
F src/vdbemem.c cb55e84b8e2c15704968ee05f0fae25883299b74
|
||||
F src/vdbesort.c c61ca318681c0e7267da8be3abfca8469652a7e9
|
||||
@@ -263,7 +264,7 @@ F src/vtab.c b05e5f1f4902461ba9f5fc49bb7eb7c3a0741a83
|
||||
F src/wal.c f5c7b5027d0ed0e9bc9afeb4a3a8dfea762ec7d2
|
||||
F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6
|
||||
F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b
|
||||
F src/where.c 4c7fec9cfa3af06597ae039e5f0ec03cbee34c58
|
||||
F src/where.c 374a6c8190f863b3c69780b441d799e8a6b9e21b
|
||||
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
|
||||
@@ -296,7 +297,7 @@ F test/auth.test 304e82f31592820d3bde26ab6b75deaa123e1a6f
|
||||
F test/auth2.test a2a371aa6df15f8b0c8109b33d3d7f0f73e4c9aa
|
||||
F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5
|
||||
F test/autoinc.test bd30d372d00045252f6c2e41b5f41455e1975acf
|
||||
F test/autoindex1.test 058d0b331ae6840a61bbee910d8cbae27bfd5991
|
||||
F test/autoindex1.test f88146c4c889ea0afbb620e49d83b5fbf5ee4d06
|
||||
F test/autovacuum.test 9f22a7733f39c56ef6a5665d10145ac25d8cb574
|
||||
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
|
||||
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
|
||||
@@ -512,7 +513,7 @@ F test/fts3snippet.test 8e956051221a34c7daeb504f023cb54d5fa5a8b2
|
||||
F test/fts3sort.test 95be0b19d7e41c44b29014f13ea8bddd495fd659
|
||||
F test/fts4aa.test 95f448fb02c4a976968b08d1b4ce134e720946ae
|
||||
F test/fts4check.test 66fa274cab2b615f2fb338b257713aba8fad88a8
|
||||
F test/fts4content.test 17b2360f7d1a9a7e5aa8022783f5c5731b6dfd4f
|
||||
F test/fts4content.test 6efc53b4fd03cab167e6998d2b0b7d4b7d419ee6
|
||||
F test/fts4langid.test 24a6e41063b416bbdf371ff6b4476fa41c194aa7
|
||||
F test/fts4merge.test c424309743fdd203f8e56a1f1cd7872cd66cc0ee
|
||||
F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891
|
||||
@@ -680,7 +681,7 @@ F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
|
||||
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
|
||||
F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
|
||||
F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d
|
||||
F test/regexp1.test bbcb74e1bbdc20a7c0b9b2360deda14c4df1b46a
|
||||
F test/regexp1.test 5cbb6e7122ca51260d71079cf9245b63b8f64e1a
|
||||
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
||||
F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
|
||||
F test/releasetest.tcl 06d289d8255794073a58d2850742f627924545ce
|
||||
@@ -727,7 +728,7 @@ F test/shared8.test b27befbefbe7f4517f1d6b7ff8f64a41ec74165d
|
||||
F test/shared9.test 5f2a8f79b4d6c7d107a01ffa1ed05ae7e6333e21
|
||||
F test/shared_err.test 0079c05c97d88cfa03989b7c20a8b266983087aa
|
||||
F test/sharedlock.test ffa0a3c4ac192145b310f1254f8afca4d553eabf
|
||||
F test/shell1.test b7896eb84028f3bc8300caf1fc796a73728aad0b
|
||||
F test/shell1.test 392a265895e63cff2de1c78554e3b5b1cbbe9b8a
|
||||
F test/shell2.test 037d6ad16e873354195d30bb2dc4b5321788154a
|
||||
F test/shell3.test 9196c42772d575685e722c92b4b39053c6ebba59
|
||||
F test/shell4.test aa4eef8118b412d1a01477a53426ece169ea86a9
|
||||
@@ -908,6 +909,7 @@ F test/trace2.test c1dc104a8d11a347c870cfea6235e3fc6f6cb06d
|
||||
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
|
||||
F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
|
||||
F test/trans3.test 373ac5183cc56be69f48ae44090e7f672939f732
|
||||
F test/transitive1.test d04aa9023e425d6f2d4aa61dd81ee9e102f89062
|
||||
F test/trigger1.test 30f343f91586765874a28ad539c06f5a5f049931
|
||||
F test/trigger2.test 834187beafd1db383af0c659cfa49b0576832816
|
||||
F test/trigger3.test d2c60d8be271c355d61727411e753181e877230a
|
||||
@@ -1045,7 +1047,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
|
||||
P 7e068e39b3b31364271664e0afb1cd95a235c26f 5774f2175ce621dfc4b6b93f7ee13fd66f3ec2b9
|
||||
R 101fdb4f64767f317f7e78bca581a816
|
||||
P 34af6fac679aeb18ab8349f74e95f3cb6e722ea4 f1127e87b90c7ba049404ec68cb4e99009c22185
|
||||
R 0f44bd61453292415328773cf0329dea
|
||||
U drh
|
||||
Z 0052399aede3694d61b751e5cf31021b
|
||||
Z ad09dedaf38b5f33a2fcbd082e7bc0e7
|
||||
|
@@ -1 +1 @@
|
||||
34af6fac679aeb18ab8349f74e95f3cb6e722ea4
|
||||
82d3d1ae824e1fbc7958657be79231590ec17ace
|
@@ -1733,7 +1733,7 @@ void sqlite3Pragma(
|
||||
}else
|
||||
#endif
|
||||
#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
|
||||
if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){
|
||||
if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){
|
||||
sqlite3_activate_see(&zRight[4]);
|
||||
|
44
src/shell.c
44
src/shell.c
@@ -1629,24 +1629,50 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
if( nArg==0 ) return 0; /* no tokens, no error */
|
||||
n = strlen30(azArg[0]);
|
||||
c = azArg[0][0];
|
||||
if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 && nArg>1 && nArg<4){
|
||||
const char *zDestFile;
|
||||
const char *zDb;
|
||||
if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 ){
|
||||
const char *zDestFile = 0;
|
||||
const char *zDb = 0;
|
||||
const char *zKey = 0;
|
||||
sqlite3 *pDest;
|
||||
sqlite3_backup *pBackup;
|
||||
if( nArg==2 ){
|
||||
zDestFile = azArg[1];
|
||||
zDb = "main";
|
||||
}else{
|
||||
zDestFile = azArg[2];
|
||||
zDb = azArg[1];
|
||||
int j;
|
||||
for(j=1; j<nArg; j++){
|
||||
const char *z = azArg[j];
|
||||
if( z[0]=='-' ){
|
||||
while( z[0]=='-' ) z++;
|
||||
if( strcmp(z,"key")==0 && j<nArg-1 ){
|
||||
zKey = azArg[++j];
|
||||
}else
|
||||
{
|
||||
fprintf(stderr, "unknown option: %s\n", azArg[j]);
|
||||
return 1;
|
||||
}
|
||||
}else if( zDestFile==0 ){
|
||||
zDestFile = azArg[j];
|
||||
}else if( zDb==0 ){
|
||||
zDb = zDestFile;
|
||||
zDestFile = azArg[j];
|
||||
}else{
|
||||
fprintf(stderr, "too many arguments to .backup\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if( zDestFile==0 ){
|
||||
fprintf(stderr, "missing FILENAME argument on .backup\n");
|
||||
return 1;
|
||||
}
|
||||
if( zDb==0 ) zDb = "main";
|
||||
rc = sqlite3_open(zDestFile, &pDest);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fprintf(stderr, "Error: cannot open \"%s\"\n", zDestFile);
|
||||
sqlite3_close(pDest);
|
||||
return 1;
|
||||
}
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
sqlite3_key(pDest, zKey, (int)strlen(zKey));
|
||||
#else
|
||||
(void)zKey;
|
||||
#endif
|
||||
open_db(p);
|
||||
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
|
||||
if( pBackup==0 ){
|
||||
|
@@ -575,6 +575,11 @@ struct BusyHandler {
|
||||
*/
|
||||
#define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0])))
|
||||
|
||||
/*
|
||||
** Determine if the argument is a power of two
|
||||
*/
|
||||
#define IsPowerOfTwo(X) (((X)&((X)-1))==0)
|
||||
|
||||
/*
|
||||
** The following value as a destructor means to use sqlite3DbFree().
|
||||
** The sqlite3DbFree() routine requires two parameters instead of the
|
||||
@@ -981,6 +986,7 @@ struct sqlite3 {
|
||||
#define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */
|
||||
#define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */
|
||||
#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
|
||||
#define SQLITE_Transitive 0x0200 /* Transitive constraints */
|
||||
#define SQLITE_AllOpts 0xffff /* All optimizations */
|
||||
|
||||
/*
|
||||
|
@@ -3817,6 +3817,7 @@ static void init_all(Tcl_Interp *interp){
|
||||
extern int Sqlitetestschema_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestsse_Init(Tcl_Interp*);
|
||||
extern int Sqlitetesttclvar_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestfs_Init(Tcl_Interp*);
|
||||
extern int SqlitetestThread_Init(Tcl_Interp*);
|
||||
extern int SqlitetestOnefile_Init();
|
||||
extern int SqlitetestOsinst_Init(Tcl_Interp*);
|
||||
@@ -3864,6 +3865,7 @@ static void init_all(Tcl_Interp *interp){
|
||||
Sqlitetest_mutex_Init(interp);
|
||||
Sqlitetestschema_Init(interp);
|
||||
Sqlitetesttclvar_Init(interp);
|
||||
Sqlitetestfs_Init(interp);
|
||||
SqlitetestThread_Init(interp);
|
||||
SqlitetestOnefile_Init(interp);
|
||||
SqlitetestOsinst_Init(interp);
|
||||
|
333
src/test_fs.c
Normal file
333
src/test_fs.c
Normal file
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
** 2013 Jan 11
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing the virtual table interfaces. This code
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** The FS virtual table is created as follows:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE tbl USING fs(idx);
|
||||
**
|
||||
** where idx is the name of a table in the db with 2 columns. The virtual
|
||||
** table also has two columns - file path and file contents.
|
||||
**
|
||||
** The first column of table idx must be an IPK, and the second contains file
|
||||
** paths. For example:
|
||||
**
|
||||
** CREATE TABLE idx(id INTEGER PRIMARY KEY, path TEXT);
|
||||
** INSERT INTO idx VALUES(4, '/etc/passwd');
|
||||
**
|
||||
** Adding the row to the idx table automatically creates a row in the
|
||||
** virtual table with rowid=4, path=/etc/passwd and a text field that
|
||||
** contains data read from file /etc/passwd on disk.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if SQLITE_OS_UNIX
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#if SQLITE_OS_WIN
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
typedef struct fs_vtab fs_vtab;
|
||||
typedef struct fs_cursor fs_cursor;
|
||||
|
||||
/*
|
||||
** A fs virtual-table object
|
||||
*/
|
||||
struct fs_vtab {
|
||||
sqlite3_vtab base;
|
||||
sqlite3 *db;
|
||||
char *zDb; /* Name of db containing zTbl */
|
||||
char *zTbl; /* Name of docid->file map table */
|
||||
};
|
||||
|
||||
/* A fs cursor object */
|
||||
struct fs_cursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
sqlite3_stmt *pStmt;
|
||||
char *zBuf;
|
||||
int nBuf;
|
||||
int nAlloc;
|
||||
};
|
||||
|
||||
/*
|
||||
** This function is the implementation of both the xConnect and xCreate
|
||||
** methods of the fs virtual table.
|
||||
**
|
||||
** The argv[] array contains the following:
|
||||
**
|
||||
** argv[0] -> module name ("fs")
|
||||
** argv[1] -> database name
|
||||
** argv[2] -> table name
|
||||
** argv[...] -> other module argument fields.
|
||||
*/
|
||||
static int fsConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
fs_vtab *pVtab;
|
||||
int nByte;
|
||||
const char *zTbl;
|
||||
const char *zDb = argv[1];
|
||||
|
||||
if( argc!=4 ){
|
||||
*pzErr = sqlite3_mprintf("wrong number of arguments");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
zTbl = argv[3];
|
||||
|
||||
nByte = sizeof(fs_vtab) + strlen(zTbl) + 1 + strlen(zDb) + 1;
|
||||
pVtab = (fs_vtab *)sqlite3MallocZero( nByte );
|
||||
if( !pVtab ) return SQLITE_NOMEM;
|
||||
|
||||
pVtab->zTbl = (char *)&pVtab[1];
|
||||
pVtab->zDb = &pVtab->zTbl[strlen(zTbl)+1];
|
||||
pVtab->db = db;
|
||||
memcpy(pVtab->zTbl, zTbl, strlen(zTbl));
|
||||
memcpy(pVtab->zDb, zDb, strlen(zDb));
|
||||
*ppVtab = &pVtab->base;
|
||||
sqlite3_declare_vtab(db, "CREATE TABLE xyz(path TEXT, data TEXT)");
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/* Note that for this virtual table, the xCreate and xConnect
|
||||
** methods are identical. */
|
||||
|
||||
static int fsDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/* The xDisconnect and xDestroy methods are also the same */
|
||||
|
||||
/*
|
||||
** Open a new fs cursor.
|
||||
*/
|
||||
static int fsOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
fs_cursor *pCur;
|
||||
pCur = sqlite3MallocZero(sizeof(fs_cursor));
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a fs cursor.
|
||||
*/
|
||||
static int fsClose(sqlite3_vtab_cursor *cur){
|
||||
fs_cursor *pCur = (fs_cursor *)cur;
|
||||
sqlite3_finalize(pCur->pStmt);
|
||||
sqlite3_free(pCur->zBuf);
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int fsNext(sqlite3_vtab_cursor *cur){
|
||||
fs_cursor *pCur = (fs_cursor *)cur;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_step(pCur->pStmt);
|
||||
if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fsFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
int rc;
|
||||
fs_cursor *pCur = (fs_cursor *)pVtabCursor;
|
||||
fs_vtab *p = (fs_vtab *)(pVtabCursor->pVtab);
|
||||
|
||||
assert( (idxNum==0 && argc==0) || (idxNum==1 && argc==1) );
|
||||
if( idxNum==1 ){
|
||||
char *zStmt = sqlite3_mprintf(
|
||||
"SELECT * FROM %Q.%Q WHERE rowid=?", p->zDb, p->zTbl);
|
||||
if( !zStmt ) return SQLITE_NOMEM;
|
||||
rc = sqlite3_prepare_v2(p->db, zStmt, -1, &pCur->pStmt, 0);
|
||||
sqlite3_free(zStmt);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_value(pCur->pStmt, 1, argv[0]);
|
||||
}
|
||||
}else{
|
||||
char *zStmt = sqlite3_mprintf("SELECT * FROM %Q.%Q", p->zDb, p->zTbl);
|
||||
if( !zStmt ) return SQLITE_NOMEM;
|
||||
rc = sqlite3_prepare_v2(p->db, zStmt, -1, &pCur->pStmt, 0);
|
||||
sqlite3_free(zStmt);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fsNext(pVtabCursor);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fsColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
fs_cursor *pCur = (fs_cursor*)cur;
|
||||
|
||||
assert( i==0 || i==1 );
|
||||
if( i==0 ){
|
||||
sqlite3_result_value(ctx, sqlite3_column_value(pCur->pStmt, 0));
|
||||
}else{
|
||||
const char *zFile = (const char *)sqlite3_column_text(pCur->pStmt, 1);
|
||||
struct stat sbuf;
|
||||
int fd;
|
||||
|
||||
fd = open(zFile, O_RDONLY);
|
||||
if( fd<0 ) return SQLITE_IOERR;
|
||||
fstat(fd, &sbuf);
|
||||
|
||||
if( sbuf.st_size>=pCur->nAlloc ){
|
||||
int nNew = sbuf.st_size*2;
|
||||
char *zNew;
|
||||
if( nNew<1024 ) nNew = 1024;
|
||||
|
||||
zNew = sqlite3Realloc(pCur->zBuf, nNew);
|
||||
if( zNew==0 ){
|
||||
close(fd);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pCur->zBuf = zNew;
|
||||
pCur->nAlloc = nNew;
|
||||
}
|
||||
|
||||
read(fd, pCur->zBuf, sbuf.st_size);
|
||||
close(fd);
|
||||
pCur->nBuf = sbuf.st_size;
|
||||
pCur->zBuf[pCur->nBuf] = '\0';
|
||||
|
||||
sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int fsRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
fs_cursor *pCur = (fs_cursor*)cur;
|
||||
*pRowid = sqlite3_column_int64(pCur->pStmt, 0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int fsEof(sqlite3_vtab_cursor *cur){
|
||||
fs_cursor *pCur = (fs_cursor*)cur;
|
||||
return (sqlite3_data_count(pCur->pStmt)==0);
|
||||
}
|
||||
|
||||
static int fsBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
int ii;
|
||||
|
||||
for(ii=0; ii<pIdxInfo->nConstraint; ii++){
|
||||
struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
|
||||
if( pCons->iColumn<0 && pCons->usable
|
||||
&& pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
struct sqlite3_index_constraint_usage *pUsage;
|
||||
pUsage = &pIdxInfo->aConstraintUsage[ii];
|
||||
pUsage->omit = 0;
|
||||
pUsage->argvIndex = 1;
|
||||
pIdxInfo->idxNum = 1;
|
||||
pIdxInfo->estimatedCost = 1.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that provides read-only access to a
|
||||
** Tcl global variable namespace.
|
||||
*/
|
||||
static sqlite3_module fsModule = {
|
||||
0, /* iVersion */
|
||||
fsConnect,
|
||||
fsConnect,
|
||||
fsBestIndex,
|
||||
fsDisconnect,
|
||||
fsDisconnect,
|
||||
fsOpen, /* xOpen - open a cursor */
|
||||
fsClose, /* xClose - close a cursor */
|
||||
fsFilter, /* xFilter - configure scan constraints */
|
||||
fsNext, /* xNext - advance a cursor */
|
||||
fsEof, /* xEof - check for end of scan */
|
||||
fsColumn, /* xColumn - read data */
|
||||
fsRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
};
|
||||
|
||||
/*
|
||||
** Decode a pointer to an sqlite3 object.
|
||||
*/
|
||||
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
|
||||
/*
|
||||
** Register the echo virtual table module.
|
||||
*/
|
||||
static int register_fs_module(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3_create_module(db, "fs", &fsModule, (void *)interp);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetestfs_Init(Tcl_Interp *interp){
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
void *clientData;
|
||||
} aObjCmd[] = {
|
||||
{ "register_fs_module", register_fs_module, 0 },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
|
||||
aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
|
||||
}
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
@@ -26,7 +26,7 @@
|
||||
** \c Character c where c is one of \{}()[]|*+?.
|
||||
** \c C-language escapes for c in afnrtv. ex: \t or \n
|
||||
** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX
|
||||
** \xXXX Where XXX is any number of hex digits, unicode value XXX
|
||||
** \xXX Where XX is exactly 2 hex digits, unicode value XX
|
||||
** [abc] Any single character from the set abc
|
||||
** [^abc] Any single character not in the set abc
|
||||
** [a-z] Any single character in the range a-z
|
||||
@@ -378,7 +378,7 @@ static int re_hex(int c, int *pV){
|
||||
}
|
||||
|
||||
/* A backslash character has been seen, read the next character and
|
||||
** return its intepretation.
|
||||
** return its interpretation.
|
||||
*/
|
||||
static unsigned re_esc_char(ReCompiled *p){
|
||||
static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]";
|
||||
@@ -387,9 +387,8 @@ static unsigned re_esc_char(ReCompiled *p){
|
||||
char c;
|
||||
if( p->sIn.i>=p->sIn.mx ) return 0;
|
||||
c = p->sIn.z[p->sIn.i];
|
||||
if( c=='u' && p->sIn.i+5<p->sIn.mx ){
|
||||
if( c=='u' && p->sIn.i+4<p->sIn.mx ){
|
||||
const unsigned char *zIn = p->sIn.z + p->sIn.i;
|
||||
v = 0;
|
||||
if( re_hex(zIn[1],&v)
|
||||
&& re_hex(zIn[2],&v)
|
||||
&& re_hex(zIn[3],&v)
|
||||
@@ -399,11 +398,12 @@ static unsigned re_esc_char(ReCompiled *p){
|
||||
return v;
|
||||
}
|
||||
}
|
||||
if( c=='x' ){
|
||||
v = 0;
|
||||
for(i=1; p->sIn.i<p->sIn.mx && re_hex(p->sIn.z[p->sIn.i+i], &v); i++){}
|
||||
if( i>1 ){
|
||||
p->sIn.i += i;
|
||||
if( c=='x' && p->sIn.i+2<p->sIn.mx ){
|
||||
const unsigned char *zIn = p->sIn.z + p->sIn.i;
|
||||
if( re_hex(zIn[1],&v)
|
||||
&& re_hex(zIn[2],&v)
|
||||
){
|
||||
p->sIn.i += 3;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
@@ -2479,7 +2479,7 @@ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
|
||||
sqlite3DbFree(db, p->zSql);
|
||||
sqlite3DbFree(db, p->pFree);
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
sqlite3_free(p->zExplain);
|
||||
sqlite3DbFree(db, p->zExplain);
|
||||
sqlite3DbFree(db, p->pExplain);
|
||||
#endif
|
||||
}
|
||||
|
262
src/where.c
262
src/where.c
@@ -98,8 +98,8 @@ struct WhereTerm {
|
||||
int leftCursor; /* Cursor number of X in "X <op> <expr>" */
|
||||
union {
|
||||
int leftColumn; /* Column number of X in "X <op> <expr>" */
|
||||
WhereOrInfo *pOrInfo; /* Extra information if eOperator==WO_OR */
|
||||
WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */
|
||||
WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */
|
||||
WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */
|
||||
} u;
|
||||
u16 eOperator; /* A WO_xx value describing <op> */
|
||||
u8 wtFlags; /* TERM_xxx bit flags. See below */
|
||||
@@ -227,6 +227,7 @@ struct WhereCost {
|
||||
#define WO_ISNULL 0x080
|
||||
#define WO_OR 0x100 /* Two or more OR-connected terms */
|
||||
#define WO_AND 0x200 /* Two or more AND-connected terms */
|
||||
#define WO_EQUIV 0x400 /* Of the form A==B, both columns */
|
||||
#define WO_NOOP 0x800 /* This term does not restrict search space */
|
||||
|
||||
#define WO_ALL 0xfff /* Mask of all possible WO_* values */
|
||||
@@ -629,6 +630,24 @@ static u16 operatorMask(int op){
|
||||
** where X is a reference to the iColumn of table iCur and <op> is one of
|
||||
** the WO_xx operator codes specified by the op parameter.
|
||||
** Return a pointer to the term. Return 0 if not found.
|
||||
**
|
||||
** The term returned might by Y=<expr> if there is another constraint in
|
||||
** the WHERE clause that specifies that X=Y. Any such constraints will be
|
||||
** identified by the WO_EQUIV bit in the pTerm->eOperator field. The
|
||||
** aEquiv[] array holds X and all its equivalents, with each SQL variable
|
||||
** taking up two slots in aEquiv[]. The first slot is for the cursor number
|
||||
** and the second is for the column number. There are 22 slots in aEquiv[]
|
||||
** so that means we can look for X plus up to 10 other equivalent values.
|
||||
** Hence a search for X will return <expr> if X=A1 and A1=A2 and A2=A3
|
||||
** and ... and A9=A10 and A10=<expr>.
|
||||
**
|
||||
** If there are multiple terms in the WHERE clause of the form "X <op> <expr>"
|
||||
** then try for the one with no dependencies on <expr> - in other words where
|
||||
** <expr> is a constant expression of some kind. Only return entries of
|
||||
** the form "X <op> Y" where Y is a column in another table if no terms of
|
||||
** the form "X <op> <const-expr>" exist. Other than this priority, if there
|
||||
** are two or more terms that match, then the choice of which term to return
|
||||
** is arbitrary.
|
||||
*/
|
||||
static WhereTerm *findTerm(
|
||||
WhereClause *pWC, /* The WHERE clause to be searched */
|
||||
@@ -638,45 +657,81 @@ static WhereTerm *findTerm(
|
||||
u32 op, /* Mask of WO_xx values describing operator */
|
||||
Index *pIdx /* Must be compatible with this index, if not NULL */
|
||||
){
|
||||
WhereTerm *pTerm;
|
||||
int k;
|
||||
WhereTerm *pTerm; /* Term being examined as possible result */
|
||||
WhereTerm *pResult = 0; /* The answer to return */
|
||||
WhereClause *pWCOrig = pWC; /* Original pWC value */
|
||||
int j, k; /* Loop counters */
|
||||
Expr *pX; /* Pointer to an expression */
|
||||
Parse *pParse; /* Parsing context */
|
||||
int iOrigCol = iColumn; /* Original value of iColumn */
|
||||
int nEquiv = 2; /* Number of entires in aEquiv[] */
|
||||
int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */
|
||||
int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */
|
||||
|
||||
assert( iCur>=0 );
|
||||
op &= WO_ALL;
|
||||
for(; pWC; pWC=pWC->pOuter){
|
||||
for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
|
||||
if( pTerm->leftCursor==iCur
|
||||
&& (pTerm->prereqRight & notReady)==0
|
||||
&& pTerm->u.leftColumn==iColumn
|
||||
&& (pTerm->eOperator & op)!=0
|
||||
){
|
||||
if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){
|
||||
Expr *pX = pTerm->pExpr;
|
||||
CollSeq *pColl;
|
||||
char idxaff;
|
||||
int j;
|
||||
Parse *pParse = pWC->pParse;
|
||||
|
||||
idxaff = pIdx->pTable->aCol[iColumn].affinity;
|
||||
if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue;
|
||||
|
||||
/* Figure out the collation sequence required from an index for
|
||||
** it to be useful for optimising expression pX. Store this
|
||||
** value in variable pColl.
|
||||
*/
|
||||
assert(pX->pLeft);
|
||||
pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
|
||||
if( pColl==0 ) pColl = pParse->db->pDfltColl;
|
||||
|
||||
for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
|
||||
if( NEVER(j>=pIdx->nColumn) ) return 0;
|
||||
aEquiv[0] = iCur;
|
||||
aEquiv[1] = iColumn;
|
||||
for(;;){
|
||||
for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){
|
||||
for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
|
||||
if( pTerm->leftCursor==iCur
|
||||
&& pTerm->u.leftColumn==iColumn
|
||||
){
|
||||
if( (pTerm->prereqRight & notReady)==0
|
||||
&& (pTerm->eOperator & op & WO_ALL)!=0
|
||||
){
|
||||
if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){
|
||||
CollSeq *pColl;
|
||||
char idxaff;
|
||||
|
||||
pX = pTerm->pExpr;
|
||||
pParse = pWC->pParse;
|
||||
idxaff = pIdx->pTable->aCol[iOrigCol].affinity;
|
||||
if( !sqlite3IndexAffinityOk(pX, idxaff) ){
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Figure out the collation sequence required from an index for
|
||||
** it to be useful for optimising expression pX. Store this
|
||||
** value in variable pColl.
|
||||
*/
|
||||
assert(pX->pLeft);
|
||||
pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight);
|
||||
if( pColl==0 ) pColl = pParse->db->pDfltColl;
|
||||
|
||||
for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){
|
||||
if( NEVER(j>=pIdx->nColumn) ) return 0;
|
||||
}
|
||||
if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
pResult = pTerm;
|
||||
if( pTerm->prereqRight==0 ) goto findTerm_success;
|
||||
}
|
||||
if( (pTerm->eOperator & WO_EQUIV)!=0
|
||||
&& nEquiv<ArraySize(aEquiv)
|
||||
){
|
||||
pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight);
|
||||
assert( pX->op==TK_COLUMN );
|
||||
for(j=0; j<nEquiv; j+=2){
|
||||
if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break;
|
||||
}
|
||||
if( j==nEquiv ){
|
||||
aEquiv[j] = pX->iTable;
|
||||
aEquiv[j+1] = pX->iColumn;
|
||||
nEquiv += 2;
|
||||
}
|
||||
}
|
||||
if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
|
||||
}
|
||||
return pTerm;
|
||||
}
|
||||
}
|
||||
if( iEquiv>=nEquiv ) break;
|
||||
iCur = aEquiv[iEquiv++];
|
||||
iColumn = aEquiv[iEquiv++];
|
||||
}
|
||||
return 0;
|
||||
findTerm_success:
|
||||
return pResult;
|
||||
}
|
||||
|
||||
/* Forward reference */
|
||||
@@ -954,7 +1009,6 @@ static void exprAnalyzeOrTerm(
|
||||
for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){
|
||||
if( (pOrTerm->eOperator & WO_SINGLE)==0 ){
|
||||
WhereAndInfo *pAndInfo;
|
||||
assert( pOrTerm->eOperator==0 );
|
||||
assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 );
|
||||
chngToIN = 0;
|
||||
pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo));
|
||||
@@ -993,7 +1047,7 @@ static void exprAnalyzeOrTerm(
|
||||
b |= getMask(pMaskSet, pOther->leftCursor);
|
||||
}
|
||||
indexable &= b;
|
||||
if( pOrTerm->eOperator!=WO_EQ ){
|
||||
if( (pOrTerm->eOperator & WO_EQ)==0 ){
|
||||
chngToIN = 0;
|
||||
}else{
|
||||
chngToIN &= b;
|
||||
@@ -1044,7 +1098,7 @@ static void exprAnalyzeOrTerm(
|
||||
for(j=0; j<2 && !okToChngToIN; j++){
|
||||
pOrTerm = pOrWc->a;
|
||||
for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){
|
||||
assert( pOrTerm->eOperator==WO_EQ );
|
||||
assert( pOrTerm->eOperator & WO_EQ );
|
||||
pOrTerm->wtFlags &= ~TERM_OR_OK;
|
||||
if( pOrTerm->leftCursor==iCursor ){
|
||||
/* This is the 2-bit case and we are on the second iteration and
|
||||
@@ -1070,7 +1124,7 @@ static void exprAnalyzeOrTerm(
|
||||
/* No candidate table+column was found. This can only occur
|
||||
** on the second iteration */
|
||||
assert( j==1 );
|
||||
assert( (chngToIN&(chngToIN-1))==0 );
|
||||
assert( IsPowerOfTwo(chngToIN) );
|
||||
assert( chngToIN==getMask(pMaskSet, iCursor) );
|
||||
break;
|
||||
}
|
||||
@@ -1080,7 +1134,7 @@ static void exprAnalyzeOrTerm(
|
||||
** table and column is common to every term in the OR clause */
|
||||
okToChngToIN = 1;
|
||||
for(; i>=0 && okToChngToIN; i--, pOrTerm++){
|
||||
assert( pOrTerm->eOperator==WO_EQ );
|
||||
assert( pOrTerm->eOperator & WO_EQ );
|
||||
if( pOrTerm->leftCursor!=iCursor ){
|
||||
pOrTerm->wtFlags &= ~TERM_OR_OK;
|
||||
}else if( pOrTerm->u.leftColumn!=iColumn ){
|
||||
@@ -1116,7 +1170,7 @@ static void exprAnalyzeOrTerm(
|
||||
|
||||
for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){
|
||||
if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue;
|
||||
assert( pOrTerm->eOperator==WO_EQ );
|
||||
assert( pOrTerm->eOperator & WO_EQ );
|
||||
assert( pOrTerm->leftCursor==iCursor );
|
||||
assert( pOrTerm->u.leftColumn==iColumn );
|
||||
pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0);
|
||||
@@ -1146,7 +1200,6 @@ static void exprAnalyzeOrTerm(
|
||||
}
|
||||
#endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */
|
||||
|
||||
|
||||
/*
|
||||
** The input to this routine is an WhereTerm structure with only the
|
||||
** "pExpr" field filled in. The job of this routine is to analyze the
|
||||
@@ -1215,17 +1268,19 @@ static void exprAnalyze(
|
||||
pTerm->leftCursor = -1;
|
||||
pTerm->iParent = -1;
|
||||
pTerm->eOperator = 0;
|
||||
if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){
|
||||
if( allowedOp(op) ){
|
||||
Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
|
||||
Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
|
||||
u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
|
||||
if( pLeft->op==TK_COLUMN ){
|
||||
pTerm->leftCursor = pLeft->iTable;
|
||||
pTerm->u.leftColumn = pLeft->iColumn;
|
||||
pTerm->eOperator = operatorMask(op);
|
||||
pTerm->eOperator = operatorMask(op) & opMask;
|
||||
}
|
||||
if( pRight && pRight->op==TK_COLUMN ){
|
||||
WhereTerm *pNew;
|
||||
Expr *pDup;
|
||||
u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */
|
||||
if( pTerm->leftCursor>=0 ){
|
||||
int idxNew;
|
||||
pDup = sqlite3ExprDup(db, pExpr, 0);
|
||||
@@ -1240,6 +1295,13 @@ static void exprAnalyze(
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pTerm->nChild = 1;
|
||||
pTerm->wtFlags |= TERM_COPIED;
|
||||
if( pExpr->op==TK_EQ
|
||||
&& !ExprHasProperty(pExpr, EP_FromJoin)
|
||||
&& OptimizationEnabled(db, SQLITE_Transitive)
|
||||
){
|
||||
pTerm->eOperator |= WO_EQUIV;
|
||||
eExtraOp = WO_EQUIV;
|
||||
}
|
||||
}else{
|
||||
pDup = pExpr;
|
||||
pNew = pTerm;
|
||||
@@ -1251,7 +1313,7 @@ static void exprAnalyze(
|
||||
testcase( (prereqLeft | extraRight) != prereqLeft );
|
||||
pNew->prereqRight = prereqLeft | extraRight;
|
||||
pNew->prereqAll = prereqAll;
|
||||
pNew->eOperator = operatorMask(pDup->op);
|
||||
pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1710,7 +1772,7 @@ static void bestOrClauseIndex(WhereBestIdx *p){
|
||||
|
||||
/* Search the WHERE clause terms for a usable WO_OR term. */
|
||||
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
|
||||
if( pTerm->eOperator==WO_OR
|
||||
if( (pTerm->eOperator & WO_OR)!=0
|
||||
&& ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0
|
||||
&& (pTerm->u.pOrInfo->indexable & maskSrc)!=0
|
||||
){
|
||||
@@ -1731,7 +1793,7 @@ static void bestOrClauseIndex(WhereBestIdx *p){
|
||||
WHERETRACE(("... Multi-index OR testing for term %d of %d....\n",
|
||||
(pOrTerm - pOrWC->a), (pTerm - pWC->a)
|
||||
));
|
||||
if( pOrTerm->eOperator==WO_AND ){
|
||||
if( (pOrTerm->eOperator& WO_AND)!=0 ){
|
||||
sBOI.pWC = &pOrTerm->u.pAndInfo->wc;
|
||||
bestIndex(&sBOI);
|
||||
}else if( pOrTerm->leftCursor==iCur ){
|
||||
@@ -1792,7 +1854,7 @@ static int termCanDriveIndex(
|
||||
){
|
||||
char aff;
|
||||
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
|
||||
if( pTerm->eOperator!=WO_EQ ) return 0;
|
||||
if( (pTerm->eOperator & WO_EQ)==0 ) return 0;
|
||||
if( (pTerm->prereqRight & notReady)!=0 ) return 0;
|
||||
aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
|
||||
if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0;
|
||||
@@ -2054,9 +2116,9 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
|
||||
** to this virtual table */
|
||||
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
|
||||
if( pTerm->leftCursor != pSrc->iCursor ) continue;
|
||||
assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
|
||||
testcase( pTerm->eOperator==WO_IN );
|
||||
testcase( pTerm->eOperator==WO_ISNULL );
|
||||
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
|
||||
testcase( pTerm->eOperator & WO_IN );
|
||||
testcase( pTerm->eOperator & WO_ISNULL );
|
||||
if( pTerm->eOperator & (WO_ISNULL) ) continue;
|
||||
if( pTerm->wtFlags & TERM_VNULL ) continue;
|
||||
nTerm++;
|
||||
@@ -2107,14 +2169,14 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
|
||||
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
|
||||
u8 op;
|
||||
if( pTerm->leftCursor != pSrc->iCursor ) continue;
|
||||
assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
|
||||
testcase( pTerm->eOperator==WO_IN );
|
||||
testcase( pTerm->eOperator==WO_ISNULL );
|
||||
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
|
||||
testcase( pTerm->eOperator & WO_IN );
|
||||
testcase( pTerm->eOperator & WO_ISNULL );
|
||||
if( pTerm->eOperator & (WO_ISNULL) ) continue;
|
||||
if( pTerm->wtFlags & TERM_VNULL ) continue;
|
||||
pIdxCons[j].iColumn = pTerm->u.leftColumn;
|
||||
pIdxCons[j].iTermOffset = i;
|
||||
op = (u8)pTerm->eOperator;
|
||||
op = (u8)pTerm->eOperator & WO_ALL;
|
||||
if( op==WO_IN ) op = WO_EQ;
|
||||
pIdxCons[j].op = op;
|
||||
/* The direct assignment in the previous line is possible only because
|
||||
@@ -2284,7 +2346,7 @@ static void bestVirtualIndex(WhereBestIdx *p){
|
||||
j = pIdxCons->iTermOffset;
|
||||
pTerm = &pWC->a[j];
|
||||
if( (pTerm->prereqRight&p->notReady)==0
|
||||
&& (bAllowIN || pTerm->eOperator!=WO_IN)
|
||||
&& (bAllowIN || (pTerm->eOperator & WO_IN)==0)
|
||||
){
|
||||
pIdxCons->usable = 1;
|
||||
}else{
|
||||
@@ -2316,7 +2378,7 @@ static void bestVirtualIndex(WhereBestIdx *p){
|
||||
j = pIdxCons->iTermOffset;
|
||||
pTerm = &pWC->a[j];
|
||||
p->cost.used |= pTerm->prereqRight;
|
||||
if( pTerm->eOperator==WO_IN && pUsage[i].omit==0 ){
|
||||
if( (pTerm->eOperator & WO_IN)!=0 && pUsage[i].omit==0 ){
|
||||
/* Do not attempt to use an IN constraint if the virtual table
|
||||
** says that the equivalent EQ constraint cannot be safely omitted.
|
||||
** If we do attempt to use such a constraint, some rows might be
|
||||
@@ -2622,24 +2684,24 @@ static int whereRangeScanEst(
|
||||
if( pLower ){
|
||||
Expr *pExpr = pLower->pExpr->pRight;
|
||||
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
|
||||
assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE );
|
||||
assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 );
|
||||
if( rc==SQLITE_OK
|
||||
&& whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK
|
||||
){
|
||||
iLower = a[0];
|
||||
if( pLower->eOperator==WO_GT ) iLower += a[1];
|
||||
if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1];
|
||||
}
|
||||
sqlite3ValueFree(pRangeVal);
|
||||
}
|
||||
if( rc==SQLITE_OK && pUpper ){
|
||||
Expr *pExpr = pUpper->pExpr->pRight;
|
||||
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
|
||||
assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE );
|
||||
assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
|
||||
if( rc==SQLITE_OK
|
||||
&& whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK
|
||||
){
|
||||
iUpper = a[0];
|
||||
if( pUpper->eOperator==WO_LE ) iUpper += a[1];
|
||||
if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1];
|
||||
}
|
||||
sqlite3ValueFree(pRangeVal);
|
||||
}
|
||||
@@ -2947,12 +3009,12 @@ static int isSortingIndex(
|
||||
WO_EQ|WO_ISNULL|WO_IN, pIdx);
|
||||
if( pConstraint==0 ){
|
||||
isEq = 0;
|
||||
}else if( pConstraint->eOperator==WO_IN ){
|
||||
}else if( (pConstraint->eOperator & WO_IN)!=0 ){
|
||||
/* Constraints of the form: "X IN ..." cannot be used with an ORDER BY
|
||||
** because we do not know in what order the values on the RHS of the IN
|
||||
** operator will occur. */
|
||||
break;
|
||||
}else if( pConstraint->eOperator==WO_ISNULL ){
|
||||
}else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){
|
||||
uniqueNotNull = 0;
|
||||
isEq = 1; /* "X IS NULL" means X has only a single value */
|
||||
}else if( pConstraint->prereqRight==0 ){
|
||||
@@ -3365,12 +3427,13 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
||||
&& pFirstTerm!=0 && aiRowEst[1]>1 ){
|
||||
assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
|
||||
if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
|
||||
testcase( pFirstTerm->eOperator==WO_EQ );
|
||||
testcase( pFirstTerm->eOperator==WO_ISNULL );
|
||||
testcase( pFirstTerm->eOperator & WO_EQ );
|
||||
testcase( pFirstTerm->eOperator & WO_EQUIV );
|
||||
testcase( pFirstTerm->eOperator & WO_ISNULL );
|
||||
whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight,
|
||||
&pc.plan.nRow);
|
||||
}else if( bInEst==0 ){
|
||||
assert( pFirstTerm->eOperator==WO_IN );
|
||||
assert( pFirstTerm->eOperator & WO_IN );
|
||||
whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList,
|
||||
&pc.plan.nRow);
|
||||
}
|
||||
@@ -3517,7 +3580,7 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
||||
** selective in practice, on average. */
|
||||
pc.plan.nRow /= 3;
|
||||
}
|
||||
}else if( pTerm->eOperator!=WO_NOOP ){
|
||||
}else if( (pTerm->eOperator & WO_NOOP)==0 ){
|
||||
/* Any other expression lowers the output row count by half */
|
||||
pc.plan.nRow /= 2;
|
||||
}
|
||||
@@ -3569,8 +3632,9 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
||||
|| p->cost.plan.u.pIdx==pSrc->pIndex
|
||||
);
|
||||
|
||||
WHERETRACE((" best index is: %s\n",
|
||||
p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk"));
|
||||
WHERETRACE((" best index is %s cost=%.1f\n",
|
||||
p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk",
|
||||
p->cost.rCost));
|
||||
|
||||
bestOrClauseIndex(p);
|
||||
bestAutomaticIndex(p);
|
||||
@@ -4152,7 +4216,6 @@ static Bitmask codeOneLoopStart(
|
||||
pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0);
|
||||
assert( pTerm!=0 );
|
||||
assert( pTerm->pExpr!=0 );
|
||||
assert( pTerm->leftCursor==iCur );
|
||||
assert( omitTable==0 );
|
||||
testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
|
||||
iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg);
|
||||
@@ -4543,7 +4606,7 @@ static Bitmask codeOneLoopStart(
|
||||
|
||||
pTerm = pLevel->plan.u.pTerm;
|
||||
assert( pTerm!=0 );
|
||||
assert( pTerm->eOperator==WO_OR );
|
||||
assert( pTerm->eOperator & WO_OR );
|
||||
assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
|
||||
pOrWc = &pTerm->u.pOrInfo->wc;
|
||||
pLevel->op = OP_Return;
|
||||
@@ -4616,7 +4679,7 @@ static Bitmask codeOneLoopStart(
|
||||
|
||||
for(ii=0; ii<pOrWc->nTerm; ii++){
|
||||
WhereTerm *pOrTerm = &pOrWc->a[ii];
|
||||
if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){
|
||||
if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){
|
||||
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
|
||||
Expr *pOrExpr = pOrTerm->pExpr;
|
||||
if( pAndExpr ){
|
||||
@@ -5071,6 +5134,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
int bestJ = -1; /* The value of j */
|
||||
Bitmask m; /* Bitmask value for j or bestJ */
|
||||
int isOptimal; /* Iterator for optimal/non-optimal search */
|
||||
int ckOptimal; /* Do the optimal scan check */
|
||||
int nUnconstrained; /* Number tables without INDEXED BY */
|
||||
Bitmask notIndexed; /* Mask of tables that cannot use an index */
|
||||
|
||||
@@ -5105,10 +5169,8 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** strategies were found by the first iteration. This second iteration
|
||||
** is used to search for the lowest cost scan overall.
|
||||
**
|
||||
** Previous versions of SQLite performed only the second iteration -
|
||||
** the next outermost loop was always that with the lowest overall
|
||||
** cost. However, this meant that SQLite could select the wrong plan
|
||||
** for scripts such as the following:
|
||||
** Without the optimal scan step (the first iteration) a suboptimal
|
||||
** plan might be chosen for queries like this:
|
||||
**
|
||||
** CREATE TABLE t1(a, b);
|
||||
** CREATE TABLE t2(c, d);
|
||||
@@ -5123,17 +5185,41 @@ WhereInfo *sqlite3WhereBegin(
|
||||
*/
|
||||
nUnconstrained = 0;
|
||||
notIndexed = 0;
|
||||
for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){
|
||||
|
||||
/* The optimal scan check only occurs if there are two or more tables
|
||||
** available to be reordered */
|
||||
if( iFrom==nTabList-1 ){
|
||||
ckOptimal = 0; /* Common case of just one table in the FROM clause */
|
||||
}else{
|
||||
ckOptimal = -1;
|
||||
for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
|
||||
int doNotReorder; /* True if this table should not be reordered */
|
||||
|
||||
doNotReorder = (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0;
|
||||
if( j!=iFrom && doNotReorder ) break;
|
||||
m = getMask(pMaskSet, sWBI.pSrc->iCursor);
|
||||
if( (m & sWBI.notValid)==0 ){
|
||||
if( j==iFrom ) iFrom++;
|
||||
continue;
|
||||
}
|
||||
if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break;
|
||||
if( ++ckOptimal ) break;
|
||||
if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break;
|
||||
}
|
||||
}
|
||||
assert( ckOptimal==0 || ckOptimal==1 );
|
||||
|
||||
for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){
|
||||
for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
|
||||
if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){
|
||||
/* This break and one like it in the ckOptimal computation loop
|
||||
** above prevent table reordering across LEFT and CROSS JOINs.
|
||||
** The LEFT JOIN case is necessary for correctness. The prohibition
|
||||
** against reordering across a CROSS JOIN is an SQLite feature that
|
||||
** allows the developer to control table reordering */
|
||||
break;
|
||||
}
|
||||
m = getMask(pMaskSet, sWBI.pSrc->iCursor);
|
||||
if( (m & sWBI.notValid)==0 ){
|
||||
assert( j>iFrom );
|
||||
continue;
|
||||
}
|
||||
sWBI.notReady = (isOptimal ? m : sWBI.notValid);
|
||||
if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
|
||||
|
||||
@@ -5162,8 +5248,8 @@ WhereInfo *sqlite3WhereBegin(
|
||||
}
|
||||
if( isOptimal ){
|
||||
pWInfo->a[j].rOptCost = sWBI.cost.rCost;
|
||||
}else if( iFrom<nTabList-1 ){
|
||||
/* If two or more tables have nearly the same outer loop cost,
|
||||
}else if( ckOptimal ){
|
||||
/* If two or more tables have nearly the same outer loop cost, but
|
||||
** very different inner loop (optimal) cost, we want to choose
|
||||
** for the outer loop that table which benefits the least from
|
||||
** being in the inner loop. The following code scales the
|
||||
@@ -5208,11 +5294,19 @@ WhereInfo *sqlite3WhereBegin(
|
||||
bestPlan = sWBI.cost;
|
||||
bestJ = j;
|
||||
}
|
||||
if( doNotReorder ) break;
|
||||
|
||||
/* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that
|
||||
** table y (and not table z) is always the next inner loop inside
|
||||
** of table x. */
|
||||
if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break;
|
||||
}
|
||||
}
|
||||
assert( bestJ>=0 );
|
||||
assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
|
||||
assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 );
|
||||
testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 );
|
||||
testcase( bestJ>iFrom && bestJ<nTabList-1
|
||||
&& (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 );
|
||||
WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n"
|
||||
" cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n",
|
||||
bestJ, pTabList->a[bestJ].pTab->zName,
|
||||
|
@@ -257,5 +257,129 @@ do_execsql_test autoindex1-700 {
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
|
||||
# The following checks a performance issue reported on the sqlite-dev
|
||||
# mailing list on 2013-01-10
|
||||
#
|
||||
do_execsql_test autoindex1-800 {
|
||||
CREATE TABLE accounts(
|
||||
_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
account_name TEXT,
|
||||
account_type TEXT,
|
||||
data_set TEXT
|
||||
);
|
||||
CREATE TABLE data(
|
||||
_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
package_id INTEGER REFERENCES package(_id),
|
||||
mimetype_id INTEGER REFERENCES mimetype(_id) NOT NULL,
|
||||
raw_contact_id INTEGER REFERENCES raw_contacts(_id) NOT NULL,
|
||||
is_read_only INTEGER NOT NULL DEFAULT 0,
|
||||
is_primary INTEGER NOT NULL DEFAULT 0,
|
||||
is_super_primary INTEGER NOT NULL DEFAULT 0,
|
||||
data_version INTEGER NOT NULL DEFAULT 0,
|
||||
data1 TEXT,
|
||||
data2 TEXT,
|
||||
data3 TEXT,
|
||||
data4 TEXT,
|
||||
data5 TEXT,
|
||||
data6 TEXT,
|
||||
data7 TEXT,
|
||||
data8 TEXT,
|
||||
data9 TEXT,
|
||||
data10 TEXT,
|
||||
data11 TEXT,
|
||||
data12 TEXT,
|
||||
data13 TEXT,
|
||||
data14 TEXT,
|
||||
data15 TEXT,
|
||||
data_sync1 TEXT,
|
||||
data_sync2 TEXT,
|
||||
data_sync3 TEXT,
|
||||
data_sync4 TEXT
|
||||
);
|
||||
CREATE TABLE mimetypes(
|
||||
_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
mimetype TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE raw_contacts(
|
||||
_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
account_id INTEGER REFERENCES accounts(_id),
|
||||
sourceid TEXT,
|
||||
raw_contact_is_read_only INTEGER NOT NULL DEFAULT 0,
|
||||
version INTEGER NOT NULL DEFAULT 1,
|
||||
dirty INTEGER NOT NULL DEFAULT 0,
|
||||
deleted INTEGER NOT NULL DEFAULT 0,
|
||||
contact_id INTEGER REFERENCES contacts(_id),
|
||||
aggregation_mode INTEGER NOT NULL DEFAULT 0,
|
||||
aggregation_needed INTEGER NOT NULL DEFAULT 1,
|
||||
custom_ringtone TEXT,
|
||||
send_to_voicemail INTEGER NOT NULL DEFAULT 0,
|
||||
times_contacted INTEGER NOT NULL DEFAULT 0,
|
||||
last_time_contacted INTEGER,
|
||||
starred INTEGER NOT NULL DEFAULT 0,
|
||||
display_name TEXT,
|
||||
display_name_alt TEXT,
|
||||
display_name_source INTEGER NOT NULL DEFAULT 0,
|
||||
phonetic_name TEXT,
|
||||
phonetic_name_style TEXT,
|
||||
sort_key TEXT,
|
||||
sort_key_alt TEXT,
|
||||
name_verified INTEGER NOT NULL DEFAULT 0,
|
||||
sync1 TEXT,
|
||||
sync2 TEXT,
|
||||
sync3 TEXT,
|
||||
sync4 TEXT,
|
||||
sync_uid TEXT,
|
||||
sync_version INTEGER NOT NULL DEFAULT 1,
|
||||
has_calendar_event INTEGER NOT NULL DEFAULT 0,
|
||||
modified_time INTEGER,
|
||||
is_restricted INTEGER DEFAULT 0,
|
||||
yp_source TEXT,
|
||||
method_selected INTEGER DEFAULT 0,
|
||||
custom_vibration_type INTEGER DEFAULT 0,
|
||||
custom_ringtone_path TEXT,
|
||||
message_notification TEXT,
|
||||
message_notification_path TEXT
|
||||
);
|
||||
CREATE INDEX data_mimetype_data1_index ON data (mimetype_id,data1);
|
||||
CREATE INDEX data_raw_contact_id ON data (raw_contact_id);
|
||||
CREATE UNIQUE INDEX mime_type ON mimetypes (mimetype);
|
||||
CREATE INDEX raw_contact_sort_key1_index ON raw_contacts (sort_key);
|
||||
CREATE INDEX raw_contact_sort_key2_index ON raw_contacts (sort_key_alt);
|
||||
CREATE INDEX raw_contacts_contact_id_index ON raw_contacts (contact_id);
|
||||
CREATE INDEX raw_contacts_source_id_account_id_index
|
||||
ON raw_contacts (sourceid, account_id);
|
||||
ANALYZE sqlite_master;
|
||||
INSERT INTO sqlite_stat1
|
||||
VALUES('raw_contacts','raw_contact_sort_key2_index','1600 4');
|
||||
INSERT INTO sqlite_stat1
|
||||
VALUES('raw_contacts','raw_contact_sort_key1_index','1600 4');
|
||||
INSERT INTO sqlite_stat1
|
||||
VALUES('raw_contacts','raw_contacts_source_id_account_id_index',
|
||||
'1600 1600 1600');
|
||||
INSERT INTO sqlite_stat1
|
||||
VALUES('raw_contacts','raw_contacts_contact_id_index','1600 1');
|
||||
INSERT INTO sqlite_stat1 VALUES('mimetypes','mime_type','12 1');
|
||||
INSERT INTO sqlite_stat1
|
||||
VALUES('data','data_mimetype_data1_index','9819 2455 3');
|
||||
INSERT INTO sqlite_stat1 VALUES('data','data_raw_contact_id','9819 7');
|
||||
INSERT INTO sqlite_stat1 VALUES('accounts',NULL,'1');
|
||||
DROP TABLE IF EXISTS sqlite_stat3;
|
||||
ANALYZE sqlite_master;
|
||||
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM
|
||||
data JOIN mimetypes ON (data.mimetype_id=mimetypes._id)
|
||||
JOIN raw_contacts ON (data.raw_contact_id=raw_contacts._id)
|
||||
JOIN accounts ON (raw_contacts.account_id=accounts._id)
|
||||
WHERE mimetype_id=10 AND data14 IS NOT NULL;
|
||||
} {/SEARCH TABLE data .*SEARCH TABLE raw_contacts/}
|
||||
do_execsql_test autoindex1-801 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM
|
||||
data JOIN mimetypes ON (data.mimetype_id=mimetypes._id)
|
||||
JOIN raw_contacts ON (data.raw_contact_id=raw_contacts._id)
|
||||
JOIN accounts ON (raw_contacts.account_id=accounts._id)
|
||||
WHERE mimetypes._id=10 AND data14 IS NOT NULL;
|
||||
} {/SEARCH TABLE data .*SEARCH TABLE raw_contacts/}
|
||||
|
||||
finish_test
|
||||
|
@@ -46,6 +46,8 @@ ifcapable !fts3 {
|
||||
# 8.* - Test that if the content=xxx and prefix options are used together,
|
||||
# the 'rebuild' command still works.
|
||||
#
|
||||
# 9.* - Test using content=xxx where xxx is a virtual table.
|
||||
#
|
||||
|
||||
do_execsql_test 1.1.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
@@ -522,4 +524,103 @@ do_execsql_test 8.4 { SELECT rowid FROM ft10 WHERE a MATCH 'ab*'; } {1 2 3}
|
||||
do_execsql_test 8.5 { SELECT rowid FROM ft10 WHERE b MATCH 'abav*'; } {3}
|
||||
do_execsql_test 8.6 { SELECT rowid FROM ft10 WHERE ft10 MATCH 'abas*'; } {1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 9.*
|
||||
#
|
||||
reset_db
|
||||
register_echo_module [sqlite3_connection_pointer db]
|
||||
|
||||
do_execsql_test 9.1 {
|
||||
CREATE TABLE tbl1(a, b);
|
||||
INSERT INTO tbl1 VALUES('a b', 'c d');
|
||||
INSERT INTO tbl1 VALUES('e f', 'a b');
|
||||
CREATE VIRTUAL TABLE e1 USING echo(tbl1);
|
||||
CREATE VIRTUAL TABLE ft1 USING fts4(content=e1);
|
||||
INSERT INTO ft1(ft1) VALUES('rebuild');
|
||||
}
|
||||
|
||||
do_execsql_test 9.2 {
|
||||
SELECT rowid, * FROM ft1 WHERE ft1 MATCH 'e'
|
||||
} {2 {e f} {a b}}
|
||||
|
||||
do_execsql_test 9.3 {
|
||||
SELECT rowid, * FROM ft1 WHERE ft1 MATCH 'a'
|
||||
} {1 {a b} {c d} 2 {e f} {a b}}
|
||||
|
||||
do_execsql_test 9.4 {
|
||||
DELETE FROM ft1 WHERE docid=1;
|
||||
}
|
||||
|
||||
do_execsql_test 9.5 {
|
||||
SELECT rowid, * FROM ft1 WHERE ft1 MATCH 'a'
|
||||
} {2 {e f} {a b}}
|
||||
|
||||
do_execsql_test 9.6 {
|
||||
INSERT INTO ft1(ft1) VALUES('rebuild');
|
||||
SELECT rowid, * FROM ft1 WHERE ft1 MATCH 'a'
|
||||
} {1 {a b} {c d} 2 {e f} {a b}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 10.*
|
||||
#
|
||||
reset_db
|
||||
register_fs_module [sqlite3_connection_pointer db]
|
||||
|
||||
proc write_file {path text} {
|
||||
set fd [open $path w]
|
||||
puts -nonewline $fd $text
|
||||
close $fd
|
||||
}
|
||||
|
||||
write_file t1.txt {a b c d e f g h i j k l m n o p q r s t u v w x y z}
|
||||
write_file t2.txt {a b c d e f g h i j k l m a b c d e f g h i j k l m}
|
||||
write_file t3.txt {n o p q r s t u v w x y z n o p q r s t u v w x y z}
|
||||
|
||||
do_execsql_test 10.1 {
|
||||
CREATE TABLE idx(id INTEGER PRIMARY KEY, path TEXT);
|
||||
INSERT INTO idx VALUES (1, 't1.txt');
|
||||
INSERT INTO idx VALUES (2, 't2.txt');
|
||||
INSERT INTO idx VALUES (3, 't3.txt');
|
||||
|
||||
CREATE VIRTUAL TABLE vt USING fs(idx);
|
||||
SELECT * FROM vt;
|
||||
} {
|
||||
1 {a b c d e f g h i j k l m n o p q r s t u v w x y z}
|
||||
2 {a b c d e f g h i j k l m a b c d e f g h i j k l m}
|
||||
3 {n o p q r s t u v w x y z n o p q r s t u v w x y z}
|
||||
}
|
||||
|
||||
do_execsql_test 10.2 {
|
||||
SELECT * FROM vt WHERE rowid = 2;
|
||||
} {
|
||||
2 {a b c d e f g h i j k l m a b c d e f g h i j k l m}
|
||||
}
|
||||
|
||||
do_execsql_test 10.3 {
|
||||
CREATE VIRTUAL TABLE ft USING fts4(content=vt);
|
||||
INSERT INTO ft(ft) VALUES('rebuild');
|
||||
}
|
||||
|
||||
do_execsql_test 10.4 {
|
||||
SELECT snippet(ft, '[', ']', '...', -1, 5) FROM ft WHERE ft MATCH 'e'
|
||||
} {
|
||||
{...c d [e] f g...} {...c d [e] f g...}
|
||||
}
|
||||
|
||||
do_execsql_test 10.5 {
|
||||
SELECT snippet(ft, '[', ']', '...', -1, 5) FROM ft WHERE ft MATCH 't'
|
||||
} {
|
||||
{...r s [t] u v...} {...r s [t] u v...}
|
||||
}
|
||||
|
||||
do_execsql_test 10.6 { DELETE FROM ft WHERE docid=2 }
|
||||
|
||||
do_execsql_test 10.7 {
|
||||
SELECT snippet(ft, '[', ']', '...', -1, 5) FROM ft WHERE ft MATCH 'e'
|
||||
} {
|
||||
{...c d [e] f g...}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@@ -197,12 +197,12 @@ do_execsql_test regexp1-2.15 {
|
||||
do_execsql_test regexp1-2.20 {
|
||||
SELECT 'abc$¢€xyz' REGEXP '^abc\u0024\u00a2\u20acxyz$',
|
||||
'abc$¢€xyz' REGEXP '^abc\u0024\u00A2\u20ACxyz$',
|
||||
'abc$¢€xyz' REGEXP '^abc\x24\xa2\x20acxyz$'
|
||||
'abc$¢€xyz' REGEXP '^abc\x24\xa2\u20acxyz$'
|
||||
} {1 1 1}
|
||||
do_execsql_test regexp1-2.21 {
|
||||
SELECT 'abc$¢€xyz' REGEXP '^abc[\u0024][\u00a2][\u20ac]xyz$',
|
||||
'abc$¢€xyz' REGEXP '^abc[\u0024\u00A2\u20AC]{3}xyz$',
|
||||
'abc$¢€xyz' REGEXP '^abc[\x24][\xa2\x20ac]+xyz$'
|
||||
'abc$¢€xyz' REGEXP '^abc[\x24][\xa2\u20ac]+xyz$'
|
||||
} {1 1 1}
|
||||
do_execsql_test regexp1-2.22 {
|
||||
SELECT 'abc$¢€xyz' REGEXP '^abc[^\u0025-X][^ -\u007f][^\u20ab]xyz$'
|
||||
|
@@ -253,7 +253,7 @@ do_test shell1-2.4.2 {
|
||||
# .backup ?DB? FILE Backup DB (default "main") to FILE
|
||||
do_test shell1-3.1.1 {
|
||||
catchcmd "test.db" ".backup"
|
||||
} {1 {Error: unknown command or invalid arguments: "backup". Enter ".help" for help}}
|
||||
} {1 {missing FILENAME argument on .backup}}
|
||||
do_test shell1-3.1.2 {
|
||||
catchcmd "test.db" ".backup FOO"
|
||||
} {0 {}}
|
||||
@@ -263,7 +263,7 @@ do_test shell1-3.1.3 {
|
||||
do_test shell1-3.1.4 {
|
||||
# too many arguments
|
||||
catchcmd "test.db" ".backup FOO BAR BAD"
|
||||
} {1 {Error: unknown command or invalid arguments: "backup". Enter ".help" for help}}
|
||||
} {1 {too many arguments to .backup}}
|
||||
|
||||
# .bail ON|OFF Stop after hitting an error. Default OFF
|
||||
do_test shell1-3.2.1 {
|
||||
|
50
test/transitive1.test
Normal file
50
test/transitive1.test
Normal file
@@ -0,0 +1,50 @@
|
||||
# 2013 April 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing of transitive WHERE clause constraints
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_execsql_test transitive1-100 {
|
||||
CREATE TABLE t1(a TEXT, b TEXT, c TEXT COLLATE NOCASE);
|
||||
INSERT INTO t1 VALUES('abc','abc','Abc');
|
||||
INSERT INTO t1 VALUES('def','def','def');
|
||||
INSERT INTO t1 VALUES('ghi','ghi','GHI');
|
||||
CREATE INDEX t1a1 ON t1(a);
|
||||
CREATE INDEX t1a2 ON t1(a COLLATE nocase);
|
||||
|
||||
SELECT * FROM t1 WHERE a=b AND c=b AND c='DEF';
|
||||
} {def def def}
|
||||
do_execsql_test transitive1-110 {
|
||||
SELECT * FROM t1 WHERE a=b AND c=b AND c>='DEF' ORDER BY +a;
|
||||
} {def def def ghi ghi GHI}
|
||||
do_execsql_test transitive1-120 {
|
||||
SELECT * FROM t1 WHERE a=b AND c=b AND c<='DEF' ORDER BY +a;
|
||||
} {abc abc Abc def def def}
|
||||
|
||||
do_execsql_test transitive1-200 {
|
||||
CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT);
|
||||
INSERT INTO t2 VALUES(100,100,100);
|
||||
INSERT INTO t2 VALUES(20,20,20);
|
||||
INSERT INTO t2 VALUES(3,3,3);
|
||||
|
||||
SELECT * FROM t2 WHERE a=b AND c=b AND c=20;
|
||||
} {20 20 20}
|
||||
do_execsql_test transitive1-210 {
|
||||
SELECT * FROM t2 WHERE a=b AND c=b AND c>=20 ORDER BY +a;
|
||||
} {3 3 3 20 20 20}
|
||||
do_execsql_test transitive1-220 {
|
||||
SELECT * FROM t2 WHERE a=b AND c=b AND c<=20 ORDER BY +a;
|
||||
} {20 20 20 100 100 100}
|
||||
|
||||
finish_test
|
Reference in New Issue
Block a user