diff --git a/manifest b/manifest index e5ba609ecb..6217c562fc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Pull\sall\sthe\slatest\strunk\senhancements\sinto\sthe\ssessions\sbranch. -D 2012-10-30T21:03:48.529 +C Merge\sthe\slatest\schanges\sfrom\strunk:\schiefly\sthe\souter/inner\sloop\squery\noptimizer\sscoring\senhancement\sand\sthe\sINSTR()\sfunction. +D 2012-11-10T01:27:59.154 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 82c41c0ed4cc94dd3cc7d498575b84c57c2c2384 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -142,10 +142,10 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c 72a70dcfda75d3a1f81041ce4573e7afddcd8e4e F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4 F src/delete.c 9bc9463952bdc9fc43111b1f9c83a0af9b8e2239 -F src/expr.c 152ba793a8747061c0c332857d0e4d4fd52d4397 +F src/expr.c 3b25a95f3d309403940ba4a3212f197b8b6251d5 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c c82a04e7a92bb728f9ab972b76590403283be2af -F src/func.c cbb90dc84b22eea25caf39528d342279e61b8898 +F src/func.c 1755cafdb8f2a291681f1ea55e0215518ebd6e52 F src/global.c fb44b11e02e06c995e6ed6642509edd23599d584 F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4 F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970 @@ -172,9 +172,9 @@ F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30 F src/os.c e1acdc09ff3ac2412945cca9766e2dcf4675f31c F src/os.h 027491c77d2404c0a678bb3fb06286f331eb9b57 F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04 -F src/os_unix.c 603d020fe6c58047794cc72c05c7c8f4a82a0579 +F src/os_unix.c f0753566e1125d8b2eef6dd080b48ed91a83d424 F src/os_win.c 43ec1285357e5d5d919cb0492eac775c58ad7d12 -F src/pager.c ee59fef31673d5124413c5a801cfd9ef3e6766d3 +F src/pager.c ed53fe75a269c1d67645fe079ea0f3f0ce6492d5 F src/pager.h 1109a06578ec5574dc2c74cf8d9f69daf36fe3e0 F src/parse.y f29df90bd3adc64b33114ab1de9fb7768fcf2099 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 @@ -188,12 +188,12 @@ F src/resolve.c 7b986a715ac281643309c29257bb58cfae7aa810 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 F src/select.c 3a8baf4719f9723b4e0b43f2baa60692d0d921f8 F src/shell.c 24cd0aa74aff73ea08594629faead564c4c2a286 -F src/sqlite.h.in d460ae07ecdd1c820272d9c217547c7b572cb4b7 +F src/sqlite.h.in 6c9386f4be831a293bc7bac803f735e12a666ac1 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 -F src/sqliteInt.h 205fa7fd18a7e6e0ed2ff9cfbccf36fb51304ee4 +F src/sqliteInt.h b071b5d387b9d256b1dcd2d0240fe50a74dbb898 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d -F src/status.c 53463144deb5dfac0a66b3be4dd7844b8f8a4c98 +F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/tclsqlite.c 289be7b639406314813219ee7bc043d21f36ab12 F src/test1.c 936afc02766403e5debca49a1817a780e116df7e @@ -227,7 +227,7 @@ F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e F src/test_onefile.c 0396f220561f3b4eedc450cef26d40c593c69a25 F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00 -F src/test_quota.c e5fdb7d28e5afae1b619922804e544db0041ec81 +F src/test_quota.c 0e0e2e3bf6766b101ecccd8c042b66e44e9be8f5 F src/test_quota.h 8761e463b25e75ebc078bd67d70e39b9c817a0cb F src/test_rtree.c aba603c949766c4193f1068b91c787f57274e0d9 F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0 @@ -261,10 +261,10 @@ F src/vtab.c b05e5f1f4902461ba9f5fc49bb7eb7c3a0741a83 F src/wal.c f5c7b5027d0ed0e9bc9afeb4a3a8dfea762ec7d2 F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6 F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b -F src/where.c 6a753aa008de6494e64dd265a27afbb0ad80ccf5 +F src/where.c 832e33fefbe5ba751c1f5a06e63de98be95e56f2 F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 -F test/aggnested.test 0be144b453e0622a085fae8665c32f5676708e00 +F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 52fc8dee494092031a556911d404ca30a749a30b F test/alter.test 57d96ec9b320bd07af77567034488dcb6642c748 @@ -556,6 +556,7 @@ F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435 F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30 F test/insert4.test 87f6798f31d60c4e177622fcc3663367e6ecbd90 F test/insert5.test 394f96728d1258f406fe5f5aeb0aaf29487c39a6 +F test/instr.test a34e1d46a9eefb098a7167ef0e730a4a3d82fba0 F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4 F test/interrupt.test 42e7cf98646fd9cb4a3b131a93ed3c50b9e149f1 F test/intpkey.test 7af30f6ae852d8d1c2b70e4bf1551946742e92d8 @@ -647,10 +648,10 @@ F test/notify3.test a86259abbfb923aa27d30f0fc038c88e5251488a F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347 F test/null.test a8b09b8ed87852742343b33441a9240022108993 F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394 -F test/orderby1.test ef4f7c40df81b9a4303a718433d34052f07db47d +F test/orderby1.test f33968647da5c546528fe4d2bf86c6a6a2e5a7ae F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04 F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3 -F test/pager1.test 07116f72a61960b882952e7472cc2846d161d6e2 +F test/pager1.test f4c57e14583da2183fe31555c67fb32feda96092 F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1 F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f F test/pagerfault.test 452f2cc23e3bfcfa935f4442aec1da4fe1dc0442 @@ -986,6 +987,8 @@ F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 F test/whereC.test 13ff5ec0dba407c0e0c075980c75b3275a6774e5 F test/whereD.test 3f3ee93825c94804f1fc91eef2de0d365981759a +F test/whereE.test 7bd34945797efef15819368479bacc34215e4e1d +F test/whereF.test a0e296643cabe5278379bc1a0aa158cf3c54a1c9 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31 F test/win32lock.test b2a539e85ae6b2d78475e016a9636b4451dc7fb9 F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688 @@ -1034,7 +1037,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 76767d651fe733614b1719aef9ad5ed4568234e4 9dca18f5fea84afbecb314ee1cdfb98430656af3 -R 44966c032a1121fa311efd7f4c01d9a9 +P fce667f2d93a4ba65ccf1e748469576a3cd7ffcc 5a3b07f0f5dfae7eea870303f52f37d6a17f1da2 +R 2cae8c229d6424df7351cab19781b1e6 U drh -Z f9e45ab7bfa5d5b4a8fa6d0808ba86fd +Z 883d57cdfacddb67b9acee024d2c5aac diff --git a/manifest.uuid b/manifest.uuid index 5b8cae9332..2c0d0dd237 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fce667f2d93a4ba65ccf1e748469576a3cd7ffcc \ No newline at end of file +2993ca20207f8dac02f58d01e31d68c84328356a \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 3eee3c6d69..ae828489a6 100644 --- a/src/expr.c +++ b/src/expr.c @@ -4030,8 +4030,10 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ ExprSetIrreducible(pExpr); pExpr->iAgg = (i16)i; pExpr->pAggInfo = pAggInfo; + return WRC_Prune; + }else{ + return WRC_Continue; } - return WRC_Prune; } } return WRC_Continue; @@ -4043,9 +4045,10 @@ static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){ } /* -** Analyze the given expression looking for aggregate functions and -** for variables that need to be added to the pParse->aAgg[] array. -** Make additional entries to the pParse->aAgg[] array as necessary. +** Analyze the pExpr expression looking for aggregate functions and +** for variables that need to be added to AggInfo object that pNC->pAggInfo +** points to. Additional entries are made on the AggInfo object as +** necessary. ** ** This routine should only be called after the expression has been ** analyzed by sqlite3ResolveExprNames(). diff --git a/src/func.c b/src/func.c index cc74a4ddda..c7af7d679c 100644 --- a/src/func.c +++ b/src/func.c @@ -168,6 +168,55 @@ static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ } } +/* +** Implementation of the instr() function. +** +** instr(haystack,needle) finds the first occurrence of needle +** in haystack and returns the number of previous characters plus 1, +** or 0 if needle does not occur within haystack. +** +** If both haystack and needle are BLOBs, then the result is one more than +** the number of bytes in haystack prior to the first occurrence of needle, +** or 0 if needle never occurs in haystack. +*/ +static void instrFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const unsigned char *zHaystack; + const unsigned char *zNeedle; + int nHaystack; + int nNeedle; + int typeHaystack, typeNeedle; + int N = 1; + int isText; + + typeHaystack = sqlite3_value_type(argv[0]); + typeNeedle = sqlite3_value_type(argv[1]); + if( typeHaystack==SQLITE_NULL || typeNeedle==SQLITE_NULL ) return; + nHaystack = sqlite3_value_bytes(argv[0]); + nNeedle = sqlite3_value_bytes(argv[1]); + if( typeHaystack==SQLITE_BLOB && typeNeedle==SQLITE_BLOB ){ + zHaystack = sqlite3_value_blob(argv[0]); + zNeedle = sqlite3_value_blob(argv[1]); + isText = 0; + }else{ + zHaystack = sqlite3_value_text(argv[0]); + zNeedle = sqlite3_value_text(argv[1]); + isText = 1; + } + while( nNeedle<=nHaystack && memcmp(zHaystack, zNeedle, nNeedle)!=0 ){ + N++; + do{ + nHaystack--; + zHaystack++; + }while( isText && (zHaystack[0]&0xc0)==0x80 ); + } + if( nNeedle>nHaystack ) N = 0; + sqlite3_result_int(context, N); +} + /* ** Implementation of the substr() function. ** @@ -1536,6 +1585,7 @@ void sqlite3RegisterGlobalFunctions(void){ AGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize ), FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH), + FUNCTION(instr, 2, 0, 0, instrFunc ), FUNCTION(substr, 2, 0, 0, substrFunc ), FUNCTION(substr, 3, 0, 0, substrFunc ), FUNCTION(abs, 1, 0, 0, absFunc ), diff --git a/src/os_unix.c b/src/os_unix.c index 9b6c9401a1..ca62139430 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -5374,8 +5374,13 @@ static int unixDelete( int rc = SQLITE_OK; UNUSED_PARAMETER(NotUsed); SimulateIOError(return SQLITE_IOERR_DELETE); - if( osUnlink(zPath)==(-1) && errno!=ENOENT ){ - return unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath); + if( osUnlink(zPath)==(-1) ){ + if( errno==ENOENT ){ + rc = SQLITE_IOERR_DELETE_NOENT; + }else{ + rc = unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath); + } + return rc; } #ifndef SQLITE_DISABLE_DIRSYNC if( (dirSync & 1)!=0 ){ diff --git a/src/pager.c b/src/pager.c index a96dc45b82..7c039e7a76 100644 --- a/src/pager.c +++ b/src/pager.c @@ -3159,6 +3159,7 @@ static int pagerOpenWalIfPresent(Pager *pPager){ if( rc ) return rc; if( nPage==0 ){ rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0); + if( rc==SQLITE_IOERR_DELETE_NOENT ) rc = SQLITE_OK; isWal = 0; }else{ rc = sqlite3OsAccess( diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 1d5406f0fe..0522973f89 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -469,6 +469,7 @@ int sqlite3_exec( #define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8)) #define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8)) #define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8)) +#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) diff --git a/src/sqliteInt.h b/src/sqliteInt.h index d7857e5668..658b67ba4f 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1975,6 +1975,7 @@ struct WhereLevel { } in; /* Used when plan.wsFlags&WHERE_IN_ABLE */ Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */ } u; + double rOptCost; /* "Optimal" cost for this level */ /* The following field is really not part of the current level. But ** we need a place to cache virtual table index information for each diff --git a/src/status.c b/src/status.c index 9b0df8001c..28349e6d3d 100644 --- a/src/status.c +++ b/src/status.c @@ -209,6 +209,7 @@ int sqlite3_db_status( db->pnBytesFreed = &nByte; for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){ sqlite3VdbeClearObject(db, pVdbe); + sqlite3DbFree(db, pVdbe); } db->pnBytesFreed = 0; diff --git a/src/test_quota.c b/src/test_quota.c index e1ec12d371..166a512f18 100644 --- a/src/test_quota.c +++ b/src/test_quota.c @@ -1179,7 +1179,13 @@ int sqlite3_quota_ftruncate(quota_FILE *p, sqlite3_int64 szNew){ rc = ftruncate(fileno(p->f), szNew); #endif #if SQLITE_OS_WIN - rc = _chsize_s(_fileno(p->f), szNew); +# if defined(__MINGW32__) && defined(SQLITE_TEST) + /* _chsize_s() is missing from MingW (as of 2012-11-06). Use + ** _chsize() as a work-around for testing purposes. */ + rc = _chsize(_fileno(p->f), (long)szNew); +# else + rc = _chsize_s(_fileno(p->f), szNew); +# endif #endif if( pFile && rc==0 ){ quotaGroup *pGroup = pFile->pGroup; diff --git a/src/where.c b/src/where.c index 38f6f1436f..20bd90979e 100644 --- a/src/where.c +++ b/src/where.c @@ -5102,6 +5102,19 @@ WhereInfo *sqlite3WhereBegin( if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ notIndexed |= m; } + if( isOptimal ){ + pWInfo->a[j].rOptCost = sWBI.cost.rCost; + }else if( iFroma[j].rOptCost)); + sWBI.cost.rCost /= pWInfo->a[j].rOptCost; + } /* Conditions under which this table becomes the best so far: ** @@ -5109,8 +5122,8 @@ WhereInfo *sqlite3WhereBegin( ** yet run. (In other words, it must not depend on tables ** in inner loops.) ** - ** (2) A full-table-scan plan cannot supercede indexed plan unless - ** the full-table-scan is an "optimal" plan as defined above. + ** (2) (This rule was removed on 2012-11-09. The scaling of the + ** cost using the optimal scan cost made this rule obsolete.) ** ** (3) All tables have an INDEXED BY clause or this table lacks an ** INDEXED BY clause or this table uses the specific @@ -5125,9 +5138,6 @@ WhereInfo *sqlite3WhereBegin( ** is defined by the compareCost() function above. */ if( (sWBI.cost.used&sWBI.notValid)==0 /* (1) */ - && (bestJ<0 || (notIndexed&m)!=0 /* (2) */ - || (bestPlan.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 - || (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0) && (nUnconstrained==0 || sWBI.pSrc->pIndex==0 /* (3) */ || NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) && (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */ diff --git a/test/aggnested.test b/test/aggnested.test index 22f0fb6b9a..6e2fd6554b 100644 --- a/test/aggnested.test +++ b/test/aggnested.test @@ -68,4 +68,164 @@ do_test aggnested-2.0 { } {A,B,B 3 33 333 3333} db2 close +##################### Test cases for ticket [bfbf38e5e9956ac69f] ############ +# +# This first test case is the original problem report: +do_test aggnested-3.0 { + db eval { + CREATE TABLE AAA ( + aaa_id INTEGER PRIMARY KEY AUTOINCREMENT + ); + CREATE TABLE RRR ( + rrr_id INTEGER PRIMARY KEY AUTOINCREMENT, + rrr_date INTEGER NOT NULL, + rrr_aaa INTEGER + ); + CREATE TABLE TTT ( + ttt_id INTEGER PRIMARY KEY AUTOINCREMENT, + target_aaa INTEGER NOT NULL, + source_aaa INTEGER NOT NULL + ); + insert into AAA (aaa_id) values (2); + insert into TTT (ttt_id, target_aaa, source_aaa) + values (4469, 2, 2); + insert into TTT (ttt_id, target_aaa, source_aaa) + values (4476, 2, 1); + insert into RRR (rrr_id, rrr_date, rrr_aaa) + values (0, 0, NULL); + insert into RRR (rrr_id, rrr_date, rrr_aaa) + values (2, 4312, 2); + SELECT i.aaa_id, + (SELECT sum(CASE WHEN (t.source_aaa == i.aaa_id) THEN 1 ELSE 0 END) + FROM TTT t + ) AS segfault + FROM + (SELECT curr.rrr_aaa as aaa_id + FROM RRR curr + -- you also can comment out the next line + -- it causes segfault to happen after one row is outputted + INNER JOIN AAA a ON (curr.rrr_aaa = aaa_id) + LEFT JOIN RRR r ON (r.rrr_id <> 0 AND r.rrr_date < curr.rrr_date) + GROUP BY curr.rrr_id + HAVING r.rrr_date IS NULL + ) i; + } +} {2 1} + +# Further variants of the test case, as found in the ticket +# +do_test aggnested-3.1 { + db eval { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1 ( + id1 INTEGER PRIMARY KEY AUTOINCREMENT, + value1 INTEGER + ); + INSERT INTO t1 VALUES(4469,2),(4476,1); + CREATE TABLE t2 ( + id2 INTEGER PRIMARY KEY AUTOINCREMENT, + value2 INTEGER + ); + INSERT INTO t2 VALUES(0,1),(2,2); + SELECT + (SELECT sum(value2==xyz) FROM t2) + FROM + (SELECT curr.value1 as xyz + FROM t1 AS curr LEFT JOIN t1 AS other + GROUP BY curr.id1); + } +} {1 1} +do_test aggnested-3.2 { + db eval { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1 ( + id1 INTEGER, + value1 INTEGER, + x1 INTEGER + ); + INSERT INTO t1 VALUES(4469,2,98),(4469,1,99),(4469,3,97); + CREATE TABLE t2 ( + value2 INTEGER + ); + INSERT INTO t2 VALUES(1); + SELECT + (SELECT sum(value2==xyz) FROM t2) + FROM + (SELECT value1 as xyz, max(x1) AS pqr + FROM t1 + GROUP BY id1); + } +} {0} +do_test aggnested-3.3 { + db eval { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(id1, value1); + INSERT INTO t1 VALUES(4469,2),(4469,1); + CREATE TABLE t2 (value2); + INSERT INTO t2 VALUES(1); + SELECT (SELECT sum(value2=value1) FROM t2), max(value1) + FROM t1 + GROUP BY id1; + } +} {0 2} + +# A batch of queries all doing approximately the same operation involving +# two nested aggregate queries. +# +do_test aggnested-3.11 { + db eval { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(id1, value1); + INSERT INTO t1 VALUES(4469,12),(4469,11),(4470,34); + CREATE INDEX t1id1 ON t1(id1); + CREATE TABLE t2 (value2); + INSERT INTO t2 VALUES(12),(34),(34); + INSERT INTO t2 SELECT value2 FROM t2; + + SELECT max(value1), (SELECT count(*) FROM t2 WHERE value2=max(value1)) + FROM t1 + GROUP BY id1; + } +} {12 2 34 4} +do_test aggnested-3.12 { + db eval { + SELECT max(value1), (SELECT count(*) FROM t2 WHERE value2=value1) + FROM t1 + GROUP BY id1; + } +} {12 2 34 4} +do_test aggnested-3.13 { + db eval { + SELECT value1, (SELECT sum(value2=value1) FROM t2) + FROM t1; + } +} {12 2 11 0 34 4} +do_test aggnested-3.14 { + db eval { + SELECT value1, (SELECT sum(value2=value1) FROM t2) + FROM t1 + WHERE value1 IN (SELECT max(value1) FROM t1 GROUP BY id1); + } +} {12 2 34 4} +do_test aggnested-3.15 { + # FIXME: If case 3.16 works, then this case really ought to work too... + catchsql { + SELECT max(value1), (SELECT sum(value2=max(value1)) FROM t2) + FROM t1 + GROUP BY id1; + } +} {1 {misuse of aggregate function max()}} +do_test aggnested-3.16 { + db eval { + SELECT max(value1), (SELECT sum(value2=value1) FROM t2) + FROM t1 + GROUP BY id1; + } +} {12 2 34 4} + + finish_test diff --git a/test/instr.test b/test/instr.test new file mode 100644 index 0000000000..b328cd1d2d --- /dev/null +++ b/test/instr.test @@ -0,0 +1,210 @@ +# 2012 October 24 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the built-in INSTR() functions. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Create a table to work with. +# +do_test instr-1.1 { + db eval {SELECT instr('abcdefg','a');} +} {1} +do_test instr-1.2 { + db eval {SELECT instr('abcdefg','b');} +} {2} +do_test instr-1.3 { + db eval {SELECT instr('abcdefg','c');} +} {3} +do_test instr-1.4 { + db eval {SELECT instr('abcdefg','d');} +} {4} +do_test instr-1.5 { + db eval {SELECT instr('abcdefg','e');} +} {5} +do_test instr-1.6 { + db eval {SELECT instr('abcdefg','f');} +} {6} +do_test instr-1.7 { + db eval {SELECT instr('abcdefg','g');} +} {7} +do_test instr-1.8 { + db eval {SELECT instr('abcdefg','h');} +} {0} +do_test instr-1.9 { + db eval {SELECT instr('abcdefg','abcdefg');} +} {1} +do_test instr-1.10 { + db eval {SELECT instr('abcdefg','abcdefgh');} +} {0} +do_test instr-1.11 { + db eval {SELECT instr('abcdefg','bcdefg');} +} {2} +do_test instr-1.12 { + db eval {SELECT instr('abcdefg','bcdefgh');} +} {0} +do_test instr-1.13 { + db eval {SELECT instr('abcdefg','cdefg');} +} {3} +do_test instr-1.14 { + db eval {SELECT instr('abcdefg','cdefgh');} +} {0} +do_test instr-1.15 { + db eval {SELECT instr('abcdefg','defg');} +} {4} +do_test instr-1.16 { + db eval {SELECT instr('abcdefg','defgh');} +} {0} +do_test instr-1.17 { + db eval {SELECT instr('abcdefg','efg');} +} {5} +do_test instr-1.18 { + db eval {SELECT instr('abcdefg','efgh');} +} {0} +do_test instr-1.19 { + db eval {SELECT instr('abcdefg','fg');} +} {6} +do_test instr-1.20 { + db eval {SELECT instr('abcdefg','fgh');} +} {0} +do_test instr-1.21 { + db eval {SELECT coalesce(instr('abcdefg',NULL),'nil');} +} {nil} +do_test instr-1.22 { + db eval {SELECT coalesce(instr(NULL,'x'),'nil');} +} {nil} +do_test instr-1.23 { + db eval {SELECT instr(12345,34);} +} {3} +do_test instr-1.24 { + db eval {SELECT instr(123456.78,34);} +} {3} +do_test instr-1.25 { + db eval {SELECT instr(123456.78,x'3334');} +} {3} +do_test instr-1.26 { + db eval {SELECT instr('äbcdefg','efg');} +} {5} +do_test instr-1.27 { + db eval {SELECT instr('€xyzzy','xyz');} +} {2} +do_test instr-1.28 { + db eval {SELECT instr('abc€xyzzy','xyz');} +} {5} +do_test instr-1.29 { + db eval {SELECT instr('abc€xyzzy','€xyz');} +} {4} +do_test instr-1.30 { + db eval {SELECT instr('abc€xyzzy','c€xyz');} +} {3} +do_test instr-1.31 { + db eval {SELECT instr(x'0102030405',x'01');} +} {1} +do_test instr-1.32 { + db eval {SELECT instr(x'0102030405',x'02');} +} {2} +do_test instr-1.33 { + db eval {SELECT instr(x'0102030405',x'03');} +} {3} +do_test instr-1.34 { + db eval {SELECT instr(x'0102030405',x'04');} +} {4} +do_test instr-1.35 { + db eval {SELECT instr(x'0102030405',x'05');} +} {5} +do_test instr-1.36 { + db eval {SELECT instr(x'0102030405',x'06');} +} {0} +do_test instr-1.37 { + db eval {SELECT instr(x'0102030405',x'0102030405');} +} {1} +do_test instr-1.38 { + db eval {SELECT instr(x'0102030405',x'02030405');} +} {2} +do_test instr-1.39 { + db eval {SELECT instr(x'0102030405',x'030405');} +} {3} +do_test instr-1.40 { + db eval {SELECT instr(x'0102030405',x'0405');} +} {4} +do_test instr-1.41 { + db eval {SELECT instr(x'0102030405',x'0506');} +} {0} +do_test instr-1.42 { + db eval {SELECT instr(x'0102030405',x'');} +} {1} +do_test instr-1.43 { + db eval {SELECT instr(x'',x'');} +} {1} +do_test instr-1.44 { + db eval {SELECT instr('','');} +} {1} +do_test instr-1.45 { + db eval {SELECT instr('abcdefg','');} +} {1} +unset -nocomplain longstr +set longstr abcdefghijklmonpqrstuvwxyz +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +append longstr $longstr +# puts [string length $longstr] +append longstr Xabcde +do_test instr-1.46 { + db eval {SELECT instr($longstr,'X');} +} {106497} +do_test instr-1.47 { + db eval {SELECT instr($longstr,'Y');} +} {0} +do_test instr-1.48 { + db eval {SELECT instr($longstr,'Xa');} +} {106497} +do_test instr-1.49 { + db eval {SELECT instr($longstr,'zXa');} +} {106496} +set longstr [string map {a ä} $longstr] +do_test instr-1.50 { + db eval {SELECT instr($longstr,'X');} +} {106497} +do_test instr-1.51 { + db eval {SELECT instr($longstr,'Y');} +} {0} +do_test instr-1.52 { + db eval {SELECT instr($longstr,'Xä');} +} {106497} +do_test instr-1.53 { + db eval {SELECT instr($longstr,'zXä');} +} {106496} +do_test instr-1.54 { + db eval {SELECT instr(x'78c3a4e282ac79','x');} +} {1} +do_test instr-1.55 { + db eval {SELECT instr(x'78c3a4e282ac79','y');} +} {4} +do_test instr-1.56 { + db eval {SELECT instr(x'78c3a4e282ac79',x'79');} +} {7} +do_test instr-1.57 { + db eval {SELECT instr('xä€y',x'79');} +} {4} + + +finish_test diff --git a/test/orderby1.test b/test/orderby1.test index 2001e34009..f459fc8195 100644 --- a/test/orderby1.test +++ b/test/orderby1.test @@ -48,7 +48,7 @@ do_test 1.0 { } {} do_test 1.1a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn } } {one-a one-c two-a two-b three-a three-c} @@ -57,7 +57,7 @@ do_test 1.1a { do_test 1.1b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn } } {~/ORDER BY/} ;# ORDER BY optimized out @@ -66,7 +66,7 @@ do_test 1.1b { # do_test 1.2a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn } } {one-a one-c two-a two-b three-a three-c} @@ -75,7 +75,7 @@ do_test 1.2a { do_test 1.2b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn } } {/ORDER BY/} ;# separate sorting pass due to "+" on ORDER BY terms @@ -85,13 +85,13 @@ do_test 1.3a { optimization_control db order-by-idx-join 0 db cache flush db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn } } {one-a one-c two-a two-b three-a three-c} do_test 1.3b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn } } {/ORDER BY/} ;# separate sorting pass due to disabled optimization optimization_control db all 1 @@ -101,53 +101,53 @@ db cache flush # do_test 1.4a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn } } {three-a three-c two-a two-b one-a one-c} do_test 1.4b { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn } } {three-a three-c two-a two-b one-a one-c} ;# verify same order after sorting do_test 1.4c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn } } {~/ORDER BY/} ;# optimized out do_test 1.5a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC } } {one-c one-a two-b two-a three-c three-a} do_test 1.5b { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC } } {one-c one-a two-b two-a three-c three-a} ;# verify same order after sorting do_test 1.5c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC } } {~/ORDER BY/} ;# optimized out do_test 1.6a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC } } {three-c three-a two-b two-a one-c one-a} do_test 1.6b { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn DESC } } {three-c three-a two-b two-a one-c one-a} ;# verify same order after sorting do_test 1.6c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC } } {~/ORDER BY/} ;# ORDER BY optimized-out @@ -183,7 +183,7 @@ do_test 2.0 { } {} do_test 2.1a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn } } {one-a one-c two-a two-b three-a three-c} @@ -192,19 +192,19 @@ do_test 2.1a { do_test 2.1b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn } } {~/ORDER BY/} ;# ORDER BY optimized out do_test 2.1c { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title, aid, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, aid, tn } } {one-a one-c two-a two-b three-a three-c} do_test 2.1d { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title, aid, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, aid, tn } } {~/ORDER BY/} ;# ORDER BY optimized out @@ -213,7 +213,7 @@ do_test 2.1d { # do_test 2.2a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn } } {one-a one-c two-a two-b three-a three-c} @@ -222,7 +222,7 @@ do_test 2.2a { do_test 2.2b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn } } {/ORDER BY/} ;# separate sorting pass due to "+" on ORDER BY terms @@ -232,13 +232,13 @@ do_test 2.3a { optimization_control db order-by-idx-join 0 db cache flush db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn } } {one-a one-c two-a two-b three-a three-c} do_test 2.3b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn } } {/ORDER BY/} ;# separate sorting pass due to disabled optimization optimization_control db all 1 @@ -248,53 +248,53 @@ db cache flush # do_test 2.4a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn } } {three-a three-c two-a two-b one-a one-c} do_test 2.4b { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn } } {three-a three-c two-a two-b one-a one-c} ;# verify same order after sorting do_test 2.4c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn } } {~/ORDER BY/} ;# optimized out do_test 2.5a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC } } {one-c one-a two-b two-a three-c three-a} do_test 2.5b { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC } } {one-c one-a two-b two-a three-c three-a} ;# verify same order after sorting do_test 2.5c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC } } {~/ORDER BY/} ;# optimized out do_test 2.6a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC } } {three-c three-a two-b two-a one-c one-a} do_test 2.6b { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn DESC } } {three-c three-a two-b two-a one-c one-a} ;# verify same order after sorting do_test 2.6c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC } } {~/ORDER BY/} ;# ORDER BY optimized out @@ -330,7 +330,7 @@ do_test 3.0 { } {} do_test 3.1a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC } } {one-c one-a two-b two-a three-c three-a} @@ -339,7 +339,7 @@ do_test 3.1a { do_test 3.1b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC } } {~/ORDER BY/} ;# ORDER BY optimized out @@ -348,7 +348,7 @@ do_test 3.1b { # do_test 3.2a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC } } {one-c one-a two-b two-a three-c three-a} @@ -357,7 +357,7 @@ do_test 3.2a { do_test 3.2b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC } } {/ORDER BY/} ;# separate sorting pass due to "+" on ORDER BY terms @@ -367,13 +367,13 @@ do_test 3.3a { optimization_control db order-by-idx-join 0 db cache flush db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC } } {one-c one-a two-b two-a three-c three-a} do_test 3.3b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC } } {/ORDER BY/} ;# separate sorting pass due to disabled optimization optimization_control db all 1 @@ -383,54 +383,54 @@ db cache flush # do_test 3.4a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn } } {one-a one-c two-a two-b three-a three-c} do_test 3.4b { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn } } {one-a one-c two-a two-b three-a three-c} ;# verify same order after sorting do_test 3.4c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn } } {~/ORDER BY/} ;# optimized out do_test 3.5a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC } } {three-c three-a two-b two-a one-c one-a} do_test 3.5b { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn DESC } } {three-c three-a two-b two-a one-c one-a} ;# verify same order after sorting do_test 3.5c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC } } {~/ORDER BY/} ;# optimzed out do_test 3.6a { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn } } {three-a three-c two-a two-b one-a one-c} do_test 3.6b { db eval { - SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn } } {three-a three-c two-a two-b one-a one-c} ;# verify same order after sorting do_test 3.6c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn + SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn } } {~/ORDER BY/} ;# inverted ASC/DESC is optimized out diff --git a/test/pager1.test b/test/pager1.test index fac434037e..6849e55bbd 100644 --- a/test/pager1.test +++ b/test/pager1.test @@ -2487,4 +2487,32 @@ do_test pager1-32.1 { # Cleanup 20MB file left by the previous test. forcedelete test.db +#------------------------------------------------------------------------- +# Test that if a transaction is committed in journal_mode=DELETE mode, +# and the call to unlink() returns an ENOENT error, the COMMIT does not +# succeed. +# +if {$::tcl_platform(platform)=="unix"} { + do_test pager1-33.1 { + sqlite3 db test.db + execsql { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('one'); + INSERT INTO t1 VALUES('two'); + BEGIN; + INSERT INTO t1 VALUES('three'); + INSERT INTO t1 VALUES('four'); + } + forcedelete bak-journal + file rename test.db-journal bak-journal + + catchsql COMMIT + } {1 {disk I/O error}} + + do_test pager1-33.2 { + file rename bak-journal test.db-journal + execsql { SELECT * FROM t1 } + } {one two} +} + finish_test diff --git a/test/whereE.test b/test/whereE.test new file mode 100644 index 0000000000..e686a4628e --- /dev/null +++ b/test/whereE.test @@ -0,0 +1,62 @@ +# 2012 November 9 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the query planner to make sure it +# is making good planning decisions. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix whereE + +do_execsql_test 1.1 { + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,10), (2,20), (3,30), (2,22), (3, 33); + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + ALTER TABLE t1 ADD COLUMN c; + UPDATE t1 SET c=a*rowid+10000; + CREATE INDEX t1ab ON t1(a,b); + + CREATE TABLE t2(x,y); + INSERT INTO t2 VALUES(4,44),(5,55),(6,66),(7,77); + INSERT INTO t2 SELECT x+4, (x+4)*11 FROM t2; + INSERT INTO t2 SELECT x+8, (x+8)*11 FROM t2; + INSERT INTO t2 SELECT x+16, (x+16)*11 FROM t2; + INSERT INTO t2 SELECT x+32, (x+32)*11 FROM t2; + INSERT INTO t2 SELECT x+64, (x+32)*11 FROM t2; + ALTER TABLE t2 ADD COLUMN z; + UPDATE t2 SET z=2; + CREATE UNIQUE INDEX t2zx ON t2(z,x); + + EXPLAIN QUERY PLAN SELECT x FROM t1, t2 WHERE a=z AND c=x; +} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/} +do_execsql_test 1.2 { + EXPLAIN QUERY PLAN SELECT x FROM t2, t1 WHERE a=z AND c=x; +} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/} +do_execsql_test 1.3 { + ANALYZE; + EXPLAIN QUERY PLAN SELECT x FROM t1, t2 WHERE a=z AND c=x; +} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/} +do_execsql_test 1.4 { + EXPLAIN QUERY PLAN SELECT x FROM t2, t1 WHERE a=z AND c=x; +} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/} + +finish_test diff --git a/test/whereF.test b/test/whereF.test new file mode 100644 index 0000000000..57bdbee058 --- /dev/null +++ b/test/whereF.test @@ -0,0 +1,115 @@ +# 2012 November 9 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# Test cases for query planning decisions. + + +# +# The tests in this file demonstrate the behaviour of the query planner +# in determining the order in which joined tables are scanned. +# +# Assume there are two tables being joined - t1 and t2. Each has a cost +# if it is the outer loop, and a cost if it is the inner loop. As follows: +# +# t1(outer) - cost of scanning t1 as the outer loop. +# t1(inner) - cost of scanning t1 as the inner loop. +# t2(outer) - cost of scanning t2 as the outer loop. +# t2(inner) - cost of scanning t2 as the inner loop. +# +# Depending on the order in which the planner nests the scans, the total +# cost of the join query is one of: +# +# t1(outer) * t2(inner) +# t2(outer) * t1(inner) +# +# The tests in this file attempt to verify that the planner nests joins in +# the correct order when the following are true: +# +# + (t1(outer) * t2(inner)) > (t1(inner) * t2(outer) +# + t1(outer) < t2(outer) +# +# In other words, when the best overall query plan has t2 as the outer loop, +# but when the outer loop is considered independent of the inner, t1 is the +# most efficient choice. +# +# In order to make them more predictable, automatic indexes are turned off for +# the tests in this file. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix x + +do_execsql_test 1.0 { + PRAGMA automatic_index = 0; + CREATE TABLE t1(a, b, c); + CREATE TABLE t2(d, e, f); + CREATE UNIQUE INDEX i1 ON t1(a); + CREATE UNIQUE INDEX i2 ON t2(d); +} {} + +foreach {tn sql} { + 1 "SELECT * FROM t1, t2 WHERE t1.a=t2.e AND t2.d? AND t2.d>t1.c AND t1.b=t2.e" + 2 "SELECT * FROM t2, t1 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e" + 3 "SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e" +} { + do_test 2.$tn { + db eval "EXPLAIN QUERY PLAN $sql" + } {/.*SCAN TABLE t2 .*SEARCH TABLE t1 .*/} +} + +do_execsql_test 3.0 { + DROP TABLE t1; + DROP TABLE t2; + CREATE TABLE t1(a, b, c); + CREATE TABLE t2(d, e, f); + + CREATE UNIQUE INDEX i1 ON t1(a, b); + CREATE INDEX i2 ON t2(d); +} {} + +foreach {tn sql} { + 1 {SELECT t1.a, t1.b, t2.d, t2.e FROM t1, t2 + WHERE t2.d=t1.b AND t1.a=(t2.d+1) AND t1.b = (t2.e+1)} + + 2 {SELECT t1.a, t1.b, t2.d, t2.e FROM t2, t1 + WHERE t2.d=t1.b AND t1.a=(t2.d+1) AND t1.b = (t2.e+1)} + + 3 {SELECT t1.a, t1.b, t2.d, t2.e FROM t2 CROSS JOIN t1 + WHERE t2.d=t1.b AND t1.a=(t2.d+1) AND t1.b = (t2.e+1)} +} { + do_test 3.$tn { + db eval "EXPLAIN QUERY PLAN $sql" + } {/.*SCAN TABLE t2 .*SEARCH TABLE t1 .*/} +} + +finish_test