From 7dc924e90997da093d8293dd5f82eb8c2fc2ef29 Mon Sep 17 00:00:00 2001 From: tkaunlaky-e6 Date: Fri, 6 Mar 2026 13:54:20 +0530 Subject: [PATCH] Add NEXT_DAY transpilation from Databricks to E6 Transpile NEXT_DAY(expr, dayOfWeek) to: CAST(DATE_ADD(expr, MOD((target_index - DAYOFWEEK(expr) + 6), 7) + 1) AS DATE) --- sqlglot/dialects/databricks.py | 1 + sqlglot/dialects/e6.py | 67 ++++++++++++++++++++++++++++++++++ sqlglot/expressions.py | 5 +++ 3 files changed, 73 insertions(+) diff --git a/sqlglot/dialects/databricks.py b/sqlglot/dialects/databricks.py index 325c4ec6f1..10a5a1d602 100644 --- a/sqlglot/dialects/databricks.py +++ b/sqlglot/dialects/databricks.py @@ -140,6 +140,7 @@ class Parser(Spark.Parser): "DATEDIFF": build_date_delta(exp.DateDiff), "DATE_DIFF": build_date_delta(exp.DateDiff), "FIND_IN_SET": exp.FindInSet.from_arg_list, + "NEXT_DAY": exp.NextDay.from_arg_list, "GETDATE": exp.CurrentTimestamp.from_arg_list, "GET_JSON_OBJECT": _build_json_extract, "TO_DATE": build_formatted_time(exp.TsOrDsToDate, "databricks"), diff --git a/sqlglot/dialects/e6.py b/sqlglot/dialects/e6.py index 3e1ab6b2a8..c13ca0eee2 100644 --- a/sqlglot/dialects/e6.py +++ b/sqlglot/dialects/e6.py @@ -1940,6 +1940,72 @@ def _last_day_sql(self: E6.Generator, expression: exp.LastDay) -> str: return self.func("LAST_DAY", date_expr, unit) return self.func("LAST_DAY", date_expr) + # Mapping from day-of-week abbreviations/names to DAYOFWEEK index (1=Sunday .. 7=Saturday) + _DAY_OF_WEEK_INDEX: t.ClassVar[t.Dict[str, int]] = { + "SU": 1, + "SUN": 1, + "SUNDAY": 1, + "MO": 2, + "MON": 2, + "MONDAY": 2, + "TU": 3, + "TUE": 3, + "TUESDAY": 3, + "WE": 4, + "WED": 4, + "WEDNESDAY": 4, + "TH": 5, + "THU": 5, + "THURSDAY": 5, + "FR": 6, + "FRI": 6, + "FRIDAY": 6, + "SA": 7, + "SAT": 7, + "SATURDAY": 7, + } + + def _next_day_sql(self: E6.Generator, expression: exp.NextDay) -> str: + date_expr = expression.this + day_expr = expression.expression + + # If the day-of-week argument is a string literal, resolve it to an integer index + if isinstance(day_expr, exp.Literal) and day_expr.is_string: + day_name = day_expr.this.upper() + target_index = self._DAY_OF_WEEK_INDEX.get(day_name) + if target_index is not None: + # CAST(DATE_ADD(expr, (target_day_index - DAYOFWEEK(expr) + 6) % 7 + 1) AS DATE) + # The +6 (instead of +7) combined with +1 ensures that if expr + # is already the target day, we skip to the next week (7 days). + date_add = exp.Anonymous( + this="DATE_ADD", + expressions=[ + date_expr, + exp.Add( + this=exp.Mod( + this=exp.Paren( + this=exp.Add( + this=exp.Sub( + this=exp.Literal.number(target_index), + expression=exp.Anonymous( + this="DAYOFWEEK", expressions=[date_expr.copy()] + ), + ), + expression=exp.Literal.number(6), + ) + ), + expression=exp.Literal.number(7), + ), + expression=exp.Literal.number(1), + ), + ], + ) + return self.sql( + exp.Cast(this=date_add, to=exp.DataType(this=exp.DataType.Type.DATE)) + ) + + raise ValueError(f"NEXT_DAY: unsupported day-of-week: {self.sql(day_expr)}") + def extract_sql(self: E6.Generator, expression: exp.Extract | exp.DayOfYear) -> str: unit = expression.this.name unit_mapped = E6.UNIT_PART_MAPPING.get(f"'{unit.lower()}'", unit) @@ -2848,6 +2914,7 @@ def create_lpad_component(func_name: str): exp.JSONObject: lambda self, e: self.func("NAMED_STRUCT", e.this, *e.expressions), exp.LastDay: _last_day_sql, exp.LastValue: rename_func("LAST_VALUE"), + exp.NextDay: _next_day_sql, exp.Length: length_sql, exp.Log: lambda self, e: self.func("LOG", e.this, e.expression), exp.Lower: rename_func("LOWER"), diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py index f7d2a9b11f..e21b466d0c 100644 --- a/sqlglot/expressions.py +++ b/sqlglot/expressions.py @@ -6055,6 +6055,11 @@ class LastDay(Func, TimeUnit): arg_types = {"this": True, "unit": False} +class NextDay(Func): + _sql_names = ["NEXT_DAY"] + arg_types = {"this": True, "expression": True} + + class Extract(Func): arg_types = {"this": True, "expression": True}