Skip to content

[feature](lance) support lance catalog in doris - #65730

Merged
Gabriel39 merged 18 commits into
apache:branch-4.1from
zhangstar333:branch-4.1-lance-catalog
Aug 5, 2026
Merged

[feature](lance) support lance catalog in doris#65730
Gabriel39 merged 18 commits into
apache:branch-4.1from
zhangstar333:branch-4.1-lance-catalog

Conversation

@zhangstar333

@zhangstar333 zhangstar333 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue #66340
Problem Summary:
doc: apache/doris-website#4024

This PR introduces read support for the Lance format in Doris, including:

  1. Querying Lance datasets through a Lance Catalog.
  2. Pushing supported predicates down to Lance to reduce the amount of data scanned.
  3. Reading Lance datasets directly through S3 and Local TVFs.
  4. Performing vector similarity search through the vector_search() TVF, with the distance exposed through the _distance column.
  5. Lance is currently read-only and uses File Scanner V2. Writing to Lance is not supported yet.
  -- Create a Lance Catalog
  CREATE CATALOG lance_catalog PROPERTIES (
      "type" = "lance",
      "lance.catalog.type" = "filesystem",
      "warehouse" = "s3://warehouse/lance",
      "s3.endpoint" = "http://127.0.0.1:9000",
      "s3.access_key" = "admin",
      "s3.secret_key" = "password",
      "s3.region" = "us-east-1",
      "use_path_style" = "true"
  );

  -- Regular query; supported WHERE predicates are pushed down to Lance
  SELECT user_id, user_name
  FROM lance_catalog.doris.user_profiles
  WHERE engagement_score >= 90;

  -- Read a Lance dataset directly through the S3 TVF
  SELECT user_id, user_name
  FROM s3(
      "uri" = "s3://warehouse/lance/doris/user_profiles.lance",
      "format" = "lance",
      "s3.endpoint" = "http://127.0.0.1:9000",
      "s3.access_key" = "admin",
      "s3.secret_key" = "password",
      "s3.region" = "us-east-1",
      "use_path_style" = "true"
  );

  -- Perform a vector similarity search
  SELECT row_id, label, _distance
  FROM vector_search(
      "table" = "lance_catalog.doris.vector_search",
      "column" = "embedding",
      "query_vector" = "[0,0,0,0]",
      "top_k" = "5",
      "metric" = "l2"
  )
  ORDER BY _distance, row_id;

None

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason
  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

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

@zhangstar333
zhangstar333 requested a review from yiguolei as a code owner July 16, 2026 14:27
@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?

@zhangstar333
zhangstar333 force-pushed the branch-4.1-lance-catalog branch 2 times, most recently from ce3b540 to f760bd0 Compare July 30, 2026 12:25
@zhangstar333 zhangstar333 changed the title Branch 4.1 test [feature](lance) support lance catalog in doris Jul 30, 2026
@zhangstar333
zhangstar333 force-pushed the branch-4.1-lance-catalog branch from 4dd1bee to eb379a8 Compare July 31, 2026 07:17
@zhangstar333

Copy link
Copy Markdown
Contributor Author

/review

@morningman morningman mentioned this pull request Jul 31, 2026
22 tasks

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

I found several security and correctness issues that should be addressed before merging.

Comment thread be/src/format_v2/table/lance_reader.cpp Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: Review step was failure (possibly timeout or cancelled)
Workflow run: https://github.com/apache/doris/actions/runs/30613481239

Please inspect the workflow logs and rerun the review after the underlying issue is resolved.

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

This adds a substantial Lance integration, but the current scan path still has blocking correctness, memory-safety, process-safety, security-display, snapshot-consistency, placement, and resource-governance defects. The inline comments identify thirteen distinct issues that remain after deduplicating against the four live comments already on the PR. Review was static-only as required; no build or test commands were run.

Comment thread be/src/format_v2/table/lance_reader.cpp Outdated
Comment thread be/src/core/data_type_serde/data_type_varbinary_serde.cpp
Comment thread be/src/core/data_type_serde/data_type_array_serde.cpp
Comment thread be/src/format_v2/table/lance_reader.cpp
Comment thread be/src/format_v2/table/lance_reader.cpp
@Gabriel39

Gabriel39 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

在这一版实现里,我们要把Postfilter改成Lance SDK 原生 Postfilter
另外,测试覆盖要把各种索引类型都加上

@zhangstar333

Copy link
Copy Markdown
Contributor Author

run buildall

@zhangstar333
zhangstar333 force-pushed the branch-4.1-lance-catalog branch from c9a4827 to 70516cf Compare August 4, 2026 11:32
@zhangstar333

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

Cloud UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 77.62% (1904/2453)
Line Coverage 64.53% (34057/52774)
Region Coverage 64.49% (17216/26694)
Branch Coverage 54.03% (9220/17064)

@zhangstar333

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

Cloud UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 77.62% (1904/2453)
Line Coverage 64.45% (34012/52774)
Region Coverage 64.46% (17206/26694)
Branch Coverage 53.96% (9207/17064)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 69.09% (847/1226) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.67% (30364/41215)
Line Coverage 57.62% (333867/579401)
Region Coverage 54.49% (277771/509733)
Branch Coverage 55.46% (124428/224358)

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 65.66% (1017/1549) 🎉
Increment coverage report
Complete coverage report

@zhangstar333

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39
Gabriel39 merged commit e3289c1 into apache:branch-4.1 Aug 5, 2026
27 of 34 checks passed
@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 69.31% (856/1235) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.67% (30386/41244)
Line Coverage 57.57% (333689/579583)
Region Coverage 54.50% (277884/509860)
Branch Coverage 55.43% (124402/224419)

FANNG1 pushed a commit to FANNG1/doris that referenced this pull request Aug 6, 2026
…pache#66495)

The vector_search() query path is algorithm-agnostic, but until now only
flat search was actually exercised: the fixture from apache#65730 never created
the vector index its tests claimed to cover (the referenced
create_vector_search_index.py was never committed), so Lance silently fell
back to flat KNN and the "IVF_PQ" regression proved nothing about indexes.

This change adds verified IVF_FLAT query coverage and repairs the IVF_PQ
fixture:

- Add lance_build_preinstalled_catalog.py, an offline fixture generator
  pinned to pylance 4.0.1 (matching lance-c v0.1.2 / lance-rs 4.0.1). It
  builds the Directory V2 catalog through the namespace API (all_types
  re-registered, doris namespace, one indexed table per algorithm:
  vector_search_ivf_flat and vector_search_ivf_pq), then self-checks:
  Lance plans contain ANNSubIndex/ANNIvfPartition, indexes cover both
  fragments, IVF_FLAT full-probe equals flat search exactly, and the
  row-256 partition-boundary discriminator holds per table. The generated
  catalog (404K) is committed; the manifest is compacted back to one
  version/one data file.
- Deterministic data: 1024 rows, two fragments, 16-dim Float32 with
  embedding[j] = (row_id - 1) + j, so a query equal to row r's vector has
  exact squared L2 distance 16 * (n - r)^2 to row n - integer ladder,
  tie-free at the endpoints, exact in Float32.
- Rewrite test_lance_vector_search to target the real IVF_PQ table and add
  test_lance_vector_search_ivf_flat. Both suites carry a silent-fallback
  discriminator: querying the row-256 boundary with nprobes=1 must differ
  from the flat result (a pipeline that ignores the index returns exactly
  the flat rows and fails the assertion). IVF_FLAT additionally asserts
  indexed == flat programmatically (its algorithm guarantee); IVF_PQ
  freezes refine_factor-reranked results in goldens without claiming
  algorithm guarantees.
- Remove run07_create_vector_types.sql and the stale golden output.

Cross-version compatibility of the generated manifest was verified against
lance-java 4.0.0 (lance-spark-bundle 0.4.0, runtime table registration)
and lance-java 9.1.0-beta.3 (FE Directory Namespace client): both list,
describe, register and deregister tables against it correctly.

The remaining algorithms (IVF_SQ, IVF_HNSW_FLAT, IVF_HNSW_SQ,
IVF_HNSW_PQ) follow the same pattern in follow-up PRs.

Draft until the .out goldens are regenerated by a full docker regression
run, which also provides the FE -> BE -> lance-c end-to-end evidence.

Part of apache#66495.

Claude-Session: https://claude.ai/code/session_014jKR13f5jdqs8NFyEuCnAN
FANNG1 pushed a commit to FANNG1/doris that referenced this pull request Aug 6, 2026
…pache#66495)

The vector_search() query path is algorithm-agnostic, but until now only
flat search was actually exercised: the fixture from apache#65730 never created
the vector index its tests claimed to cover (the referenced
create_vector_search_index.py was never committed), so Lance silently fell
back to flat KNN and the "IVF_PQ" regression proved nothing about indexes.

This change adds verified IVF_FLAT query coverage and repairs the IVF_PQ
fixture:

- Add lance_build_preinstalled_catalog.py, an offline fixture generator
  pinned to pylance 4.0.1 (matching lance-c v0.1.2 / lance-rs 4.0.1). It
  builds the Directory V2 catalog through the namespace API (all_types
  re-registered, doris namespace, one indexed table per algorithm:
  vector_search_ivf_flat and vector_search_ivf_pq), then self-checks:
  Lance plans contain ANNSubIndex/ANNIvfPartition, indexes cover both
  fragments, IVF_FLAT full-probe equals flat search exactly, and the
  row-256 partition-boundary discriminator holds per table. The generated
  catalog (404K) is committed; the manifest is compacted back to one
  version/one data file.
- Deterministic data: 1024 rows, two fragments, 16-dim Float32 with
  embedding[j] = (row_id - 1) + j, so a query equal to row r's vector has
  exact squared L2 distance 16 * (n - r)^2 to row n - integer ladder,
  tie-free at the endpoints, exact in Float32.
- Rewrite test_lance_vector_search to target the real IVF_PQ table and add
  test_lance_vector_search_ivf_flat. Both suites carry a silent-fallback
  discriminator: querying the row-256 boundary with nprobes=1 must differ
  from the flat result (a pipeline that ignores the index returns exactly
  the flat rows and fails the assertion). IVF_FLAT additionally asserts
  indexed == flat programmatically (its algorithm guarantee); IVF_PQ
  freezes refine_factor-reranked results in goldens without claiming
  algorithm guarantees.
- Remove run07_create_vector_types.sql and the stale golden output.

Cross-version compatibility of the generated manifest was verified against
lance-java 4.0.0 (lance-spark-bundle 0.4.0, runtime table registration)
and lance-java 9.1.0-beta.3 (FE Directory Namespace client): both list,
describe, register and deregister tables against it correctly.

The remaining algorithms (IVF_SQ, IVF_HNSW_FLAT, IVF_HNSW_SQ,
IVF_HNSW_PQ) follow the same pattern in follow-up PRs.

Draft until the .out goldens are regenerated by a full docker regression
run, which also provides the FE -> BE -> lance-c end-to-end evidence.

Part of apache#66495.

Claude-Session: https://claude.ai/code/session_014jKR13f5jdqs8NFyEuCnAN
u70b3 added a commit to u70b3/doris that referenced this pull request Aug 11, 2026
### What problem does this PR solve?

Issue Number: apache#66497

Related PR: apache#65730

Problem Summary: Lance external tables can contain logical vector and scalar indexes, but SHOW INDEX currently only handles internal OlapTable metadata and returns an empty result for Lance tables. Read authoritative logical index descriptions from one latest Directory Dataset snapshot, resolve field IDs from the same snapshot schema, and expose deterministic bounded metadata through the existing 13-column SHOW INDEX result without changing internal-table behavior.

### Release note

Support SHOW INDEX for Lance filesystem/Directory Catalog tables. Lance REST index inspection remains unsupported in this phase.

### Check List (For Author)

- Test: Unit Test and Regression test
- Behavior changed: Yes. SHOW INDEX now returns logical indexes for Lance Directory tables and explicitly rejects Lance REST catalogs.
- Does this need documentation: Yes. Document the supported Directory-only scope and REST limitation.
u70b3 added a commit to u70b3/doris that referenced this pull request Aug 13, 2026
### What problem does this PR solve?

Issue Number: apache#66497

Related PR: apache#65730

Problem Summary: Lance external tables can contain logical vector and scalar indexes, but SHOW INDEX currently only handles internal OlapTable metadata and returns an empty result for Lance tables. Read authoritative logical index descriptions from one latest Directory Dataset snapshot, resolve field IDs from the same snapshot schema, and expose deterministic bounded metadata through the existing 13-column SHOW INDEX result without changing internal-table behavior.

### Release note

Support SHOW INDEX for Lance filesystem/Directory Catalog tables. Lance REST index inspection remains unsupported in this phase.

### Check List (For Author)

- Test: Unit Test and Regression test
- Behavior changed: Yes. SHOW INDEX now returns logical indexes for Lance Directory tables and explicitly rejects Lance REST catalogs.
- Does this need documentation: Yes. Document the supported Directory-only scope and REST limitation.
yiguolei pushed a commit that referenced this pull request Aug 14, 2026
### What problem does this PR solve?

Issue Number: N/A

Related PR: #65730, #66581

Problem Summary:

Ordinary Lance scans currently read **every row of a fragment** even
when the
query only needs the first N rows (e.g. `SELECT ... LIMIT 10`). Lance
applies
its own LIMIT *after* the scanner's filter, so the query LIMIT can be
forwarded
to each fragment scanner and let it stop early, cutting IO and decode
cost.

**How it is fixed**

- `thrift`: add an optional `TLanceFileDesc.limit`.
- `FE` (`LanceScanNode`): push the query limit into each fragment split
via
  `canPushDownLimit()`, and surface `lanceLimit` in the explain output.
- `BE` (`lance_reader`): forward it to the scanner through
`lance_scanner_set_limit` for ordinary scans; vector search keeps its
own
  `top_k` limit.

**Correctness**

The limit is pushed **only when all predicates are already pushed into
Lance**
(no residual Doris conjunct). Otherwise Doris still re-filters the
returned rows,
and truncating a fragment early could drop valid results.

`OFFSET` needs no special handling: Nereids' `SplitLimit` rewrites
`Limit(limit, offset)` into a global `Limit(limit, offset)` over a local
`Limit(limit + offset, 0)`, and that local bound is what reaches the
scan node.
So `getLimit()` already includes the offset; each fragment fetches up to
`limit + offset` rows and the upper global LIMIT still applies the
offset and
the final bound. Per-fragment truncation is therefore always safe.

**Behavior change**

Query results are unchanged. Only the number of rows scanned per
fragment is
reduced for LIMIT queries; the explain output shows an extra
`lanceLimit=N`
line when the limit is pushed.

### Release note

Push down LIMIT into Lance fragment scanners to reduce the rows scanned
for
`LIMIT` / `LIMIT ... OFFSET` queries over Lance tables.

### Check List (For Author)

- Test
- [x] Unit Test (`LanceThriftContractTest` covers the limit round-trip
and the no-limit case)
- [ ] Manual test — `SELECT * FROM <lance_tbl> LIMIT 10` returns 10 rows
and `EXPLAIN` shows `lanceLimit=10`; a query with a non-pushable
predicate keeps the limit out of the scan
- Behavior changed:
  - [x] No.
- Does this need documentation?
  - [x] No.
u70b3 added a commit to u70b3/doris that referenced this pull request Aug 15, 2026
### What problem does this PR solve?

Issue Number: apache#66497

Related PR: apache#65730

Problem Summary: Lance external tables can contain logical vector and scalar indexes, but SHOW INDEX currently only handles internal OlapTable metadata and returns an empty result for Lance tables. Read authoritative logical index descriptions from one latest Directory Dataset snapshot, resolve field IDs from the same snapshot schema, and expose deterministic bounded metadata through the existing 13-column SHOW INDEX result without changing internal-table behavior.

