before send to remote
This commit is contained in:
0
env/lib/python3.8/site-packages/django/db/backends/base/__init__.py
vendored
Normal file
0
env/lib/python3.8/site-packages/django/db/backends/base/__init__.py
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/__init__.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/__init__.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/base.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/base.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/client.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/client.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/creation.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/creation.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/features.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/features.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/introspection.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/introspection.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/operations.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/operations.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/schema.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/schema.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/validation.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/db/backends/base/__pycache__/validation.cpython-38.pyc
vendored
Normal file
Binary file not shown.
770
env/lib/python3.8/site-packages/django/db/backends/base/base.py
vendored
Normal file
770
env/lib/python3.8/site-packages/django/db/backends/base/base.py
vendored
Normal file
@@ -0,0 +1,770 @@
|
||||
import _thread
|
||||
import copy
|
||||
import datetime
|
||||
import threading
|
||||
import time
|
||||
import warnings
|
||||
from collections import deque
|
||||
from contextlib import contextmanager
|
||||
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError:
|
||||
from backports import zoneinfo
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import DEFAULT_DB_ALIAS, DatabaseError, NotSupportedError
|
||||
from django.db.backends import utils
|
||||
from django.db.backends.base.validation import BaseDatabaseValidation
|
||||
from django.db.backends.signals import connection_created
|
||||
from django.db.transaction import TransactionManagementError
|
||||
from django.db.utils import DatabaseErrorWrapper
|
||||
from django.utils.asyncio import async_unsafe
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
NO_DB_ALIAS = "__no_db__"
|
||||
RAN_DB_VERSION_CHECK = set()
|
||||
|
||||
|
||||
# RemovedInDjango50Warning
|
||||
def timezone_constructor(tzname):
|
||||
if settings.USE_DEPRECATED_PYTZ:
|
||||
import pytz
|
||||
|
||||
return pytz.timezone(tzname)
|
||||
return zoneinfo.ZoneInfo(tzname)
|
||||
|
||||
|
||||
class BaseDatabaseWrapper:
|
||||
"""Represent a database connection."""
|
||||
|
||||
# Mapping of Field objects to their column types.
|
||||
data_types = {}
|
||||
# Mapping of Field objects to their SQL suffix such as AUTOINCREMENT.
|
||||
data_types_suffix = {}
|
||||
# Mapping of Field objects to their SQL for CHECK constraints.
|
||||
data_type_check_constraints = {}
|
||||
ops = None
|
||||
vendor = "unknown"
|
||||
display_name = "unknown"
|
||||
SchemaEditorClass = None
|
||||
# Classes instantiated in __init__().
|
||||
client_class = None
|
||||
creation_class = None
|
||||
features_class = None
|
||||
introspection_class = None
|
||||
ops_class = None
|
||||
validation_class = BaseDatabaseValidation
|
||||
|
||||
queries_limit = 9000
|
||||
|
||||
def __init__(self, settings_dict, alias=DEFAULT_DB_ALIAS):
|
||||
# Connection related attributes.
|
||||
# The underlying database connection.
|
||||
self.connection = None
|
||||
# `settings_dict` should be a dictionary containing keys such as
|
||||
# NAME, USER, etc. It's called `settings_dict` instead of `settings`
|
||||
# to disambiguate it from Django settings modules.
|
||||
self.settings_dict = settings_dict
|
||||
self.alias = alias
|
||||
# Query logging in debug mode or when explicitly enabled.
|
||||
self.queries_log = deque(maxlen=self.queries_limit)
|
||||
self.force_debug_cursor = False
|
||||
|
||||
# Transaction related attributes.
|
||||
# Tracks if the connection is in autocommit mode. Per PEP 249, by
|
||||
# default, it isn't.
|
||||
self.autocommit = False
|
||||
# Tracks if the connection is in a transaction managed by 'atomic'.
|
||||
self.in_atomic_block = False
|
||||
# Increment to generate unique savepoint ids.
|
||||
self.savepoint_state = 0
|
||||
# List of savepoints created by 'atomic'.
|
||||
self.savepoint_ids = []
|
||||
# Stack of active 'atomic' blocks.
|
||||
self.atomic_blocks = []
|
||||
# Tracks if the outermost 'atomic' block should commit on exit,
|
||||
# ie. if autocommit was active on entry.
|
||||
self.commit_on_exit = True
|
||||
# Tracks if the transaction should be rolled back to the next
|
||||
# available savepoint because of an exception in an inner block.
|
||||
self.needs_rollback = False
|
||||
|
||||
# Connection termination related attributes.
|
||||
self.close_at = None
|
||||
self.closed_in_transaction = False
|
||||
self.errors_occurred = False
|
||||
self.health_check_enabled = False
|
||||
self.health_check_done = False
|
||||
|
||||
# Thread-safety related attributes.
|
||||
self._thread_sharing_lock = threading.Lock()
|
||||
self._thread_sharing_count = 0
|
||||
self._thread_ident = _thread.get_ident()
|
||||
|
||||
# A list of no-argument functions to run when the transaction commits.
|
||||
# Each entry is an (sids, func) tuple, where sids is a set of the
|
||||
# active savepoint IDs when this function was registered.
|
||||
self.run_on_commit = []
|
||||
|
||||
# Should we run the on-commit hooks the next time set_autocommit(True)
|
||||
# is called?
|
||||
self.run_commit_hooks_on_set_autocommit_on = False
|
||||
|
||||
# A stack of wrappers to be invoked around execute()/executemany()
|
||||
# calls. Each entry is a function taking five arguments: execute, sql,
|
||||
# params, many, and context. It's the function's responsibility to
|
||||
# call execute(sql, params, many, context).
|
||||
self.execute_wrappers = []
|
||||
|
||||
self.client = self.client_class(self)
|
||||
self.creation = self.creation_class(self)
|
||||
self.features = self.features_class(self)
|
||||
self.introspection = self.introspection_class(self)
|
||||
self.ops = self.ops_class(self)
|
||||
self.validation = self.validation_class(self)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"<{self.__class__.__qualname__} "
|
||||
f"vendor={self.vendor!r} alias={self.alias!r}>"
|
||||
)
|
||||
|
||||
def ensure_timezone(self):
|
||||
"""
|
||||
Ensure the connection's timezone is set to `self.timezone_name` and
|
||||
return whether it changed or not.
|
||||
"""
|
||||
return False
|
||||
|
||||
@cached_property
|
||||
def timezone(self):
|
||||
"""
|
||||
Return a tzinfo of the database connection time zone.
|
||||
|
||||
This is only used when time zone support is enabled. When a datetime is
|
||||
read from the database, it is always returned in this time zone.
|
||||
|
||||
When the database backend supports time zones, it doesn't matter which
|
||||
time zone Django uses, as long as aware datetimes are used everywhere.
|
||||
Other users connecting to the database can choose their own time zone.
|
||||
|
||||
When the database backend doesn't support time zones, the time zone
|
||||
Django uses may be constrained by the requirements of other users of
|
||||
the database.
|
||||
"""
|
||||
if not settings.USE_TZ:
|
||||
return None
|
||||
elif self.settings_dict["TIME_ZONE"] is None:
|
||||
return datetime.timezone.utc
|
||||
else:
|
||||
return timezone_constructor(self.settings_dict["TIME_ZONE"])
|
||||
|
||||
@cached_property
|
||||
def timezone_name(self):
|
||||
"""
|
||||
Name of the time zone of the database connection.
|
||||
"""
|
||||
if not settings.USE_TZ:
|
||||
return settings.TIME_ZONE
|
||||
elif self.settings_dict["TIME_ZONE"] is None:
|
||||
return "UTC"
|
||||
else:
|
||||
return self.settings_dict["TIME_ZONE"]
|
||||
|
||||
@property
|
||||
def queries_logged(self):
|
||||
return self.force_debug_cursor or settings.DEBUG
|
||||
|
||||
@property
|
||||
def queries(self):
|
||||
if len(self.queries_log) == self.queries_log.maxlen:
|
||||
warnings.warn(
|
||||
"Limit for query logging exceeded, only the last {} queries "
|
||||
"will be returned.".format(self.queries_log.maxlen)
|
||||
)
|
||||
return list(self.queries_log)
|
||||
|
||||
def get_database_version(self):
|
||||
"""Return a tuple of the database's version."""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseWrapper may require a get_database_version() "
|
||||
"method."
|
||||
)
|
||||
|
||||
def check_database_version_supported(self):
|
||||
"""
|
||||
Raise an error if the database version isn't supported by this
|
||||
version of Django.
|
||||
"""
|
||||
if (
|
||||
self.features.minimum_database_version is not None
|
||||
and self.get_database_version() < self.features.minimum_database_version
|
||||
):
|
||||
db_version = ".".join(map(str, self.get_database_version()))
|
||||
min_db_version = ".".join(map(str, self.features.minimum_database_version))
|
||||
raise NotSupportedError(
|
||||
f"{self.display_name} {min_db_version} or later is required "
|
||||
f"(found {db_version})."
|
||||
)
|
||||
|
||||
# ##### Backend-specific methods for creating connections and cursors #####
|
||||
|
||||
def get_connection_params(self):
|
||||
"""Return a dict of parameters suitable for get_new_connection."""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseWrapper may require a get_connection_params() "
|
||||
"method"
|
||||
)
|
||||
|
||||
def get_new_connection(self, conn_params):
|
||||
"""Open a connection to the database."""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseWrapper may require a get_new_connection() "
|
||||
"method"
|
||||
)
|
||||
|
||||
def init_connection_state(self):
|
||||
"""Initialize the database connection settings."""
|
||||
global RAN_DB_VERSION_CHECK
|
||||
if self.alias not in RAN_DB_VERSION_CHECK:
|
||||
self.check_database_version_supported()
|
||||
RAN_DB_VERSION_CHECK.add(self.alias)
|
||||
|
||||
def create_cursor(self, name=None):
|
||||
"""Create a cursor. Assume that a connection is established."""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseWrapper may require a create_cursor() method"
|
||||
)
|
||||
|
||||
# ##### Backend-specific methods for creating connections #####
|
||||
|
||||
@async_unsafe
|
||||
def connect(self):
|
||||
"""Connect to the database. Assume that the connection is closed."""
|
||||
# Check for invalid configurations.
|
||||
self.check_settings()
|
||||
# In case the previous connection was closed while in an atomic block
|
||||
self.in_atomic_block = False
|
||||
self.savepoint_ids = []
|
||||
self.atomic_blocks = []
|
||||
self.needs_rollback = False
|
||||
# Reset parameters defining when to close/health-check the connection.
|
||||
self.health_check_enabled = self.settings_dict["CONN_HEALTH_CHECKS"]
|
||||
max_age = self.settings_dict["CONN_MAX_AGE"]
|
||||
self.close_at = None if max_age is None else time.monotonic() + max_age
|
||||
self.closed_in_transaction = False
|
||||
self.errors_occurred = False
|
||||
# New connections are healthy.
|
||||
self.health_check_done = True
|
||||
# Establish the connection
|
||||
conn_params = self.get_connection_params()
|
||||
self.connection = self.get_new_connection(conn_params)
|
||||
self.set_autocommit(self.settings_dict["AUTOCOMMIT"])
|
||||
self.init_connection_state()
|
||||
connection_created.send(sender=self.__class__, connection=self)
|
||||
|
||||
self.run_on_commit = []
|
||||
|
||||
def check_settings(self):
|
||||
if self.settings_dict["TIME_ZONE"] is not None and not settings.USE_TZ:
|
||||
raise ImproperlyConfigured(
|
||||
"Connection '%s' cannot set TIME_ZONE because USE_TZ is False."
|
||||
% self.alias
|
||||
)
|
||||
|
||||
@async_unsafe
|
||||
def ensure_connection(self):
|
||||
"""Guarantee that a connection to the database is established."""
|
||||
if self.connection is None:
|
||||
with self.wrap_database_errors:
|
||||
self.connect()
|
||||
|
||||
# ##### Backend-specific wrappers for PEP-249 connection methods #####
|
||||
|
||||
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.close_if_health_check_failed()
|
||||
self.ensure_connection()
|
||||
with self.wrap_database_errors:
|
||||
return self._prepare_cursor(self.create_cursor(name))
|
||||
|
||||
def _commit(self):
|
||||
if self.connection is not None:
|
||||
with self.wrap_database_errors:
|
||||
return self.connection.commit()
|
||||
|
||||
def _rollback(self):
|
||||
if self.connection is not None:
|
||||
with self.wrap_database_errors:
|
||||
return self.connection.rollback()
|
||||
|
||||
def _close(self):
|
||||
if self.connection is not None:
|
||||
with self.wrap_database_errors:
|
||||
return self.connection.close()
|
||||
|
||||
# ##### Generic wrappers for PEP-249 connection methods #####
|
||||
|
||||
@async_unsafe
|
||||
def cursor(self):
|
||||
"""Create a cursor, opening a connection if necessary."""
|
||||
return self._cursor()
|
||||
|
||||
@async_unsafe
|
||||
def commit(self):
|
||||
"""Commit a transaction and reset the dirty flag."""
|
||||
self.validate_thread_sharing()
|
||||
self.validate_no_atomic_block()
|
||||
self._commit()
|
||||
# A successful commit means that the database connection works.
|
||||
self.errors_occurred = False
|
||||
self.run_commit_hooks_on_set_autocommit_on = True
|
||||
|
||||
@async_unsafe
|
||||
def rollback(self):
|
||||
"""Roll back a transaction and reset the dirty flag."""
|
||||
self.validate_thread_sharing()
|
||||
self.validate_no_atomic_block()
|
||||
self._rollback()
|
||||
# A successful rollback means that the database connection works.
|
||||
self.errors_occurred = False
|
||||
self.needs_rollback = False
|
||||
self.run_on_commit = []
|
||||
|
||||
@async_unsafe
|
||||
def close(self):
|
||||
"""Close the connection to the database."""
|
||||
self.validate_thread_sharing()
|
||||
self.run_on_commit = []
|
||||
|
||||
# Don't call validate_no_atomic_block() to avoid making it difficult
|
||||
# to get rid of a connection in an invalid state. The next connect()
|
||||
# will reset the transaction state anyway.
|
||||
if self.closed_in_transaction or self.connection is None:
|
||||
return
|
||||
try:
|
||||
self._close()
|
||||
finally:
|
||||
if self.in_atomic_block:
|
||||
self.closed_in_transaction = True
|
||||
self.needs_rollback = True
|
||||
else:
|
||||
self.connection = None
|
||||
|
||||
# ##### Backend-specific savepoint management methods #####
|
||||
|
||||
def _savepoint(self, sid):
|
||||
with self.cursor() as cursor:
|
||||
cursor.execute(self.ops.savepoint_create_sql(sid))
|
||||
|
||||
def _savepoint_rollback(self, sid):
|
||||
with self.cursor() as cursor:
|
||||
cursor.execute(self.ops.savepoint_rollback_sql(sid))
|
||||
|
||||
def _savepoint_commit(self, sid):
|
||||
with self.cursor() as cursor:
|
||||
cursor.execute(self.ops.savepoint_commit_sql(sid))
|
||||
|
||||
def _savepoint_allowed(self):
|
||||
# Savepoints cannot be created outside a transaction
|
||||
return self.features.uses_savepoints and not self.get_autocommit()
|
||||
|
||||
# ##### Generic savepoint management methods #####
|
||||
|
||||
@async_unsafe
|
||||
def savepoint(self):
|
||||
"""
|
||||
Create a savepoint inside the current transaction. Return an
|
||||
identifier for the savepoint that will be used for the subsequent
|
||||
rollback or commit. Do nothing if savepoints are not supported.
|
||||
"""
|
||||
if not self._savepoint_allowed():
|
||||
return
|
||||
|
||||
thread_ident = _thread.get_ident()
|
||||
tid = str(thread_ident).replace("-", "")
|
||||
|
||||
self.savepoint_state += 1
|
||||
sid = "s%s_x%d" % (tid, self.savepoint_state)
|
||||
|
||||
self.validate_thread_sharing()
|
||||
self._savepoint(sid)
|
||||
|
||||
return sid
|
||||
|
||||
@async_unsafe
|
||||
def savepoint_rollback(self, sid):
|
||||
"""
|
||||
Roll back to a savepoint. Do nothing if savepoints are not supported.
|
||||
"""
|
||||
if not self._savepoint_allowed():
|
||||
return
|
||||
|
||||
self.validate_thread_sharing()
|
||||
self._savepoint_rollback(sid)
|
||||
|
||||
# Remove any callbacks registered while this savepoint was active.
|
||||
self.run_on_commit = [
|
||||
(sids, func) for (sids, func) in self.run_on_commit if sid not in sids
|
||||
]
|
||||
|
||||
@async_unsafe
|
||||
def savepoint_commit(self, sid):
|
||||
"""
|
||||
Release a savepoint. Do nothing if savepoints are not supported.
|
||||
"""
|
||||
if not self._savepoint_allowed():
|
||||
return
|
||||
|
||||
self.validate_thread_sharing()
|
||||
self._savepoint_commit(sid)
|
||||
|
||||
@async_unsafe
|
||||
def clean_savepoints(self):
|
||||
"""
|
||||
Reset the counter used to generate unique savepoint ids in this thread.
|
||||
"""
|
||||
self.savepoint_state = 0
|
||||
|
||||
# ##### Backend-specific transaction management methods #####
|
||||
|
||||
def _set_autocommit(self, autocommit):
|
||||
"""
|
||||
Backend-specific implementation to enable or disable autocommit.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseWrapper may require a _set_autocommit() method"
|
||||
)
|
||||
|
||||
# ##### Generic transaction management methods #####
|
||||
|
||||
def get_autocommit(self):
|
||||
"""Get the autocommit state."""
|
||||
self.ensure_connection()
|
||||
return self.autocommit
|
||||
|
||||
def set_autocommit(
|
||||
self, autocommit, force_begin_transaction_with_broken_autocommit=False
|
||||
):
|
||||
"""
|
||||
Enable or disable autocommit.
|
||||
|
||||
The usual way to start a transaction is to turn autocommit off.
|
||||
SQLite does not properly start a transaction when disabling
|
||||
autocommit. To avoid this buggy behavior and to actually enter a new
|
||||
transaction, an explicit BEGIN is required. Using
|
||||
force_begin_transaction_with_broken_autocommit=True will issue an
|
||||
explicit BEGIN with SQLite. This option will be ignored for other
|
||||
backends.
|
||||
"""
|
||||
self.validate_no_atomic_block()
|
||||
self.close_if_health_check_failed()
|
||||
self.ensure_connection()
|
||||
|
||||
start_transaction_under_autocommit = (
|
||||
force_begin_transaction_with_broken_autocommit
|
||||
and not autocommit
|
||||
and hasattr(self, "_start_transaction_under_autocommit")
|
||||
)
|
||||
|
||||
if start_transaction_under_autocommit:
|
||||
self._start_transaction_under_autocommit()
|
||||
else:
|
||||
self._set_autocommit(autocommit)
|
||||
|
||||
self.autocommit = autocommit
|
||||
|
||||
if autocommit and self.run_commit_hooks_on_set_autocommit_on:
|
||||
self.run_and_clear_commit_hooks()
|
||||
self.run_commit_hooks_on_set_autocommit_on = False
|
||||
|
||||
def get_rollback(self):
|
||||
"""Get the "needs rollback" flag -- for *advanced use* only."""
|
||||
if not self.in_atomic_block:
|
||||
raise TransactionManagementError(
|
||||
"The rollback flag doesn't work outside of an 'atomic' block."
|
||||
)
|
||||
return self.needs_rollback
|
||||
|
||||
def set_rollback(self, rollback):
|
||||
"""
|
||||
Set or unset the "needs rollback" flag -- for *advanced use* only.
|
||||
"""
|
||||
if not self.in_atomic_block:
|
||||
raise TransactionManagementError(
|
||||
"The rollback flag doesn't work outside of an 'atomic' block."
|
||||
)
|
||||
self.needs_rollback = rollback
|
||||
|
||||
def validate_no_atomic_block(self):
|
||||
"""Raise an error if an atomic block is active."""
|
||||
if self.in_atomic_block:
|
||||
raise TransactionManagementError(
|
||||
"This is forbidden when an 'atomic' block is active."
|
||||
)
|
||||
|
||||
def validate_no_broken_transaction(self):
|
||||
if self.needs_rollback:
|
||||
raise TransactionManagementError(
|
||||
"An error occurred in the current transaction. You can't "
|
||||
"execute queries until the end of the 'atomic' block."
|
||||
)
|
||||
|
||||
# ##### Foreign key constraints checks handling #####
|
||||
|
||||
@contextmanager
|
||||
def constraint_checks_disabled(self):
|
||||
"""
|
||||
Disable foreign key constraint checking.
|
||||
"""
|
||||
disabled = self.disable_constraint_checking()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if disabled:
|
||||
self.enable_constraint_checking()
|
||||
|
||||
def disable_constraint_checking(self):
|
||||
"""
|
||||
Backends can implement as needed to temporarily disable foreign key
|
||||
constraint checking. Should return True if the constraints were
|
||||
disabled and will need to be reenabled.
|
||||
"""
|
||||
return False
|
||||
|
||||
def enable_constraint_checking(self):
|
||||
"""
|
||||
Backends can implement as needed to re-enable foreign key constraint
|
||||
checking.
|
||||
"""
|
||||
pass
|
||||
|
||||
def check_constraints(self, table_names=None):
|
||||
"""
|
||||
Backends can override this method if they can apply constraint
|
||||
checking (e.g. via "SET CONSTRAINTS ALL IMMEDIATE"). Should raise an
|
||||
IntegrityError if any invalid foreign key references are encountered.
|
||||
"""
|
||||
pass
|
||||
|
||||
# ##### Connection termination handling #####
|
||||
|
||||
def is_usable(self):
|
||||
"""
|
||||
Test if the database connection is usable.
|
||||
|
||||
This method may assume that self.connection is not None.
|
||||
|
||||
Actual implementations should take care not to raise exceptions
|
||||
as that may prevent Django from recycling unusable connections.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseWrapper may require an is_usable() method"
|
||||
)
|
||||
|
||||
def close_if_health_check_failed(self):
|
||||
"""Close existing connection if it fails a health check."""
|
||||
if (
|
||||
self.connection is None
|
||||
or not self.health_check_enabled
|
||||
or self.health_check_done
|
||||
):
|
||||
return
|
||||
|
||||
if not self.is_usable():
|
||||
self.close()
|
||||
self.health_check_done = True
|
||||
|
||||
def close_if_unusable_or_obsolete(self):
|
||||
"""
|
||||
Close the current connection if unrecoverable errors have occurred
|
||||
or if it outlived its maximum age.
|
||||
"""
|
||||
if self.connection is not None:
|
||||
self.health_check_done = False
|
||||
# If the application didn't restore the original autocommit setting,
|
||||
# don't take chances, drop the connection.
|
||||
if self.get_autocommit() != self.settings_dict["AUTOCOMMIT"]:
|
||||
self.close()
|
||||
return
|
||||
|
||||
# If an exception other than DataError or IntegrityError occurred
|
||||
# since the last commit / rollback, check if the connection works.
|
||||
if self.errors_occurred:
|
||||
if self.is_usable():
|
||||
self.errors_occurred = False
|
||||
self.health_check_done = True
|
||||
else:
|
||||
self.close()
|
||||
return
|
||||
|
||||
if self.close_at is not None and time.monotonic() >= self.close_at:
|
||||
self.close()
|
||||
return
|
||||
|
||||
# ##### Thread safety handling #####
|
||||
|
||||
@property
|
||||
def allow_thread_sharing(self):
|
||||
with self._thread_sharing_lock:
|
||||
return self._thread_sharing_count > 0
|
||||
|
||||
def inc_thread_sharing(self):
|
||||
with self._thread_sharing_lock:
|
||||
self._thread_sharing_count += 1
|
||||
|
||||
def dec_thread_sharing(self):
|
||||
with self._thread_sharing_lock:
|
||||
if self._thread_sharing_count <= 0:
|
||||
raise RuntimeError(
|
||||
"Cannot decrement the thread sharing count below zero."
|
||||
)
|
||||
self._thread_sharing_count -= 1
|
||||
|
||||
def validate_thread_sharing(self):
|
||||
"""
|
||||
Validate that the connection isn't accessed by another thread than the
|
||||
one which originally created it, unless the connection was explicitly
|
||||
authorized to be shared between threads (via the `inc_thread_sharing()`
|
||||
method). Raise an exception if the validation fails.
|
||||
"""
|
||||
if not (self.allow_thread_sharing or self._thread_ident == _thread.get_ident()):
|
||||
raise DatabaseError(
|
||||
"DatabaseWrapper objects created in a "
|
||||
"thread can only be used in that same thread. The object "
|
||||
"with alias '%s' was created in thread id %s and this is "
|
||||
"thread id %s." % (self.alias, self._thread_ident, _thread.get_ident())
|
||||
)
|
||||
|
||||
# ##### Miscellaneous #####
|
||||
|
||||
def prepare_database(self):
|
||||
"""
|
||||
Hook to do any database check or preparation, generally called before
|
||||
migrating a project or an app.
|
||||
"""
|
||||
pass
|
||||
|
||||
@cached_property
|
||||
def wrap_database_errors(self):
|
||||
"""
|
||||
Context manager and decorator that re-throws backend-specific database
|
||||
exceptions using Django's common wrappers.
|
||||
"""
|
||||
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):
|
||||
"""Create a cursor that logs all queries in self.queries_log."""
|
||||
return utils.CursorDebugWrapper(cursor, self)
|
||||
|
||||
def make_cursor(self, cursor):
|
||||
"""Create a cursor without debug logging."""
|
||||
return utils.CursorWrapper(cursor, self)
|
||||
|
||||
@contextmanager
|
||||
def temporary_connection(self):
|
||||
"""
|
||||
Context manager that ensures that a connection is established, and
|
||||
if it opened one, closes it to avoid leaving a dangling connection.
|
||||
This is useful for operations outside of the request-response cycle.
|
||||
|
||||
Provide a cursor: with self.temporary_connection() as cursor: ...
|
||||
"""
|
||||
must_close = self.connection is None
|
||||
try:
|
||||
with self.cursor() as cursor:
|
||||
yield cursor
|
||||
finally:
|
||||
if must_close:
|
||||
self.close()
|
||||
|
||||
@contextmanager
|
||||
def _nodb_cursor(self):
|
||||
"""
|
||||
Return a cursor from an alternative connection to be used when there is
|
||||
no need to access the main database, specifically for test db
|
||||
creation/deletion. This also prevents the production database from
|
||||
being exposed to potential child threads while (or after) the test
|
||||
database is destroyed. Refs #10868, #17786, #16969.
|
||||
"""
|
||||
conn = self.__class__({**self.settings_dict, "NAME": None}, alias=NO_DB_ALIAS)
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
yield cursor
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def schema_editor(self, *args, **kwargs):
|
||||
"""
|
||||
Return a new instance of this backend's SchemaEditor.
|
||||
"""
|
||||
if self.SchemaEditorClass is None:
|
||||
raise NotImplementedError(
|
||||
"The SchemaEditorClass attribute of this database wrapper is still None"
|
||||
)
|
||||
return self.SchemaEditorClass(self, *args, **kwargs)
|
||||
|
||||
def on_commit(self, func):
|
||||
if not callable(func):
|
||||
raise TypeError("on_commit()'s callback must be a callable.")
|
||||
if self.in_atomic_block:
|
||||
# Transaction in progress; save for execution on commit.
|
||||
self.run_on_commit.append((set(self.savepoint_ids), func))
|
||||
elif not self.get_autocommit():
|
||||
raise TransactionManagementError(
|
||||
"on_commit() cannot be used in manual transaction management"
|
||||
)
|
||||
else:
|
||||
# No transaction in progress and in autocommit mode; execute
|
||||
# immediately.
|
||||
func()
|
||||
|
||||
def run_and_clear_commit_hooks(self):
|
||||
self.validate_no_atomic_block()
|
||||
current_run_on_commit = self.run_on_commit
|
||||
self.run_on_commit = []
|
||||
while current_run_on_commit:
|
||||
sids, func = current_run_on_commit.pop(0)
|
||||
func()
|
||||
|
||||
@contextmanager
|
||||
def execute_wrapper(self, wrapper):
|
||||
"""
|
||||
Return a context manager under which the wrapper is applied to suitable
|
||||
database query executions.
|
||||
"""
|
||||
self.execute_wrappers.append(wrapper)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.execute_wrappers.pop()
|
||||
|
||||
def copy(self, alias=None):
|
||||
"""
|
||||
Return a copy of this connection.
|
||||
|
||||
For tests that require two connections to the same database.
|
||||
"""
|
||||
settings_dict = copy.deepcopy(self.settings_dict)
|
||||
if alias is None:
|
||||
alias = self.alias
|
||||
return type(self)(settings_dict, alias)
|
28
env/lib/python3.8/site-packages/django/db/backends/base/client.py
vendored
Normal file
28
env/lib/python3.8/site-packages/django/db/backends/base/client.py
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
class BaseDatabaseClient:
|
||||
"""Encapsulate backend-specific methods for opening a client shell."""
|
||||
|
||||
# This should be a string representing the name of the executable
|
||||
# (e.g., "psql"). Subclasses must override this.
|
||||
executable_name = None
|
||||
|
||||
def __init__(self, connection):
|
||||
# connection is an instance of BaseDatabaseWrapper.
|
||||
self.connection = connection
|
||||
|
||||
@classmethod
|
||||
def settings_to_cmd_args_env(cls, settings_dict, parameters):
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseClient must provide a "
|
||||
"settings_to_cmd_args_env() method or override a runshell()."
|
||||
)
|
||||
|
||||
def runshell(self, parameters):
|
||||
args, env = self.settings_to_cmd_args_env(
|
||||
self.connection.settings_dict, parameters
|
||||
)
|
||||
env = {**os.environ, **env} if env else None
|
||||
subprocess.run(args, env=env, check=True)
|
380
env/lib/python3.8/site-packages/django/db/backends/base/creation.py
vendored
Normal file
380
env/lib/python3.8/site-packages/django/db/backends/base/creation.py
vendored
Normal file
@@ -0,0 +1,380 @@
|
||||
import os
|
||||
import sys
|
||||
from io import StringIO
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.core import serializers
|
||||
from django.db import router
|
||||
from django.db.transaction import atomic
|
||||
from django.utils.module_loading import import_string
|
||||
|
||||
# The prefix to put on the default database name when creating
|
||||
# the test database.
|
||||
TEST_DATABASE_PREFIX = "test_"
|
||||
|
||||
|
||||
class BaseDatabaseCreation:
|
||||
"""
|
||||
Encapsulate backend-specific differences pertaining to creation and
|
||||
destruction of the test database.
|
||||
"""
|
||||
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
|
||||
def _nodb_cursor(self):
|
||||
return self.connection._nodb_cursor()
|
||||
|
||||
def log(self, msg):
|
||||
sys.stderr.write(msg + os.linesep)
|
||||
|
||||
def create_test_db(
|
||||
self, verbosity=1, autoclobber=False, serialize=True, keepdb=False
|
||||
):
|
||||
"""
|
||||
Create a test database, prompting the user for confirmation if the
|
||||
database already exists. Return the name of the test database created.
|
||||
"""
|
||||
# Don't import django.core.management if it isn't needed.
|
||||
from django.core.management import call_command
|
||||
|
||||
test_database_name = self._get_test_db_name()
|
||||
|
||||
if verbosity >= 1:
|
||||
action = "Creating"
|
||||
if keepdb:
|
||||
action = "Using existing"
|
||||
|
||||
self.log(
|
||||
"%s test database for alias %s..."
|
||||
% (
|
||||
action,
|
||||
self._get_database_display_str(verbosity, test_database_name),
|
||||
)
|
||||
)
|
||||
|
||||
# We could skip this call if keepdb is True, but we instead
|
||||
# give it the keepdb param. This is to handle the case
|
||||
# where the test DB doesn't exist, in which case we need to
|
||||
# create it, then just not destroy it. If we instead skip
|
||||
# this, we will get an exception.
|
||||
self._create_test_db(verbosity, autoclobber, keepdb)
|
||||
|
||||
self.connection.close()
|
||||
settings.DATABASES[self.connection.alias]["NAME"] = test_database_name
|
||||
self.connection.settings_dict["NAME"] = test_database_name
|
||||
|
||||
try:
|
||||
if self.connection.settings_dict["TEST"]["MIGRATE"] is False:
|
||||
# Disable migrations for all apps.
|
||||
old_migration_modules = settings.MIGRATION_MODULES
|
||||
settings.MIGRATION_MODULES = {
|
||||
app.label: None for app in apps.get_app_configs()
|
||||
}
|
||||
# We report migrate messages at one level lower than that
|
||||
# requested. This ensures we don't get flooded with messages during
|
||||
# testing (unless you really ask to be flooded).
|
||||
call_command(
|
||||
"migrate",
|
||||
verbosity=max(verbosity - 1, 0),
|
||||
interactive=False,
|
||||
database=self.connection.alias,
|
||||
run_syncdb=True,
|
||||
)
|
||||
finally:
|
||||
if self.connection.settings_dict["TEST"]["MIGRATE"] is False:
|
||||
settings.MIGRATION_MODULES = old_migration_modules
|
||||
|
||||
# We then serialize the current state of the database into a string
|
||||
# and store it on the connection. This slightly horrific process is so people
|
||||
# who are testing on databases without transactions or who are using
|
||||
# a TransactionTestCase still get a clean database on every test run.
|
||||
if serialize:
|
||||
self.connection._test_serialized_contents = self.serialize_db_to_string()
|
||||
|
||||
call_command("createcachetable", database=self.connection.alias)
|
||||
|
||||
# Ensure a connection for the side effect of initializing the test database.
|
||||
self.connection.ensure_connection()
|
||||
|
||||
if os.environ.get("RUNNING_DJANGOS_TEST_SUITE") == "true":
|
||||
self.mark_expected_failures_and_skips()
|
||||
|
||||
return test_database_name
|
||||
|
||||
def set_as_test_mirror(self, primary_settings_dict):
|
||||
"""
|
||||
Set this database up to be used in testing as a mirror of a primary
|
||||
database whose settings are given.
|
||||
"""
|
||||
self.connection.settings_dict["NAME"] = primary_settings_dict["NAME"]
|
||||
|
||||
def serialize_db_to_string(self):
|
||||
"""
|
||||
Serialize all data in the database into a JSON string.
|
||||
Designed only for test runner usage; will not handle large
|
||||
amounts of data.
|
||||
"""
|
||||
# Iteratively return every object for all models to serialize.
|
||||
def get_objects():
|
||||
from django.db.migrations.loader import MigrationLoader
|
||||
|
||||
loader = MigrationLoader(self.connection)
|
||||
for app_config in apps.get_app_configs():
|
||||
if (
|
||||
app_config.models_module is not None
|
||||
and app_config.label in loader.migrated_apps
|
||||
and app_config.name not in settings.TEST_NON_SERIALIZED_APPS
|
||||
):
|
||||
for model in app_config.get_models():
|
||||
if model._meta.can_migrate(
|
||||
self.connection
|
||||
) and router.allow_migrate_model(self.connection.alias, model):
|
||||
queryset = model._base_manager.using(
|
||||
self.connection.alias,
|
||||
).order_by(model._meta.pk.name)
|
||||
yield from queryset.iterator()
|
||||
|
||||
# Serialize to a string
|
||||
out = StringIO()
|
||||
serializers.serialize("json", get_objects(), indent=None, stream=out)
|
||||
return out.getvalue()
|
||||
|
||||
def deserialize_db_from_string(self, data):
|
||||
"""
|
||||
Reload the database with data from a string generated by
|
||||
the serialize_db_to_string() method.
|
||||
"""
|
||||
data = StringIO(data)
|
||||
table_names = set()
|
||||
# Load data in a transaction to handle forward references and cycles.
|
||||
with atomic(using=self.connection.alias):
|
||||
# Disable constraint checks, because some databases (MySQL) doesn't
|
||||
# support deferred checks.
|
||||
with self.connection.constraint_checks_disabled():
|
||||
for obj in serializers.deserialize(
|
||||
"json", data, using=self.connection.alias
|
||||
):
|
||||
obj.save()
|
||||
table_names.add(obj.object.__class__._meta.db_table)
|
||||
# Manually check for any invalid keys that might have been added,
|
||||
# because constraint checks were disabled.
|
||||
self.connection.check_constraints(table_names=table_names)
|
||||
|
||||
def _get_database_display_str(self, verbosity, database_name):
|
||||
"""
|
||||
Return display string for a database for use in various actions.
|
||||
"""
|
||||
return "'%s'%s" % (
|
||||
self.connection.alias,
|
||||
(" ('%s')" % database_name) if verbosity >= 2 else "",
|
||||
)
|
||||
|
||||
def _get_test_db_name(self):
|
||||
"""
|
||||
Internal implementation - return the name of the test DB that will be
|
||||
created. Only useful when called from create_test_db() and
|
||||
_create_test_db() and when no external munging is done with the 'NAME'
|
||||
settings.
|
||||
"""
|
||||
if self.connection.settings_dict["TEST"]["NAME"]:
|
||||
return self.connection.settings_dict["TEST"]["NAME"]
|
||||
return TEST_DATABASE_PREFIX + self.connection.settings_dict["NAME"]
|
||||
|
||||
def _execute_create_test_db(self, cursor, parameters, keepdb=False):
|
||||
cursor.execute("CREATE DATABASE %(dbname)s %(suffix)s" % parameters)
|
||||
|
||||
def _create_test_db(self, verbosity, autoclobber, keepdb=False):
|
||||
"""
|
||||
Internal implementation - create the test db tables.
|
||||
"""
|
||||
test_database_name = self._get_test_db_name()
|
||||
test_db_params = {
|
||||
"dbname": self.connection.ops.quote_name(test_database_name),
|
||||
"suffix": self.sql_table_creation_suffix(),
|
||||
}
|
||||
# Create the test database and connect to it.
|
||||
with self._nodb_cursor() as cursor:
|
||||
try:
|
||||
self._execute_create_test_db(cursor, test_db_params, keepdb)
|
||||
except Exception as e:
|
||||
# if we want to keep the db, then no need to do any of the below,
|
||||
# just return and skip it all.
|
||||
if keepdb:
|
||||
return test_database_name
|
||||
|
||||
self.log("Got an error creating the test database: %s" % e)
|
||||
if not autoclobber:
|
||||
confirm = input(
|
||||
"Type 'yes' if you would like to try deleting the test "
|
||||
"database '%s', or 'no' to cancel: " % test_database_name
|
||||
)
|
||||
if autoclobber or confirm == "yes":
|
||||
try:
|
||||
if verbosity >= 1:
|
||||
self.log(
|
||||
"Destroying old test database for alias %s..."
|
||||
% (
|
||||
self._get_database_display_str(
|
||||
verbosity, test_database_name
|
||||
),
|
||||
)
|
||||
)
|
||||
cursor.execute("DROP DATABASE %(dbname)s" % test_db_params)
|
||||
self._execute_create_test_db(cursor, test_db_params, keepdb)
|
||||
except Exception as e:
|
||||
self.log("Got an error recreating the test database: %s" % e)
|
||||
sys.exit(2)
|
||||
else:
|
||||
self.log("Tests cancelled.")
|
||||
sys.exit(1)
|
||||
|
||||
return test_database_name
|
||||
|
||||
def clone_test_db(self, suffix, verbosity=1, autoclobber=False, keepdb=False):
|
||||
"""
|
||||
Clone a test database.
|
||||
"""
|
||||
source_database_name = self.connection.settings_dict["NAME"]
|
||||
|
||||
if verbosity >= 1:
|
||||
action = "Cloning test database"
|
||||
if keepdb:
|
||||
action = "Using existing clone"
|
||||
self.log(
|
||||
"%s for alias %s..."
|
||||
% (
|
||||
action,
|
||||
self._get_database_display_str(verbosity, source_database_name),
|
||||
)
|
||||
)
|
||||
|
||||
# We could skip this call if keepdb is True, but we instead
|
||||
# give it the keepdb param. See create_test_db for details.
|
||||
self._clone_test_db(suffix, verbosity, keepdb)
|
||||
|
||||
def get_test_db_clone_settings(self, suffix):
|
||||
"""
|
||||
Return a modified connection settings dict for the n-th clone of a DB.
|
||||
"""
|
||||
# When this function is called, the test database has been created
|
||||
# already and its name has been copied to settings_dict['NAME'] so
|
||||
# we don't need to call _get_test_db_name.
|
||||
orig_settings_dict = self.connection.settings_dict
|
||||
return {
|
||||
**orig_settings_dict,
|
||||
"NAME": "{}_{}".format(orig_settings_dict["NAME"], suffix),
|
||||
}
|
||||
|
||||
def _clone_test_db(self, suffix, verbosity, keepdb=False):
|
||||
"""
|
||||
Internal implementation - duplicate the test db tables.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"The database backend doesn't support cloning databases. "
|
||||
"Disable the option to run tests in parallel processes."
|
||||
)
|
||||
|
||||
def destroy_test_db(
|
||||
self, old_database_name=None, verbosity=1, keepdb=False, suffix=None
|
||||
):
|
||||
"""
|
||||
Destroy a test database, prompting the user for confirmation if the
|
||||
database already exists.
|
||||
"""
|
||||
self.connection.close()
|
||||
if suffix is None:
|
||||
test_database_name = self.connection.settings_dict["NAME"]
|
||||
else:
|
||||
test_database_name = self.get_test_db_clone_settings(suffix)["NAME"]
|
||||
|
||||
if verbosity >= 1:
|
||||
action = "Destroying"
|
||||
if keepdb:
|
||||
action = "Preserving"
|
||||
self.log(
|
||||
"%s test database for alias %s..."
|
||||
% (
|
||||
action,
|
||||
self._get_database_display_str(verbosity, test_database_name),
|
||||
)
|
||||
)
|
||||
|
||||
# if we want to preserve the database
|
||||
# skip the actual destroying piece.
|
||||
if not keepdb:
|
||||
self._destroy_test_db(test_database_name, verbosity)
|
||||
|
||||
# Restore the original database name
|
||||
if old_database_name is not None:
|
||||
settings.DATABASES[self.connection.alias]["NAME"] = old_database_name
|
||||
self.connection.settings_dict["NAME"] = old_database_name
|
||||
|
||||
def _destroy_test_db(self, test_database_name, verbosity):
|
||||
"""
|
||||
Internal implementation - remove the test db tables.
|
||||
"""
|
||||
# Remove the test database to clean up after
|
||||
# ourselves. Connect to the previous database (not the test database)
|
||||
# to do so, because it's not allowed to delete a database while being
|
||||
# connected to it.
|
||||
with self._nodb_cursor() as cursor:
|
||||
cursor.execute(
|
||||
"DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name)
|
||||
)
|
||||
|
||||
def mark_expected_failures_and_skips(self):
|
||||
"""
|
||||
Mark tests in Django's test suite which are expected failures on this
|
||||
database and test which should be skipped on this database.
|
||||
"""
|
||||
# Only load unittest if we're actually testing.
|
||||
from unittest import expectedFailure, skip
|
||||
|
||||
for test_name in self.connection.features.django_test_expected_failures:
|
||||
test_case_name, _, test_method_name = test_name.rpartition(".")
|
||||
test_app = test_name.split(".")[0]
|
||||
# Importing a test app that isn't installed raises RuntimeError.
|
||||
if test_app in settings.INSTALLED_APPS:
|
||||
test_case = import_string(test_case_name)
|
||||
test_method = getattr(test_case, test_method_name)
|
||||
setattr(test_case, test_method_name, expectedFailure(test_method))
|
||||
for reason, tests in self.connection.features.django_test_skips.items():
|
||||
for test_name in tests:
|
||||
test_case_name, _, test_method_name = test_name.rpartition(".")
|
||||
test_app = test_name.split(".")[0]
|
||||
# Importing a test app that isn't installed raises RuntimeError.
|
||||
if test_app in settings.INSTALLED_APPS:
|
||||
test_case = import_string(test_case_name)
|
||||
test_method = getattr(test_case, test_method_name)
|
||||
setattr(test_case, test_method_name, skip(reason)(test_method))
|
||||
|
||||
def sql_table_creation_suffix(self):
|
||||
"""
|
||||
SQL to append to the end of the test table creation statements.
|
||||
"""
|
||||
return ""
|
||||
|
||||
def test_db_signature(self):
|
||||
"""
|
||||
Return a tuple with elements of self.connection.settings_dict (a
|
||||
DATABASES setting value) that uniquely identify a database
|
||||
accordingly to the RDBMS particularities.
|
||||
"""
|
||||
settings_dict = self.connection.settings_dict
|
||||
return (
|
||||
settings_dict["HOST"],
|
||||
settings_dict["PORT"],
|
||||
settings_dict["ENGINE"],
|
||||
self._get_test_db_name(),
|
||||
)
|
||||
|
||||
def setup_worker_connection(self, _worker_id):
|
||||
settings_dict = self.get_test_db_clone_settings(str(_worker_id))
|
||||
# connection.settings_dict must be updated in place for changes to be
|
||||
# reflected in django.db.connections. If the following line assigned
|
||||
# connection.settings_dict = settings_dict, new threads would connect
|
||||
# to the default database instead of the appropriate clone.
|
||||
self.connection.settings_dict.update(settings_dict)
|
||||
self.connection.close()
|
372
env/lib/python3.8/site-packages/django/db/backends/base/features.py
vendored
Normal file
372
env/lib/python3.8/site-packages/django/db/backends/base/features.py
vendored
Normal file
@@ -0,0 +1,372 @@
|
||||
from django.db import ProgrammingError
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
|
||||
class BaseDatabaseFeatures:
|
||||
# An optional tuple indicating the minimum supported database version.
|
||||
minimum_database_version = None
|
||||
gis_enabled = False
|
||||
# Oracle can't group by LOB (large object) data types.
|
||||
allows_group_by_lob = True
|
||||
allows_group_by_pk = False
|
||||
allows_group_by_selected_pks = False
|
||||
empty_fetchmany_value = []
|
||||
update_can_self_select = True
|
||||
|
||||
# Does the backend distinguish between '' and None?
|
||||
interprets_empty_strings_as_nulls = False
|
||||
|
||||
# Does the backend allow inserting duplicate NULL rows in a nullable
|
||||
# unique field? All core backends implement this correctly, but other
|
||||
# databases such as SQL Server do not.
|
||||
supports_nullable_unique_constraints = True
|
||||
|
||||
# Does the backend allow inserting duplicate rows when a unique_together
|
||||
# constraint exists and some fields are nullable but not all of them?
|
||||
supports_partially_nullable_unique_constraints = True
|
||||
# Does the backend support initially deferrable unique constraints?
|
||||
supports_deferrable_unique_constraints = False
|
||||
|
||||
can_use_chunked_reads = True
|
||||
can_return_columns_from_insert = False
|
||||
can_return_rows_from_bulk_insert = False
|
||||
has_bulk_insert = True
|
||||
uses_savepoints = True
|
||||
can_release_savepoints = False
|
||||
|
||||
# If True, don't use integer foreign keys referring to, e.g., positive
|
||||
# integer primary keys.
|
||||
related_fields_match_type = False
|
||||
allow_sliced_subqueries_with_in = True
|
||||
has_select_for_update = False
|
||||
has_select_for_update_nowait = False
|
||||
has_select_for_update_skip_locked = False
|
||||
has_select_for_update_of = False
|
||||
has_select_for_no_key_update = False
|
||||
# Does the database's SELECT FOR UPDATE OF syntax require a column rather
|
||||
# than a table?
|
||||
select_for_update_of_column = False
|
||||
|
||||
# Does the default test database allow multiple connections?
|
||||
# Usually an indication that the test database is in-memory
|
||||
test_db_allows_multiple_connections = True
|
||||
|
||||
# Can an object be saved without an explicit primary key?
|
||||
supports_unspecified_pk = False
|
||||
|
||||
# Can a fixture contain forward references? i.e., are
|
||||
# FK constraints checked at the end of transaction, or
|
||||
# at the end of each save operation?
|
||||
supports_forward_references = True
|
||||
|
||||
# Does the backend truncate names properly when they are too long?
|
||||
truncates_names = False
|
||||
|
||||
# Is there a REAL datatype in addition to floats/doubles?
|
||||
has_real_datatype = False
|
||||
supports_subqueries_in_group_by = True
|
||||
|
||||
# Does the backend ignore unnecessary ORDER BY clauses in subqueries?
|
||||
ignores_unnecessary_order_by_in_subqueries = True
|
||||
|
||||
# Is there a true datatype for uuid?
|
||||
has_native_uuid_field = False
|
||||
|
||||
# Is there a true datatype for timedeltas?
|
||||
has_native_duration_field = False
|
||||
|
||||
# Does the database driver supports same type temporal data subtraction
|
||||
# by returning the type used to store duration field?
|
||||
supports_temporal_subtraction = False
|
||||
|
||||
# Does the __regex lookup support backreferencing and grouping?
|
||||
supports_regex_backreferencing = True
|
||||
|
||||
# Can date/datetime lookups be performed using a string?
|
||||
supports_date_lookup_using_string = True
|
||||
|
||||
# Can datetimes with timezones be used?
|
||||
supports_timezones = True
|
||||
|
||||
# Does the database have a copy of the zoneinfo database?
|
||||
has_zoneinfo_database = True
|
||||
|
||||
# When performing a GROUP BY, is an ORDER BY NULL required
|
||||
# to remove any ordering?
|
||||
requires_explicit_null_ordering_when_grouping = False
|
||||
|
||||
# Does the backend order NULL values as largest or smallest?
|
||||
nulls_order_largest = False
|
||||
|
||||
# Does the backend support NULLS FIRST and NULLS LAST in ORDER BY?
|
||||
supports_order_by_nulls_modifier = True
|
||||
|
||||
# Does the backend orders NULLS FIRST by default?
|
||||
order_by_nulls_first = False
|
||||
|
||||
# The database's limit on the number of query parameters.
|
||||
max_query_params = None
|
||||
|
||||
# Can an object have an autoincrement primary key of 0?
|
||||
allows_auto_pk_0 = True
|
||||
|
||||
# Do we need to NULL a ForeignKey out, or can the constraint check be
|
||||
# deferred
|
||||
can_defer_constraint_checks = False
|
||||
|
||||
# Does the backend support tablespaces? Default to False because it isn't
|
||||
# in the SQL standard.
|
||||
supports_tablespaces = False
|
||||
|
||||
# Does the backend reset sequences between tests?
|
||||
supports_sequence_reset = True
|
||||
|
||||
# Can the backend introspect the default value of a column?
|
||||
can_introspect_default = True
|
||||
|
||||
# Confirm support for introspected foreign keys
|
||||
# Every database can do this reliably, except MySQL,
|
||||
# which can't do it for MyISAM tables
|
||||
can_introspect_foreign_keys = True
|
||||
|
||||
# Map fields which some backends may not be able to differentiate to the
|
||||
# field it's introspected as.
|
||||
introspected_field_types = {
|
||||
"AutoField": "AutoField",
|
||||
"BigAutoField": "BigAutoField",
|
||||
"BigIntegerField": "BigIntegerField",
|
||||
"BinaryField": "BinaryField",
|
||||
"BooleanField": "BooleanField",
|
||||
"CharField": "CharField",
|
||||
"DurationField": "DurationField",
|
||||
"GenericIPAddressField": "GenericIPAddressField",
|
||||
"IntegerField": "IntegerField",
|
||||
"PositiveBigIntegerField": "PositiveBigIntegerField",
|
||||
"PositiveIntegerField": "PositiveIntegerField",
|
||||
"PositiveSmallIntegerField": "PositiveSmallIntegerField",
|
||||
"SmallAutoField": "SmallAutoField",
|
||||
"SmallIntegerField": "SmallIntegerField",
|
||||
"TimeField": "TimeField",
|
||||
}
|
||||
|
||||
# Can the backend introspect the column order (ASC/DESC) for indexes?
|
||||
supports_index_column_ordering = True
|
||||
|
||||
# Does the backend support introspection of materialized views?
|
||||
can_introspect_materialized_views = False
|
||||
|
||||
# Support for the DISTINCT ON clause
|
||||
can_distinct_on_fields = False
|
||||
|
||||
# Does the backend prevent running SQL queries in broken transactions?
|
||||
atomic_transactions = True
|
||||
|
||||
# Can we roll back DDL in a transaction?
|
||||
can_rollback_ddl = False
|
||||
|
||||
# Does it support operations requiring references rename in a transaction?
|
||||
supports_atomic_references_rename = True
|
||||
|
||||
# Can we issue more than one ALTER COLUMN clause in an ALTER TABLE?
|
||||
supports_combined_alters = False
|
||||
|
||||
# Does it support foreign keys?
|
||||
supports_foreign_keys = True
|
||||
|
||||
# Can it create foreign key constraints inline when adding columns?
|
||||
can_create_inline_fk = True
|
||||
|
||||
# Can an index be renamed?
|
||||
can_rename_index = False
|
||||
|
||||
# Does it automatically index foreign keys?
|
||||
indexes_foreign_keys = True
|
||||
|
||||
# Does it support CHECK constraints?
|
||||
supports_column_check_constraints = True
|
||||
supports_table_check_constraints = True
|
||||
# Does the backend support introspection of CHECK constraints?
|
||||
can_introspect_check_constraints = True
|
||||
|
||||
# Does the backend support 'pyformat' style ("... %(name)s ...", {'name': value})
|
||||
# parameter passing? Note this can be provided by the backend even if not
|
||||
# supported by the Python driver
|
||||
supports_paramstyle_pyformat = True
|
||||
|
||||
# Does the backend require literal defaults, rather than parameterized ones?
|
||||
requires_literal_defaults = False
|
||||
|
||||
# Does the backend require a connection reset after each material schema change?
|
||||
connection_persists_old_columns = False
|
||||
|
||||
# What kind of error does the backend throw when accessing closed cursor?
|
||||
closed_cursor_error_class = ProgrammingError
|
||||
|
||||
# Does 'a' LIKE 'A' match?
|
||||
has_case_insensitive_like = False
|
||||
|
||||
# Suffix for backends that don't support "SELECT xxx;" queries.
|
||||
bare_select_suffix = ""
|
||||
|
||||
# If NULL is implied on columns without needing to be explicitly specified
|
||||
implied_column_null = False
|
||||
|
||||
# Does the backend support "select for update" queries with limit (and offset)?
|
||||
supports_select_for_update_with_limit = True
|
||||
|
||||
# Does the backend ignore null expressions in GREATEST and LEAST queries unless
|
||||
# every expression is null?
|
||||
greatest_least_ignores_nulls = False
|
||||
|
||||
# Can the backend clone databases for parallel test execution?
|
||||
# Defaults to False to allow third-party backends to opt-in.
|
||||
can_clone_databases = False
|
||||
|
||||
# Does the backend consider table names with different casing to
|
||||
# be equal?
|
||||
ignores_table_name_case = False
|
||||
|
||||
# Place FOR UPDATE right after FROM clause. Used on MSSQL.
|
||||
for_update_after_from = False
|
||||
|
||||
# Combinatorial flags
|
||||
supports_select_union = True
|
||||
supports_select_intersection = True
|
||||
supports_select_difference = True
|
||||
supports_slicing_ordering_in_compound = False
|
||||
supports_parentheses_in_compound = True
|
||||
|
||||
# Does the database support SQL 2003 FILTER (WHERE ...) in aggregate
|
||||
# expressions?
|
||||
supports_aggregate_filter_clause = False
|
||||
|
||||
# Does the backend support indexing a TextField?
|
||||
supports_index_on_text_field = True
|
||||
|
||||
# Does the backend support window expressions (expression OVER (...))?
|
||||
supports_over_clause = False
|
||||
supports_frame_range_fixed_distance = False
|
||||
only_supports_unbounded_with_preceding_and_following = False
|
||||
|
||||
# Does the backend support CAST with precision?
|
||||
supports_cast_with_precision = True
|
||||
|
||||
# How many second decimals does the database return when casting a value to
|
||||
# a type with time?
|
||||
time_cast_precision = 6
|
||||
|
||||
# SQL to create a procedure for use by the Django test suite. The
|
||||
# functionality of the procedure isn't important.
|
||||
create_test_procedure_without_params_sql = None
|
||||
create_test_procedure_with_int_param_sql = None
|
||||
|
||||
# Does the backend support keyword parameters for cursor.callproc()?
|
||||
supports_callproc_kwargs = False
|
||||
|
||||
# What formats does the backend EXPLAIN syntax support?
|
||||
supported_explain_formats = set()
|
||||
|
||||
# Does the backend support the default parameter in lead() and lag()?
|
||||
supports_default_in_lead_lag = True
|
||||
|
||||
# Does the backend support ignoring constraint or uniqueness errors during
|
||||
# INSERT?
|
||||
supports_ignore_conflicts = True
|
||||
# Does the backend support updating rows on constraint or uniqueness errors
|
||||
# during INSERT?
|
||||
supports_update_conflicts = False
|
||||
supports_update_conflicts_with_target = False
|
||||
|
||||
# Does this backend require casting the results of CASE expressions used
|
||||
# in UPDATE statements to ensure the expression has the correct type?
|
||||
requires_casted_case_in_updates = False
|
||||
|
||||
# Does the backend support partial indexes (CREATE INDEX ... WHERE ...)?
|
||||
supports_partial_indexes = True
|
||||
supports_functions_in_partial_indexes = True
|
||||
# Does the backend support covering indexes (CREATE INDEX ... INCLUDE ...)?
|
||||
supports_covering_indexes = False
|
||||
# Does the backend support indexes on expressions?
|
||||
supports_expression_indexes = True
|
||||
# Does the backend treat COLLATE as an indexed expression?
|
||||
collate_as_index_expression = False
|
||||
|
||||
# Does the database allow more than one constraint or index on the same
|
||||
# field(s)?
|
||||
allows_multiple_constraints_on_same_fields = True
|
||||
|
||||
# Does the backend support boolean expressions in SELECT and GROUP BY
|
||||
# clauses?
|
||||
supports_boolean_expr_in_select_clause = True
|
||||
|
||||
# Does the backend support JSONField?
|
||||
supports_json_field = True
|
||||
# Can the backend introspect a JSONField?
|
||||
can_introspect_json_field = True
|
||||
# Does the backend support primitives in JSONField?
|
||||
supports_primitives_in_json_field = True
|
||||
# Is there a true datatype for JSON?
|
||||
has_native_json_field = False
|
||||
# Does the backend use PostgreSQL-style JSON operators like '->'?
|
||||
has_json_operators = False
|
||||
# Does the backend support __contains and __contained_by lookups for
|
||||
# a JSONField?
|
||||
supports_json_field_contains = True
|
||||
# Does value__d__contains={'f': 'g'} (without a list around the dict) match
|
||||
# {'d': [{'f': 'g'}]}?
|
||||
json_key_contains_list_matching_requires_list = False
|
||||
# Does the backend support JSONObject() database function?
|
||||
has_json_object_function = True
|
||||
|
||||
# Does the backend support column collations?
|
||||
supports_collation_on_charfield = True
|
||||
supports_collation_on_textfield = True
|
||||
# Does the backend support non-deterministic collations?
|
||||
supports_non_deterministic_collations = True
|
||||
|
||||
# Does the backend support the logical XOR operator?
|
||||
supports_logical_xor = False
|
||||
|
||||
# Collation names for use by the Django test suite.
|
||||
test_collations = {
|
||||
"ci": None, # Case-insensitive.
|
||||
"cs": None, # Case-sensitive.
|
||||
"non_default": None, # Non-default.
|
||||
"swedish_ci": None, # Swedish case-insensitive.
|
||||
}
|
||||
# SQL template override for tests.aggregation.tests.NowUTC
|
||||
test_now_utc_template = None
|
||||
|
||||
# A set of dotted paths to tests in Django's test suite that are expected
|
||||
# to fail on this database.
|
||||
django_test_expected_failures = set()
|
||||
# A map of reasons to sets of dotted paths to tests in Django's test suite
|
||||
# that should be skipped for this database.
|
||||
django_test_skips = {}
|
||||
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
|
||||
@cached_property
|
||||
def supports_explaining_query_execution(self):
|
||||
"""Does this backend support explaining query execution?"""
|
||||
return self.connection.ops.explain_prefix is not None
|
||||
|
||||
@cached_property
|
||||
def supports_transactions(self):
|
||||
"""Confirm support for transactions."""
|
||||
with self.connection.cursor() as cursor:
|
||||
cursor.execute("CREATE TABLE ROLLBACK_TEST (X INT)")
|
||||
self.connection.set_autocommit(False)
|
||||
cursor.execute("INSERT INTO ROLLBACK_TEST (X) VALUES (8)")
|
||||
self.connection.rollback()
|
||||
self.connection.set_autocommit(True)
|
||||
cursor.execute("SELECT COUNT(X) FROM ROLLBACK_TEST")
|
||||
(count,) = cursor.fetchone()
|
||||
cursor.execute("DROP TABLE ROLLBACK_TEST")
|
||||
return count == 0
|
||||
|
||||
def allows_group_by_selected_pks_on_model(self, model):
|
||||
if not self.allows_group_by_selected_pks:
|
||||
return False
|
||||
return model._meta.managed
|
207
env/lib/python3.8/site-packages/django/db/backends/base/introspection.py
vendored
Normal file
207
env/lib/python3.8/site-packages/django/db/backends/base/introspection.py
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
from collections import namedtuple
|
||||
|
||||
# Structure returned by DatabaseIntrospection.get_table_list()
|
||||
TableInfo = namedtuple("TableInfo", ["name", "type"])
|
||||
|
||||
# Structure returned by the DB-API cursor.description interface (PEP 249)
|
||||
FieldInfo = namedtuple(
|
||||
"FieldInfo",
|
||||
"name type_code display_size internal_size precision scale null_ok "
|
||||
"default collation",
|
||||
)
|
||||
|
||||
|
||||
class BaseDatabaseIntrospection:
|
||||
"""Encapsulate backend-specific introspection utilities."""
|
||||
|
||||
data_types_reverse = {}
|
||||
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
|
||||
def get_field_type(self, data_type, description):
|
||||
"""
|
||||
Hook for a database backend to use the cursor description to
|
||||
match a Django field type to a database column.
|
||||
|
||||
For Oracle, the column data_type on its own is insufficient to
|
||||
distinguish between a FloatField and IntegerField, for example.
|
||||
"""
|
||||
return self.data_types_reverse[data_type]
|
||||
|
||||
def identifier_converter(self, name):
|
||||
"""
|
||||
Apply a conversion to the identifier for the purposes of comparison.
|
||||
|
||||
The default identifier converter is for case sensitive comparison.
|
||||
"""
|
||||
return name
|
||||
|
||||
def table_names(self, cursor=None, include_views=False):
|
||||
"""
|
||||
Return a list of names of all tables that exist in the database.
|
||||
Sort the returned table list by Python's default sorting. Do NOT use
|
||||
the database's ORDER BY here to avoid subtle differences in sorting
|
||||
order between databases.
|
||||
"""
|
||||
|
||||
def get_names(cursor):
|
||||
return sorted(
|
||||
ti.name
|
||||
for ti in self.get_table_list(cursor)
|
||||
if include_views or ti.type == "t"
|
||||
)
|
||||
|
||||
if cursor is None:
|
||||
with self.connection.cursor() as cursor:
|
||||
return get_names(cursor)
|
||||
return get_names(cursor)
|
||||
|
||||
def get_table_list(self, cursor):
|
||||
"""
|
||||
Return an unsorted list of TableInfo named tuples of all tables and
|
||||
views that exist in the database.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseIntrospection may require a get_table_list() "
|
||||
"method"
|
||||
)
|
||||
|
||||
def get_table_description(self, cursor, table_name):
|
||||
"""
|
||||
Return a description of the table with the DB-API cursor.description
|
||||
interface.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseIntrospection may require a "
|
||||
"get_table_description() method."
|
||||
)
|
||||
|
||||
def get_migratable_models(self):
|
||||
from django.apps import apps
|
||||
from django.db import router
|
||||
|
||||
return (
|
||||
model
|
||||
for app_config in apps.get_app_configs()
|
||||
for model in router.get_migratable_models(app_config, self.connection.alias)
|
||||
if model._meta.can_migrate(self.connection)
|
||||
)
|
||||
|
||||
def django_table_names(self, only_existing=False, include_views=True):
|
||||
"""
|
||||
Return a list of all table names that have associated Django models and
|
||||
are in INSTALLED_APPS.
|
||||
|
||||
If only_existing is True, include only the tables in the database.
|
||||
"""
|
||||
tables = set()
|
||||
for model in self.get_migratable_models():
|
||||
if not model._meta.managed:
|
||||
continue
|
||||
tables.add(model._meta.db_table)
|
||||
tables.update(
|
||||
f.m2m_db_table()
|
||||
for f in model._meta.local_many_to_many
|
||||
if f.remote_field.through._meta.managed
|
||||
)
|
||||
tables = list(tables)
|
||||
if only_existing:
|
||||
existing_tables = set(self.table_names(include_views=include_views))
|
||||
tables = [
|
||||
t for t in tables if self.identifier_converter(t) in existing_tables
|
||||
]
|
||||
return tables
|
||||
|
||||
def installed_models(self, tables):
|
||||
"""
|
||||
Return a set of all models represented by the provided list of table
|
||||
names.
|
||||
"""
|
||||
tables = set(map(self.identifier_converter, tables))
|
||||
return {
|
||||
m
|
||||
for m in self.get_migratable_models()
|
||||
if self.identifier_converter(m._meta.db_table) in tables
|
||||
}
|
||||
|
||||
def sequence_list(self):
|
||||
"""
|
||||
Return a list of information about all DB sequences for all models in
|
||||
all apps.
|
||||
"""
|
||||
sequence_list = []
|
||||
with self.connection.cursor() as cursor:
|
||||
for model in self.get_migratable_models():
|
||||
if not model._meta.managed:
|
||||
continue
|
||||
if model._meta.swapped:
|
||||
continue
|
||||
sequence_list.extend(
|
||||
self.get_sequences(
|
||||
cursor, model._meta.db_table, model._meta.local_fields
|
||||
)
|
||||
)
|
||||
for f in model._meta.local_many_to_many:
|
||||
# If this is an m2m using an intermediate table,
|
||||
# we don't need to reset the sequence.
|
||||
if f.remote_field.through._meta.auto_created:
|
||||
sequence = self.get_sequences(cursor, f.m2m_db_table())
|
||||
sequence_list.extend(
|
||||
sequence or [{"table": f.m2m_db_table(), "column": None}]
|
||||
)
|
||||
return sequence_list
|
||||
|
||||
def get_sequences(self, cursor, table_name, table_fields=()):
|
||||
"""
|
||||
Return a list of introspected sequences for table_name. Each sequence
|
||||
is a dict: {'table': <table_name>, 'column': <column_name>}. An optional
|
||||
'name' key can be added if the backend supports named sequences.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseIntrospection may require a get_sequences() "
|
||||
"method"
|
||||
)
|
||||
|
||||
def get_relations(self, cursor, table_name):
|
||||
"""
|
||||
Return a dictionary of {field_name: (field_name_other_table, other_table)}
|
||||
representing all foreign keys in the given table.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseIntrospection may require a "
|
||||
"get_relations() method."
|
||||
)
|
||||
|
||||
def get_primary_key_column(self, cursor, table_name):
|
||||
"""
|
||||
Return the name of the primary key column for the given table.
|
||||
"""
|
||||
for constraint in self.get_constraints(cursor, table_name).values():
|
||||
if constraint["primary_key"]:
|
||||
return constraint["columns"][0]
|
||||
return None
|
||||
|
||||
def get_constraints(self, cursor, table_name):
|
||||
"""
|
||||
Retrieve any constraints or keys (unique, pk, fk, check, index)
|
||||
across one or more columns.
|
||||
|
||||
Return a dict mapping constraint names to their attributes,
|
||||
where attributes is a dict with keys:
|
||||
* columns: List of columns this covers
|
||||
* primary_key: True if primary key, False otherwise
|
||||
* unique: True if this is a unique constraint, False otherwise
|
||||
* foreign_key: (table, column) of target, or None
|
||||
* check: True if check constraint, False otherwise
|
||||
* index: True if index, False otherwise.
|
||||
* orders: The order (ASC/DESC) defined for the columns of indexes
|
||||
* type: The type of the index (btree, hash, etc.)
|
||||
|
||||
Some backends may return special constraint names that don't exist
|
||||
if they don't name constraints of a certain type (e.g. SQLite)
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseIntrospection may require a get_constraints() "
|
||||
"method"
|
||||
)
|
770
env/lib/python3.8/site-packages/django/db/backends/base/operations.py
vendored
Normal file
770
env/lib/python3.8/site-packages/django/db/backends/base/operations.py
vendored
Normal file
@@ -0,0 +1,770 @@
|
||||
import datetime
|
||||
import decimal
|
||||
from importlib import import_module
|
||||
|
||||
import sqlparse
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import NotSupportedError, transaction
|
||||
from django.db.backends import utils
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
|
||||
class BaseDatabaseOperations:
|
||||
"""
|
||||
Encapsulate backend-specific differences, such as the way a backend
|
||||
performs ordering or calculates the ID of a recently-inserted row.
|
||||
"""
|
||||
|
||||
compiler_module = "django.db.models.sql.compiler"
|
||||
|
||||
# Integer field safe ranges by `internal_type` as documented
|
||||
# in docs/ref/models/fields.txt.
|
||||
integer_field_ranges = {
|
||||
"SmallIntegerField": (-32768, 32767),
|
||||
"IntegerField": (-2147483648, 2147483647),
|
||||
"BigIntegerField": (-9223372036854775808, 9223372036854775807),
|
||||
"PositiveBigIntegerField": (0, 9223372036854775807),
|
||||
"PositiveSmallIntegerField": (0, 32767),
|
||||
"PositiveIntegerField": (0, 2147483647),
|
||||
"SmallAutoField": (-32768, 32767),
|
||||
"AutoField": (-2147483648, 2147483647),
|
||||
"BigAutoField": (-9223372036854775808, 9223372036854775807),
|
||||
}
|
||||
set_operators = {
|
||||
"union": "UNION",
|
||||
"intersection": "INTERSECT",
|
||||
"difference": "EXCEPT",
|
||||
}
|
||||
# Mapping of Field.get_internal_type() (typically the model field's class
|
||||
# name) to the data type to use for the Cast() function, if different from
|
||||
# DatabaseWrapper.data_types.
|
||||
cast_data_types = {}
|
||||
# CharField data type if the max_length argument isn't provided.
|
||||
cast_char_field_without_max_length = None
|
||||
|
||||
# Start and end points for window expressions.
|
||||
PRECEDING = "PRECEDING"
|
||||
FOLLOWING = "FOLLOWING"
|
||||
UNBOUNDED_PRECEDING = "UNBOUNDED " + PRECEDING
|
||||
UNBOUNDED_FOLLOWING = "UNBOUNDED " + FOLLOWING
|
||||
CURRENT_ROW = "CURRENT ROW"
|
||||
|
||||
# Prefix for EXPLAIN queries, or None EXPLAIN isn't supported.
|
||||
explain_prefix = None
|
||||
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
self._cache = None
|
||||
|
||||
def autoinc_sql(self, table, column):
|
||||
"""
|
||||
Return any SQL needed to support auto-incrementing primary keys, or
|
||||
None if no SQL is necessary.
|
||||
|
||||
This SQL is executed when a table is created.
|
||||
"""
|
||||
return None
|
||||
|
||||
def bulk_batch_size(self, fields, objs):
|
||||
"""
|
||||
Return the maximum allowed batch size for the backend. The fields
|
||||
are the fields going to be inserted in the batch, the objs contains
|
||||
all the objects to be inserted.
|
||||
"""
|
||||
return len(objs)
|
||||
|
||||
def format_for_duration_arithmetic(self, sql):
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseOperations may require a "
|
||||
"format_for_duration_arithmetic() method."
|
||||
)
|
||||
|
||||
def cache_key_culling_sql(self):
|
||||
"""
|
||||
Return an SQL query that retrieves the first cache key greater than the
|
||||
n smallest.
|
||||
|
||||
This is used by the 'db' cache backend to determine where to start
|
||||
culling.
|
||||
"""
|
||||
cache_key = self.quote_name("cache_key")
|
||||
return f"SELECT {cache_key} FROM %s ORDER BY {cache_key} LIMIT 1 OFFSET %%s"
|
||||
|
||||
def unification_cast_sql(self, output_field):
|
||||
"""
|
||||
Given a field instance, return the SQL that casts the result of a union
|
||||
to that type. The resulting string should contain a '%s' placeholder
|
||||
for the expression being cast.
|
||||
"""
|
||||
return "%s"
|
||||
|
||||
def date_extract_sql(self, lookup_type, sql, params):
|
||||
"""
|
||||
Given a lookup_type of 'year', 'month', or 'day', return the SQL that
|
||||
extracts a value from the given date field field_name.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseOperations may require a date_extract_sql() "
|
||||
"method"
|
||||
)
|
||||
|
||||
def date_trunc_sql(self, lookup_type, sql, params, tzname=None):
|
||||
"""
|
||||
Given a lookup_type of 'year', 'month', or 'day', return the SQL that
|
||||
truncates the given date or datetime field field_name to a date object
|
||||
with only the given specificity.
|
||||
|
||||
If `tzname` is provided, the given value is truncated in a specific
|
||||
timezone.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseOperations may require a date_trunc_sql() "
|
||||
"method."
|
||||
)
|
||||
|
||||
def datetime_cast_date_sql(self, sql, params, tzname):
|
||||
"""
|
||||
Return the SQL to cast a datetime value to date value.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseOperations may require a "
|
||||
"datetime_cast_date_sql() method."
|
||||
)
|
||||
|
||||
def datetime_cast_time_sql(self, sql, params, tzname):
|
||||
"""
|
||||
Return the SQL to cast a datetime value to time value.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseOperations may require a "
|
||||
"datetime_cast_time_sql() method"
|
||||
)
|
||||
|
||||
def datetime_extract_sql(self, lookup_type, sql, params, tzname):
|
||||
"""
|
||||
Given a lookup_type of 'year', 'month', 'day', 'hour', 'minute', or
|
||||
'second', return the SQL that extracts a value from the given
|
||||
datetime field field_name.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseOperations may require a datetime_extract_sql() "
|
||||
"method"
|
||||
)
|
||||
|
||||
def datetime_trunc_sql(self, lookup_type, sql, params, tzname):
|
||||
"""
|
||||
Given a lookup_type of 'year', 'month', 'day', 'hour', 'minute', or
|
||||
'second', return the SQL that truncates the given datetime field
|
||||
field_name to a datetime object with only the given specificity.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseOperations may require a datetime_trunc_sql() "
|
||||
"method"
|
||||
)
|
||||
|
||||
def time_trunc_sql(self, lookup_type, sql, params, tzname=None):
|
||||
"""
|
||||
Given a lookup_type of 'hour', 'minute' or 'second', return the SQL
|
||||
that truncates the given time or datetime field field_name to a time
|
||||
object with only the given specificity.
|
||||
|
||||
If `tzname` is provided, the given value is truncated in a specific
|
||||
timezone.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseOperations may require a time_trunc_sql() method"
|
||||
)
|
||||
|
||||
def time_extract_sql(self, lookup_type, sql, params):
|
||||
"""
|
||||
Given a lookup_type of 'hour', 'minute', or 'second', return the SQL
|
||||
that extracts a value from the given time field field_name.
|
||||
"""
|
||||
return self.date_extract_sql(lookup_type, sql, params)
|
||||
|
||||
def deferrable_sql(self):
|
||||
"""
|
||||
Return the SQL to make a constraint "initially deferred" during a
|
||||
CREATE TABLE statement.
|
||||
"""
|
||||
return ""
|
||||
|
||||
def distinct_sql(self, fields, params):
|
||||
"""
|
||||
Return an SQL DISTINCT clause which removes duplicate rows from the
|
||||
result set. If any fields are given, only check the given fields for
|
||||
duplicates.
|
||||
"""
|
||||
if fields:
|
||||
raise NotSupportedError(
|
||||
"DISTINCT ON fields is not supported by this database backend"
|
||||
)
|
||||
else:
|
||||
return ["DISTINCT"], []
|
||||
|
||||
def fetch_returned_insert_columns(self, cursor, returning_params):
|
||||
"""
|
||||
Given a cursor object that has just performed an INSERT...RETURNING
|
||||
statement into a table, return the newly created data.
|
||||
"""
|
||||
return cursor.fetchone()
|
||||
|
||||
def field_cast_sql(self, db_type, internal_type):
|
||||
"""
|
||||
Given a column type (e.g. 'BLOB', 'VARCHAR') and an internal type
|
||||
(e.g. 'GenericIPAddressField'), return the SQL to cast it before using
|
||||
it in a WHERE statement. The resulting string should contain a '%s'
|
||||
placeholder for the column being searched against.
|
||||
"""
|
||||
return "%s"
|
||||
|
||||
def force_no_ordering(self):
|
||||
"""
|
||||
Return a list used in the "ORDER BY" clause to force no ordering at
|
||||
all. Return an empty list to include nothing in the ordering.
|
||||
"""
|
||||
return []
|
||||
|
||||
def for_update_sql(self, nowait=False, skip_locked=False, of=(), no_key=False):
|
||||
"""
|
||||
Return the FOR UPDATE SQL clause to lock rows for an update operation.
|
||||
"""
|
||||
return "FOR%s UPDATE%s%s%s" % (
|
||||
" NO KEY" if no_key else "",
|
||||
" OF %s" % ", ".join(of) if of else "",
|
||||
" NOWAIT" if nowait else "",
|
||||
" SKIP LOCKED" if skip_locked else "",
|
||||
)
|
||||
|
||||
def _get_limit_offset_params(self, low_mark, high_mark):
|
||||
offset = low_mark or 0
|
||||
if high_mark is not None:
|
||||
return (high_mark - offset), offset
|
||||
elif offset:
|
||||
return self.connection.ops.no_limit_value(), offset
|
||||
return None, offset
|
||||
|
||||
def limit_offset_sql(self, low_mark, high_mark):
|
||||
"""Return LIMIT/OFFSET SQL clause."""
|
||||
limit, offset = self._get_limit_offset_params(low_mark, high_mark)
|
||||
return " ".join(
|
||||
sql
|
||||
for sql in (
|
||||
("LIMIT %d" % limit) if limit else None,
|
||||
("OFFSET %d" % offset) if offset else None,
|
||||
)
|
||||
if sql
|
||||
)
|
||||
|
||||
def last_executed_query(self, cursor, sql, params):
|
||||
"""
|
||||
Return a string of the query last executed by the given cursor, with
|
||||
placeholders replaced with actual values.
|
||||
|
||||
`sql` is the raw query containing placeholders and `params` is the
|
||||
sequence of parameters. These are used by default, but this method
|
||||
exists for database backends to provide a better implementation
|
||||
according to their own quoting schemes.
|
||||
"""
|
||||
# Convert params to contain string values.
|
||||
def to_string(s):
|
||||
return force_str(s, strings_only=True, errors="replace")
|
||||
|
||||
if isinstance(params, (list, tuple)):
|
||||
u_params = tuple(to_string(val) for val in params)
|
||||
elif params is None:
|
||||
u_params = ()
|
||||
else:
|
||||
u_params = {to_string(k): to_string(v) for k, v in params.items()}
|
||||
|
||||
return "QUERY = %r - PARAMS = %r" % (sql, u_params)
|
||||
|
||||
def last_insert_id(self, cursor, table_name, pk_name):
|
||||
"""
|
||||
Given a cursor object that has just performed an INSERT statement into
|
||||
a table that has an auto-incrementing ID, return the newly created ID.
|
||||
|
||||
`pk_name` is the name of the primary-key column.
|
||||
"""
|
||||
return cursor.lastrowid
|
||||
|
||||
def lookup_cast(self, lookup_type, internal_type=None):
|
||||
"""
|
||||
Return the string to use in a query when performing lookups
|
||||
("contains", "like", etc.). It should contain a '%s' placeholder for
|
||||
the column being searched against.
|
||||
"""
|
||||
return "%s"
|
||||
|
||||
def max_in_list_size(self):
|
||||
"""
|
||||
Return the maximum number of items that can be passed in a single 'IN'
|
||||
list condition, or None if the backend does not impose a limit.
|
||||
"""
|
||||
return None
|
||||
|
||||
def max_name_length(self):
|
||||
"""
|
||||
Return the maximum length of table and column names, or None if there
|
||||
is no limit.
|
||||
"""
|
||||
return None
|
||||
|
||||
def no_limit_value(self):
|
||||
"""
|
||||
Return the value to use for the LIMIT when we are wanting "LIMIT
|
||||
infinity". Return None if the limit clause can be omitted in this case.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseOperations may require a no_limit_value() method"
|
||||
)
|
||||
|
||||
def pk_default_value(self):
|
||||
"""
|
||||
Return the value to use during an INSERT statement to specify that
|
||||
the field should use its default value.
|
||||
"""
|
||||
return "DEFAULT"
|
||||
|
||||
def prepare_sql_script(self, sql):
|
||||
"""
|
||||
Take an SQL script that may contain multiple lines and return a list
|
||||
of statements to feed to successive cursor.execute() calls.
|
||||
|
||||
Since few databases are able to process raw SQL scripts in a single
|
||||
cursor.execute() call and PEP 249 doesn't talk about this use case,
|
||||
the default implementation is conservative.
|
||||
"""
|
||||
return [
|
||||
sqlparse.format(statement, strip_comments=True)
|
||||
for statement in sqlparse.split(sql)
|
||||
if statement
|
||||
]
|
||||
|
||||
def process_clob(self, value):
|
||||
"""
|
||||
Return the value of a CLOB column, for backends that return a locator
|
||||
object that requires additional processing.
|
||||
"""
|
||||
return value
|
||||
|
||||
def return_insert_columns(self, fields):
|
||||
"""
|
||||
For backends that support returning columns as part of an insert query,
|
||||
return the SQL and params to append to the INSERT query. The returned
|
||||
fragment should contain a format string to hold the appropriate column.
|
||||
"""
|
||||
pass
|
||||
|
||||
def compiler(self, compiler_name):
|
||||
"""
|
||||
Return the SQLCompiler class corresponding to the given name,
|
||||
in the namespace corresponding to the `compiler_module` attribute
|
||||
on this backend.
|
||||
"""
|
||||
if self._cache is None:
|
||||
self._cache = import_module(self.compiler_module)
|
||||
return getattr(self._cache, compiler_name)
|
||||
|
||||
def quote_name(self, name):
|
||||
"""
|
||||
Return a quoted version of the given table, index, or column name. Do
|
||||
not quote the given name if it's already been quoted.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseOperations may require a quote_name() method"
|
||||
)
|
||||
|
||||
def regex_lookup(self, lookup_type):
|
||||
"""
|
||||
Return the string to use in a query when performing regular expression
|
||||
lookups (using "regex" or "iregex"). It should contain a '%s'
|
||||
placeholder for the column being searched against.
|
||||
|
||||
If the feature is not supported (or part of it is not supported), raise
|
||||
NotImplementedError.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseOperations may require a regex_lookup() method"
|
||||
)
|
||||
|
||||
def savepoint_create_sql(self, sid):
|
||||
"""
|
||||
Return the SQL for starting a new savepoint. Only required if the
|
||||
"uses_savepoints" feature is True. The "sid" parameter is a string
|
||||
for the savepoint id.
|
||||
"""
|
||||
return "SAVEPOINT %s" % self.quote_name(sid)
|
||||
|
||||
def savepoint_commit_sql(self, sid):
|
||||
"""
|
||||
Return the SQL for committing the given savepoint.
|
||||
"""
|
||||
return "RELEASE SAVEPOINT %s" % self.quote_name(sid)
|
||||
|
||||
def savepoint_rollback_sql(self, sid):
|
||||
"""
|
||||
Return the SQL for rolling back the given savepoint.
|
||||
"""
|
||||
return "ROLLBACK TO SAVEPOINT %s" % self.quote_name(sid)
|
||||
|
||||
def set_time_zone_sql(self):
|
||||
"""
|
||||
Return the SQL that will set the connection's time zone.
|
||||
|
||||
Return '' if the backend doesn't support time zones.
|
||||
"""
|
||||
return ""
|
||||
|
||||
def sql_flush(self, style, tables, *, reset_sequences=False, allow_cascade=False):
|
||||
"""
|
||||
Return a list of SQL statements required to remove all data from
|
||||
the given database tables (without actually removing the tables
|
||||
themselves).
|
||||
|
||||
The `style` argument is a Style object as returned by either
|
||||
color_style() or no_style() in django.core.management.color.
|
||||
|
||||
If `reset_sequences` is True, the list includes SQL statements required
|
||||
to reset the sequences.
|
||||
|
||||
The `allow_cascade` argument determines whether truncation may cascade
|
||||
to tables with foreign keys pointing the tables being truncated.
|
||||
PostgreSQL requires a cascade even if these tables are empty.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseOperations must provide an sql_flush() method"
|
||||
)
|
||||
|
||||
def execute_sql_flush(self, sql_list):
|
||||
"""Execute a list of SQL statements to flush the database."""
|
||||
with transaction.atomic(
|
||||
using=self.connection.alias,
|
||||
savepoint=self.connection.features.can_rollback_ddl,
|
||||
):
|
||||
with self.connection.cursor() as cursor:
|
||||
for sql in sql_list:
|
||||
cursor.execute(sql)
|
||||
|
||||
def sequence_reset_by_name_sql(self, style, sequences):
|
||||
"""
|
||||
Return a list of the SQL statements required to reset sequences
|
||||
passed in `sequences`.
|
||||
|
||||
The `style` argument is a Style object as returned by either
|
||||
color_style() or no_style() in django.core.management.color.
|
||||
"""
|
||||
return []
|
||||
|
||||
def sequence_reset_sql(self, style, model_list):
|
||||
"""
|
||||
Return a list of the SQL statements required to reset sequences for
|
||||
the given models.
|
||||
|
||||
The `style` argument is a Style object as returned by either
|
||||
color_style() or no_style() in django.core.management.color.
|
||||
"""
|
||||
return [] # No sequence reset required by default.
|
||||
|
||||
def start_transaction_sql(self):
|
||||
"""Return the SQL statement required to start a transaction."""
|
||||
return "BEGIN;"
|
||||
|
||||
def end_transaction_sql(self, success=True):
|
||||
"""Return the SQL statement required to end a transaction."""
|
||||
if not success:
|
||||
return "ROLLBACK;"
|
||||
return "COMMIT;"
|
||||
|
||||
def tablespace_sql(self, tablespace, inline=False):
|
||||
"""
|
||||
Return the SQL that will be used in a query to define the tablespace.
|
||||
|
||||
Return '' if the backend doesn't support tablespaces.
|
||||
|
||||
If `inline` is True, append the SQL to a row; otherwise append it to
|
||||
the entire CREATE TABLE or CREATE INDEX statement.
|
||||
"""
|
||||
return ""
|
||||
|
||||
def prep_for_like_query(self, x):
|
||||
"""Prepare a value for use in a LIKE query."""
|
||||
return str(x).replace("\\", "\\\\").replace("%", r"\%").replace("_", r"\_")
|
||||
|
||||
# Same as prep_for_like_query(), but called for "iexact" matches, which
|
||||
# need not necessarily be implemented using "LIKE" in the backend.
|
||||
prep_for_iexact_query = prep_for_like_query
|
||||
|
||||
def validate_autopk_value(self, value):
|
||||
"""
|
||||
Certain backends do not accept some values for "serial" fields
|
||||
(for example zero in MySQL). Raise a ValueError if the value is
|
||||
invalid, otherwise return the validated value.
|
||||
"""
|
||||
return value
|
||||
|
||||
def adapt_unknown_value(self, value):
|
||||
"""
|
||||
Transform a value to something compatible with the backend driver.
|
||||
|
||||
This method only depends on the type of the value. It's designed for
|
||||
cases where the target type isn't known, such as .raw() SQL queries.
|
||||
As a consequence it may not work perfectly in all circumstances.
|
||||
"""
|
||||
if isinstance(value, datetime.datetime): # must be before date
|
||||
return self.adapt_datetimefield_value(value)
|
||||
elif isinstance(value, datetime.date):
|
||||
return self.adapt_datefield_value(value)
|
||||
elif isinstance(value, datetime.time):
|
||||
return self.adapt_timefield_value(value)
|
||||
elif isinstance(value, decimal.Decimal):
|
||||
return self.adapt_decimalfield_value(value)
|
||||
else:
|
||||
return value
|
||||
|
||||
def adapt_datefield_value(self, value):
|
||||
"""
|
||||
Transform a date value to an object compatible with what is expected
|
||||
by the backend driver for date columns.
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
return str(value)
|
||||
|
||||
def adapt_datetimefield_value(self, value):
|
||||
"""
|
||||
Transform a datetime value to an object compatible with what is expected
|
||||
by the backend driver for datetime columns.
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
# Expression values are adapted by the database.
|
||||
if hasattr(value, "resolve_expression"):
|
||||
return value
|
||||
|
||||
return str(value)
|
||||
|
||||
def adapt_timefield_value(self, value):
|
||||
"""
|
||||
Transform a time value to an object compatible with what is expected
|
||||
by the backend driver for time columns.
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
# Expression values are adapted by the database.
|
||||
if hasattr(value, "resolve_expression"):
|
||||
return value
|
||||
|
||||
if timezone.is_aware(value):
|
||||
raise ValueError("Django does not support timezone-aware times.")
|
||||
return str(value)
|
||||
|
||||
def adapt_decimalfield_value(self, value, max_digits=None, decimal_places=None):
|
||||
"""
|
||||
Transform a decimal.Decimal value to an object compatible with what is
|
||||
expected by the backend driver for decimal (numeric) columns.
|
||||
"""
|
||||
return utils.format_number(value, max_digits, decimal_places)
|
||||
|
||||
def adapt_ipaddressfield_value(self, value):
|
||||
"""
|
||||
Transform a string representation of an IP address into the expected
|
||||
type for the backend driver.
|
||||
"""
|
||||
return value or None
|
||||
|
||||
def year_lookup_bounds_for_date_field(self, value, iso_year=False):
|
||||
"""
|
||||
Return a two-elements list with the lower and upper bound to be used
|
||||
with a BETWEEN operator to query a DateField value using a year
|
||||
lookup.
|
||||
|
||||
`value` is an int, containing the looked-up year.
|
||||
If `iso_year` is True, return bounds for ISO-8601 week-numbering years.
|
||||
"""
|
||||
if iso_year:
|
||||
first = datetime.date.fromisocalendar(value, 1, 1)
|
||||
second = datetime.date.fromisocalendar(
|
||||
value + 1, 1, 1
|
||||
) - datetime.timedelta(days=1)
|
||||
else:
|
||||
first = datetime.date(value, 1, 1)
|
||||
second = datetime.date(value, 12, 31)
|
||||
first = self.adapt_datefield_value(first)
|
||||
second = self.adapt_datefield_value(second)
|
||||
return [first, second]
|
||||
|
||||
def year_lookup_bounds_for_datetime_field(self, value, iso_year=False):
|
||||
"""
|
||||
Return a two-elements list with the lower and upper bound to be used
|
||||
with a BETWEEN operator to query a DateTimeField value using a year
|
||||
lookup.
|
||||
|
||||
`value` is an int, containing the looked-up year.
|
||||
If `iso_year` is True, return bounds for ISO-8601 week-numbering years.
|
||||
"""
|
||||
if iso_year:
|
||||
first = datetime.datetime.fromisocalendar(value, 1, 1)
|
||||
second = datetime.datetime.fromisocalendar(
|
||||
value + 1, 1, 1
|
||||
) - datetime.timedelta(microseconds=1)
|
||||
else:
|
||||
first = datetime.datetime(value, 1, 1)
|
||||
second = datetime.datetime(value, 12, 31, 23, 59, 59, 999999)
|
||||
if settings.USE_TZ:
|
||||
tz = timezone.get_current_timezone()
|
||||
first = timezone.make_aware(first, tz)
|
||||
second = timezone.make_aware(second, tz)
|
||||
first = self.adapt_datetimefield_value(first)
|
||||
second = self.adapt_datetimefield_value(second)
|
||||
return [first, second]
|
||||
|
||||
def get_db_converters(self, expression):
|
||||
"""
|
||||
Return a list of functions needed to convert field data.
|
||||
|
||||
Some field types on some backends do not provide data in the correct
|
||||
format, this is the hook for converter functions.
|
||||
"""
|
||||
return []
|
||||
|
||||
def convert_durationfield_value(self, value, expression, connection):
|
||||
if value is not None:
|
||||
return datetime.timedelta(0, 0, value)
|
||||
|
||||
def check_expression_support(self, expression):
|
||||
"""
|
||||
Check that the backend supports the provided expression.
|
||||
|
||||
This is used on specific backends to rule out known expressions
|
||||
that have problematic or nonexistent implementations. If the
|
||||
expression has a known problem, the backend should raise
|
||||
NotSupportedError.
|
||||
"""
|
||||
pass
|
||||
|
||||
def conditional_expression_supported_in_where_clause(self, expression):
|
||||
"""
|
||||
Return True, if the conditional expression is supported in the WHERE
|
||||
clause.
|
||||
"""
|
||||
return True
|
||||
|
||||
def combine_expression(self, connector, sub_expressions):
|
||||
"""
|
||||
Combine a list of subexpressions into a single expression, using
|
||||
the provided connecting operator. This is required because operators
|
||||
can vary between backends (e.g., Oracle with %% and &) and between
|
||||
subexpression types (e.g., date expressions).
|
||||
"""
|
||||
conn = " %s " % connector
|
||||
return conn.join(sub_expressions)
|
||||
|
||||
def combine_duration_expression(self, connector, sub_expressions):
|
||||
return self.combine_expression(connector, sub_expressions)
|
||||
|
||||
def binary_placeholder_sql(self, value):
|
||||
"""
|
||||
Some backends require special syntax to insert binary content (MySQL
|
||||
for example uses '_binary %s').
|
||||
"""
|
||||
return "%s"
|
||||
|
||||
def modify_insert_params(self, placeholder, params):
|
||||
"""
|
||||
Allow modification of insert parameters. Needed for Oracle Spatial
|
||||
backend due to #10888.
|
||||
"""
|
||||
return params
|
||||
|
||||
def integer_field_range(self, internal_type):
|
||||
"""
|
||||
Given an integer field internal type (e.g. 'PositiveIntegerField'),
|
||||
return a tuple of the (min_value, max_value) form representing the
|
||||
range of the column type bound to the field.
|
||||
"""
|
||||
return self.integer_field_ranges[internal_type]
|
||||
|
||||
def subtract_temporals(self, internal_type, lhs, rhs):
|
||||
if self.connection.features.supports_temporal_subtraction:
|
||||
lhs_sql, lhs_params = lhs
|
||||
rhs_sql, rhs_params = rhs
|
||||
return "(%s - %s)" % (lhs_sql, rhs_sql), (*lhs_params, *rhs_params)
|
||||
raise NotSupportedError(
|
||||
"This backend does not support %s subtraction." % internal_type
|
||||
)
|
||||
|
||||
def window_frame_start(self, start):
|
||||
if isinstance(start, int):
|
||||
if start < 0:
|
||||
return "%d %s" % (abs(start), self.PRECEDING)
|
||||
elif start == 0:
|
||||
return self.CURRENT_ROW
|
||||
elif start is None:
|
||||
return self.UNBOUNDED_PRECEDING
|
||||
raise ValueError(
|
||||
"start argument must be a negative integer, zero, or None, but got '%s'."
|
||||
% start
|
||||
)
|
||||
|
||||
def window_frame_end(self, end):
|
||||
if isinstance(end, int):
|
||||
if end == 0:
|
||||
return self.CURRENT_ROW
|
||||
elif end > 0:
|
||||
return "%d %s" % (end, self.FOLLOWING)
|
||||
elif end is None:
|
||||
return self.UNBOUNDED_FOLLOWING
|
||||
raise ValueError(
|
||||
"end argument must be a positive integer, zero, or None, but got '%s'."
|
||||
% end
|
||||
)
|
||||
|
||||
def window_frame_rows_start_end(self, start=None, end=None):
|
||||
"""
|
||||
Return SQL for start and end points in an OVER clause window frame.
|
||||
"""
|
||||
if not self.connection.features.supports_over_clause:
|
||||
raise NotSupportedError("This backend does not support window expressions.")
|
||||
return self.window_frame_start(start), self.window_frame_end(end)
|
||||
|
||||
def window_frame_range_start_end(self, start=None, end=None):
|
||||
start_, end_ = self.window_frame_rows_start_end(start, end)
|
||||
features = self.connection.features
|
||||
if features.only_supports_unbounded_with_preceding_and_following and (
|
||||
(start and start < 0) or (end and end > 0)
|
||||
):
|
||||
raise NotSupportedError(
|
||||
"%s only supports UNBOUNDED together with PRECEDING and "
|
||||
"FOLLOWING." % self.connection.display_name
|
||||
)
|
||||
return start_, end_
|
||||
|
||||
def explain_query_prefix(self, format=None, **options):
|
||||
if not self.connection.features.supports_explaining_query_execution:
|
||||
raise NotSupportedError(
|
||||
"This backend does not support explaining query execution."
|
||||
)
|
||||
if format:
|
||||
supported_formats = self.connection.features.supported_explain_formats
|
||||
normalized_format = format.upper()
|
||||
if normalized_format not in supported_formats:
|
||||
msg = "%s is not a recognized format." % normalized_format
|
||||
if supported_formats:
|
||||
msg += " Allowed formats: %s" % ", ".join(sorted(supported_formats))
|
||||
else:
|
||||
msg += (
|
||||
f" {self.connection.display_name} does not support any formats."
|
||||
)
|
||||
raise ValueError(msg)
|
||||
if options:
|
||||
raise ValueError("Unknown options: %s" % ", ".join(sorted(options.keys())))
|
||||
return self.explain_prefix
|
||||
|
||||
def insert_statement(self, on_conflict=None):
|
||||
return "INSERT INTO"
|
||||
|
||||
def on_conflict_suffix_sql(self, fields, on_conflict, update_fields, unique_fields):
|
||||
return ""
|
1749
env/lib/python3.8/site-packages/django/db/backends/base/schema.py
vendored
Normal file
1749
env/lib/python3.8/site-packages/django/db/backends/base/schema.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
29
env/lib/python3.8/site-packages/django/db/backends/base/validation.py
vendored
Normal file
29
env/lib/python3.8/site-packages/django/db/backends/base/validation.py
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
class BaseDatabaseValidation:
|
||||
"""Encapsulate backend-specific validation."""
|
||||
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
|
||||
def check(self, **kwargs):
|
||||
return []
|
||||
|
||||
def check_field(self, field, **kwargs):
|
||||
errors = []
|
||||
# Backends may implement a check_field_type() method.
|
||||
if (
|
||||
hasattr(self, "check_field_type")
|
||||
and
|
||||
# Ignore any related fields.
|
||||
not getattr(field, "remote_field", None)
|
||||
):
|
||||
# Ignore fields with unsupported features.
|
||||
db_supports_all_required_features = all(
|
||||
getattr(self.connection.features, feature, False)
|
||||
for feature in field.model._meta.required_db_features
|
||||
)
|
||||
if db_supports_all_required_features:
|
||||
field_type = field.db_type(self.connection)
|
||||
# Ignore non-concrete fields.
|
||||
if field_type is not None:
|
||||
errors.extend(self.check_field_type(field, field_type))
|
||||
return errors
|
Reference in New Issue
Block a user