Skip to main content

polyglot_sql/dialects/
presto.rs

1//! Presto Dialect
2//!
3//! Presto-specific transformations based on sqlglot patterns.
4//! Presto is the base for Trino dialect.
5
6use super::{DialectImpl, DialectType};
7use crate::error::Result;
8use crate::expressions::{
9    AggFunc, AggregateFunction, BinaryOp, Case, Cast, Column, DataType, Expression, Function,
10    JsonExtractFunc, LikeOp, Literal, UnaryFunc, VarArgFunc,
11};
12#[cfg(feature = "generate")]
13use crate::generator::GeneratorConfig;
14use crate::tokens::TokenizerConfig;
15
16/// Presto dialect
17pub struct PrestoDialect;
18
19impl DialectImpl for PrestoDialect {
20    fn dialect_type(&self) -> DialectType {
21        DialectType::Presto
22    }
23
24    fn tokenizer_config(&self) -> TokenizerConfig {
25        let mut config = TokenizerConfig::default();
26        // Presto uses double quotes for identifiers
27        config.identifiers.insert('"', '"');
28        // Presto does NOT support nested comments
29        config.nested_comments = false;
30        // Presto does NOT support QUALIFY - it's a valid identifier
31        // (unlike Snowflake, BigQuery, DuckDB which have QUALIFY clause)
32        config.keywords.remove("QUALIFY");
33        config
34    }
35
36    #[cfg(feature = "generate")]
37
38    fn generator_config(&self) -> GeneratorConfig {
39        use crate::generator::IdentifierQuoteStyle;
40        GeneratorConfig {
41            identifier_quote: '"',
42            identifier_quote_style: IdentifierQuoteStyle::DOUBLE_QUOTE,
43            dialect: Some(DialectType::Presto),
44            limit_only_literals: true,
45            tz_to_with_time_zone: true,
46            ..Default::default()
47        }
48    }
49
50    #[cfg(feature = "transpile")]
51
52    fn transform_expr(&self, expr: Expression) -> Result<Expression> {
53        match expr {
54            // IFNULL -> COALESCE in Presto
55            Expression::IfNull(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
56                original_name: None,
57                expressions: vec![f.this, f.expression],
58                inferred_type: None,
59            }))),
60
61            // NVL -> COALESCE in Presto
62            Expression::Nvl(f) => Ok(Expression::Coalesce(Box::new(VarArgFunc {
63                original_name: None,
64                expressions: vec![f.this, f.expression],
65                inferred_type: None,
66            }))),
67
68            // TryCast stays as TryCast (Presto supports TRY_CAST)
69            Expression::TryCast(c) => Ok(Expression::TryCast(c)),
70
71            // SafeCast -> TRY_CAST in Presto
72            Expression::SafeCast(c) => Ok(Expression::TryCast(c)),
73
74            // ILike -> LOWER() LIKE LOWER() (Presto doesn't support ILIKE)
75            Expression::ILike(op) => {
76                let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left.clone())));
77                let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right.clone())));
78                Ok(Expression::Like(Box::new(LikeOp {
79                    left: lower_left,
80                    right: lower_right,
81                    escape: op.escape,
82                    quantifier: op.quantifier.clone(),
83                    inferred_type: None,
84                })))
85            }
86
87            // CountIf is native in Presto (keep as-is)
88            Expression::CountIf(f) => Ok(Expression::CountIf(f)),
89
90            // EXPLODE -> UNNEST in Presto
91            Expression::Explode(f) => Ok(Expression::Unnest(Box::new(
92                crate::expressions::UnnestFunc {
93                    this: f.this,
94                    expressions: Vec::new(),
95                    with_ordinality: false,
96                    alias: None,
97                    offset_alias: None,
98                    inferred_type: None,
99                },
100            ))),
101
102            // ExplodeOuter -> UNNEST in Presto
103            Expression::ExplodeOuter(f) => Ok(Expression::Unnest(Box::new(
104                crate::expressions::UnnestFunc {
105                    this: f.this,
106                    expressions: Vec::new(),
107                    with_ordinality: false,
108                    alias: None,
109                    offset_alias: None,
110                    inferred_type: None,
111                },
112            ))),
113
114            // StringAgg -> ARRAY_JOIN(ARRAY_AGG()) in Presto
115            Expression::StringAgg(f) => {
116                let array_agg = Expression::Function(Box::new(Function::new(
117                    "ARRAY_AGG".to_string(),
118                    vec![f.this.clone()],
119                )));
120                let mut join_args = vec![array_agg];
121                if let Some(sep) = f.separator {
122                    join_args.push(sep);
123                }
124                Ok(Expression::Function(Box::new(Function::new(
125                    "ARRAY_JOIN".to_string(),
126                    join_args,
127                ))))
128            }
129
130            // GroupConcat -> ARRAY_JOIN(ARRAY_AGG()) in Presto
131            Expression::GroupConcat(f) => {
132                let array_agg = Expression::Function(Box::new(Function::new(
133                    "ARRAY_AGG".to_string(),
134                    vec![f.this.clone()],
135                )));
136                let mut join_args = vec![array_agg];
137                if let Some(sep) = f.separator {
138                    join_args.push(sep);
139                }
140                Ok(Expression::Function(Box::new(Function::new(
141                    "ARRAY_JOIN".to_string(),
142                    join_args,
143                ))))
144            }
145
146            // ListAgg -> ARRAY_JOIN(ARRAY_AGG()) in Presto
147            Expression::ListAgg(f) => {
148                let array_agg = Expression::Function(Box::new(Function::new(
149                    "ARRAY_AGG".to_string(),
150                    vec![f.this.clone()],
151                )));
152                let mut join_args = vec![array_agg];
153                if let Some(sep) = f.separator {
154                    join_args.push(sep);
155                }
156                Ok(Expression::Function(Box::new(Function::new(
157                    "ARRAY_JOIN".to_string(),
158                    join_args,
159                ))))
160            }
161
162            // ParseJson: handled by generator (outputs JSON_PARSE for Presto)
163
164            // JSONExtract (variant_extract/colon accessor) -> JSON_EXTRACT in Presto
165            Expression::JSONExtract(e) if e.variant_extract.is_some() => {
166                let path = match *e.expression {
167                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
168                        let Literal::String(s) = lit.as_ref() else {
169                            unreachable!()
170                        };
171                        let normalized = if s.starts_with('$') {
172                            s.clone()
173                        } else if s.starts_with('[') {
174                            format!("${}", s)
175                        } else {
176                            format!("$.{}", s)
177                        };
178                        Expression::Literal(Box::new(Literal::String(normalized)))
179                    }
180                    other => other,
181                };
182                Ok(Expression::JsonExtract(Box::new(JsonExtractFunc {
183                    options: None,
184                    source_dialect: None,
185
186                    this: *e.this,
187                    path,
188                    returning: None,
189                    arrow_syntax: false,
190                    hash_arrow_syntax: false,
191                    wrapper_option: None,
192                    quotes_option: None,
193                    on_scalar_string: false,
194                    on_error: None,
195                })))
196            }
197
198            // Generic function transformations
199            Expression::Function(f) => self.transform_function(*f),
200
201            // Generic aggregate function transformations
202            Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
203
204            // Cast transformations
205            Expression::Cast(c) => self.transform_cast(*c),
206
207            // Div: Presto has TYPED_DIVISION - wrap left operand in CAST(AS DOUBLE)
208            // to ensure float division (only when left isn't already a float cast)
209            Expression::Div(mut op) => {
210                if !Self::is_float_cast(&op.left) {
211                    op.left = Expression::Cast(Box::new(crate::expressions::Cast {
212                        this: op.left,
213                        to: DataType::Double {
214                            precision: None,
215                            scale: None,
216                        },
217                        trailing_comments: Vec::new(),
218                        double_colon_syntax: false,
219                        format: None,
220                        default: None,
221                        inferred_type: None,
222                    }));
223                }
224                Ok(Expression::Div(op))
225            }
226
227            // IntDiv -> CAST(CAST(x AS DOUBLE) / y AS INTEGER) in Presto
228            Expression::IntDiv(f) => {
229                let cast_x = Expression::Cast(Box::new(Cast {
230                    this: f.this,
231                    to: crate::expressions::DataType::Double {
232                        precision: None,
233                        scale: None,
234                    },
235                    trailing_comments: Vec::new(),
236                    double_colon_syntax: false,
237                    format: None,
238                    default: None,
239                    inferred_type: None,
240                }));
241                let div_expr = Expression::Div(Box::new(BinaryOp::new(cast_x, f.expression)));
242                Ok(Expression::Cast(Box::new(Cast {
243                    this: div_expr,
244                    to: crate::expressions::DataType::Int {
245                        length: None,
246                        integer_spelling: true,
247                    },
248                    trailing_comments: Vec::new(),
249                    double_colon_syntax: false,
250                    format: None,
251                    default: None,
252                    inferred_type: None,
253                })))
254            }
255
256            // DELETE: Strip table alias and unqualify columns (Presto doesn't support DELETE aliases)
257            Expression::Delete(mut d) => {
258                if d.alias.is_some() {
259                    d.alias = None;
260                    d.alias_explicit_as = false;
261                    // Unqualify all columns in the WHERE clause
262                    if let Some(ref mut where_clause) = d.where_clause {
263                        where_clause.this = Self::unqualify_columns(where_clause.this.clone());
264                    }
265                }
266                Ok(Expression::Delete(d))
267            }
268
269            // Pass through everything else
270            _ => Ok(expr),
271        }
272    }
273}
274
275#[cfg(feature = "transpile")]
276impl PrestoDialect {
277    /// Recursively unqualify columns - remove table qualifiers from Column references
278    fn unqualify_columns(expr: Expression) -> Expression {
279        match expr {
280            Expression::Column(c) => {
281                if c.table.is_some() {
282                    Expression::boxed_column(Column {
283                        name: c.name,
284                        table: None,
285                        join_mark: c.join_mark,
286                        trailing_comments: c.trailing_comments,
287                        span: None,
288                        inferred_type: None,
289                    })
290                } else {
291                    Expression::Column(c)
292                }
293            }
294            // DotAccess: db.t2.c -> c (strip all qualifiers, keep only the final field name)
295            Expression::Dot(d) => Expression::boxed_column(Column {
296                name: d.field,
297                table: None,
298                join_mark: false,
299                trailing_comments: Vec::new(),
300                span: None,
301                inferred_type: None,
302            }),
303            // Recursively walk common binary expression types
304            Expression::And(mut op) => {
305                op.left = Self::unqualify_columns(op.left);
306                op.right = Self::unqualify_columns(op.right);
307                Expression::And(op)
308            }
309            Expression::Or(mut op) => {
310                op.left = Self::unqualify_columns(op.left);
311                op.right = Self::unqualify_columns(op.right);
312                Expression::Or(op)
313            }
314            Expression::Eq(mut op) => {
315                op.left = Self::unqualify_columns(op.left);
316                op.right = Self::unqualify_columns(op.right);
317                Expression::Eq(op)
318            }
319            Expression::Neq(mut op) => {
320                op.left = Self::unqualify_columns(op.left);
321                op.right = Self::unqualify_columns(op.right);
322                Expression::Neq(op)
323            }
324            Expression::Gt(mut op) => {
325                op.left = Self::unqualify_columns(op.left);
326                op.right = Self::unqualify_columns(op.right);
327                Expression::Gt(op)
328            }
329            Expression::Lt(mut op) => {
330                op.left = Self::unqualify_columns(op.left);
331                op.right = Self::unqualify_columns(op.right);
332                Expression::Lt(op)
333            }
334            Expression::Gte(mut op) => {
335                op.left = Self::unqualify_columns(op.left);
336                op.right = Self::unqualify_columns(op.right);
337                Expression::Gte(op)
338            }
339            Expression::Lte(mut op) => {
340                op.left = Self::unqualify_columns(op.left);
341                op.right = Self::unqualify_columns(op.right);
342                Expression::Lte(op)
343            }
344            // Unary operators
345            Expression::Not(mut e) => {
346                e.this = Self::unqualify_columns(e.this);
347                Expression::Not(e)
348            }
349            // Predicates
350            Expression::In(mut i) => {
351                i.this = Self::unqualify_columns(i.this);
352                i.expressions = i
353                    .expressions
354                    .into_iter()
355                    .map(Self::unqualify_columns)
356                    .collect();
357                // Also recurse into subquery if present
358                if let Some(q) = i.query {
359                    i.query = Some(Self::unqualify_columns(q));
360                }
361                Expression::In(i)
362            }
363            Expression::IsNull(mut f) => {
364                f.this = Self::unqualify_columns(f.this);
365                Expression::IsNull(f)
366            }
367            Expression::Paren(mut p) => {
368                p.this = Self::unqualify_columns(p.this);
369                Expression::Paren(p)
370            }
371            Expression::Function(mut f) => {
372                f.args = f.args.into_iter().map(Self::unqualify_columns).collect();
373                Expression::Function(f)
374            }
375            // For subqueries (SELECT statements inside IN, etc), also unqualify
376            Expression::Select(mut s) => {
377                s.expressions = s
378                    .expressions
379                    .into_iter()
380                    .map(Self::unqualify_columns)
381                    .collect();
382                if let Some(ref mut w) = s.where_clause {
383                    w.this = Self::unqualify_columns(w.this.clone());
384                }
385                Expression::Select(s)
386            }
387            Expression::Subquery(mut sq) => {
388                sq.this = Self::unqualify_columns(sq.this);
389                Expression::Subquery(sq)
390            }
391            Expression::Alias(mut a) => {
392                a.this = Self::unqualify_columns(a.this);
393                Expression::Alias(a)
394            }
395            // Pass through other expressions unchanged
396            other => other,
397        }
398    }
399
400    /// Check if an expression is already a CAST to a float type
401    fn is_float_cast(expr: &Expression) -> bool {
402        if let Expression::Cast(cast) = expr {
403            matches!(&cast.to, DataType::Double { .. } | DataType::Float { .. })
404        } else {
405            false
406        }
407    }
408
409    /// Convert Oracle/PostgreSQL-style date format to Presto's C-style format
410    /// Oracle: dd, hh, hh24, mi, mm, ss, yyyy, yy
411    /// Presto: %d, %H, %H, %i, %m, %s, %Y, %y
412    pub fn oracle_to_presto_format(fmt: &str) -> String {
413        // Process character by character to avoid double-replacement issues
414        let mut cursor = crate::format_tokens::FormatTokenCursor::new(fmt);
415        let mut result = String::new();
416        while let Some(ch) = cursor.peek() {
417            if cursor.consume_prefix("yyyy", false) {
418                result.push_str("%Y");
419            } else if cursor.consume_prefix("yy", false) {
420                result.push_str("%y");
421            } else if cursor.consume_prefix("hh24", false) {
422                result.push_str("%H");
423            } else if cursor.consume_prefix("hh", false) {
424                result.push_str("%H");
425            } else if cursor.consume_prefix("mi", false) {
426                result.push_str("%i");
427            } else if cursor.consume_prefix("mm", false) {
428                result.push_str("%m");
429            } else if cursor.consume_prefix("dd", false) {
430                result.push_str("%d");
431            } else if cursor.consume_prefix("ss", false) {
432                result.push_str("%s");
433            } else {
434                result.push(ch);
435                cursor.next_char();
436            }
437        }
438        result
439    }
440
441    /// Convert Presto's C-style date format to Java-style format (for Hive/Spark)
442    /// Presto: %Y, %m, %d, %H, %i, %S, %s, %y, %T, %F
443    /// Java:   yyyy, MM, dd, HH, mm, ss, ss, yy, HH:mm:ss, yyyy-MM-dd
444    pub fn presto_to_java_format(fmt: &str) -> String {
445        fmt.replace("%Y", "yyyy")
446            .replace("%m", "M")
447            .replace("%d", "d")
448            .replace("%H", "H")
449            .replace("%i", "m")
450            .replace("%S", "s")
451            .replace("%s", "s")
452            .replace("%y", "yy")
453            .replace("%T", "H:m:s")
454            .replace("%F", "yyyy-M-d")
455            .replace("%M", "MMMM")
456    }
457
458    /// Normalize Presto format strings (e.g., %H:%i:%S -> %T, %Y-%m-%d -> %F)
459    pub fn normalize_presto_format(fmt: &str) -> String {
460        fmt.replace("%H:%i:%S", "%T").replace("%H:%i:%s", "%T")
461    }
462
463    /// Convert Presto's C-style format to DuckDB C-style (only difference: %i -> %M for minutes)
464    pub fn presto_to_duckdb_format(fmt: &str) -> String {
465        fmt.replace("%i", "%M")
466            .replace("%s", "%S")
467            .replace("%T", "%H:%M:%S")
468    }
469
470    /// Convert Presto's C-style format to BigQuery format
471    pub fn presto_to_bigquery_format(fmt: &str) -> String {
472        // BigQuery uses %F for %Y-%m-%d, %T for %H:%M:%S
473        // BigQuery uses %M for minutes (like DuckDB), not %i
474        let result = fmt
475            .replace("%Y-%m-%d", "%F")
476            .replace("%H:%i:%S", "%T")
477            .replace("%H:%i:%s", "%T")
478            .replace("%i", "%M")
479            .replace("%s", "%S");
480        result
481    }
482
483    /// Check if a Presto format string matches the default timestamp format
484    pub fn is_default_timestamp_format(fmt: &str) -> bool {
485        let normalized = Self::normalize_presto_format(fmt);
486        normalized == "%Y-%m-%d %T"
487            || normalized == "%Y-%m-%d %H:%i:%S"
488            || fmt == "%Y-%m-%d %H:%i:%S"
489            || fmt == "%Y-%m-%d %T"
490    }
491
492    /// Check if a Presto format string matches the default date format
493    pub fn is_default_date_format(fmt: &str) -> bool {
494        fmt == "%Y-%m-%d" || fmt == "%F"
495    }
496
497    fn transform_function(&self, f: Function) -> Result<Expression> {
498        let name_upper = f.name.to_uppercase();
499        match name_upper.as_str() {
500            // IFNULL -> COALESCE
501            "IFNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
502                original_name: None,
503                expressions: f.args,
504                inferred_type: None,
505            }))),
506
507            // NVL -> COALESCE
508            "NVL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
509                original_name: None,
510                expressions: f.args,
511                inferred_type: None,
512            }))),
513
514            // ISNULL -> COALESCE
515            "ISNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
516                original_name: None,
517                expressions: f.args,
518                inferred_type: None,
519            }))),
520
521            // GETDATE -> CURRENT_TIMESTAMP
522            "GETDATE" => Ok(Expression::CurrentTimestamp(
523                crate::expressions::CurrentTimestamp {
524                    precision: None,
525                    sysdate: false,
526                },
527            )),
528
529            // NOW -> CURRENT_TIMESTAMP
530            "NOW" => Ok(Expression::CurrentTimestamp(
531                crate::expressions::CurrentTimestamp {
532                    precision: None,
533                    sysdate: false,
534                },
535            )),
536
537            // RAND -> RANDOM in Presto (but it's actually RANDOM())
538            "RAND" => Ok(Expression::Function(Box::new(Function::new(
539                "RANDOM".to_string(),
540                vec![],
541            )))),
542
543            // GROUP_CONCAT -> ARRAY_JOIN(ARRAY_AGG())
544            "GROUP_CONCAT" if !f.args.is_empty() => {
545                let mut args = f.args;
546                let first = args.remove(0);
547                let separator = args.pop();
548                let array_agg = Expression::Function(Box::new(Function::new(
549                    "ARRAY_AGG".to_string(),
550                    vec![first],
551                )));
552                let mut join_args = vec![array_agg];
553                if let Some(sep) = separator {
554                    join_args.push(sep);
555                }
556                Ok(Expression::Function(Box::new(Function::new(
557                    "ARRAY_JOIN".to_string(),
558                    join_args,
559                ))))
560            }
561
562            // STRING_AGG -> ARRAY_JOIN(ARRAY_AGG())
563            "STRING_AGG" if !f.args.is_empty() => {
564                let mut args = f.args;
565                let first = args.remove(0);
566                let separator = args.pop();
567                let array_agg = Expression::Function(Box::new(Function::new(
568                    "ARRAY_AGG".to_string(),
569                    vec![first],
570                )));
571                let mut join_args = vec![array_agg];
572                if let Some(sep) = separator {
573                    join_args.push(sep);
574                }
575                Ok(Expression::Function(Box::new(Function::new(
576                    "ARRAY_JOIN".to_string(),
577                    join_args,
578                ))))
579            }
580
581            // LISTAGG -> ARRAY_JOIN(ARRAY_AGG())
582            "LISTAGG" if !f.args.is_empty() => {
583                let mut args = f.args;
584                let first = args.remove(0);
585                let separator = args.pop();
586                let array_agg = Expression::Function(Box::new(Function::new(
587                    "ARRAY_AGG".to_string(),
588                    vec![first],
589                )));
590                let mut join_args = vec![array_agg];
591                if let Some(sep) = separator {
592                    join_args.push(sep);
593                }
594                Ok(Expression::Function(Box::new(Function::new(
595                    "ARRAY_JOIN".to_string(),
596                    join_args,
597                ))))
598            }
599
600            // SUBSTR is native in Presto (keep as-is, don't convert to SUBSTRING)
601            "SUBSTR" => Ok(Expression::Function(Box::new(f))),
602
603            // LEN -> LENGTH
604            "LEN" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc::new(
605                f.args.into_iter().next().unwrap(),
606            )))),
607
608            // CHARINDEX -> STRPOS in Presto (with swapped args)
609            "CHARINDEX" if f.args.len() >= 2 => {
610                let mut args = f.args;
611                let substring = args.remove(0);
612                let string = args.remove(0);
613                // STRPOS(string, substring) - note: argument order is reversed
614                Ok(Expression::Function(Box::new(Function::new(
615                    "STRPOS".to_string(),
616                    vec![string, substring],
617                ))))
618            }
619
620            // INSTR -> STRPOS (with same argument order)
621            "INSTR" if f.args.len() >= 2 => {
622                let args = f.args;
623                // INSTR(string, substring) -> STRPOS(string, substring)
624                Ok(Expression::Function(Box::new(Function::new(
625                    "STRPOS".to_string(),
626                    args,
627                ))))
628            }
629
630            // LOCATE -> STRPOS in Presto (with swapped args)
631            "LOCATE" if f.args.len() >= 2 => {
632                let mut args = f.args;
633                let substring = args.remove(0);
634                let string = args.remove(0);
635                // LOCATE(substring, string) -> STRPOS(string, substring)
636                Ok(Expression::Function(Box::new(Function::new(
637                    "STRPOS".to_string(),
638                    vec![string, substring],
639                ))))
640            }
641
642            // ARRAY_LENGTH -> CARDINALITY in Presto
643            "ARRAY_LENGTH" if f.args.len() == 1 => Ok(Expression::Function(Box::new(
644                Function::new("CARDINALITY".to_string(), f.args),
645            ))),
646
647            // SIZE -> CARDINALITY in Presto
648            "SIZE" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
649                "CARDINALITY".to_string(),
650                f.args,
651            )))),
652
653            // ARRAY_CONTAINS -> CONTAINS in Presto
654            "ARRAY_CONTAINS" if f.args.len() == 2 => Ok(Expression::Function(Box::new(
655                Function::new("CONTAINS".to_string(), f.args),
656            ))),
657
658            // TO_DATE -> DATE_PARSE in Presto (or CAST to DATE)
659            "TO_DATE" if !f.args.is_empty() => {
660                if f.args.len() == 1 {
661                    // Simple case: just cast to DATE
662                    Ok(Expression::Cast(Box::new(Cast {
663                        this: f.args.into_iter().next().unwrap(),
664                        to: DataType::Date,
665                        trailing_comments: Vec::new(),
666                        double_colon_syntax: false,
667                        format: None,
668                        default: None,
669                        inferred_type: None,
670                    })))
671                } else {
672                    // With format: use DATE_PARSE
673                    Ok(Expression::Function(Box::new(Function::new(
674                        "DATE_PARSE".to_string(),
675                        f.args,
676                    ))))
677                }
678            }
679
680            // TO_TIMESTAMP -> DATE_PARSE / CAST
681            "TO_TIMESTAMP" if !f.args.is_empty() => {
682                if f.args.len() == 1 {
683                    Ok(Expression::Cast(Box::new(Cast {
684                        this: f.args.into_iter().next().unwrap(),
685                        to: DataType::Timestamp {
686                            precision: None,
687                            timezone: false,
688                        },
689                        trailing_comments: Vec::new(),
690                        double_colon_syntax: false,
691                        format: None,
692                        default: None,
693                        inferred_type: None,
694                    })))
695                } else {
696                    Ok(Expression::Function(Box::new(Function::new(
697                        "DATE_PARSE".to_string(),
698                        f.args,
699                    ))))
700                }
701            }
702
703            // DATE_FORMAT -> DATE_FORMAT (native in Presto)
704            "DATE_FORMAT" => Ok(Expression::Function(Box::new(f))),
705
706            // strftime -> DATE_FORMAT in Presto
707            "STRFTIME" if f.args.len() >= 2 => {
708                let mut args = f.args;
709                // strftime(format, date) -> DATE_FORMAT(date, format)
710                let format = args.remove(0);
711                let date = args.remove(0);
712                Ok(Expression::Function(Box::new(Function::new(
713                    "DATE_FORMAT".to_string(),
714                    vec![date, format],
715                ))))
716            }
717
718            // TO_CHAR -> DATE_FORMAT in Presto (convert Oracle-style format to Presto C-style)
719            "TO_CHAR" if f.args.len() >= 2 => {
720                let mut args = f.args;
721                // Convert Oracle-style format string to Presto C-style
722                if let Expression::Literal(ref lit) = args[1] {
723                    if let Literal::String(ref s) = lit.as_ref() {
724                        let converted = Self::oracle_to_presto_format(s);
725                        args[1] = Expression::Literal(Box::new(Literal::String(converted)));
726                    }
727                }
728                Ok(Expression::Function(Box::new(Function::new(
729                    "DATE_FORMAT".to_string(),
730                    args,
731                ))))
732            }
733
734            // LEVENSHTEIN -> LEVENSHTEIN_DISTANCE in Presto
735            "LEVENSHTEIN" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
736                Function::new("LEVENSHTEIN_DISTANCE".to_string(), f.args),
737            ))),
738
739            // FLATTEN -> FLATTEN is supported in Presto for nested arrays
740            "FLATTEN" => Ok(Expression::Function(Box::new(f))),
741
742            // JSON_EXTRACT -> JSON_EXTRACT (native in Presto)
743            "JSON_EXTRACT" => Ok(Expression::Function(Box::new(f))),
744
745            // JSON_EXTRACT_SCALAR -> JSON_EXTRACT_SCALAR (native in Presto)
746            "JSON_EXTRACT_SCALAR" => Ok(Expression::Function(Box::new(f))),
747
748            // GET_JSON_OBJECT -> JSON_EXTRACT_SCALAR in Presto
749            "GET_JSON_OBJECT" if f.args.len() == 2 => Ok(Expression::Function(Box::new(
750                Function::new("JSON_EXTRACT_SCALAR".to_string(), f.args),
751            ))),
752
753            // COLLECT_LIST -> ARRAY_AGG
754            "COLLECT_LIST" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
755                Function::new("ARRAY_AGG".to_string(), f.args),
756            ))),
757
758            // COLLECT_SET -> ARRAY_DISTINCT(ARRAY_AGG())
759            "COLLECT_SET" if !f.args.is_empty() => {
760                let array_agg =
761                    Expression::Function(Box::new(Function::new("ARRAY_AGG".to_string(), f.args)));
762                Ok(Expression::Function(Box::new(Function::new(
763                    "ARRAY_DISTINCT".to_string(),
764                    vec![array_agg],
765                ))))
766            }
767
768            // RLIKE -> REGEXP_LIKE in Presto
769            "RLIKE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
770                "REGEXP_LIKE".to_string(),
771                f.args,
772            )))),
773
774            // REGEXP -> REGEXP_LIKE in Presto
775            "REGEXP" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
776                "REGEXP_LIKE".to_string(),
777                f.args,
778            )))),
779
780            // PARSE_JSON -> JSON_PARSE in Presto
781            "PARSE_JSON" => Ok(Expression::Function(Box::new(Function::new(
782                "JSON_PARSE".to_string(),
783                f.args,
784            )))),
785
786            // GET_PATH(obj, path) -> JSON_EXTRACT(obj, json_path) in Presto
787            "GET_PATH" if f.args.len() == 2 => {
788                let mut args = f.args;
789                let this = args.remove(0);
790                let path = args.remove(0);
791                let json_path = match &path {
792                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
793                        let Literal::String(s) = lit.as_ref() else {
794                            unreachable!()
795                        };
796                        let normalized = if s.starts_with('$') {
797                            s.clone()
798                        } else if s.starts_with('[') {
799                            format!("${}", s)
800                        } else {
801                            format!("$.{}", s)
802                        };
803                        Expression::Literal(Box::new(Literal::String(normalized)))
804                    }
805                    _ => path,
806                };
807                Ok(Expression::JsonExtract(Box::new(JsonExtractFunc {
808                    options: None,
809                    source_dialect: None,
810
811                    this,
812                    path: json_path,
813                    returning: None,
814                    arrow_syntax: false,
815                    hash_arrow_syntax: false,
816                    wrapper_option: None,
817                    quotes_option: None,
818                    on_scalar_string: false,
819                    on_error: None,
820                })))
821            }
822
823            // REGEXP_SUBSTR(subject, pattern, ...) -> REGEXP_EXTRACT(subject, pattern[, group])
824            "REGEXP_SUBSTR" if f.args.len() >= 2 => {
825                let mut args = f.args;
826                let subject = args.remove(0);
827                let pattern = args.remove(0);
828                // If 6-arg form: (subject, pattern, pos, occ, params, group) -> keep group
829                if args.len() >= 4 {
830                    let _pos = args.remove(0);
831                    let _occ = args.remove(0);
832                    let _params = args.remove(0);
833                    let group = args.remove(0);
834                    Ok(Expression::Function(Box::new(Function::new(
835                        "REGEXP_EXTRACT".to_string(),
836                        vec![subject, pattern, group],
837                    ))))
838                } else {
839                    Ok(Expression::Function(Box::new(Function::new(
840                        "REGEXP_EXTRACT".to_string(),
841                        vec![subject, pattern],
842                    ))))
843                }
844            }
845
846            // DATE_PART(epoch_second, x) -> TO_UNIXTIME(CAST(x AS TIMESTAMP))
847            // DATE_PART(epoch_millisecond[s], x) -> TO_UNIXTIME(CAST(x AS TIMESTAMP)) * 1000
848            "DATE_PART" if f.args.len() == 2 => {
849                let part_name = match &f.args[0] {
850                    Expression::Identifier(id) => Some(id.name.to_uppercase()),
851                    Expression::Var(v) => Some(v.this.to_uppercase()),
852                    Expression::Column(c) => Some(c.name.name.to_uppercase()),
853                    _ => None,
854                };
855                match part_name.as_deref() {
856                    Some("EPOCH_SECOND" | "EPOCH_SECONDS") => {
857                        let mut args = f.args;
858                        let value = args.remove(1);
859                        let cast_expr = Expression::Cast(Box::new(Cast {
860                            this: value,
861                            to: DataType::Timestamp {
862                                precision: None,
863                                timezone: false,
864                            },
865                            trailing_comments: Vec::new(),
866                            double_colon_syntax: false,
867                            format: None,
868                            default: None,
869                            inferred_type: None,
870                        }));
871                        Ok(Expression::Function(Box::new(Function::new(
872                            "TO_UNIXTIME".to_string(),
873                            vec![cast_expr],
874                        ))))
875                    }
876                    Some("EPOCH_MILLISECOND" | "EPOCH_MILLISECONDS") => {
877                        let mut args = f.args;
878                        let value = args.remove(1);
879                        let cast_expr = Expression::Cast(Box::new(Cast {
880                            this: value,
881                            to: DataType::Timestamp {
882                                precision: None,
883                                timezone: false,
884                            },
885                            trailing_comments: Vec::new(),
886                            double_colon_syntax: false,
887                            format: None,
888                            default: None,
889                            inferred_type: None,
890                        }));
891                        let unixtime = Expression::Function(Box::new(Function::new(
892                            "TO_UNIXTIME".to_string(),
893                            vec![cast_expr],
894                        )));
895                        Ok(Expression::Mul(Box::new(BinaryOp {
896                            left: unixtime,
897                            right: Expression::Literal(Box::new(Literal::Number(
898                                "1000".to_string(),
899                            ))),
900                            left_comments: Vec::new(),
901                            operator_comments: Vec::new(),
902                            trailing_comments: Vec::new(),
903                            inferred_type: None,
904                        })))
905                    }
906                    _ => Ok(Expression::Function(Box::new(f))),
907                }
908            }
909
910            // REPLACE(x, y) with 2 args -> REPLACE(x, y, '') - Presto requires explicit empty string
911            "REPLACE" if f.args.len() == 2 => {
912                let mut args = f.args;
913                args.push(Expression::string(""));
914                Ok(Expression::Function(Box::new(Function::new(
915                    "REPLACE".to_string(),
916                    args,
917                ))))
918            }
919
920            // REGEXP_REPLACE(x, y) with 2 args -> REGEXP_REPLACE(x, y, '')
921            "REGEXP_REPLACE" if f.args.len() == 2 => {
922                let mut args = f.args;
923                args.push(Expression::string(""));
924                Ok(Expression::Function(Box::new(Function::new(
925                    "REGEXP_REPLACE".to_string(),
926                    args,
927                ))))
928            }
929
930            // Pass through everything else
931            _ => Ok(Expression::Function(Box::new(f))),
932        }
933    }
934
935    fn transform_aggregate_function(
936        &self,
937        f: Box<crate::expressions::AggregateFunction>,
938    ) -> Result<Expression> {
939        let name_upper = f.name.to_uppercase();
940        match name_upper.as_str() {
941            // COUNT_IF -> SUM(CASE WHEN...)
942            "COUNT_IF" if !f.args.is_empty() => {
943                let condition = f.args.into_iter().next().unwrap();
944                let case_expr = Expression::Case(Box::new(Case {
945                    operand: None,
946                    whens: vec![(condition, Expression::number(1))],
947                    else_: Some(Expression::number(0)),
948                    comments: Vec::new(),
949                    inferred_type: None,
950                }));
951                Ok(Expression::Sum(Box::new(AggFunc {
952                    ignore_nulls: None,
953                    having_max: None,
954                    this: case_expr,
955                    distinct: f.distinct,
956                    filter: f.filter,
957                    order_by: Vec::new(),
958                    name: None,
959                    limit: None,
960                    inferred_type: None,
961                })))
962            }
963
964            // ANY_VALUE -> ARBITRARY in Presto
965            "ANY_VALUE" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
966                "ARBITRARY".to_string(),
967                f.args,
968            )))),
969
970            // GROUP_CONCAT -> ARRAY_JOIN(ARRAY_AGG())
971            "GROUP_CONCAT" if !f.args.is_empty() => {
972                let mut args = f.args;
973                let first = args.remove(0);
974                let separator = args.pop();
975                let array_agg = Expression::Function(Box::new(Function::new(
976                    "ARRAY_AGG".to_string(),
977                    vec![first],
978                )));
979                let mut join_args = vec![array_agg];
980                if let Some(sep) = separator {
981                    join_args.push(sep);
982                }
983                Ok(Expression::Function(Box::new(Function::new(
984                    "ARRAY_JOIN".to_string(),
985                    join_args,
986                ))))
987            }
988
989            // STRING_AGG -> ARRAY_JOIN(ARRAY_AGG())
990            "STRING_AGG" if !f.args.is_empty() => {
991                let mut args = f.args;
992                let first = args.remove(0);
993                let separator = args.pop();
994                let array_agg = Expression::Function(Box::new(Function::new(
995                    "ARRAY_AGG".to_string(),
996                    vec![first],
997                )));
998                let mut join_args = vec![array_agg];
999                if let Some(sep) = separator {
1000                    join_args.push(sep);
1001                }
1002                Ok(Expression::Function(Box::new(Function::new(
1003                    "ARRAY_JOIN".to_string(),
1004                    join_args,
1005                ))))
1006            }
1007
1008            // LISTAGG -> ARRAY_JOIN(ARRAY_AGG())
1009            "LISTAGG" if !f.args.is_empty() => {
1010                let mut args = f.args;
1011                let first = args.remove(0);
1012                let separator = args.pop();
1013                let array_agg = Expression::Function(Box::new(Function::new(
1014                    "ARRAY_AGG".to_string(),
1015                    vec![first],
1016                )));
1017                let mut join_args = vec![array_agg];
1018                if let Some(sep) = separator {
1019                    join_args.push(sep);
1020                }
1021                Ok(Expression::Function(Box::new(Function::new(
1022                    "ARRAY_JOIN".to_string(),
1023                    join_args,
1024                ))))
1025            }
1026
1027            // VAR -> VAR_POP in Presto
1028            "VAR" if !f.args.is_empty() => {
1029                Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
1030                    source_dialect: None,
1031
1032                    name: "VAR_POP".to_string(),
1033                    args: f.args,
1034                    distinct: f.distinct,
1035                    filter: f.filter,
1036                    order_by: Vec::new(),
1037                    limit: None,
1038                    ignore_nulls: None,
1039                    inferred_type: None,
1040                })))
1041            }
1042
1043            // VARIANCE -> VAR_SAMP in Presto (for sample variance)
1044            "VARIANCE" if !f.args.is_empty() => {
1045                Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
1046                    source_dialect: None,
1047
1048                    name: "VAR_SAMP".to_string(),
1049                    args: f.args,
1050                    distinct: f.distinct,
1051                    filter: f.filter,
1052                    order_by: Vec::new(),
1053                    limit: None,
1054                    ignore_nulls: None,
1055                    inferred_type: None,
1056                })))
1057            }
1058
1059            // Pass through everything else
1060            _ => Ok(Expression::AggregateFunction(f)),
1061        }
1062    }
1063
1064    fn transform_cast(&self, c: Cast) -> Result<Expression> {
1065        // Presto type mappings are handled in the generator
1066        Ok(Expression::Cast(Box::new(c)))
1067    }
1068}