1use super::{Expr, FromClause, MergeWhen, Projection, Result, SelectStmt, Statement, Value, CTE};
10use crate::ast::InternalColumnRef;
11
12#[derive(Debug, Clone)]
14pub struct ResolvedVariable {
15 pub value: Value,
16 pub declared_type: Option<String>,
17}
18
19impl ResolvedVariable {
20 #[must_use]
21 pub fn untyped(value: Value) -> Self {
22 Self {
23 value,
24 declared_type: None,
25 }
26 }
27
28 fn into_expression(self) -> Expr {
29 match self.declared_type {
30 Some(ty) => Expr::TypedLiteral {
31 composite_source: None,
32 value: self.value,
33 ty,
34 },
35 None => Expr::Literal(self.value),
36 }
37 }
38}
39
40pub trait VariableResolver {
43 fn parameter_type(&self, name: &str) -> Option<crate::ColumnType> {
46 crate::ColumnType::from_sql_name(name).ok()
47 }
48
49 fn resolve_name(&mut self, name: &str) -> Result<Option<ResolvedVariable>>;
52 fn resolve_qualified(
54 &mut self,
55 qualifier: &str,
56 column: &str,
57 ) -> Result<Option<ResolvedVariable>>;
58 fn resolve_param(&mut self, index: usize) -> Result<Option<ResolvedVariable>>;
60
61 fn rewrite_name(&mut self, name: &str) -> Result<Option<Expr>> {
63 Ok(self
64 .resolve_name(name)?
65 .map(ResolvedVariable::into_expression))
66 }
67
68 fn rewrite_qualified(&mut self, qualifier: &str, column: &str) -> Result<Option<Expr>> {
70 Ok(self
71 .resolve_qualified(qualifier, column)?
72 .map(ResolvedVariable::into_expression))
73 }
74
75 fn rewrite_qualified_star(&mut self, _qualifier: &str) -> Result<Option<Vec<Expr>>> {
80 Ok(None)
81 }
82
83 fn rewrite_qualified_whole_row(&mut self, _qualifier: &str) -> Result<Option<Expr>> {
85 Ok(None)
86 }
87
88 fn rewrite_param(&mut self, index: usize) -> Result<Option<Expr>> {
90 Ok(self
91 .resolve_param(index)?
92 .map(ResolvedVariable::into_expression))
93 }
94
95 fn rewrite_internal(&mut self, _column: InternalColumnRef) -> Result<Option<Expr>> {
98 Ok(None)
99 }
100}
101
102#[expect(
105 clippy::too_many_lines,
106 reason = "PL/pgSQL lowering preserves parser order and datum validation"
107)]
108pub fn bind_expr(expr: &Expr, r: &mut dyn VariableResolver) -> Result<Expr> {
109 Ok(match expr {
110 Expr::Column(name) => match r.rewrite_name(name)? {
111 Some(value) => value,
112 None => expr.clone(),
113 },
114 Expr::QualifiedColumn {
115 qualifier, column, ..
116 } => match r.rewrite_qualified(qualifier, column)? {
117 Some(value) => value,
118 None => expr.clone(),
119 },
120 Expr::Param(index) => match r.rewrite_param(*index)? {
121 Some(value) => value,
122 None => expr.clone(),
123 },
124 Expr::InternalColumn(column) => match r.rewrite_internal(*column)? {
125 Some(value) => value,
126 None => expr.clone(),
127 },
128 Expr::QualifiedStar(qualifier) => r
129 .rewrite_qualified_whole_row(qualifier)?
130 .unwrap_or_else(|| expr.clone()),
131 Expr::Default | Expr::Literal(_) | Expr::TypedLiteral { .. } | Expr::Star => expr.clone(),
132 Expr::Func {
133 order_syntax,
134 name,
135 binding,
136 args,
137 distinct,
138 order_by,
139 filter,
140 } => Expr::Func {
141 order_syntax: *order_syntax,
142 name: name.clone(),
143 binding: binding.clone(),
144 args: bind_exprs(args, r)?,
145 distinct: *distinct,
146 order_by: bind_order_by(order_by, r)?,
147 filter: match filter {
148 Some(f) => Some(Box::new(bind_expr(f, r)?)),
149 None => None,
150 },
151 },
152 Expr::Array(items) => Expr::Array(bind_exprs(items, r)?),
153 Expr::Row(items) => Expr::Row(bind_exprs(items, r)?),
154 Expr::CompositeRow { items, binding } => Expr::CompositeRow {
155 items: bind_exprs(items, r)?,
156 binding: binding.clone(),
157 },
158 Expr::Binary { op, lhs, rhs } => Expr::Binary {
159 op: *op,
160 lhs: Box::new(bind_expr(lhs, r)?),
161 rhs: Box::new(bind_expr(rhs, r)?),
162 },
163 Expr::UnaryMinus(inner) => Expr::UnaryMinus(Box::new(bind_expr(inner, r)?)),
164 Expr::Not(inner) => Expr::Not(Box::new(bind_expr(inner, r)?)),
165 Expr::And(items) => Expr::And(bind_exprs(items, r)?),
166 Expr::Or(items) => Expr::Or(bind_exprs(items, r)?),
167 Expr::IsNull { expr, negated } => Expr::IsNull {
168 expr: Box::new(bind_expr(expr, r)?),
169 negated: *negated,
170 },
171 Expr::Between { expr, low, high } => Expr::Between {
172 expr: Box::new(bind_expr(expr, r)?),
173 low: Box::new(bind_expr(low, r)?),
174 high: Box::new(bind_expr(high, r)?),
175 },
176 Expr::InList {
177 expr,
178 list,
179 negated,
180 } => Expr::InList {
181 expr: Box::new(bind_expr(expr, r)?),
182 list: bind_exprs(list, r)?,
183 negated: *negated,
184 },
185 Expr::WindowCall {
186 name,
187 args,
188 spec,
189 filter,
190 modifiers,
191 } => Expr::WindowCall {
192 modifiers: *modifiers,
193 name: name.clone(),
194 args: bind_exprs(args, r)?,
195 spec: Box::new(bind_window_spec(spec, r)?),
196 filter: match filter {
197 Some(f) => Some(Box::new(bind_expr(f, r)?)),
198 None => None,
199 },
200 },
201 Expr::Case {
202 base,
203 when,
204 else_branch,
205 } => Expr::Case {
206 base: match base {
207 Some(b) => Some(Box::new(bind_expr(b, r)?)),
208 None => None,
209 },
210 when: when
211 .iter()
212 .map(|(c, v)| Ok((bind_expr(c, r)?, bind_expr(v, r)?)))
213 .collect::<Result<Vec<_>>>()?,
214 else_branch: match else_branch {
215 Some(e) => Some(Box::new(bind_expr(e, r)?)),
216 None => None,
217 },
218 },
219 Expr::Cast { expr, ty, implicit } => Expr::Cast {
220 implicit: *implicit,
221 expr: Box::new(bind_expr(expr, r)?),
222 ty: ty.clone(),
223 },
224 Expr::ScalarSubquery(body) => Expr::ScalarSubquery(Box::new(bind_select(body, r)?)),
225 Expr::Exists { body, negated } => Expr::Exists {
226 body: Box::new(bind_select(body, r)?),
227 negated: *negated,
228 },
229 Expr::InSubquery {
230 expr,
231 body,
232 negated,
233 } => Expr::InSubquery {
234 expr: Box::new(bind_expr(expr, r)?),
235 body: Box::new(bind_select(body, r)?),
236 negated: *negated,
237 },
238 })
239}
240
241fn bind_frame_bound(
243 bound: &crate::ast::FrameBound,
244 r: &mut dyn VariableResolver,
245) -> Result<crate::ast::FrameBound> {
246 use crate::ast::FrameBound;
247 Ok(match bound {
248 FrameBound::Preceding(offset) => FrameBound::Preceding(Box::new(bind_expr(offset, r)?)),
249 FrameBound::Following(offset) => FrameBound::Following(Box::new(bind_expr(offset, r)?)),
250 FrameBound::UnboundedPreceding => FrameBound::UnboundedPreceding,
251 FrameBound::UnboundedFollowing => FrameBound::UnboundedFollowing,
252 FrameBound::CurrentRow => FrameBound::CurrentRow,
253 })
254}
255
256pub(super) fn bind_exprs(exprs: &[Expr], r: &mut dyn VariableResolver) -> Result<Vec<Expr>> {
257 exprs.iter().map(|e| bind_expr(e, r)).collect()
258}
259
260pub(super) fn bind_opt_expr(
261 expr: Option<&Expr>,
262 r: &mut dyn VariableResolver,
263) -> Result<Option<Expr>> {
264 match expr {
265 Some(e) => Ok(Some(bind_expr(e, r)?)),
266 None => Ok(None),
267 }
268}
269
270pub(super) fn bind_order_by(
271 items: &[crate::ast::OrderBy],
272 r: &mut dyn VariableResolver,
273) -> Result<Vec<crate::ast::OrderBy>> {
274 items
275 .iter()
276 .map(|o| {
277 Ok(crate::ast::OrderBy {
278 expr: bind_expr(&o.expr, r)?,
279 descending: o.descending,
280 nulls: o.nulls,
281 })
282 })
283 .collect()
284}
285
286pub(super) fn bind_projections(
287 items: &[Projection],
288 r: &mut dyn VariableResolver,
289) -> Result<Vec<Projection>> {
290 items
291 .iter()
292 .map(|p| {
293 Ok(Projection {
294 expr: bind_expr(&p.expr, r)?,
295 alias: p.alias.clone(),
296 })
297 })
298 .collect()
299}
300
301pub(super) fn bind_assignments(
302 items: &[(crate::ast::AssignmentTargets, Expr)],
303 r: &mut dyn VariableResolver,
304) -> Result<Vec<(crate::ast::AssignmentTargets, Expr)>> {
305 items
306 .iter()
307 .map(|(target, expression)| {
308 Ok((
309 {
310 let mut group = target.clone();
311 for target in group.targets_mut() {
312 *target = bind_assignment_target(target, r)?;
313 }
314 group
315 },
316 bind_expr(expression, r)?,
317 ))
318 })
319 .collect()
320}
321
322fn bind_assignment_target(
323 target: &crate::ast::AssignmentTarget,
324 resolver: &mut dyn VariableResolver,
325) -> Result<crate::ast::AssignmentTarget> {
326 let mut target = target.clone();
327 for expression in target.expressions_mut() {
328 *expression = bind_expr(expression, resolver)?;
329 }
330 Ok(target)
331}
332
333pub(super) fn bind_ctes(items: &[CTE], r: &mut dyn VariableResolver) -> Result<Vec<CTE>> {
334 items
335 .iter()
336 .map(|cte| {
337 Ok(CTE {
338 name: cte.name.clone(),
339 columns: cte.columns.clone(),
340 recursive: cte.recursive,
341 materialization: cte.materialization,
342 search: cte.search.clone(),
343 cycle: cte
344 .cycle
345 .as_ref()
346 .map(|cycle| -> Result<crate::ast::CteCycleClause> {
347 Ok(crate::ast::CteCycleClause {
348 columns: cycle.columns.clone(),
349 mark_column: cycle.mark_column.clone(),
350 mark_value: bind_expr(&cycle.mark_value, r)?,
351 mark_default: bind_expr(&cycle.mark_default, r)?,
352 path_column: cycle.path_column.clone(),
353 })
354 })
355 .transpose()?,
356 body: crate::ast::CteBody::try_from(bind_statement(
357 &cte.body.clone().into_statement(),
358 r,
359 )?)?,
360 })
361 })
362 .collect()
363}
364
365pub(super) fn bind_rows(
366 rows: &[Vec<Expr>],
367 r: &mut dyn VariableResolver,
368) -> Result<Vec<Vec<Expr>>> {
369 rows.iter().map(|row| bind_exprs(row, r)).collect()
370}
371
372fn bind_window_spec(
373 spec: &crate::ast::WindowSpec,
374 r: &mut dyn VariableResolver,
375) -> Result<crate::ast::WindowSpec> {
376 Ok(crate::ast::WindowSpec {
377 definition: spec.definition,
378 raw_definition: None,
379 reference: spec.reference.clone(),
380 partition_by: bind_exprs(&spec.partition_by, r)?,
381 order_by: bind_order_by(&spec.order_by, r)?,
382 frame: spec
383 .frame
384 .as_ref()
385 .map(|frame| -> Result<crate::ast::WindowFrame> {
386 Ok(crate::ast::WindowFrame {
387 start: bind_frame_bound(&frame.start, r)?,
388 end: bind_frame_bound(&frame.end, r)?,
389 ..frame.clone()
390 })
391 })
392 .transpose()?,
393 })
394}
395
396pub fn bind_select(stmt: &SelectStmt, r: &mut dyn VariableResolver) -> Result<SelectStmt> {
398 Ok(SelectStmt {
399 windows: stmt
400 .windows
401 .iter()
402 .map(|window| {
403 Ok(crate::ast::WindowDefinition {
404 name: window.name.clone(),
405 inherited: window.inherited,
406 spec: bind_window_spec(&window.spec, r)?,
407 })
408 })
409 .collect::<Result<Vec<_>>>()?,
410 projections: bind_projections(&stmt.projections, r)?,
411 values: bind_rows(&stmt.values, r)?,
412 from: match stmt.from.as_ref() {
413 Some(f) => Some(bind_from(f, r)?),
414 None => None,
415 },
416 r#where: bind_opt_expr(stmt.r#where.as_ref(), r)?,
417 group_by: bind_exprs(&stmt.group_by, r)?,
418 grouping_sets: stmt
419 .grouping_sets
420 .iter()
421 .map(|set| bind_exprs(set, r))
422 .collect::<Result<Vec<_>>>()?,
423 group_distinct: stmt.group_distinct,
424 having: bind_opt_expr(stmt.having.as_ref(), r)?,
425 order_by: bind_order_by(&stmt.order_by, r)?,
426 limit: bind_opt_expr(stmt.limit.as_ref(), r)?,
427 with_ties: stmt.with_ties,
428 offset: bind_opt_expr(stmt.offset.as_ref(), r)?,
429 with: bind_ctes(&stmt.with, r)?,
430 set_op: match stmt.set_op.as_ref() {
431 Some(op) => Some(Box::new(crate::ast::SetOp {
432 kind: op.kind,
433 all: op.all,
434 left: op
435 .left
436 .as_ref()
437 .map(|left| bind_select(left, r).map(Box::new))
438 .transpose()?,
439 right: bind_select(&op.right, r)?,
440 combined_order_by: bind_order_by(&op.combined_order_by, r)?,
441 combined_limit: bind_opt_expr(op.combined_limit.as_ref(), r)?,
442 combined_with_ties: op.combined_with_ties,
443 combined_offset: bind_opt_expr(op.combined_offset.as_ref(), r)?,
444 })),
445 None => None,
446 },
447 distinct: stmt.distinct,
448 distinct_on: bind_exprs(&stmt.distinct_on, r)?,
449 locking: stmt.locking.clone(),
450 })
451}
452
453pub(super) fn bind_from(from: &FromClause, r: &mut dyn VariableResolver) -> Result<FromClause> {
454 Ok(match from {
455 FromClause::Table { .. } => from.clone(),
456 FromClause::Join {
457 left,
458 right,
459 kind,
460 on,
461 using,
462 natural,
463 alias,
464 column_aliases,
465 lateral,
466 } => FromClause::Join {
467 left: Box::new(bind_from(left, r)?),
468 right: Box::new(bind_from(right, r)?),
469 kind: *kind,
470 on: bind_opt_expr(on.as_ref(), r)?,
471 using: using.clone(),
472 natural: *natural,
473 alias: alias.clone(),
474 column_aliases: column_aliases.clone(),
475 lateral: *lateral,
476 },
477 FromClause::Values {
478 rows,
479 alias,
480 column_aliases,
481 internal_relation,
482 internal_column_types,
483 } => FromClause::Values {
484 rows: bind_rows(rows, r)?,
485 alias: alias.clone(),
486 column_aliases: column_aliases.clone(),
487 internal_relation: *internal_relation,
488 internal_column_types: internal_column_types.clone(),
489 },
490 FromClause::Function {
491 name,
492 binding,
493 output_name,
494 relations,
495 args,
496 alias,
497 column_aliases,
498 ordinality,
499 column_types,
500 } => FromClause::Function {
501 name: name.clone(),
502 binding: binding.clone(),
503 output_name: output_name.clone(),
504 relations: relations.clone(),
505 args: bind_exprs(args, r)?,
506 alias: alias.clone(),
507 column_aliases: column_aliases.clone(),
508 ordinality: *ordinality,
509 column_types: column_types.clone(),
510 },
511 FromClause::FunctionGroup {
512 functions,
513 alias,
514 column_aliases,
515 ordinality,
516 } => FromClause::FunctionGroup {
517 functions: functions
518 .iter()
519 .map(|function| {
520 Ok(crate::ast::TableFunction {
521 name: function.name.clone(),
522 binding: function.binding.clone(),
523 output_name: function.output_name.clone(),
524 relations: function.relations.clone(),
525 args: bind_exprs(&function.args, r)?,
526 column_aliases: function.column_aliases.clone(),
527 column_types: function.column_types.clone(),
528 })
529 })
530 .collect::<Result<Vec<_>>>()?,
531 alias: alias.clone(),
532 column_aliases: column_aliases.clone(),
533 ordinality: *ordinality,
534 },
535 FromClause::Subquery {
536 body,
537 alias,
538 column_aliases,
539 } => FromClause::Subquery {
540 body: Box::new(bind_select(body, r)?),
541 alias: alias.clone(),
542 column_aliases: column_aliases.clone(),
543 },
544 })
545}
546
547#[expect(
551 clippy::too_many_lines,
552 reason = "PL/pgSQL lowering preserves parser order and datum validation"
553)]
554pub fn bind_statement(stmt: &Statement, r: &mut dyn VariableResolver) -> Result<Statement> {
555 Ok(match stmt {
556 Statement::Select(body) => Statement::Select(Box::new(bind_select(body, r)?)),
557 Statement::Insert(insert) => {
558 let mut out = insert.clone();
559 out.columns = insert
560 .columns
561 .iter()
562 .map(|target| bind_assignment_target(target, r))
563 .collect::<Result<_>>()?;
564 out.with = bind_ctes(&insert.with, r)?;
565 out.rows = bind_rows(&insert.rows, r)?;
566 out.select_source = match insert.select_source.as_ref() {
567 Some(body) => Some(Box::new(bind_select(body, r)?)),
568 None => None,
569 };
570 out.on_conflict = match insert.on_conflict.as_ref() {
571 Some(oc) => Some(crate::ast::OnConflict {
572 predicate: bind_opt_expr(oc.predicate.as_deref(), r)?.map(Box::new),
573 constraint: oc.constraint.clone(),
574 conflict_columns: oc.conflict_columns.clone(),
575 expressions: oc
576 .expressions
577 .iter()
578 .map(|expr| bind_expr(expr, r))
579 .collect::<Result<Vec<_>>>()?,
580 action: match &oc.action {
581 crate::ast::OnConflictAction::Nothing => {
582 crate::ast::OnConflictAction::Nothing
583 }
584 crate::ast::OnConflictAction::Update {
585 assignments,
586 r#where,
587 } => crate::ast::OnConflictAction::Update {
588 assignments: bind_assignments(assignments, r)?,
589 r#where: bind_opt_expr(r#where.as_deref(), r)?.map(Box::new),
590 },
591 },
592 }),
593 None => None,
594 };
595 out.returning = bind_projections(&insert.returning, r)?;
596 Statement::Insert(out)
597 }
598 Statement::Update(update) => {
599 let mut out = update.clone();
600 out.assignments = bind_assignments(&update.assignments, r)?;
601 out.r#where = bind_opt_expr(update.r#where.as_ref(), r)?;
602 out.with = bind_ctes(&update.with, r)?;
603 out.from = match update.from.as_ref() {
604 Some(f) => Some(bind_from(f, r)?),
605 None => None,
606 };
607 out.returning = bind_projections(&update.returning, r)?;
608 Statement::Update(out)
609 }
610 Statement::Delete(delete) => {
611 let mut out = delete.clone();
612 out.r#where = bind_opt_expr(delete.r#where.as_ref(), r)?;
613 out.with = bind_ctes(&delete.with, r)?;
614 out.using = match delete.using.as_ref() {
615 Some(f) => Some(bind_from(f, r)?),
616 None => None,
617 };
618 out.returning = bind_projections(&delete.returning, r)?;
619 Statement::Delete(out)
620 }
621 Statement::Values { rows } => Statement::Values {
622 rows: bind_rows(rows, r)?,
623 },
624 Statement::CreateTableAs {
625 name,
626 if_not_exists,
627 column_names,
628 with_no_data,
629 select_into,
630 persistence,
631 on_commit,
632 body,
633 } => Statement::CreateTableAs {
634 name: name.clone(),
635 if_not_exists: *if_not_exists,
636 column_names: column_names.clone(),
637 with_no_data: *with_no_data,
638 select_into: *select_into,
639 persistence: *persistence,
640 on_commit: *on_commit,
641 body: Box::new(bind_select(body, r)?),
642 },
643 Statement::CreateMaterializedView {
644 name,
645 column_names,
646 if_not_exists,
647 with_no_data,
648 options,
649 body,
650 } => Statement::CreateMaterializedView {
651 name: name.clone(),
652 column_names: column_names.clone(),
653 if_not_exists: *if_not_exists,
654 with_no_data: *with_no_data,
655 options: options.clone(),
656 body: Box::new(bind_select(body, r)?),
657 },
658 Statement::Explain {
659 analyze,
660 verbose,
661 format,
662 body,
663 } => Statement::Explain {
664 analyze: *analyze,
665 verbose: *verbose,
666 format: format.clone(),
667 body: Box::new(bind_statement(body, r)?),
668 },
669 Statement::DeclareCursor(cursor) => {
670 let mut out = cursor.clone();
671 out.query = Box::new(bind_select(&cursor.query, r)?);
672 Statement::DeclareCursor(out)
673 }
674 Statement::Merge(merge) => {
675 let mut out = merge.clone();
676 out.with = bind_ctes(&merge.with, r)?;
677 out.source = bind_from(&merge.source, r)?;
678 out.join_condition = bind_expr(&merge.join_condition, r)?;
679 out.when_clauses = merge
680 .when_clauses
681 .iter()
682 .map(|w| bind_merge_when(w, r))
683 .collect::<Result<Vec<_>>>()?;
684 out.returning = bind_projections(&merge.returning, r)?;
685 Statement::Merge(out)
686 }
687 Statement::Call { name, args } => Statement::Call {
688 name: name.clone(),
689 args: bind_exprs(args, r)?,
690 },
691 other => other.clone(),
692 })
693}
694
695pub(super) fn bind_merge_when(when: &MergeWhen, r: &mut dyn VariableResolver) -> Result<MergeWhen> {
696 Ok(match when {
697 MergeWhen::UpdateMatched {
698 condition,
699 assignments,
700 } => MergeWhen::UpdateMatched {
701 condition: bind_opt_expr(condition.as_ref(), r)?,
702 assignments: bind_assignments(assignments, r)?,
703 },
704 MergeWhen::DeleteMatched { condition } => MergeWhen::DeleteMatched {
705 condition: bind_opt_expr(condition.as_ref(), r)?,
706 },
707 MergeWhen::UpdateNotMatchedBySource {
708 condition,
709 assignments,
710 } => MergeWhen::UpdateNotMatchedBySource {
711 condition: bind_opt_expr(condition.as_ref(), r)?,
712 assignments: bind_assignments(assignments, r)?,
713 },
714 MergeWhen::DeleteNotMatchedBySource { condition } => MergeWhen::DeleteNotMatchedBySource {
715 condition: bind_opt_expr(condition.as_ref(), r)?,
716 },
717 MergeWhen::InsertNotMatched {
718 condition,
719 columns,
720 overriding,
721 values,
722 } => MergeWhen::InsertNotMatched {
723 condition: bind_opt_expr(condition.as_ref(), r)?,
724 columns: columns
725 .iter()
726 .map(|target| bind_assignment_target(target, r))
727 .collect::<Result<_>>()?,
728 overriding: *overriding,
729 values: bind_exprs(values, r)?,
730 },
731 MergeWhen::NothingMatched { condition } => MergeWhen::NothingMatched {
732 condition: bind_opt_expr(condition.as_ref(), r)?,
733 },
734 MergeWhen::NothingNotMatched { condition } => MergeWhen::NothingNotMatched {
735 condition: bind_opt_expr(condition.as_ref(), r)?,
736 },
737 MergeWhen::NothingNotMatchedBySource { condition } => {
738 MergeWhen::NothingNotMatchedBySource {
739 condition: bind_opt_expr(condition.as_ref(), r)?,
740 }
741 }
742 })
743}