diff --git a/django/contrib/postgres/search.py b/django/contrib/postgres/search.py index f8c691d73b..08d92e3514 100644 --- a/django/contrib/postgres/search.py +++ b/django/contrib/postgres/search.py @@ -60,14 +60,6 @@ class SearchVector(SearchVectorCombinable, Func): def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): resolved = super().resolve_expression(query, allow_joins, reuse, summarize, for_save) - resolved.set_source_expressions([ - Coalesce( - expression - if isinstance(expression.output_field, (CharField, TextField)) - else Cast(expression, TextField()), - Value('') - ) for expression in resolved.get_source_expressions() - ]) if self.config: if not hasattr(self.config, 'resolve_expression'): resolved.config = Value(self.config).resolve_expression(query, allow_joins, reuse, summarize, for_save) @@ -76,17 +68,26 @@ class SearchVector(SearchVectorCombinable, Func): return resolved def as_sql(self, compiler, connection, function=None, template=None): + clone = self.copy() + clone.set_source_expressions([ + Coalesce( + expression + if isinstance(expression.output_field, (CharField, TextField)) + else Cast(expression, TextField()), + Value('') + ) for expression in clone.get_source_expressions() + ]) config_params = [] if template is None: - if self.config: - config_sql, config_params = compiler.compile(self.config) + if clone.config: + config_sql, config_params = compiler.compile(clone.config) template = '%(function)s({}::regconfig, %(expressions)s)'.format(config_sql.replace('%', '%%')) else: - template = self.template - sql, params = super().as_sql(compiler, connection, function=function, template=template) + template = clone.template + sql, params = super(SearchVector, clone).as_sql(compiler, connection, function=function, template=template) extra_params = [] - if self.weight: - weight_sql, extra_params = compiler.compile(self.weight) + if clone.weight: + weight_sql, extra_params = compiler.compile(clone.weight) sql = 'setweight({}, {})'.format(sql, weight_sql) return sql, config_params + params + extra_params diff --git a/docs/releases/2.2.2.txt b/docs/releases/2.2.2.txt index 377abaa3c0..d68338dce2 100644 --- a/docs/releases/2.2.2.txt +++ b/docs/releases/2.2.2.txt @@ -14,3 +14,7 @@ Bugfixes * Fixed a regression in Django 2.2 where deprecation message crashes if ``Meta.ordering`` contains an expression (:ticket:`30463`). + +* Fixed a regression in Django 2.2.1 where + :class:`~django.contrib.postgres.search.SearchVector` generates SQL with a + redundant ``Coalesce`` call (:ticket:`30488`). diff --git a/tests/postgres_tests/test_search.py b/tests/postgres_tests/test_search.py index 8944c6342d..f5111ce8d3 100644 --- a/tests/postgres_tests/test_search.py +++ b/tests/postgres_tests/test_search.py @@ -113,6 +113,10 @@ class SearchVectorFieldTest(GrailTestData, PostgreSQLTestCase): searched = Line.objects.filter(dialogue_search_vector=SearchQuery('cadeaux', config='french')) self.assertSequenceEqual(searched, [self.french]) + def test_single_coalesce_expression(self): + searched = Line.objects.annotate(search=SearchVector('dialogue')).filter(search='cadeaux') + self.assertNotIn('COALESCE(COALESCE', str(searched.query)) + class MultipleFieldsTest(GrailTestData, PostgreSQLTestCase):