Skip to content

Commit 02f8eaa

Browse files
Jimmy Yangbjornmu
authored andcommitted
Bug #21869656 UNDO LOG DOES NOT CONTAIN ENOUGH INFORMATION ON INDEXED
VIRTUAL COLUMNS Reviewed-by: Marko Makela <[email protected]> (cherry picked from commit ccdfdec5527ac0ea559c7b5a51febcd5def73782)
1 parent 9c509d6 commit 02f8eaa

File tree

9 files changed

+757
-67
lines changed

9 files changed

+757
-67
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#
2+
# Bug#21869656 UNDO LOG DOES NOT CONTAIN ENOUGH INFORMATION
3+
# ON INDEXED VIRTUAL COLUMNS
4+
#
5+
CREATE TABLE t1 (a INT, b INT,
6+
a1 INT GENERATED ALWAYS AS (a) VIRTUAL, INDEX(a1)
7+
) ENGINE=InnoDB;
8+
INSERT INTO t1 (a,b) VALUES(1,1);
9+
CREATE TABLE t0 (a INT) ENGINE=InnoDB;
10+
BEGIN;
11+
SELECT * FROM t0;
12+
a
13+
UPDATE t1 SET a=0;
14+
ALTER TABLE t1 DROP COLUMN a1, ALGORITHM=INPLACE;
15+
ALTER TABLE t1 ADD COLUMN b1 INT GENERATED ALWAYS AS (b) VIRTUAL, ADD
16+
INDEX(b1),
17+
ALGORITHM=INPLACE;
18+
COMMIT;
19+
Timeout in wait_innodb_all_purged.inc for INNODB_PURGE_TRX_ID_AGE = 1
20+
CHECK TABLE t1;
21+
Table Op Msg_type Msg_text
22+
test.t1 check status OK
23+
SELECT b1 FROM t1;
24+
b1
25+
1
26+
ALTER TABLE t1
27+
ADD COLUMN a1 INT GENERATED ALWAYS AS (a) VIRTUAL,
28+
ADD COLUMN a2 INT GENERATED ALWAYS AS (a + b) VIRTUAL,
29+
ADD COLUMN a3 INT GENERATED ALWAYS AS (a - b) VIRTUAL,
30+
ADD COLUMN a4 INT GENERATED ALWAYS AS (a - b) VIRTUAL,
31+
ADD INDEX(a1), ADD INDEX(a2), ADD INDEX(a3), ALGORITHM=INPLACE;
32+
CREATE TABLE t2 (
33+
a BLOB,
34+
b BLOB,
35+
c BLOB GENERATED ALWAYS AS (CONCAT(a,b)) VIRTUAL,
36+
h VARCHAR(10) DEFAULT NULL
37+
) ENGINE=InnoDB;
38+
INSERT INTO t2 VALUES (REPEAT('g', 16000), REPEAT('x', 16000), DEFAULT, 'kk');
39+
INSERT INTO t2 VALUES (REPEAT('a', 16000), REPEAT('b', 16000), DEFAULT, 'mm');
40+
CREATE INDEX idx ON t2(c(100));
41+
INSERT INTO t1 (a, b) VALUES(1,1);
42+
BEGIN;
43+
SELECT * FROM t0;
44+
a
45+
UPDATE t1 SET a=0;
46+
affected rows: 1
47+
info: Rows matched: 2 Changed: 1 Warnings: 0
48+
UPDATE t1 SET b=0;
49+
affected rows: 2
50+
info: Rows matched: 2 Changed: 2 Warnings: 0
51+
ALTER TABLE t1 DROP COLUMN a3, ALGORITHM=INPLACE;
52+
affected rows: 0
53+
info: Records: 0 Duplicates: 0 Warnings: 0
54+
UPDATE t1 SET a=2;
55+
affected rows: 2
56+
info: Rows matched: 2 Changed: 2 Warnings: 0
57+
ALTER TABLE t1 DROP COLUMN a2, ALGORITHM=INPLACE;
58+
affected rows: 0
59+
info: Records: 0 Duplicates: 0 Warnings: 0
60+
UPDATE t1 SET b=3;
61+
affected rows: 2
62+
info: Rows matched: 2 Changed: 2 Warnings: 0
63+
ALTER TABLE t1 ADD COLUMN b2 INT GENERATED ALWAYS AS (b) VIRTUAL,
64+
ADD INDEX(b2), ALGORITHM=INPLACE;
65+
affected rows: 0
66+
info: Records: 0 Duplicates: 0 Warnings: 0
67+
UPDATE t1 SET b=9;
68+
affected rows: 2
69+
info: Rows matched: 2 Changed: 2 Warnings: 0
70+
ALTER TABLE t1 ADD COLUMN b3 INT GENERATED ALWAYS AS (a) VIRTUAL,
71+
ADD INDEX(b3), ALGORITHM=INPLACE;
72+
affected rows: 0
73+
info: Records: 0 Duplicates: 0 Warnings: 0
74+
UPDATE t1 SET b=10;
75+
affected rows: 2
76+
info: Rows matched: 2 Changed: 2 Warnings: 0
77+
ALTER TABLE t2 DROP COLUMN c;
78+
affected rows: 0
79+
info: Records: 0 Duplicates: 0 Warnings: 0
80+
UPDATE t2 SET a = REPEAT('s', 6000) WHERE a like 'aaa%';
81+
affected rows: 1
82+
info: Rows matched: 1 Changed: 1 Warnings: 0
83+
ALTER TABLE t2 ADD COLUMN x1 BLOB GENERATED ALWAYS AS (CONCAT(a,b)) VIRTUAL,
84+
ADD COLUMN x2 BLOB GENERATED ALWAYS AS (CONCAT(a,b)) VIRTUAL,
85+
ADD INDEX(x1(100), x2(120)), ADD INDEX (x1(20));
86+
affected rows: 0
87+
info: Records: 0 Duplicates: 0 Warnings: 0
88+
UPDATE t1 SET a=5;
89+
affected rows: 2
90+
info: Rows matched: 2 Changed: 2 Warnings: 0
91+
UPDATE t2 SET a = REPEAT('m', 16000) WHERE a like 'sss%';
92+
affected rows: 1
93+
info: Rows matched: 1 Changed: 1 Warnings: 0
94+
ALTER TABLE t1 DROP COLUMN b2, ALGORITHM=INPLACE;
95+
affected rows: 0
96+
info: Records: 0 Duplicates: 0 Warnings: 0
97+
UPDATE t1 SET a=6;
98+
affected rows: 2
99+
info: Rows matched: 2 Changed: 2 Warnings: 0
100+
ALTER TABLE t2 DROP COLUMN x1, DROP COLUMN x2, ALGORITHM=INPLACE;
101+
affected rows: 0
102+
info: Records: 0 Duplicates: 0 Warnings: 0
103+
UPDATE t2 SET a = REPEAT('x', 1000) WHERE a like 'mmm%';
104+
affected rows: 1
105+
info: Rows matched: 1 Changed: 1 Warnings: 0
106+
ALTER TABLE t1 DROP INDEX b3;
107+
affected rows: 0
108+
info: Records: 0 Duplicates: 0 Warnings: 0
109+
UPDATE t1 SET a=100;
110+
affected rows: 2
111+
info: Rows matched: 2 Changed: 2 Warnings: 0
112+
COMMIT;
113+
CHECK TABLE t1;
114+
Table Op Msg_type Msg_text
115+
test.t1 check status OK
116+
SELECT b1 FROM t1;
117+
b1
118+
10
119+
10
120+
SELECT * FROM t1;
121+
a b b1 a1 a4 b3
122+
100 10 10 100 90 100
123+
100 10 10 100 90 100
124+
CHECK TABLE t2;
125+
Table Op Msg_type Msg_text
126+
test.t2 check status OK
127+
DROP TABLE t2, t1, t0;
128+
CREATE TABLE t1 (a VARCHAR(30), b INT, a2 VARCHAR(30) GENERATED ALWAYS AS (a) VIRTUAL);
129+
CREATE INDEX idx ON t1(a2(10), b, a2(20));
130+
ERROR 42S21: Duplicate column name 'a2'
131+
DROP TABLE t1;
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
--source include/have_innodb.inc
2+
--source include/count_sessions.inc
3+
4+
--echo #
5+
--echo # Bug#21869656 UNDO LOG DOES NOT CONTAIN ENOUGH INFORMATION
6+
--echo # ON INDEXED VIRTUAL COLUMNS
7+
--echo #
8+
9+
CREATE TABLE t1 (a INT, b INT,
10+
a1 INT GENERATED ALWAYS AS (a) VIRTUAL, INDEX(a1)
11+
) ENGINE=InnoDB;
12+
13+
INSERT INTO t1 (a,b) VALUES(1,1);
14+
15+
connect (con1,localhost,root,,);
16+
# disable purge
17+
CREATE TABLE t0 (a INT) ENGINE=InnoDB;
18+
BEGIN; SELECT * FROM t0;
19+
20+
connection default;
21+
# write the problematic update_undo log record
22+
UPDATE t1 SET a=0;
23+
24+
ALTER TABLE t1 DROP COLUMN a1, ALGORITHM=INPLACE;
25+
ALTER TABLE t1 ADD COLUMN b1 INT GENERATED ALWAYS AS (b) VIRTUAL, ADD
26+
INDEX(b1),
27+
ALGORITHM=INPLACE;
28+
29+
connection con1;
30+
# enable purge
31+
COMMIT;
32+
33+
connection default;
34+
# wait for purge to process the update_undo record.
35+
--source include/wait_innodb_all_purged.inc
36+
37+
CHECK TABLE t1;
38+
SELECT b1 FROM t1;
39+
40+
41+
# Create multi-virtual column, more ADD/DROP to test it
42+
ALTER TABLE t1
43+
ADD COLUMN a1 INT GENERATED ALWAYS AS (a) VIRTUAL,
44+
ADD COLUMN a2 INT GENERATED ALWAYS AS (a + b) VIRTUAL,
45+
ADD COLUMN a3 INT GENERATED ALWAYS AS (a - b) VIRTUAL,
46+
ADD COLUMN a4 INT GENERATED ALWAYS AS (a - b) VIRTUAL,
47+
ADD INDEX(a1), ADD INDEX(a2), ADD INDEX(a3), ALGORITHM=INPLACE;
48+
49+
CREATE TABLE t2 (
50+
a BLOB,
51+
b BLOB,
52+
c BLOB GENERATED ALWAYS AS (CONCAT(a,b)) VIRTUAL,
53+
h VARCHAR(10) DEFAULT NULL
54+
) ENGINE=InnoDB;
55+
56+
INSERT INTO t2 VALUES (REPEAT('g', 16000), REPEAT('x', 16000), DEFAULT, 'kk');
57+
58+
INSERT INTO t2 VALUES (REPEAT('a', 16000), REPEAT('b', 16000), DEFAULT, 'mm');
59+
60+
CREATE INDEX idx ON t2(c(100));
61+
62+
INSERT INTO t1 (a, b) VALUES(1,1);
63+
64+
connection con1;
65+
# disable purge
66+
BEGIN; SELECT * FROM t0;
67+
68+
connection default;
69+
--enable_info
70+
71+
# write the problematic update_undo log record
72+
UPDATE t1 SET a=0;
73+
UPDATE t1 SET b=0;
74+
75+
ALTER TABLE t1 DROP COLUMN a3, ALGORITHM=INPLACE;
76+
77+
UPDATE t1 SET a=2;
78+
ALTER TABLE t1 DROP COLUMN a2, ALGORITHM=INPLACE;
79+
UPDATE t1 SET b=3;
80+
81+
ALTER TABLE t1 ADD COLUMN b2 INT GENERATED ALWAYS AS (b) VIRTUAL,
82+
ADD INDEX(b2), ALGORITHM=INPLACE;
83+
UPDATE t1 SET b=9;
84+
85+
ALTER TABLE t1 ADD COLUMN b3 INT GENERATED ALWAYS AS (a) VIRTUAL,
86+
ADD INDEX(b3), ALGORITHM=INPLACE;
87+
88+
UPDATE t1 SET b=10;
89+
90+
ALTER TABLE t2 DROP COLUMN c;
91+
92+
UPDATE t2 SET a = REPEAT('s', 6000) WHERE a like 'aaa%';
93+
94+
ALTER TABLE t2 ADD COLUMN x1 BLOB GENERATED ALWAYS AS (CONCAT(a,b)) VIRTUAL,
95+
ADD COLUMN x2 BLOB GENERATED ALWAYS AS (CONCAT(a,b)) VIRTUAL,
96+
ADD INDEX(x1(100), x2(120)), ADD INDEX (x1(20));
97+
98+
UPDATE t1 SET a=5;
99+
100+
UPDATE t2 SET a = REPEAT('m', 16000) WHERE a like 'sss%';
101+
102+
ALTER TABLE t1 DROP COLUMN b2, ALGORITHM=INPLACE;
103+
104+
UPDATE t1 SET a=6;
105+
106+
ALTER TABLE t2 DROP COLUMN x1, DROP COLUMN x2, ALGORITHM=INPLACE;
107+
108+
UPDATE t2 SET a = REPEAT('x', 1000) WHERE a like 'mmm%';
109+
110+
ALTER TABLE t1 DROP INDEX b3;
111+
UPDATE t1 SET a=100;
112+
--disable_info
113+
114+
connection con1;
115+
# enable purge
116+
COMMIT;
117+
disconnect con1;
118+
119+
connection default;
120+
# wait for purge to process the update_undo record.
121+
--source include/wait_innodb_all_purged.inc
122+
123+
CHECK TABLE t1;
124+
SELECT b1 FROM t1;
125+
126+
SELECT * FROM t1;
127+
CHECK TABLE t2;
128+
DROP TABLE t2, t1, t0;
129+
130+
CREATE TABLE t1 (a VARCHAR(30), b INT, a2 VARCHAR(30) GENERATED ALWAYS AS (a) VIRTUAL);
131+
132+
--error ER_DUP_FIELDNAME
133+
CREATE INDEX idx ON t1(a2(10), b, a2(20));
134+
135+
DROP TABLE t1;
136+
137+
--source include/wait_until_count_sessions.inc

storage/innobase/dict/dict0dict.cc

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2773,6 +2773,39 @@ dict_index_remove_from_cache_low(
27732773
/* Remove the index from the list of indexes of the table */
27742774
UT_LIST_REMOVE(table->indexes, index);
27752775

2776+
/* Remove the index from affected virtual column index list */
2777+
if (dict_index_has_virtual(index)) {
2778+
const dict_col_t* col;
2779+
const dict_v_col_t* vcol;
2780+
2781+
for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
2782+
col = dict_index_get_nth_col(index, i);
2783+
if (dict_col_is_virtual(col)) {
2784+
vcol = reinterpret_cast<const dict_v_col_t*>(
2785+
col);
2786+
2787+
/* This could be NULL, when we do add virtual
2788+
column, add index together. We do not need to
2789+
track this virtual column's index */
2790+
if (vcol->v_indexes == NULL) {
2791+
continue;
2792+
}
2793+
2794+
dict_v_idx_list::iterator it;
2795+
2796+
for (it = vcol->v_indexes->begin();
2797+
it != vcol->v_indexes->end(); ++it) {
2798+
dict_v_idx_t v_index = *it;
2799+
if (v_index.index == index) {
2800+
vcol->v_indexes->erase(it);
2801+
break;
2802+
}
2803+
}
2804+
}
2805+
2806+
}
2807+
}
2808+
27762809
size = mem_heap_get_size(index->heap);
27772810

27782811
ut_ad(!dict_table_is_intrinsic(table));
@@ -2910,6 +2943,22 @@ dict_index_add_col(
29102943
const char* col_name;
29112944

29122945
if (dict_col_is_virtual(col)) {
2946+
dict_v_col_t* v_col = reinterpret_cast<dict_v_col_t*>(col);
2947+
2948+
/* When v_col->v_indexes==NULL,
2949+
ha_innobase::commit_inplace_alter_table(commit=true)
2950+
will evict and reload the table definition, and
2951+
v_col->v_indexes will not be NULL for the new table. */
2952+
if (v_col->v_indexes != NULL) {
2953+
/* Register the index with the virtual column index
2954+
list */
2955+
struct dict_v_idx_t new_idx
2956+
= {index, index->n_def};
2957+
2958+
v_col->v_indexes->push_back(new_idx);
2959+
2960+
}
2961+
29132962
col_name = dict_table_get_v_col_name_mysql(
29142963
table, dict_col_get_no(col));
29152964
} else {

storage/innobase/dict/dict0mem.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,16 @@ dict_mem_table_free(
211211

212212
ut_free(table->name.m_name);
213213
table->name.m_name = NULL;
214+
215+
/* Clean up virtual index info structures that are registered
216+
with virtual columns */
217+
for (ulint i = 0; i < table->n_v_def; i++) {
218+
dict_v_col_t* vcol
219+
= dict_table_get_nth_v_col(table, i);
220+
221+
UT_DELETE(vcol->v_indexes);
222+
}
223+
214224
mem_heap_free(table->heap);
215225
}
216226

@@ -378,6 +388,9 @@ dict_mem_table_add_v_col(
378388

379389
v_col->num_base = num_base;
380390

391+
/* Initialize the index list for virtual columns */
392+
v_col->v_indexes = UT_NEW_NOKEY(dict_v_idx_list());
393+
381394
return(v_col);
382395
}
383396

0 commit comments

Comments
 (0)