From 7eff2080fdd5be9175e26479222b36e3ae475e09 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 20 Dec 2017 16:12:32 +0300 Subject: [PATCH] MDEV-14687 DELETE HISTORY in prepared stmt crash [fixes #421] Also fixes broken truncate after 617e108fb6e2bc24e5c9badb94e7d8eaa65d8851 --- mysql-test/suite/versioning/r/truncate.result | 22 ++++++++++---- mysql-test/suite/versioning/t/truncate.test | 21 +++++++++++-- sql/share/errmsg-utf8.txt | 4 +-- sql/sql_delete.cc | 30 ++++++++++--------- 4 files changed, 52 insertions(+), 25 deletions(-) diff --git a/mysql-test/suite/versioning/r/truncate.result b/mysql-test/suite/versioning/r/truncate.result index 7c368b99a15..238d7d97bad 100644 --- a/mysql-test/suite/versioning/r/truncate.result +++ b/mysql-test/suite/versioning/r/truncate.result @@ -28,15 +28,23 @@ select * from t for system_time all; a 11 22 -1 2 -delete history from t before system_time timestamp now(6); +prepare stmt from 'delete history from t before system_time timestamp now(6)'; +execute stmt; +drop prepare stmt; select * from t for system_time all; a 11 22 -1 -2 +delete from t; +create or replace procedure truncate_sp() +begin +delete history from t before system_time timestamp now(6); +end~~ +call truncate_sp; +select * from t for system_time all; +a +drop procedure truncate_sp; ### Issue #399, truncate partitioned table is now unimplemented create or replace table t (a int) with system versioning @@ -50,12 +58,14 @@ create or replace table t (i int) with system versioning; delete history from t before system_time now(); create or replace view v as select * from t; delete history from v before system_time now(); -ERROR HY000: TRUNCATE table_name TO doesn't work with VIEWs +ERROR HY000: DELETE HISTORY from VIEW is prohibited create or replace table t (i int); delete history from t before system_time now(); ERROR HY000: Table `t` is not system-versioned create or replace view v as select * from t; delete history from v before system_time now(); -ERROR HY000: TRUNCATE table_name TO doesn't work with VIEWs +ERROR HY000: DELETE HISTORY from VIEW is prohibited +prepare stmt from 'delete history from t before system_time now()'; +ERROR HY000: Table `t` is not system-versioned drop table t; drop view v; diff --git a/mysql-test/suite/versioning/t/truncate.test b/mysql-test/suite/versioning/t/truncate.test index 9623ee0d310..3b209364d7a 100644 --- a/mysql-test/suite/versioning/t/truncate.test +++ b/mysql-test/suite/versioning/t/truncate.test @@ -25,8 +25,21 @@ update t set a=22 where a=2; select * from t for system_time all; delete history from t before system_time timestamp @ts1; select * from t for system_time all; -delete history from t before system_time timestamp now(6); +prepare stmt from 'delete history from t before system_time timestamp now(6)'; +execute stmt; drop prepare stmt; select * from t for system_time all; +delete from t; + +delimiter ~~; +create or replace procedure truncate_sp() +begin + delete history from t before system_time timestamp now(6); +end~~ +delimiter ;~~ +call truncate_sp; +select * from t for system_time all; + +drop procedure truncate_sp; --echo ### Issue #399, truncate partitioned table is now unimplemented @@ -43,15 +56,17 @@ delete history from t before system_time current_timestamp; create or replace table t (i int) with system versioning; delete history from t before system_time now(); create or replace view v as select * from t; ---error ER_VERS_TRUNCATE_TO_VIEW +--error ER_VERS_TRUNCATE_VIEW delete history from v before system_time now(); create or replace table t (i int); --error ER_VERS_NOT_VERSIONED delete history from t before system_time now(); create or replace view v as select * from t; ---error ER_VERS_TRUNCATE_TO_VIEW +--error ER_VERS_TRUNCATE_VIEW delete history from v before system_time now(); +--error ER_VERS_NOT_VERSIONED +prepare stmt from 'delete history from t before system_time now()'; drop table t; drop view v; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index f76efb95185..3fe0d33dc03 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7930,5 +7930,5 @@ ER_VERS_ALREADY_VERSIONED WARN_VERS_TRT_EXPERIMENTAL eng "Transaction-based system versioning is EXPERIMENTAL and is subject to change in future." -ER_VERS_TRUNCATE_TO_VIEW - eng "TRUNCATE table_name TO doesn't work with VIEWs" +ER_VERS_TRUNCATE_VIEW + eng "DELETE HISTORY from VIEW is prohibited" diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index fcd5708863a..42ff093ce0a 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -223,25 +223,21 @@ bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, static bool record_should_be_deleted(THD *thd, TABLE *table, SQL_SELECT *sel, - Explain_delete *explain) + Explain_delete *explain, bool truncate_history) { + bool check_delete= true; + if (table->versioned()) { - bool row_is_alive= table->vers_end_field()->is_max(); - /* If we are doing TRUNCATE TABLE with SYSTEM_TIME condition then historical - record is deleted and current record is kept. Otherwise alive record is - deleted and historical record is kept. */ - if ((thd->lex->sql_command == SQLCOM_TRUNCATE && table->pos_in_table_list->vers_conditions) - ? row_is_alive - : !row_is_alive) - return false; + bool historical= !table->vers_end_field()->is_max(); + check_delete= truncate_history ? historical : !historical; } explain->tracker.on_record_read(); thd->inc_examined_row_count(1); if (table->vfield) (void) table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_DELETE); - if (!sel || sel->skip_record(thd) > 0) + if (check_delete && (!sel || sel->skip_record(thd) > 0)) { explain->tracker.on_record_after_where(); return true; @@ -314,7 +310,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, { if (table_list->is_view_or_derived()) { - my_error(ER_VERS_TRUNCATE_TO_VIEW, MYF(0)); + my_error(ER_VERS_TRUNCATE_VIEW, MYF(0)); DBUG_RETURN(true); } @@ -329,7 +325,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } #endif - DBUG_ASSERT(!conds); + DBUG_ASSERT(!conds || thd->stmt_arena->is_stmt_execute()); if (select_lex->vers_setup_conds(thd, table_list, &conds)) DBUG_RETURN(TRUE); @@ -708,7 +704,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, while (!(error=info.read_record()) && !thd->killed && ! thd->is_error()) { - if (record_should_be_deleted(thd, table, select, explain)) + if (record_should_be_deleted(thd, table, select, explain, truncate_history)) { table->file->position(table->record[0]); if ((error= deltempfile->unique_add((char*) table->file->ref))) @@ -735,7 +731,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ! thd->is_error()) { if (delete_while_scanning) - delete_record= record_should_be_deleted(thd, table, select, explain); + delete_record= record_should_be_deleted(thd, table, select, explain, + truncate_history); if (delete_record) { if (!truncate_history && table->triggers && @@ -945,6 +942,11 @@ l select_lex->leaf_tables, FALSE, DELETE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(TRUE); + if (table_list->vers_conditions && + select_lex->vers_setup_conds(thd, table_list, conds)) + { + DBUG_RETURN(TRUE); + } if ((wild_num && setup_wild(thd, table_list, field_list, NULL, wild_num)) || setup_fields(thd, Ref_ptr_array(), field_list, MARK_COLUMNS_READ, NULL, NULL, 0) ||