mirror of https://github.com/django/django.git
Fixed #25517 -- Made Concat function idempotent on SQLite.
This commit is contained in:
parent
0922bbf18d
commit
6c95b134e9
|
@ -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')
|
||||||
|
|
Loading…
Reference in New Issue