MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DbtuxTree.cpp
1 /*
2  Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 #define DBTUX_TREE_CPP
19 #include "Dbtux.hpp"
20 
21 /*
22  * Add entry. Handle the case when there is room for one more. This
23  * is the common case given slack in nodes.
24  */
25 void
26 Dbtux::treeAdd(TuxCtx& ctx, Frag& frag, TreePos treePos, TreeEnt ent)
27 {
28  TreeHead& tree = frag.m_tree;
29  NodeHandle node(frag);
30  do {
31  if (treePos.m_loc != NullTupLoc) {
32  // non-empty tree
33  thrjam(ctx.jamBuffer);
34  selectNode(node, treePos.m_loc);
35  unsigned pos = treePos.m_pos;
36  if (node.getOccup() < tree.m_maxOccup) {
37  // node has room
38  thrjam(ctx.jamBuffer);
39  nodePushUp(ctx, node, pos, ent, RNIL);
40  break;
41  }
42  treeAddFull(ctx, frag, node, pos, ent);
43  break;
44  }
45  thrjam(ctx.jamBuffer);
46  insertNode(node);
47  nodePushUp(ctx, node, 0, ent, RNIL);
48  node.setSide(2);
49  tree.m_root = node.m_loc;
50  break;
51  } while (0);
52 }
53 
54 /*
55  * Add entry when node is full. Handle the case when there is g.l.b
56  * node in left subtree with room for one more. It will receive the min
57  * entry of this node. The min entry could be the entry to add.
58  */
59 void
60 Dbtux::treeAddFull(TuxCtx& ctx, Frag& frag, NodeHandle lubNode, unsigned pos, TreeEnt ent)
61 {
62  TreeHead& tree = frag.m_tree;
63  TupLoc loc = lubNode.getLink(0);
64  if (loc != NullTupLoc) {
65  // find g.l.b node
66  NodeHandle glbNode(frag);
67  do {
68  thrjam(ctx.jamBuffer);
69  selectNode(glbNode, loc);
70  loc = glbNode.getLink(1);
71  } while (loc != NullTupLoc);
72  if (glbNode.getOccup() < tree.m_maxOccup) {
73  // g.l.b node has room
74  thrjam(ctx.jamBuffer);
75  Uint32 scanList = RNIL;
76  if (pos != 0) {
77  thrjam(ctx.jamBuffer);
78  // add the new entry and return min entry
79  nodePushDown(ctx, lubNode, pos - 1, ent, scanList);
80  }
81  // g.l.b node receives min entry from l.u.b node
82  nodePushUp(ctx, glbNode, glbNode.getOccup(), ent, scanList);
83  return;
84  }
85  treeAddNode(ctx, frag, lubNode, pos, ent, glbNode, 1);
86  return;
87  }
88  treeAddNode(ctx, frag, lubNode, pos, ent, lubNode, 0);
89 }
90 
91 /*
92  * Add entry when there is no g.l.b node in left subtree or the g.l.b
93  * node is full. We must add a new left or right child node which
94  * becomes the new g.l.b node.
95  */
96 void
97 Dbtux::treeAddNode(TuxCtx& ctx,
98  Frag& frag, NodeHandle lubNode, unsigned pos, TreeEnt ent, NodeHandle parentNode, unsigned i)
99 {
100  NodeHandle glbNode(frag);
101  insertNode(glbNode);
102  // connect parent and child
103  parentNode.setLink(i, glbNode.m_loc);
104  glbNode.setLink(2, parentNode.m_loc);
105  glbNode.setSide(i);
106  Uint32 scanList = RNIL;
107  if (pos != 0) {
108  thrjam(ctx.jamBuffer);
109  // add the new entry and return min entry
110  nodePushDown(ctx, lubNode, pos - 1, ent, scanList);
111  }
112  // g.l.b node receives min entry from l.u.b node
113  nodePushUp(ctx, glbNode, 0, ent, scanList);
114  // re-balance the tree
115  treeAddRebalance(ctx, frag, parentNode, i);
116 }
117 
118 /*
119  * Re-balance tree after adding a node. The process starts with the
120  * parent of the added node.
121  */
122 void
123 Dbtux::treeAddRebalance(TuxCtx & ctx, Frag& frag, NodeHandle node, unsigned i)
124 {
125  while (true) {
126  // height of subtree i has increased by 1
127  int j = (i == 0 ? -1 : +1);
128  int b = node.getBalance();
129  if (b == 0) {
130  // perfectly balanced
131  thrjam(ctx.jamBuffer);
132  node.setBalance(j);
133  // height change propagates up
134  } else if (b == -j) {
135  // height of shorter subtree increased
136  thrjam(ctx.jamBuffer);
137  node.setBalance(0);
138  // height of tree did not change - done
139  break;
140  } else if (b == j) {
141  // height of longer subtree increased
142  thrjam(ctx.jamBuffer);
143  NodeHandle childNode(frag);
144  selectNode(childNode, node.getLink(i));
145  int b2 = childNode.getBalance();
146  if (b2 == b) {
147  thrjam(ctx.jamBuffer);
148  treeRotateSingle(ctx, frag, node, i);
149  } else if (b2 == -b) {
150  thrjam(ctx.jamBuffer);
151  treeRotateDouble(ctx, frag, node, i);
152  } else {
153  // height of subtree increased so it cannot be perfectly balanced
154  ndbrequire(false);
155  }
156  // height of tree did not increase - done
157  break;
158  } else {
159  ndbrequire(false);
160  }
161  TupLoc parentLoc = node.getLink(2);
162  if (parentLoc == NullTupLoc) {
163  thrjam(ctx.jamBuffer);
164  // root node - done
165  break;
166  }
167  i = node.getSide();
168  selectNode(node, parentLoc);
169  }
170 }
171 
172 /*
173  * Remove entry. Optimize for nodes with slack. Handle the case when
174  * there is no underflow i.e. occupancy remains at least minOccup. For
175  * interior nodes this is a requirement. For others it means that we do
176  * not need to consider merge of semi-leaf and leaf.
177  */
178 void
179 Dbtux::treeRemove(Frag& frag, TreePos treePos)
180 {
181  TreeHead& tree = frag.m_tree;
182  unsigned pos = treePos.m_pos;
183  NodeHandle node(frag);
184  selectNode(node, treePos.m_loc);
185  TreeEnt ent;
186  do {
187  if (node.getOccup() > tree.m_minOccup) {
188  // no underflow in any node type
189  jam();
190  nodePopDown(c_ctx, node, pos, ent, 0);
191  break;
192  }
193  if (node.getChilds() == 2) {
194  // underflow in interior node
195  jam();
196  treeRemoveInner(frag, node, pos);
197  break;
198  }
199  // remove entry in semi/leaf
200  nodePopDown(c_ctx, node, pos, ent, 0);
201  if (node.getLink(0) != NullTupLoc) {
202  jam();
203  treeRemoveSemi(frag, node, 0);
204  break;
205  }
206  if (node.getLink(1) != NullTupLoc) {
207  jam();
208  treeRemoveSemi(frag, node, 1);
209  break;
210  }
211  treeRemoveLeaf(frag, node);
212  break;
213  } while (0);
214 }
215 
216 /*
217  * Remove entry when interior node underflows. There is g.l.b node in
218  * left subtree to borrow an entry from. The max entry of the g.l.b
219  * node becomes the min entry of this node.
220  */
221 void
222 Dbtux::treeRemoveInner(Frag& frag, NodeHandle lubNode, unsigned pos)
223 {
224  TreeEnt ent;
225  // find g.l.b node
226  NodeHandle glbNode(frag);
227  TupLoc loc = lubNode.getLink(0);
228  do {
229  jam();
230  selectNode(glbNode, loc);
231  loc = glbNode.getLink(1);
232  } while (loc != NullTupLoc);
233  // borrow max entry from semi/leaf
234  Uint32 scanList = RNIL;
235  nodePopDown(c_ctx, glbNode, glbNode.getOccup() - 1, ent, &scanList);
236  // g.l.b may be empty now
237  // a descending scan may try to enter the empty g.l.b
238  // we prevent this in scanNext
239  nodePopUp(c_ctx, lubNode, pos, ent, scanList);
240  if (glbNode.getLink(0) != NullTupLoc) {
241  jam();
242  treeRemoveSemi(frag, glbNode, 0);
243  return;
244  }
245  treeRemoveLeaf(frag, glbNode);
246 }
247 
248 /*
249  * Handle semi-leaf after removing an entry. Move entries from leaf to
250  * semi-leaf to bring semi-leaf occupancy above minOccup, if possible.
251  * The leaf may become empty.
252  */
253 void
254 Dbtux::treeRemoveSemi(Frag& frag, NodeHandle semiNode, unsigned i)
255 {
256  TreeHead& tree = frag.m_tree;
257  ndbrequire(semiNode.getChilds() < 2);
258  TupLoc leafLoc = semiNode.getLink(i);
259  NodeHandle leafNode(frag);
260  selectNode(leafNode, leafLoc);
261  if (semiNode.getOccup() < tree.m_minOccup) {
262  jam();
263  unsigned cnt = min(leafNode.getOccup(), tree.m_minOccup - semiNode.getOccup());
264  nodeSlide(c_ctx, semiNode, leafNode, cnt, i);
265  if (leafNode.getOccup() == 0) {
266  // remove empty leaf
267  jam();
268  treeRemoveNode(frag, leafNode);
269  }
270  }
271 }
272 
273 /*
274  * Handle leaf after removing an entry. If parent is semi-leaf, move
275  * entries to it as in the semi-leaf case. If parent is interior node,
276  * do nothing.
277  */
278 void
279 Dbtux::treeRemoveLeaf(Frag& frag, NodeHandle leafNode)
280 {
281  TreeHead& tree = frag.m_tree;
282  TupLoc parentLoc = leafNode.getLink(2);
283  if (parentLoc != NullTupLoc) {
284  jam();
285  NodeHandle parentNode(frag);
286  selectNode(parentNode, parentLoc);
287  unsigned i = leafNode.getSide();
288  if (parentNode.getLink(1 - i) == NullTupLoc) {
289  // parent is semi-leaf
290  jam();
291  if (parentNode.getOccup() < tree.m_minOccup) {
292  jam();
293  unsigned cnt = min(leafNode.getOccup(), tree.m_minOccup - parentNode.getOccup());
294  nodeSlide(c_ctx, parentNode, leafNode, cnt, i);
295  }
296  }
297  }
298  if (leafNode.getOccup() == 0) {
299  jam();
300  // remove empty leaf
301  treeRemoveNode(frag, leafNode);
302  }
303 }
304 
305 /*
306  * Remove empty leaf.
307  */
308 void
309 Dbtux::treeRemoveNode(Frag& frag, NodeHandle leafNode)
310 {
311  TreeHead& tree = frag.m_tree;
312  ndbrequire(leafNode.getChilds() == 0);
313  TupLoc parentLoc = leafNode.getLink(2);
314  unsigned i = leafNode.getSide();
315  deleteNode(leafNode);
316  if (parentLoc != NullTupLoc) {
317  jam();
318  NodeHandle parentNode(frag);
319  selectNode(parentNode, parentLoc);
320  parentNode.setLink(i, NullTupLoc);
321  // re-balance the tree
322  treeRemoveRebalance(frag, parentNode, i);
323  return;
324  }
325  // tree is now empty
326  tree.m_root = NullTupLoc;
327  // free even the pre-allocated node
328  freePreallocatedNode(frag);
329 }
330 
331 /*
332  * Re-balance tree after removing a node. The process starts with the
333  * parent of the removed node.
334  */
335 void
336 Dbtux::treeRemoveRebalance(Frag& frag, NodeHandle node, unsigned i)
337 {
338  while (true) {
339  // height of subtree i has decreased by 1
340  int j = (i == 0 ? -1 : +1);
341  int b = node.getBalance();
342  if (b == 0) {
343  // perfectly balanced
344  jam();
345  node.setBalance(-j);
346  // height of tree did not change - done
347  return;
348  } else if (b == j) {
349  // height of longer subtree has decreased
350  jam();
351  node.setBalance(0);
352  // height change propagates up
353  } else if (b == -j) {
354  // height of shorter subtree has decreased
355  jam();
356  // child on the other side
357  NodeHandle childNode(frag);
358  selectNode(childNode, node.getLink(1 - i));
359  int b2 = childNode.getBalance();
360  if (b2 == b) {
361  jam();
362  treeRotateSingle(c_ctx, frag, node, 1 - i);
363  // height of tree decreased and propagates up
364  } else if (b2 == -b) {
365  jam();
366  treeRotateDouble(c_ctx, frag, node, 1 - i);
367  // height of tree decreased and propagates up
368  } else {
369  jam();
370  treeRotateSingle(c_ctx, frag, node, 1 - i);
371  // height of tree did not change - done
372  return;
373  }
374  } else {
375  ndbrequire(false);
376  }
377  TupLoc parentLoc = node.getLink(2);
378  if (parentLoc == NullTupLoc) {
379  jam();
380  // root node - done
381  return;
382  }
383  i = node.getSide();
384  selectNode(node, parentLoc);
385  }
386 }
387 
388 /*
389  * Single rotation about node 5. One of LL (i=0) or RR (i=1).
390  *
391  * 0 0
392  * | |
393  * 5 ==> 3
394  * / \ / \
395  * 3 6 2 5
396  * / \ / / \
397  * 2 4 1 4 6
398  * /
399  * 1
400  *
401  * In this change 5,3 and 2 must always be there. 0, 1, 2, 4 and 6 are
402  * all optional. If 4 are there it changes side.
403 */
404 void
405 Dbtux::treeRotateSingle(TuxCtx& ctx, Frag& frag, NodeHandle& node, unsigned i)
406 {
407  ndbrequire(i <= 1);
408  /*
409  5 is the old top node that have been unbalanced due to an insert or
410  delete. The balance is still the old balance before the update.
411  Verify that bal5 is 1 if RR rotate and -1 if LL rotate.
412  */
413  NodeHandle node5 = node;
414  const TupLoc loc5 = node5.m_loc;
415  const int bal5 = node5.getBalance();
416  const int side5 = node5.getSide();
417  ndbrequire(bal5 + (1 - i) == i);
418  /*
419  3 is the new root of this part of the tree which is to swap place with
420  node 5. For an insert to cause this it must have the same balance as 5.
421  For deletes it can have the balance 0.
422  */
423  TupLoc loc3 = node5.getLink(i);
424  NodeHandle node3(frag);
425  selectNode(node3, loc3);
426  const int bal3 = node3.getBalance();
427  /*
428  2 must always be there but is not changed. Thus we mereley check that it
429  exists.
430  */
431  ndbrequire(node3.getLink(i) != NullTupLoc);
432  /*
433  4 is not necessarily there but if it is there it will move from one
434  side of 3 to the other side of 5. For LL it moves from the right side
435  to the left side and for RR it moves from the left side to the right
436  side. This means that it also changes parent from 3 to 5.
437  */
438  TupLoc loc4 = node3.getLink(1 - i);
439  NodeHandle node4(frag);
440  if (loc4 != NullTupLoc) {
441  thrjam(ctx.jamBuffer);
442  selectNode(node4, loc4);
443  ndbrequire(node4.getSide() == (1 - i) &&
444  node4.getLink(2) == loc3);
445  node4.setSide(i);
446  node4.setLink(2, loc5);
447  }//if
448 
449  /*
450  Retrieve the address of 5's parent before it is destroyed
451  */
452  TupLoc loc0 = node5.getLink(2);
453 
454  /*
455  The next step is to perform the rotation. 3 will inherit 5's parent
456  and side. 5 will become a child of 3 on the right side for LL and on
457  the left side for RR.
458  5 will get 3 as the parent. It will get 4 as a child and it will be
459  on the right side of 3 for LL and left side of 3 for RR.
460  The final step of the rotate is to check whether 5 originally had any
461  parent. If it had not then 3 is the new root node.
462  We will also verify some preconditions for the change to occur.
463  1. 3 must have had 5 as parent before the change.
464  2. 3's side is left for LL and right for RR before change.
465  */
466  ndbrequire(node3.getLink(2) == loc5);
467  ndbrequire(node3.getSide() == i);
468  node3.setLink(1 - i, loc5);
469  node3.setLink(2, loc0);
470  node3.setSide(side5);
471  node5.setLink(i, loc4);
472  node5.setLink(2, loc3);
473  node5.setSide(1 - i);
474  if (loc0 != NullTupLoc) {
475  thrjam(ctx.jamBuffer);
476  NodeHandle node0(frag);
477  selectNode(node0, loc0);
478  node0.setLink(side5, loc3);
479  } else {
480  thrjam(ctx.jamBuffer);
481  frag.m_tree.m_root = loc3;
482  }//if
483  /* The final step of the change is to update the balance of 3 and
484  5 that changed places. There are two cases here. The first case is
485  when 3 unbalanced in the same direction by an insert or a delete.
486  In this case the changes will make the tree balanced again for both
487  3 and 5.
488  The second case only occurs at deletes. In this case 3 starts out
489  balanced. In the figure above this could occur if 4 starts out with
490  a right node and the rotate is triggered by a delete of 6's only child.
491  In this case 5 will change balance but still be unbalanced and 3 will
492  be unbalanced in the opposite direction of 5.
493  */
494  if (bal3 == bal5) {
495  thrjam(ctx.jamBuffer);
496  node3.setBalance(0);
497  node5.setBalance(0);
498  } else if (bal3 == 0) {
499  thrjam(ctx.jamBuffer);
500  node3.setBalance(-bal5);
501  node5.setBalance(bal5);
502  } else {
503  ndbrequire(false);
504  }//if
505  /*
506  Set node to 3 as return parameter for enabling caller to continue
507  traversing the tree.
508  */
509  node = node3;
510 }
511 
512 /*
513  * Double rotation about node 6. One of LR (i=0) or RL (i=1).
514  *
515  * 0 0
516  * | |
517  * 6 ==> 4
518  * / \ / \
519  * 2 7 2 6
520  * / \ / \ / \
521  * 1 4 1 3 5 7
522  * / \
523  * 3 5
524  *
525  * In this change 6, 2 and 4 must be there, all others are optional.
526  * We will start by proving a Lemma.
527  * Lemma:
528  * The height of the sub-trees 1 and 7 and the maximum height of the
529  * threes from 3 and 5 are all the same.
530  * Proof:
531  * maxheight(3,5) is defined as the maximum height of 3 and 5.
532  * If height(7) > maxheight(3,5) then the AVL condition is ok and we
533  * don't need to perform a rotation.
534  * If height(7) < maxheight(3,5) then the balance of 6 would be at least
535  * -3 which cannot happen in an AVL tree even before a rotation.
536  * Thus we conclude that height(7) == maxheight(3,5)
537  *
538  * The next step is to prove that the height of 1 is equal to maxheight(3,5).
539  * If height(1) - 1 > maxheight(3,5) then we would have
540  * balance in 6 equal to -3 at least which cannot happen in an AVL-tree.
541  * If height(1) - 1 = maxheight(3,5) then we should have solved the
542  * unbalance with a single rotate and not with a double rotate.
543  * If height(1) + 1 = maxheight(3,5) then we would be doing a rotate
544  * with node 2 as the root of the rotation.
545  * If height(1) + k = maxheight(3,5) where k >= 2 then the tree could not have
546  * been an AVL-tree before the insert or delete.
547  * Thus we conclude that height(1) = maxheight(3,5)
548  *
549  * Thus we conclude that height(1) = maxheight(3,5) = height(7).
550  *
551  * Observation:
552  * The balance of node 4 before the rotation can be any (-1, 0, +1).
553  *
554  * The following changes are needed:
555  * Node 6:
556  * 1) Changes parent from 0 -> 4
557  * 2) 1 - i link stays the same
558  * 3) i side link is derived from 1 - i side link from 4
559  * 4) Side is set to 1 - i
560  * 5) Balance change:
561  * If balance(4) == 0 then balance(6) = 0
562  * since height(3) = height(5) = maxheight(3,5) = height(7)
563  * If balance(4) == +1 then balance(6) = 0
564  * since height(5) = maxheight(3,5) = height(7)
565  * If balance(4) == -1 then balance(6) = 1
566  * since height(5) + 1 = maxheight(3,5) = height(7)
567  *
568  * Node 2:
569  * 1) Changes parent from 6 -> 4
570  * 2) i side link stays the same
571  * 3) 1 - i side link is derived from i side link of 4
572  * 4) Side is set to i (thus not changed)
573  * 5) Balance change:
574  * If balance(4) == 0 then balance(2) = 0
575  * since height(3) = height(5) = maxheight(3,5) = height(1)
576  * If balance(4) == -1 then balance(2) = 0
577  * since height(3) = maxheight(3,5) = height(1)
578  * If balance(4) == +1 then balance(6) = 1
579  * since height(3) + 1 = maxheight(3,5) = height(1)
580  *
581  * Node 4:
582  * 1) Inherits parent from 6
583  * 2) i side link is 2
584  * 3) 1 - i side link is 6
585  * 4) Side is inherited from 6
586  * 5) Balance(4) = 0 independent of previous balance
587  * Proof:
588  * If height(1) = 0 then only 2, 4 and 6 are involved and then it is
589  * trivially true.
590  * If height(1) >= 1 then we are sure that 1 and 7 exist with the same
591  * height and that if 3 and 5 exist they are of the same height as 1 and
592  * 7 and thus we know that 4 is balanced since newheight(2) = newheight(6).
593  *
594  * If Node 3 exists:
595  * 1) Change parent from 4 to 2
596  * 2) Change side from i to 1 - i
597  *
598  * If Node 5 exists:
599  * 1) Change parent from 4 to 6
600  * 2) Change side from 1 - i to i
601  *
602  * If Node 0 exists:
603  * 1) previous link to 6 is replaced by link to 4 on proper side
604  *
605  * Node 1 and 7 needs no changes at all.
606  *
607  * Some additional requires are that balance(2) = - balance(6) = -1/+1 since
608  * otherwise we would do a single rotate.
609  *
610  * The balance(6) is -1 if i == 0 and 1 if i == 1
611  *
612  */
613 void
614 Dbtux::treeRotateDouble(TuxCtx& ctx, Frag& frag, NodeHandle& node, unsigned i)
615 {
616  TreeHead& tree = frag.m_tree;
617 
618  // old top node
619  NodeHandle node6 = node;
620  const TupLoc loc6 = node6.m_loc;
621  // the un-updated balance
622  const int bal6 = node6.getBalance();
623  const unsigned side6 = node6.getSide();
624 
625  // level 1
626  TupLoc loc2 = node6.getLink(i);
627  NodeHandle node2(frag);
628  selectNode(node2, loc2);
629  const int bal2 = node2.getBalance();
630 
631  // level 2
632  TupLoc loc4 = node2.getLink(1 - i);
633  NodeHandle node4(frag);
634  selectNode(node4, loc4);
635  const int bal4 = node4.getBalance();
636 
637  ndbrequire(i <= 1);
638  ndbrequire(bal6 + (1 - i) == i);
639  ndbrequire(bal2 == -bal6);
640  ndbrequire(node2.getLink(2) == loc6);
641  ndbrequire(node2.getSide() == i);
642  ndbrequire(node4.getLink(2) == loc2);
643 
644  // level 3
645  TupLoc loc3 = node4.getLink(i);
646  TupLoc loc5 = node4.getLink(1 - i);
647 
648  // fill up leaf before it becomes internal
649  if (loc3 == NullTupLoc && loc5 == NullTupLoc) {
650  thrjam(ctx.jamBuffer);
651  if (node4.getOccup() < tree.m_minOccup) {
652  thrjam(ctx.jamBuffer);
653  unsigned cnt = tree.m_minOccup - node4.getOccup();
654  ndbrequire(cnt < node2.getOccup());
655  nodeSlide(ctx, node4, node2, cnt, i);
656  ndbrequire(node4.getOccup() >= tree.m_minOccup);
657  ndbrequire(node2.getOccup() != 0);
658  }
659  } else {
660  if (loc3 != NullTupLoc) {
661  thrjam(ctx.jamBuffer);
662  NodeHandle node3(frag);
663  selectNode(node3, loc3);
664  node3.setLink(2, loc2);
665  node3.setSide(1 - i);
666  }
667  if (loc5 != NullTupLoc) {
668  thrjam(ctx.jamBuffer);
669  NodeHandle node5(frag);
670  selectNode(node5, loc5);
671  node5.setLink(2, node6.m_loc);
672  node5.setSide(i);
673  }
674  }
675  // parent
676  TupLoc loc0 = node6.getLink(2);
677  NodeHandle node0(frag);
678  // perform the rotation
679  node6.setLink(i, loc5);
680  node6.setLink(2, loc4);
681  node6.setSide(1 - i);
682 
683  node2.setLink(1 - i, loc3);
684  node2.setLink(2, loc4);
685 
686  node4.setLink(i, loc2);
687  node4.setLink(1 - i, loc6);
688  node4.setLink(2, loc0);
689  node4.setSide(side6);
690 
691  if (loc0 != NullTupLoc) {
692  thrjam(ctx.jamBuffer);
693  selectNode(node0, loc0);
694  node0.setLink(side6, loc4);
695  } else {
696  thrjam(ctx.jamBuffer);
697  frag.m_tree.m_root = loc4;
698  }
699  // set balance of changed nodes
700  node4.setBalance(0);
701  if (bal4 == 0) {
702  thrjam(ctx.jamBuffer);
703  node2.setBalance(0);
704  node6.setBalance(0);
705  } else if (bal4 == -bal2) {
706  thrjam(ctx.jamBuffer);
707  node2.setBalance(0);
708  node6.setBalance(bal2);
709  } else if (bal4 == bal2) {
710  thrjam(ctx.jamBuffer);
711  node2.setBalance(-bal2);
712  node6.setBalance(0);
713  } else {
714  ndbrequire(false);
715  }
716  // new top node
717  node = node4;
718 }