[3.1.x] Refs #32096 -- Fixed __in lookup crash against key transforms for JSONField.

Regression in 6789ded0a6 and
1251772cb8.

Thanks Simon Charette and Igor Jerosimić for the report.

Backport of 7e1e198494 from master
This commit is contained in:
Mariusz Felisiak 2020-10-13 11:35:55 +02:00
parent d94e777b66
commit 59fe0b8541
4 changed files with 33 additions and 20 deletions

View File

@ -369,27 +369,26 @@ class KeyTransformIsNull(lookups.IsNull):
class KeyTransformIn(lookups.In): class KeyTransformIn(lookups.In):
def process_rhs(self, compiler, connection): def resolve_expression_parameter(self, compiler, connection, sql, param):
rhs, rhs_params = super().process_rhs(compiler, connection) sql, params = super().resolve_expression_parameter(
if not connection.features.has_native_json_field: compiler, connection, sql, param,
func = () )
if (
not hasattr(param, 'as_sql') and
not connection.features.has_native_json_field
):
if connection.vendor == 'oracle': if connection.vendor == 'oracle':
func = [] value = json.loads(param)
for value in rhs_params: if isinstance(value, (list, dict)):
value = json.loads(value) sql = "JSON_QUERY(%s, '$.value')"
function = 'JSON_QUERY' if isinstance(value, (list, dict)) else 'JSON_VALUE' else:
func.append("%s('%s', '$.value')" % ( sql = "JSON_VALUE(%s, '$.value')"
function, params = (json.dumps({'value': value}),)
json.dumps({'value': value}),
))
func = tuple(func)
rhs_params = ()
elif connection.vendor == 'mysql' and connection.mysql_is_mariadb:
func = ("JSON_UNQUOTE(JSON_EXTRACT(%s, '$'))",) * len(rhs_params)
elif connection.vendor in {'sqlite', 'mysql'}: elif connection.vendor in {'sqlite', 'mysql'}:
func = ("JSON_EXTRACT(%s, '$')",) * len(rhs_params) sql = "JSON_EXTRACT(%s, '$')"
rhs = rhs % func if connection.vendor == 'mysql' and connection.mysql_is_mariadb:
return rhs, rhs_params sql = 'JSON_UNQUOTE(%s)' % sql
return sql, params
class KeyTransformExact(JSONExact): class KeyTransformExact(JSONExact):

View File

@ -241,7 +241,7 @@ class FieldGetDbPrepValueIterableMixin(FieldGetDbPrepValueMixin):
if hasattr(param, 'resolve_expression'): if hasattr(param, 'resolve_expression'):
param = param.resolve_expression(compiler.query) param = param.resolve_expression(compiler.query)
if hasattr(param, 'as_sql'): if hasattr(param, 'as_sql'):
sql, params = param.as_sql(compiler, connection) sql, params = compiler.compile(param)
return sql, params return sql, params
def batch_process_rhs(self, compiler, connection, rhs=None): def batch_process_rhs(self, compiler, connection, rhs=None):

View File

@ -25,3 +25,7 @@ Bugfixes
:class:`~django.contrib.postgres.aggregates.ArrayAgg` and :class:`~django.contrib.postgres.aggregates.ArrayAgg` and
:class:`~django.contrib.postgres.aggregates.StringAgg` with ``ordering`` :class:`~django.contrib.postgres.aggregates.StringAgg` with ``ordering``
on key transforms for :class:`~django.db.models.JSONField` (:ticket:`32096`). on key transforms for :class:`~django.db.models.JSONField` (:ticket:`32096`).
* Fixed a regression in Django 3.1 that caused a crash of ``__in`` lookup when
using key transforms for :class:`~django.db.models.JSONField` in the lookup
value (:ticket:`32096`).

View File

@ -631,6 +631,16 @@ class TestQuerying(TestCase):
('value__0__in', [1], [self.objs[5]]), ('value__0__in', [1], [self.objs[5]]),
('value__0__in', [1, 3], [self.objs[5]]), ('value__0__in', [1, 3], [self.objs[5]]),
('value__foo__in', ['bar'], [self.objs[7]]), ('value__foo__in', ['bar'], [self.objs[7]]),
(
'value__foo__in',
[KeyTransform('foo', KeyTransform('bax', 'value'))],
[self.objs[7]],
),
(
'value__foo__in',
[KeyTransform('foo', KeyTransform('bax', 'value')), 'baz'],
[self.objs[7]],
),
('value__foo__in', ['bar', 'baz'], [self.objs[7]]), ('value__foo__in', ['bar', 'baz'], [self.objs[7]]),
('value__bar__in', [['foo', 'bar']], [self.objs[7]]), ('value__bar__in', [['foo', 'bar']], [self.objs[7]]),
('value__bar__in', [['foo', 'bar'], ['a']], [self.objs[7]]), ('value__bar__in', [['foo', 'bar'], ['a']], [self.objs[7]]),