1 /*
2    Copyright (c) 2004, 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_SEARCH_CPP
26 #include "Dbtux.hpp"
27 
28 #define JAM_FILE_ID 368
29 
30 
31 /*
32  * Search down non-empty tree for node to update.  Compare search key to
33  * each node minimum.  If greater, move to right subtree.  This can
34  * overshoot target node.  The last such node is saved.  The search ends
35  * at a final node which is a semi-leaf or leaf.  If search key is less
36  * than final node minimum then the saved node (if any) is the g.l.b of
37  * the final node and we move back to it.
38  *
39  * Search within the found node is done by caller.  On add, search key
40  * may be before minimum or after maximum entry.  On remove, search key
41  * is within the node.
42  */
43 void
findNodeToUpdate(TuxCtx & ctx,Frag & frag,const KeyDataC & searchKey,TreeEnt searchEnt,NodeHandle & currNode)44 Dbtux::findNodeToUpdate(TuxCtx& ctx, Frag& frag, const KeyDataC& searchKey, TreeEnt searchEnt, NodeHandle& currNode)
45 {
46   const Index& index = *c_indexPool.getPtr(frag.m_indexId);
47   const Uint32 numAttrs = index.m_numAttrs;
48   const Uint32 prefAttrs = index.m_prefAttrs;
49   const Uint32 prefBytes = index.m_prefBytes;
50   KeyData entryKey(index.m_keySpec, false, 0);
51   entryKey.set_buf(ctx.c_entryKey, MaxAttrDataSize << 2);
52   KeyDataC prefKey(index.m_keySpec, false);
53   NodeHandle glbNode(frag);     // potential g.l.b of final node
54   while (true) {
55     thrjam(ctx.jamBuffer);
56     selectNode(currNode, currNode.m_loc);
57     prefKey.set_buf(currNode.getPref(), prefBytes, prefAttrs);
58     int ret = 0;
59     if (prefAttrs > 0) {
60       thrjam(ctx.jamBuffer);
61       ret = cmpSearchKey(ctx, searchKey, prefKey, prefAttrs);
62     }
63     if (ret == 0 && prefAttrs < numAttrs) {
64       thrjam(ctx.jamBuffer);
65       // read and compare all attributes
66       readKeyAttrs(ctx, frag, currNode.getEnt(0), entryKey, numAttrs);
67       ret = cmpSearchKey(ctx, searchKey, entryKey, numAttrs);
68     }
69     if (ret == 0) {
70       thrjam(ctx.jamBuffer);
71       // keys are equal, compare entry values
72       ret = searchEnt.cmp(currNode.getEnt(0));
73     }
74     if (ret < 0) {
75       thrjam(ctx.jamBuffer);
76       const TupLoc loc = currNode.getLink(0);
77       if (loc != NullTupLoc) {
78         thrjam(ctx.jamBuffer);
79         // continue to left subtree
80         currNode.m_loc = loc;
81         continue;
82       }
83       if (! glbNode.isNull()) {
84         thrjam(ctx.jamBuffer);
85         // move up to the g.l.b
86         currNode = glbNode;
87       }
88       break;
89     }
90     if (ret > 0) {
91       thrjam(ctx.jamBuffer);
92       const TupLoc loc = currNode.getLink(1);
93       if (loc != NullTupLoc) {
94         thrjam(ctx.jamBuffer);
95         // save potential g.l.b
96         glbNode = currNode;
97         // continue to right subtree
98         currNode.m_loc = loc;
99         continue;
100       }
101       break;
102     }
103     // ret == 0
104     thrjam(ctx.jamBuffer);
105     break;
106   }
107 }
108 
109 /*
110  * Find position within the final node to add entry to.  Use binary
111  * search.  Return true if ok i.e. entry to add is not a duplicate.
112  */
113 bool
findPosToAdd(TuxCtx & ctx,Frag & frag,const KeyDataC & searchKey,TreeEnt searchEnt,NodeHandle & currNode,TreePos & treePos)114 Dbtux::findPosToAdd(TuxCtx& ctx, Frag& frag, const KeyDataC& searchKey, TreeEnt searchEnt, NodeHandle& currNode, TreePos& treePos)
115 {
116   const Index& index = *c_indexPool.getPtr(frag.m_indexId);
117   int lo = -1;
118   int hi = (int)currNode.getOccup();
119   KeyData entryKey(index.m_keySpec, false, 0);
120   entryKey.set_buf(ctx.c_entryKey, MaxAttrDataSize << 2);
121   while (hi - lo > 1) {
122     thrjam(ctx.jamBuffer);
123     // hi - lo > 1 implies lo < j < hi
124     int j = (hi + lo) / 2;
125     // read and compare all attributes
126     readKeyAttrs(ctx, frag, currNode.getEnt(j), entryKey, index.m_numAttrs);
127     int ret = cmpSearchKey(ctx, searchKey, entryKey, index.m_numAttrs);
128     if (ret == 0) {
129       thrjam(ctx.jamBuffer);
130       // keys are equal, compare entry values
131       ret = searchEnt.cmp(currNode.getEnt(j));
132     }
133     if (ret < 0) {
134       thrjam(ctx.jamBuffer);
135       hi = j;
136     } else if (ret > 0) {
137       thrjam(ctx.jamBuffer);
138       lo = j;
139     } else {
140       treePos.m_pos = j;
141       // entry found - error
142       return false;
143     }
144   }
145   ndbrequire(hi - lo == 1);
146   // return hi pos, see treeAdd() for next step
147   treePos.m_pos = hi;
148   return true;
149 }
150 
151 /*
152  * Find position within the final node to remove entry from.  Use linear
153  * search.  Return true if ok i.e. the entry was found.
154  */
155 bool
findPosToRemove(TuxCtx & ctx,Frag & frag,const KeyDataC & searchKey,TreeEnt searchEnt,NodeHandle & currNode,TreePos & treePos)156 Dbtux::findPosToRemove(TuxCtx& ctx, Frag& frag, const KeyDataC& searchKey, TreeEnt searchEnt, NodeHandle& currNode, TreePos& treePos)
157 {
158   const unsigned occup = currNode.getOccup();
159   for (unsigned j = 0; j < occup; j++) {
160     thrjam(ctx.jamBuffer);
161     // compare only the entry
162     if (searchEnt.eq(currNode.getEnt(j))) {
163       thrjam(ctx.jamBuffer);
164       treePos.m_pos = j;
165       return true;
166     }
167   }
168   treePos.m_pos = occup;
169   // not found - failed
170   return false;
171 }
172 
173 /*
174  * Search for entry to add.
175  */
176 bool
searchToAdd(TuxCtx & ctx,Frag & frag,const KeyDataC & searchKey,TreeEnt searchEnt,TreePos & treePos)177 Dbtux::searchToAdd(TuxCtx& ctx, Frag& frag, const KeyDataC& searchKey, TreeEnt searchEnt, TreePos& treePos)
178 {
179   const TreeHead& tree = frag.m_tree;
180   NodeHandle currNode(frag);
181   currNode.m_loc = tree.m_root;
182   if (unlikely(currNode.m_loc == NullTupLoc)) {
183     // empty tree
184     thrjam(ctx.jamBuffer);
185     return true;
186   }
187   findNodeToUpdate(ctx, frag, searchKey, searchEnt, currNode);
188   treePos.m_loc = currNode.m_loc;
189   if (! findPosToAdd(ctx, frag, searchKey, searchEnt, currNode, treePos)) {
190     thrjam(ctx.jamBuffer);
191     return false;
192   }
193   return true;
194 }
195 
196 /*
197  * Search for entry to remove.
198  */
199 bool
searchToRemove(TuxCtx & ctx,Frag & frag,const KeyDataC & searchKey,TreeEnt searchEnt,TreePos & treePos)200 Dbtux::searchToRemove(TuxCtx& ctx, Frag& frag, const KeyDataC& searchKey, TreeEnt searchEnt, TreePos& treePos)
201 {
202   const TreeHead& tree = frag.m_tree;
203   NodeHandle currNode(frag);
204   currNode.m_loc = tree.m_root;
205   if (unlikely(currNode.m_loc == NullTupLoc)) {
206     // empty tree - failed
207     thrjam(ctx.jamBuffer);
208     return false;
209   }
210   findNodeToUpdate(ctx, frag, searchKey, searchEnt, currNode);
211   treePos.m_loc = currNode.m_loc;
212   if (! findPosToRemove(ctx, frag, searchKey, searchEnt, currNode, treePos)) {
213     thrjam(ctx.jamBuffer);
214     return false;
215   }
216   return true;
217 }
218 
219 /*
220  * Search down non-empty tree for node to start scan from.  Similar to
221  * findNodeToUpdate().  Direction is 0-ascending or 1-descending.
222  * Search within the found node is done by caller.
223  */
224 void
findNodeToScan(Frag & frag,unsigned idir,const KeyBoundC & searchBound,NodeHandle & currNode)225 Dbtux::findNodeToScan(Frag& frag, unsigned idir, const KeyBoundC& searchBound, NodeHandle& currNode)
226 {
227   const int jdir = 1 - 2 * int(idir);
228   const Index& index = *c_indexPool.getPtr(frag.m_indexId);
229   const Uint32 numAttrs = searchBound.get_data().get_cnt();
230   const Uint32 prefAttrs = min(index.m_prefAttrs, numAttrs);
231   const Uint32 prefBytes = index.m_prefBytes;
232   KeyData entryKey(index.m_keySpec, false, 0);
233   entryKey.set_buf(c_ctx.c_entryKey, MaxAttrDataSize << 2);
234   KeyDataC prefKey(index.m_keySpec, false);
235   NodeHandle glbNode(frag);     // potential g.l.b of final node
236   while (true) {
237     jam();
238     selectNode(currNode, currNode.m_loc);
239     prefKey.set_buf(currNode.getPref(), prefBytes, prefAttrs);
240     int ret = 0;
241     if (numAttrs > 0) {
242       if (prefAttrs > 0) {
243         jam();
244         // compare node prefix - result 0 implies bound is longer
245         ret = cmpSearchBound(c_ctx, searchBound, prefKey, prefAttrs);
246       }
247       if (ret == 0) {
248         jam();
249         // read and compare all attributes
250         readKeyAttrs(c_ctx, frag, currNode.getEnt(0), entryKey, numAttrs);
251         ret = cmpSearchBound(c_ctx, searchBound, entryKey, numAttrs);
252         ndbrequire(ret != 0);
253       }
254     } else {
255       jam();
256       ret = (-1) * jdir;
257     }
258     if (ret < 0) {
259       // bound is left of this node
260       jam();
261       const TupLoc loc = currNode.getLink(0);
262       if (loc != NullTupLoc) {
263         jam();
264         // continue to left subtree
265         currNode.m_loc = loc;
266         continue;
267       }
268       if (! glbNode.isNull()) {
269         jam();
270         // move up to the g.l.b
271         currNode = glbNode;
272       }
273       break;
274     }
275     if (ret > 0) {
276       // bound is at or right of this node
277       jam();
278       const TupLoc loc = currNode.getLink(1);
279       if (loc != NullTupLoc) {
280         jam();
281         // save potential g.l.b
282         glbNode = currNode;
283         // continue to right subtree
284         currNode.m_loc = loc;
285         continue;
286       }
287       break;
288     }
289     // ret == 0 never
290     ndbrequire(false);
291   }
292 }
293 
294 /*
295  * Search across final node for position to start scan from.  Use binary
296  * search similar to findPosToAdd().
297  */
298 void
findPosToScan(Frag & frag,unsigned idir,const KeyBoundC & searchBound,NodeHandle & currNode,Uint16 * pos)299 Dbtux::findPosToScan(Frag& frag, unsigned idir, const KeyBoundC& searchBound, NodeHandle& currNode, Uint16* pos)
300 {
301   const int jdir = 1 - 2 * int(idir);
302   const Index& index = *c_indexPool.getPtr(frag.m_indexId);
303   const Uint32 numAttrs = searchBound.get_data().get_cnt();
304   int lo = -1;
305   int hi = (int)currNode.getOccup();
306   KeyData entryKey(index.m_keySpec, false, 0);
307   entryKey.set_buf(c_ctx.c_entryKey, MaxAttrDataSize << 2);
308   while (hi - lo > 1) {
309     jam();
310     // hi - lo > 1 implies lo < j < hi
311     int j = (hi + lo) / 2;
312     int ret = (-1) * jdir;
313     if (numAttrs != 0) {
314       // read and compare all attributes
315       readKeyAttrs(c_ctx, frag, currNode.getEnt(j), entryKey, numAttrs);
316       ret = cmpSearchBound(c_ctx, searchBound, entryKey, numAttrs);
317       ndbrequire(ret != 0);
318     }
319     if (ret < 0) {
320       jam();
321       hi = j;
322     } else if (ret > 0) {
323       jam();
324       lo = j;
325     } else {
326       // ret == 0 never
327       ndbrequire(false);
328     }
329   }
330   // return hi pos, caller handles ascending vs descending
331   *pos = hi;
332 }
333 
334 /*
335  * Search for scan start position.
336  */
337 void
searchToScan(Frag & frag,unsigned idir,const KeyBoundC & searchBound,TreePos & treePos)338 Dbtux::searchToScan(Frag& frag, unsigned idir, const KeyBoundC& searchBound, TreePos& treePos)
339 {
340   const TreeHead& tree = frag.m_tree;
341   NodeHandle currNode(frag);
342   currNode.m_loc = tree.m_root;
343   if (unlikely(currNode.m_loc == NullTupLoc)) {
344     // empty tree
345     jam();
346     return;
347   }
348   findNodeToScan(frag, idir, searchBound, currNode);
349   treePos.m_loc = currNode.m_loc;
350   Uint16 pos;
351   findPosToScan(frag, idir, searchBound, currNode, &pos);
352   const unsigned occup = currNode.getOccup();
353   if (idir == 0) {
354     if (pos < occup) {
355       jam();
356       treePos.m_pos = pos;
357       treePos.m_dir = 3;
358     } else {
359       // start scan after node end i.e. proceed to right child
360       treePos.m_pos = ZNIL;
361       treePos.m_dir = 5;
362     }
363   } else {
364     if (pos > 0) {
365       jam();
366       // start scan from previous entry
367       treePos.m_pos = pos - 1;
368       treePos.m_dir = 3;
369     } else {
370       treePos.m_pos = ZNIL;
371       treePos.m_dir = 0;
372     }
373   }
374 }
375