150118cdfSdan# 2011 July 1 250118cdfSdan# 350118cdfSdan# The author disclaims copyright to this source code. In place of 450118cdfSdan# a legal notice, here is a blessing: 550118cdfSdan# 650118cdfSdan# May you do good and not evil. 750118cdfSdan# May you find forgiveness for yourself and forgive others. 850118cdfSdan# May you share freely, never taking more than you give. 950118cdfSdan# 1050118cdfSdan#*********************************************************************** 1150118cdfSdan# This file implements regression tests for SQLite library. The 1250118cdfSdan# focus of this script is the DISTINCT modifier. 1350118cdfSdan# 1450118cdfSdan 1550118cdfSdanset testdir [file dirname $argv0] 1650118cdfSdansource $testdir/tester.tcl 1750118cdfSdan 182f56da3fSdanifcapable !compound { 192f56da3fSdan finish_test 202f56da3fSdan return 212f56da3fSdan} 222f56da3fSdan 2350118cdfSdanset testprefix distinct 2450118cdfSdan 2550118cdfSdan 2650118cdfSdanproc is_distinct_noop {sql} { 2750118cdfSdan set sql1 $sql 2850118cdfSdan set sql2 [string map {DISTINCT ""} $sql] 2950118cdfSdan 3050118cdfSdan set program1 [list] 3150118cdfSdan set program2 [list] 3250118cdfSdan db eval "EXPLAIN $sql1" { 339be1339fSdrh if {$opcode != "Noop" && $opcode != "Explain"} { lappend program1 $opcode } 3450118cdfSdan } 3550118cdfSdan db eval "EXPLAIN $sql2" { 369be1339fSdrh if {$opcode != "Noop" && $opcode != "Explain"} { lappend program2 $opcode } 3750118cdfSdan } 3850118cdfSdan return [expr {$program1==$program2}] 3950118cdfSdan} 4050118cdfSdan 4150118cdfSdanproc do_distinct_noop_test {tn sql} { 4250118cdfSdan uplevel [list do_test $tn [list is_distinct_noop $sql] 1] 4350118cdfSdan} 4450118cdfSdanproc do_distinct_not_noop_test {tn sql} { 4550118cdfSdan uplevel [list do_test $tn [list is_distinct_noop $sql] 0] 4650118cdfSdan} 4750118cdfSdan 486f343969Sdanproc do_temptables_test {tn sql temptables} { 496f343969Sdan uplevel [list do_test $tn [subst -novar { 506f343969Sdan set ret "" 516f343969Sdan db eval "EXPLAIN [set sql]" { 521c9d835dSdrh if {$opcode == "OpenEphemeral" || $opcode == "SorterOpen"} { 53cb49f546Sdrh if {$p5!=8 && $p5!=0} { error "p5 = $p5" } 54cb49f546Sdrh if {$p5==8} { 556f343969Sdan lappend ret hash 566f343969Sdan } else { 576f343969Sdan lappend ret btree 586f343969Sdan } 596f343969Sdan } 606f343969Sdan } 616f343969Sdan set ret 626f343969Sdan }] $temptables] 636f343969Sdan} 646f343969Sdan 6550118cdfSdan 6650118cdfSdan#------------------------------------------------------------------------- 6750118cdfSdan# The following tests - distinct-1.* - check that the planner correctly 6850118cdfSdan# detects cases where a UNIQUE index means that a DISTINCT clause is 6950118cdfSdan# redundant. Currently the planner only detects such cases when there 7050118cdfSdan# is a single table in the FROM clause. 7150118cdfSdan# 7250118cdfSdando_execsql_test 1.0 { 7350118cdfSdan CREATE TABLE t1(a, b, c, d); 7450118cdfSdan CREATE UNIQUE INDEX i1 ON t1(b, c); 7550118cdfSdan CREATE UNIQUE INDEX i2 ON t1(d COLLATE nocase); 7650118cdfSdan 7750118cdfSdan CREATE TABLE t2(x INTEGER PRIMARY KEY, y); 7850118cdfSdan 796a36f435Sdan CREATE TABLE t3(c1 PRIMARY KEY NOT NULL, c2 NOT NULL); 8050118cdfSdan CREATE INDEX i3 ON t3(c2); 816a36f435Sdan 826a36f435Sdan CREATE TABLE t4(a, b NOT NULL, c NOT NULL, d NOT NULL); 836a36f435Sdan CREATE UNIQUE INDEX t4i1 ON t4(b, c); 846a36f435Sdan CREATE UNIQUE INDEX t4i2 ON t4(d COLLATE nocase); 8550118cdfSdan} 8650118cdfSdanforeach {tn noop sql} { 8750118cdfSdan 886a36f435Sdan 1.1 0 "SELECT DISTINCT b, c FROM t1" 896a36f435Sdan 1.2 1 "SELECT DISTINCT b, c FROM t4" 906a36f435Sdan 2.1 0 "SELECT DISTINCT c FROM t1 WHERE b = ?" 916a36f435Sdan 2.2 1 "SELECT DISTINCT c FROM t4 WHERE b = ?" 9250118cdfSdan 3 1 "SELECT DISTINCT rowid FROM t1" 9350118cdfSdan 4 1 "SELECT DISTINCT rowid, a FROM t1" 9450118cdfSdan 5 1 "SELECT DISTINCT x FROM t2" 9550118cdfSdan 6 1 "SELECT DISTINCT * FROM t2" 9650118cdfSdan 7 1 "SELECT DISTINCT * FROM (SELECT * FROM t2)" 9750118cdfSdan 986a36f435Sdan 8.1 0 "SELECT DISTINCT * FROM t1" 996a36f435Sdan 8.2 1 "SELECT DISTINCT * FROM t4" 10050118cdfSdan 10150118cdfSdan 8 0 "SELECT DISTINCT a, b FROM t1" 10250118cdfSdan 10350118cdfSdan 9 0 "SELECT DISTINCT c FROM t1 WHERE b IN (1,2)" 10450118cdfSdan 10 0 "SELECT DISTINCT c FROM t1" 10550118cdfSdan 11 0 "SELECT DISTINCT b FROM t1" 10650118cdfSdan 1076a36f435Sdan 12.1 0 "SELECT DISTINCT a, d FROM t1" 1086a36f435Sdan 12.2 0 "SELECT DISTINCT a, d FROM t4" 1096a36f435Sdan 13.1 0 "SELECT DISTINCT a, b, c COLLATE nocase FROM t1" 1106a36f435Sdan 13.2 0 "SELECT DISTINCT a, b, c COLLATE nocase FROM t4" 1116a36f435Sdan 14.1 0 "SELECT DISTINCT a, d COLLATE nocase FROM t1" 1126a36f435Sdan 14.2 1 "SELECT DISTINCT a, d COLLATE nocase FROM t4" 1136a36f435Sdan 11450118cdfSdan 15 0 "SELECT DISTINCT a, d COLLATE binary FROM t1" 1156a36f435Sdan 16.1 0 "SELECT DISTINCT a, b, c COLLATE binary FROM t1" 1166a36f435Sdan 16.2 1 "SELECT DISTINCT a, b, c COLLATE binary FROM t4" 11750118cdfSdan 11850118cdfSdan 16 0 "SELECT DISTINCT t1.rowid FROM t1, t2" 11950118cdfSdan 17 0 { /* Technically, it would be possible to detect that DISTINCT 12050118cdfSdan ** is a no-op in cases like the following. But SQLite does not 12150118cdfSdan ** do so. */ 12250118cdfSdan SELECT DISTINCT t1.rowid FROM t1, t2 WHERE t1.rowid=t2.rowid } 12350118cdfSdan 12450118cdfSdan 18 1 "SELECT DISTINCT c1, c2 FROM t3" 12550118cdfSdan 19 1 "SELECT DISTINCT c1 FROM t3" 12650118cdfSdan 20 1 "SELECT DISTINCT * FROM t3" 12750118cdfSdan 21 0 "SELECT DISTINCT c2 FROM t3" 12850118cdfSdan 12950118cdfSdan 22 0 "SELECT DISTINCT * FROM (SELECT 1, 2, 3 UNION SELECT 4, 5, 6)" 13050118cdfSdan 13150118cdfSdan 24 0 "SELECT DISTINCT rowid/2 FROM t1" 13250118cdfSdan 25 1 "SELECT DISTINCT rowid/2, rowid FROM t1" 1336a36f435Sdan 26.1 0 "SELECT DISTINCT rowid/2, b FROM t1 WHERE c = ?" 1346a36f435Sdan 26.2 1 "SELECT DISTINCT rowid/2, b FROM t4 WHERE c = ?" 13550118cdfSdan} { 13650118cdfSdan if {$noop} { 13750118cdfSdan do_distinct_noop_test 1.$tn $sql 13850118cdfSdan } else { 13950118cdfSdan do_distinct_not_noop_test 1.$tn $sql 14050118cdfSdan } 14150118cdfSdan} 14250118cdfSdan 1436f343969Sdan#------------------------------------------------------------------------- 1446f343969Sdan# The following tests - distinct-2.* - test cases where an index is 1456f343969Sdan# used to deliver results in order of the DISTINCT expressions. 1466f343969Sdan# 1476f343969Sdandrop_all_tables 1486f343969Sdando_execsql_test 2.0 { 1496f343969Sdan CREATE TABLE t1(a, b, c); 1506f343969Sdan 1516f343969Sdan CREATE INDEX i1 ON t1(a, b); 1526f343969Sdan CREATE INDEX i2 ON t1(b COLLATE nocase, c COLLATE nocase); 1536f343969Sdan 1546f343969Sdan INSERT INTO t1 VALUES('a', 'b', 'c'); 1556f343969Sdan INSERT INTO t1 VALUES('A', 'B', 'C'); 1566f343969Sdan INSERT INTO t1 VALUES('a', 'b', 'c'); 1576f343969Sdan INSERT INTO t1 VALUES('A', 'B', 'C'); 1586f343969Sdan} 1596f343969Sdan 1606f343969Sdanforeach {tn sql temptables res} { 1616f343969Sdan 1 "a, b FROM t1" {} {A B a b} 1626f343969Sdan 2 "b, a FROM t1" {} {B A b a} 1636284db90Sdrh 3 "a, b, c FROM t1" {hash} {A B C a b c} 1646f343969Sdan 4 "a, b, c FROM t1 ORDER BY a, b, c" {btree} {A B C a b c} 1656f343969Sdan 5 "b FROM t1 WHERE a = 'a'" {} {b} 1664fe425adSdrh 6 "b FROM t1 ORDER BY +b COLLATE binary" {btree hash} {B b} 1676f343969Sdan 7 "a FROM t1" {} {A a} 1686f343969Sdan 8 "b COLLATE nocase FROM t1" {} {b} 1694e8b9925Sdrh 9 "b COLLATE nocase FROM t1 ORDER BY b COLLATE nocase" {} {b} 1706f343969Sdan} { 1716f343969Sdan do_execsql_test 2.$tn.1 "SELECT DISTINCT $sql" $res 1726f343969Sdan do_temptables_test 2.$tn.2 "SELECT DISTINCT $sql" $temptables 1736f343969Sdan} 17450118cdfSdan 17594e08d92Sdando_execsql_test 2.A { 1763f4d1d1bSdrh SELECT (SELECT DISTINCT o.a FROM t1 AS i) FROM t1 AS o ORDER BY rowid; 17794e08d92Sdan} {a A a A} 17850118cdfSdan 179053a128fSdrhdo_test 3.0 { 180053a128fSdrh db eval { 181053a128fSdrh CREATE TABLE t3(a INTEGER, b INTEGER, c, UNIQUE(a,b)); 182053a128fSdrh INSERT INTO t3 VALUES 183053a128fSdrh (null, null, 1), 184053a128fSdrh (null, null, 2), 185053a128fSdrh (null, 3, 4), 186053a128fSdrh (null, 3, 5), 187053a128fSdrh (6, null, 7), 188053a128fSdrh (6, null, 8); 189053a128fSdrh SELECT DISTINCT a, b FROM t3 ORDER BY +a, +b; 190053a128fSdrh } 191053a128fSdrh} {{} {} {} 3 6 {}} 192053a128fSdrhdo_test 3.1 { 193053a128fSdrh regexp {OpenEphemeral} [db eval { 194053a128fSdrh EXPLAIN SELECT DISTINCT a, b FROM t3 ORDER BY +a, +b; 195053a128fSdrh }] 196053a128fSdrh} {0} 19750118cdfSdan 198826af378Sdrh#------------------------------------------------------------------------- 199826af378Sdrh# Ticket [fccbde530a6583bf2748400919f1603d5425995c] (2014-01-08) 200826af378Sdrh# The logic that computes DISTINCT sometimes thinks that a zeroblob() 201826af378Sdrh# and a blob of all zeros are different when they should be the same. 202826af378Sdrh# 203826af378Sdrhdo_execsql_test 4.1 { 204826af378Sdrh DROP TABLE IF EXISTS t1; 205826af378Sdrh DROP TABLE IF EXISTS t2; 206826af378Sdrh CREATE TABLE t1(a INTEGER); 207826af378Sdrh INSERT INTO t1 VALUES(3); 208826af378Sdrh INSERT INTO t1 VALUES(2); 209826af378Sdrh INSERT INTO t1 VALUES(1); 210826af378Sdrh INSERT INTO t1 VALUES(2); 211826af378Sdrh INSERT INTO t1 VALUES(3); 212826af378Sdrh INSERT INTO t1 VALUES(1); 213826af378Sdrh CREATE TABLE t2(x); 214826af378Sdrh INSERT INTO t2 215826af378Sdrh SELECT DISTINCT 216826af378Sdrh CASE a WHEN 1 THEN x'0000000000' 217826af378Sdrh WHEN 2 THEN zeroblob(5) 218826af378Sdrh ELSE 'xyzzy' END 219826af378Sdrh FROM t1; 220826af378Sdrh SELECT quote(x) FROM t2 ORDER BY 1; 221826af378Sdrh} {'xyzzy' X'0000000000'} 222826af378Sdrh 223dea7d70dSdrh#---------------------------------------------------------------------------- 224dea7d70dSdrh# Ticket [c5ea805691bfc4204b1cb9e9aa0103bd48bc7d34] (2014-12-04) 225dea7d70dSdrh# Make sure that DISTINCT works together with ORDER BY and descending 226dea7d70dSdrh# indexes. 227dea7d70dSdrh# 228dea7d70dSdrhdo_execsql_test 5.1 { 229dea7d70dSdrh DROP TABLE IF EXISTS t1; 230dea7d70dSdrh CREATE TABLE t1(x); 231dea7d70dSdrh INSERT INTO t1(x) VALUES(3),(1),(5),(2),(6),(4),(5),(1),(3); 232dea7d70dSdrh CREATE INDEX t1x ON t1(x DESC); 233dea7d70dSdrh SELECT DISTINCT x FROM t1 ORDER BY x ASC; 234dea7d70dSdrh} {1 2 3 4 5 6} 235dea7d70dSdrhdo_execsql_test 5.2 { 236dea7d70dSdrh SELECT DISTINCT x FROM t1 ORDER BY x DESC; 237dea7d70dSdrh} {6 5 4 3 2 1} 238dea7d70dSdrhdo_execsql_test 5.3 { 239dea7d70dSdrh SELECT DISTINCT x FROM t1 ORDER BY x; 240dea7d70dSdrh} {1 2 3 4 5 6} 241dea7d70dSdrhdo_execsql_test 5.4 { 242dea7d70dSdrh DROP INDEX t1x; 243dea7d70dSdrh CREATE INDEX t1x ON t1(x ASC); 244dea7d70dSdrh SELECT DISTINCT x FROM t1 ORDER BY x ASC; 245dea7d70dSdrh} {1 2 3 4 5 6} 246dea7d70dSdrhdo_execsql_test 5.5 { 247dea7d70dSdrh SELECT DISTINCT x FROM t1 ORDER BY x DESC; 248dea7d70dSdrh} {6 5 4 3 2 1} 249dea7d70dSdrhdo_execsql_test 5.6 { 250dea7d70dSdrh SELECT DISTINCT x FROM t1 ORDER BY x; 251dea7d70dSdrh} {1 2 3 4 5 6} 252dea7d70dSdrh 2532edc5fd7Sdrh#------------------------------------------------------------------------- 2542edc5fd7Sdrh# 2015-11-23. Problem discovered by Kostya Serebryany using libFuzzer 2552edc5fd7Sdrh# 2562edc5fd7Sdrhdb close 2572edc5fd7Sdrhsqlite3 db :memory: 2582edc5fd7Sdrhdo_execsql_test 6.1 { 2592edc5fd7Sdrh CREATE TABLE jjj(x); 2602edc5fd7Sdrh SELECT (SELECT 'mmm' UNION SELECT DISTINCT max(name) ORDER BY 1) 2612edc5fd7Sdrh FROM sqlite_master; 2622edc5fd7Sdrh} {jjj} 2632edc5fd7Sdrhdo_execsql_test 6.2 { 2642edc5fd7Sdrh CREATE TABLE nnn(x); 2652edc5fd7Sdrh SELECT (SELECT 'mmm' UNION SELECT DISTINCT max(name) ORDER BY 1) 2662edc5fd7Sdrh FROM sqlite_master; 2672edc5fd7Sdrh} {mmm} 2682edc5fd7Sdrh 2699e10f9abSdan#------------------------------------------------------------------------- 2709e10f9abSdan# Ticket [9c944882] 2719e10f9abSdan# 2729e10f9abSdanreset_db 2739e10f9abSdando_execsql_test 7.0 { 2749e10f9abSdan CREATE TABLE t1(a INTEGER PRIMARY KEY); 2759e10f9abSdan CREATE TABLE t3(a INTEGER PRIMARY KEY); 2769e10f9abSdan 2779e10f9abSdan CREATE TABLE t4(x); 2789e10f9abSdan CREATE TABLE t5(y); 2799e10f9abSdan 2809e10f9abSdan INSERT INTO t5 VALUES(1), (2), (2); 2819e10f9abSdan INSERT INTO t1 VALUES(2); 2829e10f9abSdan INSERT INTO t3 VALUES(2); 2839e10f9abSdan INSERT INTO t4 VALUES(2); 2849e10f9abSdan} 2859e10f9abSdan 2869e10f9abSdando_execsql_test 7.1 { 2879e10f9abSdan WITH t2(b) AS ( 2889e10f9abSdan SELECT DISTINCT y FROM t5 ORDER BY y 2899e10f9abSdan ) 2909e10f9abSdan SELECT * FROM 2919e10f9abSdan t4 CROSS JOIN t3 CROSS JOIN t1 2929e10f9abSdan WHERE (t1.a=t3.a) AND (SELECT count(*) FROM t2 AS y WHERE t4.x!='abc')=t1.a 2939e10f9abSdan} {2 2 2} 2942edc5fd7Sdrh 295204b6342Sdrh# 2021-04-06 forum post https://sqlite.org/forum/forumpost/66954e9ece 296204b6342Sdrhreset_db 297204b6342Sdrhdo_execsql_test 8.0 { 298204b6342Sdrh CREATE TABLE person ( pid INT) ; 299204b6342Sdrh CREATE UNIQUE INDEX idx ON person ( pid ) WHERE pid == 1; 300204b6342Sdrh INSERT INTO person VALUES (1), (10), (10); 301204b6342Sdrh SELECT DISTINCT pid FROM person where pid = 10; 302204b6342Sdrh} {10} 303204b6342Sdrh 304*247d0946Sdan#------------------------------------------------------------------------- 305*247d0946Sdanreset_db 306*247d0946Sdando_execsql_test 9.0 { 307*247d0946Sdan CREATE TABLE t1(a, b); 308*247d0946Sdan INSERT INTO t1 VALUES('a', 'a'); 309*247d0946Sdan INSERT INTO t1 VALUES('a', 'b'); 310*247d0946Sdan INSERT INTO t1 VALUES('a', 'c'); 311*247d0946Sdan 312*247d0946Sdan INSERT INTO t1 VALUES('b', 'a'); 313*247d0946Sdan INSERT INTO t1 VALUES('b', 'b'); 314*247d0946Sdan INSERT INTO t1 VALUES('b', 'c'); 315*247d0946Sdan 316*247d0946Sdan INSERT INTO t1 VALUES('a', 'a'); 317*247d0946Sdan INSERT INTO t1 VALUES('b', 'b'); 318*247d0946Sdan 319*247d0946Sdan INSERT INTO t1 VALUES('A', 'A'); 320*247d0946Sdan INSERT INTO t1 VALUES('B', 'B'); 321*247d0946Sdan} 322*247d0946Sdan 323*247d0946Sdanforeach {tn idx} { 324*247d0946Sdan 1 { } 325*247d0946Sdan 2 { CREATE INDEX i1 ON t1(a, b); } 326*247d0946Sdan 3 { CREATE INDEX i1 ON t1(b, a); } 327*247d0946Sdan 4 { CREATE INDEX i1 ON t1(a COLLATE nocase, b COLLATE nocase); } 328*247d0946Sdan 5 { CREATE INDEX i1 ON t1(b COLLATE nocase, a COLLATE nocase); } 329*247d0946Sdan} { 330*247d0946Sdan 331*247d0946Sdan execsql { DROP INDEX IF EXISTS i1 } 332*247d0946Sdan execsql $idx 333*247d0946Sdan 334*247d0946Sdan do_execsql_test 9.$tn.1 { 335*247d0946Sdan SELECT DISTINCT a, b FROM t1 ORDER BY a, b 336*247d0946Sdan } { 337*247d0946Sdan A A B B 338*247d0946Sdan a a a b a c 339*247d0946Sdan b a b b b c 340*247d0946Sdan } 341*247d0946Sdan 342*247d0946Sdan do_execsql_test 9.$tn.1 { 343*247d0946Sdan SELECT DISTINCT a COLLATE nocase, b COLLATE nocase FROM t1 344*247d0946Sdan ORDER BY a COLLATE nocase, b COLLATE nocase 345*247d0946Sdan } { 346*247d0946Sdan a a a b a c 347*247d0946Sdan b a b b b c 348*247d0946Sdan } 349*247d0946Sdan} 350*247d0946Sdan 351*247d0946Sdan 35250118cdfSdanfinish_test 353