You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add use_statistics_registry config and registry-aware join selection
Adds a pluggable statistics path for JoinSelection that uses the
StatisticsRegistry instead of each operator's built-in partition_statistics.
- Add optimizer.use_statistics_registry config flag (default=false)
- Always initialize StatisticsRegistry with built-in providers in
SessionStateBuilder, so SET via SQL takes effect without rebuild
- Override optimize_with_context in JoinSelection to pass the registry
to should_swap_join_order when the flag is enabled
- Add statistics_registry.slt demonstrating how the registry produces
more conservative join estimates for skewed data (10*10=100 cartesian
fallback vs 10*10/3=33 range-NDV estimate), triggering the correct
build-side swap that the built-in estimator misses
// TODO: We need some performance test for Right Semi/Right Join swap to Left Semi/Left Join in case that the right side is smaller but not much smaller.
57
73
// TODO: In PrestoSQL, the optimizer flips join sides only if one side is much smaller than the other by more than SIZE_DIFFERENCE_THRESHOLD times, by default is 8 times.
58
74
/// Checks whether join inputs should be swapped using available statistics.
59
75
///
60
76
/// It follows these steps:
61
-
/// 1. Compare the in-memory sizes of both sides, and place the smaller side on
77
+
/// 1. If a [`StatisticsRegistry`] is provided, use it for cross-operator estimates
78
+
/// (e.g., intermediate join outputs that would otherwise have `Absent` statistics).
79
+
/// 2. Compare the in-memory sizes of both sides, and place the smaller side on
62
80
/// the left (build) side.
63
-
/// 2. If in-memory byte sizes are unavailable, fall back to row counts.
64
-
/// 3. Do not reorder the join if neither statistic is available, or if
81
+
/// 3. If in-memory byte sizes are unavailable, fall back to row counts.
82
+
/// 4. Do not reorder the join if neither statistic is available, or if
65
83
/// `datafusion.optimizer.join_reordering` is disabled.
66
84
///
67
-
///
68
85
/// Used configurations inside arg `config`
69
86
/// - `config.optimizer.join_reordering`: allows or forbids statistics-driven join swapping
70
87
pub(crate)fnshould_swap_join_order(
71
88
left:&dynExecutionPlan,
72
89
right:&dynExecutionPlan,
73
90
config:&ConfigOptions,
91
+
registry:Option<&StatisticsRegistry>,
92
+
cache:&mutStatsCache,
74
93
) -> Result<bool>{
75
94
if !config.optimizer.join_reordering{
76
95
returnOk(false);
77
96
}
78
97
79
-
// Get the left and right table's total bytes
80
-
// If both the left and right tables contain total_byte_size statistics,
81
-
// use `total_byte_size` to determine `should_swap_join_order`, else use `num_rows`
82
-
let left_stats = left.partition_statistics(None)?;
83
-
let right_stats = right.partition_statistics(None)?;
84
-
// First compare `total_byte_size` of left and right side,
85
-
// if information in this field is insufficient fallback to the `num_rows`
98
+
let left_stats = get_stats(left, registry, cache)?;
99
+
let right_stats = get_stats(right, registry, cache)?;
100
+
101
+
// First compare total_byte_size, then fall back to num_rows if byte
@@ -458,6 +459,7 @@ datafusion.optimizer.hash_join_inlist_pushdown_max_size 131072 Maximum size in b
458
459
datafusion.optimizer.hash_join_single_partition_threshold 1048576 The maximum estimated size in bytes for one input side of a HashJoin will be collected into a single partition
459
460
datafusion.optimizer.hash_join_single_partition_threshold_rows 131072 The maximum estimated size in rows for one input side of a HashJoin will be collected into a single partition
460
461
datafusion.optimizer.join_reordering true When set to true, the physical plan optimizer may swap join inputs based on statistics. When set to false, statistics-driven join input reordering is disabled and the original join order in the query is used.
462
+
datafusion.optimizer.use_statistics_registry false When set to true, the physical plan optimizer uses the pluggable StatisticsRegistry for statistics propagation across operators. This enables more accurate cardinality estimates compared to each operator's built-in partition_statistics.
461
463
datafusion.optimizer.max_passes 3 Number of times that the optimizer will attempt to optimize the plan
462
464
datafusion.optimizer.prefer_existing_sort false When true, DataFusion will opportunistically remove sorts when the data is already sorted, (i.e. setting `preserve_order` to true on `RepartitionExec` and using `SortPreservingMergeExec`) When false, DataFusion will maximize plan parallelism using `RepartitionExec` even if this requires subsequently resorting data using a `SortExec`.
463
465
datafusion.optimizer.prefer_existing_union false When set to true, the optimizer will not attempt to convert Union to Interleave
0 commit comments