📄 row0sel.c
字号:
/*******************************************************Select(c) 1997 Innobase OyCreated 12/19/1997 Heikki Tuuri*******************************************************/#include "row0sel.h"#ifdef UNIV_NONINL#include "row0sel.ic"#endif#include "dict0dict.h"#include "dict0boot.h"#include "trx0undo.h"#include "trx0trx.h"#include "btr0btr.h"#include "btr0cur.h"#include "btr0sea.h"#include "mach0data.h"#include "que0que.h"#include "row0upd.h"#include "row0row.h"#include "row0vers.h"#include "rem0cmp.h"#include "lock0lock.h"#include "eval0eval.h"#include "pars0sym.h"#include "pars0pars.h"#include "row0mysql.h"#include "read0read.h"#include "buf0lru.h"/* Maximum number of rows to prefetch; MySQL interface has another parameter */#define SEL_MAX_N_PREFETCH 16/* Number of rows fetched, after which to start prefetching; MySQL interfacehas another parameter */#define SEL_PREFETCH_LIMIT 1/* When a select has accessed about this many pages, it returns control backto que_run_threads: this is to allow canceling runaway queries */#define SEL_COST_LIMIT 100/* Flags for search shortcut */#define SEL_FOUND 0#define SEL_EXHAUSTED 1#define SEL_RETRY 2/************************************************************************Returns TRUE if the user-defined column values in a secondary index recordare alphabetically the same as the corresponding columns in the clusteredindex record.NOTE: the comparison is NOT done as a binary comparison, but characterfields are compared with collation! */staticiboolrow_sel_sec_rec_is_for_clust_rec(/*=============================*/ /* out: TRUE if the secondary record is equal to the corresponding fields in the clustered record, when compared with collation */ rec_t* sec_rec, /* in: secondary index record */ dict_index_t* sec_index, /* in: secondary index */ rec_t* clust_rec, /* in: clustered index record */ dict_index_t* clust_index) /* in: clustered index */{ dict_field_t* ifield; dict_col_t* col; byte* sec_field; ulint sec_len; byte* clust_field; ulint clust_len; ulint n; ulint i; dtype_t* cur_type; mem_heap_t* heap = NULL; ulint clust_offsets_[REC_OFFS_NORMAL_SIZE]; ulint sec_offsets_[REC_OFFS_SMALL_SIZE]; ulint* clust_offs = clust_offsets_; ulint* sec_offs = sec_offsets_; ibool is_equal = TRUE; *clust_offsets_ = (sizeof clust_offsets_) / sizeof *clust_offsets_; *sec_offsets_ = (sizeof sec_offsets_) / sizeof *sec_offsets_; clust_offs = rec_get_offsets(clust_rec, clust_index, clust_offs, ULINT_UNDEFINED, &heap); sec_offs = rec_get_offsets(sec_rec, sec_index, sec_offs, ULINT_UNDEFINED, &heap); n = dict_index_get_n_ordering_defined_by_user(sec_index); for (i = 0; i < n; i++) { ifield = dict_index_get_nth_field(sec_index, i); col = dict_field_get_col(ifield); clust_field = rec_get_nth_field(clust_rec, clust_offs, dict_col_get_clust_pos(col), &clust_len); sec_field = rec_get_nth_field(sec_rec, sec_offs, i, &sec_len); if (ifield->prefix_len > 0 && clust_len != UNIV_SQL_NULL) { cur_type = dict_col_get_type( dict_field_get_col(ifield)); clust_len = dtype_get_at_most_n_mbchars( cur_type, ifield->prefix_len, clust_len, (char*) clust_field); } if (0 != cmp_data_data(dict_col_get_type(col), clust_field, clust_len, sec_field, sec_len)) { is_equal = FALSE; goto func_exit; } }func_exit: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(is_equal);}/*************************************************************************Creates a select node struct. */sel_node_t*sel_node_create(/*============*/ /* out, own: select node struct */ mem_heap_t* heap) /* in: memory heap where created */{ sel_node_t* node; node = mem_heap_alloc(heap, sizeof(sel_node_t)); node->common.type = QUE_NODE_SELECT; node->state = SEL_NODE_OPEN; node->select_will_do_update = FALSE; node->latch_mode = BTR_SEARCH_LEAF; node->plans = NULL; return(node);}/*************************************************************************Frees the memory private to a select node when a query graph is freed,does not free the heap where the node was originally created. */voidsel_node_free_private(/*==================*/ sel_node_t* node) /* in: select node struct */{ ulint i; plan_t* plan; if (node->plans != NULL) { for (i = 0; i < node->n_tables; i++) { plan = sel_node_get_nth_plan(node, i); btr_pcur_close(&(plan->pcur)); btr_pcur_close(&(plan->clust_pcur)); if (plan->old_vers_heap) { mem_heap_free(plan->old_vers_heap); } } }}/*************************************************************************Evaluates the values in a select list. If there are aggregate functions,their argument value is added to the aggregate total. */UNIV_INLINEvoidsel_eval_select_list(/*=================*/ sel_node_t* node) /* in: select node */{ que_node_t* exp; exp = node->select_list; while (exp) { eval_exp(exp); exp = que_node_get_next(exp); }}/*************************************************************************Assigns the values in the select list to the possible into-variables inSELECT ... INTO ... */UNIV_INLINEvoidsel_assign_into_var_values(/*=======================*/ sym_node_t* var, /* in: first variable in a list of variables */ sel_node_t* node) /* in: select node */{ que_node_t* exp; if (var == NULL) { return; } exp = node->select_list; while (var) { ut_ad(exp); eval_node_copy_val(var->alias, exp); exp = que_node_get_next(exp); var = que_node_get_next(var); }}/*************************************************************************Resets the aggregate value totals in the select list of an aggregate typequery. */UNIV_INLINEvoidsel_reset_aggregate_vals(/*=====================*/ sel_node_t* node) /* in: select node */{ func_node_t* func_node; ut_ad(node->is_aggregate); func_node = node->select_list; while (func_node) { eval_node_set_int_val(func_node, 0); func_node = que_node_get_next(func_node); } node->aggregate_already_fetched = FALSE;}/*************************************************************************Copies the input variable values when an explicit cursor is opened. */UNIV_INLINEvoidrow_sel_copy_input_variable_vals(/*=============================*/ sel_node_t* node) /* in: select node */{ sym_node_t* var; var = UT_LIST_GET_FIRST(node->copy_variables); while (var) { eval_node_copy_val(var, var->alias); var->indirection = NULL; var = UT_LIST_GET_NEXT(col_var_list, var); }}/*************************************************************************Fetches the column values from a record. */staticvoidrow_sel_fetch_columns(/*==================*/ dict_index_t* index, /* in: record index */ rec_t* rec, /* in: record in a clustered or non-clustered index */ const ulint* offsets,/* in: rec_get_offsets(rec, index) */ sym_node_t* column) /* in: first column in a column list, or NULL */{ dfield_t* val; ulint index_type; ulint field_no; byte* data; ulint len; ut_ad(rec_offs_validate(rec, index, offsets)); if (index->type & DICT_CLUSTERED) { index_type = SYM_CLUST_FIELD_NO; } else { index_type = SYM_SEC_FIELD_NO; } while (column) { field_no = column->field_nos[index_type]; if (field_no != ULINT_UNDEFINED) { data = rec_get_nth_field(rec, offsets, field_no, &len); if (column->copy_val) { eval_node_copy_and_alloc_val(column, data, len); } else { val = que_node_get_val(column); dfield_set_data(val, data, len); } } column = UT_LIST_GET_NEXT(col_var_list, column); }}/*************************************************************************Allocates a prefetch buffer for a column when prefetch is first time done. */staticvoidsel_col_prefetch_buf_alloc(/*=======================*/ sym_node_t* column) /* in: symbol table node for a column */{ sel_buf_t* sel_buf; ulint i; ut_ad(que_node_get_type(column) == QUE_NODE_SYMBOL); column->prefetch_buf = mem_alloc(SEL_MAX_N_PREFETCH * sizeof(sel_buf_t)); for (i = 0; i < SEL_MAX_N_PREFETCH; i++) { sel_buf = column->prefetch_buf + i; sel_buf->data = NULL; sel_buf->val_buf_size = 0; }}/*************************************************************************Frees a prefetch buffer for a column, including the dynamically allocatedmemory for data stored there. */voidsel_col_prefetch_buf_free(/*======================*/ sel_buf_t* prefetch_buf) /* in, own: prefetch buffer */{ sel_buf_t* sel_buf; ulint i; for (i = 0; i < SEL_MAX_N_PREFETCH; i++) { sel_buf = prefetch_buf + i; if (sel_buf->val_buf_size > 0) { mem_free(sel_buf->data); } }}/*************************************************************************Pops the column values for a prefetched, cached row from the column prefetchbuffers and places them to the val fields in the column nodes. */staticvoidsel_pop_prefetched_row(/*===================*/ plan_t* plan) /* in: plan node for a table */{ sym_node_t* column; sel_buf_t* sel_buf; dfield_t* val; byte* data; ulint len; ulint val_buf_size; ut_ad(plan->n_rows_prefetched > 0); column = UT_LIST_GET_FIRST(plan->columns); while (column) { val = que_node_get_val(column); if (!column->copy_val) { /* We did not really push any value for the column */ ut_ad(!column->prefetch_buf); ut_ad(que_node_get_val_buf_size(column) == 0);#ifdef UNIV_DEBUG dfield_set_data(val, NULL, 0);#endif goto next_col; } ut_ad(column->prefetch_buf); sel_buf = column->prefetch_buf + plan->first_prefetched; data = sel_buf->data; len = sel_buf->len; val_buf_size = sel_buf->val_buf_size; /* We must keep track of the allocated memory for column values to be able to free it later: therefore we swap the values for sel_buf and val */ sel_buf->data = dfield_get_data(val); sel_buf->len = dfield_get_len(val); sel_buf->val_buf_size = que_node_get_val_buf_size(column); dfield_set_data(val, data, len); que_node_set_val_buf_size(column, val_buf_size);next_col: column = UT_LIST_GET_NEXT(col_var_list, column); } plan->n_rows_prefetched--; plan->first_prefetched++;}/*************************************************************************Pushes the column values for a prefetched, cached row to the column prefetchbuffers from the val fields in the column nodes. */UNIV_INLINEvoidsel_push_prefetched_row(/*====================*/ plan_t* plan) /* in: plan node for a table */{ sym_node_t* column; sel_buf_t* sel_buf; dfield_t* val; byte* data; ulint len; ulint pos; ulint val_buf_size; if (plan->n_rows_prefetched == 0) { pos = 0; plan->first_prefetched = 0; } else { pos = plan->n_rows_prefetched; /* We have the convention that pushing new rows starts only after the prefetch stack has been emptied: */ ut_ad(plan->first_prefetched == 0); } plan->n_rows_prefetched++; ut_ad(pos < SEL_MAX_N_PREFETCH); column = UT_LIST_GET_FIRST(plan->columns); while (column) { if (!column->copy_val) { /* There is no sense to push pointers to database page fields when we do not keep latch on the page! */ goto next_col; } if (!column->prefetch_buf) { /* Allocate a new prefetch buffer */ sel_col_prefetch_buf_alloc(column); } sel_buf = column->prefetch_buf + pos; val = que_node_get_val(column); data = dfield_get_data(val); len = dfield_get_len(val); val_buf_size = que_node_get_val_buf_size(column); /* We must keep track of the allocated memory for column values to be able to free it later: therefore we swap the values for sel_buf and val */ dfield_set_data(val, sel_buf->data, sel_buf->len); que_node_set_val_buf_size(column, sel_buf->val_buf_size); sel_buf->data = data; sel_buf->len = len; sel_buf->val_buf_size = val_buf_size;next_col: column = UT_LIST_GET_NEXT(col_var_list, column); }}/*************************************************************************Builds a previous version of a clustered index record for a consistent read */staticulint
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -