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/09/30 01:59:51 UTC
(superset) branch pinot-dialect-sqlglot-types created (now b2a252381f)
This is an automated email from the ASF dual-hosted git repository.
beto pushed a change to branch pinot-dialect-sqlglot-types
in repository https://gitbox.apache.org/repos/asf/superset.git
at b2a252381f fix(pinot): restrict types in dialect
This branch includes the following new commits:
new b2a252381f fix(pinot): restrict types in dialect
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): restrict types in dialect
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-sqlglot-types
in repository https://gitbox.apache.org/repos/asf/superset.git
commit b2a252381f57312e55c20be412d7297fb22568a8
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Mon Sep 29 19:51:30 2025 -0400
fix(pinot): restrict types in dialect
---
.../pythonpath_dev/superset_config_docker_light.py | 1 +
superset/sql/dialects/pinot.py | 54 +++++++++++++++++++
tests/unit_tests/sql/dialects/pinot_tests.py | 60 ++++++++++++++++++++++
3 files changed, 115 insertions(+)
diff --git a/docker/pythonpath_dev/superset_config_docker_light.py b/docker/pythonpath_dev/superset_config_docker_light.py
index 9a5ae0ae67..1f053c2ce3 100644
--- a/docker/pythonpath_dev/superset_config_docker_light.py
+++ b/docker/pythonpath_dev/superset_config_docker_light.py
@@ -19,6 +19,7 @@
# Import all settings from the main config first
from flask_caching.backends.filesystemcache import FileSystemCache
+
from superset_config import * # noqa: F403
# Override caching to use simple in-memory cache instead of Redis
diff --git a/superset/sql/dialects/pinot.py b/superset/sql/dialects/pinot.py
index e8804b2ee8..05d32f004b 100644
--- a/superset/sql/dialects/pinot.py
+++ b/superset/sql/dialects/pinot.py
@@ -24,7 +24,9 @@ double quotes are used for identifiers instead of string literals.
from __future__ import annotations
+from sqlglot import exp
from sqlglot.dialects.mysql import MySQL
+from sqlglot.tokens import TokenType
class Pinot(MySQL):
@@ -41,3 +43,55 @@ class Pinot(MySQL):
QUOTES = ["'"] # Only single quotes for strings
IDENTIFIERS = ['"', "`"] # Backticks and double quotes for identifiers
STRING_ESCAPES = ["'", "\\"] # Remove double quote from string escapes
+ KEYWORDS = {
+ **MySQL.Tokenizer.KEYWORDS,
+ "STRING": TokenType.TEXT,
+ "LONG": TokenType.BIGINT,
+ "BYTES": TokenType.VARBINARY,
+ }
+
+ class Generator(MySQL.Generator):
+ TYPE_MAPPING = {
+ **MySQL.Generator.TYPE_MAPPING,
+ exp.DataType.Type.TINYINT: "INT",
+ exp.DataType.Type.SMALLINT: "INT",
+ exp.DataType.Type.INT: "INT",
+ exp.DataType.Type.BIGINT: "LONG",
+ exp.DataType.Type.FLOAT: "FLOAT",
+ exp.DataType.Type.DOUBLE: "DOUBLE",
+ exp.DataType.Type.BOOLEAN: "BOOLEAN",
+ exp.DataType.Type.TIMESTAMP: "TIMESTAMP",
+ exp.DataType.Type.TIMESTAMPTZ: "TIMESTAMP",
+ exp.DataType.Type.VARCHAR: "STRING",
+ exp.DataType.Type.CHAR: "STRING",
+ exp.DataType.Type.TEXT: "STRING",
+ exp.DataType.Type.BINARY: "BYTES",
+ exp.DataType.Type.VARBINARY: "BYTES",
+ exp.DataType.Type.JSON: "JSON",
+ }
+
+ # Override MySQL's CAST_MAPPING - don't convert integer or string types
+ CAST_MAPPING = {
+ exp.DataType.Type.LONGBLOB: exp.DataType.Type.VARBINARY,
+ exp.DataType.Type.MEDIUMBLOB: exp.DataType.Type.VARBINARY,
+ exp.DataType.Type.TINYBLOB: exp.DataType.Type.VARBINARY,
+ exp.DataType.Type.UBIGINT: "UNSIGNED",
+ }
+
+ def datatype_sql(self, expression: exp.DataType) -> str:
+ # Don't use MySQL's VARCHAR size requirement logic
+ # Just use TYPE_MAPPING for all types
+ type_value = expression.this
+ type_sql = (
+ self.TYPE_MAPPING.get(type_value, type_value.value)
+ if isinstance(type_value, exp.DataType.Type)
+ else type_value
+ )
+
+ interior = self.expressions(expression, flat=True)
+ nested = f"({interior})" if interior else ""
+
+ if expression.this in self.UNSIGNED_TYPE_MAPPING:
+ return f"{type_sql} UNSIGNED{nested}"
+
+ return f"{type_sql}{nested}"
diff --git a/tests/unit_tests/sql/dialects/pinot_tests.py b/tests/unit_tests/sql/dialects/pinot_tests.py
index 4d7eed7154..442a7c2476 100644
--- a/tests/unit_tests/sql/dialects/pinot_tests.py
+++ b/tests/unit_tests/sql/dialects/pinot_tests.py
@@ -346,3 +346,63 @@ SELECT DISTINCT
FROM "products"
""".strip()
)
+
+
+def test_cast_to_string() -> None:
+ """
+ Test that CAST to STRING is preserved (not converted to CHAR).
+ """
+ sql = "SELECT CAST(cohort_size AS STRING) FROM table"
+ ast = sqlglot.parse_one(sql, Pinot)
+ generated = Pinot().generate(expression=ast)
+
+ assert "STRING" in generated
+ assert "CHAR" not in generated
+
+
+def test_concat_with_cast_string() -> None:
+ """
+ Test CONCAT with CAST to STRING - verifies the original issue is fixed.
+ """
+ sql = """
+SELECT concat(a, cast(b AS string), ' - ')
+FROM "default".c"""
+ ast = sqlglot.parse_one(sql, Pinot)
+ generated = Pinot().generate(expression=ast)
+
+ # Verify STRING type is preserved (not converted to CHAR)
+ assert "STRING" in generated or "string" in generated.lower()
+ assert "CHAR" not in generated
+
+
+@pytest.mark.parametrize(
+ "cast_type, expected_type",
+ [
+ ("INT", "INT"),
+ ("TINYINT", "INT"),
+ ("SMALLINT", "INT"),
+ ("BIGINT", "LONG"),
+ ("LONG", "LONG"),
+ ("FLOAT", "FLOAT"),
+ ("DOUBLE", "DOUBLE"),
+ ("BOOLEAN", "BOOLEAN"),
+ ("TIMESTAMP", "TIMESTAMP"),
+ ("STRING", "STRING"),
+ ("VARCHAR", "STRING"),
+ ("CHAR", "STRING"),
+ ("TEXT", "STRING"),
+ ("BYTES", "BYTES"),
+ ("BINARY", "BYTES"),
+ ("VARBINARY", "BYTES"),
+ ("JSON", "JSON"),
+ ],
+)
+def test_type_mappings(cast_type: str, expected_type: str) -> None:
+ """
+ Test that Pinot type mappings work correctly for all basic types.
+ """
+ sql = f"SELECT CAST(col AS {cast_type}) FROM table" # noqa: S608
+ ast = sqlglot.parse_one(sql, Pinot)
+ generated = Pinot().generate(expression=ast)
+
+ assert expected_type in generated