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