mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +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) | ||||
|  | ||||
|     def as_sqlite(self, compiler, connection): | ||||
|         self.arg_joiner = ' || ' | ||||
|         self.template = '%(expressions)s' | ||||
|         self.coalesce() | ||||
|         return super(ConcatPair, self).as_sql(compiler, connection) | ||||
|         coalesced = self.coalesce() | ||||
|         coalesced.arg_joiner = ' || ' | ||||
|         coalesced.template = '%(expressions)s' | ||||
|         return super(ConcatPair, coalesced).as_sql(compiler, connection) | ||||
|  | ||||
|     def as_mysql(self, compiler, connection): | ||||
|         # Use CONCAT_WS with an empty separator so that NULLs are ignored. | ||||
| @@ -55,9 +55,12 @@ class ConcatPair(Func): | ||||
|  | ||||
|     def coalesce(self): | ||||
|         # null on either side results in null for expression, wrap with coalesce | ||||
|         c = self.copy() | ||||
|         expressions = [ | ||||
|             Coalesce(expression, Value('')) for expression in self.get_source_expressions()] | ||||
|         self.set_source_expressions(expressions) | ||||
|             Coalesce(expression, Value('')) for expression in c.get_source_expressions() | ||||
|         ] | ||||
|         c.set_source_expressions(expressions) | ||||
|         return c | ||||
|  | ||||
|  | ||||
| class Concat(Func): | ||||
|   | ||||
| @@ -23,3 +23,5 @@ Bugfixes | ||||
|   have their reverse relations disabled (:ticket:`25545`). | ||||
|  | ||||
| * 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.expressions import RawSQL | ||||
| 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.utils import six, timezone | ||||
| @@ -353,6 +354,19 @@ class FunctionTests(TestCase): | ||||
|         expected = article.title + ' - ' + article.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): | ||||
|         Author.objects.create(name='John Smith', alias='smithj') | ||||
|         Author.objects.create(name='Rhonda') | ||||
|   | ||||
		Reference in New Issue
	
	Block a user