From 981c23c0ccbcaa155a80f894fd9e832beaab661d Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Thu, 19 May 2022 09:26:48 +0200 Subject: [PATCH] Fixed #33717 -- Dropped support for PostgreSQL 11. --- django/contrib/postgres/constraints.py | 9 -------- django/contrib/postgres/indexes.py | 7 ------ django/contrib/postgres/operations.py | 6 ------ django/db/backends/postgresql/features.py | 10 +-------- docs/ref/contrib/gis/install/index.txt | 2 +- docs/ref/contrib/postgres/constraints.txt | 4 ++-- docs/ref/contrib/postgres/fields.txt | 4 ++-- docs/ref/contrib/postgres/operations.txt | 4 ---- docs/ref/databases.txt | 2 +- docs/ref/models/indexes.txt | 7 +++--- docs/releases/4.2.txt | 6 ++++++ tests/backends/postgresql/tests.py | 4 ++-- tests/postgres_tests/test_constraints.py | 26 ----------------------- tests/postgres_tests/test_indexes.py | 15 ------------- tests/postgres_tests/test_operations.py | 25 +--------------------- tests/postgres_tests/test_trigram.py | 6 ++---- tests/queries/test_explain.py | 3 +-- 17 files changed, 22 insertions(+), 118 deletions(-) diff --git a/django/contrib/postgres/constraints.py b/django/contrib/postgres/constraints.py index 8b59704063..c19602b26e 100644 --- a/django/contrib/postgres/constraints.py +++ b/django/contrib/postgres/constraints.py @@ -142,15 +142,6 @@ class ExclusionConstraint(BaseConstraint): ) def check_supported(self, schema_editor): - if ( - self.include - and self.index_type.lower() == "gist" - and not schema_editor.connection.features.supports_covering_gist_indexes - ): - raise NotSupportedError( - "Covering exclusion constraints using a GiST index require " - "PostgreSQL 12+." - ) if ( self.include and self.index_type.lower() == "spgist" diff --git a/django/contrib/postgres/indexes.py b/django/contrib/postgres/indexes.py index 97377d8cb1..363a9431ae 100644 --- a/django/contrib/postgres/indexes.py +++ b/django/contrib/postgres/indexes.py @@ -187,13 +187,6 @@ class GistIndex(PostgresIndex): with_params.append("fillfactor = %d" % self.fillfactor) return with_params - def check_supported(self, schema_editor): - if ( - self.include - and not schema_editor.connection.features.supports_covering_gist_indexes - ): - raise NotSupportedError("Covering GiST indexes require PostgreSQL 12+.") - class HashIndex(PostgresIndex): suffix = "hash" diff --git a/django/contrib/postgres/operations.py b/django/contrib/postgres/operations.py index b2d6d9f95a..9dbd491773 100644 --- a/django/contrib/postgres/operations.py +++ b/django/contrib/postgres/operations.py @@ -185,12 +185,6 @@ class CollationOperation(Operation): ) def create_collation(self, schema_editor): - if self.deterministic is False and not ( - schema_editor.connection.features.supports_non_deterministic_collations - ): - raise NotSupportedError( - "Non-deterministic collations require PostgreSQL 12+." - ) args = {"locale": schema_editor.quote_name(self.locale)} if self.provider != "libc": args["provider"] = schema_editor.quote_name(self.provider) diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py index ce01e88603..cbf499d785 100644 --- a/django/db/backends/postgresql/features.py +++ b/django/db/backends/postgresql/features.py @@ -6,7 +6,7 @@ from django.utils.functional import cached_property class DatabaseFeatures(BaseDatabaseFeatures): - minimum_database_version = (11,) + minimum_database_version = (12,) allows_group_by_selected_pks = True can_return_columns_from_insert = True can_return_rows_from_bulk_insert = True @@ -83,10 +83,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): "PositiveSmallIntegerField": "SmallIntegerField", } - @cached_property - def is_postgresql_12(self): - return self.connection.pg_version >= 120000 - @cached_property def is_postgresql_13(self): return self.connection.pg_version >= 130000 @@ -96,8 +92,4 @@ class DatabaseFeatures(BaseDatabaseFeatures): return self.connection.pg_version >= 140000 has_bit_xor = property(operator.attrgetter("is_postgresql_14")) - supports_covering_gist_indexes = property(operator.attrgetter("is_postgresql_12")) supports_covering_spgist_indexes = property(operator.attrgetter("is_postgresql_14")) - supports_non_deterministic_collations = property( - operator.attrgetter("is_postgresql_12") - ) diff --git a/docs/ref/contrib/gis/install/index.txt b/docs/ref/contrib/gis/install/index.txt index 7f4f100bb8..2277b7c5ee 100644 --- a/docs/ref/contrib/gis/install/index.txt +++ b/docs/ref/contrib/gis/install/index.txt @@ -58,7 +58,7 @@ supported versions, and any notes for each of the supported database backends: ================== ============================== ================== ========================================= Database Library Requirements Supported Versions Notes ================== ============================== ================== ========================================= -PostgreSQL GEOS, GDAL, PROJ, PostGIS 11+ Requires PostGIS. +PostgreSQL GEOS, GDAL, PROJ, PostGIS 12+ Requires PostGIS. MySQL GEOS, GDAL 5.7+ :ref:`Limited functionality `. Oracle GEOS, GDAL 19+ XE not supported. SQLite GEOS, GDAL, PROJ, SpatiaLite 3.9.0+ Requires SpatiaLite 4.3+ diff --git a/docs/ref/contrib/postgres/constraints.txt b/docs/ref/contrib/postgres/constraints.txt index 26c6356693..44f1f56db1 100644 --- a/docs/ref/contrib/postgres/constraints.txt +++ b/docs/ref/contrib/postgres/constraints.txt @@ -139,8 +139,8 @@ used for queries that select only included fields (:attr:`~ExclusionConstraint.include`) and filter only by indexed fields (:attr:`~ExclusionConstraint.expressions`). -``include`` is supported for GiST indexes on PostgreSQL 12+ and SP-GiST -indexes on PostgreSQL 14+. +``include`` is supported for GiST indexes. PostgreSQL 14+ also supports +``include`` for SP-GiST indexes. .. versionchanged:: 4.1 diff --git a/docs/ref/contrib/postgres/fields.txt b/docs/ref/contrib/postgres/fields.txt index fe4b3856ab..d460cecc70 100644 --- a/docs/ref/contrib/postgres/fields.txt +++ b/docs/ref/contrib/postgres/fields.txt @@ -287,8 +287,8 @@ transform do not change. For example:: .. admonition:: Case-insensitive collations - On PostgreSQL 12+, it's preferable to use non-deterministic collations - instead of the ``citext`` extension. You can create them using the + It's preferable to use non-deterministic collations instead of the + ``citext`` extension. You can create them using the :class:`~django.contrib.postgres.operations.CreateCollation` migration operation. For more details, see :ref:`manage-postgresql-collations` and the PostgreSQL documentation about `non-deterministic collations`_. diff --git a/docs/ref/contrib/postgres/operations.txt b/docs/ref/contrib/postgres/operations.txt index 46ba3300ae..d5b8a9e0d6 100644 --- a/docs/ref/contrib/postgres/operations.txt +++ b/docs/ref/contrib/postgres/operations.txt @@ -151,10 +151,6 @@ For example, to create a collation for German phone book ordering:: ``provider``, and ``deterministic`` arguments. Therefore, ``locale`` is required to make this operation reversible. -.. admonition:: Restrictions - - Non-deterministic collations are supported only on PostgreSQL 12+. - Concurrent index operations =========================== diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index a80c10e8f6..9c6c241184 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -114,7 +114,7 @@ below for information on how to set up your database correctly. PostgreSQL notes ================ -Django supports PostgreSQL 11 and higher. `psycopg2`_ 2.8.4 or higher is +Django supports PostgreSQL 12 and higher. `psycopg2`_ 2.8.4 or higher is required, though the latest release is recommended. .. _psycopg2: https://www.psycopg.org/ diff --git a/docs/ref/models/indexes.txt b/docs/ref/models/indexes.txt index 6b3c7422b7..c6633d6998 100644 --- a/docs/ref/models/indexes.txt +++ b/docs/ref/models/indexes.txt @@ -214,10 +214,9 @@ See the PostgreSQL documentation for more details about `covering indexes`_. .. admonition:: Restrictions on PostgreSQL - PostgreSQL < 12 only supports covering B-Tree indexes, PostgreSQL 12+ also - supports covering :class:`GiST indexes - `, and PostgreSQL 14+ also - supports covering :class:`SP-GiST indexes + PostgreSQL supports covering B-Tree and :class:`GiST indexes + `. PostgreSQL 14+ also supports + covering :class:`SP-GiST indexes `. .. versionchanged:: 4.1 diff --git a/docs/releases/4.2.txt b/docs/releases/4.2.txt index 9c16ae870e..0d95f68654 100644 --- a/docs/releases/4.2.txt +++ b/docs/releases/4.2.txt @@ -229,6 +229,12 @@ Dropped support for MariaDB 10.3 Upstream support for MariaDB 10.3 ends in May 2023. Django 4.2 supports MariaDB 10.4 and higher. +Dropped support for PostgreSQL 11 +--------------------------------- + +Upstream support for PostgreSQL 11 ends in November 2023. Django 4.2 supports +PostgreSQL 12 and higher. + Miscellaneous ------------- diff --git a/tests/backends/postgresql/tests.py b/tests/backends/postgresql/tests.py index 4e15e6843a..63d7064aac 100644 --- a/tests/backends/postgresql/tests.py +++ b/tests/backends/postgresql/tests.py @@ -315,9 +315,9 @@ class Tests(TestCase): new_connection.pg_version = 110009 self.assertEqual(new_connection.get_database_version(), (11, 9)) - @mock.patch.object(connection, "get_database_version", return_value=(10,)) + @mock.patch.object(connection, "get_database_version", return_value=(11,)) def test_check_database_version_supported(self, mocked_get_database_version): - msg = "PostgreSQL 11 or later is required (found 10)." + msg = "PostgreSQL 12 or later is required (found 11)." with self.assertRaisesMessage(NotSupportedError, msg): connection.check_database_version_supported() self.assertTrue(mocked_get_database_version.called) diff --git a/tests/postgres_tests/test_constraints.py b/tests/postgres_tests/test_constraints.py index b8e53eb4a0..d36c6fd9ed 100644 --- a/tests/postgres_tests/test_constraints.py +++ b/tests/postgres_tests/test_constraints.py @@ -845,7 +845,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase): RangesModel.objects.create(ints=(10, 19)) RangesModel.objects.create(ints=(51, 60)) - @skipUnlessDBFeature("supports_covering_gist_indexes") def test_range_adjacent_gist_include(self): constraint_name = "ints_adjacent_gist_include" self.assertNotIn( @@ -887,7 +886,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase): RangesModel.objects.create(ints=(10, 19)) RangesModel.objects.create(ints=(51, 60)) - @skipUnlessDBFeature("supports_covering_gist_indexes") def test_range_adjacent_gist_include_condition(self): constraint_name = "ints_adjacent_gist_include_condition" self.assertNotIn( @@ -921,7 +919,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase): editor.add_constraint(RangesModel, constraint) self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table)) - @skipUnlessDBFeature("supports_covering_gist_indexes") def test_range_adjacent_gist_include_deferrable(self): constraint_name = "ints_adjacent_gist_include_deferrable" self.assertNotIn( @@ -955,27 +952,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase): editor.add_constraint(RangesModel, constraint) self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table)) - def test_gist_include_not_supported(self): - constraint_name = "ints_adjacent_gist_include_not_supported" - constraint = ExclusionConstraint( - name=constraint_name, - expressions=[("ints", RangeOperators.ADJACENT_TO)], - index_type="gist", - include=["id"], - ) - msg = ( - "Covering exclusion constraints using a GiST index require " - "PostgreSQL 12+." - ) - with connection.schema_editor() as editor: - with mock.patch( - "django.db.backends.postgresql.features.DatabaseFeatures." - "supports_covering_gist_indexes", - False, - ): - with self.assertRaisesMessage(NotSupportedError, msg): - editor.add_constraint(RangesModel, constraint) - def test_spgist_include_not_supported(self): constraint_name = "ints_adjacent_spgist_include_not_supported" constraint = ExclusionConstraint( @@ -1062,7 +1038,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase): editor.add_constraint(RangesModel, constraint) self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table)) - @skipUnlessDBFeature("supports_covering_gist_indexes") def test_range_adjacent_gist_opclass_include(self): constraint_name = "ints_adjacent_gist_opclass_include" self.assertNotIn( @@ -1210,7 +1185,6 @@ class ExclusionConstraintOpclassesDepracationTests(PostgreSQLTestCase): self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table)) @ignore_warnings(category=RemovedInDjango50Warning) - @skipUnlessDBFeature("supports_covering_gist_indexes") def test_range_adjacent_gist_opclasses_include(self): constraint_name = "ints_adjacent_gist_opclasses_include" self.assertNotIn( diff --git a/tests/postgres_tests/test_indexes.py b/tests/postgres_tests/test_indexes.py index 9da2f33ae8..dc1064b35d 100644 --- a/tests/postgres_tests/test_indexes.py +++ b/tests/postgres_tests/test_indexes.py @@ -505,7 +505,6 @@ class SchemaTests(PostgreSQLTestCase): index_name, self.get_constraints(CharFieldModel._meta.db_table) ) - @skipUnlessDBFeature("supports_covering_gist_indexes") def test_gist_include(self): index_name = "scene_gist_include_setting" index = GistIndex(name=index_name, fields=["scene"], include=["setting"]) @@ -519,20 +518,6 @@ class SchemaTests(PostgreSQLTestCase): editor.remove_index(Scene, index) self.assertNotIn(index_name, self.get_constraints(Scene._meta.db_table)) - def test_gist_include_not_supported(self): - index_name = "gist_include_exception" - index = GistIndex(fields=["scene"], name=index_name, include=["setting"]) - msg = "Covering GiST indexes require PostgreSQL 12+." - with self.assertRaisesMessage(NotSupportedError, msg): - with mock.patch( - "django.db.backends.postgresql.features.DatabaseFeatures." - "supports_covering_gist_indexes", - False, - ): - with connection.schema_editor() as editor: - editor.add_index(Scene, index) - self.assertNotIn(index_name, self.get_constraints(Scene._meta.db_table)) - def test_tsvector_op_class_gist_index(self): index_name = "tsvector_op_class_gist" index = GistIndex( diff --git a/tests/postgres_tests/test_operations.py b/tests/postgres_tests/test_operations.py index a54f8811ba..f395198533 100644 --- a/tests/postgres_tests/test_operations.py +++ b/tests/postgres_tests/test_operations.py @@ -1,5 +1,4 @@ import unittest -from unittest import mock from migrations.test_base import OperationTestBase @@ -7,7 +6,7 @@ from django.db import IntegrityError, NotSupportedError, connection, transaction from django.db.migrations.state import ProjectState from django.db.models import CheckConstraint, Index, Q, UniqueConstraint from django.db.utils import ProgrammingError -from django.test import modify_settings, override_settings, skipUnlessDBFeature +from django.test import modify_settings, override_settings from django.test.utils import CaptureQueriesContext from . import PostgreSQLTestCase @@ -318,7 +317,6 @@ class CreateCollationTests(PostgreSQLTestCase): self.assertEqual(args, []) self.assertEqual(kwargs, {"name": "C_test", "locale": "C"}) - @skipUnlessDBFeature("supports_non_deterministic_collations") def test_create_non_deterministic_collation(self): operation = CreateCollation( "case_insensitive_test", @@ -383,27 +381,6 @@ class CreateCollationTests(PostgreSQLTestCase): self.assertEqual(len(captured_queries), 1) self.assertIn("DROP COLLATION", captured_queries[0]["sql"]) - def test_nondeterministic_collation_not_supported(self): - operation = CreateCollation( - "case_insensitive_test", - provider="icu", - locale="und-u-ks-level2", - deterministic=False, - ) - project_state = ProjectState() - new_state = project_state.clone() - msg = "Non-deterministic collations require PostgreSQL 12+." - with connection.schema_editor(atomic=False) as editor: - with mock.patch( - "django.db.backends.postgresql.features.DatabaseFeatures." - "supports_non_deterministic_collations", - False, - ): - with self.assertRaisesMessage(NotSupportedError, msg): - operation.database_forwards( - self.app_label, editor, project_state, new_state - ) - @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific tests.") class RemoveCollationTests(PostgreSQLTestCase): diff --git a/tests/postgres_tests/test_trigram.py b/tests/postgres_tests/test_trigram.py index a0502e0b9b..6870e80737 100644 --- a/tests/postgres_tests/test_trigram.py +++ b/tests/postgres_tests/test_trigram.py @@ -46,8 +46,7 @@ class TrigramTest(PostgreSQLTestCase): def test_trigram_similarity(self): search = "Bat sat on cat." - # Round result of similarity because PostgreSQL 12+ uses greater - # precision. + # Round result of similarity because PostgreSQL uses greater precision. self.assertQuerysetEqual( self.Model.objects.filter( field__trigram_similar=search, @@ -77,8 +76,7 @@ class TrigramTest(PostgreSQLTestCase): ) def test_trigram_similarity_alternate(self): - # Round result of distance because PostgreSQL 12+ uses greater - # precision. + # Round result of distance because PostgreSQL uses greater precision. self.assertQuerysetEqual( self.Model.objects.annotate( distance=TrigramDistance("field", "Bat sat on cat."), diff --git a/tests/queries/test_explain.py b/tests/queries/test_explain.py index 7932eb14f2..11684356b9 100644 --- a/tests/queries/test_explain.py +++ b/tests/queries/test_explain.py @@ -82,9 +82,8 @@ class ExplainTests(TestCase): {"verbose": True, "timing": True, "analyze": True}, {"verbose": False, "timing": False, "analyze": True}, {"summary": True}, + {"settings": True}, ] - if connection.features.is_postgresql_12: - test_options.append({"settings": True}) if connection.features.is_postgresql_13: test_options.append({"analyze": True, "wal": True}) for options in test_options: