Repository: TobikoData/sqlmesh
Pull request #4293 (2025-05-01 13:21:39)

Fix of render definition method for virtual update statements
Casting virtual update expressions as VirtualUpdateStatement


Patch:
diff --git a/sqlmesh/core/model/definition.py b/sqlmesh/core/model/definition.py
index 34f5dd55..ff46232d 100644
--- a/sqlmesh/core/model/definition.py
+++ b/sqlmesh/core/model/definition.py
@@ -1319,12 +1319,14 @@ class SqlModel(_Model):
             result.extend(self.render_pre_statements())
             result.append(self.render_query() or self.query)
             result.extend(self.render_post_statements())
-            result.extend(self.render_on_virtual_update())
+            if virtual_update := self.render_on_virtual_update():
+                result.append(d.VirtualUpdateStatement(expressions=virtual_update))
         else:
             result.extend(self.pre_statements)
             result.append(self.query)
             result.extend(self.post_statements)
-            result.extend(self.on_virtual_update)
+            if self.on_virtual_update:
+                result.append(d.VirtualUpdateStatement(expressions=self.on_virtual_update))
 
         return result
 


Test patch:
diff --git a/tests/core/test_context.py b/tests/core/test_context.py
index 56094040..3fdef03f 100644
--- a/tests/core/test_context.py
+++ b/tests/core/test_context.py
@@ -1290,25 +1290,30 @@ def test_rendered_diff():
 
     plan = ctx.plan("dev", auto_apply=True, no_prompts=True, diff_rendered=True)
 
-    assert '''@@ -4,13 +4,13 @@
-
- CREATE TABLE IF NOT EXISTS "foo" AS
- (
-   SELECT
--    FALSE OR TRUE
-+    TRUE
- )
- SELECT
--  6 AS "_col_0"
-+  7 AS "_col_0"
- CREATE TABLE IF NOT EXISTS "foo2" AS
- (
-   SELECT
--    TRUE AND FALSE
-+    TRUE
- )
--DROP VIEW "test"
-+DROP VIEW IF EXISTS "test"''' in plan.context_diff.text_diff('"test"')
+    assert plan.context_diff.text_diff('"test"') == (
+        "--- \n\n"
+        "+++ \n\n"
+        "@@ -4,15 +4,15 @@\n\n"
+        ' CREATE TABLE IF NOT EXISTS "foo" AS\n'
+        " (\n"
+        "   SELECT\n"
+        "-    FALSE OR TRUE\n"
+        "+    TRUE\n"
+        " )\n"
+        " SELECT\n"
+        '-  6 AS "_col_0"\n'
+        '+  7 AS "_col_0"\n'
+        ' CREATE TABLE IF NOT EXISTS "foo2" AS\n'
+        " (\n"
+        "   SELECT\n"
+        "-    TRUE AND FALSE\n"
+        "+    TRUE\n"
+        " )\n"
+        " ON_VIRTUAL_UPDATE_BEGIN;\n"
+        '-DROP VIEW "test";\n'
+        '+DROP VIEW IF EXISTS "test";\n'
+        " ON_VIRTUAL_UPDATE_END;"
+    )
 
 
 def test_plan_enable_preview_default(sushi_context: Context, sushi_dbt_context: Context):
diff --git a/tests/core/test_model.py b/tests/core/test_model.py
index 722cc006..2a72903f 100644
--- a/tests/core/test_model.py
+++ b/tests/core/test_model.py
@@ -1642,6 +1642,43 @@ def test_render_definition_partitioned_by():
     )
 
 
+def test_render_definition_with_virtual_update_statements():
+    # model has virtual update statements
+    model = load_sql_based_model(
+        d.parse(
+            f"""
+        MODEL (
+            name db.table,
+            kind FULL
+        );
+
+        select 1 as a;
+
+        ON_VIRTUAL_UPDATE_BEGIN;
+        GRANT SELECT ON VIEW @this_model TO ROLE role_name
+        ON_VIRTUAL_UPDATE_END;
+        """
+        )
+    )
+
+    assert model.on_virtual_update == [
+        exp.Grant(
+            privileges=[exp.GrantPrivilege(this=exp.Var(this="SELECT"))],
+            kind="VIEW",
+            securable=exp.Table(this=d.MacroVar(this="this_model")),
+            principals=[
+                exp.GrantPrincipal(this=exp.Identifier(this="role_name", quoted=False), kind="ROLE")
+            ],
+        )
+    ]
+    assert (
+        model.render_definition()[-1].sql(pretty=True)
+        == """ON_VIRTUAL_UPDATE_BEGIN;
+GRANT SELECT ON VIEW @this_model TO ROLE role_name;
+ON_VIRTUAL_UPDATE_END;"""
+    )
+
+
 def test_cron():
     daily = _Node(name="x", cron="@daily")
     assert to_datetime(daily.cron_prev("2020-01-01")) == to_datetime("2019-12-31")