From adc49e76fcec6490e321d87657eda5cea0293da7 Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Mon, 16 Dec 2019 17:22:07 +0000 Subject: [PATCH] [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) --- odoo/addons/base/tests/test_expression.py | 31 +++++++++++++++++++++++ odoo/osv/expression.py | 8 +++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/odoo/addons/base/tests/test_expression.py b/odoo/addons/base/tests/test_expression.py index e8cf33ef24c..2a7df364cc6 100644 --- a/odoo/addons/base/tests/test_expression.py +++ b/odoo/addons/base/tests/test_expression.py @@ -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'] diff --git a/odoo/osv/expression.py b/odoo/osv/expression.py index e9efe04fc96..35c66c7f923 100644 --- a/odoo/osv/expression.py +++ b/odoo/osv/expression.py @@ -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 + '%')]