Skip to content

Commit e5c4c08

Browse files
committed
MDEV-35443: opt_search_plan_for_table() may degrade to full table scan
opt_calc_index_goodness(): Correct an inaccurate condition. We can very well use a clustered index of a table that is subject to online rebuild. But we must not choose an index that has not been committed (it is a secondary index that was not fully created) or that is corrupted or not a normal B-tree index. opt_search_plan_for_table(): Remove some redundant code, now that opt_calc_index_goodness() checks against corrupted indexes. The test case allows this code to be exercised. The main observation in the following: ./mtr --rr innodb.stats_persistent rr replay var/log/mysqld.1.rr/latest-trace should be that when opt_search_plan_for_table() is being invoked by dict_stats_update_persistent() on the being-altered statistics table in the 2nd call after ha_innobase::inplace_alter_table(), and the fix in opt_calc_index_goodness() is absent, it would choose the code path if (n_fields == 0), that is, a full table scan, instead of searching for the record. The GDB commands to execute in "rr replay" would be as follows: break ha_innobase::inplace_alter_table continue break opt_search_plan_for_table continue continue next next … Reviewed by: Vladislav Lesin
1 parent a226f12 commit e5c4c08

File tree

4 files changed

+33
-8
lines changed

4 files changed

+33
-8
lines changed

mysql-test/suite/innodb/r/stats_persistent.result

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,23 @@ SET DEBUG_SYNC='dict_stats_update_persistent SIGNAL stop WAIT_FOR go';
66
ANALYZE TABLE t1;
77
connect con1, localhost, root;
88
SET DEBUG_SYNC='now WAIT_FOR stop';
9+
connect con2, localhost, root;
10+
SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL astop WAIT_FOR ago';
11+
ALTER TABLE mysql.innodb_index_stats FORCE;
12+
connection con1;
913
SELECT SUM(DATA_LENGTH+INDEX_LENGTH) FROM information_schema.TABLES WHERE ENGINE='InnoDB';
1014
SUM(DATA_LENGTH+INDEX_LENGTH)
1115
SUM
16+
SET DEBUG_SYNC='now WAIT_FOR astop';
1217
SET DEBUG_SYNC='now SIGNAL go';
1318
disconnect con1;
1419
connection default;
1520
Table Op Msg_type Msg_text
1621
test.t1 analyze status Engine-independent statistics collected
1722
test.t1 analyze status OK
23+
SET DEBUG_SYNC='now SIGNAL ago';
24+
connection con2;
25+
disconnect con2;
26+
connection default;
1827
SET DEBUG_SYNC= 'RESET';
1928
DROP TABLE t1;

mysql-test/suite/innodb/t/stats_persistent.test

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,25 @@ SET DEBUG_SYNC='dict_stats_update_persistent SIGNAL stop WAIT_FOR go';
1414
--connect(con1, localhost, root)
1515
SET DEBUG_SYNC='now WAIT_FOR stop';
1616

17+
--connect(con2, localhost, root)
18+
SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL astop WAIT_FOR ago';
19+
send ALTER TABLE mysql.innodb_index_stats FORCE;
20+
21+
--connection con1
1722
--replace_column 1 SUM
1823
SELECT SUM(DATA_LENGTH+INDEX_LENGTH) FROM information_schema.TABLES WHERE ENGINE='InnoDB';
1924

25+
SET DEBUG_SYNC='now WAIT_FOR astop';
2026
SET DEBUG_SYNC='now SIGNAL go';
2127
--disconnect con1
2228

2329
--connection default
2430
--reap
31+
SET DEBUG_SYNC='now SIGNAL ago';
32+
--connection con2
33+
reap;
34+
--disconnect con2
35+
--connection default
2536
SET DEBUG_SYNC= 'RESET';
2637
DROP TABLE t1;
2738

storage/innobase/include/dict0mem.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,14 @@ struct dict_index_t {
12031203
/** @return whether this is the change buffer */
12041204
bool is_ibuf() const { return UNIV_UNLIKELY(type & DICT_IBUF); }
12051205

1206+
/** @return whether this is a normal, non-virtual B-tree index
1207+
(not the change buffer, not SPATIAL or FULLTEXT) */
1208+
bool is_normal_btree() const noexcept {
1209+
return UNIV_LIKELY(!(type & (DICT_IBUF | DICT_SPATIAL
1210+
| DICT_FTS | DICT_CORRUPT
1211+
| DICT_VIRTUAL)));
1212+
}
1213+
12061214
/** @return whether the index includes virtual columns */
12071215
bool has_virtual() const { return type & DICT_VIRTUAL; }
12081216

storage/innobase/pars/pars0opt.cc

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -340,9 +340,7 @@ opt_calc_index_goodness(
340340
ulint op;
341341
ulint j;
342342

343-
/* At least for now we don't support using FTS indexes for queries
344-
done through InnoDB's own SQL parser. */
345-
if (dict_index_is_online_ddl(index) || (index->type & DICT_FTS)) {
343+
if (!index->is_normal_btree() || !index->is_committed()) {
346344
return(0);
347345
}
348346

@@ -572,11 +570,10 @@ opt_search_plan_for_table(
572570
/* Calculate goodness for each index of the table */
573571

574572
index = dict_table_get_first_index(table);
575-
best_index = index; /* Eliminate compiler warning */
573+
best_index = index;
576574
best_goodness = 0;
577575

578-
/* should be do ... until ? comment by Jani */
579-
while (index) {
576+
do {
580577
goodness = opt_calc_index_goodness(index, sel_node, i,
581578
index_plan, &last_op);
582579
if (goodness > best_goodness) {
@@ -590,8 +587,8 @@ opt_search_plan_for_table(
590587
best_last_op = last_op;
591588
}
592589

593-
dict_table_next_uncorrupted_index(index);
594-
}
590+
index = dict_table_get_next_index(index);
591+
} while (index);
595592

596593
plan->index = best_index;
597594

0 commit comments

Comments
 (0)