summaryrefslogtreecommitdiff
path: root/django/db/backends
diff options
context:
space:
mode:
authorFrançois Freitag <mail@franek.fr>2016-06-03 15:31:21 -0700
committerTim Graham <timograham@gmail.com>2017-01-11 09:25:37 -0500
commitf3b7c059367a4e82bbfc7e4f0d42b10975e79f0c (patch)
treed64dedfcd04cfbe8d7599aa6411597cccba6eb17 /django/db/backends
parent53bffe8d03f01bd3214a5404998cb965fb28cd0b (diff)
Refs #16614 -- Made QuerySet.iterator() use server-side cursors on PostgreSQL.
Thanks to Josh Smeaton for the idea of implementing server-side cursors in PostgreSQL from the iterator method, and Anssi Kääriäinen and Kevin Turner for their previous work. Also Simon Charette and Tim Graham for review.
Diffstat (limited to 'django/db/backends')
-rw-r--r--django/db/backends/base/base.py31
-rw-r--r--django/db/backends/mysql/base.py2
-rw-r--r--django/db/backends/oracle/base.py2
-rw-r--r--django/db/backends/postgresql/base.py25
-rw-r--r--django/db/backends/sqlite3/base.py2
5 files changed, 48 insertions, 14 deletions
diff --git a/django/db/backends/base/base.py b/django/db/backends/base/base.py
index 38712bd4bf..9c35ca7e47 100644
--- a/django/db/backends/base/base.py
+++ b/django/db/backends/base/base.py
@@ -165,7 +165,7 @@ class BaseDatabaseWrapper(object):
"""Initializes the database connection settings."""
raise NotImplementedError('subclasses of BaseDatabaseWrapper may require an init_connection_state() method')
- def create_cursor(self):
+ def create_cursor(self, name=None):
"""Creates a cursor. Assumes that a connection is established."""
raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a create_cursor() method')
@@ -214,10 +214,21 @@ class BaseDatabaseWrapper(object):
# ##### Backend-specific wrappers for PEP-249 connection methods #####
- def _cursor(self):
+ def _prepare_cursor(self, cursor):
+ """
+ Validate the connection is usable and perform database cursor wrapping.
+ """
+ self.validate_thread_sharing()
+ if self.queries_logged:
+ wrapped_cursor = self.make_debug_cursor(cursor)
+ else:
+ wrapped_cursor = self.make_cursor(cursor)
+ return wrapped_cursor
+
+ def _cursor(self, name=None):
self.ensure_connection()
with self.wrap_database_errors:
- return self.create_cursor()
+ return self.create_cursor(name)
def _commit(self):
if self.connection is not None:
@@ -240,12 +251,7 @@ class BaseDatabaseWrapper(object):
"""
Creates a cursor, opening a connection if necessary.
"""
- self.validate_thread_sharing()
- if self.queries_logged:
- cursor = self.make_debug_cursor(self._cursor())
- else:
- cursor = self.make_cursor(self._cursor())
- return cursor
+ return self._prepare_cursor(self._cursor())
def commit(self):
"""
@@ -553,6 +559,13 @@ class BaseDatabaseWrapper(object):
"""
return DatabaseErrorWrapper(self)
+ def chunked_cursor(self):
+ """
+ Return a cursor that tries to avoid caching in the database (if
+ supported by the database), otherwise return a regular cursor.
+ """
+ return self.cursor()
+
def make_debug_cursor(self, cursor):
"""
Creates a cursor that logs all queries in self.queries_log.
diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
index 9681bb4f33..a6fa920f1e 100644
--- a/django/db/backends/mysql/base.py
+++ b/django/db/backends/mysql/base.py
@@ -271,7 +271,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
# with SQL standards.
cursor.execute('SET SQL_AUTO_IS_NULL = 0')
- def create_cursor(self):
+ def create_cursor(self, name=None):
cursor = self.connection.cursor()
return CursorWrapper(cursor)
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index c928e2dc6e..3488769bf7 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -249,7 +249,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
if not self.get_autocommit():
self.commit()
- def create_cursor(self):
+ def create_cursor(self, name=None):
return FormatStylePlaceholderCursor(self.connection)
def _commit(self):
diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py
index b4008532ee..91dee6a722 100644
--- a/django/db/backends/postgresql/base.py
+++ b/django/db/backends/postgresql/base.py
@@ -4,6 +4,7 @@ PostgreSQL database backend for Django.
Requires psycopg 2: http://initd.org/projects/psycopg2
"""
+import threading
import warnings
from django.conf import settings
@@ -145,6 +146,10 @@ class DatabaseWrapper(BaseDatabaseWrapper):
introspection_class = DatabaseIntrospection
ops_class = DatabaseOperations
+ def __init__(self, *args, **kwargs):
+ super(DatabaseWrapper, self).__init__(*args, **kwargs)
+ self._named_cursor_idx = 0
+
def get_connection_params(self):
settings_dict = self.settings_dict
# None may be used to connect to the default 'postgres' db
@@ -206,11 +211,27 @@ class DatabaseWrapper(BaseDatabaseWrapper):
if not self.get_autocommit():
self.connection.commit()
- def create_cursor(self):
- cursor = self.connection.cursor()
+ def create_cursor(self, name=None):
+ if name:
+ # In autocommit mode, the cursor will be used outside of a
+ # transaction, hence use a holdable cursor.
+ cursor = self.connection.cursor(name, scrollable=False, withhold=self.connection.autocommit)
+ else:
+ cursor = self.connection.cursor()
cursor.tzinfo_factory = utc_tzinfo_factory if settings.USE_TZ else None
return cursor
+ def chunked_cursor(self):
+ self._named_cursor_idx += 1
+ db_cursor = self._cursor(
+ name='_django_curs_%d_%d' % (
+ # Avoid reusing name in other threads
+ threading.current_thread().ident,
+ self._named_cursor_idx,
+ )
+ )
+ return self._prepare_cursor(db_cursor)
+
def _set_autocommit(self, autocommit):
with self.wrap_database_errors:
self.connection.autocommit = autocommit
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index ac7799bfa5..7bbbd6c2f8 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -215,7 +215,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def init_connection_state(self):
pass
- def create_cursor(self):
+ def create_cursor(self, name=None):
return self.connection.cursor(factory=SQLiteCursorWrapper)
def close(self):