summaryrefslogtreecommitdiff
path: root/innobase/row/row0sel.c
diff options
context:
space:
mode:
Diffstat (limited to 'innobase/row/row0sel.c')
-rw-r--r--innobase/row/row0sel.c1222
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;
}
}