vibesql_executor/select/join/search/
config.rs

1//! Configuration for join order search
2
3#![allow(clippy::unnecessary_literal_unwrap)]
4
5/// Configuration for parallel join order search
6#[derive(Debug, Clone)]
7pub struct ParallelSearchConfig {
8    /// Enable parallel BFS search (vs sequential DFS)
9    pub enabled: bool,
10    /// Maximum depth to explore with parallel BFS (tables with >max_depth use DFS)
11    pub max_depth: usize,
12    /// Maximum states per layer before pruning
13    pub max_states_per_layer: usize,
14    /// Prune states with cost > best * threshold
15    pub pruning_threshold: f64,
16    /// Maximum time budget for join order search (milliseconds)
17    /// Default: 1000ms for OLAP workloads
18    pub time_budget_ms: u64,
19    /// Whether to use time-bounded search (vs table-count cutoff)
20    pub use_time_budget: bool,
21    /// Enable verbose logging of search statistics
22    pub verbose: bool,
23}
24
25impl Default for ParallelSearchConfig {
26    fn default() -> Self {
27        Self::with_table_count(4) // Default assumes 4 tables
28    }
29}
30
31impl ParallelSearchConfig {
32    /// Create a config with adaptive time budget based on table count
33    ///
34    /// The time budget increases with query complexity:
35    /// - 1-3 tables: 500ms (simple queries)
36    /// - 4-5 tables: 1000ms (typical OLAP queries)
37    /// - 6-7 tables: 1500ms (complex multi-way joins like Q7)
38    /// - 8+ tables: 2000ms (very complex queries like Q21)
39    ///
40    /// This adaptive approach gives more time to complex queries that need it
41    /// while keeping simple queries fast.
42    pub fn with_table_count(num_tables: usize) -> Self {
43        // Read time budget from environment variable if set (overrides adaptive)
44        let time_budget_ms = std::env::var("JOIN_REORDER_TIME_BUDGET_MS")
45            .ok()
46            .and_then(|s| s.parse().ok())
47            .unwrap_or({
48                // Adaptive budget based on table count
49                match num_tables {
50                    0..=3 => 500,  // Simple queries
51                    4..=5 => 1000, // Typical OLAP
52                    6..=7 => 1500, // Complex multi-way joins (Q7)
53                    _ => 2000,     // Very complex (Q21)
54                }
55            });
56
57        let verbose = std::env::var("JOIN_REORDER_VERBOSE").is_ok();
58
59        Self {
60            enabled: true,
61            max_depth: 8, // Support 8-way joins like TPC-H Q8
62            max_states_per_layer: 1000,
63            pruning_threshold: 1.5,
64            time_budget_ms,
65            use_time_budget: true, // New: prefer time-bounded over table-count
66            verbose,
67        }
68    }
69}