snowflake-connector-python 5.2.0__cp315-cp315-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 +148 -0
- snowflake/connector/_common/__init__.py +0 -0
- snowflake/connector/_common/extras.py +134 -0
- snowflake/connector/_common/telemetry.py +221 -0
- snowflake/connector/_common/wif_util.py +92 -0
- snowflake/connector/_core/sf_core_python.cp315-win_amd64.pyd +0 -0
- snowflake/connector/_core/sf_core_python.pyi +79 -0
- snowflake/connector/_internal/__init__.py +6 -0
- snowflake/connector/_internal/_private_key_helper.py +31 -0
- snowflake/connector/_internal/api_client/__init__.py +93 -0
- snowflake/connector/_internal/api_client/bridge.py +41 -0
- snowflake/connector/_internal/api_client/client_api.py +976 -0
- snowflake/connector/_internal/arrow.py +47 -0
- snowflake/connector/_internal/arrow_context.py +240 -0
- snowflake/connector/_internal/arrow_stream_async.py +266 -0
- snowflake/connector/_internal/arrow_stream_iterator.cp311-win_amd64.pyd +0 -0
- snowflake/connector/_internal/arrow_stream_iterator.cp312-win_amd64.pyd +0 -0
- snowflake/connector/_internal/arrow_stream_iterator.cp313-win_amd64.pyd +0 -0
- snowflake/connector/_internal/arrow_stream_iterator.cp314-win_amd64.pyd +0 -0
- snowflake/connector/_internal/arrow_stream_iterator.cp314t-win_amd64.pyd +0 -0
- snowflake/connector/_internal/arrow_stream_iterator.cp315-win_amd64.pyd +0 -0
- snowflake/connector/_internal/arrow_stream_iterator.pyi +42 -0
- snowflake/connector/_internal/arrow_stream_utils.py +171 -0
- snowflake/connector/_internal/authenticator_type.py +53 -0
- snowflake/connector/_internal/backward_compatibility.py +245 -0
- snowflake/connector/_internal/binding_converters.py +741 -0
- snowflake/connector/_internal/config_utils.py +71 -0
- snowflake/connector/_internal/connection/__init__.py +39 -0
- snowflake/connector/_internal/connection/connection.py +616 -0
- snowflake/connector/_internal/connection/connection_types.py +13 -0
- snowflake/connector/_internal/connection/constants.py +25 -0
- snowflake/connector/_internal/connection/decorators.py +31 -0
- snowflake/connector/_internal/connection/freezable_proxy.py +104 -0
- snowflake/connector/_internal/connection/queries.py +10 -0
- snowflake/connector/_internal/connection_config_mixin.py +755 -0
- snowflake/connector/_internal/cursor/__init__.py +25 -0
- snowflake/connector/_internal/cursor/base.py +732 -0
- snowflake/connector/_internal/cursor/cursor_types.py +13 -0
- snowflake/connector/_internal/cursor/decorators.py +56 -0
- snowflake/connector/_internal/cursor/query_result.py +202 -0
- snowflake/connector/_internal/cursor/query_result_waiter.py +98 -0
- snowflake/connector/_internal/cursor/result_metadata.py +255 -0
- snowflake/connector/_internal/cursor/result_set_wrapper.py +66 -0
- snowflake/connector/_internal/decorators.py +539 -0
- snowflake/connector/_internal/error_kinds.py +111 -0
- snowflake/connector/_internal/errorcode.py +35 -0
- snowflake/connector/_internal/errorhandler.py +278 -0
- snowflake/connector/_internal/fips.py +28 -0
- snowflake/connector/_internal/logging/__init__.py +52 -0
- snowflake/connector/_internal/logging/config.py +134 -0
- snowflake/connector/_internal/logging/core_logger.py +218 -0
- snowflake/connector/_internal/logging/native_extension_logger.py +144 -0
- snowflake/connector/_internal/logout_config_mapping.py +187 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +57 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrowTableConverter.cpp +943 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrowTableConverter.hpp +63 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +18 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +118 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +116 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamIterator.cpp +323 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamIterator.hpp +133 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamTableIterator.cpp +275 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamTableIterator.hpp +168 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ConverterUtil.cpp +439 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ConverterUtil.hpp +30 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +48 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +76 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +90 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +63 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +62 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +29 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +41 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +65 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +54 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +79 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +52 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +117 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +54 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +34 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +33 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +18 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +39 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +326 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +138 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Util/time.cpp +65 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/arrow_stream_iterator.cpp +13278 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/arrow_stream_iterator.pyx +204 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +109 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +125 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1914 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +208 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +128 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +58 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +144 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +160 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +98 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +243 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +207 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +80 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +83 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +201 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +108 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +164 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +926 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +182 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc.c +3052 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3019 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3354 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +344 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +29305 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +381 -0
- snowflake/connector/_internal/nanoarrow_cpp/Logging/logging.cpp +108 -0
- snowflake/connector/_internal/nanoarrow_cpp/Logging/logging.hpp +47 -0
- snowflake/connector/_internal/nanoarrow_cpp/scripts/.clang-format +8 -0
- snowflake/connector/_internal/nanoarrow_cpp/scripts/format.sh +8 -0
- snowflake/connector/_internal/protobuf_gen/__init__.py +0 -0
- snowflake/connector/_internal/protobuf_gen/database_driver_v1_pb2.py +408 -0
- snowflake/connector/_internal/protobuf_gen/database_driver_v1_pb2.pyi +4487 -0
- snowflake/connector/_internal/protobuf_gen/database_driver_v1_services.py +2088 -0
- snowflake/connector/_internal/protobuf_gen/proto_exception.py +17 -0
- snowflake/connector/_internal/result_batch.py +150 -0
- snowflake/connector/_internal/snowflake_restful.py +222 -0
- snowflake/connector/_internal/snowpark_backcompat_helpers.py +31 -0
- snowflake/connector/_internal/sqlstate.py +11 -0
- snowflake/connector/_internal/statement_utils.py +207 -0
- snowflake/connector/_internal/text_utils.py +112 -0
- snowflake/connector/_internal/type_codes.py +139 -0
- snowflake/connector/_internal/utils.py +65 -0
- snowflake/connector/_internal/write_pandas_operation.py +657 -0
- snowflake/connector/aio/README.md +168 -0
- snowflake/connector/aio/__init__.py +37 -0
- snowflake/connector/aio/connection/__init__.py +17 -0
- snowflake/connector/aio/connection/_connection.py +506 -0
- snowflake/connector/aio/cursor/__init__.py +35 -0
- snowflake/connector/aio/cursor/_base.py +1034 -0
- snowflake/connector/aio/cursor/_dict_cursor.py +78 -0
- snowflake/connector/aio/cursor/_result_set_wrapper.py +35 -0
- snowflake/connector/aio/cursor/_snowflake_cursor.py +72 -0
- snowflake/connector/aio/pandas_tools.py +279 -0
- snowflake/connector/aio/result_batch.py +165 -0
- snowflake/connector/auth/__init__.py +0 -0
- snowflake/connector/auth/workload_identity.py +5 -0
- snowflake/connector/compat.py +22 -0
- snowflake/connector/config_manager.py +392 -0
- snowflake/connector/connection/__init__.py +30 -0
- snowflake/connector/connection/_connection.py +641 -0
- snowflake/connector/connection_config.py +517 -0
- snowflake/connector/constants.py +52 -0
- snowflake/connector/cursor/__init__.py +37 -0
- snowflake/connector/cursor/_base.py +1169 -0
- snowflake/connector/cursor/_chunked_download_reader.py +75 -0
- snowflake/connector/cursor/_dict_cursor.py +78 -0
- snowflake/connector/cursor/_result_set_wrapper.py +35 -0
- snowflake/connector/cursor/_snowflake_cursor.py +72 -0
- snowflake/connector/description.py +7 -0
- snowflake/connector/errors.py +492 -0
- snowflake/connector/network.py +71 -0
- snowflake/connector/pandas_tools.py +191 -0
- snowflake/connector/py.typed +0 -0
- snowflake/connector/result_batch.py +216 -0
- snowflake/connector/telemetry.py +40 -0
- snowflake/connector/time_util.py +11 -0
- snowflake/connector/types.py +134 -0
- snowflake/connector/util_text.py +3 -0
- snowflake/connector/version.py +23 -0
- snowflake_connector_python-5.2.0.dist-info/DELVEWHEEL +2 -0
- snowflake_connector_python-5.2.0.dist-info/METADATA +190 -0
- snowflake_connector_python-5.2.0.dist-info/RECORD +204 -0
- snowflake_connector_python-5.2.0.dist-info/WHEEL +4 -0
- snowflake_connector_python.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
"""PEP 249 Connection implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import atexit
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import platform
|
|
9
|
+
import threading
|
|
10
|
+
import warnings
|
|
11
|
+
|
|
12
|
+
from collections.abc import Callable, Generator, Iterable, Iterator
|
|
13
|
+
from functools import cached_property
|
|
14
|
+
from io import StringIO
|
|
15
|
+
from typing import Any, TypeVar, cast, overload
|
|
16
|
+
|
|
17
|
+
from .._common.telemetry import TelemetryClient as _InternalTelemetryClient
|
|
18
|
+
from .._internal.api_client.client_api import core_driver
|
|
19
|
+
from .._internal.config_utils import create_config_settings_from_dict
|
|
20
|
+
from .._internal.connection import (
|
|
21
|
+
APPLICATION_NAME,
|
|
22
|
+
COMMIT_SQL,
|
|
23
|
+
CURRENT_VERSION_SQL,
|
|
24
|
+
ROLLBACK_SQL,
|
|
25
|
+
SET_AUTOCOMMIT_SQL,
|
|
26
|
+
SET_CLIENT_PREFETCH_THREADS_SQL,
|
|
27
|
+
ConnectionMixin,
|
|
28
|
+
clamp_client_prefetch_threads,
|
|
29
|
+
requires_open,
|
|
30
|
+
validate_query_id,
|
|
31
|
+
)
|
|
32
|
+
from .._internal.connection.freezable_proxy import ConnectionInfoProxy, SessionParametersProxy
|
|
33
|
+
from .._internal.decorators import api_telemetry, backward_compatibility, internal_api, pep249
|
|
34
|
+
from .._internal.errorcode import ER_INVALID_VALUE, ER_INVALID_WIF_SETTINGS
|
|
35
|
+
from .._internal.logging import get_logger
|
|
36
|
+
from .._internal.logout_config_mapping import (
|
|
37
|
+
LogoutOptionKeys,
|
|
38
|
+
logout_config_options_modifier,
|
|
39
|
+
)
|
|
40
|
+
from .._internal.protobuf_gen.database_driver_v1_pb2 import (
|
|
41
|
+
VALIDATION_CODE_CONFLICTING_WIF_PARAMETERS,
|
|
42
|
+
ConnectionHandle,
|
|
43
|
+
DatabaseHandle,
|
|
44
|
+
WrapperIdentity,
|
|
45
|
+
)
|
|
46
|
+
from .._internal.protobuf_gen.database_driver_v1_services import (
|
|
47
|
+
ConnectionGetQueryStatusResponse,
|
|
48
|
+
)
|
|
49
|
+
from .._internal.snowflake_restful import SnowflakeRestful
|
|
50
|
+
from .._internal.text_utils import split_statements
|
|
51
|
+
from .._internal.utils import get_application_path
|
|
52
|
+
from ..connection_config import ConnectionConfig
|
|
53
|
+
from ..constants import QueryStatus
|
|
54
|
+
from ..cursor import CursorInstance, CursorType, DictCursor, SnowflakeCursor, SnowflakeCursorBase
|
|
55
|
+
from ..errors import Error, ProgrammingError
|
|
56
|
+
from ..version import __version__
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
logger = get_logger(__name__)
|
|
60
|
+
|
|
61
|
+
_CursorClsT = TypeVar("_CursorClsT", bound=SnowflakeCursorBase)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# Both WIF cross-param guards in sf_core's validate_settings emit a dedicated
|
|
65
|
+
# ValidationCode, so the wrapper can key on the code alone without matching
|
|
66
|
+
# parameter names or message text.
|
|
67
|
+
def _is_wif_conflict(exc: ProgrammingError) -> bool:
|
|
68
|
+
return exc.validation_code == VALIDATION_CODE_CONFLICTING_WIF_PARAMETERS
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Connection(ConnectionMixin):
|
|
72
|
+
"""Connection objects represent a database connection."""
|
|
73
|
+
|
|
74
|
+
_session_parameters: SessionParametersProxy
|
|
75
|
+
_connection_info: ConnectionInfoProxy
|
|
76
|
+
_default_cursor_class = SnowflakeCursor
|
|
77
|
+
|
|
78
|
+
# ------------------------------------------------------------------
|
|
79
|
+
# Initialization
|
|
80
|
+
# ------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
@api_telemetry
|
|
83
|
+
def __init__(
|
|
84
|
+
self,
|
|
85
|
+
*,
|
|
86
|
+
connection_name: str | None = None,
|
|
87
|
+
connections_file_path: str | os.PathLike[str] | None = None,
|
|
88
|
+
config: ConnectionConfig | None = None,
|
|
89
|
+
**kwargs: Any,
|
|
90
|
+
) -> None:
|
|
91
|
+
"""
|
|
92
|
+
Initialize a new connection object.
|
|
93
|
+
|
|
94
|
+
``connection_name``, ``connections_file_path`` and ``config`` are
|
|
95
|
+
keyword-only to preserve the previous calling contract — the legacy
|
|
96
|
+
``__init__`` was ``(self, **kwargs)`` so any positional argument was
|
|
97
|
+
a ``TypeError``. Keeping these keyword-only guarantees that an
|
|
98
|
+
accidental positional call fails fast instead of being silently
|
|
99
|
+
bound to one of the new parameters.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
connection_name: Named connection to load from TOML configuration files
|
|
103
|
+
connections_file_path: Path to the connections configuration file the named
|
|
104
|
+
profile — or, when no other connection option is given, the default
|
|
105
|
+
profile — is read from, instead of the standard config location
|
|
106
|
+
config: Pre-built ConnectionConfig object (mutually exclusive with kwargs)
|
|
107
|
+
**kwargs: Additional connection parameters
|
|
108
|
+
"""
|
|
109
|
+
super().__init__(
|
|
110
|
+
connection_name=connection_name,
|
|
111
|
+
connections_file_path=connections_file_path,
|
|
112
|
+
config=config,
|
|
113
|
+
**kwargs,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Backward-compat: ``auto_cleanup`` is a Python-only flag controlling whether
|
|
117
|
+
# ``__del__`` / atexit should auto-close a leaked connection. The legacy
|
|
118
|
+
# snowflake-connector-python driver exposed it as ``conn.auto_cleanup`` and
|
|
119
|
+
# defaulted to ``True``; preserve both here. ``self.config.auto_cleanup``
|
|
120
|
+
# is ``None`` when the caller did not provide a value, which we map to
|
|
121
|
+
# the legacy default ``True``. The field is in ``_PYTHON_ONLY`` on
|
|
122
|
+
# ``ConnectionConfig`` so it is never forwarded to the Rust core.
|
|
123
|
+
self.auto_cleanup: bool = True if self.config.auto_cleanup is None else bool(self.config.auto_cleanup)
|
|
124
|
+
|
|
125
|
+
# Controls whether `query % params` is applied even when params is
|
|
126
|
+
# empty (e.g. {} or ()). When True, doubled percents (`%%`) are
|
|
127
|
+
self.db_handle: DatabaseHandle | None = core_driver.database_new().db_handle
|
|
128
|
+
core_driver.database_init(db_handle=self.db_handle)
|
|
129
|
+
self.conn_handle: ConnectionHandle | None = core_driver.connection_new().conn_handle
|
|
130
|
+
|
|
131
|
+
# The LogoutConfig modifier re-applies the legacy Python wrapper
|
|
132
|
+
# behaviour (default ``enable_server_session_keep_alive_auto_detection=True``
|
|
133
|
+
# with FutureWarning, ``False+True → None`` keep-alive remap, default
|
|
134
|
+
# ``logout_error_strategy=best_effort``) on top of the generated
|
|
135
|
+
# ConnectionConfig fields. See ``logout_config_options_modifier`` for
|
|
136
|
+
# the full contract.
|
|
137
|
+
options = self.config.to_proto_options(
|
|
138
|
+
options_modifiers=[logout_config_options_modifier],
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if options:
|
|
142
|
+
response = core_driver.connection_set_options(
|
|
143
|
+
conn_handle=self.conn_handle,
|
|
144
|
+
options=options,
|
|
145
|
+
no_connection_details=self.config._no_connection_details,
|
|
146
|
+
connections_file_path=self.config.connections_file_path,
|
|
147
|
+
)
|
|
148
|
+
for warning in response.warnings:
|
|
149
|
+
warnings.warn(warning.message, stacklevel=2)
|
|
150
|
+
|
|
151
|
+
# Set session parameters if provided (before connection_init)
|
|
152
|
+
session_params = self.config.session_parameters
|
|
153
|
+
if session_params:
|
|
154
|
+
core_driver.connection_set_session_parameters(conn_handle=self.conn_handle, parameters=session_params)
|
|
155
|
+
|
|
156
|
+
# Initialise close-lifecycle state before ``_connect()`` so that the
|
|
157
|
+
# ``__del__`` / atexit fail-safes always observe a sane object even
|
|
158
|
+
# if connection_init raises. ``_connect()`` deliberately does NOT
|
|
159
|
+
# touch these — re-initialising them there would also reset the
|
|
160
|
+
# close lock if ``_connect()`` were ever called more than once.
|
|
161
|
+
self._closed = False
|
|
162
|
+
self._close_lock = threading.Lock()
|
|
163
|
+
|
|
164
|
+
self._connect()
|
|
165
|
+
|
|
166
|
+
self._session_parameters = SessionParametersProxy(self.conn_handle)
|
|
167
|
+
self._connection_info = ConnectionInfoProxy(self.conn_handle)
|
|
168
|
+
|
|
169
|
+
# ------------------------------------------------------------------
|
|
170
|
+
# Connection setup
|
|
171
|
+
# ------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
def _connect(self) -> None:
|
|
174
|
+
"""Establish the connection to Snowflake via the Rust core."""
|
|
175
|
+
try:
|
|
176
|
+
if self.config.mfa_callback is None:
|
|
177
|
+
self._initialize_core_connection()
|
|
178
|
+
else:
|
|
179
|
+
self._initialize_core_connection_with_mfa_callback(self.config.mfa_callback)
|
|
180
|
+
except ProgrammingError as e:
|
|
181
|
+
# The WIF cross-param guards fire in sf_core only via connection_init
|
|
182
|
+
# (ConnectionConfig::build -> validate_settings), surfaced as errno
|
|
183
|
+
# ER_INVALID_VALUE. Re-map to ER_INVALID_WIF_SETTINGS for legacy parity.
|
|
184
|
+
if _is_wif_conflict(e):
|
|
185
|
+
raise ProgrammingError(
|
|
186
|
+
msg=str(e),
|
|
187
|
+
errno=ER_INVALID_WIF_SETTINGS,
|
|
188
|
+
parameter=e.parameter,
|
|
189
|
+
validation_code=e.validation_code,
|
|
190
|
+
) from e
|
|
191
|
+
raise
|
|
192
|
+
self._telemetry_client = _InternalTelemetryClient(
|
|
193
|
+
conn_handle=cast(ConnectionHandle, self.conn_handle),
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
if self._should_auto_cleanup():
|
|
197
|
+
atexit.register(self._close_at_process_exit)
|
|
198
|
+
|
|
199
|
+
def _initialize_core_connection(self) -> None:
|
|
200
|
+
core_driver.connection_init(
|
|
201
|
+
conn_handle=self.conn_handle, # type: ignore[arg-type]
|
|
202
|
+
db_handle=self.db_handle, # type: ignore[arg-type]
|
|
203
|
+
wrapper_identity=WrapperIdentity(
|
|
204
|
+
driver_name=APPLICATION_NAME,
|
|
205
|
+
driver_version=__version__,
|
|
206
|
+
language_runtime=platform.python_implementation(),
|
|
207
|
+
language_version=platform.python_version(),
|
|
208
|
+
language_compiler=platform.python_compiler(),
|
|
209
|
+
application_path=get_application_path(),
|
|
210
|
+
),
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def _initialize_core_connection_with_mfa_callback(self, mfa_callback: Callable[[], Iterator[None]]) -> None:
|
|
214
|
+
callback = mfa_callback()
|
|
215
|
+
completed = threading.Event()
|
|
216
|
+
errors: list[Exception] = []
|
|
217
|
+
|
|
218
|
+
def initialize() -> None:
|
|
219
|
+
try:
|
|
220
|
+
self._initialize_core_connection()
|
|
221
|
+
except Exception as error:
|
|
222
|
+
errors.append(error)
|
|
223
|
+
finally:
|
|
224
|
+
completed.set()
|
|
225
|
+
|
|
226
|
+
init_task = threading.Thread(target=initialize)
|
|
227
|
+
init_task.start()
|
|
228
|
+
try:
|
|
229
|
+
while not completed.is_set():
|
|
230
|
+
try:
|
|
231
|
+
next(callback)
|
|
232
|
+
except StopIteration:
|
|
233
|
+
completed.wait()
|
|
234
|
+
break
|
|
235
|
+
finally:
|
|
236
|
+
init_task.join()
|
|
237
|
+
if errors:
|
|
238
|
+
raise errors[0]
|
|
239
|
+
|
|
240
|
+
# ------------------------------------------------------------------
|
|
241
|
+
# Lifecycle
|
|
242
|
+
# ------------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
@pep249
|
|
245
|
+
@api_telemetry
|
|
246
|
+
def close(self, retry: bool = True) -> None:
|
|
247
|
+
"""
|
|
248
|
+
Close the connection, send logout, and release handles.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
retry: If False, overrides max_attempts to 1 (no retries) before closing.
|
|
252
|
+
If True (default), uses init-time configuration.
|
|
253
|
+
|
|
254
|
+
Thread-safety: the lock guards only the handle swap (nanoseconds, no I/O).
|
|
255
|
+
All FFI calls use local handle copies outside the lock, so concurrent
|
|
256
|
+
close() calls are safe — the second caller gets None handles and skips.
|
|
257
|
+
"""
|
|
258
|
+
atexit.unregister(self._close_at_process_exit)
|
|
259
|
+
|
|
260
|
+
# Fast path — Core query, no lock. Core marks is_closed=true via AtomicBool
|
|
261
|
+
# at the START of connection_close (before HTTP logout), so this returns True
|
|
262
|
+
# even while another thread's logout is still in-flight.
|
|
263
|
+
if self.is_closed():
|
|
264
|
+
self._close_telemetry_client()
|
|
265
|
+
return
|
|
266
|
+
|
|
267
|
+
self._session_parameters.freeze()
|
|
268
|
+
self._connection_info.freeze()
|
|
269
|
+
|
|
270
|
+
# Lock guards ONLY the handle swap — prevents concurrent double-release.
|
|
271
|
+
with self._close_lock:
|
|
272
|
+
del self._messages[:]
|
|
273
|
+
conn_handle, self.conn_handle = self.conn_handle, None
|
|
274
|
+
db_handle, self.db_handle = self.db_handle, None
|
|
275
|
+
self._close_telemetry_client()
|
|
276
|
+
|
|
277
|
+
# All I/O outside the lock, using local handle copies.
|
|
278
|
+
# try/finally ensures handles are always released — on success, Strict
|
|
279
|
+
# failure, or set_options failure.
|
|
280
|
+
try:
|
|
281
|
+
if conn_handle:
|
|
282
|
+
if not retry:
|
|
283
|
+
core_driver.connection_set_options(
|
|
284
|
+
conn_handle=conn_handle,
|
|
285
|
+
options=create_config_settings_from_dict({LogoutOptionKeys.LOGOUT_MAX_ATTEMPTS: 1}),
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
core_driver.connection_close(conn_handle=conn_handle)
|
|
289
|
+
finally:
|
|
290
|
+
if conn_handle:
|
|
291
|
+
self._release_connection_handle(conn_handle)
|
|
292
|
+
if db_handle:
|
|
293
|
+
self._release_database_handle(db_handle)
|
|
294
|
+
|
|
295
|
+
def _try_close(self) -> None:
|
|
296
|
+
"""Best-effort close for __del__ and atexit — never raises."""
|
|
297
|
+
try:
|
|
298
|
+
if not self.is_closed():
|
|
299
|
+
self.close(retry=False)
|
|
300
|
+
except Exception:
|
|
301
|
+
logger.safe_log(logging.DEBUG, "close() failed during cleanup", exc_info=True)
|
|
302
|
+
|
|
303
|
+
def _should_auto_cleanup(self) -> bool:
|
|
304
|
+
"""Whether this connection should auto-close on GC/exit.
|
|
305
|
+
|
|
306
|
+
Uses getattr with False as fallback (NOT the default value of auto_cleanup,
|
|
307
|
+
which is True). False here is a GC fail-safe: if __del__ fires on a
|
|
308
|
+
half-initialized object (exception during __init__), we must NOT attempt
|
|
309
|
+
cleanup on an object whose Core handles were never created.
|
|
310
|
+
"""
|
|
311
|
+
return getattr(self, "auto_cleanup", False)
|
|
312
|
+
|
|
313
|
+
def __del__(self) -> None:
|
|
314
|
+
if self._should_auto_cleanup():
|
|
315
|
+
self._try_close()
|
|
316
|
+
|
|
317
|
+
def _close_at_process_exit(self) -> None:
|
|
318
|
+
"""
|
|
319
|
+
Cleanup handler called by atexit when process exits.
|
|
320
|
+
|
|
321
|
+
If close() was called successfully, this handler should have been unregistered
|
|
322
|
+
and should NOT run. If it runs for an already-closed connection, that indicates
|
|
323
|
+
a potential bug (unregister failed, race condition, or multiple registrations).
|
|
324
|
+
|
|
325
|
+
The entire body is wrapped in try/except because during interpreter shutdown,
|
|
326
|
+
any call (FFI, logging, warnings) may fail due to torn-down module state.
|
|
327
|
+
"""
|
|
328
|
+
try:
|
|
329
|
+
if self.is_closed():
|
|
330
|
+
logger.debug(
|
|
331
|
+
"atexit handler ran for already-closed connection. "
|
|
332
|
+
"This may indicate atexit.unregister() failed or a race condition occurred."
|
|
333
|
+
)
|
|
334
|
+
return
|
|
335
|
+
|
|
336
|
+
# Connection is leaked (not explicitly closed) — emit FutureWarning.
|
|
337
|
+
# Auto-cleanup will be disabled by default in a future version (SNOW-2314152).
|
|
338
|
+
try:
|
|
339
|
+
warnings.warn(
|
|
340
|
+
"Connection was not explicitly closed before process exit. "
|
|
341
|
+
"Auto-cleanup at exit will be disabled by default in a future version. "
|
|
342
|
+
"Please explicitly call connection.close() or use context manager.",
|
|
343
|
+
FutureWarning,
|
|
344
|
+
stacklevel=2,
|
|
345
|
+
)
|
|
346
|
+
except Exception:
|
|
347
|
+
pass # Interpreter shutting down; warning emission is best-effort
|
|
348
|
+
|
|
349
|
+
self._try_close()
|
|
350
|
+
except Exception:
|
|
351
|
+
logger.safe_log(
|
|
352
|
+
logging.WARNING,
|
|
353
|
+
"_close_at_process_exit failed during interpreter shutdown",
|
|
354
|
+
exc_info=True,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
# ------------------------------------------------------------------
|
|
358
|
+
# Transactions
|
|
359
|
+
# ------------------------------------------------------------------
|
|
360
|
+
|
|
361
|
+
@pep249
|
|
362
|
+
@api_telemetry
|
|
363
|
+
@requires_open
|
|
364
|
+
def commit(self) -> None:
|
|
365
|
+
"""Commit any pending transaction to the database."""
|
|
366
|
+
cur = self.cursor()
|
|
367
|
+
try:
|
|
368
|
+
cur.execute(COMMIT_SQL)
|
|
369
|
+
finally:
|
|
370
|
+
cur.close()
|
|
371
|
+
|
|
372
|
+
@pep249
|
|
373
|
+
@api_telemetry
|
|
374
|
+
@requires_open
|
|
375
|
+
def rollback(self) -> None:
|
|
376
|
+
"""Roll back to the start of any pending transaction."""
|
|
377
|
+
cur = self.cursor()
|
|
378
|
+
try:
|
|
379
|
+
cur.execute(ROLLBACK_SQL)
|
|
380
|
+
finally:
|
|
381
|
+
cur.close()
|
|
382
|
+
|
|
383
|
+
# ------------------------------------------------------------------
|
|
384
|
+
# Context manager
|
|
385
|
+
# ------------------------------------------------------------------
|
|
386
|
+
|
|
387
|
+
def __enter__(self) -> Connection:
|
|
388
|
+
"""
|
|
389
|
+
Enter the runtime context for the connection.
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
Connection: Self
|
|
393
|
+
"""
|
|
394
|
+
return self
|
|
395
|
+
|
|
396
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
397
|
+
"""Exit the runtime context. Commit on success / rollback on exception if autocommit is OFF."""
|
|
398
|
+
try:
|
|
399
|
+
if not self.is_closed() and not self._autocommit:
|
|
400
|
+
if exc_type is None:
|
|
401
|
+
self.commit()
|
|
402
|
+
else:
|
|
403
|
+
try:
|
|
404
|
+
self.rollback()
|
|
405
|
+
except Exception:
|
|
406
|
+
logger.warning("Rollback failed during exception handling", exc_info=True)
|
|
407
|
+
finally:
|
|
408
|
+
self.close()
|
|
409
|
+
|
|
410
|
+
# ------------------------------------------------------------------
|
|
411
|
+
# Autocommit
|
|
412
|
+
# ------------------------------------------------------------------
|
|
413
|
+
|
|
414
|
+
@requires_open
|
|
415
|
+
@api_telemetry
|
|
416
|
+
def set_autocommit(self, autocommit: bool) -> None:
|
|
417
|
+
"""Set the autocommit mode. Executes ALTER SESSION SET autocommit on the server."""
|
|
418
|
+
# FIXME: set autocommit via core
|
|
419
|
+
if not isinstance(autocommit, bool):
|
|
420
|
+
raise ProgrammingError(msg=f"Invalid autocommit parameter: {autocommit!r}", errno=ER_INVALID_VALUE)
|
|
421
|
+
cur = self.cursor()
|
|
422
|
+
try:
|
|
423
|
+
cur.execute(SET_AUTOCOMMIT_SQL.format(autocommit=str(autocommit).lower()))
|
|
424
|
+
except Error as e:
|
|
425
|
+
logger.warning("Autocommit feature is not enabled for this connection. Ignored: %s", e)
|
|
426
|
+
finally:
|
|
427
|
+
cur.close()
|
|
428
|
+
|
|
429
|
+
@pep249
|
|
430
|
+
@api_telemetry
|
|
431
|
+
def autocommit(self, value: bool) -> None:
|
|
432
|
+
"""Set autocommit mode."""
|
|
433
|
+
self.set_autocommit(value)
|
|
434
|
+
|
|
435
|
+
# ------------------------------------------------------------------
|
|
436
|
+
# Client prefetch threads
|
|
437
|
+
# ------------------------------------------------------------------
|
|
438
|
+
|
|
439
|
+
@requires_open
|
|
440
|
+
@api_telemetry
|
|
441
|
+
def set_client_prefetch_threads(self, value: int) -> None:
|
|
442
|
+
"""Set the number of concurrent chunk-prefetch threads.
|
|
443
|
+
|
|
444
|
+
Executes ``ALTER SESSION SET CLIENT_PREFETCH_THREADS`` so the change
|
|
445
|
+
takes effect on subsequent result-set fetches — matching the legacy
|
|
446
|
+
connector's immediate, locally-effective setter — rather than only
|
|
447
|
+
updating local config, which the core's chunk downloader never reads
|
|
448
|
+
back from after connect.
|
|
449
|
+
"""
|
|
450
|
+
value = clamp_client_prefetch_threads(value)
|
|
451
|
+
self.config.client_prefetch_threads = value
|
|
452
|
+
cur = self.cursor()
|
|
453
|
+
try:
|
|
454
|
+
cur.execute(SET_CLIENT_PREFETCH_THREADS_SQL.format(value=value))
|
|
455
|
+
finally:
|
|
456
|
+
cur.close()
|
|
457
|
+
|
|
458
|
+
@property
|
|
459
|
+
@api_telemetry
|
|
460
|
+
def client_prefetch_threads(self) -> int | None:
|
|
461
|
+
"""The number of threads used to prefetch query result data."""
|
|
462
|
+
return self.config.client_prefetch_threads
|
|
463
|
+
|
|
464
|
+
@client_prefetch_threads.setter
|
|
465
|
+
@api_telemetry
|
|
466
|
+
def client_prefetch_threads(self, value: int) -> None:
|
|
467
|
+
"""Set client_prefetch_threads; applies immediately via ``ALTER SESSION SET``."""
|
|
468
|
+
self.set_client_prefetch_threads(value)
|
|
469
|
+
|
|
470
|
+
# ------------------------------------------------------------------
|
|
471
|
+
# Connection state
|
|
472
|
+
# ------------------------------------------------------------------
|
|
473
|
+
|
|
474
|
+
@api_telemetry
|
|
475
|
+
def is_closed(self) -> bool:
|
|
476
|
+
"""
|
|
477
|
+
Check if the connection is closed.
|
|
478
|
+
|
|
479
|
+
Queries Core's authoritative state. If the handle has been released
|
|
480
|
+
(connection_release after close), the query fails — treated as closed
|
|
481
|
+
since a released handle means close() already completed.
|
|
482
|
+
"""
|
|
483
|
+
try:
|
|
484
|
+
response = core_driver.connection_is_closed(conn_handle=self.conn_handle) # type: ignore[arg-type]
|
|
485
|
+
return bool(response.is_closed)
|
|
486
|
+
except Exception:
|
|
487
|
+
return True
|
|
488
|
+
|
|
489
|
+
@api_telemetry
|
|
490
|
+
def is_valid(self) -> bool:
|
|
491
|
+
"""Check whether the connection is still usable for sending queries.
|
|
492
|
+
|
|
493
|
+
Validates both the network transport and the Snowflake session by sending a heartbeat to the server.
|
|
494
|
+
"""
|
|
495
|
+
if self.is_closed():
|
|
496
|
+
return False
|
|
497
|
+
try:
|
|
498
|
+
response = core_driver.connection_heartbeat(conn_handle=self.conn_handle) # type: ignore[arg-type]
|
|
499
|
+
return bool(response.valid)
|
|
500
|
+
except Exception:
|
|
501
|
+
return False
|
|
502
|
+
|
|
503
|
+
# ------------------------------------------------------------------
|
|
504
|
+
# Cursors
|
|
505
|
+
# ------------------------------------------------------------------
|
|
506
|
+
|
|
507
|
+
@overload
|
|
508
|
+
def cursor(self, cursor_class: None = None) -> SnowflakeCursor: ...
|
|
509
|
+
|
|
510
|
+
@overload
|
|
511
|
+
def cursor(self, cursor_class: type[_CursorClsT]) -> _CursorClsT: ...
|
|
512
|
+
|
|
513
|
+
@pep249
|
|
514
|
+
@api_telemetry
|
|
515
|
+
@requires_open
|
|
516
|
+
def cursor(self, cursor_class: type[SnowflakeCursorBase] | None = None) -> Any:
|
|
517
|
+
"""Return a new Cursor object using the connection.
|
|
518
|
+
|
|
519
|
+
Args:
|
|
520
|
+
cursor_class: The class to instantiate. Defaults to the
|
|
521
|
+
connection's ``SnowflakeCursor``; pass ``DictCursor`` to get
|
|
522
|
+
results as dictionaries.
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
A new cursor object.
|
|
526
|
+
"""
|
|
527
|
+
return self._instantiate_cursor(cursor_class)
|
|
528
|
+
|
|
529
|
+
# ------------------------------------------------------------------
|
|
530
|
+
# Multi-statement execution
|
|
531
|
+
# ------------------------------------------------------------------
|
|
532
|
+
|
|
533
|
+
@api_telemetry
|
|
534
|
+
def execute_string(
|
|
535
|
+
self,
|
|
536
|
+
sql_text: str,
|
|
537
|
+
remove_comments: bool = False,
|
|
538
|
+
return_cursors: bool = True,
|
|
539
|
+
cursor_class: CursorType = SnowflakeCursor,
|
|
540
|
+
**kwargs: Any,
|
|
541
|
+
) -> Iterable[CursorInstance]:
|
|
542
|
+
"""Execute a SQL text including multiple statements. This is a non-standard convenience method."""
|
|
543
|
+
stream = StringIO(sql_text)
|
|
544
|
+
stream_generator = self.execute_stream(
|
|
545
|
+
stream,
|
|
546
|
+
remove_comments=remove_comments,
|
|
547
|
+
cursor_class=cursor_class,
|
|
548
|
+
**kwargs,
|
|
549
|
+
)
|
|
550
|
+
if return_cursors:
|
|
551
|
+
return list(stream_generator)
|
|
552
|
+
for _ in stream_generator:
|
|
553
|
+
pass
|
|
554
|
+
return []
|
|
555
|
+
|
|
556
|
+
@api_telemetry
|
|
557
|
+
def execute_stream(
|
|
558
|
+
self,
|
|
559
|
+
stream: StringIO,
|
|
560
|
+
remove_comments: bool = False,
|
|
561
|
+
cursor_class: CursorType = SnowflakeCursor,
|
|
562
|
+
**kwargs: Any,
|
|
563
|
+
) -> Generator[CursorInstance, None, None]:
|
|
564
|
+
"""Execute a stream of SQL statements. This is a non-standard convenient method."""
|
|
565
|
+
for sql, is_put_or_get in split_statements(stream, remove_comments=remove_comments):
|
|
566
|
+
if not sql:
|
|
567
|
+
continue
|
|
568
|
+
cur = self.cursor(cursor_class=cursor_class)
|
|
569
|
+
cur.execute(sql, _is_put_get=is_put_or_get, **kwargs)
|
|
570
|
+
yield cur
|
|
571
|
+
|
|
572
|
+
# ------------------------------------------------------------------
|
|
573
|
+
# Internal API
|
|
574
|
+
# ------------------------------------------------------------------
|
|
575
|
+
|
|
576
|
+
@property
|
|
577
|
+
@internal_api
|
|
578
|
+
@backward_compatibility
|
|
579
|
+
def rest(self) -> SnowflakeRestful:
|
|
580
|
+
"""Internal :class:`SnowflakeRestful` instance exposed for backward compatibility."""
|
|
581
|
+
return SnowflakeRestful(connection=self)
|
|
582
|
+
|
|
583
|
+
@property
|
|
584
|
+
@internal_api
|
|
585
|
+
@backward_compatibility
|
|
586
|
+
def _telemetry(self) -> _InternalTelemetryClient:
|
|
587
|
+
return self._telemetry_client
|
|
588
|
+
|
|
589
|
+
@property
|
|
590
|
+
def _errorhandler_connection(self) -> Connection:
|
|
591
|
+
return self
|
|
592
|
+
|
|
593
|
+
# ------------------------------------------------------------------
|
|
594
|
+
# Server metadata
|
|
595
|
+
# ------------------------------------------------------------------
|
|
596
|
+
|
|
597
|
+
@cached_property
|
|
598
|
+
@api_telemetry
|
|
599
|
+
def snowflake_version(self) -> str:
|
|
600
|
+
"""The current Snowflake server version string."""
|
|
601
|
+
with self.cursor(DictCursor) as cur:
|
|
602
|
+
cur.execute(CURRENT_VERSION_SQL)
|
|
603
|
+
row: dict[str, Any] = cur.fetchone() # type: ignore[assignment]
|
|
604
|
+
return str(row["VERSION"]).split(" ")[0]
|
|
605
|
+
|
|
606
|
+
# ------------------------------------------------------------------
|
|
607
|
+
# Query status
|
|
608
|
+
# ------------------------------------------------------------------
|
|
609
|
+
|
|
610
|
+
@api_telemetry
|
|
611
|
+
def get_query_status(self, sf_qid: str) -> QueryStatus:
|
|
612
|
+
"""Retrieve the status of query with sf_qid."""
|
|
613
|
+
status, _ = self._get_query_status_with_response(sf_qid)
|
|
614
|
+
return status
|
|
615
|
+
|
|
616
|
+
@api_telemetry
|
|
617
|
+
def get_query_status_throw_if_error(self, sf_qid: str) -> QueryStatus:
|
|
618
|
+
"""Retrieve the status of query with sf_qid and raises an exception if the query terminated with an error."""
|
|
619
|
+
status, response = self._get_query_status_with_response(sf_qid)
|
|
620
|
+
if self.is_an_error(status):
|
|
621
|
+
message = response.error_message if response.HasField("error_message") else f"Query {sf_qid} failed"
|
|
622
|
+
errno = response.error_code if response.HasField("error_code") else -1
|
|
623
|
+
raise ProgrammingError(msg=message, errno=errno, sfqid=sf_qid)
|
|
624
|
+
return status
|
|
625
|
+
|
|
626
|
+
def _get_query_status_with_response(self, sf_qid: str) -> tuple[QueryStatus, ConnectionGetQueryStatusResponse]:
|
|
627
|
+
"""Fetch query status from the server and map the status name to a QueryStatus enum value."""
|
|
628
|
+
validate_query_id(sf_qid)
|
|
629
|
+
if self.is_closed():
|
|
630
|
+
return QueryStatus.DISCONNECTED, ConnectionGetQueryStatusResponse()
|
|
631
|
+
response = core_driver.connection_get_query_status(conn_handle=self.conn_handle, query_id=sf_qid) # type: ignore[arg-type]
|
|
632
|
+
try:
|
|
633
|
+
status = QueryStatus[response.status_name]
|
|
634
|
+
except KeyError:
|
|
635
|
+
logger.warning("Unknown query status %r; treating as NO_DATA", response.status_name)
|
|
636
|
+
status = QueryStatus.NO_DATA
|
|
637
|
+
return status, response
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
# Backward compatibility alias
|
|
641
|
+
SnowflakeConnection = Connection
|