[1.8.x] Fixed #25377 -- Changed Count queries to execute COUNT(*) instead of COUNT('*').

Backport of 3fe3887a2e from master
This commit is contained in:
Adam Chainz 2015-09-10 17:07:09 +01:00 committed by Tim Graham
parent 69017bade0
commit 3c2c74f58f
6 changed files with 33 additions and 14 deletions

View File

@ -2,7 +2,7 @@
Classes to represent the definitions of aggregate functions.
"""
from django.core.exceptions import FieldError
from django.db.models.expressions import Func, Value
from django.db.models.expressions import Func, Star
from django.db.models.fields import FloatField, IntegerField
__all__ = [
@ -90,7 +90,7 @@ class Count(Aggregate):
def __init__(self, expression, distinct=False, **extra):
if expression == '*':
expression = Value(expression)
expression = Star()
super(Count, self).__init__(
expression, distinct='DISTINCT ' if distinct else '', output_field=IntegerField(), **extra)

View File

@ -584,6 +584,14 @@ class RawSQL(Expression):
return [self]
class Star(Expression):
def __repr__(self):
return "'*'"
def as_sql(self, compiler, connection):
return '*', []
class Random(Expression):
def __init__(self):
super(Random, self).__init__(output_field=fields.FloatField())

View File

@ -32,3 +32,7 @@ Bugfixes
* Fixed migrations crash on MySQL when adding a text or a blob field with an
unhashable default (:ticket:`25393`).
* Changed ``Count`` queries to execute ``COUNT(*)`` instead of ``COUNT('*')``
as versions of Django before 1.8 did (:ticket:`25377`). This may fix a
performance regression on some databases.

View File

@ -306,6 +306,12 @@ class BaseAggregateTestCase(TestCase):
vals = Book.objects.aggregate(Count("rating", distinct=True))
self.assertEqual(vals, {"rating__count": 4})
def test_count_star(self):
with self.assertNumQueries(1) as ctx:
Book.objects.aggregate(n=Count("*"))
sql = ctx.captured_queries[0]['sql']
self.assertIn('SELECT COUNT(*) ', sql)
def test_fkey_aggregate(self):
explicit = list(Author.objects.annotate(Count('book__id')))
implicit = list(Author.objects.annotate(Count('book')))

View File

@ -877,6 +877,7 @@ class ReprTests(TestCase):
def test_aggregates(self):
self.assertEqual(repr(Avg('a')), "Avg(F(a))")
self.assertEqual(repr(Count('a')), "Count(F(a), distinct=False)")
self.assertEqual(repr(Count('*')), "Count('*', distinct=False)")
self.assertEqual(repr(Max('a')), "Max(F(a))")
self.assertEqual(repr(Min('a')), "Min(F(a))")
self.assertEqual(repr(StdDev('a')), "StdDev(F(a), sample=False)")

View File

@ -63,25 +63,25 @@ class TestDebugSQL(unittest.TestCase):
if six.PY3:
expected_outputs = [
('''QUERY = 'SELECT COUNT(%s) AS "__count" '''
('''QUERY = 'SELECT COUNT(*) AS "__count" '''
'''FROM "test_runner_person" WHERE '''
'''"test_runner_person"."first_name" = %s' '''
'''- PARAMS = ('*', 'error');'''),
('''QUERY = 'SELECT COUNT(%s) AS "__count" '''
'''- PARAMS = ('error',);'''),
('''QUERY = 'SELECT COUNT(*) AS "__count" '''
'''FROM "test_runner_person" WHERE '''
'''"test_runner_person"."first_name" = %s' '''
'''- PARAMS = ('*', 'fail');'''),
'''- PARAMS = ('fail',);'''),
]
else:
expected_outputs = [
('''QUERY = u'SELECT COUNT(%s) AS "__count" '''
('''QUERY = u'SELECT COUNT(*) AS "__count" '''
'''FROM "test_runner_person" WHERE '''
'''"test_runner_person"."first_name" = %s' '''
'''- PARAMS = (u'*', u'error');'''),
('''QUERY = u'SELECT COUNT(%s) AS "__count" '''
'''- PARAMS = (u'error',);'''),
('''QUERY = u'SELECT COUNT(*) AS "__count" '''
'''FROM "test_runner_person" WHERE '''
'''"test_runner_person"."first_name" = %s' '''
'''- PARAMS = (u'*', u'fail');'''),
'''- PARAMS = (u'fail',);'''),
]
verbose_expected_outputs = [
@ -94,15 +94,15 @@ class TestDebugSQL(unittest.TestCase):
]
if six.PY3:
verbose_expected_outputs += [
('''QUERY = 'SELECT COUNT(%s) AS "__count" '''
('''QUERY = 'SELECT COUNT(*) AS "__count" '''
'''FROM "test_runner_person" WHERE '''
'''"test_runner_person"."first_name" = %s' '''
'''- PARAMS = ('*', 'pass');'''),
'''- PARAMS = ('pass',);'''),
]
else:
verbose_expected_outputs += [
('''QUERY = u'SELECT COUNT(%s) AS "__count" '''
('''QUERY = u'SELECT COUNT(*) AS "__count" '''
'''FROM "test_runner_person" WHERE '''
'''"test_runner_person"."first_name" = %s' '''
'''- PARAMS = (u'*', u'pass');'''),
'''- PARAMS = (u'pass',);'''),
]