Fixed #28626 -- Dropped support for PostgreSQL 9.3.
Thanks Simon Charette for the introspection changes.
This commit is contained in:
parent
ea7ca5db30
commit
1d8cfa3608
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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+
|
||||||
|
|
|
@ -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``
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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/
|
||||||
|
|
|
@ -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
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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}, '
|
||||||
|
|
Loading…
Reference in New Issue