Skip to content

Commit 9c6fe16

Browse files
roylysengdahlerlend
authored andcommitted
Bug#36980896: Defining local variables within a TRIGGER/PROCEDURE
causes service to shut down There are two problems here: With a production binary, we see failures in check_column_privileges() or failing to obtain a virtual function for an Item_func_eq object. With a debug binary, execution fails earlier in an assert: Query_expression::set_prepared(), Assertion `!is_prepared()' failed. Due to the assert failing in debug mode, it is difficult to get to the actual source of the problem, but we can narrow the debug issue down to establishing a proper execution context for an independent item to be evaluated in function sp_prepare_func_item(). The assumption for this function is that the current LEX and query expression found through thd->lex can be set to "prepared" and "executing" state. However, sp_prepare_func_item() can be used for two kinds of items. Some of these are standalone and have an associated LEX object and a query expression object. Examples of these are the procedure instructions sp_instr_set and sp_instr_jump_if_not. Others are expressions that are created below a LEX that is already in an executing state. Examples are items used to assign values to procedure arguments after procedure execution, and assignment to local variables after query execution. The latter is the problem in this case. The solution to the first problem is to distinguish the two cases and pass a bool argument for the standalone items and false for the remaining ones. In sp_prepare_func_item(), we then ensure that preparation and execution state is set properly for the standalone items, whereas we ensure by inspecting the current LEX object that we are already in an executing context for the dependent items. The second problem is related to the first one. It started appearing after the fix for bug#35856247 was pushed, which changes the way base_ref_items are saved in Query_block::save_properties(). But the solution also fixes this problem, since we no longer save properties for dependent items, which will interfere with the already saved properties for the current query block. We also had to set proper execution state in Sql_cmd_get_diagnostics::execute() and Sql_cmd_load_table::execute() because otherwise these functions would fail the newly added asserts. Change-Id: Ib5d31c07994340006818e180eed74e1d6aa23280
1 parent 38542e9 commit 9c6fe16

File tree

11 files changed

+109
-80
lines changed

11 files changed

+109
-80
lines changed

sql/item.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1965,7 +1965,7 @@ void Item_splocal::print(const THD *thd, String *str, enum_query_type) const {
19651965
}
19661966

19671967
bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it) {
1968-
return ctx->set_variable(thd, get_var_idx(), it);
1968+
return ctx->set_variable(thd, false, get_var_idx(), it);
19691969
}
19701970

19711971
/*****************************************************************************
@@ -9297,8 +9297,8 @@ bool Item_trigger_field::eq(const Item *item) const {
92979297
down_cast<const Item_trigger_field *>(item)->field_name);
92989298
}
92999299

9300-
bool Item_trigger_field::set_value(THD *thd, sp_rcontext * /*ctx*/, Item **it) {
9301-
Item *item = sp_prepare_func_item(thd, it);
9300+
bool Item_trigger_field::set_value(THD *thd, sp_rcontext *, Item **it) {
9301+
Item *item = sp_prepare_func_item(thd, true, it);
93029302
if (item == nullptr) return true;
93039303

93049304
if (!fixed) {

sql/query_result.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,8 @@ bool Query_dumpvar::send_data(THD *thd, const mem_root_deque<Item *> &items) {
724724
while ((mv = var_li++) && it != VisibleFields(items).end()) {
725725
Item *item = *it++;
726726
if (mv->is_local()) {
727-
if (thd->sp_runtime_ctx->set_variable(thd, mv->get_offset(), &item))
727+
if (thd->sp_runtime_ctx->set_variable(thd, false, mv->get_offset(),
728+
&item))
728729
return true;
729730
} else {
730731
Item_func_set_user_var *suv = new Item_func_set_user_var(mv->name, item);

sql/server_component/mysql_stored_program_imp.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,14 +331,14 @@ auto static set_variable(stored_program_runtime_context sp_runtime_context,
331331
if (index < 0) return MYSQL_FAILURE;
332332
auto runtime_context = reinterpret_cast<sp_rcontext *>(sp_runtime_context);
333333
if (runtime_context == nullptr) runtime_context = current_thd->sp_runtime_ctx;
334-
return runtime_context->set_variable(current_thd, index, &item);
334+
return runtime_context->set_variable(current_thd, false, index, &item);
335335
}
336336

337337
auto static set_return_value(stored_program_runtime_context sp_runtime_context,
338338
Item *item) -> int {
339339
auto runtime_context = reinterpret_cast<sp_rcontext *>(sp_runtime_context);
340340
if (runtime_context == nullptr) runtime_context = current_thd->sp_runtime_ctx;
341-
return runtime_context->set_return_value(current_thd, &item);
341+
return runtime_context->set_return_value(current_thd, false, &item);
342342
}
343343

344344
auto static get_item(stored_program_runtime_context sp_runtime_context,

sql/sp.cc

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2470,19 +2470,28 @@ bool sp_check_name(LEX_STRING *ident) {
24702470
/**
24712471
Prepare an Item for evaluation (call of fix_fields).
24722472
2473-
@param thd thread handler
2474-
@param it_addr pointer on item reference
2475-
2476-
@retval
2477-
NULL error
2478-
@retval
2479-
non-NULL prepared item
2473+
@param thd thread handler
2474+
@param standalone if true, thd->lex is directly associated with item.
2475+
Preparation and execution state will be changed and
2476+
preparation data will be saved for later executions.
2477+
If false, item is part of a larger query expression and
2478+
no such state transitions should take place.
2479+
It also means that such items cannot save preparation data.
2480+
@param it_addr pointer to item reference
2481+
2482+
@returns pointer to prepared Item on success, NULL on error.
24802483
*/
2481-
Item *sp_prepare_func_item(THD *thd, Item **it_addr) {
2484+
Item *sp_prepare_func_item(THD *thd, bool standalone, Item **it_addr) {
2485+
LEX *const lex = standalone ? thd->lex : nullptr;
2486+
2487+
// If item is part of larger query expression, it must be in executing state:
2488+
assert(lex != nullptr ||
2489+
(thd->lex->unit->is_prepared() && thd->lex->is_exec_started()));
2490+
24822491
it_addr = (*it_addr)->this_item_addr(thd, it_addr);
24832492

24842493
if ((*it_addr)->fixed) {
2485-
thd->lex->set_exec_started();
2494+
if (lex != nullptr) lex->set_exec_started();
24862495
return *it_addr;
24872496
}
24882497

@@ -2493,37 +2502,40 @@ Item *sp_prepare_func_item(THD *thd, Item **it_addr) {
24932502
DBUG_PRINT("info", ("fix_fields() failed"));
24942503
return nullptr;
24952504
}
2496-
thd->lex->unit->set_prepared();
2497-
thd->lex->save_cmd_properties(thd);
2498-
thd->lex->set_exec_started();
2499-
2505+
// If item is a separate query expression, set it's state to executing
2506+
if (lex != nullptr) {
2507+
lex->unit->set_prepared();
2508+
lex->save_cmd_properties(thd);
2509+
lex->set_exec_started();
2510+
}
25002511
return *it_addr;
25012512
}
25022513

25032514
/**
25042515
Evaluate an expression and store the result in the field.
25052516
2506-
@param thd current thread object
2507-
@param result_field the field to store the result
2508-
@param expr_item_ptr the root item of the expression
2517+
@param thd current thread object
2518+
@param standalone if true, thd->lex contains preparation and execution
2519+
state of item, otherwise item is part of
2520+
a query expression that is already in executing state.
2521+
@param result_field the field to store the result
2522+
@param expr_item_ptr the root item of the expression
25092523
2510-
@retval
2511-
false on success
2512-
@retval
2513-
true on error
2524+
@returns false on success, true on error
25142525
*/
2515-
bool sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) {
2516-
Item *expr_item;
2526+
bool sp_eval_expr(THD *thd, bool standalone, Field *result_field,
2527+
Item **expr_item_ptr) {
25172528
Strict_error_handler strict_handler(
25182529
Strict_error_handler::ENABLE_SET_SELECT_STRICT_ERROR_HANDLER);
25192530
const enum_check_fields save_check_for_truncated_fields =
25202531
thd->check_for_truncated_fields;
25212532
const unsigned int stmt_unsafe_rollback_flags =
25222533
thd->get_transaction()->get_unsafe_rollback_flags(Transaction_ctx::STMT);
25232534

2524-
if (!*expr_item_ptr) goto error;
2535+
assert(*expr_item_ptr != nullptr);
25252536

2526-
if (!(expr_item = sp_prepare_func_item(thd, expr_item_ptr))) goto error;
2537+
Item *expr_item = sp_prepare_func_item(thd, standalone, expr_item_ptr);
2538+
if (expr_item == nullptr) goto error;
25272539

25282540
/*
25292541
Set THD flags to emit warnings/errors in case of overflow/type errors

sql/sp.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,10 @@ bool sp_check_name(LEX_STRING *ident);
407407
Table_ref *sp_add_to_query_tables(THD *thd, LEX *lex, const char *db,
408408
const char *name);
409409

410-
Item *sp_prepare_func_item(THD *thd, Item **it_addr);
410+
Item *sp_prepare_func_item(THD *thd, bool standalone, Item **it_addr);
411411

412-
bool sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr);
412+
bool sp_eval_expr(THD *thd, bool standalone, Field *result_field,
413+
Item **expr_item_ptr);
413414

414415
String *sp_get_item_value(THD *thd, Item *item, String *str);
415416

sql/sp_head.cc

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2799,8 +2799,8 @@ bool sp_head::execute_function(THD *thd, Item **argp, uint argcount,
27992799
/* Arguments must be fixed in Item_func_sp::fix_fields */
28002800
assert(argp[arg_no]->fixed);
28012801

2802-
err_status = func_runtime_ctx->set_variable(thd, arg_no, &(argp[arg_no]));
2803-
2802+
err_status =
2803+
func_runtime_ctx->set_variable(thd, false, arg_no, &(argp[arg_no]));
28042804
if (err_status) goto err_with_cleanup;
28052805
}
28062806

@@ -2983,11 +2983,12 @@ bool sp_head::execute_procedure(THD *thd, mem_root_deque<Item *> *args) {
29832983
sp_rcontext *proc_runtime_ctx =
29842984
sp_rcontext::create(thd, m_root_parsing_ctx, nullptr);
29852985

2986-
if (!proc_runtime_ctx) {
2986+
if (proc_runtime_ctx == nullptr) {
29872987
thd->sp_runtime_ctx = sp_runtime_ctx_saved;
29882988

2989-
if (sp_runtime_ctx_saved != nullptr) ::destroy_at(parent_sp_runtime_ctx);
2990-
2989+
if (sp_runtime_ctx_saved != nullptr) {
2990+
::destroy_at(parent_sp_runtime_ctx);
2991+
}
29912992
return true;
29922993
}
29932994

@@ -3000,33 +3001,33 @@ bool sp_head::execute_procedure(THD *thd, mem_root_deque<Item *> *args) {
30003001

30013002
for (uint i = 0; i < params; ++i, ++it_args) {
30023003
Item *arg_item = *it_args;
3003-
if (!arg_item) break;
3004+
if (arg_item == nullptr) break;
30043005

30053006
sp_variable *spvar = m_root_parsing_ctx->find_variable(i);
3006-
3007-
if (!spvar) continue;
3007+
if (spvar == nullptr) continue;
30083008

30093009
if (spvar->mode != sp_variable::MODE_IN) {
30103010
Settable_routine_parameter *srp =
30113011
arg_item->get_settable_routine_parameter();
3012-
3013-
if (!srp) {
3012+
if (srp == nullptr) {
30143013
my_error(ER_SP_NOT_VAR_ARG, MYF(0), i + 1, m_qname.str);
30153014
err_status = true;
30163015
break;
30173016
}
30183017
}
30193018

30203019
if (spvar->mode == sp_variable::MODE_OUT) {
3021-
Item_null *null_item = new Item_null();
3022-
3023-
if (!null_item ||
3024-
proc_runtime_ctx->set_variable(thd, i, (Item **)&null_item)) {
3020+
Item *null_item = new Item_null();
3021+
if (null_item == nullptr) {
3022+
err_status = true;
3023+
break;
3024+
}
3025+
if (proc_runtime_ctx->set_variable(thd, false, i, &null_item)) {
30253026
err_status = true;
30263027
break;
30273028
}
30283029
} else {
3029-
if (proc_runtime_ctx->set_variable(thd, i, &*it_args)) {
3030+
if (proc_runtime_ctx->set_variable(thd, false, i, &*it_args)) {
30303031
err_status = true;
30313032
break;
30323033
}

sql/sp_instr.cc

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,12 +1123,13 @@ PSI_statement_info sp_instr_set::psi_info = {
11231123
bool sp_instr_set::exec_core(THD *thd, uint *nextp) {
11241124
*nextp = get_ip() + 1;
11251125

1126-
if (!thd->sp_runtime_ctx->set_variable(thd, m_offset, &m_value_item))
1126+
// LEX of instruction keeps execution state of the assignment operation
1127+
if (!thd->sp_runtime_ctx->set_variable(thd, true, m_offset, &m_value_item))
11271128
return false;
11281129

11291130
/* Failed to evaluate the value. Reset the variable to NULL. */
11301131

1131-
if (thd->sp_runtime_ctx->set_variable(thd, m_offset, nullptr)) {
1132+
if (thd->sp_runtime_ctx->set_variable(thd, true, m_offset, nullptr)) {
11321133
/* If this also failed, let's abort. */
11331134
my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
11341135
}
@@ -1275,11 +1276,11 @@ PSI_statement_info sp_instr_jump_if_not::psi_info = {
12751276
#endif
12761277

12771278
bool sp_instr_jump_if_not::exec_core(THD *thd, uint *nextp) {
1278-
assert(m_expr_item);
1279+
assert(m_expr_item != nullptr);
12791280

1280-
Item *item = sp_prepare_func_item(thd, &m_expr_item);
1281-
1282-
if (!item) return true;
1281+
// LEX of instruction keeps execution state of the expression evaluation
1282+
Item *item = sp_prepare_func_item(thd, true, &m_expr_item);
1283+
if (item == nullptr) return true;
12831284

12841285
*nextp = item->val_bool() ? get_ip() + 1 : m_dest;
12851286

@@ -1358,11 +1359,11 @@ PSI_statement_info sp_instr_jump_case_when::psi_info = {
13581359
#endif
13591360

13601361
bool sp_instr_jump_case_when::exec_core(THD *thd, uint *nextp) {
1361-
assert(m_eq_item);
1362-
1363-
Item *item = sp_prepare_func_item(thd, &m_eq_item);
1362+
assert(m_eq_item != nullptr);
13641363

1365-
if (!item) return true;
1364+
// LEX of instruction keeps execution state of the case expression
1365+
Item *item = sp_prepare_func_item(thd, true, &m_eq_item);
1366+
if (item == nullptr) return true;
13661367

13671368
*nextp = item->val_bool() ? get_ip() + 1 : m_dest;
13681369

@@ -1445,7 +1446,7 @@ bool sp_instr_freturn::exec_core(THD *thd, uint *nextp) {
14451446
do it in scope of execution the current context/block.
14461447
*/
14471448

1448-
return thd->sp_runtime_ctx->set_return_value(thd, &m_expr_item);
1449+
return thd->sp_runtime_ctx->set_return_value(thd, true, &m_expr_item);
14491450
}
14501451

14511452
void sp_instr_freturn::print(const THD *thd, String *str) {
@@ -1866,21 +1867,21 @@ bool sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp) {
18661867

18671868
sp_rcontext *rctx = thd->sp_runtime_ctx;
18681869

1869-
if (rctx->set_case_expr(thd, m_case_expr_id, &m_expr_item)) {
1870+
// LEX of instruction keeps execution state of the case expression
1871+
if (rctx->set_case_expr(thd, true, m_case_expr_id, &m_expr_item)) {
18701872
if (!rctx->get_case_expr(m_case_expr_id)) {
18711873
// Failed to evaluate the value, the case expression is still not
18721874
// initialized. Set to NULL so we can continue.
18731875
Item *null_item = new Item_null();
18741876

1875-
if (!null_item || rctx->set_case_expr(thd, m_case_expr_id, &null_item)) {
1877+
if (null_item == nullptr ||
1878+
rctx->set_case_expr(thd, true, m_case_expr_id, &null_item)) {
18761879
// If this also failed, we have to abort.
18771880
my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
18781881
}
18791882
}
1880-
18811883
return true;
18821884
}
1883-
18841885
return false;
18851886
}
18861887

sql/sp_rcontext.cc

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,13 @@ bool sp_rcontext::init_var_items(THD *thd) {
146146
return false;
147147
}
148148

149-
bool sp_rcontext::set_return_value(THD *thd, Item **return_value_item) {
149+
bool sp_rcontext::set_return_value(THD *thd, bool standalone,
150+
Item **return_value_item) {
150151
assert(m_return_value_fld);
151152

152153
m_return_value_set = true;
153154

154-
return sp_eval_expr(thd, m_return_value_fld, return_value_item);
155+
return sp_eval_expr(thd, standalone, m_return_value_fld, return_value_item);
155156
}
156157

157158
bool sp_rcontext::push_cursor(sp_instr_cpush *i) {
@@ -402,13 +403,13 @@ bool sp_rcontext::handle_sql_condition(THD *thd, uint *ip,
402403
return true;
403404
}
404405

405-
bool sp_rcontext::set_variable(THD *thd, Field *field, Item **value) {
406-
if (!value) {
406+
bool sp_rcontext::set_variable(THD *thd, bool standalone, Field *field,
407+
Item **value) {
408+
if (value == nullptr) {
407409
field->set_null();
408410
return false;
409411
}
410-
411-
return sp_eval_expr(thd, field, value);
412+
return sp_eval_expr(thd, standalone, field, value);
412413
}
413414

414415
Item_cache *sp_rcontext::create_case_expr_holder(THD *thd,
@@ -425,10 +426,11 @@ Item_cache *sp_rcontext::create_case_expr_holder(THD *thd,
425426
return holder;
426427
}
427428

428-
bool sp_rcontext::set_case_expr(THD *thd, int case_expr_id,
429+
bool sp_rcontext::set_case_expr(THD *thd, bool standalone, int case_expr_id,
429430
Item **case_expr_item_ptr) {
430-
Item *case_expr_item = sp_prepare_func_item(thd, case_expr_item_ptr);
431-
if (!case_expr_item) return true;
431+
Item *case_expr_item =
432+
sp_prepare_func_item(thd, standalone, case_expr_item_ptr);
433+
if (case_expr_item == nullptr) return true;
432434

433435
if (!m_case_expr_holders[case_expr_id] ||
434436
m_case_expr_holders[case_expr_id]->result_type() !=
@@ -547,7 +549,7 @@ bool sp_cursor::Query_fetch_into_spvars::send_data(
547549
*/
548550
while ((spvar = spvar_iter++)) {
549551
Item *item = *item_iter++;
550-
if (thd->sp_runtime_ctx->set_variable(thd, spvar->offset, &item))
552+
if (thd->sp_runtime_ctx->set_variable(thd, false, spvar->offset, &item))
551553
return true;
552554
}
553555
return false;

0 commit comments

Comments
 (0)