diff --git a/.changes/unreleased/Fixes-20240605-111652.yaml b/.changes/unreleased/Fixes-20240605-111652.yaml new file mode 100644 index 00000000000..25c756db86b --- /dev/null +++ b/.changes/unreleased/Fixes-20240605-111652.yaml @@ -0,0 +1,6 @@ +kind: Fixes +body: Fix issues with selectors and inline nodes +time: 2024-06-05T11:16:52.187667-04:00 +custom: + Author: gshank + Issue: 8943 9269 diff --git a/core/dbt/cli/flags.py b/core/dbt/cli/flags.py index d3cfd707cbb..a74172484f3 100644 --- a/core/dbt/cli/flags.py +++ b/core/dbt/cli/flags.py @@ -289,6 +289,10 @@ def _assign_params( params_assigned_from_default, ["WARN_ERROR", "WARN_ERROR_OPTIONS"] ) + # Handle arguments mutually exclusive with INLINE + self._assert_mutually_exclusive(params_assigned_from_default, ["SELECT", "INLINE"]) + self._assert_mutually_exclusive(params_assigned_from_default, ["SELECTOR", "INLINE"]) + # Support lower cased access for legacy code. params = set( x for x in dir(self) if not callable(getattr(self, x)) and not x.startswith("__") @@ -315,7 +319,9 @@ def _assert_mutually_exclusive( """ set_flag = None for flag in group: - flag_set_by_user = flag.lower() not in params_assigned_from_default + flag_set_by_user = ( + hasattr(self, flag) and flag.lower() not in params_assigned_from_default + ) if flag_set_by_user and set_flag: raise DbtUsageException( f"{flag.lower()}: not allowed with argument {set_flag.lower()}" diff --git a/core/dbt/task/compile.py b/core/dbt/task/compile.py index 5470c67c68d..d2460852fc5 100644 --- a/core/dbt/task/compile.py +++ b/core/dbt/task/compile.py @@ -104,6 +104,12 @@ def _runtime_initialize(self): ) sql_node = block_parser.parse_remote(self.args.inline, "inline_query") process_node(self.config, self.manifest, sql_node) + # Special hack to remove disabled, if it's there. This would only happen + # if all models are disabled in dbt_project + if sql_node.config.enabled is False: + sql_node.config.enabled = True + self.manifest.disabled.pop(sql_node.unique_id) + self.manifest.nodes[sql_node.unique_id] = sql_node # keep track of the node added to the manifest self._inline_node_id = sql_node.unique_id except CompilationError as exc: diff --git a/core/dbt/task/runnable.py b/core/dbt/task/runnable.py index a01e7a06c22..6afbcf8597e 100644 --- a/core/dbt/task/runnable.py +++ b/core/dbt/task/runnable.py @@ -5,7 +5,7 @@ from datetime import datetime from multiprocessing.dummy import Pool as ThreadPool from pathlib import Path -from typing import AbstractSet, Dict, Iterable, List, Optional, Set, Tuple +from typing import AbstractSet, Dict, Iterable, List, Optional, Set, Tuple, Union import dbt.exceptions import dbt.tracking @@ -108,7 +108,11 @@ def exclusion_arg(self): def get_selection_spec(self) -> SelectionSpec: default_selector_name = self.config.get_default_selector_name() - if self.args.selector: + spec: Union[SelectionSpec, bool] + if hasattr(self.args, "inline") and self.args.inline: + # We want an empty selection spec. + spec = parse_difference(None, None) + elif self.args.selector: # use pre-defined selector (--selector) spec = self.config.get_selector(self.args.selector) elif not (self.selection_arg or self.exclusion_arg) and default_selector_name: diff --git a/tests/functional/dbt_runner/test_dbt_runner.py b/tests/functional/dbt_runner/test_dbt_runner.py index 80b94b9c73a..0b1607a2eba 100644 --- a/tests/functional/dbt_runner/test_dbt_runner.py +++ b/tests/functional/dbt_runner/test_dbt_runner.py @@ -36,6 +36,9 @@ def test_command_mutually_exclusive_option(self, dbt: dbtRunner) -> None: res = dbt.invoke(["deps", "--warn-error", "--warn-error-options", '{"include": "all"}']) assert type(res.exception) == DbtUsageException + res = dbt.invoke(["compile", "--select", "models", "--inline", "select 1 as id"]) + assert type(res.exception) == DbtUsageException + def test_invalid_command(self, dbt: dbtRunner) -> None: res = dbt.invoke(["invalid-command"]) assert type(res.exception) == DbtUsageException diff --git a/tests/functional/graph_selection/test_inline.py b/tests/functional/graph_selection/test_inline.py new file mode 100644 index 00000000000..bf01ec8ae6a --- /dev/null +++ b/tests/functional/graph_selection/test_inline.py @@ -0,0 +1,64 @@ +import pytest + +from dbt.cli.exceptions import DbtUsageException +from dbt.tests.util import run_dbt, run_dbt_and_capture, write_file + +selectors_yml = """ + selectors: + - name: test_selector + description: Exclude everything + default: true + definition: + method: package + value: "foo" + """ + +dbt_project_yml = """ +name: test +profile: test +flags: + send_anonymous_usage_stats: false +""" + +dbt_project_yml_disabled_models = """ +name: test +profile: test +flags: + send_anonymous_usage_stats: false +models: + +enabled: false +""" + + +class TestCompileInlineWithSelector: + @pytest.fixture(scope="class") + def models(self): + return { + "first_model.sql": "select 1 as id", + } + + @pytest.fixture(scope="class") + def selectors(self): + return selectors_yml + + def test_inline_selectors(self, project): + (results, log_output) = run_dbt_and_capture( + ["compile", "--inline", "select * from {{ ref('first_model') }}"] + ) + assert len(results) == 1 + assert "Compiled inline node is:" in log_output + + # Set all models to disabled, check that we still get inline result + write_file(dbt_project_yml_disabled_models, project.project_root, "dbt_project.yml") + (results, log_output) = run_dbt_and_capture(["compile", "--inline", "select 1 as id"]) + assert len(results) == 1 + + # put back non-disabled dbt_project and check for mutually exclusive error message + # for --select and --inline + write_file(dbt_project_yml, project.project_root, "dbt_project.yml") + with pytest.raises(DbtUsageException): + run_dbt(["compile", "--select", "first_model", "--inline", "select 1 as id"]) + + # check for mutually exclusive --selector and --inline + with pytest.raises(DbtUsageException): + run_dbt(["compile", "--selector", "test_selector", "--inline", "select 1 as id"])