Skip to content

[fix](be) Preserve local time in TIMESTAMPTZ day arithmetic - #70003

Open
jacktengg wants to merge 8 commits into
apache:masterfrom
jacktengg:DORIS-30053
Open

jacktengg wants to merge 8 commits into
apache:masterfrom
jacktengg:DORIS-30053

Conversation

@jacktengg

@jacktengg jacktengg commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary:

TIMESTAMPTZ day arithmetic currently changes UTC fields, so adding one day across a DST transition adds exactly 24 hours and shifts the local clock. In an America/New_York session, days_add(CAST('2024-03-09 12:00:00-05:00' AS TIMESTAMPTZ(6)), 1) produces 13:00 the following day instead of 12:00.

Apply DAY intervals to the session-local date in BE and FE constant folding, then resolve the result back to UTC. Use a civil calendar intermediate consistent with FE, including local February 29 in year 0 when both UTC endpoints are valid. Preserve fractional seconds, the input offset for repeated local times, existing cctz gap resolution, and date-range errors. Zero-day intervals remain an identity at valid UTC boundaries so predicate movement remains safe. This covers days_add/days_sub and their SQL aliases; hours_add remains elapsed-time arithmetic.

Local-day arithmetic can reverse UTC ordering at a fall-back and is not always invertible. Restrict its monotonicity declaration in variable-offset zones and prevent comparison rearrangement from replacing a DAY predicate with an invalid inverse operation. For example, moving the interval in days_add(ts, 1) < '2024-11-04 01:30:00-05:00' can incorrectly include the first 01:50 from the preceding fall-back day.

Release note

TIMESTAMPTZ day addition and subtraction now preserve session-local calendar time across daylight-saving transitions. One day may represent 23 or 25 elapsed hours; explicitly adding 24 hours retains elapsed-time semantics.

Check List (For Author)

  • Test
    • Regression test: test_timestamptz_days_add_dst, test_timestamptz_dst_fold, test_timestamptz_dst_gap, test_timestamptz_utc_functions
    • Unit Test: 20 FE tests (TimestampTzLiteralTest and SimplifyArithmeticComparisonRuleTest); 11 BE tests (TimeStampTzValueTest and selected VTimestampFunctionsTest cases)
    • Manual test: New York spring transition returns local 12:00 and 82800 seconds; explicit 24 hours returns 13:00
    • Validation: ASAN BE build, FE build with Checkstyle, clang-format 16, build hygiene, and clang-tidy on modified C++ files
    • Regression expected results generated by run-regression-test.sh; 10 FE folding and BE result groups compared
  • Behavior changed:
    • Yes. TIMESTAMPTZ DAY intervals use the session-local calendar; DST-sensitive monotonicity and comparison rearrangement are restricted.
  • Does this need documentation?
    • No. Existing date arithmetic bug fix; release note included.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: TIMESTAMPTZ day arithmetic changes UTC fields, so adding a day across a DST transition adds a fixed 24 hours and shifts the local clock. In an America/New_York session, adding one day to 2024-03-09 12:00:00-05:00 produces 13:00 instead of 12:00 the following day.

Apply DAY intervals to the session-local date in BE and FE constant folding, preserving fractional seconds, preferred offsets in overlaps, existing gap resolution, and date-range errors. Restrict DST-sensitive day arithmetic monotonicity and comparison rearrangement: moving a local-day interval across a comparison can otherwise return incorrect rows at a fall-back. Explicit hour arithmetic keeps elapsed-time semantics.

### Release note

TIMESTAMPTZ day addition and subtraction preserve session-local calendar time across daylight-saving transitions, allowing a day to represent 23 or 25 elapsed hours.

### Check List (For Author)

- Test: Regression test / Unit Test / Manual test
    - Regression test: test_timestamptz_days_add_dst, test_timestamptz_dst_fold, test_timestamptz_dst_gap, test_timestamptz_utc_functions; all 4 suites passed. Expected results generated by the regression script; 7 FE/BE result groups match.
    - Unit Test: 19 FE tests in TimestampTzLiteralTest and SimplifyArithmeticComparisonRuleTest; 10 BE tests in TimeStampTzValueTest and selected VTimestampFunctionsTest cases; all passed.
    - Manual test: New York spring transition returns local 12:00 and 82800 seconds; adding 24 hours returns 13:00.
    - ASAN BE build, FE build with Checkstyle, clang-format 16, build hygiene, and clang-tidy passed.
