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