1# sql/dml.py
2# Copyright (C) 2009-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"""
8Provide :class:`_expression.Insert`, :class:`_expression.Update` and
9:class:`_expression.Delete`.
10
11"""
12from __future__ import annotations
13
14import collections.abc as collections_abc
15import operator
16from typing import Any
17from typing import cast
18from typing import Dict
19from typing import Iterable
20from typing import List
21from typing import MutableMapping
22from typing import NoReturn
23from typing import Optional
24from typing import overload
25from typing import Sequence
26from typing import Set
27from typing import Tuple
28from typing import Type
29from typing import TYPE_CHECKING
30from typing import TypeVar
31from typing import Union
32
33from . import coercions
34from . import roles
35from . import util as sql_util
36from ._typing import _TP
37from ._typing import _unexpected_kw
38from ._typing import is_column_element
39from ._typing import is_named_from_clause
40from .base import _entity_namespace_key
41from .base import _exclusive_against
42from .base import _from_objects
43from .base import _generative
44from .base import _select_iterables
45from .base import ColumnCollection
46from .base import ColumnSet
47from .base import CompileState
48from .base import DialectKWArgs
49from .base import Executable
50from .base import Generative
51from .base import HasCompileState
52from .elements import BooleanClauseList
53from .elements import ClauseElement
54from .elements import ColumnClause
55from .elements import ColumnElement
56from .elements import Null
57from .selectable import Alias
58from .selectable import ExecutableReturnsRows
59from .selectable import FromClause
60from .selectable import HasCTE
61from .selectable import HasPrefixes
62from .selectable import Join
63from .selectable import SelectLabelStyle
64from .selectable import TableClause
65from .selectable import TypedReturnsRows
66from .sqltypes import NullType
67from .visitors import InternalTraversal
68from .. import exc
69from .. import util
70from ..util.typing import Self
71from ..util.typing import TypeGuard
72
73if TYPE_CHECKING:
74 from ._typing import _ColumnExpressionArgument
75 from ._typing import _ColumnsClauseArgument
76 from ._typing import _DMLColumnArgument
77 from ._typing import _DMLColumnKeyMapping
78 from ._typing import _DMLTableArgument
79 from ._typing import _T0 # noqa
80 from ._typing import _T1 # noqa
81 from ._typing import _T2 # noqa
82 from ._typing import _T3 # noqa
83 from ._typing import _T4 # noqa
84 from ._typing import _T5 # noqa
85 from ._typing import _T6 # noqa
86 from ._typing import _T7 # noqa
87 from ._typing import _TypedColumnClauseArgument as _TCCA # noqa
88 from .base import ReadOnlyColumnCollection
89 from .compiler import SQLCompiler
90 from .elements import KeyedColumnElement
91 from .selectable import _ColumnsClauseElement
92 from .selectable import _SelectIterable
93 from .selectable import Select
94 from .selectable import Selectable
95
96 def isupdate(dml: DMLState) -> TypeGuard[UpdateDMLState]: ...
97
98 def isdelete(dml: DMLState) -> TypeGuard[DeleteDMLState]: ...
99
100 def isinsert(dml: DMLState) -> TypeGuard[InsertDMLState]: ...
101
102else:
103 isupdate = operator.attrgetter("isupdate")
104 isdelete = operator.attrgetter("isdelete")
105 isinsert = operator.attrgetter("isinsert")
106
107
108_T = TypeVar("_T", bound=Any)
109
110_DMLColumnElement = Union[str, ColumnClause[Any]]
111_DMLTableElement = Union[TableClause, Alias, Join]
112
113
114class DMLState(CompileState):
115 _no_parameters = True
116 _dict_parameters: Optional[MutableMapping[_DMLColumnElement, Any]] = None
117 _multi_parameters: Optional[
118 List[MutableMapping[_DMLColumnElement, Any]]
119 ] = None
120 _ordered_values: Optional[List[Tuple[_DMLColumnElement, Any]]] = None
121 _parameter_ordering: Optional[List[_DMLColumnElement]] = None
122 _primary_table: FromClause
123 _supports_implicit_returning = True
124
125 isupdate = False
126 isdelete = False
127 isinsert = False
128
129 statement: UpdateBase
130
131 def __init__(
132 self, statement: UpdateBase, compiler: SQLCompiler, **kw: Any
133 ):
134 raise NotImplementedError()
135
136 @classmethod
137 def get_entity_description(cls, statement: UpdateBase) -> Dict[str, Any]:
138 return {
139 "name": (
140 statement.table.name
141 if is_named_from_clause(statement.table)
142 else None
143 ),
144 "table": statement.table,
145 }
146
147 @classmethod
148 def get_returning_column_descriptions(
149 cls, statement: UpdateBase
150 ) -> List[Dict[str, Any]]:
151 return [
152 {
153 "name": c.key,
154 "type": c.type,
155 "expr": c,
156 }
157 for c in statement._all_selected_columns
158 ]
159
160 @property
161 def dml_table(self) -> _DMLTableElement:
162 return self.statement.table
163
164 if TYPE_CHECKING:
165
166 @classmethod
167 def get_plugin_class(cls, statement: Executable) -> Type[DMLState]: ...
168
169 @classmethod
170 def _get_multi_crud_kv_pairs(
171 cls,
172 statement: UpdateBase,
173 multi_kv_iterator: Iterable[Dict[_DMLColumnArgument, Any]],
174 ) -> List[Dict[_DMLColumnElement, Any]]:
175 return [
176 {
177 coercions.expect(roles.DMLColumnRole, k): v
178 for k, v in mapping.items()
179 }
180 for mapping in multi_kv_iterator
181 ]
182
183 @classmethod
184 def _get_crud_kv_pairs(
185 cls,
186 statement: UpdateBase,
187 kv_iterator: Iterable[Tuple[_DMLColumnArgument, Any]],
188 needs_to_be_cacheable: bool,
189 ) -> List[Tuple[_DMLColumnElement, Any]]:
190 return [
191 (
192 coercions.expect(roles.DMLColumnRole, k),
193 (
194 v
195 if not needs_to_be_cacheable
196 else coercions.expect(
197 roles.ExpressionElementRole,
198 v,
199 type_=NullType(),
200 is_crud=True,
201 )
202 ),
203 )
204 for k, v in kv_iterator
205 ]
206
207 def _make_extra_froms(
208 self, statement: DMLWhereBase
209 ) -> Tuple[FromClause, List[FromClause]]:
210 froms: List[FromClause] = []
211
212 all_tables = list(sql_util.tables_from_leftmost(statement.table))
213 primary_table = all_tables[0]
214 seen = {primary_table}
215
216 consider = statement._where_criteria
217 if self._dict_parameters:
218 consider += tuple(self._dict_parameters.values())
219
220 for crit in consider:
221 for item in _from_objects(crit):
222 if not seen.intersection(item._cloned_set):
223 froms.append(item)
224 seen.update(item._cloned_set)
225
226 froms.extend(all_tables[1:])
227 return primary_table, froms
228
229 def _process_values(self, statement: ValuesBase) -> None:
230 if self._no_parameters:
231 self._dict_parameters = statement._values
232 self._no_parameters = False
233
234 def _process_select_values(self, statement: ValuesBase) -> None:
235 assert statement._select_names is not None
236 parameters: MutableMapping[_DMLColumnElement, Any] = {
237 name: Null() for name in statement._select_names
238 }
239
240 if self._no_parameters:
241 self._no_parameters = False
242 self._dict_parameters = parameters
243 else:
244 # this condition normally not reachable as the Insert
245 # does not allow this construction to occur
246 assert False, "This statement already has parameters"
247
248 def _no_multi_values_supported(self, statement: ValuesBase) -> NoReturn:
249 raise exc.InvalidRequestError(
250 "%s construct does not support "
251 "multiple parameter sets." % statement.__visit_name__.upper()
252 )
253
254 def _cant_mix_formats_error(self) -> NoReturn:
255 raise exc.InvalidRequestError(
256 "Can't mix single and multiple VALUES "
257 "formats in one INSERT statement; one style appends to a "
258 "list while the other replaces values, so the intent is "
259 "ambiguous."
260 )
261
262
263@CompileState.plugin_for("default", "insert")
264class InsertDMLState(DMLState):
265 isinsert = True
266
267 include_table_with_column_exprs = False
268
269 _has_multi_parameters = False
270
271 def __init__(
272 self,
273 statement: Insert,
274 compiler: SQLCompiler,
275 disable_implicit_returning: bool = False,
276 **kw: Any,
277 ):
278 self.statement = statement
279 self._primary_table = statement.table
280
281 if disable_implicit_returning:
282 self._supports_implicit_returning = False
283
284 self.isinsert = True
285 if statement._select_names:
286 self._process_select_values(statement)
287 if statement._values is not None:
288 self._process_values(statement)
289 if statement._multi_values:
290 self._process_multi_values(statement)
291
292 @util.memoized_property
293 def _insert_col_keys(self) -> List[str]:
294 # this is also done in crud.py -> _key_getters_for_crud_column
295 return [
296 coercions.expect(roles.DMLColumnRole, col, as_key=True)
297 for col in self._dict_parameters or ()
298 ]
299
300 def _process_values(self, statement: ValuesBase) -> None:
301 if self._no_parameters:
302 self._has_multi_parameters = False
303 self._dict_parameters = statement._values
304 self._no_parameters = False
305 elif self._has_multi_parameters:
306 self._cant_mix_formats_error()
307
308 def _process_multi_values(self, statement: ValuesBase) -> None:
309 for parameters in statement._multi_values:
310 multi_parameters: List[MutableMapping[_DMLColumnElement, Any]] = [
311 (
312 {
313 c.key: value
314 for c, value in zip(statement.table.c, parameter_set)
315 }
316 if isinstance(parameter_set, collections_abc.Sequence)
317 else parameter_set
318 )
319 for parameter_set in parameters
320 ]
321
322 if self._no_parameters:
323 self._no_parameters = False
324 self._has_multi_parameters = True
325 self._multi_parameters = multi_parameters
326 self._dict_parameters = self._multi_parameters[0]
327 elif not self._has_multi_parameters:
328 self._cant_mix_formats_error()
329 else:
330 assert self._multi_parameters
331 self._multi_parameters.extend(multi_parameters)
332
333
334@CompileState.plugin_for("default", "update")
335class UpdateDMLState(DMLState):
336 isupdate = True
337
338 include_table_with_column_exprs = False
339
340 def __init__(self, statement: Update, compiler: SQLCompiler, **kw: Any):
341 self.statement = statement
342
343 self.isupdate = True
344 if statement._ordered_values is not None:
345 self._process_ordered_values(statement)
346 elif statement._values is not None:
347 self._process_values(statement)
348 elif statement._multi_values:
349 self._no_multi_values_supported(statement)
350 t, ef = self._make_extra_froms(statement)
351 self._primary_table = t
352 self._extra_froms = ef
353
354 self.is_multitable = mt = ef
355 self.include_table_with_column_exprs = bool(
356 mt and compiler.render_table_with_column_in_update_from
357 )
358
359 def _process_ordered_values(self, statement: ValuesBase) -> None:
360 parameters = statement._ordered_values
361
362 if self._no_parameters:
363 self._no_parameters = False
364 assert parameters is not None
365 self._dict_parameters = dict(parameters)
366 self._ordered_values = parameters
367 self._parameter_ordering = [key for key, value in parameters]
368 else:
369 raise exc.InvalidRequestError(
370 "Can only invoke ordered_values() once, and not mixed "
371 "with any other values() call"
372 )
373
374
375@CompileState.plugin_for("default", "delete")
376class DeleteDMLState(DMLState):
377 isdelete = True
378
379 def __init__(self, statement: Delete, compiler: SQLCompiler, **kw: Any):
380 self.statement = statement
381
382 self.isdelete = True
383 t, ef = self._make_extra_froms(statement)
384 self._primary_table = t
385 self._extra_froms = ef
386 self.is_multitable = ef
387
388
389class UpdateBase(
390 roles.DMLRole,
391 HasCTE,
392 HasCompileState,
393 DialectKWArgs,
394 HasPrefixes,
395 Generative,
396 ExecutableReturnsRows,
397 ClauseElement,
398):
399 """Form the base for ``INSERT``, ``UPDATE``, and ``DELETE`` statements."""
400
401 __visit_name__ = "update_base"
402
403 _hints: util.immutabledict[Tuple[_DMLTableElement, str], str] = (
404 util.EMPTY_DICT
405 )
406 named_with_column = False
407
408 _label_style: SelectLabelStyle = (
409 SelectLabelStyle.LABEL_STYLE_DISAMBIGUATE_ONLY
410 )
411 table: _DMLTableElement
412
413 _return_defaults = False
414 _return_defaults_columns: Optional[Tuple[_ColumnsClauseElement, ...]] = (
415 None
416 )
417 _supplemental_returning: Optional[Tuple[_ColumnsClauseElement, ...]] = None
418 _returning: Tuple[_ColumnsClauseElement, ...] = ()
419
420 is_dml = True
421
422 def _generate_fromclause_column_proxies(
423 self,
424 fromclause: FromClause,
425 columns: ColumnCollection[str, KeyedColumnElement[Any]],
426 primary_key: ColumnSet,
427 foreign_keys: Set[KeyedColumnElement[Any]],
428 ) -> None:
429 columns._populate_separate_keys(
430 col._make_proxy(
431 fromclause, primary_key=primary_key, foreign_keys=foreign_keys
432 )
433 for col in self._all_selected_columns
434 if is_column_element(col)
435 )
436
437 def params(self, *arg: Any, **kw: Any) -> NoReturn:
438 """Set the parameters for the statement.
439
440 This method raises ``NotImplementedError`` on the base class,
441 and is overridden by :class:`.ValuesBase` to provide the
442 SET/VALUES clause of UPDATE and INSERT.
443
444 """
445 raise NotImplementedError(
446 "params() is not supported for INSERT/UPDATE/DELETE statements."
447 " To set the values for an INSERT or UPDATE statement, use"
448 " stmt.values(**parameters)."
449 )
450
451 @_generative
452 def with_dialect_options(self, **opt: Any) -> Self:
453 """Add dialect options to this INSERT/UPDATE/DELETE object.
454
455 e.g.::
456
457 upd = table.update().dialect_options(mysql_limit=10)
458
459 .. versionadded: 1.4 - this method supersedes the dialect options
460 associated with the constructor.
461
462
463 """
464 self._validate_dialect_kwargs(opt)
465 return self
466
467 @_generative
468 def return_defaults(
469 self,
470 *cols: _DMLColumnArgument,
471 supplemental_cols: Optional[Iterable[_DMLColumnArgument]] = None,
472 sort_by_parameter_order: bool = False,
473 ) -> Self:
474 """Make use of a :term:`RETURNING` clause for the purpose
475 of fetching server-side expressions and defaults, for supporting
476 backends only.
477
478 .. deepalchemy::
479
480 The :meth:`.UpdateBase.return_defaults` method is used by the ORM
481 for its internal work in fetching newly generated primary key
482 and server default values, in particular to provide the underyling
483 implementation of the :paramref:`_orm.Mapper.eager_defaults`
484 ORM feature as well as to allow RETURNING support with bulk
485 ORM inserts. Its behavior is fairly idiosyncratic
486 and is not really intended for general use. End users should
487 stick with using :meth:`.UpdateBase.returning` in order to
488 add RETURNING clauses to their INSERT, UPDATE and DELETE
489 statements.
490
491 Normally, a single row INSERT statement will automatically populate the
492 :attr:`.CursorResult.inserted_primary_key` attribute when executed,
493 which stores the primary key of the row that was just inserted in the
494 form of a :class:`.Row` object with column names as named tuple keys
495 (and the :attr:`.Row._mapping` view fully populated as well). The
496 dialect in use chooses the strategy to use in order to populate this
497 data; if it was generated using server-side defaults and / or SQL
498 expressions, dialect-specific approaches such as ``cursor.lastrowid``
499 or ``RETURNING`` are typically used to acquire the new primary key
500 value.
501
502 However, when the statement is modified by calling
503 :meth:`.UpdateBase.return_defaults` before executing the statement,
504 additional behaviors take place **only** for backends that support
505 RETURNING and for :class:`.Table` objects that maintain the
506 :paramref:`.Table.implicit_returning` parameter at its default value of
507 ``True``. In these cases, when the :class:`.CursorResult` is returned
508 from the statement's execution, not only will
509 :attr:`.CursorResult.inserted_primary_key` be populated as always, the
510 :attr:`.CursorResult.returned_defaults` attribute will also be
511 populated with a :class:`.Row` named-tuple representing the full range
512 of server generated
513 values from that single row, including values for any columns that
514 specify :paramref:`_schema.Column.server_default` or which make use of
515 :paramref:`_schema.Column.default` using a SQL expression.
516
517 When invoking INSERT statements with multiple rows using
518 :ref:`insertmanyvalues <engine_insertmanyvalues>`, the
519 :meth:`.UpdateBase.return_defaults` modifier will have the effect of
520 the :attr:`_engine.CursorResult.inserted_primary_key_rows` and
521 :attr:`_engine.CursorResult.returned_defaults_rows` attributes being
522 fully populated with lists of :class:`.Row` objects representing newly
523 inserted primary key values as well as newly inserted server generated
524 values for each row inserted. The
525 :attr:`.CursorResult.inserted_primary_key` and
526 :attr:`.CursorResult.returned_defaults` attributes will also continue
527 to be populated with the first row of these two collections.
528
529 If the backend does not support RETURNING or the :class:`.Table` in use
530 has disabled :paramref:`.Table.implicit_returning`, then no RETURNING
531 clause is added and no additional data is fetched, however the
532 INSERT, UPDATE or DELETE statement proceeds normally.
533
534 E.g.::
535
536 stmt = table.insert().values(data="newdata").return_defaults()
537
538 result = connection.execute(stmt)
539
540 server_created_at = result.returned_defaults["created_at"]
541
542 When used against an UPDATE statement
543 :meth:`.UpdateBase.return_defaults` instead looks for columns that
544 include :paramref:`_schema.Column.onupdate` or
545 :paramref:`_schema.Column.server_onupdate` parameters assigned, when
546 constructing the columns that will be included in the RETURNING clause
547 by default if explicit columns were not specified. When used against a
548 DELETE statement, no columns are included in RETURNING by default, they
549 instead must be specified explicitly as there are no columns that
550 normally change values when a DELETE statement proceeds.
551
552 .. versionadded:: 2.0 :meth:`.UpdateBase.return_defaults` is supported
553 for DELETE statements also and has been moved from
554 :class:`.ValuesBase` to :class:`.UpdateBase`.
555
556 The :meth:`.UpdateBase.return_defaults` method is mutually exclusive
557 against the :meth:`.UpdateBase.returning` method and errors will be
558 raised during the SQL compilation process if both are used at the same
559 time on one statement. The RETURNING clause of the INSERT, UPDATE or
560 DELETE statement is therefore controlled by only one of these methods
561 at a time.
562
563 The :meth:`.UpdateBase.return_defaults` method differs from
564 :meth:`.UpdateBase.returning` in these ways:
565
566 1. :meth:`.UpdateBase.return_defaults` method causes the
567 :attr:`.CursorResult.returned_defaults` collection to be populated
568 with the first row from the RETURNING result. This attribute is not
569 populated when using :meth:`.UpdateBase.returning`.
570
571 2. :meth:`.UpdateBase.return_defaults` is compatible with existing
572 logic used to fetch auto-generated primary key values that are then
573 populated into the :attr:`.CursorResult.inserted_primary_key`
574 attribute. By contrast, using :meth:`.UpdateBase.returning` will
575 have the effect of the :attr:`.CursorResult.inserted_primary_key`
576 attribute being left unpopulated.
577
578 3. :meth:`.UpdateBase.return_defaults` can be called against any
579 backend. Backends that don't support RETURNING will skip the usage
580 of the feature, rather than raising an exception, *unless*
581 ``supplemental_cols`` is passed. The return value
582 of :attr:`_engine.CursorResult.returned_defaults` will be ``None``
583 for backends that don't support RETURNING or for which the target
584 :class:`.Table` sets :paramref:`.Table.implicit_returning` to
585 ``False``.
586
587 4. An INSERT statement invoked with executemany() is supported if the
588 backend database driver supports the
589 :ref:`insertmanyvalues <engine_insertmanyvalues>`
590 feature which is now supported by most SQLAlchemy-included backends.
591 When executemany is used, the
592 :attr:`_engine.CursorResult.returned_defaults_rows` and
593 :attr:`_engine.CursorResult.inserted_primary_key_rows` accessors
594 will return the inserted defaults and primary keys.
595
596 .. versionadded:: 1.4 Added
597 :attr:`_engine.CursorResult.returned_defaults_rows` and
598 :attr:`_engine.CursorResult.inserted_primary_key_rows` accessors.
599 In version 2.0, the underlying implementation which fetches and
600 populates the data for these attributes was generalized to be
601 supported by most backends, whereas in 1.4 they were only
602 supported by the ``psycopg2`` driver.
603
604
605 :param cols: optional list of column key names or
606 :class:`_schema.Column` that acts as a filter for those columns that
607 will be fetched.
608 :param supplemental_cols: optional list of RETURNING expressions,
609 in the same form as one would pass to the
610 :meth:`.UpdateBase.returning` method. When present, the additional
611 columns will be included in the RETURNING clause, and the
612 :class:`.CursorResult` object will be "rewound" when returned, so
613 that methods like :meth:`.CursorResult.all` will return new rows
614 mostly as though the statement used :meth:`.UpdateBase.returning`
615 directly. However, unlike when using :meth:`.UpdateBase.returning`
616 directly, the **order of the columns is undefined**, so can only be
617 targeted using names or :attr:`.Row._mapping` keys; they cannot
618 reliably be targeted positionally.
619
620 .. versionadded:: 2.0
621
622 :param sort_by_parameter_order: for a batch INSERT that is being
623 executed against multiple parameter sets, organize the results of
624 RETURNING so that the returned rows correspond to the order of
625 parameter sets passed in. This applies only to an :term:`executemany`
626 execution for supporting dialects and typically makes use of the
627 :term:`insertmanyvalues` feature.
628
629 .. versionadded:: 2.0.10
630
631 .. seealso::
632
633 :ref:`engine_insertmanyvalues_returning_order` - background on
634 sorting of RETURNING rows for bulk INSERT
635
636 .. seealso::
637
638 :meth:`.UpdateBase.returning`
639
640 :attr:`_engine.CursorResult.returned_defaults`
641
642 :attr:`_engine.CursorResult.returned_defaults_rows`
643
644 :attr:`_engine.CursorResult.inserted_primary_key`
645
646 :attr:`_engine.CursorResult.inserted_primary_key_rows`
647
648 """
649
650 if self._return_defaults:
651 # note _return_defaults_columns = () means return all columns,
652 # so if we have been here before, only update collection if there
653 # are columns in the collection
654 if self._return_defaults_columns and cols:
655 self._return_defaults_columns = tuple(
656 util.OrderedSet(self._return_defaults_columns).union(
657 coercions.expect(roles.ColumnsClauseRole, c)
658 for c in cols
659 )
660 )
661 else:
662 # set for all columns
663 self._return_defaults_columns = ()
664 else:
665 self._return_defaults_columns = tuple(
666 coercions.expect(roles.ColumnsClauseRole, c) for c in cols
667 )
668 self._return_defaults = True
669 if sort_by_parameter_order:
670 if not self.is_insert:
671 raise exc.ArgumentError(
672 "The 'sort_by_parameter_order' argument to "
673 "return_defaults() only applies to INSERT statements"
674 )
675 self._sort_by_parameter_order = True
676 if supplemental_cols:
677 # uniquifying while also maintaining order (the maintain of order
678 # is for test suites but also for vertical splicing
679 supplemental_col_tup = (
680 coercions.expect(roles.ColumnsClauseRole, c)
681 for c in supplemental_cols
682 )
683
684 if self._supplemental_returning is None:
685 self._supplemental_returning = tuple(
686 util.unique_list(supplemental_col_tup)
687 )
688 else:
689 self._supplemental_returning = tuple(
690 util.unique_list(
691 self._supplemental_returning
692 + tuple(supplemental_col_tup)
693 )
694 )
695
696 return self
697
698 def is_derived_from(self, fromclause: Optional[FromClause]) -> bool:
699 """Return ``True`` if this :class:`.ReturnsRows` is
700 'derived' from the given :class:`.FromClause`.
701
702 Since these are DMLs, we dont want such statements ever being adapted
703 so we return False for derives.
704
705 """
706 return False
707
708 @_generative
709 def returning(
710 self,
711 *cols: _ColumnsClauseArgument[Any],
712 sort_by_parameter_order: bool = False,
713 **__kw: Any,
714 ) -> UpdateBase:
715 r"""Add a :term:`RETURNING` or equivalent clause to this statement.
716
717 e.g.:
718
719 .. sourcecode:: pycon+sql
720
721 >>> stmt = (
722 ... table.update()
723 ... .where(table.c.data == "value")
724 ... .values(status="X")
725 ... .returning(table.c.server_flag, table.c.updated_timestamp)
726 ... )
727 >>> print(stmt)
728 {printsql}UPDATE some_table SET status=:status
729 WHERE some_table.data = :data_1
730 RETURNING some_table.server_flag, some_table.updated_timestamp
731
732 The method may be invoked multiple times to add new entries to the
733 list of expressions to be returned.
734
735 .. versionadded:: 1.4.0b2 The method may be invoked multiple times to
736 add new entries to the list of expressions to be returned.
737
738 The given collection of column expressions should be derived from the
739 table that is the target of the INSERT, UPDATE, or DELETE. While
740 :class:`_schema.Column` objects are typical, the elements can also be
741 expressions:
742
743 .. sourcecode:: pycon+sql
744
745 >>> stmt = table.insert().returning(
746 ... (table.c.first_name + " " + table.c.last_name).label("fullname")
747 ... )
748 >>> print(stmt)
749 {printsql}INSERT INTO some_table (first_name, last_name)
750 VALUES (:first_name, :last_name)
751 RETURNING some_table.first_name || :first_name_1 || some_table.last_name AS fullname
752
753 Upon compilation, a RETURNING clause, or database equivalent,
754 will be rendered within the statement. For INSERT and UPDATE,
755 the values are the newly inserted/updated values. For DELETE,
756 the values are those of the rows which were deleted.
757
758 Upon execution, the values of the columns to be returned are made
759 available via the result set and can be iterated using
760 :meth:`_engine.CursorResult.fetchone` and similar.
761 For DBAPIs which do not
762 natively support returning values (i.e. cx_oracle), SQLAlchemy will
763 approximate this behavior at the result level so that a reasonable
764 amount of behavioral neutrality is provided.
765
766 Note that not all databases/DBAPIs
767 support RETURNING. For those backends with no support,
768 an exception is raised upon compilation and/or execution.
769 For those who do support it, the functionality across backends
770 varies greatly, including restrictions on executemany()
771 and other statements which return multiple rows. Please
772 read the documentation notes for the database in use in
773 order to determine the availability of RETURNING.
774
775 :param \*cols: series of columns, SQL expressions, or whole tables
776 entities to be returned.
777 :param sort_by_parameter_order: for a batch INSERT that is being
778 executed against multiple parameter sets, organize the results of
779 RETURNING so that the returned rows correspond to the order of
780 parameter sets passed in. This applies only to an :term:`executemany`
781 execution for supporting dialects and typically makes use of the
782 :term:`insertmanyvalues` feature.
783
784 .. versionadded:: 2.0.10
785
786 .. seealso::
787
788 :ref:`engine_insertmanyvalues_returning_order` - background on
789 sorting of RETURNING rows for bulk INSERT (Core level discussion)
790
791 :ref:`orm_queryguide_bulk_insert_returning_ordered` - example of
792 use with :ref:`orm_queryguide_bulk_insert` (ORM level discussion)
793
794 .. seealso::
795
796 :meth:`.UpdateBase.return_defaults` - an alternative method tailored
797 towards efficient fetching of server-side defaults and triggers
798 for single-row INSERTs or UPDATEs.
799
800 :ref:`tutorial_insert_returning` - in the :ref:`unified_tutorial`
801
802 """ # noqa: E501
803 if __kw:
804 raise _unexpected_kw("UpdateBase.returning()", __kw)
805 if self._return_defaults:
806 raise exc.InvalidRequestError(
807 "return_defaults() is already configured on this statement"
808 )
809 self._returning += tuple(
810 coercions.expect(roles.ColumnsClauseRole, c) for c in cols
811 )
812 if sort_by_parameter_order:
813 if not self.is_insert:
814 raise exc.ArgumentError(
815 "The 'sort_by_parameter_order' argument to returning() "
816 "only applies to INSERT statements"
817 )
818 self._sort_by_parameter_order = True
819 return self
820
821 def corresponding_column(
822 self, column: KeyedColumnElement[Any], require_embedded: bool = False
823 ) -> Optional[ColumnElement[Any]]:
824 return self.exported_columns.corresponding_column(
825 column, require_embedded=require_embedded
826 )
827
828 @util.ro_memoized_property
829 def _all_selected_columns(self) -> _SelectIterable:
830 return [c for c in _select_iterables(self._returning)]
831
832 @util.ro_memoized_property
833 def exported_columns(
834 self,
835 ) -> ReadOnlyColumnCollection[Optional[str], ColumnElement[Any]]:
836 """Return the RETURNING columns as a column collection for this
837 statement.
838
839 .. versionadded:: 1.4
840
841 """
842 return ColumnCollection(
843 (c.key, c)
844 for c in self._all_selected_columns
845 if is_column_element(c)
846 ).as_readonly()
847
848 @_generative
849 def with_hint(
850 self,
851 text: str,
852 selectable: Optional[_DMLTableArgument] = None,
853 dialect_name: str = "*",
854 ) -> Self:
855 """Add a table hint for a single table to this
856 INSERT/UPDATE/DELETE statement.
857
858 .. note::
859
860 :meth:`.UpdateBase.with_hint` currently applies only to
861 Microsoft SQL Server. For MySQL INSERT/UPDATE/DELETE hints, use
862 :meth:`.UpdateBase.prefix_with`.
863
864 The text of the hint is rendered in the appropriate
865 location for the database backend in use, relative
866 to the :class:`_schema.Table` that is the subject of this
867 statement, or optionally to that of the given
868 :class:`_schema.Table` passed as the ``selectable`` argument.
869
870 The ``dialect_name`` option will limit the rendering of a particular
871 hint to a particular backend. Such as, to add a hint
872 that only takes effect for SQL Server::
873
874 mytable.insert().with_hint("WITH (PAGLOCK)", dialect_name="mssql")
875
876 :param text: Text of the hint.
877 :param selectable: optional :class:`_schema.Table` that specifies
878 an element of the FROM clause within an UPDATE or DELETE
879 to be the subject of the hint - applies only to certain backends.
880 :param dialect_name: defaults to ``*``, if specified as the name
881 of a particular dialect, will apply these hints only when
882 that dialect is in use.
883 """
884 if selectable is None:
885 selectable = self.table
886 else:
887 selectable = coercions.expect(roles.DMLTableRole, selectable)
888 self._hints = self._hints.union({(selectable, dialect_name): text})
889 return self
890
891 @property
892 def entity_description(self) -> Dict[str, Any]:
893 """Return a :term:`plugin-enabled` description of the table and/or
894 entity which this DML construct is operating against.
895
896 This attribute is generally useful when using the ORM, as an
897 extended structure which includes information about mapped
898 entities is returned. The section :ref:`queryguide_inspection`
899 contains more background.
900
901 For a Core statement, the structure returned by this accessor
902 is derived from the :attr:`.UpdateBase.table` attribute, and
903 refers to the :class:`.Table` being inserted, updated, or deleted::
904
905 >>> stmt = insert(user_table)
906 >>> stmt.entity_description
907 {
908 "name": "user_table",
909 "table": Table("user_table", ...)
910 }
911
912 .. versionadded:: 1.4.33
913
914 .. seealso::
915
916 :attr:`.UpdateBase.returning_column_descriptions`
917
918 :attr:`.Select.column_descriptions` - entity information for
919 a :func:`.select` construct
920
921 :ref:`queryguide_inspection` - ORM background
922
923 """
924 meth = DMLState.get_plugin_class(self).get_entity_description
925 return meth(self)
926
927 @property
928 def returning_column_descriptions(self) -> List[Dict[str, Any]]:
929 """Return a :term:`plugin-enabled` description of the columns
930 which this DML construct is RETURNING against, in other words
931 the expressions established as part of :meth:`.UpdateBase.returning`.
932
933 This attribute is generally useful when using the ORM, as an
934 extended structure which includes information about mapped
935 entities is returned. The section :ref:`queryguide_inspection`
936 contains more background.
937
938 For a Core statement, the structure returned by this accessor is
939 derived from the same objects that are returned by the
940 :attr:`.UpdateBase.exported_columns` accessor::
941
942 >>> stmt = insert(user_table).returning(user_table.c.id, user_table.c.name)
943 >>> stmt.entity_description
944 [
945 {
946 "name": "id",
947 "type": Integer,
948 "expr": Column("id", Integer(), table=<user>, ...)
949 },
950 {
951 "name": "name",
952 "type": String(),
953 "expr": Column("name", String(), table=<user>, ...)
954 },
955 ]
956
957 .. versionadded:: 1.4.33
958
959 .. seealso::
960
961 :attr:`.UpdateBase.entity_description`
962
963 :attr:`.Select.column_descriptions` - entity information for
964 a :func:`.select` construct
965
966 :ref:`queryguide_inspection` - ORM background
967
968 """ # noqa: E501
969 meth = DMLState.get_plugin_class(
970 self
971 ).get_returning_column_descriptions
972 return meth(self)
973
974
975class ValuesBase(UpdateBase):
976 """Supplies support for :meth:`.ValuesBase.values` to
977 INSERT and UPDATE constructs."""
978
979 __visit_name__ = "values_base"
980
981 _supports_multi_parameters = False
982
983 select: Optional[Select[Any]] = None
984 """SELECT statement for INSERT .. FROM SELECT"""
985
986 _post_values_clause: Optional[ClauseElement] = None
987 """used by extensions to Insert etc. to add additional syntacitcal
988 constructs, e.g. ON CONFLICT etc."""
989
990 _values: Optional[util.immutabledict[_DMLColumnElement, Any]] = None
991 _multi_values: Tuple[
992 Union[
993 Sequence[Dict[_DMLColumnElement, Any]],
994 Sequence[Sequence[Any]],
995 ],
996 ...,
997 ] = ()
998
999 _ordered_values: Optional[List[Tuple[_DMLColumnElement, Any]]] = None
1000
1001 _select_names: Optional[List[str]] = None
1002 _inline: bool = False
1003
1004 def __init__(self, table: _DMLTableArgument):
1005 self.table = coercions.expect(
1006 roles.DMLTableRole, table, apply_propagate_attrs=self
1007 )
1008
1009 @_generative
1010 @_exclusive_against(
1011 "_select_names",
1012 "_ordered_values",
1013 msgs={
1014 "_select_names": "This construct already inserts from a SELECT",
1015 "_ordered_values": "This statement already has ordered "
1016 "values present",
1017 },
1018 )
1019 def values(
1020 self,
1021 *args: Union[
1022 _DMLColumnKeyMapping[Any],
1023 Sequence[Any],
1024 ],
1025 **kwargs: Any,
1026 ) -> Self:
1027 r"""Specify a fixed VALUES clause for an INSERT statement, or the SET
1028 clause for an UPDATE.
1029
1030 Note that the :class:`_expression.Insert` and
1031 :class:`_expression.Update`
1032 constructs support
1033 per-execution time formatting of the VALUES and/or SET clauses,
1034 based on the arguments passed to :meth:`_engine.Connection.execute`.
1035 However, the :meth:`.ValuesBase.values` method can be used to "fix" a
1036 particular set of parameters into the statement.
1037
1038 Multiple calls to :meth:`.ValuesBase.values` will produce a new
1039 construct, each one with the parameter list modified to include
1040 the new parameters sent. In the typical case of a single
1041 dictionary of parameters, the newly passed keys will replace
1042 the same keys in the previous construct. In the case of a list-based
1043 "multiple values" construct, each new list of values is extended
1044 onto the existing list of values.
1045
1046 :param \**kwargs: key value pairs representing the string key
1047 of a :class:`_schema.Column`
1048 mapped to the value to be rendered into the
1049 VALUES or SET clause::
1050
1051 users.insert().values(name="some name")
1052
1053 users.update().where(users.c.id == 5).values(name="some name")
1054
1055 :param \*args: As an alternative to passing key/value parameters,
1056 a dictionary, tuple, or list of dictionaries or tuples can be passed
1057 as a single positional argument in order to form the VALUES or
1058 SET clause of the statement. The forms that are accepted vary
1059 based on whether this is an :class:`_expression.Insert` or an
1060 :class:`_expression.Update` construct.
1061
1062 For either an :class:`_expression.Insert` or
1063 :class:`_expression.Update`
1064 construct, a single dictionary can be passed, which works the same as
1065 that of the kwargs form::
1066
1067 users.insert().values({"name": "some name"})
1068
1069 users.update().values({"name": "some new name"})
1070
1071 Also for either form but more typically for the
1072 :class:`_expression.Insert` construct, a tuple that contains an
1073 entry for every column in the table is also accepted::
1074
1075 users.insert().values((5, "some name"))
1076
1077 The :class:`_expression.Insert` construct also supports being
1078 passed a list of dictionaries or full-table-tuples, which on the
1079 server will render the less common SQL syntax of "multiple values" -
1080 this syntax is supported on backends such as SQLite, PostgreSQL,
1081 MySQL, but not necessarily others::
1082
1083 users.insert().values(
1084 [
1085 {"name": "some name"},
1086 {"name": "some other name"},
1087 {"name": "yet another name"},
1088 ]
1089 )
1090
1091 The above form would render a multiple VALUES statement similar to:
1092
1093 .. sourcecode:: sql
1094
1095 INSERT INTO users (name) VALUES
1096 (:name_1),
1097 (:name_2),
1098 (:name_3)
1099
1100 It is essential to note that **passing multiple values is
1101 NOT the same as using traditional executemany() form**. The above
1102 syntax is a **special** syntax not typically used. To emit an
1103 INSERT statement against multiple rows, the normal method is
1104 to pass a multiple values list to the
1105 :meth:`_engine.Connection.execute`
1106 method, which is supported by all database backends and is generally
1107 more efficient for a very large number of parameters.
1108
1109 .. seealso::
1110
1111 :ref:`tutorial_multiple_parameters` - an introduction to
1112 the traditional Core method of multiple parameter set
1113 invocation for INSERTs and other statements.
1114
1115 The UPDATE construct also supports rendering the SET parameters
1116 in a specific order. For this feature refer to the
1117 :meth:`_expression.Update.ordered_values` method.
1118
1119 .. seealso::
1120
1121 :meth:`_expression.Update.ordered_values`
1122
1123
1124 """
1125 if args:
1126 # positional case. this is currently expensive. we don't
1127 # yet have positional-only args so we have to check the length.
1128 # then we need to check multiparams vs. single dictionary.
1129 # since the parameter format is needed in order to determine
1130 # a cache key, we need to determine this up front.
1131 arg = args[0]
1132
1133 if kwargs:
1134 raise exc.ArgumentError(
1135 "Can't pass positional and kwargs to values() "
1136 "simultaneously"
1137 )
1138 elif len(args) > 1:
1139 raise exc.ArgumentError(
1140 "Only a single dictionary/tuple or list of "
1141 "dictionaries/tuples is accepted positionally."
1142 )
1143
1144 elif isinstance(arg, collections_abc.Sequence):
1145 if arg and isinstance(arg[0], dict):
1146 multi_kv_generator = DMLState.get_plugin_class(
1147 self
1148 )._get_multi_crud_kv_pairs
1149 self._multi_values += (multi_kv_generator(self, arg),)
1150 return self
1151
1152 if arg and isinstance(arg[0], (list, tuple)):
1153 self._multi_values += (arg,)
1154 return self
1155
1156 if TYPE_CHECKING:
1157 # crud.py raises during compilation if this is not the
1158 # case
1159 assert isinstance(self, Insert)
1160
1161 # tuple values
1162 arg = {c.key: value for c, value in zip(self.table.c, arg)}
1163
1164 else:
1165 # kwarg path. this is the most common path for non-multi-params
1166 # so this is fairly quick.
1167 arg = cast("Dict[_DMLColumnArgument, Any]", kwargs)
1168 if args:
1169 raise exc.ArgumentError(
1170 "Only a single dictionary/tuple or list of "
1171 "dictionaries/tuples is accepted positionally."
1172 )
1173
1174 # for top level values(), convert literals to anonymous bound
1175 # parameters at statement construction time, so that these values can
1176 # participate in the cache key process like any other ClauseElement.
1177 # crud.py now intercepts bound parameters with unique=True from here
1178 # and ensures they get the "crud"-style name when rendered.
1179
1180 kv_generator = DMLState.get_plugin_class(self)._get_crud_kv_pairs
1181 coerced_arg = dict(kv_generator(self, arg.items(), True))
1182 if self._values:
1183 self._values = self._values.union(coerced_arg)
1184 else:
1185 self._values = util.immutabledict(coerced_arg)
1186 return self
1187
1188
1189class Insert(ValuesBase):
1190 """Represent an INSERT construct.
1191
1192 The :class:`_expression.Insert` object is created using the
1193 :func:`_expression.insert()` function.
1194
1195 """
1196
1197 __visit_name__ = "insert"
1198
1199 _supports_multi_parameters = True
1200
1201 select = None
1202 include_insert_from_select_defaults = False
1203
1204 _sort_by_parameter_order: bool = False
1205
1206 is_insert = True
1207
1208 table: TableClause
1209
1210 _traverse_internals = (
1211 [
1212 ("table", InternalTraversal.dp_clauseelement),
1213 ("_inline", InternalTraversal.dp_boolean),
1214 ("_select_names", InternalTraversal.dp_string_list),
1215 ("_values", InternalTraversal.dp_dml_values),
1216 ("_multi_values", InternalTraversal.dp_dml_multi_values),
1217 ("select", InternalTraversal.dp_clauseelement),
1218 ("_post_values_clause", InternalTraversal.dp_clauseelement),
1219 ("_returning", InternalTraversal.dp_clauseelement_tuple),
1220 ("_hints", InternalTraversal.dp_table_hint_list),
1221 ("_return_defaults", InternalTraversal.dp_boolean),
1222 (
1223 "_return_defaults_columns",
1224 InternalTraversal.dp_clauseelement_tuple,
1225 ),
1226 ("_sort_by_parameter_order", InternalTraversal.dp_boolean),
1227 ]
1228 + HasPrefixes._has_prefixes_traverse_internals
1229 + DialectKWArgs._dialect_kwargs_traverse_internals
1230 + Executable._executable_traverse_internals
1231 + HasCTE._has_ctes_traverse_internals
1232 )
1233
1234 def __init__(self, table: _DMLTableArgument):
1235 super().__init__(table)
1236
1237 @_generative
1238 def inline(self) -> Self:
1239 """Make this :class:`_expression.Insert` construct "inline" .
1240
1241 When set, no attempt will be made to retrieve the
1242 SQL-generated default values to be provided within the statement;
1243 in particular,
1244 this allows SQL expressions to be rendered 'inline' within the
1245 statement without the need to pre-execute them beforehand; for
1246 backends that support "returning", this turns off the "implicit
1247 returning" feature for the statement.
1248
1249
1250 .. versionchanged:: 1.4 the :paramref:`_expression.Insert.inline`
1251 parameter
1252 is now superseded by the :meth:`_expression.Insert.inline` method.
1253
1254 """
1255 self._inline = True
1256 return self
1257
1258 @_generative
1259 def from_select(
1260 self,
1261 names: Sequence[_DMLColumnArgument],
1262 select: Selectable,
1263 include_defaults: bool = True,
1264 ) -> Self:
1265 """Return a new :class:`_expression.Insert` construct which represents
1266 an ``INSERT...FROM SELECT`` statement.
1267
1268 e.g.::
1269
1270 sel = select(table1.c.a, table1.c.b).where(table1.c.c > 5)
1271 ins = table2.insert().from_select(["a", "b"], sel)
1272
1273 :param names: a sequence of string column names or
1274 :class:`_schema.Column`
1275 objects representing the target columns.
1276 :param select: a :func:`_expression.select` construct,
1277 :class:`_expression.FromClause`
1278 or other construct which resolves into a
1279 :class:`_expression.FromClause`,
1280 such as an ORM :class:`_query.Query` object, etc. The order of
1281 columns returned from this FROM clause should correspond to the
1282 order of columns sent as the ``names`` parameter; while this
1283 is not checked before passing along to the database, the database
1284 would normally raise an exception if these column lists don't
1285 correspond.
1286 :param include_defaults: if True, non-server default values and
1287 SQL expressions as specified on :class:`_schema.Column` objects
1288 (as documented in :ref:`metadata_defaults_toplevel`) not
1289 otherwise specified in the list of names will be rendered
1290 into the INSERT and SELECT statements, so that these values are also
1291 included in the data to be inserted.
1292
1293 .. note:: A Python-side default that uses a Python callable function
1294 will only be invoked **once** for the whole statement, and **not
1295 per row**.
1296
1297 """
1298
1299 if self._values:
1300 raise exc.InvalidRequestError(
1301 "This construct already inserts value expressions"
1302 )
1303
1304 self._select_names = [
1305 coercions.expect(roles.DMLColumnRole, name, as_key=True)
1306 for name in names
1307 ]
1308 self._inline = True
1309 self.include_insert_from_select_defaults = include_defaults
1310 self.select = coercions.expect(roles.DMLSelectRole, select)
1311 return self
1312
1313 if TYPE_CHECKING:
1314 # START OVERLOADED FUNCTIONS self.returning ReturningInsert 1-8 ", *, sort_by_parameter_order: bool = False" # noqa: E501
1315
1316 # code within this block is **programmatically,
1317 # statically generated** by tools/generate_tuple_map_overloads.py
1318
1319 @overload
1320 def returning(
1321 self, __ent0: _TCCA[_T0], *, sort_by_parameter_order: bool = False
1322 ) -> ReturningInsert[Tuple[_T0]]: ...
1323
1324 @overload
1325 def returning(
1326 self,
1327 __ent0: _TCCA[_T0],
1328 __ent1: _TCCA[_T1],
1329 *,
1330 sort_by_parameter_order: bool = False,
1331 ) -> ReturningInsert[Tuple[_T0, _T1]]: ...
1332
1333 @overload
1334 def returning(
1335 self,
1336 __ent0: _TCCA[_T0],
1337 __ent1: _TCCA[_T1],
1338 __ent2: _TCCA[_T2],
1339 *,
1340 sort_by_parameter_order: bool = False,
1341 ) -> ReturningInsert[Tuple[_T0, _T1, _T2]]: ...
1342
1343 @overload
1344 def returning(
1345 self,
1346 __ent0: _TCCA[_T0],
1347 __ent1: _TCCA[_T1],
1348 __ent2: _TCCA[_T2],
1349 __ent3: _TCCA[_T3],
1350 *,
1351 sort_by_parameter_order: bool = False,
1352 ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3]]: ...
1353
1354 @overload
1355 def returning(
1356 self,
1357 __ent0: _TCCA[_T0],
1358 __ent1: _TCCA[_T1],
1359 __ent2: _TCCA[_T2],
1360 __ent3: _TCCA[_T3],
1361 __ent4: _TCCA[_T4],
1362 *,
1363 sort_by_parameter_order: bool = False,
1364 ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4]]: ...
1365
1366 @overload
1367 def returning(
1368 self,
1369 __ent0: _TCCA[_T0],
1370 __ent1: _TCCA[_T1],
1371 __ent2: _TCCA[_T2],
1372 __ent3: _TCCA[_T3],
1373 __ent4: _TCCA[_T4],
1374 __ent5: _TCCA[_T5],
1375 *,
1376 sort_by_parameter_order: bool = False,
1377 ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ...
1378
1379 @overload
1380 def returning(
1381 self,
1382 __ent0: _TCCA[_T0],
1383 __ent1: _TCCA[_T1],
1384 __ent2: _TCCA[_T2],
1385 __ent3: _TCCA[_T3],
1386 __ent4: _TCCA[_T4],
1387 __ent5: _TCCA[_T5],
1388 __ent6: _TCCA[_T6],
1389 *,
1390 sort_by_parameter_order: bool = False,
1391 ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ...
1392
1393 @overload
1394 def returning(
1395 self,
1396 __ent0: _TCCA[_T0],
1397 __ent1: _TCCA[_T1],
1398 __ent2: _TCCA[_T2],
1399 __ent3: _TCCA[_T3],
1400 __ent4: _TCCA[_T4],
1401 __ent5: _TCCA[_T5],
1402 __ent6: _TCCA[_T6],
1403 __ent7: _TCCA[_T7],
1404 *,
1405 sort_by_parameter_order: bool = False,
1406 ) -> ReturningInsert[
1407 Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]
1408 ]: ...
1409
1410 # END OVERLOADED FUNCTIONS self.returning
1411
1412 @overload
1413 def returning(
1414 self,
1415 *cols: _ColumnsClauseArgument[Any],
1416 sort_by_parameter_order: bool = False,
1417 **__kw: Any,
1418 ) -> ReturningInsert[Any]: ...
1419
1420 def returning(
1421 self,
1422 *cols: _ColumnsClauseArgument[Any],
1423 sort_by_parameter_order: bool = False,
1424 **__kw: Any,
1425 ) -> ReturningInsert[Any]: ...
1426
1427
1428class ReturningInsert(Insert, TypedReturnsRows[_TP]):
1429 """Typing-only class that establishes a generic type form of
1430 :class:`.Insert` which tracks returned column types.
1431
1432 This datatype is delivered when calling the
1433 :meth:`.Insert.returning` method.
1434
1435 .. versionadded:: 2.0
1436
1437 """
1438
1439
1440class DMLWhereBase:
1441 table: _DMLTableElement
1442 _where_criteria: Tuple[ColumnElement[Any], ...] = ()
1443
1444 @_generative
1445 def where(self, *whereclause: _ColumnExpressionArgument[bool]) -> Self:
1446 """Return a new construct with the given expression(s) added to
1447 its WHERE clause, joined to the existing clause via AND, if any.
1448
1449 Both :meth:`_dml.Update.where` and :meth:`_dml.Delete.where`
1450 support multiple-table forms, including database-specific
1451 ``UPDATE...FROM`` as well as ``DELETE..USING``. For backends that
1452 don't have multiple-table support, a backend agnostic approach
1453 to using multiple tables is to make use of correlated subqueries.
1454 See the linked tutorial sections below for examples.
1455
1456 .. seealso::
1457
1458 :ref:`tutorial_correlated_updates`
1459
1460 :ref:`tutorial_update_from`
1461
1462 :ref:`tutorial_multi_table_deletes`
1463
1464 """
1465
1466 for criterion in whereclause:
1467 where_criteria: ColumnElement[Any] = coercions.expect(
1468 roles.WhereHavingRole, criterion, apply_propagate_attrs=self
1469 )
1470 self._where_criteria += (where_criteria,)
1471 return self
1472
1473 def filter(self, *criteria: roles.ExpressionElementRole[Any]) -> Self:
1474 """A synonym for the :meth:`_dml.DMLWhereBase.where` method.
1475
1476 .. versionadded:: 1.4
1477
1478 """
1479
1480 return self.where(*criteria)
1481
1482 def _filter_by_zero(self) -> _DMLTableElement:
1483 return self.table
1484
1485 def filter_by(self, **kwargs: Any) -> Self:
1486 r"""apply the given filtering criterion as a WHERE clause
1487 to this select.
1488
1489 """
1490 from_entity = self._filter_by_zero()
1491
1492 clauses = [
1493 _entity_namespace_key(from_entity, key) == value
1494 for key, value in kwargs.items()
1495 ]
1496 return self.filter(*clauses)
1497
1498 @property
1499 def whereclause(self) -> Optional[ColumnElement[Any]]:
1500 """Return the completed WHERE clause for this :class:`.DMLWhereBase`
1501 statement.
1502
1503 This assembles the current collection of WHERE criteria
1504 into a single :class:`_expression.BooleanClauseList` construct.
1505
1506
1507 .. versionadded:: 1.4
1508
1509 """
1510
1511 return BooleanClauseList._construct_for_whereclause(
1512 self._where_criteria
1513 )
1514
1515
1516class Update(DMLWhereBase, ValuesBase):
1517 """Represent an Update construct.
1518
1519 The :class:`_expression.Update` object is created using the
1520 :func:`_expression.update()` function.
1521
1522 """
1523
1524 __visit_name__ = "update"
1525
1526 is_update = True
1527
1528 _traverse_internals = (
1529 [
1530 ("table", InternalTraversal.dp_clauseelement),
1531 ("_where_criteria", InternalTraversal.dp_clauseelement_tuple),
1532 ("_inline", InternalTraversal.dp_boolean),
1533 ("_ordered_values", InternalTraversal.dp_dml_ordered_values),
1534 ("_values", InternalTraversal.dp_dml_values),
1535 ("_returning", InternalTraversal.dp_clauseelement_tuple),
1536 ("_hints", InternalTraversal.dp_table_hint_list),
1537 ("_return_defaults", InternalTraversal.dp_boolean),
1538 (
1539 "_return_defaults_columns",
1540 InternalTraversal.dp_clauseelement_tuple,
1541 ),
1542 ]
1543 + HasPrefixes._has_prefixes_traverse_internals
1544 + DialectKWArgs._dialect_kwargs_traverse_internals
1545 + Executable._executable_traverse_internals
1546 + HasCTE._has_ctes_traverse_internals
1547 )
1548
1549 def __init__(self, table: _DMLTableArgument):
1550 super().__init__(table)
1551
1552 @_generative
1553 def ordered_values(self, *args: Tuple[_DMLColumnArgument, Any]) -> Self:
1554 """Specify the VALUES clause of this UPDATE statement with an explicit
1555 parameter ordering that will be maintained in the SET clause of the
1556 resulting UPDATE statement.
1557
1558 E.g.::
1559
1560 stmt = table.update().ordered_values(("name", "ed"), ("ident", "foo"))
1561
1562 .. seealso::
1563
1564 :ref:`tutorial_parameter_ordered_updates` - full example of the
1565 :meth:`_expression.Update.ordered_values` method.
1566
1567 .. versionchanged:: 1.4 The :meth:`_expression.Update.ordered_values`
1568 method
1569 supersedes the
1570 :paramref:`_expression.update.preserve_parameter_order`
1571 parameter, which will be removed in SQLAlchemy 2.0.
1572
1573 """ # noqa: E501
1574 if self._values:
1575 raise exc.ArgumentError(
1576 "This statement already has values present"
1577 )
1578 elif self._ordered_values:
1579 raise exc.ArgumentError(
1580 "This statement already has ordered values present"
1581 )
1582
1583 kv_generator = DMLState.get_plugin_class(self)._get_crud_kv_pairs
1584 self._ordered_values = kv_generator(self, args, True)
1585 return self
1586
1587 @_generative
1588 def inline(self) -> Self:
1589 """Make this :class:`_expression.Update` construct "inline" .
1590
1591 When set, SQL defaults present on :class:`_schema.Column`
1592 objects via the
1593 ``default`` keyword will be compiled 'inline' into the statement and
1594 not pre-executed. This means that their values will not be available
1595 in the dictionary returned from
1596 :meth:`_engine.CursorResult.last_updated_params`.
1597
1598 .. versionchanged:: 1.4 the :paramref:`_expression.update.inline`
1599 parameter
1600 is now superseded by the :meth:`_expression.Update.inline` method.
1601
1602 """
1603 self._inline = True
1604 return self
1605
1606 if TYPE_CHECKING:
1607 # START OVERLOADED FUNCTIONS self.returning ReturningUpdate 1-8
1608
1609 # code within this block is **programmatically,
1610 # statically generated** by tools/generate_tuple_map_overloads.py
1611
1612 @overload
1613 def returning(
1614 self, __ent0: _TCCA[_T0]
1615 ) -> ReturningUpdate[Tuple[_T0]]: ...
1616
1617 @overload
1618 def returning(
1619 self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
1620 ) -> ReturningUpdate[Tuple[_T0, _T1]]: ...
1621
1622 @overload
1623 def returning(
1624 self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
1625 ) -> ReturningUpdate[Tuple[_T0, _T1, _T2]]: ...
1626
1627 @overload
1628 def returning(
1629 self,
1630 __ent0: _TCCA[_T0],
1631 __ent1: _TCCA[_T1],
1632 __ent2: _TCCA[_T2],
1633 __ent3: _TCCA[_T3],
1634 ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3]]: ...
1635
1636 @overload
1637 def returning(
1638 self,
1639 __ent0: _TCCA[_T0],
1640 __ent1: _TCCA[_T1],
1641 __ent2: _TCCA[_T2],
1642 __ent3: _TCCA[_T3],
1643 __ent4: _TCCA[_T4],
1644 ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4]]: ...
1645
1646 @overload
1647 def returning(
1648 self,
1649 __ent0: _TCCA[_T0],
1650 __ent1: _TCCA[_T1],
1651 __ent2: _TCCA[_T2],
1652 __ent3: _TCCA[_T3],
1653 __ent4: _TCCA[_T4],
1654 __ent5: _TCCA[_T5],
1655 ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ...
1656
1657 @overload
1658 def returning(
1659 self,
1660 __ent0: _TCCA[_T0],
1661 __ent1: _TCCA[_T1],
1662 __ent2: _TCCA[_T2],
1663 __ent3: _TCCA[_T3],
1664 __ent4: _TCCA[_T4],
1665 __ent5: _TCCA[_T5],
1666 __ent6: _TCCA[_T6],
1667 ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ...
1668
1669 @overload
1670 def returning(
1671 self,
1672 __ent0: _TCCA[_T0],
1673 __ent1: _TCCA[_T1],
1674 __ent2: _TCCA[_T2],
1675 __ent3: _TCCA[_T3],
1676 __ent4: _TCCA[_T4],
1677 __ent5: _TCCA[_T5],
1678 __ent6: _TCCA[_T6],
1679 __ent7: _TCCA[_T7],
1680 ) -> ReturningUpdate[
1681 Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]
1682 ]: ...
1683
1684 # END OVERLOADED FUNCTIONS self.returning
1685
1686 @overload
1687 def returning(
1688 self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
1689 ) -> ReturningUpdate[Any]: ...
1690
1691 def returning(
1692 self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
1693 ) -> ReturningUpdate[Any]: ...
1694
1695
1696class ReturningUpdate(Update, TypedReturnsRows[_TP]):
1697 """Typing-only class that establishes a generic type form of
1698 :class:`.Update` which tracks returned column types.
1699
1700 This datatype is delivered when calling the
1701 :meth:`.Update.returning` method.
1702
1703 .. versionadded:: 2.0
1704
1705 """
1706
1707
1708class Delete(DMLWhereBase, UpdateBase):
1709 """Represent a DELETE construct.
1710
1711 The :class:`_expression.Delete` object is created using the
1712 :func:`_expression.delete()` function.
1713
1714 """
1715
1716 __visit_name__ = "delete"
1717
1718 is_delete = True
1719
1720 _traverse_internals = (
1721 [
1722 ("table", InternalTraversal.dp_clauseelement),
1723 ("_where_criteria", InternalTraversal.dp_clauseelement_tuple),
1724 ("_returning", InternalTraversal.dp_clauseelement_tuple),
1725 ("_hints", InternalTraversal.dp_table_hint_list),
1726 ]
1727 + HasPrefixes._has_prefixes_traverse_internals
1728 + DialectKWArgs._dialect_kwargs_traverse_internals
1729 + Executable._executable_traverse_internals
1730 + HasCTE._has_ctes_traverse_internals
1731 )
1732
1733 def __init__(self, table: _DMLTableArgument):
1734 self.table = coercions.expect(
1735 roles.DMLTableRole, table, apply_propagate_attrs=self
1736 )
1737
1738 if TYPE_CHECKING:
1739 # START OVERLOADED FUNCTIONS self.returning ReturningDelete 1-8
1740
1741 # code within this block is **programmatically,
1742 # statically generated** by tools/generate_tuple_map_overloads.py
1743
1744 @overload
1745 def returning(
1746 self, __ent0: _TCCA[_T0]
1747 ) -> ReturningDelete[Tuple[_T0]]: ...
1748
1749 @overload
1750 def returning(
1751 self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
1752 ) -> ReturningDelete[Tuple[_T0, _T1]]: ...
1753
1754 @overload
1755 def returning(
1756 self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
1757 ) -> ReturningDelete[Tuple[_T0, _T1, _T2]]: ...
1758
1759 @overload
1760 def returning(
1761 self,
1762 __ent0: _TCCA[_T0],
1763 __ent1: _TCCA[_T1],
1764 __ent2: _TCCA[_T2],
1765 __ent3: _TCCA[_T3],
1766 ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3]]: ...
1767
1768 @overload
1769 def returning(
1770 self,
1771 __ent0: _TCCA[_T0],
1772 __ent1: _TCCA[_T1],
1773 __ent2: _TCCA[_T2],
1774 __ent3: _TCCA[_T3],
1775 __ent4: _TCCA[_T4],
1776 ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4]]: ...
1777
1778 @overload
1779 def returning(
1780 self,
1781 __ent0: _TCCA[_T0],
1782 __ent1: _TCCA[_T1],
1783 __ent2: _TCCA[_T2],
1784 __ent3: _TCCA[_T3],
1785 __ent4: _TCCA[_T4],
1786 __ent5: _TCCA[_T5],
1787 ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ...
1788
1789 @overload
1790 def returning(
1791 self,
1792 __ent0: _TCCA[_T0],
1793 __ent1: _TCCA[_T1],
1794 __ent2: _TCCA[_T2],
1795 __ent3: _TCCA[_T3],
1796 __ent4: _TCCA[_T4],
1797 __ent5: _TCCA[_T5],
1798 __ent6: _TCCA[_T6],
1799 ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ...
1800
1801 @overload
1802 def returning(
1803 self,
1804 __ent0: _TCCA[_T0],
1805 __ent1: _TCCA[_T1],
1806 __ent2: _TCCA[_T2],
1807 __ent3: _TCCA[_T3],
1808 __ent4: _TCCA[_T4],
1809 __ent5: _TCCA[_T5],
1810 __ent6: _TCCA[_T6],
1811 __ent7: _TCCA[_T7],
1812 ) -> ReturningDelete[
1813 Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]
1814 ]: ...
1815
1816 # END OVERLOADED FUNCTIONS self.returning
1817
1818 @overload
1819 def returning(
1820 self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
1821 ) -> ReturningDelete[Any]: ...
1822
1823 def returning(
1824 self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
1825 ) -> ReturningDelete[Any]: ...
1826
1827
1828class ReturningDelete(Update, TypedReturnsRows[_TP]):
1829 """Typing-only class that establishes a generic type form of
1830 :class:`.Delete` which tracks returned column types.
1831
1832 This datatype is delivered when calling the
1833 :meth:`.Delete.returning` method.
1834
1835 .. versionadded:: 2.0
1836
1837 """