Skip to content

Commit e3d5356

Browse files
committed
refactor: add skip_grants flag to SnapshotEvaluator.create
1 parent d2b1911 commit e3d5356

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
@@ -893,6 +893,7 @@ def create_snapshot(
893893
deployability_index=deployability_index,
894894
create_render_kwargs=create_render_kwargs,
895895
rendered_physical_properties=rendered_physical_properties,
896+
skip_grants=True,
896897
dry_run=True,
897898
)
898899

@@ -1455,12 +1456,12 @@ def _execute_create(
14551456
table_name=table_name,
14561457
model=snapshot.model,
14571458
is_table_deployable=is_table_deployable,
1459+
skip_grants=skip_grants,
14581460
render_kwargs=create_render_kwargs,
14591461
is_snapshot_deployable=is_snapshot_deployable,
14601462
is_snapshot_representative=is_snapshot_representative,
14611463
dry_run=dry_run,
14621464
physical_properties=rendered_physical_properties,
1463-
skip_grants=skip_grants,
14641465
)
14651466
if run_pre_post_statements:
14661467
evaluation_strategy.run_post_statements(
@@ -1695,6 +1696,7 @@ def create(
16951696
model: Model,
16961697
is_table_deployable: bool,
16971698
render_kwargs: t.Dict[str, t.Any],
1699+
skip_grants: bool,
16981700
**kwargs: t.Any,
16991701
) -> None:
17001702
"""Creates the target table or view.
@@ -1856,6 +1858,7 @@ def create(
18561858
model: Model,
18571859
is_table_deployable: bool,
18581860
render_kwargs: t.Dict[str, t.Any],
1861+
skip_grants: bool,
18591862
**kwargs: t.Any,
18601863
) -> None:
18611864
pass
@@ -1937,10 +1940,10 @@ def promote(
19371940
view_properties=model.render_virtual_properties(**render_kwargs),
19381941
)
19391942

1940-
# Apply grants to the physical layer table
1943+
# Apply grants to the physical layer (referenced table / view) after promotion
19411944
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
19421945

1943-
# Apply grants to the virtual layer view
1946+
# Apply grants to the virtual layer (view) after promotion
19441947
self._apply_grants(model, view_name, GrantsTargetLayer.VIRTUAL)
19451948

19461949
def demote(self, view_name: str, **kwargs: t.Any) -> None:
@@ -1961,6 +1964,7 @@ def create(
19611964
model: Model,
19621965
is_table_deployable: bool,
19631966
render_kwargs: t.Dict[str, t.Any],
1967+
skip_grants: bool,
19641968
**kwargs: t.Any,
19651969
) -> None:
19661970
ctas_query = model.ctas_query(**render_kwargs)
@@ -2006,7 +2010,7 @@ def create(
20062010
)
20072011

20082012
# Apply grants after table creation (unless explicitly skipped by caller)
2009-
if not kwargs.get("skip_grants", False):
2013+
if not skip_grants:
20102014
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
20112015

20122016
def migrate(
@@ -2048,6 +2052,7 @@ def _replace_query_for_model(
20482052
name: str,
20492053
query_or_df: QueryOrDF,
20502054
render_kwargs: t.Dict[str, t.Any],
2055+
skip_grants: bool = False,
20512056
**kwargs: t.Any,
20522057
) -> None:
20532058
"""Replaces the table for the given model.
@@ -2085,7 +2090,7 @@ def _replace_query_for_model(
20852090
)
20862091

20872092
# Apply grants after table replacement (unless explicitly skipped by caller)
2088-
if not kwargs.get("skip_grants", False):
2093+
if not skip_grants:
20892094
self._apply_grants(model, name, GrantsTargetLayer.PHYSICAL)
20902095

20912096
def _get_target_and_source_columns(
@@ -2335,6 +2340,7 @@ def create(
23352340
model: Model,
23362341
is_table_deployable: bool,
23372342
render_kwargs: t.Dict[str, t.Any],
2343+
skip_grants: bool,
23382344
**kwargs: t.Any,
23392345
) -> None:
23402346
model = t.cast(SeedModel, model)
@@ -2348,22 +2354,34 @@ def create(
23482354
)
23492355
return
23502356

2351-
# Skip grants in parent create call since we'll apply them after data insertion
2352-
kwargs_no_grants = {**kwargs}
2353-
kwargs_no_grants["skip_grants"] = True
2354-
2355-
super().create(table_name, model, is_table_deployable, render_kwargs, **kwargs_no_grants)
2357+
super().create(
2358+
table_name,
2359+
model,
2360+
is_table_deployable,
2361+
render_kwargs,
2362+
skip_grants=True, # Skip grants; they're applied after data insertion
2363+
**kwargs,
2364+
)
23562365
# For seeds we insert data at the time of table creation.
23572366
try:
23582367
for index, df in enumerate(model.render_seed()):
23592368
if index == 0:
23602369
self._replace_query_for_model(
2361-
model, table_name, df, render_kwargs, **kwargs_no_grants
2370+
model,
2371+
table_name,
2372+
df,
2373+
render_kwargs,
2374+
skip_grants=True, # Skip grants; they're applied after data insertion
2375+
**kwargs,
23622376
)
23632377
else:
23642378
self.adapter.insert_append(
23652379
table_name, df, target_columns_to_types=model.columns_to_types
23662380
)
2381+
2382+
if not skip_grants:
2383+
# Apply grants after seed table creation and data insertion
2384+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
23672385
except Exception:
23682386
self.adapter.drop_table(table_name)
23692387
raise
@@ -2411,6 +2429,7 @@ def create(
24112429
model: Model,
24122430
is_table_deployable: bool,
24132431
render_kwargs: t.Dict[str, t.Any],
2432+
skip_grants: bool,
24142433
**kwargs: t.Any,
24152434
) -> None:
24162435
assert isinstance(model.kind, (SCDType2ByTimeKind, SCDType2ByColumnKind))
@@ -2440,11 +2459,13 @@ def create(
24402459
model,
24412460
is_table_deployable,
24422461
render_kwargs,
2462+
skip_grants,
24432463
**kwargs,
24442464
)
24452465

2446-
# Apply grants after SCD Type 2 table creation
2447-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2466+
if not skip_grants:
2467+
# Apply grants after SCD Type 2 table creation
2468+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
24482469

24492470
def insert(
24502471
self,
@@ -2588,14 +2609,17 @@ def create(
25882609
model: Model,
25892610
is_table_deployable: bool,
25902611
render_kwargs: t.Dict[str, t.Any],
2612+
skip_grants: bool,
25912613
**kwargs: t.Any,
25922614
) -> None:
25932615
if self.adapter.table_exists(table_name):
25942616
# Make sure we don't recreate the view to prevent deletion of downstream views in engines with no late
25952617
# binding support (because of DROP CASCADE).
25962618
logger.info("View '%s' already exists", table_name)
2597-
# Always apply grants when present, even if view exists, to handle grants updates
2598-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2619+
2620+
if not skip_grants:
2621+
# Always apply grants when present, even if view exists, to handle grants updates
2622+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
25992623
return
26002624

26012625
logger.info("Creating view '%s'", table_name)
@@ -2619,8 +2643,9 @@ def create(
26192643
column_descriptions=model.column_descriptions if is_table_deployable else None,
26202644
)
26212645

2622-
# Apply grants after view creation
2623-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2646+
if not skip_grants:
2647+
# Apply grants after view creation
2648+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
26242649

26252650
def migrate(
26262651
self,
@@ -2936,6 +2961,7 @@ def create(
29362961
model: Model,
29372962
is_table_deployable: bool,
29382963
render_kwargs: t.Dict[str, t.Any],
2964+
skip_grants: bool,
29392965
**kwargs: t.Any,
29402966
) -> None:
29412967
is_snapshot_deployable: bool = kwargs["is_snapshot_deployable"]
@@ -2956,24 +2982,21 @@ def create(
29562982
)
29572983

29582984
# Apply grants after managed table creation
2959-
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
2985+
if not skip_grants:
2986+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
29602987

29612988
elif not is_table_deployable:
29622989
# Only create the dev preview table as a normal table.
29632990
# For the main table, if the snapshot is cant be deployed to prod (eg upstream is forward-only) do nothing.
29642991
# Any downstream models that reference it will be updated to point to the dev preview table.
29652992
# If the user eventually tries to deploy it, the logic in insert() will see it doesnt exist and create it
2966-
2967-
# Create preview table but don't apply grants here since the table is not deployable
2968-
# Grants will be applied later when the table becomes deployable
2969-
kwargs_no_grants = {**kwargs}
2970-
kwargs_no_grants["skip_grants"] = True
29712993
super().create(
29722994
table_name=table_name,
29732995
model=model,
29742996
is_table_deployable=is_table_deployable,
29752997
render_kwargs=render_kwargs,
2976-
**kwargs_no_grants,
2998+
skip_grants=skip_grants,
2999+
**kwargs,
29773000
)
29783001

29793002
def insert(
@@ -3000,6 +3023,7 @@ def insert(
30003023
column_descriptions=model.column_descriptions,
30013024
table_format=model.table_format,
30023025
)
3026+
self._apply_grants(model, table_name, GrantsTargetLayer.PHYSICAL)
30033027
elif not is_snapshot_deployable:
30043028
# Snapshot isnt deployable; update the preview table instead
30053029
# If the snapshot was deployable, then data would have already been loaded in create() because a managed table would have been created
@@ -3049,7 +3073,7 @@ def migrate(
30493073
)
30503074

30513075
# Apply grants after verifying no schema changes
3052-
# This ensures metadata-only changes (grants) are applied
3076+
# This ensures metadata-only grants changes are applied
30533077
self._apply_grants(snapshot.model, target_table_name, GrantsTargetLayer.PHYSICAL)
30543078

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