Repository: dbt-labs/dbt-core

Issue:
Ambiguity in --models flag fails fails to select models correctly
## Issue

### Issue description
The `--models` selection syntax will prefer package-based selections over path-based selections, whereas it should instead support both. This [model selection code](https://github.com/fishtown-analytics/dbt/blob/dev/guion-bluford/dbt/graph/selector.py#L89 ) uses an if/else if/else, but it should instead be three distinct if statements, I think. In particular, the nested `if is_selected_node()` branch inside of the `package` branch "steals" the opportunity for selection away from the `else` block.

Existing code:
```
        if len(qualified_name) == 1 and fqn_ish[-1] == qualified_name[0]:
            yield node

        elif qualified_name[0] in package_names:
            if is_selected_node(fqn_ish, qualified_name):
                yield node

        else:
            for package_name in package_names:
                local_qualified_node_name = [package_name] + qualified_name
                if is_selected_node(fqn_ish, local_qualified_node_name):
                    yield node
                    break
```

Suggested changes (though other implementations will work and might be smarter):
```
        if len(qualified_name) == 1 and fqn_ish[-1] == qualified_name[0]:
            yield node

        if qualified_name[0] in package_names:
            if is_selected_node(fqn_ish, qualified_name):
                yield node

        for package_name in package_names:
            local_qualified_node_name = [package_name] + qualified_name
            if is_selected_node(fqn_ish, local_qualified_node_name):
                yield node
                break
```

I think the original if/else if/else implementation was meant as an optimization to short-circuit selection checks that we knew wouldn't match, but it seems like that is not the case.

### Results
Models are not selected when they should be.

### System information
0.11.1

### Steps to reproduce

Given a project file:
```yml
# dbt_project.yml

name: my_project
```

And a model:
```
-- models/my_project/some_model.sql

select 1 as id
```

Running:
```
dbt run --model my_project.some_model
```

will not select any nodes. The `my_project` in the selector matches a package name, so dbt thinks the selection a fully qualified name for a model. Instead, it's a "locally" qualified name. You can make this work by doing:

```
dbt run --model my_project.my_project.some_model
```

which is the fully-qualified name for this model.


Discussion:

Resolving patch:
diff --git a/dbt/graph/selector.py b/dbt/graph/selector.py
--- a/dbt/graph/selector.py
+++ b/dbt/graph/selector.py
@@ -74,29 +74,43 @@ def is_selected_node(real_node, node_selector):
     return True
 
 
+def _node_is_match(qualified_name, package_names, fqn):
+    """Determine if a qualfied name matches an fqn, given the set of package
+    names in the graph.
+
+    :param List[str] qualified_name: The components of the selector or node
+        name, split on '.'.
+    :param Set[str] package_names: The set of pacakge names in the graph.
+    :param List[str] fqn: The node's fully qualified name in the graph.
+    """
+    if len(qualified_name) == 1 and fqn[-1] == qualified_name[0]:
+        return True
+
+    if qualified_name[0] in package_names:
+        if is_selected_node(fqn, qualified_name):
+            return True
+
+    for package_name in package_names:
+        local_qualified_node_name = [package_name] + qualified_name
+        if is_selected_node(fqn, local_qualified_node_name):
+            return True
+
+    return False
+
+
 def get_nodes_by_qualified_name(graph, qualified_name):
-    """ returns a node if matched, else throws a CompilerError. qualified_name
-    should be either 1) a node name or 2) a dot-notation qualified selector"""
+    """Yield all nodes in the graph that match qualified_name.
 
+    :param List[str] qualified_name: The components of the selector or node
+        name, split on '.'.
+    """
     package_names = get_package_names(graph)
 
     for node in graph.nodes():
         fqn_ish = graph.node[node]['fqn']
-
-        if len(qualified_name) == 1 and fqn_ish[-1] == qualified_name[0]:
+        if _node_is_match(qualified_name, package_names, fqn_ish):
             yield node
 
-        elif qualified_name[0] in package_names:
-            if is_selected_node(fqn_ish, qualified_name):
-                yield node
-
-        else:
-            for package_name in package_names:
-                local_qualified_node_name = [package_name] + qualified_name
-                if is_selected_node(fqn_ish, local_qualified_node_name):
-                    yield node
-                    break
-
 
 def get_nodes_from_spec(graph, spec):
     select_parents = spec['select_parents']


Test patch:
diff --git a/test/integration/007_graph_selection_tests/models/test/subdir.sql b/test/integration/007_graph_selection_tests/models/test/subdir.sql
new file mode 100644
--- /dev/null
+++ b/test/integration/007_graph_selection_tests/models/test/subdir.sql
@@ -0,0 +1 @@
+select 1 as id
diff --git a/test/integration/007_graph_selection_tests/models/test/subdir/nested_users.sql b/test/integration/007_graph_selection_tests/models/test/subdir/nested_users.sql
new file mode 100644
--- /dev/null
+++ b/test/integration/007_graph_selection_tests/models/test/subdir/nested_users.sql
@@ -0,0 +1 @@
+select 1 as id
diff --git a/test/integration/007_graph_selection_tests/test_graph_selection.py b/test/integration/007_graph_selection_tests/test_graph_selection.py
--- a/test/integration/007_graph_selection_tests/test_graph_selection.py
+++ b/test/integration/007_graph_selection_tests/test_graph_selection.py
@@ -13,8 +13,6 @@ def models(self):
 
     @attr(type='postgres')
     def test__postgres__specific_model(self):
-        self.use_profile('postgres')
-        self.use_default_project()
         self.run_sql_file("test/integration/007_graph_selection_tests/seed.sql")
 
         results = self.run_dbt(['run', '--models', 'users'])
@@ -28,8 +26,6 @@ def test__postgres__specific_model(self):
 
     @attr(type='snowflake')
     def test__snowflake__specific_model(self):
-        self.use_profile('snowflake')
-        self.use_default_project()
         self.run_sql_file("test/integration/007_graph_selection_tests/seed.sql")
 
         results = self.run_dbt(['run', '--models', 'users'])
@@ -44,8 +40,6 @@ def test__snowflake__specific_model(self):
 
     @attr(type='postgres')
     def test__postgres__specific_model_and_children(self):
-        self.use_profile('postgres')
-        self.use_default_project()
         self.run_sql_file("test/integration/007_graph_selection_tests/seed.sql")
 
         results = self.run_dbt(['run', '--models', 'users+'])
@@ -59,8 +53,6 @@ def test__postgres__specific_model_and_children(self):
 
     @attr(type='snowflake')
     def test__snowflake__specific_model_and_children(self):
-        self.use_profile('snowflake')
-        self.use_default_project()
         self.run_sql_file("test/integration/007_graph_selection_tests/seed.sql")
 
         results = self.run_dbt(['run', '--models', 'users+'])
@@ -77,8 +69,6 @@ def test__snowflake__specific_model_and_children(self):
 
     @attr(type='postgres')
     def test__postgres__specific_model_and_parents(self):
-        self.use_profile('postgres')
-        self.use_default_project()
         self.run_sql_file("test/integration/007_graph_selection_tests/seed.sql")
 
         results = self.run_dbt(['run', '--models', '+users_rollup'])
@@ -92,8 +82,6 @@ def test__postgres__specific_model_and_parents(self):
 
     @attr(type='snowflake')
     def test__snowflake__specific_model_and_parents(self):
-        self.use_profile('snowflake')
-        self.use_default_project()
         self.run_sql_file("test/integration/007_graph_selection_tests/seed.sql")
 
         results = self.run_dbt(['run', '--models', '+users_rollup'])
@@ -111,8 +99,6 @@ def test__snowflake__specific_model_and_parents(self):
 
     @attr(type='postgres')
     def test__postgres__specific_model_with_exclusion(self):
-        self.use_profile('postgres')
-        self.use_default_project()
         self.run_sql_file("test/integration/007_graph_selection_tests/seed.sql")
 
         results = self.run_dbt(
@@ -128,8 +114,6 @@ def test__postgres__specific_model_with_exclusion(self):
 
     @attr(type='snowflake')
     def test__snowflake__specific_model_with_exclusion(self):
-        self.use_profile('snowflake')
-        self.use_default_project()
         self.run_sql_file("test/integration/007_graph_selection_tests/seed.sql")
 
         results = self.run_dbt(
@@ -142,3 +126,15 @@ def test__snowflake__specific_model_with_exclusion(self):
         self.assertFalse('BASE_USERS' in created_models)
         self.assertFalse('USERS_ROLLUP' in created_models)
         self.assertFalse('EMAILS' in created_models)
+
+    @attr(type='postgres')
+    def test__postgres__locally_qualified_name(self):
+        results = self.run_dbt(['run', '--models', 'test.subdir'])
+        self.assertEqual(len(results), 2)
+
+        created_models = self.get_models_in_schema()
+        self.assertNotIn('users_rollup', created_models)
+        self.assertNotIn('base_users', created_models)
+        self.assertNotIn('emails', created_models)
+        self.assertIn('subdir', created_models)
+        self.assertIn('nested_users', created_models)
diff --git a/test/integration/007_graph_selection_tests/test_schema_test_graph_selection.py b/test/integration/007_graph_selection_tests/test_schema_test_graph_selection.py
--- a/test/integration/007_graph_selection_tests/test_schema_test_graph_selection.py
+++ b/test/integration/007_graph_selection_tests/test_schema_test_graph_selection.py
@@ -23,13 +23,10 @@ def packages_config(self):
         }
 
     def run_schema_and_assert(self, include, exclude, expected_tests):
-        self.use_profile('postgres')
-        self.use_default_project()
-
         self.run_sql_file("test/integration/007_graph_selection_tests/seed.sql")
         self.run_dbt(["deps"])
         results = self.run_dbt()
-        self.assertEqual(len(results), 5)
+        self.assertEqual(len(results), 7)
 
         args = FakeArgs()
         args.models = include