[FIX] expression: fix search for o2m with non existing id

The result of searching on a o2m field for a missing ID was
the whole set of records which do not have any lines in the
o2m fields. This is definitely not the desired behavior,
and could lead to disatrous performance, because the
returned set could be extremely large.

One example of such behavior is for recomputing fields
in the env cache in 8.0+. When a o2m line gets deleted,
it triggers the recompute of any dependent fields.

In order to locate the records to recompute, the ORM
searches for the 'parent' records in the comodel table.
When this operation is done by 2 users concurrently
the o2m line may not exist anymore, and the bug
is triggered: dependent fields are recomputed on a possibly
very large set of records that did not need any recompute!
This commit is contained in:
Cedric Snauwaert 2016-01-12 17:58:14 +01:00 committed by Olivier Dony
parent 5b4d303d05
commit 4de3f4c4ba
2 changed files with 12 additions and 0 deletions

View File

@ -423,11 +423,19 @@
u1b = res_users.create(cr, uid, {'login': 'dbo2', 'partner_id': p1})
u2 = res_users.create(cr, uid, {'login': 'rpo', 'partner_id': p2})
assert [p1] == res_partner.search(cr, uid, [('user_ids', 'in', u1a)]), "o2m IN accept single int on right side"
assert [p1] == res_partner.search(cr, uid, [('user_ids', '=', 'Dédé Boitaclou')]), "o2m NOT IN matches none on the right side"
assert [] == res_partner.search(cr, uid, [('user_ids', 'in', [10000])]), "o2m NOT IN matches none on the right side"
assert [p1,p2] == res_partner.search(cr, uid, [('user_ids', 'in', [u1a,u2])]), "o2m IN matches any on the right side"
all_partners = res_partner.search(cr, uid, [])
assert (set(all_partners) - set([p1])) == set(res_partner.search(cr, uid, [('user_ids', 'not in', u1a)])), "o2m NOT IN matches none on the right side"
# For 8.0: assert (set(all_partners) - set([p1])) == set(res_partner.search(cr, uid, [('user_ids', '!=', 'Dédé Boitaclou')])), "o2m NOT IN matches none on the right side"
assert (set(all_partners) - set([p1,p2])) == set(res_partner.search(cr, uid, [('user_ids', 'not in', [u1b, u2])])), "o2m NOT IN matches none on the right side"
# child_of x returns x and its children (direct or not).
company = self.browse(cr, uid, ref('ymltest_company3'))
expected = [ref('ymltest_company3'), ref('ymltest_company4')]

View File

@ -930,6 +930,10 @@ class expression(object):
call_null = False
o2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
push(create_substitution_leaf(leaf, ('id', o2m_op, ids2), working_model))
elif operator in ('like', 'ilike', 'in', '='):
# no match found with positive search operator => no result (FALSE_LEAF)
call_null = False
push(create_substitution_leaf(leaf, FALSE_LEAF, working_model))
if call_null:
o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in'