You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by be...@apache.org on 2025/10/01 15:55:35 UTC

(superset) branch pinot-dialect-timestamp created (now e09c890f52)

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

beto pushed a change to branch pinot-dialect-timestamp
in repository https://gitbox.apache.org/repos/asf/superset.git


      at e09c890f52 fix: Pinot dialect date truncation

This branch includes the following new commits:

     new e09c890f52 fix: Pinot dialect date truncation

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(superset) 01/01: fix: Pinot dialect date truncation

Posted by be...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

beto pushed a commit to branch pinot-dialect-timestamp
in repository https://gitbox.apache.org/repos/asf/superset.git

commit e09c890f52a1472ba1637c412238372a85d8fc32
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Wed Oct 1 11:55:23 2025 -0400

    fix: Pinot dialect date truncation
---
 superset/sql/dialects/pinot.py               | 12 +++++++
 tests/unit_tests/sql/dialects/pinot_tests.py | 48 ++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+)

diff --git a/superset/sql/dialects/pinot.py b/superset/sql/dialects/pinot.py
index 05d32f004b..27bc1738d9 100644
--- a/superset/sql/dialects/pinot.py
+++ b/superset/sql/dialects/pinot.py
@@ -78,6 +78,13 @@ class Pinot(MySQL):
             exp.DataType.Type.UBIGINT: "UNSIGNED",
         }
 
+        # Remove MySQL transformations that don't work in Pinot
+        TRANSFORMS = {
+            **MySQL.Generator.TRANSFORMS,
+        }
+        # Remove DATE_TRUNC transformation - Pinot supports standard SQL DATE_TRUNC
+        TRANSFORMS.pop(exp.DateTrunc, None)
+
         def datatype_sql(self, expression: exp.DataType) -> str:
             # Don't use MySQL's VARCHAR size requirement logic
             # Just use TYPE_MAPPING for all types
@@ -95,3 +102,8 @@ class Pinot(MySQL):
                 return f"{type_sql} UNSIGNED{nested}"
 
             return f"{type_sql}{nested}"
+
+        def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str:
+            # Pinot doesn't support MySQL's TIMESTAMP() function
+            # Use standard CAST syntax instead
+            return super(MySQL.Generator, self).cast_sql(expression, safe_prefix)
diff --git a/tests/unit_tests/sql/dialects/pinot_tests.py b/tests/unit_tests/sql/dialects/pinot_tests.py
index f1a6cfb729..2641c5783a 100644
--- a/tests/unit_tests/sql/dialects/pinot_tests.py
+++ b/tests/unit_tests/sql/dialects/pinot_tests.py
@@ -421,3 +421,51 @@ def test_unsigned_type() -> None:
 
     assert "UNSIGNED" in result
     assert "BIGINT" in result
+
+
+def test_date_trunc_preserved() -> None:
+    """
+    Test that DATE_TRUNC is preserved and not converted to MySQL's DATE() function.
+    """
+    sql = "SELECT DATE_TRUNC('day', dt_column) FROM table"
+    result = sqlglot.parse_one(sql, Pinot).sql(Pinot)
+
+    assert "DATE_TRUNC" in result
+    assert "DATE_TRUNC('day'" in result or "DATE_TRUNC('DAY'" in result
+    # Should not be converted to MySQL's DATE() function
+    assert result != "SELECT DATE(dt_column) FROM table"
+
+
+def test_cast_timestamp_preserved() -> None:
+    """
+    Test that CAST AS TIMESTAMP is preserved and not converted to TIMESTAMP() function.
+    """
+    sql = "SELECT CAST(dt_column AS TIMESTAMP) FROM table"
+    result = sqlglot.parse_one(sql, Pinot).sql(Pinot)
+
+    assert "CAST" in result
+    assert "AS TIMESTAMP" in result
+    # Should not be converted to MySQL's TIMESTAMP() function
+    assert "TIMESTAMP(dt_column)" not in result
+
+
+def test_date_trunc_with_cast_timestamp() -> None:
+    """
+    Test the original complex query with DATE_TRUNC and CAST AS TIMESTAMP.
+    Verifies that both are preserved in parse/generate round-trip.
+    """
+    sql = """
+    SELECT CAST(DATE_TRUNC('day', CAST(DATETIMECONVERT(dt_epoch_ms, '1:MILLISECONDS:EPOCH', '1:MILLISECONDS:EPOCH', '1:MILLISECONDS') AS TIMESTAMP)) AS TIMESTAMP), SUM(deposits_amount) + SUM(withdrawals_amount)
+    FROM "default".core_metrics_net_deposits_daily
+    WHERE dt_epoch_ms >= 1735690800000 AND dt_epoch_ms < 1759328588000 AND locality != 'US' GROUP BY CAST(DATE_TRUNC('day', CAST(DATETIMECONVERT(dt_epoch_ms, '1:MILLISECONDS:EPOCH', '1:MILLISECONDS:EPOCH', '1:MILLISECONDS') AS TIMESTAMP)) AS TIMESTAMP)
+    LIMIT 10000
+    """
+    result = sqlglot.parse_one(sql, Pinot).sql(Pinot)
+
+    # Verify DATE_TRUNC is preserved
+    assert "DATE_TRUNC" in result
+    # Verify CAST is preserved
+    assert "CAST" in result
+    # 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)