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