Skip to content

Commit f1d1504

Browse files
authored
Fix: Enable schema binding when creating views for queries with recursive CTEs in redshift (#4006)
1 parent a7c43fe commit f1d1504

File tree

3 files changed

+48
-8
lines changed

3 files changed

+48
-8
lines changed

sqlmesh/core/engine_adapter/mixins.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -306,9 +306,7 @@ def _build_create_table_exp(
306306

307307
temp_view_name = self._get_temp_table("ctas")
308308

309-
self.create_view(
310-
temp_view_name, select_statement, replace=False, no_schema_binding=False
311-
)
309+
self.create_view(temp_view_name, select_statement, replace=False)
312310
try:
313311
columns_to_types_from_view = self._default_precision_to_max(
314312
self.columns(temp_view_name)

sqlmesh/core/engine_adapter/redshift.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,13 @@ def create_view(
217217
underlying table without dropping the view first. This is a problem for us since we want to be able to
218218
swap tables out from under views. Therefore, we create the view as non-binding.
219219
"""
220-
221-
if create_kwargs.pop("no_schema_binding", None) is False:
222-
logger.warning(
223-
"The 'no_schema_binding' attribute is deprecated. Views in Redshift are created as non-binding."
220+
no_schema_binding = True
221+
if isinstance(query_or_df, exp.Expression):
222+
# We can't include NO SCHEMA BINDING if the query has a recursive CTE
223+
has_recursive_cte = any(
224+
w.args.get("recursive", False) for w in query_or_df.find_all(exp.With)
224225
)
226+
no_schema_binding = not has_recursive_cte
225227

226228
return super().create_view(
227229
view_name,
@@ -232,7 +234,7 @@ def create_view(
232234
materialized_properties,
233235
table_description=table_description,
234236
column_descriptions=column_descriptions,
235-
no_schema_binding=True,
237+
no_schema_binding=no_schema_binding,
236238
view_properties=view_properties,
237239
**create_kwargs,
238240
)

tests/core/engine_adapter/test_redshift.py

+40
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,46 @@ def test_create_table_from_query_exists_no_if_not_exists(
123123
columns_mock.assert_called_once_with(exp.table_("__temp_ctas_test_random_id", quoted=True))
124124

125125

126+
def test_create_table_recursive_cte(adapter: t.Callable, mocker: MockerFixture):
127+
mocker.patch(
128+
"sqlmesh.core.engine_adapter.base.random_id",
129+
return_value="test_random_id",
130+
)
131+
132+
mocker.patch(
133+
"sqlmesh.core.engine_adapter.redshift.RedshiftEngineAdapter.table_exists",
134+
return_value=True,
135+
)
136+
137+
columns_mock = mocker.patch(
138+
"sqlmesh.core.engine_adapter.redshift.RedshiftEngineAdapter.columns",
139+
return_value={
140+
"a": exp.DataType.build("VARCHAR(MAX)", dialect="redshift"),
141+
"b": exp.DataType.build("VARCHAR(60)", dialect="redshift"),
142+
"c": exp.DataType.build("VARCHAR(MAX)", dialect="redshift"),
143+
"d": exp.DataType.build("VARCHAR(MAX)", dialect="redshift"),
144+
"e": exp.DataType.build("TIMESTAMP", dialect="redshift"),
145+
},
146+
)
147+
148+
adapter.ctas(
149+
table_name="test_schema.test_table",
150+
query_or_df=parse_one(
151+
"WITH RECURSIVE cte AS (SELECT * FROM table WHERE FALSE LIMIT 0) SELECT a, b, x + 1 AS c, d AS d, e FROM cte WHERE d > 0 AND FALSE LIMIT 0",
152+
dialect="redshift",
153+
),
154+
exists=False,
155+
)
156+
157+
assert to_sql_calls(adapter) == [
158+
'CREATE VIEW "__temp_ctas_test_random_id" AS WITH RECURSIVE "cte" AS (SELECT * FROM "table") SELECT "a", "b", "x" + 1 AS "c", "d" AS "d", "e" FROM "cte"',
159+
'DROP VIEW IF EXISTS "__temp_ctas_test_random_id" CASCADE',
160+
'CREATE TABLE "test_schema"."test_table" ("a" VARCHAR(MAX), "b" VARCHAR(60), "c" VARCHAR(MAX), "d" VARCHAR(MAX), "e" TIMESTAMP)',
161+
]
162+
163+
columns_mock.assert_called_once_with(exp.table_("__temp_ctas_test_random_id", quoted=True))
164+
165+
126166
def test_create_table_from_query_exists_and_if_not_exists(
127167
adapter: t.Callable, mocker: MockerFixture
128168
):

0 commit comments

Comments
 (0)