Skip to content

[refine](expr) Bind CAST executors for direct column execution - #69639

Merged
yiguolei merged 1 commit into
apache:masterfrom
Mryange:cast-column-executor
Oct 28, 2026
Merged

yiguolei merged 1 commit into
apache:masterfrom
Mryange:cast-column-executor

Conversation

@Mryange

@Mryange Mryange commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

CAST expressions currently construct temporary Blocks and execute through the generic function framework. Bind column executors during expression preparation, capture conversion settings in CastOptions, and pass RuntimeState during execution. Migrate scalar, complex and Variant conversions to this interface while retaining a FunctionCast compatibility adapter for direct function callers.

Release note

None

Check List (For Author)

  • Test:
    • Unit Test: BUILD_TYPE_UT=ASAN ./run-be-ut.sh --run --filter='*Cast*' -j48: 395 passed, 1 skipped, 4 disabled; includes EvolutionCast overflow/clone and nested MAP deduplication coverage.
    • Regression tests were not run.
  • Behavior changed: No; CAST execution refactor.
  • Does this need documentation: No.

### What problem does this PR solve?

Problem Summary: CAST expressions construct temporary Blocks and execute through
the generic function framework. Bind typed column executors during preparation,
capture conversion settings in CastOptions, and pass RuntimeState during execution.
Keep a FunctionCast compatibility adapter for direct function callers and apply
schema evolution options before binding.

### Release note

None

### Check List (For Author)

- Test: BUILD_TYPE_UT=ASAN ./run-be-ut.sh --run --filter='*Cast*' -j48;
  395 passed, 1 skipped, 4 disabled. Regression tests were not run.
- Behavior changed: No; CAST execution refactor.
- Does this need documentation: No.
@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?

@Mryange

Mryange commented Oct 26, 2026

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.

No actionable regression was verified in PR #69639 at head 06cc333. The review covered every hunk in the authoritative diff across all 59 changed files, with two complete-scope reviewers and an additional settings/column-shape risk reviewer. All three returned NO_NEW_VALUABLE_FINDINGS; the review converged in one round. No existing inline finding remains to carry forward because both the supplied context and live PR contained no inline comments. There was no additional user-provided focus.

Critical checkpoint conclusions:

  1. Goal and evidence: CAST now binds type/option executors during preparation and executes on source/target columns. Scalar, complex, JSONB, Variant, SQL CAST/TRY_CAST and format-v2 consumers were traced. New tests cover binding reuse, nullable constants, source/conversion NULL merging, empty unsupported casts and execution after original-context closure; existing TRY_CAST and EvolutionCast tests cover conversion errors, clones and overflow. These tests were inspected, not executed.
  2. Scope and clarity: the broad signature migration is consistently focused on removing temporary Blocks and repeated binding. FunctionCast remains the compatibility adapter for direct function callers.
  3. Concurrency and locks: no new threads, locks or mutable shared execution scratch were introduced. Bound closures own immutable options/types and stateless shared kernels; execution uses local output/mask storage. No lock-order or heavy-under-lock change was found.
  4. Lifecycle and initialization: executors retain no FunctionContext pointer. Runtime context clones share query RuntimeState; deep expression clones rebuild unprepared nodes. Closing the original expression context does not invalidate a clone's captures. No new cross-translation-unit static initialization dependency was introduced.
  5. Configuration: no configuration item was added. Existing query strict/decimal flags are read before binding; explicit strict and evolution overrides are applied first. Direct function callers set JSON flags before per-call preparation.
  6. Compatibility: internal C++ signatures and their in-tree callers are migrated together; the FunctionCast adapter remains. No storage, serialization or FE/BE wire-format change occurs. Final ELF/glibc compatibility was not exercised in this static review.
  7. Thrift/protobuf: no gensrc/thrift or gensrc/proto change; field-ID/enum upgrade checks are not applicable.
  8. Parallel paths: ordinary CAST, TRY_CAST retries, the generic function adapter, scalar/complex/JSONB/Variant conversions, format-v2 Cast/EvolutionCast, mapper/TableReader consumers and unchanged Variant explode callers were checked for corresponding behavior.
  9. Special conditions and NULLs: zero rows, constant one-row execution, parent-masked constant expansion, source/conversion NULL-map merging and nested ARRAY/MAP/STRUCT payload masks retain aligned storage and owners. EvolutionCast's DATE-to-TIMESTAMP_NS strict override binds before execution; the separate Paimon truncation path is preserved. Branch reasons and adjacent invariants were checked.
  10. Test coverage: focused unit coverage exercises positive, negative, NULL, empty, nested, overflow and clone paths. The broader existing scalar/JSONB/Variant tests are migrated to column arguments. End-to-end regression execution, external-system interoperability and performance measurements remain outside this static invocation.
  11. Test expectations/results: added expected Boolean, NULL, integer and conversion-error outcomes agree with the inspected kernels. No regression .out file is added or edited. Author-reported test counts were not independently reproduced.
  12. Observability: existing conversion diagnostics and status propagation remain; no new distributed operation or operational control surface requires additional metrics. No runtime profile or benchmark claim is made.
  13. Transactions and persistence: no transaction, visible-version, rowset/delete-bitmap, EditLog, replay or failover implementation is changed; those mechanisms' checkpoints are not applicable.
  14. Writes and atomicity: no write/commit protocol is introduced. Column outputs and masks retain COW ownership; map deduplication detaches shared children. No source-owner loss or new crash-cleanup path was verified.
  15. FE/BE variables: no new transmitted variable or send path is added; constant-folding and direct function calls retain the existing adapter.
  16. Performance and memory: binding is reused and temporary execution Blocks are removed; row-dependent columns/masks stay local and tracked through existing column ownership. No additional actionable allocation, complexity or redundant-work regression was verified. Read-only build-support/check-build-hygiene.sh passed header layering/bans/budgets/PCH, extern-template pairing and unity-skip checks. No measured speedup is claimed.
  17. Other risks and final sweep: settings inheritance, constant/nullable shape, clone lifetime, TRY_CAST error classification, timezone access, aliasing and schema-evolution behavior were rechecked. Variant's default non-strict options do not discard observable JSON/decimal behavior on the traced dispatch paths: JSONB uses its dedicated route, and decimal range checks are independent of the sanitation flag. All initial risks and candidates have evidence-backed dismissals; no unresolved code-level question or changed-file coverage gap remains.

Validation limits: this was a static review. No Doris build, unit test, regression test or runtime benchmark was run; no source file was modified. Convergence describes completed static review coverage and candidate disposition, not runtime validation.

Review converged after 1 round(s).

@Mryange

Mryange commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17572	5188	5135	5135
q2	2119	324	271	271
q3	10213	1414	826	826
q4	4683	462	340	340
q5	7441	806	527	527
q6	179	183	158	158
q7	741	810	599	599
q8	9268	1335	1029	1029
q9	5611	4186	4200	4186
q10	6840	1291	1027	1027
q11	427	254	227	227
q12	633	399	285	285
q13	18083	3684	3062	3062
q14	281	282	257	257
q15	q16	739	719	677	677
q17	1199	740	595	595
q18	7102	6184	6100	6100
q19	1102	971	620	620
q20	384	331	230	230
q21	5354	2124	2313	2124
q22	346	302	255	255
Total cold run time: 100317 ms
Total hot run time: 28530 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5677	5668	5693	5668
q2	689	589	557	557
q3	5231	5412	4891	4891
q4	2061	2199	1590	1590
q5	5281	5148	5824	5148
q6	253	206	155	155
q7	2324	2113	1907	1907
q8	3192	2923	2777	2777
q9	8145	7997	7985	7985
q10	3763	3677	3281	3281
q11	661	484	435	435
q12	667	698	534	534
q13	3637	3835	3280	3280
q14	319	316	282	282
q15	q16	713	718	685	685
q17	8491	7812	7108	7108
q18	12935	12166	12833	12166
q19	953	826	835	826
q20	2155	2154	1929	1929
q21	5946	4876	5011	4876
q22	511	452	423	423
Total cold run time: 73604 ms
Total hot run time: 66503 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 150720 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 06cc3334d9111780d819d0531d2e5585ec44882c, data reload: false

