Skip to content

branch-4.1: [fix](arrow) correct the Flight SQL GetTables schema and the TIMESTAMPTZ arrow reader #66344 - #66355

Open
github-actions[bot] wants to merge 1 commit into
branch-4.1from
auto-pick-66344-branch-4.1
Open

branch-4.1: [fix](arrow) correct the Flight SQL GetTables schema and the TIMESTAMPTZ arrow reader #66344#66355
github-actions[bot] wants to merge 1 commit into
branch-4.1from
auto-pick-66344-branch-4.1

Conversation

@github-actions

@github-actions github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Cherry-picked from #66344

…PTZ arrow reader (#66344)

### What problem does this PR solve?

Related Issue: #65615

Problem Summary:

Two places where Doris says one thing about an Arrow value and does
another. Both were found
while reading Doris over Arrow Flight SQL, and both reproduce on master
on their own.

**1. `CommandGetTables` describes types the batches do not carry**

`FlightSqlSchemaHelper.getArrowType` is documented as mirroring
`convert_to_arrow_type` in the
backend -- the schema a client reads from `GetTables` is what it types
its columns by. Two of the
mappings had drifted from what BE actually writes:

- A `DATEV2` column was described as `Date(MILLISECOND)` -- Arrow date64
-- while BE writes
`arrow::Date32Type`, a day number. A client that trusts the schema
renders and compares the
  column as a datetime and then fails on the first batch with
  `not support convert to datetimev2 from arrow type: 16`.
- A complex column was described with a placeholder child: an array's
element type was the Null
type, a map's pair was a bare list, a struct had no fields at all. An
Arrow ARRAY/MAP/STRUCT
carries its element types in its children and nowhere else, so this said
"array of nothing"
about every array in the catalog while BE emitted `ListType(item)`,
`MapType(key, value)` and
  `StructType(fields)` in the data.

`describeTables` already reports the whole tree --
`Column.createChildrenColumn` even names an
array's element `item` and a map's pair `key`/`value`, which is what
Arrow calls them -- so the
children are now built from it, recursively. A descriptor that reports
no children keeps the old
placeholders: a source that cannot describe its nested types is no worse
off than before.

**2. A TIMESTAMPTZ column read from Arrow was decoded by copying its
bits**

`DataTypeTimeStampTzSerDe` never overrode `read_column_from_arrow`, so
the one it inherited from
`DataTypeNumberSerDe<TYPE_TIMESTAMPTZ>` ran: its fixed-width path
`memcpy`s the array's buffer
straight into the column. Arrow hands it int64 EPOCH values; the column
stores PACKED date/time
values. Both are eight bytes wide, so the width check passes, the scan
succeeds, and every row is
silently wrong -- a Doris datetime read back over Arrow Flight SQL
rendered as `+08:05` with no
date in it.

This is reachable today through the `remote_doris` catalog. Its schema
is the remote table's
`Column` objects deserialized verbatim
(`RemoteDorisRestClient.parseColumns`), so a remote
TIMESTAMPTZ column stays TIMESTAMPTZ locally; `RemoteDorisScanNode`
plans the scan as
`FORMAT_ARROW`; and `remote_doris_reader` materializes it through this
serde. The remote side
writes the epochs with the serde's own `write_column_to_arrow`, so both
halves of that round trip
are Doris code and the mismatch is entirely internal.

The other Arrow readers -- `arrow_stream_reader` for the `arrow` file
format,
`python_udtf_function`, `paimon_cpp_reader` -- share the same serde and
are covered by the same
change; whether each of them can present a TIMESTAMPTZ column today is
not something this PR
claims either way.

The reader converts by unit into the microseconds the column stores.
NANO is divided rather than
refused, since no Doris datetime type keeps sub-microsecond digits, and
the division floors so a
pre-1970 instant does not move forward by one microsecond. The value is
read as an instant on the
UTC line, the inverse of what `write_column_to_arrow` emits, so the
round trip is exact.

Two cases are deliberate. A null slot is not converted: the bytes under
it are whatever the source
left there, and running a garbage epoch through the range check would
fail a batch whose rows are
all well-formed. An Arrow type this serde cannot decode is an error
rather than a fallback --
accepting anything eight bytes wide is how the corruption above stayed
invisible.
@github-actions
github-actions Bot requested a review from yiguolei as a code owner August 2, 2026 00:12
@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?

@hello-stephen

Copy link
Copy Markdown
Contributor

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 70.69% (41/58) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.50% (30064/40904)
Line Coverage 57.36% (328413/572585)
Region Coverage 54.28% (273722/504232)
Branch Coverage 55.23% (121886/220681)

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 0.00% (0/31) 🎉
Increment coverage report
Complete coverage report

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