📄 row0sel.c
字号:
goto table_exhausted; } goto next_rec; } /* PHASE 7: We found a new qualifying row for the current table; push the row if prefetch is on, or move to the next table in the join */ plan->n_rows_fetched++; ut_ad(plan->pcur.latch_mode == node->latch_mode); if (node->select_will_do_update) { /* This is a searched update and we can do the update in-place, saving CPU time */ row_upd_in_place_in_select(node, thr, &mtr); leaf_contains_updates = TRUE; /* When the database is in the online backup mode, the number of log records for a single mtr should be small: increment the cost counter to ensure it */ cost_counter += 1 + (SEL_COST_LIMIT / 8); if (plan->unique_search) { goto table_exhausted; } goto next_rec; } if ((plan->n_rows_fetched <= SEL_PREFETCH_LIMIT) || plan->unique_search || plan->no_prefetch) { /* No prefetch in operation: go to the next table */ goto next_table; } sel_push_prefetched_row(plan); if (plan->n_rows_prefetched == SEL_MAX_N_PREFETCH) { /* The prefetch buffer is now full */ sel_pop_prefetched_row(plan); goto next_table; }next_rec: ut_ad(!search_latch_locked); if (mtr_has_extra_clust_latch) { /* We must commit &mtr if we are moving to the next non-clustered index record, because we could break the latching order if we would access a different clustered index page right away without releasing the previous. */ goto commit_mtr_for_a_while; } if (leaf_contains_updates && btr_pcur_is_after_last_on_page(&(plan->pcur), &mtr)) { /* We must commit &mtr if we are moving to a different page, because we have done updates to the x-latched leaf page, and the latch would be released in btr_pcur_move_to_next, without &mtr getting committed there */ ut_ad(node->asc); goto commit_mtr_for_a_while; } if (node->asc) { moved = btr_pcur_move_to_next(&(plan->pcur), &mtr); } else { moved = btr_pcur_move_to_prev(&(plan->pcur), &mtr); } if (!moved) { goto table_exhausted; } cursor_just_opened = FALSE; /* END OF RECORD LOOP ------------------ */ goto rec_loop;next_table: /* We found a record which satisfies the conditions: we can move to the next table or return a row in the result set */ ut_ad(btr_pcur_is_on_user_rec(&(plan->pcur), &mtr)); if (plan->unique_search && !node->can_get_updated) { plan->cursor_at_end = TRUE; } else { ut_ad(!search_latch_locked); plan->stored_cursor_rec_processed = TRUE; btr_pcur_store_position(&(plan->pcur), &mtr); } mtr_commit(&mtr); leaf_contains_updates = FALSE; mtr_has_extra_clust_latch = FALSE;next_table_no_mtr: /* If we use 'goto' to this label, it means that the row was popped from the prefetched rows stack, and &mtr is already committed */ if (node->fetch_table + 1 == node->n_tables) { sel_eval_select_list(node); if (node->is_aggregate) { goto table_loop; } sel_assign_into_var_values(node->into_list, node); thr->run_node = que_node_get_parent(node); if (search_latch_locked) { rw_lock_s_unlock(&btr_search_latch); } err = DB_SUCCESS; goto func_exit; } node->fetch_table++; /* When we move to the next table, we first reset the plan cursor: we do not care about resetting it when we backtrack from a table */ plan_reset_cursor(sel_node_get_nth_plan(node, node->fetch_table)); goto table_loop;table_exhausted: /* The table cursor pcur reached the result set end: backtrack to the previous table in the join if we do not have cached prefetched rows */ plan->cursor_at_end = TRUE; mtr_commit(&mtr); leaf_contains_updates = FALSE; mtr_has_extra_clust_latch = FALSE; if (plan->n_rows_prefetched > 0) { /* The table became exhausted during a prefetch */ sel_pop_prefetched_row(plan); goto next_table_no_mtr; }table_exhausted_no_mtr: if (node->fetch_table == 0) { err = DB_SUCCESS; if (node->is_aggregate && !node->aggregate_already_fetched) { node->aggregate_already_fetched = TRUE; sel_assign_into_var_values(node->into_list, node); thr->run_node = que_node_get_parent(node); if (search_latch_locked) { rw_lock_s_unlock(&btr_search_latch); } goto func_exit; } node->state = SEL_NODE_NO_MORE_ROWS; thr->run_node = que_node_get_parent(node); if (search_latch_locked) { rw_lock_s_unlock(&btr_search_latch); } goto func_exit; } node->fetch_table--; goto table_loop;stop_for_a_while: /* Return control for a while to que_run_threads, so that runaway queries can be canceled. NOTE that when we come here, we must, in a locking read, have placed the necessary (possibly waiting request) record lock on the cursor record or its successor: when we reposition the cursor, this record lock guarantees that nobody can meanwhile have inserted new records which should have appeared in the result set, which would result in the phantom problem. */ ut_ad(!search_latch_locked); plan->stored_cursor_rec_processed = FALSE; btr_pcur_store_position(&(plan->pcur), &mtr); mtr_commit(&mtr); ut_ad(sync_thread_levels_empty_gen(TRUE)); err = DB_SUCCESS; goto func_exit;commit_mtr_for_a_while: /* Stores the cursor position and commits &mtr; this is used if &mtr may contain latches which would break the latching order if &mtr would not be committed and the latches released. */ plan->stored_cursor_rec_processed = TRUE; ut_ad(!search_latch_locked); btr_pcur_store_position(&(plan->pcur), &mtr); mtr_commit(&mtr); leaf_contains_updates = FALSE; mtr_has_extra_clust_latch = FALSE; ut_ad(sync_thread_levels_empty_gen(TRUE)); goto table_loop;lock_wait_or_error: /* See the note at stop_for_a_while: the same holds for this case */ ut_ad(!btr_pcur_is_before_first_on_page(&(plan->pcur), &mtr) || !node->asc); ut_ad(!search_latch_locked); plan->stored_cursor_rec_processed = FALSE; btr_pcur_store_position(&(plan->pcur), &mtr); mtr_commit(&mtr); ut_ad(sync_thread_levels_empty_gen(TRUE));func_exit: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err);}/**************************************************************************Performs a select step. This is a high-level function used in SQL executiongraphs. */que_thr_t*row_sel_step(/*=========*/ /* out: query thread to run next or NULL */ que_thr_t* thr) /* in: query thread */{ ulint i_lock_mode; sym_node_t* table_node; sel_node_t* node; ulint err; ut_ad(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_SELECT); /* If this is a new time this node is executed (or when execution resumes after wait for a table intention lock), set intention locks on the tables, or assign a read view */ if (node->into_list && (thr->prev_node == que_node_get_parent(node))) { node->state = SEL_NODE_OPEN; } if (node->state == SEL_NODE_OPEN) { /* It may be that the current session has not yet started its transaction, or it has been committed: */ trx_start_if_not_started(thr_get_trx(thr)); plan_reset_cursor(sel_node_get_nth_plan(node, 0)); if (node->consistent_read) { /* Assign a read view for the query */ node->read_view = trx_assign_read_view( thr_get_trx(thr)); } else { if (node->set_x_locks) { i_lock_mode = LOCK_IX; } else { i_lock_mode = LOCK_IS; } table_node = node->table_list; while (table_node) { err = lock_table(0, table_node->table, i_lock_mode, thr); if (err != DB_SUCCESS) { que_thr_handle_error(thr, DB_ERROR, NULL, 0); return(NULL); } table_node = que_node_get_next(table_node); } } /* If this is an explicit cursor, copy stored procedure variable values, so that the values cannot change between fetches (currently, we copy them also for non-explicit cursors) */ if (node->explicit_cursor && UT_LIST_GET_FIRST(node->copy_variables)) { row_sel_copy_input_variable_vals(node); } node->state = SEL_NODE_FETCH; node->fetch_table = 0; if (node->is_aggregate) { /* Reset the aggregate total values */ sel_reset_aggregate_vals(node); } } err = row_sel(node, thr); /* NOTE! if queries are parallelized, the following assignment may have problems; the assignment should be made only if thr is the only top-level thr in the graph: */ thr->graph->last_sel_node = node; if (err == DB_SUCCESS) { /* Ok: do nothing */ } else if (err == DB_LOCK_WAIT) { return(NULL); } else { /* SQL error detected */ fprintf(stderr, "SQL error %lu\n", (ulong) err); que_thr_handle_error(thr, DB_ERROR, NULL, 0); return(NULL); } return(thr);} /**************************************************************************Performs a fetch for a cursor. */que_thr_t*fetch_step(/*=======*/ /* out: query thread to run next or NULL */ que_thr_t* thr) /* in: query thread */{ sel_node_t* sel_node; fetch_node_t* node; ut_ad(thr); node = thr->run_node; sel_node = node->cursor_def; ut_ad(que_node_get_type(node) == QUE_NODE_FETCH); if (thr->prev_node != que_node_get_parent(node)) { if (sel_node->state != SEL_NODE_NO_MORE_ROWS) { sel_assign_into_var_values(node->into_list, sel_node); } thr->run_node = que_node_get_parent(node); return(thr); } /* Make the fetch node the parent of the cursor definition for the time of the fetch, so that execution knows to return to this fetch node after a row has been selected or we know that there is no row left */ sel_node->common.parent = node; if (sel_node->state == SEL_NODE_CLOSED) { /* SQL error detected */ fprintf(stderr, "SQL error %lu\n", (ulong)DB_ERROR); que_thr_handle_error(thr, DB_ERROR, NULL, 0); return(NULL); } thr->run_node = sel_node; return(thr);} /***************************************************************Prints a row in a select result. */que_thr_t*row_printf_step(/*============*/ /* out: query thread to run next or NULL */ que_thr_t* thr) /* in: query thread */{ row_printf_node_t* node; sel_node_t* sel_node; que_node_t* arg; ut_ad(thr); node = thr->run_node; sel_node = node->sel_node; ut_ad(que_node_get_type(node) == QUE_NODE_ROW_PRINTF); if (thr->prev_node == que_node_get_parent(node)) { /* Reset the cursor */ sel_node->state = SEL_NODE_OPEN; /* Fetch next row to print */ thr->run_node = sel_node; return(thr); } if (sel_node->state != SEL_NODE_FETCH) { ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS); /* No more rows to print */ thr->run_node = que_node_get_parent(node); return(thr); } arg = sel_node->select_list; while (arg) { dfield_print_also_hex(que_node_get_val(arg)); fputs(" ::: ", stderr); arg = que_node_get_next(arg); } putc('\n', stderr); /* Fetch next row to print */ thr->run_node = sel_node; return(thr);} /********************************************************************Converts a key value stored in MySQL format to an Innobase dtuple. The lastfield of the key value may be just a prefix of a fixed length field: hencethe parameter key_len. But currently we do not allow search keys where thelast field is only a prefix of the full key field len and print a warning ifsuch appears. A counterpart of this function isha_innobase::store_key_val_for_row() in ha_innodb.cc. */voidrow_sel_convert_mysql_key_to_innobase(/*==================================*/ dtuple_t* tuple, /* in: tuple where to build; NOTE: we assume that the type info in the tuple is already according
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -