Skip to content

feat(table): read the registered partitions of a catalog-managed format table - #814

Merged
JingsongLi merged 3 commits into
apache:mainfrom
sundapeng:feat/format-table-catalog-managed-scan
Sep 11, 2026
Merged

JingsongLi merged 3 commits into
apache:mainfrom
sundapeng:feat/format-table-catalog-managed-scan

Conversation

@sundapeng

@sundapeng sundapeng commented Sep 11, 2026 •

Copy link
Copy Markdown
Member

Purpose

Split out of #591 so it can be reviewed on its own. A Format Table loaded from a REST catalog with metastore.partitioned-table=true has its partitions managed by the catalog: the registrations, not the directory tree, decide what a scan reads. This PR makes the Rust scan read exactly those partitions. The SQL statements that administer them (SHOW / ADD / DROP PARTITION, MSCK REPAIR TABLE) and ANALYZE TABLE follow in later PRs.

Its prerequisites #811, #813 and #812 have all merged, so this PR now contains only its own two commits.

Brief change log

  • RESTEnv keeps the Format Table partition settings the catalog returned when the table was loaded: table path, file format and path layout. Loading an external table or an engine-implemented table with catalog-managed partitions fails. Dynamic options cannot switch the partition source, the path layout or the implementation of such a table. Table::has_catalog_managed_partitions reports whether a table is one.
  • The Format Table scan lists the registered partitions instead of directories:
    • it sends a partition-name pattern built only from leading CHAR/VARCHAR equalities, whose values have one spelling, so month = 1 never filters out a partition registered as month=01;
    • it sends the partition predicate through list-by-filter (Predicate::to_rest_json); an AND keeps the conjuncts that have a wire form, anything else goes whole or not at all;
    • a catalog answering 501 for the filter is asked again by pattern; the directory tree is never used;
    • registered values accept the Java boolean spellings, and malformed registrations fail with the table named;
    • a partition registered at a custom location (options.path, [api][core][spark] Support custom partition locations paimon#9540) fails the scan instead of reading the table directory in its place.
  • RESTCatalog::list_partitions and list_partitions_paged return the 501 error for a table with catalog-managed partitions instead of falling back to a file system listing. list_partitions_by_names falls back to list_partitions, so it inherits that.
  • Partition values and path names are formatted by shared helpers. Path names are escaped as Java and PartitionComputer escape them, so a non-ASCII value in a leading-equality or value-only path is no longer percent-encoded.

Tests

  • format_partition::tests: value parsing and formatting, Java boolean spellings, partition names and relative paths per layout, name-pattern construction and refusals.
  • format_table_scan::tests: what partition_filter_json sends, and leading-equality path escaping.
  • core_options::tests, partition_utils::tests: the strict option readers and unescape_path_name.
  • rest_catalog_test.rs: loading and rejecting catalog-managed tables, dynamic option validation, a scan that reads each registered partition once and a missing registered directory as empty, a listing error and malformed metadata failing the scan, and the 501 policy of the catalog listings.
  • New crates/integrations/datafusion/tests/rest_format_table_scan.rs, with partitions registered through the catalog API: only registered partitions are read, the pattern and list-by-filter request contents, the 501 retry by pattern, registrations spelled unlike the filter, filters on partition columns, the custom-location refusal, and a catalog that cannot list partitions failing the query.

Commands run on the top commit:

cargo fmt --all -- --check
cargo clippy --locked --all-targets --workspace --features fulltext,vortex -- -D warnings
cargo test --locked -p paimon --all-targets --features fulltext,vortex
cargo test --locked --no-fail-fast -p paimon-datafusion --all-targets
cargo test --locked -p paimon-rest-server --all-targets

fmt, clippy, paimon (2937 tests) and paimon-rest-server (10) pass. Locally, 39 paimon-datafusion tests fail because they read fixture tables provisioned by make docker-up (two also need the lumina native library); the same 39 fail by name on main in this environment, and everything else passes, including the new scan tests.

API and Format

  • New public method Table::has_catalog_managed_partitions.
  • RESTCatalog partition listings no longer fall back to the file system for tables with catalog-managed partitions.
  • paimon-datafusion gains axum as a dev-dependency for the shared mock REST server.

The storage format is unchanged.

Documentation

None yet: the SQL documentation for catalog-managed Format Table partitions comes with the partition commands PR.

…at table

Split out of apache#591. A Format Table loaded from a REST catalog with
metastore.partitioned-table=true has its partitions managed by the
catalog: the registrations, not the directory tree, decide what a scan
reads.

- RESTEnv keeps the partition settings the catalog returned when the
  table was loaded (table path, file format, path layout), and loading
  an external or engine-implemented table with managed partitions
  fails. Dynamic options cannot switch the partition source, the path
  layout or the implementation of such a table.
- The format table scan lists the registered partitions instead of
  directories. It sends the catalog a partition-name pattern built only
  from leading CHAR/VARCHAR equalities, whose values have one spelling,
  and the partition predicate through list-by-filter, keeping the
  conjuncts of an AND that have a wire form. A catalog answering 501 for
  the filter is asked by pattern; the directory tree is never used.
  Registered values accept the Java boolean spellings, and a partition
  registered at a custom location fails the scan instead of reading the
  table directory in its place.
- RESTCatalog no longer falls back to a file system listing for a table
  with catalog-managed partitions when the partition endpoints answer
  501.
- Partition values and path names go through shared helpers, which
  escape path names the way Java and PartitionComputer do: a non-ASCII
  value in a leading-equality or value-only path is no longer
  percent-encoded.
Covers SELECT over a Format Table whose partitions a mock REST catalog
manages, with the partitions registered through the catalog API: only
registered partitions are read, the partition-name pattern and the
list-by-filter request carry what the scan pushes down, a 501 from
list-by-filter is retried by pattern, registrations spelled unlike the
filter are still read, filters on partition columns see the registered
value, a partition at a custom location fails the query, and a catalog
that cannot list partitions fails it instead of letting it read
directories.

The tests share the paimon crate's mock REST server, which brings axum
in as a dev-dependency.
@sundapeng
sundapeng force-pushed the feat/format-table-catalog-managed-scan branch from 079ee84 to 601f175 Compare September 11, 2026 06:32
@sundapeng
sundapeng marked this pull request as ready for review September 11, 2026 06:33
Comment thread crates/paimon/src/table/rest_env.rs Outdated
Comment thread crates/paimon/src/spec/core_options.rs Outdated
…able options

RESTEnv kept a copy of the Format Table partition settings the catalog
returned at load time, so that dynamic options could not switch where
partitions come from or how their paths are laid out. Java keeps no such
copy: FormatTable.copy compares the persisted options with the merged
ones and refuses the change.

Do the same. RESTEnv goes back to being the REST commit context.
Table::has_catalog_managed_partitions and the scan read the settings from
the table options, and copy_with_time_travel refuses dynamic options that
change metastore.partitioned-table or, for a table with catalog-managed
partitions, its path layout, engine implementation, type, path or file
format. Java fixes the last three when the table is loaded; this table
reads them from its options, so they are guarded here.

The strict option readers and their enum go with it: the two options
still consulted are read like the neighbouring CoreOptions readers.

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

+1

@JingsongLi
JingsongLi merged commit 6824487 into apache:main Sep 11, 2026
14 checks passed
jerry-024 added a commit to jerry-024/paimon-rust that referenced this pull request Sep 14, 2026
…em-table

* upstream/main:
  feat(datafusion): support ANALYZE TABLE on catalog-managed format tables (apache#815)
  refactor(table): unify vector search scan and read APIs (apache#822)
  fix(rest): sign the query string the client actually sends (apache#821)
  feat(datafusion): add MSCK REPAIR TABLE for catalog-managed format tables (apache#817)
  feat(table): execute a primary-key vector search over engine-planned splits (apache#771)
  feat(datafusion): add SHOW, ADD and DROP PARTITION for catalog-managed format tables (apache#816)
  feat(table): read the registered partitions of a catalog-managed format table (apache#814)
  fix: report unsupported time travel instead of returning current data (apache#753)
  fix(file_index): skip pruning for narrowing integer schema changes (apache#806)
  fix(c): align append vector filter with core behavior (apache#793)
  fix(table): skip staging files and list format table partitions concurrently (apache#813)
  feat(rest): add partition registration, lookup and statistics APIs (apache#812)
  fix(datafusion): keep format table partition columns out of decoder filters (apache#811)
  fix(read): null-fill nested fields a data file predates (apache#805)
  fix(scan): prune NOT IN predicates using file stats (apache#795)
  fix(spec): accept the legacy first_not_null_value aggregate name (apache#794)
  fix(table): check batch arity instead of asserting it in debug builds (apache#801)
  fix(vindex): bound a deletion-vector position by its own source file (apache#802)
  fix(lumina): decide on the index size before parsing the metric (apache#803)

# Conflicts:
#	crates/integrations/datafusion/src/physical_plan/scan.rs
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.

2 participants