|
4 | 4 | from django.db.models.expressions import Exists, OrderBy, Ref, Value |
5 | 5 | from django.db.models.functions import ConcatPair, Greatest, Least, Length, Substr |
6 | 6 | from django.db.models.sql import compiler |
| 7 | +from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE |
7 | 8 | from django.db.transaction import TransactionManagementError |
8 | 9 | from django.db.utils import DatabaseError |
9 | 10 | from django.utils import six |
@@ -72,6 +73,35 @@ def _as_sql_variance(self, compiler, connection): |
72 | 73 | function = '%sP' % function |
73 | 74 | return self.as_sql(compiler, connection, function=function) |
74 | 75 |
|
| 76 | +def _cursor_iter(cursor, sentinel, col_count): |
| 77 | + """ |
| 78 | + Yields blocks of rows from a cursor and ensures the cursor is closed when |
| 79 | + done. |
| 80 | + """ |
| 81 | + if cursor.db.supports_mars: |
| 82 | + # same as the original Django implementation |
| 83 | + try: |
| 84 | + for rows in iter((lambda: cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)), |
| 85 | + sentinel): |
| 86 | + yield [r[0:col_count] for r in rows] |
| 87 | + finally: |
| 88 | + cursor.close() |
| 89 | + else: |
| 90 | + # retrieve all chunks from the cursor and close it before yielding |
| 91 | + # so that we can open an another cursor over an iteration |
| 92 | + # (for drivers such as FreeTDS) |
| 93 | + chunks = [] |
| 94 | + try: |
| 95 | + for rows in iter((lambda: cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)), |
| 96 | + sentinel): |
| 97 | + chunks.append(rows) |
| 98 | + finally: |
| 99 | + cursor.close() |
| 100 | + for rows in chunks: |
| 101 | + yield [r[0:col_count] for r in rows] |
| 102 | + |
| 103 | +compiler.cursor_iter = _cursor_iter |
| 104 | + |
75 | 105 |
|
76 | 106 | class SQLCompiler(compiler.SQLCompiler): |
77 | 107 |
|
|
0 commit comments