xref: /sqlite-3.40.0/test/whereF.test (revision 8210233c)
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