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