snowflake-connector-python 4.4.0__cp314-cp314-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 (302) hide show
  1. snowflake/connector/__init__.py +118 -0
  2. snowflake/connector/_query_context_cache.py +276 -0
  3. snowflake/connector/_sql_util.py +48 -0
  4. snowflake/connector/_utils.py +353 -0
  5. snowflake/connector/aio/README.md +88 -0
  6. snowflake/connector/aio/__init__.py +163 -0
  7. snowflake/connector/aio/_azure_storage_client.py +227 -0
  8. snowflake/connector/aio/_bind_upload_agent.py +69 -0
  9. snowflake/connector/aio/_connection.py +1216 -0
  10. snowflake/connector/aio/_cursor.py +1402 -0
  11. snowflake/connector/aio/_description.py +5 -0
  12. snowflake/connector/aio/_direct_file_operation_utils.py +88 -0
  13. snowflake/connector/aio/_file_transfer_agent.py +316 -0
  14. snowflake/connector/aio/_gcs_storage_client.py +360 -0
  15. snowflake/connector/aio/_network.py +860 -0
  16. snowflake/connector/aio/_ocsp_asn1crypto.py +51 -0
  17. snowflake/connector/aio/_ocsp_snowflake.py +608 -0
  18. snowflake/connector/aio/_pandas_tools.py +561 -0
  19. snowflake/connector/aio/_result_batch.py +442 -0
  20. snowflake/connector/aio/_result_set.py +309 -0
  21. snowflake/connector/aio/_s3_storage_client.py +460 -0
  22. snowflake/connector/aio/_session_manager.py +588 -0
  23. snowflake/connector/aio/_storage_client.py +331 -0
  24. snowflake/connector/aio/_telemetry.py +97 -0
  25. snowflake/connector/aio/_time_util.py +57 -0
  26. snowflake/connector/aio/_wif_util.py +324 -0
  27. snowflake/connector/aio/auth/__init__.py +52 -0
  28. snowflake/connector/aio/auth/_auth.py +406 -0
  29. snowflake/connector/aio/auth/_by_plugin.py +131 -0
  30. snowflake/connector/aio/auth/_default.py +28 -0
  31. snowflake/connector/aio/auth/_idtoken.py +54 -0
  32. snowflake/connector/aio/auth/_keypair.py +60 -0
  33. snowflake/connector/aio/auth/_no_auth.py +31 -0
  34. snowflake/connector/aio/auth/_oauth.py +27 -0
  35. snowflake/connector/aio/auth/_oauth_code.py +117 -0
  36. snowflake/connector/aio/auth/_oauth_credentials.py +100 -0
  37. snowflake/connector/aio/auth/_okta.py +247 -0
  38. snowflake/connector/aio/auth/_pat.py +27 -0
  39. snowflake/connector/aio/auth/_usrpwdmfa.py +30 -0
  40. snowflake/connector/aio/auth/_webbrowser.py +405 -0
  41. snowflake/connector/aio/auth/_workload_identity.py +60 -0
  42. snowflake/connector/arrow_context.py +211 -0
  43. snowflake/connector/auth/__init__.py +53 -0
  44. snowflake/connector/auth/_auth.py +669 -0
  45. snowflake/connector/auth/_http_server.py +257 -0
  46. snowflake/connector/auth/_oauth_base.py +472 -0
  47. snowflake/connector/auth/by_plugin.py +219 -0
  48. snowflake/connector/auth/default.py +36 -0
  49. snowflake/connector/auth/idtoken.py +72 -0
  50. snowflake/connector/auth/keypair.py +225 -0
  51. snowflake/connector/auth/no_auth.py +39 -0
  52. snowflake/connector/auth/oauth.py +49 -0
  53. snowflake/connector/auth/oauth_code.py +490 -0
  54. snowflake/connector/auth/oauth_credentials.py +71 -0
  55. snowflake/connector/auth/okta.py +339 -0
  56. snowflake/connector/auth/pat.py +40 -0
  57. snowflake/connector/auth/usrpwdmfa.py +69 -0
  58. snowflake/connector/auth/webbrowser.py +514 -0
  59. snowflake/connector/auth/workload_identity.py +110 -0
  60. snowflake/connector/azure_storage_client.py +278 -0
  61. snowflake/connector/backoff_policies.py +141 -0
  62. snowflake/connector/bind_upload_agent.py +89 -0
  63. snowflake/connector/cache.py +696 -0
  64. snowflake/connector/compat.py +127 -0
  65. snowflake/connector/config_manager.py +553 -0
  66. snowflake/connector/connection.py +2671 -0
  67. snowflake/connector/connection_diagnostic.py +793 -0
  68. snowflake/connector/constants.py +460 -0
  69. snowflake/connector/converter.py +809 -0
  70. snowflake/connector/converter_issue23517.py +87 -0
  71. snowflake/connector/converter_null.py +14 -0
  72. snowflake/connector/converter_snowsql.py +205 -0
  73. snowflake/connector/crl.py +919 -0
  74. snowflake/connector/crl_cache.py +734 -0
  75. snowflake/connector/cursor.py +2099 -0
  76. snowflake/connector/dbapi.py +53 -0
  77. snowflake/connector/description.py +21 -0
  78. snowflake/connector/direct_file_operation_utils.py +88 -0
  79. snowflake/connector/encryption_util.py +220 -0
  80. snowflake/connector/errorcode.py +94 -0
  81. snowflake/connector/errors.py +666 -0
  82. snowflake/connector/externals_utils/__init__.py +0 -0
  83. snowflake/connector/externals_utils/externals_setup.py +30 -0
  84. snowflake/connector/feature.py +4 -0
  85. snowflake/connector/file_compression_type.py +118 -0
  86. snowflake/connector/file_lock.py +72 -0
  87. snowflake/connector/file_transfer_agent.py +1244 -0
  88. snowflake/connector/file_util.py +153 -0
  89. snowflake/connector/gcs_storage_client.py +476 -0
  90. snowflake/connector/gzip_decoder.py +85 -0
  91. snowflake/connector/interval_util.py +26 -0
  92. snowflake/connector/local_storage_client.py +90 -0
  93. snowflake/connector/log_configuration.py +62 -0
  94. snowflake/connector/logging_utils/__init__.py +0 -0
  95. snowflake/connector/logging_utils/filters.py +72 -0
  96. snowflake/connector/minicore/__init__.py +0 -0
  97. snowflake/connector/minicore/aix_ppc64/libsf_mini_core.a +0 -0
  98. snowflake/connector/minicore/aix_ppc64/libsf_mini_core_static.a +0 -0
  99. snowflake/connector/minicore/linux_aarch64_glibc/libsf_mini_core.so +0 -0
  100. snowflake/connector/minicore/linux_aarch64_musl/libsf_mini_core.so +0 -0
  101. snowflake/connector/minicore/linux_x86_64_glibc/libsf_mini_core.so +0 -0
  102. snowflake/connector/minicore/linux_x86_64_musl/libsf_mini_core.so +0 -0
  103. snowflake/connector/minicore/macos_aarch64/libsf_mini_core.dylib +0 -0
  104. snowflake/connector/minicore/macos_x86_64/libsf_mini_core.dylib +0 -0
  105. snowflake/connector/minicore/windows_x86_64/sf_mini_core.dll +0 -0
  106. snowflake/connector/minicore/windows_x86_64/sf_mini_core_static.lib +0 -0
  107. snowflake/connector/nanoarrow_arrow_iterator.cp314-win_amd64.pyd +0 -0
  108. snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +60 -0
  109. snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
  110. snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +19 -0
  111. snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
  112. snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
  113. snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
  114. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.cpp +570 -0
  115. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.hpp +98 -0
  116. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +125 -0
  117. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +115 -0
  118. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.cpp +1140 -0
  119. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.hpp +138 -0
  120. snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +47 -0
  121. snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
  122. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +83 -0
  123. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
  124. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +97 -0
  125. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +72 -0
  126. snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +73 -0
  127. snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
  128. snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +30 -0
  129. snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
  130. snowflake/connector/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
  131. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
  132. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +45 -0
  133. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +75 -0
  134. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +57 -0
  135. snowflake/connector/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
  136. snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +75 -0
  137. snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
  138. snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +46 -0
  139. snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
  140. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
  141. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +95 -0
  142. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +57 -0
  143. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +36 -0
  144. snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +34 -0
  145. snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
  146. snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +19 -0
  147. snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
  148. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +36 -0
  149. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
  150. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +346 -0
  151. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +145 -0
  152. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
  153. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.cpp +67 -0
  154. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
  155. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +101 -0
  156. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +127 -0
  157. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
  158. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1908 -0
  159. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +215 -0
  160. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +125 -0
  161. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
  162. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +55 -0
  163. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +148 -0
  164. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
  165. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
  166. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
  167. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +162 -0
  168. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +97 -0
  169. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +239 -0
  170. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
  171. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +210 -0
  172. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +84 -0
  173. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +84 -0
  174. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
  175. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
  176. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +206 -0
  177. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +118 -0
  178. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
  179. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
  180. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
  181. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
  182. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
  183. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +162 -0
  184. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +898 -0
  185. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +190 -0
  186. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
  187. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
  188. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc.c +3204 -0
  189. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3217 -0
  190. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3618 -0
  191. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +379 -0
  192. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_arrow_iterator.pyx +262 -0
  193. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.c +512 -0
  194. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.h +350 -0
  195. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +33273 -0
  196. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +397 -0
  197. snowflake/connector/nanoarrow_cpp/Logging/logging.cpp +100 -0
  198. snowflake/connector/nanoarrow_cpp/Logging/logging.hpp +49 -0
  199. snowflake/connector/network.py +1227 -0
  200. snowflake/connector/ocsp_asn1crypto.py +447 -0
  201. snowflake/connector/ocsp_snowflake.py +1939 -0
  202. snowflake/connector/options.py +179 -0
  203. snowflake/connector/os_details.py +88 -0
  204. snowflake/connector/pandas_tools.py +734 -0
  205. snowflake/connector/platform_detection.py +555 -0
  206. snowflake/connector/proxy.py +28 -0
  207. snowflake/connector/py.typed +0 -0
  208. snowflake/connector/result_batch.py +917 -0
  209. snowflake/connector/result_set.py +340 -0
  210. snowflake/connector/s3_storage_client.py +614 -0
  211. snowflake/connector/secret_detector.py +181 -0
  212. snowflake/connector/session_manager.py +650 -0
  213. snowflake/connector/sf_dirs.py +64 -0
  214. snowflake/connector/sfbinaryformat.py +35 -0
  215. snowflake/connector/sfdatetime.py +345 -0
  216. snowflake/connector/snow_logging.py +144 -0
  217. snowflake/connector/sqlstate.py +9 -0
  218. snowflake/connector/ssd_internal_keys.py +30 -0
  219. snowflake/connector/ssl_wrap_socket.py +281 -0
  220. snowflake/connector/storage_client.py +486 -0
  221. snowflake/connector/telemetry.py +254 -0
  222. snowflake/connector/telemetry_oob.py +544 -0
  223. snowflake/connector/test_util.py +30 -0
  224. snowflake/connector/time_util.py +159 -0
  225. snowflake/connector/token_cache.py +457 -0
  226. snowflake/connector/tool/__init__.py +0 -0
  227. snowflake/connector/tool/dump_certs.py +57 -0
  228. snowflake/connector/tool/dump_ocsp_response.py +139 -0
  229. snowflake/connector/tool/dump_ocsp_response_cache.py +194 -0
  230. snowflake/connector/tool/probe_connection.py +69 -0
  231. snowflake/connector/url_util.py +55 -0
  232. snowflake/connector/util_text.py +320 -0
  233. snowflake/connector/vendored/__init__.py +3 -0
  234. snowflake/connector/vendored/requests/LICENSE +175 -0
  235. snowflake/connector/vendored/requests/__init__.py +184 -0
  236. snowflake/connector/vendored/requests/__version__.py +14 -0
  237. snowflake/connector/vendored/requests/_internal_utils.py +50 -0
  238. snowflake/connector/vendored/requests/adapters.py +696 -0
  239. snowflake/connector/vendored/requests/api.py +157 -0
  240. snowflake/connector/vendored/requests/auth.py +314 -0
  241. snowflake/connector/vendored/requests/certs.py +17 -0
  242. snowflake/connector/vendored/requests/compat.py +106 -0
  243. snowflake/connector/vendored/requests/cookies.py +561 -0
  244. snowflake/connector/vendored/requests/exceptions.py +151 -0
  245. snowflake/connector/vendored/requests/help.py +134 -0
  246. snowflake/connector/vendored/requests/hooks.py +33 -0
  247. snowflake/connector/vendored/requests/models.py +1039 -0
  248. snowflake/connector/vendored/requests/packages.py +23 -0
  249. snowflake/connector/vendored/requests/sessions.py +831 -0
  250. snowflake/connector/vendored/requests/status_codes.py +128 -0
  251. snowflake/connector/vendored/requests/structures.py +99 -0
  252. snowflake/connector/vendored/requests/utils.py +1086 -0
  253. snowflake/connector/vendored/urllib3/LICENSE.txt +21 -0
  254. snowflake/connector/vendored/urllib3/__init__.py +211 -0
  255. snowflake/connector/vendored/urllib3/_base_connection.py +165 -0
  256. snowflake/connector/vendored/urllib3/_collections.py +487 -0
  257. snowflake/connector/vendored/urllib3/_request_methods.py +278 -0
  258. snowflake/connector/vendored/urllib3/_version.py +34 -0
  259. snowflake/connector/vendored/urllib3/connection.py +1108 -0
  260. snowflake/connector/vendored/urllib3/connectionpool.py +1178 -0
  261. snowflake/connector/vendored/urllib3/contrib/__init__.py +0 -0
  262. snowflake/connector/vendored/urllib3/contrib/emscripten/__init__.py +17 -0
  263. snowflake/connector/vendored/urllib3/contrib/emscripten/connection.py +260 -0
  264. snowflake/connector/vendored/urllib3/contrib/emscripten/emscripten_fetch_worker.js +110 -0
  265. snowflake/connector/vendored/urllib3/contrib/emscripten/fetch.py +726 -0
  266. snowflake/connector/vendored/urllib3/contrib/emscripten/request.py +22 -0
  267. snowflake/connector/vendored/urllib3/contrib/emscripten/response.py +277 -0
  268. snowflake/connector/vendored/urllib3/contrib/pyopenssl.py +564 -0
  269. snowflake/connector/vendored/urllib3/contrib/socks.py +228 -0
  270. snowflake/connector/vendored/urllib3/exceptions.py +335 -0
  271. snowflake/connector/vendored/urllib3/fields.py +341 -0
  272. snowflake/connector/vendored/urllib3/filepost.py +89 -0
  273. snowflake/connector/vendored/urllib3/http2/__init__.py +53 -0
  274. snowflake/connector/vendored/urllib3/http2/connection.py +356 -0
  275. snowflake/connector/vendored/urllib3/http2/probe.py +87 -0
  276. snowflake/connector/vendored/urllib3/poolmanager.py +651 -0
  277. snowflake/connector/vendored/urllib3/py.typed +2 -0
  278. snowflake/connector/vendored/urllib3/response.py +1480 -0
  279. snowflake/connector/vendored/urllib3/util/__init__.py +42 -0
  280. snowflake/connector/vendored/urllib3/util/connection.py +137 -0
  281. snowflake/connector/vendored/urllib3/util/proxy.py +43 -0
  282. snowflake/connector/vendored/urllib3/util/request.py +263 -0
  283. snowflake/connector/vendored/urllib3/util/response.py +101 -0
  284. snowflake/connector/vendored/urllib3/util/retry.py +549 -0
  285. snowflake/connector/vendored/urllib3/util/ssl_.py +527 -0
  286. snowflake/connector/vendored/urllib3/util/ssl_match_hostname.py +159 -0
  287. snowflake/connector/vendored/urllib3/util/ssltransport.py +271 -0
  288. snowflake/connector/vendored/urllib3/util/timeout.py +275 -0
  289. snowflake/connector/vendored/urllib3/util/url.py +469 -0
  290. snowflake/connector/vendored/urllib3/util/util.py +42 -0
  291. snowflake/connector/vendored/urllib3/util/wait.py +124 -0
  292. snowflake/connector/version.py +3 -0
  293. snowflake/connector/wif_util.py +434 -0
  294. snowflake_connector_python-4.4.0.dist-info/DELVEWHEEL +2 -0
  295. snowflake_connector_python-4.4.0.dist-info/METADATA +1591 -0
  296. snowflake_connector_python-4.4.0.dist-info/RECORD +302 -0
  297. snowflake_connector_python-4.4.0.dist-info/WHEEL +5 -0
  298. snowflake_connector_python-4.4.0.dist-info/entry_points.txt +4 -0
  299. snowflake_connector_python-4.4.0.dist-info/licenses/LICENSE.txt +202 -0
  300. snowflake_connector_python-4.4.0.dist-info/licenses/NOTICE +8 -0
  301. snowflake_connector_python-4.4.0.dist-info/top_level.txt +1 -0
  302. snowflake_connector_python.libs/msvcp140-0f885b509a685d2bbfa652fed26b5fb3.dll +0 -0
