1
+ from itertools import chain
2
+
1
3
from django .core .exceptions import EmptyResultSet , FullResultSet
2
4
from django .db import DatabaseError , IntegrityError , NotSupportedError
3
5
from django .db .models import Count , Expression
4
6
from django .db .models .aggregates import Aggregate
5
7
from django .db .models .expressions import OrderBy
6
8
from django .db .models .sql import compiler
7
- from django .db .models .sql .constants import GET_ITERATOR_CHUNK_SIZE , MULTI , ORDER_DIR
9
+ from django .db .models .sql .constants import GET_ITERATOR_CHUNK_SIZE , MULTI , ORDER_DIR , SINGLE
8
10
from django .utils .functional import cached_property
9
11
10
12
from .base import Cursor
@@ -33,11 +35,23 @@ def execute_sql(
33
35
except EmptyResultSet :
34
36
return iter ([]) if result_type == MULTI else None
35
37
36
- return (
37
- (self ._make_result (row , columns ) for row in query .fetch ())
38
- if result_type == MULTI
39
- else self ._make_result (next (query .fetch ()), columns )
40
- )
38
+ cursor = query .get_cursor ()
39
+
40
+ if result_type == SINGLE :
41
+ return self ._make_result (next (cursor ))
42
+
43
+ if result_type == MULTI :
44
+ result = self .cursor_iter (cursor , chunk_size , columns )
45
+
46
+ if not chunked_fetch or not self .connection .features .can_use_chunked_reads :
47
+ # If using non-chunked reads, return the same data structure as
48
+ # normally, but ensure it is all read into memory before going
49
+ # any further. Use chunked_fetch if requested,
50
+ # unless the database doesn't support it.
51
+ return list (result )
52
+ return result
53
+
54
+ raise ValueError ("shouldn't get here" )
41
55
42
56
def results_iter (
43
57
self ,
@@ -49,14 +63,16 @@ def results_iter(
49
63
"""
50
64
Return an iterator over the results from executing query given
51
65
to this compiler. Called by QuerySet methods.
66
+
67
+ XXX: Can this method be removed?
52
68
"""
53
69
if results is None :
54
70
# QuerySet.values() or values_list()
55
71
results = self .execute_sql (MULTI , chunked_fetch = chunked_fetch , chunk_size = chunk_size )
56
72
57
73
fields = [s [0 ] for s in self .select [0 : self .col_count ]]
58
74
converters = self .get_converters (fields )
59
- rows = results
75
+ rows = chain . from_iterable ( results )
60
76
if converters :
61
77
rows = self .apply_converters (rows , converters )
62
78
if tuple_expected :
@@ -86,6 +102,15 @@ def _make_result(self, entity, columns):
86
102
result .append (obj .get (name ))
87
103
return result
88
104
105
+ def cursor_iter (self , cursor , chunk_size , columns ):
106
+ """Generator to yield chunks from cursor."""
107
+ chunk = []
108
+ for i , row in enumerate (cursor ):
109
+ if i % chunk_size == 0 and i > 0 :
110
+ yield chunk
111
+ chunk .append (self ._make_result (row , columns ))
112
+ yield chunk
113
+
89
114
def check_query (self ):
90
115
"""Check if the current query is supported by the database."""
91
116
if self .query .is_empty ():
@@ -293,8 +318,14 @@ def check_query(self):
293
318
)
294
319
295
320
296
- class SQLUpdateCompiler (SQLCompiler ):
321
+ class SQLUpdateCompiler (compiler . SQLUpdateCompiler , SQLCompiler ):
297
322
def execute_sql (self , result_type ):
323
+ """
324
+ Execute the specified update. Return the number of rows affected by
325
+ the primary update query. The "primary update query" is the first
326
+ non-empty query that is executed. Row counts for any subsequent,
327
+ related queries are not available.
328
+ """
298
329
self .pre_sql_setup ()
299
330
values = []
300
331
for field , _ , value in self .query .values :
@@ -309,7 +340,17 @@ def execute_sql(self, result_type):
309
340
)
310
341
prepared = field .get_db_prep_save (value , connection = self .connection )
311
342
values .append ((field , prepared ))
312
- return self .update (values )
343
+ # if is_empty := not bool(values):
344
+ # rows = 0
345
+ # else:
346
+ # rows = self.update(values)
347
+ rows = 0 if (is_empty := not bool (values )) else self .update (values )
348
+ for query in self .query .get_related_updates ():
349
+ aux_rows = query .get_compiler (self .using ).execute_sql (result_type )
350
+ if is_empty and aux_rows :
351
+ rows = aux_rows
352
+ is_empty = False
353
+ return rows
313
354
314
355
def update (self , values ):
315
356
spec = {}
0 commit comments