1use super::{DialectImpl, DialectType};
22use crate::error::Result;
23use crate::expressions::{Expression, Function, ListAggFunc, Literal, VarArgFunc};
24use crate::generator::GeneratorConfig;
25use crate::tokens::TokenizerConfig;
26
27pub struct ExasolDialect;
29
30impl DialectImpl for ExasolDialect {
31 fn dialect_type(&self) -> DialectType {
32 DialectType::Exasol
33 }
34
35 fn tokenizer_config(&self) -> TokenizerConfig {
36 let mut config = TokenizerConfig::default();
37 config.identifiers.insert('"', '"');
39 config.identifiers.insert('[', ']');
41 config
42 }
43
44 fn generator_config(&self) -> GeneratorConfig {
45 use crate::generator::IdentifierQuoteStyle;
46 GeneratorConfig {
47 identifier_quote: '"',
48 identifier_quote_style: IdentifierQuoteStyle::DOUBLE_QUOTE,
49 dialect: Some(DialectType::Exasol),
50 supports_column_join_marks: true,
51 lowercase_window_frame_keywords: true,
53 ..Default::default()
54 }
55 }
56
57 fn transform_expr(&self, expr: Expression) -> Result<Expression> {
58 match expr {
59 Expression::Systimestamp(_) => Ok(Expression::Function(Box::new(Function::new(
61 "SYSTIMESTAMP".to_string(),
62 vec![],
63 )))),
64
65 Expression::WeekOfYear(f) => Ok(Expression::Function(Box::new(Function::new(
67 "WEEK".to_string(),
68 vec![f.this],
69 )))),
70
71 Expression::Nvl(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
73 original_name: None,
74 expressions: vec![f.this, f.expression],
75 }))),
76
77 Expression::IfNull(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
78 original_name: None,
79 expressions: vec![f.this, f.expression],
80 }))),
81
82 Expression::BitwiseAnd(op) => Ok(Expression::Function(Box::new(Function::new(
84 "BIT_AND".to_string(),
85 vec![op.left, op.right],
86 )))),
87
88 Expression::BitwiseOr(op) => Ok(Expression::Function(Box::new(Function::new(
89 "BIT_OR".to_string(),
90 vec![op.left, op.right],
91 )))),
92
93 Expression::BitwiseXor(op) => Ok(Expression::Function(Box::new(Function::new(
94 "BIT_XOR".to_string(),
95 vec![op.left, op.right],
96 )))),
97
98 Expression::BitwiseNot(f) => Ok(Expression::Function(Box::new(Function::new(
99 "BIT_NOT".to_string(),
100 vec![f.this],
101 )))),
102
103 Expression::BitwiseLeftShift(op) => Ok(Expression::Function(Box::new(Function::new(
104 "BIT_LSHIFT".to_string(),
105 vec![op.left, op.right],
106 )))),
107
108 Expression::BitwiseRightShift(op) => Ok(Expression::Function(Box::new(Function::new(
109 "BIT_RSHIFT".to_string(),
110 vec![op.left, op.right],
111 )))),
112
113 Expression::Mod(op) => Ok(Expression::Function(Box::new(Function::new(
115 "MOD".to_string(),
116 vec![op.left, op.right],
117 )))),
118
119 Expression::GroupConcat(f) => Ok(Expression::ListAgg(Box::new(ListAggFunc {
121 this: f.this,
122 separator: f.separator,
123 on_overflow: None,
124 order_by: f.order_by,
125 distinct: f.distinct,
126 filter: f.filter,
127 }))),
128
129 Expression::Function(f) => self.transform_function(*f),
131
132 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
134
135 _ => Ok(expr),
137 }
138 }
139}
140
141impl ExasolDialect {
142 fn transform_function(&self, f: Function) -> Result<Expression> {
143 let name_upper = f.name.to_uppercase();
144 match name_upper.as_str() {
145 "SYSTIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
149 "SYSTIMESTAMP".to_string(),
150 f.args,
151 )))),
152
153 "ALL" => Ok(Expression::Function(Box::new(Function::new(
155 "EVERY".to_string(),
156 f.args,
157 )))),
158
159 "IFNULL" | "ISNULL" | "NVL" if f.args.len() == 2 => {
161 Ok(Expression::Coalesce(Box::new(VarArgFunc {
162 original_name: None,
163 expressions: f.args,
164 })))
165 }
166
167 "DATEDIFF" => Ok(Expression::Function(Box::new(Function::new(
169 "DAYS_BETWEEN".to_string(),
170 f.args,
171 )))),
172
173 "DATEADD" | "DATE_ADD" => Ok(Expression::Function(Box::new(Function::new(
175 "ADD_DAYS".to_string(),
176 f.args,
177 )))),
178
179 "DATESUB" | "DATE_SUB" => {
181 Ok(Expression::Function(Box::new(Function::new(
183 "ADD_DAYS".to_string(),
184 f.args,
185 ))))
186 }
187
188 "DATE_TRUNC" | "TRUNC" => Ok(Expression::Function(Box::new(f))),
190
191 "LEVENSHTEIN" | "LEVENSHTEIN_DISTANCE" => Ok(Expression::Function(Box::new(
193 Function::new("EDIT_DISTANCE".to_string(), f.args),
194 ))),
195
196 "REGEXP_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
198 "REGEXP_SUBSTR".to_string(),
199 f.args,
200 )))),
201
202 "SHA" | "SHA1" => Ok(Expression::Function(Box::new(Function::new(
204 "HASH_SHA".to_string(),
205 f.args,
206 )))),
207
208 "MD5" => Ok(Expression::Function(Box::new(Function::new(
210 "HASH_MD5".to_string(),
211 f.args,
212 )))),
213
214 "SHA256" | "SHA2" => {
216 let arg = f
219 .args
220 .into_iter()
221 .next()
222 .unwrap_or(Expression::Null(crate::expressions::Null));
223 Ok(Expression::Function(Box::new(Function::new(
224 "HASH_SHA256".to_string(),
225 vec![arg],
226 ))))
227 }
228
229 "SHA512" => Ok(Expression::Function(Box::new(Function::new(
231 "HASH_SHA512".to_string(),
232 f.args,
233 )))),
234
235 "VAR_POP" | "VARIANCE_POP" => Ok(Expression::Function(Box::new(Function::new(
237 "VAR_POP".to_string(),
238 f.args,
239 )))),
240
241 "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT" => Ok(Expression::Function(Box::new(
243 Function::new("APPROXIMATE_COUNT_DISTINCT".to_string(), f.args),
244 ))),
245
246 "TO_CHAR" | "DATE_FORMAT" | "STRFTIME" => Ok(Expression::Function(Box::new(
248 Function::new("TO_CHAR".to_string(), f.args),
249 ))),
250
251 "TO_DATE" => {
253 if f.args.len() >= 2 {
254 let mut new_args = f.args.clone();
256 if let Expression::Literal(Literal::String(fmt)) = &f.args[1] {
257 new_args[1] = Expression::Literal(Literal::String(
258 Self::uppercase_exasol_format(fmt),
259 ));
260 }
261 Ok(Expression::Function(Box::new(Function::new(
262 "TO_DATE".to_string(),
263 new_args,
264 ))))
265 } else {
266 Ok(Expression::Function(Box::new(f)))
267 }
268 }
269
270 "TIME_TO_STR" => {
272 if f.args.len() >= 2 {
273 let mut new_args = vec![f.args[0].clone()];
274 if let Expression::Literal(Literal::String(fmt)) = &f.args[1] {
275 new_args.push(Expression::Literal(Literal::String(
276 Self::convert_strptime_to_exasol_format(fmt),
277 )));
278 } else {
279 new_args.push(f.args[1].clone());
280 }
281 Ok(Expression::Function(Box::new(Function::new(
282 "TO_CHAR".to_string(),
283 new_args,
284 ))))
285 } else {
286 Ok(Expression::Function(Box::new(Function::new(
287 "TO_CHAR".to_string(),
288 f.args,
289 ))))
290 }
291 }
292
293 "STR_TO_TIME" => {
295 if f.args.len() >= 2 {
296 let mut new_args = vec![f.args[0].clone()];
297 if let Expression::Literal(Literal::String(fmt)) = &f.args[1] {
298 new_args.push(Expression::Literal(Literal::String(
299 Self::convert_strptime_to_exasol_format(fmt),
300 )));
301 } else {
302 new_args.push(f.args[1].clone());
303 }
304 Ok(Expression::Function(Box::new(Function::new(
305 "TO_DATE".to_string(),
306 new_args,
307 ))))
308 } else {
309 Ok(Expression::Function(Box::new(Function::new(
310 "TO_DATE".to_string(),
311 f.args,
312 ))))
313 }
314 }
315
316 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(f))),
318
319 "CONVERT_TIMEZONE" | "AT_TIME_ZONE" => Ok(Expression::Function(Box::new(
321 Function::new("CONVERT_TZ".to_string(), f.args),
322 ))),
323
324 "STRPOS" | "POSITION" | "CHARINDEX" | "LOCATE" => Ok(Expression::Function(Box::new(
326 Function::new("INSTR".to_string(), f.args),
327 ))),
328
329 "WEEK_OF_YEAR" | "WEEKOFYEAR" => Ok(Expression::Function(Box::new(Function::new(
331 "WEEK".to_string(),
332 f.args,
333 )))),
334
335 "LAST_DAY" => {
337 Ok(Expression::Function(Box::new(f)))
340 }
341
342 _ => Ok(Expression::Function(Box::new(f))),
344 }
345 }
346
347 fn transform_aggregate_function(
348 &self,
349 f: Box<crate::expressions::AggregateFunction>,
350 ) -> Result<Expression> {
351 let name_upper = f.name.to_uppercase();
352 match name_upper.as_str() {
353 "ALL" | "EVERY" => Ok(Expression::Function(Box::new(Function::new(
355 "EVERY".to_string(),
356 f.args,
357 )))),
358
359 "GROUP_CONCAT" | "STRING_AGG" => Ok(Expression::Function(Box::new(Function::new(
361 "LISTAGG".to_string(),
362 f.args,
363 )))),
364
365 "LISTAGG" => Ok(Expression::AggregateFunction(f)),
367
368 "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT" => Ok(Expression::Function(Box::new(
370 Function::new("APPROXIMATE_COUNT_DISTINCT".to_string(), f.args),
371 ))),
372
373 _ => Ok(Expression::AggregateFunction(f)),
375 }
376 }
377
378 fn convert_strptime_to_exasol_format(format: &str) -> String {
382 let mut result = String::new();
383 let chars: Vec<char> = format.chars().collect();
384 let mut i = 0;
385 while i < chars.len() {
386 if chars[i] == '%' && i + 1 < chars.len() {
387 let spec = chars[i + 1];
388 let exasol_spec = match spec {
389 'Y' => "YYYY",
390 'y' => "YY",
391 'm' => "MM",
392 'd' => "DD",
393 'H' => "HH",
394 'M' => "MI",
395 'S' => "SS",
396 'a' => "DY", 'A' => "DAY", 'b' => "MON", 'B' => "MONTH", 'I' => "H12", 'u' => "ID", 'V' => "IW", 'G' => "IYYY", 'W' => "UW", 'U' => "UW", 'z' => "Z", _ => {
408 result.push('%');
410 result.push(spec);
411 i += 2;
412 continue;
413 }
414 };
415 result.push_str(exasol_spec);
416 i += 2;
417 } else {
418 result.push(chars[i]);
419 i += 1;
420 }
421 }
422 result
423 }
424
425 fn uppercase_exasol_format(format: &str) -> String {
428 format.to_uppercase()
430 }
431}
432
433