Skip to content

Commit cc8eefb

Browse files
MDEV-33087 ALTER TABLE...ALGORITHM=COPY should build indexes more efficiently
- During copy algorithm, InnoDB should use bulk insert operation for row by row insert operation. By doing this, copy algorithm can effectively build indexes. This optimization is disabled for temporary table, versioning table and table which has foreign key relation. Introduced the variable innodb_alter_copy_bulk to allow the bulk insert operation for copy alter operation inside InnoDB. This is enabled by default ha_innobase::extra(): HA_EXTRA_END_ALTER_COPY mode tries to apply the buffered bulk insert operation, updates the non-persistent table stats. row_merge_bulk_t::write_to_index(): Update stat_n_rows after applying the bulk insert operation row_ins_clust_index_entry_low(): In case of copy algorithm, switch to bulk insert operation. copy_data_error_ignore(): Handles the error while copying the data from source to target file.
1 parent 2844895 commit cc8eefb

22 files changed

+325
-91
lines changed

mysql-test/main/rowid_filter_innodb.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1986,7 +1986,7 @@ ANALYZE
19861986
"r_table_time_ms": "REPLACED",
19871987
"r_other_time_ms": "REPLACED",
19881988
"r_engine_stats": {
1989-
"pages_accessed": 3
1989+
"pages_accessed": 2
19901990
},
19911991
"filtered": "REPLACED",
19921992
"r_filtered": 66.66666667,
@@ -2140,7 +2140,7 @@ ANALYZE
21402140
"r_table_time_ms": "REPLACED",
21412141
"r_other_time_ms": "REPLACED",
21422142
"r_engine_stats": {
2143-
"pages_accessed": 3
2143+
"pages_accessed": 2
21442144
},
21452145
"filtered": "REPLACED",
21462146
"r_filtered": 66.66666667,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--- bulk_copy_alter.result
2+
+++ bulk_copy_alter,non_bulk_alter_copy.result
3+
@@ -5,7 +5,7 @@
4+
INSERT INTO t1 SELECT repeat('b', 200), seq FROM seq_3_to_65536;
5+
ALTER TABLE t1 ADD INDEX(f2);
6+
ALTER TABLE t1 ADD PRIMARY KEY(f1(2));
7+
-ERROR 23000: Duplicate entry 'bb' for key 'PRIMARY'
8+
+ERROR 23000: Duplicate entry 'aa' for key 'PRIMARY'
9+
INSERT INTO t1 VALUES(repeat('a', 200), 1);
10+
ALTER TABLE t1 ADD UNIQUE KEY(f2);
11+
ERROR 23000: Duplicate entry '1' for key 'f2_2'
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
SET @@alter_algorithm=COPY;
2+
CREATE TABLE t1(f1 CHAR(200), f2 INT NOT NULL)engine=InnoDB;
3+
INSERT INTO t1 SELECT repeat('a', 200), seq FROM seq_1_to_2;
4+
ALTER TABLE t1 FORCE;
5+
INSERT INTO t1 SELECT repeat('b', 200), seq FROM seq_3_to_65536;
6+
ALTER TABLE t1 ADD INDEX(f2);
7+
ALTER TABLE t1 ADD PRIMARY KEY(f1(2));
8+
ERROR 23000: Duplicate entry 'bb' for key 'PRIMARY'
9+
INSERT INTO t1 VALUES(repeat('a', 200), 1);
10+
ALTER TABLE t1 ADD UNIQUE KEY(f2);
11+
ERROR 23000: Duplicate entry '1' for key 'f2_2'
12+
ALTER IGNORE TABLE t1 MODIFY f1 CHAR(200) NOT NULL;
13+
CREATE TABLE t2(f1 INT NOT NULL,
14+
FOREIGN KEY(f1) REFERENCES t1(f2))ENGINE=InnoDB;
15+
INSERT INTO t2 VALUES(1);
16+
ALTER TABLE t2 FORCE;
17+
DROP TABLE t2, t1;
18+
CREATE TABLE t1 (f1 INT, f2 INT) ENGINE=InnoDB PARTITION BY HASH(f1) PARTITIONS 2;
19+
INSERT INTO t1 VALUES(1, 1);
20+
INSERT INTO t1 SELECT seq, seq * 2 FROM seq_1_to_2;
21+
ALTER TABLE t1 FORCE;
22+
INSERT INTO t1 SELECT seq, seq * 2 FROM seq_3_to_65536;
23+
ALTER TABLE t1 ADD INDEX(f2);
24+
DROP TABLE t1;

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,4 +1119,26 @@ test.collections check status OK
11191119
disconnect con1;
11201120
DROP TABLE binaries, collections;
11211121
# End of 10.6 tests
1122+
CREATE TABLE t1
1123+
(
1124+
f1 VARCHAR(32)BINARY NOT NULL,
1125+
f2 VARCHAR(32)BINARY NOT NULL,
1126+
PRIMARY KEY (f1),
1127+
INDEX(f2)
1128+
) ENGINE=InnoDB;
1129+
INSERT INTO t1 VALUES('MySQL', 'InnoDB'), ('MariaDB', 'NDB');
1130+
CREATE TABLE t2
1131+
(
1132+
f1 VARCHAR(32)BINARY NOT NULL,
1133+
f2 VARCHAR(255)BINARY NOT NULL,
1134+
f3 int, PRIMARY KEY (f1), INDEX(f1), INDEX(f2)
1135+
) ENGINE=InnoDB;
1136+
INSERT INTO t2 VALUES('MySQL', 'MySQL', 1),
1137+
('MariaDB', 'MariaDB', 1);
1138+
ALTER TABLE t1 ADD FOREIGN KEY (f1) REFERENCES t2 (f2);
1139+
ALTER TABLE t2 ADD FOREIGN KEY (f2) REFERENCES t2 (f2),
1140+
ADD UNIQUE INDEX(f3);
1141+
ERROR HY000: Cannot delete rows from table which is parent in a foreign key constraint 't1_ibfk_1' of table 't1'
1142+
drop table t1, t2;
11221143
SET GLOBAL innodb_stats_persistent = @save_stats_persistent;
1144+
# End of 10.11 tests
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[ON]
2+
--innodb_alter_copy_bulk=ON
3+
4+
[OFF]
5+
--innodb_alter_copy_bulk=OFF
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--source include/have_innodb.inc
2+
--source include/have_partition.inc
3+
--source include/have_sequence.inc
4+
SET @@alter_algorithm=COPY;
5+
6+
CREATE TABLE t1(f1 CHAR(200), f2 INT NOT NULL)engine=InnoDB;
7+
INSERT INTO t1 SELECT repeat('a', 200), seq FROM seq_1_to_2;
8+
# Buffer fits in the memory
9+
ALTER TABLE t1 FORCE;
10+
11+
# Insert more entries
12+
INSERT INTO t1 SELECT repeat('b', 200), seq FROM seq_3_to_65536;
13+
# Alter should use temporary file for sorting
14+
ALTER TABLE t1 ADD INDEX(f2);
15+
16+
# Error while buffering the insert operation
17+
--error ER_DUP_ENTRY
18+
ALTER TABLE t1 ADD PRIMARY KEY(f1(2));
19+
20+
INSERT INTO t1 VALUES(repeat('a', 200), 1);
21+
# Error while applying the bulk insert operation
22+
--error ER_DUP_ENTRY
23+
ALTER TABLE t1 ADD UNIQUE KEY(f2);
24+
25+
# Ignore shouldn't go through bulk operation
26+
ALTER IGNORE TABLE t1 MODIFY f1 CHAR(200) NOT NULL;
27+
28+
CREATE TABLE t2(f1 INT NOT NULL,
29+
FOREIGN KEY(f1) REFERENCES t1(f2))ENGINE=InnoDB;
30+
INSERT INTO t2 VALUES(1);
31+
# Bulk operation shouldn't happen because of foreign key constraints
32+
ALTER TABLE t2 FORCE;
33+
DROP TABLE t2, t1;
34+
35+
CREATE TABLE t1 (f1 INT, f2 INT) ENGINE=InnoDB PARTITION BY HASH(f1) PARTITIONS 2;
36+
INSERT INTO t1 VALUES(1, 1);
37+
INSERT INTO t1 SELECT seq, seq * 2 FROM seq_1_to_2;
38+
# Buffer fits in the memory
39+
ALTER TABLE t1 FORCE;
40+
# Insert more entries
41+
INSERT INTO t1 SELECT seq, seq * 2 FROM seq_3_to_65536;
42+
# Alter should use temporary file for sorting
43+
ALTER TABLE t1 ADD INDEX(f2);
44+
DROP TABLE t1;

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,30 @@ DROP TABLE binaries, collections;
11861186

11871187
--echo # End of 10.6 tests
11881188

1189+
CREATE TABLE t1
1190+
(
1191+
f1 VARCHAR(32)BINARY NOT NULL,
1192+
f2 VARCHAR(32)BINARY NOT NULL,
1193+
PRIMARY KEY (f1),
1194+
INDEX(f2)
1195+
) ENGINE=InnoDB;
1196+
INSERT INTO t1 VALUES('MySQL', 'InnoDB'), ('MariaDB', 'NDB');
1197+
1198+
CREATE TABLE t2
1199+
(
1200+
f1 VARCHAR(32)BINARY NOT NULL,
1201+
f2 VARCHAR(255)BINARY NOT NULL,
1202+
f3 int, PRIMARY KEY (f1), INDEX(f1), INDEX(f2)
1203+
) ENGINE=InnoDB;
1204+
INSERT INTO t2 VALUES('MySQL', 'MySQL', 1),
1205+
('MariaDB', 'MariaDB', 1);
1206+
ALTER TABLE t1 ADD FOREIGN KEY (f1) REFERENCES t2 (f2);
1207+
# MDEV-33927 TODO: change the warning message
1208+
--error ER_FK_CANNOT_DELETE_PARENT
1209+
ALTER TABLE t2 ADD FOREIGN KEY (f2) REFERENCES t2 (f2),
1210+
ADD UNIQUE INDEX(f3);
1211+
drop table t1, t2;
11891212
SET GLOBAL innodb_stats_persistent = @save_stats_persistent;
11901213

1214+
--echo # End of 10.11 tests
11911215
--source include/wait_until_count_sessions.inc

mysql-test/suite/sys_vars/r/sysvars_innodb.result

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ NUMERIC_BLOCK_SIZE 0
5555
ENUM_VALUE_LIST NULL
5656
READ_ONLY YES
5757
COMMAND_LINE_ARGUMENT OPTIONAL
58+
VARIABLE_NAME INNODB_ALTER_COPY_BULK
59+
SESSION_VALUE NULL
60+
DEFAULT_VALUE ON
61+
VARIABLE_SCOPE GLOBAL
62+
VARIABLE_TYPE BOOLEAN
63+
VARIABLE_COMMENT Allow bulk insert operation for copy alter operation
64+
NUMERIC_MIN_VALUE NULL
65+
NUMERIC_MAX_VALUE NULL
66+
NUMERIC_BLOCK_SIZE NULL
67+
ENUM_VALUE_LIST OFF,ON
68+
READ_ONLY NO
69+
COMMAND_LINE_ARGUMENT NONE
5870
VARIABLE_NAME INNODB_AUTOEXTEND_INCREMENT
5971
SESSION_VALUE NULL
6072
DEFAULT_VALUE 64

sql/sql_insert.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4356,7 +4356,16 @@ bool select_insert::prepare_eof()
43564356
if (info.ignore || info.handle_duplicates != DUP_ERROR)
43574357
if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
43584358
table->file->ha_rnd_end();
4359-
table->file->extra(HA_EXTRA_END_ALTER_COPY);
4359+
if (error <= 0)
4360+
{
4361+
error= table->file->extra(HA_EXTRA_END_ALTER_COPY);
4362+
if (error == HA_ERR_FOUND_DUPP_KEY)
4363+
{
4364+
uint key_nr= table->file->get_dup_key(error);
4365+
if ((int)key_nr >= 0 && key_nr < table->s->keys)
4366+
print_keydup_error(table, &table->key_info[key_nr], MYF(0));
4367+
}
4368+
}
43604369
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
43614370
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
43624371

sql/sql_table.cc

Lines changed: 67 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11766,6 +11766,59 @@ bool mysql_trans_commit_alter_copy_data(THD *thd)
1176611766
DBUG_RETURN(error);
1176711767
}
1176811768

11769+
/** Handle the error when copying data from source to target table.
11770+
@param error error code
11771+
@param ignore alter ignore statement
11772+
@param to target table handler
11773+
@param thd Mysql Thread
11774+
@param alter_ctx Runtime context for alter statement
11775+
@retval false in case of error
11776+
@retval true in case of skipping the row and continue alter operation */
11777+
static bool
11778+
copy_data_error_ignore(int &error, bool ignore, TABLE *to,
11779+
THD *thd, Alter_table_ctx *alter_ctx)
11780+
{
11781+
if (to->file->is_fatal_error(error, HA_CHECK_DUP))
11782+
{
11783+
/* Not a duplicate key error. */
11784+
to->file->print_error(error, MYF(0));
11785+
error= 1;
11786+
return false;
11787+
}
11788+
/* Duplicate key error. */
11789+
if (unlikely(alter_ctx->fk_error_if_delete_row))
11790+
{
11791+
/* We are trying to omit a row from the table which serves
11792+
as parent in a foreign key. This might have broken
11793+
referential integrity so emit an error. Note that we
11794+
can't ignore this error even if we are
11795+
executing ALTER IGNORE TABLE. IGNORE allows to skip rows, but
11796+
doesn't allow to break unique or foreign key constraints, */
11797+
my_error(ER_FK_CANNOT_DELETE_PARENT, MYF(0),
11798+
alter_ctx->fk_error_id,
11799+
alter_ctx->fk_error_table);
11800+
return false;
11801+
}
11802+
if (ignore)
11803+
return true;
11804+
/* Ordinary ALTER TABLE. Report duplicate key error. */
11805+
uint key_nr= to->file->get_dup_key(error);
11806+
if (key_nr <= MAX_KEY)
11807+
{
11808+
const char *err_msg= ER_THD(thd, ER_DUP_ENTRY_WITH_KEY_NAME);
11809+
if (key_nr == 0 && to->s->keys > 0 &&
11810+
(to->key_info[0].key_part[0].field->flags &
11811+
AUTO_INCREMENT_FLAG))
11812+
err_msg= ER_THD(thd, ER_DUP_ENTRY_AUTOINCREMENT_CASE);
11813+
print_keydup_error(to,
11814+
key_nr >= to->s->keys ? NULL :
11815+
&to->key_info[key_nr],
11816+
err_msg, MYF(0));
11817+
}
11818+
else
11819+
to->file->print_error(error, MYF(0));
11820+
return false;
11821+
}
1176911822

1177011823
static int
1177111824
copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, bool ignore,
@@ -12027,58 +12080,11 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, bool ignore,
1202712080
to->auto_increment_field_not_null= FALSE;
1202812081
if (unlikely(error))
1202912082
{
12030-
if (to->file->is_fatal_error(error, HA_CHECK_DUP))
12031-
{
12032-
/* Not a duplicate key error. */
12033-
to->file->print_error(error, MYF(0));
12034-
error= 1;
12035-
break;
12036-
}
12037-
else
12038-
{
12039-
/* Duplicate key error. */
12040-
if (unlikely(alter_ctx->fk_error_if_delete_row))
12041-
{
12042-
/*
12043-
We are trying to omit a row from the table which serves as parent
12044-
in a foreign key. This might have broken referential integrity so
12045-
emit an error. Note that we can't ignore this error even if we are
12046-
executing ALTER IGNORE TABLE. IGNORE allows to skip rows, but
12047-
doesn't allow to break unique or foreign key constraints,
12048-
*/
12049-
my_error(ER_FK_CANNOT_DELETE_PARENT, MYF(0),
12050-
alter_ctx->fk_error_id,
12051-
alter_ctx->fk_error_table);
12052-
break;
12053-
}
12054-
12055-
if (ignore)
12056-
{
12057-
/* This ALTER IGNORE TABLE. Simply skip row and continue. */
12058-
to->file->restore_auto_increment(prev_insert_id);
12059-
delete_count++;
12060-
}
12061-
else
12062-
{
12063-
/* Ordinary ALTER TABLE. Report duplicate key error. */
12064-
uint key_nr= to->file->get_dup_key(error);
12065-
if ((int) key_nr >= 0)
12066-
{
12067-
const char *err_msg= ER_THD(thd, ER_DUP_ENTRY_WITH_KEY_NAME);
12068-
if (key_nr == 0 && to->s->keys > 0 &&
12069-
(to->key_info[0].key_part[0].field->flags &
12070-
AUTO_INCREMENT_FLAG))
12071-
err_msg= ER_THD(thd, ER_DUP_ENTRY_AUTOINCREMENT_CASE);
12072-
print_keydup_error(to,
12073-
key_nr >= to->s->keys ? NULL :
12074-
&to->key_info[key_nr],
12075-
err_msg, MYF(0));
12076-
}
12077-
else
12078-
to->file->print_error(error, MYF(0));
12079-
break;
12080-
}
12081-
}
12083+
if (!copy_data_error_ignore(error, ignore, to, thd, alter_ctx))
12084+
break;
12085+
DBUG_ASSERT(ignore);
12086+
to->file->restore_auto_increment(prev_insert_id);
12087+
delete_count++;
1208212088
}
1208312089
else
1208412090
{
@@ -12105,9 +12111,15 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, bool ignore,
1210512111
error= 1;
1210612112
}
1210712113
bulk_insert_started= 0;
12108-
if (!ignore)
12109-
to->file->extra(HA_EXTRA_END_ALTER_COPY);
12110-
12114+
if (!ignore && error <= 0)
12115+
{
12116+
int alt_error= to->file->extra(HA_EXTRA_END_ALTER_COPY);
12117+
if (alt_error > 0)
12118+
{
12119+
error= alt_error;
12120+
copy_data_error_ignore(error, false, to, thd, alter_ctx);
12121+
}
12122+
}
1211112123
cleanup_done= 1;
1211212124
to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
1211312125

0 commit comments

Comments
 (0)