summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2022-12-08 05:53:18 +0100
committerGitHub <noreply@github.com>2022-12-08 05:53:18 +0100
commit95a101a690402076321331d56e67d896e8f6dfbc (patch)
treeeb2ef498880cc09229fce49b26fca97d8a1e00ea
parent0036bcdcb65874f63fff8139fe86574fa155eb26 (diff)
Fixed #34201 -- Bumped minimum supported SQLite to 3.21.0.
-rw-r--r--django/db/backends/sqlite3/base.py142
-rw-r--r--django/db/backends/sqlite3/features.py8
-rw-r--r--docs/ref/contrib/gis/install/index.txt2
-rw-r--r--docs/ref/databases.txt2
-rw-r--r--docs/releases/4.2.txt2
-rw-r--r--tests/backends/sqlite/tests.py4
6 files changed, 54 insertions, 106 deletions
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index cc46434f99..a3a382a56b 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -239,103 +239,53 @@ class DatabaseWrapper(BaseDatabaseWrapper):
determine if rows with invalid references were entered while constraint
checks were off.
"""
- if self.features.supports_pragma_foreign_key_check:
- with self.cursor() as cursor:
- if table_names is None:
- violations = cursor.execute("PRAGMA foreign_key_check").fetchall()
- else:
- violations = chain.from_iterable(
- cursor.execute(
- "PRAGMA foreign_key_check(%s)"
- % self.ops.quote_name(table_name)
- ).fetchall()
- for table_name in table_names
- )
- # See https://www.sqlite.org/pragma.html#pragma_foreign_key_check
- for (
- table_name,
- rowid,
- referenced_table_name,
- foreign_key_index,
- ) in violations:
- foreign_key = cursor.execute(
- "PRAGMA foreign_key_list(%s)" % self.ops.quote_name(table_name)
- ).fetchall()[foreign_key_index]
- column_name, referenced_column_name = foreign_key[3:5]
- primary_key_column_name = self.introspection.get_primary_key_column(
- cursor, table_name
- )
- primary_key_value, bad_value = cursor.execute(
- "SELECT %s, %s FROM %s WHERE rowid = %%s"
- % (
- self.ops.quote_name(primary_key_column_name),
- self.ops.quote_name(column_name),
- self.ops.quote_name(table_name),
- ),
- (rowid,),
- ).fetchone()
- raise IntegrityError(
- "The row in table '%s' with primary key '%s' has an "
- "invalid foreign key: %s.%s contains a value '%s' that "
- "does not have a corresponding value in %s.%s."
- % (
- table_name,
- primary_key_value,
- table_name,
- column_name,
- bad_value,
- referenced_table_name,
- referenced_column_name,
- )
- )
- else:
- with self.cursor() as cursor:
- if table_names is None:
- table_names = self.introspection.table_names(cursor)
- for table_name in table_names:
- primary_key_column_name = self.introspection.get_primary_key_column(
- cursor, table_name
- )
- if not primary_key_column_name:
- continue
- relations = self.introspection.get_relations(cursor, table_name)
- for column_name, (
- referenced_column_name,
+ with self.cursor() as cursor:
+ if table_names is None:
+ violations = cursor.execute("PRAGMA foreign_key_check").fetchall()
+ else:
+ violations = chain.from_iterable(
+ cursor.execute(
+ "PRAGMA foreign_key_check(%s)" % self.ops.quote_name(table_name)
+ ).fetchall()
+ for table_name in table_names
+ )
+ # See https://www.sqlite.org/pragma.html#pragma_foreign_key_check
+ for (
+ table_name,
+ rowid,
+ referenced_table_name,
+ foreign_key_index,
+ ) in violations:
+ foreign_key = cursor.execute(
+ "PRAGMA foreign_key_list(%s)" % self.ops.quote_name(table_name)
+ ).fetchall()[foreign_key_index]
+ column_name, referenced_column_name = foreign_key[3:5]
+ primary_key_column_name = self.introspection.get_primary_key_column(
+ cursor, table_name
+ )
+ primary_key_value, bad_value = cursor.execute(
+ "SELECT %s, %s FROM %s WHERE rowid = %%s"
+ % (
+ self.ops.quote_name(primary_key_column_name),
+ self.ops.quote_name(column_name),
+ self.ops.quote_name(table_name),
+ ),
+ (rowid,),
+ ).fetchone()
+ raise IntegrityError(
+ "The row in table '%s' with primary key '%s' has an "
+ "invalid foreign key: %s.%s contains a value '%s' that "
+ "does not have a corresponding value in %s.%s."
+ % (
+ table_name,
+ primary_key_value,
+ table_name,
+ column_name,
+ bad_value,
referenced_table_name,
- ) in relations.items():
- cursor.execute(
- """
- SELECT REFERRING.`%s`, REFERRING.`%s` FROM `%s` as REFERRING
- LEFT JOIN `%s` as REFERRED
- ON (REFERRING.`%s` = REFERRED.`%s`)
- WHERE REFERRING.`%s` IS NOT NULL AND REFERRED.`%s` IS NULL
- """
- % (
- primary_key_column_name,
- column_name,
- table_name,
- referenced_table_name,
- column_name,
- referenced_column_name,
- column_name,
- referenced_column_name,
- )
- )
- for bad_row in cursor.fetchall():
- raise IntegrityError(
- "The row in table '%s' with primary key '%s' has an "
- "invalid foreign key: %s.%s contains a value '%s' that "
- "does not have a corresponding value in %s.%s."
- % (
- table_name,
- bad_row[0],
- table_name,
- column_name,
- bad_row[1],
- referenced_table_name,
- referenced_column_name,
- )
- )
+ referenced_column_name,
+ )
+ )
def is_usable(self):
return True
diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py
index 60b9b7d3a0..7dd1c39702 100644
--- a/django/db/backends/sqlite3/features.py
+++ b/django/db/backends/sqlite3/features.py
@@ -9,7 +9,7 @@ from .base import Database
class DatabaseFeatures(BaseDatabaseFeatures):
- minimum_database_version = (3, 9)
+ minimum_database_version = (3, 21)
test_db_allows_multiple_connections = False
supports_unspecified_pk = True
supports_timezones = False
@@ -31,11 +31,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
# Is "ALTER TABLE ... DROP COLUMN" supported?
can_alter_table_drop_column = Database.sqlite_version_info >= (3, 35, 5)
supports_parentheses_in_compound = False
- # Deferred constraint checks can be emulated on SQLite < 3.20 but not in a
- # reasonably performant way.
- supports_pragma_foreign_key_check = Database.sqlite_version_info >= (3, 20, 0)
- can_defer_constraint_checks = supports_pragma_foreign_key_check
- supports_functions_in_partial_indexes = Database.sqlite_version_info >= (3, 15, 0)
+ can_defer_constraint_checks = True
supports_over_clause = Database.sqlite_version_info >= (3, 25, 0)
supports_frame_range_fixed_distance = Database.sqlite_version_info >= (3, 28, 0)
supports_aggregate_filter_clause = Database.sqlite_version_info >= (3, 30, 1)
diff --git a/docs/ref/contrib/gis/install/index.txt b/docs/ref/contrib/gis/install/index.txt
index 629a070bb8..625b3aabad 100644
--- a/docs/ref/contrib/gis/install/index.txt
+++ b/docs/ref/contrib/gis/install/index.txt
@@ -61,7 +61,7 @@ Database Library Requirements Supported Versions Notes
PostgreSQL GEOS, GDAL, PROJ, PostGIS 12+ Requires PostGIS.
MySQL GEOS, GDAL 8+ :ref:`Limited functionality <mysql-spatial-limitations>`.
Oracle GEOS, GDAL 19+ XE not supported.
-SQLite GEOS, GDAL, PROJ, SpatiaLite 3.9.0+ Requires SpatiaLite 4.3+
+SQLite GEOS, GDAL, PROJ, SpatiaLite 3.21.0+ Requires SpatiaLite 4.3+
================== ============================== ================== =========================================
See also `this comparison matrix`__ on the OSGeo Wiki for
diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt
index 19d1acc776..79b0386e1a 100644
--- a/docs/ref/databases.txt
+++ b/docs/ref/databases.txt
@@ -730,7 +730,7 @@ appropriate typecasting.
SQLite notes
============
-Django supports SQLite 3.9.0 and later.
+Django supports SQLite 3.21.0 and later.
SQLite_ provides an excellent development alternative for applications that
are predominantly read-only or require a smaller installation footprint. As
diff --git a/docs/releases/4.2.txt b/docs/releases/4.2.txt
index 77d4c8594c..d378162537 100644
--- a/docs/releases/4.2.txt
+++ b/docs/releases/4.2.txt
@@ -407,6 +407,8 @@ Miscellaneous
* The ``is_summary`` argument of the undocumented ``Query.add_annotation()``
method is removed.
+* The minimum supported version of SQLite is increased from 3.9.0 to 3.21.0.
+
.. _deprecated-features-4.2:
Features deprecated in 4.2
diff --git a/tests/backends/sqlite/tests.py b/tests/backends/sqlite/tests.py
index 0669d26191..c83cde60dd 100644
--- a/tests/backends/sqlite/tests.py
+++ b/tests/backends/sqlite/tests.py
@@ -106,9 +106,9 @@ class Tests(TestCase):
connections["default"].close()
self.assertTrue(os.path.isfile(os.path.join(tmp, "test.db")))
- @mock.patch.object(connection, "get_database_version", return_value=(3, 8))
+ @mock.patch.object(connection, "get_database_version", return_value=(3, 20))
def test_check_database_version_supported(self, mocked_get_database_version):
- msg = "SQLite 3.9 or later is required (found 3.8)."
+ msg = "SQLite 3.21 or later is required (found 3.20)."
with self.assertRaisesMessage(NotSupportedError, msg):
connection.check_database_version_supported()
self.assertTrue(mocked_get_database_version.called)