Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
dml.py1818 linesDownload Raw Back to sql
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`.
10
11"""
12from __future__ import annotations
13
14import collections.abc as collections_abc
15import operator
16from typing import Any
17from typing import cast
18from typing import Dict
19from typing import Iterable
20from typing import List
21from typing import MutableMapping
22from typing import NoReturn
23from typing import Optional
24from typing import overload
25from typing import Sequence
26from typing import Tuple
27from typing import Type
28from typing import TYPE_CHECKING
29from typing import TypeVar
30from typing import Union
31
32from . import coercions
33from . import roles
34from . import util as sql_util
35from ._typing import _TP
36from ._typing import _unexpected_kw
37from ._typing import is_column_element
38from ._typing import is_named_from_clause
39from .base import _entity_namespace_key
40from .base import _exclusive_against
41from .base import _from_objects
42from .base import _generative
43from .base import _select_iterables
44from .base import ColumnCollection
45from .base import CompileState
46from .base import DialectKWArgs
47from .base import Executable
48from .base import Generative
49from .base import HasCompileState
50from .elements import BooleanClauseList
51from .elements import ClauseElement
52from .elements import ColumnClause
53from .elements import ColumnElement
54from .elements import Null
55from .selectable import Alias
56from .selectable import ExecutableReturnsRows
57from .selectable import FromClause
58from .selectable import HasCTE
59from .selectable import HasPrefixes
60from .selectable import Join
61from .selectable import SelectLabelStyle
62from .selectable import TableClause
63from .selectable import TypedReturnsRows
64from .sqltypes import NullType
65from .visitors import InternalTraversal
66from .. import exc
67from .. import util
68from ..util.typing import Self
69from ..util.typing import TypeGuard
70
71if TYPE_CHECKING:
72    from ._typing import _ColumnExpressionArgument
73    from ._typing import _ColumnsClauseArgument
74    from ._typing import _DMLColumnArgument
75    from ._typing import _DMLColumnKeyMapping
76    from ._typing import _DMLTableArgument
77    from ._typing import _T0  # noqa
78    from ._typing import _T1  # noqa
79    from ._typing import _T2  # noqa
80    from ._typing import _T3  # noqa
81    from ._typing import _T4  # noqa
82    from ._typing import _T5  # noqa
83    from ._typing import _T6  # noqa
84    from ._typing import _T7  # noqa
85    from ._typing import _TypedColumnClauseArgument as _TCCA  # noqa
86    from .base import ReadOnlyColumnCollection
87    from .compiler import SQLCompiler
88    from .elements import KeyedColumnElement
89    from .selectable import _ColumnsClauseElement
90    from .selectable import _SelectIterable
91    from .selectable import Select
92    from .selectable import Selectable
93
94    def isupdate(dml: DMLState) -> TypeGuard[UpdateDMLState]: ...
95
96    def isdelete(dml: DMLState) -> TypeGuard[DeleteDMLState]: ...
97
98    def isinsert(dml: DMLState) -> TypeGuard[InsertDMLState]: ...
99
100else:
101    isupdate = operator.attrgetter("isupdate")
102    isdelete = operator.attrgetter("isdelete")
103    isinsert = operator.attrgetter("isinsert")
104
105
106_T = TypeVar("_T", bound=Any)
107
108_DMLColumnElement = Union[str, ColumnClause[Any]]
109_DMLTableElement = Union[TableClause, Alias, Join]
110
111
112class DMLState(CompileState):
113    _no_parameters = True
114    _dict_parameters: Optional[MutableMapping[_DMLColumnElement, Any]] = None
115    _multi_parameters: Optional[
116        List[MutableMapping[_DMLColumnElement, Any]]
117    ] = None
118    _ordered_values: Optional[List[Tuple[_DMLColumnElement, Any]]] = None
119    _parameter_ordering: Optional[List[_DMLColumnElement]] = None
120    _primary_table: FromClause
121    _supports_implicit_returning = True
122
123    isupdate = False
124    isdelete = False
125    isinsert = False
126
127    statement: UpdateBase
128
129    def __init__(
130        self, statement: UpdateBase, compiler: SQLCompiler, **kw: Any
131    ):
132        raise NotImplementedError()
133
134    @classmethod
135    def get_entity_description(cls, statement: UpdateBase) -> Dict[str, Any]:
136        return {
137            "name": (
138                statement.table.name
139                if is_named_from_clause(statement.table)
140                else None
141            ),
142            "table": statement.table,
143        }
144
145    @classmethod
146    def get_returning_column_descriptions(
147        cls, statement: UpdateBase
148    ) -> List[Dict[str, Any]]:
149        return [
150            {
151                "name": c.key,
152                "type": c.type,
153                "expr": c,
154            }
155            for c in statement._all_selected_columns
156        ]
157
158    @property
159    def dml_table(self) -> _DMLTableElement:
160        return self.statement.table
161
162    if TYPE_CHECKING:
163
164        @classmethod
165        def get_plugin_class(cls, statement: Executable) -> Type[DMLState]: ...
166
167    @classmethod
168    def _get_multi_crud_kv_pairs(
169        cls,
170        statement: UpdateBase,
171        multi_kv_iterator: Iterable[Dict[_DMLColumnArgument, Any]],
172    ) -> List[Dict[_DMLColumnElement, Any]]:
173        return [
174            {
175                coercions.expect(roles.DMLColumnRole, k): v
176                for k, v in mapping.items()
177            }
178            for mapping in multi_kv_iterator
179        ]
180
181    @classmethod
182    def _get_crud_kv_pairs(
183        cls,
184        statement: UpdateBase,
185        kv_iterator: Iterable[Tuple[_DMLColumnArgument, Any]],
186        needs_to_be_cacheable: bool,
187    ) -> List[Tuple[_DMLColumnElement, Any]]:
188        return [
189            (
190                coercions.expect(roles.DMLColumnRole, k),
191                (
192                    v
193                    if not needs_to_be_cacheable
194                    else coercions.expect(
195                        roles.ExpressionElementRole,
196                        v,
197                        type_=NullType(),
198                        is_crud=True,
199                    )
200                ),
201            )
202            for k, v in kv_iterator
203        ]
204
205    def _make_extra_froms(
206        self, statement: DMLWhereBase
207    ) -> Tuple[FromClause, List[FromClause]]:
208        froms: List[FromClause] = []
209
210        all_tables = list(sql_util.tables_from_leftmost(statement.table))
211        primary_table = all_tables[0]
212        seen = {primary_table}
213
214        consider = statement._where_criteria
215        if self._dict_parameters:
216            consider += tuple(self._dict_parameters.values())
217
218        for crit in consider:
219            for item in _from_objects(crit):
220                if not seen.intersection(item._cloned_set):
221                    froms.append(item)
222                seen.update(item._cloned_set)
223
224        froms.extend(all_tables[1:])
225        return primary_table, froms
226
227    def _process_values(self, statement: ValuesBase) -> None:
228        if self._no_parameters:
229            self._dict_parameters = statement._values
230            self._no_parameters = False
231
232    def _process_select_values(self, statement: ValuesBase) -> None:
233        assert statement._select_names is not None
234        parameters: MutableMapping[_DMLColumnElement, Any] = {
235            name: Null() for name in statement._select_names
236        }
237
238        if self._no_parameters:
239            self._no_parameters = False
240            self._dict_parameters = parameters
241        else:
242            # this condition normally not reachable as the Insert
243            # does not allow this construction to occur
244            assert False, "This statement already has parameters"
245
246    def _no_multi_values_supported(self, statement: ValuesBase) -> NoReturn:
247        raise exc.InvalidRequestError(
248            "%s construct does not support "
249            "multiple parameter sets." % statement.__visit_name__.upper()
250        )
251
252    def _cant_mix_formats_error(self) -> NoReturn:
253        raise exc.InvalidRequestError(
254            "Can't mix single and multiple VALUES "
255            "formats in one INSERT statement; one style appends to a "
256            "list while the other replaces values, so the intent is "
257            "ambiguous."
258        )
259
260
261@CompileState.plugin_for("default", "insert")
262class InsertDMLState(DMLState):
263    isinsert = True
264
265    include_table_with_column_exprs = False
266
267    _has_multi_parameters = False
268
269    def __init__(
270        self,
271        statement: Insert,
272        compiler: SQLCompiler,
273        disable_implicit_returning: bool = False,
274        **kw: Any,
275    ):
276        self.statement = statement
277        self._primary_table = statement.table
278
279        if disable_implicit_returning:
280            self._supports_implicit_returning = False
281
282        self.isinsert = True
283        if statement._select_names:
284            self._process_select_values(statement)
285        if statement._values is not None:
286            self._process_values(statement)
287        if statement._multi_values:
288            self._process_multi_values(statement)
289
290    @util.memoized_property
291    def _insert_col_keys(self) -> List[str]:
292        # this is also done in crud.py -> _key_getters_for_crud_column
293        return [
294            coercions.expect(roles.DMLColumnRole, col, as_key=True)
295            for col in self._dict_parameters or ()
296        ]
297
298    def _process_values(self, statement: ValuesBase) -> None:
299        if self._no_parameters:
300            self._has_multi_parameters = False
301            self._dict_parameters = statement._values
302            self._no_parameters = False
303        elif self._has_multi_parameters:
304            self._cant_mix_formats_error()
305
306    def _process_multi_values(self, statement: ValuesBase) -> None:
307        for parameters in statement._multi_values:
308            multi_parameters: List[MutableMapping[_DMLColumnElement, Any]] = [
309                (
310                    {
311                        c.key: value
312                        for c, value in zip(statement.table.c, parameter_set)
313                    }
314                    if isinstance(parameter_set, collections_abc.Sequence)
315                    else parameter_set
316                )
317                for parameter_set in parameters
318            ]
319
320            if self._no_parameters:
321                self._no_parameters = False
322                self._has_multi_parameters = True
323                self._multi_parameters = multi_parameters
324                self._dict_parameters = self._multi_parameters[0]
325            elif not self._has_multi_parameters:
326                self._cant_mix_formats_error()
327            else:
328                assert self._multi_parameters
329                self._multi_parameters.extend(multi_parameters)
330
331
332@CompileState.plugin_for("default", "update")
333class UpdateDMLState(DMLState):
334    isupdate = True
335
336    include_table_with_column_exprs = False
337
338    def __init__(self, statement: Update, compiler: SQLCompiler, **kw: Any):
339        self.statement = statement
340
341        self.isupdate = True
342        if statement._ordered_values is not None:
343            self._process_ordered_values(statement)
344        elif statement._values is not None:
345            self._process_values(statement)
346        elif statement._multi_values:
347            self._no_multi_values_supported(statement)
348        t, ef = self._make_extra_froms(statement)
349        self._primary_table = t
350        self._extra_froms = ef
351
352        self.is_multitable = mt = ef
353        self.include_table_with_column_exprs = bool(
354            mt and compiler.render_table_with_column_in_update_from
355        )
356
357    def _process_ordered_values(self, statement: ValuesBase) -> None:
358        parameters = statement._ordered_values
359
360        if self._no_parameters:
361            self._no_parameters = False
362            assert parameters is not None
363            self._dict_parameters = dict(parameters)
364            self._ordered_values = parameters
365            self._parameter_ordering = [key for key, value in parameters]
366        else:
367            raise exc.InvalidRequestError(
368                "Can only invoke ordered_values() once, and not mixed "
369                "with any other values() call"
370            )
371
372
373@CompileState.plugin_for("default", "delete")
374class DeleteDMLState(DMLState):
375    isdelete = True
376
377    def __init__(self, statement: Delete, compiler: SQLCompiler, **kw: Any):
378        self.statement = statement
379
380        self.isdelete = True
381        t, ef = self._make_extra_froms(statement)
382        self._primary_table = t
383        self._extra_froms = ef
384        self.is_multitable = ef
385
386
387class UpdateBase(
388    roles.DMLRole,
389    HasCTE,
390    HasCompileState,
391    DialectKWArgs,
392    HasPrefixes,
393    Generative,
394    ExecutableReturnsRows,
395    ClauseElement,
396):
397    """Form the base for ``INSERT``, ``UPDATE``, and ``DELETE`` statements."""
398
399    __visit_name__ = "update_base"
400
401    _hints: util.immutabledict[Tuple[_DMLTableElement, str], str] = (
402        util.EMPTY_DICT
403    )
404    named_with_column = False
405
406    _label_style: SelectLabelStyle = (
407        SelectLabelStyle.LABEL_STYLE_DISAMBIGUATE_ONLY
408    )
409    table: _DMLTableElement
410
411    _return_defaults = False
412    _return_defaults_columns: Optional[Tuple[_ColumnsClauseElement, ...]] = (
413        None
414    )
415    _supplemental_returning: Optional[Tuple[_ColumnsClauseElement, ...]] = None
416    _returning: Tuple[_ColumnsClauseElement, ...] = ()
417
418    is_dml = True
419
420    def _generate_fromclause_column_proxies(
421        self, fromclause: FromClause
422    ) -> None:
423        fromclause._columns._populate_separate_keys(
424            col._make_proxy(fromclause)
425            for col in self._all_selected_columns
426            if is_column_element(col)
427        )
428
429    def params(self, *arg: Any, **kw: Any) -> NoReturn:
430        """Set the parameters for the statement.
431
432        This method raises ``NotImplementedError`` on the base class,
433        and is overridden by :class:`.ValuesBase` to provide the
434        SET/VALUES clause of UPDATE and INSERT.
435
436        """
437        raise NotImplementedError(
438            "params() is not supported for INSERT/UPDATE/DELETE statements."
439            " To set the values for an INSERT or UPDATE statement, use"
440            " stmt.values(**parameters)."
441        )
442
443    @_generative
444    def with_dialect_options(self, **opt: Any) -> Self:
445        """Add dialect options to this INSERT/UPDATE/DELETE object.
446
447        e.g.::
448
449            upd = table.update().dialect_options(mysql_limit=10)
450
451        .. versionadded: 1.4 - this method supersedes the dialect options
452           associated with the constructor.
453
454
455        """
456        self._validate_dialect_kwargs(opt)
457        return self
458
459    @_generative
460    def return_defaults(
461        self,
462        *cols: _DMLColumnArgument,
463        supplemental_cols: Optional[Iterable[_DMLColumnArgument]] = None,
464        sort_by_parameter_order: bool = False,
465    ) -> Self:
466        """Make use of a :term:`RETURNING` clause for the purpose
467        of fetching server-side expressions and defaults, for supporting
468        backends only.
469
470        .. deepalchemy::
471
472            The :meth:`.UpdateBase.return_defaults` method is used by the ORM
473            for its internal work in fetching newly generated primary key
474            and server default values, in particular to provide the underyling
475            implementation of the :paramref:`_orm.Mapper.eager_defaults`
476            ORM feature as well as to allow RETURNING support with bulk
477            ORM inserts.  Its behavior is fairly idiosyncratic
478            and is not really intended for general use.  End users should
479            stick with using :meth:`.UpdateBase.returning` in order to
480            add RETURNING clauses to their INSERT, UPDATE and DELETE
481            statements.
482
483        Normally, a single row INSERT statement will automatically populate the
484        :attr:`.CursorResult.inserted_primary_key` attribute when executed,
485        which stores the primary key of the row that was just inserted in the
486        form of a :class:`.Row` object with column names as named tuple keys
487        (and the :attr:`.Row._mapping` view fully populated as well). The
488        dialect in use chooses the strategy to use in order to populate this
489        data; if it was generated using server-side defaults and / or SQL
490        expressions, dialect-specific approaches such as ``cursor.lastrowid``
491        or ``RETURNING`` are typically used to acquire the new primary key
492        value.
493
494        However, when the statement is modified by calling
495        :meth:`.UpdateBase.return_defaults` before executing the statement,
496        additional behaviors take place **only** for backends that support
497        RETURNING and for :class:`.Table` objects that maintain the
498        :paramref:`.Table.implicit_returning` parameter at its default value of
499        ``True``. In these cases, when the :class:`.CursorResult` is returned
500        from the statement's execution, not only will
501        :attr:`.CursorResult.inserted_primary_key` be populated as always, the
502        :attr:`.CursorResult.returned_defaults` attribute will also be
503        populated with a :class:`.Row` named-tuple representing the full range
504        of server generated
505        values from that single row, including values for any columns that
506        specify :paramref:`_schema.Column.server_default` or which make use of
507        :paramref:`_schema.Column.default` using a SQL expression.
508
509        When invoking INSERT statements with multiple rows using
510        :ref:`insertmanyvalues <engine_insertmanyvalues>`, the
511        :meth:`.UpdateBase.return_defaults` modifier will have the effect of
512        the :attr:`_engine.CursorResult.inserted_primary_key_rows` and
513        :attr:`_engine.CursorResult.returned_defaults_rows` attributes being
514        fully populated with lists of :class:`.Row` objects representing newly
515        inserted primary key values as well as newly inserted server generated
516        values for each row inserted. The
517        :attr:`.CursorResult.inserted_primary_key` and
518        :attr:`.CursorResult.returned_defaults` attributes will also continue
519        to be populated with the first row of these two collections.
520
521        If the backend does not support RETURNING or the :class:`.Table` in use
522        has disabled :paramref:`.Table.implicit_returning`, then no RETURNING
523        clause is added and no additional data is fetched, however the
524        INSERT, UPDATE or DELETE statement proceeds normally.
525
526        E.g.::
527
528            stmt = table.insert().values(data='newdata').return_defaults()
529
530            result = connection.execute(stmt)
531
532            server_created_at = result.returned_defaults['created_at']
533
534        When used against an UPDATE statement
535        :meth:`.UpdateBase.return_defaults` instead looks for columns that
536        include :paramref:`_schema.Column.onupdate` or
537        :paramref:`_schema.Column.server_onupdate` parameters assigned, when
538        constructing the columns that will be included in the RETURNING clause
539        by default if explicit columns were not specified. When used against a
540        DELETE statement, no columns are included in RETURNING by default, they
541        instead must be specified explicitly as there are no columns that
542        normally change values when a DELETE statement proceeds.
543
544        .. versionadded:: 2.0  :meth:`.UpdateBase.return_defaults` is supported
545           for DELETE statements also and has been moved from
546           :class:`.ValuesBase` to :class:`.UpdateBase`.
547
548        The :meth:`.UpdateBase.return_defaults` method is mutually exclusive
549        against the :meth:`.UpdateBase.returning` method and errors will be
550        raised during the SQL compilation process if both are used at the same
551        time on one statement. The RETURNING clause of the INSERT, UPDATE or
552        DELETE statement is therefore controlled by only one of these methods
553        at a time.
554
555        The :meth:`.UpdateBase.return_defaults` method differs from
556        :meth:`.UpdateBase.returning` in these ways:
557
558        1. :meth:`.UpdateBase.return_defaults` method causes the
559           :attr:`.CursorResult.returned_defaults` collection to be populated
560           with the first row from the RETURNING result. This attribute is not
561           populated when using :meth:`.UpdateBase.returning`.
562
563        2. :meth:`.UpdateBase.return_defaults` is compatible with existing
564           logic used to fetch auto-generated primary key values that are then
565           populated into the :attr:`.CursorResult.inserted_primary_key`
566           attribute. By contrast, using :meth:`.UpdateBase.returning` will
567           have the effect of the :attr:`.CursorResult.inserted_primary_key`
568           attribute being left unpopulated.
569
570        3. :meth:`.UpdateBase.return_defaults` can be called against any
571           backend. Backends that don't support RETURNING will skip the usage
572           of the feature, rather than raising an exception, *unless*
573           ``supplemental_cols`` is passed. The return value
574           of :attr:`_engine.CursorResult.returned_defaults` will be ``None``
575           for backends that don't support RETURNING or for which the target
576           :class:`.Table` sets :paramref:`.Table.implicit_returning` to
577           ``False``.
578
579        4. An INSERT statement invoked with executemany() is supported if the
580           backend database driver supports the
581           :ref:`insertmanyvalues <engine_insertmanyvalues>`
582           feature which is now supported by most SQLAlchemy-included backends.
583           When executemany is used, the
584           :attr:`_engine.CursorResult.returned_defaults_rows` and
585           :attr:`_engine.CursorResult.inserted_primary_key_rows` accessors
586           will return the inserted defaults and primary keys.
587
588           .. versionadded:: 1.4 Added
589              :attr:`_engine.CursorResult.returned_defaults_rows` and
590              :attr:`_engine.CursorResult.inserted_primary_key_rows` accessors.
591              In version 2.0, the underlying implementation which fetches and
592              populates the data for these attributes was generalized to be
593              supported by most backends, whereas in 1.4 they were only
594              supported by the ``psycopg2`` driver.
595
596
597        :param cols: optional list of column key names or
598         :class:`_schema.Column` that acts as a filter for those columns that
599         will be fetched.
600        :param supplemental_cols: optional list of RETURNING expressions,
601          in the same form as one would pass to the
602          :meth:`.UpdateBase.returning` method. When present, the additional
603          columns will be included in the RETURNING clause, and the
604          :class:`.CursorResult` object will be "rewound" when returned, so
605          that methods like :meth:`.CursorResult.all` will return new rows
606          mostly as though the statement used :meth:`.UpdateBase.returning`
607          directly. However, unlike when using :meth:`.UpdateBase.returning`
608          directly, the **order of the columns is undefined**, so can only be
609          targeted using names or :attr:`.Row._mapping` keys; they cannot
610          reliably be targeted positionally.
611
612          .. versionadded:: 2.0
613
614        :param sort_by_parameter_order: for a batch INSERT that is being
615         executed against multiple parameter sets, organize the results of
616         RETURNING so that the returned rows correspond to the order of
617         parameter sets passed in.  This applies only to an :term:`executemany`
618         execution for supporting dialects and typically makes use of the
619         :term:`insertmanyvalues` feature.
620
621         .. versionadded:: 2.0.10
622
623         .. seealso::
624
625            :ref:`engine_insertmanyvalues_returning_order` - background on
626            sorting of RETURNING rows for bulk INSERT
627
628        .. seealso::
629
630            :meth:`.UpdateBase.returning`
631
632            :attr:`_engine.CursorResult.returned_defaults`
633
634            :attr:`_engine.CursorResult.returned_defaults_rows`
635
636            :attr:`_engine.CursorResult.inserted_primary_key`
637
638            :attr:`_engine.CursorResult.inserted_primary_key_rows`
639
640        """
641
642        if self._return_defaults:
643            # note _return_defaults_columns = () means return all columns,
644            # so if we have been here before, only update collection if there
645            # are columns in the collection
646            if self._return_defaults_columns and cols:
647                self._return_defaults_columns = tuple(
648                    util.OrderedSet(self._return_defaults_columns).union(
649                        coercions.expect(roles.ColumnsClauseRole, c)
650                        for c in cols
651                    )
652                )
653            else:
654                # set for all columns
655                self._return_defaults_columns = ()
656        else:
657            self._return_defaults_columns = tuple(
658                coercions.expect(roles.ColumnsClauseRole, c) for c in cols
659            )
660        self._return_defaults = True
661        if sort_by_parameter_order:
662            if not self.is_insert:
663                raise exc.ArgumentError(
664                    "The 'sort_by_parameter_order' argument to "
665                    "return_defaults() only applies to INSERT statements"
666                )
667            self._sort_by_parameter_order = True
668        if supplemental_cols:
669            # uniquifying while also maintaining order (the maintain of order
670            # is for test suites but also for vertical splicing
671            supplemental_col_tup = (
672                coercions.expect(roles.ColumnsClauseRole, c)
673                for c in supplemental_cols
674            )
675
676            if self._supplemental_returning is None:
677                self._supplemental_returning = tuple(
678                    util.unique_list(supplemental_col_tup)
679                )
680            else:
681                self._supplemental_returning = tuple(
682                    util.unique_list(
683                        self._supplemental_returning
684                        + tuple(supplemental_col_tup)
685                    )
686                )
687
688        return self
689
690    @_generative
691    def returning(
692        self,
693        *cols: _ColumnsClauseArgument[Any],
694        sort_by_parameter_order: bool = False,
695        **__kw: Any,
696    ) -> UpdateBase:
697        r"""Add a :term:`RETURNING` or equivalent clause to this statement.
698
699        e.g.:
700
701        .. sourcecode:: pycon+sql
702
703            >>> stmt = (
704            ...     table.update()
705            ...     .where(table.c.data == "value")
706            ...     .values(status="X")
707            ...     .returning(table.c.server_flag, table.c.updated_timestamp)
708            ... )
709            >>> print(stmt)
710            {printsql}UPDATE some_table SET status=:status
711            WHERE some_table.data = :data_1
712            RETURNING some_table.server_flag, some_table.updated_timestamp
713
714        The method may be invoked multiple times to add new entries to the
715        list of expressions to be returned.
716
717        .. versionadded:: 1.4.0b2 The method may be invoked multiple times to
718         add new entries to the list of expressions to be returned.
719
720        The given collection of column expressions should be derived from the
721        table that is the target of the INSERT, UPDATE, or DELETE.  While
722        :class:`_schema.Column` objects are typical, the elements can also be
723        expressions:
724
725        .. sourcecode:: pycon+sql
726
727            >>> stmt = table.insert().returning(
728            ...     (table.c.first_name + " " + table.c.last_name).label("fullname")
729            ... )
730            >>> print(stmt)
731            {printsql}INSERT INTO some_table (first_name, last_name)
732            VALUES (:first_name, :last_name)
733            RETURNING some_table.first_name || :first_name_1 || some_table.last_name AS fullname
734
735        Upon compilation, a RETURNING clause, or database equivalent,
736        will be rendered within the statement.   For INSERT and UPDATE,
737        the values are the newly inserted/updated values.  For DELETE,
738        the values are those of the rows which were deleted.
739
740        Upon execution, the values of the columns to be returned are made
741        available via the result set and can be iterated using
742        :meth:`_engine.CursorResult.fetchone` and similar.
743        For DBAPIs which do not
744        natively support returning values (i.e. cx_oracle), SQLAlchemy will
745        approximate this behavior at the result level so that a reasonable
746        amount of behavioral neutrality is provided.
747
748        Note that not all databases/DBAPIs
749        support RETURNING.   For those backends with no support,
750        an exception is raised upon compilation and/or execution.
751        For those who do support it, the functionality across backends
752        varies greatly, including restrictions on executemany()
753        and other statements which return multiple rows. Please
754        read the documentation notes for the database in use in
755        order to determine the availability of RETURNING.
756
757        :param \*cols: series of columns, SQL expressions, or whole tables
758         entities to be returned.
759        :param sort_by_parameter_order: for a batch INSERT that is being
760         executed against multiple parameter sets, organize the results of
761         RETURNING so that the returned rows correspond to the order of
762         parameter sets passed in.  This applies only to an :term:`executemany`
763         execution for supporting dialects and typically makes use of the
764         :term:`insertmanyvalues` feature.
765
766         .. versionadded:: 2.0.10
767
768         .. seealso::
769
770            :ref:`engine_insertmanyvalues_returning_order` - background on
771            sorting of RETURNING rows for bulk INSERT (Core level discussion)
772
773            :ref:`orm_queryguide_bulk_insert_returning_ordered` - example of
774            use with :ref:`orm_queryguide_bulk_insert` (ORM level discussion)
775
776        .. seealso::
777
778          :meth:`.UpdateBase.return_defaults` - an alternative method tailored
779          towards efficient fetching of server-side defaults and triggers
780          for single-row INSERTs or UPDATEs.
781
782          :ref:`tutorial_insert_returning` - in the :ref:`unified_tutorial`
783
784        """  # noqa: E501
785        if __kw:
786            raise _unexpected_kw("UpdateBase.returning()", __kw)
787        if self._return_defaults:
788            raise exc.InvalidRequestError(
789                "return_defaults() is already configured on this statement"
790            )
791        self._returning += tuple(
792            coercions.expect(roles.ColumnsClauseRole, c) for c in cols
793        )
794        if sort_by_parameter_order:
795            if not self.is_insert:
796                raise exc.ArgumentError(
797                    "The 'sort_by_parameter_order' argument to returning() "
798                    "only applies to INSERT statements"
799                )
800            self._sort_by_parameter_order = True
801        return self
802
803    def corresponding_column(
804        self, column: KeyedColumnElement[Any], require_embedded: bool = False
805    ) -> Optional[ColumnElement[Any]]:
806        return self.exported_columns.corresponding_column(
807            column, require_embedded=require_embedded
808        )
809
810    @util.ro_memoized_property
811    def _all_selected_columns(self) -> _SelectIterable:
812        return [c for c in _select_iterables(self._returning)]
813
814    @util.ro_memoized_property
815    def exported_columns(
816        self,
817    ) -> ReadOnlyColumnCollection[Optional[str], ColumnElement[Any]]:
818        """Return the RETURNING columns as a column collection for this
819        statement.
820
821        .. versionadded:: 1.4
822
823        """
824        return ColumnCollection(
825            (c.key, c)
826            for c in self._all_selected_columns
827            if is_column_element(c)
828        ).as_readonly()
829
830    @_generative
831    def with_hint(
832        self,
833        text: str,
834        selectable: Optional[_DMLTableArgument] = None,
835        dialect_name: str = "*",
836    ) -> Self:
837        """Add a table hint for a single table to this
838        INSERT/UPDATE/DELETE statement.
839
840        .. note::
841
842         :meth:`.UpdateBase.with_hint` currently applies only to
843         Microsoft SQL Server.  For MySQL INSERT/UPDATE/DELETE hints, use
844         :meth:`.UpdateBase.prefix_with`.
845
846        The text of the hint is rendered in the appropriate
847        location for the database backend in use, relative
848        to the :class:`_schema.Table` that is the subject of this
849        statement, or optionally to that of the given
850        :class:`_schema.Table` passed as the ``selectable`` argument.
851
852        The ``dialect_name`` option will limit the rendering of a particular
853        hint to a particular backend. Such as, to add a hint
854        that only takes effect for SQL Server::
855
856            mytable.insert().with_hint("WITH (PAGLOCK)", dialect_name="mssql")
857
858        :param text: Text of the hint.
859        :param selectable: optional :class:`_schema.Table` that specifies
860         an element of the FROM clause within an UPDATE or DELETE
861         to be the subject of the hint - applies only to certain backends.
862        :param dialect_name: defaults to ``*``, if specified as the name
863         of a particular dialect, will apply these hints only when
864         that dialect is in use.
865        """
866        if selectable is None:
867            selectable = self.table
868        else:
869            selectable = coercions.expect(roles.DMLTableRole, selectable)
870        self._hints = self._hints.union({(selectable, dialect_name): text})
871        return self
872
873    @property
874    def entity_description(self) -> Dict[str, Any]:
875        """Return a :term:`plugin-enabled` description of the table and/or
876        entity which this DML construct is operating against.
877
878        This attribute is generally useful when using the ORM, as an
879        extended structure which includes information about mapped
880        entities is returned.  The section :ref:`queryguide_inspection`
881        contains more background.
882
883        For a Core statement, the structure returned by this accessor
884        is derived from the :attr:`.UpdateBase.table` attribute, and
885        refers to the :class:`.Table` being inserted, updated, or deleted::
886
887            >>> stmt = insert(user_table)
888            >>> stmt.entity_description
889            {
890                "name": "user_table",
891                "table": Table("user_table", ...)
892            }
893
894        .. versionadded:: 1.4.33
895
896        .. seealso::
897
898            :attr:`.UpdateBase.returning_column_descriptions`
899
900            :attr:`.Select.column_descriptions` - entity information for
901            a :func:`.select` construct
902
903            :ref:`queryguide_inspection` - ORM background
904
905        """
906        meth = DMLState.get_plugin_class(self).get_entity_description
907        return meth(self)
908
909    @property
910    def returning_column_descriptions(self) -> List[Dict[str, Any]]:
911        """Return a :term:`plugin-enabled` description of the columns
912        which this DML construct is RETURNING against, in other words
913        the expressions established as part of :meth:`.UpdateBase.returning`.
914
915        This attribute is generally useful when using the ORM, as an
916        extended structure which includes information about mapped
917        entities is returned.  The section :ref:`queryguide_inspection`
918        contains more background.
919
920        For a Core statement, the structure returned by this accessor is
921        derived from the same objects that are returned by the
922        :attr:`.UpdateBase.exported_columns` accessor::
923
924            >>> stmt = insert(user_table).returning(user_table.c.id, user_table.c.name)
925            >>> stmt.entity_description
926            [
927                {
928                    "name": "id",
929                    "type": Integer,
930                    "expr": Column("id", Integer(), table=<user>, ...)
931                },
932                {
933                    "name": "name",
934                    "type": String(),
935                    "expr": Column("name", String(), table=<user>, ...)
936                },
937            ]
938
939        .. versionadded:: 1.4.33
940
941        .. seealso::
942
943            :attr:`.UpdateBase.entity_description`
944
945            :attr:`.Select.column_descriptions` - entity information for
946            a :func:`.select` construct
947
948            :ref:`queryguide_inspection` - ORM background
949
950        """  # noqa: E501
951        meth = DMLState.get_plugin_class(
952            self
953        ).get_returning_column_descriptions
954        return meth(self)
955
956
957class ValuesBase(UpdateBase):
958    """Supplies support for :meth:`.ValuesBase.values` to
959    INSERT and UPDATE constructs."""
960
961    __visit_name__ = "values_base"
962
963    _supports_multi_parameters = False
964
965    select: Optional[Select[Any]] = None
966    """SELECT statement for INSERT .. FROM SELECT"""
967
968    _post_values_clause: Optional[ClauseElement] = None
969    """used by extensions to Insert etc. to add additional syntacitcal
970    constructs, e.g. ON CONFLICT etc."""
971
972    _values: Optional[util.immutabledict[_DMLColumnElement, Any]] = None
973    _multi_values: Tuple[
974        Union[
975            Sequence[Dict[_DMLColumnElement, Any]],
976            Sequence[Sequence[Any]],
977        ],
978        ...,
979    ] = ()
980
981    _ordered_values: Optional[List[Tuple[_DMLColumnElement, Any]]] = None
982
983    _select_names: Optional[List[str]] = None
984    _inline: bool = False
985
986    def __init__(self, table: _DMLTableArgument):
987        self.table = coercions.expect(
988            roles.DMLTableRole, table, apply_propagate_attrs=self
989        )
990
991    @_generative
992    @_exclusive_against(
993        "_select_names",
994        "_ordered_values",
995        msgs={
996            "_select_names": "This construct already inserts from a SELECT",
997            "_ordered_values": "This statement already has ordered "
998            "values present",
999        },
1000    )
1001    def values(
1002        self,
1003        *args: Union[
1004            _DMLColumnKeyMapping[Any],
1005            Sequence[Any],
1006        ],
1007        **kwargs: Any,
1008    ) -> Self:
1009        r"""Specify a fixed VALUES clause for an INSERT statement, or the SET
1010        clause for an UPDATE.
1011
1012        Note that the :class:`_expression.Insert` and
1013        :class:`_expression.Update`
1014        constructs support
1015        per-execution time formatting of the VALUES and/or SET clauses,
1016        based on the arguments passed to :meth:`_engine.Connection.execute`.
1017        However, the :meth:`.ValuesBase.values` method can be used to "fix" a
1018        particular set of parameters into the statement.
1019
1020        Multiple calls to :meth:`.ValuesBase.values` will produce a new
1021        construct, each one with the parameter list modified to include
1022        the new parameters sent.  In the typical case of a single
1023        dictionary of parameters, the newly passed keys will replace
1024        the same keys in the previous construct.  In the case of a list-based
1025        "multiple values" construct, each new list of values is extended
1026        onto the existing list of values.
1027
1028        :param \**kwargs: key value pairs representing the string key
1029          of a :class:`_schema.Column`
1030          mapped to the value to be rendered into the
1031          VALUES or SET clause::
1032
1033                users.insert().values(name="some name")
1034
1035                users.update().where(users.c.id==5).values(name="some name")
1036
1037        :param \*args: As an alternative to passing key/value parameters,
1038         a dictionary, tuple, or list of dictionaries or tuples can be passed
1039         as a single positional argument in order to form the VALUES or
1040         SET clause of the statement.  The forms that are accepted vary
1041         based on whether this is an :class:`_expression.Insert` or an
1042         :class:`_expression.Update` construct.
1043
1044         For either an :class:`_expression.Insert` or
1045         :class:`_expression.Update`
1046         construct, a single dictionary can be passed, which works the same as
1047         that of the kwargs form::
1048
1049            users.insert().values({"name": "some name"})
1050
1051            users.update().values({"name": "some new name"})
1052
1053         Also for either form but more typically for the
1054         :class:`_expression.Insert` construct, a tuple that contains an
1055         entry for every column in the table is also accepted::
1056
1057            users.insert().values((5, "some name"))
1058
1059         The :class:`_expression.Insert` construct also supports being
1060         passed a list of dictionaries or full-table-tuples, which on the
1061         server will render the less common SQL syntax of "multiple values" -
1062         this syntax is supported on backends such as SQLite, PostgreSQL,
1063         MySQL, but not necessarily others::
1064
1065            users.insert().values([
1066                                {"name": "some name"},
1067                                {"name": "some other name"},
1068                                {"name": "yet another name"},
1069                            ])
1070
1071         The above form would render a multiple VALUES statement similar to::
1072
1073                INSERT INTO users (name) VALUES
1074                                (:name_1),
1075                                (:name_2),
1076                                (:name_3)
1077
1078         It is essential to note that **passing multiple values is
1079         NOT the same as using traditional executemany() form**.  The above
1080         syntax is a **special** syntax not typically used.  To emit an
1081         INSERT statement against multiple rows, the normal method is
1082         to pass a multiple values list to the
1083         :meth:`_engine.Connection.execute`
1084         method, which is supported by all database backends and is generally
1085         more efficient for a very large number of parameters.
1086
1087           .. seealso::
1088
1089               :ref:`tutorial_multiple_parameters` - an introduction to
1090               the traditional Core method of multiple parameter set
1091               invocation for INSERTs and other statements.
1092
1093          The UPDATE construct also supports rendering the SET parameters
1094          in a specific order.  For this feature refer to the
1095          :meth:`_expression.Update.ordered_values` method.
1096
1097           .. seealso::
1098
1099              :meth:`_expression.Update.ordered_values`
1100
1101
1102        """
1103        if args:
1104            # positional case.  this is currently expensive.   we don't
1105            # yet have positional-only args so we have to check the length.
1106            # then we need to check multiparams vs. single dictionary.
1107            # since the parameter format is needed in order to determine
1108            # a cache key, we need to determine this up front.
1109            arg = args[0]
1110
1111            if kwargs:
1112                raise exc.ArgumentError(
1113                    "Can't pass positional and kwargs to values() "
1114                    "simultaneously"
1115                )
1116            elif len(args) > 1:
1117                raise exc.ArgumentError(
1118                    "Only a single dictionary/tuple or list of "
1119                    "dictionaries/tuples is accepted positionally."
1120                )
1121
1122            elif isinstance(arg, collections_abc.Sequence):
1123                if arg and isinstance(arg[0], dict):
1124                    multi_kv_generator = DMLState.get_plugin_class(
1125                        self
1126                    )._get_multi_crud_kv_pairs
1127                    self._multi_values += (multi_kv_generator(self, arg),)
1128                    return self
1129
1130                if arg and isinstance(arg[0], (list, tuple)):
1131                    self._multi_values += (arg,)
1132                    return self
1133
1134                if TYPE_CHECKING:
1135                    # crud.py raises during compilation if this is not the
1136                    # case
1137                    assert isinstance(self, Insert)
1138
1139                # tuple values
1140                arg = {c.key: value for c, value in zip(self.table.c, arg)}
1141
1142        else:
1143            # kwarg path.  this is the most common path for non-multi-params
1144            # so this is fairly quick.
1145            arg = cast("Dict[_DMLColumnArgument, Any]", kwargs)
1146            if args:
1147                raise exc.ArgumentError(
1148                    "Only a single dictionary/tuple or list of "
1149                    "dictionaries/tuples is accepted positionally."
1150                )
1151
1152        # for top level values(), convert literals to anonymous bound
1153        # parameters at statement construction time, so that these values can
1154        # participate in the cache key process like any other ClauseElement.
1155        # crud.py now intercepts bound parameters with unique=True from here
1156        # and ensures they get the "crud"-style name when rendered.
1157
1158        kv_generator = DMLState.get_plugin_class(self)._get_crud_kv_pairs
1159        coerced_arg = dict(kv_generator(self, arg.items(), True))
1160        if self._values:
1161            self._values = self._values.union(coerced_arg)
1162        else:
1163            self._values = util.immutabledict(coerced_arg)
1164        return self
1165
1166
1167class Insert(ValuesBase):
1168    """Represent an INSERT construct.
1169
1170    The :class:`_expression.Insert` object is created using the
1171    :func:`_expression.insert()` function.
1172
1173    """
1174
1175    __visit_name__ = "insert"
1176
1177    _supports_multi_parameters = True
1178
1179    select = None
1180    include_insert_from_select_defaults = False
1181
1182    _sort_by_parameter_order: bool = False
1183
1184    is_insert = True
1185
1186    table: TableClause
1187
1188    _traverse_internals = (
1189        [
1190            ("table", InternalTraversal.dp_clauseelement),
1191            ("_inline", InternalTraversal.dp_boolean),
1192            ("_select_names", InternalTraversal.dp_string_list),
1193            ("_values", InternalTraversal.dp_dml_values),
1194            ("_multi_values", InternalTraversal.dp_dml_multi_values),
1195            ("select", InternalTraversal.dp_clauseelement),
1196            ("_post_values_clause", InternalTraversal.dp_clauseelement),
1197            ("_returning", InternalTraversal.dp_clauseelement_tuple),
1198            ("_hints", InternalTraversal.dp_table_hint_list),
1199            ("_return_defaults", InternalTraversal.dp_boolean),
1200            (

Showing the first 1,200 of 1818 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai