Skip to content

Commit 79bba1a

Browse files
committed
refactor: add skip_grants flag to SnapshotEvaluator.create
1 parent 7f1a538 commit 79bba1a

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
@@ -882,6 +882,7 @@ def create_snapshot(
882882
deployability_index=deployability_index,
883883
create_render_kwargs=create_render_kwargs,
884884
rendered_physical_properties=rendered_physical_properties,
885+
skip_grants=True,
885886
dry_run=True,
886887
)
887888

@@ -1442,12 +1443,12 @@ def _execute_create(
14421443
table_name=table_name,
14431444
model=snapshot.model,
14441445
is_table_deployable=is_table_deployable,
1446+
skip_grants=skip_grants,
14451447
render_kwargs=create_render_kwargs,
14461448
is_snapshot_deployable=is_snapshot_deployable,
14471449
is_snapshot_representative=is_snapshot_representative,
14481450
dry_run=dry_run,
14491451
physical_properties=rendered_physical_properties,
1450-
skip_grants=skip_grants,
14511452
)
14521453
if run_pre_post_statements:
14531454
adapter.execute(snapshot.model.render_post_statements(**create_render_kwargs))
@@ -1612,6 +1613,7 @@ def create(
16121613
model: Model,
16131614
is_table_deployable: bool,
16141615
render_kwargs: t.Dict[str, t.Any],
1616+
skip_grants: bool,
16151617
**kwargs: t.Any,
16161618
) -> None:
16171619
"""Creates the target table or view.
@@ -1755,6 +1757,7 @@ def create(
17551757
model: Model,
17561758
is_table_deployable: bool,
17571759
render_kwargs: t.Dict[str, t.Any],
1760+
skip_grants: bool,
17581761
**kwargs: t.Any,
17591762
) -> None:
17601763
pass
@@ -1830,10 +1833,10 @@ def promote(
18301833
view_properties=model.render_virtual_properties(**render_kwargs),
18311834
)
18321835

1833-
# Apply grants to the physical layer table
1836+
# Apply grants to the physical layer (referenced table / view) after promotion
18341837
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
18351838

1836-
# Apply grants to the virtual layer view
1839+
# Apply grants to the virtual layer (view) after promotion
18371840
self._apply_grants(model, view_name, GrantsTargetLayer.VIRTUAL)
18381841

18391842
def demote(self, view_name: str, **kwargs: t.Any) -> None:
@@ -1848,6 +1851,7 @@ def create(
18481851
model: Model,
18491852
is_table_deployable: bool,
18501853
render_kwargs: t.Dict[str, t.Any],
1854+
skip_grants: bool,
18511855
**kwargs: t.Any,
18521856
) -> None:
18531857
ctas_query = model.ctas_query(**render_kwargs)
@@ -1893,7 +1897,7 @@ def create(
18931897
)
18941898

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

18991903
def migrate(
@@ -1935,6 +1939,7 @@ def _replace_query_for_model(
19351939
name: str,
19361940
query_or_df: QueryOrDF,
19371941
render_kwargs: t.Dict[str, t.Any],
1942+
skip_grants: bool = False,
19381943
**kwargs: t.Any,
19391944
) -> None:
19401945
"""Replaces the table for the given model.
@@ -1972,7 +1977,7 @@ def _replace_query_for_model(
19721977
)
19731978

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

19781983
def _get_target_and_source_columns(
@@ -2222,6 +2227,7 @@ def create(
22222227
model: Model,
22232228
is_table_deployable: bool,
22242229
render_kwargs: t.Dict[str, t.Any],
2230+
skip_grants: bool,
22252231
**kwargs: t.Any,
22262232
) -> None:
22272233
model = t.cast(SeedModel, model)
@@ -2235,22 +2241,34 @@ def create(
22352241
)
22362242
return
22372243

2238-
# Skip grants in parent create call since we'll apply them after data insertion
2239-
kwargs_no_grants = {**kwargs}
2240-
kwargs_no_grants["skip_grants"] = True
2241-
2242-
super().create(table_name, model, is_table_deployable, render_kwargs, **kwargs_no_grants)
2244+
super().create(
2245+
table_name,
2246+
model,
2247+
is_table_deployable,
2248+
render_kwargs,
2249+
skip_grants=True, # Skip grants; they're applied after data insertion
2250+
**kwargs,
2251+
)
22432252
# For seeds we insert data at the time of table creation.
22442253
try:
22452254
for index, df in enumerate(model.render_seed()):
22462255
if index == 0:
22472256
self._replace_query_for_model(
2248-
model, table_name, df, render_kwargs, **kwargs_no_grants
2257+
model,
2258+
table_name,
2259+
df,
2260+
render_kwargs,
2261+
skip_grants=True, # Skip grants; they're applied after data insertion
2262+
**kwargs,
22492263
)
22502264
else:
22512265
self.adapter.insert_append(
22522266
table_name, df, target_columns_to_types=model.columns_to_types
22532267
)
2268+
2269+
if not skip_grants:
2270+
# Apply grants after seed table creation and data insertion
2271+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
22542272
except Exception:
22552273
self.adapter.drop_table(table_name)
22562274
raise
@@ -2298,6 +2316,7 @@ def create(
22982316
model: Model,
22992317
is_table_deployable: bool,
23002318
render_kwargs: t.Dict[str, t.Any],
2319+
skip_grants: bool,
23012320
**kwargs: t.Any,
23022321
) -> None:
23032322
assert isinstance(model.kind, (SCDType2ByTimeKind, SCDType2ByColumnKind))
@@ -2327,11 +2346,13 @@ def create(
23272346
model,
23282347
is_table_deployable,
23292348
render_kwargs,
2349+
skip_grants,
23302350
**kwargs,
23312351
)
23322352

2333-
# Apply grants after SCD Type 2 table creation
2334-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2353+
if not skip_grants:
2354+
# Apply grants after SCD Type 2 table creation
2355+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
23352356

23362357
def insert(
23372358
self,
@@ -2475,14 +2496,17 @@ def create(
24752496
model: Model,
24762497
is_table_deployable: bool,
24772498
render_kwargs: t.Dict[str, t.Any],
2499+
skip_grants: bool,
24782500
**kwargs: t.Any,
24792501
) -> None:
24802502
if self.adapter.table_exists(table_name):
24812503
# Make sure we don't recreate the view to prevent deletion of downstream views in engines with no late
24822504
# binding support (because of DROP CASCADE).
24832505
logger.info("View '%s' already exists", table_name)
2484-
# Always apply grants when present, even if view exists, to handle grants updates
2485-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2506+
2507+
if not skip_grants:
2508+
# Always apply grants when present, even if view exists, to handle grants updates
2509+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
24862510
return
24872511

24882512
logger.info("Creating view '%s'", table_name)
@@ -2506,8 +2530,9 @@ def create(
25062530
column_descriptions=model.column_descriptions if is_table_deployable else None,
25072531
)
25082532

2509-
# Apply grants after view creation
2510-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2533+
if not skip_grants:
2534+
# Apply grants after view creation
2535+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
25112536

25122537
def migrate(
25132538
self,
@@ -2684,6 +2709,7 @@ def create(
26842709
model: Model,
26852710
is_table_deployable: bool,
26862711
render_kwargs: t.Dict[str, t.Any],
2712+
skip_grants: bool,
26872713
**kwargs: t.Any,
26882714
) -> None:
26892715
is_snapshot_deployable: bool = kwargs["is_snapshot_deployable"]
@@ -2704,24 +2730,21 @@ def create(
27042730
)
27052731

27062732
# Apply grants after managed table creation
2707-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2733+
if not skip_grants:
2734+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
27082735

27092736
elif not is_table_deployable:
27102737
# Only create the dev preview table as a normal table.
27112738
# For the main table, if the snapshot is cant be deployed to prod (eg upstream is forward-only) do nothing.
27122739
# Any downstream models that reference it will be updated to point to the dev preview table.
27132740
# If the user eventually tries to deploy it, the logic in insert() will see it doesnt exist and create it
2714-
2715-
# Create preview table but don't apply grants here since the table is not deployable
2716-
# Grants will be applied later when the table becomes deployable
2717-
kwargs_no_grants = {**kwargs}
2718-
kwargs_no_grants["skip_grants"] = True
27192741
super().create(
27202742
table_name=table_name,
27212743
model=model,
27222744
is_table_deployable=is_table_deployable,
27232745
render_kwargs=render_kwargs,
2724-
**kwargs_no_grants,
2746+
skip_grants=skip_grants,
2747+
**kwargs,
27252748
)
27262749

27272750
def insert(
@@ -2748,6 +2771,7 @@ def insert(
27482771
column_descriptions=model.column_descriptions,
27492772
table_format=model.table_format,
27502773
)
2774+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
27512775
elif not is_snapshot_deployable:
27522776
# Snapshot isnt deployable; update the preview table instead
27532777
# If the snapshot was deployable, then data would have already been loaded in create() because a managed table would have been created
@@ -2797,7 +2821,7 @@ def migrate(
27972821
)
27982822

27992823
# Apply grants after verifying no schema changes
2800-
# This ensures metadata-only changes (grants) are applied
2824+
# This ensures metadata-only grants changes are applied
28012825
self._apply_grants(snapshot.model, target_table_name, GrantsTargetLayer.PHYSICAL)
28022826

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