mariadb/mysql-test/main/alter_table_online.result
Nikita Malyavin ab4bfad206 MDEV-16329 [5/5] ALTER ONLINE TABLE
* Log rows in online_alter_binlog.
* Table online data is replicated within dedicated binlog file
* Cached data is written on commit.
* Versioning is fully supported.
* Works both wit and without binlog enabled.

* For now savepoints setup is forbidden while ONLINE ALTER goes on.
  Extra support is required. We can simply log the SAVEPOINT query events
  and replicate them together with row events. But it's not implemented
  for now.

* Cache flipping:

  We want to care for the possible bottleneck in the online alter binlog
  reading/writing in advance.

  IO_CACHE does not provide anything better that sequential access,
  besides, only a single write is mutex-protected, which is not suitable,
  since we should write a transaction atomically.

  To solve this, a special layer on top Event_log is implemented.
  There are two IO_CACHE files underneath: one for reading, and one for
  writing.

  Once the read cache is empty, an exclusive lock is acquired (we can wait
  for a currently active transaction finish writing), and flip() is emitted,
  i.e. the write cache is reopened for read, and the read cache is emptied,
  and reopened for writing.

  This reminds a buffer flip that happens in accelerated graphics
  (DirectX/OpenGL/etc).

  Cache_flip_event_log is considered non-blocking for a single reader and a
  single writer in this sense, with the only lock held by reader during flip.

  An alternative approach by implementing a fair concurrent circular buffer
  is described in MDEV-24676.

* Cache managers:
  We have two cache sinks: statement and transactional.
  It is important that the changes are first cached per-statement and
  per-transaction.
  If a statement fails, then only statement data is rolled back. The
  transaction moves along, however.

  Turns out, there's no guarantee that TABLE well persist in
  thd->open_tables to the transaction commit moment.
  If an error occurs, tables from statement are purged.
  Therefore, we can't store te caches in TABLE. Ideally, it should be
  handlerton, but we cut the corner and store it in THD in a list.
2023-08-15 10:16:11 +02:00

460 lines
12 KiB
Text

