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