xref: /sqlite-3.40.0/test/in6.test (revision 81f5ef05)
1056f5396Sdrh# 2018-06-07
2056f5396Sdrh#
3056f5396Sdrh# The author disclaims copyright to this source code.  In place of
4056f5396Sdrh# a legal notice, here is a blessing:
5056f5396Sdrh#
6056f5396Sdrh#    May you do good and not evil.
7056f5396Sdrh#    May you find forgiveness for yourself and forgive others.
8056f5396Sdrh#    May you share freely, never taking more than you give.
9056f5396Sdrh#
10056f5396Sdrh#***********************************************************************
11056f5396Sdrh#
12056f5396Sdrh# A multi-key index that uses an IN operator on one of the keys other
13056f5396Sdrh# than the left-most key is able to abort the IN-operator loop early
14056f5396Sdrh# if key terms further to the left do not match.
15056f5396Sdrh#
16056f5396Sdrh# Call this the "multikey-IN-operator early-out optimization" or
17056f5396Sdrh# just "IN-early-out" optimization for short.
18056f5396Sdrh#
19056f5396Sdrh
20056f5396Sdrhset testdir [file dirname $argv0]
21056f5396Sdrhsource $testdir/tester.tcl
22056f5396Sdrhset testprefix in6
23056f5396Sdrh
24056f5396Sdrhdo_test in6-1.1 {
25056f5396Sdrh  db eval {
26056f5396Sdrh    CREATE TABLE t1(a,b,c,d);
27056f5396Sdrh    WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)
28056f5396Sdrh      INSERT INTO t1(a,b,c,d)
29056f5396Sdrh        SELECT 100, 200+x/2, 300+x/5, x FROM c;
30056f5396Sdrh    CREATE INDEX t1abc ON t1(a,b,c);
316d6decb8Sdrh    ANALYZE;
326d6decb8Sdrh    UPDATE sqlite_stat1 SET stat='1000000 500000 500 50';
336d6decb8Sdrh    ANALYZE sqlite_master;
34056f5396Sdrh  }
35056f5396Sdrh  set ::sqlite_search_count 0
36056f5396Sdrh  db eval {
37056f5396Sdrh    SELECT d FROM t1
38056f5396Sdrh     WHERE a=99
39056f5396Sdrh       AND b IN (200,205,201,204)
40056f5396Sdrh       AND c IN (304,302,309,308);
41056f5396Sdrh  }
42056f5396Sdrh} {}
43056f5396Sdrhdo_test in6-1.2 {
44056f5396Sdrh  set ::sqlite_search_count
45056f5396Sdrh} {0}  ;# Without the IN-early-out optimization, this value would be 15
46056f5396Sdrh
47056f5396Sdrh# The multikey-IN-operator early-out optimization does not apply
48056f5396Sdrh# when the IN operator is on the left-most column of the index.
49056f5396Sdrh#
50056f5396Sdrhdo_test in6-1.3 {
51056f5396Sdrh  db eval {
52056f5396Sdrh    EXPLAIN
53056f5396Sdrh    SELECT d FROM t1
54056f5396Sdrh      WHERE a IN (98,99,100,101)
55056f5396Sdrh        AND b=200 AND c=300;
56056f5396Sdrh  }
57056f5396Sdrh} {~/(IfNoHope|SeekHit)/}
58056f5396Sdrh
59056f5396Sdrhset sqlite_search_count 0
60056f5396Sdrhdo_execsql_test in6-1.4 {
61056f5396Sdrh SELECT d FROM t1
62056f5396Sdrh  WHERE a=100
63056f5396Sdrh    AND b IN (200,201,202,204)
64056f5396Sdrh    AND c IN (300,302,301,305)
65056f5396Sdrh  ORDER BY +d;
66056f5396Sdrh} {1 2 3 4 5 8 9}
67056f5396Sdrhdo_test in6-1.5 {
68056f5396Sdrh  set ::sqlite_search_count
69056f5396Sdrh} {39}
70056f5396Sdrh
71056f5396Sdrhdo_execsql_test in6-2.1 {
72056f5396Sdrh  CREATE TABLE t2(e INT UNIQUE, f TEXT);
73056f5396Sdrh  SELECT d, f FROM t1 LEFT JOIN t2 ON (e=d)
74056f5396Sdrh  WHERE a=100
75056f5396Sdrh    AND b IN (200,201,202,204)
76056f5396Sdrh    AND c IN (300,302,301,305)
77056f5396Sdrh  ORDER BY +d;
78056f5396Sdrh} {1 {} 2 {} 3 {} 4 {} 5 {} 8 {} 9 {}}
79056f5396Sdrh
8014c98a4fSdrh# 2020-03-16 ticket 82b588d342d515d1
8114c98a4fSdrh# Ensure that the IN-early-out optimization works with LEFT JOINs
8214c98a4fSdrh#
8314c98a4fSdrhreset_db
8414c98a4fSdrhdo_execsql_test in6-3.100 {
8514c98a4fSdrh  CREATE TABLE t1(a);
8614c98a4fSdrh  INSERT INTO t1 VALUES(0);
8714c98a4fSdrh  CREATE TABLE t2(b, c, d);
8814c98a4fSdrh  INSERT INTO t2(b,c,d) VALUES(4,5,3),(4,5,4),(4,5,8);
8914c98a4fSdrh  CREATE INDEX t2bcd ON t2(b, c, d);
9014c98a4fSdrh  SELECT * FROM t1 LEFT JOIN t2 ON b=NULL AND c=5 AND d IN (2,3,4);
9114c98a4fSdrh} {0 {} {} {}}
9214c98a4fSdrhdo_execsql_test in6-3.110 {
9314c98a4fSdrh  CREATE TABLE v0(v1);
9414c98a4fSdrh  CREATE TABLE v3(v5, v4);
9514c98a4fSdrh  INSERT INTO v0 VALUES(0);
9614c98a4fSdrh  CREATE INDEX v9 ON v3(v4, v4, v5);
9714c98a4fSdrh  SELECT quote(v5) FROM v0 LEFT JOIN v3 ON v4 = NULL AND v5 IN(0);
9814c98a4fSdrh} {NULL}
9914c98a4fSdrh
100*81f5ef05Sdrh# 2021-04-29 forum https://sqlite.org/forum/forumpost/6a3ec138e9
101*81f5ef05Sdrh# An early OP_IsNull bypass might skip over the OP_Affinity and
102*81f5ef05Sdrh# cause the OP_IfNoHope to jump on a false-positive, resulting in
103*81f5ef05Sdrh# incomplete output.
104*81f5ef05Sdrh#
105*81f5ef05Sdrhreset_db
106*81f5ef05Sdrhdo_execsql_test in6-3.120 {
107*81f5ef05Sdrh  CREATE TABLE t1(a TEXT, b TEXT);
108*81f5ef05Sdrh  INSERT INTO t1 VALUES(null,10),(0,10),(10,10);
109*81f5ef05Sdrh  CREATE INDEX t1ab ON t1(a,b);
110*81f5ef05Sdrh  SELECT quote(a), quote(b), '|' FROM t1 WHERE b in (SELECT a FROM t1) AND a=0;
111*81f5ef05Sdrh} {'0' '10' |}
112*81f5ef05Sdrhdo_execsql_test in6-3.130 {
113*81f5ef05Sdrh  CREATE TABLE t2(x TEXT);
114*81f5ef05Sdrh  INSERT INTO t2(x) VALUES(NULL),(0),(10);
115*81f5ef05Sdrh  SELECT quote(x), quote(a), quote(b), 'x'
116*81f5ef05Sdrh    FROM t2 LEFT JOIN t1 ON a=x AND b in (null,0,10);
117*81f5ef05Sdrh} {NULL NULL NULL x '0' '0' '10' x '10' '10' '10' x}
118*81f5ef05Sdrh
119056f5396Sdrhfinish_test
120