Repository navigation
[fix](arrow) Correct year-zero DATE conversion on branch-4.1 - #68649
Conversation
|
run buildall |
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
|
/review |
There was a problem hiding this comment.
Static review of PR #68649 at 5945bab. Reviewed all nine files in the authoritative merge-base diff and relevant Arrow, ORC, Parquet, Iceberg, scanner, and aggregation paths. No additional user focus was provided. No existing inline threads or live comments apply.
Findings: four new inline issues (three P1, one P2): Iceberg DATE partition/file ordinal mismatch; ORC/Parquet MIN/MAX metadata bypass of an invalid interior DATE; ORC SARG/strict Parquet pruning that suppresses the new DATE error; and non-strict nullable Parquet IS NULL/COUNT(d) wrong results from physical versus logical nulls.
Critical checkpoint conclusions:
- Goal and proof: The intended proleptic Gregorian year-zero DATE correction works in direct SerDe conversions by static inspection. Added calendar/ORC unit tests and Arrow Flight/OUTFILE regression cases cover representative boundary values, but do not cover the four downstream failures. Tests and builds were prohibited and were not run; checked expected output files and author claims are not independent runtime proof.
- Scope: The changed DATE conversion code and tests are focused. The explicitly excluded legacy FileScanner v1 path remains a known scope limitation, not a new inline finding.
- Concurrency and lifecycle: The edited conversions and statistics are scan/write-local or constexpr; no new threads, locks, lock order, ownership cycle, or cross-translation-unit static initialization dependency was found. Existing reader close/pushdown lifecycle is material to the metadata bypass findings.
- Configuration: No new option is introduced. Existing enable_strict_cast reaches the strict Parquet error path; no dynamic configuration propagation issue was found.
- Compatibility and storage: The corrected external DATE ordinal changes year-zero file interpretation and old incorrectly encoded exports remain ambiguous, as described in the PR. New Iceberg file payload and partition metadata disagree for year-zero values (M1).
- Parallel paths and conditions: Arrow/Parquet/ORC normal DATE decoding, ORC statistics/SARG, Parquet footer/page indexes, aggregate pushdown, and Iceberg partition transforms were traced. Year-zero leap-day rejection is not consistently preserved by metadata shortcuts (M2-M4).
- Coverage/results: The added tests cover direct calendar conversion, ORC nested/null cases, Flight SQL, and export output. They lack partitioned Iceberg round trips and invalid-interior metadata/predicate/nullable aggregate cases. No test was run under the review restrictions.
- Observability: New conversion errors include the encoded bad DATE value; no separate logging or metric defect was substantiated. Metadata paths can suppress that error as described inline.
- Transaction, persistence, and writes: No EditLog or transaction protocol change is present. External file writes retain the existing lifecycle, but an Iceberg committed partition value can contradict its corrected DATE payload (M1); no separate crash or atomicity issue was substantiated.
- FE/BE variables: No new transmitted variable or thrift field is added. Existing strict-cast and aggregate request paths were checked.
- Performance and remaining issues: The conversion is constant time per value; no distinct regression was substantiated. The four inline issues are the remaining correctness findings. BE null handling aligns selected ORC rows and skips null payload decoding, but nullable Parquet conversion failure creates a logical NULL that physical metadata cannot currently represent (M4).
Review execution reached the permitted three rounds and all discovered candidates were resolved. Convergence is incomplete under the review prompt because the third round produced valuable finding M4; no fourth round is permitted. This is a static review only.
|
run buildall |
|
/review |
There was a problem hiding this comment.
Static review complete for head 0842fe5d56e88da5faf91e27fe84f35c3e559394 (base c6536cfde23bf8ad39d87a4498e4ea7eb59111a8). I reviewed all 16 changed files and the related reader, planner, and writer paths. Two new aggregate bypasses remain: ORC COUNT of DATE, including DATE descendants, and Parquet COUNT of a complex root containing DATE. Both skip a strict DATE conversion error; see the inline findings. The four earlier inline concerns (Parquet scalar NULL/COUNT, unsafe DATE pruning, invalid-interior MIN/MAX, and Iceberg day/bucket partitions) are fixed on this head, so none is repeated or listed as an existing blocker. The review converged after three rounds, with no unresolved candidate. The focus file supplied no additional focus points.
Part 1.3 checkpoint conclusions:
- Goal and proof: The Gregorian external DATE ordinal and Iceberg partition alignment are implemented across Arrow, ORC, and Parquet. Boundary, invalid-value, pruning, aggregate, Flight, outfile, and Iceberg cases are present; the two COUNT paths above remain gaps.
- Scope: Changes are focused on DATE conversion, metadata guards, partition transforms, and related tests.
- Concurrency: No new thread, lock, shared mutable state, or changed lock order is introduced.
- Lifecycle and initialization: Conversion scratch is local and constants are compile-time; no new ownership cycle or cross-translation-unit static initialization dependency was found.
- Configuration: No configuration item or dynamic-change requirement is added.
- Compatibility: Doris-authored old physical files containing year-zero DATE values are ambiguous under the corrected encoding and require re-export as described in the PR context. Standard-compliant files now decode consistently; no protocol or symbol change was found.
- Parallel paths: DATE32/DATE64, nullable and nested values, ORC and Parquet readers, metadata shortcuts, and Iceberg day/bucket were traced. The remaining parallel-path gaps are the two inline COUNT findings.
- Conditions: Null payload, int32 range, invalid 0000-02-29, and raw statistics interval checks follow the external encoding. The Parquet root-only COUNT condition misses DATE descendants.
- Tests: Unit and regression coverage includes boundaries, invalid ordinals, pruning on/off, metadata, and external readback; scalar and nested aggregate error regressions are still needed as described inline. No tests were run in this static review.
- Expected results: The changed Flight and outfile results match their queries and deterministic ordering; Iceberg assertions compare rows and partition values. External Iceberg execution was not independently verified here.
- Observability: New conversion errors carry the bad ordinal or row/column context; no additional metric or long-running operation was identified.
- Transactions and persistence: No EditLog or transaction-protocol change is introduced. Existing Iceberg commit metadata uses the corrected dynamic partition value.
- Writes and crashes: The Iceberg write path's payload and partition ordinal agree for valid DATE values; no new write-lifecycle or crash-recovery mechanism was found.
- FE/BE variables: None added or transmitted.
- Performance: Added arithmetic and range checks are constant-time; the ORC file-statistics walk occurs during setup. No separate material regression was substantiated.
- Other issues: Null carriers, selected stripes and Row Groups, page/footer pruning, Arrow failure propagation, all-NULL metadata, and output ordering were rechecked. No further distinct issue remained.
This is a static code review only. The review contract prohibited builds and tests; author and CI test claims were not treated as independent runtime validation.
BE UT Coverage ReportIncrement line coverage Increment coverage report
|
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
|
run buildall |
|
/review |
There was a problem hiding this comment.
Review result
Complete static review at 15768f335c1981efb3786e1457d1257610936a80. I found one P1 compatibility issue and two P2 pruning regressions; the three inline comments give the triggering cases and fixes. All three review rounds converged with no new valuable findings in the final round. No builds or tests were run under the review contract.
I rechecked all six existing inline issues against this head. The prior P1 Iceberg partition, DATE MIN/MAX, Parquet NULL/COUNT, and ORC COUNT concerns are addressed by the current code and tests; the P2 invalid-DATE pruning and nested Parquet COUNT concerns are also addressed. None remains a blocking P0/P1 issue. The new comments cover distinct V1/mixed-BE decoding, unprojected ORC DATE SARG gating, and Parquet safe-page pruning.
Critical checkpoints
- Goal and proof: The change maps Doris DATE values to proleptic-Gregorian external ordinals across Arrow, Parquet, ORC, and Iceberg, including year zero and the invalid Gregorian-only day. Calendar, format-reader, Flight, export, and Iceberg tests cover same-version paths. The goal is incomplete for the V1 and mixed-version reads in the P1 comment.
- Scope and clarity: The production change is focused on DATE conversion, partition transforms, and statistics safety. Shared conversion helpers are constant-time and make the boundary explicit. The persisted writer change has a wider rollout effect than the focused code suggests.
- Concurrency and locks: The changed conversion and reader paths execute in existing scan/write contexts; they add no thread, lock, shared mutable state, or lock-order dependency. I found no new concurrency issue.
- Lifecycle and initialization: The new helpers are inline/constexpr and the reader checks use local state. No cross-translation-unit initializer, retained allocation, ownership change, or lifecycle leak is introduced.
- Configuration: No new configuration item or dynamic-update requirement is introduced. The existing
enable_file_scanner_v2choice exposes the V1 decoder mismatch in the P1 finding. - Compatibility and rolling upgrades: There is no new FE/BE protocol or storage schema, but the persisted DATE ordinal changes. Newly written year-zero files can be misread by selectable V1 readers in the same build and rejected by older BEs during smooth upgrades. The PR discloses re-export of historical incorrectly encoded files; that separate migration limitation is not a new inline finding.
- Parallel paths and conditions: V2 Parquet/ORC, Arrow, Iceberg day/bucket, NULL handling, selected-stripe COUNT, projected Parquet COUNT, and invalid-DATE MIN/MAX were traced. V1 DATE readers remain incompatible; the ORC SARG guard checks omitted nested DATE leaves; one unsafe Parquet DATE page disables pruning of safe pages. The inline comments specify each affected condition.
- Tests and results: I inspected all changed unit tests, the ORC fixture, all four regression suites, and their three result files. The result ordering and expected values match the test code as static evidence. The added tests do not cover V1/mixed-version reads, an omitted ORC DATE sibling, or a multi-page Parquet index with one unsafe page. Author-reported ASAN results are not independent validation; this review ran no tests.
- Observability and errors: New DATE conversion failures report the raw ordinal; existing scan errors and logs remain available. I found no new metrics or log requirement. The two pruning findings cause avoidable I/O rather than a new failure message.
- Transactions, persistence, and writes: No EditLog, visible-version, delete-bitmap, or transaction commit path changes. Parquet/ORC/Iceberg writes do persist the changed ordinal, which is the P1 compatibility issue. The reviewed write code introduces no separate atomicity or crash-recovery defect.
- FE/BE variables: No new variable crosses FE/BE paths; existing scanner selection and mixed-BE planning are sufficient to expose the compatibility issue.
- Performance and remaining issues: Per-value conversion is O(1). The ORC and Parquet findings can read many otherwise prunable stripes or pages. After checking all 19 changed paths and parallel reader paths, I found no other substantiated issue.
User focus: No additional review focus was provided.
| checkArrowStatus(date32_builder.Append(cast_set<int, int64_t, false>(daynr)), | ||
| column, *array_builder)); | ||
| RETURN_IF_ERROR(checkArrowStatus( | ||
| date32_builder.Append(daynr_to_epoch_days(col_data[i].daynr())), column, |
There was a problem hiding this comment.
[P1] Keep new year-zero DATE files readable by the other scanner and older BEs. This writes 0000-01-01 as -719528 to Parquet (and the ORC writer now does likewise), but the v1 Parquet/ORC readers still decode through date_day_offset_dict when enable_file_scanner_v2=false: its failed daynr-zero fallback leaves 1900-01-01 as the result. A pre-upgrade v2 Parquet reader rejects the same ordinal. A newly committed Iceberg or exported file can therefore return a wrong DATE in a same-build v1 scan or fail on an older BE during a rolling upgrade. Update the legacy readers and gate the persisted encoding until older BEs are gone, with cross-reader and mixed-version coverage.
There was a problem hiding this comment.
V1 reader changes and mixed-version/rolling-upgrade compatibility are explicitly outside the requested scope of this PR. No changes are being made for this finding. The PR description now states this scope, and this thread remains open rather than being marked fixed.
|
run buildall |
|
/review |
There was a problem hiding this comment.
Review complete for head 768c42200314322850ceb90d8f1472db8716c289 (static review only).
Findings: One new P2 inline finding: an unsafe projected ORC DATE disables SARG pruning for safe stripes in the same file. The existing P1 V1/rolling-upgrade compatibility thread still applies: the legacy Parquet/ORC readers use the old DATE offset dictionary, and older BEs cannot safely read new year-zero ordinals. I confirmed it independently and list its ID as an existing blocker instead of repeating it. Earlier DATE MIN/MAX, NULL-statistics, COUNT, unread-sibling, and Parquet-page concerns are addressed in the current code; no other new actionable finding survived two review rounds and the final 19-file sweep.
Critical checkpoints
- Goal and tests: the shared epoch-day helpers align year-zero DATE values across Arrow, V2 Parquet/ORC, and Iceberg day/bucket partitions. The added unit and regression cases cover boundaries, NULLs, invalid ordinals, nested DATEs, COUNT, and pruning. They were inspected, not run in this invocation; the external Iceberg regression has no independent runtime result here.
- Scope and parallel paths: changes are focused on DATE conversion, metadata safety, and corresponding tests. V2 read/write and partition paths were traced. The unchanged V1 readers and older BE compatibility remain the existing P1.
- Concurrency and lifecycle: no new threads, shared mutable state, locks, lock ordering, or cross-translation-unit initialization dependency was introduced. New reader scratch/statistics checks are per-call or per-reader and follow existing cleanup and error propagation.
- Configuration and protocol: no new dynamic configuration, FE/BE field, function symbol, or wire protocol was added. Existing strict-mode and scanner settings were considered in the read-path review.
- Conditions and errors: representability checks handle bounds and the interior Gregorian-only leap day; unsafe metadata falls back to materialization. Projected ORC leaves and Parquet nested COUNT/page paths were checked against their actual readers. The new P2 is the remaining overly broad ORC condition.
- Results, observability, and performance: expected output rows and golden-file contents were inspected statically. Existing stripe/page counters can show pruning behavior; the P2 case needs a mixed-stripe regression to prove safe-stripe pruning. No other concrete hot-path regression was substantiated.
- Persistence, writes, and atomicity: external DATE files and Iceberg partition values change their persisted ordinals. No new transaction or EditLog path, commit step, or crash window was introduced; the existing V1/mixed-version P1 is the material persistence risk.
User focus: none was provided. No builds, tests, submodule initialization, or repository source changes were performed.
Existing P0/P1 findings confirmed for this head: #68649 (comment)
BE UT Coverage ReportIncrement line coverage Increment coverage report
|
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
|
run buildall |
|
/review |
|
Updated in 7c53e83 after local validation. The external group6 failure comes from Validation before this push:
Integration used the final changes rebuilt into an existing ASAN/UBSAN BE with an existing FE. HDFS and S3 transport were replaced by local file access for the local run; committed CI suites retain their original transport. A full branch-4.1 build and those external transports remain for CI. The separately muted S3 connection timeout also occurs on unrelated PRs and has no evidence tying it to this DATE change. Scope remains unchanged: legacy v1 scanner changes and rolling-upgrade compatibility are excluded; that existing thread remains open. |
There was a problem hiding this comment.
Static review of PR #68649 at head 7c53e83. I reviewed all 20 changed paths and the affected scanner, writer, aggregate, partition, and SDK paths. Three new P1 findings are inline: historical Doris DATE files read incorrectly after upgrade (M2), ORC DATE preflight exposing an unchecked row-index access and avoidable index I/O (M1), and nested ORC COUNT accessing short stripe statistics out of bounds (M4). Existing P1 comment 4140089872 still applies to newly written files read by the v1 scanner or older BEs; it is not duplicated. The other earlier P1 threads are addressed by this head.
Review status: INCOMPLETE convergence. The third and final permitted review round produced M4. I independently validated it and included it here, but the workflow prohibits a fourth convergence round. All current candidates have a clear disposition.
Critical checkpoints:
- Goal and proof: The intended year-zero DATE correction maps Doris day numbers to external proleptic Gregorian ordinals and protects metadata shortcuts. New calendar, ORC/Parquet, Flight, outfile, and Iceberg tests cover the target boundaries and read/write paths. Historical writer fixtures are absent, so M2 is untested by this PR.
- Scope and clarity: The production changes are focused on DATE conversion, external partition transforms, and metadata pruning; the additional files are targeted tests and expected output.
- Concurrency and lifecycle: No new thread, lock, shared static initializer, or cross-thread state is introduced. I traced per-reader ORC range state, active RowReaderOptions lifetime, and the pinned SDK's cached SARG evaluator through temporary and surviving readers; no separate lifecycle bug was found.
- Configuration: No new configuration item or dynamic setting is added. The existing v1 scanner switch remains a compatibility path affected by open P1 4140089872.
- Compatibility: Parquet/ORC DATE ordinals and Iceberg partitions are persisted externally. M2 affects already committed old files; the existing P1 affects new files on v1/older BEs. No FE/BE symbol, Thrift field, or transmitted variable changes are present.
- Parallel paths and conditions: Arrow, ORC, Parquet, and Iceberg writes/reads were traced, including nested DATE projection, strict/non-strict NULL conversion, unknown statistics, page/stripe pruning, and COUNT/MINMAX. M1 and M4 identify two distinct unsafe ORC metadata paths; other checked fallback conditions preserve row validation.
- Test coverage and expected results: The added boundary, nested aggregate, pruning, selected-row, Flight, outfile, Iceberg, and group6 assertions and golden rows are consistent with their inputs. Old-writer file fixtures, a malformed pruned row index, and truncated nested stripe statistics are missing. This review was static only; I ran no build or test and do not treat author/CI statements as independent validation.
- Observability: Conversion failures have ordinal context and ORC fallback paths include file context. No new metric measures the pre-pruning index I/O in M1; an I/O-count regression is warranted.
- Persistence, transactions, and writes: Existing Parquet/ORC sinks persist the changed ordinal, and Iceberg persists matching partition values. There is no new EditLog, visibility-version, commit, or failover path. The persisted-file compatibility gaps above are the material write/read risk; M1 and M4 add malformed-file memory-safety risk.
- Performance and remaining concerns: M1 adds footer/index reads and parsing for SARG-pruned stripes, potentially repeated by physical child readers. The final sweep found no other substantiated new issue. No extra user review focus was supplied.
Request changes for the three new P1 findings and the still-open existing P1.
Existing P0/P1 findings confirmed for this head: #68649 (comment)
|
run buildall |
|
/review |
There was a problem hiding this comment.
Static review of PR #68649 at 082429b00f2392f8e8eabcee6e6a5a94a403e7aa. The review is complete: the main scan, two full-coverage subagent reviews, two focused risk scans, candidate convergence, and final sweep found no new valuable inline finding. Existing P1 comment 4140089872 still applies and is carried below; it is not reposted. The other existing DATE correctness and safety threads were checked against this head and their fixes or migration procedure are present.
Critical checkpoints:
- Goal and proof: The patch aligns year-zero DATE conversions across Arrow, default Parquet/ORC paths and Iceberg day/bucket partitions, while rejecting external ordinals Doris cannot represent. Added unit and regression cases inspect the year-zero boundaries, NULL/nested values, aggregate and pruning paths, malformed ORC metadata, and readback. This invocation was static-only: no build, test, or runtime validation was performed; author-reported ASAN/integration results and the pending external Iceberg execution are not independent validation.
- Scope and clarity: Production edits are limited to the DATE SerDe/calendar helper, Iceberg partition transforms, and ORC/Parquet reader metadata guards. The standalone migration utility handles confirmed historical files. No unrelated data format or generic scanner contract was changed.
- Concurrency and lifecycle: The new ORC options and DATE stripe state are per reader. The pinned SDK's cached SARG evaluator is consumed while its options live; replacing active options follows RowReader reset, and range advancement rebuilds the reader. No new thread, lock, lock order, cross-TU initializer, or persistent ownership cycle was found. Migration closes/fsyncs its temporary output before exclusive publication and removes it on errors.
- Configuration and compatibility: No configuration item or FE-to-BE variable was added. Existing strict-cast and pruning settings drive these paths. The persisted DATE ordinal change is incompatible with the unchanged v1 Parquet/ORC readers and older BEs during mixed-version writes: the still-open P1 4140089872 remains blocking. Historical files written with the old encoding require provenance-based migration before switching consumers; the new offline utility and procedure address existing thread 4146799308, but do not infer origin automatically.
- Parallel paths and conditions: ORC projected DATE collection matches the physical projection; metadata-only stripe preflight retains unsafe/unknown stripes and disables row-index SARG only for those ranges. Parquet validates requested DATE leaves before COUNT shortcuts; unsafe footer/page ranges invalidate physical NULL proofs and retain page candidates. TableReader permits metadata aggregates only with the required no-filter/no-delete and mapping conditions. The existing v1 path remains the compatibility exception noted above.
- Errors, memory, and observability: Invalid DATE values return checked errors or nullable non-strict conversion NULLs. ORC errors include the column and raw ordinal. Changed metadata failures decline shortcuts or retain rows; no ignored Status or new unsafe nullable-column cast was found. Temporary buffers/options are reader-scoped. Existing stripe and I/O counters remain in use; no new critical metric was identified.
- Persistence, writes, and performance: Iceberg day and bucket values now use the same external epoch day as file payloads. No Doris EditLog, transaction, delete bitmap, or visible-version behavior changes. The migration publishes separate files and documents table-format transactional rewrite requirements. DATE preflight uses file-level stripe metadata; safe stripes/pages remain eligible for pruning. No substantiated new hot-loop or I/O amplification was found.
- Test results and focus: New
.outfiles carry generated-file headers and ordering is specified in the changed suites. The user focus file contains no additional focus points. Review claims here are limited to source and test inspection.
Disposition: no new inline comments; carry existing blocking P1 4140089872. Review execution is complete, while the PR retains that unresolved compatibility finding.
Existing P0/P1 findings confirmed for this head: #68649 (comment)
BE UT Coverage ReportIncrement line coverage Increment coverage report
|
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
### What problem does this PR solve? Related issue: #67366 Related PR: #67449 On branch-4.1, Arrow Flight SQL returns `0000-01-02` for a stored DATE of `0000-01-01`. Doris follows the MySQL calendar, which excludes year zero's leap day, while Arrow DATE uses the proleptic Gregorian calendar. Subtracting the Unix epoch day number directly shifts dates in `0000-01-01` through `0000-02-28` by one day, including elements of `ARRAY<DATE>`. Adapt the DATE portion of #67449 to branch-4.1, with credit to its author. Convert between the calendars at Arrow and ORC write boundaries and Arrow/default Parquet/ORC read boundaries. Use the same conversion for ORC statistics so pushed-down MIN/MAX agrees with row decoding. Reject unrepresentable external dates and ignore undefined payloads in null Arrow slots. The two DATE regression suites and their expected results are copied unchanged from #67449. Add its DATE unit coverage plus an explicit null-payload test and an exhaustive comparison against the civil calendar for all 3,652,424 representable dates. Also align Iceberg DATE day/bucket partition transforms with file ordinals. For ORC/Parquet, metadata is usable only when its raw DATE range proves that every ordinal is representable, including the interior of the range. Unsafe ranges fall back to decoding: strict reads retain their errors, non-strict Parquet reads retain conversion-generated NULLs, and COUNT uses materialized values when physical null counts are insufficient. ORC uses a metadata-only SARG to check projected DATE leaves per stripe: safe excluded stripes retain SARG pruning, while unsafe stripes remain readable with SDK row-group pruning disabled. ORC COUNT validates every DATE leaf of its argument in each selected stripe, and strict Parquet COUNT validates projected DATE descendants before reading only levels. Scope: DATE, including its Iceberg partition transforms. This does not include DATETIME changes or the independent legacy file scanner conversions used when `enable_file_scanner_v2=false`. V1 reader changes and mixed-version/rolling-upgrade compatibility gates are explicitly outside this PR's scope. ### Validation - Baseline: 9 of the original 10 DATE unit tests fail; the unchanged Flight regression reproduces the one-day shift. - Fixed: 250 unit tests pass under ASAN, including the complete ORC reader suite, DATE SerDe exhaustive calendar check, Iceberg partition transforms, Parquet statistics, DATE scans and COUNT tests. These compile the modified branch-4.1 sources against cached test dependencies. - The unchanged `test_date_year_zero` Flight regression passes locally, including nullable and array values. - `test_outfile_date_year_zero` passes for Parquet and ORC using the original rows and golden assertions, with S3 transport replaced by local outfile/TVF only for the local run. The committed suite retains S3 transport for CI. - Local integration runs use the same fix rebuilt into an existing ASAN/UBSAN BE build at `5bcb996a5ff`, with an existing FE. A full branch-4.1 build and the original S3 run remain for CI. - clang-format 16 passes for all affected C++ files. Source whitespace checks pass; generated golden files retain their conventional trailing blank line from #67449. - Review coverage: new tests reproduce partition mismatches, invalid-interior MIN/MAX bypass, ORC SARG/Parquet footer/page pruning inconsistencies, and Parquet IS NULL/COUNT discrepancies. All five reproducing tests fail before the review fixes. - Added a DATE-only Spark/Iceberg partitioned write/read regression, including selective day/bucket predicates and partition metadata comparison. Its Groovy compilation passes; execution requires the external CI environment. - Follow-up review: scalar/nested ORC COUNT and strict nested Parquet COUNT reproductions fail before the guards and pass afterward. ARRAY, STRUCT, MAP, all-null values, safe ranges, non-strict Parquet, and unprojected DATE siblings are covered. - External ORC regression: retain the original LZ4 file as a BE fixture and verify that ordinal `-719530` raises a data-quality error. Replace the obsolete `1900-01-01` golden result with an explicit error assertion, restoring the scanner setting afterward. The fixture test and Groovy compilation pass locally. The complete group6 suite is also validated locally with HDFS transport replaced by LOCAL TVF; the committed suite retains HDFS for CI. - Pruning review: both new tests fail before the fixes and pass afterward. Partial ORC STRUCT projection retains SARG when the invalid DATE sibling is not read; selecting that DATE still preserves its error. Multi-page Parquet tests retain the unsafe DATE page while pruning safe pages for IS NULL and range predicates, through both single-slot and multi-slot OR evaluators. - Mixed-stripe ORC coverage: the original file-wide guard prunes zero safe stripes; the fix prunes both safe excluded stripes and still reports the invalid DATE in the retained stripe. The active SDK options outlive their reader, and the cached SARG evaluator is discarded before opening an unsafe range. - External group6 coverage: the original `non_vec_orc_scanner.orc` fixture encodes `20191111` as an invalid epoch-day ordinal. Replace its obsolete `1900-01-01` golden result with an explicit data-quality error assertion and keep the original bytes as a small BE fixture; projecting other columns still succeeds. - Final local integration after the stripe fix: all three suites pass with no failures or skips (complete group6: 41 fixtures, 39 golden-result assertions and two expected errors; Flight year zero; Parquet/ORC outfile year zero). These use the incremental ASAN/UBSAN build and local transport substitutions described above. - Metadata safety review: the malformed pruned-stripe index and truncated nested DATE statistics reproduce SDK bounds assertions before the fix. Both regressions pass afterward; the DATE preflight now adds no file read calls or bytes compared with the same excluded stripe without DATE projection. - Historical-file migration: an explicit offline tool converts only confirmed legacy Doris DATE ordinals, including nested values. It publishes a separate output atomically and refuses overwrites. Tests cover year-zero boundaries, NULLs, hidden null-container payloads, nested ORC/Parquet round trips, schema metadata, timestamp precision, and failed-write cleanup. - Final follow-up validation: all 250 ASAN tests and 10 migration-tool tests pass. The final incremental production BE passes the three existing local suites plus six legacy migration/read/export/read checks, covering both ORC and Parquet. Original HDFS/S3 transport and the full branch build remain for CI as described above. ### Release note Fix year-zero DATE values being returned one day late through Arrow Flight SQL, align DATE encoding and decoding in the default Parquet/ORC paths, and keep Iceberg DATE partition values consistent with file contents. Existing files written with the former year-zero encoding cannot be distinguished automatically from correctly encoded files. Before switching their consumers to the corrected reader, use the explicit [legacy DATE migration tool and procedure](https://github.com/apache/doris/blob/082429b00f2392f8e8eabcee6e6a5a94a403e7aa/tools/legacy_date_migration/README.md) on files confirmed to use the old Doris encoding, or re-export from authoritative original data. The tool writes new ORC/Parquet files without modifying the source. Existing Iceberg files must be migrated through a new table or a supported table-format transaction that recomputes partition metadata; do not replace files in existing snapshots. The proleptic-only `0000-02-29` remains unrepresentable as a Doris DATE. ### Check List (For Author) - Test - [x] Regression test - [x] Unit Test - Behavior changed: - [x] Yes, external DATE ordinals use the correct calendar; internal DATE arithmetic is unchanged. - Does this need documentation? - [x] No. ### 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: apache#69581, apache#68649 Problem Summary: Reproduction: read an Arrow Date32 array holding a day outside 0000-01-01..9999-12-31 into a Doris DATE column, for example 2932897 (10000-01-01) written by PyArrow into a Lance dataset, or the same value returned by an ADBC driver, an Arrow stream load or the paimon-rust reader. The row comes back as a non-null `0000-00-00`, also inside ARRAY, STRUCT and MAP values. Root cause: the DATE32 branch of the shared `DataTypeDateV2SerDe::read_column_from_arrow` ignores the bool returned by `get_date_from_daynr()` (dropped since apache#48944) and appends the default-constructed value. It also adds the epoch offset in 32 bits, so INT32_MAX overflows, and it decodes the payload of NULL slots. The DATE64 branch next to it already rejects out-of-range days. Fix: port the DATE32 range part of the branch-4.1/4.2 fix apache#68649. NULL slots append the default value without being decoded, the day number is computed in 64 bits, and a day number <= 0, above DATE_MAX_DAYNR or rejected by `get_date_from_daynr()` returns `InvalidArgument("Arrow Date32 value is outside the Doris DATE range: row=..., days=...")` with the 4.x wording. ARRAY/STRUCT/MAP leaves get the same check through their element SerDes. `write_column_to_arrow` and the year-zero calendar mapping (the other part of apache#68649, and master draft apache#67449) are unchanged; the new tests avoid the days whose meaning that mapping changes. Before: out-of-range days read as `0000-00-00`. After: the read fails with the error above; in-range values and NULLs read as before. The Lance reader ported in apache#69581 (from the branch-4.2 snapshot, which already contains apache#68649) relies on this SerDe check. ### Release note Reading Arrow DATE32 values outside 0000-01-01..9999-12-31 (ADBC, Arrow stream load, paimon-rust, Lance) now fails instead of returning 0000-00-00. ### Check List (For Author) - Test: Unit Test - New: `DataTypeDateTimeV2SerDeTest.RejectsInvalidDate32Values`, `LanceTableReaderTypeTest.RejectsDate32OutsideDorisDateRange`, `LanceTableReaderTypeTest.ReadsLastDorisDateAndIgnoresNullDate32Payload`. - All pass on macOS arm64 (ASAN), together with `DataTypeDateTimeV2SerDeTest.*`, `DataTypeSerDeArrowTest.*`, `DataTypeSerDeArrowValidationTest.*`, `ArrowColumnToDorisColumnTest.*`, `ParquetExprTest.*`, `IcebergV2ReaderTest.*` and the Lance suites. With master's DATE32 branch restored, the first two fail. - `DataTypeDateTimeV2SerDeTest` aborts in its setup under macOS ASAN on master: `load_data_from_csv()` in `be/test/testutil/test_util.cpp` dereferences `std::set::end()`. It ran here with that assertion removed locally; that fix goes in a separate PR. - Behavior changed: Yes. An Arrow DATE32 day outside 0000-01-01..9999-12-31 used to be returned as a non-null 0000-00-00; the scan now fails with an InvalidArgument error, as Arrow DATE64 values already did. - Does this need documentation: No Co-authored-by: Gabriel <liwenqiang@selectdb.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
### What problem does this PR solve? Issue Number: None Related PR: apache#69581, apache#68649 Problem Summary: Reproduction: read an Arrow Date32 array holding a day outside 0000-01-01..9999-12-31 into a Doris DATE column, for example 2932897 (10000-01-01) written by PyArrow into a Lance dataset, or the same value returned by an ADBC driver, an Arrow stream load or the paimon-rust reader. The row comes back as a non-null `0000-00-00`, also inside ARRAY, STRUCT and MAP values. Root cause: the DATE32 branch of the shared `DataTypeDateV2SerDe::read_column_from_arrow` ignores the bool returned by `get_date_from_daynr()` (dropped since apache#48944) and appends the default-constructed value. It also adds the epoch offset in 32 bits, so INT32_MAX overflows, and it decodes the payload of NULL slots. The DATE64 branch next to it already rejects out-of-range days. Fix: port the DATE32 range part of the branch-4.1/4.2 fix apache#68649. NULL slots append the default value without being decoded, the day number is computed in 64 bits, and a day number <= 0, above DATE_MAX_DAYNR or rejected by `get_date_from_daynr()` returns `InvalidArgument("Arrow Date32 value is outside the Doris DATE range: row=..., days=...")` with the 4.x wording. ARRAY/STRUCT/MAP leaves get the same check through their element SerDes. `write_column_to_arrow` and the year-zero calendar mapping (the other part of apache#68649, and master draft apache#67449) are unchanged; the new tests avoid the days whose meaning that mapping changes. Before: out-of-range days read as `0000-00-00`. After: the read fails with the error above; in-range values and NULLs read as before. The Lance reader ported in apache#69581 (from the branch-4.2 snapshot, which already contains apache#68649) relies on this SerDe check. ### Release note Reading Arrow DATE32 values outside 0000-01-01..9999-12-31 (ADBC, Arrow stream load, paimon-rust, Lance) now fails instead of returning 0000-00-00. ### Check List (For Author) - Test: Unit Test - New: `DataTypeDateTimeV2SerDeTest.RejectsInvalidDate32Values`, `LanceTableReaderTypeTest.RejectsDate32OutsideDorisDateRange`, `LanceTableReaderTypeTest.ReadsLastDorisDateAndIgnoresNullDate32Payload`. - All pass on macOS arm64 (ASAN), together with `DataTypeDateTimeV2SerDeTest.*`, `DataTypeSerDeArrowTest.*`, `DataTypeSerDeArrowValidationTest.*`, `ArrowColumnToDorisColumnTest.*`, `ParquetExprTest.*`, `IcebergV2ReaderTest.*` and the Lance suites. With master's DATE32 branch restored, the first two fail. - `DataTypeDateTimeV2SerDeTest` aborts in its setup under macOS ASAN on master: `load_data_from_csv()` in `be/test/testutil/test_util.cpp` dereferences `std::set::end()`. It ran here with that assertion removed locally; that fix goes in a separate PR. - Behavior changed: Yes. An Arrow DATE32 day outside 0000-01-01..9999-12-31 used to be returned as a non-null 0000-00-00; the scan now fails with an InvalidArgument error, as Arrow DATE64 values already did. - Does this need documentation: No Co-authored-by: Gabriel <liwenqiang@selectdb.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
### What problem does this PR solve? Issue Number: None Related PR: apache#69581, apache#68649 Problem Summary: Reproduction: read an Arrow Date32 array holding a day outside 0000-01-01..9999-12-31 into a Doris DATE column, for example 2932897 (10000-01-01) written by PyArrow into a Lance dataset, or the same value returned by an ADBC driver, an Arrow stream load or the paimon-rust reader. The row comes back as a non-null `0000-00-00`, also inside ARRAY, STRUCT and MAP values. Root cause: the DATE32 branch of the shared `DataTypeDateV2SerDe::read_column_from_arrow` ignores the bool returned by `get_date_from_daynr()` (dropped since apache#48944) and appends the default-constructed value. It also adds the epoch offset in 32 bits, so INT32_MAX overflows, and it decodes the payload of NULL slots. The DATE64 branch next to it already rejects out-of-range days. Fix: port the DATE32 range part of the branch-4.1/4.2 fix apache#68649. NULL slots append the default value without being decoded, the day number is computed in 64 bits, and a day number <= 0, above DATE_MAX_DAYNR or rejected by `get_date_from_daynr()` returns `InvalidArgument("Arrow Date32 value is outside the Doris DATE range: row=..., days=...")` with the 4.x wording. ARRAY/STRUCT/MAP leaves get the same check through their element SerDes. `write_column_to_arrow` and the year-zero calendar mapping (the other part of apache#68649, and master draft apache#67449) are unchanged; the new tests avoid the days whose meaning that mapping changes. Before: out-of-range days read as `0000-00-00`. After: the read fails with the error above; in-range values and NULLs read as before. The Lance reader ported in apache#69581 (from the branch-4.2 snapshot, which already contains apache#68649) relies on this SerDe check. ### Release note Reading Arrow DATE32 values outside 0000-01-01..9999-12-31 (ADBC, Arrow stream load, paimon-rust, Lance) now fails instead of returning 0000-00-00. ### Check List (For Author) - Test: Unit Test - New: `DataTypeDateTimeV2SerDeTest.RejectsInvalidDate32Values`, `LanceTableReaderTypeTest.RejectsDate32OutsideDorisDateRange`, `LanceTableReaderTypeTest.ReadsLastDorisDateAndIgnoresNullDate32Payload`. - All pass on macOS arm64 (ASAN), together with `DataTypeDateTimeV2SerDeTest.*`, `DataTypeSerDeArrowTest.*`, `DataTypeSerDeArrowValidationTest.*`, `ArrowColumnToDorisColumnTest.*`, `ParquetExprTest.*`, `IcebergV2ReaderTest.*` and the Lance suites. With master's DATE32 branch restored, the first two fail. - `DataTypeDateTimeV2SerDeTest` aborts in its setup under macOS ASAN on master: `load_data_from_csv()` in `be/test/testutil/test_util.cpp` dereferences `std::set::end()`. It ran here with that assertion removed locally; that fix goes in a separate PR. - Behavior changed: Yes. An Arrow DATE32 day outside 0000-01-01..9999-12-31 used to be returned as a non-null 0000-00-00; the scan now fails with an InvalidArgument error, as Arrow DATE64 values already did. - Does this need documentation: No Co-authored-by: Gabriel <liwenqiang@selectdb.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
### What problem does this PR solve? Issue Number: None Related PR: apache#69581, apache#68649 Problem Summary: Reproduction: read an Arrow Date32 array holding a day outside 0000-01-01..9999-12-31 into a Doris DATE column, for example 2932897 (10000-01-01) written by PyArrow into a Lance dataset, or the same value returned by an ADBC driver, an Arrow stream load or the paimon-rust reader. The row comes back as a non-null `0000-00-00`, also inside ARRAY, STRUCT and MAP values. Root cause: the DATE32 branch of the shared `DataTypeDateV2SerDe::read_column_from_arrow` ignores the bool returned by `get_date_from_daynr()` (dropped since apache#48944) and appends the default-constructed value. It also adds the epoch offset in 32 bits, so INT32_MAX overflows, and it decodes the payload of NULL slots. The DATE64 branch next to it already rejects out-of-range days. Fix: port the DATE32 range part of the branch-4.1/4.2 fix apache#68649. NULL slots append the default value without being decoded, the day number is computed in 64 bits, and a day number <= 0, above DATE_MAX_DAYNR or rejected by `get_date_from_daynr()` returns `InvalidArgument("Arrow Date32 value is outside the Doris DATE range: row=..., days=...")` with the 4.x wording. ARRAY/STRUCT/MAP leaves get the same check through their element SerDes. `write_column_to_arrow` and the year-zero calendar mapping (the other part of apache#68649, and master draft apache#67449) are unchanged; the new tests avoid the days whose meaning that mapping changes. Before: out-of-range days read as `0000-00-00`. After: the read fails with the error above; in-range values and NULLs read as before. The Lance reader ported in apache#69581 (from the branch-4.2 snapshot, which already contains apache#68649) relies on this SerDe check. ### Release note Reading Arrow DATE32 values outside 0000-01-01..9999-12-31 (ADBC, Arrow stream load, paimon-rust, Lance) now fails instead of returning 0000-00-00. ### Check List (For Author) - Test: Unit Test - New: `DataTypeDateTimeV2SerDeTest.RejectsInvalidDate32Values`, `LanceTableReaderTypeTest.RejectsDate32OutsideDorisDateRange`, `LanceTableReaderTypeTest.ReadsLastDorisDateAndIgnoresNullDate32Payload`. - All pass on macOS arm64 (ASAN), together with `DataTypeDateTimeV2SerDeTest.*`, `DataTypeSerDeArrowTest.*`, `DataTypeSerDeArrowValidationTest.*`, `ArrowColumnToDorisColumnTest.*`, `ParquetExprTest.*`, `IcebergV2ReaderTest.*` and the Lance suites. With master's DATE32 branch restored, the first two fail. - `DataTypeDateTimeV2SerDeTest` aborts in its setup under macOS ASAN on master: `load_data_from_csv()` in `be/test/testutil/test_util.cpp` dereferences `std::set::end()`. It ran here with that assertion removed locally; that fix goes in a separate PR. - Behavior changed: Yes. An Arrow DATE32 day outside 0000-01-01..9999-12-31 used to be returned as a non-null 0000-00-00; the scan now fails with an InvalidArgument error, as Arrow DATE64 values already did. - Does this need documentation: No Co-authored-by: Gabriel <liwenqiang@selectdb.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
### What problem does this PR solve? Issue Number: None Related PR: apache#69581, apache#68649 Problem Summary: Reproduction: read an Arrow Date32 array holding a day outside 0000-01-01..9999-12-31 into a Doris DATE column, for example 2932897 (10000-01-01) written by PyArrow into a Lance dataset, or the same value returned by an ADBC driver, an Arrow stream load or the paimon-rust reader. The row comes back as a non-null `0000-00-00`, also inside ARRAY, STRUCT and MAP values. Root cause: the DATE32 branch of the shared `DataTypeDateV2SerDe::read_column_from_arrow` ignores the bool returned by `get_date_from_daynr()` (dropped since apache#48944) and appends the default-constructed value. It also adds the epoch offset in 32 bits, so INT32_MAX overflows, and it decodes the payload of NULL slots. The DATE64 branch next to it already rejects out-of-range days. Fix: port the DATE32 range part of the branch-4.1/4.2 fix apache#68649. NULL slots append the default value without being decoded, the day number is computed in 64 bits, and a day number <= 0, above DATE_MAX_DAYNR or rejected by `get_date_from_daynr()` returns `InvalidArgument("Arrow Date32 value is outside the Doris DATE range: row=..., days=...")` with the 4.x wording. ARRAY/STRUCT/MAP leaves get the same check through their element SerDes. `write_column_to_arrow` and the year-zero calendar mapping (the other part of apache#68649, and master draft apache#67449) are unchanged; the new tests avoid the days whose meaning that mapping changes. Before: out-of-range days read as `0000-00-00`. After: the read fails with the error above; in-range values and NULLs read as before. The Lance reader ported in apache#69581 (from the branch-4.2 snapshot, which already contains apache#68649) relies on this SerDe check. ### Release note Reading Arrow DATE32 values outside 0000-01-01..9999-12-31 (ADBC, Arrow stream load, paimon-rust, Lance) now fails instead of returning 0000-00-00. ### Check List (For Author) - Test: Unit Test - New: `DataTypeDateTimeV2SerDeTest.RejectsInvalidDate32Values`, `LanceTableReaderTypeTest.RejectsDate32OutsideDorisDateRange`, `LanceTableReaderTypeTest.ReadsLastDorisDateAndIgnoresNullDate32Payload`. - All pass on macOS arm64 (ASAN), together with `DataTypeDateTimeV2SerDeTest.*`, `DataTypeSerDeArrowTest.*`, `DataTypeSerDeArrowValidationTest.*`, `ArrowColumnToDorisColumnTest.*`, `ParquetExprTest.*`, `IcebergV2ReaderTest.*` and the Lance suites. With master's DATE32 branch restored, the first two fail. - `DataTypeDateTimeV2SerDeTest` aborts in its setup under macOS ASAN on master: `load_data_from_csv()` in `be/test/testutil/test_util.cpp` dereferences `std::set::end()`. It ran here with that assertion removed locally; that fix goes in a separate PR. - Behavior changed: Yes. An Arrow DATE32 day outside 0000-01-01..9999-12-31 used to be returned as a non-null 0000-00-00; the scan now fails with an InvalidArgument error, as Arrow DATE64 values already did. - Does this need documentation: No Co-authored-by: Gabriel <liwenqiang@selectdb.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
### What problem does this PR solve? Issue Number: None Related PR: apache#69581, apache#68649 Problem Summary: Reproduction: read an Arrow Date32 array holding a day outside 0000-01-01..9999-12-31 into a Doris DATE column, for example 2932897 (10000-01-01) written by PyArrow into a Lance dataset, or the same value returned by an ADBC driver, an Arrow stream load or the paimon-rust reader. The row comes back as a non-null `0000-00-00`, also inside ARRAY, STRUCT and MAP values. Root cause: the DATE32 branch of the shared `DataTypeDateV2SerDe::read_column_from_arrow` ignores the bool returned by `get_date_from_daynr()` (dropped since apache#48944) and appends the default-constructed value. It also adds the epoch offset in 32 bits, so INT32_MAX overflows, and it decodes the payload of NULL slots. The DATE64 branch next to it already rejects out-of-range days. Fix: port the DATE32 range part of the branch-4.1/4.2 fix apache#68649. NULL slots append the default value without being decoded, the day number is computed in 64 bits, and a day number <= 0, above DATE_MAX_DAYNR or rejected by `get_date_from_daynr()` returns `InvalidArgument("Arrow Date32 value is outside the Doris DATE range: row=..., days=...")` with the 4.x wording. ARRAY/STRUCT/MAP leaves get the same check through their element SerDes. `write_column_to_arrow` and the year-zero calendar mapping (the other part of apache#68649, and master draft apache#67449) are unchanged; the new tests avoid the days whose meaning that mapping changes. Before: out-of-range days read as `0000-00-00`. After: the read fails with the error above; in-range values and NULLs read as before. The Lance reader ported in apache#69581 (from the branch-4.2 snapshot, which already contains apache#68649) relies on this SerDe check. ### Release note Reading Arrow DATE32 values outside 0000-01-01..9999-12-31 (ADBC, Arrow stream load, paimon-rust, Lance) now fails instead of returning 0000-00-00. ### Check List (For Author) - Test: Unit Test - New: `DataTypeDateTimeV2SerDeTest.RejectsInvalidDate32Values`, `LanceTableReaderTypeTest.RejectsDate32OutsideDorisDateRange`, `LanceTableReaderTypeTest.ReadsLastDorisDateAndIgnoresNullDate32Payload`. - All pass on macOS arm64 (ASAN), together with `DataTypeDateTimeV2SerDeTest.*`, `DataTypeSerDeArrowTest.*`, `DataTypeSerDeArrowValidationTest.*`, `ArrowColumnToDorisColumnTest.*`, `ParquetExprTest.*`, `IcebergV2ReaderTest.*` and the Lance suites. With master's DATE32 branch restored, the first two fail. - `DataTypeDateTimeV2SerDeTest` aborts in its setup under macOS ASAN on master: `load_data_from_csv()` in `be/test/testutil/test_util.cpp` dereferences `std::set::end()`. It ran here with that assertion removed locally; that fix goes in a separate PR. - Behavior changed: Yes. An Arrow DATE32 day outside 0000-01-01..9999-12-31 used to be returned as a non-null 0000-00-00; the scan now fails with an InvalidArgument error, as Arrow DATE64 values already did. - Does this need documentation: No Co-authored-by: Gabriel <liwenqiang@selectdb.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
### What problem does this PR solve? Issue Number: None Related PR: apache#69581, apache#68649 Problem Summary: Reproduction: read an Arrow Date32 array holding a day outside 0000-01-01..9999-12-31 into a Doris DATE column, for example 2932897 (10000-01-01) written by PyArrow into a Lance dataset, or the same value returned by an ADBC driver, an Arrow stream load or the paimon-rust reader. The row comes back as a non-null `0000-00-00`, also inside ARRAY, STRUCT and MAP values. Root cause: the DATE32 branch of the shared `DataTypeDateV2SerDe::read_column_from_arrow` ignores the bool returned by `get_date_from_daynr()` (dropped since apache#48944) and appends the default-constructed value. It also adds the epoch offset in 32 bits, so INT32_MAX overflows, and it decodes the payload of NULL slots. The DATE64 branch next to it already rejects out-of-range days. Fix: port the DATE32 range part of the branch-4.1/4.2 fix apache#68649. NULL slots append the default value without being decoded, the day number is computed in 64 bits, and a day number <= 0, above DATE_MAX_DAYNR or rejected by `get_date_from_daynr()` returns `InvalidArgument("Arrow Date32 value is outside the Doris DATE range: row=..., days=...")` with the 4.x wording. ARRAY/STRUCT/MAP leaves get the same check through their element SerDes. `write_column_to_arrow` and the year-zero calendar mapping (the other part of apache#68649, and master draft apache#67449) are unchanged; the new tests avoid the days whose meaning that mapping changes. Before: out-of-range days read as `0000-00-00`. After: the read fails with the error above; in-range values and NULLs read as before. The Lance reader ported in apache#69581 (from the branch-4.2 snapshot, which already contains apache#68649) relies on this SerDe check. ### Release note Reading Arrow DATE32 values outside 0000-01-01..9999-12-31 (ADBC, Arrow stream load, paimon-rust, Lance) now fails instead of returning 0000-00-00. ### Check List (For Author) - Test: Unit Test - New: `DataTypeDateTimeV2SerDeTest.RejectsInvalidDate32Values`, `LanceTableReaderTypeTest.RejectsDate32OutsideDorisDateRange`, `LanceTableReaderTypeTest.ReadsLastDorisDateAndIgnoresNullDate32Payload`. - All pass on macOS arm64 (ASAN), together with `DataTypeDateTimeV2SerDeTest.*`, `DataTypeSerDeArrowTest.*`, `DataTypeSerDeArrowValidationTest.*`, `ArrowColumnToDorisColumnTest.*`, `ParquetExprTest.*`, `IcebergV2ReaderTest.*` and the Lance suites. With master's DATE32 branch restored, the first two fail. - `DataTypeDateTimeV2SerDeTest` aborts in its setup under macOS ASAN on master: `load_data_from_csv()` in `be/test/testutil/test_util.cpp` dereferences `std::set::end()`. It ran here with that assertion removed locally; that fix goes in a separate PR. - Behavior changed: Yes. An Arrow DATE32 day outside 0000-01-01..9999-12-31 used to be returned as a non-null 0000-00-00; the scan now fails with an InvalidArgument error, as Arrow DATE64 values already did. - Does this need documentation: No Co-authored-by: Gabriel <liwenqiang@selectdb.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
### What problem does this PR solve? Issue Number: None Related PR: apache#69581, apache#68649 Problem Summary: Reproduction: read an Arrow Date32 array holding a day outside 0000-01-01..9999-12-31 into a Doris DATE column, for example 2932897 (10000-01-01) written by PyArrow into a Lance dataset, or the same value returned by an ADBC driver, an Arrow stream load or the paimon-rust reader. The row comes back as a non-null `0000-00-00`, also inside ARRAY, STRUCT and MAP values. Root cause: the DATE32 branch of the shared `DataTypeDateV2SerDe::read_column_from_arrow` ignores the bool returned by `get_date_from_daynr()` (dropped since apache#48944) and appends the default-constructed value. It also adds the epoch offset in 32 bits, so INT32_MAX overflows, and it decodes the payload of NULL slots. The DATE64 branch next to it already rejects out-of-range days. Fix: port the DATE32 range part of the branch-4.1/4.2 fix apache#68649. NULL slots append the default value without being decoded, the day number is computed in 64 bits, and a day number <= 0, above DATE_MAX_DAYNR or rejected by `get_date_from_daynr()` returns `InvalidArgument("Arrow Date32 value is outside the Doris DATE range: row=..., days=...")` with the 4.x wording. ARRAY/STRUCT/MAP leaves get the same check through their element SerDes. `write_column_to_arrow` and the year-zero calendar mapping (the other part of apache#68649, and master draft apache#67449) are unchanged; the new tests avoid the days whose meaning that mapping changes. Before: out-of-range days read as `0000-00-00`. After: the read fails with the error above; in-range values and NULLs read as before. The Lance reader ported in apache#69581 (from the branch-4.2 snapshot, which already contains apache#68649) relies on this SerDe check. ### Release note Reading Arrow DATE32 values outside 0000-01-01..9999-12-31 (ADBC, Arrow stream load, paimon-rust, Lance) now fails instead of returning 0000-00-00. ### Check List (For Author) - Test: Unit Test - New: `DataTypeDateTimeV2SerDeTest.RejectsInvalidDate32Values`, `LanceTableReaderTypeTest.RejectsDate32OutsideDorisDateRange`, `LanceTableReaderTypeTest.ReadsLastDorisDateAndIgnoresNullDate32Payload`. - All pass on macOS arm64 (ASAN), together with `DataTypeDateTimeV2SerDeTest.*`, `DataTypeSerDeArrowTest.*`, `DataTypeSerDeArrowValidationTest.*`, `ArrowColumnToDorisColumnTest.*`, `ParquetExprTest.*`, `IcebergV2ReaderTest.*` and the Lance suites. With master's DATE32 branch restored, the first two fail. - `DataTypeDateTimeV2SerDeTest` aborts in its setup under macOS ASAN on master: `load_data_from_csv()` in `be/test/testutil/test_util.cpp` dereferences `std::set::end()`. It ran here with that assertion removed locally; that fix goes in a separate PR. - Behavior changed: Yes. An Arrow DATE32 day outside 0000-01-01..9999-12-31 used to be returned as a non-null 0000-00-00; the scan now fails with an InvalidArgument error, as Arrow DATE64 values already did. - Does this need documentation: No Co-authored-by: Gabriel <liwenqiang@selectdb.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
### What problem does this PR solve? Issue Number: None Related PR: #65730 #66608 #66805 #66841 #66999 #67005 #67011 #67110 #67268 #67289 #67325 #67650 #67921 #68028 #68335 #68375 #68615 #68690 #68813 #68815 #68707 #68649 Problem Summary: **In short.** This ports the Lance BE read path of branch-4.1 / branch-4.2 to master, in its branch-4.2 form as of `cabcf45c1af`, plus the BE side of three 4.x changes merged after that snapshot: the search filter `prefilter` flag and profile layout (#68813), distance bounds for `vector_search` (#68815), and the split-level index rule of #68707 that #68815 relies on. It gathers the BE parts of the PRs listed above. Master links `liblance_c` but has no Lance reader, so every `FORMAT_LANCE` scan fails today. With this PR the BE reads Lance datasets through FileScannerV2: plain scans, vector search and full-text search, predicate and runtime-filter pushdown, the schema fetch of a local Lance TVF, and the second phase of TopN lazy materialization. The PR is BE only. The FE connector plugin and the regression suites come in separate PRs, so until then nothing on master plans a Lance scan, and the BE unit tests carry the proof. Original authors: @zhangstar333 @Gabriel39 @wangzhaobo957-cloud @Jay-ju @FANNG1 **Background.** Lance shipped in 4.1.4: the Lance catalog, `local()` / `s3()` with `format=lance`, and the `vector_search` / `full_text_search` TVFs. On master the FE side is being rebuilt as a connector plugin (`fe-connector-lance`), and the BE needs the reader from 4.x. The thrift ids are the branch-4.1 / 4.2 ones, including `TExternalSearchRequest.5` (`prefilter`) and `TVectorSearchParams.6/7` (distance bounds) added after the snapshot, so a BE built from master also reads the Lance scan ranges and search requests that a 4.x FE sends during a rolling upgrade. The one exception is `TVectorSearchOptions` 6, branch-4.2's `approx_mode` (#69852, merged into branch-4.2 after the snapshot and not ported): it is reserved in a comment, and a master BE ignores it, so during a rolling upgrade a 4.2 FE that asks for `fast` or `accurate` gets Lance's default (`normal`). **The problem, and what it cost.** | | master today | with this PR | |---|---|---| | A `FORMAT_LANCE` scan | FileScannerV2 has no Lance reader, so the scan fails with "FileScannerV2 does not support file format" | read by `LanceTableReader` in FileScannerV2 | | `fetch_table_schema` for a local Lance TVF | not handled | returns the Arrow schema mapped to Doris types; Null leaves keep `NULL_TYPE` | | The row location of a Lance row in TopN lazy materialization | `GlobalRowLoacationV2` only has a 32-bit row id, but a Lance row id is a 64-bit id within the snapshot | a version 1 layout carries the 64-bit row id; phase two reads the rows with the dataset's `take_rows` | **How this PR fixes it.** - Reader: `be/src/format_v2/lance/*` and `be/src/format_v2/table/lance_reader.*`, as on branch-4.2 except one local renamed for master's `-Wshadow`. The unit tests construct `RuntimeState(TQueryOptions(), globals)`, because master removed the globals-only constructor. - FileScannerV2: the reader dispatch and `_to_file_format` handle `FORMAT_LANCE` / `"lance"`; `FileFormat::LANCE` is added to its two exhaustive switches. Since #69723 every file scan uses FileScannerV2, so nothing else routes Lance scans (on branch-4.2 a Lance query fails with NotSupported when `enable_file_scanner_v2` is off). - Row locations: `ROW_VERSION {FILE_LOCAL_ROW_ID = 0, LANCE_DATASET_ROW_ID = 1}` and a union layout for `GlobalRowLoacationV2`. Version 0 keeps its byte layout, which static asserts pin. `PRequestBlockDesc.row_id` becomes `uint64` (a proto2 varint, so the wire format is compatible). The materialization operator decodes row locations by version, rejects an unknown version or a wrong size, and stamps `row_location_version`. `RowIdStorageReader` validates the counts and the version, range-checks internal row ids against `uint32`, and groups Lance rows by plan node, snapshot version and dataset URI, so each dataset is read with one `take_rows`. Master's segment batching, scan-slot dedup and fetch concurrency stay as they are. - Thrift (`PlanNodes.thrift`): the external search request body (`TVectorElementType`, `TVectorMetric`, `TSearchVector`, `TVectorSearchParams` 1-7, `TFtsCoverageMode`, `TFtsQueryType`, `TFtsMatchOperator`, `TFullTextSearchParams`, `TSearchFilterFormat`, `TSearchFilter`, `TVectorSearchOptions`, `TExternalSearchQuery`), `TExternalSearchRequest` 2-5 and `TLanceFileDesc` 4-6, all with the branch-4.1 / 4.2 ids. The enums spell out their 4.x values. No id is reused. On master, #70075 reserved `TExternalSearchRequest` 2-5 in comments and added 6 (`paimon_options`); this PR replaces those comments with the fields and leaves 6 as it is. `TVectorSearchOptions` 6 is reserved in a comment for branch-4.2's `approx_mode` (#69852). - After the snapshot (one commit each): - #68813: `prefilter` decides whether the TVF search filter runs before (true) or after (unset / false) candidate selection, for vector and full-text search. The profile groups the Lance counters under `LanceTiming`, `LanceIO`, `LanceIndex`, `LanceVectorIndex`, `LanceScalarIndex`, `LanceExecutionPlan`, `LanceScan`, `LanceSearchPlan` and `LanceDataCache`, uses the 4.x counter names, and registers every scan metric before the statistics callback is installed, so the callback, which Lance invokes from its execution streams, never adds counters. #68813's FE tweak that hides `TUnit.NONE` labels in `AggCounter` belongs to the FE port. - #68707 (part): a vector split searches exactly the index segments FE planned for it, and a split without segments is searched flat whatever `use_index` says; a split with segments under `use_index = false` is rejected. 4.x FEs plan segments only when `use_index` allows them. - #68815: `distance_lower_bound` (inclusive) and `distance_upper_bound` (exclusive) are validated (finite FLOAT values, lower < upper at FLOAT precision, single-vector only) and installed with `lance_scanner_set_distance_range()`, which master's pinned lance-c provides. - Review fixes: - A nested array goes to the Doris SerDes without a copy only when it and every descendant start at offset 0 and reference exactly their child ranges. Lance's legacy (v1) decoder can return a struct or list that is tight at its own level over a Boolean child with a bit offset (a batch that starts mid-byte in the bitmap), and the SerDes reject that under Arrow input validation. - A zero-field `STRUCT<>`, which Lance decodes, is reported as an unsupported column at any nesting level, like the other unsupported Lance types. It used to be typed as a STRUCT that `ColumnStruct` cannot hold (its row count comes from its first child). - A Lance Decimal256 field with precision <= 38 is narrowed to Decimal128 by a safe Arrow cast before decoding. Doris types it DECIMAL32/64/128I by precision, and those SerDes read only Decimal128 arrays; the BE used to crash on the first value, also for nested leaves. Only values a reader can see are cast: before a nested leaf is narrowed, every child slot that no visible row references (under a NULL list, fixed-size-list, map or struct row, or outside a sliced parent) is marked NULL, so a hidden value does not fail the cast and a visible value outside the declared precision still does; map keys, which cannot be NULL, stay checked. - FLOAT / DOUBLE `>=` runtime filters stay residual-only. Lance compares floats by totalOrder, so a pushed `x >= c` dropped negative-NaN rows that Doris keeps. - Runtime filters that mention INT64_MIN stay residual-only. The pinned Lance SQL parser reads `-9223372036854775808` as the negation of a magnitude that it parses as i64 first; the overflow turns the literal into a Float64, which the planner cannot coerce to Int64 or Decimal, so a pushed IN, `>=` or `<=` filter on an INT64 or scale-zero decimal column failed planning and the query failed. Every other i64 is still pushed. - The shared Arrow DATE32 decoding (`DataTypeDateV2SerDe::read_column_from_arrow`) rejects days outside 0000-01-01..9999-12-31 instead of returning a non-null `0000-00-00`, skips null slots and adds the epoch in 64 bits: the DATE32 range part of branch-4.1 / 4.2 #68649. It also changes ADBC, Arrow stream load and paimon-rust reads. - Child values hidden by a NULL list, fixed-size-list, map or struct row are not decoded: the array, map and struct SerDes give the element SerDe a validity bitmap that also clears those slots (`mask_arrow_children_hidden_by_null_parents()`). Arrow leaves them unspecified, and Lance's v2.1 miniblock encoding keeps them, so a hidden 10000-01-01 under a NULL fixed-size list used to fail the DATE32 range check above. Visible values are unchanged; this applies to ADBC, Arrow stream load and paimon-rust as well. The masks come from ExecEnv's tracked Arrow pool. A nested Lance array that is compacted before decoding is masked the same way, at every level, before its `ValidateFull()`, so a hidden invalid UTF-8 string, out-of-precision decimal, Date64 or time no longer fails the batch while a visible one still does. Since #69697 the same masks keep hidden DATETIMEV2 / TIMESTAMPTZ values out of its range checks. A mask costs at most one pass per batch: a NULL row that owns no child slots, or only NULL ones, allocates nothing, and Arrow stream load masks each retained IPC batch once (`mask_arrow_descendants_not_visible()`) before it hands row ranges to the SerDes, so its per-block reads allocate nothing. The one per-block mask left is for map keys under a NULL map row that still owns entries, since a MapArray cannot hold NULL keys; Arrow's map builder writes NULL map rows without entries. - Core: `to_protobuf` keeps `NULL_TYPE` for a NULL literal type, so a Null leaf in ARRAY / MAP / STRUCT no longer comes back as BOOLEAN. The nullable serde reads an Arrow Null array as NULLs without touching buffers that the array does not have. - Configs, caches and metrics, with the 4.x names and defaults: | Config | Default | |---|---| | `lance_index_cache_size_bytes` | 10 GiB | | `lance_metadata_cache_size_bytes` | 1 GiB | | `enable_lance_data_cache` | true | | `lance_data_cache_path` | `${DORIS_HOME}/lance_data_cache` | | `lance_data_cache_disk_capacity_bytes` | 100 GiB | | `lance_data_cache_read_block_size_bytes` | 1 MiB | | `lance_io_buffer_size_bytes` (mutable) | 2 GiB | | `lance_batch_readahead` (mutable) | 5 | | `lance_fragment_readahead` (mutable) | 5 | The BE-wide Lance session (index cache, metadata cache and the optional Foyer disk cache) is created by the first Lance scan, so a BE that never reads Lance creates no cache directory. The first six configs take effect after a restart. A negative cache size (and, while the data cache is enabled, a negative disk capacity or read block size) fails the first Lance scan with a message that names the setting, as does any data-cache value lance-c rejects; the rest of the BE keeps serving. Ten `lance_session_*` metrics report the two in-memory caches. With the defaults, the disk tier keeps 51,200 sparse 2 MiB block files open from the first Lance scan on (100 GiB in blocks of twice the read block size), the same as on branch-4.1 / 4.2. Size `lance_data_cache_disk_capacity_bytes` and `lance_data_cache_read_block_size_bytes` against the BE's open-file limit, or set `enable_lance_data_cache = false`. The index and metadata caches live in lance-c memory: they are not charged to a MemTracker or registered with CacheManager, so memory GC, the cache capacity adjustment under memory pressure and `/api/clear_cache/all` do not shrink them, and `enable_lance_data_cache = false` does not turn them off. lance-c has no call to clear, evict or resize them, so size them with the two configs. The disk tier keys blocks by Lance's store prefix (`scheme$bucket` for S3, GCS and OSS, without the endpoint) and the object path (`thirdparty/patches/lance-c-foyer.patch`). Several endpoints that serve the same bucket name and data-file path with different bytes (data files registered under chosen names or replaced in place; Lance's writers use random file names) share entries, also across a BE restart; such a deployment should set `enable_lance_data_cache = false`. Both are as on branch-4.1 / 4.2 and tracked as follow-ups. A Lance scan's native working set, that is the I/O buffered up to `lance_io_buffer_size_bytes` per scanner, the decode readahead and the operators of a search plan such as the top-k of a flat vector search, is allocated inside lance-c and is charged to neither the query nor its workload group. It counts toward the BE process memory only, so memory GC, which ranks queries by tracked memory, does not attribute it to the Lance query. lance-c accepts no memory budget, so on BEs with little memory headroom size `lance_io_buffer_size_bytes`, `lance_batch_readahead` and `lance_fragment_readahead` for the number of concurrent Lance scanners. As on branch-4.1 / 4.2; tracked as a follow-up. - Build: macOS links `-framework IOKit`. This is the first time objects of `liblance_c.a` are pulled into `doris_be`, so the binary and the link time grow. With this PR `util/rle_encoding.h` reaches 231 TUs and `util/threadpool.h` 589, the same as master `315c2e00331` alone (limits 275 and 660 since #69717), and `gen_cpp/internal_service.pb.h` 562 (master alone 559, limit 660); the reach budgets are unchanged. Not included: the Lance index metadata scanner (`LANCE_INDEX_ENTRIES`); the FE side, including the rolling-upgrade guard that keeps Lance lazy materialization off while an old BE could decode a version 1 row location as version 0; the regression suites; the rest of #68707 (searching historical versions, tags and branches, rejecting fragment ids missing from the snapshot, naming the snapshot in errors and the profile). The `InitReaderAvgTime` / `GetBlockAvgTime` profile fix that #67005 carried goes in a separate PR. **Results.** | | master | with this PR | |---|---|---| | A `FORMAT_LANCE` query | fails before reading anything | read by `LanceTableReader` in FileScannerV2 | | A Lance row in the second phase of TopN lazy materialization | cannot be represented | a version 1 row location, read back with `take_rows` | | A row location of an unknown version or size in phase two | decoded as version 0 | rejected with an error | | An Arrow DATE32 day outside 0000-01-01..9999-12-31 (ADBC, Arrow stream load, paimon-rust, Lance) | read as a non-null `0000-00-00` | the read fails with `InvalidArgument` | BE unit tests (ASAN, macOS arm64, against master's pinned lance-c), on the head rebased onto master `378404eeb12`: 1029 cases in 54 suites run, 1027 pass and 2 Parquet microbenchmarks skip by design. They cover the Lance suites (107: `LanceTableReader*` 83, `LanceNestedNullTest` 10, `LanceRuntimeFilterHelperTest` 10, `LanceSessionManagerTest` 4); the row-location protocol (`MaterializationSharedStateTest`, `RowIdStorageReaderTest`, and the new `RowIdFetchRequestValidationTest`: count mismatch, unknown version, a Lance version on an internal mapping, an internal row id of 2^32); `FileScannerV2Test` (28); the ORC and Parquet reader suites (`NewOrcReaderTest`, `NewParquetReaderTest`, `ParquetScanTest` and the other `Parquet*` suites), whose row ids now go through `file_local`; and the Arrow / protobuf / DATE serde suites with the new `ArrowParentNullsTest`, plus the Iceberg and Parquet reader suites that decode Arrow dates. Reverting any of the review fixes, the #68813 flag, the #68707 split rule or the #68815 bounds makes its new tests fail. The sixth review round adds 13 cases (`ArrowParentNullsTest` 10, `LanceTableReaderTypeTest` 3) and extends one; with them the Arrow SerDe, Lance, ADBC and `IcebergV2ReaderTest` suites run 317 cases: 310 pass and the 7 ADBC SQLite cases skip without a local SQLite driver. The ninth review round adds `LanceRuntimeFilterHelperTest.KeepsInt64MinFiltersResidualOnly` and `LanceTableReaderFilterTest.KeepsInt64MinRuntimeFiltersOutOfLanceScanner`: both pass with the 106 Lance cases (`Lance*`) on the branch rebased onto `339e087b11f`, and restoring `parse_int64()` in either check makes exactly these two fail. On the head rebased onto master `315c2e00331`, with the per-block mask fix (`42cf4e3e5d1`), 328 cases in 24 suites pass: the Arrow SerDe and validation suites, `ArrowParentNullsTest` (15), `LoadArrowPipeTest` (47), the DATETIMEV2 / TIMESTAMPTZ SerDe suites, `ArrowColumnToDorisColumnTest`, `ArrowBlockConvertorTest`, `AdbcReaderTest`, `RemoteDorisV2ReaderTest`, `PaimonRustTableReaderTest` and the Lance suites. Allocating at the first NULL row of each call again fails the new `ArrowParentNullsTest` case and the four new `LoadArrowPipeTest` block cases, dropping the per-batch mask fails the LIST and STRUCT block cases, and returning the child unmasked fails the new timestamp case, four `ArrowParentNullsTest` cases and `LanceTableReaderTypeTest.IgnoresDate32PayloadHiddenByNullParents`. ### Release note Add nine `lance_*` BE configs and `lance_session_*` BE metrics. They take effect once the FE can plan Lance scans. Reading an Arrow DATE32 value outside 0000-01-01..9999-12-31 through ADBC, Arrow stream load or paimon-rust now fails instead of returning 0000-00-00. ### Check List (For Author) - Test <!-- At least one of them must be included. --> - [ ] Regression test - [x] Unit Test - `run-be-ut.sh --run --filter=...` with the suites listed under Results: 420 of 420 pass on macOS arm64 (ASAN) on the content before the rebases, and the Lance and Arrow SerDe suites pass again with the nested-offset, zero-field struct, hidden-child, hidden-Decimal256 and compaction-mask fixes (the ADBC SQLite cases skip without a local SQLite driver). Rebased onto master `43f9ceb48d7`: #69668 conflicted in the scanner selection and in `fetch_table_schema`, which now hands every file format to `format::fetch_file_schema()`, so the Lance branch runs ahead of that call; #69646 added a TopN row-group test that reads the version 0 row location fields, which now go through `file_local` (the earlier rebase onto `5caaeb3e3d4` resolved #69614 and #69601 in the scanner selection, the `FileFormat` enum, its test and the reach budget table). The 95 TUs that this PR changes or that master changed since `5caaeb3e3d4` were syntax-checked against the rebased tree with the UT build flags, and CI runs the suites again. Rebased again onto master `8d88f1cc6f7`: the only conflict was the budget table, where #65858 moved the `util/rle_encoding.h` baseline to 828, and the 163 TUs that this PR changes or that master changed since `43f9ceb48d7` pass the same syntax check. Rebased again onto master `339e087b11f`: the only conflict was the budget table again, where #67292 moved the `util/rle_encoding.h` baseline to 834; #69872 added Arrow type checks ahead of the NULL-parent masking in the map and struct readers and merged cleanly. Of the 114 sources that this PR changes or that master changed since `8d88f1cc6f7`, 111 pass the same syntax check. The other three are master's: two new #67292 files under `storage/segment` fail the same way on master alone, since they compile only inside their unity batch, and this PR adds no source to that target; the #69818 benchmark is not in the UT build and includes only `util/simd` headers that this PR does not touch. Reverting any of the review fixes, the #68813 flag, the #68707 split rule or the #68815 bounds makes its new tests fail. Rebased again onto master `378404eeb12`, which removed the legacy file scanner (#69723): the conflicts were in the scanner-selection code and tests that #69723 deleted, so this PR's Lance lines there went with them (including its `LanceQueriesAlwaysUseScannerV2` test), in the profile keys of `materialization_opertor.cpp`, and in the row fetch of `rowid_fetcher.cpp`, whose non-Lance path is now only `FileScannerV2::read_by_rows`; the deleted V1 ORC / Parquet row id tests took this PR's `file_local` rename with them, and the `util/rle_encoding.h` budget commit is dropped because master is no longer near that limit. Of the 100 sources that this PR changes or that master changed since `339e087b11f`, 99 pass the same syntax check; the other, `format_v2/orc/orc_reader.cpp`, fails the same way on master alone. On the rebased head the suites listed under Results run 1029 cases: 1027 pass and 2 microbenchmarks skip. The test-utility fix that `DataTypeDateTimeV2SerDeTest` needed on macOS (#69695) is now on master. Rebased again onto master `315c2e00331`: the only conflict was `TExternalSearchRequest` in `PlanNodes.thrift`, where #70075 had reserved fields 2-5 in comments; the two commits that add the fields replace those comments, and field 6 stays #70075's `paimon_options`. #69717 cut transitive includes from shared headers: of the 160 sources that this PR changes or that master changed since `378404eeb12`, 156 pass the same syntax check; the two `jemalloc_hook` tests have no compile command on macOS, `runtime/memory/jemalloc_hook.cpp` uses glibc aliases that macOS cannot build (#69819), and `format_v2/table_reader.cpp` alone fails the same way on master alone while its unity batch compiles. The full `doris_be_test` builds and links on the rebased head; `ArrowParentNulls*` and `Lance*` (121 cases) pass before the mask fix, and the 328 cases above with it. - `build-support/check-build-hygiene.sh` (on this branch rebased onto master `315c2e00331`), clang-format 16 and `build-support/run-clang-tidy.sh` on the changed files: all pass; on the changed lines clang-tidy (llvm 22) reports only `readability-inconsistent-ifelse-braces` on the `DORIS_CHECK` lines of the masking functions, the macro's expansion that master's existing `DORIS_CHECK`s report too. - [ ] Manual test (add detailed scripts or steps below) - [ ] No need to test or manual test. Explain why: - Behavior changed: - [ ] No. - [x] Yes. `PRequestBlockDesc.row_id` is `uint64`; phase two of TopN lazy materialization rejects row locations of an unknown version or size; `FORMAT_LANCE` scans run on FileScannerV2; an Arrow DATE32 day outside the Doris DATE range fails the read instead of becoming `0000-00-00`. - Does this need documentation? - [x] No. Lance is documented with the FE connector. - [ ] Yes. <!-- Add document PR link here. eg: apache/doris-website#1214 --> ### Check List (For Reviewer who merge this PR) - [ ] Confirm the release note - [ ] Confirm test cases - [ ] Confirm document - [ ] Add branch pick label <!-- Add branch pick label that this PR should merge into --> --------- Co-authored-by: zhangstar333 <zhangsida@selectdb.com> Co-authored-by: Gabriel <liwenqiang@selectdb.com> Co-authored-by: zhaobo wang <wangzhaobo957@gmail.com> Co-authored-by: jay <qingfengyouchen@163.com> Co-authored-by: FANNG <fanng@apache.org> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: zy-kkk <zhongyk10@gmail.com>
What problem does this PR solve?
Related issue: #67366
Related PR: #67449
On branch-4.1, Arrow Flight SQL returns
0000-01-02for a stored DATE of0000-01-01. Doris follows the MySQL calendar, which excludes year zero's leap day, while Arrow DATE uses the proleptic Gregorian calendar. Subtracting the Unix epoch day number directly shifts dates in0000-01-01through0000-02-28by one day, including elements ofARRAY<DATE>.Adapt the DATE portion of #67449 to branch-4.1, with credit to its author. Convert between the calendars at Arrow and ORC write boundaries and Arrow/default Parquet/ORC read boundaries. Use the same conversion for ORC statistics so pushed-down MIN/MAX agrees with row decoding. Reject unrepresentable external dates and ignore undefined payloads in null Arrow slots.
The two DATE regression suites and their expected results are copied unchanged from #67449. Add its DATE unit coverage plus an explicit null-payload test and an exhaustive comparison against the civil calendar for all 3,652,424 representable dates.
Also align Iceberg DATE day/bucket partition transforms with file ordinals. For ORC/Parquet, metadata is usable only when its raw DATE range proves that every ordinal is representable, including the interior of the range. Unsafe ranges fall back to decoding: strict reads retain their errors, non-strict Parquet reads retain conversion-generated NULLs, and COUNT uses materialized values when physical null counts are insufficient. ORC uses a metadata-only SARG to check projected DATE leaves per stripe: safe excluded stripes retain SARG pruning, while unsafe stripes remain readable with SDK row-group pruning disabled. ORC COUNT validates every DATE leaf of its argument in each selected stripe, and strict Parquet COUNT validates projected DATE descendants before reading only levels.
Scope: DATE, including its Iceberg partition transforms. This does not include DATETIME changes or the independent legacy file scanner conversions used when
enable_file_scanner_v2=false. V1 reader changes and mixed-version/rolling-upgrade compatibility gates are explicitly outside this PR's scope.Validation
Baseline: 9 of the original 10 DATE unit tests fail; the unchanged Flight regression reproduces the one-day shift.
Fixed: 250 unit tests pass under ASAN, including the complete ORC reader suite, DATE SerDe exhaustive calendar check, Iceberg partition transforms, Parquet statistics, DATE scans and COUNT tests. These compile the modified branch-4.1 sources against cached test dependencies.
The unchanged
test_date_year_zeroFlight regression passes locally, including nullable and array values.test_outfile_date_year_zeropasses for Parquet and ORC using the original rows and golden assertions, with S3 transport replaced by local outfile/TVF only for the local run. The committed suite retains S3 transport for CI.Local integration runs use the same fix rebuilt into an existing ASAN/UBSAN BE build at
5bcb996a5ff, with an existing FE. A full branch-4.1 build and the original S3 run remain for CI.clang-format 16 passes for all affected C++ files. Source whitespace checks pass; generated golden files retain their conventional trailing blank line from [fix](arrow) encode DATE in the proleptic Gregorian calendar at format boundaries #67449.
Review coverage: new tests reproduce partition mismatches, invalid-interior MIN/MAX bypass, ORC SARG/Parquet footer/page pruning inconsistencies, and Parquet IS NULL/COUNT discrepancies. All five reproducing tests fail before the review fixes.
Added a DATE-only Spark/Iceberg partitioned write/read regression, including selective day/bucket predicates and partition metadata comparison. Its Groovy compilation passes; execution requires the external CI environment.
Follow-up review: scalar/nested ORC COUNT and strict nested Parquet COUNT reproductions fail before the guards and pass afterward. ARRAY, STRUCT, MAP, all-null values, safe ranges, non-strict Parquet, and unprojected DATE siblings are covered.
External ORC regression: retain the original LZ4 file as a BE fixture and verify that ordinal
-719530raises a data-quality error. Replace the obsolete1900-01-01golden result with an explicit error assertion, restoring the scanner setting afterward. The fixture test and Groovy compilation pass locally. The complete group6 suite is also validated locally with HDFS transport replaced by LOCAL TVF; the committed suite retains HDFS for CI.Pruning review: both new tests fail before the fixes and pass afterward. Partial ORC STRUCT projection retains SARG when the invalid DATE sibling is not read; selecting that DATE still preserves its error. Multi-page Parquet tests retain the unsafe DATE page while pruning safe pages for IS NULL and range predicates, through both single-slot and multi-slot OR evaluators.
Mixed-stripe ORC coverage: the original file-wide guard prunes zero safe stripes; the fix prunes both safe excluded stripes and still reports the invalid DATE in the retained stripe. The active SDK options outlive their reader, and the cached SARG evaluator is discarded before opening an unsafe range.
External group6 coverage: the original
non_vec_orc_scanner.orcfixture encodes20191111as an invalid epoch-day ordinal. Replace its obsolete1900-01-01golden result with an explicit data-quality error assertion and keep the original bytes as a small BE fixture; projecting other columns still succeeds.Final local integration after the stripe fix: all three suites pass with no failures or skips (complete group6: 41 fixtures, 39 golden-result assertions and two expected errors; Flight year zero; Parquet/ORC outfile year zero). These use the incremental ASAN/UBSAN build and local transport substitutions described above.
Metadata safety review: the malformed pruned-stripe index and truncated nested DATE statistics reproduce SDK bounds assertions before the fix. Both regressions pass afterward; the DATE preflight now adds no file read calls or bytes compared with the same excluded stripe without DATE projection.
Historical-file migration: an explicit offline tool converts only confirmed legacy Doris DATE ordinals, including nested values. It publishes a separate output atomically and refuses overwrites. Tests cover year-zero boundaries, NULLs, hidden null-container payloads, nested ORC/Parquet round trips, schema metadata, timestamp precision, and failed-write cleanup.
Final follow-up validation: all 250 ASAN tests and 10 migration-tool tests pass. The final incremental production BE passes the three existing local suites plus six legacy migration/read/export/read checks, covering both ORC and Parquet. Original HDFS/S3 transport and the full branch build remain for CI as described above.
Release note
Fix year-zero DATE values being returned one day late through Arrow Flight SQL, align DATE encoding and decoding in the default Parquet/ORC paths, and keep Iceberg DATE partition values consistent with file contents.
Existing files written with the former year-zero encoding cannot be distinguished automatically from correctly encoded files. Before switching their consumers to the corrected reader, use the explicit legacy DATE migration tool and procedure on files confirmed to use the old Doris encoding, or re-export from authoritative original data. The tool writes new ORC/Parquet files without modifying the source. Existing Iceberg files must be migrated through a new table or a supported table-format transaction that recomputes partition metadata; do not replace files in existing snapshots. The proleptic-only
0000-02-29remains unrepresentable as a Doris DATE.Check List (For Author)
Check List (For Reviewer who merge this PR)