@@ -128,6 +128,17 @@ def _macro_str_replace(text: str) -> str:
128128 return f"self.template({ text } , locals())"
129129
130130
131+ class CaseInsensitiveMapping (t .Dict [str , t .Any ]):
132+ def __init__ (self , data : t .Dict [str , t .Any ]) -> None :
133+ super ().__init__ (data )
134+
135+ def __getitem__ (self , key : str ) -> t .Any :
136+ return super ().__getitem__ (key .lower ())
137+
138+ def get (self , key : str , default : t .Any = None , / ) -> t .Any :
139+ return super ().get (key .lower (), default )
140+
141+
131142class MacroDialect (Python ):
132143 class Generator (Python .Generator ):
133144 TRANSFORMS = {
@@ -256,14 +267,18 @@ def evaluate_macros(
256267 changed = True
257268 variables = self .variables
258269
259- if node .name not in self .locals and node .name .lower () not in variables :
270+ # This makes all variables case-insensitive, e.g. @X is the same as @x. We do this
271+ # for consistency, since `variables` and `blueprint_variables` are normalized.
272+ var_name = node .name .lower ()
273+
274+ if var_name not in self .locals and var_name not in variables :
260275 if not isinstance (node .parent , StagedFilePath ):
261276 raise SQLMeshError (f"Macro variable '{ node .name } ' is undefined." )
262277
263278 return node
264279
265280 # Precedence order is locals (e.g. @DEF) > blueprint variables > config variables
266- value = self .locals .get (node . name , variables .get (node . name . lower () ))
281+ value = self .locals .get (var_name , variables .get (var_name ))
267282 if isinstance (value , list ):
268283 return exp .convert (
269284 tuple (
@@ -313,11 +328,11 @@ def template(self, text: t.Any, local_variables: t.Dict[str, t.Any]) -> str:
313328 """
314329 # We try to convert all variables into sqlglot expressions because they're going to be converted
315330 # into strings; in sql we don't convert strings because that would result in adding quotes
316- mapping = {
317- k : convert_sql (v , self .dialect )
331+ base_mapping = {
332+ k . lower () : convert_sql (v , self .dialect )
318333 for k , v in chain (self .variables .items (), self .locals .items (), local_variables .items ())
319334 }
320- return MacroStrTemplate (str (text )).safe_substitute (mapping )
335+ return MacroStrTemplate (str (text )).safe_substitute (CaseInsensitiveMapping ( base_mapping ) )
321336
322337 def evaluate (self , node : MacroFunc ) -> exp .Expression | t .List [exp .Expression ] | None :
323338 if isinstance (node , MacroDef ):
@@ -327,7 +342,9 @@ def evaluate(self, node: MacroFunc) -> exp.Expression | t.List[exp.Expression] |
327342 args [0 ] if len (args ) == 1 else exp .Tuple (expressions = list (args ))
328343 )
329344 else :
330- self .locals [node .name ] = self .transform (node .expression )
345+ # Make variables defined through `@DEF` case-insensitive
346+ self .locals [node .name .lower ()] = self .transform (node .expression )
347+
331348 return node
332349
333350 if isinstance (node , (MacroSQL , MacroStrReplace )):
@@ -630,7 +647,7 @@ def substitute(
630647 ) -> exp .Expression | t .List [exp .Expression ] | None :
631648 if isinstance (node , (exp .Identifier , exp .Var )):
632649 if not isinstance (node .parent , exp .Column ):
633- name = node .name
650+ name = node .name . lower ()
634651 if name in args :
635652 return args [name ].copy ()
636653 if name in evaluator .locals :
@@ -663,7 +680,7 @@ def substitute(
663680 return expressions , lambda args : func .this .transform (
664681 substitute ,
665682 {
666- expression .name : arg
683+ expression .name . lower () : arg
667684 for expression , arg in zip (
668685 func .expressions , args .expressions if isinstance (args , exp .Tuple ) else [args ]
669686 )
0 commit comments