From ae6593cfb592426cbe18c32c124d32a373b9f56f Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 23 Dec 2010 13:12:41 +0200 Subject: [PATCH 1/3] base/test: Tests form many2one fields, empty lists bzr revid: p_christ@hol.gr-20101223111241-j18dlhemjddvcoh2 --- bin/addons/base/test/test_osv_expression.yml | 69 +++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/bin/addons/base/test/test_osv_expression.yml b/bin/addons/base/test/test_osv_expression.yml index 71488597579..966906164c0 100644 --- a/bin/addons/base/test/test_osv_expression.yml +++ b/bin/addons/base/test/test_osv_expression.yml @@ -43,4 +43,71 @@ - Filtering on invalid value across x2many relationship should return an empty set - - !assert {model: res.partner, search: "[('address.city','=','foo')]", count: 0, string: "Searching for address.city = foo should give empty results"} \ No newline at end of file + !assert {model: res.partner, search: "[('address.city','=','foo')]", count: 0, string: "Searching for address.city = foo should give empty results"} +- + Check if many2one works with empty search list +- + !assert {model: res.partner, search: "[('company_id','in', [])]", count: 0, string: "Searching for company_id in [] should be empty!" } +- + For the sake of the following tests, I will create a second company +- + !record {model: res.company, id: ymltest_company2}: + name: Acme 2 +- + And create a few partners with that company or no company +- + !python {model: res.partner }: | + for r in range(4): + self.create(cr, uid, { 'name': 'P of Acme %d' % r, + 'company_id': ref('ymltest_company2') }) + for r in range(4): + self.create(cr, uid, { 'name': 'P of All %d' % r, + 'company_id': False }) +- + Check if many2one works with negative empty list +- + !python {model: res.partner }: | + all_ids = self.search(cr, uid, []) + all_ids.sort() + res_ids = self.search(cr, uid,['|',('company_id','not in', []), ('company_id','=',False)]) + res_ids.sort() + assert all_ids == res_ids, "not in [] fails" +- + Check that many2one will pick the correct records with a list +- + !python {model: res.partner }: | + res_ids = self.search(cr, uid, [('company_id', 'in', [False,])]) + assert len(res_ids) >= 4, "We created 4 partners w/company, why find %d? %r" % \ + (len(res_ids), res_ids) +- + Check that many2one will exclude the correct records with a list +- + !python {model: res.partner }: | + # assuming that the default company is #1 + res_ids = self.search(cr, uid, [('company_id', 'not in', [1])]) + assert len(res_ids) >= 4, "We should have found 4 records at least, only have %d! %r" % \ + (len(res_ids), res_ids) +- + Check that we exclude the correct records, + False +- + !python {model: res.partner }: | + # assuming that the default company is #1 + res_ids = self.search(cr, uid, ['|', ('company_id', 'not in', [1]), ('company_id', '=', False)]) + assert len(res_ids) >= 8, "We should have found 8 records at least, only have %d! %r" % \ + (len(res_ids), res_ids) +- + Check that multi-level expressions also work +- + !python {model: res.partner }: | + res_ids = self.search(cr, uid, [('company_id.partner_id', 'in', [])]) + assert res_ids == [], "Searching an empty set should return empty result, not %r" % res_ids +- + Check that multi-level expressions with negative op work +- + !python {model: res.partner }: | + all_ids = self.search(cr, uid, [('company_id', '!=', False)]) + all_ids.sort() + res_ids = self.search(cr, uid, [('company_id.partner_id', 'not in', [])]) + res_ids.sort() + assert res_ids == all_ids, "Searching against empty set failed, returns %r" % res_ids + From cc64d52b33cdaba5a2e3563525bc6b189251eea2 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 23 Dec 2010 13:29:38 +0200 Subject: [PATCH 2/3] expression: fix m2o handling of empty lists [Bug 598454] The domain expressions wouldn't yield the expected results in the following cases (eg. for res.partner): [('company_id', 'in', [])] : should return [], not all partners [('company_id.partner_id', 'in', [])] : again, empty set After some deliberation and discussion, we *change the behavior* for them. Notes: 1. This comes after commit 913e95856f80, bug 626806 which solved the corresponding cases for m2m, o2m 2. This commit _will_ have side-effects. The result set for domains now is indeed different. But, we believe, now it is the proper one, fixes cases of unexpected results that have been reported 3. It makes the server a bit faster, because empty results are quick to compute 4. The "not in []" operator will NOT return results with NULL values in the corresponding field (aka. empty). This may not be the one expected by users, but it follows the SQL standard (par. 9.20.3): http://www.postgresql.org/docs/9.0/static/functions-subquery.html bzr revid: p_christ@hol.gr-20101223112938-ore0dmmasekdcyfn --- bin/osv/expression.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/bin/osv/expression.py b/bin/osv/expression.py index 3281fd29f7c..cbb6a0f985b 100644 --- a/bin/osv/expression.py +++ b/bin/osv/expression.py @@ -143,12 +143,18 @@ class expression(object): if len(fargs) > 1: if field._type == 'many2one': right = field_obj.search(cr, uid, [(fargs[1], operator, right)], context=context) - self.__exp[i] = (fargs[0], 'in', right) + if right == []: + self.__exp[i] = ( 'id', '=', 0 ) + else: + self.__exp[i] = (fargs[0], 'in', right) # Making search easier when there is a left operand as field.o2m or field.m2m if field._type in ['many2many','one2many']: right = field_obj.search(cr, uid, [(fargs[1], operator, right)], context=context) right1 = table.search(cr, uid, [(fargs[0],'in', right)], context=context) - self.__exp[i] = ('id', 'in', right1) + if right1 == []: + self.__exp[i] = ( 'id', '=', 0 ) + else: + self.__exp[i] = ('id', 'in', right1) if not isinstance(field,fields.property): continue @@ -313,6 +319,13 @@ class expression(object): if not isinstance(ele, basestring): m2o_str = False break + elif right == []: + m2o_str = False + if operator in ('not in', '!=', '<>'): + # (many2one not in []) should return all records + self.__exp[i] = self.__DUMMY_LEAF + else: + self.__exp[i] = ('id','=',0) else: new_op = '=' if operator in ['not like','not ilike','not in','<>','!=']: From 013243e70809cf5c94cc75018ad4fae3b9570955 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 29 Dec 2010 18:48:51 +0100 Subject: [PATCH 3/3] [FIX] addons: make sure base demo and tests are reloaded as well upon update bzr revid: odo@openerp.com-20101229174851-zcflitp2ht2xl52o --- bin/addons/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index 82bab896fbf..29f81f97928 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -819,6 +819,8 @@ def load_modules(db, force_demo=False, status=None, update_module=False): report = tools.assertion_report() # NOTE: Try to also load the modules that have been marked as uninstallable previously... STATES_TO_LOAD = ['installed', 'to upgrade', 'uninstallable'] + if 'base' in tools.config['update']: + cr.execute("update ir_module_module set state=%s where name=%s", ('to upgrade', 'base')) graph = create_graph(cr, ['base'], force) if not graph: logger.notifyChannel('init', netsvc.LOG_CRITICAL, 'module base cannot be loaded! (hint: verify addons-path)')