Skip to main content

register_parquet_table

Function register_parquet_table 

Source
pub async fn register_parquet_table(
    ctx: &SessionContext,
    spec: &ParquetTableSpec,
) -> SqlResult<()>
Expand description

Register one parquet table, attaching its declared key as a DataFusion constraint so the optimizer’s functional-dependency machinery can see it.

With no declared key this is exactly register_parquet. With one, the table is built explicitly so [ListingTable::with_constraints] can be applied — register_parquet has no way to pass them.

A declared column that is not in the file’s schema is an error rather than a silent no-op: a typo would otherwise turn into “the optimization mysteriously does not apply”, which is the least debuggable outcome.

§Why the options come from ParquetReadOptions instead of ListingOptions::new

The two branches below must describe the same table; the only intended difference is the constraint. Building the keyed branch’s options by hand silently made them differ, because ListingOptions::new does not mean “defaults” — it means:

collect_stat:      false   // vs. session `collect_statistics`, default true
target_partitions: 1       // vs. session `target_partitions`

register_parquet reaches ListingOptions through [ReadOptions::to_listing_options], which ends in .with_session_config_options(config) and sets both from the session. The hand-built branch never did, so declaring a primary key turned that table’s statistics off and collapsed its scan to one partition.

Statistics off is not a slow path, it is a blind one. Every rule that keys on a known size stops seeing anything: measured on the SF100 cluster, SpillableJoinSelection reported unmeasurable == hash_joins in all 414 passes across coordinator and executors and converted zero joins, so no oversized hash join could ever be made spillable and q21 died on a build side nothing was left to catch. It also blinds broadcast selection, ShuffleReadExec’s cut-subtree estimate, and join ordering.

So the options are produced by DataFusion’s own conversion, from the same ParquetReadOptions::default() the unkeyed branch passes. The two paths cannot drift again without DataFusion changing under both at once.