snowflake-connector-python 3.16.0__cp313-cp313-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 (246) hide show
  1. snowflake/connector/__init__.py +96 -0
  2. snowflake/connector/_query_context_cache.py +276 -0
  3. snowflake/connector/_sql_util.py +48 -0
  4. snowflake/connector/_utils.py +70 -0
  5. snowflake/connector/arrow_context.py +203 -0
  6. snowflake/connector/auth/__init__.py +53 -0
  7. snowflake/connector/auth/_auth.py +586 -0
  8. snowflake/connector/auth/_http_server.py +220 -0
  9. snowflake/connector/auth/_oauth_base.py +400 -0
  10. snowflake/connector/auth/by_plugin.py +219 -0
  11. snowflake/connector/auth/default.py +36 -0
  12. snowflake/connector/auth/idtoken.py +72 -0
  13. snowflake/connector/auth/keypair.py +222 -0
  14. snowflake/connector/auth/no_auth.py +39 -0
  15. snowflake/connector/auth/oauth.py +49 -0
  16. snowflake/connector/auth/oauth_code.py +479 -0
  17. snowflake/connector/auth/oauth_credentials.py +66 -0
  18. snowflake/connector/auth/okta.py +335 -0
  19. snowflake/connector/auth/pat.py +39 -0
  20. snowflake/connector/auth/usrpwdmfa.py +69 -0
  21. snowflake/connector/auth/webbrowser.py +502 -0
  22. snowflake/connector/auth/workload_identity.py +94 -0
  23. snowflake/connector/azure_storage_client.py +276 -0
  24. snowflake/connector/backoff_policies.py +141 -0
  25. snowflake/connector/bind_upload_agent.py +89 -0
  26. snowflake/connector/cache.py +696 -0
  27. snowflake/connector/compat.py +127 -0
  28. snowflake/connector/config_manager.py +496 -0
  29. snowflake/connector/connection.py +2273 -0
  30. snowflake/connector/connection_diagnostic.py +776 -0
  31. snowflake/connector/constants.py +442 -0
  32. snowflake/connector/converter.py +785 -0
  33. snowflake/connector/converter_issue23517.py +87 -0
  34. snowflake/connector/converter_null.py +14 -0
  35. snowflake/connector/converter_snowsql.py +205 -0
  36. snowflake/connector/cursor.py +1951 -0
  37. snowflake/connector/dbapi.py +53 -0
  38. snowflake/connector/description.py +19 -0
  39. snowflake/connector/direct_file_operation_utils.py +88 -0
  40. snowflake/connector/encryption_util.py +220 -0
  41. snowflake/connector/errorcode.py +91 -0
  42. snowflake/connector/errors.py +615 -0
  43. snowflake/connector/externals_utils/__init__.py +0 -0
  44. snowflake/connector/externals_utils/externals_setup.py +27 -0
  45. snowflake/connector/feature.py +4 -0
  46. snowflake/connector/file_compression_type.py +118 -0
  47. snowflake/connector/file_lock.py +72 -0
  48. snowflake/connector/file_transfer_agent.py +1215 -0
  49. snowflake/connector/file_util.py +153 -0
  50. snowflake/connector/gcs_storage_client.py +474 -0
  51. snowflake/connector/gzip_decoder.py +85 -0
  52. snowflake/connector/local_storage_client.py +90 -0
  53. snowflake/connector/log_configuration.py +60 -0
  54. snowflake/connector/logging_utils/__init__.py +0 -0
  55. snowflake/connector/logging_utils/filters.py +72 -0
  56. snowflake/connector/nanoarrow_arrow_iterator.cp313-win_amd64.pyd +0 -0
  57. snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +60 -0
  58. snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
  59. snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +19 -0
  60. snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
  61. snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
  62. snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
  63. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.cpp +557 -0
  64. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.hpp +98 -0
  65. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +125 -0
  66. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +115 -0
  67. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.cpp +1005 -0
  68. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.hpp +127 -0
  69. snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +47 -0
  70. snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
  71. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +83 -0
  72. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
  73. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +97 -0
  74. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +72 -0
  75. snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +73 -0
  76. snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
  77. snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +30 -0
  78. snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
  79. snowflake/connector/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
  80. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
  81. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +45 -0
  82. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +71 -0
  83. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +56 -0
  84. snowflake/connector/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
  85. snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +75 -0
  86. snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
  87. snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +46 -0
  88. snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
  89. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
  90. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +95 -0
  91. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +57 -0
  92. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +36 -0
  93. snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +34 -0
  94. snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
  95. snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +19 -0
  96. snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
  97. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +36 -0
  98. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
  99. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +346 -0
  100. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +145 -0
  101. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
  102. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.cpp +65 -0
  103. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
  104. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +101 -0
  105. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +127 -0
  106. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
  107. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1908 -0
  108. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +215 -0
  109. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +125 -0
  110. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
  111. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +55 -0
  112. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +148 -0
  113. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
  114. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
  115. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
  116. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +162 -0
  117. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +97 -0
  118. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +239 -0
  119. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
  120. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +210 -0
  121. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +84 -0
  122. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +84 -0
  123. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
  124. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
  125. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +206 -0
  126. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +118 -0
  127. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
  128. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
  129. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
  130. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
  131. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
  132. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +162 -0
  133. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +898 -0
  134. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +190 -0
  135. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
  136. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
  137. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc.c +3204 -0
  138. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3217 -0
  139. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3618 -0
  140. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +379 -0
  141. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_arrow_iterator.pyx +256 -0
  142. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.c +512 -0
  143. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.h +350 -0
  144. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +33273 -0
  145. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +397 -0
  146. snowflake/connector/nanoarrow_cpp/Logging/logging.cpp +100 -0
  147. snowflake/connector/nanoarrow_cpp/Logging/logging.hpp +49 -0
  148. snowflake/connector/network.py +1297 -0
  149. snowflake/connector/ocsp_asn1crypto.py +447 -0
  150. snowflake/connector/ocsp_snowflake.py +1883 -0
  151. snowflake/connector/options.py +131 -0
  152. snowflake/connector/pandas_tools.py +732 -0
  153. snowflake/connector/proxy.py +43 -0
  154. snowflake/connector/py.typed +0 -0
  155. snowflake/connector/result_batch.py +786 -0
  156. snowflake/connector/result_set.py +319 -0
  157. snowflake/connector/s3_storage_client.py +605 -0
  158. snowflake/connector/secret_detector.py +181 -0
  159. snowflake/connector/sf_dirs.py +64 -0
  160. snowflake/connector/sfbinaryformat.py +35 -0
  161. snowflake/connector/sfdatetime.py +345 -0
  162. snowflake/connector/snow_logging.py +144 -0
  163. snowflake/connector/sqlstate.py +9 -0
  164. snowflake/connector/ssd_internal_keys.py +30 -0
  165. snowflake/connector/ssl_wrap_socket.py +134 -0
  166. snowflake/connector/storage_client.py +478 -0
  167. snowflake/connector/telemetry.py +249 -0
  168. snowflake/connector/telemetry_oob.py +543 -0
  169. snowflake/connector/test_util.py +30 -0
  170. snowflake/connector/time_util.py +159 -0
  171. snowflake/connector/token_cache.py +402 -0
  172. snowflake/connector/tool/__init__.py +0 -0
  173. snowflake/connector/tool/dump_certs.py +57 -0
  174. snowflake/connector/tool/dump_ocsp_response.py +139 -0
  175. snowflake/connector/tool/dump_ocsp_response_cache.py +194 -0
  176. snowflake/connector/tool/probe_connection.py +69 -0
  177. snowflake/connector/url_util.py +49 -0
  178. snowflake/connector/util_text.py +301 -0
  179. snowflake/connector/vendored/__init__.py +3 -0
  180. snowflake/connector/vendored/requests/LICENSE +175 -0
  181. snowflake/connector/vendored/requests/__init__.py +169 -0
  182. snowflake/connector/vendored/requests/__version__.py +14 -0
  183. snowflake/connector/vendored/requests/_internal_utils.py +50 -0
  184. snowflake/connector/vendored/requests/adapters.py +537 -0
  185. snowflake/connector/vendored/requests/api.py +157 -0
  186. snowflake/connector/vendored/requests/auth.py +315 -0
  187. snowflake/connector/vendored/requests/certs.py +17 -0
  188. snowflake/connector/vendored/requests/compat.py +79 -0
  189. snowflake/connector/vendored/requests/cookies.py +561 -0
  190. snowflake/connector/vendored/requests/exceptions.py +140 -0
  191. snowflake/connector/vendored/requests/help.py +134 -0
  192. snowflake/connector/vendored/requests/hooks.py +33 -0
  193. snowflake/connector/vendored/requests/models.py +1033 -0
  194. snowflake/connector/vendored/requests/sessions.py +833 -0
  195. snowflake/connector/vendored/requests/status_codes.py +128 -0
  196. snowflake/connector/vendored/requests/structures.py +99 -0
  197. snowflake/connector/vendored/requests/utils.py +1093 -0
  198. snowflake/connector/vendored/urllib3/LICENSE.txt +21 -0
  199. snowflake/connector/vendored/urllib3/__init__.py +85 -0
  200. snowflake/connector/vendored/urllib3/_collections.py +355 -0
  201. snowflake/connector/vendored/urllib3/_version.py +2 -0
  202. snowflake/connector/vendored/urllib3/connection.py +572 -0
  203. snowflake/connector/vendored/urllib3/connectionpool.py +1137 -0
  204. snowflake/connector/vendored/urllib3/contrib/__init__.py +0 -0
  205. snowflake/connector/vendored/urllib3/contrib/_appengine_environ.py +36 -0
  206. snowflake/connector/vendored/urllib3/contrib/_securetransport/__init__.py +0 -0
  207. snowflake/connector/vendored/urllib3/contrib/_securetransport/bindings.py +519 -0
  208. snowflake/connector/vendored/urllib3/contrib/_securetransport/low_level.py +397 -0
  209. snowflake/connector/vendored/urllib3/contrib/appengine.py +314 -0
  210. snowflake/connector/vendored/urllib3/contrib/ntlmpool.py +130 -0
  211. snowflake/connector/vendored/urllib3/contrib/pyopenssl.py +509 -0
  212. snowflake/connector/vendored/urllib3/contrib/securetransport.py +920 -0
  213. snowflake/connector/vendored/urllib3/contrib/socks.py +216 -0
  214. snowflake/connector/vendored/urllib3/exceptions.py +323 -0
  215. snowflake/connector/vendored/urllib3/fields.py +274 -0
  216. snowflake/connector/vendored/urllib3/filepost.py +98 -0
  217. snowflake/connector/vendored/urllib3/packages/__init__.py +0 -0
  218. snowflake/connector/vendored/urllib3/packages/backports/__init__.py +0 -0
  219. snowflake/connector/vendored/urllib3/packages/backports/makefile.py +51 -0
  220. snowflake/connector/vendored/urllib3/packages/backports/weakref_finalize.py +155 -0
  221. snowflake/connector/vendored/urllib3/packages/six.py +1076 -0
  222. snowflake/connector/vendored/urllib3/poolmanager.py +540 -0
  223. snowflake/connector/vendored/urllib3/request.py +191 -0
  224. snowflake/connector/vendored/urllib3/response.py +885 -0
  225. snowflake/connector/vendored/urllib3/util/__init__.py +49 -0
  226. snowflake/connector/vendored/urllib3/util/connection.py +156 -0
  227. snowflake/connector/vendored/urllib3/util/proxy.py +57 -0
  228. snowflake/connector/vendored/urllib3/util/queue.py +22 -0
  229. snowflake/connector/vendored/urllib3/util/request.py +146 -0
  230. snowflake/connector/vendored/urllib3/util/response.py +107 -0
  231. snowflake/connector/vendored/urllib3/util/retry.py +620 -0
  232. snowflake/connector/vendored/urllib3/util/ssl_.py +495 -0
  233. snowflake/connector/vendored/urllib3/util/ssl_match_hostname.py +159 -0
  234. snowflake/connector/vendored/urllib3/util/ssltransport.py +221 -0
  235. snowflake/connector/vendored/urllib3/util/timeout.py +271 -0
  236. snowflake/connector/vendored/urllib3/util/url.py +435 -0
  237. snowflake/connector/vendored/urllib3/util/wait.py +152 -0
  238. snowflake/connector/version.py +3 -0
  239. snowflake/connector/wif_util.py +407 -0
  240. snowflake_connector_python-3.16.0.dist-info/METADATA +1475 -0
  241. snowflake_connector_python-3.16.0.dist-info/RECORD +246 -0
  242. snowflake_connector_python-3.16.0.dist-info/WHEEL +5 -0
  243. snowflake_connector_python-3.16.0.dist-info/entry_points.txt +4 -0
  244. snowflake_connector_python-3.16.0.dist-info/licenses/LICENSE.txt +202 -0
  245. snowflake_connector_python-3.16.0.dist-info/licenses/NOTICE +8 -0
  246. snowflake_connector_python-3.16.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1951 @@
1
+ #!/usr/bin/env python
2
+ from __future__ import annotations
3
+
4
+ import collections
5
+ import logging
6
+ import os
7
+ import re
8
+ import signal
9
+ import sys
10
+ import time
11
+ import uuid
12
+ import warnings
13
+ from enum import Enum
14
+ from logging import getLogger
15
+ from threading import Lock
16
+ from types import TracebackType
17
+ from typing import (
18
+ IO,
19
+ TYPE_CHECKING,
20
+ Any,
21
+ Callable,
22
+ Iterator,
23
+ Literal,
24
+ NamedTuple,
25
+ NoReturn,
26
+ Sequence,
27
+ TypeVar,
28
+ overload,
29
+ )
30
+
31
+ from typing_extensions import Self
32
+
33
+ from snowflake.connector.result_batch import create_batches_from_response
34
+ from snowflake.connector.result_set import ResultSet
35
+
36
+ from . import compat
37
+ from ._sql_util import get_file_transfer_type
38
+ from ._utils import (
39
+ REQUEST_ID_STATEMENT_PARAM_NAME,
40
+ _TrackedQueryCancellationTimer,
41
+ is_uuid4,
42
+ )
43
+ from .bind_upload_agent import BindUploadAgent, BindUploadError
44
+ from .constants import (
45
+ CMD_TYPE_DOWNLOAD,
46
+ CMD_TYPE_UPLOAD,
47
+ FIELD_NAME_TO_ID,
48
+ PARAMETER_PYTHON_CONNECTOR_QUERY_RESULT_FORMAT,
49
+ FileTransferType,
50
+ QueryStatus,
51
+ )
52
+ from .errorcode import (
53
+ ER_CURSOR_IS_CLOSED,
54
+ ER_FAILED_PROCESSING_PYFORMAT,
55
+ ER_FAILED_TO_REWRITE_MULTI_ROW_INSERT,
56
+ ER_INVALID_VALUE,
57
+ ER_NO_ARROW_RESULT,
58
+ ER_NO_PYARROW,
59
+ ER_NO_PYARROW_SNOWSQL,
60
+ ER_NOT_POSITIVE_SIZE,
61
+ ER_UNSUPPORTED_METHOD,
62
+ )
63
+ from .errors import (
64
+ DatabaseError,
65
+ Error,
66
+ IntegrityError,
67
+ InterfaceError,
68
+ NotSupportedError,
69
+ ProgrammingError,
70
+ )
71
+ from .options import installed_pandas
72
+ from .sqlstate import SQLSTATE_FEATURE_NOT_SUPPORTED
73
+ from .telemetry import TelemetryData, TelemetryField
74
+ from .time_util import get_time_millis
75
+
76
+ if TYPE_CHECKING: # pragma: no cover
77
+ from pandas import DataFrame
78
+ from pyarrow import Table
79
+
80
+ from .connection import SnowflakeConnection
81
+ from .file_transfer_agent import SnowflakeProgressPercentage
82
+ from .result_batch import ResultBatch
83
+
84
+ T = TypeVar("T", bound=collections.abc.Sequence)
85
+
86
+ logger = getLogger(__name__)
87
+
88
+
89
+ if not installed_pandas:
90
+ logger.debug(
91
+ "Failed to import pyarrow or pandas. Cannot use pandas fetch API. Please "
92
+ "install snowflake-connector-python with the `pandas` extra to use these "
93
+ "features."
94
+ )
95
+
96
+
97
+ try:
98
+ from .nanoarrow_arrow_iterator import PyArrowIterator # NOQA
99
+
100
+ CAN_USE_ARROW_RESULT_FORMAT = True
101
+ except ImportError as e: # pragma: no cover
102
+ logger.warning(
103
+ f"Failed to import ArrowResult. No Apache Arrow result set format can be used. ImportError: {e}",
104
+ )
105
+ CAN_USE_ARROW_RESULT_FORMAT = False
106
+
107
+ STATEMENT_TYPE_ID_DML = 0x3000
108
+ STATEMENT_TYPE_ID_INSERT = STATEMENT_TYPE_ID_DML + 0x100
109
+ STATEMENT_TYPE_ID_UPDATE = STATEMENT_TYPE_ID_DML + 0x200
110
+ STATEMENT_TYPE_ID_DELETE = STATEMENT_TYPE_ID_DML + 0x300
111
+ STATEMENT_TYPE_ID_MERGE = STATEMENT_TYPE_ID_DML + 0x400
112
+ STATEMENT_TYPE_ID_MULTI_TABLE_INSERT = STATEMENT_TYPE_ID_DML + 0x500
113
+
114
+ STATEMENT_TYPE_ID_DML_SET = frozenset(
115
+ [
116
+ STATEMENT_TYPE_ID_DML,
117
+ STATEMENT_TYPE_ID_INSERT,
118
+ STATEMENT_TYPE_ID_UPDATE,
119
+ STATEMENT_TYPE_ID_DELETE,
120
+ STATEMENT_TYPE_ID_MERGE,
121
+ STATEMENT_TYPE_ID_MULTI_TABLE_INSERT,
122
+ ]
123
+ )
124
+
125
+ DESC_TABLE_RE = re.compile(r"desc(?:ribe)?\s+([\w_]+)\s*;?\s*$", flags=re.IGNORECASE)
126
+
127
+ LOG_MAX_QUERY_LENGTH = 80
128
+
129
+ ASYNC_NO_DATA_MAX_RETRY = 24
130
+ ASYNC_RETRY_PATTERN = [1, 1, 2, 3, 4, 8, 10]
131
+
132
+
133
+ class _NanoarrowUsage(str, Enum):
134
+ # follow the session parameter to use nanoarrow converter or not
135
+ FOLLOW_SESSION_PARAMETER = "follow_session_parameter"
136
+ # ignore the session parameter, use nanoarrow converter
137
+ ENABLE_NANOARROW = "enable_nanoarrow"
138
+ # ignore the session parameter, do not use nanoarrow converter
139
+ DISABLE_NANOARROW = "disable_nanoarrow"
140
+
141
+
142
+ class ResultMetadata(NamedTuple):
143
+ name: str
144
+ type_code: int
145
+ display_size: int | None
146
+ internal_size: int | None
147
+ precision: int | None
148
+ scale: int | None
149
+ is_nullable: bool
150
+
151
+ @classmethod
152
+ def from_column(cls, col: dict[str, Any]):
153
+ """Initializes a ResultMetadata object from the column description in the query response."""
154
+ type_code = FIELD_NAME_TO_ID[
155
+ (
156
+ col["extTypeName"].upper()
157
+ if col.get("extTypeName")
158
+ else col["type"].upper()
159
+ )
160
+ ]
161
+
162
+ return cls(
163
+ col["name"],
164
+ type_code,
165
+ None,
166
+ col["length"],
167
+ col["precision"],
168
+ col["scale"],
169
+ col["nullable"],
170
+ )
171
+
172
+
173
+ class ResultMetadataV2:
174
+ """ResultMetadataV2 represents the type information of a single column.
175
+
176
+ It is a replacement for ResultMetadata that contains additional attributes, currently
177
+ `vector_dimension` and `fields`. This class will be unified with ResultMetadata in the
178
+ near future.
179
+ """
180
+
181
+ def __init__(
182
+ self,
183
+ name: str,
184
+ type_code: int,
185
+ is_nullable: bool,
186
+ display_size: int | None = None,
187
+ internal_size: int | None = None,
188
+ precision: int | None = None,
189
+ scale: int | None = None,
190
+ vector_dimension: int | None = None,
191
+ fields: list[ResultMetadataV2] | None = None,
192
+ ):
193
+ self._name = name
194
+ self._type_code = type_code
195
+ self._is_nullable = is_nullable
196
+ self._display_size = display_size
197
+ self._internal_size = internal_size
198
+ self._precision = precision
199
+ self._scale = scale
200
+ self._vector_dimension = vector_dimension
201
+ self._fields = fields
202
+
203
+ @classmethod
204
+ def from_column(cls, col: dict[str, Any]) -> ResultMetadataV2:
205
+ """Initializes a ResultMetadataV2 object from the column description in the query response.
206
+ This differs from ResultMetadata in that it has newly-added fields which cannot be added to
207
+ ResultMetadata since it is a named tuple.
208
+ """
209
+ col_type = (
210
+ col["extTypeName"].upper()
211
+ if col.get("extTypeName")
212
+ else col["type"].upper()
213
+ )
214
+
215
+ fields = col.get("fields")
216
+ processed_fields: Optional[List[ResultMetadataV2]] = None
217
+ if fields is not None:
218
+ if col_type in {"VECTOR", "ARRAY", "OBJECT", "MAP"}:
219
+ processed_fields = [
220
+ ResultMetadataV2.from_column({"name": None, **f})
221
+ for f in col["fields"]
222
+ ]
223
+ else:
224
+ raise ValueError(
225
+ f"Field parsing is not supported for columns of type {col_type}."
226
+ )
227
+
228
+ return cls(
229
+ col["name"],
230
+ FIELD_NAME_TO_ID[col_type],
231
+ col["nullable"],
232
+ None,
233
+ col["length"],
234
+ col["precision"],
235
+ col["scale"],
236
+ col.get("vectorDimension"),
237
+ processed_fields,
238
+ )
239
+
240
+ def _to_result_metadata_v1(self):
241
+ """Initializes a ResultMetadata object from a ResultMetadataV2 object.
242
+
243
+ This method is for internal use only.
244
+ """
245
+
246
+ return ResultMetadata(
247
+ self._name,
248
+ self._type_code,
249
+ self._display_size,
250
+ self._internal_size,
251
+ self._precision,
252
+ self._scale,
253
+ self._is_nullable,
254
+ )
255
+
256
+ def __str__(self) -> str:
257
+ return (
258
+ f"ResultMetadataV2(name={self._name},type_code={self._type_code},"
259
+ + f"is_nullable={self._is_nullable},display_size={self._display_size},"
260
+ + "internal_size={self._internal_size},precision={self._precision},"
261
+ + "scale={self._scale},vector_dimension={self._vector_dimension},"
262
+ + "fields={self.fields})"
263
+ )
264
+
265
+ def __eq__(self, other) -> bool:
266
+ if not isinstance(other, self.__class__):
267
+ return False
268
+
269
+ return (
270
+ self._name == other._name
271
+ and self._type_code == other._type_code
272
+ and self._is_nullable == other._is_nullable
273
+ and self._display_size == other._display_size
274
+ and self._internal_size == other._internal_size
275
+ and self._precision == other._precision
276
+ and self._scale == other._scale
277
+ and self._vector_dimension == other._vector_dimension
278
+ and self._fields == other._fields
279
+ )
280
+
281
+ @property
282
+ def name(self) -> str:
283
+ return self._name
284
+
285
+ @property
286
+ def type_code(self) -> int:
287
+ return self._type_code
288
+
289
+ @property
290
+ def is_nullable(self) -> bool:
291
+ return self._is_nullable
292
+
293
+ @property
294
+ def internal_size(self) -> int | None:
295
+ return self._internal_size
296
+
297
+ @property
298
+ def display_size(self) -> int | None:
299
+ return self._display_size
300
+
301
+ @property
302
+ def precision(self) -> int | None:
303
+ return self._precision
304
+
305
+ @property
306
+ def scale(self) -> int | None:
307
+ return self._scale
308
+
309
+ @property
310
+ def vector_dimension(self) -> int | None:
311
+ return self._vector_dimension
312
+
313
+ @property
314
+ def fields(self) -> list[ResultMetadataV2] | None:
315
+ return self._fields
316
+
317
+
318
+ def exit_handler(*_) -> NoReturn:
319
+ """Handler for signal. When called, it will raise SystemExit with exit code FORCE_EXIT."""
320
+ print("\nForce exit")
321
+ logger.info("Force exit")
322
+ sys.exit(1)
323
+
324
+
325
+ class ResultState(Enum):
326
+ DEFAULT = 1
327
+ VALID = 2
328
+ RESET = 3
329
+
330
+
331
+ class SnowflakeCursor:
332
+ """Implementation of Cursor object that is returned from Connection.cursor() method.
333
+
334
+ Attributes:
335
+ description: A list of namedtuples about metadata for all columns.
336
+ rowcount: The number of records updated or selected. If not clear, -1 is returned.
337
+ rownumber: The current 0-based index of the cursor in the result set or None if the index cannot be
338
+ determined.
339
+ sfqid: Snowflake query id in UUID form. Include this in the problem report to the customer support.
340
+ sqlstate: Snowflake SQL State code.
341
+ timestamp_output_format: Snowflake timestamp_output_format for timestamps.
342
+ timestamp_ltz_output_format: Snowflake output format for LTZ timestamps.
343
+ timestamp_tz_output_format: Snowflake output format for TZ timestamps.
344
+ timestamp_ntz_output_format: Snowflake output format for NTZ timestamps.
345
+ date_output_format: Snowflake output format for dates.
346
+ time_output_format: Snowflake output format for times.
347
+ timezone: Snowflake timezone.
348
+ binary_output_format: Snowflake output format for binary fields.
349
+ arraysize: The default number of rows fetched by fetchmany.
350
+ connection: The connection object by which the cursor was created.
351
+ errorhandle: The class that handles error handling.
352
+ is_file_transfer: Whether, or not the current command is a put, or get.
353
+ """
354
+
355
+ # TODO:
356
+ # Most of these attributes have no reason to be properties, we could just store them in public variables.
357
+ # Calling a function is expensive in Python and most of these getters are unnecessary.
358
+
359
+ INSERT_SQL_RE = re.compile(r"^insert\s+into", flags=re.IGNORECASE)
360
+ COMMENT_SQL_RE = re.compile(r"/\*.*\*/")
361
+ INSERT_SQL_VALUES_RE = re.compile(
362
+ r".*VALUES\s*(\(.*\)).*", re.IGNORECASE | re.MULTILINE | re.DOTALL
363
+ )
364
+ ALTER_SESSION_RE = re.compile(
365
+ r"alter\s+session\s+set\s+(\w*?)\s*=\s*\'?([^\']+?)\'?\s*(?:;|$)",
366
+ flags=re.IGNORECASE | re.MULTILINE | re.DOTALL,
367
+ )
368
+
369
+ @staticmethod
370
+ def get_file_transfer_type(sql: str) -> FileTransferType | None:
371
+ """Decide whether a SQL is a file transfer and return its type.
372
+
373
+ None is returned if the SQL isn't a file transfer so that this function can be
374
+ used in an if-statement.
375
+ """
376
+ return get_file_transfer_type(sql)
377
+
378
+ def __init__(
379
+ self,
380
+ connection: SnowflakeConnection,
381
+ use_dict_result: bool = False,
382
+ ) -> None:
383
+ """Inits a SnowflakeCursor with a connection.
384
+
385
+ Args:
386
+ connection: The connection that created this cursor.
387
+ use_dict_result: Decides whether to use dict result or not.
388
+ """
389
+ self._connection: SnowflakeConnection = connection
390
+
391
+ self._errorhandler: Callable[
392
+ [SnowflakeConnection, SnowflakeCursor, type[Error], dict[str, str]],
393
+ None,
394
+ ] = Error.default_errorhandler
395
+ self.messages: list[
396
+ tuple[type[Error] | type[Exception], dict[str, str | bool]]
397
+ ] = []
398
+ self._timebomb: _TrackedQueryCancellationTimer | None = (
399
+ None # must be here for abort_exit method
400
+ )
401
+ self._description: list[ResultMetadataV2] | None = None
402
+ self._sfqid: str | None = None
403
+ self._sqlstate = None
404
+ self._total_rowcount = -1
405
+ self._sequence_counter = -1
406
+ self._request_id: uuid.UUID | None = None
407
+ self._is_file_transfer = False
408
+ self._multi_statement_resultIds: collections.deque[str] = collections.deque()
409
+ self.multi_statement_savedIds: list[str] = []
410
+
411
+ self._timestamp_output_format = None
412
+ self._timestamp_ltz_output_format = None
413
+ self._timestamp_ntz_output_format = None
414
+ self._timestamp_tz_output_format = None
415
+ self._date_output_format = None
416
+ self._time_output_format = None
417
+ self._timezone = None
418
+ self._binary_output_format = None
419
+ self._result: Iterator[tuple] | Iterator[dict] | None = None
420
+ self._result_set: ResultSet | None = None
421
+ self._result_state: ResultState = ResultState.DEFAULT
422
+ self._use_dict_result = use_dict_result
423
+ self.query: str | None = None
424
+ # TODO: self._query_result_format could be defined as an enum
425
+ self._query_result_format: str | None = None
426
+
427
+ self._arraysize = 1 # PEP-0249: defaults to 1
428
+
429
+ self._lock_canceling = Lock()
430
+
431
+ self._first_chunk_time = None
432
+
433
+ self._log_max_query_length = connection.log_max_query_length
434
+ self._inner_cursor: SnowflakeCursor | None = None
435
+ self._prefetch_hook = None
436
+ self._rownumber: int | None = None
437
+
438
+ self.reset()
439
+
440
+ def __del__(self) -> None: # pragma: no cover
441
+ try:
442
+ self.close()
443
+ except compat.BASE_EXCEPTION_CLASS as e:
444
+ if logger.getEffectiveLevel() <= logging.INFO:
445
+ logger.info(e)
446
+
447
+ @property
448
+ def description(self) -> list[ResultMetadata]:
449
+ if self._description is None:
450
+ return None
451
+
452
+ return [meta._to_result_metadata_v1() for meta in self._description]
453
+
454
+ @property
455
+ def _description_internal(self) -> list[ResultMetadataV2]:
456
+ """Return the new format of result metadata for a query.
457
+
458
+ This method is for internal use only.
459
+ """
460
+ return self._description
461
+
462
+ @property
463
+ def rowcount(self) -> int | None:
464
+ return self._total_rowcount if self._total_rowcount >= 0 else None
465
+
466
+ @property
467
+ def rownumber(self) -> int | None:
468
+ return self._rownumber if self._rownumber >= 0 else None
469
+
470
+ @property
471
+ def sfqid(self) -> str | None:
472
+ return self._sfqid
473
+
474
+ @property
475
+ def sqlstate(self):
476
+ return self._sqlstate
477
+
478
+ @property
479
+ def timestamp_output_format(self) -> str | None:
480
+ return self._timestamp_output_format
481
+
482
+ @property
483
+ def timestamp_ltz_output_format(self) -> str | None:
484
+ return (
485
+ self._timestamp_ltz_output_format
486
+ if self._timestamp_ltz_output_format
487
+ else self._timestamp_output_format
488
+ )
489
+
490
+ @property
491
+ def timestamp_tz_output_format(self) -> str | None:
492
+ return (
493
+ self._timestamp_tz_output_format
494
+ if self._timestamp_tz_output_format
495
+ else self._timestamp_output_format
496
+ )
497
+
498
+ @property
499
+ def timestamp_ntz_output_format(self) -> str | None:
500
+ return (
501
+ self._timestamp_ntz_output_format
502
+ if self._timestamp_ntz_output_format
503
+ else self._timestamp_output_format
504
+ )
505
+
506
+ @property
507
+ def date_output_format(self) -> str | None:
508
+ return self._date_output_format
509
+
510
+ @property
511
+ def time_output_format(self) -> str | None:
512
+ return self._time_output_format
513
+
514
+ @property
515
+ def timezone(self) -> str | None:
516
+ return self._timezone
517
+
518
+ @property
519
+ def binary_output_format(self) -> str | None:
520
+ return self._binary_output_format
521
+
522
+ @property
523
+ def arraysize(self) -> int:
524
+ return self._arraysize
525
+
526
+ @arraysize.setter
527
+ def arraysize(self, value) -> None:
528
+ self._arraysize = int(value)
529
+
530
+ @property
531
+ def connection(self) -> SnowflakeConnection:
532
+ return self._connection
533
+
534
+ @property
535
+ def errorhandler(self) -> Callable:
536
+ return self._errorhandler
537
+
538
+ @errorhandler.setter
539
+ def errorhandler(self, value: Callable | None) -> None:
540
+ logger.debug("setting errorhandler: %s", value)
541
+ if value is None:
542
+ raise ProgrammingError("Invalid errorhandler is specified")
543
+ self._errorhandler = value
544
+
545
+ @property
546
+ def is_file_transfer(self) -> bool:
547
+ """Whether the command is PUT or GET."""
548
+ return hasattr(self, "_is_file_transfer") and self._is_file_transfer
549
+
550
+ @property
551
+ def lastrowid(self) -> None:
552
+ """Snowflake does not support lastrowid in which case None should be returned as per PEP249."""
553
+ return None
554
+
555
+ @overload
556
+ def callproc(self, procname: str) -> tuple: ...
557
+
558
+ @overload
559
+ def callproc(self, procname: str, args: T) -> T: ...
560
+
561
+ def callproc(self, procname: str, args=tuple()):
562
+ """Call a stored procedure.
563
+
564
+ Args:
565
+ procname: The stored procedure to be called.
566
+ args: Parameters to be passed into the stored procedure.
567
+
568
+ Returns:
569
+ The input parameters.
570
+ """
571
+ marker_format = "%s" if self._connection.is_pyformat else "?"
572
+ command = (
573
+ f"CALL {procname}({', '.join([marker_format for _ in range(len(args))])})"
574
+ )
575
+ self.execute(command, args)
576
+ return args
577
+
578
+ def close(self) -> bool | None:
579
+ """Closes the cursor object.
580
+
581
+ Returns whether the cursor was closed during this call.
582
+ """
583
+ try:
584
+ if self.is_closed():
585
+ return False
586
+ with self._lock_canceling:
587
+ self.reset(closing=True)
588
+ self._connection = None
589
+ del self.messages[:]
590
+ return True
591
+ except Exception:
592
+ return None
593
+
594
+ def is_closed(self) -> bool:
595
+ return self._connection is None or self._connection.is_closed()
596
+
597
+ def _execute_helper(
598
+ self,
599
+ query: str,
600
+ timeout: int = 0,
601
+ statement_params: dict[str, str] | None = None,
602
+ binding_params: tuple | dict[str, dict[str, str]] = None,
603
+ binding_stage: str | None = None,
604
+ is_internal: bool = False,
605
+ describe_only: bool = False,
606
+ _no_results: bool = False,
607
+ _is_put_get=None,
608
+ _no_retry: bool = False,
609
+ dataframe_ast: str | None = None,
610
+ ) -> dict[str, Any]:
611
+ del self.messages[:]
612
+
613
+ if statement_params is not None and not isinstance(statement_params, dict):
614
+ Error.errorhandler_wrapper(
615
+ self.connection,
616
+ self,
617
+ ProgrammingError,
618
+ {
619
+ "msg": "The data type of statement params is invalid. It must be dict.",
620
+ "errno": ER_INVALID_VALUE,
621
+ },
622
+ )
623
+
624
+ # check if current installation include arrow extension or not,
625
+ # if not, we set statement level query result format to be JSON
626
+ if not CAN_USE_ARROW_RESULT_FORMAT:
627
+ logger.debug("Cannot use arrow result format, fallback to json format")
628
+ if statement_params is None:
629
+ statement_params = {
630
+ PARAMETER_PYTHON_CONNECTOR_QUERY_RESULT_FORMAT: "JSON"
631
+ }
632
+ else:
633
+ result_format_val = statement_params.get(
634
+ PARAMETER_PYTHON_CONNECTOR_QUERY_RESULT_FORMAT
635
+ )
636
+ if str(result_format_val).upper() == "ARROW":
637
+ self.check_can_use_arrow_resultset()
638
+ elif result_format_val is None:
639
+ statement_params[PARAMETER_PYTHON_CONNECTOR_QUERY_RESULT_FORMAT] = (
640
+ "JSON"
641
+ )
642
+
643
+ self._sequence_counter = self._connection._next_sequence_counter()
644
+
645
+ # If requestId is contained in statement parameters, use it to set request id. Verify here it is a valid uuid4
646
+ # identifier.
647
+ if (
648
+ statement_params is not None
649
+ and REQUEST_ID_STATEMENT_PARAM_NAME in statement_params
650
+ ):
651
+ request_id = statement_params[REQUEST_ID_STATEMENT_PARAM_NAME]
652
+
653
+ if not is_uuid4(request_id):
654
+ # uuid.UUID will throw an error if invalid, but we explicitly check and throw here.
655
+ raise ValueError(f"requestId {request_id} is not a valid UUID4.")
656
+ self._request_id = uuid.UUID(str(request_id), version=4)
657
+
658
+ # Create a (deep copy) and remove the statement param, there is no need to encode it as extra parameter
659
+ # one more time.
660
+ statement_params = statement_params.copy()
661
+ statement_params.pop(REQUEST_ID_STATEMENT_PARAM_NAME)
662
+ else:
663
+ # Generate UUID for query.
664
+ self._request_id = uuid.uuid4()
665
+
666
+ logger.debug(f"Request id: {self._request_id}")
667
+
668
+ logger.debug("running query [%s]", self._format_query_for_log(query))
669
+ if _is_put_get is not None:
670
+ # if told the query is PUT or GET, use the information
671
+ self._is_file_transfer = _is_put_get
672
+ else:
673
+ # or detect it.
674
+ self._is_file_transfer = get_file_transfer_type(query) is not None
675
+ logger.debug(
676
+ "is_file_transfer: %s",
677
+ self._is_file_transfer if self._is_file_transfer is not None else "None",
678
+ )
679
+
680
+ real_timeout = (
681
+ timeout if timeout and timeout > 0 else self._connection.network_timeout
682
+ )
683
+
684
+ if real_timeout is not None:
685
+ self._timebomb = _TrackedQueryCancellationTimer(
686
+ real_timeout, self.__cancel_query, [query]
687
+ )
688
+ self._timebomb.start()
689
+ logger.debug("started timebomb in %ss", real_timeout)
690
+ else:
691
+ self._timebomb = None
692
+
693
+ original_sigint = signal.getsignal(signal.SIGINT)
694
+
695
+ def interrupt_handler(*_): # pragma: no cover
696
+ try:
697
+ signal.signal(signal.SIGINT, exit_handler)
698
+ except (ValueError, TypeError):
699
+ # ignore failures
700
+ pass
701
+ try:
702
+ if self._timebomb is not None:
703
+ self._timebomb.cancel()
704
+ logger.debug("cancelled timebomb in finally")
705
+ self._timebomb = None
706
+ self.__cancel_query(query)
707
+ finally:
708
+ if original_sigint:
709
+ try:
710
+ signal.signal(signal.SIGINT, original_sigint)
711
+ except (ValueError, TypeError):
712
+ # ignore failures
713
+ pass
714
+ raise KeyboardInterrupt
715
+
716
+ try:
717
+ if not original_sigint == exit_handler:
718
+ signal.signal(signal.SIGINT, interrupt_handler)
719
+ except ValueError: # pragma: no cover
720
+ logger.debug(
721
+ "Failed to set SIGINT handler. " "Not in main thread. Ignored..."
722
+ )
723
+ ret: dict[str, Any] = {"data": {}}
724
+ try:
725
+ ret = self._connection.cmd_query(
726
+ query,
727
+ self._sequence_counter,
728
+ self._request_id,
729
+ binding_params=binding_params,
730
+ binding_stage=binding_stage,
731
+ is_file_transfer=bool(self._is_file_transfer),
732
+ statement_params=statement_params,
733
+ is_internal=is_internal,
734
+ describe_only=describe_only,
735
+ _no_results=_no_results,
736
+ _no_retry=_no_retry,
737
+ timeout=real_timeout,
738
+ dataframe_ast=dataframe_ast,
739
+ )
740
+ finally:
741
+ try:
742
+ if original_sigint:
743
+ signal.signal(signal.SIGINT, original_sigint)
744
+ except (ValueError, TypeError): # pragma: no cover
745
+ logger.debug(
746
+ "Failed to reset SIGINT handler. Not in main " "thread. Ignored..."
747
+ )
748
+ if self._timebomb is not None:
749
+ self._timebomb.cancel()
750
+ logger.debug("cancelled timebomb in finally")
751
+
752
+ if "data" in ret and "parameters" in ret["data"]:
753
+ parameters = ret["data"].get("parameters", list())
754
+ # Set session parameters for cursor object
755
+ for kv in parameters:
756
+ if "TIMESTAMP_OUTPUT_FORMAT" in kv["name"]:
757
+ self._timestamp_output_format = kv["value"]
758
+ elif "TIMESTAMP_NTZ_OUTPUT_FORMAT" in kv["name"]:
759
+ self._timestamp_ntz_output_format = kv["value"]
760
+ elif "TIMESTAMP_LTZ_OUTPUT_FORMAT" in kv["name"]:
761
+ self._timestamp_ltz_output_format = kv["value"]
762
+ elif "TIMESTAMP_TZ_OUTPUT_FORMAT" in kv["name"]:
763
+ self._timestamp_tz_output_format = kv["value"]
764
+ elif "DATE_OUTPUT_FORMAT" in kv["name"]:
765
+ self._date_output_format = kv["value"]
766
+ elif "TIME_OUTPUT_FORMAT" in kv["name"]:
767
+ self._time_output_format = kv["value"]
768
+ elif "TIMEZONE" in kv["name"]:
769
+ self._timezone = kv["value"]
770
+ elif "BINARY_OUTPUT_FORMAT" in kv["name"]:
771
+ self._binary_output_format = kv["value"]
772
+ # Set session parameters for connection object
773
+ self._connection._update_parameters(
774
+ {p["name"]: p["value"] for p in parameters}
775
+ )
776
+
777
+ self.query = query
778
+ self._sequence_counter = -1
779
+ return ret
780
+
781
+ def _preprocess_pyformat_query(
782
+ self,
783
+ command: str,
784
+ params: Sequence[Any] | dict[Any, Any] | None = None,
785
+ ) -> str:
786
+ # pyformat/format paramstyle
787
+ # client side binding
788
+ processed_params = self._connection._process_params_pyformat(params, self)
789
+ # SNOW-513061 collect telemetry for empty sequence usage before we make the breaking change announcement
790
+ if params is not None and len(params) == 0:
791
+ self._log_telemetry_job_data(
792
+ TelemetryField.EMPTY_SEQ_INTERPOLATION,
793
+ (
794
+ TelemetryData.TRUE
795
+ if self.connection._interpolate_empty_sequences
796
+ else TelemetryData.FALSE
797
+ ),
798
+ )
799
+ if logger.getEffectiveLevel() <= logging.DEBUG:
800
+ logger.debug(
801
+ f"binding: [{self._format_query_for_log(command)}] "
802
+ f"with input=[{params}], "
803
+ f"processed=[{processed_params}]",
804
+ )
805
+ if (
806
+ self.connection._interpolate_empty_sequences
807
+ and processed_params is not None
808
+ ) or (
809
+ not self.connection._interpolate_empty_sequences
810
+ and len(processed_params) > 0
811
+ ):
812
+ query = command % processed_params
813
+ else:
814
+ query = command
815
+ return query
816
+
817
+ @overload
818
+ def execute(
819
+ self,
820
+ command: str,
821
+ params: Sequence[Any] | dict[Any, Any] | None = None,
822
+ _bind_stage: str | None = None,
823
+ timeout: int | None = None,
824
+ _exec_async: bool = False,
825
+ _no_retry: bool = False,
826
+ _do_reset: bool = True,
827
+ _put_callback: SnowflakeProgressPercentage = None,
828
+ _put_azure_callback: SnowflakeProgressPercentage = None,
829
+ _put_callback_output_stream: IO[str] = sys.stdout,
830
+ _get_callback: SnowflakeProgressPercentage = None,
831
+ _get_azure_callback: SnowflakeProgressPercentage = None,
832
+ _get_callback_output_stream: IO[str] = sys.stdout,
833
+ _show_progress_bar: bool = True,
834
+ _statement_params: dict[str, str] | None = None,
835
+ _is_internal: bool = False,
836
+ _describe_only: bool = False,
837
+ _no_results: Literal[False] = False,
838
+ _is_put_get: bool | None = None,
839
+ _raise_put_get_error: bool = True,
840
+ _force_put_overwrite: bool = False,
841
+ _skip_upload_on_content_match: bool = False,
842
+ file_stream: IO[bytes] | None = None,
843
+ num_statements: int | None = None,
844
+ _dataframe_ast: str | None = None,
845
+ ) -> Self | None: ...
846
+
847
+ @overload
848
+ def execute(
849
+ self,
850
+ command: str,
851
+ params: Sequence[Any] | dict[Any, Any] | None = None,
852
+ _bind_stage: str | None = None,
853
+ timeout: int | None = None,
854
+ _exec_async: bool = False,
855
+ _no_retry: bool = False,
856
+ _do_reset: bool = True,
857
+ _put_callback: SnowflakeProgressPercentage = None,
858
+ _put_azure_callback: SnowflakeProgressPercentage = None,
859
+ _put_callback_output_stream: IO[str] = sys.stdout,
860
+ _get_callback: SnowflakeProgressPercentage = None,
861
+ _get_azure_callback: SnowflakeProgressPercentage = None,
862
+ _get_callback_output_stream: IO[str] = sys.stdout,
863
+ _show_progress_bar: bool = True,
864
+ _statement_params: dict[str, str] | None = None,
865
+ _is_internal: bool = False,
866
+ _describe_only: bool = False,
867
+ _no_results: Literal[True] = True,
868
+ _is_put_get: bool | None = None,
869
+ _raise_put_get_error: bool = True,
870
+ _force_put_overwrite: bool = False,
871
+ _skip_upload_on_content_match: bool = False,
872
+ file_stream: IO[bytes] | None = None,
873
+ num_statements: int | None = None,
874
+ _dataframe_ast: str | None = None,
875
+ ) -> dict[str, Any] | None: ...
876
+
877
+ def execute(
878
+ self,
879
+ command: str,
880
+ params: Sequence[Any] | dict[Any, Any] | None = None,
881
+ _bind_stage: str | None = None,
882
+ timeout: int | None = None,
883
+ _exec_async: bool = False,
884
+ _no_retry: bool = False,
885
+ _do_reset: bool = True,
886
+ _put_callback: SnowflakeProgressPercentage = None,
887
+ _put_azure_callback: SnowflakeProgressPercentage = None,
888
+ _put_callback_output_stream: IO[str] = sys.stdout,
889
+ _get_callback: SnowflakeProgressPercentage = None,
890
+ _get_azure_callback: SnowflakeProgressPercentage = None,
891
+ _get_callback_output_stream: IO[str] = sys.stdout,
892
+ _show_progress_bar: bool = True,
893
+ _statement_params: dict[str, str] | None = None,
894
+ _is_internal: bool = False,
895
+ _describe_only: bool = False,
896
+ _no_results: bool = False,
897
+ _is_put_get: bool | None = None,
898
+ _raise_put_get_error: bool = True,
899
+ _force_put_overwrite: bool = False,
900
+ _skip_upload_on_content_match: bool = False,
901
+ file_stream: IO[bytes] | None = None,
902
+ num_statements: int | None = None,
903
+ _force_qmark_paramstyle: bool = False,
904
+ _dataframe_ast: str | None = None,
905
+ ) -> Self | dict[str, Any] | None:
906
+ """Executes a command/query.
907
+
908
+ Args:
909
+ command: The SQL command to be executed.
910
+ params: Parameters to be bound into the SQL statement.
911
+ _bind_stage: Path in temporary stage where binding parameters are uploaded as CSV files.
912
+ timeout: Number of seconds after which to abort the query.
913
+ _exec_async: Whether to execute this query asynchronously.
914
+ _no_retry: Whether or not to retry on known errors.
915
+ _do_reset: Whether or not the result set needs to be reset before executing query.
916
+ _put_callback: Function to which PUT command should call back to.
917
+ _put_azure_callback: Function to which an Azure PUT command should call back to.
918
+ _put_callback_output_stream: The output stream a PUT command's callback should report on.
919
+ _get_callback: Function to which GET command should call back to.
920
+ _get_azure_callback: Function to which an Azure GET command should call back to.
921
+ _get_callback_output_stream: The output stream a GET command's callback should report on.
922
+ _show_progress_bar: Whether or not to show progress bar.
923
+ _statement_params: Extra information that should be sent to Snowflake with query. This dict will not be
924
+ modified by the connector.
925
+ _is_internal: This flag indicates whether the query is issued internally by the connector.
926
+ _describe_only: If true, the query will not be executed but return the schema/description of this query.
927
+ _no_results: This flag tells the back-end to not return the result, just fire the query and return the
928
+ response returned by Snowflake's server.
929
+ _use_ijson: This flag doesn't do anything as ijson support has ended.
930
+ _is_put_get: Force decision of this SQL query being a PUT, or GET command. This is detected otherwise.
931
+ _raise_put_get_error: Whether to raise PUT and GET errors.
932
+ _force_put_overwrite: If the SQL query is a PUT, then this flag can force overwriting of an already
933
+ existing file on stage.
934
+ _skip_upload_on_content_match: If the SQL query is a PUT with overwrite enabled, then this flag will skip upload
935
+ if the file contents match to ease concurrent uploads.
936
+ file_stream: File-like object to be uploaded with PUT
937
+ num_statements: Query level parameter submitted in _statement_params constraining exact number of
938
+ statements being submitted (or 0 if submitting an uncounted number) when using a multi-statement query.
939
+ _force_qmark_paramstyle: Force the use of qmark paramstyle regardless of the connection's paramstyle.
940
+ _dataframe_ast: Base64-encoded dataframe request abstract syntax tree.
941
+
942
+ Returns:
943
+ The cursor itself, or None if some error happened, or the response returned
944
+ by Snowflake if the _no_results flag is on.
945
+ """
946
+ if _exec_async:
947
+ _no_results = True
948
+ logger.debug("executing SQL/command")
949
+ if self.is_closed():
950
+ Error.errorhandler_wrapper(
951
+ self.connection,
952
+ self,
953
+ InterfaceError,
954
+ {"msg": "Cursor is closed in execute.", "errno": ER_CURSOR_IS_CLOSED},
955
+ )
956
+
957
+ if _do_reset:
958
+ self.reset()
959
+ command = command.strip(" \t\n\r") if command else ""
960
+ if not command:
961
+ if _dataframe_ast:
962
+ logger.debug("dataframe ast: [%s]", _dataframe_ast)
963
+ else:
964
+ logger.warning("execute: no query is given to execute")
965
+ return None
966
+
967
+ logger.debug("query: [%s]", self._format_query_for_log(command))
968
+ _statement_params = _statement_params or dict()
969
+ # If we need to add another parameter, please consider introducing a dict for all extra params
970
+ # See discussion in https://github.com/snowflakedb/snowflake-connector-python/pull/1524#discussion_r1174061775
971
+ if num_statements is not None:
972
+ _statement_params = {
973
+ **_statement_params,
974
+ "MULTI_STATEMENT_COUNT": num_statements,
975
+ }
976
+
977
+ kwargs: dict[str, Any] = {
978
+ "timeout": timeout,
979
+ "statement_params": _statement_params,
980
+ "is_internal": _is_internal,
981
+ "describe_only": _describe_only,
982
+ "_no_results": _no_results,
983
+ "_is_put_get": _is_put_get,
984
+ "_no_retry": _no_retry,
985
+ "dataframe_ast": _dataframe_ast,
986
+ }
987
+
988
+ if self._connection.is_pyformat and not _force_qmark_paramstyle:
989
+ query = self._preprocess_pyformat_query(command, params)
990
+ else:
991
+ # qmark and numeric paramstyle
992
+ query = command
993
+ if _bind_stage:
994
+ kwargs["binding_stage"] = _bind_stage
995
+ else:
996
+ if params is not None and not isinstance(params, (list, tuple)):
997
+ errorvalue = {
998
+ "msg": f"Binding parameters must be a list: {params}",
999
+ "errno": ER_FAILED_PROCESSING_PYFORMAT,
1000
+ }
1001
+ Error.errorhandler_wrapper(
1002
+ self.connection, self, ProgrammingError, errorvalue
1003
+ )
1004
+
1005
+ kwargs["binding_params"] = self._connection._process_params_qmarks(
1006
+ params, self
1007
+ )
1008
+
1009
+ m = DESC_TABLE_RE.match(query)
1010
+ if m:
1011
+ query1 = f"describe table {m.group(1)}"
1012
+ logger.debug(
1013
+ "query was rewritten: org=%s, new=%s",
1014
+ " ".join(line.strip() for line in query.split("\n")),
1015
+ query1,
1016
+ )
1017
+ query = query1
1018
+
1019
+ ret = self._execute_helper(query, **kwargs)
1020
+ self._sfqid = (
1021
+ ret["data"]["queryId"]
1022
+ if "data" in ret and "queryId" in ret["data"]
1023
+ else None
1024
+ )
1025
+ logger.debug(f"sfqid: {self.sfqid}")
1026
+ self._sqlstate = (
1027
+ ret["data"]["sqlState"]
1028
+ if "data" in ret and "sqlState" in ret["data"]
1029
+ else None
1030
+ )
1031
+ logger.debug("query execution done")
1032
+
1033
+ self._first_chunk_time = get_time_millis()
1034
+
1035
+ # if server gives a send time, log the time it took to arrive
1036
+ if "data" in ret and "sendResultTime" in ret["data"]:
1037
+ time_consume_first_result = (
1038
+ self._first_chunk_time - ret["data"]["sendResultTime"]
1039
+ )
1040
+ self._log_telemetry_job_data(
1041
+ TelemetryField.TIME_CONSUME_FIRST_RESULT, time_consume_first_result
1042
+ )
1043
+
1044
+ if ret["success"]:
1045
+ logger.debug("SUCCESS")
1046
+ data = ret["data"]
1047
+
1048
+ for m in self.ALTER_SESSION_RE.finditer(query):
1049
+ # session parameters
1050
+ param = m.group(1).upper()
1051
+ value = m.group(2)
1052
+ self._connection.converter.set_parameter(param, value)
1053
+
1054
+ if "resultIds" in data:
1055
+ self._init_multi_statement_results(data)
1056
+ return self
1057
+ else:
1058
+ self.multi_statement_savedIds = []
1059
+
1060
+ self._is_file_transfer = "command" in data and data["command"] in (
1061
+ "UPLOAD",
1062
+ "DOWNLOAD",
1063
+ )
1064
+ logger.debug("PUT OR GET: %s", self.is_file_transfer)
1065
+ if self.is_file_transfer:
1066
+ from .file_transfer_agent import SnowflakeFileTransferAgent
1067
+
1068
+ # Decide whether to use the old, or new code path
1069
+ sf_file_transfer_agent = SnowflakeFileTransferAgent(
1070
+ self,
1071
+ query,
1072
+ ret,
1073
+ put_callback=_put_callback,
1074
+ put_azure_callback=_put_azure_callback,
1075
+ put_callback_output_stream=_put_callback_output_stream,
1076
+ get_callback=_get_callback,
1077
+ get_azure_callback=_get_azure_callback,
1078
+ get_callback_output_stream=_get_callback_output_stream,
1079
+ show_progress_bar=_show_progress_bar,
1080
+ raise_put_get_error=_raise_put_get_error,
1081
+ force_put_overwrite=_force_put_overwrite
1082
+ or data.get("overwrite", False),
1083
+ skip_upload_on_content_match=_skip_upload_on_content_match,
1084
+ source_from_stream=file_stream,
1085
+ multipart_threshold=data.get("threshold"),
1086
+ use_s3_regional_url=self._connection.enable_stage_s3_privatelink_for_us_east_1,
1087
+ iobound_tpe_limit=self._connection.iobound_tpe_limit,
1088
+ unsafe_file_write=self._connection.unsafe_file_write,
1089
+ )
1090
+ sf_file_transfer_agent.execute()
1091
+ data = sf_file_transfer_agent.result()
1092
+ self._total_rowcount = len(data["rowset"]) if "rowset" in data else -1
1093
+
1094
+ if _exec_async:
1095
+ self.connection._async_sfqids[self._sfqid] = None
1096
+ if _no_results:
1097
+ self._total_rowcount = (
1098
+ ret["data"]["total"]
1099
+ if "data" in ret and "total" in ret["data"]
1100
+ else -1
1101
+ )
1102
+ return data
1103
+ self._init_result_and_meta(data)
1104
+ else:
1105
+ self._total_rowcount = (
1106
+ ret["data"]["total"] if "data" in ret and "total" in ret["data"] else -1
1107
+ )
1108
+ logger.debug(ret)
1109
+ err = ret["message"]
1110
+ code = ret.get("code", -1)
1111
+ if (
1112
+ self._timebomb
1113
+ and self._timebomb.executed
1114
+ and "SQL execution canceled" in err
1115
+ ):
1116
+ # Modify the error message only if the server error response indicates the query was canceled.
1117
+ # If the error occurs before the cancellation request reaches the backend
1118
+ # (e.g., due to a very short timeout), we retain the original error message
1119
+ # as the query might have encountered an issue prior to cancellation.
1120
+ err = (
1121
+ f"SQL execution was cancelled by the client due to a timeout. "
1122
+ f"Error message received from the server: {err}"
1123
+ )
1124
+ if "data" in ret:
1125
+ err += ret["data"].get("errorMessage", "")
1126
+ errvalue = {
1127
+ "msg": err,
1128
+ "errno": int(code),
1129
+ "sqlstate": self._sqlstate,
1130
+ "sfqid": self._sfqid,
1131
+ "query": query,
1132
+ }
1133
+ is_integrity_error = (
1134
+ code == "100072"
1135
+ ) # NULL result in a non-nullable column
1136
+ error_class = IntegrityError if is_integrity_error else ProgrammingError
1137
+ Error.errorhandler_wrapper(self.connection, self, error_class, errvalue)
1138
+ return self
1139
+
1140
+ def execute_async(self, *args: Any, **kwargs: Any) -> dict[str, Any]:
1141
+ """Convenience function to execute a query without waiting for results (asynchronously).
1142
+
1143
+ This function takes the same arguments as execute, please refer to that function
1144
+ for documentation. Please note that PUT and GET statements are not supported by this method.
1145
+ """
1146
+ kwargs["_exec_async"] = True
1147
+ return self.execute(*args, **kwargs)
1148
+
1149
+ def describe(self, *args: Any, **kwargs: Any) -> list[ResultMetadata]:
1150
+ """Obtain the schema of the result without executing the query.
1151
+
1152
+ This function takes the same arguments as execute, please refer to that function
1153
+ for documentation.
1154
+
1155
+ Returns:
1156
+ The schema of the result.
1157
+ """
1158
+ kwargs["_describe_only"] = kwargs["_is_internal"] = True
1159
+ self.execute(*args, **kwargs)
1160
+
1161
+ if self._description is None:
1162
+ return None
1163
+ return [meta._to_result_metadata_v1() for meta in self._description]
1164
+
1165
+ def _describe_internal(self, *args: Any, **kwargs: Any) -> list[ResultMetadataV2]:
1166
+ """Obtain the schema of the result without executing the query.
1167
+
1168
+ This function takes the same arguments as execute, please refer to that function
1169
+ for documentation.
1170
+
1171
+ This function is for internal use only
1172
+
1173
+ Returns:
1174
+ The schema of the result, in the new result metadata format.
1175
+ """
1176
+ kwargs["_describe_only"] = kwargs["_is_internal"] = True
1177
+ self.execute(*args, **kwargs)
1178
+ return self._description
1179
+
1180
+ def _format_query_for_log(self, query: str) -> str:
1181
+ return self._connection._format_query_for_log(query)
1182
+
1183
+ def _is_dml(self, data: dict[Any, Any]) -> bool:
1184
+ return (
1185
+ "statementTypeId" in data
1186
+ and int(data["statementTypeId"]) in STATEMENT_TYPE_ID_DML_SET
1187
+ )
1188
+
1189
+ def _init_result_and_meta(self, data: dict[Any, Any]) -> None:
1190
+ is_dml = self._is_dml(data)
1191
+ self._query_result_format = data.get("queryResultFormat", "json")
1192
+ logger.debug("Query result format: %s", self._query_result_format)
1193
+
1194
+ if self._total_rowcount == -1 and not is_dml and data.get("total") is not None:
1195
+ self._total_rowcount = data["total"]
1196
+
1197
+ self._description: list[ResultMetadataV2] = [
1198
+ ResultMetadataV2.from_column(col) for col in data["rowtype"]
1199
+ ]
1200
+
1201
+ result_chunks = create_batches_from_response(
1202
+ self, self._query_result_format, data, self._description
1203
+ )
1204
+
1205
+ if not (is_dml or self.is_file_transfer):
1206
+ logger.debug(
1207
+ "Number of results in first chunk: %s", result_chunks[0].rowcount
1208
+ )
1209
+
1210
+ self._result_set = ResultSet(
1211
+ self,
1212
+ result_chunks,
1213
+ self._connection.client_fetch_threads
1214
+ or self._connection.client_prefetch_threads,
1215
+ self._connection.client_fetch_use_mp,
1216
+ )
1217
+ self._rownumber = -1
1218
+ self._result_state = ResultState.VALID
1219
+
1220
+ # don't update the row count when the result is returned from `describe` method
1221
+ if is_dml and "rowset" in data and len(data["rowset"]) > 0:
1222
+ updated_rows = 0
1223
+ for idx, desc in enumerate(self._description):
1224
+ if desc.name in (
1225
+ "number of rows updated",
1226
+ "number of multi-joined rows updated",
1227
+ "number of rows deleted",
1228
+ ) or desc.name.startswith("number of rows inserted"):
1229
+ updated_rows += int(data["rowset"][0][idx])
1230
+ if self._total_rowcount == -1:
1231
+ self._total_rowcount = updated_rows
1232
+ else:
1233
+ self._total_rowcount += updated_rows
1234
+
1235
+ def _init_multi_statement_results(self, data: dict) -> None:
1236
+ self._log_telemetry_job_data(TelemetryField.MULTI_STATEMENT, TelemetryData.TRUE)
1237
+ self.multi_statement_savedIds = data["resultIds"].split(",")
1238
+ self._multi_statement_resultIds = collections.deque(
1239
+ self.multi_statement_savedIds
1240
+ )
1241
+ if self._is_file_transfer:
1242
+ Error.errorhandler_wrapper(
1243
+ self.connection,
1244
+ self,
1245
+ ProgrammingError,
1246
+ {
1247
+ "msg": "PUT/GET commands are not supported for multi-statement queries and cannot be executed.",
1248
+ "errno": ER_INVALID_VALUE,
1249
+ },
1250
+ )
1251
+ self.nextset()
1252
+
1253
+ def check_can_use_arrow_resultset(self) -> None:
1254
+ global CAN_USE_ARROW_RESULT_FORMAT
1255
+
1256
+ if not CAN_USE_ARROW_RESULT_FORMAT:
1257
+ if self._connection.application == "SnowSQL":
1258
+ msg = "Currently SnowSQL doesn't support the result set in Apache Arrow format."
1259
+ errno = ER_NO_PYARROW_SNOWSQL
1260
+ else:
1261
+ msg = "The result set in Apache Arrow format is not supported for the platform."
1262
+ errno = ER_NO_ARROW_RESULT
1263
+
1264
+ Error.errorhandler_wrapper(
1265
+ self.connection,
1266
+ self,
1267
+ ProgrammingError,
1268
+ {
1269
+ "msg": msg,
1270
+ "errno": errno,
1271
+ },
1272
+ )
1273
+
1274
+ def check_can_use_pandas(self) -> None:
1275
+ if not installed_pandas:
1276
+ msg = (
1277
+ "Optional dependency: 'pandas' is not installed, please see the following link for install "
1278
+ "instructions: https://docs.snowflake.com/en/user-guide/python-connector-pandas.html#installation"
1279
+ )
1280
+ errno = ER_NO_PYARROW
1281
+
1282
+ Error.errorhandler_wrapper(
1283
+ self.connection,
1284
+ self,
1285
+ ProgrammingError,
1286
+ {
1287
+ "msg": msg,
1288
+ "errno": errno,
1289
+ },
1290
+ )
1291
+
1292
+ def query_result(self, qid: str) -> SnowflakeCursor:
1293
+ """Query the result of a previously executed query."""
1294
+ url = f"/queries/{qid}/result"
1295
+ ret = self._connection.rest.request(url=url, method="get")
1296
+ self._sfqid = (
1297
+ ret["data"]["queryId"]
1298
+ if "data" in ret and "queryId" in ret["data"]
1299
+ else None
1300
+ )
1301
+ self._sqlstate = (
1302
+ ret["data"]["sqlState"]
1303
+ if "data" in ret and "sqlState" in ret["data"]
1304
+ else None
1305
+ )
1306
+ logger.debug("sfqid=%s", self._sfqid)
1307
+
1308
+ if ret.get("success"):
1309
+ data = ret.get("data")
1310
+ self._init_result_and_meta(data)
1311
+ else:
1312
+ logger.debug("failed")
1313
+ logger.debug(ret)
1314
+ err = ret["message"]
1315
+ code = ret.get("code", -1)
1316
+ if "data" in ret:
1317
+ err += ret["data"].get("errorMessage", "")
1318
+ errvalue = {
1319
+ "msg": err,
1320
+ "errno": int(code),
1321
+ "sqlstate": self._sqlstate,
1322
+ "sfqid": self._sfqid,
1323
+ }
1324
+ Error.errorhandler_wrapper(
1325
+ self.connection, self, ProgrammingError, errvalue
1326
+ )
1327
+ return self
1328
+
1329
+ def fetch_arrow_batches(self) -> Iterator[Table]:
1330
+ self.check_can_use_arrow_resultset()
1331
+ if self._prefetch_hook is not None:
1332
+ self._prefetch_hook()
1333
+ if self._query_result_format != "arrow":
1334
+ raise NotSupportedError
1335
+ self._log_telemetry_job_data(
1336
+ TelemetryField.ARROW_FETCH_BATCHES, TelemetryData.TRUE
1337
+ )
1338
+ return self._result_set._fetch_arrow_batches()
1339
+
1340
+ @overload
1341
+ def fetch_arrow_all(self, force_return_table: Literal[False]) -> Table | None: ...
1342
+
1343
+ @overload
1344
+ def fetch_arrow_all(self, force_return_table: Literal[True]) -> Table: ...
1345
+
1346
+ def fetch_arrow_all(self, force_return_table: bool = False) -> Table | None:
1347
+ """
1348
+ Args:
1349
+ force_return_table: Set to True so that when the query returns zero rows,
1350
+ an empty pyarrow table will be returned with schema using the highest bit length for each column.
1351
+ Default value is False in which case None is returned in case of zero rows.
1352
+ """
1353
+ self.check_can_use_arrow_resultset()
1354
+
1355
+ if self._prefetch_hook is not None:
1356
+ self._prefetch_hook()
1357
+ if self._query_result_format != "arrow":
1358
+ raise NotSupportedError
1359
+ self._log_telemetry_job_data(TelemetryField.ARROW_FETCH_ALL, TelemetryData.TRUE)
1360
+ return self._result_set._fetch_arrow_all(force_return_table=force_return_table)
1361
+
1362
+ def fetch_pandas_batches(self, **kwargs: Any) -> Iterator[DataFrame]:
1363
+ """Fetches a single Arrow Table."""
1364
+ self.check_can_use_pandas()
1365
+ if self._prefetch_hook is not None:
1366
+ self._prefetch_hook()
1367
+ if self._query_result_format != "arrow":
1368
+ raise NotSupportedError
1369
+ self._log_telemetry_job_data(
1370
+ TelemetryField.PANDAS_FETCH_BATCHES, TelemetryData.TRUE
1371
+ )
1372
+ return self._result_set._fetch_pandas_batches(**kwargs)
1373
+
1374
+ def fetch_pandas_all(self, **kwargs: Any) -> DataFrame:
1375
+ """
1376
+ Fetch Pandas dataframes in batches, where 'batch' refers to Snowflake Chunk.
1377
+
1378
+ Returns:
1379
+ A pandas dataframe containing the cursors query results.
1380
+
1381
+ Note:
1382
+ Timestamp types that are nested in structured types may not be accurately represented
1383
+ due to a limitation in the pyarrow to pandas conversion.
1384
+ See here for more information: https://github.com/apache/arrow/issues/4116
1385
+ """
1386
+ self.check_can_use_pandas()
1387
+ if self._prefetch_hook is not None:
1388
+ self._prefetch_hook()
1389
+ if self._query_result_format != "arrow":
1390
+ raise NotSupportedError
1391
+ self._log_telemetry_job_data(
1392
+ TelemetryField.PANDAS_FETCH_ALL, TelemetryData.TRUE
1393
+ )
1394
+ return self._result_set._fetch_pandas_all(**kwargs)
1395
+
1396
+ def abort_query(self, qid: str) -> bool:
1397
+ url = f"/queries/{qid}/abort-request"
1398
+ ret = self._connection.rest.request(url=url, method="post")
1399
+ return ret.get("success")
1400
+
1401
+ def executemany(
1402
+ self,
1403
+ command: str,
1404
+ seqparams: Sequence[Any] | dict[str, Any],
1405
+ **kwargs: Any,
1406
+ ) -> SnowflakeCursor:
1407
+ """Executes a command/query with the given set of parameters sequentially."""
1408
+ logger.debug("executing many SQLs/commands")
1409
+ command = command.strip(" \t\n\r") if command else None
1410
+
1411
+ if not seqparams:
1412
+ logger.warning(
1413
+ "No parameters provided to executemany, returning without doing anything."
1414
+ )
1415
+ return self
1416
+
1417
+ if self.INSERT_SQL_RE.match(command) and (
1418
+ "num_statements" not in kwargs or kwargs.get("num_statements") == 1
1419
+ ):
1420
+ if self._connection.is_pyformat:
1421
+ # TODO(SNOW-940692) - utilize multi-statement instead of rewriting the query and
1422
+ # accumulate results to mock the result from a single insert statement as formatted below
1423
+ logger.debug("rewriting INSERT query")
1424
+ command_wo_comments = re.sub(self.COMMENT_SQL_RE, "", command)
1425
+ m = self.INSERT_SQL_VALUES_RE.match(command_wo_comments)
1426
+ if not m:
1427
+ Error.errorhandler_wrapper(
1428
+ self.connection,
1429
+ self,
1430
+ InterfaceError,
1431
+ {
1432
+ "msg": "Failed to rewrite multi-row insert",
1433
+ "errno": ER_FAILED_TO_REWRITE_MULTI_ROW_INSERT,
1434
+ },
1435
+ )
1436
+
1437
+ fmt = m.group(1)
1438
+ values = []
1439
+ for param in seqparams:
1440
+ logger.debug(f"parameter: {param}")
1441
+ values.append(
1442
+ fmt % self._connection._process_params_pyformat(param, self)
1443
+ )
1444
+ command = command.replace(fmt, ",".join(values), 1)
1445
+ self.execute(command, **kwargs)
1446
+ return self
1447
+ else:
1448
+ logger.debug("bulk insert")
1449
+ # sanity check
1450
+ row_size = len(seqparams[0])
1451
+ for row in seqparams:
1452
+ if len(row) != row_size:
1453
+ error_value = {
1454
+ "msg": f"Bulk data size don't match. expected: {row_size}, "
1455
+ f"got: {len(row)}, command: {command}",
1456
+ "errno": ER_INVALID_VALUE,
1457
+ }
1458
+ Error.errorhandler_wrapper(
1459
+ self.connection, self, InterfaceError, error_value
1460
+ )
1461
+ return self
1462
+ bind_size = len(seqparams) * row_size
1463
+ bind_stage = None
1464
+ if (
1465
+ bind_size
1466
+ >= self.connection._session_parameters[
1467
+ "CLIENT_STAGE_ARRAY_BINDING_THRESHOLD"
1468
+ ]
1469
+ > 0
1470
+ ):
1471
+ # bind stage optimization
1472
+ try:
1473
+ rows = self.connection._write_params_to_byte_rows(seqparams)
1474
+ bind_uploader = BindUploadAgent(self, rows)
1475
+ bind_uploader.upload()
1476
+ bind_stage = bind_uploader.stage_path
1477
+ except BindUploadError:
1478
+ logger.debug(
1479
+ "Failed to upload binds to stage, sending binds to "
1480
+ "Snowflake instead."
1481
+ )
1482
+ binding_param = (
1483
+ None if bind_stage else list(map(list, zip(*seqparams)))
1484
+ ) # transpose
1485
+ self.execute(
1486
+ command, params=binding_param, _bind_stage=bind_stage, **kwargs
1487
+ )
1488
+ return self
1489
+
1490
+ self.reset()
1491
+ if "num_statements" not in kwargs:
1492
+ # fall back to old driver behavior when the user does not provide the parameter to enable
1493
+ # multi-statement optimizations for executemany
1494
+ for param in seqparams:
1495
+ self.execute(command, params=param, _do_reset=False, **kwargs)
1496
+ else:
1497
+ if re.search(";/s*$", command) is None:
1498
+ command = command + "; "
1499
+ if self._connection.is_pyformat and not kwargs.get(
1500
+ "_force_qmark_paramstyle", False
1501
+ ):
1502
+ processed_queries = [
1503
+ self._preprocess_pyformat_query(command, params)
1504
+ for params in seqparams
1505
+ ]
1506
+ query = "".join(processed_queries)
1507
+ params = None
1508
+ else:
1509
+ query = command * len(seqparams)
1510
+ params = [param for parameters in seqparams for param in parameters]
1511
+
1512
+ kwargs["num_statements"]: int = kwargs.get("num_statements") * len(
1513
+ seqparams
1514
+ )
1515
+
1516
+ self.execute(query, params, _do_reset=False, **kwargs)
1517
+
1518
+ return self
1519
+
1520
+ def fetchone(self) -> dict | tuple | None:
1521
+ """Fetches one row."""
1522
+ if self._prefetch_hook is not None:
1523
+ self._prefetch_hook()
1524
+ if self._result is None and self._result_set is not None:
1525
+ self._result = iter(self._result_set)
1526
+ self._result_state = ResultState.VALID
1527
+
1528
+ try:
1529
+ _next = next(self._result, None)
1530
+ if isinstance(_next, Exception):
1531
+ Error.errorhandler_wrapper_from_ready_exception(
1532
+ self._connection,
1533
+ self,
1534
+ _next,
1535
+ )
1536
+ if _next is not None:
1537
+ self._rownumber += 1
1538
+ return _next
1539
+ except TypeError as err:
1540
+ if self._result_state == ResultState.DEFAULT:
1541
+ raise err
1542
+ else:
1543
+ return None
1544
+
1545
+ def fetchmany(self, size: int | None = None) -> list[tuple] | list[dict]:
1546
+ """Fetches the number of specified rows."""
1547
+ if size is None:
1548
+ size = self.arraysize
1549
+
1550
+ if size < 0:
1551
+ errorvalue = {
1552
+ "msg": (
1553
+ "The number of rows is not zero or " "positive number: {}"
1554
+ ).format(size),
1555
+ "errno": ER_NOT_POSITIVE_SIZE,
1556
+ }
1557
+ Error.errorhandler_wrapper(
1558
+ self.connection, self, ProgrammingError, errorvalue
1559
+ )
1560
+ ret = []
1561
+ while size > 0:
1562
+ row = self.fetchone()
1563
+ if row is None:
1564
+ break
1565
+ ret.append(row)
1566
+ if size is not None:
1567
+ size -= 1
1568
+
1569
+ return ret
1570
+
1571
+ def fetchall(self) -> list[tuple] | list[dict]:
1572
+ """Fetches all of the results."""
1573
+ ret = []
1574
+ while True:
1575
+ row = self.fetchone()
1576
+ if row is None:
1577
+ break
1578
+ ret.append(row)
1579
+ return ret
1580
+
1581
+ def nextset(self) -> SnowflakeCursor | None:
1582
+ """
1583
+ Fetches the next set of results if the previously executed query was multi-statement so that subsequent calls
1584
+ to any of the fetch*() methods will return rows from the next query's set of results. Returns None if no more
1585
+ query results are available.
1586
+ """
1587
+ if self._prefetch_hook is not None:
1588
+ self._prefetch_hook()
1589
+ self.reset()
1590
+ if self._multi_statement_resultIds:
1591
+ self.query_result(self._multi_statement_resultIds[0])
1592
+ logger.info(
1593
+ f"Retrieved results for query ID: {self._multi_statement_resultIds.popleft()}"
1594
+ )
1595
+ return self
1596
+
1597
+ return None
1598
+
1599
+ def setinputsizes(self, _) -> None:
1600
+ """Not supported."""
1601
+ logger.debug("nop")
1602
+
1603
+ def setoutputsize(self, _, column=None) -> None:
1604
+ """Not supported."""
1605
+ del column
1606
+ logger.debug("nop")
1607
+
1608
+ def scroll(self, value, mode: str = "relative") -> None:
1609
+ Error.errorhandler_wrapper(
1610
+ self.connection,
1611
+ self,
1612
+ NotSupportedError,
1613
+ {
1614
+ "msg": "scroll is not supported.",
1615
+ "errno": ER_UNSUPPORTED_METHOD,
1616
+ "sqlstate": SQLSTATE_FEATURE_NOT_SUPPORTED,
1617
+ },
1618
+ )
1619
+
1620
+ def reset(self, closing: bool = False) -> None:
1621
+ """Resets the result set."""
1622
+ # SNOW-647539: Do not erase the rowcount
1623
+ # information when closing the cursor
1624
+ if not closing:
1625
+ self._total_rowcount = -1
1626
+ if self._result_state != ResultState.DEFAULT:
1627
+ self._result_state = ResultState.RESET
1628
+ if self._result is not None:
1629
+ self._result = None
1630
+ if self._inner_cursor is not None:
1631
+ self._inner_cursor.reset(closing=closing)
1632
+ self._result = None
1633
+ self._inner_cursor = None
1634
+ self._prefetch_hook = None
1635
+ if not self.connection._reuse_results:
1636
+ self._result_set = None
1637
+
1638
+ def __iter__(self) -> Iterator[dict] | Iterator[tuple]:
1639
+ """Iteration over the result set."""
1640
+ while True:
1641
+ _next = self.fetchone()
1642
+ if _next is None:
1643
+ break
1644
+ yield _next
1645
+
1646
+ def __cancel_query(self, query) -> None:
1647
+ if self._sequence_counter >= 0 and not self.is_closed():
1648
+ logger.debug("canceled. %s, request_id: %s", query, self._request_id)
1649
+ with self._lock_canceling:
1650
+ self._connection._cancel_query(query, self._request_id)
1651
+
1652
+ def _log_telemetry_job_data(
1653
+ self, telemetry_field: TelemetryField, value: Any
1654
+ ) -> None:
1655
+ """Builds an instance of TelemetryData with the given field and logs it."""
1656
+ ts = get_time_millis()
1657
+ try:
1658
+ self._connection._log_telemetry(
1659
+ TelemetryData.from_telemetry_data_dict(
1660
+ from_dict={
1661
+ TelemetryField.KEY_TYPE.value: telemetry_field.value,
1662
+ TelemetryField.KEY_SFQID.value: self._sfqid,
1663
+ TelemetryField.KEY_VALUE.value: value,
1664
+ },
1665
+ timestamp=ts,
1666
+ connection=self._connection,
1667
+ )
1668
+ )
1669
+ except AttributeError:
1670
+ logger.warning(
1671
+ "Cursor failed to log to telemetry. Connection object may be None.",
1672
+ exc_info=True,
1673
+ )
1674
+
1675
+ def __enter__(self) -> Self:
1676
+ """Context manager."""
1677
+ return self
1678
+
1679
+ def __exit__(
1680
+ self,
1681
+ exc_type: type[BaseException] | None,
1682
+ exc_val: BaseException | None,
1683
+ exc_tb: TracebackType | None,
1684
+ ) -> None:
1685
+ """Context manager with commit or rollback."""
1686
+ self.close()
1687
+
1688
+ def get_results_from_sfqid(self, sfqid: str) -> None:
1689
+ """Gets the results from previously ran query. This methods differs from ``SnowflakeCursor.query_result``
1690
+ in that it monitors the ``sfqid`` until it is no longer running, and then retrieves the results.
1691
+ """
1692
+
1693
+ def wait_until_ready() -> None:
1694
+ """Makes sure query has finished executing and once it has retrieves results."""
1695
+ no_data_counter = 0
1696
+ retry_pattern_pos = 0
1697
+ while True:
1698
+ status, status_resp = self.connection._get_query_status(sfqid)
1699
+ self.connection._cache_query_status(sfqid, status)
1700
+ if not self.connection.is_still_running(status):
1701
+ break
1702
+ if status == QueryStatus.NO_DATA: # pragma: no cover
1703
+ no_data_counter += 1
1704
+ if no_data_counter > ASYNC_NO_DATA_MAX_RETRY:
1705
+ raise DatabaseError(
1706
+ "Cannot retrieve data on the status of this query. No information returned "
1707
+ "from server for query '{}'"
1708
+ )
1709
+ time.sleep(
1710
+ 0.5 * ASYNC_RETRY_PATTERN[retry_pattern_pos]
1711
+ ) # Same wait as JDBC
1712
+ # If we can advance in ASYNC_RETRY_PATTERN then do so
1713
+ if retry_pattern_pos < (len(ASYNC_RETRY_PATTERN) - 1):
1714
+ retry_pattern_pos += 1
1715
+ if status != QueryStatus.SUCCESS:
1716
+ logger.info(f"Status of query '{sfqid}' is {status.name}")
1717
+ self.connection._process_error_query_status(
1718
+ sfqid,
1719
+ status_resp,
1720
+ error_message=f"Status of query '{sfqid}' is {status.name}, results are unavailable",
1721
+ error_cls=DatabaseError,
1722
+ )
1723
+ self._inner_cursor.execute(f"select * from table(result_scan('{sfqid}'))")
1724
+ self._result = self._inner_cursor._result
1725
+ self._query_result_format = self._inner_cursor._query_result_format
1726
+ self._total_rowcount = self._inner_cursor._total_rowcount
1727
+ self._description = self._inner_cursor._description
1728
+ self._result_set = self._inner_cursor._result_set
1729
+ self._result_state = ResultState.VALID
1730
+ self._rownumber = 0
1731
+ # Unset this function, so that we don't block anymore
1732
+ self._prefetch_hook = None
1733
+
1734
+ if (
1735
+ self._inner_cursor._total_rowcount == 1
1736
+ and self._inner_cursor.fetchall()
1737
+ == [("Multiple statements executed successfully.",)]
1738
+ ):
1739
+ url = f"/queries/{sfqid}/result"
1740
+ ret = self._connection.rest.request(url=url, method="get")
1741
+ if "data" in ret and "resultIds" in ret["data"]:
1742
+ self._init_multi_statement_results(ret["data"])
1743
+
1744
+ self.connection.get_query_status_throw_if_error(
1745
+ sfqid
1746
+ ) # Trigger an exception if query failed
1747
+ klass = self.__class__
1748
+ self._inner_cursor = klass(self.connection)
1749
+ self._sfqid = sfqid
1750
+ self._prefetch_hook = wait_until_ready
1751
+
1752
+ def get_result_batches(self) -> list[ResultBatch] | None:
1753
+ """Get the previously executed query's ``ResultBatch`` s if available.
1754
+
1755
+ If they are unavailable, in case nothing has been executed yet None will
1756
+ be returned.
1757
+
1758
+ For a detailed description of ``ResultBatch`` s please see the docstring of:
1759
+ ``snowflake.connector.result_batches.ResultBatch``
1760
+ """
1761
+ if self._result_set is None:
1762
+ return None
1763
+ self._log_telemetry_job_data(
1764
+ TelemetryField.GET_PARTITIONS_USED, TelemetryData.TRUE
1765
+ )
1766
+ return self._result_set.batches
1767
+
1768
+ def _download(
1769
+ self,
1770
+ stage_location: str,
1771
+ target_directory: str,
1772
+ options: dict[str, Any],
1773
+ _do_reset: bool = True,
1774
+ ) -> None:
1775
+ """Downloads from the stage location to the target directory.
1776
+
1777
+ Args:
1778
+ stage_location (str): The location of the stage to download from.
1779
+ target_directory (str): The destination directory to download into.
1780
+ options (dict[str, Any]): The download options.
1781
+ _do_reset (bool, optional): Whether to reset the cursor before
1782
+ downloading, by default we will reset the cursor.
1783
+ """
1784
+ from .file_transfer_agent import SnowflakeFileTransferAgent
1785
+
1786
+ if _do_reset:
1787
+ self.reset()
1788
+
1789
+ # Interpret the file operation.
1790
+ ret = self.connection._file_operation_parser.parse_file_operation(
1791
+ stage_location=stage_location,
1792
+ local_file_name=None,
1793
+ target_directory=target_directory,
1794
+ command_type=CMD_TYPE_DOWNLOAD,
1795
+ options=options,
1796
+ )
1797
+
1798
+ # Execute the file operation based on the interpretation above.
1799
+ file_transfer_agent = SnowflakeFileTransferAgent(
1800
+ self,
1801
+ "", # empty command because it is triggered by directly calling this util not by a SQL query
1802
+ ret,
1803
+ )
1804
+ file_transfer_agent.execute()
1805
+ self._init_result_and_meta(file_transfer_agent.result())
1806
+
1807
+ def _upload(
1808
+ self,
1809
+ local_file_name: str,
1810
+ stage_location: str,
1811
+ options: dict[str, Any],
1812
+ _do_reset: bool = True,
1813
+ ) -> None:
1814
+ """Uploads the local file to the stage location.
1815
+
1816
+ Args:
1817
+ local_file_name (str): The local file to be uploaded.
1818
+ stage_location (str): The stage location to upload the local file to.
1819
+ options (dict[str, Any]): The upload options.
1820
+ _do_reset (bool, optional): Whether to reset the cursor before
1821
+ uploading, by default we will reset the cursor.
1822
+ """
1823
+ from .file_transfer_agent import SnowflakeFileTransferAgent
1824
+
1825
+ if _do_reset:
1826
+ self.reset()
1827
+
1828
+ # Interpret the file operation.
1829
+ ret = self.connection._file_operation_parser.parse_file_operation(
1830
+ stage_location=stage_location,
1831
+ local_file_name=local_file_name,
1832
+ target_directory=None,
1833
+ command_type=CMD_TYPE_UPLOAD,
1834
+ options=options,
1835
+ )
1836
+
1837
+ # Execute the file operation based on the interpretation above.
1838
+ file_transfer_agent = SnowflakeFileTransferAgent(
1839
+ self,
1840
+ "", # empty command because it is triggered by directly calling this util not by a SQL query
1841
+ ret,
1842
+ force_put_overwrite=False, # _upload should respect user decision on overwriting
1843
+ )
1844
+ file_transfer_agent.execute()
1845
+ self._init_result_and_meta(file_transfer_agent.result())
1846
+
1847
+ def _download_stream(
1848
+ self, stage_location: str, decompress: bool = False
1849
+ ) -> IO[bytes]:
1850
+ """Downloads from the stage location as a stream.
1851
+
1852
+ Args:
1853
+ stage_location (str): The location of the stage to download from.
1854
+ decompress (bool, optional): Whether to decompress the file, by
1855
+ default we do not decompress.
1856
+
1857
+ Returns:
1858
+ IO[bytes]: A stream to read from.
1859
+ """
1860
+ # Interpret the file operation.
1861
+ ret = self.connection._file_operation_parser.parse_file_operation(
1862
+ stage_location=stage_location,
1863
+ local_file_name=None,
1864
+ target_directory=None,
1865
+ command_type=CMD_TYPE_DOWNLOAD,
1866
+ options=None,
1867
+ has_source_from_stream=True,
1868
+ )
1869
+
1870
+ # Set up stream downloading based on the interpretation and return the stream for reading.
1871
+ return self.connection._stream_downloader.download_as_stream(ret, decompress)
1872
+
1873
+ def _upload_stream(
1874
+ self,
1875
+ input_stream: IO[bytes],
1876
+ stage_location: str,
1877
+ options: dict[str, Any],
1878
+ _do_reset: bool = True,
1879
+ ) -> None:
1880
+ """Uploads content in the input stream to the stage location.
1881
+
1882
+ Args:
1883
+ input_stream (IO[bytes]): A stream to read from.
1884
+ stage_location (str): The location of the stage to upload to.
1885
+ options (dict[str, Any]): The upload options.
1886
+ _do_reset (bool, optional): Whether to reset the cursor before
1887
+ uploading, by default we will reset the cursor.
1888
+ """
1889
+ from .file_transfer_agent import SnowflakeFileTransferAgent
1890
+
1891
+ if _do_reset:
1892
+ self.reset()
1893
+
1894
+ # Interpret the file operation.
1895
+ ret = self.connection._file_operation_parser.parse_file_operation(
1896
+ stage_location=stage_location,
1897
+ local_file_name=None,
1898
+ target_directory=None,
1899
+ command_type=CMD_TYPE_UPLOAD,
1900
+ options=options,
1901
+ has_source_from_stream=input_stream,
1902
+ )
1903
+
1904
+ # Execute the file operation based on the interpretation above.
1905
+ file_transfer_agent = SnowflakeFileTransferAgent(
1906
+ self,
1907
+ "", # empty command because it is triggered by directly calling this util not by a SQL query
1908
+ ret,
1909
+ source_from_stream=input_stream,
1910
+ force_put_overwrite=False, # _upload_stream should respect user decision on overwriting
1911
+ )
1912
+ file_transfer_agent.execute()
1913
+ self._init_result_and_meta(file_transfer_agent.result())
1914
+
1915
+
1916
+ class DictCursor(SnowflakeCursor):
1917
+ """Cursor returning results in a dictionary."""
1918
+
1919
+ def __init__(self, connection) -> None:
1920
+ super().__init__(
1921
+ connection,
1922
+ use_dict_result=True,
1923
+ )
1924
+
1925
+
1926
+ def __getattr__(name):
1927
+ if name == "NanoarrowUsage":
1928
+ warnings.warn(
1929
+ "snowflake.connector.cursor.NanoarrowUsage has been deprecated and will be removed in the future, "
1930
+ "please stop using the class.",
1931
+ DeprecationWarning,
1932
+ stacklevel=2,
1933
+ )
1934
+ return _NanoarrowUsage
1935
+ if name == "NANOARROW_USAGE":
1936
+ if "NANOARROW_USAGE" in os.environ:
1937
+ warnings.warn(
1938
+ "Environment variable NANOARROW_USAGE has been deprecated and will be removed in the future, "
1939
+ "please stop setting the variable.",
1940
+ DeprecationWarning,
1941
+ stacklevel=2,
1942
+ )
1943
+
1944
+ warnings.warn(
1945
+ "snowflake.connector.cursor.NANOARROW_USAGE has been deprecated and will be removed in the future, "
1946
+ "please stop using the variable.",
1947
+ DeprecationWarning,
1948
+ stacklevel=2,
1949
+ )
1950
+ return None
1951
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")