From 5371342ed66f5ff9feae9dc4586ec43830bbeca6 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Tue, 19 Jan 2021 12:25:20 +0100 Subject: [PATCH] Fixed #32357 -- Dropped support for PostgreSQL 9.6 and PostGIS 2.3. --- .../gis/db/backends/postgis/operations.py | 2 +- django/contrib/postgres/indexes.py | 4 ---- django/contrib/postgres/operations.py | 5 ----- django/db/backends/postgresql/features.py | 21 ++++--------------- .../db/backends/postgresql/introspection.py | 4 ++-- docs/ref/contrib/gis/install/geolibs.txt | 3 +-- docs/ref/contrib/gis/install/index.txt | 14 ++++++------- docs/ref/contrib/gis/install/postgis.txt | 8 +++---- docs/ref/contrib/postgres/operations.txt | 2 -- docs/ref/databases.txt | 2 +- docs/ref/unicode.txt | 6 +++--- docs/releases/4.0.txt | 11 ++++++++++ tests/inspectdb/tests.py | 1 - tests/postgres_tests/test_indexes.py | 10 --------- tests/postgres_tests/test_operations.py | 19 ----------------- tests/queries/test_explain.py | 3 +-- 16 files changed, 35 insertions(+), 80 deletions(-) diff --git a/django/contrib/gis/db/backends/postgis/operations.py b/django/contrib/gis/db/backends/postgis/operations.py index f068f28f48d62..84110fce0fdcd 100644 --- a/django/contrib/gis/db/backends/postgis/operations.py +++ b/django/contrib/gis/db/backends/postgis/operations.py @@ -179,7 +179,7 @@ def spatial_version(self): raise ImproperlyConfigured( 'Cannot determine PostGIS version for database "%s" ' 'using command "SELECT postgis_lib_version()". ' - 'GeoDjango requires at least PostGIS version 2.3. ' + 'GeoDjango requires at least PostGIS version 2.4. ' 'Was the database created from a spatial database ' 'template?' % self.connection.settings_dict['NAME'] ) diff --git a/django/contrib/postgres/indexes.py b/django/contrib/postgres/indexes.py index af9de758f7f4d..2642164aa13c6 100644 --- a/django/contrib/postgres/indexes.py +++ b/django/contrib/postgres/indexes.py @@ -98,10 +98,6 @@ def deconstruct(self): kwargs['pages_per_range'] = self.pages_per_range return path, args, kwargs - def check_supported(self, schema_editor): - if self.autosummarize and not schema_editor.connection.features.has_brin_autosummarize: - raise NotSupportedError('BRIN option autosummarize requires PostgreSQL 10+.') - def get_with_params(self): with_params = [] if self.autosummarize is not None: diff --git a/django/contrib/postgres/operations.py b/django/contrib/postgres/operations.py index a8092ec362e19..e5f2b9e92fd3b 100644 --- a/django/contrib/postgres/operations.py +++ b/django/contrib/postgres/operations.py @@ -196,11 +196,6 @@ def create_collation(self, schema_editor): raise NotSupportedError( 'Non-deterministic collations require PostgreSQL 12+.' ) - if ( - self.provider != 'libc' and - not schema_editor.connection.features.supports_alternate_collation_providers - ): - raise NotSupportedError('Non-libc providers require PostgreSQL 10+.') 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 84259c0c19f22..722bfe0475362 100644 --- a/django/db/backends/postgresql/features.py +++ b/django/db/backends/postgresql/features.py @@ -58,6 +58,10 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_deferrable_unique_constraints = True has_json_operators = True json_key_contains_list_matching_requires_list = True + test_collations = { + 'non_default': 'sv-x-icu', + 'swedish_ci': 'sv-x-icu', + } django_test_skips = { 'opclasses are PostgreSQL only.': { @@ -65,16 +69,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): }, } - @cached_property - def test_collations(self): - # PostgreSQL < 10 doesn't support ICU collations. - if self.is_postgresql_10: - return { - 'non_default': 'sv-x-icu', - 'swedish_ci': 'sv-x-icu', - } - return {} - @cached_property def introspected_field_types(self): return { @@ -84,10 +78,6 @@ def introspected_field_types(self): 'PositiveSmallIntegerField': 'SmallIntegerField', } - @cached_property - def is_postgresql_10(self): - return self.connection.pg_version >= 100000 - @cached_property def is_postgresql_11(self): return self.connection.pg_version >= 110000 @@ -100,10 +90,7 @@ def is_postgresql_12(self): def is_postgresql_13(self): return self.connection.pg_version >= 130000 - has_brin_autosummarize = property(operator.attrgetter('is_postgresql_10')) has_websearch_to_tsquery = property(operator.attrgetter('is_postgresql_11')) - supports_table_partitions = property(operator.attrgetter('is_postgresql_10')) supports_covering_indexes = property(operator.attrgetter('is_postgresql_11')) supports_covering_gist_indexes = property(operator.attrgetter('is_postgresql_12')) supports_non_deterministic_collations = property(operator.attrgetter('is_postgresql_12')) - supports_alternate_collation_providers = property(operator.attrgetter('is_postgresql_10')) diff --git a/django/db/backends/postgresql/introspection.py b/django/db/backends/postgresql/introspection.py index a0e49c8da791b..4e35cb9e97eb8 100644 --- a/django/db/backends/postgresql/introspection.py +++ b/django/db/backends/postgresql/introspection.py @@ -48,13 +48,13 @@ def get_table_list(self, cursor): """Return a list of table and view names in the current database.""" cursor.execute(""" SELECT c.relname, - CASE WHEN {} THEN 'p' WHEN c.relkind IN ('m', 'v') THEN 'v' ELSE 't' END + CASE WHEN c.relispartition THEN 'p' WHEN c.relkind IN ('m', 'v') THEN 'v' ELSE 't' END FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('f', 'm', 'p', 'r', 'v') AND n.nspname NOT IN ('pg_catalog', 'pg_toast') AND pg_catalog.pg_table_is_visible(c.oid) - """.format('c.relispartition' if self.connection.features.supports_table_partitions else 'FALSE')) + """) return [TableInfo(*row) for row in cursor.fetchall() if row[0] not in self.ignored_tables] def get_table_description(self, cursor, table_name): diff --git a/docs/ref/contrib/gis/install/geolibs.txt b/docs/ref/contrib/gis/install/geolibs.txt index 74f17d886a956..405150bc29213 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) 6.x, 5.x, 4.x :doc:`GDAL <../gdal>` Geospatial Data Abstraction Library Yes 3.1, 3.0, 2.4, 2.3, 2.2, 2.1, 2.0 :doc:`GeoIP <../geoip2>` IP-based geolocation library No 2 -`PostGIS`__ Spatial extensions for PostgreSQL Yes (PostgreSQL only) 3.0, 2.5, 2.4, 2.3 +`PostGIS`__ Spatial extensions for PostgreSQL Yes (PostgreSQL only) 3.0, 2.5, 2.4 `SpatiaLite`__ Spatial extensions for SQLite Yes (SQLite only) 4.3 ======================== ==================================== ================================ =================================== @@ -32,7 +32,6 @@ totally fine with GeoDjango. Your mileage may vary. GDAL 2.4.0 2018-12 GDAL 3.0.0 2019-05 GDAL 3.1.0 2020-05-07 - PostGIS 2.3.0 2016-09-26 PostGIS 2.4.0 2017-09-30 PostGIS 2.5.0 2018-09-23 PostGIS 3.0.0 2019-10-20 diff --git a/docs/ref/contrib/gis/install/index.txt b/docs/ref/contrib/gis/install/index.txt index c8343913cf659..cc72a8dc2bb39 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 9.6+ Requires PostGIS. +PostgreSQL GEOS, GDAL, PROJ, PostGIS 10+ Requires PostGIS. MySQL GEOS, GDAL 5.7+ :ref:`Limited functionality `. Oracle GEOS, GDAL 12.2+ XE not supported. SQLite GEOS, GDAL, PROJ, SpatiaLite 3.9.0+ Requires SpatiaLite 4.3+ @@ -347,11 +347,11 @@ running macOS. Because MacPorts still builds the software from source, Summary:: - $ sudo port install postgresql93-server + $ sudo port install postgresql13-server $ sudo port install geos - $ sudo port install proj - $ sudo port install postgis - $ sudo port install gdal +geos + $ sudo port install proj6 + $ sudo port install postgis3 + $ sudo port install gdal $ sudo port install libgeoip .. note:: @@ -359,12 +359,12 @@ Summary:: You will also have to modify the ``PATH`` in your ``.profile`` so that the MacPorts programs are accessible from the command-line:: - export PATH=/opt/local/bin:/opt/local/lib/postgresql93/bin + export PATH=/opt/local/bin:/opt/local/lib/postgresql13/bin In addition, add the ``DYLD_FALLBACK_LIBRARY_PATH`` setting so that the libraries can be found by Python:: - export DYLD_FALLBACK_LIBRARY_PATH=/opt/local/lib:/opt/local/lib/postgresql93 + export DYLD_FALLBACK_LIBRARY_PATH=/opt/local/lib:/opt/local/lib/postgresql13 __ https://www.macports.org/ diff --git a/docs/ref/contrib/gis/install/postgis.txt b/docs/ref/contrib/gis/install/postgis.txt index ec56397a601e4..c769dcf05cf09 100644 --- a/docs/ref/contrib/gis/install/postgis.txt +++ b/docs/ref/contrib/gis/install/postgis.txt @@ -66,10 +66,10 @@ Managing the database --------------------- To administer the database, you can either use the pgAdmin III program -(:menuselection:`Start --> PostgreSQL 9.x --> pgAdmin III`) or the -SQL Shell (:menuselection:`Start --> PostgreSQL 9.x --> SQL Shell`). -For example, to create a ``geodjango`` spatial database and user, the following -may be executed from the SQL Shell as the ``postgres`` user:: +(:menuselection:`Start --> PostgreSQL X --> pgAdmin III`) or the SQL Shell +(:menuselection:`Start --> PostgreSQL X --> SQL Shell`). For example, to create +a ``geodjango`` spatial database and user, the following may be executed from +the SQL Shell as the ``postgres`` user:: postgres# CREATE USER geodjango PASSWORD 'my_passwd'; postgres# CREATE DATABASE geodjango OWNER geodjango; diff --git a/docs/ref/contrib/postgres/operations.txt b/docs/ref/contrib/postgres/operations.txt index 8491ac2f52aa8..dc9faebcdb1d8 100644 --- a/docs/ref/contrib/postgres/operations.txt +++ b/docs/ref/contrib/postgres/operations.txt @@ -159,8 +159,6 @@ For example, to create a collation for German phone book ordering:: .. admonition:: Restrictions - PostgreSQL 9.6 only supports the ``'libc'`` provider. - 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 8fc9e89662365..a9edf43c542b4 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -103,7 +103,7 @@ below for information on how to set up your database correctly. PostgreSQL notes ================ -Django supports PostgreSQL 9.6 and higher. `psycopg2`_ 2.5.4 or higher is +Django supports PostgreSQL 10 and higher. `psycopg2`_ 2.5.4 or higher is required, though the latest release is recommended. .. _psycopg2: https://www.psycopg.org/ diff --git a/docs/ref/unicode.txt b/docs/ref/unicode.txt index e980147f49f05..209cd68060f36 100644 --- a/docs/ref/unicode.txt +++ b/docs/ref/unicode.txt @@ -18,8 +18,8 @@ able to store certain characters in the database, and information will be lost. * MySQL users, refer to the `MySQL manual`_ for details on how to set or alter the database character set encoding. -* PostgreSQL users, refer to the `PostgreSQL manual`_ (section 22.3.2 in - PostgreSQL 9) for details on creating databases with the correct encoding. +* PostgreSQL users, refer to the `PostgreSQL manual`_ for details on creating + databases with the correct encoding. * Oracle users, refer to the `Oracle manual`_ for details on how to set (`section 2`_) or alter (`section 11`_) the database character set encoding. @@ -28,7 +28,7 @@ able to store certain characters in the database, and information will be lost. for internal encoding. .. _MySQL manual: https://dev.mysql.com/doc/refman/en/charset-database.html -.. _PostgreSQL manual: https://www.postgresql.org/docs/current/multibyte.html +.. _PostgreSQL manual: https://www.postgresql.org/docs/current/multibyte.html#id-1.6.10.5.6 .. _Oracle manual: https://docs.oracle.com/en/database/oracle/oracle-database/18/nlspg/index.html .. _section 2: https://docs.oracle.com/en/database/oracle/oracle-database/18/nlspg/choosing-character-set.html .. _section 11: https://docs.oracle.com/en/database/oracle/oracle-database/18/nlspg/character-set-migration.html diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt index ed991fb67293e..9141a4eeaf17e 100644 --- a/docs/releases/4.0.txt +++ b/docs/releases/4.0.txt @@ -224,6 +224,17 @@ backends. * ... +:mod:`django.contrib.gis` +------------------------- + +* Support for PostGIS 2.3 is removed. + +Dropped support for PostgreSQL 9.6 +---------------------------------- + +Upstream support for PostgreSQL 9.6 ends in November 2021. Django 4.0 supports +PostgreSQL 10 and higher. + Miscellaneous ------------- diff --git a/tests/inspectdb/tests.py b/tests/inspectdb/tests.py index a0c4aff2e5af9..8be168110613e 100644 --- a/tests/inspectdb/tests.py +++ b/tests/inspectdb/tests.py @@ -399,7 +399,6 @@ def test_include_materialized_views(self): cursor.execute('DROP MATERIALIZED VIEW inspectdb_people_materialized') @skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific SQL') - @skipUnlessDBFeature('supports_table_partitions') def test_include_partitions(self): """inspectdb --include-partitions creates models for partitions.""" with connection.cursor() as cursor: diff --git a/tests/postgres_tests/test_indexes.py b/tests/postgres_tests/test_indexes.py index 49646feb97c83..a212de2087c4b 100644 --- a/tests/postgres_tests/test_indexes.py +++ b/tests/postgres_tests/test_indexes.py @@ -345,7 +345,6 @@ def test_brin_index(self): editor.remove_index(CharFieldModel, index) self.assertNotIn(index_name, self.get_constraints(CharFieldModel._meta.db_table)) - @skipUnlessDBFeature('has_brin_autosummarize') def test_brin_parameters(self): index_name = 'char_field_brin_params' index = BrinIndex(fields=['field'], name=index_name, autosummarize=True) @@ -358,15 +357,6 @@ def test_brin_parameters(self): editor.remove_index(CharFieldModel, index) self.assertNotIn(index_name, self.get_constraints(CharFieldModel._meta.db_table)) - def test_brin_autosummarize_not_supported(self): - index_name = 'brin_options_exception' - index = BrinIndex(fields=['field'], name=index_name, autosummarize=True) - with self.assertRaisesMessage(NotSupportedError, 'BRIN option autosummarize requires PostgreSQL 10+.'): - with mock.patch('django.db.backends.postgresql.features.DatabaseFeatures.has_brin_autosummarize', False): - with connection.schema_editor() as editor: - editor.add_index(CharFieldModel, index) - self.assertNotIn(index_name, self.get_constraints(CharFieldModel._meta.db_table)) - def test_btree_index(self): # Ensure the table is there and doesn't have an index. self.assertNotIn('field', self.get_constraints(CharFieldModel._meta.db_table)) diff --git a/tests/postgres_tests/test_operations.py b/tests/postgres_tests/test_operations.py index b914a705235ec..9faf938c55e9f 100644 --- a/tests/postgres_tests/test_operations.py +++ b/tests/postgres_tests/test_operations.py @@ -298,7 +298,6 @@ def test_create_non_deterministic_collation(self): 'deterministic': False, }) - @skipUnlessDBFeature('supports_alternate_collation_providers') def test_create_collation_alternate_provider(self): operation = CreateCollation( 'german_phonebook_test', @@ -339,24 +338,6 @@ def test_nondeterministic_collation_not_supported(self): with self.assertRaisesMessage(NotSupportedError, msg): operation.database_forwards(self.app_label, editor, project_state, new_state) - def test_collation_with_icu_provider_raises_error(self): - operation = CreateCollation( - 'german_phonebook', - provider='icu', - locale='de-u-co-phonebk', - ) - project_state = ProjectState() - new_state = project_state.clone() - msg = 'Non-libc providers require PostgreSQL 10+.' - with connection.schema_editor(atomic=False) as editor: - with mock.patch( - 'django.db.backends.postgresql.features.DatabaseFeatures.' - 'supports_alternate_collation_providers', - 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/queries/test_explain.py b/tests/queries/test_explain.py index ba835afb74aaa..4a22656657f83 100644 --- a/tests/queries/test_explain.py +++ b/tests/queries/test_explain.py @@ -52,9 +52,8 @@ def test_postgres_options(self): {'costs': False, 'buffers': True, 'analyze': True}, {'verbose': True, 'timing': True, 'analyze': True}, {'verbose': False, 'timing': False, 'analyze': True}, + {'summary': True}, ] - if connection.features.is_postgresql_10: - test_options.append({'summary': True}) if connection.features.is_postgresql_12: test_options.append({'settings': True}) if connection.features.is_postgresql_13: