Skip to content

[refactor](arrow) Make format convertors explicit and schema-owned - #68381

Merged
yiguolei merged 2 commits into
apache:branch-4.1from
Gabriel39:dev/arrow-format-convertors
Sep 23, 2026
Merged

yiguolei merged 2 commits into
apache:branch-4.1from
Gabriel39:dev/arrow-format-convertors

Conversation

@Gabriel39

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Related PR: #67784. This is the second split, based on the primitives merged in #68297.

Arrow batch conversion mixes protocol serialization with table-specific UUID and Variant handling, while writers construct schemas separately. Introduce explicit Doris, Python, Arrow Flight, Parquet, Hive, Iceberg and Paimon convertors, instantiated with their own schema parameters and timezone. Move table schema construction/decoding into the convertors and route nested SerDe writes through the selected format.

Separate Parquet, Hive and Iceberg writers and migrate all callers. Preserve current Parquet timestamp encoding, UUID/Variant layouts and external type mappings. Keep Python's numeric conversion timezone consistent with its declared Arrow schema, and reject incompatible nested target schemas before casts or child access. No FE, Thrift, ORC timestamp, or binary computation changes are included.

Release note

Fix Python UDF timestamp conversion to preserve wall-clock values when the Arrow protocol declares a fixed-offset timezone.

Check List (For Author)

  • Test
    • Unit Test: schema ownership/isolation, slices, invalid schemas, nested/null values, UUID/Variant bytes, fixed-offset Python batches, and Parquet timestamp representation.
    • Regression test: add Python UDF/UDTF/UDAF timezone assertions and extract the corresponding existing Python snapshot corrections from [fix](external) Fix Arrow and external timestamp semantics #67784.
  • Behavior changed:
    • Yes: align Python UDF conversion with its Arrow timezone declaration; report invalid nested schema bindings as errors.
  • Does this need documentation?
    • No.

Validation: ASAN BE build and 396 selected tests passed; the final incremental build and 285 focused tests also passed. clang-format 16 and Groovy/embedded Python syntax checks passed. Python SQL and external-catalog end-to-end regressions were not run locally and remain for CI.

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?

Related PR: apache#67784. This is the second split, based on the primitives merged in apache#68297.

Arrow batch conversion mixes protocol serialization with table-specific UUID and Variant handling, while writers construct schemas separately. Introduce explicit Doris, Python, Arrow Flight, Parquet, Hive, Iceberg and Paimon convertors, instantiated with their own schema parameters and timezone. Move table schema construction/decoding into the convertors and route nested SerDe writes through the selected format.

Separate Parquet, Hive and Iceberg writers and migrate all callers. Preserve current Parquet timestamp encoding, UUID/Variant layouts and external type mappings. Keep Python's numeric conversion timezone consistent with its declared Arrow schema, and reject incompatible nested target schemas before casts or child access. No FE, Thrift, ORC timestamp, or binary computation changes are included.

### Release note

Fix Python UDF timestamp conversion to preserve wall-clock values when the Arrow protocol declares a fixed-offset timezone.

### Check List (For Author)

- Test
    - [x] Unit Test: schema ownership/isolation, slices, invalid schemas, nested/null values, UUID/Variant bytes, fixed-offset Python batches, and Parquet timestamp representation.
    - [x] Regression test: add Python UDF/UDTF/UDAF timezone assertions and extract the corresponding existing Python snapshot corrections from apache#67784.
- Behavior changed:
    - [x] Yes: align Python UDF conversion with its Arrow timezone declaration; report invalid nested schema bindings as errors.
- Does this need documentation?
    - [x] No.

Validation: ASAN BE build and 396 selected tests passed; the final incremental build and 285 focused tests also passed. clang-format 16 and Groovy/embedded Python syntax checks passed. Python SQL and external-catalog end-to-end regressions were not run locally and remain for CI.

### Check List (For Reviewer who merge this PR)

- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label
@Gabriel39
Gabriel39 requested a review from yiguolei as a code owner September 22, 2026 08:46
@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?

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

/review

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: Codex exited with status 0 without a terminal turn event; review is incomplete
Workflow run: https://github.com/apache/doris/actions/runs/35706733880

Please inspect the workflow logs and rerun the review after the underlying issue is resolved.

### What problem does this PR solve?

Related PR: apache#68381

The timezone regression declares ARRAY<STRUCT<value:STRING>> but compares its lateral-view output directly to a string. Declare ARRAY<STRING> to match the single string yielded per row and allow the comparison to reach execution. Preserve coverage for fixed-offset timezones, microseconds, pre-epoch values and NULLs.

### Release note

None

### Check List (For Author)

- Test: Groovy syntax and embedded Python scalar, UDTF and UDAF value checks; SQL analysis verification.
- Behavior changed: No (test-only correction).
- Does this need documentation: No.
@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

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 exact head c552895e6c9dbdece9ff4bfab484a7086cd4555e.

I found two blocking correctness issues:

  • Protocol-specific Paimon/Iceberg complex writers validate only the outer container, so an incompatible nested target can reach the wrong builder or silently reinterpret decimal scale/field identity.
  • The new plain timestamp binding check rejects the supported zero-offset session alias chain (+00:00 -> Z -> Zulu/UTC) even though the zones are semantically identical.

Coverage included all 51 changed paths plus the relevant scalar SerDes, FE-to-BE timezone propagation, Paimon JNI schema ownership, Iceberg/Parquet writer lifetimes and cleanup, Arrow Flight, Python callers, build discovery, and changed tests/regressions. The writer split, schema lifetimes, cleanup/statistics paths, UUID/VARBINARY handling, and nonzero/DST timezone cases did not yield another distinct issue. No user-specific focus item was provided, and no live review/comment duplicate existed immediately before submission.

Validation was static only, as required by the review contract; no builds or tests were run.

arrow::ArrayBuilder* array_builder, int64_t start, int64_t end,
const cctz::time_zone& ctz) const {
// Reject an incompatible target before casting its nested schema or builder.
if (field->type()->id() != arrow::Type::LIST ||

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.

[P1] Validate the nested binding before dispatching to the child SerDe

This only checks that the outer target is a LIST. The Paimon/Iceberg converters bypass the recursive plain-binding validator, so a same-outer-kind mismatch reaches the child writer. For example, a Doris array<decimal(10,3)> value 1.234 has raw coefficient 1234; if the pinned target is list<decimal(10,2)>, the Decimal128 writer accepts the same builder and persists that coefficient as 12.34. array<int> versus list<string> instead reaches the wrong builder cast, and Map/Struct have the same gap. Please validate the full recursive binding (including decimal parameters, timestamp units, and struct field identity/order) before appending the parent builder.

// with DATETIMEV2; TIMESTAMPTZ must always retain its instant semantics.
if (target_timestamp.timezone().empty()) {
return primitive == TYPE_DATETIMEV2;
}

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.

[P1] Compare timezone bindings semantically instead of by cctz name

A valid +00:00 session reaches BE as Java's canonical Z. The timezone cache resolves Z to a cctz object named Zulu, while Arrow schema construction rewrites the declared Z label to UTC. write_plain_arrow_column therefore builds the source timestamp with Zulu, this line resolves the target as UTC, and the new preflight rejects UTC == Zulu before writing even though both are the same zero-offset zone. This breaks timezone-aware timestamp batches on the common plain converter path (including Parquet/Hive and memory-scratch/Arrow-Flight consumers) for the supported +00:00 session value. Please normalize both sides to one identity or compare actual zone equivalence, and add a full batch-conversion test using the cctz object resolved from Z rather than only checking the generated schema label.

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 84.89% (680/801) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 74.56% (31548/42312)
Line Coverage 58.80% (351617/598034)
Region Coverage 55.47% (293120/528450)
Branch Coverage 56.33% (132355/234961)

yiguolei pushed a commit that referenced this pull request Sep 23, 2026
…68396)

### What problem does this PR solve?

Related PR: #68381. This is the master version of the second split from
#67784, based on the primitives merged in #68301.

Arrow batch conversion mixes protocol serialization with table-specific
UUID handling, while writers construct schemas separately. Introduce
explicit Doris, Python, Arrow Flight, Parquet, Hive, Iceberg and Paimon
convertors with instance-owned schema parameters and timezone. Move
schema construction/decoding into the convertors and route nested SerDe
writes through the selected format.

Separate Parquet, Hive and Iceberg writers and migrate existing callers.
Preserve master's tracked Arrow memory pools, Iceberg statistics and
timestamp-nanosecond support. Master does not yet contain the Paimon
write backend or physical Variant table writes present on branch-4.1;
this pick adds the converter interfaces without importing those
features. Parquet timestamp encoding and external type mappings remain
unchanged.

Include the Python timezone regression correction from #68381: the
single string output uses ARRAY<STRING>, so the lateral-view comparison
reaches execution instead of failing on a STRUCT-versus-STRING
comparison. Retain coverage for four session timezones, microseconds,
pre-epoch values, NULLs, UDF, UDTF and UDAF.

### Release note

Fix Python UDF timestamp conversion to preserve wall-clock values when
the Arrow protocol declares a fixed-offset timezone.

### Check List (For Author)

- Test
- [x] Unit Test: explicit schemas and independent converter instances,
nested/null values, UUID and fixed binary bytes, timestamp bindings, and
Iceberg writer statistics.
- [x] Regression test: Python UDF/UDTF/UDAF timezone comparisons and the
existing timestamp snapshot corrections.
- Behavior changed:
- [x] Yes: align Python UDF conversion with its Arrow timezone
declaration; reject invalid nested bindings before casts.
- Does this need documentation?
    - [x] No.

Validation: ASAN BE build and 310 focused tests passed (53 suites),
covering Arrow conversion, Parquet/ORC, Variant SerDe and Python. All 42
affected C++ files passed clang-format 16, and header hygiene passed.
The original UDTF declaration reproduced the SQL analysis error on an
isolated FE; the corrected declaration passed the same analysis. Groovy
and embedded Python checks passed. Full Python SQL and external-catalog
regressions remain for CI. clang-tidy was attempted: the new converter's
size warning was resolved; analysis remains blocked by a pre-existing
unmatched NOLINTEND in core/types.h.

### Check List (For Reviewer who merge this PR)

- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label
@yiguolei
yiguolei merged commit 12a53cd into apache:branch-4.1 Sep 23, 2026
31 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Sep 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by at least one committer and no changes requested.

yiguolei pushed a commit that referenced this pull request Sep 25, 2026
…68381)

