mirror of
https://github.com/django/django.git
synced 2025-09-07 04:45:07 +03:00
Harmonize cursors used by psycopg3 and add more docs.
This commit is contained in:
parent
e7a9d756ee
commit
2709ee9cf1
|
@ -268,20 +268,12 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
|
|
||||||
conn_params.pop("assume_role", None)
|
conn_params.pop("assume_role", None)
|
||||||
conn_params.pop("isolation_level", None)
|
conn_params.pop("isolation_level", None)
|
||||||
|
conn_params.pop("server_side_binding", None)
|
||||||
|
|
||||||
pool_options = conn_params.pop("pool", None)
|
pool_options = conn_params.pop("pool", None)
|
||||||
if pool_options and not is_psycopg3:
|
if pool_options and not is_psycopg3:
|
||||||
raise ImproperlyConfigured("Database pooling requires psycopg >= 3")
|
raise ImproperlyConfigured("Database pooling requires psycopg >= 3")
|
||||||
|
|
||||||
server_side_binding = conn_params.pop("server_side_binding", None)
|
|
||||||
conn_params.setdefault(
|
|
||||||
"cursor_factory",
|
|
||||||
(
|
|
||||||
ServerBindingCursor
|
|
||||||
if is_psycopg3 and server_side_binding is True
|
|
||||||
else Cursor
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if settings_dict["USER"]:
|
if settings_dict["USER"]:
|
||||||
conn_params["user"] = settings_dict["USER"]
|
conn_params["user"] = settings_dict["USER"]
|
||||||
if settings_dict["PASSWORD"]:
|
if settings_dict["PASSWORD"]:
|
||||||
|
@ -296,9 +288,8 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
)
|
)
|
||||||
# Disable prepared statements by default to keep connection poolers
|
# Disable prepared statements by default to keep connection poolers
|
||||||
# working. Can be reenabled via OPTIONS in the settings dict.
|
# working. Can be reenabled via OPTIONS in the settings dict.
|
||||||
conn_params["prepare_threshold"] = conn_params.pop(
|
if "prepare_threshold" not in conn_params:
|
||||||
"prepare_threshold", None
|
conn_params["prepare_threshold"] = None
|
||||||
)
|
|
||||||
return conn_params
|
return conn_params
|
||||||
|
|
||||||
@async_unsafe
|
@async_unsafe
|
||||||
|
@ -405,28 +396,20 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
|
|
||||||
@async_unsafe
|
@async_unsafe
|
||||||
def create_cursor(self, name=None):
|
def create_cursor(self, name=None):
|
||||||
|
ssb = self.settings_dict["OPTIONS"].get("server_side_binding")
|
||||||
|
cursor_factory = self.settings_dict["OPTIONS"].get("cursor_factory")
|
||||||
|
|
||||||
if name:
|
if name:
|
||||||
if is_psycopg3 and (
|
# In autocommit mode, the cursor will be used outside of a
|
||||||
self.settings_dict["OPTIONS"].get("server_side_binding") is not True
|
# transaction, hence use a holdable cursor.
|
||||||
):
|
cursor = _server_cursor_factory(ssb, cursor_factory)(
|
||||||
# psycopg >= 3 forces the usage of server-side bindings for
|
self.connection,
|
||||||
# named cursors so a specialized class that implements
|
name=name,
|
||||||
# server-side cursors while performing client-side bindings
|
scrollable=False,
|
||||||
# must be used if `server_side_binding` is disabled (default).
|
withhold=self.connection.autocommit,
|
||||||
cursor = ServerSideCursor(
|
)
|
||||||
self.connection,
|
|
||||||
name=name,
|
|
||||||
scrollable=False,
|
|
||||||
withhold=self.connection.autocommit,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# 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:
|
else:
|
||||||
cursor = self.connection.cursor()
|
cursor = _cursor_factory(ssb, cursor_factory)(self.connection)
|
||||||
|
|
||||||
if is_psycopg3:
|
if is_psycopg3:
|
||||||
# Register the cursor timezone only if the connection disagrees, to
|
# Register the cursor timezone only if the connection disagrees, to
|
||||||
|
@ -571,15 +554,32 @@ if is_psycopg3:
|
||||||
return args
|
return args
|
||||||
|
|
||||||
class ServerBindingCursor(CursorMixin, Database.Cursor):
|
class ServerBindingCursor(CursorMixin, Database.Cursor):
|
||||||
pass
|
"""
|
||||||
|
Cursor that performs server-side parameter binding.
|
||||||
|
|
||||||
|
This is the default in psycopg3.
|
||||||
|
"""
|
||||||
|
|
||||||
class Cursor(CursorMixin, Database.ClientCursor):
|
class Cursor(CursorMixin, Database.ClientCursor):
|
||||||
pass
|
|
||||||
|
|
||||||
class ServerSideCursor(
|
|
||||||
CursorMixin, Database.client_cursor.ClientCursorMixin, Database.ServerCursor
|
|
||||||
):
|
|
||||||
"""
|
"""
|
||||||
|
Cursor that performs client-side parameter binding.
|
||||||
|
|
||||||
|
This is the default in Django.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class ServerCursor(CursorMixin, Database.ServerCursor):
|
||||||
|
"""
|
||||||
|
Cursor that performs server-side parameter binding and uses
|
||||||
|
server side cursors.
|
||||||
|
|
||||||
|
This is the default for named cursors in psycopg3.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class ServerSideCursor(Database.client_cursor.ClientCursorMixin, ServerCursor):
|
||||||
|
"""
|
||||||
|
Cursor that performs client-side parameter binding and uses
|
||||||
|
server side cursors.
|
||||||
|
|
||||||
psycopg >= 3 forces the usage of server-side bindings when using named
|
psycopg >= 3 forces the usage of server-side bindings when using named
|
||||||
cursors but the ORM doesn't yet support the systematic generation of
|
cursors but the ORM doesn't yet support the systematic generation of
|
||||||
prepareable SQL (#20516).
|
prepareable SQL (#20516).
|
||||||
|
@ -591,8 +591,18 @@ if is_psycopg3:
|
||||||
Mixing ClientCursorMixin in wouldn't be necessary if Cursor allowed to
|
Mixing ClientCursorMixin in wouldn't be necessary if Cursor allowed to
|
||||||
specify how parameters should be bound instead, which ServerCursor
|
specify how parameters should be bound instead, which ServerCursor
|
||||||
would inherit, but that's not the case.
|
would inherit, but that's not the case.
|
||||||
|
|
||||||
|
This is the default for named cursors in Django.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def _cursor_factory(server_side_binding, cursor_factory):
|
||||||
|
if cursor_factory:
|
||||||
|
return cursor_factory
|
||||||
|
return ServerBindingCursor if server_side_binding else Cursor
|
||||||
|
|
||||||
|
def _server_cursor_factory(server_side_binding, cursor_factory):
|
||||||
|
return ServerCursor if server_side_binding else ServerSideCursor
|
||||||
|
|
||||||
class CursorDebugWrapper(BaseCursorDebugWrapper):
|
class CursorDebugWrapper(BaseCursorDebugWrapper):
|
||||||
def copy(self, statement):
|
def copy(self, statement):
|
||||||
with self.debug_sql(statement):
|
with self.debug_sql(statement):
|
||||||
|
@ -601,6 +611,11 @@ if is_psycopg3:
|
||||||
else:
|
else:
|
||||||
Cursor = psycopg2.extensions.cursor
|
Cursor = psycopg2.extensions.cursor
|
||||||
|
|
||||||
|
def _cursor_factory(server_side_binding, cursor_factory):
|
||||||
|
return cursor_factory or Cursor
|
||||||
|
|
||||||
|
_server_cursor_factory = _cursor_factory
|
||||||
|
|
||||||
class CursorDebugWrapper(BaseCursorDebugWrapper):
|
class CursorDebugWrapper(BaseCursorDebugWrapper):
|
||||||
def copy_expert(self, sql, file, *args):
|
def copy_expert(self, sql, file, *args):
|
||||||
with self.debug_sql(sql):
|
with self.debug_sql(sql):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user