Edit on GitHub

sqlmesh.core.model.kind

  1from __future__ import annotations
  2
  3import sys
  4import typing as t
  5from enum import Enum
  6
  7from pydantic import Field
  8from sqlglot import exp
  9from sqlglot.optimizer.normalize_identifiers import normalize_identifiers
 10from sqlglot.optimizer.qualify_columns import quote_identifiers
 11from sqlglot.optimizer.simplify import gen
 12from sqlglot.time import format_time
 13
 14from sqlmesh.core import dialect as d
 15from sqlmesh.core.model.common import parse_properties
 16from sqlmesh.core.model.seed import CsvSettings
 17from sqlmesh.utils.errors import ConfigError
 18from sqlmesh.utils.pydantic import (
 19    PydanticModel,
 20    SQLGlotBool,
 21    SQLGlotColumn,
 22    SQLGlotListOfColumnsOrStar,
 23    SQLGlotListOfFields,
 24    SQLGlotPositiveInt,
 25    SQLGlotString,
 26    column_validator,
 27    field_validator,
 28    field_validator_v1_args,
 29    get_dialect,
 30)
 31
 32if sys.version_info >= (3, 9):
 33    from typing import Annotated, Literal
 34else:
 35    from typing_extensions import Annotated, Literal
 36
 37
 38if t.TYPE_CHECKING:
 39    MODEL_KIND = t.TypeVar("MODEL_KIND", bound="_ModelKind")
 40
 41
 42class ModelKindMixin:
 43    @property
 44    def model_kind_name(self) -> t.Optional[ModelKindName]:
 45        """Returns the model kind name."""
 46        raise NotImplementedError
 47
 48    @property
 49    def is_incremental_by_time_range(self) -> bool:
 50        return self.model_kind_name == ModelKindName.INCREMENTAL_BY_TIME_RANGE
 51
 52    @property
 53    def is_incremental_by_unique_key(self) -> bool:
 54        return self.model_kind_name == ModelKindName.INCREMENTAL_BY_UNIQUE_KEY
 55
 56    @property
 57    def is_incremental_unmanaged(self) -> bool:
 58        return self.model_kind_name == ModelKindName.INCREMENTAL_UNMANAGED
 59
 60    @property
 61    def is_incremental(self) -> bool:
 62        return (
 63            self.is_incremental_by_time_range
 64            or self.is_incremental_by_unique_key
 65            or self.is_incremental_unmanaged
 66            or self.is_scd_type_2
 67        )
 68
 69    @property
 70    def is_full(self) -> bool:
 71        return self.model_kind_name == ModelKindName.FULL
 72
 73    @property
 74    def is_view(self) -> bool:
 75        return self.model_kind_name == ModelKindName.VIEW
 76
 77    @property
 78    def is_embedded(self) -> bool:
 79        return self.model_kind_name == ModelKindName.EMBEDDED
 80
 81    @property
 82    def is_seed(self) -> bool:
 83        return self.model_kind_name == ModelKindName.SEED
 84
 85    @property
 86    def is_external(self) -> bool:
 87        return self.model_kind_name == ModelKindName.EXTERNAL
 88
 89    @property
 90    def is_scd_type_2(self) -> bool:
 91        return self.model_kind_name in {
 92            ModelKindName.SCD_TYPE_2,
 93            ModelKindName.SCD_TYPE_2_BY_TIME,
 94            ModelKindName.SCD_TYPE_2_BY_COLUMN,
 95        }
 96
 97    @property
 98    def is_scd_type_2_by_time(self) -> bool:
 99        return self.model_kind_name in {ModelKindName.SCD_TYPE_2, ModelKindName.SCD_TYPE_2_BY_TIME}
100
101    @property
102    def is_scd_type_2_by_column(self) -> bool:
103        return self.model_kind_name == ModelKindName.SCD_TYPE_2_BY_COLUMN
104
105    @property
106    def is_symbolic(self) -> bool:
107        """A symbolic model is one that doesn't execute at all."""
108        return self.model_kind_name in (ModelKindName.EMBEDDED, ModelKindName.EXTERNAL)
109
110    @property
111    def is_materialized(self) -> bool:
112        return not (self.is_symbolic or self.is_view)
113
114    @property
115    def only_execution_time(self) -> bool:
116        """Whether or not this model only cares about execution time to render."""
117        return self.is_view or self.is_full or self.is_scd_type_2
118
119    @property
120    def full_history_restatement_only(self) -> bool:
121        """Whether or not this model only supports restatement of full history."""
122        return (
123            self.is_incremental_unmanaged or self.is_incremental_by_unique_key or self.is_scd_type_2
124        )
125
126
127class ModelKindName(str, ModelKindMixin, Enum):
128    """The kind of model, determining how this data is computed and stored in the warehouse."""
129
130    INCREMENTAL_BY_TIME_RANGE = "INCREMENTAL_BY_TIME_RANGE"
131    INCREMENTAL_BY_UNIQUE_KEY = "INCREMENTAL_BY_UNIQUE_KEY"
132    INCREMENTAL_UNMANAGED = "INCREMENTAL_UNMANAGED"
133    FULL = "FULL"
134    # Legacy alias to SCD Type 2 By Time
135    # Only used for Parsing and mapping name to SCD Type 2 By Time
136    SCD_TYPE_2 = "SCD_TYPE_2"
137    SCD_TYPE_2_BY_TIME = "SCD_TYPE_2_BY_TIME"
138    SCD_TYPE_2_BY_COLUMN = "SCD_TYPE_2_BY_COLUMN"
139    VIEW = "VIEW"
140    EMBEDDED = "EMBEDDED"
141    SEED = "SEED"
142    EXTERNAL = "EXTERNAL"
143
144    @property
145    def model_kind_name(self) -> t.Optional[ModelKindName]:
146        return self
147
148    def __str__(self) -> str:
149        return self.name
150
151    def __repr__(self) -> str:
152        return str(self)
153
154
155class _ModelKind(PydanticModel, ModelKindMixin):
156    name: ModelKindName
157
158    @property
159    def model_kind_name(self) -> t.Optional[ModelKindName]:
160        return self.name
161
162    def to_expression(self, **kwargs: t.Any) -> d.ModelKind:
163        return d.ModelKind(this=self.name.value.upper(), **kwargs)
164
165    @property
166    def data_hash_values(self) -> t.List[t.Optional[str]]:
167        return [self.name.value]
168
169    @property
170    def metadata_hash_values(self) -> t.List[t.Optional[str]]:
171        return []
172
173
174class TimeColumn(PydanticModel):
175    column: exp.Expression
176    format: t.Optional[str] = None
177
178    @classmethod
179    def validator(cls) -> classmethod:
180        def _time_column_validator(v: t.Any, values: t.Any) -> TimeColumn:
181            dialect = get_dialect(values)
182
183            if isinstance(v, exp.Tuple):
184                column_expr = v.expressions[0]
185                column = (
186                    exp.column(column_expr)
187                    if isinstance(column_expr, exp.Identifier)
188                    else column_expr
189                )
190                format = v.expressions[1].name if len(v.expressions) > 1 else None
191            elif isinstance(v, exp.Expression):
192                column = exp.column(v) if isinstance(v, exp.Identifier) else v
193                format = None
194            elif isinstance(v, str):
195                column = d.parse_one(v, dialect=dialect)
196                column.meta.pop("sql")
197                format = None
198            elif isinstance(v, dict):
199                column_raw = v["column"]
200                column = (
201                    d.parse_one(column_raw, dialect=dialect)
202                    if isinstance(column_raw, str)
203                    else column_raw
204                )
205                format = v.get("format")
206            elif isinstance(v, TimeColumn):
207                column = v.column
208                format = v.format
209            else:
210                raise ConfigError(f"Invalid time_column: '{v}'.")
211
212            column = quote_identifiers(
213                normalize_identifiers(column, dialect=dialect), dialect=dialect
214            )
215            column.meta["dialect"] = dialect
216
217            return TimeColumn(column=column, format=format)
218
219        return field_validator("time_column", mode="before")(_time_column_validator)
220
221    @field_validator("column", mode="before")
222    @classmethod
223    def _column_validator(cls, v: t.Union[str, exp.Expression]) -> exp.Expression:
224        if not v:
225            raise ConfigError("Time Column cannot be empty.")
226        if isinstance(v, str):
227            return exp.to_column(v)
228        return v
229
230    @property
231    def expression(self) -> exp.Expression:
232        """Convert this pydantic model into a time_column SQLGlot expression."""
233        if not self.format:
234            return self.column
235
236        return exp.Tuple(expressions=[self.column, exp.Literal.string(self.format)])
237
238    def to_expression(self, dialect: str) -> exp.Expression:
239        """Convert this pydantic model into a time_column SQLGlot expression."""
240        if not self.format:
241            return self.column
242
243        return exp.Tuple(
244            expressions=[
245                self.column,
246                exp.Literal.string(
247                    format_time(self.format, d.Dialect.get_or_raise(dialect).INVERSE_TIME_MAPPING)
248                ),
249            ]
250        )
251
252    def to_property(self, dialect: str = "") -> exp.Property:
253        return exp.Property(this="time_column", value=self.to_expression(dialect))
254
255
256def _kind_dialect_validator(cls: t.Type, v: t.Optional[str]) -> str:
257    if v is None:
258        return get_dialect({})
259    return v
260
261
262kind_dialect_validator = field_validator("dialect", mode="before", always=True)(
263    _kind_dialect_validator
264)
265
266
267class _Incremental(_ModelKind):
268    dialect: t.Optional[str] = Field(None, validate_default=True)
269    batch_size: t.Optional[SQLGlotPositiveInt] = None
270    batch_concurrency: t.Optional[SQLGlotPositiveInt] = None
271    lookback: t.Optional[SQLGlotPositiveInt] = None
272    forward_only: SQLGlotBool = False
273    disable_restatement: SQLGlotBool = False
274
275    _dialect_validator = kind_dialect_validator
276
277    @property
278    def data_hash_values(self) -> t.List[t.Optional[str]]:
279        return [
280            *super().data_hash_values,
281            self.dialect,
282            str(self.lookback) if self.lookback is not None else None,
283        ]
284
285    @property
286    def metadata_hash_values(self) -> t.List[t.Optional[str]]:
287        return [
288            *super().metadata_hash_values,
289            str(self.batch_size) if self.batch_size is not None else None,
290            str(self.forward_only),
291            str(self.disable_restatement),
292        ]
293
294
295class IncrementalByTimeRangeKind(_Incremental):
296    name: Literal[ModelKindName.INCREMENTAL_BY_TIME_RANGE] = ModelKindName.INCREMENTAL_BY_TIME_RANGE
297    time_column: TimeColumn
298
299    _time_column_validator = TimeColumn.validator()
300
301    def to_expression(self, dialect: str = "", **kwargs: t.Any) -> d.ModelKind:
302        return super().to_expression(expressions=[self.time_column.to_property(dialect)])
303
304    @property
305    def data_hash_values(self) -> t.List[t.Optional[str]]:
306        return [*super().data_hash_values, gen(self.time_column.column), self.time_column.format]
307
308
309class IncrementalByUniqueKeyKind(_Incremental):
310    name: Literal[ModelKindName.INCREMENTAL_BY_UNIQUE_KEY] = ModelKindName.INCREMENTAL_BY_UNIQUE_KEY
311    unique_key: SQLGlotListOfFields
312    when_matched: t.Optional[exp.When] = None
313    batch_concurrency: Literal[1] = 1
314
315    @field_validator("when_matched", mode="before")
316    @field_validator_v1_args
317    def _when_matched_validator(
318        cls, v: t.Optional[t.Union[exp.When, str]], values: t.Dict[str, t.Any]
319    ) -> t.Optional[exp.When]:
320        def replace_table_references(expression: exp.Expression) -> exp.Expression:
321            from sqlmesh.core.engine_adapter.base import (
322                MERGE_SOURCE_ALIAS,
323                MERGE_TARGET_ALIAS,
324            )
325
326            if isinstance(expression, exp.Column):
327                if expression.table.lower() == "target":
328                    expression.set(
329                        "table",
330                        exp.to_identifier(MERGE_TARGET_ALIAS),
331                    )
332                elif expression.table.lower() == "source":
333                    expression.set(
334                        "table",
335                        exp.to_identifier(MERGE_SOURCE_ALIAS),
336                    )
337            return expression
338
339        if isinstance(v, str):
340            return t.cast(exp.When, d.parse_one(v, into=exp.When, dialect=get_dialect(values)))
341
342        if not v:
343            return v
344
345        return t.cast(exp.When, v.transform(replace_table_references))
346
347    @property
348    def data_hash_values(self) -> t.List[t.Optional[str]]:
349        return [
350            *super().data_hash_values,
351            *(gen(k) for k in self.unique_key),
352            gen(self.when_matched) if self.when_matched is not None else None,
353        ]
354
355
356class IncrementalUnmanagedKind(_ModelKind):
357    name: Literal[ModelKindName.INCREMENTAL_UNMANAGED] = ModelKindName.INCREMENTAL_UNMANAGED
358    insert_overwrite: SQLGlotBool = False
359    forward_only: SQLGlotBool = True
360    disable_restatement: SQLGlotBool = True
361
362    @property
363    def data_hash_values(self) -> t.List[t.Optional[str]]:
364        return [*super().data_hash_values, str(self.insert_overwrite)]
365
366    @property
367    def metadata_hash_values(self) -> t.List[t.Optional[str]]:
368        return [*super().metadata_hash_values, str(self.forward_only)]
369
370
371class ViewKind(_ModelKind):
372    name: Literal[ModelKindName.VIEW] = ModelKindName.VIEW
373    materialized: SQLGlotBool = False
374
375    @property
376    def data_hash_values(self) -> t.List[t.Optional[str]]:
377        return [*super().data_hash_values, str(self.materialized)]
378
379
380class SeedKind(_ModelKind):
381    name: Literal[ModelKindName.SEED] = ModelKindName.SEED
382    path: SQLGlotString
383    batch_size: SQLGlotPositiveInt = 1000
384    csv_settings: t.Optional[CsvSettings] = None
385
386    @field_validator("csv_settings", mode="before")
387    @classmethod
388    def _parse_csv_settings(cls, v: t.Any) -> t.Optional[CsvSettings]:
389        if v is None or isinstance(v, CsvSettings):
390            return v
391        if isinstance(v, exp.Expression):
392            tuple_exp = parse_properties(cls, v, {})
393            if not tuple_exp:
394                return None
395            return CsvSettings(**{e.left.name: e.right for e in tuple_exp.expressions})
396        if isinstance(v, dict):
397            return CsvSettings(**v)
398        return v
399
400    def to_expression(self, **kwargs: t.Any) -> d.ModelKind:
401        """Convert the seed kind into a SQLGlot expression."""
402        return super().to_expression(
403            expressions=[
404                exp.Property(this=exp.Var(this="path"), value=exp.Literal.string(self.path)),
405                exp.Property(
406                    this=exp.Var(this="batch_size"),
407                    value=exp.Literal.number(self.batch_size),
408                ),
409            ],
410        )
411
412    @property
413    def data_hash_values(self) -> t.List[t.Optional[str]]:
414        return [
415            *super().data_hash_values,
416            *(self.csv_settings or CsvSettings()).dict().values(),
417        ]
418
419    @property
420    def metadata_hash_values(self) -> t.List[t.Optional[str]]:
421        return [*super().metadata_hash_values, str(self.batch_size)]
422
423
424class FullKind(_ModelKind):
425    name: Literal[ModelKindName.FULL] = ModelKindName.FULL
426
427
428class _SCDType2Kind(_ModelKind):
429    dialect: t.Optional[str] = Field(None, validate_default=True)
430    unique_key: SQLGlotListOfFields
431    valid_from_name: SQLGlotColumn = Field(exp.column("valid_from"), validate_default=True)
432    valid_to_name: SQLGlotColumn = Field(exp.column("valid_to"), validate_default=True)
433    invalidate_hard_deletes: SQLGlotBool = False
434    time_data_type: exp.DataType = Field(exp.DataType.build("TIMESTAMP"), validate_default=True)
435
436    forward_only: SQLGlotBool = True
437    disable_restatement: SQLGlotBool = True
438
439    _dialect_validator = kind_dialect_validator
440    # Remove once Pydantic 1 is deprecated
441    _always_validate_column = field_validator(
442        "valid_from_name", "valid_to_name", mode="before", always=True
443    )(column_validator)
444
445    # always=True can be removed once Pydantic 1 is deprecated
446    @field_validator("time_data_type", mode="before", always=True)
447    @classmethod
448    def _time_data_type_validator(
449        cls, v: t.Union[str, exp.Expression], values: t.Any
450    ) -> exp.Expression:
451        if isinstance(v, exp.Expression) and not isinstance(v, exp.DataType):
452            v = v.name
453        dialect = get_dialect(values)
454        data_type = exp.DataType.build(v, dialect=dialect)
455        data_type.meta["dialect"] = dialect
456        return data_type
457
458    @property
459    def managed_columns(self) -> t.Dict[str, exp.DataType]:
460        return {
461            self.valid_from_name.name: self.time_data_type,
462            self.valid_to_name.name: self.time_data_type,
463        }
464
465    @property
466    def data_hash_values(self) -> t.List[t.Optional[str]]:
467        return [
468            *super().data_hash_values,
469            self.dialect,
470            *(gen(k) for k in self.unique_key),
471            gen(self.valid_from_name),
472            gen(self.valid_to_name),
473            str(self.invalidate_hard_deletes),
474            gen(self.time_data_type),
475        ]
476
477    @property
478    def metadata_hash_values(self) -> t.List[t.Optional[str]]:
479        return [
480            *super().metadata_hash_values,
481            str(self.forward_only),
482            str(self.disable_restatement),
483        ]
484
485
486class SCDType2ByTimeKind(_SCDType2Kind):
487    name: Literal[ModelKindName.SCD_TYPE_2, ModelKindName.SCD_TYPE_2_BY_TIME] = (
488        ModelKindName.SCD_TYPE_2_BY_TIME
489    )
490    updated_at_name: SQLGlotColumn = Field(exp.column("updated_at"), validate_default=True)
491    updated_at_as_valid_from: SQLGlotBool = False
492
493    # Remove once Pydantic 1 is deprecated
494    _always_validate_updated_at = field_validator("updated_at_name", mode="before", always=True)(
495        column_validator
496    )
497
498    @property
499    def data_hash_values(self) -> t.List[t.Optional[str]]:
500        return [
501            *super().data_hash_values,
502            gen(self.updated_at_name),
503            str(self.updated_at_as_valid_from),
504        ]
505
506
507class SCDType2ByColumnKind(_SCDType2Kind):
508    name: Literal[ModelKindName.SCD_TYPE_2_BY_COLUMN] = ModelKindName.SCD_TYPE_2_BY_COLUMN
509    columns: SQLGlotListOfColumnsOrStar
510    execution_time_as_valid_from: SQLGlotBool = False
511
512    @property
513    def data_hash_values(self) -> t.List[t.Optional[str]]:
514        columns_sql = (
515            [gen(c) for c in self.columns]
516            if isinstance(self.columns, list)
517            else [gen(self.columns)]
518        )
519        return [*super().data_hash_values, *columns_sql, str(self.execution_time_as_valid_from)]
520
521
522class EmbeddedKind(_ModelKind):
523    name: Literal[ModelKindName.EMBEDDED] = ModelKindName.EMBEDDED
524
525
526class ExternalKind(_ModelKind):
527    name: Literal[ModelKindName.EXTERNAL] = ModelKindName.EXTERNAL
528
529
530ModelKind = Annotated[
531    t.Union[
532        EmbeddedKind,
533        ExternalKind,
534        FullKind,
535        IncrementalByTimeRangeKind,
536        IncrementalByUniqueKeyKind,
537        IncrementalUnmanagedKind,
538        SeedKind,
539        ViewKind,
540        SCDType2ByTimeKind,
541        SCDType2ByColumnKind,
542    ],
543    Field(discriminator="name"),
544]
545
546MODEL_KIND_NAME_TO_TYPE: t.Dict[str, t.Type[ModelKind]] = {
547    ModelKindName.EMBEDDED: EmbeddedKind,
548    ModelKindName.EXTERNAL: ExternalKind,
549    ModelKindName.FULL: FullKind,
550    ModelKindName.INCREMENTAL_BY_TIME_RANGE: IncrementalByTimeRangeKind,
551    ModelKindName.INCREMENTAL_BY_UNIQUE_KEY: IncrementalByUniqueKeyKind,
552    ModelKindName.INCREMENTAL_UNMANAGED: IncrementalUnmanagedKind,
553    ModelKindName.SEED: SeedKind,
554    ModelKindName.VIEW: ViewKind,
555    ModelKindName.SCD_TYPE_2: SCDType2ByTimeKind,
556    ModelKindName.SCD_TYPE_2_BY_TIME: SCDType2ByTimeKind,
557    ModelKindName.SCD_TYPE_2_BY_COLUMN: SCDType2ByColumnKind,
558}
559
560
561def model_kind_type_from_name(name: t.Optional[str]) -> t.Type[ModelKind]:
562    klass = MODEL_KIND_NAME_TO_TYPE.get(name) if name else None
563    if not klass:
564        raise ConfigError(f"Invalid model kind '{name}'")
565    return t.cast(t.Type[ModelKind], klass)
566
567
568@field_validator_v1_args
569def _model_kind_validator(cls: t.Type, v: t.Any, values: t.Dict[str, t.Any]) -> ModelKind:
570    dialect = get_dialect(values)
571
572    if isinstance(v, _ModelKind):
573        return t.cast(ModelKind, v)
574
575    if isinstance(v, (d.ModelKind, dict)):
576        props = (
577            {prop.name: prop.args.get("value") for prop in v.expressions}
578            if isinstance(v, d.ModelKind)
579            else v
580        )
581        name = v.this if isinstance(v, d.ModelKind) else props.get("name")
582        # We want to ensure whatever name is provided to construct the class is the same name that will be
583        # found inside the class itself in order to avoid a change during plan/apply for legacy aliases.
584        # Ex: Pass in `SCD_TYPE_2` then we want to ensure we get `SCD_TYPE_2` as the kind name
585        # instead of `SCD_TYPE_2_BY_TIME`.
586        props["name"] = name
587        kind_type = model_kind_type_from_name(name)
588        if "dialect" in kind_type.all_fields() and props.get("dialect") is None:
589            props["dialect"] = dialect
590        return kind_type(**props)
591
592    name = (v.name if isinstance(v, exp.Expression) else str(v)).upper()
593    return model_kind_type_from_name(name)(name=name)  # type: ignore
594
595
596model_kind_validator = field_validator("kind", mode="before")(_model_kind_validator)
class ModelKindMixin:
 43class ModelKindMixin:
 44    @property
 45    def model_kind_name(self) -> t.Optional[ModelKindName]:
 46        """Returns the model kind name."""
 47        raise NotImplementedError
 48
 49    @property
 50    def is_incremental_by_time_range(self) -> bool:
 51        return self.model_kind_name == ModelKindName.INCREMENTAL_BY_TIME_RANGE
 52
 53    @property
 54    def is_incremental_by_unique_key(self) -> bool:
 55        return self.model_kind_name == ModelKindName.INCREMENTAL_BY_UNIQUE_KEY
 56
 57    @property
 58    def is_incremental_unmanaged(self) -> bool:
 59        return self.model_kind_name == ModelKindName.INCREMENTAL_UNMANAGED
 60
 61    @property
 62    def is_incremental(self) -> bool:
 63        return (
 64            self.is_incremental_by_time_range
 65            or self.is_incremental_by_unique_key
 66            or self.is_incremental_unmanaged
 67            or self.is_scd_type_2
 68        )
 69
 70    @property
 71    def is_full(self) -> bool:
 72        return self.model_kind_name == ModelKindName.FULL
 73
 74    @property
 75    def is_view(self) -> bool:
 76        return self.model_kind_name == ModelKindName.VIEW
 77
 78    @property
 79    def is_embedded(self) -> bool:
 80        return self.model_kind_name == ModelKindName.EMBEDDED
 81
 82    @property
 83    def is_seed(self) -> bool:
 84        return self.model_kind_name == ModelKindName.SEED
 85
 86    @property
 87    def is_external(self) -> bool:
 88        return self.model_kind_name == ModelKindName.EXTERNAL
 89
 90    @property
 91    def is_scd_type_2(self) -> bool:
 92        return self.model_kind_name in {
 93            ModelKindName.SCD_TYPE_2,
 94            ModelKindName.SCD_TYPE_2_BY_TIME,
 95            ModelKindName.SCD_TYPE_2_BY_COLUMN,
 96        }
 97
 98    @property
 99    def is_scd_type_2_by_time(self) -> bool:
100        return self.model_kind_name in {ModelKindName.SCD_TYPE_2, ModelKindName.SCD_TYPE_2_BY_TIME}
101
102    @property
103    def is_scd_type_2_by_column(self) -> bool:
104        return self.model_kind_name == ModelKindName.SCD_TYPE_2_BY_COLUMN
105
106    @property
107    def is_symbolic(self) -> bool:
108        """A symbolic model is one that doesn't execute at all."""
109        return self.model_kind_name in (ModelKindName.EMBEDDED, ModelKindName.EXTERNAL)
110
111    @property
112    def is_materialized(self) -> bool:
113        return not (self.is_symbolic or self.is_view)
114
115    @property
116    def only_execution_time(self) -> bool:
117        """Whether or not this model only cares about execution time to render."""
118        return self.is_view or self.is_full or self.is_scd_type_2
119
120    @property
121    def full_history_restatement_only(self) -> bool:
122        """Whether or not this model only supports restatement of full history."""
123        return (
124            self.is_incremental_unmanaged or self.is_incremental_by_unique_key or self.is_scd_type_2
125        )
model_kind_name: Union[sqlmesh.core.model.kind.ModelKindName, NoneType]

Returns the model kind name.

is_symbolic: bool

A symbolic model is one that doesn't execute at all.

only_execution_time: bool

Whether or not this model only cares about execution time to render.

full_history_restatement_only: bool

Whether or not this model only supports restatement of full history.

class ModelKindName(builtins.str, ModelKindMixin, enum.Enum):
128class ModelKindName(str, ModelKindMixin, Enum):
129    """The kind of model, determining how this data is computed and stored in the warehouse."""
130
131    INCREMENTAL_BY_TIME_RANGE = "INCREMENTAL_BY_TIME_RANGE"
132    INCREMENTAL_BY_UNIQUE_KEY = "INCREMENTAL_BY_UNIQUE_KEY"
133    INCREMENTAL_UNMANAGED = "INCREMENTAL_UNMANAGED"
134    FULL = "FULL"
135    # Legacy alias to SCD Type 2 By Time
136    # Only used for Parsing and mapping name to SCD Type 2 By Time
137    SCD_TYPE_2 = "SCD_TYPE_2"
138    SCD_TYPE_2_BY_TIME = "SCD_TYPE_2_BY_TIME"
139    SCD_TYPE_2_BY_COLUMN = "SCD_TYPE_2_BY_COLUMN"
140    VIEW = "VIEW"
141    EMBEDDED = "EMBEDDED"
142    SEED = "SEED"
143    EXTERNAL = "EXTERNAL"
144
145    @property
146    def model_kind_name(self) -> t.Optional[ModelKindName]:
147        return self
148
149    def __str__(self) -> str:
150        return self.name
151
152    def __repr__(self) -> str:
153        return str(self)

The kind of model, determining how this data is computed and stored in the warehouse.

INCREMENTAL_BY_TIME_RANGE = INCREMENTAL_BY_TIME_RANGE
INCREMENTAL_BY_UNIQUE_KEY = INCREMENTAL_BY_UNIQUE_KEY
INCREMENTAL_UNMANAGED = INCREMENTAL_UNMANAGED
FULL = FULL
SCD_TYPE_2 = SCD_TYPE_2
SCD_TYPE_2_BY_TIME = SCD_TYPE_2_BY_TIME
SCD_TYPE_2_BY_COLUMN = SCD_TYPE_2_BY_COLUMN
VIEW = VIEW
EMBEDDED = EMBEDDED
SEED = SEED
EXTERNAL = EXTERNAL
model_kind_name: Union[sqlmesh.core.model.kind.ModelKindName, NoneType]

Returns the model kind name.

Inherited Members
ModelKindMixin
is_symbolic
only_execution_time
full_history_restatement_only
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class TimeColumn(sqlmesh.utils.pydantic.PydanticModel):
175class TimeColumn(PydanticModel):
176    column: exp.Expression
177    format: t.Optional[str] = None
178
179    @classmethod
180    def validator(cls) -> classmethod:
181        def _time_column_validator(v: t.Any, values: t.Any) -> TimeColumn:
182            dialect = get_dialect(values)
183
184            if isinstance(v, exp.Tuple):
185                column_expr = v.expressions[0]
186                column = (
187                    exp.column(column_expr)
188                    if isinstance(column_expr, exp.Identifier)
189                    else column_expr
190                )
191                format = v.expressions[1].name if len(v.expressions) > 1 else None
192            elif isinstance(v, exp.Expression):
193                column = exp.column(v) if isinstance(v, exp.Identifier) else v
194                format = None
195            elif isinstance(v, str):
196                column = d.parse_one(v, dialect=dialect)
197                column.meta.pop("sql")
198                format = None
199            elif isinstance(v, dict):
200                column_raw = v["column"]
201                column = (
202                    d.parse_one(column_raw, dialect=dialect)
203                    if isinstance(column_raw, str)
204                    else column_raw
205                )
206                format = v.get("format")
207            elif isinstance(v, TimeColumn):
208                column = v.column
209                format = v.format
210            else:
211                raise ConfigError(f"Invalid time_column: '{v}'.")
212
213            column = quote_identifiers(
214                normalize_identifiers(column, dialect=dialect), dialect=dialect
215            )
216            column.meta["dialect"] = dialect
217
218            return TimeColumn(column=column, format=format)
219
220        return field_validator("time_column", mode="before")(_time_column_validator)
221
222    @field_validator("column", mode="before")
223    @classmethod
224    def _column_validator(cls, v: t.Union[str, exp.Expression]) -> exp.Expression:
225        if not v:
226            raise ConfigError("Time Column cannot be empty.")
227        if isinstance(v, str):
228            return exp.to_column(v)
229        return v
230
231    @property
232    def expression(self) -> exp.Expression:
233        """Convert this pydantic model into a time_column SQLGlot expression."""
234        if not self.format:
235            return self.column
236
237        return exp.Tuple(expressions=[self.column, exp.Literal.string(self.format)])
238
239    def to_expression(self, dialect: str) -> exp.Expression:
240        """Convert this pydantic model into a time_column SQLGlot expression."""
241        if not self.format:
242            return self.column
243
244        return exp.Tuple(
245            expressions=[
246                self.column,
247                exp.Literal.string(
248                    format_time(self.format, d.Dialect.get_or_raise(dialect).INVERSE_TIME_MAPPING)
249                ),
250            ]
251        )
252
253    def to_property(self, dialect: str = "") -> exp.Property:
254        return exp.Property(this="time_column", value=self.to_expression(dialect))

Usage docs: https://docs.pydantic.dev/2.7/concepts/models/

A base class for creating Pydantic models.

Attributes:
  • __class_vars__: The names of classvars defined on the model.
  • __private_attributes__: Metadata about the private attributes of the model.
  • __signature__: The signature for instantiating the model.
  • __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
  • __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
  • __pydantic_custom_init__: Whether the model has a custom __init__ function.
  • __pydantic_decorators__: Metadata containing the decorators defined on the model. This replaces Model.__validators__ and Model.__root_validators__ from Pydantic V1.
  • __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
  • __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
  • __pydantic_post_init__: The name of the post-init method for the model, if defined.
  • __pydantic_root_model__: Whether the model is a RootModel.
  • __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.
  • __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.
  • __pydantic_extra__: An instance attribute with the values of extra fields from validation when model_config['extra'] == 'allow'.
  • __pydantic_fields_set__: An instance attribute with the names of fields explicitly set.
  • __pydantic_private__: Instance attribute with the values of private attributes set on the model instance.
@classmethod
def validator(cls) -> classmethod:
179    @classmethod
180    def validator(cls) -> classmethod:
181        def _time_column_validator(v: t.Any, values: t.Any) -> TimeColumn:
182            dialect = get_dialect(values)
183
184            if isinstance(v, exp.Tuple):
185                column_expr = v.expressions[0]
186                column = (
187                    exp.column(column_expr)
188                    if isinstance(column_expr, exp.Identifier)
189                    else column_expr
190                )
191                format = v.expressions[1].name if len(v.expressions) > 1 else None
192            elif isinstance(v, exp.Expression):
193                column = exp.column(v) if isinstance(v, exp.Identifier) else v
194                format = None
195            elif isinstance(v, str):
196                column = d.parse_one(v, dialect=dialect)
197                column.meta.pop("sql")
198                format = None
199            elif isinstance(v, dict):
200                column_raw = v["column"]
201                column = (
202                    d.parse_one(column_raw, dialect=dialect)
203                    if isinstance(column_raw, str)
204                    else column_raw
205                )
206                format = v.get("format")
207            elif isinstance(v, TimeColumn):
208                column = v.column
209                format = v.format
210            else:
211                raise ConfigError(f"Invalid time_column: '{v}'.")
212
213            column = quote_identifiers(
214                normalize_identifiers(column, dialect=dialect), dialect=dialect
215            )
216            column.meta["dialect"] = dialect
217
218            return TimeColumn(column=column, format=format)
219
220        return field_validator("time_column", mode="before")(_time_column_validator)
expression: sqlglot.expressions.Expression

Convert this pydantic model into a time_column SQLGlot expression.

def to_expression(self, dialect: str) -> sqlglot.expressions.Expression:
239    def to_expression(self, dialect: str) -> exp.Expression:
240        """Convert this pydantic model into a time_column SQLGlot expression."""
241        if not self.format:
242            return self.column
243
244        return exp.Tuple(
245            expressions=[
246                self.column,
247                exp.Literal.string(
248                    format_time(self.format, d.Dialect.get_or_raise(dialect).INVERSE_TIME_MAPPING)
249                ),
250            ]
251        )

Convert this pydantic model into a time_column SQLGlot expression.

def to_property(self, dialect: str = '') -> sqlglot.expressions.Property:
253    def to_property(self, dialect: str = "") -> exp.Property:
254        return exp.Property(this="time_column", value=self.to_expression(dialect))
Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_rebuild
model_validate
model_validate_json
model_validate_strings
parse_file
from_orm
construct
schema
schema_json
validate
update_forward_refs
sqlmesh.utils.pydantic.PydanticModel
dict
json
copy
parse_obj
parse_raw
missing_required_fields
extra_fields
all_fields
all_field_infos
required_fields
model_post_init
def kind_dialect_validator(unknown):

Wrap a classmethod, staticmethod, property or unbound function and act as a descriptor that allows us to detect decorated items from the class' attributes.

This class' __get__ returns the wrapped item's __get__ result, which makes it transparent for classmethods and staticmethods.

Attributes:
  • wrapped: The decorator that has to be wrapped.
  • decorator_info: The decorator info.
  • shim: A wrapper function to wrap V1 style function.
class IncrementalByTimeRangeKind(_Incremental):
296class IncrementalByTimeRangeKind(_Incremental):
297    name: Literal[ModelKindName.INCREMENTAL_BY_TIME_RANGE] = ModelKindName.INCREMENTAL_BY_TIME_RANGE
298    time_column: TimeColumn
299
300    _time_column_validator = TimeColumn.validator()
301
302    def to_expression(self, dialect: str = "", **kwargs: t.Any) -> d.ModelKind:
303        return super().to_expression(expressions=[self.time_column.to_property(dialect)])
304
305    @property
306    def data_hash_values(self) -> t.List[t.Optional[str]]:
307        return [*super().data_hash_values, gen(self.time_column.column), self.time_column.format]

Usage docs: https://docs.pydantic.dev/2.7/concepts/models/

A base class for creating Pydantic models.

Attributes:
  • __class_vars__: The names of classvars defined on the model.
  • __private_attributes__: Metadata about the private attributes of the model.
  • __signature__: The signature for instantiating the model.
  • __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
  • __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
  • __pydantic_custom_init__: Whether the model has a custom __init__ function.
  • __pydantic_decorators__: Metadata containing the decorators defined on the model. This replaces Model.__validators__ and Model.__root_validators__ from Pydantic V1.
  • __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
  • __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
  • __pydantic_post_init__: The name of the post-init method for the model, if defined.
  • __pydantic_root_model__: Whether the model is a RootModel.
  • __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.
  • __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.
  • __pydantic_extra__: An instance attribute with the values of extra fields from validation when model_config['extra'] == 'allow'.
  • __pydantic_fields_set__: An instance attribute with the names of fields explicitly set.
  • __pydantic_private__: Instance attribute with the values of private attributes set on the model instance.
def to_expression(self, dialect: str = '', **kwargs: Any) -> sqlmesh.core.dialect.ModelKind:
302    def to_expression(self, dialect: str = "", **kwargs: t.Any) -> d.ModelKind:
303        return super().to_expression(expressions=[self.time_column.to_property(dialect)])
Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_rebuild
model_validate
model_validate_json
model_validate_strings
parse_file
from_orm
construct
schema
schema_json
validate
update_forward_refs
_ModelKind
model_kind_name
sqlmesh.utils.pydantic.PydanticModel
dict
json
copy
parse_obj
parse_raw
missing_required_fields
extra_fields
all_fields
all_field_infos
required_fields
model_post_init
ModelKindMixin
is_symbolic
only_execution_time
full_history_restatement_only
class IncrementalByUniqueKeyKind(_Incremental):
310class IncrementalByUniqueKeyKind(_Incremental):
311    name: Literal[ModelKindName.INCREMENTAL_BY_UNIQUE_KEY] = ModelKindName.INCREMENTAL_BY_UNIQUE_KEY
312    unique_key: SQLGlotListOfFields
313    when_matched: t.Optional[exp.When] = None
314    batch_concurrency: Literal[1] = 1
315
316    @field_validator("when_matched", mode="before")
317    @field_validator_v1_args
318    def _when_matched_validator(
319        cls, v: t.Optional[t.Union[exp.When, str]], values: t.Dict[str, t.Any]
320    ) -> t.Optional[exp.When]:
321        def replace_table_references(expression: exp.Expression) -> exp.Expression:
322            from sqlmesh.core.engine_adapter.base import (
323                MERGE_SOURCE_ALIAS,
324                MERGE_TARGET_ALIAS,
325            )
326
327            if isinstance(expression, exp.Column):
328                if expression.table.lower() == "target":
329                    expression.set(
330                        "table",
331                        exp.to_identifier(MERGE_TARGET_ALIAS),
332                    )
333                elif expression.table.lower() == "source":
334                    expression.set(
335                        "table",
336                        exp.to_identifier(MERGE_SOURCE_ALIAS),
337                    )
338            return expression
339
340        if isinstance(v, str):
341            return t.cast(exp.When, d.parse_one(v, into=exp.When, dialect=get_dialect(values)))
342
343        if not v:
344            return v
345
346        return t.cast(exp.When, v.transform(replace_table_references))
347
348    @property
349    def data_hash_values(self) -> t.List[t.Optional[str]]:
350        return [
351            *super().data_hash_values,
352            *(gen(k) for k in self.unique_key),
353            gen(self.when_matched) if self.when_matched is not None else None,
354        ]

Usage docs: https://docs.pydantic.dev/2.7/concepts/models/

A base class for creating Pydantic models.

Attributes:
  • __class_vars__: The names of classvars defined on the model.
  • __private_attributes__: Metadata about the private attributes of the model.
  • __signature__: The signature for instantiating the model.
  • __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
  • __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
  • __pydantic_custom_init__: Whether the model has a custom __init__ function.
  • __pydantic_decorators__: Metadata containing the decorators defined on the model. This replaces Model.__validators__ and Model.__root_validators__ from Pydantic V1.
  • __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
  • __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
  • __pydantic_post_init__: The name of the post-init method for the model, if defined.
  • __pydantic_root_model__: Whether the model is a RootModel.
  • __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.
  • __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.
  • __pydantic_extra__: An instance attribute with the values of extra fields from validation when model_config['extra'] == 'allow'.
  • __pydantic_fields_set__: An instance attribute with the names of fields explicitly set.
  • __pydantic_private__: Instance attribute with the values of private attributes set on the model instance.
Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_rebuild
model_validate
model_validate_json
model_validate_strings
parse_file
from_orm
construct
schema
schema_json
validate
update_forward_refs
_ModelKind
model_kind_name
to_expression
sqlmesh.utils.pydantic.PydanticModel
dict
json
copy
parse_obj
parse_raw
missing_required_fields
extra_fields
all_fields
all_field_infos
required_fields
model_post_init
ModelKindMixin
is_symbolic
only_execution_time
full_history_restatement_only
class IncrementalUnmanagedKind(_ModelKind):
357class IncrementalUnmanagedKind(_ModelKind):
358    name: Literal[ModelKindName.INCREMENTAL_UNMANAGED] = ModelKindName.INCREMENTAL_UNMANAGED
359    insert_overwrite: SQLGlotBool = False
360    forward_only: SQLGlotBool = True
361    disable_restatement: SQLGlotBool = True
362
363    @property
364    def data_hash_values(self) -> t.List[t.Optional[str]]:
365        return [*super().data_hash_values, str(self.insert_overwrite)]
366
367    @property
368    def metadata_hash_values(self) -> t.List[t.Optional[str]]:
369        return [*super().metadata_hash_values, str(self.forward_only)]

Usage docs: https://docs.pydantic.dev/2.7/concepts/models/

A base class for creating Pydantic models.

Attributes:
  • __class_vars__: The names of classvars defined on the model.
  • __private_attributes__: Metadata about the private attributes of the model.
  • __signature__: The signature for instantiating the model.
  • __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
  • __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
  • __pydantic_custom_init__: Whether the model has a custom __init__ function.
  • __pydantic_decorators__: Metadata containing the decorators defined on the model. This replaces Model.__validators__ and Model.__root_validators__ from Pydantic V1.
  • __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
  • __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
  • __pydantic_post_init__: The name of the post-init method for the model, if defined.
  • __pydantic_root_model__: Whether the model is a RootModel.
  • __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.
  • __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.
  • __pydantic_extra__: An instance attribute with the values of extra fields from validation when model_config['extra'] == 'allow'.
  • __pydantic_fields_set__: An instance attribute with the names of fields explicitly set.
  • __pydantic_private__: Instance attribute with the values of private attributes set on the model instance.
Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_rebuild
model_validate
model_validate_json
model_validate_strings
parse_file
from_orm
construct
schema
schema_json
validate
update_forward_refs
_ModelKind
model_kind_name
to_expression
sqlmesh.utils.pydantic.PydanticModel
dict
json
copy
parse_obj
parse_raw
missing_required_fields
extra_fields
all_fields
all_field_infos
required_fields
model_post_init
ModelKindMixin
is_symbolic
only_execution_time
full_history_restatement_only
class ViewKind(_ModelKind):
372class ViewKind(_ModelKind):
373    name: Literal[ModelKindName.VIEW] = ModelKindName.VIEW
374    materialized: SQLGlotBool = False
375
376    @property
377    def data_hash_values(self) -> t.List[t.Optional[str]]:
378        return [*super().data_hash_values, str(self.materialized)]

Usage docs: https://docs.pydantic.dev/2.7/concepts/models/

A base class for creating Pydantic models.

Attributes:
  • __class_vars__: The names of classvars defined on the model.
  • __private_attributes__: Metadata about the private attributes of the model.
  • __signature__: The signature for instantiating the model.
  • __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
  • __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
  • __pydantic_custom_init__: Whether the model has a custom __init__ function.
  • __pydantic_decorators__: Metadata containing the decorators defined on the model. This replaces Model.__validators__ and Model.__root_validators__ from Pydantic V1.
  • __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
  • __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
  • __pydantic_post_init__: The name of the post-init method for the model, if defined.
  • __pydantic_root_model__: Whether the model is a RootModel.
  • __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.
  • __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.
  • __pydantic_extra__: An instance attribute with the values of extra fields from validation when model_config['extra'] == 'allow'.
  • __pydantic_fields_set__: An instance attribute with the names of fields explicitly set.
  • __pydantic_private__: Instance attribute with the values of private attributes set on the model instance.
Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_rebuild
model_validate
model_validate_json
model_validate_strings
parse_file
from_orm
construct
schema
schema_json
validate
update_forward_refs
_ModelKind
model_kind_name
to_expression
sqlmesh.utils.pydantic.PydanticModel
dict
json
copy
parse_obj
parse_raw
missing_required_fields
extra_fields
all_fields
all_field_infos
required_fields
model_post_init
ModelKindMixin
is_symbolic
only_execution_time
full_history_restatement_only
class SeedKind(_ModelKind):
381class SeedKind(_ModelKind):
382    name: Literal[ModelKindName.SEED] = ModelKindName.SEED
383    path: SQLGlotString
384    batch_size: SQLGlotPositiveInt = 1000
385    csv_settings: t.Optional[CsvSettings] = None
386
387    @field_validator("csv_settings", mode="before")
388    @classmethod
389    def _parse_csv_settings(cls, v: t.Any) -> t.Optional[CsvSettings]:
390        if v is None or isinstance(v, CsvSettings):
391            return v
392        if isinstance(v, exp.Expression):
393            tuple_exp = parse_properties(cls, v, {})
394            if not tuple_exp:
395                return None
396            return CsvSettings(**{e.left.name: e.right for e in tuple_exp.expressions})
397        if isinstance(v, dict):
398            return CsvSettings(**v)
399        return v
400
401    def to_expression(self, **kwargs: t.Any) -> d.ModelKind:
402        """Convert the seed kind into a SQLGlot expression."""
403        return super().to_expression(
404            expressions=[
405                exp.Property(this=exp.Var(this="path"), value=exp.Literal.string(self.path)),
406                exp.Property(
407                    this=exp.Var(this="batch_size"),
408                    value=exp.Literal.number(self.batch_size),
409                ),
410            ],
411        )
412
413    @property
414    def data_hash_values(self) -> t.List[t.Optional[str]]:
415        return [
416            *super().data_hash_values,
417            *(self.csv_settings or CsvSettings()).dict().values(),
418        ]
419
420    @property
421    def metadata_hash_values(self) -> t.List[t.Optional[str]]:
422        return [*super().metadata_hash_values, str(self.batch_size)]

Usage docs: https://docs.pydantic.dev/2.7/concepts/models/

A base class for creating Pydantic models.

Attributes:
  • __class_vars__: The names of classvars defined on the model.
  • __private_attributes__: Metadata about the private attributes of the model.
  • __signature__: The signature for instantiating the model.
  • __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
  • __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
  • __pydantic_custom_init__: Whether the model has a custom __init__ function.
  • __pydantic_decorators__: Metadata containing the decorators defined on the model. This replaces Model.__validators__ and Model.__root_validators__ from Pydantic V1.
  • __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
  • __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
  • __pydantic_post_init__: The name of the post-init method for the model, if defined.
  • __pydantic_root_model__: Whether the model is a RootModel.
  • __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.
  • __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.
  • __pydantic_extra__: An instance attribute with the values of extra fields from validation when model_config['extra'] == 'allow'.
  • __pydantic_fields_set__: An instance attribute with the names of fields explicitly set.
  • __pydantic_private__: Instance attribute with the values of private attributes set on the model instance.
def to_expression(self, **kwargs: Any) -> sqlmesh.core.dialect.ModelKind:
401    def to_expression(self, **kwargs: t.Any) -> d.ModelKind:
402        """Convert the seed kind into a SQLGlot expression."""
403        return super().to_expression(
404            expressions=[
405                exp.Property(this=exp.Var(this="path"), value=exp.Literal.string(self.path)),
406                exp.Property(
407                    this=exp.Var(this="batch_size"),
408                    value=exp.Literal.number(self.batch_size),
409                ),
410            ],
411        )

Convert the seed kind into a SQLGlot expression.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_rebuild
model_validate
model_validate_json
model_validate_strings
parse_file
from_orm
construct
schema
schema_json
validate
update_forward_refs
_ModelKind
model_kind_name
sqlmesh.utils.pydantic.PydanticModel
dict
json
copy
parse_obj
parse_raw
missing_required_fields
extra_fields
all_fields
all_field_infos
required_fields
model_post_init
ModelKindMixin
is_symbolic
only_execution_time
full_history_restatement_only
class FullKind(_ModelKind):
425class FullKind(_ModelKind):
426    name: Literal[ModelKindName.FULL] = ModelKindName.FULL

Usage docs: https://docs.pydantic.dev/2.7/concepts/models/

A base class for creating Pydantic models.

Attributes:
  • __class_vars__: The names of classvars defined on the model.
  • __private_attributes__: Metadata about the private attributes of the model.
  • __signature__: The signature for instantiating the model.
  • __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
  • __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
  • __pydantic_custom_init__: Whether the model has a custom __init__ function.
  • __pydantic_decorators__: Metadata containing the decorators defined on the model. This replaces Model.__validators__ and Model.__root_validators__ from Pydantic V1.
  • __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
  • __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
  • __pydantic_post_init__: The name of the post-init method for the model, if defined.
  • __pydantic_root_model__: Whether the model is a RootModel.
  • __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.
  • __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.
  • __pydantic_extra__: An instance attribute with the values of extra fields from validation when model_config['extra'] == 'allow'.
  • __pydantic_fields_set__: An instance attribute with the names of fields explicitly set.
  • __pydantic_private__: Instance attribute with the values of private attributes set on the model instance.
Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_rebuild
model_validate
model_validate_json
model_validate_strings
parse_file
from_orm
construct
schema
schema_json
validate
update_forward_refs
_ModelKind
model_kind_name
to_expression
sqlmesh.utils.pydantic.PydanticModel
dict
json
copy
parse_obj
parse_raw
missing_required_fields
extra_fields
all_fields
all_field_infos
required_fields
model_post_init
ModelKindMixin
is_symbolic
only_execution_time
full_history_restatement_only
class SCDType2ByTimeKind(_SCDType2Kind):
487class SCDType2ByTimeKind(_SCDType2Kind):
488    name: Literal[ModelKindName.SCD_TYPE_2, ModelKindName.SCD_TYPE_2_BY_TIME] = (
489        ModelKindName.SCD_TYPE_2_BY_TIME
490    )
491    updated_at_name: SQLGlotColumn = Field(exp.column("updated_at"), validate_default=True)
492    updated_at_as_valid_from: SQLGlotBool = False
493
494    # Remove once Pydantic 1 is deprecated
495    _always_validate_updated_at = field_validator("updated_at_name", mode="before", always=True)(
496        column_validator
497    )
498
499    @property
500    def data_hash_values(self) -> t.List[t.Optional[str]]:
501        return [
502            *super().data_hash_values,
503            gen(self.updated_at_name),
504            str(self.updated_at_as_valid_from),
505        ]

Usage docs: https://docs.pydantic.dev/2.7/concepts/models/

A base class for creating Pydantic models.

Attributes:
  • __class_vars__: The names of classvars defined on the model.
  • __private_attributes__: Metadata about the private attributes of the model.
  • __signature__: The signature for instantiating the model.
  • __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
  • __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
  • __pydantic_custom_init__: Whether the model has a custom __init__ function.
  • __pydantic_decorators__: Metadata containing the decorators defined on the model. This replaces Model.__validators__ and Model.__root_validators__ from Pydantic V1.
  • __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
  • __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
  • __pydantic_post_init__: The name of the post-init method for the model, if defined.
  • __pydantic_root_model__: Whether the model is a RootModel.
  • __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.
  • __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.
  • __pydantic_extra__: An instance attribute with the values of extra fields from validation when model_config['extra'] == 'allow'.
  • __pydantic_fields_set__: An instance attribute with the names of fields explicitly set.
  • __pydantic_private__: Instance attribute with the values of private attributes set on the model instance.
Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_rebuild
model_validate
model_validate_json
model_validate_strings
parse_file
from_orm
construct
schema
schema_json
validate
update_forward_refs
_ModelKind
model_kind_name
to_expression
sqlmesh.utils.pydantic.PydanticModel
dict
json
copy
parse_obj
parse_raw
missing_required_fields
extra_fields
all_fields
all_field_infos
required_fields
model_post_init
ModelKindMixin
is_symbolic
only_execution_time
full_history_restatement_only
class SCDType2ByColumnKind(_SCDType2Kind):
508class SCDType2ByColumnKind(_SCDType2Kind):
509    name: Literal[ModelKindName.SCD_TYPE_2_BY_COLUMN] = ModelKindName.SCD_TYPE_2_BY_COLUMN
510    columns: SQLGlotListOfColumnsOrStar
511    execution_time_as_valid_from: SQLGlotBool = False
512
513    @property
514    def data_hash_values(self) -> t.List[t.Optional[str]]:
515        columns_sql = (
516            [gen(c) for c in self.columns]
517            if isinstance(self.columns, list)
518            else [gen(self.columns)]
519        )
520        return [*super().data_hash_values, *columns_sql, str(self.execution_time_as_valid_from)]

Usage docs: https://docs.pydantic.dev/2.7/concepts/models/

A base class for creating Pydantic models.

Attributes:
  • __class_vars__: The names of classvars defined on the model.
  • __private_attributes__: Metadata about the private attributes of the model.
  • __signature__: The signature for instantiating the model.
  • __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
  • __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
  • __pydantic_custom_init__: Whether the model has a custom __init__ function.
  • __pydantic_decorators__: Metadata containing the decorators defined on the model. This replaces Model.__validators__ and Model.__root_validators__ from Pydantic V1.
  • __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
  • __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
  • __pydantic_post_init__: The name of the post-init method for the model, if defined.
  • __pydantic_root_model__: Whether the model is a RootModel.
  • __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.
  • __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.
  • __pydantic_extra__: An instance attribute with the values of extra fields from validation when model_config['extra'] == 'allow'.
  • __pydantic_fields_set__: An instance attribute with the names of fields explicitly set.
  • __pydantic_private__: Instance attribute with the values of private attributes set on the model instance.
Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_rebuild
model_validate
model_validate_json
model_validate_strings
parse_file
from_orm
construct
schema
schema_json
validate
update_forward_refs
_ModelKind
model_kind_name
to_expression
sqlmesh.utils.pydantic.PydanticModel
dict
json
copy
parse_obj
parse_raw
missing_required_fields
extra_fields
all_fields
all_field_infos
required_fields
model_post_init
ModelKindMixin
is_symbolic
only_execution_time
full_history_restatement_only
class EmbeddedKind(_ModelKind):
523class EmbeddedKind(_ModelKind):
524    name: Literal[ModelKindName.EMBEDDED] = ModelKindName.EMBEDDED

Usage docs: https://docs.pydantic.dev/2.7/concepts/models/

A base class for creating Pydantic models.

Attributes:
  • __class_vars__: The names of classvars defined on the model.
  • __private_attributes__: Metadata about the private attributes of the model.
  • __signature__: The signature for instantiating the model.
  • __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
  • __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
  • __pydantic_custom_init__: Whether the model has a custom __init__ function.
  • __pydantic_decorators__: Metadata containing the decorators defined on the model. This replaces Model.__validators__ and Model.__root_validators__ from Pydantic V1.
  • __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
  • __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
  • __pydantic_post_init__: The name of the post-init method for the model, if defined.
  • __pydantic_root_model__: Whether the model is a RootModel.
  • __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.
  • __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.
  • __pydantic_extra__: An instance attribute with the values of extra fields from validation when model_config['extra'] == 'allow'.
  • __pydantic_fields_set__: An instance attribute with the names of fields explicitly set.
  • __pydantic_private__: Instance attribute with the values of private attributes set on the model instance.
Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_rebuild
model_validate
model_validate_json
model_validate_strings
parse_file
from_orm
construct
schema
schema_json
validate
update_forward_refs
_ModelKind
model_kind_name
to_expression
sqlmesh.utils.pydantic.PydanticModel
dict
json
copy
parse_obj
parse_raw
missing_required_fields
extra_fields
all_fields
all_field_infos
required_fields
model_post_init
ModelKindMixin
is_symbolic
only_execution_time
full_history_restatement_only
class ExternalKind(_ModelKind):
527class ExternalKind(_ModelKind):
528    name: Literal[ModelKindName.EXTERNAL] = ModelKindName.EXTERNAL

Usage docs: https://docs.pydantic.dev/2.7/concepts/models/

A base class for creating Pydantic models.

Attributes:
  • __class_vars__: The names of classvars defined on the model.
  • __private_attributes__: Metadata about the private attributes of the model.
  • __signature__: The signature for instantiating the model.
  • __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
  • __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
  • __pydantic_custom_init__: Whether the model has a custom __init__ function.
  • __pydantic_decorators__: Metadata containing the decorators defined on the model. This replaces Model.__validators__ and Model.__root_validators__ from Pydantic V1.
  • __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
  • __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
  • __pydantic_post_init__: The name of the post-init method for the model, if defined.
  • __pydantic_root_model__: Whether the model is a RootModel.
  • __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.
  • __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.
  • __pydantic_extra__: An instance attribute with the values of extra fields from validation when model_config['extra'] == 'allow'.
  • __pydantic_fields_set__: An instance attribute with the names of fields explicitly set.
  • __pydantic_private__: Instance attribute with the values of private attributes set on the model instance.
Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_rebuild
model_validate
model_validate_json
model_validate_strings
parse_file
from_orm
construct
schema
schema_json
validate
update_forward_refs
_ModelKind
model_kind_name
to_expression
sqlmesh.utils.pydantic.PydanticModel
dict
json
copy
parse_obj
parse_raw
missing_required_fields
extra_fields
all_fields
all_field_infos
required_fields
model_post_init
ModelKindMixin
is_symbolic
only_execution_time
full_history_restatement_only
562def model_kind_type_from_name(name: t.Optional[str]) -> t.Type[ModelKind]:
563    klass = MODEL_KIND_NAME_TO_TYPE.get(name) if name else None
564    if not klass:
565        raise ConfigError(f"Invalid model kind '{name}'")
566    return t.cast(t.Type[ModelKind], klass)
def model_kind_validator(unknown):

Wrap a classmethod, staticmethod, property or unbound function and act as a descriptor that allows us to detect decorated items from the class' attributes.

This class' __get__ returns the wrapped item's __get__ result, which makes it transparent for classmethods and staticmethods.

Attributes:
  • wrapped: The decorator that has to be wrapped.
  • decorator_info: The decorator info.
  • shim: A wrapper function to wrap V1 style function.