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