query5	4325	575	448	448
query6	424	207	186	186
query7	4801	450	235	235
query8	324	174	153	153
query9	8782	3123	3141	3123
query10	473	321	257	257
query11	5917	3846	3586	3586
query12	149	88	85	85
query13	1244	468	322	322
query14	6157	3950	3668	3668
query14_1	3401	3446	3461	3446
query15	210	195	193	193
query16	947	474	437	437
query17	862	664	549	549
query18	2412	428	323	323
query19	199	165	160	160
query20	87	86	84	84
query21	210	129	109	109
query22	13172	13045	12854	12854
query23	13601	12808	12577	12577
query23_1	12527	12536	12487	12487
query24	7353	812	460	460
query24_1	479	468	477	468
query25	539	422	357	357
query26	1276	259	138	138
query27	2753	441	275	275
query28	4626	1973	1968	1968
query29	1560	594	453	453
query30	302	214	183	183
query31	1023	932	824	824
query32	138	96	95	95
query33	533	302	245	245
query34	914	859	517	517
query35	748	753	666	666
query36	815	808	747	747
query37	129	100	86	86
query38	1771	1767	1661	1661
query39	801	801	741	741
query39_1	735	749	740	740
query40	223	116	98	98
query41	72	70	67	67
query42	86	88	83	83
query43	346	372	316	316
query44	1333	692	689	689
query45	182	184	167	167
query46	823	969	600	600
query47	2952	2884	2680	2680
query48	286	309	218	218
query49	568	394	287	287
query50	658	265	213	213
query51	10621	10274	10106	10106
query52	77	87	68	68
query53	182	204	148	148
query54	240	190	173	173
query55	68	67	63	63
query56	241	214	202	202
query57	1704	1597	1569	1569
query58	291	266	249	249
query59	2113	2154	1949	1949
query60	302	249	228	228
query61	143	143	146	143
query62	392	336	288	288
query63	190	157	160	157
query64	2743	943	753	753
query65	3339	3258	3253	3253
query66	1813	432	298	298
query67	20214	20134	19924	19924
query68	2909	1021	617	617
query69	372	300	244	244
query70	843	754	756	754
query71	297	227	216	216
query72	2677	2586	2310	2310
query73	530	530	299	299
query74	4625	4490	4346	4346
query75	2329	2307	1941	1941
query76	2298	1017	589	589
query77	347	372	261	261
query78	9744	9696	9172	9172
query79	1001	851	522	522
query80	732	421	372	372
query81	562	317	268	268
query82	572	134	108	108
query83	298	212	178	178
query84	313	115	103	103
query85	853	425	362	362
query86	357	243	228	228
query87	2026	1973	1780	1780
query88	3615	2673	2674	2673
query89	332	284	238	238
query90	1823	167	172	167
query91	164	151	128	128
query92	94	76	88	76
query93	982	976	575	575
query94	503	294	262	262
query95	572	389	305	305
query96	669	490	235	235
query97	2415	2418	2293	2293
query98	166	150	156	150
query99	773	776	661	661
Total cold run time: 234446 ms
Total hot run time: 150720 ms

@hello-stephen

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

