snowflake-sqlalchemy 1.5.2__py3-none-any.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/sqlalchemy/__init__.py +116 -0
- snowflake/sqlalchemy/_constants.py +12 -0
- snowflake/sqlalchemy/base.py +1065 -0
- snowflake/sqlalchemy/custom_commands.py +621 -0
- snowflake/sqlalchemy/custom_types.py +105 -0
- snowflake/sqlalchemy/provision.py +12 -0
- snowflake/sqlalchemy/requirements.py +297 -0
- snowflake/sqlalchemy/snowdialect.py +911 -0
- snowflake/sqlalchemy/util.py +336 -0
- snowflake/sqlalchemy/version.py +6 -0
- snowflake_sqlalchemy-1.5.2.dist-info/METADATA +503 -0
- snowflake_sqlalchemy-1.5.2.dist-info/RECORD +15 -0
- snowflake_sqlalchemy-1.5.2.dist-info/WHEEL +4 -0
- snowflake_sqlalchemy-1.5.2.dist-info/entry_points.txt +2 -0
- snowflake_sqlalchemy-1.5.2.dist-info/licenses/LICENSE.txt +202 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
import sqlalchemy.types as sqltypes
|
|
6
|
+
import sqlalchemy.util as util
|
|
7
|
+
|
|
8
|
+
TEXT = sqltypes.VARCHAR
|
|
9
|
+
CHARACTER = sqltypes.CHAR
|
|
10
|
+
DEC = sqltypes.DECIMAL
|
|
11
|
+
DOUBLE = sqltypes.FLOAT
|
|
12
|
+
FIXED = sqltypes.DECIMAL
|
|
13
|
+
NUMBER = sqltypes.DECIMAL
|
|
14
|
+
BYTEINT = sqltypes.SMALLINT
|
|
15
|
+
STRING = sqltypes.VARCHAR
|
|
16
|
+
TINYINT = sqltypes.SMALLINT
|
|
17
|
+
VARBINARY = sqltypes.BINARY
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _process_float(value):
|
|
21
|
+
if value == float("inf"):
|
|
22
|
+
return "inf"
|
|
23
|
+
elif value == float("-inf"):
|
|
24
|
+
return "-inf"
|
|
25
|
+
elif value is not None:
|
|
26
|
+
return float(value)
|
|
27
|
+
return value
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SnowflakeType(sqltypes.TypeEngine):
|
|
31
|
+
def _default_dialect(self):
|
|
32
|
+
# Get around circular import
|
|
33
|
+
return __import__("snowflake.sqlalchemy").sqlalchemy.dialect()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class VARIANT(SnowflakeType):
|
|
37
|
+
__visit_name__ = "VARIANT"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class OBJECT(SnowflakeType):
|
|
41
|
+
__visit_name__ = "OBJECT"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ARRAY(SnowflakeType):
|
|
45
|
+
__visit_name__ = "ARRAY"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TIMESTAMP_TZ(SnowflakeType):
|
|
49
|
+
__visit_name__ = "TIMESTAMP_TZ"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class TIMESTAMP_LTZ(SnowflakeType):
|
|
53
|
+
__visit_name__ = "TIMESTAMP_LTZ"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TIMESTAMP_NTZ(SnowflakeType):
|
|
57
|
+
__visit_name__ = "TIMESTAMP_NTZ"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class GEOGRAPHY(SnowflakeType):
|
|
61
|
+
__visit_name__ = "GEOGRAPHY"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class GEOMETRY(SnowflakeType):
|
|
65
|
+
__visit_name__ = "GEOMETRY"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class _CUSTOM_Date(SnowflakeType, sqltypes.Date):
|
|
69
|
+
def literal_processor(self, dialect):
|
|
70
|
+
def process(value):
|
|
71
|
+
if value is not None:
|
|
72
|
+
return f"'{value.isoformat()}'"
|
|
73
|
+
|
|
74
|
+
return process
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class _CUSTOM_DateTime(SnowflakeType, sqltypes.DateTime):
|
|
78
|
+
def literal_processor(self, dialect):
|
|
79
|
+
def process(value):
|
|
80
|
+
if value is not None:
|
|
81
|
+
datetime_str = value.isoformat(" ", timespec="microseconds")
|
|
82
|
+
return f"'{datetime_str}'"
|
|
83
|
+
|
|
84
|
+
return process
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class _CUSTOM_Time(SnowflakeType, sqltypes.Time):
|
|
88
|
+
def literal_processor(self, dialect):
|
|
89
|
+
def process(value):
|
|
90
|
+
if value is not None:
|
|
91
|
+
time_str = value.isoformat(timespec="microseconds")
|
|
92
|
+
return f"'{time_str}'"
|
|
93
|
+
|
|
94
|
+
return process
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class _CUSTOM_Float(SnowflakeType, sqltypes.Float):
|
|
98
|
+
def bind_processor(self, dialect):
|
|
99
|
+
return _process_float
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class _CUSTOM_DECIMAL(SnowflakeType, sqltypes.DECIMAL):
|
|
103
|
+
@util.memoized_property
|
|
104
|
+
def _type_affinity(self):
|
|
105
|
+
return sqltypes.INTEGER if self.scale == 0 else sqltypes.DECIMAL
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
from sqlalchemy.testing.provision import set_default_schema_on_connection
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# This is only for test purpose required by Requirement "default_schema_name_switch"
|
|
8
|
+
@set_default_schema_on_connection.for_db("snowflake")
|
|
9
|
+
def _snowflake_set_default_schema_on_connection(cfg, dbapi_connection, schema_name):
|
|
10
|
+
cursor = dbapi_connection.cursor()
|
|
11
|
+
cursor.execute(f"USE SCHEMA {dbapi_connection.database}.{schema_name};")
|
|
12
|
+
cursor.close()
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from sqlalchemy.testing import exclusions
|
|
6
|
+
from sqlalchemy.testing.requirements import SuiteRequirements
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Requirements(SuiteRequirements):
|
|
10
|
+
"""
|
|
11
|
+
stay-closed properties
|
|
12
|
+
|
|
13
|
+
1. not supported in snowflake
|
|
14
|
+
|
|
15
|
+
- autocommit: sqlalchemy's autocommit isolation level concept does not apply to snowflake
|
|
16
|
+
- isolation_level: snowflake only supports read committed
|
|
17
|
+
- ref docs: https://docs.snowflake.com/en/sql-reference/transactions.html#label-txn-autocommit
|
|
18
|
+
https://docs.sqlalchemy.org/en/14/core/connections.html#setting-transaction-isolation-levels-including-dbapi-autocommit
|
|
19
|
+
- index_ddl_if_exists: index not supported in snowflake
|
|
20
|
+
- non_updating_cascade: updating cascade supported
|
|
21
|
+
- empty_inserts: not supported in snowflake
|
|
22
|
+
- full_returning: not supported in snowflake
|
|
23
|
+
- insert_executemany_returning: not supported in snowflake
|
|
24
|
+
- returning: not supported in snowflake
|
|
25
|
+
- indexes_with_expressions: index not supported in snowflake
|
|
26
|
+
- check_constraint_reflection: not supported in snowflake
|
|
27
|
+
- reflect_tables_no_columns: not supported in snowflake
|
|
28
|
+
- server_side_cursors: no supported in snowflake
|
|
29
|
+
- index_reflects_included_columns: index not supported in snowflake
|
|
30
|
+
- savepoints: not supported in snowflake
|
|
31
|
+
- two_phase_transactions: not supported in snowflake
|
|
32
|
+
- async_dialect: no await used
|
|
33
|
+
- fetch_expression: not supported in snowflake
|
|
34
|
+
- fetch_percent: not supported in snowflake
|
|
35
|
+
- fetch_ties: not supported in snowflake
|
|
36
|
+
- supports_distinct_on: not supported in snowflake
|
|
37
|
+
- time_timezone: not supported in snowflake
|
|
38
|
+
- identity_columns_standard: not supported in snowflake, snowflake does not support setting identity with min max
|
|
39
|
+
- computed_columns: TODO: not supported in snowflake yet, check SNOW-169530 for virtual column
|
|
40
|
+
- computed_columns_default_persisted: TODO: not supported in snowflake yet, check SNOW-169530 for virtual column
|
|
41
|
+
- computed_columns_reflect_persisted: TODO: not supported in snowflake yet, check SNOW-169530 for virtual column
|
|
42
|
+
- computed_columns_virtual: TODO: not supported in snowflake yet, check SNOW-169530 for virtual column
|
|
43
|
+
- computed_columns_stored: TODO: not supported in snowflake yet, check SNOW-169530 for virtual column
|
|
44
|
+
|
|
45
|
+
2. potential service side issue / unclear service behavior
|
|
46
|
+
|
|
47
|
+
- foreign_key_constraint_option_reflection_ondelete: TODO: check service side issue or by design?
|
|
48
|
+
- fk_constraint_option_reflection_ondelete_restrict: TODO: check service side issue or by design?
|
|
49
|
+
- foreign_key_constraint_option_reflection_onupdate: TODO: check service side issue or by design?
|
|
50
|
+
- fk_constraint_option_reflection_onupdate_restrict: TODO: check service side issue or by design?
|
|
51
|
+
|
|
52
|
+
3. connector missing feature
|
|
53
|
+
|
|
54
|
+
- dbapi_lastrowid: TODO, not supported in snowflake python connector, support it in the future
|
|
55
|
+
- supports_lastrowid: TODO: not supported, check SNOW-11155
|
|
56
|
+
|
|
57
|
+
4. sqlalchemy potentially missing feature
|
|
58
|
+
note: not sure whether these to be supported
|
|
59
|
+
|
|
60
|
+
- collate: TODO: order_by_collation
|
|
61
|
+
- datetime_timezone: TODO: default for datetime type, snowflake uses TIMESTAMP_NTZ which
|
|
62
|
+
contains no time zone info consider creating a new column type TIMESTAMP_TZ for the
|
|
63
|
+
the time zone info
|
|
64
|
+
- ref: https://docs.snowflake.com/en/sql-reference/data-types-datetime.html#timestamp-ltz-timestamp-ntz-timestamp-tz
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def table_ddl_if_exists(self):
|
|
69
|
+
return exclusions.open()
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def table_value_constructor(self):
|
|
73
|
+
return exclusions.open()
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def deferrable_fks(self):
|
|
77
|
+
return exclusions.open()
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def boolean_col_expressions(self):
|
|
81
|
+
return exclusions.open()
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def nullsordering(self):
|
|
85
|
+
return exclusions.open()
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def standalone_binds(self):
|
|
89
|
+
return exclusions.open()
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def intersect(self):
|
|
93
|
+
return exclusions.open()
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def except_(self):
|
|
97
|
+
return exclusions.open()
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def window_functions(self):
|
|
101
|
+
return exclusions.open()
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def ctes(self):
|
|
105
|
+
return exclusions.open()
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def ctes_on_dml(self):
|
|
109
|
+
return exclusions.open()
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def tuple_in(self):
|
|
113
|
+
return exclusions.open()
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def emulated_lastrowid(self):
|
|
117
|
+
return exclusions.open()
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def emulated_lastrowid_even_with_sequences(self):
|
|
121
|
+
return exclusions.open()
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def views(self):
|
|
125
|
+
return exclusions.open()
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def cross_schema_fk_reflection(self):
|
|
129
|
+
return exclusions.open()
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def foreign_key_constraint_name_reflection(self):
|
|
133
|
+
return exclusions.open()
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def implicit_default_schema(self):
|
|
137
|
+
return exclusions.open()
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def default_schema_name_switch(self):
|
|
141
|
+
return exclusions.open()
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def reflects_pk_names(self):
|
|
145
|
+
return exclusions.open()
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def comment_reflection(self):
|
|
149
|
+
return exclusions.open()
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def fk_constraint_option_reflection_ondelete_noaction(self):
|
|
153
|
+
return exclusions.open()
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def temp_table_names(self):
|
|
157
|
+
return exclusions.open()
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def temporary_views(self):
|
|
161
|
+
return exclusions.open()
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def unicode_ddl(self):
|
|
165
|
+
return exclusions.open()
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def datetime_literals(self):
|
|
169
|
+
return exclusions.open()
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def timestamp_microseconds(self):
|
|
173
|
+
return exclusions.open()
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def datetime_historic(self):
|
|
177
|
+
return exclusions.open()
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def date_historic(self):
|
|
181
|
+
return exclusions.open()
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def legacy_unconditional_json_extract(self):
|
|
185
|
+
return exclusions.open()
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def precision_numerics_enotation_small(self):
|
|
189
|
+
return exclusions.open()
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def precision_numerics_enotation_large(self):
|
|
193
|
+
return exclusions.open()
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def precision_numerics_many_significant_digits(self):
|
|
197
|
+
return exclusions.open()
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def precision_numerics_retains_significant_digits(self):
|
|
201
|
+
return exclusions.open()
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def infinity_floats(self):
|
|
205
|
+
return exclusions.open()
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def update_from(self):
|
|
209
|
+
return exclusions.open()
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def delete_from(self):
|
|
213
|
+
return exclusions.open()
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def mod_operator_as_percent_sign(self):
|
|
217
|
+
return exclusions.open()
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def percent_schema_names(self):
|
|
221
|
+
return exclusions.open()
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def order_by_label_with_expression(self):
|
|
225
|
+
return exclusions.open()
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def regexp_match(self):
|
|
229
|
+
return exclusions.open()
|
|
230
|
+
|
|
231
|
+
@property
|
|
232
|
+
def regexp_replace(self):
|
|
233
|
+
return exclusions.open()
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def fetch_first(self):
|
|
237
|
+
return exclusions.open()
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def fetch_no_order_by(self):
|
|
241
|
+
return exclusions.open()
|
|
242
|
+
|
|
243
|
+
@property
|
|
244
|
+
def fetch_offset_with_options(self):
|
|
245
|
+
return exclusions.open()
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def identity_columns(self):
|
|
249
|
+
return exclusions.open()
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def duplicate_key_raises_integrity_error(self):
|
|
253
|
+
# Snowflake allows duplicate value for primary key
|
|
254
|
+
return exclusions.closed()
|
|
255
|
+
|
|
256
|
+
@property
|
|
257
|
+
def ctes_with_update_delete(self):
|
|
258
|
+
# Snowflake CTE could only be followed by SELECT
|
|
259
|
+
# https://docs.snowflake.com/en/user-guide/queries-cte.html
|
|
260
|
+
return exclusions.closed()
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def sql_expression_limit_offset(self):
|
|
264
|
+
# Snowflake only takes non-negative integer constants for offset/limit
|
|
265
|
+
return exclusions.closed()
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def json_type(self):
|
|
269
|
+
# TODO: need service/connector support
|
|
270
|
+
# check https://snowflakecomputing.atlassian.net/browse/SNOW-52370
|
|
271
|
+
return exclusions.closed()
|
|
272
|
+
|
|
273
|
+
@property
|
|
274
|
+
def implements_get_lastrowid(self):
|
|
275
|
+
# TODO: need connector lastrowid support, check SNOW-11155
|
|
276
|
+
return exclusions.closed()
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def implicit_decimal_binds(self):
|
|
280
|
+
# Supporting this would require behavior breaking change to implicitly convert str to Decimal when binding
|
|
281
|
+
# parameters in string forms of decimal values.
|
|
282
|
+
# Check https://snowflakecomputing.atlassian.net/browse/SNOW-640134 for details on breaking changes discussion.
|
|
283
|
+
return exclusions.closed()
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def datetime_implicit_bound(self):
|
|
287
|
+
# Supporting this would require behavior breaking change to implicitly convert str to datetime when binding
|
|
288
|
+
# parameters in string forms of datetime values.
|
|
289
|
+
# Check https://snowflakecomputing.atlassian.net/browse/SNOW-640134 for details on breaking changes discussion.
|
|
290
|
+
return exclusions.closed()
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def timestamp_microseconds_implicit_bound(self):
|
|
294
|
+
# Supporting this would require behavior breaking change to implicitly convert str to timestamp when binding
|
|
295
|
+
# parameters in string forms of timestamp values.
|
|
296
|
+
# Check https://snowflakecomputing.atlassian.net/browse/SNOW-640134 for details on breaking changes discussion.
|
|
297
|
+
return exclusions.closed()
|