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

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


The following commit(s) were added to refs/heads/explorable by this push:
     new f064a6301c WIP
f064a6301c is described below

commit f064a6301c6ac04fe384fdf9873222d4879a297d
Author: Beto Dealmeida <[email protected]>
AuthorDate: Wed Oct 15 18:23:14 2025 -0400

    WIP
---
 superset/semantic_layers/snowflake_.py | 152 +++++++++++++++++++++++++++------
 1 file changed, 126 insertions(+), 26 deletions(-)

diff --git a/superset/semantic_layers/snowflake_.py 
b/superset/semantic_layers/snowflake_.py
index 9887074d5b..a549bc65b3 100644
--- a/superset/semantic_layers/snowflake_.py
+++ b/superset/semantic_layers/snowflake_.py
@@ -20,6 +20,7 @@ from __future__ import annotations
 import itertools
 import re
 from collections import defaultdict
+from textwrap import dedent
 from typing import Any, Literal, Union
 
 from cryptography.hazmat.backends import default_backend
@@ -37,7 +38,6 @@ from snowflake.connector import connect, DictCursor
 from snowflake.connector.connection import SnowflakeConnection
 from snowflake.sqlalchemy.snowdialect import SnowflakeDialect
 
-from superset.common.query_object import QueryObject
 from superset.semantic_layers.types import (
     BINARY,
     BOOLEAN,
@@ -356,11 +356,13 @@ class SnowflakeSemanticLayer:
             return set()
 
         cursor = connection.cursor()
-        query = """
+        query = dedent(
+            """
             SELECT SCHEMA_NAME
             FROM INFORMATION_SCHEMA.SCHEMATA
             WHERE CATALOG_NAME = ?
-        """
+            """
+        )
         return {row[0] for row in cursor.execute(query, (database,))}
 
     def __init__(self, configuration: SnowflakeConfiguration):
@@ -381,10 +383,12 @@ class SnowflakeSemanticLayer:
         connection_parameters = get_connection_parameters(configuration)
         with connect(**connection_parameters) as connection:
             cursor = connection.cursor()
-            query = """
+            query = dedent(
+                """
                 SHOW SEMANTIC VIEWS
                     ->> SELECT "name" FROM $1;
-            """
+                """
+            )
             return {
                 SnowflakeExplorable(configuration, row[0])
                 for row in cursor.execute(query)
@@ -418,14 +422,16 @@ class SnowflakeExplorable:
         """
         dimensions: set[Dimension] = set()
 
-        query = f"""
+        query = dedent(
+            f"""
             DESC SEMANTIC VIEW {self.uid()}
                 ->> SELECT "object_name", "property", "property_value"
                     FROM $1
                     WHERE
                         "object_kind" = 'DIMENSION' AND
                         "property" IN ('COMMENT', 'DATA_TYPE', 'EXPRESSION', 
'TABLE');
-        """  # noqa: S608
+            """  # noqa: S608
+        )
 
         connection_parameters = get_connection_parameters(self.configuration)
         with connect(**connection_parameters) as connection:
@@ -453,14 +459,16 @@ class SnowflakeExplorable:
         """
         metrics: set[Metric] = set()
 
-        query = f"""
+        query = dedent(
+            f"""
             DESC SEMANTIC VIEW {self.uid()}
                 ->> SELECT "object_name", "property", "property_value"
                     FROM $1
                     WHERE
                         "object_kind" = 'METRIC' AND
                         "property" IN ('COMMENT', 'DATA_TYPE', 'EXPRESSION', 
'TABLE');
-        """  # noqa: S608
+            """  # noqa: S608
+        )
 
         connection_parameters = get_connection_parameters(self.configuration)
         with connect(**connection_parameters) as connection:
@@ -509,21 +517,23 @@ class SnowflakeExplorable:
 
         return STRING
 
-    def get_values(
+    def _build_where_clause(
         self,
-        dimension: Dimension,
-        filters: set[Filter | NativeFilter] | None = None,
-    ) -> set[Any]:
+        filters: set[Filter | NativeFilter] | None,
+    ) -> tuple[str, tuple[FilterValues]]:
         """
-        Return distinct values for a dimension.
+        Convert a set of filters to a SQL WHERE clause.
         """
+        if not filters:
+            return "", ()
+
         # convert filters predicate with associated parameters; native filters 
are
         # already strings, so we keep them as-is
         unary_operators = {Operator.IS_NULL, Operator.IS_NOT_NULL}
         predicates: list[str] = []
         parameters: list[FilterValues] = []
         for filter_ in filters or set():
-            if isinstance(filter, NativeFilter):
+            if isinstance(filter_, NativeFilter):
                 predicates.append(f"({filter_.definition})")
             else:
                 predicates.append(f"({self._build_predicate(filter_)})")
@@ -534,19 +544,32 @@ class SnowflakeExplorable:
                         else filter_.value
                     )
 
-        query = f"""
+        return "WHERE " + " AND ".join(predicates), tuple(parameters)
+
+    def get_values(
+        self,
+        dimension: Dimension,
+        filters: set[Filter | NativeFilter] | None = None,
+    ) -> set[Any]:
+        """
+        Return distinct values for a dimension.
+        """
+        where_clause, parameters = self._build_where_clause(filters)
+        query = dedent(
+            f"""
             SELECT {self._quote(dimension.name)}
-            FROM
-                SEMANTIC_VIEW(
-                    {self.uid()}
-                    DIMENSIONS {dimension.id}
-                )
-            {"WHERE " + " AND ".join(predicates) if predicates else ""}
-        """  # noqa: S608
+            FROM SEMANTIC_VIEW(
+                {self.uid()}
+                DIMENSIONS {dimension.id}
+                {where_clause}
+            )
+            """  # noqa: S608
+        )
+        print(query)
         connection_parameters = get_connection_parameters(self.configuration)
         with connect(**connection_parameters) as connection:
             cursor = connection.cursor()
-            return {row[0] for row in cursor.execute(query, tuple(parameters))}
+            return {row[0] for row in cursor.execute(query, parameters)}
 
     def _build_predicate(self, filter_: Filter) -> str:
         """
@@ -571,15 +594,65 @@ class SnowflakeExplorable:
 
         return f"{column_name} {operator.value} ?"
 
-    def get_dataframe(self, query_object: QueryObject) -> DataFrame:
+    # TODO (betodealmeida): create a class to manage the explorable request 
and pass it
+    # here, instead of individual parameters
+    def get_dataframe(
+        self,
+        metrics: list[Metric],
+        dimensions: list[Dimension],
+        filters: set[Filter | NativeFilter] | None = None,
+    ) -> DataFrame:
         """
         Execute a query and return the results as a Pandas DataFrame.
         """
-        pass
+        dimension_arguments = ", ".join(
+            f"{dimension.id} AS {self._quote(dimension.id)}" for dimension in 
dimensions
+        )
+        metric_arguments = ", ".join(
+            f"{metric.id} AS {self._quote(metric.id)}"
+            for metric in metrics
+            for metric in metrics
+        )
+
+        where_clause, parameters = self._build_where_clause(filters)
+        query = dedent(
+            f"""
+            SELECT * FROM SEMANTIC_VIEW(
+                {self.uid()}
+                DIMENSIONS
+                    {dimension_arguments}
+                METRICS
+                    {metric_arguments}
+                {where_clause}
+            )
+            """  # noqa: S608
+        )
+        print(query)
 
     __repr__ = uid
 
 
+"""
+  query_object = QueryObject(
+      columns=["I_BRAND"],
+      metrics=["TOTALSALESPRICE"],
+      row_limit=10
+  )
+
+  Generated SQL
+
+  SELECT * FROM SEMANTIC_VIEW(
+    TPCDS_SEMANTIC_VIEW_SM
+    DIMENSIONS
+      ITEM.BRAND
+    METRICS
+      STORESALES.TOTALSALESPRICE
+  )
+  ORDER BY TOTALSALESPRICE DESC
+  LIMIT 10
+"""
+
+
 if __name__ == "__main__":
     import os
 
@@ -641,3 +714,30 @@ if __name__ == "__main__":
         Filter(dimension, Operator.IN, frozenset({"Children", "Electronics"})),
     }
     print(explorable.get_values(dimension, filters))
+    print(
+        explorable.get_dataframe(
+            [
+                Metric(
+                    "STORESALES.TOTALSALESPRICE",
+                    "TOTALSALESPRICE",
+                    NUMBER,
+                    None,
+                    None,
+                )
+            ],
+            [
+                Dimension(
+                    id="ITEM.CATEGORY",
+                    name="CATEGORY",
+                    type=STRING,
+                    description=None,
+                    definition="I_CATEGORY",
+                    grain=None,
+                )
+            ],
+            {
+                NativeFilter("Year = '2002'"),
+                NativeFilter("Month = '12'"),
+            },
+        )
+    )

Reply via email to