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