mirror of
https://github.com/django/django.git
synced 2025-09-04 19:35:05 +03:00
Refs #27222 -- Adapted RETURNING handling to be usable for UPDATE queries.
Renamed existing methods and abstractions used for INSERT … RETURNING to be generic enough to be used in the context of UPDATEs as well. This also consolidates SQL compliant implementations on BaseDatabaseOperations.
This commit is contained in:
parent
dc4ee99152
commit
292b9e6fe8
|
@ -208,13 +208,6 @@ class BaseDatabaseOperations:
|
|||
else:
|
||||
return ["DISTINCT"], []
|
||||
|
||||
def fetch_returned_insert_columns(self, cursor, returning_params):
|
||||
"""
|
||||
Given a cursor object that has just performed an INSERT...RETURNING
|
||||
statement into a table, return the newly created data.
|
||||
"""
|
||||
return cursor.fetchone()
|
||||
|
||||
def force_group_by(self):
|
||||
"""
|
||||
Return a GROUP BY clause to use with a HAVING clause when no grouping
|
||||
|
@ -358,11 +351,12 @@ class BaseDatabaseOperations:
|
|||
"""
|
||||
return value
|
||||
|
||||
def return_insert_columns(self, fields):
|
||||
def returning_columns(self, fields):
|
||||
"""
|
||||
For backends that support returning columns as part of an insert query,
|
||||
return the SQL and params to append to the INSERT query. The returned
|
||||
fragment should contain a format string to hold the appropriate column.
|
||||
For backends that support returning columns as part of an insert or
|
||||
update query, return the SQL and params to append to the query.
|
||||
The returned fragment should contain a format string to hold the
|
||||
appropriate column.
|
||||
"""
|
||||
if not fields:
|
||||
return "", ()
|
||||
|
@ -376,10 +370,10 @@ class BaseDatabaseOperations:
|
|||
]
|
||||
return "RETURNING %s" % ", ".join(columns), ()
|
||||
|
||||
def fetch_returned_insert_rows(self, cursor):
|
||||
def fetch_returned_rows(self, cursor, returning_params):
|
||||
"""
|
||||
Given a cursor object that has just performed an INSERT...RETURNING
|
||||
statement into a table, return the tuple of returned data.
|
||||
Given a cursor object for a DML query with a RETURNING statement,
|
||||
return the selected returning rows of tuples.
|
||||
"""
|
||||
return cursor.fetchall()
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ from django.utils.functional import cached_property
|
|||
from django.utils.regex_helper import _lazy_re_compile
|
||||
|
||||
from .base import Database
|
||||
from .utils import BulkInsertMapper, InsertVar, Oracle_datetime
|
||||
from .utils import BoundVar, BulkInsertMapper, Oracle_datetime
|
||||
|
||||
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
|
@ -298,12 +298,27 @@ END;
|
|||
def deferrable_sql(self):
|
||||
return " DEFERRABLE INITIALLY DEFERRED"
|
||||
|
||||
def fetch_returned_insert_columns(self, cursor, returning_params):
|
||||
columns = []
|
||||
for param in returning_params:
|
||||
value = param.get_value()
|
||||
columns.append(value[0])
|
||||
return tuple(columns)
|
||||
def returning_columns(self, fields):
|
||||
if not fields:
|
||||
return "", ()
|
||||
field_names = []
|
||||
params = []
|
||||
for field in fields:
|
||||
field_names.append(
|
||||
"%s.%s"
|
||||
% (
|
||||
self.quote_name(field.model._meta.db_table),
|
||||
self.quote_name(field.column),
|
||||
)
|
||||
)
|
||||
params.append(BoundVar(field))
|
||||
return "RETURNING %s INTO %s" % (
|
||||
", ".join(field_names),
|
||||
", ".join(["%s"] * len(params)),
|
||||
), tuple(params)
|
||||
|
||||
def fetch_returned_rows(self, cursor, returning_params):
|
||||
return list(zip(*(param.get_value() for param in returning_params)))
|
||||
|
||||
def no_limit_value(self):
|
||||
return None
|
||||
|
@ -391,25 +406,6 @@ END;
|
|||
match_option = "'i'"
|
||||
return "REGEXP_LIKE(%%s, %%s, %s)" % match_option
|
||||
|
||||
def return_insert_columns(self, fields):
|
||||
if not fields:
|
||||
return "", ()
|
||||
field_names = []
|
||||
params = []
|
||||
for field in fields:
|
||||
field_names.append(
|
||||
"%s.%s"
|
||||
% (
|
||||
self.quote_name(field.model._meta.db_table),
|
||||
self.quote_name(field.column),
|
||||
)
|
||||
)
|
||||
params.append(InsertVar(field))
|
||||
return "RETURNING %s INTO %s" % (
|
||||
", ".join(field_names),
|
||||
", ".join(["%s"] * len(params)),
|
||||
), tuple(params)
|
||||
|
||||
def __foreign_key_constraints(self, table_name, recursive):
|
||||
with self.connection.cursor() as cursor:
|
||||
if recursive:
|
||||
|
|
|
@ -4,7 +4,7 @@ import decimal
|
|||
from .base import Database
|
||||
|
||||
|
||||
class InsertVar:
|
||||
class BoundVar:
|
||||
"""
|
||||
A late-binding cursor variable that can be passed to Cursor.execute
|
||||
as a parameter, in order to receive the id of the row created by an
|
||||
|
|
|
@ -1890,7 +1890,7 @@ class SQLInsertCompiler(SQLCompiler):
|
|||
result.append(on_conflict_suffix_sql)
|
||||
# Skip empty r_sql to allow subclasses to customize behavior for
|
||||
# 3rd party backends. Refs #19096.
|
||||
r_sql, self.returning_params = self.connection.ops.return_insert_columns(
|
||||
r_sql, self.returning_params = self.connection.ops.returning_columns(
|
||||
self.returning_fields
|
||||
)
|
||||
if r_sql:
|
||||
|
@ -1925,20 +1925,16 @@ class SQLInsertCompiler(SQLCompiler):
|
|||
cursor.execute(sql, params)
|
||||
if not self.returning_fields:
|
||||
return []
|
||||
obj_len = len(self.query.objs)
|
||||
if (
|
||||
self.connection.features.can_return_rows_from_bulk_insert
|
||||
and len(self.query.objs) > 1
|
||||
and obj_len > 1
|
||||
) or (
|
||||
self.connection.features.can_return_columns_from_insert and obj_len == 1
|
||||
):
|
||||
rows = self.connection.ops.fetch_returned_insert_rows(cursor)
|
||||
cols = [field.get_col(opts.db_table) for field in self.returning_fields]
|
||||
elif self.connection.features.can_return_columns_from_insert:
|
||||
assert len(self.query.objs) == 1
|
||||
rows = [
|
||||
self.connection.ops.fetch_returned_insert_columns(
|
||||
cursor,
|
||||
self.returning_params,
|
||||
)
|
||||
]
|
||||
rows = self.connection.ops.fetch_returned_rows(
|
||||
cursor, self.returning_params
|
||||
)
|
||||
cols = [field.get_col(opts.db_table) for field in self.returning_fields]
|
||||
elif returning_fields and isinstance(
|
||||
returning_field := returning_fields[0], AutoField
|
||||
|
|
|
@ -402,6 +402,18 @@ backends.
|
|||
* :class:`~django.db.backends.base.schema.BaseDatabaseSchemaEditor` and
|
||||
PostgreSQL backends no longer use ``CASCADE`` when dropping a column.
|
||||
|
||||
* ``DatabaseOperations.return_insert_columns()`` and
|
||||
``DatabaseOperations.fetch_returned_insert_rows()`` methods are renamed to
|
||||
``returning_columns()`` and ``fetch_returned_rows()``, respectively, to
|
||||
denote they can be used in the context ``UPDATE … RETURNING`` statements as
|
||||
well as ``INSERT … RETURNING``.
|
||||
|
||||
* The ``DatabaseOperations.fetch_returned_insert_columns()`` method is removed
|
||||
and the ``fetch_returned_rows()`` method replacing
|
||||
``fetch_returned_insert_rows()`` expects both a ``cursor`` and
|
||||
``returning_params`` to be provided just like
|
||||
``fetch_returned_insert_columns()`` did.
|
||||
|
||||
Dropped support for MariaDB 10.5
|
||||
--------------------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user