OLD | NEW |
| (Empty) |
1 # 2011 July 1 | |
2 # | |
3 # The author disclaims copyright to this source code. In place of | |
4 # a legal notice, here is a blessing: | |
5 # | |
6 # May you do good and not evil. | |
7 # May you find forgiveness for yourself and forgive others. | |
8 # May you share freely, never taking more than you give. | |
9 # | |
10 #*********************************************************************** | |
11 # This file implements regression tests for SQLite library. The | |
12 # focus of this script is the DISTINCT modifier. | |
13 # | |
14 | |
15 set testdir [file dirname $argv0] | |
16 source $testdir/tester.tcl | |
17 | |
18 ifcapable !compound { | |
19 finish_test | |
20 return | |
21 } | |
22 | |
23 set testprefix distinct | |
24 | |
25 | |
26 proc is_distinct_noop {sql} { | |
27 set sql1 $sql | |
28 set sql2 [string map {DISTINCT ""} $sql] | |
29 | |
30 set program1 [list] | |
31 set program2 [list] | |
32 db eval "EXPLAIN $sql1" { | |
33 if {$opcode != "Noop"} { lappend program1 $opcode } | |
34 } | |
35 db eval "EXPLAIN $sql2" { | |
36 if {$opcode != "Noop"} { lappend program2 $opcode } | |
37 } | |
38 | |
39 return [expr {$program1==$program2}] | |
40 } | |
41 | |
42 proc do_distinct_noop_test {tn sql} { | |
43 uplevel [list do_test $tn [list is_distinct_noop $sql] 1] | |
44 } | |
45 proc do_distinct_not_noop_test {tn sql} { | |
46 uplevel [list do_test $tn [list is_distinct_noop $sql] 0] | |
47 } | |
48 | |
49 proc do_temptables_test {tn sql temptables} { | |
50 uplevel [list do_test $tn [subst -novar { | |
51 set ret "" | |
52 db eval "EXPLAIN [set sql]" { | |
53 if {$opcode == "OpenEphemeral" || $opcode == "SorterOpen"} { | |
54 if {$p5 != "08" && $p5!="00"} { error "p5 = $p5" } | |
55 if {$p5 == "08"} { | |
56 lappend ret hash | |
57 } else { | |
58 lappend ret btree | |
59 } | |
60 } | |
61 } | |
62 set ret | |
63 }] $temptables] | |
64 } | |
65 | |
66 | |
67 #------------------------------------------------------------------------- | |
68 # The following tests - distinct-1.* - check that the planner correctly | |
69 # detects cases where a UNIQUE index means that a DISTINCT clause is | |
70 # redundant. Currently the planner only detects such cases when there | |
71 # is a single table in the FROM clause. | |
72 # | |
73 do_execsql_test 1.0 { | |
74 CREATE TABLE t1(a, b, c, d); | |
75 CREATE UNIQUE INDEX i1 ON t1(b, c); | |
76 CREATE UNIQUE INDEX i2 ON t1(d COLLATE nocase); | |
77 | |
78 CREATE TABLE t2(x INTEGER PRIMARY KEY, y); | |
79 | |
80 CREATE TABLE t3(c1 PRIMARY KEY NOT NULL, c2 NOT NULL); | |
81 CREATE INDEX i3 ON t3(c2); | |
82 | |
83 CREATE TABLE t4(a, b NOT NULL, c NOT NULL, d NOT NULL); | |
84 CREATE UNIQUE INDEX t4i1 ON t4(b, c); | |
85 CREATE UNIQUE INDEX t4i2 ON t4(d COLLATE nocase); | |
86 } | |
87 foreach {tn noop sql} { | |
88 | |
89 1.1 0 "SELECT DISTINCT b, c FROM t1" | |
90 1.2 1 "SELECT DISTINCT b, c FROM t4" | |
91 2.1 0 "SELECT DISTINCT c FROM t1 WHERE b = ?" | |
92 2.2 1 "SELECT DISTINCT c FROM t4 WHERE b = ?" | |
93 3 1 "SELECT DISTINCT rowid FROM t1" | |
94 4 1 "SELECT DISTINCT rowid, a FROM t1" | |
95 5 1 "SELECT DISTINCT x FROM t2" | |
96 6 1 "SELECT DISTINCT * FROM t2" | |
97 7 1 "SELECT DISTINCT * FROM (SELECT * FROM t2)" | |
98 | |
99 8.1 0 "SELECT DISTINCT * FROM t1" | |
100 8.2 1 "SELECT DISTINCT * FROM t4" | |
101 | |
102 8 0 "SELECT DISTINCT a, b FROM t1" | |
103 | |
104 9 0 "SELECT DISTINCT c FROM t1 WHERE b IN (1,2)" | |
105 10 0 "SELECT DISTINCT c FROM t1" | |
106 11 0 "SELECT DISTINCT b FROM t1" | |
107 | |
108 12.1 0 "SELECT DISTINCT a, d FROM t1" | |
109 12.2 0 "SELECT DISTINCT a, d FROM t4" | |
110 13.1 0 "SELECT DISTINCT a, b, c COLLATE nocase FROM t1" | |
111 13.2 0 "SELECT DISTINCT a, b, c COLLATE nocase FROM t4" | |
112 14.1 0 "SELECT DISTINCT a, d COLLATE nocase FROM t1" | |
113 14.2 1 "SELECT DISTINCT a, d COLLATE nocase FROM t4" | |
114 | |
115 15 0 "SELECT DISTINCT a, d COLLATE binary FROM t1" | |
116 16.1 0 "SELECT DISTINCT a, b, c COLLATE binary FROM t1" | |
117 16.2 1 "SELECT DISTINCT a, b, c COLLATE binary FROM t4" | |
118 | |
119 16 0 "SELECT DISTINCT t1.rowid FROM t1, t2" | |
120 17 0 { /* Technically, it would be possible to detect that DISTINCT | |
121 ** is a no-op in cases like the following. But SQLite does not | |
122 ** do so. */ | |
123 SELECT DISTINCT t1.rowid FROM t1, t2 WHERE t1.rowid=t2.rowid } | |
124 | |
125 18 1 "SELECT DISTINCT c1, c2 FROM t3" | |
126 19 1 "SELECT DISTINCT c1 FROM t3" | |
127 20 1 "SELECT DISTINCT * FROM t3" | |
128 21 0 "SELECT DISTINCT c2 FROM t3" | |
129 | |
130 22 0 "SELECT DISTINCT * FROM (SELECT 1, 2, 3 UNION SELECT 4, 5, 6)" | |
131 23 1 "SELECT DISTINCT rowid FROM (SELECT 1, 2, 3 UNION SELECT 4, 5, 6)" | |
132 | |
133 24 0 "SELECT DISTINCT rowid/2 FROM t1" | |
134 25 1 "SELECT DISTINCT rowid/2, rowid FROM t1" | |
135 26.1 0 "SELECT DISTINCT rowid/2, b FROM t1 WHERE c = ?" | |
136 26.2 1 "SELECT DISTINCT rowid/2, b FROM t4 WHERE c = ?" | |
137 } { | |
138 if {$noop} { | |
139 do_distinct_noop_test 1.$tn $sql | |
140 } else { | |
141 do_distinct_not_noop_test 1.$tn $sql | |
142 } | |
143 } | |
144 | |
145 #------------------------------------------------------------------------- | |
146 # The following tests - distinct-2.* - test cases where an index is | |
147 # used to deliver results in order of the DISTINCT expressions. | |
148 # | |
149 drop_all_tables | |
150 do_execsql_test 2.0 { | |
151 CREATE TABLE t1(a, b, c); | |
152 | |
153 CREATE INDEX i1 ON t1(a, b); | |
154 CREATE INDEX i2 ON t1(b COLLATE nocase, c COLLATE nocase); | |
155 | |
156 INSERT INTO t1 VALUES('a', 'b', 'c'); | |
157 INSERT INTO t1 VALUES('A', 'B', 'C'); | |
158 INSERT INTO t1 VALUES('a', 'b', 'c'); | |
159 INSERT INTO t1 VALUES('A', 'B', 'C'); | |
160 } | |
161 | |
162 foreach {tn sql temptables res} { | |
163 1 "a, b FROM t1" {} {A B a b} | |
164 2 "b, a FROM t1" {} {B A b a} | |
165 3 "a, b, c FROM t1" {hash} {A B C a b c} | |
166 4 "a, b, c FROM t1 ORDER BY a, b, c" {btree} {A B C a b c} | |
167 5 "b FROM t1 WHERE a = 'a'" {} {b} | |
168 6 "b FROM t1 ORDER BY +b COLLATE binary" {btree hash} {B b} | |
169 7 "a FROM t1" {} {A a} | |
170 8 "b COLLATE nocase FROM t1" {} {b} | |
171 9 "b COLLATE nocase FROM t1 ORDER BY b COLLATE nocase" {} {b} | |
172 } { | |
173 do_execsql_test 2.$tn.1 "SELECT DISTINCT $sql" $res | |
174 do_temptables_test 2.$tn.2 "SELECT DISTINCT $sql" $temptables | |
175 } | |
176 | |
177 do_execsql_test 2.A { | |
178 SELECT (SELECT DISTINCT o.a FROM t1 AS i) FROM t1 AS o ORDER BY rowid; | |
179 } {a A a A} | |
180 | |
181 do_test 3.0 { | |
182 db eval { | |
183 CREATE TABLE t3(a INTEGER, b INTEGER, c, UNIQUE(a,b)); | |
184 INSERT INTO t3 VALUES | |
185 (null, null, 1), | |
186 (null, null, 2), | |
187 (null, 3, 4), | |
188 (null, 3, 5), | |
189 (6, null, 7), | |
190 (6, null, 8); | |
191 SELECT DISTINCT a, b FROM t3 ORDER BY +a, +b; | |
192 } | |
193 } {{} {} {} 3 6 {}} | |
194 do_test 3.1 { | |
195 regexp {OpenEphemeral} [db eval { | |
196 EXPLAIN SELECT DISTINCT a, b FROM t3 ORDER BY +a, +b; | |
197 }] | |
198 } {0} | |
199 | |
200 #------------------------------------------------------------------------- | |
201 # Ticket [fccbde530a6583bf2748400919f1603d5425995c] (2014-01-08) | |
202 # The logic that computes DISTINCT sometimes thinks that a zeroblob() | |
203 # and a blob of all zeros are different when they should be the same. | |
204 # | |
205 do_execsql_test 4.1 { | |
206 DROP TABLE IF EXISTS t1; | |
207 DROP TABLE IF EXISTS t2; | |
208 CREATE TABLE t1(a INTEGER); | |
209 INSERT INTO t1 VALUES(3); | |
210 INSERT INTO t1 VALUES(2); | |
211 INSERT INTO t1 VALUES(1); | |
212 INSERT INTO t1 VALUES(2); | |
213 INSERT INTO t1 VALUES(3); | |
214 INSERT INTO t1 VALUES(1); | |
215 CREATE TABLE t2(x); | |
216 INSERT INTO t2 | |
217 SELECT DISTINCT | |
218 CASE a WHEN 1 THEN x'0000000000' | |
219 WHEN 2 THEN zeroblob(5) | |
220 ELSE 'xyzzy' END | |
221 FROM t1; | |
222 SELECT quote(x) FROM t2 ORDER BY 1; | |
223 } {'xyzzy' X'0000000000'} | |
224 | |
225 finish_test | |
OLD | NEW |