Skip to main content

polyglot_sql/optimizer/
mod.rs

1//! SQL Optimizer Module
2//!
3//! This module contains optimization passes for SQL AST transformation,
4//! including type annotation, column qualification, and other semantic analysis.
5
6/// Type annotation and coercion for SQL expressions
7pub mod annotate_types;
8/// Canonicalization of SQL expressions into standard form
9pub mod canonicalize;
10/// Elimination of unused Common Table Expressions
11pub mod eliminate_ctes;
12/// Elimination of unused JOIN operations
13pub mod eliminate_joins;
14/// Isolation of table references into subqueries
15pub mod isolate_table_selects;
16/// Boolean expression normalization (CNF/DNF)
17pub mod normalize;
18/// Identifier case and quoting normalization
19pub mod normalize_identifiers;
20/// JOIN reordering and cross-join optimization
21pub mod optimize_joins;
22/// Main optimizer entry point and rule orchestration
23pub mod optimizer;
24/// Predicate pushdown into subqueries and JOINs
25pub mod pushdown_predicates;
26/// Projection pushdown to eliminate unused columns
27pub mod pushdown_projections;
28/// Column qualification and star expansion
29pub mod qualify_columns;
30/// Table reference qualification with catalog and schema
31pub mod qualify_tables;
32/// Internal relation-output typing, separate from scalar expression coercion.
33pub(crate) mod set_operation_types;
34/// Boolean and algebraic expression simplification
35pub mod simplify;
36/// Subquery merging and unnesting
37pub mod subquery;
38
39/// Type annotation, type coercion classes, and the type annotator engine
40pub use annotate_types::{annotate_types, TypeAnnotator, TypeCoercionClass};
41/// Canonicalization of SQL expressions
42pub use canonicalize::canonicalize;
43/// CTE elimination and reference checking
44pub use eliminate_ctes::{eliminate_ctes, is_cte_referenced};
45/// JOIN elimination for unused joins
46pub use eliminate_joins::eliminate_joins;
47/// Table select isolation into subqueries
48pub use isolate_table_selects::isolate_table_selects;
49/// Boolean normalization, distance computation, and related utilities
50pub use normalize::{
51    normalization_distance, normalize, normalized, NormalizeError, DEFAULT_MAX_DISTANCE,
52};
53/// Identifier normalization, case sensitivity detection, and strategy types
54pub use normalize_identifiers::{
55    get_normalization_strategy, is_case_sensitive, normalize_identifier, normalize_identifiers,
56    NormalizationStrategy,
57};
58/// JOIN optimization, reordering, normalization, and reorderability checks
59pub use optimize_joins::{is_reorderable, normalize_joins, optimize_joins, reorder_joins};
60/// Full optimizer pipeline, rule configuration, and quick optimization
61pub use optimizer::{
62    optimize, optimize_with_rules, quick_optimize, OptimizationRule, OptimizerConfig, DEFAULT_RULES,
63};
64/// Predicate pushdown and alias replacement
65pub use pushdown_predicates::{pushdown_predicates, replace_aliases};
66/// Projection pushdown and default column selection
67pub use pushdown_projections::{default_selection, pushdown_projections};
68/// Column qualification, validation, output handling, and identifier quoting
69pub use qualify_columns::{
70    qualify_columns, qualify_outputs, quote_identifiers, validate_qualify_columns,
71    QualifyColumnsError, QualifyColumnsOptions,
72};
73/// Table qualification with catalog and schema defaults
74pub use qualify_tables::{qualify_tables, QualifyTablesOptions};
75/// Expression simplification, constant evaluation, and truthiness checks
76pub use simplify::{always_false, always_true, is_false, is_null, is_zero, simplify, Simplifier};
77/// Subquery merging, unnesting, and correlation analysis
78pub use subquery::{is_correlated, is_mergeable, merge_subqueries, unnest_subqueries};
79
80/// Structural identity for optimizer keys, independent of source metadata.
81/// Unlike generated SQL this also works in builds without the `generate` feature.
82fn structural_identity_key(expr: &crate::expressions::Expression) -> String {
83    fn remove_spans(value: &mut serde_json::Value) {
84        match value {
85            serde_json::Value::Object(fields) => {
86                fields.remove("span");
87                for value in fields.values_mut() {
88                    remove_spans(value);
89                }
90            }
91            serde_json::Value::Array(values) => {
92                for value in values {
93                    remove_spans(value);
94                }
95            }
96            _ => {}
97        }
98    }
99    let mut value = serde_json::to_value(expr).expect("expression serializes");
100    remove_spans(&mut value);
101    value.to_string()
102}
103
104/// Qualify a schema-aware expression without changing physical table aliases.
105///
106/// Anonymous derived tables need stable aliases before column qualification so
107/// their projected columns can be registered in the scoped schema. Physical
108/// table names and set-operation structure are otherwise left unchanged.
109pub(crate) fn qualify_schema_aware_expression(
110    expression: crate::expressions::Expression,
111    schema: &dyn crate::schema::Schema,
112    dialect: Option<crate::dialects::DialectType>,
113) -> Result<crate::expressions::Expression, QualifyColumnsError> {
114    let effective_dialect = dialect.or_else(|| schema.dialect());
115    let mut table_options = QualifyTablesOptions::new()
116        .with_alias_unaliased_tables(false)
117        .with_alias_unaliased_subqueries(true)
118        .with_normalize_set_operation_subqueries(false);
119    let mut column_options = QualifyColumnsOptions::new().with_allow_partial(true);
120
121    if let Some(dialect) = effective_dialect {
122        table_options = table_options.with_dialect(dialect);
123        column_options = column_options.with_dialect(dialect);
124    }
125
126    let expression = qualify_tables(expression, &table_options);
127    qualify_columns(expression, schema, &column_options)
128}