1use super::{
10 binding::{bind_statement, ResolvedVariable, VariableResolver},
11 variable_conflicts::{statement_variable_names, VariableSiteResolver},
12 VariableConflict,
13};
14use crate::{
15 ast::{Expr, Statement},
16 SQLError, SQLParam,
17};
18
19pub fn parameter_type_mismatch(
20 number: usize,
21 actual: &crate::ColumnType,
22 expected: &crate::ColumnType,
23) -> crate::ast::DeferredSQLError {
24 crate::ast::DeferredSQLError {
25 sqlstate: "42804".into(),
26 message: format!(
27 "type of parameter {number} ({}) does not match that when preparing the plan ({})",
28 actual.display_name(),
29 expected.display_name()
30 ),
31 }
32}
33
34#[derive(Debug, Clone)]
35pub enum PLpgSQLVariableReference {
36 Name(String),
37 Qualified { qualifier: String, column: String },
38 Parameter(usize),
39}
40impl PLpgSQLVariableReference {
41 pub fn read(&self, resolver: &mut dyn VariableResolver) -> Result<SQLParam, SQLError> {
42 let value = match self {
43 Self::Name(name) => resolver.resolve_name(name),
44 Self::Qualified { qualifier, column } => resolver.resolve_qualified(qualifier, column),
45 Self::Parameter(index) => resolver.resolve_param(*index),
46 }?
47 .ok_or_else(|| {
48 SQLError::Internal("prepared PL/pgSQL variable no longer resolves".into())
49 })?;
50 Ok(parameter(value, resolver))
51 }
52}
53fn parameter(value: ResolvedVariable, resolver: &dyn VariableResolver) -> SQLParam {
54 match value
55 .declared_type
56 .as_deref()
57 .and_then(|name| resolver.parameter_type(name))
58 {
59 Some(ty) => SQLParam::typed_scalar(value.value, ty),
60 None => SQLParam::scalar(value.value),
61 }
62}
63
64pub struct PLpgSQLVariableBindings {
65 pub plan: crate::plan::UnifiedPlan,
66 pub references: Vec<PLpgSQLVariableReference>,
67 pub parameters: Vec<SQLParam>,
68}
69
70pub fn parameterize_statement_variables(
73 statement: &Statement,
74 resolver: &mut dyn VariableResolver,
75 conflict: VariableConflict,
76 resolve_sites: VariableSiteResolver<'_>,
77 aggregates: &dyn crate::plan::AggregateClassifier,
78) -> Result<PLpgSQLVariableBindings, SQLError> {
79 let keep = statement_variable_names(statement, resolver, conflict, resolve_sites)?;
80 let mut binding = VariableBindings {
81 inner: resolver,
82 keep: &keep,
83 next_name: 0,
84 references: Vec::new(),
85 parameters: Vec::new(),
86 collations: Vec::new(),
87 };
88 let statement = bind_statement(statement, &mut binding)?;
89 let mut plan = crate::plan::UnifiedPlan::lower_with(statement, aggregates);
90 plan.rewrite_scalar_expressions(&mut |expression| {
93 let crate::ScalarExpr::Param(number) = expression else {
94 return;
95 };
96 let Some(index) = number.checked_sub(1) else {
97 return;
98 };
99 let Some(oid) = binding.collations.get(index).copied().flatten() else {
100 return;
101 };
102 let Some(ty) = binding.parameters[index].declared_scalar_type() else {
103 return;
104 };
105 if crate::schema::composites::type_is_collatable(ty) {
106 *expression = crate::ScalarExpr::Collated {
107 expr: Box::new(crate::ScalarExpr::Param(*number)),
108 ty: ty.clone(),
109 collation: crate::ir::ResultCollation::new(
110 crate::catalog::collations::derivation::Derivation::implicit(i64::from(oid)),
111 oid,
112 ),
113 };
114 }
115 });
116 Ok(PLpgSQLVariableBindings {
117 plan,
118 references: binding.references,
119 parameters: binding.parameters,
120 })
121}
122struct VariableBindings<'a> {
123 inner: &'a mut dyn VariableResolver,
124 keep: &'a [bool],
125 next_name: usize,
126 references: Vec<PLpgSQLVariableReference>,
127 parameters: Vec<SQLParam>,
128 collations: Vec<Option<u32>>,
129}
130impl VariableBindings<'_> {
131 fn mark(
132 &mut self,
133 reference: PLpgSQLVariableReference,
134 value: Option<ResolvedVariable>,
135 named: bool,
136 ) -> Option<Expr> {
137 let value = value?;
138 if named {
139 let site = self.next_name;
140 self.next_name += 1;
141 if self.keep.get(site).copied().unwrap_or(false) {
142 return None;
143 }
144 }
145 self.references.push(reference);
146 self.collations.push(value.collation_oid);
147 self.parameters.push(parameter(value, self.inner));
148 Some(Expr::Param(self.references.len()))
149 }
150}
151impl VariableResolver for VariableBindings<'_> {
152 fn resolve_name(&mut self, name: &str) -> Result<Option<ResolvedVariable>, SQLError> {
153 self.inner.resolve_name(name)
154 }
155 fn resolve_qualified(
156 &mut self,
157 qualifier: &str,
158 column: &str,
159 ) -> Result<Option<ResolvedVariable>, SQLError> {
160 self.inner.resolve_qualified(qualifier, column)
161 }
162 fn resolve_param(&mut self, index: usize) -> Result<Option<ResolvedVariable>, SQLError> {
163 self.inner.resolve_param(index)
164 }
165 fn rewrite_name(&mut self, name: &str) -> Result<Option<Expr>, SQLError> {
166 let value = self.inner.resolve_name(name)?;
167 Ok(self.mark(
168 PLpgSQLVariableReference::Name(name.to_string()),
169 value,
170 true,
171 ))
172 }
173 fn rewrite_qualified(
174 &mut self,
175 qualifier: &str,
176 column: &str,
177 ) -> Result<Option<Expr>, SQLError> {
178 let value = self.inner.resolve_qualified(qualifier, column)?;
179 Ok(self.mark(
180 PLpgSQLVariableReference::Qualified {
181 qualifier: qualifier.to_string(),
182 column: column.to_string(),
183 },
184 value,
185 true,
186 ))
187 }
188 fn rewrite_param(&mut self, index: usize) -> Result<Option<Expr>, SQLError> {
189 let value = self.inner.resolve_param(index)?;
190 Ok(self.mark(PLpgSQLVariableReference::Parameter(index), value, false))
191 }
192}
193
194#[cfg(test)]
195mod tests;