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 inferred_type: None,
76 }))),
77
78 Expression::IfNull(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
79 original_name: None,
80 expressions: vec![f.this, f.expression],
81 inferred_type: None,
82 }))),
83
84 Expression::BitwiseAnd(op) => Ok(Expression::Function(Box::new(Function::new(
86 "BIT_AND".to_string(),
87 vec![op.left, op.right],
88 )))),
89
90 Expression::BitwiseOr(op) => Ok(Expression::Function(Box::new(Function::new(
91 "BIT_OR".to_string(),
92 vec![op.left, op.right],
93 )))),
94
95 Expression::BitwiseXor(op) => Ok(Expression::Function(Box::new(Function::new(
96 "BIT_XOR".to_string(),
97 vec![op.left, op.right],
98 )))),
99
100 Expression::BitwiseNot(f) => Ok(Expression::Function(Box::new(Function::new(
101 "BIT_NOT".to_string(),
102 vec![f.this],
103 )))),
104
105 Expression::BitwiseLeftShift(op) => Ok(Expression::Function(Box::new(Function::new(
106 "BIT_LSHIFT".to_string(),
107 vec![op.left, op.right],
108 )))),
109
110 Expression::BitwiseRightShift(op) => Ok(Expression::Function(Box::new(Function::new(
111 "BIT_RSHIFT".to_string(),
112 vec![op.left, op.right],
113 )))),
114
115 Expression::Mod(op) => Ok(Expression::Function(Box::new(Function::new(
117 "MOD".to_string(),
118 vec![op.left, op.right],
119 )))),
120
121 Expression::GroupConcat(f) => Ok(Expression::ListAgg(Box::new(ListAggFunc {
123 this: f.this,
124 separator: f.separator,
125 on_overflow: None,
126 order_by: f.order_by,
127 distinct: f.distinct,
128 filter: f.filter,
129 inferred_type: None,
130 }))),
131
132 Expression::Column(col)
134 if col.table.is_none() && col.name.name.eq_ignore_ascii_case("USER") =>
135 {
136 Ok(Expression::CurrentUser(Box::new(crate::expressions::CurrentUser { this: None })))
137 }
138
139 Expression::Function(f) => self.transform_function(*f),
141
142 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
144
145 _ => Ok(expr),
147 }
148 }
149}
150
151impl ExasolDialect {
152 fn transform_function(&self, f: Function) -> Result<Expression> {
153 let name_upper = f.name.to_uppercase();
154 match name_upper.as_str() {
155 "SYSTIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
159 "SYSTIMESTAMP".to_string(),
160 f.args,
161 )))),
162
163 "ALL" => Ok(Expression::Function(Box::new(Function::new(
165 "EVERY".to_string(),
166 f.args,
167 )))),
168
169 "IFNULL" | "ISNULL" | "NVL" if f.args.len() == 2 => {
171 Ok(Expression::Coalesce(Box::new(VarArgFunc {
172 original_name: None,
173 expressions: f.args,
174 inferred_type: None,
175 })))
176 }
177
178 "DATEDIFF" => Ok(Expression::Function(Box::new(Function::new(
180 "DAYS_BETWEEN".to_string(),
181 f.args,
182 )))),
183
184 "DATEADD" | "DATE_ADD" => Ok(Expression::Function(Box::new(Function::new(
186 "ADD_DAYS".to_string(),
187 f.args,
188 )))),
189
190 "DATESUB" | "DATE_SUB" => {
192 Ok(Expression::Function(Box::new(Function::new(
194 "ADD_DAYS".to_string(),
195 f.args,
196 ))))
197 }
198
199 "DATE_TRUNC" | "TRUNC" => Ok(Expression::Function(Box::new(f))),
201
202 "LEVENSHTEIN" | "LEVENSHTEIN_DISTANCE" => Ok(Expression::Function(Box::new(
204 Function::new("EDIT_DISTANCE".to_string(), f.args),
205 ))),
206
207 "REGEXP_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
209 "REGEXP_SUBSTR".to_string(),
210 f.args,
211 )))),
212
213 "SHA" | "SHA1" => Ok(Expression::Function(Box::new(Function::new(
215 "HASH_SHA".to_string(),
216 f.args,
217 )))),
218
219 "MD5" => Ok(Expression::Function(Box::new(Function::new(
221 "HASH_MD5".to_string(),
222 f.args,
223 )))),
224
225 "SHA256" | "SHA2" => {
227 let arg = f
230 .args
231 .into_iter()
232 .next()
233 .unwrap_or(Expression::Null(crate::expressions::Null));
234 Ok(Expression::Function(Box::new(Function::new(
235 "HASH_SHA256".to_string(),
236 vec![arg],
237 ))))
238 }
239
240 "SHA512" => Ok(Expression::Function(Box::new(Function::new(
242 "HASH_SHA512".to_string(),
243 f.args,
244 )))),
245
246 "VAR_POP" | "VARIANCE_POP" => Ok(Expression::Function(Box::new(Function::new(
248 "VAR_POP".to_string(),
249 f.args,
250 )))),
251
252 "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT" => Ok(Expression::Function(Box::new(
254 Function::new("APPROXIMATE_COUNT_DISTINCT".to_string(), f.args),
255 ))),
256
257 "TO_CHAR" | "DATE_FORMAT" | "STRFTIME" => Ok(Expression::Function(Box::new(
259 Function::new("TO_CHAR".to_string(), f.args),
260 ))),
261
262 "TO_DATE" => {
264 if f.args.len() >= 2 {
265 let mut new_args = f.args.clone();
267 if let Expression::Literal(Literal::String(fmt)) = &f.args[1] {
268 new_args[1] = Expression::Literal(Literal::String(
269 Self::uppercase_exasol_format(fmt),
270 ));
271 }
272 Ok(Expression::Function(Box::new(Function::new(
273 "TO_DATE".to_string(),
274 new_args,
275 ))))
276 } else {
277 Ok(Expression::Function(Box::new(f)))
278 }
279 }
280
281 "TIME_TO_STR" => {
283 if f.args.len() >= 2 {
284 let mut new_args = vec![f.args[0].clone()];
285 if let Expression::Literal(Literal::String(fmt)) = &f.args[1] {
286 new_args.push(Expression::Literal(Literal::String(
287 Self::convert_strptime_to_exasol_format(fmt),
288 )));
289 } else {
290 new_args.push(f.args[1].clone());
291 }
292 Ok(Expression::Function(Box::new(Function::new(
293 "TO_CHAR".to_string(),
294 new_args,
295 ))))
296 } else {
297 Ok(Expression::Function(Box::new(Function::new(
298 "TO_CHAR".to_string(),
299 f.args,
300 ))))
301 }
302 }
303
304 "STR_TO_TIME" => {
306 if f.args.len() >= 2 {
307 let mut new_args = vec![f.args[0].clone()];
308 if let Expression::Literal(Literal::String(fmt)) = &f.args[1] {
309 new_args.push(Expression::Literal(Literal::String(
310 Self::convert_strptime_to_exasol_format(fmt),
311 )));
312 } else {
313 new_args.push(f.args[1].clone());
314 }
315 Ok(Expression::Function(Box::new(Function::new(
316 "TO_DATE".to_string(),
317 new_args,
318 ))))
319 } else {
320 Ok(Expression::Function(Box::new(Function::new(
321 "TO_DATE".to_string(),
322 f.args,
323 ))))
324 }
325 }
326
327 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(f))),
329
330 "CONVERT_TIMEZONE" | "AT_TIME_ZONE" => Ok(Expression::Function(Box::new(
332 Function::new("CONVERT_TZ".to_string(), f.args),
333 ))),
334
335 "STRPOS" | "POSITION" | "CHARINDEX" | "LOCATE" => Ok(Expression::Function(Box::new(
337 Function::new("INSTR".to_string(), f.args),
338 ))),
339
340 "WEEK_OF_YEAR" | "WEEKOFYEAR" => Ok(Expression::Function(Box::new(Function::new(
342 "WEEK".to_string(),
343 f.args,
344 )))),
345
346 "LAST_DAY" => {
348 Ok(Expression::Function(Box::new(f)))
351 }
352
353 "CURDATE" => Ok(Expression::CurrentDate(crate::expressions::CurrentDate)),
355
356 "USER" if f.args.is_empty() => Ok(Expression::CurrentUser(Box::new(crate::expressions::CurrentUser { this: None }))),
358
359 "NOW" => Ok(Expression::CurrentTimestamp(
361 crate::expressions::CurrentTimestamp {
362 precision: None,
363 sysdate: false,
364 },
365 )),
366
367 _ => Ok(Expression::Function(Box::new(f))),
369 }
370 }
371
372 fn transform_aggregate_function(
373 &self,
374 f: Box<crate::expressions::AggregateFunction>,
375 ) -> Result<Expression> {
376 let name_upper = f.name.to_uppercase();
377 match name_upper.as_str() {
378 "ALL" | "EVERY" => Ok(Expression::Function(Box::new(Function::new(
380 "EVERY".to_string(),
381 f.args,
382 )))),
383
384 "GROUP_CONCAT" | "STRING_AGG" => Ok(Expression::Function(Box::new(Function::new(
386 "LISTAGG".to_string(),
387 f.args,
388 )))),
389
390 "LISTAGG" => Ok(Expression::AggregateFunction(f)),
392
393 "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT" => Ok(Expression::Function(Box::new(
395 Function::new("APPROXIMATE_COUNT_DISTINCT".to_string(), f.args),
396 ))),
397
398 _ => Ok(Expression::AggregateFunction(f)),
400 }
401 }
402
403 fn convert_strptime_to_exasol_format(format: &str) -> String {
407 let mut result = String::new();
408 let chars: Vec<char> = format.chars().collect();
409 let mut i = 0;
410 while i < chars.len() {
411 if chars[i] == '%' && i + 1 < chars.len() {
412 let spec = chars[i + 1];
413 let exasol_spec = match spec {
414 'Y' => "YYYY",
415 'y' => "YY",
416 'm' => "MM",
417 'd' => "DD",
418 'H' => "HH",
419 'M' => "MI",
420 'S' => "SS",
421 'a' => "DY", 'A' => "DAY", 'b' => "MON", 'B' => "MONTH", 'I' => "H12", 'u' => "ID", 'V' => "IW", 'G' => "IYYY", 'W' => "UW", 'U' => "UW", 'z' => "Z", _ => {
433 result.push('%');
435 result.push(spec);
436 i += 2;
437 continue;
438 }
439 };
440 result.push_str(exasol_spec);
441 i += 2;
442 } else {
443 result.push(chars[i]);
444 i += 1;
445 }
446 }
447 result
448 }
449
450 fn uppercase_exasol_format(format: &str) -> String {
453 format.to_uppercase()
455 }
456}
457
458