connect con2, localhost, root,,;
connection default;
#
# Test insert
#
# Insert and add column
create or replace table t1 (a int) engine=innodb;
insert t1 values (5);
connection con2;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
alter table t1 add b int NULL, algorithm= copy, lock= none;
connection con2;
insert into t1 values (123), (456), (789);
set debug_sync= 'now SIGNAL end';
connection default;
select * from t1;
a b
5 NULL
123 NULL
456 NULL
789 NULL
# Insert and add NOT NULL column without default value
create or replace table t1 (a int) engine=innodb;
insert t1 values (5);
connection con2;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
alter table t1 add b int NOT NULL, algorithm= copy, lock= none;
connection con2;
insert into t1 values (123), (456), (789);
set debug_sync= 'now SIGNAL end';
connection default;
Warnings:
Warning 1364 Field 'b' doesn't have a default value
Warning 1364 Field 'b' doesn't have a default value
Warning 1364 Field 'b' doesn't have a default value
select * from t1;
a b
5 0
123 0
456 0
789 0
# Insert and add a column with a default value
create or replace table t1 (a int) engine=innodb;
insert t1 values (5);
connection con2;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
alter table t1 add b int NOT NULL default (222), algorithm= copy, lock= none;
connection con2;
insert into t1 values (123), (456), (789);
set debug_sync= 'now SIGNAL end';
connection default;
select * from t1;
a b
5 222
123 222
456 222
789 222
#
# Test update
#
# Update and add a column
create or replace table t1 (a int primary key, b int) engine=innodb;
insert t1 values (1, 22);
insert t1 values (3, 44);
connection con2;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
alter table t1 add c int default(1),
algorithm= copy, lock= none;
connection con2;
update t1 set b= 55 where a = 1;
set debug_sync= 'now SIGNAL end';
connection default;
select * from t1;
a b c
1 55 1
3 44 1
#
# Test primary key change
#
# Drop key, add key
create or replace table t1 (a int primary key, b int) engine=innodb;
insert t1 values (1, 22);
insert t1 values (3, 44);
connection con2;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
alter table t1 drop primary key, add primary key(b),
algorithm= copy, lock= none;
connection con2;
update t1 set b= 55 where a = 1;
set debug_sync= 'now SIGNAL end';
connection default;
select * from t1;
a b
3 44
1 55
# Drop key, add key. Two updates
create or replace table t1 (a int primary key, b int) engine=innodb;
insert t1 values (1, 11);
insert t1 values (2, 22);
connection con2;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
alter table t1 drop primary key, add primary key(b),
algorithm= copy, lock= none;
connection con2;
update t1 set b= 33 where a = 1;
update t1 set b= 44 where a = 2;
set debug_sync= 'now SIGNAL end';
connection default;
select * from t1;
a b
1 33
2 44
#
# Various tests, see below
#
create or replace table t1 (a int primary key, b int) engine=innodb;
insert t1 values (1, 11);
insert t1 values (2, 22);
insert t1 values (3, 33);
insert t1 values (4, 44);
insert t1 values (5, 55);
insert t1 values (6, 66);
insert t1 values (7, 77);
insert t1 values (8, 88);
insert t1 values (9, 99);
connection con2;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
alter table t1 drop primary key, add primary key(b),
algorithm= copy, lock= none;
connection con2;
# Two updates
update t1 set b= 1001 where a = 1;
update t1 set b= 2002 where a = 2;
# Two updates in transaction
set autocommit = 0;
start transaction;
update t1 set b= 3003 where a = 3;
update t1 set b= 4004 where a = 4;
commit;
set autocommit = 1;
# Second update is rolled back
update t1 set b= 5005 where a = 5;
set autocommit = 0;
start transaction;
update t1 set b= 6006 where a = 6;
rollback;
set autocommit = 1;
# Second execution in transaction fails
set autocommit = 0;
start transaction;
update t1 set b= 7007 where a = 7;
update t1 set a= 8, b= 8008 where a = 8 or a = 9 order by a;
ERROR 23000: Duplicate entry '8' for key 'PRIMARY'
commit;
set autocommit = 1;
select * from t1;
a b
1 1001
2 2002
3 3003
4 4004
5 5005
6 66
7 7007
8 88
9 99
set debug_sync= 'now SIGNAL end';
connection default;
select * from t1;
a b
1 1001
2 2002
3 3003
4 4004
5 5005
6 66
7 7007
8 88
9 99
#
# MYISAM. Only Inserts can be tested.
#
create or replace table t1 (a int) engine=myisam;
insert t1 values (5);
connection con2;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
alter table t1 add b int NULL, algorithm= copy, lock= none;
connection con2;
insert into t1 values (123), (456), (789);
set debug_sync= 'now SIGNAL end';
connection default;
select * from t1;
a b
5 NULL
123 NULL
456 NULL
789 NULL
# Test incompatible changes
create or replace table t1 (a int primary key, b int) engine=innodb;
insert t1 values (1, 22);
insert t1 values (3, 44);
connection con2;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
alter table t1 drop primary key, add primary key(b),
algorithm= copy, lock= none;
connection con2;
update t1 set b= 44 where a = 1;
set debug_sync= 'now SIGNAL end';
connection default;
ERROR 23000: Duplicate entry '44' for key 'PRIMARY'
select * from t1;
a b
1 44
3 44
# Test log read after EXCLUSIVE lock
# Transaction is started before ALTER, and UPDATE is made.
# Then more UPDATEs.
create or replace table t1 (a int primary key, b int) engine=innodb;
insert t1 values (1, 11);
insert t1 values (2, 22);
insert t1 values (3, 33);
insert t1 values (4, 44);
insert t1 values (5, 55);
set debug_sync= 'alter_table_online_before_lock SIGNAL locking WAIT_FOR end';
set debug_sync= 'alter_table_online_downgraded SIGNAL downgraded';
alter table t1 drop primary key, add primary key(b),
algorithm= copy, lock= none;
connection con2;
begin;
set debug_sync= 'now WAIT_FOR downgraded';
update t1 set b= 111 where a = 1;
set debug_sync= 'now WAIT_FOR locking';
set debug_sync= 'now SIGNAL end';
update t1 set b= 222 where a = 2;
update t1 set b= 333 where a = 3;
update t1 set b= 444 where a = 4;
commit;
update t1 set b= 555 where a = 5;
connection default;
select * from t1;
a b
1 111
2 222
3 333
4 444
5 555
#
# Test progress report.
#
create or replace table t1 (a int primary key, b int) engine=innodb;
insert t1 values (1, 11);
insert t1 values (2, 22);
insert t1 values (3, 33);
insert t1 values (4, 44);
set debug_sync= 'alter_table_online_before_lock SIGNAL locking WAIT_FOR end';
set debug_sync= 'alter_table_online_downgraded SIGNAL downgraded'
' WAIT_FOR start_replication';
set debug_sync= 'alter_table_online_progress SIGNAL applied WAIT_FOR proceed'
' EXECUTE 9';
alter table t1 drop primary key, add primary key(b),
algorithm= copy, lock= none;
connection con2;
set debug_sync= 'now WAIT_FOR downgraded';
update t1 set b= 111 where a = 1;
insert t1 values (5, 55);
update t1 set b= 555 where a = 5;
insert t1 values (6, 66);
update t1 set b= 666 where a = 6;
set debug_sync= 'now SIGNAL start_replication';
# First signal is for log description event.
set debug_sync= 'now WAIT_FOR applied';
select stage, progress from INFORMATION_SCHEMA.PROCESSLIST where id = @con;
stage progress
3 51.220
set debug_sync= 'now SIGNAL proceed WAIT_FOR applied';
select stage, progress from INFORMATION_SCHEMA.PROCESSLIST where id = @con;
stage progress
3 61.789
set debug_sync= 'now SIGNAL proceed WAIT_FOR applied';
select stage, progress from INFORMATION_SCHEMA.PROCESSLIST where id = @con;
stage progress
3 70.325
set debug_sync= 'now SIGNAL proceed WAIT_FOR applied';
select stage, progress from INFORMATION_SCHEMA.PROCESSLIST where id = @con;
stage progress
3 80.894
set debug_sync= 'now SIGNAL proceed WAIT_FOR applied';
select stage, progress from INFORMATION_SCHEMA.PROCESSLIST where id = @con;
stage progress
3 89.431
set debug_sync= 'now SIGNAL proceed WAIT_FOR applied';
select stage, progress from INFORMATION_SCHEMA.PROCESSLIST where id = @con;
stage progress
3 100.000
set debug_sync= 'now SIGNAL proceed WAIT_FOR locking';
begin;
update t1 set b= 222 where a = 2;
update t1 set b= 333 where a = 3;
update t1 set b= 444 where a = 4;
commit;
set debug_sync= 'now SIGNAL end WAIT_FOR applied';
select stage, progress from INFORMATION_SCHEMA.PROCESSLIST where id = @con;
stage progress
4 33.333
set debug_sync= 'now SIGNAL proceed WAIT_FOR applied';
select stage, progress from INFORMATION_SCHEMA.PROCESSLIST where id = @con;
stage progress
4 66.667
set debug_sync= 'now SIGNAL proceed WAIT_FOR applied';
select stage, progress from INFORMATION_SCHEMA.PROCESSLIST where id = @con;
stage progress
4 100.000
set debug_sync= 'now SIGNAL proceed';
connection default;
select * from t1;
a b
1 111
2 222
3 333
4 444
5 555
6 666
#
# Test system versioning
#
create or replace table t1 (a int primary key, b int);
insert t1 values (1, 22);
insert t1 values (3, 44);
connection con2;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
set timestamp = 1;
alter table t1 add system versioning,
algorithm= copy, lock= none;
connection con2;
set timestamp = 2;
update t1 set b= 55 where a = 1;
set timestamp = 3;
insert into t1 values (6, 77);
set debug_sync= 'now SIGNAL end';
connection default;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=*SUBSTITUTED* DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING
select *, UNIX_TIMESTAMP(row_start), UNIX_TIMESTAMP(row_end) from t1 for system_time all;
a b UNIX_TIMESTAMP(row_start) UNIX_TIMESTAMP(row_end)
1 55 1.000000 2147483647.999999
3 44 1.000000 2147483647.999999
6 77 1.000000 2147483647.999999
connection con2;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
alter table t1 drop system versioning,
algorithm= copy, lock= none;
connection con2;
update t1 set b= 88 where a = 1;
set debug_sync= 'now SIGNAL end';
connection default;
# Can't UPDATE versioned -> plain (and can't DELETE)
ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=*SUBSTITUTED* DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING
select *, UNIX_TIMESTAMP(row_start), UNIX_TIMESTAMP(row_end) from t1 for system_time all;
a b UNIX_TIMESTAMP(row_start) UNIX_TIMESTAMP(row_end)
1 55 1.000000 3.000000
1 88 3.000000 2147483647.999999
3 44 1.000000 2147483647.999999
6 77 1.000000 2147483647.999999
connection con2;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
alter table t1 drop system versioning,
algorithm= copy, lock= none;
connection con2;
insert into t1 values (8, 99);
set debug_sync= 'now SIGNAL end';
connection default;
# INSERT versioned -> plain works fine since it is a single versioned op.
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=*SUBSTITUTED* DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
select * from t1;
a b
1 88
3 44
6 77
8 99
#
# Test ROLLBACK TO SAVEPOINT
#
create or replace table t1 (a int) engine=innodb;
insert t1 values (1), (2);
create or replace table t2 (a int) engine=innodb;
insert t2 values (1), (2);
connection con2;
begin;
update t2 set a= 222 where a = 2;
savepoint savie;
update t2 set a= 111 where a = 1;
set debug_sync= 'now WAIT_FOR ended';
connection default;
set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end';
alter table t1 add b int NULL, algorithm= copy, lock= none;
connection con2;
update t1 set a= 123 where a = 1;
savepoint whoopsie;
ERROR 42000: Cannot set up a savepoint while online ALTER TABLE is in progress
show warnings;
Level Code Message
Error 1235 Cannot set up a savepoint while online ALTER TABLE is in progress
rollback to savepoint savie;
commit;
set debug_sync= 'now SIGNAL end';
connection default;
select * from t1;
a b
1 NULL
2 NULL
select * from t2;
a
1
222
# Cleanup
set debug_sync= 'reset';
drop table t1;
drop table t2;