mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
f748b69134
Before this fix, - a runtime error in a statement in a stored procedure with no error handlers was properly detected (as expected) - a runtime error in a statement with an error handler inherited from a non local runtime context (i.e., proc a with a handler, calling proc b) was properly detected (as expected) - a runtime error in a statement with a *local* error handler was executed as follows : a) the statement would succeed, regardless of the error condition, (bug) b) the error handler would be called (as expected). The root cause is that functions like my_messqge_sql would "forget" to set the thread flag thd->net.report_error to 1, because of the check involving sp_rcontext::found_handler_here(). Failure to set this flag would cause, later in the call stack, in Item_func::fix_fields() at line 190, the code to return FALSE and consider that executing the statement was successful. With this fix : - error handling code, that was duplicated in different places in the code, is now implemented in sp_rcontext::handle_error(), - handle_error() correctly sets thd->net.report_error when a handler is present, regardless of the handler location (local, or in the call stack). A test case, bug8153_subselect, has been written to demonstrate the change of behavior before and after the fix. Another test case, bug8153_function_a, as also been writen. This test has the same behavior before and after the fix. This test has been written to demonstrate that the previous expected result of procedure bug18787, was incorrect, since select no_such_function() should fail and therefore not produce a result. The incorrect result for bug18787 has the same root cause as Bug#8153, and the expected result has been adjusted. sql/mysqld.cc: Bug#8153, use sp_rcontext::handle_error() to handle errors. sql/sql_error.cc: Bug#8153, use sp_rcontext::handle_error() to handle errors. sql/protocol.cc: Bug#8153, use sp_rcontext::handle_error() to handle errors. sql/sp_rcontext.h: Bug#8153, created helper sp_rcontext::handle_error() to handle errors. sql/sp_rcontext.cc: Bug#8153, created helper sp_rcontext::handle_error() to handle errors. mysql-test/t/sp.test: Bug#8153, added test cases. mysql-test/r/sp.result: Bug#8153, added test cases, fixed expected result of bug18787.
335 lines
7.7 KiB
C++
335 lines
7.7 KiB
C++
/* -*- C++ -*- */
|
|
/* Copyright (C) 2002 MySQL AB
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
#ifndef _SP_RCONTEXT_H_
|
|
#define _SP_RCONTEXT_H_
|
|
|
|
#ifdef USE_PRAGMA_INTERFACE
|
|
#pragma interface /* gcc class implementation */
|
|
#endif
|
|
|
|
struct sp_cond_type;
|
|
class sp_cursor;
|
|
struct sp_variable;
|
|
class sp_lex_keeper;
|
|
class sp_instr_cpush;
|
|
|
|
#define SP_HANDLER_NONE 0
|
|
#define SP_HANDLER_EXIT 1
|
|
#define SP_HANDLER_CONTINUE 2
|
|
#define SP_HANDLER_UNDO 3
|
|
|
|
typedef struct
|
|
{
|
|
struct sp_cond_type *cond;
|
|
uint handler; // Location of handler
|
|
int type;
|
|
uint foffset; // Frame offset for the handlers declare level
|
|
} sp_handler_t;
|
|
|
|
|
|
/*
|
|
This class is a runtime context of a Stored Routine. It is used in an
|
|
execution and is intended to contain all dynamic objects (i.e. objects, which
|
|
can be changed during execution), such as:
|
|
- stored routine variables;
|
|
- cursors;
|
|
- handlers;
|
|
|
|
Runtime context is used with sp_head class. sp_head class is intended to
|
|
contain all static things, related to the stored routines (code, for example).
|
|
sp_head instance creates runtime context for the execution of a stored
|
|
routine.
|
|
|
|
There is a parsing context (an instance of sp_pcontext class), which is used
|
|
on parsing stage. However, now it contains some necessary for an execution
|
|
things, such as definition of used stored routine variables. That's why
|
|
runtime context needs a reference to the parsing context.
|
|
*/
|
|
|
|
class sp_rcontext : public Sql_alloc
|
|
{
|
|
sp_rcontext(const sp_rcontext &); /* Prevent use of these */
|
|
void operator=(sp_rcontext &);
|
|
|
|
public:
|
|
|
|
/*
|
|
Arena used to (re) allocate items on . E.g. reallocate INOUT/OUT
|
|
SP parameters when they don't fit into prealloced items. This
|
|
is common situation with String items. It is used mainly in
|
|
sp_eval_func_item().
|
|
*/
|
|
Query_arena *callers_arena;
|
|
|
|
#ifndef DBUG_OFF
|
|
/*
|
|
The routine for which this runtime context is created. Used for checking
|
|
if correct runtime context is used for variable handling.
|
|
*/
|
|
sp_head *sp;
|
|
#endif
|
|
|
|
sp_rcontext(sp_pcontext *root_parsing_ctx, Field *return_value_fld,
|
|
sp_rcontext *prev_runtime_ctx);
|
|
bool init(THD *thd);
|
|
|
|
~sp_rcontext();
|
|
|
|
int
|
|
set_variable(THD *thd, uint var_idx, Item **value);
|
|
|
|
Item *
|
|
get_item(uint var_idx);
|
|
|
|
Item **
|
|
get_item_addr(uint var_idx);
|
|
|
|
bool
|
|
set_return_value(THD *thd, Item **return_value_item);
|
|
|
|
inline bool
|
|
is_return_value_set() const
|
|
{
|
|
return m_return_value_set;
|
|
}
|
|
|
|
inline void
|
|
push_handler(struct sp_cond_type *cond, uint h, int type, uint f)
|
|
{
|
|
m_handler[m_hcount].cond= cond;
|
|
m_handler[m_hcount].handler= h;
|
|
m_handler[m_hcount].type= type;
|
|
m_handler[m_hcount].foffset= f;
|
|
m_hcount+= 1;
|
|
}
|
|
|
|
inline void
|
|
pop_handlers(uint count)
|
|
{
|
|
m_hcount-= count;
|
|
}
|
|
|
|
// Returns 1 if a handler was found, 0 otherwise.
|
|
bool
|
|
find_handler(uint sql_errno,MYSQL_ERROR::enum_warning_level level);
|
|
|
|
// If there is an error handler for this error, handle it and return TRUE.
|
|
bool
|
|
handle_error(uint sql_errno,
|
|
MYSQL_ERROR::enum_warning_level level,
|
|
THD *thd);
|
|
|
|
// Returns handler type and sets *ip to location if one was found
|
|
inline int
|
|
found_handler(uint *ip, uint *fp)
|
|
{
|
|
if (m_hfound < 0)
|
|
return SP_HANDLER_NONE;
|
|
*ip= m_handler[m_hfound].handler;
|
|
*fp= m_handler[m_hfound].foffset;
|
|
return m_handler[m_hfound].type;
|
|
}
|
|
|
|
// Returns true if we found a handler in this context
|
|
inline bool
|
|
found_handler_here()
|
|
{
|
|
return (m_hfound >= 0);
|
|
}
|
|
|
|
// Clears the handler find state
|
|
inline void
|
|
clear_handler()
|
|
{
|
|
m_hfound= -1;
|
|
}
|
|
|
|
inline void
|
|
push_hstack(uint h)
|
|
{
|
|
m_hstack[m_hsp++]= h;
|
|
}
|
|
|
|
inline uint
|
|
pop_hstack()
|
|
{
|
|
return m_hstack[--m_hsp];
|
|
}
|
|
|
|
inline void
|
|
enter_handler(int hid)
|
|
{
|
|
m_in_handler[m_ihsp++]= hid;
|
|
}
|
|
|
|
inline void
|
|
exit_handler()
|
|
{
|
|
m_ihsp-= 1;
|
|
}
|
|
|
|
void
|
|
push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
|
|
|
|
void
|
|
pop_cursors(uint count);
|
|
|
|
void
|
|
pop_all_cursors()
|
|
{
|
|
pop_cursors(m_ccount);
|
|
}
|
|
|
|
inline sp_cursor *
|
|
get_cursor(uint i)
|
|
{
|
|
return m_cstack[i];
|
|
}
|
|
|
|
/*
|
|
CASE expressions support.
|
|
*/
|
|
|
|
int
|
|
set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr);
|
|
|
|
Item *
|
|
get_case_expr(int case_expr_id);
|
|
|
|
Item **
|
|
get_case_expr_addr(int case_expr_id);
|
|
|
|
private:
|
|
sp_pcontext *m_root_parsing_ctx;
|
|
|
|
/* Virtual table for storing variables. */
|
|
TABLE *m_var_table;
|
|
|
|
/*
|
|
Collection of Item_field proxies, each of them points to the corresponding
|
|
field in m_var_table.
|
|
*/
|
|
Item **m_var_items;
|
|
|
|
/*
|
|
This is a pointer to a field, which should contain return value for stored
|
|
functions (only). For stored procedures, this pointer is NULL.
|
|
*/
|
|
Field *m_return_value_fld;
|
|
|
|
/*
|
|
Indicates whether the return value (in m_return_value_fld) has been set
|
|
during execution.
|
|
*/
|
|
bool m_return_value_set;
|
|
|
|
sp_handler_t *m_handler; // Visible handlers
|
|
uint m_hcount; // Stack pointer for m_handler
|
|
uint *m_hstack; // Return stack for continue handlers
|
|
uint m_hsp; // Stack pointer for m_hstack
|
|
uint *m_in_handler; // Active handler, for recursion check
|
|
uint m_ihsp; // Stack pointer for m_in_handler
|
|
int m_hfound; // Set by find_handler; -1 if not found
|
|
|
|
sp_cursor **m_cstack;
|
|
uint m_ccount;
|
|
|
|
Item_cache **m_case_expr_holders;
|
|
|
|
/* Previous runtime context (NULL if none) */
|
|
sp_rcontext *m_prev_runtime_ctx;
|
|
|
|
private:
|
|
bool init_var_table(THD *thd);
|
|
bool init_var_items();
|
|
|
|
Item_cache *create_case_expr_holder(THD *thd, Item_result result_type);
|
|
|
|
int set_variable(THD *thd, Field *field, Item **value);
|
|
}; // class sp_rcontext : public Sql_alloc
|
|
|
|
|
|
/*
|
|
An interceptor of cursor result set used to implement
|
|
FETCH <cname> INTO <varlist>.
|
|
*/
|
|
|
|
class Select_fetch_into_spvars: public select_result_interceptor
|
|
{
|
|
List<struct sp_variable> *spvar_list;
|
|
uint field_count;
|
|
public:
|
|
Select_fetch_into_spvars() {} /* Remove gcc warning */
|
|
uint get_field_count() { return field_count; }
|
|
void set_spvar_list(List<struct sp_variable> *vars) { spvar_list= vars; }
|
|
|
|
virtual bool send_eof() { return FALSE; }
|
|
virtual bool send_data(List<Item> &items);
|
|
virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
|
};
|
|
|
|
|
|
/* A mediator between stored procedures and server side cursors */
|
|
|
|
class sp_cursor : public Sql_alloc
|
|
{
|
|
public:
|
|
|
|
sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
|
|
|
|
virtual ~sp_cursor()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
sp_lex_keeper *
|
|
get_lex_keeper() { return m_lex_keeper; }
|
|
|
|
int
|
|
open(THD *thd);
|
|
|
|
int
|
|
close(THD *thd);
|
|
|
|
inline my_bool
|
|
is_open()
|
|
{
|
|
return test(server_side_cursor);
|
|
}
|
|
|
|
int
|
|
fetch(THD *, List<struct sp_variable> *vars);
|
|
|
|
inline sp_instr_cpush *
|
|
get_instr()
|
|
{
|
|
return m_i;
|
|
}
|
|
|
|
private:
|
|
|
|
Select_fetch_into_spvars result;
|
|
sp_lex_keeper *m_lex_keeper;
|
|
Server_side_cursor *server_side_cursor;
|
|
sp_instr_cpush *m_i; // My push instruction
|
|
void
|
|
destroy();
|
|
|
|
}; // class sp_cursor : public Sql_alloc
|
|
|
|
#endif /* _SP_RCONTEXT_H_ */
|