Skip to content

Commit cef39cf

Browse files
Fix: In blueprint vars handle the case sensitive case in lookup
1 parent 960bacb commit cef39cf

File tree

4 files changed

+69
-5
lines changed

4 files changed

+69
-5
lines changed

sqlmesh/core/context.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,9 @@ def var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.
314314

315315
def blueprint_var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
316316
"""Returns a blueprint variable value."""
317-
return self._blueprint_variables.get(var_name.lower(), default)
317+
return self._blueprint_variables.get(var_name) or self._blueprint_variables.get(
318+
var_name.lower(), default
319+
)
318320

319321
def with_variables(
320322
self,

sqlmesh/core/macros.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,14 +254,20 @@ def evaluate_macros(
254254
changed = True
255255
variables = self.variables
256256

257-
if node.name not in self.locals and node.name.lower() not in variables:
257+
if (
258+
node.name not in self.locals
259+
and node.name.lower() not in variables
260+
and node.name not in variables
261+
):
258262
if not isinstance(node.parent, StagedFilePath):
259263
raise SQLMeshError(f"Macro variable '{node.name}' is undefined.")
260264

261265
return node
262266

263267
# Precedence order is locals (e.g. @DEF) > blueprint variables > config variables
264-
value = self.locals.get(node.name, variables.get(node.name.lower()))
268+
value = self.locals.get(
269+
node.name, variables.get(node.name, variables.get(node.name.lower()))
270+
)
265271
if isinstance(value, list):
266272
return exp.convert(
267273
tuple(
@@ -532,7 +538,9 @@ def var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.
532538

533539
def blueprint_var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
534540
"""Returns the value of the specified blueprint variable, or the default value if it doesn't exist."""
535-
return (self.locals.get(c.SQLMESH_BLUEPRINT_VARS) or {}).get(var_name.lower(), default)
541+
return (self.locals.get(c.SQLMESH_BLUEPRINT_VARS) or {}).get(var_name) or (
542+
self.locals.get(c.SQLMESH_BLUEPRINT_VARS) or {}
543+
).get(var_name.lower(), default)
536544

537545
@property
538546
def variables(self) -> t.Dict[str, t.Any]:

sqlmesh/core/model/common.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,9 @@ def var(var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
199199

200200
@staticmethod
201201
def blueprint_var(var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
202-
return (blueprint_variables or {}).get(var_name.lower(), default)
202+
return (blueprint_variables or {}).get(var_name) or (blueprint_variables or {}).get(
203+
var_name.lower(), default
204+
)
203205

204206
env = prepare_env(python_env)
205207
local_env = dict.fromkeys(("context", "evaluator"), VariableResolutionContext)

tests/core/test_model.py

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

94659465

9466+
def test_blueprinting_with_uppercase_blueprint_names(tmp_path: Path) -> None:
9467+
init_example_project(tmp_path, engine_type="duckdb", template=ProjectTemplate.EMPTY)
9468+
9469+
template_with_uppercase_vars = tmp_path / "models/template_with_uppercase_vars.sql"
9470+
template_with_uppercase_vars.parent.mkdir(parents=True, exist_ok=True)
9471+
template_with_uppercase_vars.write_text(
9472+
"""
9473+
MODEL (
9474+
name @{Customer_Name}.my_table,
9475+
blueprints (
9476+
(Customer_Name := customer1, Field_A := 'value1', Field_B := 100),
9477+
(Customer_Name := customer2, Field_A := 'value2', Field_B := 200),
9478+
),
9479+
);
9480+
9481+
SELECT
9482+
@Customer_Name AS customer_name,
9483+
@Field_A AS field_a_macro,
9484+
@{Field_B} AS field_b_identifier,
9485+
@BLUEPRINT_VAR('Field_A') AS field_a_func,
9486+
@BLUEPRINT_VAR('Field_B') AS field_b_func_lower
9487+
"""
9488+
)
9489+
9490+
ctx = Context(
9491+
config=Config(model_defaults=ModelDefaultsConfig(dialect="duckdb")), paths=tmp_path
9492+
)
9493+
assert len(ctx.models) == 2
9494+
9495+
m1 = ctx.get_model('"memory"."customer1"."my_table"', raise_if_missing=True)
9496+
m2 = ctx.get_model('"memory"."customer2"."my_table"', raise_if_missing=True)
9497+
9498+
# Verify that uppercase references in the query work correctly
9499+
query1 = t.cast(exp.Query, m1.render_query()).sql()
9500+
query2 = t.cast(exp.Query, m2.render_query()).sql()
9501+
9502+
assert '"customer1"' in query1
9503+
assert "'value1'" in query1
9504+
assert "100" in query1
9505+
9506+
assert '"customer2"' in query2
9507+
assert "'value2'" in query2
9508+
assert "200" in query2
9509+
9510+
# Verify exact query structure
9511+
expected_query1 = '''SELECT "customer1" AS "customer_name", 'value1' AS "field_a_macro", "100" AS "field_b_identifier", 'value1' AS "field_a_func", 100 AS "field_b_func_lower"'''
9512+
expected_query2 = '''SELECT "customer2" AS "customer_name", 'value2' AS "field_a_macro", "200" AS "field_b_identifier", 'value2' AS "field_a_func", 200 AS "field_b_func_lower"'''
9513+
9514+
assert query1 == expected_query1
9515+
assert query2 == expected_query2
9516+
9517+
94669518
def test_blueprint_variable_precedence_sql(tmp_path: Path, assert_exp_eq: t.Callable) -> None:
94679519
init_example_project(tmp_path, engine_type="duckdb", template=ProjectTemplate.EMPTY)
94689520

0 commit comments

Comments
 (0)