snowflake-cli-labs 2.3.1__py3-none-any.whl → 2.4.0rc1__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.
- snowflake/cli/__about__.py +1 -1
- snowflake/cli/api/__init__.py +2 -0
- snowflake/cli/api/cli_global_context.py +8 -1
- snowflake/cli/api/commands/decorators.py +2 -2
- snowflake/cli/api/commands/flags.py +49 -4
- snowflake/cli/api/commands/snow_typer.py +2 -0
- snowflake/cli/api/console/abc.py +2 -0
- snowflake/cli/api/console/console.py +6 -5
- snowflake/cli/api/constants.py +5 -0
- snowflake/cli/api/exceptions.py +12 -0
- snowflake/cli/api/identifiers.py +123 -0
- snowflake/cli/api/plugins/command/__init__.py +2 -0
- snowflake/cli/api/plugins/plugin_config.py +2 -0
- snowflake/cli/api/project/definition.py +2 -0
- snowflake/cli/api/project/errors.py +3 -3
- snowflake/cli/api/project/schemas/identifier_model.py +35 -0
- snowflake/cli/api/project/schemas/native_app/native_app.py +4 -0
- snowflake/cli/api/project/schemas/native_app/path_mapping.py +21 -3
- snowflake/cli/api/project/schemas/project_definition.py +58 -6
- snowflake/cli/api/project/schemas/snowpark/argument.py +2 -0
- snowflake/cli/api/project/schemas/snowpark/callable.py +8 -17
- snowflake/cli/api/project/schemas/streamlit/streamlit.py +2 -2
- snowflake/cli/api/project/schemas/updatable_model.py +2 -0
- snowflake/cli/api/project/util.py +2 -0
- snowflake/cli/api/secure_path.py +2 -0
- snowflake/cli/api/sql_execution.py +14 -54
- snowflake/cli/api/utils/cursor.py +2 -0
- snowflake/cli/api/utils/models.py +23 -0
- snowflake/cli/api/utils/naming_utils.py +0 -27
- snowflake/cli/api/utils/rendering.py +178 -23
- snowflake/cli/app/api_impl/plugin/plugin_config_provider_impl.py +2 -0
- snowflake/cli/app/cli_app.py +4 -1
- snowflake/cli/app/commands_registration/builtin_plugins.py +8 -0
- snowflake/cli/app/commands_registration/command_plugins_loader.py +2 -0
- snowflake/cli/app/commands_registration/commands_registration_with_callbacks.py +2 -0
- snowflake/cli/app/commands_registration/typer_registration.py +2 -0
- snowflake/cli/app/dev/pycharm_remote_debug.py +2 -0
- snowflake/cli/app/loggers.py +2 -0
- snowflake/cli/app/main_typer.py +1 -1
- snowflake/cli/app/printing.py +3 -1
- snowflake/cli/app/snow_connector.py +2 -2
- snowflake/cli/plugins/connection/commands.py +5 -14
- snowflake/cli/plugins/connection/util.py +1 -1
- snowflake/cli/plugins/cortex/__init__.py +0 -0
- snowflake/cli/plugins/cortex/commands.py +312 -0
- snowflake/cli/plugins/cortex/constants.py +3 -0
- snowflake/cli/plugins/cortex/manager.py +175 -0
- snowflake/cli/plugins/cortex/plugin_spec.py +16 -0
- snowflake/cli/plugins/cortex/types.py +8 -0
- snowflake/cli/plugins/git/commands.py +15 -0
- snowflake/cli/plugins/nativeapp/artifacts.py +368 -123
- snowflake/cli/plugins/nativeapp/codegen/artifact_processor.py +45 -0
- snowflake/cli/plugins/nativeapp/codegen/compiler.py +104 -0
- snowflake/cli/plugins/nativeapp/codegen/sandbox.py +2 -0
- snowflake/cli/plugins/nativeapp/codegen/snowpark/callback_source.py.jinja +181 -0
- snowflake/cli/plugins/nativeapp/codegen/snowpark/extension_function_utils.py +196 -0
- snowflake/cli/plugins/nativeapp/codegen/snowpark/models.py +47 -0
- snowflake/cli/plugins/nativeapp/codegen/snowpark/python_processor.py +489 -0
- snowflake/cli/plugins/nativeapp/commands.py +11 -4
- snowflake/cli/plugins/nativeapp/common_flags.py +12 -5
- snowflake/cli/plugins/nativeapp/manager.py +49 -16
- snowflake/cli/plugins/nativeapp/policy.py +2 -0
- snowflake/cli/plugins/nativeapp/run_processor.py +2 -0
- snowflake/cli/plugins/nativeapp/teardown_processor.py +80 -8
- snowflake/cli/plugins/nativeapp/utils.py +7 -6
- snowflake/cli/plugins/nativeapp/version/commands.py +6 -5
- snowflake/cli/plugins/nativeapp/version/version_processor.py +2 -0
- snowflake/cli/plugins/notebook/commands.py +21 -0
- snowflake/cli/plugins/notebook/exceptions.py +6 -0
- snowflake/cli/plugins/notebook/manager.py +46 -3
- snowflake/cli/plugins/notebook/types.py +2 -0
- snowflake/cli/plugins/object/command_aliases.py +80 -0
- snowflake/cli/plugins/object/commands.py +10 -6
- snowflake/cli/plugins/object/common.py +2 -0
- snowflake/cli/plugins/object_stage_deprecated/__init__.py +1 -0
- snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +20 -0
- snowflake/cli/plugins/snowpark/commands.py +62 -6
- snowflake/cli/plugins/snowpark/common.py +17 -6
- snowflake/cli/plugins/spcs/compute_pool/commands.py +22 -1
- snowflake/cli/plugins/spcs/compute_pool/manager.py +2 -0
- snowflake/cli/plugins/spcs/image_repository/commands.py +25 -1
- snowflake/cli/plugins/spcs/image_repository/manager.py +3 -1
- snowflake/cli/plugins/spcs/services/commands.py +39 -5
- snowflake/cli/plugins/spcs/services/manager.py +2 -0
- snowflake/cli/plugins/sql/commands.py +13 -5
- snowflake/cli/plugins/sql/manager.py +40 -19
- snowflake/cli/plugins/stage/commands.py +29 -3
- snowflake/cli/plugins/stage/diff.py +2 -0
- snowflake/cli/plugins/streamlit/commands.py +26 -10
- snowflake/cli/plugins/streamlit/manager.py +9 -10
- {snowflake_cli_labs-2.3.1.dist-info → snowflake_cli_labs-2.4.0rc1.dist-info}/METADATA +4 -2
- {snowflake_cli_labs-2.3.1.dist-info → snowflake_cli_labs-2.4.0rc1.dist-info}/RECORD +96 -76
- /snowflake/cli/plugins/{object/stage_deprecated → object_stage_deprecated}/commands.py +0 -0
- {snowflake_cli_labs-2.3.1.dist-info → snowflake_cli_labs-2.4.0rc1.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-2.3.1.dist-info → snowflake_cli_labs-2.4.0rc1.dist-info}/entry_points.txt +0 -0
- {snowflake_cli_labs-2.3.1.dist-info → snowflake_cli_labs-2.4.0rc1.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from enum import Enum
|
|
5
|
-
from typing import Dict, List, Optional, Set
|
|
5
|
+
from typing import Dict, List, Optional, Set, Tuple
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
8
|
from click import ClickException
|
|
@@ -14,6 +14,8 @@ from snowflake.cli.api.commands.flags import (
|
|
|
14
14
|
ReplaceOption,
|
|
15
15
|
deprecated_flag_callback_enum,
|
|
16
16
|
execution_identifier_argument,
|
|
17
|
+
identifier_argument,
|
|
18
|
+
like_option,
|
|
17
19
|
)
|
|
18
20
|
from snowflake.cli.api.commands.project_initialisation import add_init_command
|
|
19
21
|
from snowflake.cli.api.commands.snow_typer import SnowTyper
|
|
@@ -25,6 +27,7 @@ from snowflake.cli.api.constants import (
|
|
|
25
27
|
from snowflake.cli.api.exceptions import (
|
|
26
28
|
SecretsWithoutExternalAccessIntegrationError,
|
|
27
29
|
)
|
|
30
|
+
from snowflake.cli.api.identifiers import FQN
|
|
28
31
|
from snowflake.cli.api.output.types import (
|
|
29
32
|
CollectionResult,
|
|
30
33
|
CommandResult,
|
|
@@ -32,11 +35,22 @@ from snowflake.cli.api.output.types import (
|
|
|
32
35
|
SingleQueryResult,
|
|
33
36
|
)
|
|
34
37
|
from snowflake.cli.api.project.schemas.snowpark.callable import (
|
|
35
|
-
Callable,
|
|
36
38
|
FunctionSchema,
|
|
37
39
|
ProcedureSchema,
|
|
38
40
|
)
|
|
39
41
|
from snowflake.cli.api.secure_path import SecurePath
|
|
42
|
+
from snowflake.cli.plugins.object.commands import (
|
|
43
|
+
describe as object_describe,
|
|
44
|
+
)
|
|
45
|
+
from snowflake.cli.plugins.object.commands import (
|
|
46
|
+
drop as object_drop,
|
|
47
|
+
)
|
|
48
|
+
from snowflake.cli.plugins.object.commands import (
|
|
49
|
+
list_ as object_list,
|
|
50
|
+
)
|
|
51
|
+
from snowflake.cli.plugins.object.commands import (
|
|
52
|
+
scope_option,
|
|
53
|
+
)
|
|
40
54
|
from snowflake.cli.plugins.object.manager import ObjectManager
|
|
41
55
|
from snowflake.cli.plugins.snowpark import package_utils
|
|
42
56
|
from snowflake.cli.plugins.snowpark.common import (
|
|
@@ -73,8 +87,15 @@ app = SnowTyper(
|
|
|
73
87
|
ObjectTypeArgument = typer.Argument(
|
|
74
88
|
help="Type of Snowpark object",
|
|
75
89
|
case_sensitive=False,
|
|
90
|
+
show_default=False,
|
|
91
|
+
)
|
|
92
|
+
IdentifierArgument = identifier_argument(
|
|
93
|
+
"function/procedure",
|
|
94
|
+
example="hello(int, string)",
|
|
95
|
+
)
|
|
96
|
+
LikeOption = like_option(
|
|
97
|
+
help_example='`list function --like "my%"` lists all functions that begin with “my”',
|
|
76
98
|
)
|
|
77
|
-
|
|
78
99
|
add_init_command(app, project_type="Snowpark", template="default_snowpark")
|
|
79
100
|
|
|
80
101
|
|
|
@@ -132,7 +153,7 @@ def deploy(
|
|
|
132
153
|
# Create stage
|
|
133
154
|
stage_name = snowpark.stage_name
|
|
134
155
|
stage_manager = StageManager()
|
|
135
|
-
stage_name =
|
|
156
|
+
stage_name = FQN.from_string(stage_name).using_context()
|
|
136
157
|
stage_manager.create(
|
|
137
158
|
stage_name=stage_name, comment="deployments managed by Snowflake CLI"
|
|
138
159
|
)
|
|
@@ -181,7 +202,7 @@ def deploy(
|
|
|
181
202
|
|
|
182
203
|
|
|
183
204
|
def _assert_object_definitions_are_correct(
|
|
184
|
-
object_type, object_definitions: List[
|
|
205
|
+
object_type, object_definitions: List[FunctionSchema | ProcedureSchema]
|
|
185
206
|
):
|
|
186
207
|
for definition in object_definitions:
|
|
187
208
|
database = definition.database
|
|
@@ -256,7 +277,7 @@ def get_app_stage_path(stage_name: Optional[str], project_name: str) -> str:
|
|
|
256
277
|
def _deploy_single_object(
|
|
257
278
|
manager: FunctionManager | ProcedureManager,
|
|
258
279
|
object_type: ObjectType,
|
|
259
|
-
object_definition:
|
|
280
|
+
object_definition: FunctionSchema | ProcedureSchema,
|
|
260
281
|
existing_objects: Dict[str, Dict],
|
|
261
282
|
snowflake_dependencies: List[str],
|
|
262
283
|
stage_artifact_path: str,
|
|
@@ -275,6 +296,7 @@ def _deploy_single_object(
|
|
|
275
296
|
handler = object_definition.handler
|
|
276
297
|
returns = object_definition.returns
|
|
277
298
|
imports = object_definition.imports
|
|
299
|
+
external_access_integrations = object_definition.external_access_integrations
|
|
278
300
|
replace_object = False
|
|
279
301
|
|
|
280
302
|
object_exists = identifier in existing_objects
|
|
@@ -285,6 +307,7 @@ def _deploy_single_object(
|
|
|
285
307
|
handler=handler,
|
|
286
308
|
return_type=returns,
|
|
287
309
|
snowflake_dependencies=snowflake_dependencies,
|
|
310
|
+
external_access_integrations=external_access_integrations,
|
|
288
311
|
imports=imports,
|
|
289
312
|
stage_artifact_file=stage_artifact_path,
|
|
290
313
|
)
|
|
@@ -463,3 +486,36 @@ def execute(
|
|
|
463
486
|
"execute", object_type=object_type, execution_identifier=execution_identifier
|
|
464
487
|
)
|
|
465
488
|
return SingleQueryResult(cursor)
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
@app.command("list", requires_connection=True)
|
|
492
|
+
def list_(
|
|
493
|
+
object_type: _SnowparkObject = ObjectTypeArgument,
|
|
494
|
+
like: str = LikeOption,
|
|
495
|
+
scope: Tuple[str, str] = scope_option(
|
|
496
|
+
help_example="`list function --in database my_db`"
|
|
497
|
+
),
|
|
498
|
+
**options,
|
|
499
|
+
):
|
|
500
|
+
"""Lists all available procedures or functions."""
|
|
501
|
+
object_list(object_type=object_type.value, like=like, scope=scope, **options)
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
@app.command("drop", requires_connection=True)
|
|
505
|
+
def drop(
|
|
506
|
+
object_type: _SnowparkObject = ObjectTypeArgument,
|
|
507
|
+
identifier: str = IdentifierArgument,
|
|
508
|
+
**options,
|
|
509
|
+
):
|
|
510
|
+
"""Drop procedure or function."""
|
|
511
|
+
object_drop(object_type=object_type.value, object_name=identifier, **options)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
@app.command("describe", requires_connection=True)
|
|
515
|
+
def describe(
|
|
516
|
+
object_type: _SnowparkObject = ObjectTypeArgument,
|
|
517
|
+
identifier: str = IdentifierArgument,
|
|
518
|
+
**options,
|
|
519
|
+
):
|
|
520
|
+
"""Provides description of a procedure or function."""
|
|
521
|
+
object_describe(object_type=object_type.value, object_name=identifier, **options)
|
|
@@ -4,6 +4,7 @@ import re
|
|
|
4
4
|
from typing import Dict, List, Optional, Set
|
|
5
5
|
|
|
6
6
|
from snowflake.cli.api.constants import ObjectType
|
|
7
|
+
from snowflake.cli.api.identifiers import FQN
|
|
7
8
|
from snowflake.cli.api.project.schemas.snowpark.argument import Argument
|
|
8
9
|
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
9
10
|
from snowflake.cli.plugins.snowpark.models import Requirement
|
|
@@ -21,6 +22,7 @@ def check_if_replace_is_required(
|
|
|
21
22
|
handler: str,
|
|
22
23
|
return_type: str,
|
|
23
24
|
snowflake_dependencies: List[str],
|
|
25
|
+
external_access_integrations: List[str],
|
|
24
26
|
imports: List[str],
|
|
25
27
|
stage_artifact_file: str,
|
|
26
28
|
) -> bool:
|
|
@@ -42,6 +44,15 @@ def check_if_replace_is_required(
|
|
|
42
44
|
)
|
|
43
45
|
return True
|
|
44
46
|
|
|
47
|
+
if set(external_access_integrations) != set(
|
|
48
|
+
resource_json.get("external_access_integrations", [])
|
|
49
|
+
):
|
|
50
|
+
log.info(
|
|
51
|
+
"Found difference of external access integrations. Replacing the %s.",
|
|
52
|
+
object_type,
|
|
53
|
+
)
|
|
54
|
+
return True
|
|
55
|
+
|
|
45
56
|
if (
|
|
46
57
|
resource_json["handler"].lower() != handler.lower()
|
|
47
58
|
or _sql_to_python_return_type_mapper(resource_json["returns"]).lower()
|
|
@@ -189,12 +200,12 @@ def build_udf_sproc_identifier(
|
|
|
189
200
|
result += f" default {val}"
|
|
190
201
|
return result
|
|
191
202
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
)
|
|
203
|
+
if udf_sproc.signature and udf_sproc.signature != "null":
|
|
204
|
+
arguments = ", ".join(format_arg(arg) for arg in udf_sproc.signature)
|
|
205
|
+
else:
|
|
206
|
+
arguments = ""
|
|
207
|
+
|
|
208
|
+
name = FQN.from_identifier_model(udf_sproc).using_context().identifier
|
|
198
209
|
return f"{name}({arguments})"
|
|
199
210
|
|
|
200
211
|
|
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from typing import Optional
|
|
2
4
|
|
|
3
5
|
import typer
|
|
4
6
|
from click import ClickException
|
|
5
|
-
from snowflake.cli.api.commands.flags import
|
|
7
|
+
from snowflake.cli.api.commands.flags import (
|
|
8
|
+
IfNotExistsOption,
|
|
9
|
+
OverrideableOption,
|
|
10
|
+
like_option,
|
|
11
|
+
)
|
|
6
12
|
from snowflake.cli.api.commands.snow_typer import SnowTyper
|
|
13
|
+
from snowflake.cli.api.constants import ObjectType
|
|
7
14
|
from snowflake.cli.api.output.types import CommandResult, SingleQueryResult
|
|
8
15
|
from snowflake.cli.api.project.util import is_valid_object_name
|
|
16
|
+
from snowflake.cli.plugins.object.command_aliases import (
|
|
17
|
+
add_object_command_aliases,
|
|
18
|
+
)
|
|
9
19
|
from snowflake.cli.plugins.object.common import CommentOption
|
|
10
20
|
from snowflake.cli.plugins.spcs.common import (
|
|
11
21
|
validate_and_set_instances,
|
|
@@ -69,6 +79,16 @@ AutoSuspendSecsOption = OverrideableOption(
|
|
|
69
79
|
|
|
70
80
|
_COMMENT_HELP = "Comment for the compute pool."
|
|
71
81
|
|
|
82
|
+
add_object_command_aliases(
|
|
83
|
+
app=app,
|
|
84
|
+
object_type=ObjectType.COMPUTE_POOL,
|
|
85
|
+
name_argument=ComputePoolNameArgument,
|
|
86
|
+
like_option=like_option(
|
|
87
|
+
help_example='`list --like "my%"` lists all compute pools that begin with “my”.'
|
|
88
|
+
),
|
|
89
|
+
scope_option=None,
|
|
90
|
+
)
|
|
91
|
+
|
|
72
92
|
|
|
73
93
|
@app.command(requires_connection=True)
|
|
74
94
|
def create(
|
|
@@ -77,6 +97,7 @@ def create(
|
|
|
77
97
|
...,
|
|
78
98
|
"--family",
|
|
79
99
|
help="Name of the instance family. For more information about instance families, refer to the SQL CREATE COMPUTE POOL command.",
|
|
100
|
+
show_default=False,
|
|
80
101
|
),
|
|
81
102
|
min_nodes: int = MinNodesOption(),
|
|
82
103
|
max_nodes: Optional[int] = MaxNodesOption(),
|
|
@@ -1,18 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import json
|
|
2
4
|
from typing import Optional
|
|
3
5
|
|
|
4
6
|
import requests
|
|
5
7
|
import typer
|
|
6
8
|
from click import ClickException
|
|
7
|
-
from snowflake.cli.api.commands.flags import
|
|
9
|
+
from snowflake.cli.api.commands.flags import (
|
|
10
|
+
IfNotExistsOption,
|
|
11
|
+
ReplaceOption,
|
|
12
|
+
like_option,
|
|
13
|
+
)
|
|
8
14
|
from snowflake.cli.api.commands.snow_typer import SnowTyper
|
|
9
15
|
from snowflake.cli.api.console import cli_console
|
|
16
|
+
from snowflake.cli.api.constants import ObjectType
|
|
10
17
|
from snowflake.cli.api.output.types import (
|
|
11
18
|
CollectionResult,
|
|
12
19
|
MessageResult,
|
|
13
20
|
SingleQueryResult,
|
|
14
21
|
)
|
|
15
22
|
from snowflake.cli.api.project.util import is_valid_object_name
|
|
23
|
+
from snowflake.cli.plugins.object.command_aliases import (
|
|
24
|
+
add_object_command_aliases,
|
|
25
|
+
scope_option,
|
|
26
|
+
)
|
|
16
27
|
from snowflake.cli.plugins.spcs.image_registry.manager import RegistryManager
|
|
17
28
|
from snowflake.cli.plugins.spcs.image_repository.manager import ImageRepositoryManager
|
|
18
29
|
|
|
@@ -34,6 +45,18 @@ def _repo_name_callback(name: str):
|
|
|
34
45
|
REPO_NAME_ARGUMENT = typer.Argument(
|
|
35
46
|
help="Name of the image repository.",
|
|
36
47
|
callback=_repo_name_callback,
|
|
48
|
+
show_default=False,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
add_object_command_aliases(
|
|
52
|
+
app=app,
|
|
53
|
+
object_type=ObjectType.IMAGE_REPOSITORY,
|
|
54
|
+
name_argument=REPO_NAME_ARGUMENT,
|
|
55
|
+
like_option=like_option(
|
|
56
|
+
help_example='`list --like "my%"` lists all image repositories that begin with “my”.'
|
|
57
|
+
),
|
|
58
|
+
scope_option=scope_option(help_example="`list --in database my_db`"),
|
|
59
|
+
ommit_commands=["describe"],
|
|
37
60
|
)
|
|
38
61
|
|
|
39
62
|
|
|
@@ -106,6 +129,7 @@ def list_tags(
|
|
|
106
129
|
"--image_name",
|
|
107
130
|
"-i",
|
|
108
131
|
help="Fully qualified name of the image as shown in the output of list-images",
|
|
132
|
+
show_default=False,
|
|
109
133
|
),
|
|
110
134
|
**options,
|
|
111
135
|
) -> CollectionResult:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from urllib.parse import urlparse
|
|
2
2
|
|
|
3
3
|
from snowflake.cli.api.constants import ObjectType
|
|
4
|
+
from snowflake.cli.api.identifiers import FQN
|
|
4
5
|
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
5
6
|
from snowflake.cli.plugins.spcs.common import handle_object_already_exists
|
|
6
7
|
from snowflake.connector.errors import ProgrammingError
|
|
@@ -22,8 +23,9 @@ class ImageRepositoryManager(SqlExecutionMixin):
|
|
|
22
23
|
"image repositories", repo_name, check_schema=True
|
|
23
24
|
)
|
|
24
25
|
if repo_row is None:
|
|
26
|
+
fqn = FQN.from_string(repo_name).using_connection(self._conn)
|
|
25
27
|
raise ProgrammingError(
|
|
26
|
-
f"Image repository '{
|
|
28
|
+
f"Image repository '{fqn.identifier}' does not exist or not authorized."
|
|
27
29
|
)
|
|
28
30
|
if with_scheme:
|
|
29
31
|
return f"https://{repo_row['repository_url']}"
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import sys
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
from typing import List, Optional
|
|
4
6
|
|
|
5
7
|
import typer
|
|
6
8
|
from click import ClickException
|
|
7
|
-
from snowflake.cli.api.commands.flags import
|
|
9
|
+
from snowflake.cli.api.commands.flags import (
|
|
10
|
+
IfNotExistsOption,
|
|
11
|
+
OverrideableOption,
|
|
12
|
+
like_option,
|
|
13
|
+
)
|
|
8
14
|
from snowflake.cli.api.commands.snow_typer import SnowTyper
|
|
15
|
+
from snowflake.cli.api.constants import ObjectType
|
|
9
16
|
from snowflake.cli.api.output.types import (
|
|
10
17
|
CommandResult,
|
|
11
18
|
QueryJsonValueResult,
|
|
@@ -13,6 +20,10 @@ from snowflake.cli.api.output.types import (
|
|
|
13
20
|
SingleQueryResult,
|
|
14
21
|
)
|
|
15
22
|
from snowflake.cli.api.project.util import is_valid_object_name
|
|
23
|
+
from snowflake.cli.plugins.object.command_aliases import (
|
|
24
|
+
add_object_command_aliases,
|
|
25
|
+
scope_option,
|
|
26
|
+
)
|
|
16
27
|
from snowflake.cli.plugins.object.common import CommentOption, Tag, TagOption
|
|
17
28
|
from snowflake.cli.plugins.spcs.common import (
|
|
18
29
|
print_log_lines,
|
|
@@ -36,7 +47,10 @@ def _service_name_callback(name: str) -> str:
|
|
|
36
47
|
|
|
37
48
|
|
|
38
49
|
ServiceNameArgument = typer.Argument(
|
|
39
|
-
...,
|
|
50
|
+
...,
|
|
51
|
+
help="Name of the service.",
|
|
52
|
+
callback=_service_name_callback,
|
|
53
|
+
show_default=False,
|
|
40
54
|
)
|
|
41
55
|
|
|
42
56
|
SpecPathOption = typer.Option(
|
|
@@ -46,6 +60,7 @@ SpecPathOption = typer.Option(
|
|
|
46
60
|
file_okay=True,
|
|
47
61
|
dir_okay=False,
|
|
48
62
|
exists=True,
|
|
63
|
+
show_default=False,
|
|
49
64
|
)
|
|
50
65
|
|
|
51
66
|
_MIN_INSTANCES_HELP = "Minimum number of service instances to run."
|
|
@@ -74,12 +89,25 @@ AutoResumeOption = OverrideableOption(
|
|
|
74
89
|
|
|
75
90
|
_COMMENT_HELP = "Comment for the service."
|
|
76
91
|
|
|
92
|
+
add_object_command_aliases(
|
|
93
|
+
app=app,
|
|
94
|
+
object_type=ObjectType.SERVICE,
|
|
95
|
+
name_argument=ServiceNameArgument,
|
|
96
|
+
like_option=like_option(
|
|
97
|
+
help_example='`list --like "my%"` lists all services that begin with “my”.'
|
|
98
|
+
),
|
|
99
|
+
scope_option=scope_option(help_example="`list --in compute-pool my_pool`"),
|
|
100
|
+
)
|
|
101
|
+
|
|
77
102
|
|
|
78
103
|
@app.command(requires_connection=True)
|
|
79
104
|
def create(
|
|
80
105
|
name: str = ServiceNameArgument,
|
|
81
106
|
compute_pool: str = typer.Option(
|
|
82
|
-
...,
|
|
107
|
+
...,
|
|
108
|
+
"--compute-pool",
|
|
109
|
+
help="Compute pool to run the service on.",
|
|
110
|
+
show_default=False,
|
|
83
111
|
),
|
|
84
112
|
spec_path: Path = SpecPathOption,
|
|
85
113
|
min_instances: int = MinInstancesOption(),
|
|
@@ -131,10 +159,16 @@ def status(name: str = ServiceNameArgument, **options) -> CommandResult:
|
|
|
131
159
|
def logs(
|
|
132
160
|
name: str = ServiceNameArgument,
|
|
133
161
|
container_name: str = typer.Option(
|
|
134
|
-
...,
|
|
162
|
+
...,
|
|
163
|
+
"--container-name",
|
|
164
|
+
help="Name of the container.",
|
|
165
|
+
show_default=False,
|
|
135
166
|
),
|
|
136
167
|
instance_id: str = typer.Option(
|
|
137
|
-
...,
|
|
168
|
+
...,
|
|
169
|
+
"--instance-id",
|
|
170
|
+
help="ID of the service instance, starting with 0.",
|
|
171
|
+
show_default=False,
|
|
138
172
|
),
|
|
139
173
|
num_lines: int = typer.Option(
|
|
140
174
|
500, "--num-lines", help="Number of lines to retrieve."
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from pathlib import Path
|
|
2
4
|
from typing import List, Optional
|
|
3
5
|
|
|
4
6
|
import typer
|
|
5
|
-
from snowflake.cli.api.commands.flags import
|
|
7
|
+
from snowflake.cli.api.commands.flags import (
|
|
8
|
+
parse_key_value_variables,
|
|
9
|
+
project_definition_option,
|
|
10
|
+
)
|
|
6
11
|
from snowflake.cli.api.commands.snow_typer import SnowTyper
|
|
7
12
|
from snowflake.cli.api.output.types import CommandResult, MultipleResults, QueryResult
|
|
8
13
|
from snowflake.cli.plugins.sql.manager import SqlManager
|
|
@@ -19,7 +24,7 @@ def _parse_key_value(key_value_str: str):
|
|
|
19
24
|
return parts[0], "=".join(parts[1:])
|
|
20
25
|
|
|
21
26
|
|
|
22
|
-
@app.command(name="sql", requires_connection=True)
|
|
27
|
+
@app.command(name="sql", requires_connection=True, no_args_is_help=True)
|
|
23
28
|
def execute_sql(
|
|
24
29
|
query: Optional[str] = typer.Option(
|
|
25
30
|
None,
|
|
@@ -27,7 +32,7 @@ def execute_sql(
|
|
|
27
32
|
"-q",
|
|
28
33
|
help="Query to execute.",
|
|
29
34
|
),
|
|
30
|
-
|
|
35
|
+
files: Optional[List[Path]] = typer.Option(
|
|
31
36
|
None,
|
|
32
37
|
"--filename",
|
|
33
38
|
"-f",
|
|
@@ -50,22 +55,25 @@ def execute_sql(
|
|
|
50
55
|
help="String in format of key=value. If provided the SQL content will "
|
|
51
56
|
"be treated as template and rendered using provided data.",
|
|
52
57
|
),
|
|
58
|
+
_: Optional[str] = project_definition_option(optional=True),
|
|
53
59
|
**options,
|
|
54
60
|
) -> CommandResult:
|
|
55
61
|
"""
|
|
56
62
|
Executes Snowflake query.
|
|
57
63
|
|
|
64
|
+
Use either query, filename or input option.
|
|
65
|
+
|
|
58
66
|
Query to execute can be specified using query option, filename option (all queries from file will be executed)
|
|
59
67
|
or via stdin by piping output from other command. For example `cat my.sql | snow sql -i`.
|
|
60
68
|
|
|
61
|
-
The command supports variable substitution that happens on client-side. Both
|
|
69
|
+
The command supports variable substitution that happens on client-side. Both &VARIABLE or &{ VARIABLE }
|
|
62
70
|
syntax are supported.
|
|
63
71
|
"""
|
|
64
72
|
data = {}
|
|
65
73
|
if data_override:
|
|
66
74
|
data = {v.key: v.value for v in parse_key_value_variables(data_override)}
|
|
67
75
|
|
|
68
|
-
single_statement, cursors = SqlManager().execute(query,
|
|
76
|
+
single_statement, cursors = SqlManager().execute(query, files, std_in, data=data)
|
|
69
77
|
if single_statement:
|
|
70
78
|
return QueryResult(next(cursors))
|
|
71
79
|
return MultipleResults((QueryResult(c) for c in cursors))
|
|
@@ -2,31 +2,31 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
4
|
from io import StringIO
|
|
5
|
+
from itertools import chain
|
|
5
6
|
from pathlib import Path
|
|
6
|
-
from typing import Dict, Iterable,
|
|
7
|
+
from typing import Dict, Iterable, List, Tuple
|
|
7
8
|
|
|
8
9
|
from click import ClickException, UsageError
|
|
9
10
|
from jinja2 import UndefinedError
|
|
10
11
|
from snowflake.cli.api.secure_path import UNLIMITED, SecurePath
|
|
11
12
|
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
12
|
-
from snowflake.cli.api.utils.rendering import
|
|
13
|
+
from snowflake.cli.api.utils.rendering import snowflake_sql_jinja_render
|
|
13
14
|
from snowflake.cli.plugins.sql.snowsql_templating import transpile_snowsql_templates
|
|
14
15
|
from snowflake.connector.cursor import SnowflakeCursor
|
|
15
16
|
from snowflake.connector.util_text import split_statements
|
|
16
17
|
|
|
18
|
+
IsSingleStatement = bool
|
|
19
|
+
|
|
17
20
|
|
|
18
21
|
class SqlManager(SqlExecutionMixin):
|
|
19
22
|
def execute(
|
|
20
23
|
self,
|
|
21
|
-
query:
|
|
22
|
-
|
|
24
|
+
query: str | None,
|
|
25
|
+
files: List[Path] | None,
|
|
23
26
|
std_in: bool,
|
|
24
27
|
data: Dict | None = None,
|
|
25
|
-
) -> Tuple[
|
|
26
|
-
inputs = [query,
|
|
27
|
-
if not any(inputs):
|
|
28
|
-
raise UsageError("Use either query, filename or input option.")
|
|
29
|
-
|
|
28
|
+
) -> Tuple[IsSingleStatement, Iterable[SnowflakeCursor]]:
|
|
29
|
+
inputs = [query, files, std_in]
|
|
30
30
|
# Check if any two inputs were provided simultaneously
|
|
31
31
|
if len([i for i in inputs if i]) > 1:
|
|
32
32
|
raise UsageError(
|
|
@@ -35,16 +35,37 @@ class SqlManager(SqlExecutionMixin):
|
|
|
35
35
|
|
|
36
36
|
if std_in:
|
|
37
37
|
query = sys.stdin.read()
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if
|
|
42
|
-
#
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
if query:
|
|
39
|
+
return self._execute_single_query(query=query, data=data)
|
|
40
|
+
|
|
41
|
+
if files:
|
|
42
|
+
# Multiple files
|
|
43
|
+
results = []
|
|
44
|
+
single_statement = False
|
|
45
|
+
for file in files:
|
|
46
|
+
query_from_file = SecurePath(file).read_text(
|
|
47
|
+
file_size_limit_mb=UNLIMITED
|
|
48
|
+
)
|
|
49
|
+
single_statement, result = self._execute_single_query(
|
|
50
|
+
query=query_from_file, data=data
|
|
51
|
+
)
|
|
52
|
+
results.append(result)
|
|
53
|
+
|
|
54
|
+
# Use single_statement if there's only one, otherwise this is multi statement result
|
|
55
|
+
single_statement = len(files) == 1 and single_statement
|
|
56
|
+
return single_statement, chain.from_iterable(results)
|
|
57
|
+
|
|
58
|
+
# At that point, no stdin, query or files were provided
|
|
59
|
+
raise UsageError("Use either query, filename or input option.")
|
|
60
|
+
|
|
61
|
+
def _execute_single_query(
|
|
62
|
+
self, query: str, data: Dict | None = None
|
|
63
|
+
) -> Tuple[IsSingleStatement, Iterable[SnowflakeCursor]]:
|
|
64
|
+
try:
|
|
65
|
+
query = transpile_snowsql_templates(query)
|
|
66
|
+
query = snowflake_sql_jinja_render(content=query, data=data)
|
|
67
|
+
except UndefinedError as err:
|
|
68
|
+
raise ClickException(f"SQL template rendering error: {err}")
|
|
48
69
|
|
|
49
70
|
statements = tuple(
|
|
50
71
|
statement
|
|
@@ -11,9 +11,11 @@ from snowflake.cli.api.commands.flags import (
|
|
|
11
11
|
OnErrorOption,
|
|
12
12
|
PatternOption,
|
|
13
13
|
VariablesOption,
|
|
14
|
+
like_option,
|
|
14
15
|
)
|
|
15
16
|
from snowflake.cli.api.commands.snow_typer import SnowTyper
|
|
16
17
|
from snowflake.cli.api.console import cli_console
|
|
18
|
+
from snowflake.cli.api.constants import ObjectType
|
|
17
19
|
from snowflake.cli.api.output.types import (
|
|
18
20
|
CollectionResult,
|
|
19
21
|
CommandResult,
|
|
@@ -22,6 +24,10 @@ from snowflake.cli.api.output.types import (
|
|
|
22
24
|
SingleQueryResult,
|
|
23
25
|
)
|
|
24
26
|
from snowflake.cli.api.utils.path_utils import is_stage_path
|
|
27
|
+
from snowflake.cli.plugins.object.command_aliases import (
|
|
28
|
+
add_object_command_aliases,
|
|
29
|
+
scope_option,
|
|
30
|
+
)
|
|
25
31
|
from snowflake.cli.plugins.stage.diff import DiffResult, compute_stage_diff
|
|
26
32
|
from snowflake.cli.plugins.stage.manager import OnErrorType, StageManager
|
|
27
33
|
|
|
@@ -32,6 +38,16 @@ app = SnowTyper(
|
|
|
32
38
|
|
|
33
39
|
StageNameArgument = typer.Argument(..., help="Name of the stage.", show_default=False)
|
|
34
40
|
|
|
41
|
+
add_object_command_aliases(
|
|
42
|
+
app=app,
|
|
43
|
+
object_type=ObjectType.STAGE,
|
|
44
|
+
name_argument=StageNameArgument,
|
|
45
|
+
like_option=like_option(
|
|
46
|
+
help_example='`list --like "my%"` lists all stages that begin with “my”',
|
|
47
|
+
),
|
|
48
|
+
scope_option=scope_option(help_example="`list --in database my_db`"),
|
|
49
|
+
)
|
|
50
|
+
|
|
35
51
|
|
|
36
52
|
@app.command("list-files", requires_connection=True)
|
|
37
53
|
def stage_list_files(
|
|
@@ -112,7 +128,11 @@ def stage_create(stage_name: str = StageNameArgument, **options) -> CommandResul
|
|
|
112
128
|
@app.command("remove", requires_connection=True)
|
|
113
129
|
def stage_remove(
|
|
114
130
|
stage_name: str = StageNameArgument,
|
|
115
|
-
file_name: str = typer.Argument(
|
|
131
|
+
file_name: str = typer.Argument(
|
|
132
|
+
...,
|
|
133
|
+
help="Name of the file to remove.",
|
|
134
|
+
show_default=False,
|
|
135
|
+
),
|
|
116
136
|
**options,
|
|
117
137
|
) -> CommandResult:
|
|
118
138
|
"""
|
|
@@ -125,8 +145,14 @@ def stage_remove(
|
|
|
125
145
|
|
|
126
146
|
@app.command("diff", hidden=True, requires_connection=True)
|
|
127
147
|
def stage_diff(
|
|
128
|
-
stage_name: str = typer.Argument(
|
|
129
|
-
|
|
148
|
+
stage_name: str = typer.Argument(
|
|
149
|
+
help="Fully qualified name of a stage",
|
|
150
|
+
show_default=False,
|
|
151
|
+
),
|
|
152
|
+
folder_name: str = typer.Argument(
|
|
153
|
+
help="Path to local folder",
|
|
154
|
+
show_default=False,
|
|
155
|
+
),
|
|
130
156
|
**options,
|
|
131
157
|
) -> ObjectResult:
|
|
132
158
|
"""
|