diff --git a/core/dbt/contracts/graph/manifest.py b/core/dbt/contracts/graph/manifest.py index c3e4354c270..79f1f7ab67b 100644 --- a/core/dbt/contracts/graph/manifest.py +++ b/core/dbt/contracts/graph/manifest.py @@ -381,6 +381,14 @@ def search(self, haystack: Iterable[N]) -> Optional[N]: return model return None + def search_many(self, haystack: Iterable[N]) -> List[N]: + """Find multiple entries in the given iterable by name.""" + found = [] + for model in haystack: + if self._matches(model): + found.append(model) + return found + D = TypeVar('D') @@ -515,6 +523,16 @@ def build_flat_graph(self): } } + def find_nodes_by_name( + self, name: str, package: Optional[str] = None + ) -> Optional[ManifestNode]: + searcher: NameSearcher = NameSearcher( + name, package, NodeType.refable() + ) + + result = searcher.search_many(self.nodes.values()) + return result + def find_disabled_by_name( self, name: str, package: Optional[str] = None ) -> Optional[ManifestNode]: diff --git a/core/dbt/exceptions.py b/core/dbt/exceptions.py index 1a1134e3a26..0bba69c88c0 100644 --- a/core/dbt/exceptions.py +++ b/core/dbt/exceptions.py @@ -761,6 +761,21 @@ def raise_duplicate_macro_name(node_1, node_2, namespace) -> NoReturn: ) +def raise_ambiguous_ref(node_1, node_2): + duped_name = node_1.name + get_func = f'{{{{ ref("{duped_name}") }}}}' + + raise_compiler_error( + f'The reference {get_func} is ambiguous because there are two nodes ' + f'with the name "{duped_name}". To fix this, either change the name ' + 'of one of these nodes, or use the two-argument version of ref() ' + 'to specify the package that contains the intended node. Found nodes:\n' + f'- {node_1.unique_id} ({node_1.original_file_path})\n' + f'- {node_2.unique_id} ({node_2.original_file_path})\n\n' + f'Example: {{{{ ref("{node_1.package_name}", "{node_1.name}") }}}}' + ) + + def raise_duplicate_resource_name(node_1, node_2): duped_name = node_1.name diff --git a/core/dbt/parser/manifest.py b/core/dbt/parser/manifest.py index fd0e7b3e572..0cf8a247710 100644 --- a/core/dbt/parser/manifest.py +++ b/core/dbt/parser/manifest.py @@ -522,11 +522,22 @@ def _check_resource_uniqueness( relation = relation_cls.create_from(config=config, node=node) full_node_name = str(relation) - existing_node = names_resources.get(name) - if existing_node is not None: - dbt.exceptions.raise_duplicate_resource_name( - existing_node, node - ) + # Check refs - is there an unqualified ref to a + # model that exists in two different packages? + for ref in node.refs: + if len(ref) == 1: + matched = manifest.find_nodes_by_name(ref[0]) + else: + # two-arg refs are namespaced, so no need + # to check for duplicates. Nodes must still + # have unique names within a package + continue + + if len(matched) > 1: + dbt.exceptions.raise_ambiguous_ref( + matched[0], + matched[1] + ) existing_alias = alias_resources.get(full_node_name) if existing_alias is not None: