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: &[(String, Expr)],
268 r: &mut dyn VariableResolver,
269) -> Result<Vec<(String, Expr)>> {
270 items
271 .iter()
272 .map(|(name, e)| Ok((name.clone(), bind_expr(e, r)?)))
273 .collect()
274}
275
276pub(super) fn bind_ctes(items: &[CTE], r: &mut dyn VariableResolver) -> Result<Vec<CTE>> {
277 items
278 .iter()
279 .map(|cte| {
280 Ok(CTE {
281 name: cte.name.clone(),
282 columns: cte.columns.clone(),
283 recursive: cte.recursive,
284 materialization: cte.materialization,
285 search: cte.search.clone(),
286 cycle: cte
287 .cycle
288 .as_ref()
289 .map(|cycle| -> Result<crate::ast::CteCycleClause> {
290 Ok(crate::ast::CteCycleClause {
291 columns: cycle.columns.clone(),
292 mark_column: cycle.mark_column.clone(),
293 mark_value: bind_expr(&cycle.mark_value, r)?,
294 mark_default: bind_expr(&cycle.mark_default, r)?,
295 path_column: cycle.path_column.clone(),
296 })
297 })
298 .transpose()?,
299 body: crate::ast::CteBody::try_from(bind_statement(
300 &cte.body.clone().into_statement(),
301 r,
302 )?)?,
303 })
304 })
305 .collect()
306}
307
308pub(super) fn bind_rows(
309 rows: &[Vec<Expr>],
310 r: &mut dyn VariableResolver,
311) -> Result<Vec<Vec<Expr>>> {
312 rows.iter().map(|row| bind_exprs(row, r)).collect()
313}
314
315pub fn bind_select(stmt: &SelectStmt, r: &mut dyn VariableResolver) -> Result<SelectStmt> {
317 Ok(SelectStmt {
318 projections: bind_projections(&stmt.projections, r)?,
319 values: bind_rows(&stmt.values, r)?,
320 from: match stmt.from.as_ref() {
321 Some(f) => Some(bind_from(f, r)?),
322 None => None,
323 },
324 r#where: bind_opt_expr(stmt.r#where.as_ref(), r)?,
325 group_by: bind_exprs(&stmt.group_by, r)?,
326 grouping_sets: stmt
327 .grouping_sets
328 .iter()
329 .map(|set| bind_exprs(set, r))
330 .collect::<Result<Vec<_>>>()?,
331 group_distinct: stmt.group_distinct,
332 having: bind_opt_expr(stmt.having.as_ref(), r)?,
333 order_by: bind_order_by(&stmt.order_by, r)?,
334 limit: bind_opt_expr(stmt.limit.as_ref(), r)?,
335 with_ties: stmt.with_ties,
336 offset: bind_opt_expr(stmt.offset.as_ref(), r)?,
337 with: bind_ctes(&stmt.with, r)?,
338 set_op: match stmt.set_op.as_ref() {
339 Some(op) => Some(Box::new(crate::ast::SetOp {
340 kind: op.kind,
341 all: op.all,
342 left: op
343 .left
344 .as_ref()
345 .map(|left| bind_select(left, r).map(Box::new))
346 .transpose()?,
347 right: bind_select(&op.right, r)?,
348 combined_order_by: bind_order_by(&op.combined_order_by, r)?,
349 combined_limit: bind_opt_expr(op.combined_limit.as_ref(), r)?,
350 combined_with_ties: op.combined_with_ties,
351 combined_offset: bind_opt_expr(op.combined_offset.as_ref(), r)?,
352 })),
353 None => None,
354 },
355 distinct: stmt.distinct,
356 distinct_on: bind_exprs(&stmt.distinct_on, r)?,
357 locking: stmt.locking.clone(),
358 })
359}
360
361pub(super) fn bind_from(from: &FromClause, r: &mut dyn VariableResolver) -> Result<FromClause> {
362 Ok(match from {
363 FromClause::Table { .. } => from.clone(),
364 FromClause::Join {
365 left,
366 right,
367 kind,
368 on,
369 using,
370 natural,
371 alias,
372 column_aliases,
373 lateral,
374 } => FromClause::Join {
375 left: Box::new(bind_from(left, r)?),
376 right: Box::new(bind_from(right, r)?),
377 kind: *kind,
378 on: bind_opt_expr(on.as_ref(), r)?,
379 using: using.clone(),
380 natural: *natural,
381 alias: alias.clone(),
382 column_aliases: column_aliases.clone(),
383 lateral: *lateral,
384 },
385 FromClause::Values {
386 rows,
387 alias,
388 column_aliases,
389 internal_relation,
390 internal_column_types,
391 } => FromClause::Values {
392 rows: bind_rows(rows, r)?,
393 alias: alias.clone(),
394 column_aliases: column_aliases.clone(),
395 internal_relation: *internal_relation,
396 internal_column_types: internal_column_types.clone(),
397 },
398 FromClause::Function {
399 name,
400 binding,
401 output_name,
402 relations,
403 args,
404 alias,
405 column_aliases,
406 ordinality,
407 column_types,
408 } => FromClause::Function {
409 name: name.clone(),
410 binding: binding.clone(),
411 output_name: output_name.clone(),
412 relations: relations.clone(),
413 args: bind_exprs(args, r)?,
414 alias: alias.clone(),
415 column_aliases: column_aliases.clone(),
416 ordinality: *ordinality,
417 column_types: column_types.clone(),
418 },
419 FromClause::FunctionGroup {
420 functions,
421 alias,
422 column_aliases,
423 ordinality,
424 } => FromClause::FunctionGroup {
425 functions: functions
426 .iter()
427 .map(|function| {
428 Ok(crate::ast::TableFunction {
429 name: function.name.clone(),
430 binding: function.binding.clone(),
431 output_name: function.output_name.clone(),
432 relations: function.relations.clone(),
433 args: bind_exprs(&function.args, r)?,
434 column_aliases: function.column_aliases.clone(),
435 column_types: function.column_types.clone(),
436 })
437 })
438 .collect::<Result<Vec<_>>>()?,
439 alias: alias.clone(),
440 column_aliases: column_aliases.clone(),
441 ordinality: *ordinality,
442 },
443 FromClause::Subquery {
444 body,
445 alias,
446 column_aliases,
447 } => FromClause::Subquery {
448 body: Box::new(bind_select(body, r)?),
449 alias: alias.clone(),
450 column_aliases: column_aliases.clone(),
451 },
452 })
453}
454
455#[expect(
459 clippy::too_many_lines,
460 reason = "PL/pgSQL lowering preserves parser order and datum validation"
461)]
462pub fn bind_statement(stmt: &Statement, r: &mut dyn VariableResolver) -> Result<Statement> {
463 Ok(match stmt {
464 Statement::Select(body) => Statement::Select(Box::new(bind_select(body, r)?)),
465 Statement::Insert(insert) => {
466 let mut out = insert.clone();
467 out.with = bind_ctes(&insert.with, r)?;
468 out.rows = bind_rows(&insert.rows, r)?;
469 out.select_source = match insert.select_source.as_ref() {
470 Some(body) => Some(Box::new(bind_select(body, r)?)),
471 None => None,
472 };
473 out.on_conflict = match insert.on_conflict.as_ref() {
474 Some(oc) => Some(crate::ast::OnConflict {
475 predicate: bind_opt_expr(oc.predicate.as_deref(), r)?.map(Box::new),
476 constraint: oc.constraint.clone(),
477 conflict_columns: oc.conflict_columns.clone(),
478 expressions: oc
479 .expressions
480 .iter()
481 .map(|expr| bind_expr(expr, r))
482 .collect::<Result<Vec<_>>>()?,
483 action: match &oc.action {
484 crate::ast::OnConflictAction::Nothing => {
485 crate::ast::OnConflictAction::Nothing
486 }
487 crate::ast::OnConflictAction::Update {
488 assignments,
489 r#where,
490 } => crate::ast::OnConflictAction::Update {
491 assignments: bind_assignments(assignments, r)?,
492 r#where: bind_opt_expr(r#where.as_deref(), r)?.map(Box::new),
493 },
494 },
495 }),
496 None => None,
497 };
498 out.returning = bind_projections(&insert.returning, r)?;
499 Statement::Insert(out)
500 }
501 Statement::Update(update) => {
502 let mut out = update.clone();
503 out.assignments = bind_assignments(&update.assignments, r)?;
504 out.r#where = bind_opt_expr(update.r#where.as_ref(), r)?;
505 out.with = bind_ctes(&update.with, r)?;
506 out.from = match update.from.as_ref() {
507 Some(f) => Some(bind_from(f, r)?),
508 None => None,
509 };
510 out.returning = bind_projections(&update.returning, r)?;
511 Statement::Update(out)
512 }
513 Statement::Delete(delete) => {
514 let mut out = delete.clone();
515 out.r#where = bind_opt_expr(delete.r#where.as_ref(), r)?;
516 out.with = bind_ctes(&delete.with, r)?;
517 out.using = match delete.using.as_ref() {
518 Some(f) => Some(bind_from(f, r)?),
519 None => None,
520 };
521 out.returning = bind_projections(&delete.returning, r)?;
522 Statement::Delete(out)
523 }
524 Statement::Values { rows } => Statement::Values {
525 rows: bind_rows(rows, r)?,
526 },
527 Statement::CreateTableAs {
528 name,
529 if_not_exists,
530 column_names,
531 with_no_data,
532 persistence,
533 on_commit,
534 body,
535 } => Statement::CreateTableAs {
536 name: name.clone(),
537 if_not_exists: *if_not_exists,
538 column_names: column_names.clone(),
539 with_no_data: *with_no_data,
540 persistence: *persistence,
541 on_commit: *on_commit,
542 body: Box::new(bind_select(body, r)?),
543 },
544 Statement::CreateMaterializedView {
545 name,
546 column_names,
547 if_not_exists,
548 with_no_data,
549 options,
550 body,
551 } => Statement::CreateMaterializedView {
552 name: name.clone(),
553 column_names: column_names.clone(),
554 if_not_exists: *if_not_exists,
555 with_no_data: *with_no_data,
556 options: options.clone(),
557 body: Box::new(bind_select(body, r)?),
558 },
559 Statement::Explain {
560 analyze,
561 verbose,
562 format,
563 body,
564 } => Statement::Explain {
565 analyze: *analyze,
566 verbose: *verbose,
567 format: format.clone(),
568 body: Box::new(bind_statement(body, r)?),
569 },
570 Statement::DeclareCursor(cursor) => {
571 let mut out = cursor.clone();
572 out.query = Box::new(bind_select(&cursor.query, r)?);
573 Statement::DeclareCursor(out)
574 }
575 Statement::Merge(merge) => {
576 let mut out = merge.clone();
577 out.with = bind_ctes(&merge.with, r)?;
578 out.source = bind_from(&merge.source, r)?;
579 out.join_condition = bind_expr(&merge.join_condition, r)?;
580 out.when_clauses = merge
581 .when_clauses
582 .iter()
583 .map(|w| bind_merge_when(w, r))
584 .collect::<Result<Vec<_>>>()?;
585 out.returning = bind_projections(&merge.returning, r)?;
586 Statement::Merge(out)
587 }
588 Statement::Call { name, args } => Statement::Call {
589 name: name.clone(),
590 args: bind_exprs(args, r)?,
591 },
592 other => other.clone(),
593 })
594}
595
596pub(super) fn bind_merge_when(when: &MergeWhen, r: &mut dyn VariableResolver) -> Result<MergeWhen> {
597 Ok(match when {
598 MergeWhen::UpdateMatched {
599 condition,
600 assignments,
601 } => MergeWhen::UpdateMatched {
602 condition: bind_opt_expr(condition.as_ref(), r)?,
603 assignments: bind_assignments(assignments, r)?,
604 },
605 MergeWhen::DeleteMatched { condition } => MergeWhen::DeleteMatched {
606 condition: bind_opt_expr(condition.as_ref(), r)?,
607 },
608 MergeWhen::UpdateNotMatchedBySource {
609 condition,
610 assignments,
611 } => MergeWhen::UpdateNotMatchedBySource {
612 condition: bind_opt_expr(condition.as_ref(), r)?,
613 assignments: bind_assignments(assignments, r)?,
614 },
615 MergeWhen::DeleteNotMatchedBySource { condition } => MergeWhen::DeleteNotMatchedBySource {
616 condition: bind_opt_expr(condition.as_ref(), r)?,
617 },
618 MergeWhen::InsertNotMatched {
619 condition,
620 columns,
621 values,
622 } => MergeWhen::InsertNotMatched {
623 condition: bind_opt_expr(condition.as_ref(), r)?,
624 columns: columns.clone(),
625 values: bind_exprs(values, r)?,
626 },
627 MergeWhen::NothingMatched { condition } => MergeWhen::NothingMatched {
628 condition: bind_opt_expr(condition.as_ref(), r)?,
629 },
630 MergeWhen::NothingNotMatched { condition } => MergeWhen::NothingNotMatched {
631 condition: bind_opt_expr(condition.as_ref(), r)?,
632 },
633 MergeWhen::NothingNotMatchedBySource { condition } => {
634 MergeWhen::NothingNotMatchedBySource {
635 condition: bind_opt_expr(condition.as_ref(), r)?,
636 }
637 }
638 })
639}