#include "row0sel.h"#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"Include dependency graph for row0sel.c:

Go to the source code of this file.
| #define SEL_EXHAUSTED 1 |
Definition at line 53 of file row0sel.c.
Referenced by row_search_for_mysql(), row_sel(), row_sel_try_search_shortcut(), and row_sel_try_search_shortcut_for_mysql().
| #define SEL_FOUND 0 |
Definition at line 52 of file row0sel.c.
Referenced by row_search_for_mysql(), row_sel(), row_sel_try_search_shortcut(), and row_sel_try_search_shortcut_for_mysql().
| #define SEL_MAX_N_PREFETCH 16 |
Definition at line 37 of file row0sel.c.
Referenced by row_sel(), sel_col_prefetch_buf_alloc(), sel_col_prefetch_buf_free(), and sel_push_prefetched_row().
| #define SEL_RETRY 2 |
Definition at line 54 of file row0sel.c.
Referenced by row_sel(), row_sel_try_search_shortcut(), and row_sel_try_search_shortcut_for_mysql().
Definition at line 1986 of file row0sel.c.
References pars_user_func_struct::arg, sel_node_struct::common, fetch_node_struct::cursor_def, DB_ERROR, trx_struct::error_state, pars_user_func_struct::func, fetch_node_struct::func, fetch_node_struct::into_list, NULL, que_common_struct::parent, que_thr_struct::prev_node, QUE_NODE_FETCH, que_node_get_parent(), que_node_get_type(), que_thr_struct::run_node, sel_assign_into_var_values(), SEL_NODE_CLOSED, SEL_NODE_NO_MORE_ROWS, sel_node_struct::state, thr_get_trx(), and ut_ad.
Referenced by que_thr_step().
01988 : query thread to run next or NULL */ 01989 que_thr_t* thr) /* in: query thread */ 01990 { 01991 sel_node_t* sel_node; 01992 fetch_node_t* node; 01993 01994 ut_ad(thr); 01995 01996 node = thr->run_node; 01997 sel_node = node->cursor_def; 01998 01999 ut_ad(que_node_get_type(node) == QUE_NODE_FETCH); 02000 02001 if (thr->prev_node != que_node_get_parent(node)) { 02002 02003 if (sel_node->state != SEL_NODE_NO_MORE_ROWS) { 02004 02005 if (node->into_list) { 02006 sel_assign_into_var_values(node->into_list, 02007 sel_node); 02008 } else { 02009 void* ret = (*node->func->func)(sel_node, 02010 node->func->arg); 02011 02012 if (!ret) { 02013 sel_node->state = 02014 SEL_NODE_NO_MORE_ROWS; 02015 } 02016 } 02017 } 02018 02019 thr->run_node = que_node_get_parent(node); 02020 02021 return(thr); 02022 } 02023 02024 /* Make the fetch node the parent of the cursor definition for 02025 the time of the fetch, so that execution knows to return to this 02026 fetch node after a row has been selected or we know that there is 02027 no row left */ 02028 02029 sel_node->common.parent = node; 02030 02031 if (sel_node->state == SEL_NODE_CLOSED) { 02032 fprintf(stderr, 02033 "InnoDB: Error: fetch called on a closed cursor\n"); 02034 02035 thr_get_trx(thr)->error_state = DB_ERROR; 02036 02037 return(NULL); 02038 } 02039 02040 thr->run_node = sel_node; 02041 02042 return(thr); 02043 }
Here is the call graph for this function:

Here is the caller graph for this function:

| UNIV_INLINE void plan_reset_cursor | ( | plan_t * | plan | ) |
Definition at line 1039 of file row0sel.c.
Referenced by row_sel(), and row_sel_step().
01041 : plan */ 01042 { 01043 plan->pcur_is_open = FALSE; 01044 plan->cursor_at_end = FALSE; 01045 plan->n_rows_fetched = 0; 01046 plan->n_rows_prefetched = 0; 01047 }
Here is the call graph for this function:

Here is the caller graph for this function:

| void* row_fetch_print | ( | void * | row, | |
| void * | user_arg | |||
| ) |
Definition at line 2049 of file row0sel.c.
References dfield_get_data(), dfield_get_len(), dfield_get_type(), dtype_print(), que_node_get_next(), que_node_get_val(), sel_node_struct::select_list, UT_NOT_USED, and ut_print_buf().
02051 : always returns non-NULL */ 02052 void* row, /* in: sel_node_t* */ 02053 void* user_arg) /* in: not used */ 02054 { 02055 sel_node_t* node = row; 02056 que_node_t* exp; 02057 ulint i = 0; 02058 02059 UT_NOT_USED(user_arg); 02060 02061 fprintf(stderr, "row_fetch_print: row %p\n", row); 02062 02063 exp = node->select_list; 02064 02065 while (exp) { 02066 dfield_t* dfield = que_node_get_val(exp); 02067 dtype_t* type = dfield_get_type(dfield); 02068 02069 fprintf(stderr, " column %lu:\n", (ulong)i); 02070 02071 dtype_print(type); 02072 fprintf(stderr, "\n"); 02073 02074 if (dfield_get_len(dfield) != UNIV_SQL_NULL) { 02075 ut_print_buf(stderr, dfield_get_data(dfield), 02076 dfield_get_len(dfield)); 02077 } else { 02078 fprintf(stderr, " <NULL>;"); 02079 } 02080 02081 fprintf(stderr, "\n"); 02082 02083 exp = que_node_get_next(exp); 02084 i++; 02085 } 02086 02087 return((void*)42); 02088 }
Here is the call graph for this function:

| void* row_fetch_store_uint4 | ( | void * | row, | |
| void * | user_arg | |||
| ) |
Definition at line 2096 of file row0sel.c.
References DATA_INT, DATA_UNSIGNED, dfield_get_data(), dfield_get_len(), dfield_get_type(), dtype_get_mtype(), dtype_get_prtype(), mach_read_from_4(), NULL, que_node_get_val(), sel_node_struct::select_list, and ut_a.
02098 : always returns NULL */ 02099 void* row, /* in: sel_node_t* */ 02100 void* user_arg) /* in: data pointer */ 02101 { 02102 sel_node_t* node = row; 02103 ib_uint32_t* val = user_arg; 02104 ulint tmp; 02105 02106 dfield_t* dfield = que_node_get_val(node->select_list); 02107 dtype_t* type = dfield_get_type(dfield); 02108 ulint len = dfield_get_len(dfield); 02109 02110 ut_a(dtype_get_mtype(type) == DATA_INT); 02111 ut_a(dtype_get_prtype(type) & DATA_UNSIGNED); 02112 ut_a(len == 4); 02113 02114 tmp = mach_read_from_4(dfield_get_data(dfield)); 02115 *val = tmp; 02116 02117 return(NULL); 02118 }
Here is the call graph for this function:

Definition at line 2124 of file row0sel.c.
References dfield_print_also_hex(), que_thr_struct::prev_node, que_node_get_next(), que_node_get_parent(), que_node_get_type(), que_node_get_val(), QUE_NODE_ROW_PRINTF, que_thr_struct::run_node, row_printf_node_struct::sel_node, SEL_NODE_FETCH, SEL_NODE_NO_MORE_ROWS, SEL_NODE_OPEN, sel_node_struct::select_list, sel_node_struct::state, and ut_ad.
Referenced by que_thr_step().
02126 : query thread to run next or NULL */ 02127 que_thr_t* thr) /* in: query thread */ 02128 { 02129 row_printf_node_t* node; 02130 sel_node_t* sel_node; 02131 que_node_t* arg; 02132 02133 ut_ad(thr); 02134 02135 node = thr->run_node; 02136 02137 sel_node = node->sel_node; 02138 02139 ut_ad(que_node_get_type(node) == QUE_NODE_ROW_PRINTF); 02140 02141 if (thr->prev_node == que_node_get_parent(node)) { 02142 02143 /* Reset the cursor */ 02144 sel_node->state = SEL_NODE_OPEN; 02145 02146 /* Fetch next row to print */ 02147 02148 thr->run_node = sel_node; 02149 02150 return(thr); 02151 } 02152 02153 if (sel_node->state != SEL_NODE_FETCH) { 02154 02155 ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS); 02156 02157 /* No more rows to print */ 02158 02159 thr->run_node = que_node_get_parent(node); 02160 02161 return(thr); 02162 } 02163 02164 arg = sel_node->select_list; 02165 02166 while (arg) { 02167 dfield_print_also_hex(que_node_get_val(arg)); 02168 02169 fputs(" ::: ", stderr); 02170 02171 arg = que_node_get_next(arg); 02172 } 02173 02174 putc('\n', stderr); 02175 02176 /* Fetch next row to print */ 02177 02178 thr->run_node = sel_node; 02179 02180 return(thr); 02181 }
Here is the call graph for this function:

Here is the caller graph for this function:

| ibool row_search_check_if_query_cache_permitted | ( | trx_t * | trx, | |
| const char * | norm_name | |||
| ) |
Definition at line 4405 of file row0sel.c.
References dict_table_get(), FALSE, trx_struct::global_read_view, trx_struct::global_read_view_heap, trx_struct::id, trx_struct::isolation_level, kernel_mutex, dict_table_struct::locks, mutex_enter, mutex_exit(), NULL, dict_table_struct::query_cache_inv_trx_id, trx_struct::read_view, read_view_open_now(), TRUE, TRX_ISO_REPEATABLE_READ, trx_start_if_not_started_low(), ut_dulint_cmp(), and UT_LIST_GET_LEN.
04407 : TRUE if storing or retrieving 04408 from the query cache is permitted */ 04409 trx_t* trx, /* in: transaction object */ 04410 const char* norm_name) /* in: concatenation of database name, 04411 '/' char, table name */ 04412 { 04413 dict_table_t* table; 04414 ibool ret = FALSE; 04415 04416 table = dict_table_get(norm_name); 04417 04418 if (table == NULL) { 04419 04420 return(FALSE); 04421 } 04422 04423 mutex_enter(&kernel_mutex); 04424 04425 /* Start the transaction if it is not started yet */ 04426 04427 trx_start_if_not_started_low(trx); 04428 04429 /* If there are locks on the table or some trx has invalidated the 04430 cache up to our trx id, then ret = FALSE. 04431 We do not check what type locks there are on the table, though only 04432 IX type locks actually would require ret = FALSE. */ 04433 04434 if (UT_LIST_GET_LEN(table->locks) == 0 04435 && ut_dulint_cmp(trx->id, 04436 table->query_cache_inv_trx_id) >= 0) { 04437 04438 ret = TRUE; 04439 04440 /* If the isolation level is high, assign a read view for the 04441 transaction if it does not yet have one */ 04442 04443 if (trx->isolation_level >= TRX_ISO_REPEATABLE_READ 04444 && !trx->read_view) { 04445 04446 trx->read_view = read_view_open_now(trx->id, 04447 trx->global_read_view_heap); 04448 trx->global_read_view = trx->read_view; 04449 } 04450 } 04451 04452 mutex_exit(&kernel_mutex); 04453 04454 return(ret); 04455 }
Here is the call graph for this function:

