Skip to content

[feature](lance) Support approx_mode in vector_search - #69852

Merged
yiguolei merged 3 commits into
apache:branch-4.2from
Gabriel39:dev/lance-approx-mode-4.2
Nov 2, 2026
Merged

yiguolei merged 3 commits into
apache:branch-4.2from
Gabriel39:dev/lance-approx-mode-4.2

Conversation

@Gabriel39

@Gabriel39 Gabriel39 commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

vector_search cannot currently select Lance's approximation policy. This PR adds "approx_mode"="fast|normal|accurate", validates the value before metadata access, transports it as an optional typed Thrift field, and forwards it to lance_scanner_set_approx_mode on each BE scanner. EXPLAIN shows an explicitly requested mode.

Omitting the property preserves the existing native default (normal in the pinned Lance revision). The setting applies through the shared single-vector and multi-vector scanner setup. Existing refinement, probe counts, search width, concurrency, split coverage, and distributed merging remain unchanged. The existing lance-c dependency already provides this API, so no third-party update or local patch is needed.

Website documentation is provided in apache/doris-website, updating the English and Chinese 4.x Lance Catalog pages with mode semantics, selection guidance, ACORN activation and fallback rules, SQL usage, and compatibility notes. The temporary guide in the Doris repository has been removed. The local experiment remains recorded below. In particular, accurate still selects approximate candidates and does not guarantee exact Top-K or original-vector distances on a quantized index. During a smooth upgrade, FE rejects explicit fast or accurate at scan initialization if any backend in the query's candidate pool is marked as an old smooth-upgrade source. Older BEs would ignore the optional field and silently use NORMAL, so rejecting prevents a global TopN from merging split candidates generated under different policies. The error names the backend and requested mode and advises completing the upgrade or using normal. Omission and explicit normal remain compatible. This checks only the selected backend policy, not unrelated backends, and does not detect arbitrary older builds without the smooth-upgrade-source marker.

Mode semantics and recommended selection

Use normal as the baseline; choose another mode only after measuring representative queries against an exact reference. These are recommendations based on the pinned implementation and the local experiment, not fixed speed/recall guarantees.

Search path fast normal accurate
RabitQ raw-query distance estimation Uses one-bit scores even for a multi-bit index, skipping extension-bit computation and its lower-bound gate. Uses the standard 8-bit quantized distance lookup table and extension-bit computation when available. Uses a higher-precision 16-bit distance lookup table and extension-bit computation when available.
HNSW with prefiltering Enables ACORN traversal when the runtime conditions below permit it. Uses regular filtered graph traversal. Uses the same traversal policy as normal; quantized distance computation can still differ.
IVF_FLAT or exhaustive Flat Search No mode-specific quantization change. Same scoring policy. Same scoring policy.

The 8-bit and 16-bit values describe the query-time distance lookup table, not the number of bits stored per dimension in the index. For example, on a 5-bit RabitQ index, fast uses one-bit scores on the raw-query path; the other modes can use the additional four bits. Other RabitQ paths, including residual-query scoring, can behave differently. A mode description does not expand the supported vector-type or metric combinations.

accurate is still approximate search. It does not guarantee exact Top-K, exhaustive candidate evaluation, or original-vector _distance on a quantized index. refine_factor controls reranking with original vectors; increasing it can improve scores and recall but cannot recover every candidate missed earlier. Multi-vector queries retain their mandatory original-vector refinement.

Workload Starting choice What to verify
General workloads or an unknown bottleneck normal Establish recall and latency before changing the policy.
HNSW_FLAT with an effective prefilter, where latency matters Compare fast with normal ACORN can reduce wasted distance calculations. Check recall, result count, median latency, and p95; a speedup is not guaranteed.
Multi-bit RabitQ where recall matters normal fast can discard useful encoding information and substantially reduce recall. Use it only if representative queries still meet the quality target.
RabitQ without original-vector reranking, where approximate score error matters Compare accurate with normal Measure score error and ranking quality against original-vector distances, as well as extra latency. Better estimation does not guarantee higher recall.
RabitQ already using original-vector reranking normal The final score benefit of accurate may be smaller; candidate selection can still differ, so validate recall separately.
IVF_FLAT or HNSW_FLAT without an effective prefilter Keep normal The current implementation offers no corresponding quantization or ACORN benefit from changing the mode. IVF_FLAT recall still depends on the probed partitions.

For a compatible HNSW index backed by RabitQ, fast can both enable ACORN traversal and simplify RabitQ distance estimation on the applicable raw-query path. These effects are coupled by one option. Do not infer its recall from an HNSW_FLAT test; validate the combined index independently. normal and accurate have the same HNSW traversal policy, but their RabitQ scoring can differ.

Recommended tuning order:

  1. Pin the same dataset snapshot, query set, metric, filters, and index coverage. Use use_index=false to obtain exact Top-K ground truth over the same eligible rows; put eligibility conditions in the TVF filter with prefilter=true.
  2. Establish a normal baseline. Measure Recall@K, result count, score error where relevant, and median/p95 latency.
  3. If recall is insufficient, tune IVF nprobes and HNSW ef. If original-vector scores or more reranked candidates are required, configure refine_factor, accounting for the extra vector reads and computation.
  4. With the other settings fixed, compare fast for filtered HNSW or accurate for RabitQ estimation precision. Select a mode only if it meets the quality target and provides a worthwhile measured benefit. Measure cold and warm cache behavior separately.

ACORN activation and fallback

The selected compatible index must contain an HNSW graph. Request approx_mode="fast" and a TVF filter with prefilter="true"; an outer Doris WHERE does not supply this prefilter. use_index=true alone does not select or force HNSW. The website documentation includes a complete SQL example.

The current native implementation dispatches separately inside each HNSW subindex (an IVF partition), using the number of nodes passing the prefilter:

Runtime condition Path
No effective prefilter, or all nodes pass Ordinary HNSW traversal.
Fewer than roughly 10% of nodes pass Flat scoring over the remaining nodes.
A partial filter remains and the sparse-filter rule does not apply fast uses ACORN; normal and accurate use regular filtered graph traversal.
ACORN returns too few required candidates and the query has no distance bounds Retry with regular filtered graph traversal.

