| 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 |  | 
| 31 | typedef struct st_page_level | 
| 32 | { | 
| 33 |   uint level; | 
| 34 |   my_off_t offs; | 
| 35 | } stPageLevel; | 
| 36 |  | 
| 37 | typedef 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 |  | 
| 57 | static 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 |  | 
| 167 | ok: | 
| 168 |   my_afree(page_buf); | 
| 169 |   return res; | 
| 170 |  | 
| 171 | err: | 
| 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 |  | 
| 193 | int 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 |  | 
| 247 | int 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 |  | 
| 324 | static 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 |  | 
| 424 | ok: | 
| 425 |   my_afree(page_buf); | 
| 426 |   return res; | 
| 427 |  | 
| 428 | err: | 
| 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 |  | 
| 444 | int 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 |  | 
| 471 | int 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 | 
| 522 | static 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 | 
| 554 | static 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 |  | 
| 599 | static 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 |  | 
| 697 | ok: | 
| 698 |   my_afree(page_buf); | 
| 699 |   DBUG_RETURN(res); | 
| 700 |  | 
| 701 | err: | 
| 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 |  | 
| 721 | int 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; | 
| 825 | err: | 
| 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 |  | 
| 848 | my_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); | 
| 872 | err: | 
| 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 |  | 
| 885 | static 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 |  | 
| 903 | err: | 
| 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 |  | 
| 918 | static 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 |  | 
| 1074 | ok: | 
| 1075 |   my_afree(page_buf); | 
| 1076 |   DBUG_RETURN(res); | 
| 1077 |  | 
| 1078 | err: | 
| 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 |  | 
| 1092 | my_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 |  | 
| 1108 | err: | 
| 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 |  | 
| 1115 | my_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 |  | 
| 1248 | err: | 
| 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 |  | 
| 1260 | ha_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 |  | 
| 1349 | err: | 
| 1350 |   my_afree(page_buf); | 
| 1351 |   return HA_POS_ERROR; | 
| 1352 | } | 
| 1353 |  | 
| 1354 | #endif /*HAVE_RTREE_KEYS*/ | 
| 1355 |  |