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
1=== modified file 'openerp/addons/base/test/base_test.yml'
2--- openerp/addons/base/test/base_test.yml 2014-04-16 11:22:39 +0000
3+++ openerp/addons/base/test/base_test.yml 2014-04-25 11:42:41 +0000
4@@ -137,7 +137,19 @@
5 assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
6 assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
7 -
8- 6. Restore pool state after the test
9+ 6. Tests on _parent_order.
10+-
11+ !python {model: res.partner.category }: |
12+ another_root = self.create(cr, uid, {'name':'Root categ'})
13+ struct = self.search(cr, uid, [('id','in',(ref('test_categ_root'),another_root))])
14+ assert struct[0]==another_root, "Root partner categories should be sorted."
15+ struct = self.search(cr, uid, [('id','in',(ref('test_categ_1'),ref('test_categ_2')))])
16+ assert struct[0]==ref('test_categ_1'), "Partner categories should be sorted."
17+ self.write(cr, uid, ref('test_categ_1'), {'name':'Child 3'})
18+ struct = self.search(cr, uid, [('id','in',(ref('test_categ_1'),ref('test_categ_2')))])
19+ assert struct[0]==ref('test_categ_2'), "Partner categories should be re-sorted after name update."
20+-
21+ 7. Restore pool state after the test
22 -
23 !python {model: res.partner.category}: |
24 self.pool._init = True
25
26=== modified file 'openerp/osv/orm.py'
27--- openerp/osv/orm.py 2014-04-07 10:57:40 +0000
28+++ openerp/osv/orm.py 2014-04-25 11:42:41 +0000
29@@ -4173,22 +4173,31 @@
30
31 parents_changed = []
32 parent_order = self._parent_order or self._order
33- if self._parent_store and (self._parent_name in vals):
34+ if self._parent_store:
35 # The parent_left/right computation may take up to
36 # 5 seconds. No need to recompute the values if the
37- # parent is the same.
38+ # parent and order field are the same.
39 # Note: to respect parent_order, nodes must be processed in
40 # order, so ``parents_changed`` must be ordered properly.
41- parent_val = vals[self._parent_name]
42- if parent_val:
43- query = "SELECT id FROM %s WHERE id IN %%s AND (%s != %%s OR %s IS NULL) ORDER BY %s" % \
44- (self._table, self._parent_name, self._parent_name, parent_order)
45- cr.execute(query, (tuple(ids), parent_val))
46- else:
47- query = "SELECT id FROM %s WHERE id IN %%s AND (%s IS NOT NULL) ORDER BY %s" % \
48- (self._table, self._parent_name, parent_order)
49- cr.execute(query, (tuple(ids),))
50- parents_changed = map(operator.itemgetter(0), cr.fetchall())
51+ query = "SELECT id FROM %s WHERE id IN %%s" % (self._table,)
52+ query_params = [tuple(ids)]
53+ query_clause = ""
54+ parent_params = [self._parent_name]
55+ for param in parent_order.split(','):
56+ parent_params.append(param.strip())
57+ for parent_param in parent_params:
58+ if parent_param in vals:
59+ parents_changed = True
60+ parent_val = vals[parent_param]
61+ if parent_val:
62+ query_clause += (query_clause and ' OR ' or '') + "%s != %%s OR %s IS NULL" % (parent_param,parent_param)
63+ query_params.append(parent_val)
64+ else:
65+ query_clause += (query_clause and ' OR ' or '') + "%s IS NOT NULL" % (parent_param,)
66+ if parents_changed:
67+ query += (query_clause and (' AND (' + query_clause + ')') or '') + " ORDER BY %s" % (self._parent_order,)
68+ cr.execute(query, tuple(query_params))
69+ parents_changed = map(operator.itemgetter(0), cr.fetchall())
70
71 upd0 = []
72 upd1 = []
73@@ -4286,18 +4295,16 @@
74 if self.pool._init:
75 self.pool._init_parent[self._name] = True
76 else:
77- order = self._parent_order or self._order
78- parent_val = vals[self._parent_name]
79- if parent_val:
80- clause, params = '%s=%%s' % (self._parent_name,), (parent_val,)
81- else:
82- clause, params = '%s IS NULL' % (self._parent_name,), ()
83-
84 for id in parents_changed:
85- cr.execute('SELECT parent_left, parent_right FROM %s WHERE id=%%s' % (self._table,), (id,))
86- pleft, pright = cr.fetchone()
87+ cr.execute('SELECT parent_left, parent_right, %s FROM %s WHERE id=%%s' % (self._parent_name, self._table,), (id,))
88+ pleft, pright, parent_val = cr.fetchone()
89 distance = pright - pleft + 1
90
91+ if parent_val:
92+ clause, params = '%s=%%s' % (self._parent_name,), (parent_val,)
93+ else:
94+ clause, params = '%s IS NULL' % (self._parent_name,), ()
95+
96 # Positions of current siblings, to locate proper insertion point;
97 # this can _not_ be fetched outside the loop, as it needs to be refreshed
98 # after each update, in case several nodes are sequentially inserted one
99@@ -4517,21 +4524,25 @@
100 self.pool._init_parent[self._name] = True
101 else:
102 parent = vals.get(self._parent_name, False)
103+ where_clause = ' where '+self._parent_name
104 if parent:
105- cr.execute('select parent_right from '+self._table+' where '+self._parent_name+'=%s order by '+(self._parent_order or self._order), (parent,))
106- pleft_old = None
107- result_p = cr.fetchall()
108- for (pleft,) in result_p:
109- if not pleft:
110- break
111- pleft_old = pleft
112- if not pleft_old:
113- cr.execute('select parent_left from '+self._table+' where id=%s', (parent,))
114- pleft_old = cr.fetchone()[0]
115- pleft = pleft_old
116- else:
117- cr.execute('select max(parent_right) from '+self._table)
118- pleft = cr.fetchone()[0] or 0
119+ where_clause += '=%s'
120+ where_params = (parent,)
121+ else:
122+ where_clause += ' is null'
123+ where_params = tuple()
124+ cr.execute('select parent_right from '+self._table + where_clause+' order by '+(self._parent_order or self._order), where_params)
125+ pleft_old = None
126+ result_p = cr.fetchall()
127+ for (pleft,) in result_p:
128+ if not pleft:
129+ break
130+ pleft_old = pleft
131+ if not pleft_old and parent:
132+ cr.execute('select parent_left from '+self._table+' where id=%s', (parent,))
133+ pleft = cr.fetchone()[0]
134+ else:
135+ pleft = pleft_old or -1
136 cr.execute('update '+self._table+' set parent_left=parent_left+2 where parent_left>%s', (pleft,))
137 cr.execute('update '+self._table+' set parent_right=parent_right+2 where parent_right>%s', (pleft,))
138 cr.execute('update '+self._table+' set parent_left=%s,parent_right=%s where id=%s', (pleft+1, pleft+2, id_new))