Skip to content

[python] Fix MAP key resolution and ROW projection nullability - #10054

Merged
JingsongLi merged 2 commits into
apache:masterfrom
wangzhigang1999:fix/python-projection-paths
Sep 22, 2026
Merged

JingsongLi merged 2 commits into
apache:masterfrom
wangzhigang1999:fix/python-projection-paths

Conversation

@wangzhigang1999

@wangzhigang1999 wangzhigang1999 commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Purpose

Fixes #10053.

PyPaimon can return the wrong MAP value when projecting a key that starts with a dot. For a MAP containing foo=100 and .foo=107, selecting both keys returns 100, 100. This change resolves struct children by their literal names before passing field indexes to Arrow, so the read returns 100, 107 and preserves parent NULLs.

The change also fixes two projection cases:

  • Try MAP field-name prefixes from longest to shortest until the remaining selector parses as a string key. An invalid longer prefix no longer hides a valid selector for a shorter field name.
  • Propagate nullability through ROW ancestors when building the projected schema. Copy the leaf type when widening its nullability so the source schema retains its constraints.

Exact top-level field matches and the existing ROW prefix precedence remain unchanged. The ROW path walker is extracted into a helper, and projection path annotations now include MapKey steps.

Tests

Added four regression tests, all of which fail before the fix:

  • Read dot-prefixed MAP keys alone and alongside an ordinary key, including empty and NULL maps, with Parquet and row files.
  • Read the same MAP key with either quote style when a longer field-name prefix cannot form a valid selector; retain valid longer-prefix selection and invalid-selector skipping.
  • Project a required leaf beneath a nullable ROW, then write the result to Parquet and read it back.
  • Verify ancestor and leaf nullability combinations, including unchanged source schema constraints and fully non-null paths.

The following local test scope passed: 102 passed (Python 3.12.6, PyArrow 19.0.1):

cd paimon-python
python -m pytest -q \
  pypaimon/tests/test_read_builder_nested_projection.py \
  pypaimon/tests/test_nested_projection_e2e.py \
  pypaimon/tests/map_selected_key_projection_test.py \
  pypaimon/tests/test_projection_utility.py \
  pypaimon/tests/test_outer_projection_record_reader.py \
  pypaimon/tests/projection_predicate_index_test.py

Ruff formatting checks passed for the changed ranges, Ruff lint passed for the six changed Python files, and git diff --check passed.

Flake8 4.0.1 passed for all 826 Python source files using dev/cfg.ini (Python 3.11.12). The two affected ROW/MAP selector tests also passed after the slice-formatting adjustment.

Copilot AI lite review requested due to automatic review settings September 21, 2026 08:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

No unresolved review issues remain, and regression coverage is included.

Review effort: Lite
Findings: None

What changed in this PR

Fixes nested projection handling for literal MAP keys and nullability through nullable ROW ancestors.

Changes:

  • Resolves MAP selectors using valid longest-prefix matching and literal struct-field indexes.
  • Propagates projected nullability without mutating source schemas.
  • Adds regression coverage for MAP keys, prefix handling, and nullable ROW exports.
File Description
paimon-python/​pypaimon/​utils/​projection.py Propagates projection nullability.
paimon-python/​pypaimon/​tests/​test_projection_utility.py Tests ancestor nullability.
paimon-python/​pypaimon/​tests/​test_nested_projection_e2e.py Tests nullable ROW Parquet round trips.
paimon-python/​pypaimon/​tests/​map_selected_key_projection_test.py Tests literal MAP keys and prefix resolution.
paimon-python/​pypaimon/​read/​reader/​nested_leaf_batch_reader.py Uses literal struct-field indexes.
paimon-python/​pypaimon/​read/​read_builder.py Resolves ROW and MAP projection paths.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@JingsongLi

Copy link
Copy Markdown
Contributor

Requirement fit: SUPPORTED. Literal MAP keys beginning with a dot and nullable ROW ancestors affect returned values and projected schema nullability. The patch resolves struct fields by literal index and propagates ancestor nullability without mutating the source type; regressions cover Parquet and row-file reads. Implementation: CLEAN in this diff. I did not rerun the Python suite locally.

@JingsongLi
JingsongLi merged commit 3d1caec into apache:master Sep 22, 2026
14 checks passed
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.

[Bug] PyPaimon nested projection returns incorrect MAP values and nullability

3 participants