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 }))),
130
131 Expression::Function(f) => self.transform_function(*f),
133
134 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
136
137 _ => Ok(expr),
139 }
140 }
141}
142
143impl ExasolDialect {
144 fn transform_function(&self, f: Function) -> Result<Expression> {
145 let name_upper = f.name.to_uppercase();
146 match name_upper.as_str() {
147 "SYSTIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
151 "SYSTIMESTAMP".to_string(),
152 f.args,
153 )))),
154
155 "ALL" => Ok(Expression::Function(Box::new(Function::new(
157 "EVERY".to_string(),
158 f.args,
159 )))),
160
161 "IFNULL" | "ISNULL" | "NVL" if f.args.len() == 2 => {
163 Ok(Expression::Coalesce(Box::new(VarArgFunc {
164 original_name: None,
165 expressions: f.args,
166 inferred_type: None,
167 })))
168 }
169
170 "DATEDIFF" => Ok(Expression::Function(Box::new(Function::new(
172 "DAYS_BETWEEN".to_string(),
173 f.args,
174 )))),
175
176 "DATEADD" | "DATE_ADD" => Ok(Expression::Function(Box::new(Function::new(
178 "ADD_DAYS".to_string(),
179 f.args,
180 )))),
181
182 "DATESUB" | "DATE_SUB" => {
184 Ok(Expression::Function(Box::new(Function::new(
186 "ADD_DAYS".to_string(),
187 f.args,
188 ))))
189 }
190
191 "DATE_TRUNC" | "TRUNC" => Ok(Expression::Function(Box::new(f))),
193
194 "LEVENSHTEIN" | "LEVENSHTEIN_DISTANCE" => Ok(Expression::Function(Box::new(
196 Function::new("EDIT_DISTANCE".to_string(), f.args),
197 ))),
198
199 "REGEXP_EXTRACT" => Ok(Expression::Function(Box::new(Function::new(
201 "REGEXP_SUBSTR".to_string(),
202 f.args,
203 )))),
204
205 "SHA" | "SHA1" => Ok(Expression::Function(Box::new(Function::new(
207 "HASH_SHA".to_string(),
208 f.args,
209 )))),
210
211 "MD5" => Ok(Expression::Function(Box::new(Function::new(
213 "HASH_MD5".to_string(),
214 f.args,
215 )))),
216
217 "SHA256" | "SHA2" => {
219 let arg = f
222 .args
223 .into_iter()
224 .next()
225 .unwrap_or(Expression::Null(crate::expressions::Null));
226 Ok(Expression::Function(Box::new(Function::new(
227 "HASH_SHA256".to_string(),
228 vec![arg],
229 ))))
230 }
231
232 "SHA512" => Ok(Expression::Function(Box::new(Function::new(
234 "HASH_SHA512".to_string(),
235 f.args,
236 )))),
237
238 "VAR_POP" | "VARIANCE_POP" => Ok(Expression::Function(Box::new(Function::new(
240 "VAR_POP".to_string(),
241 f.args,
242 )))),
243
244 "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT" => Ok(Expression::Function(Box::new(
246 Function::new("APPROXIMATE_COUNT_DISTINCT".to_string(), f.args),
247 ))),
248
249 "TO_CHAR" | "DATE_FORMAT" | "STRFTIME" => Ok(Expression::Function(Box::new(
251 Function::new("TO_CHAR".to_string(), f.args),
252 ))),
253
254 "TO_DATE" => {
256 if f.args.len() >= 2 {
257 let mut new_args = f.args.clone();
259 if let Expression::Literal(Literal::String(fmt)) = &f.args[1] {
260 new_args[1] = Expression::Literal(Literal::String(
261 Self::uppercase_exasol_format(fmt),
262 ));
263 }
264 Ok(Expression::Function(Box::new(Function::new(
265 "TO_DATE".to_string(),
266 new_args,
267 ))))
268 } else {
269 Ok(Expression::Function(Box::new(f)))
270 }
271 }
272
273 "TIME_TO_STR" => {
275 if f.args.len() >= 2 {
276 let mut new_args = vec![f.args[0].clone()];
277 if let Expression::Literal(Literal::String(fmt)) = &f.args[1] {
278 new_args.push(Expression::Literal(Literal::String(
279 Self::convert_strptime_to_exasol_format(fmt),
280 )));
281 } else {
282 new_args.push(f.args[1].clone());
283 }
284 Ok(Expression::Function(Box::new(Function::new(
285 "TO_CHAR".to_string(),
286 new_args,
287 ))))
288 } else {
289 Ok(Expression::Function(Box::new(Function::new(
290 "TO_CHAR".to_string(),
291 f.args,
292 ))))
293 }
294 }
295
296 "STR_TO_TIME" => {
298 if f.args.len() >= 2 {
299 let mut new_args = vec![f.args[0].clone()];
300 if let Expression::Literal(Literal::String(fmt)) = &f.args[1] {
301 new_args.push(Expression::Literal(Literal::String(
302 Self::convert_strptime_to_exasol_format(fmt),
303 )));
304 } else {
305 new_args.push(f.args[1].clone());
306 }
307 Ok(Expression::Function(Box::new(Function::new(
308 "TO_DATE".to_string(),
309 new_args,
310 ))))
311 } else {
312 Ok(Expression::Function(Box::new(Function::new(
313 "TO_DATE".to_string(),
314 f.args,
315 ))))
316 }
317 }
318
319 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(f))),
321
322 "CONVERT_TIMEZONE" | "AT_TIME_ZONE" => Ok(Expression::Function(Box::new(
324 Function::new("CONVERT_TZ".to_string(), f.args),
325 ))),
326
327 "STRPOS" | "POSITION" | "CHARINDEX" | "LOCATE" => Ok(Expression::Function(Box::new(
329 Function::new("INSTR".to_string(), f.args),
330 ))),
331
332 "WEEK_OF_YEAR" | "WEEKOFYEAR" => Ok(Expression::Function(Box::new(Function::new(
334 "WEEK".to_string(),
335 f.args,
336 )))),
337
338 "LAST_DAY" => {
340 Ok(Expression::Function(Box::new(f)))
343 }
344
345 _ => Ok(Expression::Function(Box::new(f))),
347 }
348 }
349
350 fn transform_aggregate_function(
351 &self,
352 f: Box<crate::expressions::AggregateFunction>,
353 ) -> Result<Expression> {
354 let name_upper = f.name.to_uppercase();
355 match name_upper.as_str() {
356 "ALL" | "EVERY" => Ok(Expression::Function(Box::new(Function::new(
358 "EVERY".to_string(),
359 f.args,
360 )))),
361
362 "GROUP_CONCAT" | "STRING_AGG" => Ok(Expression::Function(Box::new(Function::new(
364 "LISTAGG".to_string(),
365 f.args,
366 )))),
367
368 "LISTAGG" => Ok(Expression::AggregateFunction(f)),
370
371 "APPROX_DISTINCT" | "APPROX_COUNT_DISTINCT" => Ok(Expression::Function(Box::new(
373 Function::new("APPROXIMATE_COUNT_DISTINCT".to_string(), f.args),
374 ))),
375
376 _ => Ok(Expression::AggregateFunction(f)),
378 }
379 }
380
381 fn convert_strptime_to_exasol_format(format: &str) -> String {
385 let mut result = String::new();
386 let chars: Vec<char> = format.chars().collect();
387 let mut i = 0;
388 while i < chars.len() {
389 if chars[i] == '%' && i + 1 < chars.len() {
390 let spec = chars[i + 1];
391 let exasol_spec = match spec {
392 'Y' => "YYYY",
393 'y' => "YY",
394 'm' => "MM",
395 'd' => "DD",
396 'H' => "HH",
397 'M' => "MI",
398 'S' => "SS",
399 'a' => "DY", 'A' => "DAY", 'b' => "MON", 'B' => "MONTH", 'I' => "H12", 'u' => "ID", 'V' => "IW", 'G' => "IYYY", 'W' => "UW", 'U' => "UW", 'z' => "Z", _ => {
411 result.push('%');
413 result.push(spec);
414 i += 2;
415 continue;
416 }
417 };
418 result.push_str(exasol_spec);
419 i += 2;
420 } else {
421 result.push(chars[i]);
422 i += 1;
423 }
424 }
425 result
426 }
427
428 fn uppercase_exasol_format(format: &str) -> String {
431 format.to_uppercase()
433 }
434}
435
436