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,786 @@
1
+ from __future__ import annotations
2
+
3
+ import abc
4
+ import json
5
+ import time
6
+ from base64 import b64decode
7
+ from enum import Enum, unique
8
+ from logging import getLogger
9
+ from typing import TYPE_CHECKING, Any, Callable, Iterator, NamedTuple, Sequence
10
+
11
+ from typing_extensions import Self
12
+
13
+ from .arrow_context import ArrowConverterContext
14
+ from .backoff_policies import exponential_backoff
15
+ from .compat import OK, UNAUTHORIZED, urlparse
16
+ from .constants import FIELD_TYPES, IterUnit
17
+ from .errorcode import ER_FAILED_TO_CONVERT_ROW_TO_PYTHON_TYPE, ER_NO_PYARROW
18
+ from .errors import Error, InterfaceError, NotSupportedError, ProgrammingError
19
+ from .network import (
20
+ RetryRequest,
21
+ get_http_retryable_error,
22
+ is_retryable_http_code,
23
+ raise_failed_request_error,
24
+ raise_okta_unauthorized_error,
25
+ )
26
+ from .options import installed_pandas
27
+ from .options import pyarrow as pa
28
+ from .secret_detector import SecretDetector
29
+ from .time_util import TimerContextManager
30
+ from .vendored import requests
31
+
32
+ logger = getLogger(__name__)
33
+
34
+ MAX_DOWNLOAD_RETRY = 10
35
+ DOWNLOAD_TIMEOUT = 7 # seconds
36
+
37
+ if TYPE_CHECKING: # pragma: no cover
38
+ from pandas import DataFrame
39
+ from pyarrow import DataType, Table
40
+
41
+ from .connection import SnowflakeConnection
42
+ from .converter import SnowflakeConverterType
43
+ from .cursor import ResultMetadataV2, SnowflakeCursor
44
+ from .vendored.requests import Response
45
+
46
+
47
+ # emtpy pyarrow type array corresponding to FIELD_TYPES
48
+ FIELD_TYPE_TO_PA_TYPE: list[Callable[[ResultMetadataV2], DataType]] = []
49
+
50
+ # qrmk related constants
51
+ SSE_C_ALGORITHM = "x-amz-server-side-encryption-customer-algorithm"
52
+ SSE_C_KEY = "x-amz-server-side-encryption-customer-key"
53
+ SSE_C_AES = "AES256"
54
+
55
+
56
+ def _create_nanoarrow_iterator(
57
+ data: bytes,
58
+ context: ArrowConverterContext,
59
+ use_dict_result: bool,
60
+ numpy: bool,
61
+ number_to_decimal: bool,
62
+ row_unit: IterUnit,
63
+ check_error_on_every_column: bool = True,
64
+ ):
65
+ from .nanoarrow_arrow_iterator import PyArrowRowIterator, PyArrowTableIterator
66
+
67
+ logger.debug("Using nanoarrow as the arrow data converter")
68
+ return (
69
+ PyArrowRowIterator(
70
+ None,
71
+ data,
72
+ context,
73
+ use_dict_result,
74
+ numpy,
75
+ number_to_decimal,
76
+ check_error_on_every_column,
77
+ )
78
+ if row_unit == IterUnit.ROW_UNIT
79
+ else PyArrowTableIterator(
80
+ None,
81
+ data,
82
+ context,
83
+ use_dict_result,
84
+ numpy,
85
+ number_to_decimal,
86
+ check_error_on_every_column,
87
+ )
88
+ )
89
+
90
+
91
+ @unique
92
+ class DownloadMetrics(Enum):
93
+ """Defines the keywords by which to store metrics for chunks."""
94
+
95
+ download = "download" # Download time in milliseconds
96
+ parse = "parse" # Parsing time to final data types
97
+ load = "load" # Parsing time from initial type to intermediate types
98
+
99
+
100
+ class RemoteChunkInfo(NamedTuple):
101
+ """Small class that holds information about chunks that are given by back-end."""
102
+
103
+ url: str
104
+ uncompressedSize: int
105
+ compressedSize: int
106
+
107
+
108
+ def create_batches_from_response(
109
+ cursor: SnowflakeCursor,
110
+ _format: str,
111
+ data: dict[str, Any],
112
+ schema: Sequence[ResultMetadataV2],
113
+ ) -> list[ResultBatch]:
114
+ column_converters: list[tuple[str, SnowflakeConverterType]] = []
115
+ arrow_context: ArrowConverterContext | None = None
116
+ rowtypes = data["rowtype"]
117
+ total_len: int = data.get("total", 0)
118
+ first_chunk_len = total_len
119
+ rest_of_chunks: list[ResultBatch] = []
120
+ if _format == "json":
121
+
122
+ def col_to_converter(col: dict[str, Any]) -> tuple[str, SnowflakeConverterType]:
123
+ type_name = col["type"].upper()
124
+ python_method = cursor._connection.converter.to_python_method(
125
+ type_name, col
126
+ )
127
+ return type_name, python_method
128
+
129
+ column_converters = [col_to_converter(c) for c in rowtypes]
130
+ else:
131
+ rowset_b64 = data.get("rowsetBase64")
132
+ arrow_context = ArrowConverterContext(cursor._connection._session_parameters)
133
+ if "chunks" in data:
134
+ chunks = data["chunks"]
135
+ logger.debug(f"chunk size={len(chunks)}")
136
+ # prepare the downloader for further fetch
137
+ qrmk = data.get("qrmk")
138
+ chunk_headers: dict[str, Any] = {}
139
+ if "chunkHeaders" in data:
140
+ chunk_headers = {}
141
+ for header_key, header_value in data["chunkHeaders"].items():
142
+ chunk_headers[header_key] = header_value
143
+ if "encryption" not in header_key:
144
+ logger.debug(
145
+ f"added chunk header: key={header_key}, value={header_value}"
146
+ )
147
+ elif qrmk is not None:
148
+ logger.debug(f"qrmk={SecretDetector.mask_secrets(qrmk)}")
149
+ chunk_headers[SSE_C_ALGORITHM] = SSE_C_AES
150
+ chunk_headers[SSE_C_KEY] = qrmk
151
+
152
+ def remote_chunk_info(c: dict[str, Any]) -> RemoteChunkInfo:
153
+ return RemoteChunkInfo(
154
+ url=c["url"],
155
+ uncompressedSize=c["uncompressedSize"],
156
+ compressedSize=c["compressedSize"],
157
+ )
158
+
159
+ if _format == "json":
160
+ rest_of_chunks = [
161
+ JSONResultBatch(
162
+ c["rowCount"],
163
+ chunk_headers,
164
+ remote_chunk_info(c),
165
+ schema,
166
+ column_converters,
167
+ cursor._use_dict_result,
168
+ json_result_force_utf8_decoding=cursor._connection._json_result_force_utf8_decoding,
169
+ )
170
+ for c in chunks
171
+ ]
172
+ else:
173
+ rest_of_chunks = [
174
+ ArrowResultBatch(
175
+ c["rowCount"],
176
+ chunk_headers,
177
+ remote_chunk_info(c),
178
+ arrow_context,
179
+ cursor._use_dict_result,
180
+ cursor._connection._numpy,
181
+ schema,
182
+ cursor._connection._arrow_number_to_decimal,
183
+ )
184
+ for c in chunks
185
+ ]
186
+ for c in rest_of_chunks:
187
+ first_chunk_len -= c.rowcount
188
+ if _format == "json":
189
+ first_chunk = JSONResultBatch.from_data(
190
+ data.get("rowset"),
191
+ first_chunk_len,
192
+ schema,
193
+ column_converters,
194
+ cursor._use_dict_result,
195
+ )
196
+ elif rowset_b64 is not None:
197
+ first_chunk = ArrowResultBatch.from_data(
198
+ rowset_b64,
199
+ first_chunk_len,
200
+ arrow_context,
201
+ cursor._use_dict_result,
202
+ cursor._connection._numpy,
203
+ schema,
204
+ cursor._connection._arrow_number_to_decimal,
205
+ )
206
+ else:
207
+ logger.error(f"Don't know how to construct ResultBatches from response: {data}")
208
+ first_chunk = ArrowResultBatch.from_data(
209
+ "",
210
+ 0,
211
+ arrow_context,
212
+ cursor._use_dict_result,
213
+ cursor._connection._numpy,
214
+ schema,
215
+ cursor._connection._arrow_number_to_decimal,
216
+ )
217
+
218
+ return [first_chunk] + rest_of_chunks
219
+
220
+
221
+ class ResultBatch(abc.ABC):
222
+ """Represents what the back-end calls a result chunk.
223
+
224
+ These are parts of a result set of a query. They each know how to retrieve their
225
+ own results and convert them into Python native formats.
226
+
227
+ As you are iterating through a ResultBatch you should check whether the yielded
228
+ value is an ``Exception`` in case there was some error parsing the current row
229
+ we might yield one of these to allow iteration to continue instead of raising the
230
+ ``Exception`` when it occurs.
231
+
232
+ These objects are pickleable for easy distribution and replication.
233
+
234
+ Please note that the URLs stored in these do expire. The lifetime is dictated by the
235
+ Snowflake back-end, at the time of writing this this is 6 hours.
236
+
237
+ They can be iterated over multiple times and in different ways. Please follow the
238
+ code in ``cursor.py`` to make sure that you are using this class correctly.
239
+
240
+ """
241
+
242
+ def __init__(
243
+ self,
244
+ rowcount: int,
245
+ chunk_headers: dict[str, str] | None,
246
+ remote_chunk_info: RemoteChunkInfo | None,
247
+ schema: Sequence[ResultMetadataV2],
248
+ use_dict_result: bool,
249
+ ) -> None:
250
+ self.rowcount = rowcount
251
+ self._chunk_headers = chunk_headers
252
+ self._remote_chunk_info = remote_chunk_info
253
+ self._schema = schema
254
+ self.schema = (
255
+ [s._to_result_metadata_v1() for s in schema] if schema is not None else None
256
+ )
257
+ self._use_dict_result = use_dict_result
258
+ self._metrics: dict[str, int] = {}
259
+ self._data: str | list[tuple[Any, ...]] | None = None
260
+ if self._remote_chunk_info:
261
+ parsed_url = urlparse(self._remote_chunk_info.url)
262
+ path_parts = parsed_url.path.rsplit("/", 1)
263
+ self.id = path_parts[-1]
264
+ else:
265
+ self.id = str(self.rowcount)
266
+
267
+ @property
268
+ def _local(self) -> bool:
269
+ """Whether this chunk is local."""
270
+ return self._data is not None
271
+
272
+ @property
273
+ def compressed_size(self) -> int | None:
274
+ """Returns the size of chunk in bytes in compressed form.
275
+
276
+ If it's a local chunk this function returns None.
277
+ """
278
+ if self._local:
279
+ return None
280
+ return self._remote_chunk_info.compressedSize
281
+
282
+ @property
283
+ def uncompressed_size(self) -> int | None:
284
+ """Returns the size of chunk in bytes in uncompressed form.
285
+
286
+ If it's a local chunk this function returns None.
287
+ """
288
+ if self._local:
289
+ return None
290
+ return self._remote_chunk_info.uncompressedSize
291
+
292
+ @property
293
+ def column_names(self) -> list[str]:
294
+ return [col.name for col in self._schema]
295
+
296
+ def __iter__(
297
+ self,
298
+ ) -> Iterator[dict | Exception] | Iterator[tuple | Exception]:
299
+ """Returns an iterator through the data this chunk holds.
300
+
301
+ In case of this chunk being a local one it iterates through the local already
302
+ parsed data and if it's a remote chunk it will download, parse its data and
303
+ return an iterator through it.
304
+ """
305
+ return self.create_iter()
306
+
307
+ def _download(
308
+ self, connection: SnowflakeConnection | None = None, **kwargs
309
+ ) -> Response:
310
+ """Downloads the data that the ``ResultBatch`` is pointing at."""
311
+ sleep_timer = 1
312
+ backoff = (
313
+ connection._backoff_generator
314
+ if connection is not None
315
+ else exponential_backoff()()
316
+ )
317
+ for retry in range(MAX_DOWNLOAD_RETRY):
318
+ try:
319
+ with TimerContextManager() as download_metric:
320
+ logger.debug(f"started downloading result batch id: {self.id}")
321
+ chunk_url = self._remote_chunk_info.url
322
+ request_data = {
323
+ "url": chunk_url,
324
+ "headers": self._chunk_headers,
325
+ "timeout": DOWNLOAD_TIMEOUT,
326
+ }
327
+ # Try to reuse a connection if possible
328
+ if connection and connection._rest is not None:
329
+ with connection._rest._use_requests_session() as session:
330
+ logger.debug(
331
+ f"downloading result batch id: {self.id} with existing session {session}"
332
+ )
333
+ response = session.request("get", **request_data)
334
+ else:
335
+ logger.debug(
336
+ f"downloading result batch id: {self.id} with new session"
337
+ )
338
+ response = requests.get(**request_data)
339
+
340
+ if response.status_code == OK:
341
+ logger.debug(
342
+ f"successfully downloaded result batch id: {self.id}"
343
+ )
344
+ break
345
+
346
+ # Raise error here to correctly go in to exception clause
347
+ if is_retryable_http_code(response.status_code):
348
+ # retryable server exceptions
349
+ error: Error = get_http_retryable_error(response.status_code)
350
+ raise RetryRequest(error)
351
+ elif response.status_code == UNAUTHORIZED:
352
+ # make a unauthorized error
353
+ raise_okta_unauthorized_error(None, response)
354
+ else:
355
+ raise_failed_request_error(None, chunk_url, "get", response)
356
+
357
+ except (RetryRequest, Exception) as e:
358
+ if retry == MAX_DOWNLOAD_RETRY - 1:
359
+ # Re-throw if we failed on the last retry
360
+ e = e.args[0] if isinstance(e, RetryRequest) else e
361
+ raise e
362
+ sleep_timer = next(backoff)
363
+ logger.exception(
364
+ f"Failed to fetch the large result set batch "
365
+ f"{self.id} for the {retry + 1} th time, "
366
+ f"backing off for {sleep_timer}s for the reason: '{e}'"
367
+ )
368
+ time.sleep(sleep_timer)
369
+
370
+ self._metrics[DownloadMetrics.download.value] = (
371
+ download_metric.get_timing_millis()
372
+ )
373
+ return response
374
+
375
+ @abc.abstractmethod
376
+ def create_iter(
377
+ self, **kwargs
378
+ ) -> (
379
+ Iterator[dict | Exception]
380
+ | Iterator[tuple | Exception]
381
+ | Iterator[Table]
382
+ | Iterator[DataFrame]
383
+ ):
384
+ """Downloads the data from from blob storage that this ResultChunk points at.
385
+
386
+ This function is the one that does the actual work for ``self.__iter__``.
387
+
388
+ It is necessary because a ``ResultBatch`` can return multiple types of
389
+ iterators. A good example of this is simply iterating through
390
+ ``SnowflakeCursor`` and calling ``fetch_pandas_batches`` on it.
391
+ """
392
+ raise NotImplementedError()
393
+
394
+ def _check_can_use_pandas(self) -> None:
395
+ if not installed_pandas:
396
+ msg = (
397
+ "Optional dependency: 'pandas' is not installed, please see the following link for install "
398
+ "instructions: https://docs.snowflake.com/en/user-guide/python-connector-pandas.html#installation"
399
+ )
400
+ errno = ER_NO_PYARROW
401
+
402
+ raise Error.errorhandler_make_exception(
403
+ ProgrammingError,
404
+ {
405
+ "msg": msg,
406
+ "errno": errno,
407
+ },
408
+ )
409
+
410
+ @abc.abstractmethod
411
+ def to_pandas(self) -> DataFrame:
412
+ raise NotImplementedError()
413
+
414
+ @abc.abstractmethod
415
+ def to_arrow(self) -> Table:
416
+ raise NotImplementedError()
417
+
418
+ @abc.abstractmethod
419
+ def populate_data(
420
+ self, connection: SnowflakeConnection | None = None, **kwargs
421
+ ) -> Self:
422
+ """Downloads the data that the ``ResultBatch`` is pointing at and populates it into self._data.
423
+ Returns the instance itself."""
424
+ raise NotImplementedError()
425
+
426
+
427
+ class JSONResultBatch(ResultBatch):
428
+ def __init__(
429
+ self,
430
+ rowcount: int,
431
+ chunk_headers: dict[str, str] | None,
432
+ remote_chunk_info: RemoteChunkInfo | None,
433
+ schema: Sequence[ResultMetadataV2],
434
+ column_converters: Sequence[tuple[str, SnowflakeConverterType]],
435
+ use_dict_result: bool,
436
+ *,
437
+ json_result_force_utf8_decoding: bool = False,
438
+ ) -> None:
439
+ super().__init__(
440
+ rowcount,
441
+ chunk_headers,
442
+ remote_chunk_info,
443
+ schema,
444
+ use_dict_result,
445
+ )
446
+ self._json_result_force_utf8_decoding = json_result_force_utf8_decoding
447
+ self.column_converters = column_converters
448
+
449
+ @classmethod
450
+ def from_data(
451
+ cls,
452
+ data: Sequence[Sequence[Any]],
453
+ data_len: int,
454
+ schema: Sequence[ResultMetadataV2],
455
+ column_converters: Sequence[tuple[str, SnowflakeConverterType]],
456
+ use_dict_result: bool,
457
+ ):
458
+ """Initializes a ``JSONResultBatch`` from static, local data."""
459
+ new_chunk = cls(
460
+ len(data),
461
+ None,
462
+ None,
463
+ schema,
464
+ column_converters,
465
+ use_dict_result,
466
+ )
467
+ new_chunk._data = new_chunk._parse(data)
468
+ return new_chunk
469
+
470
+ def _load(self, response: Response) -> list:
471
+ """This function loads a compressed JSON file into memory.
472
+
473
+ Returns:
474
+ Whatever ``json.loads`` return, but in a list.
475
+ Unfortunately there's no type hint for this.
476
+ For context: https://github.com/python/typing/issues/182
477
+ """
478
+ # if users specify how to decode the data, we decode the bytes using the specified encoding
479
+ if self._json_result_force_utf8_decoding:
480
+ try:
481
+ read_data = str(response.content, "utf-8", errors="strict")
482
+ except Exception as exc:
483
+ err_msg = f"failed to decode json result content due to error {exc!r}"
484
+ logger.error(err_msg)
485
+ raise Error(msg=err_msg)
486
+ else:
487
+ # note: SNOW-787480 response.apparent_encoding is unreliable, chardet.detect can be wrong which is used by
488
+ # response.text to decode content, check issue: https://github.com/chardet/chardet/issues/148
489
+ read_data = response.text
490
+ return json.loads("".join(["[", read_data, "]"]))
491
+
492
+ def _parse(
493
+ self, downloaded_data
494
+ ) -> list[dict | Exception] | list[tuple | Exception]:
495
+ """Parses downloaded data into its final form."""
496
+ logger.debug(f"parsing for result batch id: {self.id}")
497
+ result_list = []
498
+ if self._use_dict_result:
499
+ for row in downloaded_data:
500
+ row_result = {}
501
+ try:
502
+ for (_t, c), v, col in zip(
503
+ self.column_converters,
504
+ row,
505
+ self._schema,
506
+ ):
507
+ row_result[col.name] = v if c is None or v is None else c(v)
508
+ result_list.append(row_result)
509
+ except Exception as error:
510
+ msg = f"Failed to convert: field {col.name}: {_t}::{v}, Error: {error}"
511
+ logger.exception(msg)
512
+ result_list.append(
513
+ Error.errorhandler_make_exception(
514
+ InterfaceError,
515
+ {
516
+ "msg": msg,
517
+ "errno": ER_FAILED_TO_CONVERT_ROW_TO_PYTHON_TYPE,
518
+ },
519
+ )
520
+ )
521
+ else:
522
+ for row in downloaded_data:
523
+ row_result = [None] * len(self._schema)
524
+ try:
525
+ idx = 0
526
+ for (_t, c), v, _col in zip(
527
+ self.column_converters,
528
+ row,
529
+ self._schema,
530
+ ):
531
+ row_result[idx] = v if c is None or v is None else c(v)
532
+ idx += 1
533
+ result_list.append(tuple(row_result))
534
+ except Exception as error:
535
+ msg = f"Failed to convert: field {_col.name}: {_t}::{v}, Error: {error}"
536
+ logger.exception(msg)
537
+ result_list.append(
538
+ Error.errorhandler_make_exception(
539
+ InterfaceError,
540
+ {
541
+ "msg": msg,
542
+ "errno": ER_FAILED_TO_CONVERT_ROW_TO_PYTHON_TYPE,
543
+ },
544
+ )
545
+ )
546
+ return result_list
547
+
548
+ def __repr__(self) -> str:
549
+ return f"JSONResultChunk({self.id})"
550
+
551
+ def _fetch_data(
552
+ self, connection: SnowflakeConnection | None = None, **kwargs
553
+ ) -> list[dict | Exception] | list[tuple | Exception]:
554
+ response = self._download(connection=connection)
555
+ # Load data to a intermediate form
556
+ logger.debug(f"started loading result batch id: {self.id}")
557
+ with TimerContextManager() as load_metric:
558
+ downloaded_data = self._load(response)
559
+ logger.debug(f"finished loading result batch id: {self.id}")
560
+ self._metrics[DownloadMetrics.load.value] = load_metric.get_timing_millis()
561
+ # Process downloaded data
562
+ with TimerContextManager() as parse_metric:
563
+ parsed_data = self._parse(downloaded_data)
564
+ self._metrics[DownloadMetrics.parse.value] = parse_metric.get_timing_millis()
565
+ return parsed_data
566
+
567
+ def populate_data(
568
+ self, connection: SnowflakeConnection | None = None, **kwargs
569
+ ) -> Self:
570
+ self._data = self._fetch_data(connection=connection, **kwargs)
571
+ return self
572
+
573
+ def create_iter(
574
+ self, connection: SnowflakeConnection | None = None, **kwargs
575
+ ) -> Iterator[dict | Exception] | Iterator[tuple | Exception]:
576
+ if self._local:
577
+ return iter(self._data)
578
+ return iter(self._fetch_data(connection=connection, **kwargs))
579
+
580
+ def _arrow_fetching_error(self):
581
+ return NotSupportedError(
582
+ f"Trying to use arrow fetching on {type(self)} which "
583
+ f"is not ArrowResultChunk"
584
+ )
585
+
586
+ def to_pandas(self):
587
+ raise self._arrow_fetching_error()
588
+
589
+ def to_arrow(self):
590
+ raise self._arrow_fetching_error()
591
+
592
+
593
+ class ArrowResultBatch(ResultBatch):
594
+ def __init__(
595
+ self,
596
+ rowcount: int,
597
+ chunk_headers: dict[str, str] | None,
598
+ remote_chunk_info: RemoteChunkInfo | None,
599
+ context: ArrowConverterContext,
600
+ use_dict_result: bool,
601
+ numpy: bool,
602
+ schema: Sequence[ResultMetadataV2],
603
+ number_to_decimal: bool,
604
+ ) -> None:
605
+ super().__init__(
606
+ rowcount,
607
+ chunk_headers,
608
+ remote_chunk_info,
609
+ schema,
610
+ use_dict_result,
611
+ )
612
+ self._context = context
613
+ self._numpy = numpy
614
+ self._number_to_decimal = number_to_decimal
615
+
616
+ def __repr__(self) -> str:
617
+ return f"ArrowResultChunk({self.id})"
618
+
619
+ def _load(
620
+ self, response: Response, row_unit: IterUnit
621
+ ) -> Iterator[dict | Exception] | Iterator[tuple | Exception]:
622
+ """Creates a ``PyArrowIterator`` from a response.
623
+
624
+ This is used to iterate through results in different ways depending on which
625
+ mode that ``PyArrowIterator`` is in.
626
+ """
627
+ return _create_nanoarrow_iterator(
628
+ response.content,
629
+ self._context,
630
+ self._use_dict_result,
631
+ self._numpy,
632
+ self._number_to_decimal,
633
+ row_unit,
634
+ )
635
+
636
+ def _from_data(
637
+ self,
638
+ data: str | bytes,
639
+ iter_unit: IterUnit,
640
+ check_error_on_every_column: bool = True,
641
+ ) -> Iterator[dict | Exception] | Iterator[tuple | Exception]:
642
+ """Creates a ``PyArrowIterator`` files from a str.
643
+
644
+ This is used to iterate through results in different ways depending on which
645
+ mode that ``PyArrowIterator`` is in.
646
+ """
647
+ if len(data) == 0:
648
+ return iter([])
649
+
650
+ if isinstance(data, str):
651
+ data = b64decode(data)
652
+
653
+ return _create_nanoarrow_iterator(
654
+ data,
655
+ self._context,
656
+ self._use_dict_result,
657
+ self._numpy,
658
+ self._number_to_decimal,
659
+ iter_unit,
660
+ check_error_on_every_column,
661
+ )
662
+
663
+ @classmethod
664
+ def from_data(
665
+ cls,
666
+ data: str,
667
+ data_len: int,
668
+ context: ArrowConverterContext,
669
+ use_dict_result: bool,
670
+ numpy: bool,
671
+ schema: Sequence[ResultMetadataV2],
672
+ number_to_decimal: bool,
673
+ ):
674
+ """Initializes an ``ArrowResultBatch`` from static, local data."""
675
+ new_chunk = cls(
676
+ data_len,
677
+ None,
678
+ None,
679
+ context,
680
+ use_dict_result,
681
+ numpy,
682
+ schema,
683
+ number_to_decimal,
684
+ )
685
+ new_chunk._data = data
686
+
687
+ return new_chunk
688
+
689
+ def _create_iter(
690
+ self, iter_unit: IterUnit, connection: SnowflakeConnection | None = None
691
+ ) -> Iterator[dict | Exception] | Iterator[tuple | Exception] | Iterator[Table]:
692
+ """Create an iterator for the ResultBatch. Used by get_arrow_iter."""
693
+ if self._local:
694
+ try:
695
+ return self._from_data(
696
+ self._data,
697
+ iter_unit,
698
+ (
699
+ connection.check_arrow_conversion_error_on_every_column
700
+ if connection
701
+ else None
702
+ ),
703
+ )
704
+ except Exception:
705
+ if connection and getattr(connection, "_debug_arrow_chunk", False):
706
+ logger.debug(f"arrow data can not be parsed: {self._data}")
707
+ raise
708
+ response = self._download(connection=connection)
709
+ logger.debug(f"started loading result batch id: {self.id}")
710
+ with TimerContextManager() as load_metric:
711
+ try:
712
+ loaded_data = self._load(response, iter_unit)
713
+ except Exception:
714
+ if connection and getattr(connection, "_debug_arrow_chunk", False):
715
+ logger.debug(f"arrow data can not be parsed: {response}")
716
+ raise
717
+ logger.debug(f"finished loading result batch id: {self.id}")
718
+ self._metrics[DownloadMetrics.load.value] = load_metric.get_timing_millis()
719
+ return loaded_data
720
+
721
+ def _get_arrow_iter(
722
+ self, connection: SnowflakeConnection | None = None
723
+ ) -> Iterator[Table]:
724
+ """Returns an iterator for this batch which yields a pyarrow Table"""
725
+ return self._create_iter(iter_unit=IterUnit.TABLE_UNIT, connection=connection)
726
+
727
+ def _create_empty_table(self) -> Table:
728
+ """Returns empty Arrow table based on schema"""
729
+ if installed_pandas:
730
+ # initialize pyarrow type array corresponding to FIELD_TYPES
731
+ FIELD_TYPE_TO_PA_TYPE = [e.pa_type for e in FIELD_TYPES]
732
+ fields = [
733
+ pa.field(s.name, FIELD_TYPE_TO_PA_TYPE[s.type_code](s))
734
+ for s in self._schema
735
+ ]
736
+ return pa.schema(fields).empty_table()
737
+
738
+ def to_arrow(self, connection: SnowflakeConnection | None = None) -> Table:
739
+ """Returns this batch as a pyarrow Table"""
740
+ val = next(self._get_arrow_iter(connection=connection), None)
741
+ if val is not None:
742
+ return val
743
+ return self._create_empty_table()
744
+
745
+ def to_pandas(
746
+ self, connection: SnowflakeConnection | None = None, **kwargs
747
+ ) -> DataFrame:
748
+ """Returns this batch as a pandas DataFrame"""
749
+ self._check_can_use_pandas()
750
+ table = self.to_arrow(connection=connection)
751
+ return table.to_pandas(**kwargs)
752
+
753
+ def _get_pandas_iter(
754
+ self, connection: SnowflakeConnection | None = None, **kwargs
755
+ ) -> Iterator[DataFrame]:
756
+ """An iterator for this batch which yields a pandas DataFrame"""
757
+ iterator_data = []
758
+ dataframe = self.to_pandas(connection=connection, **kwargs)
759
+ if not dataframe.empty:
760
+ iterator_data.append(dataframe)
761
+ return iter(iterator_data)
762
+
763
+ def create_iter(
764
+ self, connection: SnowflakeConnection | None = None, **kwargs
765
+ ) -> (
766
+ Iterator[dict | Exception]
767
+ | Iterator[tuple | Exception]
768
+ | Iterator[Table]
769
+ | Iterator[DataFrame]
770
+ ):
771
+ """The interface used by ResultSet to create an iterator for this ResultBatch."""
772
+ iter_unit: IterUnit = kwargs.pop("iter_unit", IterUnit.ROW_UNIT)
773
+ if iter_unit == IterUnit.TABLE_UNIT:
774
+ structure = kwargs.pop("structure", "pandas")
775
+ if structure == "pandas":
776
+ return self._get_pandas_iter(connection=connection, **kwargs)
777
+ else:
778
+ return self._get_arrow_iter(connection=connection)
779
+ else:
780
+ return self._create_iter(iter_unit=iter_unit, connection=connection)
781
+
782
+ def populate_data(
783
+ self, connection: SnowflakeConnection | None = None, **kwargs
784
+ ) -> Self:
785
+ self._data = self._download(connection=connection).content
786
+ return self