Merge lp:~cedric-lebrouster/ocb-server/ocb-7.0-bug-1253052-parent-order into lp:ocb-server

Proposed by Cedric Le Brouster(OpenFire)
Status: Rejected
Rejected by: Holger Brunn (Therp)
Proposed branch: lp:~cedric-lebrouster/ocb-server/ocb-7.0-bug-1253052-parent-order
Merge into: lp:ocb-server
Diff against target: 138 lines (+59/-36)
2 files modified
openerp/addons/base/test/base_test.yml (+13/-1)
openerp/osv/orm.py (+46/-35)
To merge this branch: bzr merge lp:~cedric-lebrouster/ocb-server/ocb-7.0-bug-1253052-parent-order
Reviewer Review Type Date Requested Status
Holger Brunn (Therp) Disapprove
Alexandre Fayolle - camptocamp code review, test Approve
Review via email: mp+209708@code.launchpad.net

Description of the change

Fixes parent_left and parent_right recomputation in write/create methods ( bug lp:1253052 )

To post a comment you must log in.
5280. By Cedric Le Brouster(OpenFire)

Add tests for _parent_order

Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

This patch looks a bit scary at first, mainly because you have to read it closely to convince yourself that it doesn't trigger a recomputation on every write().

#123-133 can be done in one SQL statement I think, given that we touch that code anyway such an optimization wouldn't hurt imho.

review: Approve (code review)
Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

This patch breaks the yaml_import tests.

Extract from the logs:

TEST test_ocb70 openerp.tools.yaml_import: 2. Duplicate the parent category and verify that the children have been duplicated too and are below the new parent
ERROR test_ocb70 openerp.tools.yaml_import: AssertionError in Python code : After duplication, previous record must have old childs records only
TEST test_ocb70 openerp.tools.yaml_import: 3. Duplicate the children then reassign them to the new parent (1st method) and check the parent_store structure.
ERROR test_ocb70 openerp.tools.yaml_import: AssertionError in Python code : After duplication, previous record must have old childs records only
TEST test_ocb70 openerp.tools.yaml_import: 4. Duplicate the children then reassign them to the new parent (2nd method) and check the parent_store structure.
ERROR test_ocb70 openerp.tools.yaml_import: AssertionError in Python code : After duplication, previous record must have old childs records only
TEST test_ocb70 openerp.tools.yaml_import: 5. Duplicate the children then reassign them to the new parent (3rd method) and make sure the parent_store structure is still right.
ERROR test_ocb70 openerp.tools.yaml_import: AssertionError in Python code : After duplication, previous record must have old childs records only

Can you please check this, and adapt the tests (which seem to have some assumptions broken by your changes) or fix the code in yaml_import?

review: Needs Fixing (code review, test)
5281. By Cedric Le Brouster(OpenFire)

[FIX] orm: fix miscalculation of _parent_left and _parent_right in create() method for some special case.
(i.e. when new object parent's _parent_left value is 0)

Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

LGTM, thanks for the fix

review: Approve (code review, test)
Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :
Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

Development for 7.0 has moved to github on https://github.com/OCA/ocb - please move your merge proposal there if it is still valid.

(I close and reject this in order to have a cleaner overview for 6.1 MPs which indeed have to be done on launchpad)

review: Disapprove

Unmerged revisions

5281. By Cedric Le Brouster(OpenFire)

[FIX] orm: fix miscalculation of _parent_left and _parent_right in create() method for some special case.
(i.e. when new object parent's _parent_left value is 0)

5280. By Cedric Le Brouster(OpenFire)

Add tests for _parent_order

5279. By Cedric Le Brouster(OpenFire)

[FIX] orm: wrong parent_left and parent_right recomputation in write/create methods (see bug 1253052)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openerp/addons/base/test/base_test.yml'
--- openerp/addons/base/test/base_test.yml 2014-04-16 11:22:39 +0000
+++ openerp/addons/base/test/base_test.yml 2014-04-25 11:42:41 +0000
@@ -137,7 +137,19 @@
137 assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"137 assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
138 assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"138 assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
139-139-
140 6. Restore pool state after the test140 6. Tests on _parent_order.
141-
142 !python {model: res.partner.category }: |
143 another_root = self.create(cr, uid, {'name':'Root categ'})
144 struct = self.search(cr, uid, [('id','in',(ref('test_categ_root'),another_root))])
145 assert struct[0]==another_root, "Root partner categories should be sorted."
146 struct = self.search(cr, uid, [('id','in',(ref('test_categ_1'),ref('test_categ_2')))])
147 assert struct[0]==ref('test_categ_1'), "Partner categories should be sorted."
148 self.write(cr, uid, ref('test_categ_1'), {'name':'Child 3'})
149 struct = self.search(cr, uid, [('id','in',(ref('test_categ_1'),ref('test_categ_2')))])
150 assert struct[0]==ref('test_categ_2'), "Partner categories should be re-sorted after name update."
151-
152 7. Restore pool state after the test
141-153-
142 !python {model: res.partner.category}: |154 !python {model: res.partner.category}: |
143 self.pool._init = True155 self.pool._init = True
144156
=== modified file 'openerp/osv/orm.py'
--- openerp/osv/orm.py 2014-04-07 10:57:40 +0000
+++ openerp/osv/orm.py 2014-04-25 11:42:41 +0000
@@ -4173,22 +4173,31 @@
41734173
4174 parents_changed = []4174 parents_changed = []
4175 parent_order = self._parent_order or self._order4175 parent_order = self._parent_order or self._order
4176 if self._parent_store and (self._parent_name in vals):4176 if self._parent_store:
4177 # The parent_left/right computation may take up to4177 # The parent_left/right computation may take up to
4178 # 5 seconds. No need to recompute the values if the4178 # 5 seconds. No need to recompute the values if the
4179 # parent is the same.4179 # parent and order field are the same.
4180 # Note: to respect parent_order, nodes must be processed in4180 # Note: to respect parent_order, nodes must be processed in
4181 # order, so ``parents_changed`` must be ordered properly.4181 # order, so ``parents_changed`` must be ordered properly.
4182 parent_val = vals[self._parent_name]4182 query = "SELECT id FROM %s WHERE id IN %%s" % (self._table,)
4183 if parent_val:4183 query_params = [tuple(ids)]
4184 query = "SELECT id FROM %s WHERE id IN %%s AND (%s != %%s OR %s IS NULL) ORDER BY %s" % \4184 query_clause = ""
4185 (self._table, self._parent_name, self._parent_name, parent_order)4185 parent_params = [self._parent_name]
4186 cr.execute(query, (tuple(ids), parent_val))4186 for param in parent_order.split(','):
4187 else:4187 parent_params.append(param.strip())
4188 query = "SELECT id FROM %s WHERE id IN %%s AND (%s IS NOT NULL) ORDER BY %s" % \4188 for parent_param in parent_params:
4189 (self._table, self._parent_name, parent_order)4189 if parent_param in vals:
4190 cr.execute(query, (tuple(ids),))4190 parents_changed = True
4191 parents_changed = map(operator.itemgetter(0), cr.fetchall())4191 parent_val = vals[parent_param]
4192 if parent_val:
4193 query_clause += (query_clause and ' OR ' or '') + "%s != %%s OR %s IS NULL" % (parent_param,parent_param)
4194 query_params.append(parent_val)
4195 else:
4196 query_clause += (query_clause and ' OR ' or '') + "%s IS NOT NULL" % (parent_param,)
4197 if parents_changed:
4198 query += (query_clause and (' AND (' + query_clause + ')') or '') + " ORDER BY %s" % (self._parent_order,)
4199 cr.execute(query, tuple(query_params))
4200 parents_changed = map(operator.itemgetter(0), cr.fetchall())
41924201
4193 upd0 = []4202 upd0 = []
4194 upd1 = []4203 upd1 = []
@@ -4286,18 +4295,16 @@
4286 if self.pool._init:4295 if self.pool._init:
4287 self.pool._init_parent[self._name] = True4296 self.pool._init_parent[self._name] = True
4288 else:4297 else:
4289 order = self._parent_order or self._order
4290 parent_val = vals[self._parent_name]
4291 if parent_val:
4292 clause, params = '%s=%%s' % (self._parent_name,), (parent_val,)
4293 else:
4294 clause, params = '%s IS NULL' % (self._parent_name,), ()
4295
4296 for id in parents_changed:4298 for id in parents_changed:
4297 cr.execute('SELECT parent_left, parent_right FROM %s WHERE id=%%s' % (self._table,), (id,))4299 cr.execute('SELECT parent_left, parent_right, %s FROM %s WHERE id=%%s' % (self._parent_name, self._table,), (id,))
4298 pleft, pright = cr.fetchone()4300 pleft, pright, parent_val = cr.fetchone()
4299 distance = pright - pleft + 14301 distance = pright - pleft + 1
43004302
4303 if parent_val:
4304 clause, params = '%s=%%s' % (self._parent_name,), (parent_val,)
4305 else:
4306 clause, params = '%s IS NULL' % (self._parent_name,), ()
4307
4301 # Positions of current siblings, to locate proper insertion point;4308 # Positions of current siblings, to locate proper insertion point;
4302 # this can _not_ be fetched outside the loop, as it needs to be refreshed4309 # this can _not_ be fetched outside the loop, as it needs to be refreshed
4303 # after each update, in case several nodes are sequentially inserted one4310 # after each update, in case several nodes are sequentially inserted one
@@ -4517,21 +4524,25 @@
4517 self.pool._init_parent[self._name] = True4524 self.pool._init_parent[self._name] = True
4518 else:4525 else:
4519 parent = vals.get(self._parent_name, False)4526 parent = vals.get(self._parent_name, False)
4527 where_clause = ' where '+self._parent_name
4520 if parent:4528 if parent:
4521 cr.execute('select parent_right from '+self._table+' where '+self._parent_name+'=%s order by '+(self._parent_order or self._order), (parent,))4529 where_clause += '=%s'
4522 pleft_old = None4530 where_params = (parent,)
4523 result_p = cr.fetchall()4531 else:
4524 for (pleft,) in result_p:4532 where_clause += ' is null'
4525 if not pleft:4533 where_params = tuple()
4526 break4534 cr.execute('select parent_right from '+self._table + where_clause+' order by '+(self._parent_order or self._order), where_params)
4527 pleft_old = pleft4535 pleft_old = None
4528 if not pleft_old:4536 result_p = cr.fetchall()
4529 cr.execute('select parent_left from '+self._table+' where id=%s', (parent,))4537 for (pleft,) in result_p:
4530 pleft_old = cr.fetchone()[0]4538 if not pleft:
4531 pleft = pleft_old4539 break
4532 else:4540 pleft_old = pleft
4533 cr.execute('select max(parent_right) from '+self._table)4541 if not pleft_old and parent:
4534 pleft = cr.fetchone()[0] or 04542 cr.execute('select parent_left from '+self._table+' where id=%s', (parent,))
4543 pleft = cr.fetchone()[0]
4544 else:
4545 pleft = pleft_old or -1
4535 cr.execute('update '+self._table+' set parent_left=parent_left+2 where parent_left>%s', (pleft,))4546 cr.execute('update '+self._table+' set parent_left=parent_left+2 where parent_left>%s', (pleft,))
4536 cr.execute('update '+self._table+' set parent_right=parent_right+2 where parent_right>%s', (pleft,))4547 cr.execute('update '+self._table+' set parent_right=parent_right+2 where parent_right>%s', (pleft,))
4537 cr.execute('update '+self._table+' set parent_left=%s,parent_right=%s where id=%s', (pleft+1, pleft+2, id_new))4548 cr.execute('update '+self._table+' set parent_left=%s,parent_right=%s where id=%s', (pleft+1, pleft+2, id_new))