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.