]>
Commit | Line | Data |
---|---|---|
b4e1fa2c AM |
1 | # name : innodb_stats.patch |
2 | # introduced : 11 or before | |
3 | # maintainer : Yasufumi | |
4 | # | |
5 | #!!! notice !!! | |
6 | # Any small change to this file in the main branch | |
7 | # should be done or reviewed by the maintainer! | |
8 | diff -ruN a/storage/innobase/btr/btr0cur.c b/storage/innobase/btr/btr0cur.c | |
9 | --- a/storage/innobase/btr/btr0cur.c 2010-12-03 15:49:59.165212710 +0900 | |
10 | +++ b/storage/innobase/btr/btr0cur.c 2010-12-03 17:19:24.834126874 +0900 | |
11 | @@ -1010,6 +1010,107 @@ | |
12 | } | |
13 | } | |
14 | ||
15 | +/**********************************************************************//** | |
16 | +Positions a cursor at a randomly chosen position within a B-tree | |
17 | +after the given path | |
18 | +@return TRUE if the position is at the first page, and cursor must point | |
19 | + the first record for used by the caller.*/ | |
20 | +UNIV_INTERN | |
21 | +ibool | |
22 | +btr_cur_open_at_rnd_pos_after_path( | |
23 | +/*====================*/ | |
24 | + dict_index_t* index, /*!< in: index */ | |
25 | + ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ | |
26 | + btr_path_t* first_rec_path, | |
27 | + btr_cur_t* cursor, /*!< in/out: B-tree cursor */ | |
28 | + mtr_t* mtr) /*!< in: mtr */ | |
29 | +{ | |
30 | + page_cur_t* page_cursor; | |
31 | + btr_path_t* slot; | |
32 | + ibool is_first_rec = TRUE; | |
33 | + ulint page_no; | |
34 | + ulint space; | |
35 | + ulint zip_size; | |
36 | + ulint height; | |
37 | + rec_t* node_ptr; | |
38 | + mem_heap_t* heap = NULL; | |
39 | + ulint offsets_[REC_OFFS_NORMAL_SIZE]; | |
40 | + ulint* offsets = offsets_; | |
41 | + rec_offs_init(offsets_); | |
42 | + | |
43 | + if (latch_mode == BTR_MODIFY_TREE) { | |
44 | + mtr_x_lock(dict_index_get_lock(index), mtr); | |
45 | + } else { | |
46 | + mtr_s_lock(dict_index_get_lock(index), mtr); | |
47 | + } | |
48 | + | |
49 | + page_cursor = btr_cur_get_page_cur(cursor); | |
50 | + cursor->index = index; | |
51 | + | |
52 | + space = dict_index_get_space(index); | |
53 | + zip_size = dict_table_zip_size(index->table); | |
54 | + page_no = dict_index_get_page(index); | |
55 | + | |
56 | + height = ULINT_UNDEFINED; | |
57 | + slot = first_rec_path; | |
58 | + | |
59 | + for (;;) { | |
60 | + buf_block_t* block; | |
61 | + page_t* page; | |
62 | + | |
63 | + block = buf_page_get_gen(space, zip_size, page_no, | |
64 | + RW_NO_LATCH, NULL, BUF_GET, | |
65 | + __FILE__, __LINE__, mtr); | |
66 | + page = buf_block_get_frame(block); | |
67 | + ut_ad(index->id == btr_page_get_index_id(page)); | |
68 | + | |
69 | + if (height == ULINT_UNDEFINED) { | |
70 | + /* We are in the root node */ | |
71 | + | |
72 | + height = btr_page_get_level(page, mtr); | |
73 | + } | |
74 | + | |
75 | + if (height == 0) { | |
76 | + btr_cur_latch_leaves(page, space, zip_size, page_no, | |
77 | + latch_mode, cursor, mtr); | |
78 | + } | |
79 | + | |
80 | + if (is_first_rec && slot->nth_rec != ULINT_UNDEFINED) { | |
81 | + if (height == 0) { | |
82 | + /* must open the first rec */ | |
83 | + page_cur_open_on_nth_user_rec(block, page_cursor, slot->nth_rec); | |
84 | + } else { | |
85 | + is_first_rec = page_cur_open_on_rnd_user_rec_after_nth(block, | |
86 | + page_cursor, slot->nth_rec); | |
87 | + } | |
88 | + } else { | |
89 | + is_first_rec = FALSE; | |
90 | + page_cur_open_on_rnd_user_rec(block, page_cursor); | |
91 | + } | |
92 | + | |
93 | + if (height == 0) { | |
94 | + break; | |
95 | + } | |
96 | + | |
97 | + ut_ad(height > 0); | |
98 | + | |
99 | + height--; | |
100 | + slot++; | |
101 | + | |
102 | + node_ptr = page_cur_get_rec(page_cursor); | |
103 | + offsets = rec_get_offsets(node_ptr, cursor->index, offsets, | |
104 | + ULINT_UNDEFINED, &heap); | |
105 | + /* Go to the child node */ | |
106 | + page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); | |
107 | + } | |
108 | + | |
109 | + if (UNIV_LIKELY_NULL(heap)) { | |
110 | + mem_heap_free(heap); | |
111 | + } | |
112 | + | |
113 | + return (is_first_rec); | |
114 | +} | |
115 | + | |
116 | /*==================== B-TREE INSERT =========================*/ | |
117 | ||
118 | /*************************************************************//** | |
d8778560 | 119 | @@ -3479,6 +3580,154 @@ |
b4e1fa2c AM |
120 | } |
121 | ||
122 | /*******************************************************************//** | |
123 | +Estimates the number of pages which have not null value of the key of n_cols. | |
124 | +@return estimated number of pages */ | |
125 | +UNIV_INTERN | |
126 | +ulint | |
127 | +btr_estimate_n_pages_not_null( | |
128 | +/*=========================*/ | |
129 | + dict_index_t* index, /*!< in: index */ | |
130 | + ulint n_cols, /*!< in: The cols should be not null */ | |
131 | + btr_path_t* path1) /*!< in: path1[BTR_PATH_ARRAY_N_SLOTS] */ | |
132 | +{ | |
133 | + dtuple_t* tuple1; | |
134 | + btr_path_t path2[BTR_PATH_ARRAY_N_SLOTS]; | |
135 | + btr_cur_t cursor; | |
136 | + btr_path_t* slot1; | |
137 | + btr_path_t* slot2; | |
138 | + ibool diverged; | |
139 | + ibool diverged_lot; | |
140 | + ulint divergence_level; | |
141 | + ulint n_pages; | |
142 | + ulint i; | |
143 | + mtr_t mtr; | |
144 | + mem_heap_t* heap; | |
145 | + | |
146 | + heap = mem_heap_create(n_cols * sizeof(dfield_t) | |
147 | + + sizeof(dtuple_t)); | |
148 | + | |
149 | + /* make tuple1 (NULL,NULL,,,) from n_cols */ | |
150 | + tuple1 = dtuple_create(heap, n_cols); | |
151 | + dict_index_copy_types(tuple1, index, n_cols); | |
152 | + | |
153 | + for (i = 0; i < n_cols; i++) { | |
154 | + dfield_set_null(dtuple_get_nth_field(tuple1, i)); | |
155 | + } | |
156 | + | |
157 | + mtr_start(&mtr); | |
158 | + | |
159 | + cursor.path_arr = path1; | |
160 | + | |
161 | + btr_cur_search_to_nth_level(index, 0, tuple1, PAGE_CUR_G, | |
162 | + BTR_SEARCH_LEAF | BTR_ESTIMATE, | |
163 | + &cursor, 0, __FILE__, __LINE__, &mtr); | |
164 | + | |
165 | + mtr_commit(&mtr); | |
166 | + | |
167 | + | |
168 | + | |
169 | + mtr_start(&mtr); | |
170 | + | |
171 | + cursor.path_arr = path2; | |
172 | + | |
173 | + btr_cur_open_at_index_side(FALSE, index, | |
174 | + BTR_SEARCH_LEAF | BTR_ESTIMATE, | |
175 | + &cursor, &mtr); | |
176 | + | |
177 | + mtr_commit(&mtr); | |
178 | + | |
179 | + mem_heap_free(heap); | |
180 | + | |
181 | + /* We have the path information for the range in path1 and path2 */ | |
182 | + | |
183 | + n_pages = 1; | |
184 | + diverged = FALSE; /* This becomes true when the path is not | |
185 | + the same any more */ | |
186 | + diverged_lot = FALSE; /* This becomes true when the paths are | |
187 | + not the same or adjacent any more */ | |
188 | + divergence_level = 1000000; /* This is the level where paths diverged | |
189 | + a lot */ | |
190 | + for (i = 0; ; i++) { | |
191 | + ut_ad(i < BTR_PATH_ARRAY_N_SLOTS); | |
192 | + | |
193 | + slot1 = path1 + i; | |
194 | + slot2 = path2 + i; | |
195 | + | |
196 | + if ((slot1 + 1)->nth_rec == ULINT_UNDEFINED | |
197 | + || (slot2 + 1)->nth_rec == ULINT_UNDEFINED) { | |
198 | + | |
199 | + if (i > divergence_level + 1) { | |
200 | + /* In trees whose height is > 1 our algorithm | |
201 | + tends to underestimate: multiply the estimate | |
202 | + by 2: */ | |
203 | + | |
204 | + n_pages = n_pages * 2; | |
205 | + } | |
206 | + | |
207 | + /* Do not estimate the number of rows in the range | |
208 | + to over 1 / 2 of the estimated rows in the whole | |
209 | + table */ | |
210 | + | |
211 | + if (n_pages > index->stat_n_leaf_pages / 2) { | |
212 | + n_pages = index->stat_n_leaf_pages / 2; | |
213 | + | |
214 | + /* If there are just 0 or 1 rows in the table, | |
215 | + then we estimate all rows are in the range */ | |
216 | + | |
217 | + if (n_pages == 0) { | |
218 | + n_pages = index->stat_n_leaf_pages; | |
219 | + } | |
220 | + } | |
221 | + | |
222 | + return(n_pages); | |
223 | + } | |
224 | + | |
225 | + if (!diverged && slot1->nth_rec != slot2->nth_rec) { | |
226 | + | |
227 | + diverged = TRUE; | |
228 | + | |
229 | + if (slot1->nth_rec < slot2->nth_rec) { | |
230 | + n_pages = slot2->nth_rec - slot1->nth_rec; | |
231 | + | |
232 | + if (n_pages > 1) { | |
233 | + diverged_lot = TRUE; | |
234 | + divergence_level = i; | |
235 | + } | |
236 | + } else { | |
237 | + /* Maybe the tree has changed between | |
238 | + searches */ | |
239 | + | |
240 | + return(10); | |
241 | + } | |
242 | + | |
243 | + } else if (diverged && !diverged_lot) { | |
244 | + | |
245 | + if (slot1->nth_rec < slot1->n_recs | |
246 | + || slot2->nth_rec > 1) { | |
247 | + | |
248 | + diverged_lot = TRUE; | |
249 | + divergence_level = i; | |
250 | + | |
251 | + n_pages = 0; | |
252 | + | |
253 | + if (slot1->nth_rec < slot1->n_recs) { | |
254 | + n_pages += slot1->n_recs | |
255 | + - slot1->nth_rec; | |
256 | + } | |
257 | + | |
258 | + if (slot2->nth_rec > 1) { | |
259 | + n_pages += slot2->nth_rec - 1; | |
260 | + } | |
261 | + } | |
262 | + } else if (diverged_lot) { | |
263 | + | |
264 | + n_pages = (n_pages * (slot1->n_recs + slot2->n_recs)) | |
265 | + / 2; | |
266 | + } | |
267 | + } | |
268 | +} | |
269 | + | |
270 | +/*******************************************************************//** | |
271 | Estimates the number of different key values in a given index, for | |
272 | each n-column prefix of the index where n <= dict_index_get_n_unique(index). | |
273 | The estimates are stored in the array index->stat_n_diff_key_vals. */ | |
d8778560 | 274 | @@ -3507,18 +3756,38 @@ |
b4e1fa2c AM |
275 | ulint offsets_next_rec_[REC_OFFS_NORMAL_SIZE]; |
276 | ulint* offsets_rec = offsets_rec_; | |
277 | ulint* offsets_next_rec= offsets_next_rec_; | |
278 | + ulint stats_method = srv_stats_method; | |
279 | + btr_path_t first_rec_path[BTR_PATH_ARRAY_N_SLOTS]; | |
280 | + ulint effective_pages; /* effective leaf pages */ | |
281 | rec_offs_init(offsets_rec_); | |
282 | rec_offs_init(offsets_next_rec_); | |
283 | ||
284 | n_cols = dict_index_get_n_unique(index); | |
285 | ||
286 | + if (stats_method == SRV_STATS_METHOD_IGNORE_NULLS) { | |
287 | + /* estimate effective pages and path for the first effective record */ | |
288 | + /* TODO: make it work also for n_cols > 1. */ | |
289 | + effective_pages = btr_estimate_n_pages_not_null(index, 1 /*k*/, first_rec_path); | |
290 | + | |
291 | + if (!effective_pages) { | |
292 | + for (j = 0; j <= n_cols; j++) { | |
293 | + index->stat_n_diff_key_vals[j] = (ib_int64_t)index->stat_n_leaf_pages; | |
294 | + } | |
295 | + return; | |
296 | + } else if (effective_pages > index->stat_n_leaf_pages) { | |
297 | + effective_pages = index->stat_n_leaf_pages; | |
298 | + } | |
299 | + } else { | |
300 | + effective_pages = index->stat_n_leaf_pages; | |
301 | + } | |
302 | + | |
303 | n_diff = mem_zalloc((n_cols + 1) * sizeof(ib_int64_t)); | |
304 | ||
305 | /* It makes no sense to test more pages than are contained | |
306 | in the index, thus we lower the number if it is too high */ | |
307 | - if (srv_stats_sample_pages > index->stat_index_size) { | |
308 | - if (index->stat_index_size > 0) { | |
309 | - n_sample_pages = index->stat_index_size; | |
310 | + if (srv_stats_sample_pages > effective_pages) { | |
311 | + if (effective_pages > 0) { | |
312 | + n_sample_pages = effective_pages; | |
313 | } else { | |
314 | n_sample_pages = 1; | |
315 | } | |
d8778560 | 316 | @@ -3530,9 +3799,15 @@ |
b4e1fa2c AM |
317 | |
318 | for (i = 0; i < n_sample_pages; i++) { | |
319 | rec_t* supremum; | |
320 | + ibool is_first_page = TRUE; | |
321 | mtr_start(&mtr); | |
322 | ||
323 | + if (stats_method == SRV_STATS_METHOD_IGNORE_NULLS) { | |
324 | + is_first_page = btr_cur_open_at_rnd_pos_after_path(index, BTR_SEARCH_LEAF, | |
325 | + first_rec_path, &cursor, &mtr); | |
326 | + } else { | |
327 | btr_cur_open_at_rnd_pos(index, BTR_SEARCH_LEAF, &cursor, &mtr); | |
328 | + } | |
329 | ||
330 | /* Count the number of different key values for each prefix of | |
331 | the key on this index page. If the prefix does not determine | |
d8778560 | 332 | @@ -3543,7 +3818,13 @@ |
b4e1fa2c AM |
333 | page = btr_cur_get_page(&cursor); |
334 | ||
335 | supremum = page_get_supremum_rec(page); | |
336 | + if (stats_method == SRV_STATS_METHOD_IGNORE_NULLS && is_first_page) { | |
337 | + /* the cursor should be the first record of the page. */ | |
338 | + /* Counting should be started from here. */ | |
339 | + rec = btr_cur_get_rec(&cursor); | |
340 | + } else { | |
341 | rec = page_rec_get_next(page_get_infimum_rec(page)); | |
342 | + } | |
343 | ||
344 | if (rec != supremum) { | |
345 | not_empty_flag = 1; | |
d8778560 | 346 | @@ -3552,7 +3833,8 @@ |
b4e1fa2c AM |
347 | } |
348 | ||
349 | while (rec != supremum) { | |
350 | - rec_t* next_rec = page_rec_get_next(rec); | |
351 | + rec_t* next_rec; | |
352 | + next_rec = page_rec_get_next(rec); | |
353 | if (next_rec == supremum) { | |
354 | break; | |
355 | } | |
d8778560 | 356 | @@ -3566,7 +3848,10 @@ |
b4e1fa2c AM |
357 | cmp_rec_rec_with_match(rec, next_rec, |
358 | offsets_rec, offsets_next_rec, | |
359 | index, &matched_fields, | |
360 | - &matched_bytes); | |
361 | + &matched_bytes, | |
362 | + (stats_method==SRV_STATS_METHOD_NULLS_NOT_EQUAL) ? | |
363 | + SRV_STATS_METHOD_NULLS_NOT_EQUAL : | |
364 | + SRV_STATS_METHOD_NULLS_EQUAL); | |
365 | ||
366 | for (j = matched_fields + 1; j <= n_cols; j++) { | |
367 | /* We add one if this index record has | |
d8778560 | 368 | @@ -3627,7 +3912,7 @@ |
b4e1fa2c AM |
369 | for (j = 0; j <= n_cols; j++) { |
370 | index->stat_n_diff_key_vals[j] | |
371 | = ((n_diff[j] | |
372 | - * (ib_int64_t)index->stat_n_leaf_pages | |
373 | + * (ib_int64_t)effective_pages | |
374 | + n_sample_pages - 1 | |
375 | + total_external_size | |
376 | + not_empty_flag) | |
d8778560 | 377 | @@ -3642,7 +3927,7 @@ |
b4e1fa2c AM |
378 | different key values, or even more. Let us try to approximate |
379 | that: */ | |
380 | ||
381 | - add_on = index->stat_n_leaf_pages | |
382 | + add_on = effective_pages | |
383 | / (10 * (n_sample_pages | |
384 | + total_external_size)); | |
385 | ||
d8778560 | 386 | @@ -3651,6 +3936,15 @@ |
b4e1fa2c AM |
387 | } |
388 | ||
389 | index->stat_n_diff_key_vals[j] += add_on; | |
390 | + | |
391 | + if (stats_method == SRV_STATS_METHOD_IGNORE_NULLS) { | |
392 | + /* index->stat_n_diff_key_vals[k] is used for calc rec_per_key, | |
393 | + as "stats.records / index->stat_n_diff_key_vals[x]". | |
394 | + So it should be adjusted to the value which is based on whole of the index. */ | |
395 | + index->stat_n_diff_key_vals[j] = | |
396 | + index->stat_n_diff_key_vals[j] * (ib_int64_t)index->stat_n_leaf_pages | |
397 | + / (ib_int64_t)effective_pages; | |
398 | + } | |
399 | } | |
400 | ||
401 | mem_free(n_diff); | |
402 | diff -ruN a/storage/innobase/dict/dict0boot.c b/storage/innobase/dict/dict0boot.c | |
403 | --- a/storage/innobase/dict/dict0boot.c 2010-12-03 15:48:03.034036843 +0900 | |
404 | +++ b/storage/innobase/dict/dict0boot.c 2010-12-03 17:19:24.835112632 +0900 | |
405 | @@ -266,6 +266,29 @@ | |
406 | /* Get the dictionary header */ | |
407 | dict_hdr = dict_hdr_get(&mtr); | |
408 | ||
409 | + if (mach_read_from_8(dict_hdr + DICT_HDR_XTRADB_MARK) | |
410 | + != DICT_HDR_XTRADB_FLAG) { | |
411 | + /* not extended yet by XtraDB, need to be extended */ | |
412 | + ulint root_page_no; | |
413 | + | |
414 | + root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, | |
415 | + DICT_HDR_SPACE, 0, DICT_STATS_ID, | |
416 | + dict_ind_redundant, &mtr); | |
417 | + if (root_page_no == FIL_NULL) { | |
418 | + fprintf(stderr, "InnoDB: Warning: failed to create SYS_STATS btr.\n"); | |
419 | + srv_use_sys_stats_table = FALSE; | |
420 | + } else { | |
421 | + mlog_write_ulint(dict_hdr + DICT_HDR_STATS, root_page_no, | |
422 | + MLOG_4BYTES, &mtr); | |
423 | + mlog_write_ull(dict_hdr + DICT_HDR_XTRADB_MARK, | |
424 | + DICT_HDR_XTRADB_FLAG, &mtr); | |
425 | + } | |
426 | + mtr_commit(&mtr); | |
427 | + /* restart mtr */ | |
428 | + mtr_start(&mtr); | |
429 | + dict_hdr = dict_hdr_get(&mtr); | |
430 | + } | |
431 | + | |
432 | /* Because we only write new row ids to disk-based data structure | |
433 | (dictionary header) when it is divisible by | |
434 | DICT_HDR_ROW_ID_WRITE_MARGIN, in recovery we will not recover | |
435 | @@ -425,7 +448,7 @@ | |
436 | table->id = DICT_FIELDS_ID; | |
437 | dict_table_add_to_cache(table, heap); | |
438 | dict_sys->sys_fields = table; | |
439 | - mem_heap_free(heap); | |
440 | + mem_heap_empty(heap); | |
441 | ||
442 | index = dict_mem_index_create("SYS_FIELDS", "CLUST_IND", | |
443 | DICT_HDR_SPACE, | |
444 | @@ -442,6 +465,41 @@ | |
445 | FALSE); | |
446 | ut_a(error == DB_SUCCESS); | |
447 | ||
448 | + /*-------------------------*/ | |
449 | + table = dict_mem_table_create("SYS_STATS", DICT_HDR_SPACE, 3, 0); | |
450 | + table->n_mysql_handles_opened = 1; /* for pin */ | |
451 | + | |
452 | + dict_mem_table_add_col(table, heap, "INDEX_ID", DATA_BINARY, 0, 0); | |
453 | + dict_mem_table_add_col(table, heap, "KEY_COLS", DATA_INT, 0, 4); | |
454 | + dict_mem_table_add_col(table, heap, "DIFF_VALS", DATA_BINARY, 0, 0); | |
455 | + | |
456 | + /* The '+ 2' below comes from the fields DB_TRX_ID, DB_ROLL_PTR */ | |
457 | +#if DICT_SYS_STATS_DIFF_VALS_FIELD != 2 + 2 | |
458 | +#error "DICT_SYS_STATS_DIFF_VALS_FIELD != 2 + 2" | |
459 | +#endif | |
460 | + | |
461 | + table->id = DICT_STATS_ID; | |
462 | + dict_table_add_to_cache(table, heap); | |
463 | + dict_sys->sys_stats = table; | |
464 | + mem_heap_empty(heap); | |
465 | + | |
466 | + index = dict_mem_index_create("SYS_STATS", "CLUST_IND", | |
467 | + DICT_HDR_SPACE, | |
468 | + DICT_UNIQUE | DICT_CLUSTERED, 2); | |
469 | + | |
470 | + dict_mem_index_add_field(index, "INDEX_ID", 0); | |
471 | + dict_mem_index_add_field(index, "KEY_COLS", 0); | |
472 | + | |
473 | + index->id = DICT_STATS_ID; | |
474 | + error = dict_index_add_to_cache(table, index, | |
475 | + mtr_read_ulint(dict_hdr | |
476 | + + DICT_HDR_STATS, | |
477 | + MLOG_4BYTES, &mtr), | |
478 | + FALSE); | |
479 | + ut_a(error == DB_SUCCESS); | |
480 | + | |
481 | + mem_heap_free(heap); | |
482 | + | |
483 | mtr_commit(&mtr); | |
484 | /*-------------------------*/ | |
485 | ||
486 | @@ -455,6 +513,7 @@ | |
487 | dict_load_sys_table(dict_sys->sys_columns); | |
488 | dict_load_sys_table(dict_sys->sys_indexes); | |
489 | dict_load_sys_table(dict_sys->sys_fields); | |
490 | + dict_load_sys_table(dict_sys->sys_stats); | |
491 | ||
492 | mutex_exit(&(dict_sys->mutex)); | |
493 | } | |
494 | diff -ruN a/storage/innobase/dict/dict0crea.c b/storage/innobase/dict/dict0crea.c | |
495 | --- a/storage/innobase/dict/dict0crea.c 2010-12-03 15:48:03.036081059 +0900 | |
496 | +++ b/storage/innobase/dict/dict0crea.c 2010-12-03 17:19:24.836964976 +0900 | |
497 | @@ -508,6 +508,51 @@ | |
498 | } | |
499 | ||
500 | /*****************************************************************//** | |
501 | +Based on an index object, this function builds the entry to be inserted | |
502 | +in the SYS_STATS system table. | |
503 | +@return the tuple which should be inserted */ | |
504 | +static | |
505 | +dtuple_t* | |
506 | +dict_create_sys_stats_tuple( | |
507 | +/*========================*/ | |
508 | + const dict_index_t* index, | |
509 | + ulint i, | |
510 | + mem_heap_t* heap) | |
511 | +{ | |
512 | + dict_table_t* sys_stats; | |
513 | + dtuple_t* entry; | |
514 | + dfield_t* dfield; | |
515 | + byte* ptr; | |
516 | + | |
517 | + ut_ad(index); | |
518 | + ut_ad(heap); | |
519 | + | |
520 | + sys_stats = dict_sys->sys_stats; | |
521 | + | |
522 | + entry = dtuple_create(heap, 3 + DATA_N_SYS_COLS); | |
523 | + | |
524 | + dict_table_copy_types(entry, sys_stats); | |
525 | + | |
526 | + /* 0: INDEX_ID -----------------------*/ | |
527 | + dfield = dtuple_get_nth_field(entry, 0/*INDEX_ID*/); | |
528 | + ptr = mem_heap_alloc(heap, 8); | |
529 | + mach_write_to_8(ptr, index->id); | |
530 | + dfield_set_data(dfield, ptr, 8); | |
531 | + /* 1: KEY_COLS -----------------------*/ | |
532 | + dfield = dtuple_get_nth_field(entry, 1/*KEY_COLS*/); | |
533 | + ptr = mem_heap_alloc(heap, 4); | |
534 | + mach_write_to_4(ptr, i); | |
535 | + dfield_set_data(dfield, ptr, 4); | |
536 | + /* 4: DIFF_VALS ----------------------*/ | |
537 | + dfield = dtuple_get_nth_field(entry, 2/*DIFF_VALS*/); | |
538 | + ptr = mem_heap_alloc(heap, 8); | |
539 | + mach_write_to_8(ptr, 0); /* initial value is 0 */ | |
540 | + dfield_set_data(dfield, ptr, 8); | |
541 | + | |
542 | + return(entry); | |
543 | +} | |
544 | + | |
545 | +/*****************************************************************//** | |
546 | Creates the tuple with which the index entry is searched for writing the index | |
547 | tree root page number, if such a tree is created. | |
548 | @return the tuple for search */ | |
549 | @@ -617,6 +662,27 @@ | |
550 | } | |
551 | ||
552 | /***************************************************************//** | |
553 | +Builds a row for storing stats to insert. | |
554 | +@return DB_SUCCESS */ | |
555 | +static | |
556 | +ulint | |
557 | +dict_build_stats_def_step( | |
558 | +/*======================*/ | |
559 | + ind_node_t* node) | |
560 | +{ | |
561 | + dict_index_t* index; | |
562 | + dtuple_t* row; | |
563 | + | |
564 | + index = node->index; | |
565 | + | |
566 | + row = dict_create_sys_stats_tuple(index, node->stats_no, node->heap); | |
567 | + | |
568 | + ins_node_set_new_row(node->stats_def, row); | |
569 | + | |
570 | + return(DB_SUCCESS); | |
571 | +} | |
572 | + | |
573 | +/***************************************************************//** | |
574 | Creates an index tree for the index if it is not a member of a cluster. | |
575 | @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ | |
576 | static | |
577 | @@ -937,6 +1003,49 @@ | |
578 | dict_sys->sys_fields, heap); | |
579 | node->field_def->common.parent = node; | |
580 | ||
581 | + if (srv_use_sys_stats_table) { | |
582 | + node->stats_def = ins_node_create(INS_DIRECT, | |
583 | + dict_sys->sys_stats, heap); | |
584 | + node->stats_def->common.parent = node; | |
585 | + } else { | |
586 | + node->stats_def = NULL; | |
587 | + } | |
588 | + | |
589 | + node->commit_node = commit_node_create(heap); | |
590 | + node->commit_node->common.parent = node; | |
591 | + | |
592 | + return(node); | |
593 | +} | |
594 | + | |
595 | +/*********************************************************************//** | |
596 | +*/ | |
597 | +UNIV_INTERN | |
598 | +ind_node_t* | |
599 | +ind_insert_stats_graph_create( | |
600 | +/*==========================*/ | |
601 | + dict_index_t* index, | |
602 | + mem_heap_t* heap) | |
603 | +{ | |
604 | + ind_node_t* node; | |
605 | + | |
606 | + node = mem_heap_alloc(heap, sizeof(ind_node_t)); | |
607 | + | |
608 | + node->common.type = QUE_NODE_INSERT_STATS; | |
609 | + | |
610 | + node->index = index; | |
611 | + | |
612 | + node->state = INDEX_BUILD_STATS_COLS; | |
613 | + node->page_no = FIL_NULL; | |
614 | + node->heap = mem_heap_create(256); | |
615 | + | |
616 | + node->ind_def = NULL; | |
617 | + node->field_def = NULL; | |
618 | + | |
619 | + node->stats_def = ins_node_create(INS_DIRECT, | |
620 | + dict_sys->sys_stats, heap); | |
621 | + node->stats_def->common.parent = node; | |
622 | + node->stats_no = 0; | |
623 | + | |
624 | node->commit_node = commit_node_create(heap); | |
625 | node->commit_node->common.parent = node; | |
626 | ||
627 | @@ -1087,6 +1196,7 @@ | |
628 | ||
629 | node->state = INDEX_BUILD_FIELD_DEF; | |
630 | node->field_no = 0; | |
631 | + node->stats_no = 0; | |
632 | ||
633 | thr->run_node = node->ind_def; | |
634 | ||
635 | @@ -1132,7 +1242,31 @@ | |
636 | goto function_exit; | |
637 | } | |
638 | ||
639 | - node->state = INDEX_CREATE_INDEX_TREE; | |
640 | + if (srv_use_sys_stats_table | |
641 | + && !((node->table->flags >> DICT_TF2_SHIFT) & DICT_TF2_TEMPORARY)) { | |
642 | + node->state = INDEX_BUILD_STATS_COLS; | |
643 | + } else { | |
644 | + node->state = INDEX_CREATE_INDEX_TREE; | |
645 | + } | |
646 | + } | |
647 | + if (node->state == INDEX_BUILD_STATS_COLS) { | |
648 | + if (node->stats_no <= dict_index_get_n_unique(node->index)) { | |
649 | + | |
650 | + err = dict_build_stats_def_step(node); | |
651 | + | |
652 | + if (err != DB_SUCCESS) { | |
653 | + | |
654 | + goto function_exit; | |
655 | + } | |
656 | + | |
657 | + node->stats_no++; | |
658 | + | |
659 | + thr->run_node = node->stats_def; | |
660 | + | |
661 | + return(thr); | |
662 | + } else { | |
663 | + node->state = INDEX_CREATE_INDEX_TREE; | |
664 | + } | |
665 | } | |
666 | ||
667 | if (node->state == INDEX_CREATE_INDEX_TREE) { | |
668 | @@ -1178,6 +1312,66 @@ | |
669 | return(NULL); | |
670 | } | |
671 | ||
672 | + thr->run_node = que_node_get_parent(node); | |
673 | + | |
674 | + return(thr); | |
675 | +} | |
676 | + | |
677 | +/****************************************************************//** | |
678 | +*/ | |
679 | +UNIV_INTERN | |
680 | +que_thr_t* | |
681 | +dict_insert_stats_step( | |
682 | +/*===================*/ | |
683 | + que_thr_t* thr) /*!< in: query thread */ | |
684 | +{ | |
685 | + ind_node_t* node; | |
686 | + ulint err = DB_ERROR; | |
687 | + trx_t* trx; | |
688 | + | |
689 | + ut_ad(thr); | |
690 | + | |
691 | + trx = thr_get_trx(thr); | |
692 | + | |
693 | + node = thr->run_node; | |
694 | + | |
695 | + if (thr->prev_node == que_node_get_parent(node)) { | |
696 | + node->state = INDEX_BUILD_STATS_COLS; | |
697 | + } | |
698 | + | |
699 | + if (node->state == INDEX_BUILD_STATS_COLS) { | |
700 | + if (node->stats_no <= dict_index_get_n_unique(node->index)) { | |
701 | + | |
702 | + err = dict_build_stats_def_step(node); | |
703 | + | |
704 | + if (err != DB_SUCCESS) { | |
705 | + | |
706 | + goto function_exit; | |
707 | + } | |
708 | + | |
709 | + node->stats_no++; | |
710 | + | |
711 | + thr->run_node = node->stats_def; | |
712 | + | |
713 | + return(thr); | |
714 | + } else { | |
715 | + node->state = INDEX_COMMIT_WORK; | |
716 | + } | |
717 | + } | |
718 | + | |
719 | + if (node->state == INDEX_COMMIT_WORK) { | |
720 | + | |
721 | + /* do not commit transaction here for now */ | |
722 | + } | |
723 | + | |
724 | +function_exit: | |
725 | + trx->error_state = err; | |
726 | + | |
727 | + if (err == DB_SUCCESS) { | |
728 | + } else { | |
729 | + return(NULL); | |
730 | + } | |
731 | + | |
732 | thr->run_node = que_node_get_parent(node); | |
733 | ||
734 | return(thr); | |
735 | diff -ruN a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c | |
736 | --- a/storage/innobase/dict/dict0dict.c 2010-12-03 15:48:03.040222428 +0900 | |
737 | +++ b/storage/innobase/dict/dict0dict.c 2010-12-03 17:19:24.841947690 +0900 | |
d8778560 | 738 | @@ -756,7 +756,7 @@ |
b4e1fa2c AM |
739 | print an error message and return without doing |
740 | anything. */ | |
741 | dict_update_statistics(table, TRUE /* only update stats | |
742 | - if they have not been initialized */); | |
743 | + if they have not been initialized */, FALSE); | |
744 | } | |
745 | ||
746 | return(table); | |
d8778560 | 747 | @@ -4304,6 +4304,240 @@ |
b4e1fa2c AM |
748 | } |
749 | ||
750 | /*********************************************************************//** | |
751 | +functions to use SYS_STATS system table. */ | |
752 | +static | |
753 | +ibool | |
754 | +dict_reload_statistics( | |
755 | +/*===================*/ | |
756 | + dict_table_t* table, | |
757 | + ulint* sum_of_index_sizes) | |
758 | +{ | |
759 | + dict_index_t* index; | |
760 | + ulint size; | |
761 | + mem_heap_t* heap; | |
762 | + | |
763 | + index = dict_table_get_first_index(table); | |
764 | + | |
765 | + if (index == NULL) { | |
766 | + /* Table definition is corrupt */ | |
767 | + | |
768 | + return(FALSE); | |
769 | + } | |
770 | + | |
771 | + heap = mem_heap_create(1000); | |
772 | + | |
773 | + while (index) { | |
774 | + size = btr_get_size(index, BTR_TOTAL_SIZE); | |
775 | + | |
776 | + index->stat_index_size = size; | |
777 | + | |
778 | + *sum_of_index_sizes += size; | |
779 | + | |
780 | + size = btr_get_size(index, BTR_N_LEAF_PAGES); | |
781 | + | |
782 | + if (size == 0) { | |
783 | + /* The root node of the tree is a leaf */ | |
784 | + size = 1; | |
785 | + } | |
786 | + | |
787 | + index->stat_n_leaf_pages = size; | |
788 | + | |
789 | +/*===========================================*/ | |
790 | +{ | |
791 | + dict_table_t* sys_stats; | |
792 | + dict_index_t* sys_index; | |
793 | + btr_pcur_t pcur; | |
794 | + dtuple_t* tuple; | |
795 | + dfield_t* dfield; | |
796 | + ulint key_cols; | |
797 | + ulint n_cols; | |
798 | + const rec_t* rec; | |
799 | + const byte* field; | |
800 | + ulint len; | |
801 | + ib_int64_t* stat_n_diff_key_vals_tmp; | |
802 | + byte* buf; | |
803 | + ulint i; | |
804 | + mtr_t mtr; | |
805 | + | |
806 | + n_cols = dict_index_get_n_unique(index); | |
807 | + stat_n_diff_key_vals_tmp = mem_heap_zalloc(heap, (n_cols + 1) * sizeof(ib_int64_t)); | |
808 | + | |
809 | + sys_stats = dict_sys->sys_stats; | |
810 | + sys_index = UT_LIST_GET_FIRST(sys_stats->indexes); | |
811 | + ut_a(!dict_table_is_comp(sys_stats)); | |
812 | + | |
813 | + tuple = dtuple_create(heap, 1); | |
814 | + dfield = dtuple_get_nth_field(tuple, 0); | |
815 | + | |
816 | + buf = mem_heap_alloc(heap, 8); | |
817 | + mach_write_to_8(buf, index->id); | |
818 | + | |
819 | + dfield_set_data(dfield, buf, 8); | |
820 | + dict_index_copy_types(tuple, sys_index, 1); | |
821 | + | |
822 | + mtr_start(&mtr); | |
823 | + | |
824 | + btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, | |
825 | + BTR_SEARCH_LEAF, &pcur, &mtr); | |
826 | + for (i = 0; i <= n_cols; i++) { | |
827 | + rec = btr_pcur_get_rec(&pcur); | |
828 | + | |
829 | + if (!btr_pcur_is_on_user_rec(&pcur) | |
830 | + || mach_read_from_8(rec_get_nth_field_old(rec, 0, &len)) | |
831 | + != index->id) { | |
832 | + /* not found: even 1 if not found should not be alowed */ | |
833 | + fprintf(stderr, "InnoDB: Warning: stats for %s/%s (%lu/%lu)" | |
d8778560 | 834 | + " not found in SYS_STATS\n", |
b4e1fa2c AM |
835 | + index->table_name, index->name, i, n_cols); |
836 | + btr_pcur_close(&pcur); | |
837 | + mtr_commit(&mtr); | |
838 | + mem_heap_free(heap); | |
839 | + return(FALSE); | |
840 | + } | |
841 | + | |
842 | + if (rec_get_deleted_flag(rec, 0)) { | |
843 | + goto next_rec; | |
844 | + } | |
845 | + | |
846 | + field = rec_get_nth_field_old(rec, 1, &len); | |
847 | + ut_a(len == 4); | |
848 | + | |
849 | + key_cols = mach_read_from_4(field); | |
850 | + | |
851 | + ut_a(i == key_cols); | |
852 | + | |
853 | + field = rec_get_nth_field_old(rec, DICT_SYS_STATS_DIFF_VALS_FIELD, &len); | |
854 | + ut_a(len == 8); | |
855 | + | |
856 | + stat_n_diff_key_vals_tmp[i] = mach_read_from_8(field); | |
857 | +next_rec: | |
858 | + btr_pcur_move_to_next_user_rec(&pcur, &mtr); | |
859 | + } | |
860 | + | |
861 | + btr_pcur_close(&pcur); | |
862 | + mtr_commit(&mtr); | |
863 | + | |
864 | + for (i = 0; i <= n_cols; i++) { | |
865 | + index->stat_n_diff_key_vals[i] = stat_n_diff_key_vals_tmp[i]; | |
866 | + } | |
867 | +} | |
868 | +/*===========================================*/ | |
869 | + | |
870 | + index = dict_table_get_next_index(index); | |
871 | + } | |
872 | + | |
873 | + mem_heap_free(heap); | |
874 | + return(TRUE); | |
875 | +} | |
876 | + | |
877 | +static | |
878 | +void | |
879 | +dict_store_statistics( | |
880 | +/*==================*/ | |
881 | + dict_table_t* table) | |
882 | +{ | |
883 | + dict_index_t* index; | |
884 | + mem_heap_t* heap; | |
885 | + | |
886 | + index = dict_table_get_first_index(table); | |
887 | + | |
888 | + ut_a(index); | |
889 | + | |
890 | + heap = mem_heap_create(1000); | |
891 | + | |
892 | + while (index) { | |
893 | +/*===========================================*/ | |
894 | +{ | |
895 | + dict_table_t* sys_stats; | |
896 | + dict_index_t* sys_index; | |
897 | + btr_pcur_t pcur; | |
898 | + dtuple_t* tuple; | |
899 | + dfield_t* dfield; | |
900 | + ulint key_cols; | |
901 | + ulint n_cols; | |
902 | + ulint rests; | |
903 | + const rec_t* rec; | |
904 | + const byte* field; | |
905 | + ulint len; | |
906 | + ib_int64_t* stat_n_diff_key_vals_tmp; | |
907 | + byte* buf; | |
908 | + ulint i; | |
909 | + mtr_t mtr; | |
910 | + | |
911 | + n_cols = dict_index_get_n_unique(index); | |
912 | + stat_n_diff_key_vals_tmp = mem_heap_zalloc(heap, (n_cols + 1) * sizeof(ib_int64_t)); | |
913 | + | |
914 | + for (i = 0; i <= n_cols; i++) { | |
915 | + stat_n_diff_key_vals_tmp[i] = index->stat_n_diff_key_vals[i]; | |
916 | + } | |
917 | + | |
918 | + sys_stats = dict_sys->sys_stats; | |
919 | + sys_index = UT_LIST_GET_FIRST(sys_stats->indexes); | |
920 | + ut_a(!dict_table_is_comp(sys_stats)); | |
921 | + | |
922 | + tuple = dtuple_create(heap, 1); | |
923 | + dfield = dtuple_get_nth_field(tuple, 0); | |
924 | + | |
925 | + buf = mem_heap_alloc(heap, 8); | |
926 | + mach_write_to_8(buf, index->id); | |
927 | + | |
928 | + dfield_set_data(dfield, buf, 8); | |
929 | + dict_index_copy_types(tuple, sys_index, 1); | |
930 | + | |
931 | + mtr_start(&mtr); | |
932 | + | |
933 | + btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, | |
934 | + BTR_MODIFY_LEAF, &pcur, &mtr); | |
935 | + rests = n_cols + 1; | |
936 | + for (i = 0; i <= n_cols; i++) { | |
937 | + rec = btr_pcur_get_rec(&pcur); | |
938 | + | |
939 | + if (!btr_pcur_is_on_user_rec(&pcur) | |
940 | + || mach_read_from_8(rec_get_nth_field_old(rec, 0, &len)) | |
941 | + != index->id) { | |
942 | + /* not found */ | |
d8778560 AM |
943 | + |
944 | + | |
b4e1fa2c AM |
945 | + break; |
946 | + } | |
947 | + | |
948 | + if (rec_get_deleted_flag(rec, 0)) { | |
949 | + goto next_rec; | |
950 | + } | |
951 | + | |
952 | + field = rec_get_nth_field_old(rec, 1, &len); | |
953 | + ut_a(len == 4); | |
954 | + | |
955 | + key_cols = mach_read_from_4(field); | |
956 | + | |
957 | + field = rec_get_nth_field_old(rec, DICT_SYS_STATS_DIFF_VALS_FIELD, &len); | |
958 | + ut_a(len == 8); | |
959 | + | |
960 | + mlog_write_ull((byte*)field, stat_n_diff_key_vals_tmp[key_cols], &mtr); | |
961 | + | |
962 | + rests--; | |
963 | + | |
964 | +next_rec: | |
965 | + btr_pcur_move_to_next_user_rec(&pcur, &mtr); | |
966 | + } | |
967 | + btr_pcur_close(&pcur); | |
968 | + mtr_commit(&mtr); | |
969 | + | |
970 | + if (rests) { | |
971 | + fprintf(stderr, "InnoDB: Warning: failed to store %lu stats entries" | |
972 | + " of %s/%s to SYS_STATS system table.\n", | |
973 | + rests, index->table_name, index->name); | |
974 | + } | |
975 | +} | |
976 | +/*===========================================*/ | |
977 | + | |
978 | + index = dict_table_get_next_index(index); | |
979 | + } | |
980 | + | |
981 | + mem_heap_free(heap); | |
982 | +} | |
983 | + | |
984 | +/*********************************************************************//** | |
985 | Calculates new estimates for table and index statistics. The statistics | |
986 | are used in query optimization. */ | |
987 | UNIV_INTERN | |
d8778560 | 988 | @@ -4311,10 +4545,11 @@ |
b4e1fa2c AM |
989 | dict_update_statistics( |
990 | /*===================*/ | |
991 | dict_table_t* table, /*!< in/out: table */ | |
992 | - ibool only_calc_if_missing_stats)/*!< in: only | |
993 | + ibool only_calc_if_missing_stats,/*!< in: only | |
994 | update/recalc the stats if they have | |
995 | not been initialized yet, otherwise | |
996 | do nothing */ | |
997 | + ibool sync) /*!< in: TRUE if must update SYS_STATS */ | |
998 | { | |
999 | dict_index_t* index; | |
1000 | ulint sum_of_index_sizes = 0; | |
d8778560 | 1001 | @@ -4331,6 +4566,27 @@ |
b4e1fa2c AM |
1002 | return; |
1003 | } | |
1004 | ||
1005 | + if (srv_use_sys_stats_table && !((table->flags >> DICT_TF2_SHIFT) & DICT_TF2_TEMPORARY) && !sync) { | |
1006 | + dict_table_stats_lock(table, RW_X_LATCH); | |
1007 | + | |
1008 | + /* reload statistics from SYS_STATS table */ | |
1009 | + if (dict_reload_statistics(table, &sum_of_index_sizes)) { | |
1010 | + /* success */ | |
1011 | +#ifdef UNIV_DEBUG | |
d8778560 | 1012 | + fprintf(stderr, "InnoDB: DEBUG: reload_statistics succeeded for %s.\n", |
b4e1fa2c AM |
1013 | + table->name); |
1014 | +#endif | |
1015 | + goto end; | |
1016 | + } | |
1017 | + | |
1018 | + dict_table_stats_unlock(table, RW_X_LATCH); | |
1019 | + } | |
1020 | +#ifdef UNIV_DEBUG | |
1021 | + fprintf(stderr, "InnoDB: DEBUG: update_statistics for %s.\n", | |
1022 | + table->name); | |
1023 | +#endif | |
1024 | + sum_of_index_sizes = 0; | |
1025 | + | |
1026 | /* Find out the sizes of the indexes and how many different values | |
1027 | for the key they approximately have */ | |
1028 | ||
d8778560 | 1029 | @@ -4391,6 +4647,11 @@ |
b4e1fa2c AM |
1030 | index = dict_table_get_next_index(index); |
1031 | } while (index); | |
1032 | ||
1033 | + if (srv_use_sys_stats_table && !((table->flags >> DICT_TF2_SHIFT) & DICT_TF2_TEMPORARY)) { | |
1034 | + /* store statistics to SYS_STATS table */ | |
1035 | + dict_store_statistics(table); | |
1036 | + } | |
1037 | +end: | |
1038 | index = dict_table_get_first_index(table); | |
1039 | ||
1040 | table->stat_n_rows = index->stat_n_diff_key_vals[ | |
d8778560 | 1041 | @@ -4485,7 +4746,8 @@ |
b4e1fa2c AM |
1042 | |
1043 | ut_ad(mutex_own(&(dict_sys->mutex))); | |
1044 | ||
1045 | - dict_update_statistics(table, FALSE /* update even if initialized */); | |
1046 | + if (srv_stats_auto_update) | |
1047 | + dict_update_statistics(table, FALSE /* update even if initialized */, FALSE); | |
1048 | ||
1049 | dict_table_stats_lock(table, RW_S_LATCH); | |
1050 | ||
1051 | diff -ruN a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c | |
1052 | --- a/storage/innobase/dict/dict0load.c 2010-11-03 07:01:13.000000000 +0900 | |
1053 | +++ b/storage/innobase/dict/dict0load.c 2010-12-03 17:19:24.845947460 +0900 | |
d8778560 | 1054 | @@ -50,7 +50,8 @@ |
b4e1fa2c AM |
1055 | "SYS_COLUMNS", |
1056 | "SYS_FIELDS", | |
1057 | "SYS_FOREIGN", | |
1058 | - "SYS_FOREIGN_COLS" | |
1059 | + "SYS_FOREIGN_COLS", | |
1060 | + "SYS_STATS" | |
1061 | }; | |
1062 | /****************************************************************//** | |
1063 | Compare the name of an index column. | |
d8778560 | 1064 | @@ -343,12 +344,13 @@ |
b4e1fa2c AM |
1065 | } |
1066 | ||
1067 | if ((status & DICT_TABLE_UPDATE_STATS) | |
1068 | + && srv_stats_auto_update | |
1069 | && dict_table_get_first_index(*table)) { | |
1070 | ||
1071 | /* Update statistics if DICT_TABLE_UPDATE_STATS | |
1072 | is set */ | |
1073 | dict_update_statistics(*table, FALSE /* update even if | |
1074 | - initialized */); | |
1075 | + initialized */, FALSE); | |
1076 | } | |
1077 | ||
1078 | return(NULL); | |
d8778560 AM |
1079 | @@ -582,6 +584,61 @@ |
1080 | //#endif /* FOREIGN_NOT_USED */ | |
b4e1fa2c | 1081 | |
d8778560 | 1082 | /********************************************************************//** |
b4e1fa2c AM |
1083 | +This function parses a SYS_STATS record and extract necessary |
1084 | +information from the record and return to caller. | |
1085 | +@return error message, or NULL on success */ | |
1086 | +UNIV_INTERN | |
1087 | +const char* | |
1088 | +dict_process_sys_stats_rec( | |
1089 | +/*=============================*/ | |
d8778560 | 1090 | + mem_heap_t* heap __attribute__((unused)), /*!< in/out: heap memory */ |
b4e1fa2c AM |
1091 | + const rec_t* rec, /*!< in: current SYS_STATS rec */ |
1092 | + index_id_t* index_id, /*!< out: INDEX_ID */ | |
1093 | + ulint* key_cols, /*!< out: KEY_COLS */ | |
1094 | + ib_uint64_t* diff_vals) /*!< out: DIFF_VALS */ | |
1095 | +{ | |
1096 | + ulint len; | |
1097 | + const byte* field; | |
1098 | + | |
1099 | + if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, 0))) { | |
1100 | + return("delete-marked record in SYS_STATS"); | |
1101 | + } | |
1102 | + | |
1103 | + if (UNIV_UNLIKELY(rec_get_n_fields_old(rec) != 5)) { | |
1104 | + return("wrong number of columns in SYS_STATS record"); | |
1105 | + } | |
1106 | + | |
1107 | + field = rec_get_nth_field_old(rec, 0/*INDEX_ID*/, &len); | |
1108 | + if (UNIV_UNLIKELY(len != 8)) { | |
1109 | +err_len: | |
1110 | + return("incorrect column length in SYS_STATS"); | |
1111 | + } | |
1112 | + *index_id = mach_read_from_8(field); | |
1113 | + | |
1114 | + field = rec_get_nth_field_old(rec, 1/*KEY_COLS*/, &len); | |
1115 | + if (UNIV_UNLIKELY(len != 4)) { | |
1116 | + goto err_len; | |
1117 | + } | |
1118 | + *key_cols = mach_read_from_4(field); | |
1119 | + | |
1120 | + rec_get_nth_field_offs_old(rec, 2/*DB_TRX_ID*/, &len); | |
1121 | + if (UNIV_UNLIKELY(len != DATA_TRX_ID_LEN && len != UNIV_SQL_NULL)) { | |
1122 | + goto err_len; | |
1123 | + } | |
1124 | + rec_get_nth_field_offs_old(rec, 3/*DB_ROLL_PTR*/, &len); | |
1125 | + if (UNIV_UNLIKELY(len != DATA_ROLL_PTR_LEN && len != UNIV_SQL_NULL)) { | |
1126 | + goto err_len; | |
1127 | + } | |
1128 | + | |
1129 | + field = rec_get_nth_field_old(rec, 4/*DIFF_VALS*/, &len); | |
1130 | + if (UNIV_UNLIKELY(len != 8)) { | |
1131 | + goto err_len; | |
1132 | + } | |
1133 | + *diff_vals = mach_read_from_8(field); | |
1134 | + | |
1135 | + return(NULL); | |
1136 | +} | |
d8778560 | 1137 | +/********************************************************************//** |
b4e1fa2c AM |
1138 | Determine the flags of a table described in SYS_TABLES. |
1139 | @return compressed page size in kilobytes; or 0 if the tablespace is | |
d8778560 | 1140 | uncompressed, ULINT_UNDEFINED on error */ |
b4e1fa2c AM |
1141 | diff -ruN a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc |
1142 | --- a/storage/innobase/handler/ha_innodb.cc 2010-12-03 17:17:03.665960357 +0900 | |
1143 | +++ b/storage/innobase/handler/ha_innodb.cc 2010-12-03 17:22:21.586939783 +0900 | |
1144 | @@ -187,6 +187,7 @@ | |
1145 | static my_bool innobase_rollback_on_timeout = FALSE; | |
1146 | static my_bool innobase_create_status_file = FALSE; | |
1147 | static my_bool innobase_stats_on_metadata = TRUE; | |
1148 | +static my_bool innobase_use_sys_stats_table = FALSE; | |
1149 | ||
1150 | ||
1151 | static char* internal_innobase_data_file_path = NULL; | |
d8778560 | 1152 | @@ -2388,6 +2389,8 @@ |
b4e1fa2c AM |
1153 | goto error; |
1154 | } | |
1155 | ||
1156 | + srv_use_sys_stats_table = (ibool) innobase_use_sys_stats_table; | |
1157 | + | |
1158 | /* -------------- Log files ---------------------------*/ | |
1159 | ||
1160 | /* The default dir for log files is the datadir of MySQL */ | |
d8778560 | 1161 | @@ -5192,6 +5195,10 @@ |
b4e1fa2c AM |
1162 | |
1163 | error = row_insert_for_mysql((byte*) record, prebuilt); | |
1164 | ||
1165 | +#ifdef EXTENDED_FOR_USERSTAT | |
1166 | + if (error == DB_SUCCESS) rows_changed++; | |
1167 | +#endif | |
1168 | + | |
1169 | /* Handle duplicate key errors */ | |
1170 | if (auto_inc_used) { | |
1171 | ulint err; | |
d8778560 | 1172 | @@ -5528,6 +5535,10 @@ |
b4e1fa2c AM |
1173 | } |
1174 | } | |
1175 | ||
1176 | +#ifdef EXTENDED_FOR_USERSTAT | |
1177 | + if (error == DB_SUCCESS) rows_changed++; | |
1178 | +#endif | |
1179 | + | |
1180 | innodb_srv_conc_exit_innodb(trx); | |
1181 | ||
1182 | error = convert_error_code_to_mysql(error, | |
d8778560 | 1183 | @@ -5581,6 +5592,10 @@ |
b4e1fa2c AM |
1184 | |
1185 | error = row_update_for_mysql((byte*) record, prebuilt); | |
1186 | ||
1187 | +#ifdef EXTENDED_FOR_USERSTAT | |
1188 | + if (error == DB_SUCCESS) rows_changed++; | |
1189 | +#endif | |
1190 | + | |
1191 | innodb_srv_conc_exit_innodb(trx); | |
1192 | ||
1193 | error = convert_error_code_to_mysql( | |
d8778560 AM |
1194 | @@ -5899,6 +5914,11 @@ |
1195 | case DB_SUCCESS: | |
1196 | error = 0; | |
1197 | table->status = 0; | |
1198 | +#ifdef EXTENDED_FOR_USERSTAT | |
1199 | + rows_read++; | |
1200 | + if (active_index >= 0 && active_index < MAX_KEY) | |
1201 | + index_rows_read[active_index]++; | |
1202 | +#endif | |
1203 | break; | |
1204 | case DB_RECORD_NOT_FOUND: | |
1205 | error = HA_ERR_KEY_NOT_FOUND; | |
1206 | @@ -6108,6 +6128,11 @@ | |
b4e1fa2c AM |
1207 | case DB_SUCCESS: |
1208 | error = 0; | |
1209 | table->status = 0; | |
1210 | +#ifdef EXTENDED_FOR_USERSTAT | |
1211 | + rows_read++; | |
1212 | + if (active_index >= 0 && active_index < MAX_KEY) | |
1213 | + index_rows_read[active_index]++; | |
1214 | +#endif | |
1215 | break; | |
1216 | case DB_RECORD_NOT_FOUND: | |
1217 | error = HA_ERR_END_OF_FILE; | |
d8778560 | 1218 | @@ -7999,11 +8024,31 @@ |
b4e1fa2c AM |
1219 | /* In sql_show we call with this flag: update |
1220 | then statistics so that they are up-to-date */ | |
1221 | ||
1222 | + if (srv_use_sys_stats_table && !((ib_table->flags >> DICT_TF2_SHIFT) & DICT_TF2_TEMPORARY) | |
1223 | + && called_from_analyze) { | |
1224 | + /* If the indexes on the table don't have enough rows in SYS_STATS system table, */ | |
1225 | + /* they need to be created. */ | |
1226 | + dict_index_t* index; | |
1227 | + | |
1228 | + prebuilt->trx->op_info = "confirming rows of SYS_STATS to store statistics"; | |
1229 | + | |
1230 | + ut_a(prebuilt->trx->conc_state == TRX_NOT_STARTED); | |
1231 | + | |
1232 | + for (index = dict_table_get_first_index(ib_table); | |
1233 | + index != NULL; | |
1234 | + index = dict_table_get_next_index(index)) { | |
1235 | + row_insert_stats_for_mysql(index, prebuilt->trx); | |
1236 | + innobase_commit_low(prebuilt->trx); | |
1237 | + } | |
1238 | + | |
1239 | + ut_a(prebuilt->trx->conc_state == TRX_NOT_STARTED); | |
1240 | + } | |
1241 | + | |
1242 | prebuilt->trx->op_info = "updating table statistics"; | |
1243 | ||
1244 | dict_update_statistics(ib_table, | |
1245 | FALSE /* update even if stats | |
1246 | - are initialized */); | |
1247 | + are initialized */, called_from_analyze); | |
1248 | ||
1249 | prebuilt->trx->op_info = "returning various info to MySQL"; | |
1250 | } | |
d8778560 | 1251 | @@ -8081,7 +8126,7 @@ |
b4e1fa2c AM |
1252 | are asked by MySQL to avoid locking. Another reason to |
1253 | avoid the call is that it uses quite a lot of CPU. | |
1254 | See Bug#38185. */ | |
d8778560 AM |
1255 | - if (flag & HA_STATUS_NO_LOCK |
1256 | + if (flag & HA_STATUS_NO_LOCK || !srv_stats_update_need_lock | |
1257 | || !(flag & HA_STATUS_VARIABLE_EXTRA)) { | |
b4e1fa2c AM |
1258 | /* We do not update delete_length if no |
1259 | locking is requested so the "old" value can | |
d8778560 | 1260 | @@ -11281,6 +11326,45 @@ |
b4e1fa2c AM |
1261 | "The number of index pages to sample when calculating statistics (default 8)", |
1262 | NULL, NULL, 8, 1, ~0ULL, 0); | |
1263 | ||
1264 | +const char *innobase_stats_method_names[]= | |
1265 | +{ | |
1266 | + "nulls_equal", | |
1267 | + "nulls_unequal", | |
1268 | + "nulls_ignored", | |
1269 | + NullS | |
1270 | +}; | |
1271 | +TYPELIB innobase_stats_method_typelib= | |
1272 | +{ | |
1273 | + array_elements(innobase_stats_method_names) - 1, "innobase_stats_method_typelib", | |
1274 | + innobase_stats_method_names, NULL | |
1275 | +}; | |
1276 | +static MYSQL_SYSVAR_ENUM(stats_method, srv_stats_method, | |
1277 | + PLUGIN_VAR_RQCMDARG, | |
1278 | + "Specifies how InnoDB index statistics collection code should threat NULLs. " | |
1279 | + "Possible values of name are same to for 'myisam_stats_method'. " | |
1280 | + "This is startup parameter.", | |
1281 | + NULL, NULL, 0, &innobase_stats_method_typelib); | |
1282 | + | |
1283 | +static MYSQL_SYSVAR_ULONG(stats_auto_update, srv_stats_auto_update, | |
1284 | + PLUGIN_VAR_RQCMDARG, | |
1285 | + "Enable/Disable InnoDB's auto update statistics of indexes. " | |
1286 | + "(except for ANALYZE TABLE command) 0:disable 1:enable", | |
1287 | + NULL, NULL, 1, 0, 1, 0); | |
1288 | + | |
1289 | +static MYSQL_SYSVAR_ULONG(stats_update_need_lock, srv_stats_update_need_lock, | |
1290 | + PLUGIN_VAR_RQCMDARG, | |
1291 | + "Enable/Disable InnoDB's update statistics which needs to lock dictionary. " | |
1292 | + "e.g. Data_free.", | |
1293 | + NULL, NULL, 1, 0, 1, 0); | |
1294 | + | |
1295 | +static MYSQL_SYSVAR_BOOL(use_sys_stats_table, innobase_use_sys_stats_table, | |
1296 | + PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY, | |
1297 | + "Enable to use SYS_STATS system table to store statistics statically, " | |
1298 | + "And avoids to calculate statistics at every first open of the tables. " | |
1299 | + "This option may make the opportunities of update statistics less. " | |
1300 | + "So you should use ANALYZE TABLE command intentionally.", | |
1301 | + NULL, NULL, FALSE); | |
1302 | + | |
1303 | static MYSQL_SYSVAR_BOOL(adaptive_hash_index, btr_search_enabled, | |
1304 | PLUGIN_VAR_OPCMDARG, | |
1305 | "Enable InnoDB adaptive hash index (enabled by default). " | |
d8778560 | 1306 | @@ -11604,6 +11688,10 @@ |
a9ee80b9 | 1307 | MYSQL_SYSVAR(recovery_update_relay_log), |
b4e1fa2c AM |
1308 | MYSQL_SYSVAR(rollback_on_timeout), |
1309 | MYSQL_SYSVAR(stats_on_metadata), | |
1310 | + MYSQL_SYSVAR(stats_method), | |
1311 | + MYSQL_SYSVAR(stats_auto_update), | |
1312 | + MYSQL_SYSVAR(stats_update_need_lock), | |
1313 | + MYSQL_SYSVAR(use_sys_stats_table), | |
1314 | MYSQL_SYSVAR(stats_sample_pages), | |
1315 | MYSQL_SYSVAR(adaptive_hash_index), | |
1316 | MYSQL_SYSVAR(replication_delay), | |
d8778560 | 1317 | @@ -11672,7 +11760,10 @@ |
b4e1fa2c AM |
1318 | i_s_innodb_sys_columns, |
1319 | i_s_innodb_sys_fields, | |
1320 | i_s_innodb_sys_foreign, | |
1321 | -i_s_innodb_sys_foreign_cols | |
1322 | +i_s_innodb_sys_foreign_cols, | |
1323 | +i_s_innodb_sys_stats, | |
1324 | +i_s_innodb_table_stats, | |
1325 | +i_s_innodb_index_stats | |
1326 | mysql_declare_plugin_end; | |
1327 | ||
1328 | /** @brief Initialize the default value of innodb_commit_concurrency. | |
1329 | diff -ruN a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc | |
1330 | --- a/storage/innobase/handler/i_s.cc 2010-12-03 17:17:03.666956117 +0900 | |
1331 | +++ b/storage/innobase/handler/i_s.cc 2010-12-03 17:19:24.880964526 +0900 | |
1332 | @@ -49,6 +49,7 @@ | |
1333 | #include "trx0trx.h" /* for TRX_QUE_STATE_STR_MAX_LEN */ | |
1334 | #include "trx0rseg.h" /* for trx_rseg_struct */ | |
1335 | #include "trx0sys.h" /* for trx_sys */ | |
1336 | +#include "dict0dict.h" /* for dict_sys */ | |
1337 | } | |
1338 | ||
1339 | static const char plugin_author[] = "Innobase Oy"; | |
d8778560 | 1340 | @@ -3457,6 +3458,203 @@ |
b4e1fa2c AM |
1341 | STRUCT_FLD(__reserved1, NULL) |
1342 | }; | |
1343 | ||
1344 | +/* Fields of the dynamic table INFORMATION_SCHEMA.innodb_sys_stats */ | |
1345 | +static ST_FIELD_INFO innodb_sys_stats_fields_info[] = | |
1346 | +{ | |
1347 | +#define SYS_STATS_INDEX_ID 0 | |
1348 | + {STRUCT_FLD(field_name, "INDEX_ID"), | |
1349 | + STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), | |
1350 | + STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), | |
1351 | + STRUCT_FLD(value, 0), | |
1352 | + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), | |
1353 | + STRUCT_FLD(old_name, ""), | |
1354 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1355 | + | |
1356 | +#define SYS_STATS_KEY_COLS 1 | |
1357 | + {STRUCT_FLD(field_name, "KEY_COLS"), | |
1358 | + STRUCT_FLD(field_length, MY_INT32_NUM_DECIMAL_DIGITS), | |
1359 | + STRUCT_FLD(field_type, MYSQL_TYPE_LONG), | |
1360 | + STRUCT_FLD(value, 0), | |
1361 | + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), | |
1362 | + STRUCT_FLD(old_name, ""), | |
1363 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1364 | + | |
1365 | +#define SYS_STATS_DIFF_VALS 2 | |
1366 | + {STRUCT_FLD(field_name, "DIFF_VALS"), | |
1367 | + STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), | |
1368 | + STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), | |
1369 | + STRUCT_FLD(value, 0), | |
1370 | + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), | |
1371 | + STRUCT_FLD(old_name, ""), | |
1372 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1373 | + | |
1374 | + END_OF_ST_FIELD_INFO | |
1375 | +}; | |
1376 | +/**********************************************************************//** | |
1377 | +Function to fill information_schema.innodb_sys_stats | |
1378 | +@return 0 on success */ | |
1379 | +static | |
1380 | +int | |
1381 | +i_s_dict_fill_sys_stats( | |
1382 | +/*====================*/ | |
1383 | + THD* thd, /*!< in: thread */ | |
1384 | + index_id_t index_id, /*!< in: INDEX_ID */ | |
1385 | + ulint key_cols, /*!< in: KEY_COLS */ | |
1386 | + ib_uint64_t diff_vals, /*!< in: DIFF_VALS */ | |
1387 | + TABLE* table_to_fill) /*!< in/out: fill this table */ | |
1388 | +{ | |
1389 | + Field** fields; | |
1390 | + | |
1391 | + DBUG_ENTER("i_s_dict_fill_sys_stats"); | |
1392 | + | |
1393 | + fields = table_to_fill->field; | |
1394 | + | |
1395 | + OK(fields[SYS_STATS_INDEX_ID]->store(longlong(index_id), TRUE)); | |
1396 | + | |
1397 | + OK(fields[SYS_STATS_KEY_COLS]->store(key_cols)); | |
1398 | + | |
1399 | + OK(fields[SYS_STATS_DIFF_VALS]->store(longlong(diff_vals), TRUE)); | |
1400 | + | |
1401 | + OK(schema_table_store_record(thd, table_to_fill)); | |
1402 | + | |
1403 | + DBUG_RETURN(0); | |
1404 | +} | |
1405 | +/*******************************************************************//** | |
1406 | +Function to populate INFORMATION_SCHEMA.innodb_sys_stats table. | |
1407 | +@return 0 on success */ | |
1408 | +static | |
1409 | +int | |
1410 | +i_s_sys_stats_fill_table( | |
1411 | +/*=====================*/ | |
1412 | + THD* thd, /*!< in: thread */ | |
1413 | + TABLE_LIST* tables, /*!< in/out: tables to fill */ | |
1414 | + COND* cond) /*!< in: condition (not used) */ | |
1415 | +{ | |
1416 | + btr_pcur_t pcur; | |
1417 | + const rec_t* rec; | |
1418 | + mem_heap_t* heap; | |
1419 | + mtr_t mtr; | |
1420 | + | |
1421 | + DBUG_ENTER("i_s_sys_stats_fill_table"); | |
1422 | + | |
1423 | + /* deny access to non-superusers */ | |
1424 | + if (check_global_access(thd, PROCESS_ACL)) { | |
1425 | + DBUG_RETURN(0); | |
1426 | + } | |
1427 | + | |
1428 | + heap = mem_heap_create(1000); | |
1429 | + mutex_enter(&dict_sys->mutex); | |
1430 | + mtr_start(&mtr); | |
1431 | + | |
1432 | + rec = dict_startscan_system(&pcur, &mtr, SYS_STATS); | |
1433 | + | |
1434 | + while (rec) { | |
1435 | + const char* err_msg; | |
1436 | + index_id_t index_id; | |
1437 | + ulint key_cols; | |
1438 | + ib_uint64_t diff_vals; | |
1439 | + | |
1440 | + /* Extract necessary information from a SYS_FOREIGN_COLS row */ | |
1441 | + err_msg = dict_process_sys_stats_rec( | |
1442 | + heap, rec, &index_id, &key_cols, &diff_vals); | |
1443 | + | |
1444 | + mtr_commit(&mtr); | |
1445 | + mutex_exit(&dict_sys->mutex); | |
1446 | + | |
1447 | + if (!err_msg) { | |
1448 | + i_s_dict_fill_sys_stats( | |
1449 | + thd, index_id, key_cols, diff_vals, | |
1450 | + tables->table); | |
1451 | + } else { | |
1452 | + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, | |
1453 | + ER_CANT_FIND_SYSTEM_REC, | |
1454 | + err_msg); | |
1455 | + } | |
1456 | + | |
1457 | + mem_heap_empty(heap); | |
1458 | + | |
1459 | + /* Get the next record */ | |
1460 | + mutex_enter(&dict_sys->mutex); | |
1461 | + mtr_start(&mtr); | |
1462 | + rec = dict_getnext_system(&pcur, &mtr); | |
1463 | + } | |
1464 | + | |
1465 | + mtr_commit(&mtr); | |
1466 | + mutex_exit(&dict_sys->mutex); | |
1467 | + mem_heap_free(heap); | |
1468 | + | |
1469 | + DBUG_RETURN(0); | |
1470 | +} | |
1471 | +/*******************************************************************//** | |
1472 | +Bind the dynamic table INFORMATION_SCHEMA.innodb_sys_stats | |
1473 | +@return 0 on success */ | |
1474 | +static | |
1475 | +int | |
1476 | +innodb_sys_stats_init( | |
1477 | +/*========================*/ | |
1478 | + void* p) /*!< in/out: table schema object */ | |
1479 | +{ | |
1480 | + ST_SCHEMA_TABLE* schema; | |
1481 | + | |
1482 | + DBUG_ENTER("innodb_sys_stats_init"); | |
1483 | + | |
1484 | + schema = (ST_SCHEMA_TABLE*) p; | |
1485 | + | |
1486 | + schema->fields_info = innodb_sys_stats_fields_info; | |
1487 | + schema->fill_table = i_s_sys_stats_fill_table; | |
1488 | + | |
1489 | + DBUG_RETURN(0); | |
1490 | +} | |
1491 | + | |
1492 | +UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_stats = | |
1493 | +{ | |
1494 | + /* the plugin type (a MYSQL_XXX_PLUGIN value) */ | |
1495 | + /* int */ | |
1496 | + STRUCT_FLD(type, MYSQL_INFORMATION_SCHEMA_PLUGIN), | |
1497 | + | |
1498 | + /* pointer to type-specific plugin descriptor */ | |
1499 | + /* void* */ | |
1500 | + STRUCT_FLD(info, &i_s_info), | |
1501 | + | |
1502 | + /* plugin name */ | |
1503 | + /* const char* */ | |
1504 | + STRUCT_FLD(name, "INNODB_SYS_STATS"), | |
1505 | + | |
1506 | + /* plugin author (for SHOW PLUGINS) */ | |
1507 | + /* const char* */ | |
1508 | + STRUCT_FLD(author, plugin_author), | |
1509 | + | |
1510 | + /* general descriptive text (for SHOW PLUGINS) */ | |
1511 | + /* const char* */ | |
1512 | + STRUCT_FLD(descr, "XtraDB SYS_STATS table"), | |
1513 | + | |
1514 | + /* the plugin license (PLUGIN_LICENSE_XXX) */ | |
1515 | + /* int */ | |
1516 | + STRUCT_FLD(license, PLUGIN_LICENSE_GPL), | |
1517 | + | |
1518 | + /* the function to invoke when plugin is loaded */ | |
1519 | + /* int (*)(void*); */ | |
1520 | + STRUCT_FLD(init, innodb_sys_stats_init), | |
1521 | + | |
1522 | + /* the function to invoke when plugin is unloaded */ | |
1523 | + /* int (*)(void*); */ | |
1524 | + STRUCT_FLD(deinit, i_s_common_deinit), | |
1525 | + | |
1526 | + /* plugin version (for SHOW PLUGINS) */ | |
1527 | + /* unsigned int */ | |
1528 | + STRUCT_FLD(version, INNODB_VERSION_SHORT), | |
1529 | + | |
1530 | + /* struct st_mysql_show_var* */ | |
1531 | + STRUCT_FLD(status_vars, NULL), | |
1532 | + | |
1533 | + /* struct st_mysql_sys_var** */ | |
1534 | + STRUCT_FLD(system_vars, NULL), | |
1535 | + | |
1536 | + /* reserved for dependency checking */ | |
1537 | + /* void* */ | |
1538 | + STRUCT_FLD(__reserved1, NULL) | |
1539 | +}; | |
1540 | + | |
1541 | /*********************************************************************** | |
1542 | */ | |
1543 | static ST_FIELD_INFO i_s_innodb_rseg_fields_info[] = | |
d8778560 | 1544 | @@ -3619,3 +3817,347 @@ |
b4e1fa2c AM |
1545 | /* void* */ |
1546 | STRUCT_FLD(__reserved1, NULL) | |
1547 | }; | |
1548 | + | |
1549 | +/*********************************************************************** | |
1550 | +*/ | |
1551 | +static ST_FIELD_INFO i_s_innodb_table_stats_info[] = | |
1552 | +{ | |
1553 | + {STRUCT_FLD(field_name, "table_schema"), | |
1554 | + STRUCT_FLD(field_length, NAME_LEN), | |
1555 | + STRUCT_FLD(field_type, MYSQL_TYPE_STRING), | |
1556 | + STRUCT_FLD(value, 0), | |
1557 | + STRUCT_FLD(field_flags, 0), | |
1558 | + STRUCT_FLD(old_name, ""), | |
1559 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1560 | + | |
1561 | + {STRUCT_FLD(field_name, "table_name"), | |
1562 | + STRUCT_FLD(field_length, NAME_LEN), | |
1563 | + STRUCT_FLD(field_type, MYSQL_TYPE_STRING), | |
1564 | + STRUCT_FLD(value, 0), | |
1565 | + STRUCT_FLD(field_flags, 0), | |
1566 | + STRUCT_FLD(old_name, ""), | |
1567 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1568 | + | |
1569 | + {STRUCT_FLD(field_name, "rows"), | |
1570 | + STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), | |
1571 | + STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), | |
1572 | + STRUCT_FLD(value, 0), | |
1573 | + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), | |
1574 | + STRUCT_FLD(old_name, ""), | |
1575 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1576 | + | |
1577 | + {STRUCT_FLD(field_name, "clust_size"), | |
1578 | + STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), | |
1579 | + STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), | |
1580 | + STRUCT_FLD(value, 0), | |
1581 | + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), | |
1582 | + STRUCT_FLD(old_name, ""), | |
1583 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1584 | + | |
1585 | + {STRUCT_FLD(field_name, "other_size"), | |
1586 | + STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), | |
1587 | + STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), | |
1588 | + STRUCT_FLD(value, 0), | |
1589 | + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), | |
1590 | + STRUCT_FLD(old_name, ""), | |
1591 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1592 | + | |
1593 | + {STRUCT_FLD(field_name, "modified"), | |
1594 | + STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), | |
1595 | + STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), | |
1596 | + STRUCT_FLD(value, 0), | |
1597 | + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), | |
1598 | + STRUCT_FLD(old_name, ""), | |
1599 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1600 | + | |
1601 | + END_OF_ST_FIELD_INFO | |
1602 | +}; | |
1603 | + | |
1604 | +static ST_FIELD_INFO i_s_innodb_index_stats_info[] = | |
1605 | +{ | |
1606 | + {STRUCT_FLD(field_name, "table_schema"), | |
1607 | + STRUCT_FLD(field_length, NAME_LEN), | |
1608 | + STRUCT_FLD(field_type, MYSQL_TYPE_STRING), | |
1609 | + STRUCT_FLD(value, 0), | |
1610 | + STRUCT_FLD(field_flags, 0), | |
1611 | + STRUCT_FLD(old_name, ""), | |
1612 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1613 | + | |
1614 | + {STRUCT_FLD(field_name, "table_name"), | |
1615 | + STRUCT_FLD(field_length, NAME_LEN), | |
1616 | + STRUCT_FLD(field_type, MYSQL_TYPE_STRING), | |
1617 | + STRUCT_FLD(value, 0), | |
1618 | + STRUCT_FLD(field_flags, 0), | |
1619 | + STRUCT_FLD(old_name, ""), | |
1620 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1621 | + | |
1622 | + {STRUCT_FLD(field_name, "index_name"), | |
1623 | + STRUCT_FLD(field_length, NAME_LEN), | |
1624 | + STRUCT_FLD(field_type, MYSQL_TYPE_STRING), | |
1625 | + STRUCT_FLD(value, 0), | |
1626 | + STRUCT_FLD(field_flags, 0), | |
1627 | + STRUCT_FLD(old_name, ""), | |
1628 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1629 | + | |
1630 | + {STRUCT_FLD(field_name, "fields"), | |
1631 | + STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), | |
1632 | + STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), | |
1633 | + STRUCT_FLD(value, 0), | |
1634 | + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), | |
1635 | + STRUCT_FLD(old_name, ""), | |
1636 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1637 | + | |
1638 | + {STRUCT_FLD(field_name, "rows_per_key"), | |
1639 | + STRUCT_FLD(field_length, 256), | |
1640 | + STRUCT_FLD(field_type, MYSQL_TYPE_STRING), | |
1641 | + STRUCT_FLD(value, 0), | |
1642 | + STRUCT_FLD(field_flags, 0), | |
1643 | + STRUCT_FLD(old_name, ""), | |
1644 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1645 | + | |
1646 | + {STRUCT_FLD(field_name, "index_total_pages"), | |
1647 | + STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), | |
1648 | + STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), | |
1649 | + STRUCT_FLD(value, 0), | |
1650 | + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), | |
1651 | + STRUCT_FLD(old_name, ""), | |
1652 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1653 | + | |
1654 | + {STRUCT_FLD(field_name, "index_leaf_pages"), | |
1655 | + STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), | |
1656 | + STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), | |
1657 | + STRUCT_FLD(value, 0), | |
1658 | + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), | |
1659 | + STRUCT_FLD(old_name, ""), | |
1660 | + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, | |
1661 | + | |
1662 | + END_OF_ST_FIELD_INFO | |
1663 | +}; | |
1664 | + | |
1665 | +static | |
1666 | +int | |
1667 | +i_s_innodb_table_stats_fill( | |
1668 | +/*========================*/ | |
1669 | + THD* thd, | |
1670 | + TABLE_LIST* tables, | |
1671 | + COND* cond) | |
1672 | +{ | |
1673 | + TABLE* i_s_table = (TABLE *) tables->table; | |
1674 | + int status = 0; | |
1675 | + dict_table_t* table; | |
1676 | + | |
1677 | + DBUG_ENTER("i_s_innodb_table_stats_fill"); | |
1678 | + | |
1679 | + /* deny access to non-superusers */ | |
1680 | + if (check_global_access(thd, PROCESS_ACL)) { | |
1681 | + DBUG_RETURN(0); | |
1682 | + } | |
1683 | + | |
1684 | + mutex_enter(&(dict_sys->mutex)); | |
1685 | + | |
1686 | + table = UT_LIST_GET_FIRST(dict_sys->table_LRU); | |
1687 | + | |
1688 | + while (table) { | |
1689 | + char buf[NAME_LEN * 2 + 2]; | |
1690 | + char* ptr; | |
1691 | + | |
1692 | + if (table->stat_clustered_index_size == 0) { | |
1693 | + table = UT_LIST_GET_NEXT(table_LRU, table); | |
1694 | + continue; | |
1695 | + } | |
1696 | + | |
1697 | + buf[NAME_LEN * 2 + 1] = 0; | |
1698 | + strncpy(buf, table->name, NAME_LEN * 2 + 1); | |
1699 | + ptr = strchr(buf, '/'); | |
1700 | + if (ptr) { | |
1701 | + *ptr = '\0'; | |
1702 | + ++ptr; | |
1703 | + } else { | |
1704 | + ptr = buf; | |
1705 | + } | |
1706 | + | |
1707 | + field_store_string(i_s_table->field[0], buf); | |
1708 | + field_store_string(i_s_table->field[1], ptr); | |
1709 | + i_s_table->field[2]->store(table->stat_n_rows); | |
1710 | + i_s_table->field[3]->store(table->stat_clustered_index_size); | |
1711 | + i_s_table->field[4]->store(table->stat_sum_of_other_index_sizes); | |
1712 | + i_s_table->field[5]->store(table->stat_modified_counter); | |
1713 | + | |
1714 | + if (schema_table_store_record(thd, i_s_table)) { | |
1715 | + status = 1; | |
1716 | + break; | |
1717 | + } | |
1718 | + | |
1719 | + table = UT_LIST_GET_NEXT(table_LRU, table); | |
1720 | + } | |
1721 | + | |
1722 | + mutex_exit(&(dict_sys->mutex)); | |
1723 | + | |
1724 | + DBUG_RETURN(status); | |
1725 | +} | |
1726 | + | |
1727 | +static | |
1728 | +int | |
1729 | +i_s_innodb_index_stats_fill( | |
1730 | +/*========================*/ | |
1731 | + THD* thd, | |
1732 | + TABLE_LIST* tables, | |
1733 | + COND* cond) | |
1734 | +{ | |
1735 | + TABLE* i_s_table = (TABLE *) tables->table; | |
1736 | + int status = 0; | |
1737 | + dict_table_t* table; | |
1738 | + dict_index_t* index; | |
1739 | + | |
1740 | + DBUG_ENTER("i_s_innodb_index_stats_fill"); | |
1741 | + | |
1742 | + /* deny access to non-superusers */ | |
1743 | + if (check_global_access(thd, PROCESS_ACL)) { | |
1744 | + DBUG_RETURN(0); | |
1745 | + } | |
1746 | + | |
1747 | + mutex_enter(&(dict_sys->mutex)); | |
1748 | + | |
1749 | + table = UT_LIST_GET_FIRST(dict_sys->table_LRU); | |
1750 | + | |
1751 | + while (table) { | |
1752 | + if (table->stat_clustered_index_size == 0) { | |
1753 | + table = UT_LIST_GET_NEXT(table_LRU, table); | |
1754 | + continue; | |
1755 | + } | |
1756 | + | |
1757 | + ib_int64_t n_rows = table->stat_n_rows; | |
1758 | + | |
1759 | + if (n_rows < 0) { | |
1760 | + n_rows = 0; | |
1761 | + } | |
1762 | + | |
1763 | + index = dict_table_get_first_index(table); | |
1764 | + | |
1765 | + while (index) { | |
1766 | + char buff[256+1]; | |
1767 | + char row_per_keys[256+1]; | |
1768 | + char buf[NAME_LEN * 2 + 2]; | |
1769 | + char* ptr; | |
1770 | + ulint i; | |
1771 | + | |
1772 | + buf[NAME_LEN * 2 + 1] = 0; | |
1773 | + strncpy(buf, table->name, NAME_LEN * 2 + 1); | |
1774 | + ptr = strchr(buf, '/'); | |
1775 | + if (ptr) { | |
1776 | + *ptr = '\0'; | |
1777 | + ++ptr; | |
1778 | + } else { | |
1779 | + ptr = buf; | |
1780 | + } | |
1781 | + | |
1782 | + field_store_string(i_s_table->field[0], buf); | |
1783 | + field_store_string(i_s_table->field[1], ptr); | |
1784 | + field_store_string(i_s_table->field[2], index->name); | |
1785 | + i_s_table->field[3]->store(index->n_uniq); | |
1786 | + | |
1787 | + row_per_keys[0] = '\0'; | |
1788 | + | |
1789 | + /* It is remained optimistic operation still for now */ | |
1790 | + //dict_index_stat_mutex_enter(index); | |
1791 | + if (index->stat_n_diff_key_vals) { | |
1792 | + for (i = 1; i <= index->n_uniq; i++) { | |
1793 | + ib_int64_t rec_per_key; | |
1794 | + if (index->stat_n_diff_key_vals[i]) { | |
1795 | + rec_per_key = n_rows / index->stat_n_diff_key_vals[i]; | |
1796 | + } else { | |
1797 | + rec_per_key = n_rows; | |
1798 | + } | |
1799 | + ut_snprintf(buff, 256, (i == index->n_uniq)?"%llu":"%llu, ", | |
1800 | + rec_per_key); | |
1801 | + strncat(row_per_keys, buff, 256 - strlen(row_per_keys)); | |
1802 | + } | |
1803 | + } | |
1804 | + //dict_index_stat_mutex_exit(index); | |
1805 | + | |
1806 | + field_store_string(i_s_table->field[4], row_per_keys); | |
1807 | + | |
1808 | + i_s_table->field[5]->store(index->stat_index_size); | |
1809 | + i_s_table->field[6]->store(index->stat_n_leaf_pages); | |
1810 | + | |
1811 | + if (schema_table_store_record(thd, i_s_table)) { | |
1812 | + status = 1; | |
1813 | + break; | |
1814 | + } | |
1815 | + | |
1816 | + index = dict_table_get_next_index(index); | |
1817 | + } | |
1818 | + | |
1819 | + if (status == 1) { | |
1820 | + break; | |
1821 | + } | |
1822 | + | |
1823 | + table = UT_LIST_GET_NEXT(table_LRU, table); | |
1824 | + } | |
1825 | + | |
1826 | + mutex_exit(&(dict_sys->mutex)); | |
1827 | + | |
1828 | + DBUG_RETURN(status); | |
1829 | +} | |
1830 | + | |
1831 | +static | |
1832 | +int | |
1833 | +i_s_innodb_table_stats_init( | |
1834 | +/*========================*/ | |
1835 | + void* p) | |
1836 | +{ | |
1837 | + DBUG_ENTER("i_s_innodb_table_stats_init"); | |
1838 | + ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p; | |
1839 | + | |
1840 | + schema->fields_info = i_s_innodb_table_stats_info; | |
1841 | + schema->fill_table = i_s_innodb_table_stats_fill; | |
1842 | + | |
1843 | + DBUG_RETURN(0); | |
1844 | +} | |
1845 | + | |
1846 | +static | |
1847 | +int | |
1848 | +i_s_innodb_index_stats_init( | |
1849 | +/*========================*/ | |
1850 | + void* p) | |
1851 | +{ | |
1852 | + DBUG_ENTER("i_s_innodb_index_stats_init"); | |
1853 | + ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p; | |
1854 | + | |
1855 | + schema->fields_info = i_s_innodb_index_stats_info; | |
1856 | + schema->fill_table = i_s_innodb_index_stats_fill; | |
1857 | + | |
1858 | + DBUG_RETURN(0); | |
1859 | +} | |
1860 | + | |
1861 | +UNIV_INTERN struct st_mysql_plugin i_s_innodb_table_stats = | |
1862 | +{ | |
1863 | + STRUCT_FLD(type, MYSQL_INFORMATION_SCHEMA_PLUGIN), | |
1864 | + STRUCT_FLD(info, &i_s_info), | |
1865 | + STRUCT_FLD(name, "INNODB_TABLE_STATS"), | |
1866 | + STRUCT_FLD(author, plugin_author), | |
1867 | + STRUCT_FLD(descr, "InnoDB table statistics in memory"), | |
1868 | + STRUCT_FLD(license, PLUGIN_LICENSE_GPL), | |
1869 | + STRUCT_FLD(init, i_s_innodb_table_stats_init), | |
1870 | + STRUCT_FLD(deinit, i_s_common_deinit), | |
1871 | + STRUCT_FLD(version, 0x0100 /* 1.0 */), | |
1872 | + STRUCT_FLD(status_vars, NULL), | |
1873 | + STRUCT_FLD(system_vars, NULL), | |
1874 | + STRUCT_FLD(__reserved1, NULL) | |
1875 | +}; | |
1876 | + | |
1877 | +UNIV_INTERN struct st_mysql_plugin i_s_innodb_index_stats = | |
1878 | +{ | |
1879 | + STRUCT_FLD(type, MYSQL_INFORMATION_SCHEMA_PLUGIN), | |
1880 | + STRUCT_FLD(info, &i_s_info), | |
1881 | + STRUCT_FLD(name, "INNODB_INDEX_STATS"), | |
1882 | + STRUCT_FLD(author, plugin_author), | |
1883 | + STRUCT_FLD(descr, "InnoDB index statistics in memory"), | |
1884 | + STRUCT_FLD(license, PLUGIN_LICENSE_GPL), | |
1885 | + STRUCT_FLD(init, i_s_innodb_index_stats_init), | |
1886 | + STRUCT_FLD(deinit, i_s_common_deinit), | |
1887 | + STRUCT_FLD(version, 0x0100 /* 1.0 */), | |
1888 | + STRUCT_FLD(status_vars, NULL), | |
1889 | + STRUCT_FLD(system_vars, NULL), | |
1890 | + STRUCT_FLD(__reserved1, NULL) | |
1891 | +}; | |
1892 | diff -ruN a/storage/innobase/handler/i_s.h b/storage/innobase/handler/i_s.h | |
1893 | --- a/storage/innobase/handler/i_s.h 2010-12-03 17:17:03.668953884 +0900 | |
1894 | +++ b/storage/innobase/handler/i_s.h 2010-12-03 17:19:24.882947826 +0900 | |
1895 | @@ -41,5 +41,8 @@ | |
1896 | extern struct st_mysql_plugin i_s_innodb_sys_foreign; | |
1897 | extern struct st_mysql_plugin i_s_innodb_sys_foreign_cols; | |
1898 | extern struct st_mysql_plugin i_s_innodb_rseg; | |
1899 | +extern struct st_mysql_plugin i_s_innodb_sys_stats; | |
1900 | +extern struct st_mysql_plugin i_s_innodb_table_stats; | |
1901 | +extern struct st_mysql_plugin i_s_innodb_index_stats; | |
1902 | ||
1903 | #endif /* i_s_h */ | |
1904 | diff -ruN a/storage/innobase/include/dict0boot.h b/storage/innobase/include/dict0boot.h | |
1905 | --- a/storage/innobase/include/dict0boot.h 2010-11-03 07:01:13.000000000 +0900 | |
1906 | +++ b/storage/innobase/include/dict0boot.h 2010-12-03 17:19:24.885947372 +0900 | |
1907 | @@ -104,6 +104,7 @@ | |
1908 | #define DICT_COLUMNS_ID 2 | |
1909 | #define DICT_INDEXES_ID 3 | |
1910 | #define DICT_FIELDS_ID 4 | |
1911 | +#define DICT_STATS_ID 6 | |
1912 | /* The following is a secondary index on SYS_TABLES */ | |
1913 | #define DICT_TABLE_IDS_ID 5 | |
1914 | ||
1915 | @@ -131,10 +132,13 @@ | |
1916 | #define DICT_HDR_INDEXES 44 /* Root of the index index tree */ | |
1917 | #define DICT_HDR_FIELDS 48 /* Root of the index field | |
1918 | index tree */ | |
1919 | +#define DICT_HDR_STATS 52 /* Root of the stats tree */ | |
1920 | ||
1921 | #define DICT_HDR_FSEG_HEADER 56 /* Segment header for the tablespace | |
1922 | segment into which the dictionary | |
1923 | header is created */ | |
1924 | + | |
1925 | +#define DICT_HDR_XTRADB_MARK 256 /* Flag to distinguish expansion of XtraDB */ | |
1926 | /*-------------------------------------------------------------*/ | |
1927 | ||
1928 | /* The field number of the page number field in the sys_indexes table | |
1929 | @@ -144,11 +148,15 @@ | |
1930 | #define DICT_SYS_INDEXES_TYPE_FIELD 6 | |
1931 | #define DICT_SYS_INDEXES_NAME_FIELD 4 | |
1932 | ||
1933 | +#define DICT_SYS_STATS_DIFF_VALS_FIELD 4 | |
1934 | + | |
1935 | /* When a row id which is zero modulo this number (which must be a power of | |
1936 | two) is assigned, the field DICT_HDR_ROW_ID on the dictionary header page is | |
1937 | updated */ | |
1938 | #define DICT_HDR_ROW_ID_WRITE_MARGIN 256 | |
1939 | ||
1940 | +#define DICT_HDR_XTRADB_FLAG 0x5854524144425F31ULL /* "XTRADB_1" */ | |
1941 | + | |
1942 | #ifndef UNIV_NONINL | |
1943 | #include "dict0boot.ic" | |
1944 | #endif | |
1945 | diff -ruN a/storage/innobase/include/dict0crea.h b/storage/innobase/include/dict0crea.h | |
1946 | --- a/storage/innobase/include/dict0crea.h 2010-11-03 07:01:13.000000000 +0900 | |
1947 | +++ b/storage/innobase/include/dict0crea.h 2010-12-03 17:19:24.886949643 +0900 | |
1948 | @@ -53,6 +53,14 @@ | |
1949 | dict_index_t* index, /*!< in: index to create, built as a memory data | |
1950 | structure */ | |
1951 | mem_heap_t* heap); /*!< in: heap where created */ | |
1952 | +/*********************************************************************//** | |
1953 | +*/ | |
1954 | +UNIV_INTERN | |
1955 | +ind_node_t* | |
1956 | +ind_insert_stats_graph_create( | |
1957 | +/*==========================*/ | |
1958 | + dict_index_t* index, | |
1959 | + mem_heap_t* heap); | |
1960 | /***********************************************************//** | |
1961 | Creates a table. This is a high-level function used in SQL execution graphs. | |
1962 | @return query thread to run next or NULL */ | |
1963 | @@ -62,6 +70,13 @@ | |
1964 | /*===================*/ | |
1965 | que_thr_t* thr); /*!< in: query thread */ | |
1966 | /***********************************************************//** | |
1967 | +*/ | |
1968 | +UNIV_INTERN | |
1969 | +que_thr_t* | |
1970 | +dict_insert_stats_step( | |
1971 | +/*===================*/ | |
1972 | + que_thr_t* thr); | |
1973 | +/***********************************************************//** | |
1974 | Creates an index. This is a high-level function used in SQL execution | |
1975 | graphs. | |
1976 | @return query thread to run next or NULL */ | |
1977 | @@ -170,6 +185,7 @@ | |
1978 | ins_node_t* field_def; /* child node which does the inserts of | |
1979 | the field definitions; the row to be inserted | |
1980 | is built by the parent node */ | |
1981 | + ins_node_t* stats_def; | |
1982 | commit_node_t* commit_node; | |
1983 | /* child node which performs a commit after | |
1984 | a successful index creation */ | |
1985 | @@ -180,6 +196,7 @@ | |
1986 | dict_table_t* table; /*!< table which owns the index */ | |
1987 | dtuple_t* ind_row;/* index definition row built */ | |
1988 | ulint field_no;/* next field definition to insert */ | |
1989 | + ulint stats_no; | |
1990 | mem_heap_t* heap; /*!< memory heap used as auxiliary storage */ | |
1991 | }; | |
1992 | ||
1993 | @@ -189,6 +206,7 @@ | |
1994 | #define INDEX_CREATE_INDEX_TREE 3 | |
1995 | #define INDEX_COMMIT_WORK 4 | |
1996 | #define INDEX_ADD_TO_CACHE 5 | |
1997 | +#define INDEX_BUILD_STATS_COLS 6 | |
1998 | ||
1999 | #ifndef UNIV_NONINL | |
2000 | #include "dict0crea.ic" | |
2001 | diff -ruN a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h | |
2002 | --- a/storage/innobase/include/dict0dict.h 2010-12-03 15:48:03.073024387 +0900 | |
2003 | +++ b/storage/innobase/include/dict0dict.h 2010-12-03 17:19:24.888965622 +0900 | |
2004 | @@ -1084,10 +1084,11 @@ | |
2005 | dict_update_statistics( | |
2006 | /*===================*/ | |
2007 | dict_table_t* table, /*!< in/out: table */ | |
2008 | - ibool only_calc_if_missing_stats);/*!< in: only | |
2009 | + ibool only_calc_if_missing_stats, /*!< in: only | |
2010 | update/recalc the stats if they have | |
2011 | not been initialized yet, otherwise | |
2012 | do nothing */ | |
2013 | + ibool sync); | |
2014 | /********************************************************************//** | |
2015 | Reserves the dictionary system mutex for MySQL. */ | |
2016 | UNIV_INTERN | |
2017 | @@ -1202,6 +1203,7 @@ | |
2018 | dict_table_t* sys_columns; /*!< SYS_COLUMNS table */ | |
2019 | dict_table_t* sys_indexes; /*!< SYS_INDEXES table */ | |
2020 | dict_table_t* sys_fields; /*!< SYS_FIELDS table */ | |
2021 | + dict_table_t* sys_stats; /*!< SYS_STATS table */ | |
2022 | }; | |
2023 | #endif /* !UNIV_HOTBACKUP */ | |
2024 | ||
2025 | diff -ruN a/storage/innobase/include/dict0load.h b/storage/innobase/include/dict0load.h | |
2026 | --- a/storage/innobase/include/dict0load.h 2010-11-03 07:01:13.000000000 +0900 | |
2027 | +++ b/storage/innobase/include/dict0load.h 2010-12-03 17:19:24.889947481 +0900 | |
2028 | @@ -41,6 +41,7 @@ | |
2029 | SYS_FIELDS, | |
2030 | SYS_FOREIGN, | |
2031 | SYS_FOREIGN_COLS, | |
2032 | + SYS_STATS, | |
2033 | ||
2034 | /* This must be last item. Defines the number of system tables. */ | |
2035 | SYS_NUM_SYSTEM_TABLES | |
2036 | @@ -319,6 +320,19 @@ | |
2037 | const char** ref_col_name, /*!< out: referenced column name | |
2038 | in referenced table */ | |
2039 | ulint* pos); /*!< out: column position */ | |
2040 | +/********************************************************************//** | |
2041 | +This function parses a SYS_STATS record and extract necessary | |
2042 | +information from the record and return to caller. | |
2043 | +@return error message, or NULL on success */ | |
2044 | +UNIV_INTERN | |
2045 | +const char* | |
2046 | +dict_process_sys_stats_rec( | |
2047 | +/*=============================*/ | |
2048 | + mem_heap_t* heap, /*!< in/out: heap memory */ | |
2049 | + const rec_t* rec, /*!< in: current SYS_STATS rec */ | |
2050 | + index_id_t* index_id, /*!< out: INDEX_ID */ | |
2051 | + ulint* key_cols, /*!< out: KEY_COLS */ | |
2052 | + ib_uint64_t* diff_vals); /*!< out: DIFF_VALS */ | |
2053 | #ifndef UNIV_NONINL | |
2054 | #include "dict0load.ic" | |
2055 | #endif | |
2056 | diff -ruN a/storage/innobase/include/page0cur.h b/storage/innobase/include/page0cur.h | |
2057 | --- a/storage/innobase/include/page0cur.h 2010-11-03 07:01:13.000000000 +0900 | |
2058 | +++ b/storage/innobase/include/page0cur.h 2010-12-03 17:19:24.891954511 +0900 | |
2059 | @@ -293,6 +293,22 @@ | |
2060 | /*==========================*/ | |
2061 | buf_block_t* block, /*!< in: page */ | |
2062 | page_cur_t* cursor);/*!< out: page cursor */ | |
2063 | + | |
2064 | +UNIV_INTERN | |
2065 | +void | |
2066 | +page_cur_open_on_nth_user_rec( | |
2067 | +/*==========================*/ | |
2068 | + buf_block_t* block, /*!< in: page */ | |
2069 | + page_cur_t* cursor, /*!< out: page cursor */ | |
2070 | + ulint nth); | |
2071 | + | |
2072 | +UNIV_INTERN | |
2073 | +ibool | |
2074 | +page_cur_open_on_rnd_user_rec_after_nth( | |
2075 | +/*==========================*/ | |
2076 | + buf_block_t* block, /*!< in: page */ | |
2077 | + page_cur_t* cursor, /*!< out: page cursor */ | |
2078 | + ulint nth); | |
2079 | #endif /* !UNIV_HOTBACKUP */ | |
2080 | /***********************************************************//** | |
2081 | Parses a log record of a record insert on a page. | |
2082 | diff -ruN a/storage/innobase/include/que0que.h b/storage/innobase/include/que0que.h | |
2083 | --- a/storage/innobase/include/que0que.h 2010-11-03 07:01:13.000000000 +0900 | |
2084 | +++ b/storage/innobase/include/que0que.h 2010-12-03 17:19:24.892947946 +0900 | |
2085 | @@ -492,6 +492,8 @@ | |
2086 | #define QUE_NODE_CALL 31 | |
2087 | #define QUE_NODE_EXIT 32 | |
2088 | ||
2089 | +#define QUE_NODE_INSERT_STATS 34 | |
2090 | + | |
2091 | /* Query thread states */ | |
2092 | #define QUE_THR_RUNNING 1 | |
2093 | #define QUE_THR_PROCEDURE_WAIT 2 | |
2094 | diff -ruN a/storage/innobase/include/rem0cmp.h b/storage/innobase/include/rem0cmp.h | |
2095 | --- a/storage/innobase/include/rem0cmp.h 2010-11-03 07:01:13.000000000 +0900 | |
2096 | +++ b/storage/innobase/include/rem0cmp.h 2010-12-03 17:19:24.893953395 +0900 | |
2097 | @@ -169,10 +169,11 @@ | |
2098 | matched fields; when the function returns, | |
2099 | contains the value the for current | |
2100 | comparison */ | |
2101 | - ulint* matched_bytes);/*!< in/out: number of already matched | |
2102 | + ulint* matched_bytes, /*!< in/out: number of already matched | |
2103 | bytes within the first field not completely | |
2104 | matched; when the function returns, contains | |
2105 | the value for the current comparison */ | |
2106 | + ulint stats_method); | |
2107 | /*************************************************************//** | |
2108 | This function is used to compare two physical records. Only the common | |
2109 | first fields are compared. | |
2110 | diff -ruN a/storage/innobase/include/rem0cmp.ic b/storage/innobase/include/rem0cmp.ic | |
2111 | --- a/storage/innobase/include/rem0cmp.ic 2010-11-03 07:01:13.000000000 +0900 | |
2112 | +++ b/storage/innobase/include/rem0cmp.ic 2010-12-03 17:19:24.902983425 +0900 | |
2113 | @@ -87,5 +87,5 @@ | |
2114 | ulint match_b = 0; | |
2115 | ||
2116 | return(cmp_rec_rec_with_match(rec1, rec2, offsets1, offsets2, index, | |
2117 | - &match_f, &match_b)); | |
2118 | + &match_f, &match_b, 0)); | |
2119 | } | |
2120 | diff -ruN a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h | |
2121 | --- a/storage/innobase/include/row0mysql.h 2010-11-03 07:01:13.000000000 +0900 | |
2122 | +++ b/storage/innobase/include/row0mysql.h 2010-12-03 17:19:24.904973020 +0900 | |
2123 | @@ -387,6 +387,14 @@ | |
2124 | then checked for not being too | |
2125 | large. */ | |
2126 | /*********************************************************************//** | |
2127 | +*/ | |
2128 | +UNIV_INTERN | |
2129 | +int | |
2130 | +row_insert_stats_for_mysql( | |
2131 | +/*=======================*/ | |
2132 | + dict_index_t* index, | |
2133 | + trx_t* trx); | |
2134 | +/*********************************************************************//** | |
2135 | Scans a table create SQL string and adds to the data dictionary | |
2136 | the foreign key constraints declared in the string. This function | |
2137 | should be called after the indexes for a table have been created. | |
2138 | diff -ruN a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h | |
2139 | --- a/storage/innobase/include/srv0srv.h 2010-12-03 15:53:54.622036720 +0900 | |
2140 | +++ b/storage/innobase/include/srv0srv.h 2010-12-03 17:19:24.906953188 +0900 | |
2141 | @@ -209,6 +209,13 @@ | |
2142 | extern ibool srv_innodb_status; | |
2143 | ||
2144 | extern unsigned long long srv_stats_sample_pages; | |
2145 | +extern ulint srv_stats_method; | |
2146 | +#define SRV_STATS_METHOD_NULLS_EQUAL 0 | |
2147 | +#define SRV_STATS_METHOD_NULLS_NOT_EQUAL 1 | |
2148 | +#define SRV_STATS_METHOD_IGNORE_NULLS 2 | |
2149 | +extern ulint srv_stats_auto_update; | |
2150 | +extern ulint srv_stats_update_need_lock; | |
2151 | +extern ibool srv_use_sys_stats_table; | |
2152 | ||
2153 | extern ibool srv_use_doublewrite_buf; | |
2154 | extern ibool srv_use_checksums; | |
2155 | diff -ruN a/storage/innobase/page/page0cur.c b/storage/innobase/page/page0cur.c | |
2156 | --- a/storage/innobase/page/page0cur.c 2010-11-03 07:01:13.000000000 +0900 | |
2157 | +++ b/storage/innobase/page/page0cur.c 2010-12-03 17:19:24.908973357 +0900 | |
2158 | @@ -564,6 +564,74 @@ | |
2159 | } while (rnd--); | |
2160 | } | |
2161 | ||
2162 | +UNIV_INTERN | |
2163 | +void | |
2164 | +page_cur_open_on_nth_user_rec( | |
2165 | +/*==========================*/ | |
2166 | + buf_block_t* block, /*!< in: page */ | |
2167 | + page_cur_t* cursor, /*!< out: page cursor */ | |
2168 | + ulint nth) | |
2169 | +{ | |
2170 | + ulint n_recs = page_get_n_recs(buf_block_get_frame(block)); | |
2171 | + | |
2172 | + page_cur_set_before_first(block, cursor); | |
2173 | + | |
2174 | + if (UNIV_UNLIKELY(n_recs == 0)) { | |
2175 | + | |
2176 | + return; | |
2177 | + } | |
2178 | + | |
2179 | + nth--; | |
2180 | + | |
2181 | + if (nth >= n_recs) { | |
2182 | + nth = n_recs - 1; | |
2183 | + } | |
2184 | + | |
2185 | + do { | |
2186 | + page_cur_move_to_next(cursor); | |
2187 | + } while (nth--); | |
2188 | +} | |
2189 | + | |
2190 | +UNIV_INTERN | |
2191 | +ibool | |
2192 | +page_cur_open_on_rnd_user_rec_after_nth( | |
2193 | +/*==========================*/ | |
2194 | + buf_block_t* block, /*!< in: page */ | |
2195 | + page_cur_t* cursor, /*!< out: page cursor */ | |
2196 | + ulint nth) | |
2197 | +{ | |
2198 | + ulint rnd; | |
2199 | + ulint n_recs = page_get_n_recs(buf_block_get_frame(block)); | |
2200 | + ibool ret; | |
2201 | + | |
2202 | + page_cur_set_before_first(block, cursor); | |
2203 | + | |
2204 | + if (UNIV_UNLIKELY(n_recs == 0)) { | |
2205 | + | |
2206 | + return (FALSE); | |
2207 | + } | |
2208 | + | |
2209 | + nth--; | |
2210 | + | |
2211 | + if (nth >= n_recs) { | |
2212 | + nth = n_recs - 1; | |
2213 | + } | |
2214 | + | |
2215 | + rnd = (ulint) (nth + page_cur_lcg_prng() % (n_recs - nth)); | |
2216 | + | |
2217 | + if (rnd == nth) { | |
2218 | + ret = TRUE; | |
2219 | + } else { | |
2220 | + ret = FALSE; | |
2221 | + } | |
2222 | + | |
2223 | + do { | |
2224 | + page_cur_move_to_next(cursor); | |
2225 | + } while (rnd--); | |
2226 | + | |
2227 | + return (ret); | |
2228 | +} | |
2229 | + | |
2230 | /***********************************************************//** | |
2231 | Writes the log record of a record insert on a page. */ | |
2232 | static | |
2233 | diff -ruN a/storage/innobase/que/que0que.c b/storage/innobase/que/que0que.c | |
2234 | --- a/storage/innobase/que/que0que.c 2010-11-03 07:01:13.000000000 +0900 | |
2235 | +++ b/storage/innobase/que/que0que.c 2010-12-03 17:19:24.910953422 +0900 | |
2236 | @@ -621,11 +621,21 @@ | |
2237 | ||
2238 | que_graph_free_recursive(cre_ind->ind_def); | |
2239 | que_graph_free_recursive(cre_ind->field_def); | |
2240 | + if (srv_use_sys_stats_table) | |
2241 | + que_graph_free_recursive(cre_ind->stats_def); | |
2242 | que_graph_free_recursive(cre_ind->commit_node); | |
2243 | ||
2244 | mem_heap_free(cre_ind->heap); | |
2245 | ||
2246 | break; | |
2247 | + case QUE_NODE_INSERT_STATS: | |
2248 | + cre_ind = node; | |
2249 | + | |
2250 | + que_graph_free_recursive(cre_ind->stats_def); | |
2251 | + que_graph_free_recursive(cre_ind->commit_node); | |
2252 | + | |
2253 | + mem_heap_free(cre_ind->heap); | |
2254 | + break; | |
2255 | case QUE_NODE_PROC: | |
2256 | que_graph_free_stat_list(((proc_node_t*)node)->stat_list); | |
2257 | ||
2258 | @@ -1138,6 +1148,8 @@ | |
2259 | str = "CREATE TABLE"; | |
2260 | } else if (type == QUE_NODE_CREATE_INDEX) { | |
2261 | str = "CREATE INDEX"; | |
2262 | + } else if (type == QUE_NODE_INSERT_STATS) { | |
2263 | + str = "INSERT TO SYS_STATS"; | |
2264 | } else if (type == QUE_NODE_FOR) { | |
2265 | str = "FOR LOOP"; | |
2266 | } else if (type == QUE_NODE_RETURN) { | |
2267 | @@ -1255,6 +1267,8 @@ | |
2268 | thr = dict_create_table_step(thr); | |
2269 | } else if (type == QUE_NODE_CREATE_INDEX) { | |
2270 | thr = dict_create_index_step(thr); | |
2271 | + } else if (type == QUE_NODE_INSERT_STATS) { | |
2272 | + thr = dict_insert_stats_step(thr); | |
2273 | } else if (type == QUE_NODE_ROW_PRINTF) { | |
2274 | thr = row_printf_step(thr); | |
2275 | } else { | |
2276 | diff -ruN a/storage/innobase/rem/rem0cmp.c b/storage/innobase/rem/rem0cmp.c | |
2277 | --- a/storage/innobase/rem/rem0cmp.c 2010-11-03 07:01:13.000000000 +0900 | |
2278 | +++ b/storage/innobase/rem/rem0cmp.c 2010-12-03 17:19:24.911953579 +0900 | |
2279 | @@ -866,10 +866,11 @@ | |
2280 | matched fields; when the function returns, | |
2281 | contains the value the for current | |
2282 | comparison */ | |
2283 | - ulint* matched_bytes) /*!< in/out: number of already matched | |
2284 | + ulint* matched_bytes, /*!< in/out: number of already matched | |
2285 | bytes within the first field not completely | |
2286 | matched; when the function returns, contains | |
2287 | the value for the current comparison */ | |
2288 | + ulint stats_method) | |
2289 | { | |
2290 | ulint rec1_n_fields; /* the number of fields in rec */ | |
2291 | ulint rec1_f_len; /* length of current field in rec */ | |
2292 | @@ -962,7 +963,11 @@ | |
2293 | ||
2294 | if (rec1_f_len == rec2_f_len) { | |
2295 | ||
2296 | - goto next_field; | |
2297 | + if (stats_method == SRV_STATS_METHOD_NULLS_EQUAL) { | |
2298 | + goto next_field; | |
2299 | + } else { | |
2300 | + ret = -1; | |
2301 | + } | |
2302 | ||
2303 | } else if (rec2_f_len == UNIV_SQL_NULL) { | |
2304 | ||
2305 | diff -ruN a/storage/innobase/row/row0merge.c b/storage/innobase/row/row0merge.c | |
2306 | --- a/storage/innobase/row/row0merge.c 2010-11-03 07:01:13.000000000 +0900 | |
2307 | +++ b/storage/innobase/row/row0merge.c 2010-12-03 17:19:24.914955391 +0900 | |
2308 | @@ -2020,6 +2020,8 @@ | |
2309 | "UPDATE SYS_INDEXES SET NAME=CONCAT('" | |
2310 | TEMP_INDEX_PREFIX_STR "', NAME) WHERE ID = :indexid;\n" | |
2311 | "COMMIT WORK;\n" | |
2312 | + /* Drop the statistics of the index. */ | |
2313 | + "DELETE FROM SYS_STATS WHERE INDEX_ID = :indexid;\n" | |
2314 | /* Drop the field definitions of the index. */ | |
2315 | "DELETE FROM SYS_FIELDS WHERE INDEX_ID = :indexid;\n" | |
2316 | /* Drop the index definition and the B-tree. */ | |
2317 | diff -ruN a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c | |
2318 | --- a/storage/innobase/row/row0mysql.c 2010-11-03 07:01:13.000000000 +0900 | |
2319 | +++ b/storage/innobase/row/row0mysql.c 2010-12-03 17:19:24.918953476 +0900 | |
2320 | @@ -921,6 +921,9 @@ | |
2321 | ||
2322 | table->stat_modified_counter = counter + 1; | |
2323 | ||
2324 | + if (!srv_stats_auto_update) | |
2325 | + return; | |
2326 | + | |
2327 | /* Calculate new statistics if 1 / 16 of table has been modified | |
2328 | since the last time a statistics batch was run, or if | |
2329 | stat_modified_counter > 2 000 000 000 (to avoid wrap-around). | |
2330 | @@ -931,7 +934,7 @@ | |
2331 | || ((ib_int64_t)counter > 16 + table->stat_n_rows / 16)) { | |
2332 | ||
2333 | dict_update_statistics(table, FALSE /* update even if stats | |
2334 | - are initialized */); | |
2335 | + are initialized */, TRUE); | |
2336 | } | |
2337 | } | |
2338 | ||
d8778560 | 2339 | @@ -2103,6 +2106,45 @@ |
b4e1fa2c AM |
2340 | } |
2341 | ||
2342 | /*********************************************************************//** | |
2343 | +*/ | |
2344 | +UNIV_INTERN | |
2345 | +int | |
2346 | +row_insert_stats_for_mysql( | |
2347 | +/*=======================*/ | |
2348 | + dict_index_t* index, | |
2349 | + trx_t* trx) | |
2350 | +{ | |
2351 | + ind_node_t* node; | |
2352 | + mem_heap_t* heap; | |
2353 | + que_thr_t* thr; | |
2354 | + ulint err; | |
2355 | + | |
2356 | + ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); | |
2357 | + | |
2358 | + trx->op_info = "try to insert rows to SYS_STATS"; | |
2359 | + | |
2360 | + trx_start_if_not_started(trx); | |
2361 | + trx->error_state = DB_SUCCESS; | |
2362 | + | |
2363 | + heap = mem_heap_create(512); | |
2364 | + | |
2365 | + node = ind_insert_stats_graph_create(index, heap); | |
2366 | + | |
2367 | + thr = pars_complete_graph_for_exec(node, trx, heap); | |
2368 | + | |
2369 | + ut_a(thr == que_fork_start_command(que_node_get_parent(thr))); | |
2370 | + que_run_threads(thr); | |
2371 | + | |
2372 | + err = trx->error_state; | |
2373 | + | |
2374 | + que_graph_free((que_t*) que_node_get_parent(thr)); | |
2375 | + | |
2376 | + trx->op_info = ""; | |
2377 | + | |
2378 | + return((int) err); | |
2379 | +} | |
2380 | + | |
2381 | +/*********************************************************************//** | |
2382 | Scans a table create SQL string and adds to the data dictionary | |
2383 | the foreign key constraints declared in the string. This function | |
2384 | should be called after the indexes for a table have been created. | |
d8778560 | 2385 | @@ -3022,7 +3064,7 @@ |
b4e1fa2c AM |
2386 | dict_table_autoinc_initialize(table, 1); |
2387 | dict_table_autoinc_unlock(table); | |
2388 | dict_update_statistics(table, FALSE /* update even if stats are | |
2389 | - initialized */); | |
2390 | + initialized */, TRUE); | |
2391 | ||
2392 | trx_commit_for_mysql(trx); | |
2393 | ||
d8778560 | 2394 | @@ -3324,6 +3366,8 @@ |
b4e1fa2c AM |
2395 | " IF (SQL % NOTFOUND) THEN\n" |
2396 | " found := 0;\n" | |
2397 | " ELSE\n" | |
2398 | + " DELETE FROM SYS_STATS\n" | |
2399 | + " WHERE INDEX_ID = index_id;\n" | |
2400 | " DELETE FROM SYS_FIELDS\n" | |
2401 | " WHERE INDEX_ID = index_id;\n" | |
2402 | " DELETE FROM SYS_INDEXES\n" | |
2403 | diff -ruN a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c | |
2404 | --- a/storage/innobase/srv/srv0srv.c 2010-12-03 15:53:54.625288512 +0900 | |
2405 | +++ b/storage/innobase/srv/srv0srv.c 2010-12-03 17:19:24.922953561 +0900 | |
d8778560 | 2406 | @@ -397,6 +397,10 @@ |
b4e1fa2c AM |
2407 | /* When estimating number of different key values in an index, sample |
2408 | this many index pages */ | |
2409 | UNIV_INTERN unsigned long long srv_stats_sample_pages = 8; | |
2410 | +UNIV_INTERN ulint srv_stats_method = 0; | |
2411 | +UNIV_INTERN ulint srv_stats_auto_update = 1; | |
2412 | +UNIV_INTERN ulint srv_stats_update_need_lock = 1; | |
2413 | +UNIV_INTERN ibool srv_use_sys_stats_table = FALSE; | |
2414 | ||
2415 | UNIV_INTERN ibool srv_use_doublewrite_buf = TRUE; | |
2416 | UNIV_INTERN ibool srv_use_checksums = TRUE; |