The threshold applies within each subindex, not to the whole table. There is no separate force-ACORN property in this interface. In Doris, distance-range safety rules can also select Flat Search before native graph dispatch.

EXPLAIN showing lanceApproxMode=FAST confirms the requested policy, not that every subindex actually ran ACORN. HNSW_RabitQ behavior is inferred from the coupled traversal/scoring implementation; the local HNSW measurements below used HNSW_FLAT and do not validate the combined index.

Local validation of behavior and effect

These are native lance-c scanner measurements, not end-to-end Doris SQL timings. The library was freshly built from pinned lance-c a51ab47a9c814a746cb1714c99fad93dd3a188b8, using Lance 68c12dfd7efe02f90ad2d7f3a239a7eb64884e57. The unoptimized development build and Python/Arrow result conversion make the timings illustrative, not production performance claims.

Procedure:

  1. Generate 16,384 Gaussian Float32 vectors of dimension 128, then 128 independent query vectors, using xorshift64/Box-Muller with seed 20261029. Assign row_id=0..16383 and category=row_id%10.
  2. Build fully covering IVF_RQ (32 partitions, 5 bits), IVF_FLAT (32 partitions), and IVF_HNSW_FLAT (32 partitions, 16 edges, construction ef=100) indexes. Select each index explicitly by its segment UUIDs.
  3. Search squared L2 with k=10, nprobes=32, and query_parallelism=1. Repeat IVF_RQ with refine_factor=4. For HNSW, set ef=64 and prefilter category < 5 (about 50% of rows).
  4. Compute exact Top-10 independently with NumPy. Warm up each mode with eight queries; run three rounds of 128 queries per mode, rotating mode order per query and round. The table below reports mean Recall@10, median latency, and p95 latency, from 384 searches per mode.
  5. Compare omitted mode with explicit normal over all 128 queries per case, checking ordered IDs and Float32 distances. Independently recompute original-vector distances for returned IVF_RQ rows to measure score error.
Index / refinement Mode Recall@10 Median (ms) p95 (ms)
IVF_RQ / none fast 26.80% 14.57 16.03
IVF_RQ / none normal 91.72% 15.90 18.12
IVF_RQ / none accurate 91.72% 20.52 22.91
IVF_RQ / 4 fast 54.30% 20.50 22.07
IVF_RQ / 4 normal 99.92% 23.44 25.64
IVF_RQ / 4 accurate 99.92% 28.19 30.58
IVF_FLAT / none fast 100% 25.71 29.62
IVF_FLAT / none normal 100% 25.78 29.82
IVF_FLAT / none accurate 100% 25.71 30.74
IVF_HNSW_FLAT / 50% prefilter fast 100% 40.45 44.04
IVF_HNSW_FLAT / 50% prefilter normal 100% 44.51 49.86
IVF_HNSW_FLAT / 50% prefilter accurate 100% 44.48 50.05

All searches returned ten rows. Omitted mode matched explicit normal exactly in every case. IVF_FLAT and filtered HNSW returned identical ordered IDs and distances across modes. The data seed fixes vectors; ANN index training/layout can vary across rebuilds.

Without refinement, normal and accurate had equal mean recall but different ordered IDs on 89/128 queries. Mean absolute squared-L2 score error was 30.7325 for fast, 0.7986 for normal, and 0.7783 for accurate. Thus accurate slightly improved distance estimation on this fixture without improving mean recall. With refinement factor 4, normal and accurate returned identical results, while fast still missed many neighbors. The mode is not interchangeable with refinement, and its effect depends on the index/path and data.

Release note

Add the optional approx_mode property to Lance vector_search, accepting fast, normal, and accurate, with the existing default preserved when omitted.

Check List (For Author)

  • Test

    • Unit tests: focused source compilation and JUnit execution of VectorSearchTableValuedFunctionTest and LanceScanNodeTest: 59 tests passed. Coverage includes accepted/invalid modes, typed Thrift round trips, default omission, EXPLAIN, and smooth-upgrade scan initialization.
    • Review-fix regression: the new real scan-initialization test failed on the previous code (the expected upgrade rejection was missing), then passed with the gate. Coverage includes FAST/ACCURATE with old-only and mixed candidate pools, single/multi-vector requests, all modes on current BEs, omitted/explicit NORMAL on mixed BEs, and unaffected FTS initialization.
    • Manual test: native mode comparison described above, plus independent distance-error checks.
    • FE mvn -B clean checkstyle:check passed; clang-format 16 checks passed for both affected C++ files; generated Thrift sources rebuilt successfully.
    • Fresh BE reader and test source objects compiled successfully against the new headers and pinned C API.
    • Full BE unit-test execution: not run locally because the available prebuilt BE libraries have an incompatible ABI. New request-validation coverage is included for CI.
    • SQL regression execution: added mode pass-through, EXPLAIN, and invalid-value coverage to test_lance_vector_search; the full suite parsed successfully with Groovy 4.0.19, but execution is pending CI.
  • Behavior changed:

    • Yes. Explicit approx_mode now selects the native policy; omission preserves existing behavior.
  • Does this need documentation?

    • Yes. English and Chinese 4.x documentation: website PR.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

@Gabriel39
Gabriel39 requested a review from yiguolei as a code owner October 29, 2026 14:57
@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?

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

/review

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

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.

Reviewed all eight changed files at head 0301f4c. No actionable defect was found. There are no existing inline findings to carry forward. The review converged in one round: the FE, BE/test, and dedicated native-policy reviewers each returned NO_NEW_VALUABLE_FINDINGS, and main verification resolved every considered point.

