Skip to main content

uqa_sql/binding/
composite_rename.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Rename stored field selectors by their scoped composite identity, without evaluating inputs.
8
9use super::{BindingContext, SchemaScope};
10use crate::{
11    ast::{CreateFunction, Expr, FunctionBinding, Statement},
12    plan::{ExpressionPlan, QueryPlan, UnifiedPlan},
13    routines::RoutineResolution,
14    RowSchema, SQLError, SQLParam, ScalarExpr,
15};
16use uqa_core::Value;
17
18pub struct CompositeFieldRename<'a> {
19    pub target: u32,
20    pub number: i16,
21    pub to: &'a str,
22    pub routines: &'a dyn RoutineResolution,
23    pub binding: BindingContext<'a>,
24}
25
26#[cfg(test)]
27mod tests;
28
29impl CompositeFieldRename<'_> {
30    pub fn relation_schema(&self, name: &str) -> Result<RowSchema, SQLError> {
31        let (_, qualifier) =
32            uqa_core::RelationIdentity::parse_reference(name).map_err(SQLError::Internal)?;
33        super::sources::analyze_source_plan_schema(
34            self.routines,
35            &crate::plan::SourcePlan::Table {
36                name: name.to_owned(),
37                qualifier,
38                alias: None,
39                column_aliases: Vec::new(),
40                include_descendants: true,
41                bound_columns: None,
42            },
43            &[],
44            &self.binding,
45            None,
46        )
47    }
48
49    pub fn transition_schema(&self, name: &str) -> Result<RowSchema, SQLError> {
50        let schema = self.relation_schema(name)?;
51        Ok(RowSchema::join(
52            &RowSchema::with_relation_qualifier(&schema, "old"),
53            &RowSchema::with_relation_qualifier(&schema, "new"),
54            [],
55        ))
56    }
57    fn scope(&self) -> Result<SchemaScope, SQLError> {
58        let mut scope = SchemaScope::for_analysis(&self.binding)?;
59        scope.scalar_binding = super::ScalarBindingMode::CompositeInputs;
60        scope.preserve_syntax_shape = true;
61        Ok(scope)
62    }
63
64    fn selected(&self, binding: Option<&FunctionBinding>) -> bool {
65        binding
66            .and_then(|binding| binding.composite_field.as_ref())
67            .is_some_and(|field| field.type_oid == self.target && field.number == self.number)
68    }
69
70    fn syntax_node(&self, node: &mut Expr) -> bool {
71        if let Expr::Func { binding, args, .. } = node {
72            if self.selected(binding.as_ref()) {
73                if let Some(Expr::Literal(Value::Str(name))) = args.get_mut(1) {
74                    if name != self.to {
75                        self.to.clone_into(name);
76                        return true;
77                    }
78                }
79            }
80        }
81        false
82    }
83
84    fn scalar_node(&self, node: &mut ScalarExpr) -> bool {
85        if let ScalarExpr::Func { binding, args, .. } = node {
86            if self.selected(binding.as_ref()) {
87                if let Some(ScalarExpr::Literal(Value::Str(name))) = args.get_mut(1) {
88                    if name != self.to {
89                        self.to.clone_into(name);
90                        return true;
91                    }
92                }
93            }
94        }
95        false
96    }
97
98    pub fn expression(&self, expression: &mut Expr, schema: &RowSchema) -> Result<bool, SQLError> {
99        let lowered = ExpressionPlan::lower(expression.clone());
100        let mut plan = lowered.clone();
101        self.bind_expression_plan(&mut plan, schema)?;
102        let sites = super::syntax_sites::expression_syntax_sites(&lowered, &plan)?;
103        let mut next = expression.clone();
104        crate::catalog::stored_ast::bind_stored_expression_sites(&mut next, &sites)?;
105        let mut changed = false;
106        crate::catalog::stored_ast::visit_stored_expression(&mut next, &mut |node| {
107            changed |= self.syntax_node(node);
108            Ok(())
109        })?;
110        if changed {
111            *expression = next;
112        }
113        Ok(changed)
114    }
115
116    fn bind_expression_plan(
117        &self,
118        plan: &mut ExpressionPlan,
119        schema: &RowSchema,
120    ) -> Result<(), SQLError> {
121        let mut scope = self.scope()?;
122        scope.stored_expression_outer = Some(schema.clone());
123        for query in &mut plan.subqueries {
124            scope.bind_query_routines_for_storage(self.routines, query, &[], Some(schema))?;
125        }
126        scope.bind_scalar_routines_for_storage(
127            self.routines,
128            &mut plan.scalar,
129            schema,
130            &plan.subqueries,
131            &[],
132        )
133    }
134
135    pub fn expression_plan(
136        &self,
137        plan: &mut ExpressionPlan,
138        schema: &RowSchema,
139    ) -> Result<bool, SQLError> {
140        let mut next = plan.clone();
141        self.bind_expression_plan(&mut next, schema)?;
142        let mut changed = false;
143        crate::plan::rewrite_scalar_expression(&mut next.scalar, &mut |node| {
144            changed |= self.scalar_node(node);
145        });
146        for query in &mut next.subqueries {
147            query.rewrite_scalar_expressions(&mut |node| changed |= self.scalar_node(node));
148        }
149        if changed {
150            *plan = next;
151        }
152        Ok(changed)
153    }
154
155    pub fn query(&self, query: &mut QueryPlan) -> Result<bool, SQLError> {
156        let mut next = query.clone();
157        self.scope()?
158            .bind_query_routines_for_storage(self.routines, &mut next, &[], None)?;
159        let mut changed = false;
160        next.rewrite_scalar_expressions(&mut |node| changed |= self.scalar_node(node));
161        if changed {
162            *query = next;
163        }
164        Ok(changed)
165    }
166
167    pub fn statement(
168        &self,
169        statement: &mut Statement,
170        definition: Option<&CreateFunction>,
171        outer: Option<&RowSchema>,
172    ) -> Result<bool, SQLError> {
173        let mut plan = UnifiedPlan::lower_with(statement.clone(), &|name: &str| {
174            self.routines.has_registered_aggregate_function(name)
175        });
176        super::stored_routines::mark_catalog_statement_relations_bound(&mut plan)?;
177        let params = if let Some(definition) = definition {
178            let params = self.routine_params(definition)?;
179            let scope =
180                crate::routines::body_parameters::sql_body_parameter_scope(definition, &params)?;
181            super::bind_routine_parameter_references(
182                self.routines,
183                &mut plan,
184                &params,
185                &self.binding,
186                &scope,
187            )?;
188            params
189        } else {
190            Vec::new()
191        };
192        let bound = super::stored_routines::bind_catalog_statement_composite_inputs(
193            &super::stored_routines::CatalogRoutineContext {
194                routines: self.routines,
195                binding: &self.binding,
196            },
197            &plan,
198            &params,
199            outer,
200        )?;
201        let mut next = statement.clone();
202        crate::catalog::stored_ast::bind_stored_statement_sites(&mut next, &bound.sites)?;
203        let mut changed = false;
204        crate::catalog::stored_ast::visit_stored_statement_expressions(&mut next, &mut |node| {
205            changed |= self.syntax_node(node);
206            Ok(())
207        })?;
208        if changed {
209            *statement = next;
210        }
211        Ok(changed)
212    }
213
214    pub fn bound_plan(&self, plan: &mut UnifiedPlan) -> bool {
215        let mut changed = false;
216        plan.rewrite_scalar_expressions(&mut |node| changed |= self.scalar_node(node));
217        changed
218    }
219
220    pub fn bind_routine_plan(
221        &self,
222        plan: &mut UnifiedPlan,
223        definition: &CreateFunction,
224    ) -> Result<(), SQLError> {
225        let params = self.routine_params(definition)?;
226        let parameters =
227            crate::routines::body_parameters::sql_body_parameter_scope(definition, &params)?;
228        let mut scope = self.scope()?;
229        scope.routine_parameters = Some(parameters.clone());
230        scope.bind_statement_parameters(self.routines, plan, &params, Some(parameters.schema()))
231    }
232
233    fn routine_params(&self, definition: &CreateFunction) -> Result<Vec<SQLParam>, SQLError> {
234        crate::routines::body_parameters::sql_body_parameters(definition)
235            .into_iter()
236            .map(|parameter| {
237                self.routines
238                    .resolve_type_name(&parameter.type_name)
239                    .map(|ty| {
240                        ty.map_or_else(
241                            || SQLParam::scalar(Value::Null),
242                            |ty| SQLParam::typed_scalar(Value::Null, ty),
243                        )
244                    })
245            })
246            .collect()
247    }
248}