1/* Copyright (C) 2006 MySQL AB & Ramil Kalimullin & MySQL Finland AB
2 & TCX DataKonsult AB
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 Street, Fifth Floor, Boston, MA 02111-1301 USA */
16
17#include "maria_def.h"
18#include "trnman.h"
19#include "ma_key_recover.h"
20
21#ifdef HAVE_RTREE_KEYS
22
23#include "ma_rt_index.h"
24#include "ma_rt_key.h"
25#include "ma_rt_mbr.h"
26
27#define REINSERT_BUFFER_INC 10
28#define PICK_BY_AREA
29/*#define PICK_BY_PERIMETER*/
30
31typedef struct st_page_level
32{
33 uint level;
34 my_off_t offs;
35} stPageLevel;
36
37typedef struct st_page_list
38{
39 uint n_pages;
40 uint m_pages;
41 stPageLevel *pages;
42} stPageList;
43
44
45/*
46 Find next key in r-tree according to search_flag recursively
47
48 NOTES
49 Used in maria_rtree_find_first() and maria_rtree_find_next()
50
51 RETURN
52 -1 Error
53 0 Found
54 1 Not found
55*/
56
57static int maria_rtree_find_req(MARIA_HA *info, MARIA_KEYDEF *keyinfo,
58 uint32 search_flag,
59 uint nod_cmp_flag, my_off_t page_pos,
60 int level)
61{
62 MARIA_SHARE *share= info->s;
63 uint nod_flag;
64 int res;
65 uchar *page_buf, *k, *last;
66 int key_data_length;
67 uint *saved_key= (uint*) (info->maria_rtree_recursion_state) + level;
68 MARIA_PAGE page;
69
70 if (!(page_buf= (uchar*) my_alloca((uint) keyinfo->block_length)))
71 {
72 my_errno= HA_ERR_OUT_OF_MEM;
73 return -1;
74 }
75 if (_ma_fetch_keypage(&page, info, keyinfo, page_pos,
76 PAGECACHE_LOCK_LEFT_UNLOCKED,
77 DFLT_INIT_HITS, page_buf, 0))
78 goto err;
79 nod_flag= page.node;
80
81 key_data_length= keyinfo->keylength - share->base.rec_reflength;
82
83 if (info->maria_rtree_recursion_depth >= level)
84 {
85 k= page_buf + *saved_key;
86 }
87 else
88 {
89 k= rt_PAGE_FIRST_KEY(share, page_buf, nod_flag);
90 }
91 last= rt_PAGE_END(&page);
92
93 for (; k < last; k= rt_PAGE_NEXT_KEY(share, k, key_data_length, nod_flag))
94 {
95 if (nod_flag)
96 {
97 /* this is an internal node in the tree */
98 if (!(res= maria_rtree_key_cmp(keyinfo->seg,
99 info->first_mbr_key, k,
100 info->last_rkey_length, nod_cmp_flag)))
101 {
102 switch ((res= maria_rtree_find_req(info, keyinfo, search_flag,
103 nod_cmp_flag,
104 _ma_kpos(nod_flag, k),
105 level + 1)))
106 {
107 case 0: /* found - exit from recursion */
108 *saved_key= (uint) (k - page_buf);
109 goto ok;
110 case 1: /* not found - continue searching */
111 info->maria_rtree_recursion_depth= level;
112 break;
113 default: /* error */
114 case -1:
115 goto err;
116 }
117 }
118 }
119 else
120 {
121 /* this is a leaf */
122 if (!maria_rtree_key_cmp(keyinfo->seg, info->first_mbr_key,
123 k, info->last_rkey_length, search_flag))
124 {
125 uchar *after_key= rt_PAGE_NEXT_KEY(share, k, key_data_length, 0);
126 MARIA_KEY tmp_key;
127
128 /*
129 We don't need to set all MARIA_KEY elements here as
130 _ma_row_pos_from_key() only uses a few of them.
131 */
132 tmp_key.keyinfo= keyinfo;
133 tmp_key.data= k;
134 tmp_key.data_length= key_data_length;
135
136 info->cur_row.lastpos= _ma_row_pos_from_key(&tmp_key);
137 info->last_key.data_length= key_data_length;
138 info->last_key.ref_length= share->base.rec_reflength;
139 info->last_key.flag= 0;
140 memcpy(info->last_key.data, k,
141 info->last_key.data_length + info->last_key.ref_length);
142 info->maria_rtree_recursion_depth= level;
143 *saved_key= (uint) (last - page_buf);
144
145 if (after_key < last)
146 {
147 uchar *keyread_buff= info->keyread_buff;
148 info->int_keypos= keyread_buff;
149 info->int_maxpos= keyread_buff + (last - after_key);
150 memcpy(keyread_buff, after_key, last - after_key);
151 info->keyread_buff_used= 0;
152 }
153 else
154 {
155 info->keyread_buff_used= 1;
156 }
157
158 res= 0;
159 goto ok;
160 }
161 }
162 }
163 info->cur_row.lastpos= HA_OFFSET_ERROR;
164 my_errno= HA_ERR_KEY_NOT_FOUND;
165 res= 1;
166
167ok:
168 my_afree(page_buf);
169 return res;
170
171err:
172 my_afree(page_buf);
173 info->cur_row.lastpos= HA_OFFSET_ERROR;
174 return -1;
175}
176
177
178/*
179 Find first key in r-tree according to search_flag condition
180
181 SYNOPSIS
182 maria_rtree_find_first()
183 info Handler to MARIA file
184 key Key to search for
185 search_flag Bitmap of flags how to do the search
186
187 RETURN
188 -1 Error
189 0 Found
190 1 Not found
191*/
192
193int maria_rtree_find_first(MARIA_HA *info, MARIA_KEY *key, uint32 search_flag)
194{
195 my_off_t root;
196 uint nod_cmp_flag;
197 MARIA_KEYDEF *keyinfo= key->keyinfo;
198
199 /*
200 At the moment index can only properly handle the
201 MBR_INTERSECT, so we use it for all sorts of queries.
202 TODO: better searsh for CONTAINS/WITHIN.
203 */
204 search_flag= nod_cmp_flag= MBR_INTERSECT;
205 if ((root= info->s->state.key_root[keyinfo->key_nr]) == HA_OFFSET_ERROR)
206 {
207 my_errno= HA_ERR_END_OF_FILE;
208 return -1;
209 }
210
211 /*
212 Save searched key, include data pointer.
213 The data pointer is required if the search_flag contains MBR_DATA.
214 (minimum bounding rectangle)
215 */
216 memcpy(info->first_mbr_key, key->data, key->data_length + key->ref_length);
217 info->last_rkey_length= key->data_length;
218
219 info->maria_rtree_recursion_depth= -1;
220 info->keyread_buff_used= 1;
221
222 /*
223 TODO better search for CONTAINS/WITHIN.
224 nod_cmp_flag= ((search_flag & (MBR_EQUAL | MBR_WITHIN)) ?
225 MBR_WITHIN : MBR_INTERSECT);
226 */
227 return maria_rtree_find_req(info, keyinfo, search_flag, nod_cmp_flag, root,
228 0);
229}
230
231
232/*
233 Find next key in r-tree according to search_flag condition
234
235 SYNOPSIS
236 maria_rtree_find_next()
237 info Handler to MARIA file
238 uint keynr Key number to use
239 search_flag Bitmap of flags how to do the search
240
241 RETURN
242 -1 Error
243 0 Found
244 1 Not found
245*/
246
247int maria_rtree_find_next(MARIA_HA *info, uint keynr, uint32 search_flag)
248{
249 my_off_t root;
250 uint32 nod_cmp_flag;
251 MARIA_KEYDEF *keyinfo= info->s->keyinfo + keynr;
252 DBUG_ASSERT(info->last_key.keyinfo == keyinfo);
253 /*
254 At the moment index can only properly handle the
255 MBR_INTERSECT, so we use it for all sorts of queries.
256 TODO: better searsh for CONTAINS/WITHIN.
257 */
258 search_flag= nod_cmp_flag= MBR_INTERSECT;
259
260 if (info->update & HA_STATE_DELETED)
261 return maria_rtree_find_first(info, &info->last_key, search_flag);
262
263 if (!info->keyread_buff_used)
264 {
265 uchar *key= info->int_keypos;
266
267 while (key < info->int_maxpos)
268 {
269 if (!maria_rtree_key_cmp(keyinfo->seg,
270 info->first_mbr_key, key,
271 info->last_rkey_length, search_flag))
272 {
273 uchar *after_key= key + keyinfo->keylength;
274 MARIA_KEY tmp_key;
275
276 /*
277 We don't need to set all MARIA_KEY elements here as
278 _ma_row_pos_from_key only uses a few of them.
279 */
280 tmp_key.keyinfo= keyinfo;
281 tmp_key.data= key;
282 tmp_key.data_length= keyinfo->keylength - info->s->base.rec_reflength;
283
284 info->cur_row.lastpos= _ma_row_pos_from_key(&tmp_key);
285 memcpy(info->last_key.data, key, info->last_key.data_length);
286
287 if (after_key < info->int_maxpos)
288 info->int_keypos= after_key;
289 else
290 info->keyread_buff_used= 1;
291 return 0;
292 }
293 key+= keyinfo->keylength;
294 }
295 }
296 if ((root= info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
297 {
298 my_errno= HA_ERR_END_OF_FILE;
299 return -1;
300 }
301
302 /*
303 TODO better search for CONTAINS/WITHIN.
304 nod_cmp_flag= (((search_flag & (MBR_EQUAL | MBR_WITHIN)) ?
305 MBR_WITHIN : MBR_INTERSECT));
306 */
307 return maria_rtree_find_req(info, keyinfo, search_flag, nod_cmp_flag, root,
308 0);
309}
310
311
312/*
313 Get next key in r-tree recursively
314
315 NOTES
316 Used in maria_rtree_get_first() and maria_rtree_get_next()
317
318 RETURN
319 -1 Error
320 0 Found
321 1 Not found
322*/
323
324static int maria_rtree_get_req(MARIA_HA *info, MARIA_KEYDEF *keyinfo,
325 uint key_length, my_off_t page_pos, int level)
326{
327 MARIA_SHARE *share= info->s;
328 uchar *page_buf, *last, *k;
329 uint nod_flag, key_data_length;
330 int res;
331 uint *saved_key= (uint*) (info->maria_rtree_recursion_state) + level;
332 MARIA_PAGE page;
333
334 if (!(page_buf= (uchar*) my_alloca((uint) keyinfo->block_length)))
335 return -1;
336 if (_ma_fetch_keypage(&page, info, keyinfo, page_pos,
337 PAGECACHE_LOCK_LEFT_UNLOCKED,
338 DFLT_INIT_HITS, page_buf, 0))
339 goto err;
340 nod_flag= page.node;
341
342 key_data_length= keyinfo->keylength - share->base.rec_reflength;
343
344 if (info->maria_rtree_recursion_depth >= level)
345 {
346 k= page.buff + *saved_key;
347 if (!nod_flag)
348 {
349 /* Only leaf pages contain data references. */
350 /* Need to check next key with data reference. */
351 k= rt_PAGE_NEXT_KEY(share, k, key_data_length, nod_flag);
352 }
353 }
354 else
355 {
356 k= rt_PAGE_FIRST_KEY(share, page.buff, nod_flag);
357 }
358 last= rt_PAGE_END(&page);
359
360 for (; k < last; k= rt_PAGE_NEXT_KEY(share, k, key_data_length, nod_flag))
361 {
362 if (nod_flag)
363 {
364 /* this is an internal node in the tree */
365 switch ((res= maria_rtree_get_req(info, keyinfo, key_length,
366 _ma_kpos(nod_flag, k), level + 1)))
367 {
368 case 0: /* found - exit from recursion */
369 *saved_key= (uint) (k - page.buff);
370 goto ok;
371 case 1: /* not found - continue searching */
372 info->maria_rtree_recursion_depth= level;
373 break;
374 default:
375 case -1: /* error */
376 goto err;
377 }
378 }
379 else
380 {
381 /* this is a leaf */
382 uchar *after_key= rt_PAGE_NEXT_KEY(share, k, key_data_length, 0);
383 MARIA_KEY tmp_key;
384
385 /*
386 We don't need to set all MARIA_KEY elements here as
387 _ma_row_pos_from_key() only uses a few of them.
388 */
389 tmp_key.keyinfo= keyinfo;
390 tmp_key.data= k;
391 tmp_key.data_length= key_data_length;
392
393 info->cur_row.lastpos= _ma_row_pos_from_key(&tmp_key);
394 info->last_key.data_length= key_data_length;
395 info->last_key.ref_length= share->base.rec_reflength;
396
397 memcpy(info->last_key.data, k,
398 info->last_key.data_length + info->last_key.ref_length);
399
400 info->maria_rtree_recursion_depth= level;
401 *saved_key= (uint) (k - page.buff);
402
403 if (after_key < last)
404 {
405 uchar *keyread_buff= info->keyread_buff;
406 info->last_rtree_keypos= saved_key;
407 memcpy(keyread_buff, page.buff, page.size);
408 info->int_maxpos= keyread_buff + page.size;
409 info->keyread_buff_used= 0;
410 }
411 else
412 {
413 info->keyread_buff_used= 1;
414 }
415
416 res= 0;
417 goto ok;
418 }
419 }
420 info->cur_row.lastpos= HA_OFFSET_ERROR;
421 my_errno= HA_ERR_KEY_NOT_FOUND;
422 res= 1;
423
424ok:
425 my_afree(page_buf);
426 return res;
427
428err:
429 my_afree(page_buf);
430 info->cur_row.lastpos= HA_OFFSET_ERROR;
431 return -1;
432}
433
434
435/*
436 Get first key in r-tree
437
438 RETURN
439 -1 Error
440 0 Found
441 1 Not found
442*/
443
444int maria_rtree_get_first(MARIA_HA *info, uint keynr, uint key_length)
445{
446 my_off_t root;
447 MARIA_KEYDEF *keyinfo= info->s->keyinfo + keynr;
448
449 if ((root= info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
450 {
451 my_errno= HA_ERR_END_OF_FILE;
452 return -1;
453 }
454
455 info->maria_rtree_recursion_depth= -1;
456 info->keyread_buff_used= 1;
457
458 return maria_rtree_get_req(info, keyinfo, key_length, root, 0);
459}
460
461
462/*
463 Get next key in r-tree
464
465 RETURN
466 -1 Error
467 0 Found
468 1 Not found
469*/
470
471int maria_rtree_get_next(MARIA_HA *info, uint keynr, uint key_length)
472{
473 my_off_t root;
474 MARIA_KEYDEF *keyinfo= info->s->keyinfo + keynr;
475 uchar *keyread_buff= info->keyread_buff;
476
477 if (!info->keyread_buff_used)
478 {
479 uint key_data_length= keyinfo->keylength - info->s->base.rec_reflength;
480 /* rt_PAGE_NEXT_KEY(*info->last_rtree_keypos) */
481 uchar *key= keyread_buff + *info->last_rtree_keypos + keyinfo->keylength;
482 /* rt_PAGE_NEXT_KEY(key) */
483 uchar *after_key= key + keyinfo->keylength;
484 MARIA_KEY tmp_key;
485
486 tmp_key.keyinfo= keyinfo;
487 tmp_key.data= key;
488 tmp_key.data_length= key_data_length;
489 tmp_key.ref_length= info->s->base.rec_reflength;
490 tmp_key.flag= 0;
491
492 info->cur_row.lastpos= _ma_row_pos_from_key(&tmp_key);
493 _ma_copy_key(&info->last_key, &tmp_key);
494
495 *info->last_rtree_keypos= (uint) (key - keyread_buff);
496 if (after_key >= info->int_maxpos)
497 {
498 info->keyread_buff_used= 1;
499 }
500
501 return 0;
502 }
503 else
504 {
505 if ((root= info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
506 {
507 my_errno= HA_ERR_END_OF_FILE;
508 return -1;
509 }
510
511 return maria_rtree_get_req(info, &keyinfo[keynr], key_length, root, 0);
512 }
513}
514
515
516/*
517 Choose non-leaf better key for insertion
518
519 Returns a pointer inside the page_buf buffer.
520*/
521#ifdef PICK_BY_PERIMETER
522static const uchar *maria_rtree_pick_key(const MARIA_KEY *key,
523 const MARIA_PAGE *page)
524{
525 double increase;
526 double UNINIT_VAR(best_incr);
527 double perimeter;
528 double UNINIT_VAR(best_perimeter);
529 uchar *best_key= NULL;
530 const MARIA_HA *info= page->info;
531
532 uchar *k= rt_PAGE_FIRST_KEY(info->s, page->buf, page->node);
533 uchar *last= rt_PAGE_END(info, page);
534
535 for (; k < last; k= rt_PAGE_NEXT_KEY(k, key->data_length, nod_flag))
536 {
537 if ((increase= maria_rtree_perimeter_increase(keyinfo->seg, k, key,
538 &perimeter)) == -1)
539 return NULL;
540 if ((increase < best_incr)||
541 (increase == best_incr && perimeter < best_perimeter))
542 {
543 best_key= k;
544 best_perimeter= perimeter;
545 best_incr= increase;
546 }
547 }
548 return best_key;
549}
550
551#endif /*PICK_BY_PERIMETER*/
552
553#ifdef PICK_BY_AREA
554static const uchar *maria_rtree_pick_key(const MARIA_KEY *key,
555 const MARIA_PAGE *page)
556{
557 const MARIA_HA *info= page->info;
558 MARIA_SHARE *share= info->s;
559 double increase;
560 double best_incr= DBL_MAX;
561 double area;
562 double UNINIT_VAR(best_area);
563 const uchar *best_key= NULL;
564 const uchar *k= rt_PAGE_FIRST_KEY(share, page->buff, page->node);
565 const uchar *last= rt_PAGE_END(page);
566
567 for (; k < last;
568 k= rt_PAGE_NEXT_KEY(share, k, key->data_length, page->node))
569 {
570 /* The following is safe as -1.0 is an exact number */
571 if ((increase= maria_rtree_area_increase(key->keyinfo->seg, k, key->data,
572 key->data_length +
573 key->ref_length,
574 &area)) == -1.0)
575 return NULL;
576 /* The following should be safe, even if we compare doubles */
577 if (!best_key || increase < best_incr ||
578 ((increase == best_incr) && (area < best_area)))
579 {
580 best_key= k;
581 best_area= area;
582 best_incr= increase;
583 }
584 }
585 return best_key;
586}
587
588#endif /*PICK_BY_AREA*/
589
590/*
591 Go down and insert key into tree
592
593 RETURN
594 -1 Error
595 0 Child was not split
596 1 Child was split
597*/
598
599static int maria_rtree_insert_req(MARIA_HA *info, MARIA_KEY *key,
600 my_off_t page_pos, my_off_t *new_page,
601 int ins_level, int level)
602{
603 uint nod_flag;
604 uint key_length= key->data_length;
605 int res;
606 uchar *page_buf, *k;
607 MARIA_SHARE *share= info->s;
608 MARIA_KEYDEF *keyinfo= key->keyinfo;
609 MARIA_PAGE page;
610 DBUG_ENTER("maria_rtree_insert_req");
611
612 if (!(page_buf= (uchar*) my_alloca((uint) keyinfo->block_length +
613 MARIA_MAX_KEY_BUFF)))
614 {
615 my_errno= HA_ERR_OUT_OF_MEM;
616 DBUG_RETURN(-1); /* purecov: inspected */
617 }
618 if (_ma_fetch_keypage(&page, info, keyinfo, page_pos, PAGECACHE_LOCK_WRITE,
619 DFLT_INIT_HITS, page_buf, 0))
620 goto err;
621 nod_flag= page.node;
622 DBUG_PRINT("rtree", ("page: %lu level: %d ins_level: %d nod_flag: %u",
623 (ulong) page.pos, level, ins_level, nod_flag));
624
625 if ((ins_level == -1 && nod_flag) || /* key: go down to leaf */
626 (ins_level > -1 && ins_level > level)) /* branch: go down to ins_level */
627 {
628 if (!(k= (uchar *)maria_rtree_pick_key(key, &page)))
629 goto err;
630 /* k is now a pointer inside the page_buf buffer */
631 switch ((res= maria_rtree_insert_req(info, key,
632 _ma_kpos(nod_flag, k), new_page,
633 ins_level, level + 1)))
634 {
635 case 0: /* child was not split, most common case */
636 {
637 maria_rtree_combine_rect(keyinfo->seg, k, key->data, k, key_length);
638 if (share->now_transactional &&
639 _ma_log_change(&page, k, key_length,
640 KEY_OP_DEBUG_RTREE_COMBINE))
641 goto err;
642 page_mark_changed(info, &page);
643 if (_ma_write_keypage(&page, PAGECACHE_LOCK_LEFT_WRITELOCKED,
644 DFLT_INIT_HITS))
645 goto err;
646 goto ok;
647 }
648 case 1: /* child was split */
649 {
650 /* Set new_key to point to a free buffer area */
651 uchar *new_key_buff= page_buf + keyinfo->block_length + nod_flag;
652 MARIA_KEY new_key;
653 MARIA_KEY k_key;
654
655 DBUG_ASSERT(nod_flag);
656 k_key.keyinfo= new_key.keyinfo= keyinfo;
657 new_key.data= new_key_buff;
658 k_key.data= k;
659 k_key.data_length= new_key.data_length= key->data_length;
660 k_key.ref_length= new_key.ref_length= key->ref_length;
661 k_key.flag= new_key.flag= 0; /* Safety */
662
663 /* set proper MBR for key */
664 if (maria_rtree_set_key_mbr(info, &k_key, _ma_kpos(nod_flag, k)))
665 goto err;
666 if (share->now_transactional &&
667 _ma_log_change(&page, k, key_length,
668 KEY_OP_DEBUG_RTREE_SPLIT))
669 goto err;
670 /* add new key for new page */
671 _ma_kpointer(info, new_key_buff - nod_flag, *new_page);
672 if (maria_rtree_set_key_mbr(info, &new_key, *new_page))
673 goto err;
674 res= maria_rtree_add_key(&new_key, &page, new_page);
675 page_mark_changed(info, &page);
676 if (_ma_write_keypage(&page, PAGECACHE_LOCK_LEFT_WRITELOCKED,
677 DFLT_INIT_HITS))
678 goto err;
679 goto ok;
680 }
681 default:
682 case -1: /* error */
683 {
684 goto err;
685 }
686 }
687 }
688 else
689 {
690 res= maria_rtree_add_key(key, &page, new_page);
691 page_mark_changed(info, &page);
692 if (_ma_write_keypage(&page, PAGECACHE_LOCK_LEFT_WRITELOCKED,
693 DFLT_INIT_HITS))
694 goto err;
695 }
696
697ok:
698 my_afree(page_buf);
699 DBUG_RETURN(res);
700
701err:
702 res= -1; /* purecov: inspected */
703 goto ok; /* purecov: inspected */
704}
705
706
707/**
708 Insert key into the tree
709
710 @param info table
711 @param key KEY to insert
712 @param ins_level at which level key insertion should start
713 @param root put new key_root there
714
715 @return Operation result
716 @retval -1 Error
717 @retval 0 Root was not split
718 @retval 1 Root was split
719*/
720
721int maria_rtree_insert_level(MARIA_HA *info, MARIA_KEY *key, int ins_level,
722 my_off_t *root)
723{
724 my_off_t old_root;
725 MARIA_SHARE *share= info->s;
726 MARIA_KEYDEF *keyinfo= key->keyinfo;
727 int res;
728 my_off_t new_page;
729 enum pagecache_page_lock write_lock;
730 DBUG_ENTER("maria_rtree_insert_level");
731
732 if ((old_root= share->state.key_root[keyinfo->key_nr]) == HA_OFFSET_ERROR)
733 {
734 MARIA_PINNED_PAGE tmp_page_link, *page_link;
735 MARIA_PAGE page;
736
737 page_link= &tmp_page_link;
738 if ((old_root= _ma_new(info, DFLT_INIT_HITS, &page_link)) ==
739 HA_OFFSET_ERROR)
740 DBUG_RETURN(-1);
741 write_lock= page_link->write_lock;
742 info->keyread_buff_used= 1;
743 bzero(info->buff, share->block_size);
744 _ma_store_keynr(share, info->buff, keyinfo->key_nr);
745 _ma_store_page_used(share, info->buff, share->keypage_header);
746 _ma_page_setup(&page, info, keyinfo, old_root, info->buff);
747
748 if (share->now_transactional && _ma_log_new(&page, 1))
749 DBUG_RETURN(1);
750
751 res= maria_rtree_add_key(key, &page, NULL);
752 if (_ma_write_keypage(&page, write_lock, DFLT_INIT_HITS))
753 DBUG_RETURN(1);
754 *root= old_root;
755 DBUG_RETURN(res);
756 }
757
758 switch ((res= maria_rtree_insert_req(info, key, old_root, &new_page,
759 ins_level, 0)))
760 {
761 case 0: /* root was not split */
762 {
763 break;
764 }
765 case 1: /* root was split, grow a new root; very rare */
766 {
767 uchar *new_root_buf, *new_key_buff;
768 my_off_t new_root;
769 uint nod_flag= share->base.key_reflength;
770 MARIA_PINNED_PAGE tmp_page_link, *page_link;
771 MARIA_KEY new_key;
772 MARIA_PAGE page;
773 page_link= &tmp_page_link;
774
775 DBUG_PRINT("rtree", ("root was split, grow a new root"));
776 if (!(new_root_buf= (uchar*) my_alloca((uint) keyinfo->block_length +
777 MARIA_MAX_KEY_BUFF)))
778 {
779 my_errno= HA_ERR_OUT_OF_MEM;
780 DBUG_RETURN(-1); /* purecov: inspected */
781 }
782
783 bzero(new_root_buf, share->block_size);
784 _ma_store_keypage_flag(share, new_root_buf, KEYPAGE_FLAG_ISNOD);
785 _ma_store_keynr(share, new_root_buf, keyinfo->key_nr);
786 _ma_store_page_used(share, new_root_buf, share->keypage_header);
787 if ((new_root= _ma_new(info, DFLT_INIT_HITS, &page_link)) ==
788 HA_OFFSET_ERROR)
789 goto err;
790 write_lock= page_link->write_lock;
791
792 _ma_page_setup(&page, info, keyinfo, new_root, new_root_buf);
793
794 if (share->now_transactional && _ma_log_new(&page, 1))
795 goto err;
796
797 /* Point to some free space */
798 new_key_buff= new_root_buf + keyinfo->block_length + nod_flag;
799 new_key.keyinfo= keyinfo;
800 new_key.data= new_key_buff;
801 new_key.data_length= key->data_length;
802 new_key.ref_length= key->ref_length;
803 new_key.flag= 0;
804
805 _ma_kpointer(info, new_key_buff - nod_flag, old_root);
806 if (maria_rtree_set_key_mbr(info, &new_key, old_root))
807 goto err;
808 if (maria_rtree_add_key(&new_key, &page, NULL)
809 == -1)
810 goto err;
811 _ma_kpointer(info, new_key_buff - nod_flag, new_page);
812 if (maria_rtree_set_key_mbr(info, &new_key, new_page))
813 goto err;
814 if (maria_rtree_add_key(&new_key, &page, NULL)
815 == -1)
816 goto err;
817 if (_ma_write_keypage(&page, write_lock, DFLT_INIT_HITS))
818 goto err;
819 *root= new_root;
820 DBUG_PRINT("rtree", ("new root page: %lu level: %d nod_flag: %u",
821 (ulong) new_root, 0, page.node));
822
823 my_afree(new_root_buf);
824 break;
825err:
826 my_afree(new_root_buf);
827 DBUG_RETURN(-1); /* purecov: inspected */
828 }
829 default:
830 case -1: /* error */
831 {
832 DBUG_ASSERT(0);
833 break;
834 }
835 }
836 DBUG_RETURN(res);
837}
838
839
840/*
841 Insert key into the tree - interface function
842
843 RETURN
844 1 Error
845 0 OK
846*/
847
848my_bool maria_rtree_insert(MARIA_HA *info, MARIA_KEY *key)
849{
850 int res;
851 MARIA_SHARE *share= info->s;
852 my_off_t *root, new_root;
853 LSN lsn= LSN_IMPOSSIBLE;
854 DBUG_ENTER("maria_rtree_insert");
855
856 if (!key)
857 DBUG_RETURN(1); /* _ma_sp_make_key failed */
858
859 root= &share->state.key_root[key->keyinfo->key_nr];
860 new_root= *root;
861
862 if ((res= (maria_rtree_insert_level(info, key, -1, &new_root) == -1)))
863 goto err;
864 if (share->now_transactional)
865 res= _ma_write_undo_key_insert(info, key, root, new_root, &lsn);
866 else
867 {
868 *root= new_root;
869 _ma_fast_unlock_key_del(info);
870 }
871 _ma_unpin_all_pages_and_finalize_row(info, lsn);
872err:
873 DBUG_RETURN(res != 0);
874}
875
876
877/*
878 Fill reinsert page buffer
879
880 RETURN
881 1 Error
882 0 OK
883*/
884
885static my_bool maria_rtree_fill_reinsert_list(stPageList *ReinsertList,
886 my_off_t page, int level)
887{
888 DBUG_ENTER("maria_rtree_fill_reinsert_list");
889 DBUG_PRINT("rtree", ("page: %lu level: %d", (ulong) page, level));
890 if (ReinsertList->n_pages == ReinsertList->m_pages)
891 {
892 ReinsertList->m_pages += REINSERT_BUFFER_INC;
893 if (!(ReinsertList->pages= (stPageLevel*)my_realloc((uchar*)ReinsertList->pages,
894 ReinsertList->m_pages * sizeof(stPageLevel), MYF(MY_ALLOW_ZERO_PTR))))
895 goto err;
896 }
897 /* save page to ReinsertList */
898 ReinsertList->pages[ReinsertList->n_pages].offs= page;
899 ReinsertList->pages[ReinsertList->n_pages].level= level;
900 ReinsertList->n_pages++;
901 DBUG_RETURN(0);
902
903err:
904 DBUG_RETURN(1); /* purecov: inspected */
905}
906
907
908/*
909 Go down and delete key from the tree
910
911 RETURN
912 -1 Error
913 0 Deleted
914 1 Not found
915 2 Empty leaf
916*/
917
918static int maria_rtree_delete_req(MARIA_HA *info, const MARIA_KEY *key,
919 my_off_t page_pos, uint *page_size,
920 stPageList *ReinsertList, int level)
921{
922 ulong i;
923 uint nod_flag;
924 int res;
925 uchar *page_buf, *last, *k;
926 MARIA_SHARE *share= info->s;
927 MARIA_KEYDEF *keyinfo= key->keyinfo;
928 MARIA_PAGE page;
929 DBUG_ENTER("maria_rtree_delete_req");
930
931 if (!(page_buf= (uchar*) my_alloca((uint) keyinfo->block_length)))
932 {
933 my_errno= HA_ERR_OUT_OF_MEM;
934 DBUG_RETURN(-1); /* purecov: inspected */
935 }
936 if (_ma_fetch_keypage(&page, info, keyinfo, page_pos, PAGECACHE_LOCK_WRITE,
937 DFLT_INIT_HITS, page_buf, 0))
938 goto err;
939 nod_flag= page.node;
940 DBUG_PRINT("rtree", ("page: %lu level: %d nod_flag: %u",
941 (ulong) page_pos, level, nod_flag));
942
943 k= rt_PAGE_FIRST_KEY(share, page_buf, nod_flag);
944 last= rt_PAGE_END(&page);
945
946 for (i= 0;
947 k < last;
948 k= rt_PAGE_NEXT_KEY(share, k, key->data_length, nod_flag), i++)
949 {
950 if (nod_flag)
951 {
952 /* not leaf */
953 if (!maria_rtree_key_cmp(keyinfo->seg, key->data, k, key->data_length,
954 MBR_WITHIN))
955 {
956 switch ((res= maria_rtree_delete_req(info, key,
957 _ma_kpos(nod_flag, k),
958 page_size, ReinsertList,
959 level + 1)))
960 {
961 case 0: /* deleted */
962 {
963 /* test page filling */
964 if (*page_size + key->data_length >=
965 rt_PAGE_MIN_SIZE(keyinfo->block_length))
966 {
967 /* OK */
968 /* Calculate a new key value (MBR) for the shrinked block. */
969 MARIA_KEY tmp_key;
970 tmp_key.keyinfo= keyinfo;
971 tmp_key.data= k;
972 tmp_key.data_length= key->data_length;
973 tmp_key.ref_length= key->ref_length;
974 tmp_key.flag= 0; /* Safety */
975
976 if (maria_rtree_set_key_mbr(info, &tmp_key,
977 _ma_kpos(nod_flag, k)))
978 goto err;
979 if (share->now_transactional &&
980 _ma_log_change(&page, k, key->data_length,
981 KEY_OP_DEBUG_RTREE_SET_KEY))
982 goto err;
983 page_mark_changed(info, &page)
984 if (_ma_write_keypage(&page,
985 PAGECACHE_LOCK_LEFT_WRITELOCKED,
986 DFLT_INIT_HITS))
987 goto err;
988 }
989 else
990 {
991 /*
992 Too small: delete key & add it descendant to reinsert list.
993 Store position and level of the block so that it can be
994 accessed later for inserting the remaining keys.
995 */
996 DBUG_PRINT("rtree", ("too small. move block to reinsert list"));
997 if (maria_rtree_fill_reinsert_list(ReinsertList,
998 _ma_kpos(nod_flag, k),
999 level + 1))
1000 goto err;
1001 /*
1002 Delete the key that references the block. This makes the
1003 block disappear from the index. Hence we need to insert
1004 its remaining keys later. Note: if the block is a branch
1005 block, we do not only remove this block, but the whole
1006 subtree. So we need to re-insert its keys on the same
1007 level later to reintegrate the subtrees.
1008 */
1009 if (maria_rtree_delete_key(&page, k, key->data_length))
1010 goto err;
1011 page_mark_changed(info, &page);
1012 if (_ma_write_keypage(&page, PAGECACHE_LOCK_LEFT_WRITELOCKED,
1013 DFLT_INIT_HITS))
1014 goto err;
1015 *page_size= page.size;
1016 }
1017
1018 goto ok;
1019 }
1020 case 1: /* not found - continue searching */
1021 {
1022 break;
1023 }
1024 case 2: /* vacuous case: last key in the leaf */
1025 {
1026 if (maria_rtree_delete_key(&page, k, key->data_length))
1027 goto err;
1028 page_mark_changed(info, &page);
1029 if (_ma_write_keypage(&page, PAGECACHE_LOCK_LEFT_WRITELOCKED,
1030 DFLT_INIT_HITS))
1031 goto err;
1032 *page_size= page.size;
1033 res= 0;
1034 goto ok;
1035 }
1036 default: /* error */
1037 case -1:
1038 {
1039 goto err;
1040 }
1041 }
1042 }
1043 }
1044 else
1045 {
1046 /* leaf */
1047 if (!maria_rtree_key_cmp(keyinfo->seg, key->data, k, key->data_length,
1048 MBR_EQUAL | MBR_DATA))
1049 {
1050 page_mark_changed(info, &page);
1051 if (maria_rtree_delete_key(&page, k, key->data_length))
1052 goto err;
1053 *page_size= page.size;
1054 if (*page_size == info->s->keypage_header)
1055 {
1056 /* last key in the leaf */
1057 res= 2;
1058 if (_ma_dispose(info, page.pos, 0))
1059 goto err;
1060 }
1061 else
1062 {
1063 res= 0;
1064 if (_ma_write_keypage(&page, PAGECACHE_LOCK_LEFT_WRITELOCKED,
1065 DFLT_INIT_HITS))
1066 goto err;
1067 }
1068 goto ok;
1069 }
1070 }
1071 }
1072 res= 1;
1073
1074ok:
1075 my_afree(page_buf);
1076 DBUG_RETURN(res);
1077
1078err:
1079 my_afree(page_buf);
1080 DBUG_RETURN(-1); /* purecov: inspected */
1081}
1082
1083
1084/*
1085 Delete key - interface function
1086
1087 RETURN
1088 1 Error
1089 0 Deleted
1090*/
1091
1092my_bool maria_rtree_delete(MARIA_HA *info, MARIA_KEY *key)
1093{
1094 MARIA_SHARE *share= info->s;
1095 my_off_t new_root= share->state.key_root[key->keyinfo->key_nr];
1096 int res;
1097 LSN lsn= LSN_IMPOSSIBLE;
1098 DBUG_ENTER("maria_rtree_delete");
1099
1100 if ((res= maria_rtree_real_delete(info, key, &new_root)))
1101 goto err;
1102
1103 if (share->now_transactional)
1104 res= _ma_write_undo_key_delete(info, key, new_root, &lsn);
1105 else
1106 share->state.key_root[key->keyinfo->key_nr]= new_root;
1107
1108err:
1109 _ma_fast_unlock_key_del(info);
1110 _ma_unpin_all_pages_and_finalize_row(info, lsn);
1111 DBUG_RETURN(res != 0);
1112}
1113
1114
1115my_bool maria_rtree_real_delete(MARIA_HA *info, MARIA_KEY *key,
1116 my_off_t *root)
1117{
1118 uint page_size;
1119 stPageList ReinsertList;
1120 my_off_t old_root;
1121 MARIA_SHARE *share= info->s;
1122 MARIA_KEYDEF *keyinfo= key->keyinfo;
1123 uint key_data_length= key->data_length;
1124 DBUG_ENTER("maria_rtree_real_delete");
1125
1126 if ((old_root= share->state.key_root[keyinfo->key_nr]) ==
1127 HA_OFFSET_ERROR)
1128 {
1129 my_errno= HA_ERR_END_OF_FILE;
1130 DBUG_RETURN(1); /* purecov: inspected */
1131 }
1132 DBUG_PRINT("rtree", ("starting deletion at root page: %lu",
1133 (ulong) old_root));
1134
1135 ReinsertList.pages= NULL;
1136 ReinsertList.n_pages= 0;
1137 ReinsertList.m_pages= 0;
1138
1139 switch (maria_rtree_delete_req(info, key, old_root, &page_size,
1140 &ReinsertList, 0)) {
1141 case 2: /* empty */
1142 {
1143 *root= HA_OFFSET_ERROR;
1144 break;
1145 }
1146 case 0: /* deleted */
1147 {
1148 uint nod_flag;
1149 ulong i;
1150 uchar *page_buf;
1151 MARIA_PAGE page;
1152 MARIA_KEY tmp_key;
1153 tmp_key.keyinfo= key->keyinfo;
1154 tmp_key.data_length= key->data_length;
1155 tmp_key.ref_length= key->ref_length;
1156 tmp_key.flag= 0; /* Safety */
1157
1158 if (ReinsertList.n_pages)
1159 {
1160 if (!(page_buf= (uchar*) my_alloca((uint) keyinfo->block_length)))
1161 {
1162 my_errno= HA_ERR_OUT_OF_MEM;
1163 goto err;
1164 }
1165
1166 for (i= 0; i < ReinsertList.n_pages; ++i)
1167 {
1168 uchar *k, *last;
1169 if (_ma_fetch_keypage(&page, info, keyinfo, ReinsertList.pages[i].offs,
1170 PAGECACHE_LOCK_WRITE,
1171 DFLT_INIT_HITS, page_buf, 0))
1172 goto err;
1173 nod_flag= page.node;
1174 DBUG_PRINT("rtree", ("reinserting keys from "
1175 "page: %lu level: %d nod_flag: %u",
1176 (ulong) ReinsertList.pages[i].offs,
1177 ReinsertList.pages[i].level, nod_flag));
1178
1179 k= rt_PAGE_FIRST_KEY(share, page.buff, nod_flag);
1180 last= rt_PAGE_END(&page);
1181 for (; k < last; k= rt_PAGE_NEXT_KEY(share, k, key_data_length,
1182 nod_flag))
1183 {
1184 int res;
1185 tmp_key.data= k;
1186 if ((res= maria_rtree_insert_level(info, &tmp_key,
1187 ReinsertList.pages[i].level,
1188 root)) == -1)
1189 {
1190 my_afree(page_buf);
1191 goto err;
1192 }
1193 if (res)
1194 {
1195 uint j;
1196 DBUG_PRINT("rtree", ("root has been split, adjust levels"));
1197 for (j= i; j < ReinsertList.n_pages; j++)
1198 {
1199 ReinsertList.pages[j].level++;
1200 DBUG_PRINT("rtree", ("keys from page: %lu now level: %d",
1201 (ulong) ReinsertList.pages[i].offs,
1202 ReinsertList.pages[i].level));
1203 }
1204 }
1205 }
1206 page_mark_changed(info, &page);
1207 if (_ma_dispose(info, page.pos, 0))
1208 {
1209 my_afree(page_buf);
1210 goto err;
1211 }
1212 }
1213 my_afree(page_buf);
1214 my_free(ReinsertList.pages);
1215 }
1216
1217 /* check for redundant root (not leaf, 1 child) and eliminate */
1218 if ((old_root= *root) == HA_OFFSET_ERROR)
1219 goto err;
1220 if (_ma_fetch_keypage(&page, info, keyinfo, old_root,
1221 PAGECACHE_LOCK_WRITE,
1222 DFLT_INIT_HITS, info->buff, 0))
1223 goto err;
1224 nod_flag= page.node;
1225 if (nod_flag && (page.size == share->keypage_header + key_data_length +
1226 nod_flag))
1227 {
1228 *root= _ma_kpos(nod_flag,
1229 rt_PAGE_FIRST_KEY(share, info->buff, nod_flag));
1230 page_mark_changed(info, &page);
1231 if (_ma_dispose(info, page.pos, 0))
1232 goto err;
1233 }
1234 info->update= HA_STATE_DELETED;
1235 break;
1236 }
1237 case 1: /* not found */
1238 {
1239 my_errno= HA_ERR_KEY_NOT_FOUND;
1240 goto err;
1241 }
1242 case -1: /* error */
1243 default:
1244 goto err; /* purecov: inspected */
1245 }
1246 DBUG_RETURN(0);
1247
1248err:
1249 DBUG_RETURN(1);
1250}
1251
1252
1253/*
1254 Estimate number of suitable keys in the tree
1255
1256 RETURN
1257 estimated value
1258*/
1259
1260ha_rows maria_rtree_estimate(MARIA_HA *info, MARIA_KEY *key, uint32 flag)
1261{
1262 my_off_t root;
1263 uint i= 0;
1264 uint nod_flag, key_data_length;
1265 uchar *page_buf, *k, *last;
1266 double area= 0;
1267 ha_rows res= 0;
1268 MARIA_SHARE *share= info->s;
1269 MARIA_KEYDEF *keyinfo= key->keyinfo;
1270 MARIA_PAGE page;
1271
1272 if (flag & MBR_DISJOINT)
1273 return HA_POS_ERROR;
1274
1275 if ((root= share->state.key_root[key->keyinfo->key_nr]) == HA_OFFSET_ERROR)
1276 return HA_POS_ERROR;
1277 if (!(page_buf= (uchar*) my_alloca((uint) keyinfo->block_length)))
1278 return HA_POS_ERROR;
1279 if (_ma_fetch_keypage(&page, info, keyinfo, root,
1280 PAGECACHE_LOCK_LEFT_UNLOCKED, DFLT_INIT_HITS, page_buf,
1281 0))
1282 goto err;
1283 nod_flag= page.node;
1284
1285 key_data_length= key->data_length;
1286
1287 k= rt_PAGE_FIRST_KEY(share, page.buff, nod_flag);
1288 last= rt_PAGE_END(&page);
1289
1290 for (; k < last;
1291 k= rt_PAGE_NEXT_KEY(share, k, key_data_length, nod_flag), i++)
1292 {
1293 if (nod_flag)
1294 {
1295 double k_area= maria_rtree_rect_volume(keyinfo->seg, k, key_data_length);
1296
1297 /* The following should be safe, even if we compare doubles */
1298 if (k_area == 0)
1299 {
1300 if (flag & (MBR_CONTAIN | MBR_INTERSECT))
1301 {
1302 area+= 1;
1303 }
1304 else if (flag & (MBR_WITHIN | MBR_EQUAL))
1305 {
1306 if (!maria_rtree_key_cmp(keyinfo->seg, key->data, k, key_data_length,
1307 MBR_WITHIN))
1308 area+= 1;
1309 }
1310 else
1311 goto err;
1312 }
1313 else
1314 {
1315 if (flag & (MBR_CONTAIN | MBR_INTERSECT))
1316 {
1317 area+= maria_rtree_overlapping_area(keyinfo->seg, key->data, k,
1318 key_data_length) / k_area;
1319 }
1320 else if (flag & (MBR_WITHIN | MBR_EQUAL))
1321 {
1322 if (!maria_rtree_key_cmp(keyinfo->seg, key->data, k, key_data_length,
1323 MBR_WITHIN))
1324 area+= (maria_rtree_rect_volume(keyinfo->seg, key->data,
1325 key_data_length) / k_area);
1326 }
1327 else
1328 goto err;
1329 }
1330 }
1331 else
1332 {
1333 if (!maria_rtree_key_cmp(keyinfo->seg, key->data, k, key_data_length,
1334 flag))
1335 ++res;
1336 }
1337 }
1338 if (nod_flag)
1339 {
1340 if (i)
1341 res= (ha_rows) (area / i * info->state->records);
1342 else
1343 res= HA_POS_ERROR;
1344 }
1345
1346 my_afree(page_buf);
1347 return res;
1348
1349err:
1350 my_afree(page_buf);
1351 return HA_POS_ERROR;
1352}
1353
1354#endif /*HAVE_RTREE_KEYS*/
1355