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 { original_name: None,
73 expressions: vec![f.this, f.expression],
74 }))),
75
76 Expression::IfNull(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc { original_name: None,
77 expressions: vec![f.this, f.expression],
78 }))),
79
80 Expression::BitwiseAnd(op) => Ok(Expression::Function(Box::new(Function::new(
82 "BIT_AND".to_string(),
83 vec![op.left, op.right],
84 )))),
85
86 Expression::BitwiseOr(op) => Ok(Expression::Function(Box::new(Function::new(
87 "BIT_OR".to_string(),
88 vec![op.left, op.right],
89 )))),
90
91 Expression::BitwiseXor(op) => Ok(Expression::Function(Box::new(Function::new(
92 "BIT_XOR".to_string(),
93 vec![op.left, op.right],
94 )))),
95
96 Expression::BitwiseNot(f) => Ok(Expression::Function(Box::new(Function::new(
97 "BIT_NOT".to_string(),
98 vec![f.this],
99 )))),
100
101 Expression::BitwiseLeftShift(op) => Ok(Expression::Function(Box::new(Function::new(
102 "BIT_LSHIFT".to_string(),
103 vec![op.left, op.right],
104 )))),
105
106 Expression::BitwiseRightShift(op) => Ok(Expression::Function(Box::new(Function::new(
107 "BIT_RSHIFT".to_string(),
108 vec![op.left, op.right],
109 )))),
110
111 Expression::Mod(op) => Ok(Expression::Function(Box::new(Function::new(
113 "MOD".to_string(),
114 vec![op.left, op.right],
115 )))),
116
117 Expression::GroupConcat(f) => Ok(Expression::ListAgg(Box::new(ListAggFunc {
119 this: f.this,
120 separator: f.separator,
121 on_overflow: None,
122 order_by: f.order_by,
123 distinct: f.distinct,
124 filter: f.filter,
125 }))),
126
127 Expression::Function(f) => self.transform_function(*f),
129
130 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
132
133 _ => Ok(expr),
135 }
136 }
137}
138
139impl ExasolDialect {
140 fn transform_function(&self, f: Function) -> Result<Expression> {
141 let name_upper = f.name.to_uppercase();
142 match name_upper.as_str() {
143 "SYSTIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
147 "SYSTIMESTAMP".to_string(),
148 f.args,
149 )))),
150
151 "ALL" => Ok(Expression::Function(Box::new(Function::new(
153 "EVERY".to_string(),
154 f.args,
155 )))),
156
157 "IFNULL" | "ISNULL" | "NVL" if f.args.len() == 2 => {
159 Ok(Expression::Coalesce(Box::new(VarArgFunc { original_name: None,
160 expressions: f.args,
161 })))
162 }
163
164 "DATEDIFF" => Ok(Expression::Function(Box::new(Function::new(
166 "DAYS_BETWEEN".to_string(),
167 f.args,
168 )))),
169
170 "DATEADD" | "DATE_ADD" => Ok(Expression::Function(Box::new(Function::new(
172 "ADD_DAYS".to_string(),
173 f.args,
174 )))),
175
176 "DATESUB" | "DATE_SUB" => {
178 Ok(Expression::Function(Box::new(Function::new(
180 "ADD_DAYS".to_string(),
181 f.args,
182 ))))
183 }
184
185 "DATE_TRUNC" | "TRUNC" => Ok(Expression::Function(Box::new(f))),
187
188 "LEVENSHTEIN" | "LEVENSHTEIN_DISTANCE" => {
190 Ok(Expression::Function(Box::new(Function::new(
191 "EDIT_DISTANCE".to_string(),
192 f.args,
193 ))))
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.args.into_iter().next().unwrap_or(Expression::Null(crate::expressions::Null));
219 Ok(Expression::Function(Box::new(Function::new(
220 "HASH_SHA256".to_string(),
221 vec![arg],
222 ))))
223 }
224
225 "SHA512" => Ok(Expression::Function(Box::new(Function::new(
227 "HASH_SHA512".to_string(),
228 f.args,
229 )))),
230
231 "VAR_POP" | "VARIANCE_POP" => Ok(Expression::Function(Box::new(Function::new(
233 "VAR_POP".to_string(),
234 f.args,
235 )))),
236
237 "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT" => {
239 Ok(Expression::Function(Box::new(Function::new(
240 "APPROXIMATE_COUNT_DISTINCT".to_string(),
241 f.args,
242 ))))
243 }
244
245 "TO_CHAR" | "DATE_FORMAT" | "STRFTIME" => {
247 Ok(Expression::Function(Box::new(Function::new(
248 "TO_CHAR".to_string(),
249 f.args,
250 ))))
251 }
252
253 "TO_DATE" => {
255 if f.args.len() >= 2 {
256 let mut new_args = f.args.clone();
258 if let Expression::Literal(Literal::String(fmt)) = &f.args[1] {
259 new_args[1] = Expression::Literal(Literal::String(
260 Self::uppercase_exasol_format(fmt),
261 ));
262 }
263 Ok(Expression::Function(Box::new(Function::new(
264 "TO_DATE".to_string(),
265 new_args,
266 ))))
267 } else {
268 Ok(Expression::Function(Box::new(f)))
269 }
270 }
271
272 "TIME_TO_STR" => {
274 if f.args.len() >= 2 {
275 let mut new_args = vec![f.args[0].clone()];
276 if let Expression::Literal(Literal::String(fmt)) = &f.args[1] {
277 new_args.push(Expression::Literal(Literal::String(
278 Self::convert_strptime_to_exasol_format(fmt),
279 )));
280 } else {
281 new_args.push(f.args[1].clone());
282 }
283 Ok(Expression::Function(Box::new(Function::new(
284 "TO_CHAR".to_string(),
285 new_args,
286 ))))
287 } else {
288 Ok(Expression::Function(Box::new(Function::new(
289 "TO_CHAR".to_string(),
290 f.args,
291 ))))
292 }
293 }
294
295 "STR_TO_TIME" => {
297 if f.args.len() >= 2 {
298 let mut new_args = vec![f.args[0].clone()];
299 if let Expression::Literal(Literal::String(fmt)) = &f.args[1] {
300 new_args.push(Expression::Literal(Literal::String(
301 Self::convert_strptime_to_exasol_format(fmt),
302 )));
303 } else {
304 new_args.push(f.args[1].clone());
305 }
306 Ok(Expression::Function(Box::new(Function::new(
307 "TO_DATE".to_string(),
308 new_args,
309 ))))
310 } else {
311 Ok(Expression::Function(Box::new(Function::new(
312 "TO_DATE".to_string(),
313 f.args,
314 ))))
315 }
316 }
317
318 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(f))),
320
321 "CONVERT_TIMEZONE" | "AT_TIME_ZONE" => {
323 Ok(Expression::Function(Box::new(Function::new(
324 "CONVERT_TZ".to_string(),
325 f.args,
326 ))))
327 }
328
329 "STRPOS" | "POSITION" | "CHARINDEX" | "LOCATE" => {
331 Ok(Expression::Function(Box::new(Function::new(
332 "INSTR".to_string(),
333 f.args,
334 ))))
335 }
336
337 "WEEK_OF_YEAR" | "WEEKOFYEAR" => Ok(Expression::Function(Box::new(Function::new(
339 "WEEK".to_string(),
340 f.args,
341 )))),
342
343 "LAST_DAY" => {
345 Ok(Expression::Function(Box::new(f)))
348 }
349
350 _ => Ok(Expression::Function(Box::new(f))),
352 }
353 }
354
355 fn transform_aggregate_function(
356 &self,
357 f: Box<crate::expressions::AggregateFunction>,
358 ) -> Result<Expression> {
359 let name_upper = f.name.to_uppercase();
360 match name_upper.as_str() {
361 "ALL" | "EVERY" => Ok(Expression::Function(Box::new(Function::new(
363 "EVERY".to_string(),
364 f.args,
365 )))),
366
367 "GROUP_CONCAT" | "STRING_AGG" => Ok(Expression::Function(Box::new(Function::new(
369 "LISTAGG".to_string(),
370 f.args,
371 )))),
372
373 "LISTAGG" => Ok(Expression::AggregateFunction(f)),
375
376 "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT" => {
378 Ok(Expression::Function(Box::new(Function::new(
379 "APPROXIMATE_COUNT_DISTINCT".to_string(),
380 f.args,
381 ))))
382 }
383
384 _ => Ok(Expression::AggregateFunction(f)),
386 }
387 }
388
389 fn convert_strptime_to_exasol_format(format: &str) -> String {
393 let mut result = String::new();
394 let chars: Vec<char> = format.chars().collect();
395 let mut i = 0;
396 while i < chars.len() {
397 if chars[i] == '%' && i + 1 < chars.len() {
398 let spec = chars[i + 1];
399 let exasol_spec = match spec {
400 'Y' => "YYYY",
401 'y' => "YY",
402 'm' => "MM",
403 'd' => "DD",
404 'H' => "HH",
405 'M' => "MI",
406 'S' => "SS",
407 'a' => "DY", 'A' => "DAY", 'b' => "MON", 'B' => "MONTH", 'I' => "H12", 'u' => "ID", 'V' => "IW", 'G' => "IYYY", 'W' => "UW", 'U' => "UW", 'z' => "Z", _ => {
419 result.push('%');
421 result.push(spec);
422 i += 2;
423 continue;
424 }
425 };
426 result.push_str(exasol_spec);
427 i += 2;
428 } else {
429 result.push(chars[i]);
430 i += 1;
431 }
432 }
433 result
434 }
435
436 fn uppercase_exasol_format(format: &str) -> String {
439 format.to_uppercase()
441 }
442}
443
444