1 | #include "catch.hpp" |
2 | #include "duckdb/common/types/date.hpp" |
3 | #include "duckdb/common/types/time.hpp" |
4 | #include "duckdb/common/types/timestamp.hpp" |
5 | #include "test_helpers.hpp" |
6 | #include "iostream" |
7 | |
8 | using namespace duckdb; |
9 | using namespace std; |
10 | |
11 | TEST_CASE("Test date truncate functionality" , "[date]" ) { |
12 | unique_ptr<QueryResult> result; |
13 | DuckDB db(nullptr); |
14 | Connection con(db); |
15 | con.EnableQueryVerification(); |
16 | |
17 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE dates(d DATE, s VARCHAR);" )); |
18 | REQUIRE_NO_FAIL(con.Query("CREATE TABLE timestamps(d TIMESTAMP, s VARCHAR);" )); |
19 | REQUIRE_NO_FAIL( |
20 | con.Query("INSERT INTO dates VALUES ('1992-12-02', 'year'), ('1993-03-03', 'month'), ('1994-05-05', 'day');" )); |
21 | REQUIRE_NO_FAIL(con.Query("INSERT INTO timestamps VALUES " |
22 | "('1992-02-02 02:02:03', 'millennium'), " |
23 | "('1992-02-02 02:02:03', 'century'), " |
24 | "('1992-02-02 02:02:03', 'decade'), " |
25 | "('1992-02-02 02:02:03', 'year'), " |
26 | "('1992-02-02 02:02:03', 'quarter'), " |
27 | "('1992-02-02 02:02:03', 'month'), " |
28 | "('1992-02-02 02:02:03', 'week'), " |
29 | "('1992-02-02 02:02:03', 'day'), " |
30 | "('1992-02-02 02:02:03', 'hour'), " |
31 | "('1992-02-02 02:02:03', 'minute'), " |
32 | "('1992-02-02 02:02:03', 'second'), " |
33 | "('1992-02-02 02:02:03', 'milliseconds'), " |
34 | "('1992-02-02 02:02:03', 'microseconds');" )); |
35 | |
36 | // test date_trunc with different combinations of constant/non-constant columns on both dates and timestamps |
37 | result = con.Query("SELECT date_trunc(NULL::VARCHAR, NULL::TIMESTAMP) FROM dates;" ); |
38 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), Value(), Value()})); |
39 | result = con.Query("SELECT date_trunc(s, NULL::TIMESTAMP) FROM dates;" ); |
40 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), Value(), Value()})); |
41 | result = con.Query("SELECT date_trunc(NULL, d) FROM dates;" ); |
42 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), Value(), Value()})); |
43 | result = con.Query("SELECT date_trunc(NULL::VARCHAR, NULL::TIMESTAMP) FROM timestamps LIMIT 3;" ); |
44 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), Value(), Value()})); |
45 | result = con.Query("SELECT date_trunc(s, NULL::TIMESTAMP) FROM timestamps LIMIT 3;" ); |
46 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), Value(), Value()})); |
47 | result = con.Query("SELECT date_trunc(NULL, d) FROM timestamps LIMIT 3;" ); |
48 | REQUIRE(CHECK_COLUMN(result, 0, {Value(), Value(), Value()})); |
49 | |
50 | // dates should be cast to timestamp correctly |
51 | result = con.Query("SELECT date_trunc('month', DATE '1992-02-02') FROM dates LIMIT 1;" ); |
52 | REQUIRE(result->sql_types[0] == SQLType::TIMESTAMP); |
53 | REQUIRE(CHECK_COLUMN(result, 0, {Value::BIGINT(Timestamp::FromString("1992-02-01 00:00:00" ))})); |
54 | result = con.Query("SELECT date_trunc(s, d) FROM dates;" ); |
55 | REQUIRE(CHECK_COLUMN(result, 0, |
56 | {Value::BIGINT(Timestamp::FromString("1992-01-01 00:00:00" )), |
57 | Value::BIGINT(Timestamp::FromString("1993-03-01 00:00:00" )), |
58 | Value::BIGINT(Timestamp::FromString("1994-05-05 00:00:00" ))})); |
59 | |
60 | // Timestamps should return timestamp type |
61 | result = con.Query("SELECT date_trunc('minute', TIMESTAMP '1992-02-02 04:03:02') FROM timestamps LIMIT 1;" ); |
62 | REQUIRE(result->sql_types[0] == SQLType::TIMESTAMP); |
63 | REQUIRE(CHECK_COLUMN(result, 0, {Value::BIGINT(Timestamp::FromString("1992-02-02 04:03:00" ))})); |
64 | |
65 | // Test all truncate operators on timestamps |
66 | result = con.Query("SELECT date_trunc(s, d) FROM timestamps;" ); |
67 | REQUIRE(CHECK_COLUMN(result, 0, |
68 | { |
69 | Value::BIGINT(Timestamp::FromString("1000-01-01 00:00:00" )), // millennium |
70 | Value::BIGINT(Timestamp::FromString("1900-01-01 00:00:00" )), // century |
71 | Value::BIGINT(Timestamp::FromString("1990-01-01 00:00:00" )), // decade |
72 | Value::BIGINT(Timestamp::FromString("1992-01-01 00:00:00" )), // year |
73 | Value::BIGINT(Timestamp::FromString("1992-01-01 00:00:00" )), // quarter |
74 | Value::BIGINT(Timestamp::FromString("1992-02-01 00:00:00" )), // month |
75 | Value::BIGINT(Timestamp::FromString("1992-01-27 00:00:00" )), // week |
76 | Value::BIGINT(Timestamp::FromString("1992-02-02 00:00:00" )), // day |
77 | Value::BIGINT(Timestamp::FromString("1992-02-02 02:00:00" )), // hour |
78 | Value::BIGINT(Timestamp::FromString("1992-02-02 02:02:00" )), // minute |
79 | Value::BIGINT(Timestamp::FromString("1992-02-02 02:02:03" )), // second |
80 | Value::BIGINT(Timestamp::FromString("1992-02-02 02:02:03" )), // millisecond |
81 | Value::BIGINT(Timestamp::FromString("1992-02-02 02:02:03" )) // microsecond |
82 | })); |
83 | |
84 | // Redo previous test but with casting to date first |
85 | result = con.Query("SELECT date_trunc(s, CAST(d as DATE)) FROM timestamps;" ); |
86 | REQUIRE(CHECK_COLUMN(result, 0, |
87 | { |
88 | Value::BIGINT(Timestamp::FromString("1000-01-01 00:00:00" )), // millennium |
89 | Value::BIGINT(Timestamp::FromString("1900-01-01 00:00:00" )), // century |
90 | Value::BIGINT(Timestamp::FromString("1990-01-01 00:00:00" )), // decade |
91 | Value::BIGINT(Timestamp::FromString("1992-01-01 00:00:00" )), // year |
92 | Value::BIGINT(Timestamp::FromString("1992-01-01 00:00:00" )), // quarter |
93 | Value::BIGINT(Timestamp::FromString("1992-02-01 00:00:00" )), // month |
94 | Value::BIGINT(Timestamp::FromString("1992-01-27 00:00:00" )), // week |
95 | Value::BIGINT(Timestamp::FromString("1992-02-02 00:00:00" )), // day |
96 | Value::BIGINT(Timestamp::FromString("1992-02-02 00:00:00" )), // hour |
97 | Value::BIGINT(Timestamp::FromString("1992-02-02 00:00:00" )), // minute |
98 | Value::BIGINT(Timestamp::FromString("1992-02-02 00:00:00" )), // second |
99 | Value::BIGINT(Timestamp::FromString("1992-02-02 00:00:00" )), // millisecond |
100 | Value::BIGINT(Timestamp::FromString("1992-02-02 00:00:00" )) // microsecond |
101 | })); |
102 | |
103 | // Test week operator special cases |
104 | result = con.Query("SELECT date_trunc('week', TIMESTAMP '2020-01-01 04:03:02') FROM timestamps LIMIT 1;" ); |
105 | REQUIRE(result->sql_types[0] == SQLType::TIMESTAMP); |
106 | REQUIRE(CHECK_COLUMN(result, 0, {Value::BIGINT(Timestamp::FromString("2019-12-30 00:00:00" ))})); |
107 | result = con.Query("SELECT date_trunc('week', TIMESTAMP '2019-01-06 04:03:02') FROM timestamps LIMIT 1;" ); |
108 | REQUIRE(result->sql_types[0] == SQLType::TIMESTAMP); |
109 | REQUIRE(CHECK_COLUMN(result, 0, {Value::BIGINT(Timestamp::FromString("2018-12-31 00:00:00" ))})); |
110 | |
111 | // Test quarter operator more thoroughly |
112 | result = con.Query("SELECT date_trunc('quarter', TIMESTAMP '2020-12-02 04:03:02') FROM timestamps LIMIT 1;" ); |
113 | REQUIRE(result->sql_types[0] == SQLType::TIMESTAMP); |
114 | REQUIRE(CHECK_COLUMN(result, 0, {Value::BIGINT(Timestamp::FromString("2020-10-01 00:00:00" ))})); |
115 | result = con.Query("SELECT date_trunc('quarter', TIMESTAMP '2019-01-06 04:03:02') FROM timestamps LIMIT 1;" ); |
116 | REQUIRE(result->sql_types[0] == SQLType::TIMESTAMP); |
117 | REQUIRE(CHECK_COLUMN(result, 0, {Value::BIGINT(Timestamp::FromString("2019-01-01 00:00:00" ))})); |
118 | |
119 | // Unknown specifier should fail |
120 | REQUIRE_FAIL(con.Query("SELECT date_trunc('epoch', TIMESTAMP '2019-01-06 04:03:02') FROM timestamps LIMIT 1;" )); |
121 | } |
122 | |