Commit aa423575 authored by Claude Paroz's avatar Claude Paroz
Browse files

Fixed #17760 -- Implemented callable database features as cached properties

This does remove the requirement to call features.confirm() method
before checking the properties.
Thanks cdestiger and Ramiro Morales for their work on the patch.
parent 484fcd34
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -31,9 +31,6 @@ class SpatiaLiteCreation(DatabaseCreation):
        self.connection.close()
        self.connection.settings_dict["NAME"] = test_database_name

        # Confirm the feature set of the test database
        self.connection.features.confirm()

        # Need to load the SpatiaLite initialization SQL before running `syncdb`.
        self.load_spatialite_sql()

+9 −22
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ from django.conf import settings
from django.db import DEFAULT_DB_ALIAS
from django.db.backends import util
from django.db.transaction import TransactionManagementError
from django.utils.functional import cached_property
from django.utils.importlib import import_module
from django.utils.timezone import is_aware

@@ -402,12 +403,10 @@ class BaseDatabaseFeatures(object):
    # Does the backend reset sequences between tests?
    supports_sequence_reset = True

    # Features that need to be confirmed at runtime
    # Cache whether the confirmation has been performed.
    _confirmed = False
    supports_transactions = None
    supports_stddev = None
    can_introspect_foreign_keys = None
    # 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

    # Support for the DISTINCT ON clause
    can_distinct_on_fields = False
@@ -415,15 +414,8 @@ class BaseDatabaseFeatures(object):
    def __init__(self, connection):
        self.connection = connection

    def confirm(self):
        "Perform manual checks of any database features that might vary between installs"
        if not self._confirmed:
            self._confirmed = True
            self.supports_transactions = self._supports_transactions()
            self.supports_stddev = self._supports_stddev()
            self.can_introspect_foreign_keys = self._can_introspect_foreign_keys()

    def _supports_transactions(self):
    @cached_property
    def supports_transactions(self):
        "Confirm support for transactions"
        cursor = self.connection.cursor()
        cursor.execute('CREATE TABLE ROLLBACK_TEST (X INT)')
@@ -436,7 +428,8 @@ class BaseDatabaseFeatures(object):
        self.connection._commit()
        return count == 0

    def _supports_stddev(self):
    @cached_property
    def supports_stddev(self):
        "Confirm support for STDDEV and related stats functions"
        class StdDevPop(object):
            sql_function = 'STDDEV_POP'
@@ -447,12 +440,6 @@ class BaseDatabaseFeatures(object):
        except NotImplementedError:
            return False

    def _can_introspect_foreign_keys(self):
        "Confirm support for introspected foreign keys"
        # Every database can do this reliably, except MySQL,
        # which can't do it for MyISAM tables
        return True


class BaseDatabaseOperations(object):
    """
+0 −3
Original line number Diff line number Diff line
@@ -264,9 +264,6 @@ class BaseDatabaseCreation(object):
        self.connection.close()
        self.connection.settings_dict["NAME"] = test_database_name

        # Confirm the feature set of the test database
        self.connection.features.confirm()

        # Report syncdb 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)
+16 −16
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ from django.db.backends.mysql.client import DatabaseClient
from django.db.backends.mysql.creation import DatabaseCreation
from django.db.backends.mysql.introspection import DatabaseIntrospection
from django.db.backends.mysql.validation import DatabaseValidation
from django.utils.functional import cached_property
from django.utils.safestring import SafeString, SafeUnicode
from django.utils import timezone

@@ -170,11 +171,10 @@ class DatabaseFeatures(BaseDatabaseFeatures):

    def __init__(self, connection):
        super(DatabaseFeatures, self).__init__(connection)
        self._storage_engine = None

    @cached_property
    def _mysql_storage_engine(self):
        "Internal method used in Django tests. Don't rely on this from your code"
        if self._storage_engine is None:
        cursor = self.connection.cursor()
        cursor.execute('CREATE TABLE INTROSPECT_TEST (X INT)')
        # This command is MySQL specific; the second column
@@ -184,12 +184,12 @@ class DatabaseFeatures(BaseDatabaseFeatures):
        cursor.execute("SHOW TABLE STATUS WHERE Name='INTROSPECT_TEST'")
        result = cursor.fetchone()
        cursor.execute('DROP TABLE INTROSPECT_TEST')
            self._storage_engine = result[1]
        return self._storage_engine
        return result[1]

    def _can_introspect_foreign_keys(self):
    @cached_property
    def can_introspect_foreign_keys(self):
        "Confirm support for introspected foreign keys"
        return self._mysql_storage_engine() != 'MyISAM'
        return self._mysql_storage_engine != 'MyISAM'

class DatabaseOperations(BaseDatabaseOperations):
    compiler_module = "django.db.backends.mysql.compiler"
+3 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ from django.db.backends.sqlite3.client import DatabaseClient
from django.db.backends.sqlite3.creation import DatabaseCreation
from django.db.backends.sqlite3.introspection import DatabaseIntrospection
from django.utils.dateparse import parse_date, parse_datetime, parse_time
from django.utils.functional import cached_property
from django.utils.safestring import SafeString
from django.utils import timezone

@@ -86,7 +87,8 @@ class DatabaseFeatures(BaseDatabaseFeatures):
    has_bulk_insert = True
    can_combine_inserts_with_and_without_auto_increment_pk = True

    def _supports_stddev(self):
    @cached_property
    def supports_stddev(self):
        """Confirm support for STDDEV and related stats functions

        SQLite supports STDDEV as an extension package; so
Loading