This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# sql/dml.py
|
||||
# Copyright (C) 2009-2023 the SQLAlchemy authors and contributors
|
||||
# Copyright (C) 2009-2025 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
@@ -23,6 +23,7 @@ from typing import NoReturn
|
||||
from typing import Optional
|
||||
from typing import overload
|
||||
from typing import Sequence
|
||||
from typing import Set
|
||||
from typing import Tuple
|
||||
from typing import Type
|
||||
from typing import TYPE_CHECKING
|
||||
@@ -42,6 +43,7 @@ from .base import _from_objects
|
||||
from .base import _generative
|
||||
from .base import _select_iterables
|
||||
from .base import ColumnCollection
|
||||
from .base import ColumnSet
|
||||
from .base import CompileState
|
||||
from .base import DialectKWArgs
|
||||
from .base import Executable
|
||||
@@ -91,14 +93,11 @@ if TYPE_CHECKING:
|
||||
from .selectable import Select
|
||||
from .selectable import Selectable
|
||||
|
||||
def isupdate(dml: DMLState) -> TypeGuard[UpdateDMLState]:
|
||||
...
|
||||
def isupdate(dml: DMLState) -> TypeGuard[UpdateDMLState]: ...
|
||||
|
||||
def isdelete(dml: DMLState) -> TypeGuard[DeleteDMLState]:
|
||||
...
|
||||
def isdelete(dml: DMLState) -> TypeGuard[DeleteDMLState]: ...
|
||||
|
||||
def isinsert(dml: DMLState) -> TypeGuard[InsertDMLState]:
|
||||
...
|
||||
def isinsert(dml: DMLState) -> TypeGuard[InsertDMLState]: ...
|
||||
|
||||
else:
|
||||
isupdate = operator.attrgetter("isupdate")
|
||||
@@ -137,9 +136,11 @@ class DMLState(CompileState):
|
||||
@classmethod
|
||||
def get_entity_description(cls, statement: UpdateBase) -> Dict[str, Any]:
|
||||
return {
|
||||
"name": statement.table.name
|
||||
if is_named_from_clause(statement.table)
|
||||
else None,
|
||||
"name": (
|
||||
statement.table.name
|
||||
if is_named_from_clause(statement.table)
|
||||
else None
|
||||
),
|
||||
"table": statement.table,
|
||||
}
|
||||
|
||||
@@ -163,8 +164,7 @@ class DMLState(CompileState):
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@classmethod
|
||||
def get_plugin_class(cls, statement: Executable) -> Type[DMLState]:
|
||||
...
|
||||
def get_plugin_class(cls, statement: Executable) -> Type[DMLState]: ...
|
||||
|
||||
@classmethod
|
||||
def _get_multi_crud_kv_pairs(
|
||||
@@ -190,13 +190,15 @@ class DMLState(CompileState):
|
||||
return [
|
||||
(
|
||||
coercions.expect(roles.DMLColumnRole, k),
|
||||
v
|
||||
if not needs_to_be_cacheable
|
||||
else coercions.expect(
|
||||
roles.ExpressionElementRole,
|
||||
v,
|
||||
type_=NullType(),
|
||||
is_crud=True,
|
||||
(
|
||||
v
|
||||
if not needs_to_be_cacheable
|
||||
else coercions.expect(
|
||||
roles.ExpressionElementRole,
|
||||
v,
|
||||
type_=NullType(),
|
||||
is_crud=True,
|
||||
)
|
||||
),
|
||||
)
|
||||
for k, v in kv_iterator
|
||||
@@ -306,12 +308,14 @@ class InsertDMLState(DMLState):
|
||||
def _process_multi_values(self, statement: ValuesBase) -> None:
|
||||
for parameters in statement._multi_values:
|
||||
multi_parameters: List[MutableMapping[_DMLColumnElement, Any]] = [
|
||||
{
|
||||
c.key: value
|
||||
for c, value in zip(statement.table.c, parameter_set)
|
||||
}
|
||||
if isinstance(parameter_set, collections_abc.Sequence)
|
||||
else parameter_set
|
||||
(
|
||||
{
|
||||
c.key: value
|
||||
for c, value in zip(statement.table.c, parameter_set)
|
||||
}
|
||||
if isinstance(parameter_set, collections_abc.Sequence)
|
||||
else parameter_set
|
||||
)
|
||||
for parameter_set in parameters
|
||||
]
|
||||
|
||||
@@ -396,9 +400,9 @@ class UpdateBase(
|
||||
|
||||
__visit_name__ = "update_base"
|
||||
|
||||
_hints: util.immutabledict[
|
||||
Tuple[_DMLTableElement, str], str
|
||||
] = util.EMPTY_DICT
|
||||
_hints: util.immutabledict[Tuple[_DMLTableElement, str], str] = (
|
||||
util.EMPTY_DICT
|
||||
)
|
||||
named_with_column = False
|
||||
|
||||
_label_style: SelectLabelStyle = (
|
||||
@@ -407,19 +411,25 @@ class UpdateBase(
|
||||
table: _DMLTableElement
|
||||
|
||||
_return_defaults = False
|
||||
_return_defaults_columns: Optional[
|
||||
Tuple[_ColumnsClauseElement, ...]
|
||||
] = None
|
||||
_return_defaults_columns: Optional[Tuple[_ColumnsClauseElement, ...]] = (
|
||||
None
|
||||
)
|
||||
_supplemental_returning: Optional[Tuple[_ColumnsClauseElement, ...]] = None
|
||||
_returning: Tuple[_ColumnsClauseElement, ...] = ()
|
||||
|
||||
is_dml = True
|
||||
|
||||
def _generate_fromclause_column_proxies(
|
||||
self, fromclause: FromClause
|
||||
self,
|
||||
fromclause: FromClause,
|
||||
columns: ColumnCollection[str, KeyedColumnElement[Any]],
|
||||
primary_key: ColumnSet,
|
||||
foreign_keys: Set[KeyedColumnElement[Any]],
|
||||
) -> None:
|
||||
fromclause._columns._populate_separate_keys(
|
||||
col._make_proxy(fromclause)
|
||||
columns._populate_separate_keys(
|
||||
col._make_proxy(
|
||||
fromclause, primary_key=primary_key, foreign_keys=foreign_keys
|
||||
)
|
||||
for col in self._all_selected_columns
|
||||
if is_column_element(col)
|
||||
)
|
||||
@@ -523,11 +533,11 @@ class UpdateBase(
|
||||
|
||||
E.g.::
|
||||
|
||||
stmt = table.insert().values(data='newdata').return_defaults()
|
||||
stmt = table.insert().values(data="newdata").return_defaults()
|
||||
|
||||
result = connection.execute(stmt)
|
||||
|
||||
server_created_at = result.returned_defaults['created_at']
|
||||
server_created_at = result.returned_defaults["created_at"]
|
||||
|
||||
When used against an UPDATE statement
|
||||
:meth:`.UpdateBase.return_defaults` instead looks for columns that
|
||||
@@ -685,6 +695,16 @@ class UpdateBase(
|
||||
|
||||
return self
|
||||
|
||||
def is_derived_from(self, fromclause: Optional[FromClause]) -> bool:
|
||||
"""Return ``True`` if this :class:`.ReturnsRows` is
|
||||
'derived' from the given :class:`.FromClause`.
|
||||
|
||||
Since these are DMLs, we dont want such statements ever being adapted
|
||||
so we return False for derives.
|
||||
|
||||
"""
|
||||
return False
|
||||
|
||||
@_generative
|
||||
def returning(
|
||||
self,
|
||||
@@ -1030,7 +1050,7 @@ class ValuesBase(UpdateBase):
|
||||
|
||||
users.insert().values(name="some name")
|
||||
|
||||
users.update().where(users.c.id==5).values(name="some name")
|
||||
users.update().where(users.c.id == 5).values(name="some name")
|
||||
|
||||
:param \*args: As an alternative to passing key/value parameters,
|
||||
a dictionary, tuple, or list of dictionaries or tuples can be passed
|
||||
@@ -1060,13 +1080,17 @@ class ValuesBase(UpdateBase):
|
||||
this syntax is supported on backends such as SQLite, PostgreSQL,
|
||||
MySQL, but not necessarily others::
|
||||
|
||||
users.insert().values([
|
||||
{"name": "some name"},
|
||||
{"name": "some other name"},
|
||||
{"name": "yet another name"},
|
||||
])
|
||||
users.insert().values(
|
||||
[
|
||||
{"name": "some name"},
|
||||
{"name": "some other name"},
|
||||
{"name": "yet another name"},
|
||||
]
|
||||
)
|
||||
|
||||
The above form would render a multiple VALUES statement similar to::
|
||||
The above form would render a multiple VALUES statement similar to:
|
||||
|
||||
.. sourcecode:: sql
|
||||
|
||||
INSERT INTO users (name) VALUES
|
||||
(:name_1),
|
||||
@@ -1244,7 +1268,7 @@ class Insert(ValuesBase):
|
||||
e.g.::
|
||||
|
||||
sel = select(table1.c.a, table1.c.b).where(table1.c.c > 5)
|
||||
ins = table2.insert().from_select(['a', 'b'], sel)
|
||||
ins = table2.insert().from_select(["a", "b"], sel)
|
||||
|
||||
:param names: a sequence of string column names or
|
||||
:class:`_schema.Column`
|
||||
@@ -1295,8 +1319,7 @@ class Insert(ValuesBase):
|
||||
@overload
|
||||
def returning(
|
||||
self, __ent0: _TCCA[_T0], *, sort_by_parameter_order: bool = False
|
||||
) -> ReturningInsert[Tuple[_T0]]:
|
||||
...
|
||||
) -> ReturningInsert[Tuple[_T0]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1305,8 +1328,7 @@ class Insert(ValuesBase):
|
||||
__ent1: _TCCA[_T1],
|
||||
*,
|
||||
sort_by_parameter_order: bool = False,
|
||||
) -> ReturningInsert[Tuple[_T0, _T1]]:
|
||||
...
|
||||
) -> ReturningInsert[Tuple[_T0, _T1]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1316,8 +1338,7 @@ class Insert(ValuesBase):
|
||||
__ent2: _TCCA[_T2],
|
||||
*,
|
||||
sort_by_parameter_order: bool = False,
|
||||
) -> ReturningInsert[Tuple[_T0, _T1, _T2]]:
|
||||
...
|
||||
) -> ReturningInsert[Tuple[_T0, _T1, _T2]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1328,8 +1349,7 @@ class Insert(ValuesBase):
|
||||
__ent3: _TCCA[_T3],
|
||||
*,
|
||||
sort_by_parameter_order: bool = False,
|
||||
) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3]]:
|
||||
...
|
||||
) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1341,8 +1361,7 @@ class Insert(ValuesBase):
|
||||
__ent4: _TCCA[_T4],
|
||||
*,
|
||||
sort_by_parameter_order: bool = False,
|
||||
) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4]]:
|
||||
...
|
||||
) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1355,8 +1374,7 @@ class Insert(ValuesBase):
|
||||
__ent5: _TCCA[_T5],
|
||||
*,
|
||||
sort_by_parameter_order: bool = False,
|
||||
) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
|
||||
...
|
||||
) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1370,8 +1388,7 @@ class Insert(ValuesBase):
|
||||
__ent6: _TCCA[_T6],
|
||||
*,
|
||||
sort_by_parameter_order: bool = False,
|
||||
) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
|
||||
...
|
||||
) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1386,8 +1403,9 @@ class Insert(ValuesBase):
|
||||
__ent7: _TCCA[_T7],
|
||||
*,
|
||||
sort_by_parameter_order: bool = False,
|
||||
) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
|
||||
...
|
||||
) -> ReturningInsert[
|
||||
Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]
|
||||
]: ...
|
||||
|
||||
# END OVERLOADED FUNCTIONS self.returning
|
||||
|
||||
@@ -1397,16 +1415,14 @@ class Insert(ValuesBase):
|
||||
*cols: _ColumnsClauseArgument[Any],
|
||||
sort_by_parameter_order: bool = False,
|
||||
**__kw: Any,
|
||||
) -> ReturningInsert[Any]:
|
||||
...
|
||||
) -> ReturningInsert[Any]: ...
|
||||
|
||||
def returning(
|
||||
self,
|
||||
*cols: _ColumnsClauseArgument[Any],
|
||||
sort_by_parameter_order: bool = False,
|
||||
**__kw: Any,
|
||||
) -> ReturningInsert[Any]:
|
||||
...
|
||||
) -> ReturningInsert[Any]: ...
|
||||
|
||||
|
||||
class ReturningInsert(Insert, TypedReturnsRows[_TP]):
|
||||
@@ -1541,9 +1557,7 @@ class Update(DMLWhereBase, ValuesBase):
|
||||
|
||||
E.g.::
|
||||
|
||||
stmt = table.update().ordered_values(
|
||||
("name", "ed"), ("ident": "foo")
|
||||
)
|
||||
stmt = table.update().ordered_values(("name", "ed"), ("ident", "foo"))
|
||||
|
||||
.. seealso::
|
||||
|
||||
@@ -1556,7 +1570,7 @@ class Update(DMLWhereBase, ValuesBase):
|
||||
:paramref:`_expression.update.preserve_parameter_order`
|
||||
parameter, which will be removed in SQLAlchemy 2.0.
|
||||
|
||||
"""
|
||||
""" # noqa: E501
|
||||
if self._values:
|
||||
raise exc.ArgumentError(
|
||||
"This statement already has values present"
|
||||
@@ -1596,20 +1610,19 @@ class Update(DMLWhereBase, ValuesBase):
|
||||
# statically generated** by tools/generate_tuple_map_overloads.py
|
||||
|
||||
@overload
|
||||
def returning(self, __ent0: _TCCA[_T0]) -> ReturningUpdate[Tuple[_T0]]:
|
||||
...
|
||||
def returning(
|
||||
self, __ent0: _TCCA[_T0]
|
||||
) -> ReturningUpdate[Tuple[_T0]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1]]:
|
||||
...
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1, _T2]]:
|
||||
...
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1, _T2]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1618,8 +1631,7 @@ class Update(DMLWhereBase, ValuesBase):
|
||||
__ent1: _TCCA[_T1],
|
||||
__ent2: _TCCA[_T2],
|
||||
__ent3: _TCCA[_T3],
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3]]:
|
||||
...
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1629,8 +1641,7 @@ class Update(DMLWhereBase, ValuesBase):
|
||||
__ent2: _TCCA[_T2],
|
||||
__ent3: _TCCA[_T3],
|
||||
__ent4: _TCCA[_T4],
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4]]:
|
||||
...
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1641,8 +1652,7 @@ class Update(DMLWhereBase, ValuesBase):
|
||||
__ent3: _TCCA[_T3],
|
||||
__ent4: _TCCA[_T4],
|
||||
__ent5: _TCCA[_T5],
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
|
||||
...
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1654,8 +1664,7 @@ class Update(DMLWhereBase, ValuesBase):
|
||||
__ent4: _TCCA[_T4],
|
||||
__ent5: _TCCA[_T5],
|
||||
__ent6: _TCCA[_T6],
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
|
||||
...
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1668,21 +1677,20 @@ class Update(DMLWhereBase, ValuesBase):
|
||||
__ent5: _TCCA[_T5],
|
||||
__ent6: _TCCA[_T6],
|
||||
__ent7: _TCCA[_T7],
|
||||
) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
|
||||
...
|
||||
) -> ReturningUpdate[
|
||||
Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]
|
||||
]: ...
|
||||
|
||||
# END OVERLOADED FUNCTIONS self.returning
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
|
||||
) -> ReturningUpdate[Any]:
|
||||
...
|
||||
) -> ReturningUpdate[Any]: ...
|
||||
|
||||
def returning(
|
||||
self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
|
||||
) -> ReturningUpdate[Any]:
|
||||
...
|
||||
) -> ReturningUpdate[Any]: ...
|
||||
|
||||
|
||||
class ReturningUpdate(Update, TypedReturnsRows[_TP]):
|
||||
@@ -1734,20 +1742,19 @@ class Delete(DMLWhereBase, UpdateBase):
|
||||
# statically generated** by tools/generate_tuple_map_overloads.py
|
||||
|
||||
@overload
|
||||
def returning(self, __ent0: _TCCA[_T0]) -> ReturningDelete[Tuple[_T0]]:
|
||||
...
|
||||
def returning(
|
||||
self, __ent0: _TCCA[_T0]
|
||||
) -> ReturningDelete[Tuple[_T0]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
|
||||
) -> ReturningDelete[Tuple[_T0, _T1]]:
|
||||
...
|
||||
) -> ReturningDelete[Tuple[_T0, _T1]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
|
||||
) -> ReturningDelete[Tuple[_T0, _T1, _T2]]:
|
||||
...
|
||||
) -> ReturningDelete[Tuple[_T0, _T1, _T2]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1756,8 +1763,7 @@ class Delete(DMLWhereBase, UpdateBase):
|
||||
__ent1: _TCCA[_T1],
|
||||
__ent2: _TCCA[_T2],
|
||||
__ent3: _TCCA[_T3],
|
||||
) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3]]:
|
||||
...
|
||||
) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1767,8 +1773,7 @@ class Delete(DMLWhereBase, UpdateBase):
|
||||
__ent2: _TCCA[_T2],
|
||||
__ent3: _TCCA[_T3],
|
||||
__ent4: _TCCA[_T4],
|
||||
) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4]]:
|
||||
...
|
||||
) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1779,8 +1784,7 @@ class Delete(DMLWhereBase, UpdateBase):
|
||||
__ent3: _TCCA[_T3],
|
||||
__ent4: _TCCA[_T4],
|
||||
__ent5: _TCCA[_T5],
|
||||
) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
|
||||
...
|
||||
) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1792,8 +1796,7 @@ class Delete(DMLWhereBase, UpdateBase):
|
||||
__ent4: _TCCA[_T4],
|
||||
__ent5: _TCCA[_T5],
|
||||
__ent6: _TCCA[_T6],
|
||||
) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
|
||||
...
|
||||
) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ...
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
@@ -1806,21 +1809,20 @@ class Delete(DMLWhereBase, UpdateBase):
|
||||
__ent5: _TCCA[_T5],
|
||||
__ent6: _TCCA[_T6],
|
||||
__ent7: _TCCA[_T7],
|
||||
) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
|
||||
...
|
||||
) -> ReturningDelete[
|
||||
Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]
|
||||
]: ...
|
||||
|
||||
# END OVERLOADED FUNCTIONS self.returning
|
||||
|
||||
@overload
|
||||
def returning(
|
||||
self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
|
||||
) -> ReturningDelete[Any]:
|
||||
...
|
||||
) -> ReturningDelete[Any]: ...
|
||||
|
||||
def returning(
|
||||
self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
|
||||
) -> ReturningDelete[Any]:
|
||||
...
|
||||
) -> ReturningDelete[Any]: ...
|
||||
|
||||
|
||||
class ReturningDelete(Update, TypedReturnsRows[_TP]):
|
||||
|
||||
Reference in New Issue
Block a user