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