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,1297 @@
1
+ #!/usr/bin/env python
2
+ from __future__ import annotations
3
+
4
+ import collections
5
+ import contextlib
6
+ import gzip
7
+ import itertools
8
+ import json
9
+ import logging
10
+ import re
11
+ import time
12
+ import uuid
13
+ from collections import OrderedDict
14
+ from threading import Lock
15
+ from typing import TYPE_CHECKING, Any
16
+
17
+ import OpenSSL.SSL
18
+
19
+ from snowflake.connector.secret_detector import SecretDetector
20
+ from snowflake.connector.vendored.requests.models import PreparedRequest
21
+ from snowflake.connector.vendored.urllib3.connectionpool import (
22
+ HTTPConnectionPool,
23
+ HTTPSConnectionPool,
24
+ )
25
+
26
+ from . import ssl_wrap_socket
27
+ from .compat import (
28
+ BAD_GATEWAY,
29
+ BAD_REQUEST,
30
+ FORBIDDEN,
31
+ GATEWAY_TIMEOUT,
32
+ INTERNAL_SERVER_ERROR,
33
+ METHOD_NOT_ALLOWED,
34
+ OK,
35
+ REQUEST_TIMEOUT,
36
+ SERVICE_UNAVAILABLE,
37
+ TOO_MANY_REQUESTS,
38
+ UNAUTHORIZED,
39
+ BadStatusLine,
40
+ IncompleteRead,
41
+ urlencode,
42
+ urlparse,
43
+ )
44
+ from .constants import (
45
+ _CONNECTIVITY_ERR_MSG,
46
+ _SNOWFLAKE_HOST_SUFFIX_REGEX,
47
+ HTTP_HEADER_ACCEPT,
48
+ HTTP_HEADER_CONTENT_TYPE,
49
+ HTTP_HEADER_SERVICE_NAME,
50
+ HTTP_HEADER_USER_AGENT,
51
+ )
52
+ from .description import (
53
+ CLIENT_NAME,
54
+ CLIENT_VERSION,
55
+ COMPILER,
56
+ IMPLEMENTATION,
57
+ OPERATING_SYSTEM,
58
+ PLATFORM,
59
+ PYTHON_VERSION,
60
+ SNOWFLAKE_CONNECTOR_VERSION,
61
+ )
62
+ from .errorcode import (
63
+ ER_CONNECTION_IS_CLOSED,
64
+ ER_CONNECTION_TIMEOUT,
65
+ ER_FAILED_TO_CONNECT_TO_DB,
66
+ ER_FAILED_TO_RENEW_SESSION,
67
+ ER_FAILED_TO_REQUEST,
68
+ ER_RETRYABLE_CODE,
69
+ )
70
+ from .errors import (
71
+ BadGatewayError,
72
+ BadRequest,
73
+ DatabaseError,
74
+ Error,
75
+ ForbiddenError,
76
+ GatewayTimeoutError,
77
+ InterfaceError,
78
+ InternalServerError,
79
+ MethodNotAllowed,
80
+ OperationalError,
81
+ OtherHTTPRetryableError,
82
+ ProgrammingError,
83
+ RefreshTokenError,
84
+ ServiceUnavailableError,
85
+ TooManyRequests,
86
+ )
87
+ from .sqlstate import (
88
+ SQLSTATE_CONNECTION_NOT_EXISTS,
89
+ SQLSTATE_CONNECTION_REJECTED,
90
+ SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
91
+ )
92
+ from .time_util import (
93
+ DEFAULT_MASTER_VALIDITY_IN_SECONDS,
94
+ TimeoutBackoffCtx,
95
+ get_time_millis,
96
+ )
97
+ from .tool.probe_connection import probe_connection
98
+ from .vendored import requests
99
+ from .vendored.requests import Response, Session
100
+ from .vendored.requests.adapters import HTTPAdapter
101
+ from .vendored.requests.auth import AuthBase
102
+ from .vendored.requests.exceptions import (
103
+ ConnectionError,
104
+ ConnectTimeout,
105
+ InvalidProxyURL,
106
+ ReadTimeout,
107
+ SSLError,
108
+ )
109
+ from .vendored.requests.utils import prepend_scheme_if_needed, select_proxy
110
+ from .vendored.urllib3.exceptions import ProtocolError
111
+ from .vendored.urllib3.poolmanager import ProxyManager
112
+ from .vendored.urllib3.util.url import parse_url
113
+
114
+ if TYPE_CHECKING:
115
+ from .connection import SnowflakeConnection
116
+ logger = logging.getLogger(__name__)
117
+
118
+ """
119
+ Monkey patch for PyOpenSSL Socket wrapper
120
+ """
121
+ ssl_wrap_socket.inject_into_urllib3()
122
+
123
+ # known applications
124
+ APPLICATION_SNOWSQL = "SnowSQL"
125
+
126
+ # requests parameters
127
+ REQUESTS_RETRY = 1 # requests library builtin retry
128
+ DEFAULT_SOCKET_CONNECT_TIMEOUT = 1 * 60 # don't reduce less than 45 seconds
129
+
130
+ # return codes
131
+ QUERY_IN_PROGRESS_CODE = "333333" # GS code: the query is in progress
132
+ QUERY_IN_PROGRESS_ASYNC_CODE = "333334" # GS code: the query is detached
133
+
134
+ ID_TOKEN_EXPIRED_GS_CODE = "390110"
135
+ SESSION_EXPIRED_GS_CODE = "390112" # GS code: session expired. need to renew
136
+ MASTER_TOKEN_NOTFOUND_GS_CODE = "390113"
137
+ MASTER_TOKEN_EXPIRED_GS_CODE = "390114"
138
+ MASTER_TOKEN_INVALD_GS_CODE = "390115"
139
+ ID_TOKEN_INVALID_LOGIN_REQUEST_GS_CODE = "390195"
140
+ BAD_REQUEST_GS_CODE = "390400"
141
+ OAUTH_ACCESS_TOKEN_EXPIRED_GS_CODE = "390318"
142
+
143
+ # other constants
144
+ CONTENT_TYPE_APPLICATION_JSON = "application/json"
145
+ ACCEPT_TYPE_APPLICATION_SNOWFLAKE = "application/snowflake"
146
+
147
+ REQUEST_TYPE_RENEW = "RENEW"
148
+
149
+ HEADER_AUTHORIZATION_KEY = "Authorization"
150
+ HEADER_SNOWFLAKE_TOKEN = 'Snowflake Token="{token}"'
151
+ HEADER_EXTERNAL_SESSION_KEY = "X-Snowflake-External-Session-ID"
152
+
153
+ REQUEST_ID = "requestId"
154
+ REQUEST_GUID = "request_guid"
155
+ SNOWFLAKE_HOST_SUFFIX = ".snowflakecomputing.com"
156
+
157
+ SNOWFLAKE_CONNECTOR_VERSION = SNOWFLAKE_CONNECTOR_VERSION
158
+ PYTHON_VERSION = PYTHON_VERSION
159
+ OPERATING_SYSTEM = OPERATING_SYSTEM
160
+ PLATFORM = PLATFORM
161
+ IMPLEMENTATION = IMPLEMENTATION
162
+ COMPILER = COMPILER
163
+
164
+ CLIENT_NAME = CLIENT_NAME # don't change!
165
+ CLIENT_VERSION = CLIENT_VERSION
166
+ PYTHON_CONNECTOR_USER_AGENT = f"{CLIENT_NAME}/{SNOWFLAKE_CONNECTOR_VERSION} ({PLATFORM}) {IMPLEMENTATION}/{PYTHON_VERSION}"
167
+
168
+ NO_TOKEN = "no-token"
169
+ NO_EXTERNAL_SESSION_ID = "no-external-session-id"
170
+
171
+ STATUS_TO_EXCEPTION: dict[int, type[Error]] = {
172
+ INTERNAL_SERVER_ERROR: InternalServerError,
173
+ FORBIDDEN: ForbiddenError,
174
+ SERVICE_UNAVAILABLE: ServiceUnavailableError,
175
+ GATEWAY_TIMEOUT: GatewayTimeoutError,
176
+ BAD_REQUEST: BadRequest,
177
+ BAD_GATEWAY: BadGatewayError,
178
+ METHOD_NOT_ALLOWED: MethodNotAllowed,
179
+ TOO_MANY_REQUESTS: TooManyRequests,
180
+ }
181
+
182
+ DEFAULT_AUTHENTICATOR = "SNOWFLAKE" # default authenticator name
183
+ EXTERNAL_BROWSER_AUTHENTICATOR = "EXTERNALBROWSER"
184
+ KEY_PAIR_AUTHENTICATOR = "SNOWFLAKE_JWT"
185
+ OAUTH_AUTHENTICATOR = "OAUTH"
186
+ OAUTH_AUTHORIZATION_CODE = "OAUTH_AUTHORIZATION_CODE"
187
+ OAUTH_CLIENT_CREDENTIALS = "OAUTH_CLIENT_CREDENTIALS"
188
+ ID_TOKEN_AUTHENTICATOR = "ID_TOKEN"
189
+ USR_PWD_MFA_AUTHENTICATOR = "USERNAME_PASSWORD_MFA"
190
+ PROGRAMMATIC_ACCESS_TOKEN = "PROGRAMMATIC_ACCESS_TOKEN"
191
+ NO_AUTH_AUTHENTICATOR = "NO_AUTH"
192
+ WORKLOAD_IDENTITY_AUTHENTICATOR = "WORKLOAD_IDENTITY"
193
+ PAT_WITH_EXTERNAL_SESSION = "PAT_WITH_EXTERNAL_SESSION"
194
+
195
+
196
+ def is_retryable_http_code(code: int) -> bool:
197
+ """Decides whether code is a retryable HTTP issue."""
198
+ return 500 <= code < 600 or code in (
199
+ BAD_REQUEST, # 400
200
+ FORBIDDEN, # 403
201
+ METHOD_NOT_ALLOWED, # 405
202
+ REQUEST_TIMEOUT, # 408
203
+ TOO_MANY_REQUESTS, # 429
204
+ )
205
+
206
+
207
+ def get_http_retryable_error(status_code: int) -> Error:
208
+ error_class: type[Error] = STATUS_TO_EXCEPTION.get(
209
+ status_code, OtherHTTPRetryableError
210
+ )
211
+ return error_class(errno=status_code)
212
+
213
+
214
+ def raise_okta_unauthorized_error(
215
+ connection: SnowflakeConnection | None, response: Response
216
+ ) -> None:
217
+ Error.errorhandler_wrapper(
218
+ connection,
219
+ None,
220
+ DatabaseError,
221
+ {
222
+ "msg": f"Failed to get authentication by OKTA: {response.status_code}: {response.reason}",
223
+ "errno": ER_FAILED_TO_CONNECT_TO_DB,
224
+ "sqlstate": SQLSTATE_CONNECTION_REJECTED,
225
+ },
226
+ )
227
+
228
+
229
+ def raise_failed_request_error(
230
+ connection: SnowflakeConnection | None,
231
+ url: str,
232
+ method: str,
233
+ response: Response,
234
+ ) -> None:
235
+ Error.errorhandler_wrapper(
236
+ connection,
237
+ None,
238
+ InterfaceError,
239
+ {
240
+ "msg": f"{response.status_code} {response.reason}: {method} {url}",
241
+ "errno": ER_FAILED_TO_REQUEST,
242
+ "sqlstate": SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
243
+ },
244
+ )
245
+
246
+
247
+ def is_login_request(url: str) -> bool:
248
+ return "login-request" in parse_url(url).path
249
+
250
+
251
+ class ProxySupportAdapter(HTTPAdapter):
252
+ """This Adapter creates proper headers for Proxy CONNECT messages."""
253
+
254
+ def get_connection(
255
+ self, url: str, proxies: OrderedDict | None = None
256
+ ) -> HTTPConnectionPool | HTTPSConnectionPool:
257
+ proxy = select_proxy(url, proxies)
258
+ parsed_url = urlparse(url)
259
+
260
+ if proxy:
261
+ proxy = prepend_scheme_if_needed(proxy, "http")
262
+ proxy_url = parse_url(proxy)
263
+ if not proxy_url.host:
264
+ raise InvalidProxyURL(
265
+ "Please check proxy URL. It is malformed"
266
+ " and could be missing the host."
267
+ )
268
+ proxy_manager = self.proxy_manager_for(proxy)
269
+
270
+ if isinstance(proxy_manager, ProxyManager):
271
+ # Add Host to proxy header SNOW-232777
272
+ proxy_manager.proxy_headers["Host"] = parsed_url.hostname
273
+ else:
274
+ logger.debug(
275
+ f"Unable to set 'Host' to proxy manager of type {type(proxy_manager)} as"
276
+ f" it does not have attribute 'proxy_headers'."
277
+ )
278
+ conn = proxy_manager.connection_from_url(url)
279
+ else:
280
+ # Only scheme should be lower case
281
+ url = parsed_url.geturl()
282
+ conn = self.poolmanager.connection_from_url(url)
283
+
284
+ return conn
285
+
286
+
287
+ class RetryRequest(Exception):
288
+ """Signal to retry request."""
289
+
290
+ pass
291
+
292
+
293
+ class ReauthenticationRequest(Exception):
294
+ """Signal to reauthenticate."""
295
+
296
+ def __init__(self, cause) -> None:
297
+ self.cause = cause
298
+
299
+
300
+ class SnowflakeAuth(AuthBase):
301
+ """Attaches HTTP Authorization header for Snowflake."""
302
+
303
+ def __init__(self, token) -> None:
304
+ # setup any auth-related data here
305
+ self.token = token
306
+
307
+ def __call__(self, r: PreparedRequest) -> PreparedRequest:
308
+ """Modifies and returns the request."""
309
+ if HEADER_AUTHORIZATION_KEY in r.headers:
310
+ del r.headers[HEADER_AUTHORIZATION_KEY]
311
+ if self.token != NO_TOKEN:
312
+ r.headers[HEADER_AUTHORIZATION_KEY] = HEADER_SNOWFLAKE_TOKEN.format(
313
+ token=self.token
314
+ )
315
+ return r
316
+
317
+
318
+ class PATWithExternalSessionAuth(AuthBase):
319
+ """Attaches HTTP Authorization headers for PAT with External Session."""
320
+
321
+ def __init__(self, token, external_session_id) -> None:
322
+ # setup any auth-related data here
323
+ self.token = token
324
+ self.external_session_id = external_session_id
325
+
326
+ def __call__(self, r: PreparedRequest) -> PreparedRequest:
327
+ """Modifies and returns the request."""
328
+ if HEADER_AUTHORIZATION_KEY in r.headers:
329
+ del r.headers[HEADER_AUTHORIZATION_KEY]
330
+ if self.token != NO_TOKEN:
331
+ r.headers[HEADER_AUTHORIZATION_KEY] = "Bearer " + self.token
332
+ if self.external_session_id != NO_EXTERNAL_SESSION_ID:
333
+ r.headers[HEADER_EXTERNAL_SESSION_KEY] = self.external_session_id
334
+ return r
335
+
336
+
337
+ class SessionPool:
338
+ def __init__(self, rest: SnowflakeRestful) -> None:
339
+ # A stack of the idle sessions
340
+ self._idle_sessions: list[Session] = []
341
+ self._active_sessions: set[Session] = set()
342
+ self._rest: SnowflakeRestful = rest
343
+
344
+ def get_session(self) -> Session:
345
+ """Returns a session from the session pool or creates a new one."""
346
+ try:
347
+ session = self._idle_sessions.pop()
348
+ except IndexError:
349
+ session = self._rest.make_requests_session()
350
+ self._active_sessions.add(session)
351
+ return session
352
+
353
+ def return_session(self, session: Session) -> None:
354
+ """Places an active session back into the idle session stack."""
355
+ try:
356
+ self._active_sessions.remove(session)
357
+ except KeyError:
358
+ logger.debug("session doesn't exist in the active session pool. Ignored...")
359
+ self._idle_sessions.append(session)
360
+
361
+ def __str__(self) -> str:
362
+ total_sessions = len(self._active_sessions) + len(self._idle_sessions)
363
+ return (
364
+ f"SessionPool {len(self._active_sessions)}/{total_sessions} active sessions"
365
+ )
366
+
367
+ def close(self) -> None:
368
+ """Closes all active and idle sessions in this session pool."""
369
+ if self._active_sessions:
370
+ logger.debug(f"Closing {len(self._active_sessions)} active sessions")
371
+ for s in itertools.chain(self._active_sessions, self._idle_sessions):
372
+ try:
373
+ s.close()
374
+ except Exception as e:
375
+ logger.info(f"Session cleanup failed: {e}")
376
+ self._active_sessions.clear()
377
+ self._idle_sessions.clear()
378
+
379
+
380
+ # Customizable JSONEncoder to support additional types.
381
+ class SnowflakeRestfulJsonEncoder(json.JSONEncoder):
382
+ def default(self, o):
383
+ if isinstance(o, uuid.UUID):
384
+ return str(o)
385
+
386
+ return super().default(o)
387
+
388
+
389
+ class SnowflakeRestful:
390
+ """Snowflake Restful class."""
391
+
392
+ def __init__(
393
+ self,
394
+ host: str = "127.0.0.1",
395
+ port: int = 8080,
396
+ protocol: str = "http",
397
+ inject_client_pause: int = 0,
398
+ connection: SnowflakeConnection | None = None,
399
+ ) -> None:
400
+ self._host = host
401
+ self._port = port
402
+ self._protocol = protocol
403
+ self._inject_client_pause = inject_client_pause
404
+ self._connection = connection
405
+ self._lock_token = Lock()
406
+ self._sessions_map: dict[str | None, SessionPool] = collections.defaultdict(
407
+ lambda: SessionPool(self)
408
+ )
409
+
410
+ # OCSP mode (OCSPMode.FAIL_OPEN by default)
411
+ ssl_wrap_socket.FEATURE_OCSP_MODE = (
412
+ self._connection._ocsp_mode()
413
+ if self._connection
414
+ else ssl_wrap_socket.DEFAULT_OCSP_MODE
415
+ )
416
+ # cache file name (enabled by default)
417
+ ssl_wrap_socket.FEATURE_OCSP_RESPONSE_CACHE_FILE_NAME = (
418
+ self._connection._ocsp_response_cache_filename if self._connection else None
419
+ )
420
+
421
+ # This is to address the issue where requests hangs
422
+ _ = "dummy".encode("idna").decode("utf-8")
423
+
424
+ @property
425
+ def token(self) -> str | None:
426
+ return self._token if hasattr(self, "_token") else None
427
+
428
+ @property
429
+ def external_session_id(self) -> str | None:
430
+ return (
431
+ self._external_session_id if hasattr(self, "_external_session_id") else None
432
+ )
433
+
434
+ @property
435
+ def master_token(self) -> str | None:
436
+ return self._master_token if hasattr(self, "_master_token") else None
437
+
438
+ @property
439
+ def master_validity_in_seconds(self) -> int:
440
+ return (
441
+ self._master_validity_in_seconds
442
+ if hasattr(self, "_master_validity_in_seconds")
443
+ and self._master_validity_in_seconds
444
+ else DEFAULT_MASTER_VALIDITY_IN_SECONDS
445
+ )
446
+
447
+ @master_validity_in_seconds.setter
448
+ def master_validity_in_seconds(self, value) -> None:
449
+ self._master_validity_in_seconds = (
450
+ value if value else DEFAULT_MASTER_VALIDITY_IN_SECONDS
451
+ )
452
+
453
+ @property
454
+ def id_token(self):
455
+ return getattr(self, "_id_token", None)
456
+
457
+ @id_token.setter
458
+ def id_token(self, value) -> None:
459
+ self._id_token = value
460
+
461
+ @property
462
+ def mfa_token(self) -> str | None:
463
+ return getattr(self, "_mfa_token", None)
464
+
465
+ @mfa_token.setter
466
+ def mfa_token(self, value: str) -> None:
467
+ self._mfa_token = value
468
+
469
+ @property
470
+ def server_url(self) -> str:
471
+ return f"{self._protocol}://{self._host}:{self._port}"
472
+
473
+ def close(self) -> None:
474
+ if hasattr(self, "_token"):
475
+ del self._token
476
+ if hasattr(self, "_master_token"):
477
+ del self._master_token
478
+ if hasattr(self, "_id_token"):
479
+ del self._id_token
480
+ if hasattr(self, "_mfa_token"):
481
+ del self._mfa_token
482
+
483
+ for session_pool in self._sessions_map.values():
484
+ session_pool.close()
485
+
486
+ def request(
487
+ self,
488
+ url,
489
+ body=None,
490
+ method: str = "post",
491
+ client: str = "sfsql",
492
+ timeout: int | None = None,
493
+ _no_results: bool = False,
494
+ _include_retry_params: bool = False,
495
+ _no_retry: bool = False,
496
+ ):
497
+ if body is None:
498
+ body = {}
499
+ if self.master_token is None and self.token is None:
500
+ Error.errorhandler_wrapper(
501
+ self._connection,
502
+ None,
503
+ DatabaseError,
504
+ {
505
+ "msg": "Connection is closed",
506
+ "errno": ER_CONNECTION_IS_CLOSED,
507
+ "sqlstate": SQLSTATE_CONNECTION_NOT_EXISTS,
508
+ },
509
+ )
510
+
511
+ if client == "sfsql":
512
+ accept_type = ACCEPT_TYPE_APPLICATION_SNOWFLAKE
513
+ else:
514
+ accept_type = CONTENT_TYPE_APPLICATION_JSON
515
+
516
+ headers = {
517
+ HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_APPLICATION_JSON,
518
+ HTTP_HEADER_ACCEPT: accept_type,
519
+ HTTP_HEADER_USER_AGENT: PYTHON_CONNECTOR_USER_AGENT,
520
+ }
521
+ try:
522
+ # SNOW-1763555: inject OpenTelemetry headers if available specifically in WC3 format
523
+ # into our request headers in case tracing is enabled. This should make sure that
524
+ # our requests are accounted for properly if OpenTelemetry is used by users.
525
+ from opentelemetry.trace.propagation.tracecontext import (
526
+ TraceContextTextMapPropagator,
527
+ )
528
+
529
+ TraceContextTextMapPropagator().inject(headers)
530
+ except Exception:
531
+ logger.debug(
532
+ "Opentelemtry otel injection failed",
533
+ exc_info=True,
534
+ )
535
+ if self._connection.service_name:
536
+ headers[HTTP_HEADER_SERVICE_NAME] = self._connection.service_name
537
+ if method == "post":
538
+ return self._post_request(
539
+ url,
540
+ headers,
541
+ json.dumps(body, cls=SnowflakeRestfulJsonEncoder),
542
+ token=self.token,
543
+ external_session_id=self.external_session_id,
544
+ _no_results=_no_results,
545
+ timeout=timeout,
546
+ _include_retry_params=_include_retry_params,
547
+ no_retry=_no_retry,
548
+ )
549
+ else:
550
+ return self._get_request(
551
+ url,
552
+ headers,
553
+ token=self.token,
554
+ external_session_id=self.external_session_id,
555
+ timeout=timeout,
556
+ )
557
+
558
+ def update_tokens(
559
+ self,
560
+ session_token,
561
+ master_token,
562
+ master_validity_in_seconds=None,
563
+ id_token=None,
564
+ mfa_token=None,
565
+ ) -> None:
566
+ """Updates session and master tokens and optionally temporary credential."""
567
+ with self._lock_token:
568
+ self._token = session_token
569
+ self._master_token = master_token
570
+ self._id_token = id_token
571
+ self._mfa_token = mfa_token
572
+ self._master_validity_in_seconds = master_validity_in_seconds
573
+
574
+ def set_pat_and_external_session(
575
+ self,
576
+ personal_access_token,
577
+ external_session_id,
578
+ ) -> None:
579
+ """Updates session and master tokens and optionally temporary credential."""
580
+ with self._lock_token:
581
+ self._personal_access_token = personal_access_token
582
+ self._token = personal_access_token
583
+ self._external_session_id = external_session_id
584
+
585
+ def _renew_session(self):
586
+ """Renew a session and master token."""
587
+ return self._token_request(REQUEST_TYPE_RENEW)
588
+
589
+ def _token_request(self, request_type):
590
+ logger.debug(
591
+ "updating session. master_token: {}".format(
592
+ "****" if self.master_token else None
593
+ )
594
+ )
595
+ headers = {
596
+ HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_APPLICATION_JSON,
597
+ HTTP_HEADER_ACCEPT: CONTENT_TYPE_APPLICATION_JSON,
598
+ HTTP_HEADER_USER_AGENT: PYTHON_CONNECTOR_USER_AGENT,
599
+ }
600
+ if self._connection.service_name:
601
+ headers[HTTP_HEADER_SERVICE_NAME] = self._connection.service_name
602
+ request_id = str(uuid.uuid4())
603
+ logger.debug("request_id: %s", request_id)
604
+ url = "/session/token-request?" + urlencode({REQUEST_ID: request_id})
605
+
606
+ # NOTE: ensure an empty key if master token is not set.
607
+ # This avoids HTTP 400.
608
+ header_token = self.master_token or ""
609
+ body = {
610
+ "oldSessionToken": self.token,
611
+ "requestType": request_type,
612
+ }
613
+ ret = self._post_request(
614
+ url,
615
+ headers,
616
+ json.dumps(body, cls=SnowflakeRestfulJsonEncoder),
617
+ token=header_token,
618
+ )
619
+ if ret.get("success") and ret.get("data", {}).get("sessionToken"):
620
+ logger.debug("success: %s", SecretDetector.mask_secrets(str(ret)))
621
+ self.update_tokens(
622
+ ret["data"]["sessionToken"],
623
+ ret["data"].get("masterToken"),
624
+ master_validity_in_seconds=ret["data"].get("masterValidityInSeconds"),
625
+ )
626
+ logger.debug("updating session completed")
627
+ return ret
628
+ else:
629
+ logger.debug("failed: %s", SecretDetector.mask_secrets(str(ret)))
630
+ err = ret.get("message")
631
+ if err is not None and ret.get("data"):
632
+ err += ret["data"].get("errorMessage", "")
633
+ errno = ret.get("code") or ER_FAILED_TO_RENEW_SESSION
634
+ if errno in (
635
+ ID_TOKEN_EXPIRED_GS_CODE,
636
+ SESSION_EXPIRED_GS_CODE,
637
+ MASTER_TOKEN_NOTFOUND_GS_CODE,
638
+ MASTER_TOKEN_EXPIRED_GS_CODE,
639
+ MASTER_TOKEN_INVALD_GS_CODE,
640
+ BAD_REQUEST_GS_CODE,
641
+ ):
642
+ raise ReauthenticationRequest(
643
+ ProgrammingError(
644
+ msg=err,
645
+ errno=int(errno),
646
+ sqlstate=SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
647
+ )
648
+ )
649
+ Error.errorhandler_wrapper(
650
+ self._connection,
651
+ None,
652
+ ProgrammingError,
653
+ {
654
+ "msg": err,
655
+ "errno": int(errno),
656
+ "sqlstate": SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
657
+ },
658
+ )
659
+
660
+ def _heartbeat(self) -> Any | dict[Any, Any] | None:
661
+ headers = {
662
+ HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_APPLICATION_JSON,
663
+ HTTP_HEADER_ACCEPT: CONTENT_TYPE_APPLICATION_JSON,
664
+ HTTP_HEADER_USER_AGENT: PYTHON_CONNECTOR_USER_AGENT,
665
+ }
666
+ if self._connection.service_name:
667
+ headers[HTTP_HEADER_SERVICE_NAME] = self._connection.service_name
668
+ request_id = str(uuid.uuid4())
669
+ logger.debug("request_id: %s", request_id)
670
+ url = "/session/heartbeat?" + urlencode({REQUEST_ID: request_id})
671
+ ret = self._post_request(
672
+ url,
673
+ headers,
674
+ None,
675
+ token=self.token,
676
+ )
677
+ if not ret.get("success"):
678
+ logger.error("Failed to heartbeat. code: %s, url: %s", ret.get("code"), url)
679
+ return ret
680
+
681
+ def delete_session(self, retry: bool = False) -> None:
682
+ """Deletes the session."""
683
+ if self.master_token is None:
684
+ Error.errorhandler_wrapper(
685
+ self._connection,
686
+ None,
687
+ DatabaseError,
688
+ {
689
+ "msg": "Connection is closed",
690
+ "errno": ER_CONNECTION_IS_CLOSED,
691
+ "sqlstate": SQLSTATE_CONNECTION_NOT_EXISTS,
692
+ },
693
+ )
694
+
695
+ url = "/session?" + urlencode({"delete": "true"})
696
+ headers = {
697
+ HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_APPLICATION_JSON,
698
+ HTTP_HEADER_ACCEPT: CONTENT_TYPE_APPLICATION_JSON,
699
+ HTTP_HEADER_USER_AGENT: PYTHON_CONNECTOR_USER_AGENT,
700
+ }
701
+ if self._connection.service_name:
702
+ headers[HTTP_HEADER_SERVICE_NAME] = self._connection.service_name
703
+
704
+ body = {}
705
+ retry_limit = 3 if retry else 1
706
+ num_retries = 0
707
+ should_retry = True
708
+ while should_retry and (num_retries < retry_limit):
709
+ try:
710
+ should_retry = False
711
+ ret = self._post_request(
712
+ url,
713
+ headers,
714
+ json.dumps(body, cls=SnowflakeRestfulJsonEncoder),
715
+ token=self.token,
716
+ timeout=5,
717
+ no_retry=True,
718
+ )
719
+ if not ret:
720
+ if retry:
721
+ should_retry = True
722
+ else:
723
+ return
724
+ elif ret.get("success"):
725
+ return
726
+ err = ret.get("message")
727
+ if err is not None and ret.get("data"):
728
+ err += ret["data"].get("errorMessage", "")
729
+ # no exception is raised
730
+ logger.debug("error in deleting session. ignoring...: %s", err)
731
+ except Exception as e:
732
+ logger.debug("error in deleting session. ignoring...: %s", e)
733
+ finally:
734
+ num_retries += 1
735
+
736
+ def _get_request(
737
+ self,
738
+ url: str,
739
+ headers: dict[str, str],
740
+ token: str = None,
741
+ external_session_id: str = None,
742
+ timeout: int | None = None,
743
+ is_fetch_query_status: bool = False,
744
+ ) -> dict[str, Any]:
745
+ if "Content-Encoding" in headers:
746
+ del headers["Content-Encoding"]
747
+ if "Content-Length" in headers:
748
+ del headers["Content-Length"]
749
+
750
+ full_url = f"{self.server_url}{url}"
751
+ ret = self.fetch(
752
+ "get",
753
+ full_url,
754
+ headers,
755
+ timeout=timeout,
756
+ token=token,
757
+ external_session_id=external_session_id,
758
+ is_fetch_query_status=is_fetch_query_status,
759
+ )
760
+ if (
761
+ ret.get("code") == SESSION_EXPIRED_GS_CODE
762
+ and self._connection._authenticator != PAT_WITH_EXTERNAL_SESSION
763
+ ):
764
+ try:
765
+ ret = self._renew_session()
766
+ except ReauthenticationRequest as ex:
767
+ if self._connection._authenticator != EXTERNAL_BROWSER_AUTHENTICATOR:
768
+ raise ex.cause
769
+ ret = self._connection._reauthenticate()
770
+ logger.debug(
771
+ "ret[code] = {code} after renew_session".format(
772
+ code=(ret.get("code", "N/A"))
773
+ )
774
+ )
775
+ if ret.get("success"):
776
+ return self._get_request(
777
+ url,
778
+ headers,
779
+ token=self.token,
780
+ is_fetch_query_status=is_fetch_query_status,
781
+ )
782
+
783
+ return ret
784
+
785
+ def _post_request(
786
+ self,
787
+ url,
788
+ headers,
789
+ body,
790
+ token=None,
791
+ external_session_id: str | None = None,
792
+ timeout: int | None = None,
793
+ socket_timeout: int | None = None,
794
+ _no_results: bool = False,
795
+ no_retry: bool = False,
796
+ _include_retry_params: bool = False,
797
+ ):
798
+ full_url = f"{self.server_url}{url}"
799
+ if self._connection._probe_connection:
800
+ from pprint import pprint
801
+
802
+ ret = probe_connection(full_url)
803
+ pprint(ret)
804
+
805
+ ret = self.fetch(
806
+ "post",
807
+ full_url,
808
+ headers,
809
+ data=body,
810
+ timeout=timeout,
811
+ token=token,
812
+ external_session_id=external_session_id,
813
+ no_retry=no_retry,
814
+ _include_retry_params=_include_retry_params,
815
+ socket_timeout=socket_timeout,
816
+ )
817
+ logger.debug(
818
+ "ret[code] = {code}, after post request".format(
819
+ code=(ret.get("code", "N/A"))
820
+ )
821
+ )
822
+
823
+ if ret.get("code") == MASTER_TOKEN_EXPIRED_GS_CODE:
824
+ self._connection.expired = True
825
+ elif (
826
+ ret.get("code") == SESSION_EXPIRED_GS_CODE
827
+ and self._connection._authenticator != PAT_WITH_EXTERNAL_SESSION
828
+ ):
829
+ try:
830
+ ret = self._renew_session()
831
+ except ReauthenticationRequest as ex:
832
+ if self._connection._authenticator != EXTERNAL_BROWSER_AUTHENTICATOR:
833
+ raise ex.cause
834
+ ret = self._connection._reauthenticate()
835
+ logger.debug(
836
+ "ret[code] = {code} after renew_session".format(
837
+ code=(ret.get("code", "N/A"))
838
+ )
839
+ )
840
+ if ret.get("success"):
841
+ return self._post_request(
842
+ url, headers, body, token=self.token, timeout=timeout
843
+ )
844
+
845
+ if isinstance(ret.get("data"), dict) and ret["data"].get("queryId"):
846
+ logger.debug("Query id: {}".format(ret["data"]["queryId"]))
847
+
848
+ if ret.get("code") == QUERY_IN_PROGRESS_ASYNC_CODE and _no_results:
849
+ return ret
850
+
851
+ while ret.get("code") in (QUERY_IN_PROGRESS_CODE, QUERY_IN_PROGRESS_ASYNC_CODE):
852
+ if self._inject_client_pause > 0:
853
+ logger.debug("waiting for %s...", self._inject_client_pause)
854
+ time.sleep(self._inject_client_pause)
855
+ # ping pong
856
+ result_url = ret["data"]["getResultUrl"]
857
+ logger.debug("ping pong starting...")
858
+ ret = self._get_request(
859
+ result_url,
860
+ headers,
861
+ token=self.token,
862
+ timeout=timeout,
863
+ is_fetch_query_status=bool(
864
+ re.match(r"^/queries/.+/result$", result_url)
865
+ ),
866
+ )
867
+ logger.debug("ret[code] = %s", ret.get("code", "N/A"))
868
+ logger.debug("ping pong done")
869
+
870
+ return ret
871
+
872
+ def fetch(
873
+ self,
874
+ method: str,
875
+ full_url: str,
876
+ headers: dict[str, Any],
877
+ data: dict[str, Any] | None = None,
878
+ timeout: int | None = None,
879
+ **kwargs,
880
+ ) -> dict[Any, Any]:
881
+ """Carry out API request with session management."""
882
+
883
+ class RetryCtx(TimeoutBackoffCtx):
884
+ def __init__(
885
+ self,
886
+ _include_retry_params: bool = False,
887
+ _include_retry_reason: bool = False,
888
+ **kwargs,
889
+ ) -> None:
890
+ super().__init__(**kwargs)
891
+ self.retry_reason = 0
892
+ self._include_retry_params = _include_retry_params
893
+ self._include_retry_reason = _include_retry_reason
894
+
895
+ def add_retry_params(self, full_url: str) -> str:
896
+ if self._include_retry_params and self.current_retry_count > 0:
897
+ retry_params = {
898
+ "clientStartTime": self._start_time_millis,
899
+ "retryCount": self.current_retry_count,
900
+ }
901
+ if self._include_retry_reason:
902
+ retry_params.update({"retryReason": self.retry_reason})
903
+ suffix = urlencode(retry_params)
904
+ sep = "&" if urlparse(full_url).query else "?"
905
+ return full_url + sep + suffix
906
+ else:
907
+ return full_url
908
+
909
+ include_retry_reason = self._connection._enable_retry_reason_in_query_response
910
+ include_retry_params = kwargs.pop("_include_retry_params", False)
911
+
912
+ with self._use_requests_session(full_url) as session:
913
+ retry_ctx = RetryCtx(
914
+ _include_retry_params=include_retry_params,
915
+ _include_retry_reason=include_retry_reason,
916
+ timeout=(
917
+ timeout if timeout is not None else self._connection.network_timeout
918
+ ),
919
+ backoff_generator=self._connection._backoff_generator,
920
+ )
921
+
922
+ retry_ctx.set_start_time()
923
+ while True:
924
+ ret = self._request_exec_wrapper(
925
+ session, method, full_url, headers, data, retry_ctx, **kwargs
926
+ )
927
+ if ret is not None:
928
+ return ret
929
+
930
+ @staticmethod
931
+ def add_request_guid(full_url: str) -> str:
932
+ """Adds request_guid parameter for HTTP request tracing."""
933
+ parsed_url = urlparse(full_url)
934
+ if not re.search(_SNOWFLAKE_HOST_SUFFIX_REGEX, parsed_url.hostname):
935
+ return full_url
936
+ request_guid = str(uuid.uuid4())
937
+ suffix = urlencode({REQUEST_GUID: request_guid})
938
+ logger.debug(f"Request guid: {request_guid}")
939
+ sep = "&" if parsed_url.query else "?"
940
+ # url has query string already, just add fields
941
+ return full_url + sep + suffix
942
+
943
+ def _request_exec_wrapper(
944
+ self,
945
+ session,
946
+ method,
947
+ full_url,
948
+ headers,
949
+ data,
950
+ retry_ctx,
951
+ no_retry: bool = False,
952
+ token=NO_TOKEN,
953
+ external_session_id=NO_EXTERNAL_SESSION_ID,
954
+ **kwargs,
955
+ ):
956
+ conn = self._connection
957
+ logger.debug(
958
+ "remaining request timeout: %s ms, retry cnt: %s",
959
+ retry_ctx.remaining_time_millis if retry_ctx.timeout is not None else "N/A",
960
+ retry_ctx.current_retry_count + 1,
961
+ )
962
+
963
+ full_url = retry_ctx.add_retry_params(full_url)
964
+ full_url = SnowflakeRestful.add_request_guid(full_url)
965
+ is_fetch_query_status = kwargs.pop("is_fetch_query_status", False)
966
+ # raise_raw_http_failure is not a public parameter and may change in the future
967
+ # it enables raising raw http errors that are not handled by
968
+ # connector, connector handles the following http error:
969
+ # 1. FORBIDDEN error when trying to login
970
+ # 2. retryable http code defined in method is_retryable_http_code
971
+ # 3. UNAUTHORIZED error when using okta authentication
972
+ # raise_raw_http_failure doesn't work for the 3 mentioned cases.
973
+ raise_raw_http_failure = kwargs.pop("raise_raw_http_failure", False)
974
+ try:
975
+ return_object = self._request_exec(
976
+ session=session,
977
+ method=method,
978
+ full_url=full_url,
979
+ headers=headers,
980
+ data=data,
981
+ token=token,
982
+ external_session_id=external_session_id,
983
+ raise_raw_http_failure=raise_raw_http_failure,
984
+ **kwargs,
985
+ )
986
+ if return_object is not None:
987
+ return return_object
988
+ if is_fetch_query_status:
989
+ err_msg = "fetch query status failed and http request returned None, this is usually caused by transient network failures, retrying..."
990
+ logger.info(err_msg)
991
+ raise RetryRequest(err_msg)
992
+ self._handle_unknown_error(method, full_url, headers, data, conn)
993
+ return {}
994
+ except RetryRequest as e:
995
+ cause = e.args[0]
996
+ if no_retry:
997
+ self.log_and_handle_http_error_with_cause(
998
+ e,
999
+ full_url,
1000
+ method,
1001
+ retry_ctx.timeout,
1002
+ retry_ctx.current_retry_count,
1003
+ conn,
1004
+ timed_out=False,
1005
+ )
1006
+ return {} # required for tests
1007
+ if not retry_ctx.should_retry:
1008
+ self.log_and_handle_http_error_with_cause(
1009
+ e,
1010
+ full_url,
1011
+ method,
1012
+ retry_ctx.timeout,
1013
+ retry_ctx.current_retry_count,
1014
+ conn,
1015
+ )
1016
+ return {} # required for tests
1017
+
1018
+ logger.debug(
1019
+ "retrying: errorclass=%s, "
1020
+ "error=%s, "
1021
+ "counter=%s, "
1022
+ "sleeping=%s(s)",
1023
+ type(cause),
1024
+ cause,
1025
+ retry_ctx.current_retry_count + 1,
1026
+ retry_ctx.current_sleep_time,
1027
+ )
1028
+ time.sleep(float(retry_ctx.current_sleep_time))
1029
+ retry_ctx.increment()
1030
+
1031
+ reason = getattr(cause, "errno", 0)
1032
+ retry_ctx.retry_reason = reason
1033
+
1034
+ if "Connection aborted" in repr(e) and "ECONNRESET" in repr(e):
1035
+ # connection is reset by the server, the underlying connection is broken and can not be reused
1036
+ # we need a new urllib3 http(s) connection in this case.
1037
+ # We need to first close the old one so that urllib3 pool manager can create a new connection
1038
+ # for new requests
1039
+ try:
1040
+ logger.debug(
1041
+ "shutting down requests session adapter due to connection aborted"
1042
+ )
1043
+ session.get_adapter(full_url).close()
1044
+ except Exception as close_adapter_exc:
1045
+ logger.debug(
1046
+ "Ignored error caused by closing https connection failure: %s",
1047
+ close_adapter_exc,
1048
+ )
1049
+ return None # retry
1050
+ except Exception as e:
1051
+ if (
1052
+ raise_raw_http_failure and isinstance(e, requests.exceptions.HTTPError)
1053
+ ) or not no_retry:
1054
+ raise e
1055
+ logger.debug("Ignored error", exc_info=True)
1056
+ return {}
1057
+
1058
+ def log_and_handle_http_error_with_cause(
1059
+ self,
1060
+ e: Exception,
1061
+ full_url: str,
1062
+ method: str,
1063
+ retry_timeout: int,
1064
+ retry_count: int,
1065
+ conn: SnowflakeConnection,
1066
+ timed_out: bool = True,
1067
+ ) -> None:
1068
+ cause = e.args[0]
1069
+ logger.error(cause, exc_info=True)
1070
+ if isinstance(cause, Error):
1071
+ Error.errorhandler_wrapper_from_cause(conn, cause)
1072
+ else:
1073
+ self.handle_invalid_certificate_error(conn, full_url, cause)
1074
+
1075
+ def handle_invalid_certificate_error(self, conn, full_url, cause) -> None:
1076
+ # all other errors raise exception
1077
+ Error.errorhandler_wrapper(
1078
+ conn,
1079
+ None,
1080
+ OperationalError,
1081
+ {
1082
+ "msg": f"Failed to execute request: {cause}",
1083
+ "errno": ER_FAILED_TO_REQUEST,
1084
+ },
1085
+ )
1086
+
1087
+ def _handle_unknown_error(self, method, full_url, headers, data, conn) -> None:
1088
+ """Handles unknown errors."""
1089
+ if data:
1090
+ _, masked_data, err_str = SecretDetector.mask_secrets(data)
1091
+ if err_str is None:
1092
+ data = masked_data
1093
+ logger.error(
1094
+ f"Failed to get the response. Hanging? "
1095
+ f"method: {method}, url: {full_url}, headers:{headers}, "
1096
+ f"data: {data}"
1097
+ )
1098
+ Error.errorhandler_wrapper(
1099
+ conn,
1100
+ None,
1101
+ OperationalError,
1102
+ {
1103
+ "msg": f"Failed to get the response. Hanging? method: {method}, url: {full_url}",
1104
+ "errno": ER_FAILED_TO_REQUEST,
1105
+ },
1106
+ )
1107
+
1108
+ def _request_exec(
1109
+ self,
1110
+ session,
1111
+ method,
1112
+ full_url,
1113
+ headers,
1114
+ data,
1115
+ token,
1116
+ external_session_id=None,
1117
+ catch_okta_unauthorized_error: bool = False,
1118
+ is_raw_text: bool = False,
1119
+ is_raw_binary: bool = False,
1120
+ binary_data_handler=None,
1121
+ socket_timeout: int | None = None,
1122
+ is_okta_authentication: bool = False,
1123
+ raise_raw_http_failure: bool = False,
1124
+ ):
1125
+ if socket_timeout is None:
1126
+ if self._connection.socket_timeout is not None:
1127
+ logger.debug("socket_timeout specified in connection")
1128
+ socket_timeout = self._connection.socket_timeout
1129
+ else:
1130
+ socket_timeout = DEFAULT_SOCKET_CONNECT_TIMEOUT
1131
+ logger.debug("socket timeout: %s", socket_timeout)
1132
+
1133
+ try:
1134
+ if not catch_okta_unauthorized_error and data and len(data) > 0:
1135
+ headers["Content-Encoding"] = "gzip"
1136
+ input_data = gzip.compress(data.encode("utf-8"))
1137
+ else:
1138
+ input_data = data
1139
+
1140
+ download_start_time = get_time_millis()
1141
+ # socket timeout is constant. You should be able to receive
1142
+ # the response within the time. If not, ConnectReadTimeout or
1143
+ # ReadTimeout is raised.
1144
+ auth = (
1145
+ PATWithExternalSessionAuth(token, external_session_id)
1146
+ if (external_session_id is not None and token is not None)
1147
+ else SnowflakeAuth(token)
1148
+ )
1149
+ raw_ret = session.request(
1150
+ method=method,
1151
+ url=full_url,
1152
+ headers=headers,
1153
+ data=input_data,
1154
+ timeout=socket_timeout,
1155
+ verify=True,
1156
+ stream=is_raw_binary,
1157
+ auth=auth,
1158
+ )
1159
+ download_end_time = get_time_millis()
1160
+
1161
+ try:
1162
+ if raw_ret.status_code == OK:
1163
+ logger.debug("SUCCESS")
1164
+ if is_raw_text:
1165
+ ret = raw_ret.text
1166
+ elif is_raw_binary:
1167
+ ret = binary_data_handler.to_iterator(
1168
+ raw_ret.raw, download_end_time - download_start_time
1169
+ )
1170
+ else:
1171
+ ret = raw_ret.json()
1172
+ return ret
1173
+
1174
+ if is_login_request(full_url) and raw_ret.status_code == FORBIDDEN:
1175
+ raise ForbiddenError
1176
+
1177
+ elif is_retryable_http_code(raw_ret.status_code):
1178
+ err = get_http_retryable_error(raw_ret.status_code)
1179
+ # retryable server exceptions
1180
+ if is_okta_authentication:
1181
+ raise RefreshTokenError(
1182
+ msg="OKTA authentication requires token refresh."
1183
+ )
1184
+ if is_login_request(full_url):
1185
+ logger.debug(
1186
+ "Received retryable response code while logging in. Will be handled by "
1187
+ f"authenticator. Ignore the following. Error stack: {err}",
1188
+ exc_info=True,
1189
+ )
1190
+ raise OperationalError(
1191
+ msg="Login request is retryable. Will be handled by authenticator",
1192
+ errno=ER_RETRYABLE_CODE,
1193
+ )
1194
+ else:
1195
+ logger.debug(f"{err}. Retrying...")
1196
+ raise RetryRequest(err)
1197
+
1198
+ elif (
1199
+ raw_ret.status_code == UNAUTHORIZED
1200
+ and catch_okta_unauthorized_error
1201
+ ):
1202
+ # OKTA Unauthorized errors
1203
+ raise_okta_unauthorized_error(self._connection, raw_ret)
1204
+ return None # required for tests
1205
+ else:
1206
+ if raise_raw_http_failure:
1207
+ raw_ret.raise_for_status()
1208
+ raise_failed_request_error(
1209
+ self._connection, full_url, method, raw_ret
1210
+ )
1211
+ return None # required for tests
1212
+ finally:
1213
+ raw_ret.close() # ensure response is closed
1214
+ except SSLError as se:
1215
+ msg = f"Hit non-retryable SSL error, {str(se)}.\n{_CONNECTIVITY_ERR_MSG}"
1216
+ logger.debug(msg)
1217
+ # the following code is for backward compatibility with old versions of python connector which calls
1218
+ # self._handle_unknown_error to process SSLError
1219
+ Error.errorhandler_wrapper(
1220
+ self._connection,
1221
+ None,
1222
+ OperationalError,
1223
+ {
1224
+ "msg": msg,
1225
+ "errno": ER_FAILED_TO_REQUEST,
1226
+ },
1227
+ )
1228
+ except (
1229
+ BadStatusLine,
1230
+ ConnectionError,
1231
+ ConnectTimeout,
1232
+ IncompleteRead,
1233
+ ProtocolError, # from urllib3 # from urllib3
1234
+ OpenSSL.SSL.SysCallError,
1235
+ KeyError, # SNOW-39175: asn1crypto.keys.PublicKeyInfo
1236
+ ValueError,
1237
+ ReadTimeout,
1238
+ RuntimeError,
1239
+ AttributeError, # json decoding error
1240
+ ) as err:
1241
+ if is_login_request(full_url):
1242
+ logger.debug(
1243
+ "Hit a timeout error while logging in. Will be handled by "
1244
+ f"authenticator. Ignore the following. Error stack: {err}",
1245
+ exc_info=True,
1246
+ )
1247
+ raise OperationalError(
1248
+ msg="ConnectionTimeout occurred during login. Will be handled by authenticator",
1249
+ errno=ER_CONNECTION_TIMEOUT,
1250
+ )
1251
+ else:
1252
+ logger.debug(
1253
+ "Hit retryable client error. Retrying... Ignore the following "
1254
+ f"error stack: {err}",
1255
+ exc_info=True,
1256
+ )
1257
+ raise RetryRequest(err)
1258
+ except Exception as err:
1259
+ raise err
1260
+
1261
+ def make_requests_session(self) -> Session:
1262
+ s = requests.Session()
1263
+ s.mount("http://", ProxySupportAdapter(max_retries=REQUESTS_RETRY))
1264
+ s.mount("https://", ProxySupportAdapter(max_retries=REQUESTS_RETRY))
1265
+ s._reuse_count = itertools.count()
1266
+ return s
1267
+
1268
+ @contextlib.contextmanager
1269
+ def _use_requests_session(self, url: str | None = None):
1270
+ """Session caching context manager.
1271
+
1272
+ Notes:
1273
+ The session is not closed until close() is called so each session may be used multiple times.
1274
+ """
1275
+ # short-lived session, not added to the _sessions_map
1276
+ if self._connection.disable_request_pooling:
1277
+ session = self.make_requests_session()
1278
+ try:
1279
+ yield session
1280
+ finally:
1281
+ session.close()
1282
+ else:
1283
+ try:
1284
+ hostname = urlparse(url).hostname
1285
+ except Exception:
1286
+ hostname = None
1287
+
1288
+ session_pool: SessionPool = self._sessions_map[hostname]
1289
+ session = session_pool.get_session()
1290
+ logger.debug(f"Session status for SessionPool '{hostname}', {session_pool}")
1291
+ try:
1292
+ yield session
1293
+ finally:
1294
+ session_pool.return_session(session)
1295
+ logger.debug(
1296
+ f"Session status for SessionPool '{hostname}', {session_pool}"
1297
+ )