Skip to content

Commit

Permalink
PS-9222 ALTER TABLE ALGORITHM=INSTANT FIX #1
Browse files Browse the repository at this point in the history
https://perconadev.atlassian.net/browse/PS-9222

Problem
=======
When writing to the redo log, an issue of column order change not
being recorded with INSTANT DDL was fixed by creating an array
with size equal to the number of fields in the index which kept
track of whether the original position of the field was changed
or not. Later, that array would be used to make a decision on
logging the field.
But, this solution didn't take into account the fact that
there could be column prefixes because of the primary key. This
resulted in inaccurate entries being filled in the
fields_with_changed_order[] array.

Solution
========
It is fixed by using the method, get_col_phy_pos() which takes
into account the existence of column prefix instead of get_phy_pos()
while generating fields_with_changed_order[] array.
  • Loading branch information
VarunNagaraju committed Jun 28, 2024
1 parent 353e667 commit 366031e
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 2 deletions.
40 changes: 40 additions & 0 deletions mysql-test/suite/innodb/r/instant_alter_index_prefix.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
without prefix field in the index
CREATE TABLE t1 (c1 TINYTEXT COLLATE ascii_bin NOT NULL, c2 DATETIME(3) NOT NULL, c3 TEXT, UNIQUE KEY (c1(30)));
INSERT INTO t1 (c1, c2, c3) VALUE ('k1','2021-12-21','something');
INSERT INTO t1 (c1, c2, c3) VALUE ('k3','2021-12-21','something else');
ALTER TABLE t1 ADD COLUMN c4 VARCHAR(18) NOT NULL, ALGORITHM=INSTANT;
# Make sure nothing gets flushed on disk
SET GLOBAL innodb_log_checkpoint_now = ON;
SET GLOBAL innodb_page_cleaner_disabled_debug = 1;
SET GLOBAL innodb_dict_stats_disabled_debug = 1;
SET GLOBAL innodb_master_thread_disabled_debug = 1;
SET GLOBAL innodb_checkpoint_disabled = ON;
UPDATE t1 SET c4 = 'value' WHERE c1 = 'k1';
# Restart the server and reload the table to see if tables are corrupted.
# Kill and restart
# Run a select to confirm that the database started up successfully
SELECT * FROM t1;
c1 c2 c3 c4
k1 2021-12-21 00:00:00.000 something value
k3 2021-12-21 00:00:00.000 something else
DROP TABLE t1;
with prefix field in the index
CREATE TABLE t1 (c1 TINYTEXT COLLATE ascii_bin NOT NULL, c2 DATETIME(3) NOT NULL, c3 TEXT, PRIMARY KEY (c1(30)));
INSERT INTO t1 (c1, c2, c3) VALUE ('k1','2021-12-21','something');
INSERT INTO t1 (c1, c2, c3) VALUE ('k3','2021-12-21','something else');
ALTER TABLE t1 ADD COLUMN c4 VARCHAR(18) NOT NULL, ALGORITHM=INSTANT;
# Make sure nothing gets flushed on disk
SET GLOBAL innodb_log_checkpoint_now = ON;
SET GLOBAL innodb_page_cleaner_disabled_debug = 1;
SET GLOBAL innodb_dict_stats_disabled_debug = 1;
SET GLOBAL innodb_master_thread_disabled_debug = 1;
SET GLOBAL innodb_checkpoint_disabled = ON;
UPDATE t1 SET c4 = 'value' WHERE c1 = 'k1';
# Restart the server and reload the table to see if tables are corrupted.
# Kill and restart
# Run a select to confirm that the database started up successfully
SELECT * FROM t1;
c1 c2 c3 c4
k1 2021-12-21 00:00:00.000 something value
k3 2021-12-21 00:00:00.000 something else
DROP TABLE t1;
51 changes: 51 additions & 0 deletions mysql-test/suite/innodb/t/instant_alter_index_prefix.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# PS-9222 Testing if ALGORITHM=INSTANT crashes server

--echo without prefix field in the index
CREATE TABLE t1 (c1 TINYTEXT COLLATE ascii_bin NOT NULL, c2 DATETIME(3) NOT NULL, c3 TEXT, UNIQUE KEY (c1(30)));
INSERT INTO t1 (c1, c2, c3) VALUE ('k1','2021-12-21','something');
INSERT INTO t1 (c1, c2, c3) VALUE ('k3','2021-12-21','something else');

ALTER TABLE t1 ADD COLUMN c4 VARCHAR(18) NOT NULL, ALGORITHM=INSTANT;

--echo # Make sure nothing gets flushed on disk
SET GLOBAL innodb_log_checkpoint_now = ON;
SET GLOBAL innodb_page_cleaner_disabled_debug = 1;
SET GLOBAL innodb_dict_stats_disabled_debug = 1;
SET GLOBAL innodb_master_thread_disabled_debug = 1;
SET GLOBAL innodb_checkpoint_disabled = ON;

UPDATE t1 SET c4 = 'value' WHERE c1 = 'k1';

--echo # Restart the server and reload the table to see if tables are corrupted.
--source include/kill_and_restart_mysqld.inc

-- echo # Run a select to confirm that the database started up successfully
SELECT * FROM t1;

# cleanup
DROP TABLE t1;

--echo with prefix field in the index
CREATE TABLE t1 (c1 TINYTEXT COLLATE ascii_bin NOT NULL, c2 DATETIME(3) NOT NULL, c3 TEXT, PRIMARY KEY (c1(30)));
INSERT INTO t1 (c1, c2, c3) VALUE ('k1','2021-12-21','something');
INSERT INTO t1 (c1, c2, c3) VALUE ('k3','2021-12-21','something else');

ALTER TABLE t1 ADD COLUMN c4 VARCHAR(18) NOT NULL, ALGORITHM=INSTANT;

--echo # Make sure nothing gets flushed on disk
SET GLOBAL innodb_log_checkpoint_now = ON;
SET GLOBAL innodb_page_cleaner_disabled_debug = 1;
SET GLOBAL innodb_dict_stats_disabled_debug = 1;
SET GLOBAL innodb_master_thread_disabled_debug = 1;
SET GLOBAL innodb_checkpoint_disabled = ON;

UPDATE t1 SET c4 = 'value' WHERE c1 = 'k1';

--echo # Restart the server and reload the table to see if tables are corrupted.
--source include/kill_and_restart_mysqld.inc

-- echo # Run a select to confirm that the database started up successfully
SELECT * FROM t1;

# cleanup
DROP TABLE t1;
4 changes: 2 additions & 2 deletions storage/innobase/mtr/mtr0log.cc
Original file line number Diff line number Diff line change
Expand Up @@ -862,8 +862,8 @@ bool mlog_open_and_write_index(mtr_t *mtr, const byte *rec,

if (col->is_instant_added() || col->is_instant_dropped()) {
continue;
} else if (col->get_phy_pos() >= phy_pos) {
phy_pos = col->get_phy_pos();
} else if (col->get_col_phy_pos() >= phy_pos) {
phy_pos = col->get_col_phy_pos();
} else {
fields_with_changed_order[i] = true;
}
Expand Down

0 comments on commit 366031e

Please sign in to comment.