Critical checkpoint conclusions:

  1. Goal and evidence: named fast/normal/accurate values are validated before metadata access, transported as a typed optional field, shown in EXPLAIN, and applied to each native vector scanner. Inspected tests cover parsing, omission, serialization, validation and SQL pass-through.
  2. Scope: the production changes are focused on option parsing, protocol, EXPLAIN and native configuration; the remaining changes are tests.
  3. Concurrency: the change adds no threads, shared mutable state or locks. Request copies and per-scanner setup preserve existing concurrency and lock ordering.
  4. Lifecycle: fresh scanners are created per split; RAII retains cleanup on setter failures, with existing split/EOF/cancellation/close handling. No ownership cycle or cross-translation-unit static initialization is introduced.
  5. Configuration: this is a per-query property, with no new process/session configuration or dynamic-update requirement.
  6. Compatibility: omitted mode preserves the pinned native Normal default. The additive optional field leaves old requests compatible; older BEs ignore it, so all participating BEs must support it to honor an explicit policy consistently. No storage-format incompatibility is introduced.
  7. Thrift/proto: the complete touched definitions were checked against live branch-4.2, branch-4.1, branch-4.0 and branch-3.1 snapshots. No field/enum reuse, numeric collision or new required field was found; no protobuf changes.
  8. Parallel paths: full request copies and both FE transport paths preserve the option. Single/multi-vector and indexed/uncovered/forced-flat splits share configuration; ordinary scans and FTS correctly remain outside it. No separate cloud path needs additional propagation.
  9. Conditions and errors: FE accepts trimmed case-insensitive names and rejects unsupported values; BE rejects unknown enums and propagates native setter errors. Presence guards preserve omission semantics. Existing null/column mapping contracts remain intact.
  10. Coverage: inspected FE tests exercise accepted/invalid values, default omission, Thrift round trips and EXPLAIN; BE tests exercise validation before opening a dataset; regression cases check pass-through, deterministic output and expected errors.
  11. Test results: no golden result files changed. New assertions are consistent with the implementation by inspection; no tests were executed during this review.
  12. Observability: EXPLAIN identifies the requested policy; setter failures identify approx_mode, and existing scanner timers/statistics remain on the same paths. EXPLAIN does not prove ACORN activation.
  13. Transactions/persistence: no edit-log, transaction, snapshot-selection, visible-version or delete-bitmap changes; the fixed-snapshot read boundary is preserved.
  14. Writes/crash safety: no data writes, persistent state or new recovery obligations; scanner error cleanup remains intact.
  15. FE-BE propagation: deep copies retain the entire external request in shared TLanceScanParams through planning and transport; split Top-K/offset adjustment does not alter the mode.
  16. Performance/memory: the new conversion/setup is constant-size work per query/scanner, outside row/batch loops, with no new large allocation or I/O. The pinned lance-c setter/build ordering and native Query clones preserve the mode through actual RabitQ/HNSW consumers; exact flat search intentionally ignores index approximation policy.
  17. Other issues: the final sweep found no additional correctness, resource-lifetime or error-propagation regression in the changed paths. No unresolved candidate or uncovered source-review scope remains.

User focus: no additional focus points were supplied.

Validation limits: this was a static review; no builds, tests or benchmarks ran, and no submodules were initialized. The new BE test stops at initialization, and the refined IVF_PQ SQL fixture is intentionally mode-insensitive. Committed coverage therefore does not discriminate mode-dependent RabitQ/HNSW behavior or explicit-mode multi-vector execution. A mode-sensitive fixture would strengthen regression protection. The PR's reported runtime measurements were not independently reproduced; this review makes no latency/recall guarantee.

Review converged after 1 round(s).

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

Reviewed PR 69852 at the exact requested head. Static review converged after two bounded rounds. One blocking mixed-version semantic issue remains: explicit FAST/ACCURATE approx_mode values are accepted and serialized, but older smooth-upgrade BEs skip optional Thrift field 6 and keep Lance's NORMAL default while new BEs apply the requested mode. LanceScanNode only gates smooth-upgrade BEs for newer Lance type readers, so ordinary vector-search splits can reach old and new BEs before global TopN merges candidates from different approximation policies. Reject explicit non-default modes while any selected BE is a smooth-upgrade source, or add a capability gate that keeps all splits on mode-aware BEs and reports the effective mode. I independently verified FE parsing, deferred/prepared and split deep-copy propagation, Thrift field and enum compatibility, BE validation and native mapping, setter status/lifecycle, and indexed/flat/single/multi-vector paths. There are no existing inline findings and no additional user focus. Under the static-only review contract, builds and tests were not executed; the added IVF_PQ regression is a pass-through coverage control because that index may ignore ApproxMode, which is a non-actionable test gap.

Review converged after 2 round(s).

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 92.86% (13/14) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 79.31% (23/29) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 64.57% (30151/46693)
Line Coverage 49.42% (318612/644647)
Region Coverage 45.24% (256870/567792)
Branch Coverage 46.87% (121159/258519)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 79.31% (23/29) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.23% (34567/45347)
Line Coverage 61.08% (390436/639264)
Region Coverage 57.82% (328832/568681)
Branch Coverage 58.84% (151945/258225)

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

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.

Static review of PR 69852 at exact head c6d4647 found no new verified defect. The new approx_mode path is parsed case-insensitively with invalid values rejected before metadata access, transported as optional Thrift field 6, preserved through split deep copies, gated for explicit FAST/ACCURATE during smooth upgrades, and mapped to the pinned Lance-C enum before scanner execution. Indexed, uncovered-fragment, flat, single-vector, multi-vector, ordinary-scan, and full-text paths were traced; no additional issue was found. Existing inline P1 4416183888 was independently revalidated as fixed by the current LanceScanNode backend gate and is not reposted. No user review focus was supplied.

Tests and checks were inspected only under the static-review contract; no builds, tests, benchmarks, source edits, or submodule initialization were performed. The regression fixture intentionally uses a mode-insensitive refined IVF_PQ control, so it verifies propagation and validation rather than mode-dependent recall.

The main agent completed the full changed-file and upstream/downstream sweep. Convergence is false only because all assigned subagent launches and retries terminated with the platform model-capacity error before returning NO_NEW_VALUABLE_FINDINGS; no unresolved code candidate remains, but the strict independent-subagent convergence condition could not be satisfied within the three-round bound.

Review did not converge after 3 rounds. This automated review cannot pass the review check.

Assigned review subagents could not run: every launch and retry failed with the platform model-capacity error, so the required independent NO_NEW_VALUABLE_FINDINGS responses are unavailable. The main-agent full static sweep completed with no unresolved code candidate; delivery is non-converged at the round-3 cap.

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 37.29% (22/59) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 79.31% (23/29) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 64.59% (30174/46715)
Line Coverage 49.45% (318925/644942)
Region Coverage 45.23% (256882/567980)
Branch Coverage 46.88% (121253/258645)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 79.31% (23/29) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.20% (34570/45368)
Line Coverage 61.05% (390462/639558)
Region Coverage 57.84% (329004/568866)
Branch Coverage 58.85% (152031/258351)

morningman pushed a commit to apache/doris-website that referenced this pull request Oct 31, 2026
## What is changed

Document the `approx_mode` property added by
apache/doris#69852 in the English and Chinese
4.x Lance Catalog pages. This is the companion website documentation for
that feature; merge it together with or after the implementation.

The parameter reference and new guidance cover:

- Accepted values and default omission; index-specific differences
between `fast`, `normal`, and `accurate`.
- A workload selection table and tuning order using exact ground truth,
recall, latency, and original-vector refinement.
- ACORN prerequisites, a SQL example, per-HNSW-subindex filtering
thresholds, fallback behavior, and the limits of EXPLAIN.
- The coupled ACORN and RabitQ scoring effects on compatible HNSW/RabitQ
indexes; HNSW_FLAT measurements do not establish the combined index's
recall.
- Lookup-table precision versus stored encoding bits, approximate versus
exact results, and FE/BE upgrade compatibility.

The compatibility note now also describes FE rejection of explicit
FAST/ACCURATE when the initialized query backend policy contains a
marked old smooth-upgrade source, the omitted/NORMAL exception, the
error's recovery action, and the limit for unmarked older builds.

The detailed native validation measurements are recorded in the
implementation PR. The temporary guide in the Doris source repository is
being removed in favor of this website documentation.

## Validation and scope

- Both changed pages compiled successfully with the Docusaurus MDX
processor.
- Ran the documentation governance checks and compared them with the
same checks on the unchanged base content. No new warnings or errors;
the only new informational entries report the public implementation-PR
links.
- Reviewed the complete diff and Chinese/English parity. Existing URLs,
heading anchors, navigation entries, front matter, and unrelated content
are preserved. No new content issues found.
- The existing sidebar/SEO warnings are unchanged. The version-sync
warning is intentionally explained here: the implementation targets
**branch-4.2**, so only the **4.x** pages are updated. The option has
not been added to the Doris master/development implementation; copying
the guidance to development-version docs would claim support before that
implementation exists. Other locales retain their existing content.

## Versions

- [ ] dev
- [x] 4.x
- [ ] 3.x
- [ ] 2.1 or older

## Languages

- [x] Chinese
- [x] English

## Docs Checklist

- [x] Checked by AI: full self-review completed, including content
correctness, locale/version scope, links, anchors, metadata, and example
consistency.
- [x] Test Cases Built: both changed MDX pages compiled; governance
delta checked against the base; mode behavior was exercised in the
companion implementation's native validation.
- [x] Updated required version and language counterparts, or explained
why not.
- [x] If only one language changed, confirmed whether source/translation
counterparts need sync: both English and Chinese are updated.
@yiguolei
yiguolei merged commit 7872ff4 into apache:branch-4.2 Nov 2, 2026
36 of 39 checks passed
morningman added a commit to morningman/doris that referenced this pull request Nov 5, 2026
### What problem does this PR solve?

Issue Number: None

Related PR: apache#65730 apache#66608 apache#66805 apache#66841 apache#66999 apache#67005 apache#67011 apache#67110 apache#67268 apache#67289 apache#67325 apache#67650 apache#67921 apache#68028 apache#68335 apache#68375 apache#68615 apache#68690

Problem Summary:

Port the Lance BE read path of branch-4.1/4.2 (as of branch-4.2
cabcf45) to master. Master links liblance_c but has no Lance reader:
file_scan_operator keeps FORMAT_LANCE off FileScannerV2 and V1 has no
Lance path, so every FORMAT_LANCE scan fails today. This change is BE
only. The FE connector plugin and the regression suites follow
separately, so nothing on master plans a Lance scan yet and the BE unit
tests carry the proof.