query1	0.00	0.00	0.01
query2	0.10	0.05	0.04
query3	0.26	0.09	0.09
query4	1.61	0.10	0.10
query5	0.23	0.25	0.24
query6	1.17	0.68	0.68
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.39	0.36	0.36
query10	0.57	0.57	0.57
query11	0.19	0.15	0.15
query12	0.20	0.15	0.16
query13	0.49	0.50	0.49
query14	1.04	1.00	1.00
query15	0.61	0.60	0.61
query16	0.34	0.34	0.35
query17	1.15	1.15	1.17
query18	0.22	0.19	0.20
query19	2.09	1.98	1.96
query20	0.02	0.02	0.01
query21	15.52	0.18	0.13
query22	4.93	0.05	0.05
query23	16.27	0.34	0.12
query24	3.02	0.51	0.38
query25	0.13	0.04	0.05
query26	0.92	0.20	0.16
query27	0.05	0.05	0.04
query28	3.92	0.80	0.37
query29	12.49	4.18	3.34
query30	0.27	0.13	0.09
query31	2.79	0.56	0.32
query32	3.22	0.60	0.51
query33	3.11	3.28	3.20
query34	15.35	4.01	3.41
query35	3.35	3.32	3.38
query36	0.55	0.45	0.42
query37	0.09	0.06	0.06
query38	0.05	0.04	0.04
query39	0.04	0.02	0.02
query40	0.17	0.16	0.14
query41	0.08	0.03	0.03
query42	0.04	0.03	0.02
query43	0.04	0.03	0.03
Total cold run time: 97.18 s
Total hot run time: 24.41 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 95.57% (367/384) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 77.42% (36110/46640)
Line Coverage 62.62% (407950/651441)
Region Coverage 59.02% (344452/583591)
Branch Coverage 60.00% (159579/265956)

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

Statically reviewed the CAST/TRY_CAST and format-v2 execution paths, including NULL handling and executor lifetimes. No blocking correctness issues found. Current CI checks pass. Adapter simplification and focused before/after CAST benchmarks can be addressed as follow-up improvements.

