1use std::fmt::Write;
10
11use crate::ast::ColumnType;
12use crate::expr::{format_regtype_value, value_to_string, vector_value_to_string, EngineHook};
13use crate::SQLError;
14use uqa_core::Value;
15
16pub fn format_postgres_text(
18 value: &Value,
19 ty: &ColumnType,
20 engine: Option<&dyn EngineHook>,
21) -> Result<String, SQLError> {
22 if let ColumnType::Domain { base, .. } = ty {
23 return format_postgres_text(value, base, engine);
24 }
25 if let Some(text) = format_regtype_value(value, ty, engine)? {
26 return Ok(text);
27 }
28 if matches!(ty, ColumnType::Int2Vector | ColumnType::OidVector) {
29 return vector_value_to_string(value)
30 .ok_or_else(|| SQLError::Internal("invalid catalog vector result carrier".into()));
31 }
32 if let ColumnType::Array(element) = ty {
33 return format_array(value, element, engine);
34 }
35 Ok(match value {
36 Value::Bool(value) => if *value { "t" } else { "f" }.into(),
37 Value::FixedChar(value) => value.clone(),
38 Value::Float(value) if matches!(ty, ColumnType::Real) => {
39 crate::expr::format_real(*value as f32)
40 }
41 Value::Float(value) => uqa_core::format_float_pg(*value),
42 _ => value_to_string(value),
43 })
44}
45
46fn format_array(
47 value: &Value,
48 element: &ColumnType,
49 engine: Option<&dyn EngineHook>,
50) -> Result<String, SQLError> {
51 let mut element = element;
54 while let ColumnType::Array(inner) = element {
55 element = inner;
56 }
57 let (values, prefix) = match value {
58 Value::Array(array) => {
59 let mut prefix = String::new();
60 if array.lower_bounds().iter().any(|lower| *lower != 1) {
61 for (lower, length) in array.lower_bounds().iter().zip(array.dimensions()) {
62 let upper = i64::from(*lower) + *length as i64 - 1;
63 write!(prefix, "[{lower}:{upper}]").expect("writing to String cannot fail");
64 }
65 prefix.push('=');
66 }
67 (array.elements(), prefix)
68 }
69 Value::List(values) => (values.as_slice(), String::new()),
70 _ => return Err(SQLError::Internal("invalid array result carrier".into())),
71 };
72 let mut fields = Vec::with_capacity(values.len());
73 for value in values {
74 fields.push(match value {
75 Value::Null => "NULL".into(),
76 Value::List(_) | Value::Array(_) => format_array(value, element, engine)?,
77 _ => {
78 let text = format_postgres_text(value, element, engine)?;
79 if text.is_empty()
80 || text.eq_ignore_ascii_case("null")
81 || text.chars().any(|c| {
82 c.is_ascii_whitespace() || matches!(c, ',' | '{' | '}' | '"' | '\\')
83 })
84 {
85 format!("\"{}\"", text.replace('\\', "\\\\").replace('"', "\\\""))
86 } else {
87 text
88 }
89 }
90 });
91 }
92 Ok(format!("{prefix}{{{}}}", fields.join(",")))
93}