mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	[1.9.x] Fixed #25517 -- Made Concat function idempotent on SQLite.
Backport of 6c95b134e9 from master
			
			
This commit is contained in:
		| @@ -42,10 +42,10 @@ class ConcatPair(Func): | |||||||
|         super(ConcatPair, self).__init__(left, right, **extra) |         super(ConcatPair, self).__init__(left, right, **extra) | ||||||
|  |  | ||||||
|     def as_sqlite(self, compiler, connection): |     def as_sqlite(self, compiler, connection): | ||||||
|         self.arg_joiner = ' || ' |         coalesced = self.coalesce() | ||||||
|         self.template = '%(expressions)s' |         coalesced.arg_joiner = ' || ' | ||||||
|         self.coalesce() |         coalesced.template = '%(expressions)s' | ||||||
|         return super(ConcatPair, self).as_sql(compiler, connection) |         return super(ConcatPair, coalesced).as_sql(compiler, connection) | ||||||
|  |  | ||||||
|     def as_mysql(self, compiler, connection): |     def as_mysql(self, compiler, connection): | ||||||
|         # Use CONCAT_WS with an empty separator so that NULLs are ignored. |         # Use CONCAT_WS with an empty separator so that NULLs are ignored. | ||||||
| @@ -55,9 +55,12 @@ class ConcatPair(Func): | |||||||
|  |  | ||||||
|     def coalesce(self): |     def coalesce(self): | ||||||
|         # null on either side results in null for expression, wrap with coalesce |         # null on either side results in null for expression, wrap with coalesce | ||||||
|  |         c = self.copy() | ||||||
|         expressions = [ |         expressions = [ | ||||||
|             Coalesce(expression, Value('')) for expression in self.get_source_expressions()] |             Coalesce(expression, Value('')) for expression in c.get_source_expressions() | ||||||
|         self.set_source_expressions(expressions) |         ] | ||||||
|  |         c.set_source_expressions(expressions) | ||||||
|  |         return c | ||||||
|  |  | ||||||
|  |  | ||||||
| class Concat(Func): | class Concat(Func): | ||||||
|   | |||||||
| @@ -23,3 +23,5 @@ Bugfixes | |||||||
|   have their reverse relations disabled (:ticket:`25545`). |   have their reverse relations disabled (:ticket:`25545`). | ||||||
|  |  | ||||||
| * Allowed filtering over a ``RawSQL`` annotation (:ticket:`25506`). | * Allowed filtering over a ``RawSQL`` annotation (:ticket:`25506`). | ||||||
|  |  | ||||||
|  | * Made the ``Concat`` database function idempotent on SQLite (:ticket:`25517`). | ||||||
|   | |||||||
| @@ -7,7 +7,8 @@ from django.db import connection | |||||||
| from django.db.models import CharField, TextField, Value as V | from django.db.models import CharField, TextField, Value as V | ||||||
| from django.db.models.expressions import RawSQL | from django.db.models.expressions import RawSQL | ||||||
| from django.db.models.functions import ( | from django.db.models.functions import ( | ||||||
|     Coalesce, Concat, Greatest, Least, Length, Lower, Now, Substr, Upper, |     Coalesce, Concat, ConcatPair, Greatest, Least, Length, Lower, Now, Substr, | ||||||
|  |     Upper, | ||||||
| ) | ) | ||||||
| from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature | from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature | ||||||
| from django.utils import six, timezone | from django.utils import six, timezone | ||||||
| @@ -353,6 +354,19 @@ class FunctionTests(TestCase): | |||||||
|         expected = article.title + ' - ' + article.text |         expected = article.title + ' - ' + article.text | ||||||
|         self.assertEqual(expected.upper(), article.title_text) |         self.assertEqual(expected.upper(), article.title_text) | ||||||
|  |  | ||||||
|  |     @skipUnless(connection.vendor == 'sqlite', "sqlite specific implementation detail.") | ||||||
|  |     def test_concat_coalesce_idempotent(self): | ||||||
|  |         pair = ConcatPair(V('a'), V('b')) | ||||||
|  |         # Check nodes counts | ||||||
|  |         self.assertEqual(len(list(pair.flatten())), 3) | ||||||
|  |         self.assertEqual(len(list(pair.coalesce().flatten())), 7)  # + 2 Coalesce + 2 Value() | ||||||
|  |         self.assertEqual(len(list(pair.flatten())), 3) | ||||||
|  |  | ||||||
|  |     def test_concat_sql_generation_idempotency(self): | ||||||
|  |         qs = Article.objects.annotate(description=Concat('title', V(': '), 'summary')) | ||||||
|  |         # Multiple compilations should not alter the generated query. | ||||||
|  |         self.assertEqual(str(qs.query), str(qs.all().query)) | ||||||
|  |  | ||||||
|     def test_lower(self): |     def test_lower(self): | ||||||
|         Author.objects.create(name='John Smith', alias='smithj') |         Author.objects.create(name='John Smith', alias='smithj') | ||||||
|         Author.objects.create(name='Rhonda') |         Author.objects.create(name='Rhonda') | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user