summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2016-12-24 13:26:15 +0100
committerTim Graham <timograham@gmail.com>2016-12-28 08:14:14 -0500
commit6dbe56ed7855f34585884a2381fb1cec22ddc824 (patch)
treea3791fc871c0280c28cfb7ed383d56cf5f3148c5
parentf6671c5d784e744f18bbc9e06dc4fcdb9e10065b (diff)
Fixed #27632 -- Unified query parameters by their values on Oracle.
-rw-r--r--django/db/backends/oracle/base.py16
-rw-r--r--tests/aggregation_regress/tests.py13
2 files changed, 27 insertions, 2 deletions
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index 2e6f02cc14..68936388d4 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -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))
diff --git a/tests/aggregation_regress/tests.py b/tests/aggregation_regress/tests.py
index 59a40c7176..0229020234 100644
--- a/tests/aggregation_regress/tests.py
+++ b/tests/aggregation_regress/tests.py
@@ -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