Skip to content

[fix](arrow-flight) Fix utf8/large string schema-array mismatch for oversized columns#65789

Open
morningman wants to merge 2 commits into
apache:masterfrom
morningman:fix-adbc-large-string-schema
Open

[fix](arrow-flight) Fix utf8/large string schema-array mismatch for oversized columns#65789
morningman wants to merge 2 commits into
apache:masterfrom
morningman:fix-adbc-large-string-schema

Conversation

@morningman

Copy link
Copy Markdown
Contributor

Problem

When an Arrow Flight result column's data reaches 2GB within a single batch,
FromBlockToRecordBatchConverter silently rebuilt that column's array as
large_utf8/large_binary (int64 offsets) while the RecordBatch schema kept
utf8/binary (int32 offsets). The batch then violates the Arrow spec — its
array offsets (int64) disagree with its declared schema type (int32):

  • generic Arrow clients (pyarrow, Flight SQL JDBC, …) read the int64 offset
    buffer as int32 → corrupted string values;
  • the Doris receiver (read_column_from_arrow, cross-cluster type=doris
    catalog) derives negative lengths → out-of-bounds memcpy / crash.

Arrow Flight sends one fixed schema for the whole stream, so a per-batch type
swap can never be correct.

Fix

Keep the schema authoritative and never swap array types:

  • Remove the silent large_utf8/large_binary upgrade in the converter.
  • Add convert_to_arrow_batches(): an O(1) fast-path check (no cost on the
    normal path); when a string/binary column would exceed the int32 offset
    limit, split the block by rows so every emitted batch stays valid utf8.
    A single value that alone exceeds the limit returns a clear error instead of
    corrupting data or crashing.
  • The Arrow Flight local/remote readers drain the (possibly several) batches
    produced from one block.
  • Validate() the RecordBatch after building it, so any future schema/array
    divergence fails loudly here instead of silently corrupting downstream.
  • Replace the hard-coded 2GB constant with BE config
    arrow_flight_result_max_utf8_bytes (default 2^31) so tests can exercise the
    split path without materializing 2GB of data.

Columns under the limit are emitted exactly as before (utf8/binary) — no
wire change on the normal path; only the previously-broken >2GB path changes.

Testing

  • BE unit tests (DataTypeSerDeArrowTest): split-and-round-trip byte identity,
    no-split under the limit, single-value-over-limit error, and split driven by
    the tightest column.
  • arrow_flight_sql_p0 regression passes unchanged (no wire change on the
    normal path).

…versized columns

When an Arrow Flight result column's data reaches 2GB within a single batch,
FromBlockToRecordBatchConverter silently rebuilt that column's array as
large_utf8/large_binary (int64 offsets) while the RecordBatch schema kept
utf8/binary (int32 offsets). The batch then violates the Arrow spec: its array
offsets (int64) disagree with its declared schema type (int32).

- Generic Arrow clients (pyarrow, Flight SQL JDBC, ...) read the int64 offset
  buffer as int32 -> corrupted string values.
- The Doris receiver (read_column_from_arrow, cross-cluster type=doris catalog)
  derives negative lengths -> out-of-bounds memcpy / crash.

Arrow Flight sends one fixed schema for the whole stream, so a per-batch type
swap can never be correct.

Fix: keep the schema authoritative and never swap array types.
- Remove the silent large_utf8/large_binary upgrade in the converter.
- Add convert_to_arrow_batches(): an O(1) fast-path check (no cost on the normal
  path); when a string/binary column would exceed the int32 offset limit, split
  the block by rows so every emitted batch stays valid utf8. A single value that
  alone exceeds the limit returns a clear error instead of corrupting or
  crashing.
- The Arrow Flight local/remote readers drain the batches produced from one
  block.
- Validate() the RecordBatch after building it so any future schema/array
  divergence fails loudly instead of silently corrupting downstream.
