uqa_sql/routines/
definition_output.rs1use crate::semantics::parameters::{catalog::find_parameter, definition::ParameterFlags};
10use crate::SQLError;
11
12pub fn source_clause(source: &str, procedure: bool) -> String {
14 let mut delimiter = if procedure {
15 "$procedure".to_owned()
16 } else {
17 "$function".to_owned()
18 };
19 while source.contains(delimiter.as_str()) {
20 delimiter.push('x');
21 }
22 delimiter.push('$');
23 format!("AS {delimiter}{source}{delimiter}")
24}
25
26pub fn estimate(value: f32) -> String {
28 if value.is_nan() {
29 return "NaN".into();
30 }
31 let sign = if value.is_sign_negative() { "-" } else { "" };
32 if value.is_infinite() {
33 return format!("{sign}Infinity");
34 }
35 if value == 0.0 {
36 return format!("{sign}0");
37 }
38
39 let scientific = format!("{:.5e}", f64::from(value.abs()));
42 let (mantissa, exponent) = scientific.split_once('e').expect("scientific estimate");
43 let exponent: i32 = exponent.parse().expect("scientific exponent");
44 if !(-4..6).contains(&exponent) {
45 let mantissa = mantissa.trim_end_matches('0').trim_end_matches('.');
46 return format!("{sign}{mantissa}e{exponent:+03}");
47 }
48
49 let digits = mantissa.replace('.', "");
50 let mut output = if exponent < 0 {
51 format!("{sign}0.{}{digits}", "0".repeat((-exponent - 1) as usize))
52 } else {
53 let (integer, fraction) = digits.split_at(exponent as usize + 1);
54 if fraction.is_empty() {
55 return format!("{sign}{integer}");
56 }
57 format!("{sign}{integer}.{fraction}")
58 };
59 while output.ends_with('0') {
60 output.pop();
61 }
62 if output.ends_with('.') {
63 output.pop();
64 }
65 output
66}
67
68pub fn configuration_clauses(
70 config: &[(String, String)],
71 standard_strings: bool,
72) -> Result<String, SQLError> {
73 let mut output = String::new();
74 for (name, value) in config {
75 output.push_str(" SET ");
76 output.push_str(&crate::expr::quote_ident(name));
77 output.push_str(" TO ");
78 if find_parameter(name)
79 .is_some_and(|definition| definition.has_flag(ParameterFlags::LIST_QUOTE))
80 {
81 let values = configuration_list(value).ok_or_else(|| SQLError::Routine {
82 sqlstate: "XX000".into(),
83 message: "invalid list syntax in proconfig item".into(),
84 })?;
85 for (index, value) in values.iter().enumerate() {
86 if index > 0 {
87 output.push_str(", ");
88 }
89 append_literal(&mut output, value, standard_strings);
90 }
91 } else {
92 append_literal(&mut output, value, standard_strings);
93 }
94 output.push('\n');
95 }
96 Ok(output)
97}
98
99fn append_literal(output: &mut String, value: &str, standard_strings: bool) {
100 output.push('\'');
101 for character in value.chars() {
102 if character == '\'' || (character == '\\' && !standard_strings) {
103 output.push(character);
104 }
105 output.push(character);
106 }
107 output.push('\'');
108}
109
110fn configuration_list(value: &str) -> Option<Vec<String>> {
114 let mut input = value.chars().peekable();
115 let mut values = Vec::new();
116 while input.peek().copied().is_some_and(list_space) {
117 input.next();
118 }
119 if input.peek().is_none() {
120 return Some(values);
121 }
122 loop {
123 let mut value = String::new();
124 if input.peek() == Some(&'"') {
125 input.next();
126 loop {
127 match input.next()? {
128 '"' if input.peek() == Some(&'"') => {
129 input.next();
130 value.push('"');
131 }
132 '"' => break,
133 character => value.push(character),
134 }
135 }
136 } else {
137 while input
138 .peek()
139 .copied()
140 .is_some_and(|character| character != ',' && !list_space(character))
141 {
142 value.push(input.next()?);
143 }
144 if value.is_empty() {
145 return None;
146 }
147 }
148 while input.peek().copied().is_some_and(list_space) {
149 input.next();
150 }
151 values.push(value);
152 match input.next() {
153 None => return Some(values),
154 Some(',') => {
155 while input.peek().copied().is_some_and(list_space) {
156 input.next();
157 }
158 }
159 Some(_) => return None,
160 }
161 }
162}
163
164const fn list_space(character: char) -> bool {
165 matches!(character, ' ' | '\t' | '\n' | '\r' | '\u{b}' | '\u{c}')
166}
167
168#[cfg(test)]
169mod tests;