Skip to content

Commit 9524c12

Browse files
committed
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 5db8f4b commit 9524c12

10 files changed

+107
-79
lines changed

sql/item.cc

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

18611861
bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it) {
1862-
return ctx->set_variable(thd, get_var_idx(), it);
1862+
return ctx->set_variable(thd, false, get_var_idx(), it);
18631863
}
18641864

18651865
/*****************************************************************************
@@ -9176,8 +9176,8 @@ bool Item_trigger_field::eq(const Item *item, bool) const {
91769176
down_cast<const Item_trigger_field *>(item)->field_name);
91779177
}
91789178

9179-
bool Item_trigger_field::set_value(THD *thd, sp_rcontext * /*ctx*/, Item **it) {
9180-
Item *item = sp_prepare_func_item(thd, it);
9179+
bool Item_trigger_field::set_value(THD *thd, sp_rcontext *, Item **it) {
9180+
Item *item = sp_prepare_func_item(thd, true, it);
91819181
if (item == nullptr) return true;
91829182

91839183
if (!fixed) {

sql/query_result.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,8 @@ bool Query_dumpvar::send_data(THD *thd, const mem_root_deque<Item *> &items) {
718718
while ((mv = var_li++) && it != VisibleFields(items).end()) {
719719
Item *item = *it++;
720720
if (mv->is_local()) {
721-
if (thd->sp_runtime_ctx->set_variable(thd, mv->get_offset(), &item))
721+
if (thd->sp_runtime_ctx->set_variable(thd, false, mv->get_offset(),
722+
&item))
722723
return true;
723724
} else {
724725
Item_func_set_user_var *suv = new Item_func_set_user_var(mv->name, item);

sql/sp.cc

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2431,19 +2431,28 @@ bool sp_check_name(LEX_STRING *ident) {
24312431
/**
24322432
Prepare an Item for evaluation (call of fix_fields).
24332433
2434-
@param thd thread handler
2435-
@param it_addr pointer on item reference
2436-
2437-
@retval
2438-
NULL error
2439-
@retval
2440-
non-NULL prepared item
2434+
@param thd thread handler
2435+
@param standalone if true, thd->lex is directly associated with item.
2436+
Preparation and execution state will be changed and
2437+
preparation data will be saved for later executions.
2438+
If false, item is part of a larger query expression and
2439+
no such state transitions should take place.
2440+
It also means that such items cannot save preparation data.
2441+
@param it_addr pointer to item reference
2442+
2443+
@returns pointer to prepared Item on success, NULL on error.
24412444
*/
2442-
Item *sp_prepare_func_item(THD *thd, Item **it_addr) {
2445+
Item *sp_prepare_func_item(THD *thd, bool standalone, Item **it_addr) {
2446+
LEX *const lex = standalone ? thd->lex : nullptr;
2447+
2448+
// If item is part of larger query expression, it must be in executing state:
2449+
assert(lex != nullptr ||
2450+
(thd->lex->unit->is_prepared() && thd->lex->is_exec_started()));
2451+
24432452
it_addr = (*it_addr)->this_item_addr(thd, it_addr);
24442453

24452454
if ((*it_addr)->fixed) {
2446-
thd->lex->set_exec_started();
2455+
if (lex != nullptr) lex->set_exec_started();
24472456
return *it_addr;
24482457
}
24492458

@@ -2454,37 +2463,40 @@ Item *sp_prepare_func_item(THD *thd, Item **it_addr) {
24542463
DBUG_PRINT("info", ("fix_fields() failed"));
24552464
return nullptr;
24562465
}
2457-
thd->lex->unit->set_prepared();
2458-
thd->lex->save_cmd_properties(thd);
2459-
thd->lex->set_exec_started();
2460-
2466+
// If item is a separate query expression, set its state to executing
2467+
if (lex != nullptr) {
2468+
lex->unit->set_prepared();
2469+
lex->save_cmd_properties(thd);
2470+
lex->set_exec_started();
2471+
}
24612472
return *it_addr;
24622473
}
24632474

24642475
/**
24652476
Evaluate an expression and store the result in the field.
24662477
2467-
@param thd current thread object
2468-
@param result_field the field to store the result
2469-
@param expr_item_ptr the root item of the expression
2478+
@param thd current thread object
2479+
@param standalone if true, thd->lex contains preparation and execution
2480+
state of item, otherwise item is part of
2481+
a query expression that is already in executing state.
2482+
@param result_field the field to store the result
2483+
@param expr_item_ptr the root item of the expression
24702484
2471-
@retval
2472-
false on success
2473-
@retval
2474-
true on error
2485+
@returns false on success, true on error
24752486
*/
2476-
bool sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) {
2477-
Item *expr_item;
2487+
bool sp_eval_expr(THD *thd, bool standalone, Field *result_field,
2488+
Item **expr_item_ptr) {
24782489
Strict_error_handler strict_handler(
24792490
Strict_error_handler::ENABLE_SET_SELECT_STRICT_ERROR_HANDLER);
24802491
enum_check_fields save_check_for_truncated_fields =
24812492
thd->check_for_truncated_fields;
24822493
unsigned int stmt_unsafe_rollback_flags =
24832494
thd->get_transaction()->get_unsafe_rollback_flags(Transaction_ctx::STMT);
24842495

2485-
if (!*expr_item_ptr) goto error;
2496+
assert(*expr_item_ptr != nullptr);
24862497

2487-
if (!(expr_item = sp_prepare_func_item(thd, expr_item_ptr))) goto error;
2498+
Item *expr_item = sp_prepare_func_item(thd, standalone, expr_item_ptr);
2499+
if (expr_item == nullptr) goto error;
24882500

24892501
/*
24902502
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
@@ -408,9 +408,10 @@ bool sp_check_name(LEX_STRING *ident);
408408
Table_ref *sp_add_to_query_tables(THD *thd, LEX *lex, const char *db,
409409
const char *name);
410410

411-
Item *sp_prepare_func_item(THD *thd, Item **it_addr);
411+
Item *sp_prepare_func_item(THD *thd, bool standalone, Item **it_addr);
412412

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

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

sql/sp_head.cc

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

2573-
err_status = func_runtime_ctx->set_variable(thd, arg_no, &(argp[arg_no]));
2574-
2573+
err_status =
2574+
func_runtime_ctx->set_variable(thd, false, arg_no, &(argp[arg_no]));
25752575
if (err_status) goto err_with_cleanup;
25762576
}
25772577

@@ -2751,12 +2751,12 @@ bool sp_head::execute_procedure(THD *thd, mem_root_deque<Item *> *args) {
27512751

27522752
sp_rcontext *proc_runtime_ctx =
27532753
sp_rcontext::create(thd, m_root_parsing_ctx, nullptr);
2754-
2755-
if (!proc_runtime_ctx) {
2754+
if (proc_runtime_ctx == nullptr) {
27562755
thd->sp_runtime_ctx = sp_runtime_ctx_saved;
27572756

2758-
if (!sp_runtime_ctx_saved) ::destroy(parent_sp_runtime_ctx);
2759-
2757+
if (sp_runtime_ctx_saved == nullptr) {
2758+
::destroy(parent_sp_runtime_ctx);
2759+
}
27602760
return true;
27612761
}
27622762

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

27702770
for (uint i = 0; i < params; ++i, ++it_args) {
27712771
Item *arg_item = *it_args;
2772-
if (!arg_item) break;
2772+
if (arg_item == nullptr) break;
27732773

27742774
sp_variable *spvar = m_root_parsing_ctx->find_variable(i);
2775-
2776-
if (!spvar) continue;
2775+
if (spvar == nullptr) continue;
27772776

27782777
if (spvar->mode != sp_variable::MODE_IN) {
27792778
Settable_routine_parameter *srp =
27802779
arg_item->get_settable_routine_parameter();
2781-
2782-
if (!srp) {
2780+
if (srp == nullptr) {
27832781
my_error(ER_SP_NOT_VAR_ARG, MYF(0), i + 1, m_qname.str);
27842782
err_status = true;
27852783
break;
27862784
}
27872785
}
27882786

27892787
if (spvar->mode == sp_variable::MODE_OUT) {
2790-
Item_null *null_item = new Item_null();
2791-
2792-
if (!null_item ||
2793-
proc_runtime_ctx->set_variable(thd, i, (Item **)&null_item)) {
2788+
Item *null_item = new Item_null();
2789+
if (null_item == nullptr) {
2790+
err_status = true;
2791+
break;
2792+
}
2793+
if (proc_runtime_ctx->set_variable(thd, false, i, &null_item)) {
27942794
err_status = true;
27952795
break;
27962796
}
27972797
} else {
2798-
if (proc_runtime_ctx->set_variable(thd, i, &*it_args)) {
2798+
if (proc_runtime_ctx->set_variable(thd, false, i, &*it_args)) {
27992799
err_status = true;
28002800
break;
28012801
}

sql/sp_instr.cc

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

1014-
if (!thd->sp_runtime_ctx->set_variable(thd, m_offset, &m_value_item))
1014+
// LEX of instruction keeps execution state of the assignment operation
1015+
if (!thd->sp_runtime_ctx->set_variable(thd, true, m_offset, &m_value_item))
10151016
return false;
10161017

10171018
/* Failed to evaluate the value. Reset the variable to NULL. */
10181019

1019-
if (thd->sp_runtime_ctx->set_variable(thd, m_offset, nullptr)) {
1020+
if (thd->sp_runtime_ctx->set_variable(thd, true, m_offset, nullptr)) {
10201021
/* If this also failed, let's abort. */
10211022
my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
10221023
}
@@ -1163,11 +1164,11 @@ PSI_statement_info sp_instr_jump_if_not::psi_info = {
11631164
#endif
11641165

11651166
bool sp_instr_jump_if_not::exec_core(THD *thd, uint *nextp) {
1166-
assert(m_expr_item);
1167+
assert(m_expr_item != nullptr);
11671168

1168-
Item *item = sp_prepare_func_item(thd, &m_expr_item);
1169-
1170-
if (!item) return true;
1169+
// LEX of instruction keeps execution state of the expression evaluation
1170+
Item *item = sp_prepare_func_item(thd, true, &m_expr_item);
1171+
if (item == nullptr) return true;
11711172

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

@@ -1246,11 +1247,11 @@ PSI_statement_info sp_instr_jump_case_when::psi_info = {
12461247
#endif
12471248

12481249
bool sp_instr_jump_case_when::exec_core(THD *thd, uint *nextp) {
1249-
assert(m_eq_item);
1250-
1251-
Item *item = sp_prepare_func_item(thd, &m_eq_item);
1250+
assert(m_eq_item != nullptr);
12521251

1253-
if (!item) return true;
1252+
// LEX of instruction keeps execution state of the case expression
1253+
Item *item = sp_prepare_func_item(thd, true, &m_eq_item);
1254+
if (item == nullptr) return true;
12541255

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

@@ -1333,7 +1334,7 @@ bool sp_instr_freturn::exec_core(THD *thd, uint *nextp) {
13331334
do it in scope of execution the current context/block.
13341335
*/
13351336

1336-
return thd->sp_runtime_ctx->set_return_value(thd, &m_expr_item);
1337+
return thd->sp_runtime_ctx->set_return_value(thd, true, &m_expr_item);
13371338
}
13381339

13391340
void sp_instr_freturn::print(const THD *thd, String *str) {
@@ -1754,21 +1755,21 @@ bool sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp) {
17541755

17551756
sp_rcontext *rctx = thd->sp_runtime_ctx;
17561757

1757-
if (rctx->set_case_expr(thd, m_case_expr_id, &m_expr_item)) {
1758+
// LEX of instruction keeps execution state of the case expression
1759+
if (rctx->set_case_expr(thd, true, m_case_expr_id, &m_expr_item)) {
17581760
if (!rctx->get_case_expr(m_case_expr_id)) {
17591761
// Failed to evaluate the value, the case expression is still not
17601762
// initialized. Set to NULL so we can continue.
17611763
Item *null_item = new Item_null();
17621764

1763-
if (!null_item || rctx->set_case_expr(thd, m_case_expr_id, &null_item)) {
1765+
if (null_item == nullptr ||
1766+
rctx->set_case_expr(thd, true, m_case_expr_id, &null_item)) {
17641767
// If this also failed, we have to abort.
17651768
my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
17661769
}
17671770
}
1768-
17691771
return true;
17701772
}
1771-
17721773
return false;
17731774
}
17741775

sql/sp_rcontext.cc

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

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

151152
m_return_value_set = true;
152153

153-
return sp_eval_expr(thd, m_return_value_fld, return_value_item);
154+
return sp_eval_expr(thd, standalone, m_return_value_fld, return_value_item);
154155
}
155156

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

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

413414
Item_cache *sp_rcontext::create_case_expr_holder(THD *thd,
@@ -424,10 +425,11 @@ Item_cache *sp_rcontext::create_case_expr_holder(THD *thd,
424425
return holder;
425426
}
426427

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

432434
if (!m_case_expr_holders[case_expr_id] ||
433435
m_case_expr_holders[case_expr_id]->result_type() !=
@@ -546,7 +548,7 @@ bool sp_cursor::Query_fetch_into_spvars::send_data(
546548
*/
547549
while ((spvar = spvar_iter++)) {
548550
Item *item = *item_iter++;
549-
if (thd->sp_runtime_ctx->set_variable(thd, spvar->offset, &item))
551+
if (thd->sp_runtime_ctx->set_variable(thd, false, spvar->offset, &item))
550552
return true;
551553
}
552554
return false;

0 commit comments

Comments
 (0)