snowflake-cli 3.0.2__py3-none-any.whl → 3.1.0__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 (57) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/cli_app.py +3 -0
  3. snowflake/cli/_app/dev/docs/templates/overview.rst.jinja2 +1 -1
  4. snowflake/cli/_app/dev/docs/templates/usage.rst.jinja2 +2 -2
  5. snowflake/cli/_app/telemetry.py +69 -4
  6. snowflake/cli/_plugins/connection/commands.py +40 -2
  7. snowflake/cli/_plugins/git/commands.py +6 -3
  8. snowflake/cli/_plugins/git/manager.py +5 -0
  9. snowflake/cli/_plugins/nativeapp/artifacts.py +13 -3
  10. snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
  11. snowflake/cli/_plugins/nativeapp/codegen/compiler.py +7 -0
  12. snowflake/cli/_plugins/nativeapp/codegen/sandbox.py +10 -10
  13. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +2 -2
  14. snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +1 -1
  15. snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +8 -8
  16. snowflake/cli/_plugins/nativeapp/commands.py +135 -186
  17. snowflake/cli/_plugins/nativeapp/entities/application.py +176 -24
  18. snowflake/cli/_plugins/nativeapp/entities/application_package.py +112 -136
  19. snowflake/cli/_plugins/nativeapp/exceptions.py +12 -0
  20. snowflake/cli/_plugins/nativeapp/manager.py +3 -26
  21. snowflake/cli/_plugins/nativeapp/v2_conversions/{v2_to_v1_decorator.py → compat.py} +131 -72
  22. snowflake/cli/_plugins/nativeapp/version/commands.py +30 -29
  23. snowflake/cli/_plugins/nativeapp/version/version_processor.py +1 -43
  24. snowflake/cli/_plugins/snowpark/common.py +60 -18
  25. snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +2 -2
  26. snowflake/cli/_plugins/spcs/image_repository/commands.py +4 -37
  27. snowflake/cli/_plugins/spcs/image_repository/manager.py +4 -1
  28. snowflake/cli/_plugins/spcs/services/commands.py +36 -4
  29. snowflake/cli/_plugins/spcs/services/manager.py +36 -4
  30. snowflake/cli/_plugins/stage/commands.py +8 -3
  31. snowflake/cli/_plugins/stage/diff.py +16 -16
  32. snowflake/cli/_plugins/stage/manager.py +164 -73
  33. snowflake/cli/_plugins/stage/md5.py +1 -1
  34. snowflake/cli/_plugins/workspace/commands.py +21 -1
  35. snowflake/cli/_plugins/workspace/context.py +38 -0
  36. snowflake/cli/_plugins/workspace/manager.py +23 -13
  37. snowflake/cli/api/cli_global_context.py +3 -3
  38. snowflake/cli/api/commands/flags.py +23 -7
  39. snowflake/cli/api/config.py +7 -4
  40. snowflake/cli/api/connections.py +12 -1
  41. snowflake/cli/api/entities/common.py +4 -2
  42. snowflake/cli/api/entities/utils.py +17 -37
  43. snowflake/cli/api/exceptions.py +32 -0
  44. snowflake/cli/api/identifiers.py +8 -0
  45. snowflake/cli/api/project/definition_conversion.py +139 -40
  46. snowflake/cli/api/project/schemas/entities/common.py +11 -0
  47. snowflake/cli/api/project/schemas/project_definition.py +30 -25
  48. snowflake/cli/api/sql_execution.py +5 -7
  49. snowflake/cli/api/stage_path.py +241 -0
  50. snowflake/cli/api/utils/definition_rendering.py +3 -5
  51. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/METADATA +11 -11
  52. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/RECORD +55 -55
  53. snowflake/cli/_plugins/nativeapp/teardown_processor.py +0 -70
  54. snowflake/cli/_plugins/workspace/action_context.py +0 -18
  55. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/WHEEL +0 -0
  56. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/entry_points.txt +0 -0
  57. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -29,14 +29,40 @@ from snowflake.cli.api.cli_global_context import (
29
29
  get_cli_context_manager,
30
30
  )
31
31
  from snowflake.cli.api.commands.decorators import _options_decorator_factory
32
+ from snowflake.cli.api.project.definition_conversion import (
33
+ convert_project_definition_to_v2,
34
+ )
32
35
  from snowflake.cli.api.project.schemas.entities.common import EntityModelBase
33
36
  from snowflake.cli.api.project.schemas.project_definition import (
34
37
  DefinitionV11,
35
38
  DefinitionV20,
39
+ ProjectDefinition,
40
+ ProjectDefinitionV1,
36
41
  )
37
42
  from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import PathMapping
38
43
  from snowflake.cli.api.utils.definition_rendering import render_definition_template
39
44
 
45
+ APP_AND_PACKAGE_OPTIONS = [
46
+ inspect.Parameter(
47
+ "package_entity_id",
48
+ inspect.Parameter.KEYWORD_ONLY,
49
+ annotation=Optional[str],
50
+ default=typer.Option(
51
+ default="",
52
+ help="The ID of the package entity on which to operate when definition_version is 2 or higher.",
53
+ ),
54
+ ),
55
+ inspect.Parameter(
56
+ "app_entity_id",
57
+ inspect.Parameter.KEYWORD_ONLY,
58
+ annotation=Optional[str],
59
+ default=typer.Option(
60
+ default="",
61
+ help="The ID of the application entity on which to operate when definition_version is 2 or higher.",
62
+ ),
63
+ ),
64
+ ]
65
+
40
66
 
