Skip to content

[python] Deserialize cross-language SplitSerializer v1 into pypaimon DataSplit#8523

Draft
XiaoHongbo-Hope wants to merge 9 commits into
apache:masterfrom
XiaoHongbo-Hope:rust_plan
Draft

[python] Deserialize cross-language SplitSerializer v1 into pypaimon DataSplit#8523
XiaoHongbo-Hope wants to merge 9 commits into
apache:masterfrom
XiaoHongbo-Hope:rust_plan

Conversation

@XiaoHongbo-Hope

Copy link
Copy Markdown
Contributor

Purpose

Tests

…DataSplit

pypaimon_rust plans splits fast and serializes them as the cross-language
SplitSerializer v1 binary (Split.serialize()). Add a pypaimon-side deserializer
that rebuilds a DataSplit from that binary (DataSplit v8 frame: partition,
bucket, bucket_path, DataFileMeta rows, deletion files), so pypaimon can read
Rust-planned splits. Update rust_plan.py to consume serialize() instead of the
removed to_dict(). Verified against the Java-generated golden.
Remove the now-unused ArrayType import (flake8). Add tests for the
BinaryArray<string> decoder (inline and var-pointer elements) and for the
IndexedSplit (type id 3) golden.
Raise a clear error on unexpected SplitSerializer/DataSplit version (only the
magics were checked before). Document why min/max key + stats are left empty
(planning-only; the append/data-evolution read path never uses them) and that
modified-UTF8 decoding is BMP-only. Add a negative test for bad magic/version
and fix the embedded IndexedSplit golden (had a corrupted version byte).
deserialize_split_v1 returned a plain DataSplit for an IndexedSplit (type 3),
silently discarding its row_ranges/scores. The reader only restricts rows when
the split is an IndexedSplit, so a Rust-planned ANN/row-id split would scan the
whole file and return extra rows. Parse row_ranges + scores and return an
IndexedSplit; the golden test now asserts them.
…ng contract

Validate the IndexedSplit magic/version (were only skipped) with clear errors
and a negative test. Document that predicate/projection/limit passed to
rust_plan prune only at the split level; the caller must apply the equivalent
pypaimon filter/projection/limit on the read for row-level correctness.
PK merge-on-read needs real min/max keys, which the deserializer leaves empty;
fail loud instead of silently breaking the sort-merge. Add a guard test.
@XiaoHongbo-Hope XiaoHongbo-Hope marked this pull request as ready for review July 9, 2026 11:26
@XiaoHongbo-Hope XiaoHongbo-Hope marked this pull request as draft July 9, 2026 11:31
@XiaoHongbo-Hope XiaoHongbo-Hope marked this pull request as ready for review July 9, 2026 11:32
return [by_name[name] for name in schema.partition_keys]


def rust_plan(table, table_identifier: str, catalog_options: dict, *,

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 is not called, can this PR not be introduced

rust_plan() had no in-tree caller and pulled in the optional pypaimon_rust
dependency, so it was dead code. Per review, remove the bridge (and its PK-guard
test) and keep only the self-contained, byte-exact SplitSerializer v1
deserializer. The bridge/consumer (rust_read) will land together with its
actual caller.
@XiaoHongbo-Hope XiaoHongbo-Hope marked this pull request as draft July 9, 2026 13:48
Wire the rust-plan bridge into TableScan.plan() behind a config switch
(scan.rust-plan.enabled, default off), so the SplitSerializer v1 deserializer
has a real caller. When on, pypaimon_rust plans the splits and the pypaimon
reader reads them. The predicate is not pushed to Rust, so partition pruning is
re-applied on the pypaimon side and the reader's row filter/limit still apply.
Primary-key tables are rejected; append tables only, experimental.
@Zouxxyy

Zouxxyy commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

@codex

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