crate 0.29.0__py2.py3-none-any.whl → 0.30.1__py2.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.
Files changed (43) hide show
  1. crate/client/__init__.py +1 -1
  2. crate/client/_pep440.py +501 -0
  3. crate/client/connection.py +3 -3
  4. crate/client/sqlalchemy/__init__.py +24 -0
  5. crate/client/sqlalchemy/compat/__init__.py +0 -0
  6. crate/client/sqlalchemy/compat/api13.py +156 -0
  7. crate/client/sqlalchemy/compat/core10.py +264 -0
  8. crate/client/sqlalchemy/compat/core14.py +359 -0
  9. crate/client/sqlalchemy/compat/core20.py +447 -0
  10. crate/client/sqlalchemy/compiler.py +1 -481
  11. crate/client/sqlalchemy/dialect.py +32 -17
  12. crate/client/sqlalchemy/sa_version.py +4 -3
  13. crate/client/sqlalchemy/tests/__init__.py +17 -6
  14. crate/client/sqlalchemy/tests/array_test.py +6 -3
  15. crate/client/sqlalchemy/tests/bulk_test.py +7 -4
  16. crate/client/sqlalchemy/tests/compiler_test.py +10 -9
  17. crate/client/sqlalchemy/tests/connection_test.py +25 -11
  18. crate/client/sqlalchemy/tests/create_table_test.py +19 -16
  19. crate/client/sqlalchemy/tests/datetime_test.py +6 -3
  20. crate/client/sqlalchemy/tests/dialect_test.py +42 -13
  21. crate/client/sqlalchemy/tests/dict_test.py +17 -13
  22. crate/client/sqlalchemy/tests/function_test.py +6 -3
  23. crate/client/sqlalchemy/tests/insert_from_select_test.py +9 -6
  24. crate/client/sqlalchemy/tests/match_test.py +6 -3
  25. crate/client/sqlalchemy/tests/update_test.py +6 -3
  26. crate/client/sqlalchemy/tests/warnings_test.py +33 -0
  27. crate/client/test_connection.py +25 -0
  28. crate/client/tests.py +98 -119
  29. crate/testing/layer.py +1 -1
  30. crate/testing/settings.py +51 -0
  31. crate/testing/test_layer.py +188 -2
  32. crate/testing/tests.py +2 -38
  33. crate/testing/util.py +20 -0
  34. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/METADATA +10 -8
  35. crate-0.30.1.dist-info/RECORD +53 -0
  36. crate-0.29.0.dist-info/RECORD +0 -44
  37. /crate-0.29.0-py3.9-nspkg.pth → /crate-0.30.1-py3.9-nspkg.pth +0 -0
  38. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/LICENSE +0 -0
  39. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/NOTICE +0 -0
  40. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/WHEEL +0 -0
  41. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/entry_points.txt +0 -0
  42. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/namespace_packages.txt +0 -0
  43. {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