Skip to main content

uqa_sql/semantics/rules/
action_binding.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Query-scope-aware binding for rewrite-rule OLD/NEW pseudo-relations.
8
9use std::collections::{BTreeMap, BTreeSet};
10
11use crate::ast::{
12    Expr, FrameBound, FromClause, OrderBy, Projection, RuleEvent, SelectStmt, Statement,
13    UpdateStmt, CTE,
14};
15use crate::plpgsql::{bind_statement, ResolvedVariable, VariableResolver};
16use crate::SQLError;
17
18mod insert;
19use insert::bind_insert;
20mod namespace;
21mod references;
22mod returning;
23mod row_expansion;
24mod scope;
25pub use references::{
26    first_rule_row_reference_in_expr, first_rule_row_reference_in_select,
27    first_rule_row_reference_in_statement, rule_action_has_set_operation,
28    rule_condition_plan_references_whole_row, rule_condition_plan_row_columns,
29    rule_expr_references_row, rule_expr_references_whole_row, rule_expr_row_columns,
30    rule_new_row_columns, rule_statement_references_row, rule_statement_references_whole_row,
31    rule_statement_row_columns,
32};
33pub use returning::expand_rule_action_returning_stars;
34pub use row_expansion::expand_rule_action_row_stars;
35use scope::{
36    apply_positional_aliases, collect_visible_scope, cte_output_columns, RuleBindingScope,
37};
38
39pub fn action_target_qualifier_referenced(
40    catalog: &dyn RuleSourceCatalog,
41    action: &Statement,
42    qualifier: &str,
43) -> bool {
44    namespace::action_target_qualifier_referenced(catalog, action, qualifier)
45}
46
47mod context;
48use context::RuleBindingContext;
49pub use context::{rule_action_target_columns, rule_action_target_row_type, RuleSourceCatalog};
50
51/// Bind an action's event-row OLD/NEW references while preserving the action
52/// row-image aliases visible to its DML RETURNING clause.
53pub fn bind_rule_action(
54    catalog: &dyn RuleSourceCatalog,
55    action: &Statement,
56    action_columns: &BTreeSet<String>,
57    resolver: &mut dyn VariableResolver,
58) -> Result<Statement, SQLError> {
59    let context = RuleBindingContext::with_catalog(catalog, true);
60    let (returning, action_event) = match action {
61        Statement::Insert(statement) => (&statement.returning, RuleEvent::Insert),
62        Statement::Update(statement) => (&statement.returning, RuleEvent::Update),
63        Statement::Delete(statement) => (&statement.returning, RuleEvent::Delete),
64        _ => {
65            return bind_rule_statement_body(
66                action,
67                resolver,
68                &RuleBindingScope::default(),
69                &context,
70            )
71        }
72    };
73    let mut body = action.clone();
74    let aliases = match &mut body {
75        Statement::Insert(statement) => {
76            statement.returning.clear();
77            statement.returning_aliases.clone()
78        }
79        Statement::Update(statement) => {
80            statement.returning.clear();
81            statement.returning_aliases.clone()
82        }
83        Statement::Delete(statement) => {
84            statement.returning.clear();
85            statement.returning_aliases.clone()
86        }
87        _ => unreachable!("DML rule action changed statement kind"),
88    };
89    let mut bound = {
90        let mut body_resolver = RuleActionBodyResolver {
91            action_columns,
92            action_event,
93            event_resolver: resolver,
94        };
95        bind_rule_statement_body(
96            &body,
97            &mut body_resolver,
98            &RuleBindingScope::default(),
99            &context,
100        )?
101    };
102    if returning.is_empty() {
103        return Ok(bound);
104    }
105    let returning = returning::bind_rule_action_returning(
106        returning,
107        action_columns,
108        &aliases,
109        action_event,
110        resolver,
111        &context,
112    )?;
113    match &mut bound {
114        Statement::Insert(statement) => statement.returning = returning,
115        Statement::Update(statement) => statement.returning = returning,
116        Statement::Delete(statement) => statement.returning = returning,
117        _ => unreachable!("bound DML rule action changed statement kind"),
118    }
119    Ok(bound)
120}
121
122struct RuleActionBodyResolver<'a, 'resolver> {
123    action_columns: &'a BTreeSet<String>,
124    action_event: RuleEvent,
125    event_resolver: &'resolver mut dyn VariableResolver,
126}
127
128impl RuleActionBodyResolver<'_, '_> {
129    fn is_action_column(&self, name: &str) -> bool {
130        matches!(self.action_event, RuleEvent::Update | RuleEvent::Delete)
131            && self
132                .action_columns
133                .iter()
134                .any(|column| column.eq_ignore_ascii_case(name))
135    }
136}
137
138impl VariableResolver for RuleActionBodyResolver<'_, '_> {
139    fn resolve_name(&mut self, name: &str) -> Result<Option<ResolvedVariable>, SQLError> {
140        if self.is_action_column(name) {
141            Ok(None)
142        } else {
143            self.event_resolver.resolve_name(name)
144        }
145    }
146
147    fn resolve_qualified(
148        &mut self,
149        qualifier: &str,
150        column: &str,
151    ) -> Result<Option<ResolvedVariable>, SQLError> {
152        self.event_resolver.resolve_qualified(qualifier, column)
153    }
154
155    fn resolve_param(&mut self, index: usize) -> Result<Option<ResolvedVariable>, SQLError> {
156        self.event_resolver.resolve_param(index)
157    }
158
159    fn rewrite_name(&mut self, name: &str) -> Result<Option<Expr>, SQLError> {
160        if self.is_action_column(name) {
161            Ok(None)
162        } else {
163            self.event_resolver.rewrite_name(name)
164        }
165    }
166
167    fn rewrite_qualified(
168        &mut self,
169        qualifier: &str,
170        column: &str,
171    ) -> Result<Option<Expr>, SQLError> {
172        self.event_resolver.rewrite_qualified(qualifier, column)
173    }
174
175    fn rewrite_qualified_star(&mut self, qualifier: &str) -> Result<Option<Vec<Expr>>, SQLError> {
176        self.event_resolver.rewrite_qualified_star(qualifier)
177    }
178
179    fn rewrite_qualified_whole_row(&mut self, qualifier: &str) -> Result<Option<Expr>, SQLError> {
180        self.event_resolver.rewrite_qualified_whole_row(qualifier)
181    }
182
183    fn rewrite_param(&mut self, index: usize) -> Result<Option<Expr>, SQLError> {
184        self.event_resolver.rewrite_param(index)
185    }
186
187    fn rewrite_internal(
188        &mut self,
189        column: crate::ast::InternalColumnRef,
190    ) -> Result<Option<Expr>, SQLError> {
191        self.event_resolver.rewrite_internal(column)
192    }
193}
194
195/// Bind one expression while masking relation qualifiers supplied by the SQL scope around it. Nested query scopes add their own visible aliases.
196pub fn bind_rule_expr_scoped(
197    expr: &Expr,
198    resolver: &mut dyn VariableResolver,
199    shadowed: &BTreeSet<String>,
200) -> Result<Expr, SQLError> {
201    bind_rule_expr_with_scope(
202        expr,
203        resolver,
204        &RuleBindingScope::from_qualifiers(shadowed),
205        &RuleBindingContext::default(),
206    )
207}
208
209fn bind_rule_expr_with_scope(
210    expr: &Expr,
211    resolver: &mut dyn VariableResolver,
212    scope: &RuleBindingScope,
213    context: &RuleBindingContext<'_>,
214) -> Result<Expr, SQLError> {
215    Ok(match expr {
216        Expr::Column(name) => {
217            if scope.qualifier_is_shadowed(name) || scope.column_is_shadowed(name) {
218                expr.clone()
219            } else {
220                resolver.rewrite_name(name)?.unwrap_or_else(|| expr.clone())
221            }
222        }
223        Expr::QualifiedColumn { qualifier, column } => {
224            if scope.qualifier_is_shadowed(qualifier) {
225                expr.clone()
226            } else {
227                resolver
228                    .rewrite_qualified(qualifier, column)?
229                    .unwrap_or_else(|| expr.clone())
230            }
231        }
232        Expr::Param(index) => resolver
233            .rewrite_param(*index)?
234            .unwrap_or_else(|| expr.clone()),
235        Expr::InternalColumn(column) => resolver
236            .rewrite_internal(*column)?
237            .unwrap_or_else(|| expr.clone()),
238        Expr::QualifiedStar(qualifier) => {
239            if scope.qualifier_is_shadowed(qualifier) {
240                expr.clone()
241            } else {
242                resolver
243                    .rewrite_qualified_whole_row(qualifier)?
244                    .unwrap_or_else(|| expr.clone())
245            }
246        }
247        Expr::Default | Expr::Literal(_) | Expr::TypedLiteral { .. } | Expr::Star => expr.clone(),
248        Expr::Func { .. } => bind_rule_function_expression(expr, resolver, scope, context)?,
249        Expr::Array(items) => Expr::Array(bind_exprs(items, resolver, scope, context)?),
250        Expr::Row(items) => Expr::Row(bind_expanding_exprs(items, resolver, scope, context)?),
251        Expr::CompositeRow { items, binding } => Expr::CompositeRow {
252            items: bind_expanding_exprs(items, resolver, scope, context)?,
253            binding: binding.clone(),
254        },
255        Expr::Binary { op, lhs, rhs } => Expr::Binary {
256            op: *op,
257            lhs: Box::new(bind_rule_expr_with_scope(lhs, resolver, scope, context)?),
258            rhs: Box::new(bind_rule_expr_with_scope(rhs, resolver, scope, context)?),
259        },
260        Expr::UnaryMinus(inner) => Expr::UnaryMinus(Box::new(bind_rule_expr_with_scope(
261            inner, resolver, scope, context,
262        )?)),
263        Expr::Not(inner) => Expr::Not(Box::new(bind_rule_expr_with_scope(
264            inner, resolver, scope, context,
265        )?)),
266        Expr::And(items) => Expr::And(bind_exprs(items, resolver, scope, context)?),
267        Expr::Or(items) => Expr::Or(bind_exprs(items, resolver, scope, context)?),
268        Expr::IsNull { expr, negated } => Expr::IsNull {
269            expr: Box::new(bind_rule_expr_with_scope(expr, resolver, scope, context)?),
270            negated: *negated,
271        },
272        Expr::Between { expr, low, high } => Expr::Between {
273            expr: Box::new(bind_rule_expr_with_scope(expr, resolver, scope, context)?),
274            low: Box::new(bind_rule_expr_with_scope(low, resolver, scope, context)?),
275            high: Box::new(bind_rule_expr_with_scope(high, resolver, scope, context)?),
276        },
277        Expr::InList {
278            expr,
279            list,
280            negated,
281        } => Expr::InList {
282            expr: Box::new(bind_rule_expr_with_scope(expr, resolver, scope, context)?),
283            list: bind_exprs(list, resolver, scope, context)?,
284            negated: *negated,
285        },
286        Expr::WindowCall { .. } => bind_rule_window_expression(expr, resolver, scope, context)?,
287        Expr::Case { .. } => bind_rule_case_expression(expr, resolver, scope, context)?,
288        Expr::Cast { expr, ty, implicit } => Expr::Cast {
289            implicit: *implicit,
290            expr: Box::new(bind_rule_expr_with_scope(expr, resolver, scope, context)?),
291            ty: ty.clone(),
292        },
293        Expr::ScalarSubquery(body) => Expr::ScalarSubquery(Box::new(bind_select_with_scope(
294            body, resolver, scope, context,
295        )?)),
296        Expr::Exists { body, negated } => Expr::Exists {
297            body: Box::new(bind_select_with_scope(body, resolver, scope, context)?),
298            negated: *negated,
299        },
300        Expr::InSubquery {
301            expr,
302            body,
303            negated,
304        } => Expr::InSubquery {
305            expr: Box::new(bind_rule_expr_with_scope(expr, resolver, scope, context)?),
306            body: Box::new(bind_select_with_scope(body, resolver, scope, context)?),
307            negated: *negated,
308        },
309    })
310}
311
312fn bind_rule_function_expression(
313    expr: &Expr,
314    resolver: &mut dyn VariableResolver,
315    scope: &RuleBindingScope,
316    context: &RuleBindingContext<'_>,
317) -> Result<Expr, SQLError> {
318    if let Some((qualifier, column)) = namespace::event_field_reference(expr, scope) {
319        return Ok(resolver
320            .rewrite_qualified(qualifier, column)?
321            .unwrap_or_else(|| Expr::qualified_column(qualifier, column)));
322    }
323    let Expr::Func {
324        order_syntax,
325        name,
326        binding,
327        args,
328        distinct,
329        order_by,
330        filter,
331    } = expr
332    else {
333        unreachable!("function binder received a non-function expression")
334    };
335    Ok(Expr::Func {
336        order_syntax: *order_syntax,
337        name: name.clone(),
338        binding: binding.clone(),
339        args: bind_exprs(args, resolver, scope, context)?,
340        distinct: *distinct,
341        order_by: bind_orders(order_by, resolver, scope, context)?,
342        filter: filter
343            .as_deref()
344            .map(|expr| bind_rule_expr_with_scope(expr, resolver, scope, context).map(Box::new))
345            .transpose()?,
346    })
347}
348
349fn bind_window_spec(
350    spec: &crate::ast::WindowSpec,
351    resolver: &mut dyn VariableResolver,
352    scope: &RuleBindingScope,
353    context: &RuleBindingContext<'_>,
354) -> Result<crate::ast::WindowSpec, SQLError> {
355    Ok(crate::ast::WindowSpec {
356        definition: spec.definition,
357        raw_definition: None,
358        reference: spec.reference.clone(),
359        partition_by: bind_exprs(&spec.partition_by, resolver, scope, context)?,
360        order_by: bind_orders(&spec.order_by, resolver, scope, context)?,
361        frame: spec
362            .frame
363            .as_ref()
364            .map(|frame| -> Result<crate::ast::WindowFrame, SQLError> {
365                Ok(crate::ast::WindowFrame {
366                    mode: frame.mode,
367                    start: bind_frame_bound(&frame.start, resolver, scope, context)?,
368                    end: bind_frame_bound(&frame.end, resolver, scope, context)?,
369                    between: frame.between,
370                    exclusion: frame.exclusion,
371                })
372            })
373            .transpose()?,
374    })
375}
376
377fn bind_rule_window_expression(
378    expr: &Expr,
379    resolver: &mut dyn VariableResolver,
380    scope: &RuleBindingScope,
381    context: &RuleBindingContext<'_>,
382) -> Result<Expr, SQLError> {
383    let Expr::WindowCall {
384        name,
385        args,
386        spec,
387        filter,
388        modifiers,
389    } = expr
390    else {
391        unreachable!("window binder received a non-window expression")
392    };
393    Ok(Expr::WindowCall {
394        modifiers: *modifiers,
395        name: name.clone(),
396        args: bind_exprs(args, resolver, scope, context)?,
397        filter: filter
398            .as_deref()
399            .map(|filter| bind_rule_expr_with_scope(filter, resolver, scope, context).map(Box::new))
400            .transpose()?,
401        spec: Box::new(bind_window_spec(spec, resolver, scope, context)?),
402    })
403}
404
405fn bind_rule_case_expression(
406    expr: &Expr,
407    resolver: &mut dyn VariableResolver,
408    scope: &RuleBindingScope,
409    context: &RuleBindingContext<'_>,
410) -> Result<Expr, SQLError> {
411    let Expr::Case {
412        base,
413        when,
414        else_branch,
415    } = expr
416    else {
417        unreachable!("CASE binder received a non-CASE expression")
418    };
419    Ok(Expr::Case {
420        base: base
421            .as_deref()
422            .map(|expr| bind_rule_expr_with_scope(expr, resolver, scope, context).map(Box::new))
423            .transpose()?,
424        when: when
425            .iter()
426            .map(|(condition, result)| {
427                Ok((
428                    bind_rule_expr_with_scope(condition, resolver, scope, context)?,
429                    bind_rule_expr_with_scope(result, resolver, scope, context)?,
430                ))
431            })
432            .collect::<Result<Vec<_>, SQLError>>()?,
433        else_branch: else_branch
434            .as_deref()
435            .map(|expr| bind_rule_expr_with_scope(expr, resolver, scope, context).map(Box::new))
436            .transpose()?,
437    })
438}
439
440fn bind_rule_select_scoped(
441    catalog: &dyn RuleSourceCatalog,
442    select: &SelectStmt,
443    resolver: &mut dyn VariableResolver,
444) -> Result<SelectStmt, SQLError> {
445    bind_select_with_scope(
446        select,
447        resolver,
448        &RuleBindingScope::default(),
449        &RuleBindingContext::with_catalog(catalog, false),
450    )
451}
452
453fn bind_rule_statement_body(
454    statement: &Statement,
455    resolver: &mut dyn VariableResolver,
456    inherited: &RuleBindingScope,
457    context: &RuleBindingContext<'_>,
458) -> Result<Statement, SQLError> {
459    Ok(match statement {
460        Statement::Select(select) => Statement::Select(Box::new(bind_select_with_scope(
461            select, resolver, inherited, context,
462        )?)),
463        Statement::Insert(insert) => {
464            Statement::Insert(bind_insert(insert, resolver, inherited, context)?)
465        }
466        Statement::Update(update) => {
467            Statement::Update(bind_update(update, resolver, inherited, context)?)
468        }
469        Statement::Delete(delete) => {
470            let mut output = delete.clone();
471            output.with = bind_ctes(&delete.with, resolver, inherited, context)?;
472            let context = context.with_ctes(&delete.with)?;
473            let mut target_scope = inherited.clone();
474            target_scope.insert_qualifier(&delete.target_qualifier);
475            output.using = delete
476                .using
477                .as_ref()
478                .map(|source| bind_from(source, resolver, &target_scope, &context))
479                .transpose()?;
480            let mut expression_scope = target_scope;
481            if let Some(source) = &delete.using {
482                collect_visible_scope(source, &context, &mut expression_scope)?;
483            }
484            output.r#where = bind_optional_expr(
485                delete.r#where.as_ref(),
486                resolver,
487                &expression_scope,
488                &context,
489            )?;
490            output.returning.clear();
491            Statement::Delete(output)
492        }
493        _ => return bind_statement(statement, resolver),
494    })
495}
496
497fn bind_assignment_targets(
498    targets: &crate::ast::AssignmentTargets,
499    resolver: &mut dyn VariableResolver,
500    scope: &RuleBindingScope,
501    context: &RuleBindingContext<'_>,
502) -> Result<crate::ast::AssignmentTargets, SQLError> {
503    let mut targets = targets.clone();
504    for expression in targets.expressions_mut() {
505        *expression = bind_rule_expr_with_scope(expression, resolver, scope, context)?;
506    }
507    Ok(targets)
508}
509
510fn bind_assignment_target(
511    target: &crate::ast::AssignmentTarget,
512    resolver: &mut dyn VariableResolver,
513    scope: &RuleBindingScope,
514    context: &RuleBindingContext<'_>,
515) -> Result<crate::ast::AssignmentTarget, SQLError> {
516    let mut target = target.clone();
517    for expression in target.expressions_mut() {
518        *expression = bind_rule_expr_with_scope(expression, resolver, scope, context)?;
519    }
520    Ok(target)
521}
522
523fn bind_update(
524    update: &UpdateStmt,
525    resolver: &mut dyn VariableResolver,
526    inherited: &RuleBindingScope,
527    context: &RuleBindingContext<'_>,
528) -> Result<UpdateStmt, SQLError> {
529    let mut output = update.clone();
530    output.with = bind_ctes(&update.with, resolver, inherited, context)?;
531    let context = context.with_ctes(&update.with)?;
532    let mut target_scope = inherited.clone();
533    target_scope.insert_qualifier(&update.target_qualifier);
534    output.from = update
535        .from
536        .as_ref()
537        .map(|source| bind_from(source, resolver, &target_scope, &context))
538        .transpose()?;
539    let mut expression_scope = target_scope;
540    if let Some(source) = &update.from {
541        collect_visible_scope(source, &context, &mut expression_scope)?;
542    }
543    output.assignments = update
544        .assignments
545        .iter()
546        .map(|(column, expr)| {
547            Ok((
548                bind_assignment_targets(column, resolver, &expression_scope, &context)?,
549                bind_rule_expr_with_scope(expr, resolver, &expression_scope, &context)?,
550            ))
551        })
552        .collect::<Result<Vec<_>, SQLError>>()?;
553    output.r#where = bind_optional_expr(
554        update.r#where.as_ref(),
555        resolver,
556        &expression_scope,
557        &context,
558    )?;
559    output.returning.clear();
560    Ok(output)
561}
562
563fn bind_select_with_scope(
564    select: &SelectStmt,
565    resolver: &mut dyn VariableResolver,
566    inherited: &RuleBindingScope,
567    context: &RuleBindingContext<'_>,
568) -> Result<SelectStmt, SQLError> {
569    let local_context = context.with_ctes(&select.with)?;
570    let mut scope = inherited.clone();
571    if let Some(source) = &select.from {
572        collect_visible_scope(source, &local_context, &mut scope)?;
573    }
574    Ok(SelectStmt {
575        windows: select
576            .windows
577            .iter()
578            .map(|window| {
579                Ok(crate::ast::WindowDefinition {
580                    name: window.name.clone(),
581                    inherited: window.inherited,
582                    spec: bind_window_spec(&window.spec, resolver, &scope, &local_context)?,
583                })
584            })
585            .collect::<Result<Vec<_>, SQLError>>()?,
586        projections: bind_projections(&select.projections, resolver, &scope, &local_context)?,
587        values: select
588            .values
589            .iter()
590            .map(|row| bind_expanding_exprs(row, resolver, &scope, &local_context))
591            .collect::<Result<Vec<_>, SQLError>>()?,
592        from: select
593            .from
594            .as_ref()
595            .map(|source| bind_from(source, resolver, inherited, &local_context))
596            .transpose()?,
597        r#where: bind_optional_expr(select.r#where.as_ref(), resolver, &scope, &local_context)?,
598        group_by: bind_exprs(&select.group_by, resolver, &scope, &local_context)?,
599        grouping_sets: select
600            .grouping_sets
601            .iter()
602            .map(|set| bind_exprs(set, resolver, &scope, &local_context))
603            .collect::<Result<Vec<_>, SQLError>>()?,
604        group_distinct: select.group_distinct,
605        having: bind_optional_expr(select.having.as_ref(), resolver, &scope, &local_context)?,
606        order_by: bind_orders(&select.order_by, resolver, &scope, &local_context)?,
607        limit: bind_optional_expr(select.limit.as_ref(), resolver, &scope, &local_context)?,
608        with_ties: select.with_ties,
609        offset: bind_optional_expr(select.offset.as_ref(), resolver, &scope, &local_context)?,
610        with: bind_ctes(&select.with, resolver, inherited, context)?,
611        set_op: select
612            .set_op
613            .as_ref()
614            .map(|operation| -> Result<Box<crate::ast::SetOp>, SQLError> {
615                Ok(Box::new(crate::ast::SetOp {
616                    kind: operation.kind,
617                    all: operation.all,
618                    left: operation
619                        .left
620                        .as_deref()
621                        .map(|left| {
622                            bind_select_with_scope(left, resolver, inherited, &local_context)
623                                .map(Box::new)
624                        })
625                        .transpose()?,
626                    right: bind_select_with_scope(
627                        &operation.right,
628                        resolver,
629                        inherited,
630                        &local_context,
631                    )?,
632                    combined_order_by: bind_orders(
633                        &operation.combined_order_by,
634                        resolver,
635                        inherited,
636                        &local_context,
637                    )?,
638                    combined_limit: bind_optional_expr(
639                        operation.combined_limit.as_ref(),
640                        resolver,
641                        inherited,
642                        &local_context,
643                    )?,
644                    combined_with_ties: operation.combined_with_ties,
645                    combined_offset: bind_optional_expr(
646                        operation.combined_offset.as_ref(),
647                        resolver,
648                        inherited,
649                        &local_context,
650                    )?,
651                }))
652            })
653            .transpose()?,
654        distinct: select.distinct,
655        distinct_on: bind_exprs(&select.distinct_on, resolver, &scope, &local_context)?,
656        locking: select.locking.clone(),
657    })
658}
659
660fn bind_from(
661    from: &FromClause,
662    resolver: &mut dyn VariableResolver,
663    inherited: &RuleBindingScope,
664    context: &RuleBindingContext<'_>,
665) -> Result<FromClause, SQLError> {
666    Ok(match from {
667        FromClause::Table { .. } => from.clone(),
668        FromClause::Join { .. } => bind_join_from(from, resolver, inherited, context)?,
669        FromClause::Values {
670            rows,
671            alias,
672            column_aliases,
673            internal_relation,
674            internal_column_types,
675            internal_column_collations,
676        } => FromClause::Values {
677            rows: rows
678                .iter()
679                .map(|row| bind_exprs(row, resolver, inherited, context))
680                .collect::<Result<Vec<_>, SQLError>>()?,
681            alias: alias.clone(),
682            column_aliases: column_aliases.clone(),
683            internal_relation: *internal_relation,
684            internal_column_types: internal_column_types.clone(),
685            internal_column_collations: internal_column_collations.clone(),
686        },
687        FromClause::Function {
688            name,
689            binding,
690            output_name,
691            relations,
692            args,
693            alias,
694            column_aliases,
695            ordinality,
696            column_types,
697        } => FromClause::Function {
698            name: name.clone(),
699            binding: binding.clone(),
700            output_name: output_name.clone(),
701            relations: relations.clone(),
702            args: bind_exprs(args, resolver, inherited, context)?,
703            alias: alias.clone(),
704            column_aliases: column_aliases.clone(),
705            ordinality: *ordinality,
706            column_types: column_types.clone(),
707        },
708        FromClause::FunctionGroup {
709            functions,
710            alias,
711            column_aliases,
712            ordinality,
713        } => FromClause::FunctionGroup {
714            functions: functions
715                .iter()
716                .map(|function| {
717                    Ok(crate::ast::TableFunction {
718                        name: function.name.clone(),
719                        binding: function.binding.clone(),
720                        output_name: function.output_name.clone(),
721                        relations: function.relations.clone(),
722                        args: bind_exprs(&function.args, resolver, inherited, context)?,
723                        column_aliases: function.column_aliases.clone(),
724                        column_types: function.column_types.clone(),
725                    })
726                })
727                .collect::<Result<Vec<_>, SQLError>>()?,
728            alias: alias.clone(),
729            column_aliases: column_aliases.clone(),
730            ordinality: *ordinality,
731        },
732        FromClause::Subquery {
733            body,
734            alias,
735            column_aliases,
736        } => FromClause::Subquery {
737            body: Box::new(bind_select_with_scope(body, resolver, inherited, context)?),
738            alias: alias.clone(),
739            column_aliases: column_aliases.clone(),
740        },
741    })
742}
743
744fn bind_join_from(
745    from: &FromClause,
746    resolver: &mut dyn VariableResolver,
747    inherited: &RuleBindingScope,
748    context: &RuleBindingContext<'_>,
749) -> Result<FromClause, SQLError> {
750    let FromClause::Join {
751        left,
752        right,
753        kind,
754        on,
755        using,
756        natural,
757        alias,
758        column_aliases,
759        lateral,
760    } = from
761    else {
762        unreachable!("join binder received a non-join source")
763    };
764    let bound_left = bind_from(left, resolver, inherited, context)?;
765    let mut right_scope = inherited.clone();
766    if *lateral {
767        collect_visible_scope(left, context, &mut right_scope)?;
768    }
769    let bound_right = bind_from(right, resolver, &right_scope, context)?;
770    let mut on_scope = inherited.clone();
771    collect_visible_scope(left, context, &mut on_scope)?;
772    collect_visible_scope(right, context, &mut on_scope)?;
773    Ok(FromClause::Join {
774        left: Box::new(bound_left),
775        right: Box::new(bound_right),
776        kind: *kind,
777        on: bind_optional_expr(on.as_ref(), resolver, &on_scope, context)?,
778        using: using.clone(),
779        natural: *natural,
780        alias: alias.clone(),
781        column_aliases: column_aliases.clone(),
782        lateral: *lateral,
783    })
784}
785
786fn bind_ctes(
787    ctes: &[CTE],
788    resolver: &mut dyn VariableResolver,
789    inherited: &RuleBindingScope,
790    context: &RuleBindingContext<'_>,
791) -> Result<Vec<CTE>, SQLError> {
792    let mut visible = context.clone();
793    let mut bound = Vec::with_capacity(ctes.len());
794    for cte in ctes {
795        if cte.recursive {
796            visible
797                .ctes
798                .entry(cte.name.to_ascii_lowercase())
799                .or_default();
800            let mut columns = cte_output_columns(&cte.body, &visible)?;
801            apply_positional_aliases(&mut columns, &cte.columns);
802            visible.ctes.insert(cte.name.to_ascii_lowercase(), columns);
803        }
804        bound.push(CTE {
805            name: cte.name.clone(),
806            columns: cte.columns.clone(),
807            recursive: cte.recursive,
808            materialization: cte.materialization,
809            search: cte.search.clone(),
810            cycle: cte
811                .cycle
812                .as_ref()
813                .map(|cycle| -> Result<crate::ast::CteCycleClause, SQLError> {
814                    Ok(crate::ast::CteCycleClause {
815                        columns: cycle.columns.clone(),
816                        mark_column: cycle.mark_column.clone(),
817                        mark_value: bind_rule_expr_with_scope(
818                            &cycle.mark_value,
819                            resolver,
820                            inherited,
821                            &visible,
822                        )?,
823                        mark_default: bind_rule_expr_with_scope(
824                            &cycle.mark_default,
825                            resolver,
826                            inherited,
827                            &visible,
828                        )?,
829                        path_column: cycle.path_column.clone(),
830                    })
831                })
832                .transpose()?,
833            body: match &cte.body {
834                crate::ast::CteBody::Query(query) => crate::ast::CteBody::Query(Box::new(
835                    bind_select_with_scope(query, resolver, inherited, &visible)?,
836                )),
837                // Command CTEs cannot reference a rule's event row. Their own DML row-image aliases belong to RETURNING and must retain their local identities.
838                body => body.clone(),
839            },
840        });
841        let mut columns = cte_output_columns(&cte.body, &visible)?;
842        apply_positional_aliases(&mut columns, &cte.columns);
843        if let Some(search) = &cte.search {
844            columns.push(search.sequence_column.clone());
845        }
846        if let Some(cycle) = &cte.cycle {
847            columns.push(cycle.mark_column.clone());
848            columns.push(cycle.path_column.clone());
849        }
850        visible.ctes.insert(cte.name.to_ascii_lowercase(), columns);
851    }
852    Ok(bound)
853}
854
855fn bind_frame_bound(
856    bound: &FrameBound,
857    resolver: &mut dyn VariableResolver,
858    scope: &RuleBindingScope,
859    context: &RuleBindingContext<'_>,
860) -> Result<FrameBound, SQLError> {
861    Ok(match bound {
862        FrameBound::Preceding(expr) => FrameBound::Preceding(Box::new(bind_rule_expr_with_scope(
863            expr, resolver, scope, context,
864        )?)),
865        FrameBound::Following(expr) => FrameBound::Following(Box::new(bind_rule_expr_with_scope(
866            expr, resolver, scope, context,
867        )?)),
868        _ => bound.clone(),
869    })
870}
871
872fn bind_projections(
873    projections: &[Projection],
874    resolver: &mut dyn VariableResolver,
875    scope: &RuleBindingScope,
876    context: &RuleBindingContext<'_>,
877) -> Result<Vec<Projection>, SQLError> {
878    let mut bound = Vec::with_capacity(projections.len());
879    for projection in projections {
880        if let Some(expressions) = expand_qualified_star(&projection.expr, resolver, scope)? {
881            bound.extend(expressions.into_iter().map(|expr| Projection {
882                expr,
883                alias: projection.alias.clone(),
884            }));
885        } else {
886            bound.push(Projection {
887                expr: bind_rule_expr_with_scope(&projection.expr, resolver, scope, context)?,
888                alias: projection.alias.clone(),
889            });
890        }
891    }
892    Ok(bound)
893}
894
895fn bind_expanding_exprs(
896    expressions: &[Expr],
897    resolver: &mut dyn VariableResolver,
898    scope: &RuleBindingScope,
899    context: &RuleBindingContext<'_>,
900) -> Result<Vec<Expr>, SQLError> {
901    let mut bound = Vec::with_capacity(expressions.len());
902    for expression in expressions {
903        if let Some(expressions) = expand_qualified_star(expression, resolver, scope)? {
904            bound.extend(expressions);
905        } else {
906            bound.push(bind_rule_expr_with_scope(
907                expression, resolver, scope, context,
908            )?);
909        }
910    }
911    Ok(bound)
912}
913
914fn expand_qualified_star(
915    expression: &Expr,
916    resolver: &mut dyn VariableResolver,
917    scope: &RuleBindingScope,
918) -> Result<Option<Vec<Expr>>, SQLError> {
919    let Expr::QualifiedStar(qualifier) = expression else {
920        return Ok(None);
921    };
922    if scope.qualifier_is_shadowed(qualifier) {
923        return Ok(None);
924    }
925    resolver.rewrite_qualified_star(qualifier)
926}
927
928fn bind_exprs(
929    expressions: &[Expr],
930    resolver: &mut dyn VariableResolver,
931    scope: &RuleBindingScope,
932    context: &RuleBindingContext<'_>,
933) -> Result<Vec<Expr>, SQLError> {
934    expressions
935        .iter()
936        .map(|expr| bind_rule_expr_with_scope(expr, resolver, scope, context))
937        .collect()
938}
939
940fn bind_optional_expr(
941    expression: Option<&Expr>,
942    resolver: &mut dyn VariableResolver,
943    scope: &RuleBindingScope,
944    context: &RuleBindingContext<'_>,
945) -> Result<Option<Expr>, SQLError> {
946    expression
947        .map(|expr| bind_rule_expr_with_scope(expr, resolver, scope, context))
948        .transpose()
949}
950
951fn bind_orders(
952    orders: &[OrderBy],
953    resolver: &mut dyn VariableResolver,
954    scope: &RuleBindingScope,
955    context: &RuleBindingContext<'_>,
956) -> Result<Vec<OrderBy>, SQLError> {
957    orders
958        .iter()
959        .map(|order| {
960            Ok(OrderBy {
961                expr: bind_rule_expr_with_scope(&order.expr, resolver, scope, context)?,
962                descending: order.descending,
963                nulls: order.nulls,
964            })
965        })
966        .collect()
967}
968
969pub fn invalid_rule_relation_reference(qualifier: &str) -> SQLError {
970    SQLError::Diagnostic {
971        sqlstate: "42P01".into(),
972        message: format!("invalid reference to FROM-clause entry for table \"{qualifier}\""),
973        detail: Some(format!(
974            "There is an entry for table \"{qualifier}\", but it cannot be referenced from this part of the query."
975        )),
976        hint: None,
977    }
978}