Skip to content

Commit

Permalink
Fix: deleting models that depend on external nodes (#8330)
Browse files Browse the repository at this point in the history
  • Loading branch information
MichelleArk authored Aug 14, 2023
1 parent 27be929 commit 34e6edb
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 2 deletions.
7 changes: 7 additions & 0 deletions .changes/unreleased/Fixes-20230811-212008.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: Fixes
body: 'Fix: DbtInternalError after model that previously ref''d external model is
deleted'
time: 2023-08-11T21:20:08.145554-04:00
custom:
Author: michelleark
Issue: "8375"
5 changes: 3 additions & 2 deletions core/dbt/parser/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
from dbt.version import __version__

from dbt.dataclass_schema import StrEnum, dbtClassMixin
from dbt.plugins import get_plugin_manager
from dbt import plugins

from dbt_semantic_interfaces.enum_extension import assert_values_exhausted
from dbt_semantic_interfaces.type_enums import MetricType
Expand Down Expand Up @@ -503,6 +503,7 @@ def load(self):
self.manifest.selectors = self.root_project.manifest_selectors

# inject any available external nodes
self.manifest.build_parent_and_child_maps()
external_nodes_modified = self.inject_external_nodes()
if external_nodes_modified:
self.manifest.rebuild_ref_lookup()
Expand Down Expand Up @@ -751,7 +752,7 @@ def inject_external_nodes(self) -> bool:
manifest_nodes_modified = True

# Inject any newly-available external nodes
pm = get_plugin_manager(self.root_project.project_name)
pm = plugins.get_plugin_manager(self.root_project.project_name)
plugin_model_nodes = pm.get_nodes().models
for node_arg in plugin_model_nodes.values():
node = ModelNode.from_args(node_arg)
Expand Down
72 changes: 72 additions & 0 deletions tests/functional/partial_parsing/test_partial_parsing.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
from unittest import mock

from dbt.tests.util import run_dbt, get_manifest, write_file, rm_file, run_dbt_and_capture
from dbt.tests.fixtures.project import write_project_files
Expand Down Expand Up @@ -71,6 +72,7 @@
from dbt.exceptions import CompilationError, ParsingError
from dbt.contracts.files import ParseFileType
from dbt.contracts.results import TestStatus
from dbt.plugins.manifest import PluginNodes, ModelNodeArgs

import re
import os
Expand Down Expand Up @@ -736,3 +738,73 @@ def test_pp_groups(self, project):
)
with pytest.raises(ParsingError):
results = run_dbt(["--partial-parse", "run"])


class TestExternalModels:
@pytest.fixture(scope="class")
def external_model_node(self):
return ModelNodeArgs(
name="external_model",
package_name="external",
identifier="test_identifier",
schema="test_schema",
)

@pytest.fixture(scope="class")
def external_model_node_versioned(self):
return ModelNodeArgs(
name="external_model_versioned",
package_name="external",
identifier="test_identifier_v1",
schema="test_schema",
version=1,
)

@pytest.fixture(scope="class")
def models(self):
return {"model_one.sql": model_one_sql}

@mock.patch("dbt.plugins.get_plugin_manager")
def test_pp_external_models(
self, get_plugin_manager, project, external_model_node, external_model_node_versioned
):
# initial plugin - one external model
external_nodes = PluginNodes()
external_nodes.add_model(external_model_node)
get_plugin_manager.return_value.get_nodes.return_value = external_nodes

# initial run
manifest = run_dbt(["parse"])
assert len(manifest.nodes) == 2
assert set(manifest.nodes.keys()) == {
"model.external.external_model",
"model.test.model_one",
}
assert len(manifest.external_node_unique_ids) == 1
assert manifest.external_node_unique_ids == ["model.external.external_model"]

# add a model file
write_file(model_two_sql, project.project_root, "models", "model_two.sql")
manifest = run_dbt(["--partial-parse", "parse"])
assert len(manifest.nodes) == 3

# add an external model
external_nodes.add_model(external_model_node_versioned)
manifest = run_dbt(["--partial-parse", "parse"])
assert len(manifest.nodes) == 4
assert len(manifest.external_node_unique_ids) == 2

# add a model file that depends on external model
write_file(
"SELECT * FROM {{ref('external', 'external_model')}}",
project.project_root,
"models",
"model_depends_on_external.sql",
)
manifest = run_dbt(["--partial-parse", "parse"])
assert len(manifest.nodes) == 5

# remove a model file that depends on external model
rm_file(project.project_root, "models", "model_depends_on_external.sql")
manifest = run_dbt(["--partial-parse", "parse"])
assert len(manifest.nodes) == 4

0 comments on commit 34e6edb

Please sign in to comment.