### Release note

Support SHOW INDEX for Lance filesystem/Directory Catalog tables. Lance REST index inspection remains unsupported in this phase.

### Check List (For Author)

- Test: Unit Test and Regression test
- Behavior changed: Yes. SHOW INDEX now returns logical indexes for Lance Directory tables and explicitly rejects Lance REST catalogs.
- Does this need documentation: Yes. Document the supported Directory-only scope and REST limitation.
u70b3 added a commit to u70b3/doris that referenced this pull request Aug 16, 2026
### What problem does this PR solve?

Issue Number: apache#66497

Related PR: apache#65730

Problem Summary: Lance external tables can contain logical vector and scalar indexes, but SHOW INDEX currently only handles internal OlapTable metadata and returns an empty result for Lance tables. Read authoritative logical index descriptions from one latest Directory Dataset snapshot, resolve field IDs from the same snapshot schema, and expose deterministic bounded metadata through the existing 13-column SHOW INDEX result without changing internal-table behavior.

### Release note

Support SHOW INDEX for Lance filesystem/Directory Catalog tables. Lance REST index inspection remains unsupported in this phase.

### Check List (For Author)

- Test: Unit Test and Regression test
- Behavior changed: Yes. SHOW INDEX now returns logical indexes for Lance Directory tables and explicitly rejects Lance REST catalogs.
- Does this need documentation: Yes. Document the supported Directory-only scope and REST limitation.
u70b3 added a commit to u70b3/doris that referenced this pull request Aug 20, 2026
### What problem does this PR solve?

Issue Number: apache#66497

Related PR: apache#65730

Problem Summary: Lance external tables can contain logical vector and scalar indexes, but SHOW INDEX currently only handles internal OlapTable metadata and returns an empty result for Lance tables. Read authoritative logical index descriptions from one latest Directory Dataset snapshot, resolve field IDs from the same snapshot schema, and expose deterministic bounded metadata through the existing 13-column SHOW INDEX result without changing internal-table behavior.

### Release note

Support SHOW INDEX for Lance filesystem/Directory Catalog tables. Lance REST index inspection remains unsupported in this phase.

### Check List (For Author)

- Test: Unit Test and Regression test
- Behavior changed: Yes. SHOW INDEX now returns logical indexes for Lance Directory tables and explicitly rejects Lance REST catalogs.
- Does this need documentation: Yes. Document the supported Directory-only scope and REST limitation.
u70b3 added a commit to u70b3/doris that referenced this pull request Aug 20, 2026
### What problem does this PR solve?

Issue Number: apache#66497

Related PR: apache#65730

Problem Summary: Lance external tables can contain logical vector and scalar indexes, but SHOW INDEX currently only handles internal OlapTable metadata and returns an empty result for Lance tables. Read authoritative logical index descriptions from one latest Directory Dataset snapshot, resolve field IDs from the same snapshot schema, and expose deterministic bounded metadata through the existing 13-column SHOW INDEX result without changing internal-table behavior.

### Release note

Support SHOW INDEX for Lance filesystem/Directory Catalog tables. Lance REST index inspection remains unsupported in this phase.

### Check List (For Author)

- Test: Unit Test and Regression test
- Behavior changed: Yes. SHOW INDEX now returns logical indexes for Lance Directory tables and explicitly rejects Lance REST catalogs.
- Does this need documentation: Yes. Document the supported Directory-only scope and REST limitation.
u70b3 added a commit to u70b3/doris that referenced this pull request Aug 21, 2026
### What problem does this PR solve?

Issue Number: apache#66497

Related PR: apache#65730

Problem Summary: Lance external tables can contain logical vector and scalar indexes, but SHOW INDEX currently only handles internal OlapTable metadata and returns an empty result for Lance tables. Read authoritative logical index descriptions from one latest Directory Dataset snapshot, resolve field IDs from the same snapshot schema, and expose deterministic bounded metadata through the existing 13-column SHOW INDEX result without changing internal-table behavior.

### Release note

Support SHOW INDEX for Lance filesystem/Directory Catalog tables. Lance REST index inspection remains unsupported in this phase.

### Check List (For Author)

- Test: Unit Test and Regression test
- Behavior changed: Yes. SHOW INDEX now returns logical indexes for Lance Directory tables and explicitly rejects Lance REST catalogs.
- Does this need documentation: Yes. Document the supported Directory-only scope and REST limitation.
u70b3 added a commit to u70b3/doris that referenced this pull request Aug 21, 2026
### What problem does this PR solve?

Issue Number: apache#66497

Related PR: apache#65730

Problem Summary: Lance external tables can contain logical vector and scalar indexes, but SHOW INDEX currently only handles internal OlapTable metadata and returns an empty result for Lance tables. Read authoritative logical index descriptions from one latest Directory Dataset snapshot, resolve field IDs from the same snapshot schema, and expose deterministic bounded metadata through the existing 13-column SHOW INDEX result without changing internal-table behavior.

### Release note

Support SHOW INDEX for Lance filesystem/Directory Catalog tables. Lance REST index inspection remains unsupported in this phase.

### Check List (For Author)

- Test: Unit Test and Regression test
- Behavior changed: Yes. SHOW INDEX now returns logical indexes for Lance Directory tables and explicitly rejects Lance REST catalogs.
- Does this need documentation: Yes. Document the supported Directory-only scope and REST limitation.
u70b3 added a commit to u70b3/doris that referenced this pull request Aug 21, 2026
### What problem does this PR solve?

Issue Number: apache#66497

Related PR: apache#65730

Problem Summary: Lance external tables can contain logical vector and scalar indexes, but SHOW INDEX currently only handles internal OlapTable metadata and returns an empty result for Lance tables. Read authoritative logical index descriptions from one latest Directory Dataset snapshot, resolve field IDs from the same snapshot schema, and expose deterministic bounded metadata through the existing 13-column SHOW INDEX result without changing internal-table behavior.

### Release note

Support SHOW INDEX for Lance filesystem/Directory Catalog tables. Lance REST index inspection remains unsupported in this phase.

### Check List (For Author)

- Test: Unit Test and Regression test
- Behavior changed: Yes. SHOW INDEX now returns logical indexes for Lance Directory tables and explicitly rejects Lance REST catalogs.
- Does this need documentation: Yes. Document the supported Directory-only scope and REST limitation.
u70b3 added a commit to u70b3/doris that referenced this pull request Aug 21, 2026
### What problem does this PR solve?

Issue Number: apache#66497

Related PR: apache#65730

Problem Summary: Lance external tables can contain logical vector and scalar indexes, but SHOW INDEX currently only handles internal OlapTable metadata and returns an empty result for Lance tables. Read authoritative logical index descriptions from one latest Directory Dataset snapshot, resolve field IDs from the same snapshot schema, and expose deterministic bounded metadata through the existing 13-column SHOW INDEX result without changing internal-table behavior.

### Release note

Support SHOW INDEX for Lance filesystem/Directory Catalog tables. Lance REST index inspection remains unsupported in this phase.

### Check List (For Author)

- Test: Unit Test and Regression test
- Behavior changed: Yes. SHOW INDEX now returns logical indexes for Lance Directory tables and explicitly rejects Lance REST catalogs.
- Does this need documentation: Yes. Document the supported Directory-only scope and REST limitation.
yiguolei pushed a commit that referenced this pull request Aug 24, 2026
### What problem does this PR solve?

Issue Number: #66497

Related PR: #65730

Lance external tables can contain logical vector and scalar indexes, but
`SHOW INDEX` currently only handles internal `OlapTable` metadata and
returns an empty result for Lance tables.

This change reads authoritative logical index descriptions from one
latest Lance Directory Dataset snapshot, resolves field IDs against the
schema from that same snapshot, and exposes deterministic, bounded
metadata through the existing 13-column `SHOW INDEX` result without
changing internal-table behavior.

### What changed?

- Add immutable Lance logical-index metadata and a loader backed by one
latest Dataset snapshot.
- Support `SHOW INDEX`, `SHOW INDEXES`, `SHOW KEY`, and `SHOW KEYS` for
Lance filesystem/Directory Catalog tables.
- Preserve existing internal-table behavior and permission-check
ordering (privilege check runs before any catalog initialization).
- Return a fixed unsupported error for Lance REST catalogs in this
phase.
- Sanitize Dataset URI, storage credential, and REST secret values from
index metadata errors.
- Defensively reject non-Lance tables resolved from a Lance catalog
instead of failing on a raw cast.
- Add unit and regression coverage for normalization, formatting,
permissions, aliases, empty-index tables, and error handling.

### Design walkthrough (diagrams)

`SHOW INDEX` for Lance Directory tables is a small read pipeline whose
main concern is object ownership across the JNI boundary: SDK objects
(`Dataset`, `IndexDescription`, Arrow allocators) never escape the
loader, and the command layer only sees immutable `LanceLogicalIndex`
values.

Layered view — three new classes (`LanceLogicalIndex`,
`LanceIndexMetadataLoader`, `LanceMetadataReadExecutor`) and three touch
points (`ShowIndexCommand`, `LanceExternalTable`,
`LanceExternalCatalog`):

```mermaid
flowchart TB
  subgraph CMD["Command layer · Nereids"]
    A["ShowIndexCommand<br/>analyze() privilege check (:83)<br/>handleShowIndex() dispatch (:111)<br/>buildLanceRows() row mapping (:159)"]
  end
  subgraph EXT["External table layer"]
    B["LanceExternalTable<br/>loadIndexMetadata() (:69) — thin delegate"]
  end
  subgraph CAT["Catalog layer · caller thread"]
    C["LanceExternalCatalog<br/>isRestCatalogConfigured() (:172) early REST rejection<br/>resolveTableAccess() (:371) namespace resolution"]
    C2["sanitizedRootCauseMessage() (:473)<br/>URI / credential / token redaction"]
  end
  subgraph BND["Execution boundary · lance-metadata-read pool"]
    D["LanceMetadataReadExecutor (:50)<br/>4 concurrent · 16 queued · deadline ≤ 60s"]
    E["LanceIndexMetadataLoader<br/>load() (:82) → normalize() (:206)"]
  end
  subgraph NAT["native · JNI"]
    F["Dataset.open(uri, latest)<br/>schema + describeIndices from one snapshot"]
  end
  G["LanceLogicalIndex<br/>immutable value object · pure-Java boundary"]
  A --> B --> C --> D --> E --> F
  E --> G
  C -.->|any failure| C2
  C2 -.->|redacted exception only| A
```

