Skip to content

Commit 77b6b20

Browse files
authored
Merge branch 'main' into clickhouse-secure-field
2 parents 375ba7e + 3be5bba commit 77b6b20

38 files changed

Lines changed: 562 additions & 125 deletions

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ dependencies = [
2424
"requests",
2525
"rich[jupyter]",
2626
"ruamel.yaml",
27-
"sqlglot~=30.0.1",
27+
"sqlglot~=30.4.2",
2828
"tenacity",
2929
"time-machine",
3030
"json-stream"

sqlmesh/core/config/connection.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
ValidationInfo,
3535
field_validator,
3636
model_validator,
37+
validation_data,
3738
validation_error_message,
3839
get_concrete_types_from_typehint,
3940
)
@@ -1081,7 +1082,7 @@ def validate_execution_project(
10811082
v: t.Optional[str],
10821083
info: ValidationInfo,
10831084
) -> t.Optional[str]:
1084-
if v and not info.data.get("project"):
1085+
if v and not validation_data(info).get("project"):
10851086
raise ConfigError(
10861087
"If the `execution_project` field is specified, you must also specify the `project` field to provide a default object location."
10871088
)
@@ -1093,7 +1094,7 @@ def validate_quota_project(
10931094
v: t.Optional[str],
10941095
info: ValidationInfo,
10951096
) -> t.Optional[str]:
1096-
if v and not info.data.get("project"):
1097+
if v and not validation_data(info).get("project"):
10971098
raise ConfigError(
10981099
"If the `quota_project` field is specified, you must also specify the `project` field to provide a default object location."
10991100
)
@@ -2343,35 +2344,28 @@ def init(cursor: t.Any) -> None:
23432344
return init
23442345

23452346

2347+
_CONNECTION_CONFIG_EXCLUDE: t.Set[t.Type[ConnectionConfig]] = {
2348+
ConnectionConfig, # type: ignore[type-abstract]
2349+
BaseDuckDBConnectionConfig, # type: ignore[type-abstract]
2350+
}
2351+
23462352
CONNECTION_CONFIG_TO_TYPE = {
23472353
# Map all subclasses of ConnectionConfig to the value of their `type_` field.
23482354
tpe.all_field_infos()["type_"].default: tpe
2349-
for tpe in subclasses(
2350-
__name__,
2351-
ConnectionConfig,
2352-
exclude={ConnectionConfig, BaseDuckDBConnectionConfig},
2353-
)
2355+
for tpe in subclasses(__name__, ConnectionConfig, exclude=_CONNECTION_CONFIG_EXCLUDE)
23542356
}
23552357

23562358
DIALECT_TO_TYPE = {
23572359
tpe.all_field_infos()["type_"].default: tpe.DIALECT
2358-
for tpe in subclasses(
2359-
__name__,
2360-
ConnectionConfig,
2361-
exclude={ConnectionConfig, BaseDuckDBConnectionConfig},
2362-
)
2360+
for tpe in subclasses(__name__, ConnectionConfig, exclude=_CONNECTION_CONFIG_EXCLUDE)
23632361
}
23642362

23652363
INIT_DISPLAY_INFO_TO_TYPE = {
23662364
tpe.all_field_infos()["type_"].default: (
23672365
tpe.DISPLAY_ORDER,
23682366
tpe.DISPLAY_NAME,
23692367
)
2370-
for tpe in subclasses(
2371-
__name__,
2372-
ConnectionConfig,
2373-
exclude={ConnectionConfig, BaseDuckDBConnectionConfig},
2374-
)
2368+
for tpe in subclasses(__name__, ConnectionConfig, exclude=_CONNECTION_CONFIG_EXCLUDE)
23752369
}
23762370

23772371

sqlmesh/core/config/scheduler.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,10 @@ def get_default_catalog_per_gateway(self, context: GenericContext) -> t.Dict[str
144144
return default_catalogs_per_gateway
145145

146146

147-
SCHEDULER_CONFIG_TO_TYPE = {
147+
SCHEDULER_CONFIG_TO_TYPE: t.Dict[str, t.Type[SchedulerConfig]] = {
148148
tpe.all_field_infos()["type_"].default: tpe
149149
for tpe in subclasses(__name__, BaseConfig, exclude={BaseConfig})
150+
if issubclass(tpe, SchedulerConfig)
150151
}
151152

152153

sqlmesh/core/context.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,9 +1605,11 @@ def plan_builder(
16051605
backfill_models = None
16061606

16071607
models_override: t.Optional[UniqueKeyDict[str, Model]] = None
1608+
selected_fqns: t.Set[str] = set()
1609+
selected_deletion_fqns: t.Set[str] = set()
16081610
if select_models:
16091611
try:
1610-
models_override = model_selector.select_models(
1612+
models_override, selected_fqns = model_selector.select_models(
16111613
select_models,
16121614
environment,
16131615
fallback_env_name=create_from or c.PROD,
@@ -1622,12 +1624,17 @@ def plan_builder(
16221624
# Only backfill selected models unless explicitly specified.
16231625
backfill_models = model_selector.expand_model_selections(select_models)
16241626

1627+
if not backfill_models:
1628+
# The selection matched nothing locally. Check whether it matched models
1629+
# in the deployed environment that were deleted locally.
1630+
selected_deletion_fqns = selected_fqns - set(self._models)
1631+
16251632
expanded_restate_models = None
16261633
if restate_models is not None:
16271634
expanded_restate_models = model_selector.expand_model_selections(restate_models)
16281635

16291636
if (restate_models is not None and not expanded_restate_models) or (
1630-
backfill_models is not None and not backfill_models
1637+
backfill_models is not None and not backfill_models and not selected_deletion_fqns
16311638
):
16321639
raise PlanError(
16331640
"Selector did not return any models. Please check your model selection and try again."
@@ -1636,7 +1643,7 @@ def plan_builder(
16361643
if always_include_local_changes is None:
16371644
# default behaviour - if restatements are detected; we operate entirely out of state and ignore local changes
16381645
force_no_diff = restate_models is not None or (
1639-
backfill_models is not None and not backfill_models
1646+
backfill_models is not None and not backfill_models and not selected_deletion_fqns
16401647
)
16411648
else:
16421649
force_no_diff = not always_include_local_changes

sqlmesh/core/dialect.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from sqlglot.dialects.dialect import DialectType
1515
from sqlglot.dialects import DuckDB, Snowflake, TSQL
1616
import sqlglot.dialects.athena as athena
17+
import sqlglot.generators.athena as athena_generators
1718
from sqlglot.parsers.athena import AthenaTrinoParser
1819
from sqlglot.helper import seq_get
1920
from sqlglot.optimizer.normalize_identifiers import normalize_identifiers
@@ -1048,8 +1049,8 @@ def extend_sqlglot() -> None:
10481049
if dialect == athena.Athena:
10491050
tokenizers.add(athena._TrinoTokenizer)
10501051
parsers.add(AthenaTrinoParser)
1051-
generators.add(athena._TrinoGenerator)
1052-
generators.add(athena._HiveGenerator)
1052+
generators.add(athena_generators.AthenaTrinoGenerator)
1053+
generators.add(athena_generators._HiveGenerator)
10531054

10541055
if hasattr(dialect, "Tokenizer"):
10551056
tokenizers.add(dialect.Tokenizer)

sqlmesh/core/engine_adapter/base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2920,7 +2920,11 @@ def _replace_by_key(
29202920
target_columns_to_types = self.columns(target_table)
29212921

29222922
temp_table = self._get_temp_table(target_table)
2923-
key_exp = exp.func("CONCAT_WS", "'__SQLMESH_DELIM__'", *key) if len(key) > 1 else key[0]
2923+
key_exp = (
2924+
exp.func("CONCAT_WS", "'__SQLMESH_DELIM__'", *key, dialect=self.dialect)
2925+
if len(key) > 1
2926+
else key[0]
2927+
)
29242928
column_names = list(target_columns_to_types or [])
29252929

29262930
with self.transaction():

sqlmesh/core/engine_adapter/clickhouse.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,11 @@ def _replace_by_key(
423423
source_columns=source_columns,
424424
)
425425

426-
key_exp = exp.func("CONCAT_WS", "'__SQLMESH_DELIM__'", *key) if len(key) > 1 else key[0]
426+
key_exp = (
427+
exp.func("CONCAT_WS", "'__SQLMESH_DELIM__'", *key, dialect=self.dialect)
428+
if len(key) > 1
429+
else key[0]
430+
)
427431

428432
self._insert_overwrite_by_condition(
429433
target_table,

sqlmesh/core/environment.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ def _sanitize_name(cls, v: str) -> str:
5656
@classmethod
5757
def _validate_boolean_field(cls, v: t.Any, info: ValidationInfo) -> bool:
5858
if v is None:
59-
return info.field_name == "normalize_name"
59+
# Pydantic 2.13+ sets field_name to None during model_validate_json()
60+
return (info.field_name or "") == "normalize_name"
6061
return bool(v)
6162

6263
@t.overload

sqlmesh/core/linter/rules/builtin.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,4 +318,5 @@ def check_model(self, model: Model) -> t.Optional[RuleViolation]:
318318
return None
319319

320320

321-
BUILTIN_RULES = RuleSet(subclasses(__name__, Rule, exclude={Rule}))
321+
_RULE_EXCLUDE: t.Set[t.Type[Rule]] = {Rule} # type: ignore[type-abstract]
322+
BUILTIN_RULES = RuleSet(subclasses(__name__, Rule, exclude=_RULE_EXCLUDE))

sqlmesh/core/loader.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,8 @@ def _load_linting_rules(self) -> RuleSet:
840840
if os.path.getsize(path):
841841
self._track_file(path)
842842
module = import_python_file(path, self.config_path)
843-
module_rules = subclasses(module.__name__, Rule, exclude={Rule})
843+
_rule_exclude: t.Set[t.Type[Rule]] = {Rule} # type: ignore[type-abstract]
844+
module_rules = subclasses(module.__name__, Rule, exclude=_rule_exclude)
844845
for user_rule in module_rules:
845846
user_rules[user_rule.name] = user_rule
846847

0 commit comments

Comments
 (0)