- Behavior changed: Yes. TIMESTAMPTZ DAY intervals use session-local calendar dates; unsafe monotonicity and inverse comparison rewrites are disabled for variable-offset zones.
- Does this need documentation: No. Existing date arithmetic bug fix; release note included.
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

### What problem does this PR solve?

Issue Number: None

Related PR: apache#70003

Problem Summary: The session-local intermediate of a valid UTC TIMESTAMPTZ can be February 29 in year 0. DateV2 rejects that date before applying a DAY interval, although FE uses a civil calendar and can produce valid UTC output. Use the same civil calendar in BE and validate the final UTC value. For example, UTC 0000-02-28 20:00:00 at +08:00 minus one day now consistently yields UTC 0000-02-27 20:00:00.

A zero-day interval must also remain an infallible identity for valid UTC boundary values. Resolve zero before local-year validation in both FE and BE so existing predicate movement cannot introduce errors on rows excluded by a join. Add direct boundary unit tests, year-0 projection regressions, and a zero-day join regression with runtime filters disabled.

### Release note

Keep TIMESTAMPTZ day arithmetic consistent between FE and BE at year-0 calendar boundaries and preserve zero-day identity at valid UTC boundaries.

### Check List (For Author)

- Test: Regression test / Unit Test
    - Unit Test: 11 selected BE tests and 20 FE tests passed using run-be-ut.sh and run-fe-ut.sh.
    - Regression test: test_timestamptz_days_add_dst, test_timestamptz_dst_fold, test_timestamptz_dst_gap, test_timestamptz_utc_functions; all 4 suites passed. Expected output generated by the regression script; 10 FE/BE groups match.
    - ASAN BE build, FE build with Checkstyle, clang-format 16, build hygiene and clang-tidy passed.
- Behavior changed: Yes. Valid UTC boundaries retain zero-day identity; civil calendar intermediate dates no longer cause FE/BE divergence.
- Does this need documentation: No. Boundary correctness fixes for existing date arithmetic.
@jacktengg

Copy link
Copy Markdown
Contributor Author

Local pipeline review — ✅ PASS

schema: doris-repo-review/v1
status: PASS
pr: apache/doris#70003
commit: 4e4a662c1355be835d5a4e6437bb3e9847d48ed8
base: 4bc7f426828e4cedfd2b7ce9b76fe95b14b5b146
reviewed_at: 2026-11-03T02:23+08:00
reviewer: jacktengg
model: gpt-6-astra
effort: xhigh
findings: {blocker: 0, major: 0, minor: 0, nit: 0}
rounds: 1
converged: true

Notes for maintainers

None.

Reviewed locally with the doris-repo-review pipeline. Repository policy may accept this receipt for the matching commit; it is not a human Apache approval.

@jacktengg

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28599 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 4e4a662c1355be835d5a4e6437bb3e9847d48ed8, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17591	5143	5043	5043
q2	2088	363	252	252
q3	10202	1425	829	829
q4	4669	463	341	341
q5	7434	797	527	527
q6	186	184	152	152
q7	734	807	593	593
q8	9262	1354	1054	1054
q9	5514	4216	4223	4216
q10	6832	1300	1039	1039
q11	440	249	229	229
q12	640	400	283	283
q13	18127	3696	3062	3062
q14	270	271	251	251
q15	q16	725	715	668	668
q17	1231	746	578	578
q18	7067	6203	6129	6129
q19	1115	948	605	605
q20	402	349	229	229
q21	5164	2318	2267	2267
q22	353	300	252	252
Total cold run time: 100046 ms
Total hot run time: 28599 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5587	5624	5585	5585
q2	678	556	531	531
q3	5243	5393	4897	4897
q4	1999	2151	1668	1668
q5	5233	5149	5709	5149
q6	261	198	150	150
q7	2341	2065	1944	1944
q8	3136	2859	2692	2692
q9	8168	7983	7958	7958
q10	3732	3666	3255	3255
q11	603	458	410	410
q12	683	688	527	527
q13	3789	4009	3283	3283
q14	305	320	299	299
q15	q16	696	727	684	684
q17	8315	7564	6882	6882
q18	12919	12207	12905	12207
q19	915	791	792	791
q20	2167	2170	1922	1922
q21	5750	4709	4823	4709
q22	505	439	428	428
Total cold run time: 73025 ms
Total hot run time: 65971 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 150786 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 4e4a662c1355be835d5a4e6437bb3e9847d48ed8, data reload: false

query5	4312	592	429	429
query6	437	207	185	185
query7	4818	437	225	225
query8	324	167	152	152
query9	8763	3148	3164	3148
query10	476	300	257	257
query11	6049	3837	3563	3563
query12	144	92	87	87
query13	1275	442	341	341
query14	6126	3913	3630	3630
query14_1	3420	3397	3380	3380
query15	206	201	198	198
query16	952	396	361	361
query17	858	654	546	546
query18	2436	418	334	334
query19	189	161	127	127
query20	82	79	83	79
query21	210	122	104	104
query22	13004	12913	12833	12833
query23	13353	12819	12582	12582
query23_1	12554	12599	12569	12569
query24	7367	814	466	466
query24_1	476	476	484	476
query25	504	392	344	344
query26	1264	253	131	131
query27	2790	437	268	268
query28	4645	2016	2051	2016
query29	1550	594	436	436
query30	286	217	186	186
query31	1001	930	812	812
query32	143	83	86	83
query33	518	299	230	230
query34	905	857	504	504
query35	739	772	634	634
query36	817	810	713	713
query37	116	96	79	79
query38	1757	1719	1668	1668
query39	784	801	754	754
query39_1	736	739	739	739
query40	226	115	96	96
query41	66	67	64	64
query42	85	81	81	81
query43	339	360	307	307
query44	1300	696	705	696
query45	171	183	161	161
query46	819	969	564	564
query47	2943	2565	2898	2565
query48	294	306	217	217
query49	577	396	297	297
query50	693	283	210	210
query51	10573	10539	10098	10098
query52	83	79	78	78
query53	188	207	155	155
query54	267	201	187	187
query55	71	69	65	65
query56	260	228	226	226
query57	1648	1604	1562	1562
query58	292	272	273	272
query59	2187	2204	1994	1994
query60	301	258	220	220
query61	168	173	171	171
query62	384	338	290	290
query63	193	164	159	159
query64	2865	1066	907	907
query65	3332	3290	3267	3267
query66	1783	437	370	370
query67	20112	20331	19974	19974
query68	3073	1003	636	636
query69	389	275	248	248
query70	820	766	754	754
query71	289	217	209	209
query72	2846	2422	2267	2267
query73	517	520	298	298
query74	4602	4478	4314	4314
query75	2340	2304	1881	1881
query76	2313	1007	577	577
query77	360	372	268	268
query78	9766	9801	9192	9192
query79	997	864	532	532
query80	1213	419	332	332
query81	592	322	282	282
query82	578	135	106	106
query83	278	191	182	182
query84	317	114	96	96
query85	1160	425	377	377
query86	369	229	235	229
query87	2014	1940	1796	1796
query88	3778	2782	2758	2758
query89	326	277	244	244
query90	1724	167	168	167
query91	158	152	128	128
query92	116	90	92	90
query93	998	990	582	582
query94	558	275	272	272
query95	576	395	307	307
query96	669	491	242	242
query97	2445	2401	2327	2327
query98	159	150	154	150
query99	762	761	644	644
Total cold run time: 235372 ms
Total hot run time: 150786 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.33 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 4e4a662c1355be835d5a4e6437bb3e9847d48ed8, data reload: false

