Skip to content

Commit 0bda7cb

Browse files
committed
refactor: add skip_grants flag to SnapshotEvaluator.create
1 parent 7e21ca0 commit 0bda7cb

2 files changed

Lines changed: 50 additions & 26 deletions

File tree

sqlmesh/core/snapshot/evaluator.py

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,7 @@ def create_snapshot(
888888
deployability_index=deployability_index,
889889
create_render_kwargs=create_render_kwargs,
890890
rendered_physical_properties=rendered_physical_properties,
891+
skip_grants=True,
891892
dry_run=True,
892893
)
893894

@@ -1450,12 +1451,12 @@ def _execute_create(
14501451
table_name=table_name,
14511452
model=snapshot.model,
14521453
is_table_deployable=is_table_deployable,
1454+
skip_grants=skip_grants,
14531455
render_kwargs=create_render_kwargs,
14541456
is_snapshot_deployable=is_snapshot_deployable,
14551457
is_snapshot_representative=is_snapshot_representative,
14561458
dry_run=dry_run,
14571459
physical_properties=rendered_physical_properties,
1458-
skip_grants=skip_grants,
14591460
)
14601461
if run_pre_post_statements:
14611462
evaluation_strategy.run_post_statements(
@@ -1636,6 +1637,7 @@ def create(
16361637
model: Model,
16371638
is_table_deployable: bool,
16381639
render_kwargs: t.Dict[str, t.Any],
1640+
skip_grants: bool,
16391641
**kwargs: t.Any,
16401642
) -> None:
16411643
"""Creates the target table or view.
@@ -1797,6 +1799,7 @@ def create(
17971799
model: Model,
17981800
is_table_deployable: bool,
17991801
render_kwargs: t.Dict[str, t.Any],
1802+
skip_grants: bool,
18001803
**kwargs: t.Any,
18011804
) -> None:
18021805
pass
@@ -1878,10 +1881,10 @@ def promote(
18781881
view_properties=model.render_virtual_properties(**render_kwargs),
18791882
)
18801883

1881-
# Apply grants to the physical layer table
1884+
# Apply grants to the physical layer (referenced table / view) after promotion
18821885
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
18831886

1884-
# Apply grants to the virtual layer view
1887+
# Apply grants to the virtual layer (view) after promotion
18851888
self._apply_grants(model, view_name, GrantsTargetLayer.VIRTUAL)
18861889

18871890
def demote(self, view_name: str, **kwargs: t.Any) -> None:
@@ -1902,6 +1905,7 @@ def create(
19021905
model: Model,
19031906
is_table_deployable: bool,
19041907
render_kwargs: t.Dict[str, t.Any],
1908+
skip_grants: bool,
19051909
**kwargs: t.Any,
19061910
) -> None:
19071911
ctas_query = model.ctas_query(**render_kwargs)
@@ -1947,7 +1951,7 @@ def create(
19471951
)
19481952

19491953
# Apply grants after table creation (unless explicitly skipped by caller)
1950-
if not kwargs.get("skip_grants", False):
1954+
if not skip_grants:
19511955
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
19521956

19531957
def migrate(
@@ -1989,6 +1993,7 @@ def _replace_query_for_model(
19891993
name: str,
19901994
query_or_df: QueryOrDF,
19911995
render_kwargs: t.Dict[str, t.Any],
1996+
skip_grants: bool = False,
19921997
**kwargs: t.Any,
19931998
) -> None:
19941999
"""Replaces the table for the given model.
@@ -2026,7 +2031,7 @@ def _replace_query_for_model(
20262031
)
20272032

20282033
# Apply grants after table replacement (unless explicitly skipped by caller)
2029-
if not kwargs.get("skip_grants", False):
2034+
if not skip_grants:
20302035
self._apply_grants(model, name, GrantsTargetLayer.PHYSICAL)
20312036

20322037
def _get_target_and_source_columns(
@@ -2276,6 +2281,7 @@ def create(
22762281
model: Model,
22772282
is_table_deployable: bool,
22782283
render_kwargs: t.Dict[str, t.Any],
2284+
skip_grants: bool,
22792285
**kwargs: t.Any,
22802286
) -> None:
22812287
model = t.cast(SeedModel, model)
@@ -2289,22 +2295,34 @@ def create(
22892295
)
22902296
return
22912297

2292-
# Skip grants in parent create call since we'll apply them after data insertion
2293-
kwargs_no_grants = {**kwargs}
2294-
kwargs_no_grants["skip_grants"] = True
2295-
2296-
super().create(table_name, model, is_table_deployable, render_kwargs, **kwargs_no_grants)
2298+
super().create(
2299+
table_name,
2300+
model,
2301+
is_table_deployable,
2302+
render_kwargs,
2303+
skip_grants=True, # Skip grants; they're applied after data insertion
2304+
**kwargs,
2305+
)
22972306
# For seeds we insert data at the time of table creation.
22982307
try:
22992308
for index, df in enumerate(model.render_seed()):
23002309
if index == 0:
23012310
self._replace_query_for_model(
2302-
model, table_name, df, render_kwargs, **kwargs_no_grants
2311+
model,
2312+
table_name,
2313+
df,
2314+
render_kwargs,
2315+
skip_grants=True, # Skip grants; they're applied after data insertion
2316+
**kwargs,
23032317
)
23042318
else:
23052319
self.adapter.insert_append(
23062320
table_name, df, target_columns_to_types=model.columns_to_types
23072321
)
2322+
2323+
if not skip_grants:
2324+
# Apply grants after seed table creation and data insertion
2325+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
23082326
except Exception:
23092327
self.adapter.drop_table(table_name)
23102328
raise
@@ -2352,6 +2370,7 @@ def create(
23522370
model: Model,
23532371
is_table_deployable: bool,
23542372
render_kwargs: t.Dict[str, t.Any],
2373+
skip_grants: bool,
23552374
**kwargs: t.Any,
23562375
) -> None:
23572376
assert isinstance(model.kind, (SCDType2ByTimeKind, SCDType2ByColumnKind))
@@ -2381,11 +2400,13 @@ def create(
23812400
model,
23822401
is_table_deployable,
23832402
render_kwargs,
2403+
skip_grants,
23842404
**kwargs,
23852405
)
23862406

2387-
# Apply grants after SCD Type 2 table creation
2388-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2407+
if not skip_grants:
2408+
# Apply grants after SCD Type 2 table creation
2409+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
23892410

23902411
def insert(
23912412
self,
@@ -2529,14 +2550,17 @@ def create(
25292550
model: Model,
25302551
is_table_deployable: bool,
25312552
render_kwargs: t.Dict[str, t.Any],
2553+
skip_grants: bool,
25322554
**kwargs: t.Any,
25332555
) -> None:
25342556
if self.adapter.table_exists(table_name):
25352557
# Make sure we don't recreate the view to prevent deletion of downstream views in engines with no late
25362558
# binding support (because of DROP CASCADE).
25372559
logger.info("View '%s' already exists", table_name)
2538-
# Always apply grants when present, even if view exists, to handle grants updates
2539-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2560+
2561+
if not skip_grants:
2562+
# Always apply grants when present, even if view exists, to handle grants updates
2563+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
25402564
return
25412565

25422566
logger.info("Creating view '%s'", table_name)
@@ -2560,8 +2584,9 @@ def create(
25602584
column_descriptions=model.column_descriptions if is_table_deployable else None,
25612585
)
25622586

2563-
# Apply grants after view creation
2564-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2587+
if not skip_grants:
2588+
# Apply grants after view creation
2589+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
25652590

25662591
def migrate(
25672592
self,
@@ -2877,6 +2902,7 @@ def create(
28772902
model: Model,
28782903
is_table_deployable: bool,
28792904
render_kwargs: t.Dict[str, t.Any],
2905+
skip_grants: bool,
28802906
**kwargs: t.Any,
28812907
) -> None:
28822908
is_snapshot_deployable: bool = kwargs["is_snapshot_deployable"]
@@ -2897,24 +2923,21 @@ def create(
28972923
)
28982924

28992925
# Apply grants after managed table creation
2900-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2926+
if not skip_grants:
2927+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
29012928

29022929
elif not is_table_deployable:
29032930
# Only create the dev preview table as a normal table.
29042931
# For the main table, if the snapshot is cant be deployed to prod (eg upstream is forward-only) do nothing.
29052932
# Any downstream models that reference it will be updated to point to the dev preview table.
29062933
# If the user eventually tries to deploy it, the logic in insert() will see it doesnt exist and create it
2907-
2908-
# Create preview table but don't apply grants here since the table is not deployable
2909-
# Grants will be applied later when the table becomes deployable
2910-
kwargs_no_grants = {**kwargs}
2911-
kwargs_no_grants["skip_grants"] = True
29122934
super().create(
29132935
table_name=table_name,
29142936
model=model,
29152937
is_table_deployable=is_table_deployable,
29162938
render_kwargs=render_kwargs,
2917-
**kwargs_no_grants,
2939+
skip_grants=skip_grants,
2940+
**kwargs,
29182941
)
29192942

29202943
def insert(
@@ -2941,6 +2964,7 @@ def insert(
29412964
column_descriptions=model.column_descriptions,
29422965
table_format=model.table_format,
29432966
)
2967+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
29442968
elif not is_snapshot_deployable:
29452969
# Snapshot isnt deployable; update the preview table instead
29462970
# If the snapshot was deployable, then data would have already been loaded in create() because a managed table would have been created
@@ -2990,7 +3014,7 @@ def migrate(
29903014
)
29913015

29923016
# Apply grants after verifying no schema changes
2993-
# This ensures metadata-only changes (grants) are applied
3017+
# This ensures metadata-only grants changes are applied
29943018
self._apply_grants(snapshot.model, target_table_name, GrantsTargetLayer.PHYSICAL)
29953019

29963020
def delete(self, name: str, **kwargs: t.Any) -> None:

tests/core/test_context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3192,7 +3192,6 @@ def test_grants_through_plan_apply(sushi_context, mocker):
31923192
update={
31933193
"grants": {"select": ["analyst", "reporter"]},
31943194
"grants_target_layer": GrantsTargetLayer.ALL,
3195-
"stamp": "add initial grants",
31963195
}
31973196
)
31983197
sushi_context.upsert_model(model_with_grants)
@@ -3219,5 +3218,6 @@ def test_grants_through_plan_apply(sushi_context, mocker):
32193218
sushi_context.plan("dev", no_prompts=True, auto_apply=True)
32203219

32213220
assert sync_grants_mock.call_count == 2
3221+
32223222
expected_grants = {"select": ["analyst", "reporter", "manager"], "insert": ["etl_user"]}
32233223
assert all(call[0][1] == expected_grants for call in sync_grants_mock.call_args_list)

0 commit comments

Comments
 (0)