Repository: dbt-labs/dbt-core

Issue:
[CT-3226] [Bug] Compilation Error 'metric.my_project.distinct_customers' depends on 'semantic_model.my_project.orderves' which is not in the graph!
### Is this a new bug in dbt-core?

- [X] I believe this is a new bug in dbt-core
- [X] I have searched the existing issues, and I could not find an existing issue for this bug

### Workaround

The easiest workaround is do a [`dbt clean`](https://docs.getdbt.com/reference/commands/clean):
```shell
dbt clean
```

<details>
<summary>More detailed workaround</summary>

Here's a workaround that may work in the meantime:

Use `--no-partial-parse` to [disable partial parsing](https://docs.getdbt.com/docs/dbt-versions/release-notes/Feb-2023/no-partial-parse-config):
```shell
dbt --no-partial-parse compile
```

Alternatively, delete the [`partial_parse.msgpack`](https://docs.getdbt.com/reference/parsing#partial-parsing) file from your `target` directory (for example, via [`dbt clean`](https://docs.getdbt.com/reference/commands/clean)).
</details>

### Current Behavior

I like small finger foods so much that I want to create a semantic model related to them:
```yaml
semantic_models:
  - name: orderves
  ...
```

After publishing the docs, a colleague reaches out and tells me: the correct spelling is "hors d'oeuvres" not "orderves" 😅

So I change the spelling of my semantic models from "orderves" to "orders" 'cause that seems easiest.

But the next time I do any of the following, I get an error:
```
dbt list
dbt complile
dbt run
dbt test
dbt build
...
```

```
22:18:17  Encountered an error:
Compilation Error
  'metric.my_project.distinct_customers' depends on 'semantic_model.my_project.orderves' which is not in the graph!
```

### Expected Behavior

I should be able to fix my speling misstakes when defining my semantic models (or make other changes) and be able re-run dbt without a compilation error.

### Steps To Reproduce

`models/orders.yml`
```yaml
semantic_models:
  - name: orderves
    model: ref('orders')
    description: |
      Order fact table. This table is at the order grain with one row per order.
    defaults:
      agg_time_dimension: ordered_at
    entities:
      - name: order_id
        type: primary
    dimensions:
      - name: ordered_at
        expr: ordered_at
        type: time
        type_params:
          time_granularity: day
    measures:
      - name: customers_with_orders
        description: Distinct count of customers placing orders
        agg: count_distinct
        expr: customer_id

metrics:
  - name: distinct_customers
    description: "Distict count of customers placing orders"
    label: Customers w/ Orders
    type: simple
    type_params:
      measure: customers_with_orders
```

`seeds/orders.csv`
```csv
"order_id","location_id","customer_id","order_total","tax_paid","ordered_at","count_food_items","count_drink_items","count_items","subtotal_drink_items","subtotal_food_items","subtotal","order_cost","location_name","is_food_order","is_drink_order"
"3d12eb16-1bbb-47ad-a732-bf156c322fa6","7f790ed7-0fc4-4de2-a1b0-cce72e657fc4","7cd5e7f3-39c8-48ce-950a-1b70449e7829",6.3600000000000000,0.36000000000000000000,2016-09-01 19:58:00.000,0,1,1,6.0000000000000000,0,6.0000000000000000,0.82000000000000000000,Philadelphia,false,true
"2c54134f-2322-4704-9f82-e2e9b77bcb19","7f790ed7-0fc4-4de2-a1b0-cce72e657fc4","96c8937f-d99c-4515-acec-db24d07768f9",50.8800000000000000,2.8800000000000000,2016-09-01 09:35:00.000,3,2,5,11.0000000000000000,37.0000000000000000,48.0000000000000000,10.32000000000000000000,Philadelphia,true,true
```

Create `models/metricflow_time_spine.sql` as described [here](https://docs.getdbt.com/docs/build/metricflow-time-spine).

Run these commands:
```
dbt seed
dbt compile
```

Update the name of the semantic model in `models/orders.yml` from "orderves" to "orders". Then try to compile again:
```
dbt compile
```

### Relevant log output

```shell
============================== 16:09:53.565057 | a34ca773-a861-4559-9a4b-0ed59811bac3 ==============================
16:09:53.565057 [info ] [MainThread]: Running with dbt=1.7.0-rc1
16:09:53.570655 [debug] [MainThread]: running dbt with arguments {'printer_width': '80', 'indirect_selection': 'eager', 'log_cache_events': 'False', 'write_json': 'True', 'partial_parse': 'True', 'cache_selected_only': 'False', 'warn_error': 'None', 'version_check': 'True', 'debug': 'False', 'log_path': '/Users/dbeatty/projects/copier-templates/metricflow-explore-6/logs', 'profiles_dir': '/Users/dbeatty/projects/copier-templates/metricflow-explore-6', 'fail_fast': 'False', 'use_colors': 'True', 'use_experimental_parser': 'False', 'no_print': 'None', 'quiet': 'False', 'warn_error_options': 'WarnErrorOptions(include=[], exclude=[])', 'static_parser': 'True', 'introspect': 'True', 'log_format': 'default', 'target_path': 'None', 'invocation_command': 'dbt compile', 'send_anonymous_usage_stats': 'False'}
16:09:53.843370 [info ] [MainThread]: Registered adapter: postgres=1.7.0-rc1
16:09:53.872753 [debug] [MainThread]: checksum: 04cf5834ef18f9eb3badf7fdcf1eabc6278b84fae1038a5ced0a7c14afafaac5, vars: {}, profile: , target: , version: 1.7.0rc1
16:09:53.941314 [debug] [MainThread]: Partial parsing enabled: 0 files deleted, 0 files added, 1 files changed.
16:09:53.942202 [debug] [MainThread]: Partial parsing: updated file: my_project://models/orders.yml
16:09:54.067871 [error] [MainThread]: Encountered an error:
Compilation Error
  'metric.my_project.distinct_customers' depends on 'semantic_model.my_project.orderves' which is not in the graph!
16:09:54.070212 [debug] [MainThread]: Resource report: {"command_name": "compile", "command_wall_clock_time": 0.57334316, "process_user_time": 2.156979, "process_kernel_time": 0.174989, "process_mem_max_rss": "121016320", "command_success": false, "process_in_blocks": "0", "process_out_blocks": "0"}
16:09:54.070892 [debug] [MainThread]: Command `dbt compile` failed at 16:09:54.070724 after 0.57 seconds
16:09:54.071379 [debug] [MainThread]: Flushing usage events
```


### Environment

```markdown
- OS:
- Python:
- dbt: 1.7.0-rc1
```


### Which database adapter are you using with dbt?

postgres

### Additional Context

This may be related to:
- https://github.com/dbt-labs/dbt-core/issues/8609


Discussion:
### Workaround

The easiest workaround is do a [`dbt clean`](https://docs.getdbt.com/reference/commands/clean):
```shell
dbt clean
```

<details>
<summary>More detailed workaround</summary>

Here's a workaround that may work in the meantime:

Use `--no-partial-parse` to [disable partial parsing](https://docs.getdbt.com/docs/dbt-versions/release-notes/Feb-2023/no-partial-parse-config):
```shell
dbt --no-partial-parse compile
```

Alternatively, delete the [`partial_parse.msgpack`](https://docs.getdbt.com/reference/parsing#partial-parsing) file from your `target` directory (for example, via [`dbt clean`](https://docs.getdbt.com/reference/commands/clean)).
</details>
Did a bit digging and found out that we did not actually properly processed the semantic models in the changed schema files [here](https://github.com/dbt-labs/dbt-core/blob/af0cbcb6a58c44991a09da978a9edd2fc65eb6de/core/dbt/parser/partial.py#L612).

Also when we update it we probably need to update all of the things that depends on the semantic model.
Thank you @ChenyuLInx 🙏

Sounds like we may need to either add `semantic_models` to [this](https://github.com/dbt-labs/dbt-core/blob/af0cbcb6a58c44991a09da978a9edd2fc65eb6de/core/dbt/parser/partial.py#L620-L621) list or do something similar to the [one for sources](https://github.com/dbt-labs/dbt-core/blob/af0cbcb6a58c44991a09da978a9edd2fc65eb6de/core/dbt/parser/partial.py#L643-L644)?

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
@@ -922,6 +922,9 @@ def delete_schema_semantic_model(self, schema_file, semantic_model_dict):
             if unique_id in self.saved_manifest.semantic_models:
                 semantic_model = self.saved_manifest.semantic_models[unique_id]
                 if semantic_model.name == semantic_model_name:
+                    # Need to find everything that referenced this semantic model and schedule for parsing
+                    if unique_id in self.saved_manifest.child_map:
+                        self.schedule_nodes_for_parsing(self.saved_manifest.child_map[unique_id])
                     self.saved_manifest.semantic_models.pop(unique_id)
                     schema_file.semantic_models.remove(unique_id)
             elif unique_id in self.saved_manifest.disabled:


Test patch:
diff --git a/tests/functional/semantic_models/fixtures.py b/tests/functional/semantic_models/fixtures.py
--- a/tests/functional/semantic_models/fixtures.py
+++ b/tests/functional/semantic_models/fixtures.py
@@ -86,6 +86,39 @@
       agg_time_dimension: created_at
 """
 
+semantic_model_people_diff_name_yml = """
+version: 2
+
+semantic_models:
+  - name: semantic_people_diff_name
+    label: "Semantic People"
+    model: ref('people')
+    dimensions:
+      - name: favorite_color
+        label: "Favorite Color"
+        type: categorical
+      - name: created_at
+        label: "Created At"
+        type: TIME
+        type_params:
+          time_granularity: day
+    measures:
+      - name: years_tenure
+        label: "Years Tenure"
+        agg: SUM
+        expr: tenure
+      - name: people
+        label: "People"
+        agg: count
+        expr: id
+    entities:
+      - name: id
+        label: "Primary ID"
+        type: primary
+    defaults:
+      agg_time_dimension: created_at
+"""
+
 semantic_model_descriptions = """
 {% docs semantic_model_description %} foo {% enddocs %}
 {% docs dimension_description %} bar {% enddocs %}
diff --git a/tests/functional/semantic_models/test_semantic_models.py b/tests/functional/semantic_models/test_semantic_models.py
--- a/tests/functional/semantic_models/test_semantic_models.py
+++ b/tests/functional/semantic_models/test_semantic_models.py
@@ -3,11 +3,13 @@
 from dbt.contracts.graph.manifest import Manifest
 from dbt.exceptions import CompilationError
 from dbt.tests.util import run_dbt
+from dbt.tests.util import write_file
 from tests.functional.semantic_models.fixtures import (
     models_people_sql,
     simple_metricflow_time_spine_sql,
     semantic_model_people_yml,
     models_people_metrics_yml,
+    semantic_model_people_diff_name_yml,
     semantic_model_people_yml_with_docs,
     semantic_model_descriptions,
 )
@@ -71,3 +73,27 @@ def test_unknown_model_raises_issue(self, project):
         with pytest.raises(CompilationError) as excinfo:
             run_dbt(["parse"])
         assert "depends on a node named 'people' which was not found" in str(excinfo.value)
+
+
+class TestSemanticModelPartialParsing:
+    @pytest.fixture(scope="class")
+    def models(self):
+        return {
+            "people.sql": models_people_sql,
+            "metricflow_time_spine.sql": simple_metricflow_time_spine_sql,
+            "semantic_models.yml": semantic_model_people_yml,
+            "people_metrics.yml": models_people_metrics_yml,
+        }
+
+    def test_semantic_model_deleted_partial_parsing(self, project):
+        # First, use the default saved_queries.yml to define our saved_query, and
+        # run the dbt parse command
+        run_dbt(["parse"])
+        # Next, modify the default semantic_models.yml to remove the saved query.
+        write_file(
+            semantic_model_people_diff_name_yml,
+            project.project_root,
+            "models",
+            "semantic_models.yml",
+        )
+        run_dbt(["compile"])