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