Request timeline and resource ownership — namespace resolution stays on
the caller thread (it owns the catalog's shared namespace/allocator),
the JNI read runs on a bounded worker with a task-owned allocator, and a
timed-out caller never cancels the in-flight native call:

```mermaid
sequenceDiagram
  autonumber
  participant U as User
  participant C as Caller thread<br/>ShowIndexCommand
  participant K as Caller thread<br/>LanceExternalCatalog
  participant X as Executor<br/>boundary
  participant W as Worker thread<br/>lance-metadata-read
  participant J as Lance JNI<br/>Dataset
  U->>C: SHOW INDEX FROM lance_db.t
  C->>C: analyze() → checkTblPriv(SHOW)
  Note over C: privilege check runs before any catalog initialization
  C->>K: isRestCatalogConfigured()
  Note over K: reads normalized properties only, no namespace init —<br/>REST catalogs rejected here with a fixed error
  C->>K: resolveTableAccess()
  Note over K: shared namespace / allocator owned by the caller thread,<br/>so catalog close stays safe
  K-->>C: ResolvedTableAccess(uri, options)
  C->>X: execute(task)
  X->>W: submit (wrapped task checks deadline first)
  W->>W: expired in queue? fail before entering JNI
  W->>J: open(latest) + schema + describeIndices
  Note over W,J: task-owned RootAllocator (256MB) —<br/>catalog close cannot release it early
  J-->>W: IndexDescription + LanceSchema
  W-->>X: immutable index list
  X-->>C: future.get(remaining deadline)
  Note over C,X: timeout / interrupt never cancels the Future —<br/>the worker stays sole owner of native resources
  C->>C: buildLanceRows() → 13 columns
  C-->>U: ShowResultSet
```

Loader internals — one snapshot feeds both the schema mapping and the
index descriptions; normalization is fail-closed and produces
deterministic, bounded output:

```mermaid
flowchart LR
  O["Dataset.open(uri, latest)<br/>index cache 0 · metadata cache 64MB<br/>schema and indexes from one snapshot"]
  O --> S["getLanceSchema().fields()"]
  O --> L["listIndexes()<br/>physical entries ≤ 16384"]
  subgraph SB["schema branch"]
    S --> DFS["buildFieldNamesById() (:148)<br/>DFS depth ≤ 64 · fields ≤ 16384<br/>field id → dotted path, quoted when needed"]
  end
  subgraph IB["index branch"]
    L --> FL["drop system indexes<br/>__lance_frag_reuse · __lance_mem_wal"]
    FL --> DD["dedupe by logical name ≤ 256"]
    DD --> CR["describeUserIndexes() (:95)<br/>per-name describeIndices(hasName)<br/>must return exactly 1, name must match"]
  end
  DFS --> V
  CR --> V
  subgraph NB["normalize() (:206) · fail-closed"]
    V["known, unique field ids<br/>columns ≤ 64 · names ≤ 16KB total"] --> P["details JSON allowlist<br/>TreeMap-sorted · ≤ 400B"]
    P --> R["exact duplicate names rejected<br/>sorted by (name, position)"]
  end
  R --> OUT["LanceLogicalIndex list<br/>immutable · deterministic"]
```

### Fail-closed semantics (intentional)

Three strict behaviors are deliberate and locked by unit tests — the
command reports an error rather than showing potentially misleading
metadata:

- An index referencing an unknown or nested field ID fails the whole
`SHOW INDEX` (`testRejectsUnknownNestedDuplicateNullAndEmptyFieldIds`).
- One malformed index entry (invalid details JSON, oversize fields,
duplicate name) fails the whole result instead of silently dropping rows
(`testRejectsMalformedAndNonObjectJsonWithoutEchoingInput`).
- Index names differing only by case are preserved as distinct entries;
only exact duplicates are rejected
(`testRejectsExactDuplicateNameButPreservesCaseOnlyNames`).

### User/developer impact

Users can inspect logical Lance indexes through the standard 13-column
`SHOW INDEX` interface for filesystem/Directory catalogs. Lance REST
index inspection remains unsupported.

### Release note

Support `SHOW INDEX` for Lance filesystem/Directory Catalog tables.

### Check List (For Author)

- [x] Unit tests: 28 tests passed
- [x] Full FE build: all 27 modules passed
- [x] Lance regression test: generation and normal verification modes
passed
- [x] `git diff --check`
- [x] Behavior changed: `SHOW INDEX` now returns logical indexes for
Lance Directory tables and explicitly rejects Lance REST catalogs
- [x] Documentation needed: document the supported Directory-only scope
and REST limitation
wangzhaobo957-cloud pushed a commit to wangzhaobo957-cloud/doris that referenced this pull request Aug 31, 2026
Issue Number: close apache#66496

Related PR: apache#65730

Problem Summary: Lance schema discovery rejected Blob v2, Arrow JSON, BFloat16, Arrow null, and Arrow duration fields as unsupported. Add validated FE and BE mappings, materialize Blob payloads through lance-c, normalize BFloat16 values to FLOAT, preserve duration counts as BIGINT, and materialize Arrow null values safely.

Support reading Lance Blob v2, Arrow JSON, BFloat16, null, and duration columns.

- Test: Unit Test (added; not run locally at user request)

- Behavior changed: Yes (the newly supported Lance columns are queryable)

- Does this need documentation: Yes (type matrix is documented in the regression suite; user documentation follow-up is required)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by one committer. dev/5.0.x

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants