Fixed #27632 -- Unified query parameters by their values on Oracle.
This commit is contained in:
parent
f6671c5d78
commit
6dbe56ed78
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue