xref: /sqlite-3.40.0/test/distinct.test (revision 247d0946)
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