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.
Files changed (200) hide show
  1. snowflake/snowpark_connect/__init__.py +1 -0
  2. snowflake/snowpark_connect/analyze_plan/map_tree_string.py +8 -4
  3. snowflake/snowpark_connect/client/__init__.py +15 -0
  4. snowflake/snowpark_connect/client/error_utils.py +30 -0
  5. snowflake/snowpark_connect/client/exceptions.py +36 -0
  6. snowflake/snowpark_connect/client/query_results.py +90 -0
  7. snowflake/snowpark_connect/client/server.py +717 -0
  8. snowflake/snowpark_connect/client/utils/__init__.py +10 -0
  9. snowflake/snowpark_connect/client/utils/session.py +85 -0
  10. snowflake/snowpark_connect/column_name_handler.py +404 -243
  11. snowflake/snowpark_connect/column_qualifier.py +43 -0
  12. snowflake/snowpark_connect/config.py +309 -26
  13. snowflake/snowpark_connect/constants.py +2 -0
  14. snowflake/snowpark_connect/dataframe_container.py +102 -8
  15. snowflake/snowpark_connect/date_time_format_mapping.py +71 -13
  16. snowflake/snowpark_connect/error/error_codes.py +50 -0
  17. snowflake/snowpark_connect/error/error_utils.py +172 -23
  18. snowflake/snowpark_connect/error/exceptions.py +13 -4
  19. snowflake/snowpark_connect/execute_plan/map_execution_command.py +15 -160
  20. snowflake/snowpark_connect/execute_plan/map_execution_root.py +26 -20
  21. snowflake/snowpark_connect/execute_plan/utils.py +5 -1
  22. snowflake/snowpark_connect/expression/error_utils.py +28 -0
  23. snowflake/snowpark_connect/expression/function_defaults.py +9 -2
  24. snowflake/snowpark_connect/expression/hybrid_column_map.py +53 -5
  25. snowflake/snowpark_connect/expression/integral_types_support.py +219 -0
  26. snowflake/snowpark_connect/expression/literal.py +37 -13
  27. snowflake/snowpark_connect/expression/map_cast.py +224 -15
  28. snowflake/snowpark_connect/expression/map_expression.py +80 -27
  29. snowflake/snowpark_connect/expression/map_extension.py +322 -12
  30. snowflake/snowpark_connect/expression/map_sql_expression.py +316 -81
  31. snowflake/snowpark_connect/expression/map_udf.py +86 -20
  32. snowflake/snowpark_connect/expression/map_unresolved_attribute.py +451 -173
  33. snowflake/snowpark_connect/expression/map_unresolved_function.py +2964 -829
  34. snowflake/snowpark_connect/expression/map_unresolved_star.py +87 -23
  35. snowflake/snowpark_connect/expression/map_update_fields.py +70 -18
  36. snowflake/snowpark_connect/expression/map_window_function.py +18 -3
  37. snowflake/snowpark_connect/includes/jars/json4s-ast_2.13-3.7.0-M11.jar +0 -0
  38. snowflake/snowpark_connect/includes/jars/{scala-library-2.12.18.jar → sas-scala-udf_2.12-0.2.0.jar} +0 -0
  39. snowflake/snowpark_connect/includes/jars/sas-scala-udf_2.13-0.2.0.jar +0 -0
  40. snowflake/snowpark_connect/includes/jars/scala-reflect-2.13.16.jar +0 -0
  41. snowflake/snowpark_connect/includes/jars/spark-common-utils_2.13-3.5.6.jar +0 -0
  42. 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
  43. snowflake/snowpark_connect/includes/jars/{spark-sql_2.12-3.5.6.jar → spark-sql_2.13-3.5.6.jar} +0 -0
  44. snowflake/snowpark_connect/includes/python/pyspark/sql/connect/streaming/worker/foreach_batch_worker.py +1 -1
  45. snowflake/snowpark_connect/includes/python/pyspark/sql/connect/streaming/worker/listener_worker.py +1 -1
  46. snowflake/snowpark_connect/proto/snowflake_expression_ext_pb2.py +12 -10
  47. snowflake/snowpark_connect/proto/snowflake_expression_ext_pb2.pyi +14 -2
  48. snowflake/snowpark_connect/proto/snowflake_relation_ext_pb2.py +10 -8
  49. snowflake/snowpark_connect/proto/snowflake_relation_ext_pb2.pyi +13 -6
  50. snowflake/snowpark_connect/relation/catalogs/abstract_spark_catalog.py +65 -17
  51. snowflake/snowpark_connect/relation/catalogs/snowflake_catalog.py +297 -49
  52. snowflake/snowpark_connect/relation/catalogs/utils.py +12 -4
  53. snowflake/snowpark_connect/relation/io_utils.py +110 -10
  54. snowflake/snowpark_connect/relation/map_aggregate.py +239 -256
  55. snowflake/snowpark_connect/relation/map_catalog.py +5 -1
  56. snowflake/snowpark_connect/relation/map_column_ops.py +264 -96
  57. snowflake/snowpark_connect/relation/map_extension.py +263 -29
  58. snowflake/snowpark_connect/relation/map_join.py +683 -442
  59. snowflake/snowpark_connect/relation/map_local_relation.py +28 -1
  60. snowflake/snowpark_connect/relation/map_map_partitions.py +83 -8
  61. snowflake/snowpark_connect/relation/map_relation.py +48 -19
  62. snowflake/snowpark_connect/relation/map_row_ops.py +310 -91
  63. snowflake/snowpark_connect/relation/map_show_string.py +13 -6
  64. snowflake/snowpark_connect/relation/map_sql.py +1233 -222
  65. snowflake/snowpark_connect/relation/map_stats.py +48 -9
  66. snowflake/snowpark_connect/relation/map_subquery_alias.py +11 -2
  67. snowflake/snowpark_connect/relation/map_udtf.py +14 -4
  68. snowflake/snowpark_connect/relation/read/jdbc_read_dbapi.py +53 -14
  69. snowflake/snowpark_connect/relation/read/map_read.py +134 -43
  70. snowflake/snowpark_connect/relation/read/map_read_csv.py +326 -47
  71. snowflake/snowpark_connect/relation/read/map_read_jdbc.py +21 -6
  72. snowflake/snowpark_connect/relation/read/map_read_json.py +324 -86
  73. snowflake/snowpark_connect/relation/read/map_read_parquet.py +146 -28
  74. snowflake/snowpark_connect/relation/read/map_read_partitioned_parquet.py +142 -0
  75. snowflake/snowpark_connect/relation/read/map_read_socket.py +15 -3
  76. snowflake/snowpark_connect/relation/read/map_read_table.py +86 -6
  77. snowflake/snowpark_connect/relation/read/map_read_text.py +22 -4
  78. snowflake/snowpark_connect/relation/read/metadata_utils.py +170 -0
  79. snowflake/snowpark_connect/relation/read/reader_config.py +42 -3
  80. snowflake/snowpark_connect/relation/read/utils.py +50 -5
  81. snowflake/snowpark_connect/relation/stage_locator.py +91 -55
  82. snowflake/snowpark_connect/relation/utils.py +128 -5
  83. snowflake/snowpark_connect/relation/write/jdbc_write_dbapi.py +19 -3
  84. snowflake/snowpark_connect/relation/write/map_write.py +929 -319
  85. snowflake/snowpark_connect/relation/write/map_write_jdbc.py +8 -2
  86. snowflake/snowpark_connect/resources/java_udfs-1.0-SNAPSHOT.jar +0 -0
  87. snowflake/snowpark_connect/resources_initializer.py +171 -48
  88. snowflake/snowpark_connect/server.py +528 -473
  89. snowflake/snowpark_connect/server_common/__init__.py +503 -0
  90. snowflake/snowpark_connect/snowflake_session.py +65 -0
  91. snowflake/snowpark_connect/start_server.py +53 -5
  92. snowflake/snowpark_connect/type_mapping.py +349 -27
  93. snowflake/snowpark_connect/type_support.py +130 -0
  94. snowflake/snowpark_connect/typed_column.py +9 -7
  95. snowflake/snowpark_connect/utils/artifacts.py +9 -8
  96. snowflake/snowpark_connect/utils/cache.py +49 -27
  97. snowflake/snowpark_connect/utils/concurrent.py +36 -1
  98. snowflake/snowpark_connect/utils/context.py +195 -37
  99. snowflake/snowpark_connect/utils/describe_query_cache.py +68 -53
  100. snowflake/snowpark_connect/utils/env_utils.py +5 -1
  101. snowflake/snowpark_connect/utils/expression_transformer.py +172 -0
  102. snowflake/snowpark_connect/utils/identifiers.py +137 -3
  103. snowflake/snowpark_connect/utils/io_utils.py +57 -1
  104. snowflake/snowpark_connect/utils/java_stored_procedure.py +151 -0
  105. snowflake/snowpark_connect/utils/java_udaf_utils.py +321 -0
  106. snowflake/snowpark_connect/utils/java_udtf_utils.py +239 -0
  107. snowflake/snowpark_connect/utils/jvm_udf_utils.py +281 -0
  108. snowflake/snowpark_connect/utils/open_telemetry.py +516 -0
  109. snowflake/snowpark_connect/utils/pandas_udtf_utils.py +8 -4
  110. snowflake/snowpark_connect/utils/patch_spark_line_number.py +181 -0
  111. snowflake/snowpark_connect/utils/profiling.py +25 -8
  112. snowflake/snowpark_connect/utils/scala_udf_utils.py +185 -340
  113. snowflake/snowpark_connect/utils/sequence.py +21 -0
  114. snowflake/snowpark_connect/utils/session.py +64 -28
  115. snowflake/snowpark_connect/utils/snowpark_connect_logging.py +51 -9
  116. snowflake/snowpark_connect/utils/spcs_logger.py +290 -0
  117. snowflake/snowpark_connect/utils/telemetry.py +192 -40
  118. snowflake/snowpark_connect/utils/temporary_view_cache.py +67 -0
  119. snowflake/snowpark_connect/utils/temporary_view_helper.py +334 -0
  120. snowflake/snowpark_connect/utils/udf_cache.py +117 -41
  121. snowflake/snowpark_connect/utils/udf_helper.py +39 -37
  122. snowflake/snowpark_connect/utils/udf_utils.py +133 -14
  123. snowflake/snowpark_connect/utils/udtf_helper.py +8 -1
  124. snowflake/snowpark_connect/utils/udtf_utils.py +46 -31
  125. snowflake/snowpark_connect/utils/udxf_import_utils.py +9 -2
  126. snowflake/snowpark_connect/utils/upload_java_jar.py +57 -0
  127. snowflake/snowpark_connect/version.py +1 -1
  128. snowflake/snowpark_decoder/dp_session.py +6 -2
  129. snowflake/snowpark_decoder/spark_decoder.py +12 -0
  130. {snowpark_connect-0.27.0.data → snowpark_connect-1.7.0.data}/scripts/snowpark-submit +14 -4
  131. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/METADATA +16 -7
  132. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/RECORD +139 -168
  133. snowflake/snowpark_connect/hidden_column.py +0 -39
  134. snowflake/snowpark_connect/includes/jars/antlr4-runtime-4.9.3.jar +0 -0
  135. snowflake/snowpark_connect/includes/jars/commons-cli-1.5.0.jar +0 -0
  136. snowflake/snowpark_connect/includes/jars/commons-codec-1.16.1.jar +0 -0
  137. snowflake/snowpark_connect/includes/jars/commons-collections-3.2.2.jar +0 -0
  138. snowflake/snowpark_connect/includes/jars/commons-collections4-4.4.jar +0 -0
  139. snowflake/snowpark_connect/includes/jars/commons-compiler-3.1.9.jar +0 -0
  140. snowflake/snowpark_connect/includes/jars/commons-compress-1.26.0.jar +0 -0
  141. snowflake/snowpark_connect/includes/jars/commons-crypto-1.1.0.jar +0 -0
  142. snowflake/snowpark_connect/includes/jars/commons-dbcp-1.4.jar +0 -0
  143. snowflake/snowpark_connect/includes/jars/commons-io-2.16.1.jar +0 -0
  144. snowflake/snowpark_connect/includes/jars/commons-lang-2.6.jar +0 -0
  145. snowflake/snowpark_connect/includes/jars/commons-lang3-3.12.0.jar +0 -0
  146. snowflake/snowpark_connect/includes/jars/commons-logging-1.1.3.jar +0 -0
  147. snowflake/snowpark_connect/includes/jars/commons-math3-3.6.1.jar +0 -0
  148. snowflake/snowpark_connect/includes/jars/commons-pool-1.5.4.jar +0 -0
  149. snowflake/snowpark_connect/includes/jars/commons-text-1.10.0.jar +0 -0
  150. snowflake/snowpark_connect/includes/jars/hadoop-client-api-trimmed-3.3.4.jar +0 -0
  151. snowflake/snowpark_connect/includes/jars/jackson-annotations-2.15.2.jar +0 -0
  152. snowflake/snowpark_connect/includes/jars/jackson-core-2.15.2.jar +0 -0
  153. snowflake/snowpark_connect/includes/jars/jackson-core-asl-1.9.13.jar +0 -0
  154. snowflake/snowpark_connect/includes/jars/jackson-databind-2.15.2.jar +0 -0
  155. snowflake/snowpark_connect/includes/jars/jackson-dataformat-yaml-2.15.2.jar +0 -0
  156. snowflake/snowpark_connect/includes/jars/jackson-datatype-jsr310-2.15.2.jar +0 -0
  157. snowflake/snowpark_connect/includes/jars/jackson-module-scala_2.12-2.15.2.jar +0 -0
  158. snowflake/snowpark_connect/includes/jars/json4s-ast_2.12-3.7.0-M11.jar +0 -0
  159. snowflake/snowpark_connect/includes/jars/json4s-core_2.12-3.7.0-M11.jar +0 -0
  160. snowflake/snowpark_connect/includes/jars/json4s-jackson_2.12-3.7.0-M11.jar +0 -0
  161. snowflake/snowpark_connect/includes/jars/json4s-native_2.12-3.7.0-M11.jar +0 -0
  162. snowflake/snowpark_connect/includes/jars/json4s-scalap_2.12-3.7.0-M11.jar +0 -0
  163. snowflake/snowpark_connect/includes/jars/kryo-shaded-4.0.2.jar +0 -0
  164. snowflake/snowpark_connect/includes/jars/log4j-1.2-api-2.20.0.jar +0 -0
  165. snowflake/snowpark_connect/includes/jars/log4j-api-2.20.0.jar +0 -0
  166. snowflake/snowpark_connect/includes/jars/log4j-core-2.20.0.jar +0 -0
  167. snowflake/snowpark_connect/includes/jars/log4j-slf4j2-impl-2.20.0.jar +0 -0
  168. snowflake/snowpark_connect/includes/jars/paranamer-2.8.3.jar +0 -0
  169. snowflake/snowpark_connect/includes/jars/paranamer-2.8.jar +0 -0
  170. snowflake/snowpark_connect/includes/jars/sas-scala-udf_2.12-0.1.0.jar +0 -0
  171. snowflake/snowpark_connect/includes/jars/scala-collection-compat_2.12-2.7.0.jar +0 -0
  172. snowflake/snowpark_connect/includes/jars/scala-parser-combinators_2.12-2.3.0.jar +0 -0
  173. snowflake/snowpark_connect/includes/jars/scala-reflect-2.12.18.jar +0 -0
  174. snowflake/snowpark_connect/includes/jars/scala-xml_2.12-2.1.0.jar +0 -0
  175. snowflake/snowpark_connect/includes/jars/slf4j-api-2.0.7.jar +0 -0
  176. snowflake/snowpark_connect/includes/jars/spark-catalyst_2.12-3.5.6.jar +0 -0
  177. snowflake/snowpark_connect/includes/jars/spark-common-utils_2.12-3.5.6.jar +0 -0
  178. snowflake/snowpark_connect/includes/jars/spark-core_2.12-3.5.6.jar +0 -0
  179. snowflake/snowpark_connect/includes/jars/spark-graphx_2.12-3.5.6.jar +0 -0
  180. snowflake/snowpark_connect/includes/jars/spark-hive-thriftserver_2.12-3.5.6.jar +0 -0
  181. snowflake/snowpark_connect/includes/jars/spark-hive_2.12-3.5.6.jar +0 -0
  182. snowflake/snowpark_connect/includes/jars/spark-kvstore_2.12-3.5.6.jar +0 -0
  183. snowflake/snowpark_connect/includes/jars/spark-launcher_2.12-3.5.6.jar +0 -0
  184. snowflake/snowpark_connect/includes/jars/spark-mesos_2.12-3.5.6.jar +0 -0
  185. snowflake/snowpark_connect/includes/jars/spark-mllib-local_2.12-3.5.6.jar +0 -0
  186. snowflake/snowpark_connect/includes/jars/spark-network-common_2.12-3.5.6.jar +0 -0
  187. snowflake/snowpark_connect/includes/jars/spark-network-shuffle_2.12-3.5.6.jar +0 -0
  188. snowflake/snowpark_connect/includes/jars/spark-repl_2.12-3.5.6.jar +0 -0
  189. snowflake/snowpark_connect/includes/jars/spark-sketch_2.12-3.5.6.jar +0 -0
  190. snowflake/snowpark_connect/includes/jars/spark-sql-api_2.12-3.5.6.jar +0 -0
  191. snowflake/snowpark_connect/includes/jars/spark-tags_2.12-3.5.6.jar +0 -0
  192. snowflake/snowpark_connect/includes/jars/spark-unsafe_2.12-3.5.6.jar +0 -0
  193. snowflake/snowpark_connect/includes/jars/spark-yarn_2.12-3.5.6.jar +0 -0
  194. {snowpark_connect-0.27.0.data → snowpark_connect-1.7.0.data}/scripts/snowpark-connect +0 -0
  195. {snowpark_connect-0.27.0.data → snowpark_connect-1.7.0.data}/scripts/snowpark-session +0 -0
  196. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/WHEEL +0 -0
  197. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/licenses/LICENSE-binary +0 -0
  198. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/licenses/LICENSE.txt +0 -0
  199. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/licenses/NOTICE-binary +0 -0
  200. {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 BooleanType
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
- raise SnowparkConnectNotImplementedError(
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
- with push_evaluating_sql_scope(), push_outer_dataframe(current_outer_df):
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
- raise SnowparkConnectNotImplementedError(
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
- result_type = df.schema[0].datatype
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
- result_type = BooleanType()
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
- raise SnowparkConnectNotImplementedError("Unexpected table arg")
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
- result_type = BooleanType()
205
+ result_tc = TypedColumn(result_exp, lambda: [BooleanType()])
107
206
  case other:
108
- raise SnowparkConnectNotImplementedError(
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], TypedColumn(result_exp, lambda: [result_type])
215
+ return [name], result_tc
113
216
 
114
217
  case other:
115
- raise SnowparkConnectNotImplementedError(f"Unexpected extension {other}")
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