mirror of
https://github.com/django/django.git
synced 2024-12-22 09:05:43 +00:00
Fixed #35479 -- Dropped support for PostgreSQL 13 and PostGIS 3.0.
This commit is contained in:
parent
bcbc4b9b8a
commit
b049bec7cf
4
.github/workflows/schedule_tests.yml
vendored
4
.github/workflows/schedule_tests.yml
vendored
@ -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
|
||||
|
2
.github/workflows/selenium.yml
vendored
2
.github/workflows/selenium.yml
vendored
@ -43,7 +43,7 @@ jobs:
|
||||
name: PostgreSQL
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:13-alpine
|
||||
image: postgres:14-alpine
|
||||
env:
|
||||
POSTGRES_DB: django
|
||||
POSTGRES_USER: user
|
||||
|
@ -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"]
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
@ -152,10 +152,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
"PositiveSmallIntegerField": "SmallIntegerField",
|
||||
}
|
||||
|
||||
@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")
|
||||
|
@ -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
|
||||
|
@ -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/
|
||||
|
||||
|
@ -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
|
||||
|
@ -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::
|
||||
|
@ -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
|
||||
-------------
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user