- Reader: be/src/format_v2/lance/* and format_v2/table/lance_reader.*
  (plain scans, vector and full-text search, Substrait and runtime
  filter pushdown, scalar index segments, COUNT(*) from metadata, LIMIT,
  profiles), copied from branch-4.2 except one local renamed for
  -Wshadow.
- Hooks: FileFormat::LANCE and its exhaustive switches, FileScannerV2
  dispatch, and fetch_table_schema for FORMAT_LANCE. Lance queries use
  FileScannerV2 whatever enable_file_scanner_v2 says, as ADBC and fluss
  do: there is no V1 Lance reader. The second phase of TopN lazy
  materialization reads Lance rows through dataset take_rows.
- Row locations: GlobalRowLoacationV2 gets a version 1 layout with a
  64-bit Lance row id (the version 0 layout is unchanged),
  PRequestBlockDesc.row_id becomes uint64 (varint, wire compatible), and
  the materialization operator and RowIdStorageReader decode and
  validate by version.
- Thrift: the external search request body, TLanceFileDesc 4-6 and
  TExternalSearchRequest 2-4 with the branch-4.1/4.2 ids, in place of the
  comments that reserve them on master (apache#70075); field 6 stays master's
  paimon_options.
  TVectorSearchOptions 6 is branch-4.2's approx_mode (apache#69852), reserved
  in a comment.
- Core: PTypeDesc keeps NULL_TYPE for a NULL literal type, and the
  nullable serde reads an Arrow Null array as NULLs.
- Nine lance_* BE configs, a BE-wide Lance session with an optional
  Foyer data cache and 10 lance_session_* metrics; macOS links IOKit.
- Tests: the Lance BE unit tests and their fixtures, including the
  docker fixture all_types.lance that one of them reads. Under master's
  -ffile-prefix-map the session test locates its fixture from the
  repository root, and the reader tests shrink the data cache to 64 MiB
  under the UT temp dir: the default 100 GiB disk tier is 51200 block
  files that stay open for the rest of the test process.

Not included: the MaterializationOperator::push timer fix (apache#69579), the
Lance index metadata scanner, the FE side, regression suites, and the
4.x changes after cabcf45 (apache#68707, apache#68813, apache#68815).

### Release note

Add nine lance_* BE configs and lance_session_* BE metrics. They take
effect once the FE can plan Lance scans.

### Check List (For Author)

- Test: Unit Test
    - BE UT: the Lance reader, nested Null, runtime filter and session
      suites, plus the row-location protocol and FileScannerV2 cases
- Behavior changed: Yes. PRequestBlockDesc.row_id is uint64, and the
  second phase of TopN lazy materialization rejects row locations of an
  unknown version or size.
- Does this need documentation: No (documented with the FE connector)

Co-authored-by: zhangstar333 <[email protected]>
Co-authored-by: Gabriel <[email protected]>
Co-authored-by: zhaobo wang <[email protected]>
Co-authored-by: jay <[email protected]>
Co-authored-by: FANNG <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
yiguolei pushed a commit that referenced this pull request Nov 6, 2026
### What problem does this PR solve?

Issue Number: None

Related PR: #65730 #66608 #66805 #66841 #66999 #67005 #67011 #67110
#67268 #67289 #67325 #67650 #67921 #68028 #68335 #68375 #68615 #68690
#68813 #68815 #68707 #68649

Problem Summary:

**In short.** This ports the Lance BE read path of branch-4.1 /
branch-4.2 to master, in its branch-4.2 form as of `cabcf45c1af`, plus
the BE side of three 4.x changes merged after that snapshot: the search
filter `prefilter` flag and profile layout (#68813), distance bounds for
`vector_search` (#68815), and the split-level index rule of #68707 that
#68815 relies on. It gathers the BE parts of the PRs listed above.
Master links `liblance_c` but has no Lance reader, so every
`FORMAT_LANCE` scan fails today. With this PR the BE reads Lance
datasets through FileScannerV2: plain scans, vector search and full-text
search, predicate and runtime-filter pushdown, the schema fetch of a
local Lance TVF, and the second phase of TopN lazy materialization. The
PR is BE only. The FE connector plugin and the regression suites come in
separate PRs, so until then nothing on master plans a Lance scan, and
the BE unit tests carry the proof.

Original authors: @zhangstar333 @Gabriel39 @wangzhaobo957-cloud @Jay-ju
@FANNG1

**Background.** Lance shipped in 4.1.4: the Lance catalog, `local()` /
`s3()` with `format=lance`, and the `vector_search` / `full_text_search`
TVFs. On master the FE side is being rebuilt as a connector plugin
(`fe-connector-lance`), and the BE needs the reader from 4.x. The thrift
ids are the branch-4.1 / 4.2 ones, including `TExternalSearchRequest.5`
(`prefilter`) and `TVectorSearchParams.6/7` (distance bounds) added
after the snapshot, so a BE built from master also reads the Lance scan
ranges and search requests that a 4.x FE sends during a rolling upgrade.
The one exception is `TVectorSearchOptions` 6, branch-4.2's
`approx_mode` (#69852, merged into branch-4.2 after the snapshot and not
ported): it is reserved in a comment, and a master BE ignores it, so
during a rolling upgrade a 4.2 FE that asks for `fast` or `accurate`
gets Lance's default (`normal`).

**The problem, and what it cost.**

| | master today | with this PR |
|---|---|---|
| A `FORMAT_LANCE` scan | FileScannerV2 has no Lance reader, so the scan
fails with "FileScannerV2 does not support file format" | read by
`LanceTableReader` in FileScannerV2 |
| `fetch_table_schema` for a local Lance TVF | not handled | returns the
Arrow schema mapped to Doris types; Null leaves keep `NULL_TYPE` |
| The row location of a Lance row in TopN lazy materialization |
`GlobalRowLoacationV2` only has a 32-bit row id, but a Lance row id is a
64-bit id within the snapshot | a version 1 layout carries the 64-bit
row id; phase two reads the rows with the dataset's `take_rows` |

**How this PR fixes it.**

- Reader: `be/src/format_v2/lance/*` and
`be/src/format_v2/table/lance_reader.*`, as on branch-4.2 except one
local renamed for master's `-Wshadow`. The unit tests construct
`RuntimeState(TQueryOptions(), globals)`, because master removed the
globals-only constructor.
- FileScannerV2: the reader dispatch and `_to_file_format` handle
`FORMAT_LANCE` / `"lance"`; `FileFormat::LANCE` is added to its two
exhaustive switches. Since #69723 every file scan uses FileScannerV2, so
nothing else routes Lance scans (on branch-4.2 a Lance query fails with
NotSupported when `enable_file_scanner_v2` is off).
- Row locations: `ROW_VERSION {FILE_LOCAL_ROW_ID = 0,
LANCE_DATASET_ROW_ID = 1}` and a union layout for
`GlobalRowLoacationV2`. Version 0 keeps its byte layout, which static
asserts pin. `PRequestBlockDesc.row_id` becomes `uint64` (a proto2
varint, so the wire format is compatible). The materialization operator
decodes row locations by version, rejects an unknown version or a wrong
size, and stamps `row_location_version`. `RowIdStorageReader` validates
the counts and the version, range-checks internal row ids against
`uint32`, and groups Lance rows by plan node, snapshot version and
dataset URI, so each dataset is read with one `take_rows`. Master's
segment batching, scan-slot dedup and fetch concurrency stay as they
are.
- Thrift (`PlanNodes.thrift`): the external search request body
(`TVectorElementType`, `TVectorMetric`, `TSearchVector`,
`TVectorSearchParams` 1-7, `TFtsCoverageMode`, `TFtsQueryType`,
`TFtsMatchOperator`, `TFullTextSearchParams`, `TSearchFilterFormat`,
`TSearchFilter`, `TVectorSearchOptions`, `TExternalSearchQuery`),
`TExternalSearchRequest` 2-5 and `TLanceFileDesc` 4-6, all with the
branch-4.1 / 4.2 ids. The enums spell out their 4.x values. No id is
reused. On master, #70075 reserved `TExternalSearchRequest` 2-5 in
comments and added 6 (`paimon_options`); this PR replaces those comments
with the fields and leaves 6 as it is. `TVectorSearchOptions` 6 is
reserved in a comment for branch-4.2's `approx_mode` (#69852).
- After the snapshot (one commit each):
- #68813: `prefilter` decides whether the TVF search filter runs before
(true) or after (unset / false) candidate selection, for vector and
full-text search. The profile groups the Lance counters under
`LanceTiming`, `LanceIO`, `LanceIndex`, `LanceVectorIndex`,
`LanceScalarIndex`, `LanceExecutionPlan`, `LanceScan`, `LanceSearchPlan`
and `LanceDataCache`, uses the 4.x counter names, and registers every
scan metric before the statistics callback is installed, so the
callback, which Lance invokes from its execution streams, never adds
counters. #68813's FE tweak that hides `TUnit.NONE` labels in
`AggCounter` belongs to the FE port.
- #68707 (part): a vector split searches exactly the index segments FE
planned for it, and a split without segments is searched flat whatever
`use_index` says; a split with segments under `use_index = false` is
rejected. 4.x FEs plan segments only when `use_index` allows them.
- #68815: `distance_lower_bound` (inclusive) and `distance_upper_bound`
(exclusive) are validated (finite FLOAT values, lower < upper at FLOAT
precision, single-vector only) and installed with
`lance_scanner_set_distance_range()`, which master's pinned lance-c
provides.
- Review fixes:
- A nested array goes to the Doris SerDes without a copy only when it
and every descendant start at offset 0 and reference exactly their child
ranges. Lance's legacy (v1) decoder can return a struct or list that is
tight at its own level over a Boolean child with a bit offset (a batch
that starts mid-byte in the bitmap), and the SerDes reject that under
Arrow input validation.
- A zero-field `STRUCT<>`, which Lance decodes, is reported as an
unsupported column at any nesting level, like the other unsupported
Lance types. It used to be typed as a STRUCT that `ColumnStruct` cannot
hold (its row count comes from its first child).
- A Lance Decimal256 field with precision <= 38 is narrowed to
Decimal128 by a safe Arrow cast before decoding. Doris types it
DECIMAL32/64/128I by precision, and those SerDes read only Decimal128
arrays; the BE used to crash on the first value, also for nested leaves.
Only values a reader can see are cast: before a nested leaf is narrowed,
every child slot that no visible row references (under a NULL list,
fixed-size-list, map or struct row, or outside a sliced parent) is
marked NULL, so a hidden value does not fail the cast and a visible
value outside the declared precision still does; map keys, which cannot
be NULL, stay checked.
- FLOAT / DOUBLE `>=` runtime filters stay residual-only. Lance compares
floats by totalOrder, so a pushed `x >= c` dropped negative-NaN rows
that Doris keeps.
- Runtime filters that mention INT64_MIN stay residual-only. The pinned
Lance SQL parser reads `-9223372036854775808` as the negation of a
magnitude that it parses as i64 first; the overflow turns the literal
into a Float64, which the planner cannot coerce to Int64 or Decimal, so
a pushed IN, `>=` or `<=` filter on an INT64 or scale-zero decimal
column failed planning and the query failed. Every other i64 is still
pushed.
- The shared Arrow DATE32 decoding
(`DataTypeDateV2SerDe::read_column_from_arrow`) rejects days outside
0000-01-01..9999-12-31 instead of returning a non-null `0000-00-00`,
skips null slots and adds the epoch in 64 bits: the DATE32 range part of
branch-4.1 / 4.2 #68649. It also changes ADBC, Arrow stream load and
paimon-rust reads.
- Child values hidden by a NULL list, fixed-size-list, map or struct row
are not decoded: the array, map and struct SerDes give the element SerDe
a validity bitmap that also clears those slots
(`mask_arrow_children_hidden_by_null_parents()`). Arrow leaves them
unspecified, and Lance's v2.1 miniblock encoding keeps them, so a hidden
10000-01-01 under a NULL fixed-size list used to fail the DATE32 range
check above. Visible values are unchanged; this applies to ADBC, Arrow
stream load and paimon-rust as well. The masks come from ExecEnv's
tracked Arrow pool. A nested Lance array that is compacted before
decoding is masked the same way, at every level, before its
`ValidateFull()`, so a hidden invalid UTF-8 string, out-of-precision
decimal, Date64 or time no longer fails the batch while a visible one
still does. Since #69697 the same masks keep hidden DATETIMEV2 /
TIMESTAMPTZ values out of its range checks. A mask costs at most one
pass per batch: a NULL row that owns no child slots, or only NULL ones,
allocates nothing, and Arrow stream load masks each retained IPC batch
once (`mask_arrow_descendants_not_visible()`) before it hands row ranges
to the SerDes, so its per-block reads allocate nothing. The one
per-block mask left is for map keys under a NULL map row that still owns
entries, since a MapArray cannot hold NULL keys; Arrow's map builder
writes NULL map rows without entries.
- Core: `to_protobuf` keeps `NULL_TYPE` for a NULL literal type, so a
Null leaf in ARRAY / MAP / STRUCT no longer comes back as BOOLEAN. The
nullable serde reads an Arrow Null array as NULLs without touching
buffers that the array does not have.
- Configs, caches and metrics, with the 4.x names and defaults:

  | Config | Default |
  |---|---|
  | `lance_index_cache_size_bytes` | 10 GiB |
  | `lance_metadata_cache_size_bytes` | 1 GiB |
  | `enable_lance_data_cache` | true |
  | `lance_data_cache_path` | `${DORIS_HOME}/lance_data_cache` |
  | `lance_data_cache_disk_capacity_bytes` | 100 GiB |
  | `lance_data_cache_read_block_size_bytes` | 1 MiB |
  | `lance_io_buffer_size_bytes` (mutable) | 2 GiB |
  | `lance_batch_readahead` (mutable) | 5 |
  | `lance_fragment_readahead` (mutable) | 5 |

The BE-wide Lance session (index cache, metadata cache and the optional
Foyer disk cache) is created by the first Lance scan, so a BE that never
reads Lance creates no cache directory. The first six configs take
effect after a restart. A negative cache size (and, while the data cache
is enabled, a negative disk capacity or read block size) fails the first
Lance scan with a message that names the setting, as does any data-cache
value lance-c rejects; the rest of the BE keeps serving. Ten
`lance_session_*` metrics report the two in-memory caches. With the
defaults, the disk tier keeps 51,200 sparse 2 MiB block files open from
the first Lance scan on (100 GiB in blocks of twice the read block
size), the same as on branch-4.1 / 4.2. Size
`lance_data_cache_disk_capacity_bytes` and
`lance_data_cache_read_block_size_bytes` against the BE's open-file
limit, or set `enable_lance_data_cache = false`.

The index and metadata caches live in lance-c memory: they are not
charged to a MemTracker or registered with CacheManager, so memory GC,
the cache capacity adjustment under memory pressure and
`/api/clear_cache/all` do not shrink them, and `enable_lance_data_cache
= false` does not turn them off. lance-c has no call to clear, evict or
resize them, so size them with the two configs. The disk tier keys
blocks by Lance's store prefix (`scheme$bucket` for S3, GCS and OSS,
without the endpoint) and the object path
(`thirdparty/patches/lance-c-foyer.patch`). Several endpoints that serve
the same bucket name and data-file path with different bytes (data files
registered under chosen names or replaced in place; Lance's writers use
random file names) share entries, also across a BE restart; such a
deployment should set `enable_lance_data_cache = false`. Both are as on
branch-4.1 / 4.2 and tracked as follow-ups.

A Lance scan's native working set, that is the I/O buffered up to
`lance_io_buffer_size_bytes` per scanner, the decode readahead and the
operators of a search plan such as the top-k of a flat vector search, is
allocated inside lance-c and is charged to neither the query nor its
workload group. It counts toward the BE process memory only, so memory
GC, which ranks queries by tracked memory, does not attribute it to the
Lance query. lance-c accepts no memory budget, so on BEs with little
memory headroom size `lance_io_buffer_size_bytes`,
`lance_batch_readahead` and `lance_fragment_readahead` for the number of
concurrent Lance scanners. As on branch-4.1 / 4.2; tracked as a
follow-up.
- Build: macOS links `-framework IOKit`. This is the first time objects
of `liblance_c.a` are pulled into `doris_be`, so the binary and the link
time grow. With this PR `util/rle_encoding.h` reaches 231 TUs and
`util/threadpool.h` 589, the same as master `315c2e00331` alone (limits
275 and 660 since #69717), and `gen_cpp/internal_service.pb.h` 562
(master alone 559, limit 660); the reach budgets are unchanged.

Not included: the Lance index metadata scanner (`LANCE_INDEX_ENTRIES`);
the FE side, including the rolling-upgrade guard that keeps Lance lazy
materialization off while an old BE could decode a version 1 row
location as version 0; the regression suites; the rest of #68707
(searching historical versions, tags and branches, rejecting fragment
ids missing from the snapshot, naming the snapshot in errors and the
profile). The `InitReaderAvgTime` / `GetBlockAvgTime` profile fix that
#67005 carried goes in a separate PR.

**Results.**

| | master | with this PR |
|---|---|---|
| A `FORMAT_LANCE` query | fails before reading anything | read by
`LanceTableReader` in FileScannerV2 |
| A Lance row in the second phase of TopN lazy materialization | cannot
be represented | a version 1 row location, read back with `take_rows` |
| A row location of an unknown version or size in phase two | decoded as
version 0 | rejected with an error |
| An Arrow DATE32 day outside 0000-01-01..9999-12-31 (ADBC, Arrow stream
load, paimon-rust, Lance) | read as a non-null `0000-00-00` | the read
fails with `InvalidArgument` |

BE unit tests (ASAN, macOS arm64, against master's pinned lance-c), on
the head rebased onto master `378404eeb12`: 1029 cases in 54 suites run,
1027 pass and 2 Parquet microbenchmarks skip by design. They cover the
Lance suites (107: `LanceTableReader*` 83, `LanceNestedNullTest` 10,
`LanceRuntimeFilterHelperTest` 10, `LanceSessionManagerTest` 4); the
row-location protocol (`MaterializationSharedStateTest`,
`RowIdStorageReaderTest`, and the new `RowIdFetchRequestValidationTest`:
count mismatch, unknown version, a Lance version on an internal mapping,
an internal row id of 2^32); `FileScannerV2Test` (28); the ORC and
Parquet reader suites (`NewOrcReaderTest`, `NewParquetReaderTest`,
`ParquetScanTest` and the other `Parquet*` suites), whose row ids now go
through `file_local`; and the Arrow / protobuf / DATE serde suites with
the new `ArrowParentNullsTest`, plus the Iceberg and Parquet reader
suites that decode Arrow dates. Reverting any of the review fixes, the
#68813 flag, the #68707 split rule or the #68815 bounds makes its new
tests fail. The sixth review round adds 13 cases (`ArrowParentNullsTest`
10, `LanceTableReaderTypeTest` 3) and extends one; with them the Arrow
SerDe, Lance, ADBC and `IcebergV2ReaderTest` suites run 317 cases: 310
pass and the 7 ADBC SQLite cases skip without a local SQLite driver. The
ninth review round adds
`LanceRuntimeFilterHelperTest.KeepsInt64MinFiltersResidualOnly` and
`LanceTableReaderFilterTest.KeepsInt64MinRuntimeFiltersOutOfLanceScanner`:
both pass with the 106 Lance cases (`Lance*`) on the branch rebased onto
`339e087b11f`, and restoring `parse_int64()` in either check makes
exactly these two fail. On the head rebased onto master `315c2e00331`,
with the per-block mask fix (`42cf4e3e5d1`), 328 cases in 24 suites
pass: the Arrow SerDe and validation suites, `ArrowParentNullsTest`
(15), `LoadArrowPipeTest` (47), the DATETIMEV2 / TIMESTAMPTZ SerDe
suites, `ArrowColumnToDorisColumnTest`, `ArrowBlockConvertorTest`,
`AdbcReaderTest`, `RemoteDorisV2ReaderTest`, `PaimonRustTableReaderTest`
and the Lance suites. Allocating at the first NULL row of each call
again fails the new `ArrowParentNullsTest` case and the four new
`LoadArrowPipeTest` block cases, dropping the per-batch mask fails the
LIST and STRUCT block cases, and returning the child unmasked fails the
new timestamp case, four `ArrowParentNullsTest` cases and
`LanceTableReaderTypeTest.IgnoresDate32PayloadHiddenByNullParents`.

### Release note

Add nine `lance_*` BE configs and `lance_session_*` BE metrics. They
take effect once the FE can plan Lance scans. Reading an Arrow DATE32
value outside 0000-01-01..9999-12-31 through ADBC, Arrow stream load or
paimon-rust now fails instead of returning 0000-00-00.

### Check List (For Author)

- Test <!-- At least one of them must be included. -->
    - [ ] Regression test
    - [x] Unit Test
- `run-be-ut.sh --run --filter=...` with the suites listed under
Results: 420 of 420 pass on macOS arm64 (ASAN) on the content before the
rebases, and the Lance and Arrow SerDe suites pass again with the
nested-offset, zero-field struct, hidden-child, hidden-Decimal256 and
compaction-mask fixes (the ADBC SQLite cases skip without a local SQLite
driver). Rebased onto master `43f9ceb48d7`: #69668 conflicted in the
scanner selection and in `fetch_table_schema`, which now hands every
file format to `format::fetch_file_schema()`, so the Lance branch runs
ahead of that call; #69646 added a TopN row-group test that reads the
version 0 row location fields, which now go through `file_local` (the
earlier rebase onto `5caaeb3e3d4` resolved #69614 and #69601 in the
scanner selection, the `FileFormat` enum, its test and the reach budget
table). The 95 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. Rebased again onto master
`8d88f1cc6f7`: the only conflict was the budget table, where #65858
moved the `util/rle_encoding.h` baseline to 828, and the 163 TUs that
this PR changes or that master changed since `43f9ceb48d7` pass the same
syntax check. Rebased again onto master `339e087b11f`: the only conflict
was the budget table again, where #67292 moved the `util/rle_encoding.h`
baseline to 834; #69872 added Arrow type checks ahead of the NULL-parent
masking in the map and struct readers and merged cleanly. Of the 114
sources that this PR changes or that master changed since `8d88f1cc6f7`,
111 pass the same syntax check. The other three are master's: two new
#67292 files under `storage/segment` fail the same way on master alone,
since they compile only inside their unity batch, and this PR adds no
source to that target; the #69818 benchmark is not in the UT build and
includes only `util/simd` headers that this PR does not touch. Reverting
any of the review fixes, the #68813 flag, the #68707 split rule or the
#68815 bounds makes its new tests fail. Rebased again onto master
`378404eeb12`, which removed the legacy file scanner (#69723): the
conflicts were in the scanner-selection code and tests that #69723
deleted, so this PR's Lance lines there went with them (including its
`LanceQueriesAlwaysUseScannerV2` test), in the profile keys of
`materialization_opertor.cpp`, and in the row fetch of
`rowid_fetcher.cpp`, whose non-Lance path is now only
`FileScannerV2::read_by_rows`; the deleted V1 ORC / Parquet row id tests
took this PR's `file_local` rename with them, and the
`util/rle_encoding.h` budget commit is dropped because master is no
longer near that limit. Of the 100 sources that this PR changes or that
master changed since `339e087b11f`, 99 pass the same syntax check; the
other, `format_v2/orc/orc_reader.cpp`, fails the same way on master
alone. On the rebased head the suites listed under Results run 1029
cases: 1027 pass and 2 microbenchmarks skip. The test-utility fix that
`DataTypeDateTimeV2SerDeTest` needed on macOS (#69695) is now on master.
Rebased again onto master `315c2e00331`: the only conflict was
`TExternalSearchRequest` in `PlanNodes.thrift`, where #70075 had
reserved fields 2-5 in comments; the two commits that add the fields
replace those comments, and field 6 stays #70075's `paimon_options`.
#69717 cut transitive includes from shared headers: of the 160 sources
that this PR changes or that master changed since `378404eeb12`, 156
pass the same syntax check; the two `jemalloc_hook` tests have no
compile command on macOS, `runtime/memory/jemalloc_hook.cpp` uses glibc
aliases that macOS cannot build (#69819), and
`format_v2/table_reader.cpp` alone fails the same way on master alone
while its unity batch compiles. The full `doris_be_test` builds and
links on the rebased head; `ArrowParentNulls*` and `Lance*` (121 cases)
pass before the mask fix, and the 328 cases above with it.
- `build-support/check-build-hygiene.sh` (on this branch rebased onto
master `315c2e00331`), clang-format 16 and
`build-support/run-clang-tidy.sh` on the changed files: all pass; on the
changed lines clang-tidy (llvm 22) reports only
`readability-inconsistent-ifelse-braces` on the `DORIS_CHECK` lines of
the masking functions, the macro's expansion that master's existing
`DORIS_CHECK`s report too.
    - [ ] Manual test (add detailed scripts or steps below)
    - [ ] No need to test or manual test. Explain why:
- Behavior changed:
    - [ ] No.
- [x] Yes. `PRequestBlockDesc.row_id` is `uint64`; phase two of TopN
lazy materialization rejects row locations of an unknown version or
size; `FORMAT_LANCE` scans run on FileScannerV2; an Arrow DATE32 day
outside the Doris DATE range fails the read instead of becoming
`0000-00-00`.
- Does this need documentation?
    - [x] No. Lance is documented with the FE connector.
- [ ] 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: zhangstar333 <[email protected]>
Co-authored-by: Gabriel <[email protected]>
Co-authored-by: zhaobo wang <[email protected]>
Co-authored-by: jay <[email protected]>
Co-authored-by: FANNG <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Co-authored-by: zy-kkk <[email protected]>
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.

3 participants