[3.1.x] Fixed #31835 -- Dropped support for JSONField __contains lookup on Oracle.

The current implementation works only for basic examples without
supporting nested structures and doesn't follow "the general principle
that the contained object must match the containing object as to
structure and data contents, possibly after discarding some
non-matching array elements or object key/value pairs from the
containing object".
Backport of 02447fb133 from master
This commit is contained in:
Mariusz Felisiak 2020-07-28 11:54:01 +02:00
parent 3116444559
commit 028a5f86f2
3 changed files with 22 additions and 20 deletions

View File

@ -146,24 +146,7 @@ class DataContains(PostgresOperatorLookup):
return 'JSON_CONTAINS(%s, %s)' % (lhs, rhs), params return 'JSON_CONTAINS(%s, %s)' % (lhs, rhs), params
def as_oracle(self, compiler, connection): def as_oracle(self, compiler, connection):
if isinstance(self.rhs, KeyTransform): raise NotSupportedError('contains lookup is not supported on Oracle.')
return HasKey(self.lhs, self.rhs).as_oracle(compiler, connection)
lhs, lhs_params = self.process_lhs(compiler, connection)
params = tuple(lhs_params)
sql = (
"JSON_QUERY(%s, '$%s' WITH WRAPPER) = "
"JSON_QUERY('%s', '$.value' WITH WRAPPER)"
)
rhs = json.loads(self.rhs)
if isinstance(rhs, dict):
if not rhs:
return "DBMS_LOB.SUBSTR(%s) LIKE '{%%%%}'" % lhs, params
return ' AND '.join([
sql % (
lhs, '.%s' % json.dumps(key), json.dumps({'value': value}),
) for key, value in rhs.items()
]), params
return sql % (lhs, '', json.dumps({'value': rhs})), params
class ContainedBy(PostgresOperatorLookup): class ContainedBy(PostgresOperatorLookup):

View File

@ -960,6 +960,10 @@ contained in the top-level of the field. For example::
>>> Dog.objects.filter(data__contains={'breed': 'collie'}) >>> Dog.objects.filter(data__contains={'breed': 'collie'})
<QuerySet [<Dog: Meg>]> <QuerySet [<Dog: Meg>]>
.. admonition:: Oracle
``contains`` is not supported on Oracle.
.. fieldlookup:: jsonfield.contained_by .. fieldlookup:: jsonfield.contained_by
``contained_by`` ``contained_by``

View File

@ -426,6 +426,10 @@ class TestQuerying(TestCase):
[self.objs[3], self.objs[4], self.objs[6]], [self.objs[3], self.objs[4], self.objs[6]],
) )
@skipIf(
connection.vendor == 'oracle',
"Oracle doesn't support contains lookup.",
)
def test_contains(self): def test_contains(self):
tests = [ tests = [
({}, self.objs[2:5] + self.objs[6:8]), ({}, self.objs[2:5] + self.objs[6:8]),
@ -441,6 +445,17 @@ class TestQuerying(TestCase):
qs = NullableJSONModel.objects.filter(value__contains=value) qs = NullableJSONModel.objects.filter(value__contains=value)
self.assertSequenceEqual(qs, expected) self.assertSequenceEqual(qs, expected)
@skipUnless(
connection.vendor == 'oracle',
"Oracle doesn't support contains lookup.",
)
def test_contains_unsupported(self):
msg = 'contains lookup is not supported on Oracle.'
with self.assertRaisesMessage(NotSupportedError, msg):
NullableJSONModel.objects.filter(
value__contains={'baz': {'a': 'b', 'c': 'd'}},
).get()
@skipUnlessDBFeature('supports_primitives_in_json_field') @skipUnlessDBFeature('supports_primitives_in_json_field')
def test_contains_primitives(self): def test_contains_primitives(self):
for value in self.primitives: for value in self.primitives:
@ -647,12 +662,12 @@ class TestQuerying(TestCase):
('value__baz__has_key', 'c'), ('value__baz__has_key', 'c'),
('value__baz__has_keys', ['a', 'c']), ('value__baz__has_keys', ['a', 'c']),
('value__baz__has_any_keys', ['a', 'x']), ('value__baz__has_any_keys', ['a', 'x']),
('value__contains', KeyTransform('bax', 'value')),
('value__has_key', KeyTextTransform('foo', 'value')), ('value__has_key', KeyTextTransform('foo', 'value')),
) )
# contained_by lookup is not supported on Oracle. # contained_by and contains lookups are not supported on Oracle.
if connection.vendor != 'oracle': if connection.vendor != 'oracle':
tests += ( tests += (
('value__contains', KeyTransform('bax', 'value')),
('value__baz__contained_by', {'a': 'b', 'c': 'd', 'e': 'f'}), ('value__baz__contained_by', {'a': 'b', 'c': 'd', 'e': 'f'}),
( (
'value__contained_by', 'value__contained_by',