Skip to content

Commit 64e62eb

Browse files
fix: resolve refs from disabled manifest nodes (transient ClickHouse CI failure) (#944)
* fix: resolve refs from disabled manifest nodes when elementary_enabled=False corrupts manifest When a test runs dbt with elementary_enabled=False (e.g. test_collect_metrics_elementary_disabled), dbt rewrites target/manifest.json with elementary models in the 'disabled' section instead of 'nodes'. If a subsequent test on the same xdist worker uses AdapterQueryRunner to read_table, it reads this stale manifest and fails with 'Cannot resolve ref: not found in dbt manifest'. Fix: _load_manifest_maps now scans both 'nodes' and 'disabled' sections. For disabled nodes that lack relation_name (dbt skips compilation for disabled nodes), the relation name is synthesized from database/schema/alias using the adapter's Relation class. Co-Authored-By: Itamar Hartstein <haritamar@gmail.com> * address review: add debug logging to fallback paths, simplify disabled node handling Co-Authored-By: Itamar Hartstein <haritamar@gmail.com> * address review: use adapter include_policy instead of try/except for database handling Co-Authored-By: Itamar Hartstein <haritamar@gmail.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Itamar Hartstein <haritamar@gmail.com>
1 parent c69ebaf commit 64e62eb

1 file changed

Lines changed: 47 additions & 5 deletions

File tree

integration_tests/tests/adapter_query_runner.py

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,38 @@ def _create_adapter(project_dir: str, target: str) -> BaseAdapter:
115115
# Ref resolution
116116
# ------------------------------------------------------------------
117117

118+
def _build_relation_name(self, node: Dict[str, Any]) -> Optional[str]:
119+
"""Construct a relation name from node metadata.
120+
121+
Uses the adapter's ``Relation`` class so that quoting and include
122+
policies are applied correctly for the target warehouse.
123+
The adapter's ``include_policy`` determines whether ``database`` is
124+
passed (e.g. ClickHouse sets ``database=False``).
125+
"""
126+
schema = node.get("schema")
127+
identifier = node.get("alias") or node.get("name")
128+
if not identifier:
129+
return None
130+
131+
include_policy = self._adapter.Relation.get_default_include_policy()
132+
database = (node.get("database") or "") if include_policy.database else ""
133+
134+
relation = self._adapter.Relation.create(
135+
database=database,
136+
schema=schema,
137+
identifier=identifier,
138+
)
139+
return relation.render()
140+
118141
def _load_manifest_maps(self) -> None:
119-
"""Load ref and source maps from the dbt manifest."""
142+
"""Load ref and source maps from the dbt manifest.
143+
144+
Scans both ``nodes`` (enabled models) and ``disabled`` (models
145+
disabled via config, e.g. ``elementary_enabled = False``).
146+
For disabled nodes whose ``relation_name`` is ``None`` (dbt skips
147+
compilation for disabled nodes), the relation name is synthesised
148+
from ``database`` / ``schema`` / ``alias`` via the adapter.
149+
"""
120150
manifest_path = Path(self._project_dir) / "target" / "manifest.json"
121151
if not manifest_path.exists():
122152
raise FileNotFoundError(
@@ -126,12 +156,24 @@ def _load_manifest_maps(self) -> None:
126156
with open(manifest_path) as fh:
127157
manifest = json.load(fh)
128158

159+
# Collect every node dict: enabled nodes first, then disabled.
160+
all_nodes: List[Dict[str, Any]] = list(manifest.get("nodes", {}).values())
161+
disabled = manifest.get("disabled", {})
162+
for versions in disabled.values():
163+
all_nodes.extend(versions)
164+
129165
ref_map: Dict[str, str] = {}
130-
for node in manifest.get("nodes", {}).values():
131-
relation_name = node.get("relation_name")
166+
for node in all_nodes:
132167
name = node.get("name")
133-
if relation_name and name:
134-
ref_map[name] = relation_name
168+
if not name:
169+
continue
170+
relation_name = node.get("relation_name")
171+
if not relation_name:
172+
relation_name = self._build_relation_name(node)
173+
if relation_name:
174+
# First writer wins — enabled nodes are processed first,
175+
# so a disabled duplicate won't overwrite an enabled one.
176+
ref_map.setdefault(name, relation_name)
135177

136178
source_map: Dict[tuple, str] = {}
137179
for source in manifest.get("sources", {}).values():

0 commit comments

Comments
 (0)