- Replace the hard-coded 2GB constant with BE config
  arrow_flight_result_max_utf8_bytes (default 2^31) so tests can exercise the
  split path without materializing 2GB of data.

Columns under the limit are emitted exactly as before (utf8/binary): no wire
change on the normal path; only the previously-broken >2GB path changes.
@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?

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 29470 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 777f405263016ac8f63feac52640bfbfdd12a26f, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17707	4063	3994	3994
q2	2071	327	208	208
q3	10296	1426	842	842
q4	4684	475	339	339
q5	7549	864	576	576
q6	194	175	137	137
q7	778	831	631	631
q8	9366	1592	1662	1592
q9	5612	4404	4375	4375
q10	6767	1741	1477	1477
q11	511	357	321	321
q12	748	584	459	459
q13	18114	3458	2755	2755
q14	284	268	246	246
q15	q16	789	781	709	709
q17	1025	1010	1054	1010
q18	6893	5875	5586	5586
q19	1170	1305	992	992
q20	796	705	558	558
q21	5628	2619	2366	2366
q22	435	356	297	297
Total cold run time: 101417 ms
Total hot run time: 29470 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4421	4302	4303	4302
q2	296	315	212	212
q3	4588	4960	4445	4445
q4	2075	2163	1379	1379
q5	4419	4254	4284	4254
q6	235	173	127	127
q7	1724	1951	1937	1937
q8	2580	2219	2233	2219
q9	8058	8225	7875	7875
q10	4696	4638	4221	4221
q11	575	455	390	390
q12	761	771	558	558
q13	3328	3632	3007	3007
q14	294	307	286	286
q15	q16	727	732	639	639
q17	1406	1308	1340	1308
q18	8194	7315	7403	7315
q19	1183	1157	1078	1078
q20	2217	2219	1945	1945
q21	5294	4581	4437	4437
q22	529	465	396	396
Total cold run time: 57600 ms
Total hot run time: 52330 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 178975 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 777f405263016ac8f63feac52640bfbfdd12a26f, data reload: false

query5	4332	636	484	484
query6	467	223	207	207
query7	4834	599	336	336
query8	335	214	180	180
query9	8803	4085	4075	4075
query10	472	360	326	326
query11	5906	2343	2195	2195
query12	161	102	103	102
query13	1260	610	437	437
query14	6248	5272	4988	4988
query14_1	4309	4287	4275	4275
query15	210	196	180	180
query16	1019	482	483	482
query17	935	735	584	584
query18	2451	484	351	351
query19	214	204	150	150
query20	115	111	110	110
query21	238	159	137	137
query22	13735	13554	13298	13298
query23	17394	16678	16023	16023
query23_1	16322	16223	16156	16156
query24	7485	1759	1278	1278
query24_1	1317	1258	1275	1258
query25	595	458	394	394
query26	1371	350	226	226
query27	2555	593	383	383
query28	4445	1992	2002	1992
query29	1085	631	496	496
query30	360	267	231	231
query31	1122	1088	981	981
query32	109	67	64	64
query33	528	324	261	261
query34	1199	1147	653	653
query35	769	782	687	687
query36	1185	1202	1063	1063
query37	152	111	94	94
query38	1930	1698	1671	1671
query39	874	885	858	858
query39_1	838	829	849	829
query40	243	162	139	139
query41	64	62	62	62
query42	94	93	93	93
query43	335	321	285	285
query44	1448	740	754	740
query45	193	185	176	176
query46	1080	1201	758	758
query47	2127	2168	1973	1973
query48	407	428	284	284
query49	579	422	311	311
query50	1039	439	350	350
query51	11153	11039	10769	10769
query52	89	87	82	82
query53	287	275	196	196
query54	308	229	214	214
query55	75	71	65	65
query56	295	290	290	290
query57	1330	1286	1209	1209
query58	288	261	269	261
query59	1621	1678	1456	1456
query60	310	269	253	253
query61	153	149	145	145
query62	548	495	430	430
query63	243	200	206	200
query64	2782	1025	876	876
query65	4719	4610	4619	4610
query66	1888	508	377	377
query67	29250	29172	29100	29100
query68	3163	1480	922	922
query69	410	299	262	262
query70	1085	1020	949	949
query71	389	341	318	318
query72	3101	2671	2398	2398
query73	876	763	417	417
query74	5074	4929	4732	4732
query75	2521	2503	2136	2136
query76	2317	1162	774	774
query77	347	393	277	277
query78	11905	11905	11387	11387
query79	1373	1114	746	746
query80	1295	563	452	452
query81	526	327	290	290
query82	597	159	125	125
query83	385	322	291	291
query84	320	161	132	132
query85	982	619	531	531
query86	426	275	269	269
query87	1827	1827	1740	1740
query88	3802	2785	2775	2775
query89	432	379	322	322
query90	1914	203	208	203
query91	208	193	161	161
query92	63	60	60	60
query93	1772	1515	1003	1003
query94	710	344	310	310
query95	792	571	466	466
query96	998	787	341	341
query97	2629	2635	2500	2500
query98	217	206	202	202
query99	1100	1112	975	975
Total cold run time: 264600 ms
Total hot run time: 178975 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.91 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 777f405263016ac8f63feac52640bfbfdd12a26f, data reload: false

