1 | #include "catch.hpp" |
2 | #include "test_helpers.hpp" |
3 | |
4 | using namespace duckdb; |
5 | using namespace std; |
6 | |
7 | TEST_CASE("Test binding parameters with union expressions" , "[setops]" ) { |
8 | unique_ptr<QueryResult> result; |
9 | DuckDB db(nullptr); |
10 | Connection con(db); |
11 | con.EnableQueryVerification(); |
12 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE test(a INTEGER);" )); |
13 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE test2(b INTEGER);" )); |
14 | |
15 | REQUIRE_NO_FAIL(con.Query("INSERT INTO test VALUES (1), (2), (3), (NULL);" )); |
16 | REQUIRE_NO_FAIL(con.Query("INSERT INTO test2 VALUES (2), (3), (4), (NULL);" )); |
17 | |
18 | result = con.Query("(SELECT a FROM test ORDER BY a+1) UNION SELECT b FROM test2 ORDER BY 1;" ); |
19 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
20 | REQUIRE(result->types.size() == 1); |
21 | |
22 | // union returns only one column |
23 | result = con.Query("SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test2) res ORDER BY 1;" ); |
24 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
25 | REQUIRE(result->types.size() == 1); |
26 | // we can only bind by the column names of the first query |
27 | result = con.Query("SELECT a FROM (SELECT * FROM test UNION SELECT * FROM test2) res ORDER BY 1;" ); |
28 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
29 | REQUIRE(result->types.size() == 1); |
30 | // hence this does not work: "b" is from the second query |
31 | REQUIRE_FAIL(con.Query("SELECT b FROM (SELECT * FROM test UNION SELECT * FROM test2) res ORDER BY 1;" )); |
32 | // it works if we reverse the tables |
33 | result = con.Query("SELECT b FROM (SELECT * FROM test2 UNION SELECT * FROM test) res ORDER BY 1;" ); |
34 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
35 | REQUIRE(result->types.size() == 1); |
36 | // we can give explicit subquery aliases |
37 | result = con.Query("SELECT col1 FROM (SELECT * FROM test2 UNION SELECT * FROM test) res(col1) ORDER BY 1;" ); |
38 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
39 | REQUIRE(result->types.size() == 1); |
40 | |
41 | // we can ORDER BY names from both sides |
42 | result = con.Query("SELECT a FROM test UNION SELECT b FROM test2 ORDER BY a;" ); |
43 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
44 | REQUIRE(result->types.size() == 1); |
45 | result = con.Query("SELECT a FROM test UNION SELECT b FROM test2 ORDER BY b;" ); |
46 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
47 | REQUIRE(result->types.size() == 1); |
48 | // if names are ambiguous, throw an error |
49 | REQUIRE_FAIL(con.Query("SELECT 1, a FROM test UNION SELECT b AS a, 1 FROM test2 ORDER BY a;" )); |
50 | // if expressions are ambiguous as well, throw an error |
51 | REQUIRE_FAIL(con.Query("SELECT 1, a+1 FROM test UNION SELECT a+1, 1 FROM test ORDER BY a+1;" )); |
52 | // also if we have multiple setops |
53 | result = con.Query("SELECT a FROM test UNION SELECT b FROM test2 UNION SELECT b AS c FROM test2 ORDER BY c;" ); |
54 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
55 | REQUIRE(result->types.size() == 1); |
56 | // we can also order by the expression itself |
57 | result = con.Query("SELECT a FROM test UNION SELECT b FROM test2 UNION SELECT b + 1 FROM test2 ORDER BY b + 1;" ); |
58 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4, 5})); |
59 | REQUIRE(result->types.size() == 1); |
60 | // multiple columns order |
61 | result = con.Query("SELECT a, 10 - a AS b FROM test UNION SELECT b, b + 1 FROM test2 ORDER BY 2;" ); |
62 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 2, 3, 4, 3, 2, 1})); |
63 | REQUIRE(CHECK_COLUMN(result, 1, {Value(), 3, 4, 5, 7, 8, 9})); |
64 | // ambiguous naming reference should fail |
65 | REQUIRE_FAIL(con.Query("SELECT a, 10 - a AS b FROM test UNION SELECT b, b + 1 FROM test2 ORDER BY b;" )); |
66 | // and by constant references |
67 | result = con.Query("SELECT a FROM test UNION SELECT b FROM test2 ORDER BY 1;" ); |
68 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
69 | REQUIRE(result->types.size() == 1); |
70 | // out of range constant reference |
71 | REQUIRE_FAIL(con.Query("SELECT a FROM test UNION SELECT b FROM test2 ORDER BY 2;" )); |
72 | |
73 | // what if our subqueries have an order by clause? |
74 | result = con.Query("(SELECT a FROM test ORDER BY a+1) UNION SELECT b FROM test2 ORDER BY 1;" ); |
75 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
76 | REQUIRE(result->types.size() == 1); |
77 | |
78 | // unions with SELECT * also allows orders |
79 | result = con.Query("SELECT * FROM test UNION SELECT * FROM test2 ORDER BY a;" ); |
80 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
81 | result = con.Query("SELECT * FROM test UNION SELECT * FROM test2 ORDER BY b;" ); |
82 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
83 | |
84 | // test union with/without table specifiers |
85 | result = con.Query("SELECT a FROM test UNION SELECT * FROM test2 ORDER BY test.a;" ); |
86 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
87 | result = con.Query("SELECT a FROM test UNION SELECT b FROM test2 ORDER BY test2.b;" ); |
88 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
89 | result = con.Query("SELECT test.a FROM test UNION SELECT * FROM test2 ORDER BY a;" ); |
90 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
91 | result = con.Query("SELECT test.a FROM test UNION SELECT test2.b FROM test2 ORDER BY b;" ); |
92 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
93 | |
94 | // what about multiple set ops? |
95 | result = con.Query( |
96 | "SELECT a FROM test UNION SELECT * FROM test2 UNION SELECT * FROM test t1 ORDER BY test.a, test2.b, t1.a;" ); |
97 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
98 | result = con.Query("SELECT a FROM test UNION SELECT * FROM test2 UNION SELECT * FROM test t1 ORDER BY a;" ); |
99 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
100 | // and subqueries |
101 | result = con.Query("SELECT a FROM (SELECT * FROM test) bla UNION SELECT * FROM test2 ORDER BY bla.a;" ); |
102 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 3, 4})); |
103 | // what if we have cross products or joins |
104 | result = con.Query("SELECT t1.a, t2.a FROM test t1, test t2 WHERE t1.a=t2.a UNION SELECT b, b - 1 FROM test2 ORDER " |
105 | "BY t1.a, t2.a, test2.b;" ); |
106 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), 1, 2, 2, 3, 3, 4})); |
107 | REQUIRE(CHECK_COLUMN(result, 1, {Value(), 1, 1, 2, 2, 3, 3})); |
108 | } |
109 | |
110 | TEST_CASE("Test union with nulls" , "[setops]" ) { |
111 | unique_ptr<QueryResult> result; |
112 | DuckDB db(nullptr); |
113 | Connection con(db); |
114 | |
115 | result = con.Query("SELECT NULL as a, NULL as b, 1 as id UNION SELECT CAST('2015-10-11 00:00:00' AS TIMESTAMP) as " |
116 | "a, CAST('2015-10-11 12:34:56' AS TIMESTAMP) as b, 2 as id ORDER BY 3" ); |
117 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), Value::TIMESTAMP(2015, 10, 11, 0, 0, 0, 0)})); |
118 | REQUIRE(CHECK_COLUMN(result, 1, {Value(), Value::TIMESTAMP(2015, 10, 11, 12, 34, 56, 0)})); |
119 | REQUIRE(CHECK_COLUMN(result, 2, {1, 2})); |
120 | } |
121 | |