diff options
Diffstat (limited to 'django')
| -rw-r--r-- | django/db/backends/base/base.py | 34 | ||||
| -rw-r--r-- | django/db/backends/base/features.py | 2 | ||||
| -rw-r--r-- | django/db/backends/mysql/base.py | 4 | ||||
| -rw-r--r-- | django/db/backends/mysql/features.py | 7 | ||||
| -rw-r--r-- | django/db/backends/oracle/base.py | 4 | ||||
| -rw-r--r-- | django/db/backends/oracle/features.py | 1 | ||||
| -rw-r--r-- | django/db/backends/postgresql/base.py | 8 | ||||
| -rw-r--r-- | django/db/backends/postgresql/features.py | 1 | ||||
| -rw-r--r-- | django/db/backends/sqlite3/base.py | 15 | ||||
| -rw-r--r-- | django/db/backends/sqlite3/features.py | 1 |
10 files changed, 60 insertions, 17 deletions
diff --git a/django/db/backends/base/base.py b/django/db/backends/base/base.py index d2e79c1dd4..f093f2bd8b 100644 --- a/django/db/backends/base/base.py +++ b/django/db/backends/base/base.py @@ -13,7 +13,7 @@ except ImportError: from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from django.db import DEFAULT_DB_ALIAS, DatabaseError +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 @@ -24,6 +24,7 @@ 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 @@ -185,6 +186,29 @@ class BaseDatabaseWrapper: ) 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): @@ -203,10 +227,10 @@ class BaseDatabaseWrapper: def init_connection_state(self): """Initialize the database connection settings.""" - raise NotImplementedError( - "subclasses of BaseDatabaseWrapper may require an init_connection_state() " - "method" - ) + 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.""" diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py index 42399b769a..ccf9104c21 100644 --- a/django/db/backends/base/features.py +++ b/django/db/backends/base/features.py @@ -3,6 +3,8 @@ 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 diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 1c20554b4d..ca12917322 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -200,6 +200,9 @@ class DatabaseWrapper(BaseDatabaseWrapper): ops_class = DatabaseOperations validation_class = DatabaseValidation + def get_database_version(self): + return self.mysql_version + def get_connection_params(self): kwargs = { "conv": django_conversions, @@ -251,6 +254,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): return connection def init_connection_state(self): + super().init_connection_state() assignments = [] if self.features.is_sql_auto_is_null_enabled: # SQL_AUTO_IS_NULL controls whether an AUTO_INCREMENT column on diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py index e27e766e48..f5b9ef9b55 100644 --- a/django/db/backends/mysql/features.py +++ b/django/db/backends/mysql/features.py @@ -49,6 +49,13 @@ class DatabaseFeatures(BaseDatabaseFeatures): order_by_nulls_first = True @cached_property + def minimum_database_version(self): + if self.connection.mysql_is_mariadb: + return (10, 2) + else: + return (5, 7) + + @cached_property def test_collations(self): charset = "utf8" if self.connection.mysql_is_mariadb and self.connection.mysql_version >= ( diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 7cbee768ea..2ccd3bc028 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -239,6 +239,9 @@ class DatabaseWrapper(BaseDatabaseWrapper): ) self.features.can_return_columns_from_insert = use_returning_into + def get_database_version(self): + return self.oracle_version + def get_connection_params(self): conn_params = self.settings_dict["OPTIONS"].copy() if "use_returning_into" in conn_params: @@ -255,6 +258,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): ) def init_connection_state(self): + super().init_connection_state() cursor = self.create_cursor() # Set the territory first. The territory overrides NLS_DATE_FORMAT # and NLS_TIMESTAMP_FORMAT to the territory default. When all of diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py index 257c3e1b87..2580e5c6c1 100644 --- a/django/db/backends/oracle/features.py +++ b/django/db/backends/oracle/features.py @@ -4,6 +4,7 @@ from django.utils.functional import cached_property class DatabaseFeatures(BaseDatabaseFeatures): + minimum_database_version = (19,) # Oracle crashes with "ORA-00932: inconsistent datatypes: expected - got # BLOB" when grouping by LOBs (#24096). allows_group_by_lob = False diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index 92f393227e..630da22964 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -153,6 +153,13 @@ class DatabaseWrapper(BaseDatabaseWrapper): # PostgreSQL backend-specific attributes. _named_cursor_idx = 0 + def get_database_version(self): + """ + Return a tuple of the database's version. + E.g. for pg_version 120004, return (12, 4). + """ + return divmod(self.pg_version, 10000) + def get_connection_params(self): settings_dict = self.settings_dict # None may be used to connect to the default 'postgres' db @@ -236,6 +243,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): return False def init_connection_state(self): + super().init_connection_state() self.connection.set_client_encoding("UTF8") timezone_changed = self.ensure_timezone() diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py index 7c5d09d193..5e6752b97a 100644 --- a/django/db/backends/postgresql/features.py +++ b/django/db/backends/postgresql/features.py @@ -6,6 +6,7 @@ from django.utils.functional import cached_property class DatabaseFeatures(BaseDatabaseFeatures): + minimum_database_version = (10,) allows_group_by_selected_pks = True can_return_columns_from_insert = True can_return_rows_from_bulk_insert = True diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 5bcd61eb96..8ca076a1d9 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -29,15 +29,6 @@ def decoder(conv_func): return lambda s: conv_func(s.decode()) -def check_sqlite_version(): - if Database.sqlite_version_info < (3, 9, 0): - raise ImproperlyConfigured( - "SQLite 3.9.0 or later is required (found %s)." % Database.sqlite_version - ) - - -check_sqlite_version() - Database.register_converter("bool", b"1".__eq__) Database.register_converter("time", decoder(parse_time)) Database.register_converter("datetime", decoder(parse_datetime)) @@ -168,6 +159,9 @@ class DatabaseWrapper(BaseDatabaseWrapper): kwargs.update({"check_same_thread": False, "uri": True}) return kwargs + def get_database_version(self): + return self.Database.sqlite_version_info + @async_unsafe def get_new_connection(self, conn_params): conn = Database.connect(**conn_params) @@ -179,9 +173,6 @@ class DatabaseWrapper(BaseDatabaseWrapper): conn.execute("PRAGMA legacy_alter_table = OFF") return conn - def init_connection_state(self): - pass - def create_cursor(self, name=None): return self.connection.cursor(factory=SQLiteCursorWrapper) diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py index 9161ae3133..2886ecc3be 100644 --- a/django/db/backends/sqlite3/features.py +++ b/django/db/backends/sqlite3/features.py @@ -9,6 +9,7 @@ from .base import Database class DatabaseFeatures(BaseDatabaseFeatures): + minimum_database_version = (3, 9) test_db_allows_multiple_connections = False supports_unspecified_pk = True supports_timezones = False |