### What problem does this PR solve?

Related PR: #67784. This is the second split, based on the primitives
merged in #68297.

Arrow batch conversion mixes protocol serialization with table-specific
UUID and Variant handling, while writers construct schemas separately.
Introduce explicit Doris, Python, Arrow Flight, Parquet, Hive, Iceberg
and Paimon convertors, instantiated with their own schema parameters and
timezone. Move table schema construction/decoding into the convertors
and route nested SerDe writes through the selected format.

Separate Parquet, Hive and Iceberg writers and migrate all callers.
Preserve current Parquet timestamp encoding, UUID/Variant layouts and
external type mappings. Keep Python's numeric conversion timezone
consistent with its declared Arrow schema, and reject incompatible
nested target schemas before casts or child access. No FE, Thrift, ORC
timestamp, or binary computation changes are included.

### Release note

Fix Python UDF timestamp conversion to preserve wall-clock values when
the Arrow protocol declares a fixed-offset timezone.

### Check List (For Author)

- Test
- [x] Unit Test: schema ownership/isolation, slices, invalid schemas,
nested/null values, UUID/Variant bytes, fixed-offset Python batches, and
Parquet timestamp representation.
- [x] Regression test: add Python UDF/UDTF/UDAF timezone assertions and
extract the corresponding existing Python snapshot corrections from
#67784.
- Behavior changed:
- [x] Yes: align Python UDF conversion with its Arrow timezone
declaration; report invalid nested schema bindings as errors.
- Does this need documentation?
    - [x] No.

Validation: ASAN BE build and 396 selected tests passed; the final
incremental build and 285 focused tests also passed. clang-format 16 and
Groovy/embedded Python syntax checks passed. Python SQL and
external-catalog end-to-end regressions were not run locally and remain
for CI.

### Check List (For Reviewer who merge this PR)

- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by one committer. reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants