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,732 @@
|
|
|
1
|
+
"""Shared cursor base mixin for sync and async cursor implementations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import abc
|
|
6
|
+
import ctypes
|
|
7
|
+
import re
|
|
8
|
+
import uuid
|
|
9
|
+
import warnings
|
|
10
|
+
|
|
11
|
+
from collections.abc import Sequence
|
|
12
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
13
|
+
|
|
14
|
+
from ..._common.extras import check_dependency, pandas, pyarrow
|
|
15
|
+
from ...constants import SessionParameterName, StatementParameterName
|
|
16
|
+
from ...errors import (
|
|
17
|
+
Error,
|
|
18
|
+
ErrorDetails,
|
|
19
|
+
InterfaceError,
|
|
20
|
+
NotSupportedError,
|
|
21
|
+
OperationalError,
|
|
22
|
+
ProgrammingError,
|
|
23
|
+
StructuredErrorHandler,
|
|
24
|
+
)
|
|
25
|
+
from ..api_client.client_api import core_driver
|
|
26
|
+
from ..binding_converters import (
|
|
27
|
+
BindingConverterBase,
|
|
28
|
+
ClientSideBindingConverter,
|
|
29
|
+
CsvBindingConverter,
|
|
30
|
+
JsonBindingConverter,
|
|
31
|
+
ParamStyle,
|
|
32
|
+
parse_stage_binding_threshold,
|
|
33
|
+
)
|
|
34
|
+
from ..config_utils import create_config_setting
|
|
35
|
+
from ..decorators import api_telemetry, backward_compatibility, pep249, snowpark_compat
|
|
36
|
+
from ..error_kinds import is_stage_binding_disabled
|
|
37
|
+
from ..errorcode import ER_FAILED_TO_REWRITE_MULTI_ROW_INSERT, ER_INVALID_VALUE
|
|
38
|
+
from ..errorhandler import ErrorHandlerMixin
|
|
39
|
+
from ..protobuf_gen.database_driver_v1_pb2 import BinaryDataPtr, ConfigSetting, QueryBindings, StatementHandle
|
|
40
|
+
from ..text_utils import extract_values_clause
|
|
41
|
+
from .decorators import requires_open
|
|
42
|
+
from .query_result import MultiStatementQueryResultState, QueryResult
|
|
43
|
+
from .result_metadata import QueryResultStats, ResultMetadata
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if TYPE_CHECKING:
|
|
47
|
+
from ...aio.connection import Connection as AsyncConnection
|
|
48
|
+
from ...connection import Connection
|
|
49
|
+
from .result_metadata import ResultMetadataV2
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class CursorBaseMixin(ErrorHandlerMixin, abc.ABC):
|
|
53
|
+
"""Zero-I/O cursor members shared by sync and async base cursor classes."""
|
|
54
|
+
|
|
55
|
+
_BARE_DESC_SQL_RE = re.compile(r"desc(?:ribe)?\s+([\w_]+)\s*;?\s*$", re.IGNORECASE)
|
|
56
|
+
_INSERT_SQL_RE = re.compile(r"^insert\s+into", re.IGNORECASE)
|
|
57
|
+
_COMMENT_SQL_RE = re.compile(r"/\*.*\*/")
|
|
58
|
+
|
|
59
|
+
# Set by subclass ``__init__`` before ``super().__init__()``.
|
|
60
|
+
_connection: Connection | AsyncConnection
|
|
61
|
+
_closed: bool
|
|
62
|
+
_messages: list[tuple[type[Error], ErrorDetails]]
|
|
63
|
+
_errorhandler: StructuredErrorHandler
|
|
64
|
+
_arraysize: int
|
|
65
|
+
_query_result: QueryResult
|
|
66
|
+
_rownumber: int
|
|
67
|
+
_multi_statement: MultiStatementQueryResultState | None
|
|
68
|
+
_statement_parameters: dict[str, Any]
|
|
69
|
+
_binding_data: None | bytes
|
|
70
|
+
|
|
71
|
+
def __init__(self) -> None:
|
|
72
|
+
self._closed = False
|
|
73
|
+
self._messages = []
|
|
74
|
+
self._errorhandler = Error.default_errorhandler
|
|
75
|
+
|
|
76
|
+
# -- PEP 249 cursor configuration (persists for cursor lifetime) --
|
|
77
|
+
self._arraysize = 1
|
|
78
|
+
|
|
79
|
+
# -- Query result state (replaced on execute, mutated on reset/consume) --
|
|
80
|
+
self._query_result = QueryResult()
|
|
81
|
+
|
|
82
|
+
# Cursor navigation position — mutable to avoid allocation per fetchone
|
|
83
|
+
self._rownumber = -1
|
|
84
|
+
|
|
85
|
+
# -- Multi-statement navigation (set by _handle_multi_statement_response, cleared on reset) --
|
|
86
|
+
self._multi_statement = None
|
|
87
|
+
|
|
88
|
+
# -- Statement parameters (persists until explicitly changed) --
|
|
89
|
+
self._statement_parameters = {}
|
|
90
|
+
|
|
91
|
+
# Keep binding data reference to prevent garbage collection while Rust uses it
|
|
92
|
+
self._binding_data = None
|
|
93
|
+
|
|
94
|
+
# ------------------------------------------------------------------
|
|
95
|
+
# PEP 249 attributes
|
|
96
|
+
# ------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
@pep249
|
|
100
|
+
@api_telemetry
|
|
101
|
+
def description(self) -> list[ResultMetadata] | None:
|
|
102
|
+
"""
|
|
103
|
+
Read-only attribute describing the result columns of a query.
|
|
104
|
+
|
|
105
|
+
Returns a sequence of 7-item tuples, each containing:
|
|
106
|
+
- name: Column name (str)
|
|
107
|
+
- type_code: Integer type code (int)
|
|
108
|
+
- display_size: Display size in characters (int | None)
|
|
109
|
+
- internal_size: Internal size in characters (int | None)
|
|
110
|
+
- precision: Precision for numeric types (int | None)
|
|
111
|
+
- scale: Scale for numeric types (int | None)
|
|
112
|
+
- null_ok: True if column can contain NULLs (bool | None)
|
|
113
|
+
|
|
114
|
+
Returns None if no query has been executed or if the query didn't produce a result set.
|
|
115
|
+
"""
|
|
116
|
+
return self._query_result.description
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
@snowpark_compat
|
|
120
|
+
@backward_compatibility
|
|
121
|
+
def _description_internal(self) -> list[ResultMetadataV2] | None:
|
|
122
|
+
"""New-format column metadata for the last executed statement.
|
|
123
|
+
|
|
124
|
+
Snowpark probes for this attribute and falls back to :attr:`description`
|
|
125
|
+
when it is absent, which loses ``fields`` and ``vector_dimension``.
|
|
126
|
+
"""
|
|
127
|
+
return self._query_result.description_v2
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
@snowpark_compat
|
|
131
|
+
def _query_result_format(self) -> str | None:
|
|
132
|
+
"""``"arrow"`` or ``"json"`` for the last statement, or None before one
|
|
133
|
+
produced a result set. A format-less response reads as ``"json"``, as it
|
|
134
|
+
does on the legacy connector.
|
|
135
|
+
|
|
136
|
+
Snowpark reads this to decide whether results can be fetched as pandas.
|
|
137
|
+
"""
|
|
138
|
+
return self._query_result.query_result_format
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
@pep249
|
|
142
|
+
@api_telemetry
|
|
143
|
+
def rowcount(self) -> int | None:
|
|
144
|
+
"""
|
|
145
|
+
Read-only attribute specifying the number of rows that the last
|
|
146
|
+
.execute*() produced or affected.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
int: Number of rows affected, or None if not determined
|
|
150
|
+
"""
|
|
151
|
+
return self._query_result.rowcount
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
@pep249
|
|
155
|
+
@api_telemetry
|
|
156
|
+
def arraysize(self) -> int:
|
|
157
|
+
"""Number of rows to fetch at a time with .fetchmany(). Defaults to 1."""
|
|
158
|
+
return self._arraysize
|
|
159
|
+
|
|
160
|
+
@arraysize.setter
|
|
161
|
+
@api_telemetry
|
|
162
|
+
def arraysize(self, value: int) -> None:
|
|
163
|
+
self._arraysize = int(value)
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
@pep249
|
|
167
|
+
@api_telemetry
|
|
168
|
+
def messages(self) -> list[tuple[type[Error], ErrorDetails]]:
|
|
169
|
+
"""List of (exception class, exception value) tuples received from the database."""
|
|
170
|
+
return self._messages
|
|
171
|
+
|
|
172
|
+
@messages.setter
|
|
173
|
+
@api_telemetry
|
|
174
|
+
def messages(self, value: list[tuple[type[Error], ErrorDetails]]) -> None:
|
|
175
|
+
self._messages = value
|
|
176
|
+
|
|
177
|
+
# ------------------------------------------------------------------
|
|
178
|
+
# Execution metadata
|
|
179
|
+
# ------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
@api_telemetry
|
|
183
|
+
def query(self) -> str | None:
|
|
184
|
+
"""
|
|
185
|
+
Read-only attribute containing the SQL text of the last executed or described query.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
str | None: The SQL query string, or None if no query has been executed or described
|
|
189
|
+
"""
|
|
190
|
+
return self._query_result.query
|
|
191
|
+
|
|
192
|
+
@property
|
|
193
|
+
@api_telemetry
|
|
194
|
+
def sfqid(self) -> str | None:
|
|
195
|
+
"""
|
|
196
|
+
Read-only attribute containing the Snowflake Query ID for the last executed or described query.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
str | None: Snowflake Query ID (UUID format), or None if no query has been executed or described
|
|
200
|
+
"""
|
|
201
|
+
return self._query_result.sfqid
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
@api_telemetry
|
|
205
|
+
def request_id(self) -> uuid.UUID | None:
|
|
206
|
+
"""
|
|
207
|
+
Read-only attribute containing the client-generated ``requestId`` of the last query submission.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
uuid.UUID | None: Client request id, or None when unavailable.
|
|
211
|
+
"""
|
|
212
|
+
value = self._query_result.request_id
|
|
213
|
+
if not value:
|
|
214
|
+
return None
|
|
215
|
+
return uuid.UUID(value)
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
@snowpark_compat
|
|
219
|
+
@backward_compatibility(recommendation="Use the public `request_id` property instead.")
|
|
220
|
+
def _request_id(self) -> uuid.UUID | None:
|
|
221
|
+
return self.request_id
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
@api_telemetry
|
|
225
|
+
def stats(self) -> QueryResultStats:
|
|
226
|
+
"""Returns detailed row-level statistics for DML operations."""
|
|
227
|
+
return self._query_result.stats
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
@pep249
|
|
231
|
+
@api_telemetry
|
|
232
|
+
def rownumber(self) -> int | None:
|
|
233
|
+
"""The current 0-based index of the cursor in the result set, or ``None`` if indeterminate."""
|
|
234
|
+
return self._rownumber if self._rownumber >= 0 else None
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
@api_telemetry
|
|
238
|
+
def sqlstate(self) -> str | None:
|
|
239
|
+
"""The SQLSTATE code of the last executed operation."""
|
|
240
|
+
return self._query_result.sqlstate
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
@api_telemetry
|
|
244
|
+
def multi_statement_parent_sfqid(self) -> str | None:
|
|
245
|
+
"""
|
|
246
|
+
Read-only attribute containing the parent Snowflake Query ID for multi-statement queries.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
str | None: Parent query ID for multi-statement, or None for single statements.
|
|
250
|
+
"""
|
|
251
|
+
return self._multi_statement.parent_qid if self._multi_statement else None
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
@api_telemetry
|
|
255
|
+
def multi_statement_savedIds(self) -> list[str]:
|
|
256
|
+
"""
|
|
257
|
+
Read-only attribute containing child query IDs for multi-statement queries.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
list[str]: List of child query IDs (empty list for single statements).
|
|
261
|
+
"""
|
|
262
|
+
return self._multi_statement.child_query_ids if self._multi_statement else []
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
@api_telemetry
|
|
266
|
+
def is_file_transfer(self) -> bool:
|
|
267
|
+
"""Whether the last executed command was a PUT or GET file transfer."""
|
|
268
|
+
return self._query_result.is_file_transfer
|
|
269
|
+
|
|
270
|
+
def _stage_binding_threshold(self) -> int:
|
|
271
|
+
raw = self._connection._session_parameters[SessionParameterName.CLIENT_STAGE_ARRAY_BINDING_THRESHOLD]
|
|
272
|
+
return parse_stage_binding_threshold(raw)
|
|
273
|
+
|
|
274
|
+
def _should_use_csv_binding(self, parameters: Sequence[Any], threshold: int, query: str = "") -> bool:
|
|
275
|
+
"""Return whether array bindings should be uploaded as CSV to a stage.
|
|
276
|
+
|
|
277
|
+
Stage binding applies only to INSERT statements with multi-row (array)
|
|
278
|
+
bindings whose cell count meets ``CLIENT_STAGE_ARRAY_BINDING_THRESHOLD``.
|
|
279
|
+
The server rejects CSV stage bindings for non-INSERT queries, matching
|
|
280
|
+
the legacy connector's behavior of gating on INSERT.
|
|
281
|
+
Scalar binds always use inline JSON regardless of threshold.
|
|
282
|
+
|
|
283
|
+
"""
|
|
284
|
+
if not query.lstrip().upper().startswith("INSERT"):
|
|
285
|
+
return False
|
|
286
|
+
if not any(isinstance(value, list) for value in parameters):
|
|
287
|
+
return False
|
|
288
|
+
effective_cells = BindingConverterBase.effective_binding_cells(parameters)
|
|
289
|
+
if effective_cells == 0 or threshold <= 0:
|
|
290
|
+
return False
|
|
291
|
+
return effective_cells >= threshold
|
|
292
|
+
|
|
293
|
+
def _build_query_bindings(
|
|
294
|
+
self, parameters: Sequence[Any], query: str = "", *, force_json: bool = False
|
|
295
|
+
) -> QueryBindings | None:
|
|
296
|
+
"""Serialize parameters and build a QueryBindings protobuf message.
|
|
297
|
+
|
|
298
|
+
Converts Python parameter values to JSON or CSV, then wraps the result
|
|
299
|
+
in a zero-copy BinaryDataPtr so the Rust core can read the bytes
|
|
300
|
+
directly from Python memory.
|
|
301
|
+
|
|
302
|
+
The encoded bytes are stored on ``self._binding_data`` to prevent
|
|
303
|
+
garbage collection while Rust holds the pointer.
|
|
304
|
+
|
|
305
|
+
``force_json`` bypasses stage-binding selection to rebuild inline JSON
|
|
306
|
+
bindings for a retry after the ``SYSTEM$BIND`` stage was rejected.
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
QueryBindings with the serialized JSON or CSV, or None if parameters
|
|
310
|
+
serialize to nothing (e.g. empty list).
|
|
311
|
+
"""
|
|
312
|
+
threshold = self._stage_binding_threshold()
|
|
313
|
+
use_csv = False if force_json else self._should_use_csv_binding(parameters, threshold, query)
|
|
314
|
+
|
|
315
|
+
if use_csv:
|
|
316
|
+
binding_bytes = CsvBindingConverter.serialize_parameters_to_csv(parameters)
|
|
317
|
+
if binding_bytes is None:
|
|
318
|
+
return None
|
|
319
|
+
else:
|
|
320
|
+
json_str, _ = JsonBindingConverter.serialize_parameters(parameters)
|
|
321
|
+
if json_str is None:
|
|
322
|
+
return None
|
|
323
|
+
binding_bytes = json_str.encode("utf-8")
|
|
324
|
+
|
|
325
|
+
self._binding_data = binding_bytes
|
|
326
|
+
length = len(binding_bytes)
|
|
327
|
+
|
|
328
|
+
ptr_value = ctypes.cast(ctypes.c_char_p(self._binding_data), ctypes.c_void_p).value
|
|
329
|
+
if ptr_value is None:
|
|
330
|
+
raise RuntimeError("Failed to obtain memory pointer for binding data")
|
|
331
|
+
|
|
332
|
+
ptr_bytes = ptr_value.to_bytes(8, byteorder="little", signed=False)
|
|
333
|
+
binary_data_ptr = BinaryDataPtr(value=ptr_bytes, length=length)
|
|
334
|
+
if use_csv:
|
|
335
|
+
return QueryBindings(csv=binary_data_ptr)
|
|
336
|
+
return QueryBindings(json=binary_data_ptr)
|
|
337
|
+
|
|
338
|
+
def _should_retry_with_inline_json(self, bindings: QueryBindings | None, exc: OperationalError) -> bool:
|
|
339
|
+
"""Return whether *exc* should trigger a retry with inline JSON bindings.
|
|
340
|
+
|
|
341
|
+
Only a CSV (stage) attempt can hit a stage-binding-disabled error --
|
|
342
|
+
that upload only happens when ``bindings`` selected CSV, so a JSON
|
|
343
|
+
attempt is excluded even though ``is_stage_binding_disabled`` would
|
|
344
|
+
report True for it.
|
|
345
|
+
"""
|
|
346
|
+
return bindings is not None and bindings.HasField("csv") and is_stage_binding_disabled(exc)
|
|
347
|
+
|
|
348
|
+
def _prepare_query(
|
|
349
|
+
self,
|
|
350
|
+
operation: str,
|
|
351
|
+
parameters: Sequence[Any] | dict[str, Any] | None,
|
|
352
|
+
_force_qmark_paramstyle: bool = False,
|
|
353
|
+
) -> tuple[str, Sequence[Any] | None]:
|
|
354
|
+
"""Prepare query and server-side binding parameters based on paramstyle.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
operation: SQL statement
|
|
358
|
+
parameters: Parameters to bind (sequence or dict)
|
|
359
|
+
_force_qmark_paramstyle: If True, treat the call as qmark
|
|
360
|
+
regardless of the connection's configured paramstyle. Used by
|
|
361
|
+
callers (e.g. snowflake.core) that emit ``?`` placeholders
|
|
362
|
+
while connections may default to pyformat.
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
Tuple of (query string, server-side binding parameters or None)
|
|
366
|
+
|
|
367
|
+
Raises:
|
|
368
|
+
ProgrammingError: If dict parameters used with server-side binding
|
|
369
|
+
"""
|
|
370
|
+
match = self._BARE_DESC_SQL_RE.match(operation)
|
|
371
|
+
if match:
|
|
372
|
+
warnings.warn(
|
|
373
|
+
"Bare 'DESC <table>' and 'DESCRIBE <table>' statements are deprecated "
|
|
374
|
+
"and will be removed in a future release; use 'DESC TABLE <table>' instead.",
|
|
375
|
+
DeprecationWarning,
|
|
376
|
+
stacklevel=4,
|
|
377
|
+
)
|
|
378
|
+
operation = f"describe table {match.group(1)}"
|
|
379
|
+
|
|
380
|
+
if parameters is None:
|
|
381
|
+
return operation, None
|
|
382
|
+
|
|
383
|
+
paramstyle = ParamStyle.QMARK if _force_qmark_paramstyle else self._connection.paramstyle
|
|
384
|
+
|
|
385
|
+
if paramstyle.is_client_side():
|
|
386
|
+
# format paramstyle only supports positional params (%s), not named params
|
|
387
|
+
if paramstyle == ParamStyle.FORMAT and isinstance(parameters, dict):
|
|
388
|
+
raise ProgrammingError(
|
|
389
|
+
msg="Dict parameters not supported with format paramstyle. "
|
|
390
|
+
"Use pyformat paramstyle for named parameters, or use a sequence.",
|
|
391
|
+
errno=ER_INVALID_VALUE,
|
|
392
|
+
)
|
|
393
|
+
# Client-side binding: interpolate parameters into SQL string
|
|
394
|
+
query = ClientSideBindingConverter.interpolate_query(
|
|
395
|
+
operation,
|
|
396
|
+
parameters,
|
|
397
|
+
interpolate_empty_sequences=self._connection._interpolate_empty_sequences,
|
|
398
|
+
)
|
|
399
|
+
return query, None
|
|
400
|
+
else:
|
|
401
|
+
# Server-side binding: qmark or numeric
|
|
402
|
+
if isinstance(parameters, dict):
|
|
403
|
+
raise ProgrammingError(
|
|
404
|
+
msg="Named parameters (dict) not supported with qmark/numeric paramstyle. "
|
|
405
|
+
"Use pyformat paramstyle for named parameters.",
|
|
406
|
+
errno=ER_INVALID_VALUE,
|
|
407
|
+
)
|
|
408
|
+
return operation, parameters
|
|
409
|
+
|
|
410
|
+
def _format_query_for_log(self, query: str) -> str:
|
|
411
|
+
return self._connection._format_query_for_log(query)
|
|
412
|
+
|
|
413
|
+
def _prepare_call_proc_statement(self, procname: str, args: Any = None) -> tuple[str, Sequence[Any]]:
|
|
414
|
+
"""Validate ``callproc`` arguments and build the CALL statement."""
|
|
415
|
+
if args is None:
|
|
416
|
+
args = ()
|
|
417
|
+
if isinstance(args, (str, bytes)):
|
|
418
|
+
raise TypeError(f"callproc args must be a sequence (e.g. list or tuple), not {type(args).__name__}")
|
|
419
|
+
if not isinstance(args, Sequence):
|
|
420
|
+
raise TypeError(f"callproc args must be a sequence (e.g. list or tuple), not {type(args).__name__}")
|
|
421
|
+
command = f"CALL {procname}({self._connection.paramstyle.placeholders(len(args))})"
|
|
422
|
+
return command, args
|
|
423
|
+
|
|
424
|
+
def _set_statement_parameter(self, key: str, value: Any) -> None:
|
|
425
|
+
"""Store a statement-level parameter for the next execute."""
|
|
426
|
+
self._statement_parameters[key] = value
|
|
427
|
+
|
|
428
|
+
@api_telemetry
|
|
429
|
+
@requires_open
|
|
430
|
+
def set_statement_parameter(self, key: str, value: Any) -> None:
|
|
431
|
+
"""Set a sticky statement-level parameter (e.g., MULTI_STATEMENT_COUNT).
|
|
432
|
+
|
|
433
|
+
Persists across `execute()` calls on this cursor until explicitly
|
|
434
|
+
changed. For per-call kwargs (one execute, no bleed across calls),
|
|
435
|
+
use the `statement_parameters` channel in `execute()` instead.
|
|
436
|
+
|
|
437
|
+
This must be called before execute() to take effect.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
key: Parameter name (e.g., "MULTI_STATEMENT_COUNT").
|
|
441
|
+
value: Parameter value.
|
|
442
|
+
|
|
443
|
+
Raises:
|
|
444
|
+
InterfaceError: If cursor is closed.
|
|
445
|
+
|
|
446
|
+
Example:
|
|
447
|
+
cursor.set_statement_parameter("MULTI_STATEMENT_COUNT", 3)
|
|
448
|
+
cursor.execute("SELECT 1; SELECT 2; SELECT 3")
|
|
449
|
+
"""
|
|
450
|
+
self._set_statement_parameter(key, value)
|
|
451
|
+
|
|
452
|
+
def _build_array_binding_params(
|
|
453
|
+
self,
|
|
454
|
+
operation: str,
|
|
455
|
+
seq_of_parameters: Sequence[Sequence[Any] | dict[str, Any]],
|
|
456
|
+
first_params: Sequence[Any] | dict[str, Any],
|
|
457
|
+
) -> list[list[Any]]:
|
|
458
|
+
"""Validate uniform row widths and transpose rows to column-major bind arrays.
|
|
459
|
+
|
|
460
|
+
Used by ``executemany`` server-side array binding (sequence params only;
|
|
461
|
+
dict params are handled by the caller before reaching here).
|
|
462
|
+
"""
|
|
463
|
+
# Dict params were handled by the caller; only sequences reach here.
|
|
464
|
+
rows = cast("Sequence[Sequence[Any]]", seq_of_parameters)
|
|
465
|
+
|
|
466
|
+
# Error code 251007 (ER_INVALID_VALUE) matches reference driver behavior
|
|
467
|
+
first_len = len(first_params)
|
|
468
|
+
for params in rows:
|
|
469
|
+
if len(params) != first_len:
|
|
470
|
+
raise InterfaceError(
|
|
471
|
+
msg=f"Bulk data size don't match. expected: {first_len}, got: {len(params)}, command: {operation}",
|
|
472
|
+
errno=ER_INVALID_VALUE,
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
# Transpose from row-major to column-major format
|
|
476
|
+
# Input: [(row1_col1, row1_col2), (row2_col1, row2_col2), ...]
|
|
477
|
+
# Output: [[row1_col1, row2_col1, ...], [row1_col2, row2_col2, ...]]
|
|
478
|
+
return [[row[col_idx] for row in rows] for col_idx in range(first_len)]
|
|
479
|
+
|
|
480
|
+
def _rewrite_multirow_insert(
|
|
481
|
+
self,
|
|
482
|
+
operation: str,
|
|
483
|
+
seq_of_parameters: Sequence[Sequence[Any] | dict[str, Any]],
|
|
484
|
+
) -> str | None:
|
|
485
|
+
"""Rewrite a client-side-bound INSERT into a single multi-row INSERT.
|
|
486
|
+
|
|
487
|
+
Used by ``executemany`` for pyformat/format paramstyles so bulk
|
|
488
|
+
inserts issue one HTTP request instead of one per row, mirroring the
|
|
489
|
+
reference connector's rewrite. Returns ``None`` when ``operation`` is
|
|
490
|
+
not a rewritable ``INSERT INTO ... VALUES (...)`` statement (e.g.
|
|
491
|
+
UPDATE, DELETE, MERGE) — callers fall back to per-row execution.
|
|
492
|
+
"""
|
|
493
|
+
stripped = operation.strip(" \t\n\r")
|
|
494
|
+
if not self._INSERT_SQL_RE.match(stripped):
|
|
495
|
+
return None
|
|
496
|
+
|
|
497
|
+
command_wo_comments = self._COMMENT_SQL_RE.sub("", stripped)
|
|
498
|
+
fmt = extract_values_clause(command_wo_comments)
|
|
499
|
+
if fmt is None:
|
|
500
|
+
raise ProgrammingError(
|
|
501
|
+
msg=(
|
|
502
|
+
"executemany() failed to rewrite INSERT as multi-row: no VALUES clause found."
|
|
503
|
+
" Ensure the statement has the form 'INSERT INTO ... VALUES (...)'."
|
|
504
|
+
),
|
|
505
|
+
errno=ER_FAILED_TO_REWRITE_MULTI_ROW_INSERT,
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
values = [ClientSideBindingConverter.interpolate_query(fmt, params) for params in seq_of_parameters]
|
|
509
|
+
return stripped.replace(fmt, ",".join(values), 1)
|
|
510
|
+
|
|
511
|
+
def _collect_statement_params(
|
|
512
|
+
self,
|
|
513
|
+
*,
|
|
514
|
+
skip_upload_on_content_match: bool,
|
|
515
|
+
num_statements: int | None = None,
|
|
516
|
+
statement_params: dict[str, Any] | None = None,
|
|
517
|
+
) -> dict[str, Any]:
|
|
518
|
+
"""Collect per-call statement parameters for a single execute().
|
|
519
|
+
|
|
520
|
+
Merges the caller-supplied ``_statement_params`` bag (opaque passthrough,
|
|
521
|
+
e.g. ``DATE_INPUT_FORMAT``) with the driver's known per-call fold-ins
|
|
522
|
+
(``num_statements`` → MULTI_STATEMENT_COUNT, ``skip_upload_on_content_match``
|
|
523
|
+
→ SKIP_UPLOAD_ON_CONTENT_MATCH). Scoped to one call and never written to
|
|
524
|
+
the cursor's sticky `_statement_parameters`.
|
|
525
|
+
"""
|
|
526
|
+
# Seed from the caller's bag, then apply the driver fold-ins on top so
|
|
527
|
+
# they win on a key collision — matches legacy, which spreads
|
|
528
|
+
# ``{**_statement_params, "MULTI_STATEMENT_COUNT": num_statements}``.
|
|
529
|
+
params: dict[str, Any] = dict(statement_params or {})
|
|
530
|
+
if skip_upload_on_content_match:
|
|
531
|
+
params[StatementParameterName.SKIP_UPLOAD_ON_CONTENT_MATCH] = True
|
|
532
|
+
if num_statements is not None:
|
|
533
|
+
params[StatementParameterName.MULTI_STATEMENT_COUNT] = num_statements
|
|
534
|
+
return params
|
|
535
|
+
|
|
536
|
+
def _build_statement_parameters_options(
|
|
537
|
+
self, statement_parameters: dict[str, Any] | None = None
|
|
538
|
+
) -> dict[str, ConfigSetting]:
|
|
539
|
+
"""Build SetOptions from the cursor's sticky `_statement_parameters`
|
|
540
|
+
merged with per-call `statement_parameters`. Per-call wins on key
|
|
541
|
+
collision and is never persisted on the cursor.
|
|
542
|
+
"""
|
|
543
|
+
merged_statement_params = {**self._statement_parameters, **(statement_parameters or {})}
|
|
544
|
+
options: dict[str, ConfigSetting] = {}
|
|
545
|
+
for key, value in merged_statement_params.items():
|
|
546
|
+
try:
|
|
547
|
+
setting = create_config_setting(value)
|
|
548
|
+
except TypeError as err:
|
|
549
|
+
raise ProgrammingError(msg=f"Cannot set parameter '{key}': {err}") from err
|
|
550
|
+
if setting is not None:
|
|
551
|
+
options[key] = setting
|
|
552
|
+
return options
|
|
553
|
+
|
|
554
|
+
def _apply_statement_parameters(
|
|
555
|
+
self,
|
|
556
|
+
stmt_handle: StatementHandle,
|
|
557
|
+
statement_parameters: dict[str, Any] | None = None,
|
|
558
|
+
) -> None:
|
|
559
|
+
"""Apply sticky `_statement_parameters` merged with per-call
|
|
560
|
+
`statement_parameters` via SetOptions RPC. Per-call wins on key
|
|
561
|
+
collision and is never persisted on the cursor.
|
|
562
|
+
"""
|
|
563
|
+
options = self._build_statement_parameters_options(statement_parameters)
|
|
564
|
+
if not options:
|
|
565
|
+
return
|
|
566
|
+
core_driver.statement_set_options(stmt_handle=stmt_handle, options=options)
|
|
567
|
+
|
|
568
|
+
# ------------------------------------------------------------------
|
|
569
|
+
# Cursor state / navigation
|
|
570
|
+
# ------------------------------------------------------------------
|
|
571
|
+
|
|
572
|
+
@abc.abstractmethod
|
|
573
|
+
def reset(self, closing: bool = False) -> None:
|
|
574
|
+
"""Release result-set resources; implemented by ``SnowflakeCursorBase``."""
|
|
575
|
+
...
|
|
576
|
+
|
|
577
|
+
@api_telemetry
|
|
578
|
+
def is_closed(self) -> bool:
|
|
579
|
+
"""
|
|
580
|
+
Check if the cursor is closed.
|
|
581
|
+
|
|
582
|
+
Returns:
|
|
583
|
+
bool: True if closed, False otherwise
|
|
584
|
+
"""
|
|
585
|
+
return self._closed or self._connection.is_closed()
|
|
586
|
+
|
|
587
|
+
@pep249
|
|
588
|
+
@api_telemetry
|
|
589
|
+
def close(self) -> bool | None:
|
|
590
|
+
"""Close the cursor now.
|
|
591
|
+
|
|
592
|
+
Returns whether the cursor was closed during this call.
|
|
593
|
+
"""
|
|
594
|
+
try:
|
|
595
|
+
if self._closed:
|
|
596
|
+
return False
|
|
597
|
+
self.reset(closing=True)
|
|
598
|
+
self._closed = True
|
|
599
|
+
del self._messages[:]
|
|
600
|
+
return True
|
|
601
|
+
except Exception:
|
|
602
|
+
return None
|
|
603
|
+
|
|
604
|
+
@pep249
|
|
605
|
+
@api_telemetry
|
|
606
|
+
def scroll(self, value: int, mode: str = "relative") -> None:
|
|
607
|
+
"""Scroll the cursor in the result set."""
|
|
608
|
+
raise NotSupportedError("scroll is not supported")
|
|
609
|
+
|
|
610
|
+
@pep249
|
|
611
|
+
@api_telemetry
|
|
612
|
+
def setinputsizes(self, sizes: Sequence[Any]) -> None:
|
|
613
|
+
"""Not supported."""
|
|
614
|
+
return None
|
|
615
|
+
|
|
616
|
+
@pep249
|
|
617
|
+
@api_telemetry
|
|
618
|
+
def setoutputsize(self, size: int, column: int | None = None) -> None:
|
|
619
|
+
"""Not supported."""
|
|
620
|
+
return None
|
|
621
|
+
|
|
622
|
+
@property
|
|
623
|
+
@pep249
|
|
624
|
+
@api_telemetry
|
|
625
|
+
def lastrowid(self) -> None:
|
|
626
|
+
"""Snowflake does not support lastrowid; returns None per PEP 249."""
|
|
627
|
+
return None
|
|
628
|
+
|
|
629
|
+
# ------------------------------------------------------------------
|
|
630
|
+
# Session parameter accessors
|
|
631
|
+
# ------------------------------------------------------------------
|
|
632
|
+
|
|
633
|
+
@property
|
|
634
|
+
@api_telemetry
|
|
635
|
+
def timestamp_output_format(self) -> str | None:
|
|
636
|
+
"""The session's ``TIMESTAMP_OUTPUT_FORMAT`` parameter value."""
|
|
637
|
+
return self._connection._session_parameters._get_string("TIMESTAMP_OUTPUT_FORMAT")
|
|
638
|
+
|
|
639
|
+
@property
|
|
640
|
+
@api_telemetry
|
|
641
|
+
def timestamp_ltz_output_format(self) -> str | None:
|
|
642
|
+
"""The session's ``TIMESTAMP_LTZ_OUTPUT_FORMAT`` parameter value.
|
|
643
|
+
|
|
644
|
+
Falls back to :pyattr:`timestamp_output_format` when not set explicitly.
|
|
645
|
+
"""
|
|
646
|
+
return (
|
|
647
|
+
self._connection._session_parameters._get_string("TIMESTAMP_LTZ_OUTPUT_FORMAT")
|
|
648
|
+
or self.timestamp_output_format
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
@property
|
|
652
|
+
@api_telemetry
|
|
653
|
+
def timestamp_tz_output_format(self) -> str | None:
|
|
654
|
+
"""The session's ``TIMESTAMP_TZ_OUTPUT_FORMAT`` parameter value.
|
|
655
|
+
|
|
656
|
+
Falls back to :pyattr:`timestamp_output_format` when not set explicitly.
|
|
657
|
+
"""
|
|
658
|
+
return (
|
|
659
|
+
self._connection._session_parameters._get_string("TIMESTAMP_TZ_OUTPUT_FORMAT")
|
|
660
|
+
or self.timestamp_output_format
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
@property
|
|
664
|
+
@api_telemetry
|
|
665
|
+
def timestamp_ntz_output_format(self) -> str | None:
|
|
666
|
+
"""The session's ``TIMESTAMP_NTZ_OUTPUT_FORMAT`` parameter value.
|
|
667
|
+
|
|
668
|
+
Falls back to :pyattr:`timestamp_output_format` when not set explicitly.
|
|
669
|
+
"""
|
|
670
|
+
return (
|
|
671
|
+
self._connection._session_parameters._get_string("TIMESTAMP_NTZ_OUTPUT_FORMAT")
|
|
672
|
+
or self.timestamp_output_format
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
@property
|
|
676
|
+
@api_telemetry
|
|
677
|
+
def date_output_format(self) -> str | None:
|
|
678
|
+
"""The session's ``DATE_OUTPUT_FORMAT`` parameter value."""
|
|
679
|
+
return self._connection._session_parameters._get_string("DATE_OUTPUT_FORMAT")
|
|
680
|
+
|
|
681
|
+
@property
|
|
682
|
+
@api_telemetry
|
|
683
|
+
def time_output_format(self) -> str | None:
|
|
684
|
+
"""The session's ``TIME_OUTPUT_FORMAT`` parameter value."""
|
|
685
|
+
return self._connection._session_parameters._get_string("TIME_OUTPUT_FORMAT")
|
|
686
|
+
|
|
687
|
+
@property
|
|
688
|
+
@api_telemetry
|
|
689
|
+
def timezone(self) -> str | None:
|
|
690
|
+
"""The session's ``TIMEZONE`` parameter value."""
|
|
691
|
+
return self._connection._session_parameters._get_string("TIMEZONE")
|
|
692
|
+
|
|
693
|
+
@property
|
|
694
|
+
@api_telemetry
|
|
695
|
+
def binary_output_format(self) -> str | None:
|
|
696
|
+
"""The session's ``BINARY_OUTPUT_FORMAT`` parameter value (``HEX`` or ``BASE64``)."""
|
|
697
|
+
return self._connection._session_parameters._get_string("BINARY_OUTPUT_FORMAT")
|
|
698
|
+
|
|
699
|
+
# ------------------------------------------------------------------
|
|
700
|
+
# Error handling
|
|
701
|
+
# ------------------------------------------------------------------
|
|
702
|
+
|
|
703
|
+
@property
|
|
704
|
+
@pep249
|
|
705
|
+
@api_telemetry
|
|
706
|
+
def errorhandler(self) -> StructuredErrorHandler:
|
|
707
|
+
"""PEP 249 error handler for this cursor."""
|
|
708
|
+
return self._errorhandler
|
|
709
|
+
|
|
710
|
+
@errorhandler.setter
|
|
711
|
+
@api_telemetry
|
|
712
|
+
def errorhandler(self, value: StructuredErrorHandler | None) -> None:
|
|
713
|
+
if value is None:
|
|
714
|
+
raise ProgrammingError("Invalid errorhandler is specified")
|
|
715
|
+
self._errorhandler = value
|
|
716
|
+
|
|
717
|
+
def _resolve_force_microsecond_precision(self, override: bool | None) -> bool:
|
|
718
|
+
if override is None:
|
|
719
|
+
return bool(self._connection.force_microsecond_precision)
|
|
720
|
+
return bool(override)
|
|
721
|
+
|
|
722
|
+
# ------------------------------------------------------------------
|
|
723
|
+
# Optional dependency checks
|
|
724
|
+
# ------------------------------------------------------------------
|
|
725
|
+
|
|
726
|
+
@api_telemetry
|
|
727
|
+
def check_can_use_arrow_resultset(self) -> None:
|
|
728
|
+
check_dependency(pyarrow)
|
|
729
|
+
|
|
730
|
+
@api_telemetry
|
|
731
|
+
def check_can_use_pandas(self) -> None:
|
|
732
|
+
check_dependency(pandas)
|