summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2024-05-24 21:23:50 +0200
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2024-05-27 09:49:25 +0200
commitb049bec7cfe9b5854584d240addb44fa1e9375a5 (patch)
tree802915666be8c9429530add0f621a560b5cca6be
parentbcbc4b9b8a4a47c8e045b060a9860a5c038192de (diff)
Fixed #35479 -- Dropped support for PostgreSQL 13 and PostGIS 3.0.
-rw-r--r--.github/workflows/schedule_tests.yml4
-rw-r--r--.github/workflows/selenium.yml2
-rw-r--r--django/contrib/gis/db/backends/postgis/operations.py2
-rw-r--r--django/contrib/postgres/constraints.py14
-rw-r--r--django/contrib/postgres/indexes.py8
-rw-r--r--django/db/backends/postgresql/features.py8
-rw-r--r--docs/ref/contrib/gis/install/geolibs.txt3
-rw-r--r--docs/ref/contrib/gis/install/index.txt8
-rw-r--r--docs/ref/contrib/postgres/functions.txt6
-rw-r--r--docs/ref/databases.txt2
-rw-r--r--docs/releases/5.2.txt11
-rw-r--r--tests/backends/postgresql/tests.py8
-rw-r--r--tests/postgres_tests/test_aggregates.py12
-rw-r--r--tests/postgres_tests/test_constraints.py27
-rw-r--r--tests/postgres_tests/test_indexes.py20
15 files changed, 32 insertions, 103 deletions
diff --git a/.github/workflows/schedule_tests.yml b/.github/workflows/schedule_tests.yml
index c4523af4a0..8b1f01ad86 100644
--- a/.github/workflows/schedule_tests.yml
+++ b/.github/workflows/schedule_tests.yml
@@ -90,7 +90,7 @@ jobs:
continue-on-error: true
services:
postgres:
- image: postgres:13-alpine
+ image: postgres:14-alpine
env:
POSTGRES_DB: django
POSTGRES_USER: user
@@ -163,7 +163,7 @@ jobs:
name: Selenium tests, PostgreSQL
services:
postgres:
- image: postgres:13-alpine
+ image: postgres:14-alpine
env:
POSTGRES_DB: django
POSTGRES_USER: user
diff --git a/.github/workflows/selenium.yml b/.github/workflows/selenium.yml
index fa916a0ded..7e46e0cfb1 100644
--- a/.github/workflows/selenium.yml
+++ b/.github/workflows/selenium.yml
@@ -43,7 +43,7 @@ jobs:
name: PostgreSQL
services:
postgres:
- image: postgres:13-alpine
+ image: postgres:14-alpine
env:
POSTGRES_DB: django
POSTGRES_USER: user
diff --git a/django/contrib/gis/db/backends/postgis/operations.py b/django/contrib/gis/db/backends/postgis/operations.py
index 17d7b3213d..7a347c5287 100644
--- a/django/contrib/gis/db/backends/postgis/operations.py
+++ b/django/contrib/gis/db/backends/postgis/operations.py
@@ -203,7 +203,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations):
raise ImproperlyConfigured(
'Cannot determine PostGIS version for database "%s" '
'using command "SELECT postgis_lib_version()". '
- "GeoDjango requires at least PostGIS version 3.0. "
+ "GeoDjango requires at least PostGIS version 3.1. "
"Was the database created from a spatial database "
"template?" % self.connection.settings_dict["NAME"]
)
diff --git a/django/contrib/postgres/constraints.py b/django/contrib/postgres/constraints.py
index a31f657183..a6351dc008 100644
--- a/django/contrib/postgres/constraints.py
+++ b/django/contrib/postgres/constraints.py
@@ -1,7 +1,7 @@
from types import NoneType
from django.core.exceptions import ValidationError
-from django.db import DEFAULT_DB_ALIAS, NotSupportedError
+from django.db import DEFAULT_DB_ALIAS
from django.db.backends.ddl_references import Expressions, Statement, Table
from django.db.models import BaseConstraint, Deferrable, F, Q
from django.db.models.expressions import Exists, ExpressionList
@@ -114,7 +114,6 @@ class ExclusionConstraint(BaseConstraint):
)
def create_sql(self, model, schema_editor):
- self.check_supported(schema_editor)
return Statement(
"ALTER TABLE %(table)s ADD %(constraint)s",
table=Table(model._meta.db_table, schema_editor.quote_name),
@@ -128,17 +127,6 @@ class ExclusionConstraint(BaseConstraint):
schema_editor.quote_name(self.name),
)
- def check_supported(self, schema_editor):
- if (
- self.include
- and self.index_type.lower() == "spgist"
- and not schema_editor.connection.features.supports_covering_spgist_indexes
- ):
- raise NotSupportedError(
- "Covering exclusion constraints using an SP-GiST index "
- "require PostgreSQL 14+."
- )
-
def deconstruct(self):
path, args, kwargs = super().deconstruct()
kwargs["expressions"] = self.expressions
diff --git a/django/contrib/postgres/indexes.py b/django/contrib/postgres/indexes.py
index 05fdbeed5e..ce9e2cee14 100644
--- a/django/contrib/postgres/indexes.py
+++ b/django/contrib/postgres/indexes.py
@@ -1,4 +1,3 @@
-from django.db import NotSupportedError
from django.db.models import Func, Index
from django.utils.functional import cached_property
@@ -234,13 +233,6 @@ class SpGistIndex(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_spgist_indexes
- ):
- raise NotSupportedError("Covering SP-GiST indexes require PostgreSQL 14+.")
-
class OpClass(Func):
template = "%(expressions)s %(name)s"
diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py
index ef697e85b0..6170b5501a 100644
--- a/django/db/backends/postgresql/features.py
+++ b/django/db/backends/postgresql/features.py
@@ -7,7 +7,7 @@ from django.utils.functional import cached_property
class DatabaseFeatures(BaseDatabaseFeatures):
- minimum_database_version = (13,)
+ minimum_database_version = (14,)
allows_group_by_selected_pks = True
can_return_columns_from_insert = True
can_return_rows_from_bulk_insert = True
@@ -153,10 +153,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
}
@cached_property
- def is_postgresql_14(self):
- return self.connection.pg_version >= 140000
-
- @cached_property
def is_postgresql_15(self):
return self.connection.pg_version >= 150000
@@ -164,8 +160,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
def is_postgresql_16(self):
return self.connection.pg_version >= 160000
- has_bit_xor = property(operator.attrgetter("is_postgresql_14"))
- supports_covering_spgist_indexes = property(operator.attrgetter("is_postgresql_14"))
supports_unlimited_charfield = True
supports_nulls_distinct_unique_constraints = property(
operator.attrgetter("is_postgresql_15")
diff --git a/docs/ref/contrib/gis/install/geolibs.txt b/docs/ref/contrib/gis/install/geolibs.txt
index 041409d53c..a0a66c0dc6 100644
--- a/docs/ref/contrib/gis/install/geolibs.txt
+++ b/docs/ref/contrib/gis/install/geolibs.txt
@@ -12,7 +12,7 @@ Program Description Required
`PROJ`_ Cartographic Projections library Yes (PostgreSQL and SQLite only) 9.x, 8.x, 7.x, 6.x
:doc:`GDAL <../gdal>` Geospatial Data Abstraction Library Yes 3.8, 3.7, 3.6, 3.5, 3.4, 3.3, 3.2, 3.1, 3.0
:doc:`GeoIP <../geoip2>` IP-based geolocation library No 2
-`PostGIS`__ Spatial extensions for PostgreSQL Yes (PostgreSQL only) 3.4, 3.3, 3.2, 3.1, 3.0
+`PostGIS`__ Spatial extensions for PostgreSQL Yes (PostgreSQL only) 3.4, 3.3, 3.2, 3.1
`SpatiaLite`__ Spatial extensions for SQLite Yes (SQLite only) 5.1, 5.0, 4.3
======================== ==================================== ================================ ===========================================
@@ -35,7 +35,6 @@ totally fine with GeoDjango. Your mileage may vary.
GDAL 3.6.0 2022-11-03
GDAL 3.7.0 2023-05-10
GDAL 3.8.0 2023-11-13
- PostGIS 3.0.0 2019-10-20
PostGIS 3.1.0 2020-12-18
PostGIS 3.2.0 2021-12-18
PostGIS 3.3.0 2022-08-27
diff --git a/docs/ref/contrib/gis/install/index.txt b/docs/ref/contrib/gis/install/index.txt
index 7706790b2a..e7bc885d4b 100644
--- a/docs/ref/contrib/gis/install/index.txt
+++ b/docs/ref/contrib/gis/install/index.txt
@@ -56,7 +56,7 @@ supported versions, and any notes for each of the supported database backends:
================== ============================== ================== =========================================
Database Library Requirements Supported Versions Notes
================== ============================== ================== =========================================
-PostgreSQL GEOS, GDAL, PROJ, PostGIS 13+ Requires PostGIS.
+PostgreSQL GEOS, GDAL, PROJ, PostGIS 14+ Requires PostGIS.
MySQL GEOS, GDAL 8.0.11+ :ref:`Limited functionality <mysql-spatial-limitations>`.
Oracle GEOS, GDAL 19+ XE not supported.
SQLite GEOS, GDAL, PROJ, SpatiaLite 3.31.0+ Requires SpatiaLite 4.3+
@@ -300,7 +300,7 @@ Summary:
.. code-block:: shell
- $ sudo port install postgresql13-server
+ $ sudo port install postgresql14-server
$ sudo port install geos
$ sudo port install proj6
$ sudo port install postgis3
@@ -314,14 +314,14 @@ Summary:
.. code-block:: shell
- export PATH=/opt/local/bin:/opt/local/lib/postgresql13/bin
+ export PATH=/opt/local/bin:/opt/local/lib/postgresql14/bin
In addition, add the ``DYLD_FALLBACK_LIBRARY_PATH`` setting so that
the libraries can be found by Python:
.. code-block:: shell
- export DYLD_FALLBACK_LIBRARY_PATH=/opt/local/lib:/opt/local/lib/postgresql13
+ export DYLD_FALLBACK_LIBRARY_PATH=/opt/local/lib:/opt/local/lib/postgresql14
__ https://www.macports.org/
diff --git a/docs/ref/contrib/postgres/functions.txt b/docs/ref/contrib/postgres/functions.txt
index f5d9cdd873..4602f7fd9d 100644
--- a/docs/ref/contrib/postgres/functions.txt
+++ b/docs/ref/contrib/postgres/functions.txt
@@ -14,12 +14,6 @@ All of these functions are available from the
Returns a version 4 UUID.
-On PostgreSQL < 13, the `pgcrypto extension`_ must be installed. You can use
-the :class:`~django.contrib.postgres.operations.CryptoExtension` migration
-operation to install it.
-
-.. _pgcrypto extension: https://www.postgresql.org/docs/current/pgcrypto.html
-
Usage example:
.. code-block:: pycon
diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt
index c8e9f2ebff..3e50d2e46a 100644
--- a/docs/ref/databases.txt
+++ b/docs/ref/databases.txt
@@ -115,7 +115,7 @@ below for information on how to set up your database correctly.
PostgreSQL notes
================
-Django supports PostgreSQL 13 and higher. `psycopg`_ 3.1.8+ or `psycopg2`_
+Django supports PostgreSQL 14 and higher. `psycopg`_ 3.1.8+ or `psycopg2`_
2.8.4+ is required, though the latest `psycopg`_ 3.1.8+ is recommended.
.. note::
diff --git a/docs/releases/5.2.txt b/docs/releases/5.2.txt
index 9d28415df1..fb2a1d0177 100644
--- a/docs/releases/5.2.txt
+++ b/docs/releases/5.2.txt
@@ -238,6 +238,17 @@ backends.
* ...
+:mod:`django.contrib.gis`
+-------------------------
+
+* Support for PostGIS 3.0 is removed.
+
+Dropped support for PostgreSQL 13
+---------------------------------
+
+Upstream support for PostgreSQL 13 ends in November 2025. Django 5.2 supports
+PostgreSQL 14 and higher.
+
Miscellaneous
-------------
diff --git a/tests/backends/postgresql/tests.py b/tests/backends/postgresql/tests.py
index 47f8d94004..0b4f580612 100644
--- a/tests/backends/postgresql/tests.py
+++ b/tests/backends/postgresql/tests.py
@@ -548,12 +548,12 @@ class Tests(TestCase):
def test_get_database_version(self):
new_connection = no_pool_connection()
- new_connection.pg_version = 130009
- self.assertEqual(new_connection.get_database_version(), (13, 9))
+ new_connection.pg_version = 140009
+ self.assertEqual(new_connection.get_database_version(), (14, 9))
- @mock.patch.object(connection, "get_database_version", return_value=(12,))
+ @mock.patch.object(connection, "get_database_version", return_value=(13,))
def test_check_database_version_supported(self, mocked_get_database_version):
- msg = "PostgreSQL 13 or later is required (found 12)."
+ msg = "PostgreSQL 14 or later is required (found 13)."
with self.assertRaisesMessage(NotSupportedError, msg):
connection.check_database_version_supported()
self.assertTrue(mocked_get_database_version.called)
diff --git a/tests/postgres_tests/test_aggregates.py b/tests/postgres_tests/test_aggregates.py
index 7e1e16d0c0..b72310bdf1 100644
--- a/tests/postgres_tests/test_aggregates.py
+++ b/tests/postgres_tests/test_aggregates.py
@@ -1,4 +1,4 @@
-from django.db import connection, transaction
+from django.db import transaction
from django.db.models import (
CharField,
F,
@@ -13,7 +13,6 @@ from django.db.models import (
)
from django.db.models.fields.json import KeyTextTransform, KeyTransform
from django.db.models.functions import Cast, Concat, LPad, Substr
-from django.test import skipUnlessDBFeature
from django.test.utils import Approximate
from django.utils import timezone
@@ -95,9 +94,8 @@ class TestGeneralAggregate(PostgreSQLTestCase):
BoolOr("boolean_field"),
JSONBAgg("integer_field"),
StringAgg("char_field", delimiter=";"),
+ BitXor("integer_field"),
]
- if connection.features.has_bit_xor:
- tests.append(BitXor("integer_field"))
for aggregation in tests:
with self.subTest(aggregation=aggregation):
# Empty result with non-execution optimization.
@@ -133,9 +131,8 @@ class TestGeneralAggregate(PostgreSQLTestCase):
StringAgg("char_field", delimiter=";", default=Value("<empty>")),
"<empty>",
),
+ (BitXor("integer_field", default=0), 0),
]
- if connection.features.has_bit_xor:
- tests.append((BitXor("integer_field", default=0), 0))
for aggregation, expected_result in tests:
with self.subTest(aggregation=aggregation):
# Empty result with non-execution optimization.
@@ -348,7 +345,6 @@ class TestGeneralAggregate(PostgreSQLTestCase):
)
self.assertEqual(values, {"bitor": 0})
- @skipUnlessDBFeature("has_bit_xor")
def test_bit_xor_general(self):
AggregateTestModel.objects.create(integer_field=3)
values = AggregateTestModel.objects.filter(
@@ -356,14 +352,12 @@ class TestGeneralAggregate(PostgreSQLTestCase):
).aggregate(bitxor=BitXor("integer_field"))
self.assertEqual(values, {"bitxor": 2})
- @skipUnlessDBFeature("has_bit_xor")
def test_bit_xor_on_only_true_values(self):
values = AggregateTestModel.objects.filter(
integer_field=1,
).aggregate(bitxor=BitXor("integer_field"))
self.assertEqual(values, {"bitxor": 1})
- @skipUnlessDBFeature("has_bit_xor")
def test_bit_xor_on_only_false_values(self):
values = AggregateTestModel.objects.filter(
integer_field=0,
diff --git a/tests/postgres_tests/test_constraints.py b/tests/postgres_tests/test_constraints.py
index 3cc76cdcfe..770d4b1702 100644
--- a/tests/postgres_tests/test_constraints.py
+++ b/tests/postgres_tests/test_constraints.py
@@ -4,7 +4,7 @@ from unittest import mock
from django.contrib.postgres.indexes import OpClass
from django.core.checks import Error
from django.core.exceptions import ValidationError
-from django.db import IntegrityError, NotSupportedError, connection, transaction
+from django.db import IntegrityError, connection, transaction
from django.db.models import (
CASCADE,
CharField,
@@ -997,7 +997,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
RangesModel.objects.create(ints=(10, 19))
RangesModel.objects.create(ints=(51, 60))
- @skipUnlessDBFeature("supports_covering_spgist_indexes")
def test_range_adjacent_spgist_include(self):
constraint_name = "ints_adjacent_spgist_include"
self.assertNotIn(
@@ -1034,7 +1033,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
editor.add_constraint(RangesModel, constraint)
self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table))
- @skipUnlessDBFeature("supports_covering_spgist_indexes")
def test_range_adjacent_spgist_include_condition(self):
constraint_name = "ints_adjacent_spgist_include_condition"
self.assertNotIn(
@@ -1067,7 +1065,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
editor.add_constraint(RangesModel, constraint)
self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table))
- @skipUnlessDBFeature("supports_covering_spgist_indexes")
def test_range_adjacent_spgist_include_deferrable(self):
constraint_name = "ints_adjacent_spgist_include_deferrable"
self.assertNotIn(
@@ -1084,27 +1081,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
editor.add_constraint(RangesModel, constraint)
self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table))
- def test_spgist_include_not_supported(self):
- constraint_name = "ints_adjacent_spgist_include_not_supported"
- constraint = ExclusionConstraint(
- name=constraint_name,
- expressions=[("ints", RangeOperators.ADJACENT_TO)],
- index_type="spgist",
- include=["id"],
- )
- msg = (
- "Covering exclusion constraints using an SP-GiST index require "
- "PostgreSQL 14+."
- )
- with connection.schema_editor() as editor:
- with mock.patch(
- "django.db.backends.postgresql.features.DatabaseFeatures."
- "supports_covering_spgist_indexes",
- False,
- ):
- with self.assertRaisesMessage(NotSupportedError, msg):
- editor.add_constraint(RangesModel, constraint)
-
def test_range_adjacent_opclass(self):
constraint_name = "ints_adjacent_opclass"
self.assertNotIn(
@@ -1187,7 +1163,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
editor.add_constraint(RangesModel, constraint)
self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table))
- @skipUnlessDBFeature("supports_covering_spgist_indexes")
def test_range_adjacent_spgist_opclass_include(self):
constraint_name = "ints_adjacent_spgist_opclass_include"
self.assertNotIn(
diff --git a/tests/postgres_tests/test_indexes.py b/tests/postgres_tests/test_indexes.py
index 8a7ee39a76..f98d03c6c1 100644
--- a/tests/postgres_tests/test_indexes.py
+++ b/tests/postgres_tests/test_indexes.py
@@ -1,5 +1,3 @@
-from unittest import mock
-
from django.contrib.postgres.indexes import (
BloomIndex,
BrinIndex,
@@ -11,10 +9,9 @@ from django.contrib.postgres.indexes import (
PostgresIndex,
SpGistIndex,
)
-from django.db import NotSupportedError, connection
+from django.db import connection
from django.db.models import CharField, F, Index, Q
from django.db.models.functions import Cast, Collate, Length, Lower
-from django.test import skipUnlessDBFeature
from django.test.utils import register_lookup
from . import PostgreSQLSimpleTestCase, PostgreSQLTestCase
@@ -640,7 +637,6 @@ class SchemaTests(PostgreSQLTestCase):
index_name, self.get_constraints(TextFieldModel._meta.db_table)
)
- @skipUnlessDBFeature("supports_covering_spgist_indexes")
def test_spgist_include(self):
index_name = "scene_spgist_include_setting"
index = SpGistIndex(name=index_name, fields=["scene"], include=["setting"])
@@ -654,20 +650,6 @@ class SchemaTests(PostgreSQLTestCase):
editor.remove_index(Scene, index)
self.assertNotIn(index_name, self.get_constraints(Scene._meta.db_table))
- def test_spgist_include_not_supported(self):
- index_name = "spgist_include_exception"
- index = SpGistIndex(fields=["scene"], name=index_name, include=["setting"])
- msg = "Covering SP-GiST indexes require PostgreSQL 14+."
- with self.assertRaisesMessage(NotSupportedError, msg):
- with mock.patch(
- "django.db.backends.postgresql.features.DatabaseFeatures."
- "supports_covering_spgist_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_custom_suffix(self):
class CustomSuffixIndex(PostgresIndex):
suffix = "sfx"