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,732 @@
1
+ """Shared cursor base mixin for sync and async cursor implementations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import abc
6
+ import ctypes
7
+ import re
8
+ import uuid
9
+ import warnings
10
+
11
+ from collections.abc import Sequence
12
+ from typing import TYPE_CHECKING, Any, cast
13
+
14
+ from ..._common.extras import check_dependency, pandas, pyarrow
15
+ from ...constants import SessionParameterName, StatementParameterName
16
+ from ...errors import (
17
+ Error,
18
+ ErrorDetails,
19
+ InterfaceError,
20
+ NotSupportedError,
21
+ OperationalError,
22
+ ProgrammingError,
23
+ StructuredErrorHandler,
24
+ )
25
+ from ..api_client.client_api import core_driver
26
+ from ..binding_converters import (
27
+ BindingConverterBase,
28
+ ClientSideBindingConverter,
29
+ CsvBindingConverter,
30
+ JsonBindingConverter,
31
+ ParamStyle,
32
+ parse_stage_binding_threshold,
33
+ )
34
+ from ..config_utils import create_config_setting
35
+ from ..decorators import api_telemetry, backward_compatibility, pep249, snowpark_compat
36
+ from ..error_kinds import is_stage_binding_disabled
37
+ from ..errorcode import ER_FAILED_TO_REWRITE_MULTI_ROW_INSERT, ER_INVALID_VALUE
38
+ from ..errorhandler import ErrorHandlerMixin
39
+ from ..protobuf_gen.database_driver_v1_pb2 import BinaryDataPtr, ConfigSetting, QueryBindings, StatementHandle
40
+ from ..text_utils import extract_values_clause
41
+ from .decorators import requires_open
42
+ from .query_result import MultiStatementQueryResultState, QueryResult
43
+ from .result_metadata import QueryResultStats, ResultMetadata
44
+
45
+
46
+ if TYPE_CHECKING:
47
+ from ...aio.connection import Connection as AsyncConnection
48
+ from ...connection import Connection
49
+ from .result_metadata import ResultMetadataV2
50
+
51
+
52
+ class CursorBaseMixin(ErrorHandlerMixin, abc.ABC):
53
+ """Zero-I/O cursor members shared by sync and async base cursor classes."""
54
+
55
+ _BARE_DESC_SQL_RE = re.compile(r"desc(?:ribe)?\s+([\w_]+)\s*;?\s*$", re.IGNORECASE)
56
+ _INSERT_SQL_RE = re.compile(r"^insert\s+into", re.IGNORECASE)
57
+ _COMMENT_SQL_RE = re.compile(r"/\*.*\*/")
58
+
59
+ # Set by subclass ``__init__`` before ``super().__init__()``.
60
+ _connection: Connection | AsyncConnection
61
+ _closed: bool
62
+ _messages: list[tuple[type[Error], ErrorDetails]]
63
+ _errorhandler: StructuredErrorHandler
64
+ _arraysize: int
65
+ _query_result: QueryResult
66
+ _rownumber: int
67
+ _multi_statement: MultiStatementQueryResultState | None
68
+ _statement_parameters: dict[str, Any]
69
+ _binding_data: None | bytes
70
+
71
+ def __init__(self) -> None:
72
+ self._closed = False
73
+ self._messages = []
74
+ self._errorhandler = Error.default_errorhandler
75
+
76
+ # -- PEP 249 cursor configuration (persists for cursor lifetime) --
77
+ self._arraysize = 1
78
+
79
+ # -- Query result state (replaced on execute, mutated on reset/consume) --
80
+ self._query_result = QueryResult()
81
+
82
+ # Cursor navigation position — mutable to avoid allocation per fetchone
83
+ self._rownumber = -1
84
+
85
+ # -- Multi-statement navigation (set by _handle_multi_statement_response, cleared on reset) --
86
+ self._multi_statement = None
87
+
88
+ # -- Statement parameters (persists until explicitly changed) --
89
+ self._statement_parameters = {}
90
+
91
+ # Keep binding data reference to prevent garbage collection while Rust uses it
92
+ self._binding_data = None
93
+
94
+ # ------------------------------------------------------------------
95
+ # PEP 249 attributes
96
+ # ------------------------------------------------------------------
97
+
98
+ @property
99
+ @pep249
100
+ @api_telemetry
101
+ def description(self) -> list[ResultMetadata] | None:
102
+ """
103
+ Read-only attribute describing the result columns of a query.
104
+
105
+ Returns a sequence of 7-item tuples, each containing:
106
+ - name: Column name (str)
107
+ - type_code: Integer type code (int)
108
+ - display_size: Display size in characters (int | None)
109
+ - internal_size: Internal size in characters (int | None)
110
+ - precision: Precision for numeric types (int | None)
111
+ - scale: Scale for numeric types (int | None)
112
+ - null_ok: True if column can contain NULLs (bool | None)
113
+
114
+ Returns None if no query has been executed or if the query didn't produce a result set.
115
+ """
116
+ return self._query_result.description
117
+
118
+ @property
119
+ @snowpark_compat
120
+ @backward_compatibility
121
+ def _description_internal(self) -> list[ResultMetadataV2] | None:
122
+ """New-format column metadata for the last executed statement.
123
+
124
+ Snowpark probes for this attribute and falls back to :attr:`description`
125
+ when it is absent, which loses ``fields`` and ``vector_dimension``.
126
+ """
127
+ return self._query_result.description_v2
128
+
129
+ @property
130
+ @snowpark_compat
131
+ def _query_result_format(self) -> str | None:
132
+ """``"arrow"`` or ``"json"`` for the last statement, or None before one
133
+ produced a result set. A format-less response reads as ``"json"``, as it
134
+ does on the legacy connector.
135
+
136
+ Snowpark reads this to decide whether results can be fetched as pandas.
137
+ """
138
+ return self._query_result.query_result_format
139
+
140
+ @property
141
+ @pep249
142
+ @api_telemetry
143
+ def rowcount(self) -> int | None:
144
+ """
145
+ Read-only attribute specifying the number of rows that the last
146
+ .execute*() produced or affected.
147
+
148
+ Returns:
149
+ int: Number of rows affected, or None if not determined
150
+ """
151
+ return self._query_result.rowcount
152
+
153
+ @property
154
+ @pep249
155
+ @api_telemetry
156
+ def arraysize(self) -> int:
157
+ """Number of rows to fetch at a time with .fetchmany(). Defaults to 1."""
158
+ return self._arraysize
159
+
160
+ @arraysize.setter
161
+ @api_telemetry
162
+ def arraysize(self, value: int) -> None:
163
+ self._arraysize = int(value)
164
+
165
+ @property
166
+ @pep249
167
+ @api_telemetry
168
+ def messages(self) -> list[tuple[type[Error], ErrorDetails]]:
169
+ """List of (exception class, exception value) tuples received from the database."""
170
+ return self._messages
171
+
172
+ @messages.setter
173
+ @api_telemetry
174
+ def messages(self, value: list[tuple[type[Error], ErrorDetails]]) -> None:
175
+ self._messages = value
176
+
177
+ # ------------------------------------------------------------------
178
+ # Execution metadata
179
+ # ------------------------------------------------------------------
180
+
181
+ @property
182
+ @api_telemetry
183
+ def query(self) -> str | None:
184
+ """
185
+ Read-only attribute containing the SQL text of the last executed or described query.
186
+
187
+ Returns:
188
+ str | None: The SQL query string, or None if no query has been executed or described
189
+ """
190
+ return self._query_result.query
191
+
192
+ @property
193
+ @api_telemetry
194
+ def sfqid(self) -> str | None:
195
+ """
196
+ Read-only attribute containing the Snowflake Query ID for the last executed or described query.
197
+
198
+ Returns:
199
+ str | None: Snowflake Query ID (UUID format), or None if no query has been executed or described
200
+ """
201
+ return self._query_result.sfqid
202
+
203
+ @property
204
+ @api_telemetry
205
+ def request_id(self) -> uuid.UUID | None:
206
+ """
207
+ Read-only attribute containing the client-generated ``requestId`` of the last query submission.
208
+
209
+ Returns:
210
+ uuid.UUID | None: Client request id, or None when unavailable.
211
+ """
212
+ value = self._query_result.request_id
213
+ if not value:
214
+ return None
215
+ return uuid.UUID(value)
216
+
217
+ @property
218
+ @snowpark_compat
219
+ @backward_compatibility(recommendation="Use the public `request_id` property instead.")
220
+ def _request_id(self) -> uuid.UUID | None:
221
+ return self.request_id
222
+
223
+ @property
224
+ @api_telemetry
225
+ def stats(self) -> QueryResultStats:
226
+ """Returns detailed row-level statistics for DML operations."""
227
+ return self._query_result.stats
228
+
229
+ @property
230
+ @pep249
231
+ @api_telemetry
232
+ def rownumber(self) -> int | None:
233
+ """The current 0-based index of the cursor in the result set, or ``None`` if indeterminate."""
234
+ return self._rownumber if self._rownumber >= 0 else None
235
+
236
+ @property
237
+ @api_telemetry
238
+ def sqlstate(self) -> str | None:
239
+ """The SQLSTATE code of the last executed operation."""
240
+ return self._query_result.sqlstate
241
+
242
+ @property
243
+ @api_telemetry
244
+ def multi_statement_parent_sfqid(self) -> str | None:
245
+ """
246
+ Read-only attribute containing the parent Snowflake Query ID for multi-statement queries.
247
+
248
+ Returns:
249
+ str | None: Parent query ID for multi-statement, or None for single statements.
250
+ """
251
+ return self._multi_statement.parent_qid if self._multi_statement else None
252
+
253
+ @property
254
+ @api_telemetry
255
+ def multi_statement_savedIds(self) -> list[str]:
256
+ """
257
+ Read-only attribute containing child query IDs for multi-statement queries.
258
+
259
+ Returns:
260
+ list[str]: List of child query IDs (empty list for single statements).
261
+ """
262
+ return self._multi_statement.child_query_ids if self._multi_statement else []
263
+
264
+ @property
265
+ @api_telemetry
266
+ def is_file_transfer(self) -> bool:
267
+ """Whether the last executed command was a PUT or GET file transfer."""
268
+ return self._query_result.is_file_transfer
269
+
270
+ def _stage_binding_threshold(self) -> int:
271
+ raw = self._connection._session_parameters[SessionParameterName.CLIENT_STAGE_ARRAY_BINDING_THRESHOLD]
272
+ return parse_stage_binding_threshold(raw)
273
+
274
+ def _should_use_csv_binding(self, parameters: Sequence[Any], threshold: int, query: str = "") -> bool:
275
+ """Return whether array bindings should be uploaded as CSV to a stage.
276
+
277
+ Stage binding applies only to INSERT statements with multi-row (array)
278
+ bindings whose cell count meets ``CLIENT_STAGE_ARRAY_BINDING_THRESHOLD``.
279
+ The server rejects CSV stage bindings for non-INSERT queries, matching
280
+ the legacy connector's behavior of gating on INSERT.
281
+ Scalar binds always use inline JSON regardless of threshold.
282
+
283
+ """
284
+ if not query.lstrip().upper().startswith("INSERT"):
285
+ return False
286
+ if not any(isinstance(value, list) for value in parameters):
287
+ return False
288
+ effective_cells = BindingConverterBase.effective_binding_cells(parameters)
289
+ if effective_cells == 0 or threshold <= 0:
290
+ return False
291
+ return effective_cells >= threshold
292
+
293
+ def _build_query_bindings(
294
+ self, parameters: Sequence[Any], query: str = "", *, force_json: bool = False
295
+ ) -> QueryBindings | None:
296
+ """Serialize parameters and build a QueryBindings protobuf message.
297
+
298
+ Converts Python parameter values to JSON or CSV, then wraps the result
299
+ in a zero-copy BinaryDataPtr so the Rust core can read the bytes
300
+ directly from Python memory.
301
+
302
+ The encoded bytes are stored on ``self._binding_data`` to prevent
303
+ garbage collection while Rust holds the pointer.
304
+
305
+ ``force_json`` bypasses stage-binding selection to rebuild inline JSON
306
+ bindings for a retry after the ``SYSTEM$BIND`` stage was rejected.
307
+
308
+ Returns:
309
+ QueryBindings with the serialized JSON or CSV, or None if parameters
310
+ serialize to nothing (e.g. empty list).
311
+ """
312
+ threshold = self._stage_binding_threshold()
313
+ use_csv = False if force_json else self._should_use_csv_binding(parameters, threshold, query)
314
+
315
+ if use_csv:
316
+ binding_bytes = CsvBindingConverter.serialize_parameters_to_csv(parameters)
317
+ if binding_bytes is None:
318
+ return None
319
+ else:
320
+ json_str, _ = JsonBindingConverter.serialize_parameters(parameters)
321
+ if json_str is None:
322
+ return None
323
+ binding_bytes = json_str.encode("utf-8")
324
+
325
+ self._binding_data = binding_bytes
326
+ length = len(binding_bytes)
327
+
328
+ ptr_value = ctypes.cast(ctypes.c_char_p(self._binding_data), ctypes.c_void_p).value
329
+ if ptr_value is None:
330
+ raise RuntimeError("Failed to obtain memory pointer for binding data")
331
+
332
+ ptr_bytes = ptr_value.to_bytes(8, byteorder="little", signed=False)
333
+ binary_data_ptr = BinaryDataPtr(value=ptr_bytes, length=length)
334
+ if use_csv:
335
+ return QueryBindings(csv=binary_data_ptr)
336
+ return QueryBindings(json=binary_data_ptr)
337
+
338
+ def _should_retry_with_inline_json(self, bindings: QueryBindings | None, exc: OperationalError) -> bool:
339
+ """Return whether *exc* should trigger a retry with inline JSON bindings.
340
+
341
+ Only a CSV (stage) attempt can hit a stage-binding-disabled error --
342
+ that upload only happens when ``bindings`` selected CSV, so a JSON
343
+ attempt is excluded even though ``is_stage_binding_disabled`` would
344
+ report True for it.
345
+ """
346
+ return bindings is not None and bindings.HasField("csv") and is_stage_binding_disabled(exc)
347
+
348
+ def _prepare_query(
349
+ self,
350
+ operation: str,
351
+ parameters: Sequence[Any] | dict[str, Any] | None,
352
+ _force_qmark_paramstyle: bool = False,
353
+ ) -> tuple[str, Sequence[Any] | None]:
354
+ """Prepare query and server-side binding parameters based on paramstyle.
355
+
356
+ Args:
357
+ operation: SQL statement
358
+ parameters: Parameters to bind (sequence or dict)
359
+ _force_qmark_paramstyle: If True, treat the call as qmark
360
+ regardless of the connection's configured paramstyle. Used by
361
+ callers (e.g. snowflake.core) that emit ``?`` placeholders
362
+ while connections may default to pyformat.
363
+
364
+ Returns:
365
+ Tuple of (query string, server-side binding parameters or None)
366
+
367
+ Raises:
368
+ ProgrammingError: If dict parameters used with server-side binding
369
+ """
370
+ match = self._BARE_DESC_SQL_RE.match(operation)
371
+ if match:
372
+ warnings.warn(
373
+ "Bare 'DESC <table>' and 'DESCRIBE <table>' statements are deprecated "
374
+ "and will be removed in a future release; use 'DESC TABLE <table>' instead.",
375
+ DeprecationWarning,
376
+ stacklevel=4,
377
+ )
378
+ operation = f"describe table {match.group(1)}"
379
+
380
+ if parameters is None:
381
+ return operation, None
382
+
383
+ paramstyle = ParamStyle.QMARK if _force_qmark_paramstyle else self._connection.paramstyle
384
+
385
+ if paramstyle.is_client_side():
386
+ # format paramstyle only supports positional params (%s), not named params
387
+ if paramstyle == ParamStyle.FORMAT and isinstance(parameters, dict):
388
+ raise ProgrammingError(
389
+ msg="Dict parameters not supported with format paramstyle. "
390
+ "Use pyformat paramstyle for named parameters, or use a sequence.",
391
+ errno=ER_INVALID_VALUE,
392
+ )
393
+ # Client-side binding: interpolate parameters into SQL string
394
+ query = ClientSideBindingConverter.interpolate_query(
395
+ operation,
396
+ parameters,
397
+ interpolate_empty_sequences=self._connection._interpolate_empty_sequences,
398
+ )
399
+ return query, None
400
+ else:
401
+ # Server-side binding: qmark or numeric
402
+ if isinstance(parameters, dict):
403
+ raise ProgrammingError(
404
+ msg="Named parameters (dict) not supported with qmark/numeric paramstyle. "
405
+ "Use pyformat paramstyle for named parameters.",
406
+ errno=ER_INVALID_VALUE,
407
+ )
408
+ return operation, parameters
409
+
410
+ def _format_query_for_log(self, query: str) -> str:
411
+ return self._connection._format_query_for_log(query)
412
+
413
+ def _prepare_call_proc_statement(self, procname: str, args: Any = None) -> tuple[str, Sequence[Any]]:
414
+ """Validate ``callproc`` arguments and build the CALL statement."""
415
+ if args is None:
416
+ args = ()
417
+ if isinstance(args, (str, bytes)):
418
+ raise TypeError(f"callproc args must be a sequence (e.g. list or tuple), not {type(args).__name__}")
419
+ if not isinstance(args, Sequence):
420
+ raise TypeError(f"callproc args must be a sequence (e.g. list or tuple), not {type(args).__name__}")
421
+ command = f"CALL {procname}({self._connection.paramstyle.placeholders(len(args))})"
422
+ return command, args
423
+
424
+ def _set_statement_parameter(self, key: str, value: Any) -> None:
425
+ """Store a statement-level parameter for the next execute."""
426
+ self._statement_parameters[key] = value
427
+
428
+ @api_telemetry
429
+ @requires_open
430
+ def set_statement_parameter(self, key: str, value: Any) -> None:
431
+ """Set a sticky statement-level parameter (e.g., MULTI_STATEMENT_COUNT).
432
+
433
+ Persists across `execute()` calls on this cursor until explicitly
434
+ changed. For per-call kwargs (one execute, no bleed across calls),
435
+ use the `statement_parameters` channel in `execute()` instead.
436
+
437
+ This must be called before execute() to take effect.
438
+
439
+ Args:
440
+ key: Parameter name (e.g., "MULTI_STATEMENT_COUNT").
441
+ value: Parameter value.
442
+
443
+ Raises:
444
+ InterfaceError: If cursor is closed.
445
+
446
+ Example:
447
+ cursor.set_statement_parameter("MULTI_STATEMENT_COUNT", 3)
448
+ cursor.execute("SELECT 1; SELECT 2; SELECT 3")
449
+ """
450
+ self._set_statement_parameter(key, value)
451
+
452
+ def _build_array_binding_params(
453
+ self,
454
+ operation: str,
455
+ seq_of_parameters: Sequence[Sequence[Any] | dict[str, Any]],
456
+ first_params: Sequence[Any] | dict[str, Any],
457
+ ) -> list[list[Any]]:
458
+ """Validate uniform row widths and transpose rows to column-major bind arrays.
459
+
460
+ Used by ``executemany`` server-side array binding (sequence params only;
461
+ dict params are handled by the caller before reaching here).
462
+ """
463
+ # Dict params were handled by the caller; only sequences reach here.
464
+ rows = cast("Sequence[Sequence[Any]]", seq_of_parameters)
465
+
466
+ # Error code 251007 (ER_INVALID_VALUE) matches reference driver behavior
467
+ first_len = len(first_params)
468
+ for params in rows:
469
+ if len(params) != first_len:
470
+ raise InterfaceError(
471
+ msg=f"Bulk data size don't match. expected: {first_len}, got: {len(params)}, command: {operation}",
472
+ errno=ER_INVALID_VALUE,
473
+ )
474
+
475
+ # Transpose from row-major to column-major format
476
+ # Input: [(row1_col1, row1_col2), (row2_col1, row2_col2), ...]
477
+ # Output: [[row1_col1, row2_col1, ...], [row1_col2, row2_col2, ...]]
478
+ return [[row[col_idx] for row in rows] for col_idx in range(first_len)]
479
+
480
+ def _rewrite_multirow_insert(
481
+ self,
482
+ operation: str,
483
+ seq_of_parameters: Sequence[Sequence[Any] | dict[str, Any]],
484
+ ) -> str | None:
485
+ """Rewrite a client-side-bound INSERT into a single multi-row INSERT.
486
+
487
+ Used by ``executemany`` for pyformat/format paramstyles so bulk
488
+ inserts issue one HTTP request instead of one per row, mirroring the
489
+ reference connector's rewrite. Returns ``None`` when ``operation`` is
490
+ not a rewritable ``INSERT INTO ... VALUES (...)`` statement (e.g.
491
+ UPDATE, DELETE, MERGE) — callers fall back to per-row execution.
492
+ """
493
+ stripped = operation.strip(" \t\n\r")
494
+ if not self._INSERT_SQL_RE.match(stripped):
495
+ return None
496
+
497
+ command_wo_comments = self._COMMENT_SQL_RE.sub("", stripped)
498
+ fmt = extract_values_clause(command_wo_comments)
499
+ if fmt is None:
500
+ raise ProgrammingError(
501
+ msg=(
502
+ "executemany() failed to rewrite INSERT as multi-row: no VALUES clause found."
503
+ " Ensure the statement has the form 'INSERT INTO ... VALUES (...)'."
504
+ ),
505
+ errno=ER_FAILED_TO_REWRITE_MULTI_ROW_INSERT,
506
+ )
507
+
508
+ values = [ClientSideBindingConverter.interpolate_query(fmt, params) for params in seq_of_parameters]
509
+ return stripped.replace(fmt, ",".join(values), 1)
510
+
511
+ def _collect_statement_params(
512
+ self,
513
+ *,
514
+ skip_upload_on_content_match: bool,
515
+ num_statements: int | None = None,
516
+ statement_params: dict[str, Any] | None = None,
517
+ ) -> dict[str, Any]:
518
+ """Collect per-call statement parameters for a single execute().
519
+
520
+ Merges the caller-supplied ``_statement_params`` bag (opaque passthrough,
521
+ e.g. ``DATE_INPUT_FORMAT``) with the driver's known per-call fold-ins
522
+ (``num_statements`` → MULTI_STATEMENT_COUNT, ``skip_upload_on_content_match``
523
+ → SKIP_UPLOAD_ON_CONTENT_MATCH). Scoped to one call and never written to
524
+ the cursor's sticky `_statement_parameters`.
525
+ """
526
+ # Seed from the caller's bag, then apply the driver fold-ins on top so
527
+ # they win on a key collision — matches legacy, which spreads
528
+ # ``{**_statement_params, "MULTI_STATEMENT_COUNT": num_statements}``.
529
+ params: dict[str, Any] = dict(statement_params or {})
530
+ if skip_upload_on_content_match:
531
+ params[StatementParameterName.SKIP_UPLOAD_ON_CONTENT_MATCH] = True
532
+ if num_statements is not None:
533
+ params[StatementParameterName.MULTI_STATEMENT_COUNT] = num_statements
534
+ return params
535
+
536
+ def _build_statement_parameters_options(
537
+ self, statement_parameters: dict[str, Any] | None = None
538
+ ) -> dict[str, ConfigSetting]:
539
+ """Build SetOptions from the cursor's sticky `_statement_parameters`
540
+ merged with per-call `statement_parameters`. Per-call wins on key
541
+ collision and is never persisted on the cursor.
542
+ """
543
+ merged_statement_params = {**self._statement_parameters, **(statement_parameters or {})}
544
+ options: dict[str, ConfigSetting] = {}
545
+ for key, value in merged_statement_params.items():
546
+ try:
547
+ setting = create_config_setting(value)
548
+ except TypeError as err:
549
+ raise ProgrammingError(msg=f"Cannot set parameter '{key}': {err}") from err
550
+ if setting is not None:
551
+ options[key] = setting
552
+ return options
553
+
554
+ def _apply_statement_parameters(
555
+ self,
556
+ stmt_handle: StatementHandle,
557
+ statement_parameters: dict[str, Any] | None = None,
558
+ ) -> None:
559
+ """Apply sticky `_statement_parameters` merged with per-call
560
+ `statement_parameters` via SetOptions RPC. Per-call wins on key
561
+ collision and is never persisted on the cursor.
562
+ """
563
+ options = self._build_statement_parameters_options(statement_parameters)
564
+ if not options:
565
+ return
566
+ core_driver.statement_set_options(stmt_handle=stmt_handle, options=options)
567
+
568
+ # ------------------------------------------------------------------
569
+ # Cursor state / navigation
570
+ # ------------------------------------------------------------------
571
+
572
+ @abc.abstractmethod
573
+ def reset(self, closing: bool = False) -> None:
574
+ """Release result-set resources; implemented by ``SnowflakeCursorBase``."""
575
+ ...
576
+
577
+ @api_telemetry
578
+ def is_closed(self) -> bool:
579
+ """
580
+ Check if the cursor is closed.
581
+
582
+ Returns:
583
+ bool: True if closed, False otherwise
584
+ """
585
+ return self._closed or self._connection.is_closed()
586
+
587
+ @pep249
588
+ @api_telemetry
589
+ def close(self) -> bool | None:
590
+ """Close the cursor now.
591
+
592
+ Returns whether the cursor was closed during this call.
593
+ """
594
+ try:
595
+ if self._closed:
596
+ return False
597
+ self.reset(closing=True)
598
+ self._closed = True
599
+ del self._messages[:]
600
+ return True
601
+ except Exception:
602
+ return None
603
+
604
+ @pep249
605
+ @api_telemetry
606
+ def scroll(self, value: int, mode: str = "relative") -> None:
607
+ """Scroll the cursor in the result set."""
608
+ raise NotSupportedError("scroll is not supported")
609
+
610
+ @pep249
611
+ @api_telemetry
612
+ def setinputsizes(self, sizes: Sequence[Any]) -> None:
613
+ """Not supported."""
614
+ return None
615
+
616
+ @pep249
617
+ @api_telemetry
618
+ def setoutputsize(self, size: int, column: int | None = None) -> None:
619
+ """Not supported."""
620
+ return None
621
+
622
+ @property
623
+ @pep249
624
+ @api_telemetry
625
+ def lastrowid(self) -> None:
626
+ """Snowflake does not support lastrowid; returns None per PEP 249."""
627
+ return None
628
+
629
+ # ------------------------------------------------------------------
630
+ # Session parameter accessors
631
+ # ------------------------------------------------------------------
632
+
633
+ @property
634
+ @api_telemetry
635
+ def timestamp_output_format(self) -> str | None:
636
+ """The session's ``TIMESTAMP_OUTPUT_FORMAT`` parameter value."""
637
+ return self._connection._session_parameters._get_string("TIMESTAMP_OUTPUT_FORMAT")
638
+
639
+ @property
640
+ @api_telemetry
641
+ def timestamp_ltz_output_format(self) -> str | None:
642
+ """The session's ``TIMESTAMP_LTZ_OUTPUT_FORMAT`` parameter value.
643
+
644
+ Falls back to :pyattr:`timestamp_output_format` when not set explicitly.
645
+ """
646
+ return (
647
+ self._connection._session_parameters._get_string("TIMESTAMP_LTZ_OUTPUT_FORMAT")
648
+ or self.timestamp_output_format
649
+ )
650
+
651
+ @property
652
+ @api_telemetry
653
+ def timestamp_tz_output_format(self) -> str | None:
654
+ """The session's ``TIMESTAMP_TZ_OUTPUT_FORMAT`` parameter value.
655
+
656
+ Falls back to :pyattr:`timestamp_output_format` when not set explicitly.
657
+ """
658
+ return (
659
+ self._connection._session_parameters._get_string("TIMESTAMP_TZ_OUTPUT_FORMAT")
660
+ or self.timestamp_output_format
661
+ )
662
+
663
+ @property
664
+ @api_telemetry
665
+ def timestamp_ntz_output_format(self) -> str | None:
666
+ """The session's ``TIMESTAMP_NTZ_OUTPUT_FORMAT`` parameter value.
667
+
668
+ Falls back to :pyattr:`timestamp_output_format` when not set explicitly.
669
+ """
670
+ return (
671
+ self._connection._session_parameters._get_string("TIMESTAMP_NTZ_OUTPUT_FORMAT")
672
+ or self.timestamp_output_format
673
+ )
674
+
675
+ @property
676
+ @api_telemetry
677
+ def date_output_format(self) -> str | None:
678
+ """The session's ``DATE_OUTPUT_FORMAT`` parameter value."""
679
+ return self._connection._session_parameters._get_string("DATE_OUTPUT_FORMAT")
680
+
681
+ @property
682
+ @api_telemetry
683
+ def time_output_format(self) -> str | None:
684
+ """The session's ``TIME_OUTPUT_FORMAT`` parameter value."""
685
+ return self._connection._session_parameters._get_string("TIME_OUTPUT_FORMAT")
686
+
687
+ @property
688
+ @api_telemetry
689
+ def timezone(self) -> str | None:
690
+ """The session's ``TIMEZONE`` parameter value."""
691
+ return self._connection._session_parameters._get_string("TIMEZONE")
692
+
693
+ @property
694
+ @api_telemetry
695
+ def binary_output_format(self) -> str | None:
696
+ """The session's ``BINARY_OUTPUT_FORMAT`` parameter value (``HEX`` or ``BASE64``)."""
697
+ return self._connection._session_parameters._get_string("BINARY_OUTPUT_FORMAT")
698
+
699
+ # ------------------------------------------------------------------
700
+ # Error handling
701
+ # ------------------------------------------------------------------
702
+
703
+ @property
704
+ @pep249
705
+ @api_telemetry
706
+ def errorhandler(self) -> StructuredErrorHandler:
707
+ """PEP 249 error handler for this cursor."""
708
+ return self._errorhandler
709
+
710
+ @errorhandler.setter
711
+ @api_telemetry
712
+ def errorhandler(self, value: StructuredErrorHandler | None) -> None:
713
+ if value is None:
714
+ raise ProgrammingError("Invalid errorhandler is specified")
715
+ self._errorhandler = value
716
+
717
+ def _resolve_force_microsecond_precision(self, override: bool | None) -> bool:
718
+ if override is None:
719
+ return bool(self._connection.force_microsecond_precision)
720
+ return bool(override)
721
+
722
+ # ------------------------------------------------------------------
723
+ # Optional dependency checks
724
+ # ------------------------------------------------------------------
725
+
726
+ @api_telemetry
727
+ def check_can_use_arrow_resultset(self) -> None:
728
+ check_dependency(pyarrow)
729
+
730
+ @api_telemetry
731
+ def check_can_use_pandas(self) -> None:
732
+ check_dependency(pandas)