41
67
  def _convert_v2_artifact_to_v1_dict(
42
68
  v2_artifact: Union[PathMapping, str]
@@ -58,45 +84,12 @@ def _pdf_v2_to_v1(
58
84
  ) -> DefinitionV11:
59
85
  pdfv1: Dict[str, Any] = {"definition_version": "1.1", "native_app": {}}
60
86
 
61
- # Determine the application entity to convert, there can be zero or one
62
- app_definition = find_entity(
63
- v2_definition,
64
- ApplicationEntityModel,
65
- app_entity_id,
66
- disambiguation_option="--app-entity-id",
67
- required=app_required,
68
- )
69
-
70
- # Infer or verify the package if we have an app entity to convert
71
- if app_definition:
72
- target_package = app_definition.from_.target
73
- if package_entity_id:
74
- # If the user specified a package entity ID,
75
- # check that the app entity targets the user-specified package entity
76
- # if the app entity is used by the command being run
77
- if target_package != package_entity_id and app_required:
78
- raise ClickException(
79
- f"The application entity {app_definition.entity_id} does not "
80
- f"target the application package entity {package_entity_id}. Either"
81
- f"use --package-entity-id {target_package} to target the correct package entity, "
82
- f"or omit the --package-entity-id flag to automatically use the package entity "
83
- f"that the application entity targets."
84
- )
85
- elif target_package in v2_definition.get_entities_by_type(
86
- ApplicationPackageEntityModel.get_type()
87
- ):
88
- # If the user didn't target a specific package entity, use the one the app entity targets
89
- package_entity_id = target_package
90
-
91
- # Determine the package entity to convert, there must be one
92
- app_package_definition = find_entity(
93
- v2_definition,
94
- ApplicationPackageEntityModel,
95
- package_entity_id,
96
- disambiguation_option="--package-entity-id",
97
- required=True,
87
+ app_definition, app_package_definition = _find_app_and_package_entities(
88
+ v2_definition=v2_definition,
89
+ package_entity_id=package_entity_id,
90
+ app_entity_id=app_entity_id,
91
+ app_required=app_required,
98
92
  )
99
- assert app_package_definition is not None # satisfy mypy
100
93
 
101
94
  # NativeApp
102
95
  if app_definition and app_definition.fqn.identifier:
@@ -156,6 +149,52 @@ def _pdf_v2_to_v1(
156
149
  return result.project_definition
157
150
 
158
151
 
152
+ def _find_app_and_package_entities(
153
+ v2_definition: DefinitionV20,
154
+ package_entity_id: str,
155
+ app_entity_id: str,
156
+ app_required: bool,
157
+ ):
158
+ # Determine the application entity to convert, there can be zero or one
159
+ app_definition = find_entity(
160
+ v2_definition,
161
+ ApplicationEntityModel,
162
+ app_entity_id,
163
+ disambiguation_option="--app-entity-id",
164
+ required=app_required,
165
+ )
166
+ # Infer or verify the package if we have an app entity to convert
167
+ if app_definition:
168
+ target_package = app_definition.from_.target
169
+ if package_entity_id:
170
+ # If the user specified a package entity ID,
171
+ # check that the app entity targets the user-specified package entity
172
+ # if the app entity is used by the command being run
173
+ if target_package != package_entity_id and app_required:
174
+ raise ClickException(
175
+ f"The application entity {app_definition.entity_id} does not "
176
+ f"target the application package entity {package_entity_id}. Either"
177
+ f"use --package-entity-id {target_package} to target the correct package entity, "
178
+ f"or omit the --package-entity-id flag to automatically use the package entity "
179
+ f"that the application entity targets."
180
+ )
181
+ elif target_package in v2_definition.get_entities_by_type(
182
+ ApplicationPackageEntityModel.get_type()
183
+ ):
184
+ # If the user didn't target a specific package entity, use the one the app entity targets
185
+ package_entity_id = target_package
186
+ # Determine the package entity to convert, there must be one
187
+ app_package_definition = find_entity(
188
+ v2_definition,
189
+ ApplicationPackageEntityModel,
190
+ package_entity_id,
191
+ disambiguation_option="--package-entity-id",
192
+ required=True,
193
+ )
194
+ assert app_package_definition is not None # satisfy mypy
195
+ return app_definition, app_package_definition
196
+
197
+
159
198
  T = TypeVar("T", bound=EntityModelBase)
160
199
 
161
200
 
@@ -209,54 +248,74 @@ def find_entity(
209
248
  return entity
210
249
 
211
250
 
212
- def nativeapp_definition_v2_to_v1(*, app_required: bool = False):
251
+ def force_project_definition_v2(
252
+ *, single_app_and_package: bool = True, app_required: bool = False
253
+ ):
213
254
  """
214
- A command decorator that attempts to automatically convert a native app project from
215
- definition v2 to v1.1. Assumes with_project_definition() has already been called.
216
- The definition object in CliGlobalContext will be replaced with the converted object.
217
- Exactly one application package entity type is expected, and up to one application
218
- entity type is expected.
255
+ A command decorator that forces the project definition to be converted to v2.
256
+
257
+ If a v1 definition is found, it is converted to v2 in-memory and the global context
258
+ is updated with the new definition object.
259
+
260
+ If a v2 definition is already found, it is used as-is, optionally limiting the number
261
+ of application and package entities to one each (true by default).
262
+
263
+ Assumes with_project_definition() has already been called.
219
264
  """
220
265
 
221
266
  def decorator(func):
222
267
  @wraps(func)
223
268
  def wrapper(*args, **kwargs):
224
- original_pdf: Optional[DefinitionV20] = get_cli_context().project_definition
269
+ cli_context = get_cli_context()
270
+ original_pdf: Optional[ProjectDefinition] = cli_context.project_definition
225
271
  if not original_pdf:
226
272
  raise ValueError(
227
- "Project definition could not be found. The nativeapp_definition_v2_to_v1 command decorator assumes with_project_definition() was called before it."
273
+ "Project definition could not be found. "
274
+ "The single_app_and_package() command decorator assumes "
275
+ "that with_project_definition() was called before it."
276
+ )
277
+ if isinstance(original_pdf, ProjectDefinitionV1):
278
+ pdfv2 = convert_project_definition_to_v2(
279
+ cli_context.project_root,
280
+ original_pdf,
281
+ accept_templates=False, # Templates should all be rendered by now
282
+ template_context=None, # Force inclusion of all fields
283
+ in_memory=True, # Convert the definition knowing it will be used immediately
228
284
  )
229
- if original_pdf.definition_version == "2":
285
+ for entity_id, entity in pdfv2.entities.items():
286
+ # Backfill kwargs for the command to use,
287
+ # there can only be one entity of each type
288
+ is_package = isinstance(entity, ApplicationPackageEntityModel)
289
+ key = "package_entity_id" if is_package else "app_entity_id"
290
+ kwargs[key] = entity_id
291
+
292
+ cm = get_cli_context_manager()
293
+
294
+ # Override the project definition so that the command operates on the new entities
295
+ cm.override_project_definition = pdfv2
296
+ elif single_app_and_package:
230
297
  package_entity_id = kwargs.get("package_entity_id", "")
231
298
  app_entity_id = kwargs.get("app_entity_id", "")
232
- pdfv1 = _pdf_v2_to_v1(
299
+ app_definition, app_package_definition = _find_app_and_package_entities(
233
300
  original_pdf, package_entity_id, app_entity_id, app_required
234
301
  )
235
- get_cli_context_manager().override_project_definition = pdfv1
302
+ entities_to_keep = {app_package_definition.entity_id}
303
+ kwargs["package_entity_id"] = app_package_definition.entity_id
304
+ if app_definition:
305
+ entities_to_keep.add(app_definition.entity_id)
306
+ kwargs["app_entity_id"] = app_definition.entity_id
307
+ for entity_id in list(original_pdf.entities):
308
+ if entity_id not in entities_to_keep:
309
+ # This happens after templates are rendered,
310
+ # so we can safely remove the entity
311
+ del original_pdf.entities[entity_id]
236
312
  return func(*args, **kwargs)
237
313
 
238
- return _options_decorator_factory(
239
- wrapper,
240
- additional_options=[
241
- inspect.Parameter(
242
- "package_entity_id",
243
- inspect.Parameter.KEYWORD_ONLY,
244
- annotation=Optional[str],
245
- default=typer.Option(
246
- default="",
247
- help="The ID of the package entity on which to operate when definition_version is 2 or higher.",
248
- ),
249
- ),
250
- inspect.Parameter(
251
- "app_entity_id",
252
- inspect.Parameter.KEYWORD_ONLY,
253
- annotation=Optional[str],
254
- default=typer.Option(
255
- default="",
256
- help="The ID of the application entity on which to operate when definition_version is 2 or higher.",
257
- ),
258
- ),
259
- ],
260
- )
314
+ if single_app_and_package:
315
+ # Add the --app-entity-id and --package-entity-id options to the command
316
+ return _options_decorator_factory(
317
+ wrapper, additional_options=APP_AND_PACKAGE_OPTIONS
318
+ )
319
+ return wrapper
261
320
 
262
321
  return decorator
@@ -20,21 +20,17 @@ from typing import Optional
20
20
  import typer
21
21
  from click import MissingParameter
22
22
  from snowflake.cli._plugins.nativeapp.common_flags import ForceOption, InteractiveOption
23
- from snowflake.cli._plugins.nativeapp.run_processor import NativeAppRunProcessor
24
- from snowflake.cli._plugins.nativeapp.v2_conversions.v2_to_v1_decorator import (
25
- nativeapp_definition_v2_to_v1,
26
- )
27
- from snowflake.cli._plugins.nativeapp.version.version_processor import (
28
- NativeAppVersionCreateProcessor,
29
- NativeAppVersionDropProcessor,
23
+ from snowflake.cli._plugins.nativeapp.v2_conversions.compat import (
24
+ force_project_definition_v2,
30
25
  )
26
+ from snowflake.cli._plugins.workspace.manager import WorkspaceManager
31
27
  from snowflake.cli.api.cli_global_context import get_cli_context
32
28
  from snowflake.cli.api.commands.decorators import (
33
29
  with_project_definition,
34
30
  )
35
31
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
32
+ from snowflake.cli.api.entities.common import EntityActions
36
33
  from snowflake.cli.api.output.types import CommandResult, MessageResult, QueryResult
37
- from snowflake.cli.api.project.project_verification import assert_project_type
38
34
 
39
35
  app = SnowTyperFactory(
40
36
  name="version",
@@ -46,7 +42,7 @@ log = logging.getLogger(__name__)
46
42
 
47
43
  @app.command(requires_connection=True)
48
44
  @with_project_definition()
49
- @nativeapp_definition_v2_to_v1()
45
+ @force_project_definition_v2()
50
46
  def create(
51
47
  version: Optional[str] = typer.Argument(
52
48
  None,
@@ -71,18 +67,18 @@ def create(
71
67
  """
72
68
  Adds a new patch to the provided version defined in your application package. If the version does not exist, creates a version with patch 0.
73
69
  """
74
-
75
- assert_project_type("native_app")
76
-
77
70
  if version is None and patch is not None:
78
71
  raise MissingParameter("Cannot provide a patch without version!")
79
72
 
80
73
  cli_context = get_cli_context()
81
- processor = NativeAppVersionCreateProcessor(
82
- project_definition=cli_context.project_definition.native_app,
74
+ ws = WorkspaceManager(
75
+ project_definition=cli_context.project_definition,
83
76
  project_root=cli_context.project_root,
84
77
  )
85
- processor.process(
78
+ package_id = options["package_entity_id"]
79
+ ws.perform_action(
80
+ package_id,
81
+ EntityActions.VERSION_CREATE,
86
82
  version=version,
87
83
  patch=patch,
88
84
  force=force,
@@ -94,28 +90,29 @@ def create(
94
90
 
95
91
  @app.command("list", requires_connection=True)
96
92
  @with_project_definition()
97
- @nativeapp_definition_v2_to_v1()
93
+ @force_project_definition_v2()
98
94
  def version_list(
99
95
  **options,
100
96
  ) -> CommandResult:
101
97
  """
102
98
  Lists all versions defined in an application package.
103
99
  """
104
-
105
- assert_project_type("native_app")
106
-
107
100
  cli_context = get_cli_context()
108
- processor = NativeAppRunProcessor(
109
- project_definition=cli_context.project_definition.native_app,
101
+ ws = WorkspaceManager(
102
+ project_definition=cli_context.project_definition,
110
103
  project_root=cli_context.project_root,
111
104
  )
112
- cursor = processor.get_all_existing_versions()
105
+ package_id = options["package_entity_id"]
106
+ cursor = ws.perform_action(
107
+ package_id,
108
+ EntityActions.VERSION_LIST,
109
+ )
113
110
  return QueryResult(cursor)
114
111
 
115
112
 
116
113
  @app.command(requires_connection=True)
117
114
  @with_project_definition()
118
- @nativeapp_definition_v2_to_v1()
115
+ @force_project_definition_v2()
119
116
  def drop(
120
117
  version: Optional[str] = typer.Argument(
121
118
  None,
@@ -129,13 +126,17 @@ def drop(
129
126
  Drops a version defined in your application package. Versions can either be passed in as an argument to the command or read from the `manifest.yml` file.
130
127
  Dropping patches is not allowed.
131
128
  """
132
-
133
- assert_project_type("native_app")
134
-
135
129
  cli_context = get_cli_context()
136
- processor = NativeAppVersionDropProcessor(
137
- project_definition=cli_context.project_definition.native_app,
130
+ ws = WorkspaceManager(
131
+ project_definition=cli_context.project_definition,
138
132
  project_root=cli_context.project_root,
139
133
  )
140
- processor.process(version, force, interactive)
134
+ package_id = options["package_entity_id"]
135
+ ws.perform_action(
136
+ package_id,
137
+ EntityActions.VERSION_DROP,
138
+ version=version,
139
+ interactive=interactive,
140
+ force=force,
141
+ )
141
142
  return MessageResult(f"Version drop is now complete.")
@@ -15,7 +15,7 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  from pathlib import Path
18
- from typing import Dict, Optional
18
+ from typing import Optional
19
19
 
20
20
  from snowflake.cli._plugins.nativeapp.entities.application_package import (
21
21
  ApplicationPackageEntity,
@@ -24,52 +24,10 @@ from snowflake.cli._plugins.nativeapp.manager import (
24
24
  NativeAppCommandProcessor,
25
25
  NativeAppManager,
26
26
  )
27
- from snowflake.cli._plugins.nativeapp.run_processor import NativeAppRunProcessor
28
27
  from snowflake.cli.api.console import cli_console as cc
29
28
  from snowflake.cli.api.project.schemas.v1.native_app.native_app import NativeApp
30
29
 
31
30
 
32
- class NativeAppVersionCreateProcessor(NativeAppRunProcessor):
33
- def __init__(self, project_definition: Dict, project_root: Path):
34
- super().__init__(project_definition, project_root)
35
-
36
- def process(
37
- self,
38
- version: Optional[str],
39
- patch: Optional[int],
40
- force: bool,
41
- interactive: bool,
42
- skip_git_check: bool,
43
- *args,
44
- **kwargs,
45
- ):
46
- return ApplicationPackageEntity.version_create(
47
- console=cc,
48
- project_root=self.project_root,
49
- deploy_root=self.deploy_root,
50
- bundle_root=self.bundle_root,
51
- generated_root=self.generated_root,
52
- artifacts=self.artifacts,
53
- package_name=self.package_name,
54
- package_role=self.package_role,
55
- package_distribution=self.package_distribution,
56
- prune=True,
57
- recursive=True,
58
- paths=None,
59
- print_diff=True,
60
- validate=True,
61
- stage_fqn=self.stage_fqn,
62
- package_warehouse=self.package_warehouse,
63
- post_deploy_hooks=self.package_post_deploy_hooks,
64
- package_scripts=self.package_scripts,
65
- version=version,
66
- patch=patch,
67
- force=force,
68
- interactive=interactive,
69
- skip_git_check=skip_git_check,
70
- )
71
-
72
-
73
31
  class NativeAppVersionDropProcessor(NativeAppManager, NativeAppCommandProcessor):
74
32
  def __init__(self, project_definition: NativeApp, project_root: Path):
75
33
  super().__init__(project_definition, project_root)
@@ -158,10 +158,8 @@ def _check_if_replace_is_required(
158
158
  )
159
159
  return True
160
160
 
161
- if (
162
- resource_json["handler"].lower() != entity.handler.lower()
163
- or _sql_to_python_return_type_mapper(resource_json["returns"]).lower()
164
- != entity.returns.lower()
161
+ if resource_json["handler"].lower() != entity.handler.lower() or not same_type(
162
+ resource_json["returns"], entity.returns
165
163
  ):
166
164
  log.info(
167
165
  "Return type or handler types do not match. Replacing the %s.", object_type
@@ -216,24 +214,68 @@ def _snowflake_dependencies_differ(
216
214
  return _standardize(old_dependencies) != _standardize(new_dependencies)
217
215
 
218
216
 
219
- def _sql_to_python_return_type_mapper(resource_return_type: str) -> str:
220
- """
221
- Some of the Python data types get converted to SQL types, when function/procedure is created.
222
- So, to properly compare types, we use mapping based on:
223
- https://docs.snowflake.com/en/developer-guide/udf-stored-procedure-data-type-mapping#sql-python-data-type-mappings
217
+ def same_type(sf_type: str, local_type: str) -> bool:
218
+ sf_type, local_type = sf_type.upper(), local_type.upper()
224
219
 
225
- Mind you, this only applies to cases, in which Snowflake accepts Python type as return.
226
- Ie. if function returns list, it has to be declared as 'array' during creation,
227
- therefore any conversion is not necessary
228
- """
220
+ # 1. Types are equal out of the box
221
+ if sf_type == local_type:
222
+ return True
223
+
224
+ # 2. Local type is alias for Snowflake type
225
+ local_type = user_to_sql_type_mapper(local_type).upper()
226
+ if sf_type == local_type:
227
+ return True
228
+
229
+ # 3. Local type is a subset of Snowflake type, e.g. VARCHAR(N) == VARCHAR
230
+ # We solved for local VARCHAR(N) in point 1 & 2 as those are explicit types
231
+ if sf_type.startswith(local_type):
232
+ return True
233
+
234
+ # 4. Snowflake types is subset of local type
235
+ if local_type.startswith(sf_type):
236
+ return True
237
+
238
+ return False
239
+
240
+
241
+ def user_to_sql_type_mapper(user_provided_type: str) -> str:
229
242
  mapping = {
230
- "number(38,0)": "int",
231
- "timestamp_ntz(9)": "datetime",
232
- "timestamp_tz(9)": "datetime",
233
- "varchar(16777216)": "string",
243
+ ("VARCHAR", "(16777216)"): ("CHAR", "TEXT", "STRING"),
244
+ ("BINARY", "(8388608)"): ("BINARY", "VARBINARY"),
245
+ ("NUMBER", "(38,0)"): (
246
+ "NUMBER",
247
+ "DECIMAL",
248
+ "INT",
249
+ "INTEGER",
250
+ "BIGINT",
251
+ "SMALLINT",
252
+ "TINYINT",
253
+ "BYTEINT",
254
+ ),
255
+ ("FLOAT", ""): (
256
+ "FLOAT",
257
+ "DOUBLE",
258
+ "DOUBLE PRECISION",
259
+ "REAL",
260
+ "FLOAT",
261
+ "FLOAT4",
262
+ "FLOAT8",
263
+ ),
264
+ ("TIMESTAMP_NTZ", ""): ("TIMESTAMP_NTZ", "TIMESTAMPNTZ", "DATETIME"),
265
+ ("TIMESTAMP_LTZ", ""): ("TIMESTAMP_LTZ", "TIMESTAMPLTZ"),
266
+ ("TIMESTAMP_TZ", ""): ("TIMESTAMP_TZ", "TIMESTAMPTZ"),
234
267
  }
235
268
 
236
- return mapping.get(resource_return_type.lower(), resource_return_type.lower())
269
+ user_provided_type = user_provided_type.upper()
270
+ for (cast_type, default), matching_types in mapping.items():
271
+ for type_ in matching_types:
272
+ if user_provided_type == type_:
273
+ # TEXT -> VARCHAR(16777216)
274
+ return cast_type + default
275
+ if user_provided_type.startswith(type_):
276
+ # TEXT(30) -> VARCHAR(30)
277
+ return user_provided_type.replace(type_, cast_type + default)
278
+ return user_provided_type
237
279
 
238
280
 
239
281
  def _compare_imports(
@@ -169,7 +169,7 @@ class AnacondaPackagesManager(SqlExecutionMixin):
169
169
 
170
170
  def find_packages_available_in_snowflake_anaconda(self) -> AnacondaPackages:
171
171
  """
172
- Finds python packages available in Snowflake to use in functions and stored procedures.
172
+ Finds Python packages available in Snowflake to use in functions and stored procedures.
173
173
  It tries to get the list of packages using SQL query
174
174
  but if the try fails then the fallback is to parse JSON containing info about Snowflake's Anaconda channel.
175
175
  """
@@ -178,7 +178,7 @@ class AnacondaPackagesManager(SqlExecutionMixin):
178
178
 
179
179
  def _query_snowflake_for_available_packages(self) -> dict[str, AvailablePackage]:
180
180
  cursor = self._execute_query(
181
- "select package_name, version from information_schema.packages where language = 'python'",
181
+ "select package_name, version from snowflake.information_schema.packages where language = 'python'",
182
182
  cursor_class=DictCursor,
183
183
  )
184
184
  if cursor.rowcount is None or cursor.rowcount == 0:
@@ -39,6 +39,7 @@ from snowflake.cli.api.identifiers import FQN
39
39
  from snowflake.cli.api.output.types import (
40
40
  CollectionResult,
41
41
  MessageResult,
42
+ QueryResult,
42
43
  SingleQueryResult,
43
44
  )
44
45
  from snowflake.cli.api.project.util import is_valid_object_name
@@ -99,44 +100,10 @@ def list_images(
99
100
  **options,
100
101
  ) -> CollectionResult:
101
102
  """Lists images in the given repository."""
102
- repository_manager = ImageRepositoryManager()
103
- database = repository_manager.get_database()
104
- schema = repository_manager.get_schema()
105
- url = repository_manager.get_repository_url(name.identifier)
106
- api_url = repository_manager.get_repository_api_url(url)
107
- bearer_login = RegistryManager().login_to_registry(api_url)
108
- repos = []
109
- query: Optional[str] = f"{api_url}/_catalog?n=10"
110
-
111
- while query:
112
- # Make paginated catalog requests
113
- response = requests.get(
114
- query, headers={"Authorization": f"Bearer {bearer_login}"}
115
- )
116
-
117
- if response.status_code != 200:
118
- raise ClickException(f"Call to the registry failed {response.text}")
119
-
120
- data = json.loads(response.text)
121
- if "repositories" in data:
122
- repos.extend(data["repositories"])
123
-
124
- if "Link" in response.headers:
125
- # There are more results
126
- query = f"{api_url}/_catalog?n=10&last={repos[-1]}"
127
- else:
128
- query = None
129
-
130
- images = []
131
- for repo in repos:
132
- prefix = f"/{database}/{schema}/{name}/"
133
- repo = repo.replace("baserepo/", prefix)
134
- images.append({"image": repo})
135
-
136
- return CollectionResult(images)
103
+ return QueryResult(ImageRepositoryManager().list_images(name.identifier))
137
104
 
138
105
 
139
- @app.command("list-tags", requires_connection=True)
106
+ @app.command("list-tags", requires_connection=True, deprecated=True)
140
107
  def list_tags(
141
108
  name: FQN = REPO_NAME_ARGUMENT,
142
109
  image_name: str = typer.Option(
@@ -149,7 +116,7 @@ def list_tags(
149
116
  ),
150
117
  **options,
151
118
  ) -> CollectionResult:
152
- """Lists tags for the given image in a repository."""
119
+ """Lists tags for the given image in a repository. This command is deprecated and will be removed in a future release. Use `list-images` instead."""
153
120
 
154
121
  repository_manager = ImageRepositoryManager()
155
122
  url = repository_manager.get_repository_url(name.identifier)
@@ -18,6 +18,7 @@ from snowflake.cli._plugins.spcs.common import handle_object_already_exists
18
18
  from snowflake.cli.api.constants import ObjectType
19
19
  from snowflake.cli.api.identifiers import FQN
20
20
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
21
+ from snowflake.connector.cursor import SnowflakeCursor
21
22
  from snowflake.connector.errors import ProgrammingError
22
23
 
23
24
 
@@ -32,7 +33,6 @@ class ImageRepositoryManager(SqlExecutionMixin):
32
33
  return self._conn.role
33
34
 
34
35
  def get_repository_url(self, repo_name: str, with_scheme: bool = True):
35
-
36
36
  repo_row = self.show_specific_object(
37
37
  "image repositories", repo_name, check_schema=True
38
38
  )
@@ -82,3 +82,6 @@ class ImageRepositoryManager(SqlExecutionMixin):
82
82
  handle_object_already_exists(
83
83
  e, ObjectType.IMAGE_REPOSITORY, name, replace_available=True
84
84
  )
85
+
86
+ def list_images(self, repo_name: str) -> SnowflakeCursor:
87
+ return self._execute_query(f"show images in image repository {repo_name}")