@yiguolei
yiguolei merged commit b6ff1b3 into apache:master Oct 28, 2026
38 checks passed
Gabriel39 pushed a commit that referenced this pull request Oct 29, 2026
…lete and data file types (#69711)

### What problem does this PR solve?

Issue Number: None

Related PR: #69536, #68267

Problem Summary:

**In short.** FileScannerV2 now compares an Iceberg equality-delete key
in the wider of the delete file's and the data file's own key types,
instead of in the one table type the FE sends for the whole scan. For
some table histories that single type is wrong: the reader then deletes
rows it must keep, keeps rows it must delete, or fails the scan. This
follows the review of #69536, where these cases came up.

**Background.** An equality delete removes a data row whose key values
equal a tombstone's. After a type promotion the two sides can be stored
in different types: tombstones written before an INT -> LONG promotion
hold INT values that must still match LONG data. #68267 made
FileScannerV2 compare in the current schema's type, and for a key that
the current schema no longer has, the FE sends a type recovered from the
retained schemas (#69536). The reader casts both the tombstones and the
data keys to that one type.

**The problem, and what it cost.** One type per scan cannot describe
every delete file:

| History | Table type from the FE | Delete file | Data file | Before |
After |
|---|---|---|---|---|---|
| `expireSnapshots().cleanExpiredMetadata(true)` removed the schema of a
schema-only INT -> LONG promotion of a dropped key | INT | LONG
4294967296 | INT, one NULL key | the tombstone becomes NULL in INT and
deletes the NULL-key row | row kept |
| A read pinned before a later DATE -> TIMESTAMP_NS promotion of the key
| TIMESTAMP_NS | DATE 1000-01-01 | DATE 1000-01-01 | scan fails:
`TIMESTAMP_NS overflow when casting row ...` | row deleted |
| No retained schema gives the key a concrete type, so the FE sends
Iceberg `unknown` as STRING | STRING | TIMESTAMP_NS 2024-01-02 00:00 |
DATE 2024-01-02 | `2024-01-02` != `2024-01-02 00:00:00.000000000`, the
row stays visible | row deleted |
| The delete was prepared under a TIMESTAMP_NS schema that the table
later abandoned for TIMESTAMP | TIMESTAMP | TIMESTAMP_NS
12:00:00.000000500 | TIMESTAMP 12:00:00.000000 | the tombstone is
rounded to microseconds and deletes the row | row kept |

Three of the four return wrong rows without any error. The V1 reader is
not changed by this PR.

**How this PR fixes it.**

- The delete block keeps the delete file's own key types. When the
predicate for a data file is built, each key is compared in the wider of
the delete file's type and the data file's type
(`equality_delete_comparison_type`). The accepted widenings are the
Iceberg promotions (INT -> LONG, FLOAT -> DOUBLE, decimal precision,
DATE -> TIMESTAMP and DATE -> TIMESTAMP_NS), a wider fractional scale of
the same date-time kind, wider integers, STRING -> VARBINARY (an Iceberg
BINARY key decodes as either one, depending on
`enable_mapping_varbinary`), and TIMESTAMP -> TIMESTAMP_NS. The last one
is not an Iceberg promotion; it is accepted for the abandoned-schema
case above, because scaling microseconds to nanoseconds is exact. When
the table field is `timestamptz`, a `DATETIMEV2` key (Parquet INT96,
which carries no instant annotation, as Spark and Hive write it) and a
`TIMESTAMPTZ` key compare as `TIMESTAMPTZ` at the larger scale, the
`DATETIMEV2` side converted with the session time zone as the projection
converts that column. When the table field is the STRING placeholder the
FE sends for an Iceberg TIME, which Doris cannot represent, an ORC TIME
key (a LONG annotated `iceberg.long-type=TIME`, read as `BIGINT`) and a
Parquet one (`TIME(MICROS)`, read as `TIMEV2(6)`) compare as
`TIMEV2(6)`, the `BIGINT` side converted exactly as microseconds from
midnight by a dedicated expression (`CAST(BIGINT AS TIME)` reads the
integer's digits as HHMMSS), and a value outside
00:00:00..23:59:59.999999 fails the scan. Two types that convert in
neither direction fail the scan instead of being compared in a third
type.
- The data keys are cast to that type. When it is the data file's wider
type, the tombstones are widened once per scanner, and the converted
delete block and its hash index are cached next to the original
(`_convert_equality_delete_filter`). A table without type changes is
compared as before, with no cast and no copy.
- A key that the data file lacks is compared through its initial
default, in the wider of the delete file's type and the table type. When
that default is NULL (no initial default, or a struct default `{}` whose
leaf has none) the key is NULL in that file whatever its table type, so
it is compared in the delete file's type (an `unknown` key arrives as
STRING). A non-NULL default behind the STRING placeholder of an Iceberg
TIME is Iceberg's text of the value (`00:00`), with at most six
fractional digits, so it is parsed exactly in the tombstones' type,
`TIMEV2` (`TIMEV2(6)` against an ORC TIME tombstone), by the strict
comparison cast. A non-NULL default of a `timestamptz_ns` key fails the
scan with `NotSupported`: V2 reads its tombstones as `TIMESTAMPTZ(6)`
(Parquet floors the nanoseconds, ORC rounds them), so no parse of the
nanosecond default compares exactly; the default epoch would match a
tombstone 100 ns later and delete every row of the file.
- `AncestorNullDefaultExpr`, which gives a missing child the NULL of its
NULL parent and its default otherwise, now opens its children. Its
`open()` returned before opening them, because `prepare()` had already
set the flag its guard checks, so a default cast to the comparison type
below it was never opened and aborted the BE with `Check failed:
_open_finished` in `Cast`.
- The comparison casts are strict whatever the session's cast mode,
through a new `strict` flag on `EvolutionCast`: a non-strict overflow
would turn a key into NULL and let a NULL tombstone delete it. DATE ->
TIMESTAMP_NS was already strict; other `EvolutionCast` users are
unchanged.
- A delete file without rows is skipped before its keys are resolved: it
deletes nothing, but the predicate evaluates its keys first, so its
strict casts could fail the scan (a DATE key 1000-01-01 against an empty
TIMESTAMP_NS delete file).
- The table types from the FE still resolve table fields and the
defaults of missing keys; the delete-file cache key is unchanged.

**Results.**

- 14 new BE unit tests in `iceberg_reader_test.cpp`: the four histories
above; INT tombstones against LONG, INT and LONG data files of one scan
(the third split reuses the cached widened copy); a missing key's INT
default against a LONG tombstone; a missing key without a default under
an `unknown` table type; a microsecond key outside the nanosecond range,
which must fail; and incomparable key types. With the reader before this
PR, 6 of them fail; the other three pin behavior the old code shares.
Removing the TIMESTAMP -> TIMESTAMP_NS widening fails 2, and making the
casts non-strict fails 1. Three more came from review: an INT96 data key
against an adjusted-UTC tombstone in Asia/Shanghai, a NULL leaf under a
struct default `{}`, and an empty TIMESTAMP_NS delete file against a
DATE key; removing the INT96 rule, the NULL-leaf walk or the empty-file
skip fails its test. Two more came from the third round: Iceberg TIME
keys across all four ORC / Parquet data and delete file pairs, with NULL
and non-NULL tombstones, and a nested Iceberg TIME key added with a text
default, against Parquet TIME and ORC TIME tombstones, under NULL and
non-NULL parents. Removing the TIME pair rule or the default parsing, or
converting the ORC TIME with a SQL cast, fails them; leaving
`AncestorNullDefaultExpr`'s children unopened aborts. One more came from
the fourth round: a nested `timestamptz_ns` key with the defaults
`...0000001`, the epoch and `...0000005` fails against an epoch, a 100
ns and a 500 ns tombstone, while a key without a default still matches
NULL tombstones only; parsing those defaults again fails it.
- End to end on a local cluster: a v2 table in a `file://` Hadoop
catalog, built with the Iceberg 1.12.0 Java API: `k INT` with rows (1,
NULL), (2, 0), (3, 1); `k` promoted to LONG without a snapshot; a delete
of `k = 4294967296` written under that schema; `k` dropped; the delete
committed; then
`expireSnapshots().retainLast(2).cleanExpiredMetadata(true)`, which
leaves schema 0 (`k INT`) and schema 2 (no `k`). `SELECT id` through
FileScannerV2 returned 2, 3 before this PR and returns 1, 2, 3 with it.

branch-4.1 and branch-4.2 have the same FileScannerV2 code.

### Release note

Fix FileScannerV2 comparing Iceberg equality-delete keys in a single
scan-wide type, which could delete or keep the wrong rows, or fail the
scan, after a key's type changed (for example a schema-only promotion
removed by `cleanExpiredMetadata`, or a key whose retained schemas only
have it as `unknown`).

### Check List (For Author)

- Test <!-- At least one of them must be included. -->
    - [ ] Regression test
    - [x] Unit Test
- `run-be-ut.sh --run
--filter='IcebergV2ReaderTest.*:*EqualityDelete*:*ColumnMapper*:*EvolutionCast*:*TableReader*'`:
526 of 526 pass on macOS arm64 (ASAN), including the 14 new cases.
- Rebased onto master `43f9ceb48d7`: #69639 moved the strict-mode
override of `EvolutionCast` from `prepare()` into
`configure_cast_options()`, so the new `strict` flag now sets
`CastOptions::strict_mode` there, next to the existing DATE ->
TIMESTAMP_NS rule. The run above is on the content before the rebase
(the third-round commit applies to both unchanged); the 67 TUs that this
PR changes or that master changed since `5caaeb3e3d4` were
syntax-checked against the rebased tree with the UT build flags, and CI
runs the suites again.
    - [x] Manual test (add detailed scripts or steps below)
- The local end-to-end check described under Results, with an ASAN BE
built from this change.
    - [ ] No need to test or manual test. Explain why:
- Behavior changed:
    - [ ] No.
- [x] Yes. FileScannerV2 compares equality-delete keys per delete file
and data file; two key types that convert in neither direction now fail
the scan; the comparison casts are strict.
- Does this need documentation?
    - [x] No.
- [ ] Yes. <!-- Add document PR link here. eg:
apache/doris-website#1214 -->

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

- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label <!-- Add branch pick label that this PR
should merge into -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

4 participants