Skip to content

Commit ba6b042

Browse files
committed
Add test for variable precedence
1 parent dcaa576 commit ba6b042

2 files changed

Lines changed: 140 additions & 2 deletions

File tree

sqlmesh/core/model/definition.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
from sqlmesh.utils.pydantic import PydanticModel, PRIVATE_FIELDS
5353
from sqlmesh.utils.metaprogramming import (
5454
Executable,
55+
SqlValue,
5556
build_env,
5657
prepare_env,
5758
serialize_env,
@@ -1749,8 +1750,10 @@ def render(
17491750
variables = env.get(c.SQLMESH_VARS, {})
17501751
variables.update(kwargs.pop("variables", {}))
17511752

1752-
blueprint_variables = env.get(c.SQLMESH_BLUEPRINT_VARS, {})
1753-
1753+
blueprint_variables = {
1754+
k: d.parse_one(v.sql, dialect=self.dialect) if isinstance(v, SqlValue) else v
1755+
for k, v in env.get(c.SQLMESH_BLUEPRINT_VARS, {}).items()
1756+
}
17541757
try:
17551758
kwargs = {
17561759
**variables,

tests/core/test_model.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8405,6 +8405,141 @@ def test_blueprinting_with_quotes(tmp_path: Path) -> None:
84058405
assert t.cast(exp.Query, m2.render_query()).sql() == '''SELECT 'c d' AS "c1", "c d" AS "c2"'''
84068406

84078407

8408+
def test_blueprint_variable_precedence_sql(tmp_path: Path, assert_exp_eq: t.Callable) -> None:
8409+
init_example_project(tmp_path, dialect="duckdb", template=ProjectTemplate.EMPTY)
8410+
8411+
blueprint_variables = tmp_path / "models/blueprint_variables.sql"
8412+
blueprint_variables.parent.mkdir(parents=True, exist_ok=True)
8413+
blueprint_variables.write_text(
8414+
"""
8415+
MODEL (
8416+
name s.@{bp_name},
8417+
blueprints (
8418+
(bp_name := m1, var1 := 'v1', var2 := 'v2'),
8419+
(bp_name := m2, var1 := 'v3'),
8420+
),
8421+
);
8422+
8423+
@DEF(bp_name, override);
8424+
8425+
SELECT
8426+
@var1 AS var1_macro_var,
8427+
@{var1} AS var1_identifier,
8428+
@VAR('var1') AS var1_var_macro_func,
8429+
@BLUEPRINT_VAR('var1') AS var1_blueprint_var_macro_func,
8430+
8431+
@var2 AS var2_macro_var,
8432+
@{var2} AS var2_identifier,
8433+
@VAR('var2') AS var2_var_macro_func,
8434+
@BLUEPRINT_VAR('var2') AS var2_blueprint_var_macro_func,
8435+
8436+
@bp_name AS bp_name_macro_var,
8437+
@{bp_name} AS bp_name_identifier,
8438+
@VAR('bp_name') AS bp_name_var_macro_func,
8439+
@BLUEPRINT_VAR('bp_name') AS bp_name_blueprint_var_macro_func,
8440+
"""
8441+
)
8442+
8443+
ctx = Context(
8444+
config=Config(
8445+
model_defaults=ModelDefaultsConfig(dialect="duckdb"),
8446+
variables={"var2": "1"},
8447+
),
8448+
paths=tmp_path,
8449+
)
8450+
assert len(ctx.models) == 2
8451+
8452+
m1 = ctx.get_model("s.m1", raise_if_missing=True)
8453+
m2 = ctx.get_model("s.m2", raise_if_missing=True)
8454+
8455+
assert_exp_eq(
8456+
m1.render_query(),
8457+
"""
8458+
SELECT
8459+
'v1' AS "var1_macro_var",
8460+
"v1" AS "var1_identifier",
8461+
NULL AS "var1_var_macro_func",
8462+
'v1' AS "var1_blueprint_var_macro_func",
8463+
'v2' AS "var2_macro_var",
8464+
"v2" AS "var2_identifier",
8465+
'1' AS "var2_var_macro_func",
8466+
'v2' AS "var2_blueprint_var_macro_func",
8467+
"override" AS "bp_name_macro_var",
8468+
"override" AS "bp_name_identifier",
8469+
NULL AS "bp_name_var_macro_func",
8470+
"m1" AS "bp_name_blueprint_var_macro_func"
8471+
""",
8472+
)
8473+
assert_exp_eq(
8474+
m2.render_query(),
8475+
"""
8476+
SELECT
8477+
'v3' AS "var1_macro_var",
8478+
"v3" AS "var1_identifier",
8479+
NULL AS "var1_var_macro_func",
8480+
'v3' AS "var1_blueprint_var_macro_func",
8481+
'1' AS "var2_macro_var",
8482+
"1" AS "var2_identifier",
8483+
'1' AS "var2_var_macro_func",
8484+
NULL AS "var2_blueprint_var_macro_func",
8485+
"override" AS "bp_name_macro_var",
8486+
"override" AS "bp_name_identifier",
8487+
NULL AS "bp_name_var_macro_func",
8488+
"m2" AS "bp_name_blueprint_var_macro_func"
8489+
""",
8490+
)
8491+
8492+
8493+
def test_blueprint_variable_precedence_python(tmp_path: Path, mocker: MockerFixture) -> None:
8494+
init_example_project(tmp_path, dialect="duckdb", template=ProjectTemplate.EMPTY)
8495+
8496+
blueprint_variables = tmp_path / "models/blueprint_variables.py"
8497+
blueprint_variables.parent.mkdir(parents=True, exist_ok=True)
8498+
blueprint_variables.write_text(
8499+
"""
8500+
import pandas as pd
8501+
from sqlglot import exp
8502+
from sqlmesh import model
8503+
8504+
8505+
@model(
8506+
"s.@{bp_name}",
8507+
blueprints=[{"bp_name": "m", "var1": exp.to_column("v1"), "var2": 1}],
8508+
kind="FULL",
8509+
columns={"x": "INT"},
8510+
)
8511+
def entrypoint(context, *args, **kwargs):
8512+
assert "bp_name" not in kwargs
8513+
assert "var1" not in kwargs
8514+
assert kwargs.get("var2") == "1"
8515+
8516+
assert context.var("bp_name") is None
8517+
assert context.var("var1") is None
8518+
assert context.var("var2") == "1"
8519+
8520+
assert context.blueprint_var("bp_name") == "m"
8521+
assert context.blueprint_var("var1") == exp.to_column("v1")
8522+
assert context.blueprint_var("var2") == 1
8523+
8524+
return pd.DataFrame({"x": [1]})
8525+
"""
8526+
)
8527+
8528+
ctx = Context(
8529+
config=Config(
8530+
model_defaults=ModelDefaultsConfig(dialect="duckdb"),
8531+
variables={"var2": "1"},
8532+
),
8533+
paths=tmp_path,
8534+
)
8535+
assert len(ctx.models) == 1
8536+
8537+
m = ctx.get_model("s.m", raise_if_missing=True)
8538+
context = ExecutionContext(mocker.Mock(), {}, None, None)
8539+
8540+
assert list(m.render(context=context))[0].to_dict() == {"x": {0: 1}}
8541+
8542+
84088543
@time_machine.travel("2020-01-01 00:00:00 UTC")
84098544
def test_dynamic_date_spine_model(assert_exp_eq):
84108545
@macro()

0 commit comments

Comments
 (0)