Skip to content

Commit f330518

Browse files
Bug #30556595 FAILING ASSERTION: !CURSOR->INDEX->IS_COMMITTED() ON
TABLE WITH GCOLS Problem: Secondary index over virtual column got corrupted if the index is being built online. Analysis: If we create index over a virtual column, concurrent UPDATE query don't get virtual column value from server layer and don't generate virtual column value. Since Index is being created as online DDL, UPDATE query write NULL value in row log for the index record. During row_log_apply(), we update (insert/del-mark) index record with NULL value. Therefore Index records got corrupted. Fix: For UPDATE query, If virtual column value of the index record is set to NULL, then generate virtual column value from cluster index record. RB: 26149 Reviewed by : Debarun Banerjee <debarun.banerjee@oracle.com>
1 parent be9954a commit f330518

File tree

3 files changed

+222
-0
lines changed

3 files changed

+222
-0
lines changed

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

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,101 @@ t1 CREATE TABLE `t1` (
211211
KEY `n` (`col2`)
212212
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
213213
DROP TABLE t1;
214+
#
215+
# Bug #30556595 FAILING ASSERTION: !CURSOR->INDEX->IS_COMMITTED()
216+
# ON TABLE WITH GCOLS
217+
#
218+
# Testcase-1
219+
CREATE TABLE t (pid int PRIMARY KEY,
220+
uid int,
221+
vid int AS (uid) VIRTUAL);
222+
# Create index on virtual column and stop before applying row logs.
223+
SET DEBUG_SYNC = 'row_log_apply_before SIGNAL start_dml WAIT_FOR resume_ddl';
224+
ALTER TABLE t ADD INDEX idx_vid(vid), algorithm=inplace;
225+
SET DEBUG_SYNC= 'now WAIT_FOR start_dml';
226+
# Insert a record to generate an insert row log for the new index.
227+
# LOG-1 idx_vid : INSERT [10, 1]
228+
INSERT INTO t(pid, uid) VALUES (1,10);
229+
# Update the record changing PK. It should generate 2 row logs for the new index.
230+
# LOG-2 idx_vid : DELETE [10, 1]
231+
# LOG-3 idx_vid : INSERT [10, 2]
232+
UPDATE t SET pid = 2 WHERE pid = 1;
233+
SELECT * FROM t;
234+
pid uid vid
235+
2 10 10
236+
SET DEBUG_SYNC= 'now SIGNAL resume_ddl';
237+
# Insert a record to re-use the delete marked record in idx_vid [10, 1]
238+
INSERT INTO t(pid, uid) VALUES (1,10);
239+
SELECT * FROM t;
240+
pid uid vid
241+
1 10 10
242+
2 10 10
243+
DROP TABLE t;
244+
# Testcase-2
245+
CREATE TABLE t (pid int PRIMARY KEY,
246+
uid int,
247+
vid int AS (uid) VIRTUAL);
248+
# Create index on virtual column and stop before applying row logs.
249+
SET DEBUG_SYNC = 'row_log_apply_before SIGNAL start_dml WAIT_FOR resume_ddl';
250+
ALTER TABLE t ADD INDEX idx_vid(vid), algorithm=inplace;
251+
SET DEBUG_SYNC= 'now WAIT_FOR start_dml';
252+
# Start transaction
253+
BEGIN;
254+
# Insert a record to generate an insert row log for the new index.
255+
# LOG-1 idx_vid : INSERT [10, 1]
256+
INSERT INTO t(pid, uid) VALUES (1,10);
257+
# Update the record changing PK. It should generate 2 row logs for the new index.
258+
# LOG-2 idx_vid : DELETE [10, 1]
259+
# LOG-3 idx_vid : INSERT [10, 2]
260+
UPDATE t SET pid = 2 WHERE pid = 1;
261+
SELECT * FROM t;
262+
pid uid vid
263+
2 10 10
264+
# Rollback transaction
265+
# LOG-4 idx_vid : DELETE [10, 2]
266+
# LOG-5 idx_vid : INSERT [10, 1]
267+
# LOG-6 idx_vid : DELETE [10, 1]
268+
ROLLBACK;
269+
SET DEBUG_SYNC= 'now SIGNAL resume_ddl';
270+
# Insert a record to check valid entry in idx_vid [NULL, 1]
271+
INSERT INTO t(pid) VALUES (1);
272+
SELECT * FROM t;
273+
pid uid vid
274+
1 NULL NULL
275+
DROP TABLE t;
276+
# Testcase-3
277+
CREATE TABLE t (pid int PRIMARY KEY,
278+
uid int,
279+
vid int AS (uid) VIRTUAL);
280+
INSERT INTO t(pid, uid) VALUES (1,10);
281+
# Create index on virtual column and stop before applying row logs.
282+
SET DEBUG_SYNC = 'row_log_apply_before SIGNAL start_dml WAIT_FOR resume_ddl';
283+
ALTER TABLE t ADD INDEX idx_vid(vid), algorithm=inplace;
284+
SET DEBUG_SYNC= 'now WAIT_FOR start_dml';
285+
# Update the record changing PK. It should generate 2 row logs for the new index.
286+
# LOG-2 idx_vid : DELETE [10, 1]
287+
# LOG-3 idx_vid : INSERT [10, 2]
288+
UPDATE t SET pid = 2 WHERE pid = 1;
289+
SET DEBUG_SYNC= 'now SIGNAL resume_ddl';
290+
# Read [2, 10, 10] row using idx_vid index key.
291+
select * from t where VID=10;
292+
pid uid vid
293+
2 10 10
294+
DROP TABLE t;
295+
# Testcase-4
296+
CREATE TABLE t (pid int PRIMARY KEY,
297+
uid int,
298+
vid int AS (uid) VIRTUAL);
299+
INSERT INTO t(pid, uid) VALUES (1,10);
300+
# Create index on virtual column and stop before applying row logs.
301+
SET DEBUG_SYNC = 'row_log_apply_before SIGNAL start_dml WAIT_FOR resume_ddl';
302+
ALTER TABLE t ADD INDEX idx_vid(vid), algorithm=inplace;
303+
SET DEBUG_SYNC= 'now WAIT_FOR start_dml';
304+
# Update the record changing PK. It should generate 2 row logs for the new index.
305+
# LOG-2 idx_vid : DELETE [10, 1]
306+
# LOG-3 idx_vid : INSERT [10, 2]
307+
UPDATE t SET pid = 2 WHERE pid = 1;
308+
SET DEBUG_SYNC= 'now SIGNAL resume_ddl';
309+
# Delete a record.
310+
delete from t limit 1;
311+
DROP TABLE t;

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

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,120 @@ if (0)
263263
DROP TABLE t1;
264264
SET sql_mode = default;
265265
}
266+
267+
--echo #
268+
--echo # Bug #30556595 FAILING ASSERTION: !CURSOR->INDEX->IS_COMMITTED()
269+
--echo # ON TABLE WITH GCOLS
270+
--echo #
271+
272+
--echo # Testcase-1
273+
CREATE TABLE t (pid int PRIMARY KEY,
274+
uid int,
275+
vid int AS (uid) VIRTUAL);
276+
connect(con1,localhost,root,,);
277+
--echo # Create index on virtual column and stop before applying row logs.
278+
SET DEBUG_SYNC = 'row_log_apply_before SIGNAL start_dml WAIT_FOR resume_ddl';
279+
--send ALTER TABLE t ADD INDEX idx_vid(vid), algorithm=inplace
280+
connection default;
281+
SET DEBUG_SYNC= 'now WAIT_FOR start_dml';
282+
--echo # Insert a record to generate an insert row log for the new index.
283+
--echo # LOG-1 idx_vid : INSERT [10, 1]
284+
INSERT INTO t(pid, uid) VALUES (1,10);
285+
--echo # Update the record changing PK. It should generate 2 row logs for the new index.
286+
--echo # LOG-2 idx_vid : DELETE [10, 1]
287+
--echo # LOG-3 idx_vid : INSERT [10, 2]
288+
UPDATE t SET pid = 2 WHERE pid = 1;
289+
SELECT * FROM t;
290+
SET DEBUG_SYNC= 'now SIGNAL resume_ddl';
291+
connection con1;
292+
--reap
293+
connection default;
294+
--echo # Insert a record to re-use the delete marked record in idx_vid [10, 1]
295+
INSERT INTO t(pid, uid) VALUES (1,10);
296+
SELECT * FROM t;
297+
disconnect con1;
298+
DROP TABLE t;
299+
300+
--echo # Testcase-2
301+
CREATE TABLE t (pid int PRIMARY KEY,
302+
uid int,
303+
vid int AS (uid) VIRTUAL);
304+
connect(con1,localhost,root,,);
305+
--echo # Create index on virtual column and stop before applying row logs.
306+
SET DEBUG_SYNC = 'row_log_apply_before SIGNAL start_dml WAIT_FOR resume_ddl';
307+
--send ALTER TABLE t ADD INDEX idx_vid(vid), algorithm=inplace
308+
connection default;
309+
SET DEBUG_SYNC= 'now WAIT_FOR start_dml';
310+
--echo # Start transaction
311+
BEGIN;
312+
--echo # Insert a record to generate an insert row log for the new index.
313+
--echo # LOG-1 idx_vid : INSERT [10, 1]
314+
INSERT INTO t(pid, uid) VALUES (1,10);
315+
--echo # Update the record changing PK. It should generate 2 row logs for the new index.
316+
--echo # LOG-2 idx_vid : DELETE [10, 1]
317+
--echo # LOG-3 idx_vid : INSERT [10, 2]
318+
UPDATE t SET pid = 2 WHERE pid = 1;
319+
SELECT * FROM t;
320+
--echo # Rollback transaction
321+
--echo # LOG-4 idx_vid : DELETE [10, 2]
322+
--echo # LOG-5 idx_vid : INSERT [10, 1]
323+
--echo # LOG-6 idx_vid : DELETE [10, 1]
324+
ROLLBACK;
325+
SET DEBUG_SYNC= 'now SIGNAL resume_ddl';
326+
connection con1;
327+
--reap
328+
connection default;
329+
--echo # Insert a record to check valid entry in idx_vid [NULL, 1]
330+
INSERT INTO t(pid) VALUES (1);
331+
SELECT * FROM t;
332+
disconnect con1;
333+
DROP TABLE t;
334+
335+
--echo # Testcase-3
336+
CREATE TABLE t (pid int PRIMARY KEY,
337+
uid int,
338+
vid int AS (uid) VIRTUAL);
339+
INSERT INTO t(pid, uid) VALUES (1,10);
340+
connect(con1,localhost,root,,);
341+
--echo # Create index on virtual column and stop before applying row logs.
342+
SET DEBUG_SYNC = 'row_log_apply_before SIGNAL start_dml WAIT_FOR resume_ddl';
343+
--send ALTER TABLE t ADD INDEX idx_vid(vid), algorithm=inplace
344+
connection default;
345+
SET DEBUG_SYNC= 'now WAIT_FOR start_dml';
346+
--echo # Update the record changing PK. It should generate 2 row logs for the new index.
347+
--echo # LOG-2 idx_vid : DELETE [10, 1]
348+
--echo # LOG-3 idx_vid : INSERT [10, 2]
349+
UPDATE t SET pid = 2 WHERE pid = 1;
350+
SET DEBUG_SYNC= 'now SIGNAL resume_ddl';
351+
connection con1;
352+
--reap
353+
connection default;
354+
--echo # Read [2, 10, 10] row using idx_vid index key.
355+
--sleep 1
356+
select * from t where VID=10;
357+
disconnect con1;
358+
DROP TABLE t;
359+
360+
--echo # Testcase-4
361+
CREATE TABLE t (pid int PRIMARY KEY,
362+
uid int,
363+
vid int AS (uid) VIRTUAL);
364+
INSERT INTO t(pid, uid) VALUES (1,10);
365+
connect(con1,localhost,root,,);
366+
--echo # Create index on virtual column and stop before applying row logs.
367+
SET DEBUG_SYNC = 'row_log_apply_before SIGNAL start_dml WAIT_FOR resume_ddl';
368+
--send ALTER TABLE t ADD INDEX idx_vid(vid), algorithm=inplace
369+
connection default;
370+
SET DEBUG_SYNC= 'now WAIT_FOR start_dml';
371+
--echo # Update the record changing PK. It should generate 2 row logs for the new index.
372+
--echo # LOG-2 idx_vid : DELETE [10, 1]
373+
--echo # LOG-3 idx_vid : INSERT [10, 2]
374+
UPDATE t SET pid = 2 WHERE pid = 1;
375+
SET DEBUG_SYNC= 'now SIGNAL resume_ddl';
376+
connection con1;
377+
--reap
378+
connection default;
379+
--echo # Delete a record.
380+
delete from t limit 1;
381+
disconnect con1;
382+
DROP TABLE t;

storage/innobase/row/row0upd.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2016,6 +2016,13 @@ row_upd_store_v_row(
20162016
col_no);
20172017
dfield_copy_data(dfield, vfield);
20182018
dfield_dup(dfield, node->heap);
2019+
if (dfield_is_null(dfield)) {
2020+
innobase_get_computed_value(
2021+
node->row, col, index,
2022+
&heap, node->heap, NULL,
2023+
thd, mysql_table, NULL,
2024+
NULL, NULL);
2025+
}
20192026
}
20202027
} else {
20212028
/* Need to compute, this happens when

0 commit comments

Comments
 (0)