📄 row0sel.c
字号:
row_sel_build_prev_vers(/*====================*/ /* out: DB_SUCCESS or error code */ read_view_t* read_view, /* in: read view */ plan_t* plan, /* in: plan node for table */ rec_t* rec, /* in: record in a clustered index */ ulint** offsets, /* in/out: offsets returned by rec_get_offsets(rec, plan->index) */ mem_heap_t** offset_heap, /* in/out: memory heap from which the offsets are allocated */ rec_t** old_vers, /* out: old version, or NULL if the record does not exist in the view: i.e., it was freshly inserted afterwards */ mtr_t* mtr) /* in: mtr */{ ulint err; if (plan->old_vers_heap) { mem_heap_empty(plan->old_vers_heap); } else { plan->old_vers_heap = mem_heap_create(512); } err = row_vers_build_for_consistent_read(rec, mtr, plan->index, offsets, read_view, offset_heap, plan->old_vers_heap, old_vers); return(err);}/*************************************************************************Tests the conditions which determine when the index segment we are searchingthrough has been exhausted. */UNIV_INLINEiboolrow_sel_test_end_conds(/*===================*/ /* out: TRUE if row passed the tests */ plan_t* plan) /* in: plan for the table; the column values must already have been retrieved and the right sides of comparisons evaluated */{ func_node_t* cond; /* All conditions in end_conds are comparisons of a column to an expression */ cond = UT_LIST_GET_FIRST(plan->end_conds); while (cond) { /* Evaluate the left side of the comparison, i.e., get the column value if there is an indirection */ eval_sym(cond->args); /* Do the comparison */ if (!eval_cmp(cond)) { return(FALSE); } cond = UT_LIST_GET_NEXT(cond_list, cond); } return(TRUE);}/*************************************************************************Tests the other conditions. */UNIV_INLINEiboolrow_sel_test_other_conds(/*=====================*/ /* out: TRUE if row passed the tests */ plan_t* plan) /* in: plan for the table; the column values must already have been retrieved */{ func_node_t* cond; cond = UT_LIST_GET_FIRST(plan->other_conds); while (cond) { eval_exp(cond); if (!eval_node_get_ibool_val(cond)) { return(FALSE); } cond = UT_LIST_GET_NEXT(cond_list, cond); } return(TRUE);}/*************************************************************************Retrieves the clustered index record corresponding to a record in anon-clustered index. Does the necessary locking. */staticulintrow_sel_get_clust_rec(/*==================*/ /* out: DB_SUCCESS or error code */ sel_node_t* node, /* in: select_node */ plan_t* plan, /* in: plan node for table */ rec_t* rec, /* in: record in a non-clustered index */ que_thr_t* thr, /* in: query thread */ rec_t** out_rec,/* out: clustered record or an old version of it, NULL if the old version did not exist in the read view, i.e., it was a fresh inserted version */ mtr_t* mtr) /* in: mtr used to get access to the non-clustered record; the same mtr is used to access the clustered index */{ dict_index_t* index; rec_t* clust_rec; rec_t* old_vers; ulint err; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; *offsets_ = (sizeof offsets_) / sizeof *offsets_; *out_rec = NULL; offsets = rec_get_offsets(rec, btr_pcur_get_btr_cur(&plan->pcur)->index, offsets, ULINT_UNDEFINED, &heap); row_build_row_ref_fast(plan->clust_ref, plan->clust_map, rec, offsets); index = dict_table_get_first_index(plan->table); btr_pcur_open_with_no_init(index, plan->clust_ref, PAGE_CUR_LE, node->latch_mode, &(plan->clust_pcur), 0, mtr); clust_rec = btr_pcur_get_rec(&(plan->clust_pcur)); /* Note: only if the search ends up on a non-infimum record is the low_match value the real match to the search tuple */ if (!page_rec_is_user_rec(clust_rec) || btr_pcur_get_low_match(&(plan->clust_pcur)) < dict_index_get_n_unique(index)) { ut_a(rec_get_deleted_flag(rec, plan->table->comp)); ut_a(node->read_view); /* In a rare case it is possible that no clust rec is found for a delete-marked secondary index record: if in row0umod.c in row_undo_mod_remove_clust_low() we have already removed the clust rec, while purge is still cleaning and removing secondary index records associated with earlier versions of the clustered index record. In that case we know that the clustered index record did not exist in the read view of trx. */ goto func_exit; } offsets = rec_get_offsets(clust_rec, index, offsets, ULINT_UNDEFINED, &heap); if (!node->read_view) { /* Try to place a lock on the index record */ /* If innodb_locks_unsafe_for_binlog option is used, we lock only the record, i.e., next-key locking is not used. */ ulint lock_type; if (srv_locks_unsafe_for_binlog) { lock_type = LOCK_REC_NOT_GAP; } else { lock_type = LOCK_ORDINARY; } err = lock_clust_rec_read_check_and_lock(0, clust_rec, index, offsets, node->row_lock_mode, lock_type, thr); if (err != DB_SUCCESS) { goto err_exit; } } else { /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ old_vers = NULL; if (!lock_clust_rec_cons_read_sees(clust_rec, index, offsets, node->read_view)) { err = row_sel_build_prev_vers(node->read_view, plan, clust_rec, &offsets, &heap, &old_vers, mtr); if (err != DB_SUCCESS) { goto err_exit; } clust_rec = old_vers; if (clust_rec == NULL) { goto func_exit; } } /* If we had to go to an earlier version of row or the secondary index record is delete marked, then it may be that the secondary index record corresponding to clust_rec (or old_vers) is not rec; in that case we must ignore such row because in our snapshot rec would not have existed. Remember that from rec we cannot see directly which transaction id corresponds to it: we have to go to the clustered index record. A query where we want to fetch all rows where the secondary index value is in some interval would return a wrong result if we would not drop rows which we come to visit through secondary index records that would not really exist in our snapshot. */ if ((old_vers || rec_get_deleted_flag(rec, plan->table->comp)) && !row_sel_sec_rec_is_for_clust_rec(rec, plan->index, clust_rec, index)) { goto func_exit; } } /* Fetch the columns needed in test conditions */ row_sel_fetch_columns(index, clust_rec, offsets, UT_LIST_GET_FIRST(plan->columns)); *out_rec = clust_rec;func_exit: err = DB_SUCCESS;err_exit: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err);}/*************************************************************************Sets a lock on a record. */UNIV_INLINEulintsel_set_rec_lock(/*=============*/ /* out: DB_SUCCESS or error code */ rec_t* rec, /* in: record */ dict_index_t* index, /* in: index */ const ulint* offsets,/* in: rec_get_offsets(rec, index) */ ulint mode, /* in: lock mode */ ulint type, /* in: LOCK_ORDINARY, LOCK_GAP, or LOC_REC_NOT_GAP */ que_thr_t* thr) /* in: query thread */ { trx_t* trx; ulint err; trx = thr_get_trx(thr); if (UT_LIST_GET_LEN(trx->trx_locks) > 10000) { if (buf_LRU_buf_pool_running_out()) { return(DB_LOCK_TABLE_FULL); } } if (index->type & DICT_CLUSTERED) { err = lock_clust_rec_read_check_and_lock(0, rec, index, offsets, mode, type, thr); } else { err = lock_sec_rec_read_check_and_lock(0, rec, index, offsets, mode, type, thr); } return(err);}/*************************************************************************Opens a pcur to a table index. */staticvoidrow_sel_open_pcur(/*==============*/ sel_node_t* node, /* in: select node */ plan_t* plan, /* in: table plan */ ibool search_latch_locked, /* in: TRUE if the thread currently has the search latch locked in s-mode */ mtr_t* mtr) /* in: mtr */{ dict_index_t* index; func_node_t* cond; que_node_t* exp; ulint n_fields; ulint has_search_latch = 0; /* RW_S_LATCH or 0 */ ulint i; if (search_latch_locked) { has_search_latch = RW_S_LATCH; } index = plan->index; /* Calculate the value of the search tuple: the exact match columns get their expressions evaluated when we evaluate the right sides of end_conds */ cond = UT_LIST_GET_FIRST(plan->end_conds); while (cond) { eval_exp(que_node_get_next(cond->args)); cond = UT_LIST_GET_NEXT(cond_list, cond); } if (plan->tuple) { n_fields = dtuple_get_n_fields(plan->tuple); if (plan->n_exact_match < n_fields) { /* There is a non-exact match field which must be evaluated separately */ eval_exp(plan->tuple_exps[n_fields - 1]); } for (i = 0; i < n_fields; i++) { exp = plan->tuple_exps[i]; dfield_copy_data(dtuple_get_nth_field(plan->tuple, i), que_node_get_val(exp)); } /* Open pcur to the index */ btr_pcur_open_with_no_init(index, plan->tuple, plan->mode, node->latch_mode, &(plan->pcur), has_search_latch, mtr); } else { /* Open the cursor to the start or the end of the index (FALSE: no init) */ btr_pcur_open_at_index_side(plan->asc, index, node->latch_mode, &(plan->pcur), FALSE, mtr); } ut_ad(plan->n_rows_prefetched == 0); ut_ad(plan->n_rows_fetched == 0); ut_ad(plan->cursor_at_end == FALSE); plan->pcur_is_open = TRUE;}/*************************************************************************Restores a stored pcur position to a table index. */staticiboolrow_sel_restore_pcur_pos(/*=====================*/ /* out: TRUE if the cursor should be moved to the next record after we return from this function (moved to the previous, in the case of a descending cursor) without processing again the current cursor record */ sel_node_t* node, /* in: select node */ plan_t* plan, /* in: table plan */ mtr_t* mtr) /* in: mtr */{ ibool equal_position; ulint relative_position; ut_ad(!plan->cursor_at_end); relative_position = btr_pcur_get_rel_pos(&(plan->pcur)); equal_position = btr_pcur_restore_position(node->latch_mode, &(plan->pcur), mtr); /* If the cursor is traveling upwards, and relative_position is (1) BTR_PCUR_BEFORE: this is not allowed, as we did not have a lock yet on the successor of the page infimum; (2) BTR_PCUR_AFTER: btr_pcur_restore_position placed the cursor on the first record GREATER than the predecessor of a page supremum; we have not yet processed the cursor record: no need to move the cursor to the next record; (3) BTR_PCUR_ON: btr_pcur_restore_position placed the cursor on the last record LESS or EQUAL to the old stored user record; (a) if equal_position is FALSE, this means that the cursor is now on a record less than the old user record, and we must move to the next record; (b) if equal_position is TRUE, then if plan->stored_cursor_rec_processed is TRUE, we must move to the next record, else there is no need to move the cursor. */ if (plan->asc) { if (relative_position == BTR_PCUR_ON) { if (equal_position) { return(plan->stored_cursor_rec_processed); } return(TRUE); } ut_ad(relative_position == BTR_PCUR_AFTER || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE); return(FALSE); } /* If the cursor is traveling downwards, and relative_position is (1) BTR_PCUR_BEFORE: btr_pcur_restore_position placed the cursor on the last record LESS than the successor of a page infimum; we have not processed the cursor record: no need to move the cursor; (2) BTR_PCUR_AFTER: btr_pcur_restore_position placed the cursor on the first record GREATER than the predecessor of a page supremum; we have processed the cursor record: we should move the cursor to the previous record; (3) BTR_PCUR_ON: btr_pcur_restore_position placed the cursor on the last record LESS or EQUAL to the old stored user record; (a) if equal_position is FALSE, this means that the cursor is now on a record less than the old user record, and we need not move to the previous record; (b) if equal_position is TRUE, then if plan->stored_cursor_rec_processed is TRUE, we must move to the previous record, else there is no need to move the cursor. */ if (relative_position == BTR_PCUR_BEFORE || relative_position == BTR_PCUR_BEFORE_FIRST_IN_TREE) { return(FALSE); } if (relative_position == BTR_PCUR_ON) { if (equal_position) { return(plan->stored_cursor_rec_processed); } return(FALSE); } ut_ad(relative_position == BTR_PCUR_AFTER || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE); return(TRUE);}/*************************************************************************Resets a plan cursor to a closed state. */UNIV_INLINEvoidplan_reset_cursor(/*==============*/ plan_t* plan) /* in: plan */{ plan->pcur_is_open = FALSE; plan->cursor_at_end = FALSE; plan->n_rows_fetched = 0; plan->n_rows_prefetched = 0;} /*************************************************************************Tries to do a shortcut to fetch a clustered index record with a unique key,using the hash index if possible (not always). */staticulintrow_sel_try_search_shortcut(/*========================*/ /* out: SEL_FOUND, SEL_EXHAUSTED, SEL_RETRY */ sel_node_t* node, /* in: select node for a consistent read */ plan_t* plan, /* in: plan for a unique search in clustered index */ mtr_t* mtr) /* in: mtr */{ dict_index_t* index; rec_t* rec; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; ulint ret; *offsets_ = (sizeof offsets_) / sizeof *offsets_; index = plan->index; ut_ad(node->read_view); ut_ad(plan->unique_search); ut_ad(!plan->must_get_clust);#ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));#endif /* UNIV_SYNC_DEBUG */ row_sel_open_pcur(node, plan, TRUE, mtr); rec = btr_pcur_get_rec(&(plan->pcur)); if (!page_rec_is_user_rec(rec)) { return(SEL_RETRY);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -