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,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, [])