Repository: dbt-labs/dbt-core

Issue:
[CT-3071] [Bug] failed to resolve schema version
### 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

### Current Behavior


The latest `get_manifest_schema_version` will get wrong versions and drop the metrics data:

https://github.com/dbt-labs/dbt-core/blob/v1.6.1/core/dbt/contracts/graph/manifest.py

```py
def get_manifest_schema_version(dct: dict) -> int:
    schema_version = dct.get("metadata", {}).get("dbt_schema_version", None)
    if not schema_version:
        raise ValueError("Manifest doesn't have schema version")
    return int(schema_version.split(".")[-2][-1])
```

This input will get `0`

```
https://schemas.getdbt.com/dbt/manifest/v10.json
```

And it will make scheam upgrader to drop metrics data
https://github.com/dbt-labs/dbt-core/blob/v1.6.1/core/dbt/contracts/graph/manifest_upgrade.py#L65-L69

```py
def upgrade_manifest_json(manifest: dict, manifest_schema_version: int) -> dict:
    # this should remain 9 while the check in `upgrade_schema_version` may change
    if manifest_schema_version <= 9:
        drop_v9_and_prior_metrics(manifest=manifest)
   ...
```

### Expected Behavior

The `metrics` data should be kept

### Steps To Reproduce

1. use the dbt 1.6 project with metrics (https://github.com/InfuseAI/jaffle-sl-template)
2. create the state directory `dbt parse --target-path baseline`
3. modify some metrics criteria
4. select modified and nothing selected `dbt list -s 'state:modified' --state baseline`


<img width="1582" alt="image" src="https://github.com/dbt-labs/dbt-core/assets/193223/fb3bfe07-20e8-4c28-8539-b9ca862c3b52">


### Relevant log output

_No response_

### Environment

```markdown
- OS: OSX
- Python: 3.10.6
- dbt:


Core:
  - installed: 1.6.1
  - latest:    1.6.1 - Up to date!

Plugins:
  - snowflake: 1.6.1 - Update available!
  - duckdb:    1.6.0 - Up to date!

  At least one plugin is out of date or incompatible with dbt-core.
  You can find instructions for upgrading here:
  https://docs.getdbt.com/docs/installation
```
```


### Which database adapter are you using with dbt?

other (mention it in "Additional Context")

### Additional Context

_No response_
[CT-3071] [Bug] failed to resolve schema version
### 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

### Current Behavior


The latest `get_manifest_schema_version` will get wrong versions and drop the metrics data:

https://github.com/dbt-labs/dbt-core/blob/v1.6.1/core/dbt/contracts/graph/manifest.py

```py
def get_manifest_schema_version(dct: dict) -> int:
    schema_version = dct.get("metadata", {}).get("dbt_schema_version", None)
    if not schema_version:
        raise ValueError("Manifest doesn't have schema version")
    return int(schema_version.split(".")[-2][-1])
```

This input will get `0`

```
https://schemas.getdbt.com/dbt/manifest/v10.json
```

And it will make scheam upgrader to drop metrics data
https://github.com/dbt-labs/dbt-core/blob/v1.6.1/core/dbt/contracts/graph/manifest_upgrade.py#L65-L69

```py
def upgrade_manifest_json(manifest: dict, manifest_schema_version: int) -> dict:
    # this should remain 9 while the check in `upgrade_schema_version` may change
    if manifest_schema_version <= 9:
        drop_v9_and_prior_metrics(manifest=manifest)
   ...
```

### Expected Behavior

The `metrics` data should be kept

### Steps To Reproduce

1. use the dbt 1.6 project with metrics (https://github.com/InfuseAI/jaffle-sl-template)
2. create the state directory `dbt parse --target-path baseline`
3. modify some metrics criteria
4. select modified and nothing selected `dbt list -s 'state:modified' --state baseline`


<img width="1582" alt="image" src="https://github.com/dbt-labs/dbt-core/assets/193223/fb3bfe07-20e8-4c28-8539-b9ca862c3b52">


### Relevant log output

_No response_

### Environment

```markdown
- OS: OSX
- Python: 3.10.6
- dbt:


Core:
  - installed: 1.6.1
  - latest:    1.6.1 - Up to date!

Plugins:
  - snowflake: 1.6.1 - Update available!
  - duckdb:    1.6.0 - Up to date!

  At least one plugin is out of date or incompatible with dbt-core.
  You can find instructions for upgrading here:
  https://docs.getdbt.com/docs/installation
```
```


### Which database adapter are you using with dbt?

other (mention it in "Additional Context")

### Additional Context

_No response_


Discussion:
@qrtt1 Thanks for opening! This is definitely a bug, and one we should resolve quickly, since it will affect anyone who's upgrading to v1.6.

This is a naive fix:
```python
import re
...
def get_manifest_schema_version(dct: dict) -> int:
    schema_version = dct.get("metadata", {}).get("dbt_schema_version", None)
    if not schema_version:
        raise ValueError("Manifest doesn't have schema version")
    match_str = "v([0-9]+)"
    return int(re.search(match_str, schema_version).groups()[0])
```
```python
>>> schema_version = "https://schemas.getdbt.com/dbt/manifest/v10.json"
>>> match_str = "v([0-9]+)"
>>> int(re.search(match_str, schema_version).groups()[0])
10
```

We should also figure out why this errant behavior wasn't being caught in our test for manifest version upgrades / backward compatibility: https://github.com/dbt-labs/dbt-core/blob/main/tests/functional/artifacts/test_previous_version_state.py
I'm able to replicate this issue and updated the test case to fail: https://github.com/dbt-labs/dbt-core/pull/8551.

I'll now work on updating the code to make the test case pass.
@qrtt1 Thanks for opening! This is definitely a bug, and one we should resolve quickly, since it will affect anyone who's upgrading to v1.6.

This is a naive fix:
```python
import re
...
def get_manifest_schema_version(dct: dict) -> int:
    schema_version = dct.get("metadata", {}).get("dbt_schema_version", None)
    if not schema_version:
        raise ValueError("Manifest doesn't have schema version")
    match_str = "v([0-9]+)"
    return int(re.search(match_str, schema_version).groups()[0])
```
```python
>>> schema_version = "https://schemas.getdbt.com/dbt/manifest/v10.json"
>>> match_str = "v([0-9]+)"
>>> int(re.search(match_str, schema_version).groups()[0])
10
```

We should also figure out why this errant behavior wasn't being caught in our test for manifest version upgrades / backward compatibility: https://github.com/dbt-labs/dbt-core/blob/main/tests/functional/artifacts/test_previous_version_state.py
I'm able to replicate this issue and updated the test case to fail: https://github.com/dbt-labs/dbt-core/pull/8551.

I'll now work on updating the code to make the test case pass.

Resolving patch:
diff --git a/core/dbt/contracts/graph/manifest.py b/core/dbt/contracts/graph/manifest.py
--- a/core/dbt/contracts/graph/manifest.py
+++ b/core/dbt/contracts/graph/manifest.py
@@ -1528,7 +1528,15 @@ def get_manifest_schema_version(dct: dict) -> int:
     schema_version = dct.get("metadata", {}).get("dbt_schema_version", None)
     if not schema_version:
         raise ValueError("Manifest doesn't have schema version")
-    return int(schema_version.split(".")[-2][-1])
+
+    # schema_version is in this format: https://schemas.getdbt.com/dbt/manifest/v10.json
+    # What the code below is doing:
+    # 1. Split on "/" – v10.json
+    # 2. Split on "." – v10
+    # 3. Skip first character – 10
+    # 4. Convert to int
+    # TODO: If this gets more complicated, turn into a regex
+    return int(schema_version.split("/")[-1].split(".")[0][1:])
 
 
 def _check_duplicates(value: BaseNode, src: Mapping[str, BaseNode]):


Test patch:
diff --git a/tests/functional/artifacts/test_previous_version_state.py b/tests/functional/artifacts/test_previous_version_state.py
--- a/tests/functional/artifacts/test_previous_version_state.py
+++ b/tests/functional/artifacts/test_previous_version_state.py
@@ -1,9 +1,12 @@
-import pytest
+import json
 import os
 import shutil
-from dbt.tests.util import run_dbt, get_manifest
+
+import pytest
+
+from dbt.contracts.graph.manifest import WritableManifest, get_manifest_schema_version
 from dbt.exceptions import IncompatibleSchemaError
-from dbt.contracts.graph.manifest import WritableManifest
+from dbt.tests.util import run_dbt, get_manifest
 
 # This project must have one of each kind of node type, plus disabled versions, for
 # test coverage to be complete.
@@ -352,3 +355,13 @@ def test_nonbackwards_compatible_versions(self, project):
         # schema versions 1, 2, 3 are all not forward compatible
         for schema_version in range(1, 4):
             self.compare_previous_state(project, schema_version, False, 0)
+
+    def test_get_manifest_schema_version(self, project):
+        for schema_version in range(1, self.CURRENT_EXPECTED_MANIFEST_VERSION):
+            manifest_path = os.path.join(
+                project.test_data_dir, f"state/v{schema_version}/manifest.json"
+            )
+            manifest = json.load(open(manifest_path))
+
+            manifest_version = get_manifest_schema_version(manifest)
+            assert manifest_version == schema_version