diff --git a/mysql-test/suite/versioning/r/delete_history.result b/mysql-test/suite/versioning/r/delete_history.result index 22a3fb7cb58..c52d6afa622 100644 --- a/mysql-test/suite/versioning/r/delete_history.result +++ b/mysql-test/suite/versioning/r/delete_history.result @@ -251,3 +251,216 @@ t CREATE TABLE `t` ( PARTITIONS 2 drop table t; # End of 10.9 tests +# +# MDEV-34046 Parameterized PS converts error to warning, causes replication problems +# +create table t (a int) with system versioning; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert into t values (1), (100); +delete history from t before system_time @@timestamp; +ERROR HY000: Illegal parameter data type double for operation 'FOR SYSTEM_TIME' +execute immediate "delete history from t before system_time @@timestamp"; +ERROR HY000: Illegal parameter data type double for operation 'FOR SYSTEM_TIME' +execute immediate "delete history from t before system_time ?" using @@timestamp; +ERROR HY000: Illegal parameter data type double for operation 'FOR SYSTEM_TIME' +set @ts1= '2000-01-01 00:00:01'; +set timestamp= unix_timestamp(@ts1); +update t set a= a + 1 where a < 100; +set @ts2= '2000-01-01 00:00:02'; +set timestamp= unix_timestamp(@ts2); +update t set a= a + 1 where a < 100; +set @ts3= '2000-01-01 00:00:03'; +set timestamp= unix_timestamp(@ts3); +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +1 2000-01-01 00:00:00.000000 2000-01-01 00:00:01.000000 +2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000 +3 2000-01-01 00:00:02.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute immediate "delete history from t before system_time @ts1"; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +1 2000-01-01 00:00:00.000000 2000-01-01 00:00:01.000000 +2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000 +3 2000-01-01 00:00:02.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute immediate "delete history from t before system_time @ts2"; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000 +3 2000-01-01 00:00:02.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute immediate "delete history from t before system_time ?" using @ts3; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +3 2000-01-01 00:00:02.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute immediate "delete history from t before system_time ?" using @ts3; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +3 2000-01-01 00:00:02.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +update t set a= a + 1 where a < 100; +set @ts4= '2000-01-01 00:00:04'; +set timestamp= unix_timestamp(@ts4); +update t set a= a + 1 where a < 100; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000 +4 2000-01-01 00:00:03.000000 2000-01-01 00:00:04.000000 +5 2000-01-01 00:00:04.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute immediate "delete history from t before system_time ?" using '2000-01-01 00:00:04'; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +4 2000-01-01 00:00:03.000000 2000-01-01 00:00:04.000000 +5 2000-01-01 00:00:04.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +set @ts5= '2000-01-01 00:00:05'; +set timestamp= unix_timestamp(@ts5); +update t set a= a + 1 where a < 100; +set @ts6= '2000-01-01 00:00:06'; +set timestamp= unix_timestamp(@ts6); +update t set a= a + 1 where a < 100; +set @ts7= '2000-01-01 00:00:07'; +set timestamp= unix_timestamp(@ts7); +update t set a= a + 1 where a < 100; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +4 2000-01-01 00:00:03.000000 2000-01-01 00:00:04.000000 +5 2000-01-01 00:00:04.000000 2000-01-01 00:00:05.000000 +6 2000-01-01 00:00:05.000000 2000-01-01 00:00:06.000000 +7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000 +8 2000-01-01 00:00:07.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +prepare stmt from 'delete history from t before system_time ?'; +execute stmt using @ts4; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +4 2000-01-01 00:00:03.000000 2000-01-01 00:00:04.000000 +5 2000-01-01 00:00:04.000000 2000-01-01 00:00:05.000000 +6 2000-01-01 00:00:05.000000 2000-01-01 00:00:06.000000 +7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000 +8 2000-01-01 00:00:07.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute stmt using @ts5; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +5 2000-01-01 00:00:04.000000 2000-01-01 00:00:05.000000 +6 2000-01-01 00:00:05.000000 2000-01-01 00:00:06.000000 +7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000 +8 2000-01-01 00:00:07.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute stmt using @ts6; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +6 2000-01-01 00:00:05.000000 2000-01-01 00:00:06.000000 +7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000 +8 2000-01-01 00:00:07.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute stmt using '2000-01-01 00:00:06'; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +6 2000-01-01 00:00:05.000000 2000-01-01 00:00:06.000000 +7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000 +8 2000-01-01 00:00:07.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute stmt using '2000-01-01 00:00:06.000001'; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000 +8 2000-01-01 00:00:07.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +set @ts8= '2000-01-01 00:00:08'; +set timestamp= unix_timestamp(@ts8); +delete from t; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000 +8 2000-01-01 00:00:07.000000 2000-01-01 00:00:08.000000 +100 2000-01-01 00:00:00.000000 2000-01-01 00:00:08.000000 +execute immediate "delete history from t before system_time from_unixtime(?)" using @@timestamp; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +8 2000-01-01 00:00:07.000000 2000-01-01 00:00:08.000000 +100 2000-01-01 00:00:00.000000 2000-01-01 00:00:08.000000 +execute stmt using '2020-01-01'; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +drop prepare stmt; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert into t values (1), (100); +set @ts1= '2000-01-01 00:00:01'; +set timestamp= unix_timestamp(@ts1); +update t set a= a + 1 where a < 100; +set timestamp= @@timestamp + 1; +set @ts2= @@timestamp; +update t set a= a + 1 where a < 100; +set timestamp= @@timestamp + 1; +update t set a= a + 1 where a < 100; +prepare stmt from 'delete history from t before system_time from_unixtime(? + ?)'; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +1 2000-01-01 00:00:00.000000 2000-01-01 00:00:01.000000 +2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000 +3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000 +4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute stmt using @ts1, 0; +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: '2000-01-01 00:00:01' +Warning 1292 Truncated incorrect DOUBLE value: '2000-01-01 00:00:01' +Warning 1292 Truncated incorrect DOUBLE value: '2000-01-01 00:00:01' +Warning 1292 Truncated incorrect DOUBLE value: '2000-01-01 00:00:01' +Warning 1292 Truncated incorrect DOUBLE value: '2000-01-01 00:00:01' +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +1 2000-01-01 00:00:00.000000 2000-01-01 00:00:01.000000 +2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000 +3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000 +4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute stmt using @ts2, 0; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000 +3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000 +4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute stmt using @@timestamp, NULL; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000 +3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000 +4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute stmt using NULL, NULL; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000 +3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000 +4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute stmt using @ts2, 1; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000 +4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +execute stmt using @ts2, @ts2; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999 +100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 +delete from t; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +4 2000-01-01 00:00:03.000000 2000-01-01 00:00:03.000000 +100 2000-01-01 00:00:00.000000 2000-01-01 00:00:03.000000 +execute stmt using @ts2, @ts2; +select *, row_start, row_end from t for system_time all order by a; +a row_start row_end +drop prepare stmt; +drop table t; +set timestamp= default; +# End of 10.11 tests diff --git a/mysql-test/suite/versioning/t/delete_history.test b/mysql-test/suite/versioning/t/delete_history.test index 880b16e1553..c34f31e5ce5 100644 --- a/mysql-test/suite/versioning/t/delete_history.test +++ b/mysql-test/suite/versioning/t/delete_history.test @@ -256,4 +256,102 @@ drop table t; --echo # End of 10.9 tests +--echo # +--echo # MDEV-34046 Parameterized PS converts error to warning, causes replication problems +--echo # +create table t (a int) with system versioning; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert into t values (1), (100); + +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +delete history from t before system_time @@timestamp; +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +execute immediate "delete history from t before system_time @@timestamp"; +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +execute immediate "delete history from t before system_time ?" using @@timestamp; + +set @ts1= '2000-01-01 00:00:01'; set timestamp= unix_timestamp(@ts1); +update t set a= a + 1 where a < 100; +set @ts2= '2000-01-01 00:00:02'; set timestamp= unix_timestamp(@ts2); +update t set a= a + 1 where a < 100; +set @ts3= '2000-01-01 00:00:03'; set timestamp= unix_timestamp(@ts3); +select *, row_start, row_end from t for system_time all order by a; +execute immediate "delete history from t before system_time @ts1"; +select *, row_start, row_end from t for system_time all order by a; +execute immediate "delete history from t before system_time @ts2"; +select *, row_start, row_end from t for system_time all order by a; +execute immediate "delete history from t before system_time ?" using @ts3; +select *, row_start, row_end from t for system_time all order by a; +execute immediate "delete history from t before system_time ?" using @ts3; +select *, row_start, row_end from t for system_time all order by a; +update t set a= a + 1 where a < 100; +set @ts4= '2000-01-01 00:00:04'; set timestamp= unix_timestamp(@ts4); +update t set a= a + 1 where a < 100; +select *, row_start, row_end from t for system_time all order by a; +execute immediate "delete history from t before system_time ?" using '2000-01-01 00:00:04'; +select *, row_start, row_end from t for system_time all order by a; +set @ts5= '2000-01-01 00:00:05'; set timestamp= unix_timestamp(@ts5); +update t set a= a + 1 where a < 100; +set @ts6= '2000-01-01 00:00:06'; set timestamp= unix_timestamp(@ts6); +update t set a= a + 1 where a < 100; +set @ts7= '2000-01-01 00:00:07'; set timestamp= unix_timestamp(@ts7); +update t set a= a + 1 where a < 100; +select *, row_start, row_end from t for system_time all order by a; +prepare stmt from 'delete history from t before system_time ?'; +execute stmt using @ts4; +select *, row_start, row_end from t for system_time all order by a; +execute stmt using @ts5; +select *, row_start, row_end from t for system_time all order by a; +execute stmt using @ts6; +select *, row_start, row_end from t for system_time all order by a; +execute stmt using '2000-01-01 00:00:06'; +select *, row_start, row_end from t for system_time all order by a; +execute stmt using '2000-01-01 00:00:06.000001'; +select *, row_start, row_end from t for system_time all order by a; +set @ts8= '2000-01-01 00:00:08'; set timestamp= unix_timestamp(@ts8); +delete from t; +select *, row_start, row_end from t for system_time all order by a; +execute immediate "delete history from t before system_time from_unixtime(?)" using @@timestamp; +select *, row_start, row_end from t for system_time all order by a; +execute stmt using '2020-01-01'; +select *, row_start, row_end from t for system_time all order by a; +drop prepare stmt; + +# Check expression +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert into t values (1), (100); +set @ts1= '2000-01-01 00:00:01'; set timestamp= unix_timestamp(@ts1); +update t set a= a + 1 where a < 100; +set timestamp= @@timestamp + 1; +set @ts2= @@timestamp; +update t set a= a + 1 where a < 100; +set timestamp= @@timestamp + 1; +update t set a= a + 1 where a < 100; + +prepare stmt from 'delete history from t before system_time from_unixtime(? + ?)'; +select *, row_start, row_end from t for system_time all order by a; +execute stmt using @ts1, 0; +select *, row_start, row_end from t for system_time all order by a; +execute stmt using @ts2, 0; +select *, row_start, row_end from t for system_time all order by a; +execute stmt using @@timestamp, NULL; +select *, row_start, row_end from t for system_time all order by a; +execute stmt using NULL, NULL; +select *, row_start, row_end from t for system_time all order by a; +execute stmt using @ts2, 1; +select *, row_start, row_end from t for system_time all order by a; +execute stmt using @ts2, @ts2; +select *, row_start, row_end from t for system_time all order by a; +delete from t; +select *, row_start, row_end from t for system_time all order by a; +execute stmt using @ts2, @ts2; +select *, row_start, row_end from t for system_time all order by a; + +drop prepare stmt; + +drop table t; +set timestamp= default; + +--echo # End of 10.11 tests + --source suite/versioning/common_finish.inc diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 59703b719b2..ec8ed6efff3 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -393,7 +393,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } if (delete_history) + { + DBUG_ASSERT(conds); table->vers_write= false; + } if (returning) (void) result->prepare(returning->item_list, NULL); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 87dabd1e222..7834b1902ea 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1279,10 +1279,25 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) } if (vers_conditions.type == SYSTEM_TIME_ALL) + { + if (vers_conditions.has_param) + { + /* + Parameter substitution (set_params_from_actual_params()) works + on existing items so we don't have to reevaluate table->where + (in update_this below), we just update SELECT_LEX WHERE expression + from existing table conditions. + */ + DBUG_ASSERT(vers_conditions.delete_history); + DBUG_ASSERT(thd->stmt_arena->is_stmt_execute()); + where= and_items(thd, where, table->where); + } continue; + } } bool timestamps_only= table->table->versioned(VERS_TIMESTAMP); + bool update_this= update_conds; if (vers_conditions.is_set() && vers_conditions.type != SYSTEM_TIME_HISTORY) { @@ -1299,9 +1314,23 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), table->table_name.str); DBUG_RETURN(-1); } + if (vers_conditions.has_param) + { + /* + PS parameter in history expression requires processing at execution + stage when parameters has values substituted. So at prepare continue + the loop, but at execution enter update_this. The second execution + is skipped on vers_conditions.type == SYSTEM_TIME_ALL condition. + */ + DBUG_ASSERT(vers_conditions.delete_history); + if (thd->stmt_arena->is_stmt_prepare()) + continue; + DBUG_ASSERT(thd->stmt_arena->is_stmt_execute()); + update_this= true; + } } - if (update_conds) + if (update_this) { vers_conditions.period = &table->table->s->vers; Item *cond= period_get_condition(thd, table, this, &vers_conditions, diff --git a/sql/table.cc b/sql/table.cc index 81f734269ea..99fa6bb005e 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -10519,8 +10519,8 @@ bool vers_select_conds_t::check_units(THD *thd) { DBUG_ASSERT(type != SYSTEM_TIME_UNSPECIFIED); DBUG_ASSERT(start.item); - return start.check_unit(thd) || - end.check_unit(thd); + return start.check_unit(thd, this) || + end.check_unit(thd, this); } bool vers_select_conds_t::eq(const vers_select_conds_t &conds) const @@ -10546,7 +10546,7 @@ bool vers_select_conds_t::eq(const vers_select_conds_t &conds) const } -bool Vers_history_point::check_unit(THD *thd) +bool Vers_history_point::check_unit(THD *thd, vers_select_conds_t *vers_conds) { if (!item) return false; @@ -10556,6 +10556,9 @@ bool Vers_history_point::check_unit(THD *thd) item->full_name(), "FOR SYSTEM_TIME"); return true; } + else if (item->with_param()) + vers_conds->has_param= true; + if (item->fix_fields_if_needed(thd, &item)) return true; const Type_handler *t= item->this_item()->real_type_handler(); diff --git a/sql/table.h b/sql/table.h index 297ce8d5c9a..753548f5713 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2140,6 +2140,8 @@ struct vers_history_point_t Item *item; }; +struct vers_select_conds_t; + class Vers_history_point : public vers_history_point_t { void fix_item(); @@ -2160,7 +2162,8 @@ public: } void empty() { unit= VERS_TIMESTAMP; item= NULL; } void print(String *str, enum_query_type, const char *prefix, size_t plen) const; - bool check_unit(THD *thd); + bool check_unit(THD *thd, vers_select_conds_t *vers_conds); + bool has_param() const; bool eq(const vers_history_point_t &point) const; }; @@ -2170,6 +2173,7 @@ struct vers_select_conds_t vers_system_time_t orig_type; bool used:1; bool delete_history:1; + bool has_param:1; Vers_history_point start; Vers_history_point end; Lex_ident name; @@ -2185,6 +2189,7 @@ struct vers_select_conds_t orig_type= SYSTEM_TIME_UNSPECIFIED; used= false; delete_history= false; + has_param= false; start.empty(); end.empty(); } @@ -2199,6 +2204,7 @@ struct vers_select_conds_t used= false; delete_history= (type == SYSTEM_TIME_HISTORY || type == SYSTEM_TIME_BEFORE); + has_param= false; start= _start; end= _end; name= _name;