#!!! notice !!!
# Any small change to this file in the main branch
# should be done or reviewed by the maintainer!
-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 @@
- return(NULL);
- }
+@@ -1183,6 +1322,66 @@
+ }
-+ thr->run_node = que_node_get_parent(node);
-+
-+ return(thr);
-+}
-+
-+/****************************************************************//**
+ /****************************************************************//**
+*/
+UNIV_INTERN
+que_thr_t*
+ 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 @@
++ thr->run_node = que_node_get_parent(node);
++
++ return(thr);
++}
++
++/****************************************************************//**
+ Creates the foreign key constraints system tables inside InnoDB
+ at database creation or database start if they are not found or are
+ not of the right form.
+--- 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);
-@@ -4310,6 +4310,240 @@
+@@ -4343,6 +4343,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
-@@ -4317,10 +4551,11 @@
+@@ -4350,10 +4639,11 @@
dict_update_statistics(
/*===================*/
dict_table_t* table, /*!< in/out: table */
{
dict_index_t* index;
ulint sum_of_index_sizes = 0;
-@@ -4337,6 +4572,27 @@
+@@ -4370,6 +4660,27 @@
return;
}
/* Find out the sizes of the indexes and how many different values
for the key they approximately have */
-@@ -4401,6 +4657,11 @@
+@@ -4434,6 +4745,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[
-@@ -4495,7 +4756,8 @@
+@@ -4451,6 +4767,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
+@@ -4528,7 +4916,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",
}
return(NULL);
-@@ -582,6 +584,61 @@
+@@ -582,6 +584,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;
-@@ -2407,6 +2408,8 @@
+@@ -2439,6 +2440,8 @@
goto error;
}
/* -------------- Log files ---------------------------*/
/* The default dir for log files is the datadir of MySQL */
-@@ -5211,6 +5214,10 @@
+@@ -5247,6 +5250,10 @@
error = row_insert_for_mysql((byte*) record, prebuilt);
/* Handle duplicate key errors */
if (auto_inc_used) {
ulint err;
-@@ -5547,6 +5554,10 @@
+@@ -5583,6 +5590,10 @@
}
}
innodb_srv_conc_exit_innodb(trx);
error = convert_error_code_to_mysql(error,
-@@ -5600,6 +5611,10 @@
+@@ -5636,6 +5647,10 @@
error = row_update_for_mysql((byte*) record, prebuilt);
innodb_srv_conc_exit_innodb(trx);
error = convert_error_code_to_mysql(
-@@ -5918,6 +5933,11 @@
+@@ -5954,6 +5969,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;
-@@ -6127,6 +6147,11 @@
+@@ -6163,6 +6183,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;
-@@ -8077,11 +8102,31 @@
+@@ -8105,11 +8130,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";
}
-@@ -8159,7 +8204,7 @@
+@@ -8187,7 +8236,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
-@@ -11354,6 +11399,26 @@
+@@ -11401,6 +11450,26 @@
"The number of index pages to sample when calculating statistics (default 8)",
NULL, NULL, 8, 1, ~0ULL, 0);
static MYSQL_SYSVAR_BOOL(adaptive_hash_index, btr_search_enabled,
PLUGIN_VAR_OPCMDARG,
"Enable InnoDB adaptive hash index (enabled by default). "
-@@ -11684,6 +11749,9 @@
+@@ -11727,6 +11796,9 @@
MYSQL_SYSVAR(recovery_update_relay_log),
MYSQL_SYSVAR(rollback_on_timeout),
MYSQL_SYSVAR(stats_on_metadata),
MYSQL_SYSVAR(stats_sample_pages),
MYSQL_SYSVAR(adaptive_hash_index),
MYSQL_SYSVAR(stats_method),
-@@ -11753,7 +11821,10 @@
+@@ -11796,7 +11868,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 @@
+ #define OK(expr) \
+@@ -3455,6 +3456,221 @@
STRUCT_FLD(__reserved1, NULL)
};
+ 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* */
/***********************************************************************
*/
static ST_FIELD_INFO i_s_innodb_rseg_fields_info[] =
-@@ -3619,3 +3817,347 @@
+@@ -3617,3 +3833,347 @@
/* void* */
STRUCT_FLD(__reserved1, NULL)
};
+ 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(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(system_vars, NULL),
+ STRUCT_FLD(__reserved1, NULL)
+};
-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
/*-------------------------------------------------------------*/
/* The field number of the page number field in the sys_indexes table
-@@ -144,11 +148,15 @@
+@@ -144,11 +148,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
+@@ -1109,10 +1109,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 @@
+@@ -1227,6 +1235,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/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/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
-@@ -214,6 +214,9 @@
+--- a/storage/innobase/include/srv0srv.h
++++ b/storage/innobase/include/srv0srv.h
+@@ -211,6 +211,9 @@
extern ibool srv_innodb_status;
extern unsigned long long srv_stats_sample_pages;
extern ibool srv_use_doublewrite_buf;
extern ibool srv_use_checksums;
-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/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 @@
+--- a/storage/innobase/row/row0ins.c
++++ b/storage/innobase/row/row0ins.c
+@@ -2013,6 +2013,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
+--- a/storage/innobase/row/row0mysql.c
++++ b/storage/innobase/row/row0mysql.c
@@ -921,6 +921,9 @@
table->stat_modified_counter = counter + 1;
}
}
-@@ -2105,6 +2108,45 @@
+@@ -2076,6 +2079,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.
-@@ -3024,7 +3066,7 @@
+@@ -3000,7 +3068,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);
-@@ -3326,6 +3368,8 @@
+@@ -3302,6 +3370,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
-@@ -402,6 +402,9 @@
+--- a/storage/innobase/row/row0row.c
++++ b/storage/innobase/row/row0row.c
+@@ -373,6 +373,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,
+@@ -384,6 +392,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
+@@ -398,6 +398,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 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,15 +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;
+
+- for (i = 0; i < upd_get_n_fields(update); i++) {
++ 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) - extended; i++) {
+
+ ulint pos = upd_get_nth_field(update, i)->field_no;
+