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