snowflake-connector-python 5.2.0__cp315-cp315-win_amd64.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 (204) hide show
  1. snowflake/connector/__init__.py +148 -0
  2. snowflake/connector/_common/__init__.py +0 -0
  3. snowflake/connector/_common/extras.py +134 -0
  4. snowflake/connector/_common/telemetry.py +221 -0
  5. snowflake/connector/_common/wif_util.py +92 -0
  6. snowflake/connector/_core/sf_core_python.cp315-win_amd64.pyd +0 -0
  7. snowflake/connector/_core/sf_core_python.pyi +79 -0
  8. snowflake/connector/_internal/__init__.py +6 -0
  9. snowflake/connector/_internal/_private_key_helper.py +31 -0
  10. snowflake/connector/_internal/api_client/__init__.py +93 -0
  11. snowflake/connector/_internal/api_client/bridge.py +41 -0
  12. snowflake/connector/_internal/api_client/client_api.py +976 -0
  13. snowflake/connector/_internal/arrow.py +47 -0
  14. snowflake/connector/_internal/arrow_context.py +240 -0
  15. snowflake/connector/_internal/arrow_stream_async.py +266 -0
  16. snowflake/connector/_internal/arrow_stream_iterator.cp311-win_amd64.pyd +0 -0
  17. snowflake/connector/_internal/arrow_stream_iterator.cp312-win_amd64.pyd +0 -0
  18. snowflake/connector/_internal/arrow_stream_iterator.cp313-win_amd64.pyd +0 -0
  19. snowflake/connector/_internal/arrow_stream_iterator.cp314-win_amd64.pyd +0 -0
  20. snowflake/connector/_internal/arrow_stream_iterator.cp314t-win_amd64.pyd +0 -0
  21. snowflake/connector/_internal/arrow_stream_iterator.cp315-win_amd64.pyd +0 -0
  22. snowflake/connector/_internal/arrow_stream_iterator.pyi +42 -0
  23. snowflake/connector/_internal/arrow_stream_utils.py +171 -0
  24. snowflake/connector/_internal/authenticator_type.py +53 -0
  25. snowflake/connector/_internal/backward_compatibility.py +245 -0
  26. snowflake/connector/_internal/binding_converters.py +741 -0
  27. snowflake/connector/_internal/config_utils.py +71 -0
  28. snowflake/connector/_internal/connection/__init__.py +39 -0
  29. snowflake/connector/_internal/connection/connection.py +616 -0
  30. snowflake/connector/_internal/connection/connection_types.py +13 -0
  31. snowflake/connector/_internal/connection/constants.py +25 -0
  32. snowflake/connector/_internal/connection/decorators.py +31 -0
  33. snowflake/connector/_internal/connection/freezable_proxy.py +104 -0
  34. snowflake/connector/_internal/connection/queries.py +10 -0
  35. snowflake/connector/_internal/connection_config_mixin.py +755 -0
  36. snowflake/connector/_internal/cursor/__init__.py +25 -0
  37. snowflake/connector/_internal/cursor/base.py +732 -0
  38. snowflake/connector/_internal/cursor/cursor_types.py +13 -0
  39. snowflake/connector/_internal/cursor/decorators.py +56 -0
  40. snowflake/connector/_internal/cursor/query_result.py +202 -0
  41. snowflake/connector/_internal/cursor/query_result_waiter.py +98 -0
  42. snowflake/connector/_internal/cursor/result_metadata.py +255 -0
  43. snowflake/connector/_internal/cursor/result_set_wrapper.py +66 -0
  44. snowflake/connector/_internal/decorators.py +539 -0
  45. snowflake/connector/_internal/error_kinds.py +111 -0
  46. snowflake/connector/_internal/errorcode.py +35 -0
  47. snowflake/connector/_internal/errorhandler.py +278 -0
  48. snowflake/connector/_internal/fips.py +28 -0
  49. snowflake/connector/_internal/logging/__init__.py +52 -0
  50. snowflake/connector/_internal/logging/config.py +134 -0
  51. snowflake/connector/_internal/logging/core_logger.py +218 -0
  52. snowflake/connector/_internal/logging/native_extension_logger.py +144 -0
  53. snowflake/connector/_internal/logout_config_mapping.py +187 -0
  54. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +57 -0
  55. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
  56. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrowTableConverter.cpp +943 -0
  57. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrowTableConverter.hpp +63 -0
  58. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +18 -0
  59. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
  60. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
  61. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
  62. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +118 -0
  63. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +116 -0
  64. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamIterator.cpp +323 -0
  65. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamIterator.hpp +133 -0
  66. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamTableIterator.cpp +275 -0
  67. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamTableIterator.hpp +168 -0
  68. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ConverterUtil.cpp +439 -0
  69. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ConverterUtil.hpp +30 -0
  70. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +48 -0
  71. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
  72. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +76 -0
  73. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
  74. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +90 -0
  75. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +63 -0
  76. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +62 -0
  77. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
  78. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +29 -0
  79. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
  80. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
  81. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
  82. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +41 -0
  83. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +65 -0
  84. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +54 -0
  85. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
  86. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +79 -0
  87. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
  88. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +52 -0
  89. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
  90. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
  91. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +117 -0
  92. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +54 -0
  93. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +34 -0
  94. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +33 -0
  95. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
  96. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +18 -0
  97. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
  98. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +39 -0
  99. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
  100. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +326 -0
  101. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +138 -0
  102. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
  103. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Util/time.cpp +65 -0
  104. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
  105. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/arrow_stream_iterator.cpp +13278 -0
  106. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/arrow_stream_iterator.pyx +204 -0
  107. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +109 -0
  108. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +125 -0
  109. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
  110. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1914 -0
  111. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +208 -0
  112. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +128 -0
  113. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
  114. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +58 -0
  115. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +144 -0
  116. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
  117. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
  118. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
  119. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +160 -0
  120. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +98 -0
  121. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +243 -0
  122. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
  123. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +207 -0
  124. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +80 -0
  125. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +83 -0
  126. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
  127. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
  128. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +201 -0
  129. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +108 -0
  130. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
  131. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
  132. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
  133. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
  134. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
  135. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +164 -0
  136. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +926 -0
  137. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +182 -0
  138. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
  139. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
  140. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc.c +3052 -0
  141. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3019 -0
  142. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3354 -0
  143. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +344 -0
  144. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +29305 -0
  145. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +381 -0
  146. snowflake/connector/_internal/nanoarrow_cpp/Logging/logging.cpp +108 -0
  147. snowflake/connector/_internal/nanoarrow_cpp/Logging/logging.hpp +47 -0
  148. snowflake/connector/_internal/nanoarrow_cpp/scripts/.clang-format +8 -0
  149. snowflake/connector/_internal/nanoarrow_cpp/scripts/format.sh +8 -0
  150. snowflake/connector/_internal/protobuf_gen/__init__.py +0 -0
  151. snowflake/connector/_internal/protobuf_gen/database_driver_v1_pb2.py +408 -0
  152. snowflake/connector/_internal/protobuf_gen/database_driver_v1_pb2.pyi +4487 -0
  153. snowflake/connector/_internal/protobuf_gen/database_driver_v1_services.py +2088 -0
  154. snowflake/connector/_internal/protobuf_gen/proto_exception.py +17 -0
  155. snowflake/connector/_internal/result_batch.py +150 -0
  156. snowflake/connector/_internal/snowflake_restful.py +222 -0
  157. snowflake/connector/_internal/snowpark_backcompat_helpers.py +31 -0
  158. snowflake/connector/_internal/sqlstate.py +11 -0
  159. snowflake/connector/_internal/statement_utils.py +207 -0
  160. snowflake/connector/_internal/text_utils.py +112 -0
  161. snowflake/connector/_internal/type_codes.py +139 -0
  162. snowflake/connector/_internal/utils.py +65 -0
  163. snowflake/connector/_internal/write_pandas_operation.py +657 -0
  164. snowflake/connector/aio/README.md +168 -0
  165. snowflake/connector/aio/__init__.py +37 -0
  166. snowflake/connector/aio/connection/__init__.py +17 -0
  167. snowflake/connector/aio/connection/_connection.py +506 -0
  168. snowflake/connector/aio/cursor/__init__.py +35 -0
  169. snowflake/connector/aio/cursor/_base.py +1034 -0
  170. snowflake/connector/aio/cursor/_dict_cursor.py +78 -0
  171. snowflake/connector/aio/cursor/_result_set_wrapper.py +35 -0
  172. snowflake/connector/aio/cursor/_snowflake_cursor.py +72 -0
  173. snowflake/connector/aio/pandas_tools.py +279 -0
  174. snowflake/connector/aio/result_batch.py +165 -0
  175. snowflake/connector/auth/__init__.py +0 -0
  176. snowflake/connector/auth/workload_identity.py +5 -0
  177. snowflake/connector/compat.py +22 -0
  178. snowflake/connector/config_manager.py +392 -0
  179. snowflake/connector/connection/__init__.py +30 -0
  180. snowflake/connector/connection/_connection.py +641 -0
  181. snowflake/connector/connection_config.py +517 -0
  182. snowflake/connector/constants.py +52 -0
  183. snowflake/connector/cursor/__init__.py +37 -0
  184. snowflake/connector/cursor/_base.py +1169 -0
  185. snowflake/connector/cursor/_chunked_download_reader.py +75 -0
  186. snowflake/connector/cursor/_dict_cursor.py +78 -0
  187. snowflake/connector/cursor/_result_set_wrapper.py +35 -0
  188. snowflake/connector/cursor/_snowflake_cursor.py +72 -0
  189. snowflake/connector/description.py +7 -0
  190. snowflake/connector/errors.py +492 -0
  191. snowflake/connector/network.py +71 -0
  192. snowflake/connector/pandas_tools.py +191 -0
  193. snowflake/connector/py.typed +0 -0
  194. snowflake/connector/result_batch.py +216 -0
  195. snowflake/connector/telemetry.py +40 -0
  196. snowflake/connector/time_util.py +11 -0
  197. snowflake/connector/types.py +134 -0
  198. snowflake/connector/util_text.py +3 -0
  199. snowflake/connector/version.py +23 -0
  200. snowflake_connector_python-5.2.0.dist-info/DELVEWHEEL +2 -0
  201. snowflake_connector_python-5.2.0.dist-info/METADATA +190 -0
  202. snowflake_connector_python-5.2.0.dist-info/RECORD +204 -0
  203. snowflake_connector_python-5.2.0.dist-info/WHEEL +4 -0
  204. snowflake_connector_python.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
@@ -0,0 +1,1169 @@
1
+ """Base cursor class for database operations (PEP 249)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import abc
6
+ import logging
7
+
8
+ from collections.abc import Callable, Iterable, Iterator, Sequence
9
+ from typing import TYPE_CHECKING, Any, BinaryIO, Self, cast, overload
10
+
11
+ from .._common.extras import pandas, pyarrow, requires_dependency
12
+ from .._internal.api_client.client_api import CHUNK_SIZE, core_driver
13
+ from .._internal.arrow_context import ArrowConverterContext
14
+ from .._internal.arrow_stream_utils import (
15
+ collect_arrow_table,
16
+ create_row_iterator,
17
+ create_table_iterator,
18
+ )
19
+ from .._internal.binding_converters import ParamStyle
20
+ from .._internal.cursor import (
21
+ Args,
22
+ CursorBaseMixin,
23
+ DictRow,
24
+ MultiStatementQueryResultState,
25
+ QueryResult,
26
+ QueryResultWaiter,
27
+ ResultMetadata,
28
+ ResultMetadataV2,
29
+ Row,
30
+ )
31
+ from .._internal.cursor.decorators import (
32
+ requires_open,
33
+ requires_open_cursor_not_connection,
34
+ with_prefetch_hook,
35
+ )
36
+ from .._internal.decorators import (
37
+ api_telemetry,
38
+ backward_compatibility,
39
+ pep249,
40
+ snowpark_compat,
41
+ )
42
+ from .._internal.errorcode import ER_CURSOR_IS_CLOSED, ER_INVALID_VALUE
43
+ from .._internal.errorhandler import simplified_error_handling
44
+ from .._internal.logging import get_logger
45
+ from .._internal.protobuf_gen.database_driver_v1_pb2 import (
46
+ ABORT_QUERY_OUTCOME_ABORTED,
47
+ ExecuteQueryResponse,
48
+ MultiStatementResult,
49
+ PrepareResult,
50
+ QueryBindings,
51
+ ResultSetResponse,
52
+ StatementHandle,
53
+ )
54
+ from .._internal.statement_utils import statement
55
+ from .._internal.utils import _coerce_executemany_params, _resolve_alias
56
+ from ..errors import InterfaceError, OperationalError, ProgrammingError
57
+ from ..result_batch import ResultBatch
58
+ from ._chunked_download_reader import _ChunkedDownloadReader
59
+ from ._result_set_wrapper import _ResultSetWrapper
60
+
61
+
62
+ if TYPE_CHECKING:
63
+ from pandas import DataFrame
64
+ from pyarrow import Table
65
+
66
+ from .._internal.arrow import ArrowRowIterator
67
+ from ..connection import Connection
68
+
69
+ logger = get_logger(__name__)
70
+
71
+
72
+ class SnowflakeCursorBase(CursorBaseMixin, abc.ABC):
73
+ """
74
+ Base cursor class for database operations (PEP 249).
75
+
76
+ This is the abstract base for all cursor types, equivalent to
77
+ ``SnowflakeCursorBase`` in the old connector. Concrete subclasses
78
+ must override :pyattr:`_use_dict_result` and :pymeth:`fetchone`.
79
+ """
80
+
81
+ _connection: Connection
82
+ _iterator: ArrowRowIterator | None
83
+
84
+ def __init__(self, connection: Connection) -> None:
85
+ """
86
+ Initialize a new cursor object.
87
+
88
+ Args:
89
+ connection: Connection object that created this cursor
90
+ """
91
+ self._connection = connection
92
+ super().__init__()
93
+ self._iterator = None
94
+
95
+ # -- ResultSet guard (set by _execute; released on reset unless reuse_results) --
96
+ self._result_set = _ResultSetWrapper()
97
+ # Deferred result loading (set by get_results_from_sfqid, invoked on first fetch)
98
+ self._prefetch_hook: Callable[[], None] | None = None
99
+
100
+ # ------------------------------------------------------------------
101
+ # PEP 249 attributes
102
+ # ------------------------------------------------------------------
103
+
104
+ @property
105
+ @pep249
106
+ @api_telemetry
107
+ def connection(self) -> Connection:
108
+ """The :class:`Connection` object that created this cursor."""
109
+ return self._connection
110
+
111
+ # ------------------------------------------------------------------
112
+ # Result format control
113
+ # ------------------------------------------------------------------
114
+
115
+ @property
116
+ @abc.abstractmethod
117
+ def _use_dict_result(self) -> bool:
118
+ """Whether fetch methods return dicts instead of tuples."""
119
+
120
+ # ------------------------------------------------------------------
121
+ # Error handling
122
+ # ------------------------------------------------------------------
123
+
124
+ @property
125
+ def _errorhandler_cursor(self) -> SnowflakeCursorBase:
126
+ return self
127
+
128
+ @property
129
+ def _errorhandler_connection(self) -> Connection:
130
+ return self._connection
131
+
132
+ # ------------------------------------------------------------------
133
+ # Execution
134
+ # ------------------------------------------------------------------
135
+
136
+ @overload
137
+ def callproc(self, procname: str) -> tuple: ...
138
+
139
+ @overload
140
+ def callproc(self, procname: str, args: Args) -> Args: ...
141
+
142
+ @pep249
143
+ @api_telemetry
144
+ @requires_open
145
+ def callproc(self, procname: str, args: Any = None) -> Any:
146
+ """Call a stored procedure.
147
+
148
+ Args:
149
+ procname: The stored procedure to be called.
150
+ args: Parameters to be passed into the stored procedure.
151
+ ``None`` is treated as no arguments.
152
+
153
+ Returns:
154
+ The input parameters.
155
+ """
156
+ command, args = self._prepare_call_proc_statement(procname, args)
157
+ self.execute(command, args)
158
+ return args
159
+
160
+ @pep249
161
+ @api_telemetry
162
+ @requires_open
163
+ def execute(
164
+ self,
165
+ operation: str,
166
+ parameters: Sequence[Any] | dict[str, Any] | None = None,
167
+ _is_put_get: bool | None = None,
168
+ num_statements: int | None = None,
169
+ _skip_upload_on_content_match: bool = False,
170
+ *,
171
+ params: Sequence[Any] | dict[str, Any] | None = None,
172
+ _force_qmark_paramstyle: bool = False,
173
+ _statement_params: dict[str, Any] | None = None,
174
+ file_stream: BinaryIO | None = None,
175
+ **kwargs: Any,
176
+ ) -> Self:
177
+ """
178
+ Execute a database operation (query or command).
179
+ Resets the cursor state before the execution.
180
+
181
+ Args:
182
+ operation (str): SQL statement to execute
183
+ parameters (sequence or dict): Parameters for the operation.
184
+ For qmark/numeric paramstyle: sequence of values
185
+ For pyformat/format paramstyle: sequence (%s) or dict (%(name)s)
186
+ For format paramstyle: sequence (%s)
187
+ num_statements (int, optional): Number of statements in a multistatement query.
188
+ _skip_upload_on_content_match (bool, optional): On PUT, skip
189
+ re-upload when the remote stored digest metadata (S3
190
+ ``x-amz-meta-sfc-digest`` / Azure ``x-ms-meta-sfcdigest`` / GCS
191
+ ``x-goog-meta-sfc-digest``) matches the locally-computed
192
+ SHA-256. Opt-in optimization for racing concurrent uploaders;
193
+ only meaningful with ``OVERWRITE=TRUE``. Underscore-prefixed
194
+ for parity with the legacy Python-connector kwarg name.
195
+ params: Legacy alias for ``parameters`` (kwarg-only). Cannot be
196
+ supplied together with ``parameters``.
197
+ _force_qmark_paramstyle: If True, bind as qmark (``?``) even when
198
+ the connection's paramstyle is pyformat/format. Used by
199
+ callers that emit ``?`` placeholders unconditionally.
200
+ _statement_params: Extra per-statement parameters (e.g. ``QUERY_TAG``)
201
+ sent to Snowflake with this query only. Never persisted on the
202
+ cursor; forwarded as query-request parameters, so they tag only
203
+ this query without mutating session state.
204
+ file_stream: When set, ``operation`` must be a PUT statement with
205
+ a ``file://`` path (only the basename is used as the
206
+ destination filename). Its contents come from this stream
207
+ instead of disk, forwarded to the core driver in chunks so
208
+ the whole payload is never buffered in the wrapper. Non-PUT
209
+ ``operation`` raises ProgrammingError.
210
+ """
211
+ parameters = _resolve_alias(parameters, params, "parameters", "params") # type: ignore[assignment]
212
+
213
+ # Per-call params: this execute() only, never persisted on the cursor.
214
+ statement_parameters = self._collect_statement_params(
215
+ skip_upload_on_content_match=_skip_upload_on_content_match,
216
+ num_statements=num_statements,
217
+ statement_params=_statement_params,
218
+ )
219
+
220
+ self.reset()
221
+ return self._execute(
222
+ operation,
223
+ parameters,
224
+ _is_put_get,
225
+ statement_parameters=statement_parameters,
226
+ _force_qmark_paramstyle=_force_qmark_paramstyle,
227
+ file_stream=file_stream,
228
+ **kwargs,
229
+ )
230
+
231
+ def _execute(
232
+ self,
233
+ operation: str,
234
+ parameters: Sequence[Any] | dict[str, Any] | None = None,
235
+ _is_put_get: bool | None = None,
236
+ *,
237
+ statement_parameters: dict[str, Any] | None = None,
238
+ _force_qmark_paramstyle: bool = False,
239
+ file_stream: BinaryIO | None = None,
240
+ **kwargs: Any,
241
+ ) -> Self:
242
+ """Execute query logic."""
243
+ operation = operation.strip(" \t\n\r") if operation else ""
244
+
245
+ if file_stream is not None:
246
+ self._execute_upload_stream(operation, file_stream)
247
+ self._rownumber = -1
248
+ return self
249
+
250
+ if not operation:
251
+ logger.warning("execute: no query is given to execute")
252
+ return self
253
+
254
+ if logger.is_enabled_for(logging.INFO) and self._connection.config.log_query_text:
255
+ logger.info("query: [%s]", self._format_query_for_log(operation))
256
+
257
+ query, binding_params = self._prepare_query(
258
+ operation, parameters, _force_qmark_paramstyle=_force_qmark_paramstyle
259
+ )
260
+
261
+ with statement(self.connection.conn_handle, query) as stmt_handle: # type: ignore[arg-type]
262
+ self._apply_statement_parameters(stmt_handle, statement_parameters)
263
+
264
+ bindings = self._build_query_bindings(binding_params, query) if binding_params is not None else None
265
+ try:
266
+ response = self._execute_query(stmt_handle, bindings)
267
+ except OperationalError as exc:
268
+ if not self._should_retry_with_inline_json(bindings, exc):
269
+ raise
270
+ bindings = self._build_query_bindings(cast(Sequence[Any], binding_params), query, force_json=True)
271
+ response = self._execute_query(stmt_handle, bindings)
272
+ request_id = response.request_id or None
273
+
274
+ if response.HasField("multi"):
275
+ self._handle_multi_statement_response(response.multi, query, request_id)
276
+ else:
277
+ self._apply_result_set(response.single, query, request_id)
278
+
279
+ self._rownumber = -1 # reset the rownumber (rownumber is not reset in reset() for backward compatibility)
280
+ return self
281
+
282
+ def _execute_upload_stream(self, query: str, file_stream: BinaryIO) -> None:
283
+ """Chunked streaming PUT: feed *file_stream* to the core driver in bounded chunks.
284
+
285
+ Bytes cross the RPC boundary via begin/chunk/finish, bounding wrapper
286
+ memory to one chunk. A failure after begin aborts the session
287
+ server-side before re-raising.
288
+ """
289
+ # Callers commonly hand over a stream whose position is not at the start.
290
+ file_stream.seek(0)
291
+ upload_handle = core_driver.upload_stream_begin(
292
+ conn_handle=self._connection.conn_handle, # type: ignore[arg-type]
293
+ sql=query,
294
+ ).upload_handle
295
+ try:
296
+ while chunk := file_stream.read(CHUNK_SIZE):
297
+ core_driver.upload_stream_chunk(upload_handle, chunk)
298
+ finish_response = core_driver.upload_stream_finish(upload_handle)
299
+ except BaseException:
300
+ try:
301
+ core_driver.upload_stream_abort(upload_handle)
302
+ except Exception:
303
+ logger.debug("upload_stream_abort during cleanup failed; propagating original error", exc_info=True)
304
+ raise
305
+ self._apply_result_set(finish_response, query) # type: ignore[arg-type]
306
+
307
+ @api_telemetry
308
+ @requires_open
309
+ def download_stream(self, stage_location: str, decompress: bool = False) -> BinaryIO:
310
+ """Return a lazily-read binary stream for a stage file (chunked, bounded memory).
311
+
312
+ Splits *stage_location* into stage name and filename on the last ``/``
313
+ and opens via the Begin RPC. Nothing happens until you read; each read
314
+ pulls one chunk. Close the stream (or use ``with``) to release the
315
+ download session early.
316
+ """
317
+ logger.info("download_stream: entry")
318
+ try:
319
+ stage_name, sep, source_filename = stage_location.rpartition("/")
320
+ if not sep:
321
+ # No "/": treat the whole string as the stage name, no filename.
322
+ stage_name, source_filename = stage_location, ""
323
+ begin = core_driver.download_stream_begin(
324
+ conn_handle=self._connection.conn_handle, # type: ignore[arg-type]
325
+ stage_name=stage_name,
326
+ source_filename=source_filename,
327
+ decompress=decompress,
328
+ )
329
+ return _ChunkedDownloadReader(begin.download_handle) # type: ignore[return-value]
330
+ finally:
331
+ logger.info("download_stream: exit")
332
+
333
+ @snowpark_compat
334
+ @backward_compatibility
335
+ @requires_open
336
+ def _upload(
337
+ self,
338
+ local_file_name: str,
339
+ stage_location: str,
340
+ options: dict[str, Any],
341
+ _do_reset: bool = True,
342
+ ) -> None:
343
+ """Synthesize and run the PUT that ``session.file.put`` would otherwise issue as SQL."""
344
+ from .._internal.snowpark_backcompat_helpers import file_uri, put_get_options, stage_ref
345
+
346
+ sql = f"PUT {file_uri(local_file_name)} {stage_ref(stage_location)} {put_get_options(options)}"
347
+ self._execute_file_transfer(sql.rstrip(), _do_reset=_do_reset)
348
+
349
+ @snowpark_compat
350
+ @backward_compatibility
351
+ @requires_open
352
+ def _download(
353
+ self,
354
+ stage_location: str,
355
+ target_directory: str,
356
+ options: dict[str, Any],
357
+ _do_reset: bool = True,
358
+ ) -> None:
359
+ """Synthesize and run the GET that ``session.file.get`` would otherwise issue as SQL."""
360
+ from .._internal.snowpark_backcompat_helpers import file_uri, put_get_options, stage_ref
361
+
362
+ sql = f"GET {stage_ref(stage_location)} {file_uri(target_directory)} {put_get_options(options)}"
363
+ self._execute_file_transfer(sql.rstrip(), _do_reset=_do_reset)
364
+
365
+ @snowpark_compat
366
+ @backward_compatibility
367
+ @requires_open
368
+ def _upload_stream(
369
+ self,
370
+ input_stream: BinaryIO,
371
+ stage_location: str,
372
+ options: dict[str, Any],
373
+ _do_reset: bool = True,
374
+ ) -> None:
375
+ """Stream-upload to the file named by *stage_location* (a file, not a directory)."""
376
+ from .._internal.snowpark_backcompat_helpers import put_get_options, unquoted_stage_ref
377
+
378
+ stage_dir, _, filename = unquoted_stage_ref(stage_location).rpartition("/")
379
+ sql = f"PUT file://{filename} {stage_dir} {put_get_options(options)}"
380
+ self._execute_file_transfer(sql.rstrip(), _do_reset=_do_reset, file_stream=input_stream)
381
+
382
+ @snowpark_compat
383
+ @backward_compatibility
384
+ def _download_stream(self, stage_location: str, decompress: bool = False) -> BinaryIO:
385
+ """Delegate to :meth:`download_stream`, adding the ``@`` prefix Snowpark omits."""
386
+ from .._internal.snowpark_backcompat_helpers import unquoted_stage_ref
387
+
388
+ return self.download_stream(unquoted_stage_ref(stage_location), decompress)
389
+
390
+ def _execute_file_transfer(
391
+ self,
392
+ sql: str,
393
+ *,
394
+ _do_reset: bool,
395
+ file_stream: BinaryIO | None = None,
396
+ ) -> None:
397
+ """Run a synthesized PUT/GET, resetting the cursor first unless told not to."""
398
+ if _do_reset:
399
+ self.execute(sql, file_stream=file_stream)
400
+ else:
401
+ self._execute(sql, file_stream=file_stream)
402
+
403
+ def _execute_query(self, stmt_handle: StatementHandle, bindings: QueryBindings | None) -> ExecuteQueryResponse:
404
+ """Execute query and return ExecuteQueryResponse (single or multi)."""
405
+ try:
406
+ return core_driver.statement_execute_query(stmt_handle=stmt_handle, bindings=bindings)
407
+ except ProgrammingError as exc:
408
+ self._query_result = QueryResult.from_programming_error(exc)
409
+ raise
410
+
411
+ def _handle_multi_statement_response(
412
+ self, result: MultiStatementResult, query: str | None, request_id: str | None
413
+ ) -> None:
414
+ self._multi_statement = MultiStatementQueryResultState.from_result(result, request_id)
415
+
416
+ # Edge case: empty multi-statement result
417
+ if self._multi_statement is None:
418
+ self._query_result = QueryResult(query=query, request_id=request_id)
419
+ return
420
+
421
+ first_qid = self._multi_statement.advance() # always non-None: from_result() guarantees non-empty children
422
+ # already populate cursor with first child query results
423
+ rs_response = self._fetch_result_set_by_query_id(first_qid) # type: ignore[arg-type]
424
+ self._apply_result_set(rs_response, query, request_id)
425
+
426
+ def _apply_result_set(
427
+ self, rs_response: ResultSetResponse, query: str | None, request_id: str | None = None
428
+ ) -> None:
429
+ self._result_set.replace(rs_response.result_set_handle)
430
+ self._query_result = QueryResult.from_result_set_response(rs_response, query, request_id)
431
+
432
+ def _fetch_result_set_by_query_id(self, query_id: str) -> ResultSetResponse:
433
+ """Fetch a ResultSetResponse (handle + descriptor) for a given query ID."""
434
+ try:
435
+ return core_driver.connection_get_result_set(
436
+ conn_handle=self._connection.conn_handle, # type: ignore[arg-type]
437
+ query_id=query_id,
438
+ )
439
+ except Exception as exc:
440
+ if isinstance(exc, ProgrammingError):
441
+ raise
442
+ raise ProgrammingError(msg=f"Failed to fetch result set for query_id={query_id}: {exc}") from exc
443
+
444
+ def _prepare(self, stmt_handle: StatementHandle, bindings: QueryBindings | None) -> PrepareResult | None:
445
+ try:
446
+ return core_driver.statement_prepare(stmt_handle=stmt_handle, bindings=bindings).result
447
+ except ProgrammingError as exc:
448
+ self._query_result = QueryResult.from_programming_error(exc)
449
+ raise
450
+
451
+ def _executemany_per_row(
452
+ self,
453
+ operation: str,
454
+ seq_of_parameters: Sequence[Sequence[Any] | dict[str, Any]],
455
+ _force_qmark_paramstyle: bool = False,
456
+ num_statements: int | None = None,
457
+ ) -> None:
458
+ """Execute each parameter set individually and aggregate rowcount.
459
+
460
+ Used by ``executemany`` whenever array binding does not apply: client-side
461
+ paramstyles, dict parameters, or statements the server reports as not
462
+ array-bindable (e.g. UPDATE, DELETE, MERGE).
463
+ """
464
+ statement_parameters = None
465
+ if num_statements is not None:
466
+ statement_parameters = self._collect_statement_params(
467
+ skip_upload_on_content_match=False,
468
+ num_statements=num_statements,
469
+ )
470
+ self.reset()
471
+ total_rowcount = 0
472
+ unknown_rowcount = False
473
+ for params in seq_of_parameters:
474
+ self._execute(
475
+ operation,
476
+ params,
477
+ _force_qmark_paramstyle=_force_qmark_paramstyle,
478
+ statement_parameters=statement_parameters,
479
+ ) # no reset between calls
480
+ rc = self._query_result.rowcount
481
+ if rc is None or rc == -1:
482
+ unknown_rowcount = True
483
+ elif not unknown_rowcount:
484
+ total_rowcount += rc
485
+ # Per PEP 249, -1 indicates that the number of rows is unknown,
486
+ # but for backward compatibility it's set to None.
487
+ self._query_result.rowcount = None if unknown_rowcount else total_rowcount
488
+
489
+ @pep249
490
+ @api_telemetry
491
+ @requires_open
492
+ def executemany(
493
+ self,
494
+ operation: str,
495
+ seq_of_parameters: Iterable[Sequence[Any] | dict[str, Any]] | None = None,
496
+ num_statements: int | None = None,
497
+ *,
498
+ seqparams: Iterable[Sequence[Any] | dict[str, Any]] | None = None,
499
+ _force_qmark_paramstyle: bool = False,
500
+ ) -> Self:
501
+ """
502
+ Execute a database operation repeatedly for each element in seq_of_parameters.
503
+
504
+ For qmark/numeric paramstyles with sequence parameters, issues a describe
505
+ request first to read the server's ``arrayBindSupported`` flag (the same
506
+ mechanism used by the JDBC and ODBC drivers). If the server confirms array
507
+ binding is supported, all rows are transposed into column-major arrays and
508
+ sent in a single request; when the cell count meets
509
+ ``CLIENT_STAGE_ARRAY_BINDING_THRESHOLD`` the bindings are serialized to CSV
510
+ and staged. If the server reports array binding is not supported (e.g. for
511
+ UPDATE, DELETE, MERGE), each parameter set is executed individually with
512
+ scalar JSON binding.
513
+
514
+ For pyformat/format paramstyles and dict parameters, executes each row
515
+ individually with client-side interpolation.
516
+
517
+ Args:
518
+ operation (str): SQL statement (INSERT, UPDATE, DELETE, etc.)
519
+ seq_of_parameters: Iterable of parameter sequences or dicts. Generators
520
+ and other one-shot iterators are materialized so the first row can
521
+ be inspected and array-binding can walk the collection more than once.
522
+ num_statements (int, optional): Number of statements in a
523
+ multistatement query. Forwarded to each ``execute()`` call.
524
+ seqparams: Legacy alias for ``seq_of_parameters`` (kwarg-only).
525
+ Cannot be supplied together with ``seq_of_parameters``.
526
+ _force_qmark_paramstyle: If True, treat as qmark even when the
527
+ connection's paramstyle is pyformat/format.
528
+
529
+ Returns:
530
+ The cursor itself (``self``), so callers can read ``.sfqid`` after
531
+ execution — matching the legacy connector.
532
+
533
+ Raises:
534
+ InterfaceError: If parameter sequences have inconsistent lengths.
535
+ ProgrammingError: If both ``seq_of_parameters`` and ``seqparams`` are
536
+ supplied, or if ``seq_of_parameters`` is not iterable.
537
+ """
538
+ seq_of_parameters = _coerce_executemany_params(
539
+ _resolve_alias(seq_of_parameters, seqparams, "seq_of_parameters", "seqparams")
540
+ )
541
+
542
+ if not seq_of_parameters:
543
+ return self # Empty sequence - no-op per PEP 249
544
+
545
+ paramstyle = ParamStyle.QMARK if _force_qmark_paramstyle else self._connection.paramstyle
546
+ first_params = seq_of_parameters[0]
547
+
548
+ # Execute individually for:
549
+ # - Client-side binding (pyformat/format)
550
+ # - Dict parameters (server-side doesn't support named binding)
551
+ if paramstyle.is_client_side() or isinstance(first_params, dict):
552
+ # A caller-supplied count other than 1 is a multi-statement batch.
553
+ # Rewriting that into one INSERT would drop the later statements.
554
+ if paramstyle.is_client_side() and num_statements in (None, 1):
555
+ # INSERT with client-side binding: rewrite into a single
556
+ # multi-row INSERT to avoid one HTTP request per row.
557
+ rewritten = self._rewrite_multirow_insert(operation, seq_of_parameters)
558
+ if rewritten is not None:
559
+ # Values were already interpolated and escaped by
560
+ # ClientSideBindingConverter.interpolate_query — no further binding needed.
561
+ return self.execute(rewritten, num_statements=num_statements)
562
+ self._executemany_per_row(
563
+ operation,
564
+ seq_of_parameters,
565
+ _force_qmark_paramstyle,
566
+ num_statements=num_statements,
567
+ )
568
+ return self
569
+
570
+ # A caller-supplied count other than 1 is a multi-statement batch.
571
+ # Array binding sends one statement, so each row is executed on its own.
572
+ if num_statements not in (None, 1):
573
+ self._executemany_per_row(
574
+ operation,
575
+ seq_of_parameters,
576
+ _force_qmark_paramstyle,
577
+ num_statements=num_statements,
578
+ )
579
+ return self
580
+
581
+ # Validate row widths and pre-compute column-major layout before the server
582
+ # round-trip so InterfaceError from mismatched lengths surfaces early.
583
+ transposed = self._build_array_binding_params(operation, seq_of_parameters, first_params)
584
+
585
+ # Ask the server whether array binding is supported for this statement.
586
+ # This mirrors how JDBC (describeSqlIfNotTried) and the C/ODBC driver
587
+ # (_snowflake_execute_ex describe-only request) gate batch execution.
588
+ with statement(self.connection.conn_handle, operation) as stmt_handle: # type: ignore[arg-type]
589
+ prepare_result = self._prepare(stmt_handle, None)
590
+
591
+ if prepare_result is None or not prepare_result.array_bind_supported:
592
+ # Per-row fallback: server does not support array binding for this
593
+ # statement type (e.g. UPDATE, DELETE, MERGE).
594
+ self._executemany_per_row(
595
+ operation,
596
+ seq_of_parameters,
597
+ _force_qmark_paramstyle,
598
+ num_statements=num_statements,
599
+ )
600
+ return self
601
+
602
+ # Server confirmed array binding: use pre-computed column-major params.
603
+ return self.execute(
604
+ operation,
605
+ transposed,
606
+ _force_qmark_paramstyle=_force_qmark_paramstyle,
607
+ num_statements=num_statements,
608
+ )
609
+
610
+ @api_telemetry
611
+ @requires_open
612
+ def describe(
613
+ self,
614
+ operation: str,
615
+ parameters: Sequence[Any] | dict[str, Any] | None = None,
616
+ **kwargs: Any,
617
+ ) -> list[ResultMetadata] | None:
618
+ """Obtain the schema of the result without executing the query.
619
+
620
+ This method prepares the query on the server with describeOnly=true to obtain
621
+ column metadata without actually executing the query or returning data rows.
622
+
623
+ Args:
624
+ operation: SQL statement to describe
625
+ parameters: Parameters for the SQL statement (same as execute())
626
+ **kwargs: Additional keyword arguments (for future compatibility)
627
+
628
+ Returns:
629
+ List of ResultMetadata tuples describing result columns, or None if the
630
+ statement produces no result set (e.g., INSERT, UPDATE, DELETE, DDL).
631
+
632
+ Side effects:
633
+ - Updates cursor.description with the column metadata
634
+ """
635
+ self.reset()
636
+ query, binding_params = self._prepare_query(operation, parameters)
637
+
638
+ prepare_result: PrepareResult | None = None
639
+ with statement(self.connection.conn_handle, query) as stmt_handle: # type: ignore[arg-type]
640
+ bindings = self._build_query_bindings(binding_params, query) if binding_params is not None else None
641
+ prepare_result = self._prepare(stmt_handle, bindings)
642
+
643
+ self._query_result = QueryResult.from_prepare_result(prepare_result)
644
+
645
+ if self._query_result.description:
646
+ self._rownumber = -1
647
+
648
+ return self._query_result.description
649
+
650
+ @snowpark_compat
651
+ @backward_compatibility
652
+ @requires_open
653
+ def _describe_internal(
654
+ self,
655
+ operation: str,
656
+ parameters: Sequence[Any] | dict[str, Any] | None = None,
657
+ *,
658
+ params: Sequence[Any] | dict[str, Any] | None = None,
659
+ **kwargs: Any,
660
+ ) -> list[ResultMetadataV2] | None:
661
+ """Describe-only path returning new-format metadata.
662
+
663
+ Returns ``list[ResultMetadataV2]`` with ``vector_dimension`` and
664
+ ``fields`` populated. ``fields`` carries nested metadata for structured
665
+ types and is ``None`` for every other type.
666
+ """
667
+ parameters = _resolve_alias(parameters, params, "parameters", "params") # type: ignore[assignment]
668
+ self.reset()
669
+ query, binding_params = self._prepare_query(operation, parameters)
670
+ prepare_result: PrepareResult | None = None
671
+ with statement(self.connection.conn_handle, query) as stmt_handle: # type: ignore[arg-type]
672
+ bindings = self._build_query_bindings(binding_params, query) if binding_params is not None else None
673
+ prepare_result = self._prepare(stmt_handle, bindings)
674
+ self._query_result = QueryResult.from_prepare_result(prepare_result)
675
+ if self._query_result.description:
676
+ self._rownumber = -1
677
+ return self._query_result.description_v2
678
+
679
+ @property
680
+ @snowpark_compat
681
+ @with_prefetch_hook
682
+ def _query_result_format(self) -> str | None:
683
+ """Like the base property, but loads a deferred async result first."""
684
+ return super()._query_result_format
685
+
686
+ # ------------------------------------------------------------------
687
+ # Fetch – shared implementation
688
+ # Intentionally no @api_telemetry on fetch methods - they are hot paths.
689
+ # fetchone / fetchmany use @simplified_error_handling so the success path
690
+ # avoids a ContextVar round-trip per call.
691
+ # ------------------------------------------------------------------
692
+
693
+ def _prepare_fetch(self) -> None:
694
+ # Consciously skip @requires_open_cursor_not_connection and
695
+ # @with_prefetch_hook on fetch hot paths - both checks live here.
696
+ if self._closed:
697
+ raise InterfaceError(msg="Cursor is closed.", errno=ER_CURSOR_IS_CLOSED)
698
+ if self._prefetch_hook is not None:
699
+ self._prefetch_hook()
700
+
701
+ def _fetchone(self) -> Row | DictRow | None:
702
+ """Fetch the next row internally.
703
+
704
+ Return a dict if ``_use_dict_result`` is True, otherwise a tuple.
705
+ Concrete subclasses expose this through a type-safe ``fetchone``.
706
+ """
707
+ try:
708
+ self._prepare_fetch()
709
+ if not self._iterator:
710
+ self._iterator = self._create_row_iterator()
711
+ row: Row | DictRow = next(self._iterator)
712
+ self._rownumber += 1
713
+ return row
714
+ except StopIteration:
715
+ return None
716
+
717
+ @pep249
718
+ @simplified_error_handling
719
+ @abc.abstractmethod
720
+ def fetchone(self) -> Row | DictRow | None:
721
+ """Fetch the next row of a query result set."""
722
+
723
+ @pep249
724
+ @simplified_error_handling
725
+ def fetchmany(self, size: int | None = None) -> list[Any]:
726
+ """
727
+ Fetch the next set of rows of a query result.
728
+
729
+ Args:
730
+ size (int): Number of rows to fetch (defaults to arraysize)
731
+
732
+ Returns:
733
+ sequence: List of rows
734
+
735
+ Raises:
736
+ ProgrammingError: If the number of rows is not zero or positive number
737
+ """
738
+ self._prepare_fetch()
739
+ if size is None:
740
+ size = self.arraysize
741
+
742
+ if size < 0:
743
+ raise ProgrammingError(
744
+ msg=f"The number of rows is not zero or positive number: {size}", errno=ER_INVALID_VALUE
745
+ )
746
+
747
+ if size == 0:
748
+ return []
749
+
750
+ if not self._iterator:
751
+ self._iterator = self._create_row_iterator()
752
+ rows = self._iterator.fetch_many(size)
753
+ self._rownumber += len(rows)
754
+ return rows
755
+
756
+ @pep249
757
+ def fetchall(self) -> list[Any]:
758
+ """
759
+ Fetch all (remaining) rows of a query result.
760
+
761
+ Returns:
762
+ sequence: List of all remaining rows
763
+ """
764
+ self._prepare_fetch()
765
+ if not self._iterator:
766
+ self._iterator = self._create_row_iterator()
767
+ rows = self._iterator.fetch_all()
768
+ self._rownumber += len(rows)
769
+ return rows
770
+
771
+ # ------------------------------------------------------------------
772
+ # Iterator protocol
773
+ # ------------------------------------------------------------------
774
+
775
+ def _create_row_iterator(self) -> ArrowRowIterator:
776
+ stream_ptr = self._result_set.get_arrow_stream_ptr()
777
+ return create_row_iterator(
778
+ stream_ptr=stream_ptr,
779
+ context=ArrowConverterContext.create(self._connection),
780
+ use_dict_result=self._use_dict_result,
781
+ use_numpy=bool(self._connection.config.numpy),
782
+ use_core_arrow=bool(self._connection.config.use_core_arrow),
783
+ )
784
+
785
+ @pep249
786
+ def __iter__(self) -> Self:
787
+ """Return the cursor itself as an iterator."""
788
+ return self
789
+
790
+ def __next__(self) -> Row | DictRow:
791
+ """
792
+ Fetch the next row from the currently executed statement.
793
+
794
+ Returns:
795
+ sequence: Next row
796
+
797
+ Raises:
798
+ StopIteration: When no more rows are available
799
+ """
800
+ row = self.fetchone()
801
+ if row is None:
802
+ raise StopIteration
803
+ return row
804
+
805
+ @pep249
806
+ def next(self) -> Row | DictRow:
807
+ """Python 2 compatibility method."""
808
+ return self.__next__()
809
+
810
+ # ------------------------------------------------------------------
811
+ # PEP 249 optional / no-op methods
812
+ # ------------------------------------------------------------------
813
+
814
+ @pep249
815
+ @api_telemetry
816
+ @requires_open
817
+ @with_prefetch_hook
818
+ def nextset(self) -> SnowflakeCursorBase | None:
819
+ """
820
+ Skip to the next available result set, discarding remaining rows from current set.
821
+
822
+ This method is used for multi-statement queries where a single execute() produces
823
+ multiple result sets. Call nextset() to advance to the next query's results.
824
+
825
+ Loads a deferred async result first.
826
+
827
+ Returns:
828
+ SnowflakeCursorBase: Self if next set is available.
829
+ None: If no more result sets are available.
830
+
831
+ Raises:
832
+ InterfaceError: If cursor is closed.
833
+
834
+ Example:
835
+ cursor.set_statement_parameter("MULTI_STATEMENT_COUNT", 3)
836
+ cursor.execute("SELECT 1; SELECT 2; SELECT 3")
837
+ print(cursor.fetchone()) # (1,)
838
+ cursor.nextset()
839
+ print(cursor.fetchone()) # (2,)
840
+ cursor.nextset()
841
+ print(cursor.fetchone()) # (3,)
842
+ result = cursor.nextset() # None - no more results
843
+ """
844
+ if self._multi_statement is None:
845
+ return None
846
+
847
+ query_id = self._multi_statement.advance()
848
+ if query_id is None:
849
+ return None
850
+
851
+ # Detach multi-statement state so reset() doesn't clear it
852
+ ms = self._multi_statement
853
+ self._multi_statement = None
854
+ self.reset()
855
+ self._multi_statement = ms
856
+
857
+ rs_response = self._fetch_result_set_by_query_id(query_id)
858
+ self._apply_result_set(rs_response, query=None, request_id=ms.request_id)
859
+ self._rownumber = -1
860
+
861
+ return self
862
+
863
+ # ------------------------------------------------------------------
864
+ # Context manager
865
+ # ------------------------------------------------------------------
866
+
867
+ def __enter__(self) -> Self:
868
+ """Enter the runtime context for the cursor."""
869
+ return self
870
+
871
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
872
+ """Exit the runtime context for the cursor."""
873
+ self.close()
874
+
875
+ @api_telemetry
876
+ @requires_open_cursor_not_connection
877
+ def reset(self, closing: bool = False) -> None:
878
+ """Reset the result set.
879
+
880
+ Frees heavy result data (arrow streams, multi-statement state) while
881
+ for backward compatibility preserving metadata that the old driver
882
+ also keeps across resets: ``description``, ``rownumber``, ``sfqid``,
883
+ ``query``, and ``sqlstate``.
884
+
885
+ Also clears the ``messages`` list so that errors from previous
886
+ operations do not leak into the next one.
887
+
888
+ When the connection was created with ``reuse_results=True``, the result
889
+ handle is kept so a later fetch can re-read the same rows. ``close()``
890
+ always releases the handle.
891
+
892
+ Args:
893
+ closing: If True, do not reset rowcount,
894
+ see: SNOW-647539: Do not erase the rowcount information when closing the cursor.
895
+ If False, reset rowcount to None.
896
+ """
897
+ del self._messages[:]
898
+ self._query_result.reset(closing=closing)
899
+ if closing or self._connection._reuse_results is not True:
900
+ self._result_set.release()
901
+ self._iterator = None
902
+ self._binding_data = None
903
+ self._prefetch_hook = None
904
+ # Clear multistatement state
905
+ self._multi_statement = None
906
+
907
+ # ------------------------------------------------------------------
908
+ # Fetch – Arrow / Pandas
909
+ # ------------------------------------------------------------------
910
+
911
+ @requires_dependency(pyarrow)
912
+ @api_telemetry
913
+ @requires_open
914
+ @with_prefetch_hook
915
+ def fetch_arrow_batches(
916
+ self,
917
+ force_microsecond_precision: bool | None = None,
918
+ ) -> Iterator[Table]:
919
+ """Fetch Arrow Tables in batches.
920
+
921
+ Timestamp values stay at nanosecond precision unless
922
+ ``force_microsecond_precision`` is true. When the argument is omitted,
923
+ the connection's ``force_microsecond_precision`` is used.
924
+ """
925
+ force_microsecond_precision = self._resolve_force_microsecond_precision(force_microsecond_precision)
926
+ stream_ptr = self._result_set.get_arrow_stream_ptr()
927
+ iterator = create_table_iterator(
928
+ stream_ptr=stream_ptr,
929
+ context=ArrowConverterContext.create(self._connection),
930
+ number_to_decimal=self._connection.arrow_number_to_decimal,
931
+ force_microsecond_precision=force_microsecond_precision,
932
+ use_core_arrow=bool(self._connection.config.use_core_arrow),
933
+ )
934
+ for batch in iterator:
935
+ yield pyarrow.Table.from_batches([batch])
936
+
937
+ @requires_dependency(pyarrow)
938
+ @api_telemetry
939
+ @requires_open
940
+ @with_prefetch_hook
941
+ def fetch_arrow_all(
942
+ self,
943
+ force_return_table: bool = False,
944
+ force_microsecond_precision: bool | None = None,
945
+ ) -> Table | None:
946
+ """Fetch all results as a single Arrow Table.
947
+
948
+ Timestamp values stay at nanosecond precision unless
949
+ ``force_microsecond_precision`` is true. When the argument is omitted,
950
+ the connection's ``force_microsecond_precision`` is used.
951
+ """
952
+ force_microsecond_precision = self._resolve_force_microsecond_precision(force_microsecond_precision)
953
+ stream_ptr = self._result_set.get_arrow_stream_ptr()
954
+ iterator = create_table_iterator(
955
+ stream_ptr=stream_ptr,
956
+ context=ArrowConverterContext.create(self._connection),
957
+ number_to_decimal=self._connection.arrow_number_to_decimal,
958
+ force_microsecond_precision=force_microsecond_precision,
959
+ use_core_arrow=bool(self._connection.config.use_core_arrow),
960
+ )
961
+ return collect_arrow_table(
962
+ table_iterator=iterator,
963
+ columns_metadata=self._query_result.description,
964
+ force_return_table=force_return_table,
965
+ )
966
+
967
+ @requires_dependency(pandas)
968
+ @api_telemetry
969
+ @requires_open
970
+ def fetch_pandas_batches(self, **kwargs: Any) -> Iterator[DataFrame]:
971
+ """Fetch Pandas DataFrames in batches.
972
+
973
+ ``force_microsecond_precision`` overrides the connection setting for
974
+ the Arrow conversion when the caller passes it, and is taken from the
975
+ connection when omitted. Every other kwarg is forwarded to
976
+ ``pyarrow.Table.to_pandas`` (for example ``split_blocks``).
977
+ """
978
+ force_microsecond_precision = self._resolve_force_microsecond_precision(
979
+ kwargs.pop("force_microsecond_precision", None)
980
+ )
981
+ for table in self.fetch_arrow_batches(force_microsecond_precision=force_microsecond_precision):
982
+ yield table.to_pandas(**kwargs)
983
+
984
+ @requires_dependency(pandas)
985
+ @api_telemetry
986
+ @requires_open
987
+ def fetch_pandas_all(self, **kwargs: Any) -> DataFrame:
988
+ """Fetch all results as a single Pandas DataFrame.
989
+
990
+ ``force_microsecond_precision`` overrides the connection setting for
991
+ the Arrow conversion when the caller passes it, and is taken from the
992
+ connection when omitted. Every other kwarg is forwarded to
993
+ ``pyarrow.Table.to_pandas``.
994
+ """
995
+ force_microsecond_precision = self._resolve_force_microsecond_precision(
996
+ kwargs.pop("force_microsecond_precision", None)
997
+ )
998
+ table: Table = self.fetch_arrow_all(
999
+ force_return_table=True, force_microsecond_precision=force_microsecond_precision
1000
+ )
1001
+ return table.to_pandas(**kwargs)
1002
+
1003
+ # ------------------------------------------------------------------
1004
+ # Distributed fetch
1005
+ # ------------------------------------------------------------------
1006
+
1007
+ @api_telemetry
1008
+ @requires_open
1009
+ @with_prefetch_hook
1010
+ def get_result_batches(self) -> list[ResultBatch] | None:
1011
+ """Get the previously executed query's ResultBatches if available."""
1012
+ result_chunks = self._result_set.get_chunks()
1013
+ if result_chunks is None:
1014
+ return None
1015
+ return ResultBatch.from_chunks(
1016
+ list(result_chunks.chunks),
1017
+ self._query_result.description,
1018
+ self._connection,
1019
+ list(result_chunks.columns),
1020
+ )
1021
+
1022
+ # ------------------------------------------------------------------
1023
+ # Async query support
1024
+ # ------------------------------------------------------------------
1025
+
1026
+ @api_telemetry
1027
+ @requires_open
1028
+ def query_result(self, qid: str) -> SnowflakeCursorBase:
1029
+ """
1030
+ Fetch the result of a previously executed query by its Snowflake Query ID.
1031
+
1032
+ Resets the cursor and populates it with the results from the specified
1033
+ query, making them available through the standard fetch methods
1034
+ (fetchone, fetchall, fetch_arrow_all, etc.).
1035
+
1036
+ Args:
1037
+ qid: Snowflake Query ID (sfqid) of the previously executed query.
1038
+
1039
+ Returns:
1040
+ This cursor instance, now populated with the query results.
1041
+
1042
+ Raises:
1043
+ ProgrammingError: If the query ID is invalid, the query is still
1044
+ running, or the results are no longer available.
1045
+ """
1046
+ self.reset()
1047
+
1048
+ response = core_driver.connection_get_query_result(
1049
+ conn_handle=self._connection.conn_handle, # type: ignore[arg-type]
1050
+ query_id=qid,
1051
+ )
1052
+
1053
+ if response.HasField("multi"):
1054
+ self._handle_multi_statement_response(response.multi, query=None, request_id=None)
1055
+ else:
1056
+ rs_response = response.single
1057
+ self._apply_result_set(rs_response, query=None)
1058
+
1059
+ self._rownumber = -1
1060
+
1061
+ return self
1062
+
1063
+ @api_telemetry
1064
+ @requires_open
1065
+ def get_results_from_sfqid(self, sfqid: str) -> None:
1066
+ """Get results from a previously executed query.
1067
+
1068
+ Polls query status until completion, then loads results lazily
1069
+ on first fetch call.
1070
+
1071
+ Before the first fetch triggers the prefetch hook, result-dependent
1072
+ cursor attributes and methods such as ``description``, ``rowcount``,
1073
+ and ``fetch*`` are not populated and MUST NOT be relied upon.
1074
+
1075
+ Args:
1076
+ sfqid: Snowflake Query ID of the target query.
1077
+
1078
+ Raises:
1079
+ ProgrammingError: If the query has already failed at call time,
1080
+ or if it reaches a terminal error status while being polled
1081
+ for completion.
1082
+ DatabaseError: If the server stops returning status information
1083
+ while polling for query completion.
1084
+ """
1085
+ self.reset()
1086
+ self.connection.get_query_status_throw_if_error(sfqid)
1087
+ self._query_result.sfqid = sfqid
1088
+ waiter = QueryResultWaiter(self._connection, sfqid)
1089
+
1090
+ def prefetch_hook() -> None:
1091
+ waiter.wait()
1092
+ self._prefetch_hook = None
1093
+ self.query_result(sfqid)
1094
+
1095
+ self._prefetch_hook = prefetch_hook
1096
+
1097
+ @api_telemetry
1098
+ @requires_open
1099
+ def execute_async(
1100
+ self,
1101
+ command: str,
1102
+ params: Sequence[Any] | dict[str, Any] | None = None,
1103
+ num_statements: int | None = None,
1104
+ *,
1105
+ _statement_params: dict[str, Any] | None = None,
1106
+ **kwargs: Any,
1107
+ ) -> dict[str, str | None]:
1108
+ """Submit a query for async execution and return immediately with the query ID.
1109
+
1110
+ This is the first step in the async query lifecycle::
1111
+
1112
+ # 1. Submit the query
1113
+ result = cursor.execute_async("SELECT ...")
1114
+ query_id = result["queryId"]
1115
+
1116
+ # 2. Poll until complete
1117
+ status = connection.get_query_status(query_id)
1118
+
1119
+ # 3. Retrieve results
1120
+ cursor.get_results_from_sfqid(query_id)
1121
+
1122
+ Args:
1123
+ command: SQL statement to execute.
1124
+ params: Parameters for the operation (sequence or dict).
1125
+ num_statements: Number of statements in a multistatement query.
1126
+ _statement_params: Extra per-statement parameters (e.g. ``QUERY_TAG``)
1127
+ sent to Snowflake with this query only. Never persisted on the cursor.
1128
+
1129
+ Returns:
1130
+ dict with a ``queryId`` key containing the Snowflake Query ID.
1131
+ """
1132
+ statement_parameters = self._collect_statement_params(
1133
+ skip_upload_on_content_match=False,
1134
+ num_statements=num_statements,
1135
+ statement_params=_statement_params,
1136
+ )
1137
+ # TODO: deprecate returning the dict, return just the sfqid itself
1138
+ self.reset()
1139
+ return self._execute_async(command, params, statement_parameters=statement_parameters)
1140
+
1141
+ def _execute_async(
1142
+ self,
1143
+ command: str,
1144
+ params: Sequence[Any] | dict[str, Any] | None,
1145
+ *,
1146
+ statement_parameters: dict[str, Any] | None = None,
1147
+ ) -> dict[str, str | None]:
1148
+ query, binding_params = self._prepare_query(command, params)
1149
+
1150
+ with statement(self._connection.conn_handle, query) as stmt_handle: # type: ignore[arg-type]
1151
+ self._apply_statement_parameters(stmt_handle, statement_parameters)
1152
+ bindings = self._build_query_bindings(binding_params, query) if binding_params is not None else None
1153
+ response = core_driver.statement_execute_async(stmt_handle=stmt_handle, bindings=bindings)
1154
+
1155
+ query_id = response.query_id or None
1156
+ request_id = response.request_id or None
1157
+ self._query_result = QueryResult(sfqid=query_id, request_id=request_id)
1158
+
1159
+ return {"queryId": query_id}
1160
+
1161
+ @api_telemetry
1162
+ @requires_open
1163
+ def abort_query(self, qid: str) -> bool:
1164
+ """Abort a running query."""
1165
+ response = core_driver.connection_abort_query(
1166
+ conn_handle=self._connection.conn_handle, # type: ignore[arg-type]
1167
+ query_id=qid,
1168
+ )
1169
+ return response.outcome == ABORT_QUERY_OUTCOME_ABORTED