Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: partial parsing - reparse downstream nodes when adding versioning #11184

Merged
merged 8 commits into from
Jan 7, 2025
6 changes: 6 additions & 0 deletions .changes/unreleased/Fixes-20250102-140543.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Fixes
body: Make partial parsing reparse referencing nodes of newly versioned models.
time: 2025-01-02T14:05:43.629959-05:00
custom:
Author: d-cole
Issue: "8872"
16 changes: 9 additions & 7 deletions core/dbt/parser/partial.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
from copy import deepcopy
from typing import Callable, Dict, List, MutableMapping
from typing import Callable, Dict, List, MutableMapping, Union

from dbt.constants import DEFAULT_ENV_PLACEHOLDER
from dbt.contracts.files import (
Expand All @@ -10,6 +10,7 @@
parse_file_type_to_parser,
)
from dbt.contracts.graph.manifest import Manifest
from dbt.contracts.graph.nodes import AnalysisNode, ModelNode, SeedNode, SnapshotNode
from dbt.events.types import PartialParsingEnabled, PartialParsingFile
from dbt.node_types import NodeType
from dbt_common.context import get_invocation_context
Expand Down Expand Up @@ -820,7 +821,7 @@ def merge_patch(self, schema_file, key, patch, new_patch=False):

# For model, seed, snapshot, analysis schema dictionary keys,
# delete the patches and tests from the patch
def delete_schema_mssa_links(self, schema_file, dict_key, elem):
def delete_schema_mssa_links(self, schema_file, dict_key, elem) -> None:
# find elem node unique_id in node_patches
prefix = key_to_prefix[dict_key]
elem_unique_ids = []
Expand All @@ -841,11 +842,12 @@ def delete_schema_mssa_links(self, schema_file, dict_key, elem):
elem_unique_id in self.saved_manifest.nodes
or elem_unique_id in self.saved_manifest.disabled
):
nodes: List[Union[ModelNode, SeedNode, SnapshotNode, AnalysisNode]] = []
if elem_unique_id in self.saved_manifest.nodes:
nodes = [self.saved_manifest.nodes.pop(elem_unique_id)]
nodes = [self.saved_manifest.nodes.pop(elem_unique_id)] # type: ignore[list-item]
else:
# The value of disabled items is a list of nodes
nodes = self.saved_manifest.disabled.pop(elem_unique_id)
nodes = self.saved_manifest.disabled.pop(elem_unique_id) # type: ignore[assignment]
# need to add the node source_file to pp_files
for node in nodes:
file_id = node.file_id
Expand All @@ -858,9 +860,9 @@ def delete_schema_mssa_links(self, schema_file, dict_key, elem):
# if the node's group has changed - need to reparse all referencing nodes to ensure valid ref access
if node.group != elem.get("group"):
self.schedule_referencing_nodes_for_parsing(node.unique_id)
# If the latest version has changed or a version has been removed we need to
# reparse referencing nodes.
if node.is_versioned:
# If the latest version has changed, a version has been removed, or a version has been added,
# we need to reparse referencing nodes.
if node.is_versioned or elem.get("versions"):
self.schedule_referencing_nodes_for_parsing(node.unique_id)
# remove from patches
schema_file.node_patches.remove(elem_unique_id)
Expand Down
37 changes: 37 additions & 0 deletions tests/functional/partial_parsing/test_versioned_models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pathlib
from typing import Dict

import pytest

Expand Down Expand Up @@ -120,3 +121,39 @@ def test_pp_versioned_models(self, project):
write_file(model_one_sql, project.project_root, "models", "model_one.sql")
with pytest.raises(DuplicateVersionedUnversionedError):
run_dbt(["parse"])


model_unversioned_schema_yml = """
models:
- name: model_one
description: "The first model"
"""


model_versioned_schema_yml = """
models:
- name: model_one
description: "The first model"
latest_version: 1
versions:
- v: 1
"""


class TestAddingVersioningToModel:
@pytest.fixture(scope="class")
def models(self) -> Dict[str, str]:
return {
"model_one.sql": model_one_sql,
"model_one_downstream.sql": model_one_downstream_sql,
"schema.yml": model_unversioned_schema_yml,
}

def test_pp_newly_versioned_models(self, project) -> None:
results = run_dbt(["run"])
assert len(results) == 2

# update schema.yml block - model_one is now versioned
write_file(model_versioned_schema_yml, project.project_root, "models", "schema.yml")
results = run_dbt(["--partial-parse", "run"])
assert len(results) == 2
29 changes: 29 additions & 0 deletions tests/unit/parser/test_partial.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import time
from copy import deepcopy
from typing import Dict, List
from unittest import mock

import pytest

Expand Down Expand Up @@ -188,6 +189,34 @@ def test_schedule_macro_nodes_for_parsing_basic(partial_parsing):
}


def test_schedule_nodes_for_parsing_versioning(partial_parsing) -> None:
# Modify schema file to add versioning
schema_file_id = "my_test://" + normalize("models/schema.yml")
partial_parsing.new_files[schema_file_id].checksum = FileHash.from_contents("changed")
partial_parsing.new_files[schema_file_id].dfy = {
"version": 2,
"models": [
{
"name": "my_model",
"description": "Test model",
"latest_version": 1,
"versions": [{"v": 1}],
},
{"name": "python_model", "description": "python"},
{"name": "not_null", "model": "test.my_test.test_my_model"},
],
}
with mock.patch.object(
partial_parsing, "schedule_referencing_nodes_for_parsing"
) as mock_schedule_referencing_nodes_for_parsing:
partial_parsing.build_file_diff()
partial_parsing.get_parsing_files()

mock_schedule_referencing_nodes_for_parsing.assert_called_once_with(
"model.my_test.my_model"
)


class TestFileDiff:
@pytest.fixture
def partial_parsing(self, manifest, files):
Expand Down
Loading