| ulint row_search_for_mysql | ( | byte * | buf, | |
| ulint | mode, | |||
| row_prebuilt_t * | prebuilt, | |||
| ulint | match_mode, | |||
| ulint | direction | |||
| ) |
Definition at line 3175 of file row0sel.c.
References btr_index_rec_validate(), btr_pcur_get_rec(), btr_pcur_move_to_last_on_page(), btr_pcur_move_to_next(), btr_pcur_move_to_prev(), btr_pcur_open_at_index_side(), btr_pcur_open_with_no_init(), btr_pcur_store_position(), BTR_SEA_TIMEOUT, btr_search_latch, BTR_SEARCH_LEAF, buf_block_align(), buf_frame_align(), buf_frame_get_page_no(), buf_page_print(), buf_pool, row_prebuilt_struct::clust_index_was_generated, cmp_dtuple_is_prefix_of_rec(), cmp_dtuple_rec(), DB_CORRUPTION, DB_END_OF_INDEX, DB_ERROR, DB_LOCK_WAIT, DB_RECORD_NOT_FOUND, DB_SUCCESS, DB_TOO_BIG_RECORD, DICT_CLUSTERED, dict_index_get_n_unique(), dict_index_name_print(), dict_str_starts_with_keyword(), dict_table_get_first_index(), dict_table_is_comp(), DICT_UNIQUE, dtuple_contains_null(), dtuple_get_n_fields(), dtuple_get_n_fields_cmp(), err, trx_struct::error_state, FALSE, row_prebuilt_struct::fetch_cache_first, row_prebuilt_struct::fetch_direction, buf_pool_struct::frame_zero, trx_struct::has_search_latch, buf_pool_struct::high_end, dict_table_struct::ibd_file_missing, row_prebuilt_struct::index, index(), trx_struct::isolation_level, kernel_mutex, lock_cancel_waiting_and_release(), lock_clust_rec_cons_read_sees(), LOCK_GAP, LOCK_IS, LOCK_IX, LOCK_NONE, LOCK_ORDINARY, LOCK_REC_NOT_GAP, LOCK_S, lock_sec_rec_cons_read_sees(), que_thr_struct::lock_state, lock_table(), mach_write_to_4(), row_prebuilt_struct::magic_n, mem_analyze_corruption(), mem_heap_free, memcpy, mtr_commit(), mtr_start(), mutex_enter, mutex_exit(), MYSQL_FETCH_CACHE_SIZE, MYSQL_FETCH_CACHE_THRESHOLD, trx_struct::mysql_n_tables_locked, trx_struct::mysql_query_str, row_prebuilt_struct::mysql_row_len, trx_struct::mysql_thd, trx_struct::mysql_thread_id, row_prebuilt_struct::n_fetch_cached, trx_struct::n_mysql_tables_in_use, row_prebuilt_struct::n_rows_fetched, dict_table_struct::name, row_prebuilt_struct::need_to_access_clustered, NULL, trx_struct::op_info, os_thread_get_curr_id(), PAGE_CUR_G, PAGE_CUR_GE, PAGE_CUR_L, PAGE_DIR, PAGE_NEW_SUPREMUM, PAGE_OLD_SUPREMUM, page_rec_is_comp(), page_rec_is_infimum(), page_rec_is_supremum(), row_prebuilt_struct::pcur, que_fork_get_first_thr(), QUE_THR_LOCK_NOLOCK, QUE_THR_LOCK_ROW, que_thr_move_to_run_state_for_mysql(), que_thr_stop_for_mysql(), que_thr_stop_for_mysql_no_error(), trx_struct::read_view, rec_get_deleted_flag(), rec_get_next_offs(), rec_get_offsets, rec_offs_extra_size(), REC_OFFS_NORMAL_SIZE, rec_offs_size(), rec_offs_validate(), rec_validate(), ROW_MYSQL_DUMMY_TEMPLATE, row_mysql_handle_errors(), row_prebuild_sel_graph(), ROW_PREBUILT_ALLOCATED, ROW_READ_DID_SEMI_CONSISTENT, ROW_READ_TRY_SEMI_CONSISTENT, row_prebuilt_struct::row_read_type, ROW_READ_WITH_LOCKS, row_sel_build_committed_vers_for_mysql(), row_sel_build_prev_vers_for_mysql(), ROW_SEL_EXACT, ROW_SEL_EXACT_PREFIX, row_sel_get_clust_rec_for_mysql(), ROW_SEL_NEXT, row_sel_pop_cached_row_for_mysql(), row_sel_push_cache_row_for_mysql(), row_sel_store_mysql_rec(), row_sel_store_row_id_to_prebuilt(), row_sel_try_search_shortcut_for_mysql(), row_unlock_for_mysql(), RW_LOCK_NOT_LOCKED, rw_lock_s_lock, rw_lock_s_unlock, trx_struct::search_latch_timeout, btr_pcur_struct::search_mode, row_prebuilt_struct::search_tuple, SEL_EXHAUSTED, SEL_FOUND, row_prebuilt_struct::sel_graph, sel_restore_position_for_mysql(), sel_set_rec_lock(), row_prebuilt_struct::select_lock_type, row_prebuilt_struct::sql_stat_start, srv_force_recovery, srv_locks_unsafe_for_binlog, srv_n_rows_read, row_prebuilt_struct::table, row_prebuilt_struct::templ_contains_blob, row_prebuilt_struct::template_type, TRUE, row_prebuilt_struct::trx, trx_assign_read_view(), btr_pcur_struct::trx_if_known, TRX_ISO_READ_COMMITTED, TRX_ISO_READ_UNCOMMITTED, trx_print(), trx_reset_new_rec_lock_info(), trx_start_if_not_started(), UNIV_PAGE_SIZE, row_prebuilt_struct::used_in_HANDLER, ut_a, ut_ad, ut_align_offset(), ut_error, ut_print_name(), ut_print_timestamp(), trx_struct::wait_lock, and trx_struct::was_chosen_as_deadlock_victim.
Referenced by row_scan_and_check_index().
03177 : DB_SUCCESS, 03178 DB_RECORD_NOT_FOUND, 03179 DB_END_OF_INDEX, DB_DEADLOCK, 03180 DB_LOCK_TABLE_FULL, DB_CORRUPTION, 03181 or DB_TOO_BIG_RECORD */ 03182 byte* buf, /* in/out: buffer for the fetched 03183 row in the MySQL format */ 03184 ulint mode, /* in: search mode PAGE_CUR_L, ... */ 03185 row_prebuilt_t* prebuilt, /* in: prebuilt struct for the 03186 table handle; this contains the info 03187 of search_tuple, index; if search 03188 tuple contains 0 fields then we 03189 position the cursor at the start or 03190 the end of the index, depending on 03191 'mode' */ 03192 ulint match_mode, /* in: 0 or ROW_SEL_EXACT or 03193 ROW_SEL_EXACT_PREFIX */ 03194 ulint direction) /* in: 0 or ROW_SEL_NEXT or 03195 ROW_SEL_PREV; NOTE: if this is != 0, 03196 then prebuilt must have a pcur 03197 with stored position! In opening of a 03198 cursor 'direction' should be 0. */ 03199 { 03200 dict_index_t* index = prebuilt->index; 03201 ibool comp = dict_table_is_comp(index->table); 03202 dtuple_t* search_tuple = prebuilt->search_tuple; 03203 btr_pcur_t* pcur = prebuilt->pcur; 03204 trx_t* trx = prebuilt->trx; 03205 dict_index_t* clust_index; 03206 que_thr_t* thr; 03207 rec_t* rec; 03208 rec_t* result_rec; 03209 rec_t* clust_rec; 03210 ulint err = DB_SUCCESS; 03211 ibool unique_search = FALSE; 03212 ibool unique_search_from_clust_index = FALSE; 03213 ibool mtr_has_extra_clust_latch = FALSE; 03214 ibool moves_up = FALSE; 03215 ibool set_also_gap_locks = TRUE; 03216 /* if the query is a plain 03217 locking SELECT, and the isolation 03218 level is <= TRX_ISO_READ_COMMITTED, 03219 then this is set to FALSE */ 03220 ibool did_semi_consistent_read = FALSE; 03221 /* if the returned record was locked 03222 and we did a semi-consistent read 03223 (fetch the newest committed version), 03224 then this is set to TRUE */ 03225 #ifdef UNIV_SEARCH_DEBUG 03226 ulint cnt = 0; 03227 #endif /* UNIV_SEARCH_DEBUG */ 03228 ulint next_offs; 03229 ibool same_user_rec; 03230 mtr_t mtr; 03231 mem_heap_t* heap = NULL; 03232 ulint offsets_[REC_OFFS_NORMAL_SIZE]; 03233 ulint* offsets = offsets_; 03234 03235 *offsets_ = (sizeof offsets_) / sizeof *offsets_; 03236 03237 ut_ad(index && pcur && search_tuple); 03238 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 03239 03240 if (UNIV_UNLIKELY(prebuilt->table->ibd_file_missing)) { 03241 ut_print_timestamp(stderr); 03242 fprintf(stderr, " InnoDB: Error:\n" 03243 "InnoDB: MySQL is trying to use a table handle but the .ibd file for\n" 03244 "InnoDB: table %s does not exist.\n" 03245 "InnoDB: Have you deleted the .ibd file from the database directory under\n" 03246 "InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n" 03247 "InnoDB: Look from\n" 03248 "http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n" 03249 "InnoDB: how you can resolve the problem.\n", 03250 prebuilt->table->name); 03251 03252 return(DB_ERROR); 03253 } 03254 03255 if (UNIV_UNLIKELY(prebuilt->magic_n != ROW_PREBUILT_ALLOCATED)) { 03256 fprintf(stderr, 03257 "InnoDB: Error: trying to free a corrupt\n" 03258 "InnoDB: table handle. Magic n %lu, table name ", 03259 (ulong) prebuilt->magic_n); 03260 ut_print_name(stderr, trx, TRUE, prebuilt->table->name); 03261 putc('\n', stderr); 03262 03263 mem_analyze_corruption(prebuilt); 03264 03265 ut_error; 03266 } 03267 03268 if (trx->n_mysql_tables_in_use == 0 03269 && UNIV_UNLIKELY(prebuilt->select_lock_type == LOCK_NONE)) { 03270 /* Note that if MySQL uses an InnoDB temp table that it 03271 created inside LOCK TABLES, then n_mysql_tables_in_use can 03272 be zero; in that case select_lock_type is set to LOCK_X in 03273 ::start_stmt. */ 03274 03275 /* August 19, 2005 by Heikki: temporarily disable this error print until the 03276 cursor lock count is done correctly. See bugs #12263 and #12456! 03277 03278 fputs( 03279 "InnoDB: Error: MySQL is trying to perform a SELECT\n" 03280 "InnoDB: but it has not locked any tables in ::external_lock()!\n", 03281 stderr); 03282 trx_print(stderr, trx, 600); 03283 fputc('\n', stderr); 03284 */ 03285 03286 } 03287 03288 /* fprintf(stderr, "Match mode %lu\n search tuple ", (ulong) match_mode); 03289 dtuple_print(search_tuple); 03290 03291 fprintf(stderr, "N tables locked %lu\n", trx->mysql_n_tables_locked); 03292 */ 03293 /*-------------------------------------------------------------*/ 03294 /* PHASE 0: Release a possible s-latch we are holding on the 03295 adaptive hash index latch if there is someone waiting behind */ 03296 03297 if (UNIV_UNLIKELY(btr_search_latch.writer != RW_LOCK_NOT_LOCKED) 03298 && trx->has_search_latch) { 03299 03300 /* There is an x-latch request on the adaptive hash index: 03301 release the s-latch to reduce starvation and wait for 03302 BTR_SEA_TIMEOUT rounds before trying to keep it again over 03303 calls from MySQL */ 03304 03305 rw_lock_s_unlock(&btr_search_latch); 03306 trx->has_search_latch = FALSE; 03307 03308 trx->search_latch_timeout = BTR_SEA_TIMEOUT; 03309 } 03310 03311 /* Reset the new record lock info if srv_locks_unsafe_for_binlog 03312 is set or session is using a READ COMMITED isolation level. Then 03313 we are able to remove the record locks set here on an individual 03314 row. */ 03315 03316 if ((srv_locks_unsafe_for_binlog 03317 || trx->isolation_level == TRX_ISO_READ_COMMITTED) 03318 && prebuilt->select_lock_type != LOCK_NONE) { 03319 03320 trx_reset_new_rec_lock_info(trx); 03321 } 03322 03323 /*-------------------------------------------------------------*/ 03324 /* PHASE 1: Try to pop the row from the prefetch cache */ 03325 03326 if (UNIV_UNLIKELY(direction == 0)) { 03327 trx->op_info = "starting index read"; 03328 03329 prebuilt->n_rows_fetched = 0; 03330 prebuilt->n_fetch_cached = 0; 03331 prebuilt->fetch_cache_first = 0; 03332 03333 if (prebuilt->sel_graph == NULL) { 03334 /* Build a dummy select query graph */ 03335 row_prebuild_sel_graph(prebuilt); 03336 } 03337 } else { 03338 trx->op_info = "fetching rows"; 03339 03340 if (prebuilt->n_rows_fetched == 0) { 03341 prebuilt->fetch_direction = direction; 03342 } 03343 03344 if (UNIV_UNLIKELY(direction != prebuilt->fetch_direction)) { 03345 if (UNIV_UNLIKELY(prebuilt->n_fetch_cached > 0)) { 03346 ut_error; 03347 /* TODO: scrollable cursor: restore cursor to 03348 the place of the latest returned row, 03349 or better: prevent caching for a scroll 03350 cursor! */ 03351 } 03352 03353 prebuilt->n_rows_fetched = 0; 03354 prebuilt->n_fetch_cached = 0; 03355 prebuilt->fetch_cache_first = 0; 03356 03357 } else if (UNIV_LIKELY(prebuilt->n_fetch_cached > 0)) { 03358 row_sel_pop_cached_row_for_mysql(buf, prebuilt); 03359 03360 prebuilt->n_rows_fetched++; 03361 03362 srv_n_rows_read++; 03363 err = DB_SUCCESS; 03364 goto func_exit; 03365 } 03366 03367 if (prebuilt->fetch_cache_first > 0 03368 && prebuilt->fetch_cache_first < MYSQL_FETCH_CACHE_SIZE) { 03369 03370 /* The previous returned row was popped from the fetch 03371 cache, but the cache was not full at the time of the 03372 popping: no more rows can exist in the result set */ 03373 03374 err = DB_RECORD_NOT_FOUND; 03375 goto func_exit; 03376 } 03377 03378 prebuilt->n_rows_fetched++; 03379 03380 if (prebuilt->n_rows_fetched > 1000000000) { 03381 /* Prevent wrap-over */ 03382 prebuilt->n_rows_fetched = 500000000; 03383 } 03384 03385 mode = pcur->search_mode; 03386 } 03387 03388 /* In a search where at most one record in the index may match, we 03389 can use a LOCK_REC_NOT_GAP type record lock when locking a non-delete- 03390 marked matching record. 03391 03392 Note that in a unique secondary index there may be different delete- 03393 marked versions of a record where only the primary key values differ: 03394 thus in a secondary index we must use next-key locks when locking 03395 delete-marked records. */ 03396 03397 if (match_mode == ROW_SEL_EXACT 03398 && index->type & DICT_UNIQUE 03399 && dtuple_get_n_fields(search_tuple) 03400 == dict_index_get_n_unique(index) 03401 && (index->type & DICT_CLUSTERED 03402 || !dtuple_contains_null(search_tuple))) { 03403 03404 /* Note above that a UNIQUE secondary index can contain many 03405 rows with the same key value if one of the columns is the SQL 03406 null. A clustered index under MySQL can never contain null 03407 columns because we demand that all the columns in primary key 03408 are non-null. */ 03409 03410 unique_search = TRUE; 03411 03412 /* Even if the condition is unique, MySQL seems to try to 03413 retrieve also a second row if a primary key contains more than 03414 1 column. Return immediately if this is not a HANDLER 03415 command. */ 03416 03417 if (UNIV_UNLIKELY(direction != 0 && 03418 !prebuilt->used_in_HANDLER)) { 03419 03420 err = DB_RECORD_NOT_FOUND; 03421 goto func_exit; 03422 } 03423 } 03424 03425 mtr_start(&mtr); 03426 03427 /*-------------------------------------------------------------*/ 03428 /* PHASE 2: Try fast adaptive hash index search if possible */ 03429 03430 /* Next test if this is the special case where we can use the fast 03431 adaptive hash index to try the search. Since we must release the 03432 search system latch when we retrieve an externally stored field, we 03433 cannot use the adaptive hash index in a search in the case the row 03434 may be long and there may be externally stored fields */ 03435 03436 if (UNIV_UNLIKELY(direction == 0) 03437 && unique_search 03438 && index->type & DICT_CLUSTERED 03439 && !prebuilt->templ_contains_blob 03440 && !prebuilt->used_in_HANDLER 03441 && (prebuilt->mysql_row_len < UNIV_PAGE_SIZE / 8)) { 03442 03443 mode = PAGE_CUR_GE; 03444 03445 unique_search_from_clust_index = TRUE; 03446 03447 if (trx->mysql_n_tables_locked == 0 03448 && prebuilt->select_lock_type == LOCK_NONE 03449 && trx->isolation_level > TRX_ISO_READ_UNCOMMITTED 03450 && trx->read_view) { 03451 03452 /* This is a SELECT query done as a consistent read, 03453 and the read view has already been allocated: 03454 let us try a search shortcut through the hash 03455 index. 03456 NOTE that we must also test that 03457 mysql_n_tables_locked == 0, because this might 03458 also be INSERT INTO ... SELECT ... or 03459 CREATE TABLE ... SELECT ... . Our algorithm is 03460 NOT prepared to inserts interleaved with the SELECT, 03461 and if we try that, we can deadlock on the adaptive 03462 hash index semaphore! */ 03463 03464 #ifndef UNIV_SEARCH_DEBUG 03465 if (!trx->has_search_latch) { 03466 rw_lock_s_lock(&btr_search_latch); 03467 trx->has_search_latch = TRUE; 03468 } 03469 #endif 03470 switch (row_sel_try_search_shortcut_for_mysql(&rec, 03471 prebuilt, &offsets, &heap, &mtr)) { 03472 case SEL_FOUND: 03473 #ifdef UNIV_SEARCH_DEBUG 03474 ut_a(0 == cmp_dtuple_rec(search_tuple, 03475 rec, offsets)); 03476 #endif 03477 if (!row_sel_store_mysql_rec(buf, prebuilt, 03478 rec, offsets)) { 03479 err = DB_TOO_BIG_RECORD; 03480 03481 /* We let the main loop to do the 03482 error handling */ 03483 goto shortcut_fails_too_big_rec; 03484 } 03485 03486 mtr_commit(&mtr); 03487 03488 /* ut_print_name(stderr, index->name); 03489 fputs(" shortcut\n", stderr); */ 03490 03491 srv_n_rows_read++; 03492 03493 if (trx->search_latch_timeout > 0 03494 && trx->has_search_latch) { 03495 03496 trx->search_latch_timeout--; 03497 03498 rw_lock_s_unlock(&btr_search_latch); 03499 trx->has_search_latch = FALSE; 03500 } 03501 03502 /* NOTE that we do NOT store the cursor 03503 position */ 03504 err = DB_SUCCESS; 03505 goto func_exit; 03506 03507 case SEL_EXHAUSTED: 03508 mtr_commit(&mtr); 03509 03510 /* ut_print_name(stderr, index->name); 03511 fputs(" record not found 2\n", stderr); */ 03512 03513 if (trx->search_latch_timeout > 0 03514 && trx->has_search_latch) { 03515 03516 trx->search_latch_timeout--; 03517 03518 rw_lock_s_unlock(&btr_search_latch); 03519 trx->has_search_latch = FALSE; 03520 } 03521 03522 /* NOTE that we do NOT store the cursor 03523 position */ 03524 03525 err = DB_RECORD_NOT_FOUND; 03526 goto func_exit; 03527 } 03528 shortcut_fails_too_big_rec: 03529 mtr_commit(&mtr); 03530 mtr_start(&mtr); 03531 } 03532 } 03533 03534 /*-------------------------------------------------------------*/ 03535 /* PHASE 3: Open or restore index cursor position */ 03536 03537 if (trx->has_search_latch) { 03538 rw_lock_s_unlock(&btr_search_latch); 03539 trx->has_search_latch = FALSE; 03540 } 03541 03542 trx_start_if_not_started(trx); 03543 03544 if (trx->isolation_level <= TRX_ISO_READ_COMMITTED 03545 && prebuilt->select_lock_type != LOCK_NONE 03546 && trx->mysql_query_str && trx->mysql_thd) { 03547 03548 /* Scan the MySQL query string; check if SELECT is the first 03549 word there */ 03550 03551 if (dict_str_starts_with_keyword(trx->mysql_thd, 03552 *trx->mysql_query_str, "SELECT")) { 03553 /* It is a plain locking SELECT and the isolation 03554 level is low: do not lock gaps */ 03555 03556 set_also_gap_locks = FALSE; 03557 } 03558 } 03559 03560 /* Note that if the search mode was GE or G, then the cursor 03561 naturally moves upward (in fetch next) in alphabetical order, 03562 otherwise downward */ 03563 03564 if (UNIV_UNLIKELY(direction == 0)) { 03565 if (mode == PAGE_CUR_GE || mode == PAGE_CUR_G) { 03566 moves_up = TRUE; 03567 } 03568 } else if (direction == ROW_SEL_NEXT) { 03569 moves_up = TRUE; 03570 } 03571 03572 thr = que_fork_get_first_thr(prebuilt->sel_graph); 03573 03574 que_thr_move_to_run_state_for_mysql(thr, trx); 03575 03576 clust_index = dict_table_get_first_index(index->table); 03577 03578 if (UNIV_LIKELY(direction != 0)) { 03579 ibool need_to_process = sel_restore_position_for_mysql( 03580 &same_user_rec, BTR_SEARCH_LEAF, 03581 pcur, moves_up, &mtr); 03582 03583 if (UNIV_UNLIKELY(need_to_process)) { 03584 if (UNIV_UNLIKELY(prebuilt->row_read_type 03585 == ROW_READ_DID_SEMI_CONSISTENT)) { 03586 /* We did a semi-consistent read, 03587 but the record was removed in 03588 the meantime. */ 03589 prebuilt->row_read_type 03590 = ROW_READ_TRY_SEMI_CONSISTENT; 03591 } 03592 } else if (UNIV_LIKELY(prebuilt->row_read_type 03593 != ROW_READ_DID_SEMI_CONSISTENT)) { 03594 03595 /* The cursor was positioned on the record 03596 that we returned previously. If we need 03597 to repeat a semi-consistent read as a 03598 pessimistic locking read, the record 03599 cannot be skipped. */ 03600 03601 goto next_rec; 03602 } 03603 03604 } else if (dtuple_get_n_fields(search_tuple) > 0) { 03605 03606 btr_pcur_open_with_no_init(index, search_tuple, mode, 03607 BTR_SEARCH_LEAF, 03608 pcur, 0, &mtr); 03609 03610 pcur->trx_if_known = trx; 03611 } else { 03612 if (mode == PAGE_CUR_G) { 03613 btr_pcur_open_at_index_side(TRUE, index, 03614 BTR_SEARCH_LEAF, pcur, FALSE, &mtr); 03615 } else if (mode == PAGE_CUR_L) { 03616 btr_pcur_open_at_index_side(FALSE, index, 03617 BTR_SEARCH_LEAF, pcur, FALSE, &mtr); 03618 } 03619 } 03620 03621 if (!prebuilt->sql_stat_start) { 03622 /* No need to set an intention lock or assign a read view */ 03623 03624 if (trx->read_view == NULL 03625 && prebuilt->select_lock_type == LOCK_NONE) { 03626 03627 fputs( 03628 "InnoDB: Error: MySQL is trying to perform a consistent read\n" 03629 "InnoDB: but the read view is not assigned!\n", stderr); 03630 trx_print(stderr, trx, 600); 03631 fputc('\n', stderr); 03632 ut_a(0); 03633 } 03634 } else if (prebuilt->select_lock_type == LOCK_NONE) { 03635 /* This is a consistent read */ 03636 /* Assign a read view for the query */ 03637 03638 trx_assign_read_view(trx); 03639 prebuilt->sql_stat_start = FALSE; 03640 } else { 03641 ulint lock_mode; 03642 if (prebuilt->select_lock_type == LOCK_S) { 03643 lock_mode = LOCK_IS; 03644 } else { 03645 lock_mode = LOCK_IX; 03646 } 03647 err = lock_table(0, index->table, lock_mode, thr); 03648 03649 if (err != DB_SUCCESS) { 03650 03651 goto lock_wait_or_error; 03652 } 03653 prebuilt->sql_stat_start = FALSE; 03654 } 03655 03656 rec_loop: 03657 /*-------------------------------------------------------------*/ 03658 /* PHASE 4: Look for matching records in a loop */ 03659 03660 rec = btr_pcur_get_rec(pcur); 03661 ut_ad(!!page_rec_is_comp(rec) == comp); 03662 #ifdef UNIV_SEARCH_DEBUG 03663 /* 03664 fputs("Using ", stderr); 03665 dict_index_name_print(stderr, index); 03666 fprintf(stderr, " cnt %lu ; Page no %lu\n", cnt, 03667 buf_frame_get_page_no(buf_frame_align(rec))); 03668 rec_print(rec); 03669 */ 03670 #endif /* UNIV_SEARCH_DEBUG */ 03671 03672 if (page_rec_is_infimum(rec)) { 03673 03674 /* The infimum record on a page cannot be in the result set, 03675 and neither can a record lock be placed on it: we skip such 03676 a record. */ 03677 03678 goto next_rec; 03679 } 03680 03681 if (page_rec_is_supremum(rec)) { 03682 03683 if (set_also_gap_locks 03684 && !(srv_locks_unsafe_for_binlog 03685 || trx->isolation_level == TRX_ISO_READ_COMMITTED) 03686 && prebuilt->select_lock_type != LOCK_NONE) { 03687 03688 /* Try to place a lock on the index record */ 03689 03690 /* If innodb_locks_unsafe_for_binlog option is used 03691 or this session is using a READ COMMITTED isolation 03692 level we do not lock gaps. Supremum record is really 03693 a gap and therefore we do not set locks there. */ 03694 03695 offsets = rec_get_offsets(rec, index, offsets, 03696 ULINT_UNDEFINED, &heap); 03697 err = sel_set_rec_lock(rec, index, offsets, 03698 prebuilt->select_lock_type, 03699 LOCK_ORDINARY, thr); 03700 03701 if (err != DB_SUCCESS) { 03702 03703 goto lock_wait_or_error; 03704 } 03705 } 03706 /* A page supremum record cannot be in the result set: skip 03707 it now that we have placed a possible lock on it */ 03708 03709 goto next_rec; 03710 } 03711 03712 /*-------------------------------------------------------------*/ 03713 /* Do sanity checks in case our cursor has bumped into page 03714 corruption */ 03715 03716 if (comp) { 03717 next_offs = rec_get_next_offs(rec, TRUE); 03718 if (UNIV_UNLIKELY(next_offs < PAGE_NEW_SUPREMUM)) { 03719 03720 goto wrong_offs; 03721 } 03722 } else { 03723 next_offs = rec_get_next_offs(rec, FALSE); 03724 if (UNIV_UNLIKELY(next_offs < PAGE_OLD_SUPREMUM)) { 03725 03726 goto wrong_offs; 03727 } 03728 } 03729 03730 if (UNIV_UNLIKELY(next_offs >= UNIV_PAGE_SIZE - PAGE_DIR)) { 03731 03732 wrong_offs: 03733 if (srv_force_recovery == 0 || moves_up == FALSE) { 03734 ut_print_timestamp(stderr); 03735 buf_page_print(buf_frame_align(rec)); 03736 fprintf(stderr, 03737 "\nInnoDB: rec address %p, first buffer frame %p\n" 03738 "InnoDB: buffer pool high end %p, buf block fix count %lu\n", 03739 rec, buf_pool->frame_zero, 03740 buf_pool->high_end, 03741 (ulong)buf_block_align(rec)->buf_fix_count); 03742 fprintf(stderr, 03743 "InnoDB: Index corruption: rec offs %lu next offs %lu, page no %lu,\n" 03744 "InnoDB: ", 03745 (ulong) ut_align_offset(rec, UNIV_PAGE_SIZE), 03746 (ulong) next_offs, 03747 (ulong) buf_frame_get_page_no(rec)); 03748 dict_index_name_print(stderr, trx, index); 03749 fputs(". Run CHECK TABLE. You may need to\n" 03750 "InnoDB: restore from a backup, or dump + drop + reimport the table.\n", 03751 stderr); 03752 03753 err = DB_CORRUPTION; 03754 03755 goto lock_wait_or_error; 03756 } else { 03757 /* The user may be dumping a corrupt table. Jump 03758 over the corruption to recover as much as possible. */ 03759 03760 fprintf(stderr, 03761 "InnoDB: Index corruption: rec offs %lu next offs %lu, page no %lu,\n" 03762 "InnoDB: ", 03763 (ulong) ut_align_offset(rec, UNIV_PAGE_SIZE), 03764 (ulong) next_offs, 03765 (ulong) buf_frame_get_page_no(rec)); 03766 dict_index_name_print(stderr, trx, index); 03767 fputs(". We try to skip the rest of the page.\n", 03768 stderr); 03769 03770 btr_pcur_move_to_last_on_page(pcur, &mtr); 03771 03772 goto next_rec; 03773 } 03774 } 03775 /*-------------------------------------------------------------*/ 03776 03777 /* Calculate the 'offsets' associated with 'rec' */ 03778 03779 offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); 03780 03781 if (UNIV_UNLIKELY(srv_force_recovery > 0)) { 03782 if (!rec_validate(rec, offsets) 03783 || !btr_index_rec_validate(rec, index, FALSE)) { 03784 fprintf(stderr, 03785 "InnoDB: Index corruption: rec offs %lu next offs %lu, page no %lu,\n" 03786 "InnoDB: ", 03787 (ulong) ut_align_offset(rec, UNIV_PAGE_SIZE), 03788 (ulong) next_offs, 03789 (ulong) buf_frame_get_page_no(rec)); 03790 dict_index_name_print(stderr, trx, index); 03791 fputs(". We try to skip the record.\n", 03792 stderr); 03793 03794 goto next_rec; 03795 } 03796 } 03797 03798 /* Note that we cannot trust the up_match value in the cursor at this 03799 place because we can arrive here after moving the cursor! Thus 03800 we have to recompare rec and search_tuple to determine if they 03801 match enough. */ 03802 03803 if (match_mode == ROW_SEL_EXACT) { 03804 /* Test if the index record matches completely to search_tuple 03805 in prebuilt: if not, then we return with DB_RECORD_NOT_FOUND */ 03806 03807 /* fputs("Comparing rec and search tuple\n", stderr); */ 03808 03809 if (0 != cmp_dtuple_rec(search_tuple, rec, offsets)) { 03810 03811 if (set_also_gap_locks 03812 && !(srv_locks_unsafe_for_binlog 03813 || trx->isolation_level == TRX_ISO_READ_COMMITTED) 03814 && prebuilt->select_lock_type != LOCK_NONE) { 03815 03816 /* Try to place a gap lock on the index 03817 record only if innodb_locks_unsafe_for_binlog 03818 option is not set or this session is not 03819 using a READ COMMITTED isolation level. */ 03820 03821 err = sel_set_rec_lock(rec, index, offsets, 03822 prebuilt->select_lock_type, 03823 LOCK_GAP, thr); 03824 03825 if (err != DB_SUCCESS) { 03826 03827 goto lock_wait_or_error; 03828 } 03829 } 03830 03831 btr_pcur_store_position(pcur, &mtr); 03832 03833 err = DB_RECORD_NOT_FOUND; 03834 /* ut_print_name(stderr, index->name); 03835 fputs(" record not found 3\n", stderr); */ 03836 03837 goto normal_return; 03838 } 03839 03840 } else if (match_mode == ROW_SEL_EXACT_PREFIX) { 03841 03842 if (!cmp_dtuple_is_prefix_of_rec(search_tuple, rec, offsets)) { 03843 03844 if (set_also_gap_locks 03845 && !(srv_locks_unsafe_for_binlog 03846 || trx->isolation_level == TRX_ISO_READ_COMMITTED) 03847 && prebuilt->select_lock_type != LOCK_NONE) { 03848 03849 /* Try to place a gap lock on the index 03850 record only if innodb_locks_unsafe_for_binlog 03851 option is not set or this session is not 03852 using a READ COMMITTED isolation level. */ 03853 03854 err = sel_set_rec_lock(rec, index, offsets, 03855 prebuilt->select_lock_type, 03856 LOCK_GAP, thr); 03857 03858 if (err != DB_SUCCESS) { 03859 03860 goto lock_wait_or_error; 03861 } 03862 } 03863 03864 btr_pcur_store_position(pcur, &mtr); 03865 03866 err = DB_RECORD_NOT_FOUND; 03867 /* ut_print_name(stderr, index->name); 03868 fputs(" record not found 4\n", stderr); */ 03869 03870 goto normal_return; 03871 } 03872 } 03873 03874 /* We are ready to look at a possible new index entry in the result 03875 set: the cursor is now placed on a user record */ 03876 03877 if (prebuilt->select_lock_type != LOCK_NONE) { 03878 /* Try to place a lock on the index record; note that delete 03879 marked records are a special case in a unique search. If there 03880 is a non-delete marked record, then it is enough to lock its 03881 existence with LOCK_REC_NOT_GAP. */ 03882 03883 /* If innodb_locks_unsafe_for_binlog option is used 03884 or this session is using a READ COMMITED isolation 03885 level we lock only the record, i.e., next-key locking is 03886 not used. */ 03887 03888 ulint lock_type; 03889 03890 if (!set_also_gap_locks 03891 || srv_locks_unsafe_for_binlog 03892 || trx->isolation_level == TRX_ISO_READ_COMMITTED 03893 || (unique_search 03894 && !UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp)))) { 03895 03896 goto no_gap_lock; 03897 } else { 03898 lock_type = LOCK_ORDINARY; 03899 } 03900 03901 /* If we are doing a 'greater or equal than a primary key 03902 value' search from a clustered index, and we find a record 03903 that has that exact primary key value, then there is no need 03904 to lock the gap before the record, because no insert in the 03905 gap can be in our search range. That is, no phantom row can 03906 appear that way. 03907 03908 An example: if col1 is the primary key, the search is WHERE 03909 col1 >= 100, and we find a record where col1 = 100, then no 03910 need to lock the gap before that record. */ 03911 03912 if (index == clust_index 03913 && mode == PAGE_CUR_GE 03914 && direction == 0 03915 && dtuple_get_n_fields_cmp(search_tuple) 03916 == dict_index_get_n_unique(index) 03917 && 0 == cmp_dtuple_rec(search_tuple, rec, offsets)) { 03918 no_gap_lock: 03919 lock_type = LOCK_REC_NOT_GAP; 03920 } 03921 03922 err = sel_set_rec_lock(rec, index, offsets, 03923 prebuilt->select_lock_type, 03924 lock_type, thr); 03925 03926 switch (err) { 03927 rec_t* old_vers; 03928 case DB_SUCCESS: 03929 break; 03930 case DB_LOCK_WAIT: 03931 if (UNIV_LIKELY(prebuilt->row_read_type 03932 != ROW_READ_TRY_SEMI_CONSISTENT) 03933 || index != clust_index) { 03934 03935 goto lock_wait_or_error; 03936 } 03937 03938 /* The following call returns 'offsets' 03939 associated with 'old_vers' */ 03940 err = row_sel_build_committed_vers_for_mysql( 03941 clust_index, prebuilt, rec, 03942 &offsets, &heap, 03943 &old_vers, &mtr); 03944 03945 if (err != DB_SUCCESS) { 03946 03947 goto lock_wait_or_error; 03948 } 03949 03950 mutex_enter(&kernel_mutex); 03951 if (trx->was_chosen_as_deadlock_victim) { 03952 mutex_exit(&kernel_mutex); 03953 03954 goto lock_wait_or_error; 03955 } 03956 if (UNIV_LIKELY(trx->wait_lock != NULL)) { 03957 lock_cancel_waiting_and_release( 03958 trx->wait_lock); 03959 trx_reset_new_rec_lock_info(trx); 03960 } else { 03961 mutex_exit(&kernel_mutex); 03962 03963 /* The lock was granted while we were 03964 searching for the last committed version. 03965 Do a normal locking read. */ 03966 03967 offsets = rec_get_offsets(rec, index, offsets, 03968 ULINT_UNDEFINED, &heap); 03969 err = DB_SUCCESS; 03970 break; 03971 } 03972 mutex_exit(&kernel_mutex); 03973 03974 if (old_vers == NULL) { 03975 /* The row was not yet committed */ 03976 03977 goto next_rec; 03978 } 03979 03980 did_semi_consistent_read = TRUE; 03981 rec = old_vers; 03982 break; 03983 default: 03984 03985 goto lock_wait_or_error; 03986 } 03987 } else { 03988 /* This is a non-locking consistent read: if necessary, fetch 03989 a previous version of the record */ 03990 03991 if (trx->isolation_level == TRX_ISO_READ_UNCOMMITTED) { 03992 03993 /* Do nothing: we let a non-locking SELECT read the 03994 latest version of the record */ 03995 03996 } else if (index == clust_index) { 03997 03998 /* Fetch a previous version of the row if the current 03999 one is not visible in the snapshot; if we have a very 04000 high force recovery level set, we try to avoid crashes 04001 by skipping this lookup */ 04002 04003 if (UNIV_LIKELY(srv_force_recovery < 5) 04004 && !lock_clust_rec_cons_read_sees(rec, index, 04005 offsets, trx->read_view)) { 04006 04007 rec_t* old_vers; 04008 /* The following call returns 'offsets' 04009 associated with 'old_vers' */ 04010 err = row_sel_build_prev_vers_for_mysql( 04011 trx->read_view, clust_index, 04012 prebuilt, rec, 04013 &offsets, &heap, 04014 &old_vers, &mtr); 04015 04016 if (err != DB_SUCCESS) { 04017 04018 goto lock_wait_or_error; 04019 } 04020 04021 if (old_vers == NULL) { 04022 /* The row did not exist yet in 04023 the read view */ 04024 04025 goto next_rec; 04026 } 04027 04028 rec = old_vers; 04029 } 04030 } else if (!lock_sec_rec_cons_read_sees(rec, index, 04031 trx->read_view)) { 04032 /* We are looking into a non-clustered index, 04033 and to get the right version of the record we 04034 have to look also into the clustered index: this 04035 is necessary, because we can only get the undo 04036 information via the clustered index record. */ 04037 04038 ut_ad(index != clust_index); 04039 04040 goto requires_clust_rec; 04041 } 04042 } 04043 04044 /* NOTE that at this point rec can be an old version of a clustered 04045 index record built for a consistent read. We cannot assume after this 04046 point that rec is on a buffer pool page. Functions like 04047 page_rec_is_comp() cannot be used! */ 04048 04049 if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp))) { 04050 04051 /* The record is delete-marked: we can skip it */ 04052 04053 if ((srv_locks_unsafe_for_binlog 04054 || trx->isolation_level == TRX_ISO_READ_COMMITTED) 04055 && prebuilt->select_lock_type != LOCK_NONE 04056 && !did_semi_consistent_read) { 04057 04058 /* No need to keep a lock on a delete-marked record 04059 if we do not want to use next-key locking. */ 04060 04061 row_unlock_for_mysql(prebuilt, TRUE); 04062 } 04063 04064 goto next_rec; 04065 } 04066 04067 /* Get the clustered index record if needed, if we did not do the 04068 search using the clustered index. */ 04069 04070 if (index != clust_index && prebuilt->need_to_access_clustered) { 04071 04072 requires_clust_rec: 04073 /* We use a 'goto' to the preceding label if a consistent 04074 read of a secondary index record requires us to look up old 04075 versions of the associated clustered index record. */ 04076 04077 ut_ad(rec_offs_validate(rec, index, offsets)); 04078 04079 /* It was a non-clustered index and we must fetch also the 04080 clustered index record */ 04081 04082 mtr_has_extra_clust_latch = TRUE; 04083 04084 /* The following call returns 'offsets' associated with 04085 'clust_rec'. Note that 'clust_rec' can be an old version 04086 built for a consistent read. */ 04087 04088 err = row_sel_get_clust_rec_for_mysql(prebuilt, index, rec, 04089 thr, &clust_rec, 04090 &offsets, &heap, &mtr); 04091 if (err != DB_SUCCESS) { 04092 04093 goto lock_wait_or_error; 04094 } 04095 04096 if (clust_rec == NULL) { 04097 /* The record did not exist in the read view */ 04098 ut_ad(prebuilt->select_lock_type == LOCK_NONE); 04099 04100 goto next_rec; 04101 } 04102 04103 if (UNIV_UNLIKELY(rec_get_deleted_flag(clust_rec, comp))) { 04104 04105 /* The record is delete marked: we can skip it */ 04106 04107 if ((srv_locks_unsafe_for_binlog 04108 || trx->isolation_level == TRX_ISO_READ_COMMITTED) 04109 && prebuilt->select_lock_type != LOCK_NONE) { 04110 04111 /* No need to keep a lock on a delete-marked 04112 record if we do not want to use next-key 04113 locking. */ 04114 04115 row_unlock_for_mysql(prebuilt, TRUE); 04116 } 04117 04118 goto next_rec; 04119 } 04120 04121 if (prebuilt->need_to_access_clustered) { 04122 04123 result_rec = clust_rec; 04124 04125 ut_ad(rec_offs_validate(result_rec, clust_index, 04126 offsets)); 04127 } else { 04128 /* We used 'offsets' for the clust rec, recalculate 04129 them for 'rec' */ 04130 offsets = rec_get_offsets(rec, index, offsets, 04131 ULINT_UNDEFINED, &heap); 04132 result_rec = rec; 04133 } 04134 } else { 04135 result_rec = rec; 04136 } 04137 04138 /* We found a qualifying record 'result_rec'. At this point, 04139 'offsets' are associated with 'result_rec'. */ 04140 04141 ut_ad(rec_offs_validate(result_rec, 04142 result_rec != rec ? clust_index : index, 04143 offsets)); 04144 04145 if ((match_mode == ROW_SEL_EXACT 04146 || prebuilt->n_rows_fetched >= MYSQL_FETCH_CACHE_THRESHOLD) 04147 && prebuilt->select_lock_type == LOCK_NONE 04148 && !prebuilt->templ_contains_blob 04149 && !prebuilt->clust_index_was_generated 04150 && !prebuilt->used_in_HANDLER 04151 && prebuilt->template_type 04152 != ROW_MYSQL_DUMMY_TEMPLATE) { 04153 04154 /* Inside an update, for example, we do not cache rows, 04155 since we may use the cursor position to do the actual 04156 update, that is why we require ...lock_type == LOCK_NONE. 04157 Since we keep space in prebuilt only for the BLOBs of 04158 a single row, we cannot cache rows in the case there 04159 are BLOBs in the fields to be fetched. In HANDLER we do 04160 not cache rows because there the cursor is a scrollable 04161 cursor. */ 04162 04163 row_sel_push_cache_row_for_mysql(prebuilt, result_rec, 04164 offsets); 04165 if (prebuilt->n_fetch_cached == MYSQL_FETCH_CACHE_SIZE) { 04166 04167 goto got_row; 04168 } 04169 04170 goto next_rec; 04171 } else { 04172 if (prebuilt->template_type == ROW_MYSQL_DUMMY_TEMPLATE) { 04173 memcpy(buf + 4, result_rec 04174 - rec_offs_extra_size(offsets), 04175 rec_offs_size(offsets)); 04176 mach_write_to_4(buf, 04177 rec_offs_extra_size(offsets) + 4); 04178 } else { 04179 if (!row_sel_store_mysql_rec(buf, prebuilt, 04180 result_rec, offsets)) { 04181 err = DB_TOO_BIG_RECORD; 04182 04183 goto lock_wait_or_error; 04184 } 04185 } 04186 04187 if (prebuilt->clust_index_was_generated) { 04188 if (result_rec != rec) { 04189 offsets = rec_get_offsets( 04190 rec, index, offsets, 04191 ULINT_UNDEFINED, &heap); 04192 } 04193 row_sel_store_row_id_to_prebuilt(prebuilt, rec, 04194 index, offsets); 04195 } 04196 } 04197 04198 /* From this point on, 'offsets' are invalid. */ 04199 04200 got_row: 04201 /* We have an optimization to save CPU time: if this is a consistent 04202 read on a unique condition on the clustered index, then we do not 04203 store the pcur position, because any fetch next or prev will anyway 04204 return 'end of file'. Exceptions are locking reads and the MySQL 04205 HANDLER command where the user can move the cursor with PREV or NEXT 04206 even after a unique search. */ 04207 04208 if (!unique_search_from_clust_index 04209 || prebuilt->select_lock_type != LOCK_NONE 04210 || prebuilt->used_in_HANDLER) { 04211 04212 /* Inside an update always store the cursor position */ 04213 04214 btr_pcur_store_position(pcur, &mtr); 04215 } 04216 04217 err = DB_SUCCESS; 04218 04219 goto normal_return; 04220 04221 next_rec: 04222 /* Reset the old and new "did semi-consistent read" flags. */ 04223 if (UNIV_UNLIKELY(prebuilt->row_read_type 04224 == ROW_READ_DID_SEMI_CONSISTENT)) { 04225 prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT; 04226 } 04227 did_semi_consistent_read = FALSE; 04228 04229 if (UNIV_UNLIKELY(srv_locks_unsafe_for_binlog 04230 || trx->isolation_level == TRX_ISO_READ_COMMITTED) 04231 && prebuilt->select_lock_type != LOCK_NONE) { 04232 04233 trx_reset_new_rec_lock_info(trx); 04234 } 04235 04236 /*-------------------------------------------------------------*/ 04237 /* PHASE 5: Move the cursor to the next index record */ 04238 04239 if (UNIV_UNLIKELY(mtr_has_extra_clust_latch)) { 04240 /* We must commit mtr if we are moving to the next 04241 non-clustered index record, because we could break the 04242 latching order if we would access a different clustered 04243 index page right away without releasing the previous. */ 04244 04245 btr_pcur_store_position(pcur, &mtr); 04246 04247 mtr_commit(&mtr); 04248 mtr_has_extra_clust_latch = FALSE; 04249 04250 mtr_start(&mtr); 04251 if (sel_restore_position_for_mysql(&same_user_rec, 04252 BTR_SEARCH_LEAF, 04253 pcur, moves_up, &mtr)) { 04254 #ifdef UNIV_SEARCH_DEBUG 04255 cnt++; 04256 #endif /* UNIV_SEARCH_DEBUG */ 04257 04258 goto rec_loop; 04259 } 04260 } 04261 04262 if (moves_up) { 04263 if (UNIV_UNLIKELY(!btr_pcur_move_to_next(pcur, &mtr))) { 04264 not_moved: 04265 btr_pcur_store_position(pcur, &mtr); 04266 04267 if (match_mode != 0) { 04268 err = DB_RECORD_NOT_FOUND; 04269 } else { 04270 err = DB_END_OF_INDEX; 04271 } 04272 04273 goto normal_return; 04274 } 04275 } else { 04276 if (UNIV_UNLIKELY(!btr_pcur_move_to_prev(pcur, &mtr))) { 04277 goto not_moved; 04278 } 04279 } 04280 04281 #ifdef UNIV_SEARCH_DEBUG 04282 cnt++; 04283 #endif /* UNIV_SEARCH_DEBUG */ 04284 04285 goto rec_loop; 04286 04287 lock_wait_or_error: 04288 /* Reset the old and new "did semi-consistent read" flags. */ 04289 if (UNIV_UNLIKELY(prebuilt->row_read_type 04290 == ROW_READ_DID_SEMI_CONSISTENT)) { 04291 prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT; 04292 } 04293 did_semi_consistent_read = FALSE; 04294 04295 /*-------------------------------------------------------------*/ 04296 04297 btr_pcur_store_position(pcur, &mtr); 04298 04299 mtr_commit(&mtr); 04300 mtr_has_extra_clust_latch = FALSE; 04301 04302 trx->error_state = err; 04303 04304 /* The following is a patch for MySQL */ 04305 04306 que_thr_stop_for_mysql(thr); 04307 04308 thr->lock_state = QUE_THR_LOCK_ROW; 04309 04310 if (row_mysql_handle_errors(&err, trx, thr, NULL)) { 04311 /* It was a lock wait, and it ended */ 04312 04313 thr->lock_state = QUE_THR_LOCK_NOLOCK; 04314 mtr_start(&mtr); 04315 04316 sel_restore_position_for_mysql(&same_user_rec, 04317 BTR_SEARCH_LEAF, pcur, 04318 moves_up, &mtr); 04319 04320 if ((srv_locks_unsafe_for_binlog 04321 || trx->isolation_level == TRX_ISO_READ_COMMITTED) 04322 && !same_user_rec) { 04323 04324 /* Since we were not able to restore the cursor 04325 on the same user record, we cannot use 04326 row_unlock_for_mysql() to unlock any records, and 04327 we must thus reset the new rec lock info. Since 04328 in lock0lock.c we have blocked the inheriting of gap 04329 X-locks, we actually do not have any new record locks 04330 set in this case. 04331 04332 Note that if we were able to restore on the 'same' 04333 user record, it is still possible that we were actually 04334 waiting on a delete-marked record, and meanwhile 04335 it was removed by purge and inserted again by some 04336 other user. But that is no problem, because in 04337 rec_loop we will again try to set a lock, and 04338 new_rec_lock_info in trx will be right at the end. */ 04339 04340 trx_reset_new_rec_lock_info(trx); 04341 } 04342 04343 mode = pcur->search_mode; 04344 04345 goto rec_loop; 04346 } 04347 04348 thr->lock_state = QUE_THR_LOCK_NOLOCK; 04349 04350 #ifdef UNIV_SEARCH_DEBUG 04351 /* fputs("Using ", stderr); 04352 dict_index_name_print(stderr, index); 04353 fprintf(stderr, " cnt %lu ret value %lu err\n", cnt, err); */ 04354 #endif /* UNIV_SEARCH_DEBUG */ 04355 goto func_exit; 04356 04357 normal_return: 04358 /*-------------------------------------------------------------*/ 04359 que_thr_stop_for_mysql_no_error(thr, trx); 04360 04361 mtr_commit(&mtr); 04362 04363 if (prebuilt->n_fetch_cached > 0) { 04364 row_sel_pop_cached_row_for_mysql(buf, prebuilt); 04365 04366 err = DB_SUCCESS; 04367 } 04368 04369 #ifdef UNIV_SEARCH_DEBUG 04370 /* fputs("Using ", stderr); 04371 dict_index_name_print(stderr, index); 04372 fprintf(stderr, " cnt %lu ret value %lu err\n", cnt, err); */ 04373 #endif /* UNIV_SEARCH_DEBUG */ 04374 if (err == DB_SUCCESS) { 04375 srv_n_rows_read++; 04376 } 04377 04378 func_exit: 04379 trx->op_info = ""; 04380 if (UNIV_LIKELY_NULL(heap)) { 04381 mem_heap_free(heap); 04382 } 04383 04384 /* Set or reset the "did semi-consistent read" flag on return. 04385 The flag did_semi_consistent_read is set if and only if 04386 the record being returned was fetched with a semi-consistent read. */ 04387 ut_ad(prebuilt->row_read_type != ROW_READ_WITH_LOCKS 04388 || !did_semi_consistent_read); 04389 04390 if (UNIV_UNLIKELY(prebuilt->row_read_type != ROW_READ_WITH_LOCKS)) { 04391 if (UNIV_UNLIKELY(did_semi_consistent_read)) { 04392 prebuilt->row_read_type = ROW_READ_DID_SEMI_CONSISTENT; 04393 } else { 04394 prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT; 04395 } 04396 } 04397 return(err); 04398 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static ulint row_sel | ( | sel_node_t * | node, | |
| que_thr_t * | thr | |||
| ) | [static] |
Definition at line 1150 of file row0sel.c.
References sel_node_struct::aggregate_already_fetched, sel_node_struct::asc, BIG_ROW_SIZE, btr_pcur_get_rec(), btr_pcur_get_up_match(), btr_pcur_is_after_last_on_page(), btr_pcur_is_before_first_on_page(), btr_pcur_is_on_user_rec(), btr_pcur_move_to_next(), btr_pcur_move_to_prev(), btr_pcur_store_position(), btr_search_latch, sel_node_struct::can_get_updated, DB_SUCCESS, DICT_CLUSTERED, dict_table_is_comp(), err, FALSE, sel_node_struct::fetch_table, index(), sel_node_struct::into_list, sel_node_struct::is_aggregate, trx_struct::isolation_level, sel_node_struct::latch_mode, lock_clust_rec_cons_read_sees(), LOCK_ORDINARY, LOCK_REC_NOT_GAP, lock_sec_rec_cons_read_sees(), mem_heap_free, mtr_commit(), mtr_start(), sel_node_struct::n_tables, NULL, PAGE_CUR_GE, page_rec_get_next(), page_rec_is_infimum(), page_rec_is_supremum(), page_rec_is_user_rec(), plan(), plan_reset_cursor(), que_node_get_parent(), sel_node_struct::read_view, rec_get_deleted_flag(), rec_get_offsets, REC_OFFS_NORMAL_SIZE, sel_node_struct::row_lock_mode, row_sel_build_prev_vers(), row_sel_fetch_columns(), row_sel_get_clust_rec(), row_sel_open_pcur(), row_sel_restore_pcur_pos(), row_sel_test_end_conds(), row_sel_test_other_conds(), row_sel_try_search_shortcut(), row_upd_in_place_in_select(), que_thr_struct::run_node, rw_lock_s_lock, rw_lock_s_unlock, sel_assign_into_var_values(), SEL_COST_LIMIT, sel_eval_select_list(), SEL_EXHAUSTED, SEL_FOUND, SEL_MAX_N_PREFETCH, sel_node_get_nth_plan(), SEL_NODE_NO_MORE_ROWS, sel_pop_prefetched_row(), SEL_PREFETCH_LIMIT, sel_push_prefetched_row(), SEL_RETRY, sel_set_rec_lock(), sel_node_struct::select_will_do_update, srv_locks_unsafe_for_binlog, sel_node_struct::state, sync_thread_levels_empty_gen(), thr_get_trx(), TRUE, TRX_ISO_READ_COMMITTED, ut_ad, and UT_LIST_GET_FIRST.
Referenced by row_sel_step().
01152 : DB_SUCCESS or error code */ 01153 sel_node_t* node, /* in: select node */ 01154 que_thr_t* thr) /* in: query thread */ 01155 { 01156 dict_index_t* index; 01157 plan_t* plan; 01158 mtr_t mtr; 01159 ibool moved; 01160 rec_t* rec; 01161 rec_t* old_vers; 01162 rec_t* clust_rec; 01163 ibool search_latch_locked; 01164 ibool consistent_read; 01165 01166 /* The following flag becomes TRUE when we are doing a 01167 consistent read from a non-clustered index and we must look 01168 at the clustered index to find out the previous delete mark 01169 state of the non-clustered record: */ 01170 01171 ibool cons_read_requires_clust_rec = FALSE; 01172 ulint cost_counter = 0; 01173 ibool cursor_just_opened; 01174 ibool must_go_to_next; 01175 ibool leaf_contains_updates = FALSE; 01176 /* TRUE if select_will_do_update is 01177 TRUE and the current clustered index 01178 leaf page has been updated during 01179 the current mtr: mtr must be committed 01180 at the same time as the leaf x-latch 01181 is released */ 01182 ibool mtr_has_extra_clust_latch = FALSE; 01183 /* TRUE if the search was made using 01184 a non-clustered index, and we had to 01185 access the clustered record: now &mtr 01186 contains a clustered index latch, and 01187 &mtr must be committed before we move 01188 to the next non-clustered record */ 01189 ulint found_flag; 01190 ulint err; 01191 mem_heap_t* heap = NULL; 01192 ulint offsets_[REC_OFFS_NORMAL_SIZE]; 01193 ulint* offsets = offsets_; 01194 *offsets_ = (sizeof offsets_) / sizeof *offsets_; 01195 01196 ut_ad(thr->run_node == node); 01197 01198 search_latch_locked = FALSE; 01199 01200 if (node->read_view) { 01201 /* In consistent reads, we try to do with the hash index and 01202 not to use the buffer page get. This is to reduce memory bus 01203 load resulting from semaphore operations. The search latch 01204 will be s-locked when we access an index with a unique search 01205 condition, but not locked when we access an index with a 01206 less selective search condition. */ 01207 01208 consistent_read = TRUE; 01209 } else { 01210 consistent_read = FALSE; 01211 } 01212 01213 table_loop: 01214 /* TABLE LOOP 01215 ---------- 01216 This is the outer major loop in calculating a join. We come here when 01217 node->fetch_table changes, and after adding a row to aggregate totals 01218 and, of course, when this function is called. */ 01219 01220 ut_ad(leaf_contains_updates == FALSE); 01221 ut_ad(mtr_has_extra_clust_latch == FALSE); 01222 01223 plan = sel_node_get_nth_plan(node, node->fetch_table); 01224 index = plan->index; 01225 01226 if (plan->n_rows_prefetched > 0) { 01227 sel_pop_prefetched_row(plan); 01228 01229 goto next_table_no_mtr; 01230 } 01231 01232 if (plan->cursor_at_end) { 01233 /* The cursor has already reached the result set end: no more 01234 rows to process for this table cursor, as also the prefetch 01235 stack was empty */ 01236 01237 ut_ad(plan->pcur_is_open); 01238 01239 goto table_exhausted_no_mtr; 01240 } 01241 01242 /* Open a cursor to index, or restore an open cursor position */ 01243 01244 mtr_start(&mtr); 01245 01246 if (consistent_read && plan->unique_search && !plan->pcur_is_open 01247 && !plan->must_get_clust 01248 && (plan->table->max_row_size < BIG_ROW_SIZE)) { 01249 if (!search_latch_locked) { 01250 rw_lock_s_lock(&btr_search_latch); 01251 01252 search_latch_locked = TRUE; 01253 } else if (btr_search_latch.writer_is_wait_ex) { 01254 01255 /* There is an x-latch request waiting: release the 01256 s-latch for a moment; as an s-latch here is often 01257 kept for some 10 searches before being released, 01258 a waiting x-latch request would block other threads 01259 from acquiring an s-latch for a long time, lowering 01260 performance significantly in multiprocessors. */ 01261 01262 rw_lock_s_unlock(&btr_search_latch); 01263 rw_lock_s_lock(&btr_search_latch); 01264 } 01265 01266 found_flag = row_sel_try_search_shortcut(node, plan, &mtr); 01267 01268 if (found_flag == SEL_FOUND) { 01269 01270 goto next_table; 01271 01272 } else if (found_flag == SEL_EXHAUSTED) { 01273 01274 goto table_exhausted; 01275 } 01276 01277 ut_ad(found_flag == SEL_RETRY); 01278 01279 plan_reset_cursor(plan); 01280 01281 mtr_commit(&mtr); 01282 mtr_start(&mtr); 01283 } 01284 01285 if (search_latch_locked) { 01286 rw_lock_s_unlock(&btr_search_latch); 01287 01288 search_latch_locked = FALSE; 01289 } 01290 01291 if (!plan->pcur_is_open) { 01292 /* Evaluate the expressions to build the search tuple and 01293 open the cursor */ 01294 01295 row_sel_open_pcur(node, plan, search_latch_locked, &mtr); 01296 01297 cursor_just_opened = TRUE; 01298 01299 /* A new search was made: increment the cost counter */ 01300 cost_counter++; 01301 } else { 01302 /* Restore pcur position to the index */ 01303 01304 must_go_to_next = row_sel_restore_pcur_pos(node, plan, &mtr); 01305 01306 cursor_just_opened = FALSE; 01307 01308 if (must_go_to_next) { 01309 /* We have already processed the cursor record: move 01310 to the next */ 01311 01312 goto next_rec; 01313 } 01314 } 01315 01316 rec_loop: 01317 /* RECORD LOOP 01318 ----------- 01319 In this loop we use pcur and try to fetch a qualifying row, and 01320 also fill the prefetch buffer for this table if n_rows_fetched has 01321 exceeded a threshold. While we are inside this loop, the following 01322 holds: 01323 (1) &mtr is started, 01324 (2) pcur is positioned and open. 01325 01326 NOTE that if cursor_just_opened is TRUE here, it means that we came 01327 to this point right after row_sel_open_pcur. */ 01328 01329 ut_ad(mtr_has_extra_clust_latch == FALSE); 01330 01331 rec = btr_pcur_get_rec(&(plan->pcur)); 01332 01333 /* PHASE 1: Set a lock if specified */ 01334 01335 if (!node->asc && cursor_just_opened 01336 && !page_rec_is_supremum(rec)) { 01337 01338 /* When we open a cursor for a descending search, we must set 01339 a next-key lock on the successor record: otherwise it would 01340 be possible to insert new records next to the cursor position, 01341 and it might be that these new records should appear in the 01342 search result set, resulting in the phantom problem. */ 01343 01344 if (!consistent_read) { 01345 01346 /* If innodb_locks_unsafe_for_binlog option is used 01347 or this session is using READ COMMITTED isolation 01348 level, we lock only the record, i.e., next-key 01349 locking is not used. */ 01350 01351 rec_t* next_rec = page_rec_get_next(rec); 01352 ulint lock_type; 01353 trx_t* trx; 01354 01355 trx = thr_get_trx(thr); 01356 01357 offsets = rec_get_offsets(next_rec, index, offsets, 01358 ULINT_UNDEFINED, &heap); 01359 01360 if (srv_locks_unsafe_for_binlog 01361 || trx->isolation_level == TRX_ISO_READ_COMMITTED) { 01362 01363 if (page_rec_is_supremum(next_rec)) { 01364 01365 goto skip_lock; 01366 } 01367 01368 lock_type = LOCK_REC_NOT_GAP; 01369 } else { 01370 lock_type = LOCK_ORDINARY; 01371 } 01372 01373 err = sel_set_rec_lock(next_rec, index, offsets, 01374 node->row_lock_mode, lock_type, thr); 01375 01376 if (err != DB_SUCCESS) { 01377 /* Note that in this case we will store in pcur 01378 the PREDECESSOR of the record we are waiting 01379 the lock for */ 01380 01381 goto lock_wait_or_error; 01382 } 01383 } 01384 } 01385 01386 skip_lock: 01387 if (page_rec_is_infimum(rec)) { 01388 01389 /* The infimum record on a page cannot be in the result set, 01390 and neither can a record lock be placed on it: we skip such 01391 a record. We also increment the cost counter as we may have 01392 processed yet another page of index. */ 01393 01394 cost_counter++; 01395 01396 goto next_rec; 01397 } 01398 01399 if (!consistent_read) { 01400 /* Try to place a lock on the index record */ 01401 01402 /* If innodb_locks_unsafe_for_binlog option is used 01403 or this session is using READ COMMITTED isolation level, 01404 we lock only the record, i.e., next-key locking is 01405 not used. */ 01406 01407 ulint lock_type; 01408 trx_t* trx; 01409 01410 offsets = rec_get_offsets(rec, index, offsets, 01411 ULINT_UNDEFINED, &heap); 01412 01413 trx = thr_get_trx(thr); 01414 01415 if (srv_locks_unsafe_for_binlog 01416 || trx->isolation_level == TRX_ISO_READ_COMMITTED) { 01417 01418 if (page_rec_is_supremum(rec)) { 01419 01420 goto next_rec; 01421 } 01422 01423 lock_type = LOCK_REC_NOT_GAP; 01424 } else { 01425 lock_type = LOCK_ORDINARY; 01426 } 01427 01428 err = sel_set_rec_lock(rec, index, offsets, 01429 node->row_lock_mode, lock_type, thr); 01430 01431 if (err != DB_SUCCESS) { 01432 01433 goto lock_wait_or_error; 01434 } 01435 } 01436 01437 if (page_rec_is_supremum(rec)) { 01438 01439 /* A page supremum record cannot be in the result set: skip 01440 it now when we have placed a possible lock on it */ 01441 01442 goto next_rec; 01443 } 01444 01445 ut_ad(page_rec_is_user_rec(rec)); 01446 01447 if (cost_counter > SEL_COST_LIMIT) { 01448 01449 /* Now that we have placed the necessary locks, we can stop 01450 for a while and store the cursor position; NOTE that if we 01451 would store the cursor position BEFORE placing a record lock, 01452 it might happen that the cursor would jump over some records 01453 that another transaction could meanwhile insert adjacent to 01454 the cursor: this would result in the phantom problem. */ 01455 01456 goto stop_for_a_while; 01457 } 01458 01459 /* PHASE 2: Check a mixed index mix id if needed */ 01460 01461 if (plan->unique_search && cursor_just_opened) { 01462 01463 ut_ad(plan->mode == PAGE_CUR_GE); 01464 01465 /* As the cursor is now placed on a user record after a search 01466 with the mode PAGE_CUR_GE, the up_match field in the cursor 01467 tells how many fields in the user record matched to the search 01468 tuple */ 01469 01470 if (btr_pcur_get_up_match(&(plan->pcur)) 01471 < plan->n_exact_match) { 01472 goto table_exhausted; 01473 } 01474 01475 /* Ok, no need to test end_conds or mix id */ 01476 01477 } 01478 01479 /* We are ready to look at a possible new index entry in the result 01480 set: the cursor is now placed on a user record */ 01481 01482 /* PHASE 3: Get previous version in a consistent read */ 01483 01484 cons_read_requires_clust_rec = FALSE; 01485 offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); 01486 01487 if (consistent_read) { 01488 /* This is a non-locking consistent read: if necessary, fetch 01489 a previous version of the record */ 01490 01491 if (index->type & DICT_CLUSTERED) { 01492 01493 if (!lock_clust_rec_cons_read_sees(rec, index, offsets, 01494 node->read_view)) { 01495 01496 err = row_sel_build_prev_vers(node->read_view, 01497 plan, rec, 01498 &offsets, &heap, 01499 &old_vers, &mtr); 01500 if (err != DB_SUCCESS) { 01501 01502 goto lock_wait_or_error; 01503 } 01504 01505 if (old_vers == NULL) { 01506 offsets = rec_get_offsets( 01507 rec, index, offsets, 01508 ULINT_UNDEFINED, &heap); 01509 row_sel_fetch_columns(index, rec, 01510 offsets, 01511 UT_LIST_GET_FIRST(plan->columns)); 01512 01513 if (!row_sel_test_end_conds(plan)) { 01514 01515 goto table_exhausted; 01516 } 01517 01518 goto next_rec; 01519 } 01520 01521 rec = old_vers; 01522 } 01523 } else if (!lock_sec_rec_cons_read_sees(rec, index, 01524 node->read_view)) { 01525 cons_read_requires_clust_rec = TRUE; 01526 } 01527 } 01528 01529 /* PHASE 4: Test search end conditions and deleted flag */ 01530 01531 /* Fetch the columns needed in test conditions */ 01532 01533 row_sel_fetch_columns(index, rec, offsets, 01534 UT_LIST_GET_FIRST(plan->columns)); 01535 01536 /* Test the selection end conditions: these can only contain columns 01537 which already are found in the index, even though the index might be 01538 non-clustered */ 01539 01540 if (plan->unique_search && cursor_just_opened) { 01541 01542 /* No test necessary: the test was already made above */ 01543 01544 } else if (!row_sel_test_end_conds(plan)) { 01545 01546 goto table_exhausted; 01547 } 01548 01549 if (rec_get_deleted_flag(rec, dict_table_is_comp(plan->table)) 01550 && !cons_read_requires_clust_rec) { 01551 01552 /* The record is delete marked: we can skip it if this is 01553 not a consistent read which might see an earlier version 01554 of a non-clustered index record */ 01555 01556 if (plan->unique_search) { 01557 01558 goto table_exhausted; 01559 } 01560 01561 goto next_rec; 01562 } 01563 01564 /* PHASE 5: Get the clustered index record, if needed and if we did 01565 not do the search using the clustered index */ 01566 01567 if (plan->must_get_clust || cons_read_requires_clust_rec) { 01568 01569 /* It was a non-clustered index and we must fetch also the 01570 clustered index record */ 01571 01572 err = row_sel_get_clust_rec(node, plan, rec, thr, &clust_rec, 01573 &mtr); 01574 mtr_has_extra_clust_latch = TRUE; 01575 01576 if (err != DB_SUCCESS) { 01577 01578 goto lock_wait_or_error; 01579 } 01580 01581 /* Retrieving the clustered record required a search: 01582 increment the cost counter */ 01583 01584 cost_counter++; 01585 01586 if (clust_rec == NULL) { 01587 /* The record did not exist in the read view */ 01588 ut_ad(consistent_read); 01589 01590 goto next_rec; 01591 } 01592 01593 if (rec_get_deleted_flag(clust_rec, 01594 dict_table_is_comp(plan->table))) { 01595 01596 /* The record is delete marked: we can skip it */ 01597 01598 goto next_rec; 01599 } 01600 01601 if (node->can_get_updated) { 01602 01603 btr_pcur_store_position(&(plan->clust_pcur), &mtr); 01604 } 01605 } 01606 01607 /* PHASE 6: Test the rest of search conditions */ 01608 01609 if (!row_sel_test_other_conds(plan)) { 01610 01611 if (plan->unique_search) { 01612 01613 goto table_exhausted; 01614 } 01615 01616 goto next_rec; 01617 } 01618 01619 /* PHASE 7: We found a new qualifying row for the current table; push 01620 the row if prefetch is on, or move to the next table in the join */ 01621 01622 plan->n_rows_fetched++; 01623 01624 ut_ad(plan->pcur.latch_mode == node->latch_mode); 01625 01626 if (node->select_will_do_update) { 01627 /* This is a searched update and we can do the update in-place, 01628 saving CPU time */ 01629 01630 row_upd_in_place_in_select(node, thr, &mtr); 01631 01632 leaf_contains_updates = TRUE; 01633 01634 /* When the database is in the online backup mode, the number 01635 of log records for a single mtr should be small: increment the 01636 cost counter to ensure it */ 01637 01638 cost_counter += 1 + (SEL_COST_LIMIT / 8); 01639 01640 if (plan->unique_search) { 01641 01642 goto table_exhausted; 01643 } 01644 01645 goto next_rec; 01646 } 01647 01648 if ((plan->n_rows_fetched <= SEL_PREFETCH_LIMIT) 01649 || plan->unique_search || plan->no_prefetch 01650 || (plan->table->max_row_size >= BIG_ROW_SIZE)) { 01651 01652 /* No prefetch in operation: go to the next table */ 01653 01654 goto next_table; 01655 } 01656 01657 sel_push_prefetched_row(plan); 01658 01659 if (plan->n_rows_prefetched == SEL_MAX_N_PREFETCH) { 01660 01661 /* The prefetch buffer is now full */ 01662 01663 sel_pop_prefetched_row(plan); 01664 01665 goto next_table; 01666 } 01667 01668 next_rec: 01669 ut_ad(!search_latch_locked); 01670 01671 if (mtr_has_extra_clust_latch) { 01672 01673 /* We must commit &mtr if we are moving to the next 01674 non-clustered index record, because we could break the 01675 latching order if we would access a different clustered 01676 index page right away without releasing the previous. */ 01677 01678 goto commit_mtr_for_a_while; 01679 } 01680 01681 if (leaf_contains_updates 01682 && btr_pcur_is_after_last_on_page(&(plan->pcur), &mtr)) { 01683 01684 /* We must commit &mtr if we are moving to a different page, 01685 because we have done updates to the x-latched leaf page, and 01686 the latch would be released in btr_pcur_move_to_next, without 01687 &mtr getting committed there */ 01688 01689 ut_ad(node->asc); 01690 01691 goto commit_mtr_for_a_while; 01692 } 01693 01694 if (node->asc) { 01695 moved = btr_pcur_move_to_next(&(plan->pcur), &mtr); 01696 } else { 01697 moved = btr_pcur_move_to_prev(&(plan->pcur), &mtr); 01698 } 01699 01700 if (!moved) { 01701 01702 goto table_exhausted; 01703 } 01704 01705 cursor_just_opened = FALSE; 01706 01707 /* END OF RECORD LOOP 01708 ------------------ */ 01709 goto rec_loop; 01710 01711 next_table: 01712 /* We found a record which satisfies the conditions: we can move to 01713 the next table or return a row in the result set */ 01714 01715 ut_ad(btr_pcur_is_on_user_rec(&(plan->pcur), &mtr)); 01716 01717 if (plan->unique_search && !node->can_get_updated) { 01718 01719 plan->cursor_at_end = TRUE; 01720 } else { 01721 ut_ad(!search_latch_locked); 01722 01723 plan->stored_cursor_rec_processed = TRUE; 01724 01725 btr_pcur_store_position(&(plan->pcur), &mtr); 01726 } 01727 01728 mtr_commit(&mtr); 01729 01730 leaf_contains_updates = FALSE; 01731 mtr_has_extra_clust_latch = FALSE; 01732 01733 next_table_no_mtr: 01734 /* If we use 'goto' to this label, it means that the row was popped 01735 from the prefetched rows stack, and &mtr is already committed */ 01736 01737 if (node->fetch_table + 1 == node->n_tables) { 01738 01739 sel_eval_select_list(node); 01740 01741 if (node->is_aggregate) { 01742 01743 goto table_loop; 01744 } 01745 01746 sel_assign_into_var_values(node->into_list, node); 01747 01748 thr->run_node = que_node_get_parent(node); 01749 01750 if (search_latch_locked) { 01751 rw_lock_s_unlock(&btr_search_latch); 01752 } 01753 01754 err = DB_SUCCESS; 01755 goto func_exit; 01756 } 01757 01758 node->fetch_table++; 01759 01760 /* When we move to the next table, we first reset the plan cursor: 01761 we do not care about resetting it when we backtrack from a table */ 01762 01763 plan_reset_cursor(sel_node_get_nth_plan(node, node->fetch_table)); 01764 01765 goto table_loop; 01766 01767 table_exhausted: 01768 /* The table cursor pcur reached the result set end: backtrack to the 01769 previous table in the join if we do not have cached prefetched rows */ 01770 01771 plan->cursor_at_end = TRUE; 01772 01773 mtr_commit(&mtr); 01774 01775 leaf_contains_updates = FALSE; 01776 mtr_has_extra_clust_latch = FALSE; 01777 01778 if (plan->n_rows_prefetched > 0) { 01779 /* The table became exhausted during a prefetch */ 01780 01781 sel_pop_prefetched_row(plan); 01782 01783 goto next_table_no_mtr; 01784 } 01785 01786 table_exhausted_no_mtr: 01787 if (node->fetch_table == 0) { 01788 err = DB_SUCCESS; 01789 01790 if (node->is_aggregate && !node->aggregate_already_fetched) { 01791 01792 node->aggregate_already_fetched = TRUE; 01793 01794 sel_assign_into_var_values(node->into_list, node); 01795 01796 thr->run_node = que_node_get_parent(node); 01797 01798 if (search_latch_locked) { 01799 rw_lock_s_unlock(&btr_search_latch); 01800 } 01801 01802 goto func_exit; 01803 } 01804 01805 node->state = SEL_NODE_NO_MORE_ROWS; 01806 01807 thr->run_node = que_node_get_parent(node); 01808 01809 if (search_latch_locked) { 01810 rw_lock_s_unlock(&btr_search_latch); 01811 } 01812 01813 goto func_exit; 01814 } 01815 01816 node->fetch_table--; 01817 01818 goto table_loop; 01819 01820 stop_for_a_while: 01821 /* Return control for a while to que_run_threads, so that runaway 01822 queries can be canceled. NOTE that when we come here, we must, in a 01823 locking read, have placed the necessary (possibly waiting request) 01824 record lock on the cursor record or its successor: when we reposition 01825 the cursor, this record lock guarantees that nobody can meanwhile have 01826 inserted new records which should have appeared in the result set, 01827 which would result in the phantom problem. */ 01828 01829 ut_ad(!search_latch_locked); 01830 01831 plan->stored_cursor_rec_processed = FALSE; 01832 btr_pcur_store_position(&(plan->pcur), &mtr); 01833 01834 mtr_commit(&mtr); 01835 01836 ut_ad(sync_thread_levels_empty_gen(TRUE)); 01837 err = DB_SUCCESS; 01838 goto func_exit; 01839 01840 commit_mtr_for_a_while: 01841 /* Stores the cursor position and commits &mtr; this is used if 01842 &mtr may contain latches which would break the latching order if 01843 &mtr would not be committed and the latches released. */ 01844 01845 plan->stored_cursor_rec_processed = TRUE; 01846 01847 ut_ad(!search_latch_locked); 01848 btr_pcur_store_position(&(plan->pcur), &mtr); 01849 01850 mtr_commit(&mtr); 01851 01852 leaf_contains_updates = FALSE; 01853 mtr_has_extra_clust_latch = FALSE; 01854 01855 ut_ad(sync_thread_levels_empty_gen(TRUE)); 01856 01857 goto table_loop; 01858 01859 lock_wait_or_error: 01860 /* See the note at stop_for_a_while: the same holds for this case */ 01861 01862 ut_ad(!btr_pcur_is_before_first_on_page(&(plan->pcur), &mtr) 01863 || !node->asc); 01864 ut_ad(!search_latch_locked); 01865 01866 plan->stored_cursor_rec_processed = FALSE; 01867 btr_pcur_store_position(&(plan->pcur), &mtr); 01868 01869 mtr_commit(&mtr); 01870 01871 ut_ad(sync_thread_levels_empty_gen(TRUE)); 01872 01873 func_exit: 01874 if (UNIV_LIKELY_NULL(heap)) { 01875 mem_heap_free(heap); 01876 } 01877 return(err); 01878 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static ulint row_sel_build_committed_vers_for_mysql | ( | dict_index_t * | clust_index, | |
| row_prebuilt_t * | prebuilt, | |||
| rec_t * | rec, | |||
| ulint ** | offsets, | |||
| mem_heap_t ** | offset_heap, | |||
| rec_t ** | old_vers, | |||
| mtr_t * | mtr | |||
| ) | [static] |
Definition at line 572 of file row0sel.c.
References err, mem_heap_create, mem_heap_empty(), row_prebuilt_struct::old_vers_heap, and row_vers_build_for_semi_consistent_read().
Referenced by row_search_for_mysql().
00574 : DB_SUCCESS or error code */ 00575 dict_index_t* clust_index, /* in: clustered index */ 00576 row_prebuilt_t* prebuilt, /* in: prebuilt struct */ 00577 rec_t* rec, /* in: record in a clustered index */ 00578 ulint** offsets, /* in/out: offsets returned by 00579 rec_get_offsets(rec, clust_index) */ 00580 mem_heap_t** offset_heap, /* in/out: memory heap from which 00581 the offsets are allocated */ 00582 rec_t** old_vers, /* out: old version, or NULL if the 00583 record does not exist in the view: 00584 i.e., it was freshly inserted 00585 afterwards */ 00586 mtr_t* mtr) /* in: mtr */ 00587 { 00588 ulint err; 00589 00590 if (prebuilt->old_vers_heap) { 00591 mem_heap_empty(prebuilt->old_vers_heap); 00592 } else { 00593 prebuilt->old_vers_heap = mem_heap_create(200); 00594 } 00595 00596 err = row_vers_build_for_semi_consistent_read(rec, mtr, clust_index, 00597 offsets, offset_heap, 00598 prebuilt->old_vers_heap, old_vers); 00599 return(err); 00600 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static ulint row_sel_build_prev_vers | ( | read_view_t * | read_view, | |
| plan_t * | plan, | |||
| rec_t * | rec, | |||
| ulint ** | offsets, | |||
| mem_heap_t ** | offset_heap, | |||
| rec_t ** | old_vers, | |||
| mtr_t * | mtr | |||
| ) | [static] |
Definition at line 537 of file row0sel.c.
References err, mem_heap_create, mem_heap_empty(), plan(), and row_vers_build_for_consistent_read().
Referenced by row_sel(), and row_sel_get_clust_rec().
00539 : DB_SUCCESS or error code */ 00540 read_view_t* read_view, /* in: read view */ 00541 plan_t* plan, /* in: plan node for table */ 00542 rec_t* rec, /* in: record in a clustered index */ 00543 ulint** offsets, /* in/out: offsets returned by 00544 rec_get_offsets(rec, plan->index) */ 00545 mem_heap_t** offset_heap, /* in/out: memory heap from which 00546 the offsets are allocated */ 00547 rec_t** old_vers, /* out: old version, or NULL if the 00548 record does not exist in the view: 00549 i.e., it was freshly inserted 00550 afterwards */ 00551 mtr_t* mtr) /* in: mtr */ 00552 { 00553 ulint err; 00554 00555 if (plan->old_vers_heap) { 00556 mem_heap_empty(plan->old_vers_heap); 00557 } else { 00558 plan->old_vers_heap = mem_heap_create(512); 00559 } 00560 00561 err = row_vers_build_for_consistent_read(rec, mtr, plan->index, 00562 offsets, read_view, offset_heap, 00563 plan->old_vers_heap, old_vers); 00564 return(err); 00565 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static ulint row_sel_build_prev_vers_for_mysql | ( | read_view_t * | read_view, | |
| dict_index_t * | clust_index, | |||
| row_prebuilt_t * | prebuilt, | |||
| rec_t * | rec, | |||
| ulint ** | offsets, | |||
| mem_heap_t ** | offset_heap, | |||
| rec_t ** | old_vers, | |||
| mtr_t * | mtr | |||
| ) | [static] |
Definition at line 2720 of file row0sel.c.
References err, mem_heap_create, mem_heap_empty(), row_prebuilt_struct::old_vers_heap, and row_vers_build_for_consistent_read().
Referenced by row_search_for_mysql(), and row_sel_get_clust_rec_for_mysql().
02722 : DB_SUCCESS or error code */ 02723 read_view_t* read_view, /* in: read view */ 02724 dict_index_t* clust_index, /* in: clustered index */ 02725 row_prebuilt_t* prebuilt, /* in: prebuilt struct */ 02726 rec_t* rec, /* in: record in a clustered index */ 02727 ulint** offsets, /* in/out: offsets returned by 02728 rec_get_offsets(rec, clust_index) */ 02729 mem_heap_t** offset_heap, /* in/out: memory heap from which 02730 the offsets are allocated */ 02731 rec_t** old_vers, /* out: old version, or NULL if the 02732 record does not exist in the view: 02733 i.e., it was freshly inserted 02734 afterwards */ 02735 mtr_t* mtr) /* in: mtr */ 02736 { 02737 ulint err; 02738 02739 if (prebuilt->old_vers_heap) { 02740 mem_heap_empty(prebuilt->old_vers_heap); 02741 } else { 02742 prebuilt->old_vers_heap = mem_heap_create(200); 02743 } 02744 02745 err = row_vers_build_for_consistent_read(rec, mtr, clust_index, 02746 offsets, read_view, offset_heap, 02747 prebuilt->old_vers_heap, old_vers); 02748 return(err); 02749 }
Here is the call graph for this function:

Here is the caller graph for this function:

| void row_sel_convert_mysql_key_to_innobase | ( | dtuple_t * | tuple, | |
| byte * | buf, | |||
| ulint | buf_len, | |||
| dict_index_t * | index, | |||
| byte * | key_ptr, | |||
| ulint | key_len, | |||
| trx_t * | trx | |||
| ) |
Definition at line 2192 of file row0sel.c.
References dict_field_struct::col, DATA_BLOB, DATA_INT, DATA_MYSQL_TRUE_VARCHAR, DATA_NOT_NULL, DATA_ROW_ID_LEN, DATA_SYS, dfield_get_type(), dfield_set_data(), dict_col_get_type(), dict_index_get_nth_field(), dict_index_name_print(), dict_table_is_comp(), dtuple_get_nth_field(), dtuple_set_n_fields(), dtype_get_mysql_type(), FALSE, index(), key_end(), dfield_struct::len, dtype_struct::len, dtype_struct::mtype, NULL, dict_field_struct::prefix_len, row_mysql_store_col_in_innobase_format(), TRUE, ut_a, ut_print_buf(), and ut_print_timestamp().
02194 : tuple where to build; 02195 NOTE: we assume that the type info 02196 in the tuple is already according 02197 to index! */ 02198 byte* buf, /* in: buffer to use in field 02199 conversions */ 02200 ulint buf_len, /* in: buffer length */ 02201 dict_index_t* index, /* in: index of the key value */ 02202 byte* key_ptr, /* in: MySQL key value */ 02203 ulint key_len, /* in: MySQL key value length */ 02204 trx_t* trx) /* in: transaction */ 02205 { 02206 byte* original_buf = buf; 02207 byte* original_key_ptr = key_ptr; 02208 dict_field_t* field; 02209 dfield_t* dfield; 02210 ulint data_offset; 02211 ulint data_len; 02212 ulint data_field_len; 02213 ibool is_null; 02214 byte* key_end; 02215 ulint n_fields = 0; 02216 ulint type; 02217 02218 /* For documentation of the key value storage format in MySQL, see 02219 ha_innobase::store_key_val_for_row() in ha_innodb.cc. */ 02220 02221 key_end = key_ptr + key_len; 02222 02223 /* Permit us to access any field in the tuple (ULINT_MAX): */ 02224 02225 dtuple_set_n_fields(tuple, ULINT_MAX); 02226 02227 dfield = dtuple_get_nth_field(tuple, 0); 02228 field = dict_index_get_nth_field(index, 0); 02229 02230 if (dfield_get_type(dfield)->mtype == DATA_SYS) { 02231 /* A special case: we are looking for a position in the 02232 generated clustered index which InnoDB automatically added 02233 to a table with no primary key: the first and the only 02234 ordering column is ROW_ID which InnoDB stored to the key_ptr 02235 buffer. */ 02236 02237 ut_a(key_len == DATA_ROW_ID_LEN); 02238 02239 dfield_set_data(dfield, key_ptr, DATA_ROW_ID_LEN); 02240 02241 dtuple_set_n_fields(tuple, 1); 02242 02243 return; 02244 } 02245 02246 while (key_ptr < key_end) { 02247 02248 ut_a(dict_col_get_type(field->col)->mtype 02249 == dfield_get_type(dfield)->mtype); 02250 02251 data_offset = 0; 02252 is_null = FALSE; 02253 02254 if (!(dfield_get_type(dfield)->prtype & DATA_NOT_NULL)) { 02255 /* The first byte in the field tells if this is 02256 an SQL NULL value */ 02257 02258 data_offset = 1; 02259 02260 if (*key_ptr != 0) { 02261 dfield_set_data(dfield, NULL, UNIV_SQL_NULL); 02262 02263 is_null = TRUE; 02264 } 02265 } 02266 02267 type = dfield_get_type(dfield)->mtype; 02268 02269 /* Calculate data length and data field total length */ 02270 02271 if (type == DATA_BLOB) { 02272 /* The key field is a column prefix of a BLOB or 02273 TEXT */ 02274 02275 ut_a(field->prefix_len > 0); 02276 02277 /* MySQL stores the actual data length to the first 2 02278 bytes after the optional SQL NULL marker byte. The 02279 storage format is little-endian, that is, the most 02280 significant byte at a higher address. In UTF-8, MySQL 02281 seems to reserve field->prefix_len bytes for 02282 storing this field in the key value buffer, even 02283 though the actual value only takes data_len bytes 02284 from the start. */ 02285 02286 data_len = key_ptr[data_offset] 02287 + 256 * key_ptr[data_offset + 1]; 02288 data_field_len = data_offset + 2 + field->prefix_len; 02289 02290 data_offset += 2; 02291 02292 /* Now that we know the length, we store the column 02293 value like it would be a fixed char field */ 02294 02295 } else if (field->prefix_len > 0) { 02296 /* Looks like MySQL pads unused end bytes in the 02297 prefix with space. Therefore, also in UTF-8, it is ok 02298 to compare with a prefix containing full prefix_len 02299 bytes, and no need to take at most prefix_len / 3 02300 UTF-8 characters from the start. 02301 If the prefix is used as the upper end of a LIKE 02302 'abc%' query, then MySQL pads the end with chars 02303 0xff. TODO: in that case does it any harm to compare 02304 with the full prefix_len bytes. How do characters 02305 0xff in UTF-8 behave? */ 02306 02307 data_len = field->prefix_len; 02308 data_field_len = data_offset + data_len; 02309 } else { 02310 data_len = dfield_get_type(dfield)->len; 02311 data_field_len = data_offset + data_len; 02312 } 02313 02314 if (dtype_get_mysql_type(dfield_get_type(dfield)) 02315 == DATA_MYSQL_TRUE_VARCHAR 02316 && dfield_get_type(dfield)->mtype != DATA_INT) { 02317 /* In a MySQL key value format, a true VARCHAR is 02318 always preceded by 2 bytes of a length field. 02319 dfield_get_type(dfield)->len returns the maximum 02320 'payload' len in bytes. That does not include the 02321 2 bytes that tell the actual data length. 02322 02323 We added the check != DATA_INT to make sure we do 02324 not treat MySQL ENUM or SET as a true VARCHAR! */ 02325 02326 data_len += 2; 02327 data_field_len += 2; 02328 } 02329 02330 /* Storing may use at most data_len bytes of buf */ 02331 02332 if (!is_null) { 02333 row_mysql_store_col_in_innobase_format( 02334 dfield, 02335 buf, 02336 FALSE, /* MySQL key value format col */ 02337 key_ptr + data_offset, 02338 data_len, 02339 dict_table_is_comp(index->table)); 02340 buf += data_len; 02341 } 02342 02343 key_ptr += data_field_len; 02344 02345 if (key_ptr > key_end) { 02346 /* The last field in key was not a complete key field 02347 but a prefix of it. 02348 02349 Print a warning about this! HA_READ_PREFIX_LAST does 02350 not currently work in InnoDB with partial-field key 02351 value prefixes. Since MySQL currently uses a padding 02352 trick to calculate LIKE 'abc%' type queries there 02353 should never be partial-field prefixes in searches. */ 02354 02355 ut_print_timestamp(stderr); 02356 02357 fputs( 02358 " InnoDB: Warning: using a partial-field key prefix in search.\n" 02359 "InnoDB: ", stderr); 02360 dict_index_name_print(stderr, trx, index); 02361 fprintf(stderr, ". Last data field length %lu bytes,\n" 02362 "InnoDB: key ptr now exceeds key end by %lu bytes.\n" 02363 "InnoDB: Key value in the MySQL format:\n", 02364 (ulong) data_field_len, 02365 (ulong) (key_ptr - key_end)); 02366 fflush(stderr); 02367 ut_print_buf(stderr, original_key_ptr, key_len); 02368 fprintf(stderr, "\n"); 02369 02370 if (!is_null) { 02371 dfield->len -= (ulint)(key_ptr - key_end); 02372 } 02373 } 02374 02375 n_fields++; 02376 field++; 02377 dfield++; 02378 } 02379 02380 ut_a(buf <= original_buf + buf_len); 02381 02382 /* We set the length of tuple to n_fields: we assume that the memory 02383 area allocated for it is big enough (usually bigger than n_fields). */ 02384 02385 dtuple_set_n_fields(tuple, n_fields); 02386 }
Here is the call graph for this function:

| UNIV_INLINE void row_sel_copy_input_variable_vals | ( | sel_node_t * | node | ) |
Definition at line 263 of file row0sel.c.
References sym_node_struct::alias, sel_node_struct::copy_variables, eval_node_copy_val(), NULL, UT_LIST_GET_FIRST, and UT_LIST_GET_NEXT.
Referenced by row_sel_step().
00265 : select node */ 00266 { 00267 sym_node_t* var; 00268 00269 var = UT_LIST_GET_FIRST(node->copy_variables); 00270 00271 while (var) { 00272 eval_node_copy_val(var, var->alias); 00273 00274 var->indirection = NULL; 00275 00276 var = UT_LIST_GET_NEXT(col_var_list, var); 00277 } 00278 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static void row_sel_fetch_columns | ( | dict_index_t * | index, | |
| rec_t * | rec, | |||
| const ulint * | offsets, | |||
| sym_node_t * | column | |||
| ) | [static] |
Definition at line 284 of file row0sel.c.
References btr_rec_copy_externally_stored_field(), sym_node_struct::copy_val, data, dfield_set_data(), DICT_CLUSTERED, eval_node_copy_and_alloc_val(), sym_node_struct::field_nos, index(), mem_heap_create, mem_heap_free, NULL, que_node_get_val(), rec_get_nth_field(), rec_offs_nth_extern(), rec_offs_validate(), SYM_CLUST_FIELD_NO, SYM_SEC_FIELD_NO, TRUE, ut_a, ut_ad, and UT_LIST_GET_NEXT.
Referenced by row_sel(), row_sel_get_clust_rec(), and row_sel_try_search_shortcut().
00286 : record index */ 00287 rec_t* rec, /* in: record in a clustered or non-clustered 00288 index */ 00289 const ulint* offsets,/* in: rec_get_offsets(rec, index) */ 00290 sym_node_t* column) /* in: first column in a column list, or 00291 NULL */ 00292 { 00293 dfield_t* val; 00294 ulint index_type; 00295 ulint field_no; 00296 byte* data; 00297 ulint len; 00298 00299 ut_ad(rec_offs_validate(rec, index, offsets)); 00300 00301 if (index->type & DICT_CLUSTERED) { 00302 index_type = SYM_CLUST_FIELD_NO; 00303 } else { 00304 index_type = SYM_SEC_FIELD_NO; 00305 } 00306 00307 while (column) { 00308 mem_heap_t* heap = NULL; 00309 ibool needs_copy; 00310 00311 field_no = column->field_nos[index_type]; 00312 00313 if (field_no != ULINT_UNDEFINED) { 00314 00315 if (UNIV_UNLIKELY(rec_offs_nth_extern(offsets, 00316 field_no))) { 00317 00318 /* Copy an externally stored field to the 00319 temporary heap */ 00320 00321 heap = mem_heap_create(1); 00322 00323 data = btr_rec_copy_externally_stored_field( 00324 rec, offsets, field_no, &len, heap); 00325 00326 ut_a(len != UNIV_SQL_NULL); 00327 00328 needs_copy = TRUE; 00329 } else { 00330 data = rec_get_nth_field(rec, offsets, 00331 field_no, &len); 00332 00333 needs_copy = column->copy_val; 00334 } 00335 00336 if (needs_copy) { 00337 eval_node_copy_and_alloc_val(column, data, 00338 len); 00339 } else { 00340 val = que_node_get_val(column); 00341 dfield_set_data(val, data, len); 00342 } 00343 00344 if (UNIV_LIKELY_NULL(heap)) { 00345 mem_heap_free(heap); 00346 } 00347 } 00348 00349 column = UT_LIST_GET_NEXT(col_var_list, column); 00350 } 00351 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static void row_sel_field_store_in_mysql_format | ( | byte * | dest, | |
| const mysql_row_templ_t * | templ, | |||
| byte * | data, | |||
| ulint | len | |||
| ) | [static] |
Definition at line 2429 of file row0sel.c.
References DATA_INT, mysql_row_templ_struct::is_unsigned, mysql_row_templ_struct::mysql_col_len, mysql_row_templ_struct::type, and ut_ad.
02431 : buffer where to store; NOTE that BLOBs 02432 are not in themselves stored here: the caller must 02433 allocate and copy the BLOB into buffer before, and pass 02434 the pointer to the BLOB in 'data' */ 02435 const mysql_row_templ_t* templ, /* in: MySQL column template. 02436 Its following fields are referenced: 02437 type, is_unsigned, mysql_col_len, mbminlen, mbmaxlen */ 02438 byte* data, /* in: data to store */ 02439 ulint len) /* in: length of the data */ 02440 { 02441 byte* ptr; 02442 byte* field_end; 02443 byte* pad_ptr; 02444 02445 ut_ad(len != UNIV_SQL_NULL); 02446 02447 if (templ->type == DATA_INT) { 02448 /* Convert integer data from Innobase to a little-endian 02449 format, sign bit restored to normal */ 02450 02451 ptr = dest + len; 02452 02453 for (;;) { 02454 ptr--; 02455 *ptr = *data; 02456 if (ptr == dest) { 02457 break; 02458 } 02459 data++; 02460 } 02461 02462 if (!templ->is_unsigned) { 02463 dest[len - 1] = (byte) (dest[len - 1] ^ 128); 02464 } 02465 02466 ut_ad(templ->mysql_col_len == len); 02467 } else if (templ->type == DATA_VARCHAR 02468 || templ->type == DATA_VARMYSQL 02469 || templ->type == DATA_BINARY) { 02470 02471 field_end = dest + templ->mysql_col_len; 02472 02473 if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) { 02474 /* This is a >= 5.0.3 type true VARCHAR. Store the 02475 length of the data to the first byte or the first 02476 two bytes of dest. */ 02477 02478 dest = row_mysql_store_true_var_len(dest, len, 02479 templ->mysql_length_bytes); 02480 } 02481 02482 /* Copy the actual data */ 02483 ut_memcpy(dest, data, len); 02484 02485 /* Pad with trailing spaces. We pad with spaces also the 02486 unused end of a >= 5.0.3 true VARCHAR column, just in case 02487 MySQL expects its contents to be deterministic. */ 02488 02489 pad_ptr = dest + len; 02490 02491 ut_ad(templ->mbminlen <= templ->mbmaxlen); 02492 02493 /* We handle UCS2 charset strings differently. */ 02494 if (templ->mbminlen == 2) { 02495 /* A space char is two bytes, 0x0020 in UCS2 */ 02496 02497 if (len & 1) { 02498 /* A 0x20 has been stripped from the column. 02499 Pad it back. */ 02500 02501 if (pad_ptr < field_end) { 02502 *pad_ptr = 0x20; 02503 pad_ptr++; 02504 } 02505 } 02506 02507 /* Pad the rest of the string with 0x0020 */ 02508 02509 while (pad_ptr < field_end) { 02510 *pad_ptr = 0x00; 02511 pad_ptr++; 02512 *pad_ptr = 0x20; 02513 pad_ptr++; 02514 } 02515 } else { 02516 ut_ad(templ->mbminlen == 1); 02517 /* space=0x20 */ 02518 02519 memset(pad_ptr, 0x20, field_end - pad_ptr); 02520 } 02521 } else if (templ->type == DATA_BLOB) { 02522 /* Store a pointer to the BLOB buffer to dest: the BLOB was 02523 already copied to the buffer in row_sel_store_mysql_rec */ 02524 02525 row_mysql_store_blob_ref(dest, templ->mysql_col_len, data, 02526 len); 02527 } else if (templ->type == DATA_MYSQL) { 02528 memcpy(dest, data, len); 02529 02530 ut_ad(templ->mysql_col_len >= len); 02531 ut_ad(templ->mbmaxlen >= templ->mbminlen); 02532 02533 ut_ad(templ->mbmaxlen > templ->mbminlen 02534 || templ->mysql_col_len == len); 02535 /* The following assertion would fail for old tables 02536 containing UTF-8 ENUM columns due to Bug #9526. */ 02537 ut_ad(!templ->mbmaxlen 02538 || !(templ->mysql_col_len % templ->mbmaxlen)); 02539 ut_ad(len * templ->mbmaxlen >= templ->mysql_col_len); 02540 02541 if (templ->mbminlen != templ->mbmaxlen) { 02542 /* Pad with spaces. This undoes the stripping 02543 done in row0mysql.ic, function 02544 row_mysql_store_col_in_innobase_format(). */ 02545 02546 memset(dest + len, 0x20, templ->mysql_col_len - len); 02547 } 02548 } else { 02549 ut_ad(templ->type == DATA_CHAR 02550 || templ->type == DATA_FIXBINARY 02551 /*|| templ->type == DATA_SYS_CHILD 02552 || templ->type == DATA_SYS*/ 02553 || templ->type == DATA_FLOAT 02554 || templ->type == DATA_DOUBLE 02555 || templ->type == DATA_DECIMAL); 02556 ut_ad(templ->mysql_col_len == len); 02557 02558 memcpy(dest, data, len); 02559 } 02560 }
| static ulint row_sel_get_clust_rec | ( | sel_node_t * | node, | |
| plan_t * | plan, | |||
| rec_t * | rec, | |||
| que_thr_t * | thr, | |||
| rec_t ** | out_rec, | |||
| mtr_t * | mtr | |||
| ) | [static] |
Definition at line 673 of file row0sel.c.
References btr_pcur_get_btr_cur(), btr_pcur_get_low_match(), btr_pcur_get_rec(), btr_pcur_open_with_no_init(), DB_SUCCESS, dict_index_get_n_unique(), dict_table_get_first_index(), dict_table_is_comp(), err, btr_cur_struct::index, index(), trx_struct::isolation_level, sel_node_struct::latch_mode, lock_clust_rec_cons_read_sees(), lock_clust_rec_read_check_and_lock(), LOCK_ORDINARY, LOCK_REC_NOT_GAP, mem_heap_free, NULL, PAGE_CUR_LE, page_rec_is_user_rec(), plan(), sel_node_struct::read_view, rec_get_deleted_flag(), rec_get_offsets, REC_OFFS_NORMAL_SIZE, row_build_row_ref_fast(), sel_node_struct::row_lock_mode, row_sel_build_prev_vers(), row_sel_fetch_columns(), row_sel_sec_rec_is_for_clust_rec(), srv_locks_unsafe_for_binlog, thr_get_trx(), TRX_ISO_READ_COMMITTED, ut_a, and UT_LIST_GET_FIRST.
Referenced by row_sel().
00675 : DB_SUCCESS or error code */ 00676 sel_node_t* node, /* in: select_node */ 00677 plan_t* plan, /* in: plan node for table */ 00678 rec_t* rec, /* in: record in a non-clustered index */ 00679 que_thr_t* thr, /* in: query thread */ 00680 rec_t** out_rec,/* out: clustered record or an old version of 00681 it, NULL if the old version did not exist 00682 in the read view, i.e., it was a fresh 00683 inserted version */ 00684 mtr_t* mtr) /* in: mtr used to get access to the 00685 non-clustered record; the same mtr is used to 00686 access the clustered index */ 00687 { 00688 dict_index_t* index; 00689 rec_t* clust_rec; 00690 rec_t* old_vers; 00691 ulint err; 00692 mem_heap_t* heap = NULL; 00693 ulint offsets_[REC_OFFS_NORMAL_SIZE]; 00694 ulint* offsets = offsets_; 00695 *offsets_ = (sizeof offsets_) / sizeof *offsets_; 00696 00697 *out_rec = NULL; 00698 00699 offsets = rec_get_offsets(rec, 00700 btr_pcur_get_btr_cur(&plan->pcur)->index, 00701 offsets, ULINT_UNDEFINED, &heap); 00702 00703 row_build_row_ref_fast(plan->clust_ref, plan->clust_map, rec, offsets); 00704 00705 index = dict_table_get_first_index(plan->table); 00706 00707 btr_pcur_open_with_no_init(index, plan->clust_ref, PAGE_CUR_LE, 00708 node->latch_mode, &(plan->clust_pcur), 00709 0, mtr); 00710 00711 clust_rec = btr_pcur_get_rec(&(plan->clust_pcur)); 00712 00713 /* Note: only if the search ends up on a non-infimum record is the 00714 low_match value the real match to the search tuple */ 00715 00716 if (!page_rec_is_user_rec(clust_rec) 00717 || btr_pcur_get_low_match(&(plan->clust_pcur)) 00718 < dict_index_get_n_unique(index)) { 00719 00720 ut_a(rec_get_deleted_flag(rec, 00721 dict_table_is_comp(plan->table))); 00722 ut_a(node->read_view); 00723 00724 /* In a rare case it is possible that no clust rec is found 00725 for a delete-marked secondary index record: if in row0umod.c 00726 in row_undo_mod_remove_clust_low() we have already removed 00727 the clust rec, while purge is still cleaning and removing 00728 secondary index records associated with earlier versions of 00729 the clustered index record. In that case we know that the 00730 clustered index record did not exist in the read view of 00731 trx. */ 00732 00733 goto func_exit; 00734 } 00735 00736 offsets = rec_get_offsets(clust_rec, index, offsets, 00737 ULINT_UNDEFINED, &heap); 00738 00739 if (!node->read_view) { 00740 /* Try to place a lock on the index record */ 00741 00742 /* If innodb_locks_unsafe_for_binlog option is used 00743 or this session is using READ COMMITTED isolation level 00744 we lock only the record, i.e., next-key locking is 00745 not used. */ 00746 ulint lock_type; 00747 trx_t* trx; 00748 00749 trx = thr_get_trx(thr); 00750 00751 if (srv_locks_unsafe_for_binlog 00752 || trx->isolation_level == TRX_ISO_READ_COMMITTED) { 00753 lock_type = LOCK_REC_NOT_GAP; 00754 } else { 00755 lock_type = LOCK_ORDINARY; 00756 } 00757 00758 err = lock_clust_rec_read_check_and_lock(0, 00759 clust_rec, index, offsets, 00760 node->row_lock_mode, lock_type, thr); 00761 00762 if (err != DB_SUCCESS) { 00763 00764 goto err_exit; 00765 } 00766 } else { 00767 /* This is a non-locking consistent read: if necessary, fetch 00768 a previous version of the record */ 00769 00770 old_vers = NULL; 00771 00772 if (!lock_clust_rec_cons_read_sees(clust_rec, index, offsets, 00773 node->read_view)) { 00774 00775 err = row_sel_build_prev_vers(node->read_view, plan, 00776 clust_rec, &offsets, &heap, 00777 &old_vers, mtr); 00778 if (err != DB_SUCCESS) { 00779 00780 goto err_exit; 00781 } 00782 00783 clust_rec = old_vers; 00784 00785 if (clust_rec == NULL) { 00786 goto func_exit; 00787 } 00788 } 00789 00790 /* If we had to go to an earlier version of row or the 00791 secondary index record is delete marked, then it may be that 00792 the secondary index record corresponding to clust_rec 00793 (or old_vers) is not rec; in that case we must ignore 00794 such row because in our snapshot rec would not have existed. 00795 Remember that from rec we cannot see directly which transaction 00796 id corresponds to it: we have to go to the clustered index 00797 record. A query where we want to fetch all rows where 00798 the secondary index value is in some interval would return 00799 a wrong result if we would not drop rows which we come to 00800 visit through secondary index records that would not really 00801 exist in our snapshot. */ 00802 00803 if ((old_vers || rec_get_deleted_flag(rec, 00804 dict_table_is_comp(plan->table))) 00805 && !row_sel_sec_rec_is_for_clust_rec(rec, plan->index, 00806 clust_rec, index)) { 00807 goto func_exit; 00808 } 00809 } 00810 00811 /* Fetch the columns needed in test conditions */ 00812 00813 row_sel_fetch_columns(index, clust_rec, offsets, 00814 UT_LIST_GET_FIRST(plan->columns)); 00815 *out_rec = clust_rec; 00816 func_exit: 00817 err = DB_SUCCESS; 00818 err_exit: 00819 if (UNIV_LIKELY_NULL(heap)) { 00820 mem_heap_free(heap); 00821 } 00822 return(err); 00823 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static ulint row_sel_get_clust_rec_for_mysql | ( | row_prebuilt_t * | prebuilt, | |
| dict_index_t * | sec_index, | |||
| rec_t * | rec, | |||
| que_thr_t * | thr, | |||
| rec_t ** | out_rec, | |||
| ulint ** | offsets, | |||
| mem_heap_t ** | offset_heap, | |||
| mtr_t * | mtr | |||
| ) | [static] |
Definition at line 2757 of file row0sel.c.
References btr_pcur_get_low_match(), btr_pcur_get_rec(), btr_pcur_open_with_no_init(), btr_pcur_store_position(), BTR_SEARCH_LEAF, row_prebuilt_struct::clust_pcur, row_prebuilt_struct::clust_ref, DB_SUCCESS, dict_index_get_n_unique(), dict_index_name_print(), dict_table_get_first_index(), dict_table_is_comp(), err, trx_struct::isolation_level, lock_clust_rec_cons_read_sees(), lock_clust_rec_read_check_and_lock(), LOCK_NONE, LOCK_REC_NOT_GAP, LOCK_X, NULL, PAGE_CUR_LE, page_rec_is_user_rec(), trx_struct::read_view, rec_get_deleted_flag(), rec_get_offsets, rec_print(), row_build_row_ref_in_tuple(), row_sel_build_prev_vers_for_mysql(), row_sel_sec_rec_is_for_clust_rec(), row_prebuilt_struct::select_lock_type, dict_index_struct::table, thr_get_trx(), btr_pcur_struct::trx_if_known, TRX_ISO_READ_UNCOMMITTED, trx_print(), ut_a, and ut_print_timestamp().
Referenced by row_search_for_mysql().
02759 : DB_SUCCESS or error code */ 02760 row_prebuilt_t* prebuilt,/* in: prebuilt struct in the handle */ 02761 dict_index_t* sec_index,/* in: secondary index where rec resides */ 02762 rec_t* rec, /* in: record in a non-clustered index; if 02763 this is a locking read, then rec is not 02764 allowed to be delete-marked, and that would 02765 not make sense either */ 02766 que_thr_t* thr, /* in: query thread */ 02767 rec_t** out_rec,/* out: clustered record or an old version of 02768 it, NULL if the old version did not exist 02769 in the read view, i.e., it was a fresh 02770 inserted version */ 02771 ulint** offsets,/* out: offsets returned by 02772 rec_get_offsets(out_rec, clust_index) */ 02773 mem_heap_t** offset_heap,/* in/out: memory heap from which 02774 the offsets are allocated */ 02775 mtr_t* mtr) /* in: mtr used to get access to the 02776 non-clustered record; the same mtr is used to 02777 access the clustered index */ 02778 { 02779 dict_index_t* clust_index; 02780 rec_t* clust_rec; 02781 rec_t* old_vers; 02782 ulint err; 02783 trx_t* trx; 02784 02785 *out_rec = NULL; 02786 trx = thr_get_trx(thr); 02787 02788 row_build_row_ref_in_tuple(prebuilt->clust_ref, sec_index, rec, trx); 02789 02790 clust_index = dict_table_get_first_index(sec_index->table); 02791 02792 btr_pcur_open_with_no_init(clust_index, prebuilt->clust_ref, 02793 PAGE_CUR_LE, BTR_SEARCH_LEAF, 02794 prebuilt->clust_pcur, 0, mtr); 02795 02796 clust_rec = btr_pcur_get_rec(prebuilt->clust_pcur); 02797 02798 prebuilt->clust_pcur->trx_if_known = trx; 02799 02800 /* Note: only if the search ends up on a non-infimum record is the 02801 low_match value the real match to the search tuple */ 02802 02803 if (!page_rec_is_user_rec(clust_rec) 02804 || btr_pcur_get_low_match(prebuilt->clust_pcur) 02805 < dict_index_get_n_unique(clust_index)) { 02806 02807 /* In a rare case it is possible that no clust rec is found 02808 for a delete-marked secondary index record: if in row0umod.c 02809 in row_undo_mod_remove_clust_low() we have already removed 02810 the clust rec, while purge is still cleaning and removing 02811 secondary index records associated with earlier versions of 02812 the clustered index record. In that case we know that the 02813 clustered index record did not exist in the read view of 02814 trx. */ 02815 02816 if (!rec_get_deleted_flag(rec, 02817 dict_table_is_comp(sec_index->table)) 02818 || prebuilt->select_lock_type != LOCK_NONE) { 02819 ut_print_timestamp(stderr); 02820 fputs(" InnoDB: error clustered record" 02821 " for sec rec not found\n" 02822 "InnoDB: ", stderr); 02823 dict_index_name_print(stderr, trx, sec_index); 02824 fputs("\n" 02825 "InnoDB: sec index record ", stderr); 02826 rec_print(stderr, rec, sec_index); 02827 fputs("\n" 02828 "InnoDB: clust index record ", stderr); 02829 rec_print(stderr, clust_rec, clust_index); 02830 putc('\n', stderr); 02831 trx_print(stderr, trx, 600); 02832 02833 fputs("\n" 02834 "InnoDB: Submit a detailed bug report to http://bugs.mysql.com\n", stderr); 02835 } 02836 02837 clust_rec = NULL; 02838 02839 goto func_exit; 02840 } 02841 02842 *offsets = rec_get_offsets(clust_rec, clust_index, *offsets, 02843 ULINT_UNDEFINED, offset_heap); 02844 02845 if (prebuilt->select_lock_type != LOCK_NONE) { 02846 /* Try to place a lock on the index record; we are searching 02847 the clust rec with a unique condition, hence 02848 we set a LOCK_REC_NOT_GAP type lock */ 02849 02850 err = lock_clust_rec_read_check_and_lock(0, clust_rec, 02851 clust_index, *offsets, 02852 prebuilt->select_lock_type, 02853 LOCK_REC_NOT_GAP, thr); 02854 if (err != DB_SUCCESS) { 02855 02856 goto err_exit; 02857 } 02858 } else { 02859 /* This is a non-locking consistent read: if necessary, fetch 02860 a previous version of the record */ 02861 02862 old_vers = NULL; 02863 02864 /* If the isolation level allows reading of uncommitted data, 02865 then we never look for an earlier version */ 02866 02867 if (trx->isolation_level > TRX_ISO_READ_UNCOMMITTED 02868 && !lock_clust_rec_cons_read_sees(clust_rec, 02869 clust_index, *offsets, trx->read_view)) { 02870 02871 /* The following call returns 'offsets' associated with 02872 'old_vers' */ 02873 err = row_sel_build_prev_vers_for_mysql( 02874 trx->read_view, clust_index, 02875 prebuilt, clust_rec, 02876 offsets, offset_heap, 02877 &old_vers, mtr); 02878 02879 if (err != DB_SUCCESS) { 02880 02881 goto err_exit; 02882 } 02883 02884 clust_rec = old_vers; 02885 } 02886 02887 /* If we had to go to an earlier version of row or the 02888 secondary index record is delete marked, then it may be that 02889 the secondary index record corresponding to clust_rec 02890 (or old_vers) is not rec; in that case we must ignore 02891 such row because in our snapshot rec would not have existed. 02892 Remember that from rec we cannot see directly which transaction 02893 id corresponds to it: we have to go to the clustered index 02894 record. A query where we want to fetch all rows where 02895 the secondary index value is in some interval would return 02896 a wrong result if we would not drop rows which we come to 02897 visit through secondary index records that would not really 02898 exist in our snapshot. */ 02899 02900 if (clust_rec && (old_vers 02901 || rec_get_deleted_flag(rec, 02902 dict_table_is_comp(sec_index->table))) 02903 && !row_sel_sec_rec_is_for_clust_rec(rec, sec_index, 02904 clust_rec, clust_index)) { 02905 clust_rec = NULL; 02906 } else { 02907 #ifdef UNIV_SEARCH_DEBUG 02908 ut_a(clust_rec == NULL || 02909 row_sel_sec_rec_is_for_clust_rec(rec, 02910 sec_index, clust_rec, clust_index)); 02911 #endif 02912 } 02913 } 02914 02915 func_exit: 02916 *out_rec = clust_rec; 02917 02918 if (prebuilt->select_lock_type == LOCK_X) { 02919 /* We may use the cursor in update: store its position */ 02920 02921 btr_pcur_store_position(prebuilt->clust_pcur, mtr); 02922 } 02923 02924 err = DB_SUCCESS; 02925 err_exit: 02926 return(err); 02927 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static void row_sel_open_pcur | ( | sel_node_t * | node, | |
| plan_t * | plan, | |||
| ibool | search_latch_locked, | |||
| mtr_t * | mtr | |||
| ) | [static] |
Definition at line 866 of file row0sel.c.
References btr_pcur_open_at_index_side(), btr_pcur_open_with_no_init(), cond, dfield_copy_data(), dtuple_get_n_fields(), dtuple_get_nth_field(), eval_exp(), FALSE, index(), sel_node_struct::latch_mode, plan(), que_node_get_next(), que_node_get_val(), RW_S_LATCH, TRUE, ut_ad, UT_LIST_GET_FIRST, and UT_LIST_GET_NEXT.
Referenced by row_sel(), and row_sel_try_search_shortcut().
00868 : select node */ 00869 plan_t* plan, /* in: table plan */ 00870 ibool search_latch_locked, 00871 /* in: TRUE if the thread currently 00872 has the search latch locked in 00873 s-mode */ 00874 mtr_t* mtr) /* in: mtr */ 00875 { 00876 dict_index_t* index; 00877 func_node_t* cond; 00878 que_node_t* exp; 00879 ulint n_fields; 00880 ulint has_search_latch = 0; /* RW_S_LATCH or 0 */ 00881 ulint i; 00882 00883 if (search_latch_locked) { 00884 has_search_latch = RW_S_LATCH; 00885 } 00886 00887 index = plan->index; 00888 00889 /* Calculate the value of the search tuple: the exact match columns 00890 get their expressions evaluated when we evaluate the right sides of 00891 end_conds */ 00892 00893 cond = UT_LIST_GET_FIRST(plan->end_conds); 00894 00895 while (cond) { 00896 eval_exp(que_node_get_next(cond->args)); 00897 00898 cond = UT_LIST_GET_NEXT(cond_list, cond); 00899 } 00900 00901 if (plan->tuple) { 00902 n_fields = dtuple_get_n_fields(plan->tuple); 00903 00904 if (plan->n_exact_match < n_fields) { 00905 /* There is a non-exact match field which must be 00906 evaluated separately */ 00907 00908 eval_exp(plan->tuple_exps[n_fields - 1]); 00909 } 00910 00911 for (i = 0; i < n_fields; i++) { 00912 exp = plan->tuple_exps[i]; 00913 00914 dfield_copy_data(dtuple_get_nth_field(plan->tuple, i), 00915 que_node_get_val(exp)); 00916 } 00917 00918 /* Open pcur to the index */ 00919 00920 btr_pcur_open_with_no_init(index, plan->tuple, plan->mode, 00921 node->latch_mode, &(plan->pcur), 00922 has_search_latch, mtr); 00923 } else { 00924 /* Open the cursor to the start or the end of the index 00925 (FALSE: no init) */ 00926 00927 btr_pcur_open_at_index_side(plan->asc, index, node->latch_mode, 00928 &(plan->pcur), FALSE, mtr); 00929 } 00930 00931 ut_ad(plan->n_rows_prefetched == 0); 00932 ut_ad(plan->n_rows_fetched == 0); 00933 ut_ad(plan->cursor_at_end == FALSE); 00934 00935 plan->pcur_is_open = TRUE; 00936 }
Here is the call graph for this function:

Here is the caller graph for this function:

| UNIV_INLINE void row_sel_pop_cached_row_for_mysql | ( | byte * | buf, | |
| row_prebuilt_t * | prebuilt | |||
| ) |
Definition at line 3003 of file row0sel.c.
References row_prebuilt_struct::fetch_cache, row_prebuilt_struct::fetch_cache_first, row_prebuilt_struct::keep_other_fields_on_keyread, mysql_row_templ_struct::mysql_col_len, mysql_row_templ_struct::mysql_col_offset, mysql_row_templ_struct::mysql_null_bit_mask, mysql_row_templ_struct::mysql_null_byte_offset, row_prebuilt_struct::mysql_prefix_len, row_prebuilt_struct::mysql_row_len, row_prebuilt_struct::mysql_template, row_prebuilt_struct::n_fetch_cached, row_prebuilt_struct::n_template, ut_ad, and ut_memcpy().
Referenced by row_search_for_mysql().
03005 : buffer where to copy the 03006 row */ 03007 row_prebuilt_t* prebuilt) /* in: prebuilt struct */ 03008 { 03009 ulint i; 03010 mysql_row_templ_t* templ; 03011 byte* cached_rec; 03012 ut_ad(prebuilt->n_fetch_cached > 0); 03013 ut_ad(prebuilt->mysql_prefix_len <= prebuilt->mysql_row_len); 03014 03015 if (UNIV_UNLIKELY(prebuilt->keep_other_fields_on_keyread)) { 03016 /* Copy cache record field by field, don't touch fields that 03017 are not covered by current key */ 03018 cached_rec = 03019 prebuilt->fetch_cache[prebuilt->fetch_cache_first]; 03020 03021 for (i = 0; i < prebuilt->n_template; i++) { 03022 templ = prebuilt->mysql_template + i; 03023 ut_memcpy( 03024 buf + templ->mysql_col_offset, 03025 cached_rec + templ->mysql_col_offset, 03026 templ->mysql_col_len); 03027 /* Copy NULL bit of the current field from cached_rec 03028 to buf */ 03029 if (templ->mysql_null_bit_mask) { 03030 buf[templ->mysql_null_byte_offset] ^= 03031 (buf[templ->mysql_null_byte_offset] ^ 03032 cached_rec[templ->mysql_null_byte_offset]) & 03033 (byte)templ->mysql_null_bit_mask; 03034 } 03035 } 03036 } 03037 else { 03038 ut_memcpy(buf, prebuilt->fetch_cache[prebuilt->fetch_cache_first], 03039 prebuilt->mysql_prefix_len); 03040 } 03041 prebuilt->n_fetch_cached--; 03042 prebuilt->fetch_cache_first++; 03043 03044 if (prebuilt->n_fetch_cached == 0) { 03045 prebuilt->fetch_cache_first = 0; 03046 } 03047 }
Here is the call graph for this function:

