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 this: f.this,
131 separator: f.separator,
132 on_overflow: None,
133 order_by: f.order_by,
134 distinct: f.distinct,
135 filter: f.filter,
136 inferred_type: None,
137 }))),
138
139 Expression::Column(col)
141 if col.table.is_none() && col.name.name.eq_ignore_ascii_case("USER") =>
142 {
143 Ok(Expression::CurrentUser(Box::new(
144 crate::expressions::CurrentUser { this: None },
145 )))
146 }
147
148 Expression::Function(f) => self.transform_function(*f),
150
151 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
153
154 Expression::Select(select) => Ok(Expression::Select(Box::new(
155 self.qualify_local_alias_predicates(*select),
156 ))),
157
158 _ => Ok(expr),
160 }
161 }
162}
163
164#[cfg(feature = "transpile")]
165impl ExasolDialect {
166 fn qualify_local_alias_predicates(&self, mut select: Select) -> Select {
167 let aliases = Self::select_aliases(&select);
168 if aliases.is_empty() {
169 return select;
170 }
171
172 if let Some(where_clause) = select.where_clause.as_mut() {
173 where_clause.this = Self::qualify_local_alias_expr(where_clause.this.clone(), &aliases);
174 }
175 if let Some(having) = select.having.as_mut() {
176 having.this = Self::qualify_local_alias_expr(having.this.clone(), &aliases);
177 }
178
179 select
180 }
181
182 fn select_aliases(select: &Select) -> std::collections::HashMap<String, Identifier> {
183 let mut aliases = std::collections::HashMap::new();
184 for expression in &select.expressions {
185 if let Expression::Alias(alias) = expression {
186 aliases.insert(alias.alias.name.to_ascii_uppercase(), alias.alias.clone());
187 }
188 }
189 aliases
190 }
191
192 fn qualify_local_alias_expr(
193 expr: Expression,
194 aliases: &std::collections::HashMap<String, Identifier>,
195 ) -> Expression {
196 match expr {
197 Expression::Column(col) if col.table.is_none() => {
198 if let Some(alias) = aliases.get(&col.name.name.to_ascii_uppercase()) {
199 return Expression::Raw(crate::expressions::Raw {
200 sql: format!("LOCAL.{}", alias.name),
201 });
202 }
203 Expression::Column(col)
204 }
205 Expression::Identifier(id) => {
206 if let Some(alias) = aliases.get(&id.name.to_ascii_uppercase()) {
207 Expression::Raw(crate::expressions::Raw {
208 sql: format!("LOCAL.{}", alias.name),
209 })
210 } else {
211 Expression::Identifier(id)
212 }
213 }
214 Expression::And(op) => Self::qualify_binary(op, aliases, Expression::And),
215 Expression::Or(op) => Self::qualify_binary(op, aliases, Expression::Or),
216 Expression::Eq(op) => Self::qualify_binary(op, aliases, Expression::Eq),
217 Expression::Neq(op) => Self::qualify_binary(op, aliases, Expression::Neq),
218 Expression::Lt(op) => Self::qualify_binary(op, aliases, Expression::Lt),
219 Expression::Lte(op) => Self::qualify_binary(op, aliases, Expression::Lte),
220 Expression::Gt(op) => Self::qualify_binary(op, aliases, Expression::Gt),
221 Expression::Gte(op) => Self::qualify_binary(op, aliases, Expression::Gte),
222 Expression::Like(op) => Self::qualify_like(op, aliases, Expression::Like),
223 Expression::ILike(op) => Self::qualify_like(op, aliases, Expression::ILike),
224 Expression::Not(mut op) => {
225 op.this = Self::qualify_local_alias_expr(op.this, aliases);
226 Expression::Not(op)
227 }
228 other => other,
229 }
230 }
231
232 fn qualify_binary(
233 mut op: Box<BinaryOp>,
234 aliases: &std::collections::HashMap<String, Identifier>,
235 wrap: fn(Box<BinaryOp>) -> Expression,
236 ) -> Expression {
237 op.left = Self::qualify_local_alias_expr(op.left, aliases);
238 op.right = Self::qualify_local_alias_expr(op.right, aliases);
239 wrap(op)
240 }
241
242 fn qualify_like(
243 mut op: Box<LikeOp>,
244 aliases: &std::collections::HashMap<String, Identifier>,
245 wrap: fn(Box<LikeOp>) -> Expression,
246 ) -> Expression {
247 op.left = Self::qualify_local_alias_expr(op.left, aliases);
248 op.right = Self::qualify_local_alias_expr(op.right, aliases);
249 if let Some(escape) = op.escape.take() {
250 op.escape = Some(Self::qualify_local_alias_expr(escape, aliases));
251 }
252 wrap(op)
253 }
254
255 fn transform_function(&self, f: Function) -> Result<Expression> {
256 let name_upper = f.name.to_uppercase();
257 match name_upper.as_str() {
258 "SYSTIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
262 "SYSTIMESTAMP".to_string(),
263 f.args,
264 )))),
265
266 "ALL" => Ok(Expression::Function(Box::new(Function::new(
268 "EVERY".to_string(),
269 f.args,
270 )))),
271
272 "IFNULL" | "ISNULL" | "NVL" if f.args.len() == 2 => {
274 Ok(Expression::Coalesce(Box::new(VarArgFunc {
275 original_name: None,
276 expressions: f.args,
277 inferred_type: None,
278 })))
279 }
280
281 "DATEDIFF" => Ok(Expression::Function(Box::new(Function::new(
283 "DAYS_BETWEEN".to_string(),
284 f.args,
285 )))),
286
287 "DATEADD" | "DATE_ADD" => Ok(Expression::Function(Box::new(Function::new(
289 "ADD_DAYS".to_string(),
290 f.args,
291 )))),
292
293 "DATESUB" | "DATE_SUB" => {
295 Ok(Expression::Function(Box::new(Function::new(
297 "ADD_DAYS".to_string(),
298 f.args,
299 ))))
300 }
301
302 "DATE_TRUNC" | "TRUNC" => Ok(Expression::Function(Box::new(f))),
304
305 "LEVENSHTEIN" | "LEVENSHTEIN_DISTANCE" => Ok(Expression::Function(Box::new(
307 Function::new("EDIT_DISTANCE".to_string(), f.args),
308 ))),
309
310 "REGEXP_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
312 "REGEXP_SUBSTR".to_string(),
313 f.args,
314 )))),
315
316 "SHA" | "SHA1" => Ok(Expression::Function(Box::new(Function::new(
318 "HASH_SHA".to_string(),
319 f.args,
320 )))),
321
322 "MD5" => Ok(Expression::Function(Box::new(Function::new(
324 "HASH_MD5".to_string(),
325 f.args,
326 )))),
327
328 "SHA256" | "SHA2" => {
330 let arg = f
333 .args
334 .into_iter()
335 .next()
336 .unwrap_or(Expression::Null(crate::expressions::Null));
337 Ok(Expression::Function(Box::new(Function::new(
338 "HASH_SHA256".to_string(),
339 vec![arg],
340 ))))
341 }
342
343 "SHA512" => Ok(Expression::Function(Box::new(Function::new(
345 "HASH_SHA512".to_string(),
346 f.args,
347 )))),
348
349 "VAR_POP" | "VARIANCE_POP" => Ok(Expression::Function(Box::new(Function::new(
351 "VAR_POP".to_string(),
352 f.args,
353 )))),
354
355 "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT" => Ok(Expression::Function(Box::new(
357 Function::new("APPROXIMATE_COUNT_DISTINCT".to_string(), f.args),
358 ))),
359
360 "TO_CHAR" | "DATE_FORMAT" | "STRFTIME" => {
363 let mut args = f.args;
364 if args.len() >= 2 {
365 if let Expression::Literal(lit) = &args[1] {
367 if let Literal::String(fmt) = lit.as_ref() {
368 let exasol_fmt = Self::convert_c_format_to_exasol(fmt);
369 args[1] = Expression::Literal(Box::new(Literal::String(exasol_fmt)));
370 }
371 }
372 if matches!(&args[0], Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)))
374 && (f.name.eq_ignore_ascii_case("DATE_FORMAT")
375 || f.name.eq_ignore_ascii_case("STRFTIME"))
376 {
377 args[0] = Expression::Cast(Box::new(crate::expressions::Cast {
378 this: args[0].clone(),
379 to: crate::expressions::DataType::Timestamp {
380 timezone: false,
381 precision: None,
382 },
383 trailing_comments: vec![],
384 double_colon_syntax: false,
385 format: None,
386 default: None,
387 inferred_type: None,
388 }));
389 }
390 }
391 Ok(Expression::Function(Box::new(Function::new(
392 "TO_CHAR".to_string(),
393 args,
394 ))))
395 }
396
397 "TO_DATE" => {
399 if f.args.len() >= 2 {
400 let mut new_args = f.args.clone();
402 if let Expression::Literal(lit) = &f.args[1] {
403 if let Literal::String(fmt) = lit.as_ref() {
404 new_args[1] = Expression::Literal(Box::new(Literal::String(
405 Self::uppercase_exasol_format(fmt),
406 )));
407 }
408 }
409 Ok(Expression::Function(Box::new(Function::new(
410 "TO_DATE".to_string(),
411 new_args,
412 ))))
413 } else {
414 Ok(Expression::Function(Box::new(f)))
415 }
416 }
417
418 "TIME_TO_STR" => {
420 if f.args.len() >= 2 {
421 let mut new_args = vec![f.args[0].clone()];
422 if let Expression::Literal(lit) = &f.args[1] {
423 if let Literal::String(fmt) = lit.as_ref() {
424 new_args.push(Expression::Literal(Box::new(Literal::String(
425 Self::convert_strptime_to_exasol_format(fmt),
426 ))));
427 }
428 } else {
429 new_args.push(f.args[1].clone());
430 }
431 Ok(Expression::Function(Box::new(Function::new(
432 "TO_CHAR".to_string(),
433 new_args,
434 ))))
435 } else {
436 Ok(Expression::Function(Box::new(Function::new(
437 "TO_CHAR".to_string(),
438 f.args,
439 ))))
440 }
441 }
442
443 "STR_TO_TIME" => {
445 if f.args.len() >= 2 {
446 let mut new_args = vec![f.args[0].clone()];
447 if let Expression::Literal(lit) = &f.args[1] {
448 if let Literal::String(fmt) = lit.as_ref() {
449 new_args.push(Expression::Literal(Box::new(Literal::String(
450 Self::convert_strptime_to_exasol_format(fmt),
451 ))));
452 }
453 } else {
454 new_args.push(f.args[1].clone());
455 }
456 Ok(Expression::Function(Box::new(Function::new(
457 "TO_DATE".to_string(),
458 new_args,
459 ))))
460 } else {
461 Ok(Expression::Function(Box::new(Function::new(
462 "TO_DATE".to_string(),
463 f.args,
464 ))))
465 }
466 }
467
468 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(f))),
470
471 "CONVERT_TIMEZONE" | "AT_TIME_ZONE" => Ok(Expression::Function(Box::new(
473 Function::new("CONVERT_TZ".to_string(), f.args),
474 ))),
475
476 "STRPOS" | "POSITION" | "CHARINDEX" | "LOCATE" => Ok(Expression::Function(Box::new(
478 Function::new("INSTR".to_string(), f.args),
479 ))),
480
481 "WEEK_OF_YEAR" | "WEEKOFYEAR" => Ok(Expression::Function(Box::new(Function::new(
483 "WEEK".to_string(),
484 f.args,
485 )))),
486
487 "LAST_DAY" => {
489 Ok(Expression::Function(Box::new(f)))
492 }
493
494 "CURDATE" => Ok(Expression::CurrentDate(crate::expressions::CurrentDate)),
496
497 "USER" if f.args.is_empty() => Ok(Expression::CurrentUser(Box::new(
499 crate::expressions::CurrentUser { this: None },
500 ))),
501
502 "NOW" => Ok(Expression::CurrentTimestamp(
504 crate::expressions::CurrentTimestamp {
505 precision: None,
506 sysdate: false,
507 },
508 )),
509
510 _ => Ok(Expression::Function(Box::new(f))),
512 }
513 }
514
515 fn transform_aggregate_function(
516 &self,
517 f: Box<crate::expressions::AggregateFunction>,
518 ) -> Result<Expression> {
519 let name_upper = f.name.to_uppercase();
520 match name_upper.as_str() {
521 "ALL" | "EVERY" => Ok(Expression::Function(Box::new(Function::new(
523 "EVERY".to_string(),
524 f.args,
525 )))),
526
527 "GROUP_CONCAT" | "STRING_AGG" => Ok(Expression::Function(Box::new(Function::new(
529 "LISTAGG".to_string(),
530 f.args,
531 )))),
532
533 "LISTAGG" => Ok(Expression::AggregateFunction(f)),
535
536 "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT" => Ok(Expression::Function(Box::new(
538 Function::new("APPROXIMATE_COUNT_DISTINCT".to_string(), f.args),
539 ))),
540
541 _ => Ok(Expression::AggregateFunction(f)),
543 }
544 }
545
546 fn convert_strptime_to_exasol_format(format: &str) -> String {
550 let mut result = String::new();
551 let chars: Vec<char> = format.chars().collect();
552 let mut i = 0;
553 while i < chars.len() {
554 if chars[i] == '%' && i + 1 < chars.len() {
555 let spec = chars[i + 1];
556 let exasol_spec = match spec {
557 'Y' => "YYYY",
558 'y' => "YY",
559 'm' => "MM",
560 'd' => "DD",
561 'H' => "HH",
562 'M' => "MI",
563 'S' => "SS",
564 'a' => "DY", 'A' => "DAY", 'b' => "MON", 'B' => "MONTH", 'I' => "H12", 'u' => "ID", 'V' => "IW", 'G' => "IYYY", 'W' => "UW", 'U' => "UW", 'z' => "Z", _ => {
576 result.push('%');
578 result.push(spec);
579 i += 2;
580 continue;
581 }
582 };
583 result.push_str(exasol_spec);
584 i += 2;
585 } else {
586 result.push(chars[i]);
587 i += 1;
588 }
589 }
590 result
591 }
592
593 fn convert_c_format_to_exasol(format: &str) -> String {
596 let mut result = String::new();
597 let chars: Vec<char> = format.chars().collect();
598 let mut i = 0;
599 while i < chars.len() {
600 if chars[i] == '%' && i + 1 < chars.len() {
601 let spec = chars[i + 1];
602 let exasol_spec = match spec {
603 'Y' => "YYYY",
604 'y' => "YY",
605 'm' => "MM",
606 'd' => "DD",
607 'H' => "HH",
608 'M' => "MI", 'i' => "MI", 'S' | 's' => "SS",
611 '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", _ => {
624 result.push('%');
626 result.push(spec);
627 i += 2;
628 continue;
629 }
630 };
631 result.push_str(exasol_spec);
632 i += 2;
633 } else {
634 result.push(chars[i]);
635 i += 1;
636 }
637 }
638 result
639 }
640
641 fn uppercase_exasol_format(format: &str) -> String {
644 format.to_uppercase()
646 }
647}
648
649