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.
- snowflake/connector/__init__.py +118 -0
- snowflake/connector/_query_context_cache.py +276 -0
- snowflake/connector/_sql_util.py +48 -0
- snowflake/connector/_utils.py +353 -0
- snowflake/connector/aio/README.md +88 -0
- snowflake/connector/aio/__init__.py +163 -0
- snowflake/connector/aio/_azure_storage_client.py +227 -0
- snowflake/connector/aio/_bind_upload_agent.py +69 -0
- snowflake/connector/aio/_connection.py +1216 -0
- snowflake/connector/aio/_cursor.py +1402 -0
- snowflake/connector/aio/_description.py +5 -0
- snowflake/connector/aio/_direct_file_operation_utils.py +88 -0
- snowflake/connector/aio/_file_transfer_agent.py +316 -0
- snowflake/connector/aio/_gcs_storage_client.py +360 -0
- snowflake/connector/aio/_network.py +860 -0
- snowflake/connector/aio/_ocsp_asn1crypto.py +51 -0
- snowflake/connector/aio/_ocsp_snowflake.py +608 -0
- snowflake/connector/aio/_pandas_tools.py +561 -0
- snowflake/connector/aio/_result_batch.py +442 -0
- snowflake/connector/aio/_result_set.py +309 -0
- snowflake/connector/aio/_s3_storage_client.py +460 -0
- snowflake/connector/aio/_session_manager.py +588 -0
- snowflake/connector/aio/_storage_client.py +331 -0
- snowflake/connector/aio/_telemetry.py +97 -0
- snowflake/connector/aio/_time_util.py +57 -0
- snowflake/connector/aio/_wif_util.py +324 -0
- snowflake/connector/aio/auth/__init__.py +52 -0
- snowflake/connector/aio/auth/_auth.py +406 -0
- snowflake/connector/aio/auth/_by_plugin.py +131 -0
- snowflake/connector/aio/auth/_default.py +28 -0
- snowflake/connector/aio/auth/_idtoken.py +54 -0
- snowflake/connector/aio/auth/_keypair.py +60 -0
- snowflake/connector/aio/auth/_no_auth.py +31 -0
- snowflake/connector/aio/auth/_oauth.py +27 -0
- snowflake/connector/aio/auth/_oauth_code.py +117 -0
- snowflake/connector/aio/auth/_oauth_credentials.py +100 -0
- snowflake/connector/aio/auth/_okta.py +247 -0
- snowflake/connector/aio/auth/_pat.py +27 -0
- snowflake/connector/aio/auth/_usrpwdmfa.py +30 -0
- snowflake/connector/aio/auth/_webbrowser.py +405 -0
- snowflake/connector/aio/auth/_workload_identity.py +60 -0
- snowflake/connector/arrow_context.py +211 -0
- snowflake/connector/auth/__init__.py +53 -0
- snowflake/connector/auth/_auth.py +669 -0
- snowflake/connector/auth/_http_server.py +257 -0
- snowflake/connector/auth/_oauth_base.py +472 -0
- snowflake/connector/auth/by_plugin.py +219 -0
- snowflake/connector/auth/default.py +36 -0
- snowflake/connector/auth/idtoken.py +72 -0
- snowflake/connector/auth/keypair.py +225 -0
- snowflake/connector/auth/no_auth.py +39 -0
- snowflake/connector/auth/oauth.py +49 -0
- snowflake/connector/auth/oauth_code.py +490 -0
- snowflake/connector/auth/oauth_credentials.py +71 -0
- snowflake/connector/auth/okta.py +339 -0
- snowflake/connector/auth/pat.py +40 -0
- snowflake/connector/auth/usrpwdmfa.py +69 -0
- snowflake/connector/auth/webbrowser.py +514 -0
- snowflake/connector/auth/workload_identity.py +110 -0
- snowflake/connector/azure_storage_client.py +278 -0
- snowflake/connector/backoff_policies.py +141 -0
- snowflake/connector/bind_upload_agent.py +89 -0
- snowflake/connector/cache.py +696 -0
- snowflake/connector/compat.py +127 -0
- snowflake/connector/config_manager.py +553 -0
- snowflake/connector/connection.py +2671 -0
- snowflake/connector/connection_diagnostic.py +793 -0
- snowflake/connector/constants.py +460 -0
- snowflake/connector/converter.py +809 -0
- snowflake/connector/converter_issue23517.py +87 -0
- snowflake/connector/converter_null.py +14 -0
- snowflake/connector/converter_snowsql.py +205 -0
- snowflake/connector/crl.py +919 -0
- snowflake/connector/crl_cache.py +734 -0
- snowflake/connector/cursor.py +2099 -0
- snowflake/connector/dbapi.py +53 -0
- snowflake/connector/description.py +21 -0
- snowflake/connector/direct_file_operation_utils.py +88 -0
- snowflake/connector/encryption_util.py +220 -0
- snowflake/connector/errorcode.py +94 -0
- snowflake/connector/errors.py +666 -0
- snowflake/connector/externals_utils/__init__.py +0 -0
- snowflake/connector/externals_utils/externals_setup.py +30 -0
- snowflake/connector/feature.py +4 -0
- snowflake/connector/file_compression_type.py +118 -0
- snowflake/connector/file_lock.py +72 -0
- snowflake/connector/file_transfer_agent.py +1244 -0
- snowflake/connector/file_util.py +153 -0
- snowflake/connector/gcs_storage_client.py +476 -0
- snowflake/connector/gzip_decoder.py +85 -0
- snowflake/connector/interval_util.py +26 -0
- snowflake/connector/local_storage_client.py +90 -0
- snowflake/connector/log_configuration.py +62 -0
- snowflake/connector/logging_utils/__init__.py +0 -0
- snowflake/connector/logging_utils/filters.py +72 -0
- snowflake/connector/minicore/__init__.py +0 -0
- snowflake/connector/minicore/aix_ppc64/libsf_mini_core.a +0 -0
- snowflake/connector/minicore/aix_ppc64/libsf_mini_core_static.a +0 -0
- snowflake/connector/minicore/linux_aarch64_glibc/libsf_mini_core.so +0 -0
- snowflake/connector/minicore/linux_aarch64_musl/libsf_mini_core.so +0 -0
- snowflake/connector/minicore/linux_x86_64_glibc/libsf_mini_core.so +0 -0
- snowflake/connector/minicore/linux_x86_64_musl/libsf_mini_core.so +0 -0
- snowflake/connector/minicore/macos_aarch64/libsf_mini_core.dylib +0 -0
- snowflake/connector/minicore/macos_x86_64/libsf_mini_core.dylib +0 -0
- snowflake/connector/minicore/windows_x86_64/sf_mini_core.dll +0 -0
- snowflake/connector/minicore/windows_x86_64/sf_mini_core_static.lib +0 -0
- snowflake/connector/nanoarrow_arrow_iterator.cp314-win_amd64.pyd +0 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +60 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.cpp +570 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.hpp +98 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +125 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +115 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.cpp +1140 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.hpp +138 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +47 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +83 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +97 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +72 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +73 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +30 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +45 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +75 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +57 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +75 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +46 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +95 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +57 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +36 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +34 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +36 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +346 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +145 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.cpp +67 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +101 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +127 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1908 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +215 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +125 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +55 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +148 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +162 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +97 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +239 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +210 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +84 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +84 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +206 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +118 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +162 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +898 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +190 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc.c +3204 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3217 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3618 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +379 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_arrow_iterator.pyx +262 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.c +512 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.h +350 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +33273 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +397 -0
- snowflake/connector/nanoarrow_cpp/Logging/logging.cpp +100 -0
- snowflake/connector/nanoarrow_cpp/Logging/logging.hpp +49 -0
- snowflake/connector/network.py +1227 -0
- snowflake/connector/ocsp_asn1crypto.py +447 -0
- snowflake/connector/ocsp_snowflake.py +1939 -0
- snowflake/connector/options.py +179 -0
- snowflake/connector/os_details.py +88 -0
- snowflake/connector/pandas_tools.py +734 -0
- snowflake/connector/platform_detection.py +555 -0
- snowflake/connector/proxy.py +28 -0
- snowflake/connector/py.typed +0 -0
- snowflake/connector/result_batch.py +917 -0
- snowflake/connector/result_set.py +340 -0
- snowflake/connector/s3_storage_client.py +614 -0
- snowflake/connector/secret_detector.py +181 -0
- snowflake/connector/session_manager.py +650 -0
- snowflake/connector/sf_dirs.py +64 -0
- snowflake/connector/sfbinaryformat.py +35 -0
- snowflake/connector/sfdatetime.py +345 -0
- snowflake/connector/snow_logging.py +144 -0
- snowflake/connector/sqlstate.py +9 -0
- snowflake/connector/ssd_internal_keys.py +30 -0
- snowflake/connector/ssl_wrap_socket.py +281 -0
- snowflake/connector/storage_client.py +486 -0
- snowflake/connector/telemetry.py +254 -0
- snowflake/connector/telemetry_oob.py +544 -0
- snowflake/connector/test_util.py +30 -0
- snowflake/connector/time_util.py +159 -0
- snowflake/connector/token_cache.py +457 -0
- snowflake/connector/tool/__init__.py +0 -0
- snowflake/connector/tool/dump_certs.py +57 -0
- snowflake/connector/tool/dump_ocsp_response.py +139 -0
- snowflake/connector/tool/dump_ocsp_response_cache.py +194 -0
- snowflake/connector/tool/probe_connection.py +69 -0
- snowflake/connector/url_util.py +55 -0
- snowflake/connector/util_text.py +320 -0
- snowflake/connector/vendored/__init__.py +3 -0
- snowflake/connector/vendored/requests/LICENSE +175 -0
- snowflake/connector/vendored/requests/__init__.py +184 -0
- snowflake/connector/vendored/requests/__version__.py +14 -0
- snowflake/connector/vendored/requests/_internal_utils.py +50 -0
- snowflake/connector/vendored/requests/adapters.py +696 -0
- snowflake/connector/vendored/requests/api.py +157 -0
- snowflake/connector/vendored/requests/auth.py +314 -0
- snowflake/connector/vendored/requests/certs.py +17 -0
- snowflake/connector/vendored/requests/compat.py +106 -0
- snowflake/connector/vendored/requests/cookies.py +561 -0
- snowflake/connector/vendored/requests/exceptions.py +151 -0
- snowflake/connector/vendored/requests/help.py +134 -0
- snowflake/connector/vendored/requests/hooks.py +33 -0
- snowflake/connector/vendored/requests/models.py +1039 -0
- snowflake/connector/vendored/requests/packages.py +23 -0
- snowflake/connector/vendored/requests/sessions.py +831 -0
- snowflake/connector/vendored/requests/status_codes.py +128 -0
- snowflake/connector/vendored/requests/structures.py +99 -0
- snowflake/connector/vendored/requests/utils.py +1086 -0
- snowflake/connector/vendored/urllib3/LICENSE.txt +21 -0
- snowflake/connector/vendored/urllib3/__init__.py +211 -0
- snowflake/connector/vendored/urllib3/_base_connection.py +165 -0
- snowflake/connector/vendored/urllib3/_collections.py +487 -0
- snowflake/connector/vendored/urllib3/_request_methods.py +278 -0
- snowflake/connector/vendored/urllib3/_version.py +34 -0
- snowflake/connector/vendored/urllib3/connection.py +1108 -0
- snowflake/connector/vendored/urllib3/connectionpool.py +1178 -0
- snowflake/connector/vendored/urllib3/contrib/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/contrib/emscripten/__init__.py +17 -0
- snowflake/connector/vendored/urllib3/contrib/emscripten/connection.py +260 -0
- snowflake/connector/vendored/urllib3/contrib/emscripten/emscripten_fetch_worker.js +110 -0
- snowflake/connector/vendored/urllib3/contrib/emscripten/fetch.py +726 -0
- snowflake/connector/vendored/urllib3/contrib/emscripten/request.py +22 -0
- snowflake/connector/vendored/urllib3/contrib/emscripten/response.py +277 -0
- snowflake/connector/vendored/urllib3/contrib/pyopenssl.py +564 -0
- snowflake/connector/vendored/urllib3/contrib/socks.py +228 -0
- snowflake/connector/vendored/urllib3/exceptions.py +335 -0
- snowflake/connector/vendored/urllib3/fields.py +341 -0
- snowflake/connector/vendored/urllib3/filepost.py +89 -0
- snowflake/connector/vendored/urllib3/http2/__init__.py +53 -0
- snowflake/connector/vendored/urllib3/http2/connection.py +356 -0
- snowflake/connector/vendored/urllib3/http2/probe.py +87 -0
- snowflake/connector/vendored/urllib3/poolmanager.py +651 -0
- snowflake/connector/vendored/urllib3/py.typed +2 -0
- snowflake/connector/vendored/urllib3/response.py +1480 -0
- snowflake/connector/vendored/urllib3/util/__init__.py +42 -0
- snowflake/connector/vendored/urllib3/util/connection.py +137 -0
- snowflake/connector/vendored/urllib3/util/proxy.py +43 -0
- snowflake/connector/vendored/urllib3/util/request.py +263 -0
- snowflake/connector/vendored/urllib3/util/response.py +101 -0
- snowflake/connector/vendored/urllib3/util/retry.py +549 -0
- snowflake/connector/vendored/urllib3/util/ssl_.py +527 -0
- snowflake/connector/vendored/urllib3/util/ssl_match_hostname.py +159 -0
- snowflake/connector/vendored/urllib3/util/ssltransport.py +271 -0
- snowflake/connector/vendored/urllib3/util/timeout.py +275 -0
- snowflake/connector/vendored/urllib3/util/url.py +469 -0
- snowflake/connector/vendored/urllib3/util/util.py +42 -0
- snowflake/connector/vendored/urllib3/util/wait.py +124 -0
- snowflake/connector/version.py +3 -0
- snowflake/connector/wif_util.py +434 -0
- snowflake_connector_python-4.4.0.dist-info/DELVEWHEEL +2 -0
- snowflake_connector_python-4.4.0.dist-info/METADATA +1591 -0
- snowflake_connector_python-4.4.0.dist-info/RECORD +302 -0
- snowflake_connector_python-4.4.0.dist-info/WHEEL +5 -0
- snowflake_connector_python-4.4.0.dist-info/entry_points.txt +4 -0
- snowflake_connector_python-4.4.0.dist-info/licenses/LICENSE.txt +202 -0
- snowflake_connector_python-4.4.0.dist-info/licenses/NOTICE +8 -0
- snowflake_connector_python-4.4.0.dist-info/top_level.txt +1 -0
- snowflake_connector_python.libs/msvcp140-0f885b509a685d2bbfa652fed26b5fb3.dll +0 -0
|
@@ -0,0 +1,860 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import contextlib
|
|
5
|
+
import gzip
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import re
|
|
9
|
+
import uuid
|
|
10
|
+
from typing import TYPE_CHECKING, Any, AsyncGenerator
|
|
11
|
+
|
|
12
|
+
import OpenSSL.SSL
|
|
13
|
+
|
|
14
|
+
from ..compat import FORBIDDEN, OK, UNAUTHORIZED, urlencode, urlparse, urlsplit
|
|
15
|
+
from ..constants import (
|
|
16
|
+
_CONNECTIVITY_ERR_MSG,
|
|
17
|
+
HTTP_HEADER_ACCEPT,
|
|
18
|
+
HTTP_HEADER_CONTENT_TYPE,
|
|
19
|
+
HTTP_HEADER_SERVICE_NAME,
|
|
20
|
+
HTTP_HEADER_USER_AGENT,
|
|
21
|
+
)
|
|
22
|
+
from ..errorcode import (
|
|
23
|
+
ER_CONNECTION_IS_CLOSED,
|
|
24
|
+
ER_CONNECTION_TIMEOUT,
|
|
25
|
+
ER_FAILED_TO_CONNECT_TO_DB,
|
|
26
|
+
ER_FAILED_TO_RENEW_SESSION,
|
|
27
|
+
ER_FAILED_TO_REQUEST,
|
|
28
|
+
ER_HTTP_GENERAL_ERROR,
|
|
29
|
+
ER_RETRYABLE_CODE,
|
|
30
|
+
)
|
|
31
|
+
from ..errors import (
|
|
32
|
+
DatabaseError,
|
|
33
|
+
Error,
|
|
34
|
+
ForbiddenError,
|
|
35
|
+
HttpError,
|
|
36
|
+
OperationalError,
|
|
37
|
+
ProgrammingError,
|
|
38
|
+
RefreshTokenError,
|
|
39
|
+
RevocationCheckError,
|
|
40
|
+
)
|
|
41
|
+
from ..network import (
|
|
42
|
+
ACCEPT_TYPE_APPLICATION_SNOWFLAKE,
|
|
43
|
+
BAD_REQUEST_GS_CODE,
|
|
44
|
+
CONTENT_TYPE_APPLICATION_JSON,
|
|
45
|
+
DEFAULT_SOCKET_CONNECT_TIMEOUT,
|
|
46
|
+
EXTERNAL_BROWSER_AUTHENTICATOR,
|
|
47
|
+
HEADER_AUTHORIZATION_KEY,
|
|
48
|
+
HEADER_SNOWFLAKE_TOKEN,
|
|
49
|
+
ID_TOKEN_EXPIRED_GS_CODE,
|
|
50
|
+
IMPLEMENTATION,
|
|
51
|
+
MASTER_TOKEN_EXPIRED_GS_CODE,
|
|
52
|
+
MASTER_TOKEN_INVALD_GS_CODE,
|
|
53
|
+
MASTER_TOKEN_NOTFOUND_GS_CODE,
|
|
54
|
+
NO_TOKEN,
|
|
55
|
+
PLATFORM,
|
|
56
|
+
PYTHON_VERSION,
|
|
57
|
+
QUERY_IN_PROGRESS_ASYNC_CODE,
|
|
58
|
+
QUERY_IN_PROGRESS_CODE,
|
|
59
|
+
REQUEST_ID,
|
|
60
|
+
REQUEST_TYPE_RENEW,
|
|
61
|
+
SESSION_EXPIRED_GS_CODE,
|
|
62
|
+
SNOWFLAKE_CONNECTOR_VERSION,
|
|
63
|
+
ReauthenticationRequest,
|
|
64
|
+
RetryRequest,
|
|
65
|
+
)
|
|
66
|
+
from ..network import SnowflakeRestful as SnowflakeRestfulSync
|
|
67
|
+
from ..network import (
|
|
68
|
+
SnowflakeRestfulJsonEncoder,
|
|
69
|
+
get_http_retryable_error,
|
|
70
|
+
is_econnreset_exception,
|
|
71
|
+
is_login_request,
|
|
72
|
+
is_retryable_http_code,
|
|
73
|
+
)
|
|
74
|
+
from ..secret_detector import SecretDetector
|
|
75
|
+
from ..sqlstate import (
|
|
76
|
+
SQLSTATE_CONNECTION_NOT_EXISTS,
|
|
77
|
+
SQLSTATE_CONNECTION_REJECTED,
|
|
78
|
+
SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
|
|
79
|
+
)
|
|
80
|
+
from ..time_util import TimeoutBackoffCtx
|
|
81
|
+
from ._description import CLIENT_NAME
|
|
82
|
+
from ._session_manager import (
|
|
83
|
+
SessionManager,
|
|
84
|
+
SessionManagerFactory,
|
|
85
|
+
SnowflakeSSLConnectorFactory,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if TYPE_CHECKING:
|
|
89
|
+
from snowflake.connector.aio import SnowflakeConnection
|
|
90
|
+
|
|
91
|
+
logger = logging.getLogger(__name__)
|
|
92
|
+
|
|
93
|
+
PYTHON_CONNECTOR_USER_AGENT = f"{CLIENT_NAME}/{SNOWFLAKE_CONNECTOR_VERSION} ({PLATFORM}) {IMPLEMENTATION}/{PYTHON_VERSION}"
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
import aiohttp
|
|
97
|
+
except ImportError:
|
|
98
|
+
logger.warning("Please install aiohttp to use asyncio features.")
|
|
99
|
+
raise
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def raise_okta_unauthorized_error(
|
|
103
|
+
connection: SnowflakeConnection | None, response: aiohttp.ClientResponse
|
|
104
|
+
) -> None:
|
|
105
|
+
Error.errorhandler_wrapper(
|
|
106
|
+
connection,
|
|
107
|
+
None,
|
|
108
|
+
DatabaseError,
|
|
109
|
+
{
|
|
110
|
+
"msg": f"Failed to get authentication by OKTA: {response.status}: {response.reason}",
|
|
111
|
+
"errno": ER_FAILED_TO_CONNECT_TO_DB,
|
|
112
|
+
"sqlstate": SQLSTATE_CONNECTION_REJECTED,
|
|
113
|
+
},
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def raise_failed_request_error(
|
|
118
|
+
connection: SnowflakeConnection | None,
|
|
119
|
+
url: str,
|
|
120
|
+
method: str,
|
|
121
|
+
response: aiohttp.ClientResponse,
|
|
122
|
+
) -> None:
|
|
123
|
+
Error.errorhandler_wrapper(
|
|
124
|
+
connection,
|
|
125
|
+
None,
|
|
126
|
+
HttpError,
|
|
127
|
+
{
|
|
128
|
+
"msg": f"{response.status} {response.reason}: {method} {urlsplit(url).netloc}{urlsplit(url).path}",
|
|
129
|
+
"errno": ER_HTTP_GENERAL_ERROR + response.status,
|
|
130
|
+
"sqlstate": SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
|
|
131
|
+
},
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class SnowflakeRestful(SnowflakeRestfulSync):
|
|
136
|
+
def __init__(
|
|
137
|
+
self,
|
|
138
|
+
host: str = "127.0.0.1",
|
|
139
|
+
port: int = 8080,
|
|
140
|
+
protocol: str = "http",
|
|
141
|
+
inject_client_pause: int = 0,
|
|
142
|
+
connection: SnowflakeConnection | None = None,
|
|
143
|
+
session_manager: SessionManager | None = None,
|
|
144
|
+
):
|
|
145
|
+
super().__init__(host, port, protocol, inject_client_pause, connection)
|
|
146
|
+
self._lock_token = asyncio.Lock()
|
|
147
|
+
|
|
148
|
+
if session_manager is None:
|
|
149
|
+
session_manager = (
|
|
150
|
+
connection._session_manager
|
|
151
|
+
if (connection and connection._session_manager)
|
|
152
|
+
else SessionManagerFactory.get_manager(
|
|
153
|
+
connector_factory=SnowflakeSSLConnectorFactory()
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
self._session_manager = session_manager
|
|
157
|
+
|
|
158
|
+
async def close(self) -> None:
|
|
159
|
+
if hasattr(self, "_token"):
|
|
160
|
+
del self._token
|
|
161
|
+
if hasattr(self, "_master_token"):
|
|
162
|
+
del self._master_token
|
|
163
|
+
if hasattr(self, "_id_token"):
|
|
164
|
+
del self._id_token
|
|
165
|
+
if hasattr(self, "_mfa_token"):
|
|
166
|
+
del self._mfa_token
|
|
167
|
+
|
|
168
|
+
await self._session_manager.close()
|
|
169
|
+
|
|
170
|
+
async def request(
|
|
171
|
+
self,
|
|
172
|
+
url,
|
|
173
|
+
body=None,
|
|
174
|
+
method: str = "post",
|
|
175
|
+
client: str = "sfsql",
|
|
176
|
+
timeout: int | None = None,
|
|
177
|
+
_no_results: bool = False,
|
|
178
|
+
_include_retry_params: bool = False,
|
|
179
|
+
_no_retry: bool = False,
|
|
180
|
+
):
|
|
181
|
+
# log to reflect vendored.urllib3.connectionpool:connectionpool.py:474
|
|
182
|
+
logger.debug("%s %s", method.upper(), url)
|
|
183
|
+
if body is None:
|
|
184
|
+
body = {}
|
|
185
|
+
if self.master_token is None and self.token is None:
|
|
186
|
+
Error.errorhandler_wrapper(
|
|
187
|
+
self._connection,
|
|
188
|
+
None,
|
|
189
|
+
DatabaseError,
|
|
190
|
+
{
|
|
191
|
+
"msg": "Connection is closed",
|
|
192
|
+
"errno": ER_CONNECTION_IS_CLOSED,
|
|
193
|
+
"sqlstate": SQLSTATE_CONNECTION_NOT_EXISTS,
|
|
194
|
+
},
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
if client == "sfsql":
|
|
198
|
+
accept_type = ACCEPT_TYPE_APPLICATION_SNOWFLAKE
|
|
199
|
+
else:
|
|
200
|
+
accept_type = CONTENT_TYPE_APPLICATION_JSON
|
|
201
|
+
|
|
202
|
+
headers = {
|
|
203
|
+
HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_APPLICATION_JSON,
|
|
204
|
+
HTTP_HEADER_ACCEPT: accept_type,
|
|
205
|
+
HTTP_HEADER_USER_AGENT: PYTHON_CONNECTOR_USER_AGENT,
|
|
206
|
+
}
|
|
207
|
+
try:
|
|
208
|
+
# SNOW-1763555: inject OpenTelemetry headers if available specifically in WC3 format
|
|
209
|
+
# into our request headers in case tracing is enabled. This should make sure that
|
|
210
|
+
# our requests are accounted for properly if OpenTelemetry is used by users.
|
|
211
|
+
from opentelemetry.trace.propagation.tracecontext import (
|
|
212
|
+
TraceContextTextMapPropagator,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
TraceContextTextMapPropagator().inject(headers)
|
|
216
|
+
except Exception:
|
|
217
|
+
logger.debug(
|
|
218
|
+
"Opentelemtry otel injection failed",
|
|
219
|
+
exc_info=True,
|
|
220
|
+
)
|
|
221
|
+
if self._connection.service_name:
|
|
222
|
+
headers[HTTP_HEADER_SERVICE_NAME] = self._connection.service_name
|
|
223
|
+
if method == "post":
|
|
224
|
+
return await self._post_request(
|
|
225
|
+
url,
|
|
226
|
+
headers,
|
|
227
|
+
json.dumps(body, cls=SnowflakeRestfulJsonEncoder),
|
|
228
|
+
token=self.token,
|
|
229
|
+
_no_results=_no_results,
|
|
230
|
+
timeout=timeout,
|
|
231
|
+
_include_retry_params=_include_retry_params,
|
|
232
|
+
no_retry=_no_retry,
|
|
233
|
+
)
|
|
234
|
+
else:
|
|
235
|
+
return await self._get_request(
|
|
236
|
+
url,
|
|
237
|
+
headers,
|
|
238
|
+
token=self.token,
|
|
239
|
+
timeout=timeout,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
async def update_tokens(
|
|
243
|
+
self,
|
|
244
|
+
session_token,
|
|
245
|
+
master_token,
|
|
246
|
+
master_validity_in_seconds=None,
|
|
247
|
+
id_token=None,
|
|
248
|
+
mfa_token=None,
|
|
249
|
+
) -> None:
|
|
250
|
+
"""Updates session and master tokens and optionally temporary credential."""
|
|
251
|
+
async with self._lock_token:
|
|
252
|
+
self._token = session_token
|
|
253
|
+
self._master_token = master_token
|
|
254
|
+
self._id_token = id_token
|
|
255
|
+
self._mfa_token = mfa_token
|
|
256
|
+
self._master_validity_in_seconds = master_validity_in_seconds
|
|
257
|
+
|
|
258
|
+
async def _renew_session(self):
|
|
259
|
+
"""Renew a session and master token."""
|
|
260
|
+
return await self._token_request(REQUEST_TYPE_RENEW)
|
|
261
|
+
|
|
262
|
+
async def _token_request(self, request_type):
|
|
263
|
+
logger.debug(
|
|
264
|
+
"updating session. master_token: {}".format(
|
|
265
|
+
"****" if self.master_token else None
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
headers = {
|
|
269
|
+
HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_APPLICATION_JSON,
|
|
270
|
+
HTTP_HEADER_ACCEPT: CONTENT_TYPE_APPLICATION_JSON,
|
|
271
|
+
HTTP_HEADER_USER_AGENT: PYTHON_CONNECTOR_USER_AGENT,
|
|
272
|
+
}
|
|
273
|
+
if self._connection.service_name:
|
|
274
|
+
headers[HTTP_HEADER_SERVICE_NAME] = self._connection.service_name
|
|
275
|
+
request_id = str(uuid.uuid4())
|
|
276
|
+
logger.debug("request_id: %s", request_id)
|
|
277
|
+
url = "/session/token-request?" + urlencode({REQUEST_ID: request_id})
|
|
278
|
+
|
|
279
|
+
# NOTE: ensure an empty key if master token is not set.
|
|
280
|
+
# This avoids HTTP 400.
|
|
281
|
+
header_token = self.master_token or ""
|
|
282
|
+
body = {
|
|
283
|
+
"oldSessionToken": self.token,
|
|
284
|
+
"requestType": request_type,
|
|
285
|
+
}
|
|
286
|
+
ret = await self._post_request(
|
|
287
|
+
url,
|
|
288
|
+
headers,
|
|
289
|
+
json.dumps(body, cls=SnowflakeRestfulJsonEncoder),
|
|
290
|
+
token=header_token,
|
|
291
|
+
)
|
|
292
|
+
if ret.get("success") and ret.get("data", {}).get("sessionToken"):
|
|
293
|
+
logger.debug("success: %s", SecretDetector.mask_secrets(str(ret)))
|
|
294
|
+
await self.update_tokens(
|
|
295
|
+
ret["data"]["sessionToken"],
|
|
296
|
+
ret["data"].get("masterToken"),
|
|
297
|
+
master_validity_in_seconds=ret["data"].get("masterValidityInSeconds"),
|
|
298
|
+
)
|
|
299
|
+
logger.debug("updating session completed")
|
|
300
|
+
return ret
|
|
301
|
+
else:
|
|
302
|
+
logger.debug("failed: %s", SecretDetector.mask_secrets(str(ret)))
|
|
303
|
+
err = ret.get("message")
|
|
304
|
+
if err is not None and ret.get("data"):
|
|
305
|
+
err += ret["data"].get("errorMessage", "")
|
|
306
|
+
errno = ret.get("code") or ER_FAILED_TO_RENEW_SESSION
|
|
307
|
+
if errno in (
|
|
308
|
+
ID_TOKEN_EXPIRED_GS_CODE,
|
|
309
|
+
SESSION_EXPIRED_GS_CODE,
|
|
310
|
+
MASTER_TOKEN_NOTFOUND_GS_CODE,
|
|
311
|
+
MASTER_TOKEN_EXPIRED_GS_CODE,
|
|
312
|
+
MASTER_TOKEN_INVALD_GS_CODE,
|
|
313
|
+
BAD_REQUEST_GS_CODE,
|
|
314
|
+
):
|
|
315
|
+
raise ReauthenticationRequest(
|
|
316
|
+
ProgrammingError(
|
|
317
|
+
msg=err,
|
|
318
|
+
errno=int(errno),
|
|
319
|
+
sqlstate=SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
|
|
320
|
+
)
|
|
321
|
+
)
|
|
322
|
+
Error.errorhandler_wrapper(
|
|
323
|
+
self._connection,
|
|
324
|
+
None,
|
|
325
|
+
ProgrammingError,
|
|
326
|
+
{
|
|
327
|
+
"msg": err,
|
|
328
|
+
"errno": int(errno),
|
|
329
|
+
"sqlstate": SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
|
|
330
|
+
},
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
async def _heartbeat(self) -> Any | dict[Any, Any] | None:
|
|
334
|
+
headers = {
|
|
335
|
+
HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_APPLICATION_JSON,
|
|
336
|
+
HTTP_HEADER_ACCEPT: CONTENT_TYPE_APPLICATION_JSON,
|
|
337
|
+
HTTP_HEADER_USER_AGENT: PYTHON_CONNECTOR_USER_AGENT,
|
|
338
|
+
}
|
|
339
|
+
if self._connection.service_name:
|
|
340
|
+
headers[HTTP_HEADER_SERVICE_NAME] = self._connection.service_name
|
|
341
|
+
request_id = str(uuid.uuid4())
|
|
342
|
+
logger.debug("request_id: %s", request_id)
|
|
343
|
+
url = "/session/heartbeat?" + urlencode({REQUEST_ID: request_id})
|
|
344
|
+
ret = await self._post_request(
|
|
345
|
+
url,
|
|
346
|
+
headers,
|
|
347
|
+
None,
|
|
348
|
+
token=self.token,
|
|
349
|
+
)
|
|
350
|
+
if not ret.get("success"):
|
|
351
|
+
logger.error("Failed to heartbeat. code: %s, url: %s", ret.get("code"), url)
|
|
352
|
+
return ret
|
|
353
|
+
|
|
354
|
+
async def delete_session(self, retry: bool = False) -> None:
|
|
355
|
+
"""Deletes the session."""
|
|
356
|
+
if self.master_token is None:
|
|
357
|
+
Error.errorhandler_wrapper(
|
|
358
|
+
self._connection,
|
|
359
|
+
None,
|
|
360
|
+
DatabaseError,
|
|
361
|
+
{
|
|
362
|
+
"msg": "Connection is closed",
|
|
363
|
+
"errno": ER_CONNECTION_IS_CLOSED,
|
|
364
|
+
"sqlstate": SQLSTATE_CONNECTION_NOT_EXISTS,
|
|
365
|
+
},
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
url = "/session?" + urlencode({"delete": "true"})
|
|
369
|
+
headers = {
|
|
370
|
+
HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_APPLICATION_JSON,
|
|
371
|
+
HTTP_HEADER_ACCEPT: CONTENT_TYPE_APPLICATION_JSON,
|
|
372
|
+
HTTP_HEADER_USER_AGENT: PYTHON_CONNECTOR_USER_AGENT,
|
|
373
|
+
}
|
|
374
|
+
if self._connection.service_name:
|
|
375
|
+
headers[HTTP_HEADER_SERVICE_NAME] = self._connection.service_name
|
|
376
|
+
|
|
377
|
+
body = {}
|
|
378
|
+
retry_limit = 3 if retry else 1
|
|
379
|
+
num_retries = 0
|
|
380
|
+
should_retry = True
|
|
381
|
+
while should_retry and (num_retries < retry_limit):
|
|
382
|
+
try:
|
|
383
|
+
should_retry = False
|
|
384
|
+
ret = await self._post_request(
|
|
385
|
+
url,
|
|
386
|
+
headers,
|
|
387
|
+
json.dumps(body, cls=SnowflakeRestfulJsonEncoder),
|
|
388
|
+
token=self.token,
|
|
389
|
+
timeout=5,
|
|
390
|
+
no_retry=True,
|
|
391
|
+
)
|
|
392
|
+
if not ret:
|
|
393
|
+
if retry:
|
|
394
|
+
should_retry = True
|
|
395
|
+
else:
|
|
396
|
+
return
|
|
397
|
+
elif ret.get("success"):
|
|
398
|
+
return
|
|
399
|
+
err = ret.get("message")
|
|
400
|
+
if err is not None and ret.get("data"):
|
|
401
|
+
err += ret["data"].get("errorMessage", "")
|
|
402
|
+
# no exception is raised
|
|
403
|
+
logger.debug("error in deleting session. ignoring...: %s", err)
|
|
404
|
+
except Exception as e:
|
|
405
|
+
logger.debug("error in deleting session. ignoring...: %s", e)
|
|
406
|
+
finally:
|
|
407
|
+
num_retries += 1
|
|
408
|
+
|
|
409
|
+
async def _get_request(
|
|
410
|
+
self,
|
|
411
|
+
url: str,
|
|
412
|
+
headers: dict[str, str],
|
|
413
|
+
token: str = None,
|
|
414
|
+
timeout: int | None = None,
|
|
415
|
+
is_fetch_query_status: bool = False,
|
|
416
|
+
) -> dict[str, Any]:
|
|
417
|
+
if "Content-Encoding" in headers:
|
|
418
|
+
del headers["Content-Encoding"]
|
|
419
|
+
if "Content-Length" in headers:
|
|
420
|
+
del headers["Content-Length"]
|
|
421
|
+
|
|
422
|
+
full_url = f"{self.server_url}{url}"
|
|
423
|
+
ret = await self.fetch(
|
|
424
|
+
"get",
|
|
425
|
+
full_url,
|
|
426
|
+
headers,
|
|
427
|
+
timeout=timeout,
|
|
428
|
+
token=token,
|
|
429
|
+
is_fetch_query_status=is_fetch_query_status,
|
|
430
|
+
)
|
|
431
|
+
if ret.get("code") == SESSION_EXPIRED_GS_CODE:
|
|
432
|
+
try:
|
|
433
|
+
ret = await self._renew_session()
|
|
434
|
+
except ReauthenticationRequest as ex:
|
|
435
|
+
if self._connection._authenticator != EXTERNAL_BROWSER_AUTHENTICATOR:
|
|
436
|
+
raise ex.cause
|
|
437
|
+
ret = await self._connection._reauthenticate()
|
|
438
|
+
logger.debug(
|
|
439
|
+
"ret[code] = {code} after renew_session".format(
|
|
440
|
+
code=(ret.get("code", "N/A"))
|
|
441
|
+
)
|
|
442
|
+
)
|
|
443
|
+
if ret.get("success"):
|
|
444
|
+
return await self._get_request(
|
|
445
|
+
url,
|
|
446
|
+
headers,
|
|
447
|
+
token=self.token,
|
|
448
|
+
is_fetch_query_status=is_fetch_query_status,
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
return ret
|
|
452
|
+
|
|
453
|
+
async def _post_request(
|
|
454
|
+
self,
|
|
455
|
+
url,
|
|
456
|
+
headers,
|
|
457
|
+
body,
|
|
458
|
+
token=None,
|
|
459
|
+
timeout: int | None = None,
|
|
460
|
+
socket_timeout: int | None = None,
|
|
461
|
+
_no_results: bool = False,
|
|
462
|
+
no_retry: bool = False,
|
|
463
|
+
_include_retry_params: bool = False,
|
|
464
|
+
) -> dict[str, Any]:
|
|
465
|
+
full_url = f"{self.server_url}{url}"
|
|
466
|
+
if self._connection._probe_connection:
|
|
467
|
+
# TODO: SNOW-1572318 for probe connection
|
|
468
|
+
raise NotImplementedError("probe_connection is not supported in asyncio")
|
|
469
|
+
|
|
470
|
+
ret = await self.fetch(
|
|
471
|
+
"post",
|
|
472
|
+
full_url,
|
|
473
|
+
headers,
|
|
474
|
+
data=body,
|
|
475
|
+
timeout=timeout,
|
|
476
|
+
token=token,
|
|
477
|
+
no_retry=no_retry,
|
|
478
|
+
_include_retry_params=_include_retry_params,
|
|
479
|
+
socket_timeout=socket_timeout,
|
|
480
|
+
)
|
|
481
|
+
logger.debug(
|
|
482
|
+
"ret[code] = {code}, after post request".format(
|
|
483
|
+
code=(ret.get("code", "N/A"))
|
|
484
|
+
)
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
if ret.get("code") == MASTER_TOKEN_EXPIRED_GS_CODE:
|
|
488
|
+
self._connection.expired = True
|
|
489
|
+
elif ret.get("code") == SESSION_EXPIRED_GS_CODE:
|
|
490
|
+
try:
|
|
491
|
+
ret = await self._renew_session()
|
|
492
|
+
except ReauthenticationRequest as ex:
|
|
493
|
+
if self._connection._authenticator != EXTERNAL_BROWSER_AUTHENTICATOR:
|
|
494
|
+
raise ex.cause
|
|
495
|
+
ret = await self._connection._reauthenticate()
|
|
496
|
+
logger.debug(
|
|
497
|
+
"ret[code] = {code} after renew_session".format(
|
|
498
|
+
code=(ret.get("code", "N/A"))
|
|
499
|
+
)
|
|
500
|
+
)
|
|
501
|
+
if ret.get("success"):
|
|
502
|
+
return await self._post_request(
|
|
503
|
+
url, headers, body, token=self.token, timeout=timeout
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
if isinstance(ret.get("data"), dict) and ret["data"].get("queryId"):
|
|
507
|
+
logger.debug("Query id: {}".format(ret["data"]["queryId"]))
|
|
508
|
+
|
|
509
|
+
if ret.get("code") == QUERY_IN_PROGRESS_ASYNC_CODE and _no_results:
|
|
510
|
+
return ret
|
|
511
|
+
|
|
512
|
+
while ret.get("code") in (QUERY_IN_PROGRESS_CODE, QUERY_IN_PROGRESS_ASYNC_CODE):
|
|
513
|
+
if self._inject_client_pause > 0:
|
|
514
|
+
logger.debug("waiting for %s...", self._inject_client_pause)
|
|
515
|
+
await asyncio.sleep(self._inject_client_pause)
|
|
516
|
+
# ping pong
|
|
517
|
+
result_url = ret["data"]["getResultUrl"]
|
|
518
|
+
logger.debug("ping pong starting...")
|
|
519
|
+
ret = await self._get_request(
|
|
520
|
+
result_url,
|
|
521
|
+
headers,
|
|
522
|
+
token=self.token,
|
|
523
|
+
timeout=timeout,
|
|
524
|
+
is_fetch_query_status=bool(
|
|
525
|
+
re.match(r"^/queries/.+/result$", result_url)
|
|
526
|
+
),
|
|
527
|
+
)
|
|
528
|
+
logger.debug("ret[code] = %s", ret.get("code", "N/A"))
|
|
529
|
+
logger.debug("ping pong done")
|
|
530
|
+
|
|
531
|
+
return ret
|
|
532
|
+
|
|
533
|
+
async def fetch(
|
|
534
|
+
self,
|
|
535
|
+
method: str,
|
|
536
|
+
full_url: str,
|
|
537
|
+
headers: dict[str, Any],
|
|
538
|
+
data: dict[str, Any] | None = None,
|
|
539
|
+
timeout: int | None = None,
|
|
540
|
+
**kwargs,
|
|
541
|
+
) -> dict[Any, Any]:
|
|
542
|
+
"""Carry out API request with session management."""
|
|
543
|
+
|
|
544
|
+
class RetryCtx(TimeoutBackoffCtx):
|
|
545
|
+
def __init__(
|
|
546
|
+
self,
|
|
547
|
+
_include_retry_params: bool = False,
|
|
548
|
+
_include_retry_reason: bool = False,
|
|
549
|
+
**kwargs,
|
|
550
|
+
) -> None:
|
|
551
|
+
super().__init__(**kwargs)
|
|
552
|
+
self.retry_reason = 0
|
|
553
|
+
self._include_retry_params = _include_retry_params
|
|
554
|
+
self._include_retry_reason = _include_retry_reason
|
|
555
|
+
|
|
556
|
+
def add_retry_params(self, full_url: str) -> str:
|
|
557
|
+
if self._include_retry_params and self.current_retry_count > 0:
|
|
558
|
+
retry_params = {
|
|
559
|
+
"clientStartTime": self._start_time_millis,
|
|
560
|
+
"retryCount": self.current_retry_count,
|
|
561
|
+
}
|
|
562
|
+
if self._include_retry_reason:
|
|
563
|
+
retry_params.update({"retryReason": self.retry_reason})
|
|
564
|
+
suffix = urlencode(retry_params)
|
|
565
|
+
sep = "&" if urlparse(full_url).query else "?"
|
|
566
|
+
return full_url + sep + suffix
|
|
567
|
+
else:
|
|
568
|
+
return full_url
|
|
569
|
+
|
|
570
|
+
include_retry_reason = self._connection._enable_retry_reason_in_query_response
|
|
571
|
+
include_retry_params = kwargs.pop("_include_retry_params", False)
|
|
572
|
+
|
|
573
|
+
async with self.use_session(full_url) as session:
|
|
574
|
+
retry_ctx = RetryCtx(
|
|
575
|
+
_include_retry_params=include_retry_params,
|
|
576
|
+
_include_retry_reason=include_retry_reason,
|
|
577
|
+
timeout=(
|
|
578
|
+
timeout if timeout is not None else self._connection.network_timeout
|
|
579
|
+
),
|
|
580
|
+
backoff_generator=self._connection._backoff_generator,
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
retry_ctx.set_start_time()
|
|
584
|
+
while True:
|
|
585
|
+
ret = await self._request_exec_wrapper(
|
|
586
|
+
session, method, full_url, headers, data, retry_ctx, **kwargs
|
|
587
|
+
)
|
|
588
|
+
if ret is not None:
|
|
589
|
+
return ret
|
|
590
|
+
|
|
591
|
+
async def _request_exec_wrapper(
|
|
592
|
+
self,
|
|
593
|
+
session,
|
|
594
|
+
method,
|
|
595
|
+
full_url,
|
|
596
|
+
headers,
|
|
597
|
+
data,
|
|
598
|
+
retry_ctx,
|
|
599
|
+
no_retry: bool = False,
|
|
600
|
+
token=NO_TOKEN,
|
|
601
|
+
**kwargs,
|
|
602
|
+
):
|
|
603
|
+
conn = self._connection
|
|
604
|
+
logger.debug(
|
|
605
|
+
"remaining request timeout: %s ms, retry cnt: %s",
|
|
606
|
+
retry_ctx.remaining_time_millis if retry_ctx.timeout is not None else "N/A",
|
|
607
|
+
retry_ctx.current_retry_count + 1,
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
full_url = retry_ctx.add_retry_params(full_url)
|
|
611
|
+
full_url = SnowflakeRestful.add_request_guid(full_url)
|
|
612
|
+
is_fetch_query_status = kwargs.pop("is_fetch_query_status", False)
|
|
613
|
+
try:
|
|
614
|
+
return_object = await self._request_exec(
|
|
615
|
+
session=session,
|
|
616
|
+
method=method,
|
|
617
|
+
full_url=full_url,
|
|
618
|
+
headers=headers,
|
|
619
|
+
data=data,
|
|
620
|
+
token=token,
|
|
621
|
+
**kwargs,
|
|
622
|
+
)
|
|
623
|
+
if return_object is not None:
|
|
624
|
+
return return_object
|
|
625
|
+
if is_fetch_query_status:
|
|
626
|
+
err_msg = (
|
|
627
|
+
"fetch query status failed and http request returned None, this"
|
|
628
|
+
" is usually caused by transient network failures, retrying..."
|
|
629
|
+
)
|
|
630
|
+
logger.info(err_msg)
|
|
631
|
+
raise RetryRequest(err_msg)
|
|
632
|
+
self._handle_unknown_error(method, full_url, headers, data, conn)
|
|
633
|
+
return {}
|
|
634
|
+
except RevocationCheckError as rce:
|
|
635
|
+
rce.exception_telemetry(rce.msg, None, self._connection)
|
|
636
|
+
raise rce
|
|
637
|
+
except RetryRequest as e:
|
|
638
|
+
cause = e.args[0]
|
|
639
|
+
if no_retry:
|
|
640
|
+
self.log_and_handle_http_error_with_cause(
|
|
641
|
+
e,
|
|
642
|
+
full_url,
|
|
643
|
+
method,
|
|
644
|
+
retry_ctx.timeout,
|
|
645
|
+
retry_ctx.current_retry_count,
|
|
646
|
+
conn,
|
|
647
|
+
timed_out=False,
|
|
648
|
+
)
|
|
649
|
+
return {} # required for tests
|
|
650
|
+
if not retry_ctx.should_retry:
|
|
651
|
+
self.log_and_handle_http_error_with_cause(
|
|
652
|
+
e,
|
|
653
|
+
full_url,
|
|
654
|
+
method,
|
|
655
|
+
retry_ctx.timeout,
|
|
656
|
+
retry_ctx.current_retry_count,
|
|
657
|
+
conn,
|
|
658
|
+
)
|
|
659
|
+
return {} # required for tests
|
|
660
|
+
|
|
661
|
+
logger.debug(
|
|
662
|
+
"retrying: errorclass=%s, "
|
|
663
|
+
"error=%s, "
|
|
664
|
+
"counter=%s, "
|
|
665
|
+
"sleeping=%s(s)",
|
|
666
|
+
type(cause),
|
|
667
|
+
cause,
|
|
668
|
+
retry_ctx.current_retry_count + 1,
|
|
669
|
+
retry_ctx.current_sleep_time,
|
|
670
|
+
)
|
|
671
|
+
await asyncio.sleep(float(retry_ctx.current_sleep_time))
|
|
672
|
+
retry_ctx.increment()
|
|
673
|
+
|
|
674
|
+
reason = getattr(cause, "errno", 0)
|
|
675
|
+
if reason is None:
|
|
676
|
+
reason = 0
|
|
677
|
+
else:
|
|
678
|
+
reason = (
|
|
679
|
+
reason - ER_HTTP_GENERAL_ERROR
|
|
680
|
+
if reason >= ER_HTTP_GENERAL_ERROR
|
|
681
|
+
else reason
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
retry_ctx.retry_reason = reason
|
|
685
|
+
# notes: in sync implementation we check ECONNRESET in error message and close low level urllib session
|
|
686
|
+
# we do not have the logic here because aiohttp handles low level connection close-reopen for us
|
|
687
|
+
return None # retry
|
|
688
|
+
except Exception as e:
|
|
689
|
+
if not no_retry:
|
|
690
|
+
raise e
|
|
691
|
+
logger.debug("Ignored error", exc_info=True)
|
|
692
|
+
return {}
|
|
693
|
+
|
|
694
|
+
async def _request_exec(
|
|
695
|
+
self,
|
|
696
|
+
session: aiohttp.ClientSession,
|
|
697
|
+
method,
|
|
698
|
+
full_url,
|
|
699
|
+
headers,
|
|
700
|
+
data,
|
|
701
|
+
token,
|
|
702
|
+
catch_okta_unauthorized_error: bool = False,
|
|
703
|
+
is_raw_text: bool = False,
|
|
704
|
+
is_raw_binary: bool = False,
|
|
705
|
+
binary_data_handler=None,
|
|
706
|
+
socket_timeout: int | None = None,
|
|
707
|
+
is_okta_authentication: bool = False,
|
|
708
|
+
):
|
|
709
|
+
if socket_timeout is None:
|
|
710
|
+
if self._connection.socket_timeout is not None:
|
|
711
|
+
logger.debug("socket_timeout specified in connection")
|
|
712
|
+
socket_timeout = self._connection.socket_timeout
|
|
713
|
+
else:
|
|
714
|
+
socket_timeout = DEFAULT_SOCKET_CONNECT_TIMEOUT
|
|
715
|
+
logger.debug("socket timeout: %s", socket_timeout)
|
|
716
|
+
|
|
717
|
+
try:
|
|
718
|
+
if not catch_okta_unauthorized_error and data and len(data) > 0:
|
|
719
|
+
headers["Content-Encoding"] = "gzip"
|
|
720
|
+
input_data = gzip.compress(data.encode("utf-8"))
|
|
721
|
+
else:
|
|
722
|
+
input_data = data
|
|
723
|
+
|
|
724
|
+
if HEADER_AUTHORIZATION_KEY in headers:
|
|
725
|
+
del headers[HEADER_AUTHORIZATION_KEY]
|
|
726
|
+
if token != NO_TOKEN:
|
|
727
|
+
headers[HEADER_AUTHORIZATION_KEY] = HEADER_SNOWFLAKE_TOKEN.format(
|
|
728
|
+
token=token
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
# socket timeout is constant. You should be able to receive
|
|
732
|
+
# the response within the time. If not, asyncio.TimeoutError is raised.
|
|
733
|
+
|
|
734
|
+
# delta compared to sync:
|
|
735
|
+
# - in sync, we specify "verify" to True; in aiohttp,
|
|
736
|
+
# the counter parameter is "ssl" and it already defaults to True
|
|
737
|
+
raw_ret = await session.request(
|
|
738
|
+
method=method,
|
|
739
|
+
url=full_url,
|
|
740
|
+
headers=headers,
|
|
741
|
+
data=input_data,
|
|
742
|
+
timeout=aiohttp.ClientTimeout(socket_timeout),
|
|
743
|
+
)
|
|
744
|
+
try:
|
|
745
|
+
if raw_ret.status == OK:
|
|
746
|
+
logger.debug("SUCCESS")
|
|
747
|
+
if is_raw_text:
|
|
748
|
+
ret = await raw_ret.text()
|
|
749
|
+
elif is_raw_binary:
|
|
750
|
+
# TODO: SNOW-1738595 for is_raw_binary support
|
|
751
|
+
raise NotImplementedError(
|
|
752
|
+
"reading raw binary data is not supported in asyncio connector,"
|
|
753
|
+
" please open a feature request issue in"
|
|
754
|
+
" github: https://github.com/snowflakedb/snowflake-connector-python/issues/new/choose"
|
|
755
|
+
)
|
|
756
|
+
else:
|
|
757
|
+
ret = await raw_ret.json()
|
|
758
|
+
return ret
|
|
759
|
+
|
|
760
|
+
if is_login_request(full_url) and raw_ret.status == FORBIDDEN:
|
|
761
|
+
raise ForbiddenError
|
|
762
|
+
|
|
763
|
+
elif is_retryable_http_code(raw_ret.status):
|
|
764
|
+
err = get_http_retryable_error(raw_ret.status)
|
|
765
|
+
# retryable server exceptions
|
|
766
|
+
if is_okta_authentication:
|
|
767
|
+
raise RefreshTokenError(
|
|
768
|
+
msg="OKTA authentication requires token refresh."
|
|
769
|
+
)
|
|
770
|
+
if is_login_request(full_url):
|
|
771
|
+
logger.debug(
|
|
772
|
+
"Received retryable response code while logging in. Will be handled by "
|
|
773
|
+
f"authenticator. Ignore the following. Error stack: {err}",
|
|
774
|
+
exc_info=True,
|
|
775
|
+
)
|
|
776
|
+
raise OperationalError(
|
|
777
|
+
msg="Login request is retryable. Will be handled by authenticator",
|
|
778
|
+
errno=ER_RETRYABLE_CODE,
|
|
779
|
+
)
|
|
780
|
+
else:
|
|
781
|
+
logger.debug(f"{err}. Retrying...")
|
|
782
|
+
raise RetryRequest(err)
|
|
783
|
+
|
|
784
|
+
elif raw_ret.status == UNAUTHORIZED and catch_okta_unauthorized_error:
|
|
785
|
+
# OKTA Unauthorized errors
|
|
786
|
+
raise_okta_unauthorized_error(self._connection, raw_ret)
|
|
787
|
+
return None # required for tests
|
|
788
|
+
else:
|
|
789
|
+
raise_failed_request_error(
|
|
790
|
+
self._connection, full_url, method, raw_ret
|
|
791
|
+
)
|
|
792
|
+
return None # required for tests
|
|
793
|
+
finally:
|
|
794
|
+
raw_ret.close() # ensure response is closed
|
|
795
|
+
except (aiohttp.ClientSSLError, aiohttp.ClientConnectorSSLError) as se:
|
|
796
|
+
if is_econnreset_exception(se):
|
|
797
|
+
raise RetryRequest(se.os_error)
|
|
798
|
+
msg = f"Hit non-retryable SSL error, {str(se)}.\n{_CONNECTIVITY_ERR_MSG}"
|
|
799
|
+
logger.debug(msg)
|
|
800
|
+
# the following code is for backward compatibility with old versions of python connector which calls
|
|
801
|
+
# self._handle_unknown_error to process SSLError
|
|
802
|
+
Error.errorhandler_wrapper(
|
|
803
|
+
self._connection,
|
|
804
|
+
None,
|
|
805
|
+
OperationalError,
|
|
806
|
+
{
|
|
807
|
+
"msg": msg,
|
|
808
|
+
"errno": ER_FAILED_TO_REQUEST,
|
|
809
|
+
},
|
|
810
|
+
)
|
|
811
|
+
except (
|
|
812
|
+
aiohttp.ClientConnectionError,
|
|
813
|
+
aiohttp.ClientConnectorError,
|
|
814
|
+
aiohttp.ConnectionTimeoutError,
|
|
815
|
+
asyncio.TimeoutError,
|
|
816
|
+
OpenSSL.SSL.SysCallError,
|
|
817
|
+
KeyError, # SNOW-39175: asn1crypto.keys.PublicKeyInfo
|
|
818
|
+
ValueError,
|
|
819
|
+
RuntimeError,
|
|
820
|
+
AttributeError, # json decoding error
|
|
821
|
+
) as err:
|
|
822
|
+
if isinstance(err, RuntimeError) and "Event loop is closed" in str(err):
|
|
823
|
+
logger.info(
|
|
824
|
+
"If you see the logging error message 'RuntimeError: Event loop is closed' during program exit, it probably indicates that the connection was not closed properly before the event loop was shut down. Please use SnowflakeConnection.close() to close connection."
|
|
825
|
+
)
|
|
826
|
+
raise err
|
|
827
|
+
if is_login_request(full_url):
|
|
828
|
+
logger.debug(
|
|
829
|
+
"Hit a timeout error while logging in. Will be handled by "
|
|
830
|
+
f"authenticator. Ignore the following. Error stack: {err}",
|
|
831
|
+
exc_info=True,
|
|
832
|
+
)
|
|
833
|
+
raise OperationalError(
|
|
834
|
+
msg="ConnectionTimeout occurred during login. Will be handled by authenticator",
|
|
835
|
+
errno=ER_CONNECTION_TIMEOUT,
|
|
836
|
+
)
|
|
837
|
+
else:
|
|
838
|
+
logger.debug(
|
|
839
|
+
"Hit retryable client error. Retrying... Ignore the following "
|
|
840
|
+
f"error stack: {err}",
|
|
841
|
+
exc_info=True,
|
|
842
|
+
)
|
|
843
|
+
raise RetryRequest(err)
|
|
844
|
+
except Exception as err:
|
|
845
|
+
if isinstance(err, (Error, RetryRequest, ReauthenticationRequest)):
|
|
846
|
+
raise err
|
|
847
|
+
raise OperationalError(
|
|
848
|
+
msg=f"Unexpected error occurred during request execution: {err}"
|
|
849
|
+
"Please check the stack trace for more information and retry the operation."
|
|
850
|
+
"If you think this is a bug, please collect the error information and open a bug report in github: "
|
|
851
|
+
"https://github.com/snowflakedb/snowflake-connector-python/issues/new/choose.",
|
|
852
|
+
errno=ER_FAILED_TO_REQUEST,
|
|
853
|
+
) from err
|
|
854
|
+
|
|
855
|
+
@contextlib.asynccontextmanager
|
|
856
|
+
async def use_session(
|
|
857
|
+
self, url: str | None = None
|
|
858
|
+
) -> AsyncGenerator[aiohttp.ClientSession]:
|
|
859
|
+
async with self._session_manager.use_session(url) as session:
|
|
860
|
+
yield session
|