summaryrefslogtreecommitdiff
path: root/storage/xtradb/row/row0sel.cc
diff options
context:
space:
mode:
authorThirunarayanan Balathandayuthapani <thiru@mariadb.com>2018-03-20 17:51:57 +0530
committerThirunarayanan Balathandayuthapani <thiru@mariadb.com>2018-03-20 17:53:33 +0530
commiteee73ddfbb29816320c9fc78c8ff1012cac6567a (patch)
tree35d39750a085733e62ca3fb57ccdb11ccafdd7c7 /storage/xtradb/row/row0sel.cc
parentbc2e7d7889e35f30390d1ef8653f6ac9c038b5b2 (diff)
downloadmariadb-git-eee73ddfbb29816320c9fc78c8ff1012cac6567a.tar.gz
MDEV-12255 innodb_prefix_index_cluster_optimization hits debug build
assert on UTF-8 columns Problem: ======= (1) Multi-byte character cases are not considered during prefix index cluster optimization check. It leads to fetch of improper results during read operation. (2) Strict assert in row_sel_field_store_in_mysql_format_func and it asserts for prefix index record to mysql conversion. Solution: ======== (1) Consider the case of multi-byte character during prefix index cluster optimization check. (2) Relax the assert in row_sel_field_store_in_mysql_format_func to allow prefix index record to mysql format conversion. The patch is taken from https://github.com/laurynas-biveinis/percona-server/commit/1eee538087ffcf121c37f844b447ba5480faf081
Diffstat (limited to 'storage/xtradb/row/row0sel.cc')
-rw-r--r--storage/xtradb/row/row0sel.cc180
1 files changed, 115 insertions, 65 deletions
diff --git a/storage/xtradb/row/row0sel.cc b/storage/xtradb/row/row0sel.cc
index b81ea60a413..97007c1107c 100644
--- a/storage/xtradb/row/row0sel.cc
+++ b/storage/xtradb/row/row0sel.cc
@@ -3685,6 +3685,117 @@ row_search_idx_cond_check(
return(result);
}
+/** Return the record field length in characters.
+@param[in] col table column of the field
+@param[in] field_no field number
+@param[in] rec physical record
+@param[in] offsets field offsets in the physical record
+@return field length in characters. */
+static
+size_t
+rec_field_len_in_chars(
+ const dict_col_t* col,
+ const ulint field_no,
+ const rec_t* rec,
+ const ulint* offsets)
+{
+ const ulint cset = dtype_get_charset_coll(col->prtype);
+ const CHARSET_INFO* cs = all_charsets[cset];
+ ulint rec_field_len;
+ const char* rec_field = reinterpret_cast<const char *>(
+ rec_get_nth_field(
+ rec, offsets, field_no, &rec_field_len));
+
+ if (UNIV_UNLIKELY(!cs)) {
+ ib_logf(IB_LOG_LEVEL_WARN, "Missing collation " ULINTPF, cset);
+ return SIZE_T_MAX;
+ }
+
+ return(cs->cset->numchars(cs, rec_field, rec_field + rec_field_len));
+}
+
+
+/** Avoid the clustered index lookup if all the following conditions
+are true:
+1) all columns are in secondary index
+2) all values for columns that are prefix-only indexes are shorter
+than the prefix size. This optimization can avoid many IOs for certain schemas.
+@return true, to avoid clustered index lookup. */
+static
+bool row_search_with_covering_prefix(
+ row_prebuilt_t* prebuilt,
+ const rec_t* rec,
+ const ulint* offsets)
+{
+ const dict_index_t* index = prebuilt->index;
+ ut_ad(!dict_index_is_clust(index));
+
+ if (!srv_prefix_index_cluster_optimization) {
+ return false;
+ }
+
+ /** Optimization only applicable if the number of secondary index
+ fields are greater than or equal to number of clustered index fields. */
+ if (prebuilt->n_template > index->n_fields) {
+ return false;
+ }
+
+ for (ulint i = 0; i < prebuilt->n_template; i++) {
+ mysql_row_templ_t* templ = prebuilt->mysql_template + i;
+ ulint j = templ->rec_prefix_field_no;
+
+ /** Condition (1) : is the field in the index. */
+ if (j == ULINT_UNDEFINED) {
+ return false;
+ }
+
+ /** Condition (2): If this is a prefix index then
+ row's value size shorter than prefix length. */
+
+ if (!templ->rec_field_is_prefix) {
+ continue;
+ }
+
+ ulint rec_size = rec_offs_nth_size(offsets, j);
+ const dict_field_t* field = dict_index_get_nth_field(index, j);
+ ulint max_chars = field->prefix_len / templ->mbmaxlen;
+
+ ut_a(field->prefix_len > 0);
+
+ if (rec_size < max_chars) {
+ /* Record in bytes shorter than the index
+ prefix length in char. */
+ continue;
+ }
+
+ if (rec_size * templ->mbminlen >= field->prefix_len) {
+ /* Shortest representation string by the
+ byte length of the record is longer than the
+ maximum possible index prefix. */
+ return false;
+ }
+
+
+ size_t num_chars = rec_field_len_in_chars(
+ field->col, j, rec, offsets);
+
+ if (num_chars >= max_chars) {
+ /* No of chars to store the record exceeds
+ the index prefix character length. */
+ return false;
+ }
+ }
+
+ for (ulint i = 0; i < prebuilt->n_template; i++) {
+ mysql_row_templ_t* templ = prebuilt->mysql_template + i;
+ templ->rec_field_no = templ->rec_prefix_field_no;
+ ut_a(templ->rec_field_no != ULINT_UNDEFINED);
+ }
+
+ srv_stats.n_sec_rec_cluster_reads_avoided.inc();
+ return true;
+}
+
/********************************************************************//**
Searches for rows in the database. This is used in the interface to
MySQL. This function opens a cursor, and also implements fetch next
@@ -3748,7 +3859,6 @@ row_search_for_mysql(
ulint* offsets = offsets_;
ibool table_lock_waited = FALSE;
byte* next_buf = 0;
- bool use_clustered_index = false;
rec_offs_init(offsets_);
@@ -4810,71 +4920,10 @@ locks_ok:
break;
}
- /* Get the clustered index record if needed, if we did not do the
- search using the clustered index... */
-
- use_clustered_index =
- (index != clust_index && prebuilt->need_to_access_clustered);
-
- if (use_clustered_index && srv_prefix_index_cluster_optimization
- && prebuilt->n_template <= index->n_fields) {
- /* ...but, perhaps avoid the clustered index lookup if
- all of the following are true:
- 1) all columns are in the secondary index
- 2) all values for columns that are prefix-only
- indexes are shorter than the prefix size
- This optimization can avoid many IOs for certain schemas.
- */
- bool row_contains_all_values = true;
- unsigned int i;
- for (i = 0; i < prebuilt->n_template; i++) {
- /* Condition (1) from above: is the field in the
- index (prefix or not)? */
- const mysql_row_templ_t* templ =
- prebuilt->mysql_template + i;
- ulint secondary_index_field_no =
- templ->rec_prefix_field_no;
- if (secondary_index_field_no == ULINT_UNDEFINED) {
- row_contains_all_values = false;
- break;
- }
- /* Condition (2) from above: if this is a
- prefix, is this row's value size shorter
- than the prefix? */
- if (templ->rec_field_is_prefix) {
- ulint record_size = rec_offs_nth_size(
- offsets,
- secondary_index_field_no);
- const dict_field_t *field =
- dict_index_get_nth_field(
- index,
- secondary_index_field_no);
- ut_a(field->prefix_len > 0);
- if (record_size >= field->prefix_len
- / templ->mbmaxlen) {
- row_contains_all_values = false;
- break;
- }
- }
- }
- /* If (1) and (2) were true for all columns above, use
- rec_prefix_field_no instead of rec_field_no, and skip
- the clustered lookup below. */
- if (row_contains_all_values) {
- for (i = 0; i < prebuilt->n_template; i++) {
- mysql_row_templ_t* templ =
- prebuilt->mysql_template + i;
- templ->rec_field_no =
- templ->rec_prefix_field_no;
- ut_a(templ->rec_field_no != ULINT_UNDEFINED);
- }
- use_clustered_index = false;
- srv_stats.n_sec_rec_cluster_reads_avoided.inc();
+ if (index != clust_index && prebuilt->need_to_access_clustered) {
+ if (row_search_with_covering_prefix(prebuilt, rec, offsets)) {
+ goto use_covering_index;
}
- }
-
- if (use_clustered_index) {
-
requires_clust_rec:
ut_ad(index != clust_index);
/* We use a 'goto' to the preceding label if a consistent
@@ -4960,6 +5009,7 @@ requires_clust_rec:
}
}
} else {
+use_covering_index:
result_rec = rec;
}