From e76f9d5b443845639262e18d9020ef4b070f1c7d Mon Sep 17 00:00:00 2001 From: Nick Pope Date: Fri, 28 May 2021 23:52:57 +0100 Subject: [PATCH] Refs #32943 -- Added support for covering SP-GiST indexes on PostgreSQL 14+. --- django/contrib/postgres/indexes.py | 7 +++++ django/db/backends/postgresql/features.py | 1 + docs/ref/contrib/postgres/indexes.txt | 4 +++ docs/ref/models/indexes.txt | 12 ++++++--- docs/releases/4.1.txt | 3 +++ docs/spelling_wordlist | 1 + .../migrations/0002_create_test_models.py | 2 +- tests/postgres_tests/models.py | 2 +- tests/postgres_tests/test_indexes.py | 27 +++++++++++++++++++ 9 files changed, 54 insertions(+), 5 deletions(-) diff --git a/django/contrib/postgres/indexes.py b/django/contrib/postgres/indexes.py index 014bbc379d..2e3de1d275 100644 --- a/django/contrib/postgres/indexes.py +++ b/django/contrib/postgres/indexes.py @@ -220,6 +220,13 @@ 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' diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py index caa37335e0..2917fc4f9a 100644 --- a/django/db/backends/postgresql/features.py +++ b/django/db/backends/postgresql/features.py @@ -99,4 +99,5 @@ class DatabaseFeatures(BaseDatabaseFeatures): has_websearch_to_tsquery = property(operator.attrgetter('is_postgresql_11')) supports_covering_indexes = property(operator.attrgetter('is_postgresql_11')) supports_covering_gist_indexes = property(operator.attrgetter('is_postgresql_12')) + supports_covering_spgist_indexes = property(operator.attrgetter('is_postgresql_14')) supports_non_deterministic_collations = property(operator.attrgetter('is_postgresql_12')) diff --git a/docs/ref/contrib/postgres/indexes.txt b/docs/ref/contrib/postgres/indexes.txt index de1715c239..35de2bf31a 100644 --- a/docs/ref/contrib/postgres/indexes.txt +++ b/docs/ref/contrib/postgres/indexes.txt @@ -138,6 +138,10 @@ available from the ``django.contrib.postgres.indexes`` module. Provide an integer value from 10 to 100 to the fillfactor_ parameter to tune how packed the index pages will be. PostgreSQL's default is 90. + .. versionchanged:: 4.1 + + Support for covering SP-GiST indexes on PostgreSQL 14+ was added. + .. _fillfactor: https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-STORAGE-PARAMETERS ``OpClass()`` expressions diff --git a/docs/ref/models/indexes.txt b/docs/ref/models/indexes.txt index 77739aab65..2992d51d2a 100644 --- a/docs/ref/models/indexes.txt +++ b/docs/ref/models/indexes.txt @@ -210,8 +210,14 @@ See the PostgreSQL documentation for more details about `covering indexes`_. .. admonition:: Restrictions on PostgreSQL - PostgreSQL 11+ only supports covering B-Tree indexes, and PostgreSQL 12+ - also supports covering :class:`GiST indexes - `. + PostgreSQL 11+ only supports covering B-Tree indexes, PostgreSQL 12+ also + supports covering :class:`GiST indexes + `, and PostgreSQL 14+ also + supports covering :class:`SP-GiST indexes + `. + +.. versionchanged:: 4.1 + + Support for covering SP-GiST indexes with PostgreSQL 14+ was added. .. _covering indexes: https://www.postgresql.org/docs/current/indexes-index-only-scans.html diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt index e089ea6cf9..da805df315 100644 --- a/docs/releases/4.1.txt +++ b/docs/releases/4.1.txt @@ -68,6 +68,9 @@ Minor features aggregate function returns an ``int`` of the bitwise ``XOR`` of all non-null input values. +* :class:`~django.contrib.postgres.indexes.SpGistIndex` now supports covering + indexes on PostgreSQL 14+. + :mod:`django.contrib.redirects` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/spelling_wordlist b/docs/spelling_wordlist index 8d9b17a43d..af9b8ca56b 100644 --- a/docs/spelling_wordlist +++ b/docs/spelling_wordlist @@ -196,6 +196,7 @@ geospatial Gettext GiB gis +GiST Googol Greenhill gunicorn diff --git a/tests/postgres_tests/migrations/0002_create_test_models.py b/tests/postgres_tests/migrations/0002_create_test_models.py index 32f99ed6bb..377e220db1 100644 --- a/tests/postgres_tests/migrations/0002_create_test_models.py +++ b/tests/postgres_tests/migrations/0002_create_test_models.py @@ -142,7 +142,7 @@ class Migration(migrations.Migration): name='Scene', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('scene', models.CharField(max_length=255)), + ('scene', models.TextField()), ('setting', models.CharField(max_length=255)), ], options=None, diff --git a/tests/postgres_tests/models.py b/tests/postgres_tests/models.py index fa20378071..adb2e89201 100644 --- a/tests/postgres_tests/models.py +++ b/tests/postgres_tests/models.py @@ -101,7 +101,7 @@ class BigAutoFieldModel(models.Model): # Scene/Character/Line models are used to test full text search. They're # populated with content from Monty Python and the Holy Grail. class Scene(models.Model): - scene = models.CharField(max_length=255) + scene = models.TextField() setting = models.CharField(max_length=255) diff --git a/tests/postgres_tests/test_indexes.py b/tests/postgres_tests/test_indexes.py index e5d1bb02bb..75d0640a08 100644 --- a/tests/postgres_tests/test_indexes.py +++ b/tests/postgres_tests/test_indexes.py @@ -518,6 +518,33 @@ class SchemaTests(PostgreSQLTestCase): editor.remove_index(TextFieldModel, index) self.assertNotIn(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']) + with connection.schema_editor() as editor: + editor.add_index(Scene, index) + constraints = self.get_constraints(Scene._meta.db_table) + self.assertIn(index_name, constraints) + self.assertEqual(constraints[index_name]['type'], SpGistIndex.suffix) + self.assertEqual(constraints[index_name]['columns'], ['scene', 'setting']) + with connection.schema_editor() as editor: + 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_op_class(self): index_name = 'test_op_class' index = Index(