snowflake-cli 3.9.1__py3-none-any.whl → 3.10.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/commands_registration/builtin_plugins.py +2 -2
  3. snowflake/cli/_app/printing.py +53 -13
  4. snowflake/cli/_app/snow_connector.py +1 -0
  5. snowflake/cli/_app/telemetry.py +2 -0
  6. snowflake/cli/_app/version_check.py +73 -6
  7. snowflake/cli/_plugins/cortex/commands.py +8 -3
  8. snowflake/cli/_plugins/cortex/manager.py +24 -20
  9. snowflake/cli/_plugins/dbt/commands.py +5 -2
  10. snowflake/cli/_plugins/dbt/manager.py +9 -7
  11. snowflake/cli/_plugins/{project → dcm}/commands.py +95 -48
  12. snowflake/cli/_plugins/{project/project_entity_model.py → dcm/dcm_project_entity_model.py} +5 -5
  13. snowflake/cli/_plugins/{project → dcm}/manager.py +35 -14
  14. snowflake/cli/_plugins/{project → dcm}/plugin_spec.py +1 -1
  15. snowflake/cli/_plugins/git/manager.py +1 -11
  16. snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +4 -0
  17. snowflake/cli/_plugins/nativeapp/commands.py +3 -4
  18. snowflake/cli/_plugins/nativeapp/entities/application_package.py +1 -1
  19. snowflake/cli/_plugins/nativeapp/release_channel/commands.py +1 -2
  20. snowflake/cli/_plugins/nativeapp/version/commands.py +1 -2
  21. snowflake/cli/_plugins/snowpark/common.py +23 -11
  22. snowflake/cli/_plugins/snowpark/snowpark_entity.py +13 -5
  23. snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +10 -2
  24. snowflake/cli/_plugins/sql/commands.py +49 -1
  25. snowflake/cli/_plugins/sql/manager.py +14 -4
  26. snowflake/cli/_plugins/sql/repl.py +4 -0
  27. snowflake/cli/_plugins/stage/commands.py +30 -11
  28. snowflake/cli/_plugins/stage/diff.py +2 -0
  29. snowflake/cli/_plugins/stage/manager.py +79 -55
  30. snowflake/cli/_plugins/streamlit/streamlit_entity.py +17 -30
  31. snowflake/cli/api/artifacts/upload.py +1 -1
  32. snowflake/cli/api/cli_global_context.py +5 -14
  33. snowflake/cli/api/commands/decorators.py +7 -0
  34. snowflake/cli/api/commands/flags.py +12 -0
  35. snowflake/cli/api/commands/snow_typer.py +23 -2
  36. snowflake/cli/api/config.py +9 -5
  37. snowflake/cli/api/connections.py +1 -0
  38. snowflake/cli/api/constants.py +2 -2
  39. snowflake/cli/api/entities/common.py +16 -13
  40. snowflake/cli/api/entities/utils.py +15 -9
  41. snowflake/cli/api/feature_flags.py +2 -5
  42. snowflake/cli/api/output/formats.py +6 -0
  43. snowflake/cli/api/output/types.py +48 -2
  44. snowflake/cli/api/project/schemas/entities/entities.py +6 -6
  45. snowflake/cli/api/rendering/sql_templates.py +67 -11
  46. snowflake/cli/api/rest_api.py +1 -0
  47. snowflake/cli/api/stage_path.py +41 -5
  48. {snowflake_cli-3.9.1.dist-info → snowflake_cli-3.10.1.dist-info}/METADATA +46 -13
  49. {snowflake_cli-3.9.1.dist-info → snowflake_cli-3.10.1.dist-info}/RECORD +53 -54
  50. snowflake/cli/_plugins/project/feature_flags.py +0 -22
  51. /snowflake/cli/_plugins/{project → dcm}/__init__.py +0 -0
  52. {snowflake_cli-3.9.1.dist-info → snowflake_cli-3.10.1.dist-info}/WHEEL +0 -0
  53. {snowflake_cli-3.9.1.dist-info → snowflake_cli-3.10.1.dist-info}/entry_points.txt +0 -0
  54. {snowflake_cli-3.9.1.dist-info → snowflake_cli-3.10.1.dist-info}/licenses/LICENSE +0 -0
@@ -16,7 +16,7 @@ from __future__ import annotations
16
16
 
17
17
  from enum import Enum, unique
18
18
 
19
- VERSION = "3.9.1"
19
+ VERSION = "3.10.1"
20
20
 
21
21
 
22
22
  @unique
@@ -16,6 +16,7 @@ from snowflake.cli._plugins.auth.keypair import plugin_spec as auth_plugin_spec
16
16
  from snowflake.cli._plugins.connection import plugin_spec as connection_plugin_spec
17
17
  from snowflake.cli._plugins.cortex import plugin_spec as cortex_plugin_spec
18
18
  from snowflake.cli._plugins.dbt import plugin_spec as dbt_plugin_spec
19
+ from snowflake.cli._plugins.dcm import plugin_spec as dcm_project_plugin_spec
19
20
  from snowflake.cli._plugins.git import plugin_spec as git_plugin_spec
20
21
  from snowflake.cli._plugins.helpers import plugin_spec as migrate_plugin_spec
21
22
  from snowflake.cli._plugins.init import plugin_spec as init_plugin_spec
@@ -24,7 +25,6 @@ from snowflake.cli._plugins.nativeapp import plugin_spec as nativeapp_plugin_spe
24
25
  from snowflake.cli._plugins.notebook import plugin_spec as notebook_plugin_spec
25
26
  from snowflake.cli._plugins.object import plugin_spec as object_plugin_spec
26
27
  from snowflake.cli._plugins.plugin import plugin_spec as plugin_plugin_spec
27
- from snowflake.cli._plugins.project import plugin_spec as project_plugin_spec
28
28
  from snowflake.cli._plugins.snowpark import plugin_spec as snowpark_plugin_spec
29
29
  from snowflake.cli._plugins.spcs import plugin_spec as spcs_plugin_spec
30
30
  from snowflake.cli._plugins.sql import plugin_spec as sql_plugin_spec
@@ -42,7 +42,7 @@ def get_builtin_plugin_name_to_plugin_spec():
42
42
  "spcs": spcs_plugin_spec,
43
43
  "app": nativeapp_plugin_spec,
44
44
  "object": object_plugin_spec,
45
- "project": project_plugin_spec,
45
+ "dcm": dcm_project_plugin_spec,
46
46
  "snowpark": snowpark_plugin_spec,
47
47
  "stage": stage_plugin_spec,
48
48
  "sql": sql_plugin_spec,
@@ -14,9 +14,11 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ import csv
17
18
  import json
18
19
  import sys
19
- from datetime import date, datetime
20
+ from datetime import date, datetime, time
21
+ from decimal import Decimal
20
22
  from json import JSONEncoder
21
23
  from pathlib import Path
22
24
  from textwrap import indent
@@ -57,10 +59,12 @@ class CustomJSONEncoder(JSONEncoder):
57
59
  return o.result
58
60
  if isinstance(o, (CollectionResult, MultipleResults)):
59
61
  return list(o.result)
60
- if isinstance(o, (date, datetime)):
62
+ if isinstance(o, (date, datetime, time)):
61
63
  return o.isoformat()
62
- if isinstance(o, Path):
64
+ if isinstance(o, (Path, Decimal)):
63
65
  return str(o)
66
+ if isinstance(o, bytearray):
67
+ return o.hex()
64
68
  return super().default(o)
65
69
 
66
70
 
@@ -86,36 +90,72 @@ def _print_multiple_table_results(obj: CollectionResult):
86
90
  for column in first_item.keys():
87
91
  table.add_column(column, overflow="fold")
88
92
  with Live(table, refresh_per_second=4):
89
- table.add_row(*[str(i) for i in first_item.values()])
93
+ table.add_row(*[__to_str(i) for i in first_item.values()])
90
94
  for item in items:
91
- table.add_row(*[str(i) for i in item.values()])
95
+ table.add_row(*[__to_str(i) for i in item.values()])
92
96
  # Add separator between tables
93
97
  rich_print(flush=True)
94
98
 
95
99
 
100
+ def __to_str(val):
101
+ if isinstance(val, bytearray):
102
+ return val.hex()
103
+ return str(val)
104
+
105
+
96
106
  def is_structured_format(output_format):
97
- return output_format == OutputFormat.JSON
107
+ return output_format.is_json or output_format == OutputFormat.CSV
98
108
 
99
109
 
100
- def print_structured(result: CommandResult):
110
+ def print_structured(
111
+ result: CommandResult, output_format: OutputFormat = OutputFormat.JSON
112
+ ):
101
113
  """Handles outputs like json, yml and other structured and parsable formats."""
102
114
  printed_end_line = False
103
115
  if isinstance(result, MultipleResults):
104
- _stream_json(result)
116
+ if output_format == OutputFormat.CSV:
117
+ for command_result in result.result:
118
+ _print_csv_result(command_result)
119
+ print(flush=True)
120
+ printed_end_line = True
121
+ else:
122
+ _stream_json(result)
105
123
  elif isinstance(result, StreamResult):
106
124
  # A StreamResult prints each value onto its own line
107
- # instead of joining all the values into a JSON array
125
+ # instead of joining all the values into a JSON array or CSV entry set
108
126
  for r in result.result:
109
- json.dump(r, sys.stdout, cls=CustomJSONEncoder)
127
+ if output_format == OutputFormat.CSV:
128
+ _print_csv_result(r.result)
129
+ else:
130
+ json.dump(r, sys.stdout, cls=CustomJSONEncoder)
110
131
  print(flush=True)
111
132
  printed_end_line = True
112
133
  else:
113
- json.dump(result, sys.stdout, cls=CustomJSONEncoder, indent=4)
134
+ if output_format == OutputFormat.CSV:
135
+ _print_csv_result(result)
136
+ printed_end_line = True
137
+ else:
138
+ json.dump(result, sys.stdout, cls=CustomJSONEncoder, indent=4)
114
139
  # Adds empty line at the end
115
140
  if not printed_end_line:
116
141
  print(flush=True)
117
142
 
118
143
 
144
+ def _print_csv_result(result: CommandResult):
145
+ data = json.loads(json.dumps(result, cls=CustomJSONEncoder))
146
+ if isinstance(data, dict):
147
+ writer = csv.DictWriter(sys.stdout, [*data], lineterminator="\n")
148
+ writer.writeheader()
149
+ writer.writerow(data)
150
+ elif isinstance(data, list):
151
+ if not data:
152
+ return
153
+ writer = csv.DictWriter(sys.stdout, [*data[0]], lineterminator="\n")
154
+ writer.writeheader()
155
+ for entry in data:
156
+ writer.writerow(entry)
157
+
158
+
119
159
  def _stream_json(result):
120
160
  """Simple helper for streaming multiple results as a JSON."""
121
161
  indent_size = 2
@@ -160,7 +200,7 @@ def _print_single_table(obj):
160
200
  table.add_column("value", overflow="fold")
161
201
  for key, value in obj.result.items():
162
202
  table.add_row(
163
- sanitize_for_terminal(str(key)), sanitize_for_terminal(str(value))
203
+ sanitize_for_terminal(str(key)), sanitize_for_terminal(__to_str(value))
164
204
  )
165
205
  rich_print(table, flush=True)
166
206
 
@@ -168,7 +208,7 @@ def _print_single_table(obj):
168
208
  def print_result(cmd_result: CommandResult, output_format: OutputFormat | None = None):
169
209
  output_format = output_format or _get_format_type()
170
210
  if is_structured_format(output_format):
171
- print_structured(cmd_result)
211
+ print_structured(cmd_result, output_format)
172
212
  elif isinstance(cmd_result, (MultipleResults, StreamResult)):
173
213
  for res in cmd_result.result:
174
214
  print_result(res)
@@ -63,6 +63,7 @@ SUPPORTED_ENV_OVERRIDES = [
63
63
  "warehouse",
64
64
  "session_token",
65
65
  "master_token",
66
+ "token",
66
67
  "token_file_path",
67
68
  "oauth_client_id",
68
69
  "oauth_client_secret",
@@ -178,6 +178,8 @@ def _get_definition_version() -> str | None:
178
178
 
179
179
 
180
180
  def _get_ci_environment_type() -> str:
181
+ if "SF_GITHUB_ACTION" in os.environ:
182
+ return "SF_GITHUB_ACTION"
181
183
  if "GITHUB_ACTIONS" in os.environ:
182
184
  return "GITHUB_ACTIONS"
183
185
  if "GITLAB_CI" in os.environ:
@@ -6,18 +6,54 @@ import requests
6
6
  from packaging.version import Version
7
7
  from snowflake.cli.__about__ import VERSION
8
8
  from snowflake.cli.api.cli_global_context import get_cli_context
9
+ from snowflake.cli.api.config import (
10
+ CLI_SECTION,
11
+ IGNORE_NEW_VERSION_WARNING_KEY,
12
+ get_config_bool_value,
13
+ )
9
14
  from snowflake.cli.api.secure_path import SecurePath
10
15
  from snowflake.connector.config_manager import CONFIG_MANAGER
11
16
 
12
17
  REPOSITORY_URL_PIP = "https://pypi.org/pypi/snowflake-cli/json"
13
18
  REPOSITORY_URL_BREW = "https://formulae.brew.sh/api/formula/snowflake-cli.json"
14
19
 
20
+ # How often to refresh the version cache (seconds)
21
+ VERSION_CACHE_REFRESH_INTERVAL = 60 * 60 # 1 hour
22
+ # How often to show the new version message (seconds)
23
+ NEW_VERSION_MESSAGE_INTERVAL = 60 * 60 * 24 * 7 # 1 week
24
+
25
+
26
+ def should_ignore_new_version_warning() -> bool:
27
+ return get_config_bool_value(
28
+ CLI_SECTION, key=IGNORE_NEW_VERSION_WARNING_KEY, default=False
29
+ )
30
+
31
+
32
+ def was_warning_shown_recently(last_time_shown: float | int | None) -> bool:
33
+ """
34
+ Returns True if the new version warning was shown recently (within the interval),
35
+ meaning we should NOT show the warning again yet.
36
+ """
37
+ if not last_time_shown:
38
+ return False
39
+ now = time.time()
40
+ return last_time_shown >= now - NEW_VERSION_MESSAGE_INTERVAL
41
+
15
42
 
16
43
  def get_new_version_msg() -> str | None:
17
- last = _VersionCache().get_last_version()
18
- current = Version(VERSION)
19
- if last and last > current:
20
- return f"\nNew version of Snowflake CLI available. Newest: {last}, current: {VERSION}\n"
44
+ if should_ignore_new_version_warning():
45
+ return None
46
+ cache = _VersionCache()
47
+ last_version = cache.get_last_version()
48
+ last_time_shown = cache.get_last_time_shown()
49
+ current_version = Version(VERSION)
50
+ if (
51
+ last_version
52
+ and last_version > current_version
53
+ and not was_warning_shown_recently(last_time_shown)
54
+ ):
55
+ cache.update_last_time_shown()
56
+ return f"\nNew version of Snowflake CLI available. Newest: {last_version}, current: {VERSION}\n"
21
57
  return None
22
58
 
23
59
 
@@ -32,6 +68,7 @@ def show_new_version_banner_callback(msg):
32
68
  class _VersionCache:
33
69
  _last_time = "last_time_check"
34
70
  _version = "version"
71
+ _last_time_shown = "last_time_shown"
35
72
  _version_cache_file = SecurePath(
36
73
  CONFIG_MANAGER.file_path.parent / ".cli_version.cache"
37
74
  )
@@ -44,6 +81,28 @@ class _VersionCache:
44
81
  _VersionCache._last_time: time.time(),
45
82
  _VersionCache._version: str(version),
46
83
  }
84
+ if self._cache_file.exists():
85
+ try:
86
+ old_data = json.loads(self._cache_file.read_text(file_size_limit_mb=1))
87
+ if _VersionCache._last_time_shown in old_data:
88
+ data[_VersionCache._last_time_shown] = old_data[
89
+ _VersionCache._last_time_shown
90
+ ]
91
+ except Exception:
92
+ pass
93
+ self._cache_file.parent.mkdir(parents=True, exist_ok=True)
94
+ self._cache_file.write_text(json.dumps(data))
95
+
96
+ def update_last_time_shown(self):
97
+ if self._cache_file.exists():
98
+ try:
99
+ data = json.loads(self._cache_file.read_text(file_size_limit_mb=1))
100
+ except Exception:
101
+ data = {}
102
+ else:
103
+ data = {}
104
+ data[_VersionCache._last_time_shown] = time.time()
105
+ self._cache_file.parent.mkdir(parents=True, exist_ok=True)
47
106
  self._cache_file.write_text(json.dumps(data))
48
107
 
49
108
  @staticmethod
@@ -73,9 +132,8 @@ class _VersionCache:
73
132
  if self._cache_file.exists():
74
133
  data = json.loads(self._cache_file.read_text(file_size_limit_mb=1))
75
134
  now = time.time()
76
- if data[_VersionCache._last_time] > now - 60 * 60:
135
+ if data[_VersionCache._last_time] > now - VERSION_CACHE_REFRESH_INTERVAL:
77
136
  return Version(data[_VersionCache._version])
78
-
79
137
  return self._update_latest_version()
80
138
 
81
139
  def get_last_version(self) -> Version | None:
@@ -83,3 +141,12 @@ class _VersionCache:
83
141
  return self._read_latest_version()
84
142
  except: # anything, this it not crucial feature
85
143
  return None
144
+
145
+ def get_last_time_shown(self) -> float | int | None:
146
+ if self._cache_file.exists():
147
+ try:
148
+ data = json.loads(self._cache_file.read_text(file_size_limit_mb=1))
149
+ return data.get(_VersionCache._last_time_shown, 0)
150
+ except Exception:
151
+ return None
152
+ return None
@@ -41,6 +41,7 @@ from snowflake.cli.api.commands.overrideable_parameter import (
41
41
  )
42
42
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
43
43
  from snowflake.cli.api.constants import DEFAULT_SIZE_LIMIT_MB, PYTHON_3_12
44
+ from snowflake.cli.api.exceptions import CliError
44
45
  from snowflake.cli.api.output.types import (
45
46
  CollectionResult,
46
47
  CommandResult,
@@ -94,6 +95,7 @@ def search(
94
95
  """
95
96
  Performs query search using Cortex Search Services.
96
97
  """
98
+ from snowflake.core import Root
97
99
 
98
100
  if not SEARCH_COMMAND_ENABLED:
99
101
  raise click.ClickException(
@@ -104,7 +106,12 @@ def search(
104
106
  columns = []
105
107
 
106
108
  conn = get_cli_context().connection
107
- root = get_cli_context().snow_api_root
109
+ if conn:
110
+ root = Root(conn)
111
+ else:
112
+ raise CliError(
113
+ "Cortex Search requires a connection to be established. Please connect to a Snowflake account first."
114
+ )
108
115
 
109
116
  search_service = (
110
117
  root.databases[conn.database]
@@ -173,11 +180,9 @@ def complete(
173
180
  is_file_input=is_file_input,
174
181
  )
175
182
  elif backend == Backend.REST:
176
- root = get_cli_context().snow_api_root
177
183
  result_text = manager.rest_complete(
178
184
  text=Text(prompt),
179
185
  model=Model(model),
180
- root=root,
181
186
  )
182
187
  else:
183
188
  raise UsageError("--backend option should be either rest or sql.")
@@ -26,18 +26,13 @@ from snowflake.cli._plugins.cortex.types import (
26
26
  SourceDocument,
27
27
  Text,
28
28
  )
29
+ from snowflake.cli.api.cli_global_context import get_cli_context
29
30
  from snowflake.cli.api.constants import DEFAULT_SIZE_LIMIT_MB
30
- from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
31
+ from snowflake.cli.api.exceptions import CliError, SnowflakeSQLExecutionError
31
32
  from snowflake.cli.api.secure_path import SecurePath
32
33
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
33
34
  from snowflake.connector import ProgrammingError
34
35
  from snowflake.connector.cursor import DictCursor
35
- from snowflake.core._root import Root
36
- from snowflake.core.cortex.inference_service import CortexInferenceService
37
- from snowflake.core.cortex.inference_service._generated.models import CompleteRequest
38
- from snowflake.core.cortex.inference_service._generated.models.complete_request_messages_inner import (
39
- CompleteRequestMessagesInner,
40
- )
41
36
 
42
37
  log = logging.getLogger(__name__)
43
38
 
@@ -89,24 +84,33 @@ class CortexManager(SqlExecutionMixin):
89
84
  lambda: json_result["choices"][0]["messages"]
90
85
  )
91
86
 
92
- def make_rest_complete_request(
93
- self,
94
- model: Model,
95
- prompt: Text,
96
- ) -> CompleteRequest:
97
- return CompleteRequest(
98
- model=str(model),
99
- messages=[CompleteRequestMessagesInner(content=str(prompt))],
100
- stream=True,
101
- )
102
-
103
87
  def rest_complete(
104
88
  self,
105
89
  text: Text,
106
90
  model: Model,
107
- root: "Root",
108
91
  ) -> str:
109
- complete_request = self.make_rest_complete_request(model=model, prompt=text)
92
+ from snowflake.core import Root
93
+ from snowflake.core.cortex.inference_service import CortexInferenceService
94
+ from snowflake.core.cortex.inference_service._generated.models import (
95
+ CompleteRequest,
96
+ )
97
+ from snowflake.core.cortex.inference_service._generated.models.complete_request_messages_inner import (
98
+ CompleteRequestMessagesInner,
99
+ )
100
+
101
+ cli_context = get_cli_context()
102
+ if cli_context.connection:
103
+ root = Root(cli_context.connection)
104
+ else:
105
+ raise CliError(
106
+ "Cortex Search requires a connection to be established. Please connect to a Snowflake account first."
107
+ )
108
+
109
+ complete_request = CompleteRequest(
110
+ model=str(model),
111
+ messages=[CompleteRequestMessagesInner(content=str(text))],
112
+ stream=True,
113
+ )
110
114
  cortex_inference_service = CortexInferenceService(root=root)
111
115
  try:
112
116
  raw_resp = cortex_inference_service.complete(
@@ -45,8 +45,9 @@ from snowflake.cli.api.secure_path import SecurePath
45
45
 
46
46
  app = SnowTyperFactory(
47
47
  name="dbt",
48
- help="Manages dbt on Snowflake projects",
48
+ help="Manages dbt on Snowflake projects.",
49
49
  is_hidden=FeatureFlag.ENABLE_DBT.is_disabled,
50
+ preview=True,
50
51
  )
51
52
  log = logging.getLogger(__name__)
52
53
 
@@ -64,7 +65,7 @@ add_object_command_aliases(
64
65
  object_type=ObjectType.DBT_PROJECT,
65
66
  name_argument=DBTNameArgument,
66
67
  like_option=like_option(
67
- help_example='`list --like "my%"` lists all dbt projects that begin with my'
68
+ help_example='`list --like "my%"` lists all dbt projects that begin with "my"'
68
69
  ),
69
70
  scope_option=scope_option(help_example="`list --in database my_db`"),
70
71
  ommit_commands=["drop", "create", "describe"],
@@ -115,6 +116,7 @@ dbt_execute_app = SnowTyperFactory(
115
116
  help="Execute a dbt command on Snowflake. Subcommand name and all "
116
117
  "parameters following it will be passed over to dbt.",
117
118
  subcommand_metavar="DBT_COMMAND",
119
+ preview=True,
118
120
  )
119
121
  app.add_typer(dbt_execute_app)
120
122
 
@@ -141,6 +143,7 @@ for cmd in DBT_COMMANDS:
141
143
  context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
142
144
  help=f"Execute {cmd} command on Snowflake. Command name and all parameters following it will be passed over to dbt.",
143
145
  add_help_option=False,
146
+ preview=True,
144
147
  )
145
148
  def _dbt_execute(
146
149
  ctx: typer.Context,
@@ -26,6 +26,7 @@ from snowflake.cli.api.console import cli_console
26
26
  from snowflake.cli.api.constants import DEFAULT_SIZE_LIMIT_MB, ObjectType
27
27
  from snowflake.cli.api.exceptions import CliError
28
28
  from snowflake.cli.api.identifiers import FQN
29
+ from snowflake.cli.api.project.util import unquote_identifier
29
30
  from snowflake.cli.api.secure_path import SecurePath
30
31
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
31
32
  from snowflake.connector.cursor import SnowflakeCursor
@@ -44,7 +45,7 @@ class DBTManager(SqlExecutionMixin):
44
45
 
45
46
  def deploy(
46
47
  self,
47
- name: FQN,
48
+ fqn: FQN,
48
49
  path: SecurePath,
49
50
  profiles_path: SecurePath,
50
51
  force: bool,
@@ -66,7 +67,8 @@ class DBTManager(SqlExecutionMixin):
66
67
 
67
68
  with cli_console.phase("Creating temporary stage"):
68
69
  stage_manager = StageManager()
69
- stage_fqn = FQN.from_string(f"dbt_{name}_stage").using_context()
70
+ unquoted_name = unquote_identifier(fqn.name)
71
+ stage_fqn = FQN.from_string(f"DBT_{unquoted_name}_STAGE").using_context()
70
72
  stage_name = stage_manager.get_standard_stage_prefix(stage_fqn)
71
73
  stage_manager.create(stage_fqn, temporary=True)
72
74
 
@@ -86,11 +88,11 @@ class DBTManager(SqlExecutionMixin):
86
88
 
87
89
  with cli_console.phase("Creating DBT project"):
88
90
  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"
91
+ query = f"CREATE OR REPLACE DBT PROJECT {fqn}"
92
+ elif self.exists(name=fqn):
93
+ query = f"ALTER DBT PROJECT {fqn} ADD VERSION"
92
94
  else:
93
- query = f"CREATE DBT PROJECT {name}"
95
+ query = f"CREATE DBT PROJECT {fqn}"
94
96
  query += f"\nFROM {stage_name}"
95
97
  return self.execute_query(query)
96
98
 
@@ -174,7 +176,7 @@ class DBTManager(SqlExecutionMixin):
174
176
  yaml.safe_dump(yaml.safe_load(sfd), tfd)
175
177
 
176
178
  def execute(
177
- self, dbt_command: str, name: str, run_async: bool, *dbt_cli_args
179
+ self, dbt_command: str, name: FQN, run_async: bool, *dbt_cli_args
178
180
  ) -> SnowflakeCursor:
179
181
  if dbt_cli_args:
180
182
  dbt_command = " ".join([dbt_command, *dbt_cli_args]).strip()