query1	0.00	0.01	0.00
query2	0.10	0.04	0.05
query3	0.26	0.10	0.08
query4	1.61	0.11	0.09
query5	0.23	0.26	0.24
query6	1.17	0.68	0.68
query7	0.03	0.01	0.01
query8	0.06	0.04	0.04
query9	0.39	0.36	0.35
query10	0.59	0.59	0.57
query11	0.20	0.14	0.14
query12	0.18	0.15	0.14
query13	0.50	0.49	0.48
query14	1.03	1.01	1.02
query15	0.62	0.63	0.62
query16	0.33	0.35	0.35
query17	1.13	1.09	1.15
query18	0.20	0.20	0.18
query19	2.11	1.96	2.02
query20	0.02	0.01	0.02
query21	15.52	0.19	0.13
query22	4.92	0.05	0.06
query23	16.27	0.34	0.13
query24	2.82	0.54	0.34
query25	0.13	0.07	0.06
query26	1.25	0.22	0.16
query27	0.04	0.05	0.05
query28	3.25	0.75	0.40
query29	12.50	4.15	3.25
query30	0.28	0.13	0.10
query31	2.77	0.56	0.33
query32	3.22	0.60	0.50
query33	3.15	3.26	3.27
query34	15.34	4.02	3.38
query35	3.32	3.33	3.37
query36	0.56	0.45	0.42
query37	0.08	0.06	0.07
query38	0.05	0.04	0.03
query39	0.04	0.03	0.03
query40	0.17	0.14	0.15
query41	0.09	0.03	0.03
query42	0.03	0.04	0.03
query43	0.04	0.04	0.04
Total cold run time: 96.6 s
Total hot run time: 24.33 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 94.87% (37/39) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 77.71% (35899/46194)
Line Coverage 63.02% (403591/640410)
Region Coverage 59.62% (341600/572969)
Branch Coverage 60.73% (158097/260329)

Comment thread be/src/core/value/timestamptz_value.cpp Resolved
### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: The date comparison comment omits the session timezone, complete dates, and the reason for passing null range bounds to isMonotonic. Describe the ordering requirement and show how the New York autumn transition changes the comparison result after interval rearrangement. Use short descriptive sentences and consistent technical terms.

### Release note

None

### Check List (For Author)

- Test: No need to run functional tests because only comments changed.
    - Targeted Maven Checkstyle passed with zero violations.
    - Java token comparison confirmed that executable code is unchanged.
    - Verified the example's UTC conversions and opposite Boolean results.
    - Comment sentences have at most 18 words; all lines fit the 120-character limit.
- Behavior changed: No
- Does this need documentation: No. This change updates the source comment.
### What problem does this PR solve?

Issue Number: N/A

Related PR: N/A

Problem Summary: Each nonzero TIMESTAMPTZ DAY operation calls the local-to-UTC
conversion overload with a preferred offset. This overload queried the civil
time and then called cctz::convert, which repeated the same lookup. Initialize
the time point from the existing civil_lookup, using trans for SKIPPED times
and pre otherwise, to reduce these civil-time lookups from two to one per
conversion. Keep the REPEATED preferred-offset selection and fractional seconds
unchanged. This preserves gap and overlap semantics. No end-to-end SQL
performance improvement is claimed.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - ./run-be-ut.sh --run --filter='TimeStampTzValueTest.*' -j64 (ASAN; all 9 tests passed)
    - build-support/clang-format.sh and build-support/check-format.sh (clang-format 16)
    - build-support/run-clang-tidy.sh --build-dir be/ut_build_ASAN (passed; toolchain resource directory supplied)
    - git diff --check
- Behavior changed: No
- Does this need documentation: No
### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#70204

Problem Summary: TIMESTAMPTZ day arithmetic clamped a target local time in a
DST gap to the transition instant. In America/New_York, adding one day to
2024-03-09 02:30:00.123456-05:00 produced 03:00:00.123456-04:00 instead of
03:30:00.123456-04:00 on March 10.

Initialize the preferred-offset local-to-UTC conversion from the existing
civil lookup's pre value, aligning its gap policy with apache#70204 while retaining
a single civil-time lookup and the overlap offset selection. Align FE DAY
constant folding with the same policy through ZonedDateTime.ofLocal. Update
unit and regression coverage for adding and subtracting a day into a gap,
and disable SQL caching in the FE/BE comparison. Regenerate the regression
output with the test runner.

### Release note

TIMESTAMPTZ day arithmetic shifts nonexistent local times forward by the DST
gap length and preserves fractional seconds in both FE and BE.

### Check List (For Author)

- Test: Unit Test / Regression test
    - ASAN BE and FE build passed via build.sh --be --fe -j64
    - run-be-ut.sh: all 9 TimeStampTzValueTest tests passed
    - run-fe-ut.sh: all 17 TimestampTzLiteralTest tests passed
    - run-regression-test.sh: test_timestamptz_days_add_dst and test_timestamptz_dst_fold passed
    - DAY regression passed with SQL caching disabled; its .out file was generated by the runner
    - clang-format 16, header hygiene, clang-tidy, FE Checkstyle and git diff --check passed
- Behavior changed: Yes. Gap targets retain their position and fraction instead of clamping to the transition.
- Does this need documentation: No
@jacktengg

Copy link
Copy Markdown
Contributor Author

/review

@jacktengg

Copy link
Copy Markdown
Contributor Author

run buildall

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static review of all 12 changed files at 633f33a found two P2 correctness issues, reported inline: the shared gap resolver can move existing floor results after their input, and fixed-offset DAY comparison rearrangement can introduce an invalid inverse into an otherwise valid query.

Critical checkpoint conclusions:

  1. Goal and behavior: session-local DAY arithmetic preserves clock time across DST in BE and FE, including overlap preference, fractional seconds and zero-day identity; the two inline issues remain.
  2. Scope and reuse: changes are focused and reuse the date-function framework; the shared resolver change also affects existing rounding callers (first finding).
  3. Concurrency and locks: no new concurrent mutation, thread entry, lock or lock-order change; timezone state follows the existing query-context lifetime.
  4. Lifecycle and initialization: new calculations use scalar locals and existing timezone objects; no new resource lifetime or cross-translation-unit initializer dependency.
  5. Configuration: no new configuration; the existing session timezone reaches the BE RuntimeState and FE folding paths.
  6. Compatibility: no function-name, result-type, precision, wire or storage-format change. DAY result semantics intentionally change, as stated in the release note; FE/BE versions should be aligned when checking results during an upgrade.
  7. Thrift/protobuf: no changes under gensrc; field-ID review is not applicable.
  8. Parallel paths: checked days_add/days_sub and DATE_ADD/DATE_SUB/ADDDATE/SUBDATE binding, const/vector/nullable dispatch, FE folding, comparison inversion and both monotonicity consumers. HOUR remains elapsed arithmetic; WEEK and compound intervals retain their existing implementation.
  9. Conditions and errors: new zero, local-year and DST guards have concrete comments; range failures propagate through existing exception boundaries. The optimizer needs the inverse-validity check described in the second finding.
  10. Test coverage: inspected BE value/dispatch tests, FE literal/rewrite tests and the new regression suite for both DST directions, gaps, overlaps, NULLs, precision, UTC boundaries and year-zero intermediates. The reported floor and invalid-inverse cases are missing.
  11. Expected results: manually checked every new golden output against its query and intended DAY policy; FE/BE groups agree. The author reports script generation; generation provenance was not executed or independently reproduced here.
  12. Observability: existing date-range error reporting remains appropriate; no new distributed operation requires metrics or logs.
  13. Transactions and persistence: no EditLog, replay, failover, visible-version or delete-bitmap changes.
  14. Writes and atomicity: no storage-write path, transaction-atomicity or crash-recovery changes.
  15. FE/BE variables: no new serialized variable or protocol field; the existing session timezone is used.
  16. Performance and memory: the previously reported redundant civil lookup is removed; no new owned buffer or obvious avoidable allocation in the BE row loop. No performance benchmark was run.
  17. Additional checks: reviewed preferred-offset helper callers, fixed-offset inversion, special calendar bounds, header/include and test conventions; no other verified issue or remaining static scope gap.

Existing review context: discussion 4524950284 and its reply were read. Its duplicate-lookup concern is addressed on this head and is not reposted; the first finding describes a distinct result-correctness regression. No existing P0/P1 blocker was identified. User focus: no additional focus was supplied.

Validation was static inspection only. No builds, tests, benchmarks, source edits or submodule initialization were performed.

Review converged after 2 round(s).

Comment thread be/src/core/value/timestamptz_value.cpp Resolved
### What problem does this PR solve?

Issue Number: N/A

Related PR: N/A

Problem Summary: In a +08:00 session, rewriting days_add(ts, 1) <
TIMESTAMPTZ '9999-12-31 23:00:00+00:00' introduces days_sub(cutoff, 1).
The cutoff is valid UTC but has session-local year 10000, so its inverse
fails even though the original comparison can evaluate successfully.
The fixed-offset monotonicity check does not establish that the inverse
is valid, and failed FE folding leaves the invalid call for BE execution.

Fold the candidate inverse for TIMESTAMPTZ DAY addition and subtraction,
and require a TimestampTzLiteral before rearranging the comparison. Keep
the original predicate when folding cannot produce a valid literal, and
reuse the literal when it succeeds. Cover local and UTC year boundaries,
positive and negative intervals, zero-day identities, and valid inverses.