query1	0.00	0.00	0.00
query2	0.12	0.05	0.05
query3	0.25	0.14	0.14
query4	1.61	0.14	0.15
query5	0.24	0.23	0.22
query6	1.24	1.14	1.05
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.39	0.30	0.32
query10	0.57	0.55	0.55
query11	0.20	0.14	0.13
query12	0.18	0.14	0.13
query13	0.46	0.48	0.47
query14	1.04	1.00	1.00
query15	0.62	0.58	0.59
query16	0.32	0.32	0.33
query17	1.10	1.16	1.07
query18	0.22	0.21	0.21
query19	2.01	1.89	1.92
query20	0.02	0.01	0.02
query21	15.44	0.19	0.15
query22	4.98	0.06	0.05
query23	16.09	0.31	0.12
query24	2.96	0.44	0.33
query25	0.11	0.06	0.05
query26	0.73	0.21	0.14
query27	0.04	0.04	0.03
query28	3.50	0.89	0.51
query29	12.54	4.15	3.28
query30	0.29	0.15	0.15
query31	2.78	0.61	0.32
query32	3.21	0.58	0.49
query33	3.21	3.26	3.18
query34	15.50	4.24	3.49
query35	3.54	3.51	3.53
query36	0.56	0.44	0.42
query37	0.09	0.07	0.06
query38	0.06	0.04	0.04
query39	0.04	0.03	0.03
query40	0.19	0.17	0.16
query41	0.09	0.04	0.03
query42	0.04	0.03	0.03
query43	0.04	0.03	0.04
Total cold run time: 96.72 s
Total hot run time: 24.91 s

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@morningman

Copy link
Copy Markdown
Contributor Author

This should close #62681

@morningman

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.

Request changes. Keeping the fixed Flight stream schema authoritative is the right direction, and the ordinary top-level String/Varbinary splitter preserves row order and nulls, but four correctness gaps remain in the targeted oversized-result path; each is described inline.

Critical checkpoint conclusions:

  • Goal and proof: Partially achieved. The schema/array mismatch is removed for ordinary top-level String/Varbinary, but SerDe-rendered types can bypass accounting (MAIN-2), nested int32-offset descendants are not split (MAIN-3), remote/proxy results can fail before conversion (MAIN-4), and the exact Arrow 17 boundary is off by one (MAIN-1). The added unit tests prove only the plain top-level utf8 helper path.
  • Scope and reuse: The change is focused and reuses the existing converter/readers. No unrelated source change was found.
  • Concurrency: The new pending deque is used by one serialized RecordBatchStream reader. Producer/consumer buffer state remains under existing locks; no new lock-order, deadlock, or atomic-ordering issue was found.
  • Lifecycle and memory: Pending batches have acyclic reader ownership, drain before the next source fetch, and are released on return/destruction. Eager conversion retains one source block's Arrow output, but no separate lifecycle leak was substantiated.
  • Configuration: The mutable limit is snapshotted per conversion and subsequent blocks observe updates, but its validated maximum exceeds Arrow 17's actual builder capacity (MAIN-1).
  • Compatibility and parallel paths: Keeping utf8/binary schema types is wire-compatible on normal results and fixes the invalid type swap. Both local and remote readers were edited, but the remote protobuf boundary remains before splitting (MAIN-4), and recursive List/Map/Struct paths remain uncovered (MAIN-3). No storage-format or rolling-upgrade protocol change applies.
  • Conditions, errors, and data correctness: Statuses are otherwise propagated and the single-value error includes useful field/row/size context. The physical-size assumption is not a valid invariant for all String/Binary SerDes and can silently produce an empty valid value after unchecked int32 narrowing (MAIN-2).
  • Tests and results: New tests cover non-null top-level utf8 splitting, no-split, one oversized value, and two top-level columns. They do not cover binary, nullable, transformed/complex SerDes, nested schemas, exact Arrow capacity, reader draining/EOF, or proxy routing. This review was static as required; no local build was attempted. Current CI shows Linux compile/BE UT and formatting green; regression jobs are pending, and macOS BE UT stopped before tests because the runner had JDK 25 instead of required JDK 17.
  • Performance and observability: The normal fast path is O(fields), the rare slow path is O(rows times tracked fields), and existing conversion timers/packet logs remain. Correct emitted-size accounting should avoid double rendering where practical. No additional observability blocker was found.
  • Transactions, persistence, data writes, and FE-BE variable propagation: Not applicable to this result-serialization change.
  • Additional user focus: No additional user-provided focus was supplied; the complete PR was reviewed.

Overall, the PR should not merge until the four inline correctness issues and their missing boundary/integration coverage are addressed.

Comment thread be/src/common/config.cpp Outdated
// below 2^31 (= INT32_MAX + 1). When a block would exceed this, the Arrow Flight readers split
// it by rows into multiple batches so each batch stays valid. Default is the hard 2^31 limit;
// only lower it (e.g. in tests) to exercise the split path without materializing 2GB of data.
DEFINE_mInt64(arrow_flight_result_max_utf8_bytes, "2147483648");

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.

[P2] Match Arrow 17's actual builder capacity

Doris pins Arrow 17, where BaseBinaryBuilder::memory_limit() is INT32_MAX - 1 and ValidateOverflow rejects anything larger. This exclusive cap is INT32_MAX + 1, and the splitter only cuts on >= max_bytes, so an ordinary String/Varbinary payload totaling exactly INT32_MAX is passed through and then fails with CapacityError instead of being split. Please cap the exclusive threshold at INT32_MAX (so emitted data is at most INT32_MAX - 1) and add an exact boundary test.

// batch -- zero added cost on the normal path, and this never misses an overflow.
std::vector<int> big_fields;
for (int idx = 0; idx < num_fields; ++idx) {
if (is_int32_offset_binary_type(schema->field(idx)->type()) &&

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] Measure the bytes that each SerDe actually appends

This gate assumes physical IColumn::byte_size()/get_data_at() matches the Arrow payload, but several types with an utf8/binary schema transform their data. For example, binary JSONB below 2 GiB can render above INT32_MAX after escaping; DataTypeJsonbSerDe then narrows that size with cast_set<int, size_t, false>, producing a non-positive length on supported targets. Arrow 17 treats that as a valid empty value, so Validate() passes and local Flight silently corrupts the result. Variant, formatted fixed-width types, and Bitmap/HLL/QuantileState have the same storage-vs-SerDe mismatch. Please base cuts on the emitted SerDe payload and reject before any int32 narrowing, with JSONB/Variant/complex-type tests.


// Only utf8/binary use int32 offsets and can overflow at 2^31; fixed-size and large_* cannot.
bool is_int32_offset_binary_type(const std::shared_ptr<arrow::DataType>& type) {
return type->id() == arrow::Type::STRING || type->id() == arrow::Type::BINARY;

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] Recurse into nested int32-offset builders

This predicate only recognizes a top-level STRING/BINARY. ARRAY, MAP, and STRUCT can contain shared child String/Binary builders (and List/Map themselves have int32 element offsets), but those fields always take the fast path. For example, two ARRAY<STRING> parent rows with individually representable child data can exceed the child builder together and fail conversion, although emitting one parent row per batch would succeed. Please account for descendant byte/element limits per top-level row, return a clear error when one parent row cannot fit, and add nested nullable collection tests.

auto st = convert_to_arrow_batch(*_block, _schema, arrow::default_memory_pool(), out,
_timezone_obj);
std::vector<std::shared_ptr<arrow::RecordBatch>> batches;
auto st = convert_to_arrow_batches(*_block, _schema, arrow::default_memory_pool(), &batches,

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] Split before the remote protobuf boundary

The remote reader reaches this helper only after _fetch_data() receives and deserializes one complete PFetchArrowDataResult.block. On the producing BE, GetArrowResultBatchCtx::on_data() rejects any response whose ByteSizeLong() exceeds INT32_MAX and clears the block, so a proxy-routed high-entropy result at the size this PR targets fails before this line even though the local reader can split it. Please chunk on the producing BE before protobuf serialization (or use a streaming transport) and add a proxy-routed boundary test.

@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.

Supplemental request for changes. Review 4730854844 already covers the exact Arrow builder boundary, SerDe/storage byte mismatch, nested int32-offset builders, and the remote protobuf boundary. This review adds two distinct blockers inline: the complete Arrow Flight IPC body can still exceed Arrow 17's transport limit even when each value buffer fits, and the new mutable configuration accepts and can persist unchecked invalid values.

Critical checkpoint conclusions:

  • Goal and proof: Partially achieved. Keeping the declared utf8/binary schema authoritative fixes the original schema-array mismatch for ordinary top-level strings, but the live review's four issues plus the two additional inline issues leave the oversized-result path incomplete. The added tests prove only the direct plain-ColumnString helper behavior.
  • Focus, minimality, and reuse: The change is localized and reuses the existing converter and readers; no unrelated source change was found. The new helper's assumptions are narrower than the supported Doris-to-Arrow type and transport contracts.
  • Concurrency: The pending-batch deques are consumed by the serialized stream-reader calls, and the existing producer/result-buffer locking is unchanged. No new race, deadlock, or atomic-ordering issue was substantiated.
  • Lifecycle and ownership: Pending batches drain before another source block is fetched and are released with the reader. EOF and error statuses otherwise propagate through the reader; the already-reported remote serialization failure occurs before this lifecycle can help.
  • Configuration: The declared range and default are documented, but the exact-capacity problem is already reported and the mutable validator path validates the old value, then commits and optionally persists the unchecked candidate (second inline issue).
  • Compatibility and parallel paths: Preserving utf8/binary schema types is wire-compatible for normal results and removes the invalid large_utf8/large_binary swap. Local and remote readers and generic converter call sites were checked; nested, transformed-SerDe, remote-proxy, and complete Flight-payload paths remain incomplete as reported.
  • Conditions, errors, and data correctness: The ordinary single-value error is contextual, but supported transformed/nested types can bypass its assumptions, and batches that pass RecordBatch::Validate() can still be rejected by Flight payload validation (first inline issue).
  • Tests and results: New tests cover direct top-level ColumnString split/no-split/error cases, but not FlightPayload validation, nested or transformed types, remote no-compression routing, or set_config invalid/recovery/persistence behavior. This review was static as required; no local build was attempted. Current CI has Linux compile/BE UT, formatting, external regression, cloud_p0, and vault_p0 green; P0 and NonConcurrent regression remain pending. macOS BE UT stopped before tests because the runner used JDK 25 instead of required JDK 17.
  • Observability: Existing conversion timers and contextual status messages remain. No additional observability blocker was found beyond the already-reported remote failure occurring before a useful client response.
  • Transactions, persistence, data writes, and FE-BE variables: No transaction or storage-data write path changes. BE config persistence is applicable and unsafe under the new mutable declaration (second inline issue); no new FE-BE variable propagation applies.
  • Performance: The common fast path remains cheap and the row scan is confined to suspected large columns. Correct full-payload and SerDe-aware sizing should preserve bounded overhead and avoid rendering values twice where possible.
  • Additional user focus: No additional focus was supplied; the complete PR and its upstream/downstream paths were reviewed.

The review is complete after three convergence rounds. The PR should not merge until the existing four issues and the two additional inline blockers are addressed with boundary and integration coverage.

schema->field(big_fields[c])->name(), end, rb, max_bytes);
}
row_bytes[c] = rb;
if (running[c] + rb >= max_bytes) {

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] Bound the complete Flight IPC body, not only value bytes

The exact per-buffer threshold is covered separately; even after that is corrected, Arrow 17's FlightPayload::Validate() rejects an ipc_message.body_length above INT32_MAX, while this cut accounts only for one tracked value buffer. Offsets, validity buffers, padding, fixed-width columns, and other individually sub-limit string columns all contribute to the same body. The existing BigString test is already a counterexample: its 2,147,483,640 value bytes pass this < 2^31 check by 8 bytes, but the offset buffer makes the Flight payload too large even though RecordBatch::Validate() succeeds. Please size/cut for the complete IPC body as well and add a Flight-payload-level test.

Comment thread be/src/common/config.cpp Outdated
// below 2^31 (= INT32_MAX + 1). When a block would exceed this, the Arrow Flight readers split
// it by rows into multiple batches so each batch stays valid. Default is the hard 2^31 limit;
// only lower it (e.g. in tests) to exercise the split path without materializing 2GB of data.
DEFINE_mInt64(arrow_flight_result_max_utf8_bytes, "2147483648");

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] Do not expose this validator through the broken mutable path

DEFINE_mInt64 exposes this value through /api/update_config, but UPDATE_FIELD invokes the registered zero-argument validator before assigning new_value, so it validates the old default and then accepts 0, -1, or a value above 2^31. Zero makes every nonempty result hit the row-limit error, while a negative value becomes a huge size_t and disables splitting; after that, a valid recovery update is rejected because the invalid old value is what gets validated. With persist=true, the unchecked value is also written to be_custom.conf and can prevent the next startup. Since the comment says this is lowered only in tests, please make it non-mutable, or fix the update path to validate the candidate before committing it, and add set_config coverage.

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 76.85% (83/108) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 75.02% (30510/40669)
Line Coverage 59.14% (336244/568596)
Region Coverage 55.79% (281715/504912)
Branch Coverage 57.18% (125214/218973)

…type serde size

Follow-up on the review of this PR:

- Cap the split threshold at INT32_MAX (was 2^31). Arrow's BaseBinaryBuilder
  rejects a value buffer larger than memory_limit() = INT32_MAX - 1; the
  splitter cuts before a column reaches the threshold, so INT32_MAX keeps each
  committed batch at most INT32_MAX - 1 bytes -- exactly the builder limit.
  Previously 2^31 let a batch total INT32_MAX and then fail with CapacityError.

- Guard JSONB -> Arrow against silent corruption. JSONB is stored as binary but
  rendered to (usually larger) JSON text, so the physical column size does not
  bound the arrow payload; the old unchecked cast_set<int, size_t, false> of a
  rendered length > INT32_MAX would wrap to a bogus small/negative length that
  Arrow silently accepts. Reject with a clear error before narrowing. (Variant
  already uses a checked cast.)

- Scope the row-splitter to raw string/binary types (is_string_type || VARBINARY)
  whose SerDe appends get_data_at(row) verbatim, so physical byte_size equals the
  emitted payload and per-row cuts are exact. Transform-serde types that also map
  to utf8/binary (JSONB, Variant, largeint/date/datetime text) stay on the fast
  path and rely on their checked size cast plus the Arrow builder capacity check
  to error cleanly instead of being mis-split (Variant has no get_data_at at all).

Tests: exact INT32_MAX-vs-(INT32_MAX-1) boundary; transform type (largeint)
whose physical size exceeds a lowered threshold is not split.
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 29662 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 885f8c1374abd4727c0dbb4a01ad49aea950dc5e, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17604	4110	4117	4110
q2	2044	327	204	204
q3	10250	1450	824	824
q4	4680	473	348	348
q5	7597	869	561	561
q6	192	179	139	139
q7	790	824	617	617
q8	9411	1540	1649	1540
q9	5646	4348	4359	4348
q10	6783	1836	1495	1495
q11	522	356	321	321
q12	741	588	454	454
q13	18127	3483	2797	2797
q14	267	267	245	245
q15	q16	800	772	707	707
q17	1038	1145	901	901
q18	6861	5784	5621	5621
q19	1315	1382	1129	1129
q20	786	674	573	573
q21	6051	2666	2429	2429
q22	442	366	299	299
Total cold run time: 101947 ms
Total hot run time: 29662 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4502	4379	4397	4379
q2	289	328	209	209
q3	4596	4931	4456	4456
q4	2103	2155	1364	1364
q5	4423	4283	4284	4283
q6	233	178	128	128
q7	1733	2293	1671	1671
q8	2577	2241	2232	2232
q9	8046	8155	8052	8052
q10	4710	4699	4246	4246
q11	596	414	393	393
q12	759	757	546	546
q13	3301	3553	2986	2986
q14	289	313	287	287
q15	q16	719	740	648	648
q17	1386	1360	1393	1360
q18	7882	7250	7247	7247
q19	1178	1137	1109	1109
q20	2212	2201	1939	1939
q21	5291	4599	4460	4460
q22	509	475	409	409
Total cold run time: 57334 ms
Total hot run time: 52404 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 178572 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 885f8c1374abd4727c0dbb4a01ad49aea950dc5e, data reload: false

