This is an automated email from the ASF dual-hosted git repository.

beto pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 5428376662 fix(pinot): `DATE_ADD` function (#35424)
5428376662 is described below

commit 542837666207e67e92b0863836ac9a201f48db56
Author: Beto Dealmeida <[email protected]>
AuthorDate: Thu Oct 2 09:56:20 2025 -0400

    fix(pinot): `DATE_ADD` function (#35424)
---
 superset/sql/dialects/pinot.py               | 17 ++++++++++
 superset/sql/parse.py                        | 18 +++++-----
 tests/unit_tests/sql/dialects/pinot_tests.py | 50 +++++++++++++++++++++++++++-
 3 files changed, 76 insertions(+), 9 deletions(-)

diff --git a/superset/sql/dialects/pinot.py b/superset/sql/dialects/pinot.py
index f667b9bdab..2e7cbe9ed5 100644
--- a/superset/sql/dialects/pinot.py
+++ b/superset/sql/dialects/pinot.py
@@ -26,6 +26,7 @@ from __future__ import annotations
 
 from sqlglot import exp
 from sqlglot.dialects.mysql import MySQL
+from sqlglot.helper import seq_get
 from sqlglot.tokens import TokenType
 
 
@@ -50,6 +51,16 @@ class Pinot(MySQL):
             "BYTES": TokenType.VARBINARY,
         }
 
+    class Parser(MySQL.Parser):
+        FUNCTIONS = {
+            **MySQL.Parser.FUNCTIONS,
+            "DATE_ADD": lambda args: exp.DateAdd(
+                this=seq_get(args, 2),
+                expression=seq_get(args, 1),
+                unit=seq_get(args, 0),
+            ),
+        }
+
     class Generator(MySQL.Generator):
         TYPE_MAPPING = {
             **MySQL.Generator.TYPE_MAPPING,
@@ -80,6 +91,12 @@ class Pinot(MySQL):
 
         TRANSFORMS = {
             **MySQL.Generator.TRANSFORMS,
+            exp.DateAdd: lambda self, e: self.func(
+                "DATE_ADD",
+                exp.Literal.string(str(e.args.get("unit").name)),
+                e.args.get("expression"),
+                e.this,
+            ),
         }
         # Remove DATE_TRUNC transformation - Pinot supports standard SQL 
DATE_TRUNC
         TRANSFORMS.pop(exp.DateTrunc, None)
diff --git a/superset/sql/parse.py b/superset/sql/parse.py
index 822b8ec79b..d5392115f0 100644
--- a/superset/sql/parse.py
+++ b/superset/sql/parse.py
@@ -552,14 +552,16 @@ class SQLStatement(BaseSQLStatement[exp.Expression]):
         try:
             statements = sqlglot.parse(script, dialect=dialect)
         except sqlglot.errors.ParseError as ex:
-            error = ex.errors[0]
-            raise SupersetParseError(
-                script,
-                engine,
-                highlight=error["highlight"],
-                line=error["line"],
-                column=error["col"],
-            ) from ex
+            kwargs = (
+                {
+                    "highlight": ex.errors[0]["highlight"],
+                    "line": ex.errors[0]["line"],
+                    "column": ex.errors[0]["col"],
+                }
+                if ex.errors
+                else {}
+            )
+            raise SupersetParseError(script, engine, **kwargs) from ex
         except sqlglot.errors.SqlglotError as ex:
             raise SupersetParseError(
                 script,
diff --git a/tests/unit_tests/sql/dialects/pinot_tests.py 
b/tests/unit_tests/sql/dialects/pinot_tests.py
index 226a04e0f1..bd2c000332 100644
--- a/tests/unit_tests/sql/dialects/pinot_tests.py
+++ b/tests/unit_tests/sql/dialects/pinot_tests.py
@@ -22,7 +22,9 @@ from superset.sql.dialects.pinot import Pinot
 
 
 def test_pinot_dialect_registered() -> None:
-    """Test that Pinot dialect is properly registered."""
+    """
+    Test that Pinot dialect is properly registered.
+    """
     from superset.sql.parse import SQLGLOT_DIALECTS
 
     assert "pinot" in SQLGLOT_DIALECTS
@@ -498,3 +500,49 @@ LIMIT
     # Verify these are NOT converted to MySQL functions
     assert "TIMESTAMP(DATETIMECONVERT" not in result
     assert result.count("DATE_TRUNC") == 2  # Should appear twice (SELECT and 
GROUP BY)
+
+
+def test_pinot_date_add_parsing() -> None:
+    """
+    Test that Pinot's DATE_ADD function with Presto-like syntax can be parsed.
+    """
+    from superset.sql.parse import SQLScript
+
+    sql = """
+SELECT dt_epoch_ms FROM my_table WHERE dt_epoch_ms >= date_add('day', -180, 
now())
+    """
+    script = SQLScript(sql, "pinot")
+    assert len(script.statements) == 1
+    assert not script.has_mutation()
+
+
+def test_pinot_date_add_simple() -> None:
+    """
+    Test parsing of simple DATE_ADD expressions.
+    """
+    test_cases = [
+        "date_add('day', -180, now())",
+        "DATE_ADD('month', 5, current_timestamp())",
+        "date_add('year', 1, my_date_column)",
+    ]
+
+    for sql in test_cases:
+        parsed = sqlglot.parse_one(sql, Pinot)
+        assert parsed is not None
+        # Verify that it generates valid SQL
+        generated = parsed.sql(dialect=Pinot)
+        assert "DATE_ADD" in generated.upper()
+
+
+def test_pinot_date_add_unit_quoted() -> None:
+    """
+    Test that DATE_ADD preserves quotes around the unit argument.
+
+    Pinot requires the unit to be a quoted string, not an identifier.
+    """
+    sql = "dt_epoch_ms >= date_add('day', -180, now())"
+    result = sqlglot.parse_one(sql, Pinot).sql(Pinot)
+
+    # The unit should be quoted: 'DAY' not DAY
+    assert "DATE_ADD('DAY', -180, NOW())" in result
+    assert "DATE_ADD(DAY," not in result

Reply via email to