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,47 @@
|
|
|
1
|
+
"""Shared protocols for Arrow row and table iterators (Cython nanoarrow and native PyO3)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import AsyncIterator
|
|
6
|
+
from typing import Any, Protocol
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ArrowRowIterator(Protocol):
|
|
10
|
+
"""Row iterator produced by ``create_row_iterator``.
|
|
11
|
+
|
|
12
|
+
Both the Cython ``ArrowStreamIterator`` and the PyO3
|
|
13
|
+
``sf_core_python.ArrowStreamIterator`` (``native-arrow`` builds) satisfy
|
|
14
|
+
this protocol, so call sites do not branch on the backend.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __iter__(self) -> ArrowRowIterator: ...
|
|
18
|
+
def __next__(self) -> Any: ...
|
|
19
|
+
def fetch_many(self, size: int) -> list[Any]: ...
|
|
20
|
+
def fetch_all(self) -> list[Any]: ...
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AsyncArrowRowIterator(Protocol):
|
|
24
|
+
"""Async row iterator used by the aio cursor and ResultBatch.
|
|
25
|
+
|
|
26
|
+
The ``to_thread`` wrapper over a sync iterator and the driver over the
|
|
27
|
+
PyO3 batch reader (``native-arrow`` builds) both satisfy this protocol.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __aiter__(self) -> AsyncIterator[Any]: ...
|
|
31
|
+
async def __anext__(self) -> Any: ...
|
|
32
|
+
async def fetch_next(self, default: object = None) -> Any: ...
|
|
33
|
+
async def fetch_many(self, size: int) -> list[Any]: ...
|
|
34
|
+
async def fetch_all(self) -> list[Any]: ...
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ArrowTableIterator(Protocol):
|
|
38
|
+
"""RecordBatch iterator produced by ``create_table_iterator``.
|
|
39
|
+
|
|
40
|
+
Both the Cython ``ArrowStreamTableIterator`` and the PyO3
|
|
41
|
+
``sf_core_python.ArrowStreamTableIterator`` (``native-arrow`` builds)
|
|
42
|
+
satisfy this protocol, so call sites do not branch on the backend.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __iter__(self) -> ArrowTableIterator: ...
|
|
46
|
+
def __next__(self) -> Any: ...
|
|
47
|
+
def get_converted_schema(self) -> Any: ...
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""Arrow converter context for type conversions.
|
|
2
|
+
|
|
3
|
+
This module is based on the original snowflake-connector-python arrow_context.py
|
|
4
|
+
and provides Python helper functions for Arrow conversions in the C++ converters.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import decimal
|
|
10
|
+
|
|
11
|
+
from datetime import datetime, timedelta, timezone, tzinfo
|
|
12
|
+
from sys import byteorder
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
14
|
+
|
|
15
|
+
import pytz
|
|
16
|
+
|
|
17
|
+
from .logging import get_logger
|
|
18
|
+
from .._common.extras import numpy as np
|
|
19
|
+
from .._common.extras import tzlocal, MissingOptionalDependency
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from numpy import datetime64, float64, int64, timedelta64
|
|
23
|
+
|
|
24
|
+
from ..aio.connection import Connection as AsyncConnection
|
|
25
|
+
from ..connection import Connection
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
ZERO_EPOCH = datetime.fromtimestamp(0, timezone.utc).replace(tzinfo=None)
|
|
29
|
+
PARAMETER_TIMEZONE = "TIMEZONE"
|
|
30
|
+
logger = get_logger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _generate_tzinfo_from_tzoffset(tzoffset_minutes: int) -> tzinfo:
|
|
34
|
+
"""Generate tzinfo object from tzoffset."""
|
|
35
|
+
return pytz.FixedOffset(tzoffset_minutes)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def interval_year_month_to_string(interval: int, scale: int) -> str:
|
|
39
|
+
"""Convert a year-month interval to a string.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
interval: The year-month interval value in months.
|
|
43
|
+
scale: The scale of the interval which represents subtype as follows:
|
|
44
|
+
0: INTERVAL YEAR TO MONTH
|
|
45
|
+
1: INTERVAL YEAR
|
|
46
|
+
2: INTERVAL MONTH
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
The string representation of the interval.
|
|
50
|
+
"""
|
|
51
|
+
sign = "+" if interval >= 0 else "-"
|
|
52
|
+
interval = abs(interval)
|
|
53
|
+
if scale == 2: # INTERVAL MONTH
|
|
54
|
+
return f"{sign}{interval}"
|
|
55
|
+
years = interval // 12
|
|
56
|
+
if scale == 1: # INTERVAL YEAR
|
|
57
|
+
return f"{sign}{years}"
|
|
58
|
+
# INTERVAL YEAR TO MONTH
|
|
59
|
+
months = interval % 12
|
|
60
|
+
return f"{sign}{years}-{months:02}"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ArrowConverterContext:
|
|
64
|
+
"""Python helper functions for arrow conversions.
|
|
65
|
+
|
|
66
|
+
Windows timestamp functions are necessary because Windows cannot handle -ve timestamps.
|
|
67
|
+
Putting the OS check into the non-windows function would probably take up more CPU cycles then
|
|
68
|
+
just deciding this at compile time.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def __init__(
|
|
72
|
+
self,
|
|
73
|
+
timezone: str | None = None,
|
|
74
|
+
*,
|
|
75
|
+
timezone_supplier: Callable[[], str | None] | None = None,
|
|
76
|
+
) -> None:
|
|
77
|
+
# An explicit ``timezone`` takes precedence and marks the value resolved.
|
|
78
|
+
# Otherwise, ``timezone_supplier`` (if any) is invoked lazily on first access.
|
|
79
|
+
self._tz_value = timezone
|
|
80
|
+
self._tz_supplier = timezone_supplier if timezone is None else None
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def create(cls, connection: Connection | AsyncConnection) -> ArrowConverterContext:
|
|
84
|
+
"""Create a context whose timezone resolves lazily from the session."""
|
|
85
|
+
return cls(
|
|
86
|
+
timezone_supplier=lambda: connection._session_parameters._get_string(PARAMETER_TIMEZONE),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def _timezone(self) -> str | None:
|
|
91
|
+
"""Resolved session timezone, invoking the supplier once on first read.
|
|
92
|
+
|
|
93
|
+
Named ``_timezone`` because the C++ table iterator reads this attribute
|
|
94
|
+
directly via ``PyObject_GetAttrString(context, "_timezone")``.
|
|
95
|
+
"""
|
|
96
|
+
supplier = self._tz_supplier
|
|
97
|
+
if supplier is not None:
|
|
98
|
+
self._tz_value = supplier()
|
|
99
|
+
self._tz_supplier = None
|
|
100
|
+
return self._tz_value
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def timezone(self) -> str | None:
|
|
104
|
+
return self._timezone
|
|
105
|
+
|
|
106
|
+
@timezone.setter
|
|
107
|
+
def timezone(self, tz: str | None) -> None:
|
|
108
|
+
self._tz_value = tz
|
|
109
|
+
self._tz_supplier = None
|
|
110
|
+
|
|
111
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
112
|
+
# resolve timezone before pickling
|
|
113
|
+
return {"timezone": self._timezone}
|
|
114
|
+
|
|
115
|
+
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
116
|
+
self._tz_value = state.get("timezone")
|
|
117
|
+
self._tz_supplier = None
|
|
118
|
+
|
|
119
|
+
def _get_session_tz(self) -> tzinfo:
|
|
120
|
+
"""Get the session timezone or use the local computer's timezone."""
|
|
121
|
+
try:
|
|
122
|
+
tz = "UTC" if not self.timezone else str(self.timezone)
|
|
123
|
+
return pytz.timezone(tz)
|
|
124
|
+
except pytz.exceptions.UnknownTimeZoneError:
|
|
125
|
+
logger.warning("converting to tzinfo failed")
|
|
126
|
+
if not isinstance(tzlocal, MissingOptionalDependency):
|
|
127
|
+
return tzlocal.get_localzone() # type: ignore[no-any-return]
|
|
128
|
+
else:
|
|
129
|
+
return timezone.utc
|
|
130
|
+
|
|
131
|
+
def TIMESTAMP_TZ_to_python(self, epoch: int, microseconds: int, tz: int) -> datetime:
|
|
132
|
+
tzinfo = _generate_tzinfo_from_tzoffset(tz - 1440)
|
|
133
|
+
return datetime.fromtimestamp(epoch, tz=tzinfo) + timedelta(microseconds=microseconds)
|
|
134
|
+
|
|
135
|
+
def TIMESTAMP_TZ_to_python_windows(self, epoch: int, microseconds: int, tz: int) -> datetime:
|
|
136
|
+
tzinfo = _generate_tzinfo_from_tzoffset(tz - 1440)
|
|
137
|
+
t = ZERO_EPOCH + timedelta(seconds=epoch, microseconds=microseconds)
|
|
138
|
+
if pytz.utc != tzinfo:
|
|
139
|
+
offset = tzinfo.utcoffset(t)
|
|
140
|
+
if offset is not None:
|
|
141
|
+
t += offset
|
|
142
|
+
return t.replace(tzinfo=tzinfo)
|
|
143
|
+
|
|
144
|
+
def TIMESTAMP_NTZ_to_python(self, epoch: int, microseconds: int) -> datetime:
|
|
145
|
+
return datetime.fromtimestamp(epoch, timezone.utc).replace(tzinfo=None) + timedelta(microseconds=microseconds)
|
|
146
|
+
|
|
147
|
+
def TIMESTAMP_NTZ_to_python_windows(self, epoch: int, microseconds: int) -> datetime:
|
|
148
|
+
return ZERO_EPOCH + timedelta(seconds=epoch, microseconds=microseconds)
|
|
149
|
+
|
|
150
|
+
def TIMESTAMP_LTZ_to_python(self, epoch: int, microseconds: int) -> datetime:
|
|
151
|
+
tzinfo = self._get_session_tz()
|
|
152
|
+
return datetime.fromtimestamp(epoch, tz=tzinfo) + timedelta(microseconds=microseconds)
|
|
153
|
+
|
|
154
|
+
def TIMESTAMP_LTZ_to_python_windows(self, epoch: int, microseconds: int) -> datetime:
|
|
155
|
+
tzinfo = self._get_session_tz()
|
|
156
|
+
ts = ZERO_EPOCH + timedelta(seconds=epoch, microseconds=microseconds)
|
|
157
|
+
return pytz.utc.localize(ts, is_dst=False).astimezone(tzinfo)
|
|
158
|
+
|
|
159
|
+
def REAL_to_numpy_float64(self, py_double: float) -> float64:
|
|
160
|
+
return np.float64(py_double) # type: ignore[no-any-return]
|
|
161
|
+
|
|
162
|
+
def FIXED_to_numpy_int64(self, py_long: int) -> int64:
|
|
163
|
+
return np.int64(py_long) # type: ignore[no-any-return]
|
|
164
|
+
|
|
165
|
+
def FIXED_to_numpy_float64(self, py_long: int, scale: int) -> float64:
|
|
166
|
+
return np.float64(decimal.Decimal(py_long).scaleb(-scale)) # type: ignore[no-any-return]
|
|
167
|
+
|
|
168
|
+
def DATE_to_numpy_datetime64(self, py_days: int) -> datetime64:
|
|
169
|
+
return np.datetime64(py_days, "D") # type: ignore[no-any-return]
|
|
170
|
+
|
|
171
|
+
def TIMESTAMP_NTZ_ONE_FIELD_to_numpy_datetime64(self, value: int, scale: int) -> datetime64:
|
|
172
|
+
nanoseconds = int(decimal.Decimal(value).scaleb(9 - scale))
|
|
173
|
+
return np.datetime64(nanoseconds, "ns") # type: ignore[no-any-return]
|
|
174
|
+
|
|
175
|
+
def TIMESTAMP_NTZ_TWO_FIELD_to_numpy_datetime64(self, epoch: int, fraction: int) -> datetime64:
|
|
176
|
+
nanoseconds = int(decimal.Decimal(epoch).scaleb(9) + decimal.Decimal(fraction))
|
|
177
|
+
return np.datetime64(nanoseconds, "ns") # type: ignore[no-any-return]
|
|
178
|
+
|
|
179
|
+
def DECIMAL128_to_decimal_or_int(self, int128_bytes: bytes, scale: int) -> decimal.Decimal | int:
|
|
180
|
+
"""When scale=0 (integer), returns Python int. When scale>0 (decimal), returns Python Decimal.
|
|
181
|
+
|
|
182
|
+
Large scale integers (int128) fall back here as they're not supported by native C++ conversion.
|
|
183
|
+
"""
|
|
184
|
+
int128 = int.from_bytes(int128_bytes, byteorder=byteorder, signed=True)
|
|
185
|
+
if scale == 0:
|
|
186
|
+
return int128
|
|
187
|
+
digits = [int(digit) for digit in str(int128) if digit != "-"]
|
|
188
|
+
sign = int128 < 0
|
|
189
|
+
return decimal.Decimal((sign, digits, -scale))
|
|
190
|
+
|
|
191
|
+
def DECFLOAT_to_decimal(self, exponent: int, significand: bytes) -> decimal.Decimal:
|
|
192
|
+
# significand is two's complement big endian.
|
|
193
|
+
significand_int = int.from_bytes(significand, byteorder="big", signed=True)
|
|
194
|
+
return decimal.Decimal(significand_int).scaleb(exponent)
|
|
195
|
+
|
|
196
|
+
def DECFLOAT_to_numpy_float64(self, exponent: int, significand: bytes) -> float64:
|
|
197
|
+
return np.float64(self.DECFLOAT_to_decimal(exponent, significand)) # type: ignore[no-any-return]
|
|
198
|
+
|
|
199
|
+
def INTERVAL_YEAR_MONTH_to_str(self, months: int, scale: int) -> str:
|
|
200
|
+
return interval_year_month_to_string(months, scale)
|
|
201
|
+
|
|
202
|
+
def INTERVAL_YEAR_MONTH_to_numpy_timedelta(self, months: int, scale: int) -> timedelta64:
|
|
203
|
+
if scale == 1:
|
|
204
|
+
return np.timedelta64(months // 12, "Y") # type: ignore[no-any-return]
|
|
205
|
+
return np.timedelta64(months, "M") # type: ignore[no-any-return]
|
|
206
|
+
|
|
207
|
+
def INTERVAL_DAY_TIME_int_to_numpy_timedelta(self, nanos: int) -> timedelta64:
|
|
208
|
+
return np.timedelta64(nanos, "ns") # type: ignore[no-any-return]
|
|
209
|
+
|
|
210
|
+
def INTERVAL_DAY_TIME_int_to_timedelta(self, nanos: int) -> timedelta:
|
|
211
|
+
# Python timedelta only supports microsecond precision. We receive value in
|
|
212
|
+
# nanoseconds.
|
|
213
|
+
return timedelta(microseconds=nanos // 1000)
|
|
214
|
+
|
|
215
|
+
def INTERVAL_DAY_TIME_decimal_to_numpy_timedelta(self, value: bytes) -> timedelta64:
|
|
216
|
+
# Snowflake supports up to 9 digits leading field precision for the day-time
|
|
217
|
+
# interval. That when represented in nanoseconds can not be stored in a 64-bit
|
|
218
|
+
# integer. So we send these as Decimal128 from server to client.
|
|
219
|
+
# Arrow uses little-endian by default.
|
|
220
|
+
# https://arrow.apache.org/docs/format/Columnar.html#byte-order-endianness
|
|
221
|
+
nanos = int.from_bytes(value, byteorder="little", signed=True)
|
|
222
|
+
# Numpy timedelta only supports up to 64-bit integers, so we need to change the
|
|
223
|
+
# unit to milliseconds to avoid overflow.
|
|
224
|
+
# Max value received from server
|
|
225
|
+
# = 10**9 * NANOS_PER_DAY - 1
|
|
226
|
+
# = 86399999999999999999999 nanoseconds
|
|
227
|
+
# = 86399999999999999 milliseconds
|
|
228
|
+
# math.log2(86399999999999999) = 56.3 < 64
|
|
229
|
+
return np.timedelta64(nanos // 1_000_000, "ms") # type: ignore[no-any-return]
|
|
230
|
+
|
|
231
|
+
def INTERVAL_DAY_TIME_decimal_to_timedelta(self, value: bytes) -> timedelta:
|
|
232
|
+
# Snowflake supports up to 9 digits leading field precision for the day-time
|
|
233
|
+
# interval. That when represented in nanoseconds can not be stored in a 64-bit
|
|
234
|
+
# integer. So we send these as Decimal128 from server to client.
|
|
235
|
+
# Arrow uses little-endian by default.
|
|
236
|
+
# https://arrow.apache.org/docs/format/Columnar.html#byte-order-endianness
|
|
237
|
+
nanos = int.from_bytes(value, byteorder="little", signed=True)
|
|
238
|
+
# Python timedelta only supports microsecond precision. We receive value in
|
|
239
|
+
# nanoseconds.
|
|
240
|
+
return timedelta(microseconds=nanos // 1000)
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Async adapters over the synchronous nanoarrow iterators.
|
|
2
|
+
|
|
3
|
+
Why every advance goes through ``asyncio.to_thread``
|
|
4
|
+
----------------------------------------------------
|
|
5
|
+
The Arrow stream iterators (:class:`ArrowStreamIterator`,
|
|
6
|
+
the table iterator from :func:`create_table_iterator`) and the helpers in :mod:`arrow_stream_utils`
|
|
7
|
+
expose a purely synchronous API: there is no native async entry point. A single
|
|
8
|
+
``next()`` does two kinds of work, and both are unfriendly to the event loop:
|
|
9
|
+
|
|
10
|
+
1. **Thread-blocking I/O at chunk boundaries.** Under the hood the core builds a
|
|
11
|
+
prefetching reader whose ``next()`` calls ``blocking_recv()`` on a channel:
|
|
12
|
+
it parks the *calling OS thread* until a background task has finished
|
|
13
|
+
downloading and parsing the next result chunk. The downloads themselves run
|
|
14
|
+
on the core's own (multi-threaded Tokio) runtime, so they do **not** depend
|
|
15
|
+
on the Python event loop, but the consuming ``next()`` still blocks its
|
|
16
|
+
thread waiting for them.
|
|
17
|
+
2. **GIL-bound CPU work.** Decoding a row/batch into Python objects (building
|
|
18
|
+
tuples/dicts, running the per-column converters) holds the GIL the entire
|
|
19
|
+
time. Only the chunk-boundary fetch above releases the GIL; the decode does
|
|
20
|
+
not.
|
|
21
|
+
|
|
22
|
+
If we called ``next()`` directly inside a coroutine, both of those would run on
|
|
23
|
+
the event-loop thread: chunk-boundary advances would block the loop on network
|
|
24
|
+
I/O, and every decode would hold the GIL, so no other task could make progress.
|
|
25
|
+
This does *not* deadlock (the downloads complete independently on the core
|
|
26
|
+
runtime), but it defeats the point of an async API. Offloading to a worker
|
|
27
|
+
thread via :func:`asyncio.to_thread` keeps the loop free while a blocking
|
|
28
|
+
advance is in flight.
|
|
29
|
+
|
|
30
|
+
So why offload *every* call rather than just the slow ones? From Python there
|
|
31
|
+
is no reliable way to tell a cheap advance (popping an already-buffered row)
|
|
32
|
+
from an expensive one (a chunk-boundary ``blocking_recv`` or a heavy decode), so
|
|
33
|
+
the uniform, safe choice is to offload them all and accept a thread handoff on
|
|
34
|
+
the cheap ones.
|
|
35
|
+
|
|
36
|
+
Possible modifications (intentionally not done yet)
|
|
37
|
+
---------------------------------------------------
|
|
38
|
+
The row path pays this hop *per row*: ``__anext__`` / :meth:`fetch_next` offload
|
|
39
|
+
a single ``next()`` each, so a large result set incurs one ``to_thread`` hop
|
|
40
|
+
(and a round of GIL ping-pong) for every row. ``fetch_many`` / ``fetch_all``
|
|
41
|
+
already avoid this by batching many rows into one native call under a single
|
|
42
|
+
hop. Row iteration could be made similarly cheap by backing ``__anext__`` with
|
|
43
|
+
an internal buffer refilled via :meth:`fetch_many` under one hop, instead of one
|
|
44
|
+
hop per row.
|
|
45
|
+
|
|
46
|
+
Alternatively, if performance tests show the offload is unnecessary for the
|
|
47
|
+
common case (e.g. the loop-blocking cost is negligible in practice), the advance
|
|
48
|
+
could simply be a plain synchronous ``next()`` call with no ``to_thread`` hop at
|
|
49
|
+
all. Left as-is for now; revisit either way if async iteration shows up as a
|
|
50
|
+
bottleneck.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
This module centralizes those ``to_thread`` hops behind small async wrappers so
|
|
54
|
+
the async cursor and async result batch share one implementation (and one
|
|
55
|
+
place documenting this rationale) instead of repeating
|
|
56
|
+
``await asyncio.to_thread(...)`` at every call site.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
from __future__ import annotations
|
|
60
|
+
|
|
61
|
+
import asyncio
|
|
62
|
+
|
|
63
|
+
from collections.abc import AsyncIterator, Sequence
|
|
64
|
+
from typing import TYPE_CHECKING, Any, Protocol
|
|
65
|
+
|
|
66
|
+
from snowflake.connector._core import sf_core_python
|
|
67
|
+
|
|
68
|
+
from .arrow_stream_utils import (
|
|
69
|
+
_missing_native_class,
|
|
70
|
+
collect_arrow_table,
|
|
71
|
+
create_row_iterator,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if TYPE_CHECKING:
|
|
76
|
+
from pyarrow import Table
|
|
77
|
+
|
|
78
|
+
from .arrow import ArrowRowIterator, ArrowTableIterator, AsyncArrowRowIterator
|
|
79
|
+
from .arrow_context import ArrowConverterContext
|
|
80
|
+
from .cursor.result_metadata import ResultMetadata
|
|
81
|
+
from .protobuf_gen.database_driver_v1_pb2 import ResultSetHandle
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# Sentinel handed to ``next(iterator, default)`` so a ``StopIteration`` raised
|
|
85
|
+
# inside a worker thread never has to cross the ``to_thread`` boundary (where it
|
|
86
|
+
# would be wrapped in confusing ways). The caller compares identity against it.
|
|
87
|
+
_ITER_DONE = object()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _next_or_default(iterator: Any, default: object) -> Any:
|
|
91
|
+
"""Typed ``next(iterator, default)`` suitable for :func:`asyncio.to_thread`.
|
|
92
|
+
|
|
93
|
+
The builtin ``next`` is overloaded, which mypy refuses to pass directly to
|
|
94
|
+
``to_thread``. This thin wrapper gives it a concrete, checkable signature.
|
|
95
|
+
"""
|
|
96
|
+
return next(iterator, default)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class AsyncArrowStreamIterator(AsyncIterator[Any]):
|
|
100
|
+
"""Async facade over a synchronous nanoarrow iterator.
|
|
101
|
+
|
|
102
|
+
Wraps an :class:`ArrowRowIterator` (row iterator) or an
|
|
103
|
+
:class:`ArrowTableIterator` (RecordBatch iterator) and exposes async
|
|
104
|
+
iteration plus bulk ``fetch_many`` / ``fetch_all`` helpers. Every advance of
|
|
105
|
+
the underlying iterator runs on a worker thread via :func:`asyncio.to_thread`
|
|
106
|
+
(see module docstring for why).
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
def __init__(
|
|
110
|
+
self,
|
|
111
|
+
iterator: ArrowRowIterator | ArrowTableIterator,
|
|
112
|
+
) -> None:
|
|
113
|
+
self._iterator = iterator
|
|
114
|
+
|
|
115
|
+
def __aiter__(self) -> AsyncArrowStreamIterator:
|
|
116
|
+
return self
|
|
117
|
+
|
|
118
|
+
async def __anext__(self) -> Any:
|
|
119
|
+
item = await asyncio.to_thread(_next_or_default, self._iterator, _ITER_DONE)
|
|
120
|
+
if item is _ITER_DONE:
|
|
121
|
+
raise StopAsyncIteration
|
|
122
|
+
return item
|
|
123
|
+
|
|
124
|
+
async def fetch_next(self, default: object = None) -> Any:
|
|
125
|
+
"""Return the next item, or *default* when the stream is exhausted.
|
|
126
|
+
|
|
127
|
+
Unlike :meth:`__anext__` this never raises ``StopAsyncIteration``; it
|
|
128
|
+
is the async analogue of ``next(it, default)`` and is convenient for
|
|
129
|
+
manual fetch loops (e.g. ``fetchone``).
|
|
130
|
+
"""
|
|
131
|
+
item = await asyncio.to_thread(_next_or_default, self._iterator, _ITER_DONE)
|
|
132
|
+
return default if item is _ITER_DONE else item
|
|
133
|
+
|
|
134
|
+
async def fetch_many(self, size: int) -> list[Any]:
|
|
135
|
+
"""Fetch up to *size* rows in a single threaded C++ call."""
|
|
136
|
+
return await asyncio.to_thread(self._iterator.fetch_many, size) # type: ignore[union-attr]
|
|
137
|
+
|
|
138
|
+
async def fetch_all(self) -> list[Any]:
|
|
139
|
+
"""Fetch all remaining rows in a single threaded C++ call."""
|
|
140
|
+
return await asyncio.to_thread(self._iterator.fetch_all) # type: ignore[union-attr]
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class NativeArrowBatchReader(Protocol):
|
|
144
|
+
"""The ``native-arrow`` reader: one awaitable for I/O, the rest synchronous."""
|
|
145
|
+
|
|
146
|
+
async def next_batch(self) -> bool: ...
|
|
147
|
+
def take_row(self, default: object = None) -> Any: ...
|
|
148
|
+
def convert(self, count: int | None = None) -> int: ...
|
|
149
|
+
def take_converted(self) -> list[Any]: ...
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class NativeAsyncArrowRowIterator(AsyncIterator[Any]):
|
|
153
|
+
"""Row iterator over a :class:`NativeArrowBatchReader`.
|
|
154
|
+
|
|
155
|
+
Only ``next_batch`` suspends, and it does no Python-object work. Every row
|
|
156
|
+
is materialized by a synchronous call, so conversion runs on whichever
|
|
157
|
+
thread drives the coroutine — the event-loop thread — rather than on a
|
|
158
|
+
core runtime worker.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
def __init__(self, reader: NativeArrowBatchReader) -> None:
|
|
162
|
+
self._reader = reader
|
|
163
|
+
|
|
164
|
+
def __aiter__(self) -> NativeAsyncArrowRowIterator:
|
|
165
|
+
return self
|
|
166
|
+
|
|
167
|
+
async def __anext__(self) -> Any:
|
|
168
|
+
row = await self.fetch_next(_ITER_DONE)
|
|
169
|
+
if row is _ITER_DONE:
|
|
170
|
+
raise StopAsyncIteration
|
|
171
|
+
return row
|
|
172
|
+
|
|
173
|
+
async def fetch_next(self, default: object = None) -> Any:
|
|
174
|
+
while True:
|
|
175
|
+
row = self._reader.take_row(_ITER_DONE)
|
|
176
|
+
if row is not _ITER_DONE:
|
|
177
|
+
return row
|
|
178
|
+
if not await self._reader.next_batch():
|
|
179
|
+
return default
|
|
180
|
+
|
|
181
|
+
async def fetch_many(self, size: int) -> list[Any]:
|
|
182
|
+
remaining = size
|
|
183
|
+
while remaining > 0:
|
|
184
|
+
remaining -= self._reader.convert(remaining)
|
|
185
|
+
if remaining > 0 and not await self._reader.next_batch():
|
|
186
|
+
break
|
|
187
|
+
return self._reader.take_converted()
|
|
188
|
+
|
|
189
|
+
async def fetch_all(self) -> list[Any]:
|
|
190
|
+
while True:
|
|
191
|
+
self._reader.convert()
|
|
192
|
+
if not await self._reader.next_batch():
|
|
193
|
+
return self._reader.take_converted()
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
async def create_async_row_iterator_from_result_set(
|
|
197
|
+
result_set_handle: ResultSetHandle,
|
|
198
|
+
*,
|
|
199
|
+
context: ArrowConverterContext,
|
|
200
|
+
use_dict_result: bool = False,
|
|
201
|
+
use_numpy: bool = False,
|
|
202
|
+
) -> AsyncArrowRowIterator:
|
|
203
|
+
"""Yield rows from the live result set; upcoming chunks download in the background."""
|
|
204
|
+
iterator_cls = getattr(sf_core_python, "AsyncArrowStreamIterator", None)
|
|
205
|
+
if iterator_cls is None:
|
|
206
|
+
raise _missing_native_class("AsyncArrowStreamIterator")
|
|
207
|
+
reader = await iterator_cls.from_result_set(
|
|
208
|
+
result_set_handle.id,
|
|
209
|
+
result_set_handle.magic,
|
|
210
|
+
session_timezone=context.timezone,
|
|
211
|
+
use_dict_result=use_dict_result,
|
|
212
|
+
use_numpy=use_numpy,
|
|
213
|
+
)
|
|
214
|
+
return NativeAsyncArrowRowIterator(reader)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def create_async_row_iterator_from_stream_ptr(
|
|
218
|
+
stream_ptr: int,
|
|
219
|
+
*,
|
|
220
|
+
context: ArrowConverterContext,
|
|
221
|
+
use_dict_result: bool = False,
|
|
222
|
+
use_numpy: bool = False,
|
|
223
|
+
use_core_arrow: bool = False,
|
|
224
|
+
) -> AsyncArrowRowIterator:
|
|
225
|
+
"""Wrap the sync iterator in ``asyncio.to_thread``.
|
|
226
|
+
|
|
227
|
+
Used when native-arrow is off, and for ResultBatches that still
|
|
228
|
+
arrive as a C stream pointer.
|
|
229
|
+
"""
|
|
230
|
+
return AsyncArrowStreamIterator(
|
|
231
|
+
create_row_iterator(
|
|
232
|
+
stream_ptr,
|
|
233
|
+
context=context,
|
|
234
|
+
use_dict_result=use_dict_result,
|
|
235
|
+
use_numpy=use_numpy,
|
|
236
|
+
use_core_arrow=use_core_arrow,
|
|
237
|
+
)
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
async def collect_arrow_table_async(
|
|
242
|
+
table_iterator: ArrowTableIterator,
|
|
243
|
+
columns_metadata: Sequence[ResultMetadata] | None = None,
|
|
244
|
+
force_return_table: bool = False,
|
|
245
|
+
) -> Table | None:
|
|
246
|
+
"""Async wrapper for :func:`collect_arrow_table`.
|
|
247
|
+
|
|
248
|
+
Draining *table_iterator* into a single Arrow table runs every blocking
|
|
249
|
+
``next()`` plus the final ``Table.from_batches`` on a worker thread.
|
|
250
|
+
"""
|
|
251
|
+
return await asyncio.to_thread(
|
|
252
|
+
collect_arrow_table,
|
|
253
|
+
table_iterator,
|
|
254
|
+
columns_metadata,
|
|
255
|
+
force_return_table,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
async def to_pandas_async(table: Table, **kwargs: Any) -> Any:
|
|
260
|
+
"""Convert an Arrow table to a pandas DataFrame off the event loop.
|
|
261
|
+
|
|
262
|
+
``Table.to_pandas`` is CPU-heavy C/Cython work with no async entry point, so
|
|
263
|
+
it is offloaded for the same reason as the fetch path. ``**kwargs`` are
|
|
264
|
+
forwarded to ``Table.to_pandas`` (e.g. ``split_blocks``, ``self_destruct``).
|
|
265
|
+
"""
|
|
266
|
+
return await asyncio.to_thread(table.to_pandas, **kwargs)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Type stubs for the Cython arrow_stream_iterator extension module.
|
|
2
|
+
|
|
3
|
+
The actual implementation is compiled from
|
|
4
|
+
nanoarrow_cpp/ArrowIterator/arrow_stream_iterator.pyx at wheel-build time.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Iterator
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
class ArrowStreamIterator(Iterator[Any]):
|
|
13
|
+
"""Python wrapper for C++ Arrow stream iterator.
|
|
14
|
+
|
|
15
|
+
Reads directly from an ArrowArrayStream pointer.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
stream_ptr: int,
|
|
21
|
+
arrow_context: Any,
|
|
22
|
+
use_dict_result: bool = False,
|
|
23
|
+
use_numpy: bool = False,
|
|
24
|
+
) -> None: ...
|
|
25
|
+
def __iter__(self) -> ArrowStreamIterator: ...
|
|
26
|
+
def __next__(self) -> Any: ...
|
|
27
|
+
def fetch_many(self, size: int) -> list[Any]: ...
|
|
28
|
+
def fetch_all(self) -> list[Any]: ...
|
|
29
|
+
|
|
30
|
+
class ArrowStreamTableIterator(Iterator[Any]):
|
|
31
|
+
"""Iterator that yields one pyarrow RecordBatch per batch with Snowflake type conversions applied."""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
stream_ptr: int,
|
|
36
|
+
arrow_context: Any,
|
|
37
|
+
number_to_decimal: bool = False,
|
|
38
|
+
force_microsecond_precision: bool = False,
|
|
39
|
+
) -> None: ...
|
|
40
|
+
def __iter__(self) -> ArrowStreamTableIterator: ...
|
|
41
|
+
def __next__(self) -> Any: ...
|
|
42
|
+
def get_converted_schema(self) -> Any: ...
|