diff options
-rw-r--r-- | mysql-test/suite/versioning/r/truncate_history.result | 115 | ||||
-rw-r--r-- | mysql-test/suite/versioning/r/truncate_innodb_rpl.result | 7 | ||||
-rw-r--r-- | mysql-test/suite/versioning/t/truncate_history.test | 94 | ||||
-rw-r--r-- | mysql-test/suite/versioning/t/truncate_innodb_rpl.test | 10 | ||||
-rw-r--r-- | sql/handler.cc | 2 | ||||
-rw-r--r-- | sql/handler.h | 2 | ||||
-rw-r--r-- | sql/share/errmsg-utf8.txt | 3 | ||||
-rw-r--r-- | sql/sql_delete.cc | 58 | ||||
-rw-r--r-- | sql/sql_select.cc | 4 | ||||
-rw-r--r-- | sql/sql_select.h | 3 | ||||
-rw-r--r-- | sql/sql_truncate.cc | 9 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 3 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 6 | ||||
-rw-r--r-- | storage/innobase/include/row0mysql.h | 3 | ||||
-rw-r--r-- | storage/innobase/row/row0mysql.cc | 11 |
15 files changed, 301 insertions, 29 deletions
diff --git a/mysql-test/suite/versioning/r/truncate_history.result b/mysql-test/suite/versioning/r/truncate_history.result new file mode 100644 index 00000000000..d71354bcc36 --- /dev/null +++ b/mysql-test/suite/versioning/r/truncate_history.result @@ -0,0 +1,115 @@ +create table t (a int); +truncate t for system_time all; +ERROR HY000: System Versioning required: `FOR SYSTEM_TIME` query +create procedure truncate_history_of_t() +begin +prepare stmt from 'truncate t for system_time timestamp between \'1-1-1\' and now(6)'; +execute stmt; +drop prepare stmt; +end~~ +create or replace table t (a int) with system versioning; +insert into t values (1); +update t set a=2; +select * from t for system_time all; +a +2 +1 +set @test = 'correct'; +create trigger trg_before before delete on t for each row set @test = 'incorrect'; +create trigger trg_after after delete on t for each row set @test = 'incorrect'; +truncate t for system_time all; +select * from t for system_time all; +a +2 +select @test from t; +@test +correct +drop trigger trg_before; +drop trigger trg_after; +update t set a=3; +update t set a=4; +truncate t for system_time as of timestamp now(6); +select * from t for system_time all; +a +4 +2 +3 +truncate t for system_time timestamp between '1-1-1' and now(6); +select * from t for system_time all; +a +4 +update t set a=5; +truncate t for system_time timestamp from '1-1-1' to now(6); +select * from t for system_time all; +a +5 +update t set a=6; +call truncate_history_of_t(); +select * from t for system_time all; +a +6 +set @ts1 = now(6); +update t set a=7; +set @ts2 = now(6); +update t set a=8; +truncate t for system_time timestamp from '1-1-1' to @ts1; +select * from t for system_time all; +a +8 +7 +update t set a=9; +truncate t for system_time timestamp between '1-1-1' and @ts2; +select * from t for system_time all; +a +9 +8 +create or replace table t (a int) with system versioning engine=innodb; +insert into t values (1); +update t set a=2; +select * from t for system_time all; +a +2 +1 +truncate t for system_time all; +select * from t for system_time all; +a +2 +update t set a=3; +update t set a=4; +truncate t for system_time as of timestamp now(6); +select * from t for system_time all; +a +4 +2 +3 +truncate t for system_time timestamp between '1-1-1' and now(6); +select * from t for system_time all; +a +4 +update t set a=5; +truncate t for system_time timestamp from '1-1-1' to now(6); +select * from t for system_time all; +a +5 +update t set a=6; +call truncate_history_of_t(); +select * from t for system_time all; +a +6 +set @ts1 = now(6); +update t set a=7; +set @ts2 = now(6); +update t set a=8; +truncate t for system_time timestamp from '1-1-1' to @ts1; +select * from t for system_time all; +a +8 +7 +update t set a=9; +truncate t for system_time timestamp between '1-1-1' and @ts2; +select * from t for system_time all; +a +9 +8 +drop table t; +drop procedure truncate_history_of_t; diff --git a/mysql-test/suite/versioning/r/truncate_innodb_rpl.result b/mysql-test/suite/versioning/r/truncate_innodb_rpl.result new file mode 100644 index 00000000000..1aa29d7b95f --- /dev/null +++ b/mysql-test/suite/versioning/r/truncate_innodb_rpl.result @@ -0,0 +1,7 @@ +include/master-slave.inc +[connection master] +create table t (a int) with system versioning engine=innodb; +truncate t for system_time all; +ERROR HY000: `TRUNCATE FOR SYSTEM_TIME with row-based replication` is not allowed for versioned table +drop table t; +include/rpl_end.inc diff --git a/mysql-test/suite/versioning/t/truncate_history.test b/mysql-test/suite/versioning/t/truncate_history.test new file mode 100644 index 00000000000..42f60f51d46 --- /dev/null +++ b/mysql-test/suite/versioning/t/truncate_history.test @@ -0,0 +1,94 @@ +-- source include/have_innodb.inc + +create table t (a int); +--error ER_VERSIONING_REQUIRED +truncate t for system_time all; + +delimiter ~~; +create procedure truncate_history_of_t() +begin + prepare stmt from 'truncate t for system_time timestamp between \'1-1-1\' and now(6)'; + execute stmt; + drop prepare stmt; +end~~ +delimiter ;~~ + +create or replace table t (a int) with system versioning; +insert into t values (1); +update t set a=2; +select * from t for system_time all; + +set @test = 'correct'; +create trigger trg_before before delete on t for each row set @test = 'incorrect'; +create trigger trg_after after delete on t for each row set @test = 'incorrect'; + +truncate t for system_time all; +select * from t for system_time all; + +select @test from t; +drop trigger trg_before; +drop trigger trg_after; + +update t set a=3; +update t set a=4; +truncate t for system_time as of timestamp now(6); +select * from t for system_time all; + +truncate t for system_time timestamp between '1-1-1' and now(6); +select * from t for system_time all; + +update t set a=5; +truncate t for system_time timestamp from '1-1-1' to now(6); +select * from t for system_time all; + +update t set a=6; +call truncate_history_of_t(); +select * from t for system_time all; + +set @ts1 = now(6); +update t set a=7; +set @ts2 = now(6); +update t set a=8; +truncate t for system_time timestamp from '1-1-1' to @ts1; +select * from t for system_time all; +update t set a=9; +truncate t for system_time timestamp between '1-1-1' and @ts2; +select * from t for system_time all; + + +create or replace table t (a int) with system versioning engine=innodb; +insert into t values (1); +update t set a=2; +select * from t for system_time all; + +truncate t for system_time all; +select * from t for system_time all; + +update t set a=3; +update t set a=4; +truncate t for system_time as of timestamp now(6); +select * from t for system_time all; + +truncate t for system_time timestamp between '1-1-1' and now(6); +select * from t for system_time all; + +update t set a=5; +truncate t for system_time timestamp from '1-1-1' to now(6); +select * from t for system_time all; + +update t set a=6; +call truncate_history_of_t(); +select * from t for system_time all; + +set @ts1 = now(6); +update t set a=7; +set @ts2 = now(6); +update t set a=8; +truncate t for system_time timestamp from '1-1-1' to @ts1; +select * from t for system_time all; +update t set a=9; +truncate t for system_time timestamp between '1-1-1' and @ts2; +select * from t for system_time all; + +drop table t; +drop procedure truncate_history_of_t; diff --git a/mysql-test/suite/versioning/t/truncate_innodb_rpl.test b/mysql-test/suite/versioning/t/truncate_innodb_rpl.test new file mode 100644 index 00000000000..57fad82f3ba --- /dev/null +++ b/mysql-test/suite/versioning/t/truncate_innodb_rpl.test @@ -0,0 +1,10 @@ +-- source include/have_binlog_format_row.inc +-- source include/master-slave.inc +-- source include/have_innodb.inc + +create table t (a int) with system versioning engine=innodb; +--error ER_VERS_NOT_ALLOWED +truncate t for system_time all; +drop table t; + +-- source include/rpl_end.inc diff --git a/sql/handler.cc b/sql/handler.cc index 0712d3e5798..98f558f50cc 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -5659,7 +5659,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat) 1 Row needs to be logged */ -inline bool handler::check_table_binlog_row_based(bool binlog_row) +bool handler::check_table_binlog_row_based(bool binlog_row) { if (unlikely((table->in_use->variables.sql_log_bin_off))) return 0; /* Called by partitioning engine */ diff --git a/sql/handler.h b/sql/handler.h index 5bcacf1f50b..56ee1addd6d 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -4079,7 +4079,7 @@ protected: virtual int delete_table(const char *name); public: - inline bool check_table_binlog_row_based(bool binlog_row); + bool check_table_binlog_row_based(bool binlog_row); private: /* Cache result to avoid extra calls */ inline void mark_trx_read_write() diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index c25b8b655cd..daca9b4ce52 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7522,3 +7522,6 @@ WARN_VERS_PARAMETERS WARN_VERS_PART_ROTATION eng "Switching from partition %`s to %`s" + +ER_VERS_NOT_ALLOWED + eng "%`s is not allowed for versioned table" diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 775d0c0d023..5d62b54b297 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -215,19 +215,13 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, inline int TABLE::delete_row() { - int error; - if (!versioned_by_sql()) - error= file->ha_delete_row(record[0]); - else - { - store_record(this, record[1]); - Field *sys_trx_end= vers_end_field(); - sys_trx_end->set_time(); - error= file->ha_update_row(record[1], record[0]); - } - return error; -} + if (!versioned_by_sql() || !vers_end_field()->is_max()) + return file->ha_delete_row(record[0]); + store_record(this, record[1]); + vers_end_field()->set_time(); + return file->ha_update_row(record[1], record[0]); +} /** Implement DELETE SQL word. @@ -269,6 +263,34 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (open_and_lock_tables(thd, table_list, TRUE, 0)) DBUG_RETURN(TRUE); + bool truncate_history= + select_lex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED; + if (truncate_history) + { + TABLE *table= table_list->table; + DBUG_ASSERT(table); + + if (table->versioned_by_engine() && + table->file->check_table_binlog_row_based(1)) + { + my_error(ER_VERS_NOT_ALLOWED, MYF(0), + "TRUNCATE FOR SYSTEM_TIME with row-based replication"); + DBUG_RETURN(TRUE); + } + + DBUG_ASSERT(!conds); + if (vers_setup_select(thd, table_list, &conds, select_lex)) + DBUG_RETURN(TRUE); + + // trx_sees() in InnoDB reads sys_trx_start + if (!table->versioned_by_sql() && + (select_lex->vers_conditions.type == FOR_SYSTEM_TIME_BETWEEN || + select_lex->vers_conditions.type == FOR_SYSTEM_TIME_FROM_TO)) + { + bitmap_set_bit(table->read_set, table->vers_start_field()->field_index); + } + } + if (mysql_handle_list_of_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT)) DBUG_RETURN(TRUE); if (mysql_handle_list_of_derived(thd->lex, table_list, DT_PREPARE)) @@ -577,9 +599,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, while (!(error=info.read_record(&info)) && !thd->killed && ! thd->is_error()) { - if (table->versioned() && !table->vers_end_field()->is_max()) + if (table->versioned()) { - continue; + bool row_is_alive= table->vers_end_field()->is_max(); + if (truncate_history && row_is_alive) + continue; + if (!truncate_history && !row_is_alive) + continue; } explain->tracker.on_record_read(); @@ -589,7 +615,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (!select || select->skip_record(thd) > 0) { explain->tracker.on_record_after_where(); - if (table->triggers && + if (!truncate_history && table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE)) { @@ -607,7 +633,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (!error) { deleted++; - if (table->triggers && + if (!truncate_history && table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_AFTER, FALSE)) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4dc67267b74..6655f71e10a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -667,8 +667,8 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array, DBUG_RETURN(res); } -static int -vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *slex) +int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, + SELECT_LEX *slex) { DBUG_ENTER("vers_setup_select"); #define newx new (thd->mem_root) diff --git a/sql/sql_select.h b/sql/sql_select.h index 77cf73d785f..aa238c9b741 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -2301,4 +2301,7 @@ int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort); JOIN_TAB *first_explain_order_tab(JOIN* join); JOIN_TAB *next_explain_order_tab(JOIN* join, JOIN_TAB* tab); +int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, + SELECT_LEX *slex); + #endif /* SQL_SELECT_INCLUDED */ diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index daa295d768e..4faad5b4711 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -26,7 +26,8 @@ #include "sql_truncate.h" #include "wsrep_mysqld.h" #include "sql_show.h" //append_identifier() - +#include "sql_select.h" +#include "sql_delete.h" /** Append a list of field names to a string. @@ -480,7 +481,6 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) DBUG_RETURN(error); } - /** Execute a TRUNCATE statement at runtime. @@ -495,6 +495,11 @@ bool Sql_cmd_truncate_table::execute(THD *thd) TABLE_LIST *first_table= thd->lex->select_lex.table_list.first; DBUG_ENTER("Sql_cmd_truncate_table::execute"); + bool truncate_history= thd->lex->current_select->vers_conditions.type != + FOR_SYSTEM_TIME_UNSPECIFIED; + if (truncate_history) + DBUG_RETURN(mysql_delete(thd, first_table, NULL, NULL, -1, 0, NULL)); + if (check_one_table_access(thd, DROP_ACL, first_table)) DBUG_RETURN(res); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f591566d91a..4c781a6afd1 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -12998,8 +12998,9 @@ truncate: lex->select_lex.init_order(); YYPS->m_lock_type= TL_WRITE; YYPS->m_mdl_type= MDL_EXCLUSIVE; + Select->vers_conditions.empty(); } - table_name opt_lock_wait_timeout + table_name opt_for_system_time_clause opt_lock_wait_timeout { LEX* lex= thd->lex; DBUG_ASSERT(!lex->m_sql_cmd); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 4ad15369ac5..60e34103d76 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -9644,7 +9644,11 @@ ha_innobase::delete_row( innobase_srv_conc_enter_innodb(m_prebuilt); - error = row_update_for_mysql((byte*) record, m_prebuilt); + bool delete_history_row = + table->versioned() && !table->vers_end_field()->is_max(); + + error = row_update_for_mysql( + (byte *)record, m_prebuilt, delete_history_row); innobase_srv_conc_exit_innodb(m_prebuilt); diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index 6fa0a926e3f..922236283ea 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -280,7 +280,8 @@ row_table_got_default_clust_index( dberr_t row_update_for_mysql( const byte* mysql_rec, - row_prebuilt_t* prebuilt) + row_prebuilt_t* prebuilt, + bool delete_history_row = false) MY_ATTRIBUTE((warn_unused_result)); /** This can only be used when srv_locks_unsafe_for_binlog is TRUE or this diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 14a393830af..fceaf65c407 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -1859,7 +1859,8 @@ static dberr_t row_update_for_mysql_using_upd_graph( const byte* mysql_rec, - row_prebuilt_t* prebuilt) + row_prebuilt_t* prebuilt, + bool delete_history_row) { trx_savept_t savept; dberr_t err; @@ -1993,7 +1994,7 @@ row_update_for_mysql_using_upd_graph( run_again: if (DICT_TF2_FLAG_IS_SET(node->table, DICT_TF2_VERSIONED) && - (node->is_delete || node->versioned)) + (node->is_delete || node->versioned) && !delete_history_row) { /* System Versioning: modify update vector to set sys_trx_start (or sys_trx_end in case of DELETE) @@ -2257,10 +2258,12 @@ error: dberr_t row_update_for_mysql( const byte* mysql_rec, - row_prebuilt_t* prebuilt) + row_prebuilt_t* prebuilt, + bool delete_history_row) { ut_a(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW); - return(row_update_for_mysql_using_upd_graph(mysql_rec, prebuilt)); + return (row_update_for_mysql_using_upd_graph( + mysql_rec, prebuilt, delete_history_row)); } /** This can only be used when srv_locks_unsafe_for_binlog is TRUE or this |