mirror of
https://github.com/django/django.git
synced 2025-06-05 11:39:13 +00:00
Fixed #35918 -- Added support for execute_sql to directly return row counts.
This commit is contained in:
parent
d97cacc2ae
commit
ddefc3fed1
@ -26,7 +26,7 @@ from django.db.models.deletion import Collector
|
|||||||
from django.db.models.expressions import Case, F, Value, When
|
from django.db.models.expressions import Case, F, Value, When
|
||||||
from django.db.models.functions import Cast, Trunc
|
from django.db.models.functions import Cast, Trunc
|
||||||
from django.db.models.query_utils import FilteredRelation, Q
|
from django.db.models.query_utils import FilteredRelation, Q
|
||||||
from django.db.models.sql.constants import CURSOR, GET_ITERATOR_CHUNK_SIZE
|
from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE, ROW_COUNT
|
||||||
from django.db.models.utils import (
|
from django.db.models.utils import (
|
||||||
AltersData,
|
AltersData,
|
||||||
create_namedtuple_class,
|
create_namedtuple_class,
|
||||||
@ -1209,11 +1209,7 @@ class QuerySet(AltersData):
|
|||||||
"""
|
"""
|
||||||
query = self.query.clone()
|
query = self.query.clone()
|
||||||
query.__class__ = sql.DeleteQuery
|
query.__class__ = sql.DeleteQuery
|
||||||
cursor = query.get_compiler(using).execute_sql(CURSOR)
|
return query.get_compiler(using).execute_sql(ROW_COUNT)
|
||||||
if cursor:
|
|
||||||
with cursor:
|
|
||||||
return cursor.rowcount
|
|
||||||
return 0
|
|
||||||
|
|
||||||
_raw_delete.alters_data = True
|
_raw_delete.alters_data = True
|
||||||
|
|
||||||
@ -1252,7 +1248,7 @@ class QuerySet(AltersData):
|
|||||||
# Clear any annotations so that they won't be present in subqueries.
|
# Clear any annotations so that they won't be present in subqueries.
|
||||||
query.annotations = {}
|
query.annotations = {}
|
||||||
with transaction.mark_for_rollback_on_error(using=self.db):
|
with transaction.mark_for_rollback_on_error(using=self.db):
|
||||||
rows = query.get_compiler(self.db).execute_sql(CURSOR)
|
rows = query.get_compiler(self.db).execute_sql(ROW_COUNT)
|
||||||
self._result_cache = None
|
self._result_cache = None
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
@ -1277,7 +1273,7 @@ class QuerySet(AltersData):
|
|||||||
# Clear any annotations so that they won't be present in subqueries.
|
# Clear any annotations so that they won't be present in subqueries.
|
||||||
query.annotations = {}
|
query.annotations = {}
|
||||||
self._result_cache = None
|
self._result_cache = None
|
||||||
return query.get_compiler(self.db).execute_sql(CURSOR)
|
return query.get_compiler(self.db).execute_sql(ROW_COUNT)
|
||||||
|
|
||||||
_update.alters_data = True
|
_update.alters_data = True
|
||||||
_update.queryset_only = False
|
_update.queryset_only = False
|
||||||
|
@ -19,6 +19,7 @@ from django.db.models.sql.constants import (
|
|||||||
MULTI,
|
MULTI,
|
||||||
NO_RESULTS,
|
NO_RESULTS,
|
||||||
ORDER_DIR,
|
ORDER_DIR,
|
||||||
|
ROW_COUNT,
|
||||||
SINGLE,
|
SINGLE,
|
||||||
)
|
)
|
||||||
from django.db.models.sql.query import Query, get_order_dir
|
from django.db.models.sql.query import Query, get_order_dir
|
||||||
@ -1596,15 +1597,15 @@ class SQLCompiler:
|
|||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Run the query against the database and return the result(s). The
|
Run the query against the database and return the result(s). The
|
||||||
return value is a single data item if result_type is SINGLE, or an
|
return value depends on the value of result_type.
|
||||||
iterator over the results if the result_type is MULTI.
|
|
||||||
|
|
||||||
result_type is either MULTI (use fetchmany() to retrieve all rows),
|
When result_type is:
|
||||||
SINGLE (only retrieve a single row), or None. In this last case, the
|
- MULTI: Retrieves all rows using fetchmany(). Wraps in an iterator for
|
||||||
cursor is returned if any query is executed, since it's used by
|
chunked reads when supported.
|
||||||
subclasses such as InsertQuery). It's possible, however, that no query
|
- SINGLE: Retrieves a single row using fetchone().
|
||||||
is needed, as the filters describe an empty set. In that case, None is
|
- ROW_COUNT: Retrieves the number of rows in the result.
|
||||||
returned, to avoid any unnecessary database interaction.
|
- CURSOR: Runs the query, and returns the cursor object. It is the
|
||||||
|
caller's responsibility to close the cursor.
|
||||||
"""
|
"""
|
||||||
result_type = result_type or NO_RESULTS
|
result_type = result_type or NO_RESULTS
|
||||||
try:
|
try:
|
||||||
@ -1627,6 +1628,11 @@ class SQLCompiler:
|
|||||||
cursor.close()
|
cursor.close()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
if result_type == ROW_COUNT:
|
||||||
|
try:
|
||||||
|
return cursor.rowcount
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
if result_type == CURSOR:
|
if result_type == CURSOR:
|
||||||
# Give the caller the cursor to process and close.
|
# Give the caller the cursor to process and close.
|
||||||
return cursor
|
return cursor
|
||||||
@ -2069,19 +2075,19 @@ class SQLUpdateCompiler(SQLCompiler):
|
|||||||
non-empty query that is executed. Row counts for any subsequent,
|
non-empty query that is executed. Row counts for any subsequent,
|
||||||
related queries are not available.
|
related queries are not available.
|
||||||
"""
|
"""
|
||||||
cursor = super().execute_sql(result_type)
|
row_count = super().execute_sql(result_type)
|
||||||
try:
|
is_empty = row_count is None
|
||||||
rows = cursor.rowcount if cursor else 0
|
row_count = row_count or 0
|
||||||
is_empty = cursor is None
|
|
||||||
finally:
|
|
||||||
if cursor:
|
|
||||||
cursor.close()
|
|
||||||
for query in self.query.get_related_updates():
|
for query in self.query.get_related_updates():
|
||||||
aux_rows = query.get_compiler(self.using).execute_sql(result_type)
|
# If the result_type is NO_RESULTS then the aux_row_count is None.
|
||||||
if is_empty and aux_rows:
|
aux_row_count = query.get_compiler(self.using).execute_sql(result_type)
|
||||||
rows = aux_rows
|
if is_empty and aux_row_count:
|
||||||
|
# Returns the row count for any related updates as the number of
|
||||||
|
# rows updated.
|
||||||
|
row_count = aux_row_count
|
||||||
is_empty = False
|
is_empty = False
|
||||||
return rows
|
return row_count
|
||||||
|
|
||||||
def pre_sql_setup(self):
|
def pre_sql_setup(self):
|
||||||
"""
|
"""
|
||||||
|
@ -11,8 +11,10 @@ GET_ITERATOR_CHUNK_SIZE = 100
|
|||||||
# How many results to expect from a cursor.execute call
|
# How many results to expect from a cursor.execute call
|
||||||
MULTI = "multi"
|
MULTI = "multi"
|
||||||
SINGLE = "single"
|
SINGLE = "single"
|
||||||
CURSOR = "cursor"
|
|
||||||
NO_RESULTS = "no results"
|
NO_RESULTS = "no results"
|
||||||
|
# Rather than returning results, returns:
|
||||||
|
CURSOR = "cursor"
|
||||||
|
ROW_COUNT = "row count"
|
||||||
|
|
||||||
ORDER_DIR = {
|
ORDER_DIR = {
|
||||||
"ASC": ("ASC", "DESC"),
|
"ASC": ("ASC", "DESC"),
|
||||||
|
@ -3,7 +3,11 @@ Query subclasses which provide extra functionality beyond simple data retrieval.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from django.db.models.sql.constants import CURSOR, GET_ITERATOR_CHUNK_SIZE, NO_RESULTS
|
from django.db.models.sql.constants import (
|
||||||
|
GET_ITERATOR_CHUNK_SIZE,
|
||||||
|
NO_RESULTS,
|
||||||
|
ROW_COUNT,
|
||||||
|
)
|
||||||
from django.db.models.sql.query import Query
|
from django.db.models.sql.query import Query
|
||||||
|
|
||||||
__all__ = ["DeleteQuery", "UpdateQuery", "InsertQuery", "AggregateQuery"]
|
__all__ = ["DeleteQuery", "UpdateQuery", "InsertQuery", "AggregateQuery"]
|
||||||
@ -17,11 +21,7 @@ class DeleteQuery(Query):
|
|||||||
def do_query(self, table, where, using):
|
def do_query(self, table, where, using):
|
||||||
self.alias_map = {table: self.alias_map[table]}
|
self.alias_map = {table: self.alias_map[table]}
|
||||||
self.where = where
|
self.where = where
|
||||||
cursor = self.get_compiler(using).execute_sql(CURSOR)
|
return self.get_compiler(using).execute_sql(ROW_COUNT)
|
||||||
if cursor:
|
|
||||||
with cursor:
|
|
||||||
return cursor.rowcount
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def delete_batch(self, pk_list, using):
|
def delete_batch(self, pk_list, using):
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user