From 9d77cbbcae5007a345b0ce58ce5a8aaa5bc2c02f Mon Sep 17 00:00:00 2001 From: sravankumarkunadi Date: Thu, 30 Jul 2026 10:47:06 +0530 Subject: [PATCH] fix(macros): resolve_template fails in audits when this_model is a subquery Signed-off-by: sravankumarkunadi --- sqlmesh/core/macros.py | 8 +++++++- tests/core/test_audit.py | 24 ++++++++++++++++++++++++ tests/core/test_macros.py | 23 +++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/sqlmesh/core/macros.py b/sqlmesh/core/macros.py index 8706897120..ed06c85821 100644 --- a/sqlmesh/core/macros.py +++ b/sqlmesh/core/macros.py @@ -1402,7 +1402,13 @@ def resolve_template( "'s3://data-bucket/prod/test_catalog/sqlmesh__test/test__test_model__2517971505'" """ if "this_model" in evaluator.locals: - this_model = exp.to_table(evaluator.locals["this_model"], dialect=evaluator.dialect) + this_model_expr = evaluator.locals["this_model"] + if isinstance(this_model_expr, exp.Subquery): + # Audits on models with a time column render @this_model as a subquery that filters the + # physical table on the audited time range, so extract the table it selects from + this_model_expr = this_model_expr.find(exp.Table) or this_model_expr + + this_model = exp.to_table(this_model_expr, dialect=evaluator.dialect) template_str: str = template.this result = ( template_str.replace("@{catalog_name}", this_model.catalog) diff --git a/tests/core/test_audit.py b/tests/core/test_audit.py index 90ac655cc6..b5226563b5 100644 --- a/tests/core/test_audit.py +++ b/tests/core/test_audit.py @@ -413,6 +413,30 @@ def test_macro(model: Model): assert model.render_audit_query(audit_jinja).sql() == expected_query +def test_resolve_template(model_default_catalog: Model): + # @this_model is rendered as a subquery for models with a time column, but @resolve_template + # should still resolve to the underlying physical table + audit = ModelAudit( + name="test_audit", + query="SELECT * FROM @resolve_template('@{catalog_name}.@{schema_name}.@{table_name}$partitions', mode := 'table') WHERE a IS NULL", + ) + + assert ( + model_default_catalog.render_audit_query(audit).sql() + == """SELECT * FROM "test_catalog"."db"."test_model$partitions" AS "test_model$partitions" WHERE "a" IS NULL""" + ) + + literal_audit = ModelAudit( + name="test_audit", + query="SELECT @resolve_template('s3://bucket/@{catalog_name}/@{schema_name}/@{table_name}') AS path FROM @this_model", + ) + + assert ( + model_default_catalog.render_audit_query(literal_audit).sql() + == """SELECT 's3://bucket/test_catalog/db/test_model' AS "path" FROM (SELECT * FROM "test_catalog"."db"."test_model" AS "test_model" WHERE "ds" BETWEEN '1970-01-01' AND '1970-01-01') AS "_0\"""" + ) + + def test_load_with_defaults(model, assert_exp_eq): expressions = parse( """ diff --git a/tests/core/test_macros.py b/tests/core/test_macros.py index 17b699d11f..d8e9183d18 100644 --- a/tests/core/test_macros.py +++ b/tests/core/test_macros.py @@ -1138,6 +1138,29 @@ def test_resolve_template_table(): ) +def test_resolve_template_subquery(): + # Audits on models with a time column render @this_model as a subquery that filters the + # physical table on the time range, so the underlying table needs to be extracted from it + parsed_sql = parse_one( + "SELECT * FROM @resolve_template('@{catalog_name}.@{schema_name}.@{table_name}$partitions', mode := 'table')" + ) + + evaluator = MacroEvaluator(runtime_stage=RuntimeStage.EVALUATING) + evaluator.locals.update( + { + "this_model": exp.select("*") + .from_(exp.to_table("test_catalog.sqlmesh__test.test__test_model__2517971505")) + .where(exp.column("ds").between("2020-01-01", "2020-01-02")) + .subquery() + } + ) + + assert ( + evaluator.transform(parsed_sql).sql(identify=True) + == 'SELECT * FROM "test_catalog"."sqlmesh__test"."test__test_model__2517971505$partitions"' + ) + + def test_macro_with_spaces(): evaluator = MacroEvaluator() evaluator.evaluate(d.parse_one(""" @DEF(x, "a b") """))