1 /*
2    Copyright (c) 2003, 2021, Oracle and/or its affiliates.
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, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #define DBTUX_NODE_CPP
26 #include "Dbtux.hpp"
27 
28 #define JAM_FILE_ID 372
29 
30 
31 /*
32  * Allocate index node in TUP.
33  */
34 int
allocNode(TuxCtx & ctx,NodeHandle & node)35 Dbtux::allocNode(TuxCtx& ctx, NodeHandle& node)
36 {
37   if (ERROR_INSERTED(12007)) {
38     jam();
39     CLEAR_ERROR_INSERT_VALUE;
40     return TuxMaintReq::NoMemError;
41   }
42   Frag& frag = node.m_frag;
43   Uint32 pageId = NullTupLoc.getPageId();
44   Uint32 pageOffset = NullTupLoc.getPageOffset();
45   Uint32* node32 = 0;
46   int errorCode = c_tup->tuxAllocNode(ctx.jamBuffer,
47                                       frag.m_tupIndexFragPtrI,
48                                       pageId, pageOffset, node32);
49   thrjamEntry(ctx.jamBuffer);
50   if (errorCode == 0) {
51     thrjam(ctx.jamBuffer);
52     node.m_loc = TupLoc(pageId, pageOffset);
53     node.m_node = reinterpret_cast<TreeNode*>(node32);
54     ndbrequire(node.m_loc != NullTupLoc && node.m_node != 0);
55   } else {
56     switch (errorCode) {
57     case 827:
58       errorCode = TuxMaintReq::NoMemError;
59       break;
60     }
61   }
62   return errorCode;
63 }
64 
65 /*
66  * Free index node in TUP
67  */
68 void
freeNode(NodeHandle & node)69 Dbtux::freeNode(NodeHandle& node)
70 {
71   Frag& frag = node.m_frag;
72   Uint32 pageId = node.m_loc.getPageId();
73   Uint32 pageOffset = node.m_loc.getPageOffset();
74   Uint32* node32 = reinterpret_cast<Uint32*>(node.m_node);
75   c_tup->tuxFreeNode(frag.m_tupIndexFragPtrI,
76                      pageId, pageOffset, node32);
77   jamEntry();
78   // invalidate the handle
79   node.m_loc = NullTupLoc;
80   node.m_node = 0;
81 }
82 
83 /*
84  * Set handle to point to existing node.
85  */
86 void
selectNode(NodeHandle & node,TupLoc loc)87 Dbtux::selectNode(NodeHandle& node, TupLoc loc)
88 {
89   Frag& frag = node.m_frag;
90   ndbrequire(loc != NullTupLoc);
91   Uint32 pageId = loc.getPageId();
92   Uint32 pageOffset = loc.getPageOffset();
93   Uint32* node32 = 0;
94   c_tup->tuxGetNode(frag.m_tupIndexFragPtrI, pageId, pageOffset, node32);
95   node.m_loc = loc;
96   node.m_node = reinterpret_cast<TreeNode*>(node32);
97   ndbrequire(node.m_loc != NullTupLoc && node.m_node != 0);
98 }
99 
100 /*
101  * Set handle to point to new node.  Uses a pre-allocated node.
102  */
103 void
insertNode(NodeHandle & node)104 Dbtux::insertNode(NodeHandle& node)
105 {
106   Frag& frag = node.m_frag;
107   // use up pre-allocated node
108   selectNode(node, frag.m_freeLoc);
109   frag.m_freeLoc = NullTupLoc;
110   new (node.m_node) TreeNode();
111 #ifdef VM_TRACE
112   TreeHead& tree = frag.m_tree;
113   memset(node.getPref(), DataFillByte, tree.m_prefSize << 2);
114   TreeEnt* entList = tree.getEntList(node.m_node);
115   memset(entList, NodeFillByte, tree.m_maxOccup * (TreeEntSize << 2));
116 #endif
117 }
118 
119 /*
120  * Delete existing node.  Make it the pre-allocated free node if there
121  * is none.  Otherwise return it to fragment's free list.
122  */
123 void
deleteNode(NodeHandle & node)124 Dbtux::deleteNode(NodeHandle& node)
125 {
126   Frag& frag = node.m_frag;
127   ndbrequire(node.getOccup() == 0);
128   if (frag.m_freeLoc == NullTupLoc)
129   {
130     jam();
131     frag.m_freeLoc = node.m_loc;
132     // invalidate the handle
133     node.m_loc = NullTupLoc;
134     node.m_node = 0;
135   }
136   else
137   {
138     jam();
139     freeNode(node);
140   }
141 }
142 
143 /*
144  * Free the pre-allocated node, called when tree is empty.  This avoids
145  * leaving any used pages in DataMemory.
146  */
147 void
freePreallocatedNode(Frag & frag)148 Dbtux::freePreallocatedNode(Frag& frag)
149 {
150   if (frag.m_freeLoc != NullTupLoc)
151   {
152     jam();
153     NodeHandle node(frag);
154     selectNode(node, frag.m_freeLoc);
155     freeNode(node);
156     frag.m_freeLoc = NullTupLoc;
157   }
158 }
159 
160 /*
161  * Set prefix.  Copies the defined number of attributes.
162  */
163 void
setNodePref(TuxCtx & ctx,NodeHandle & node)164 Dbtux::setNodePref(TuxCtx & ctx, NodeHandle& node)
165 {
166   const Frag& frag = node.m_frag;
167   const Index& index = *c_indexPool.getPtr(frag.m_indexId);
168   /*
169    * bug#12873640
170    * Node prefix exists if it has non-zero number of attributes.  It is
171    * then a partial instance of KeyData.  If the prefix does not exist
172    * then set_buf() could overwrite m_pageId1 in first entry, causing
173    * random crash in TUP via readKeyAttrs().
174    */
175   if (index.m_prefAttrs > 0) {
176     KeyData prefKey(index.m_keySpec, false, 0);
177     prefKey.set_buf(node.getPref(), index.m_prefBytes);
178     thrjam(ctx.jamBuffer);
179     readKeyAttrs(ctx, frag, node.getEnt(0), prefKey, index.m_prefAttrs);
180 #ifdef VM_TRACE
181     if (debugFlags & DebugMaint) {
182       debugOut << "setNodePref: " << node;
183       debugOut << " " << prefKey.print(ctx.c_debugBuffer, DebugBufferBytes);
184       debugOut << endl;
185     }
186 #endif
187   }
188 }
189 
190 // node operations
191 
192 /*
193  * Add entry at position.  Move entries greater than or equal to the old
194  * one (if any) to the right.
195  *
196  *            X
197  *            v
198  *      A B C D E _ _  =>  A B C X D E _
199  *      0 1 2 3 4 5 6      0 1 2 3 4 5 6
200  *
201  * Add list of scans at the new entry.
202  */
203 void
nodePushUp(TuxCtx & ctx,NodeHandle & node,unsigned pos,const TreeEnt & ent,Uint32 scanList)204 Dbtux::nodePushUp(TuxCtx & ctx, NodeHandle& node, unsigned pos, const TreeEnt& ent, Uint32 scanList)
205 {
206   Frag& frag = node.m_frag;
207   TreeHead& tree = frag.m_tree;
208   const unsigned occup = node.getOccup();
209   ndbrequire(occup < tree.m_maxOccup && pos <= occup);
210   // fix old scans
211   if (node.getNodeScan() != RNIL)
212     nodePushUpScans(node, pos);
213   // fix node
214   TreeEnt* const entList = tree.getEntList(node.m_node);
215   for (unsigned i = occup; i > pos; i--) {
216     thrjam(ctx.jamBuffer);
217     entList[i] = entList[i - 1];
218   }
219   entList[pos] = ent;
220   node.setOccup(occup + 1);
221   // add new scans
222   if (scanList != RNIL)
223     addScanList(node, pos, scanList);
224   // fix prefix
225   if (occup == 0 || pos == 0)
226     setNodePref(ctx, node);
227 }
228 
229 void
nodePushUpScans(NodeHandle & node,unsigned pos)230 Dbtux::nodePushUpScans(NodeHandle& node, unsigned pos)
231 {
232   const unsigned occup = node.getOccup();
233   ScanOpPtr scanPtr;
234   scanPtr.i = node.getNodeScan();
235   do {
236     jam();
237     c_scanOpPool.getPtr(scanPtr);
238     TreePos& scanPos = scanPtr.p->m_scanPos;
239     ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup);
240     if (scanPos.m_pos >= pos) {
241       jam();
242 #ifdef VM_TRACE
243       if (debugFlags & DebugScan) {
244         debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl;
245         debugOut << "At pushUp pos=" << pos << " " << node << endl;
246       }
247 #endif
248       scanPos.m_pos++;
249     }
250     scanPtr.i = scanPtr.p->m_nodeScan;
251   } while (scanPtr.i != RNIL);
252 }
253 
254 /*
255  * Remove and return entry at position.  Move entries greater than the
256  * removed one to the left.  This is the opposite of nodePushUp.
257  *
258  *                               D
259  *            ^                  ^
260  *      A B C D E F _  =>  A B C E F _ _
261  *      0 1 2 3 4 5 6      0 1 2 3 4 5 6
262  *
263  * Scans at removed entry are returned if non-zero location is passed or
264  * else moved forward.
265  */
266 void
nodePopDown(TuxCtx & ctx,NodeHandle & node,unsigned pos,TreeEnt & ent,Uint32 * scanList)267 Dbtux::nodePopDown(TuxCtx& ctx, NodeHandle& node, unsigned pos, TreeEnt& ent, Uint32* scanList)
268 {
269   Frag& frag = node.m_frag;
270   TreeHead& tree = frag.m_tree;
271   const unsigned occup = node.getOccup();
272   ndbrequire(occup <= tree.m_maxOccup && pos < occup);
273   if (node.getNodeScan() != RNIL) {
274     // remove or move scans at this position
275     if (scanList == 0)
276       moveScanList(node, pos);
277     else
278       removeScanList(node, pos, *scanList);
279     // fix other scans
280     if (node.getNodeScan() != RNIL)
281       nodePopDownScans(node, pos);
282   }
283   // fix node
284   TreeEnt* const entList = tree.getEntList(node.m_node);
285   ent = entList[pos];
286   for (unsigned i = pos; i < occup - 1; i++) {
287     thrjam(ctx.jamBuffer);
288     entList[i] = entList[i + 1];
289   }
290   node.setOccup(occup - 1);
291   // fix prefix
292   if (occup != 1 && pos == 0)
293     setNodePref(ctx, node);
294 }
295 
296 void
nodePopDownScans(NodeHandle & node,unsigned pos)297 Dbtux::nodePopDownScans(NodeHandle& node, unsigned pos)
298 {
299   const unsigned occup = node.getOccup();
300   ScanOpPtr scanPtr;
301   scanPtr.i = node.getNodeScan();
302   do {
303     jam();
304     c_scanOpPool.getPtr(scanPtr);
305     TreePos& scanPos = scanPtr.p->m_scanPos;
306     ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup);
307     // handled before
308     ndbrequire(scanPos.m_pos != pos);
309     if (scanPos.m_pos > pos) {
310       jam();
311 #ifdef VM_TRACE
312       if (debugFlags & DebugScan) {
313         debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl;
314         debugOut << "At popDown pos=" << pos << " " << node << endl;
315       }
316 #endif
317       scanPos.m_pos--;
318     }
319     scanPtr.i = scanPtr.p->m_nodeScan;
320   } while (scanPtr.i != RNIL);
321 }
322 
323 /*
324  * Add entry at existing position.  Move entries less than or equal to
325  * the old one to the left.  Remove and return old min entry.
326  *
327  *            X            A
328  *      ^     v            ^
329  *      A B C D E _ _  =>  B C D X E _ _
330  *      0 1 2 3 4 5 6      0 1 2 3 4 5 6
331  *
332  * Return list of scans at the removed position 0.
333  */
334 void
nodePushDown(TuxCtx & ctx,NodeHandle & node,unsigned pos,TreeEnt & ent,Uint32 & scanList)335 Dbtux::nodePushDown(TuxCtx& ctx, NodeHandle& node, unsigned pos, TreeEnt& ent, Uint32& scanList)
336 {
337   Frag& frag = node.m_frag;
338   TreeHead& tree = frag.m_tree;
339   const unsigned occup = node.getOccup();
340   ndbrequire(occup <= tree.m_maxOccup && pos < occup);
341   if (node.getNodeScan() != RNIL) {
342     // remove scans at 0
343     removeScanList(node, 0, scanList);
344     // fix other scans
345     if (node.getNodeScan() != RNIL)
346       nodePushDownScans(node, pos);
347   }
348   // fix node
349   TreeEnt* const entList = tree.getEntList(node.m_node);
350   TreeEnt oldMin = entList[0];
351   for (unsigned i = 0; i < pos; i++) {
352     thrjam(ctx.jamBuffer);
353     entList[i] = entList[i + 1];
354   }
355   entList[pos] = ent;
356   ent = oldMin;
357   // fix prefix
358   if (true)
359     setNodePref(ctx, node);
360 }
361 
362 void
nodePushDownScans(NodeHandle & node,unsigned pos)363 Dbtux::nodePushDownScans(NodeHandle& node, unsigned pos)
364 {
365   const unsigned occup = node.getOccup();
366   ScanOpPtr scanPtr;
367   scanPtr.i = node.getNodeScan();
368   do {
369     jam();
370     c_scanOpPool.getPtr(scanPtr);
371     TreePos& scanPos = scanPtr.p->m_scanPos;
372     ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup);
373     // handled before
374     ndbrequire(scanPos.m_pos != 0);
375     if (scanPos.m_pos <= pos) {
376       jam();
377 #ifdef VM_TRACE
378       if (debugFlags & DebugScan) {
379         debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl;
380         debugOut << "At pushDown pos=" << pos << " " << node << endl;
381       }
382 #endif
383       scanPos.m_pos--;
384     }
385     scanPtr.i = scanPtr.p->m_nodeScan;
386   } while (scanPtr.i != RNIL);
387 }
388 
389 /*
390  * Remove and return entry at position.  Move entries less than the
391  * removed one to the right.  Replace min entry by the input entry.
392  * This is the opposite of nodePushDown.
393  *
394  *      X                        D
395  *      v     ^                  ^
396  *      A B C D E _ _  =>  X A B C E _ _
397  *      0 1 2 3 4 5 6      0 1 2 3 4 5 6
398  *
399  * Move scans at removed entry and add scans at the new entry.
400  */
401 void
nodePopUp(TuxCtx & ctx,NodeHandle & node,unsigned pos,TreeEnt & ent,Uint32 scanList)402 Dbtux::nodePopUp(TuxCtx& ctx, NodeHandle& node, unsigned pos, TreeEnt& ent, Uint32 scanList)
403 {
404   Frag& frag = node.m_frag;
405   TreeHead& tree = frag.m_tree;
406   const unsigned occup = node.getOccup();
407   ndbrequire(occup <= tree.m_maxOccup && pos < occup);
408   if (node.getNodeScan() != RNIL) {
409     // move scans whose entry disappears
410     moveScanList(node, pos);
411     // fix other scans
412     if (node.getNodeScan() != RNIL)
413       nodePopUpScans(node, pos);
414   }
415   // fix node
416   TreeEnt* const entList = tree.getEntList(node.m_node);
417   TreeEnt newMin = ent;
418   ent = entList[pos];
419   for (unsigned i = pos; i > 0; i--) {
420     thrjam(ctx.jamBuffer);
421     entList[i] = entList[i - 1];
422   }
423   entList[0] = newMin;
424   // add scans
425   if (scanList != RNIL)
426     addScanList(node, 0, scanList);
427   // fix prefix
428   if (true)
429     setNodePref(ctx, node);
430 }
431 
432 void
nodePopUpScans(NodeHandle & node,unsigned pos)433 Dbtux::nodePopUpScans(NodeHandle& node, unsigned pos)
434 {
435   const unsigned occup = node.getOccup();
436   ScanOpPtr scanPtr;
437   scanPtr.i = node.getNodeScan();
438   do {
439     jam();
440     c_scanOpPool.getPtr(scanPtr);
441     TreePos& scanPos = scanPtr.p->m_scanPos;
442     ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup);
443     ndbrequire(scanPos.m_pos != pos);
444     if (scanPos.m_pos < pos) {
445       jam();
446 #ifdef VM_TRACE
447       if (debugFlags & DebugScan) {
448         debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl;
449         debugOut << "At popUp pos=" << pos << " " << node << endl;
450       }
451 #endif
452       scanPos.m_pos++;
453     }
454     scanPtr.i = scanPtr.p->m_nodeScan;
455   } while (scanPtr.i != RNIL);
456 }
457 
458 /*
459  * Move number of entries from another node to this node before the min
460  * (i=0) or after the max (i=1).  Expensive but not often used.
461  */
462 void
nodeSlide(TuxCtx & ctx,NodeHandle & dstNode,NodeHandle & srcNode,unsigned cnt,unsigned i)463 Dbtux::nodeSlide(TuxCtx& ctx, NodeHandle& dstNode, NodeHandle& srcNode, unsigned cnt, unsigned i)
464 {
465   ndbrequire(i <= 1);
466   while (cnt != 0) {
467     TreeEnt ent;
468     Uint32 scanList = RNIL;
469     nodePopDown(ctx, srcNode, i == 0 ? srcNode.getOccup() - 1 : 0, ent, &scanList);
470     nodePushUp(ctx, dstNode, i == 0 ? 0 : dstNode.getOccup(), ent, scanList);
471     cnt--;
472   }
473 }
474 
475 // scans linked to node
476 
477 
478 /*
479  * Add list of scans to node at given position.
480  */
481 void
addScanList(NodeHandle & node,unsigned pos,Uint32 scanList)482 Dbtux::addScanList(NodeHandle& node, unsigned pos, Uint32 scanList)
483 {
484   ScanOpPtr scanPtr;
485   scanPtr.i = scanList;
486   do {
487     jam();
488     c_scanOpPool.getPtr(scanPtr);
489 #ifdef VM_TRACE
490       if (debugFlags & DebugScan) {
491         debugOut << "Add scan " << scanPtr.i << " " << *scanPtr.p << endl;
492         debugOut << "To pos=" << pos << " " << node << endl;
493       }
494 #endif
495     const Uint32 nextPtrI = scanPtr.p->m_nodeScan;
496     scanPtr.p->m_nodeScan = RNIL;
497     linkScan(node, scanPtr);
498     TreePos& scanPos = scanPtr.p->m_scanPos;
499     // set position but leave direction alone
500     scanPos.m_loc = node.m_loc;
501     scanPos.m_pos = pos;
502     scanPtr.i = nextPtrI;
503   } while (scanPtr.i != RNIL);
504 }
505 
506 /*
507  * Remove list of scans from node at given position.  The return
508  * location must point to existing list (in fact RNIL always).
509  */
510 void
removeScanList(NodeHandle & node,unsigned pos,Uint32 & scanList)511 Dbtux::removeScanList(NodeHandle& node, unsigned pos, Uint32& scanList)
512 {
513   ScanOpPtr scanPtr;
514   scanPtr.i = node.getNodeScan();
515   do {
516     jam();
517     c_scanOpPool.getPtr(scanPtr);
518     const Uint32 nextPtrI = scanPtr.p->m_nodeScan;
519     TreePos& scanPos = scanPtr.p->m_scanPos;
520     ndbrequire(scanPos.m_loc == node.m_loc);
521     if (scanPos.m_pos == pos) {
522       jam();
523 #ifdef VM_TRACE
524       if (debugFlags & DebugScan) {
525         debugOut << "Remove scan " << scanPtr.i << " " << *scanPtr.p << endl;
526         debugOut << "Fron pos=" << pos << " " << node << endl;
527       }
528 #endif
529       unlinkScan(node, scanPtr);
530       scanPtr.p->m_nodeScan = scanList;
531       scanList = scanPtr.i;
532       // unset position but leave direction alone
533       scanPos.m_loc = NullTupLoc;
534       scanPos.m_pos = ZNIL;
535     }
536     scanPtr.i = nextPtrI;
537   } while (scanPtr.i != RNIL);
538 }
539 
540 /*
541  * Move list of scans away from entry about to be removed.  Uses scan
542  * method scanNext().
543  */
544 void
moveScanList(NodeHandle & node,unsigned pos)545 Dbtux::moveScanList(NodeHandle& node, unsigned pos)
546 {
547   ScanOpPtr scanPtr;
548   scanPtr.i = node.getNodeScan();
549   do {
550     jam();
551     c_scanOpPool.getPtr(scanPtr);
552     TreePos& scanPos = scanPtr.p->m_scanPos;
553     const Uint32 nextPtrI = scanPtr.p->m_nodeScan;
554     ndbrequire(scanPos.m_loc == node.m_loc);
555     if (scanPos.m_pos == pos) {
556       jam();
557 #ifdef VM_TRACE
558       if (debugFlags & DebugScan) {
559         debugOut << "Move scan " << scanPtr.i << " " << *scanPtr.p << endl;
560         debugOut << "At pos=" << pos << " " << node << endl;
561       }
562 #endif
563       scanNext(scanPtr, true);
564       ndbrequire(! (scanPos.m_loc == node.m_loc && scanPos.m_pos == pos));
565     }
566     scanPtr.i = nextPtrI;
567   } while (scanPtr.i != RNIL);
568 }
569 
570 /*
571  * Link scan to the list under the node.  The list is single-linked and
572  * ordering does not matter.
573  */
574 void
linkScan(NodeHandle & node,ScanOpPtr scanPtr)575 Dbtux::linkScan(NodeHandle& node, ScanOpPtr scanPtr)
576 {
577 #ifdef VM_TRACE
578   if (debugFlags & DebugScan) {
579     debugOut << "Link scan " << scanPtr.i << " " << *scanPtr.p << endl;
580     debugOut << "To node " << node << endl;
581   }
582 #endif
583   ndbrequire(! islinkScan(node, scanPtr) && scanPtr.p->m_nodeScan == RNIL);
584   scanPtr.p->m_nodeScan = node.getNodeScan();
585   node.setNodeScan(scanPtr.i);
586 }
587 
588 /*
589  * Unlink a scan from the list under the node.
590  */
591 void
unlinkScan(NodeHandle & node,ScanOpPtr scanPtr)592 Dbtux::unlinkScan(NodeHandle& node, ScanOpPtr scanPtr)
593 {
594 #ifdef VM_TRACE
595   if (debugFlags & DebugScan) {
596     debugOut << "Unlink scan " << scanPtr.i << " " << *scanPtr.p << endl;
597     debugOut << "From node " << node << endl;
598   }
599 #endif
600   ScanOpPtr currPtr;
601   currPtr.i = node.getNodeScan();
602   ScanOpPtr prevPtr;
603   prevPtr.i = RNIL;
604   while (true) {
605     jam();
606     c_scanOpPool.getPtr(currPtr);
607     Uint32 nextPtrI = currPtr.p->m_nodeScan;
608     if (currPtr.i == scanPtr.i) {
609       jam();
610       if (prevPtr.i == RNIL) {
611         node.setNodeScan(nextPtrI);
612       } else {
613         jam();
614         prevPtr.p->m_nodeScan = nextPtrI;
615       }
616       scanPtr.p->m_nodeScan = RNIL;
617       // check for duplicates
618       ndbrequire(! islinkScan(node, scanPtr));
619       return;
620     }
621     prevPtr = currPtr;
622     currPtr.i = nextPtrI;
623   }
624 }
625 
626 /*
627  * Check if a scan is linked to this node.  Only for ndbrequire.
628  */
629 bool
islinkScan(NodeHandle & node,ScanOpPtr scanPtr)630 Dbtux::islinkScan(NodeHandle& node, ScanOpPtr scanPtr)
631 {
632   ScanOpPtr currPtr;
633   currPtr.i = node.getNodeScan();
634   while (currPtr.i != RNIL) {
635     jam();
636     c_scanOpPool.getPtr(currPtr);
637     if (currPtr.i == scanPtr.i) {
638       jam();
639       return true;
640     }
641     currPtr.i = currPtr.p->m_nodeScan;
642   }
643   return false;
644 }
645 
646 void
progError(int line,int cause,const char * file)647 Dbtux::NodeHandle::progError(int line, int cause, const char* file)
648 {
649   ErrorReporter::handleAssert("Dbtux::NodeHandle: assert failed", file, line);
650 }
651