snowflake-connector-python 3.16.0__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- snowflake/connector/__init__.py +96 -0
- snowflake/connector/_query_context_cache.py +276 -0
- snowflake/connector/_sql_util.py +48 -0
- snowflake/connector/_utils.py +70 -0
- snowflake/connector/arrow_context.py +203 -0
- snowflake/connector/auth/__init__.py +53 -0
- snowflake/connector/auth/_auth.py +586 -0
- snowflake/connector/auth/_http_server.py +220 -0
- snowflake/connector/auth/_oauth_base.py +400 -0
- snowflake/connector/auth/by_plugin.py +219 -0
- snowflake/connector/auth/default.py +36 -0
- snowflake/connector/auth/idtoken.py +72 -0
- snowflake/connector/auth/keypair.py +222 -0
- snowflake/connector/auth/no_auth.py +39 -0
- snowflake/connector/auth/oauth.py +49 -0
- snowflake/connector/auth/oauth_code.py +479 -0
- snowflake/connector/auth/oauth_credentials.py +66 -0
- snowflake/connector/auth/okta.py +335 -0
- snowflake/connector/auth/pat.py +39 -0
- snowflake/connector/auth/usrpwdmfa.py +69 -0
- snowflake/connector/auth/webbrowser.py +502 -0
- snowflake/connector/auth/workload_identity.py +94 -0
- snowflake/connector/azure_storage_client.py +276 -0
- snowflake/connector/backoff_policies.py +141 -0
- snowflake/connector/bind_upload_agent.py +89 -0
- snowflake/connector/cache.py +696 -0
- snowflake/connector/compat.py +127 -0
- snowflake/connector/config_manager.py +496 -0
- snowflake/connector/connection.py +2273 -0
- snowflake/connector/connection_diagnostic.py +776 -0
- snowflake/connector/constants.py +442 -0
- snowflake/connector/converter.py +785 -0
- snowflake/connector/converter_issue23517.py +87 -0
- snowflake/connector/converter_null.py +14 -0
- snowflake/connector/converter_snowsql.py +205 -0
- snowflake/connector/cursor.py +1951 -0
- snowflake/connector/dbapi.py +53 -0
- snowflake/connector/description.py +19 -0
- snowflake/connector/direct_file_operation_utils.py +88 -0
- snowflake/connector/encryption_util.py +220 -0
- snowflake/connector/errorcode.py +91 -0
- snowflake/connector/errors.py +615 -0
- snowflake/connector/externals_utils/__init__.py +0 -0
- snowflake/connector/externals_utils/externals_setup.py +27 -0
- snowflake/connector/feature.py +4 -0
- snowflake/connector/file_compression_type.py +118 -0
- snowflake/connector/file_lock.py +72 -0
- snowflake/connector/file_transfer_agent.py +1215 -0
- snowflake/connector/file_util.py +153 -0
- snowflake/connector/gcs_storage_client.py +474 -0
- snowflake/connector/gzip_decoder.py +85 -0
- snowflake/connector/local_storage_client.py +90 -0
- snowflake/connector/log_configuration.py +60 -0
- snowflake/connector/logging_utils/__init__.py +0 -0
- snowflake/connector/logging_utils/filters.py +72 -0
- snowflake/connector/nanoarrow_arrow_iterator.cp313-win_amd64.pyd +0 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +60 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.cpp +557 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.hpp +98 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +125 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +115 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.cpp +1005 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.hpp +127 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +47 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +83 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +97 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +72 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +73 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +30 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +45 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +71 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +56 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +75 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +46 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +95 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +57 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +36 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +34 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +36 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +346 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +145 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.cpp +65 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +101 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +127 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1908 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +215 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +125 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +55 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +148 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +162 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +97 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +239 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +210 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +84 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +84 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +206 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +118 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +162 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +898 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +190 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc.c +3204 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3217 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3618 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +379 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_arrow_iterator.pyx +256 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.c +512 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.h +350 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +33273 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +397 -0
- snowflake/connector/nanoarrow_cpp/Logging/logging.cpp +100 -0
- snowflake/connector/nanoarrow_cpp/Logging/logging.hpp +49 -0
- snowflake/connector/network.py +1297 -0
- snowflake/connector/ocsp_asn1crypto.py +447 -0
- snowflake/connector/ocsp_snowflake.py +1883 -0
- snowflake/connector/options.py +131 -0
- snowflake/connector/pandas_tools.py +732 -0
- snowflake/connector/proxy.py +43 -0
- snowflake/connector/py.typed +0 -0
- snowflake/connector/result_batch.py +786 -0
- snowflake/connector/result_set.py +319 -0
- snowflake/connector/s3_storage_client.py +605 -0
- snowflake/connector/secret_detector.py +181 -0
- snowflake/connector/sf_dirs.py +64 -0
- snowflake/connector/sfbinaryformat.py +35 -0
- snowflake/connector/sfdatetime.py +345 -0
- snowflake/connector/snow_logging.py +144 -0
- snowflake/connector/sqlstate.py +9 -0
- snowflake/connector/ssd_internal_keys.py +30 -0
- snowflake/connector/ssl_wrap_socket.py +134 -0
- snowflake/connector/storage_client.py +478 -0
- snowflake/connector/telemetry.py +249 -0
- snowflake/connector/telemetry_oob.py +543 -0
- snowflake/connector/test_util.py +30 -0
- snowflake/connector/time_util.py +159 -0
- snowflake/connector/token_cache.py +402 -0
- snowflake/connector/tool/__init__.py +0 -0
- snowflake/connector/tool/dump_certs.py +57 -0
- snowflake/connector/tool/dump_ocsp_response.py +139 -0
- snowflake/connector/tool/dump_ocsp_response_cache.py +194 -0
- snowflake/connector/tool/probe_connection.py +69 -0
- snowflake/connector/url_util.py +49 -0
- snowflake/connector/util_text.py +301 -0
- snowflake/connector/vendored/__init__.py +3 -0
- snowflake/connector/vendored/requests/LICENSE +175 -0
- snowflake/connector/vendored/requests/__init__.py +169 -0
- snowflake/connector/vendored/requests/__version__.py +14 -0
- snowflake/connector/vendored/requests/_internal_utils.py +50 -0
- snowflake/connector/vendored/requests/adapters.py +537 -0
- snowflake/connector/vendored/requests/api.py +157 -0
- snowflake/connector/vendored/requests/auth.py +315 -0
- snowflake/connector/vendored/requests/certs.py +17 -0
- snowflake/connector/vendored/requests/compat.py +79 -0
- snowflake/connector/vendored/requests/cookies.py +561 -0
- snowflake/connector/vendored/requests/exceptions.py +140 -0
- snowflake/connector/vendored/requests/help.py +134 -0
- snowflake/connector/vendored/requests/hooks.py +33 -0
- snowflake/connector/vendored/requests/models.py +1033 -0
- snowflake/connector/vendored/requests/sessions.py +833 -0
- snowflake/connector/vendored/requests/status_codes.py +128 -0
- snowflake/connector/vendored/requests/structures.py +99 -0
- snowflake/connector/vendored/requests/utils.py +1093 -0
- snowflake/connector/vendored/urllib3/LICENSE.txt +21 -0
- snowflake/connector/vendored/urllib3/__init__.py +85 -0
- snowflake/connector/vendored/urllib3/_collections.py +355 -0
- snowflake/connector/vendored/urllib3/_version.py +2 -0
- snowflake/connector/vendored/urllib3/connection.py +572 -0
- snowflake/connector/vendored/urllib3/connectionpool.py +1137 -0
- snowflake/connector/vendored/urllib3/contrib/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/contrib/_appengine_environ.py +36 -0
- snowflake/connector/vendored/urllib3/contrib/_securetransport/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/contrib/_securetransport/bindings.py +519 -0
- snowflake/connector/vendored/urllib3/contrib/_securetransport/low_level.py +397 -0
- snowflake/connector/vendored/urllib3/contrib/appengine.py +314 -0
- snowflake/connector/vendored/urllib3/contrib/ntlmpool.py +130 -0
- snowflake/connector/vendored/urllib3/contrib/pyopenssl.py +509 -0
- snowflake/connector/vendored/urllib3/contrib/securetransport.py +920 -0
- snowflake/connector/vendored/urllib3/contrib/socks.py +216 -0
- snowflake/connector/vendored/urllib3/exceptions.py +323 -0
- snowflake/connector/vendored/urllib3/fields.py +274 -0
- snowflake/connector/vendored/urllib3/filepost.py +98 -0
- snowflake/connector/vendored/urllib3/packages/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/packages/backports/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/packages/backports/makefile.py +51 -0
- snowflake/connector/vendored/urllib3/packages/backports/weakref_finalize.py +155 -0
- snowflake/connector/vendored/urllib3/packages/six.py +1076 -0
- snowflake/connector/vendored/urllib3/poolmanager.py +540 -0
- snowflake/connector/vendored/urllib3/request.py +191 -0
- snowflake/connector/vendored/urllib3/response.py +885 -0
- snowflake/connector/vendored/urllib3/util/__init__.py +49 -0
- snowflake/connector/vendored/urllib3/util/connection.py +156 -0
- snowflake/connector/vendored/urllib3/util/proxy.py +57 -0
- snowflake/connector/vendored/urllib3/util/queue.py +22 -0
- snowflake/connector/vendored/urllib3/util/request.py +146 -0
- snowflake/connector/vendored/urllib3/util/response.py +107 -0
- snowflake/connector/vendored/urllib3/util/retry.py +620 -0
- snowflake/connector/vendored/urllib3/util/ssl_.py +495 -0
- snowflake/connector/vendored/urllib3/util/ssl_match_hostname.py +159 -0
- snowflake/connector/vendored/urllib3/util/ssltransport.py +221 -0
- snowflake/connector/vendored/urllib3/util/timeout.py +271 -0
- snowflake/connector/vendored/urllib3/util/url.py +435 -0
- snowflake/connector/vendored/urllib3/util/wait.py +152 -0
- snowflake/connector/version.py +3 -0
- snowflake/connector/wif_util.py +407 -0
- snowflake_connector_python-3.16.0.dist-info/METADATA +1475 -0
- snowflake_connector_python-3.16.0.dist-info/RECORD +246 -0
- snowflake_connector_python-3.16.0.dist-info/WHEEL +5 -0
- snowflake_connector_python-3.16.0.dist-info/entry_points.txt +4 -0
- snowflake_connector_python-3.16.0.dist-info/licenses/LICENSE.txt +202 -0
- snowflake_connector_python-3.16.0.dist-info/licenses/NOTICE +8 -0
- snowflake_connector_python-3.16.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import binascii
|
|
5
|
+
import decimal
|
|
6
|
+
import json
|
|
7
|
+
import time
|
|
8
|
+
from datetime import date, datetime
|
|
9
|
+
from datetime import time as dt_t
|
|
10
|
+
from datetime import timedelta, timezone, tzinfo
|
|
11
|
+
from functools import partial
|
|
12
|
+
from logging import getLogger
|
|
13
|
+
from math import ceil
|
|
14
|
+
from time import struct_time
|
|
15
|
+
from typing import TYPE_CHECKING, Any, Callable, NoReturn
|
|
16
|
+
|
|
17
|
+
import pytz
|
|
18
|
+
from pytz import UTC
|
|
19
|
+
|
|
20
|
+
from .compat import IS_BINARY, IS_NUMERIC
|
|
21
|
+
from .errorcode import ER_NOT_SUPPORT_DATA_TYPE
|
|
22
|
+
from .errors import ProgrammingError
|
|
23
|
+
from .sfbinaryformat import binary_to_python, binary_to_snowflake
|
|
24
|
+
from .sfdatetime import sfdatetime_total_seconds_from_timedelta
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from numpy import bool_, int64
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
import numpy
|
|
31
|
+
except ImportError:
|
|
32
|
+
numpy = None
|
|
33
|
+
try:
|
|
34
|
+
import tzlocal
|
|
35
|
+
except ImportError:
|
|
36
|
+
tzlocal = None
|
|
37
|
+
|
|
38
|
+
BITS_FOR_TIMEZONE = 14
|
|
39
|
+
ZERO_TIMEDELTA = timedelta(seconds=0)
|
|
40
|
+
ZERO_EPOCH_DATE = date(1970, 1, 1)
|
|
41
|
+
ZERO_EPOCH = datetime.fromtimestamp(0, timezone.utc).replace(tzinfo=None)
|
|
42
|
+
ZERO_FILL = "000000000"
|
|
43
|
+
|
|
44
|
+
logger = getLogger(__name__)
|
|
45
|
+
|
|
46
|
+
PYTHON_TO_SNOWFLAKE_TYPE = {
|
|
47
|
+
"int": "FIXED",
|
|
48
|
+
"long": "FIXED",
|
|
49
|
+
"decimal": "FIXED",
|
|
50
|
+
"float": "REAL",
|
|
51
|
+
"str": "TEXT",
|
|
52
|
+
"unicode": "TEXT",
|
|
53
|
+
"bytes": "BINARY",
|
|
54
|
+
"bytearray": "BINARY",
|
|
55
|
+
"bool": "BOOLEAN",
|
|
56
|
+
"bool_": "BOOLEAN",
|
|
57
|
+
"nonetype": "ANY",
|
|
58
|
+
"datetime": "TIMESTAMP_NTZ",
|
|
59
|
+
"sfdatetime": "TIMESTAMP_NTZ",
|
|
60
|
+
"date": "DATE",
|
|
61
|
+
"time": "TIME",
|
|
62
|
+
"struct_time": "TIMESTAMP_NTZ",
|
|
63
|
+
"timedelta": "TIME",
|
|
64
|
+
"list": "TEXT",
|
|
65
|
+
"tuple": "TEXT",
|
|
66
|
+
"int8": "FIXED",
|
|
67
|
+
"int16": "FIXED",
|
|
68
|
+
"int32": "FIXED",
|
|
69
|
+
"int64": "FIXED",
|
|
70
|
+
"uint8": "FIXED",
|
|
71
|
+
"uint16": "FIXED",
|
|
72
|
+
"uint32": "FIXED",
|
|
73
|
+
"uint64": "FIXED",
|
|
74
|
+
"float16": "REAL",
|
|
75
|
+
"float32": "REAL",
|
|
76
|
+
"float64": "REAL",
|
|
77
|
+
"datetime64": "TIMESTAMP_NTZ",
|
|
78
|
+
"quoted_name": "TEXT",
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Type alias
|
|
82
|
+
SnowflakeConverterType = Callable[[Any], Any]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def convert_datetime_to_epoch(dt: datetime) -> float:
|
|
86
|
+
"""Converts datetime to epoch time in seconds.
|
|
87
|
+
|
|
88
|
+
If Python > 3.3, you may use timestamp() method.
|
|
89
|
+
"""
|
|
90
|
+
if dt.tzinfo is not None:
|
|
91
|
+
dt0 = dt.astimezone(pytz.UTC).replace(tzinfo=None)
|
|
92
|
+
else:
|
|
93
|
+
dt0 = dt
|
|
94
|
+
return (dt0 - ZERO_EPOCH).total_seconds()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _convert_datetime_to_epoch_nanoseconds(dt: datetime) -> str:
|
|
98
|
+
return f"{convert_datetime_to_epoch(dt):f}".replace(".", "") + "000"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _convert_time_to_epoch_nanoseconds(tm: dt_t) -> str:
|
|
102
|
+
return (
|
|
103
|
+
str(tm.hour * 3600 + tm.minute * 60 + tm.second)
|
|
104
|
+
+ f"{tm.microsecond:06d}"
|
|
105
|
+
+ "000"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _convert_date_to_epoch_seconds(dt: date) -> str:
|
|
110
|
+
return f"{int((dt - ZERO_EPOCH_DATE).total_seconds())}"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _convert_date_to_epoch_milliseconds(dt: date) -> str:
|
|
114
|
+
return f"{(dt - ZERO_EPOCH_DATE).total_seconds():.3f}".replace(".", "")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _convert_date_to_epoch_nanoseconds(dt: date) -> str:
|
|
118
|
+
return f"{(dt - ZERO_EPOCH_DATE).total_seconds():.9f}".replace(".", "")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _extract_timestamp(value: str, ctx: dict) -> tuple[float, int]:
|
|
122
|
+
"""Extracts timestamp from a raw data."""
|
|
123
|
+
scale = ctx["scale"]
|
|
124
|
+
microseconds = float(value[0 : -scale + 6]) if scale > 6 else float(value)
|
|
125
|
+
fraction_of_nanoseconds = _adjust_fraction_of_nanoseconds(
|
|
126
|
+
value, ctx["max_fraction"], scale
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return microseconds, fraction_of_nanoseconds
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _adjust_fraction_of_nanoseconds(value: str, max_fraction: int, scale: int) -> int:
|
|
133
|
+
if scale == 0:
|
|
134
|
+
return 0
|
|
135
|
+
if value[0] != "-":
|
|
136
|
+
return int(value[-scale:] + ZERO_FILL[: 9 - scale])
|
|
137
|
+
|
|
138
|
+
frac = int(value[-scale:])
|
|
139
|
+
if frac == 0:
|
|
140
|
+
return 0
|
|
141
|
+
else:
|
|
142
|
+
return int(str(max_fraction - frac) + ZERO_FILL[: 9 - scale])
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _generate_tzinfo_from_tzoffset(tzoffset_minutes: int) -> tzinfo:
|
|
146
|
+
"""Generates tzinfo object from tzoffset."""
|
|
147
|
+
return pytz.FixedOffset(tzoffset_minutes)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class SnowflakeConverter:
|
|
151
|
+
def __init__(self, **kwargs) -> None:
|
|
152
|
+
self._parameters: dict[str, str | int | bool] = {}
|
|
153
|
+
self._use_numpy = kwargs.get("use_numpy", False) and numpy is not None
|
|
154
|
+
|
|
155
|
+
logger.debug("use_numpy: %s", self._use_numpy)
|
|
156
|
+
|
|
157
|
+
def set_parameters(self, new_parameters: dict) -> None:
|
|
158
|
+
self._parameters = new_parameters
|
|
159
|
+
|
|
160
|
+
def set_parameter(self, param: Any, value: Any) -> None:
|
|
161
|
+
self._parameters[param] = value
|
|
162
|
+
|
|
163
|
+
def get_parameters(self) -> dict[str, str | int | bool]:
|
|
164
|
+
return self._parameters
|
|
165
|
+
|
|
166
|
+
def get_parameter(self, param: str) -> str | int | bool | None:
|
|
167
|
+
return self._parameters.get(param)
|
|
168
|
+
|
|
169
|
+
def to_python_method(self, type_name, column) -> SnowflakeConverterType:
|
|
170
|
+
"""FROM Snowflake to Python Objects"""
|
|
171
|
+
ctx = column.copy()
|
|
172
|
+
if ctx.get("scale") is not None:
|
|
173
|
+
ctx["max_fraction"] = int(10 ** ctx["scale"])
|
|
174
|
+
ctx["zero_fill"] = "0" * (9 - ctx["scale"])
|
|
175
|
+
converters = [f"_{type_name}_to_python"]
|
|
176
|
+
if self._use_numpy:
|
|
177
|
+
converters.insert(0, f"_{type_name}_numpy_to_python")
|
|
178
|
+
for conv in converters:
|
|
179
|
+
try:
|
|
180
|
+
return getattr(self, conv)(ctx)
|
|
181
|
+
except AttributeError:
|
|
182
|
+
pass
|
|
183
|
+
logger.warning("No column converter found for type: %s", type_name)
|
|
184
|
+
return None # Skip conversion
|
|
185
|
+
|
|
186
|
+
def _FIXED_to_python(self, ctx: dict[str, Any]) -> Callable:
|
|
187
|
+
return int if ctx["scale"] == 0 else decimal.Decimal
|
|
188
|
+
|
|
189
|
+
def _FIXED_numpy_to_python(self, ctx: dict[str, Any]) -> Callable:
|
|
190
|
+
if ctx["scale"]:
|
|
191
|
+
return numpy.float64
|
|
192
|
+
else:
|
|
193
|
+
|
|
194
|
+
def conv(value: str) -> int64:
|
|
195
|
+
try:
|
|
196
|
+
return numpy.int64(value)
|
|
197
|
+
except OverflowError:
|
|
198
|
+
return int(value)
|
|
199
|
+
|
|
200
|
+
return conv
|
|
201
|
+
|
|
202
|
+
def _DECFLOAT_numpy_to_python(self, ctx: dict[str, Any]) -> Callable:
|
|
203
|
+
return numpy.float64
|
|
204
|
+
|
|
205
|
+
def _DECFLOAT_to_python(self, ctx: dict[str, Any]) -> Callable:
|
|
206
|
+
return decimal.Decimal
|
|
207
|
+
|
|
208
|
+
def _REAL_to_python(self, _: dict[str, str | None] | dict[str, str]) -> Callable:
|
|
209
|
+
return float
|
|
210
|
+
|
|
211
|
+
def _REAL_numpy_to_python(self, _) -> Callable:
|
|
212
|
+
return numpy.float64
|
|
213
|
+
|
|
214
|
+
def _TEXT_to_python(self, _: dict[str, Any]) -> None:
|
|
215
|
+
return None # skip conv
|
|
216
|
+
|
|
217
|
+
def _BINARY_to_python(self, _) -> Callable:
|
|
218
|
+
return binary_to_python
|
|
219
|
+
|
|
220
|
+
def _DATE_to_python(self, _: dict[str, str | None]) -> Callable:
|
|
221
|
+
"""Converts DATE to date."""
|
|
222
|
+
|
|
223
|
+
def conv(value: str) -> date:
|
|
224
|
+
try:
|
|
225
|
+
return (
|
|
226
|
+
datetime.fromtimestamp(int(value) * 86400, timezone.utc)
|
|
227
|
+
.replace(tzinfo=None)
|
|
228
|
+
.date()
|
|
229
|
+
)
|
|
230
|
+
except (OSError, ValueError) as e:
|
|
231
|
+
logger.debug("Failed to convert: %s", e)
|
|
232
|
+
ts = ZERO_EPOCH + timedelta(seconds=int(value) * (24 * 60 * 60))
|
|
233
|
+
return date(ts.year, ts.month, ts.day)
|
|
234
|
+
|
|
235
|
+
return conv
|
|
236
|
+
|
|
237
|
+
def _DATE_numpy_to_python(self, _) -> Callable:
|
|
238
|
+
"""Converts DATE to datetime.
|
|
239
|
+
|
|
240
|
+
No timezone is attached.
|
|
241
|
+
"""
|
|
242
|
+
return lambda x: numpy.datetime64(int(x), "D")
|
|
243
|
+
|
|
244
|
+
def _TIMESTAMP_TZ_to_python(self, ctx: dict[str, Any]) -> Callable:
|
|
245
|
+
"""Converts TIMESTAMP TZ to datetime.
|
|
246
|
+
|
|
247
|
+
The timezone offset is piggybacked.
|
|
248
|
+
"""
|
|
249
|
+
scale = ctx["scale"]
|
|
250
|
+
|
|
251
|
+
def conv(encoded_value: str) -> datetime:
|
|
252
|
+
value, tz = encoded_value.split()
|
|
253
|
+
tzinfo = _generate_tzinfo_from_tzoffset(int(tz) - 1440)
|
|
254
|
+
return SnowflakeConverter.create_timestamp_from_string(
|
|
255
|
+
value=value, scale=scale, tz=tzinfo
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
return conv
|
|
259
|
+
|
|
260
|
+
def _get_session_tz(self) -> tzinfo | UTC:
|
|
261
|
+
"""Gets the session timezone or use the local computer's timezone."""
|
|
262
|
+
try:
|
|
263
|
+
tz = self.get_parameter("TIMEZONE")
|
|
264
|
+
if not tz:
|
|
265
|
+
tz = "UTC"
|
|
266
|
+
return pytz.timezone(tz)
|
|
267
|
+
except pytz.exceptions.UnknownTimeZoneError:
|
|
268
|
+
logger.warning("converting to tzinfo failed")
|
|
269
|
+
if tzlocal is not None:
|
|
270
|
+
return tzlocal.get_localzone()
|
|
271
|
+
else:
|
|
272
|
+
return datetime.timezone.utc
|
|
273
|
+
|
|
274
|
+
def _pre_TIMESTAMP_LTZ_to_python(
|
|
275
|
+
self,
|
|
276
|
+
value,
|
|
277
|
+
ctx,
|
|
278
|
+
) -> tuple[datetime, int] | tuple[struct_time, int]:
|
|
279
|
+
"""Converts TIMESTAMP LTZ to datetime.
|
|
280
|
+
|
|
281
|
+
This takes consideration of the session parameter TIMEZONE if available. If not, tzlocal is used.
|
|
282
|
+
"""
|
|
283
|
+
microseconds, fraction_of_nanoseconds = _extract_timestamp(value, ctx)
|
|
284
|
+
tzinfo_value = self._get_session_tz()
|
|
285
|
+
|
|
286
|
+
try:
|
|
287
|
+
t0 = ZERO_EPOCH + timedelta(seconds=microseconds)
|
|
288
|
+
t = pytz.utc.localize(t0, is_dst=False).astimezone(tzinfo_value)
|
|
289
|
+
return t, fraction_of_nanoseconds
|
|
290
|
+
except OverflowError:
|
|
291
|
+
logger.debug(
|
|
292
|
+
"OverflowError in converting from epoch time to "
|
|
293
|
+
"timestamp_ltz: %s(ms). Falling back to use struct_time."
|
|
294
|
+
)
|
|
295
|
+
return time.localtime(microseconds), fraction_of_nanoseconds
|
|
296
|
+
|
|
297
|
+
def _TIMESTAMP_LTZ_to_python(self, ctx: dict[str, Any]) -> Callable:
|
|
298
|
+
tzinfo = self._get_session_tz()
|
|
299
|
+
scale = ctx["scale"]
|
|
300
|
+
|
|
301
|
+
return partial(
|
|
302
|
+
SnowflakeConverter.create_timestamp_from_string, scale=scale, tz=tzinfo
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
_TIMESTAMP_to_python = _TIMESTAMP_LTZ_to_python
|
|
306
|
+
|
|
307
|
+
def _TIMESTAMP_NTZ_to_python(self, ctx: dict[str, Any]) -> Callable:
|
|
308
|
+
"""TIMESTAMP NTZ to datetime with no timezone info is attached."""
|
|
309
|
+
scale = ctx["scale"]
|
|
310
|
+
|
|
311
|
+
return partial(SnowflakeConverter.create_timestamp_from_string, scale=scale)
|
|
312
|
+
|
|
313
|
+
def _TIMESTAMP_NTZ_numpy_to_python(self, ctx):
|
|
314
|
+
"""TIMESTAMP NTZ to datetime64 with no timezone info is attached."""
|
|
315
|
+
|
|
316
|
+
def conv(value: str) -> numpy.datetime64:
|
|
317
|
+
nanoseconds = int(decimal.Decimal(value).scaleb(9))
|
|
318
|
+
return numpy.datetime64(nanoseconds, "ns")
|
|
319
|
+
|
|
320
|
+
return conv
|
|
321
|
+
|
|
322
|
+
def _TIME_to_python(self, ctx: dict[str, Any]) -> Callable:
|
|
323
|
+
"""TIME to formatted string, SnowflakeDateTime, or datetime.time with no timezone attached."""
|
|
324
|
+
scale = ctx["scale"]
|
|
325
|
+
|
|
326
|
+
def conv0(value: str) -> time:
|
|
327
|
+
return (
|
|
328
|
+
datetime.fromtimestamp(float(value), timezone.utc)
|
|
329
|
+
.replace(tzinfo=None)
|
|
330
|
+
.time()
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
def conv(value: str) -> dt_t:
|
|
334
|
+
microseconds = float(value[0 : -scale + 6])
|
|
335
|
+
return (
|
|
336
|
+
datetime.fromtimestamp(microseconds, timezone.utc)
|
|
337
|
+
.replace(tzinfo=None)
|
|
338
|
+
.time()
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
return conv if scale > 6 else conv0
|
|
342
|
+
|
|
343
|
+
def _VARIANT_to_python(self, _: dict[str, Any]) -> Any | None:
|
|
344
|
+
return None # skip conv
|
|
345
|
+
|
|
346
|
+
_OBJECT_to_python = _VARIANT_to_python
|
|
347
|
+
|
|
348
|
+
_ARRAY_to_python = _VARIANT_to_python
|
|
349
|
+
|
|
350
|
+
def _VECTOR_to_python(self, ctx: dict[str, Any]) -> Callable:
|
|
351
|
+
return lambda v: json.loads(v)
|
|
352
|
+
|
|
353
|
+
def _BOOLEAN_to_python(
|
|
354
|
+
self, ctx: dict[str, str | None] | dict[str, str]
|
|
355
|
+
) -> Callable:
|
|
356
|
+
return lambda value: value in ("1", "TRUE")
|
|
357
|
+
|
|
358
|
+
def snowflake_type(self, value: Any) -> str | None:
|
|
359
|
+
"""Returns Snowflake data type for the value. This is used for qmark parameter style."""
|
|
360
|
+
type_name = value.__class__.__name__.lower()
|
|
361
|
+
return PYTHON_TO_SNOWFLAKE_TYPE.get(type_name)
|
|
362
|
+
|
|
363
|
+
def to_snowflake_bindings(self, snowflake_type: str, value: Any) -> str:
|
|
364
|
+
"""Converts Python data to snowflake data for qmark and numeric parameter style.
|
|
365
|
+
|
|
366
|
+
The output is bound in a query in the server side.
|
|
367
|
+
"""
|
|
368
|
+
type_name = value.__class__.__name__.lower()
|
|
369
|
+
return getattr(self, f"_{type_name}_to_snowflake_bindings")(
|
|
370
|
+
snowflake_type, value
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
def _str_to_snowflake_bindings(self, _, value: str) -> str:
|
|
374
|
+
# NOTE: str type is always taken as a text data and never binary
|
|
375
|
+
return str(value)
|
|
376
|
+
|
|
377
|
+
def _date_to_snowflake_bindings_in_bulk_insertion(self, value: date) -> str:
|
|
378
|
+
# notes: this is for date type bulk insertion, it's different from non-bulk date type insertion flow
|
|
379
|
+
milliseconds = _convert_date_to_epoch_milliseconds(value)
|
|
380
|
+
# according to https://docs.snowflake.com/en/sql-reference/functions/to_date
|
|
381
|
+
# through test, value in seconds will lead to wrong date
|
|
382
|
+
# millisecond and nanoarrow second are good
|
|
383
|
+
# if the milliseconds is beyond the range of 31536000000000, we switch to use nanoseconds
|
|
384
|
+
# otherwise we will hit overflow error in snowflake
|
|
385
|
+
if int(milliseconds) < 31536000000000:
|
|
386
|
+
return milliseconds
|
|
387
|
+
return _convert_date_to_epoch_nanoseconds(value)
|
|
388
|
+
|
|
389
|
+
_int_to_snowflake_bindings = _str_to_snowflake_bindings
|
|
390
|
+
_long_to_snowflake_bindings = _str_to_snowflake_bindings
|
|
391
|
+
_float_to_snowflake_bindings = _str_to_snowflake_bindings
|
|
392
|
+
_unicode_to_snowflake_bindings = _str_to_snowflake_bindings
|
|
393
|
+
_decimal_to_snowflake_bindings = _str_to_snowflake_bindings
|
|
394
|
+
|
|
395
|
+
def _bytes_to_snowflake_bindings(self, _, value: bytes) -> str:
|
|
396
|
+
return binascii.hexlify(value).decode("utf-8")
|
|
397
|
+
|
|
398
|
+
_bytearray_to_snowflake_bindings = _bytes_to_snowflake_bindings
|
|
399
|
+
|
|
400
|
+
def _bool_to_snowflake_bindings(self, _, value: bool) -> str:
|
|
401
|
+
return str(value).lower()
|
|
402
|
+
|
|
403
|
+
def _nonetype_to_snowflake_bindings(self, *_) -> None:
|
|
404
|
+
return None
|
|
405
|
+
|
|
406
|
+
def _date_to_snowflake_bindings(self, _, value: date) -> str:
|
|
407
|
+
# this is for date type non-bulk insertion, it's different from bulk date type insertion flow
|
|
408
|
+
# milliseconds
|
|
409
|
+
return _convert_date_to_epoch_milliseconds(value)
|
|
410
|
+
|
|
411
|
+
def _time_to_snowflake_bindings(self, _, value: dt_t) -> str:
|
|
412
|
+
# nanoseconds
|
|
413
|
+
return _convert_time_to_epoch_nanoseconds(value)
|
|
414
|
+
|
|
415
|
+
def _datetime_to_snowflake_bindings(
|
|
416
|
+
self, snowflake_type: str, value: datetime
|
|
417
|
+
) -> str:
|
|
418
|
+
snowflake_type = snowflake_type.upper()
|
|
419
|
+
if snowflake_type == "TIMESTAMP_LTZ":
|
|
420
|
+
_, t = self._derive_offset_timestamp(value)
|
|
421
|
+
return _convert_datetime_to_epoch_nanoseconds(t)
|
|
422
|
+
elif snowflake_type == "TIMESTAMP_NTZ":
|
|
423
|
+
# nanoseconds
|
|
424
|
+
return _convert_datetime_to_epoch_nanoseconds(value)
|
|
425
|
+
elif snowflake_type == "TIMESTAMP_TZ":
|
|
426
|
+
offset, t = self._derive_offset_timestamp(value, is_utc=True)
|
|
427
|
+
return _convert_datetime_to_epoch_nanoseconds(t) + " {:04d}".format(
|
|
428
|
+
int(offset)
|
|
429
|
+
)
|
|
430
|
+
else:
|
|
431
|
+
raise ProgrammingError(
|
|
432
|
+
msg="Binding datetime object with Snowflake data type {} is "
|
|
433
|
+
"not supported.".format(snowflake_type),
|
|
434
|
+
errno=ER_NOT_SUPPORT_DATA_TYPE,
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
def _derive_offset_timestamp(
|
|
438
|
+
self, value: datetime, is_utc: bool = False
|
|
439
|
+
) -> tuple[float, datetime]:
|
|
440
|
+
"""Derives TZ offset and timestamp from the datetime objects."""
|
|
441
|
+
tzinfo = value.tzinfo
|
|
442
|
+
if tzinfo is None:
|
|
443
|
+
# If no tzinfo is attached, use local timezone.
|
|
444
|
+
tzinfo = self._get_session_tz() if not is_utc else pytz.UTC
|
|
445
|
+
t = pytz.utc.localize(value, is_dst=False).astimezone(tzinfo)
|
|
446
|
+
else:
|
|
447
|
+
# if tzinfo is attached, just covert to epoch time
|
|
448
|
+
# as the server expects it in UTC anyway
|
|
449
|
+
t = value
|
|
450
|
+
offset = tzinfo.utcoffset(t.replace(tzinfo=None)).total_seconds() / 60 + 1440
|
|
451
|
+
return offset, t
|
|
452
|
+
|
|
453
|
+
def _struct_time_to_snowflake_bindings(
|
|
454
|
+
self, snowflake_type: str, value: time.struct_time
|
|
455
|
+
) -> str:
|
|
456
|
+
return self._datetime_to_snowflake_bindings(
|
|
457
|
+
snowflake_type, datetime.fromtimestamp(time.mktime(value))
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
def _timedelta_to_snowflake_bindings(
|
|
461
|
+
self, snowflake_type: str, value: timedelta
|
|
462
|
+
) -> str:
|
|
463
|
+
snowflake_type = snowflake_type.upper()
|
|
464
|
+
if snowflake_type != "TIME":
|
|
465
|
+
raise ProgrammingError(
|
|
466
|
+
msg="Binding timedelta object with Snowflake data type {} is "
|
|
467
|
+
"not supported.".format(snowflake_type),
|
|
468
|
+
errno=ER_NOT_SUPPORT_DATA_TYPE,
|
|
469
|
+
)
|
|
470
|
+
(hours, r) = divmod(value.seconds, 3600)
|
|
471
|
+
(mins, secs) = divmod(r, 60)
|
|
472
|
+
hours += value.days * 24
|
|
473
|
+
return (
|
|
474
|
+
str(hours * 3600 + mins * 60 + secs) + f"{value.microseconds:06d}" + "000"
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
def to_snowflake(self, value: Any) -> Any:
|
|
478
|
+
"""Converts Python data to Snowflake data for pyformat/format style.
|
|
479
|
+
|
|
480
|
+
The output is bound in a query in the client side.
|
|
481
|
+
"""
|
|
482
|
+
type_name = value.__class__.__name__.lower()
|
|
483
|
+
return getattr(self, f"_{type_name}_to_snowflake")(value)
|
|
484
|
+
|
|
485
|
+
def _int_to_snowflake(self, value: int) -> int:
|
|
486
|
+
return int(value)
|
|
487
|
+
|
|
488
|
+
def _long_to_snowflake(self, value):
|
|
489
|
+
return long(value)
|
|
490
|
+
|
|
491
|
+
def _float_to_snowflake(self, value: float) -> float:
|
|
492
|
+
return float(value)
|
|
493
|
+
|
|
494
|
+
def _str_to_snowflake(self, value: str) -> str:
|
|
495
|
+
return str(value)
|
|
496
|
+
|
|
497
|
+
_unicode_to_snowflake = _str_to_snowflake
|
|
498
|
+
|
|
499
|
+
def _bytes_to_snowflake(self, value: bytes) -> bytes:
|
|
500
|
+
return binary_to_snowflake(value)
|
|
501
|
+
|
|
502
|
+
_bytearray_to_snowflake = _bytes_to_snowflake
|
|
503
|
+
|
|
504
|
+
def _bool_to_snowflake(self, value: bool | bool_) -> bool:
|
|
505
|
+
return bool(value)
|
|
506
|
+
|
|
507
|
+
def _bool__to_snowflake(self, value) -> bool:
|
|
508
|
+
return bool(value)
|
|
509
|
+
|
|
510
|
+
def _nonetype_to_snowflake(self, _: Any | None) -> Any | None:
|
|
511
|
+
return None
|
|
512
|
+
|
|
513
|
+
def _total_seconds_from_timedelta(self, td: timedelta) -> int:
|
|
514
|
+
return sfdatetime_total_seconds_from_timedelta(td)
|
|
515
|
+
|
|
516
|
+
def _datetime_to_snowflake(self, value: datetime) -> str:
|
|
517
|
+
tzinfo_value = value.tzinfo
|
|
518
|
+
if tzinfo_value:
|
|
519
|
+
if pytz.utc != tzinfo_value:
|
|
520
|
+
try:
|
|
521
|
+
td = tzinfo_value.utcoffset(value)
|
|
522
|
+
except pytz.exceptions.AmbiguousTimeError:
|
|
523
|
+
td = tzinfo_value.utcoffset(value, is_dst=False)
|
|
524
|
+
else:
|
|
525
|
+
td = ZERO_TIMEDELTA
|
|
526
|
+
sign = "+" if td >= ZERO_TIMEDELTA else "-"
|
|
527
|
+
td_secs = sfdatetime_total_seconds_from_timedelta(td)
|
|
528
|
+
h, m = divmod(abs(td_secs // 60), 60)
|
|
529
|
+
if value.microsecond:
|
|
530
|
+
return (
|
|
531
|
+
"{year:d}-{month:02d}-{day:02d} "
|
|
532
|
+
"{hour:02d}:{minute:02d}:{second:02d}."
|
|
533
|
+
"{microsecond:06d}{sign}{tzh:02d}:{tzm:02d}"
|
|
534
|
+
).format(
|
|
535
|
+
year=value.year,
|
|
536
|
+
month=value.month,
|
|
537
|
+
day=value.day,
|
|
538
|
+
hour=value.hour,
|
|
539
|
+
minute=value.minute,
|
|
540
|
+
second=value.second,
|
|
541
|
+
microsecond=value.microsecond,
|
|
542
|
+
sign=sign,
|
|
543
|
+
tzh=h,
|
|
544
|
+
tzm=m,
|
|
545
|
+
)
|
|
546
|
+
return (
|
|
547
|
+
"{year:d}-{month:02d}-{day:02d} "
|
|
548
|
+
"{hour:02d}:{minute:02d}:{second:02d}"
|
|
549
|
+
"{sign}{tzh:02d}:{tzm:02d}"
|
|
550
|
+
).format(
|
|
551
|
+
year=value.year,
|
|
552
|
+
month=value.month,
|
|
553
|
+
day=value.day,
|
|
554
|
+
hour=value.hour,
|
|
555
|
+
minute=value.minute,
|
|
556
|
+
second=value.second,
|
|
557
|
+
sign=sign,
|
|
558
|
+
tzh=h,
|
|
559
|
+
tzm=m,
|
|
560
|
+
)
|
|
561
|
+
else:
|
|
562
|
+
if value.microsecond:
|
|
563
|
+
return (
|
|
564
|
+
"{year:d}-{month:02d}-{day:02d} "
|
|
565
|
+
"{hour:02d}:{minute:02d}:{second:02d}."
|
|
566
|
+
"{microsecond:06d}"
|
|
567
|
+
).format(
|
|
568
|
+
year=value.year,
|
|
569
|
+
month=value.month,
|
|
570
|
+
day=value.day,
|
|
571
|
+
hour=value.hour,
|
|
572
|
+
minute=value.minute,
|
|
573
|
+
second=value.second,
|
|
574
|
+
microsecond=value.microsecond,
|
|
575
|
+
)
|
|
576
|
+
return (
|
|
577
|
+
"{year:d}-{month:02d}-{day:02d} " "{hour:02d}:{minute:02d}:{second:02d}"
|
|
578
|
+
).format(
|
|
579
|
+
year=value.year,
|
|
580
|
+
month=value.month,
|
|
581
|
+
day=value.day,
|
|
582
|
+
hour=value.hour,
|
|
583
|
+
minute=value.minute,
|
|
584
|
+
second=value.second,
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
def _date_to_snowflake(self, value: date) -> str:
|
|
588
|
+
"""Converts Date object to Snowflake object."""
|
|
589
|
+
return "{year:d}-{month:02d}-{day:02d}".format(
|
|
590
|
+
year=value.year, month=value.month, day=value.day
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
def _time_to_snowflake(self, value: dt_t) -> str:
|
|
594
|
+
if value.microsecond:
|
|
595
|
+
return value.strftime("%H:%M:%S.%%06d") % value.microsecond
|
|
596
|
+
return value.strftime("%H:%M:%S")
|
|
597
|
+
|
|
598
|
+
def _struct_time_to_snowflake(self, value: time.struct_time) -> str:
|
|
599
|
+
tzinfo_value = _generate_tzinfo_from_tzoffset(time.timezone // 60)
|
|
600
|
+
t = datetime.fromtimestamp(time.mktime(value))
|
|
601
|
+
if pytz.utc != tzinfo_value:
|
|
602
|
+
t += tzinfo_value.utcoffset(t)
|
|
603
|
+
t = t.replace(tzinfo=tzinfo_value)
|
|
604
|
+
return self._datetime_to_snowflake(t)
|
|
605
|
+
|
|
606
|
+
def _timedelta_to_snowflake(self, value: timedelta) -> str:
|
|
607
|
+
(hours, r) = divmod(value.seconds, 3600)
|
|
608
|
+
(mins, secs) = divmod(r, 60)
|
|
609
|
+
hours += value.days * 24
|
|
610
|
+
if value.microseconds:
|
|
611
|
+
return ("{hour:02d}:{minute:02d}:{second:02d}." "{microsecond:06d}").format(
|
|
612
|
+
hour=hours, minute=mins, second=secs, microsecond=value.microseconds
|
|
613
|
+
)
|
|
614
|
+
return "{hour:02d}:{minute:02d}:{second:02d}".format(
|
|
615
|
+
hour=hours, minute=mins, second=secs
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
def _decimal_to_snowflake(self, value: decimal.Decimal) -> str | None:
|
|
619
|
+
if isinstance(value, decimal.Decimal):
|
|
620
|
+
return str(value)
|
|
621
|
+
|
|
622
|
+
return None
|
|
623
|
+
|
|
624
|
+
def _list_to_snowflake(self, value: list) -> list:
|
|
625
|
+
return [
|
|
626
|
+
SnowflakeConverter.quote(v0)
|
|
627
|
+
for v0 in [SnowflakeConverter.escape(v) for v in value]
|
|
628
|
+
]
|
|
629
|
+
|
|
630
|
+
_tuple_to_snowflake = _list_to_snowflake
|
|
631
|
+
|
|
632
|
+
def __numpy_to_snowflake(self, value):
|
|
633
|
+
return value
|
|
634
|
+
|
|
635
|
+
def _float16_to_snowflake(self, value):
|
|
636
|
+
return float(value)
|
|
637
|
+
|
|
638
|
+
_int8_to_snowflake = __numpy_to_snowflake
|
|
639
|
+
_int16_to_snowflake = __numpy_to_snowflake
|
|
640
|
+
_int32_to_snowflake = __numpy_to_snowflake
|
|
641
|
+
_int64_to_snowflake = __numpy_to_snowflake
|
|
642
|
+
_uint8_to_snowflake = __numpy_to_snowflake
|
|
643
|
+
_uint16_to_snowflake = __numpy_to_snowflake
|
|
644
|
+
_uint32_to_snowflake = __numpy_to_snowflake
|
|
645
|
+
_uint64_to_snowflake = __numpy_to_snowflake
|
|
646
|
+
_float32_to_snowflake = _float16_to_snowflake
|
|
647
|
+
_float64_to_snowflake = _float16_to_snowflake
|
|
648
|
+
|
|
649
|
+
def _datetime64_to_snowflake(self, value) -> str:
|
|
650
|
+
return str(value) + "+00:00"
|
|
651
|
+
|
|
652
|
+
def _quoted_name_to_snowflake(self, value) -> str:
|
|
653
|
+
return str(value)
|
|
654
|
+
|
|
655
|
+
def __getattr__(self, item: str) -> NoReturn:
|
|
656
|
+
if item.endswith("_to_snowflake"):
|
|
657
|
+
raise ProgrammingError(
|
|
658
|
+
msg="Binding data in type ({}) is not supported.".format(
|
|
659
|
+
item[1 : item.find("_to_snowflake")]
|
|
660
|
+
),
|
|
661
|
+
errno=ER_NOT_SUPPORT_DATA_TYPE,
|
|
662
|
+
)
|
|
663
|
+
elif item.endswith("to_snowflake_bindings"):
|
|
664
|
+
raise ProgrammingError(
|
|
665
|
+
msg="Binding data in type ({}) is not supported.".format(
|
|
666
|
+
item[1 : item.find("_to_snowflake_bindings")]
|
|
667
|
+
),
|
|
668
|
+
errno=ER_NOT_SUPPORT_DATA_TYPE,
|
|
669
|
+
)
|
|
670
|
+
raise AttributeError(f"No method is available: {item}")
|
|
671
|
+
|
|
672
|
+
def to_csv_bindings(self, value: tuple[str, Any] | Any) -> str | None:
|
|
673
|
+
"""Convert value to a string representation in CSV-escaped format to INSERT INTO."""
|
|
674
|
+
if isinstance(value, tuple) and len(value) == 2:
|
|
675
|
+
_type, val = value
|
|
676
|
+
if _type in ["TIMESTAMP_TZ", "TIME"]:
|
|
677
|
+
# unspecified timezone is considered utc
|
|
678
|
+
if getattr(val, "tzinfo", 1) is None:
|
|
679
|
+
val = self.to_snowflake(pytz.utc.localize(val))
|
|
680
|
+
else:
|
|
681
|
+
val = self.to_snowflake(val)
|
|
682
|
+
else:
|
|
683
|
+
val = self.to_snowflake_bindings(_type, val)
|
|
684
|
+
else:
|
|
685
|
+
if isinstance(value, (dt_t, timedelta)):
|
|
686
|
+
val = self.to_snowflake(value)
|
|
687
|
+
elif isinstance(value, date) and not isinstance(value, datetime):
|
|
688
|
+
# FIX SNOW-770678 and SNOW-966444
|
|
689
|
+
# bulk insertion congestion is different from non-bulk insertion
|
|
690
|
+
# to_csv_bindings is only used in bulk insertion logic
|
|
691
|
+
val = self._date_to_snowflake_bindings_in_bulk_insertion(value)
|
|
692
|
+
else:
|
|
693
|
+
_type = self.snowflake_type(value)
|
|
694
|
+
val = self.to_snowflake_bindings(_type, value)
|
|
695
|
+
return self.escape_for_csv(val)
|
|
696
|
+
|
|
697
|
+
@staticmethod
|
|
698
|
+
def escape(value: Any) -> Any:
|
|
699
|
+
if isinstance(value, list):
|
|
700
|
+
return value
|
|
701
|
+
if value is None or IS_NUMERIC(value) or IS_BINARY(value):
|
|
702
|
+
return value
|
|
703
|
+
res = value
|
|
704
|
+
res = res.replace("\\", "\\\\")
|
|
705
|
+
res = res.replace("\n", "\\n")
|
|
706
|
+
res = res.replace("\r", "\\r")
|
|
707
|
+
res = res.replace("\047", "\134\047") # single quotes
|
|
708
|
+
return res
|
|
709
|
+
|
|
710
|
+
@staticmethod
|
|
711
|
+
def quote(value) -> str:
|
|
712
|
+
if isinstance(value, list):
|
|
713
|
+
return ",".join(value)
|
|
714
|
+
if value is None:
|
|
715
|
+
return "NULL"
|
|
716
|
+
elif isinstance(value, bool):
|
|
717
|
+
return "TRUE" if value else "FALSE"
|
|
718
|
+
elif IS_NUMERIC(value):
|
|
719
|
+
return str(repr(value))
|
|
720
|
+
elif IS_BINARY(value):
|
|
721
|
+
# Binary literal syntax
|
|
722
|
+
return "X'{}'".format(value.decode("ascii"))
|
|
723
|
+
|
|
724
|
+
return f"'{value}'"
|
|
725
|
+
|
|
726
|
+
@staticmethod
|
|
727
|
+
def escape_for_csv(value: str) -> str:
|
|
728
|
+
if value is None: # NULL
|
|
729
|
+
return ""
|
|
730
|
+
elif not value: # Empty string
|
|
731
|
+
return '""'
|
|
732
|
+
if (
|
|
733
|
+
value.find('"') >= 0
|
|
734
|
+
or value.find("\n") >= 0
|
|
735
|
+
or value.find(",") >= 0
|
|
736
|
+
or value.find("\\") >= 0
|
|
737
|
+
):
|
|
738
|
+
# replace single quote with double quotes
|
|
739
|
+
value = value.replace('"', '""')
|
|
740
|
+
return f'"{value}"'
|
|
741
|
+
else:
|
|
742
|
+
return value
|
|
743
|
+
|
|
744
|
+
@staticmethod
|
|
745
|
+
def get_seconds_microseconds(
|
|
746
|
+
value: str,
|
|
747
|
+
scale: int,
|
|
748
|
+
) -> tuple[int, int]:
|
|
749
|
+
"""Calculate the second and microsecond parts og a timestamp given as a string.
|
|
750
|
+
|
|
751
|
+
The trick is that we always want to do floor division, but if the timestamp
|
|
752
|
+
is negative then it is given as its inverse. So -0.000_000_009
|
|
753
|
+
(which is 1969-12-31-23:59:59.999999991) should round down to 6
|
|
754
|
+
fraction figures as Python doesn't support sub-microseconds.
|
|
755
|
+
Ultimately for the aforementioned example we should return two integers 0 and -000_001.
|
|
756
|
+
"""
|
|
757
|
+
negative = value[0] == "-"
|
|
758
|
+
lhs, _, rhs = value.partition(".")
|
|
759
|
+
seconds = int(lhs)
|
|
760
|
+
microseconds = int(rhs) if rhs else 0
|
|
761
|
+
if scale < 6:
|
|
762
|
+
microseconds *= 10 ** (6 - scale)
|
|
763
|
+
elif scale > 6:
|
|
764
|
+
if negative:
|
|
765
|
+
microseconds = ceil(microseconds / 10 ** (scale - 6))
|
|
766
|
+
else:
|
|
767
|
+
microseconds = microseconds // 10 ** (scale - 6)
|
|
768
|
+
if negative:
|
|
769
|
+
microseconds = -microseconds
|
|
770
|
+
return seconds, microseconds
|
|
771
|
+
|
|
772
|
+
@staticmethod
|
|
773
|
+
def create_timestamp_from_string(
|
|
774
|
+
value: str,
|
|
775
|
+
scale: int,
|
|
776
|
+
tz: tzinfo | None = None,
|
|
777
|
+
) -> datetime:
|
|
778
|
+
seconds, fraction = SnowflakeConverter.get_seconds_microseconds(
|
|
779
|
+
value=value, scale=scale
|
|
780
|
+
)
|
|
781
|
+
if not tz:
|
|
782
|
+
return datetime.fromtimestamp(seconds, timezone.utc).replace(
|
|
783
|
+
tzinfo=None
|
|
784
|
+
) + timedelta(microseconds=fraction)
|
|
785
|
+
return datetime.fromtimestamp(seconds, tz=tz) + timedelta(microseconds=fraction)
|