diff options
Diffstat (limited to 'innobase/row/row0sel.c')
-rw-r--r-- | innobase/row/row0sel.c | 1222 |
1 files changed, 795 insertions, 427 deletions
diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index f8218e08297..f8a65e6ff82 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -78,8 +78,20 @@ row_sel_sec_rec_is_for_clust_rec( ulint n; ulint i; dtype_t* cur_type; + mem_heap_t* heap = NULL; + ulint clust_offsets_[REC_OFFS_NORMAL_SIZE]; + ulint sec_offsets_[REC_OFFS_SMALL_SIZE]; + ulint* clust_offs = clust_offsets_; + ulint* sec_offs = sec_offsets_; + ibool is_equal = TRUE; - UT_NOT_USED(clust_index); + *clust_offsets_ = (sizeof clust_offsets_) / sizeof *clust_offsets_; + *sec_offsets_ = (sizeof sec_offsets_) / sizeof *sec_offsets_; + + clust_offs = rec_get_offsets(clust_rec, clust_index, clust_offs, + ULINT_UNDEFINED, &heap); + sec_offs = rec_get_offsets(sec_rec, sec_index, sec_offs, + ULINT_UNDEFINED, &heap); n = dict_index_get_n_ordering_defined_by_user(sec_index); @@ -87,10 +99,10 @@ row_sel_sec_rec_is_for_clust_rec( ifield = dict_index_get_nth_field(sec_index, i); col = dict_field_get_col(ifield); - clust_field = rec_get_nth_field(clust_rec, + clust_field = rec_get_nth_field(clust_rec, clust_offs, dict_col_get_clust_pos(col), &clust_len); - sec_field = rec_get_nth_field(sec_rec, i, &sec_len); + sec_field = rec_get_nth_field(sec_rec, sec_offs, i, &sec_len); if (ifield->prefix_len > 0 && clust_len != UNIV_SQL_NULL) { @@ -101,17 +113,22 @@ row_sel_sec_rec_is_for_clust_rec( clust_len = dtype_get_at_most_n_mbchars( cur_type, ifield->prefix_len, - clust_len, clust_field); + clust_len, (char*) clust_field); } if (0 != cmp_data_data(dict_col_get_type(col), clust_field, clust_len, sec_field, sec_len)) { - return(FALSE); + is_equal = FALSE; + goto func_exit; } } - return(TRUE); +func_exit: + if (UNIV_LIKELY_NULL(heap)) { + mem_heap_free(heap); + } + return(is_equal); } /************************************************************************* @@ -266,6 +283,7 @@ row_sel_fetch_columns( dict_index_t* index, /* in: record index */ rec_t* rec, /* in: record in a clustered or non-clustered index */ + const ulint* offsets,/* in: rec_get_offsets(rec, index) */ sym_node_t* column) /* in: first column in a column list, or NULL */ { @@ -275,6 +293,8 @@ row_sel_fetch_columns( byte* data; ulint len; + ut_ad(rec_offs_validate(rec, index, offsets)); + if (index->type & DICT_CLUSTERED) { index_type = SYM_CLUST_FIELD_NO; } else { @@ -286,7 +306,7 @@ row_sel_fetch_columns( if (field_no != ULINT_UNDEFINED) { - data = rec_get_nth_field(rec, field_no, &len); + data = rec_get_nth_field(rec, offsets, field_no, &len); if (column->copy_val) { eval_node_copy_and_alloc_val(column, data, @@ -491,6 +511,10 @@ row_sel_build_prev_vers( read_view_t* read_view, /* in: read view */ plan_t* plan, /* in: plan node for table */ rec_t* rec, /* in: record in a clustered index */ + ulint** offsets, /* in/out: offsets returned by + rec_get_offsets(rec, plan->index) */ + mem_heap_t** offset_heap, /* in/out: memory heap from which + the offsets are allocated */ rec_t** old_vers, /* out: old version, or NULL if the record does not exist in the view: i.e., it was freshly inserted @@ -506,8 +530,8 @@ row_sel_build_prev_vers( } err = row_vers_build_for_consistent_read(rec, mtr, plan->index, - read_view, plan->old_vers_heap, - old_vers); + offsets, read_view, offset_heap, + plan->old_vers_heap, old_vers); return(err); } @@ -601,8 +625,18 @@ row_sel_get_clust_rec( rec_t* clust_rec; rec_t* old_vers; ulint err; + mem_heap_t* heap = NULL; + ulint offsets_[REC_OFFS_NORMAL_SIZE]; + ulint* offsets = offsets_; + *offsets_ = (sizeof offsets_) / sizeof *offsets_; + + *out_rec = NULL; + + offsets = rec_get_offsets(rec, + btr_pcur_get_btr_cur(&plan->pcur)->index, + offsets, ULINT_UNDEFINED, &heap); - row_build_row_ref_fast(plan->clust_ref, plan->clust_map, rec); + row_build_row_ref_fast(plan->clust_ref, plan->clust_map, rec, offsets); index = dict_table_get_first_index(plan->table); @@ -619,7 +653,7 @@ row_sel_get_clust_rec( || btr_pcur_get_low_match(&(plan->clust_pcur)) < dict_index_get_n_unique(index)) { - ut_a(rec_get_deleted_flag(rec)); + ut_a(rec_get_deleted_flag(rec, plan->table->comp)); ut_a(node->read_view); /* In a rare case it is possible that no clust rec is found @@ -631,34 +665,33 @@ row_sel_get_clust_rec( clustered index record did not exist in the read view of trx. */ - clust_rec = NULL; - goto func_exit; } + offsets = rec_get_offsets(clust_rec, index, offsets, + ULINT_UNDEFINED, &heap); + if (!node->read_view) { /* Try to place a lock on the index record */ /* If innodb_locks_unsafe_for_binlog option is used, - we lock only the record, i.e. next-key locking is - not used. - */ + we lock only the record, i.e., next-key locking is + not used. */ + ulint lock_type; if (srv_locks_unsafe_for_binlog) { - err = lock_clust_rec_read_check_and_lock(0, - clust_rec, - index, node->row_lock_mode, - LOCK_REC_NOT_GAP, thr); + lock_type = LOCK_REC_NOT_GAP; } else { - err = lock_clust_rec_read_check_and_lock(0, - clust_rec, - index, node->row_lock_mode, - LOCK_ORDINARY, thr); + lock_type = LOCK_ORDINARY; } + err = lock_clust_rec_read_check_and_lock(0, + clust_rec, index, offsets, + node->row_lock_mode, lock_type, thr); + if (err != DB_SUCCESS) { - return(err); + goto err_exit; } } else { /* This is a non-locking consistent read: if necessary, fetch @@ -666,22 +699,21 @@ row_sel_get_clust_rec( old_vers = NULL; - if (!lock_clust_rec_cons_read_sees(clust_rec, index, + if (!lock_clust_rec_cons_read_sees(clust_rec, index, offsets, node->read_view)) { err = row_sel_build_prev_vers(node->read_view, plan, - clust_rec, &old_vers, mtr); + clust_rec, &offsets, &heap, + &old_vers, mtr); if (err != DB_SUCCESS) { - return(err); + goto err_exit; } clust_rec = old_vers; if (clust_rec == NULL) { - *out_rec = clust_rec; - - return(DB_SUCCESS); + goto func_exit; } } @@ -698,24 +730,25 @@ row_sel_get_clust_rec( visit through secondary index records that would not really exist in our snapshot. */ - if ((old_vers || rec_get_deleted_flag(rec)) + if ((old_vers || rec_get_deleted_flag(rec, plan->table->comp)) && !row_sel_sec_rec_is_for_clust_rec(rec, plan->index, clust_rec, index)) { - clust_rec = NULL; - *out_rec = clust_rec; - - return(DB_SUCCESS); + goto func_exit; } } /* Fetch the columns needed in test conditions */ - - row_sel_fetch_columns(index, clust_rec, + + row_sel_fetch_columns(index, clust_rec, offsets, UT_LIST_GET_FIRST(plan->columns)); -func_exit: *out_rec = clust_rec; - - return(DB_SUCCESS); +func_exit: + err = DB_SUCCESS; +err_exit: + if (UNIV_LIKELY_NULL(heap)) { + mem_heap_free(heap); + } + return(err); } /************************************************************************* @@ -727,6 +760,7 @@ sel_set_rec_lock( /* out: DB_SUCCESS or error code */ rec_t* rec, /* in: record */ dict_index_t* index, /* in: index */ + const ulint* offsets,/* in: rec_get_offsets(rec, index) */ ulint mode, /* in: lock mode */ ulint type, /* in: LOCK_ORDINARY, LOCK_GAP, or LOC_REC_NOT_GAP */ que_thr_t* thr) /* in: query thread */ @@ -744,11 +778,11 @@ sel_set_rec_lock( } if (index->type & DICT_CLUSTERED) { - err = lock_clust_rec_read_check_and_lock(0, rec, index, mode, - type, thr); + err = lock_clust_rec_read_check_and_lock(0, + rec, index, offsets, mode, type, thr); } else { - err = lock_sec_rec_read_check_and_lock(0, rec, index, mode, - type, thr); + err = lock_sec_rec_read_check_and_lock(0, + rec, index, offsets, mode, type, thr); } return(err); @@ -956,6 +990,11 @@ row_sel_try_search_shortcut( { dict_index_t* index; rec_t* rec; + mem_heap_t* heap = NULL; + ulint offsets_[REC_OFFS_NORMAL_SIZE]; + ulint* offsets = offsets_; + ulint ret; + *offsets_ = (sizeof offsets_) / sizeof *offsets_; index = plan->index; @@ -989,37 +1028,48 @@ row_sel_try_search_shortcut( /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ + offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); + if (index->type & DICT_CLUSTERED) { - if (!lock_clust_rec_cons_read_sees(rec, index, + if (!lock_clust_rec_cons_read_sees(rec, index, offsets, node->read_view)) { - return(SEL_RETRY); + ret = SEL_RETRY; + goto func_exit; } } else if (!lock_sec_rec_cons_read_sees(rec, index, node->read_view)) { - return(SEL_RETRY); + ret = SEL_RETRY; + goto func_exit; } /* Test deleted flag. Fetch the columns needed in test conditions. */ - - row_sel_fetch_columns(index, rec, UT_LIST_GET_FIRST(plan->columns)); - if (rec_get_deleted_flag(rec)) { + row_sel_fetch_columns(index, rec, offsets, + UT_LIST_GET_FIRST(plan->columns)); - return(SEL_EXHAUSTED); + if (rec_get_deleted_flag(rec, plan->table->comp)) { + + ret = SEL_EXHAUSTED; + goto func_exit; } /* Test the rest of search conditions */ if (!row_sel_test_other_conds(plan)) { - return(SEL_EXHAUSTED); + ret = SEL_EXHAUSTED; + goto func_exit; } ut_ad(plan->pcur.latch_mode == node->latch_mode); plan->n_rows_fetched++; - - return(SEL_FOUND); + ret = SEL_FOUND; +func_exit: + if (UNIV_LIKELY_NULL(heap)) { + mem_heap_free(heap); + } + return(ret); } /************************************************************************* @@ -1067,7 +1117,11 @@ row_sel( to the next non-clustered record */ ulint found_flag; ulint err; - + mem_heap_t* heap = NULL; + ulint offsets_[REC_OFFS_NORMAL_SIZE]; + ulint* offsets = offsets_; + *offsets_ = (sizeof offsets_) / sizeof *offsets_; + ut_ad(thr->run_node == node); search_latch_locked = FALSE; @@ -1207,7 +1261,7 @@ rec_loop: /* PHASE 1: Set a lock if specified */ if (!node->asc && cursor_just_opened - && (rec != page_get_supremum_rec(buf_frame_align(rec)))) { + && !page_rec_is_supremum(rec)) { /* When we open a cursor for a descending search, we must set a next-key lock on the successor record: otherwise it would @@ -1218,22 +1272,23 @@ rec_loop: if (!consistent_read) { /* If innodb_locks_unsafe_for_binlog option is used, - we lock only the record, i.e. next-key locking is - not used. - */ + we lock only the record, i.e., next-key locking is + not used. */ + + rec_t* next_rec = page_rec_get_next(rec); + ulint lock_type; + offsets = rec_get_offsets(next_rec, index, offsets, + ULINT_UNDEFINED, &heap); if (srv_locks_unsafe_for_binlog) { - err = sel_set_rec_lock(page_rec_get_next(rec), - index, - node->row_lock_mode, - LOCK_REC_NOT_GAP, thr); + lock_type = LOCK_REC_NOT_GAP; } else { - err = sel_set_rec_lock(page_rec_get_next(rec), - index, - node->row_lock_mode, - LOCK_ORDINARY, thr); + lock_type = LOCK_ORDINARY; } + err = sel_set_rec_lock(next_rec, index, offsets, + node->row_lock_mode, lock_type, thr); + if (err != DB_SUCCESS) { /* Note that in this case we will store in pcur the PREDECESSOR of the record we are waiting @@ -1244,7 +1299,7 @@ rec_loop: } } - if (rec == page_get_infimum_rec(buf_frame_align(rec))) { + if (page_rec_is_infimum(rec)) { /* The infimum record on a page cannot be in the result set, and neither can a record lock be placed on it: we skip such @@ -1260,25 +1315,35 @@ rec_loop: /* Try to place a lock on the index record */ /* If innodb_locks_unsafe_for_binlog option is used, - we lock only the record, i.e. next-key locking is - not used. - */ + we lock only the record, i.e., next-key locking is + not used. */ + + ulint lock_type; + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); if (srv_locks_unsafe_for_binlog) { - err = sel_set_rec_lock(rec, index, node->row_lock_mode, - LOCK_REC_NOT_GAP, thr); + + if (page_rec_is_supremum(rec)) { + + goto next_rec; + } + + lock_type = LOCK_REC_NOT_GAP; } else { - err = sel_set_rec_lock(rec, index, node->row_lock_mode, - LOCK_ORDINARY, thr); + lock_type = LOCK_ORDINARY; } + err = sel_set_rec_lock(rec, index, offsets, + node->row_lock_mode, lock_type, thr); + if (err != DB_SUCCESS) { goto lock_wait_or_error; } } - if (rec == page_get_supremum_rec(buf_frame_align(rec))) { + if (page_rec_is_supremum(rec)) { /* A page supremum record cannot be in the result set: skip it now when we have placed a possible lock on it */ @@ -1334,6 +1399,7 @@ rec_loop: /* PHASE 3: Get previous version in a consistent read */ cons_read_requires_clust_rec = FALSE; + offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (consistent_read) { /* This is a non-locking consistent read: if necessary, fetch @@ -1341,19 +1407,24 @@ rec_loop: if (index->type & DICT_CLUSTERED) { - if (!lock_clust_rec_cons_read_sees(rec, index, + if (!lock_clust_rec_cons_read_sees(rec, index, offsets, node->read_view)) { err = row_sel_build_prev_vers(node->read_view, - plan, rec, &old_vers, - &mtr); + plan, rec, + &offsets, &heap, + &old_vers, &mtr); if (err != DB_SUCCESS) { goto lock_wait_or_error; } if (old_vers == NULL) { + offsets = rec_get_offsets( + rec, index, offsets, + ULINT_UNDEFINED, &heap); row_sel_fetch_columns(index, rec, + offsets, UT_LIST_GET_FIRST(plan->columns)); if (!row_sel_test_end_conds(plan)) { @@ -1376,7 +1447,8 @@ rec_loop: /* Fetch the columns needed in test conditions */ - row_sel_fetch_columns(index, rec, UT_LIST_GET_FIRST(plan->columns)); + row_sel_fetch_columns(index, rec, offsets, + UT_LIST_GET_FIRST(plan->columns)); /* Test the selection end conditions: these can only contain columns which already are found in the index, even though the index might be @@ -1391,7 +1463,8 @@ rec_loop: goto table_exhausted; } - if (rec_get_deleted_flag(rec) && !cons_read_requires_clust_rec) { + if (rec_get_deleted_flag(rec, plan->table->comp) + && !cons_read_requires_clust_rec) { /* The record is delete marked: we can skip it if this is not a consistent read which might see an earlier version @@ -1434,7 +1507,7 @@ rec_loop: goto next_rec; } - if (rec_get_deleted_flag(clust_rec)) { + if (rec_get_deleted_flag(clust_rec, plan->table->comp)) { /* The record is delete marked: we can skip it */ @@ -1592,8 +1665,9 @@ next_table_no_mtr: if (search_latch_locked) { rw_lock_s_unlock(&btr_search_latch); } - - return(DB_SUCCESS); + + err = DB_SUCCESS; + goto func_exit; } node->fetch_table++; @@ -1626,6 +1700,7 @@ table_exhausted: table_exhausted_no_mtr: if (node->fetch_table == 0) { + err = DB_SUCCESS; if (node->is_aggregate && !node->aggregate_already_fetched) { @@ -1639,7 +1714,7 @@ table_exhausted_no_mtr: rw_lock_s_unlock(&btr_search_latch); } - return(DB_SUCCESS); + goto func_exit; } node->state = SEL_NODE_NO_MORE_ROWS; @@ -1650,7 +1725,7 @@ table_exhausted_no_mtr: rw_lock_s_unlock(&btr_search_latch); } - return(DB_SUCCESS); + goto func_exit; } node->fetch_table--; @@ -1674,8 +1749,8 @@ stop_for_a_while: mtr_commit(&mtr); ut_ad(sync_thread_levels_empty_gen(TRUE)); - - return(DB_SUCCESS); + err = DB_SUCCESS; + goto func_exit; commit_mtr_for_a_while: /* Stores the cursor position and commits &mtr; this is used if @@ -1710,6 +1785,10 @@ lock_wait_or_error: ut_ad(sync_thread_levels_empty_gen(TRUE)); +func_exit: + if (UNIV_LIKELY_NULL(heap)) { + mem_heap_free(heap); + } return(err); } @@ -1945,7 +2024,8 @@ Converts a key value stored in MySQL format to an Innobase dtuple. The last field of the key value may be just a prefix of a fixed length field: hence the parameter key_len. But currently we do not allow search keys where the last field is only a prefix of the full key field len and print a warning if -such appears. */ +such appears. A counterpart of this function is +ha_innobase::store_key_val_for_row() in ha_innodb.cc. */ void row_sel_convert_mysql_key_to_innobase( @@ -2026,10 +2106,10 @@ row_sel_convert_mysql_key_to_innobase( type = dfield_get_type(dfield)->mtype; /* Calculate data length and data field total length */ - + if (type == DATA_BLOB) { /* The key field is a column prefix of a BLOB or - TEXT type column */ + TEXT */ ut_a(field->prefix_len > 0); @@ -2045,11 +2125,12 @@ row_sel_convert_mysql_key_to_innobase( data_len = key_ptr[data_offset] + 256 * key_ptr[data_offset + 1]; data_field_len = data_offset + 2 + field->prefix_len; + data_offset += 2; - - type = DATA_CHAR; /* now that we know the length, we - store the column value like it would - be a fixed char field */ + + /* Now that we know the length, we store the column + value like it would be a fixed char field */ + } else if (field->prefix_len > 0) { /* Looks like MySQL pads unused end bytes in the prefix with space. Therefore, also in UTF-8, it is ok @@ -2069,14 +2150,32 @@ row_sel_convert_mysql_key_to_innobase( data_field_len = data_offset + data_len; } + if (dtype_get_mysql_type(dfield_get_type(dfield)) + == DATA_MYSQL_TRUE_VARCHAR + && dfield_get_type(dfield)->mtype != DATA_INT) { + /* In a MySQL key value format, a true VARCHAR is + always preceded by 2 bytes of a length field. + dfield_get_type(dfield)->len returns the maximum + 'payload' len in bytes. That does not include the + 2 bytes that tell the actual data length. + + We added the check != DATA_INT to make sure we do + not treat MySQL ENUM or SET as a true VARCHAR! */ + + data_len += 2; + data_field_len += 2; + } + /* Storing may use at most data_len bytes of buf */ if (!is_null) { row_mysql_store_col_in_innobase_format( - dfield, buf, key_ptr + data_offset, - data_len, type, - dfield_get_type(dfield)->prtype - & DATA_UNSIGNED); + dfield, + buf, + FALSE, /* MySQL key value format col */ + key_ptr + data_offset, + data_len, + index->table->comp); buf += data_len; } @@ -2133,11 +2232,16 @@ row_sel_store_row_id_to_prebuilt( /*=============================*/ row_prebuilt_t* prebuilt, /* in: prebuilt */ rec_t* index_rec, /* in: record */ - dict_index_t* index) /* in: index of the record */ + dict_index_t* index, /* in: index of the record */ + const ulint* offsets) /* in: rec_get_offsets + (index_rec, index) */ { byte* data; ulint len; - data = rec_get_nth_field(index_rec, + + ut_ad(rec_offs_validate(index_rec, index, offsets)); + + data = rec_get_nth_field(index_rec, offsets, dict_index_get_sys_col_pos(index, DATA_ROW_ID), &len); if (len != DATA_ROW_ID_LEN) { @@ -2146,8 +2250,8 @@ row_sel_store_row_id_to_prebuilt( dict_index_name_print(stderr, prebuilt->trx, index); fprintf(stderr, "\n" "InnoDB: Field number %lu, record:\n", - (ulong) dict_index_get_sys_col_pos(index, DATA_ROW_ID)); - rec_print(stderr, index_rec); + (ulong) dict_index_get_sys_col_pos(index, DATA_ROW_ID)); + rec_print_new(stderr, index_rec, offsets); putc('\n', stderr); ut_error; } @@ -2156,8 +2260,9 @@ row_sel_store_row_id_to_prebuilt( } /****************************************************************** -Stores a non-SQL-NULL field in the MySQL format. */ -UNIV_INLINE +Stores a non-SQL-NULL field in the MySQL format. The counterpart of this +function is row_mysql_store_col_in_innobase_format() in row0mysql.c. */ +static void row_sel_field_store_in_mysql_format( /*================================*/ @@ -2165,17 +2270,19 @@ row_sel_field_store_in_mysql_format( are not in themselves stored here: the caller must allocate and copy the BLOB into buffer before, and pass the pointer to the BLOB in 'data' */ - ulint col_len,/* in: MySQL column length */ + const mysql_row_templ_t* templ, /* in: MySQL column template. + Its following fields are referenced: + type, is_unsigned, mysql_col_len, mbminlen, mbmaxlen */ byte* data, /* in: data to store */ - ulint len, /* in: length of the data */ - ulint type, /* in: data type */ - ulint is_unsigned)/* in: != 0 if an unsigned integer type */ + ulint len) /* in: length of the data */ { byte* ptr; + byte* field_end; + byte* pad_ptr; ut_ad(len != UNIV_SQL_NULL); - if (type == DATA_INT) { + if (templ->type == DATA_INT) { /* Convert integer data from Innobase to a little-endian format, sign bit restored to normal */ @@ -2190,31 +2297,103 @@ row_sel_field_store_in_mysql_format( data++; } - if (!is_unsigned) { + if (!templ->is_unsigned) { dest[len - 1] = (byte) (dest[len - 1] ^ 128); } - ut_ad(col_len == len); - } else if (type == DATA_VARCHAR || type == DATA_VARMYSQL - || type == DATA_BINARY) { - /* Store the length of the data to the first two bytes of - dest; does not do anything yet because MySQL has - no real vars! */ + ut_ad(templ->mysql_col_len == len); + } else if (templ->type == DATA_VARCHAR + || templ->type == DATA_VARMYSQL + || templ->type == DATA_BINARY) { + + field_end = dest + templ->mysql_col_len; + + if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) { + /* This is a >= 5.0.3 type true VARCHAR. Store the + length of the data to the first byte or the first + two bytes of dest. */ - dest = row_mysql_store_var_len(dest, len); - ut_memcpy(dest, data, len); + dest = row_mysql_store_true_var_len(dest, len, + templ->mysql_length_bytes); + } - /* ut_ad(col_len >= len + 2); No real var implemented in - MySQL yet! */ + /* Copy the actual data */ + ut_memcpy(dest, data, len); - } else if (type == DATA_BLOB) { + /* Pad with trailing spaces. We pad with spaces also the + unused end of a >= 5.0.3 true VARCHAR column, just in case + MySQL expects its contents to be deterministic. */ + + pad_ptr = dest + len; + + ut_ad(templ->mbminlen <= templ->mbmaxlen); + + /* We handle UCS2 charset strings differently. */ + if (templ->mbminlen == 2) { + /* A space char is two bytes, 0x0020 in UCS2 */ + + if (len & 1) { + /* A 0x20 has been stripped from the column. + Pad it back. */ + + if (pad_ptr < field_end) { + *pad_ptr = 0x20; + pad_ptr++; + } + } + + /* Pad the rest of the string with 0x0020 */ + + while (pad_ptr < field_end) { + *pad_ptr = 0x00; + pad_ptr++; + *pad_ptr = 0x20; + pad_ptr++; + } + } else { + ut_ad(templ->mbminlen == 1); + /* space=0x20 */ + + memset(pad_ptr, 0x20, field_end - pad_ptr); + } + } else if (templ->type == DATA_BLOB) { /* Store a pointer to the BLOB buffer to dest: the BLOB was already copied to the buffer in row_sel_store_mysql_rec */ - row_mysql_store_blob_ref(dest, col_len, data, len); + row_mysql_store_blob_ref(dest, templ->mysql_col_len, data, + len); + } else if (templ->type == DATA_MYSQL) { + memcpy(dest, data, len); + + ut_ad(templ->mysql_col_len >= len); + ut_ad(templ->mbmaxlen >= templ->mbminlen); + + ut_ad(templ->mbmaxlen > templ->mbminlen + || templ->mysql_col_len == len); + /* The following assertion would fail for old tables + containing UTF-8 ENUM columns due to Bug #9526. */ + ut_ad(!templ->mbmaxlen + || !(templ->mysql_col_len % templ->mbmaxlen)); + ut_ad(len * templ->mbmaxlen >= templ->mysql_col_len); + + if (templ->mbminlen != templ->mbmaxlen) { + /* Pad with spaces. This undoes the stripping + done in row0mysql.ic, function + row_mysql_store_col_in_innobase_format(). */ + + memset(dest + len, 0x20, templ->mysql_col_len - len); + } } else { - ut_memcpy(dest, data, len); - ut_ad(col_len == len); + ut_ad(templ->type == DATA_CHAR + || templ->type == DATA_FIXBINARY + /*|| templ->type == DATA_SYS_CHILD + || templ->type == DATA_SYS*/ + || templ->type == DATA_FLOAT + || templ->type == DATA_DOUBLE + || templ->type == DATA_DECIMAL); + ut_ad(templ->mysql_col_len == len); + + memcpy(dest, data, len); } } @@ -2233,37 +2412,35 @@ row_sel_store_mysql_rec( case) */ byte* mysql_rec, /* out: row in the MySQL format */ row_prebuilt_t* prebuilt, /* in: prebuilt struct */ - rec_t* rec) /* in: Innobase record in the index + rec_t* rec, /* in: Innobase record in the index which was described in prebuilt's template */ + const ulint* offsets) /* in: array returned by + rec_get_offsets() */ { mysql_row_templ_t* templ; mem_heap_t* extern_field_heap = NULL; byte* data; ulint len; - byte* blob_buf; - int pad_char; ulint i; ut_ad(prebuilt->mysql_template); + ut_ad(rec_offs_validate(rec, NULL, offsets)); - if (prebuilt->blob_heap != NULL) { + if (UNIV_LIKELY_NULL(prebuilt->blob_heap)) { mem_heap_free(prebuilt->blob_heap); prebuilt->blob_heap = NULL; } - /* MySQL assumes that all columns have the SQL NULL bit set unless it - is a nullable column with a non-NULL value */ - - memset(mysql_rec, 0xFF, prebuilt->null_bitmap_len); - for (i = 0; i < prebuilt->n_template; i++) { templ = prebuilt->mysql_template + i; - data = rec_get_nth_field(rec, templ->rec_field_no, &len); + data = rec_get_nth_field(rec, offsets, + templ->rec_field_no, &len); - if (rec_get_nth_field_extern_bit(rec, templ->rec_field_no)) { + if (UNIV_UNLIKELY(rec_offs_nth_extern(offsets, + templ->rec_field_no))) { /* Copy an externally stored field to the temporary heap */ @@ -2277,14 +2454,14 @@ row_sel_store_mysql_rec( causes an assert */ data = btr_rec_copy_externally_stored_field(rec, - templ->rec_field_no, &len, + offsets, templ->rec_field_no, &len, extern_field_heap); ut_a(len != UNIV_SQL_NULL); } if (len != UNIV_SQL_NULL) { - if (templ->type == DATA_BLOB) { + if (UNIV_UNLIKELY(templ->type == DATA_BLOB)) { ut_a(prebuilt->templ_contains_blob); @@ -2293,8 +2470,9 @@ row_sel_store_mysql_rec( of 1000000 bytes. Since the test takes some CPU time, we do not use it for small BLOBs. */ - if (len > 2000000 - && !ut_test_malloc(len + 1000000)) { + if (UNIV_UNLIKELY(len > 2000000) + && UNIV_UNLIKELY(!ut_test_malloc( + len + 1000000))) { ut_print_timestamp(stderr); fprintf(stderr, @@ -2320,55 +2498,14 @@ row_sel_store_mysql_rec( mem_heap_create(len); } - blob_buf = mem_heap_alloc(prebuilt->blob_heap, - len); - ut_memcpy(blob_buf, data, len); - - data = blob_buf; + data = memcpy(mem_heap_alloc( + prebuilt->blob_heap, len), + data, len); } row_sel_field_store_in_mysql_format( mysql_rec + templ->mysql_col_offset, - templ->mysql_col_len, data, len, - templ->type, templ->is_unsigned); - - if (templ->type == DATA_VARCHAR - || templ->type == DATA_VARMYSQL - || templ->type == DATA_BINARY) { - /* Pad with trailing spaces */ - data = mysql_rec + templ->mysql_col_offset; - - /* Handle UCS2 strings differently. As no new - collations will be introduced in 4.1, we - hardcode the charset-collation codes here. - 5.0 will use a different approach. */ - if (templ->charset == 35 - || templ->charset == 90 - || (templ->charset >= 128 - && templ->charset <= 144)) { - /* space=0x0020 */ - ulint col_len = templ->mysql_col_len; - - ut_a(!(col_len & 1)); - if (len & 1) { - /* A 0x20 has been stripped - from the column. - Pad it back. */ - goto pad_0x20; - } - /* Pad the rest of the string - with 0x0020 */ - while (len < col_len) { - data[len++] = 0x00; - pad_0x20: - data[len++] = 0x20; - } - } else { - /* space=0x20 */ - memset(data + len, 0x20, - templ->mysql_col_len - len); - } - } + templ, data, len); /* Cleanup */ if (extern_field_heap) { @@ -2388,45 +2525,53 @@ row_sel_store_mysql_rec( account caused seg faults with NULL BLOB fields, and bug number 154 in the MySQL bug database: GROUP BY and DISTINCT could treat NULL values inequal. */ - - if (templ->type == DATA_VARCHAR - || templ->type == DATA_CHAR - || templ->type == DATA_BINARY - || templ->type == DATA_FIXBINARY - || templ->type == DATA_MYSQL - || templ->type == DATA_VARMYSQL) { - /* MySQL pads all non-BLOB and non-TEXT - string types with space ' ' */ - - pad_char = ' '; - } else { - pad_char = '\0'; + int pad_char; + + mysql_rec[templ->mysql_null_byte_offset] |= + (byte) (templ->mysql_null_bit_mask); + switch (templ->type) { + case DATA_VARCHAR: + case DATA_BINARY: + case DATA_VARMYSQL: + if (templ->mysql_type + == DATA_MYSQL_TRUE_VARCHAR) { + /* This is a >= 5.0.3 type + true VARCHAR. Zero the field. */ + pad_char = 0x00; + break; + } + /* Fall through */ + case DATA_CHAR: + case DATA_FIXBINARY: + case DATA_MYSQL: + /* MySQL pads all string types (except + BLOB, TEXT and true VARCHAR) with space. */ + if (UNIV_UNLIKELY(templ->mbminlen == 2)) { + /* Treat UCS2 as a special case. */ + data = mysql_rec + + templ->mysql_col_offset; + len = templ->mysql_col_len; + /* There are two UCS2 bytes per char, + so the length has to be even. */ + ut_a(!(len & 1)); + /* Pad with 0x0020. */ + while (len) { + *data++ = 0x00; + *data++ = 0x20; + len -= 2; + } + continue; + } + pad_char = 0x20; + break; + default: + pad_char = 0x00; + break; } - /* Handle UCS2 strings differently. As no new - collations will be introduced in 4.1, - we hardcode the charset-collation codes here. - 5.0 will use a different approach. */ - if (pad_char != '\0' - && (templ->charset == 35 - || templ->charset == 90 - || (templ->charset >= 128 - && templ->charset <= 144))) { - /* There are two bytes per char, so the length - has to be an even number. */ - ut_a(!(templ->mysql_col_len & 1)); - data = mysql_rec + templ->mysql_col_offset; - len = templ->mysql_col_len; - /* Pad with 0x0020. */ - while (len >= 2) { - *data++ = 0x00; - *data++ = 0x20; - len -= 2; - } - } else { - memset(mysql_rec + templ->mysql_col_offset, + ut_ad(!pad_char || templ->mbminlen == 1); + memset(mysql_rec + templ->mysql_col_offset, pad_char, templ->mysql_col_len); - } } } @@ -2444,6 +2589,10 @@ row_sel_build_prev_vers_for_mysql( dict_index_t* clust_index, /* in: clustered index */ row_prebuilt_t* prebuilt, /* in: prebuilt struct */ rec_t* rec, /* in: record in a clustered index */ + ulint** offsets, /* in/out: offsets returned by + rec_get_offsets(rec, clust_index) */ + mem_heap_t** offset_heap, /* in/out: memory heap from which + the offsets are allocated */ rec_t** old_vers, /* out: old version, or NULL if the record does not exist in the view: i.e., it was freshly inserted @@ -2459,8 +2608,8 @@ row_sel_build_prev_vers_for_mysql( } err = row_vers_build_for_consistent_read(rec, mtr, clust_index, - read_view, prebuilt->old_vers_heap, - old_vers); + offsets, read_view, offset_heap, + prebuilt->old_vers_heap, old_vers); return(err); } @@ -2484,6 +2633,10 @@ row_sel_get_clust_rec_for_mysql( it, NULL if the old version did not exist in the read view, i.e., it was a fresh inserted version */ + ulint** offsets,/* out: offsets returned by + rec_get_offsets(out_rec, clust_index) */ + mem_heap_t** offset_heap,/* in/out: memory heap from which + the offsets are allocated */ mtr_t* mtr) /* in: mtr used to get access to the non-clustered record; the same mtr is used to access the clustered index */ @@ -2525,9 +2678,8 @@ row_sel_get_clust_rec_for_mysql( clustered index record did not exist in the read view of trx. */ - if (!rec_get_deleted_flag(rec) + if (!rec_get_deleted_flag(rec, sec_index->table->comp) || prebuilt->select_lock_type != LOCK_NONE) { - ut_print_timestamp(stderr); fputs(" InnoDB: error clustered record" " for sec rec not found\n" @@ -2535,12 +2687,12 @@ row_sel_get_clust_rec_for_mysql( dict_index_name_print(stderr, trx, sec_index); fputs("\n" "InnoDB: sec index record ", stderr); - rec_print(stderr, rec); + rec_print(stderr, rec, sec_index); fputs("\n" "InnoDB: clust index record ", stderr); - rec_print(stderr, clust_rec); + rec_print(stderr, clust_rec, clust_index); putc('\n', stderr); - trx_print(stderr, trx); + trx_print(stderr, trx, 600); fputs("\n" "InnoDB: Submit a detailed bug report to http://bugs.mysql.com\n", stderr); @@ -2551,18 +2703,21 @@ row_sel_get_clust_rec_for_mysql( goto func_exit; } + *offsets = rec_get_offsets(clust_rec, clust_index, *offsets, + ULINT_UNDEFINED, offset_heap); + if (prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a lock on the index record; we are searching the clust rec with a unique condition, hence we set a LOCK_REC_NOT_GAP type lock */ err = lock_clust_rec_read_check_and_lock(0, clust_rec, - clust_index, + clust_index, *offsets, prebuilt->select_lock_type, LOCK_REC_NOT_GAP, thr); if (err != DB_SUCCESS) { - return(err); + goto err_exit; } } else { /* This is a non-locking consistent read: if necessary, fetch @@ -2575,16 +2730,19 @@ row_sel_get_clust_rec_for_mysql( if (trx->isolation_level > TRX_ISO_READ_UNCOMMITTED && !lock_clust_rec_cons_read_sees(clust_rec, clust_index, - trx->read_view)) { - + *offsets, trx->read_view)) { + + /* The following call returns 'offsets' associated with + 'old_vers' */ err = row_sel_build_prev_vers_for_mysql( trx->read_view, clust_index, prebuilt, clust_rec, + offsets, offset_heap, &old_vers, mtr); if (err != DB_SUCCESS) { - return(err); + goto err_exit; } clust_rec = old_vers; @@ -2603,7 +2761,8 @@ row_sel_get_clust_rec_for_mysql( visit through secondary index records that would not really exist in our snapshot. */ - if (clust_rec && (old_vers || rec_get_deleted_flag(rec)) + if (clust_rec && (old_vers + || rec_get_deleted_flag(rec, sec_index->table->comp)) && !row_sel_sec_rec_is_for_clust_rec(rec, sec_index, clust_rec, clust_index)) { clust_rec = NULL; @@ -2619,13 +2778,15 @@ row_sel_get_clust_rec_for_mysql( func_exit: *out_rec = clust_rec; - if (prebuilt->select_lock_type == LOCK_X) { - /* We may use the cursor in update: store its position */ + if (prebuilt->select_lock_type != LOCK_NONE) { + /* We may use the cursor in unlock: store its position */ btr_pcur_store_position(prebuilt->clust_pcur, mtr); } - return(DB_SUCCESS); + err = DB_SUCCESS; +err_exit: + return(err); } /************************************************************************ @@ -2640,6 +2801,10 @@ sel_restore_position_for_mysql( process the record the cursor is now positioned on (i.e. we should not go to the next record yet) */ + ibool* same_user_rec, /* out: TRUE if we were able to restore + the cursor on a user record with the + same ordering prefix in in the + B-tree index */ ulint latch_mode, /* in: latch mode wished in restoration */ btr_pcur_t* pcur, /* in: cursor whose position @@ -2656,6 +2821,8 @@ sel_restore_position_for_mysql( success = btr_pcur_restore_position(latch_mode, pcur, mtr); + *same_user_rec = success; + if (relative_position == BTR_PCUR_ON) { if (success) { return(FALSE); @@ -2702,10 +2869,41 @@ row_sel_pop_cached_row_for_mysql( row */ row_prebuilt_t* prebuilt) /* in: prebuilt struct */ { - ut_ad(prebuilt->n_fetch_cached > 0); - - ut_memcpy(buf, prebuilt->fetch_cache[prebuilt->fetch_cache_first], - prebuilt->mysql_row_len); + ulint i; + mysql_row_templ_t* templ; + byte* cached_rec; + ut_ad(prebuilt->n_fetch_cached > 0); + ut_ad(prebuilt->mysql_prefix_len <= prebuilt->mysql_row_len); + + if (UNIV_UNLIKELY(prebuilt->keep_other_fields_on_keyread)) + { + /* Copy cache record field by field, don't touch fields that + are not covered by current key */ + cached_rec = + prebuilt->fetch_cache[prebuilt->fetch_cache_first]; + + for (i = 0; i < prebuilt->n_template; i++) { + templ = prebuilt->mysql_template + i; + ut_memcpy( + buf + templ->mysql_col_offset, + cached_rec + templ->mysql_col_offset, + templ->mysql_col_len); + /* Copy NULL bit of the current field from cached_rec + to buf */ + if (templ->mysql_null_bit_mask) + { + buf[templ->mysql_null_byte_offset] ^= + (buf[templ->mysql_null_byte_offset] ^ + cached_rec[templ->mysql_null_byte_offset]) & + (byte)templ->mysql_null_bit_mask; + } + } + } + else + { + ut_memcpy(buf, prebuilt->fetch_cache[prebuilt->fetch_cache_first], + prebuilt->mysql_prefix_len); + } prebuilt->n_fetch_cached--; prebuilt->fetch_cache_first++; @@ -2721,12 +2919,14 @@ void row_sel_push_cache_row_for_mysql( /*=============================*/ row_prebuilt_t* prebuilt, /* in: prebuilt struct */ - rec_t* rec) /* in: record to push */ + rec_t* rec, /* in: record to push */ + const ulint* offsets) /* in: rec_get_offsets() */ { byte* buf; ulint i; ut_ad(prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE); + ut_ad(rec_offs_validate(rec, NULL, offsets)); ut_a(!prebuilt->templ_contains_blob); if (prebuilt->fetch_cache[0] == NULL) { @@ -2750,9 +2950,11 @@ row_sel_push_cache_row_for_mysql( ut_ad(prebuilt->fetch_cache_first == 0); - ut_a(row_sel_store_mysql_rec( + if (UNIV_UNLIKELY(!row_sel_store_mysql_rec( prebuilt->fetch_cache[prebuilt->n_fetch_cached], - prebuilt, rec)); + prebuilt, rec, offsets))) { + ut_error; + } prebuilt->n_fetch_cached++; } @@ -2769,6 +2971,8 @@ row_sel_try_search_shortcut_for_mysql( /* out: SEL_FOUND, SEL_EXHAUSTED, SEL_RETRY */ rec_t** out_rec,/* out: record if found */ row_prebuilt_t* prebuilt,/* in: prebuilt struct */ + ulint** offsets,/* in/out: for rec_get_offsets(*out_rec) */ + mem_heap_t** heap, /* in/out: heap for rec_get_offsets() */ mtr_t* mtr) /* in: started mtr */ { dict_index_t* index = prebuilt->index; @@ -2806,13 +3010,17 @@ row_sel_try_search_shortcut_for_mysql( /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ - - if (!lock_clust_rec_cons_read_sees(rec, index, trx->read_view)) { + + *offsets = rec_get_offsets(rec, index, *offsets, + ULINT_UNDEFINED, heap); + + if (!lock_clust_rec_cons_read_sees(rec, index, + *offsets, trx->read_view)) { return(SEL_RETRY); } - if (rec_get_deleted_flag(rec)) { + if (rec_get_deleted_flag(rec, index->table->comp)) { return(SEL_EXHAUSTED); } @@ -2856,21 +3064,17 @@ row_search_for_mysql( cursor 'direction' should be 0. */ { dict_index_t* index = prebuilt->index; + ibool comp = index->table->comp; dtuple_t* search_tuple = prebuilt->search_tuple; btr_pcur_t* pcur = prebuilt->pcur; trx_t* trx = prebuilt->trx; dict_index_t* clust_index; que_thr_t* thr; rec_t* rec; - rec_t* index_rec; + rec_t* result_rec; rec_t* clust_rec; rec_t* old_vers; - ulint err = DB_SUCCESS; - ibool moved; - ibool cons_read_requires_clust_rec; - ibool was_lock_wait; - ulint ret; - ulint shortcut; + ulint err = DB_SUCCESS; ibool unique_search = FALSE; ibool unique_search_from_clust_index = FALSE; ibool mtr_has_extra_clust_latch = FALSE; @@ -2880,15 +3084,22 @@ row_search_for_mysql( locking SELECT, and the isolation level is <= TRX_ISO_READ_COMMITTED, then this is set to FALSE */ - ibool success; +#ifdef UNIV_SEARCH_DEBUG ulint cnt = 0; +#endif /* UNIV_SEARCH_DEBUG */ ulint next_offs; + ibool same_user_rec; mtr_t mtr; - + mem_heap_t* heap = NULL; + ulint offsets_[REC_OFFS_NORMAL_SIZE]; + ulint* offsets = offsets_; + + *offsets_ = (sizeof offsets_) / sizeof *offsets_; + ut_ad(index && pcur && search_tuple); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); - if (prebuilt->table->ibd_file_missing) { + if (UNIV_UNLIKELY(prebuilt->table->ibd_file_missing)) { ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: Error:\n" "InnoDB: MySQL is trying to use a table handle but the .ibd file for\n" @@ -2896,13 +3107,14 @@ row_search_for_mysql( "InnoDB: Have you deleted the .ibd file from the database directory under\n" "InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n" "InnoDB: Look from\n" -"http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n" +"InnoDB: http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n" "InnoDB: how you can resolve the problem.\n", prebuilt->table->name); + return(DB_ERROR); } - if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { + if (UNIV_UNLIKELY(prebuilt->magic_n != ROW_PREBUILT_ALLOCATED)) { fprintf(stderr, "InnoDB: Error: trying to free a corrupt\n" "InnoDB: table handle. Magic n %lu, table name ", @@ -2916,18 +3128,23 @@ row_search_for_mysql( } if (trx->n_mysql_tables_in_use == 0 - && prebuilt->select_lock_type == LOCK_NONE) { + && UNIV_UNLIKELY(prebuilt->select_lock_type == LOCK_NONE)) { /* Note that if MySQL uses an InnoDB temp table that it created inside LOCK TABLES, then n_mysql_tables_in_use can be zero; in that case select_lock_type is set to LOCK_X in ::start_stmt. */ +/* August 19, 2005 by Heikki: temporarily disable this error print until the +cursor lock count is done correctly. See bugs #12263 and #12456! + fputs( "InnoDB: Error: MySQL is trying to perform a SELECT\n" "InnoDB: but it has not locked any tables in ::external_lock()!\n", stderr); - trx_print(stderr, trx); + trx_print(stderr, trx, 600); fputc('\n', stderr); +*/ + } /* fprintf(stderr, "Match mode %lu\n search tuple ", (ulong) match_mode); @@ -2939,8 +3156,8 @@ row_search_for_mysql( /* PHASE 0: Release a possible s-latch we are holding on the adaptive hash index latch if there is someone waiting behind */ - if (trx->has_search_latch - && btr_search_latch.writer != RW_LOCK_NOT_LOCKED) { + if (UNIV_UNLIKELY(btr_search_latch.writer != RW_LOCK_NOT_LOCKED) + && trx->has_search_latch) { /* There is an x-latch request on the adaptive hash index: release the s-latch to reduce starvation and wait for @@ -2953,10 +3170,20 @@ row_search_for_mysql( trx->search_latch_timeout = BTR_SEA_TIMEOUT; } + /* Reset the new record lock info if we srv_locks_unsafe_for_binlog + is set. Then we are able to remove the record locks set here on an + individual row. */ + + if (srv_locks_unsafe_for_binlog + && prebuilt->select_lock_type != LOCK_NONE) { + + trx_reset_new_rec_lock_info(trx); + } + /*-------------------------------------------------------------*/ /* PHASE 1: Try to pop the row from the prefetch cache */ - if (direction == 0) { + if (UNIV_UNLIKELY(direction == 0)) { trx->op_info = "starting index read"; prebuilt->n_rows_fetched = 0; @@ -2974,8 +3201,8 @@ row_search_for_mysql( prebuilt->fetch_direction = direction; } - if (direction != prebuilt->fetch_direction) { - if (prebuilt->n_fetch_cached > 0) { + if (UNIV_UNLIKELY(direction != prebuilt->fetch_direction)) { + if (UNIV_UNLIKELY(prebuilt->n_fetch_cached > 0)) { ut_error; /* TODO: scrollable cursor: restore cursor to the place of the latest returned row, @@ -2987,15 +3214,14 @@ row_search_for_mysql( prebuilt->n_fetch_cached = 0; prebuilt->fetch_cache_first = 0; - } else if (prebuilt->n_fetch_cached > 0) { + } else if (UNIV_LIKELY(prebuilt->n_fetch_cached > 0)) { row_sel_pop_cached_row_for_mysql(buf, prebuilt); prebuilt->n_rows_fetched++; srv_n_rows_read++; - trx->op_info = ""; - - return(DB_SUCCESS); + err = DB_SUCCESS; + goto func_exit; } if (prebuilt->fetch_cache_first > 0 @@ -3004,9 +3230,9 @@ row_search_for_mysql( /* The previous returned row was popped from the fetch cache, but the cache was not full at the time of the popping: no more rows can exist in the result set */ - - trx->op_info = ""; - return(DB_RECORD_NOT_FOUND); + + err = DB_RECORD_NOT_FOUND; + goto func_exit; } prebuilt->n_rows_fetched++; @@ -3048,10 +3274,11 @@ row_search_for_mysql( 1 column. Return immediately if this is not a HANDLER command. */ - if (direction != 0 && !prebuilt->used_in_HANDLER) { + if (UNIV_UNLIKELY(direction != 0 && + !prebuilt->used_in_HANDLER)) { - trx->op_info = ""; - return(DB_RECORD_NOT_FOUND); + err = DB_RECORD_NOT_FOUND; + goto func_exit; } } @@ -3066,9 +3293,9 @@ row_search_for_mysql( cannot use the adaptive hash index in a search in the case the row may be long and there may be externally stored fields */ - if (unique_search + if (UNIV_UNLIKELY(direction == 0) + && unique_search && index->type & DICT_CLUSTERED - && direction == 0 && !prebuilt->templ_contains_blob && !prebuilt->used_in_HANDLER && (prebuilt->mysql_row_len < UNIV_PAGE_SIZE / 8)) { @@ -3100,14 +3327,15 @@ row_search_for_mysql( trx->has_search_latch = TRUE; } #endif - shortcut = row_sel_try_search_shortcut_for_mysql(&rec, - prebuilt, &mtr); - if (shortcut == SEL_FOUND) { + switch (row_sel_try_search_shortcut_for_mysql(&rec, + prebuilt, &offsets, &heap, &mtr)) { + case SEL_FOUND: #ifdef UNIV_SEARCH_DEBUG - ut_a(0 == cmp_dtuple_rec(search_tuple, rec)); + ut_a(0 == cmp_dtuple_rec(search_tuple, + rec, offsets)); #endif if (!row_sel_store_mysql_rec(buf, prebuilt, - rec)) { + rec, offsets)) { err = DB_TOO_BIG_RECORD; /* We let the main loop to do the @@ -3131,15 +3359,12 @@ row_search_for_mysql( trx->has_search_latch = FALSE; } - trx->op_info = ""; - /* NOTE that we do NOT store the cursor position */ + err = DB_SUCCESS; + goto func_exit; - return(DB_SUCCESS); - - } else if (shortcut == SEL_EXHAUSTED) { - + case SEL_EXHAUSTED: mtr_commit(&mtr); /* ut_print_name(stderr, index->name); @@ -3154,12 +3379,11 @@ row_search_for_mysql( trx->has_search_latch = FALSE; } - trx->op_info = ""; - /* NOTE that we do NOT store the cursor position */ - return(DB_RECORD_NOT_FOUND); + err = DB_RECORD_NOT_FOUND; + goto func_exit; } shortcut_fails_too_big_rec: mtr_commit(&mtr); @@ -3183,6 +3407,7 @@ shortcut_fails_too_big_rec: /* Scan the MySQL query string; check if SELECT is the first word there */ + ibool success; dict_accept(*trx->mysql_query_str, "SELECT", &success); @@ -3198,7 +3423,7 @@ shortcut_fails_too_big_rec: naturally moves upward (in fetch next) in alphabetical order, otherwise downward */ - if (direction == 0) { + if (UNIV_UNLIKELY(direction == 0)) { if (mode == PAGE_CUR_GE || mode == PAGE_CUR_G) { moves_up = TRUE; } @@ -3212,10 +3437,10 @@ shortcut_fails_too_big_rec: clust_index = dict_table_get_first_index(index->table); - if (direction != 0) { - moved = sel_restore_position_for_mysql(BTR_SEARCH_LEAF, pcur, - moves_up, &mtr); - if (!moved) { + if (UNIV_LIKELY(direction != 0)) { + if (!sel_restore_position_for_mysql(&same_user_rec, + BTR_SEARCH_LEAF, + pcur, moves_up, &mtr)) { goto next_rec; } @@ -3245,7 +3470,7 @@ shortcut_fails_too_big_rec: fputs( "InnoDB: Error: MySQL is trying to perform a consistent read\n" "InnoDB: but the read view is not assigned!\n", stderr); - trx_print(stderr, trx); + trx_print(stderr, trx, 600); fputc('\n', stderr); ut_a(0); } @@ -3256,11 +3481,13 @@ shortcut_fails_too_big_rec: trx_assign_read_view(trx); prebuilt->sql_stat_start = FALSE; } else { + ulint lock_mode; if (prebuilt->select_lock_type == LOCK_S) { - err = lock_table(0, index->table, LOCK_IS, thr); + lock_mode = LOCK_IS; } else { - err = lock_table(0, index->table, LOCK_IX, thr); + lock_mode = LOCK_IX; } + err = lock_table(0, index->table, lock_mode, thr); if (err != DB_SUCCESS) { @@ -3274,6 +3501,8 @@ rec_loop: /* PHASE 4: Look for matching records in a loop */ rec = btr_pcur_get_rec(pcur); + ut_ad(!!page_rec_is_comp(rec) == comp); +#ifdef UNIV_SEARCH_DEBUG /* fputs("Using ", stderr); dict_index_name_print(stderr, index); @@ -3281,7 +3510,9 @@ rec_loop: buf_frame_get_page_no(buf_frame_align(rec))); rec_print(rec); */ - if (rec == page_get_infimum_rec(buf_frame_align(rec))) { +#endif /* UNIV_SEARCH_DEBUG */ + + if (page_rec_is_infimum(rec)) { /* The infimum record on a page cannot be in the result set, and neither can a record lock be placed on it: we skip such @@ -3290,10 +3521,11 @@ rec_loop: goto next_rec; } - if (rec == page_get_supremum_rec(buf_frame_align(rec))) { + if (page_rec_is_supremum(rec)) { - if (prebuilt->select_lock_type != LOCK_NONE - && set_also_gap_locks) { + if (set_also_gap_locks + && !srv_locks_unsafe_for_binlog + && prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a lock on the index record */ @@ -3301,16 +3533,16 @@ rec_loop: we do not lock gaps. Supremum record is really a gap and therefore we do not set locks there. */ - if (srv_locks_unsafe_for_binlog == FALSE) { - err = sel_set_rec_lock(rec, index, - prebuilt->select_lock_type, - LOCK_ORDINARY, thr); - if (err != DB_SUCCESS) { + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); + err = sel_set_rec_lock(rec, index, offsets, + prebuilt->select_lock_type, + LOCK_ORDINARY, thr); - goto lock_wait_or_error; - } - } + if (err != DB_SUCCESS) { + goto lock_wait_or_error; + } } /* A page supremum record cannot be in the result set: skip it now that we have placed a possible lock on it */ @@ -3322,10 +3554,23 @@ rec_loop: /* Do sanity checks in case our cursor has bumped into page corruption */ - next_offs = rec_get_next_offs(rec); + if (comp) { + next_offs = rec_get_next_offs(rec, TRUE); + if (UNIV_UNLIKELY(next_offs < PAGE_NEW_SUPREMUM)) { + + goto wrong_offs; + } + } else { + next_offs = rec_get_next_offs(rec, FALSE); + if (UNIV_UNLIKELY(next_offs < PAGE_OLD_SUPREMUM)) { - if (next_offs >= UNIV_PAGE_SIZE || next_offs < PAGE_SUPREMUM) { + goto wrong_offs; + } + } + if (UNIV_UNLIKELY(next_offs >= UNIV_PAGE_SIZE - PAGE_DIR)) { + +wrong_offs: if (srv_force_recovery == 0 || moves_up == FALSE) { ut_print_timestamp(stderr); buf_page_print(buf_frame_align(rec)); @@ -3338,7 +3583,7 @@ rec_loop: fprintf(stderr, "InnoDB: Index corruption: rec offs %lu next offs %lu, page no %lu,\n" "InnoDB: ", - (ulong) (rec - buf_frame_align(rec)), + (ulong) ut_align_offset(rec, UNIV_PAGE_SIZE), (ulong) next_offs, (ulong) buf_frame_get_page_no(rec)); dict_index_name_print(stderr, trx, index); @@ -3356,7 +3601,7 @@ rec_loop: fprintf(stderr, "InnoDB: Index corruption: rec offs %lu next offs %lu, page no %lu,\n" "InnoDB: ", - (ulong) (rec - buf_frame_align(rec)), + (ulong) ut_align_offset(rec, UNIV_PAGE_SIZE), (ulong) next_offs, (ulong) buf_frame_get_page_no(rec)); dict_index_name_print(stderr, trx, index); @@ -3368,14 +3613,19 @@ rec_loop: goto next_rec; } } + /*-------------------------------------------------------------*/ - if (srv_force_recovery > 0) { - if (!rec_validate(rec) || !btr_index_rec_validate(rec, index, - FALSE)) { + /* Calculate the 'offsets' associated with 'rec' */ + + offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); + + if (UNIV_UNLIKELY(srv_force_recovery > 0)) { + if (!rec_validate(rec, offsets) + || !btr_index_rec_validate(rec, index, FALSE)) { fprintf(stderr, "InnoDB: Index corruption: rec offs %lu next offs %lu, page no %lu,\n" "InnoDB: ", - (ulong) (rec - buf_frame_align(rec)), + (ulong) ut_align_offset(rec, UNIV_PAGE_SIZE), (ulong) next_offs, (ulong) buf_frame_get_page_no(rec)); dict_index_name_print(stderr, trx, index); @@ -3386,8 +3636,6 @@ rec_loop: } } - /*-------------------------------------------------------------*/ - /* Note that we cannot trust the up_match value in the cursor at this place because we can arrive here after moving the cursor! Thus we have to recompare rec and search_tuple to determine if they @@ -3399,31 +3647,29 @@ rec_loop: /* fputs("Comparing rec and search tuple\n", stderr); */ - if (0 != cmp_dtuple_rec(search_tuple, rec)) { + if (0 != cmp_dtuple_rec(search_tuple, rec, offsets)) { - if (prebuilt->select_lock_type != LOCK_NONE - && set_also_gap_locks) { + if (set_also_gap_locks + && !srv_locks_unsafe_for_binlog + && prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a gap lock on the index record only if innodb_locks_unsafe_for_binlog option is not set */ - if (srv_locks_unsafe_for_binlog == FALSE) { - - err = sel_set_rec_lock(rec, index, + err = sel_set_rec_lock(rec, index, offsets, prebuilt->select_lock_type, LOCK_GAP, thr); - if (err != DB_SUCCESS) { - goto lock_wait_or_error; - } - } + if (err != DB_SUCCESS) { + goto lock_wait_or_error; + } } btr_pcur_store_position(pcur, &mtr); - ret = DB_RECORD_NOT_FOUND; + err = DB_RECORD_NOT_FOUND; /* ut_print_name(stderr, index->name); fputs(" record not found 3\n", stderr); */ @@ -3432,70 +3678,86 @@ rec_loop: } else if (match_mode == ROW_SEL_EXACT_PREFIX) { - if (!cmp_dtuple_is_prefix_of_rec(search_tuple, rec)) { + if (!cmp_dtuple_is_prefix_of_rec(search_tuple, rec, offsets)) { - if (prebuilt->select_lock_type != LOCK_NONE - && set_also_gap_locks) { + if (set_also_gap_locks + && !srv_locks_unsafe_for_binlog + && prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a gap lock on the index record only if innodb_locks_unsafe_for_binlog option is not set */ - if (srv_locks_unsafe_for_binlog == FALSE) { - - err = sel_set_rec_lock(rec, index, + err = sel_set_rec_lock(rec, index, offsets, prebuilt->select_lock_type, LOCK_GAP, thr); - if (err != DB_SUCCESS) { - goto lock_wait_or_error; - } - } + if (err != DB_SUCCESS) { + goto lock_wait_or_error; + } } btr_pcur_store_position(pcur, &mtr); - ret = DB_RECORD_NOT_FOUND; + err = DB_RECORD_NOT_FOUND; /* ut_print_name(stderr, index->name); fputs(" record not found 4\n", stderr); */ goto normal_return; } } - + /* We are ready to look at a possible new index entry in the result set: the cursor is now placed on a user record */ - cons_read_requires_clust_rec = FALSE; - if (prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a lock on the index record; note that delete marked records are a special case in a unique search. If there is a non-delete marked record, then it is enough to lock its existence with LOCK_REC_NOT_GAP. */ + /* If innodb_locks_unsafe_for_binlog option is used, + we lock only the record, i.e., next-key locking is + not used. */ + + ulint lock_type; + if (!set_also_gap_locks - || (unique_search && !rec_get_deleted_flag(rec))) { - err = sel_set_rec_lock(rec, index, - prebuilt->select_lock_type, - LOCK_REC_NOT_GAP, thr); + || srv_locks_unsafe_for_binlog + || (unique_search && !UNIV_UNLIKELY(rec_get_deleted_flag( + rec, comp)))) { + + goto no_gap_lock; } else { - /* If innodb_locks_unsafe_for_binlog option is used, - we lock only the record, i.e. next-key locking is - not used. */ + lock_type = LOCK_ORDINARY; + } - if (srv_locks_unsafe_for_binlog) { - err = sel_set_rec_lock(rec, index, - prebuilt->select_lock_type, - LOCK_REC_NOT_GAP, thr); - } else { - err = sel_set_rec_lock(rec, index, - prebuilt->select_lock_type, - LOCK_ORDINARY, thr); - } + /* If we are doing a 'greater or equal than a primary key + value' search from a clustered index, and we find a record + that has that exact primary key value, then there is no need + to lock the gap before the record, because no insert in the + gap can be in our search range. That is, no phantom row can + appear that way. + + An example: if col1 is the primary key, the search is WHERE + col1 >= 100, and we find a record where col1 = 100, then no + need to lock the gap before that record. */ + + if (index == clust_index + && mode == PAGE_CUR_GE + && direction == 0 + && dtuple_get_n_fields_cmp(search_tuple) + == dict_index_get_n_unique(index) + && 0 == cmp_dtuple_rec(search_tuple, rec, offsets)) { +no_gap_lock: + lock_type = LOCK_REC_NOT_GAP; } - + + err = sel_set_rec_lock(rec, index, offsets, + prebuilt->select_lock_type, + lock_type, thr); + if (err != DB_SUCCESS) { goto lock_wait_or_error; @@ -3516,13 +3778,16 @@ rec_loop: high force recovery level set, we try to avoid crashes by skipping this lookup */ - if (srv_force_recovery < 5 + if (UNIV_LIKELY(srv_force_recovery < 5) && !lock_clust_rec_cons_read_sees(rec, index, - trx->read_view)) { + offsets, trx->read_view)) { + /* The following call returns 'offsets' + associated with 'old_vers' */ err = row_sel_build_prev_vers_for_mysql( trx->read_view, clust_index, prebuilt, rec, + &offsets, &heap, &old_vers, &mtr); if (err != DB_SUCCESS) { @@ -3546,35 +3811,60 @@ rec_loop: have to look also into the clustered index: this is necessary, because we can only get the undo information via the clustered index record. */ - - cons_read_requires_clust_rec = TRUE; + + ut_ad(index != clust_index); + + goto requires_clust_rec; } } - if (rec_get_deleted_flag(rec) && !cons_read_requires_clust_rec) { + /* NOTE that at this point rec can be an old version of a clustered + index record built for a consistent read. We cannot assume after this + point that rec is on a buffer pool page. Functions like + page_rec_is_comp() cannot be used! */ - /* The record is delete-marked: we can skip it if this is - not a consistent read which might see an earlier version - of a non-clustered index record */ + if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp))) { + + /* The record is delete-marked: we can skip it */ + + if (srv_locks_unsafe_for_binlog + && prebuilt->select_lock_type != LOCK_NONE) { + + /* No need to keep a lock on a delete-marked record + if we do not want to use next-key locking. */ + + row_unlock_for_mysql(prebuilt, TRUE); + + trx_reset_new_rec_lock_info(trx); + } goto next_rec; } - /* Get the clustered index record if needed and if we did - not do the search using the clustered index */ + /* Get the clustered index record if needed, if we did not do the + search using the clustered index. */ + + if (index != clust_index && prebuilt->need_to_access_clustered) { - index_rec = rec; +requires_clust_rec: + /* We use a 'goto' to the preceding label if a consistent + read of a secondary index record requires us to look up old + versions of the associated clustered index record. */ - if (index != clust_index && (cons_read_requires_clust_rec - || prebuilt->need_to_access_clustered)) { + ut_ad(rec_offs_validate(rec, index, offsets)); /* It was a non-clustered index and we must fetch also the clustered index record */ mtr_has_extra_clust_latch = TRUE; + + /* The following call returns 'offsets' associated with + 'clust_rec'. Note that 'clust_rec' can be an old version + built for a consistent read. */ err = row_sel_get_clust_rec_for_mysql(prebuilt, index, rec, - thr, &clust_rec, &mtr); + thr, &clust_rec, + &offsets, &heap, &mtr); if (err != DB_SUCCESS) { goto lock_wait_or_error; @@ -3587,21 +3877,51 @@ rec_loop: goto next_rec; } - if (rec_get_deleted_flag(clust_rec)) { + if (UNIV_UNLIKELY(rec_get_deleted_flag(clust_rec, comp))) { /* The record is delete marked: we can skip it */ + if (srv_locks_unsafe_for_binlog + && prebuilt->select_lock_type != LOCK_NONE) { + + /* No need to keep a lock on a delete-marked + record if we do not want to use next-key + locking. */ + + row_unlock_for_mysql(prebuilt, TRUE); + + trx_reset_new_rec_lock_info(trx); + } + goto next_rec; } if (prebuilt->need_to_access_clustered) { - rec = clust_rec; + + result_rec = clust_rec; + + ut_ad(rec_offs_validate(result_rec, clust_index, + offsets)); + } else { + /* We used 'offsets' for the clust rec, recalculate + them for 'rec' */ + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); + result_rec = rec; } + } else { + result_rec = rec; } - /* We found a qualifying row */ - - if (prebuilt->n_rows_fetched >= MYSQL_FETCH_CACHE_THRESHOLD + /* We found a qualifying record 'result_rec'. At this point, + 'offsets' are associated with 'result_rec'. */ + + ut_ad(rec_offs_validate(result_rec, + result_rec != rec ? clust_index : index, + offsets)); + + if ((match_mode == ROW_SEL_EXACT + || prebuilt->n_rows_fetched >= MYSQL_FETCH_CACHE_THRESHOLD) && prebuilt->select_lock_type == LOCK_NONE && !prebuilt->templ_contains_blob && !prebuilt->clust_index_was_generated @@ -3618,8 +3938,8 @@ rec_loop: not cache rows because there the cursor is a scrollable cursor. */ - row_sel_push_cache_row_for_mysql(prebuilt, rec); - + row_sel_push_cache_row_for_mysql(prebuilt, result_rec, + offsets); if (prebuilt->n_fetch_cached == MYSQL_FETCH_CACHE_SIZE) { goto got_row; @@ -3628,11 +3948,14 @@ rec_loop: goto next_rec; } else { if (prebuilt->template_type == ROW_MYSQL_DUMMY_TEMPLATE) { - ut_memcpy(buf + 4, rec - rec_get_extra_size(rec), - rec_get_size(rec)); - mach_write_to_4(buf, rec_get_extra_size(rec) + 4); + memcpy(buf + 4, result_rec + - rec_offs_extra_size(offsets), + rec_offs_size(offsets)); + mach_write_to_4(buf, + rec_offs_extra_size(offsets) + 4); } else { - if (!row_sel_store_mysql_rec(buf, prebuilt, rec)) { + if (!row_sel_store_mysql_rec(buf, prebuilt, + result_rec, offsets)) { err = DB_TOO_BIG_RECORD; goto lock_wait_or_error; @@ -3640,20 +3963,28 @@ rec_loop: } if (prebuilt->clust_index_was_generated) { - row_sel_store_row_id_to_prebuilt(prebuilt, index_rec, - index); + if (result_rec != rec) { + offsets = rec_get_offsets( + rec, index, offsets, + ULINT_UNDEFINED, &heap); + } + row_sel_store_row_id_to_prebuilt(prebuilt, rec, + index, offsets); } } + + /* From this point on, 'offsets' are invalid. */ + got_row: /* We have an optimization to save CPU time: if this is a consistent read on a unique condition on the clustered index, then we do not store the pcur position, because any fetch next or prev will anyway - return 'end of file'. An exception is the MySQL HANDLER command - where the user can move the cursor with PREV or NEXT even after - a unique search. */ + return 'end of file'. Exceptions are locking reads and the MySQL + HANDLER command where the user can move the cursor with PREV or NEXT + even after a unique search. */ if (!unique_search_from_clust_index - || prebuilt->select_lock_type == LOCK_X + || prebuilt->select_lock_type != LOCK_NONE || prebuilt->used_in_HANDLER) { /* Inside an update always store the cursor position */ @@ -3661,15 +3992,15 @@ got_row: btr_pcur_store_position(pcur, &mtr); } - ret = DB_SUCCESS; + err = DB_SUCCESS; goto normal_return; next_rec: /*-------------------------------------------------------------*/ /* PHASE 5: Move the cursor to the next index record */ - - if (mtr_has_extra_clust_latch) { + + if (UNIV_UNLIKELY(mtr_has_extra_clust_latch)) { /* We must commit mtr if we are moving to the next non-clustered index record, because we could break the latching order if we would access a different clustered @@ -3681,34 +4012,39 @@ next_rec: mtr_has_extra_clust_latch = FALSE; mtr_start(&mtr); - moved = sel_restore_position_for_mysql(BTR_SEARCH_LEAF, pcur, - moves_up, &mtr); - if (moved) { + if (sel_restore_position_for_mysql(&same_user_rec, + BTR_SEARCH_LEAF, + pcur, moves_up, &mtr)) { +#ifdef UNIV_SEARCH_DEBUG cnt++; +#endif /* UNIV_SEARCH_DEBUG */ goto rec_loop; } } if (moves_up) { - moved = btr_pcur_move_to_next(pcur, &mtr); - } else { - moved = btr_pcur_move_to_prev(pcur, &mtr); - } + if (UNIV_UNLIKELY(!btr_pcur_move_to_next(pcur, &mtr))) { +not_moved: + btr_pcur_store_position(pcur, &mtr); - if (!moved) { - btr_pcur_store_position(pcur, &mtr); + if (match_mode != 0) { + err = DB_RECORD_NOT_FOUND; + } else { + err = DB_END_OF_INDEX; + } - if (match_mode != 0) { - ret = DB_RECORD_NOT_FOUND; - } else { - ret = DB_END_OF_INDEX; + goto normal_return; + } + } else { + if (UNIV_UNLIKELY(!btr_pcur_move_to_prev(pcur, &mtr))) { + goto not_moved; } - - goto normal_return; } +#ifdef UNIV_SEARCH_DEBUG cnt++; +#endif /* UNIV_SEARCH_DEBUG */ goto rec_loop; @@ -3726,24 +4062,50 @@ lock_wait_or_error: que_thr_stop_for_mysql(thr); - was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL); - - if (was_lock_wait) { + thr->lock_state = QUE_THR_LOCK_ROW; + + if (row_mysql_handle_errors(&err, trx, thr, NULL)) { + /* It was a lock wait, and it ended */ + + thr->lock_state = QUE_THR_LOCK_NOLOCK; mtr_start(&mtr); - sel_restore_position_for_mysql(BTR_SEARCH_LEAF, pcur, - moves_up, &mtr); + sel_restore_position_for_mysql(&same_user_rec, + BTR_SEARCH_LEAF, pcur, + moves_up, &mtr); + if (srv_locks_unsafe_for_binlog && !same_user_rec) { + /* Since we were not able to restore the cursor + on the same user record, we cannot use + row_unlock_for_mysql() to unlock any records, and + we must thus reset the new rec lock info. Since + in lock0lock.c we have blocked the inheriting of gap + X-locks, we actually do not have any new record locks + set in this case. + + Note that if we were able to restore on the 'same' + user record, it is still possible that we were actually + waiting on a delete-marked record, and meanwhile + it was removed by purge and inserted again by some + other user. But that is no problem, because in + rec_loop we will again try to set a lock, and + new_rec_lock_info in trx will be right at the end. */ + + trx_reset_new_rec_lock_info(trx); + } + mode = pcur->search_mode; goto rec_loop; } + thr->lock_state = QUE_THR_LOCK_NOLOCK; + +#ifdef UNIV_SEARCH_DEBUG /* fputs("Using ", stderr); dict_index_name_print(stderr, index); fprintf(stderr, " cnt %lu ret value %lu err\n", cnt, err); */ - trx->op_info = ""; - - return(err); +#endif /* UNIV_SEARCH_DEBUG */ + goto func_exit; normal_return: /*-------------------------------------------------------------*/ @@ -3754,19 +4116,24 @@ normal_return: if (prebuilt->n_fetch_cached > 0) { row_sel_pop_cached_row_for_mysql(buf, prebuilt); - ret = DB_SUCCESS; + err = DB_SUCCESS; } +#ifdef UNIV_SEARCH_DEBUG /* fputs("Using ", stderr); dict_index_name_print(stderr, index); fprintf(stderr, " cnt %lu ret value %lu err\n", cnt, err); */ - if (ret == DB_SUCCESS) { +#endif /* UNIV_SEARCH_DEBUG */ + if (err == DB_SUCCESS) { srv_n_rows_read++; } +func_exit: trx->op_info = ""; - - return(ret); + if (UNIV_LIKELY_NULL(heap)) { + mem_heap_free(heap); + } + return(err); } /*********************************************************************** @@ -3815,7 +4182,8 @@ row_search_check_if_query_cache_permitted( && !trx->read_view) { trx->read_view = read_view_open_now(trx, - trx->read_view_heap); + trx->global_read_view_heap); + trx->global_read_view = trx->read_view; } } |