crate 0.29.0__py2.py3-none-any.whl → 0.30.1__py2.py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- crate/client/__init__.py +1 -1
- crate/client/_pep440.py +501 -0
- crate/client/connection.py +3 -3
- crate/client/sqlalchemy/__init__.py +24 -0
- crate/client/sqlalchemy/compat/__init__.py +0 -0
- crate/client/sqlalchemy/compat/api13.py +156 -0
- crate/client/sqlalchemy/compat/core10.py +264 -0
- crate/client/sqlalchemy/compat/core14.py +359 -0
- crate/client/sqlalchemy/compat/core20.py +447 -0
- crate/client/sqlalchemy/compiler.py +1 -481
- crate/client/sqlalchemy/dialect.py +32 -17
- crate/client/sqlalchemy/sa_version.py +4 -3
- crate/client/sqlalchemy/tests/__init__.py +17 -6
- crate/client/sqlalchemy/tests/array_test.py +6 -3
- crate/client/sqlalchemy/tests/bulk_test.py +7 -4
- crate/client/sqlalchemy/tests/compiler_test.py +10 -9
- crate/client/sqlalchemy/tests/connection_test.py +25 -11
- crate/client/sqlalchemy/tests/create_table_test.py +19 -16
- crate/client/sqlalchemy/tests/datetime_test.py +6 -3
- crate/client/sqlalchemy/tests/dialect_test.py +42 -13
- crate/client/sqlalchemy/tests/dict_test.py +17 -13
- crate/client/sqlalchemy/tests/function_test.py +6 -3
- crate/client/sqlalchemy/tests/insert_from_select_test.py +9 -6
- crate/client/sqlalchemy/tests/match_test.py +6 -3
- crate/client/sqlalchemy/tests/update_test.py +6 -3
- crate/client/sqlalchemy/tests/warnings_test.py +33 -0
- crate/client/test_connection.py +25 -0
- crate/client/tests.py +98 -119
- crate/testing/layer.py +1 -1
- crate/testing/settings.py +51 -0
- crate/testing/test_layer.py +188 -2
- crate/testing/tests.py +2 -38
- crate/testing/util.py +20 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/METADATA +10 -8
- crate-0.30.1.dist-info/RECORD +53 -0
- crate-0.29.0.dist-info/RECORD +0 -44
- /crate-0.29.0-py3.9-nspkg.pth → /crate-0.30.1-py3.9-nspkg.pth +0 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/LICENSE +0 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/NOTICE +0 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/WHEEL +0 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/entry_points.txt +0 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/namespace_packages.txt +0 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,156 @@
|
|
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
|
+
# https://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
|
+
"""
|
23
|
+
Compatibility module for running a subset of SQLAlchemy 2.0 programs on
|
24
|
+
SQLAlchemy 1.3. By using monkey-patching, it can do two things:
|
25
|
+
|
26
|
+
1. Add the `exec_driver_sql` method to SA's `Connection` and `Engine`.
|
27
|
+
2. Amend the `sql.select` function to accept the calling semantics of
|
28
|
+
the modern variant.
|
29
|
+
|
30
|
+
Reason: `exec_driver_sql` gets used within the CrateDB dialect already,
|
31
|
+
and the new calling semantics of `sql.select` already get used within
|
32
|
+
many of the test cases already. Please note that the patch for
|
33
|
+
`sql.select` is only applied when running the test suite.
|
34
|
+
"""
|
35
|
+
|
36
|
+
import collections.abc as collections_abc
|
37
|
+
|
38
|
+
from sqlalchemy import exc
|
39
|
+
from sqlalchemy.sql import Select
|
40
|
+
from sqlalchemy.sql import select as original_select
|
41
|
+
from sqlalchemy.util import immutabledict
|
42
|
+
|
43
|
+
|
44
|
+
# `_distill_params_20` copied from SA14's `sqlalchemy.engine.{base,util}`.
|
45
|
+
_no_tuple = ()
|
46
|
+
_no_kw = immutabledict()
|
47
|
+
|
48
|
+
|
49
|
+
def _distill_params_20(params):
|
50
|
+
if params is None:
|
51
|
+
return _no_tuple, _no_kw
|
52
|
+
elif isinstance(params, list):
|
53
|
+
# collections_abc.MutableSequence): # avoid abc.__instancecheck__
|
54
|
+
if params and not isinstance(params[0], (collections_abc.Mapping, tuple)):
|
55
|
+
raise exc.ArgumentError(
|
56
|
+
"List argument must consist only of tuples or dictionaries"
|
57
|
+
)
|
58
|
+
|
59
|
+
return (params,), _no_kw
|
60
|
+
elif isinstance(
|
61
|
+
params,
|
62
|
+
(tuple, dict, immutabledict),
|
63
|
+
# only do abc.__instancecheck__ for Mapping after we've checked
|
64
|
+
# for plain dictionaries and would otherwise raise
|
65
|
+
) or isinstance(params, collections_abc.Mapping):
|
66
|
+
return (params,), _no_kw
|
67
|
+
else:
|
68
|
+
raise exc.ArgumentError("mapping or sequence expected for parameters")
|
69
|
+
|
70
|
+
|
71
|
+
def exec_driver_sql(self, statement, parameters=None, execution_options=None):
|
72
|
+
"""
|
73
|
+
Adapter for `exec_driver_sql`, which is available since SA14, for SA13.
|
74
|
+
"""
|
75
|
+
if execution_options is not None:
|
76
|
+
raise ValueError(
|
77
|
+
"SA13 backward-compatibility: "
|
78
|
+
"`exec_driver_sql` does not support `execution_options`"
|
79
|
+
)
|
80
|
+
args_10style, kwargs_10style = _distill_params_20(parameters)
|
81
|
+
return self.execute(statement, *args_10style, **kwargs_10style)
|
82
|
+
|
83
|
+
|
84
|
+
def monkeypatch_add_exec_driver_sql():
|
85
|
+
"""
|
86
|
+
Transparently add SA14's `exec_driver_sql()` method to SA13.
|
87
|
+
|
88
|
+
AttributeError: 'Connection' object has no attribute 'exec_driver_sql'
|
89
|
+
AttributeError: 'Engine' object has no attribute 'exec_driver_sql'
|
90
|
+
"""
|
91
|
+
from sqlalchemy.engine.base import Connection, Engine
|
92
|
+
|
93
|
+
# Add `exec_driver_sql` method to SA's `Connection` and `Engine` classes.
|
94
|
+
Connection.exec_driver_sql = exec_driver_sql
|
95
|
+
Engine.exec_driver_sql = exec_driver_sql
|
96
|
+
|
97
|
+
|
98
|
+
def select_sa14(*columns, **kw) -> Select:
|
99
|
+
"""
|
100
|
+
Adapt SA14/SA20's calling semantics of `sql.select()` to SA13.
|
101
|
+
|
102
|
+
With SA20, `select()` no longer accepts varied constructor arguments, only
|
103
|
+
the "generative" style of `select()` will be supported. The list of columns
|
104
|
+
/ tables to select from should be passed positionally.
|
105
|
+
|
106
|
+
Derived from https://github.com/sqlalchemy/alembic/blob/b1fad6b6/alembic/util/sqla_compat.py#L557-L558
|
107
|
+
|
108
|
+
sqlalchemy.exc.ArgumentError: columns argument to select() must be a Python list or other iterable
|
109
|
+
"""
|
110
|
+
if isinstance(columns, tuple) and isinstance(columns[0], list):
|
111
|
+
if "whereclause" in kw:
|
112
|
+
raise ValueError(
|
113
|
+
"SA13 backward-compatibility: "
|
114
|
+
"`whereclause` is both in kwargs and columns tuple"
|
115
|
+
)
|
116
|
+
columns, whereclause = columns
|
117
|
+
kw["whereclause"] = whereclause
|
118
|
+
return original_select(columns, **kw)
|
119
|
+
|
120
|
+
|
121
|
+
def monkeypatch_amend_select_sa14():
|
122
|
+
"""
|
123
|
+
Make SA13's `sql.select()` transparently accept calling semantics of SA14
|
124
|
+
and SA20, by swapping in the newer variant of `select_sa14()`.
|
125
|
+
|
126
|
+
This supports the test suite of `crate-python`, because it already uses the
|
127
|
+
modern calling semantics.
|
128
|
+
"""
|
129
|
+
import sqlalchemy
|
130
|
+
|
131
|
+
sqlalchemy.select = select_sa14
|
132
|
+
sqlalchemy.sql.select = select_sa14
|
133
|
+
sqlalchemy.sql.expression.select = select_sa14
|
134
|
+
|
135
|
+
|
136
|
+
@property
|
137
|
+
def connectionfairy_driver_connection_sa14(self):
|
138
|
+
"""The connection object as returned by the driver after a connect.
|
139
|
+
|
140
|
+
.. versionadded:: 1.4.24
|
141
|
+
|
142
|
+
.. seealso::
|
143
|
+
|
144
|
+
:attr:`._ConnectionFairy.dbapi_connection`
|
145
|
+
|
146
|
+
:attr:`._ConnectionRecord.driver_connection`
|
147
|
+
|
148
|
+
:ref:`faq_dbapi_connection`
|
149
|
+
|
150
|
+
"""
|
151
|
+
return self.connection
|
152
|
+
|
153
|
+
|
154
|
+
def monkeypatch_add_connectionfairy_driver_connection():
|
155
|
+
import sqlalchemy.pool.base
|
156
|
+
sqlalchemy.pool.base._ConnectionFairy.driver_connection = connectionfairy_driver_connection_sa14
|
@@ -0,0 +1,264 @@
|
|
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
|
+
import sqlalchemy as sa
|
23
|
+
from sqlalchemy.dialects.postgresql.base import PGCompiler
|
24
|
+
from sqlalchemy.sql.crud import (REQUIRED, _create_bind_param,
|
25
|
+
_extend_values_for_multiparams,
|
26
|
+
_get_multitable_params,
|
27
|
+
_get_stmt_parameters_params,
|
28
|
+
_key_getters_for_crud_column, _scan_cols,
|
29
|
+
_scan_insert_from_select_cols)
|
30
|
+
|
31
|
+
from crate.client.sqlalchemy.compiler import CrateCompiler
|
32
|
+
|
33
|
+
|
34
|
+
class CrateCompilerSA10(CrateCompiler):
|
35
|
+
|
36
|
+
def returning_clause(self, stmt, returning_cols):
|
37
|
+
"""
|
38
|
+
Generate RETURNING clause, PostgreSQL-compatible.
|
39
|
+
"""
|
40
|
+
return PGCompiler.returning_clause(self, stmt, returning_cols)
|
41
|
+
|
42
|
+
def visit_update(self, update_stmt, **kw):
|
43
|
+
"""
|
44
|
+
used to compile <sql.expression.Update> expressions
|
45
|
+
Parts are taken from the SQLCompiler base class.
|
46
|
+
"""
|
47
|
+
|
48
|
+
# [10] CrateDB patch.
|
49
|
+
if not update_stmt.parameters and \
|
50
|
+
not hasattr(update_stmt, '_crate_specific'):
|
51
|
+
return super().visit_update(update_stmt, **kw)
|
52
|
+
|
53
|
+
self.isupdate = True
|
54
|
+
|
55
|
+
extra_froms = update_stmt._extra_froms
|
56
|
+
|
57
|
+
text = 'UPDATE '
|
58
|
+
|
59
|
+
if update_stmt._prefixes:
|
60
|
+
text += self._generate_prefixes(update_stmt,
|
61
|
+
update_stmt._prefixes, **kw)
|
62
|
+
|
63
|
+
table_text = self.update_tables_clause(update_stmt, update_stmt.table,
|
64
|
+
extra_froms, **kw)
|
65
|
+
|
66
|
+
dialect_hints = None
|
67
|
+
if update_stmt._hints:
|
68
|
+
dialect_hints, table_text = self._setup_crud_hints(
|
69
|
+
update_stmt, table_text
|
70
|
+
)
|
71
|
+
|
72
|
+
# [10] CrateDB patch.
|
73
|
+
crud_params = _get_crud_params(self, update_stmt, **kw)
|
74
|
+
|
75
|
+
text += table_text
|
76
|
+
|
77
|
+
text += ' SET '
|
78
|
+
|
79
|
+
# [10] CrateDB patch begin.
|
80
|
+
include_table = \
|
81
|
+
extra_froms and self.render_table_with_column_in_update_from
|
82
|
+
|
83
|
+
set_clauses = []
|
84
|
+
|
85
|
+
for k, v in crud_params:
|
86
|
+
clause = k._compiler_dispatch(self,
|
87
|
+
include_table=include_table) + \
|
88
|
+
' = ' + v
|
89
|
+
set_clauses.append(clause)
|
90
|
+
|
91
|
+
for k, v in update_stmt.parameters.items():
|
92
|
+
if isinstance(k, str) and '[' in k:
|
93
|
+
bindparam = sa.sql.bindparam(k, v)
|
94
|
+
set_clauses.append(k + ' = ' + self.process(bindparam))
|
95
|
+
|
96
|
+
text += ', '.join(set_clauses)
|
97
|
+
# [10] CrateDB patch end.
|
98
|
+
|
99
|
+
if self.returning or update_stmt._returning:
|
100
|
+
if not self.returning:
|
101
|
+
self.returning = update_stmt._returning
|
102
|
+
if self.returning_precedes_values:
|
103
|
+
text += " " + self.returning_clause(
|
104
|
+
update_stmt, self.returning)
|
105
|
+
|
106
|
+
if extra_froms:
|
107
|
+
extra_from_text = self.update_from_clause(
|
108
|
+
update_stmt,
|
109
|
+
update_stmt.table,
|
110
|
+
extra_froms,
|
111
|
+
dialect_hints,
|
112
|
+
**kw)
|
113
|
+
if extra_from_text:
|
114
|
+
text += " " + extra_from_text
|
115
|
+
|
116
|
+
if update_stmt._whereclause is not None:
|
117
|
+
t = self.process(update_stmt._whereclause)
|
118
|
+
if t:
|
119
|
+
text += " WHERE " + t
|
120
|
+
|
121
|
+
limit_clause = self.update_limit_clause(update_stmt)
|
122
|
+
if limit_clause:
|
123
|
+
text += " " + limit_clause
|
124
|
+
|
125
|
+
if self.returning and not self.returning_precedes_values:
|
126
|
+
text += " " + self.returning_clause(
|
127
|
+
update_stmt, self.returning)
|
128
|
+
|
129
|
+
return text
|
130
|
+
|
131
|
+
|
132
|
+
def _get_crud_params(compiler, stmt, **kw):
|
133
|
+
"""create a set of tuples representing column/string pairs for use
|
134
|
+
in an INSERT or UPDATE statement.
|
135
|
+
|
136
|
+
Also generates the Compiled object's postfetch, prefetch, and
|
137
|
+
returning column collections, used for default handling and ultimately
|
138
|
+
populating the ResultProxy's prefetch_cols() and postfetch_cols()
|
139
|
+
collections.
|
140
|
+
|
141
|
+
"""
|
142
|
+
|
143
|
+
compiler.postfetch = []
|
144
|
+
compiler.insert_prefetch = []
|
145
|
+
compiler.update_prefetch = []
|
146
|
+
compiler.returning = []
|
147
|
+
|
148
|
+
# no parameters in the statement, no parameters in the
|
149
|
+
# compiled params - return binds for all columns
|
150
|
+
if compiler.column_keys is None and stmt.parameters is None:
|
151
|
+
return [
|
152
|
+
(c, _create_bind_param(compiler, c, None, required=True))
|
153
|
+
for c in stmt.table.columns
|
154
|
+
]
|
155
|
+
|
156
|
+
if stmt._has_multi_parameters:
|
157
|
+
stmt_parameters = stmt.parameters[0]
|
158
|
+
else:
|
159
|
+
stmt_parameters = stmt.parameters
|
160
|
+
|
161
|
+
# getters - these are normally just column.key,
|
162
|
+
# but in the case of mysql multi-table update, the rules for
|
163
|
+
# .key must conditionally take tablename into account
|
164
|
+
(
|
165
|
+
_column_as_key,
|
166
|
+
_getattr_col_key,
|
167
|
+
_col_bind_name,
|
168
|
+
) = _key_getters_for_crud_column(compiler, stmt)
|
169
|
+
|
170
|
+
# if we have statement parameters - set defaults in the
|
171
|
+
# compiled params
|
172
|
+
if compiler.column_keys is None:
|
173
|
+
parameters = {}
|
174
|
+
else:
|
175
|
+
parameters = dict(
|
176
|
+
(_column_as_key(key), REQUIRED)
|
177
|
+
for key in compiler.column_keys
|
178
|
+
if not stmt_parameters or key not in stmt_parameters
|
179
|
+
)
|
180
|
+
|
181
|
+
# create a list of column assignment clauses as tuples
|
182
|
+
values = []
|
183
|
+
|
184
|
+
if stmt_parameters is not None:
|
185
|
+
_get_stmt_parameters_params(
|
186
|
+
compiler, parameters, stmt_parameters, _column_as_key, values, kw
|
187
|
+
)
|
188
|
+
|
189
|
+
check_columns = {}
|
190
|
+
|
191
|
+
# special logic that only occurs for multi-table UPDATE
|
192
|
+
# statements
|
193
|
+
if compiler.isupdate and stmt._extra_froms and stmt_parameters:
|
194
|
+
_get_multitable_params(
|
195
|
+
compiler,
|
196
|
+
stmt,
|
197
|
+
stmt_parameters,
|
198
|
+
check_columns,
|
199
|
+
_col_bind_name,
|
200
|
+
_getattr_col_key,
|
201
|
+
values,
|
202
|
+
kw,
|
203
|
+
)
|
204
|
+
|
205
|
+
if compiler.isinsert and stmt.select_names:
|
206
|
+
_scan_insert_from_select_cols(
|
207
|
+
compiler,
|
208
|
+
stmt,
|
209
|
+
parameters,
|
210
|
+
_getattr_col_key,
|
211
|
+
_column_as_key,
|
212
|
+
_col_bind_name,
|
213
|
+
check_columns,
|
214
|
+
values,
|
215
|
+
kw,
|
216
|
+
)
|
217
|
+
else:
|
218
|
+
_scan_cols(
|
219
|
+
compiler,
|
220
|
+
stmt,
|
221
|
+
parameters,
|
222
|
+
_getattr_col_key,
|
223
|
+
_column_as_key,
|
224
|
+
_col_bind_name,
|
225
|
+
check_columns,
|
226
|
+
values,
|
227
|
+
kw,
|
228
|
+
)
|
229
|
+
|
230
|
+
# [10] CrateDB patch.
|
231
|
+
#
|
232
|
+
# This sanity check performed by SQLAlchemy currently needs to be
|
233
|
+
# deactivated in order to satisfy the rewriting logic of the CrateDB
|
234
|
+
# dialect in `rewrite_update` and `visit_update`.
|
235
|
+
#
|
236
|
+
# It can be quickly reproduced by activating this section and running the
|
237
|
+
# test cases::
|
238
|
+
#
|
239
|
+
# ./bin/test -vvvv -t dict_test
|
240
|
+
#
|
241
|
+
# That croaks like::
|
242
|
+
#
|
243
|
+
# sqlalchemy.exc.CompileError: Unconsumed column names: characters_name, data['nested']
|
244
|
+
#
|
245
|
+
# TODO: Investigate why this is actually happening and eventually mitigate
|
246
|
+
# the root cause.
|
247
|
+
"""
|
248
|
+
if parameters and stmt_parameters:
|
249
|
+
check = (
|
250
|
+
set(parameters)
|
251
|
+
.intersection(_column_as_key(k) for k in stmt_parameters)
|
252
|
+
.difference(check_columns)
|
253
|
+
)
|
254
|
+
if check:
|
255
|
+
raise exc.CompileError(
|
256
|
+
"Unconsumed column names: %s"
|
257
|
+
% (", ".join("%s" % c for c in check))
|
258
|
+
)
|
259
|
+
"""
|
260
|
+
|
261
|
+
if stmt._has_multi_parameters:
|
262
|
+
values = _extend_values_for_multiparams(compiler, stmt, values, kw)
|
263
|
+
|
264
|
+
return values
|