From 1d8cfa36089f2d1295abad03a99fc3c259bde6b5 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Wed, 6 Sep 2017 10:26:45 -0400 Subject: [PATCH] Fixed #28626 -- Dropped support for PostgreSQL 9.3. Thanks Simon Charette for the introspection changes. --- django/db/backends/postgresql/features.py | 9 +------- .../db/backends/postgresql/introspection.py | 22 +++++-------------- docs/ref/contrib/gis/install/index.txt | 2 +- docs/ref/contrib/postgres/fields.txt | 2 -- docs/ref/contrib/postgres/functions.txt | 2 -- docs/ref/databases.txt | 2 +- docs/releases/2.1.txt | 6 +++++ tests/postgres_tests/__init__.py | 10 --------- .../migrations/0002_create_test_models.py | 2 +- tests/postgres_tests/models.py | 5 +---- tests/postgres_tests/test_functions.py | 3 +-- tests/postgres_tests/test_introspection.py | 2 -- tests/postgres_tests/test_json.py | 4 ---- 13 files changed, 18 insertions(+), 53 deletions(-) diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py index 0349493dae..c70966c2f6 100644 --- a/django/db/backends/postgresql/features.py +++ b/django/db/backends/postgresql/features.py @@ -49,10 +49,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): END; $$ LANGUAGE plpgsql;""" supports_over_clause = True - - @cached_property - def supports_aggregate_filter_clause(self): - return self.connection.pg_version >= 90400 + supports_aggregate_filter_clause = True @cached_property def has_select_for_update_skip_locked(self): @@ -62,10 +59,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): def has_brin_index_support(self): return self.connection.pg_version >= 90500 - @cached_property - def has_jsonb_datatype(self): - return self.connection.pg_version >= 90400 - @cached_property def has_jsonb_agg(self): return self.connection.pg_version >= 90500 diff --git a/django/db/backends/postgresql/introspection.py b/django/db/backends/postgresql/introspection.py index f060546a53..30f764f2f0 100644 --- a/django/db/backends/postgresql/introspection.py +++ b/django/db/backends/postgresql/introspection.py @@ -145,17 +145,12 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): # Loop over the key table, collecting things as constraints. The column # array must return column names in the same order in which they were # created. - # The subquery containing generate_series can be replaced with - # "WITH ORDINALITY" when support for PostgreSQL 9.3 is dropped. cursor.execute(""" SELECT c.conname, array( SELECT attname - FROM ( - SELECT unnest(c.conkey) AS colid, - generate_series(1, array_length(c.conkey, 1)) AS arridx - ) AS cols + FROM unnest(c.conkey) WITH ORDINALITY cols(colid, arridx) JOIN pg_attribute AS ca ON cols.colid = ca.attnum WHERE ca.attrelid = c.conrelid ORDER BY cols.arridx @@ -183,17 +178,13 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): "options": options, } # Now get indexes - # The row_number() function for ordering the index fields can be - # replaced by WITH ORDINALITY in the unnest() functions when support - # for PostgreSQL 9.3 is dropped. cursor.execute(""" SELECT - indexname, array_agg(attname ORDER BY rnum), indisunique, indisprimary, - array_agg(ordering ORDER BY rnum), amname, exprdef, s2.attoptions + indexname, array_agg(attname ORDER BY arridx), indisunique, indisprimary, + array_agg(ordering ORDER BY arridx), amname, exprdef, s2.attoptions FROM ( SELECT - row_number() OVER () as rnum, c2.relname as indexname, - idx.*, attr.attname, am.amname, + c2.relname as indexname, idx.*, attr.attname, am.amname, CASE WHEN idx.indexprs IS NOT NULL THEN pg_get_indexdef(idx.indexrelid) @@ -206,9 +197,8 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): END as ordering, c2.reloptions as attoptions FROM ( - SELECT - *, unnest(i.indkey) as key, unnest(i.indoption) as option - FROM pg_index i + SELECT * + FROM pg_index i, unnest(i.indkey, i.indoption) WITH ORDINALITY koi(key, option, arridx) ) idx LEFT JOIN pg_class c ON idx.indrelid = c.oid LEFT JOIN pg_class c2 ON idx.indexrelid = c2.oid diff --git a/docs/ref/contrib/gis/install/index.txt b/docs/ref/contrib/gis/install/index.txt index c547c9b268..9872f25602 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.4, PostGIS 9.3+ Requires PostGIS. +PostgreSQL GEOS, GDAL, PROJ.4, PostGIS 9.4+ Requires PostGIS. MySQL GEOS, GDAL 5.6+ Not OGC-compliant; :ref:`limited functionality `. Oracle GEOS, GDAL 12.1+ XE not supported. SQLite GEOS, GDAL, PROJ.4, SpatiaLite 3.6.+ Requires SpatiaLite 4.0+ diff --git a/docs/ref/contrib/postgres/fields.txt b/docs/ref/contrib/postgres/fields.txt index 0a39f000f1..38ca9200cd 100644 --- a/docs/ref/contrib/postgres/fields.txt +++ b/docs/ref/contrib/postgres/fields.txt @@ -509,8 +509,6 @@ using in conjunction with lookups on of the JSON which allows indexing. The trade-off is a small additional cost on writing to the ``jsonb`` field. ``JSONField`` uses ``jsonb``. - **As a result, this field requires PostgreSQL ≥ 9.4**. - Querying ``JSONField`` ---------------------- diff --git a/docs/ref/contrib/postgres/functions.txt b/docs/ref/contrib/postgres/functions.txt index 171272c518..8d3df51864 100644 --- a/docs/ref/contrib/postgres/functions.txt +++ b/docs/ref/contrib/postgres/functions.txt @@ -16,8 +16,6 @@ All of these functions are available from the Returns a version 4 UUID. -Requires PostgreSQL 9.4 or greater. - The `pgcrypto extension`_ must be installed. You can use the :class:`~django.contrib.postgres.operations.CryptoExtension` migration operation to install it. diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index e7d474a81d..1005780f19 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -92,7 +92,7 @@ below for information on how to set up your database correctly. PostgreSQL notes ================ -Django supports PostgreSQL 9.3 and higher. `psycopg2`_ 2.5.4 or higher is +Django supports PostgreSQL 9.4 and higher. `psycopg2`_ 2.5.4 or higher is required, though the latest release is recommended. .. _psycopg2: http://initd.org/psycopg/ diff --git a/docs/releases/2.1.txt b/docs/releases/2.1.txt index ead4d2fde3..fa4b407d9a 100644 --- a/docs/releases/2.1.txt +++ b/docs/releases/2.1.txt @@ -206,6 +206,12 @@ Dropped support for MySQL 5.5 The end of upstream support for MySQL 5.5 is December 2018. Django 2.1 supports MySQL 5.6 and higher. +Dropped support for PostgreSQL 9.3 +---------------------------------- + +The end of upstream support for PostgreSQL 9.3 is September 2018. Django 2.1 +supports PostgreSQL 9.4 and higher. + Miscellaneous ------------- diff --git a/tests/postgres_tests/__init__.py b/tests/postgres_tests/__init__.py index ea6b96aff9..24d78c9bfe 100644 --- a/tests/postgres_tests/__init__.py +++ b/tests/postgres_tests/__init__.py @@ -7,16 +7,6 @@ from django.db.backends.signals import connection_created from django.test import TestCase, modify_settings -def skipUnlessPG94(test): - try: - PG_VERSION = connection.pg_version - except AttributeError: - PG_VERSION = 0 - if PG_VERSION < 90400: - return unittest.skip('PostgreSQL ≥ 9.4 required')(test) - return test - - @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific tests") class PostgreSQLTestCase(TestCase): @classmethod diff --git a/tests/postgres_tests/migrations/0002_create_test_models.py b/tests/postgres_tests/migrations/0002_create_test_models.py index 1cb6fe686e..cbc984d485 100644 --- a/tests/postgres_tests/migrations/0002_create_test_models.py +++ b/tests/postgres_tests/migrations/0002_create_test_models.py @@ -242,7 +242,7 @@ class Migration(migrations.Migration): ('field_custom', JSONField(null=True, blank=True, encoder=DjangoJSONEncoder)), ], options={ - 'required_db_features': {'has_jsonb_datatype'}, + 'required_db_vendor': 'postgresql', }, bases=(models.Model,), ), diff --git a/tests/postgres_tests/models.py b/tests/postgres_tests/models.py index b7094b77e1..fa390daaa8 100644 --- a/tests/postgres_tests/models.py +++ b/tests/postgres_tests/models.py @@ -140,13 +140,10 @@ class RangeLookupsModel(PostgreSQLModel): date = models.DateField(blank=True, null=True) -class JSONModel(models.Model): +class JSONModel(PostgreSQLModel): field = JSONField(blank=True, null=True) field_custom = JSONField(blank=True, null=True, encoder=DjangoJSONEncoder) - class Meta: - required_db_features = ['has_jsonb_datatype'] - class ArrayFieldSubclass(ArrayField): def __init__(self, *args, **kwargs): diff --git a/tests/postgres_tests/test_functions.py b/tests/postgres_tests/test_functions.py index de5cb4d767..875a4b9520 100644 --- a/tests/postgres_tests/test_functions.py +++ b/tests/postgres_tests/test_functions.py @@ -4,7 +4,7 @@ from time import sleep from django.contrib.postgres.functions import RandomUUID, TransactionNow -from . import PostgreSQLTestCase, skipUnlessPG94 +from . import PostgreSQLTestCase from .models import NowTestModel, UUIDTestModel @@ -29,7 +29,6 @@ class TestTransactionNow(PostgreSQLTestCase): self.assertEqual(m1.when, m2.when) -@skipUnlessPG94 class TestRandomUUID(PostgreSQLTestCase): def test_random_uuid(self): diff --git a/tests/postgres_tests/test_introspection.py b/tests/postgres_tests/test_introspection.py index b17d9fc81a..a63f08db50 100644 --- a/tests/postgres_tests/test_introspection.py +++ b/tests/postgres_tests/test_introspection.py @@ -1,7 +1,6 @@ from io import StringIO from django.core.management import call_command -from django.test import skipUnlessDBFeature from django.test.utils import modify_settings from . import PostgreSQLTestCase @@ -20,7 +19,6 @@ class InspectDBTests(PostgreSQLTestCase): for field_output in field_outputs: self.assertIn(field_output, output) - @skipUnlessDBFeature('has_jsonb_datatype') def test_json_field(self): self.assertFieldsInModel( 'postgres_tests_jsonmodel', diff --git a/tests/postgres_tests/test_json.py b/tests/postgres_tests/test_json.py index 2506fc36d6..cdabca04d6 100644 --- a/tests/postgres_tests/test_json.py +++ b/tests/postgres_tests/test_json.py @@ -5,7 +5,6 @@ from decimal import Decimal from django.core import exceptions, serializers from django.core.serializers.json import DjangoJSONEncoder from django.forms import CharField, Form, widgets -from django.test import skipUnlessDBFeature from django.utils.html import escape from . import PostgreSQLTestCase @@ -18,7 +17,6 @@ except ImportError: pass -@skipUnlessDBFeature('has_jsonb_datatype') class TestSaveLoad(PostgreSQLTestCase): def test_null(self): instance = JSONModel() @@ -92,7 +90,6 @@ class TestSaveLoad(PostgreSQLTestCase): self.assertEqual(loaded.field_custom, obj_after) -@skipUnlessDBFeature('has_jsonb_datatype') class TestQuerying(PostgreSQLTestCase): @classmethod def setUpTestData(cls): @@ -262,7 +259,6 @@ class TestQuerying(PostgreSQLTestCase): self.assertTrue(JSONModel.objects.filter(field__foo__iregex=r'^bAr$').exists()) -@skipUnlessDBFeature('has_jsonb_datatype') class TestSerialization(PostgreSQLTestCase): test_data = ( '[{"fields": {"field": {"a": "b", "c": null}, "field_custom": null}, '