From: drh Date: Mon, 31 Aug 2015 17:34:41 +0000 (+0000) Subject: Make the distinction between truly deterministic functions and date/time X-Git-Tag: version-3.9.0~150^2~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1d85e405e6c7674d0df4d4a720ceaeb47273b9ba;p=thirdparty%2Fsqlite.git Make the distinction between truly deterministic functions and date/time functions which only return the same answer for a single query. Only truly deterministic functions are allowed in indexes. Add new expression index test cases. FossilOrigin-Name: c77554b5c42327106a7b90334e9cc3c07b007c76 --- diff --git a/manifest b/manifest index 64b3dc444a..bcc1d9de6b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\sanalysis\sand\susage\sof\sindexed\sexpressions\sin\sthe\squery\splanner. -D 2015-08-31T15:58:06.350 +C Make\sthe\sdistinction\sbetween\struly\sdeterministic\sfunctions\sand\sdate/time\nfunctions\swhich\sonly\sreturn\sthe\ssame\sanswer\sfor\sa\ssingle\squery.\s\sOnly\struly\ndeterministic\sfunctions\sare\sallowed\sin\sindexes.\s\sAdd\snew\sexpression\sindex\ntest\scases. +D 2015-08-31T17:34:41.308 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in f85066ce844a28b671aaeeff320921cd0ce36239 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -286,7 +286,7 @@ F src/build.c e0902658fc86dbd60a5c6772ca45429c69ee81fe F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0 F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b -F src/date.c 8ec787fed4929d8ccdf6b1bc360fccc3e1d2ca58 +F src/date.c fb1c99172017dcc8e237339132c91a21a0788584 F src/dbstat.c f402e77e25089c6003d0c60b3233b9b3947d599a F src/delete.c 224e5c9a0eae7fbdd3b461ca971465c992851354 F src/expr.c cf0c5ef6467d3eb65e3ff5c88eb2f602ba3b5163 @@ -335,14 +335,14 @@ F src/pragma.h 631a91c8b0e6ca8f051a1d8a4a0da4150e04620a F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1 F src/printf.c 2bc439ff20a4aad0e0ad50a37a67b5eae7d20edc F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 -F src/resolve.c e6dc5a5490cf93afc1cc2cb58280c98da56acb3c +F src/resolve.c 3ec6456a534bcbf0f3fc11ad83c6208301db60a2 F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e F src/select.c b52c80f2b1bdb62491f9ce40eea0c5f80c78d105 F src/shell.c 6332ef06db1390ef812cfdff1fc97b4fd76cdd42 F src/sqlite.h.in 378bebc8fe6a88bade25e5f23b7e6123fdc64b00 F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad F src/sqlite3ext.h f700e6a9dd1fdcccc9951ab022b366fb66b9e413 -F src/sqliteInt.h 340fc115a707838ad365b52057f126b143f6aa60 +F src/sqliteInt.h 332ae4f753d91c92421a2c4227c195298d5b154c F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46 F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179 F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e @@ -780,7 +780,7 @@ F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7 F test/index6.test 7102ec371414c42dfb1d5ca37eb4519aa9edc23a F test/index7.test 9c6765a74fc3fcde7aebc5b3bd40d98df14a527c F test/indexedby.test 5f527a78bae74c61b8046ae3037f9dfb0bf0c353 -F test/indexexpr1.test 36b7aab1fdb03710b99ee876ff764b92c7b96e3e +F test/indexexpr1.test 1e57b4bb93b65a451803e7547a51f685af1c35db F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7 F test/insert.test 38742b5e9601c8f8d76e9b7555f7270288c2d371 @@ -1381,7 +1381,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 7bde6d4d8cf05e1beb9bdf20b85760dc3e7a76c9 -R b7744e5a00368fcccd1913a83a0521e8 +P f8893696387cba9d293a05a68dc38228077b3dc5 +R 73b6719fd207957d3d30c3e1b6c218bc U drh -Z 32945199df036dc2360847bdab3537ac +Z fe2263cb4a747333f78b4cc6eb661f66 diff --git a/manifest.uuid b/manifest.uuid index 327673b19f..3dc8f017b9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f8893696387cba9d293a05a68dc38228077b3dc5 \ No newline at end of file +c77554b5c42327106a7b90334e9cc3c07b007c76 \ No newline at end of file diff --git a/src/date.c b/src/date.c index 6b11d9904a..8a66eae900 100644 --- a/src/date.c +++ b/src/date.c @@ -1115,14 +1115,14 @@ static void currentTimeFunc( void sqlite3RegisterDateTimeFunctions(void){ static SQLITE_WSD FuncDef aDateTimeFuncs[] = { #ifndef SQLITE_OMIT_DATETIME_FUNCS - FUNCTION(julianday, -1, 0, 0, juliandayFunc ), - FUNCTION(date, -1, 0, 0, dateFunc ), - FUNCTION(time, -1, 0, 0, timeFunc ), - FUNCTION(datetime, -1, 0, 0, datetimeFunc ), - FUNCTION(strftime, -1, 0, 0, strftimeFunc ), - FUNCTION(current_time, 0, 0, 0, ctimeFunc ), - FUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), - FUNCTION(current_date, 0, 0, 0, cdateFunc ), + DFUNCTION(julianday, -1, 0, 0, juliandayFunc ), + DFUNCTION(date, -1, 0, 0, dateFunc ), + DFUNCTION(time, -1, 0, 0, timeFunc ), + DFUNCTION(datetime, -1, 0, 0, datetimeFunc ), + DFUNCTION(strftime, -1, 0, 0, strftimeFunc ), + DFUNCTION(current_time, 0, 0, 0, ctimeFunc ), + DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), + DFUNCTION(current_date, 0, 0, 0, cdateFunc ), #else STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc), STR_FUNCTION(current_date, 0, "%Y-%m-%d", 0, currentTimeFunc), diff --git a/src/resolve.c b/src/resolve.c index eb43396454..0b8dde7cc6 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -732,9 +732,15 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ return WRC_Prune; } #endif - if( pDef->funcFlags & SQLITE_FUNC_CONSTANT ){ + if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_DATETIME) ){ + /* For the purposes of the EP_ConstFunc flag, date and time + ** functions are considered constant because the the time does + ** not change for the duration of a query. */ ExprSetProperty(pExpr,EP_ConstFunc); - }else{ + } + if( (pDef->funcFlags & SQLITE_FUNC_CONSTANT)==0 ){ + /* DATETIME functions are considered non-deterministic because of + ** the 'now' capability */ notValid(pParse, pNC, "non-deterministic functions", NC_IdxExpr, 0); } } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 69f98da26d..613f351aa5 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1384,18 +1384,19 @@ struct FuncDestructor { ** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. There ** are assert() statements in the code to verify this. */ -#define SQLITE_FUNC_ENCMASK 0x003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */ -#define SQLITE_FUNC_LIKE 0x004 /* Candidate for the LIKE optimization */ -#define SQLITE_FUNC_CASE 0x008 /* Case-sensitive LIKE-type function */ -#define SQLITE_FUNC_EPHEM 0x010 /* Ephemeral. Delete with VDBE */ -#define SQLITE_FUNC_NEEDCOLL 0x020 /* sqlite3GetFuncCollSeq() might be called */ -#define SQLITE_FUNC_LENGTH 0x040 /* Built-in length() function */ -#define SQLITE_FUNC_TYPEOF 0x080 /* Built-in typeof() function */ -#define SQLITE_FUNC_COUNT 0x100 /* Built-in count(*) aggregate */ -#define SQLITE_FUNC_COALESCE 0x200 /* Built-in coalesce() or ifnull() */ -#define SQLITE_FUNC_UNLIKELY 0x400 /* Built-in unlikely() function */ -#define SQLITE_FUNC_CONSTANT 0x800 /* Constant inputs give a constant output */ -#define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */ +#define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */ +#define SQLITE_FUNC_LIKE 0x0004 /* Candidate for the LIKE optimization */ +#define SQLITE_FUNC_CASE 0x0008 /* Case-sensitive LIKE-type function */ +#define SQLITE_FUNC_EPHEM 0x0010 /* Ephemeral. Delete with VDBE */ +#define SQLITE_FUNC_NEEDCOLL 0x0020 /* sqlite3GetFuncCollSeq() might be called*/ +#define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */ +#define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */ +#define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */ +#define SQLITE_FUNC_COALESCE 0x0200 /* Built-in coalesce() or ifnull() */ +#define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */ +#define SQLITE_FUNC_CONSTANT 0x0800 /* Constant inputs give a constant output */ +#define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */ +#define SQLITE_FUNC_DATETIME 0x2000 /* Function returns a date and/or time */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are @@ -1411,6 +1412,10 @@ struct FuncDestructor { ** VFUNCTION(zName, nArg, iArg, bNC, xFunc) ** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag. ** +** DFUNCTION(zName, nArg, iArg, bNC, xFunc) +** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag and +** adds the SQLITE_FUNC_DATETIME flag. Used for date & time functions. +** ** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal) ** Used to create an aggregate function definition implemented by ** the C functions xStep and xFinal. The first four parameters @@ -1431,6 +1436,9 @@ struct FuncDestructor { #define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} +#define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \ + {nArg, SQLITE_FUNC_DATETIME|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} #define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ {nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} @@ -2121,7 +2129,7 @@ struct Expr { #define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */ #define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */ #define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ -#define EP_ConstFunc 0x080000 /* Node is a SQLITE_FUNC_CONSTANT function */ +#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _DATETIME function */ #define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */ #define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */ diff --git a/test/indexexpr1.test b/test/indexexpr1.test index c912226ce5..0b3e7c6603 100644 --- a/test/indexexpr1.test +++ b/test/indexexpr1.test @@ -18,28 +18,131 @@ source $testdir/tester.tcl do_execsql_test indexexpr1-100 { CREATE TABLE t1(a,b,c); INSERT INTO t1(a,b,c) - VALUES('In the beginning was the Word',1,1), - ('and the Word was with God',1,2), - ('and the Word was God',1,3), - ('The same was in the beginning with God',2,1), - ('All things were made by him',3,1), - ('and without him was not any thing made that was made',3,2); + VALUES('In_the_beginning_was_the_Word',1,1), + ('and_the_Word_was_with_God',1,2), + ('and_the_Word_was_God',1,3), + ('The_same_was_in_the_beginning_with_God',2,1), + ('All_things_were_made_by_him',3,1), + ('and_without_him_was_not_any_thing_made_that_was_made',3,2); CREATE INDEX t1a1 ON t1(substr(a,1,12)); } {} do_execsql_test indexexpr1-110 { - SELECT b, c, '|' FROM t1 WHERE substr(a,1,12)=='and the Word' ORDER BY b, c; + SELECT b, c, '|' FROM t1 WHERE substr(a,1,12)=='and_the_Word' ORDER BY b, c; } {1 2 | 1 3 |} do_execsql_test indexexpr1-110eqp { EXPLAIN QUERY PLAN - SELECT b, c, '|' FROM t1 WHERE substr(a,1,12)=='and the Word' ORDER BY b, c; + SELECT b, c, '|' FROM t1 WHERE substr(a,1,12)=='and_the_Word' ORDER BY b, c; } {/USING INDEX t1a1/} do_execsql_test indexexpr1-120 { - SELECT b, c, '|' FROM t1 WHERE 'and the Word'==substr(a,1,12) ORDER BY b, c; + SELECT b, c, '|' FROM t1 WHERE 'and_the_Word'==substr(a,1,12) ORDER BY b, c; } {1 2 | 1 3 |} do_execsql_test indexexpr1-120eqp { EXPLAIN QUERY PLAN - SELECT b, c, '|' FROM t1 WHERE 'and the Word'==substr(a,1,12) ORDER BY b, c; + SELECT b, c, '|' FROM t1 WHERE 'and_the_Word'==substr(a,1,12) ORDER BY b, c; } {/USING INDEX t1a1/} +do_execsql_test indexexpr1-130 { + CREATE INDEX t1ba ON t1(b,substr(a,2,3),c); + SELECT c FROM t1 WHERE b=1 AND substr(a,2,3)='nd_' ORDER BY c; +} {2 3} +do_execsql_test indexexpr1-130eqp { + EXPLAIN QUERY PLAN + SELECT c FROM t1 WHERE b=1 AND substr(a,2,3)='nd_' ORDER BY c; +} {/USING INDEX t1ba/} + +do_execsql_test indexexpr1-140 { + SELECT rowid, substr(a,b,3), '|' FROM t1 ORDER BY 2; +} {1 In_ | 2 and | 3 and | 6 d_w | 4 he_ | 5 l_t |} +do_execsql_test indexexpr1-141 { + CREATE INDEX t1abx ON t1(substr(a,b,3)); + SELECT rowid FROM t1 WHERE substr(a,b,3)<='and' ORDER BY +rowid; +} {1 2 3} +do_execsql_test indexexpr1-141eqp { + EXPLAIN QUERY PLAN + SELECT rowid FROM t1 WHERE substr(a,b,3)<='and' ORDER BY +rowid; +} {/USING INDEX t1abx/} +do_execsql_test indexexpr1-142 { + SELECT rowid FROM t1 WHERE +substr(a,b,3)<='and' ORDER BY +rowid; +} {1 2 3} +do_execsql_test indexexpr1-150 { + SELECT rowid FROM t1 WHERE substr(a,b,3) IN ('and','l_t','xyz') + ORDER BY +rowid; +} {2 3 5} +do_execsql_test indexexpr1-150eqp { + EXPLAIN QUERY PLAN + SELECT rowid FROM t1 WHERE substr(a,b,3) IN ('and','l_t','xyz') + ORDER BY +rowid; +} {/USING INDEX t1abx/} + +do_execsql_test indexexpr1-200 { + DROP TABLE t1; + CREATE TABLE t1(id ANY PRIMARY KEY, a,b,c) WITHOUT ROWID; + INSERT INTO t1(id,a,b,c) + VALUES(1,'In_the_beginning_was_the_Word',1,1), + (2,'and_the_Word_was_with_God',1,2), + (3,'and_the_Word_was_God',1,3), + (4,'The_same_was_in_the_beginning_with_God',2,1), + (5,'All_things_were_made_by_him',3,1), + (6,'and_without_him_was_not_any_thing_made_that_was_made',3,2); + CREATE INDEX t1a1 ON t1(substr(a,1,12)); +} {} +do_execsql_test indexexpr1-210 { + SELECT b, c, '|' FROM t1 WHERE substr(a,1,12)=='and_the_Word' ORDER BY b, c; +} {1 2 | 1 3 |} +do_execsql_test indexexpr1-210eqp { + EXPLAIN QUERY PLAN + SELECT b, c, '|' FROM t1 WHERE substr(a,1,12)=='and_the_Word' ORDER BY b, c; +} {/USING INDEX t1a1/} +do_execsql_test indexexpr1-220 { + SELECT b, c, '|' FROM t1 WHERE 'and_the_Word'==substr(a,1,12) ORDER BY b, c; +} {1 2 | 1 3 |} +do_execsql_test indexexpr1-220eqp { + EXPLAIN QUERY PLAN + SELECT b, c, '|' FROM t1 WHERE 'and_the_Word'==substr(a,1,12) ORDER BY b, c; +} {/USING INDEX t1a1/} + +do_execsql_test indexexpr1-230 { + CREATE INDEX t1ba ON t1(b,substr(a,2,3),c); + SELECT c FROM t1 WHERE b=1 AND substr(a,2,3)='nd_' ORDER BY c; +} {2 3} +do_execsql_test indexexpr1-230eqp { + EXPLAIN QUERY PLAN + SELECT c FROM t1 WHERE b=1 AND substr(a,2,3)='nd_' ORDER BY c; +} {/USING INDEX t1ba/} + +do_execsql_test indexexpr1-240 { + SELECT id, substr(a,b,3), '|' FROM t1 ORDER BY 2; +} {1 In_ | 2 and | 3 and | 6 d_w | 4 he_ | 5 l_t |} +do_execsql_test indexexpr1-241 { + CREATE INDEX t1abx ON t1(substr(a,b,3)); + SELECT id FROM t1 WHERE substr(a,b,3)<='and' ORDER BY +id; +} {1 2 3} +do_execsql_test indexexpr1-241eqp { + EXPLAIN QUERY PLAN + SELECT id FROM t1 WHERE substr(a,b,3)<='and' ORDER BY +id; +} {/USING INDEX t1abx/} +do_execsql_test indexexpr1-242 { + SELECT id FROM t1 WHERE +substr(a,b,3)<='and' ORDER BY +id; +} {1 2 3} +do_execsql_test indexexpr1-250 { + SELECT id FROM t1 WHERE substr(a,b,3) IN ('and','l_t','xyz') + ORDER BY +id; +} {2 3 5} +do_execsql_test indexexpr1-250eqp { + EXPLAIN QUERY PLAN + SELECT id FROM t1 WHERE substr(a,b,3) IN ('and','l_t','xyz') + ORDER BY +id; +} {/USING INDEX t1abx/} + +do_catchsql_test indexexpr1-300 { + CREATE TABLE t2(a,b,c); + CREATE INDEX t2x1 ON t2(a,b+random()); +} {1 {non-deterministic functions prohibited in index expressions}} +do_catchsql_test indexexpr1-301 { + CREATE INDEX t2x1 ON t2(a+julianday('now')); +} {1 {non-deterministic functions prohibited in index expressions}} +do_catchsql_test indexexpr1-310 { + CREATE INDEX t2x2 ON t2(a,b+(SELECT 15)); +} {1 {subqueries prohibited in index expressions}} finish_test