diff options
author | Nikita Malyavin <nikitamalyavin@gmail.com> | 2021-12-14 18:04:24 +0300 |
---|---|---|
committer | Nikita Malyavin <nikitamalyavin@gmail.com> | 2022-10-17 15:24:44 +0300 |
commit | c4298ec5d55798258f202aed8f6aaed727ad591d (patch) | |
tree | 6a543eb6bd0c503e0246db5b26134e7bd2f18539 | |
parent | 5d45826f87492c4d0aca68ced6d984ca4b66d3fa (diff) | |
download | mariadb-git-c4298ec5d55798258f202aed8f6aaed727ad591d.tar.gz |
savepoints
-rw-r--r-- | mysql-test/main/alter_table_online.result | 55 | ||||
-rw-r--r-- | mysql-test/main/alter_table_online.test | 50 | ||||
-rw-r--r-- | sql/handler.cc | 7 | ||||
-rw-r--r-- | sql/log.cc | 65 | ||||
-rw-r--r-- | sql/log.h | 3 | ||||
-rw-r--r-- | sql/transaction.cc | 74 |
6 files changed, 216 insertions, 38 deletions
diff --git a/mysql-test/main/alter_table_online.result b/mysql-test/main/alter_table_online.result index dbee4445324..77cfc1b5c5f 100644 --- a/mysql-test/main/alter_table_online.result +++ b/mysql-test/main/alter_table_online.result @@ -459,10 +459,6 @@ 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'; @@ -475,7 +471,58 @@ select * from t2; a 1 222 +create or replace table t1 (a int); +insert t1 values (1), (2); +create or replace table t2 (a int); +insert t2 values (1), (2); +create or replace table t3 (a int) engine=myisam; +insert t3 values (1); +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= 222 where a = 2; +savepoint whoopsie; +update t1 set a= 123 where a = 1; +insert t3 values (2); +select * from t1; +a +123 +222 +rollback to savepoint whoopsie; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +select * from t1; +a +1 +222 +select * from t3; +a +1 +2 +commit; +set debug_sync= 'now SIGNAL end'; +connection default; +select * from t1; +a b +1 NULL +222 NULL +select * from t2; +a +111 +222 +select * from t3; +a +1 +2 # Cleanup set debug_sync= 'reset'; drop table t1; drop table t2; +drop table t3; diff --git a/mysql-test/main/alter_table_online.test b/mysql-test/main/alter_table_online.test index a1beda943dc..37562a2d764 100644 --- a/mysql-test/main/alter_table_online.test +++ b/mysql-test/main/alter_table_online.test @@ -601,9 +601,7 @@ alter table t1 add b int NULL, algorithm= copy, lock= none; --reap update t1 set a= 123 where a = 1; ---error ER_NOT_SUPPORTED_YET savepoint whoopsie; -show warnings; rollback to savepoint savie; commit; @@ -616,7 +614,55 @@ set debug_sync= 'now SIGNAL end'; select * from t1; select * from t2; + +create or replace table t1 (a int); +insert t1 values (1), (2); + +create or replace table t2 (a int); +insert t2 values (1), (2); + +create or replace table t3 (a int) engine=myisam; +insert t3 values (1); + +--connection con2 +begin; +update t2 set a= 222 where a = 2; +savepoint savie; +update t2 set a= 111 where a = 1; + +--send +set debug_sync= 'now WAIT_FOR ended'; + +--connection default +set debug_sync= 'alter_table_copy_end SIGNAL ended WAIT_FOR end'; + +--send +alter table t1 add b int NULL, algorithm= copy, lock= none; + +--connection con2 +--reap +update t1 set a= 222 where a = 2; +savepoint whoopsie; +update t1 set a= 123 where a = 1; +insert t3 values (2); + +select * from t1; +rollback to savepoint whoopsie; +select * from t1; +select * from t3; +commit; + +set debug_sync= 'now SIGNAL end'; + +--connection default +--reap + +select * from t1; +select * from t2; +select * from t3; + --echo # Cleanup set debug_sync= 'reset'; drop table t1; drop table t2; +drop table t3; diff --git a/sql/handler.cc b/sql/handler.cc index f846f6687bd..053e6a0fa3c 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2920,13 +2920,6 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv) Ha_trx_info *ha_info= trans->ha_list; DBUG_ENTER("ha_savepoint"); - if (!thd->online_alter_cache_list.empty()) - { - my_printf_error(ER_NOT_SUPPORTED_YET, "Cannot set up a savepoint while " - "online ALTER TABLE is in progress", MYF(0)); - DBUG_RETURN(1); - } - for (; ha_info; ha_info= ha_info->next()) { int err; diff --git a/sql/log.cc b/sql/log.cc index a4b38d03592..a1af7a03030 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -279,7 +279,7 @@ void make_default_log_name(char **out, const char* log_ext, bool once) class binlog_cache_data: public ilist_node<> { public: - binlog_cache_data(): share(0), m_pending(0), status(0), + binlog_cache_data(): share(0), sv_list(0), m_pending(0), status(0), before_stmt_pos(MY_OFF_T_UNDEF), incident(FALSE), saved_max_binlog_cache_size(0), ptr_binlog_cache_use(0), @@ -420,6 +420,7 @@ public: IO_CACHE cache_log; TABLE_SHARE *share; // for online alter table + SAVEPOINT *sv_list; private: /* Pending binrows event. This event is the event where the rows are currently @@ -2565,6 +2566,9 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv) int error= 1; DBUG_ENTER("binlog_savepoint_set"); + if (!mysql_bin_log.is_open() && !thd->online_alter_cache_list.empty()) + DBUG_RETURN(0); + char buf[1024]; String log_query(buf, sizeof(buf), &my_charset_bin); @@ -2597,8 +2601,8 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv) { DBUG_ENTER("binlog_savepoint_rollback"); - if (!thd->online_alter_cache_list.empty()) - binlog_online_alter_rollback(thd, !thd->in_sub_stmt); + if (!mysql_bin_log.is_open() && !thd->online_alter_cache_list.empty()) + DBUG_RETURN(0); /* Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some @@ -7684,6 +7688,61 @@ static void binlog_online_alter_rollback(THD *thd, bool all) #endif // HAVE_REPLICATION } +SAVEPOINT** find_savepoint_in_list(THD *thd, LEX_CSTRING name, + SAVEPOINT ** const list); + +SAVEPOINT* savepoint_add(THD *thd, LEX_CSTRING name, SAVEPOINT **list, + int (*release_old)(THD*, SAVEPOINT*)); + +int online_alter_savepoint_set(THD *thd, LEX_CSTRING name) +{ + + DBUG_ENTER("binlog_online_alter_savepoint"); +#ifdef HAVE_REPLICATION + if (thd->online_alter_cache_list.empty()) + DBUG_RETURN(0); + + if (savepoint_alloc_size < sizeof (SAVEPOINT) + sizeof(my_off_t)) + savepoint_alloc_size= sizeof (SAVEPOINT) + sizeof(my_off_t); + + for (auto &cache: thd->online_alter_cache_list) + { + if (cache.share->db_type()->savepoint_set == NULL) + continue; + + SAVEPOINT *sv= savepoint_add(thd, name, &cache.sv_list, NULL); + if(unlikely(sv == NULL)) + DBUG_RETURN(1); + my_off_t *pos= (my_off_t*)(sv+1); + *pos= cache.get_byte_position(); + + sv->prev= cache.sv_list; + cache.sv_list= sv; + } +#endif + DBUG_RETURN(0); +} + +int online_alter_savepoint_rollback(THD *thd, LEX_CSTRING name) +{ + DBUG_ENTER("online_alter_savepoint_rollback"); +#ifdef HAVE_REPLICATION + for (auto &cache: thd->online_alter_cache_list) + { + if (cache.share->db_type()->savepoint_set == NULL) + continue; + + SAVEPOINT **sv= find_savepoint_in_list(thd, name, &cache.sv_list); + // sv is null if savepoint was set up before online table was modified + my_off_t pos= *sv ? *(my_off_t*)(*sv+1) : 0; + + cache.restore_savepoint(pos); + } + +#endif + DBUG_RETURN(0); +} + /* Write the contents of a cache to the binary log. diff --git a/sql/log.h b/sql/log.h index 1bf17103d53..5f24c9f5492 100644 --- a/sql/log.h +++ b/sql/log.h @@ -1415,4 +1415,7 @@ int binlog_rollback_by_xid(handlerton *hton, XID *xid); bool write_bin_log_start_alter(THD *thd, bool& partial_alter, uint64 start_alter_id, bool log_if_exists); +int online_alter_savepoint_set(THD *thd, LEX_CSTRING name); +int online_alter_savepoint_rollback(THD *thd, LEX_CSTRING name); + #endif /* LOG_H */ diff --git a/sql/transaction.cc b/sql/transaction.cc index 123f9283d23..d7425295a79 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -550,17 +550,17 @@ bool trans_rollback_stmt(THD *thd) DBUG_RETURN(FALSE); } -/* Find a named savepoint in the current transaction. */ -static SAVEPOINT ** -find_savepoint(THD *thd, LEX_CSTRING name) +/** Find a savepoint by name in a savepoint list */ +SAVEPOINT** find_savepoint_in_list(THD *thd, LEX_CSTRING name, + SAVEPOINT ** const list) { - SAVEPOINT **sv= &thd->transaction->savepoints; + SAVEPOINT **sv= list; while (*sv) { if (system_charset_info->strnncoll( - (uchar *) name.str, name.length, - (uchar *) (*sv)->name, (*sv)->length) == 0) + (uchar *) name.str, name.length, + (uchar *) (*sv)->name, (*sv)->length) == 0) break; sv= &(*sv)->prev; } @@ -568,6 +568,43 @@ find_savepoint(THD *thd, LEX_CSTRING name) return sv; } +/* Find a named savepoint in the current transaction. */ +static SAVEPOINT ** +find_savepoint(THD *thd, LEX_CSTRING name) +{ + return find_savepoint_in_list(thd, name, &thd->transaction->savepoints); +} + +SAVEPOINT* savepoint_add(THD *thd, LEX_CSTRING name, SAVEPOINT **list, + int (*release_old)(THD*, SAVEPOINT*)) +{ + DBUG_ENTER("savepoint_add"); + + SAVEPOINT **sv= find_savepoint_in_list(thd, name, list); + + SAVEPOINT *newsv; + + if (*sv) /* old savepoint of the same name exists */ + { + newsv= *sv; + if (release_old){ + int error= release_old(thd, *sv); + if (error) + DBUG_RETURN(NULL); + } + *sv= (*sv)->prev; + } + else if ((newsv= (SAVEPOINT *) alloc_root(&thd->transaction->mem_root, + savepoint_alloc_size)) == NULL) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + DBUG_RETURN(NULL); + } + + newsv->name= strmake_root(&thd->transaction->mem_root, name.str, name.length); + newsv->length= (uint)name.length; + DBUG_RETURN(newsv); +} /** Set a named transaction savepoint. @@ -581,7 +618,6 @@ find_savepoint(THD *thd, LEX_CSTRING name) bool trans_savepoint(THD *thd, LEX_CSTRING name) { - SAVEPOINT **sv, *newsv; DBUG_ENTER("trans_savepoint"); if (!(thd->in_multi_stmt_transaction_mode() || thd->in_sub_stmt) || @@ -591,23 +627,11 @@ bool trans_savepoint(THD *thd, LEX_CSTRING name) if (thd->transaction->xid_state.check_has_uncommitted_xa()) DBUG_RETURN(TRUE); - sv= find_savepoint(thd, name); + SAVEPOINT *newsv= savepoint_add(thd, name, &thd->transaction->savepoints, + ha_release_savepoint); - if (*sv) /* old savepoint of the same name exists */ - { - newsv= *sv; - ha_release_savepoint(thd, *sv); - *sv= (*sv)->prev; - } - else if ((newsv= (SAVEPOINT *) alloc_root(&thd->transaction->mem_root, - savepoint_alloc_size)) == NULL) - { - my_error(ER_OUT_OF_RESOURCES, MYF(0)); + if (newsv == NULL) DBUG_RETURN(TRUE); - } - - newsv->name= strmake_root(&thd->transaction->mem_root, name.str, name.length); - newsv->length= (uint)name.length; /* if we'll get an error here, don't add new savepoint to the list. @@ -617,6 +641,10 @@ bool trans_savepoint(THD *thd, LEX_CSTRING name) if (unlikely(ha_savepoint(thd, newsv))) DBUG_RETURN(TRUE); + int error= online_alter_savepoint_set(thd, name); + if (unlikely(error)) + DBUG_RETURN(error); + newsv->prev= thd->transaction->savepoints; thd->transaction->savepoints= newsv; @@ -676,6 +704,8 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_CSTRING name) ER_WARNING_NOT_COMPLETE_ROLLBACK, ER_THD(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK)); + res= res || online_alter_savepoint_rollback(thd, name); + thd->transaction->savepoints= sv; if (res) |