query5	4362	630	481	481
query6	482	222	219	219
query7	5057	627	327	327
query8	337	196	174	174
query9	8799	4141	4119	4119
query10	478	368	304	304
query11	5932	2356	2139	2139
query12	156	100	102	100
query13	1244	608	423	423
query14	6214	5215	4858	4858
query14_1	4195	4212	4198	4198
query15	213	209	181	181
query16	1194	475	439	439
query17	913	721	547	547
query18	2423	468	345	345
query19	204	199	147	147
query20	109	106	105	105
query21	244	161	138	138
query22	13574	13597	13457	13457
query23	17470	16489	16077	16077
query23_1	16256	16296	16308	16296
query24	7545	1736	1293	1293
query24_1	1298	1291	1312	1291
query25	597	476	404	404
query26	1344	360	224	224
query27	2585	622	388	388
query28	4475	2011	1993	1993
query29	1092	635	511	511
query30	342	273	235	235
query31	1124	1092	985	985
query32	114	63	62	62
query33	548	332	258	258
query34	1175	1194	636	636
query35	772	800	677	677
query36	1206	1201	1030	1030
query37	161	110	93	93
query38	1877	1716	1675	1675
query39	886	905	871	871
query39_1	846	861	830	830
query40	251	170	149	149
query41	78	70	68	68
query42	97	95	97	95
query43	324	328	286	286
query44	1490	797	773	773
query45	207	204	189	189
query46	1080	1266	735	735
query47	2165	2121	2016	2016
query48	413	399	310	310
query49	599	433	320	320
query50	1054	437	339	339
query51	10538	10720	10652	10652
query52	85	93	77	77
query53	265	283	214	214
query54	296	249	241	241
query55	77	75	68	68
query56	341	335	311	311
query57	1319	1283	1224	1224
query58	300	259	271	259
query59	1598	1668	1480	1480
query60	316	284	277	277
query61	212	149	152	149
query62	539	494	438	438
query63	243	204	209	204
query64	2834	1063	863	863
query65	4704	4676	4568	4568
query66	1816	514	380	380
query67	29290	29271	29196	29196
query68	3239	1586	947	947
query69	409	301	270	270
query70	1063	940	936	936
query71	375	345	311	311
query72	3081	2712	2422	2422
query73	812	792	433	433
query74	5055	4927	4745	4745
query75	2683	2525	2135	2135
query76	2332	1232	806	806
query77	361	376	279	279
query78	11729	11877	11338	11338
query79	1382	1151	752	752
query80	1291	567	500	500
query81	526	334	290	290
query82	635	156	122	122
query83	389	323	305	305
query84	333	160	131	131
query85	998	620	519	519
query86	424	287	277	277
query87	1821	1820	1768	1768
query88	3705	2801	2792	2792
query89	443	380	329	329
query90	1928	204	194	194
query91	210	197	168	168
query92	66	63	57	57
query93	1677	1518	1040	1040
query94	712	355	310	310
query95	794	588	463	463
query96	1106	857	348	348
query97	2623	2643	2505	2505
query98	224	204	200	200
query99	1103	1146	973	973
Total cold run time: 264090 ms
Total hot run time: 178572 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.97 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 885f8c1374abd4727c0dbb4a01ad49aea950dc5e, data reload: false

query1	0.01	0.01	0.00
query2	0.09	0.05	0.05
query3	0.26	0.14	0.14
query4	1.61	0.14	0.14
query5	0.24	0.23	0.22
query6	1.23	1.08	1.07
query7	0.03	0.00	0.00
query8	0.06	0.04	0.04
query9	0.38	0.31	0.35
query10	0.55	0.55	0.55
query11	0.19	0.14	0.14
query12	0.19	0.14	0.14
query13	0.47	0.47	0.48
query14	1.04	1.00	1.01
query15	0.60	0.59	0.60
query16	0.32	0.32	0.33
query17	1.11	1.10	1.06
query18	0.23	0.22	0.22
query19	2.03	1.89	1.99
query20	0.02	0.02	0.01
query21	15.41	0.21	0.15
query22	4.90	0.06	0.05
query23	16.15	0.30	0.13
query24	2.94	0.42	0.32
query25	0.11	0.06	0.05
query26	0.74	0.20	0.16
query27	0.04	0.04	0.04
query28	3.54	0.92	0.50
query29	12.52	4.11	3.35
query30	0.28	0.15	0.17
query31	2.77	0.58	0.31
query32	3.22	0.58	0.48
query33	3.27	3.16	3.18
query34	15.54	4.24	3.53
query35	3.62	3.43	3.55
query36	0.56	0.44	0.44
query37	0.10	0.07	0.06
query38	0.05	0.04	0.04
query39	0.04	0.03	0.03
query40	0.18	0.16	0.15
query41	0.08	0.04	0.03
query42	0.04	0.02	0.02
query43	0.04	0.03	0.04
Total cold run time: 96.8 s
Total hot run time: 24.97 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 75.61% (93/123) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.58% (29925/40669)
Line Coverage 57.37% (326199/568607)
Region Coverage 53.68% (271033/504921)
Branch Coverage 54.74% (119873/218979)

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