1 /* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
22 
23 #include "myisamdef.h"
24 
25 #ifdef HAVE_RTREE_KEYS
26 
27 #include "rt_index.h"
28 #include "rt_key.h"
29 #include "rt_mbr.h"
30 
31 #define REINSERT_BUFFER_INC 10
32 #define PICK_BY_AREA
33 /*#define PICK_BY_PERIMETER*/
34 
35 typedef struct st_page_level
36 {
37   uint level;
38   my_off_t offs;
39 } stPageLevel;
40 
41 typedef struct st_page_list
42 {
43   ulong n_pages;
44   ulong m_pages;
45   stPageLevel *pages;
46 } stPageList;
47 
48 
49 /*
50    Find next key in r-tree according to search_flag recursively
51 
52    NOTES
53      Used in rtree_find_first() and rtree_find_next()
54 
55    RETURN
56      -1	 Error
57      0   Found
58      1   Not found
59 */
60 
rtree_find_req(MI_INFO * info,MI_KEYDEF * keyinfo,uint search_flag,uint nod_cmp_flag,my_off_t page,int level)61 static int rtree_find_req(MI_INFO *info, MI_KEYDEF *keyinfo, uint search_flag,
62 			  uint nod_cmp_flag, my_off_t page, int level)
63 {
64   uchar *k;
65   uchar *last;
66   uint nod_flag;
67   int res;
68   uchar *page_buf;
69   int k_len;
70   uint *saved_key = (uint*) (info->rtree_recursion_state) + level;
71 
72   if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length)))
73   {
74     my_errno = HA_ERR_OUT_OF_MEM;
75     return -1;
76   }
77   if (!_mi_fetch_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf, 0))
78     goto err1;
79   nod_flag = mi_test_if_nod(page_buf);
80 
81   k_len = keyinfo->keylength - info->s->base.rec_reflength;
82 
83   if(info->rtree_recursion_depth >= level)
84   {
85     k = page_buf + *saved_key;
86   }
87   else
88   {
89     k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
90   }
91   last = rt_PAGE_END(page_buf);
92 
93   for (; k < last; k = rt_PAGE_NEXT_KEY(k, k_len, nod_flag))
94   {
95     if (nod_flag)
96     {
97       /* this is an internal node in the tree */
98       if (!(res = rtree_key_cmp(keyinfo->seg, info->first_mbr_key, k,
99                             info->last_rkey_length, nod_cmp_flag)))
100       {
101         switch ((res = rtree_find_req(info, keyinfo, search_flag, nod_cmp_flag,
102                                       _mi_kpos(nod_flag, k), level + 1)))
103         {
104           case 0: /* found - exit from recursion */
105             *saved_key = (uint) (k - page_buf);
106             goto ok;
107           case 1: /* not found - continue searching */
108             info->rtree_recursion_depth = level;
109             break;
110           default: /* error */
111           case -1:
112             goto err1;
113         }
114       }
115     }
116     else
117     {
118       /* this is a leaf */
119       if (!rtree_key_cmp(keyinfo->seg, info->first_mbr_key, k,
120                          info->last_rkey_length, search_flag))
121       {
122         uchar *after_key = rt_PAGE_NEXT_KEY(k, k_len, nod_flag);
123         info->lastpos = _mi_dpos(info, 0, after_key);
124         info->lastkey_length = k_len + info->s->base.rec_reflength;
125         memcpy(info->lastkey, k, info->lastkey_length);
126         info->rtree_recursion_depth = level;
127         *saved_key = (uint) (last - page_buf);
128 
129         if (after_key < last)
130         {
131           info->int_keypos = info->buff;
132           info->int_maxpos = info->buff + (last - after_key);
133           memcpy(info->buff, after_key, last - after_key);
134           info->buff_used = 0;
135         }
136         else
137         {
138 	  info->buff_used = 1;
139         }
140 
141         res = 0;
142         goto ok;
143       }
144     }
145   }
146   info->lastpos = HA_OFFSET_ERROR;
147   my_errno = HA_ERR_KEY_NOT_FOUND;
148   res = 1;
149 
150 ok:
151   my_afree((uchar*)page_buf);
152   return res;
153 
154 err1:
155   my_afree((uchar*)page_buf);
156   info->lastpos = HA_OFFSET_ERROR;
157   return -1;
158 }
159 
160 
161 /*
162   Find first key in r-tree according to search_flag condition
163 
164   SYNOPSIS
165    rtree_find_first()
166    info			Handler to MyISAM file
167    uint keynr		Key number to use
168    key			Key to search for
169    key_length		Length of 'key'
170    search_flag		Bitmap of flags how to do the search
171 
172   RETURN
173     -1  Error
174     0   Found
175     1   Not found
176 */
177 
rtree_find_first(MI_INFO * info,uint keynr,uchar * key,uint key_length,uint search_flag)178 int rtree_find_first(MI_INFO *info, uint keynr, uchar *key, uint key_length,
179                     uint search_flag)
180 {
181   my_off_t root;
182   uint nod_cmp_flag;
183   MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
184 
185   if ((root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
186   {
187     my_errno= HA_ERR_END_OF_FILE;
188     return -1;
189   }
190 
191   /*
192     Save searched key, include data pointer.
193     The data pointer is required if the search_flag contains MBR_DATA.
194     (minimum bounding rectangle)
195   */
196   memcpy(info->first_mbr_key, key, keyinfo->keylength);
197   info->last_rkey_length = key_length;
198 
199   info->rtree_recursion_depth = -1;
200   info->buff_used = 1;
201 
202   nod_cmp_flag = ((search_flag & (MBR_EQUAL | MBR_WITHIN)) ?
203         MBR_WITHIN : MBR_INTERSECT);
204   return rtree_find_req(info, keyinfo, search_flag, nod_cmp_flag, root, 0);
205 }
206 
207 
208 /*
209    Find next key in r-tree according to search_flag condition
210 
211   SYNOPSIS
212    rtree_find_next()
213    info			Handler to MyISAM file
214    uint keynr		Key number to use
215    search_flag		Bitmap of flags how to do the search
216 
217    RETURN
218      -1  Error
219      0   Found
220      1   Not found
221 */
222 
rtree_find_next(MI_INFO * info,uint keynr,uint search_flag)223 int rtree_find_next(MI_INFO *info, uint keynr, uint search_flag)
224 {
225   my_off_t root;
226   uint nod_cmp_flag;
227   MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
228 
229   if (info->update & HA_STATE_DELETED)
230     return rtree_find_first(info, keynr, info->lastkey, info->lastkey_length,
231 			    search_flag);
232 
233   if (!info->buff_used)
234   {
235     uchar *key= info->int_keypos;
236 
237     while (key < info->int_maxpos)
238     {
239       if (!rtree_key_cmp(keyinfo->seg, info->first_mbr_key, key,
240                          info->last_rkey_length, search_flag))
241       {
242         uchar *after_key = key + keyinfo->keylength;
243 
244         info->lastpos= _mi_dpos(info, 0, after_key);
245         memcpy(info->lastkey, key, info->lastkey_length);
246 
247         if (after_key < info->int_maxpos)
248 	  info->int_keypos= after_key;
249         else
250 	  info->buff_used= 1;
251         return 0;
252       }
253       key+= keyinfo->keylength;
254     }
255   }
256   if ((root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
257   {
258     my_errno= HA_ERR_END_OF_FILE;
259     return -1;
260   }
261 
262   nod_cmp_flag = ((search_flag & (MBR_EQUAL | MBR_WITHIN)) ?
263         MBR_WITHIN : MBR_INTERSECT);
264   return rtree_find_req(info, keyinfo, search_flag, nod_cmp_flag, root, 0);
265 }
266 
267 
268 /*
269   Get next key in r-tree recursively
270 
271   NOTES
272     Used in rtree_get_first() and rtree_get_next()
273 
274   RETURN
275     -1  Error
276     0   Found
277     1   Not found
278 */
279 
rtree_get_req(MI_INFO * info,MI_KEYDEF * keyinfo,uint key_length,my_off_t page,int level)280 static int rtree_get_req(MI_INFO *info, MI_KEYDEF *keyinfo, uint key_length,
281                          my_off_t page, int level)
282 {
283   uchar *k;
284   uchar *last;
285   uint nod_flag;
286   int res;
287   uchar *page_buf;
288   uint k_len;
289   uint *saved_key = (uint*) (info->rtree_recursion_state) + level;
290 
291   if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length)))
292     return -1;
293   if (!_mi_fetch_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf, 0))
294     goto err1;
295   nod_flag = mi_test_if_nod(page_buf);
296 
297   k_len = keyinfo->keylength - info->s->base.rec_reflength;
298 
299   if(info->rtree_recursion_depth >= level)
300   {
301     k = page_buf + *saved_key;
302     if (!nod_flag)
303     {
304       /* Only leaf pages contain data references. */
305       /* Need to check next key with data reference. */
306       k = rt_PAGE_NEXT_KEY(k, k_len, nod_flag);
307     }
308   }
309   else
310   {
311     k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
312   }
313   last = rt_PAGE_END(page_buf);
314 
315   for (; k < last; k = rt_PAGE_NEXT_KEY(k, k_len, nod_flag))
316   {
317     if (nod_flag)
318     {
319       /* this is an internal node in the tree */
320       switch ((res = rtree_get_req(info, keyinfo, key_length,
321                                   _mi_kpos(nod_flag, k), level + 1)))
322       {
323         case 0: /* found - exit from recursion */
324           *saved_key = (uint) (k - page_buf);
325           goto ok;
326         case 1: /* not found - continue searching */
327           info->rtree_recursion_depth = level;
328           break;
329         default:
330         case -1: /* error */
331           goto err1;
332       }
333     }
334     else
335     {
336       /* this is a leaf */
337       uchar *after_key = rt_PAGE_NEXT_KEY(k, k_len, nod_flag);
338       info->lastpos = _mi_dpos(info, 0, after_key);
339       info->lastkey_length = k_len + info->s->base.rec_reflength;
340       memcpy(info->lastkey, k, info->lastkey_length);
341 
342       info->rtree_recursion_depth = level;
343       *saved_key = (uint) (k - page_buf);
344 
345       if (after_key < last)
346       {
347         info->int_keypos = (uchar*)saved_key;
348         memcpy(info->buff, page_buf, keyinfo->block_length);
349         info->int_maxpos = rt_PAGE_END(info->buff);
350         info->buff_used = 0;
351       }
352       else
353       {
354 	info->buff_used = 1;
355       }
356 
357       res = 0;
358       goto ok;
359     }
360   }
361   info->lastpos = HA_OFFSET_ERROR;
362   my_errno = HA_ERR_KEY_NOT_FOUND;
363   res = 1;
364 
365 ok:
366   my_afree((uchar*)page_buf);
367   return res;
368 
369 err1:
370   my_afree((uchar*)page_buf);
371   info->lastpos = HA_OFFSET_ERROR;
372   return -1;
373 }
374 
375 
376 /*
377   Get first key in r-tree
378 
379   RETURN
380     -1	Error
381     0	Found
382     1	Not found
383 */
384 
rtree_get_first(MI_INFO * info,uint keynr,uint key_length)385 int rtree_get_first(MI_INFO *info, uint keynr, uint key_length)
386 {
387   my_off_t root;
388   MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
389 
390   if ((root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
391   {
392     my_errno= HA_ERR_END_OF_FILE;
393     return -1;
394   }
395 
396   info->rtree_recursion_depth = -1;
397   info->buff_used = 1;
398 
399   return rtree_get_req(info, keyinfo, key_length, root, 0);
400 }
401 
402 
403 /*
404   Get next key in r-tree
405 
406   RETURN
407     -1	Error
408     0	Found
409     1	Not found
410 */
411 
rtree_get_next(MI_INFO * info,uint keynr,uint key_length)412 int rtree_get_next(MI_INFO *info, uint keynr, uint key_length)
413 {
414   my_off_t root= info->s->state.key_root[keynr];
415   MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
416 
417   if (root == HA_OFFSET_ERROR)
418   {
419     my_errno= HA_ERR_END_OF_FILE;
420     return -1;
421   }
422 
423   if (!info->buff_used && !info->page_changed)
424   {
425     uint k_len = keyinfo->keylength - info->s->base.rec_reflength;
426     /* rt_PAGE_NEXT_KEY(info->int_keypos) */
427     uchar *key = info->buff + *(int*)info->int_keypos + k_len +
428                  info->s->base.rec_reflength;
429     /* rt_PAGE_NEXT_KEY(key) */
430     uchar *after_key = key + k_len + info->s->base.rec_reflength;
431 
432     info->lastpos = _mi_dpos(info, 0, after_key);
433     info->lastkey_length = k_len + info->s->base.rec_reflength;
434     memcpy(info->lastkey, key, k_len + info->s->base.rec_reflength);
435 
436     *(uint*)info->int_keypos = (uint) (key - info->buff);
437     if (after_key >= info->int_maxpos)
438     {
439       info->buff_used = 1;
440     }
441 
442     return 0;
443   }
444 
445   return rtree_get_req(info, keyinfo, key_length, root, 0);
446 }
447 
448 
449 /*
450   Choose non-leaf better key for insertion
451 */
452 
453 #ifdef PICK_BY_PERIMETER
rtree_pick_key(MI_INFO * info,MI_KEYDEF * keyinfo,uchar * key,uint key_length,uchar * page_buf,uint nod_flag)454 static uchar *rtree_pick_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
455 			     uint key_length, uchar *page_buf, uint nod_flag)
456 {
457   double increase;
458   double best_incr = DBL_MAX;
459   double perimeter;
460   double best_perimeter;
461   uchar *best_key;
462   uchar *k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
463   uchar *last = rt_PAGE_END(page_buf);
464 
465   LINT_INIT(best_perimeter);
466   LINT_INIT(best_key);
467 
468   for (; k < last; k = rt_PAGE_NEXT_KEY(k, key_length, nod_flag))
469   {
470     if ((increase = rtree_perimeter_increase(keyinfo->seg, k, key, key_length,
471 					     &perimeter)) == -1)
472       return NULL;
473     if ((increase < best_incr)||
474 	(increase == best_incr && perimeter < best_perimeter))
475     {
476       best_key = k;
477       best_perimeter= perimeter;
478       best_incr = increase;
479     }
480   }
481   return best_key;
482 }
483 
484 #endif /*PICK_BY_PERIMETER*/
485 
486 #ifdef PICK_BY_AREA
rtree_pick_key(MI_INFO * info,MI_KEYDEF * keyinfo,uchar * key,uint key_length,uchar * page_buf,uint nod_flag)487 static uchar *rtree_pick_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
488 			     uint key_length, uchar *page_buf, uint nod_flag)
489 {
490   double increase;
491   double UNINIT_VAR(best_incr);
492   double area;
493   double UNINIT_VAR(best_area);
494   uchar *best_key= NULL;
495   uchar *k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
496   uchar *last = rt_PAGE_END(page_buf);
497 
498   for (; k < last; k = rt_PAGE_NEXT_KEY(k, key_length, nod_flag))
499   {
500     /* The following is safe as -1.0 is an exact number */
501     if ((increase = rtree_area_increase(keyinfo->seg, k, key, key_length,
502                                         &area)) == -1.0)
503       return NULL;
504     /* The following should be safe, even if we compare doubles */
505     if (!best_key || increase < best_incr ||
506         ((increase == best_incr) && (area < best_area)))
507     {
508       best_key = k;
509       best_area = area;
510       best_incr = increase;
511     }
512   }
513   return best_key;
514 }
515 
516 #endif /*PICK_BY_AREA*/
517 
518 /*
519   Go down and insert key into tree
520 
521   RETURN
522     -1	Error
523     0	Child was not split
524     1	Child was split
525 */
526 
rtree_insert_req(MI_INFO * info,MI_KEYDEF * keyinfo,uchar * key,uint key_length,my_off_t page,my_off_t * new_page,int ins_level,int level)527 static int rtree_insert_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
528                             uint key_length, my_off_t page, my_off_t *new_page,
529                             int ins_level, int level)
530 {
531   uchar *k;
532   uint nod_flag;
533   uchar *page_buf;
534   int res;
535   DBUG_ENTER("rtree_insert_req");
536 
537   if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length +
538                                      MI_MAX_KEY_BUFF)))
539   {
540     my_errno = HA_ERR_OUT_OF_MEM;
541     DBUG_RETURN(-1); /* purecov: inspected */
542   }
543   if (!_mi_fetch_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf, 0))
544     goto err1;
545   nod_flag = mi_test_if_nod(page_buf);
546   DBUG_PRINT("rtree", ("page: %lu  level: %d  ins_level: %d  nod_flag: %u",
547                        (ulong) page, level, ins_level, nod_flag));
548 
549   if ((ins_level == -1 && nod_flag) ||       /* key: go down to leaf */
550       (ins_level > -1 && ins_level > level)) /* branch: go down to ins_level */
551   {
552     if ((k = rtree_pick_key(info, keyinfo, key, key_length, page_buf,
553                              nod_flag)) == NULL)
554       goto err1;
555     switch ((res = rtree_insert_req(info, keyinfo, key, key_length,
556                      _mi_kpos(nod_flag, k), new_page, ins_level, level + 1)))
557     {
558       case 0: /* child was not split */
559       {
560         rtree_combine_rect(keyinfo->seg, k, key, k, key_length);
561         if (_mi_write_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf))
562           goto err1;
563         goto ok;
564       }
565       case 1: /* child was split */
566       {
567         uchar *new_key = page_buf + keyinfo->block_length + nod_flag;
568         /* set proper MBR for key */
569         if (rtree_set_key_mbr(info, keyinfo, k, key_length,
570                             _mi_kpos(nod_flag, k)))
571           goto err1;
572         /* add new key for new page */
573         _mi_kpointer(info, new_key - nod_flag, *new_page);
574         if (rtree_set_key_mbr(info, keyinfo, new_key, key_length, *new_page))
575           goto err1;
576         res = rtree_add_key(info, keyinfo, new_key, key_length,
577                            page_buf, new_page);
578         if (_mi_write_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf))
579           goto err1;
580         goto ok;
581       }
582       default:
583       case -1: /* error */
584       {
585         goto err1;
586       }
587     }
588   }
589   else
590   {
591     res = rtree_add_key(info, keyinfo, key, key_length, page_buf, new_page);
592     if (_mi_write_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf))
593       goto err1;
594     goto ok;
595   }
596 
597 ok:
598   my_afree((uchar*)page_buf);
599   DBUG_RETURN(res);
600 
601 err1:
602   my_afree((uchar*)page_buf);
603   DBUG_RETURN(-1); /* purecov: inspected */
604 }
605 
606 
607 /*
608   Insert key into the tree
609 
610   RETURN
611     -1	Error
612     0	Root was not split
613     1	Root was split
614 */
615 
rtree_insert_level(MI_INFO * info,uint keynr,uchar * key,uint key_length,int ins_level)616 static int rtree_insert_level(MI_INFO *info, uint keynr, uchar *key,
617                              uint key_length, int ins_level)
618 {
619   my_off_t old_root;
620   MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
621   int res;
622   my_off_t new_page;
623   DBUG_ENTER("rtree_insert_level");
624 
625   if ((old_root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
626   {
627     if ((old_root = _mi_new(info, keyinfo, DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
628       DBUG_RETURN(-1);
629     info->buff_used = 1;
630     mi_putint(info->buff, 2, 0);
631     res = rtree_add_key(info, keyinfo, key, key_length, info->buff, NULL);
632     if (_mi_write_keypage(info, keyinfo, old_root, DFLT_INIT_HITS, info->buff))
633       DBUG_RETURN(1);
634     info->s->state.key_root[keynr] = old_root;
635     DBUG_RETURN(res);
636   }
637 
638   switch ((res = rtree_insert_req(info, keyinfo, key, key_length,
639                                   old_root, &new_page, ins_level, 0)))
640   {
641     case 0: /* root was not split */
642     {
643       break;
644     }
645     case 1: /* root was split, grow a new root */
646     {
647       uchar *new_root_buf= info->buff + info->s->base.max_key_block_length;
648       my_off_t new_root;
649       uchar *new_key;
650       uint nod_flag = info->s->base.key_reflength;
651 
652       DBUG_PRINT("rtree", ("root was split, grow a new root"));
653 
654       mi_putint(new_root_buf, 2, nod_flag);
655       if ((new_root = _mi_new(info, keyinfo, DFLT_INIT_HITS)) ==
656 	  HA_OFFSET_ERROR)
657         goto err1;
658 
659       new_key = new_root_buf + keyinfo->block_length + nod_flag;
660 
661       _mi_kpointer(info, new_key - nod_flag, old_root);
662       if (rtree_set_key_mbr(info, keyinfo, new_key, key_length, old_root))
663         goto err1;
664       if (rtree_add_key(info, keyinfo, new_key, key_length, new_root_buf, NULL)
665           == -1)
666         goto err1;
667       _mi_kpointer(info, new_key - nod_flag, new_page);
668       if (rtree_set_key_mbr(info, keyinfo, new_key, key_length, new_page))
669         goto err1;
670       if (rtree_add_key(info, keyinfo, new_key, key_length, new_root_buf, NULL)
671           == -1)
672         goto err1;
673       if (_mi_write_keypage(info, keyinfo, new_root,
674                             DFLT_INIT_HITS, new_root_buf))
675         goto err1;
676       info->s->state.key_root[keynr] = new_root;
677       DBUG_PRINT("rtree", ("new root page: %lu  level: %d  nod_flag: %u",
678                            (ulong) new_root, 0, mi_test_if_nod(new_root_buf)));
679 
680       break;
681 err1:
682       DBUG_RETURN(-1); /* purecov: inspected */
683     }
684     default:
685     case -1: /* error */
686     {
687       break;
688     }
689   }
690   DBUG_RETURN(res);
691 }
692 
693 
694 /*
695   Insert key into the tree - interface function
696 
697   RETURN
698     -1	Error
699     0	OK
700 */
701 
rtree_insert(MI_INFO * info,uint keynr,uchar * key,uint key_length)702 int rtree_insert(MI_INFO *info, uint keynr, uchar *key, uint key_length)
703 {
704   DBUG_ENTER("rtree_insert");
705   DBUG_RETURN((!key_length ||
706                (rtree_insert_level(info, keynr, key, key_length, -1) == -1)) ?
707               -1 : 0);
708 }
709 
710 
711 /*
712   Fill reinsert page buffer
713 
714   RETURN
715     -1	Error
716     0	OK
717 */
718 
rtree_fill_reinsert_list(stPageList * ReinsertList,my_off_t page,int level)719 static int rtree_fill_reinsert_list(stPageList *ReinsertList, my_off_t page,
720                                     int level)
721 {
722   DBUG_ENTER("rtree_fill_reinsert_list");
723   DBUG_PRINT("rtree", ("page: %lu  level: %d", (ulong) page, level));
724   if (ReinsertList->n_pages == ReinsertList->m_pages)
725   {
726     ReinsertList->m_pages += REINSERT_BUFFER_INC;
727     if (!(ReinsertList->pages = (stPageLevel*)my_realloc((uchar*)ReinsertList->pages,
728       ReinsertList->m_pages * sizeof(stPageLevel), MYF(MY_ALLOW_ZERO_PTR))))
729       goto err1;
730   }
731   /* save page to ReinsertList */
732   ReinsertList->pages[ReinsertList->n_pages].offs = page;
733   ReinsertList->pages[ReinsertList->n_pages].level = level;
734   ReinsertList->n_pages++;
735   DBUG_RETURN(0);
736 
737 err1:
738   DBUG_RETURN(-1); /* purecov: inspected */
739 }
740 
741 
742 /*
743   Go down and delete key from the tree
744 
745   RETURN
746     -1	Error
747     0	Deleted
748     1	Not found
749     2	Empty leaf
750 */
751 
rtree_delete_req(MI_INFO * info,MI_KEYDEF * keyinfo,uchar * key,uint key_length,my_off_t page,uint * page_size,stPageList * ReinsertList,int level)752 static int rtree_delete_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
753                            uint key_length, my_off_t page, uint *page_size,
754                            stPageList *ReinsertList, int level)
755 {
756   uchar *k;
757   uchar *last;
758   ulong i;
759   uint nod_flag;
760   uchar *page_buf;
761   int res;
762   DBUG_ENTER("rtree_delete_req");
763 
764   if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length)))
765   {
766     my_errno = HA_ERR_OUT_OF_MEM;
767     DBUG_RETURN(-1); /* purecov: inspected */
768   }
769   if (!_mi_fetch_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf, 0))
770     goto err1;
771   nod_flag = mi_test_if_nod(page_buf);
772   DBUG_PRINT("rtree", ("page: %lu  level: %d  nod_flag: %u",
773                        (ulong) page, level, nod_flag));
774 
775   k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
776   last = rt_PAGE_END(page_buf);
777 
778   for (i = 0; k < last; k = rt_PAGE_NEXT_KEY(k, key_length, nod_flag), ++i)
779   {
780     if (nod_flag)
781     {
782       /* not leaf */
783       if (!rtree_key_cmp(keyinfo->seg, key, k, key_length, MBR_WITHIN))
784       {
785         switch ((res = rtree_delete_req(info, keyinfo, key, key_length,
786                   _mi_kpos(nod_flag, k), page_size, ReinsertList, level + 1)))
787         {
788           case 0: /* deleted */
789           {
790             /* test page filling */
791             if (*page_size + key_length >= rt_PAGE_MIN_SIZE(keyinfo->block_length))
792             {
793               /* OK */
794               /* Calculate a new key value (MBR) for the shrinked block. */
795               if (rtree_set_key_mbr(info, keyinfo, k, key_length,
796                                   _mi_kpos(nod_flag, k)))
797                 goto err1;
798               if (_mi_write_keypage(info, keyinfo, page,
799                                     DFLT_INIT_HITS, page_buf))
800                 goto err1;
801             }
802             else
803             {
804               /*
805                 Too small: delete key & add it descendant to reinsert list.
806                 Store position and level of the block so that it can be
807                 accessed later for inserting the remaining keys.
808               */
809               DBUG_PRINT("rtree", ("too small. move block to reinsert list"));
810               if (rtree_fill_reinsert_list(ReinsertList, _mi_kpos(nod_flag, k),
811                                            level + 1))
812                 goto err1;
813               /*
814                 Delete the key that references the block. This makes the
815                 block disappear from the index. Hence we need to insert
816                 its remaining keys later. Note: if the block is a branch
817                 block, we do not only remove this block, but the whole
818                 subtree. So we need to re-insert its keys on the same
819                 level later to reintegrate the subtrees.
820               */
821               rtree_delete_key(info, page_buf, k, key_length, nod_flag);
822               if (_mi_write_keypage(info, keyinfo, page,
823                                     DFLT_INIT_HITS, page_buf))
824                 goto err1;
825               *page_size = mi_getint(page_buf);
826             }
827 
828             goto ok;
829           }
830           case 1: /* not found - continue searching */
831           {
832             break;
833           }
834           case 2: /* vacuous case: last key in the leaf */
835           {
836             rtree_delete_key(info, page_buf, k, key_length, nod_flag);
837             if (_mi_write_keypage(info, keyinfo, page,
838                                   DFLT_INIT_HITS, page_buf))
839               goto err1;
840             *page_size = mi_getint(page_buf);
841             res = 0;
842             goto ok;
843           }
844           default: /* error */
845           case -1:
846           {
847             goto err1;
848           }
849         }
850       }
851     }
852     else
853     {
854       /* leaf */
855       if (!rtree_key_cmp(keyinfo->seg, key, k, key_length, MBR_EQUAL | MBR_DATA))
856       {
857         rtree_delete_key(info, page_buf, k, key_length, nod_flag);
858         *page_size = mi_getint(page_buf);
859         if (*page_size == 2)
860         {
861           /* last key in the leaf */
862           res = 2;
863           if (_mi_dispose(info, keyinfo, page, DFLT_INIT_HITS))
864             goto err1;
865         }
866         else
867         {
868           res = 0;
869           if (_mi_write_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf))
870             goto err1;
871         }
872         goto ok;
873       }
874     }
875   }
876   res = 1;
877 
878 ok:
879   my_afree((uchar*)page_buf);
880   DBUG_RETURN(res);
881 
882 err1:
883   my_afree((uchar*)page_buf);
884   DBUG_RETURN(-1); /* purecov: inspected */
885 }
886 
887 
888 /*
889   Delete key - interface function
890 
891   RETURN
892     -1	Error
893     0	Deleted
894 */
895 
rtree_delete(MI_INFO * info,uint keynr,uchar * key,uint key_length)896 int rtree_delete(MI_INFO *info, uint keynr, uchar *key, uint key_length)
897 {
898   uint page_size;
899   stPageList ReinsertList;
900   my_off_t old_root;
901   MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
902   DBUG_ENTER("rtree_delete");
903 
904   if ((old_root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
905   {
906     my_errno= HA_ERR_END_OF_FILE;
907     DBUG_RETURN(-1); /* purecov: inspected */
908   }
909   DBUG_PRINT("rtree", ("starting deletion at root page: %lu",
910                        (ulong) old_root));
911 
912   ReinsertList.pages = NULL;
913   ReinsertList.n_pages = 0;
914   ReinsertList.m_pages = 0;
915 
916   switch (rtree_delete_req(info, keyinfo, key, key_length, old_root,
917                                  &page_size, &ReinsertList, 0))
918   {
919     case 2: /* empty */
920     {
921       info->s->state.key_root[keynr] = HA_OFFSET_ERROR;
922       DBUG_RETURN(0);
923     }
924     case 0: /* deleted */
925     {
926       uint nod_flag;
927       ulong i;
928       for (i = 0; i < ReinsertList.n_pages; ++i)
929       {
930         uchar *page_buf;
931         uchar *k;
932         uchar *last;
933 
934         if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length)))
935         {
936           my_errno = HA_ERR_OUT_OF_MEM;
937           goto err1;
938         }
939         if (!_mi_fetch_keypage(info, keyinfo, ReinsertList.pages[i].offs,
940                                DFLT_INIT_HITS, page_buf, 0))
941           goto err1;
942         nod_flag = mi_test_if_nod(page_buf);
943         DBUG_PRINT("rtree", ("reinserting keys from "
944                              "page: %lu  level: %d  nod_flag: %u",
945                              (ulong) ReinsertList.pages[i].offs,
946                              ReinsertList.pages[i].level, nod_flag));
947 
948         k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
949         last = rt_PAGE_END(page_buf);
950         for (; k < last; k = rt_PAGE_NEXT_KEY(k, key_length, nod_flag))
951         {
952           int res;
953           if ((res= rtree_insert_level(info, keynr, k, key_length,
954                                        ReinsertList.pages[i].level)) == -1)
955           {
956             my_afree((uchar*)page_buf);
957             goto err1;
958           }
959           if (res)
960           {
961             ulong j;
962             DBUG_PRINT("rtree", ("root has been split, adjust levels"));
963             for (j= i; j < ReinsertList.n_pages; j++)
964             {
965               ReinsertList.pages[j].level++;
966               DBUG_PRINT("rtree", ("keys from page: %lu  now level: %d",
967                                    (ulong) ReinsertList.pages[i].offs,
968                                    ReinsertList.pages[i].level));
969             }
970           }
971         }
972         my_afree((uchar*)page_buf);
973         if (_mi_dispose(info, keyinfo, ReinsertList.pages[i].offs,
974             DFLT_INIT_HITS))
975           goto err1;
976       }
977       if (ReinsertList.pages)
978         my_free(ReinsertList.pages);
979 
980       /* check for redundant root (not leaf, 1 child) and eliminate */
981       if ((old_root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
982         goto err1;
983       if (!_mi_fetch_keypage(info, keyinfo, old_root, DFLT_INIT_HITS,
984                              info->buff, 0))
985         goto err1;
986       nod_flag = mi_test_if_nod(info->buff);
987       page_size = mi_getint(info->buff);
988       if (nod_flag && (page_size == 2 + key_length + nod_flag))
989       {
990         my_off_t new_root = _mi_kpos(nod_flag,
991                                      rt_PAGE_FIRST_KEY(info->buff, nod_flag));
992         if (_mi_dispose(info, keyinfo, old_root, DFLT_INIT_HITS))
993           goto err1;
994         info->s->state.key_root[keynr] = new_root;
995       }
996       info->update= HA_STATE_DELETED;
997       DBUG_RETURN(0);
998 
999 err1:
1000       DBUG_RETURN(-1); /* purecov: inspected */
1001     }
1002     case 1: /* not found */
1003     {
1004       my_errno = HA_ERR_KEY_NOT_FOUND;
1005       DBUG_RETURN(-1); /* purecov: inspected */
1006     }
1007     default:
1008     case -1: /* error */
1009     {
1010       DBUG_RETURN(-1); /* purecov: inspected */
1011     }
1012   }
1013 }
1014 
1015 
1016 /*
1017   Estimate number of suitable keys in the tree
1018 
1019   RETURN
1020     estimated value
1021 */
1022 
rtree_estimate(MI_INFO * info,uint keynr,uchar * key,uint key_length,uint flag)1023 ha_rows rtree_estimate(MI_INFO *info, uint keynr, uchar *key,
1024                        uint key_length, uint flag)
1025 {
1026   MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
1027   my_off_t root;
1028   uint i = 0;
1029   uchar *k;
1030   uchar *last;
1031   uint nod_flag;
1032   uchar *page_buf;
1033   uint k_len;
1034   double area = 0;
1035   ha_rows res = 0;
1036 
1037   if (flag & MBR_DISJOINT)
1038     return info->state->records;
1039 
1040   if ((root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
1041     return HA_POS_ERROR;
1042   if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length)))
1043     return HA_POS_ERROR;
1044   if (!_mi_fetch_keypage(info, keyinfo, root, DFLT_INIT_HITS, page_buf, 0))
1045     goto err1;
1046   nod_flag = mi_test_if_nod(page_buf);
1047 
1048   k_len = keyinfo->keylength - info->s->base.rec_reflength;
1049 
1050   k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
1051   last = rt_PAGE_END(page_buf);
1052 
1053   for (; k < last; k = rt_PAGE_NEXT_KEY(k, k_len, nod_flag), ++i)
1054   {
1055     if (nod_flag)
1056     {
1057       double k_area = rtree_rect_volume(keyinfo->seg, k, key_length);
1058 
1059       /* The following should be safe, even if we compare doubles */
1060       if (k_area == 0)
1061       {
1062         if (flag & (MBR_CONTAIN | MBR_INTERSECT))
1063         {
1064           area += 1;
1065         }
1066         else if (flag & (MBR_WITHIN | MBR_EQUAL))
1067         {
1068           if (!rtree_key_cmp(keyinfo->seg, key, k, key_length, MBR_WITHIN))
1069             area += 1;
1070         }
1071         else
1072           goto err1;
1073       }
1074       else
1075       {
1076         if (flag & (MBR_CONTAIN | MBR_INTERSECT))
1077         {
1078           area += rtree_overlapping_area(keyinfo->seg, key, k, key_length) /
1079                   k_area;
1080         }
1081         else if (flag & (MBR_WITHIN | MBR_EQUAL))
1082         {
1083           if (!rtree_key_cmp(keyinfo->seg, key, k, key_length, MBR_WITHIN))
1084             area += rtree_rect_volume(keyinfo->seg, key, key_length) /
1085                     k_area;
1086         }
1087         else
1088           goto err1;
1089       }
1090     }
1091     else
1092     {
1093       if (!rtree_key_cmp(keyinfo->seg, key, k, key_length, flag))
1094         ++res;
1095     }
1096   }
1097   if (nod_flag)
1098   {
1099     if (i)
1100       res = (ha_rows) (area / i * info->state->records);
1101     else
1102       res = HA_POS_ERROR;
1103   }
1104 
1105   my_afree((uchar*)page_buf);
1106   return res;
1107 
1108 err1:
1109   my_afree((uchar*)page_buf);
1110   return HA_POS_ERROR;
1111 }
1112 
1113 #endif /*HAVE_RTREE_KEYS*/
1114 
1115