Here is the caller graph for this function:

| UNIV_INLINE void row_sel_push_cache_row_for_mysql | ( | row_prebuilt_t * | prebuilt, | |
| rec_t * | rec, | |||
| const ulint * | offsets | |||
| ) |
Definition at line 3053 of file row0sel.c.
References buf, row_prebuilt_struct::fetch_cache, row_prebuilt_struct::fetch_cache_first, mach_write_to_4(), mem_alloc, MYSQL_FETCH_CACHE_SIZE, row_prebuilt_struct::mysql_row_len, row_prebuilt_struct::n_fetch_cached, NULL, rec_offs_validate(), ROW_PREBUILT_FETCH_MAGIC_N, row_sel_store_mysql_rec(), row_prebuilt_struct::templ_contains_blob, ut_a, ut_ad, and ut_error.
Referenced by row_search_for_mysql().
03055 : prebuilt struct */ 03056 rec_t* rec, /* in: record to push */ 03057 const ulint* offsets) /* in: rec_get_offsets() */ 03058 { 03059 byte* buf; 03060 ulint i; 03061 03062 ut_ad(prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE); 03063 ut_ad(rec_offs_validate(rec, NULL, offsets)); 03064 ut_a(!prebuilt->templ_contains_blob); 03065 03066 if (prebuilt->fetch_cache[0] == NULL) { 03067 /* Allocate memory for the fetch cache */ 03068 03069 for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) { 03070 03071 /* A user has reported memory corruption in these 03072 buffers in Linux. Put magic numbers there to help 03073 to track a possible bug. */ 03074 03075 buf = mem_alloc(prebuilt->mysql_row_len + 8); 03076 03077 prebuilt->fetch_cache[i] = buf + 4; 03078 03079 mach_write_to_4(buf, ROW_PREBUILT_FETCH_MAGIC_N); 03080 mach_write_to_4(buf + 4 + prebuilt->mysql_row_len, 03081 ROW_PREBUILT_FETCH_MAGIC_N); 03082 } 03083 } 03084 03085 ut_ad(prebuilt->fetch_cache_first == 0); 03086 03087 if (UNIV_UNLIKELY(!row_sel_store_mysql_rec( 03088 prebuilt->fetch_cache[prebuilt->n_fetch_cached], 03089 prebuilt, rec, offsets))) { 03090 ut_error; 03091 } 03092 03093 prebuilt->n_fetch_cached++; 03094 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static ibool row_sel_restore_pcur_pos | ( | sel_node_t * | node, | |
| plan_t * | plan, | |||
| mtr_t * | mtr | |||
| ) | [static] |
Definition at line 942 of file row0sel.c.
References BTR_PCUR_AFTER, BTR_PCUR_AFTER_LAST_IN_TREE, BTR_PCUR_BEFORE, BTR_PCUR_BEFORE_FIRST_IN_TREE, btr_pcur_get_rel_pos(), BTR_PCUR_ON, btr_pcur_restore_position(), FALSE, sel_node_struct::latch_mode, plan(), TRUE, and ut_ad.
Referenced by row_sel().
00944 : TRUE if the cursor should be moved to 00945 the next record after we return from this 00946 function (moved to the previous, in the case 00947 of a descending cursor) without processing 00948 again the current cursor record */ 00949 sel_node_t* node, /* in: select node */ 00950 plan_t* plan, /* in: table plan */ 00951 mtr_t* mtr) /* in: mtr */ 00952 { 00953 ibool equal_position; 00954 ulint relative_position; 00955 00956 ut_ad(!plan->cursor_at_end); 00957 00958 relative_position = btr_pcur_get_rel_pos(&(plan->pcur)); 00959 00960 equal_position = btr_pcur_restore_position(node->latch_mode, 00961 &(plan->pcur), mtr); 00962 00963 /* If the cursor is traveling upwards, and relative_position is 00964 00965 (1) BTR_PCUR_BEFORE: this is not allowed, as we did not have a lock 00966 yet on the successor of the page infimum; 00967 (2) BTR_PCUR_AFTER: btr_pcur_restore_position placed the cursor on the 00968 first record GREATER than the predecessor of a page supremum; we have 00969 not yet processed the cursor record: no need to move the cursor to the 00970 next record; 00971 (3) BTR_PCUR_ON: btr_pcur_restore_position placed the cursor on the 00972 last record LESS or EQUAL to the old stored user record; (a) if 00973 equal_position is FALSE, this means that the cursor is now on a record 00974 less than the old user record, and we must move to the next record; 00975 (b) if equal_position is TRUE, then if 00976 plan->stored_cursor_rec_processed is TRUE, we must move to the next 00977 record, else there is no need to move the cursor. */ 00978 00979 if (plan->asc) { 00980 if (relative_position == BTR_PCUR_ON) { 00981 00982 if (equal_position) { 00983 00984 return(plan->stored_cursor_rec_processed); 00985 } 00986 00987 return(TRUE); 00988 } 00989 00990 ut_ad(relative_position == BTR_PCUR_AFTER 00991 || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE); 00992 00993 return(FALSE); 00994 } 00995 00996 /* If the cursor is traveling downwards, and relative_position is 00997 00998 (1) BTR_PCUR_BEFORE: btr_pcur_restore_position placed the cursor on 00999 the last record LESS than the successor of a page infimum; we have not 01000 processed the cursor record: no need to move the cursor; 01001 (2) BTR_PCUR_AFTER: btr_pcur_restore_position placed the cursor on the 01002 first record GREATER than the predecessor of a page supremum; we have 01003 processed the cursor record: we should move the cursor to the previous 01004 record; 01005 (3) BTR_PCUR_ON: btr_pcur_restore_position placed the cursor on the 01006 last record LESS or EQUAL to the old stored user record; (a) if 01007 equal_position is FALSE, this means that the cursor is now on a record 01008 less than the old user record, and we need not move to the previous 01009 record; (b) if equal_position is TRUE, then if 01010 plan->stored_cursor_rec_processed is TRUE, we must move to the previous 01011 record, else there is no need to move the cursor. */ 01012 01013 if (relative_position == BTR_PCUR_BEFORE 01014 || relative_position == BTR_PCUR_BEFORE_FIRST_IN_TREE) { 01015 01016 return(FALSE); 01017 } 01018 01019 if (relative_position == BTR_PCUR_ON) { 01020 01021 if (equal_position) { 01022 01023 return(plan->stored_cursor_rec_processed); 01024 } 01025 01026 return(FALSE); 01027 } 01028 01029 ut_ad(relative_position == BTR_PCUR_AFTER 01030 || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE); 01031 01032 return(TRUE); 01033 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static ibool row_sel_sec_rec_is_for_clust_rec | ( | rec_t * | sec_rec, | |
| dict_index_t * | sec_index, | |||
| rec_t * | clust_rec, | |||
| dict_index_t * | clust_index | |||
| ) | [static] |
Definition at line 64 of file row0sel.c.
References cmp_data_data(), dict_col_get_clust_pos(), dict_col_get_type(), dict_field_get_col(), dict_index_get_n_ordering_defined_by_user(), dict_index_get_nth_field(), dtype_get_at_most_n_mbchars(), FALSE, is_equal(), mem_heap_free, n, NULL, dict_field_struct::prefix_len, rec_get_nth_field(), rec_get_offsets, REC_OFFS_NORMAL_SIZE, REC_OFFS_SMALL_SIZE, and TRUE.
Referenced by row_sel_get_clust_rec(), and row_sel_get_clust_rec_for_mysql().
00066 : TRUE if the secondary 00067 record is equal to the corresponding 00068 fields in the clustered record, 00069 when compared with collation */ 00070 rec_t* sec_rec, /* in: secondary index record */ 00071 dict_index_t* sec_index, /* in: secondary index */ 00072 rec_t* clust_rec, /* in: clustered index record */ 00073 dict_index_t* clust_index) /* in: clustered index */ 00074 { 00075 dict_field_t* ifield; 00076 dict_col_t* col; 00077 byte* sec_field; 00078 ulint sec_len; 00079 byte* clust_field; 00080 ulint clust_len; 00081 ulint n; 00082 ulint i; 00083 dtype_t* cur_type; 00084 mem_heap_t* heap = NULL; 00085 ulint clust_offsets_[REC_OFFS_NORMAL_SIZE]; 00086 ulint sec_offsets_[REC_OFFS_SMALL_SIZE]; 00087 ulint* clust_offs = clust_offsets_; 00088 ulint* sec_offs = sec_offsets_; 00089 ibool is_equal = TRUE; 00090 00091 *clust_offsets_ = (sizeof clust_offsets_) / sizeof *clust_offsets_; 00092 *sec_offsets_ = (sizeof sec_offsets_) / sizeof *sec_offsets_; 00093 00094 clust_offs = rec_get_offsets(clust_rec, clust_index, clust_offs, 00095 ULINT_UNDEFINED, &heap); 00096 sec_offs = rec_get_offsets(sec_rec, sec_index, sec_offs, 00097 ULINT_UNDEFINED, &heap); 00098 00099 n = dict_index_get_n_ordering_defined_by_user(sec_index); 00100 00101 for (i = 0; i < n; i++) { 00102 ifield = dict_index_get_nth_field(sec_index, i); 00103 col = dict_field_get_col(ifield); 00104 00105 clust_field = rec_get_nth_field(clust_rec, clust_offs, 00106 dict_col_get_clust_pos(col), 00107 &clust_len); 00108 sec_field = rec_get_nth_field(sec_rec, sec_offs, i, &sec_len); 00109 00110 if (ifield->prefix_len > 0 00111 && clust_len != UNIV_SQL_NULL) { 00112 00113 cur_type = dict_col_get_type( 00114 dict_field_get_col(ifield)); 00115 00116 clust_len = dtype_get_at_most_n_mbchars( 00117 cur_type, 00118 ifield->prefix_len, 00119 clust_len, (char*) clust_field); 00120 } 00121 00122 if (0 != cmp_data_data(dict_col_get_type(col), 00123 clust_field, clust_len, 00124 sec_field, sec_len)) { 00125 is_equal = FALSE; 00126 goto func_exit; 00127 } 00128 } 00129 00130 func_exit: 00131 if (UNIV_LIKELY_NULL(heap)) { 00132 mem_heap_free(heap); 00133 } 00134 return(is_equal); 00135 }
Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 1885 of file row0sel.c.
References sel_node_struct::consistent_read, sel_node_struct::copy_variables, DB_SUCCESS, err, trx_struct::error_state, sel_node_struct::explicit_cursor, sel_node_struct::fetch_table, que_thr_struct::graph, sel_node_struct::into_list, sel_node_struct::is_aggregate, que_fork_struct::last_sel_node, LOCK_IS, LOCK_IX, lock_table(), NULL, plan_reset_cursor(), que_thr_struct::prev_node, que_node_get_next(), que_node_get_parent(), que_node_get_type(), QUE_NODE_SELECT, sel_node_struct::read_view, row_sel(), row_sel_copy_input_variable_vals(), que_thr_struct::run_node, SEL_NODE_FETCH, sel_node_get_nth_plan(), SEL_NODE_OPEN, sel_reset_aggregate_vals(), sel_node_struct::set_x_locks, sel_node_struct::state, sym_node_struct::table, sel_node_struct::table_list, thr_get_trx(), trx_assign_read_view(), trx_start_if_not_started(), ut_ad, and UT_LIST_GET_FIRST.
Referenced by que_thr_step().
01887 : query thread to run next or NULL */ 01888 que_thr_t* thr) /* in: query thread */ 01889 { 01890 ulint i_lock_mode; 01891 sym_node_t* table_node; 01892 sel_node_t* node; 01893 ulint err; 01894 01895 ut_ad(thr); 01896 01897 node = thr->run_node; 01898 01899 ut_ad(que_node_get_type(node) == QUE_NODE_SELECT); 01900 01901 /* If this is a new time this node is executed (or when execution 01902 resumes after wait for a table intention lock), set intention locks 01903 on the tables, or assign a read view */ 01904 01905 if (node->into_list && (thr->prev_node == que_node_get_parent(node))) { 01906 01907 node->state = SEL_NODE_OPEN; 01908 } 01909 01910 if (node->state == SEL_NODE_OPEN) { 01911 01912 /* It may be that the current session has not yet started 01913 its transaction, or it has been committed: */ 01914 01915 trx_start_if_not_started(thr_get_trx(thr)); 01916 01917 plan_reset_cursor(sel_node_get_nth_plan(node, 0)); 01918 01919 if (node->consistent_read) { 01920 /* Assign a read view for the query */ 01921 node->read_view = trx_assign_read_view( 01922 thr_get_trx(thr)); 01923 } else { 01924 if (node->set_x_locks) { 01925 i_lock_mode = LOCK_IX; 01926 } else { 01927 i_lock_mode = LOCK_IS; 01928 } 01929 01930 table_node = node->table_list; 01931 01932 while (table_node) { 01933 err = lock_table(0, table_node->table, 01934 i_lock_mode, thr); 01935 if (err != DB_SUCCESS) { 01936 thr_get_trx(thr)->error_state = err; 01937 01938 return(NULL); 01939 } 01940 01941 table_node = que_node_get_next(table_node); 01942 } 01943 } 01944 01945 /* If this is an explicit cursor, copy stored procedure 01946 variable values, so that the values cannot change between 01947 fetches (currently, we copy them also for non-explicit 01948 cursors) */ 01949 01950 if (node->explicit_cursor && 01951 UT_LIST_GET_FIRST(node->copy_variables)) { 01952 01953 row_sel_copy_input_variable_vals(node); 01954 } 01955 01956 node->state = SEL_NODE_FETCH; 01957 node->fetch_table = 0; 01958 01959 if (node->is_aggregate) { 01960 /* Reset the aggregate total values */ 01961 sel_reset_aggregate_vals(node); 01962 } 01963 } 01964 01965 err = row_sel(node, thr); 01966 01967 /* NOTE! if queries are parallelized, the following assignment may 01968 have problems; the assignment should be made only if thr is the 01969 only top-level thr in the graph: */ 01970 01971 thr->graph->last_sel_node = node; 01972 01973 if (err != DB_SUCCESS) { 01974 thr_get_trx(thr)->error_state = err; 01975 01976 return(NULL); 01977 } 01978 01979 return(thr); 01980 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static ibool row_sel_store_mysql_rec | ( | byte * | mysql_rec, | |
| row_prebuilt_t * | prebuilt, | |||
| rec_t * | rec, | |||
| const ulint * | offsets | |||
| ) | [static] |
Definition at line 2569 of file row0sel.c.
References row_prebuilt_struct::blob_heap, data, DATA_BLOB, trx_struct::has_search_latch, mem_heap_create, mem_heap_free, row_prebuilt_struct::mysql_template, row_prebuilt_struct::n_template, NULL, mysql_row_templ_struct::rec_field_no, rec_offs_nth_extern(), rec_offs_validate(), row_prebuilt_struct::trx, mysql_row_templ_struct::type, UNIV_PAGE_SIZE, ut_a, and ut_ad.
Referenced by row_search_for_mysql(), and row_sel_push_cache_row_for_mysql().
02571 : TRUE if success, FALSE if 02572 could not allocate memory for a BLOB 02573 (though we may also assert in that 02574 case) */ 02575 byte* mysql_rec, /* out: row in the MySQL format */ 02576 row_prebuilt_t* prebuilt, /* in: prebuilt struct */ 02577 rec_t* rec, /* in: Innobase record in the index 02578 which was described in prebuilt's 02579 template */ 02580 const ulint* offsets) /* in: array returned by 02581 rec_get_offsets() */ 02582 { 02583 mysql_row_templ_t* templ; 02584 mem_heap_t* extern_field_heap = NULL; 02585 mem_heap_t* heap; 02586 byte* data; 02587 ulint len; 02588 ulint i; 02589 02590 ut_ad(prebuilt->mysql_template); 02591 ut_ad(rec_offs_validate(rec, NULL, offsets)); 02592 02593 if (UNIV_LIKELY_NULL(prebuilt->blob_heap)) { 02594 mem_heap_free(prebuilt->blob_heap); 02595 prebuilt->blob_heap = NULL; 02596 } 02597 02598 for (i = 0; i < prebuilt->n_template; i++) { 02599 02600 templ = prebuilt->mysql_template + i; 02601 02602 if (UNIV_UNLIKELY(rec_offs_nth_extern(offsets, 02603 templ->rec_field_no))) { 02604 02605 /* Copy an externally stored field to the temporary 02606 heap */ 02607 02608 ut_a(!prebuilt->trx->has_search_latch); 02609 02610 if (UNIV_UNLIKELY(templ->type == DATA_BLOB)) { 02611 if (prebuilt->blob_heap == NULL) { 02612 prebuilt->blob_heap = 02613 mem_heap_create(UNIV_PAGE_SIZE); 02614 } 02615 02616 heap = prebuilt->blob_heap; 02617 } else { 02618 extern_field_heap = 02619 mem_heap_create(UNIV_PAGE_SIZE); 02620 02621 heap = extern_field_heap; 02622 } 02623 02624 /* NOTE: if we are retrieving a big BLOB, we may 02625 already run out of memory in the next call, which 02626 causes an assert */ 02627 02628 data = btr_rec_copy_externally_stored_field(rec, 02629 offsets, templ->rec_field_no, &len, 02630 heap); 02631 02632 ut_a(len != UNIV_SQL_NULL); 02633 } else { 02634 /* Field is stored in the row. */ 02635 02636 data = rec_get_nth_field(rec, offsets, 02637 templ->rec_field_no, &len); 02638 } 02639 02640 if (len != UNIV_SQL_NULL) { 02641 row_sel_field_store_in_mysql_format( 02642 mysql_rec + templ->mysql_col_offset, 02643 templ, data, len); 02644 02645 /* Cleanup */ 02646 if (extern_field_heap) { 02647 mem_heap_free(extern_field_heap); 02648 extern_field_heap = NULL; 02649 } 02650 02651 if (templ->mysql_null_bit_mask) { 02652 /* It is a nullable column with a non-NULL 02653 value */ 02654 mysql_rec[templ->mysql_null_byte_offset] &= 02655 ~(byte) (templ->mysql_null_bit_mask); 02656 } 02657 } else { 02658 /* MySQL seems to assume the field for an SQL NULL 02659 value is set to zero or space. Not taking this into 02660 account caused seg faults with NULL BLOB fields, and 02661 bug number 154 in the MySQL bug database: GROUP BY 02662 and DISTINCT could treat NULL values inequal. */ 02663 int pad_char; 02664 02665 mysql_rec[templ->mysql_null_byte_offset] |= 02666 (byte) (templ->mysql_null_bit_mask); 02667 switch (templ->type) { 02668 case DATA_VARCHAR: 02669 case DATA_BINARY: 02670 case DATA_VARMYSQL: 02671 if (templ->mysql_type 02672 == DATA_MYSQL_TRUE_VARCHAR) { 02673 /* This is a >= 5.0.3 type 02674 true VARCHAR. Zero the field. */ 02675 pad_char = 0x00; 02676 break; 02677 } 02678 /* Fall through */ 02679 case DATA_CHAR: 02680 case DATA_FIXBINARY: 02681 case DATA_MYSQL: 02682 /* MySQL pads all string types (except 02683 BLOB, TEXT and true VARCHAR) with space. */ 02684 if (UNIV_UNLIKELY(templ->mbminlen == 2)) { 02685 /* Treat UCS2 as a special case. */ 02686 data = mysql_rec 02687 + templ->mysql_col_offset; 02688 len = templ->mysql_col_len; 02689 /* There are two UCS2 bytes per char, 02690 so the length has to be even. */ 02691 ut_a(!(len & 1)); 02692 /* Pad with 0x0020. */ 02693 while (len) { 02694 *data++ = 0x00; 02695 *data++ = 0x20; 02696 len -= 2; 02697 } 02698 continue; 02699 } 02700 pad_char = 0x20; 02701 break; 02702 default: 02703 pad_char = 0x00; 02704 break; 02705 } 02706 02707 ut_ad(!pad_char || templ->mbminlen == 1); 02708 memset(mysql_rec + templ->mysql_col_offset, 02709 pad_char, templ->mysql_col_len); 02710 } 02711 } 02712 02713 return(TRUE); 02714 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static void row_sel_store_row_id_to_prebuilt | ( | row_prebuilt_t * | prebuilt, | |
| rec_t * | index_rec, | |||
| dict_index_t * | index, | |||
| const ulint * | offsets | |||
| ) | [static] |
Definition at line 2392 of file row0sel.c.
References data, DATA_ROW_ID, DATA_ROW_ID_LEN, dict_index_get_sys_col_pos(), dict_index_name_print(), index(), rec_get_nth_field(), rec_offs_validate(), rec_print_new(), row_prebuilt_struct::row_id, row_prebuilt_struct::trx, ut_ad, ut_error, and ut_memcpy().
Referenced by row_search_for_mysql().
02394 : prebuilt */ 02395 rec_t* index_rec, /* in: record */ 02396 dict_index_t* index, /* in: index of the record */ 02397 const ulint* offsets) /* in: rec_get_offsets 02398 (index_rec, index) */ 02399 { 02400 byte* data; 02401 ulint len; 02402 02403 ut_ad(rec_offs_validate(index_rec, index, offsets)); 02404 02405 data = rec_get_nth_field(index_rec, offsets, 02406 dict_index_get_sys_col_pos(index, DATA_ROW_ID), &len); 02407 02408 if (len != DATA_ROW_ID_LEN) { 02409 fprintf(stderr, 02410 "InnoDB: Error: Row id field is wrong length %lu in ", (ulong) len); 02411 dict_index_name_print(stderr, prebuilt->trx, index); 02412 fprintf(stderr, "\n" 02413 "InnoDB: Field number %lu, record:\n", 02414 (ulong) dict_index_get_sys_col_pos(index, 02415 DATA_ROW_ID)); 02416 rec_print_new(stderr, index_rec, offsets); 02417 putc('\n', stderr); 02418 ut_error; 02419 } 02420 02421 ut_memcpy(prebuilt->row_id, data, len); 02422 }
Here is the call graph for this function:

Here is the caller graph for this function:

| UNIV_INLINE ibool row_sel_test_end_conds | ( | plan_t * | plan | ) |
Definition at line 607 of file row0sel.c.
References cond, eval_cmp(), eval_sym(), FALSE, plan(), TRUE, UT_LIST_GET_FIRST, and UT_LIST_GET_NEXT.
Referenced by row_sel().
00609 : TRUE if row passed the tests */ 00610 plan_t* plan) /* in: plan for the table; the column values must 00611 already have been retrieved and the right sides of 00612 comparisons evaluated */ 00613 { 00614 func_node_t* cond; 00615 00616 /* All conditions in end_conds are comparisons of a column to an 00617 expression */ 00618 00619 cond = UT_LIST_GET_FIRST(plan->end_conds); 00620 00621 while (cond) { 00622 /* Evaluate the left side of the comparison, i.e., get the 00623 column value if there is an indirection */ 00624 00625 eval_sym(cond->args); 00626 00627 /* Do the comparison */ 00628 00629 if (!eval_cmp(cond)) { 00630 00631 return(FALSE); 00632 } 00633 00634 cond = UT_LIST_GET_NEXT(cond_list, cond); 00635 } 00636 00637 return(TRUE); 00638 }
Here is the call graph for this function:

Here is the caller graph for this function:

