1use super::{invalid, values, Field, Node};
10use crate::SQLError;
11
12mod bodies;
13mod coercions;
14mod control;
15mod fields;
16mod operators;
17mod partition;
18mod relation;
19mod rows;
20mod subscripts;
21mod types;
22
23pub use bodies::return_body;
24pub use relation::validate_relation;
25
26#[cfg(test)]
27mod tests;
28
29pub trait ExpressionNames {
30 fn column(&self, attribute: i64) -> Result<String, SQLError>;
31 fn relation_name(&self) -> Result<String, SQLError> {
33 Err(invalid("whole-row reference requires its relation name"))
34 }
35 fn routine(&self, oid: i64) -> Result<Vec<String>, SQLError>;
36 fn type_name(&self, oid: i64, modifier: i64) -> Result<String, SQLError>;
37
38 fn collation(&self, oid: i64) -> Result<Vec<String>, SQLError> {
40 crate::catalog::collations::builtin_collation_name(oid)
41 .map(|name| vec![name.into()])
42 .ok_or_else(|| invalid("unknown collation identity"))
43 }
44
45 fn value_catalog(&self) -> Option<&dyn crate::expr::SQLValueCatalog> {
47 None
48 }
49
50 fn standard_conforming_strings(&self) -> Result<bool, SQLError> {
52 Ok(true)
53 }
54
55 fn parameter(&self, number: i64) -> Result<String, SQLError> {
57 Ok(format!("${number}"))
58 }
59}
60
61pub fn expression(
62 value: &Field,
63 names: &dyn ExpressionNames,
64 pretty: bool,
65) -> Result<String, SQLError> {
66 let renderer = Renderer {
67 names,
68 pretty,
69 indent: 0,
70 };
71 match value {
72 Field::List(values) => values
73 .iter()
74 .map(|value| renderer.field(coercions::implicit_cast_input(value)?, pretty))
75 .collect::<Result<Vec<_>, _>>()
76 .map(|values| values.join(", ")),
77 _ => renderer.field(coercions::implicit_cast_input(value)?, pretty),
78 }
79}
80
81struct Renderer<'a> {
82 names: &'a dyn ExpressionNames,
83 pretty: bool,
84 indent: usize,
85}
86
87impl Renderer<'_> {
88 fn field(&self, value: &Field, outer: bool) -> Result<String, SQLError> {
89 match value {
90 Field::Node(node) => self.node(node, outer),
91 Field::List(values) => values
92 .iter()
93 .map(|value| self.field(value, outer))
94 .collect::<Result<Vec<_>, _>>()
95 .map(|values| values.join(", ")),
96 _ => Err(invalid("expected an expression node")),
97 }
98 }
99
100 fn node(&self, node: &Node, outer: bool) -> Result<String, SQLError> {
101 match node.kind.as_str() {
102 "COERCETODOMAINVALUE" => Ok("VALUE".into()),
103 "VAR" => self.variable(node),
104 "PARAM" => self.parameter(node),
105 "SUBSCRIPTINGREF" => self.array_access(node),
106 "FIELDSELECT" => self.field_select(node),
107 "CONST" => self.constant(node),
108 "PARTITIONBOUNDSPEC" => self.partition_bound(node),
109 "COLLATEEXPR" => self.collate(node, outer),
110 "OPEXPR" | "DISTINCTEXPR" => self.operator(node, outer),
111 "ROWEXPR" => self.row(node),
112 "ROWCOMPAREEXPR" => self.row_comparison(node),
113 "NULLIFEXPR" => {
114 let [left, right] = list(node, "args")? else {
115 return Err(invalid("invalid NULLIF operands"));
116 };
117 Ok(format!(
118 "NULLIF({}, {})",
119 self.field(left, self.pretty)?,
120 self.field(right, self.pretty)?
121 ))
122 }
123 "FUNCEXPR" => self.function(node),
124 "NAMEDARGEXPR" => Ok(format!(
125 "{} => {}",
126 crate::expr::quote_ident(atom(node, "name")?),
127 self.field(node.field("arg")?, self.pretty)?
128 )),
129 "RELABELTYPE" | "COERCEVIAIO" | "COERCETODOMAIN" | "ARRAYCOERCEEXPR" => {
130 self.coercion(node, outer)
131 }
132 "BOOLEXPR" => self.boolean(node, outer),
133 "BOOLEANTEST" => self.boolean_test(node, outer),
134 "NULLTEST" => self.null_test(node, outer),
135 "COALESCEEXPR" | "MINMAXEXPR" => {
136 let name = match node.kind.as_str() {
137 "COALESCEEXPR" => "COALESCE",
138 _ => match node.integer("op")? {
139 0 => "GREATEST",
140 1 => "LEAST",
141 _ => return Err(invalid("invalid minimum/maximum expression")),
142 },
143 };
144 let arguments = list(node, "args")?
145 .iter()
146 .map(|arg| self.field(arg, self.pretty))
147 .collect::<Result<Vec<_>, _>>()?;
148 Ok(format!("{name}({})", arguments.join(", ")))
149 }
150 "ARRAYEXPR" => {
151 let elements = list(node, "elements")?
152 .iter()
153 .map(|arg| self.field(arg, self.pretty))
154 .collect::<Result<Vec<_>, _>>()?;
155 if elements.is_empty() {
156 return Ok(format!(
157 "ARRAY[]::{}",
158 self.names.type_name(node.integer("array_typeid")?, -1)?
159 ));
160 }
161 Ok(format!("ARRAY[{}]", elements.join(", ")))
162 }
163 "CASEEXPR" => self.case(node),
164 "SCALARARRAYOPEXPR" => {
165 let operator =
166 crate::type_resolution::binary_operator_by_oid(node.integer("opno")?)
167 .ok_or_else(|| invalid("unknown scalar-array operator"))?;
168 let [left, right] = list(node, "args")? else {
169 return Err(invalid("invalid scalar-array operands"));
170 };
171 let quantifier = if node.boolean("useOr")? { "ANY" } else { "ALL" };
172 Ok(parentheses(
173 format!(
174 "{} {} {quantifier} ({})",
175 self.field(left, false)?,
176 operator.name,
177 self.field(right, false)?
178 ),
179 !outer,
180 ))
181 }
182 _ => Err(SQLError::Unsupported(format!(
183 "catalog expression deparser for {}",
184 node.kind
185 ))),
186 }
187 }
188
189 fn variable(&self, node: &Node) -> Result<String, SQLError> {
190 if node.integer("varno")? != 1 || node.integer("varlevelsup")? != 0 {
191 return Err(invalid("invalid varno in stored expression"));
192 }
193 match node.integer("varattno")? {
194 0 => self
195 .names
196 .relation_name()
197 .map(|name| format!("{}.*", crate::expr::quote_ident(&name))),
198 -6 => Ok("tableoid".into()),
199 attribute => self
200 .names
201 .column(attribute)
202 .map(|name| crate::expr::quote_ident(&name)),
203 }
204 }
205
206 fn parameter(&self, node: &Node) -> Result<String, SQLError> {
207 let number = node.integer("paramid")?;
208 if node.integer("paramkind")? != 0 || number <= 0 {
209 return Err(invalid("invalid external parameter in stored expression"));
210 }
211 self.names.parameter(number)
212 }
213
214 fn collate(&self, node: &Node, outer: bool) -> Result<String, SQLError> {
215 let name = self
216 .names
217 .collation(node.integer("collOid")?)?
218 .iter()
219 .map(|name| crate::expr::quote_ident(name))
220 .collect::<Vec<_>>()
221 .join(".");
222 Ok(parentheses(
223 format!("{} COLLATE {name}", self.field(node.field("arg")?, false)?),
224 !outer,
225 ))
226 }
227
228 fn constant(&self, node: &Node) -> Result<String, SQLError> {
229 self.constant_labeled(node, true)
230 }
231
232 fn constant_labeled(&self, node: &Node, show_type: bool) -> Result<String, SQLError> {
233 let oid = node.integer("consttype")?;
234 let modifier = node.integer("consttypmod")?;
235 let type_name = || self.names.type_name(oid, modifier);
236 if node.boolean("constisnull")? {
237 return if show_type {
238 Ok(format!("NULL::{}", type_name()?))
239 } else {
240 Ok("NULL".into())
241 };
242 }
243 let Field::Datum { length, bytes } = node.field("constvalue")? else {
244 return Err(invalid("constant has no Datum"));
245 };
246 if node.boolean("constbyval")? && (bytes.len() != 8 || !matches!(*length, 1 | 2 | 4 | 8)) {
247 return Err(invalid("invalid by-value Datum length"));
248 }
249 let quoted = |value: &str| self.constant_literal(value, node, show_type);
250 match oid {
251 705 => self.constant_literal(values::unknown_text(node)?, node, false),
252 16 => match bytes.first() {
253 Some(0) => Ok("false".into()),
254 Some(1) => Ok("true".into()),
255 _ => Err(invalid("invalid boolean Datum")),
256 },
257 20 | 21 | 23 | 26 | 28 => {
258 let value = match oid {
259 21 => i64::from(i16::from_le_bytes(prefix(bytes)?)),
260 23 => i64::from(i32::from_le_bytes(prefix(bytes)?)),
261 26 | 28 => i64::from(u32::from_le_bytes(prefix(bytes)?)),
262 _ => i64::from_le_bytes(prefix(bytes)?),
263 };
264 if oid == 23 && value >= 0 {
265 Ok(value.to_string())
266 } else {
267 quoted(&value.to_string())
268 }
269 }
270 700 => quoted(&crate::expr::format_real(f32::from_le_bytes(prefix(
271 bytes,
272 )?))),
273 701 => quoted(&uqa_core::format_float_pg(f64::from_le_bytes(prefix(
274 bytes,
275 )?))),
276 18 => {
277 let byte = *bytes
278 .first()
279 .ok_or_else(|| invalid("empty character Datum"))?;
280 quoted(&crate::expr::internal_char::output_with_control(
281 byte,
282 &uqa_core::memory::ProductionControl::uncontrolled(),
283 )?)
284 }
285 19 => {
286 let end = bytes
287 .iter()
288 .position(|byte| *byte == 0)
289 .unwrap_or(bytes.len());
290 quoted(
291 std::str::from_utf8(&bytes[..end])
292 .map_err(|_| invalid("invalid name Datum"))?,
293 )
294 }
295 25 | 1042 | 1043 | 1790 => quoted(
296 std::str::from_utf8(values::varlena_payload(*length, bytes)?)
297 .map_err(|_| invalid("invalid string Datum"))?,
298 ),
299 17 | 114 | 2950 | 3802 => quoted(&values::scalar_text(node)?),
300 1700 => {
301 let text = values::numeric::decode(values::varlena_payload(*length, bytes)?)?;
302 if text.as_bytes().first().is_some_and(u8::is_ascii_digit) && text.contains('.') {
303 if modifier < 0 || !show_type {
304 Ok(text)
305 } else {
306 Ok(format!("{text}::{}", type_name()?))
307 }
308 } else {
309 quoted(&text)
310 }
311 }
312 1082 | 1083 | 1114 | 1184 | 1186 | 1266 => quoted(&crate::expr::value_to_string(
313 &uqa_core::Value::Temporal(values::temporal::decode(bytes, oid)?),
314 )?),
315 _ => match values::catalog_text(node, self.names.value_catalog())? {
316 Some(text) => quoted(&text),
317 None => Err(SQLError::Unsupported(format!(
318 "catalog expression Datum deparser for type {oid}"
319 ))),
320 },
321 }
322 }
323 fn constant_literal(
324 &self,
325 value: &str,
326 node: &Node,
327 show_type: bool,
328 ) -> Result<String, SQLError> {
329 let mut output = String::new();
330 crate::expr::append_sql_literal(
331 &mut output,
332 value,
333 self.names.standard_conforming_strings()?,
334 );
335 if show_type {
336 output.push_str("::");
337 output.push_str(
338 &self
339 .names
340 .type_name(node.integer("consttype")?, node.integer("consttypmod")?)?,
341 );
342 }
343 Ok(output)
344 }
345}
346
347fn list<'a>(node: &'a Node, name: &str) -> Result<&'a [Field], SQLError> {
348 match node.field(name)? {
349 Field::List(values) => Ok(values),
350 Field::Null => Ok(&[]),
351 _ => Err(invalid(format!("expected node list in {name}"))),
352 }
353}
354
355fn atom<'a>(node: &'a Node, name: &str) -> Result<&'a str, SQLError> {
356 match node.field(name)? {
357 Field::Atom(value) | Field::String(value) => Ok(value),
358 _ => Err(invalid(format!("expected token in {name}"))),
359 }
360}
361
362fn prefix<const N: usize>(bytes: &[u8]) -> Result<[u8; N], SQLError> {
363 bytes
364 .get(..N)
365 .and_then(|prefix| prefix.try_into().ok())
366 .ok_or_else(|| invalid("truncated constant Datum"))
367}
368
369fn parentheses(text: String, required: bool) -> String {
370 if required {
371 format!("({text})")
372 } else {
373 text
374 }
375}