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