Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
crud.py1753 linesDownload Raw Back to sql
1# sql/crud.py
2# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7# mypy: allow-untyped-defs, allow-untyped-calls
8
9"""Functions used by compiler.py to determine the parameters rendered
10within INSERT and UPDATE statements.
11
12"""
13from __future__ import annotations
14
15import functools
16import operator
17from typing import Any
18from typing import Callable
19from typing import cast
20from typing import Dict
21from typing import Iterable
22from typing import List
23from typing import MutableMapping
24from typing import NamedTuple
25from typing import Optional
26from typing import overload
27from typing import Sequence
28from typing import Set
29from typing import Tuple
30from typing import TYPE_CHECKING
31from typing import Union
32
33from . import coercions
34from . import dml
35from . import elements
36from . import roles
37from .base import _DefaultDescriptionTuple
38from .dml import isinsert as _compile_state_isinsert
39from .elements import ColumnClause
40from .schema import default_is_clause_element
41from .schema import default_is_sequence
42from .selectable import Select
43from .selectable import TableClause
44from .. import exc
45from .. import util
46from ..util.typing import Literal
47
48if TYPE_CHECKING:
49    from .compiler import _BindNameForColProtocol
50    from .compiler import SQLCompiler
51    from .dml import _DMLColumnElement
52    from .dml import DMLState
53    from .dml import ValuesBase
54    from .elements import ColumnElement
55    from .elements import KeyedColumnElement
56    from .schema import _SQLExprDefault
57    from .schema import Column
58
59REQUIRED = util.symbol(
60    "REQUIRED",
61    """
62Placeholder for the value within a :class:`.BindParameter`
63which is required to be present when the statement is passed
64to :meth:`_engine.Connection.execute`.
65
66This symbol is typically used when a :func:`_expression.insert`
67or :func:`_expression.update` statement is compiled without parameter
68values present.
69
70""",
71)
72
73
74def _as_dml_column(c: ColumnElement[Any]) -> ColumnClause[Any]:
75    if not isinstance(c, ColumnClause):
76        raise exc.CompileError(
77            f"Can't create DML statement against column expression {c!r}"
78        )
79    return c
80
81
82_CrudParamElement = Tuple[
83    "ColumnElement[Any]",
84    str,  # column name
85    Optional[
86        Union[str, "_SQLExprDefault"]
87    ],  # bound parameter string or SQL expression to apply
88    Iterable[str],
89]
90_CrudParamElementStr = Tuple[
91    "KeyedColumnElement[Any]",
92    str,  # column name
93    str,  # bound parameter string
94    Iterable[str],
95]
96_CrudParamElementSQLExpr = Tuple[
97    "ColumnClause[Any]",
98    str,
99    "_SQLExprDefault",  # SQL expression to apply
100    Iterable[str],
101]
102
103_CrudParamSequence = List[_CrudParamElement]
104
105
106class _CrudParams(NamedTuple):
107    single_params: List[_CrudParamElementStr]
108    all_multi_params: List[Sequence[_CrudParamElementStr]]
109    is_default_metavalue_only: bool = False
110    use_insertmanyvalues: bool = False
111    use_sentinel_columns: Optional[Sequence[Column[Any]]] = None
112
113
114def _get_crud_params(
115    compiler: SQLCompiler,
116    stmt: ValuesBase,
117    compile_state: DMLState,
118    toplevel: bool,
119    **kw: Any,
120) -> _CrudParams:
121    """create a set of tuples representing column/string pairs for use
122    in an INSERT or UPDATE statement.
123
124    Also generates the Compiled object's postfetch, prefetch, and
125    returning column collections, used for default handling and ultimately
126    populating the CursorResult's prefetch_cols() and postfetch_cols()
127    collections.
128
129    """
130
131    # note: the _get_crud_params() system was written with the notion in mind
132    # that INSERT, UPDATE, DELETE are always the top level statement and
133    # that there is only one of them.  With the addition of CTEs that can
134    # make use of DML, this assumption is no longer accurate; the DML
135    # statement is not necessarily the top-level "row returning" thing
136    # and it is also theoretically possible (fortunately nobody has asked yet)
137    # to have a single statement with multiple DMLs inside of it via CTEs.
138
139    # the current _get_crud_params() design doesn't accommodate these cases
140    # right now.  It "just works" for a CTE that has a single DML inside of
141    # it, and for a CTE with multiple DML, it's not clear what would happen.
142
143    # overall, the "compiler.XYZ" collections here would need to be in a
144    # per-DML structure of some kind, and DefaultDialect would need to
145    # navigate these collections on a per-statement basis, with additional
146    # emphasis on the "toplevel returning data" statement.  However we
147    # still need to run through _get_crud_params() for all DML as we have
148    # Python / SQL generated column defaults that need to be rendered.
149
150    # if there is user need for this kind of thing, it's likely a post 2.0
151    # kind of change as it would require deep changes to DefaultDialect
152    # as well as here.
153
154    compiler.postfetch = []
155    compiler.insert_prefetch = []
156    compiler.update_prefetch = []
157    compiler.implicit_returning = []
158
159    visiting_cte = kw.get("visiting_cte", None)
160    if visiting_cte is not None:
161        # for insert -> CTE -> insert, don't populate an incoming
162        # _crud_accumulate_bind_names collection; the INSERT we process here
163        # will not be inline within the VALUES of the enclosing INSERT as the
164        # CTE is placed on the outside.  See issue #9173
165        kw.pop("accumulate_bind_names", None)
166    assert (
167        "accumulate_bind_names" not in kw
168    ), "Don't know how to handle insert within insert without a CTE"
169
170    # getters - these are normally just column.key,
171    # but in the case of mysql multi-table update, the rules for
172    # .key must conditionally take tablename into account
173    (
174        _column_as_key,
175        _getattr_col_key,
176        _col_bind_name,
177    ) = _key_getters_for_crud_column(compiler, stmt, compile_state)
178
179    compiler._get_bind_name_for_col = _col_bind_name
180
181    if stmt._returning and stmt._return_defaults:
182        raise exc.CompileError(
183            "Can't compile statement that includes returning() and "
184            "return_defaults() simultaneously"
185        )
186
187    if compile_state.isdelete:
188        _setup_delete_return_defaults(
189            compiler,
190            stmt,
191            compile_state,
192            (),
193            _getattr_col_key,
194            _column_as_key,
195            _col_bind_name,
196            (),
197            (),
198            toplevel,
199            kw,
200        )
201        return _CrudParams([], [])
202
203    # no parameters in the statement, no parameters in the
204    # compiled params - return binds for all columns
205    if compiler.column_keys is None and compile_state._no_parameters:
206        return _CrudParams(
207            [
208                (
209                    c,
210                    compiler.preparer.format_column(c),
211                    _create_bind_param(compiler, c, None, required=True),
212                    (c.key,),
213                )
214                for c in stmt.table.columns
215                if not c._omit_from_statements
216            ],
217            [],
218        )
219
220    stmt_parameter_tuples: Optional[
221        List[Tuple[Union[str, ColumnClause[Any]], Any]]
222    ]
223    spd: Optional[MutableMapping[_DMLColumnElement, Any]]
224
225    if (
226        _compile_state_isinsert(compile_state)
227        and compile_state._has_multi_parameters
228    ):
229        mp = compile_state._multi_parameters
230        assert mp is not None
231        spd = mp[0]
232        stmt_parameter_tuples = list(spd.items())
233        spd_str_key = {_column_as_key(key) for key in spd}
234    elif compile_state._ordered_values:
235        spd = compile_state._dict_parameters
236        stmt_parameter_tuples = compile_state._ordered_values
237        assert spd is not None
238        spd_str_key = {_column_as_key(key) for key in spd}
239    elif compile_state._dict_parameters:
240        spd = compile_state._dict_parameters
241        stmt_parameter_tuples = list(spd.items())
242        spd_str_key = {_column_as_key(key) for key in spd}
243    else:
244        stmt_parameter_tuples = spd_str_key = None
245
246    # if we have statement parameters - set defaults in the
247    # compiled params
248    if compiler.column_keys is None:
249        parameters = {}
250    elif stmt_parameter_tuples:
251        assert spd_str_key is not None
252        parameters = {
253            _column_as_key(key): REQUIRED
254            for key in compiler.column_keys
255            if key not in spd_str_key
256        }
257    else:
258        parameters = {
259            _column_as_key(key): REQUIRED for key in compiler.column_keys
260        }
261
262    # create a list of column assignment clauses as tuples
263    values: List[_CrudParamElementStr] = []
264
265    if stmt_parameter_tuples is not None:
266        _get_stmt_parameter_tuples_params(
267            compiler,
268            compile_state,
269            parameters,
270            stmt_parameter_tuples,
271            _column_as_key,
272            values,
273            kw,
274        )
275
276    check_columns: Dict[str, ColumnClause[Any]] = {}
277
278    # special logic that only occurs for multi-table UPDATE
279    # statements
280    if dml.isupdate(compile_state) and compile_state.is_multitable:
281        _get_update_multitable_params(
282            compiler,
283            stmt,
284            compile_state,
285            stmt_parameter_tuples,
286            check_columns,
287            _col_bind_name,
288            _getattr_col_key,
289            values,
290            kw,
291        )
292
293    if _compile_state_isinsert(compile_state) and stmt._select_names:
294        # is an insert from select, is not a multiparams
295
296        assert not compile_state._has_multi_parameters
297
298        _scan_insert_from_select_cols(
299            compiler,
300            stmt,
301            compile_state,
302            parameters,
303            _getattr_col_key,
304            _column_as_key,
305            _col_bind_name,
306            check_columns,
307            values,
308            toplevel,
309            kw,
310        )
311        use_insertmanyvalues = False
312        use_sentinel_columns = None
313    else:
314        use_insertmanyvalues, use_sentinel_columns = _scan_cols(
315            compiler,
316            stmt,
317            compile_state,
318            parameters,
319            _getattr_col_key,
320            _column_as_key,
321            _col_bind_name,
322            check_columns,
323            values,
324            toplevel,
325            kw,
326        )
327
328    if parameters and stmt_parameter_tuples:
329        check = (
330            set(parameters)
331            .intersection(_column_as_key(k) for k, v in stmt_parameter_tuples)
332            .difference(check_columns)
333        )
334        if check:
335
336            if dml.isupdate(compile_state):
337                tables_mentioned = set(
338                    c.table
339                    for c, v in stmt_parameter_tuples
340                    if isinstance(c, ColumnClause) and c.table is not None
341                ).difference([compile_state.dml_table])
342
343                multi_not_in_from = tables_mentioned.difference(
344                    compile_state._extra_froms
345                )
346
347                if tables_mentioned and (
348                    not compile_state.is_multitable
349                    or not compiler.render_table_with_column_in_update_from
350                ):
351                    if not compiler.render_table_with_column_in_update_from:
352                        preamble = (
353                            "Backend does not support additional "
354                            "tables in the SET clause"
355                        )
356                    else:
357                        preamble = (
358                            "Statement is not a multi-table UPDATE statement"
359                        )
360
361                    raise exc.CompileError(
362                        f"{preamble}; cannot "
363                        f"""include columns from table(s) {
364                            ", ".join(f"'{t.description}'"
365                                      for t in tables_mentioned)
366                        } in SET clause"""
367                    )
368
369                elif multi_not_in_from:
370                    assert compiler.render_table_with_column_in_update_from
371                    raise exc.CompileError(
372                        f"Multi-table UPDATE statement does not include "
373                        "table(s) "
374                        f"""{
375                            ", ".join(
376                                f"'{t.description}'" for
377                                t in multi_not_in_from)
378                        }"""
379                    )
380
381            raise exc.CompileError(
382                "Unconsumed column names: %s"
383                % (", ".join("%s" % (c,) for c in check))
384            )
385
386    is_default_metavalue_only = False
387
388    if (
389        _compile_state_isinsert(compile_state)
390        and compile_state._has_multi_parameters
391    ):
392        # is a multiparams, is not an insert from a select
393        assert not stmt._select_names
394        multi_extended_values = _extend_values_for_multiparams(
395            compiler,
396            stmt,
397            compile_state,
398            cast(
399                "Sequence[_CrudParamElementStr]",
400                values,
401            ),
402            cast("Callable[..., str]", _column_as_key),
403            kw,
404        )
405        return _CrudParams(values, multi_extended_values)
406    elif (
407        not values
408        and compiler.for_executemany
409        and compiler.dialect.supports_default_metavalue
410    ):
411        # convert an "INSERT DEFAULT VALUES"
412        # into INSERT (firstcol) VALUES (DEFAULT) which can be turned
413        # into an in-place multi values.  This supports
414        # insert_executemany_returning mode :)
415        values = [
416            (
417                _as_dml_column(stmt.table.columns[0]),
418                compiler.preparer.format_column(stmt.table.columns[0]),
419                compiler.dialect.default_metavalue_token,
420                (),
421            )
422        ]
423        is_default_metavalue_only = True
424
425    return _CrudParams(
426        values,
427        [],
428        is_default_metavalue_only=is_default_metavalue_only,
429        use_insertmanyvalues=use_insertmanyvalues,
430        use_sentinel_columns=use_sentinel_columns,
431    )
432
433
434@overload
435def _create_bind_param(
436    compiler: SQLCompiler,
437    col: ColumnElement[Any],
438    value: Any,
439    process: Literal[True] = ...,
440    required: bool = False,
441    name: Optional[str] = None,
442    force_anonymous: bool = False,
443    **kw: Any,
444) -> str: ...
445
446
447@overload
448def _create_bind_param(
449    compiler: SQLCompiler,
450    col: ColumnElement[Any],
451    value: Any,
452    **kw: Any,
453) -> str: ...
454
455
456def _create_bind_param(
457    compiler: SQLCompiler,
458    col: ColumnElement[Any],
459    value: Any,
460    process: bool = True,
461    required: bool = False,
462    name: Optional[str] = None,
463    force_anonymous: bool = False,
464    **kw: Any,
465) -> Union[str, elements.BindParameter[Any]]:
466    if force_anonymous:
467        name = None
468    elif name is None:
469        name = col.key
470
471    bindparam = elements.BindParameter(
472        name, value, type_=col.type, required=required
473    )
474    bindparam._is_crud = True
475    if process:
476        return bindparam._compiler_dispatch(compiler, **kw)
477    else:
478        return bindparam
479
480
481def _handle_values_anonymous_param(compiler, col, value, name, **kw):
482    # the insert() and update() constructs as of 1.4 will now produce anonymous
483    # bindparam() objects in the values() collections up front when given plain
484    # literal values.  This is so that cache key behaviors, which need to
485    # produce bound parameters in deterministic order without invoking any
486    # compilation here, can be applied to these constructs when they include
487    # values() (but not yet multi-values, which are not included in caching
488    # right now).
489    #
490    # in order to produce the desired "crud" style name for these parameters,
491    # which will also be targetable in engine/default.py through the usual
492    # conventions, apply our desired name to these unique parameters by
493    # populating the compiler truncated names cache with the desired name,
494    # rather than having
495    # compiler.visit_bindparam()->compiler._truncated_identifier make up a
496    # name.  Saves on call counts also.
497
498    # for INSERT/UPDATE that's a CTE, we don't need names to match to
499    # external parameters and these would also conflict in the case where
500    # multiple insert/update are combined together using CTEs
501    is_cte = "visiting_cte" in kw
502
503    if (
504        not is_cte
505        and value.unique
506        and isinstance(value.key, elements._truncated_label)
507    ):
508        compiler.truncated_names[("bindparam", value.key)] = name
509
510    if value.type._isnull:
511        # either unique parameter, or other bound parameters that were
512        # passed in directly
513        # set type to that of the column unconditionally
514        value = value._with_binary_element_type(col.type)
515
516    return value._compiler_dispatch(compiler, **kw)
517
518
519def _key_getters_for_crud_column(
520    compiler: SQLCompiler, stmt: ValuesBase, compile_state: DMLState
521) -> Tuple[
522    Callable[[Union[str, ColumnClause[Any]]], Union[str, Tuple[str, str]]],
523    Callable[[ColumnClause[Any]], Union[str, Tuple[str, str]]],
524    _BindNameForColProtocol,
525]:
526    if dml.isupdate(compile_state) and compile_state._extra_froms:
527        # when extra tables are present, refer to the columns
528        # in those extra tables as table-qualified, including in
529        # dictionaries and when rendering bind param names.
530        # the "main" table of the statement remains unqualified,
531        # allowing the most compatibility with a non-multi-table
532        # statement.
533        _et = set(compile_state._extra_froms)
534
535        c_key_role = functools.partial(
536            coercions.expect_as_key, roles.DMLColumnRole
537        )
538
539        def _column_as_key(
540            key: Union[ColumnClause[Any], str],
541        ) -> Union[str, Tuple[str, str]]:
542            str_key = c_key_role(key)
543            if hasattr(key, "table") and key.table in _et:
544                return (key.table.name, str_key)  # type: ignore
545            else:
546                return str_key
547
548        def _getattr_col_key(
549            col: ColumnClause[Any],
550        ) -> Union[str, Tuple[str, str]]:
551            if col.table in _et:
552                return (col.table.name, col.key)  # type: ignore
553            else:
554                return col.key
555
556        def _col_bind_name(col: ColumnClause[Any]) -> str:
557            if col.table in _et:
558                if TYPE_CHECKING:
559                    assert isinstance(col.table, TableClause)
560                return "%s_%s" % (col.table.name, col.key)
561            else:
562                return col.key
563
564    else:
565        _column_as_key = functools.partial(
566            coercions.expect_as_key, roles.DMLColumnRole
567        )
568        _getattr_col_key = _col_bind_name = operator.attrgetter("key")  # type: ignore  # noqa: E501
569
570    return _column_as_key, _getattr_col_key, _col_bind_name
571
572
573def _scan_insert_from_select_cols(
574    compiler,
575    stmt,
576    compile_state,
577    parameters,
578    _getattr_col_key,
579    _column_as_key,
580    _col_bind_name,
581    check_columns,
582    values,
583    toplevel,
584    kw,
585):
586    cols = [stmt.table.c[_column_as_key(name)] for name in stmt._select_names]
587
588    assert compiler.stack[-1]["selectable"] is stmt
589
590    compiler.stack[-1]["insert_from_select"] = stmt.select
591
592    add_select_cols: List[_CrudParamElementSQLExpr] = []
593    if stmt.include_insert_from_select_defaults:
594        col_set = set(cols)
595        for col in stmt.table.columns:
596            # omit columns that were not in the SELECT statement.
597            # this will omit columns marked as omit_from_statements naturally,
598            # as long as that col was not explicit in the SELECT.
599            # if an omit_from_statements col has a "default" on it, then
600            # we need to include it, as these defaults should still fire off.
601            # but, if it has that default and it's the "sentinel" default,
602            # we don't do sentinel default operations for insert_from_select
603            # here so we again omit it.
604            if (
605                col not in col_set
606                and col.default
607                and not col.default.is_sentinel
608            ):
609                cols.append(col)
610
611    for c in cols:
612        col_key = _getattr_col_key(c)
613        if col_key in parameters and col_key not in check_columns:
614            parameters.pop(col_key)
615            values.append((c, compiler.preparer.format_column(c), None, ()))
616        else:
617            _append_param_insert_select_hasdefault(
618                compiler, stmt, c, add_select_cols, kw
619            )
620
621    if add_select_cols:
622        values.extend(add_select_cols)
623        ins_from_select = compiler.stack[-1]["insert_from_select"]
624        if not isinstance(ins_from_select, Select):
625            raise exc.CompileError(
626                f"Can't extend statement for INSERT..FROM SELECT to include "
627                f"additional default-holding column(s) "
628                f"""{
629                    ', '.join(repr(key) for _, key, _, _ in add_select_cols)
630                }.  Convert the selectable to a subquery() first, or pass """
631                "include_defaults=False to Insert.from_select() to skip these "
632                "columns."
633            )
634        ins_from_select = ins_from_select._generate()
635        # copy raw_columns
636        ins_from_select._raw_columns = list(ins_from_select._raw_columns) + [
637            expr for _, _, expr, _ in add_select_cols
638        ]
639        compiler.stack[-1]["insert_from_select"] = ins_from_select
640
641
642def _scan_cols(
643    compiler,
644    stmt,
645    compile_state,
646    parameters,
647    _getattr_col_key,
648    _column_as_key,
649    _col_bind_name,
650    check_columns,
651    values,
652    toplevel,
653    kw,
654):
655    (
656        need_pks,
657        implicit_returning,
658        implicit_return_defaults,
659        postfetch_lastrowid,
660        use_insertmanyvalues,
661        use_sentinel_columns,
662    ) = _get_returning_modifiers(compiler, stmt, compile_state, toplevel)
663
664    assert compile_state.isupdate or compile_state.isinsert
665
666    if compile_state._parameter_ordering:
667        parameter_ordering = [
668            _column_as_key(key) for key in compile_state._parameter_ordering
669        ]
670        ordered_keys = set(parameter_ordering)
671        cols = [
672            stmt.table.c[key]
673            for key in parameter_ordering
674            if isinstance(key, str) and key in stmt.table.c
675        ] + [c for c in stmt.table.c if c.key not in ordered_keys]
676
677    else:
678        cols = stmt.table.columns
679
680    isinsert = _compile_state_isinsert(compile_state)
681    if isinsert and not compile_state._has_multi_parameters:
682        # new rules for #7998.  fetch lastrowid or implicit returning
683        # for autoincrement column even if parameter is NULL, for DBs that
684        # override NULL param for primary key (sqlite, mysql/mariadb)
685        autoincrement_col = stmt.table._autoincrement_column
686        insert_null_pk_still_autoincrements = (
687            compiler.dialect.insert_null_pk_still_autoincrements
688        )
689    else:
690        autoincrement_col = insert_null_pk_still_autoincrements = None
691
692    if stmt._supplemental_returning:
693        supplemental_returning = set(stmt._supplemental_returning)
694    else:
695        supplemental_returning = set()
696
697    compiler_implicit_returning = compiler.implicit_returning
698
699    # TODO - see TODO(return_defaults_columns) below
700    # cols_in_params = set()
701
702    for c in cols:
703        # scan through every column in the target table
704
705        col_key = _getattr_col_key(c)
706
707        if col_key in parameters and col_key not in check_columns:
708            # parameter is present for the column.  use that.
709
710            _append_param_parameter(
711                compiler,
712                stmt,
713                compile_state,
714                c,
715                col_key,
716                parameters,
717                _col_bind_name,
718                implicit_returning,
719                implicit_return_defaults,
720                postfetch_lastrowid,
721                values,
722                autoincrement_col,
723                insert_null_pk_still_autoincrements,
724                kw,
725            )
726
727            # TODO - see TODO(return_defaults_columns) below
728            # cols_in_params.add(c)
729
730        elif isinsert:
731            # no parameter is present and it's an insert.
732
733            if c.primary_key and need_pks:
734                # it's a primary key column, it will need to be generated by a
735                # default generator of some kind, and the statement expects
736                # inserted_primary_key to be available.
737
738                if implicit_returning:
739                    # we can use RETURNING, find out how to invoke this
740                    # column and get the value where RETURNING is an option.
741                    # we can inline server-side functions in this case.
742
743                    _append_param_insert_pk_returning(
744                        compiler, stmt, c, values, kw
745                    )
746                else:
747                    # otherwise, find out how to invoke this column
748                    # and get its value where RETURNING is not an option.
749                    # if we have to invoke a server-side function, we need
750                    # to pre-execute it.   or if this is a straight
751                    # autoincrement column and the dialect supports it
752                    # we can use cursor.lastrowid.
753
754                    _append_param_insert_pk_no_returning(
755                        compiler, stmt, c, values, kw
756                    )
757
758            elif c.default is not None:
759                # column has a default, but it's not a pk column, or it is but
760                # we don't need to get the pk back.
761                if not c.default.is_sentinel or (
762                    use_sentinel_columns is not None
763                ):
764                    _append_param_insert_hasdefault(
765                        compiler, stmt, c, implicit_return_defaults, values, kw
766                    )
767
768            elif c.server_default is not None:
769                # column has a DDL-level default, and is either not a pk
770                # column or we don't need the pk.
771                if implicit_return_defaults and c in implicit_return_defaults:
772                    compiler_implicit_returning.append(c)
773                elif not c.primary_key:
774                    compiler.postfetch.append(c)
775
776            elif implicit_return_defaults and c in implicit_return_defaults:
777                compiler_implicit_returning.append(c)
778
779            elif (
780                c.primary_key
781                and c is not stmt.table._autoincrement_column
782                and not c.nullable
783            ):
784                _warn_pk_with_no_anticipated_value(c)
785
786        elif compile_state.isupdate:
787            # no parameter is present and it's an insert.
788
789            _append_param_update(
790                compiler,
791                compile_state,
792                stmt,
793                c,
794                implicit_return_defaults,
795                values,
796                kw,
797            )
798
799        # adding supplemental cols to implicit_returning in table
800        # order so that order is maintained between multiple INSERT
801        # statements which may have different parameters included, but all
802        # have the same RETURNING clause
803        if (
804            c in supplemental_returning
805            and c not in compiler_implicit_returning
806        ):
807            compiler_implicit_returning.append(c)
808
809    if supplemental_returning:
810        # we should have gotten every col into implicit_returning,
811        # however supplemental returning can also have SQL functions etc.
812        # in it
813        remaining_supplemental = supplemental_returning.difference(
814            compiler_implicit_returning
815        )
816        compiler_implicit_returning.extend(
817            c
818            for c in stmt._supplemental_returning
819            if c in remaining_supplemental
820        )
821
822    # TODO(return_defaults_columns): there can still be more columns in
823    # _return_defaults_columns in the case that they are from something like an
824    # aliased of the table. we can add them here, however this breaks other ORM
825    # things. so this is for another day. see
826    # test/orm/dml/test_update_delete_where.py -> test_update_from_alias
827
828    # if stmt._return_defaults_columns:
829    #     compiler_implicit_returning.extend(
830    #         set(stmt._return_defaults_columns)
831    #         .difference(compiler_implicit_returning)
832    #         .difference(cols_in_params)
833    #     )
834
835    return (use_insertmanyvalues, use_sentinel_columns)
836
837
838def _setup_delete_return_defaults(
839    compiler,
840    stmt,
841    compile_state,
842    parameters,
843    _getattr_col_key,
844    _column_as_key,
845    _col_bind_name,
846    check_columns,
847    values,
848    toplevel,
849    kw,
850):
851    (_, _, implicit_return_defaults, *_) = _get_returning_modifiers(
852        compiler, stmt, compile_state, toplevel
853    )
854
855    if not implicit_return_defaults:
856        return
857
858    if stmt._return_defaults_columns:
859        compiler.implicit_returning.extend(implicit_return_defaults)
860
861    if stmt._supplemental_returning:
862        ir_set = set(compiler.implicit_returning)
863        compiler.implicit_returning.extend(
864            c for c in stmt._supplemental_returning if c not in ir_set
865        )
866
867
868def _append_param_parameter(
869    compiler,
870    stmt,
871    compile_state,
872    c,
873    col_key,
874    parameters,
875    _col_bind_name,
876    implicit_returning,
877    implicit_return_defaults,
878    postfetch_lastrowid,
879    values,
880    autoincrement_col,
881    insert_null_pk_still_autoincrements,
882    kw,
883):
884    value = parameters.pop(col_key)
885
886    has_visiting_cte = kw.get("visiting_cte") is not None
887    col_value = compiler.preparer.format_column(
888        c, use_table=compile_state.include_table_with_column_exprs
889    )
890
891    accumulated_bind_names: Set[str] = set()
892
893    if coercions._is_literal(value):
894        if (
895            insert_null_pk_still_autoincrements
896            and c.primary_key
897            and c is autoincrement_col
898        ):
899            # support use case for #7998, fetch autoincrement cols
900            # even if value was given.
901
902            if postfetch_lastrowid:
903                compiler.postfetch_lastrowid = True
904            elif implicit_returning:
905                compiler.implicit_returning.append(c)
906
907        value = _create_bind_param(
908            compiler,
909            c,
910            value,
911            required=value is REQUIRED,
912            name=(
913                _col_bind_name(c)
914                if not _compile_state_isinsert(compile_state)
915                or not compile_state._has_multi_parameters
916                else "%s_m0" % _col_bind_name(c)
917            ),
918            accumulate_bind_names=accumulated_bind_names,
919            force_anonymous=has_visiting_cte,
920            **kw,
921        )
922    elif value._is_bind_parameter:
923        if (
924            insert_null_pk_still_autoincrements
925            and value.value is None
926            and c.primary_key
927            and c is autoincrement_col
928        ):
929            # support use case for #7998, fetch autoincrement cols
930            # even if value was given
931            if implicit_returning:
932                compiler.implicit_returning.append(c)
933            elif compiler.dialect.postfetch_lastrowid:
934                compiler.postfetch_lastrowid = True
935
936        value = _handle_values_anonymous_param(
937            compiler,
938            c,
939            value,
940            name=(
941                _col_bind_name(c)
942                if not _compile_state_isinsert(compile_state)
943                or not compile_state._has_multi_parameters
944                else "%s_m0" % _col_bind_name(c)
945            ),
946            accumulate_bind_names=accumulated_bind_names,
947            **kw,
948        )
949    else:
950        # value is a SQL expression
951        value = compiler.process(
952            value.self_group(),
953            accumulate_bind_names=accumulated_bind_names,
954            **kw,
955        )
956
957        if compile_state.isupdate:
958            if implicit_return_defaults and c in implicit_return_defaults:
959                compiler.implicit_returning.append(c)
960
961            else:
962                compiler.postfetch.append(c)
963        else:
964            if c.primary_key:
965                if implicit_returning:
966                    compiler.implicit_returning.append(c)
967                elif compiler.dialect.postfetch_lastrowid:
968                    compiler.postfetch_lastrowid = True
969
970            elif implicit_return_defaults and (c in implicit_return_defaults):
971                compiler.implicit_returning.append(c)
972
973            else:
974                # postfetch specifically means, "we can SELECT the row we just
975                # inserted by primary key to get back the server generated
976                # defaults". so by definition this can't be used to get the
977                # primary key value back, because we need to have it ahead of
978                # time.
979
980                compiler.postfetch.append(c)
981
982    values.append((c, col_value, value, accumulated_bind_names))
983
984
985def _append_param_insert_pk_returning(compiler, stmt, c, values, kw):
986    """Create a primary key expression in the INSERT statement where
987    we want to populate result.inserted_primary_key and RETURNING
988    is available.
989
990    """
991    if c.default is not None:
992        if c.default.is_sequence:
993            if compiler.dialect.supports_sequences and (
994                not c.default.optional
995                or not compiler.dialect.sequences_optional
996            ):
997                accumulated_bind_names: Set[str] = set()
998                values.append(
999                    (
1000                        c,
1001                        compiler.preparer.format_column(c),
1002                        compiler.process(
1003                            c.default,
1004                            accumulate_bind_names=accumulated_bind_names,
1005                            **kw,
1006                        ),
1007                        accumulated_bind_names,
1008                    )
1009                )
1010            compiler.implicit_returning.append(c)
1011        elif c.default.is_clause_element:
1012            accumulated_bind_names = set()
1013            values.append(
1014                (
1015                    c,
1016                    compiler.preparer.format_column(c),
1017                    compiler.process(
1018                        c.default.arg.self_group(),
1019                        accumulate_bind_names=accumulated_bind_names,
1020                        **kw,
1021                    ),
1022                    accumulated_bind_names,
1023                )
1024            )
1025            compiler.implicit_returning.append(c)
1026        else:
1027            # client side default.  OK we can't use RETURNING, need to
1028            # do a "prefetch", which in fact fetches the default value
1029            # on the Python side
1030            values.append(
1031                (
1032                    c,
1033                    compiler.preparer.format_column(c),
1034                    _create_insert_prefetch_bind_param(compiler, c, **kw),
1035                    (c.key,),
1036                )
1037            )
1038    elif c is stmt.table._autoincrement_column or c.server_default is not None:
1039        compiler.implicit_returning.append(c)
1040    elif not c.nullable:
1041        # no .default, no .server_default, not autoincrement, we have
1042        # no indication this primary key column will have any value
1043        _warn_pk_with_no_anticipated_value(c)
1044
1045
1046def _append_param_insert_pk_no_returning(compiler, stmt, c, values, kw):
1047    """Create a primary key expression in the INSERT statement where
1048    we want to populate result.inserted_primary_key and we cannot use
1049    RETURNING.
1050
1051    Depending on the kind of default here we may create a bound parameter
1052    in the INSERT statement and pre-execute a default generation function,
1053    or we may use cursor.lastrowid if supported by the dialect.
1054
1055
1056    """
1057
1058    if (
1059        # column has a Python-side default
1060        c.default is not None
1061        and (
1062            # and it either is not a sequence, or it is and we support
1063            # sequences and want to invoke it
1064            not c.default.is_sequence
1065            or (
1066                compiler.dialect.supports_sequences
1067                and (
1068                    not c.default.optional
1069                    or not compiler.dialect.sequences_optional
1070                )
1071            )
1072        )
1073    ) or (
1074        # column is the "autoincrement column"
1075        c is stmt.table._autoincrement_column
1076        and (
1077            # dialect can't use cursor.lastrowid
1078            not compiler.dialect.postfetch_lastrowid
1079            and (
1080                # column has a Sequence and we support those
1081                (
1082                    c.default is not None
1083                    and c.default.is_sequence
1084                    and compiler.dialect.supports_sequences
1085                )
1086                or
1087                # column has no default on it, but dialect can run the
1088                # "autoincrement" mechanism explicitly, e.g. PostgreSQL
1089                # SERIAL we know the sequence name
1090                (
1091                    c.default is None
1092                    and compiler.dialect.preexecute_autoincrement_sequences
1093                )
1094            )
1095        )
1096    ):
1097        # do a pre-execute of the default
1098        values.append(
1099            (
1100                c,
1101                compiler.preparer.format_column(c),
1102                _create_insert_prefetch_bind_param(compiler, c, **kw),
1103                (c.key,),
1104            )
1105        )
1106    elif (
1107        c.default is None
1108        and c.server_default is None
1109        and not c.nullable
1110        and c is not stmt.table._autoincrement_column
1111    ):
1112        # no .default, no .server_default, not autoincrement, we have
1113        # no indication this primary key column will have any value
1114        _warn_pk_with_no_anticipated_value(c)
1115    elif compiler.dialect.postfetch_lastrowid:
1116        # finally, where it seems like there will be a generated primary key
1117        # value and we haven't set up any other way to fetch it, and the
1118        # dialect supports cursor.lastrowid, switch on the lastrowid flag so
1119        # that the DefaultExecutionContext calls upon cursor.lastrowid
1120        compiler.postfetch_lastrowid = True
1121
1122
1123def _append_param_insert_hasdefault(
1124    compiler, stmt, c, implicit_return_defaults, values, kw
1125):
1126    if c.default.is_sequence:
1127        if compiler.dialect.supports_sequences and (
1128            not c.default.optional or not compiler.dialect.sequences_optional
1129        ):
1130            accumulated_bind_names: Set[str] = set()
1131            values.append(
1132                (
1133                    c,
1134                    compiler.preparer.format_column(c),
1135                    compiler.process(
1136                        c.default,
1137                        accumulate_bind_names=accumulated_bind_names,
1138                        **kw,
1139                    ),
1140                    accumulated_bind_names,
1141                )
1142            )
1143            if implicit_return_defaults and c in implicit_return_defaults:
1144                compiler.implicit_returning.append(c)
1145            elif not c.primary_key:
1146                compiler.postfetch.append(c)
1147    elif c.default.is_clause_element:
1148        accumulated_bind_names = set()
1149        values.append(
1150            (
1151                c,
1152                compiler.preparer.format_column(c),
1153                compiler.process(
1154                    c.default.arg.self_group(),
1155                    accumulate_bind_names=accumulated_bind_names,
1156                    **kw,
1157                ),
1158                accumulated_bind_names,
1159            )
1160        )
1161
1162        if implicit_return_defaults and c in implicit_return_defaults:
1163            compiler.implicit_returning.append(c)
1164        elif not c.primary_key:
1165            # don't add primary key column to postfetch
1166            compiler.postfetch.append(c)
1167    else:
1168        values.append(
1169            (
1170                c,
1171                compiler.preparer.format_column(c),
1172                _create_insert_prefetch_bind_param(compiler, c, **kw),
1173                (c.key,),
1174            )
1175        )
1176
1177
1178def _append_param_insert_select_hasdefault(
1179    compiler: SQLCompiler,
1180    stmt: ValuesBase,
1181    c: ColumnClause[Any],
1182    values: List[_CrudParamElementSQLExpr],
1183    kw: Dict[str, Any],
1184) -> None:
1185    if default_is_sequence(c.default):
1186        if compiler.dialect.supports_sequences and (
1187            not c.default.optional or not compiler.dialect.sequences_optional
1188        ):
1189            values.append(
1190                (
1191                    c,
1192                    compiler.preparer.format_column(c),
1193                    c.default.next_value(),
1194                    (),
1195                )
1196            )
1197    elif default_is_clause_element(c.default):
1198        values.append(
1199            (
1200                c,

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

codekingpro/portable-devtools · Team Ai