@@ -0,0 +1,1402 @@
1
+ from __future__ import annotations
2
+
3
+ import abc
4
+ import asyncio
5
+ import collections
6
+ import logging
7
+ import re
8
+ import signal
9
+ import sys
10
+ import typing
11
+ import uuid
12
+ from logging import getLogger
13
+ from types import TracebackType
14
+ from typing import IO, TYPE_CHECKING, Any, AsyncIterator, Literal, Sequence, overload
15
+
16
+ from typing_extensions import Self
17
+
18
+ import snowflake.connector.cursor
19
+ from snowflake.connector import (
20
+ Error,
21
+ IntegrityError,
22
+ InterfaceError,
23
+ NotSupportedError,
24
+ ProgrammingError,
25
+ )
26
+ from snowflake.connector._sql_util import get_file_transfer_type
27
+ from snowflake.connector.aio._bind_upload_agent import BindUploadAgent
28
+ from snowflake.connector.aio._result_batch import (
29
+ ResultBatch,
30
+ create_batches_from_response,
31
+ )
32
+ from snowflake.connector.aio._result_set import ResultSet, ResultSetIterator
33
+ from snowflake.connector.constants import (
34
+ CMD_TYPE_DOWNLOAD,
35
+ CMD_TYPE_UPLOAD,
36
+ PARAMETER_PYTHON_CONNECTOR_QUERY_RESULT_FORMAT,
37
+ QueryStatus,
38
+ )
39
+ from snowflake.connector.cursor import (
40
+ ASYNC_NO_DATA_MAX_RETRY,
41
+ ASYNC_RETRY_PATTERN,
42
+ DESC_TABLE_RE,
43
+ ResultMetadata,
44
+ ResultMetadataV2,
45
+ ResultState,
46
+ )
47
+ from snowflake.connector.cursor import SnowflakeCursorBase as SnowflakeCursorBaseSync
48
+ from snowflake.connector.cursor import T
49
+ from snowflake.connector.errorcode import (
50
+ ER_CURSOR_IS_CLOSED,
51
+ ER_FAILED_PROCESSING_PYFORMAT,
52
+ ER_FAILED_TO_REWRITE_MULTI_ROW_INSERT,
53
+ ER_INVALID_VALUE,
54
+ ER_NOT_POSITIVE_SIZE,
55
+ )
56
+ from snowflake.connector.errors import BindUploadError, DatabaseError
57
+ from snowflake.connector.file_transfer_agent import SnowflakeProgressPercentage
58
+ from snowflake.connector.telemetry import TelemetryData, TelemetryField
59
+ from snowflake.connector.time_util import get_time_millis
60
+
61
+ from .._utils import REQUEST_ID_STATEMENT_PARAM_NAME, is_uuid4
62
+
63
+ if TYPE_CHECKING:
64
+ from pandas import DataFrame
65
+ from pyarrow import Table
66
+
67
+ from snowflake.connector.aio import SnowflakeConnection
68
+
69
+ logger = getLogger(__name__)
70
+
71
+ FetchRow = typing.TypeVar(
72
+ "FetchRow", bound=typing.Union[typing.Tuple[Any, ...], typing.Dict[str, Any]]
73
+ )
74
+
75
+
76
+ class SnowflakeCursorBase(SnowflakeCursorBaseSync, abc.ABC, typing.Generic[FetchRow]):
77
+ def __init__(
78
+ self,
79
+ connection: SnowflakeConnection,
80
+ ):
81
+ super().__init__(connection)
82
+ # the following fixes type hint
83
+ self._connection = typing.cast("SnowflakeConnection", self._connection)
84
+ self._inner_cursor: SnowflakeCursorBase | None = None
85
+ self._lock_canceling = asyncio.Lock()
86
+ self._timebomb: asyncio.Task | None = None
87
+ self._prefetch_hook: typing.Callable[[], typing.Awaitable] | None = None
88
+
89
+ def __aiter__(self):
90
+ return self
91
+
92
+ def __iter__(self):
93
+ raise TypeError(
94
+ "'snowflake.connector.aio.SnowflakeCursor' only supports async iteration."
95
+ )
96
+
97
+ async def __anext__(self):
98
+ while True:
99
+ _next = await self.fetchone()
100
+ if _next is None:
101
+ raise StopAsyncIteration
102
+ return _next
103
+
104
+ async def __aenter__(self):
105
+ return self
106
+
107
+ def __enter__(self):
108
+ # async cursor does not support sync context manager
109
+ raise TypeError(
110
+ "'SnowflakeCursor' object does not support the context manager protocol"
111
+ )
112
+
113
+ def __exit__(self, exc_type, exc_val, exc_tb):
114
+ # async cursor does not support sync context manager
115
+ raise TypeError(
116
+ "'SnowflakeCursor' object does not support the context manager protocol"
117
+ )
118
+
119
+ def __del__(self):
120
+ # do nothing in async, __del__ is unreliable
121
+ pass
122
+
123
+ async def __aexit__(
124
+ self,
125
+ exc_type: type[BaseException] | None,
126
+ exc_val: BaseException | None,
127
+ exc_tb: TracebackType | None,
128
+ ) -> None:
129
+ """Context manager with commit or rollback."""
130
+ await self.close()
131
+
132
+ async def _timebomb_task(self, timeout, query):
133
+ try:
134
+ logger.debug("started timebomb in %ss", timeout)
135
+ await asyncio.sleep(timeout)
136
+ await self.__cancel_query(query)
137
+ return True
138
+ except asyncio.CancelledError:
139
+ logger.debug("cancelled timebomb in timebomb task")
140
+ return False
141
+
142
+ async def __cancel_query(self, query) -> None:
143
+ if self._sequence_counter >= 0 and not self.is_closed():
144
+ logger.debug("canceled. %s, request_id: %s", query, self._request_id)
145
+ async with self._lock_canceling:
146
+ await self._connection._cancel_query(query, self._request_id)
147
+
148
+ async def _describe_internal(
149
+ self, *args: Any, **kwargs: Any
150
+ ) -> list[ResultMetadataV2]:
151
+ """Obtain the schema of the result without executing the query.
152
+
153
+ This function takes the same arguments as execute, please refer to that function
154
+ for documentation.
155
+
156
+ This function is for internal use only
157
+
158
+ Returns:
159
+ The schema of the result, in the new result metadata format.
160
+ """
161
+ kwargs["_describe_only"] = kwargs["_is_internal"] = True
162
+ await self.execute(*args, **kwargs)
163
+ return self._description
164
+
165
+ async def _execute_helper(
166
+ self,
167
+ query: str,
168
+ timeout: int = 0,
169
+ statement_params: dict[str, str] | None = None,
170
+ binding_params: tuple | dict[str, dict[str, str]] = None,
171
+ binding_stage: str | None = None,
172
+ is_internal: bool = False,
173
+ describe_only: bool = False,
174
+ _no_results: bool = False,
175
+ _is_put_get=None,
176
+ _no_retry: bool = False,
177
+ dataframe_ast: str | None = None,
178
+ ) -> dict[str, Any]:
179
+ del self.messages[:]
180
+
181
+ if statement_params is not None and not isinstance(statement_params, dict):
182
+ Error.errorhandler_wrapper(
183
+ self.connection,
184
+ self,
185
+ ProgrammingError,
186
+ {
187
+ "msg": "The data type of statement params is invalid. It must be dict.",
188
+ "errno": ER_INVALID_VALUE,
189
+ },
190
+ )
191
+
192
+ # check if current installation include arrow extension or not,
193
+ # if not, we set statement level query result format to be JSON
194
+ if not snowflake.connector.cursor.CAN_USE_ARROW_RESULT_FORMAT:
195
+ logger.debug("Cannot use arrow result format, fallback to json format")
196
+ if statement_params is None:
197
+ statement_params = {
198
+ PARAMETER_PYTHON_CONNECTOR_QUERY_RESULT_FORMAT: "JSON"
199
+ }
200
+ else:
201
+ result_format_val = statement_params.get(
202
+ PARAMETER_PYTHON_CONNECTOR_QUERY_RESULT_FORMAT
203
+ )
204
+ if str(result_format_val).upper() == "ARROW":
205
+ self.check_can_use_arrow_resultset()
206
+ elif result_format_val is None:
207
+ statement_params[PARAMETER_PYTHON_CONNECTOR_QUERY_RESULT_FORMAT] = (
208
+ "JSON"
209
+ )
210
+
211
+ self._sequence_counter = await self._connection._next_sequence_counter()
212
+
213
+ # If requestId is contained in statement parameters, use it to set request id. Verify here it is a valid uuid4
214
+ # identifier.
215
+ if (
216
+ statement_params is not None
217
+ and REQUEST_ID_STATEMENT_PARAM_NAME in statement_params
218
+ ):
219
+ request_id = statement_params[REQUEST_ID_STATEMENT_PARAM_NAME]
220
+
221
+ if not is_uuid4(request_id):
222
+ # uuid.UUID will throw an error if invalid, but we explicitly check and throw here.
223
+ raise ValueError(f"requestId {request_id} is not a valid UUID4.")
224
+ self._request_id = uuid.UUID(str(request_id), version=4)
225
+
226
+ # Create a (deep copy) and remove the statement param, there is no need to encode it as extra parameter
227
+ # one more time.
228
+ statement_params = statement_params.copy()
229
+ statement_params.pop(REQUEST_ID_STATEMENT_PARAM_NAME)
230
+ else:
231
+ # Generate UUID for query.
232
+ self._request_id = uuid.uuid4()
233
+
234
+ logger.debug(f"Request id: {self._request_id}")
235
+
236
+ logger.debug("running query [%s]", self._format_query_for_log(query))
237
+ if _is_put_get is not None:
238
+ # if told the query is PUT or GET, use the information
239
+ self._is_file_transfer = _is_put_get
240
+ else:
241
+ # or detect it.
242
+ self._is_file_transfer = get_file_transfer_type(query) is not None
243
+ logger.debug(
244
+ "is_file_transfer: %s",
245
+ self._is_file_transfer if self._is_file_transfer is not None else "None",
246
+ )
247
+
248
+ real_timeout = (
249
+ timeout if timeout and timeout > 0 else self._connection.network_timeout
250
+ )
251
+
252
+ if real_timeout is not None:
253
+ self._timebomb = asyncio.create_task(
254
+ self._timebomb_task(real_timeout, query)
255
+ )
256
+ logger.debug("started timebomb in %ss", real_timeout)
257
+ else:
258
+ self._timebomb = None
259
+
260
+ original_sigint = signal.getsignal(signal.SIGINT)
261
+
262
+ def interrupt_handler(*_): # pragma: no cover
263
+ try:
264
+ signal.signal(signal.SIGINT, snowflake.connector.cursor.exit_handler)
265
+ except (ValueError, TypeError):
266
+ # ignore failures
267
+ pass
268
+ try:
269
+ if self._timebomb is not None:
270
+ self._timebomb.cancel()
271
+ self._timebomb = None
272
+ logger.debug("cancelled timebomb in finally")
273
+ asyncio.create_task(self.__cancel_query(query))
274
+ finally:
275
+ if original_sigint:
276
+ try:
277
+ signal.signal(signal.SIGINT, original_sigint)
278
+ except (ValueError, TypeError):
279
+ # ignore failures
280
+ pass
281
+ raise KeyboardInterrupt
282
+
283
+ try:
284
+ if not original_sigint == snowflake.connector.cursor.exit_handler:
285
+ signal.signal(signal.SIGINT, interrupt_handler)
286
+ except ValueError: # pragma: no cover
287
+ logger.debug(
288
+ "Failed to set SIGINT handler. " "Not in main thread. Ignored..."
289
+ )
290
+ ret: dict[str, Any] = {"data": {}}
291
+ try:
292
+ ret = await self._connection.cmd_query(
293
+ query,
294
+ self._sequence_counter,
295
+ self._request_id,
296
+ binding_params=binding_params,
297
+ binding_stage=binding_stage,
298
+ is_file_transfer=bool(self._is_file_transfer),
299
+ statement_params=statement_params,
300
+ is_internal=is_internal,
301
+ describe_only=describe_only,
302
+ _no_results=_no_results,
303
+ _no_retry=_no_retry,
304
+ timeout=real_timeout,
305
+ dataframe_ast=dataframe_ast,
306
+ )
307
+ finally:
308
+ try:
309
+ if original_sigint:
310
+ signal.signal(signal.SIGINT, original_sigint)
311
+ except (ValueError, TypeError): # pragma: no cover
312
+ logger.debug(
313
+ "Failed to reset SIGINT handler. Not in main " "thread. Ignored..."
314
+ )
315
+ if self._timebomb is not None:
316
+ self._timebomb.cancel()
317
+ try:
318
+ await self._timebomb
319
+ except asyncio.CancelledError:
320
+ pass
321
+ logger.debug("cancelled timebomb in finally")
322
+
323
+ if "data" in ret and "parameters" in ret["data"]:
324
+ parameters = ret["data"].get("parameters", list())
325
+ # Set session parameters for cursor object
326
+ for kv in parameters:
327
+ if "TIMESTAMP_OUTPUT_FORMAT" in kv["name"]:
328
+ self._timestamp_output_format = kv["value"]
329
+ elif "TIMESTAMP_NTZ_OUTPUT_FORMAT" in kv["name"]:
330
+ self._timestamp_ntz_output_format = kv["value"]
331
+ elif "TIMESTAMP_LTZ_OUTPUT_FORMAT" in kv["name"]:
332
+ self._timestamp_ltz_output_format = kv["value"]
333
+ elif "TIMESTAMP_TZ_OUTPUT_FORMAT" in kv["name"]:
334
+ self._timestamp_tz_output_format = kv["value"]
335
+ elif "DATE_OUTPUT_FORMAT" in kv["name"]:
336
+ self._date_output_format = kv["value"]
337
+ elif "TIME_OUTPUT_FORMAT" in kv["name"]:
338
+ self._time_output_format = kv["value"]
339
+ elif "TIMEZONE" in kv["name"]:
340
+ self._timezone = kv["value"]
341
+ elif "BINARY_OUTPUT_FORMAT" in kv["name"]:
342
+ self._binary_output_format = kv["value"]
343
+ # Set session parameters for connection object
344
+ await self._connection._update_parameters(
345
+ {p["name"]: p["value"] for p in parameters}
346
+ )
347
+
348
+ self.query = query
349
+ self._sequence_counter = -1
350
+ return ret
351
+
352
+ async def _init_result_and_meta(self, data: dict[Any, Any]) -> None:
353
+ is_dml = self._is_dml(data)
354
+ self._query_result_format = data.get("queryResultFormat", "json")
355
+ logger.debug("Query result format: %s", self._query_result_format)
356
+
357
+ if self._total_rowcount == -1 and not is_dml and data.get("total") is not None:
358
+ self._total_rowcount = data["total"]
359
+
360
+ self._description: list[ResultMetadataV2] = [
361
+ ResultMetadataV2.from_column(col) for col in data["rowtype"]
362
+ ]
363
+
364
+ result_chunks = create_batches_from_response(
365
+ self, self._query_result_format, data, self._description
366
+ )
367
+
368
+ if not (is_dml or self.is_file_transfer):
369
+ logger.debug(
370
+ "Number of results in first chunk: %s", result_chunks[0].rowcount
371
+ )
372
+
373
+ self._result_set = ResultSet(
374
+ self,
375
+ result_chunks,
376
+ self._connection.client_prefetch_threads,
377
+ )
378
+ self._rownumber = -1
379
+ self._result_state = ResultState.VALID
380
+
381
+ # Extract stats object if available (for DML operations like CTAS, INSERT, UPDATE, DELETE)
382
+ self._stats_data = data.get("stats", None)
383
+ logger.debug("Execution DML stats: %s", self.stats)
384
+
385
+ # don't update the row count when the result is returned from `describe` method
386
+ if is_dml and "rowset" in data and len(data["rowset"]) > 0:
387
+ updated_rows = 0
388
+ for idx, desc in enumerate(self._description):
389
+ if desc.name in (
390
+ "number of rows updated",
391
+ "number of multi-joined rows updated",
392
+ "number of rows deleted",
393
+ ) or desc.name.startswith("number of rows inserted"):
394
+ updated_rows += int(data["rowset"][0][idx])
395
+ if self._total_rowcount == -1:
396
+ self._total_rowcount = updated_rows
397
+ else:
398
+ self._total_rowcount += updated_rows
399
+
400
+ async def _init_multi_statement_results(self, data: dict) -> None:
401
+ await self._log_telemetry_job_data(
402
+ TelemetryField.MULTI_STATEMENT, TelemetryData.TRUE
403
+ )
404
+ self.multi_statement_savedIds = data["resultIds"].split(",")
405
+ self._multi_statement_resultIds = collections.deque(
406
+ self.multi_statement_savedIds
407
+ )
408
+ if self._is_file_transfer:
409
+ Error.errorhandler_wrapper(
410
+ self.connection,
411
+ self,
412
+ ProgrammingError,
413
+ {
414
+ "msg": "PUT/GET commands are not supported for multi-statement queries and cannot be executed.",
415
+ "errno": ER_INVALID_VALUE,
416
+ },
417
+ )
418
+ await self.nextset()
419
+
420
+ async def _log_telemetry_job_data(
421
+ self, telemetry_field: TelemetryField, value: Any
422
+ ) -> None:
423
+ ts = get_time_millis()
424
+ try:
425
+ await self._connection._log_telemetry(
426
+ TelemetryData.from_telemetry_data_dict(
427
+ from_dict={
428
+ TelemetryField.KEY_TYPE.value: telemetry_field.value,
429
+ TelemetryField.KEY_SFQID.value: self._sfqid,
430
+ TelemetryField.KEY_VALUE.value: value,
431
+ },
432
+ timestamp=ts,
433
+ connection=self._connection,
434
+ )
435
+ )
436
+ except AttributeError:
437
+ logger.warning(
438
+ "Cursor failed to log to telemetry. Connection object may be None.",
439
+ exc_info=True,
440
+ )
441
+
442
+ async def _preprocess_pyformat_query(
443
+ self,
444
+ command: str,
445
+ params: Sequence[Any] | dict[Any, Any] | None = None,
446
+ ) -> str:
447
+ # pyformat/format paramstyle
448
+ # client side binding
449
+ processed_params = self._connection._process_params_pyformat(params, self)
450
+ # SNOW-513061 collect telemetry for empty sequence usage before we make the breaking change announcement
451
+ if params is not None and len(params) == 0:
452
+ await self._log_telemetry_job_data(
453
+ TelemetryField.EMPTY_SEQ_INTERPOLATION,
454
+ (
455
+ TelemetryData.TRUE
456
+ if self.connection._interpolate_empty_sequences
457
+ else TelemetryData.FALSE
458
+ ),
459
+ )
460
+ if logger.getEffectiveLevel() <= logging.DEBUG:
461
+ logger.debug(
462
+ f"binding: [{self._format_query_for_log(command)}] "
463
+ f"with input=[{params}], "
464
+ f"processed=[{processed_params}]",
465
+ )
466
+ if (
467
+ self.connection._interpolate_empty_sequences
468
+ and processed_params is not None
469
+ ) or (
470
+ not self.connection._interpolate_empty_sequences
471
+ and len(processed_params) > 0
472
+ ):
473
+ query = command % processed_params
474
+ else:
475
+ query = command
476
+ return query
477
+
478
+ async def abort_query(self, qid: str) -> bool:
479
+ url = f"/queries/{qid}/abort-request"
480
+ ret = await self._connection.rest.request(url=url, method="post")
481
+ return ret.get("success")
482
+
483
+ @overload
484
+ async def callproc(self, procname: str) -> tuple: ...
485
+
486
+ @overload
487
+ async def callproc(self, procname: str, args: T) -> T: ...
488
+
489
+ async def callproc(self, procname: str, args=tuple()):
490
+ """Call a stored procedure.
491
+
492
+ Args:
493
+ procname: The stored procedure to be called.
494
+ args: Parameters to be passed into the stored procedure.
495
+
496
+ Returns:
497
+ The input parameters.
498
+ """
499
+ marker_format = "%s" if self._connection.is_pyformat else "?"
500
+ command = (
501
+ f"CALL {procname}({', '.join([marker_format for _ in range(len(args))])})"
502
+ )
503
+ await self.execute(command, args)
504
+ return args
505
+
506
+ @property
507
+ def connection(self) -> SnowflakeConnection:
508
+ return self._connection
509
+
510
+ async def close(self):
511
+ """Closes the cursor object.
512
+
513
+ Returns whether the cursor was closed during this call.
514
+ """
515
+ try:
516
+ if self.is_closed():
517
+ return False
518
+ async with self._lock_canceling:
519
+ self.reset(closing=True)
520
+ self._connection = None
521
+ del self.messages[:]
522
+ return True
523
+ except Exception:
524
+ return None
525
+
526
+ async def execute(
527
+ self,
528
+ command: str,
529
+ params: Sequence[Any] | dict[Any, Any] | None = None,
530
+ _bind_stage: str | None = None,
531
+ timeout: int | None = None,
532
+ _exec_async: bool = False,
533
+ _no_retry: bool = False,
534
+ _do_reset: bool = True,
535
+ _put_callback: SnowflakeProgressPercentage = None,
536
+ _put_azure_callback: SnowflakeProgressPercentage = None,
537
+ _put_callback_output_stream: IO[str] = sys.stdout,
538
+ _get_callback: SnowflakeProgressPercentage = None,
539
+ _get_azure_callback: SnowflakeProgressPercentage = None,
540
+ _get_callback_output_stream: IO[str] = sys.stdout,
541
+ _show_progress_bar: bool = True,
542
+ _statement_params: dict[str, str] | None = None,
543
+ _is_internal: bool = False,
544
+ _describe_only: bool = False,
545
+ _no_results: bool = False,
546
+ _is_put_get: bool | None = None,
547
+ _raise_put_get_error: bool = True,
548
+ _force_put_overwrite: bool = False,
549
+ _skip_upload_on_content_match: bool = False,
550
+ file_stream: IO[bytes] | None = None,
551
+ num_statements: int | None = None,
552
+ _force_qmark_paramstyle: bool = False,
553
+ _dataframe_ast: str | None = None,
554
+ ) -> Self | dict[str, Any] | None:
555
+ if _exec_async:
556
+ _no_results = True
557
+ logger.debug("executing SQL/command")
558
+ if self.is_closed():
559
+ Error.errorhandler_wrapper(
560
+ self.connection,
561
+ self,
562
+ InterfaceError,
563
+ {"msg": "Cursor is closed in execute.", "errno": ER_CURSOR_IS_CLOSED},
564
+ )
565
+
566
+ if _do_reset:
567
+ self.reset()
568
+ command = command.strip(" \t\n\r") if command else ""
569
+ if not command:
570
+ if _dataframe_ast:
571
+ logger.debug("dataframe ast: [%s]", _dataframe_ast)
572
+ else:
573
+ logger.warning("execute: no query is given to execute")
574
+ return None
575
+
576
+ logger.debug("query: [%s]", self._format_query_for_log(command))
577
+
578
+ _statement_params = _statement_params or dict()
579
+ # If we need to add another parameter, please consider introducing a dict for all extra params
580
+ # See discussion in https://github.com/snowflakedb/snowflake-connector-python/pull/1524#discussion_r1174061775
581
+ if num_statements is not None:
582
+ _statement_params = {
583
+ **_statement_params,
584
+ "MULTI_STATEMENT_COUNT": num_statements,
585
+ }
586
+
587
+ kwargs: dict[str, Any] = {
588
+ "timeout": timeout,
589
+ "statement_params": _statement_params,
590
+ "is_internal": _is_internal,
591
+ "describe_only": _describe_only,
592
+ "_no_results": _no_results,
593
+ "_is_put_get": _is_put_get,
594
+ "_no_retry": _no_retry,
595
+ "dataframe_ast": _dataframe_ast,
596
+ }
597
+
598
+ if self._connection.is_pyformat and not _force_qmark_paramstyle:
599
+ query = await self._preprocess_pyformat_query(command, params)
600
+ else:
601
+ # qmark and numeric paramstyle
602
+ query = command
603
+ if _bind_stage:
604
+ kwargs["binding_stage"] = _bind_stage
605
+ else:
606
+ if params is not None and not isinstance(params, (list, tuple)):
607
+ errorvalue = {
608
+ "msg": f"Binding parameters must be a list: {params}",
609
+ "errno": ER_FAILED_PROCESSING_PYFORMAT,
610
+ }
611
+ Error.errorhandler_wrapper(
612
+ self.connection, self, ProgrammingError, errorvalue
613
+ )
614
+
615
+ kwargs["binding_params"] = self._connection._process_params_qmarks(
616
+ params, self
617
+ )
618
+
619
+ m = DESC_TABLE_RE.match(query)
620
+ if m:
621
+ query1 = f"describe table {m.group(1)}"
622
+ logger.debug(
623
+ "query was rewritten: org=%s, new=%s",
624
+ " ".join(line.strip() for line in query.split("\n")),
625
+ query1,
626
+ )
627
+ query = query1
628
+
629
+ ret = await self._execute_helper(query, **kwargs)
630
+ self._sfqid = (
631
+ ret["data"]["queryId"]
632
+ if "data" in ret and "queryId" in ret["data"]
633
+ else None
634
+ )
635
+ logger.debug(f"sfqid: {self.sfqid}")
636
+ self._sqlstate = (
637
+ ret["data"]["sqlState"]
638
+ if "data" in ret and "sqlState" in ret["data"]
639
+ else None
640
+ )
641
+ logger.debug("query execution done")
642
+
643
+ self._first_chunk_time = get_time_millis()
644
+
645
+ # if server gives a send time, log the time it took to arrive
646
+ if "data" in ret and "sendResultTime" in ret["data"]:
647
+ time_consume_first_result = (
648
+ self._first_chunk_time - ret["data"]["sendResultTime"]
649
+ )
650
+ await self._log_telemetry_job_data(
651
+ TelemetryField.TIME_CONSUME_FIRST_RESULT, time_consume_first_result
652
+ )
653
+
654
+ if ret["success"]:
655
+ logger.debug("SUCCESS")
656
+ data = ret["data"]
657
+
658
+ for m in self.ALTER_SESSION_RE.finditer(query):
659
+ # session parameters
660
+ param = m.group(1).upper()
661
+ value = m.group(2)
662
+ self._connection.converter.set_parameter(param, value)
663
+
664
+ if "resultIds" in data:
665
+ await self._init_multi_statement_results(data)
666
+ return self
667
+ else:
668
+ self.multi_statement_savedIds = []
669
+
670
+ self._is_file_transfer = "command" in data and data["command"] in (
671
+ "UPLOAD",
672
+ "DOWNLOAD",
673
+ )
674
+ logger.debug("PUT OR GET: %s", self.is_file_transfer)
675
+ if self.is_file_transfer:
676
+ # Decide whether to use the old, or new code path
677
+ sf_file_transfer_agent = self._create_file_transfer_agent(
678
+ query,
679
+ ret,
680
+ put_callback=_put_callback,
681
+ put_azure_callback=_put_azure_callback,
682
+ put_callback_output_stream=_put_callback_output_stream,
683
+ get_callback=_get_callback,
684
+ get_azure_callback=_get_azure_callback,
685
+ get_callback_output_stream=_get_callback_output_stream,
686
+ show_progress_bar=_show_progress_bar,
687
+ raise_put_get_error=_raise_put_get_error,
688
+ force_put_overwrite=_force_put_overwrite
689
+ or data.get("overwrite", False),
690
+ skip_upload_on_content_match=_skip_upload_on_content_match,
691
+ source_from_stream=file_stream,
692
+ multipart_threshold=data.get("threshold"),
693
+ )
694
+ await sf_file_transfer_agent.execute()
695
+ data = sf_file_transfer_agent.result()
696
+ self._total_rowcount = len(data["rowset"]) if "rowset" in data else -1
697
+
698
+ if _exec_async:
699
+ self.connection._async_sfqids[self._sfqid] = None
700
+ if _no_results:
701
+ self._total_rowcount = (
702
+ ret["data"]["total"]
703
+ if "data" in ret and "total" in ret["data"]
704
+ else -1
705
+ )
706
+ return data
707
+ await self._init_result_and_meta(data)
708
+ else:
709
+ self._total_rowcount = (
710
+ ret["data"]["total"] if "data" in ret and "total" in ret["data"] else -1
711
+ )
712
+ logger.debug(ret)
713
+ err = ret["message"]
714
+ code = ret.get("code", -1)
715
+ if (
716
+ self._timebomb
717
+ and self._timebomb.result()
718
+ and "SQL execution canceled" in err
719
+ ):
720
+ # Modify the error message only if the server error response indicates the query was canceled.
721
+ # If the error occurs before the cancellation request reaches the backend
722
+ # (e.g., due to a very short timeout), we retain the original error message
723
+ # as the query might have encountered an issue prior to cancellation.
724
+ err = (
725
+ f"SQL execution was cancelled by the client due to a timeout. "
726
+ f"Error message received from the server: {err}"
727
+ )
728
+ if "data" in ret:
729
+ err += ret["data"].get("errorMessage", "")
730
+ errvalue = {
731
+ "msg": err,
732
+ "errno": int(code),
733
+ "sqlstate": self._sqlstate,
734
+ "sfqid": self._sfqid,
735
+ "query": query,
736
+ }
737
+ is_integrity_error = (
738
+ code == "100072"
739
+ ) # NULL result in a non-nullable column
740
+ error_class = IntegrityError if is_integrity_error else ProgrammingError
741
+ Error.errorhandler_wrapper(self.connection, self, error_class, errvalue)
742
+ return self
743
+
744
+ async def executemany(
745
+ self,
746
+ command: str,
747
+ seqparams: Sequence[Any] | dict[str, Any],
748
+ **kwargs: Any,
749
+ ) -> SnowflakeCursor:
750
+ """Executes a command/query with the given set of parameters sequentially."""
751
+ logger.debug("executing many SQLs/commands")
752
+ command = command.strip(" \t\n\r") if command else None
753
+
754
+ if not seqparams:
755
+ logger.warning(
756
+ "No parameters provided to executemany, returning without doing anything."
757
+ )
758
+ return self
759
+
760
+ if self.INSERT_SQL_RE.match(command) and (
761
+ "num_statements" not in kwargs or kwargs.get("num_statements") == 1
762
+ ):
763
+ if self._connection.is_pyformat:
764
+ # TODO(SNOW-940692) - utilize multi-statement instead of rewriting the query and
765
+ # accumulate results to mock the result from a single insert statement as formatted below
766
+ logger.debug("rewriting INSERT query")
767
+ command_wo_comments = re.sub(self.COMMENT_SQL_RE, "", command)
768
+ m = self.INSERT_SQL_VALUES_RE.match(command_wo_comments)
769
+ if not m:
770
+ Error.errorhandler_wrapper(
771
+ self.connection,
772
+ self,
773
+ InterfaceError,
774
+ {
775
+ "msg": "Failed to rewrite multi-row insert",
776
+ "errno": ER_FAILED_TO_REWRITE_MULTI_ROW_INSERT,
777
+ },
778
+ )
779
+
780
+ fmt = m.group(1)
781
+ values = []
782
+ for param in seqparams:
783
+ logger.debug(f"parameter: {param}")
784
+ values.append(
785
+ fmt % self._connection._process_params_pyformat(param, self)
786
+ )
787
+ command = command.replace(fmt, ",".join(values), 1)
788
+ await self.execute(command, **kwargs)
789
+ return self
790
+ else:
791
+ logger.debug("bulk insert")
792
+ # sanity check
793
+ row_size = len(seqparams[0])
794
+ for row in seqparams:
795
+ if len(row) != row_size:
796
+ error_value = {
797
+ "msg": f"Bulk data size don't match. expected: {row_size}, "
798
+ f"got: {len(row)}, command: {command}",
799
+ "errno": ER_INVALID_VALUE,
800
+ }
801
+ Error.errorhandler_wrapper(
802
+ self.connection, self, InterfaceError, error_value
803
+ )
804
+ return self
805
+ bind_size = len(seqparams) * row_size
806
+ bind_stage = None
807
+ if (
808
+ bind_size
809
+ >= self.connection._session_parameters[
810
+ "CLIENT_STAGE_ARRAY_BINDING_THRESHOLD"
811
+ ]
812
+ > 0
813
+ ):
814
+ # bind stage optimization
815
+ try:
816
+ rows = self.connection._write_params_to_byte_rows(seqparams)
817
+ bind_uploader = BindUploadAgent(self, rows)
818
+ await bind_uploader.upload()
819
+ bind_stage = bind_uploader.stage_path
820
+ except BindUploadError:
821
+ logger.debug(
822
+ "Failed to upload binds to stage, sending binds to "
823
+ "Snowflake instead."
824
+ )
825
+ binding_param = (
826
+ None if bind_stage else list(map(list, zip(*seqparams)))
827
+ ) # transpose
828
+ await self.execute(
829
+ command, params=binding_param, _bind_stage=bind_stage, **kwargs
830
+ )
831
+ return self
832
+
833
+ self.reset()
834
+ if "num_statements" not in kwargs:
835
+ # fall back to old driver behavior when the user does not provide the parameter to enable
836
+ # multi-statement optimizations for executemany
837
+ for param in seqparams:
838
+ await self.execute(command, params=param, _do_reset=False, **kwargs)
839
+ else:
840
+ if re.search(";/s*$", command) is None:
841
+ command = command + "; "
842
+ if self._connection.is_pyformat and not kwargs.get(
843
+ "_force_qmark_paramstyle", False
844
+ ):
845
+ processed_queries = [
846
+ await self._preprocess_pyformat_query(command, params)
847
+ for params in seqparams
848
+ ]
849
+ query = "".join(processed_queries)
850
+ params = None
851
+ else:
852
+ query = command * len(seqparams)
853
+ params = [param for parameters in seqparams for param in parameters]
854
+
855
+ kwargs["num_statements"]: int = kwargs.get("num_statements") * len(
856
+ seqparams
857
+ )
858
+
859
+ await self.execute(query, params, _do_reset=False, **kwargs)
860
+
861
+ return self
862
+
863
+ async def execute_async(self, *args: Any, **kwargs: Any) -> dict[str, Any]:
864
+ """Convenience function to execute a query without waiting for results (asynchronously).
865
+
866
+ This function takes the same arguments as execute, please refer to that function
867
+ for documentation. Please note that PUT and GET statements are not supported by this method.
868
+ """
869
+ kwargs["_exec_async"] = True
870
+ return await self.execute(*args, **kwargs)
871
+
872
+ @property
873
+ def errorhandler(self):
874
+ # TODO: SNOW-1763103 for async error handler
875
+ raise NotImplementedError(
876
+ "Async Snowflake Python Connector does not support errorhandler. "
877
+ "Please open a feature request issue in github if your want this feature: "
878
+ "https://github.com/snowflakedb/snowflake-connector-python/issues/new/choose."
879
+ )
880
+
881
+ @errorhandler.setter
882
+ def errorhandler(self, value):
883
+ # TODO: SNOW-1763103 for async error handler
884
+ raise NotImplementedError(
885
+ "Async Snowflake Python Connector does not support errorhandler. "
886
+ "Please open a feature request issue in github if your want this feature: "
887
+ "https://github.com/snowflakedb/snowflake-connector-python/issues/new/choose."
888
+ )
889
+
890
+ async def describe(self, *args: Any, **kwargs: Any) -> list[ResultMetadata]:
891
+ """Obtain the schema of the result without executing the query.
892
+
893
+ This function takes the same arguments as execute, please refer to that function
894
+ for documentation.
895
+
896
+ Returns:
897
+ The schema of the result.
898
+ """
899
+ kwargs["_describe_only"] = kwargs["_is_internal"] = True
900
+ await self.execute(*args, **kwargs)
901
+
902
+ if self._description is None:
903
+ return None
904
+ return [meta._to_result_metadata_v1() for meta in self._description]
905
+
906
+ @abc.abstractmethod
907
+ async def fetchone(self) -> FetchRow:
908
+ pass
909
+
910
+ async def _fetchone(self) -> dict[str, Any] | tuple[Any, ...] | None:
911
+ """
912
+ Fetches one row.
913
+
914
+ Returns a dict if self._use_dict_result is True, otherwise
915
+ returns tuple.
916
+ """
917
+ if self._prefetch_hook is not None:
918
+ await self._prefetch_hook()
919
+ if self._result is None and self._result_set is not None:
920
+ self._result: ResultSetIterator = await self._result_set._create_iter()
921
+ self._result_state = ResultState.VALID
922
+ try:
923
+ if self._result is None:
924
+ raise TypeError("'NoneType' object is not an iterator")
925
+ _next = await self._result.get_next()
926
+ if isinstance(_next, Exception):
927
+ Error.errorhandler_wrapper_from_ready_exception(
928
+ self._connection,
929
+ self,
930
+ _next,
931
+ )
932
+ if _next is not None:
933
+ self._rownumber += 1
934
+ return _next
935
+ except TypeError as err:
936
+ if self._result_state == ResultState.DEFAULT:
937
+ raise err
938
+ else:
939
+ return None
940
+
941
+ async def fetchmany(self, size: int | None = None) -> list[FetchRow]:
942
+ """Fetches the number of specified rows."""
943
+ if size is None:
944
+ size = self.arraysize
945
+
946
+ if size < 0:
947
+ errorvalue = {
948
+ "msg": (
949
+ "The number of rows is not zero or " "positive number: {}"
950
+ ).format(size),
951
+ "errno": ER_NOT_POSITIVE_SIZE,
952
+ }
953
+ Error.errorhandler_wrapper(
954
+ self.connection, self, ProgrammingError, errorvalue
955
+ )
956
+ ret = []
957
+ while size > 0:
958
+ row = await self.fetchone()
959
+ if row is None:
960
+ break
961
+ ret.append(row)
962
+ if size is not None:
963
+ size -= 1
964
+
965
+ return ret
966
+
967
+ async def fetchall(self) -> list[tuple] | list[dict]:
968
+ """Fetches all of the results."""
969
+ if self._prefetch_hook is not None:
970
+ await self._prefetch_hook()
971
+ if self._result is None and self._result_set is not None:
972
+ self._result: ResultSetIterator = await self._result_set._create_iter(
973
+ is_fetch_all=True,
974
+ )
975
+ self._result_state = ResultState.VALID
976
+
977
+ if self._result is None:
978
+ if self._result_state == ResultState.DEFAULT:
979
+ raise TypeError("'NoneType' object is not an iterator")
980
+ else:
981
+ return []
982
+
983
+ return await self._result.fetch_all_data()
984
+
985
+ async def fetch_arrow_batches(self) -> AsyncIterator[Table]:
986
+ self.check_can_use_arrow_resultset()
987
+ if self._prefetch_hook is not None:
988
+ await self._prefetch_hook()
989
+ if self._query_result_format != "arrow":
990
+ raise NotSupportedError
991
+ await self._log_telemetry_job_data(
992
+ TelemetryField.ARROW_FETCH_BATCHES, TelemetryData.TRUE
993
+ )
994
+ return await self._result_set._fetch_arrow_batches()
995
+
996
+ @overload
997
+ async def fetch_arrow_all(
998
+ self, force_return_table: Literal[False]
999
+ ) -> Table | None: ...
1000
+
1001
+ @overload
1002
+ async def fetch_arrow_all(self, force_return_table: Literal[True]) -> Table: ...
1003
+
1004
+ async def fetch_arrow_all(self, force_return_table: bool = False) -> Table | None:
1005
+ """
1006
+ Args:
1007
+ force_return_table: Set to True so that when the query returns zero rows,
1008
+ an empty pyarrow table will be returned with schema using the highest bit length for each column.
1009
+ Default value is False in which case None is returned in case of zero rows.
1010
+ """
1011
+ self.check_can_use_arrow_resultset()
1012
+
1013
+ if self._prefetch_hook is not None:
1014
+ await self._prefetch_hook()
1015
+ if self._query_result_format != "arrow":
1016
+ raise NotSupportedError
1017
+ await self._log_telemetry_job_data(
1018
+ TelemetryField.ARROW_FETCH_ALL, TelemetryData.TRUE
1019
+ )
1020
+ return await self._result_set._fetch_arrow_all(
1021
+ force_return_table=force_return_table
1022
+ )
1023
+
1024
+ async def fetch_pandas_batches(self, **kwargs: Any) -> AsyncIterator[DataFrame]:
1025
+ """Fetches a single Arrow Table."""
1026
+ self.check_can_use_pandas()
1027
+ if self._prefetch_hook is not None:
1028
+ await self._prefetch_hook()
1029
+ if self._query_result_format != "arrow":
1030
+ raise NotSupportedError
1031
+ await self._log_telemetry_job_data(
1032
+ TelemetryField.PANDAS_FETCH_BATCHES, TelemetryData.TRUE
1033
+ )
1034
+ return await self._result_set._fetch_pandas_batches(**kwargs)
1035
+
1036
+ async def fetch_pandas_all(self, **kwargs: Any) -> DataFrame:
1037
+ self.check_can_use_pandas()
1038
+ if self._prefetch_hook is not None:
1039
+ await self._prefetch_hook()
1040
+ if self._query_result_format != "arrow":
1041
+ raise NotSupportedError
1042
+ await self._log_telemetry_job_data(
1043
+ TelemetryField.PANDAS_FETCH_ALL, TelemetryData.TRUE
1044
+ )
1045
+ return await self._result_set._fetch_pandas_all(**kwargs)
1046
+
1047
+ async def nextset(self) -> SnowflakeCursor | None:
1048
+ """
1049
+ Fetches the next set of results if the previously executed query was multi-statement so that subsequent calls
1050
+ to any of the fetch*() methods will return rows from the next query's set of results. Returns None if no more
1051
+ query results are available.
1052
+ """
1053
+ if self._prefetch_hook is not None:
1054
+ await self._prefetch_hook()
1055
+ self.reset()
1056
+ if self._multi_statement_resultIds:
1057
+ await self.query_result(self._multi_statement_resultIds[0])
1058
+ logger.info(
1059
+ f"Retrieved results for query ID: {self._multi_statement_resultIds.popleft()}"
1060
+ )
1061
+ return self
1062
+
1063
+ return None
1064
+
1065
+ async def get_result_batches(self) -> list[ResultBatch] | None:
1066
+ """Get the previously executed query's ``ResultBatch`` s if available.
1067
+
1068
+ If they are unavailable, in case nothing has been executed yet None will
1069
+ be returned.
1070
+
1071
+ For a detailed description of ``ResultBatch`` s please see the docstring of:
1072
+ ``snowflake.connector.result_batches.ResultBatch``
1073
+ """
1074
+ if self._result_set is None:
1075
+ return None
1076
+ await self._log_telemetry_job_data(
1077
+ TelemetryField.GET_PARTITIONS_USED, TelemetryData.TRUE
1078
+ )
1079
+ return self._result_set.batches
1080
+
1081
+ async def _download(
1082
+ self,
1083
+ stage_location: str,
1084
+ target_directory: str,
1085
+ options: dict[str, Any],
1086
+ _do_reset: bool = True,
1087
+ ) -> None:
1088
+ """Downloads from the stage location to the target directory.
1089
+
1090
+ Args:
1091
+ stage_location (str): The location of the stage to download from.
1092
+ target_directory (str): The destination directory to download into.
1093
+ options (dict[str, Any]): The download options.
1094
+ _do_reset (bool, optional): Whether to reset the cursor before
1095
+ downloading, by default we will reset the cursor.
1096
+ """
1097
+ if _do_reset:
1098
+ self.reset()
1099
+
1100
+ # Interpret the file operation.
1101
+ ret = await self.connection._file_operation_parser.parse_file_operation(
1102
+ stage_location=stage_location,
1103
+ local_file_name=None,
1104
+ target_directory=target_directory,
1105
+ command_type=CMD_TYPE_DOWNLOAD,
1106
+ options=options,
1107
+ )
1108
+
1109
+ # Execute the file operation based on the interpretation above.
1110
+ file_transfer_agent = self._create_file_transfer_agent(
1111
+ "", # empty command because it is triggered by directly calling this util not by a SQL query
1112
+ ret,
1113
+ )
1114
+ await file_transfer_agent.execute()
1115
+ await self._init_result_and_meta(file_transfer_agent.result())
1116
+
1117
+ async def _upload(
1118
+ self,
1119
+ local_file_name: str,
1120
+ stage_location: str,
1121
+ options: dict[str, Any],
1122
+ _do_reset: bool = True,
1123
+ ) -> None:
1124
+ """Uploads the local file to the stage location.
1125
+
1126
+ Args:
1127
+ local_file_name (str): The local file to be uploaded.
1128
+ stage_location (str): The stage location to upload the local file to.
1129
+ options (dict[str, Any]): The upload options.
1130
+ _do_reset (bool, optional): Whether to reset the cursor before
1131
+ uploading, by default we will reset the cursor.
1132
+ """
1133
+ if _do_reset:
1134
+ self.reset()
1135
+
1136
+ # Interpret the file operation.
1137
+ ret = await self.connection._file_operation_parser.parse_file_operation(
1138
+ stage_location=stage_location,
1139
+ local_file_name=local_file_name,
1140
+ target_directory=None,
1141
+ command_type=CMD_TYPE_UPLOAD,
1142
+ options=options,
1143
+ )
1144
+
1145
+ # Execute the file operation based on the interpretation above.
1146
+ file_transfer_agent = self._create_file_transfer_agent(
1147
+ "", # empty command because it is triggered by directly calling this util not by a SQL query
1148
+ ret,
1149
+ force_put_overwrite=False, # _upload should respect user decision on overwriting
1150
+ )
1151
+ await file_transfer_agent.execute()
1152
+ await self._init_result_and_meta(file_transfer_agent.result())
1153
+
1154
+ async def _download_stream(
1155
+ self, stage_location: str, decompress: bool = False
1156
+ ) -> IO[bytes]:
1157
+ """Downloads from the stage location as a stream.
1158
+
1159
+ Args:
1160
+ stage_location (str): The location of the stage to download from.
1161
+ decompress (bool, optional): Whether to decompress the file, by
1162
+ default we do not decompress.
1163
+
1164
+ Returns:
1165
+ IO[bytes]: A stream to read from.
1166
+ """
1167
+ # Interpret the file operation.
1168
+ ret = await self.connection._file_operation_parser.parse_file_operation(
1169
+ stage_location=stage_location,
1170
+ local_file_name=None,
1171
+ target_directory=None,
1172
+ command_type=CMD_TYPE_DOWNLOAD,
1173
+ options=None,
1174
+ has_source_from_stream=True,
1175
+ )
1176
+
1177
+ # Set up stream downloading based on the interpretation and return the stream for reading.
1178
+ return await self.connection._stream_downloader.download_as_stream(
1179
+ ret, decompress
1180
+ )
1181
+
1182
+ async def _upload_stream(
1183
+ self,
1184
+ input_stream: IO[bytes],
1185
+ stage_location: str,
1186
+ options: dict[str, Any],
1187
+ _do_reset: bool = True,
1188
+ ) -> None:
1189
+ """Uploads content in the input stream to the stage location.
1190
+
1191
+ Args:
1192
+ input_stream (IO[bytes]): A stream to read from.
1193
+ stage_location (str): The location of the stage to upload to.
1194
+ options (dict[str, Any]): The upload options.
1195
+ _do_reset (bool, optional): Whether to reset the cursor before
1196
+ uploading, by default we will reset the cursor.
1197
+ """
1198
+ if _do_reset:
1199
+ self.reset()
1200
+
1201
+ # Interpret the file operation.
1202
+ ret = await self.connection._file_operation_parser.parse_file_operation(
1203
+ stage_location=stage_location,
1204
+ local_file_name=None,
1205
+ target_directory=None,
1206
+ command_type=CMD_TYPE_UPLOAD,
1207
+ options=options,
1208
+ has_source_from_stream=input_stream,
1209
+ )
1210
+
1211
+ # Execute the file operation based on the interpretation above.
1212
+ file_transfer_agent = self._create_file_transfer_agent(
1213
+ "", # empty command because it is triggered by directly calling this util not by a SQL query
1214
+ ret,
1215
+ source_from_stream=input_stream,
1216
+ force_put_overwrite=False, # _upload should respect user decision on overwriting
1217
+ )
1218
+ await file_transfer_agent.execute()
1219
+ await self._init_result_and_meta(file_transfer_agent.result())
1220
+
1221
+ async def get_results_from_sfqid(self, sfqid: str) -> None:
1222
+ """Gets the results from previously ran query. This methods differs from ``SnowflakeCursor.query_result``
1223
+ in that it monitors the ``sfqid`` until it is no longer running, and then retrieves the results.
1224
+ """
1225
+
1226
+ async def wait_until_ready() -> None:
1227
+ """Makes sure query has finished executing and once it has retrieves results."""
1228
+ no_data_counter = 0
1229
+ retry_pattern_pos = 0
1230
+ while True:
1231
+ status, status_resp = await self.connection._get_query_status(sfqid)
1232
+ self.connection._cache_query_status(sfqid, status)
1233
+ if not self.connection.is_still_running(status):
1234
+ break
1235
+ if status == QueryStatus.NO_DATA: # pragma: no cover
1236
+ no_data_counter += 1
1237
+ if no_data_counter > ASYNC_NO_DATA_MAX_RETRY:
1238
+ raise DatabaseError(
1239
+ "Cannot retrieve data on the status of this query. No information returned "
1240
+ "from server for query '{}'"
1241
+ )
1242
+ await asyncio.sleep(
1243
+ 0.5 * ASYNC_RETRY_PATTERN[retry_pattern_pos]
1244
+ ) # Same wait as JDBC
1245
+ # If we can advance in ASYNC_RETRY_PATTERN then do so
1246
+ if retry_pattern_pos < (len(ASYNC_RETRY_PATTERN) - 1):
1247
+ retry_pattern_pos += 1
1248
+ if status != QueryStatus.SUCCESS:
1249
+ logger.info(f"Status of query '{sfqid}' is {status.name}")
1250
+ self.connection._process_error_query_status(
1251
+ sfqid,
1252
+ status_resp,
1253
+ error_message=f"Status of query '{sfqid}' is {status.name}, results are unavailable",
1254
+ error_cls=DatabaseError,
1255
+ )
1256
+ await self._inner_cursor.execute(
1257
+ f"select * from table(result_scan('{sfqid}'))"
1258
+ )
1259
+ self._result = self._inner_cursor._result
1260
+ self._query_result_format = self._inner_cursor._query_result_format
1261
+ self._total_rowcount = self._inner_cursor._total_rowcount
1262
+ self._description = self._inner_cursor._description
1263
+ self._result_set = self._inner_cursor._result_set
1264
+ self._result_state = ResultState.VALID
1265
+ self._rownumber = 0
1266
+ # Unset this function, so that we don't block anymore
1267
+ self._prefetch_hook = None
1268
+
1269
+ if self._inner_cursor._total_rowcount == 1 and _is_successful_multi_stmt(
1270
+ await self._inner_cursor.fetchall()
1271
+ ):
1272
+ url = f"/queries/{sfqid}/result"
1273
+ ret = await self._connection.rest.request(url=url, method="get")
1274
+ if "data" in ret and "resultIds" in ret["data"]:
1275
+ await self._init_multi_statement_results(ret["data"])
1276
+
1277
+ def _is_successful_multi_stmt(rows: list[Any]) -> bool:
1278
+ if len(rows) != 1:
1279
+ return False
1280
+ row = rows[0]
1281
+ if isinstance(row, tuple):
1282
+ return row == ("Multiple statements executed successfully.",)
1283
+ elif isinstance(row, dict):
1284
+ return row == {
1285
+ "multiple statement execution": "Multiple statements executed successfully."
1286
+ }
1287
+ else:
1288
+ return False
1289
+
1290
+ await self.connection.get_query_status_throw_if_error(
1291
+ sfqid
1292
+ ) # Trigger an exception if query failed
1293
+ self._inner_cursor = self.__class__(self.connection)
1294
+ self._sfqid = sfqid
1295
+ self._prefetch_hook = wait_until_ready
1296
+
1297
+ async def query_result(self, qid: str) -> SnowflakeCursor:
1298
+ """Query the result of a previously executed query."""
1299
+ url = f"/queries/{qid}/result"
1300
+ ret = await self._connection.rest.request(url=url, method="get")
1301
+ self._sfqid = (
1302
+ ret["data"]["queryId"]
1303
+ if "data" in ret and "queryId" in ret["data"]
1304
+ else None
1305
+ )
1306
+ self._sqlstate = (
1307
+ ret["data"]["sqlState"]
1308
+ if "data" in ret and "sqlState" in ret["data"]
1309
+ else None
1310
+ )
1311
+ logger.debug("sfqid=%s", self._sfqid)
1312
+
1313
+ if ret.get("success"):
1314
+ data = ret.get("data")
1315
+ await self._init_result_and_meta(data)
1316
+ else:
1317
+ logger.debug("failed")
1318
+ logger.debug(ret)
1319
+ err = ret["message"]
1320
+ code = ret.get("code", -1)
1321
+ if "data" in ret:
1322
+ err += ret["data"].get("errorMessage", "")
1323
+ errvalue = {
1324
+ "msg": err,
1325
+ "errno": int(code),
1326
+ "sqlstate": self._sqlstate,
1327
+ "sfqid": self._sfqid,
1328
+ }
1329
+ Error.errorhandler_wrapper(
1330
+ self.connection, self, ProgrammingError, errvalue
1331
+ )
1332
+ return self
1333
+
1334
+ def _create_file_transfer_agent(
1335
+ self,
1336
+ command: str,
1337
+ ret: dict[str, Any],
1338
+ /,
1339
+ **kwargs,
1340
+ ) -> SnowflakeFileTransferAgent:
1341
+ from snowflake.connector.aio._file_transfer_agent import (
1342
+ SnowflakeFileTransferAgent,
1343
+ )
1344
+
1345
+ return SnowflakeFileTransferAgent(
1346
+ self,
1347
+ command,
1348
+ ret,
1349
+ use_s3_regional_url=self._connection.enable_stage_s3_privatelink_for_us_east_1,
1350
+ unsafe_file_write=self._connection.unsafe_file_write,
1351
+ reraise_error_in_file_transfer_work_function=self._connection._reraise_error_in_file_transfer_work_function,
1352
+ **kwargs,
1353
+ )
1354
+
1355
+
1356
+ class SnowflakeCursor(SnowflakeCursorBase[tuple[Any, ...]]):
1357
+ """Implementation of Cursor object that is returned from Connection.cursor() method.
1358
+
1359
+ Attributes:
1360
+ description: A list of namedtuples about metadata for all columns.
1361
+ rowcount: The number of records updated or selected. If not clear, -1 is returned.
1362
+ rownumber: The current 0-based index of the cursor in the result set or None if the index cannot be
1363
+ determined.
1364
+ sfqid: Snowflake query id in UUID form. Include this in the problem report to the customer support.
1365
+ sqlstate: Snowflake SQL State code.
1366
+ timestamp_output_format: Snowflake timestamp_output_format for timestamps.
1367
+ timestamp_ltz_output_format: Snowflake output format for LTZ timestamps.
1368
+ timestamp_tz_output_format: Snowflake output format for TZ timestamps.
1369
+ timestamp_ntz_output_format: Snowflake output format for NTZ timestamps.
1370
+ date_output_format: Snowflake output format for dates.
1371
+ time_output_format: Snowflake output format for times.
1372
+ timezone: Snowflake timezone.
1373
+ binary_output_format: Snowflake output format for binary fields.
1374
+ arraysize: The default number of rows fetched by fetchmany.
1375
+ connection: The connection object by which the cursor was created.
1376
+ errorhandle: The class that handles error handling.
1377
+ is_file_transfer: Whether, or not the current command is a put, or get.
1378
+ """
1379
+
1380
+ @property
1381
+ def _use_dict_result(self) -> bool:
1382
+ return False
1383
+
1384
+ async def fetchone(self) -> tuple[Any, ...] | None:
1385
+ row = await self._fetchone()
1386
+ if not (row is None or isinstance(row, tuple)):
1387
+ raise TypeError(f"fetchone got unexpected result: {row}")
1388
+ return row
1389
+
1390
+
1391
+ class DictCursor(SnowflakeCursorBase[dict[str, Any]]):
1392
+ """Cursor returning results in a dictionary."""
1393
+
1394
+ @property
1395
+ def _use_dict_result(self) -> bool:
1396
+ return True
1397
+
1398
+ async def fetchone(self) -> dict[str, Any] | None:
1399
+ row = await self._fetchone()
1400
+ if not (row is None or isinstance(row, dict)):
1401
+ raise TypeError(f"fetchone got unexpected result: {row}")
1402
+ return row