Repository: dbt-labs/dbt-core
Pull request: [Fix] respect project root when loading seeds
resolves https://github.com/dbt-labs/dbt-core/issues/6875

<!---
  Include the number of the issue addressed by this PR above if applicable.
  PRs for code changes without an associated issue *will not be merged*.
  See CONTRIBUTING.md for more information.

  Add the `user docs` label to this PR if it will need docs changes.  An 
  issue will get opened in docs.getdbt.com upon successful merge of this PR.
-->

### Problem
`dbt seed` fails when attempting to leverage partial parse file across dbt invocations in the same project, but different root directories. 

<!---
  Describe the problem this PR is solving. What is the application state
  before this PR is merged?
-->

### Solution

<!---
  Describe the way this PR solves the above problem. Add as much detail as you
  can to help reviewers understand your changes. Include any alternatives and
  tradeoffs you considered.
-->

Use the project root instead of the root path on the model node (which could be from a prior dbt run in a different directory, but the same _project_) when loading seeds.

Risk assessment (for backporting): 
* Added additional safety here wrt preserving previous behaviour and try to use the model.root_path, and if the file doesn't exist, falling back to the project root.
* no changes to manifest schema

### Checklist

- [x] I have read [the contributing guide](https://github.com/dbt-labs/dbt-core/blob/main/CONTRIBUTING.md) and understand what's expected of me  
- [x] I have run this code in development and it appears to resolve the stated issue  
- [x] This PR includes tests, or tests are not required/relevant for this PR
- [x] This PR has no interface changes (e.g. macros, cli, logs, json artifacts, config files, adapter interface, etc) or this PR has already received feedback and approval from Product or DX
- [x] This PR includes [type annotations](https://docs.python.org/3/library/typing.html) for new and modified functions


Problem statement:
[CT-2042] Enable seeds to be handled from stored manifest data
Right now we don't store the content of a seed file, we just store a checksum of the content or just the path if the size of the seed exceeds 1 megabyte. The seed is retrieved later using the absolute path of the file.  This is not functional for a world in which files can come from a file diff and we have portable manifests.

We need to decide how to do this for file diffs and portable manifests. How important is the existence of large seeds to our users? It is documented as an anti-pattern, but I'm sure some people are doing it. Do we want to  load the content below a certain size? Do some kind of special handling?

Hints from issue discussion:
My instinct here is that we should start storing raw seed contents up to 1 MB. For any seeds that are larger, we should continue storing only the file path pointing to that seed.

That would be consistent with the behavior we've established for detecting seed modifications in `state:modified`: comparing a checksum of contents for seeds that are <1 MB; only comparing the file path for seeds that are >1 MB.

The risk is that a project has 100 seeds, each 0.9 MB in size, and we've suddenly bloated the manifest by 90 MB. We could also look into compression... or that could be a problem for another application to solve.
Let's revisit & prioritize as part of broader scoping for `file_system` work
In order to do this we might want to break out the current manifest into several individual pieces first.

Commit message: preserve relative path in seed root_path

Patch to review:
diff --git a/core/dbt/context/providers.py b/core/dbt/context/providers.py
index 996d5027c58..1d4820ee52e 100644
--- a/core/dbt/context/providers.py
+++ b/core/dbt/context/providers.py
@@ -863,7 +863,9 @@ def load_agate_table(self) -> agate.Table:
         if not isinstance(self.model, SeedNode):
             raise LoadAgateTableNotSeedError(self.model.resource_type, node=self.model)
         assert self.model.root_path
-        path = os.path.join(self.model.root_path, self.model.original_file_path)
+        path = os.path.join(
+            self.config.project_root, self.model.root_path, self.model.original_file_path
+        )
         column_types = self.model.config.column_types
         delimiter = self.model.config.delimiter
         try:
diff --git a/core/dbt/parser/seeds.py b/core/dbt/parser/seeds.py
index 23c77e1ed7c..c11e97df757 100644
--- a/core/dbt/parser/seeds.py
+++ b/core/dbt/parser/seeds.py
@@ -1,3 +1,5 @@
+from os.path import relpath
+
 from dbt.context.context_config import ContextConfig
 from dbt.contracts.graph.nodes import SeedNode
 from dbt.node_types import NodeType
@@ -8,7 +10,7 @@
 class SeedParser(SimpleSQLParser[SeedNode]):
     def parse_from_dict(self, dct, validate=True) -> SeedNode:
         # seeds need the root_path because the contents are not loaded
-        dct["root_path"] = self.project.project_root
+        dct["root_path"] = relpath(self.project.project_root, self.root_project.project_root)
         if "language" in dct:
             del dct["language"]
         # raw_code is not currently used, but it might be in the future


Code review comments:
diff_hunk: @@ -8,7 +10,7 @@
 class SeedParser(SimpleSQLParser[SeedNode]):
     def parse_from_dict(self, dct, validate=True) -> SeedNode:
         # seeds need the root_path because the contents are not loaded
-        dct["root_path"] = self.project.project_root
+        dct["root_path"] = relpath(self.project.project_root, self.root_project.project_root)
path: core/dbt/parser/seeds.py
text: @author:
Changing this attribute to store the relative root path instead of the absolute root path to the seed node definition file

@author:
This assumes that the dependency project and root project have a shared common path. Is it possible to install packages to a directory outside of the root project? Via packages-install-path? https://docs.getdbt.com/reference/project-configs/packages-install-path

@author:
Doing some manual testing, looks like dbt always installs the provided packages-install-path to the project directory, which is good news!