Skip to content

[Feature](lance) Support additional Arrow and Lance data types - #67325

Draft
wangzhaobo957-cloud wants to merge 7 commits into
apache:branch-4.1from
wangzhaobo957-cloud:feature/lance-additional-data-types
Draft

[Feature](lance) Support additional Arrow and Lance data types#67325
wangzhaobo957-cloud wants to merge 7 commits into
apache:branch-4.1from
wangzhaobo957-cloud:feature/lance-additional-data-types

Conversation

@wangzhaobo957-cloud

@wangzhaobo957-cloud wangzhaobo957-cloud commented Aug 31, 2026

Copy link
Copy Markdown

What problem does this PR solve?

Issue Number: close #66496

Problem Summary:

The Lance reader previously reported several Arrow and Lance-specific types as UNSUPPORTED, preventing Doris from correctly discovering schemas or reading these columns.

This PR adds Doris-side support for:

  • Arrow Null as Doris NULL
  • Arrow Duration as Doris BIGINT
  • Arrow and Lance JSON extensions as Doris JSON
  • Lance BFloat16 as Doris FLOAT
  • Lance Blob v2 as Doris STRUCT<kind, position, size, blob_id, blob_uri>

The Blob v2 mapping exposes only descriptor metadata. Doris does not materialize or return the Blob binary payload, because a single payload can be several gigabytes.

Implementation details:

  • FE recognizes the logical Blob v2 schema exposed by the Lance Java SDK and advertises the fixed descriptor Struct.
  • BE distinguishes dataset-schema discovery from scan output. Dataset schemas accept the logical struct<data, uri[, position, size]> layout, while scan batches must return the five-field descriptor.
  • BE rejects materialized LARGE_BINARY Blob payloads and unexpected descriptor layouts.
  • BFloat16 values are widened to Float32, including nested values.
  • Ordinary nested Arrow columns bypass reconstruction when no BFloat16 or registered extension array requires normalization.
  • Only top-level Arrow Null fields are supported. Nested Null fields remain unsupported because the current complex-type deserialization path cannot handle them safely.

The pinned lance-c dependency already uses BlobHandling::BlobsDescriptions by default, so this PR does not change the lance-c dependency.

Release note

Add Doris support for Arrow Null and Duration types, Lance JSON and BFloat16 extensions, and Blob v2 descriptor metadata. Blob v2 is exposed as STRUCT<kind, position, size, blob_id, blob_uri> without loading the binary payload.

Check List (For Author)

  • Test
    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test

Coverage includes:

  • FE type-conversion tests for Null, Duration, JSON, BFloat16, and Blob v2.
  • BE schema and value tests, including both logical Blob layouts, strict scan descriptor validation, and the nested-column no-op normalization path.
  • test_lance_catalog_all_types
  • test_lance_s3_tvf
  • test_lance_local_tvf, covering local TVF schema discovery and Blob descriptor reads.

Previously verified results before the latest review fix:

  • FE unit tests: 14 passed
  • BE Lance unit tests: 13 passed
  • test_lance_catalog_all_types: passed
  • test_lance_s3_tvf: passed

Latest review-fix validation:

  • Production C++ source passed syntax compilation in normal and BE_TEST configurations.

  • git diff --check passed.

  • A complete local BE UT run could not finish because the macOS OpenBLAS getarch configuration process stalled before Doris compilation; CI will run the full suite.

  • Behavior changed:

    • No.
    • Yes. Supported Lance schemas are mapped to Doris types instead of UNSUPPORTED. Blob v2 now exposes descriptor metadata rather than materializing its payload.
  • Does this need documentation?

    • No.
    • Yes. A follow-up documentation PR will be submitted separately.

Check List (For Reviewer who merge this PR)

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

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

wangzhaobo and others added 5 commits August 31, 2026 11:24
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)
Keep the pinned lance-c dependency unchanged. Handle top-level Null safely, reject unsupported nested Null shapes, protect VARBINARY lazy materialization, and update FE, BE, and regression coverage.
@wangzhaobo957-cloud
wangzhaobo957-cloud force-pushed the feature/lance-additional-data-types branch from eecec32 to 57a5835 Compare August 31, 2026 03:32
…pache#66496

Reading a Lance Blob v2 column as VARBINARY materialized the full payload,
which can reach gigabytes per value and exhaust the BE. lance-c returns the
Blob v2 descriptor (kind, position, size, blob_id, blob_uri) by default, so
expose that metadata instead of the bytes.

- FE LanceTypeConverter maps lance.blob.v2 to
  STRUCT<kind, position, size, blob_id, blob_uri>.
- BE lance_reader validates the descriptor layout, routes Blob v2 through the
  generic struct mapping, and rejects a materialized LARGE_BINARY payload.
- MaterializeProbeVisitor drops the VARBINARY guard now that Blob v2 is a
  struct covered by hasSubColPath(), restoring plain VARBINARY lazy fetch.
- Update FE/BE unit tests and regression suites/outputs accordingly.
@zhangstar333

Copy link
Copy Markdown
Contributor

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

Requesting changes for two issues:

  1. Correctness / external compatibility: local local(..., format="lance") schema discovery imports the logical Blob v2 dataset schema, but the new BE validator accepts only the five-field scan descriptor, so a valid Blob dataset is rejected before planning.
  2. Performance: BFloat normalization recursively traverses and allocates Arrow wrapper metadata for every ordinary nested column on every scanner and take_rows batch, even when the subtree contains no BFloat16 extension.

Critical checkpoints:

  • Type and reader correctness: reviewed top-level Null, raw-count Duration, Arrow/Lance JSON, nested BFloat16, Blob descriptor materialization, catalog/S3 scans, and vector take_rows; no other distinct correctness defect remains. The local-TVF Blob boundary above is blocking.
  • Lifecycle and malformed input: reviewed schema projection, nulls/slices, whole-STRUCT versus subcolumn lazy fetch, row-ID ordering/count, EOF/error cleanup, and invalid extension layouts; no separate lifecycle or malformed-input issue remains.
  • Performance and observability: the per-batch no-op normalization is blocking; no separate I/O amplification or profile-counter regression was found.
  • Tests: the current BE/unit and catalog/S3 coverage does not exercise local BE schema discovery with Blob v2 or keep the ordinary nested no-extension batch path allocation-free. The inline comments request focused coverage. Tests were not run in this review-only environment.
  • Documentation / release note: the live PR description says Blob v2 maps to VARBINARY and reads a materialized binary payload, while the patch and expected outputs expose STRUCT<kind,position,size,blob_id,blob_uri> and explicitly never materialize the payload. Please align the description/release note before merge.
  • User focus: no additional focus point was supplied.
  • Completion: complete and converged after three review rounds on head 5807f583490e9a2fc28f3a5b2e7b11497bfda3a9; all candidates were accepted, deduplicated, or dismissed with evidence.

Comment thread be/src/format_v2/table/lance_reader.cpp Outdated
};
// lance-c returns the Blob v2 descriptor as this fixed 5-field struct (Lance
// BLOB_V2_DESC_FIELDS): where a Blob lives and how large it is, never its bytes.
const bool descriptor_layout = fields.size() == 5 &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Keep local TVF schema discovery compatible with logical Blob v2 schemas. fetch_schema() imports lance_dataset_schema() directly, and the pinned lance-c v0.1.7 implementation returns the dataset's logical Blob schema there (struct<data,uri> or the ranged four-field form), not the five-field scan descriptor. Both valid layouts therefore reach this branch and fail fields.size() == 5, so local(..., format="lance") reports the Blob column as unsupported before a query can run; the new catalog/S3 tests use FE schema discovery and do not cover this path. Please map the logical layouts to the advertised descriptor during dataset-schema conversion (while keeping the strict check for scan output), and add a local Lance TVF schema/query regression with Blob v2.

Comment thread be/src/format_v2/table/lance_reader.cpp Outdated
field->name(), child_fields.size(), child_data.size());
}

auto normalized_data = storage_array->data()->Copy();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Keep no-op nested normalization out of the batch hot path. _fill_block_from_record_batch() calls this routine for every projected column, and for each ordinary LIST/MAP/STRUCT node with no BFloat16 descendant we still allocate an ArrayData copy and field vector, wrap every child with MakeArray(), and build replacement Fields before changed remains false and all of that work is discarded. This regresses every existing nested Lance scan once per record batch, proportional to schema width/depth, even though the new conversion is needed only for extension-bearing subtrees. Please precompute/gate which fields need normalization, or defer ancestor allocations until a child actually changes, and cover the no-extension path with a wide/deep nested benchmark or allocation-sensitive test.

…he#66496

Separate dataset schema conversion from scan-output validation so local Lance
TVFs can map the logical Blob v2 layout to the descriptor struct while scans
still require the five-field descriptor.

Avoid rebuilding ordinary nested Arrow arrays when their field subtree has no
BFloat16 or registered extension array. Add focused schema, fast-path, strict
scan validation, fixture scan, and local TVF regression coverage.
@wangzhaobo957-cloud

Copy link
Copy Markdown
Author

/review

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