Fixed #27632 -- Unified query parameters by their values on Oracle.

This commit is contained in:
Mariusz Felisiak 2016-12-24 13:26:15 +01:00 committed by Tim Graham
parent f6671c5d78
commit 6dbe56ed78
2 changed files with 27 additions and 2 deletions

View File

@ -450,7 +450,7 @@ class FormatStylePlaceholderCursor(object):
else:
return [p.force_bytes for p in params]
def _fix_for_params(self, query, params):
def _fix_for_params(self, query, params, unify_by_values=False):
# cx_Oracle wants no trailing ';' for SQL statements. For PL/SQL, it
# it does want a trailing ';' but not a trailing '/'. However, these
# characters must be included in the original query in case the query
@ -464,6 +464,18 @@ class FormatStylePlaceholderCursor(object):
# Handle params as dict
args = {k: ":%s" % k for k in params.keys()}
query = convert_unicode(query % args, self.charset)
elif unify_by_values:
# Handle params as a dict with unified query parameters by their
# values. It can be used only in single query execute() because
# executemany() shares the formatted query with each of the params
# list. e.g. for input params = [0.75, 2, 0.75, 'sth', 0.75]
# params_dict = {0.75: ':arg0', 2: ':arg1', 'sth': ':arg2'}
# args = [':arg0', ':arg1', ':arg0', ':arg2', ':arg0']
# params = {':arg0': 0.75, ':arg1': 2, ':arg2': 'sth'}
params_dict = {param: ':arg%d' % i for i, param in enumerate(set(params))}
args = [params_dict[param] for param in params]
params = dict(zip(params_dict.values(), params_dict.keys()))
query = convert_unicode(query % tuple(args), self.charset)
else:
# Handle params as sequence
args = [(':arg%d' % i) for i in range(len(params))]
@ -471,7 +483,7 @@ class FormatStylePlaceholderCursor(object):
return query, self._format_params(params)
def execute(self, query, params=None):
query, params = self._fix_for_params(query, params)
query, params = self._fix_for_params(query, params, unify_by_values=True)
self._guess_input_sizes([params])
try:
return self.cursor.execute(query, self._param_generator(params))

View File

@ -107,6 +107,19 @@ class AggregationTests(TestCase):
for attr, value in six.iteritems(kwargs):
self.assertEqual(getattr(obj, attr), value)
def test_annotation_with_value(self):
values = Book.objects.filter(
name='Practical Django Projects',
).annotate(
discount_price=F('price') * 2,
).values(
'discount_price',
).annotate(sum_discount=Sum('discount_price'))
self.assertSequenceEqual(
values,
[{'discount_price': Decimal('59.38'), 'sum_discount': Decimal('59.38')}]
)
def test_aggregates_in_where_clause(self):
"""
Regression test for #12822: DatabaseError: aggregates not allowed in