matanper opened a new pull request, #69767:
URL: https://github.com/apache/doris/pull/69767

   ### What problem does this PR solve?
   
   Issue Number: close #68890
   
   Related PR: #68883
   
   Problem Summary:
   
   Stream Load with `format=arrow` could not load the Arrow forms clients
   send for a VARIANT column. `DataTypeVariantV2SerDe::read_column_from_arrow`
   accepted only the exact shape Doris writes for Arrow Flight,
   `struct<metadata: binary, value: binary>` in that field order. It rejected
   the other shapes:
   
   - JSON text, one document per row (`utf8`, `large_utf8`, `binary`,
     `large_binary`), which is how most clients send semi-structured events;
   - `large_binary` children, or the two fields in the other order (pyarrow
     builds the struct by name);
   - a dictionary-encoded `metadata` child. A producer that encodes a record
     batch at a time has one metadata blob per batch. As a plain binary child,
     that blob is repeated in every row: about 1 KB per row for a batch mixing
     ~10 event sources and about 45 KB per row for one mixing all of them,
     against ~1.3 KB of value bytes. As `dictionary<int32, binary>` with one
     entry, it costs a few bytes per row.
   
   On the reader side, FileScannerV2's Arrow stream reader normalizes every
   column before the SerDe sees it, which decodes a dictionary into plain
   binary. For dictionary metadata that copies the shared blob into every row,
   and the SerDe then compares it row by row to find that all rows share it.
   
   This PR:
   
   1. Extends the VARIANT V2 Arrow reader to accept, with or without the
      `arrow.parquet.variant` extension:
      - JSON text in `utf8`/`large_utf8`/`binary`/`large_binary`, parsed with
        the same options as `parse_to_variant`. A NULL row is JSON null.
      - `struct<metadata, value>` in either field order with `binary` or
        `large_binary` children. Rows go through
        `ColumnVariantV2::EncodedRowsAppender`, which validates every row before
        appending and keeps a batch that shares one metadata blob in the
        single-metadata form. Before, each row was copied into a
        `VariantBatchBuilder` and then copied again into the column.
      - the same struct with `dictionary<integer, binary | large_binary>`
        metadata. It maps directly onto `ColumnVariantV2::insert_encoded_rows`:
        dictionary entries are the metadata blobs and indices are the metadata
        ids. When every row uses one entry, only that entry is passed, so no
        row's metadata is copied, hashed or compared.
      Every shape runs `ValidateFull()` on the slice and Variant 
metadata/payload
      validation before the destination changes, so a bad batch appends
      nothing. NULL struct rows become Variant null.
   2. Adds `DataTypeVariantV2SerDe::is_arrow_input_type`. The FileScannerV2
      Arrow stream reader skips `normalize_arrow_array` for a VARIANT column
      whose Arrow type the SerDe reads directly, so dictionary metadata reaches
      it encoded. `is_native_arrow_type`, which the Arrow writer uses to choose
      its output shape, is unchanged.
   
   Benchmark (`be/benchmark/variant/benchmark_variant_arrow_input.hpp`): one
   4064-row batch of 24-field events (~1.3 KB of values per row), read into a
   VARIANT column, RELEASE build, before = master, after = this PR.
   
   | Input | Shared metadata | master | this PR |
   |---|---|---|---|
   | JSON text (`utf8`) | 1.7 KB | rejected | 12.3 ms |
   | JSON text (`utf8`) | 64 KB | rejected | 14.9 ms |
   | `struct<binary, binary>` | 1.7 KB | 39.0 ms | 6.3 ms (6.2x) |
   | `struct<binary, binary>` | 64 KB | 531 ms | 52.9 ms (10x) |
   | dictionary metadata, as the stream reader reads it | 1.7 KB | 41.3 ms 
(decoded first) | 6.0 ms (6.9x) |
   | dictionary metadata, as the stream reader reads it | 64 KB | 620 ms 
(decoded first) | 6.2 ms (100x) |
   
   Medians of 10 runs on one core of a c7i.8xlarge. On master, the
   dictionary row is normalize_arrow_array followed by the SerDe, which is
   what the stream reader did. In this PR the reader passes it through. The
   plain struct with 64 KB metadata is still the slowest binary form, because
   every row's copy of the metadata has to be compared to find that it is
   shared; dictionary metadata avoids that.
   
   ### Release note
   
   Stream Load with `format=arrow` can load VARIANT columns from JSON text
   (`utf8`, `large_utf8`, `binary`, `large_binary`) or from Parquet Variant
   binary `struct<metadata, value>` (with or without the `arrow.parquet.variant`
   extension, `binary` or `large_binary` children, either field order, and the
   `metadata` child optionally dictionary encoded).
   
   ### Check List (For Author)
   
   - Test:
       - Unit Test: `DataTypeVariantV2SerdeArrowInputTest` (12 new cases), with
         the existing `DataTypeVariantV2Serde*` and `ArrowFlightVariantTest`
         suites (59 tests, ASAN)
       - Regression test: `load_p0/stream_load/test_arrow_stream_load_variant`, 
run
         against a RELEASE FE/BE built from this branch (`.out` generated by 
the run)
       - Benchmark: before/after numbers above
   - Behavior changed: Yes. A VARIANT column in an Arrow Stream Load now
     accepts the Arrow types above instead of failing. Text that is not valid
     JSON fails the load, as other Arrow conversion errors do.
   - Does this need documentation: No
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to