Skip to content

Commit 2a20bcc

Browse files
Fix: [SNOW-2193494] Use name part of FQN for DBT temporary stage creation
1 parent 5c07465 commit 2a20bcc

File tree

4 files changed

+96
-7
lines changed

4 files changed

+96
-7
lines changed

RELEASE-NOTES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
## New additions
2222

2323
## Fixes and improvements
24+
* Fixed DBT deploy command to properly handle fully qualified names
2425

2526

2627
# v3.10.0

src/snowflake/cli/_plugins/dbt/manager.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def exists(name: FQN) -> bool:
4444

4545
def deploy(
4646
self,
47-
name: FQN,
47+
fqn: FQN,
4848
path: SecurePath,
4949
profiles_path: SecurePath,
5050
force: bool,
@@ -66,7 +66,7 @@ def deploy(
6666

6767
with cli_console.phase("Creating temporary stage"):
6868
stage_manager = StageManager()
69-
stage_fqn = FQN.from_string(f"dbt_{name}_stage").using_context()
69+
stage_fqn = FQN.from_string(f"dbt_{fqn.name}_stage").using_context()
7070
stage_name = stage_manager.get_standard_stage_prefix(stage_fqn)
7171
stage_manager.create(stage_fqn, temporary=True)
7272

@@ -86,11 +86,11 @@ def deploy(
8686

8787
with cli_console.phase("Creating DBT project"):
8888
if force is True:
89-
query = f"CREATE OR REPLACE DBT PROJECT {name}"
90-
elif self.exists(name=name):
91-
query = f"ALTER DBT PROJECT {name} ADD VERSION"
89+
query = f"CREATE OR REPLACE DBT PROJECT {fqn}"
90+
elif self.exists(name=fqn):
91+
query = f"ALTER DBT PROJECT {fqn} ADD VERSION"
9292
else:
93-
query = f"CREATE DBT PROJECT {name}"
93+
query = f"CREATE DBT PROJECT {fqn}"
9494
query += f"\nFROM {stage_name}"
9595
return self.execute_query(query)
9696

@@ -174,7 +174,7 @@ def _prepare_profiles_file(profiles_path: Path, tmp_path: Path):
174174
yaml.safe_dump(yaml.safe_load(sfd), tfd)
175175

176176
def execute(
177-
self, dbt_command: str, name: str, run_async: bool, *dbt_cli_args
177+
self, dbt_command: str, name: FQN, run_async: bool, *dbt_cli_args
178178
) -> SnowflakeCursor:
179179
if dbt_cli_args:
180180
dbt_command = " ".join([dbt_command, *dbt_cli_args]).strip()

tests/dbt/test_dbt_commands.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,38 @@ def test_dbt_deploy_with_custom_profiles_dir(
213213
assert result.exit_code == 0, result.output
214214
mock_put_recursive.assert_called_once()
215215

216+
@mock.patch("snowflake.cli._plugins.dbt.manager.StageManager.put_recursive")
217+
@mock.patch("snowflake.cli._plugins.dbt.manager.StageManager.create")
218+
def test_deploys_project_with_fqn_uses_name_only_for_stage(
219+
self,
220+
mock_create,
221+
mock_put_recursive,
222+
mock_connect,
223+
runner,
224+
dbt_project_path,
225+
mock_exists,
226+
):
227+
228+
result = runner.invoke(
229+
[
230+
"dbt",
231+
"deploy",
232+
"MockDatabase.MockSchema.test_dbt_project",
233+
f"--source={dbt_project_path}",
234+
]
235+
)
236+
237+
assert result.exit_code == 0, result.output
238+
assert (
239+
mock_connect.mocked_ctx.get_query()
240+
== """CREATE DBT PROJECT MockDatabase.MockSchema.test_dbt_project
241+
FROM @MockDatabase.MockSchema.dbt_test_dbt_project_stage"""
242+
)
243+
# Verify stage creation uses only the name part of the FQN
244+
stage_fqn = FQN.from_string(f"dbt_test_dbt_project_stage").using_context()
245+
mock_create.assert_called_once_with(stage_fqn, temporary=True)
246+
mock_put_recursive.assert_called_once()
247+
216248
def test_raises_when_dbt_project_yml_is_not_available(
217249
self, dbt_project_path, mock_connect, runner
218250
):
@@ -347,6 +379,16 @@ class TestDBTExecute:
347379
"EXECUTE DBT PROJECT pipeline_name args='compile'",
348380
id="with-cli-flag",
349381
),
382+
pytest.param(
383+
[
384+
"dbt",
385+
"execute",
386+
"database.schema.pipeline_name",
387+
"run",
388+
],
389+
"EXECUTE DBT PROJECT database.schema.pipeline_name args='run'",
390+
id="with-fqn",
391+
),
350392
],
351393
)
352394
def test_dbt_execute(self, mock_connect, mock_cursor, runner, args, expected_query):

tests_integration/test_dbt.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import pytest
1919
import yaml
2020

21+
from snowflake.cli.api.identifiers import FQN
2122
from snowflake.cli._plugins.dbt.constants import PROFILES_FILENAME
2223

2324

@@ -97,6 +98,51 @@ def test_deploy_and_execute(
9798
assert result.json[0]["COUNT"] == 1, result.json[0]
9899

99100

101+
@pytest.mark.integration
102+
@pytest.mark.qa_only
103+
def test_deploy_and_execute_with_full_fqn(
104+
runner,
105+
snowflake_session,
106+
test_database,
107+
project_directory,
108+
snapshot,
109+
):
110+
with project_directory("dbt_project") as root_dir:
111+
# Given a local dbt project
112+
ts = int(datetime.datetime.now().timestamp())
113+
name = f"dbt_project_{ts}"
114+
fqn = FQN.from_string(
115+
f"{snowflake_session.database}.{snowflake_session.schema}.{name}"
116+
)
117+
118+
_setup_dbt_profile(root_dir, snowflake_session, include_password=False)
119+
result = runner.invoke_with_connection_json(["dbt", "deploy", str(fqn)])
120+
assert result.exit_code == 0, result.output
121+
122+
# call `run` on dbt object
123+
result = runner.invoke_passthrough_with_connection(
124+
args=[
125+
"dbt",
126+
"execute",
127+
],
128+
passthrough_args=[str(fqn), "run"],
129+
)
130+
131+
# a successful execution should produce data in my_second_dbt_model and
132+
assert result.exit_code == 0, result.output
133+
assert "Done. PASS=2 WARN=0 ERROR=0 SKIP=0 TOTAL=2" in result.output
134+
135+
result = runner.invoke_with_connection_json(
136+
[
137+
"sql",
138+
"-q",
139+
f"select count(*) as COUNT from {fqn.database}.{fqn.schema}.my_second_dbt_model;",
140+
]
141+
)
142+
assert len(result.json) == 1, result.json
143+
assert result.json[0]["COUNT"] == 1, result.json[0]
144+
145+
100146
@pytest.mark.integration
101147
@pytest.mark.qa_only
102148
def test_dbt_deploy_options(

0 commit comments

Comments
 (0)