snowpark-connect 0.27.0__py3-none-any.whl → 1.7.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.
- snowflake/snowpark_connect/__init__.py +1 -0
- snowflake/snowpark_connect/analyze_plan/map_tree_string.py +8 -4
- snowflake/snowpark_connect/client/__init__.py +15 -0
- snowflake/snowpark_connect/client/error_utils.py +30 -0
- snowflake/snowpark_connect/client/exceptions.py +36 -0
- snowflake/snowpark_connect/client/query_results.py +90 -0
- snowflake/snowpark_connect/client/server.py +717 -0
- snowflake/snowpark_connect/client/utils/__init__.py +10 -0
- snowflake/snowpark_connect/client/utils/session.py +85 -0
- snowflake/snowpark_connect/column_name_handler.py +404 -243
- snowflake/snowpark_connect/column_qualifier.py +43 -0
- snowflake/snowpark_connect/config.py +309 -26
- snowflake/snowpark_connect/constants.py +2 -0
- snowflake/snowpark_connect/dataframe_container.py +102 -8
- snowflake/snowpark_connect/date_time_format_mapping.py +71 -13
- snowflake/snowpark_connect/error/error_codes.py +50 -0
- snowflake/snowpark_connect/error/error_utils.py +172 -23
- snowflake/snowpark_connect/error/exceptions.py +13 -4
- snowflake/snowpark_connect/execute_plan/map_execution_command.py +15 -160
- snowflake/snowpark_connect/execute_plan/map_execution_root.py +26 -20
- snowflake/snowpark_connect/execute_plan/utils.py +5 -1
- snowflake/snowpark_connect/expression/error_utils.py +28 -0
- snowflake/snowpark_connect/expression/function_defaults.py +9 -2
- snowflake/snowpark_connect/expression/hybrid_column_map.py +53 -5
- snowflake/snowpark_connect/expression/integral_types_support.py +219 -0
- snowflake/snowpark_connect/expression/literal.py +37 -13
- snowflake/snowpark_connect/expression/map_cast.py +224 -15
- snowflake/snowpark_connect/expression/map_expression.py +80 -27
- snowflake/snowpark_connect/expression/map_extension.py +322 -12
- snowflake/snowpark_connect/expression/map_sql_expression.py +316 -81
- snowflake/snowpark_connect/expression/map_udf.py +86 -20
- snowflake/snowpark_connect/expression/map_unresolved_attribute.py +451 -173
- snowflake/snowpark_connect/expression/map_unresolved_function.py +2964 -829
- snowflake/snowpark_connect/expression/map_unresolved_star.py +87 -23
- snowflake/snowpark_connect/expression/map_update_fields.py +70 -18
- snowflake/snowpark_connect/expression/map_window_function.py +18 -3
- snowflake/snowpark_connect/includes/jars/json4s-ast_2.13-3.7.0-M11.jar +0 -0
- snowflake/snowpark_connect/includes/jars/{scala-library-2.12.18.jar → sas-scala-udf_2.12-0.2.0.jar} +0 -0
- snowflake/snowpark_connect/includes/jars/sas-scala-udf_2.13-0.2.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/scala-reflect-2.13.16.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-common-utils_2.13-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/{spark-connect-client-jvm_2.12-3.5.6.jar → spark-connect-client-jvm_2.13-3.5.6.jar} +0 -0
- snowflake/snowpark_connect/includes/jars/{spark-sql_2.12-3.5.6.jar → spark-sql_2.13-3.5.6.jar} +0 -0
- snowflake/snowpark_connect/includes/python/pyspark/sql/connect/streaming/worker/foreach_batch_worker.py +1 -1
- snowflake/snowpark_connect/includes/python/pyspark/sql/connect/streaming/worker/listener_worker.py +1 -1
- snowflake/snowpark_connect/proto/snowflake_expression_ext_pb2.py +12 -10
- snowflake/snowpark_connect/proto/snowflake_expression_ext_pb2.pyi +14 -2
- snowflake/snowpark_connect/proto/snowflake_relation_ext_pb2.py +10 -8
- snowflake/snowpark_connect/proto/snowflake_relation_ext_pb2.pyi +13 -6
- snowflake/snowpark_connect/relation/catalogs/abstract_spark_catalog.py +65 -17
- snowflake/snowpark_connect/relation/catalogs/snowflake_catalog.py +297 -49
- snowflake/snowpark_connect/relation/catalogs/utils.py +12 -4
- snowflake/snowpark_connect/relation/io_utils.py +110 -10
- snowflake/snowpark_connect/relation/map_aggregate.py +239 -256
- snowflake/snowpark_connect/relation/map_catalog.py +5 -1
- snowflake/snowpark_connect/relation/map_column_ops.py +264 -96
- snowflake/snowpark_connect/relation/map_extension.py +263 -29
- snowflake/snowpark_connect/relation/map_join.py +683 -442
- snowflake/snowpark_connect/relation/map_local_relation.py +28 -1
- snowflake/snowpark_connect/relation/map_map_partitions.py +83 -8
- snowflake/snowpark_connect/relation/map_relation.py +48 -19
- snowflake/snowpark_connect/relation/map_row_ops.py +310 -91
- snowflake/snowpark_connect/relation/map_show_string.py +13 -6
- snowflake/snowpark_connect/relation/map_sql.py +1233 -222
- snowflake/snowpark_connect/relation/map_stats.py +48 -9
- snowflake/snowpark_connect/relation/map_subquery_alias.py +11 -2
- snowflake/snowpark_connect/relation/map_udtf.py +14 -4
- snowflake/snowpark_connect/relation/read/jdbc_read_dbapi.py +53 -14
- snowflake/snowpark_connect/relation/read/map_read.py +134 -43
- snowflake/snowpark_connect/relation/read/map_read_csv.py +326 -47
- snowflake/snowpark_connect/relation/read/map_read_jdbc.py +21 -6
- snowflake/snowpark_connect/relation/read/map_read_json.py +324 -86
- snowflake/snowpark_connect/relation/read/map_read_parquet.py +146 -28
- snowflake/snowpark_connect/relation/read/map_read_partitioned_parquet.py +142 -0
- snowflake/snowpark_connect/relation/read/map_read_socket.py +15 -3
- snowflake/snowpark_connect/relation/read/map_read_table.py +86 -6
- snowflake/snowpark_connect/relation/read/map_read_text.py +22 -4
- snowflake/snowpark_connect/relation/read/metadata_utils.py +170 -0
- snowflake/snowpark_connect/relation/read/reader_config.py +42 -3
- snowflake/snowpark_connect/relation/read/utils.py +50 -5
- snowflake/snowpark_connect/relation/stage_locator.py +91 -55
- snowflake/snowpark_connect/relation/utils.py +128 -5
- snowflake/snowpark_connect/relation/write/jdbc_write_dbapi.py +19 -3
- snowflake/snowpark_connect/relation/write/map_write.py +929 -319
- snowflake/snowpark_connect/relation/write/map_write_jdbc.py +8 -2
- snowflake/snowpark_connect/resources/java_udfs-1.0-SNAPSHOT.jar +0 -0
- snowflake/snowpark_connect/resources_initializer.py +171 -48
- snowflake/snowpark_connect/server.py +528 -473
- snowflake/snowpark_connect/server_common/__init__.py +503 -0
- snowflake/snowpark_connect/snowflake_session.py +65 -0
- snowflake/snowpark_connect/start_server.py +53 -5
- snowflake/snowpark_connect/type_mapping.py +349 -27
- snowflake/snowpark_connect/type_support.py +130 -0
- snowflake/snowpark_connect/typed_column.py +9 -7
- snowflake/snowpark_connect/utils/artifacts.py +9 -8
- snowflake/snowpark_connect/utils/cache.py +49 -27
- snowflake/snowpark_connect/utils/concurrent.py +36 -1
- snowflake/snowpark_connect/utils/context.py +195 -37
- snowflake/snowpark_connect/utils/describe_query_cache.py +68 -53
- snowflake/snowpark_connect/utils/env_utils.py +5 -1
- snowflake/snowpark_connect/utils/expression_transformer.py +172 -0
- snowflake/snowpark_connect/utils/identifiers.py +137 -3
- snowflake/snowpark_connect/utils/io_utils.py +57 -1
- snowflake/snowpark_connect/utils/java_stored_procedure.py +151 -0
- snowflake/snowpark_connect/utils/java_udaf_utils.py +321 -0
- snowflake/snowpark_connect/utils/java_udtf_utils.py +239 -0
- snowflake/snowpark_connect/utils/jvm_udf_utils.py +281 -0
- snowflake/snowpark_connect/utils/open_telemetry.py +516 -0
- snowflake/snowpark_connect/utils/pandas_udtf_utils.py +8 -4
- snowflake/snowpark_connect/utils/patch_spark_line_number.py +181 -0
- snowflake/snowpark_connect/utils/profiling.py +25 -8
- snowflake/snowpark_connect/utils/scala_udf_utils.py +185 -340
- snowflake/snowpark_connect/utils/sequence.py +21 -0
- snowflake/snowpark_connect/utils/session.py +64 -28
- snowflake/snowpark_connect/utils/snowpark_connect_logging.py +51 -9
- snowflake/snowpark_connect/utils/spcs_logger.py +290 -0
- snowflake/snowpark_connect/utils/telemetry.py +192 -40
- snowflake/snowpark_connect/utils/temporary_view_cache.py +67 -0
- snowflake/snowpark_connect/utils/temporary_view_helper.py +334 -0
- snowflake/snowpark_connect/utils/udf_cache.py +117 -41
- snowflake/snowpark_connect/utils/udf_helper.py +39 -37
- snowflake/snowpark_connect/utils/udf_utils.py +133 -14
- snowflake/snowpark_connect/utils/udtf_helper.py +8 -1
- snowflake/snowpark_connect/utils/udtf_utils.py +46 -31
- snowflake/snowpark_connect/utils/udxf_import_utils.py +9 -2
- snowflake/snowpark_connect/utils/upload_java_jar.py +57 -0
- snowflake/snowpark_connect/version.py +1 -1
- snowflake/snowpark_decoder/dp_session.py +6 -2
- snowflake/snowpark_decoder/spark_decoder.py +12 -0
- {snowpark_connect-0.27.0.data → snowpark_connect-1.7.0.data}/scripts/snowpark-submit +14 -4
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/METADATA +16 -7
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/RECORD +139 -168
- snowflake/snowpark_connect/hidden_column.py +0 -39
- snowflake/snowpark_connect/includes/jars/antlr4-runtime-4.9.3.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-cli-1.5.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-codec-1.16.1.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-collections-3.2.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-collections4-4.4.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-compiler-3.1.9.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-compress-1.26.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-crypto-1.1.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-dbcp-1.4.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-io-2.16.1.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-lang-2.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-lang3-3.12.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-logging-1.1.3.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-math3-3.6.1.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-pool-1.5.4.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-text-1.10.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/hadoop-client-api-trimmed-3.3.4.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-annotations-2.15.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-core-2.15.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-core-asl-1.9.13.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-databind-2.15.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-dataformat-yaml-2.15.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-datatype-jsr310-2.15.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-module-scala_2.12-2.15.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/json4s-ast_2.12-3.7.0-M11.jar +0 -0
- snowflake/snowpark_connect/includes/jars/json4s-core_2.12-3.7.0-M11.jar +0 -0
- snowflake/snowpark_connect/includes/jars/json4s-jackson_2.12-3.7.0-M11.jar +0 -0
- snowflake/snowpark_connect/includes/jars/json4s-native_2.12-3.7.0-M11.jar +0 -0
- snowflake/snowpark_connect/includes/jars/json4s-scalap_2.12-3.7.0-M11.jar +0 -0
- snowflake/snowpark_connect/includes/jars/kryo-shaded-4.0.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/log4j-1.2-api-2.20.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/log4j-api-2.20.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/log4j-core-2.20.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/log4j-slf4j2-impl-2.20.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/paranamer-2.8.3.jar +0 -0
- snowflake/snowpark_connect/includes/jars/paranamer-2.8.jar +0 -0
- snowflake/snowpark_connect/includes/jars/sas-scala-udf_2.12-0.1.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/scala-collection-compat_2.12-2.7.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/scala-parser-combinators_2.12-2.3.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/scala-reflect-2.12.18.jar +0 -0
- snowflake/snowpark_connect/includes/jars/scala-xml_2.12-2.1.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/slf4j-api-2.0.7.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-catalyst_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-common-utils_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-core_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-graphx_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-hive-thriftserver_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-hive_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-kvstore_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-launcher_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-mesos_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-mllib-local_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-network-common_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-network-shuffle_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-repl_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-sketch_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-sql-api_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-tags_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-unsafe_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-yarn_2.12-3.5.6.jar +0 -0
- {snowpark_connect-0.27.0.data → snowpark_connect-1.7.0.data}/scripts/snowpark-connect +0 -0
- {snowpark_connect-0.27.0.data → snowpark_connect-1.7.0.data}/scripts/snowpark-session +0 -0
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/WHEEL +0 -0
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/licenses/LICENSE-binary +0 -0
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/licenses/LICENSE.txt +0 -0
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/licenses/NOTICE-binary +0 -0
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/top_level.txt +0 -0
|
@@ -6,18 +6,51 @@ import pyspark.sql.connect.proto.expressions_pb2 as expressions_proto
|
|
|
6
6
|
|
|
7
7
|
import snowflake.snowpark.functions as snowpark_fn
|
|
8
8
|
import snowflake.snowpark_connect.proto.snowflake_expression_ext_pb2 as snowflake_proto
|
|
9
|
-
from snowflake.snowpark.types import
|
|
9
|
+
from snowflake.snowpark.types import (
|
|
10
|
+
BooleanType,
|
|
11
|
+
DayTimeIntervalType,
|
|
12
|
+
StringType,
|
|
13
|
+
YearMonthIntervalType,
|
|
14
|
+
)
|
|
10
15
|
from snowflake.snowpark_connect.column_name_handler import ColumnNameMap
|
|
16
|
+
from snowflake.snowpark_connect.error.error_codes import ErrorCodes
|
|
17
|
+
from snowflake.snowpark_connect.error.error_utils import attach_custom_error_code
|
|
11
18
|
from snowflake.snowpark_connect.expression.typer import ExpressionTyper
|
|
12
19
|
from snowflake.snowpark_connect.typed_column import TypedColumn
|
|
13
20
|
from snowflake.snowpark_connect.utils.context import (
|
|
21
|
+
get_captured_attribute_names,
|
|
14
22
|
push_evaluating_sql_scope,
|
|
15
23
|
push_outer_dataframe,
|
|
24
|
+
resolving_subquery_exp,
|
|
16
25
|
)
|
|
17
26
|
from snowflake.snowpark_connect.utils.telemetry import (
|
|
18
27
|
SnowparkConnectNotImplementedError,
|
|
19
28
|
)
|
|
20
29
|
|
|
30
|
+
# Formatting constants for interval display
|
|
31
|
+
_TWO_DIGIT_FORMAT = (
|
|
32
|
+
"{:02d}" # Zero-padded 2-digit format for hours, minutes, and whole seconds
|
|
33
|
+
)
|
|
34
|
+
_THREE_DIGIT_FORMAT = (
|
|
35
|
+
"{:03d}" # Zero-padded 3-digit format for hours/minutes and whole seconds
|
|
36
|
+
)
|
|
37
|
+
_SECONDS_PRECISION_FORMAT = (
|
|
38
|
+
"{:09.6f}" # 6 decimal places with leading zeros for seconds
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _format_time_component(value: int, is_negative: bool = False) -> str:
|
|
43
|
+
"""Format a time component (hours, minutes, whole seconds) with proper zero-padding."""
|
|
44
|
+
if is_negative:
|
|
45
|
+
return _THREE_DIGIT_FORMAT.format(value)
|
|
46
|
+
else:
|
|
47
|
+
return _TWO_DIGIT_FORMAT.format(value)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _format_seconds_precise(seconds: float) -> str:
|
|
51
|
+
"""Format seconds with full precision, stripping trailing zeros."""
|
|
52
|
+
return _SECONDS_PRECISION_FORMAT.format(seconds).rstrip("0").rstrip(".")
|
|
53
|
+
|
|
21
54
|
|
|
22
55
|
def map_extension(
|
|
23
56
|
exp: expressions_proto.Expression,
|
|
@@ -49,11 +82,63 @@ def map_extension(
|
|
|
49
82
|
elif value.HasField("unresolved_attribute"):
|
|
50
83
|
name = "__" + key + "__" + exp_name[0]
|
|
51
84
|
else:
|
|
52
|
-
|
|
85
|
+
exception = SnowparkConnectNotImplementedError(
|
|
53
86
|
"Named argument not supported yet for this input."
|
|
54
87
|
)
|
|
88
|
+
attach_custom_error_code(exception, ErrorCodes.UNSUPPORTED_OPERATION)
|
|
89
|
+
raise exception
|
|
55
90
|
return [name], typed_col
|
|
56
91
|
|
|
92
|
+
case "interval_literal":
|
|
93
|
+
interval_ext = extension.interval_literal
|
|
94
|
+
literal = interval_ext.literal
|
|
95
|
+
start_field = (
|
|
96
|
+
interval_ext.start_field
|
|
97
|
+
if interval_ext.HasField("start_field")
|
|
98
|
+
else None
|
|
99
|
+
)
|
|
100
|
+
end_field = (
|
|
101
|
+
interval_ext.end_field if interval_ext.HasField("end_field") else None
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Format interval with proper context-aware formatting
|
|
105
|
+
if literal.HasField("year_month_interval"):
|
|
106
|
+
total_months = literal.year_month_interval
|
|
107
|
+
lit_value, lit_name = _format_year_month_interval(
|
|
108
|
+
total_months, start_field, end_field
|
|
109
|
+
)
|
|
110
|
+
if start_field is not None and end_field is not None:
|
|
111
|
+
interval_data_type = YearMonthIntervalType(start_field, end_field)
|
|
112
|
+
else:
|
|
113
|
+
interval_data_type = YearMonthIntervalType()
|
|
114
|
+
|
|
115
|
+
# Create column using SQL expression with context-aware formatting
|
|
116
|
+
col = snowpark_fn.sql_expr(lit_value)
|
|
117
|
+
|
|
118
|
+
elif literal.HasField("day_time_interval"):
|
|
119
|
+
total_microseconds = literal.day_time_interval
|
|
120
|
+
lit_value, lit_name = _format_day_time_interval(
|
|
121
|
+
total_microseconds, start_field, end_field
|
|
122
|
+
)
|
|
123
|
+
if start_field is not None and end_field is not None:
|
|
124
|
+
interval_data_type = DayTimeIntervalType(start_field, end_field)
|
|
125
|
+
else:
|
|
126
|
+
interval_data_type = DayTimeIntervalType()
|
|
127
|
+
|
|
128
|
+
# Create column using SQL expression to get proper interval type (same as year-month)
|
|
129
|
+
col = snowpark_fn.sql_expr(lit_value)
|
|
130
|
+
|
|
131
|
+
else:
|
|
132
|
+
# Fallback - shouldn't happen
|
|
133
|
+
lit_value = str(literal)
|
|
134
|
+
lit_name = str(literal)
|
|
135
|
+
interval_data_type = StringType()
|
|
136
|
+
col = snowpark_fn.lit(lit_value)
|
|
137
|
+
|
|
138
|
+
typed_col = TypedColumn(col, lambda: [interval_data_type])
|
|
139
|
+
|
|
140
|
+
return [lit_name], typed_col
|
|
141
|
+
|
|
57
142
|
case "subquery_expression":
|
|
58
143
|
from snowflake.snowpark_connect.dataframe_container import (
|
|
59
144
|
DataFrameContainer,
|
|
@@ -67,29 +152,43 @@ def map_extension(
|
|
|
67
152
|
dataframe=typer.df, column_map=column_mapping
|
|
68
153
|
)
|
|
69
154
|
|
|
70
|
-
|
|
155
|
+
attr_names = []
|
|
156
|
+
with push_evaluating_sql_scope(), push_outer_dataframe(
|
|
157
|
+
current_outer_df
|
|
158
|
+
), resolving_subquery_exp():
|
|
71
159
|
df_container = map_relation(extension.subquery_expression.input)
|
|
72
160
|
df = df_container.dataframe
|
|
161
|
+
attr_names = get_captured_attribute_names()
|
|
73
162
|
|
|
74
163
|
queries = df.queries["queries"]
|
|
75
164
|
if len(queries) != 1:
|
|
76
|
-
|
|
165
|
+
exception = SnowparkConnectNotImplementedError(
|
|
77
166
|
f"Unexpected number of queries: {len(queries)}"
|
|
78
167
|
)
|
|
168
|
+
attach_custom_error_code(exception, ErrorCodes.UNSUPPORTED_OPERATION)
|
|
169
|
+
raise exception
|
|
79
170
|
query = f"({queries[0]})"
|
|
80
171
|
|
|
81
172
|
match extension.subquery_expression.subquery_type:
|
|
82
173
|
case snowflake_proto.SubqueryExpression.SUBQUERY_TYPE_SCALAR:
|
|
83
|
-
name = "scalarsubquery()"
|
|
174
|
+
name = f"scalarsubquery({', '.join(attr_names)})"
|
|
84
175
|
result_exp = snowpark_fn.expr(query)
|
|
85
|
-
|
|
176
|
+
result_tc = TypedColumn(
|
|
177
|
+
result_exp, lambda: [f.datatype for f in df.schema]
|
|
178
|
+
)
|
|
86
179
|
case snowflake_proto.SubqueryExpression.SUBQUERY_TYPE_EXISTS:
|
|
87
180
|
name = "exists()"
|
|
88
181
|
result_exp = snowpark_fn.expr(f"(EXISTS {query})")
|
|
89
|
-
|
|
182
|
+
result_tc = TypedColumn(result_exp, lambda: [BooleanType()])
|
|
90
183
|
case snowflake_proto.SubqueryExpression.SUBQUERY_TYPE_TABLE_ARG:
|
|
91
184
|
# TODO: Currently, map_sql.py handles this, so we never end up here.
|
|
92
|
-
|
|
185
|
+
exception = SnowparkConnectNotImplementedError(
|
|
186
|
+
"Unexpected table arg"
|
|
187
|
+
)
|
|
188
|
+
attach_custom_error_code(
|
|
189
|
+
exception, ErrorCodes.UNSUPPORTED_OPERATION
|
|
190
|
+
)
|
|
191
|
+
raise exception
|
|
93
192
|
case snowflake_proto.SubqueryExpression.SUBQUERY_TYPE_IN:
|
|
94
193
|
cols = [
|
|
95
194
|
map_expression(e, column_mapping, typer)
|
|
@@ -103,13 +202,224 @@ def map_extension(
|
|
|
103
202
|
result_exp = snowpark_fn.in_(
|
|
104
203
|
[col.col for _, col in cols], snowpark_fn.expr(query)
|
|
105
204
|
)
|
|
106
|
-
|
|
205
|
+
result_tc = TypedColumn(result_exp, lambda: [BooleanType()])
|
|
107
206
|
case other:
|
|
108
|
-
|
|
207
|
+
exception = SnowparkConnectNotImplementedError(
|
|
109
208
|
f"Unexpected subquery type: {other}"
|
|
110
209
|
)
|
|
210
|
+
attach_custom_error_code(
|
|
211
|
+
exception, ErrorCodes.UNSUPPORTED_OPERATION
|
|
212
|
+
)
|
|
213
|
+
raise exception
|
|
111
214
|
|
|
112
|
-
return [name],
|
|
215
|
+
return [name], result_tc
|
|
113
216
|
|
|
114
217
|
case other:
|
|
115
|
-
|
|
218
|
+
exception = SnowparkConnectNotImplementedError(
|
|
219
|
+
f"Unexpected extension {other}"
|
|
220
|
+
)
|
|
221
|
+
attach_custom_error_code(exception, ErrorCodes.UNSUPPORTED_OPERATION)
|
|
222
|
+
raise exception
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _format_year_month_interval(
|
|
226
|
+
total_months: int, start_field: int | None, end_field: int | None
|
|
227
|
+
) -> tuple[str, str]:
|
|
228
|
+
"""Format year-month interval with context-aware precision."""
|
|
229
|
+
|
|
230
|
+
# Calculate years and months from absolute value
|
|
231
|
+
is_negative = total_months < 0
|
|
232
|
+
abs_months = abs(total_months)
|
|
233
|
+
years = abs_months // 12
|
|
234
|
+
months = abs_months % 12
|
|
235
|
+
|
|
236
|
+
# Determine interval type
|
|
237
|
+
is_year_only = (
|
|
238
|
+
start_field == YearMonthIntervalType.YEAR
|
|
239
|
+
and end_field == YearMonthIntervalType.YEAR
|
|
240
|
+
)
|
|
241
|
+
is_month_only = (
|
|
242
|
+
start_field == YearMonthIntervalType.MONTH
|
|
243
|
+
and end_field == YearMonthIntervalType.MONTH
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Format based on type and sign
|
|
247
|
+
if is_year_only:
|
|
248
|
+
sign = "-" if is_negative else ""
|
|
249
|
+
str_value = f"INTERVAL '{sign}{years}' YEAR"
|
|
250
|
+
elif is_month_only:
|
|
251
|
+
str_value = f"INTERVAL '{total_months}' MONTH" # Keep original sign
|
|
252
|
+
else: # YEAR TO MONTH (default)
|
|
253
|
+
if is_negative:
|
|
254
|
+
str_value = f"INTERVAL '-{years}-{months}' YEAR TO MONTH"
|
|
255
|
+
else:
|
|
256
|
+
str_value = f"INTERVAL '{years}-{months}' YEAR TO MONTH"
|
|
257
|
+
|
|
258
|
+
return str_value, str_value
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _format_day_time_interval(
|
|
262
|
+
total_microseconds: int, start_field: int | None, end_field: int | None
|
|
263
|
+
) -> tuple[str, str]:
|
|
264
|
+
"""Format day-time interval with context-aware precision."""
|
|
265
|
+
total_seconds = total_microseconds / 1_000_000
|
|
266
|
+
|
|
267
|
+
# Handle negative intervals correctly
|
|
268
|
+
is_negative = total_seconds < 0
|
|
269
|
+
|
|
270
|
+
# Work with integer microseconds to preserve precision
|
|
271
|
+
abs_total_microseconds = abs(total_microseconds)
|
|
272
|
+
|
|
273
|
+
# Convert to components using integer arithmetic
|
|
274
|
+
days = int(abs_total_microseconds // (86400 * 1_000_000))
|
|
275
|
+
remaining_microseconds = abs_total_microseconds % (86400 * 1_000_000)
|
|
276
|
+
hours = int(remaining_microseconds // (3600 * 1_000_000))
|
|
277
|
+
remaining_microseconds = remaining_microseconds % (3600 * 1_000_000)
|
|
278
|
+
minutes = int(remaining_microseconds // (60 * 1_000_000))
|
|
279
|
+
remaining_microseconds = remaining_microseconds % (60 * 1_000_000)
|
|
280
|
+
|
|
281
|
+
# Convert final microseconds to seconds with full precision
|
|
282
|
+
seconds = remaining_microseconds / 1_000_000
|
|
283
|
+
|
|
284
|
+
# Apply negative sign back to days if needed
|
|
285
|
+
if is_negative:
|
|
286
|
+
days = -days
|
|
287
|
+
|
|
288
|
+
# Calculate days string representation (handle -0 case)
|
|
289
|
+
days_str = "-0" if (is_negative and days == 0) else str(days)
|
|
290
|
+
|
|
291
|
+
# Format based on the specific start/end field context
|
|
292
|
+
if (
|
|
293
|
+
start_field == DayTimeIntervalType.DAY and end_field == DayTimeIntervalType.DAY
|
|
294
|
+
): # DAY only
|
|
295
|
+
str_value = f"INTERVAL '{days}' DAY"
|
|
296
|
+
elif (
|
|
297
|
+
start_field == DayTimeIntervalType.HOUR
|
|
298
|
+
and end_field == DayTimeIntervalType.HOUR
|
|
299
|
+
): # HOUR only
|
|
300
|
+
# For HOUR-only intervals, calculate total hours (don't break down into days)
|
|
301
|
+
total_hours = int(abs(total_microseconds) // (3600 * 1_000_000))
|
|
302
|
+
if total_microseconds < 0:
|
|
303
|
+
total_hours = -total_hours
|
|
304
|
+
if total_hours >= 0:
|
|
305
|
+
str_value = f"INTERVAL '{_TWO_DIGIT_FORMAT.format(total_hours)}' HOUR"
|
|
306
|
+
else:
|
|
307
|
+
str_value = f"INTERVAL '{_THREE_DIGIT_FORMAT.format(total_hours)}' HOUR"
|
|
308
|
+
elif (
|
|
309
|
+
start_field == DayTimeIntervalType.MINUTE
|
|
310
|
+
and end_field == DayTimeIntervalType.MINUTE
|
|
311
|
+
): # MINUTE only
|
|
312
|
+
# For MINUTE-only intervals, calculate total minutes (don't break down into hours/days)
|
|
313
|
+
total_minutes = int(abs(total_microseconds) // (60 * 1_000_000))
|
|
314
|
+
if total_microseconds < 0:
|
|
315
|
+
total_minutes = -total_minutes
|
|
316
|
+
if total_minutes >= 0:
|
|
317
|
+
str_value = f"INTERVAL '{_TWO_DIGIT_FORMAT.format(total_minutes)}' MINUTE"
|
|
318
|
+
else:
|
|
319
|
+
str_value = f"INTERVAL '{_THREE_DIGIT_FORMAT.format(total_minutes)}' MINUTE"
|
|
320
|
+
elif (
|
|
321
|
+
start_field == DayTimeIntervalType.SECOND
|
|
322
|
+
and end_field == DayTimeIntervalType.SECOND
|
|
323
|
+
): # SECOND only
|
|
324
|
+
# For SECOND-only intervals, use total seconds (don't break down into minutes/hours/days)
|
|
325
|
+
total_seconds_precise = total_microseconds / 1_000_000
|
|
326
|
+
if total_seconds_precise == int(total_seconds_precise):
|
|
327
|
+
if total_seconds_precise >= 0:
|
|
328
|
+
str_value = f"INTERVAL '{_TWO_DIGIT_FORMAT.format(int(total_seconds_precise))}' SECOND"
|
|
329
|
+
else:
|
|
330
|
+
str_value = f"INTERVAL '{_THREE_DIGIT_FORMAT.format(int(total_seconds_precise))}' SECOND"
|
|
331
|
+
else:
|
|
332
|
+
seconds_str = _format_seconds_precise(total_seconds_precise)
|
|
333
|
+
str_value = f"INTERVAL '{seconds_str}' SECOND"
|
|
334
|
+
elif (
|
|
335
|
+
start_field == DayTimeIntervalType.MINUTE
|
|
336
|
+
and end_field == DayTimeIntervalType.SECOND
|
|
337
|
+
): # MINUTE TO SECOND
|
|
338
|
+
# For MINUTE TO SECOND intervals, calculate total minutes and remaining seconds
|
|
339
|
+
total_minutes = int(abs_total_microseconds // (60 * 1_000_000))
|
|
340
|
+
remaining_microseconds_for_minutes = abs_total_microseconds % (60 * 1_000_000)
|
|
341
|
+
remaining_seconds_for_minutes = remaining_microseconds_for_minutes / 1_000_000
|
|
342
|
+
|
|
343
|
+
# Format seconds appropriately (whole or fractional)
|
|
344
|
+
if remaining_seconds_for_minutes == int(remaining_seconds_for_minutes):
|
|
345
|
+
seconds_str = _TWO_DIGIT_FORMAT.format(int(remaining_seconds_for_minutes))
|
|
346
|
+
else:
|
|
347
|
+
seconds_str = _format_seconds_precise(remaining_seconds_for_minutes)
|
|
348
|
+
|
|
349
|
+
# Apply sign and format
|
|
350
|
+
if is_negative:
|
|
351
|
+
str_value = f"INTERVAL '-{_TWO_DIGIT_FORMAT.format(total_minutes)}:{seconds_str}' MINUTE TO SECOND"
|
|
352
|
+
else:
|
|
353
|
+
str_value = f"INTERVAL '{_TWO_DIGIT_FORMAT.format(total_minutes)}:{seconds_str}' MINUTE TO SECOND"
|
|
354
|
+
elif (
|
|
355
|
+
start_field == DayTimeIntervalType.HOUR
|
|
356
|
+
and end_field == DayTimeIntervalType.MINUTE
|
|
357
|
+
): # HOUR TO MINUTE
|
|
358
|
+
if is_negative:
|
|
359
|
+
str_value = f"INTERVAL '-{_TWO_DIGIT_FORMAT.format(hours)}:{_TWO_DIGIT_FORMAT.format(minutes)}' HOUR TO MINUTE"
|
|
360
|
+
else:
|
|
361
|
+
str_value = f"INTERVAL '{_TWO_DIGIT_FORMAT.format(hours)}:{_TWO_DIGIT_FORMAT.format(minutes)}' HOUR TO MINUTE"
|
|
362
|
+
elif (
|
|
363
|
+
start_field == DayTimeIntervalType.HOUR
|
|
364
|
+
and end_field == DayTimeIntervalType.SECOND
|
|
365
|
+
): # HOUR TO SECOND
|
|
366
|
+
if seconds == int(seconds):
|
|
367
|
+
str_value = f"INTERVAL '{_format_time_component(hours)}:{_format_time_component(minutes)}:{_format_time_component(int(seconds))}' HOUR TO SECOND"
|
|
368
|
+
else:
|
|
369
|
+
seconds_str = _format_seconds_precise(seconds)
|
|
370
|
+
str_value = f"INTERVAL '{_format_time_component(hours)}:{_format_time_component(minutes)}:{seconds_str}' HOUR TO SECOND"
|
|
371
|
+
elif (
|
|
372
|
+
start_field == DayTimeIntervalType.DAY and end_field == DayTimeIntervalType.HOUR
|
|
373
|
+
): # DAY TO HOUR
|
|
374
|
+
str_value = f"INTERVAL '{days} {_format_time_component(hours)}' DAY TO HOUR"
|
|
375
|
+
elif (
|
|
376
|
+
start_field == DayTimeIntervalType.DAY
|
|
377
|
+
and end_field == DayTimeIntervalType.MINUTE
|
|
378
|
+
): # DAY TO MINUTE
|
|
379
|
+
str_value = f"INTERVAL '{days} {_format_time_component(hours)}:{_format_time_component(minutes)}' DAY TO MINUTE"
|
|
380
|
+
elif (
|
|
381
|
+
start_field == DayTimeIntervalType.DAY
|
|
382
|
+
and end_field == DayTimeIntervalType.SECOND
|
|
383
|
+
): # DAY TO SECOND
|
|
384
|
+
if seconds == int(seconds):
|
|
385
|
+
str_value = f"INTERVAL '{days_str} {_format_time_component(hours)}:{_format_time_component(minutes)}:{_format_time_component(int(seconds))}' DAY TO SECOND"
|
|
386
|
+
else:
|
|
387
|
+
seconds_str = _format_seconds_precise(seconds)
|
|
388
|
+
str_value = f"INTERVAL '{days_str} {_format_time_component(hours)}:{_format_time_component(minutes)}:{seconds_str}' DAY TO SECOND"
|
|
389
|
+
else:
|
|
390
|
+
# Fallback - use smart formatting like the original literal.py logic
|
|
391
|
+
if days >= 0:
|
|
392
|
+
if hours == 0 and minutes == 0 and seconds == 0:
|
|
393
|
+
str_value = f"INTERVAL '{int(days)}' DAY"
|
|
394
|
+
else:
|
|
395
|
+
if seconds == int(seconds):
|
|
396
|
+
str_value = f"INTERVAL '{days_str} {_format_time_component(hours)}:{_format_time_component(minutes)}:{_format_time_component(int(seconds))}' DAY TO SECOND"
|
|
397
|
+
else:
|
|
398
|
+
seconds_str = _format_seconds_precise(seconds)
|
|
399
|
+
str_value = f"INTERVAL '{days_str} {_format_time_component(hours)}:{_format_time_component(minutes)}:{seconds_str}' DAY TO SECOND"
|
|
400
|
+
elif hours > 0:
|
|
401
|
+
if minutes == 0 and seconds == 0:
|
|
402
|
+
str_value = f"INTERVAL '{_format_time_component(hours)}' HOUR"
|
|
403
|
+
else:
|
|
404
|
+
if seconds == int(seconds):
|
|
405
|
+
str_value = f"INTERVAL '{_format_time_component(hours)}:{_format_time_component(minutes)}:{_format_time_component(int(seconds))}' HOUR TO SECOND"
|
|
406
|
+
else:
|
|
407
|
+
seconds_str = _format_seconds_precise(seconds)
|
|
408
|
+
str_value = f"INTERVAL '{_format_time_component(hours)}:{_format_time_component(minutes)}:{seconds_str}' HOUR TO SECOND"
|
|
409
|
+
elif minutes > 0:
|
|
410
|
+
if seconds == 0:
|
|
411
|
+
str_value = f"INTERVAL '{_format_time_component(minutes)}' MINUTE"
|
|
412
|
+
else:
|
|
413
|
+
if seconds == int(seconds):
|
|
414
|
+
str_value = f"INTERVAL '{_format_time_component(minutes)}:{_format_time_component(int(seconds))}' MINUTE TO SECOND"
|
|
415
|
+
else:
|
|
416
|
+
seconds_str = _format_seconds_precise(seconds)
|
|
417
|
+
str_value = f"INTERVAL '{_format_time_component(minutes)}:{seconds_str}' MINUTE TO SECOND"
|
|
418
|
+
else:
|
|
419
|
+
if seconds == int(seconds):
|
|
420
|
+
str_value = f"INTERVAL '{_format_time_component(int(seconds))}' SECOND"
|
|
421
|
+
else:
|
|
422
|
+
seconds_str = _format_seconds_precise(seconds)
|
|
423
|
+
str_value = f"INTERVAL '{seconds_str}' SECOND"
|
|
424
|
+
|
|
425
|
+
return str_value, str_value
|