Repository: dbt-labs/dbt-core

Issue:
[CT-2419] Ensure process_refs is called for nodes referring to versioned models
If a node refs a versioned model but does not specify a version, the ref will use the latest version. But if a new latest version is added to the ref'd model and nothing changes in the model with the ref, the depends_on unique_id will not be updated.

Create a test case for this situation and update the code in core/dbt/parser/manifest.py "process_refs". We could check for refs to versioned models or just process all refs (not just models with an updated "created_at" timestamp. Processing refs is pretty fast, though it might be useful to compare the time it takes to do it both ways.


Discussion:
I think this may not actually be a real problem, since referencing nodes are scheduled for re-parsing. 
reproduction case: 

```yaml
---models/schema.yml
models:
  - name: my_model
    latest_version: 0
    versions:
      - v: 1
      - v: 0
```

```sql
---models/depends_on_version.sql
SELECT * FROM {{ ref('my_model') }}
```
1. initial run, without partial parsing - depends on v0 (latest) as expected
```sh
dbt --no-partial-parse --quiet ls --select depends_on_version --output json --output-keys unique_id depends_on
{"unique_id": "model.jaffle_shop.depends_on_version", "depends_on": {"macros": [], "nodes": ["model.jaffle_shop.my_model.v0"]}}
```

2. updating `latest_version` to `1` in schema.yml + subsequent runs _with_ partial parsing - does _not_ depend on v1 (latest) as expected
```sh
dbt --quiet ls --select depends_on_version --output json --output-keys unique_id depends_on
{"unique_id": "model.jaffle_shop.depends_on_version", "depends_on": {"macros": [], "nodes": ["model.jaffle_shop.my_model.v0"]}}
```

3. proceeding to run depends_on_version leads to: 
```
dbt run --select depends_on_version
23:36:59  Running with dbt=1.6.0-a1
23:37:00  Found 3 models, 0 tests, 0 snapshots, 0 analyses, 552 macros, 0 operations, 0 seed files, 0 sources, 0 exposures, 0 metrics, 0 groups
23:37:00  
23:37:02  Concurrency: 1 threads (target='dev')
23:37:02  
23:37:02  1 of 1 START sql view model dbt_marky.depends_on_version ....................... [RUN]
23:37:02  1 of 1 ERROR creating sql view model dbt_marky.depends_on_version .............. [ERROR in 0.02s]
23:37:02  
23:37:02  Finished running 1 view model in 0 hours 0 minutes and 1.85 seconds (1.85s).
23:37:02  
23:37:02  Completed with 1 error and 0 warnings:
23:37:02  
23:37:02  Compilation Error in model depends_on_version (models2/depends_on_version.sql)
23:37:02    dbt was unable to infer all dependencies for the model "depends_on_version".
23:37:02    This typically happens when ref() is placed within a conditional block.
23:37:02    
23:37:02    To fix this, add the following hint to the top of the model "depends_on_version":
23:37:02    
23:37:02    -- depends_on: {{ ref('my_model') }}
23:37:02  
23:37:02  Done. PASS=0 WARN=0 ERROR=1 SKIP=0 TOTAL=1
```



Resolving patch:
diff --git a/core/dbt/parser/partial.py b/core/dbt/parser/partial.py
--- a/core/dbt/parser/partial.py
+++ b/core/dbt/parser/partial.py
@@ -851,6 +851,9 @@ def delete_schema_mssa_links(self, schema_file, dict_key, elem):
                     if self.saved_files[file_id]:
                         source_file = self.saved_files[file_id]
                         self.add_to_pp_files(source_file)
+                    # if the node's latest version has changed - need to reparse all referencing nodes to ensure correct ref resolution
+                    if node.is_versioned and node.latest_version != elem.get("latest_version"):
+                        self.schedule_referencing_nodes_for_parsing(elem_unique_id)
             # remove from patches
             schema_file.node_patches.remove(elem_unique_id)
 


Test patch:
diff --git a/test/unit/utils.py b/test/unit/utils.py
--- a/test/unit/utils.py
+++ b/test/unit/utils.py
@@ -336,11 +336,7 @@ def MockNode(package, name, resource_type=None, **kwargs):
 
     version = kwargs.get("version")
     search_name = name if version is None else f"{name}.v{version}"
-    unique_id = (
-        f"{str(resource_type)}.{package}.{name}"
-        # if version is None
-        # else f"{str(resource_type)}.{package}.{name}.v{version}"
-    )
+    unique_id = f"{str(resource_type)}.{package}.{name}"
     node = mock.MagicMock(
         __class__=cls,
         resource_type=resource_type,
diff --git a/tests/functional/partial_parsing/test_partial_parsing.py b/tests/functional/partial_parsing/test_partial_parsing.py
--- a/tests/functional/partial_parsing/test_partial_parsing.py
+++ b/tests/functional/partial_parsing/test_partial_parsing.py
@@ -304,36 +304,43 @@ def models(self):
         return {
             "model_one.sql": model_one_sql,
             "model_one_v2.sql": model_one_sql,
+            "model_one_downstream.sql": model_four2_sql,
             "schema.yml": models_versions_schema_yml,
         }
 
     def test_pp_versioned_models(self, project):
         results = run_dbt(["run"])
-        assert len(results) == 2
+        assert len(results) == 3
 
         manifest = get_manifest(project.project_root)
         model_one_node = manifest.nodes["model.test.model_one.v1"]
         assert not model_one_node.is_latest_version
         model_two_node = manifest.nodes["model.test.model_one.v2"]
         assert model_two_node.is_latest_version
+        # assert unpinned ref points to latest version
+        model_one_downstream_node = manifest.nodes["model.test.model_one_downstream"]
+        assert model_one_downstream_node.depends_on.nodes == ["model.test.model_one.v2"]
 
         # update versions schema.yml block - latest_version from 2 to 1
         write_file(
             models_versions_updated_schema_yml, project.project_root, "models", "schema.yml"
         )
         results = run_dbt(["--partial-parse", "run"])
-        assert len(results) == 2
+        assert len(results) == 3
 
         manifest = get_manifest(project.project_root)
         model_one_node = manifest.nodes["model.test.model_one.v1"]
         assert model_one_node.is_latest_version
         model_two_node = manifest.nodes["model.test.model_one.v2"]
         assert not model_two_node.is_latest_version
+        # assert unpinned ref points to latest version
+        model_one_downstream_node = manifest.nodes["model.test.model_one_downstream"]
+        assert model_one_downstream_node.depends_on.nodes == ["model.test.model_one.v1"]
 
         # update versioned model
         write_file(model_two_sql, project.project_root, "models", "model_one_v2.sql")
         results = run_dbt(["--partial-parse", "run"])
-        assert len(results) == 2
+        assert len(results) == 3
 
 
 class TestSources: