Skip to content

fix: convert Arrow JSON when updating columns via merge/update path#7472

Merged
wjones127 merged 2 commits into
lance-format:mainfrom
xloya:upstream-pr/json-fragment-update
Jul 7, 2026
Merged

fix: convert Arrow JSON when updating columns via merge/update path#7472
wjones127 merged 2 commits into
lance-format:mainfrom
xloya:upstream-pr/json-fragment-update

Conversation

@xloya

@xloya xloya commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Problem

Updating an Arrow JSON column through fragment.update_columns / the update merge path fails with a type-mismatch error.

Root cause

The HashJoiner right-side stream did not convert Arrow JSON (Utf8) to Lance JSON (LargeBinary/JSONB) before joining.

Fix

Wrap the right-side stream in a JsonConvertingReader that converts Arrow JSON fields to Lance JSON before the join.

Test

Python test_fragment_update_columns_with_json_column plus dataset update tests.

@github-actions github-actions Bot added A-python Python bindings bug Something isn't working labels Jun 25, 2026
@xloya xloya force-pushed the upstream-pr/json-fragment-update branch from 7de1961 to 9939817 Compare June 25, 2026 08:06
@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.41270% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
rust/lance/src/dataset/write/merge_insert.rs 97.22% 0 Missing and 3 partials ⚠️

📢 Thoughts on this report? Let us know!

@wjones127 wjones127 self-requested a review July 2, 2026 21:35

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

Thanks for making a PR. I noticed one potential blocking issue when reviewing, but otherwise this looks like a nice fix.

@@ -1134,8 +1135,25 @@ impl MergeInsertJob {
// will be updates.
let mut source_batches = Vec::with_capacity(batches.len() + 1);
source_batches.push(batches[0].clone()); // placeholder for source data

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.

issue(blocking): You convert all the batches in this vector except the first one added here, which means the schema might be inconsistent between the batches. I think you need to convert this one as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch. I removed the needs_json_conversion flag entirely and now call convert_json_columns on every batch. Since convert_json_columns already no-ops (returns a clone) when there's nothing to convert, the separate schema scan was redundant — this fixes the schema-consistency bug and also handles nested Arrow JSON that the old top-level predicate missed. So the schema_has_json_fields helper is no longer needed.

Comment on lines +1143 to +1145
.fields()
.iter()
.any(|f| is_arrow_json_field(f) || has_json_fields(f));

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.

suggestion: should we just make a helper schema_has_json_fields(schema: &Schema) -> bool?

@xloya xloya force-pushed the upstream-pr/json-fragment-update branch from afea1ac to 281ba83 Compare July 7, 2026 04:37
@wjones127 wjones127 self-requested a review July 7, 2026 05:59
xiaojiebao and others added 2 commits July 7, 2026 15:42
Convert every source batch (including the placeholder) with
convert_json_columns, which is a no-op clone when there is nothing to
convert. This fixes the placeholder batch skipping conversion and drops
the now-unused flag/predicate helpers.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
@xloya xloya force-pushed the upstream-pr/json-fragment-update branch from 281ba83 to 0ed54f4 Compare July 7, 2026 07:50
Comment on lines +2961 to +2967
/// A wrapper around a `RecordBatchReader` that converts Arrow JSON columns
/// (Utf8/LargeUtf8 with `arrow.json` extension) to Lance JSON columns
/// (LargeBinary with `lance.json` extension / JSONB format).
///
/// This is needed when user-provided data contains Arrow JSON fields but the
/// dataset stores them in Lance's JSONB binary format.
struct JsonConvertingReader {

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.

suggestion: should we later consolidate this with SchemaAdapter?

/// Adapter around the existing JSON and view-type conversion utilities.
#[derive(Debug, Clone)]
pub struct SchemaAdapter {
logical_schema: ArrowSchemaRef,
}

Can do that as a follow up.

@wjones127 wjones127 merged commit 3e82c05 into lance-format:main Jul 7, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-python Python bindings bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants