Files
Gabriel a01cc89378 [fix](external) Preserve external binary, UUID and timestamp mappings on master (#68786)
### What problem does this PR solve?

Related PR: #68532. Supersedes #68603.

Preserve external binary, UUID, and timestamp semantics across connector
schemas, JDBC transport, file readers, and external writes. The tables
below list the type mappings changed by this PR, including mappings that
previously depended on an opt-in property.

### Behavior changes

- The binary mappings listed below now expose `VARBINARY` and preserve
raw bytes, including invalid UTF-8, embedded NULs, and trailing zeros.
File TVFs also apply this rule to nested binary fields. `DESCRIBE` and
query results therefore expose binary types and hexadecimal values
instead of decoded text.
- External logical UUID types map to native Doris `UUID`, including
nested fields, independently of binary mapping options. Native UUID
inputs and canonical/compact UUID text can be written to Iceberg UUID
columns; invalid text fails explicitly. UUID file reads preserve network
byte order and can load directly into native Doris UUID columns.
- Timestamps representing an instant map to `TIMESTAMPTZ`; timestamps
representing local wall-clock fields map to `DATETIMEV2`. Instant
semantics also apply to MySQL `TIMESTAMP` and ClickHouse
`DateTime`/`DateTime64` without an explicit timezone in the type name.
`TIMESTAMPTZ` preserves the instant, not the source timezone identifier
or original offset spelling.
- Catalog properties `enable.mapping.varbinary` /
`enable.mapping.timestamp_tz` and file TVF properties
`enable_mapping_varbinary` / `enable_mapping_timestamp_tz` remain
accepted but no longer select the mappings below. Absent, `false`, and
`true` settings produce the same types. Recursive mappings apply to
supported ARRAY/MAP/STRUCT fields.
- JDBC reads and writes preserve UTC instants and supported fractional
precision independently of JVM/session timezone. PostgreSQL
infinite/out-of-range `timestamptz` values become SQL NULL, with
nullable metadata propagated even for source NOT NULL columns. MySQL
retains its configured zero-date handling. Predicates mixing instant and
wall-clock timestamps stay local when remote evaluation would change
their meaning.
- Binary partition values retain byte identity and distinguish SQL NULL
from literal text. Iceberg UUID writes preserve UUID semantics through
ARRAY/MAP/STRUCT leaves, including map keys and values; fixed-width
binary writes validate the actual byte count. Paimon legacy ORC routing
uses the split's owning branch and historical schema so instant columns
use the appropriate reader.
- MySQL query TVF wrapping distinguishes `--1` arithmetic from comments
and honors the remote connection’s `NO_BACKSLASH_ESCAPES` mode. SQL
Server UUID range predicates stay local because its GUID ordering
differs from Doris. Iceberg UUID value predicates also stay local
because file bounds can otherwise prune matching UUID rows.
- Ordinary views retain binary types and declared VARBINARY bounds.
Native-table materialization remains subject to Doris storage
restrictions and needs explicit conversion where the target cannot store
VARBINARY. Unsupported VARBINARY comparisons/functions remain
unsupported; FE validation prevents implicit text coercion for the
guarded operations. CDC JSON ingestion retains its existing transport
conversions.

### Changed read mappings: external system → Doris

“Before” describes the previous default (mapping options absent/false).
Where an opt-in mapping already existed, this PR makes that mapping
unconditional. `p` denotes the effective fractional precision supported
by the mapper, capped at 6 (HMS uses the catalog timestamp precision);
fixed precisions are shown explicitly. `n` denotes the supported
declared byte bound; unqualified `VARBINARY` uses Doris's default
maximum bound. Unchanged source-type mappings are omitted.

| External system / access path | External type | Before | After |
| --- | --- | --- | --- |
| Hive / HMS | `BINARY` | `STRING` | `VARBINARY` |
| Hive / HMS | `TIMESTAMP WITH LOCAL TIME ZONE` | `DATETIMEV2(p)` |
`TIMESTAMPTZ(p)` |
| Iceberg | `binary` | `STRING` | `VARBINARY` |
| Iceberg | `fixed(n)` | `CHAR(n)` | `VARBINARY(n)` |
| Iceberg | `uuid` | `STRING` | `UUID` |
| Iceberg | `timestamptz` (`shouldAdjustToUTC=true`) | `DATETIMEV2(6)` |
`TIMESTAMPTZ(6)` |
| Paimon | `BINARY(n)`, `VARBINARY(n)` / `BYTES` | `STRING` |
`VARBINARY(n)` / `VARBINARY` |
| Paimon | `TIMESTAMP WITH LOCAL TIME ZONE` / `TIMESTAMP_LTZ(p)` |
`DATETIMEV2(p)` | `TIMESTAMPTZ(p)` |
| Fluss | `BINARY(n)`, `BYTES` | `STRING` | `VARBINARY(n)`, `VARBINARY`
respectively |
| Fluss | `TIMESTAMP_LTZ(p)` | `DATETIMEV2(p)` | `TIMESTAMPTZ(p)` |
| Hudi / Avro logical schema | Logical `uuid` on `string` | `STRING` |
`UUID` |
| Hudi / Avro logical schema | `timestamp-millis`, `timestamp-micros` |
`DATETIMEV2(3)`, `DATETIMEV2(6)` | `TIMESTAMPTZ(3)`, `TIMESTAMPTZ(6)`
respectively |
| Hudi / Avro logical schema | `local-timestamp-millis`,
`local-timestamp-micros` | `BIGINT` | `DATETIMEV2(3)`, `DATETIMEV2(6)`
respectively |
| MaxCompute | `TIMESTAMP` | `DATETIMEV2(6)` | `TIMESTAMPTZ(6)` |
| Trino Connector | `UUID` | Unsupported by the type mapper | `UUID` |
| Trino Connector | `VARBINARY` | `STRING` | `VARBINARY` |
| Trino Connector | `TIMESTAMP(p) WITH TIME ZONE` | `DATETIMEV2(p)` |
`TIMESTAMPTZ(p)` |
| MySQL / MariaDB JDBC | `BINARY`, `VARBINARY`, `TINYBLOB`, `BLOB`,
`MEDIUMBLOB`, `LONGBLOB` | `STRING` | `VARBINARY` with the supported
JDBC-reported byte bound |
| MySQL / MariaDB JDBC | `TIMESTAMP(p)` | `DATETIMEV2(p)` |
`TIMESTAMPTZ(p)` |
| PostgreSQL JDBC | `uuid`, including array elements | `STRING` | `UUID`
|
| PostgreSQL JDBC | `bytea` | `STRING` | `VARBINARY` with the supported
JDBC-reported byte bound |
| PostgreSQL JDBC | `timestamptz` | `DATETIMEV2(p)` | `TIMESTAMPTZ(p)` |
| Oracle JDBC | `BLOB` | `STRING` | `VARBINARY` with the supported
JDBC-reported byte bound |
| Oracle JDBC | `TIMESTAMP(p) WITH LOCAL TIME ZONE` | `DATETIMEV2(p)` |
`TIMESTAMPTZ(p)` |
| Oracle JDBC | `TIMESTAMP(p) WITH TIME ZONE` | `STRING` in the
connector; unsupported in the legacy client | `TIMESTAMPTZ(p)` |
| SQL Server JDBC | `uniqueidentifier` | `STRING` | `UUID` |
| SQL Server JDBC | `binary`, `varbinary`, `image` | `STRING` |
`VARBINARY` with the supported JDBC-reported byte bound |
| SQL Server JDBC | `datetimeoffset(p)` | `STRING` | `TIMESTAMPTZ(p)` |
| DB2 JDBC | `BLOB`, `BINARY`, `VARBINARY` | `STRING` | `VARBINARY` with
the supported JDBC-reported byte bound |
| GBase JDBC | JDBC `BINARY`, `VARBINARY`, `LONGVARBINARY`, `BLOB` |
`STRING` | `VARBINARY` with the supported JDBC-reported byte bound |
| ClickHouse JDBC | `DateTime`, `DateTime('zone')` | `DATETIMEV2(0)` |
`TIMESTAMPTZ(0)` |
| ClickHouse JDBC | `DateTime64(p[, 'zone'])` | `DATETIMEV2(p)` |
`TIMESTAMPTZ(p)` |
| Trino / Presto JDBC | `uuid`, including array elements | Unsupported
by the type mapper | `UUID` |
| Trino / Presto JDBC | `TIMESTAMP(p) WITH TIME ZONE`, `TIMESTAMP WITH
TIME ZONE` | `DATETIMEV2` / `STRING`, or a metadata precision-parsing
failure depending on spelling/path | `TIMESTAMPTZ(p)`; precision
defaults to 6 when absent |
| Trino / Presto JDBC connector | Unparameterized `TIMESTAMP` metadata |
`STRING` | `DATETIMEV2(6)` |
| Parquet file TVFs | Raw `BYTE_ARRAY`, including binary fallback for
legacy ENUM/BSON annotations | `STRING` | `VARBINARY` |
| Parquet file TVFs | UUID logical annotation on
`FIXED_LEN_BYTE_ARRAY(16)` | `STRING` during legacy schema inference;
scanner V2 already supported native UUID; opt-in binary mapping exposed
`VARBINARY(16)` | `UUID` consistently |
| Parquet file TVFs | Logical `TIMESTAMP` with `isAdjustedToUTC=true` |
`DATETIMEV2(3/6)` | `TIMESTAMPTZ(3/6)` for millis / micros or nanos |
| ORC file TVFs | `BINARY` | `STRING` | `VARBINARY` |
| ORC file TVFs | `BINARY` with `iceberg.binary-type=UUID` | `STRING` |
`UUID` |
| ORC file TVFs | `TIMESTAMP_INSTANT` | `DATETIMEV2(6)` |
`TIMESTAMPTZ(6)` |

OceanBase JDBC delegates to the MySQL or Oracle mapper according to its
database mode and inherits the corresponding changed mappings above.

The file TVF rows apply to schema inference through supported file
sources such as S3, HDFS, LOCAL, and HTTP. Existing unzoned timestamp
mappings (for example MySQL `DATETIME`, PostgreSQL `timestamp`, Iceberg
`timestamp`, and Paimon/Fluss `TIMESTAMP`) remain `DATETIMEV2` and are
omitted from the table. Precision beyond microseconds is truncated.
Physical binary storage with a recognized STRING/UTF8/DECIMAL annotation
retains that logical type.

### Changed external schema mappings: Doris → external system

| External target | Doris type | Before | After |
| --- | --- | --- | --- |
| Iceberg | `UUID`, including nested fields | Unsupported by the reverse
schema mapper | Iceberg `uuid` |
| Hive / HMS | `VARBINARY` | Unsupported by the reverse schema mapper |
Hive `binary`, including nested fields |
| Paimon | `DATETIMEV2(p)` | Paimon `TIMESTAMP(6)`; source precision
discarded | Paimon `TIMESTAMP(p)` |
| Paimon | `TIMESTAMPTZ(p)` | Unsupported by the reverse schema mapper |
Paimon `TIMESTAMP_LTZ(p)` |
| MaxCompute | `TIMESTAMPTZ` | Unsupported by the reverse schema mapper
| MaxCompute `TIMESTAMP`, with UTC microsecond write transport |

### Release note

The external binary, UUID, and timestamp mappings listed above are
determined by source semantics. The former mapping options no longer opt
out of VARBINARY or TIMESTAMPTZ. Applications reading these columns may
observe changed schemas, binary result formatting, and timestamp
semantics. External UUIDs retain native UUID semantics rather than
mapping to string or binary.

### Validation

- FE and Java plugin package build passed with 339 targeted unit tests,
plus 59 JDBC planning/binding tests. FE Checkstyle passed.
- 202 BE unit tests passed, covering UUID byte order and nullability,
Parquet/ORC data and equality-delete combinations, missing-column
defaults, ORC writes, and UUID partitions. Changed C++ lines passed
clang-tidy; clang-format 16 and build hygiene checks passed.
- 13 local regression suites passed: seven UUID file suites, PostgreSQL
native UUID and all-types suites, MySQL query TVFs under both SQL modes,
Iceberg nested UUID writes, cross-format reads/deletes, and initial
defaults. Result files are generated by actual execution and compared in
a separate run.
- Iceberg write coverage includes canonical/compact text, native UUID
INSERT SELECT, identity partitions, ARRAY/MAP/STRUCT nesting, NULL and
empty containers, invalid inputs, and volatile map expressions whose
key/value pairing must be retained. A non-pushable reference verifies
UUID equality predicates do not lose rows to file pruning.
- SQL Server, Trino, and Hudi UUID paths have unit coverage; their
external integration suites were not run in this update.
- Previous revision validation also covered the affected HDFS, Paimon,
and HTTP TVF suites.

### Check List (For Author)

- [x] Unit and regression coverage included.
- [x] User-visible behavior and changed type mappings described.

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

- [ ] Confirm the release note.
- [ ] Confirm test cases.
- [ ] Confirm document.
- [ ] Add branch pick label if needed.
2026-10-10 18:04:41 +08:00
..