#!!! notice !!!
# Any small change to this file in the main branch
# should be done or reviewed by the maintainer!
-diff -ruN a/storage/innobase/btr/btr0cur.c b/storage/innobase/btr/btr0cur.c
---- a/storage/innobase/btr/btr0cur.c 2010-12-03 15:49:59.165212710 +0900
-+++ b/storage/innobase/btr/btr0cur.c 2010-12-03 17:19:24.834126874 +0900
-@@ -1010,6 +1010,107 @@
- }
- }
-
-+/**********************************************************************//**
-+Positions a cursor at a randomly chosen position within a B-tree
-+after the given path
-+@return TRUE if the position is at the first page, and cursor must point
-+ the first record for used by the caller.*/
-+UNIV_INTERN
-+ibool
-+btr_cur_open_at_rnd_pos_after_path(
-+/*====================*/
-+ dict_index_t* index, /*!< in: index */
-+ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */
-+ btr_path_t* first_rec_path,
-+ btr_cur_t* cursor, /*!< in/out: B-tree cursor */
-+ mtr_t* mtr) /*!< in: mtr */
-+{
-+ page_cur_t* page_cursor;
-+ btr_path_t* slot;
-+ ibool is_first_rec = TRUE;
-+ ulint page_no;
-+ ulint space;
-+ ulint zip_size;
-+ ulint height;
-+ rec_t* node_ptr;
-+ mem_heap_t* heap = NULL;
-+ ulint offsets_[REC_OFFS_NORMAL_SIZE];
-+ ulint* offsets = offsets_;
-+ rec_offs_init(offsets_);
-+
-+ if (latch_mode == BTR_MODIFY_TREE) {
-+ mtr_x_lock(dict_index_get_lock(index), mtr);
-+ } else {
-+ mtr_s_lock(dict_index_get_lock(index), mtr);
-+ }
-+
-+ page_cursor = btr_cur_get_page_cur(cursor);
-+ cursor->index = index;
-+
-+ space = dict_index_get_space(index);
-+ zip_size = dict_table_zip_size(index->table);
-+ page_no = dict_index_get_page(index);
-+
-+ height = ULINT_UNDEFINED;
-+ slot = first_rec_path;
-+
-+ for (;;) {
-+ buf_block_t* block;
-+ page_t* page;
-+
-+ block = buf_page_get_gen(space, zip_size, page_no,
-+ RW_NO_LATCH, NULL, BUF_GET,
-+ __FILE__, __LINE__, mtr);
-+ page = buf_block_get_frame(block);
-+ ut_ad(index->id == btr_page_get_index_id(page));
-+
-+ if (height == ULINT_UNDEFINED) {
-+ /* We are in the root node */
-+
-+ height = btr_page_get_level(page, mtr);
-+ }
-+
-+ if (height == 0) {
-+ btr_cur_latch_leaves(page, space, zip_size, page_no,
-+ latch_mode, cursor, mtr);
-+ }
-+
-+ if (is_first_rec && slot->nth_rec != ULINT_UNDEFINED) {
-+ if (height == 0) {
-+ /* must open the first rec */
-+ page_cur_open_on_nth_user_rec(block, page_cursor, slot->nth_rec);
-+ } else {
-+ is_first_rec = page_cur_open_on_rnd_user_rec_after_nth(block,
-+ page_cursor, slot->nth_rec);
-+ }
-+ } else {
-+ is_first_rec = FALSE;
-+ page_cur_open_on_rnd_user_rec(block, page_cursor);
-+ }
-+
-+ if (height == 0) {
-+ break;
-+ }
-+
-+ ut_ad(height > 0);
-+
-+ height--;
-+ slot++;
-+
-+ node_ptr = page_cur_get_rec(page_cursor);
-+ offsets = rec_get_offsets(node_ptr, cursor->index, offsets,
-+ ULINT_UNDEFINED, &heap);
-+ /* Go to the child node */
-+ page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets);
-+ }
-+
-+ if (UNIV_LIKELY_NULL(heap)) {
-+ mem_heap_free(heap);
-+ }
-+
-+ return (is_first_rec);
-+}
-+
- /*==================== B-TREE INSERT =========================*/
-
- /*************************************************************//**
-@@ -3479,6 +3580,154 @@
- }
-
- /*******************************************************************//**
-+Estimates the number of pages which have not null value of the key of n_cols.
-+@return estimated number of pages */
-+UNIV_INTERN
-+ulint
-+btr_estimate_n_pages_not_null(
-+/*=========================*/
-+ dict_index_t* index, /*!< in: index */
-+ ulint n_cols, /*!< in: The cols should be not null */
-+ btr_path_t* path1) /*!< in: path1[BTR_PATH_ARRAY_N_SLOTS] */
-+{
-+ dtuple_t* tuple1;
-+ btr_path_t path2[BTR_PATH_ARRAY_N_SLOTS];
-+ btr_cur_t cursor;
-+ btr_path_t* slot1;
-+ btr_path_t* slot2;
-+ ibool diverged;
-+ ibool diverged_lot;
-+ ulint divergence_level;
-+ ulint n_pages;
-+ ulint i;
-+ mtr_t mtr;
-+ mem_heap_t* heap;
-+
-+ heap = mem_heap_create(n_cols * sizeof(dfield_t)
-+ + sizeof(dtuple_t));
-+
-+ /* make tuple1 (NULL,NULL,,,) from n_cols */
-+ tuple1 = dtuple_create(heap, n_cols);
-+ dict_index_copy_types(tuple1, index, n_cols);
-+
-+ for (i = 0; i < n_cols; i++) {
-+ dfield_set_null(dtuple_get_nth_field(tuple1, i));
-+ }
-+
-+ mtr_start(&mtr);
-+
-+ cursor.path_arr = path1;
-+
-+ btr_cur_search_to_nth_level(index, 0, tuple1, PAGE_CUR_G,
-+ BTR_SEARCH_LEAF | BTR_ESTIMATE,
-+ &cursor, 0, __FILE__, __LINE__, &mtr);
-+
-+ mtr_commit(&mtr);
-+
-+
-+
-+ mtr_start(&mtr);
-+
-+ cursor.path_arr = path2;
-+
-+ btr_cur_open_at_index_side(FALSE, index,
-+ BTR_SEARCH_LEAF | BTR_ESTIMATE,
-+ &cursor, &mtr);
-+
-+ mtr_commit(&mtr);
-+
-+ mem_heap_free(heap);
-+
-+ /* We have the path information for the range in path1 and path2 */
-+
-+ n_pages = 1;
-+ diverged = FALSE; /* This becomes true when the path is not
-+ the same any more */
-+ diverged_lot = FALSE; /* This becomes true when the paths are
-+ not the same or adjacent any more */
-+ divergence_level = 1000000; /* This is the level where paths diverged
-+ a lot */
-+ for (i = 0; ; i++) {
-+ ut_ad(i < BTR_PATH_ARRAY_N_SLOTS);
-+
-+ slot1 = path1 + i;
-+ slot2 = path2 + i;
-+
-+ if ((slot1 + 1)->nth_rec == ULINT_UNDEFINED
-+ || (slot2 + 1)->nth_rec == ULINT_UNDEFINED) {
-+
-+ if (i > divergence_level + 1) {
-+ /* In trees whose height is > 1 our algorithm
-+ tends to underestimate: multiply the estimate
-+ by 2: */
-+
-+ n_pages = n_pages * 2;
-+ }
-+
-+ /* Do not estimate the number of rows in the range
-+ to over 1 / 2 of the estimated rows in the whole
-+ table */
-+
-+ if (n_pages > index->stat_n_leaf_pages / 2) {
-+ n_pages = index->stat_n_leaf_pages / 2;
-+
-+ /* If there are just 0 or 1 rows in the table,
-+ then we estimate all rows are in the range */
-+
-+ if (n_pages == 0) {
-+ n_pages = index->stat_n_leaf_pages;
-+ }
-+ }
-+
-+ return(n_pages);
-+ }
-+
-+ if (!diverged && slot1->nth_rec != slot2->nth_rec) {
-+
-+ diverged = TRUE;
-+
-+ if (slot1->nth_rec < slot2->nth_rec) {
-+ n_pages = slot2->nth_rec - slot1->nth_rec;
-+
-+ if (n_pages > 1) {
-+ diverged_lot = TRUE;
-+ divergence_level = i;
-+ }
-+ } else {
-+ /* Maybe the tree has changed between
-+ searches */
-+
-+ return(10);
-+ }
-+
-+ } else if (diverged && !diverged_lot) {
-+
-+ if (slot1->nth_rec < slot1->n_recs
-+ || slot2->nth_rec > 1) {
-+
-+ diverged_lot = TRUE;
-+ divergence_level = i;
-+
-+ n_pages = 0;
-+
-+ if (slot1->nth_rec < slot1->n_recs) {
-+ n_pages += slot1->n_recs
-+ - slot1->nth_rec;
-+ }
-+
-+ if (slot2->nth_rec > 1) {
-+ n_pages += slot2->nth_rec - 1;
-+ }
-+ }
-+ } else if (diverged_lot) {
-+
-+ n_pages = (n_pages * (slot1->n_recs + slot2->n_recs))
-+ / 2;
-+ }
-+ }
-+}
-+
-+/*******************************************************************//**
- Estimates the number of different key values in a given index, for
- each n-column prefix of the index where n <= dict_index_get_n_unique(index).
- The estimates are stored in the array index->stat_n_diff_key_vals. */
-@@ -3507,18 +3756,38 @@
- ulint offsets_next_rec_[REC_OFFS_NORMAL_SIZE];
- ulint* offsets_rec = offsets_rec_;
- ulint* offsets_next_rec= offsets_next_rec_;
-+ ulint stats_method = srv_stats_method;
-+ btr_path_t first_rec_path[BTR_PATH_ARRAY_N_SLOTS];
-+ ulint effective_pages; /* effective leaf pages */
- rec_offs_init(offsets_rec_);
- rec_offs_init(offsets_next_rec_);
-
- n_cols = dict_index_get_n_unique(index);
-
-+ if (stats_method == SRV_STATS_METHOD_IGNORE_NULLS) {
-+ /* estimate effective pages and path for the first effective record */
-+ /* TODO: make it work also for n_cols > 1. */
-+ effective_pages = btr_estimate_n_pages_not_null(index, 1 /*k*/, first_rec_path);
-+
-+ if (!effective_pages) {
-+ for (j = 0; j <= n_cols; j++) {
-+ index->stat_n_diff_key_vals[j] = (ib_int64_t)index->stat_n_leaf_pages;
-+ }
-+ return;
-+ } else if (effective_pages > index->stat_n_leaf_pages) {
-+ effective_pages = index->stat_n_leaf_pages;
-+ }
-+ } else {
-+ effective_pages = index->stat_n_leaf_pages;
-+ }
-+
- n_diff = mem_zalloc((n_cols + 1) * sizeof(ib_int64_t));
-
- /* It makes no sense to test more pages than are contained
- in the index, thus we lower the number if it is too high */
-- if (srv_stats_sample_pages > index->stat_index_size) {
-- if (index->stat_index_size > 0) {
-- n_sample_pages = index->stat_index_size;
-+ if (srv_stats_sample_pages > effective_pages) {
-+ if (effective_pages > 0) {
-+ n_sample_pages = effective_pages;
- } else {
- n_sample_pages = 1;
- }
-@@ -3530,9 +3799,15 @@
-
- for (i = 0; i < n_sample_pages; i++) {
- rec_t* supremum;
-+ ibool is_first_page = TRUE;
- mtr_start(&mtr);
-
-+ if (stats_method == SRV_STATS_METHOD_IGNORE_NULLS) {
-+ is_first_page = btr_cur_open_at_rnd_pos_after_path(index, BTR_SEARCH_LEAF,
-+ first_rec_path, &cursor, &mtr);
-+ } else {
- btr_cur_open_at_rnd_pos(index, BTR_SEARCH_LEAF, &cursor, &mtr);
-+ }
-
- /* Count the number of different key values for each prefix of
- the key on this index page. If the prefix does not determine
-@@ -3543,7 +3818,13 @@
- page = btr_cur_get_page(&cursor);
-
- supremum = page_get_supremum_rec(page);
-+ if (stats_method == SRV_STATS_METHOD_IGNORE_NULLS && is_first_page) {
-+ /* the cursor should be the first record of the page. */
-+ /* Counting should be started from here. */
-+ rec = btr_cur_get_rec(&cursor);
-+ } else {
- rec = page_rec_get_next(page_get_infimum_rec(page));
-+ }
-
- if (rec != supremum) {
- not_empty_flag = 1;
-@@ -3552,7 +3833,8 @@
- }
-
- while (rec != supremum) {
-- rec_t* next_rec = page_rec_get_next(rec);
-+ rec_t* next_rec;
-+ next_rec = page_rec_get_next(rec);
- if (next_rec == supremum) {
- break;
- }
-@@ -3566,7 +3848,10 @@
- cmp_rec_rec_with_match(rec, next_rec,
- offsets_rec, offsets_next_rec,
- index, &matched_fields,
-- &matched_bytes);
-+ &matched_bytes,
-+ (stats_method==SRV_STATS_METHOD_NULLS_NOT_EQUAL) ?
-+ SRV_STATS_METHOD_NULLS_NOT_EQUAL :
-+ SRV_STATS_METHOD_NULLS_EQUAL);
-
- for (j = matched_fields + 1; j <= n_cols; j++) {
- /* We add one if this index record has
-@@ -3627,7 +3912,7 @@
- for (j = 0; j <= n_cols; j++) {
- index->stat_n_diff_key_vals[j]
- = ((n_diff[j]
-- * (ib_int64_t)index->stat_n_leaf_pages
-+ * (ib_int64_t)effective_pages
- + n_sample_pages - 1
- + total_external_size
- + not_empty_flag)
-@@ -3642,7 +3927,7 @@
- different key values, or even more. Let us try to approximate
- that: */
-
-- add_on = index->stat_n_leaf_pages
-+ add_on = effective_pages
- / (10 * (n_sample_pages
- + total_external_size));
-
-@@ -3651,6 +3936,15 @@
- }
-
- index->stat_n_diff_key_vals[j] += add_on;
-+
-+ if (stats_method == SRV_STATS_METHOD_IGNORE_NULLS) {
-+ /* index->stat_n_diff_key_vals[k] is used for calc rec_per_key,
-+ as "stats.records / index->stat_n_diff_key_vals[x]".
-+ So it should be adjusted to the value which is based on whole of the index. */
-+ index->stat_n_diff_key_vals[j] =
-+ index->stat_n_diff_key_vals[j] * (ib_int64_t)index->stat_n_leaf_pages
-+ / (ib_int64_t)effective_pages;
-+ }
- }
-
- mem_free(n_diff);
-diff -ruN a/storage/innobase/dict/dict0boot.c b/storage/innobase/dict/dict0boot.c
---- a/storage/innobase/dict/dict0boot.c 2010-12-03 15:48:03.034036843 +0900
-+++ b/storage/innobase/dict/dict0boot.c 2010-12-03 17:19:24.835112632 +0900
+--- a/storage/innobase/dict/dict0boot.c
++++ b/storage/innobase/dict/dict0boot.c
@@ -266,6 +266,29 @@
/* Get the dictionary header */
dict_hdr = dict_hdr_get(&mtr);
index = dict_mem_index_create("SYS_FIELDS", "CLUST_IND",
DICT_HDR_SPACE,
-@@ -442,6 +465,41 @@
+@@ -442,6 +465,45 @@
FALSE);
ut_a(error == DB_SUCCESS);
+ /*-------------------------*/
-+ table = dict_mem_table_create("SYS_STATS", DICT_HDR_SPACE, 3, 0);
++ table = dict_mem_table_create("SYS_STATS", DICT_HDR_SPACE, 4, 0);
+ table->n_mysql_handles_opened = 1; /* for pin */
+
+ dict_mem_table_add_col(table, heap, "INDEX_ID", DATA_BINARY, 0, 0);
+ dict_mem_table_add_col(table, heap, "KEY_COLS", DATA_INT, 0, 4);
+ dict_mem_table_add_col(table, heap, "DIFF_VALS", DATA_BINARY, 0, 0);
++ dict_mem_table_add_col(table, heap, "NON_NULL_VALS", DATA_BINARY, 0, 0);
+
+ /* The '+ 2' below comes from the fields DB_TRX_ID, DB_ROLL_PTR */
+#if DICT_SYS_STATS_DIFF_VALS_FIELD != 2 + 2
+#error "DICT_SYS_STATS_DIFF_VALS_FIELD != 2 + 2"
+#endif
++#if DICT_SYS_STATS_NON_NULL_VALS_FIELD != 3 + 2
++#error "DICT_SYS_STATS_NON_NULL_VALS_FIELD != 3 + 2"
++#endif
+
+ table->id = DICT_STATS_ID;
+ dict_table_add_to_cache(table, heap);
mtr_commit(&mtr);
/*-------------------------*/
-@@ -455,6 +513,7 @@
+@@ -455,6 +517,7 @@
dict_load_sys_table(dict_sys->sys_columns);
dict_load_sys_table(dict_sys->sys_indexes);
dict_load_sys_table(dict_sys->sys_fields);
mutex_exit(&(dict_sys->mutex));
}
-diff -ruN a/storage/innobase/dict/dict0crea.c b/storage/innobase/dict/dict0crea.c
---- a/storage/innobase/dict/dict0crea.c 2010-12-03 15:48:03.036081059 +0900
-+++ b/storage/innobase/dict/dict0crea.c 2010-12-03 17:19:24.836964976 +0900
-@@ -508,6 +508,51 @@
+--- a/storage/innobase/dict/dict0crea.c
++++ b/storage/innobase/dict/dict0crea.c
+@@ -508,6 +508,56 @@
}
/*****************************************************************//**
+
+ sys_stats = dict_sys->sys_stats;
+
-+ entry = dtuple_create(heap, 3 + DATA_N_SYS_COLS);
++ entry = dtuple_create(heap, 4 + DATA_N_SYS_COLS);
+
+ dict_table_copy_types(entry, sys_stats);
+
+ ptr = mem_heap_alloc(heap, 8);
+ mach_write_to_8(ptr, 0); /* initial value is 0 */
+ dfield_set_data(dfield, ptr, 8);
++ /* 5: NON_NULL_VALS ------------------*/
++ dfield = dtuple_get_nth_field(entry, 3/*NON_NULL_VALS*/);
++ ptr = mem_heap_alloc(heap, 8);
++ mach_write_to_8(ptr, 0); /* initial value is 0 */
++ dfield_set_data(dfield, ptr, 8);
+
+ return(entry);
+}
Creates the tuple with which the index entry is searched for writing the index
tree root page number, if such a tree is created.
@return the tuple for search */
-@@ -617,6 +662,27 @@
+@@ -617,6 +667,27 @@
}
/***************************************************************//**
Creates an index tree for the index if it is not a member of a cluster.
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
static
-@@ -937,6 +1003,49 @@
+@@ -936,6 +1007,49 @@
dict_sys->sys_fields, heap);
node->field_def->common.parent = node;
node->commit_node = commit_node_create(heap);
node->commit_node->common.parent = node;
-@@ -1087,6 +1196,7 @@
+@@ -1086,6 +1200,7 @@
node->state = INDEX_BUILD_FIELD_DEF;
node->field_no = 0;
thr->run_node = node->ind_def;
-@@ -1132,7 +1242,31 @@
+@@ -1131,7 +1246,31 @@
goto function_exit;
}
}
if (node->state == INDEX_CREATE_INDEX_TREE) {
-@@ -1178,6 +1312,66 @@
+@@ -1177,6 +1316,66 @@
return(NULL);
}
thr->run_node = que_node_get_parent(node);
return(thr);
-diff -ruN a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c
---- a/storage/innobase/dict/dict0dict.c 2010-12-03 15:48:03.040222428 +0900
-+++ b/storage/innobase/dict/dict0dict.c 2010-12-03 17:19:24.841947690 +0900
-@@ -756,7 +756,7 @@
+--- a/storage/innobase/dict/dict0dict.c
++++ b/storage/innobase/dict/dict0dict.c
+@@ -755,7 +755,7 @@
print an error message and return without doing
anything. */
dict_update_statistics(table, TRUE /* only update stats
}
return(table);
-@@ -4304,6 +4304,240 @@
+@@ -4354,6 +4354,295 @@
}
/*********************************************************************//**
+ ulint key_cols;
+ ulint n_cols;
+ const rec_t* rec;
++ ulint n_fields;
+ const byte* field;
+ ulint len;
+ ib_int64_t* stat_n_diff_key_vals_tmp;
++ ib_int64_t* stat_n_non_null_key_vals_tmp;
+ byte* buf;
+ ulint i;
+ mtr_t mtr;
+
+ n_cols = dict_index_get_n_unique(index);
+ stat_n_diff_key_vals_tmp = mem_heap_zalloc(heap, (n_cols + 1) * sizeof(ib_int64_t));
++ stat_n_non_null_key_vals_tmp = mem_heap_zalloc(heap, (n_cols + 1) * sizeof(ib_int64_t));
+
+ sys_stats = dict_sys->sys_stats;
+ sys_index = UT_LIST_GET_FIRST(sys_stats->indexes);
+ }
+
+ if (rec_get_deleted_flag(rec, 0)) {
++ /* don't count */
++ i--;
+ goto next_rec;
+ }
+
++ n_fields = rec_get_n_fields_old(rec);
++
+ field = rec_get_nth_field_old(rec, 1, &len);
+ ut_a(len == 4);
+
+ ut_a(len == 8);
+
+ stat_n_diff_key_vals_tmp[i] = mach_read_from_8(field);
++
++ if (n_fields > DICT_SYS_STATS_NON_NULL_VALS_FIELD) {
++ field = rec_get_nth_field_old(rec, DICT_SYS_STATS_NON_NULL_VALS_FIELD, &len);
++ ut_a(len == 8);
++
++ stat_n_non_null_key_vals_tmp[i] = mach_read_from_8(field);
++ } else {
++ /* not enough fields: should be older */
++ fprintf(stderr, "InnoDB: Notice: stats for %s/%s (%lu/%lu)"
++ " in SYS_STATS seems older format. "
++ "Please execute ANALYZE TABLE for it.\n",
++ index->table_name, index->name, i, n_cols);
++
++ stat_n_non_null_key_vals_tmp[i] = ((ib_int64_t)(-1));
++ }
+next_rec:
+ btr_pcur_move_to_next_user_rec(&pcur, &mtr);
+ }
+
+ for (i = 0; i <= n_cols; i++) {
+ index->stat_n_diff_key_vals[i] = stat_n_diff_key_vals_tmp[i];
++ if (stat_n_non_null_key_vals_tmp[i] == ((ib_int64_t)(-1))) {
++ /* approximate value */
++ index->stat_n_non_null_key_vals[i] = stat_n_diff_key_vals_tmp[n_cols];
++ } else {
++ index->stat_n_non_null_key_vals[i] = stat_n_non_null_key_vals_tmp[i];
++ }
+ }
+}
+/*===========================================*/
+ ulint n_cols;
+ ulint rests;
+ const rec_t* rec;
++ ulint n_fields;
+ const byte* field;
+ ulint len;
+ ib_int64_t* stat_n_diff_key_vals_tmp;
++ ib_int64_t* stat_n_non_null_key_vals_tmp;
+ byte* buf;
+ ulint i;
+ mtr_t mtr;
+
+ n_cols = dict_index_get_n_unique(index);
+ stat_n_diff_key_vals_tmp = mem_heap_zalloc(heap, (n_cols + 1) * sizeof(ib_int64_t));
++ stat_n_non_null_key_vals_tmp = mem_heap_zalloc(heap, (n_cols + 1) * sizeof(ib_int64_t));
+
+ for (i = 0; i <= n_cols; i++) {
+ stat_n_diff_key_vals_tmp[i] = index->stat_n_diff_key_vals[i];
++ stat_n_non_null_key_vals_tmp[i] = index->stat_n_non_null_key_vals[i];
+ }
+
+ sys_stats = dict_sys->sys_stats;
+ break;
+ }
+
++ btr_pcur_store_position(&pcur, &mtr);
++
+ if (rec_get_deleted_flag(rec, 0)) {
++ /* don't count */
++ i--;
++ goto next_rec;
++ }
++
++ n_fields = rec_get_n_fields_old(rec);
++
++ if (n_fields <= DICT_SYS_STATS_NON_NULL_VALS_FIELD) {
++ /* not update for the older smaller format */
++ fprintf(stderr, "InnoDB: Notice: stats for %s/%s (%lu/%lu)"
++ " in SYS_STATS seems older format. Please ANALYZE TABLE it.\n",
++ index->table_name, index->name, i, n_cols);
+ goto next_rec;
+ }
+
+
+ mlog_write_ull((byte*)field, stat_n_diff_key_vals_tmp[key_cols], &mtr);
+
++ field = rec_get_nth_field_old(rec, DICT_SYS_STATS_NON_NULL_VALS_FIELD, &len);
++ ut_a(len == 8);
++
++ mlog_write_ull((byte*)field, stat_n_non_null_key_vals_tmp[key_cols], &mtr);
++
+ rests--;
+
+next_rec:
++ mtr_commit(&mtr);
++ mtr_start(&mtr);
++ btr_pcur_restore_position(BTR_MODIFY_LEAF, &pcur, &mtr);
++
+ btr_pcur_move_to_next_user_rec(&pcur, &mtr);
+ }
+ btr_pcur_close(&pcur);
Calculates new estimates for table and index statistics. The statistics
are used in query optimization. */
UNIV_INTERN
-@@ -4311,10 +4545,11 @@
+@@ -4361,10 +4650,11 @@
dict_update_statistics(
/*===================*/
dict_table_t* table, /*!< in/out: table */
{
dict_index_t* index;
ulint sum_of_index_sizes = 0;
-@@ -4331,6 +4566,27 @@
+@@ -4381,6 +4671,27 @@
return;
}
/* Find out the sizes of the indexes and how many different values
for the key they approximately have */
-@@ -4391,6 +4647,11 @@
+@@ -4445,6 +4756,11 @@
index = dict_table_get_next_index(index);
} while (index);
index = dict_table_get_first_index(table);
table->stat_n_rows = index->stat_n_diff_key_vals[
-@@ -4485,7 +4746,8 @@
+@@ -4462,6 +4778,78 @@
+ dict_table_stats_unlock(table, RW_X_LATCH);
+ }
+
++/*********************************************************************//**
++*/
++UNIV_INTERN
++ibool
++dict_is_older_statistics(
++/*=====================*/
++ dict_index_t* index)
++{
++ mem_heap_t* heap;
++ dict_table_t* sys_stats;
++ dict_index_t* sys_index;
++ btr_pcur_t pcur;
++ dtuple_t* tuple;
++ dfield_t* dfield;
++ const rec_t* rec;
++ ulint n_fields;
++ ulint len;
++ byte* buf;
++ mtr_t mtr;
++
++ heap = mem_heap_create(100);
++
++ sys_stats = dict_sys->sys_stats;
++ sys_index = UT_LIST_GET_FIRST(sys_stats->indexes);
++ ut_a(!dict_table_is_comp(sys_stats));
++
++ tuple = dtuple_create(heap, 1);
++ dfield = dtuple_get_nth_field(tuple, 0);
++
++ buf = mem_heap_alloc(heap, 8);
++ mach_write_to_8(buf, index->id);
++
++ dfield_set_data(dfield, buf, 8);
++ dict_index_copy_types(tuple, sys_index, 1);
++
++ mtr_start(&mtr);
++
++ btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE,
++ BTR_SEARCH_LEAF, &pcur, &mtr);
++
++next_rec:
++ rec = btr_pcur_get_rec(&pcur);
++
++ if (!btr_pcur_is_on_user_rec(&pcur)
++ || mach_read_from_8(rec_get_nth_field_old(rec, 0, &len))
++ != index->id) {
++ /* not found */
++ btr_pcur_close(&pcur);
++ mtr_commit(&mtr);
++ mem_heap_free(heap);
++ /* no statistics == not older statistics */
++ return(FALSE);
++ }
++
++ if (rec_get_deleted_flag(rec, 0)) {
++ btr_pcur_move_to_next_user_rec(&pcur, &mtr);
++ goto next_rec;
++ }
++
++ n_fields = rec_get_n_fields_old(rec);
++
++ btr_pcur_close(&pcur);
++ mtr_commit(&mtr);
++ mem_heap_free(heap);
++
++ if (n_fields > DICT_SYS_STATS_NON_NULL_VALS_FIELD) {
++ return(FALSE);
++ } else {
++ return(TRUE);
++ }
++}
++
+ /**********************************************************************//**
+ Prints info of a foreign key constraint. */
+ static
+@@ -4539,7 +4927,8 @@
ut_ad(mutex_own(&(dict_sys->mutex)));
dict_table_stats_lock(table, RW_S_LATCH);
-diff -ruN a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c
---- a/storage/innobase/dict/dict0load.c 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/dict/dict0load.c 2010-12-03 17:19:24.845947460 +0900
+--- a/storage/innobase/dict/dict0load.c
++++ b/storage/innobase/dict/dict0load.c
@@ -50,7 +50,8 @@
"SYS_COLUMNS",
"SYS_FIELDS",
+ "SYS_FOREIGN_COLS",
+ "SYS_STATS"
};
- /****************************************************************//**
- Compare the name of an index column.
-@@ -343,12 +344,13 @@
+
+ /* If this flag is TRUE, then we will load the cluster index's (and tables')
+@@ -348,12 +349,13 @@
}
if ((status & DICT_TABLE_UPDATE_STATS)
}
return(NULL);
-@@ -582,6 +584,61 @@
+@@ -587,6 +589,75 @@
//#endif /* FOREIGN_NOT_USED */
/********************************************************************//**
+ const rec_t* rec, /*!< in: current SYS_STATS rec */
+ index_id_t* index_id, /*!< out: INDEX_ID */
+ ulint* key_cols, /*!< out: KEY_COLS */
-+ ib_uint64_t* diff_vals) /*!< out: DIFF_VALS */
++ ib_uint64_t* diff_vals, /*!< out: DIFF_VALS */
++ ib_uint64_t* non_null_vals) /*!< out: NON_NULL_VALS */
+{
+ ulint len;
+ const byte* field;
++ ulint n_fields;
+
+ if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, 0))) {
+ return("delete-marked record in SYS_STATS");
+ }
+
-+ if (UNIV_UNLIKELY(rec_get_n_fields_old(rec) != 5)) {
++ n_fields = rec_get_n_fields_old(rec);
++
++ if (UNIV_UNLIKELY(n_fields < 5)) {
+ return("wrong number of columns in SYS_STATS record");
+ }
+
+ }
+ *diff_vals = mach_read_from_8(field);
+
++ if (n_fields < 6) {
++ *non_null_vals = ((ib_uint64_t)(-1));
++ } else {
++ field = rec_get_nth_field_old(rec, 5/*NON_NULL_VALS*/, &len);
++ if (UNIV_UNLIKELY(len != 8)) {
++ goto err_len;
++ }
++ *non_null_vals = mach_read_from_8(field);
++ }
++
+ return(NULL);
+}
+/********************************************************************//**
Determine the flags of a table described in SYS_TABLES.
@return compressed page size in kilobytes; or 0 if the tablespace is
uncompressed, ULINT_UNDEFINED on error */
-diff -ruN a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
---- a/storage/innobase/handler/ha_innodb.cc 2010-12-03 17:17:03.665960357 +0900
-+++ b/storage/innobase/handler/ha_innodb.cc 2010-12-03 17:22:21.586939783 +0900
-@@ -187,6 +187,7 @@
- static my_bool innobase_rollback_on_timeout = FALSE;
+--- a/storage/innobase/handler/ha_innodb.cc
++++ b/storage/innobase/handler/ha_innodb.cc
+@@ -188,6 +188,7 @@
static my_bool innobase_create_status_file = FALSE;
static my_bool innobase_stats_on_metadata = TRUE;
+ static my_bool innobase_large_prefix = FALSE;
+static my_bool innobase_use_sys_stats_table = FALSE;
static char* internal_innobase_data_file_path = NULL;
-@@ -2388,6 +2389,8 @@
+@@ -2468,6 +2469,8 @@
goto error;
}
/* -------------- Log files ---------------------------*/
/* The default dir for log files is the datadir of MySQL */
-@@ -5192,6 +5195,10 @@
+@@ -5256,6 +5259,10 @@
error = row_insert_for_mysql((byte*) record, prebuilt);
/* Handle duplicate key errors */
if (auto_inc_used) {
ulint err;
-@@ -5528,6 +5535,10 @@
+@@ -5591,6 +5598,10 @@
}
}
innodb_srv_conc_exit_innodb(trx);
error = convert_error_code_to_mysql(error,
-@@ -5581,6 +5592,10 @@
+@@ -5644,6 +5655,10 @@
error = row_update_for_mysql((byte*) record, prebuilt);
innodb_srv_conc_exit_innodb(trx);
error = convert_error_code_to_mysql(
-@@ -5899,6 +5914,11 @@
+@@ -5965,6 +5980,11 @@
case DB_SUCCESS:
error = 0;
table->status = 0;
+#ifdef EXTENDED_FOR_USERSTAT
+ rows_read++;
-+ if (active_index >= 0 && active_index < MAX_KEY)
++ if (active_index < MAX_KEY)
+ index_rows_read[active_index]++;
+#endif
break;
case DB_RECORD_NOT_FOUND:
error = HA_ERR_KEY_NOT_FOUND;
-@@ -6108,6 +6128,11 @@
+@@ -6196,6 +6216,11 @@
case DB_SUCCESS:
error = 0;
table->status = 0;
+#ifdef EXTENDED_FOR_USERSTAT
+ rows_read++;
-+ if (active_index >= 0 && active_index < MAX_KEY)
++ if (active_index < MAX_KEY)
+ index_rows_read[active_index]++;
+#endif
break;
case DB_RECORD_NOT_FOUND:
error = HA_ERR_END_OF_FILE;
-@@ -7999,11 +8024,31 @@
+@@ -8149,11 +8174,35 @@
/* In sql_show we call with this flag: update
then statistics so that they are up-to-date */
+ for (index = dict_table_get_first_index(ib_table);
+ index != NULL;
+ index = dict_table_get_next_index(index)) {
++ if (dict_is_older_statistics(index)) {
++ row_delete_stats_for_mysql(index, prebuilt->trx);
++ innobase_commit_low(prebuilt->trx);
++ }
+ row_insert_stats_for_mysql(index, prebuilt->trx);
+ innobase_commit_low(prebuilt->trx);
+ }
prebuilt->trx->op_info = "returning various info to MySQL";
}
-@@ -8081,7 +8126,7 @@
+@@ -8238,7 +8287,7 @@
are asked by MySQL to avoid locking. Another reason to
avoid the call is that it uses quite a lot of CPU.
See Bug#38185. */
|| !(flag & HA_STATUS_VARIABLE_EXTRA)) {
/* We do not update delete_length if no
locking is requested so the "old" value can
-@@ -11281,6 +11326,45 @@
+@@ -11511,6 +11560,26 @@
"The number of index pages to sample when calculating statistics (default 8)",
NULL, NULL, 8, 1, ~0ULL, 0);
-+const char *innobase_stats_method_names[]=
-+{
-+ "nulls_equal",
-+ "nulls_unequal",
-+ "nulls_ignored",
-+ NullS
-+};
-+TYPELIB innobase_stats_method_typelib=
-+{
-+ array_elements(innobase_stats_method_names) - 1, "innobase_stats_method_typelib",
-+ innobase_stats_method_names, NULL
-+};
-+static MYSQL_SYSVAR_ENUM(stats_method, srv_stats_method,
-+ PLUGIN_VAR_RQCMDARG,
-+ "Specifies how InnoDB index statistics collection code should threat NULLs. "
-+ "Possible values of name are same to for 'myisam_stats_method'. "
-+ "This is startup parameter.",
-+ NULL, NULL, 0, &innobase_stats_method_typelib);
-+
+static MYSQL_SYSVAR_ULONG(stats_auto_update, srv_stats_auto_update,
+ PLUGIN_VAR_RQCMDARG,
+ "Enable/Disable InnoDB's auto update statistics of indexes. "
static MYSQL_SYSVAR_BOOL(adaptive_hash_index, btr_search_enabled,
PLUGIN_VAR_OPCMDARG,
"Enable InnoDB adaptive hash index (enabled by default). "
-@@ -11604,6 +11688,10 @@
+@@ -11883,6 +11952,9 @@
MYSQL_SYSVAR(recovery_update_relay_log),
MYSQL_SYSVAR(rollback_on_timeout),
MYSQL_SYSVAR(stats_on_metadata),
-+ MYSQL_SYSVAR(stats_method),
+ MYSQL_SYSVAR(stats_auto_update),
+ MYSQL_SYSVAR(stats_update_need_lock),
+ MYSQL_SYSVAR(use_sys_stats_table),
MYSQL_SYSVAR(stats_sample_pages),
MYSQL_SYSVAR(adaptive_hash_index),
- MYSQL_SYSVAR(replication_delay),
-@@ -11672,7 +11760,10 @@
+ MYSQL_SYSVAR(stats_method),
+@@ -11957,7 +12029,10 @@
i_s_innodb_sys_columns,
i_s_innodb_sys_fields,
i_s_innodb_sys_foreign,
mysql_declare_plugin_end;
/** @brief Initialize the default value of innodb_commit_concurrency.
-diff -ruN a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc
---- a/storage/innobase/handler/i_s.cc 2010-12-03 17:17:03.666956117 +0900
-+++ b/storage/innobase/handler/i_s.cc 2010-12-03 17:19:24.880964526 +0900
+--- a/storage/innobase/handler/i_s.cc
++++ b/storage/innobase/handler/i_s.cc
@@ -49,6 +49,7 @@
#include "trx0trx.h" /* for TRX_QUE_STATE_STR_MAX_LEN */
#include "trx0rseg.h" /* for trx_rseg_struct */
+#include "dict0dict.h" /* for dict_sys */
}
- static const char plugin_author[] = "Innobase Oy";
-@@ -3457,6 +3458,203 @@
- STRUCT_FLD(__reserved1, NULL)
+ #define OK(expr) \
+@@ -3511,6 +3512,225 @@
+ STRUCT_FLD(flags, 0UL)
};
+/* Fields of the dynamic table INFORMATION_SCHEMA.innodb_sys_stats */
+ STRUCT_FLD(old_name, ""),
+ STRUCT_FLD(open_method, SKIP_OPEN_TABLE)},
+
++#define SYS_STATS_NON_NULL_VALS 3
++ {STRUCT_FLD(field_name, "NON_NULL_VALS"),
++ STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS),
++ STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG),
++ STRUCT_FLD(value, 0),
++ STRUCT_FLD(field_flags, MY_I_S_UNSIGNED | MY_I_S_MAYBE_NULL),
++ STRUCT_FLD(old_name, ""),
++ STRUCT_FLD(open_method, SKIP_OPEN_TABLE)},
++
+ END_OF_ST_FIELD_INFO
+};
+/**********************************************************************//**
+ index_id_t index_id, /*!< in: INDEX_ID */
+ ulint key_cols, /*!< in: KEY_COLS */
+ ib_uint64_t diff_vals, /*!< in: DIFF_VALS */
++ ib_uint64_t non_null_vals, /*!< in: NON_NULL_VALS */
+ TABLE* table_to_fill) /*!< in/out: fill this table */
+{
+ Field** fields;
+
+ OK(fields[SYS_STATS_DIFF_VALS]->store(longlong(diff_vals), TRUE));
+
++ if (non_null_vals == ((ib_uint64_t)(-1))) {
++ fields[SYS_STATS_NON_NULL_VALS]->set_null();
++ } else {
++ OK(fields[SYS_STATS_NON_NULL_VALS]->store(longlong(non_null_vals), TRUE));
++ fields[SYS_STATS_NON_NULL_VALS]->set_notnull();
++ }
++
+ OK(schema_table_store_record(thd, table_to_fill));
+
+ DBUG_RETURN(0);
+ index_id_t index_id;
+ ulint key_cols;
+ ib_uint64_t diff_vals;
++ ib_uint64_t non_null_vals;
+
+ /* Extract necessary information from a SYS_FOREIGN_COLS row */
+ err_msg = dict_process_sys_stats_rec(
-+ heap, rec, &index_id, &key_cols, &diff_vals);
++ heap, rec, &index_id, &key_cols, &diff_vals, &non_null_vals);
+
+ mtr_commit(&mtr);
+ mutex_exit(&dict_sys->mutex);
+
+ if (!err_msg) {
+ i_s_dict_fill_sys_stats(
-+ thd, index_id, key_cols, diff_vals,
++ thd, index_id, key_cols, diff_vals, non_null_vals,
+ tables->table);
+ } else {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+
+ /* plugin author (for SHOW PLUGINS) */
+ /* const char* */
-+ STRUCT_FLD(author, plugin_author),
++ STRUCT_FLD(author, "Percona"),
+
+ /* general descriptive text (for SHOW PLUGINS) */
+ /* const char* */
+
+ /* reserved for dependency checking */
+ /* void* */
-+ STRUCT_FLD(__reserved1, NULL)
++ STRUCT_FLD(__reserved1, NULL),
++
++ /* flags for plugin */
++ /* unsigned long */
++ STRUCT_FLD(flags, 0UL)
+};
+
/***********************************************************************
*/
static ST_FIELD_INFO i_s_innodb_rseg_fields_info[] =
-@@ -3619,3 +3817,347 @@
- /* void* */
- STRUCT_FLD(__reserved1, NULL)
+@@ -3677,3 +3897,349 @@
+ /* unsigned long */
+ STRUCT_FLD(flags, 0UL),
};
+
+/***********************************************************************
+ STRUCT_FLD(type, MYSQL_INFORMATION_SCHEMA_PLUGIN),
+ STRUCT_FLD(info, &i_s_info),
+ STRUCT_FLD(name, "INNODB_TABLE_STATS"),
-+ STRUCT_FLD(author, plugin_author),
++ STRUCT_FLD(author, "Percona"),
+ STRUCT_FLD(descr, "InnoDB table statistics in memory"),
+ STRUCT_FLD(license, PLUGIN_LICENSE_GPL),
+ STRUCT_FLD(init, i_s_innodb_table_stats_init),
+ STRUCT_FLD(version, 0x0100 /* 1.0 */),
+ STRUCT_FLD(status_vars, NULL),
+ STRUCT_FLD(system_vars, NULL),
-+ STRUCT_FLD(__reserved1, NULL)
++ STRUCT_FLD(__reserved1, NULL),
++ STRUCT_FLD(flags, 0UL)
+};
+
+UNIV_INTERN struct st_mysql_plugin i_s_innodb_index_stats =
+ STRUCT_FLD(type, MYSQL_INFORMATION_SCHEMA_PLUGIN),
+ STRUCT_FLD(info, &i_s_info),
+ STRUCT_FLD(name, "INNODB_INDEX_STATS"),
-+ STRUCT_FLD(author, plugin_author),
++ STRUCT_FLD(author, "Percona"),
+ STRUCT_FLD(descr, "InnoDB index statistics in memory"),
+ STRUCT_FLD(license, PLUGIN_LICENSE_GPL),
+ STRUCT_FLD(init, i_s_innodb_index_stats_init),
+ STRUCT_FLD(version, 0x0100 /* 1.0 */),
+ STRUCT_FLD(status_vars, NULL),
+ STRUCT_FLD(system_vars, NULL),
-+ STRUCT_FLD(__reserved1, NULL)
++ STRUCT_FLD(__reserved1, NULL),
++ STRUCT_FLD(flags, 0UL)
+};
-diff -ruN a/storage/innobase/handler/i_s.h b/storage/innobase/handler/i_s.h
---- a/storage/innobase/handler/i_s.h 2010-12-03 17:17:03.668953884 +0900
-+++ b/storage/innobase/handler/i_s.h 2010-12-03 17:19:24.882947826 +0900
-@@ -41,5 +41,8 @@
+--- a/storage/innobase/handler/i_s.h
++++ b/storage/innobase/handler/i_s.h
+@@ -43,5 +43,8 @@
extern struct st_mysql_plugin i_s_innodb_sys_foreign;
extern struct st_mysql_plugin i_s_innodb_sys_foreign_cols;
extern struct st_mysql_plugin i_s_innodb_rseg;
+extern struct st_mysql_plugin i_s_innodb_index_stats;
#endif /* i_s_h */
-diff -ruN a/storage/innobase/include/dict0boot.h b/storage/innobase/include/dict0boot.h
---- a/storage/innobase/include/dict0boot.h 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/include/dict0boot.h 2010-12-03 17:19:24.885947372 +0900
+--- a/storage/innobase/include/dict0boot.h
++++ b/storage/innobase/include/dict0boot.h
@@ -104,6 +104,7 @@
#define DICT_COLUMNS_ID 2
#define DICT_INDEXES_ID 3
+#define DICT_HDR_XTRADB_MARK 256 /* Flag to distinguish expansion of XtraDB */
/*-------------------------------------------------------------*/
- /* The field number of the page number field in the sys_indexes table
-@@ -144,11 +148,15 @@
+ /* The field numbers in the SYS_TABLES clustered index */
+@@ -146,11 +150,16 @@
#define DICT_SYS_INDEXES_TYPE_FIELD 6
#define DICT_SYS_INDEXES_NAME_FIELD 4
+#define DICT_SYS_STATS_DIFF_VALS_FIELD 4
++#define DICT_SYS_STATS_NON_NULL_VALS_FIELD 5
+
/* When a row id which is zero modulo this number (which must be a power of
two) is assigned, the field DICT_HDR_ROW_ID on the dictionary header page is
#ifndef UNIV_NONINL
#include "dict0boot.ic"
#endif
-diff -ruN a/storage/innobase/include/dict0crea.h b/storage/innobase/include/dict0crea.h
---- a/storage/innobase/include/dict0crea.h 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/include/dict0crea.h 2010-12-03 17:19:24.886949643 +0900
+--- a/storage/innobase/include/dict0crea.h
++++ b/storage/innobase/include/dict0crea.h
@@ -53,6 +53,14 @@
dict_index_t* index, /*!< in: index to create, built as a memory data
structure */
#ifndef UNIV_NONINL
#include "dict0crea.ic"
-diff -ruN a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h
---- a/storage/innobase/include/dict0dict.h 2010-12-03 15:48:03.073024387 +0900
-+++ b/storage/innobase/include/dict0dict.h 2010-12-03 17:19:24.888965622 +0900
-@@ -1084,10 +1084,11 @@
+--- a/storage/innobase/include/dict0dict.h
++++ b/storage/innobase/include/dict0dict.h
+@@ -1126,10 +1126,18 @@
dict_update_statistics(
/*===================*/
dict_table_t* table, /*!< in/out: table */
not been initialized yet, otherwise
do nothing */
+ ibool sync);
++/*********************************************************************//**
++*/
++UNIV_INTERN
++ibool
++dict_is_older_statistics(
++/*=====================*/
++ dict_index_t* index);
/********************************************************************//**
Reserves the dictionary system mutex for MySQL. */
UNIV_INTERN
-@@ -1202,6 +1203,7 @@
+@@ -1244,6 +1252,7 @@
dict_table_t* sys_columns; /*!< SYS_COLUMNS table */
dict_table_t* sys_indexes; /*!< SYS_INDEXES table */
dict_table_t* sys_fields; /*!< SYS_FIELDS table */
};
#endif /* !UNIV_HOTBACKUP */
-diff -ruN a/storage/innobase/include/dict0load.h b/storage/innobase/include/dict0load.h
---- a/storage/innobase/include/dict0load.h 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/include/dict0load.h 2010-12-03 17:19:24.889947481 +0900
+--- a/storage/innobase/include/dict0load.h
++++ b/storage/innobase/include/dict0load.h
@@ -41,6 +41,7 @@
SYS_FIELDS,
SYS_FOREIGN,
/* This must be last item. Defines the number of system tables. */
SYS_NUM_SYSTEM_TABLES
-@@ -319,6 +320,19 @@
+@@ -327,6 +328,20 @@
const char** ref_col_name, /*!< out: referenced column name
in referenced table */
ulint* pos); /*!< out: column position */
+ const rec_t* rec, /*!< in: current SYS_STATS rec */
+ index_id_t* index_id, /*!< out: INDEX_ID */
+ ulint* key_cols, /*!< out: KEY_COLS */
-+ ib_uint64_t* diff_vals); /*!< out: DIFF_VALS */
++ ib_uint64_t* diff_vals, /*!< out: DIFF_VALS */
++ ib_uint64_t* non_null_vals); /*!< out: NON_NULL_VALS */
#ifndef UNIV_NONINL
#include "dict0load.ic"
#endif
-diff -ruN a/storage/innobase/include/page0cur.h b/storage/innobase/include/page0cur.h
---- a/storage/innobase/include/page0cur.h 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/include/page0cur.h 2010-12-03 17:19:24.891954511 +0900
-@@ -293,6 +293,22 @@
- /*==========================*/
- buf_block_t* block, /*!< in: page */
- page_cur_t* cursor);/*!< out: page cursor */
-+
-+UNIV_INTERN
-+void
-+page_cur_open_on_nth_user_rec(
-+/*==========================*/
-+ buf_block_t* block, /*!< in: page */
-+ page_cur_t* cursor, /*!< out: page cursor */
-+ ulint nth);
-+
-+UNIV_INTERN
-+ibool
-+page_cur_open_on_rnd_user_rec_after_nth(
-+/*==========================*/
-+ buf_block_t* block, /*!< in: page */
-+ page_cur_t* cursor, /*!< out: page cursor */
-+ ulint nth);
- #endif /* !UNIV_HOTBACKUP */
- /***********************************************************//**
- Parses a log record of a record insert on a page.
-diff -ruN a/storage/innobase/include/que0que.h b/storage/innobase/include/que0que.h
---- a/storage/innobase/include/que0que.h 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/include/que0que.h 2010-12-03 17:19:24.892947946 +0900
+--- a/storage/innobase/include/que0que.h
++++ b/storage/innobase/include/que0que.h
@@ -492,6 +492,8 @@
#define QUE_NODE_CALL 31
#define QUE_NODE_EXIT 32
/* Query thread states */
#define QUE_THR_RUNNING 1
#define QUE_THR_PROCEDURE_WAIT 2
-diff -ruN a/storage/innobase/include/rem0cmp.h b/storage/innobase/include/rem0cmp.h
---- a/storage/innobase/include/rem0cmp.h 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/include/rem0cmp.h 2010-12-03 17:19:24.893953395 +0900
-@@ -169,10 +169,11 @@
- matched fields; when the function returns,
- contains the value the for current
- comparison */
-- ulint* matched_bytes);/*!< in/out: number of already matched
-+ ulint* matched_bytes, /*!< in/out: number of already matched
- bytes within the first field not completely
- matched; when the function returns, contains
- the value for the current comparison */
-+ ulint stats_method);
- /*************************************************************//**
- This function is used to compare two physical records. Only the common
- first fields are compared.
-diff -ruN a/storage/innobase/include/rem0cmp.ic b/storage/innobase/include/rem0cmp.ic
---- a/storage/innobase/include/rem0cmp.ic 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/include/rem0cmp.ic 2010-12-03 17:19:24.902983425 +0900
-@@ -87,5 +87,5 @@
- ulint match_b = 0;
-
- return(cmp_rec_rec_with_match(rec1, rec2, offsets1, offsets2, index,
-- &match_f, &match_b));
-+ &match_f, &match_b, 0));
- }
-diff -ruN a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h
---- a/storage/innobase/include/row0mysql.h 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/include/row0mysql.h 2010-12-03 17:19:24.904973020 +0900
-@@ -387,6 +387,14 @@
+--- a/storage/innobase/include/row0mysql.h
++++ b/storage/innobase/include/row0mysql.h
+@@ -387,6 +387,22 @@
then checked for not being too
large. */
/*********************************************************************//**
+/*=======================*/
+ dict_index_t* index,
+ trx_t* trx);
++/*********************************************************************//**
++*/
++UNIV_INTERN
++int
++row_delete_stats_for_mysql(
++/*=======================*/
++ dict_index_t* index,
++ trx_t* trx);
+/*********************************************************************//**
Scans a table create SQL string and adds to the data dictionary
the foreign key constraints declared in the string. This function
should be called after the indexes for a table have been created.
-diff -ruN a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h
---- a/storage/innobase/include/srv0srv.h 2010-12-03 15:53:54.622036720 +0900
-+++ b/storage/innobase/include/srv0srv.h 2010-12-03 17:19:24.906953188 +0900
-@@ -209,6 +209,13 @@
+--- a/storage/innobase/include/srv0srv.h
++++ b/storage/innobase/include/srv0srv.h
+@@ -216,6 +216,9 @@
extern ibool srv_innodb_status;
extern unsigned long long srv_stats_sample_pages;
-+extern ulint srv_stats_method;
-+#define SRV_STATS_METHOD_NULLS_EQUAL 0
-+#define SRV_STATS_METHOD_NULLS_NOT_EQUAL 1
-+#define SRV_STATS_METHOD_IGNORE_NULLS 2
+extern ulint srv_stats_auto_update;
+extern ulint srv_stats_update_need_lock;
+extern ibool srv_use_sys_stats_table;
extern ibool srv_use_doublewrite_buf;
extern ibool srv_use_checksums;
-diff -ruN a/storage/innobase/page/page0cur.c b/storage/innobase/page/page0cur.c
---- a/storage/innobase/page/page0cur.c 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/page/page0cur.c 2010-12-03 17:19:24.908973357 +0900
-@@ -564,6 +564,74 @@
- } while (rnd--);
- }
-
-+UNIV_INTERN
-+void
-+page_cur_open_on_nth_user_rec(
-+/*==========================*/
-+ buf_block_t* block, /*!< in: page */
-+ page_cur_t* cursor, /*!< out: page cursor */
-+ ulint nth)
-+{
-+ ulint n_recs = page_get_n_recs(buf_block_get_frame(block));
-+
-+ page_cur_set_before_first(block, cursor);
-+
-+ if (UNIV_UNLIKELY(n_recs == 0)) {
-+
-+ return;
-+ }
-+
-+ nth--;
-+
-+ if (nth >= n_recs) {
-+ nth = n_recs - 1;
-+ }
-+
-+ do {
-+ page_cur_move_to_next(cursor);
-+ } while (nth--);
-+}
-+
-+UNIV_INTERN
-+ibool
-+page_cur_open_on_rnd_user_rec_after_nth(
-+/*==========================*/
-+ buf_block_t* block, /*!< in: page */
-+ page_cur_t* cursor, /*!< out: page cursor */
-+ ulint nth)
-+{
-+ ulint rnd;
-+ ulint n_recs = page_get_n_recs(buf_block_get_frame(block));
-+ ibool ret;
-+
-+ page_cur_set_before_first(block, cursor);
-+
-+ if (UNIV_UNLIKELY(n_recs == 0)) {
-+
-+ return (FALSE);
-+ }
-+
-+ nth--;
-+
-+ if (nth >= n_recs) {
-+ nth = n_recs - 1;
-+ }
-+
-+ rnd = (ulint) (nth + page_cur_lcg_prng() % (n_recs - nth));
-+
-+ if (rnd == nth) {
-+ ret = TRUE;
-+ } else {
-+ ret = FALSE;
-+ }
-+
-+ do {
-+ page_cur_move_to_next(cursor);
-+ } while (rnd--);
-+
-+ return (ret);
-+}
-+
- /***********************************************************//**
- Writes the log record of a record insert on a page. */
- static
-diff -ruN a/storage/innobase/que/que0que.c b/storage/innobase/que/que0que.c
---- a/storage/innobase/que/que0que.c 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/que/que0que.c 2010-12-03 17:19:24.910953422 +0900
+--- a/storage/innobase/que/que0que.c
++++ b/storage/innobase/que/que0que.c
@@ -621,11 +621,21 @@
que_graph_free_recursive(cre_ind->ind_def);
} else if (type == QUE_NODE_ROW_PRINTF) {
thr = row_printf_step(thr);
} else {
-diff -ruN a/storage/innobase/rem/rem0cmp.c b/storage/innobase/rem/rem0cmp.c
---- a/storage/innobase/rem/rem0cmp.c 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/rem/rem0cmp.c 2010-12-03 17:19:24.911953579 +0900
-@@ -866,10 +866,11 @@
- matched fields; when the function returns,
- contains the value the for current
- comparison */
-- ulint* matched_bytes) /*!< in/out: number of already matched
-+ ulint* matched_bytes, /*!< in/out: number of already matched
- bytes within the first field not completely
- matched; when the function returns, contains
- the value for the current comparison */
-+ ulint stats_method)
- {
- ulint rec1_n_fields; /* the number of fields in rec */
- ulint rec1_f_len; /* length of current field in rec */
-@@ -962,7 +963,11 @@
-
- if (rec1_f_len == rec2_f_len) {
-
-- goto next_field;
-+ if (stats_method == SRV_STATS_METHOD_NULLS_EQUAL) {
-+ goto next_field;
-+ } else {
-+ ret = -1;
-+ }
-
- } else if (rec2_f_len == UNIV_SQL_NULL) {
+--- a/storage/innobase/row/row0ins.c
++++ b/storage/innobase/row/row0ins.c
+@@ -2018,6 +2018,8 @@
+ }
-diff -ruN a/storage/innobase/row/row0merge.c b/storage/innobase/row/row0merge.c
---- a/storage/innobase/row/row0merge.c 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/row/row0merge.c 2010-12-03 17:19:24.914955391 +0900
-@@ -2020,6 +2020,8 @@
+ #ifdef UNIV_DEBUG
++ if (!srv_use_sys_stats_table
++ || index != UT_LIST_GET_FIRST(dict_sys->sys_stats->indexes))
+ {
+ page_t* page = btr_cur_get_page(&cursor);
+ rec_t* first_rec = page_rec_get_next(
+--- a/storage/innobase/row/row0merge.c
++++ b/storage/innobase/row/row0merge.c
+@@ -2019,6 +2019,8 @@
"UPDATE SYS_INDEXES SET NAME=CONCAT('"
TEMP_INDEX_PREFIX_STR "', NAME) WHERE ID = :indexid;\n"
"COMMIT WORK;\n"
/* Drop the field definitions of the index. */
"DELETE FROM SYS_FIELDS WHERE INDEX_ID = :indexid;\n"
/* Drop the index definition and the B-tree. */
-diff -ruN a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c
---- a/storage/innobase/row/row0mysql.c 2010-11-03 07:01:13.000000000 +0900
-+++ b/storage/innobase/row/row0mysql.c 2010-12-03 17:19:24.918953476 +0900
-@@ -921,6 +921,9 @@
+--- a/storage/innobase/row/row0mysql.c
++++ b/storage/innobase/row/row0mysql.c
+@@ -922,6 +922,9 @@
table->stat_modified_counter = counter + 1;
/* Calculate new statistics if 1 / 16 of table has been modified
since the last time a statistics batch was run, or if
stat_modified_counter > 2 000 000 000 (to avoid wrap-around).
-@@ -931,7 +934,7 @@
+@@ -932,7 +935,7 @@
|| ((ib_int64_t)counter > 16 + table->stat_n_rows / 16)) {
dict_update_statistics(table, FALSE /* update even if stats
}
}
-@@ -2103,6 +2106,45 @@
+@@ -2077,6 +2080,71 @@
}
/*********************************************************************//**
+ que_thr_t* thr;
+ ulint err;
+
-+ ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
++ //ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
+
+ trx->op_info = "try to insert rows to SYS_STATS";
+
+ return((int) err);
+}
+
++/*********************************************************************//**
++*/
++UNIV_INTERN
++int
++row_delete_stats_for_mysql(
++/*=============================*/
++ dict_index_t* index,
++ trx_t* trx)
++{
++ pars_info_t* info = pars_info_create();
++
++ trx->op_info = "delete rows from SYS_STATS";
++
++ trx_start_if_not_started(trx);
++ trx->error_state = DB_SUCCESS;
++
++ pars_info_add_ull_literal(info, "indexid", index->id);
++
++ return((int) que_eval_sql(info,
++ "PROCEDURE DELETE_STATISTICS_PROC () IS\n"
++ "BEGIN\n"
++ "DELETE FROM SYS_STATS WHERE INDEX_ID = :indexid;\n"
++ "END;\n"
++ , TRUE, trx));
++}
++
+/*********************************************************************//**
Scans a table create SQL string and adds to the data dictionary
the foreign key constraints declared in the string. This function
should be called after the indexes for a table have been created.
-@@ -3022,7 +3064,7 @@
+@@ -3001,7 +3069,7 @@
dict_table_autoinc_initialize(table, 1);
dict_table_autoinc_unlock(table);
dict_update_statistics(table, FALSE /* update even if stats are
trx_commit_for_mysql(trx);
-@@ -3324,6 +3366,8 @@
+@@ -3312,6 +3380,8 @@
" IF (SQL % NOTFOUND) THEN\n"
" found := 0;\n"
" ELSE\n"
" DELETE FROM SYS_FIELDS\n"
" WHERE INDEX_ID = index_id;\n"
" DELETE FROM SYS_INDEXES\n"
-diff -ruN a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c
---- a/storage/innobase/srv/srv0srv.c 2010-12-03 15:53:54.625288512 +0900
-+++ b/storage/innobase/srv/srv0srv.c 2010-12-03 17:19:24.922953561 +0900
-@@ -397,6 +397,10 @@
+--- a/storage/innobase/row/row0row.c
++++ b/storage/innobase/row/row0row.c
+@@ -364,6 +364,14 @@
+
+ rec_len = rec_offs_n_fields(offsets);
+
++ if (srv_use_sys_stats_table
++ && index == UT_LIST_GET_FIRST(dict_sys->sys_stats->indexes)) {
++ if (rec_len < dict_index_get_n_fields(index)) {
++ /* the new record should be extended */
++ rec_len = dict_index_get_n_fields(index);
++ }
++ }
++
+ entry = dtuple_create(heap, rec_len);
+
+ dtuple_set_n_fields_cmp(entry,
+@@ -375,6 +383,14 @@
+ for (i = 0; i < rec_len; i++) {
+
+ dfield = dtuple_get_nth_field(entry, i);
++
++ if (srv_use_sys_stats_table
++ && index == UT_LIST_GET_FIRST(dict_sys->sys_stats->indexes)
++ && i >= rec_offs_n_fields(offsets)) {
++ dfield_set_null(dfield);
++ continue;
++ }
++
+ field = rec_get_nth_field(rec, offsets, i, &len);
+
+ dfield_set_data(dfield, field, len);
+--- a/storage/innobase/row/row0upd.c
++++ b/storage/innobase/row/row0upd.c
+@@ -439,6 +439,12 @@
+ 0);
+ }
+
++ if (srv_use_sys_stats_table
++ && index == UT_LIST_GET_FIRST(dict_sys->sys_stats->indexes)
++ && upd_field->field_no >= rec_offs_n_fields(offsets)) {
++ return(TRUE);
++ }
++
+ old_len = rec_offs_nth_size(offsets, upd_field->field_no);
+
+ if (rec_offs_comp(offsets)
+@@ -879,6 +885,18 @@
+
+ for (i = 0; i < dtuple_get_n_fields(entry); i++) {
+
++ if (srv_use_sys_stats_table
++ && index == UT_LIST_GET_FIRST(dict_sys->sys_stats->indexes)
++ && i >= rec_offs_n_fields(offsets)) {
++ dfield = dtuple_get_nth_field(entry, i);
++
++ upd_field = upd_get_nth_field(update, n_diff);
++ dfield_copy(&(upd_field->new_val), dfield);
++ upd_field_set_field_no(upd_field, i, index, trx);
++ n_diff++;
++ goto skip_compare;
++ }
++
+ data = rec_get_nth_field(rec, offsets, i, &len);
+
+ dfield = dtuple_get_nth_field(entry, i);
+--- a/storage/innobase/srv/srv0srv.c
++++ b/storage/innobase/srv/srv0srv.c
+@@ -400,6 +400,9 @@
/* When estimating number of different key values in an index, sample
this many index pages */
UNIV_INTERN unsigned long long srv_stats_sample_pages = 8;
-+UNIV_INTERN ulint srv_stats_method = 0;
+UNIV_INTERN ulint srv_stats_auto_update = 1;
+UNIV_INTERN ulint srv_stats_update_need_lock = 1;
+UNIV_INTERN ibool srv_use_sys_stats_table = FALSE;
UNIV_INTERN ibool srv_use_doublewrite_buf = TRUE;
UNIV_INTERN ibool srv_use_checksums = TRUE;
+--- a/storage/innobase/trx/trx0rec.c
++++ b/storage/innobase/trx/trx0rec.c
+@@ -669,14 +669,27 @@
+ /* Save to the undo log the old values of the columns to be updated. */
+
+ if (update) {
++ ulint extended = 0;
++
+ if (trx_undo_left(undo_page, ptr) < 5) {
+
+ return(0);
+ }
+
+- ptr += mach_write_compressed(ptr, upd_get_n_fields(update));
++ if (srv_use_sys_stats_table
++ && index == UT_LIST_GET_FIRST(dict_sys->sys_stats->indexes)) {
++ for (i = 0; i < upd_get_n_fields(update); i++) {
++ ulint pos = upd_get_nth_field(update, i)->field_no;
++
++ if (pos >= rec_offs_n_fields(offsets)) {
++ extended++;
++ }
++ }
++ }
++
++ ptr += mach_write_compressed(ptr, upd_get_n_fields(update) - extended);
+
+- for (i = 0; i < upd_get_n_fields(update); i++) {
++ for (i = 0; i < upd_get_n_fields(update) - extended; i++) {
+
+ ulint pos = upd_get_nth_field(update, i)->field_no;
+
+--- /dev/null
++++ b/mysql-test/r/percona_innodb_use_sys_stats_table.result
+@@ -0,0 +1,3 @@
++show variables like 'innodb_use_sys_stats%';
++Variable_name Value
++innodb_use_sys_stats_table ON
+--- /dev/null
++++ b/mysql-test/t/percona_innodb_use_sys_stats_table-master.opt
+@@ -0,0 +1 @@
++--innodb_use_sys_stats_table
+--- /dev/null
++++ b/mysql-test/t/percona_innodb_use_sys_stats_table.test
+@@ -0,0 +1,2 @@
++--source include/have_innodb.inc
++show variables like 'innodb_use_sys_stats%';