sqlalchemy-cratedb 0.41.0.dev0__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.
- sqlalchemy_cratedb/__init__.py +65 -0
- sqlalchemy_cratedb/compat/__init__.py +0 -0
- sqlalchemy_cratedb/compat/api13.py +152 -0
- sqlalchemy_cratedb/compat/core10.py +253 -0
- sqlalchemy_cratedb/compat/core14.py +337 -0
- sqlalchemy_cratedb/compat/core20.py +423 -0
- sqlalchemy_cratedb/compiler.py +361 -0
- sqlalchemy_cratedb/dialect.py +414 -0
- sqlalchemy_cratedb/predicate.py +96 -0
- sqlalchemy_cratedb/sa_version.py +28 -0
- sqlalchemy_cratedb/support/__init__.py +18 -0
- sqlalchemy_cratedb/support/pandas.py +110 -0
- sqlalchemy_cratedb/support/polyfill.py +130 -0
- sqlalchemy_cratedb/support/util.py +82 -0
- sqlalchemy_cratedb/type/__init__.py +13 -0
- sqlalchemy_cratedb/type/array.py +143 -0
- sqlalchemy_cratedb/type/geo.py +43 -0
- sqlalchemy_cratedb/type/object.py +94 -0
- sqlalchemy_cratedb/type/vector.py +176 -0
- sqlalchemy_cratedb-0.41.0.dev0.dist-info/LICENSE +178 -0
- sqlalchemy_cratedb-0.41.0.dev0.dist-info/METADATA +143 -0
- sqlalchemy_cratedb-0.41.0.dev0.dist-info/NOTICE +24 -0
- sqlalchemy_cratedb-0.41.0.dev0.dist-info/RECORD +26 -0
- sqlalchemy_cratedb-0.41.0.dev0.dist-info/WHEEL +5 -0
- sqlalchemy_cratedb-0.41.0.dev0.dist-info/entry_points.txt +2 -0
- sqlalchemy_cratedb-0.41.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,423 @@
|
|
1
|
+
# -*- coding: utf-8; -*-
|
2
|
+
#
|
3
|
+
# Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
|
4
|
+
# license agreements. See the NOTICE file distributed with this work for
|
5
|
+
# additional information regarding copyright ownership. Crate licenses
|
6
|
+
# this file to you under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License. You may
|
8
|
+
# obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
14
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
15
|
+
# License for the specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
#
|
18
|
+
# However, if you have executed another commercial license agreement
|
19
|
+
# with Crate these terms will supersede the license and you may use the
|
20
|
+
# software solely pursuant to the terms of the relevant commercial agreement.
|
21
|
+
|
22
|
+
# ruff: noqa: S101 # Use of `assert` detected
|
23
|
+
|
24
|
+
from typing import Any, Dict, List, MutableMapping, Optional, Tuple, Union
|
25
|
+
|
26
|
+
import sqlalchemy as sa
|
27
|
+
from sqlalchemy import ColumnClause, ValuesBase, cast, exc
|
28
|
+
from sqlalchemy.sql import dml
|
29
|
+
from sqlalchemy.sql.base import _from_objects
|
30
|
+
from sqlalchemy.sql.compiler import SQLCompiler
|
31
|
+
from sqlalchemy.sql.crud import (
|
32
|
+
REQUIRED,
|
33
|
+
_as_dml_column,
|
34
|
+
_create_bind_param,
|
35
|
+
_CrudParamElement,
|
36
|
+
_CrudParams,
|
37
|
+
_extend_values_for_multiparams,
|
38
|
+
_get_stmt_parameter_tuples_params,
|
39
|
+
_get_update_multitable_params,
|
40
|
+
_key_getters_for_crud_column,
|
41
|
+
_scan_cols,
|
42
|
+
_scan_insert_from_select_cols,
|
43
|
+
_setup_delete_return_defaults,
|
44
|
+
)
|
45
|
+
from sqlalchemy.sql.dml import DMLState, _DMLColumnElement
|
46
|
+
from sqlalchemy.sql.dml import isinsert as _compile_state_isinsert
|
47
|
+
|
48
|
+
from sqlalchemy_cratedb.compiler import CrateCompiler
|
49
|
+
|
50
|
+
|
51
|
+
class CrateCompilerSA20(CrateCompiler):
|
52
|
+
def visit_update(self, update_stmt, **kw):
|
53
|
+
compile_state = update_stmt._compile_state_factory(update_stmt, self, **kw)
|
54
|
+
update_stmt = compile_state.statement
|
55
|
+
|
56
|
+
# [20] CrateDB patch.
|
57
|
+
if not compile_state._dict_parameters and not hasattr(update_stmt, "_crate_specific"):
|
58
|
+
return super().visit_update(update_stmt, **kw)
|
59
|
+
|
60
|
+
toplevel = not self.stack
|
61
|
+
if toplevel:
|
62
|
+
self.isupdate = True
|
63
|
+
if not self.dml_compile_state:
|
64
|
+
self.dml_compile_state = compile_state
|
65
|
+
if not self.compile_state:
|
66
|
+
self.compile_state = compile_state
|
67
|
+
|
68
|
+
extra_froms = compile_state._extra_froms
|
69
|
+
is_multitable = bool(extra_froms)
|
70
|
+
|
71
|
+
if is_multitable:
|
72
|
+
# main table might be a JOIN
|
73
|
+
main_froms = set(_from_objects(update_stmt.table))
|
74
|
+
render_extra_froms = [f for f in extra_froms if f not in main_froms]
|
75
|
+
correlate_froms = main_froms.union(extra_froms)
|
76
|
+
else:
|
77
|
+
render_extra_froms = []
|
78
|
+
correlate_froms = {update_stmt.table}
|
79
|
+
|
80
|
+
self.stack.append(
|
81
|
+
{
|
82
|
+
"correlate_froms": correlate_froms,
|
83
|
+
"asfrom_froms": correlate_froms,
|
84
|
+
"selectable": update_stmt,
|
85
|
+
}
|
86
|
+
)
|
87
|
+
|
88
|
+
text = "UPDATE "
|
89
|
+
|
90
|
+
if update_stmt._prefixes:
|
91
|
+
text += self._generate_prefixes(update_stmt, update_stmt._prefixes, **kw)
|
92
|
+
|
93
|
+
table_text = self.update_tables_clause(
|
94
|
+
update_stmt, update_stmt.table, render_extra_froms, **kw
|
95
|
+
)
|
96
|
+
# [20] CrateDB patch.
|
97
|
+
crud_params_struct = _get_crud_params(self, update_stmt, compile_state, toplevel, **kw)
|
98
|
+
crud_params = crud_params_struct.single_params
|
99
|
+
|
100
|
+
if update_stmt._hints:
|
101
|
+
dialect_hints, table_text = self._setup_crud_hints(update_stmt, table_text)
|
102
|
+
else:
|
103
|
+
dialect_hints = None
|
104
|
+
|
105
|
+
if update_stmt._independent_ctes:
|
106
|
+
self._dispatch_independent_ctes(update_stmt, kw)
|
107
|
+
|
108
|
+
text += table_text
|
109
|
+
|
110
|
+
text += " SET "
|
111
|
+
|
112
|
+
# [20] CrateDB patch begin.
|
113
|
+
include_table = extra_froms and self.render_table_with_column_in_update_from
|
114
|
+
|
115
|
+
set_clauses = []
|
116
|
+
|
117
|
+
for c, expr, value, _ in crud_params: # noqa: B007
|
118
|
+
key = c._compiler_dispatch(self, include_table=include_table)
|
119
|
+
clause = key + " = " + value
|
120
|
+
set_clauses.append(clause)
|
121
|
+
|
122
|
+
for k, v in compile_state._dict_parameters.items():
|
123
|
+
if isinstance(k, str) and "[" in k:
|
124
|
+
bindparam = sa.sql.bindparam(k, v)
|
125
|
+
clause = k + " = " + self.process(bindparam)
|
126
|
+
set_clauses.append(clause)
|
127
|
+
|
128
|
+
text += ", ".join(set_clauses)
|
129
|
+
# [20] CrateDB patch end.
|
130
|
+
|
131
|
+
if self.implicit_returning or update_stmt._returning:
|
132
|
+
if self.returning_precedes_values:
|
133
|
+
text += " " + self.returning_clause(
|
134
|
+
update_stmt,
|
135
|
+
self.implicit_returning or update_stmt._returning,
|
136
|
+
populate_result_map=toplevel,
|
137
|
+
)
|
138
|
+
|
139
|
+
if extra_froms:
|
140
|
+
extra_from_text = self.update_from_clause(
|
141
|
+
update_stmt,
|
142
|
+
update_stmt.table,
|
143
|
+
render_extra_froms,
|
144
|
+
dialect_hints,
|
145
|
+
**kw,
|
146
|
+
)
|
147
|
+
if extra_from_text:
|
148
|
+
text += " " + extra_from_text
|
149
|
+
|
150
|
+
if update_stmt._where_criteria:
|
151
|
+
t = self._generate_delimited_and_list(update_stmt._where_criteria, **kw)
|
152
|
+
if t:
|
153
|
+
text += " WHERE " + t
|
154
|
+
|
155
|
+
limit_clause = self.update_limit_clause(update_stmt)
|
156
|
+
if limit_clause:
|
157
|
+
text += " " + limit_clause
|
158
|
+
|
159
|
+
if (
|
160
|
+
self.implicit_returning or update_stmt._returning
|
161
|
+
) and not self.returning_precedes_values:
|
162
|
+
text += " " + self.returning_clause(
|
163
|
+
update_stmt,
|
164
|
+
self.implicit_returning or update_stmt._returning,
|
165
|
+
populate_result_map=toplevel,
|
166
|
+
)
|
167
|
+
|
168
|
+
if self.ctes:
|
169
|
+
nesting_level = len(self.stack) if not toplevel else None
|
170
|
+
text = self._render_cte_clause(nesting_level=nesting_level) + text
|
171
|
+
|
172
|
+
self.stack.pop(-1)
|
173
|
+
|
174
|
+
return text
|
175
|
+
|
176
|
+
|
177
|
+
def _get_crud_params(
|
178
|
+
compiler: SQLCompiler,
|
179
|
+
stmt: ValuesBase,
|
180
|
+
compile_state: DMLState,
|
181
|
+
toplevel: bool,
|
182
|
+
**kw: Any,
|
183
|
+
) -> _CrudParams:
|
184
|
+
"""create a set of tuples representing column/string pairs for use
|
185
|
+
in an INSERT or UPDATE statement.
|
186
|
+
|
187
|
+
Also generates the Compiled object's postfetch, prefetch, and
|
188
|
+
returning column collections, used for default handling and ultimately
|
189
|
+
populating the CursorResult's prefetch_cols() and postfetch_cols()
|
190
|
+
collections.
|
191
|
+
|
192
|
+
"""
|
193
|
+
|
194
|
+
# note: the _get_crud_params() system was written with the notion in mind
|
195
|
+
# that INSERT, UPDATE, DELETE are always the top level statement and
|
196
|
+
# that there is only one of them. With the addition of CTEs that can
|
197
|
+
# make use of DML, this assumption is no longer accurate; the DML
|
198
|
+
# statement is not necessarily the top-level "row returning" thing
|
199
|
+
# and it is also theoretically possible (fortunately nobody has asked yet)
|
200
|
+
# to have a single statement with multiple DMLs inside of it via CTEs.
|
201
|
+
|
202
|
+
# the current _get_crud_params() design doesn't accommodate these cases
|
203
|
+
# right now. It "just works" for a CTE that has a single DML inside of
|
204
|
+
# it, and for a CTE with multiple DML, it's not clear what would happen.
|
205
|
+
|
206
|
+
# overall, the "compiler.XYZ" collections here would need to be in a
|
207
|
+
# per-DML structure of some kind, and DefaultDialect would need to
|
208
|
+
# navigate these collections on a per-statement basis, with additional
|
209
|
+
# emphasis on the "toplevel returning data" statement. However we
|
210
|
+
# still need to run through _get_crud_params() for all DML as we have
|
211
|
+
# Python / SQL generated column defaults that need to be rendered.
|
212
|
+
|
213
|
+
# if there is user need for this kind of thing, it's likely a post 2.0
|
214
|
+
# kind of change as it would require deep changes to DefaultDialect
|
215
|
+
# as well as here.
|
216
|
+
|
217
|
+
compiler.postfetch = []
|
218
|
+
compiler.insert_prefetch = []
|
219
|
+
compiler.update_prefetch = []
|
220
|
+
compiler.implicit_returning = []
|
221
|
+
|
222
|
+
# getters - these are normally just column.key,
|
223
|
+
# but in the case of mysql multi-table update, the rules for
|
224
|
+
# .key must conditionally take tablename into account
|
225
|
+
(
|
226
|
+
_column_as_key,
|
227
|
+
_getattr_col_key,
|
228
|
+
_col_bind_name,
|
229
|
+
) = _key_getters_for_crud_column(compiler, stmt, compile_state)
|
230
|
+
|
231
|
+
compiler._get_bind_name_for_col = _col_bind_name
|
232
|
+
|
233
|
+
if stmt._returning and stmt._return_defaults:
|
234
|
+
raise exc.CompileError(
|
235
|
+
"Can't compile statement that includes returning() and return_defaults() simultaneously"
|
236
|
+
)
|
237
|
+
|
238
|
+
if compile_state.isdelete:
|
239
|
+
_setup_delete_return_defaults(
|
240
|
+
compiler,
|
241
|
+
stmt,
|
242
|
+
compile_state,
|
243
|
+
(),
|
244
|
+
_getattr_col_key,
|
245
|
+
_column_as_key,
|
246
|
+
_col_bind_name,
|
247
|
+
(),
|
248
|
+
(),
|
249
|
+
toplevel,
|
250
|
+
kw,
|
251
|
+
)
|
252
|
+
return _CrudParams([], [])
|
253
|
+
|
254
|
+
# no parameters in the statement, no parameters in the
|
255
|
+
# compiled params - return binds for all columns
|
256
|
+
if compiler.column_keys is None and compile_state._no_parameters:
|
257
|
+
return _CrudParams(
|
258
|
+
[
|
259
|
+
(
|
260
|
+
c,
|
261
|
+
compiler.preparer.format_column(c),
|
262
|
+
_create_bind_param(compiler, c, None, required=True),
|
263
|
+
(c.key,),
|
264
|
+
)
|
265
|
+
for c in stmt.table.columns
|
266
|
+
],
|
267
|
+
[],
|
268
|
+
)
|
269
|
+
|
270
|
+
stmt_parameter_tuples: Optional[List[Tuple[Union[str, ColumnClause[Any]], Any]]]
|
271
|
+
spd: Optional[MutableMapping[_DMLColumnElement, Any]]
|
272
|
+
|
273
|
+
if _compile_state_isinsert(compile_state) and compile_state._has_multi_parameters:
|
274
|
+
mp = compile_state._multi_parameters
|
275
|
+
assert mp is not None
|
276
|
+
spd = mp[0]
|
277
|
+
stmt_parameter_tuples = list(spd.items())
|
278
|
+
elif compile_state._ordered_values:
|
279
|
+
spd = compile_state._dict_parameters
|
280
|
+
stmt_parameter_tuples = compile_state._ordered_values
|
281
|
+
elif compile_state._dict_parameters:
|
282
|
+
spd = compile_state._dict_parameters
|
283
|
+
stmt_parameter_tuples = list(spd.items())
|
284
|
+
else:
|
285
|
+
stmt_parameter_tuples = spd = None
|
286
|
+
|
287
|
+
# if we have statement parameters - set defaults in the
|
288
|
+
# compiled params
|
289
|
+
if compiler.column_keys is None:
|
290
|
+
parameters = {}
|
291
|
+
elif stmt_parameter_tuples:
|
292
|
+
assert spd is not None
|
293
|
+
parameters = {
|
294
|
+
_column_as_key(key): REQUIRED for key in compiler.column_keys if key not in spd
|
295
|
+
}
|
296
|
+
else:
|
297
|
+
parameters = {_column_as_key(key): REQUIRED for key in compiler.column_keys}
|
298
|
+
|
299
|
+
# create a list of column assignment clauses as tuples
|
300
|
+
values: List[_CrudParamElement] = []
|
301
|
+
|
302
|
+
if stmt_parameter_tuples is not None:
|
303
|
+
_get_stmt_parameter_tuples_params(
|
304
|
+
compiler,
|
305
|
+
compile_state,
|
306
|
+
parameters,
|
307
|
+
stmt_parameter_tuples,
|
308
|
+
_column_as_key,
|
309
|
+
values,
|
310
|
+
kw,
|
311
|
+
)
|
312
|
+
|
313
|
+
check_columns: Dict[str, ColumnClause[Any]] = {}
|
314
|
+
|
315
|
+
# special logic that only occurs for multi-table UPDATE
|
316
|
+
# statements
|
317
|
+
if dml.isupdate(compile_state) and compile_state.is_multitable:
|
318
|
+
_get_update_multitable_params(
|
319
|
+
compiler,
|
320
|
+
stmt,
|
321
|
+
compile_state,
|
322
|
+
stmt_parameter_tuples,
|
323
|
+
check_columns,
|
324
|
+
_col_bind_name,
|
325
|
+
_getattr_col_key,
|
326
|
+
values,
|
327
|
+
kw,
|
328
|
+
)
|
329
|
+
|
330
|
+
if _compile_state_isinsert(compile_state) and stmt._select_names:
|
331
|
+
# is an insert from select, is not a multiparams
|
332
|
+
|
333
|
+
assert not compile_state._has_multi_parameters
|
334
|
+
|
335
|
+
_scan_insert_from_select_cols(
|
336
|
+
compiler,
|
337
|
+
stmt,
|
338
|
+
compile_state,
|
339
|
+
parameters,
|
340
|
+
_getattr_col_key,
|
341
|
+
_column_as_key,
|
342
|
+
_col_bind_name,
|
343
|
+
check_columns,
|
344
|
+
values,
|
345
|
+
toplevel,
|
346
|
+
kw,
|
347
|
+
)
|
348
|
+
else:
|
349
|
+
_scan_cols(
|
350
|
+
compiler,
|
351
|
+
stmt,
|
352
|
+
compile_state,
|
353
|
+
parameters,
|
354
|
+
_getattr_col_key,
|
355
|
+
_column_as_key,
|
356
|
+
_col_bind_name,
|
357
|
+
check_columns,
|
358
|
+
values,
|
359
|
+
toplevel,
|
360
|
+
kw,
|
361
|
+
)
|
362
|
+
|
363
|
+
# [20] CrateDB patch.
|
364
|
+
#
|
365
|
+
# This sanity check performed by SQLAlchemy currently needs to be
|
366
|
+
# deactivated in order to satisfy the rewriting logic of the CrateDB
|
367
|
+
# dialect in `rewrite_update` and `visit_update`.
|
368
|
+
#
|
369
|
+
# It can be quickly reproduced by activating this section and running the
|
370
|
+
# test cases::
|
371
|
+
#
|
372
|
+
# ./bin/test -vvvv -t dict_test
|
373
|
+
#
|
374
|
+
# That croaks like::
|
375
|
+
#
|
376
|
+
# sqlalchemy.exc.CompileError: Unconsumed column names: characters_name
|
377
|
+
#
|
378
|
+
# TODO: Investigate why this is actually happening and eventually mitigate
|
379
|
+
# the root cause.
|
380
|
+
"""
|
381
|
+
if parameters and stmt_parameter_tuples:
|
382
|
+
check = (
|
383
|
+
set(parameters)
|
384
|
+
.intersection(_column_as_key(k) for k, v in stmt_parameter_tuples)
|
385
|
+
.difference(check_columns)
|
386
|
+
)
|
387
|
+
if check:
|
388
|
+
raise exc.CompileError(
|
389
|
+
"Unconsumed column names: %s"
|
390
|
+
% (", ".join("%s" % (c,) for c in check))
|
391
|
+
)
|
392
|
+
"""
|
393
|
+
|
394
|
+
if _compile_state_isinsert(compile_state) and compile_state._has_multi_parameters:
|
395
|
+
# is a multiparams, is not an insert from a select
|
396
|
+
assert not stmt._select_names
|
397
|
+
multi_extended_values = _extend_values_for_multiparams(
|
398
|
+
compiler,
|
399
|
+
stmt,
|
400
|
+
compile_state,
|
401
|
+
cast(
|
402
|
+
"Sequence[_CrudParamElementStr]",
|
403
|
+
values,
|
404
|
+
),
|
405
|
+
cast("Callable[..., str]", _column_as_key),
|
406
|
+
kw,
|
407
|
+
)
|
408
|
+
return _CrudParams(values, multi_extended_values)
|
409
|
+
elif not values and compiler.for_executemany and compiler.dialect.supports_default_metavalue:
|
410
|
+
# convert an "INSERT DEFAULT VALUES"
|
411
|
+
# into INSERT (firstcol) VALUES (DEFAULT) which can be turned
|
412
|
+
# into an in-place multi values. This supports
|
413
|
+
# insert_executemany_returning mode :)
|
414
|
+
values = [
|
415
|
+
(
|
416
|
+
_as_dml_column(stmt.table.columns[0]),
|
417
|
+
compiler.preparer.format_column(stmt.table.columns[0]),
|
418
|
+
compiler.dialect.default_metavalue_token,
|
419
|
+
(),
|
420
|
+
)
|
421
|
+
]
|
422
|
+
|
423
|
+
return _CrudParams(values, [])
|