/** Restore the stored position of a persistent cursor bufferfixing the page */ static bool
rtr_cur_restore_position(
btr_cur_t* cursor, /*!< in: detached persistent cursor */
ulint level, /*!< in: index level */
mtr_t* mtr); /*!< in: mtr */
/*************************************************************//**
Pop out used parent path entry, until we find the parent with matching
page number */ static void
rtr_adjust_parent_path( /*===================*/
rtr_info_t* rtr_info, /* R-Tree info struct */
ulint page_no) /* page number to look for */
{ while (!rtr_info->parent_path->empty()) { if (rtr_info->parent_path->back().child_no == page_no) { break;
} else { if (rtr_info->parent_path->back().cursor) {
btr_pcur_close(
rtr_info->parent_path->back().cursor);
ut_free(rtr_info->parent_path->back().cursor);
}
switch (latch_mode) {
uint32_t left_page_no;
uint32_t right_page_no; default:
ut_ad(latch_mode == BTR_CONT_MODIFY_TREE); break; case BTR_MODIFY_TREE: /* It is exclusive for other operations which calls
btr_page_set_prev() */
ut_ad(mtr->memo_contains_flagged(&cursor->index()->lock,
MTR_MEMO_X_LOCK
| MTR_MEMO_SX_LOCK)); /* x-latch also siblings from left to right */
left_page_no = btr_page_get_prev(block->page.frame);
if (left_page_no != FIL_NULL) {
btr_block_get(*cursor->index(), left_page_no,
RW_X_LATCH, mtr);
}
/* There should be no insert coming to this function. Only
mode with BTR_MODIFY_* should be delete */
ut_ad(mode != PAGE_CUR_RTREE_INSERT);
ut_ad(my_latch_mode == BTR_SEARCH_LEAF
|| my_latch_mode == BTR_MODIFY_LEAF
|| my_latch_mode == BTR_MODIFY_TREE
|| my_latch_mode == BTR_CONT_MODIFY_TREE);
/* Whether need to track parent information. Only need so
when we do tree altering operations (such as index page merge) */
static_assert(BTR_CONT_MODIFY_TREE == (4 | BTR_MODIFY_TREE), "");
/* Pop each node/page to be searched from "path" structure anddoasearchonit.Pleasenote,anypagesthatarein the"path"structureareprotectedby"page"lock,sotey
cannot be shrunk away */ do {
buf_block_t* block;
node_seq_t path_ssn; const page_t* page;
rw_lock_type_t rw_latch;
/* Maintain the parent path info as well, if needed */ if (need_parent && !skip_parent && !new_split) {
ulint old_level;
ulint new_level;
ut_ad(!rtr_info->parent_path->empty());
/* Cleanup unused parent info */ if (rtr_info->parent_path->back().cursor) {
btr_pcur_close(
rtr_info->parent_path->back().cursor);
ut_free(rtr_info->parent_path->back().cursor);
}
old_level = rtr_info->parent_path->back().level;
rtr_info->parent_path->pop_back();
ut_ad(!rtr_info->parent_path->empty());
/* check whether there is a level change. If so, thecurrentparentpathneedstopopenough
nodes to adjust to the new search page */
new_level = rtr_info->parent_path->back().level;
if (old_level < new_level) {
rtr_adjust_parent_path(
rtr_info, next_rec.page_no);
}
/* If there are splits, push the splitted page. NotethatwehaveSXlockonindex->lock,there
should not be any split/shrink happening here */ if (page_ssn > path_ssn) {
uint32_t next_page_no = btr_page_get_next(page);
rtr_non_leaf_stack_push(
rtr_info->path, next_page_no, path_ssn,
level, 0, NULL, 0);
/*************************************************************//**
Find the next matching record. This function will first exhaust
the copied record listed in the rtr_info->matches vector before
moving to the next page
@returntrueif there is suitable record found, otherwise false */ bool
rtr_pcur_move_to_next( /*==================*/ const dtuple_t* tuple, /*!< in: data tuple; NOTE: n_fields_cmp in tuplemustbesetsothatitcannotget
compared to the node ptr page number field! */
page_cur_mode_t mode, /*!< in: cursor search mode */
btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the
function may release the page latch */
ulint level, /*!< in: target level */
mtr_t* mtr) /*!< in: mtr */
{
rtr_info_t* rtr_info = cursor->btr_cur.rtr_info;
mysql_mutex_lock(&rtr_info->matches->rtr_match_mutex); /* First retrieve the next record on the current page */ if (!rtr_info->matches->matched_recs->empty()) {
rtr_rec_t rec;
rec = rtr_info->matches->matched_recs->back();
rtr_info->matches->matched_recs->pop_back();
cursor->btr_cur.page_cur.block = rtr_info->matches->block;
mysql_mutex_unlock(&rtr_info->matches->rtr_match_mutex);
/* We use these modified search modes on non-leaf levels of the B-tree.TheseletusendupintherightB-treeleaf.Inthatleaf
we use the original search mode. */
search_loop: auto buf_mode= BUF_GET;
rw_lock_type_t rw_latch= RW_NO_LATCH;
if (height)
{ /* We are about to fetch the root or a non-leaf page. */ if (latch_mode != BTR_MODIFY_TREE || height == level) /* If doesn't have SX or X latch of index,
each page should be latched before reading. */
rw_latch= upper_rw_latch;
} elseif (latch_mode <= BTR_MODIFY_LEAF)
rw_latch= rw_lock_type_t(latch_mode);
dberr_t err; auto block_savepoint= mtr->get_savepoint();
buf_block_t *block= buf_page_get_gen(page_id, zip_size, rw_latch, guess,
buf_mode, mtr, &err); if (!block)
{ if (err)
{
err_exit:
btr_read_failed(err, *index);
mtr->rollback_to_savepoint(savepoint);
}
func_exit: if (UNIV_LIKELY_NULL(heap))
mem_heap_free(heap);
if (mbr_adj) /* remember that we will need to adjust parent MBR */
cur->rtr_info->mbr_adj= true;
/* If SSN in memory is not initialized, fetch it from root page */ if (!rtr_get_current_ssn_id(index)) /* FIXME: do this in dict_load_table_one() */
index->set_ssn(page_get_ssn_id(page) + 1);
/* Save the MBR */
cur->rtr_info->thr= thr;
rtr_get_mbr_from_tuple(tuple, &cur->rtr_info->mbr);
#ifdef BTR_CUR_ADAPT
guess= block; #endif
}
if (height == 0)
{ if (rw_latch == RW_NO_LATCH)
{
ut_ad(block == mtr->at_savepoint(block_savepoint));
rtr_latch_leaves(block_savepoint, latch_mode, cur, mtr);
}
switch (latch_mode) { case BTR_MODIFY_TREE: case BTR_CONT_MODIFY_TREE: break; default: if (!latch_by_caller)
{ /* Release the tree s-latch */
mtr->rollback_to_savepoint(savepoint,
savepoint + 1);
block_savepoint--;
root_savepoint--;
} /* release upper blocks */ if (savepoint < block_savepoint)
mtr->rollback_to_savepoint(savepoint, block_savepoint);
}
page_mode= mode;
}
/* Remember the page search mode */
search_mode= page_mode;
/* Some adjustment on search mode, when the page search mode is PAGE_CUR_RTREE_LOCATEorPAGE_CUR_RTREE_INSERT,aswearesearching withMBRs.Whenitisnotthetargetlevel,weshouldsearchall sub-treesthat"CONTAIN"thesearchrange/MBR.Whenitisatthe
target level, the search becomes PAGE_CUR_LE */
if (latch_mode == BTR_MODIFY_TREE || latch_mode == BTR_CONT_MODIFY_TREE) /* Tree are locked, no need for Page Lock to protect the "path" */
cur->rtr_info->need_page_lock= false;
/* Need to use BTR_MODIFY_TREE to do the MBR adjustment */ if (search_mode == PAGE_CUR_RTREE_INSERT && cur->rtr_info->mbr_adj) {
static_assert(BTR_MODIFY_TREE == (8 | BTR_MODIFY_LEAF), "");
if (!(latch_mode & 8)) /* Parent MBR needs updated, should retry with BTR_MODIFY_TREE */ goto func_exit;
cur->rtr_info->mbr_adj= false;
mbr_adj= true;
}
if (found && page_mode == PAGE_CUR_RTREE_GET_FATHER)
cur->low_match= DICT_INDEX_SPATIAL_NODEPTR_SIZE + 1;
} else
{ /* Search for complete index fields. */
up_bytes= low_bytes= 0; if (page_cur_search_with_match(tuple, page_mode, &up_match,
&low_match, &cur->page_cur, nullptr)) {
err= DB_CORRUPTION; goto err_exit;
}
}
/* If this is the desired level, leave the loop */
/* Add Predicate lock if it is serializable isolation
and only if it is in the search case */ if (mode >= PAGE_CUR_CONTAIN && mode != PAGE_CUR_RTREE_INSERT &&
mode != PAGE_CUR_RTREE_LOCATE && cur->rtr_info->need_prdt_lock)
{
lock_prdt_t prdt;
if (page_rec_is_supremum(node_ptr))
{
cur->low_match= 0;
cur->up_match= 0; goto func_exit;
}
/* If we are doing insertion or record locating,
remember the tree nodes we visited */ if (page_mode == PAGE_CUR_RTREE_INSERT ||
(search_mode == PAGE_CUR_RTREE_LOCATE &&
latch_mode != BTR_MODIFY_LEAF))
{ constbool add_latch= latch_mode == BTR_MODIFY_TREE &&
rw_latch == RW_NO_LATCH;
if (add_latch)
{
ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK |
MTR_MEMO_SX_LOCK));
block->page.lock.s_lock();
}
/* Store the parent cursor location */
ut_d(auto num_stored=)
rtr_store_parent_path(block, cur, latch_mode, height + 1, mtr);
if (page_mode == PAGE_CUR_RTREE_INSERT)
{
btr_pcur_t *r_cursor= rtr_get_parent_cursor(cur, height + 1, true); /* If it is insertion, there should be only one parent for
each level traverse */
ut_ad(num_stored == 1);
node_ptr= btr_pcur_get_rec(r_cursor);
}
if (!thr) { /* Purge will U lock the tree instead of take Page Locks */
} else {
btr_cursor->rtr_info->need_page_lock = true;
btr_cursor->rtr_info->thr = thr;
}
MY_ATTRIBUTE((warn_unused_result)) /********************************************************************//**
Returns the upper level node pointer to a R-Tree page. It is assumed
that mtr holds an x-latch on the tree. */ staticconst rec_t* rtr_get_father_node(
ulint level, /*!< in: the tree level of search */ const dtuple_t* tuple, /*!< in: data tuple; NOTE: n_fields_cmp in tuplemustbesetsothatitcannotget
compared to the node ptr page number field! */
btr_cur_t* sea_cur,/*!< in: search cursor */
btr_cur_t* btr_cur,/*!< in/out: tree cursor; the cursor page is
s- or x-latched, but see also above! */
que_thr_t* thr, /*!< in/out: query thread */
ulint page_no,/*!< Current page no */
mtr_t* mtr) /*!< in: mtr */
{ const rec_t* rec = nullptr; auto had_rtr = btr_cur->rtr_info;
ut_d(dict_index_t* const index = btr_cur->index());
/* Try to optimally locate the parent node. Level should always
less than sea_cur->tree_height unless the root is splitting */ if (sea_cur && sea_cur->tree_height > level) {
ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK
| MTR_MEMO_SX_LOCK)); if (rtr_cur_restore_position(sea_cur, level, mtr)) {
btr_pcur_t* r_cursor = rtr_get_parent_cursor(
sea_cur, level, false);
/** Returns the upper level node pointer to a R-Tree page. It is assumed thatmtrholdsanSX-latchorX-latchonthetree.
@return rec_get_offsets() of the node pointer record */ static
rec_offs*
rtr_page_get_father_node_ptr(
rec_offs* offsets,/*!< in: work area for the return value */
mem_heap_t* heap, /*!< in: memory heap to use */
btr_cur_t* sea_cur,/*!< in: search cursor */
btr_cur_t* cursor, /*!< in: cursor pointing to user record, out:cursoronnodepointerrecord,
its page x-latched */
que_thr_t* thr, /*!< in/out: query thread */
mtr_t* mtr) /*!< in: mtr */
{
dtuple_t* tuple;
ulint level;
ulint page_no;
dict_index_t* index;
rtr_mbr_t mbr;
page_no = btr_cur_get_block(cursor)->page.id().page_no();
index = btr_cur_get_index(cursor);
if (btr_node_ptr_get_child_page_no(node_ptr, offsets) != page_no) {
offsets = nullptr;
}
return(offsets);
}
/************************************************************//**
Returns the father block to a page. It is assumed that mtr holds
an X or SX latch on the tree.
@return rec_get_offsets() of the node pointer record */
rec_offs*
rtr_page_get_father_block( /*======================*/
rec_offs* offsets,/*!< in: work area for the return value */
mem_heap_t* heap, /*!< in: memory heap to use */
btr_cur_t* sea_cur,/*!< in: search cursor, contains information
about parent nodes in search */
btr_cur_t* cursor, /*!< out: cursor on node pointer record,
its page x-latched */
que_thr_t* thr, /*!< in/out: query thread */
mtr_t* mtr) /*!< in/out: mtr */
{ const page_t *const page= cursor->block()->page.frame; const rec_t *rec= page_is_comp(page)
? page_rec_next_get<true>(page, page + PAGE_NEW_INFIMUM)
: page_rec_next_get<false>(page, page + PAGE_OLD_INFIMUM); if (!rec) return nullptr;
cursor->page_cur.rec= const_cast<rec_t*>(rec); return rtr_page_get_father_node_ptr(offsets, heap, sea_cur, cursor,
thr, mtr);
}
/*******************************************************************//**
Create a RTree search info structure */
rtr_info_t*
rtr_create_rtr_info( /******************/ bool need_prdt, /*!< in: Whether predicate lock
is needed */ bool init_matches, /*!< in: Whether to initiate the "matches"structureforcollecting
matched leaf records */
que_thr_t* thr, /*!< in/out: query thread */
btr_cur_t* cursor) /*!< in: tree search cursor */
{
rtr_info_t* rtr_info;
dict_index_t* index = cursor->index();
ut_ad(index);
/*******************************************************************//**
Update a btr_cur_t with rtr_info */ void
rtr_info_update_btr( /******************/
btr_cur_t* cursor, /*!< in/out: tree cursor */
rtr_info_t* rtr_info) /*!< in: rtr_info to set to the
cursor */
{
ut_ad(rtr_info);
cursor->rtr_info = rtr_info;
}
/*******************************************************************//**
Initialize a R-Tree Search structure */ void
rtr_init_rtr_info( /****************/
rtr_info_t* rtr_info, /*!< in: rtr_info to set to the
cursor */ bool need_prdt, /*!< in: Whether predicate lock is
needed */
btr_cur_t* cursor, /*!< in: tree search cursor */
dict_index_t* index, /*!< in: index structure */ bool reinit) /*!< in: Whether this is a reinit */
{
ut_ad(rtr_info);
if (!reinit) { /* Reset all members. */
memset(rtr_info, 0, sizeof *rtr_info);
static_assert(PAGE_CUR_UNSUPP == 0, "compatibility");
mysql_mutex_init(rtr_path_mutex_key, &rtr_info->rtr_path_mutex,
nullptr);
}
/**************************************************************//**
Clean up R-Tree search structure */ void
rtr_clean_rtr_info( /*===============*/
rtr_info_t* rtr_info, /*!< in: RTree search info */ bool free_all) /*!< in: need to free rtr_info itself */
{
dict_index_t* index; bool initialized = false;
if (!rtr_info) { return;
}
index = rtr_info->index;
if (index) {
mysql_mutex_lock(&index->rtr_track->rtr_active_mutex);
}
while (rtr_info->parent_path && !rtr_info->parent_path->empty()) {
btr_pcur_t* cur = rtr_info->parent_path->back().cursor;
rtr_info->parent_path->pop_back();
if (index) {
index->rtr_track->rtr_active.remove(rtr_info);
mysql_mutex_unlock(&index->rtr_track->rtr_active_mutex);
}
if (free_all) { if (rtr_info->matches) { if (rtr_info->matches->block) {
buf_block_free(rtr_info->matches->block);
rtr_info->matches->block = nullptr;
}
/**************************************************************//**
Check whether a discarding page is in anyone's search path */ void
rtr_check_discard_page( /*===================*/
dict_index_t* index, /*!< in: index */
btr_cur_t* cursor, /*!< in: cursor on the page to discard: not on
the root page */
buf_block_t* block) /*!< in: block of page to be discarded */
{ const page_id_t id{block->page.id()};
mem_heap_free(heap);
} while (0); #endif/* UNIV_DEBUG */
return(true);
}
/* Page has changed, for R-Tree, the page cannot be shrunk away,
so we search the page and its right siblings */
node_seq_t page_ssn; const page_t* page;
page_cur_t* page_cursor;
node_visit_t* node = rtr_get_parent_node(btr_cur, level, false);
node_seq_t path_ssn = node->seq_no; constunsigned zip_size = index->table->space->zip_size();
uint32_t page_no = node->page_no;
/* Check the page SSN to see if it has been splitted, if so, search
the right page */ if (!ret && page_ssn > path_ssn) {
page_no = btr_page_get_next(page); goto search_again;
}
func_exit:
mem_heap_free(heap);
return(ret);
}
/****************************************************************//**
Copy the leaf level R-tree record, and push it to matched_rec in rtr_info */ static void
rtr_leaf_push_match_rec( /*====================*/ const rec_t* rec, /*!< in: record to copy */
rtr_info_t* rtr_info, /*!< in/out: search stack */
rec_offs* offsets, /*!< in: offsets */ bool is_comp) /*!< in: is compact format */
{
byte* buf;
matched_rec_t* match_rec = rtr_info->matches;
rec_t* copy;
ulint data_len;
rtr_rec_t rtr_rec;
/****************************************************************//**
Generate a shadow copy of the page block header to save the
matched records */ static void
rtr_init_match( /*===========*/
matched_rec_t* matches,/*!< in/out: match to initialize */ const buf_block_t* block, /*!< in: buffer block */ const page_t* page) /*!< in: buffer page */
{
ut_ad(matches->matched_recs->empty());
matches->locked = false;
matches->valid = false; if (!matches->block) {
matches->block = buf_block_alloc();
}
matches->block->page.init(buf_page_t::MEMORY, block->page.id()); /* We have to copy PAGE_*_SUPREMUM_END bytes so that we can
use infimum/supremum of this page as normal btr page for search. */
matches->used = page_is_comp(page)
? PAGE_NEW_SUPREMUM_END
: PAGE_OLD_SUPREMUM_END;
memcpy(matches->block->page.frame, page, matches->used); #ifdef RTR_SEARCH_DIAGNOSTIC
ulint pageno = page_get_page_no(page);
fprintf(stderr, "INNODB_RTR: Searching leaf page %d\n", static_cast<int>(pageno)); #endif/* RTR_SEARCH_DIAGNOSTIC */
}
/****************************************************************//**
Get the bounding box content from an index record */ void
rtr_get_mbr_from_rec( /*=================*/ const rec_t* rec, /*!< in: data tuple */ const rec_offs* offsets,/*!< in: offsets array */
rtr_mbr_t* mbr) /*!< out MBR */
{
ulint rec_f_len; const byte* data;
data = rec_get_nth_field(rec, offsets, 0, &rec_f_len);
rtr_read_mbr(data, mbr);
}
/****************************************************************//**
Get the bounding box content from a MBR data record */ void
rtr_get_mbr_from_tuple( /*===================*/ const dtuple_t* dtuple, /*!< in: data tuple */
rtr_mbr* mbr) /*!< out: mbr to fill */
{ const dfield_t* dtuple_field;
ulint dtuple_f_len;
/** Compare a GIS data tuple to a physical record in rtree non-leaf node. Weneedtocheckthepagenumberfield,sincewedon'tstorepkfieldin rtreenon-leafnode. @param[in]dtupledatatuple @param[in]recR-treerecord
@return whether dtuple is less than rec */ staticbool
cmp_dtuple_rec_with_gis_internal(const dtuple_t* dtuple, const rec_t* rec)
{ const dfield_t *dtuple_field= dtuple_get_nth_field(dtuple, 0);
ut_ad(dfield_get_len(dtuple_field) == DATA_MBR_LEN);
if (cmp_gis_field(PAGE_CUR_WITHIN, dfield_get_data(dtuple_field), rec)) returntrue;
if (page_rec_is_infimum(rec)) {
rec = page_rec_get_next_const(rec); if (UNIV_UNLIKELY(!rec)) { returnfalse;
}
}
/* Check insert tuple size is larger than first rec, and try to
avoid it if possible */ if (mode == PAGE_CUR_RTREE_INSERT && !page_rec_is_supremum(rec)) {
if (rec_offs_size(offsets) < new_rec_size) {
first_rec = rec;
}
/* If this is the left-most page of this index level andthetableisacompressedtable,trytoavoid firstpageasmuchaspossible,astherewillbeproblem
when update MIN_REC rec in compress table */ if (is_buf_block_get_page_zip(block)
&& !page_has_prev(page)
&& page_get_n_recs(page) >= 2) {
rec = page_rec_get_next_const(rec);
}
}
while (!page_rec_is_supremum(rec)) { if (!n_core) { switch (mode) { case PAGE_CUR_CONTAIN: case PAGE_CUR_INTERSECT: case PAGE_CUR_MBR_EQUAL: /* At non-leaf level, we will need to check bothCONTAINandINTERSECTforeitherof
the search mode */
cmp = cmp_dtuple_rec_with_gis(
tuple, rec, PAGE_CUR_CONTAIN);
/* If located, the matching node/rec will be pushed tortr_info->pathfornon-leafnodes,or
rtr_info->matches for leaf nodes */ if (rtr_info && mode != PAGE_CUR_RTREE_INSERT) { if (!n_core) {
uint32_t page_no;
node_seq_t new_seq; bool is_loc;
last_match_rec = rec;
} else { /* This is the insertion case, it will break onceitfindsthefirstMBRthatcanaccomodate
the inserting rec */ break;
}
}
last_rec = rec;
rec = page_rec_get_next_const(rec);
}
/* All records on page are searched */ if (rec && page_rec_is_supremum(rec)) { if (!n_core) { if (!found) { /* No match case, if it is for insertion, thenweselecttherecordthatresultin
least increased area */ if (mode == PAGE_CUR_RTREE_INSERT) {
ut_ad(least_inc < DBL_MAX);
offsets = rec_get_offsets(
best_rec, index, offsets, 0, ULINT_UNDEFINED, &heap);
uint32_t child_no =
btr_node_ptr_get_child_page_no(
best_rec, offsets);
page_cur_position(best_rec, block,
cursor);
rtr_info->mbr_adj = true;
} else { /* Position at the last rec of the
page, if it is not the leaf page */
page_cur_position(last_rec, block,
cursor);
}
} else { /* There are matching records, position
in the last matching records */ if (rtr_info) {
rec = last_match_rec;
page_cur_position(
rec, block, cursor);
}
}
} elseif (rtr_info) { /* Leaf level, no match, position at the
last (supremum) rec */ if (!last_match_rec) {
page_cur_position(rec, block, cursor); goto func_exit;
}
/* There are matched records */
matched_rec_t* match_rec = rtr_info->matches;
/* Verify the record to be positioned is the same
as the last record in matched_rec vector */
offsets2 = rec_get_offsets(test_rec.r_rec, index,
offsets2, index->n_fields,
ULINT_UNDEFINED, &heap);
ut_ad(cmp_rec_rec(test_rec.r_rec, last_match_rec,
offsets2, offsets, index) == 0); #endif/* UNIV_DEBUG */ /* Pop the last match record and position on it */
match_rec->matched_recs->pop_back();
page_cur_position(test_rec.r_rec, match_rec->block,
cursor);
}
} else {
#ifdef UNIV_DEBUG /* Verify that we are positioned at the same child page as pushed in
the path stack */ if (!n_core && (!page_rec_is_supremum(rec) || found)
&& mode != PAGE_CUR_RTREE_INSERT) {
ulint page_no;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.