Skip to content

Commit 9874f9e

Browse files
authored
Fix groupable node partial parsing, raise DbtReferenceError in RuntimeRefResolver (dbt-labs#7438)
1 parent 2739d5f commit 9874f9e

File tree

5 files changed

+110
-19
lines changed

5 files changed

+110
-19
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: Fixes
2+
body: fix groupable node partial parsing, raise DbtReferenceError at runtime for safety
3+
time: 2023-04-24T16:18:43.130637-04:00
4+
custom:
5+
Author: MichelleArk
6+
Issue: "7437"

core/dbt/context/providers.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
Resource,
3939
ManifestNode,
4040
RefArgs,
41+
AccessType,
4142
)
4243
from dbt.contracts.graph.metrics import MetricReference, ResolvedMetricReference
4344
from dbt.contracts.graph.unparsed import NodeVersion
@@ -66,11 +67,12 @@
6667
DbtRuntimeError,
6768
TargetNotFoundError,
6869
DbtValidationError,
70+
DbtReferenceError,
6971
)
7072
from dbt.config import IsFQNResource
7173
from dbt.node_types import NodeType, ModelLanguage
7274

73-
from dbt.utils import merge, AttrDict, MultiDict, args_to_dict
75+
from dbt.utils import merge, AttrDict, MultiDict, args_to_dict, cast_to_str
7476

7577
from dbt import selected_resources
7678

@@ -495,6 +497,17 @@ def resolve(
495497
target_version=target_version,
496498
disabled=isinstance(target_model, Disabled),
497499
)
500+
elif (
501+
target_model.resource_type == NodeType.Model
502+
and target_model.access == AccessType.Private
503+
):
504+
if not self.model.group or self.model.group != target_model.group:
505+
raise DbtReferenceError(
506+
unique_id=self.model.unique_id,
507+
ref_unique_id=target_model.unique_id,
508+
group=cast_to_str(target_model.group),
509+
)
510+
498511
self.validate(target_model, target_name, target_package, target_version)
499512
return self.create_relation(target_model)
500513

core/dbt/parser/partial.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -851,9 +851,12 @@ def delete_schema_mssa_links(self, schema_file, dict_key, elem):
851851
if self.saved_files[file_id]:
852852
source_file = self.saved_files[file_id]
853853
self.add_to_pp_files(source_file)
854+
# if the node's group has changed - need to reparse all referencing nodes to ensure valid ref access
855+
if node.group != elem.get("group"):
856+
self.schedule_referencing_nodes_for_parsing(node.unique_id)
854857
# if the node's latest version has changed - need to reparse all referencing nodes to ensure correct ref resolution
855858
if node.is_versioned and node.latest_version != elem.get("latest_version"):
856-
self.schedule_referencing_nodes_for_parsing(elem_unique_id)
859+
self.schedule_referencing_nodes_for_parsing(node.unique_id)
857860
# remove from patches
858861
schema_file.node_patches.remove(elem_unique_id)
859862

tests/functional/partial_parsing/fixtures.py

+46
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,11 @@
628628
629629
"""
630630

631+
orders_downstream_sql = """
632+
select * from {{ ref('orders') }}
633+
634+
"""
635+
631636
model_a_sql = """
632637
select 1 as fun
633638
@@ -1040,6 +1045,47 @@
10401045
description: "Some order data"
10411046
"""
10421047

1048+
1049+
groups_schema_yml_two_groups_private_orders_valid_access = """
1050+
1051+
groups:
1052+
- name: test_group
1053+
owner:
1054+
name: test_group_owner
1055+
- name: test_group2
1056+
owner:
1057+
name: test_group_owner2
1058+
1059+
models:
1060+
- name: orders
1061+
group: test_group
1062+
access: private
1063+
description: "Some order data"
1064+
- name: orders_downstream
1065+
group: test_group
1066+
description: "Some order data"
1067+
"""
1068+
1069+
groups_schema_yml_two_groups_private_orders_invalid_access = """
1070+
1071+
groups:
1072+
- name: test_group
1073+
owner:
1074+
name: test_group_owner
1075+
- name: test_group2
1076+
owner:
1077+
name: test_group_owner2
1078+
1079+
models:
1080+
- name: orders
1081+
group: test_group2
1082+
access: private
1083+
description: "Some order data"
1084+
- name: orders_downstream
1085+
group: test_group
1086+
description: "Some order data"
1087+
"""
1088+
10431089
groups_schema_yml_one_group_model_in_group2 = """
10441090
10451091
groups:

tests/functional/partial_parsing/test_partial_parsing.py

+40-17
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
gsm_override_sql,
5656
gsm_override2_sql,
5757
orders_sql,
58+
orders_downstream_sql,
5859
snapshot_sql,
5960
snapshot2_sql,
6061
generic_schema_yml,
@@ -65,6 +66,8 @@
6566
groups_schema_yml_two_groups,
6667
groups_schema_yml_two_groups_edited,
6768
groups_schema_yml_one_group_model_in_group2,
69+
groups_schema_yml_two_groups_private_orders_valid_access,
70+
groups_schema_yml_two_groups_private_orders_invalid_access,
6871
)
6972

7073
from dbt.exceptions import CompilationError, ParsingError
@@ -699,56 +702,57 @@ class TestGroups:
699702
def models(self):
700703
return {
701704
"orders.sql": orders_sql,
705+
"orders_downstream.sql": orders_downstream_sql,
702706
"schema.yml": groups_schema_yml_one_group,
703707
}
704708

705709
def test_pp_groups(self, project):
706710

707711
# initial run
708712
results = run_dbt()
709-
assert len(results) == 1
713+
assert len(results) == 2
710714
manifest = get_manifest(project.project_root)
711-
expected_nodes = ["model.test.orders"]
715+
expected_nodes = ["model.test.orders", "model.test.orders_downstream"]
712716
expected_groups = ["group.test.test_group"]
713-
assert expected_nodes == list(manifest.nodes.keys())
714-
assert expected_groups == list(manifest.groups.keys())
717+
assert expected_nodes == sorted(list(manifest.nodes.keys()))
718+
assert expected_groups == sorted(list(manifest.groups.keys()))
715719

716720
# add group to schema
717721
write_file(groups_schema_yml_two_groups, project.project_root, "models", "schema.yml")
718722
results = run_dbt(["--partial-parse", "run"])
719-
assert len(results) == 1
723+
assert len(results) == 2
720724
manifest = get_manifest(project.project_root)
721-
expected_nodes = ["model.test.orders"]
725+
expected_nodes = ["model.test.orders", "model.test.orders_downstream"]
722726
expected_groups = ["group.test.test_group", "group.test.test_group2"]
723-
assert expected_nodes == list(manifest.nodes.keys())
724-
assert expected_groups == list(manifest.groups.keys())
727+
assert expected_nodes == sorted(list(manifest.nodes.keys()))
728+
assert expected_groups == sorted(list(manifest.groups.keys()))
725729

726730
# edit group in schema
727731
write_file(
728732
groups_schema_yml_two_groups_edited, project.project_root, "models", "schema.yml"
729733
)
730734
results = run_dbt(["--partial-parse", "run"])
731-
assert len(results) == 1
735+
assert len(results) == 2
732736
manifest = get_manifest(project.project_root)
733-
expected_nodes = ["model.test.orders"]
737+
expected_nodes = ["model.test.orders", "model.test.orders_downstream"]
734738
expected_groups = ["group.test.test_group", "group.test.test_group2_edited"]
735-
assert expected_nodes == list(manifest.nodes.keys())
736-
assert expected_groups == list(manifest.groups.keys())
739+
assert expected_nodes == sorted(list(manifest.nodes.keys()))
740+
assert expected_groups == sorted(list(manifest.groups.keys()))
737741

738742
# delete group in schema
739743
write_file(groups_schema_yml_one_group, project.project_root, "models", "schema.yml")
740744
results = run_dbt(["--partial-parse", "run"])
741-
assert len(results) == 1
745+
assert len(results) == 2
742746
manifest = get_manifest(project.project_root)
743-
expected_nodes = ["model.test.orders"]
747+
expected_nodes = ["model.test.orders", "model.test.orders_downstream"]
744748
expected_groups = ["group.test.test_group"]
745-
assert expected_nodes == list(manifest.nodes.keys())
746-
assert expected_groups == list(manifest.groups.keys())
749+
assert expected_nodes == sorted(list(manifest.nodes.keys()))
750+
assert expected_groups == sorted(list(manifest.groups.keys()))
747751

748752
# add back second group
749753
write_file(groups_schema_yml_two_groups, project.project_root, "models", "schema.yml")
750754
results = run_dbt(["--partial-parse", "run"])
751-
assert len(results) == 1
755+
assert len(results) == 2
752756

753757
# remove second group with model still configured to second group
754758
write_file(
@@ -759,3 +763,22 @@ def test_pp_groups(self, project):
759763
)
760764
with pytest.raises(ParsingError):
761765
results = run_dbt(["--partial-parse", "run"])
766+
767+
# add back second group, make orders private with valid ref
768+
write_file(
769+
groups_schema_yml_two_groups_private_orders_valid_access,
770+
project.project_root,
771+
"models",
772+
"schema.yml",
773+
)
774+
results = run_dbt(["--partial-parse", "run"])
775+
assert len(results) == 2
776+
777+
write_file(
778+
groups_schema_yml_two_groups_private_orders_invalid_access,
779+
project.project_root,
780+
"models",
781+
"schema.yml",
782+
)
783+
with pytest.raises(ParsingError):
784+
results = run_dbt(["--partial-parse", "run"])

0 commit comments

Comments
 (0)