1# sql/crud.py
2# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7# mypy: allow-untyped-defs, allow-untyped-calls
8
9"""Functions used by compiler.py to determine the parameters rendered
10within INSERT and UPDATE statements.
11
12"""
13from __future__ import annotations
14
15import functools
16import operator
17from typing import Any
18from typing import Callable
19from typing import cast
20from typing import Dict
21from typing import Iterable
22from typing import List
23from typing import MutableMapping
24from typing import NamedTuple
25from typing import Optional
26from typing import overload
27from typing import Sequence
28from typing import Set
29from typing import Tuple
30from typing import TYPE_CHECKING
31from typing import Union
32
33from . import coercions
34from . import dml
35from . import elements
36from . import roles
37from .base import _DefaultDescriptionTuple
38from .dml import isinsert as _compile_state_isinsert
39from .elements import ColumnClause
40from .schema import default_is_clause_element
41from .schema import default_is_sequence
42from .selectable import Select
43from .selectable import TableClause
44from .. import exc
45from .. import util
46from ..util.typing import Literal
47
48if TYPE_CHECKING:
49 from .compiler import _BindNameForColProtocol
50 from .compiler import SQLCompiler
51 from .dml import _DMLColumnElement
52 from .dml import DMLState
53 from .dml import ValuesBase
54 from .elements import ColumnElement
55 from .elements import KeyedColumnElement
56 from .schema import _SQLExprDefault
57 from .schema import Column
58
59REQUIRED = util.symbol(
60 "REQUIRED",
61 """
62Placeholder for the value within a :class:`.BindParameter`
63which is required to be present when the statement is passed
64to :meth:`_engine.Connection.execute`.
65
66This symbol is typically used when a :func:`_expression.insert`
67or :func:`_expression.update` statement is compiled without parameter
68values present.
69
70""",
71)
72
73
74def _as_dml_column(c: ColumnElement[Any]) -> ColumnClause[Any]:
75 if not isinstance(c, ColumnClause):
76 raise exc.CompileError(
77 f"Can't create DML statement against column expression {c!r}"
78 )
79 return c
80
81
82_CrudParamElement = Tuple[
83 "ColumnElement[Any]",
84 str, # column name
85 Optional[
86 Union[str, "_SQLExprDefault"]
87 ], # bound parameter string or SQL expression to apply
88 Iterable[str],
89]
90_CrudParamElementStr = Tuple[
91 "KeyedColumnElement[Any]",
92 str, # column name
93 str, # bound parameter string
94 Iterable[str],
95]
96_CrudParamElementSQLExpr = Tuple[
97 "ColumnClause[Any]",
98 str,
99 "_SQLExprDefault", # SQL expression to apply
100 Iterable[str],
101]
102
103_CrudParamSequence = List[_CrudParamElement]
104
105
106class _CrudParams(NamedTuple):
107 single_params: _CrudParamSequence
108 all_multi_params: List[Sequence[_CrudParamElementStr]]
109 is_default_metavalue_only: bool = False
110 use_insertmanyvalues: bool = False
111 use_sentinel_columns: Optional[Sequence[Column[Any]]] = None
112
113
114def _get_crud_params(
115 compiler: SQLCompiler,
116 stmt: ValuesBase,
117 compile_state: DMLState,
118 toplevel: bool,
119 **kw: Any,
120) -> _CrudParams:
121 """create a set of tuples representing column/string pairs for use
122 in an INSERT or UPDATE statement.
123
124 Also generates the Compiled object's postfetch, prefetch, and
125 returning column collections, used for default handling and ultimately
126 populating the CursorResult's prefetch_cols() and postfetch_cols()
127 collections.
128
129 """
130
131 # note: the _get_crud_params() system was written with the notion in mind
132 # that INSERT, UPDATE, DELETE are always the top level statement and
133 # that there is only one of them. With the addition of CTEs that can
134 # make use of DML, this assumption is no longer accurate; the DML
135 # statement is not necessarily the top-level "row returning" thing
136 # and it is also theoretically possible (fortunately nobody has asked yet)
137 # to have a single statement with multiple DMLs inside of it via CTEs.
138
139 # the current _get_crud_params() design doesn't accommodate these cases
140 # right now. It "just works" for a CTE that has a single DML inside of
141 # it, and for a CTE with multiple DML, it's not clear what would happen.
142
143 # overall, the "compiler.XYZ" collections here would need to be in a
144 # per-DML structure of some kind, and DefaultDialect would need to
145 # navigate these collections on a per-statement basis, with additional
146 # emphasis on the "toplevel returning data" statement. However we
147 # still need to run through _get_crud_params() for all DML as we have
148 # Python / SQL generated column defaults that need to be rendered.
149
150 # if there is user need for this kind of thing, it's likely a post 2.0
151 # kind of change as it would require deep changes to DefaultDialect
152 # as well as here.
153
154 compiler.postfetch = []
155 compiler.insert_prefetch = []
156 compiler.update_prefetch = []
157 compiler.implicit_returning = []
158
159 visiting_cte = kw.get("visiting_cte", None)
160 if visiting_cte is not None:
161 # for insert -> CTE -> insert, don't populate an incoming
162 # _crud_accumulate_bind_names collection; the INSERT we process here
163 # will not be inline within the VALUES of the enclosing INSERT as the
164 # CTE is placed on the outside. See issue #9173
165 kw.pop("accumulate_bind_names", None)
166 assert (
167 "accumulate_bind_names" not in kw
168 ), "Don't know how to handle insert within insert without a CTE"
169
170 # getters - these are normally just column.key,
171 # but in the case of mysql multi-table update, the rules for
172 # .key must conditionally take tablename into account
173 (
174 _column_as_key,
175 _getattr_col_key,
176 _col_bind_name,
177 ) = _key_getters_for_crud_column(compiler, stmt, compile_state)
178
179 compiler._get_bind_name_for_col = _col_bind_name
180
181 if stmt._returning and stmt._return_defaults:
182 raise exc.CompileError(
183 "Can't compile statement that includes returning() and "
184 "return_defaults() simultaneously"
185 )
186
187 if compile_state.isdelete:
188 _setup_delete_return_defaults(
189 compiler,
190 stmt,
191 compile_state,
192 (),
193 _getattr_col_key,
194 _column_as_key,
195 _col_bind_name,
196 (),
197 (),
198 toplevel,
199 kw,
200 )
201 return _CrudParams([], [])
202
203 # no parameters in the statement, no parameters in the
204 # compiled params - return binds for all columns
205 if compiler.column_keys is None and compile_state._no_parameters:
206 return _CrudParams(
207 [
208 (
209 c,
210 compiler.preparer.format_column(c),
211 _create_bind_param(compiler, c, None, required=True),
212 (c.key,),
213 )
214 for c in stmt.table.columns
215 if not c._omit_from_statements
216 ],
217 [],
218 )
219
220 stmt_parameter_tuples: Optional[
221 List[Tuple[Union[str, ColumnClause[Any]], Any]]
222 ]
223 spd: Optional[MutableMapping[_DMLColumnElement, Any]]
224
225 if (
226 _compile_state_isinsert(compile_state)
227 and compile_state._has_multi_parameters
228 ):
229 mp = compile_state._multi_parameters
230 assert mp is not None
231 spd = mp[0]
232 stmt_parameter_tuples = list(spd.items())
233 spd_str_key = {_column_as_key(key) for key in spd}
234 elif compile_state._ordered_values:
235 spd = compile_state._dict_parameters
236 stmt_parameter_tuples = compile_state._ordered_values
237 assert spd is not None
238 spd_str_key = {_column_as_key(key) for key in spd}
239 elif compile_state._dict_parameters:
240 spd = compile_state._dict_parameters
241 stmt_parameter_tuples = list(spd.items())
242 spd_str_key = {_column_as_key(key) for key in spd}
243 else:
244 stmt_parameter_tuples = spd_str_key = None
245
246 # if we have statement parameters - set defaults in the
247 # compiled params
248 if compiler.column_keys is None:
249 parameters = {}
250 elif stmt_parameter_tuples:
251 assert spd_str_key is not None
252 parameters = {
253 _column_as_key(key): REQUIRED
254 for key in compiler.column_keys
255 if key not in spd_str_key
256 }
257 else:
258 parameters = {
259 _column_as_key(key): REQUIRED for key in compiler.column_keys
260 }
261
262 # create a list of column assignment clauses as tuples
263 values: List[_CrudParamElement] = []
264
265 if stmt_parameter_tuples is not None:
266 _get_stmt_parameter_tuples_params(
267 compiler,
268 compile_state,
269 parameters,
270 stmt_parameter_tuples,
271 _column_as_key,
272 values,
273 kw,
274 )
275
276 check_columns: Dict[str, ColumnClause[Any]] = {}
277
278 # special logic that only occurs for multi-table UPDATE
279 # statements
280 if dml.isupdate(compile_state) and compile_state.is_multitable:
281 _get_update_multitable_params(
282 compiler,
283 stmt,
284 compile_state,
285 stmt_parameter_tuples,
286 check_columns,
287 _col_bind_name,
288 _getattr_col_key,
289 values,
290 kw,
291 )
292
293 if _compile_state_isinsert(compile_state) and stmt._select_names:
294 # is an insert from select, is not a multiparams
295
296 assert not compile_state._has_multi_parameters
297
298 _scan_insert_from_select_cols(
299 compiler,
300 stmt,
301 compile_state,
302 parameters,
303 _getattr_col_key,
304 _column_as_key,
305 _col_bind_name,
306 check_columns,
307 values,
308 toplevel,
309 kw,
310 )
311 use_insertmanyvalues = False
312 use_sentinel_columns = None
313 else:
314 use_insertmanyvalues, use_sentinel_columns = _scan_cols(
315 compiler,
316 stmt,
317 compile_state,
318 parameters,
319 _getattr_col_key,
320 _column_as_key,
321 _col_bind_name,
322 check_columns,
323 values,
324 toplevel,
325 kw,
326 )
327
328 if parameters and stmt_parameter_tuples:
329 check = (
330 set(parameters)
331 .intersection(_column_as_key(k) for k, v in stmt_parameter_tuples)
332 .difference(check_columns)
333 )
334 if check:
335 raise exc.CompileError(
336 "Unconsumed column names: %s"
337 % (", ".join("%s" % (c,) for c in check))
338 )
339
340 is_default_metavalue_only = False
341
342 if (
343 _compile_state_isinsert(compile_state)
344 and compile_state._has_multi_parameters
345 ):
346 # is a multiparams, is not an insert from a select
347 assert not stmt._select_names
348 multi_extended_values = _extend_values_for_multiparams(
349 compiler,
350 stmt,
351 compile_state,
352 cast(
353 "Sequence[_CrudParamElementStr]",
354 values,
355 ),
356 cast("Callable[..., str]", _column_as_key),
357 kw,
358 )
359 return _CrudParams(values, multi_extended_values)
360 elif (
361 not values
362 and compiler.for_executemany
363 and compiler.dialect.supports_default_metavalue
364 ):
365 # convert an "INSERT DEFAULT VALUES"
366 # into INSERT (firstcol) VALUES (DEFAULT) which can be turned
367 # into an in-place multi values. This supports
368 # insert_executemany_returning mode :)
369 values = [
370 (
371 _as_dml_column(stmt.table.columns[0]),
372 compiler.preparer.format_column(stmt.table.columns[0]),
373 compiler.dialect.default_metavalue_token,
374 (),
375 )
376 ]
377 is_default_metavalue_only = True
378
379 return _CrudParams(
380 values,
381 [],
382 is_default_metavalue_only=is_default_metavalue_only,
383 use_insertmanyvalues=use_insertmanyvalues,
384 use_sentinel_columns=use_sentinel_columns,
385 )
386
387
388@overload
389def _create_bind_param(
390 compiler: SQLCompiler,
391 col: ColumnElement[Any],
392 value: Any,
393 process: Literal[True] = ...,
394 required: bool = False,
395 name: Optional[str] = None,
396 force_anonymous: bool = False,
397 **kw: Any,
398) -> str: ...
399
400
401@overload
402def _create_bind_param(
403 compiler: SQLCompiler,
404 col: ColumnElement[Any],
405 value: Any,
406 **kw: Any,
407) -> str: ...
408
409
410def _create_bind_param(
411 compiler: SQLCompiler,
412 col: ColumnElement[Any],
413 value: Any,
414 process: bool = True,
415 required: bool = False,
416 name: Optional[str] = None,
417 force_anonymous: bool = False,
418 **kw: Any,
419) -> Union[str, elements.BindParameter[Any]]:
420 if force_anonymous:
421 name = None
422 elif name is None:
423 name = col.key
424
425 bindparam = elements.BindParameter(
426 name, value, type_=col.type, required=required
427 )
428 bindparam._is_crud = True
429 if process:
430 return bindparam._compiler_dispatch(compiler, **kw)
431 else:
432 return bindparam
433
434
435def _handle_values_anonymous_param(compiler, col, value, name, **kw):
436 # the insert() and update() constructs as of 1.4 will now produce anonymous
437 # bindparam() objects in the values() collections up front when given plain
438 # literal values. This is so that cache key behaviors, which need to
439 # produce bound parameters in deterministic order without invoking any
440 # compilation here, can be applied to these constructs when they include
441 # values() (but not yet multi-values, which are not included in caching
442 # right now).
443 #
444 # in order to produce the desired "crud" style name for these parameters,
445 # which will also be targetable in engine/default.py through the usual
446 # conventions, apply our desired name to these unique parameters by
447 # populating the compiler truncated names cache with the desired name,
448 # rather than having
449 # compiler.visit_bindparam()->compiler._truncated_identifier make up a
450 # name. Saves on call counts also.
451
452 # for INSERT/UPDATE that's a CTE, we don't need names to match to
453 # external parameters and these would also conflict in the case where
454 # multiple insert/update are combined together using CTEs
455 is_cte = "visiting_cte" in kw
456
457 if (
458 not is_cte
459 and value.unique
460 and isinstance(value.key, elements._truncated_label)
461 ):
462 compiler.truncated_names[("bindparam", value.key)] = name
463
464 if value.type._isnull:
465 # either unique parameter, or other bound parameters that were
466 # passed in directly
467 # set type to that of the column unconditionally
468 value = value._with_binary_element_type(col.type)
469
470 return value._compiler_dispatch(compiler, **kw)
471
472
473def _key_getters_for_crud_column(
474 compiler: SQLCompiler, stmt: ValuesBase, compile_state: DMLState
475) -> Tuple[
476 Callable[[Union[str, ColumnClause[Any]]], Union[str, Tuple[str, str]]],
477 Callable[[ColumnClause[Any]], Union[str, Tuple[str, str]]],
478 _BindNameForColProtocol,
479]:
480 if dml.isupdate(compile_state) and compile_state._extra_froms:
481 # when extra tables are present, refer to the columns
482 # in those extra tables as table-qualified, including in
483 # dictionaries and when rendering bind param names.
484 # the "main" table of the statement remains unqualified,
485 # allowing the most compatibility with a non-multi-table
486 # statement.
487 _et = set(compile_state._extra_froms)
488
489 c_key_role = functools.partial(
490 coercions.expect_as_key, roles.DMLColumnRole
491 )
492
493 def _column_as_key(
494 key: Union[ColumnClause[Any], str],
495 ) -> Union[str, Tuple[str, str]]:
496 str_key = c_key_role(key)
497 if hasattr(key, "table") and key.table in _et:
498 return (key.table.name, str_key) # type: ignore
499 else:
500 return str_key
501
502 def _getattr_col_key(
503 col: ColumnClause[Any],
504 ) -> Union[str, Tuple[str, str]]:
505 if col.table in _et:
506 return (col.table.name, col.key) # type: ignore
507 else:
508 return col.key
509
510 def _col_bind_name(col: ColumnClause[Any]) -> str:
511 if col.table in _et:
512 if TYPE_CHECKING:
513 assert isinstance(col.table, TableClause)
514 return "%s_%s" % (col.table.name, col.key)
515 else:
516 return col.key
517
518 else:
519 _column_as_key = functools.partial(
520 coercions.expect_as_key, roles.DMLColumnRole
521 )
522 _getattr_col_key = _col_bind_name = operator.attrgetter("key") # type: ignore # noqa: E501
523
524 return _column_as_key, _getattr_col_key, _col_bind_name
525
526
527def _scan_insert_from_select_cols(
528 compiler,
529 stmt,
530 compile_state,
531 parameters,
532 _getattr_col_key,
533 _column_as_key,
534 _col_bind_name,
535 check_columns,
536 values,
537 toplevel,
538 kw,
539):
540 cols = [stmt.table.c[_column_as_key(name)] for name in stmt._select_names]
541
542 assert compiler.stack[-1]["selectable"] is stmt
543
544 compiler.stack[-1]["insert_from_select"] = stmt.select
545
546 add_select_cols: List[_CrudParamElementSQLExpr] = []
547 if stmt.include_insert_from_select_defaults:
548 col_set = set(cols)
549 for col in stmt.table.columns:
550 # omit columns that were not in the SELECT statement.
551 # this will omit columns marked as omit_from_statements naturally,
552 # as long as that col was not explicit in the SELECT.
553 # if an omit_from_statements col has a "default" on it, then
554 # we need to include it, as these defaults should still fire off.
555 # but, if it has that default and it's the "sentinel" default,
556 # we don't do sentinel default operations for insert_from_select
557 # here so we again omit it.
558 if (
559 col not in col_set
560 and col.default
561 and not col.default.is_sentinel
562 ):
563 cols.append(col)
564
565 for c in cols:
566 col_key = _getattr_col_key(c)
567 if col_key in parameters and col_key not in check_columns:
568 parameters.pop(col_key)
569 values.append((c, compiler.preparer.format_column(c), None, ()))
570 else:
571 _append_param_insert_select_hasdefault(
572 compiler, stmt, c, add_select_cols, kw
573 )
574
575 if add_select_cols:
576 values.extend(add_select_cols)
577 ins_from_select = compiler.stack[-1]["insert_from_select"]
578 if not isinstance(ins_from_select, Select):
579 raise exc.CompileError(
580 f"Can't extend statement for INSERT..FROM SELECT to include "
581 f"additional default-holding column(s) "
582 f"""{
583 ', '.join(repr(key) for _, key, _, _ in add_select_cols)
584 }. Convert the selectable to a subquery() first, or pass """
585 "include_defaults=False to Insert.from_select() to skip these "
586 "columns."
587 )
588 ins_from_select = ins_from_select._generate()
589 # copy raw_columns
590 ins_from_select._raw_columns = list(ins_from_select._raw_columns) + [
591 expr for _, _, expr, _ in add_select_cols
592 ]
593 compiler.stack[-1]["insert_from_select"] = ins_from_select
594
595
596def _scan_cols(
597 compiler,
598 stmt,
599 compile_state,
600 parameters,
601 _getattr_col_key,
602 _column_as_key,
603 _col_bind_name,
604 check_columns,
605 values,
606 toplevel,
607 kw,
608):
609 (
610 need_pks,
611 implicit_returning,
612 implicit_return_defaults,
613 postfetch_lastrowid,
614 use_insertmanyvalues,
615 use_sentinel_columns,
616 ) = _get_returning_modifiers(compiler, stmt, compile_state, toplevel)
617
618 assert compile_state.isupdate or compile_state.isinsert
619
620 if compile_state._parameter_ordering:
621 parameter_ordering = [
622 _column_as_key(key) for key in compile_state._parameter_ordering
623 ]
624 ordered_keys = set(parameter_ordering)
625 cols = [
626 stmt.table.c[key]
627 for key in parameter_ordering
628 if isinstance(key, str) and key in stmt.table.c
629 ] + [c for c in stmt.table.c if c.key not in ordered_keys]
630
631 else:
632 cols = stmt.table.columns
633
634 isinsert = _compile_state_isinsert(compile_state)
635 if isinsert and not compile_state._has_multi_parameters:
636 # new rules for #7998. fetch lastrowid or implicit returning
637 # for autoincrement column even if parameter is NULL, for DBs that
638 # override NULL param for primary key (sqlite, mysql/mariadb)
639 autoincrement_col = stmt.table._autoincrement_column
640 insert_null_pk_still_autoincrements = (
641 compiler.dialect.insert_null_pk_still_autoincrements
642 )
643 else:
644 autoincrement_col = insert_null_pk_still_autoincrements = None
645
646 if stmt._supplemental_returning:
647 supplemental_returning = set(stmt._supplemental_returning)
648 else:
649 supplemental_returning = set()
650
651 compiler_implicit_returning = compiler.implicit_returning
652
653 # TODO - see TODO(return_defaults_columns) below
654 # cols_in_params = set()
655
656 for c in cols:
657 # scan through every column in the target table
658
659 col_key = _getattr_col_key(c)
660
661 if col_key in parameters and col_key not in check_columns:
662 # parameter is present for the column. use that.
663
664 _append_param_parameter(
665 compiler,
666 stmt,
667 compile_state,
668 c,
669 col_key,
670 parameters,
671 _col_bind_name,
672 implicit_returning,
673 implicit_return_defaults,
674 postfetch_lastrowid,
675 values,
676 autoincrement_col,
677 insert_null_pk_still_autoincrements,
678 kw,
679 )
680
681 # TODO - see TODO(return_defaults_columns) below
682 # cols_in_params.add(c)
683
684 elif isinsert:
685 # no parameter is present and it's an insert.
686
687 if c.primary_key and need_pks:
688 # it's a primary key column, it will need to be generated by a
689 # default generator of some kind, and the statement expects
690 # inserted_primary_key to be available.
691
692 if implicit_returning:
693 # we can use RETURNING, find out how to invoke this
694 # column and get the value where RETURNING is an option.
695 # we can inline server-side functions in this case.
696
697 _append_param_insert_pk_returning(
698 compiler, stmt, c, values, kw
699 )
700 else:
701 # otherwise, find out how to invoke this column
702 # and get its value where RETURNING is not an option.
703 # if we have to invoke a server-side function, we need
704 # to pre-execute it. or if this is a straight
705 # autoincrement column and the dialect supports it
706 # we can use cursor.lastrowid.
707
708 _append_param_insert_pk_no_returning(
709 compiler, stmt, c, values, kw
710 )
711
712 elif c.default is not None:
713 # column has a default, but it's not a pk column, or it is but
714 # we don't need to get the pk back.
715 if not c.default.is_sentinel or (
716 use_sentinel_columns is not None
717 ):
718 _append_param_insert_hasdefault(
719 compiler, stmt, c, implicit_return_defaults, values, kw
720 )
721
722 elif c.server_default is not None:
723 # column has a DDL-level default, and is either not a pk
724 # column or we don't need the pk.
725 if implicit_return_defaults and c in implicit_return_defaults:
726 compiler_implicit_returning.append(c)
727 elif not c.primary_key:
728 compiler.postfetch.append(c)
729
730 elif implicit_return_defaults and c in implicit_return_defaults:
731 compiler_implicit_returning.append(c)
732
733 elif (
734 c.primary_key
735 and c is not stmt.table._autoincrement_column
736 and not c.nullable
737 ):
738 _warn_pk_with_no_anticipated_value(c)
739
740 elif compile_state.isupdate:
741 # no parameter is present and it's an insert.
742
743 _append_param_update(
744 compiler,
745 compile_state,
746 stmt,
747 c,
748 implicit_return_defaults,
749 values,
750 kw,
751 )
752
753 # adding supplemental cols to implicit_returning in table
754 # order so that order is maintained between multiple INSERT
755 # statements which may have different parameters included, but all
756 # have the same RETURNING clause
757 if (
758 c in supplemental_returning
759 and c not in compiler_implicit_returning
760 ):
761 compiler_implicit_returning.append(c)
762
763 if supplemental_returning:
764 # we should have gotten every col into implicit_returning,
765 # however supplemental returning can also have SQL functions etc.
766 # in it
767 remaining_supplemental = supplemental_returning.difference(
768 compiler_implicit_returning
769 )
770 compiler_implicit_returning.extend(
771 c
772 for c in stmt._supplemental_returning
773 if c in remaining_supplemental
774 )
775
776 # TODO(return_defaults_columns): there can still be more columns in
777 # _return_defaults_columns in the case that they are from something like an
778 # aliased of the table. we can add them here, however this breaks other ORM
779 # things. so this is for another day. see
780 # test/orm/dml/test_update_delete_where.py -> test_update_from_alias
781
782 # if stmt._return_defaults_columns:
783 # compiler_implicit_returning.extend(
784 # set(stmt._return_defaults_columns)
785 # .difference(compiler_implicit_returning)
786 # .difference(cols_in_params)
787 # )
788
789 return (use_insertmanyvalues, use_sentinel_columns)
790
791
792def _setup_delete_return_defaults(
793 compiler,
794 stmt,
795 compile_state,
796 parameters,
797 _getattr_col_key,
798 _column_as_key,
799 _col_bind_name,
800 check_columns,
801 values,
802 toplevel,
803 kw,
804):
805 (_, _, implicit_return_defaults, *_) = _get_returning_modifiers(
806 compiler, stmt, compile_state, toplevel
807 )
808
809 if not implicit_return_defaults:
810 return
811
812 if stmt._return_defaults_columns:
813 compiler.implicit_returning.extend(implicit_return_defaults)
814
815 if stmt._supplemental_returning:
816 ir_set = set(compiler.implicit_returning)
817 compiler.implicit_returning.extend(
818 c for c in stmt._supplemental_returning if c not in ir_set
819 )
820
821
822def _append_param_parameter(
823 compiler,
824 stmt,
825 compile_state,
826 c,
827 col_key,
828 parameters,
829 _col_bind_name,
830 implicit_returning,
831 implicit_return_defaults,
832 postfetch_lastrowid,
833 values,
834 autoincrement_col,
835 insert_null_pk_still_autoincrements,
836 kw,
837):
838 value = parameters.pop(col_key)
839
840 has_visiting_cte = kw.get("visiting_cte") is not None
841 col_value = compiler.preparer.format_column(
842 c, use_table=compile_state.include_table_with_column_exprs
843 )
844
845 accumulated_bind_names: Set[str] = set()
846
847 if coercions._is_literal(value):
848 if (
849 insert_null_pk_still_autoincrements
850 and c.primary_key
851 and c is autoincrement_col
852 ):
853 # support use case for #7998, fetch autoincrement cols
854 # even if value was given.
855
856 if postfetch_lastrowid:
857 compiler.postfetch_lastrowid = True
858 elif implicit_returning:
859 compiler.implicit_returning.append(c)
860
861 value = _create_bind_param(
862 compiler,
863 c,
864 value,
865 required=value is REQUIRED,
866 name=(
867 _col_bind_name(c)
868 if not _compile_state_isinsert(compile_state)
869 or not compile_state._has_multi_parameters
870 else "%s_m0" % _col_bind_name(c)
871 ),
872 accumulate_bind_names=accumulated_bind_names,
873 force_anonymous=has_visiting_cte,
874 **kw,
875 )
876 elif value._is_bind_parameter:
877 if (
878 insert_null_pk_still_autoincrements
879 and value.value is None
880 and c.primary_key
881 and c is autoincrement_col
882 ):
883 # support use case for #7998, fetch autoincrement cols
884 # even if value was given
885 if implicit_returning:
886 compiler.implicit_returning.append(c)
887 elif compiler.dialect.postfetch_lastrowid:
888 compiler.postfetch_lastrowid = True
889
890 value = _handle_values_anonymous_param(
891 compiler,
892 c,
893 value,
894 name=(
895 _col_bind_name(c)
896 if not _compile_state_isinsert(compile_state)
897 or not compile_state._has_multi_parameters
898 else "%s_m0" % _col_bind_name(c)
899 ),
900 accumulate_bind_names=accumulated_bind_names,
901 **kw,
902 )
903 else:
904 # value is a SQL expression
905 value = compiler.process(
906 value.self_group(),
907 accumulate_bind_names=accumulated_bind_names,
908 **kw,
909 )
910
911 if compile_state.isupdate:
912 if implicit_return_defaults and c in implicit_return_defaults:
913 compiler.implicit_returning.append(c)
914
915 else:
916 compiler.postfetch.append(c)
917 else:
918 if c.primary_key:
919 if implicit_returning:
920 compiler.implicit_returning.append(c)
921 elif compiler.dialect.postfetch_lastrowid:
922 compiler.postfetch_lastrowid = True
923
924 elif implicit_return_defaults and (c in implicit_return_defaults):
925 compiler.implicit_returning.append(c)
926
927 else:
928 # postfetch specifically means, "we can SELECT the row we just
929 # inserted by primary key to get back the server generated
930 # defaults". so by definition this can't be used to get the
931 # primary key value back, because we need to have it ahead of
932 # time.
933
934 compiler.postfetch.append(c)
935
936 values.append((c, col_value, value, accumulated_bind_names))
937
938
939def _append_param_insert_pk_returning(compiler, stmt, c, values, kw):
940 """Create a primary key expression in the INSERT statement where
941 we want to populate result.inserted_primary_key and RETURNING
942 is available.
943
944 """
945 if c.default is not None:
946 if c.default.is_sequence:
947 if compiler.dialect.supports_sequences and (
948 not c.default.optional
949 or not compiler.dialect.sequences_optional
950 ):
951 accumulated_bind_names: Set[str] = set()
952 values.append(
953 (
954 c,
955 compiler.preparer.format_column(c),
956 compiler.process(
957 c.default,
958 accumulate_bind_names=accumulated_bind_names,
959 **kw,
960 ),
961 accumulated_bind_names,
962 )
963 )
964 compiler.implicit_returning.append(c)
965 elif c.default.is_clause_element:
966 accumulated_bind_names = set()
967 values.append(
968 (
969 c,
970 compiler.preparer.format_column(c),
971 compiler.process(
972 c.default.arg.self_group(),
973 accumulate_bind_names=accumulated_bind_names,
974 **kw,
975 ),
976 accumulated_bind_names,
977 )
978 )
979 compiler.implicit_returning.append(c)
980 else:
981 # client side default. OK we can't use RETURNING, need to
982 # do a "prefetch", which in fact fetches the default value
983 # on the Python side
984 values.append(
985 (
986 c,
987 compiler.preparer.format_column(c),
988 _create_insert_prefetch_bind_param(compiler, c, **kw),
989 (c.key,),
990 )
991 )
992 elif c is stmt.table._autoincrement_column or c.server_default is not None:
993 compiler.implicit_returning.append(c)
994 elif not c.nullable:
995 # no .default, no .server_default, not autoincrement, we have
996 # no indication this primary key column will have any value
997 _warn_pk_with_no_anticipated_value(c)
998
999
1000def _append_param_insert_pk_no_returning(compiler, stmt, c, values, kw):
1001 """Create a primary key expression in the INSERT statement where
1002 we want to populate result.inserted_primary_key and we cannot use
1003 RETURNING.
1004
1005 Depending on the kind of default here we may create a bound parameter
1006 in the INSERT statement and pre-execute a default generation function,
1007 or we may use cursor.lastrowid if supported by the dialect.
1008
1009
1010 """
1011
1012 if (
1013 # column has a Python-side default
1014 c.default is not None
1015 and (
1016 # and it either is not a sequence, or it is and we support
1017 # sequences and want to invoke it
1018 not c.default.is_sequence
1019 or (
1020 compiler.dialect.supports_sequences
1021 and (
1022 not c.default.optional
1023 or not compiler.dialect.sequences_optional
1024 )
1025 )
1026 )
1027 ) or (
1028 # column is the "autoincrement column"
1029 c is stmt.table._autoincrement_column
1030 and (
1031 # dialect can't use cursor.lastrowid
1032 not compiler.dialect.postfetch_lastrowid
1033 and (
1034 # column has a Sequence and we support those
1035 (
1036 c.default is not None
1037 and c.default.is_sequence
1038 and compiler.dialect.supports_sequences
1039 )
1040 or
1041 # column has no default on it, but dialect can run the
1042 # "autoincrement" mechanism explicitly, e.g. PostgreSQL
1043 # SERIAL we know the sequence name
1044 (
1045 c.default is None
1046 and compiler.dialect.preexecute_autoincrement_sequences
1047 )
1048 )
1049 )
1050 ):
1051 # do a pre-execute of the default
1052 values.append(
1053 (
1054 c,
1055 compiler.preparer.format_column(c),
1056 _create_insert_prefetch_bind_param(compiler, c, **kw),
1057 (c.key,),
1058 )
1059 )
1060 elif (
1061 c.default is None
1062 and c.server_default is None
1063 and not c.nullable
1064 and c is not stmt.table._autoincrement_column
1065 ):
1066 # no .default, no .server_default, not autoincrement, we have
1067 # no indication this primary key column will have any value
1068 _warn_pk_with_no_anticipated_value(c)
1069 elif compiler.dialect.postfetch_lastrowid:
1070 # finally, where it seems like there will be a generated primary key
1071 # value and we haven't set up any other way to fetch it, and the
1072 # dialect supports cursor.lastrowid, switch on the lastrowid flag so
1073 # that the DefaultExecutionContext calls upon cursor.lastrowid
1074 compiler.postfetch_lastrowid = True
1075
1076
1077def _append_param_insert_hasdefault(
1078 compiler, stmt, c, implicit_return_defaults, values, kw
1079):
1080 if c.default.is_sequence:
1081 if compiler.dialect.supports_sequences and (
1082 not c.default.optional or not compiler.dialect.sequences_optional
1083 ):
1084 accumulated_bind_names: Set[str] = set()
1085 values.append(
1086 (
1087 c,
1088 compiler.preparer.format_column(c),
1089 compiler.process(
1090 c.default,
1091 accumulate_bind_names=accumulated_bind_names,
1092 **kw,
1093 ),
1094 accumulated_bind_names,
1095 )
1096 )
1097 if implicit_return_defaults and c in implicit_return_defaults:
1098 compiler.implicit_returning.append(c)
1099 elif not c.primary_key:
1100 compiler.postfetch.append(c)
1101 elif c.default.is_clause_element:
1102 accumulated_bind_names = set()
1103 values.append(
1104 (
1105 c,
1106 compiler.preparer.format_column(c),
1107 compiler.process(
1108 c.default.arg.self_group(),
1109 accumulate_bind_names=accumulated_bind_names,
1110 **kw,
1111 ),
1112 accumulated_bind_names,
1113 )
1114 )
1115
1116 if implicit_return_defaults and c in implicit_return_defaults:
1117 compiler.implicit_returning.append(c)
1118 elif not c.primary_key:
1119 # don't add primary key column to postfetch
1120 compiler.postfetch.append(c)
1121 else:
1122 values.append(
1123 (
1124 c,
1125 compiler.preparer.format_column(c),
1126 _create_insert_prefetch_bind_param(compiler, c, **kw),
1127 (c.key,),
1128 )
1129 )
1130
1131
1132def _append_param_insert_select_hasdefault(
1133 compiler: SQLCompiler,
1134 stmt: ValuesBase,
1135 c: ColumnClause[Any],
1136 values: List[_CrudParamElementSQLExpr],
1137 kw: Dict[str, Any],
1138) -> None:
1139 if default_is_sequence(c.default):
1140 if compiler.dialect.supports_sequences and (
1141 not c.default.optional or not compiler.dialect.sequences_optional
1142 ):
1143 values.append(
1144 (
1145 c,
1146 compiler.preparer.format_column(c),
1147 c.default.next_value(),
1148 (),
1149 )
1150 )
1151 elif default_is_clause_element(c.default):
1152 values.append(
1153 (
1154 c,
1155 compiler.preparer.format_column(c),
1156 c.default.arg.self_group(),
1157 (),
1158 )
1159 )
1160 else:
1161 values.append(
1162 (
1163 c,
1164 compiler.preparer.format_column(c),
1165 _create_insert_prefetch_bind_param(
1166 compiler, c, process=False, **kw
1167 ),
1168 (c.key,),
1169 )
1170 )
1171
1172
1173def _append_param_update(
1174 compiler, compile_state, stmt, c, implicit_return_defaults, values, kw
1175):
1176 include_table = compile_state.include_table_with_column_exprs
1177 if c.onupdate is not None and not c.onupdate.is_sequence:
1178 if c.onupdate.is_clause_element:
1179 values.append(
1180 (
1181 c,
1182 compiler.preparer.format_column(
1183 c,
1184 use_table=include_table,
1185 ),
1186 compiler.process(c.onupdate.arg.self_group(), **kw),
1187 (),
1188 )
1189 )
1190 if implicit_return_defaults and c in implicit_return_defaults:
1191 compiler.implicit_returning.append(c)
1192 else:
1193 compiler.postfetch.append(c)
1194 else:
1195 values.append(
1196 (
1197 c,
1198 compiler.preparer.format_column(
1199 c,
1200 use_table=include_table,
1201 ),
1202 _create_update_prefetch_bind_param(compiler, c, **kw),
1203 (c.key,),
1204 )
1205 )
1206 elif c.server_onupdate is not None:
1207 if implicit_return_defaults and c in implicit_return_defaults:
1208 compiler.implicit_returning.append(c)
1209 else:
1210 compiler.postfetch.append(c)
1211 elif (
1212 implicit_return_defaults
1213 and (stmt._return_defaults_columns or not stmt._return_defaults)
1214 and c in implicit_return_defaults
1215 ):
1216 compiler.implicit_returning.append(c)
1217
1218
1219@overload
1220def _create_insert_prefetch_bind_param(
1221 compiler: SQLCompiler,
1222 c: ColumnElement[Any],
1223 process: Literal[True] = ...,
1224 **kw: Any,
1225) -> str: ...
1226
1227
1228@overload
1229def _create_insert_prefetch_bind_param(
1230 compiler: SQLCompiler,
1231 c: ColumnElement[Any],
1232 process: Literal[False],
1233 **kw: Any,
1234) -> elements.BindParameter[Any]: ...
1235
1236
1237def _create_insert_prefetch_bind_param(
1238 compiler: SQLCompiler,
1239 c: ColumnElement[Any],
1240 process: bool = True,
1241 name: Optional[str] = None,
1242 **kw: Any,
1243) -> Union[elements.BindParameter[Any], str]:
1244 param = _create_bind_param(
1245 compiler, c, None, process=process, name=name, **kw
1246 )
1247 compiler.insert_prefetch.append(c) # type: ignore
1248 return param
1249
1250
1251@overload
1252def _create_update_prefetch_bind_param(
1253 compiler: SQLCompiler,
1254 c: ColumnElement[Any],
1255 process: Literal[True] = ...,
1256 **kw: Any,
1257) -> str: ...
1258
1259
1260@overload
1261def _create_update_prefetch_bind_param(
1262 compiler: SQLCompiler,
1263 c: ColumnElement[Any],
1264 process: Literal[False],
1265 **kw: Any,
1266) -> elements.BindParameter[Any]: ...
1267
1268
1269def _create_update_prefetch_bind_param(
1270 compiler: SQLCompiler,
1271 c: ColumnElement[Any],
1272 process: bool = True,
1273 name: Optional[str] = None,
1274 **kw: Any,
1275) -> Union[elements.BindParameter[Any], str]:
1276 param = _create_bind_param(
1277 compiler, c, None, process=process, name=name, **kw
1278 )
1279 compiler.update_prefetch.append(c) # type: ignore
1280 return param
1281
1282
1283class _multiparam_column(elements.ColumnElement[Any]):
1284 _is_multiparam_column = True
1285
1286 def __init__(self, original, index):
1287 self.index = index
1288 self.key = "%s_m%d" % (original.key, index + 1)
1289 self.original = original
1290 self.default = original.default
1291 self.type = original.type
1292
1293 def compare(self, other, **kw):
1294 raise NotImplementedError()
1295
1296 def _copy_internals(self, **kw):
1297 raise NotImplementedError()
1298
1299 def __eq__(self, other):
1300 return (
1301 isinstance(other, _multiparam_column)
1302 and other.key == self.key
1303 and other.original == self.original
1304 )
1305
1306 @util.memoized_property
1307 def _default_description_tuple(self) -> _DefaultDescriptionTuple:
1308 """used by default.py -> _process_execute_defaults()"""
1309
1310 return _DefaultDescriptionTuple._from_column_default(self.default)
1311
1312 @util.memoized_property
1313 def _onupdate_description_tuple(self) -> _DefaultDescriptionTuple:
1314 """used by default.py -> _process_execute_defaults()"""
1315
1316 return _DefaultDescriptionTuple._from_column_default(self.onupdate)
1317
1318
1319def _process_multiparam_default_bind(
1320 compiler: SQLCompiler,
1321 stmt: ValuesBase,
1322 c: KeyedColumnElement[Any],
1323 index: int,
1324 kw: Dict[str, Any],
1325) -> str:
1326 if not c.default:
1327 raise exc.CompileError(
1328 "INSERT value for column %s is explicitly rendered as a bound"
1329 "parameter in the VALUES clause; "
1330 "a Python-side value or SQL expression is required" % c
1331 )
1332 elif default_is_clause_element(c.default):
1333 return compiler.process(c.default.arg.self_group(), **kw)
1334 elif c.default.is_sequence:
1335 # these conditions would have been established
1336 # by append_param_insert_(?:hasdefault|pk_returning|pk_no_returning)
1337 # in order for us to be here, so these don't need to be
1338 # checked
1339 # assert compiler.dialect.supports_sequences and (
1340 # not c.default.optional
1341 # or not compiler.dialect.sequences_optional
1342 # )
1343 return compiler.process(c.default, **kw)
1344 else:
1345 col = _multiparam_column(c, index)
1346 assert isinstance(stmt, dml.Insert)
1347 return _create_insert_prefetch_bind_param(
1348 compiler, col, process=True, **kw
1349 )
1350
1351
1352def _get_update_multitable_params(
1353 compiler,
1354 stmt,
1355 compile_state,
1356 stmt_parameter_tuples,
1357 check_columns,
1358 _col_bind_name,
1359 _getattr_col_key,
1360 values,
1361 kw,
1362):
1363 normalized_params = {
1364 coercions.expect(roles.DMLColumnRole, c): param
1365 for c, param in stmt_parameter_tuples or ()
1366 }
1367
1368 include_table = compile_state.include_table_with_column_exprs
1369
1370 affected_tables = set()
1371 for t in compile_state._extra_froms:
1372 for c in t.c:
1373 if c in normalized_params:
1374 affected_tables.add(t)
1375 check_columns[_getattr_col_key(c)] = c
1376 value = normalized_params[c]
1377
1378 col_value = compiler.process(c, include_table=include_table)
1379 if coercions._is_literal(value):
1380 value = _create_bind_param(
1381 compiler,
1382 c,
1383 value,
1384 required=value is REQUIRED,
1385 name=_col_bind_name(c),
1386 **kw, # TODO: no test coverage for literal binds here
1387 )
1388 accumulated_bind_names: Iterable[str] = (c.key,)
1389 elif value._is_bind_parameter:
1390 cbn = _col_bind_name(c)
1391 value = _handle_values_anonymous_param(
1392 compiler, c, value, name=cbn, **kw
1393 )
1394 accumulated_bind_names = (cbn,)
1395 else:
1396 compiler.postfetch.append(c)
1397 value = compiler.process(value.self_group(), **kw)
1398 accumulated_bind_names = ()
1399 values.append((c, col_value, value, accumulated_bind_names))
1400 # determine tables which are actually to be updated - process onupdate
1401 # and server_onupdate for these
1402 for t in affected_tables:
1403 for c in t.c:
1404 if c in normalized_params:
1405 continue
1406 elif c.onupdate is not None and not c.onupdate.is_sequence:
1407 if c.onupdate.is_clause_element:
1408 values.append(
1409 (
1410 c,
1411 compiler.process(c, include_table=include_table),
1412 compiler.process(
1413 c.onupdate.arg.self_group(), **kw
1414 ),
1415 (),
1416 )
1417 )
1418 compiler.postfetch.append(c)
1419 else:
1420 values.append(
1421 (
1422 c,
1423 compiler.process(c, include_table=include_table),
1424 _create_update_prefetch_bind_param(
1425 compiler, c, name=_col_bind_name(c), **kw
1426 ),
1427 (c.key,),
1428 )
1429 )
1430 elif c.server_onupdate is not None:
1431 compiler.postfetch.append(c)
1432
1433
1434def _extend_values_for_multiparams(
1435 compiler: SQLCompiler,
1436 stmt: ValuesBase,
1437 compile_state: DMLState,
1438 initial_values: Sequence[_CrudParamElementStr],
1439 _column_as_key: Callable[..., str],
1440 kw: Dict[str, Any],
1441) -> List[Sequence[_CrudParamElementStr]]:
1442 values_0 = initial_values
1443 values = [initial_values]
1444
1445 has_visiting_cte = kw.get("visiting_cte") is not None
1446 mp = compile_state._multi_parameters
1447 assert mp is not None
1448 for i, row in enumerate(mp[1:]):
1449 extension: List[_CrudParamElementStr] = []
1450
1451 row = {_column_as_key(key): v for key, v in row.items()}
1452
1453 for col, col_expr, param, accumulated_names in values_0:
1454 if col.key in row:
1455 key = col.key
1456
1457 if coercions._is_literal(row[key]):
1458 new_param = _create_bind_param(
1459 compiler,
1460 col,
1461 row[key],
1462 name=("%s_m%d" % (col.key, i + 1)),
1463 force_anonymous=has_visiting_cte,
1464 **kw,
1465 )
1466 else:
1467 new_param = compiler.process(row[key].self_group(), **kw)
1468 else:
1469 new_param = _process_multiparam_default_bind(
1470 compiler, stmt, col, i, kw
1471 )
1472
1473 extension.append((col, col_expr, new_param, accumulated_names))
1474
1475 values.append(extension)
1476
1477 return values
1478
1479
1480def _get_stmt_parameter_tuples_params(
1481 compiler,
1482 compile_state,
1483 parameters,
1484 stmt_parameter_tuples,
1485 _column_as_key,
1486 values,
1487 kw,
1488):
1489 for k, v in stmt_parameter_tuples:
1490 colkey = _column_as_key(k)
1491 if colkey is not None:
1492 parameters.setdefault(colkey, v)
1493 else:
1494 # a non-Column expression on the left side;
1495 # add it to values() in an "as-is" state,
1496 # coercing right side to bound param
1497
1498 # note one of the main use cases for this is array slice
1499 # updates on PostgreSQL, as the left side is also an expression.
1500
1501 col_expr = compiler.process(
1502 k, include_table=compile_state.include_table_with_column_exprs
1503 )
1504
1505 if coercions._is_literal(v):
1506 v = compiler.process(
1507 elements.BindParameter(None, v, type_=k.type), **kw
1508 )
1509 else:
1510 if v._is_bind_parameter and v.type._isnull:
1511 # either unique parameter, or other bound parameters that
1512 # were passed in directly
1513 # set type to that of the column unconditionally
1514 v = v._with_binary_element_type(k.type)
1515
1516 v = compiler.process(v.self_group(), **kw)
1517
1518 # TODO: not sure if accumulated_bind_names applies here
1519 values.append((k, col_expr, v, ()))
1520
1521
1522def _get_returning_modifiers(compiler, stmt, compile_state, toplevel):
1523 """determines RETURNING strategy, if any, for the statement.
1524
1525 This is where it's determined what we need to fetch from the
1526 INSERT or UPDATE statement after it's invoked.
1527
1528 """
1529
1530 dialect = compiler.dialect
1531
1532 need_pks = (
1533 toplevel
1534 and _compile_state_isinsert(compile_state)
1535 and not stmt._inline
1536 and (
1537 not compiler.for_executemany
1538 or (dialect.insert_executemany_returning and stmt._return_defaults)
1539 )
1540 and not stmt._returning
1541 # and (not stmt._returning or stmt._return_defaults)
1542 and not compile_state._has_multi_parameters
1543 )
1544
1545 # check if we have access to simple cursor.lastrowid. we can use that
1546 # after the INSERT if that's all we need.
1547 postfetch_lastrowid = (
1548 need_pks
1549 and dialect.postfetch_lastrowid
1550 and stmt.table._autoincrement_column is not None
1551 )
1552
1553 # see if we want to add RETURNING to an INSERT in order to get
1554 # primary key columns back. This would be instead of postfetch_lastrowid
1555 # if that's set.
1556 implicit_returning = (
1557 # statement itself can veto it
1558 need_pks
1559 # the dialect can veto it if it just doesnt support RETURNING
1560 # with INSERT
1561 and dialect.insert_returning
1562 # user-defined implicit_returning on Table can veto it
1563 and compile_state._primary_table.implicit_returning
1564 # the compile_state can veto it (SQlite uses this to disable
1565 # RETURNING for an ON CONFLICT insert, as SQLite does not return
1566 # for rows that were updated, which is wrong)
1567 and compile_state._supports_implicit_returning
1568 and (
1569 # since we support MariaDB and SQLite which also support lastrowid,
1570 # decide if we should use lastrowid or RETURNING. for insert
1571 # that didnt call return_defaults() and has just one set of
1572 # parameters, we can use lastrowid. this is more "traditional"
1573 # and a lot of weird use cases are supported by it.
1574 # SQLite lastrowid times 3x faster than returning,
1575 # Mariadb lastrowid 2x faster than returning
1576 (not postfetch_lastrowid or dialect.favor_returning_over_lastrowid)
1577 or compile_state._has_multi_parameters
1578 or stmt._return_defaults
1579 )
1580 )
1581 if implicit_returning:
1582 postfetch_lastrowid = False
1583
1584 if _compile_state_isinsert(compile_state):
1585 should_implicit_return_defaults = (
1586 implicit_returning and stmt._return_defaults
1587 )
1588 explicit_returning = (
1589 should_implicit_return_defaults
1590 or stmt._returning
1591 or stmt._supplemental_returning
1592 )
1593 use_insertmanyvalues = (
1594 toplevel
1595 and compiler.for_executemany
1596 and dialect.use_insertmanyvalues
1597 and (
1598 explicit_returning or dialect.use_insertmanyvalues_wo_returning
1599 )
1600 )
1601
1602 use_sentinel_columns = None
1603 if (
1604 use_insertmanyvalues
1605 and explicit_returning
1606 and stmt._sort_by_parameter_order
1607 ):
1608 use_sentinel_columns = compiler._get_sentinel_column_for_table(
1609 stmt.table
1610 )
1611
1612 elif compile_state.isupdate:
1613 should_implicit_return_defaults = (
1614 stmt._return_defaults
1615 and compile_state._primary_table.implicit_returning
1616 and compile_state._supports_implicit_returning
1617 and dialect.update_returning
1618 )
1619 use_insertmanyvalues = False
1620 use_sentinel_columns = None
1621 elif compile_state.isdelete:
1622 should_implicit_return_defaults = (
1623 stmt._return_defaults
1624 and compile_state._primary_table.implicit_returning
1625 and compile_state._supports_implicit_returning
1626 and dialect.delete_returning
1627 )
1628 use_insertmanyvalues = False
1629 use_sentinel_columns = None
1630 else:
1631 should_implicit_return_defaults = False # pragma: no cover
1632 use_insertmanyvalues = False
1633 use_sentinel_columns = None
1634
1635 if should_implicit_return_defaults:
1636 if not stmt._return_defaults_columns:
1637 # TODO: this is weird. See #9685 where we have to
1638 # take an extra step to prevent this from happening. why
1639 # would this ever be *all* columns? but if we set to blank, then
1640 # that seems to break things also in the ORM. So we should
1641 # try to clean this up and figure out what return_defaults
1642 # needs to do w/ the ORM etc. here
1643 implicit_return_defaults = set(stmt.table.c)
1644 else:
1645 implicit_return_defaults = set(stmt._return_defaults_columns)
1646 else:
1647 implicit_return_defaults = None
1648
1649 return (
1650 need_pks,
1651 implicit_returning or should_implicit_return_defaults,
1652 implicit_return_defaults,
1653 postfetch_lastrowid,
1654 use_insertmanyvalues,
1655 use_sentinel_columns,
1656 )
1657
1658
1659def _warn_pk_with_no_anticipated_value(c):
1660 msg = (
1661 "Column '%s.%s' is marked as a member of the "
1662 "primary key for table '%s', "
1663 "but has no Python-side or server-side default generator indicated, "
1664 "nor does it indicate 'autoincrement=True' or 'nullable=True', "
1665 "and no explicit value is passed. "
1666 "Primary key columns typically may not store NULL."
1667 % (c.table.fullname, c.name, c.table.fullname)
1668 )
1669 if len(c.table.primary_key) > 1:
1670 msg += (
1671 " Note that as of SQLAlchemy 1.1, 'autoincrement=True' must be "
1672 "indicated explicitly for composite (e.g. multicolumn) primary "
1673 "keys if AUTO_INCREMENT/SERIAL/IDENTITY "
1674 "behavior is expected for one of the columns in the primary key. "
1675 "CREATE TABLE statements are impacted by this change as well on "
1676 "most backends."
1677 )
1678 util.warn(msg)