Skip to main content

sql_cli/query_plan/
mod.rs

1// Main query plan module
2mod query_plan;
3
4// Sub-modules
5pub mod correlated_subquery_analyzer;
6pub mod cte_hoister;
7pub mod dependency_analyzer;
8pub mod expression_lifter;
9pub mod group_by_alias_expander;
10pub mod having_alias_transformer;
11pub mod ilike_to_like_transformer;
12pub mod in_operator_lifter;
13pub mod into_clause_remover;
14pub mod order_by_alias_transformer;
15pub mod pipeline;
16pub mod pivot_expander;
17pub mod qualify_to_where_transformer;
18pub mod transformer_adapters;
19pub mod where_alias_expander;
20
21// Re-export main types
22pub use query_plan::{
23    DependencyGraph, PlanMetadata, QueryAnalyzer, QueryPlan, WorkUnit, WorkUnitExpression,
24    WorkUnitType,
25};
26
27// Re-export commonly used items
28pub use correlated_subquery_analyzer::{
29    CorrelatedSubqueryAnalyzer, CorrelationAnalysis, SubqueryInfo, SubqueryLocation, SubqueryType,
30};
31pub use cte_hoister::CTEHoister;
32pub use dependency_analyzer::{ScriptDependencyGraph, StatementNode};
33pub use expression_lifter::{ExpressionLifter, LiftableExpression};
34pub use group_by_alias_expander::GroupByAliasExpander;
35pub use having_alias_transformer::HavingAliasTransformer;
36pub use ilike_to_like_transformer::ILikeToLikeTransformer;
37pub use in_operator_lifter::{InOperatorLifter, LiftedInExpression};
38pub use into_clause_remover::IntoClauseRemover;
39pub use order_by_alias_transformer::OrderByAliasTransformer;
40pub use pivot_expander::PivotExpander;
41pub use qualify_to_where_transformer::QualifyToWhereTransformer;
42pub use where_alias_expander::WhereAliasExpander;
43
44// Re-export pipeline types
45pub use pipeline::{
46    ASTTransformer, PipelineBuilder, PipelineConfig, PreprocessingPipeline, PreprocessingStats,
47    TransformStats,
48};
49
50// Re-export transformer adapters
51pub use transformer_adapters::{
52    CTEHoisterTransformer, ExpressionLifterTransformer, InOperatorLifterTransformer,
53};
54
55/// Configuration for selective transformer enabling/disabling
56#[derive(Clone, Debug)]
57pub struct TransformerConfig {
58    pub enable_pivot_expander: bool,
59    pub enable_expression_lifter: bool,
60    pub enable_where_expansion: bool,
61    pub enable_group_by_expansion: bool,
62    pub enable_having_expansion: bool,
63    pub enable_order_by_expansion: bool,
64    pub enable_qualify_to_where: bool,
65    pub enable_ilike_to_like: bool,
66    pub enable_cte_hoister: bool,
67    pub enable_in_lifter: bool,
68}
69
70impl Default for TransformerConfig {
71    fn default() -> Self {
72        // By default, all transformers are enabled for compatibility
73        Self::all_enabled()
74    }
75}
76
77impl TransformerConfig {
78    /// Create a config with all transformers enabled
79    pub fn all_enabled() -> Self {
80        Self {
81            enable_pivot_expander: true,
82            enable_expression_lifter: true,
83            enable_where_expansion: true,
84            enable_group_by_expansion: true,
85            enable_having_expansion: true,
86            enable_order_by_expansion: true,
87            enable_qualify_to_where: true,
88            enable_ilike_to_like: true,
89            enable_cte_hoister: true,
90            enable_in_lifter: true,
91        }
92    }
93}
94
95/// Create a preprocessing pipeline with configurable transformers
96///
97/// # Arguments
98/// * `verbose` - Whether to enable verbose logging
99/// * `transformer_config` - Configuration for which transformers to enable
100///
101/// # Example
102/// ```ignore
103/// let config = TransformerConfig::all_enabled();
104/// let mut pipeline = create_pipeline_with_config(false, config);
105/// let transformed = pipeline.process(statement)?;
106/// ```
107pub fn create_pipeline_with_config(
108    verbose: bool,
109    show_sql_transformations: bool,
110    transformer_config: TransformerConfig,
111) -> PreprocessingPipeline {
112    let config = if verbose || show_sql_transformations {
113        PipelineConfig {
114            enabled: true,
115            verbose_logging: verbose,
116            collect_stats: true,
117            debug_ast_changes: false,
118            show_sql_transformations,
119        }
120    } else {
121        PipelineConfig::default()
122    };
123
124    let mut builder = PipelineBuilder::with_config(config);
125
126    // Add transformers in the correct order based on configuration
127    // Order matters!
128    // 1. PivotExpander must run FIRST to expand PIVOT into standard SQL
129    // 2. ExpressionLifter must run before CTEHoister
130    // 3. WhereAliasExpander and GroupByAliasExpander run early to expand aliases
131    // 4. HavingAliasTransformer runs after GROUP BY to ensure proper aggregate aliases
132
133    // PIVOT expansion must happen first, before any other transformations
134    if transformer_config.enable_pivot_expander {
135        builder = builder.with_transformer(Box::new(PivotExpander));
136    }
137
138    if transformer_config.enable_expression_lifter {
139        builder = builder.with_transformer(Box::new(ExpressionLifterTransformer::new()));
140    }
141
142    // QualifyToWhereTransformer must run after ExpressionLifter
143    // so that window functions are already lifted to CTEs
144    if transformer_config.enable_qualify_to_where {
145        builder = builder.with_transformer(Box::new(QualifyToWhereTransformer::new()));
146    }
147
148    // ILikeToLikeTransformer runs early before WHERE expansion
149    // to transform ILIKE operators before WHERE clause processing
150    if transformer_config.enable_ilike_to_like {
151        builder = builder.with_transformer(Box::new(ILikeToLikeTransformer::new()));
152    }
153
154    if transformer_config.enable_where_expansion {
155        builder = builder.with_transformer(Box::new(WhereAliasExpander::new()));
156    }
157
158    if transformer_config.enable_group_by_expansion {
159        builder = builder.with_transformer(Box::new(GroupByAliasExpander::new()));
160    }
161
162    if transformer_config.enable_having_expansion {
163        builder = builder.with_transformer(Box::new(HavingAliasTransformer::new()));
164    }
165
166    if transformer_config.enable_order_by_expansion {
167        builder = builder.with_transformer(Box::new(OrderByAliasTransformer::new()));
168    }
169
170    if transformer_config.enable_cte_hoister {
171        builder = builder.with_transformer(Box::new(CTEHoisterTransformer::new()));
172    }
173
174    if transformer_config.enable_in_lifter {
175        builder = builder.with_transformer(Box::new(InOperatorLifterTransformer::new()));
176    }
177
178    builder.build()
179}
180
181/// Create a standard preprocessing pipeline with all default transformers
182///
183/// The transformers are applied in this order:
184/// 1. ExpressionLifter - Lifts column alias dependencies and window functions
185/// 2. WhereAliasExpander - Expands SELECT aliases in WHERE clauses
186/// 3. GroupByAliasExpander - Expands SELECT aliases in GROUP BY clauses
187/// 4. HavingAliasTransformer - Adds aliases to aggregates and rewrites HAVING
188/// 5. CTEHoister - Hoists nested CTEs to top level
189/// 6. InOperatorLifter - Optimizes large IN expressions
190///
191/// # Arguments
192/// * `verbose` - Whether to enable verbose logging
193///
194/// # Example
195/// ```ignore
196/// let mut pipeline = create_standard_pipeline(false);
197/// let transformed = pipeline.process(statement)?;
198/// ```
199pub fn create_standard_pipeline(verbose: bool) -> PreprocessingPipeline {
200    let config = if verbose {
201        PipelineConfig {
202            enabled: true,
203            verbose_logging: true,
204            collect_stats: true,
205            debug_ast_changes: false,
206            show_sql_transformations: false,
207        }
208    } else {
209        PipelineConfig::default()
210    };
211
212    let mut builder = PipelineBuilder::with_config(config);
213
214    // Add transformers in the correct order
215    // Order matters!
216    // 1. PivotExpander MUST run FIRST to expand PIVOT into standard SQL
217    // 2. ExpressionLifter must run before CTEHoister
218    // 3. ILikeToLikeTransformer runs early to convert ILIKE before other processing
219    // 4. WhereAliasExpander and GroupByAliasExpander run early to expand aliases
220    // 5. HavingAliasTransformer and OrderByAliasTransformer run after GROUP BY
221    builder = builder
222        .with_transformer(Box::new(PivotExpander))
223        .with_transformer(Box::new(ExpressionLifterTransformer::new()))
224        .with_transformer(Box::new(ILikeToLikeTransformer::new()))
225        .with_transformer(Box::new(WhereAliasExpander::new()))
226        .with_transformer(Box::new(GroupByAliasExpander::new()))
227        .with_transformer(Box::new(HavingAliasTransformer::new()))
228        .with_transformer(Box::new(OrderByAliasTransformer::new()))
229        .with_transformer(Box::new(CTEHoisterTransformer::new()))
230        .with_transformer(Box::new(InOperatorLifterTransformer::new()));
231
232    builder.build()
233}