diff --git a/django/contrib/postgres/indexes.py b/django/contrib/postgres/indexes.py index 43c5588d58..7c41c7f340 100644 --- a/django/contrib/postgres/indexes.py +++ b/django/contrib/postgres/indexes.py @@ -31,20 +31,25 @@ class PostgresIndex(Index): class BrinIndex(PostgresIndex): suffix = 'brin' - def __init__(self, *, pages_per_range=None, **kwargs): + def __init__(self, *, autosummarize=None, pages_per_range=None, **kwargs): if pages_per_range is not None and pages_per_range <= 0: raise ValueError('pages_per_range must be None or a positive integer') + self.autosummarize = autosummarize self.pages_per_range = pages_per_range super().__init__(**kwargs) def deconstruct(self): path, args, kwargs = super().deconstruct() + if self.autosummarize is not None: + kwargs['autosummarize'] = self.autosummarize if self.pages_per_range is not None: kwargs['pages_per_range'] = self.pages_per_range return path, args, kwargs def get_with_params(self): with_params = [] + if self.autosummarize is not None: + with_params.append('autosummarize = %s' % ('on' if self.autosummarize else 'off')) if self.pages_per_range is not None: with_params.append('pages_per_range = %d' % self.pages_per_range) return with_params diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py index 06b0303bac..a2a231554f 100644 --- a/django/db/backends/postgresql/features.py +++ b/django/db/backends/postgresql/features.py @@ -57,7 +57,12 @@ class DatabaseFeatures(BaseDatabaseFeatures): def is_postgresql_9_5(self): return self.connection.pg_version >= 90500 + @cached_property + def is_postgresql_10(self): + return self.connection.pg_version >= 100000 + has_select_for_update_skip_locked = is_postgresql_9_5 has_brin_index_support = is_postgresql_9_5 has_jsonb_agg = is_postgresql_9_5 + has_brin_autosummarize = is_postgresql_10 has_gin_pending_list_limit = is_postgresql_9_5 diff --git a/docs/ref/contrib/postgres/indexes.txt b/docs/ref/contrib/postgres/indexes.txt index 67a8e1b98b..cb0decc9a1 100644 --- a/docs/ref/contrib/postgres/indexes.txt +++ b/docs/ref/contrib/postgres/indexes.txt @@ -10,13 +10,22 @@ available from the ``django.contrib.postgres.indexes`` module. ``BrinIndex`` ============= -.. class:: BrinIndex(pages_per_range=None, **options) +.. class:: BrinIndex(autosummarize=None, pages_per_range=None, **options) Creates a `BRIN index `_. + Set the ``autosummarize`` parameter to ``True`` to enable `automatic + summarization`_ to be performed by autovacuum. + The ``pages_per_range`` argument takes a positive integer. + .. _automatic summarization: https://www.postgresql.org/docs/current/static/brin-intro.html#BRIN-OPERATION + + .. versionchanged:: 2.2 + + The ``autosummarize`` parameter was added. + ``GinIndex`` ============ diff --git a/docs/releases/2.2.txt b/docs/releases/2.2.txt index 82018cba18..161f477695 100644 --- a/docs/releases/2.2.txt +++ b/docs/releases/2.2.txt @@ -86,6 +86,9 @@ Minor features :class:`~django.contrib.postgres.indexes.SpGistIndex` classes allow creating ``hash`` and ``SP-GiST`` indexes in the database. +* :class:`~django.contrib.postgres.indexes.BrinIndex` now has the + ``autosummarize`` parameter. + :mod:`django.contrib.redirects` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/spelling_wordlist b/docs/spelling_wordlist index 6721a77e6f..f8a718a3bc 100644 --- a/docs/spelling_wordlist +++ b/docs/spelling_wordlist @@ -42,6 +42,7 @@ autoextend autogenerated autoincrement autoreload +autovacuum Azerbaijani backend backends diff --git a/tests/postgres_tests/test_indexes.py b/tests/postgres_tests/test_indexes.py index b07716e40d..9bca4510fe 100644 --- a/tests/postgres_tests/test_indexes.py +++ b/tests/postgres_tests/test_indexes.py @@ -31,11 +31,16 @@ class BrinIndexTests(IndexTestMixin, PostgreSQLTestCase): self.assertEqual(BrinIndex.suffix, 'brin') def test_deconstruction(self): - index = BrinIndex(fields=['title'], name='test_title_brin', pages_per_range=16) + index = BrinIndex(fields=['title'], name='test_title_brin', autosummarize=True, pages_per_range=16) path, args, kwargs = index.deconstruct() self.assertEqual(path, 'django.contrib.postgres.indexes.BrinIndex') self.assertEqual(args, ()) - self.assertEqual(kwargs, {'fields': ['title'], 'name': 'test_title_brin', 'pages_per_range': 16}) + self.assertEqual(kwargs, { + 'fields': ['title'], + 'name': 'test_title_brin', + 'autosummarize': True, + 'pages_per_range': 16, + }) def test_invalid_pages_per_range(self): with self.assertRaisesMessage(ValueError, 'pages_per_range must be None or a positive integer'): @@ -176,6 +181,19 @@ class SchemaTests(PostgreSQLTestCase): editor.remove_index(CharFieldModel, index) self.assertNotIn(index_name, self.get_constraints(CharFieldModel._meta.db_table)) + @skipUnlessDBFeature('has_brin_index_support', 'has_brin_autosummarize') + def test_brin_parameters(self): + index_name = 'char_field_brin_params' + index = BrinIndex(fields=['field'], name=index_name, autosummarize=True) + with connection.schema_editor() as editor: + editor.add_index(CharFieldModel, index) + constraints = self.get_constraints(CharFieldModel._meta.db_table) + self.assertEqual(constraints[index_name]['type'], BrinIndex.suffix) + self.assertEqual(constraints[index_name]['options'], ['autosummarize=on']) + with connection.schema_editor() as editor: + editor.remove_index(CharFieldModel, index) + self.assertNotIn(index_name, self.get_constraints(CharFieldModel._meta.db_table)) + def test_gist_index(self): # Ensure the table is there and doesn't have an index. self.assertNotIn('field', self.get_constraints(CharFieldModel._meta.db_table))