### Release note

Prevent optimizer-introduced out-of-range errors in TIMESTAMPTZ DAY
comparisons near timezone and year boundaries.

### Check List (For Author)

- Test: Unit Test / Regression test / Manual test
    - 22 FE unit tests passed: SimplifyArithmeticComparisonRuleTest and TimestampTzLiteralTest.
    - Regression suites test_timestamptz_day_predicate_boundary and test_timestamptz_days_add_dst passed.
    - Generated the new regression output with run-regression-test.sh -forceGenOut, then compared it in a normal run.
    - Reproduced the original out-of-range failure and verified the fixed plan retains the original DAY comparison.
    - FE build via build.sh --fe and Checkstyle passed.
    - git diff --check passed with the generated .out trailing blank separator allowed.
- Behavior changed: Yes; preserve valid DAY comparisons when their inverse cannot be evaluated safely.
- Does this need documentation: No
This reverts commit 3b6bca3 in preparation for cherry-picking
cd42918 from PR apache#70556.

### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#70556

Problem Summary: Replace the TIMESTAMPTZ DAY-only inverse validation with
the shared date arithmetic validation from PR apache#70556. The replacement
covers all date arithmetic functions in the rearrangement map, applies
type coercion, and validates the inverse exclusively on FE. Keep the
existing DST monotonicity guard from earlier commits.

### Release note

None; this preparatory revert is followed by the broader fix.

### Check List (For Author)

- Test: Manual test; inspected the reverse patch and replacement code. Run unit and regression tests after applying the replacement.
- Behavior changed: Yes; this intermediate commit removes the narrow inverse guard before its replacement.
- Does this need documentation: No
### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#70556

Problem Summary: Moving date arithmetic across a comparison can introduce
an inverse operation outside the date type's range even when the source
operation is valid. Fixed-offset TIMESTAMPTZ sessions can also have a valid
UTC cutoff whose local year is outside the supported range.

Coerce and evaluate each candidate date inverse on FE, accepting only a
DateLiteral and reusing it in the rewritten comparison. Preserve the
original comparison for invalid, NULL or nonconstant boundaries. The
shared guard covers week, day, hour, minute and second addition and
subtraction across the supported date types.

Preserve this branch's existing DST monotonicity guard. Resolve import
conflicts and explicitly use UTC in the two imported boundary unit tests
so their DAY assertions exercise inverse validation in a fixed-offset
session. No additional production changes were needed for integration.

### Release note

Prevent date comparison errors caused by optimizer-generated arithmetic
that exceeds the date type's range.

### Check List (For Author)

- Test: Unit Test / Regression test / Manual test
    - 22 FE tests passed via run-fe-ut.sh: SimplifyArithmeticComparisonRuleTest and TimestampTzLiteralTest.
    - FE build via build.sh --fe and Checkstyle passed.
    - Six regression suites passed: test_date_comparison_boundary, test_simplify_arithmetic, test_simplify_comparison_predicate, test_date_arithmetic_rejecting_boundary, test_timestamptz_day_predicate_boundary, and test_timestamptz_days_add_dst.
    - Temporarily restored the reverted boundary suite to verify all 12 original queries, then removed the temporary copies.
    - Verified the original +08:00 boundary query returns its row and retains days_add in EXPLAIN with BE constant folding enabled.
    - git diff --check passed with the generated .out trailing separator allowed.
- Behavior changed: Yes; reject unsafe date-comparison rearrangements while preserving valid inverse optimizations.
- Does this need documentation: No

(cherry picked from commit cd42918)
@jacktengg

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static review of all 14 changed files at c158d92 found no additional issues to report inline. The previously reported inverse-range issue is addressed. The existing P2 gap-rounding concern still applies to this exact head and is preserved below without a duplicate comment.

Critical checkpoint conclusions:

  1. Goal and proof: BE and FE apply TIMESTAMPTZ DAY intervals to the session-local calendar, preserving fractions, overlap preference and zero-day identity. Paired unit and regression cases cover spring/fall and both directions. The existing shared-rounding concern remains.
  2. Scope and reuse: the change reuses the date-function framework, type coercion and FE constant folder. The inverse-validity guard consistently covers the mapped date units and types. The shared conversion helper also affects existing rounding callers, as already reported.
  3. Concurrency and locks: no new thread, shared mutable state, lock or lock-order change. FE reads the existing thread-local session; BE reads the existing query RuntimeState timezone.
  4. Lifecycle and initialization: new civil/time values are local intermediates. No new resource ownership, reference cycle, global initialization dependency or teardown requirement.
  5. Configuration: no new configuration. Existing session timezone reaches FE folding and every BE dispatch shape.
  6. Compatibility: no function-name, result-type, precision, storage or wire-format change. DAY results intentionally change across timezone transitions, as documented in the release note; aligned FE/BE versions are needed for uniform new semantics. No new libc API or linker dependency is introduced; final ELF ABI was not audited in this static review.
  7. Thrift/protobuf: no gensrc definitions changed; field-ID review is inapplicable.
  8. Parallel paths: checked days_add/days_sub, DATE_ADD/DATE_SUB/ADDDATE/SUBDATE, INTERVAL DAY and timestampadd binding, FE folding, all constant/vector/nullable combinations, partition-bound evaluation and runtime-filter monotonicity. HOUR remains elapsed arithmetic; WEEK and compound intervals retain their consistent existing UTC paths.
  9. Conditions and errors: zero-day, local-year, DST-monotonicity and inverse-validity guards have concrete explanations. Only inverses that fold on FE to a DateLiteral are accepted; NULL, column boundaries, range failures and unfolded expressions preserve the original comparison. Existing date-range exception propagation remains intact.
  10. Test coverage: inspected both new BE unit-test blocks, FE literal/rewrite tests and both regression suites. Coverage includes DST gaps/overlaps, negative/zero days, fractions, NULLs, UTC bounds, year-zero local intermediates, nonmonotonic predicates, comparison types/operators/signs, column boundaries, recursive inverses and BE-fold mode. The existing rounding gap scenario remains absent from the added tests; non-hour transitions and installed timezone-database parity were assessed statically rather than executed.
  11. Expected results: every new golden-output group agrees with its query and the inspected arithmetic/NULL semantics, including 82800/90000-second transitions, overlap offsets, shifted gaps, lexical order_qt ordering and boundary comparison truth values. The author reports script generation; generation provenance was not independently reproduced.
  12. Observability: existing date-range errors provide input and interval context. Pure scalar arithmetic adds no distributed operation needing new metrics or logging.
  13. Transactions and persistence: no EditLog, replay, failover, visible-version or delete-bitmap path changes.
  14. Writes and atomicity: no production storage-write or crash-recovery changes. Source columns are read only and results use newly owned columns.
  15. FE/BE variables: no new serialized field. The existing RuntimeState timezone is forwarded through the generic date-operation wrapper, including constant handling.
  16. Performance and memory: the previously reported duplicate civil lookup is removed. New BE arithmetic uses fixed-size local intermediates and required timezone lookups; no new owned buffer, per-row column copy or untracked container was introduced. No benchmark was run.
  17. Additional sweep: checked all preferred-offset helper callers, source and target calendar limits, final UTC validity, historical second offsets, subtraction overloads, unrelated plusDays callers, alias registration, monotonicity consumers, include/header conventions and all changed paths. No additional verified issue or unresolved static coverage gap remains.

Existing review context and user focus:

  • P2 gap-rounding thread: independently confirmed still applicable. The current HOUR/MINUTE floor code computes a local civil grid and calls the changed preferred-offset converter. In New York, the reported hour_floor example still resolves skipped 02:30 to 03:30, later than the 03:15 input. The author's reference to follow-up PR #70204 does not change this head. This concern is already covered and is not reposted.
  • P2 inverse-range thread: addressed by the new FE-only inverse-fold and DateLiteral gate, including the +08:00 source-local-year overflow scenario. The original predicate is retained when the inverse is invalid.
  • Duplicate-lookup thread: addressed by reuse of the existing civil lookup.
  • No existing P0/P1 inline finding was identified. No additional user-provided focus was supplied.

Validation was static inspection only. No builds, tests, benchmarks, source edits or submodule initialization were performed. Runtime behavior, installed timezone-database agreement, golden-file generation and final artifacts were not independently executed or validated. Three reviewers returned NO_NEW_VALUABLE_FINDINGS in round 1; all candidates and risk items have evidence-backed dispositions.

Review converged after 1 round(s).

@jacktengg

Copy link
Copy Markdown
Contributor Author

run buildall

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants