summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/suite/versioning/r/truncate_history.result115
-rw-r--r--mysql-test/suite/versioning/r/truncate_innodb_rpl.result7
-rw-r--r--mysql-test/suite/versioning/t/truncate_history.test94
-rw-r--r--mysql-test/suite/versioning/t/truncate_innodb_rpl.test10
-rw-r--r--sql/handler.cc2
-rw-r--r--sql/handler.h2
-rw-r--r--sql/share/errmsg-utf8.txt3
-rw-r--r--sql/sql_delete.cc58
-rw-r--r--sql/sql_select.cc4
-rw-r--r--sql/sql_select.h3
-rw-r--r--sql/sql_truncate.cc9
-rw-r--r--sql/sql_yacc.yy3
-rw-r--r--storage/innobase/handler/ha_innodb.cc6
-rw-r--r--storage/innobase/include/row0mysql.h3
-rw-r--r--storage/innobase/row/row0mysql.cc11
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