diff --git a/mysql-test/main/custom_aggregate_functions.result b/mysql-test/main/custom_aggregate_functions.result index 7f2cde1b6fe88..e1950f5880e25 100644 --- a/mysql-test/main/custom_aggregate_functions.result +++ b/mysql-test/main/custom_aggregate_functions.result @@ -1222,3 +1222,71 @@ ENDS CURRENT_TIMESTAMP + INTERVAL 1 MONTH + INTERVAL 1 WEEK DO FETCH GROUP NEXT ROW; ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context # End of 10.4 tests +# +# MDEV-38004 Double free on repeated execution of prepared aggregate +# function against empty table in Query_arena::free_items() +# +create aggregate function agg_cnt() returns int +begin +declare z int default 0; +declare continue handler for not found return z; +loop +fetch group next row; +set z= z+1; +end loop; +end| +create table t1 (id int); +prepare stmt from "select agg_cnt() from t1"; +execute stmt; +agg_cnt() +NULL +execute stmt; +agg_cnt() +NULL +insert into t1 values (1),(2),(2); +execute stmt; +agg_cnt() +3 +execute stmt; +agg_cnt() +3 +deallocate prepare stmt; +# +# Compare with +select agg_cnt() from t1; +agg_cnt() +3 +prepare stmt from "select id, agg_cnt() from t1 group by id with rollup"; +execute stmt; +id agg_cnt() +1 1 +2 2 +NULL 3 +execute stmt; +id agg_cnt() +1 1 +2 2 +NULL 3 +deallocate prepare stmt; +# +# The same item is reused between two CALLs of a stored procedure +# +delete from t1; +create procedure p() select agg_cnt() from t1; +call p(); +agg_cnt() +NULL +call p(); +agg_cnt() +NULL +insert into t1 values (1),(2),(2); +call p(); +agg_cnt() +3 +call p(); +agg_cnt() +3 +drop procedure p; +drop function agg_cnt; +drop table t1; +# End of 10.11 tests diff --git a/mysql-test/main/custom_aggregate_functions.test b/mysql-test/main/custom_aggregate_functions.test index a6097b3784791..7228f6d3c59ec 100644 --- a/mysql-test/main/custom_aggregate_functions.test +++ b/mysql-test/main/custom_aggregate_functions.test @@ -1062,3 +1062,55 @@ CREATE EVENT ev1 DO FETCH GROUP NEXT ROW; --echo # End of 10.4 tests + +--echo # +--echo # MDEV-38004 Double free on repeated execution of prepared aggregate +--echo # function against empty table in Query_arena::free_items() +--echo # + +--delimiter | +create aggregate function agg_cnt() returns int +begin +declare z int default 0; +declare continue handler for not found return z; +loop +fetch group next row; +set z= z+1; +end loop; +end| +--delimiter ; + +create table t1 (id int); +prepare stmt from "select agg_cnt() from t1"; +execute stmt; +execute stmt; +insert into t1 values (1),(2),(2); +execute stmt; +execute stmt; +deallocate prepare stmt; +--echo # +--echo # Compare with +select agg_cnt() from t1; + +prepare stmt from "select id, agg_cnt() from t1 group by id with rollup"; +execute stmt; +execute stmt; +deallocate prepare stmt; + +--echo # +--echo # The same item is reused between two CALLs of a stored procedure +--echo # +delete from t1; +create procedure p() select agg_cnt() from t1; +call p(); +call p(); +insert into t1 values (1),(2),(2); +call p(); +call p(); + +# Cleanup +drop procedure p; +drop function agg_cnt; +drop table t1; + +--echo # End of 10.11 tests diff --git a/sql/item.cc b/sql/item.cc index b3fbf6b72e876..ab3ee094a1ad7 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2874,13 +2874,29 @@ Item_sp::func_name_cstring(THD *thd, bool is_package_function) const return { qname.c_ptr_safe(), qname.length() }; } +/* + Free the context of a stored function call. + + note: The items on the call arena are allocated on sp_mem_root, so they + have to be destroyed before the memory root they live in is freed. +*/ + void -Item_sp::cleanup() +Item_sp::free_call_ctx() { - m_sp= NULL; delete func_ctx; func_ctx= NULL; + sp_query_arena->free_items(); free_root(&sp_mem_root, MYF(0)); + memset(&sp_mem_root, 0, sizeof(sp_mem_root)); +} + + +void +Item_sp::cleanup() +{ + m_sp= NULL; + free_call_ctx(); dummy_table->alias.free(); } @@ -3010,11 +3026,7 @@ Item_sp::execute_impl(THD *thd, Item **args, uint arg_count) if (err_status || func_ctx->quit_func) { /* Free Items allocated during function execution. */ - delete func_ctx; - func_ctx= NULL; - sp_query_arena->free_items(); - free_root(&sp_mem_root, MYF(0)); - memset(&sp_mem_root, 0, sizeof(sp_mem_root)); + free_call_ctx(); } thd->restore_sub_statement_state(&statement_state); diff --git a/sql/item.h b/sql/item.h index ed017c383ee09..e631d1632b554 100644 --- a/sql/item.h +++ b/sql/item.h @@ -5904,6 +5904,7 @@ class Item_sp } LEX_CSTRING func_name_cstring(THD *thd, bool is_package_function) const; void cleanup(); + void free_call_ctx(); bool sp_check_access(THD *thd); bool execute(THD *thd, bool *null_value, Item **args, uint arg_count); bool execute_impl(THD *thd, Item **args, uint arg_count); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 1c2789f86b737..0231870b91aa8 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1457,10 +1457,7 @@ Item_sum_sp::add() void Item_sum_sp::clear() { - delete func_ctx; - func_ctx= NULL; - sp_query_arena->free_items(); - free_root(&sp_mem_root, MYF(0)); + free_call_ctx(); } const Type_handler *Item_sum_sp::type_handler() const