Skip to content

Commit d8e6b4d

Browse files
committed
refactor: add skip_grants flag to SnapshotEvaluator.create
1 parent 1a41024 commit d8e6b4d

2 files changed

Lines changed: 50 additions & 29 deletions

File tree

sqlmesh/core/snapshot/evaluator.py

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,7 @@ def create_snapshot(
880880
deployability_index=deployability_index,
881881
create_render_kwargs=create_render_kwargs,
882882
rendered_physical_properties=rendered_physical_properties,
883+
skip_grants=True,
883884
dry_run=True,
884885
)
885886

@@ -1440,12 +1441,12 @@ def _execute_create(
14401441
table_name=table_name,
14411442
model=snapshot.model,
14421443
is_table_deployable=is_table_deployable,
1444+
skip_grants=skip_grants,
14431445
render_kwargs=create_render_kwargs,
14441446
is_snapshot_deployable=is_snapshot_deployable,
14451447
is_snapshot_representative=is_snapshot_representative,
14461448
dry_run=dry_run,
14471449
physical_properties=rendered_physical_properties,
1448-
skip_grants=skip_grants,
14491450
)
14501451
if run_pre_post_statements:
14511452
adapter.execute(snapshot.model.render_post_statements(**create_render_kwargs))
@@ -1610,6 +1611,7 @@ def create(
16101611
model: Model,
16111612
is_table_deployable: bool,
16121613
render_kwargs: t.Dict[str, t.Any],
1614+
skip_grants: bool,
16131615
**kwargs: t.Any,
16141616
) -> None:
16151617
"""Creates the target table or view.
@@ -1753,6 +1755,7 @@ def create(
17531755
model: Model,
17541756
is_table_deployable: bool,
17551757
render_kwargs: t.Dict[str, t.Any],
1758+
skip_grants: bool,
17561759
**kwargs: t.Any,
17571760
) -> None:
17581761
pass
@@ -1828,10 +1831,10 @@ def promote(
18281831
view_properties=model.render_virtual_properties(**render_kwargs),
18291832
)
18301833

1831-
# Apply grants to the physical layer table
1834+
# Apply grants to the physical layer (referenced table / view) after promotion
18321835
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
18331836

1834-
# Apply grants to the virtual layer view
1837+
# Apply grants to the virtual layer (view) after promotion
18351838
self._apply_grants(model, view_name, GrantsTargetLayer.VIRTUAL)
18361839

18371840
def demote(self, view_name: str, **kwargs: t.Any) -> None:
@@ -1846,6 +1849,7 @@ def create(
18461849
model: Model,
18471850
is_table_deployable: bool,
18481851
render_kwargs: t.Dict[str, t.Any],
1852+
skip_grants: bool,
18491853
**kwargs: t.Any,
18501854
) -> None:
18511855
ctas_query = model.ctas_query(**render_kwargs)
@@ -1891,7 +1895,7 @@ def create(
18911895
)
18921896

18931897
# Apply grants after table creation (unless explicitly skipped by caller)
1894-
if not kwargs.get("skip_grants", False):
1898+
if not skip_grants:
18951899
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
18961900

18971901
def migrate(
@@ -1933,6 +1937,7 @@ def _replace_query_for_model(
19331937
name: str,
19341938
query_or_df: QueryOrDF,
19351939
render_kwargs: t.Dict[str, t.Any],
1940+
skip_grants: bool = False,
19361941
**kwargs: t.Any,
19371942
) -> None:
19381943
"""Replaces the table for the given model.
@@ -1970,7 +1975,7 @@ def _replace_query_for_model(
19701975
)
19711976

19721977
# Apply grants after table replacement (unless explicitly skipped by caller)
1973-
if not kwargs.get("skip_grants", False):
1978+
if not skip_grants:
19741979
self._apply_grants(model, name, GrantsTargetLayer.PHYSICAL)
19751980

19761981
def _get_target_and_source_columns(
@@ -2220,6 +2225,7 @@ def create(
22202225
model: Model,
22212226
is_table_deployable: bool,
22222227
render_kwargs: t.Dict[str, t.Any],
2228+
skip_grants: bool,
22232229
**kwargs: t.Any,
22242230
) -> None:
22252231
model = t.cast(SeedModel, model)
@@ -2233,29 +2239,38 @@ def create(
22332239
)
22342240
return
22352241

2236-
# Skip grants in parent create call since we'll apply them after data insertion
2237-
kwargs_no_grants = {**kwargs}
2238-
kwargs_no_grants["skip_grants"] = True
2239-
2240-
super().create(table_name, model, is_table_deployable, render_kwargs, **kwargs_no_grants)
2242+
super().create(
2243+
table_name,
2244+
model,
2245+
is_table_deployable,
2246+
render_kwargs,
2247+
skip_grants=True, # Skip grants; they're applied after data insertion
2248+
**kwargs,
2249+
)
22412250
# For seeds we insert data at the time of table creation.
22422251
try:
22432252
for index, df in enumerate(model.render_seed()):
22442253
if index == 0:
22452254
self._replace_query_for_model(
2246-
model, table_name, df, render_kwargs, **kwargs_no_grants
2255+
model,
2256+
table_name,
2257+
df,
2258+
render_kwargs,
2259+
skip_grants=True, # Skip grants; they're applied after data insertion
2260+
**kwargs,
22472261
)
22482262
else:
22492263
self.adapter.insert_append(
22502264
table_name, df, target_columns_to_types=model.columns_to_types
22512265
)
2266+
2267+
if not skip_grants:
2268+
# Apply grants after seed table creation and data insertion
2269+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
22522270
except Exception:
22532271
self.adapter.drop_table(table_name)
22542272
raise
22552273

2256-
# Apply grants after seed table creation or data insertion
2257-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2258-
22592274
def insert(
22602275
self,
22612276
table_name: str,
@@ -2287,6 +2302,7 @@ def create(
22872302
model: Model,
22882303
is_table_deployable: bool,
22892304
render_kwargs: t.Dict[str, t.Any],
2305+
skip_grants: bool,
22902306
**kwargs: t.Any,
22912307
) -> None:
22922308
assert isinstance(model.kind, (SCDType2ByTimeKind, SCDType2ByColumnKind))
@@ -2316,11 +2332,13 @@ def create(
23162332
model,
23172333
is_table_deployable,
23182334
render_kwargs,
2335+
skip_grants,
23192336
**kwargs,
23202337
)
23212338

2322-
# Apply grants after SCD Type 2 table creation
2323-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2339+
if not skip_grants:
2340+
# Apply grants after SCD Type 2 table creation
2341+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
23242342

23252343
def insert(
23262344
self,
@@ -2464,14 +2482,17 @@ def create(
24642482
model: Model,
24652483
is_table_deployable: bool,
24662484
render_kwargs: t.Dict[str, t.Any],
2485+
skip_grants: bool,
24672486
**kwargs: t.Any,
24682487
) -> None:
24692488
if self.adapter.table_exists(table_name):
24702489
# Make sure we don't recreate the view to prevent deletion of downstream views in engines with no late
24712490
# binding support (because of DROP CASCADE).
24722491
logger.info("View '%s' already exists", table_name)
2473-
# Always apply grants when present, even if view exists, to handle grants updates
2474-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2492+
2493+
if not skip_grants:
2494+
# Always apply grants when present, even if view exists, to handle grants updates
2495+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
24752496
return
24762497

24772498
logger.info("Creating view '%s'", table_name)
@@ -2495,8 +2516,9 @@ def create(
24952516
column_descriptions=model.column_descriptions if is_table_deployable else None,
24962517
)
24972518

2498-
# Apply grants after view creation
2499-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2519+
if not skip_grants:
2520+
# Apply grants after view creation
2521+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
25002522

25012523
def migrate(
25022524
self,
@@ -2673,6 +2695,7 @@ def create(
26732695
model: Model,
26742696
is_table_deployable: bool,
26752697
render_kwargs: t.Dict[str, t.Any],
2698+
skip_grants: bool,
26762699
**kwargs: t.Any,
26772700
) -> None:
26782701
is_snapshot_deployable: bool = kwargs["is_snapshot_deployable"]
@@ -2693,24 +2716,21 @@ def create(
26932716
)
26942717

26952718
# Apply grants after managed table creation
2696-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2719+
if not skip_grants:
2720+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
26972721

26982722
elif not is_table_deployable:
26992723
# Only create the dev preview table as a normal table.
27002724
# For the main table, if the snapshot is cant be deployed to prod (eg upstream is forward-only) do nothing.
27012725
# Any downstream models that reference it will be updated to point to the dev preview table.
27022726
# If the user eventually tries to deploy it, the logic in insert() will see it doesnt exist and create it
2703-
2704-
# Create preview table but don't apply grants here since the table is not deployable
2705-
# Grants will be applied later when the table becomes deployable
2706-
kwargs_no_grants = {**kwargs}
2707-
kwargs_no_grants["skip_grants"] = True
27082727
super().create(
27092728
table_name=table_name,
27102729
model=model,
27112730
is_table_deployable=is_table_deployable,
27122731
render_kwargs=render_kwargs,
2713-
**kwargs_no_grants,
2732+
skip_grants=skip_grants,
2733+
**kwargs,
27142734
)
27152735

27162736
def insert(
@@ -2737,6 +2757,7 @@ def insert(
27372757
column_descriptions=model.column_descriptions,
27382758
table_format=model.table_format,
27392759
)
2760+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
27402761
elif not is_snapshot_deployable:
27412762
# Snapshot isnt deployable; update the preview table instead
27422763
# If the snapshot was deployable, then data would have already been loaded in create() because a managed table would have been created
@@ -2786,7 +2807,7 @@ def migrate(
27862807
)
27872808

27882809
# Apply grants after verifying no schema changes
2789-
# This ensures metadata-only changes (grants) are applied
2810+
# This ensures metadata-only grants changes are applied
27902811
self._apply_grants(snapshot.model, target_table_name, GrantsTargetLayer.PHYSICAL)
27912812

27922813
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)