snowpark-connect 0.28.0__py3-none-any.whl → 0.28.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.
Potentially problematic release.
This version of snowpark-connect might be problematic. Click here for more details.
- snowflake/snowpark_connect/config.py +1 -1
- snowflake/snowpark_connect/execute_plan/map_execution_command.py +31 -68
- snowflake/snowpark_connect/relation/catalogs/snowflake_catalog.py +207 -20
- snowflake/snowpark_connect/relation/map_sql.py +112 -53
- snowflake/snowpark_connect/relation/read/map_read_table.py +58 -0
- snowflake/snowpark_connect/relation/write/map_write.py +57 -10
- snowflake/snowpark_connect/utils/context.py +21 -0
- snowflake/snowpark_connect/utils/identifiers.py +8 -2
- snowflake/snowpark_connect/utils/temporary_view_cache.py +61 -0
- snowflake/snowpark_connect/utils/udf_utils.py +9 -8
- snowflake/snowpark_connect/utils/udtf_utils.py +3 -2
- snowflake/snowpark_connect/version.py +1 -1
- {snowpark_connect-0.28.0.dist-info → snowpark_connect-0.28.1.dist-info}/METADATA +1 -1
- {snowpark_connect-0.28.0.dist-info → snowpark_connect-0.28.1.dist-info}/RECORD +22 -21
- {snowpark_connect-0.28.0.data → snowpark_connect-0.28.1.data}/scripts/snowpark-connect +0 -0
- {snowpark_connect-0.28.0.data → snowpark_connect-0.28.1.data}/scripts/snowpark-session +0 -0
- {snowpark_connect-0.28.0.data → snowpark_connect-0.28.1.data}/scripts/snowpark-submit +0 -0
- {snowpark_connect-0.28.0.dist-info → snowpark_connect-0.28.1.dist-info}/WHEEL +0 -0
- {snowpark_connect-0.28.0.dist-info → snowpark_connect-0.28.1.dist-info}/licenses/LICENSE-binary +0 -0
- {snowpark_connect-0.28.0.dist-info → snowpark_connect-0.28.1.dist-info}/licenses/LICENSE.txt +0 -0
- {snowpark_connect-0.28.0.dist-info → snowpark_connect-0.28.1.dist-info}/licenses/NOTICE-binary +0 -0
- {snowpark_connect-0.28.0.dist-info → snowpark_connect-0.28.1.dist-info}/top_level.txt +0 -0
|
@@ -283,7 +283,7 @@ class SessionConfig:
|
|
|
283
283
|
default_session_config = {
|
|
284
284
|
"snowpark.connect.sql.identifiers.auto-uppercase": "all_except_columns",
|
|
285
285
|
"snowpark.connect.sql.passthrough": "false",
|
|
286
|
-
"snowpark.connect.cte.optimization_enabled": "
|
|
286
|
+
"snowpark.connect.cte.optimization_enabled": "false",
|
|
287
287
|
"snowpark.connect.udtf.compatibility_mode": "false",
|
|
288
288
|
"snowpark.connect.views.duplicate_column_names_handling_mode": "rename",
|
|
289
289
|
"spark.sql.execution.pythonUDTF.arrow.enabled": "false",
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Copyright (c) 2012-2025 Snowflake Computing Inc. All rights reserved.
|
|
3
3
|
#
|
|
4
|
-
import re
|
|
5
|
-
import uuid
|
|
6
4
|
from collections import Counter
|
|
7
5
|
|
|
8
6
|
import pyspark.sql.connect.proto.base_pb2 as proto_base
|
|
9
7
|
import pyspark.sql.connect.proto.relations_pb2 as relation_proto
|
|
10
8
|
|
|
11
|
-
from snowflake.snowpark import
|
|
12
|
-
from snowflake.snowpark.exceptions import SnowparkSQLException
|
|
9
|
+
from snowflake.snowpark.types import StructField, StructType
|
|
13
10
|
from snowflake.snowpark_connect.column_name_handler import ColumnNames
|
|
14
11
|
from snowflake.snowpark_connect.config import global_config, sessions_config
|
|
15
12
|
from snowflake.snowpark_connect.constants import SERVER_SIDE_SESSION_ID
|
|
13
|
+
from snowflake.snowpark_connect.dataframe_container import DataFrameContainer
|
|
16
14
|
from snowflake.snowpark_connect.execute_plan.utils import pandas_to_arrow_batches_bytes
|
|
17
15
|
from snowflake.snowpark_connect.expression import map_udf
|
|
18
16
|
from snowflake.snowpark_connect.relation import map_udtf
|
|
@@ -28,10 +26,7 @@ from snowflake.snowpark_connect.utils.snowpark_connect_logging import logger
|
|
|
28
26
|
from snowflake.snowpark_connect.utils.telemetry import (
|
|
29
27
|
SnowparkConnectNotImplementedError,
|
|
30
28
|
)
|
|
31
|
-
|
|
32
|
-
_INTERNAL_VIEW_PREFIX = "__SC_RENAMED_V_"
|
|
33
|
-
|
|
34
|
-
_CREATE_VIEW_PATTERN = re.compile(r"create\s+or\s+replace\s+view", re.IGNORECASE)
|
|
29
|
+
from snowflake.snowpark_connect.utils.temporary_view_cache import register_temp_view
|
|
35
30
|
|
|
36
31
|
|
|
37
32
|
def _create_column_rename_map(
|
|
@@ -98,32 +93,35 @@ def map_execution_command(
|
|
|
98
93
|
input_df = input_df_container.dataframe
|
|
99
94
|
column_map = input_df_container.column_map
|
|
100
95
|
|
|
96
|
+
# TODO: Remove code handling deduplication. When view are not materialized we don't have to care about it.
|
|
101
97
|
session_config = sessions_config[get_session_id()]
|
|
102
98
|
duplicate_column_names_handling_mode = session_config[
|
|
103
99
|
"snowpark.connect.views.duplicate_column_names_handling_mode"
|
|
104
100
|
]
|
|
105
101
|
|
|
102
|
+
spark_columns = input_df_container.column_map.get_spark_columns()
|
|
106
103
|
# rename columns to match spark names
|
|
107
104
|
if duplicate_column_names_handling_mode == "rename":
|
|
108
105
|
# deduplicate column names by appending _DEDUP_1, _DEDUP_2, etc.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
)
|
|
106
|
+
rename_map = _create_column_rename_map(column_map.columns, True)
|
|
107
|
+
snowpark_columns = list(rename_map.values())
|
|
108
|
+
input_df = input_df.rename(rename_map)
|
|
112
109
|
elif duplicate_column_names_handling_mode == "drop":
|
|
113
110
|
# Drop duplicate column names by removing all but the first occurrence.
|
|
114
111
|
duplicated_columns, remaining_columns = _find_duplicated_columns(
|
|
115
112
|
column_map.columns
|
|
116
113
|
)
|
|
114
|
+
rename_map = _create_column_rename_map(remaining_columns, False)
|
|
115
|
+
snowpark_columns = list(rename_map.values())
|
|
116
|
+
spark_columns = list(dict.fromkeys(spark_columns))
|
|
117
117
|
if len(duplicated_columns) > 0:
|
|
118
118
|
input_df = input_df.drop(*duplicated_columns)
|
|
119
|
-
input_df = input_df.rename(
|
|
120
|
-
_create_column_rename_map(remaining_columns, False)
|
|
121
|
-
)
|
|
119
|
+
input_df = input_df.rename(rename_map)
|
|
122
120
|
else:
|
|
123
121
|
# rename columns without deduplication
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
)
|
|
122
|
+
rename_map = _create_column_rename_map(column_map.columns, True)
|
|
123
|
+
snowpark_columns = list(rename_map.values())
|
|
124
|
+
input_df = input_df.rename(rename_map)
|
|
127
125
|
|
|
128
126
|
if req.is_global:
|
|
129
127
|
view_name = [global_config.spark_sql_globalTempDatabase, req.name]
|
|
@@ -132,18 +130,23 @@ def map_execution_command(
|
|
|
132
130
|
view_name = [
|
|
133
131
|
spark_to_sf_single_id_with_unquoting(part) for part in view_name
|
|
134
132
|
]
|
|
133
|
+
joined_view_name = ".".join(view_name)
|
|
135
134
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
135
|
+
schema = StructType(
|
|
136
|
+
[
|
|
137
|
+
StructField(field.name, field.datatype)
|
|
138
|
+
for field in input_df.schema.fields
|
|
139
|
+
]
|
|
140
|
+
)
|
|
141
|
+
input_df_container = DataFrameContainer.create_with_column_mapping(
|
|
142
|
+
dataframe=input_df,
|
|
143
|
+
spark_column_names=spark_columns,
|
|
144
|
+
snowpark_column_names=snowpark_columns,
|
|
145
|
+
parent_column_name_map=input_df_container.column_map,
|
|
146
|
+
cached_schema_getter=lambda: schema,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
register_temp_view(joined_view_name, input_df_container, req.replace)
|
|
147
150
|
case "write_stream_operation_start":
|
|
148
151
|
match request.plan.command.write_stream_operation_start.format:
|
|
149
152
|
case "console":
|
|
@@ -207,43 +210,3 @@ def map_execution_command(
|
|
|
207
210
|
raise SnowparkConnectNotImplementedError(
|
|
208
211
|
f"Command type {other} not implemented"
|
|
209
212
|
)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
def _generate_random_builtin_view_name() -> str:
|
|
213
|
-
return _INTERNAL_VIEW_PREFIX + str(uuid.uuid4()).replace("-", "")
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def _is_error_caused_by_view_referencing_itself(exc: Exception) -> bool:
|
|
217
|
-
return "view definition refers to view being defined" in str(exc).lower()
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
def _create_chained_view(input_df: DataFrame, view_name: str) -> None:
|
|
221
|
-
"""
|
|
222
|
-
In order to create a view, which references itself, Spark would here take the previous
|
|
223
|
-
definition of A and paste it in place of `FROM A`. Snowflake would fail in such case, so
|
|
224
|
-
as a workaround, we create a chain of internal views instead. This function:
|
|
225
|
-
1. Renames previous definition of A to some internal name (instead of deleting).
|
|
226
|
-
2. Adjusts the DDL of a new statement to reference the name of a renmaed internal view, instead of itself.
|
|
227
|
-
"""
|
|
228
|
-
|
|
229
|
-
session = Session.get_active_session()
|
|
230
|
-
|
|
231
|
-
view_name = ".".join(view_name)
|
|
232
|
-
|
|
233
|
-
tmp_name = _generate_random_builtin_view_name()
|
|
234
|
-
old_name_replacement = _generate_random_builtin_view_name()
|
|
235
|
-
|
|
236
|
-
input_df.create_or_replace_temp_view(tmp_name)
|
|
237
|
-
|
|
238
|
-
session.sql(f"ALTER VIEW {view_name} RENAME TO {old_name_replacement}").collect()
|
|
239
|
-
|
|
240
|
-
ddl: str = session.sql(f"SELECT GET_DDL('VIEW', '{tmp_name}')").collect()[0][0]
|
|
241
|
-
|
|
242
|
-
ddl = ddl.replace(view_name, old_name_replacement)
|
|
243
|
-
|
|
244
|
-
# GET_DDL result doesn't contain `TEMPORARY`, it's likely a bug.
|
|
245
|
-
ddl = _CREATE_VIEW_PATTERN.sub("create or replace temp view", ddl)
|
|
246
|
-
|
|
247
|
-
session.sql(ddl).collect()
|
|
248
|
-
|
|
249
|
-
session.sql(f"ALTER VIEW {tmp_name} RENAME TO {view_name}").collect()
|
|
@@ -13,7 +13,6 @@ from snowflake.core.exceptions import APIError, NotFoundError
|
|
|
13
13
|
from snowflake.core.schema import Schema
|
|
14
14
|
from snowflake.core.table import Table, TableColumn
|
|
15
15
|
|
|
16
|
-
from snowflake.snowpark import functions
|
|
17
16
|
from snowflake.snowpark._internal.analyzer.analyzer_utils import (
|
|
18
17
|
quote_name_without_upper_casing,
|
|
19
18
|
unquote_if_quoted,
|
|
@@ -34,12 +33,19 @@ from snowflake.snowpark_connect.relation.catalogs.abstract_spark_catalog import
|
|
|
34
33
|
)
|
|
35
34
|
from snowflake.snowpark_connect.type_mapping import proto_to_snowpark_type
|
|
36
35
|
from snowflake.snowpark_connect.utils.identifiers import (
|
|
36
|
+
FQN,
|
|
37
|
+
spark_to_sf_single_id_with_unquoting,
|
|
37
38
|
split_fully_qualified_spark_name,
|
|
38
39
|
)
|
|
39
40
|
from snowflake.snowpark_connect.utils.session import get_or_create_snowpark_session
|
|
40
41
|
from snowflake.snowpark_connect.utils.telemetry import (
|
|
41
42
|
SnowparkConnectNotImplementedError,
|
|
42
43
|
)
|
|
44
|
+
from snowflake.snowpark_connect.utils.temporary_view_cache import (
|
|
45
|
+
get_temp_view,
|
|
46
|
+
get_temp_view_normalized_names,
|
|
47
|
+
unregister_temp_view,
|
|
48
|
+
)
|
|
43
49
|
from snowflake.snowpark_connect.utils.udf_cache import cached_udf
|
|
44
50
|
|
|
45
51
|
|
|
@@ -203,6 +209,93 @@ class SnowflakeCatalog(AbstractSparkCatalog):
|
|
|
203
209
|
exists = False
|
|
204
210
|
return pandas.DataFrame({"exists": [exists]})
|
|
205
211
|
|
|
212
|
+
def _get_temp_view_prefixes(self, spark_dbName: str | None) -> list[str]:
|
|
213
|
+
if spark_dbName is None:
|
|
214
|
+
return []
|
|
215
|
+
return [
|
|
216
|
+
quote_name_without_upper_casing(part)
|
|
217
|
+
for part in split_fully_qualified_spark_name(spark_dbName)
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
def _list_temp_views(
|
|
221
|
+
self,
|
|
222
|
+
spark_dbName: str | None = None,
|
|
223
|
+
pattern: str | None = None,
|
|
224
|
+
) -> typing.Tuple[
|
|
225
|
+
list[str | None],
|
|
226
|
+
list[list[str | None]],
|
|
227
|
+
list[str],
|
|
228
|
+
list[str | None],
|
|
229
|
+
list[str | None],
|
|
230
|
+
list[bool],
|
|
231
|
+
]:
|
|
232
|
+
catalogs: list[str | None] = list()
|
|
233
|
+
namespaces: list[list[str | None]] = list()
|
|
234
|
+
names: list[str] = list()
|
|
235
|
+
descriptions: list[str | None] = list()
|
|
236
|
+
table_types: list[str | None] = list()
|
|
237
|
+
is_temporaries: list[bool] = list()
|
|
238
|
+
|
|
239
|
+
temp_views_prefix = ".".join(self._get_temp_view_prefixes(spark_dbName))
|
|
240
|
+
normalized_spark_dbName = (
|
|
241
|
+
temp_views_prefix.lower()
|
|
242
|
+
if global_config.spark_sql_caseSensitive
|
|
243
|
+
else temp_views_prefix
|
|
244
|
+
)
|
|
245
|
+
normalized_global_temp_database_name = (
|
|
246
|
+
quote_name_without_upper_casing(
|
|
247
|
+
global_config.spark_sql_globalTempDatabase.lower()
|
|
248
|
+
)
|
|
249
|
+
if global_config.spark_sql_caseSensitive
|
|
250
|
+
else quote_name_without_upper_casing(
|
|
251
|
+
global_config.spark_sql_globalTempDatabase
|
|
252
|
+
)
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
temp_views = get_temp_view_normalized_names()
|
|
256
|
+
null_safe_pattern = pattern if pattern is not None else ""
|
|
257
|
+
|
|
258
|
+
for temp_view in temp_views:
|
|
259
|
+
normalized_temp_view = (
|
|
260
|
+
temp_view.lower()
|
|
261
|
+
if global_config.spark_sql_caseSensitive
|
|
262
|
+
else temp_view
|
|
263
|
+
)
|
|
264
|
+
fqn = FQN.from_string(temp_view)
|
|
265
|
+
normalized_schema = (
|
|
266
|
+
fqn.schema.lower()
|
|
267
|
+
if fqn.schema is not None and global_config.spark_sql_caseSensitive
|
|
268
|
+
else fqn.schema
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
is_global_view = normalized_global_temp_database_name == normalized_schema
|
|
272
|
+
is_local_temp_view = fqn.schema is None
|
|
273
|
+
# Temporary views are always shown if they match the pattern
|
|
274
|
+
matches_prefix = (
|
|
275
|
+
normalized_spark_dbName == normalized_schema or is_local_temp_view
|
|
276
|
+
)
|
|
277
|
+
if matches_prefix and bool(
|
|
278
|
+
re.match(null_safe_pattern, normalized_temp_view)
|
|
279
|
+
):
|
|
280
|
+
names.append(unquote_if_quoted(fqn.name))
|
|
281
|
+
catalogs.append(None)
|
|
282
|
+
namespaces.append(
|
|
283
|
+
[global_config.spark_sql_globalTempDatabase]
|
|
284
|
+
if is_global_view
|
|
285
|
+
else []
|
|
286
|
+
)
|
|
287
|
+
descriptions.append(None)
|
|
288
|
+
table_types.append("TEMPORARY")
|
|
289
|
+
is_temporaries.append(True)
|
|
290
|
+
return (
|
|
291
|
+
catalogs,
|
|
292
|
+
namespaces,
|
|
293
|
+
names,
|
|
294
|
+
descriptions,
|
|
295
|
+
table_types,
|
|
296
|
+
is_temporaries,
|
|
297
|
+
)
|
|
298
|
+
|
|
206
299
|
def listTables(
|
|
207
300
|
self,
|
|
208
301
|
spark_dbName: str | None = None,
|
|
@@ -232,8 +325,7 @@ class SnowflakeCatalog(AbstractSparkCatalog):
|
|
|
232
325
|
schema=sf_quote(sf_schema),
|
|
233
326
|
pattern=_normalize_identifier(pattern),
|
|
234
327
|
)
|
|
235
|
-
|
|
236
|
-
catalogs: list[str] = list()
|
|
328
|
+
catalogs: list[str | None] = list()
|
|
237
329
|
namespaces: list[list[str | None]] = list()
|
|
238
330
|
names: list[str] = list()
|
|
239
331
|
descriptions: list[str | None] = list()
|
|
@@ -253,6 +345,22 @@ class SnowflakeCatalog(AbstractSparkCatalog):
|
|
|
253
345
|
descriptions.append(o[6] if o[6] else None)
|
|
254
346
|
table_types.append("PERMANENT")
|
|
255
347
|
is_temporaries.append(False)
|
|
348
|
+
|
|
349
|
+
(
|
|
350
|
+
non_materialized_catalogs,
|
|
351
|
+
non_materialized_namespaces,
|
|
352
|
+
non_materialized_names,
|
|
353
|
+
non_materialized_descriptions,
|
|
354
|
+
non_materialized_table_types,
|
|
355
|
+
non_materialized_is_temporaries,
|
|
356
|
+
) = self._list_temp_views(spark_dbName, pattern)
|
|
357
|
+
catalogs.extend(non_materialized_catalogs)
|
|
358
|
+
namespaces.extend(non_materialized_namespaces)
|
|
359
|
+
names.extend(non_materialized_names)
|
|
360
|
+
descriptions.extend(non_materialized_descriptions)
|
|
361
|
+
table_types.extend(non_materialized_table_types)
|
|
362
|
+
is_temporaries.extend(non_materialized_is_temporaries)
|
|
363
|
+
|
|
256
364
|
return pandas.DataFrame(
|
|
257
365
|
{
|
|
258
366
|
"name": names,
|
|
@@ -297,6 +405,36 @@ class SnowflakeCatalog(AbstractSparkCatalog):
|
|
|
297
405
|
spark_tableName: str,
|
|
298
406
|
) -> pandas.DataFrame:
|
|
299
407
|
"""Listing a single table/view with provided name that's accessible in Snowflake."""
|
|
408
|
+
|
|
409
|
+
def _get_temp_view():
|
|
410
|
+
spark_table_name_parts = [
|
|
411
|
+
quote_name_without_upper_casing(part)
|
|
412
|
+
for part in split_fully_qualified_spark_name(spark_tableName)
|
|
413
|
+
]
|
|
414
|
+
spark_view_name = ".".join(spark_table_name_parts)
|
|
415
|
+
temp_view = get_temp_view(spark_view_name)
|
|
416
|
+
if temp_view:
|
|
417
|
+
return pandas.DataFrame(
|
|
418
|
+
{
|
|
419
|
+
"name": [unquote_if_quoted(spark_table_name_parts[-1])],
|
|
420
|
+
"catalog": [None],
|
|
421
|
+
"namespace": [
|
|
422
|
+
[unquote_if_quoted(spark_table_name_parts[-2])]
|
|
423
|
+
if len(spark_table_name_parts) > 1
|
|
424
|
+
else []
|
|
425
|
+
],
|
|
426
|
+
"description": [None],
|
|
427
|
+
"tableType": ["TEMPORARY"],
|
|
428
|
+
"isTemporary": [True],
|
|
429
|
+
}
|
|
430
|
+
)
|
|
431
|
+
return None
|
|
432
|
+
|
|
433
|
+
# Attempt to get the view from the non materialized views first
|
|
434
|
+
temp_view = _get_temp_view()
|
|
435
|
+
if temp_view is not None:
|
|
436
|
+
return temp_view
|
|
437
|
+
|
|
300
438
|
sp_catalog = get_or_create_snowpark_session().catalog
|
|
301
439
|
catalog, sf_database, sf_schema, table_name = _process_multi_layer_identifier(
|
|
302
440
|
spark_tableName
|
|
@@ -360,12 +498,64 @@ class SnowflakeCatalog(AbstractSparkCatalog):
|
|
|
360
498
|
exists = False
|
|
361
499
|
return pandas.DataFrame({"exists": [exists]})
|
|
362
500
|
|
|
501
|
+
def _list_temp_view_columns(
|
|
502
|
+
self,
|
|
503
|
+
spark_tableName: str,
|
|
504
|
+
spark_dbName: typing.Optional[str] = None,
|
|
505
|
+
):
|
|
506
|
+
spark_view_name_parts = [
|
|
507
|
+
quote_name_without_upper_casing(part)
|
|
508
|
+
for part in split_fully_qualified_spark_name(spark_tableName)
|
|
509
|
+
]
|
|
510
|
+
spark_view_name_parts = (
|
|
511
|
+
self._get_temp_view_prefixes(spark_dbName) + spark_view_name_parts
|
|
512
|
+
)
|
|
513
|
+
spark_view_name = ".".join(spark_view_name_parts)
|
|
514
|
+
temp_view = get_temp_view(spark_view_name)
|
|
515
|
+
|
|
516
|
+
if not temp_view:
|
|
517
|
+
return None
|
|
518
|
+
|
|
519
|
+
names: list[str] = list()
|
|
520
|
+
descriptions: list[str | None] = list()
|
|
521
|
+
data_types: list[str] = list()
|
|
522
|
+
nullables: list[bool] = list()
|
|
523
|
+
is_partitions: list[bool] = list()
|
|
524
|
+
is_buckets: list[bool] = list()
|
|
525
|
+
|
|
526
|
+
for field, spark_column in zip(
|
|
527
|
+
temp_view.dataframe.schema.fields,
|
|
528
|
+
temp_view.column_map.get_spark_columns(),
|
|
529
|
+
):
|
|
530
|
+
names.append(spark_column)
|
|
531
|
+
descriptions.append(None)
|
|
532
|
+
data_types.append(field.datatype.simpleString())
|
|
533
|
+
nullables.append(field.nullable)
|
|
534
|
+
is_partitions.append(False)
|
|
535
|
+
is_buckets.append(False)
|
|
536
|
+
|
|
537
|
+
return pandas.DataFrame(
|
|
538
|
+
{
|
|
539
|
+
"name": names,
|
|
540
|
+
"description": descriptions,
|
|
541
|
+
"dataType": data_types,
|
|
542
|
+
"nullable": nullables,
|
|
543
|
+
"isPartition": is_partitions,
|
|
544
|
+
"isBucket": is_buckets,
|
|
545
|
+
}
|
|
546
|
+
)
|
|
547
|
+
|
|
363
548
|
def listColumns(
|
|
364
549
|
self,
|
|
365
550
|
spark_tableName: str,
|
|
366
551
|
spark_dbName: typing.Optional[str] = None,
|
|
367
552
|
) -> pandas.DataFrame:
|
|
368
553
|
"""List all columns in a table/view, optionally database name filter can be provided."""
|
|
554
|
+
|
|
555
|
+
temp_view_columns = self._list_temp_view_columns(spark_tableName, spark_dbName)
|
|
556
|
+
if temp_view_columns is not None:
|
|
557
|
+
return temp_view_columns
|
|
558
|
+
|
|
369
559
|
sp_catalog = get_or_create_snowpark_session().catalog
|
|
370
560
|
columns: list[TableColumn] | None = None
|
|
371
561
|
if spark_dbName is None:
|
|
@@ -455,17 +645,15 @@ class SnowflakeCatalog(AbstractSparkCatalog):
|
|
|
455
645
|
spark_view_name: str,
|
|
456
646
|
) -> DataFrameContainer:
|
|
457
647
|
session = get_or_create_snowpark_session()
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
)
|
|
463
|
-
result_df = result_df.select(
|
|
464
|
-
functions.contains('"status"', functions.lit("successfully dropped")).alias(
|
|
465
|
-
"value"
|
|
648
|
+
if not spark_view_name == "":
|
|
649
|
+
schema = global_config.spark_sql_globalTempDatabase
|
|
650
|
+
result = unregister_temp_view(
|
|
651
|
+
f"{spark_to_sf_single_id_with_unquoting(schema)}.{spark_to_sf_single_id_with_unquoting(spark_view_name)}"
|
|
466
652
|
)
|
|
467
|
-
|
|
653
|
+
else:
|
|
654
|
+
result = False
|
|
468
655
|
columns = ["value"]
|
|
656
|
+
result_df = session.createDataFrame([result], schema=columns)
|
|
469
657
|
return DataFrameContainer.create_with_column_mapping(
|
|
470
658
|
dataframe=result_df,
|
|
471
659
|
spark_column_names=columns,
|
|
@@ -479,15 +667,14 @@ class SnowflakeCatalog(AbstractSparkCatalog):
|
|
|
479
667
|
) -> DataFrameContainer:
|
|
480
668
|
"""Drop the current temporary view."""
|
|
481
669
|
session = get_or_create_snowpark_session()
|
|
482
|
-
result = session.sql(
|
|
483
|
-
"drop view if exists identifier(?)",
|
|
484
|
-
params=[sf_quote(spark_view_name)],
|
|
485
|
-
).collect()
|
|
486
|
-
view_was_dropped = (
|
|
487
|
-
len(result) == 1 and "successfully dropped" in result[0]["status"]
|
|
488
|
-
)
|
|
489
|
-
result_df = session.createDataFrame([(view_was_dropped,)], schema=["value"])
|
|
490
670
|
columns = ["value"]
|
|
671
|
+
if spark_view_name:
|
|
672
|
+
result = unregister_temp_view(
|
|
673
|
+
spark_to_sf_single_id_with_unquoting(spark_view_name)
|
|
674
|
+
)
|
|
675
|
+
else:
|
|
676
|
+
result = False
|
|
677
|
+
result_df = session.createDataFrame([result], schema=columns)
|
|
491
678
|
return DataFrameContainer.create_with_column_mapping(
|
|
492
679
|
dataframe=result_df,
|
|
493
680
|
spark_column_names=columns,
|
|
@@ -61,6 +61,7 @@ from snowflake.snowpark_connect.utils.context import (
|
|
|
61
61
|
get_session_id,
|
|
62
62
|
get_sql_plan,
|
|
63
63
|
push_evaluating_sql_scope,
|
|
64
|
+
push_processed_view,
|
|
64
65
|
push_sql_scope,
|
|
65
66
|
set_plan_id_map,
|
|
66
67
|
set_sql_args,
|
|
@@ -80,7 +81,16 @@ from ..expression.map_sql_expression import (
|
|
|
80
81
|
map_logical_plan_expression,
|
|
81
82
|
sql_parser,
|
|
82
83
|
)
|
|
83
|
-
from ..utils.identifiers import
|
|
84
|
+
from ..utils.identifiers import (
|
|
85
|
+
spark_to_sf_single_id,
|
|
86
|
+
spark_to_sf_single_id_with_unquoting,
|
|
87
|
+
)
|
|
88
|
+
from ..utils.temporary_view_cache import (
|
|
89
|
+
get_temp_view,
|
|
90
|
+
register_temp_view,
|
|
91
|
+
unregister_temp_view,
|
|
92
|
+
)
|
|
93
|
+
from .catalogs import SNOWFLAKE_CATALOG
|
|
84
94
|
|
|
85
95
|
_ctes = ContextVar[dict[str, relation_proto.Relation]]("_ctes", default={})
|
|
86
96
|
_cte_definitions = ContextVar[dict[str, any]]("_cte_definitions", default={})
|
|
@@ -403,6 +413,7 @@ def map_sql_to_pandas_df(
|
|
|
403
413
|
) == "UnresolvedHint":
|
|
404
414
|
logical_plan = logical_plan.child()
|
|
405
415
|
|
|
416
|
+
# TODO: Add support for temporary views for SQL cases such as ShowViews, ShowColumns ect. (Currently the cases are not compatible with Spark, returning raw Snowflake rows)
|
|
406
417
|
match class_name:
|
|
407
418
|
case "AddColumns":
|
|
408
419
|
# Handle ALTER TABLE ... ADD COLUMNS (col_name data_type) -> ADD COLUMN col_name data_type
|
|
@@ -577,6 +588,23 @@ def map_sql_to_pandas_df(
|
|
|
577
588
|
)
|
|
578
589
|
snowflake_sql = parsed_sql.sql(dialect="snowflake")
|
|
579
590
|
session.sql(f"{snowflake_sql}{empty_select}").collect()
|
|
591
|
+
spark_view_name = next(
|
|
592
|
+
sqlglot.parse_one(sql_string, dialect="spark").find_all(
|
|
593
|
+
sqlglot.exp.Table
|
|
594
|
+
)
|
|
595
|
+
).name
|
|
596
|
+
snowflake_view_name = spark_to_sf_single_id_with_unquoting(
|
|
597
|
+
spark_view_name
|
|
598
|
+
)
|
|
599
|
+
temp_view = get_temp_view(snowflake_view_name)
|
|
600
|
+
if temp_view is not None and not logical_plan.replace():
|
|
601
|
+
raise AnalysisException(
|
|
602
|
+
f"[TEMP_TABLE_OR_VIEW_ALREADY_EXISTS] Cannot create the temporary view `{spark_view_name}` because it already exists."
|
|
603
|
+
)
|
|
604
|
+
else:
|
|
605
|
+
unregister_temp_view(
|
|
606
|
+
spark_to_sf_single_id_with_unquoting(spark_view_name)
|
|
607
|
+
)
|
|
580
608
|
case "CreateView":
|
|
581
609
|
current_schema = session.connection.schema
|
|
582
610
|
if (
|
|
@@ -613,50 +641,60 @@ def map_sql_to_pandas_df(
|
|
|
613
641
|
else None,
|
|
614
642
|
)
|
|
615
643
|
case "CreateViewCommand":
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
644
|
+
with push_processed_view(logical_plan.name().identifier()):
|
|
645
|
+
df_container = execute_logical_plan(logical_plan.plan())
|
|
646
|
+
df = df_container.dataframe
|
|
647
|
+
user_specified_spark_column_names = [
|
|
648
|
+
str(col._1())
|
|
649
|
+
for col in as_java_list(logical_plan.userSpecifiedColumns())
|
|
650
|
+
]
|
|
651
|
+
df_container = DataFrameContainer.create_with_column_mapping(
|
|
652
|
+
dataframe=df,
|
|
653
|
+
spark_column_names=user_specified_spark_column_names
|
|
654
|
+
if user_specified_spark_column_names
|
|
655
|
+
else df_container.column_map.get_spark_columns(),
|
|
656
|
+
snowpark_column_names=df_container.column_map.get_snowpark_columns(),
|
|
657
|
+
parent_column_name_map=df_container.column_map,
|
|
624
658
|
)
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
logical_plan.viewType(),
|
|
631
|
-
jpype.JClass(
|
|
632
|
-
"org.apache.spark.sql.catalyst.analysis.GlobalTempView$"
|
|
633
|
-
),
|
|
634
|
-
):
|
|
635
|
-
name = f"{global_config.spark_sql_globalTempDatabase}.{name}"
|
|
636
|
-
comment = logical_plan.comment()
|
|
637
|
-
maybe_comment = (
|
|
638
|
-
_escape_sql_comment(str(comment.get()))
|
|
639
|
-
if comment.isDefined()
|
|
640
|
-
else None
|
|
641
|
-
)
|
|
642
|
-
|
|
643
|
-
df = _rename_columns(
|
|
644
|
-
df, logical_plan.userSpecifiedColumns(), df_container.column_map
|
|
645
|
-
)
|
|
646
|
-
|
|
647
|
-
if logical_plan.replace():
|
|
648
|
-
df.create_or_replace_temp_view(
|
|
649
|
-
name,
|
|
650
|
-
comment=maybe_comment,
|
|
659
|
+
is_global = isinstance(
|
|
660
|
+
logical_plan.viewType(),
|
|
661
|
+
jpype.JClass(
|
|
662
|
+
"org.apache.spark.sql.catalyst.analysis.GlobalTempView$"
|
|
663
|
+
),
|
|
651
664
|
)
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
665
|
+
if is_global:
|
|
666
|
+
view_name = [
|
|
667
|
+
global_config.spark_sql_globalTempDatabase,
|
|
668
|
+
logical_plan.name().quotedString(),
|
|
669
|
+
]
|
|
670
|
+
else:
|
|
671
|
+
view_name = [logical_plan.name().quotedString()]
|
|
672
|
+
view_name = [
|
|
673
|
+
spark_to_sf_single_id_with_unquoting(part) for part in view_name
|
|
674
|
+
]
|
|
675
|
+
joined_view_name = ".".join(view_name)
|
|
676
|
+
|
|
677
|
+
register_temp_view(
|
|
678
|
+
joined_view_name,
|
|
679
|
+
df_container,
|
|
680
|
+
logical_plan.replace(),
|
|
681
|
+
)
|
|
682
|
+
tmp_views = _get_current_temp_objects()
|
|
683
|
+
tmp_views.add(
|
|
684
|
+
(
|
|
685
|
+
CURRENT_CATALOG_NAME,
|
|
686
|
+
session.connection.schema,
|
|
687
|
+
str(logical_plan.name().identifier()),
|
|
688
|
+
)
|
|
656
689
|
)
|
|
657
690
|
case "DescribeColumn":
|
|
658
|
-
name =
|
|
691
|
+
name = get_relation_identifier_name_without_uppercasing(
|
|
692
|
+
logical_plan.column()
|
|
693
|
+
)
|
|
694
|
+
if get_temp_view(name):
|
|
695
|
+
return SNOWFLAKE_CATALOG.listColumns(unquote_if_quoted(name)), ""
|
|
659
696
|
# todo double check if this is correct
|
|
697
|
+
name = get_relation_identifier_name(logical_plan.column())
|
|
660
698
|
rows = session.sql(f"DESCRIBE TABLE {name}").collect()
|
|
661
699
|
case "DescribeNamespace":
|
|
662
700
|
name = get_relation_identifier_name(logical_plan.namespace(), True)
|
|
@@ -731,9 +769,13 @@ def map_sql_to_pandas_df(
|
|
|
731
769
|
if_exists = "IF EXISTS " if logical_plan.ifExists() else ""
|
|
732
770
|
session.sql(f"DROP TABLE {if_exists}{name}").collect()
|
|
733
771
|
case "DropView":
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
772
|
+
temporary_view_name = get_relation_identifier_name_without_uppercasing(
|
|
773
|
+
logical_plan.child()
|
|
774
|
+
)
|
|
775
|
+
if not unregister_temp_view(temporary_view_name):
|
|
776
|
+
name = get_relation_identifier_name(logical_plan.child())
|
|
777
|
+
if_exists = "IF EXISTS " if logical_plan.ifExists() else ""
|
|
778
|
+
session.sql(f"DROP VIEW {if_exists}{name}").collect()
|
|
737
779
|
case "ExplainCommand":
|
|
738
780
|
inner_plan = logical_plan.logicalPlan()
|
|
739
781
|
logical_plan_name = inner_plan.nodeName()
|
|
@@ -2173,21 +2215,38 @@ def map_logical_plan_relation(
|
|
|
2173
2215
|
return proto
|
|
2174
2216
|
|
|
2175
2217
|
|
|
2176
|
-
def
|
|
2218
|
+
def _get_relation_identifier(name_obj) -> str:
|
|
2219
|
+
# IDENTIFIER(<table_name>), or IDENTIFIER(<method name>)
|
|
2220
|
+
expr_proto = map_logical_plan_expression(name_obj.identifierExpr())
|
|
2221
|
+
session = snowpark.Session.get_active_session()
|
|
2222
|
+
m = ColumnNameMap([], [], None)
|
|
2223
|
+
expr = map_single_column_expression(
|
|
2224
|
+
expr_proto, m, ExpressionTyper.dummy_typer(session)
|
|
2225
|
+
)
|
|
2226
|
+
return spark_to_sf_single_id(session.range(1).select(expr[1].col).collect()[0][0])
|
|
2227
|
+
|
|
2228
|
+
|
|
2229
|
+
def get_relation_identifier_name_without_uppercasing(name_obj) -> str:
|
|
2177
2230
|
if name_obj.getClass().getSimpleName() in (
|
|
2178
2231
|
"PlanWithUnresolvedIdentifier",
|
|
2179
2232
|
"ExpressionWithUnresolvedIdentifier",
|
|
2180
2233
|
):
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
expr_proto, m, ExpressionTyper.dummy_typer(session)
|
|
2187
|
-
)
|
|
2188
|
-
name = spark_to_sf_single_id(
|
|
2189
|
-
session.range(1).select(expr[1].col).collect()[0][0]
|
|
2234
|
+
return _get_relation_identifier(name_obj)
|
|
2235
|
+
else:
|
|
2236
|
+
name = ".".join(
|
|
2237
|
+
quote_name_without_upper_casing(str(part))
|
|
2238
|
+
for part in as_java_list(name_obj.nameParts())
|
|
2190
2239
|
)
|
|
2240
|
+
|
|
2241
|
+
return name
|
|
2242
|
+
|
|
2243
|
+
|
|
2244
|
+
def get_relation_identifier_name(name_obj, is_multi_part: bool = False) -> str:
|
|
2245
|
+
if name_obj.getClass().getSimpleName() in (
|
|
2246
|
+
"PlanWithUnresolvedIdentifier",
|
|
2247
|
+
"ExpressionWithUnresolvedIdentifier",
|
|
2248
|
+
):
|
|
2249
|
+
return _get_relation_identifier(name_obj)
|
|
2191
2250
|
else:
|
|
2192
2251
|
if is_multi_part:
|
|
2193
2252
|
try:
|
|
@@ -11,11 +11,17 @@ from snowflake.snowpark._internal.analyzer.analyzer_utils import (
|
|
|
11
11
|
unquote_if_quoted,
|
|
12
12
|
)
|
|
13
13
|
from snowflake.snowpark.exceptions import SnowparkSQLException
|
|
14
|
+
from snowflake.snowpark.types import StructField, StructType
|
|
15
|
+
from snowflake.snowpark_connect.column_name_handler import (
|
|
16
|
+
ColumnNameMap,
|
|
17
|
+
make_column_names_snowpark_compatible,
|
|
18
|
+
)
|
|
14
19
|
from snowflake.snowpark_connect.config import auto_uppercase_non_column_identifiers
|
|
15
20
|
from snowflake.snowpark_connect.dataframe_container import DataFrameContainer
|
|
16
21
|
from snowflake.snowpark_connect.relation.read.utils import (
|
|
17
22
|
rename_columns_as_snowflake_standard,
|
|
18
23
|
)
|
|
24
|
+
from snowflake.snowpark_connect.utils.context import get_processed_views
|
|
19
25
|
from snowflake.snowpark_connect.utils.identifiers import (
|
|
20
26
|
split_fully_qualified_spark_name,
|
|
21
27
|
)
|
|
@@ -23,6 +29,7 @@ from snowflake.snowpark_connect.utils.session import _get_current_snowpark_sessi
|
|
|
23
29
|
from snowflake.snowpark_connect.utils.telemetry import (
|
|
24
30
|
SnowparkConnectNotImplementedError,
|
|
25
31
|
)
|
|
32
|
+
from snowflake.snowpark_connect.utils.temporary_view_cache import get_temp_view
|
|
26
33
|
|
|
27
34
|
|
|
28
35
|
def post_process_df(
|
|
@@ -64,15 +71,66 @@ def post_process_df(
|
|
|
64
71
|
raise
|
|
65
72
|
|
|
66
73
|
|
|
74
|
+
def _get_temporary_view(
|
|
75
|
+
temp_view: DataFrameContainer, table_name: str, plan_id: int
|
|
76
|
+
) -> DataFrameContainer:
|
|
77
|
+
fields_names = [field.name for field in temp_view.dataframe.schema.fields]
|
|
78
|
+
fields_types = [field.datatype for field in temp_view.dataframe.schema.fields]
|
|
79
|
+
|
|
80
|
+
snowpark_column_names = make_column_names_snowpark_compatible(fields_names, plan_id)
|
|
81
|
+
# Rename columns in dataframe to prevent conflicting names during joins
|
|
82
|
+
renamed_df = temp_view.dataframe.select(
|
|
83
|
+
*(
|
|
84
|
+
temp_view.dataframe.col(orig).alias(alias)
|
|
85
|
+
for orig, alias in zip(fields_names, snowpark_column_names)
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
new_column_map = ColumnNameMap(
|
|
90
|
+
spark_column_names=temp_view.column_map.get_spark_columns(),
|
|
91
|
+
snowpark_column_names=snowpark_column_names,
|
|
92
|
+
column_metadata=temp_view.column_map.column_metadata,
|
|
93
|
+
column_qualifiers=[split_fully_qualified_spark_name(table_name)]
|
|
94
|
+
* len(temp_view.column_map.get_spark_columns()),
|
|
95
|
+
parent_column_name_map=temp_view.column_map.get_parent_column_name_map(),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
schema = StructType(
|
|
99
|
+
[
|
|
100
|
+
StructField(name, type, _is_column=False)
|
|
101
|
+
for name, type in zip(snowpark_column_names, fields_types)
|
|
102
|
+
]
|
|
103
|
+
)
|
|
104
|
+
return DataFrameContainer(
|
|
105
|
+
dataframe=renamed_df,
|
|
106
|
+
column_map=new_column_map,
|
|
107
|
+
table_name=temp_view.table_name,
|
|
108
|
+
alias=temp_view.alias,
|
|
109
|
+
partition_hint=temp_view.partition_hint,
|
|
110
|
+
cached_schema_getter=lambda: schema,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
67
114
|
def get_table_from_name(
|
|
68
115
|
table_name: str, session: snowpark.Session, plan_id: int
|
|
69
116
|
) -> DataFrameContainer:
|
|
70
117
|
"""Get table from name returning a container."""
|
|
118
|
+
|
|
119
|
+
# Verify if recursive view read is not attempted
|
|
120
|
+
if table_name in get_processed_views():
|
|
121
|
+
raise AnalysisException(
|
|
122
|
+
f"[RECURSIVE_VIEW] Recursive view `{table_name}` detected (cycle: `{table_name}` -> `{table_name}`)"
|
|
123
|
+
)
|
|
124
|
+
|
|
71
125
|
snowpark_name = ".".join(
|
|
72
126
|
quote_name_without_upper_casing(part)
|
|
73
127
|
for part in split_fully_qualified_spark_name(table_name)
|
|
74
128
|
)
|
|
75
129
|
|
|
130
|
+
temp_view = get_temp_view(snowpark_name)
|
|
131
|
+
if temp_view:
|
|
132
|
+
return _get_temporary_view(temp_view, table_name, plan_id)
|
|
133
|
+
|
|
76
134
|
if auto_uppercase_non_column_identifiers():
|
|
77
135
|
snowpark_name = snowpark_name.upper()
|
|
78
136
|
|
|
@@ -218,8 +218,9 @@ def map_write(request: proto_base.ExecutePlanRequest):
|
|
|
218
218
|
},
|
|
219
219
|
"overwrite": overwrite,
|
|
220
220
|
}
|
|
221
|
-
#
|
|
222
|
-
|
|
221
|
+
# Download from the base write path to ensure we fetch whatever Snowflake produced.
|
|
222
|
+
# Using the base avoids coupling to exact filenames/prefixes.
|
|
223
|
+
download_stage_path = write_path
|
|
223
224
|
|
|
224
225
|
# Check for partition hint early to determine precedence over single option
|
|
225
226
|
partition_hint = result.partition_hint
|
|
@@ -238,13 +239,19 @@ def map_write(request: proto_base.ExecutePlanRequest):
|
|
|
238
239
|
raise SnowparkConnectNotImplementedError(
|
|
239
240
|
"Partitioning is only supported for parquet format"
|
|
240
241
|
)
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
242
|
+
# Build Spark-style directory structure: col1=value1/col2=value2/...
|
|
243
|
+
# Example produced expression (Snowflake SQL):
|
|
244
|
+
# 'department=' || TO_VARCHAR("department") || '/' || 'region=' || TO_VARCHAR("region")
|
|
245
|
+
partitioning_column_names = list(write_op.partitioning_columns)
|
|
246
|
+
partition_expr_parts: list[str] = []
|
|
247
|
+
for col_name in partitioning_column_names:
|
|
248
|
+
quoted = f'"{col_name}"'
|
|
249
|
+
segment = f"'{col_name}=' || COALESCE(TO_VARCHAR({quoted}), '__HIVE_DEFAULT_PARTITION__')"
|
|
250
|
+
partition_expr_parts.append(segment)
|
|
251
|
+
parameters["partition_by"] = " || '/' || ".join(partition_expr_parts)
|
|
252
|
+
# When using PARTITION BY, Snowflake writes into subdirectories under the base path.
|
|
253
|
+
# Download from the base write path to preserve partition directories locally.
|
|
254
|
+
download_stage_path = write_path
|
|
248
255
|
|
|
249
256
|
# If a partition hint is present (from DataFrame.repartition(n)), optionally split the
|
|
250
257
|
# write into n COPY INTO calls by assigning a synthetic partition id. Controlled by config.
|
|
@@ -978,7 +985,47 @@ def store_files_locally(
|
|
|
978
985
|
)
|
|
979
986
|
if overwrite and os.path.isdir(target_path):
|
|
980
987
|
_truncate_directory(real_path)
|
|
981
|
-
|
|
988
|
+
# Per Snowflake docs: "The command does not preserve stage directory structure when transferring files to your client machine"
|
|
989
|
+
# https://docs.snowflake.com/en/sql-reference/sql/get
|
|
990
|
+
# Preserve directory structure under stage_path by listing files and
|
|
991
|
+
# downloading each into its corresponding local subdirectory when partition subdirs exist.
|
|
992
|
+
# Otherwise, fall back to a direct GET which flattens.
|
|
993
|
+
|
|
994
|
+
# TODO(SNOW-2326973): This can be parallelized further. Its not done here because it only affects
|
|
995
|
+
# write to local storage.
|
|
996
|
+
|
|
997
|
+
ls_dataframe = session.sql(f"LS {stage_path}")
|
|
998
|
+
ls_iterator = ls_dataframe.toLocalIterator()
|
|
999
|
+
|
|
1000
|
+
# Build a normalized base prefix from stage_path to compute relatives
|
|
1001
|
+
# Example: stage_path='@MY_STAGE/prefix' -> base_prefix='my_stage/prefix/'
|
|
1002
|
+
base_prefix = stage_path.lstrip("@").rstrip("/") + "/"
|
|
1003
|
+
base_prefix_lower = base_prefix.lower()
|
|
1004
|
+
|
|
1005
|
+
# Group by parent directory under the base prefix, then issue a GET per directory.
|
|
1006
|
+
# This gives a small parallelism advantage if we have many files per partition directory.
|
|
1007
|
+
parent_dirs: set[str] = set()
|
|
1008
|
+
for row in ls_iterator:
|
|
1009
|
+
name: str = row[0]
|
|
1010
|
+
name_lower = name.lower()
|
|
1011
|
+
rel_start = name_lower.find(base_prefix_lower)
|
|
1012
|
+
relative = name[rel_start + len(base_prefix) :] if rel_start != -1 else name
|
|
1013
|
+
parent_dir = os.path.dirname(relative)
|
|
1014
|
+
if parent_dir and parent_dir != ".":
|
|
1015
|
+
parent_dirs.add(parent_dir)
|
|
1016
|
+
|
|
1017
|
+
# If no parent directories were discovered (non-partitioned unload prefix), use direct GET.
|
|
1018
|
+
if not parent_dirs:
|
|
1019
|
+
snowpark.file_operation.FileOperation(session).get(stage_path, str(real_path))
|
|
1020
|
+
return
|
|
1021
|
+
|
|
1022
|
+
file_op = snowpark.file_operation.FileOperation(session)
|
|
1023
|
+
for parent_dir in sorted(parent_dirs):
|
|
1024
|
+
local_dir = real_path / parent_dir
|
|
1025
|
+
os.makedirs(local_dir, exist_ok=True)
|
|
1026
|
+
|
|
1027
|
+
src_dir = f"@{base_prefix}{parent_dir}"
|
|
1028
|
+
file_op.get(src_dir, str(local_dir))
|
|
982
1029
|
|
|
983
1030
|
|
|
984
1031
|
def _truncate_directory(directory_path: Path) -> None:
|
|
@@ -70,6 +70,26 @@ _lca_alias_map: ContextVar[dict[str, TypedColumn]] = ContextVar(
|
|
|
70
70
|
default={},
|
|
71
71
|
)
|
|
72
72
|
|
|
73
|
+
_view_process_context = ContextVar("_view_process_context", default=[])
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@contextmanager
|
|
77
|
+
def push_processed_view(name: str):
|
|
78
|
+
_view_process_context.set(_view_process_context.get() + [name])
|
|
79
|
+
yield
|
|
80
|
+
_view_process_context.set(_view_process_context.get()[:-1])
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def get_processed_views() -> list[str]:
|
|
84
|
+
return _view_process_context.get()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def register_processed_view(name: str) -> None:
|
|
88
|
+
context = _view_process_context.get()
|
|
89
|
+
context.append(name)
|
|
90
|
+
_view_process_context.set(context)
|
|
91
|
+
|
|
92
|
+
|
|
73
93
|
# Context variable to track current grouping columns for grouping_id() function
|
|
74
94
|
_current_grouping_columns: ContextVar[list[str]] = ContextVar(
|
|
75
95
|
"_current_grouping_columns",
|
|
@@ -387,6 +407,7 @@ def clear_context_data() -> None:
|
|
|
387
407
|
_plan_id_map.set({})
|
|
388
408
|
_alias_map.set({})
|
|
389
409
|
|
|
410
|
+
_view_process_context.set([])
|
|
390
411
|
_next_sql_plan_id.set(_STARTING_SQL_PLAN_ID)
|
|
391
412
|
_sql_plan_name_map.set({})
|
|
392
413
|
_map_partitions_stack.set(0)
|
|
@@ -28,12 +28,18 @@ def unquote_spark_identifier_if_quoted(spark_name: str) -> str:
|
|
|
28
28
|
raise AnalysisException(f"Invalid name: {spark_name}")
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def spark_to_sf_single_id_with_unquoting(
|
|
31
|
+
def spark_to_sf_single_id_with_unquoting(
|
|
32
|
+
name: str, use_auto_upper_case: bool = False
|
|
33
|
+
) -> str:
|
|
32
34
|
"""
|
|
33
35
|
Transforms a spark name to a valid snowflake name by quoting and potentially uppercasing it.
|
|
34
36
|
Unquotes the spark name if necessary. Will raise an AnalysisException if given name is not valid.
|
|
35
37
|
"""
|
|
36
|
-
return
|
|
38
|
+
return (
|
|
39
|
+
spark_to_sf_single_id(unquote_spark_identifier_if_quoted(name))
|
|
40
|
+
if use_auto_upper_case
|
|
41
|
+
else quote_name_without_upper_casing(unquote_spark_identifier_if_quoted(name))
|
|
42
|
+
)
|
|
37
43
|
|
|
38
44
|
|
|
39
45
|
def spark_to_sf_single_id(name: str, is_column: bool = False) -> str:
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2012-2025 Snowflake Computing Inc. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Tuple
|
|
6
|
+
|
|
7
|
+
from pyspark.errors import AnalysisException
|
|
8
|
+
|
|
9
|
+
from snowflake.snowpark_connect.dataframe_container import DataFrameContainer
|
|
10
|
+
from snowflake.snowpark_connect.utils.concurrent import SynchronizedDict
|
|
11
|
+
from snowflake.snowpark_connect.utils.context import get_session_id
|
|
12
|
+
|
|
13
|
+
_temp_views = SynchronizedDict[Tuple[str, str], DataFrameContainer]()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def register_temp_view(name: str, df: DataFrameContainer, replace: bool) -> None:
|
|
17
|
+
normalized_name = _normalize(name)
|
|
18
|
+
current_session_id = get_session_id()
|
|
19
|
+
for key in list(_temp_views.keys()):
|
|
20
|
+
if _normalize(key[0]) == normalized_name and key[1] == current_session_id:
|
|
21
|
+
if replace:
|
|
22
|
+
_temp_views.remove(key)
|
|
23
|
+
break
|
|
24
|
+
else:
|
|
25
|
+
raise AnalysisException(
|
|
26
|
+
f"[TEMP_TABLE_OR_VIEW_ALREADY_EXISTS] Cannot create the temporary view `{name}` because it already exists."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
_temp_views[(name, current_session_id)] = df
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def unregister_temp_view(name: str) -> bool:
|
|
33
|
+
normalized_name = _normalize(name)
|
|
34
|
+
|
|
35
|
+
for key in _temp_views.keys():
|
|
36
|
+
normalized_key = _normalize(key[0])
|
|
37
|
+
if normalized_name == normalized_key and key[1] == get_session_id():
|
|
38
|
+
pop_result = _temp_views.remove(key)
|
|
39
|
+
return pop_result is not None
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_temp_view(name: str) -> Optional[DataFrameContainer]:
|
|
44
|
+
normalized_name = _normalize(name)
|
|
45
|
+
for key in _temp_views.keys():
|
|
46
|
+
normalized_key = _normalize(key[0])
|
|
47
|
+
if normalized_name == normalized_key and key[1] == get_session_id():
|
|
48
|
+
return _temp_views.get(key)
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_temp_view_normalized_names() -> list[str]:
|
|
53
|
+
return [
|
|
54
|
+
_normalize(key[0]) for key in _temp_views.keys() if key[1] == get_session_id()
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _normalize(name: str) -> str:
|
|
59
|
+
from snowflake.snowpark_connect.config import global_config
|
|
60
|
+
|
|
61
|
+
return name if global_config.spark_sql_caseSensitive else name.lower()
|
|
@@ -176,14 +176,6 @@ class ProcessCommonInlineUserDefinedFunction:
|
|
|
176
176
|
tar_ref.extractall(archive[: -len(".archive")])
|
|
177
177
|
os.remove(archive)
|
|
178
178
|
|
|
179
|
-
def callable_func(*args, **kwargs):
|
|
180
|
-
import_staged_files()
|
|
181
|
-
return original_callable(*args, **kwargs)
|
|
182
|
-
|
|
183
|
-
callable_func.__signature__ = inspect.signature(original_callable)
|
|
184
|
-
if hasattr(original_callable, "__annotations__"):
|
|
185
|
-
callable_func.__annotations__ = original_callable.__annotations__
|
|
186
|
-
|
|
187
179
|
if self._udf_packages:
|
|
188
180
|
packages = [p.strip() for p in self._udf_packages.strip("[]").split(",")]
|
|
189
181
|
else:
|
|
@@ -193,6 +185,15 @@ class ProcessCommonInlineUserDefinedFunction:
|
|
|
193
185
|
else:
|
|
194
186
|
imports = []
|
|
195
187
|
|
|
188
|
+
def callable_func(*args, **kwargs):
|
|
189
|
+
if imports:
|
|
190
|
+
import_staged_files()
|
|
191
|
+
return original_callable(*args, **kwargs)
|
|
192
|
+
|
|
193
|
+
callable_func.__signature__ = inspect.signature(original_callable)
|
|
194
|
+
if hasattr(original_callable, "__annotations__"):
|
|
195
|
+
callable_func.__annotations__ = original_callable.__annotations__
|
|
196
|
+
|
|
196
197
|
update_none_input_types()
|
|
197
198
|
|
|
198
199
|
needs_struct_conversion = isinstance(self._original_return_type, StructType)
|
|
@@ -38,8 +38,9 @@ def create_udtf(
|
|
|
38
38
|
# We subtract one here since UDTF functions are class methods and always have "self" as the first parameter.
|
|
39
39
|
input_types = [VariantType()] * (len(func_signature.parameters) - 1)
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
if imports:
|
|
42
|
+
# Wrapp callable to allow reading imported files
|
|
43
|
+
callable_func = artifacts_reader_wrapper(callable_func)
|
|
43
44
|
|
|
44
45
|
if is_arrow_enabled:
|
|
45
46
|
callable_func = spark_compatible_udtf_wrapper_with_arrow(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
snowflake/snowpark_connect/__init__.py,sha256=Sml4x1LTNnxZyw6nnDeJrZWUi3eUAR46Rsw6N-wHUSA,605
|
|
2
2
|
snowflake/snowpark_connect/column_name_handler.py,sha256=_Z2j4teR_3nsCLjMxhyChGmds_1v6tP51OfkEpmWXWk,27164
|
|
3
|
-
snowflake/snowpark_connect/config.py,sha256=
|
|
3
|
+
snowflake/snowpark_connect/config.py,sha256=B2TeNqpfFy2ObjuUhgBNOhQ0Knxc7ZYiI7doOSkuSFw,28146
|
|
4
4
|
snowflake/snowpark_connect/constants.py,sha256=FBDxNUxdqWxnf6d5-eanHlYdFFyQqCqvNyZG-uOiO6Q,598
|
|
5
5
|
snowflake/snowpark_connect/control_server.py,sha256=mz3huYo84hgqUB6maZxu3LYyGq7vVL1nv7-7-MjuSYY,1956
|
|
6
6
|
snowflake/snowpark_connect/dataframe_container.py,sha256=0ozyUXrWErzM7Gltnb-i2o5ZyXVVeT_HCqpuYliQXwc,8798
|
|
@@ -13,7 +13,7 @@ snowflake/snowpark_connect/start_server.py,sha256=udegO0rk2FeSnXsIcCIYQW3VRlGDjB
|
|
|
13
13
|
snowflake/snowpark_connect/tcm.py,sha256=ftncZFbVO-uyWMhF1_HYKQykB7KobHEYoyQsYbQj1EM,203
|
|
14
14
|
snowflake/snowpark_connect/type_mapping.py,sha256=6Hg-h1iVzVB_FnwG3Sjl-UGr2Itrs4LxVb2Pz5Ue-YA,41566
|
|
15
15
|
snowflake/snowpark_connect/typed_column.py,sha256=Tavii8b4zMj5IWOvN6tlOVmC80W6eQct0pC_tF2nlhU,3867
|
|
16
|
-
snowflake/snowpark_connect/version.py,sha256=
|
|
16
|
+
snowflake/snowpark_connect/version.py,sha256=174Rf6GxIIk1u2aUG821BNtN-OKC59qCc6Nc3xldSWw,118
|
|
17
17
|
snowflake/snowpark_connect/analyze_plan/__init__.py,sha256=xsIE96jDASko3F-MeNf4T4Gg5ufthS8CejeiJDfri0M,76
|
|
18
18
|
snowflake/snowpark_connect/analyze_plan/map_tree_string.py,sha256=Q3ZD-Z7uForrF7W3mSAjwaiEcIv2KDXr5jPfVbromVg,1470
|
|
19
19
|
snowflake/snowpark_connect/error/__init__.py,sha256=oQo6k4zztLmNF1c5IvJLcS99J6RWY9KBTN3RJ2pKimg,249
|
|
@@ -21,7 +21,7 @@ snowflake/snowpark_connect/error/error_mapping.py,sha256=vdnLOU1Sqpocpu_uCXjfhiv
|
|
|
21
21
|
snowflake/snowpark_connect/error/error_utils.py,sha256=Sb_p5dsrFZsLrR9B_Tp8d80Z6KcPtz9OM40lNbgsgRI,14863
|
|
22
22
|
snowflake/snowpark_connect/error/exceptions.py,sha256=EOnTDiJZuVJ9dNBzy5cK0OBprbYCD3gWTCCLITjd1mY,677
|
|
23
23
|
snowflake/snowpark_connect/execute_plan/__init__.py,sha256=xsIE96jDASko3F-MeNf4T4Gg5ufthS8CejeiJDfri0M,76
|
|
24
|
-
snowflake/snowpark_connect/execute_plan/map_execution_command.py,sha256=
|
|
24
|
+
snowflake/snowpark_connect/execute_plan/map_execution_command.py,sha256=j3XjBAwqOHY3D2cwu7Ez2jSZq57UUbal-1ynwXnrOHM,9355
|
|
25
25
|
snowflake/snowpark_connect/execute_plan/map_execution_root.py,sha256=wFxeSxtuQY1OmI_BRLOzudrGwkJsmbBUOFQfsxxvRrU,7461
|
|
26
26
|
snowflake/snowpark_connect/execute_plan/utils.py,sha256=OsjEd-WnQEX2oNVvlzGR6rpJVYyfxx1LACP09k1Y4lk,7830
|
|
27
27
|
snowflake/snowpark_connect/expression/__init__.py,sha256=xsIE96jDASko3F-MeNf4T4Gg5ufthS8CejeiJDfri0M,76
|
|
@@ -408,7 +408,7 @@ snowflake/snowpark_connect/relation/map_relation.py,sha256=etQ_SGXAAXNZxqfG6ouh6
|
|
|
408
408
|
snowflake/snowpark_connect/relation/map_row_ops.py,sha256=x1Jqircy4I0iiSljx3zbq0YxwGvGzPcXIY8_nhtl2PM,30528
|
|
409
409
|
snowflake/snowpark_connect/relation/map_sample_by.py,sha256=8ALQbeUsB89sI3uiUFqG3w1A4TtOzOAL4umdKp6-c38,1530
|
|
410
410
|
snowflake/snowpark_connect/relation/map_show_string.py,sha256=GgKg0qp1pGqSC7TuFedTU4IYaIm-Fx23OJ1LfkcGOHw,3382
|
|
411
|
-
snowflake/snowpark_connect/relation/map_sql.py,sha256=
|
|
411
|
+
snowflake/snowpark_connect/relation/map_sql.py,sha256=7LeobApI0Pfvb1D2rSJYFMjyD6PMJ8Ig4Frcxv_k0Bc,104069
|
|
412
412
|
snowflake/snowpark_connect/relation/map_stats.py,sha256=kqRYvix8RfluTKx1cAy9JhBUv6arYQHgfxpP1R4QwBM,13985
|
|
413
413
|
snowflake/snowpark_connect/relation/map_subquery_alias.py,sha256=rHgE9XUzuWWkjNPtJz3Sxzz2aFo690paHKZh9frqPXk,1456
|
|
414
414
|
snowflake/snowpark_connect/relation/map_udtf.py,sha256=cfDnbZ3TRJ6eb0EVResu6GL-OwQpaEabWLbrhgWnkRw,13316
|
|
@@ -416,7 +416,7 @@ snowflake/snowpark_connect/relation/stage_locator.py,sha256=c30Z4N_xFavaL5XhIl9s
|
|
|
416
416
|
snowflake/snowpark_connect/relation/utils.py,sha256=AhE58g0Zz2DWY9gW4JnB_iBU-r4RMnWCj4okQdHSz_4,8398
|
|
417
417
|
snowflake/snowpark_connect/relation/catalogs/__init__.py,sha256=0yJ5Nfg7SIxudI0P7_U5EWPyiTpkMet8tSq-IwutSZo,265
|
|
418
418
|
snowflake/snowpark_connect/relation/catalogs/abstract_spark_catalog.py,sha256=wujeuKotJZRzpPp_WqVB9TkfprFU2tIXTQJk_eFOR0o,9826
|
|
419
|
-
snowflake/snowpark_connect/relation/catalogs/snowflake_catalog.py,sha256=
|
|
419
|
+
snowflake/snowpark_connect/relation/catalogs/snowflake_catalog.py,sha256=e9rXL0DOAf4VBZfa-idqehLAEZoCgIoE4IDiLVvR4dQ,28475
|
|
420
420
|
snowflake/snowpark_connect/relation/catalogs/utils.py,sha256=AgiBkK9Xwm9ZyjsKZYK9eTb4YZ9C5dAimD9DLlft-tY,1753
|
|
421
421
|
snowflake/snowpark_connect/relation/read/__init__.py,sha256=5J3IOTKu4Qmenouz1Oz_bUu_4c6KpxtaC63mPUGLyeY,132
|
|
422
422
|
snowflake/snowpark_connect/relation/read/jdbc_read_dbapi.py,sha256=nwvBYwdDTRQc8ljt4CLLaVl5M2uei1TDICJkz7CDSG8,24608
|
|
@@ -426,24 +426,24 @@ snowflake/snowpark_connect/relation/read/map_read_jdbc.py,sha256=rfuf2Gp89v-9YFh
|
|
|
426
426
|
snowflake/snowpark_connect/relation/read/map_read_json.py,sha256=z6xdXNCFOfRbZ-6J4QQ2wB7ad6vEm8S1H7T3Xx5CBck,12371
|
|
427
427
|
snowflake/snowpark_connect/relation/read/map_read_parquet.py,sha256=VIUR5GWP6DBaYCDUsrDJzHQOM3y4hIbtWiCx0f4mczs,7168
|
|
428
428
|
snowflake/snowpark_connect/relation/read/map_read_socket.py,sha256=yg7aO7jXMOyLQ9k3-abywNN2VAgvtvix2V8LsR_XQO4,2267
|
|
429
|
-
snowflake/snowpark_connect/relation/read/map_read_table.py,sha256=
|
|
429
|
+
snowflake/snowpark_connect/relation/read/map_read_table.py,sha256=mlbC4mbsV4_mHxYpxkOJ8nr8aNwEh2xBzfzC07XXaCQ,6607
|
|
430
430
|
snowflake/snowpark_connect/relation/read/map_read_text.py,sha256=Vcm6EY3gyRPucdAXcXcu2mREcgq4N_4h3NDKNPhqsHY,3434
|
|
431
431
|
snowflake/snowpark_connect/relation/read/reader_config.py,sha256=PMh1R5IjqqTwiAAqvDRhnTx8Jxnhq8wVCmpZRqqKD3E,16437
|
|
432
432
|
snowflake/snowpark_connect/relation/read/utils.py,sha256=rIIM6d2WXHh7MLGyHNiRc9tS8b0dmyFQr7rHepIYJOU,4111
|
|
433
433
|
snowflake/snowpark_connect/relation/write/__init__.py,sha256=xsIE96jDASko3F-MeNf4T4Gg5ufthS8CejeiJDfri0M,76
|
|
434
434
|
snowflake/snowpark_connect/relation/write/jdbc_write_dbapi.py,sha256=GI9FyGZuQQNV-6Q8Ob-Xr0im3iAPdH-Jkyx8bjwbOuE,11931
|
|
435
|
-
snowflake/snowpark_connect/relation/write/map_write.py,sha256=
|
|
435
|
+
snowflake/snowpark_connect/relation/write/map_write.py,sha256=swxSLYq4P1B3Yj7Spruw-vaBE1z-Q-er4iScuPqVyxc,45606
|
|
436
436
|
snowflake/snowpark_connect/relation/write/map_write_jdbc.py,sha256=1nOWRgjtZzfRwnSRGFP9V6mqBVlGhSBr2KHGHbe4JMU,1404
|
|
437
437
|
snowflake/snowpark_connect/resources/java_udfs-1.0-SNAPSHOT.jar,sha256=tVyOp6tXxu9nm6SDufwQiGzfH3pnuh_7PowsMZxOolY,9773
|
|
438
438
|
snowflake/snowpark_connect/utils/__init__.py,sha256=xsIE96jDASko3F-MeNf4T4Gg5ufthS8CejeiJDfri0M,76
|
|
439
439
|
snowflake/snowpark_connect/utils/artifacts.py,sha256=TkHZ2uNfZiphgtG91V1_c_h9yP9dP677BXUMymboCss,2498
|
|
440
440
|
snowflake/snowpark_connect/utils/cache.py,sha256=bAyoNBW6Z1ui9BuppDywbQeG6fdju4L-owFHzySOTnk,3382
|
|
441
441
|
snowflake/snowpark_connect/utils/concurrent.py,sha256=BTbUmvupLzUSRd6L7kKk9yIXFdqlDOkXebVMaECRD-A,3653
|
|
442
|
-
snowflake/snowpark_connect/utils/context.py,sha256=
|
|
442
|
+
snowflake/snowpark_connect/utils/context.py,sha256=yJ0pun5I69xu3__s39XX5LdQjqIEezFCgo1A-FhiWJ8,13829
|
|
443
443
|
snowflake/snowpark_connect/utils/describe_query_cache.py,sha256=HByBsP-XJ1PAk414OQtwIqcFnqpC2zq-ZK_YxR4O5gg,9100
|
|
444
444
|
snowflake/snowpark_connect/utils/env_utils.py,sha256=g__Uio5ae20Tm1evahIHdJUXQYPmjNUT_kYPSIy5JDU,1488
|
|
445
445
|
snowflake/snowpark_connect/utils/external_udxf_cache.py,sha256=eSZHMbjTxnkg78IlbG5P1Vno6j5ag_FSI0c4Xi2UyPs,1044
|
|
446
|
-
snowflake/snowpark_connect/utils/identifiers.py,sha256=
|
|
446
|
+
snowflake/snowpark_connect/utils/identifiers.py,sha256=yHsOklj6SM2re5auhSSMLPSvCYhpR8Cgy-Kgevf8gDc,7810
|
|
447
447
|
snowflake/snowpark_connect/utils/interrupt.py,sha256=_awhdrzF1KQO-EQThneEcfMg3Zxed4p3HtMpkcAb6ek,2790
|
|
448
448
|
snowflake/snowpark_connect/utils/io_utils.py,sha256=xu0AMrHy-qsY7TfdIxzWChf0hU_7bnvm3Ruk0XScRns,1781
|
|
449
449
|
snowflake/snowpark_connect/utils/pandas_udtf_utils.py,sha256=3WA_9IVRZL8fnwIHo048LTg62-bPGfCDUZzYd-zjzQQ,7564
|
|
@@ -452,11 +452,12 @@ snowflake/snowpark_connect/utils/scala_udf_utils.py,sha256=RFDDMmgQ_xBWk98kdfWaw
|
|
|
452
452
|
snowflake/snowpark_connect/utils/session.py,sha256=BWwpIbhdplQOhvY4GpbLgZ-BrJLidrWR6A_cJmGFCq8,7372
|
|
453
453
|
snowflake/snowpark_connect/utils/snowpark_connect_logging.py,sha256=23bvbALGqixJ3Ap9QWM3OpcKNK-sog2mr9liSmvwqYU,1123
|
|
454
454
|
snowflake/snowpark_connect/utils/telemetry.py,sha256=4TImrE4XY5rfiesHyPQQ0xtU63qnN1wUpDXUP12PAvA,22259
|
|
455
|
+
snowflake/snowpark_connect/utils/temporary_view_cache.py,sha256=M1czwb-uv0nP9w1meB72NR7cj0iryJc_OYFPztx5XuU,2080
|
|
455
456
|
snowflake/snowpark_connect/utils/udf_cache.py,sha256=8K7kASEhvpnp-l1hjzovjyboUzKctDq7PiGXRcNv6Lg,12125
|
|
456
457
|
snowflake/snowpark_connect/utils/udf_helper.py,sha256=g-TxTs4ARyJWYgADrosfQQG-ykBBQdm1g5opslxJq_E,12563
|
|
457
|
-
snowflake/snowpark_connect/utils/udf_utils.py,sha256=
|
|
458
|
+
snowflake/snowpark_connect/utils/udf_utils.py,sha256=aQX58P2f9WuJDAuoT5Ag5MNTZVxcx4NIZg_UJhcfW7k,12037
|
|
458
459
|
snowflake/snowpark_connect/utils/udtf_helper.py,sha256=9B_1iOckfFXQfVv-UHerIJ32fDd4qucKaHGqxtBEi4w,14969
|
|
459
|
-
snowflake/snowpark_connect/utils/udtf_utils.py,sha256=
|
|
460
|
+
snowflake/snowpark_connect/utils/udtf_utils.py,sha256=7pYIOHUn7BLzQPqOqGaBOlV4fOnVIrWpd61quYuZ9PI,33134
|
|
460
461
|
snowflake/snowpark_connect/utils/udxf_import_utils.py,sha256=pPtcaGsyh0tUdy0aAvNqTj04jqPKlEcGmvaZDP9O8Gc,536
|
|
461
462
|
snowflake/snowpark_connect/utils/xxhash64.py,sha256=ysJRxhBPf25LeNhM1RK_H36MWl6q6C6vBRHa-jIna_A,7477
|
|
462
463
|
snowflake/snowpark_decoder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -464,17 +465,17 @@ snowflake/snowpark_decoder/dp_session.py,sha256=HIr3TfKgYl5zqaGR5xpFU9ZVkcaTB9I8
|
|
|
464
465
|
snowflake/snowpark_decoder/spark_decoder.py,sha256=EQiCvBiqB736Bc17o3gnYGtcYVcyfGxroO5e1kbe1Co,2885
|
|
465
466
|
snowflake/snowpark_decoder/_internal/proto/generated/DataframeProcessorMsg_pb2.py,sha256=2eSDqeyfMvmIJ6_rF663DrEe1dg_anrP4OpVJNTJHaQ,2598
|
|
466
467
|
snowflake/snowpark_decoder/_internal/proto/generated/DataframeProcessorMsg_pb2.pyi,sha256=aIH23k52bXdw5vO3RtM5UcOjDPaWsJFx1SRUSk3qOK8,6142
|
|
467
|
-
snowpark_connect-0.28.
|
|
468
|
-
snowpark_connect-0.28.
|
|
469
|
-
snowpark_connect-0.28.
|
|
470
|
-
snowpark_connect-0.28.
|
|
471
|
-
snowpark_connect-0.28.
|
|
472
|
-
snowpark_connect-0.28.
|
|
468
|
+
snowpark_connect-0.28.1.data/scripts/snowpark-connect,sha256=yZ94KqbWACxnwV8mpg8NjILvvRNjnF8B3cs3ZFNuIM4,1546
|
|
469
|
+
snowpark_connect-0.28.1.data/scripts/snowpark-session,sha256=NMAHSonTo-nmOZSkQNlszUC0jLJ8QWEDUsUmMe2UAOw,190
|
|
470
|
+
snowpark_connect-0.28.1.data/scripts/snowpark-submit,sha256=Zd98H9W_d0dIqMSkQLdHyW5G3myxF0t4c3vNBt2nD6A,12056
|
|
471
|
+
snowpark_connect-0.28.1.dist-info/licenses/LICENSE-binary,sha256=fmBlX39HwTlBUyiKEznaLZGuxQy-7ndLLG_rTXjF02Y,22916
|
|
472
|
+
snowpark_connect-0.28.1.dist-info/licenses/LICENSE.txt,sha256=Ff9cPv4xu0z7bnMTHzo4vDncOShsy33w4oJMA2xjn6c,11365
|
|
473
|
+
snowpark_connect-0.28.1.dist-info/licenses/NOTICE-binary,sha256=elMF8brgGNJwOz8YdorzBF6-U8ZhR8F-77FfGkZng7U,57843
|
|
473
474
|
spark/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
474
475
|
spark/connect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
475
476
|
spark/connect/envelope_pb2.py,sha256=7Gc6OUA3vaCuTCIKamb_Iiw7W9jPTcWNEv1im20eWHM,2726
|
|
476
477
|
spark/connect/envelope_pb2.pyi,sha256=VXTJSPpcxzB_dWqVdvPY4KkPhJfh0WmkX7SNHWoLhx0,3358
|
|
477
|
-
snowpark_connect-0.28.
|
|
478
|
-
snowpark_connect-0.28.
|
|
479
|
-
snowpark_connect-0.28.
|
|
480
|
-
snowpark_connect-0.28.
|
|
478
|
+
snowpark_connect-0.28.1.dist-info/METADATA,sha256=VYJ19DYnoBCToiaU__mf5hnm3hBrzN0bBv89QDVre8E,1594
|
|
479
|
+
snowpark_connect-0.28.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
480
|
+
snowpark_connect-0.28.1.dist-info/top_level.txt,sha256=ExnWqVpoTHRG99fu_AxXZVOz8c-De7nNu0yFCGylM8I,16
|
|
481
|
+
snowpark_connect-0.28.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{snowpark_connect-0.28.0.dist-info → snowpark_connect-0.28.1.dist-info}/licenses/LICENSE-binary
RENAMED
|
File without changes
|
{snowpark_connect-0.28.0.dist-info → snowpark_connect-0.28.1.dist-info}/licenses/LICENSE.txt
RENAMED
|
File without changes
|
{snowpark_connect-0.28.0.dist-info → snowpark_connect-0.28.1.dist-info}/licenses/NOTICE-binary
RENAMED
|
File without changes
|
|
File without changes
|