1782d68a4Sdrh# 2012 November 9 2782d68a4Sdrh# 3782d68a4Sdrh# The author disclaims copyright to this source code. In place of 4782d68a4Sdrh# a legal notice, here is a blessing: 5782d68a4Sdrh# 6782d68a4Sdrh# May you do good and not evil. 7782d68a4Sdrh# May you find forgiveness for yourself and forgive others. 8782d68a4Sdrh# May you share freely, never taking more than you give. 9782d68a4Sdrh# 10782d68a4Sdrh#*********************************************************************** 11782d68a4Sdrh# 12782d68a4Sdrh# Test cases for query planning decisions. 13782d68a4Sdrh 14782d68a4Sdrh 15782d68a4Sdrh# 16782d68a4Sdrh# The tests in this file demonstrate the behaviour of the query planner 17782d68a4Sdrh# in determining the order in which joined tables are scanned. 18782d68a4Sdrh# 19782d68a4Sdrh# Assume there are two tables being joined - t1 and t2. Each has a cost 20782d68a4Sdrh# if it is the outer loop, and a cost if it is the inner loop. As follows: 21782d68a4Sdrh# 22782d68a4Sdrh# t1(outer) - cost of scanning t1 as the outer loop. 23782d68a4Sdrh# t1(inner) - cost of scanning t1 as the inner loop. 24782d68a4Sdrh# t2(outer) - cost of scanning t2 as the outer loop. 25782d68a4Sdrh# t2(inner) - cost of scanning t2 as the inner loop. 26782d68a4Sdrh# 27782d68a4Sdrh# Depending on the order in which the planner nests the scans, the total 28782d68a4Sdrh# cost of the join query is one of: 29782d68a4Sdrh# 30782d68a4Sdrh# t1(outer) * t2(inner) 31782d68a4Sdrh# t2(outer) * t1(inner) 32782d68a4Sdrh# 33782d68a4Sdrh# The tests in this file attempt to verify that the planner nests joins in 34782d68a4Sdrh# the correct order when the following are true: 35782d68a4Sdrh# 36782d68a4Sdrh# + (t1(outer) * t2(inner)) > (t1(inner) * t2(outer) 37782d68a4Sdrh# + t1(outer) < t2(outer) 38782d68a4Sdrh# 39782d68a4Sdrh# In other words, when the best overall query plan has t2 as the outer loop, 40782d68a4Sdrh# but when the outer loop is considered independent of the inner, t1 is the 41782d68a4Sdrh# most efficient choice. 42782d68a4Sdrh# 43782d68a4Sdrh# In order to make them more predictable, automatic indexes are turned off for 44782d68a4Sdrh# the tests in this file. 45782d68a4Sdrh# 46782d68a4Sdrh 47782d68a4Sdrhset testdir [file dirname $argv0] 48782d68a4Sdrhsource $testdir/tester.tcl 49fd5874d2Sdrhset testprefix whereF 50782d68a4Sdrh 51782d68a4Sdrhdo_execsql_test 1.0 { 52782d68a4Sdrh PRAGMA automatic_index = 0; 53782d68a4Sdrh CREATE TABLE t1(a, b, c); 54782d68a4Sdrh CREATE TABLE t2(d, e, f); 55782d68a4Sdrh CREATE UNIQUE INDEX i1 ON t1(a); 56782d68a4Sdrh CREATE UNIQUE INDEX i2 ON t2(d); 57782d68a4Sdrh} {} 58782d68a4Sdrh 59782d68a4Sdrhforeach {tn sql} { 60782d68a4Sdrh 1 "SELECT * FROM t1, t2 WHERE t1.a=t2.e AND t2.d<t1.b AND t1.c!=10" 61782d68a4Sdrh 2 "SELECT * FROM t2, t1 WHERE t1.a=t2.e AND t2.d<t1.b AND t1.c!=10" 62782d68a4Sdrh 3 "SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a=t2.e AND t2.d<t1.b AND t1.c!=10" 63782d68a4Sdrh} { 64782d68a4Sdrh do_test 1.$tn { 65782d68a4Sdrh db eval "EXPLAIN QUERY PLAN $sql" 66*8210233cSdrh } {/.*SCAN t2\y.*SEARCH t1\y.*/} 67782d68a4Sdrh} 68782d68a4Sdrh 69782d68a4Sdrhdo_execsql_test 2.0 { 70782d68a4Sdrh DROP TABLE t1; 71782d68a4Sdrh DROP TABLE t2; 72782d68a4Sdrh CREATE TABLE t1(a, b, c); 73782d68a4Sdrh CREATE TABLE t2(d, e, f); 74782d68a4Sdrh 75782d68a4Sdrh CREATE UNIQUE INDEX i1 ON t1(a); 76782d68a4Sdrh CREATE UNIQUE INDEX i2 ON t1(b); 77782d68a4Sdrh CREATE UNIQUE INDEX i3 ON t2(d); 78782d68a4Sdrh} {} 79782d68a4Sdrh 80782d68a4Sdrhforeach {tn sql} { 81782d68a4Sdrh 1 "SELECT * FROM t1, t2 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e" 82782d68a4Sdrh 2 "SELECT * FROM t2, t1 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e" 83782d68a4Sdrh 3 "SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a>? AND t2.d>t1.c AND t1.b=t2.e" 84782d68a4Sdrh} { 85782d68a4Sdrh do_test 2.$tn { 86782d68a4Sdrh db eval "EXPLAIN QUERY PLAN $sql" 87*8210233cSdrh } {/.*SCAN t2\y.*SEARCH t1\y.*/} 88782d68a4Sdrh} 89782d68a4Sdrh 90782d68a4Sdrhdo_execsql_test 3.0 { 91782d68a4Sdrh DROP TABLE t1; 92782d68a4Sdrh DROP TABLE t2; 93782d68a4Sdrh CREATE TABLE t1(a, b, c); 94782d68a4Sdrh CREATE TABLE t2(d, e, f); 95782d68a4Sdrh 96782d68a4Sdrh CREATE UNIQUE INDEX i1 ON t1(a, b); 97782d68a4Sdrh CREATE INDEX i2 ON t2(d); 98782d68a4Sdrh} {} 99782d68a4Sdrh 100782d68a4Sdrhforeach {tn sql} { 101782d68a4Sdrh 1 {SELECT t1.a, t1.b, t2.d, t2.e FROM t1, t2 102782d68a4Sdrh WHERE t2.d=t1.b AND t1.a=(t2.d+1) AND t1.b = (t2.e+1)} 103782d68a4Sdrh 104782d68a4Sdrh 2 {SELECT t1.a, t1.b, t2.d, t2.e FROM t2, t1 105782d68a4Sdrh WHERE t2.d=t1.b AND t1.a=(t2.d+1) AND t1.b = (t2.e+1)} 106782d68a4Sdrh 107782d68a4Sdrh 3 {SELECT t1.a, t1.b, t2.d, t2.e FROM t2 CROSS JOIN t1 108782d68a4Sdrh WHERE t2.d=t1.b AND t1.a=(t2.d+1) AND t1.b = (t2.e+1)} 109782d68a4Sdrh} { 110782d68a4Sdrh do_test 3.$tn { 111782d68a4Sdrh db eval "EXPLAIN QUERY PLAN $sql" 112*8210233cSdrh } {/.*SCAN t2\y.*SEARCH t1\y.*/} 113782d68a4Sdrh} 114782d68a4Sdrh 115f46af737Sdrhdo_execsql_test 4.0 { 116f46af737Sdrh CREATE TABLE t4(a,b,c,d,e, PRIMARY KEY(a,b,c)); 117f46af737Sdrh CREATE INDEX t4adc ON t4(a,d,c); 118f46af737Sdrh CREATE UNIQUE INDEX t4aebc ON t4(a,e,b,c); 119f46af737Sdrh EXPLAIN QUERY PLAN SELECT rowid FROM t4 WHERE a=? AND b=?; 120f46af737Sdrh} {/a=. AND b=./} 121f46af737Sdrh 122c456a76fSdan#------------------------------------------------------------------------- 123c456a76fSdan# Test the following case: 124c456a76fSdan# 125c456a76fSdan# ... FROM t1, t2 WHERE ( 126c456a76fSdan# t2.rowid = +t1.rowid OR (t2.f2 = t1.f1 AND t1.f1!=-1) 127c456a76fSdan# ) 128c456a76fSdan# 129c456a76fSdan# where there is an index on t2(f2). The planner should use "t1" as the 130c456a76fSdan# outer loop. The inner loop, on "t2", is an OR optimization. One pass 131c456a76fSdan# for: 132c456a76fSdan# 133c456a76fSdan# t2.rowid = $1 134c456a76fSdan# 135c456a76fSdan# and another for: 136c456a76fSdan# 137c456a76fSdan# t2.f2=$1 AND $1!=-1 138c456a76fSdan# 139c456a76fSdan# the test is to ensure that on the second pass, the ($1!=-1) condition 140c456a76fSdan# is tested before any seek operations are performed - i.e. outside of 141c456a76fSdan# the loop through the f2=$1 range of the t2(f2) index. 142c456a76fSdan# 143c456a76fSdanreset_db 144c456a76fSdando_execsql_test 5.0 { 145c456a76fSdan CREATE TABLE t1(f1); 146c456a76fSdan CREATE TABLE t2(f2); 147c456a76fSdan CREATE INDEX t2f ON t2(f2); 148c456a76fSdan 149c456a76fSdan INSERT INTO t1 VALUES(-1); 150c456a76fSdan INSERT INTO t1 VALUES(-1); 151c456a76fSdan INSERT INTO t1 VALUES(-1); 152c456a76fSdan INSERT INTO t1 VALUES(-1); 153c456a76fSdan 154c456a76fSdan WITH w(i) AS ( 155c456a76fSdan SELECT 1 UNION ALL SELECT i+1 FROM w WHERE i<1000 156c456a76fSdan ) 157c456a76fSdan INSERT INTO t2 SELECT -1 FROM w; 158c456a76fSdan} 159c456a76fSdan 160c456a76fSdando_execsql_test 5.1 { 161c456a76fSdan SELECT count(*) FROM t1, t2 WHERE t2.rowid = +t1.rowid 162c456a76fSdan} {4} 163c456a76fSdando_test 5.2 { expr [db status vmstep]<200 } 1 164c456a76fSdan 165c456a76fSdando_execsql_test 5.3 { 166c456a76fSdan SELECT count(*) FROM t1, t2 WHERE ( 167c456a76fSdan t2.rowid = +t1.rowid OR t2.f2 = t1.f1 168c456a76fSdan ) 169c456a76fSdan} {4000} 170c456a76fSdando_test 5.4 { expr [db status vmstep]>1000 } 1 171c456a76fSdan 172c456a76fSdando_execsql_test 5.5 { 173c456a76fSdan SELECT count(*) FROM t1, t2 WHERE ( 174c456a76fSdan t2.rowid = +t1.rowid OR (t2.f2 = t1.f1 AND t1.f1!=-1) 175c456a76fSdan ) 176c456a76fSdan} {4} 177c456a76fSdando_test 5.6 { expr [db status vmstep]<200 } 1 178c456a76fSdan 1797e6f980bSdrh# 2017-09-04 ticket b899b6042f97f52d 1807e6f980bSdrh# Segfault on correlated subquery... 1817e6f980bSdrh# 182e1b972bdSdanifcapable json1&&vtab { 1837e6f980bSdrh do_execsql_test 6.1 { 1847e6f980bSdrh CREATE TABLE t6(x); 1857e6f980bSdrh SELECT * FROM t6 WHERE 1 IN (SELECT value FROM json_each(x)); 1867e6f980bSdrh } {} 18768262d85Sdrh 18868262d85Sdrh do_execsql_test 6.2 { 18968262d85Sdrh DROP TABLE t6; 19068262d85Sdrh CREATE TABLE t6(a,b,c); 19168262d85Sdrh INSERT INTO t6 VALUES 19268262d85Sdrh (0,null,'{"a":0,"b":[3,4,5],"c":{"x":4.5,"y":7.8}}'), 19368262d85Sdrh (1,null,'{"a":1,"b":[3,4,5],"c":{"x":4.5,"y":7.8}}'), 19468262d85Sdrh (2,null,'{"a":9,"b":[3,4,5],"c":{"x":4.5,"y":7.8}}'); 19568262d85Sdrh SELECT * FROM t6 19668262d85Sdrh WHERE (EXISTS (SELECT 1 FROM json_each(t6.c) AS x WHERE x.value=1)); 19768262d85Sdrh } {1 {} {{"a":1,"b":[3,4,5],"c":{"x":4.5,"y":7.8}}}} 19868262d85Sdrh 199f3b2c7aaSdrh # Another test case derived from a posting by Wout Mertens on the 200f3b2c7aaSdrh # sqlite-users mailing list on 2017-10-04. 201f3b2c7aaSdrh do_execsql_test 6.3 { 202f3b2c7aaSdrh DROP TABLE IF EXISTS t; 203f3b2c7aaSdrh CREATE TABLE t(json JSON); 204f3b2c7aaSdrh SELECT * FROM t 205f3b2c7aaSdrh WHERE(EXISTS(SELECT 1 FROM json_each(t.json,"$.foo") j 206f3b2c7aaSdrh WHERE j.value = 'meep')); 207f3b2c7aaSdrh } {} 208f3b2c7aaSdrh do_execsql_test 6.4 { 209f3b2c7aaSdrh INSERT INTO t VALUES('{"xyzzy":null}'); 210f3b2c7aaSdrh INSERT INTO t VALUES('{"foo":"meep","other":12345}'); 211f3b2c7aaSdrh INSERT INTO t VALUES('{"foo":"bingo","alt":5.25}'); 212f3b2c7aaSdrh SELECT * FROM t 213f3b2c7aaSdrh WHERE(EXISTS(SELECT 1 FROM json_each(t.json,"$.foo") j 214f3b2c7aaSdrh WHERE j.value = 'meep')); 215f3b2c7aaSdrh } {{{"foo":"meep","other":12345}}} 2167e6f980bSdrh} 2177e6f980bSdrh 21833f10207Sdrh# 2018-01-27 21933f10207Sdrh# Ticket https://sqlite.org/src/tktview/ec32177c99ccac2b180fd3ea2083 22033f10207Sdrh# Incorrect result when using the new OR clause factoring optimization 22133f10207Sdrh# 22233f10207Sdrh# This is the original test case as reported on the sqlite-users mailing 22333f10207Sdrh# list 22433f10207Sdrh# 22533f10207Sdrhdo_execsql_test 7.1 { 22633f10207Sdrh DROP TABLE IF EXISTS cd; 22733f10207Sdrh CREATE TABLE cd ( cdid INTEGER PRIMARY KEY NOT NULL, genreid integer ); 22833f10207Sdrh CREATE INDEX cd_idx_genreid ON cd (genreid); 22933f10207Sdrh INSERT INTO cd ( cdid, genreid ) VALUES 23033f10207Sdrh ( 1, 1 ), 23133f10207Sdrh ( 2, NULL ), 23233f10207Sdrh ( 3, NULL ), 23333f10207Sdrh ( 4, NULL ), 23433f10207Sdrh ( 5, NULL ); 23533f10207Sdrh 23633f10207Sdrh SELECT cdid 23733f10207Sdrh FROM cd me 23833f10207Sdrh WHERE 2 > ( 23933f10207Sdrh SELECT COUNT( * ) 24033f10207Sdrh FROM cd rownum__emulation 24133f10207Sdrh WHERE 24233f10207Sdrh ( 24333f10207Sdrh me.genreid IS NOT NULL 24433f10207Sdrh AND 24533f10207Sdrh rownum__emulation.genreid IS NULL 24633f10207Sdrh ) 24733f10207Sdrh OR 24833f10207Sdrh ( 24933f10207Sdrh me.genreid IS NOT NULL 25033f10207Sdrh AND 25133f10207Sdrh rownum__emulation.genreid IS NOT NULL 25233f10207Sdrh AND 25333f10207Sdrh rownum__emulation.genreid < me.genreid 25433f10207Sdrh ) 25533f10207Sdrh OR 25633f10207Sdrh ( 25733f10207Sdrh ( me.genreid = rownum__emulation.genreid OR ( me.genreid IS NULL 25833f10207Sdrh AND rownum__emulation.genreid IS NULL ) ) 25933f10207Sdrh AND 26033f10207Sdrh rownum__emulation.cdid > me.cdid 26133f10207Sdrh ) 26233f10207Sdrh ); 26333f10207Sdrh} {4 5} 26433f10207Sdrh 26533f10207Sdrh# Simplified test cases from the ticket 26633f10207Sdrh# 26733f10207Sdrhdo_execsql_test 7.2 { 26833f10207Sdrh DROP TABLE IF EXISTS t1; 26933f10207Sdrh DROP TABLE IF EXISTS t2; 27033f10207Sdrh CREATE TABLE t1(a INTEGER PRIMARY KEY, b); 27133f10207Sdrh INSERT INTO t1(a,b) VALUES(1,1); 27233f10207Sdrh CREATE TABLE t2(aa INTEGER PRIMARY KEY, bb); 27333f10207Sdrh INSERT INTO t2(aa,bb) VALUES(1,1),(2,NULL),(3,NULL); 27433f10207Sdrh SELECT ( 27533f10207Sdrh SELECT COUNT(*) FROM t2 27633f10207Sdrh WHERE ( t1.b IS NOT NULL AND t2.bb IS NULL ) 27733f10207Sdrh OR ( t2.bb < t1.b ) 27833f10207Sdrh OR ( t1.b IS t2.bb AND t2.aa > t1.a ) 27933f10207Sdrh ) 28033f10207Sdrh FROM t1; 28133f10207Sdrh} {2} 28233f10207Sdrh 283c9f3db33Sdrh# The fix for ticket ec32177c99ccac2b180fd3ea2083 only makes a difference 284c9f3db33Sdrh# in the output when there is a TERM_VNULL entry in the WhereClause array. 285c9f3db33Sdrh# And TERM_VNULL entries are only generated when compiling with 286c9f3db33Sdrh# SQLITE_ENABLE_STAT4. Nevertheless, it is correct that TERM_VIRTUAL terms 287c9f3db33Sdrh# should not participate in the factoring optimization. In all cases other 288c9f3db33Sdrh# than TERM_VNULL, participation is harmless, but it does consume a few 289c9f3db33Sdrh# extra CPU cycles. 290c9f3db33Sdrh# 291c9f3db33Sdrh# The following test verifies that the TERM_VIRTUAL terms resulting from 292c9f3db33Sdrh# a GLOB operator do not appear anywhere in the generated code. This 293c9f3db33Sdrh# confirms that the problem is fixed, even on builds that omit STAT4. 294c9f3db33Sdrh# 295c9f3db33Sdrhdo_execsql_test 7.3 { 296c9f3db33Sdrh DROP TABLE IF EXISTS t1; 297c9f3db33Sdrh DROP TABLE IF EXISTS t2; 298c9f3db33Sdrh CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT); 299c9f3db33Sdrh INSERT INTO t1(a,b) VALUES(1,'abcxyz'); 300c9f3db33Sdrh CREATE TABLE t2(aa INTEGER PRIMARY KEY, bb TEXT); 301c9f3db33Sdrh INSERT INTO t2(aa,bb) VALUES(1,'abc'),(2,'wxyz'),(3,'xyz'); 302c9f3db33Sdrh CREATE INDEX t2bb ON t2(bb); 303c9f3db33Sdrh EXPLAIN SELECT ( 304c9f3db33Sdrh SELECT COUNT(*) FROM t2 305c9f3db33Sdrh WHERE ( t1.b GLOB 'a*z' AND t2.bb='xyz' ) 306c9f3db33Sdrh OR ( t2.bb = t1.b ) 307c9f3db33Sdrh OR ( t2.aa = t1.a ) 308c9f3db33Sdrh ) 309c9f3db33Sdrh FROM t1; 310c9f3db33Sdrh} {~/ (Lt|Ge) /} 31133f10207Sdrh 312782d68a4Sdrhfinish_test 313