1010import pandas as pd
1111from sqlglot import Dialect , Generator , Parser , Tokenizer , TokenType , exp
1212from sqlglot .dialects .dialect import DialectType
13+ from sqlglot .dialects .snowflake import Snowflake
1314from sqlglot .optimizer .normalize_identifiers import normalize_identifiers
1415from sqlglot .optimizer .scope import traverse_scope
1516from sqlglot .tokens import Token
@@ -89,6 +90,10 @@ def output_name(self) -> str:
8990 return self .this .name
9091
9192
93+ class StagedFilePath (exp .Table ):
94+ """Represents paths to "staged files" in Snowflake."""
95+
96+
9297def _scan_var (self : Tokenizer ) -> None :
9398 param = False
9499 bracket = False
@@ -315,6 +320,22 @@ def _parse_types(
315320 return parsed_type
316321
317322
323+ # Only needed for Snowflake: its "staged file" syntax (@<path>) clashes with our macro
324+ # var syntax. By converting the Var representation to a MacroVar, we should be able to
325+ # handle both use cases: if there's no value in the MacroEvaluator's context for that
326+ # MacroVar, it'll render into @<path>, so it won't break staged file path references.
327+ #
328+ # See: https://docs.snowflake.com/en/user-guide/querying-stage
329+ def _parse_table_parts (self : Parser , schema : bool = False ) -> exp .Table :
330+ table = self .__parse_table_parts (schema = schema ) # type: ignore
331+ table_arg = table .this
332+
333+ if isinstance (table_arg , exp .Var ) and table_arg .name .startswith ("@" ):
334+ return StagedFilePath (this = MacroVar (this = table_arg .name [1 :]))
335+
336+ return table
337+
338+
318339def _create_parser (parser_type : t .Type [exp .Expression ], table_keys : t .List [str ]) -> t .Callable :
319340 def parse (self : Parser ) -> t .Optional [exp .Expression ]:
320341 from sqlmesh .core .model .kind import ModelKindName
@@ -634,18 +655,19 @@ def extend_sqlglot() -> None:
634655 {
635656 Audit : lambda self , e : _sqlmesh_ddl_sql (self , e , "Audit" ),
636657 DColonCast : lambda self , e : f"{ self .sql (e , 'this' )} ::{ self .sql (e , 'to' )} " ,
658+ Jinja : lambda self , e : e .name ,
659+ JinjaQuery : lambda self , e : f"{ JINJA_QUERY_BEGIN } ;\n { e .name } \n { JINJA_END } ;" ,
660+ JinjaStatement : lambda self , e : f"{ JINJA_STATEMENT_BEGIN } ;\n { e .name .strip ()} \n { JINJA_END } ;" ,
637661 MacroDef : lambda self , e : f"@DEF({ self .sql (e .this )} , { self .sql (e .expression )} )" ,
638662 MacroFunc : _macro_func_sql ,
639663 MacroStrReplace : lambda self , e : f"@{ self .sql (e .this )} " ,
640664 MacroSQL : lambda self , e : f"@SQL({ self .sql (e .this )} )" ,
641665 MacroVar : lambda self , e : f"@{ e .name } " ,
642666 Metric : lambda self , e : _sqlmesh_ddl_sql (self , e , "METRIC" ),
643667 Model : lambda self , e : _sqlmesh_ddl_sql (self , e , "MODEL" ),
644- Jinja : lambda self , e : e .name ,
645- JinjaQuery : lambda self , e : f"{ JINJA_QUERY_BEGIN } ;\n { e .name } \n { JINJA_END } ;" ,
646- JinjaStatement : lambda self , e : f"{ JINJA_STATEMENT_BEGIN } ;\n { e .name .strip ()} \n { JINJA_END } ;" ,
647668 ModelKind : _model_kind_sql ,
648669 PythonCode : lambda self , e : self .expressions (e , sep = "\n " , indent = False ),
670+ StagedFilePath : lambda self , e : self .table_sql (e ),
649671 }
650672 )
651673
@@ -661,6 +683,7 @@ def extend_sqlglot() -> None:
661683 _override (Parser , _parse_having )
662684 _override (Parser , _parse_lambda )
663685 _override (Parser , _parse_types )
686+ _override (Snowflake .Parser , _parse_table_parts )
664687
665688
666689def select_from_values (
0 commit comments