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