1use super::{DialectImpl, DialectType};
22use crate::error::Result;
23use crate::expressions::{
24 BinaryOp, Expression, Function, Identifier, LikeOp, ListAggFunc, Literal, Select, VarArgFunc,
25};
26#[cfg(feature = "generate")]
27use crate::generator::GeneratorConfig;
28use crate::tokens::TokenizerConfig;
29
30pub struct ExasolDialect;
32
33impl DialectImpl for ExasolDialect {
34 fn dialect_type(&self) -> DialectType {
35 DialectType::Exasol
36 }
37
38 fn tokenizer_config(&self) -> TokenizerConfig {
39 let mut config = TokenizerConfig::default();
40 config.identifiers.insert('"', '"');
42 config.identifiers.insert('[', ']');
44 config
45 }
46
47 #[cfg(feature = "generate")]
48
49 fn generator_config(&self) -> GeneratorConfig {
50 use crate::generator::IdentifierQuoteStyle;
51 GeneratorConfig {
52 identifier_quote: '"',
53 identifier_quote_style: IdentifierQuoteStyle::DOUBLE_QUOTE,
54 dialect: Some(DialectType::Exasol),
55 supports_column_join_marks: true,
56 lowercase_window_frame_keywords: true,
58 ..Default::default()
59 }
60 }
61
62 #[cfg(feature = "transpile")]
63
64 fn transform_expr(&self, expr: Expression) -> Result<Expression> {
65 match expr {
66 Expression::Systimestamp(_) => Ok(Expression::Function(Box::new(Function::new(
68 "SYSTIMESTAMP".to_string(),
69 vec![],
70 )))),
71
72 Expression::WeekOfYear(f) => Ok(Expression::Function(Box::new(Function::new(
74 "WEEK".to_string(),
75 vec![f.this],
76 )))),
77
78 Expression::Nvl(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
80 original_name: None,
81 expressions: vec![f.this, f.expression],
82 inferred_type: None,
83 }))),
84
85 Expression::IfNull(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
86 original_name: None,
87 expressions: vec![f.this, f.expression],
88 inferred_type: None,
89 }))),
90
91 Expression::BitwiseAnd(op) => Ok(Expression::Function(Box::new(Function::new(
93 "BIT_AND".to_string(),
94 vec![op.left, op.right],
95 )))),
96
97 Expression::BitwiseOr(op) => Ok(Expression::Function(Box::new(Function::new(
98 "BIT_OR".to_string(),
99 vec![op.left, op.right],
100 )))),
101
102 Expression::BitwiseXor(op) => Ok(Expression::Function(Box::new(Function::new(
103 "BIT_XOR".to_string(),
104 vec![op.left, op.right],
105 )))),
106
107 Expression::BitwiseNot(f) => Ok(Expression::Function(Box::new(Function::new(
108 "BIT_NOT".to_string(),
109 vec![f.this],
110 )))),
111
112 Expression::BitwiseLeftShift(op) => Ok(Expression::Function(Box::new(Function::new(
113 "BIT_LSHIFT".to_string(),
114 vec![op.left, op.right],
115 )))),
116
117 Expression::BitwiseRightShift(op) => Ok(Expression::Function(Box::new(Function::new(
118 "BIT_RSHIFT".to_string(),
119 vec![op.left, op.right],
120 )))),
121
122 Expression::Mod(op) => Ok(Expression::Function(Box::new(Function::new(
124 "MOD".to_string(),
125 vec![op.left, op.right],
126 )))),
127
128 Expression::GroupConcat(f) => Ok(Expression::ListAgg(Box::new(ListAggFunc {
130 source_dialect: None,
131 this: f.this,
132 separator: f.separator,
133 on_overflow: None,
134 max_length: None,
135 order_by: f.order_by,
136 distinct: f.distinct,
137 filter: f.filter,
138 inferred_type: None,
139 }))),
140
141 Expression::Column(col)
143 if col.table.is_none() && col.name.name.eq_ignore_ascii_case("USER") =>
144 {
145 Ok(Expression::CurrentUser(Box::new(
146 crate::expressions::CurrentUser { this: None },
147 )))
148 }
149
150 Expression::Function(f) => self.transform_function(*f),
152
153 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
155
156 Expression::Select(select) => Ok(Expression::Select(Box::new(
157 self.qualify_local_alias_predicates(*select),
158 ))),
159
160 _ => Ok(expr),
162 }
163 }
164}
165
166#[cfg(feature = "transpile")]
167impl ExasolDialect {
168 fn qualify_local_alias_predicates(&self, mut select: Select) -> Select {
169 let aliases = Self::select_aliases(&select);
170 if aliases.is_empty() {
171 return select;
172 }
173
174 if let Some(where_clause) = select.where_clause.as_mut() {
175 where_clause.this = Self::qualify_local_alias_expr(where_clause.this.clone(), &aliases);
176 }
177 if let Some(having) = select.having.as_mut() {
178 having.this = Self::qualify_local_alias_expr(having.this.clone(), &aliases);
179 }
180
181 select
182 }
183
184 fn select_aliases(select: &Select) -> std::collections::HashMap<String, Identifier> {
185 let mut aliases = std::collections::HashMap::new();
186 for expression in &select.expressions {
187 if let Expression::Alias(alias) = expression {
188 aliases.insert(alias.alias.name.to_ascii_uppercase(), alias.alias.clone());
189 }
190 }
191 aliases
192 }
193
194 fn qualify_local_alias_expr(
195 expr: Expression,
196 aliases: &std::collections::HashMap<String, Identifier>,
197 ) -> Expression {
198 match expr {
199 Expression::Column(col) if col.table.is_none() => {
200 if let Some(alias) = aliases.get(&col.name.name.to_ascii_uppercase()) {
201 return Expression::Raw(crate::expressions::Raw {
202 sql: format!("LOCAL.{}", alias.name),
203 });
204 }
205 Expression::Column(col)
206 }
207 Expression::Identifier(id) => {
208 if let Some(alias) = aliases.get(&id.name.to_ascii_uppercase()) {
209 Expression::Raw(crate::expressions::Raw {
210 sql: format!("LOCAL.{}", alias.name),
211 })
212 } else {
213 Expression::Identifier(id)
214 }
215 }
216 Expression::And(op) => Self::qualify_binary(op, aliases, Expression::And),
217 Expression::Or(op) => Self::qualify_binary(op, aliases, Expression::Or),
218 Expression::Eq(op) => Self::qualify_binary(op, aliases, Expression::Eq),
219 Expression::Neq(op) => Self::qualify_binary(op, aliases, Expression::Neq),
220 Expression::Lt(op) => Self::qualify_binary(op, aliases, Expression::Lt),
221 Expression::Lte(op) => Self::qualify_binary(op, aliases, Expression::Lte),
222 Expression::Gt(op) => Self::qualify_binary(op, aliases, Expression::Gt),
223 Expression::Gte(op) => Self::qualify_binary(op, aliases, Expression::Gte),
224 Expression::Like(op) => Self::qualify_like(op, aliases, Expression::Like),
225 Expression::ILike(op) => Self::qualify_like(op, aliases, Expression::ILike),
226 Expression::Not(mut op) => {
227 op.this = Self::qualify_local_alias_expr(op.this, aliases);
228 Expression::Not(op)
229 }
230 other => other,
231 }
232 }
233
234 fn qualify_binary(
235 mut op: Box<BinaryOp>,
236 aliases: &std::collections::HashMap<String, Identifier>,
237 wrap: fn(Box<BinaryOp>) -> Expression,
238 ) -> Expression {
239 op.left = Self::qualify_local_alias_expr(op.left, aliases);
240 op.right = Self::qualify_local_alias_expr(op.right, aliases);
241 wrap(op)
242 }
243
244 fn qualify_like(
245 mut op: Box<LikeOp>,
246 aliases: &std::collections::HashMap<String, Identifier>,
247 wrap: fn(Box<LikeOp>) -> Expression,
248 ) -> Expression {
249 op.left = Self::qualify_local_alias_expr(op.left, aliases);
250 op.right = Self::qualify_local_alias_expr(op.right, aliases);
251 if let Some(escape) = op.escape.take() {
252 op.escape = Some(Self::qualify_local_alias_expr(escape, aliases));
253 }
254 wrap(op)
255 }
256
257 fn transform_function(&self, f: Function) -> Result<Expression> {
258 let name_upper = f.name.to_uppercase();
259 match name_upper.as_str() {
260 "SYSTIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
264 "SYSTIMESTAMP".to_string(),
265 f.args,
266 )))),
267
268 "ALL" => Ok(Expression::Function(Box::new(Function::new(
270 "EVERY".to_string(),
271 f.args,
272 )))),
273
274 "IFNULL" | "ISNULL" | "NVL" if f.args.len() == 2 => {
276 Ok(Expression::Coalesce(Box::new(VarArgFunc {
277 original_name: None,
278 expressions: f.args,
279 inferred_type: None,
280 })))
281 }
282
283 "DATEDIFF" => Ok(Expression::Function(Box::new(Function::new(
285 "DAYS_BETWEEN".to_string(),
286 f.args,
287 )))),
288
289 "DATEADD" | "DATE_ADD" => Ok(Expression::Function(Box::new(Function::new(
291 "ADD_DAYS".to_string(),
292 f.args,
293 )))),
294
295 "DATESUB" | "DATE_SUB" => {
297 Ok(Expression::Function(Box::new(Function::new(
299 "ADD_DAYS".to_string(),
300 f.args,
301 ))))
302 }
303
304 "DATE_TRUNC" | "TRUNC" => Ok(Expression::Function(Box::new(f))),
306
307 "LEVENSHTEIN" | "LEVENSHTEIN_DISTANCE" => Ok(Expression::Function(Box::new(
309 Function::new("EDIT_DISTANCE".to_string(), f.args),
310 ))),
311
312 "REGEXP_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
314 "REGEXP_SUBSTR".to_string(),
315 f.args,
316 )))),
317
318 "SHA" | "SHA1" => Ok(Expression::Function(Box::new(Function::new(
320 "HASH_SHA".to_string(),
321 f.args,
322 )))),
323
324 "MD5" => Ok(Expression::Function(Box::new(Function::new(
326 "HASH_MD5".to_string(),
327 f.args,
328 )))),
329
330 "SHA256" | "SHA2" => {
332 let arg = f
335 .args
336 .into_iter()
337 .next()
338 .unwrap_or(Expression::Null(crate::expressions::Null));
339 Ok(Expression::Function(Box::new(Function::new(
340 "HASH_SHA256".to_string(),
341 vec![arg],
342 ))))
343 }
344
345 "SHA512" => Ok(Expression::Function(Box::new(Function::new(
347 "HASH_SHA512".to_string(),
348 f.args,
349 )))),
350
351 "VAR_POP" | "VARIANCE_POP" => Ok(Expression::Function(Box::new(Function::new(
353 "VAR_POP".to_string(),
354 f.args,
355 )))),
356
357 "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT" => Ok(Expression::Function(Box::new(
359 Function::new("APPROXIMATE_COUNT_DISTINCT".to_string(), f.args),
360 ))),
361
362 "TO_CHAR" | "DATE_FORMAT" | "STRFTIME" => {
365 let mut args = f.args;
366 if args.len() >= 2 {
367 if let Expression::Literal(lit) = &args[1] {
369 if let Literal::String(fmt) = lit.as_ref() {
370 let exasol_fmt = Self::convert_c_format_to_exasol(fmt);
371 args[1] = Expression::Literal(Box::new(Literal::String(exasol_fmt)));
372 }
373 }
374 if matches!(&args[0], Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)))
376 && (f.name.eq_ignore_ascii_case("DATE_FORMAT")
377 || f.name.eq_ignore_ascii_case("STRFTIME"))
378 {
379 args[0] = Expression::Cast(Box::new(crate::expressions::Cast {
380 this: args[0].clone(),
381 to: crate::expressions::DataType::Timestamp {
382 timezone: false,
383 precision: None,
384 },
385 trailing_comments: vec![],
386 double_colon_syntax: false,
387 format: None,
388 default: None,
389 inferred_type: None,
390 }));
391 }
392 }
393 Ok(Expression::Function(Box::new(Function::new(
394 "TO_CHAR".to_string(),
395 args,
396 ))))
397 }
398
399 "TO_DATE" => {
401 if f.args.len() >= 2 {
402 let mut new_args = f.args.clone();
404 if let Expression::Literal(lit) = &f.args[1] {
405 if let Literal::String(fmt) = lit.as_ref() {
406 new_args[1] = Expression::Literal(Box::new(Literal::String(
407 Self::uppercase_exasol_format(fmt),
408 )));
409 }
410 }
411 Ok(Expression::Function(Box::new(Function::new(
412 "TO_DATE".to_string(),
413 new_args,
414 ))))
415 } else {
416 Ok(Expression::Function(Box::new(f)))
417 }
418 }
419
420 "TIME_TO_STR" => {
422 if f.args.len() >= 2 {
423 let mut new_args = vec![f.args[0].clone()];
424 if let Expression::Literal(lit) = &f.args[1] {
425 if let Literal::String(fmt) = lit.as_ref() {
426 new_args.push(Expression::Literal(Box::new(Literal::String(
427 Self::convert_strptime_to_exasol_format(fmt),
428 ))));
429 }
430 } else {
431 new_args.push(f.args[1].clone());
432 }
433 Ok(Expression::Function(Box::new(Function::new(
434 "TO_CHAR".to_string(),
435 new_args,
436 ))))
437 } else {
438 Ok(Expression::Function(Box::new(Function::new(
439 "TO_CHAR".to_string(),
440 f.args,
441 ))))
442 }
443 }
444
445 "STR_TO_TIME" => {
447 if f.args.len() >= 2 {
448 let mut new_args = vec![f.args[0].clone()];
449 if let Expression::Literal(lit) = &f.args[1] {
450 if let Literal::String(fmt) = lit.as_ref() {
451 new_args.push(Expression::Literal(Box::new(Literal::String(
452 Self::convert_strptime_to_exasol_format(fmt),
453 ))));
454 }
455 } else {
456 new_args.push(f.args[1].clone());
457 }
458 Ok(Expression::Function(Box::new(Function::new(
459 "TO_DATE".to_string(),
460 new_args,
461 ))))
462 } else {
463 Ok(Expression::Function(Box::new(Function::new(
464 "TO_DATE".to_string(),
465 f.args,
466 ))))
467 }
468 }
469
470 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(f))),
472
473 "CONVERT_TIMEZONE" | "AT_TIME_ZONE" => Ok(Expression::Function(Box::new(
475 Function::new("CONVERT_TZ".to_string(), f.args),
476 ))),
477
478 "STRPOS" | "POSITION" | "CHARINDEX" | "LOCATE" => Ok(Expression::Function(Box::new(
480 Function::new("INSTR".to_string(), f.args),
481 ))),
482
483 "WEEK_OF_YEAR" | "WEEKOFYEAR" => Ok(Expression::Function(Box::new(Function::new(
485 "WEEK".to_string(),
486 f.args,
487 )))),
488
489 "LAST_DAY" => {
491 Ok(Expression::Function(Box::new(f)))
494 }
495
496 "CURDATE" => Ok(Expression::CurrentDate(crate::expressions::CurrentDate)),
498
499 "USER" if f.args.is_empty() => Ok(Expression::CurrentUser(Box::new(
501 crate::expressions::CurrentUser { this: None },
502 ))),
503
504 "NOW" => Ok(Expression::CurrentTimestamp(
506 crate::expressions::CurrentTimestamp {
507 precision: None,
508 sysdate: false,
509 },
510 )),
511
512 _ => Ok(Expression::Function(Box::new(f))),
514 }
515 }
516
517 fn transform_aggregate_function(
518 &self,
519 f: Box<crate::expressions::AggregateFunction>,
520 ) -> Result<Expression> {
521 let name_upper = f.name.to_uppercase();
522 match name_upper.as_str() {
523 "ALL" | "EVERY" => Ok(Expression::Function(Box::new(Function::new(
525 "EVERY".to_string(),
526 f.args,
527 )))),
528
529 "GROUP_CONCAT" | "STRING_AGG" => Ok(Expression::Function(Box::new(Function::new(
531 "LISTAGG".to_string(),
532 f.args,
533 )))),
534
535 "LISTAGG" => Ok(Expression::AggregateFunction(f)),
537
538 "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT" => Ok(Expression::Function(Box::new(
540 Function::new("APPROXIMATE_COUNT_DISTINCT".to_string(), f.args),
541 ))),
542
543 _ => Ok(Expression::AggregateFunction(f)),
545 }
546 }
547
548 fn convert_strptime_to_exasol_format(format: &str) -> String {
552 let mut result = String::new();
553 let chars: Vec<char> = format.chars().collect();
554 let mut i = 0;
555 while i < chars.len() {
556 if chars[i] == '%' && i + 1 < chars.len() {
557 let spec = chars[i + 1];
558 let exasol_spec = match spec {
559 'Y' => "YYYY",
560 'y' => "YY",
561 'm' => "MM",
562 'd' => "DD",
563 'H' => "HH",
564 'M' => "MI",
565 'S' => "SS",
566 'a' => "DY", 'A' => "DAY", 'b' => "MON", 'B' => "MONTH", 'I' => "H12", 'u' => "ID", 'V' => "IW", 'G' => "IYYY", 'W' => "UW", 'U' => "UW", 'z' => "Z", _ => {
578 result.push('%');
580 result.push(spec);
581 i += 2;
582 continue;
583 }
584 };
585 result.push_str(exasol_spec);
586 i += 2;
587 } else {
588 result.push(chars[i]);
589 i += 1;
590 }
591 }
592 result
593 }
594
595 fn convert_c_format_to_exasol(format: &str) -> String {
598 let mut result = String::new();
599 let chars: Vec<char> = format.chars().collect();
600 let mut i = 0;
601 while i < chars.len() {
602 if chars[i] == '%' && i + 1 < chars.len() {
603 let spec = chars[i + 1];
604 let exasol_spec = match spec {
605 'Y' => "YYYY",
606 'y' => "YY",
607 'm' => "MM",
608 'd' => "DD",
609 'H' => "HH",
610 'M' => "MI", 'i' => "MI", 'S' | 's' => "SS",
613 'T' => "HH:MI:SS", 'a' => "DY", 'A' => "DAY", 'b' => "MON", 'B' => "MONTH", 'I' => "H12", 'u' => "ID", 'V' => "IW", 'G' => "IYYY", 'W' => "UW", 'U' => "UW", 'z' => "Z", _ => {
626 result.push('%');
628 result.push(spec);
629 i += 2;
630 continue;
631 }
632 };
633 result.push_str(exasol_spec);
634 i += 2;
635 } else {
636 result.push(chars[i]);
637 i += 1;
638 }
639 }
640 result
641 }
642
643 fn uppercase_exasol_format(format: &str) -> String {
646 format.to_uppercase()
648 }
649}
650
651