| UNIV_INLINE ibool row_sel_test_other_conds | ( | plan_t * | plan | ) |
Definition at line 644 of file row0sel.c.
References cond, eval_exp(), eval_node_get_ibool_val(), FALSE, plan(), TRUE, UT_LIST_GET_FIRST, and UT_LIST_GET_NEXT.
Referenced by row_sel(), and row_sel_try_search_shortcut().
00646 : TRUE if row passed the tests */ 00647 plan_t* plan) /* in: plan for the table; the column values must 00648 already have been retrieved */ 00649 { 00650 func_node_t* cond; 00651 00652 cond = UT_LIST_GET_FIRST(plan->other_conds); 00653 00654 while (cond) { 00655 eval_exp(cond); 00656 00657 if (!eval_node_get_ibool_val(cond)) { 00658 00659 return(FALSE); 00660 } 00661 00662 cond = UT_LIST_GET_NEXT(cond_list, cond); 00663 } 00664 00665 return(TRUE); 00666 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static ulint row_sel_try_search_shortcut | ( | sel_node_t * | node, | |
| plan_t * | plan, | |||
| mtr_t * | mtr | |||
| ) | [static] |
Definition at line 1054 of file row0sel.c.
References btr_pcur_get_rec(), btr_pcur_get_up_match(), btr_search_latch, DICT_CLUSTERED, dict_table_is_comp(), index(), sel_node_struct::latch_mode, lock_clust_rec_cons_read_sees(), lock_sec_rec_cons_read_sees(), mem_heap_free, NULL, PAGE_CUR_GE, page_rec_is_user_rec(), plan(), sel_node_struct::read_view, rec_get_deleted_flag(), rec_get_offsets, REC_OFFS_NORMAL_SIZE, row_sel_fetch_columns(), row_sel_open_pcur(), row_sel_test_other_conds(), RW_LOCK_SHARED, SEL_EXHAUSTED, SEL_FOUND, SEL_RETRY, TRUE, ut_ad, and UT_LIST_GET_FIRST.
Referenced by row_sel().
01056 : SEL_FOUND, SEL_EXHAUSTED, SEL_RETRY */ 01057 sel_node_t* node, /* in: select node for a consistent read */ 01058 plan_t* plan, /* in: plan for a unique search in clustered 01059 index */ 01060 mtr_t* mtr) /* in: mtr */ 01061 { 01062 dict_index_t* index; 01063 rec_t* rec; 01064 mem_heap_t* heap = NULL; 01065 ulint offsets_[REC_OFFS_NORMAL_SIZE]; 01066 ulint* offsets = offsets_; 01067 ulint ret; 01068 *offsets_ = (sizeof offsets_) / sizeof *offsets_; 01069 01070 index = plan->index; 01071 01072 ut_ad(node->read_view); 01073 ut_ad(plan->unique_search); 01074 ut_ad(!plan->must_get_clust); 01075 #ifdef UNIV_SYNC_DEBUG 01076 ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_SHARED)); 01077 #endif /* UNIV_SYNC_DEBUG */ 01078 01079 row_sel_open_pcur(node, plan, TRUE, mtr); 01080 01081 rec = btr_pcur_get_rec(&(plan->pcur)); 01082 01083 if (!page_rec_is_user_rec(rec)) { 01084 01085 return(SEL_RETRY); 01086 } 01087 01088 ut_ad(plan->mode == PAGE_CUR_GE); 01089 01090 /* As the cursor is now placed on a user record after a search with 01091 the mode PAGE_CUR_GE, the up_match field in the cursor tells how many 01092 fields in the user record matched to the search tuple */ 01093 01094 if (btr_pcur_get_up_match(&(plan->pcur)) < plan->n_exact_match) { 01095 01096 return(SEL_EXHAUSTED); 01097 } 01098 01099 /* This is a non-locking consistent read: if necessary, fetch 01100 a previous version of the record */ 01101 01102 offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); 01103 01104 if (index->type & DICT_CLUSTERED) { 01105 if (!lock_clust_rec_cons_read_sees(rec, index, offsets, 01106 node->read_view)) { 01107 ret = SEL_RETRY; 01108 goto func_exit; 01109 } 01110 } else if (!lock_sec_rec_cons_read_sees(rec, index, node->read_view)) { 01111 01112 ret = SEL_RETRY; 01113 goto func_exit; 01114 } 01115 01116 /* Test deleted flag. Fetch the columns needed in test conditions. */ 01117 01118 row_sel_fetch_columns(index, rec, offsets, 01119 UT_LIST_GET_FIRST(plan->columns)); 01120 01121 if (rec_get_deleted_flag(rec, dict_table_is_comp(plan->table))) { 01122 01123 ret = SEL_EXHAUSTED; 01124 goto func_exit; 01125 } 01126 01127 /* Test the rest of search conditions */ 01128 01129 if (!row_sel_test_other_conds(plan)) { 01130 01131 ret = SEL_EXHAUSTED; 01132 goto func_exit; 01133 } 01134 01135 ut_ad(plan->pcur.latch_mode == node->latch_mode); 01136 01137 plan->n_rows_fetched++; 01138 ret = SEL_FOUND; 01139 func_exit: 01140 if (UNIV_LIKELY_NULL(heap)) { 01141 mem_heap_free(heap); 01142 } 01143 return(ret); 01144 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static ulint row_sel_try_search_shortcut_for_mysql | ( | rec_t ** | out_rec, | |
| row_prebuilt_t * | prebuilt, | |||
| ulint ** | offsets, | |||
| mem_heap_t ** | heap, | |||
| mtr_t * | mtr | |||
| ) | [static] |
Definition at line 3103 of file row0sel.c.
References btr_pcur_get_rec(), btr_pcur_get_up_match(), btr_pcur_open_with_no_init(), BTR_SEARCH_LEAF, DICT_CLUSTERED, dict_table_is_comp(), dtuple_get_n_fields(), row_prebuilt_struct::index, index(), lock_clust_rec_cons_read_sees(), PAGE_CUR_GE, page_rec_is_user_rec(), row_prebuilt_struct::pcur, trx_struct::read_view, rec_get_deleted_flag(), rec_get_offsets, RW_S_LATCH, row_prebuilt_struct::search_tuple, SEL_EXHAUSTED, SEL_FOUND, SEL_RETRY, row_prebuilt_struct::templ_contains_blob, row_prebuilt_struct::trx, and ut_ad.
Referenced by row_search_for_mysql().
03105 : SEL_FOUND, SEL_EXHAUSTED, SEL_RETRY */ 03106 rec_t** out_rec,/* out: record if found */ 03107 row_prebuilt_t* prebuilt,/* in: prebuilt struct */ 03108 ulint** offsets,/* in/out: for rec_get_offsets(*out_rec) */ 03109 mem_heap_t** heap, /* in/out: heap for rec_get_offsets() */ 03110 mtr_t* mtr) /* in: started mtr */ 03111 { 03112 dict_index_t* index = prebuilt->index; 03113 dtuple_t* search_tuple = prebuilt->search_tuple; 03114 btr_pcur_t* pcur = prebuilt->pcur; 03115 trx_t* trx = prebuilt->trx; 03116 rec_t* rec; 03117 03118 ut_ad(index->type & DICT_CLUSTERED); 03119 ut_ad(!prebuilt->templ_contains_blob); 03120 03121 btr_pcur_open_with_no_init(index, search_tuple, PAGE_CUR_GE, 03122 BTR_SEARCH_LEAF, pcur, 03123 #ifndef UNIV_SEARCH_DEBUG 03124 RW_S_LATCH, 03125 #else 03126 0, 03127 #endif 03128 mtr); 03129 rec = btr_pcur_get_rec(pcur); 03130 03131 if (!page_rec_is_user_rec(rec)) { 03132 03133 return(SEL_RETRY); 03134 } 03135 03136 /* As the cursor is now placed on a user record after a search with 03137 the mode PAGE_CUR_GE, the up_match field in the cursor tells how many 03138 fields in the user record matched to the search tuple */ 03139 03140 if (btr_pcur_get_up_match(pcur) < dtuple_get_n_fields(search_tuple)) { 03141 03142 return(SEL_EXHAUSTED); 03143 } 03144 03145 /* This is a non-locking consistent read: if necessary, fetch 03146 a previous version of the record */ 03147 03148 *offsets = rec_get_offsets(rec, index, *offsets, 03149 ULINT_UNDEFINED, heap); 03150 03151 if (!lock_clust_rec_cons_read_sees(rec, index, 03152 *offsets, trx->read_view)) { 03153 03154 return(SEL_RETRY); 03155 } 03156 03157 if (rec_get_deleted_flag(rec, dict_table_is_comp(index->table))) { 03158 03159 return(SEL_EXHAUSTED); 03160 } 03161 03162 *out_rec = rec; 03163 03164 return(SEL_FOUND); 03165 }
Here is the call graph for this function:

Here is the caller graph for this function:

| UNIV_INLINE void sel_assign_into_var_values | ( | sym_node_t * | var, | |
| sel_node_t * | node | |||
| ) |
Definition at line 211 of file row0sel.c.
References sym_node_struct::alias, eval_node_copy_val(), NULL, que_node_get_next(), sel_node_struct::select_list, and ut_ad.
Referenced by fetch_step(), and row_sel().
00213 : first variable in a list of variables */ 00214 sel_node_t* node) /* in: select node */ 00215 { 00216 que_node_t* exp; 00217 00218 if (var == NULL) { 00219 00220 return; 00221 } 00222 00223 exp = node->select_list; 00224 00225 while (var) { 00226 ut_ad(exp); 00227 00228 eval_node_copy_val(var->alias, exp); 00229 00230 exp = que_node_get_next(exp); 00231 var = que_node_get_next(var); 00232 } 00233 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static void sel_col_prefetch_buf_alloc | ( | sym_node_t * | column | ) | [static] |
Definition at line 357 of file row0sel.c.
References sel_buf_struct::data, mem_alloc, NULL, sym_node_struct::prefetch_buf, que_node_get_type(), QUE_NODE_SYMBOL, SEL_MAX_N_PREFETCH, ut_ad, and sel_buf_struct::val_buf_size.
Referenced by sel_push_prefetched_row().
00359 : symbol table node for a column */ 00360 { 00361 sel_buf_t* sel_buf; 00362 ulint i; 00363 00364 ut_ad(que_node_get_type(column) == QUE_NODE_SYMBOL); 00365 00366 column->prefetch_buf = mem_alloc(SEL_MAX_N_PREFETCH 00367 * sizeof(sel_buf_t)); 00368 for (i = 0; i < SEL_MAX_N_PREFETCH; i++) { 00369 sel_buf = column->prefetch_buf + i; 00370 00371 sel_buf->data = NULL; 00372 00373 sel_buf->val_buf_size = 0; 00374 } 00375 }
Here is the call graph for this function:

Here is the caller graph for this function:

| void sel_col_prefetch_buf_free | ( | sel_buf_t * | prefetch_buf | ) |
Definition at line 382 of file row0sel.c.
References sel_buf_struct::data, mem_free, SEL_MAX_N_PREFETCH, and sel_buf_struct::val_buf_size.
Referenced by sym_tab_free_private().
00384 : prefetch buffer */ 00385 { 00386 sel_buf_t* sel_buf; 00387 ulint i; 00388 00389 for (i = 0; i < SEL_MAX_N_PREFETCH; i++) { 00390 sel_buf = prefetch_buf + i; 00391 00392 if (sel_buf->val_buf_size > 0) { 00393 00394 mem_free(sel_buf->data); 00395 } 00396 } 00397 }
Here is the caller graph for this function:

| UNIV_INLINE void sel_eval_select_list | ( | sel_node_t * | node | ) |
Definition at line 191 of file row0sel.c.
References eval_exp(), que_node_get_next(), and sel_node_struct::select_list.
Referenced by row_sel().
00193 : select node */ 00194 { 00195 que_node_t* exp; 00196 00197 exp = node->select_list; 00198 00199 while (exp) { 00200 eval_exp(exp); 00201 00202 exp = que_node_get_next(exp); 00203 } 00204 }
Here is the call graph for this function:

Here is the caller graph for this function:

| sel_node_t* sel_node_create | ( | mem_heap_t * | heap | ) |
Definition at line 141 of file row0sel.c.
References BTR_SEARCH_LEAF, sel_node_struct::common, FALSE, sel_node_struct::latch_mode, mem_heap_alloc(), NULL, sel_node_struct::plans, QUE_NODE_SELECT, SEL_NODE_OPEN, sel_node_struct::select_will_do_update, sel_node_struct::state, and que_common_struct::type.
Referenced by pars_select_list(), and row_prebuild_sel_graph().
00143 : select node struct */ 00144 mem_heap_t* heap) /* in: memory heap where created */ 00145 { 00146 sel_node_t* node; 00147 00148 node = mem_heap_alloc(heap, sizeof(sel_node_t)); 00149 node->common.type = QUE_NODE_SELECT; 00150 node->state = SEL_NODE_OPEN; 00151 00152 node->select_will_do_update = FALSE; 00153 node->latch_mode = BTR_SEARCH_LEAF; 00154 00155 node->plans = NULL; 00156 00157 return(node); 00158 }
Here is the call graph for this function:

Here is the caller graph for this function:

| void sel_node_free_private | ( | sel_node_t * | node | ) |
Definition at line 165 of file row0sel.c.
References btr_pcur_close(), mem_heap_free, sel_node_struct::n_tables, NULL, plan(), sel_node_struct::plans, and sel_node_get_nth_plan().
Referenced by que_graph_free_recursive().
00167 : select node struct */ 00168 { 00169 ulint i; 00170 plan_t* plan; 00171 00172 if (node->plans != NULL) { 00173 for (i = 0; i < node->n_tables; i++) { 00174 plan = sel_node_get_nth_plan(node, i); 00175 00176 btr_pcur_close(&(plan->pcur)); 00177 btr_pcur_close(&(plan->clust_pcur)); 00178 00179 if (plan->old_vers_heap) { 00180 mem_heap_free(plan->old_vers_heap); 00181 } 00182 } 00183 } 00184 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static void sel_pop_prefetched_row | ( | plan_t * | plan | ) | [static] |
Definition at line 404 of file row0sel.c.
References sym_node_struct::copy_val, sel_buf_struct::data, data, dfield_get_data(), dfield_get_len(), dfield_set_data(), sel_buf_struct::len, NULL, plan(), sym_node_struct::prefetch_buf, que_node_get_val(), que_node_get_val_buf_size(), que_node_set_val_buf_size(), ut_ad, UT_LIST_GET_FIRST, UT_LIST_GET_NEXT, and sel_buf_struct::val_buf_size.
Referenced by row_sel().
00406 : plan node for a table */ 00407 { 00408 sym_node_t* column; 00409 sel_buf_t* sel_buf; 00410 dfield_t* val; 00411 byte* data; 00412 ulint len; 00413 ulint val_buf_size; 00414 00415 ut_ad(plan->n_rows_prefetched > 0); 00416 00417 column = UT_LIST_GET_FIRST(plan->columns); 00418 00419 while (column) { 00420 val = que_node_get_val(column); 00421 00422 if (!column->copy_val) { 00423 /* We did not really push any value for the 00424 column */ 00425 00426 ut_ad(!column->prefetch_buf); 00427 ut_ad(que_node_get_val_buf_size(column) == 0); 00428 #ifdef UNIV_DEBUG 00429 dfield_set_data(val, NULL, 0); 00430 #endif 00431 goto next_col; 00432 } 00433 00434 ut_ad(column->prefetch_buf); 00435 00436 sel_buf = column->prefetch_buf + plan->first_prefetched; 00437 00438 data = sel_buf->data; 00439 len = sel_buf->len; 00440 val_buf_size = sel_buf->val_buf_size; 00441 00442 /* We must keep track of the allocated memory for 00443 column values to be able to free it later: therefore 00444 we swap the values for sel_buf and val */ 00445 00446 sel_buf->data = dfield_get_data(val); 00447 sel_buf->len = dfield_get_len(val); 00448 sel_buf->val_buf_size = que_node_get_val_buf_size(column); 00449 00450 dfield_set_data(val, data, len); 00451 que_node_set_val_buf_size(column, val_buf_size); 00452 next_col: 00453 column = UT_LIST_GET_NEXT(col_var_list, column); 00454 } 00455 00456 plan->n_rows_prefetched--; 00457 00458 plan->first_prefetched++; 00459 }
Here is the call graph for this function:

Here is the caller graph for this function:

| UNIV_INLINE void sel_push_prefetched_row | ( | plan_t * | plan | ) |
Definition at line 466 of file row0sel.c.
References sym_node_struct::copy_val, sel_buf_struct::data, data, dfield_get_data(), dfield_get_len(), dfield_set_data(), sel_buf_struct::len, plan(), pos(), sym_node_struct::prefetch_buf, que_node_get_val(), que_node_get_val_buf_size(), que_node_set_val_buf_size(), sel_col_prefetch_buf_alloc(), SEL_MAX_N_PREFETCH, ut_ad, UT_LIST_GET_FIRST, UT_LIST_GET_NEXT, and sel_buf_struct::val_buf_size.
Referenced by row_sel().
00468 : plan node for a table */ 00469 { 00470 sym_node_t* column; 00471 sel_buf_t* sel_buf; 00472 dfield_t* val; 00473 byte* data; 00474 ulint len; 00475 ulint pos; 00476 ulint val_buf_size; 00477 00478 if (plan->n_rows_prefetched == 0) { 00479 pos = 0; 00480 plan->first_prefetched = 0; 00481 } else { 00482 pos = plan->n_rows_prefetched; 00483 00484 /* We have the convention that pushing new rows starts only 00485 after the prefetch stack has been emptied: */ 00486 00487 ut_ad(plan->first_prefetched == 0); 00488 } 00489 00490 plan->n_rows_prefetched++; 00491 00492 ut_ad(pos < SEL_MAX_N_PREFETCH); 00493 00494 column = UT_LIST_GET_FIRST(plan->columns); 00495 00496 while (column) { 00497 if (!column->copy_val) { 00498 /* There is no sense to push pointers to database 00499 page fields when we do not keep latch on the page! */ 00500 00501 goto next_col; 00502 } 00503 00504 if (!column->prefetch_buf) { 00505 /* Allocate a new prefetch buffer */ 00506 00507 sel_col_prefetch_buf_alloc(column); 00508 } 00509 00510 sel_buf = column->prefetch_buf + pos; 00511 00512 val = que_node_get_val(column); 00513 00514 data = dfield_get_data(val); 00515 len = dfield_get_len(val); 00516 val_buf_size = que_node_get_val_buf_size(column); 00517 00518 /* We must keep track of the allocated memory for 00519 column values to be able to free it later: therefore 00520 we swap the values for sel_buf and val */ 00521 00522 dfield_set_data(val, sel_buf->data, sel_buf->len); 00523 que_node_set_val_buf_size(column, sel_buf->val_buf_size); 00524 00525 sel_buf->data = data; 00526 sel_buf->len = len; 00527 sel_buf->val_buf_size = val_buf_size; 00528 next_col: 00529 column = UT_LIST_GET_NEXT(col_var_list, column); 00530 } 00531 }
Here is the call graph for this function:

Here is the caller graph for this function:

| UNIV_INLINE void sel_reset_aggregate_vals | ( | sel_node_t * | node | ) |
Definition at line 240 of file row0sel.c.
References sel_node_struct::aggregate_already_fetched, eval_node_set_int_val(), FALSE, sel_node_struct::is_aggregate, que_node_get_next(), sel_node_struct::select_list, and ut_ad.
Referenced by row_sel_step().
00242 : select node */ 00243 { 00244 func_node_t* func_node; 00245 00246 ut_ad(node->is_aggregate); 00247 00248 func_node = node->select_list; 00249 00250 while (func_node) { 00251 eval_node_set_int_val(func_node, 0); 00252 00253 func_node = que_node_get_next(func_node); 00254 } 00255 00256 node->aggregate_already_fetched = FALSE; 00257 }
Here is the call graph for this function:

Here is the caller graph for this function:

| static ibool sel_restore_position_for_mysql | ( | ibool * | same_user_rec, | |
| ulint | latch_mode, | |||
| btr_pcur_t * | pcur, | |||
| ibool | moves_up, | |||
| mtr_t * | mtr | |||
| ) | [static] |
Definition at line 2935 of file row0sel.c.
References BTR_PCUR_AFTER, BTR_PCUR_AFTER_LAST_IN_TREE, BTR_PCUR_BEFORE, BTR_PCUR_BEFORE_FIRST_IN_TREE, btr_pcur_is_on_user_rec(), btr_pcur_move_to_next(), btr_pcur_move_to_prev(), BTR_PCUR_ON, btr_pcur_restore_position(), FALSE, btr_pcur_struct::rel_pos, TRUE, and ut_ad.
Referenced by row_search_for_mysql().
02937 : TRUE if we may need to 02938 process the record the cursor is 02939 now positioned on (i.e. we should 02940 not go to the next record yet) */ 02941 ibool* same_user_rec, /* out: TRUE if we were able to restore 02942 the cursor on a user record with the 02943 same ordering prefix in in the 02944 B-tree index */ 02945 ulint latch_mode, /* in: latch mode wished in 02946 restoration */ 02947 btr_pcur_t* pcur, /* in: cursor whose position 02948 has been stored */ 02949 ibool moves_up, /* in: TRUE if the cursor moves up 02950 in the index */ 02951 mtr_t* mtr) /* in: mtr; CAUTION: may commit 02952 mtr temporarily! */ 02953 { 02954 ibool success; 02955 ulint relative_position; 02956 02957 relative_position = pcur->rel_pos; 02958 02959 success = btr_pcur_restore_position(latch_mode, pcur, mtr); 02960 02961 *same_user_rec = success; 02962 02963 if (relative_position == BTR_PCUR_ON) { 02964 if (success) { 02965 return(FALSE); 02966 } 02967 02968 if (moves_up) { 02969 btr_pcur_move_to_next(pcur, mtr); 02970 } 02971 02972 return(TRUE); 02973 } 02974 02975 if (relative_position == BTR_PCUR_AFTER 02976 || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE) { 02977 02978 if (moves_up) { 02979 return(TRUE); 02980 } 02981 02982 if (btr_pcur_is_on_user_rec(pcur, mtr)) { 02983 btr_pcur_move_to_prev(pcur, mtr); 02984 } 02985 02986 return(TRUE); 02987 } 02988 02989 ut_ad(relative_position == BTR_PCUR_BEFORE 02990 || relative_position == BTR_PCUR_BEFORE_FIRST_IN_TREE); 02991 02992 if (moves_up && btr_pcur_is_on_user_rec(pcur, mtr)) { 02993 btr_pcur_move_to_next(pcur, mtr); 02994 } 02995 02996 return(TRUE); 02997 }
Here is the call graph for this function:

Here is the caller graph for this function:

| UNIV_INLINE ulint sel_set_rec_lock | ( | rec_t * | rec, | |
| dict_index_t * | index, | |||
| const ulint * | offsets, | |||
| ulint | mode, | |||
| ulint | type, | |||
| que_thr_t * | thr | |||
| ) |
Definition at line 829 of file row0sel.c.
References buf_LRU_buf_pool_running_out(), DB_LOCK_TABLE_FULL, DICT_CLUSTERED, err, index(), lock_clust_rec_read_check_and_lock(), lock_sec_rec_read_check_and_lock(), thr_get_trx(), trx_struct::trx_locks, and UT_LIST_GET_LEN.
Referenced by row_search_for_mysql(), and row_sel().
00831 : DB_SUCCESS or error code */ 00832 rec_t* rec, /* in: record */ 00833 dict_index_t* index, /* in: index */ 00834 const ulint* offsets,/* in: rec_get_offsets(rec, index) */ 00835 ulint mode, /* in: lock mode */ 00836 ulint type, /* in: LOCK_ORDINARY, LOCK_GAP, or LOC_REC_NOT_GAP */ 00837 que_thr_t* thr) /* in: query thread */ 00838 { 00839 trx_t* trx; 00840 ulint err; 00841 00842 trx = thr_get_trx(thr); 00843 00844 if (UT_LIST_GET_LEN(trx->trx_locks) > 10000) { 00845 if (buf_LRU_buf_pool_running_out()) { 00846 00847 return(DB_LOCK_TABLE_FULL); 00848 } 00849 } 00850 00851 if (index->type & DICT_CLUSTERED) { 00852 err = lock_clust_rec_read_check_and_lock(0, 00853 rec, index, offsets, mode, type, thr); 00854 } else { 00855 err = lock_sec_rec_read_check_and_lock(0, 00856 rec, index, offsets, mode, type, thr); 00857 } 00858 00859 return(err); 00860 }
Here is the call graph for this function:

Here is the caller graph for this function:

1.4.7

