diff --git a/mysql-test/suite/rocksdb/r/delete.result b/mysql-test/suite/rocksdb/r/delete.result index 6a5ec7764ba8..8ec3c50f4664 100644 --- a/mysql-test/suite/rocksdb/r/delete.result +++ b/mysql-test/suite/rocksdb/r/delete.result @@ -119,4 +119,48 @@ a b BEGIN; DELETE FROM t1 WHERE a <= 4 ORDER BY b DESC LIMIT 1; SAVEPOINT spt1; +DELETE FROM t1; +RELEASE SAVEPOINT spt1; +ROLLBACK; +SELECT a,b FROM t1; +a b +10000 foobar +10000 foobar +2 b +2 b +4 d +4 d +5 e +5 e +6 f +6 f +7 g +7 g +8 h +8 h +BEGIN; +DELETE FROM t1 WHERE a <= 4 ORDER BY b DESC LIMIT 1; +SAVEPOINT spt1; +DELETE FROM t1; +INSERT INTO t1 (a,b) VALUES (1,'a'); +ROLLBACK TO SAVEPOINT spt1; +ERROR HY000: MyRocks currently does not support ROLLBACK TO SAVEPOINT if modifying rows. +COMMIT; +ERROR HY000: This transaction was rolled back and cannot be committed. Only supported operation is to roll it back, so all pending changes will be discarded. Please restart another transaction. +SELECT a,b FROM t1; +a b +10000 foobar +10000 foobar +2 b +2 b +4 d +4 d +5 e +5 e +6 f +6 f +7 g +7 g +8 h +8 h DROP TABLE t1; diff --git a/mysql-test/suite/rocksdb/r/insert.result b/mysql-test/suite/rocksdb/r/insert.result index a9395e72ee2f..a1fb3ae90be0 100644 --- a/mysql-test/suite/rocksdb/r/insert.result +++ b/mysql-test/suite/rocksdb/r/insert.result @@ -185,21 +185,18 @@ SAVEPOINT spt1; INSERT INTO t1 SET a = 11, b = 'f'; INSERT t1 SET b = DEFAULT; ROLLBACK TO SAVEPOINT spt1; +ERROR HY000: MyRocks currently does not support ROLLBACK TO SAVEPOINT if modifying rows. INSERT INTO t1 (b,a) VALUES ('test1',10); COMMIT; +ERROR HY000: This transaction was rolled back and cannot be committed. Only supported operation is to roll it back, so all pending changes will be discarded. Please restart another transaction. SELECT a,b FROM t1; a b 1 a -10 NULL 10 foo -10 test1 100 foo 11 abc -11 f 2 b -20 NULL 3 c 4 d 5 e -NULL NULL DROP TABLE t1; diff --git a/mysql-test/suite/rocksdb/r/rpl_savepoint.result b/mysql-test/suite/rocksdb/r/rpl_savepoint.result new file mode 100644 index 000000000000..9424238da939 --- /dev/null +++ b/mysql-test/suite/rocksdb/r/rpl_savepoint.result @@ -0,0 +1,103 @@ +include/master-slave.inc +Warnings: +Note #### Sending passwords in plain text without SSL/TLS is extremely insecure. +Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information. +[connection master] +drop table if exists t1; +create table t1 (id int primary key, value int); +insert into t1 values (1,1), (2,2), (3,3); +begin; +insert into t1 values (11, 1); +savepoint a; +insert into t1 values (12, 1); +rollback to savepoint a; +ERROR HY000: MyRocks currently does not support ROLLBACK TO SAVEPOINT if modifying rows. +commit; +ERROR HY000: This transaction was rolled back and cannot be committed. Only supported operation is to roll it back, so all pending changes will be discarded. Please restart another transaction. +commit; +select * from t1; +id value +1 1 +2 2 +3 3 +include/sync_slave_sql_with_master.inc +select * from t1; +id value +1 1 +2 2 +3 3 +begin; +insert into t1 values (21, 1); +savepoint a; +insert into t1 values (22, 1); +rollback to savepoint a; +ERROR HY000: MyRocks currently does not support ROLLBACK TO SAVEPOINT if modifying rows. +insert into t1 values (23, 1); +ERROR HY000: This transaction was rolled back and cannot be committed. Only supported operation is to roll it back, so all pending changes will be discarded. Please restart another transaction. +commit; +ERROR HY000: This transaction was rolled back and cannot be committed. Only supported operation is to roll it back, so all pending changes will be discarded. Please restart another transaction. +commit; +select * from t1; +id value +1 1 +2 2 +3 3 +include/sync_slave_sql_with_master.inc +select * from t1; +id value +1 1 +2 2 +3 3 +begin; +insert into t1 values (31, 1); +savepoint a; +insert into t1 values (32, 1); +savepoint b; +insert into t1 values (33, 1); +rollback to savepoint a; +ERROR HY000: MyRocks currently does not support ROLLBACK TO SAVEPOINT if modifying rows. +insert into t1 values (34, 1); +ERROR HY000: This transaction was rolled back and cannot be committed. Only supported operation is to roll it back, so all pending changes will be discarded. Please restart another transaction. +rollback; +select * from t1; +id value +1 1 +2 2 +3 3 +include/sync_slave_sql_with_master.inc +select * from t1; +id value +1 1 +2 2 +3 3 +SET autocommit=off; +select * from t1; +id value +1 1 +2 2 +3 3 +SAVEPOINT A; +select * from t1; +id value +1 1 +2 2 +3 3 +SAVEPOINT A; +insert into t1 values (35, 35); +ROLLBACK TO SAVEPOINT A; +ERROR HY000: MyRocks currently does not support ROLLBACK TO SAVEPOINT if modifying rows. +START TRANSACTION; +ERROR HY000: This transaction was rolled back and cannot be committed. Only supported operation is to roll it back, so all pending changes will be discarded. Please restart another transaction. +select * from t1; +id value +1 1 +2 2 +3 3 +include/sync_slave_sql_with_master.inc +select * from t1; +id value +1 1 +2 2 +3 3 +drop table t1; +include/rpl_end.inc diff --git a/mysql-test/suite/rocksdb/r/update.result b/mysql-test/suite/rocksdb/r/update.result index 2d11d98664db..33bf8d09405b 100644 --- a/mysql-test/suite/rocksdb/r/update.result +++ b/mysql-test/suite/rocksdb/r/update.result @@ -92,20 +92,22 @@ UPDATE t1 SET b = 'update2' WHERE a <= 100; SAVEPOINT spt1; UPDATE t1 SET b = ''; ROLLBACK TO SAVEPOINT spt1; +ERROR HY000: MyRocks currently does not support ROLLBACK TO SAVEPOINT if modifying rows. UPDATE t1 SET b = 'upd' WHERE a = 10050; COMMIT; +ERROR HY000: This transaction was rolled back and cannot be committed. Only supported operation is to roll it back, so all pending changes will be discarded. Please restart another transaction. SELECT * FROM t1 ORDER BY pk; a b pk -51 1 -52 2 -53 3 -54 4 -55 5 -10050 upd 6 -51 7 -52 8 -53 9 -54 10 -55 11 -10050 upd 12 +51 NULL 1 +52 NULL 2 +53 NULL 3 +54 NULL 4 +55 NULL 5 +10050 NULL 6 +51 NULL 7 +52 NULL 8 +53 NULL 9 +54 NULL 10 +55 NULL 11 +10050 NULL 12 DROP TABLE t1; diff --git a/mysql-test/suite/rocksdb/t/delete.test b/mysql-test/suite/rocksdb/t/delete.test index b3ea014e8a2b..b1654e606a5c 100644 --- a/mysql-test/suite/rocksdb/t/delete.test +++ b/mysql-test/suite/rocksdb/t/delete.test @@ -79,8 +79,6 @@ BEGIN; DELETE FROM t1 WHERE a <= 4 ORDER BY b DESC LIMIT 1; SAVEPOINT spt1; ---disable_parsing - DELETE FROM t1; RELEASE SAVEPOINT spt1; ROLLBACK; @@ -92,10 +90,12 @@ DELETE FROM t1 WHERE a <= 4 ORDER BY b DESC LIMIT 1; SAVEPOINT spt1; DELETE FROM t1; INSERT INTO t1 (a,b) VALUES (1,'a'); +--error ER_UNKNOWN_ERROR ROLLBACK TO SAVEPOINT spt1; +--error ER_UNKNOWN_ERROR COMMIT; - ---enable_parsing +--sorted_result +SELECT a,b FROM t1; DROP TABLE t1; diff --git a/mysql-test/suite/rocksdb/t/insert.test b/mysql-test/suite/rocksdb/t/insert.test index 4cc0a2d219c8..14cfe1cadb86 100644 --- a/mysql-test/suite/rocksdb/t/insert.test +++ b/mysql-test/suite/rocksdb/t/insert.test @@ -86,8 +86,10 @@ INSERT t1 (a) VALUE (10),(20); SAVEPOINT spt1; INSERT INTO t1 SET a = 11, b = 'f'; INSERT t1 SET b = DEFAULT; +--error ER_UNKNOWN_ERROR ROLLBACK TO SAVEPOINT spt1; INSERT INTO t1 (b,a) VALUES ('test1',10); +--error ER_UNKNOWN_ERROR COMMIT; --sorted_result SELECT a,b FROM t1; diff --git a/mysql-test/suite/rocksdb/t/rpl_savepoint.cnf b/mysql-test/suite/rocksdb/t/rpl_savepoint.cnf new file mode 100644 index 000000000000..b46b417c257b --- /dev/null +++ b/mysql-test/suite/rocksdb/t/rpl_savepoint.cnf @@ -0,0 +1,7 @@ +!include suite/rpl/my.cnf + +[mysqld.1] +binlog_format=row +[mysqld.2] +binlog_format=row + diff --git a/mysql-test/suite/rocksdb/t/rpl_savepoint.test b/mysql-test/suite/rocksdb/t/rpl_savepoint.test new file mode 100644 index 000000000000..0f26c24c27d6 --- /dev/null +++ b/mysql-test/suite/rocksdb/t/rpl_savepoint.test @@ -0,0 +1,90 @@ +--source include/have_rocksdb.inc + +source include/master-slave.inc; + +connection master; +--disable_warnings +drop table if exists t1; +--enable_warnings + +connection master; + +create table t1 (id int primary key, value int); +insert into t1 values (1,1), (2,2), (3,3); + +begin; +insert into t1 values (11, 1); +savepoint a; +insert into t1 values (12, 1); +--error ER_UNKNOWN_ERROR +rollback to savepoint a; +--error ER_UNKNOWN_ERROR +commit; +commit; +select * from t1; + +--source include/sync_slave_sql_with_master.inc +connection slave; + +select * from t1; + +connection master; +begin; +insert into t1 values (21, 1); +savepoint a; +insert into t1 values (22, 1); +--error ER_UNKNOWN_ERROR +rollback to savepoint a; +--error ER_UNKNOWN_ERROR +insert into t1 values (23, 1); +--error ER_UNKNOWN_ERROR +commit; +commit; +select * from t1; + +--source include/sync_slave_sql_with_master.inc +connection slave; +select * from t1; + + +connection master; +begin; +insert into t1 values (31, 1); +savepoint a; +insert into t1 values (32, 1); +savepoint b; +insert into t1 values (33, 1); +--error ER_UNKNOWN_ERROR +rollback to savepoint a; +--error ER_UNKNOWN_ERROR +insert into t1 values (34, 1); +rollback; +select * from t1; + +--source include/sync_slave_sql_with_master.inc +connection slave; +select * from t1; + +### GitHub Issue#195 +connection master; +SET autocommit=off; +select * from t1; +SAVEPOINT A; +select * from t1; +SAVEPOINT A; +insert into t1 values (35, 35); +--error ER_UNKNOWN_ERROR +ROLLBACK TO SAVEPOINT A; +--error ER_UNKNOWN_ERROR +START TRANSACTION; +select * from t1; +--source include/sync_slave_sql_with_master.inc +connection slave; +select * from t1; + + +connection master; +drop table t1; + +--source include/rpl_end.inc + diff --git a/mysql-test/suite/rocksdb/t/update.test b/mysql-test/suite/rocksdb/t/update.test index a1d53d57a2b5..e0a49ee8ca50 100644 --- a/mysql-test/suite/rocksdb/t/update.test +++ b/mysql-test/suite/rocksdb/t/update.test @@ -61,8 +61,10 @@ BEGIN; UPDATE t1 SET b = 'update2' WHERE a <= 100; SAVEPOINT spt1; UPDATE t1 SET b = ''; +--error ER_UNKNOWN_ERROR ROLLBACK TO SAVEPOINT spt1; UPDATE t1 SET b = 'upd' WHERE a = 10050; +--error ER_UNKNOWN_ERROR COMMIT; SELECT * FROM t1 ORDER BY pk; diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc index 7e0dbc7430f8..1a25e6552d70 100644 --- a/storage/rocksdb/ha_rocksdb.cc +++ b/storage/rocksdb/ha_rocksdb.cc @@ -150,6 +150,13 @@ background_thread_control bg_control; // collation check requirement. std::vector collation_exception_list; +static const char* const ERRSTR_ROLLBACK_ONLY + = "This transaction was rolled back and cannot be " + "committed. Only supported operation is to roll it back, " + "so all pending changes will be discarded. " + "Please restart another transaction."; + + static void rocksdb_flush_all_memtables() { @@ -1229,6 +1236,7 @@ class Rdb_transaction bool is_trx_failed= false; bool is_delayed_snapshot= false; bool tx_read_only= false; + bool rollback_only= false; THD* thd_= nullptr; @@ -1308,7 +1316,27 @@ class Rdb_transaction bool commit() { - if (get_n_writes() != 0) + if (get_n_writes() == 0) + { + rollback(); + return false; + } + else if (rollback_only) + { + /* + Transactions marked as rollback_only are expected to be rolled back at + prepare(). But there are some exceptions like below that prepare() is + never called and commit() is called instead. + 1. Binlog is disabled + 2. No modification exists in binlog cache for the transaction (#195) + In both cases, rolling back transaction is safe. Nothing is written to + binlog. + */ + my_printf_error(ER_UNKNOWN_ERROR, ERRSTR_ROLLBACK_ONLY, MYF(0)); + rollback(); + return true; + } + else { thd_binlog_pos(thd_, &mysql_log_file_name, &mysql_log_offset, &mysql_gtid); @@ -1317,11 +1345,6 @@ class Rdb_transaction mysql_gtid, txn->GetWriteBatch()); return commit_no_binlog(); } - else - { - rollback(); - return 0; - } } bool commit_no_binlog() @@ -1338,6 +1361,7 @@ class Rdb_transaction txn= NULL; n_writes= 0; set_tx_read_only(false); + rollback_only= false; return res; } @@ -1353,6 +1377,7 @@ class Rdb_transaction delete txn; txn= NULL; set_tx_read_only(false); + rollback_only= false; } } @@ -1458,6 +1483,12 @@ class Rdb_transaction return txn->SingleDelete(column_family, key); } + bool has_modifications() + { + return txn->GetWriteBatch() && txn->GetWriteBatch()->GetWriteBatch() && + txn->GetWriteBatch()->GetWriteBatch()->Count() > 0; + } + /* Return a WriteBatch that one can write to. The writes will skip any transaction locking. The writes WILL be visible to the transaction. @@ -1584,6 +1615,30 @@ class Rdb_transaction } } + bool can_prepare() + { + if (rollback_only) + { + my_printf_error(ER_UNKNOWN_ERROR, ERRSTR_ROLLBACK_ONLY, MYF(0)); + return false; + } + return true; + } + + int rollback_to_savepoint(void *savepoint) + { + if (has_modifications()) + { + my_printf_error(ER_UNKNOWN_ERROR, + "MyRocks currently does not support ROLLBACK TO " + "SAVEPOINT if modifying rows.", + MYF(0)); + rollback_only= true; + return 1; + } + return 0; + } + /* This is used by transactions started with "START TRANSACTION WITH " "CONSISTENT [ROCKSDB] SNAPSHOT". When tx_read_only is turned on, @@ -1668,6 +1723,11 @@ static int rocksdb_close_connection(handlerton* hton, THD* thd) static int rocksdb_prepare(handlerton* hton, THD* thd, bool prepare_trx, bool async) { + Rdb_transaction*& trx= get_trx_from_thd(thd); + if (!trx->can_prepare()) + { + return 1; + } if (prepare_trx || (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { /* We were instructed to prepare the whole transaction, or @@ -1675,7 +1735,6 @@ static int rocksdb_prepare(handlerton* hton, THD* thd, bool prepare_trx, std::vector slave_gtid_info; thd_slave_gtid_info(thd, &slave_gtid_info); for (auto it : slave_gtid_info) { - Rdb_transaction*& trx= get_trx_from_thd(thd); rocksdb::WriteBatchBase* write_batch = trx->GetBlindWriteBatch(); binlog_manager.update_slave_gtid_info(it.id, it.db, it.gtid, write_batch); } @@ -2123,7 +2182,8 @@ static int rocksdb_savepoint(handlerton *hton, THD *thd, void *savepoint) static int rocksdb_rollback_to_savepoint(handlerton *hton, THD *thd, void *savepoint) { - return 0; + Rdb_transaction*& trx= get_trx_from_thd(thd); + return trx->rollback_to_savepoint(savepoint); } static bool rocksdb_rollback_to_savepoint_can_release_mdl(handlerton *hton,