Files
greptimedb/tests/cases/standalone/common/insert/insert_default_timezone.result
T
dennis zhuang 1851f6bf4d fix(query): keep INSERT timestamp conversion out of the source query (#8911)
* fix(query): keep INSERT timestamp conversion out of the source query

Interpreting an INSERT's string timestamps used to work by pushing the
conversion down into the source query, which changed what that query
means. Two consequences:

- Pushing through a UNION's DISTINCT moved the dedup key from the raw
  strings to parsed instants, so rows spelling the same instant
  differently collapsed into one. On an append-only table that is a
  silently dropped row.
- A UNION branch that needed no conversion (a NULL, or an explicit cast)
  made the whole column give up, leaving sibling branches on UTC while
  the rest of the row used the session timezone.

Convert at the assignment instead, by routing its cast through a
timezone-carrying timestamp type and back. Arrow applies the timezone
when a cast target carries one, and stripping it afterwards preserves
the value. The source query is no longer touched, so both cases go away
and the tree-walking rewrite (roughly 160 lines) is deleted.

The rewrite reads source types, so it now runs TypeCoercion first: a
UNION still carries its loose per-branch schema before coercion, and
retargeting a cast whose input later becomes a timestamp would shift the
value rather than reinterpret it.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(query): address review on INSERT assignment rewrite

- Clone the input `Arc` instead of the whole subtree, and only rebuild it
  when a `Values` row actually changes.
- Defer cloning the cast source until the literal-folding path has been
  ruled out.
- Move the UTC check onto `Timezone::is_utc`, replacing a bare string
  compare.
- Cover a prepared `INSERT ... VALUES (?)`: an untyped placeholder types
  as `Null`, so the assignment cast is left for parameter substitution.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

---------

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
2026-08-25 12:34:53 +00:00

211 lines
5.4 KiB
Plaintext

--- insert timestamp with default values aware of session timezone test ---
CREATE TABLE test1 (i INTEGER, j TIMESTAMP default '2024-01-30 00:01:01' TIME INDEX, PRIMARY KEY(i));
Affected Rows: 0
INSERT INTO test1 VALUES (1, DEFAULT), (2, DEFAULT), (3, '2024-01-31 00:01:01'), (4, '2025-02-01 00:01:01');
Affected Rows: 4
SELECT * FROM test1 ORDER BY j;
+---+---------------------+
| i | j |
+---+---------------------+
| 1 | 2024-01-30T00:01:01 |
| 2 | 2024-01-30T00:01:01 |
| 3 | 2024-01-31T00:01:01 |
| 4 | 2025-02-01T00:01:01 |
+---+---------------------+
SET time_zone = 'Asia/Shanghai';
Affected Rows: 0
CREATE TABLE test2 (i INTEGER, j TIMESTAMP default '2024-01-30 00:01:01' TIME INDEX, PRIMARY KEY(i));
Affected Rows: 0
INSERT INTO test2 VALUES (1, DEFAULT), (2, DEFAULT), (3, '2024-01-31 00:01:01'), (4, '2025-02-01 00:01:01');
Affected Rows: 4
SELECT * FROM test2 ORDER BY j;
+---+---------------------+
| i | j |
+---+---------------------+
| 1 | 2024-01-29T16:01:01 |
| 2 | 2024-01-29T16:01:01 |
| 3 | 2024-01-30T16:01:01 |
| 4 | 2025-01-31T16:01:01 |
+---+---------------------+
SELECT * FROM test1 ORDER BY j;
+---+---------------------+
| i | j |
+---+---------------------+
| 1 | 2024-01-30T00:01:01 |
| 2 | 2024-01-30T00:01:01 |
| 3 | 2024-01-31T00:01:01 |
| 4 | 2025-02-01T00:01:01 |
+---+---------------------+
CREATE TABLE test3 (ts TIMESTAMP TIME INDEX, st TIMESTAMP, ts_ns TIMESTAMP(9));
Affected Rows: 0
INSERT INTO test3 (ts) VALUES ('2026-08-01 12:00:00.001');
Affected Rows: 1
INSERT INTO test3 (ts, st) VALUES ('2026-08-02 12:00:00.001', now());
Affected Rows: 1
INSERT INTO test3 (ts, st) SELECT '2026-08-03 12:00:00.001', now();
Affected Rows: 1
INSERT INTO test3 (ts, st) SELECT '2026-08-04 12:00:00.001', now() LIMIT 1;
Affected Rows: 1
INSERT INTO test3 (ts, st)
SELECT '2026-08-06 12:00:00.001', now()
UNION ALL
SELECT '2026-08-07 12:00:00.001', now();
Affected Rows: 2
INSERT INTO test3 (ts, st) VALUES (
CAST('2026-08-08 12:00:00.001' AS TIMESTAMP),
now()
);
Affected Rows: 1
INSERT INTO test3 (ts, st)
SELECT '2026-08-10 12:00:00.001', now()
UNION ALL
SELECT CAST('2026-08-11 12:00:00.001' AS TIMESTAMP), now();
Affected Rows: 2
INSERT INTO test3 (ts, st)
SELECT '2026-08-16 12:00:00.001', now()
UNION
SELECT '2026-08-17 12:00:00.001', now();
Affected Rows: 2
INSERT INTO test3 (ts, ts_ns) SELECT a, b FROM (
SELECT c AS a, c AS b FROM (SELECT '2026-08-12 12:00:00.123456789' AS c) AS t1
) AS t2;
Affected Rows: 1
INSERT INTO test3 (ts, st, ts_ns) VALUES (
'2026-08-09 12:00:00.001',
now(),
'2026-08-09 12:00:00.123456789'
);
Affected Rows: 1
-- a NULL branch must not cancel the conversion for the whole column
INSERT INTO test3 (ts, ts_ns)
SELECT '2026-08-13 12:00:00.001' AS a, '2026-08-13 12:00:00.123456789' AS b
UNION ALL
SELECT '2026-08-14 12:00:00.001', NULL;
Affected Rows: 2
-- NULL in the first branch: the union's schema starts out as Null
INSERT INTO test3 (ts, ts_ns)
SELECT '2026-08-15 12:00:00.001' AS a, NULL AS b
UNION ALL
SELECT '2026-08-19 12:00:00.001', '2026-08-19 12:00:00.123456789';
Affected Rows: 2
-- the assignment cast also lands on non-literal VALUES expressions
INSERT INTO test3 (ts, st) VALUES (concat('2026-08-20 ', '12:00:00.001'), now());
Affected Rows: 1
SELECT ts, ts_ns FROM test3 ORDER BY ts;
+-------------------------+-------------------------------+
| ts | ts_ns |
+-------------------------+-------------------------------+
| 2026-08-01T04:00:00.001 | |
| 2026-08-02T04:00:00.001 | |
| 2026-08-03T04:00:00.001 | |
| 2026-08-04T04:00:00.001 | |
| 2026-08-06T04:00:00.001 | |
| 2026-08-07T04:00:00.001 | |
| 2026-08-08T12:00:00.001 | |
| 2026-08-09T04:00:00.001 | 2026-08-09T04:00:00.123456789 |
| 2026-08-10T12:00:00.001 | |
| 2026-08-11T12:00:00.001 | |
| 2026-08-12T04:00:00.123 | 2026-08-12T04:00:00.123456789 |
| 2026-08-13T04:00:00.001 | 2026-08-13T04:00:00.123456789 |
| 2026-08-14T04:00:00.001 | |
| 2026-08-15T04:00:00.001 | |
| 2026-08-16T04:00:00.001 | |
| 2026-08-17T04:00:00.001 | |
| 2026-08-19T04:00:00.001 | 2026-08-19T04:00:00.123456789 |
| 2026-08-20T04:00:00.001 | |
+-------------------------+-------------------------------+
-- UNION dedup keys must stay on the source strings: these two spell the same
-- instant differently, so the source query yields two rows and both are kept.
CREATE TABLE test4 (ts TIMESTAMP TIME INDEX) WITH ('append_mode'='true');
Affected Rows: 0
INSERT INTO test4 (ts)
SELECT '2026-08-06 04:00:00' UNION SELECT '2026-08-06 04:00:00.000';
Affected Rows: 2
SELECT count(*) FROM test4;
+----------+
| count(*) |
+----------+
| 2 |
+----------+
SELECT ts FROM test4 ORDER BY ts;
+---------------------+
| ts |
+---------------------+
| 2026-08-05T20:00:00 |
| 2026-08-05T20:00:00 |
+---------------------+
SET time_zone = 'UTC';
Affected Rows: 0
DROP TABLE test1;
Affected Rows: 0
DROP TABLE test2;
Affected Rows: 0
DROP TABLE test3;
Affected Rows: 0
DROP TABLE test4;
Affected Rows: 0