Skip to content

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

Open
Gabriel39 wants to merge 2 commits into
apache:branch-4.1from
Gabriel39:dev/arrow-format-convertors
Open

Gabriel39 wants to merge 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)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants