From b6327914837ded12c721e7c6a2955af6446e5793 Mon Sep 17 00:00:00 2001 From: William Deng Date: Fri, 2 Dec 2022 13:32:56 -0500 Subject: [PATCH] added renderer for SqlGenerateUuidExpression --- metricflow/sql/render/big_query.py | 8 +++++++ metricflow/sql/render/duckdb_renderer.py | 8 +++++++ metricflow/sql/render/expr_renderer.py | 7 ++++++ metricflow/sql/render/postgres.py | 12 +++++++++- metricflow/sql/render/redshift.py | 21 +++++++++++++++++- metricflow/sql/render/snowflake.py | 28 ++++++++++++++++++++++++ metricflow/sql_clients/snowflake.py | 4 ++-- 7 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 metricflow/sql/render/snowflake.py diff --git a/metricflow/sql/render/big_query.py b/metricflow/sql/render/big_query.py index fbebc82787..560efb870c 100644 --- a/metricflow/sql/render/big_query.py +++ b/metricflow/sql/render/big_query.py @@ -4,9 +4,11 @@ SqlExpressionRenderResult, ) from metricflow.sql.render.sql_plan_renderer import DefaultSqlQueryPlanRenderer +from metricflow.sql.sql_bind_parameters import SqlBindParameters from metricflow.sql.sql_exprs import ( SqlCastToTimestampExpression, SqlDateTruncExpression, + SqlGenerateUuidExpression, SqlTimeDeltaExpression, ) from metricflow.time.time_granularity import TimeGranularity @@ -57,6 +59,12 @@ def visit_time_delta_expr(self, node: SqlTimeDeltaExpression) -> SqlExpressionRe execution_parameters=column.execution_parameters, ) + def visit_generate_uuid_expr(self, node: SqlGenerateUuidExpression) -> SqlExpressionRenderResult: # noqa: D + return SqlExpressionRenderResult( + sql="GENERATE_UUID()", + execution_parameters=SqlBindParameters(), + ) + class BigQuerySqlQueryPlanRenderer(DefaultSqlQueryPlanRenderer): """Plan renderer for the BigQuery engine.""" diff --git a/metricflow/sql/render/duckdb_renderer.py b/metricflow/sql/render/duckdb_renderer.py index 872f62570a..c7a3200b96 100644 --- a/metricflow/sql/render/duckdb_renderer.py +++ b/metricflow/sql/render/duckdb_renderer.py @@ -4,7 +4,9 @@ SqlExpressionRenderResult, ) from metricflow.sql.render.sql_plan_renderer import DefaultSqlQueryPlanRenderer +from metricflow.sql.sql_bind_parameters import SqlBindParameters from metricflow.sql.sql_exprs import ( + SqlGenerateUuidExpression, SqlTimeDeltaExpression, ) from metricflow.time.time_granularity import TimeGranularity @@ -32,6 +34,12 @@ def visit_time_delta_expr(self, node: SqlTimeDeltaExpression) -> SqlExpressionRe execution_parameters=arg_rendered.execution_parameters, ) + def visit_generate_uuid_expr(self, node: SqlGenerateUuidExpression) -> SqlExpressionRenderResult: # noqa: D + return SqlExpressionRenderResult( + sql="GEN_RANDOM_UUID()", + execution_parameters=SqlBindParameters(), + ) + class DuckDbSqlQueryPlanRenderer(DefaultSqlQueryPlanRenderer): """Plan renderer for the DuckDB engine.""" diff --git a/metricflow/sql/render/expr_renderer.py b/metricflow/sql/render/expr_renderer.py index 55e550b15b..4febe63fde 100644 --- a/metricflow/sql/render/expr_renderer.py +++ b/metricflow/sql/render/expr_renderer.py @@ -17,6 +17,7 @@ SqlComparisonExpression, SqlExpressionNode, SqlFunction, + SqlGenerateUuidExpression, SqlAggregateFunctionExpression, SqlNullExpression, SqlLogicalExpression, @@ -314,3 +315,9 @@ def visit_window_function_expr(self, node: SqlWindowFunctionExpression) -> SqlEx sql=f"{node.sql_function.value}({sql_function_args_string}) OVER ({window_string})", execution_parameters=combined_params, ) + + def visit_generate_uuid_expr(self, node: SqlGenerateUuidExpression) -> SqlExpressionRenderResult: # noqa: D + return SqlExpressionRenderResult( + sql="UUID()", + execution_parameters=SqlBindParameters(), + ) diff --git a/metricflow/sql/render/postgres.py b/metricflow/sql/render/postgres.py index 9a858f40d9..4f66cb63d3 100644 --- a/metricflow/sql/render/postgres.py +++ b/metricflow/sql/render/postgres.py @@ -4,7 +4,11 @@ SqlExpressionRenderResult, ) from metricflow.sql.render.sql_plan_renderer import DefaultSqlQueryPlanRenderer -from metricflow.sql.sql_exprs import SqlTimeDeltaExpression +from metricflow.sql.sql_bind_parameters import SqlBindParameters +from metricflow.sql.sql_exprs import ( + SqlGenerateUuidExpression, + SqlTimeDeltaExpression, +) from metricflow.time.time_granularity import TimeGranularity @@ -34,6 +38,12 @@ def visit_time_delta_expr(self, node: SqlTimeDeltaExpression) -> SqlExpressionRe execution_parameters=arg_rendered.execution_parameters, ) + def visit_generate_uuid_expr(self, node: SqlGenerateUuidExpression) -> SqlExpressionRenderResult: # noqa: D + return SqlExpressionRenderResult( + sql="GEN_RANDOM_UUID()", + execution_parameters=SqlBindParameters(), + ) + class PostgresSQLSqlQueryPlanRenderer(DefaultSqlQueryPlanRenderer): """Plan renderer for the PostgreSQL engine.""" diff --git a/metricflow/sql/render/redshift.py b/metricflow/sql/render/redshift.py index 7ff20c41b1..d1403f8cf4 100644 --- a/metricflow/sql/render/redshift.py +++ b/metricflow/sql/render/redshift.py @@ -1,5 +1,11 @@ -from metricflow.sql.render.expr_renderer import DefaultSqlExpressionRenderer, SqlExpressionRenderer +from metricflow.sql.render.expr_renderer import ( + DefaultSqlExpressionRenderer, + SqlExpressionRenderer, + SqlExpressionRenderResult, +) from metricflow.sql.render.sql_plan_renderer import DefaultSqlQueryPlanRenderer +from metricflow.sql.sql_bind_parameters import SqlBindParameters +from metricflow.sql.sql_exprs import SqlGenerateUuidExpression class RedshiftSqlExpressionRenderer(DefaultSqlExpressionRenderer): @@ -10,6 +16,19 @@ def double_data_type(self) -> str: """Custom double data type for the Redshift engine""" return "DOUBLE PRECISION" + def visit_generate_uuid_expr(self, node: SqlGenerateUuidExpression) -> SqlExpressionRenderResult: # noqa: D + """Generates a "good enough" random key to simulate a UUID. + + NOTE: This is a temporary hacky solution as redshift does not have any UUID generation function. + + Proposed solutions that requires more thinking: + - create a python UDF (Could we insert this without needing additional permissions?) + """ + return SqlExpressionRenderResult( + sql="CONCAT(CAST(RANDOM()*100000000 AS INT)::VARCHAR,CAST(RANDOM()*100000000 AS INT)::VARCHAR)", + execution_parameters=SqlBindParameters(), + ) + class RedshiftSqlQueryPlanRenderer(DefaultSqlQueryPlanRenderer): """Plan renderer for the Redshift engine.""" diff --git a/metricflow/sql/render/snowflake.py b/metricflow/sql/render/snowflake.py new file mode 100644 index 0000000000..83a0f84889 --- /dev/null +++ b/metricflow/sql/render/snowflake.py @@ -0,0 +1,28 @@ +from metricflow.sql.render.expr_renderer import ( + DefaultSqlExpressionRenderer, + SqlExpressionRenderer, + SqlExpressionRenderResult, +) +from metricflow.sql.render.sql_plan_renderer import DefaultSqlQueryPlanRenderer +from metricflow.sql.sql_bind_parameters import SqlBindParameters +from metricflow.sql.sql_exprs import SqlGenerateUuidExpression + + +class SnowflakeSqlExpressionRenderer(DefaultSqlExpressionRenderer): + """Expression renderer for the Snowflake engine.""" + + def visit_generate_uuid_expr(self, node: SqlGenerateUuidExpression) -> SqlExpressionRenderResult: # noqa: D + return SqlExpressionRenderResult( + sql="UUID_STRING()", + execution_parameters=SqlBindParameters(), + ) + + +class SnowflakeSqlQueryPlanRenderer(DefaultSqlQueryPlanRenderer): + """Plan renderer for the Snowflake engine.""" + + EXPR_RENDERER = SnowflakeSqlExpressionRenderer() + + @property + def expr_renderer(self) -> SqlExpressionRenderer: # noqa :D + return self.EXPR_RENDERER diff --git a/metricflow/sql_clients/snowflake.py b/metricflow/sql_clients/snowflake.py index 18f25401fb..e0e15001a2 100644 --- a/metricflow/sql_clients/snowflake.py +++ b/metricflow/sql_clients/snowflake.py @@ -12,6 +12,7 @@ import sqlalchemy from sqlalchemy.exc import ProgrammingError +from metricflow.sql.render.snowflake import SnowflakeSqlQueryPlanRenderer from metricflow.protocols.sql_client import SqlEngine, SqlIsolationLevel from metricflow.protocols.sql_client import SqlEngineAttributes from metricflow.protocols.sql_request import ( @@ -21,7 +22,6 @@ MF_EXTRA_TAGS_KEY, SqlJsonTag, ) -from metricflow.sql.render.sql_plan_renderer import DefaultSqlQueryPlanRenderer from metricflow.sql.render.sql_plan_renderer import SqlQueryPlanRenderer from metricflow.sql.sql_bind_parameters import SqlBindParameters from metricflow.sql_clients.async_request import SqlStatementCommentMetadata, CombinedSqlTags @@ -56,7 +56,7 @@ class SnowflakeEngineAttributes: random_function_name: ClassVar[str] = "RANDOM" # MetricFlow attributes - sql_query_plan_renderer: ClassVar[SqlQueryPlanRenderer] = DefaultSqlQueryPlanRenderer() + sql_query_plan_renderer: ClassVar[SqlQueryPlanRenderer] = SnowflakeSqlQueryPlanRenderer() class SnowflakeSqlClient(SqlAlchemySqlClient):