[FIX] expression.py: avoid using TRUE_DOMAIN/FALSE_DOMAIN

Ensure that expression.OR and expression.AND and some other
expression.py methods do not propagate or rely on TRUE_DOMAIN and
FALSE_DOMAIN that may be muted on some instance.

For example, if we did:

  self.search(expression.OR([]))

then in the search method we do something like:

  received_domain.append(('res_field', '=', False))

before this commit, FALSE_DOMAIN would be altered for any succeeding code
that try to use it in `[(0, '=', 1), ('res_field', '=', 'False')]`.

Without the changeset, the added test would fail with:

    [(1, '=', 1), ('id', '=', 1)] != [(1, '=', 1)]
    [(0, '=', 1), ('id', '=', 1)] != [(0, '=', 1)]
    [(0, '=', 1), ('id', '=', 1)] != [(0, '=', 1)]
    [(1, '=', 1), ('id', '=', 1)] != [(1, '=', 1)]
    [(1, '=', 1), ('id', '=', 1)] != [(1, '=', 1)]

note: another commit referenced in #41968 should make the TRUE_DOMAIN
and FALSE_DOMAIN immutable.

related to work on opw-2154448
closes #42107

closes odoo/odoo#42222

X-original-commit: 303ce32e6ccd7432b96ffae545e4a6eced68ae84
Signed-off-by: Nicolas Lempereur (nle) <nle@odoo.com>
This commit is contained in:
Nicolas Lempereur
2019-12-19 20:35:08 +00:00
parent 705b2faa69
commit adc49e76fc
2 changed files with 35 additions and 4 deletions
+31
View File
@@ -604,6 +604,37 @@ class TestExpression(SavepointCaseWithUserDemo):
self.assertNotIn(helene, Company.search([('name','not ilike','Helene')]))
self.assertNotIn(helene, Company.search([('name','not ilike','hélène')]))
def test_pure_function(self):
orig_false = expression.FALSE_DOMAIN.copy()
orig_true = expression.TRUE_DOMAIN.copy()
false = orig_false.copy()
true = orig_true.copy()
domain = expression.AND([])
domain += [('id', '=', 1)]
domain = expression.AND([])
self.assertEqual(domain, orig_true)
domain = expression.AND([false])
domain += [('id', '=', 1)]
domain = expression.AND([false])
self.assertEqual(domain, orig_false)
domain = expression.OR([])
domain += [('id', '=', 1)]
domain = expression.OR([])
self.assertEqual(domain, orig_false)
domain = expression.OR([true])
domain += [('id', '=', 1)]
domain = expression.OR([true])
self.assertEqual(domain, orig_true)
domain = expression.normalize_domain([])
domain += [('id', '=', 1)]
domain = expression.normalize_domain([])
self.assertEqual(domain, orig_true)
def test_like_wildcards(self):
# check that =like/=ilike expressions are working on an untranslated field
Partner = self.env['res.partner']
+4 -4
View File
@@ -191,7 +191,7 @@ def normalize_domain(domain):
"""
assert isinstance(domain, (list, tuple)), "Domains to normalize must have a 'domain' form: a list or tuple of domain components"
if not domain:
return TRUE_DOMAIN
return [TRUE_LEAF]
result = []
expected = 1 # expected number of expressions
op_arity = {NOT_OPERATOR: 1, AND_OPERATOR: 2, OR_OPERATOR: 2}
@@ -269,12 +269,12 @@ def combine(operator, unit, zero, domains):
def AND(domains):
"""AND([D1,D2,...]) returns a domain representing D1 and D2 and ... """
return combine(AND_OPERATOR, TRUE_DOMAIN, FALSE_DOMAIN, domains)
return combine(AND_OPERATOR, [TRUE_LEAF], [FALSE_LEAF], domains)
def OR(domains):
"""OR([D1,D2,...]) returns a domain representing D1 or D2 or ... """
return combine(OR_OPERATOR, FALSE_DOMAIN, TRUE_DOMAIN, domains)
return combine(OR_OPERATOR, [FALSE_LEAF], [TRUE_LEAF], domains)
def distribute_not(domain):
@@ -752,7 +752,7 @@ class expression(object):
either as a range using the parent_path tree lookup field
(when available), or as an expanded [(left,in,child_ids)] """
if not ids:
return FALSE_DOMAIN
return [FALSE_LEAF]
if left_model._parent_store:
doms = OR([
[('parent_path', '=like', rec.parent_path + '%')]