Edit on GitHub

sqlmesh.core.macros

   1from __future__ import annotations
   2
   3import inspect
   4import sys
   5import types
   6import typing as t
   7from enum import Enum
   8from functools import lru_cache, reduce
   9from itertools import chain
  10from pathlib import Path
  11from string import Template
  12from datetime import datetime, date
  13
  14import sqlglot
  15from sqlglot import Generator, exp, parse_one
  16from sqlglot.executor.env import ENV
  17from sqlglot.executor.python import Python
  18from sqlglot.generators.python import PythonGenerator
  19from sqlglot.helper import csv, ensure_collection
  20from sqlglot.optimizer.normalize_identifiers import normalize_identifiers
  21from sqlglot.schema import MappingSchema
  22
  23from sqlmesh.core import constants as c
  24from sqlmesh.core.dialect import (
  25    SQLMESH_MACRO_PREFIX,
  26    Dialect,
  27    MacroDef,
  28    MacroFunc,
  29    MacroSQL,
  30    MacroStrReplace,
  31    MacroVar,
  32    StagedFilePath,
  33    normalize_model_name,
  34)
  35from sqlmesh.utils import (
  36    DECORATOR_RETURN_TYPE,
  37    UniqueKeyDict,
  38    columns_to_types_all_known,
  39    registry_decorator,
  40)
  41from sqlmesh.utils.date import DatetimeRanges, to_datetime, to_date
  42from sqlmesh.utils.errors import MacroEvalError, SQLMeshError
  43from sqlmesh.utils.metaprogramming import (
  44    Executable,
  45    SqlValue,
  46    format_evaluated_code_exception,
  47    prepare_env,
  48)
  49
  50if t.TYPE_CHECKING:
  51    from sqlglot.dialects.dialect import DialectType
  52    from sqlmesh.core._typing import TableName
  53    from sqlmesh.core.engine_adapter import EngineAdapter
  54    from sqlmesh.core.snapshot import Snapshot
  55    from sqlmesh.core.environment import EnvironmentNamingInfo
  56
  57
  58if sys.version_info >= (3, 10):
  59    UNION_TYPES = (t.Union, types.UnionType)
  60else:
  61    UNION_TYPES = (t.Union,)
  62
  63
  64class RuntimeStage(Enum):
  65    LOADING = "loading"
  66    CREATING = "creating"
  67    EVALUATING = "evaluating"
  68    PROMOTING = "promoting"
  69    DEMOTING = "demoting"
  70    AUDITING = "auditing"
  71    TESTING = "testing"
  72    BEFORE_ALL = "before_all"
  73    AFTER_ALL = "after_all"
  74
  75
  76class MacroStrTemplate(Template):
  77    delimiter = SQLMESH_MACRO_PREFIX
  78
  79
  80EXPRESSIONS_NAME_MAP = {}
  81SQL = t.NewType("SQL", str)
  82
  83
  84@lru_cache()
  85def get_supported_types() -> t.Dict[str, t.Any]:
  86    from sqlmesh.core.context import ExecutionContext
  87
  88    return {
  89        "t": t,
  90        "typing": t,
  91        "List": t.List,
  92        "Tuple": t.Tuple,
  93        "Union": t.Union,
  94        "DatetimeRanges": DatetimeRanges,
  95        "exp": exp,
  96        "SQL": SQL,
  97        "MacroEvaluator": MacroEvaluator,
  98        "ExecutionContext": ExecutionContext,
  99    }
 100
 101
 102for klass in sqlglot.Parser.EXPRESSION_PARSERS:
 103    name = klass if isinstance(klass, str) else klass.__name__  # type: ignore
 104    EXPRESSIONS_NAME_MAP[name.lower()] = name
 105
 106
 107def _macro_sql(sql: str, into: t.Optional[str] = None) -> str:
 108    args = [_macro_str_replace(sql)]
 109    if into in EXPRESSIONS_NAME_MAP:
 110        args.append(f"into=exp.{EXPRESSIONS_NAME_MAP[into]}")
 111    return f"self.parse_one({', '.join(args)})"
 112
 113
 114def _macro_func_sql(self: Generator, e: exp.Expr) -> str:
 115    func = e.this
 116
 117    if isinstance(func, exp.Anonymous):
 118        return f"""self.send({csv("'" + func.name + "'", self.expressions(func))})"""
 119    return self.sql(func)
 120
 121
 122def _macro_str_replace(text: str) -> str:
 123    """Stringifies python code for variable replacement
 124    Args:
 125        text: text string
 126    Returns:
 127        Stringified python code to execute variable replacement
 128    """
 129    return f"self.template({text}, locals())"
 130
 131
 132class CaseInsensitiveMapping(t.Dict[str, t.Any]):
 133    def __init__(self, data: t.Dict[str, t.Any]) -> None:
 134        super().__init__(data)
 135
 136    def __getitem__(self, key: str) -> t.Any:
 137        return super().__getitem__(key.lower())
 138
 139    def get(self, key: str, default: t.Any = None, /) -> t.Any:
 140        return super().get(key.lower(), default)
 141
 142
 143class MacroDialect(Python):
 144    class Generator(PythonGenerator):
 145        TRANSFORMS = {
 146            **PythonGenerator.TRANSFORMS,
 147            exp.Column: lambda self, e: f"exp.to_column('{self.sql(e, 'this')}')",
 148            exp.Lambda: lambda self, e: f"lambda {self.expressions(e)}: {self.sql(e, 'this')}",
 149            MacroFunc: _macro_func_sql,
 150            MacroSQL: lambda self, e: _macro_sql(self.sql(e, "this"), e.args.get("into")),
 151            MacroStrReplace: lambda self, e: _macro_str_replace(self.sql(e, "this")),
 152        }
 153
 154
 155class MacroEvaluator:
 156    """The class responsible for evaluating SQLMesh Macros/SQL.
 157
 158    SQLMesh supports special preprocessed SQL prefixed with `@`. Although it provides similar power to
 159    traditional methods like string templating, there is semantic understanding of SQL which prevents
 160    common errors like leading/trailing commas, syntax errors, etc.
 161
 162    SQLMesh SQL allows for macro variables and macro functions. Macro variables take the form of @variable. These are used for variable substitution.
 163
 164    SELECT * FROM foo WHERE ds BETWEEN @start_date AND @end_date
 165
 166    Macro variables can be defined with a special macro function.
 167
 168    @DEF(start_date, '2021-01-01')
 169
 170    Args:
 171        dialect: Dialect of the SQL to evaluate.
 172        python_env: Serialized Python environment.
 173    """
 174
 175    def __init__(
 176        self,
 177        dialect: DialectType = "",
 178        python_env: t.Optional[t.Dict[str, Executable]] = None,
 179        schema: t.Optional[MappingSchema] = None,
 180        runtime_stage: RuntimeStage = RuntimeStage.LOADING,
 181        resolve_table: t.Optional[t.Callable[[str | exp.Table], str]] = None,
 182        resolve_tables: t.Optional[t.Callable[[exp.Expr], exp.Expr]] = None,
 183        snapshots: t.Optional[t.Dict[str, Snapshot]] = None,
 184        default_catalog: t.Optional[str] = None,
 185        path: t.Optional[Path] = None,
 186        environment_naming_info: t.Optional[EnvironmentNamingInfo] = None,
 187        model_fqn: t.Optional[str] = None,
 188    ):
 189        self.dialect = dialect
 190        self.generator = MacroDialect().generator()
 191        self.locals: t.Dict[str, t.Any] = {
 192            "runtime_stage": runtime_stage.value,
 193            "default_catalog": default_catalog,
 194        }
 195        self.env = {
 196            **ENV,
 197            "self": self,
 198            "SQL": SQL,
 199            "MacroEvaluator": MacroEvaluator,
 200        }
 201        self.python_env = python_env or {}
 202        self.macros = {normalize_macro_name(k): v.func for k, v in macro.get_registry().items()}
 203        self.columns_to_types_called = False
 204        self.default_catalog = default_catalog
 205
 206        self._schema = schema
 207        self._resolve_table = resolve_table
 208        self._resolve_tables = resolve_tables
 209        self._snapshots = snapshots if snapshots is not None else {}
 210        self._path = path
 211        self._environment_naming_info = environment_naming_info
 212        self._model_fqn = model_fqn
 213
 214        prepare_env(self.python_env, self.env)
 215        for k, v in self.python_env.items():
 216            if v.is_definition:
 217                self.macros[normalize_macro_name(k)] = self.env[v.name or k]
 218            elif v.is_import and getattr(self.env.get(k), c.SQLMESH_MACRO, None):
 219                self.macros[normalize_macro_name(k)] = self.env[k]
 220            elif v.is_value:
 221                value = self.env[k]
 222                if k in (
 223                    c.SQLMESH_VARS,
 224                    c.SQLMESH_VARS_METADATA,
 225                    c.SQLMESH_BLUEPRINT_VARS,
 226                    c.SQLMESH_BLUEPRINT_VARS_METADATA,
 227                ):
 228                    value = {
 229                        var_name: (
 230                            self.parse_one(var_value.sql)
 231                            if isinstance(var_value, SqlValue)
 232                            else var_value
 233                        )
 234                        for var_name, var_value in value.items()
 235                    }
 236
 237                self.locals[k] = value
 238
 239    def send(
 240        self, name: str, *args: t.Any, **kwargs: t.Any
 241    ) -> t.Union[None, exp.Expr, t.List[exp.Expr]]:
 242        func = self.macros.get(normalize_macro_name(name))
 243
 244        if not callable(func):
 245            raise MacroEvalError(f"Macro '{name}' does not exist.")
 246
 247        try:
 248            return call_macro(
 249                func, self.dialect, self._path, provided_args=(self, *args), provided_kwargs=kwargs
 250            )  # type: ignore
 251        except Exception as e:
 252            raise MacroEvalError(
 253                f"An error occurred during evaluation of '{name}'\n\n"
 254                + format_evaluated_code_exception(e, self.python_env)
 255            )
 256
 257    def transform(self, expression: exp.Expr) -> exp.Expr | t.List[exp.Expr] | None:
 258        changed = False
 259
 260        def evaluate_macros(
 261            node: exp.Expr,
 262        ) -> exp.Expr | t.List[exp.Expr] | None:
 263            nonlocal changed
 264
 265            if isinstance(node, MacroVar):
 266                changed = True
 267                variables = self.variables
 268
 269                # This makes all variables case-insensitive, e.g. @X is the same as @x. We do this
 270                # for consistency, since `variables` and `blueprint_variables` are normalized.
 271                var_name = node.name.lower()
 272
 273                if var_name not in self.locals and var_name not in variables:
 274                    if not isinstance(node.parent, StagedFilePath):
 275                        raise SQLMeshError(f"Macro variable '{node.name}' is undefined.")
 276
 277                    return node
 278
 279                # Precedence order is locals (e.g. @DEF) > blueprint variables > config variables
 280                value = self.locals.get(var_name, variables.get(var_name))
 281                if isinstance(value, list):
 282                    return exp.convert(
 283                        tuple(self.transform(v) if isinstance(v, exp.Expr) else v for v in value)
 284                    )
 285
 286                return exp.convert(self.transform(value) if isinstance(value, exp.Expr) else value)
 287            if isinstance(node, exp.Identifier) and "@" in node.this:
 288                text = self.template(node.this, {})
 289                if node.this != text:
 290                    changed = True
 291                    return exp.to_identifier(text, quoted=node.quoted or None)
 292            if isinstance(node, MacroFunc):
 293                changed = True
 294                return self.evaluate(node)
 295            return node
 296
 297        transformed = exp.replace_tree(
 298            expression.copy(),
 299            evaluate_macros,  # type: ignore[arg-type]
 300            prune=lambda n: isinstance(n, exp.Lambda),
 301        )
 302
 303        if changed:
 304            # the transformations could have corrupted the ast, turning this into sql and reparsing ensures
 305            # that the ast is correct
 306            if isinstance(transformed, list):
 307                return [
 308                    self.parse_one(node.sql(dialect=self.dialect, copy=False))
 309                    for node in transformed
 310                ]
 311            if isinstance(transformed, exp.Expr):
 312                return self.parse_one(transformed.sql(dialect=self.dialect, copy=False))
 313
 314        return transformed
 315
 316    def template(self, text: t.Any, local_variables: t.Dict[str, t.Any]) -> str:
 317        """Substitute @vars with locals.
 318
 319        Args:
 320            text: The string to do substitition on.
 321            local_variables: Local variables in the context so that lambdas can be used.
 322
 323        Returns:
 324           The rendered string.
 325        """
 326        # We try to convert all variables into sqlglot expressions because they're going to be converted
 327        # into strings; in sql we don't convert strings because that would result in adding quotes
 328        base_mapping = {
 329            k.lower(): convert_sql(v, self.dialect)
 330            for k, v in chain(self.variables.items(), self.locals.items(), local_variables.items())
 331            if k.lower()
 332            not in (
 333                "engine_adapter",
 334                "snapshot",
 335            )
 336        }
 337        return MacroStrTemplate(str(text)).safe_substitute(CaseInsensitiveMapping(base_mapping))
 338
 339    def evaluate(self, node: MacroFunc) -> exp.Expr | t.List[exp.Expr] | None:
 340        if isinstance(node, MacroDef):
 341            if isinstance(node.expression, exp.Lambda):
 342                _, fn = _norm_var_arg_lambda(self, node.expression)
 343                self.macros[normalize_macro_name(node.name)] = lambda _, *args: fn(
 344                    args[0] if len(args) == 1 else exp.Tuple(expressions=list(args))
 345                )
 346            else:
 347                # Make variables defined through `@DEF` case-insensitive
 348                self.locals[node.name.lower()] = self.transform(node.expression)
 349
 350            return node
 351
 352        if isinstance(node, (MacroSQL, MacroStrReplace)):
 353            result: t.Optional[exp.Expr | t.List[exp.Expr]] = exp.convert(
 354                self.eval_expression(node)
 355            )
 356        else:
 357            func = t.cast(exp.Anonymous, node.this)
 358
 359            args = []
 360            kwargs = {}
 361            for e in func.expressions:
 362                if isinstance(e, exp.PropertyEQ):
 363                    kwargs[e.this.name] = e.expression
 364                else:
 365                    if kwargs:
 366                        raise MacroEvalError(
 367                            "Positional argument cannot follow keyword argument.\n  "
 368                            f"{func.sql(dialect=self.dialect)} at '{self._path}'"
 369                        )
 370
 371                    args.append(e)
 372
 373            result = self.send(func.name, *args, **kwargs)
 374
 375        if result is None:
 376            return None
 377
 378        if isinstance(result, (tuple, list)):
 379            result = [self.parse_one(item) for item in result if item is not None]
 380
 381            if (
 382                len(result) == 1
 383                and isinstance(result[0], (exp.Array, exp.Tuple))
 384                and node.find_ancestor(MacroFunc)
 385            ):
 386                """
 387                if:
 388                 - the output of evaluating this node is being passed as an argument to another macro function
 389                 - and that output is something that _norm_var_arg_lambda() will unpack into varargs
 390                   > (a list containing a single item of type exp.Tuple/exp.Array)
 391                then we will get inconsistent behaviour depending on if this node emits a list with a single item vs multiple items.
 392                
 393                In the first case, emitting a list containing a single array item will cause that array to get unpacked and its *members* passed to the calling macro
 394                In the second case, emitting a list containing multiple array items will cause each item to get passed as-is to the calling macro
 395                
 396                To prevent this inconsistency, we wrap this node output in an exp.Array so that _norm_var_arg_lambda() can "unpack" that into the
 397                actual argument we want to pass to the parent macro function
 398                
 399                Note we only do this for evaluation results that get passed as an argument to another macro, because when the final
 400                result is given to something like SELECT, we still want that to be unpacked into a list of items like:
 401                 - SELECT ARRAY(1), ARRAY(2)
 402                rather than a single item like:
 403                 - SELECT ARRAY(ARRAY(1), ARRAY(2))                
 404                """
 405                result = [exp.Array(expressions=result)]
 406        else:
 407            result = self.parse_one(result)
 408
 409        return result
 410
 411    def eval_expression(self, node: t.Any) -> t.Any:
 412        """Converts a SQLGlot expression into executable Python code and evals it.
 413
 414        If the node is not an expression, it will simply be returned.
 415
 416        Args:
 417            node: expression
 418        Returns:
 419            The return value of the evaled Python Code.
 420        """
 421        if not isinstance(node, exp.Expr):
 422            return node
 423        code = node.sql()
 424        try:
 425            code = self.generator.generate(node)
 426            return eval(code, self.env, self.locals)
 427        except Exception as e:
 428            raise MacroEvalError(
 429                f"Error trying to eval macro.\n\nGenerated code: {code}\n\nOriginal sql: {node}\n\n"
 430                + format_evaluated_code_exception(e, self.python_env)
 431            )
 432
 433    def parse_one(
 434        self, sql: str | exp.Expr, into: t.Optional[exp.IntoType] = None, **opts: t.Any
 435    ) -> exp.Expr:
 436        """Parses the given SQL string and returns a syntax tree for the first
 437        parsed SQL statement.
 438
 439        Args:
 440            sql: the SQL code or expression to parse.
 441            into: the Expression to parse into
 442            **opts: other options
 443
 444        Returns:
 445            Expression: the syntax tree for the first parsed statement
 446        """
 447        return sqlglot.maybe_parse(sql, dialect=self.dialect, into=into, **opts)
 448
 449    def columns_to_types(self, model_name: TableName | exp.Column) -> t.Dict[str, exp.DataType]:
 450        """Returns the columns-to-types mapping corresponding to the specified model."""
 451
 452        # We only return this dummy schema at load time, because if we don't actually know the
 453        # target model's schema at creation/evaluation time, returning a dummy schema could lead
 454        # to unintelligible errors when the query is executed
 455        if (self._schema is None or self._schema.empty) and self.runtime_stage == "loading":
 456            self.columns_to_types_called = True
 457            return {"__schema_unavailable_at_load__": exp.DataType.build("unknown")}
 458
 459        normalized_model_name = normalize_model_name(
 460            model_name,
 461            default_catalog=self.default_catalog,
 462            dialect=self.dialect,
 463        )
 464        model_name = exp.to_table(normalized_model_name)
 465
 466        columns_to_types = (
 467            self._schema.find(model_name, ensure_data_types=True) if self._schema else None
 468        )
 469        if columns_to_types is None:
 470            snapshot = self.get_snapshot(model_name)
 471            if snapshot and snapshot.node.is_model:
 472                columns_to_types = snapshot.node.columns_to_types  # type: ignore
 473
 474        if columns_to_types is None:
 475            raise SQLMeshError(f"Schema for model '{model_name}' can't be statically determined.")
 476
 477        return columns_to_types
 478
 479    def get_snapshot(self, model_name: TableName | exp.Column) -> t.Optional[Snapshot]:
 480        """Returns the snapshot that corresponds to the given model name."""
 481        return self._snapshots.get(
 482            normalize_model_name(
 483                model_name,
 484                default_catalog=self.default_catalog,
 485                dialect=self.dialect,
 486            )
 487        )
 488
 489    def resolve_table(self, table: str | exp.Table) -> str:
 490        """Gets the physical table name for a given model."""
 491        if not self._resolve_table:
 492            raise SQLMeshError(
 493                "Macro evaluator not properly initialized with resolve_table lambda."
 494            )
 495        return self._resolve_table(table)
 496
 497    def resolve_tables(self, query: exp.Expr) -> exp.Expr:
 498        """Resolves queries with references to SQLMesh model names to their physical tables."""
 499        if not self._resolve_tables:
 500            raise SQLMeshError(
 501                "Macro evaluator not properly initialized with resolve_tables lambda."
 502            )
 503        return self._resolve_tables(query)
 504
 505    @property
 506    def runtime_stage(self) -> RuntimeStage:
 507        """Returns the current runtime stage of the macro evaluation."""
 508        return self.locals["runtime_stage"]
 509
 510    @property
 511    def this_model(self) -> str:
 512        """Returns the resolved name of the surrounding model."""
 513        this_model = self.locals.get("this_model")
 514        if not this_model:
 515            raise SQLMeshError("Model name is not available in the macro evaluator.")
 516        return this_model.sql(dialect=self.dialect, identify=True, comments=False)
 517
 518    @property
 519    def this_model_fqn(self) -> str:
 520        if self._model_fqn is None:
 521            raise SQLMeshError("Model name is not available in the macro evaluator.")
 522        return self._model_fqn
 523
 524    @property
 525    def engine_adapter(self) -> EngineAdapter:
 526        engine_adapter = self.locals.get("engine_adapter")
 527        if not engine_adapter:
 528            raise SQLMeshError(
 529                "The engine adapter is not available while models are loading."
 530                " You can gate these calls by checking in Python: evaluator.runtime_stage != 'loading' or SQL: @runtime_stage <> 'loading'."
 531            )
 532        return self.locals["engine_adapter"]
 533
 534    @property
 535    def gateway(self) -> t.Optional[str]:
 536        """Returns the gateway name."""
 537        return self.var(c.GATEWAY)
 538
 539    @property
 540    def snapshots(self) -> t.Dict[str, Snapshot]:
 541        """Returns the snapshots if available."""
 542        return self._snapshots
 543
 544    @property
 545    def this_env(self) -> str:
 546        """Returns the name of the current environment in before after all."""
 547        if "this_env" not in self.locals:
 548            raise SQLMeshError("Environment name is only available in before_all and after_all")
 549        return self.locals["this_env"]
 550
 551    @property
 552    def schemas(self) -> t.List[str]:
 553        """Returns the schemas of the current environment in before after all macros."""
 554        if "schemas" not in self.locals:
 555            raise SQLMeshError("Schemas are only available in before_all and after_all")
 556        return self.locals["schemas"]
 557
 558    @property
 559    def views(self) -> t.List[str]:
 560        """Returns the views of the current environment in before after all macros."""
 561        if "views" not in self.locals:
 562            raise SQLMeshError("Views are only available in before_all and after_all")
 563        return self.locals["views"]
 564
 565    def var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
 566        """Returns the value of the specified variable, or the default value if it doesn't exist."""
 567        return {
 568            **(self.locals.get(c.SQLMESH_VARS) or {}),
 569            **(self.locals.get(c.SQLMESH_VARS_METADATA) or {}),
 570        }.get(var_name.lower(), default)
 571
 572    def blueprint_var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
 573        """Returns the value of the specified blueprint variable, or the default value if it doesn't exist."""
 574        return {
 575            **(self.locals.get(c.SQLMESH_BLUEPRINT_VARS) or {}),
 576            **(self.locals.get(c.SQLMESH_BLUEPRINT_VARS_METADATA) or {}),
 577        }.get(var_name.lower(), default)
 578
 579    @property
 580    def variables(self) -> t.Dict[str, t.Any]:
 581        return {
 582            **self.locals.get(c.SQLMESH_VARS, {}),
 583            **self.locals.get(c.SQLMESH_VARS_METADATA, {}),
 584            **self.locals.get(c.SQLMESH_BLUEPRINT_VARS, {}),
 585            **self.locals.get(c.SQLMESH_BLUEPRINT_VARS_METADATA, {}),
 586        }
 587
 588    def _coerce(self, expr: exp.Expr, typ: t.Any, strict: bool = False) -> t.Any:
 589        """Coerces the given expression to the specified type on a best-effort basis."""
 590        return _coerce(expr, typ, self.dialect, self._path, strict)
 591
 592
 593class macro(registry_decorator):
 594    """Specifies a function is a macro and registers it the global MACROS registry.
 595
 596    Registered macros can be referenced in SQL statements to make queries more dynamic/cleaner.
 597
 598    Example:
 599        from sqlglot import exp
 600        from sqlmesh.core.macros import MacroEvaluator, macro
 601
 602        @macro()
 603        def add_one(evaluator: MacroEvaluator, column: exp.Literal) -> exp.Add:
 604            return evaluator.parse_one(f"{column} + 1")
 605
 606    Args:
 607        name: A custom name for the macro, the default is the name of the function.
 608    """
 609
 610    registry_name = "macros"
 611
 612    def __init__(self, *args: t.Any, metadata_only: bool = False, **kwargs: t.Any) -> None:
 613        super().__init__(*args, **kwargs)
 614        self.metadata_only = metadata_only
 615
 616    def __call__(
 617        self, func: t.Callable[..., DECORATOR_RETURN_TYPE]
 618    ) -> t.Callable[..., DECORATOR_RETURN_TYPE]:
 619        if self.metadata_only:
 620            setattr(func, c.SQLMESH_METADATA, self.metadata_only)
 621        wrapper = super().__call__(func)
 622
 623        # This is used to identify macros at runtime to unwrap during serialization.
 624        setattr(wrapper, c.SQLMESH_MACRO, True)
 625        return wrapper
 626
 627
 628ExecutableOrMacro = t.Union[Executable, macro]
 629MacroRegistry = UniqueKeyDict[str, ExecutableOrMacro]
 630
 631
 632def _norm_var_arg_lambda(
 633    evaluator: MacroEvaluator, func: exp.Lambda, *items: t.Any
 634) -> t.Tuple[t.Iterable, t.Callable]:
 635    """
 636    Converts sql literal array and lambda into actual python iterable + callable.
 637
 638    In order to support expressions like @EACH([a, b, c], x -> @SQL('@x')), the lambda var x
 639    needs be passed to the local state.
 640
 641    Args:
 642        evaluator: MacroEvaluator that invoked the macro
 643        func: Lambda SQLGlot expression.
 644        items: Array or items of SQLGlot expressions.
 645    """
 646
 647    def substitute(
 648        node: exp.Expr, args: t.Dict[str, exp.Expr]
 649    ) -> exp.Expr | t.List[exp.Expr] | None:
 650        if isinstance(node, (exp.Identifier, exp.Var)):
 651            name = node.name.lower()
 652            if name in args:
 653                return args[name].copy()
 654            if not isinstance(node.parent, exp.Column):
 655                if name in evaluator.locals:
 656                    return exp.convert(evaluator.locals[name])
 657            if SQLMESH_MACRO_PREFIX in node.name:
 658                return node.__class__(
 659                    this=evaluator.template(node.name, {k: v.name for k, v in args.items()})
 660                )
 661        elif isinstance(node, MacroFunc):
 662            local_copy = evaluator.locals.copy()
 663            evaluator.locals.update(args)
 664            result = evaluator.transform(node)
 665            evaluator.locals = local_copy
 666            return result
 667        return node
 668
 669    if len(items) == 1:
 670        item = items[0]
 671        expressions = (
 672            item.expressions
 673            if isinstance(item, (exp.Array, exp.Tuple))
 674            else [item.this]
 675            if isinstance(item, exp.Paren)
 676            else item
 677        )
 678    else:
 679        expressions = items
 680
 681    if not callable(func):
 682        return expressions, lambda args: func.this.transform(
 683            substitute,
 684            {
 685                expression.name.lower(): arg
 686                for expression, arg in zip(
 687                    func.expressions, args.expressions if isinstance(args, exp.Tuple) else [args]
 688                )
 689            },
 690        )
 691
 692    return expressions, func
 693
 694
 695@macro()
 696def each(
 697    evaluator: MacroEvaluator,
 698    *args: t.Any,
 699) -> t.List[t.Any]:
 700    """Iterates through items calling func on each.
 701
 702    If a func call on item returns None, it will be excluded from the list.
 703
 704    Args:
 705        evaluator: MacroEvaluator that invoked the macro
 706        args: The last argument should be a lambda of the form x -> x +1. The first argument can be
 707            an Array or var args can be used.
 708
 709    Returns:
 710        A list of items that is the result of func
 711    """
 712    *items, func = args
 713    items, func = _norm_var_arg_lambda(evaluator, func, *items)  # type: ignore
 714    return [item for item in map(func, ensure_collection(items)) if item is not None]
 715
 716
 717@macro("IF")
 718def if_(
 719    evaluator: MacroEvaluator,
 720    condition: t.Any,
 721    true: t.Any,
 722    false: t.Any = None,
 723) -> t.Any:
 724    """Evaluates a given condition and returns the second argument if true or else the third argument.
 725
 726    If false is not passed in, the default return value will be None.
 727
 728    Example:
 729        >>> from sqlglot import parse_one
 730        >>> from sqlmesh.core.macros import MacroEvaluator
 731        >>> MacroEvaluator().transform(parse_one("@IF('a' = 1, a, b)")).sql()
 732        'b'
 733
 734        >>> MacroEvaluator().transform(parse_one("@IF('a' = 1, a)"))
 735    """
 736
 737    if evaluator.eval_expression(condition):
 738        return true
 739    return false
 740
 741
 742@macro("REDUCE")
 743def reduce_(evaluator: MacroEvaluator, *args: t.Any) -> t.Any:
 744    """Iterates through items applying provided function that takes two arguments
 745    cumulatively to the items of iterable items, from left to right, so as to reduce
 746    the iterable to a single item.
 747
 748    Example:
 749        >>> from sqlglot import parse_one
 750        >>> from sqlmesh.core.macros import MacroEvaluator
 751        >>> sql = "@SQL(@REDUCE([100, 200, 300, 400], (x, y) -> x + y))"
 752        >>> MacroEvaluator().transform(parse_one(sql)).sql()
 753        '1000'
 754
 755    Args:
 756        evaluator: MacroEvaluator that invoked the macro
 757        args: The last argument should be a lambda of the form (x, y) -> x + y. The first argument can be
 758            an Array or var args can be used.
 759    Returns:
 760        A single item that is the result of applying func cumulatively to items
 761    """
 762    *items, func = args
 763    items, func = _norm_var_arg_lambda(evaluator, func, *items)  # type: ignore
 764    return reduce(lambda a, b: func(exp.Tuple(expressions=[a, b])), ensure_collection(items))
 765
 766
 767@macro("FILTER")
 768def filter_(evaluator: MacroEvaluator, *args: t.Any) -> t.List[t.Any]:
 769    """Iterates through items, applying provided function to each item and removing
 770    all items where the function returns False
 771
 772    Example:
 773        >>> from sqlglot import parse_one
 774        >>> from sqlmesh.core.macros import MacroEvaluator
 775        >>> sql = "@REDUCE(@FILTER([1, 2, 3], x -> x > 1), (x, y) -> x + y)"
 776        >>> MacroEvaluator().transform(parse_one(sql)).sql()
 777        '2 + 3'
 778
 779        >>> sql = "@EVAL(@REDUCE(@FILTER([1, 2, 3], x -> x > 1), (x, y) -> x + y))"
 780        >>> MacroEvaluator().transform(parse_one(sql)).sql()
 781        '5'
 782
 783    Args:
 784        evaluator: MacroEvaluator that invoked the macro
 785        args: The last argument should be a lambda of the form x -> x > 1. The first argument can be
 786            an Array or var args can be used.
 787    Returns:
 788        The items for which the func returned True
 789    """
 790    *items, func = args
 791    items, func = _norm_var_arg_lambda(evaluator, func, *items)  # type: ignore
 792    return list(filter(lambda arg: evaluator.eval_expression(func(arg)), items))
 793
 794
 795def _optional_expression(
 796    evaluator: MacroEvaluator,
 797    condition: exp.Condition,
 798    expression: exp.Expr,
 799) -> t.Optional[exp.Expr]:
 800    """Inserts expression when the condition is True
 801
 802    The following examples express the usage of this function in the context of the macros which wrap it.
 803
 804    Examples:
 805        >>> from sqlglot import parse_one
 806        >>> from sqlmesh.core.macros import MacroEvaluator
 807        >>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
 808        >>> MacroEvaluator().transform(parse_one(sql)).sql()
 809        'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
 810        >>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
 811        >>> MacroEvaluator().transform(parse_one(sql)).sql()
 812        'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
 813        >>> sql = "select * from city @GROUP_BY(True) country, population"
 814        >>> MacroEvaluator().transform(parse_one(sql)).sql()
 815        'SELECT * FROM city GROUP BY country, population'
 816        >>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
 817        >>> MacroEvaluator().transform(parse_one(sql)).sql()
 818        "SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
 819
 820    Args:
 821        evaluator: MacroEvaluator that invoked the macro
 822        condition: Condition expression
 823        expression: SQL expression
 824    Returns:
 825        Expression if the conditional is True; otherwise None
 826    """
 827    return expression if evaluator.eval_expression(condition) else None
 828
 829
 830with_ = macro("WITH")(_optional_expression)
 831join = macro("JOIN")(_optional_expression)
 832where = macro("WHERE")(_optional_expression)
 833group_by = macro("GROUP_BY")(_optional_expression)
 834having = macro("HAVING")(_optional_expression)
 835order_by = macro("ORDER_BY")(_optional_expression)
 836limit = macro("LIMIT")(_optional_expression)
 837
 838
 839@macro("eval")
 840def eval_(evaluator: MacroEvaluator, condition: exp.Condition) -> t.Any:
 841    """Evaluate the given condition in a Python/SQL interpretor.
 842
 843    Example:
 844        >>> from sqlglot import parse_one
 845        >>> from sqlmesh.core.macros import MacroEvaluator
 846        >>> sql = "@EVAL(1 + 1)"
 847        >>> MacroEvaluator().transform(parse_one(sql)).sql()
 848        '2'
 849    """
 850    return evaluator.eval_expression(condition)
 851
 852
 853# macros with union types need to use t.Union since | isn't available until 3.9
 854@macro()
 855def star(
 856    evaluator: MacroEvaluator,
 857    relation: exp.Table,
 858    alias: exp.Column = t.cast(exp.Column, exp.column("")),
 859    exclude: t.Union[exp.Array, exp.Tuple] = exp.Tuple(expressions=[]),
 860    prefix: exp.Literal = exp.Literal.string(""),
 861    suffix: exp.Literal = exp.Literal.string(""),
 862    quote_identifiers: exp.Boolean = exp.true(),
 863    except_: t.Union[exp.Array, exp.Tuple] = exp.Tuple(expressions=[]),
 864) -> t.List[exp.Expr]:
 865    """Returns a list of projections for the given relation.
 866
 867    Args:
 868        evaluator: MacroEvaluator that invoked the macro
 869        relation: The relation to select star from
 870        alias: The alias of the relation
 871        exclude: Columns to exclude
 872        prefix: A prefix to use for all selections
 873        suffix: A suffix to use for all selections
 874        quote_identifiers: Whether or not quote the resulting aliases, defaults to true
 875        except_: Alias for exclude (TODO: deprecate this, update docs)
 876
 877    Returns:
 878        An array of columns.
 879
 880    Example:
 881        >>> from sqlglot import parse_one, exp
 882        >>> from sqlglot.schema import MappingSchema
 883        >>> from sqlmesh.core.macros import MacroEvaluator
 884        >>> sql = "SELECT @STAR(foo, bar, exclude := [c], prefix := 'baz_') FROM foo AS bar"
 885        >>> MacroEvaluator(schema=MappingSchema({"foo": {"a": exp.DataType.build("string"), "b": exp.DataType.build("string"), "c": exp.DataType.build("string"), "d": exp.DataType.build("int")}})).transform(parse_one(sql)).sql()
 886        'SELECT CAST("bar"."a" AS TEXT) AS "baz_a", CAST("bar"."b" AS TEXT) AS "baz_b", CAST("bar"."d" AS INT) AS "baz_d" FROM foo AS bar'
 887    """
 888    if alias and not isinstance(alias, (exp.Identifier, exp.Column)):
 889        raise SQLMeshError(f"Invalid alias '{alias}'. Expected an identifier.")
 890    if exclude and not isinstance(exclude, (exp.Array, exp.Tuple)):
 891        raise SQLMeshError(f"Invalid exclude '{exclude}'. Expected an array.")
 892    if except_ != exp.tuple_():
 893        from sqlmesh.core.console import get_console
 894
 895        get_console().log_warning(
 896            "The 'except_' argument in @STAR will soon be deprecated. Use 'exclude' instead."
 897        )
 898        if not isinstance(exclude, (exp.Array, exp.Tuple)):
 899            raise SQLMeshError(f"Invalid exclude_ '{exclude}'. Expected an array.")
 900    if prefix and not isinstance(prefix, exp.Literal):
 901        raise SQLMeshError(f"Invalid prefix '{prefix}'. Expected a literal.")
 902    if suffix and not isinstance(suffix, exp.Literal):
 903        raise SQLMeshError(f"Invalid suffix '{suffix}'. Expected a literal.")
 904    if not isinstance(quote_identifiers, exp.Boolean):
 905        raise SQLMeshError(f"Invalid quote_identifiers '{quote_identifiers}'. Expected a boolean.")
 906
 907    excluded_names = {
 908        normalize_identifiers(excluded, dialect=evaluator.dialect).name
 909        for excluded in exclude.expressions or except_.expressions
 910    }
 911    quoted = quote_identifiers.this
 912    table_identifier = normalize_identifiers(
 913        alias if alias.name else relation, dialect=evaluator.dialect
 914    ).name
 915
 916    columns_to_types = {
 917        k: v for k, v in evaluator.columns_to_types(relation).items() if k not in excluded_names
 918    }
 919    if columns_to_types_all_known(columns_to_types):
 920        return [
 921            exp.cast(
 922                exp.column(column, table=table_identifier, quoted=quoted),
 923                dtype,
 924                dialect=evaluator.dialect,
 925            ).as_(f"{prefix.this}{column}{suffix.this}", quoted=quoted)
 926            for column, dtype in columns_to_types.items()
 927        ]
 928    return [
 929        exp.column(column, table=table_identifier, quoted=quoted).as_(
 930            f"{prefix.this}{column}{suffix.this}", quoted=quoted
 931        )
 932        for column, type_ in columns_to_types.items()
 933    ]
 934
 935
 936@macro()
 937def generate_surrogate_key(
 938    evaluator: MacroEvaluator,
 939    *fields: exp.Expr,
 940    hash_function: exp.Literal = exp.Literal.string("MD5"),
 941) -> exp.Func:
 942    """Generates a surrogate key (string) for the given fields.
 943
 944    Example:
 945        >>> from sqlglot import parse_one
 946        >>> from sqlmesh.core.macros import MacroEvaluator
 947        >>>
 948        >>> sql = "SELECT @GENERATE_SURROGATE_KEY(a, b, c) FROM foo"
 949        >>> MacroEvaluator(dialect="bigquery").transform(parse_one(sql, dialect="bigquery")).sql("bigquery")
 950        "SELECT TO_HEX(MD5(CONCAT(COALESCE(CAST(a AS STRING), '_sqlmesh_surrogate_key_null_'), '|', COALESCE(CAST(b AS STRING), '_sqlmesh_surrogate_key_null_'), '|', COALESCE(CAST(c AS STRING), '_sqlmesh_surrogate_key_null_')))) FROM foo"
 951        >>>
 952        >>> sql = "SELECT @GENERATE_SURROGATE_KEY(a, b, c, hash_function := 'SHA256') FROM foo"
 953        >>> MacroEvaluator(dialect="bigquery").transform(parse_one(sql, dialect="bigquery")).sql("bigquery")
 954        "SELECT SHA256(CONCAT(COALESCE(CAST(a AS STRING), '_sqlmesh_surrogate_key_null_'), '|', COALESCE(CAST(b AS STRING), '_sqlmesh_surrogate_key_null_'), '|', COALESCE(CAST(c AS STRING), '_sqlmesh_surrogate_key_null_'))) FROM foo"
 955    """
 956    string_fields: t.List[exp.Expr] = []
 957    for i, field in enumerate(fields):
 958        if i > 0:
 959            string_fields.append(exp.Literal.string("|"))
 960        string_fields.append(
 961            exp.func(
 962                "COALESCE",
 963                exp.cast(field, exp.DataType.build("text")),
 964                exp.Literal.string("_sqlmesh_surrogate_key_null_"),
 965            )
 966        )
 967
 968    func = exp.func(
 969        hash_function.name,
 970        exp.func("CONCAT", *string_fields),
 971        dialect=evaluator.dialect,
 972    )
 973    if isinstance(func, exp.MD5Digest):
 974        func = exp.MD5(this=func.this)
 975
 976    return func
 977
 978
 979@macro()
 980def safe_add(_: MacroEvaluator, *fields: exp.Expr) -> exp.Case:
 981    """Adds numbers together, substitutes nulls for 0s and only returns null if all fields are null.
 982
 983    Example:
 984        >>> from sqlglot import parse_one
 985        >>> from sqlmesh.core.macros import MacroEvaluator
 986        >>> sql = "SELECT @SAFE_ADD(a, b) FROM foo"
 987        >>> MacroEvaluator().transform(parse_one(sql)).sql()
 988        'SELECT CASE WHEN a IS NULL AND b IS NULL THEN NULL ELSE COALESCE(a, 0) + COALESCE(b, 0) END FROM foo'
 989    """
 990    return (
 991        exp.Case()
 992        .when(exp.and_(*(field.is_(exp.null()) for field in fields)), exp.null())
 993        .else_(reduce(lambda a, b: a + b, [exp.func("COALESCE", field, 0) for field in fields]))  # type: ignore
 994    )
 995
 996
 997@macro()
 998def safe_sub(_: MacroEvaluator, *fields: exp.Expr) -> exp.Case:
 999    """Subtract numbers, substitutes nulls for 0s and only returns null if all fields are null.
1000
1001    Example:
1002        >>> from sqlglot import parse_one
1003        >>> from sqlmesh.core.macros import MacroEvaluator
1004        >>> sql = "SELECT @SAFE_SUB(a, b) FROM foo"
1005        >>> MacroEvaluator().transform(parse_one(sql)).sql()
1006        'SELECT CASE WHEN a IS NULL AND b IS NULL THEN NULL ELSE COALESCE(a, 0) - COALESCE(b, 0) END FROM foo'
1007    """
1008    return (
1009        exp.Case()
1010        .when(exp.and_(*(field.is_(exp.null()) for field in fields)), exp.null())
1011        .else_(reduce(lambda a, b: a - b, [exp.func("COALESCE", field, 0) for field in fields]))  # type: ignore
1012    )
1013
1014
1015@macro()
1016def safe_div(_: MacroEvaluator, numerator: exp.Expr, denominator: exp.Expr) -> exp.Div:
1017    """Divides numbers, returns null if the denominator is 0.
1018
1019    Example:
1020        >>> from sqlglot import parse_one
1021        >>> from sqlmesh.core.macros import MacroEvaluator
1022        >>> sql = "SELECT @SAFE_DIV(a, b) FROM foo"
1023        >>> MacroEvaluator().transform(parse_one(sql)).sql()
1024        'SELECT a / NULLIF(b, 0) FROM foo'
1025    """
1026    return numerator / exp.func("NULLIF", denominator, 0)
1027
1028
1029@macro()
1030def union(
1031    evaluator: MacroEvaluator,
1032    *args: exp.Expr,
1033) -> exp.Query:
1034    """Returns a UNION of the given tables. Only choosing columns that have the same name and type.
1035
1036    Args:
1037        evaluator: MacroEvaluator that invoked the macro
1038        args: Variable arguments that can be:
1039            - First argument can be a condition (exp.Condition)
1040            - A union type ('ALL' or 'DISTINCT') as exp.Literal
1041            - Tables (exp.Table)
1042
1043    Example:
1044        >>> from sqlglot import parse_one
1045        >>> from sqlglot.schema import MappingSchema
1046        >>> from sqlmesh.core.macros import MacroEvaluator
1047        >>> sql = "@UNION('distinct', foo, bar)"
1048        >>> MacroEvaluator(schema=MappingSchema({"foo": {"a": "int", "b": "string", "c": "string"}, "bar": {"c": "string", "a": "int", "b": "int"}})).transform(parse_one(sql)).sql()
1049        'SELECT CAST(a AS INT) AS a, CAST(c AS TEXT) AS c FROM foo UNION SELECT CAST(a AS INT) AS a, CAST(c AS TEXT) AS c FROM bar'
1050        >>> sql = "@UNION(True, 'distinct', foo, bar)"
1051        >>> MacroEvaluator(schema=MappingSchema({"foo": {"a": "int", "b": "string", "c": "string"}, "bar": {"c": "string", "a": "int", "b": "int"}})).transform(parse_one(sql)).sql()
1052        'SELECT CAST(a AS INT) AS a, CAST(c AS TEXT) AS c FROM foo UNION SELECT CAST(a AS INT) AS a, CAST(c AS TEXT) AS c FROM bar'
1053    """
1054
1055    if not args:
1056        raise SQLMeshError("At least one table is required for the @UNION macro.")
1057
1058    arg_idx = 0
1059    # Check for condition
1060    condition = evaluator.eval_expression(args[arg_idx])
1061    if isinstance(condition, bool):
1062        arg_idx += 1
1063        if arg_idx >= len(args):
1064            raise SQLMeshError("Expected more arguments after the condition of the `@UNION` macro.")
1065
1066    # Check for union type
1067    type_ = exp.Literal.string("ALL")
1068    if isinstance(args[arg_idx], exp.Literal):
1069        type_ = args[arg_idx]  # type: ignore
1070        arg_idx += 1
1071    kind = type_.name.upper()
1072    if kind not in ("ALL", "DISTINCT"):
1073        raise SQLMeshError(f"Invalid type '{type_}'. Expected 'ALL' or 'DISTINCT'.")
1074
1075    # Remaining args should be tables
1076    tables = [
1077        exp.to_table(e.sql(evaluator.dialect), dialect=evaluator.dialect) for e in args[arg_idx:]
1078    ]
1079
1080    columns = {
1081        column
1082        for column, _ in reduce(
1083            lambda a, b: a & b,  # type: ignore
1084            (evaluator.columns_to_types(table).items() for table in tables),
1085        )
1086    }
1087
1088    projections = [
1089        exp.cast(column, type_, dialect=evaluator.dialect).as_(column)
1090        for column, type_ in evaluator.columns_to_types(tables[0]).items()
1091        if column in columns
1092    ]
1093
1094    # Skip the union if condition is False
1095    if condition == False:
1096        return exp.select(*projections).from_(tables[0])
1097
1098    return reduce(
1099        lambda a, b: a.union(b, distinct=kind == "DISTINCT"),  # type: ignore
1100        [exp.select(*projections).from_(t) for t in tables],
1101    )
1102
1103
1104@macro()
1105def haversine_distance(
1106    _: MacroEvaluator,
1107    lat1: exp.Expr,
1108    lon1: exp.Expr,
1109    lat2: exp.Expr,
1110    lon2: exp.Expr,
1111    unit: exp.Literal = exp.Literal.string("mi"),
1112) -> exp.Mul:
1113    """Returns the haversine distance between two points.
1114
1115    Example:
1116        >>> from sqlglot import parse_one
1117        >>> from sqlmesh.core.macros import MacroEvaluator
1118        >>> sql = "SELECT @HAVERSINE_DISTANCE(driver_y, driver_x, passenger_y, passenger_x, 'mi') FROM rides"
1119        >>> MacroEvaluator().transform(parse_one(sql)).sql()
1120        'SELECT 7922 * ASIN(SQRT((POWER(SIN(RADIANS((passenger_y - driver_y) / 2)), 2)) + (COS(RADIANS(driver_y)) * COS(RADIANS(passenger_y)) * POWER(SIN(RADIANS((passenger_x - driver_x) / 2)), 2)))) * 1.0 FROM rides'
1121    """
1122    if unit.this == "mi":
1123        conversion_rate = 1.0
1124    elif unit.this == "km":
1125        conversion_rate = 1.60934
1126    else:
1127        raise SQLMeshError(f"Invalid unit '{unit}'. Expected 'mi' or 'km'.")
1128
1129    return (
1130        2
1131        * 3961
1132        * exp.func(
1133            "ASIN",
1134            exp.func(
1135                "SQRT",
1136                exp.func("POWER", exp.func("SIN", exp.func("RADIANS", (lat2 - lat1) / 2)), 2)
1137                + exp.func("COS", exp.func("RADIANS", lat1))
1138                * exp.func("COS", exp.func("RADIANS", lat2))
1139                * exp.func("POWER", exp.func("SIN", exp.func("RADIANS", (lon2 - lon1) / 2)), 2),
1140            ),
1141        )
1142        * conversion_rate
1143    )
1144
1145
1146@macro()
1147def pivot(
1148    evaluator: MacroEvaluator,
1149    column: SQL,
1150    values: t.List[exp.Expr],
1151    alias: bool = True,
1152    agg: exp.Expr = exp.Literal.string("SUM"),
1153    cmp: exp.Expr = exp.Literal.string("="),
1154    prefix: exp.Expr = exp.Literal.string(""),
1155    suffix: exp.Expr = exp.Literal.string(""),
1156    then_value: SQL = SQL("1"),
1157    else_value: SQL = SQL("0"),
1158    quote: bool = True,
1159    distinct: bool = False,
1160) -> t.List[exp.Expr]:
1161    """Returns a list of projections as a result of pivoting the given column on the given values.
1162
1163    Example:
1164        >>> from sqlglot import parse_one
1165        >>> from sqlmesh.core.macros import MacroEvaluator
1166        >>> sql = "SELECT date_day, @PIVOT(status, ['cancelled', 'completed']) FROM rides GROUP BY 1"
1167        >>> MacroEvaluator().transform(parse_one(sql)).sql()
1168        'SELECT date_day, SUM(CASE WHEN status = \\'cancelled\\' THEN 1 ELSE 0 END) AS "cancelled", SUM(CASE WHEN status = \\'completed\\' THEN 1 ELSE 0 END) AS "completed" FROM rides GROUP BY 1'
1169        >>> sql = "SELECT @PIVOT(a, ['v'], then_value := tv, suffix := '_sfx', quote := FALSE)"
1170        >>> MacroEvaluator(dialect="bigquery").transform(parse_one(sql)).sql("bigquery")
1171        "SELECT SUM(CASE WHEN a = 'v' THEN tv ELSE 0 END) AS v_sfx"
1172    """
1173    aggregates: t.List[exp.Expr] = []
1174    for value in values:
1175        proj = f"{agg.name}("
1176        if distinct:
1177            proj += "DISTINCT "
1178
1179        proj += f"CASE WHEN {column} {cmp.name} {value.sql(evaluator.dialect)} THEN {then_value} ELSE {else_value} END) "
1180        node: exp.Expr = evaluator.parse_one(proj)
1181
1182        if alias:
1183            node = node.as_(
1184                f"{prefix.name}{value.name}{suffix.name}",
1185                quoted=quote,
1186                copy=False,
1187                dialect=evaluator.dialect,
1188            )
1189
1190        aggregates.append(node)
1191
1192    return aggregates
1193
1194
1195@macro("AND")
1196def and_(evaluator: MacroEvaluator, *expressions: t.Optional[exp.Expr]) -> exp.Condition:
1197    """Returns an AND statement filtering out any NULL expressions."""
1198    conditions = [e for e in expressions if not isinstance(e, exp.Null)]
1199
1200    if not conditions:
1201        return exp.true()
1202
1203    return exp.and_(*conditions, dialect=evaluator.dialect)
1204
1205
1206@macro("OR")
1207def or_(evaluator: MacroEvaluator, *expressions: t.Optional[exp.Expr]) -> exp.Condition:
1208    """Returns an OR statement filtering out any NULL expressions."""
1209    conditions = [e for e in expressions if not isinstance(e, exp.Null)]
1210
1211    if not conditions:
1212        return exp.true()
1213
1214    return exp.or_(*conditions, dialect=evaluator.dialect)
1215
1216
1217@macro("VAR")
1218def var(
1219    evaluator: MacroEvaluator, var_name: exp.Expr, default: t.Optional[exp.Expr] = None
1220) -> exp.Expr:
1221    """Returns the value of a variable or the default value if the variable is not set."""
1222    if not var_name.is_string:
1223        raise SQLMeshError(f"Invalid variable name '{var_name.sql()}'. Expected a string literal.")
1224
1225    return exp.convert(evaluator.var(var_name.this, default))
1226
1227
1228@macro("BLUEPRINT_VAR")
1229def blueprint_var(
1230    evaluator: MacroEvaluator, var_name: exp.Expr, default: t.Optional[exp.Expr] = None
1231) -> exp.Expr:
1232    """Returns the value of a blueprint variable or the default value if the variable is not set."""
1233    if not var_name.is_string:
1234        raise SQLMeshError(
1235            f"Invalid blueprint variable name '{var_name.sql()}'. Expected a string literal."
1236        )
1237
1238    return exp.convert(evaluator.blueprint_var(var_name.this, default))
1239
1240
1241@macro()
1242def deduplicate(
1243    evaluator: MacroEvaluator,
1244    relation: exp.Expr,
1245    partition_by: t.List[exp.Expr],
1246    order_by: t.List[str],
1247) -> exp.Query:
1248    """Returns a QUERY to deduplicate rows within a table
1249
1250    Args:
1251        relation: table or CTE name to deduplicate
1252        partition_by: column names, or expressions to use to identify a window of rows out of which to select one as the deduplicated row
1253        order_by: A list of strings representing the ORDER BY clause
1254
1255    Example:
1256        >>> from sqlglot import parse_one
1257        >>> from sqlglot.schema import MappingSchema
1258        >>> from sqlmesh.core.macros import MacroEvaluator
1259        >>> sql = "@deduplicate(demo.table, [user_id, cast(timestamp as date)], ['timestamp desc', 'status asc'])"
1260        >>> MacroEvaluator().transform(parse_one(sql)).sql()
1261        'SELECT * FROM demo.table QUALIFY ROW_NUMBER() OVER (PARTITION BY user_id, CAST(timestamp AS DATE) ORDER BY timestamp DESC, status ASC) = 1'
1262    """
1263    if not isinstance(partition_by, list):
1264        raise SQLMeshError(
1265            "partition_by must be a list of columns: [<column>, cast(<column> as <type>)]"
1266        )
1267
1268    if not isinstance(order_by, list):
1269        raise SQLMeshError(
1270            "order_by must be a list of strings, optional - nulls ordering: ['<column> <asc|desc> nulls <first|last>']"
1271        )
1272
1273    partition_clause = exp.tuple_(*partition_by)
1274
1275    order_expressions = [
1276        evaluator.transform(parse_one(order_item, into=exp.Ordered, dialect=evaluator.dialect))
1277        for order_item in order_by
1278    ]
1279
1280    if not order_expressions:
1281        raise SQLMeshError(
1282            "order_by must be a list of strings, optional - nulls ordering: ['<column> <asc|desc> nulls <first|last>']"
1283        )
1284
1285    order_clause = exp.Order(expressions=order_expressions)
1286
1287    window_function = exp.Window(
1288        this=exp.RowNumber(), partition_by=partition_clause, order=order_clause
1289    )
1290
1291    first_unique_row = window_function.eq(1)
1292
1293    query = exp.select("*").from_(relation).qualify(first_unique_row)
1294
1295    return query
1296
1297
1298@macro()
1299def date_spine(
1300    evaluator: MacroEvaluator,
1301    datepart: exp.Expr,
1302    start_date: exp.Expr,
1303    end_date: exp.Expr,
1304) -> exp.Select:
1305    """Returns a query that produces a date spine with the given datepart, and range of start_date and end_date. Useful for joining as a date lookup table.
1306
1307    Args:
1308        datepart: The datepart to use for the date spine - day, week, month, quarter, year
1309        start_date: The start date for the date spine in format YYYY-MM-DD
1310        end_date: The end date for the date spine in format YYYY-MM-DD
1311
1312    Example:
1313        >>> from sqlglot import parse_one
1314        >>> from sqlglot.schema import MappingSchema
1315        >>> from sqlmesh.core.macros import MacroEvaluator
1316        >>> sql = "@date_spine('week', '2022-01-20', '2024-12-16')"
1317        >>> MacroEvaluator().transform(parse_one(sql)).sql()
1318        "SELECT date_week FROM UNNEST(GENERATE_DATE_ARRAY(CAST(\'2022-01-20\' AS DATE), CAST(\'2024-12-16\' AS DATE), INTERVAL \'1\' WEEK)) AS _exploded(date_week)"
1319    """
1320    datepart_name = datepart.name.lower()
1321    if datepart_name not in ("day", "week", "month", "quarter", "year"):
1322        raise SQLMeshError(
1323            f"Invalid datepart '{datepart_name}'. Expected: 'day', 'week', 'month', 'quarter', or 'year'"
1324        )
1325
1326    start_date_name = start_date.name
1327    end_date_name = end_date.name
1328
1329    try:
1330        if start_date.is_string and end_date.is_string:
1331            start_date_obj = datetime.strptime(start_date_name, "%Y-%m-%d").date()
1332            end_date_obj = datetime.strptime(end_date_name, "%Y-%m-%d").date()
1333        else:
1334            start_date_obj = None
1335            end_date_obj = None
1336    except Exception as e:
1337        raise SQLMeshError(
1338            f"Invalid date format - start_date and end_date must be in format: YYYY-MM-DD. Error: {e}"
1339        )
1340
1341    if start_date_obj and end_date_obj:
1342        if start_date_obj > end_date_obj:
1343            raise SQLMeshError(
1344                f"Invalid date range - start_date '{start_date_name}' is after end_date '{end_date_name}'."
1345            )
1346
1347        start_date = exp.cast(start_date, "DATE")
1348        end_date = exp.cast(end_date, "DATE")
1349
1350    if datepart_name == "quarter" and evaluator.dialect in (
1351        "spark",
1352        "spark2",
1353        "databricks",
1354        "postgres",
1355    ):
1356        date_interval = exp.Interval(this=exp.Literal.number(3), unit=exp.var("month"))
1357    else:
1358        date_interval = exp.Interval(this=exp.Literal.number(1), unit=exp.var(datepart_name))
1359
1360    generate_date_array = exp.func(
1361        "GENERATE_DATE_ARRAY",
1362        start_date,
1363        end_date,
1364        date_interval,
1365    )
1366
1367    alias_name = f"date_{datepart_name}"
1368    exploded = exp.alias_(exp.func("unnest", generate_date_array), "_exploded", table=[alias_name])
1369
1370    return exp.select(alias_name).from_(exploded)
1371
1372
1373@macro()
1374def resolve_template(
1375    evaluator: MacroEvaluator,
1376    template: exp.Literal,
1377    mode: str = "literal",
1378) -> t.Union[exp.Literal, exp.Table]:
1379    """
1380    Generates either a String literal or an exp.Table representing a physical table location, based on rendering the provided template String literal.
1381
1382    Note: It relies on the @this_model variable being available in the evaluation context (@this_model resolves to an exp.Table object
1383    representing the current physical table).
1384    Therefore, the @resolve_template macro must be used at creation or evaluation time and not at load time.
1385
1386    Args:
1387        template: Template string literal. Can contain the following placeholders:
1388            @{catalog_name} -> replaced with the catalog of the exp.Table returned from @this_model
1389            @{schema_name} -> replaced with the schema of the exp.Table returned from @this_model
1390            @{table_name} -> replaced with the name of the exp.Table returned from @this_model
1391        mode: What to return.
1392            'literal' -> return an exp.Literal string
1393            'table' -> return an exp.Table
1394
1395    Example:
1396        >>> from sqlglot import parse_one, exp
1397        >>> from sqlmesh.core.macros import MacroEvaluator, RuntimeStage
1398        >>> sql = "@resolve_template('s3://data-bucket/prod/@{catalog_name}/@{schema_name}/@{table_name}')"
1399        >>> evaluator = MacroEvaluator(runtime_stage=RuntimeStage.CREATING)
1400        >>> evaluator.locals.update({"this_model": exp.to_table("test_catalog.sqlmesh__test.test__test_model__2517971505")})
1401        >>> evaluator.transform(parse_one(sql)).sql()
1402        "'s3://data-bucket/prod/test_catalog/sqlmesh__test/test__test_model__2517971505'"
1403    """
1404    if "this_model" in evaluator.locals:
1405        this_model = exp.to_table(evaluator.locals["this_model"], dialect=evaluator.dialect)
1406        template_str: str = template.this
1407        result = (
1408            template_str.replace("@{catalog_name}", this_model.catalog)
1409            .replace("@{schema_name}", this_model.db)
1410            .replace("@{table_name}", this_model.name)
1411        )
1412
1413        if mode.lower() == "table":
1414            return exp.to_table(result, dialect=evaluator.dialect)
1415        return exp.Literal.string(result)
1416    if evaluator.runtime_stage != RuntimeStage.LOADING.value:
1417        # only error if we are CREATING, EVALUATING or TESTING and @this_model is not present; this could indicate a bug
1418        # otherwise, for LOADING, it's a no-op
1419        raise SQLMeshError(
1420            "@this_model must be present in the macro evaluation context in order to use @resolve_template"
1421        )
1422
1423    return template
1424
1425
1426def normalize_macro_name(name: str) -> str:
1427    """Prefix macro name with @ and upcase"""
1428    return f"@{name.upper()}"
1429
1430
1431for m in macro.get_registry().values():
1432    setattr(m, c.SQLMESH_BUILTIN, True)
1433
1434
1435def call_macro(
1436    func: t.Callable,
1437    dialect: DialectType,
1438    path: t.Optional[Path],
1439    provided_args: t.Tuple[t.Any, ...],
1440    provided_kwargs: t.Dict[str, t.Any],
1441    **optional_kwargs: t.Any,
1442) -> t.Any:
1443    # Bind the macro's actual parameters to its formal parameters
1444    sig = inspect.signature(func)
1445
1446    if optional_kwargs:
1447        provided_kwargs = provided_kwargs.copy()
1448
1449    for k, v in optional_kwargs.items():
1450        if k in sig.parameters:
1451            provided_kwargs[k] = v
1452
1453    bound = sig.bind(*provided_args, **provided_kwargs)
1454    bound.apply_defaults()
1455
1456    try:
1457        annotations = t.get_type_hints(func, localns=get_supported_types())
1458    except (NameError, TypeError):  # forward references aren't handled
1459        annotations = {}
1460
1461    # If the macro is annotated, we try coerce the actual parameters to the corresponding types
1462    if annotations:
1463        for arg, value in bound.arguments.items():
1464            typ = annotations.get(arg)
1465            if not typ:
1466                continue
1467
1468            # Changes to bound.arguments will reflect in bound.args and bound.kwargs
1469            # https://docs.python.org/3/library/inspect.html#inspect.BoundArguments.arguments
1470            param = sig.parameters[arg]
1471            if param.kind is inspect.Parameter.VAR_POSITIONAL:
1472                bound.arguments[arg] = tuple(_coerce(v, typ, dialect, path) for v in value)
1473            elif param.kind is inspect.Parameter.VAR_KEYWORD:
1474                bound.arguments[arg] = {k: _coerce(v, typ, dialect, path) for k, v in value.items()}
1475            else:
1476                bound.arguments[arg] = _coerce(value, typ, dialect, path)
1477
1478    return func(*bound.args, **bound.kwargs)
1479
1480
1481def _coerce(
1482    expr: t.Any,
1483    typ: t.Any,
1484    dialect: DialectType,
1485    path: t.Optional[Path] = None,
1486    strict: bool = False,
1487) -> t.Any:
1488    """Coerces the given expression to the specified type on a best-effort basis."""
1489    base_err_msg = f"Failed to coerce expression '{expr}' to type '{typ}'."
1490    try:
1491        if typ is None or typ is t.Any or not isinstance(expr, exp.Expr):
1492            return expr
1493        base = t.get_origin(typ) or typ
1494
1495        # We need to handle Union and TypeVars first since we cannot use isinstance with it
1496        if base in UNION_TYPES:
1497            for branch in t.get_args(typ):
1498                try:
1499                    return _coerce(expr, branch, dialect, path, strict=True)
1500                except Exception:
1501                    pass
1502            raise SQLMeshError(base_err_msg)
1503        if base is SQL and isinstance(expr, exp.Expr):
1504            return expr.sql(dialect)
1505
1506        if base is t.Literal:
1507            if not isinstance(expr, (exp.Literal, exp.Boolean)):
1508                raise SQLMeshError(
1509                    f"{base_err_msg} Coercion to {base} requires a literal expression."
1510                )
1511            literal_type_args = t.get_args(typ)
1512            try:
1513                for literal_type_arg in literal_type_args:
1514                    expr_is_bool = isinstance(expr.this, bool)
1515                    literal_is_bool = isinstance(literal_type_arg, bool)
1516                    if (expr_is_bool and literal_is_bool and literal_type_arg == expr.this) or (
1517                        not expr_is_bool
1518                        and not literal_is_bool
1519                        and str(literal_type_arg) == str(expr.this)
1520                    ):
1521                        return type(literal_type_arg)(expr.this)
1522            except Exception:
1523                raise SQLMeshError(base_err_msg)
1524            raise SQLMeshError(base_err_msg)
1525
1526        if isinstance(expr, base):
1527            return expr
1528        if issubclass(base, exp.Expr):
1529            d = Dialect.get_or_raise(dialect)
1530            into = base if base in d.parser_class.EXPRESSION_PARSERS else None
1531            if into is None:
1532                if isinstance(expr, exp.Literal):
1533                    coerced = parse_one(expr.this)
1534                else:
1535                    raise SQLMeshError(
1536                        f"{base_err_msg} Coercion to {base} requires a literal expression."
1537                    )
1538            else:
1539                coerced = parse_one(
1540                    expr.this if isinstance(expr, exp.Literal) else expr.sql(), into=into
1541                )
1542            if isinstance(coerced, base):
1543                return coerced
1544            raise SQLMeshError(base_err_msg)
1545
1546        if base in (int, float, str) and isinstance(expr, exp.Literal):
1547            return base(expr.this)
1548        if base is str and isinstance(expr, exp.Column) and not expr.table:
1549            return expr.name
1550        if base is bool and isinstance(expr, exp.Boolean):
1551            return expr.this
1552        if base is datetime and isinstance(expr, exp.Literal):
1553            return to_datetime(expr.this)
1554        if base is date and isinstance(expr, exp.Literal):
1555            return to_date(expr.this)
1556        if base is tuple and isinstance(expr, (exp.Tuple, exp.Array)):
1557            generic = t.get_args(typ)
1558            if not generic:
1559                return tuple(expr.expressions)
1560            if generic[-1] is ...:
1561                return tuple(_coerce(expr, generic[0], dialect, path) for expr in expr.expressions)
1562            if len(generic) == len(expr.expressions):
1563                return tuple(
1564                    _coerce(expr, generic[i], dialect, path)
1565                    for i, expr in enumerate(expr.expressions)
1566                )
1567            raise SQLMeshError(f"{base_err_msg} Expected {len(generic)} items.")
1568        if base is list and isinstance(expr, (exp.Array, exp.Tuple)):
1569            generic = t.get_args(typ)
1570            if not generic:
1571                return expr.expressions
1572            return [_coerce(expr, generic[0], dialect, path) for expr in expr.expressions]
1573        raise SQLMeshError(base_err_msg)
1574    except Exception:
1575        if strict:
1576            raise
1577
1578        from sqlmesh.core.console import get_console
1579
1580        get_console().log_error(
1581            f"Coercion of expression '{expr}' to type '{typ}' failed. Using non coerced expression at '{path}'",
1582        )
1583        return expr
1584
1585
1586def convert_sql(v: t.Any, dialect: DialectType) -> t.Any:
1587    try:
1588        return _cache_convert_sql(v, dialect, v.__class__)
1589    # dicts aren't hashable but are convertable
1590    except TypeError:
1591        return _convert_sql(v, dialect)
1592
1593
1594def _convert_sql(v: t.Any, dialect: DialectType) -> t.Any:
1595    if not isinstance(v, str):
1596        try:
1597            v = exp.convert(v)
1598        # we use bare Exception instead of ValueError because there's
1599        # a recursive error with MagicMock.
1600        except Exception:
1601            pass
1602
1603    if isinstance(v, exp.Expr):
1604        if (isinstance(v, exp.Column) and not v.table) or (
1605            isinstance(v, exp.Identifier) or v.is_string
1606        ):
1607            return v.name
1608        v = v.sql(dialect=dialect)
1609    return v
1610
1611
1612@lru_cache(maxsize=16384)
1613def _cache_convert_sql(v: t.Any, dialect: DialectType, t: type) -> t.Any:
1614    return _convert_sql(v, dialect)
class RuntimeStage(enum.Enum):
65class RuntimeStage(Enum):
66    LOADING = "loading"
67    CREATING = "creating"
68    EVALUATING = "evaluating"
69    PROMOTING = "promoting"
70    DEMOTING = "demoting"
71    AUDITING = "auditing"
72    TESTING = "testing"
73    BEFORE_ALL = "before_all"
74    AFTER_ALL = "after_all"

An enumeration.

LOADING = <RuntimeStage.LOADING: 'loading'>
CREATING = <RuntimeStage.CREATING: 'creating'>
EVALUATING = <RuntimeStage.EVALUATING: 'evaluating'>
PROMOTING = <RuntimeStage.PROMOTING: 'promoting'>
DEMOTING = <RuntimeStage.DEMOTING: 'demoting'>
AUDITING = <RuntimeStage.AUDITING: 'auditing'>
TESTING = <RuntimeStage.TESTING: 'testing'>
BEFORE_ALL = <RuntimeStage.BEFORE_ALL: 'before_all'>
AFTER_ALL = <RuntimeStage.AFTER_ALL: 'after_all'>
Inherited Members
enum.Enum
name
value
class MacroStrTemplate(string.Template):
77class MacroStrTemplate(Template):
78    delimiter = SQLMESH_MACRO_PREFIX

A string class for supporting $-substitutions.

delimiter = '@'
pattern = re.compile('\n @(?:\n (?P<escaped>@) | # Escape sequence of two delimiters\n (?P<named>(?a:[_a-z][_a-z0-9]*)) | # delimiter and a Python identifier\n , re.IGNORECASE|re.VERBOSE)
Inherited Members
string.Template
Template
idpattern
braceidpattern
flags
template
substitute
safe_substitute
EXPRESSIONS_NAME_MAP = {'cluster': 'Cluster', 'column': 'Column', 'columndef': 'ColumnDef', 'condition': 'Condition', 'datatype': 'DataType', 'expr': 'Expr', 'from': 'From', 'grantprincipal': 'GrantPrincipal', 'grantprivilege': 'GrantPrivilege', 'group': 'Group', 'having': 'Having', 'hint': 'Hint', 'identifier': 'Identifier', 'join': 'Join', 'lambda': 'Lambda', 'lateral': 'Lateral', 'limit': 'Limit', 'offset': 'Offset', 'order': 'Order', 'ordered': 'Ordered', 'properties': 'Properties', 'partitionedbyproperty': 'PartitionedByProperty', 'qualify': 'Qualify', 'returning': 'Returning', 'select': 'Select', 'sort': 'Sort', 'table': 'Table', 'tablealias': 'TableAlias', 'tuple': 'Tuple', 'whens': 'Whens', 'where': 'Where', 'window': 'Window', 'with': 'With'}
SQL = SQL
@lru_cache()
def get_supported_types() -> Dict[str, Any]:
 85@lru_cache()
 86def get_supported_types() -> t.Dict[str, t.Any]:
 87    from sqlmesh.core.context import ExecutionContext
 88
 89    return {
 90        "t": t,
 91        "typing": t,
 92        "List": t.List,
 93        "Tuple": t.Tuple,
 94        "Union": t.Union,
 95        "DatetimeRanges": DatetimeRanges,
 96        "exp": exp,
 97        "SQL": SQL,
 98        "MacroEvaluator": MacroEvaluator,
 99        "ExecutionContext": ExecutionContext,
100    }
class CaseInsensitiveMapping(typing.Dict[str, typing.Any]):
133class CaseInsensitiveMapping(t.Dict[str, t.Any]):
134    def __init__(self, data: t.Dict[str, t.Any]) -> None:
135        super().__init__(data)
136
137    def __getitem__(self, key: str) -> t.Any:
138        return super().__getitem__(key.lower())
139
140    def get(self, key: str, default: t.Any = None, /) -> t.Any:
141        return super().get(key.lower(), default)
def get(self, key: str, default: Any = None, /) -> Any:
140    def get(self, key: str, default: t.Any = None, /) -> t.Any:
141        return super().get(key.lower(), default)

Return the value for key if key is in the dictionary, else default.

Inherited Members
builtins.dict
setdefault
pop
popitem
keys
items
values
update
fromkeys
clear
copy
class MacroDialect(sqlglot.executor.python.Python):
144class MacroDialect(Python):
145    class Generator(PythonGenerator):
146        TRANSFORMS = {
147            **PythonGenerator.TRANSFORMS,
148            exp.Column: lambda self, e: f"exp.to_column('{self.sql(e, 'this')}')",
149            exp.Lambda: lambda self, e: f"lambda {self.expressions(e)}: {self.sql(e, 'this')}",
150            MacroFunc: _macro_func_sql,
151            MacroSQL: lambda self, e: _macro_sql(self.sql(e, "this"), e.args.get("into")),
152            MacroStrReplace: lambda self, e: _macro_str_replace(self.sql(e, "this")),
153        }
SUPPORTS_COLUMN_JOIN_MARKS = False

Whether the old-style outer join (+) syntax is supported.

UNESCAPED_SEQUENCES: dict[str, str] = {'\\a': '\x07', '\\b': '\x08', '\\f': '\x0c', '\\n': '\n', '\\r': '\r', '\\t': '\t', '\\v': '\x0b', '\\\\': '\\'}

Mapping of an escaped sequence (\n) to its unescaped version ( ).

STRINGS_SUPPORT_ESCAPED_SEQUENCES: bool = True

Whether string literals support escape sequences (e.g. \n). Set by the metaclass based on the tokenizer's STRING_ESCAPES.

BYTE_STRINGS_SUPPORT_ESCAPED_SEQUENCES: bool = True

Whether byte string literals support escape sequences. Set by the metaclass based on the tokenizer's BYTE_STRING_ESCAPES.

INITCAP_SUPPORTS_CUSTOM_DELIMITERS = False
tokenizer_class = <class 'sqlglot.dialects.dialect.Tokenizer'>
jsonpath_tokenizer_class = <class 'sqlglot.dialects.dialect.JSONPathTokenizer'>
parser_class = <class 'sqlglot.parsers.base.BaseParser'>
generator_class = <class 'MacroDialect.Generator'>
TIME_TRIE: dict = {}
FORMAT_TRIE: dict = {}
INVERSE_TIME_MAPPING: dict[str, str] = {}
INVERSE_TIME_TRIE: dict = {}
INVERSE_FORMAT_MAPPING: dict[str, str] = {}
INVERSE_FORMAT_TRIE: dict = {}
INVERSE_CREATABLE_KIND_MAPPING: dict[str, str] = {}
ESCAPED_SEQUENCES: dict[str, str] = {'\x07': '\\a', '\x08': '\\b', '\x0c': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t', '\x0b': '\\v', '\\': '\\\\'}
QUOTE_START = "'"
QUOTE_END = "'"
IDENTIFIER_START = '"'
IDENTIFIER_END = '"'
VALID_INTERVAL_UNITS: set[str] = {'EPOCH', 'M', 'MINUTES', 'SECS', 'USECOND', 'WEEKDAY_ISO', 'MILLENIA', 'WY', 'MIN', 'MICROSECOND', 'MS', 'WEEKISO', 'WEEKOFYEARISO', 'MILLISEC', 'H', 'NSEC', 'MIL', 'MONTH', 'WOY', 'C', 'EPOCH_SECOND', 'MONS', 'YYYY', 'DAY', 'QUARTERS', 'MICROSEC', 'DECADES', 'NANOSECOND', 'NS', 'SECOND', 'MILLENNIUM', 'MSEC', 'MICROSECONDS', 'DAY OF WEEK', 'SECONDS', 'TZM', 'HR', 'SEC', 'DECS', 'MSECOND', 'MINS', 'USECONDS', 'CENT', 'MILLISECOND', 'NANOSECS', 'MM', 'HH', 'EPOCH_MICROSECOND', 'TIMEZONE_HOUR', 'US', 'MON', 'CENTURY', 'YY', 'DAYOFWEEK', 'DOY', 'DW', 'DAYOFWEEK_ISO', 'EPOCH_MILLISECONDS', 'DAYOFWEEKISO', 'Y', 'D', 'QTR', 'YR', 'MONTHS', 'Q', 'HRS', 'DOW', 'TZH', 'MSECONDS', 'DW_ISO', 'NSECOND', 'DAYS', 'NSECONDS', 'EPOCH_MILLISECOND', 'DD', 'W', 'WEEKDAY', 'USECS', 'WEEKOFYEAR_ISO', 'MI', 'TIMEZONE_MINUTE', 'DAY OF YEAR', 'CENTS', 'HOURS', 'NANOSEC', 'YYY', 'MICROSECS', 'MSECS', 'MILLISECONDS', 'DY', 'CENTURIES', 'YEAR', 'YEARS', 'WEEK', 'USEC', 'EPOCH_SECONDS', 'WK', 'DEC', 'EPOCH_MICROSECONDS', 'MILLISECS', 'S', 'EPOCH_NANOSECOND', 'DAYOFMONTH', 'DOW_ISO', 'QTRS', 'QUARTER', 'DECADE', 'YRS', 'MINUTE', 'HOUR', 'WEEK_ISO', 'MILLISECON', 'EPOCH_NANOSECONDS', 'DAYOFYEAR', 'MILS', 'WEEKOFYEAR'}
BIT_START: str | None = None
BIT_END: str | None = None
HEX_START: str | None = None
HEX_END: str | None = None
BYTE_START: str | None = None
BYTE_END: str | None = None
UNICODE_START: str | None = None
UNICODE_END: str | None = None
Inherited Members
sqlglot.dialects.dialect.Dialect
Dialect
INDEX_OFFSET
WEEK_OFFSET
UNNEST_COLUMN_ONLY
ALIAS_POST_TABLESAMPLE
TABLESAMPLE_SIZE_IS_PERCENT
NORMALIZATION_STRATEGY
IDENTIFIERS_CAN_START_WITH_DIGIT
DPIPE_IS_STRING_CONCAT
STRICT_STRING_CONCAT
SUPPORTS_USER_DEFINED_TYPES
COPY_PARAMS_ARE_CSV
NORMALIZE_FUNCTIONS
PRESERVE_ORIGINAL_NAMES
LOG_BASE_FIRST
NULL_ORDERING
TYPED_DIVISION
SAFE_DIVISION
CONCAT_COALESCE
CONCAT_WS_COALESCE
HEX_LOWERCASE
DATE_FORMAT
DATEINT_FORMAT
TIME_FORMAT
TIME_MAPPING
FORMAT_MAPPING
INVERSE_VECTOR_TYPE_ALIASES
PSEUDOCOLUMNS
PREFER_CTE_ALIAS_COLUMN
FORCE_EARLY_ALIAS_REF_EXPANSION
EXPAND_ONLY_GROUP_ALIAS_REF
ANNOTATE_ALL_SCOPES
DISABLES_ALIAS_REF_EXPANSION
SUPPORTS_ALIAS_REFS_IN_JOIN_CONDITIONS
SUPPORTS_ORDER_BY_ALL
PROJECTION_ALIASES_SHADOW_SOURCE_NAMES
TABLES_REFERENCEABLE_AS_COLUMNS
SUPPORTS_STRUCT_STAR_EXPANSION
EXCLUDES_PSEUDOCOLUMNS_FROM_STAR
QUERY_RESULTS_ARE_STRUCTS
REQUIRES_PARENTHESIZED_STRUCT_ACCESS
SUPPORTS_NULL_TYPE
COALESCE_COMPARISON_NON_STANDARD
HAS_DISTINCT_ARRAY_CONSTRUCTORS
SUPPORTS_FIXED_SIZE_ARRAYS
STRICT_JSON_PATH_SYNTAX
JSON_PATH_SINGLE_DOT_IS_WILDCARD
ON_CONDITION_EMPTY_BEFORE_ERROR
ARRAY_AGG_INCLUDES_NULLS
ARRAY_FUNCS_PROPAGATES_NULLS
PROMOTE_TO_INFERRED_DATETIME_TYPE
SUPPORTS_VALUES_DEFAULT
NUMBERS_CAN_BE_UNDERSCORE_SEPARATED
HEX_STRING_IS_INTEGER_TYPE
REGEXP_EXTRACT_DEFAULT_GROUP
REGEXP_EXTRACT_POSITION_OVERFLOW_RETURNS_NULL
SET_OP_DISTINCT_BY_DEFAULT
CREATABLE_KIND_MAPPING
ALTER_TABLE_SUPPORTS_CASCADE
ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN
TRY_CAST_REQUIRES_STRING
SAFE_TO_ELIMINATE_DOUBLE_NEGATION
INITCAP_DEFAULT_DELIMITER_CHARS
BYTE_STRING_IS_BYTES_TYPE
UUID_IS_STRING_TYPE
JSON_EXTRACT_SCALAR_SCALAR_ONLY
DEFAULT_FUNCTIONS_COLUMN_NAMES
DEFAULT_NULL_TYPE
LEAST_GREATEST_IGNORES_NULLS
PRIORITIZE_NON_LITERAL_TYPES
ALIAS_POST_VERSION
DATE_PART_MAPPING
COERCES_TO
EXPRESSION_METADATA
SUPPORTED_SETTINGS
get_or_raise
format_time
version
settings
normalize_identifier
case_sensitive
can_quote
quote_identifier
to_json_path
parse
parse_into
generate
transpile
tokenize
tokenizer
jsonpath_tokenizer
parser
generator
generate_values_aliases
sqlglot.executor.python.Python
Tokenizer
class MacroDialect.Generator(sqlglot.generators.python.PythonGenerator):
145    class Generator(PythonGenerator):
146        TRANSFORMS = {
147            **PythonGenerator.TRANSFORMS,
148            exp.Column: lambda self, e: f"exp.to_column('{self.sql(e, 'this')}')",
149            exp.Lambda: lambda self, e: f"lambda {self.expressions(e)}: {self.sql(e, 'this')}",
150            MacroFunc: _macro_func_sql,
151            MacroSQL: lambda self, e: _macro_sql(self.sql(e, "this"), e.args.get("into")),
152            MacroStrReplace: lambda self, e: _macro_str_replace(self.sql(e, "this")),
153        }

Generator converts a given syntax tree to the corresponding SQL string.

Arguments:
  • pretty: Whether to format the produced SQL string. Default: False.
  • identify: Determines when an identifier should be quoted. Possible values are: False (default): Never quote, except in cases where it's mandatory by the dialect. True: Always quote except for specials cases. 'safe': Only quote identifiers that are case insensitive.
  • normalize: Whether to normalize identifiers to lowercase. Default: False.
  • pad: The pad size in a formatted string. For example, this affects the indentation of a projection in a query, relative to its nesting level. Default: 2.
  • indent: The indentation size in a formatted string. For example, this affects the indentation of subqueries and filters under a WHERE clause. Default: 2.
  • normalize_functions: How to normalize function names. Possible values are: "upper" or True (default): Convert names to uppercase. "lower": Convert names to lowercase. False: Disables function name normalization.
  • unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. Default ErrorLevel.WARN.
  • max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. This is only relevant if unsupported_level is ErrorLevel.RAISE. Default: 3
  • leading_comma: Whether the comma is leading or trailing in select expressions. This is only relevant when generating in pretty mode. Default: False
  • max_text_width: The max number of characters in a segment before creating new lines in pretty mode. The default is on the smaller end because the length only represents a segment and not the true line length. Default: 80
  • comments: Whether to preserve comments in the output SQL code. Default: True
TRANSFORMS = {<class 'sqlglot.expressions.core.Add'>: <function _rename>, <class 'sqlglot.expressions.core.Adjacent'>: <function _rename>, <class 'sqlglot.expressions.core.And'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.array.ArrayContains'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayContainsAll'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayOverlaps'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayPosition'>: <function _rename>, <class 'sqlglot.expressions.core.Binary'>: <function _rename>, <class 'sqlglot.expressions.core.BitwiseAnd'>: <function _rename>, <class 'sqlglot.expressions.core.BitwiseLeftShift'>: <function _rename>, <class 'sqlglot.expressions.core.BitwiseOr'>: <function _rename>, <class 'sqlglot.expressions.core.BitwiseRightShift'>: <function _rename>, <class 'sqlglot.expressions.core.BitwiseXor'>: <function _rename>, <class 'sqlglot.expressions.functions.Collate'>: <function _rename>, <class 'sqlglot.expressions.core.Connector'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Corr'>: <function _rename>, <class 'sqlglot.expressions.core.DPipe'>: <function _rename>, <class 'sqlglot.expressions.core.Distance'>: <function _rename>, <class 'sqlglot.expressions.core.Div'>: <function _div_sql>, <class 'sqlglot.expressions.core.Dot'>: <function _rename>, <class 'sqlglot.expressions.core.EQ'>: <function _rename>, <class 'sqlglot.expressions.core.Escape'>: <function _rename>, <class 'sqlglot.expressions.core.ExtendsLeft'>: <function _rename>, <class 'sqlglot.expressions.core.ExtendsRight'>: <function _rename>, <class 'sqlglot.expressions.core.GT'>: <function _rename>, <class 'sqlglot.expressions.core.GTE'>: <function _rename>, <class 'sqlglot.expressions.core.Glob'>: <function _rename>, <class 'sqlglot.expressions.core.ILike'>: <function _rename>, <class 'sqlglot.expressions.core.IntDiv'>: <function _rename>, <class 'sqlglot.expressions.core.Is'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.json.JSONArrayContains'>: <function _rename>, <class 'sqlglot.expressions.json.JSONBContains'>: <function _rename>, <class 'sqlglot.expressions.json.JSONBContainsAllTopKeys'>: <function _rename>, <class 'sqlglot.expressions.json.JSONBContainsAnyTopKeys'>: <function _rename>, <class 'sqlglot.expressions.json.JSONBDeleteAtPath'>: <function _rename>, <class 'sqlglot.expressions.json.JSONBExtract'>: <function _rename>, <class 'sqlglot.expressions.json.JSONBExtractScalar'>: <function _rename>, <class 'sqlglot.expressions.json.JSONExtract'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.json.JSONExtractScalar'>: <function _rename>, <class 'sqlglot.expressions.core.Kwarg'>: <function _rename>, <class 'sqlglot.expressions.core.LT'>: <function _rename>, <class 'sqlglot.expressions.core.LTE'>: <function _rename>, <class 'sqlglot.expressions.core.Like'>: <function _rename>, <class 'sqlglot.expressions.core.Match'>: <function _rename>, <class 'sqlglot.expressions.core.Mod'>: <function _rename>, <class 'sqlglot.expressions.core.Mul'>: <function _rename>, <class 'sqlglot.expressions.core.NEQ'>: <function _rename>, <class 'sqlglot.expressions.core.NestedJSONSelect'>: <function _rename>, <class 'sqlglot.expressions.core.NullSafeEQ'>: <function _rename>, <class 'sqlglot.expressions.core.NullSafeNEQ'>: <function _rename>, <class 'sqlglot.expressions.core.Operator'>: <function _rename>, <class 'sqlglot.expressions.core.Or'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.core.Overlaps'>: <function _rename>, <class 'sqlglot.expressions.core.Pow'>: <function _rename>, <class 'sqlglot.expressions.core.PropertyEQ'>: <function _rename>, <class 'sqlglot.expressions.string.RegexpFullMatch'>: <function _rename>, <class 'sqlglot.expressions.string.RegexpILike'>: <function _rename>, <class 'sqlglot.expressions.core.RegexpLike'>: <function _rename>, <class 'sqlglot.expressions.core.SimilarTo'>: <function _rename>, <class 'sqlglot.expressions.core.Sub'>: <function _rename>, <class 'sqlglot.expressions.core.Xor'>: <function _rename>, <class 'sqlglot.expressions.aggregate.AIAgg'>: <function _rename>, <class 'sqlglot.expressions.functions.AIClassify'>: <function _rename>, <class 'sqlglot.expressions.functions.AIEmbed'>: <function _rename>, <class 'sqlglot.expressions.functions.AIForecast'>: <function _rename>, <class 'sqlglot.expressions.functions.AIGenerate'>: <function _rename>, <class 'sqlglot.expressions.functions.AISimilarity'>: <function _rename>, <class 'sqlglot.expressions.aggregate.AISummarizeAgg'>: <function _rename>, <class 'sqlglot.expressions.math.Abs'>: <function _rename>, <class 'sqlglot.expressions.math.Acos'>: <function _rename>, <class 'sqlglot.expressions.math.Acosh'>: <function _rename>, <class 'sqlglot.expressions.temporal.AddMonths'>: <function _rename>, <class 'sqlglot.expressions.core.AnonymousAggFunc'>: <function _rename>, <class 'sqlglot.expressions.aggregate.AnyValue'>: <function _rename>, <class 'sqlglot.expressions.array.Apply'>: <function _rename>, <class 'sqlglot.expressions.core.ApproxDistinct'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ApproxPercentileAccumulate'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ApproxPercentileCombine'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ApproxPercentileEstimate'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ApproxQuantile'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ApproxQuantiles'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ApproxTopK'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ApproxTopKAccumulate'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ApproxTopKCombine'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ApproxTopKEstimate'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ApproxTopSum'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ApproximateSimilarity'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ArgMax'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ArgMin'>: <function _rename>, <class 'sqlglot.expressions.array.Array'>: <function inline_array_sql>, <class 'sqlglot.expressions.aggregate.ArrayAgg'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayAll'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayAny'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayAppend'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayCompact'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayConcat'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ArrayConcatAgg'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayConstructCompact'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayDistinct'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayExcept'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayFilter'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayFirst'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayInsert'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayIntersect'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayLast'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayMax'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayMin'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayPrepend'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayRemove'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayRemoveAt'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayReverse'>: <function _rename>, <class 'sqlglot.expressions.array.ArraySize'>: <function _rename>, <class 'sqlglot.expressions.array.ArraySlice'>: <function _rename>, <class 'sqlglot.expressions.array.ArraySort'>: <function _rename>, <class 'sqlglot.expressions.array.ArraySum'>: <function _rename>, <class 'sqlglot.expressions.array.ArrayToString'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ArrayUnionAgg'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ArrayUniqueAgg'>: <function _rename>, <class 'sqlglot.expressions.array.ArraysZip'>: <function _rename>, <class 'sqlglot.expressions.string.Ascii'>: <function _rename>, <class 'sqlglot.expressions.math.Asin'>: <function _rename>, <class 'sqlglot.expressions.math.Asinh'>: <function _rename>, <class 'sqlglot.expressions.math.Atan'>: <function _rename>, <class 'sqlglot.expressions.math.Atan2'>: <function _rename>, <class 'sqlglot.expressions.math.Atanh'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Avg'>: <function _rename>, <class 'sqlglot.expressions.string.Base64DecodeBinary'>: <function _rename>, <class 'sqlglot.expressions.string.Base64DecodeString'>: <function _rename>, <class 'sqlglot.expressions.string.Base64Encode'>: <function _rename>, <class 'sqlglot.expressions.string.BitLength'>: <function _rename>, <class 'sqlglot.expressions.math.BitmapBitPosition'>: <function _rename>, <class 'sqlglot.expressions.math.BitmapBucketNumber'>: <function _rename>, <class 'sqlglot.expressions.math.BitmapConstructAgg'>: <function _rename>, <class 'sqlglot.expressions.math.BitmapCount'>: <function _rename>, <class 'sqlglot.expressions.math.BitmapOrAgg'>: <function _rename>, <class 'sqlglot.expressions.math.BitwiseAndAgg'>: <function _rename>, <class 'sqlglot.expressions.math.BitwiseCount'>: <function _rename>, <class 'sqlglot.expressions.math.BitwiseOrAgg'>: <function _rename>, <class 'sqlglot.expressions.math.BitwiseXorAgg'>: <function _rename>, <class 'sqlglot.expressions.math.Booland'>: <function _rename>, <class 'sqlglot.expressions.math.Boolnot'>: <function _rename>, <class 'sqlglot.expressions.math.Boolor'>: <function _rename>, <class 'sqlglot.expressions.math.BoolxorAgg'>: <function _rename>, <class 'sqlglot.expressions.string.ByteLength'>: <function _rename>, <class 'sqlglot.expressions.functions.Case'>: <function _case_sql>, <class 'sqlglot.expressions.functions.Cast'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.functions.CastToStrType'>: <function _rename>, <class 'sqlglot.expressions.math.Cbrt'>: <function _rename>, <class 'sqlglot.expressions.math.Ceil'>: <function _rename>, <class 'sqlglot.expressions.json.CheckJson'>: <function _rename>, <class 'sqlglot.expressions.functions.CheckXml'>: <function _rename>, <class 'sqlglot.expressions.string.Chr'>: <function _rename>, <class 'sqlglot.expressions.string.CityHash64'>: <function _rename>, <class 'sqlglot.expressions.functions.Coalesce'>: <function _rename>, <class 'sqlglot.expressions.string.CodePointsToBytes'>: <function _rename>, <class 'sqlglot.expressions.string.CodePointsToString'>: <function _rename>, <class 'sqlglot.expressions.functions.Collation'>: <function _rename>, <class 'sqlglot.expressions.functions.Columns'>: <function _rename>, <class 'sqlglot.expressions.core.CombinedAggFunc'>: <function _rename>, <class 'sqlglot.expressions.core.CombinedParameterizedAgg'>: <function _rename>, <class 'sqlglot.expressions.string.Compress'>: <function _rename>, <class 'sqlglot.expressions.string.Concat'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.string.ConcatWs'>: <function _rename>, <class 'sqlglot.expressions.functions.ConnectByRoot'>: <function _rename>, <class 'sqlglot.expressions.string.Contains'>: <function _rename>, <class 'sqlglot.expressions.functions.Convert'>: <function _rename>, <class 'sqlglot.expressions.temporal.ConvertTimezone'>: <function _rename>, <class 'sqlglot.expressions.string.ConvertToCharset'>: <function _rename>, <class 'sqlglot.expressions.math.Cos'>: <function _rename>, <class 'sqlglot.expressions.math.Cosh'>: <function _rename>, <class 'sqlglot.expressions.math.CosineDistance'>: <function _rename>, <class 'sqlglot.expressions.math.Cot'>: <function _rename>, <class 'sqlglot.expressions.math.Coth'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Count'>: <function _rename>, <class 'sqlglot.expressions.aggregate.CountIf'>: <function _rename>, <class 'sqlglot.expressions.aggregate.CovarPop'>: <function _rename>, <class 'sqlglot.expressions.aggregate.CovarSamp'>: <function _rename>, <class 'sqlglot.expressions.math.Csc'>: <function _rename>, <class 'sqlglot.expressions.math.Csch'>: <function _rename>, <class 'sqlglot.expressions.aggregate.CumeDist'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentAccount'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentAccountName'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentAvailableRoles'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentCatalog'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentClient'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentDatabase'>: <function _rename>, <class 'sqlglot.expressions.temporal.CurrentDate'>: <function _rename>, <class 'sqlglot.expressions.temporal.CurrentDatetime'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentIpAddress'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentOrganizationName'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentOrganizationUser'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentRegion'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentRole'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentRoleType'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentSchema'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentSchemas'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentSecondaryRoles'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentSession'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentStatement'>: <function _rename>, <class 'sqlglot.expressions.temporal.CurrentTime'>: <function _rename>, <class 'sqlglot.expressions.temporal.CurrentTimestamp'>: <function _rename>, <class 'sqlglot.expressions.temporal.CurrentTimestampLTZ'>: <function _rename>, <class 'sqlglot.expressions.temporal.CurrentTimezone'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentTransaction'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentUser'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentUserId'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentVersion'>: <function _rename>, <class 'sqlglot.expressions.functions.CurrentWarehouse'>: <function _rename>, <class 'sqlglot.expressions.temporal.Date'>: <function _rename>, <class 'sqlglot.expressions.temporal.DateAdd'>: <function _rename>, <class 'sqlglot.expressions.temporal.DateBin'>: <function _rename>, <class 'sqlglot.expressions.temporal.DateDiff'>: <function _rename>, <class 'sqlglot.expressions.temporal.DateFromParts'>: <function _rename>, <class 'sqlglot.expressions.temporal.DateFromUnixDate'>: <function _rename>, <class 'sqlglot.expressions.temporal.DateStrToDate'>: <function _rename>, <class 'sqlglot.expressions.temporal.DateSub'>: <function _rename>, <class 'sqlglot.expressions.temporal.DateToDateStr'>: <function _rename>, <class 'sqlglot.expressions.temporal.DateToDi'>: <function _rename>, <class 'sqlglot.expressions.temporal.DateTrunc'>: <function _rename>, <class 'sqlglot.expressions.temporal.Datetime'>: <function _rename>, <class 'sqlglot.expressions.temporal.DatetimeAdd'>: <function _rename>, <class 'sqlglot.expressions.temporal.DatetimeDiff'>: <function _rename>, <class 'sqlglot.expressions.temporal.DatetimeSub'>: <function _rename>, <class 'sqlglot.expressions.temporal.DatetimeTrunc'>: <function _rename>, <class 'sqlglot.expressions.temporal.Day'>: <function _rename>, <class 'sqlglot.expressions.temporal.DayOfMonth'>: <function _rename>, <class 'sqlglot.expressions.temporal.DayOfWeek'>: <function _rename>, <class 'sqlglot.expressions.temporal.DayOfWeekIso'>: <function _rename>, <class 'sqlglot.expressions.temporal.DayOfYear'>: <function _rename>, <class 'sqlglot.expressions.temporal.Dayname'>: <function _rename>, <class 'sqlglot.expressions.string.Decode'>: <function _rename>, <class 'sqlglot.expressions.functions.DecodeCase'>: <function _rename>, <class 'sqlglot.expressions.string.DecompressBinary'>: <function _rename>, <class 'sqlglot.expressions.string.DecompressString'>: <function _rename>, <class 'sqlglot.expressions.string.Decrypt'>: <function _rename>, <class 'sqlglot.expressions.string.DecryptRaw'>: <function _rename>, <class 'sqlglot.expressions.math.Degrees'>: <function _rename>, <class 'sqlglot.expressions.aggregate.DenseRank'>: <function _rename>, <class 'sqlglot.expressions.temporal.DiToDate'>: <function _rename>, <class 'sqlglot.expressions.math.DotProduct'>: <function _rename>, <class 'sqlglot.expressions.string.Elt'>: <function _rename>, <class 'sqlglot.expressions.string.Encode'>: <function _rename>, <class 'sqlglot.expressions.string.Encrypt'>: <function _rename>, <class 'sqlglot.expressions.string.EncryptRaw'>: <function _rename>, <class 'sqlglot.expressions.string.EndsWith'>: <function _rename>, <class 'sqlglot.expressions.functions.EqualNull'>: <function _rename>, <class 'sqlglot.expressions.math.EuclideanDistance'>: <function _rename>, <class 'sqlglot.expressions.functions.Exists'>: <function _rename>, <class 'sqlglot.expressions.math.Exp'>: <function _rename>, <class 'sqlglot.expressions.array.Explode'>: <function _rename>, <class 'sqlglot.expressions.array.ExplodingGenerateSeries'>: <function _rename>, <class 'sqlglot.expressions.temporal.Extract'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.math.Factorial'>: <function _rename>, <class 'sqlglot.expressions.string.FarmFingerprint'>: <function _rename>, <class 'sqlglot.expressions.functions.FeaturesAtTime'>: <function _rename>, <class 'sqlglot.expressions.aggregate.First'>: <function _rename>, <class 'sqlglot.expressions.aggregate.FirstValue'>: <function _rename>, <class 'sqlglot.expressions.array.Flatten'>: <function _rename>, <class 'sqlglot.expressions.functions.Float64'>: <function _rename>, <class 'sqlglot.expressions.math.Floor'>: <function _rename>, <class 'sqlglot.expressions.string.Format'>: <function _rename>, <class 'sqlglot.expressions.string.FromBase'>: <function _rename>, <class 'sqlglot.expressions.string.FromBase32'>: <function _rename>, <class 'sqlglot.expressions.string.FromBase64'>: <function _rename>, <class 'sqlglot.expressions.temporal.FromISO8601Timestamp'>: <function _rename>, <class 'sqlglot.expressions.temporal.GapFill'>: <function _rename>, <class 'sqlglot.expressions.functions.GenerateBool'>: <function _rename>, <class 'sqlglot.expressions.temporal.GenerateDateArray'>: <function _rename>, <class 'sqlglot.expressions.functions.GenerateDouble'>: <function _rename>, <class 'sqlglot.expressions.functions.GenerateEmbedding'>: <function _rename>, <class 'sqlglot.expressions.functions.GenerateInt'>: <function _rename>, <class 'sqlglot.expressions.array.GenerateSeries'>: <function _rename>, <class 'sqlglot.expressions.functions.GenerateTable'>: <function _rename>, <class 'sqlglot.expressions.functions.GenerateText'>: <function _rename>, <class 'sqlglot.expressions.temporal.GenerateTimestampArray'>: <function _rename>, <class 'sqlglot.expressions.array.Generator'>: <function _rename>, <class 'sqlglot.expressions.temporal.GetExtract'>: <function _rename>, <class 'sqlglot.expressions.math.Getbit'>: <function _rename>, <class 'sqlglot.expressions.functions.Greatest'>: <function _rename>, <class 'sqlglot.expressions.aggregate.GroupConcat'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Grouping'>: <function _rename>, <class 'sqlglot.expressions.aggregate.GroupingId'>: <function _rename>, <class 'sqlglot.expressions.core.HashAgg'>: <function _rename>, <class 'sqlglot.expressions.string.Hex'>: <function _rename>, <class 'sqlglot.expressions.string.HexDecodeString'>: <function _rename>, <class 'sqlglot.expressions.core.Hll'>: <function _rename>, <class 'sqlglot.expressions.functions.Host'>: <function _rename>, <class 'sqlglot.expressions.temporal.Hour'>: <function _rename>, <class 'sqlglot.expressions.functions.If'>: <function _rename>, <class 'sqlglot.expressions.string.Initcap'>: <function _rename>, <class 'sqlglot.expressions.array.Inline'>: <function _rename>, <class 'sqlglot.expressions.functions.Int64'>: <function _rename>, <class 'sqlglot.expressions.functions.IsArray'>: <function _rename>, <class 'sqlglot.expressions.string.IsAscii'>: <function _rename>, <class 'sqlglot.expressions.math.IsInf'>: <function _rename>, <class 'sqlglot.expressions.math.IsNan'>: <function _rename>, <class 'sqlglot.expressions.functions.IsNullValue'>: <function _rename>, <class 'sqlglot.expressions.json.JSONArray'>: <function _rename>, <class 'sqlglot.expressions.json.JSONArrayAgg'>: <function _rename>, <class 'sqlglot.expressions.json.JSONArrayAppend'>: <function _rename>, <class 'sqlglot.expressions.json.JSONArrayInsert'>: <function _rename>, <class 'sqlglot.expressions.json.JSONBExists'>: <function _rename>, <class 'sqlglot.expressions.json.JSONBObjectAgg'>: <function _rename>, <class 'sqlglot.expressions.json.JSONBool'>: <function _rename>, <class 'sqlglot.expressions.functions.JSONCast'>: <function _rename>, <class 'sqlglot.expressions.json.JSONExists'>: <function _rename>, <class 'sqlglot.expressions.json.JSONExtractArray'>: <function _rename>, <class 'sqlglot.expressions.json.JSONFormat'>: <function _rename>, <class 'sqlglot.expressions.json.JSONKeys'>: <function _rename>, <class 'sqlglot.expressions.json.JSONKeysAtDepth'>: <function _rename>, <class 'sqlglot.expressions.json.JSONObject'>: <function _rename>, <class 'sqlglot.expressions.json.JSONObjectAgg'>: <function _rename>, <class 'sqlglot.expressions.json.JSONRemove'>: <function _rename>, <class 'sqlglot.expressions.json.JSONSet'>: <function _rename>, <class 'sqlglot.expressions.json.JSONStripNulls'>: <function _rename>, <class 'sqlglot.expressions.json.JSONTable'>: <function _rename>, <class 'sqlglot.expressions.json.JSONType'>: <function _rename>, <class 'sqlglot.expressions.query.JSONValueArray'>: <function _rename>, <class 'sqlglot.expressions.math.JarowinklerSimilarity'>: <function _rename>, <class 'sqlglot.expressions.temporal.JustifyDays'>: <function _rename>, <class 'sqlglot.expressions.temporal.JustifyHours'>: <function _rename>, <class 'sqlglot.expressions.temporal.JustifyInterval'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Kurtosis'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Lag'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Last'>: <function _rename>, <class 'sqlglot.expressions.temporal.LastDay'>: <function _rename>, <class 'sqlglot.expressions.aggregate.LastValue'>: <function _rename>, <class 'sqlglot.expressions.functions.LaxBool'>: <function _rename>, <class 'sqlglot.expressions.functions.LaxFloat64'>: <function _rename>, <class 'sqlglot.expressions.functions.LaxInt64'>: <function _rename>, <class 'sqlglot.expressions.functions.LaxString'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Lead'>: <function _rename>, <class 'sqlglot.expressions.functions.Least'>: <function _rename>, <class 'sqlglot.expressions.string.Left'>: <function _rename>, <class 'sqlglot.expressions.string.Length'>: <function _rename>, <class 'sqlglot.expressions.string.Levenshtein'>: <function _rename>, <class 'sqlglot.expressions.array.List'>: <function _rename>, <class 'sqlglot.expressions.math.Ln'>: <function _rename>, <class 'sqlglot.expressions.temporal.Localtime'>: <function _rename>, <class 'sqlglot.expressions.temporal.Localtimestamp'>: <function _rename>, <class 'sqlglot.expressions.math.Log'>: <function _rename>, <class 'sqlglot.expressions.aggregate.LogicalAnd'>: <function _rename>, <class 'sqlglot.expressions.aggregate.LogicalOr'>: <function _rename>, <class 'sqlglot.expressions.string.Lower'>: <function _rename>, <class 'sqlglot.expressions.string.LowerHex'>: <function _rename>, <class 'sqlglot.expressions.string.MD5'>: <function _rename>, <class 'sqlglot.expressions.string.MD5Digest'>: <function _rename>, <class 'sqlglot.expressions.string.MD5NumberLower64'>: <function _rename>, <class 'sqlglot.expressions.string.MD5NumberUpper64'>: <function _rename>, <class 'sqlglot.expressions.functions.MLForecast'>: <function _rename>, <class 'sqlglot.expressions.functions.MLTranslate'>: <function _rename>, <class 'sqlglot.expressions.temporal.MakeInterval'>: <function _rename>, <class 'sqlglot.expressions.math.ManhattanDistance'>: <function _rename>, <class 'sqlglot.expressions.array.Map'>: <function _rename>, <class 'sqlglot.expressions.array.MapCat'>: <function _rename>, <class 'sqlglot.expressions.array.MapContainsKey'>: <function _rename>, <class 'sqlglot.expressions.array.MapDelete'>: <function _rename>, <class 'sqlglot.expressions.array.MapFromEntries'>: <function _rename>, <class 'sqlglot.expressions.array.MapInsert'>: <function _rename>, <class 'sqlglot.expressions.array.MapKeys'>: <function _rename>, <class 'sqlglot.expressions.array.MapPick'>: <function _rename>, <class 'sqlglot.expressions.array.MapSize'>: <function _rename>, <class 'sqlglot.expressions.string.MatchAgainst'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Max'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Median'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Min'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Minhash'>: <function _rename>, <class 'sqlglot.expressions.aggregate.MinhashCombine'>: <function _rename>, <class 'sqlglot.expressions.temporal.Minute'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Mode'>: <function _rename>, <class 'sqlglot.expressions.temporal.Month'>: <function _rename>, <class 'sqlglot.expressions.temporal.Monthname'>: <function _rename>, <class 'sqlglot.expressions.temporal.MonthsBetween'>: <function _rename>, <class 'sqlglot.expressions.functions.NetFunc'>: <function _rename>, <class 'sqlglot.expressions.temporal.NextDay'>: <function _rename>, <class 'sqlglot.expressions.ddl.NextValueFor'>: <function _rename>, <class 'sqlglot.expressions.functions.Normal'>: <function _rename>, <class 'sqlglot.expressions.string.Normalize'>: <function _rename>, <class 'sqlglot.expressions.aggregate.NthValue'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Ntile'>: <function _rename>, <class 'sqlglot.expressions.functions.Nullif'>: <function _rename>, <class 'sqlglot.expressions.string.NumberToStr'>: <function _rename>, <class 'sqlglot.expressions.functions.Nvl2'>: <function _rename>, <class 'sqlglot.expressions.aggregate.ObjectAgg'>: <function _rename>, <class 'sqlglot.expressions.json.ObjectId'>: <function _rename>, <class 'sqlglot.expressions.json.ObjectInsert'>: <function _rename>, <class 'sqlglot.expressions.functions.ObjectTransform'>: <function _rename>, <class 'sqlglot.expressions.json.OpenJSON'>: <function _rename>, <class 'sqlglot.expressions.string.Overlay'>: <function _rename>, <class 'sqlglot.expressions.string.Pad'>: <function _rename>, <class 'sqlglot.expressions.core.ParameterizedAgg'>: <function _rename>, <class 'sqlglot.expressions.string.ParseBignumeric'>: <function _rename>, <class 'sqlglot.expressions.temporal.ParseDatetime'>: <function _rename>, <class 'sqlglot.expressions.functions.ParseIp'>: <function _rename>, <class 'sqlglot.expressions.json.ParseJSON'>: <function _rename>, <class 'sqlglot.expressions.string.ParseNumeric'>: <function _rename>, <class 'sqlglot.expressions.temporal.ParseTime'>: <function _rename>, <class 'sqlglot.expressions.string.ParseUrl'>: <function _rename>, <class 'sqlglot.expressions.aggregate.PercentRank'>: <function _rename>, <class 'sqlglot.expressions.aggregate.PercentileCont'>: <function _rename>, <class 'sqlglot.expressions.aggregate.PercentileDisc'>: <function _rename>, <class 'sqlglot.expressions.math.Pi'>: <function _rename>, <class 'sqlglot.expressions.array.Posexplode'>: <function _rename>, <class 'sqlglot.expressions.array.PosexplodeOuter'>: <function _rename>, <class 'sqlglot.expressions.functions.Predict'>: <function _rename>, <class 'sqlglot.expressions.temporal.PreviousDay'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Quantile'>: <function _rename>, <class 'sqlglot.expressions.temporal.Quarter'>: <function _rename>, <class 'sqlglot.expressions.math.Radians'>: <function _rename>, <class 'sqlglot.expressions.functions.Rand'>: <function _rename>, <class 'sqlglot.expressions.functions.Randn'>: <function _rename>, <class 'sqlglot.expressions.functions.Randstr'>: <function _rename>, <class 'sqlglot.expressions.functions.RangeBucket'>: <function _rename>, <class 'sqlglot.expressions.functions.RangeN'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Rank'>: <function _rename>, <class 'sqlglot.expressions.functions.ReadCSV'>: <function _rename>, <class 'sqlglot.expressions.functions.ReadParquet'>: <function _rename>, <class 'sqlglot.expressions.array.Reduce'>: <function _rename>, <class 'sqlglot.expressions.functions.RegDomain'>: <function _rename>, <class 'sqlglot.expressions.string.RegexpCount'>: <function _rename>, <class 'sqlglot.expressions.string.RegexpExtract'>: <function _rename>, <class 'sqlglot.expressions.string.RegexpExtractAll'>: <function _rename>, <class 'sqlglot.expressions.string.RegexpInstr'>: <function _rename>, <class 'sqlglot.expressions.string.RegexpReplace'>: <function _rename>, <class 'sqlglot.expressions.string.RegexpSplit'>: <function _rename>, <class 'sqlglot.expressions.aggregate.RegrAvgx'>: <function _rename>, <class 'sqlglot.expressions.aggregate.RegrAvgy'>: <function _rename>, <class 'sqlglot.expressions.aggregate.RegrCount'>: <function _rename>, <class 'sqlglot.expressions.aggregate.RegrIntercept'>: <function _rename>, <class 'sqlglot.expressions.aggregate.RegrR2'>: <function _rename>, <class 'sqlglot.expressions.aggregate.RegrSlope'>: <function _rename>, <class 'sqlglot.expressions.aggregate.RegrSxx'>: <function _rename>, <class 'sqlglot.expressions.aggregate.RegrSxy'>: <function _rename>, <class 'sqlglot.expressions.aggregate.RegrSyy'>: <function _rename>, <class 'sqlglot.expressions.aggregate.RegrValx'>: <function _rename>, <class 'sqlglot.expressions.aggregate.RegrValy'>: <function _rename>, <class 'sqlglot.expressions.string.Repeat'>: <function _rename>, <class 'sqlglot.expressions.string.Replace'>: <function _rename>, <class 'sqlglot.expressions.string.Reverse'>: <function _rename>, <class 'sqlglot.expressions.string.Right'>: <function _rename>, <class 'sqlglot.expressions.math.Round'>: <function _rename>, <class 'sqlglot.expressions.aggregate.RowNumber'>: <function _rename>, <class 'sqlglot.expressions.string.RtrimmedLength'>: <function _rename>, <class 'sqlglot.expressions.string.SHA'>: <function _rename>, <class 'sqlglot.expressions.string.SHA1Digest'>: <function _rename>, <class 'sqlglot.expressions.string.SHA2'>: <function _rename>, <class 'sqlglot.expressions.string.SHA2Digest'>: <function _rename>, <class 'sqlglot.expressions.math.SafeAdd'>: <function _rename>, <class 'sqlglot.expressions.string.SafeConvertBytesToString'>: <function _rename>, <class 'sqlglot.expressions.math.SafeDivide'>: <function _rename>, <class 'sqlglot.expressions.core.SafeFunc'>: <function _rename>, <class 'sqlglot.expressions.math.SafeMultiply'>: <function _rename>, <class 'sqlglot.expressions.math.SafeNegate'>: <function _rename>, <class 'sqlglot.expressions.math.SafeSubtract'>: <function _rename>, <class 'sqlglot.expressions.string.Search'>: <function _rename>, <class 'sqlglot.expressions.string.SearchIp'>: <function _rename>, <class 'sqlglot.expressions.math.Sec'>: <function _rename>, <class 'sqlglot.expressions.math.Sech'>: <function _rename>, <class 'sqlglot.expressions.temporal.Second'>: <function _rename>, <class 'sqlglot.expressions.functions.Seq1'>: <function _rename>, <class 'sqlglot.expressions.functions.Seq2'>: <function _rename>, <class 'sqlglot.expressions.functions.Seq4'>: <function _rename>, <class 'sqlglot.expressions.functions.Seq8'>: <function _rename>, <class 'sqlglot.expressions.functions.SessionUser'>: <function _rename>, <class 'sqlglot.expressions.math.Sign'>: <function _rename>, <class 'sqlglot.expressions.math.Sin'>: <function _rename>, <class 'sqlglot.expressions.math.Sinh'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Skewness'>: <function _rename>, <class 'sqlglot.expressions.array.SortArray'>: <function _rename>, <class 'sqlglot.expressions.string.Soundex'>: <function _rename>, <class 'sqlglot.expressions.string.SoundexP123'>: <function _rename>, <class 'sqlglot.expressions.string.Space'>: <function _rename>, <class 'sqlglot.expressions.string.Split'>: <function _rename>, <class 'sqlglot.expressions.string.SplitPart'>: <function _rename>, <class 'sqlglot.expressions.math.Sqrt'>: <function _rename>, <class 'sqlglot.expressions.array.StDistance'>: <function _rename>, <class 'sqlglot.expressions.array.StPoint'>: <function _rename>, <class 'sqlglot.expressions.string.StandardHash'>: <function _rename>, <class 'sqlglot.expressions.array.StarMap'>: <function _rename>, <class 'sqlglot.expressions.string.StartsWith'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Stddev'>: <function _rename>, <class 'sqlglot.expressions.aggregate.StddevPop'>: <function _rename>, <class 'sqlglot.expressions.aggregate.StddevSamp'>: <function _rename>, <class 'sqlglot.expressions.string.StrPosition'>: <function _rename>, <class 'sqlglot.expressions.temporal.StrToDate'>: <function _rename>, <class 'sqlglot.expressions.string.StrToMap'>: <function _rename>, <class 'sqlglot.expressions.temporal.StrToTime'>: <function _rename>, <class 'sqlglot.expressions.temporal.StrToUnix'>: <function _rename>, <class 'sqlglot.expressions.string.String'>: <function _rename>, <class 'sqlglot.expressions.array.StringToArray'>: <function _rename>, <class 'sqlglot.expressions.json.StripNullValue'>: <function _rename>, <class 'sqlglot.expressions.string.Strtok'>: <function _rename>, <class 'sqlglot.expressions.array.StrtokToArray'>: <function _rename>, <class 'sqlglot.expressions.array.Struct'>: <function _rename>, <class 'sqlglot.expressions.array.StructExtract'>: <function _rename>, <class 'sqlglot.expressions.string.Stuff'>: <function _rename>, <class 'sqlglot.expressions.string.Substring'>: <function _rename>, <class 'sqlglot.expressions.string.SubstringIndex'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Sum'>: <function _rename>, <class 'sqlglot.expressions.temporal.Systimestamp'>: <function _rename>, <class 'sqlglot.expressions.math.Tan'>: <function _rename>, <class 'sqlglot.expressions.math.Tanh'>: <function _rename>, <class 'sqlglot.expressions.temporal.Time'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimeAdd'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimeDiff'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimeFromParts'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimeSlice'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimeStrToDate'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimeStrToTime'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimeStrToUnix'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimeSub'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimeToStr'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimeToTimeStr'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimeToUnix'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimeTrunc'>: <function _rename>, <class 'sqlglot.expressions.temporal.Timestamp'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimestampAdd'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimestampDiff'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimestampFromParts'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimestampLtzFromParts'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimestampSub'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimestampTrunc'>: <function _rename>, <class 'sqlglot.expressions.temporal.TimestampTzFromParts'>: <function _rename>, <class 'sqlglot.expressions.array.ToArray'>: <function _rename>, <class 'sqlglot.expressions.string.ToBase32'>: <function _rename>, <class 'sqlglot.expressions.string.ToBase64'>: <function _rename>, <class 'sqlglot.expressions.string.ToBinary'>: <function _rename>, <class 'sqlglot.expressions.functions.ToBoolean'>: <function _rename>, <class 'sqlglot.expressions.string.ToChar'>: <function _rename>, <class 'sqlglot.expressions.string.ToCodePoints'>: <function _rename>, <class 'sqlglot.expressions.temporal.ToDays'>: <function _rename>, <class 'sqlglot.expressions.string.ToDecfloat'>: <function _rename>, <class 'sqlglot.expressions.string.ToDouble'>: <function _rename>, <class 'sqlglot.expressions.string.ToFile'>: <function _rename>, <class 'sqlglot.expressions.array.ToMap'>: <function _rename>, <class 'sqlglot.expressions.string.ToNumber'>: <function _rename>, <class 'sqlglot.expressions.functions.ToVariant'>: <function _rename>, <class 'sqlglot.expressions.array.Transform'>: <function _rename>, <class 'sqlglot.expressions.string.Translate'>: <function _rename>, <class 'sqlglot.expressions.string.Trim'>: <function _rename>, <class 'sqlglot.expressions.math.Trunc'>: <function _rename>, <class 'sqlglot.expressions.functions.Try'>: <function _rename>, <class 'sqlglot.expressions.string.TryBase64DecodeBinary'>: <function _rename>, <class 'sqlglot.expressions.string.TryBase64DecodeString'>: <function _rename>, <class 'sqlglot.expressions.functions.TryCast'>: <function _rename>, <class 'sqlglot.expressions.string.TryHexDecodeBinary'>: <function _rename>, <class 'sqlglot.expressions.string.TryHexDecodeString'>: <function _rename>, <class 'sqlglot.expressions.string.TryToDecfloat'>: <function _rename>, <class 'sqlglot.expressions.temporal.TsOrDiToDi'>: <function _rename>, <class 'sqlglot.expressions.temporal.TsOrDsAdd'>: <function _rename>, <class 'sqlglot.expressions.temporal.TsOrDsDiff'>: <function _rename>, <class 'sqlglot.expressions.temporal.TsOrDsToDate'>: <function _rename>, <class 'sqlglot.expressions.temporal.TsOrDsToDateStr'>: <function _rename>, <class 'sqlglot.expressions.temporal.TsOrDsToDatetime'>: <function _rename>, <class 'sqlglot.expressions.temporal.TsOrDsToTime'>: <function _rename>, <class 'sqlglot.expressions.temporal.TsOrDsToTimestamp'>: <function _rename>, <class 'sqlglot.expressions.core.Typeof'>: <function _rename>, <class 'sqlglot.expressions.string.Unhex'>: <function _rename>, <class 'sqlglot.expressions.string.Unicode'>: <function _rename>, <class 'sqlglot.expressions.functions.Uniform'>: <function _rename>, <class 'sqlglot.expressions.temporal.UnixDate'>: <function _rename>, <class 'sqlglot.expressions.temporal.UnixMicros'>: <function _rename>, <class 'sqlglot.expressions.temporal.UnixMillis'>: <function _rename>, <class 'sqlglot.expressions.temporal.UnixSeconds'>: <function _rename>, <class 'sqlglot.expressions.temporal.UnixToStr'>: <function _rename>, <class 'sqlglot.expressions.temporal.UnixToTime'>: <function _rename>, <class 'sqlglot.expressions.temporal.UnixToTimeStr'>: <function _rename>, <class 'sqlglot.expressions.array.Unnest'>: <function _rename>, <class 'sqlglot.expressions.string.Upper'>: <function _rename>, <class 'sqlglot.expressions.temporal.UtcDate'>: <function _rename>, <class 'sqlglot.expressions.temporal.UtcTime'>: <function _rename>, <class 'sqlglot.expressions.temporal.UtcTimestamp'>: <function _rename>, <class 'sqlglot.expressions.functions.Uuid'>: <function _rename>, <class 'sqlglot.expressions.array.VarMap'>: <function _rename>, <class 'sqlglot.expressions.aggregate.Variance'>: <function _rename>, <class 'sqlglot.expressions.aggregate.VariancePop'>: <function _rename>, <class 'sqlglot.expressions.functions.VectorSearch'>: <function _rename>, <class 'sqlglot.expressions.temporal.Week'>: <function _rename>, <class 'sqlglot.expressions.temporal.WeekOfYear'>: <function _rename>, <class 'sqlglot.expressions.functions.WeekStart'>: <function _rename>, <class 'sqlglot.expressions.functions.WidthBucket'>: <function _rename>, <class 'sqlglot.expressions.functions.XMLElement'>: <function _rename>, <class 'sqlglot.expressions.functions.XMLGet'>: <function _rename>, <class 'sqlglot.expressions.functions.XMLTable'>: <function _rename>, <class 'sqlglot.expressions.temporal.Year'>: <function _rename>, <class 'sqlglot.expressions.temporal.YearOfWeek'>: <function _rename>, <class 'sqlglot.expressions.temporal.YearOfWeekIso'>: <function _rename>, <class 'sqlglot.expressions.functions.Zipf'>: <function _rename>, <class 'sqlglot.expressions.array._ExplodeOuter'>: <function _rename>, <class 'sqlglot.expressions.core.Alias'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.core.Between'>: <function _rename>, <class 'sqlglot.expressions.core.Boolean'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.core.Column'>: <function MacroDialect.Generator.<lambda>>, <class 'sqlglot.expressions.core.Distinct'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.core.In'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.datatypes.Interval'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.query.JSONPath'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.query.JSONPathKey'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.query.JSONPathSubscript'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.query.Lambda'>: <function MacroDialect.Generator.<lambda>>, <class 'sqlglot.expressions.core.Not'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.core.Null'>: <function PythonGenerator.<lambda>>, <class 'sqlglot.expressions.core.Ordered'>: <function _ordered_py>, <class 'sqlglot.expressions.core.Star'>: <function PythonGenerator.<lambda>>, <class 'sqlmesh.core.dialect.MacroFunc'>: <function _macro_func_sql>, <class 'sqlmesh.core.dialect.MacroSQL'>: <function MacroDialect.Generator.<lambda>>, <class 'sqlmesh.core.dialect.MacroStrReplace'>: <function MacroDialect.Generator.<lambda>>}
Inherited Members
sqlglot.generator.Generator
Generator
NULL_ORDERING_SUPPORTED
WINDOW_FUNCS_WITH_NULL_ORDERING
IGNORE_NULLS_IN_FUNC
IGNORE_NULLS_BEFORE_ORDER
LOCKING_READS_SUPPORTED
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
WRAP_DERIVED_VALUES
CREATE_FUNCTION_RETURN_AS
MATCHED_BY_SOURCE
SUPPORTS_MERGE_WHERE
SINGLE_STRING_INTERVAL
INTERVAL_ALLOWS_PLURAL_FORM
LIMIT_FETCH
LIMIT_ONLY_LITERALS
RENAME_TABLE_WITH_DB
GROUPINGS_SEP
INDEX_ON
INOUT_SEPARATOR
JOIN_HINTS
DIRECTED_JOINS
TABLE_HINTS
QUERY_HINTS
QUERY_HINT_SEP
IS_BOOL_ALLOWED
DUPLICATE_KEY_UPDATE_WITH_SET
LIMIT_IS_TOP
RETURNING_END
EXTRACT_ALLOWS_QUOTES
TZ_TO_WITH_TIME_ZONE
NVL2_SUPPORTED
SELECT_KINDS
VALUES_AS_TABLE
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD
UNNEST_WITH_ORDINALITY
AGGREGATE_FILTER_SUPPORTED
SEMI_ANTI_JOIN_WITH_SIDE
COMPUTED_COLUMN_WITH_TYPE
SUPPORTS_TABLE_COPY
TABLESAMPLE_REQUIRES_PARENS
TABLESAMPLE_SIZE_IS_ROWS
TABLESAMPLE_KEYWORDS
TABLESAMPLE_WITH_METHOD
TABLESAMPLE_SEED_KEYWORD
COLLATE_IS_FUNC
DATA_TYPE_SPECIFIERS_ALLOWED
ENSURE_BOOLS
CTE_RECURSIVE_KEYWORD_REQUIRED
SUPPORTS_SINGLE_ARG_CONCAT
LAST_DAY_SUPPORTS_DATE_PART
SUPPORTS_TABLE_ALIAS_COLUMNS
SUPPORTS_NAMED_CTE_COLUMNS
UNPIVOT_ALIASES_ARE_IDENTIFIERS
JSON_KEY_VALUE_PAIR_SEP
INSERT_OVERWRITE
SUPPORTS_SELECT_INTO
SUPPORTS_UNLOGGED_TABLES
SUPPORTS_CREATE_TABLE_LIKE
SUPPORTS_MODIFY_COLUMN
SUPPORTS_CHANGE_COLUMN
LIKE_PROPERTY_INSIDE_SCHEMA
MULTI_ARG_DISTINCT
JSON_TYPE_REQUIRED_FOR_EXTRACTION
JSON_PATH_BRACKETED_KEY_SUPPORTED
JSON_PATH_SINGLE_QUOTE_ESCAPE
SUPPORTED_JSON_PATH_PARTS
CAN_IMPLEMENT_ARRAY_ANY
SUPPORTS_TO_NUMBER
SUPPORTS_WINDOW_EXCLUDE
SET_OP_MODIFIERS
COPY_PARAMS_ARE_WRAPPED
COPY_PARAMS_EQ_REQUIRED
COPY_HAS_INTO_KEYWORD
TRY_SUPPORTED
SUPPORTS_UESCAPE
UNICODE_SUBSTITUTE
STAR_EXCEPT
HEX_FUNC
WITH_PROPERTIES_PREFIX
QUOTE_JSON_PATH
PAD_FILL_PATTERN_IS_REQUIRED
SUPPORTS_EXPLODING_PROJECTIONS
ARRAY_CONCAT_IS_VAR_LEN
SUPPORTS_CONVERT_TIMEZONE
SUPPORTS_MEDIAN
SUPPORTS_UNIX_SECONDS
ALTER_SET_WRAPPED
NORMALIZE_EXTRACT_DATE_PARTS
PARSE_JSON_NAME
ARRAY_SIZE_NAME
ALTER_SET_TYPE
ARRAY_SIZE_DIM_REQUIRED
SUPPORTS_DECODE_CASE
SUPPORTS_BETWEEN_FLAGS
SUPPORTS_LIKE_QUANTIFIERS
MATCH_AGAINST_TABLE_PREFIX
SET_ASSIGNMENT_REQUIRES_VARIABLE_KEYWORD
DECLARE_DEFAULT_ASSIGNMENT
UPDATE_STATEMENT_SUPPORTS_FROM
STAR_EXCLUDE_REQUIRES_DERIVED_TABLE
SUPPORTS_DROP_ALTER_ICEBERG_PROPERTY
TYPE_MAPPING
UNSUPPORTED_TYPES
TYPE_PARAM_SETTINGS
TIME_PART_SINGULARS
AFTER_HAVING_MODIFIER_TRANSFORMS
TOKEN_MAPPING
STRUCT_DELIMITER
PARAMETER_TOKEN
NAMED_PLACEHOLDER_TOKEN
EXPRESSION_PRECEDES_PROPERTIES_CREATABLES
PROPERTIES_LOCATION
RESERVED_KEYWORDS
WITH_SEPARATED_COMMENTS
EXCLUDE_COMMENTS
UNWRAPPED_INTERVAL_VALUES
PARAMETERIZABLE_TEXT_TYPES
EXPRESSIONS_WITHOUT_NESTED_CTES
RESPECT_IGNORE_NULLS_UNSUPPORTED_EXPRESSIONS
SAFE_JSON_PATH_KEY_RE
SENTINEL_LINE_BREAK
pretty
identify
normalize
pad
unsupported_level
max_unsupported
leading_comma
max_text_width
comments
dialect
normalize_functions
unsupported_messages
generate
preprocess
unsupported
sep
seg
sanitize_comment
maybe_comment
wrap
no_identify
normalize_func
indent
sql
uncache_sql
cache_sql
characterset_sql
column_parts
column_sql
pseudocolumn_sql
columnposition_sql
columndef_sql
columnconstraint_sql
computedcolumnconstraint_sql
autoincrementcolumnconstraint_sql
compresscolumnconstraint_sql
generatedasidentitycolumnconstraint_sql
generatedasrowcolumnconstraint_sql
periodforsystemtimeconstraint_sql
notnullcolumnconstraint_sql
primarykeycolumnconstraint_sql
uniquecolumnconstraint_sql
inoutcolumnconstraint_sql
createable_sql
create_sql
sequenceproperties_sql
triggerproperties_sql
triggerreferencing_sql
triggerevent_sql
clone_sql
describe_sql
heredoc_sql
prepend_ctes
with_sql
cte_sql
tablealias_sql
bitstring_sql
hexstring_sql
bytestring_sql
unicodestring_sql
rawstring_sql
datatypeparam_sql
datatype_param_bound_limiter
datatype_sql
directory_sql
delete_sql
drop_sql
set_operation
set_operations
fetch_sql
limitoptions_sql
filter_sql
hint_sql
indexparameters_sql
index_sql
identifier_sql
hex_sql
lowerhex_sql
inputoutputformat_sql
national_sql
partition_sql
properties_sql
root_properties
properties
with_properties
locate_properties
property_name
property_sql
uuidproperty_sql
likeproperty_sql
fallbackproperty_sql
journalproperty_sql
freespaceproperty_sql
checksumproperty_sql
mergeblockratioproperty_sql
moduleproperty_sql
datablocksizeproperty_sql
blockcompressionproperty_sql
isolatedloadingproperty_sql
partitionboundspec_sql
partitionedofproperty_sql
lockingproperty_sql
withdataproperty_sql
withsystemversioningproperty_sql
insert_sql
introducer_sql
kill_sql
pseudotype_sql
objectidentifier_sql
onconflict_sql
returning_sql
rowformatdelimitedproperty_sql
withtablehint_sql
indextablehint_sql
historicaldata_sql
table_parts
table_sql
tablefromrows_sql
tablesample_sql
pivot_sql
version_sql
tuple_sql
update_sql
values_sql
var_sql
into_sql
from_sql
groupingsets_sql
rollup_sql
rollupindex_sql
rollupproperty_sql
cube_sql
group_sql
having_sql
connect_sql
prior_sql
join_sql
lambda_sql
lateral_op
lateral_sql
limit_sql
offset_sql
setitem_sql
set_sql
queryband_sql
pragma_sql
lock_sql
literal_sql
escape_str
loaddata_sql
null_sql
boolean_sql
booland_sql
boolor_sql
order_sql
withfill_sql
cluster_sql
distribute_sql
sort_sql
ordered_sql
matchrecognizemeasure_sql
matchrecognize_sql
query_modifiers
options_modifier
for_modifiers
queryoption_sql
offset_limit_modifiers
after_limit_modifiers
select_sql
schema_sql
schema_columns_sql
star_sql
parameter_sql
sessionparameter_sql
placeholder_sql
subquery_sql
qualify_sql
unnest_sql
prewhere_sql
where_sql
window_sql
partition_by_sql
windowspec_sql
withingroup_sql
between_sql
bracket_offset_expressions
bracket_sql
all_sql
any_sql
exists_sql
case_sql
constraint_sql
nextvaluefor_sql
extract_sql
trim_sql
convert_concat_args
concat_sql
concatws_sql
check_sql
foreignkey_sql
primarykey_sql
if_sql
matchagainst_sql
jsonkeyvalue_sql
jsonpath_sql
json_path_part
formatjson_sql
formatphrase_sql
jsonarray_sql
jsonarrayagg_sql
jsoncolumndef_sql
jsonschema_sql
jsontable_sql
openjsoncolumndef_sql
openjson_sql
in_sql
in_unnest_op
interval_sql
return_sql
reference_sql
anonymous_sql
paren_sql
neg_sql
not_sql
alias_sql
pivotalias_sql
aliases_sql
atindex_sql
attimezone_sql
fromtimezone_sql
add_sql
and_sql
or_sql
xor_sql
connector_sql
bitwiseand_sql
bitwiseleftshift_sql
bitwisenot_sql
bitwiseor_sql
bitwiserightshift_sql
bitwisexor_sql
cast_sql
strtotime_sql
currentdate_sql
collate_sql
command_sql
comment_sql
mergetreettlaction_sql
mergetreettl_sql
transaction_sql
commit_sql
rollback_sql
altercolumn_sql
modifycolumn_sql
alterindex_sql
alterdiststyle_sql
altersortkey_sql
alterrename_sql
renamecolumn_sql
alterset_sql
alter_sql
altersession_sql
add_column_sql
droppartition_sql
dropprimarykey_sql
addconstraint_sql
addpartition_sql
distinct_sql
ignorenulls_sql
respectnulls_sql
havingmax_sql
intdiv_sql
dpipe_sql
div_sql
safedivide_sql
overlaps_sql
distance_sql
dot_sql
eq_sql
propertyeq_sql
escape_sql
glob_sql
gt_sql
gte_sql
is_sql
like_sql
ilike_sql
match_sql
similarto_sql
lt_sql
lte_sql
mod_sql
mul_sql
neq_sql
nullsafeeq_sql
nullsafeneq_sql
sub_sql
trycast_sql
jsoncast_sql
try_sql
log_sql
use_sql
binary
ceil_floor
function_fallback_sql
func
format_args
too_wide
format_time
expressions
op_expressions
naked_property
tag_sql
token_sql
userdefinedfunction_sql
joinhint_sql
kwarg_sql
when_sql
whens_sql
merge_sql
tochar_sql
tonumber_sql
dictproperty_sql
dictrange_sql
dictsubproperty_sql
duplicatekeyproperty_sql
uniquekeyproperty_sql
distributedbyproperty_sql
oncluster_sql
clusteredbyproperty_sql
anyvalue_sql
querytransform_sql
indexconstraintoption_sql
checkcolumnconstraint_sql
indexcolumnconstraint_sql
nvl2_sql
comprehension_sql
columnprefix_sql
opclass_sql
predict_sql
generateembedding_sql
generatetext_sql
generatetable_sql
generatebool_sql
generateint_sql
generatedouble_sql
mltranslate_sql
mlforecast_sql
aiforecast_sql
featuresattime_sql
vectorsearch_sql
forin_sql
refresh_sql
toarray_sql
tsordstotime_sql
tsordstotimestamp_sql
tsordstodatetime_sql
tsordstodate_sql
unixdate_sql
lastday_sql
dateadd_sql
arrayany_sql
struct_sql
partitionrange_sql
truncatetable_sql
convert_sql
copyparameter_sql
credentials_sql
copy_sql
semicolon_sql
datadeletionproperty_sql
maskingpolicycolumnconstraint_sql
gapfill_sql
scope_resolution
scoperesolution_sql
parsejson_sql
rand_sql
changes_sql
pad_sql
summarize_sql
explodinggenerateseries_sql
converttimezone_sql
json_sql
jsonvalue_sql
skipjsoncolumn_sql
conditionalinsert_sql
multitableinserts_sql
oncondition_sql
jsonextractquote_sql
jsonexists_sql
arrayagg_sql
slice_sql
apply_sql
grant_sql
revoke_sql
grantprivilege_sql
grantprincipal_sql
columns_sql
overlay_sql
todouble_sql
string_sql
median_sql
overflowtruncatebehavior_sql
unixseconds_sql
arraysize_sql
attach_sql
detach_sql
attachoption_sql
watermarkcolumnconstraint_sql
encodeproperty_sql
includeproperty_sql
xmlelement_sql
xmlkeyvalueoption_sql
partitionbyrangeproperty_sql
partitionbyrangepropertydynamic_sql
unpivotcolumns_sql
analyzesample_sql
analyzestatistics_sql
analyzehistogram_sql
analyzedelete_sql
analyzelistchainedrows_sql
analyzevalidate_sql
analyze_sql
xmltable_sql
xmlnamespace_sql
export_sql
declare_sql
declareitem_sql
recursivewithsearch_sql
parameterizedagg_sql
anonymousaggfunc_sql
combinedaggfunc_sql
combinedparameterizedagg_sql
show_sql
install_sql
get_put_sql
translatecharacters_sql
decodecase_sql
semanticview_sql
getextract_sql
datefromunixdate_sql
space_sql
buildproperty_sql
refreshtriggerproperty_sql
modelattribute_sql
directorystage_sql
uuid_sql
initcap_sql
localtime_sql
localtimestamp_sql
weekstart_sql
chr_sql
block_sql
storedprocedure_sql
ifblock_sql
whileblock_sql
execute_sql
executesql_sql
altermodifysqlsecurity_sql
usingproperty_sql
renameindex_sql
class MacroEvaluator:
156class MacroEvaluator:
157    """The class responsible for evaluating SQLMesh Macros/SQL.
158
159    SQLMesh supports special preprocessed SQL prefixed with `@`. Although it provides similar power to
160    traditional methods like string templating, there is semantic understanding of SQL which prevents
161    common errors like leading/trailing commas, syntax errors, etc.
162
163    SQLMesh SQL allows for macro variables and macro functions. Macro variables take the form of @variable. These are used for variable substitution.
164
165    SELECT * FROM foo WHERE ds BETWEEN @start_date AND @end_date
166
167    Macro variables can be defined with a special macro function.
168
169    @DEF(start_date, '2021-01-01')
170
171    Args:
172        dialect: Dialect of the SQL to evaluate.
173        python_env: Serialized Python environment.
174    """
175
176    def __init__(
177        self,
178        dialect: DialectType = "",
179        python_env: t.Optional[t.Dict[str, Executable]] = None,
180        schema: t.Optional[MappingSchema] = None,
181        runtime_stage: RuntimeStage = RuntimeStage.LOADING,
182        resolve_table: t.Optional[t.Callable[[str | exp.Table], str]] = None,
183        resolve_tables: t.Optional[t.Callable[[exp.Expr], exp.Expr]] = None,
184        snapshots: t.Optional[t.Dict[str, Snapshot]] = None,
185        default_catalog: t.Optional[str] = None,
186        path: t.Optional[Path] = None,
187        environment_naming_info: t.Optional[EnvironmentNamingInfo] = None,
188        model_fqn: t.Optional[str] = None,
189    ):
190        self.dialect = dialect
191        self.generator = MacroDialect().generator()
192        self.locals: t.Dict[str, t.Any] = {
193            "runtime_stage": runtime_stage.value,
194            "default_catalog": default_catalog,
195        }
196        self.env = {
197            **ENV,
198            "self": self,
199            "SQL": SQL,
200            "MacroEvaluator": MacroEvaluator,
201        }
202        self.python_env = python_env or {}
203        self.macros = {normalize_macro_name(k): v.func for k, v in macro.get_registry().items()}
204        self.columns_to_types_called = False
205        self.default_catalog = default_catalog
206
207        self._schema = schema
208        self._resolve_table = resolve_table
209        self._resolve_tables = resolve_tables
210        self._snapshots = snapshots if snapshots is not None else {}
211        self._path = path
212        self._environment_naming_info = environment_naming_info
213        self._model_fqn = model_fqn
214
215        prepare_env(self.python_env, self.env)
216        for k, v in self.python_env.items():
217            if v.is_definition:
218                self.macros[normalize_macro_name(k)] = self.env[v.name or k]
219            elif v.is_import and getattr(self.env.get(k), c.SQLMESH_MACRO, None):
220                self.macros[normalize_macro_name(k)] = self.env[k]
221            elif v.is_value:
222                value = self.env[k]
223                if k in (
224                    c.SQLMESH_VARS,
225                    c.SQLMESH_VARS_METADATA,
226                    c.SQLMESH_BLUEPRINT_VARS,
227                    c.SQLMESH_BLUEPRINT_VARS_METADATA,
228                ):
229                    value = {
230                        var_name: (
231                            self.parse_one(var_value.sql)
232                            if isinstance(var_value, SqlValue)
233                            else var_value
234                        )
235                        for var_name, var_value in value.items()
236                    }
237
238                self.locals[k] = value
239
240    def send(
241        self, name: str, *args: t.Any, **kwargs: t.Any
242    ) -> t.Union[None, exp.Expr, t.List[exp.Expr]]:
243        func = self.macros.get(normalize_macro_name(name))
244
245        if not callable(func):
246            raise MacroEvalError(f"Macro '{name}' does not exist.")
247
248        try:
249            return call_macro(
250                func, self.dialect, self._path, provided_args=(self, *args), provided_kwargs=kwargs
251            )  # type: ignore
252        except Exception as e:
253            raise MacroEvalError(
254                f"An error occurred during evaluation of '{name}'\n\n"
255                + format_evaluated_code_exception(e, self.python_env)
256            )
257
258    def transform(self, expression: exp.Expr) -> exp.Expr | t.List[exp.Expr] | None:
259        changed = False
260
261        def evaluate_macros(
262            node: exp.Expr,
263        ) -> exp.Expr | t.List[exp.Expr] | None:
264            nonlocal changed
265
266            if isinstance(node, MacroVar):
267                changed = True
268                variables = self.variables
269
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:
275                    if not isinstance(node.parent, StagedFilePath):
276                        raise SQLMeshError(f"Macro variable '{node.name}' is undefined.")
277
278                    return node
279
280                # Precedence order is locals (e.g. @DEF) > blueprint variables > config variables
281                value = self.locals.get(var_name, variables.get(var_name))
282                if isinstance(value, list):
283                    return exp.convert(
284                        tuple(self.transform(v) if isinstance(v, exp.Expr) else v for v in value)
285                    )
286
287                return exp.convert(self.transform(value) if isinstance(value, exp.Expr) else value)
288            if isinstance(node, exp.Identifier) and "@" in node.this:
289                text = self.template(node.this, {})
290                if node.this != text:
291                    changed = True
292                    return exp.to_identifier(text, quoted=node.quoted or None)
293            if isinstance(node, MacroFunc):
294                changed = True
295                return self.evaluate(node)
296            return node
297
298        transformed = exp.replace_tree(
299            expression.copy(),
300            evaluate_macros,  # type: ignore[arg-type]
301            prune=lambda n: isinstance(n, exp.Lambda),
302        )
303
304        if changed:
305            # the transformations could have corrupted the ast, turning this into sql and reparsing ensures
306            # that the ast is correct
307            if isinstance(transformed, list):
308                return [
309                    self.parse_one(node.sql(dialect=self.dialect, copy=False))
310                    for node in transformed
311                ]
312            if isinstance(transformed, exp.Expr):
313                return self.parse_one(transformed.sql(dialect=self.dialect, copy=False))
314
315        return transformed
316
317    def template(self, text: t.Any, local_variables: t.Dict[str, t.Any]) -> str:
318        """Substitute @vars with locals.
319
320        Args:
321            text: The string to do substitition on.
322            local_variables: Local variables in the context so that lambdas can be used.
323
324        Returns:
325           The rendered string.
326        """
327        # We try to convert all variables into sqlglot expressions because they're going to be converted
328        # into strings; in sql we don't convert strings because that would result in adding quotes
329        base_mapping = {
330            k.lower(): convert_sql(v, self.dialect)
331            for k, v in chain(self.variables.items(), self.locals.items(), local_variables.items())
332            if k.lower()
333            not in (
334                "engine_adapter",
335                "snapshot",
336            )
337        }
338        return MacroStrTemplate(str(text)).safe_substitute(CaseInsensitiveMapping(base_mapping))
339
340    def evaluate(self, node: MacroFunc) -> exp.Expr | t.List[exp.Expr] | None:
341        if isinstance(node, MacroDef):
342            if isinstance(node.expression, exp.Lambda):
343                _, fn = _norm_var_arg_lambda(self, node.expression)
344                self.macros[normalize_macro_name(node.name)] = lambda _, *args: fn(
345                    args[0] if len(args) == 1 else exp.Tuple(expressions=list(args))
346                )
347            else:
348                # Make variables defined through `@DEF` case-insensitive
349                self.locals[node.name.lower()] = self.transform(node.expression)
350
351            return node
352
353        if isinstance(node, (MacroSQL, MacroStrReplace)):
354            result: t.Optional[exp.Expr | t.List[exp.Expr]] = exp.convert(
355                self.eval_expression(node)
356            )
357        else:
358            func = t.cast(exp.Anonymous, node.this)
359
360            args = []
361            kwargs = {}
362            for e in func.expressions:
363                if isinstance(e, exp.PropertyEQ):
364                    kwargs[e.this.name] = e.expression
365                else:
366                    if kwargs:
367                        raise MacroEvalError(
368                            "Positional argument cannot follow keyword argument.\n  "
369                            f"{func.sql(dialect=self.dialect)} at '{self._path}'"
370                        )
371
372                    args.append(e)
373
374            result = self.send(func.name, *args, **kwargs)
375
376        if result is None:
377            return None
378
379        if isinstance(result, (tuple, list)):
380            result = [self.parse_one(item) for item in result if item is not None]
381
382            if (
383                len(result) == 1
384                and isinstance(result[0], (exp.Array, exp.Tuple))
385                and node.find_ancestor(MacroFunc)
386            ):
387                """
388                if:
389                 - the output of evaluating this node is being passed as an argument to another macro function
390                 - and that output is something that _norm_var_arg_lambda() will unpack into varargs
391                   > (a list containing a single item of type exp.Tuple/exp.Array)
392                then we will get inconsistent behaviour depending on if this node emits a list with a single item vs multiple items.
393                
394                In the first case, emitting a list containing a single array item will cause that array to get unpacked and its *members* passed to the calling macro
395                In the second case, emitting a list containing multiple array items will cause each item to get passed as-is to the calling macro
396                
397                To prevent this inconsistency, we wrap this node output in an exp.Array so that _norm_var_arg_lambda() can "unpack" that into the
398                actual argument we want to pass to the parent macro function
399                
400                Note we only do this for evaluation results that get passed as an argument to another macro, because when the final
401                result is given to something like SELECT, we still want that to be unpacked into a list of items like:
402                 - SELECT ARRAY(1), ARRAY(2)
403                rather than a single item like:
404                 - SELECT ARRAY(ARRAY(1), ARRAY(2))                
405                """
406                result = [exp.Array(expressions=result)]
407        else:
408            result = self.parse_one(result)
409
410        return result
411
412    def eval_expression(self, node: t.Any) -> t.Any:
413        """Converts a SQLGlot expression into executable Python code and evals it.
414
415        If the node is not an expression, it will simply be returned.
416
417        Args:
418            node: expression
419        Returns:
420            The return value of the evaled Python Code.
421        """
422        if not isinstance(node, exp.Expr):
423            return node
424        code = node.sql()
425        try:
426            code = self.generator.generate(node)
427            return eval(code, self.env, self.locals)
428        except Exception as e:
429            raise MacroEvalError(
430                f"Error trying to eval macro.\n\nGenerated code: {code}\n\nOriginal sql: {node}\n\n"
431                + format_evaluated_code_exception(e, self.python_env)
432            )
433
434    def parse_one(
435        self, sql: str | exp.Expr, into: t.Optional[exp.IntoType] = None, **opts: t.Any
436    ) -> exp.Expr:
437        """Parses the given SQL string and returns a syntax tree for the first
438        parsed SQL statement.
439
440        Args:
441            sql: the SQL code or expression to parse.
442            into: the Expression to parse into
443            **opts: other options
444
445        Returns:
446            Expression: the syntax tree for the first parsed statement
447        """
448        return sqlglot.maybe_parse(sql, dialect=self.dialect, into=into, **opts)
449
450    def columns_to_types(self, model_name: TableName | exp.Column) -> t.Dict[str, exp.DataType]:
451        """Returns the columns-to-types mapping corresponding to the specified model."""
452
453        # We only return this dummy schema at load time, because if we don't actually know the
454        # target model's schema at creation/evaluation time, returning a dummy schema could lead
455        # to unintelligible errors when the query is executed
456        if (self._schema is None or self._schema.empty) and self.runtime_stage == "loading":
457            self.columns_to_types_called = True
458            return {"__schema_unavailable_at_load__": exp.DataType.build("unknown")}
459
460        normalized_model_name = normalize_model_name(
461            model_name,
462            default_catalog=self.default_catalog,
463            dialect=self.dialect,
464        )
465        model_name = exp.to_table(normalized_model_name)
466
467        columns_to_types = (
468            self._schema.find(model_name, ensure_data_types=True) if self._schema else None
469        )
470        if columns_to_types is None:
471            snapshot = self.get_snapshot(model_name)
472            if snapshot and snapshot.node.is_model:
473                columns_to_types = snapshot.node.columns_to_types  # type: ignore
474
475        if columns_to_types is None:
476            raise SQLMeshError(f"Schema for model '{model_name}' can't be statically determined.")
477
478        return columns_to_types
479
480    def get_snapshot(self, model_name: TableName | exp.Column) -> t.Optional[Snapshot]:
481        """Returns the snapshot that corresponds to the given model name."""
482        return self._snapshots.get(
483            normalize_model_name(
484                model_name,
485                default_catalog=self.default_catalog,
486                dialect=self.dialect,
487            )
488        )
489
490    def resolve_table(self, table: str | exp.Table) -> str:
491        """Gets the physical table name for a given model."""
492        if not self._resolve_table:
493            raise SQLMeshError(
494                "Macro evaluator not properly initialized with resolve_table lambda."
495            )
496        return self._resolve_table(table)
497
498    def resolve_tables(self, query: exp.Expr) -> exp.Expr:
499        """Resolves queries with references to SQLMesh model names to their physical tables."""
500        if not self._resolve_tables:
501            raise SQLMeshError(
502                "Macro evaluator not properly initialized with resolve_tables lambda."
503            )
504        return self._resolve_tables(query)
505
506    @property
507    def runtime_stage(self) -> RuntimeStage:
508        """Returns the current runtime stage of the macro evaluation."""
509        return self.locals["runtime_stage"]
510
511    @property
512    def this_model(self) -> str:
513        """Returns the resolved name of the surrounding model."""
514        this_model = self.locals.get("this_model")
515        if not this_model:
516            raise SQLMeshError("Model name is not available in the macro evaluator.")
517        return this_model.sql(dialect=self.dialect, identify=True, comments=False)
518
519    @property
520    def this_model_fqn(self) -> str:
521        if self._model_fqn is None:
522            raise SQLMeshError("Model name is not available in the macro evaluator.")
523        return self._model_fqn
524
525    @property
526    def engine_adapter(self) -> EngineAdapter:
527        engine_adapter = self.locals.get("engine_adapter")
528        if not engine_adapter:
529            raise SQLMeshError(
530                "The engine adapter is not available while models are loading."
531                " You can gate these calls by checking in Python: evaluator.runtime_stage != 'loading' or SQL: @runtime_stage <> 'loading'."
532            )
533        return self.locals["engine_adapter"]
534
535    @property
536    def gateway(self) -> t.Optional[str]:
537        """Returns the gateway name."""
538        return self.var(c.GATEWAY)
539
540    @property
541    def snapshots(self) -> t.Dict[str, Snapshot]:
542        """Returns the snapshots if available."""
543        return self._snapshots
544
545    @property
546    def this_env(self) -> str:
547        """Returns the name of the current environment in before after all."""
548        if "this_env" not in self.locals:
549            raise SQLMeshError("Environment name is only available in before_all and after_all")
550        return self.locals["this_env"]
551
552    @property
553    def schemas(self) -> t.List[str]:
554        """Returns the schemas of the current environment in before after all macros."""
555        if "schemas" not in self.locals:
556            raise SQLMeshError("Schemas are only available in before_all and after_all")
557        return self.locals["schemas"]
558
559    @property
560    def views(self) -> t.List[str]:
561        """Returns the views of the current environment in before after all macros."""
562        if "views" not in self.locals:
563            raise SQLMeshError("Views are only available in before_all and after_all")
564        return self.locals["views"]
565
566    def var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
567        """Returns the value of the specified variable, or the default value if it doesn't exist."""
568        return {
569            **(self.locals.get(c.SQLMESH_VARS) or {}),
570            **(self.locals.get(c.SQLMESH_VARS_METADATA) or {}),
571        }.get(var_name.lower(), default)
572
573    def blueprint_var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
574        """Returns the value of the specified blueprint variable, or the default value if it doesn't exist."""
575        return {
576            **(self.locals.get(c.SQLMESH_BLUEPRINT_VARS) or {}),
577            **(self.locals.get(c.SQLMESH_BLUEPRINT_VARS_METADATA) or {}),
578        }.get(var_name.lower(), default)
579
580    @property
581    def variables(self) -> t.Dict[str, t.Any]:
582        return {
583            **self.locals.get(c.SQLMESH_VARS, {}),
584            **self.locals.get(c.SQLMESH_VARS_METADATA, {}),
585            **self.locals.get(c.SQLMESH_BLUEPRINT_VARS, {}),
586            **self.locals.get(c.SQLMESH_BLUEPRINT_VARS_METADATA, {}),
587        }
588
589    def _coerce(self, expr: exp.Expr, typ: t.Any, strict: bool = False) -> t.Any:
590        """Coerces the given expression to the specified type on a best-effort basis."""
591        return _coerce(expr, typ, self.dialect, self._path, strict)

The class responsible for evaluating SQLMesh Macros/SQL.

SQLMesh supports special preprocessed SQL prefixed with @. Although it provides similar power to traditional methods like string templating, there is semantic understanding of SQL which prevents common errors like leading/trailing commas, syntax errors, etc.

SQLMesh SQL allows for macro variables and macro functions. Macro variables take the form of @variable. These are used for variable substitution.

SELECT * FROM foo WHERE ds BETWEEN @start_date AND @end_date

Macro variables can be defined with a special macro function.

@DEF(start_date, '2021-01-01')

Arguments:
  • dialect: Dialect of the SQL to evaluate.
  • python_env: Serialized Python environment.
MacroEvaluator( dialect: Union[str, sqlglot.dialects.dialect.Dialect, type[sqlglot.dialects.dialect.Dialect], NoneType] = '', python_env: Optional[Dict[str, sqlmesh.utils.metaprogramming.Executable]] = None, schema: Optional[sqlglot.schema.MappingSchema] = None, runtime_stage: RuntimeStage = <RuntimeStage.LOADING: 'loading'>, resolve_table: Optional[Callable[[str | sqlglot.expressions.query.Table], str]] = None, resolve_tables: Optional[Callable[[sqlglot.expressions.core.Expr], sqlglot.expressions.core.Expr]] = None, snapshots: Optional[Dict[str, sqlmesh.core.snapshot.definition.Snapshot]] = None, default_catalog: Optional[str] = None, path: Optional[pathlib.Path] = None, environment_naming_info: Optional[sqlmesh.core.environment.EnvironmentNamingInfo] = None, model_fqn: Optional[str] = None)
176    def __init__(
177        self,
178        dialect: DialectType = "",
179        python_env: t.Optional[t.Dict[str, Executable]] = None,
180        schema: t.Optional[MappingSchema] = None,
181        runtime_stage: RuntimeStage = RuntimeStage.LOADING,
182        resolve_table: t.Optional[t.Callable[[str | exp.Table], str]] = None,
183        resolve_tables: t.Optional[t.Callable[[exp.Expr], exp.Expr]] = None,
184        snapshots: t.Optional[t.Dict[str, Snapshot]] = None,
185        default_catalog: t.Optional[str] = None,
186        path: t.Optional[Path] = None,
187        environment_naming_info: t.Optional[EnvironmentNamingInfo] = None,
188        model_fqn: t.Optional[str] = None,
189    ):
190        self.dialect = dialect
191        self.generator = MacroDialect().generator()
192        self.locals: t.Dict[str, t.Any] = {
193            "runtime_stage": runtime_stage.value,
194            "default_catalog": default_catalog,
195        }
196        self.env = {
197            **ENV,
198            "self": self,
199            "SQL": SQL,
200            "MacroEvaluator": MacroEvaluator,
201        }
202        self.python_env = python_env or {}
203        self.macros = {normalize_macro_name(k): v.func for k, v in macro.get_registry().items()}
204        self.columns_to_types_called = False
205        self.default_catalog = default_catalog
206
207        self._schema = schema
208        self._resolve_table = resolve_table
209        self._resolve_tables = resolve_tables
210        self._snapshots = snapshots if snapshots is not None else {}
211        self._path = path
212        self._environment_naming_info = environment_naming_info
213        self._model_fqn = model_fqn
214
215        prepare_env(self.python_env, self.env)
216        for k, v in self.python_env.items():
217            if v.is_definition:
218                self.macros[normalize_macro_name(k)] = self.env[v.name or k]
219            elif v.is_import and getattr(self.env.get(k), c.SQLMESH_MACRO, None):
220                self.macros[normalize_macro_name(k)] = self.env[k]
221            elif v.is_value:
222                value = self.env[k]
223                if k in (
224                    c.SQLMESH_VARS,
225                    c.SQLMESH_VARS_METADATA,
226                    c.SQLMESH_BLUEPRINT_VARS,
227                    c.SQLMESH_BLUEPRINT_VARS_METADATA,
228                ):
229                    value = {
230                        var_name: (
231                            self.parse_one(var_value.sql)
232                            if isinstance(var_value, SqlValue)
233                            else var_value
234                        )
235                        for var_name, var_value in value.items()
236                    }
237
238                self.locals[k] = value
dialect
generator
locals: Dict[str, Any]
env
python_env
macros
columns_to_types_called
default_catalog
def send( self, name: str, *args: Any, **kwargs: Any) -> Union[NoneType, sqlglot.expressions.core.Expr, List[sqlglot.expressions.core.Expr]]:
240    def send(
241        self, name: str, *args: t.Any, **kwargs: t.Any
242    ) -> t.Union[None, exp.Expr, t.List[exp.Expr]]:
243        func = self.macros.get(normalize_macro_name(name))
244
245        if not callable(func):
246            raise MacroEvalError(f"Macro '{name}' does not exist.")
247
248        try:
249            return call_macro(
250                func, self.dialect, self._path, provided_args=(self, *args), provided_kwargs=kwargs
251            )  # type: ignore
252        except Exception as e:
253            raise MacroEvalError(
254                f"An error occurred during evaluation of '{name}'\n\n"
255                + format_evaluated_code_exception(e, self.python_env)
256            )
def transform( self, expression: sqlglot.expressions.core.Expr) -> Union[sqlglot.expressions.core.Expr, List[sqlglot.expressions.core.Expr], NoneType]:
258    def transform(self, expression: exp.Expr) -> exp.Expr | t.List[exp.Expr] | None:
259        changed = False
260
261        def evaluate_macros(
262            node: exp.Expr,
263        ) -> exp.Expr | t.List[exp.Expr] | None:
264            nonlocal changed
265
266            if isinstance(node, MacroVar):
267                changed = True
268                variables = self.variables
269
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:
275                    if not isinstance(node.parent, StagedFilePath):
276                        raise SQLMeshError(f"Macro variable '{node.name}' is undefined.")
277
278                    return node
279
280                # Precedence order is locals (e.g. @DEF) > blueprint variables > config variables
281                value = self.locals.get(var_name, variables.get(var_name))
282                if isinstance(value, list):
283                    return exp.convert(
284                        tuple(self.transform(v) if isinstance(v, exp.Expr) else v for v in value)
285                    )
286
287                return exp.convert(self.transform(value) if isinstance(value, exp.Expr) else value)
288            if isinstance(node, exp.Identifier) and "@" in node.this:
289                text = self.template(node.this, {})
290                if node.this != text:
291                    changed = True
292                    return exp.to_identifier(text, quoted=node.quoted or None)
293            if isinstance(node, MacroFunc):
294                changed = True
295                return self.evaluate(node)
296            return node
297
298        transformed = exp.replace_tree(
299            expression.copy(),
300            evaluate_macros,  # type: ignore[arg-type]
301            prune=lambda n: isinstance(n, exp.Lambda),
302        )
303
304        if changed:
305            # the transformations could have corrupted the ast, turning this into sql and reparsing ensures
306            # that the ast is correct
307            if isinstance(transformed, list):
308                return [
309                    self.parse_one(node.sql(dialect=self.dialect, copy=False))
310                    for node in transformed
311                ]
312            if isinstance(transformed, exp.Expr):
313                return self.parse_one(transformed.sql(dialect=self.dialect, copy=False))
314
315        return transformed
def template(self, text: Any, local_variables: Dict[str, Any]) -> str:
317    def template(self, text: t.Any, local_variables: t.Dict[str, t.Any]) -> str:
318        """Substitute @vars with locals.
319
320        Args:
321            text: The string to do substitition on.
322            local_variables: Local variables in the context so that lambdas can be used.
323
324        Returns:
325           The rendered string.
326        """
327        # We try to convert all variables into sqlglot expressions because they're going to be converted
328        # into strings; in sql we don't convert strings because that would result in adding quotes
329        base_mapping = {
330            k.lower(): convert_sql(v, self.dialect)
331            for k, v in chain(self.variables.items(), self.locals.items(), local_variables.items())
332            if k.lower()
333            not in (
334                "engine_adapter",
335                "snapshot",
336            )
337        }
338        return MacroStrTemplate(str(text)).safe_substitute(CaseInsensitiveMapping(base_mapping))

Substitute @vars with locals.

Arguments:
  • text: The string to do substitition on.
  • local_variables: Local variables in the context so that lambdas can be used.
Returns:

The rendered string.

def evaluate( self, node: sqlmesh.core.dialect.MacroFunc) -> Union[sqlglot.expressions.core.Expr, List[sqlglot.expressions.core.Expr], NoneType]:
340    def evaluate(self, node: MacroFunc) -> exp.Expr | t.List[exp.Expr] | None:
341        if isinstance(node, MacroDef):
342            if isinstance(node.expression, exp.Lambda):
343                _, fn = _norm_var_arg_lambda(self, node.expression)
344                self.macros[normalize_macro_name(node.name)] = lambda _, *args: fn(
345                    args[0] if len(args) == 1 else exp.Tuple(expressions=list(args))
346                )
347            else:
348                # Make variables defined through `@DEF` case-insensitive
349                self.locals[node.name.lower()] = self.transform(node.expression)
350
351            return node
352
353        if isinstance(node, (MacroSQL, MacroStrReplace)):
354            result: t.Optional[exp.Expr | t.List[exp.Expr]] = exp.convert(
355                self.eval_expression(node)
356            )
357        else:
358            func = t.cast(exp.Anonymous, node.this)
359
360            args = []
361            kwargs = {}
362            for e in func.expressions:
363                if isinstance(e, exp.PropertyEQ):
364                    kwargs[e.this.name] = e.expression
365                else:
366                    if kwargs:
367                        raise MacroEvalError(
368                            "Positional argument cannot follow keyword argument.\n  "
369                            f"{func.sql(dialect=self.dialect)} at '{self._path}'"
370                        )
371
372                    args.append(e)
373
374            result = self.send(func.name, *args, **kwargs)
375
376        if result is None:
377            return None
378
379        if isinstance(result, (tuple, list)):
380            result = [self.parse_one(item) for item in result if item is not None]
381
382            if (
383                len(result) == 1
384                and isinstance(result[0], (exp.Array, exp.Tuple))
385                and node.find_ancestor(MacroFunc)
386            ):
387                """
388                if:
389                 - the output of evaluating this node is being passed as an argument to another macro function
390                 - and that output is something that _norm_var_arg_lambda() will unpack into varargs
391                   > (a list containing a single item of type exp.Tuple/exp.Array)
392                then we will get inconsistent behaviour depending on if this node emits a list with a single item vs multiple items.
393                
394                In the first case, emitting a list containing a single array item will cause that array to get unpacked and its *members* passed to the calling macro
395                In the second case, emitting a list containing multiple array items will cause each item to get passed as-is to the calling macro
396                
397                To prevent this inconsistency, we wrap this node output in an exp.Array so that _norm_var_arg_lambda() can "unpack" that into the
398                actual argument we want to pass to the parent macro function
399                
400                Note we only do this for evaluation results that get passed as an argument to another macro, because when the final
401                result is given to something like SELECT, we still want that to be unpacked into a list of items like:
402                 - SELECT ARRAY(1), ARRAY(2)
403                rather than a single item like:
404                 - SELECT ARRAY(ARRAY(1), ARRAY(2))                
405                """
406                result = [exp.Array(expressions=result)]
407        else:
408            result = self.parse_one(result)
409
410        return result
def eval_expression(self, node: Any) -> Any:
412    def eval_expression(self, node: t.Any) -> t.Any:
413        """Converts a SQLGlot expression into executable Python code and evals it.
414
415        If the node is not an expression, it will simply be returned.
416
417        Args:
418            node: expression
419        Returns:
420            The return value of the evaled Python Code.
421        """
422        if not isinstance(node, exp.Expr):
423            return node
424        code = node.sql()
425        try:
426            code = self.generator.generate(node)
427            return eval(code, self.env, self.locals)
428        except Exception as e:
429            raise MacroEvalError(
430                f"Error trying to eval macro.\n\nGenerated code: {code}\n\nOriginal sql: {node}\n\n"
431                + format_evaluated_code_exception(e, self.python_env)
432            )

Converts a SQLGlot expression into executable Python code and evals it.

If the node is not an expression, it will simply be returned.

Arguments:
  • node: expression
Returns:

The return value of the evaled Python Code.

def parse_one( self, sql: str | sqlglot.expressions.core.Expr, into: Union[type[sqlglot.expressions.core.Expr], collections.abc.Collection[type[sqlglot.expressions.core.Expr]], NoneType] = None, **opts: Any) -> sqlglot.expressions.core.Expr:
434    def parse_one(
435        self, sql: str | exp.Expr, into: t.Optional[exp.IntoType] = None, **opts: t.Any
436    ) -> exp.Expr:
437        """Parses the given SQL string and returns a syntax tree for the first
438        parsed SQL statement.
439
440        Args:
441            sql: the SQL code or expression to parse.
442            into: the Expression to parse into
443            **opts: other options
444
445        Returns:
446            Expression: the syntax tree for the first parsed statement
447        """
448        return sqlglot.maybe_parse(sql, dialect=self.dialect, into=into, **opts)

Parses the given SQL string and returns a syntax tree for the first parsed SQL statement.

Arguments:
  • sql: the SQL code or expression to parse.
  • into: the Expression to parse into
  • **opts: other options
Returns:

Expression: the syntax tree for the first parsed statement

def columns_to_types( self, model_name: Union[str, sqlglot.expressions.query.Table, sqlglot.expressions.core.Column]) -> Dict[str, sqlglot.expressions.datatypes.DataType]:
450    def columns_to_types(self, model_name: TableName | exp.Column) -> t.Dict[str, exp.DataType]:
451        """Returns the columns-to-types mapping corresponding to the specified model."""
452
453        # We only return this dummy schema at load time, because if we don't actually know the
454        # target model's schema at creation/evaluation time, returning a dummy schema could lead
455        # to unintelligible errors when the query is executed
456        if (self._schema is None or self._schema.empty) and self.runtime_stage == "loading":
457            self.columns_to_types_called = True
458            return {"__schema_unavailable_at_load__": exp.DataType.build("unknown")}
459
460        normalized_model_name = normalize_model_name(
461            model_name,
462            default_catalog=self.default_catalog,
463            dialect=self.dialect,
464        )
465        model_name = exp.to_table(normalized_model_name)
466
467        columns_to_types = (
468            self._schema.find(model_name, ensure_data_types=True) if self._schema else None
469        )
470        if columns_to_types is None:
471            snapshot = self.get_snapshot(model_name)
472            if snapshot and snapshot.node.is_model:
473                columns_to_types = snapshot.node.columns_to_types  # type: ignore
474
475        if columns_to_types is None:
476            raise SQLMeshError(f"Schema for model '{model_name}' can't be statically determined.")
477
478        return columns_to_types

Returns the columns-to-types mapping corresponding to the specified model.

def get_snapshot( self, model_name: Union[str, sqlglot.expressions.query.Table, sqlglot.expressions.core.Column]) -> Optional[sqlmesh.core.snapshot.definition.Snapshot]:
480    def get_snapshot(self, model_name: TableName | exp.Column) -> t.Optional[Snapshot]:
481        """Returns the snapshot that corresponds to the given model name."""
482        return self._snapshots.get(
483            normalize_model_name(
484                model_name,
485                default_catalog=self.default_catalog,
486                dialect=self.dialect,
487            )
488        )

Returns the snapshot that corresponds to the given model name.

def resolve_table(self, table: str | sqlglot.expressions.query.Table) -> str:
490    def resolve_table(self, table: str | exp.Table) -> str:
491        """Gets the physical table name for a given model."""
492        if not self._resolve_table:
493            raise SQLMeshError(
494                "Macro evaluator not properly initialized with resolve_table lambda."
495            )
496        return self._resolve_table(table)

Gets the physical table name for a given model.

def resolve_tables( self, query: sqlglot.expressions.core.Expr) -> sqlglot.expressions.core.Expr:
498    def resolve_tables(self, query: exp.Expr) -> exp.Expr:
499        """Resolves queries with references to SQLMesh model names to their physical tables."""
500        if not self._resolve_tables:
501            raise SQLMeshError(
502                "Macro evaluator not properly initialized with resolve_tables lambda."
503            )
504        return self._resolve_tables(query)

Resolves queries with references to SQLMesh model names to their physical tables.

runtime_stage: RuntimeStage
506    @property
507    def runtime_stage(self) -> RuntimeStage:
508        """Returns the current runtime stage of the macro evaluation."""
509        return self.locals["runtime_stage"]

Returns the current runtime stage of the macro evaluation.

this_model: str
511    @property
512    def this_model(self) -> str:
513        """Returns the resolved name of the surrounding model."""
514        this_model = self.locals.get("this_model")
515        if not this_model:
516            raise SQLMeshError("Model name is not available in the macro evaluator.")
517        return this_model.sql(dialect=self.dialect, identify=True, comments=False)

Returns the resolved name of the surrounding model.

this_model_fqn: str
519    @property
520    def this_model_fqn(self) -> str:
521        if self._model_fqn is None:
522            raise SQLMeshError("Model name is not available in the macro evaluator.")
523        return self._model_fqn
525    @property
526    def engine_adapter(self) -> EngineAdapter:
527        engine_adapter = self.locals.get("engine_adapter")
528        if not engine_adapter:
529            raise SQLMeshError(
530                "The engine adapter is not available while models are loading."
531                " You can gate these calls by checking in Python: evaluator.runtime_stage != 'loading' or SQL: @runtime_stage <> 'loading'."
532            )
533        return self.locals["engine_adapter"]
gateway: Optional[str]
535    @property
536    def gateway(self) -> t.Optional[str]:
537        """Returns the gateway name."""
538        return self.var(c.GATEWAY)

Returns the gateway name.

snapshots: Dict[str, sqlmesh.core.snapshot.definition.Snapshot]
540    @property
541    def snapshots(self) -> t.Dict[str, Snapshot]:
542        """Returns the snapshots if available."""
543        return self._snapshots

Returns the snapshots if available.

this_env: str
545    @property
546    def this_env(self) -> str:
547        """Returns the name of the current environment in before after all."""
548        if "this_env" not in self.locals:
549            raise SQLMeshError("Environment name is only available in before_all and after_all")
550        return self.locals["this_env"]

Returns the name of the current environment in before after all.

schemas: List[str]
552    @property
553    def schemas(self) -> t.List[str]:
554        """Returns the schemas of the current environment in before after all macros."""
555        if "schemas" not in self.locals:
556            raise SQLMeshError("Schemas are only available in before_all and after_all")
557        return self.locals["schemas"]

Returns the schemas of the current environment in before after all macros.

views: List[str]
559    @property
560    def views(self) -> t.List[str]:
561        """Returns the views of the current environment in before after all macros."""
562        if "views" not in self.locals:
563            raise SQLMeshError("Views are only available in before_all and after_all")
564        return self.locals["views"]

Returns the views of the current environment in before after all macros.

def var(self, var_name: str, default: Optional[Any] = None) -> Optional[Any]:
566    def var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
567        """Returns the value of the specified variable, or the default value if it doesn't exist."""
568        return {
569            **(self.locals.get(c.SQLMESH_VARS) or {}),
570            **(self.locals.get(c.SQLMESH_VARS_METADATA) or {}),
571        }.get(var_name.lower(), default)

Returns the value of the specified variable, or the default value if it doesn't exist.

def blueprint_var(self, var_name: str, default: Optional[Any] = None) -> Optional[Any]:
573    def blueprint_var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
574        """Returns the value of the specified blueprint variable, or the default value if it doesn't exist."""
575        return {
576            **(self.locals.get(c.SQLMESH_BLUEPRINT_VARS) or {}),
577            **(self.locals.get(c.SQLMESH_BLUEPRINT_VARS_METADATA) or {}),
578        }.get(var_name.lower(), default)

Returns the value of the specified blueprint variable, or the default value if it doesn't exist.

variables: Dict[str, Any]
580    @property
581    def variables(self) -> t.Dict[str, t.Any]:
582        return {
583            **self.locals.get(c.SQLMESH_VARS, {}),
584            **self.locals.get(c.SQLMESH_VARS_METADATA, {}),
585            **self.locals.get(c.SQLMESH_BLUEPRINT_VARS, {}),
586            **self.locals.get(c.SQLMESH_BLUEPRINT_VARS_METADATA, {}),
587        }
class macro(sqlmesh.utils.registry_decorator):
594class macro(registry_decorator):
595    """Specifies a function is a macro and registers it the global MACROS registry.
596
597    Registered macros can be referenced in SQL statements to make queries more dynamic/cleaner.
598
599    Example:
600        from sqlglot import exp
601        from sqlmesh.core.macros import MacroEvaluator, macro
602
603        @macro()
604        def add_one(evaluator: MacroEvaluator, column: exp.Literal) -> exp.Add:
605            return evaluator.parse_one(f"{column} + 1")
606
607    Args:
608        name: A custom name for the macro, the default is the name of the function.
609    """
610
611    registry_name = "macros"
612
613    def __init__(self, *args: t.Any, metadata_only: bool = False, **kwargs: t.Any) -> None:
614        super().__init__(*args, **kwargs)
615        self.metadata_only = metadata_only
616
617    def __call__(
618        self, func: t.Callable[..., DECORATOR_RETURN_TYPE]
619    ) -> t.Callable[..., DECORATOR_RETURN_TYPE]:
620        if self.metadata_only:
621            setattr(func, c.SQLMESH_METADATA, self.metadata_only)
622        wrapper = super().__call__(func)
623
624        # This is used to identify macros at runtime to unwrap during serialization.
625        setattr(wrapper, c.SQLMESH_MACRO, True)
626        return wrapper

Specifies a function is a macro and registers it the global MACROS registry.

Registered macros can be referenced in SQL statements to make queries more dynamic/cleaner.

Example:

from sqlglot import exp from sqlmesh.core.macros import MacroEvaluator, macro

@macro() def add_one(evaluator: MacroEvaluator, column: exp.Literal) -> exp.Add: return evaluator.parse_one(f"{column} + 1")

Arguments:
  • name: A custom name for the macro, the default is the name of the function.
macro(*args: Any, metadata_only: bool = False, **kwargs: Any)
613    def __init__(self, *args: t.Any, metadata_only: bool = False, **kwargs: t.Any) -> None:
614        super().__init__(*args, **kwargs)
615        self.metadata_only = metadata_only
registry_name = 'macros'
metadata_only
ExecutableOrMacro = typing.Union[sqlmesh.utils.metaprogramming.Executable, macro]
@macro()
def each(evaluator: MacroEvaluator, *args: Any) -> List[Any]:
696@macro()
697def each(
698    evaluator: MacroEvaluator,
699    *args: t.Any,
700) -> t.List[t.Any]:
701    """Iterates through items calling func on each.
702
703    If a func call on item returns None, it will be excluded from the list.
704
705    Args:
706        evaluator: MacroEvaluator that invoked the macro
707        args: The last argument should be a lambda of the form x -> x +1. The first argument can be
708            an Array or var args can be used.
709
710    Returns:
711        A list of items that is the result of func
712    """
713    *items, func = args
714    items, func = _norm_var_arg_lambda(evaluator, func, *items)  # type: ignore
715    return [item for item in map(func, ensure_collection(items)) if item is not None]

Iterates through items calling func on each.

If a func call on item returns None, it will be excluded from the list.

Arguments:
  • evaluator: MacroEvaluator that invoked the macro
  • args: The last argument should be a lambda of the form x -> x +1. The first argument can be an Array or var args can be used.
Returns:

A list of items that is the result of func

@macro('IF')
def if_( evaluator: MacroEvaluator, condition: Any, true: Any, false: Any = None) -> Any:
718@macro("IF")
719def if_(
720    evaluator: MacroEvaluator,
721    condition: t.Any,
722    true: t.Any,
723    false: t.Any = None,
724) -> t.Any:
725    """Evaluates a given condition and returns the second argument if true or else the third argument.
726
727    If false is not passed in, the default return value will be None.
728
729    Example:
730        >>> from sqlglot import parse_one
731        >>> from sqlmesh.core.macros import MacroEvaluator
732        >>> MacroEvaluator().transform(parse_one("@IF('a' = 1, a, b)")).sql()
733        'b'
734
735        >>> MacroEvaluator().transform(parse_one("@IF('a' = 1, a)"))
736    """
737
738    if evaluator.eval_expression(condition):
739        return true
740    return false

Evaluates a given condition and returns the second argument if true or else the third argument.

If false is not passed in, the default return value will be None.

Example:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> MacroEvaluator().transform(parse_one("@IF('a' = 1, a, b)")).sql()
'b'
>>> MacroEvaluator().transform(parse_one("@IF('a' = 1, a)"))
@macro('REDUCE')
def reduce_(evaluator: MacroEvaluator, *args: Any) -> Any:
743@macro("REDUCE")
744def reduce_(evaluator: MacroEvaluator, *args: t.Any) -> t.Any:
745    """Iterates through items applying provided function that takes two arguments
746    cumulatively to the items of iterable items, from left to right, so as to reduce
747    the iterable to a single item.
748
749    Example:
750        >>> from sqlglot import parse_one
751        >>> from sqlmesh.core.macros import MacroEvaluator
752        >>> sql = "@SQL(@REDUCE([100, 200, 300, 400], (x, y) -> x + y))"
753        >>> MacroEvaluator().transform(parse_one(sql)).sql()
754        '1000'
755
756    Args:
757        evaluator: MacroEvaluator that invoked the macro
758        args: The last argument should be a lambda of the form (x, y) -> x + y. The first argument can be
759            an Array or var args can be used.
760    Returns:
761        A single item that is the result of applying func cumulatively to items
762    """
763    *items, func = args
764    items, func = _norm_var_arg_lambda(evaluator, func, *items)  # type: ignore
765    return reduce(lambda a, b: func(exp.Tuple(expressions=[a, b])), ensure_collection(items))

Iterates through items applying provided function that takes two arguments cumulatively to the items of iterable items, from left to right, so as to reduce the iterable to a single item.

Example:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@SQL(@REDUCE([100, 200, 300, 400], (x, y) -> x + y))"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'1000'
Arguments:
  • evaluator: MacroEvaluator that invoked the macro
  • args: The last argument should be a lambda of the form (x, y) -> x + y. The first argument can be an Array or var args can be used.
Returns:

A single item that is the result of applying func cumulatively to items

@macro('FILTER')
def filter_(evaluator: MacroEvaluator, *args: Any) -> List[Any]:
768@macro("FILTER")
769def filter_(evaluator: MacroEvaluator, *args: t.Any) -> t.List[t.Any]:
770    """Iterates through items, applying provided function to each item and removing
771    all items where the function returns False
772
773    Example:
774        >>> from sqlglot import parse_one
775        >>> from sqlmesh.core.macros import MacroEvaluator
776        >>> sql = "@REDUCE(@FILTER([1, 2, 3], x -> x > 1), (x, y) -> x + y)"
777        >>> MacroEvaluator().transform(parse_one(sql)).sql()
778        '2 + 3'
779
780        >>> sql = "@EVAL(@REDUCE(@FILTER([1, 2, 3], x -> x > 1), (x, y) -> x + y))"
781        >>> MacroEvaluator().transform(parse_one(sql)).sql()
782        '5'
783
784    Args:
785        evaluator: MacroEvaluator that invoked the macro
786        args: The last argument should be a lambda of the form x -> x > 1. The first argument can be
787            an Array or var args can be used.
788    Returns:
789        The items for which the func returned True
790    """
791    *items, func = args
792    items, func = _norm_var_arg_lambda(evaluator, func, *items)  # type: ignore
793    return list(filter(lambda arg: evaluator.eval_expression(func(arg)), items))

Iterates through items, applying provided function to each item and removing all items where the function returns False

Example:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@REDUCE(@FILTER([1, 2, 3], x -> x > 1), (x, y) -> x + y)"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'2 + 3'
>>> sql = "@EVAL(@REDUCE(@FILTER([1, 2, 3], x -> x > 1), (x, y) -> x + y))"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'5'
Arguments:
  • evaluator: MacroEvaluator that invoked the macro
  • args: The last argument should be a lambda of the form x -> x > 1. The first argument can be an Array or var args can be used.
Returns:

The items for which the func returned True

def with_( evaluator: MacroEvaluator, condition: sqlglot.expressions.core.Condition, expression: sqlglot.expressions.core.Expr) -> Optional[sqlglot.expressions.core.Expr]:
796def _optional_expression(
797    evaluator: MacroEvaluator,
798    condition: exp.Condition,
799    expression: exp.Expr,
800) -> t.Optional[exp.Expr]:
801    """Inserts expression when the condition is True
802
803    The following examples express the usage of this function in the context of the macros which wrap it.
804
805    Examples:
806        >>> from sqlglot import parse_one
807        >>> from sqlmesh.core.macros import MacroEvaluator
808        >>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
809        >>> MacroEvaluator().transform(parse_one(sql)).sql()
810        'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
811        >>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
812        >>> MacroEvaluator().transform(parse_one(sql)).sql()
813        'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
814        >>> sql = "select * from city @GROUP_BY(True) country, population"
815        >>> MacroEvaluator().transform(parse_one(sql)).sql()
816        'SELECT * FROM city GROUP BY country, population'
817        >>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
818        >>> MacroEvaluator().transform(parse_one(sql)).sql()
819        "SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
820
821    Args:
822        evaluator: MacroEvaluator that invoked the macro
823        condition: Condition expression
824        expression: SQL expression
825    Returns:
826        Expression if the conditional is True; otherwise None
827    """
828    return expression if evaluator.eval_expression(condition) else None

Inserts expression when the condition is True

The following examples express the usage of this function in the context of the macros which wrap it.

Examples:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
>>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
>>> sql = "select * from city @GROUP_BY(True) country, population"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city GROUP BY country, population'
>>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
"SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
Arguments:
  • evaluator: MacroEvaluator that invoked the macro
  • condition: Condition expression
  • expression: SQL expression
Returns:

Expression if the conditional is True; otherwise None

def join( evaluator: MacroEvaluator, condition: sqlglot.expressions.core.Condition, expression: sqlglot.expressions.core.Expr) -> Optional[sqlglot.expressions.core.Expr]:
796def _optional_expression(
797    evaluator: MacroEvaluator,
798    condition: exp.Condition,
799    expression: exp.Expr,
800) -> t.Optional[exp.Expr]:
801    """Inserts expression when the condition is True
802
803    The following examples express the usage of this function in the context of the macros which wrap it.
804
805    Examples:
806        >>> from sqlglot import parse_one
807        >>> from sqlmesh.core.macros import MacroEvaluator
808        >>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
809        >>> MacroEvaluator().transform(parse_one(sql)).sql()
810        'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
811        >>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
812        >>> MacroEvaluator().transform(parse_one(sql)).sql()
813        'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
814        >>> sql = "select * from city @GROUP_BY(True) country, population"
815        >>> MacroEvaluator().transform(parse_one(sql)).sql()
816        'SELECT * FROM city GROUP BY country, population'
817        >>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
818        >>> MacroEvaluator().transform(parse_one(sql)).sql()
819        "SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
820
821    Args:
822        evaluator: MacroEvaluator that invoked the macro
823        condition: Condition expression
824        expression: SQL expression
825    Returns:
826        Expression if the conditional is True; otherwise None
827    """
828    return expression if evaluator.eval_expression(condition) else None

Inserts expression when the condition is True

The following examples express the usage of this function in the context of the macros which wrap it.

Examples:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
>>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
>>> sql = "select * from city @GROUP_BY(True) country, population"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city GROUP BY country, population'
>>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
"SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
Arguments:
  • evaluator: MacroEvaluator that invoked the macro
  • condition: Condition expression
  • expression: SQL expression
Returns:

Expression if the conditional is True; otherwise None

def where( evaluator: MacroEvaluator, condition: sqlglot.expressions.core.Condition, expression: sqlglot.expressions.core.Expr) -> Optional[sqlglot.expressions.core.Expr]:
796def _optional_expression(
797    evaluator: MacroEvaluator,
798    condition: exp.Condition,
799    expression: exp.Expr,
800) -> t.Optional[exp.Expr]:
801    """Inserts expression when the condition is True
802
803    The following examples express the usage of this function in the context of the macros which wrap it.
804
805    Examples:
806        >>> from sqlglot import parse_one
807        >>> from sqlmesh.core.macros import MacroEvaluator
808        >>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
809        >>> MacroEvaluator().transform(parse_one(sql)).sql()
810        'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
811        >>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
812        >>> MacroEvaluator().transform(parse_one(sql)).sql()
813        'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
814        >>> sql = "select * from city @GROUP_BY(True) country, population"
815        >>> MacroEvaluator().transform(parse_one(sql)).sql()
816        'SELECT * FROM city GROUP BY country, population'
817        >>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
818        >>> MacroEvaluator().transform(parse_one(sql)).sql()
819        "SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
820
821    Args:
822        evaluator: MacroEvaluator that invoked the macro
823        condition: Condition expression
824        expression: SQL expression
825    Returns:
826        Expression if the conditional is True; otherwise None
827    """
828    return expression if evaluator.eval_expression(condition) else None

Inserts expression when the condition is True

The following examples express the usage of this function in the context of the macros which wrap it.

Examples:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
>>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
>>> sql = "select * from city @GROUP_BY(True) country, population"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city GROUP BY country, population'
>>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
"SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
Arguments:
  • evaluator: MacroEvaluator that invoked the macro
  • condition: Condition expression
  • expression: SQL expression
Returns:

Expression if the conditional is True; otherwise None

def group_by( evaluator: MacroEvaluator, condition: sqlglot.expressions.core.Condition, expression: sqlglot.expressions.core.Expr) -> Optional[sqlglot.expressions.core.Expr]:
796def _optional_expression(
797    evaluator: MacroEvaluator,
798    condition: exp.Condition,
799    expression: exp.Expr,
800) -> t.Optional[exp.Expr]:
801    """Inserts expression when the condition is True
802
803    The following examples express the usage of this function in the context of the macros which wrap it.
804
805    Examples:
806        >>> from sqlglot import parse_one
807        >>> from sqlmesh.core.macros import MacroEvaluator
808        >>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
809        >>> MacroEvaluator().transform(parse_one(sql)).sql()
810        'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
811        >>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
812        >>> MacroEvaluator().transform(parse_one(sql)).sql()
813        'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
814        >>> sql = "select * from city @GROUP_BY(True) country, population"
815        >>> MacroEvaluator().transform(parse_one(sql)).sql()
816        'SELECT * FROM city GROUP BY country, population'
817        >>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
818        >>> MacroEvaluator().transform(parse_one(sql)).sql()
819        "SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
820
821    Args:
822        evaluator: MacroEvaluator that invoked the macro
823        condition: Condition expression
824        expression: SQL expression
825    Returns:
826        Expression if the conditional is True; otherwise None
827    """
828    return expression if evaluator.eval_expression(condition) else None

Inserts expression when the condition is True

The following examples express the usage of this function in the context of the macros which wrap it.

Examples:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
>>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
>>> sql = "select * from city @GROUP_BY(True) country, population"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city GROUP BY country, population'
>>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
"SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
Arguments:
  • evaluator: MacroEvaluator that invoked the macro
  • condition: Condition expression
  • expression: SQL expression
Returns:

Expression if the conditional is True; otherwise None

def having( evaluator: MacroEvaluator, condition: sqlglot.expressions.core.Condition, expression: sqlglot.expressions.core.Expr) -> Optional[sqlglot.expressions.core.Expr]:
796def _optional_expression(
797    evaluator: MacroEvaluator,
798    condition: exp.Condition,
799    expression: exp.Expr,
800) -> t.Optional[exp.Expr]:
801    """Inserts expression when the condition is True
802
803    The following examples express the usage of this function in the context of the macros which wrap it.
804
805    Examples:
806        >>> from sqlglot import parse_one
807        >>> from sqlmesh.core.macros import MacroEvaluator
808        >>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
809        >>> MacroEvaluator().transform(parse_one(sql)).sql()
810        'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
811        >>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
812        >>> MacroEvaluator().transform(parse_one(sql)).sql()
813        'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
814        >>> sql = "select * from city @GROUP_BY(True) country, population"
815        >>> MacroEvaluator().transform(parse_one(sql)).sql()
816        'SELECT * FROM city GROUP BY country, population'
817        >>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
818        >>> MacroEvaluator().transform(parse_one(sql)).sql()
819        "SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
820
821    Args:
822        evaluator: MacroEvaluator that invoked the macro
823        condition: Condition expression
824        expression: SQL expression
825    Returns:
826        Expression if the conditional is True; otherwise None
827    """
828    return expression if evaluator.eval_expression(condition) else None

Inserts expression when the condition is True

The following examples express the usage of this function in the context of the macros which wrap it.

Examples:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
>>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
>>> sql = "select * from city @GROUP_BY(True) country, population"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city GROUP BY country, population'
>>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
"SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
Arguments:
  • evaluator: MacroEvaluator that invoked the macro
  • condition: Condition expression
  • expression: SQL expression
Returns:

Expression if the conditional is True; otherwise None

def order_by( evaluator: MacroEvaluator, condition: sqlglot.expressions.core.Condition, expression: sqlglot.expressions.core.Expr) -> Optional[sqlglot.expressions.core.Expr]:
796def _optional_expression(
797    evaluator: MacroEvaluator,
798    condition: exp.Condition,
799    expression: exp.Expr,
800) -> t.Optional[exp.Expr]:
801    """Inserts expression when the condition is True
802
803    The following examples express the usage of this function in the context of the macros which wrap it.
804
805    Examples:
806        >>> from sqlglot import parse_one
807        >>> from sqlmesh.core.macros import MacroEvaluator
808        >>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
809        >>> MacroEvaluator().transform(parse_one(sql)).sql()
810        'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
811        >>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
812        >>> MacroEvaluator().transform(parse_one(sql)).sql()
813        'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
814        >>> sql = "select * from city @GROUP_BY(True) country, population"
815        >>> MacroEvaluator().transform(parse_one(sql)).sql()
816        'SELECT * FROM city GROUP BY country, population'
817        >>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
818        >>> MacroEvaluator().transform(parse_one(sql)).sql()
819        "SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
820
821    Args:
822        evaluator: MacroEvaluator that invoked the macro
823        condition: Condition expression
824        expression: SQL expression
825    Returns:
826        Expression if the conditional is True; otherwise None
827    """
828    return expression if evaluator.eval_expression(condition) else None

Inserts expression when the condition is True

The following examples express the usage of this function in the context of the macros which wrap it.

Examples:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
>>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
>>> sql = "select * from city @GROUP_BY(True) country, population"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city GROUP BY country, population'
>>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
"SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
Arguments:
  • evaluator: MacroEvaluator that invoked the macro
  • condition: Condition expression
  • expression: SQL expression
Returns:

Expression if the conditional is True; otherwise None

def limit( evaluator: MacroEvaluator, condition: sqlglot.expressions.core.Condition, expression: sqlglot.expressions.core.Expr) -> Optional[sqlglot.expressions.core.Expr]:
796def _optional_expression(
797    evaluator: MacroEvaluator,
798    condition: exp.Condition,
799    expression: exp.Expr,
800) -> t.Optional[exp.Expr]:
801    """Inserts expression when the condition is True
802
803    The following examples express the usage of this function in the context of the macros which wrap it.
804
805    Examples:
806        >>> from sqlglot import parse_one
807        >>> from sqlmesh.core.macros import MacroEvaluator
808        >>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
809        >>> MacroEvaluator().transform(parse_one(sql)).sql()
810        'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
811        >>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
812        >>> MacroEvaluator().transform(parse_one(sql)).sql()
813        'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
814        >>> sql = "select * from city @GROUP_BY(True) country, population"
815        >>> MacroEvaluator().transform(parse_one(sql)).sql()
816        'SELECT * FROM city GROUP BY country, population'
817        >>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
818        >>> MacroEvaluator().transform(parse_one(sql)).sql()
819        "SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
820
821    Args:
822        evaluator: MacroEvaluator that invoked the macro
823        condition: Condition expression
824        expression: SQL expression
825    Returns:
826        Expression if the conditional is True; otherwise None
827    """
828    return expression if evaluator.eval_expression(condition) else None

Inserts expression when the condition is True

The following examples express the usage of this function in the context of the macros which wrap it.

Examples:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@WITH(True) all_cities as (select * from city) select all_cities"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'WITH all_cities AS (SELECT * FROM city) SELECT all_cities'
>>> sql = "select * from city left outer @JOIN(True) country on city.country = country.name"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city LEFT OUTER JOIN country ON city.country = country.name'
>>> sql = "select * from city @GROUP_BY(True) country, population"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM city GROUP BY country, population'
>>> sql = "select * from city group by country @HAVING(True) population > 100 and country = 'Mexico'"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
"SELECT * FROM city GROUP BY country HAVING population > 100 AND country = 'Mexico'"
Arguments:
  • evaluator: MacroEvaluator that invoked the macro
  • condition: Condition expression
  • expression: SQL expression
Returns:

Expression if the conditional is True; otherwise None

@macro('eval')
def eval_( evaluator: MacroEvaluator, condition: sqlglot.expressions.core.Condition) -> Any:
840@macro("eval")
841def eval_(evaluator: MacroEvaluator, condition: exp.Condition) -> t.Any:
842    """Evaluate the given condition in a Python/SQL interpretor.
843
844    Example:
845        >>> from sqlglot import parse_one
846        >>> from sqlmesh.core.macros import MacroEvaluator
847        >>> sql = "@EVAL(1 + 1)"
848        >>> MacroEvaluator().transform(parse_one(sql)).sql()
849        '2'
850    """
851    return evaluator.eval_expression(condition)

Evaluate the given condition in a Python/SQL interpretor.

Example:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@EVAL(1 + 1)"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'2'
@macro()
def star( evaluator: MacroEvaluator, relation: sqlglot.expressions.query.Table, alias: sqlglot.expressions.core.Column = Column( this=Identifier(this='', quoted=True)), exclude: Union[sqlglot.expressions.array.Array, sqlglot.expressions.query.Tuple] = Tuple(), prefix: sqlglot.expressions.core.Literal = Literal(this='', is_string=True), suffix: sqlglot.expressions.core.Literal = Literal(this='', is_string=True), quote_identifiers: sqlglot.expressions.core.Boolean = Boolean(this=True), except_: Union[sqlglot.expressions.array.Array, sqlglot.expressions.query.Tuple] = Tuple()) -> List[sqlglot.expressions.core.Expr]:
855@macro()
856def star(
857    evaluator: MacroEvaluator,
858    relation: exp.Table,
859    alias: exp.Column = t.cast(exp.Column, exp.column("")),
860    exclude: t.Union[exp.Array, exp.Tuple] = exp.Tuple(expressions=[]),
861    prefix: exp.Literal = exp.Literal.string(""),
862    suffix: exp.Literal = exp.Literal.string(""),
863    quote_identifiers: exp.Boolean = exp.true(),
864    except_: t.Union[exp.Array, exp.Tuple] = exp.Tuple(expressions=[]),
865) -> t.List[exp.Expr]:
866    """Returns a list of projections for the given relation.
867
868    Args:
869        evaluator: MacroEvaluator that invoked the macro
870        relation: The relation to select star from
871        alias: The alias of the relation
872        exclude: Columns to exclude
873        prefix: A prefix to use for all selections
874        suffix: A suffix to use for all selections
875        quote_identifiers: Whether or not quote the resulting aliases, defaults to true
876        except_: Alias for exclude (TODO: deprecate this, update docs)
877
878    Returns:
879        An array of columns.
880
881    Example:
882        >>> from sqlglot import parse_one, exp
883        >>> from sqlglot.schema import MappingSchema
884        >>> from sqlmesh.core.macros import MacroEvaluator
885        >>> sql = "SELECT @STAR(foo, bar, exclude := [c], prefix := 'baz_') FROM foo AS bar"
886        >>> MacroEvaluator(schema=MappingSchema({"foo": {"a": exp.DataType.build("string"), "b": exp.DataType.build("string"), "c": exp.DataType.build("string"), "d": exp.DataType.build("int")}})).transform(parse_one(sql)).sql()
887        'SELECT CAST("bar"."a" AS TEXT) AS "baz_a", CAST("bar"."b" AS TEXT) AS "baz_b", CAST("bar"."d" AS INT) AS "baz_d" FROM foo AS bar'
888    """
889    if alias and not isinstance(alias, (exp.Identifier, exp.Column)):
890        raise SQLMeshError(f"Invalid alias '{alias}'. Expected an identifier.")
891    if exclude and not isinstance(exclude, (exp.Array, exp.Tuple)):
892        raise SQLMeshError(f"Invalid exclude '{exclude}'. Expected an array.")
893    if except_ != exp.tuple_():
894        from sqlmesh.core.console import get_console
895
896        get_console().log_warning(
897            "The 'except_' argument in @STAR will soon be deprecated. Use 'exclude' instead."
898        )
899        if not isinstance(exclude, (exp.Array, exp.Tuple)):
900            raise SQLMeshError(f"Invalid exclude_ '{exclude}'. Expected an array.")
901    if prefix and not isinstance(prefix, exp.Literal):
902        raise SQLMeshError(f"Invalid prefix '{prefix}'. Expected a literal.")
903    if suffix and not isinstance(suffix, exp.Literal):
904        raise SQLMeshError(f"Invalid suffix '{suffix}'. Expected a literal.")
905    if not isinstance(quote_identifiers, exp.Boolean):
906        raise SQLMeshError(f"Invalid quote_identifiers '{quote_identifiers}'. Expected a boolean.")
907
908    excluded_names = {
909        normalize_identifiers(excluded, dialect=evaluator.dialect).name
910        for excluded in exclude.expressions or except_.expressions
911    }
912    quoted = quote_identifiers.this
913    table_identifier = normalize_identifiers(
914        alias if alias.name else relation, dialect=evaluator.dialect
915    ).name
916
917    columns_to_types = {
918        k: v for k, v in evaluator.columns_to_types(relation).items() if k not in excluded_names
919    }
920    if columns_to_types_all_known(columns_to_types):
921        return [
922            exp.cast(
923                exp.column(column, table=table_identifier, quoted=quoted),
924                dtype,
925                dialect=evaluator.dialect,
926            ).as_(f"{prefix.this}{column}{suffix.this}", quoted=quoted)
927            for column, dtype in columns_to_types.items()
928        ]
929    return [
930        exp.column(column, table=table_identifier, quoted=quoted).as_(
931            f"{prefix.this}{column}{suffix.this}", quoted=quoted
932        )
933        for column, type_ in columns_to_types.items()
934    ]

Returns a list of projections for the given relation.

Arguments:
  • evaluator: MacroEvaluator that invoked the macro
  • relation: The relation to select star from
  • alias: The alias of the relation
  • exclude: Columns to exclude
  • prefix: A prefix to use for all selections
  • suffix: A suffix to use for all selections
  • quote_identifiers: Whether or not quote the resulting aliases, defaults to true
  • except_: Alias for exclude (TODO: deprecate this, update docs)
Returns:

An array of columns.

Example:
>>> from sqlglot import parse_one, exp
>>> from sqlglot.schema import MappingSchema
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "SELECT @STAR(foo, bar, exclude := [c], prefix := 'baz_') FROM foo AS bar"
>>> MacroEvaluator(schema=MappingSchema({"foo": {"a": exp.DataType.build("string"), "b": exp.DataType.build("string"), "c": exp.DataType.build("string"), "d": exp.DataType.build("int")}})).transform(parse_one(sql)).sql()
'SELECT CAST("bar"."a" AS TEXT) AS "baz_a", CAST("bar"."b" AS TEXT) AS "baz_b", CAST("bar"."d" AS INT) AS "baz_d" FROM foo AS bar'
@macro()
def generate_surrogate_key( evaluator: MacroEvaluator, *fields: sqlglot.expressions.core.Expr, hash_function: sqlglot.expressions.core.Literal = Literal(this='MD5', is_string=True)) -> sqlglot.expressions.core.Func:
937@macro()
938def generate_surrogate_key(
939    evaluator: MacroEvaluator,
940    *fields: exp.Expr,
941    hash_function: exp.Literal = exp.Literal.string("MD5"),
942) -> exp.Func:
943    """Generates a surrogate key (string) for the given fields.
944
945    Example:
946        >>> from sqlglot import parse_one
947        >>> from sqlmesh.core.macros import MacroEvaluator
948        >>>
949        >>> sql = "SELECT @GENERATE_SURROGATE_KEY(a, b, c) FROM foo"
950        >>> MacroEvaluator(dialect="bigquery").transform(parse_one(sql, dialect="bigquery")).sql("bigquery")
951        "SELECT TO_HEX(MD5(CONCAT(COALESCE(CAST(a AS STRING), '_sqlmesh_surrogate_key_null_'), '|', COALESCE(CAST(b AS STRING), '_sqlmesh_surrogate_key_null_'), '|', COALESCE(CAST(c AS STRING), '_sqlmesh_surrogate_key_null_')))) FROM foo"
952        >>>
953        >>> sql = "SELECT @GENERATE_SURROGATE_KEY(a, b, c, hash_function := 'SHA256') FROM foo"
954        >>> MacroEvaluator(dialect="bigquery").transform(parse_one(sql, dialect="bigquery")).sql("bigquery")
955        "SELECT SHA256(CONCAT(COALESCE(CAST(a AS STRING), '_sqlmesh_surrogate_key_null_'), '|', COALESCE(CAST(b AS STRING), '_sqlmesh_surrogate_key_null_'), '|', COALESCE(CAST(c AS STRING), '_sqlmesh_surrogate_key_null_'))) FROM foo"
956    """
957    string_fields: t.List[exp.Expr] = []
958    for i, field in enumerate(fields):
959        if i > 0:
960            string_fields.append(exp.Literal.string("|"))
961        string_fields.append(
962            exp.func(
963                "COALESCE",
964                exp.cast(field, exp.DataType.build("text")),
965                exp.Literal.string("_sqlmesh_surrogate_key_null_"),
966            )
967        )
968
969    func = exp.func(
970        hash_function.name,
971        exp.func("CONCAT", *string_fields),
972        dialect=evaluator.dialect,
973    )
974    if isinstance(func, exp.MD5Digest):
975        func = exp.MD5(this=func.this)
976
977    return func

Generates a surrogate key (string) for the given fields.

Example:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>>
>>> sql = "SELECT @GENERATE_SURROGATE_KEY(a, b, c) FROM foo"
>>> MacroEvaluator(dialect="bigquery").transform(parse_one(sql, dialect="bigquery")).sql("bigquery")
"SELECT TO_HEX(MD5(CONCAT(COALESCE(CAST(a AS STRING), '_sqlmesh_surrogate_key_null_'), '|', COALESCE(CAST(b AS STRING), '_sqlmesh_surrogate_key_null_'), '|', COALESCE(CAST(c AS STRING), '_sqlmesh_surrogate_key_null_')))) FROM foo"
>>>
>>> sql = "SELECT @GENERATE_SURROGATE_KEY(a, b, c, hash_function := 'SHA256') FROM foo"
>>> MacroEvaluator(dialect="bigquery").transform(parse_one(sql, dialect="bigquery")).sql("bigquery")
"SELECT SHA256(CONCAT(COALESCE(CAST(a AS STRING), '_sqlmesh_surrogate_key_null_'), '|', COALESCE(CAST(b AS STRING), '_sqlmesh_surrogate_key_null_'), '|', COALESCE(CAST(c AS STRING), '_sqlmesh_surrogate_key_null_'))) FROM foo"
@macro()
def safe_add( _: MacroEvaluator, *fields: sqlglot.expressions.core.Expr) -> sqlglot.expressions.functions.Case:
980@macro()
981def safe_add(_: MacroEvaluator, *fields: exp.Expr) -> exp.Case:
982    """Adds numbers together, substitutes nulls for 0s and only returns null if all fields are null.
983
984    Example:
985        >>> from sqlglot import parse_one
986        >>> from sqlmesh.core.macros import MacroEvaluator
987        >>> sql = "SELECT @SAFE_ADD(a, b) FROM foo"
988        >>> MacroEvaluator().transform(parse_one(sql)).sql()
989        'SELECT CASE WHEN a IS NULL AND b IS NULL THEN NULL ELSE COALESCE(a, 0) + COALESCE(b, 0) END FROM foo'
990    """
991    return (
992        exp.Case()
993        .when(exp.and_(*(field.is_(exp.null()) for field in fields)), exp.null())
994        .else_(reduce(lambda a, b: a + b, [exp.func("COALESCE", field, 0) for field in fields]))  # type: ignore
995    )

Adds numbers together, substitutes nulls for 0s and only returns null if all fields are null.

Example:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "SELECT @SAFE_ADD(a, b) FROM foo"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT CASE WHEN a IS NULL AND b IS NULL THEN NULL ELSE COALESCE(a, 0) + COALESCE(b, 0) END FROM foo'
@macro()
def safe_sub( _: MacroEvaluator, *fields: sqlglot.expressions.core.Expr) -> sqlglot.expressions.functions.Case:
 998@macro()
 999def safe_sub(_: MacroEvaluator, *fields: exp.Expr) -> exp.Case:
1000    """Subtract numbers, substitutes nulls for 0s and only returns null if all fields are null.
1001
1002    Example:
1003        >>> from sqlglot import parse_one
1004        >>> from sqlmesh.core.macros import MacroEvaluator
1005        >>> sql = "SELECT @SAFE_SUB(a, b) FROM foo"
1006        >>> MacroEvaluator().transform(parse_one(sql)).sql()
1007        'SELECT CASE WHEN a IS NULL AND b IS NULL THEN NULL ELSE COALESCE(a, 0) - COALESCE(b, 0) END FROM foo'
1008    """
1009    return (
1010        exp.Case()
1011        .when(exp.and_(*(field.is_(exp.null()) for field in fields)), exp.null())
1012        .else_(reduce(lambda a, b: a - b, [exp.func("COALESCE", field, 0) for field in fields]))  # type: ignore
1013    )

Subtract numbers, substitutes nulls for 0s and only returns null if all fields are null.

Example:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "SELECT @SAFE_SUB(a, b) FROM foo"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT CASE WHEN a IS NULL AND b IS NULL THEN NULL ELSE COALESCE(a, 0) - COALESCE(b, 0) END FROM foo'
@macro()
def safe_div( _: MacroEvaluator, numerator: sqlglot.expressions.core.Expr, denominator: sqlglot.expressions.core.Expr) -> sqlglot.expressions.core.Div:
1016@macro()
1017def safe_div(_: MacroEvaluator, numerator: exp.Expr, denominator: exp.Expr) -> exp.Div:
1018    """Divides numbers, returns null if the denominator is 0.
1019
1020    Example:
1021        >>> from sqlglot import parse_one
1022        >>> from sqlmesh.core.macros import MacroEvaluator
1023        >>> sql = "SELECT @SAFE_DIV(a, b) FROM foo"
1024        >>> MacroEvaluator().transform(parse_one(sql)).sql()
1025        'SELECT a / NULLIF(b, 0) FROM foo'
1026    """
1027    return numerator / exp.func("NULLIF", denominator, 0)

Divides numbers, returns null if the denominator is 0.

Example:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "SELECT @SAFE_DIV(a, b) FROM foo"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT a / NULLIF(b, 0) FROM foo'
@macro()
def union( evaluator: MacroEvaluator, *args: sqlglot.expressions.core.Expr) -> sqlglot.expressions.query.Query:
1030@macro()
1031def union(
1032    evaluator: MacroEvaluator,
1033    *args: exp.Expr,
1034) -> exp.Query:
1035    """Returns a UNION of the given tables. Only choosing columns that have the same name and type.
1036
1037    Args:
1038        evaluator: MacroEvaluator that invoked the macro
1039        args: Variable arguments that can be:
1040            - First argument can be a condition (exp.Condition)
1041            - A union type ('ALL' or 'DISTINCT') as exp.Literal
1042            - Tables (exp.Table)
1043
1044    Example:
1045        >>> from sqlglot import parse_one
1046        >>> from sqlglot.schema import MappingSchema
1047        >>> from sqlmesh.core.macros import MacroEvaluator
1048        >>> sql = "@UNION('distinct', foo, bar)"
1049        >>> MacroEvaluator(schema=MappingSchema({"foo": {"a": "int", "b": "string", "c": "string"}, "bar": {"c": "string", "a": "int", "b": "int"}})).transform(parse_one(sql)).sql()
1050        'SELECT CAST(a AS INT) AS a, CAST(c AS TEXT) AS c FROM foo UNION SELECT CAST(a AS INT) AS a, CAST(c AS TEXT) AS c FROM bar'
1051        >>> sql = "@UNION(True, 'distinct', foo, bar)"
1052        >>> MacroEvaluator(schema=MappingSchema({"foo": {"a": "int", "b": "string", "c": "string"}, "bar": {"c": "string", "a": "int", "b": "int"}})).transform(parse_one(sql)).sql()
1053        'SELECT CAST(a AS INT) AS a, CAST(c AS TEXT) AS c FROM foo UNION SELECT CAST(a AS INT) AS a, CAST(c AS TEXT) AS c FROM bar'
1054    """
1055
1056    if not args:
1057        raise SQLMeshError("At least one table is required for the @UNION macro.")
1058
1059    arg_idx = 0
1060    # Check for condition
1061    condition = evaluator.eval_expression(args[arg_idx])
1062    if isinstance(condition, bool):
1063        arg_idx += 1
1064        if arg_idx >= len(args):
1065            raise SQLMeshError("Expected more arguments after the condition of the `@UNION` macro.")
1066
1067    # Check for union type
1068    type_ = exp.Literal.string("ALL")
1069    if isinstance(args[arg_idx], exp.Literal):
1070        type_ = args[arg_idx]  # type: ignore
1071        arg_idx += 1
1072    kind = type_.name.upper()
1073    if kind not in ("ALL", "DISTINCT"):
1074        raise SQLMeshError(f"Invalid type '{type_}'. Expected 'ALL' or 'DISTINCT'.")
1075
1076    # Remaining args should be tables
1077    tables = [
1078        exp.to_table(e.sql(evaluator.dialect), dialect=evaluator.dialect) for e in args[arg_idx:]
1079    ]
1080
1081    columns = {
1082        column
1083        for column, _ in reduce(
1084            lambda a, b: a & b,  # type: ignore
1085            (evaluator.columns_to_types(table).items() for table in tables),
1086        )
1087    }
1088
1089    projections = [
1090        exp.cast(column, type_, dialect=evaluator.dialect).as_(column)
1091        for column, type_ in evaluator.columns_to_types(tables[0]).items()
1092        if column in columns
1093    ]
1094
1095    # Skip the union if condition is False
1096    if condition == False:
1097        return exp.select(*projections).from_(tables[0])
1098
1099    return reduce(
1100        lambda a, b: a.union(b, distinct=kind == "DISTINCT"),  # type: ignore
1101        [exp.select(*projections).from_(t) for t in tables],
1102    )

Returns a UNION of the given tables. Only choosing columns that have the same name and type.

Arguments:
  • evaluator: MacroEvaluator that invoked the macro
  • args: Variable arguments that can be:
    • First argument can be a condition (exp.Condition)
    • A union type ('ALL' or 'DISTINCT') as exp.Literal
    • Tables (exp.Table)
Example:
>>> from sqlglot import parse_one
>>> from sqlglot.schema import MappingSchema
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@UNION('distinct', foo, bar)"
>>> MacroEvaluator(schema=MappingSchema({"foo": {"a": "int", "b": "string", "c": "string"}, "bar": {"c": "string", "a": "int", "b": "int"}})).transform(parse_one(sql)).sql()
'SELECT CAST(a AS INT) AS a, CAST(c AS TEXT) AS c FROM foo UNION SELECT CAST(a AS INT) AS a, CAST(c AS TEXT) AS c FROM bar'
>>> sql = "@UNION(True, 'distinct', foo, bar)"
>>> MacroEvaluator(schema=MappingSchema({"foo": {"a": "int", "b": "string", "c": "string"}, "bar": {"c": "string", "a": "int", "b": "int"}})).transform(parse_one(sql)).sql()
'SELECT CAST(a AS INT) AS a, CAST(c AS TEXT) AS c FROM foo UNION SELECT CAST(a AS INT) AS a, CAST(c AS TEXT) AS c FROM bar'
@macro()
def haversine_distance( _: MacroEvaluator, lat1: sqlglot.expressions.core.Expr, lon1: sqlglot.expressions.core.Expr, lat2: sqlglot.expressions.core.Expr, lon2: sqlglot.expressions.core.Expr, unit: sqlglot.expressions.core.Literal = Literal(this='mi', is_string=True)) -> sqlglot.expressions.core.Mul:
1105@macro()
1106def haversine_distance(
1107    _: MacroEvaluator,
1108    lat1: exp.Expr,
1109    lon1: exp.Expr,
1110    lat2: exp.Expr,
1111    lon2: exp.Expr,
1112    unit: exp.Literal = exp.Literal.string("mi"),
1113) -> exp.Mul:
1114    """Returns the haversine distance between two points.
1115
1116    Example:
1117        >>> from sqlglot import parse_one
1118        >>> from sqlmesh.core.macros import MacroEvaluator
1119        >>> sql = "SELECT @HAVERSINE_DISTANCE(driver_y, driver_x, passenger_y, passenger_x, 'mi') FROM rides"
1120        >>> MacroEvaluator().transform(parse_one(sql)).sql()
1121        'SELECT 7922 * ASIN(SQRT((POWER(SIN(RADIANS((passenger_y - driver_y) / 2)), 2)) + (COS(RADIANS(driver_y)) * COS(RADIANS(passenger_y)) * POWER(SIN(RADIANS((passenger_x - driver_x) / 2)), 2)))) * 1.0 FROM rides'
1122    """
1123    if unit.this == "mi":
1124        conversion_rate = 1.0
1125    elif unit.this == "km":
1126        conversion_rate = 1.60934
1127    else:
1128        raise SQLMeshError(f"Invalid unit '{unit}'. Expected 'mi' or 'km'.")
1129
1130    return (
1131        2
1132        * 3961
1133        * exp.func(
1134            "ASIN",
1135            exp.func(
1136                "SQRT",
1137                exp.func("POWER", exp.func("SIN", exp.func("RADIANS", (lat2 - lat1) / 2)), 2)
1138                + exp.func("COS", exp.func("RADIANS", lat1))
1139                * exp.func("COS", exp.func("RADIANS", lat2))
1140                * exp.func("POWER", exp.func("SIN", exp.func("RADIANS", (lon2 - lon1) / 2)), 2),
1141            ),
1142        )
1143        * conversion_rate
1144    )

Returns the haversine distance between two points.

Example:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "SELECT @HAVERSINE_DISTANCE(driver_y, driver_x, passenger_y, passenger_x, 'mi') FROM rides"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT 7922 * ASIN(SQRT((POWER(SIN(RADIANS((passenger_y - driver_y) / 2)), 2)) + (COS(RADIANS(driver_y)) * COS(RADIANS(passenger_y)) * POWER(SIN(RADIANS((passenger_x - driver_x) / 2)), 2)))) * 1.0 FROM rides'
@macro()
def pivot( evaluator: MacroEvaluator, column: SQL, values: List[sqlglot.expressions.core.Expr], alias: bool = True, agg: sqlglot.expressions.core.Expr = Literal(this='SUM', is_string=True), cmp: sqlglot.expressions.core.Expr = Literal(this='=', is_string=True), prefix: sqlglot.expressions.core.Expr = Literal(this='', is_string=True), suffix: sqlglot.expressions.core.Expr = Literal(this='', is_string=True), then_value: SQL = '1', else_value: SQL = '0', quote: bool = True, distinct: bool = False) -> List[sqlglot.expressions.core.Expr]:
1147@macro()
1148def pivot(
1149    evaluator: MacroEvaluator,
1150    column: SQL,
1151    values: t.List[exp.Expr],
1152    alias: bool = True,
1153    agg: exp.Expr = exp.Literal.string("SUM"),
1154    cmp: exp.Expr = exp.Literal.string("="),
1155    prefix: exp.Expr = exp.Literal.string(""),
1156    suffix: exp.Expr = exp.Literal.string(""),
1157    then_value: SQL = SQL("1"),
1158    else_value: SQL = SQL("0"),
1159    quote: bool = True,
1160    distinct: bool = False,
1161) -> t.List[exp.Expr]:
1162    """Returns a list of projections as a result of pivoting the given column on the given values.
1163
1164    Example:
1165        >>> from sqlglot import parse_one
1166        >>> from sqlmesh.core.macros import MacroEvaluator
1167        >>> sql = "SELECT date_day, @PIVOT(status, ['cancelled', 'completed']) FROM rides GROUP BY 1"
1168        >>> MacroEvaluator().transform(parse_one(sql)).sql()
1169        'SELECT date_day, SUM(CASE WHEN status = \\'cancelled\\' THEN 1 ELSE 0 END) AS "cancelled", SUM(CASE WHEN status = \\'completed\\' THEN 1 ELSE 0 END) AS "completed" FROM rides GROUP BY 1'
1170        >>> sql = "SELECT @PIVOT(a, ['v'], then_value := tv, suffix := '_sfx', quote := FALSE)"
1171        >>> MacroEvaluator(dialect="bigquery").transform(parse_one(sql)).sql("bigquery")
1172        "SELECT SUM(CASE WHEN a = 'v' THEN tv ELSE 0 END) AS v_sfx"
1173    """
1174    aggregates: t.List[exp.Expr] = []
1175    for value in values:
1176        proj = f"{agg.name}("
1177        if distinct:
1178            proj += "DISTINCT "
1179
1180        proj += f"CASE WHEN {column} {cmp.name} {value.sql(evaluator.dialect)} THEN {then_value} ELSE {else_value} END) "
1181        node: exp.Expr = evaluator.parse_one(proj)
1182
1183        if alias:
1184            node = node.as_(
1185                f"{prefix.name}{value.name}{suffix.name}",
1186                quoted=quote,
1187                copy=False,
1188                dialect=evaluator.dialect,
1189            )
1190
1191        aggregates.append(node)
1192
1193    return aggregates

Returns a list of projections as a result of pivoting the given column on the given values.

Example:
>>> from sqlglot import parse_one
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "SELECT date_day, @PIVOT(status, ['cancelled', 'completed']) FROM rides GROUP BY 1"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT date_day, SUM(CASE WHEN status = \'cancelled\' THEN 1 ELSE 0 END) AS "cancelled", SUM(CASE WHEN status = \'completed\' THEN 1 ELSE 0 END) AS "completed" FROM rides GROUP BY 1'
>>> sql = "SELECT @PIVOT(a, ['v'], then_value := tv, suffix := '_sfx', quote := FALSE)"
>>> MacroEvaluator(dialect="bigquery").transform(parse_one(sql)).sql("bigquery")
"SELECT SUM(CASE WHEN a = 'v' THEN tv ELSE 0 END) AS v_sfx"
@macro('AND')
def and_( evaluator: MacroEvaluator, *expressions: Optional[sqlglot.expressions.core.Expr]) -> sqlglot.expressions.core.Condition:
1196@macro("AND")
1197def and_(evaluator: MacroEvaluator, *expressions: t.Optional[exp.Expr]) -> exp.Condition:
1198    """Returns an AND statement filtering out any NULL expressions."""
1199    conditions = [e for e in expressions if not isinstance(e, exp.Null)]
1200
1201    if not conditions:
1202        return exp.true()
1203
1204    return exp.and_(*conditions, dialect=evaluator.dialect)

Returns an AND statement filtering out any NULL expressions.

@macro('OR')
def or_( evaluator: MacroEvaluator, *expressions: Optional[sqlglot.expressions.core.Expr]) -> sqlglot.expressions.core.Condition:
1207@macro("OR")
1208def or_(evaluator: MacroEvaluator, *expressions: t.Optional[exp.Expr]) -> exp.Condition:
1209    """Returns an OR statement filtering out any NULL expressions."""
1210    conditions = [e for e in expressions if not isinstance(e, exp.Null)]
1211
1212    if not conditions:
1213        return exp.true()
1214
1215    return exp.or_(*conditions, dialect=evaluator.dialect)

Returns an OR statement filtering out any NULL expressions.

@macro('VAR')
def var( evaluator: MacroEvaluator, var_name: sqlglot.expressions.core.Expr, default: Optional[sqlglot.expressions.core.Expr] = None) -> sqlglot.expressions.core.Expr:
1218@macro("VAR")
1219def var(
1220    evaluator: MacroEvaluator, var_name: exp.Expr, default: t.Optional[exp.Expr] = None
1221) -> exp.Expr:
1222    """Returns the value of a variable or the default value if the variable is not set."""
1223    if not var_name.is_string:
1224        raise SQLMeshError(f"Invalid variable name '{var_name.sql()}'. Expected a string literal.")
1225
1226    return exp.convert(evaluator.var(var_name.this, default))

Returns the value of a variable or the default value if the variable is not set.

@macro('BLUEPRINT_VAR')
def blueprint_var( evaluator: MacroEvaluator, var_name: sqlglot.expressions.core.Expr, default: Optional[sqlglot.expressions.core.Expr] = None) -> sqlglot.expressions.core.Expr:
1229@macro("BLUEPRINT_VAR")
1230def blueprint_var(
1231    evaluator: MacroEvaluator, var_name: exp.Expr, default: t.Optional[exp.Expr] = None
1232) -> exp.Expr:
1233    """Returns the value of a blueprint variable or the default value if the variable is not set."""
1234    if not var_name.is_string:
1235        raise SQLMeshError(
1236            f"Invalid blueprint variable name '{var_name.sql()}'. Expected a string literal."
1237        )
1238
1239    return exp.convert(evaluator.blueprint_var(var_name.this, default))

Returns the value of a blueprint variable or the default value if the variable is not set.

@macro()
def deduplicate( evaluator: MacroEvaluator, relation: sqlglot.expressions.core.Expr, partition_by: List[sqlglot.expressions.core.Expr], order_by: List[str]) -> sqlglot.expressions.query.Query:
1242@macro()
1243def deduplicate(
1244    evaluator: MacroEvaluator,
1245    relation: exp.Expr,
1246    partition_by: t.List[exp.Expr],
1247    order_by: t.List[str],
1248) -> exp.Query:
1249    """Returns a QUERY to deduplicate rows within a table
1250
1251    Args:
1252        relation: table or CTE name to deduplicate
1253        partition_by: column names, or expressions to use to identify a window of rows out of which to select one as the deduplicated row
1254        order_by: A list of strings representing the ORDER BY clause
1255
1256    Example:
1257        >>> from sqlglot import parse_one
1258        >>> from sqlglot.schema import MappingSchema
1259        >>> from sqlmesh.core.macros import MacroEvaluator
1260        >>> sql = "@deduplicate(demo.table, [user_id, cast(timestamp as date)], ['timestamp desc', 'status asc'])"
1261        >>> MacroEvaluator().transform(parse_one(sql)).sql()
1262        'SELECT * FROM demo.table QUALIFY ROW_NUMBER() OVER (PARTITION BY user_id, CAST(timestamp AS DATE) ORDER BY timestamp DESC, status ASC) = 1'
1263    """
1264    if not isinstance(partition_by, list):
1265        raise SQLMeshError(
1266            "partition_by must be a list of columns: [<column>, cast(<column> as <type>)]"
1267        )
1268
1269    if not isinstance(order_by, list):
1270        raise SQLMeshError(
1271            "order_by must be a list of strings, optional - nulls ordering: ['<column> <asc|desc> nulls <first|last>']"
1272        )
1273
1274    partition_clause = exp.tuple_(*partition_by)
1275
1276    order_expressions = [
1277        evaluator.transform(parse_one(order_item, into=exp.Ordered, dialect=evaluator.dialect))
1278        for order_item in order_by
1279    ]
1280
1281    if not order_expressions:
1282        raise SQLMeshError(
1283            "order_by must be a list of strings, optional - nulls ordering: ['<column> <asc|desc> nulls <first|last>']"
1284        )
1285
1286    order_clause = exp.Order(expressions=order_expressions)
1287
1288    window_function = exp.Window(
1289        this=exp.RowNumber(), partition_by=partition_clause, order=order_clause
1290    )
1291
1292    first_unique_row = window_function.eq(1)
1293
1294    query = exp.select("*").from_(relation).qualify(first_unique_row)
1295
1296    return query

Returns a QUERY to deduplicate rows within a table

Arguments:
  • relation: table or CTE name to deduplicate
  • partition_by: column names, or expressions to use to identify a window of rows out of which to select one as the deduplicated row
  • order_by: A list of strings representing the ORDER BY clause
Example:
>>> from sqlglot import parse_one
>>> from sqlglot.schema import MappingSchema
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@deduplicate(demo.table, [user_id, cast(timestamp as date)], ['timestamp desc', 'status asc'])"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
'SELECT * FROM demo.table QUALIFY ROW_NUMBER() OVER (PARTITION BY user_id, CAST(timestamp AS DATE) ORDER BY timestamp DESC, status ASC) = 1'
@macro()
def date_spine( evaluator: MacroEvaluator, datepart: sqlglot.expressions.core.Expr, start_date: sqlglot.expressions.core.Expr, end_date: sqlglot.expressions.core.Expr) -> sqlglot.expressions.query.Select:
1299@macro()
1300def date_spine(
1301    evaluator: MacroEvaluator,
1302    datepart: exp.Expr,
1303    start_date: exp.Expr,
1304    end_date: exp.Expr,
1305) -> exp.Select:
1306    """Returns a query that produces a date spine with the given datepart, and range of start_date and end_date. Useful for joining as a date lookup table.
1307
1308    Args:
1309        datepart: The datepart to use for the date spine - day, week, month, quarter, year
1310        start_date: The start date for the date spine in format YYYY-MM-DD
1311        end_date: The end date for the date spine in format YYYY-MM-DD
1312
1313    Example:
1314        >>> from sqlglot import parse_one
1315        >>> from sqlglot.schema import MappingSchema
1316        >>> from sqlmesh.core.macros import MacroEvaluator
1317        >>> sql = "@date_spine('week', '2022-01-20', '2024-12-16')"
1318        >>> MacroEvaluator().transform(parse_one(sql)).sql()
1319        "SELECT date_week FROM UNNEST(GENERATE_DATE_ARRAY(CAST(\'2022-01-20\' AS DATE), CAST(\'2024-12-16\' AS DATE), INTERVAL \'1\' WEEK)) AS _exploded(date_week)"
1320    """
1321    datepart_name = datepart.name.lower()
1322    if datepart_name not in ("day", "week", "month", "quarter", "year"):
1323        raise SQLMeshError(
1324            f"Invalid datepart '{datepart_name}'. Expected: 'day', 'week', 'month', 'quarter', or 'year'"
1325        )
1326
1327    start_date_name = start_date.name
1328    end_date_name = end_date.name
1329
1330    try:
1331        if start_date.is_string and end_date.is_string:
1332            start_date_obj = datetime.strptime(start_date_name, "%Y-%m-%d").date()
1333            end_date_obj = datetime.strptime(end_date_name, "%Y-%m-%d").date()
1334        else:
1335            start_date_obj = None
1336            end_date_obj = None
1337    except Exception as e:
1338        raise SQLMeshError(
1339            f"Invalid date format - start_date and end_date must be in format: YYYY-MM-DD. Error: {e}"
1340        )
1341
1342    if start_date_obj and end_date_obj:
1343        if start_date_obj > end_date_obj:
1344            raise SQLMeshError(
1345                f"Invalid date range - start_date '{start_date_name}' is after end_date '{end_date_name}'."
1346            )
1347
1348        start_date = exp.cast(start_date, "DATE")
1349        end_date = exp.cast(end_date, "DATE")
1350
1351    if datepart_name == "quarter" and evaluator.dialect in (
1352        "spark",
1353        "spark2",
1354        "databricks",
1355        "postgres",
1356    ):
1357        date_interval = exp.Interval(this=exp.Literal.number(3), unit=exp.var("month"))
1358    else:
1359        date_interval = exp.Interval(this=exp.Literal.number(1), unit=exp.var(datepart_name))
1360
1361    generate_date_array = exp.func(
1362        "GENERATE_DATE_ARRAY",
1363        start_date,
1364        end_date,
1365        date_interval,
1366    )
1367
1368    alias_name = f"date_{datepart_name}"
1369    exploded = exp.alias_(exp.func("unnest", generate_date_array), "_exploded", table=[alias_name])
1370
1371    return exp.select(alias_name).from_(exploded)

Returns a query that produces a date spine with the given datepart, and range of start_date and end_date. Useful for joining as a date lookup table.

Arguments:
  • datepart: The datepart to use for the date spine - day, week, month, quarter, year
  • start_date: The start date for the date spine in format YYYY-MM-DD
  • end_date: The end date for the date spine in format YYYY-MM-DD
Example:
>>> from sqlglot import parse_one
>>> from sqlglot.schema import MappingSchema
>>> from sqlmesh.core.macros import MacroEvaluator
>>> sql = "@date_spine('week', '2022-01-20', '2024-12-16')"
>>> MacroEvaluator().transform(parse_one(sql)).sql()
"SELECT date_week FROM UNNEST(GENERATE_DATE_ARRAY(CAST('2022-01-20' AS DATE), CAST('2024-12-16' AS DATE), INTERVAL '1' WEEK)) AS _exploded(date_week)"
@macro()
def resolve_template( evaluator: MacroEvaluator, template: sqlglot.expressions.core.Literal, mode: str = 'literal') -> Union[sqlglot.expressions.core.Literal, sqlglot.expressions.query.Table]:
1374@macro()
1375def resolve_template(
1376    evaluator: MacroEvaluator,
1377    template: exp.Literal,
1378    mode: str = "literal",
1379) -> t.Union[exp.Literal, exp.Table]:
1380    """
1381    Generates either a String literal or an exp.Table representing a physical table location, based on rendering the provided template String literal.
1382
1383    Note: It relies on the @this_model variable being available in the evaluation context (@this_model resolves to an exp.Table object
1384    representing the current physical table).
1385    Therefore, the @resolve_template macro must be used at creation or evaluation time and not at load time.
1386
1387    Args:
1388        template: Template string literal. Can contain the following placeholders:
1389            @{catalog_name} -> replaced with the catalog of the exp.Table returned from @this_model
1390            @{schema_name} -> replaced with the schema of the exp.Table returned from @this_model
1391            @{table_name} -> replaced with the name of the exp.Table returned from @this_model
1392        mode: What to return.
1393            'literal' -> return an exp.Literal string
1394            'table' -> return an exp.Table
1395
1396    Example:
1397        >>> from sqlglot import parse_one, exp
1398        >>> from sqlmesh.core.macros import MacroEvaluator, RuntimeStage
1399        >>> sql = "@resolve_template('s3://data-bucket/prod/@{catalog_name}/@{schema_name}/@{table_name}')"
1400        >>> evaluator = MacroEvaluator(runtime_stage=RuntimeStage.CREATING)
1401        >>> evaluator.locals.update({"this_model": exp.to_table("test_catalog.sqlmesh__test.test__test_model__2517971505")})
1402        >>> evaluator.transform(parse_one(sql)).sql()
1403        "'s3://data-bucket/prod/test_catalog/sqlmesh__test/test__test_model__2517971505'"
1404    """
1405    if "this_model" in evaluator.locals:
1406        this_model = exp.to_table(evaluator.locals["this_model"], dialect=evaluator.dialect)
1407        template_str: str = template.this
1408        result = (
1409            template_str.replace("@{catalog_name}", this_model.catalog)
1410            .replace("@{schema_name}", this_model.db)
1411            .replace("@{table_name}", this_model.name)
1412        )
1413
1414        if mode.lower() == "table":
1415            return exp.to_table(result, dialect=evaluator.dialect)
1416        return exp.Literal.string(result)
1417    if evaluator.runtime_stage != RuntimeStage.LOADING.value:
1418        # only error if we are CREATING, EVALUATING or TESTING and @this_model is not present; this could indicate a bug
1419        # otherwise, for LOADING, it's a no-op
1420        raise SQLMeshError(
1421            "@this_model must be present in the macro evaluation context in order to use @resolve_template"
1422        )
1423
1424    return template

Generates either a String literal or an exp.Table representing a physical table location, based on rendering the provided template String literal.

Note: It relies on the @this_model variable being available in the evaluation context (@this_model resolves to an exp.Table object representing the current physical table). Therefore, the @resolve_template macro must be used at creation or evaluation time and not at load time.

Arguments:
  • template: Template string literal. Can contain the following placeholders: @{catalog_name} -> replaced with the catalog of the exp.Table returned from @this_model @{schema_name} -> replaced with the schema of the exp.Table returned from @this_model @{table_name} -> replaced with the name of the exp.Table returned from @this_model
  • mode: What to return. 'literal' -> return an exp.Literal string 'table' -> return an exp.Table
Example:
>>> from sqlglot import parse_one, exp
>>> from sqlmesh.core.macros import MacroEvaluator, RuntimeStage
>>> sql = "@resolve_template('s3://data-bucket/prod/@{catalog_name}/@{schema_name}/@{table_name}')"
>>> evaluator = MacroEvaluator(runtime_stage=RuntimeStage.CREATING)
>>> evaluator.locals.update({"this_model": exp.to_table("test_catalog.sqlmesh__test.test__test_model__2517971505")})
>>> evaluator.transform(parse_one(sql)).sql()
"'s3://data-bucket/prod/test_catalog/sqlmesh__test/test__test_model__2517971505'"
def normalize_macro_name(name: str) -> str:
1427def normalize_macro_name(name: str) -> str:
1428    """Prefix macro name with @ and upcase"""
1429    return f"@{name.upper()}"

Prefix macro name with @ and upcase

def call_macro( func: Callable, dialect: Union[str, sqlglot.dialects.dialect.Dialect, type[sqlglot.dialects.dialect.Dialect], NoneType], path: Optional[pathlib.Path], provided_args: Tuple[Any, ...], provided_kwargs: Dict[str, Any], **optional_kwargs: Any) -> Any:
1436def call_macro(
1437    func: t.Callable,
1438    dialect: DialectType,
1439    path: t.Optional[Path],
1440    provided_args: t.Tuple[t.Any, ...],
1441    provided_kwargs: t.Dict[str, t.Any],
1442    **optional_kwargs: t.Any,
1443) -> t.Any:
1444    # Bind the macro's actual parameters to its formal parameters
1445    sig = inspect.signature(func)
1446
1447    if optional_kwargs:
1448        provided_kwargs = provided_kwargs.copy()
1449
1450    for k, v in optional_kwargs.items():
1451        if k in sig.parameters:
1452            provided_kwargs[k] = v
1453
1454    bound = sig.bind(*provided_args, **provided_kwargs)
1455    bound.apply_defaults()
1456
1457    try:
1458        annotations = t.get_type_hints(func, localns=get_supported_types())
1459    except (NameError, TypeError):  # forward references aren't handled
1460        annotations = {}
1461
1462    # If the macro is annotated, we try coerce the actual parameters to the corresponding types
1463    if annotations:
1464        for arg, value in bound.arguments.items():
1465            typ = annotations.get(arg)
1466            if not typ:
1467                continue
1468
1469            # Changes to bound.arguments will reflect in bound.args and bound.kwargs
1470            # https://docs.python.org/3/library/inspect.html#inspect.BoundArguments.arguments
1471            param = sig.parameters[arg]
1472            if param.kind is inspect.Parameter.VAR_POSITIONAL:
1473                bound.arguments[arg] = tuple(_coerce(v, typ, dialect, path) for v in value)
1474            elif param.kind is inspect.Parameter.VAR_KEYWORD:
1475                bound.arguments[arg] = {k: _coerce(v, typ, dialect, path) for k, v in value.items()}
1476            else:
1477                bound.arguments[arg] = _coerce(value, typ, dialect, path)
1478
1479    return func(*bound.args, **bound.kwargs)
def convert_sql( v: Any, dialect: Union[str, sqlglot.dialects.dialect.Dialect, type[sqlglot.dialects.dialect.Dialect], NoneType]) -> Any:
1587def convert_sql(v: t.Any, dialect: DialectType) -> t.Any:
1588    try:
1589        return _cache_convert_sql(v, dialect, v.__class__)
1590    # dicts aren't hashable but are convertable
1591    except TypeError:
1592        return _convert_sql(v, dialect)
m = <macro object>