Bug#25673 - spatial index corruption, error 126

          incorrect key file for table

In certain cases it could happen that deleting a row could
corrupt an RTREE index.

According to Guttman's algorithm, page underflow is handled
by storing the page in a list for later re-insertion. The
keys from the stored pages have to be inserted into the
remaining pages of the same level of the tree. Hence the
level number is stored in the re-insertion list together
with the page.

In the MySQL RTree implementation the level counts from zero
at the root page, increasing numbers for levels down the tree.

If during re-insertion of the keys the tree height grows, all
level numbers become invalid. The remaining keys will be
inserted at the wrong level.

The fix is to increment the level numbers stored in the
reinsert list after a split of the root block during reinsertion.
parent cf9aca84
This diff is collapsed.
...@@ -35,6 +35,7 @@ int rtree_add_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key, ...@@ -35,6 +35,7 @@ int rtree_add_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
{ {
uint page_size = mi_getint(page_buf); uint page_size = mi_getint(page_buf);
uint nod_flag = mi_test_if_nod(page_buf); uint nod_flag = mi_test_if_nod(page_buf);
DBUG_ENTER("rtree_add_key");
if (page_size + key_length + info->s->base.rec_reflength <= if (page_size + key_length + info->s->base.rec_reflength <=
keyinfo->block_length) keyinfo->block_length)
...@@ -43,22 +44,26 @@ int rtree_add_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key, ...@@ -43,22 +44,26 @@ int rtree_add_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
if (nod_flag) if (nod_flag)
{ {
/* save key */ /* save key */
DBUG_ASSERT(_mi_kpos(nod_flag, key) < info->state->key_file_length);
memcpy(rt_PAGE_END(page_buf), key - nod_flag, key_length + nod_flag); memcpy(rt_PAGE_END(page_buf), key - nod_flag, key_length + nod_flag);
page_size += key_length + nod_flag; page_size += key_length + nod_flag;
} }
else else
{ {
/* save key */ /* save key */
DBUG_ASSERT(_mi_dpos(info, nod_flag, key + key_length +
info->s->base.rec_reflength) <
info->state->data_file_length + info->s->base.pack_reclength);
memcpy(rt_PAGE_END(page_buf), key, key_length + memcpy(rt_PAGE_END(page_buf), key, key_length +
info->s->base.rec_reflength); info->s->base.rec_reflength);
page_size += key_length + info->s->base.rec_reflength; page_size += key_length + info->s->base.rec_reflength;
} }
mi_putint(page_buf, page_size, nod_flag); mi_putint(page_buf, page_size, nod_flag);
return 0; DBUG_RETURN(0);
} }
return (rtree_split_page(info, keyinfo, page_buf, key, key_length, DBUG_RETURN((rtree_split_page(info, keyinfo, page_buf, key, key_length,
new_page) ? -1 : 1); new_page) ? -1 : 1));
} }
/* /*
...@@ -90,11 +95,13 @@ int rtree_delete_key(MI_INFO *info, uchar *page_buf, uchar *key, ...@@ -90,11 +95,13 @@ int rtree_delete_key(MI_INFO *info, uchar *page_buf, uchar *key,
int rtree_set_key_mbr(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key, int rtree_set_key_mbr(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
uint key_length, my_off_t child_page) uint key_length, my_off_t child_page)
{ {
DBUG_ENTER("rtree_set_key_mbr");
if (!_mi_fetch_keypage(info, keyinfo, child_page, if (!_mi_fetch_keypage(info, keyinfo, child_page,
DFLT_INIT_HITS, info->buff, 0)) DFLT_INIT_HITS, info->buff, 0))
return -1; DBUG_RETURN(-1); /* purecov: inspected */
return rtree_page_mbr(info, keyinfo->seg, info->buff, key, key_length); DBUG_RETURN(rtree_page_mbr(info, keyinfo->seg, info->buff, key, key_length));
} }
#endif /*HAVE_RTREE_KEYS*/ #endif /*HAVE_RTREE_KEYS*/
...@@ -268,13 +268,15 @@ int rtree_split_page(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, uchar *key, ...@@ -268,13 +268,15 @@ int rtree_split_page(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, uchar *key,
info->s->base.rec_reflength); info->s->base.rec_reflength);
int max_keys = (mi_getint(page)-2) / (full_length); int max_keys = (mi_getint(page)-2) / (full_length);
DBUG_ENTER("rtree_split_page");
DBUG_PRINT("rtree", ("splitting block"));
n_dim = keyinfo->keysegs / 2; n_dim = keyinfo->keysegs / 2;
if (!(coord_buf= (double*) my_alloca(n_dim * 2 * sizeof(double) * if (!(coord_buf= (double*) my_alloca(n_dim * 2 * sizeof(double) *
(max_keys + 1 + 4) + (max_keys + 1 + 4) +
sizeof(SplitStruct) * (max_keys + 1)))) sizeof(SplitStruct) * (max_keys + 1))))
return -1; DBUG_RETURN(-1); /* purecov: inspected */
task= (SplitStruct *)(coord_buf + n_dim * 2 * (max_keys + 1 + 4)); task= (SplitStruct *)(coord_buf + n_dim * 2 * (max_keys + 1 + 4));
...@@ -346,12 +348,13 @@ int rtree_split_page(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, uchar *key, ...@@ -346,12 +348,13 @@ int rtree_split_page(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, uchar *key,
else else
err_code= _mi_write_keypage(info, keyinfo, *new_page_offs, err_code= _mi_write_keypage(info, keyinfo, *new_page_offs,
DFLT_INIT_HITS, new_page); DFLT_INIT_HITS, new_page);
DBUG_PRINT("rtree", ("split new block: %lu", (ulong) *new_page_offs));
my_afree((byte*)new_page); my_afree((byte*)new_page);
split_err: split_err:
my_afree((byte*) coord_buf); my_afree((byte*) coord_buf);
return err_code; DBUG_RETURN(err_code);
} }
#endif /*HAVE_RTREE_KEYS*/ #endif /*HAVE_RTREE_KEYS*/
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment