Fixed #28626 -- Dropped support for PostgreSQL 9.3.

Thanks Simon Charette for the introspection changes.
This commit is contained in:
Tim Graham 2017-09-06 10:26:45 -04:00
parent ea7ca5db30
commit 1d8cfa3608
13 changed files with 18 additions and 53 deletions

View File

@ -49,10 +49,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
END; END;
$$ LANGUAGE plpgsql;""" $$ LANGUAGE plpgsql;"""
supports_over_clause = True supports_over_clause = True
supports_aggregate_filter_clause = True
@cached_property
def supports_aggregate_filter_clause(self):
return self.connection.pg_version >= 90400
@cached_property @cached_property
def has_select_for_update_skip_locked(self): def has_select_for_update_skip_locked(self):
@ -62,10 +59,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
def has_brin_index_support(self): def has_brin_index_support(self):
return self.connection.pg_version >= 90500 return self.connection.pg_version >= 90500
@cached_property
def has_jsonb_datatype(self):
return self.connection.pg_version >= 90400
@cached_property @cached_property
def has_jsonb_agg(self): def has_jsonb_agg(self):
return self.connection.pg_version >= 90500 return self.connection.pg_version >= 90500

View File

@ -145,17 +145,12 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
# Loop over the key table, collecting things as constraints. The column # Loop over the key table, collecting things as constraints. The column
# array must return column names in the same order in which they were # array must return column names in the same order in which they were
# created. # created.
# The subquery containing generate_series can be replaced with
# "WITH ORDINALITY" when support for PostgreSQL 9.3 is dropped.
cursor.execute(""" cursor.execute("""
SELECT SELECT
c.conname, c.conname,
array( array(
SELECT attname SELECT attname
FROM ( FROM unnest(c.conkey) WITH ORDINALITY cols(colid, arridx)
SELECT unnest(c.conkey) AS colid,
generate_series(1, array_length(c.conkey, 1)) AS arridx
) AS cols
JOIN pg_attribute AS ca ON cols.colid = ca.attnum JOIN pg_attribute AS ca ON cols.colid = ca.attnum
WHERE ca.attrelid = c.conrelid WHERE ca.attrelid = c.conrelid
ORDER BY cols.arridx ORDER BY cols.arridx
@ -183,17 +178,13 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
"options": options, "options": options,
} }
# Now get indexes # 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(""" cursor.execute("""
SELECT SELECT
indexname, array_agg(attname ORDER BY rnum), indisunique, indisprimary, indexname, array_agg(attname ORDER BY arridx), indisunique, indisprimary,
array_agg(ordering ORDER BY rnum), amname, exprdef, s2.attoptions array_agg(ordering ORDER BY arridx), amname, exprdef, s2.attoptions
FROM ( FROM (
SELECT SELECT
row_number() OVER () as rnum, c2.relname as indexname, c2.relname as indexname, idx.*, attr.attname, am.amname,
idx.*, attr.attname, am.amname,
CASE CASE
WHEN idx.indexprs IS NOT NULL THEN WHEN idx.indexprs IS NOT NULL THEN
pg_get_indexdef(idx.indexrelid) pg_get_indexdef(idx.indexrelid)
@ -206,9 +197,8 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
END as ordering, END as ordering,
c2.reloptions as attoptions c2.reloptions as attoptions
FROM ( FROM (
SELECT SELECT *
*, unnest(i.indkey) as key, unnest(i.indoption) as option FROM pg_index i, unnest(i.indkey, i.indoption) WITH ORDINALITY koi(key, option, arridx)
FROM pg_index i
) idx ) idx
LEFT JOIN pg_class c ON idx.indrelid = c.oid LEFT JOIN pg_class c ON idx.indrelid = c.oid
LEFT JOIN pg_class c2 ON idx.indexrelid = c2.oid LEFT JOIN pg_class c2 ON idx.indexrelid = c2.oid

View File

@ -58,7 +58,7 @@ supported versions, and any notes for each of the supported database backends:
================== ============================== ================== ========================================= ================== ============================== ================== =========================================
Database Library Requirements Supported Versions Notes 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 <mysql-spatial-limitations>`. MySQL GEOS, GDAL 5.6+ Not OGC-compliant; :ref:`limited functionality <mysql-spatial-limitations>`.
Oracle GEOS, GDAL 12.1+ XE not supported. Oracle GEOS, GDAL 12.1+ XE not supported.
SQLite GEOS, GDAL, PROJ.4, SpatiaLite 3.6.+ Requires SpatiaLite 4.0+ SQLite GEOS, GDAL, PROJ.4, SpatiaLite 3.6.+ Requires SpatiaLite 4.0+

View File

@ -509,8 +509,6 @@ using in conjunction with lookups on
of the JSON which allows indexing. The trade-off is a small additional cost of the JSON which allows indexing. The trade-off is a small additional cost
on writing to the ``jsonb`` field. ``JSONField`` uses ``jsonb``. on writing to the ``jsonb`` field. ``JSONField`` uses ``jsonb``.
**As a result, this field requires PostgreSQL ≥ 9.4**.
Querying ``JSONField`` Querying ``JSONField``
---------------------- ----------------------

View File

@ -16,8 +16,6 @@ All of these functions are available from the
Returns a version 4 UUID. Returns a version 4 UUID.
Requires PostgreSQL 9.4 or greater.
The `pgcrypto extension`_ must be installed. You can use the The `pgcrypto extension`_ must be installed. You can use the
:class:`~django.contrib.postgres.operations.CryptoExtension` migration :class:`~django.contrib.postgres.operations.CryptoExtension` migration
operation to install it. operation to install it.

View File

@ -92,7 +92,7 @@ below for information on how to set up your database correctly.
PostgreSQL notes 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. required, though the latest release is recommended.
.. _psycopg2: http://initd.org/psycopg/ .. _psycopg2: http://initd.org/psycopg/

View File

@ -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 The end of upstream support for MySQL 5.5 is December 2018. Django 2.1 supports
MySQL 5.6 and higher. 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 Miscellaneous
------------- -------------

View File

@ -7,16 +7,6 @@ from django.db.backends.signals import connection_created
from django.test import TestCase, modify_settings 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") @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific tests")
class PostgreSQLTestCase(TestCase): class PostgreSQLTestCase(TestCase):
@classmethod @classmethod

View File

@ -242,7 +242,7 @@ class Migration(migrations.Migration):
('field_custom', JSONField(null=True, blank=True, encoder=DjangoJSONEncoder)), ('field_custom', JSONField(null=True, blank=True, encoder=DjangoJSONEncoder)),
], ],
options={ options={
'required_db_features': {'has_jsonb_datatype'}, 'required_db_vendor': 'postgresql',
}, },
bases=(models.Model,), bases=(models.Model,),
), ),

View File

@ -140,13 +140,10 @@ class RangeLookupsModel(PostgreSQLModel):
date = models.DateField(blank=True, null=True) date = models.DateField(blank=True, null=True)
class JSONModel(models.Model): class JSONModel(PostgreSQLModel):
field = JSONField(blank=True, null=True) field = JSONField(blank=True, null=True)
field_custom = JSONField(blank=True, null=True, encoder=DjangoJSONEncoder) field_custom = JSONField(blank=True, null=True, encoder=DjangoJSONEncoder)
class Meta:
required_db_features = ['has_jsonb_datatype']
class ArrayFieldSubclass(ArrayField): class ArrayFieldSubclass(ArrayField):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

View File

@ -4,7 +4,7 @@ from time import sleep
from django.contrib.postgres.functions import RandomUUID, TransactionNow from django.contrib.postgres.functions import RandomUUID, TransactionNow
from . import PostgreSQLTestCase, skipUnlessPG94 from . import PostgreSQLTestCase
from .models import NowTestModel, UUIDTestModel from .models import NowTestModel, UUIDTestModel
@ -29,7 +29,6 @@ class TestTransactionNow(PostgreSQLTestCase):
self.assertEqual(m1.when, m2.when) self.assertEqual(m1.when, m2.when)
@skipUnlessPG94
class TestRandomUUID(PostgreSQLTestCase): class TestRandomUUID(PostgreSQLTestCase):
def test_random_uuid(self): def test_random_uuid(self):

View File

@ -1,7 +1,6 @@
from io import StringIO from io import StringIO
from django.core.management import call_command from django.core.management import call_command
from django.test import skipUnlessDBFeature
from django.test.utils import modify_settings from django.test.utils import modify_settings
from . import PostgreSQLTestCase from . import PostgreSQLTestCase
@ -20,7 +19,6 @@ class InspectDBTests(PostgreSQLTestCase):
for field_output in field_outputs: for field_output in field_outputs:
self.assertIn(field_output, output) self.assertIn(field_output, output)
@skipUnlessDBFeature('has_jsonb_datatype')
def test_json_field(self): def test_json_field(self):
self.assertFieldsInModel( self.assertFieldsInModel(
'postgres_tests_jsonmodel', 'postgres_tests_jsonmodel',

View File

@ -5,7 +5,6 @@ from decimal import Decimal
from django.core import exceptions, serializers from django.core import exceptions, serializers
from django.core.serializers.json import DjangoJSONEncoder from django.core.serializers.json import DjangoJSONEncoder
from django.forms import CharField, Form, widgets from django.forms import CharField, Form, widgets
from django.test import skipUnlessDBFeature
from django.utils.html import escape from django.utils.html import escape
from . import PostgreSQLTestCase from . import PostgreSQLTestCase
@ -18,7 +17,6 @@ except ImportError:
pass pass
@skipUnlessDBFeature('has_jsonb_datatype')
class TestSaveLoad(PostgreSQLTestCase): class TestSaveLoad(PostgreSQLTestCase):
def test_null(self): def test_null(self):
instance = JSONModel() instance = JSONModel()
@ -92,7 +90,6 @@ class TestSaveLoad(PostgreSQLTestCase):
self.assertEqual(loaded.field_custom, obj_after) self.assertEqual(loaded.field_custom, obj_after)
@skipUnlessDBFeature('has_jsonb_datatype')
class TestQuerying(PostgreSQLTestCase): class TestQuerying(PostgreSQLTestCase):
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -262,7 +259,6 @@ class TestQuerying(PostgreSQLTestCase):
self.assertTrue(JSONModel.objects.filter(field__foo__iregex=r'^bAr$').exists()) self.assertTrue(JSONModel.objects.filter(field__foo__iregex=r'^bAr$').exists())
@skipUnlessDBFeature('has_jsonb_datatype')
class TestSerialization(PostgreSQLTestCase): class TestSerialization(PostgreSQLTestCase):
test_data = ( test_data = (
'[{"fields": {"field": {"a": "b", "c": null}, "field_custom": null}, ' '[{"fields": {"field": {"a": "b", "c": null}, "field_custom": null}, '