#ifndef _SP_INSTR_H_
#define _SP_INSTR_H_
#include "mariadb.h"
#include "sql_alloc.h" // Sql_alloc
#include "sql_class.h" // THD, Query_arena
#include "sql_lex.h" // class sp_lex_local
#include "sp_pcontext.h" // class sp_pcontext
#include "sp_head.h" // class sp_head
/*
Sufficient max length of frame offsets .
*/
static const int SP_INSTR_UINT_MAXLEN= 8 ;
class sp_lex_cursor: public sp_lex_local, public Query_arena
{
public :
sp_lex_cursor(THD *thd, const LEX *oldlex, MEM_ROOT *mem_root_arg)
: sp_lex_local(thd, oldlex),
Query_arena(mem_root_arg, STMT_INITIALIZED_FOR_SP),
m_expr_str(empty_clex_str)
{}
sp_lex_cursor(THD *thd, const LEX *oldlex)
: sp_lex_local(thd, oldlex),
Query_arena(thd->lex->sphead->get_main_mem_root(),
STMT_INITIALIZED_FOR_SP),
m_expr_str(empty_clex_str)
{}
~sp_lex_cursor() { free_items(); }
bool cleanup_stmt(bool /*restore_set_statement_vars*/) override
{
return false ;
}
Query_arena *query_arena() override
{
return this ;
}
bool validate()
{
DBUG_ASSERT(sql_command == SQLCOM_SELECT || sql_command == SQLCOM_END);
if (result)
{
my_error(ER_SP_BAD_CURSOR_SELECT, MYF(0 ));
return true ;
}
return false ;
}
bool stmt_finalize(THD *thd)
{
if (validate())
return true ;
sp_lex_in_use= true ;
free_list= thd->free_list;
thd->free_list= nullptr;
return false ;
}
bool sp_cursor_stmt_finalize(THD *thd, const Lex_ident_sys_st &ps_name,
const char *beg, const char *end)
{
m_ps_name= Lex_ident_sys(ps_name);
pop_select(); //main select
if (unlikely(stmt_finalize(thd)))
return true ;
/*
Always make a query copy , even if is_metadata_used ( ) is false and
therefore the cursor statement won ' t need re - prepare .
This is needed to properly log into the slow log simple queries like :
OPEN c FOR SELECT SLEEP ( 1 ) ;
*/
LEX_CSTRING expr_str= make_string(thd, beg, end);
if (expr_str.str == nullptr)
return true ;
set_expr_str(expr_str);
return sphead->restore_lex(thd);
}
void set_expr_str(const LEX_CSTRING &expr_str)
{
m_expr_str= expr_str;
}
const LEX_CSTRING &get_expr_str() const
{
return m_expr_str;
}
sp_lex_cursor* get_lex_for_cursor() override
{
return this ;
}
// Set a prepared statement name
void set_ps_name(const Lex_ident_sys_st &ps_name)
{
m_ps_name= ps_name;
}
// Get a prepared statement name
const Lex_ident_sys &get_ps_name() const
{
return m_ps_name;
}
private :
LEX_CSTRING m_expr_str;
/*
A prepared statement name in case of a dynamic cursor ,
e . g . " stmt " in this example :
DECLARE c CURSOR FOR stmt ;
PREPARE stmt FROM ' SELECT 1 ' ;
OPEN c ;
In case if the cursor is not dynamic , m_ps_name is equal to { 0 , 0 } .
*/
Lex_ident_sys m_ps_name;
};
//
// "Instructions"...
//
// Forward declaration for use in the method sp_instr::opt_move().
class sp_instr_opt_meta;
class sp_instr :public Query_arena, public Sql_alloc
{
sp_instr(const sp_instr &); /**< Prevent use of these */
void operator =(sp_instr &);
public :
uint marked;
uint m_ip; ///< My index
sp_pcontext *m_ctx; ///< My parse context
uint m_lineno;
/// Should give each a name or type code for debugging purposes?
sp_instr(uint ip, sp_pcontext *ctx)
: Query_arena(0 , STMT_INITIALIZED_FOR_SP),
marked(0 ),
m_ip(ip),
m_ctx(ctx),
m_lineno(0 )
#ifdef PROTECT_STATEMENT_MEMROOT
, m_has_been_run(NON_RUN)
#endif
{}
virtual ~sp_instr()
{
free_items();
}
/**
Execute this instruction
@ param thd Thread handle
@ param [ out ] nextp index of the next instruction to execute . ( For most
instructions this will be the instruction following this
one ) . Note that this parameter is undefined in case of
errors , use get_cont_dest ( ) to find the continuation
instruction for CONTINUE error handlers .
@ retval 0 on success ,
@ retval other if some error occurred
*/
virtual int execute(THD *thd, uint *nextp) = 0 ;
/**
Execute < code > open_and_lock_tables ( ) < / code > for this statement .
Open and lock the tables used by this statement , as a pre - requisite
to execute the core logic of this instruction with
< code > exec_core ( ) < / code > .
@ param thd the current thread
@ param tables the list of tables to open and lock
@ return zero on success , non zero on failure .
*/
int exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables);
/**
Get the continuation destination of this instruction .
@ return the continuation destination
*/
virtual uint get_cont_dest() const ;
/*
Execute core function of instruction after all preparations ( e . g .
setting of proper LEX , saving part of the thread context have been
done ) .
Should be implemented for instructions using expressions or whole
statements ( thus having to have own LEX ) . Used in concert with
sp_lex_keeper class and its descendants ( there are none currently ) .
*/
virtual int exec_core(THD *thd, uint *nextp);
virtual void print(String *str) = 0 ;
void print_cmd_and_array_element(String *str,
const LEX_CSTRING &cmd,
const LEX_CSTRING &rcontext_name,
const LEX_CSTRING &array_name,
uint index_offset,
size_t extra_reserve= 0 ) const ;
void print_fetch_into(String *str, List<sp_fetch_target> list);
virtual void backpatch(uint dest, sp_pcontext *dst_ctx)
{}
/**
Mark this instruction as reachable during optimization and return the
index to the next instruction . Jump instruction will add their
destination to the leads list .
*/
virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads)
{
marked= 1 ;
return m_ip+1 ;
}
/**
Short - cut jumps to jumps during optimization . This is used by the
jump instructions ' opt_mark ( ) methods . ' start ' is the starting point ,
used to prevent the mark sweep from looping for ever . Return the
end destination .
*/
virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
{
return m_ip;
}
/**
Inform the instruction that it has been moved during optimization .
Most instructions will simply update its index , but jump instructions
must also take care of their destination pointers . Forward jumps get
pushed to the backpatch list ' ibp ' .
*/
virtual void opt_move(uint dst, List<sp_instr_opt_meta> *ibp)
{
m_ip= dst;
}
virtual PSI_statement_info* get_psi_info() = 0 ;
virtual SQL_I_List<Item_trigger_field>* get_instr_trig_field_list()
{
return nullptr;
}
#ifdef PROTECT_STATEMENT_MEMROOT
bool has_been_run() const
{
return m_has_been_run == RUN;
}
void mark_as_qc_used()
{
m_has_been_run= QC;
}
void mark_as_run()
{
if (m_has_been_run == QC)
m_has_been_run= NON_RUN; // answer was from WC => not really executed
else
m_has_been_run= RUN;
}
void mark_as_not_run()
{
m_has_been_run= NON_RUN;
}
private :
enum {NON_RUN, QC, RUN} m_has_been_run;
#endif
}; // class sp_instr : public Sql_alloc
class sp_instr;
class sp_lex_instr;
/**
Auxilary class to which instructions delegate responsibility
for handling LEX and preparations before executing statement
or calculating complex expression .
Exist mainly to avoid having double hierarchy between instruction
classes .
@ todo
Add ability to not store LEX and do any preparations if
expression used is simple .
*/
class sp_lex_keeper final
{
/** Prevent use of these */
sp_lex_keeper(const sp_lex_keeper &);
void operator =(sp_lex_keeper &);
public :
sp_lex_keeper(LEX *lex, bool lex_resp)
: m_lex(lex),
m_lex_resp(lex_resp),
prelocking_tables(nullptr),
lex_query_tables_own_last(nullptr),
m_first_execution(true )
{
lex->sp_lex_in_use= true ;
}
~sp_lex_keeper()
{
if (m_lex_resp)
{
m_lex_resp= false ;
/* Prevent endless recursion. */
m_lex->sphead= nullptr;
delete m_lex->result;
lex_end(m_lex);
delete m_lex;
}
}
/**
Prepare execution of instruction using LEX , if requested check whenever
we have read access to tables used and open / lock them , call instruction ' s
exec_core ( ) method , perform cleanup afterwards .
@ todo Conflicting comment in sp_head . cc
*/
int reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
sp_instr* instr, bool rerun_the_same_instr);
/**
Do several attempts to execute an instruction .
This method installs Reprepare_observer to catch possible metadata changes
on depending database objects , then calls reset_lex_and_exec_core ( )
to execute the instruction . If execution of the instruction fails , does
re - parsing of the instruction and re - execute it .
@ param thd Thread context .
@ param [ out ] nextp Pointer for storing a next instruction to execute
@ param open_tables Flag to specify if the function should check read
access to tables in LEX ' s table list and open and
lock them ( used in instructions which need to
calculate some expression and don ' t execute
complete statement ) .
@ param instr instruction which we prepare context and run .
@ return 0 on success , 1 on error
*/
int validate_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
sp_lex_instr* instr);
int cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
sp_lex_instr *instr);
/**
( Re - ) parse the query corresponding to this instruction and return a new
LEX - object .
@ param thd Thread context .
@ param sp The stored program .
@ return new LEX - object or NULL in case of failure .
*/
LEX *parse_expr(THD *thd, const sp_head *sp);
inline uint sql_command() const
{
return (uint)m_lex->sql_command;
}
void disable_query_cache()
{
m_lex->safe_to_cache_query= 0 ;
}
LEX *lex() const
{
return m_lex;
}
private :
/**
Clean up and destroy owned LEX object .
*/
void free_lex(THD *thd);
/**
Set LEX object .
@ param lex LEX - object
*/
void set_lex(LEX *lex);
private :
LEX *m_lex;
/**
Indicates whenever this sp_lex_keeper instance responsible
for LEX deletion .
*/
bool m_lex_resp;
/*
Support for being able to execute this statement in two modes :
a ) inside prelocked mode set by the calling procedure or its ancestor .
b ) outside of prelocked mode , when this statement enters / leaves
prelocked mode itself .
*/
/**
List of additional tables this statement needs to lock when it
enters / leaves prelocked mode on its own .
*/
TABLE_LIST *prelocking_tables;
/**
The value m_lex - > query_tables_own_last should be set to this when the
statement enters / leaves prelocked mode on its own .
*/
TABLE_LIST **lex_query_tables_own_last;
bool m_first_execution;
};
/**
The base class for any stored program instruction that need to get access
to a LEX object on execution .
*/
class sp_lex_instr : public sp_instr
{
public :
sp_lex_instr(uint ip, sp_pcontext *ctx, LEX *lex, bool is_lex_owner)
: sp_instr(ip, ctx),
m_lex_keeper(lex, is_lex_owner),
m_mem_root_for_reparsing(nullptr),
m_sp{lex->sphead}
{}
~sp_lex_instr() override
{
if (m_mem_root_for_reparsing)
{
/*
Free items owned by an instance of sp_lex_instr and call m_lex_keeper ' s
destructor explicitly to avoid referencing a deallocated memory
owned by the memory root m_mem_root_for_reparsing that else would take
place in case their implicit invocations ( in that case , m_lex_keeper ' s
destructor and the method free_items ( ) called by ~ sp_instr are invoked
after the memory owned by the memory root m_mem_root_for_reparsing
be freed , that would result in abnormal server termination )
*/
free_items();
m_lex_keeper.~sp_lex_keeper();
/*
Ignore the OOM error explicitly since we are inside destructor
*/
(void )m_sp->register_instr_mem_root_for_deallocation(
m_mem_root_for_reparsing);
m_mem_root_for_reparsing= nullptr;
}
}
virtual bool is_invalid() const = 0 ;
virtual void invalidate() = 0 ;
/**
Return the query string , which can be passed to the parser ,
that is a valid SQL - statement .
@ param [ out ] sql_query SQL - statement query string .
*/
virtual void get_query(String *sql_query) const ;
/**
( Re - ) parse the query corresponding to this instruction and return a new
LEX - object .
@ param thd Thread context .
@ param sp The stored program .
@ param lex SP instruction ' s lex
@ return new LEX - object or NULL in case of failure .
*/
LEX *parse_expr(THD *thd, sp_head *sp, LEX *lex);
SQL_I_List<Item_trigger_field>* get_instr_trig_field_list() override
{
return &m_cur_trigger_stmt_items;
}
protected :
/**
@ return the expression query string . This string can ' t be passed directly
to the parser as it is most likely not a valid SQL - statement .
*/
virtual LEX_CSTRING get_expr_query() const = 0 ;
/**
Some expressions may be re - parsed as SELECT statements .
This method is overridden in derived classes for instructions
those SQL command should be adjusted .
*/
virtual void adjust_sql_command(LEX *)
{}
/**
Callback method which is called after an expression string successfully
parsed and the thread context has not been switched to the outer context .
The thread context contains new LEX - object corresponding to the parsed
expression string .
@ param thd Thread context .
@ return Error flag .
*/
virtual bool on_after_expr_parsing(THD *)
{
return false ;
}
sp_lex_keeper m_lex_keeper;
private :
/**
Buffer responsible for keeping the query text over the lifetime of
* this . If reparsing is required , then this string is cleared and
replaced with the new text on reparse . Allows for deferred hint
resolution when query hints are specified as part of the trigger
definition .
*/
String sql_query_cache;
/**
List of Item_trigger_field objects created on parsing of a SQL statement
corresponding to this SP - instruction .
*/
SQL_I_List<Item_trigger_field> m_cur_trigger_stmt_items;
/**
MEM_ROOT used for allocation of memory on re - parsing of a statement
caused failure of SP - instruction execution
*/
MEM_ROOT *m_mem_root_for_reparsing;
/**
Clean up items previously created on behalf of the current instruction .
@ return a list of Item_param instances representing position parameters
specified for the instruction that is a part of a prepared
statement
*/
List<Item_param> cleanup_before_parsing(enum_sp_type sp_type);
sp_head *m_sp;
/**
Set up field object for every NEW / OLD item of the trigger and
move the list of Item_trigger_field objects , created on parsing the current
trigger ' s instruction , from sp_head to trigger ' s SP instruction object .
@ param thd current thread
@ param sp sp_head object of the trigger
@ param next_trig_items_list pointer to the next list of Item_trigger_field
objects that used as a link between lists
to support list of lists structure .
@ return false on success , true on failure
*/
bool setup_table_fields_for_trigger(
THD *thd, sp_head *sp,
SQL_I_List<Item_trigger_field> *next_trig_items_list);
bool setup_memroot_for_reparsing(sp_head *sphead,
bool *new_memroot_allocated);
void put_back_item_params(THD *thd, LEX *lex,
const List<Item_param>& param_values);
};
/**
The class sp_instr_stmt represents almost all conventional SQL - statements .
*/
class sp_instr_stmt : public sp_lex_instr
{
sp_instr_stmt(const sp_instr_stmt &); /**< Prevent use of these */
void operator =(sp_instr_stmt &);
/**
Flag to tell whether a metadata this instruction depends on
has been changed and a LEX object should be reinitialized .
*/
bool m_valid;
LEX_STRING m_query; ///< For thd->query
public :
sp_instr_stmt(uint ip, sp_pcontext *ctx, LEX *lex, const LEX_STRING& query)
: sp_lex_instr(ip, ctx, lex, true ),
m_valid(true ),
m_query(query)
{}
virtual ~sp_instr_stmt() = default ;
int execute(THD *thd, uint *nextp) override;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
bool is_invalid() const override
{
return !m_valid;
}
void invalidate() override
{
m_valid= false ;
}
void get_query(String *sql_query) const override
{
sql_query->append(get_expr_query());
}
protected :
LEX_CSTRING get_expr_query() const override
{
return m_query;
}
bool on_after_expr_parsing(THD *) override
{
m_valid= true ;
return false ;
}
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_stmt : public sp_lex_instr
class sp_instr_set : public sp_lex_instr,
public sp_rcontext_addr
{
sp_instr_set(const sp_instr_set &); /**< Prevent use of these */
void operator =(sp_instr_set &);
public :
sp_instr_set(uint ip, sp_pcontext *ctx,
const Sp_rcontext_handler *rh,
uint offset, Item *val,
LEX *lex, bool lex_resp,
const LEX_CSTRING &expr_str)
: sp_lex_instr(ip, ctx, lex, lex_resp),
sp_rcontext_addr(rh, offset),
m_value(val),
m_expr_str(expr_str)
{}
virtual ~sp_instr_set() = default ;
int execute(THD *thd, uint *nextp) override;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
bool is_invalid() const override
{
return m_value == nullptr;
}
void invalidate() override
{
m_value= nullptr;
}
protected :
LEX_CSTRING get_expr_query() const override
{
return m_expr_str;
}
void adjust_sql_command(LEX *lex) override
{
DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT);
lex->sql_command= SQLCOM_SET_OPTION;
}
bool on_after_expr_parsing(THD *thd) override
{
DBUG_ASSERT(thd->lex->current_select->item_list.elements == 1 );
m_value= thd->lex->current_select->item_list.head();
DBUG_ASSERT(m_value != nullptr);
/*
In case there is a default value , update the dangling pointer
left after clean up of item before re - parsing of SP instruction
*/
sp_variable *spvar= m_ctx->find_variable(offset());
if (spvar->default_value)
spvar->default_value= m_value;
// Return error in release version if m_value == nullptr
return m_value == nullptr;
}
sp_rcontext *get_rcontext(THD *thd) const ;
Item *m_value;
private :
LEX_CSTRING m_expr_str;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_set : public sp_lex_instr
/*
An instruction to set a dynamic cursor placeholder value from an expression
in the USING clause in a script like this :
DECLARE c CURSOR FOR stmt ;
PREPARE c FROM ' SELECT ? ' ;
OPEN c USING 1 ;
or
OPEN c FOR ' SELECT ? ' USING 1 ;
*/
class sp_instr_set_ps_placeholder: public sp_lex_instr,
public sp_rcontext_ref,
public Type_handler_hybrid_field_type,
public Type_all_attributes,
public Type_extra_attributes
{
// Prevent use of these
sp_instr_set_ps_placeholder(const sp_instr_set_ps_placeholder &) = delete ;
void operator =(sp_instr_set_ps_placeholder &) = delete ;
public :
sp_instr_set_ps_placeholder(uint ip, sp_pcontext *ctx,
const sp_rcontext_ref &cursor_ref,
uint using_clause_offset,
sp_assignment_lex *lex)
:sp_lex_instr(ip, ctx, lex, true ),
sp_rcontext_ref(cursor_ref),
m_expr(lex->get_item()),
m_expr_str(lex->get_expr_str()),
m_using_clause_offset(using_clause_offset),
m_decimal_precision(0 )
{
// Only local cursors are supported until MDEV-36053 is done
DBUG_ASSERT((cursor_ref.rcontext_handler() == &sp_rcontext_handler_local &&
cursor_ref.deref_rcontext_handler() == nullptr) ||
cursor_ref.deref_rcontext_handler() ==
&sp_rcontext_handler_statement);
}
virtual ~sp_instr_set_ps_placeholder() = default ;
int execute(THD *thd, uint *nextp) override;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
bool is_invalid() const override
{
return m_expr == nullptr;
}
void invalidate() override
{
m_expr= nullptr;
}
bool save_in_param(THD *thd, Item_param *dst) const
{
DBUG_ASSERT(m_decimal_precision > 0 ); // Attributes were set
return dst->set_from_value(thd, m_value, type_handler(), *this );
}
uint using_clause_offset() const
{
return m_using_clause_offset;
}
void set_type_maybe_null(bool maybe_null_arg) override
{
// Nullability is not important
}
decimal_digits_t decimal_precision() const override
{
return m_decimal_precision;
}
Type_extra_attributes *type_extra_attributes_addr() override
{
return this ;
}
const Type_extra_attributes type_extra_attributes() const override
{
return *this ;
}
protected :
LEX_CSTRING get_expr_query() const override
{
return m_expr_str;
}
void adjust_sql_command(LEX *lex) override
{
DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT);
lex->sql_command= SQLCOM_SET_OPTION;
}
bool on_after_expr_parsing(THD *thd) override
{
DBUG_ASSERT(thd->lex->current_select->item_list.elements == 1 );
m_expr= thd->lex->current_select->item_list.head();
DBUG_ASSERT(m_expr != nullptr);
return m_expr == nullptr; // Error if nullptr
}
private :
Value m_value;
Item *m_expr;
LEX_CSTRING m_expr_str;
uint m_using_clause_offset;
decimal_digits_t m_decimal_precision;
public :
PSI_statement_info* get_psi_info() override
{
return & sp_instr_set::psi_info;
}
}; // class sp_instr_set_ps_placeholder
/*
This instr initializes parameters with default values
if it ' s parameter ' s spvar was not set by caller .
*/
class sp_instr_set_default_param : public sp_instr_set
{
/**< Prevent use of these */
sp_instr_set_default_param(const sp_instr_set_default_param &);
void operator =(sp_instr_set_default_param &);
public :
sp_instr_set_default_param(uint ip, sp_pcontext *ctx,
const Sp_rcontext_handler *rh,
uint offset, Item *val,
LEX *lex, bool lex_resp,
const LEX_CSTRING &expr_str)
: sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp, expr_str)
{}
virtual ~sp_instr_set_default_param() = default ;
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
};
/*
This class handles assignments of a ROW fields :
DECLARE rec ROW ( a INT , b INT ) ;
SET rec . a = 10 ;
*/
class sp_instr_set_row_field : public sp_instr_set
{
sp_instr_set_row_field(const sp_instr_set_row_field &); // Prevent use of this
void operator =(sp_instr_set_row_field &);
uint m_field_offset;
public :
sp_instr_set_row_field(uint ip, sp_pcontext *ctx,
const Sp_rcontext_handler *rh,
uint offset, uint field_offset,
Item *val,
LEX *lex, bool lex_resp,
const LEX_CSTRING &value_query)
: sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp, value_query),
m_field_offset(field_offset)
{}
virtual ~sp_instr_set_row_field() = default ;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
}; // class sp_instr_set_field : public sp_instr_set
/**
This class handles assignment instructions like this :
DECLARE
CURSOR cur IS SELECT * FROM t1 ;
rec cur % ROWTYPE ;
BEGIN
rec . column1 : = 10 ; - - This instruction
END ;
The idea is that during sp_rcontext : : create ( ) we do not know the extact
structure of " rec " . It gets resolved at run time , during the corresponding
sp_instr_cursor_copy_struct : : exec_core ( ) .
So sp_instr_set_composite_field_by_name searches for ROW fields by name ,
while sp_instr_set_row_field ( see above ) searches for ROW fields by index .
Additionally , this class is used for assignments of associative arrays
by key :
DECLARE
TYPE t IS TABLE OF rec_t INDEX BY VARCHAR2 ( 20 ) ;
arr t ;
BEGIN
arr ( ' key ' ) : = rec_t ( 10 , 20 ) ; - - This instruction
END ;
*/
class sp_instr_set_composite_field_by_name : public sp_instr_set
{
using SELF= sp_instr_set_composite_field_by_name;
sp_instr_set_composite_field_by_name(const SELF &);
void operator =(SELF &);
LEX_CSTRING m_field_name;
Item* m_key;
public :
sp_instr_set_composite_field_by_name(uint ip, sp_pcontext *ctx,
const Sp_rcontext_handler *rh,
uint offset,
const LEX_CSTRING &field_name,
Item *val,
LEX *lex, bool lex_resp,
const LEX_CSTRING &value_query)
: sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp, value_query),
m_field_name(field_name),
m_key(nullptr)
{}
sp_instr_set_composite_field_by_name(uint ip, sp_pcontext *ctx,
const sp_rcontext_addr &addr,
Item* key, Item *val,
LEX *lex, bool lex_resp,
const LEX_CSTRING &value_query)
: sp_instr_set(ip, ctx,
addr.rcontext_handler(),
addr.offset(), val, lex,
lex_resp, value_query),
m_key(key)
{}
virtual ~sp_instr_set_composite_field_by_name() = default ;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
}; // class sp_instr_set_composite_field_by_name : public sp_instr_set
/*
This class handles assignments of non scalar associative array ' s element
assignments .
DECLARE
TYPE t IS TABLE OF rec_t INDEX BY VARCHAR2 ( 20 ) ;
arr t ;
BEGIN
arr ( ' key ' ) : = rec_t ( 10 , 20 ) ;
arr ( ' key ' ) . field : = 30 ; - - This instruction
END ;
*/
class sp_instr_set_composite_field_by_key : public sp_instr_set
{
using SELF= sp_instr_set_composite_field_by_key;
sp_instr_set_composite_field_by_key(const SELF &);
void operator =(SELF &);
Item* m_key;
const LEX_CSTRING m_field_name;
public :
sp_instr_set_composite_field_by_key(uint ip, sp_pcontext *ctx,
const sp_rcontext_addr &addr,
Item* key,
const LEX_CSTRING &field_name,
Item *val,
LEX *lex, bool lex_resp,
const LEX_CSTRING &value_query)
: sp_instr_set(ip, ctx,
addr.rcontext_handler(),
addr.offset(), val, lex,
lex_resp, value_query),
m_key(key),
m_field_name(field_name)
{}
virtual ~sp_instr_set_composite_field_by_key() = default ;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
}; // class sp_instr_set_composite_field_by_key : public sp_instr_set
/**
Set NEW / OLD row field value instruction . Used in triggers .
*/
class sp_instr_set_trigger_field : public sp_lex_instr
{
sp_instr_set_trigger_field(const sp_instr_set_trigger_field &);
void operator =(sp_instr_set_trigger_field &);
public :
sp_instr_set_trigger_field(uint ip, sp_pcontext *ctx,
Item_trigger_field *trg_fld,
Item *val, LEX *lex,
const LEX_CSTRING &value_query)
: sp_lex_instr(ip, ctx, lex, true ),
trigger_field(trg_fld),
value(val),
m_expr_str(value_query)
{
m_trigger_field_name=
LEX_CSTRING{strdup_root(current_thd->mem_root, trg_fld->field_name.str),
trg_fld->field_name.length};
}
virtual ~sp_instr_set_trigger_field() = default ;
int execute(THD *thd, uint *nextp) override;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
bool is_invalid() const override
{
return value == nullptr;
}
void invalidate() override
{
value= nullptr;
}
protected :
LEX_CSTRING get_expr_query() const override
{
return m_expr_str;
}
bool on_after_expr_parsing(THD *thd) override;
private :
Item_trigger_field *trigger_field;
Item *value;
/**
SQL clause corresponding to the expression value .
*/
LEX_CSTRING m_expr_str;
LEX_CSTRING m_trigger_field_name;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_trigger_field : public sp_lex_instr
/**
Destruct a variable in the end of a BEGIN . . END block
*/
class sp_instr_destruct_variable: public sp_instr
{
public :
sp_instr_destruct_variable(uint ip, sp_pcontext *pctx, uint offset)
:sp_instr(ip, pctx),
m_offset(offset)
{ }
virtual ~sp_instr_destruct_variable() = default ;
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
uint offset() const { return m_offset; }
private :
uint m_offset;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
};
/**
An abstract class for all instructions with destinations that
needs to be updated by the optimizer .
Even if not all subclasses will use both the normal destination and
the continuation destination , we put them both here for simplicity .
*/
class sp_instr_opt_meta
{
public :
uint m_dest; ///< Where we will go
uint m_cont_dest; ///< Where continue handlers will go
explicit sp_instr_opt_meta(uint dest)
: m_dest(dest),
m_cont_dest(0 ),
m_optdest(0 ),
m_cont_optdest(0 )
{}
virtual ~sp_instr_opt_meta() = default ;
virtual void set_destination(uint old_dest, uint new_dest) = 0 ;
protected :
sp_instr *m_optdest; ///< Used during optimization
sp_instr *m_cont_optdest; ///< Used during optimization
}; // class sp_instr_opt_meta
class sp_instr_jump : public sp_instr, public sp_instr_opt_meta
{
sp_instr_jump(const sp_instr_jump &); /**< Prevent use of these */
void operator =(sp_instr_jump &);
public :
sp_instr_jump(uint ip, sp_pcontext *ctx)
: sp_instr(ip, ctx),
sp_instr_opt_meta(0 )
{}
sp_instr_jump(uint ip, sp_pcontext *ctx, uint dest)
: sp_instr(ip, ctx),
sp_instr_opt_meta(dest)
{}
virtual ~sp_instr_jump() = default ;
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
uint opt_mark(sp_head *sp, List<sp_instr> *leads) override;
uint opt_shortcut_jump(sp_head *sp, sp_instr *start) override;
void opt_move(uint dst, List<sp_instr_opt_meta> *ibp) override;
void backpatch(uint dest, sp_pcontext *dst_ctx) override
{
/* Calling backpatch twice is a logic flaw in jump resolution. */
DBUG_ASSERT(m_dest == 0 );
m_dest= dest;
}
uint get_cont_dest() const override
{
return m_cont_dest;
}
/**
Update the destination ; used by the optimizer .
*/
void set_destination(uint old_dest, uint new_dest) override
{
if (m_dest == old_dest)
m_dest= new_dest;
}
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_jump : public sp_instr, public sp_instr_opt_meta
class sp_instr_jump_if_not : public sp_lex_instr, public sp_instr_opt_meta
{
/**< Prevent use of these */
sp_instr_jump_if_not(const sp_instr_jump_if_not &);
void operator =(sp_instr_jump_if_not &);
public :
sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i, LEX *lex,
const LEX_CSTRING &expr_query)
: sp_lex_instr(ip, ctx, lex, true ),
sp_instr_opt_meta(0 ),
m_expr(i),
m_expr_str(expr_query)
{}
sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i, uint dest, LEX *lex,
const LEX_CSTRING &expr_query)
: sp_lex_instr(ip, ctx, lex, true ),
sp_instr_opt_meta(dest),
m_expr(i),
m_expr_str(expr_query)
{}
virtual ~sp_instr_jump_if_not() = default ;
int execute(THD *thd, uint *nextp) override;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
uint opt_mark(sp_head *sp, List<sp_instr> *leads) override;
/** Override sp_instr_jump's shortcut; we stop here */
uint opt_shortcut_jump(sp_head *sp, sp_instr *start) override
{
return m_ip;
}
void opt_move(uint dst, List<sp_instr_opt_meta> *ibp) override;
uint get_cont_dest() const override
{
return m_cont_dest;
}
void set_destination(uint old_dest, uint new_dest) override
{
if (m_dest == old_dest)
m_dest= new_dest;
if (m_cont_dest == old_dest)
m_cont_dest= new_dest;
}
void backpatch(uint dest, sp_pcontext *dst_ctx) override
{
/* Calling backpatch twice is a logic flaw in jump resolution. */
DBUG_ASSERT(m_dest == 0 );
m_dest= dest;
}
bool is_invalid() const override
{
return m_expr == nullptr;
}
void invalidate() override
{
m_expr= nullptr;
}
protected :
LEX_CSTRING get_expr_query() const override
{
return m_expr_str;
}
void adjust_sql_command(LEX *lex) override
{
assert(lex->sql_command == SQLCOM_SELECT);
lex->sql_command= SQLCOM_END;
}
bool on_after_expr_parsing(THD *thd) override
{
DBUG_ASSERT(thd->lex->current_select->item_list.elements == 1 );
m_expr= thd->lex->current_select->item_list.head();
DBUG_ASSERT(m_expr != nullptr);
// Return error in release version if m_expr == nullptr
return m_expr == nullptr;
}
private :
Item *m_expr; ///< The condition
LEX_CSTRING m_expr_str;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_jump_if_not
class sp_instr_preturn : public sp_instr
{
sp_instr_preturn(const sp_instr_preturn &); /**< Prevent use of these */
void operator =(sp_instr_preturn &);
public :
sp_instr_preturn(uint ip, sp_pcontext *ctx)
: sp_instr(ip, ctx)
{}
virtual ~sp_instr_preturn() = default ;
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
uint opt_mark(sp_head *sp, List<sp_instr> *leads) override
{
marked= 1 ;
return UINT_MAX;
}
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_preturn : public sp_instr
class sp_instr_freturn : public sp_lex_instr
{
sp_instr_freturn(const sp_instr_freturn &); /**< Prevent use of these */
void operator =(sp_instr_freturn &);
public :
sp_instr_freturn(uint ip, sp_pcontext *ctx,
Item *val, const Type_handler *handler, sp_expr_lex *lex)
: sp_lex_instr(ip, ctx, lex, true ),
m_value(val),
m_type_handler(handler),
m_expr_str(lex->get_expr_str())
{}
virtual ~sp_instr_freturn() = default ;
int execute(THD *thd, uint *nextp) override;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
uint opt_mark(sp_head *sp, List<sp_instr> *leads) override
{
marked= 1 ;
return UINT_MAX;
}
bool is_invalid() const override
{
return m_value == nullptr;
}
void invalidate() override
{
m_value= nullptr;
}
protected :
LEX_CSTRING get_expr_query() const override
{
return m_expr_str;
}
bool on_after_expr_parsing(THD *thd) override
{
DBUG_ASSERT(thd->lex->current_select->item_list.elements == 1 );
m_value= thd->lex->current_select->item_list.head();
DBUG_ASSERT(m_value != nullptr);
// Return error in release version if m_value == nullptr
return m_value == nullptr;
}
Item *m_value;
const Type_handler *m_type_handler;
private :
/**
SQL - query corresponding to the RETURN - expression .
*/
LEX_CSTRING m_expr_str;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_freturn : public sp_lex_instr
class sp_instr_hpush_jump : public sp_instr_jump
{
sp_instr_hpush_jump(const sp_instr_hpush_jump &); /**< Prevent use of these */
void operator =(sp_instr_hpush_jump &);
public :
sp_instr_hpush_jump(uint ip,
sp_pcontext *ctx,
sp_handler *handler)
: sp_instr_jump(ip, ctx),
m_handler(handler),
m_opt_hpop(0 ),
m_frame(ctx->current_var_count())
{
DBUG_ASSERT(m_handler->condition_values.elements == 0 );
}
~sp_instr_hpush_jump() override
{
m_handler->condition_values.empty();
m_handler= nullptr;
}
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
uint opt_mark(sp_head *sp, List<sp_instr> *leads) override;
/** Override sp_instr_jump's shortcut; we stop here. */
uint opt_shortcut_jump(sp_head *sp, sp_instr *start) override
{
return m_ip;
}
void backpatch(uint dest, sp_pcontext *dst_ctx) override
{
DBUG_ASSERT(!m_dest || !m_opt_hpop);
if (!m_dest)
m_dest= dest;
else
m_opt_hpop= dest;
}
void add_condition(sp_condition_value *condition_value)
{
m_handler->condition_values.push_back(condition_value);
}
sp_handler *get_handler()
{
return m_handler;
}
private :
/// Handler.
sp_handler *m_handler;
/// hpop marking end of handler scope.
uint m_opt_hpop;
// This attribute is needed for SHOW PROCEDURE CODE only (i.e. it's needed in
// debug version only). It's used in print().
uint m_frame;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_hpush_jump : public sp_instr_jump
class sp_instr_hpop : public sp_instr
{
sp_instr_hpop(const sp_instr_hpop &); /**< Prevent use of these */
void operator =(sp_instr_hpop &);
public :
sp_instr_hpop(uint ip, sp_pcontext *ctx, uint count)
: sp_instr(ip, ctx),
m_count(count)
{}
virtual ~sp_instr_hpop() = default ;
void update_count(uint count)
{
m_count= count;
}
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
private :
uint m_count;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_hpop : public sp_instr
class sp_instr_hreturn : public sp_instr_jump
{
sp_instr_hreturn(const sp_instr_hreturn &); /**< Prevent use of these */
void operator =(sp_instr_hreturn &);
public :
sp_instr_hreturn(uint ip, sp_pcontext *ctx)
: sp_instr_jump(ip, ctx),
m_frame(ctx->current_var_count())
{}
virtual ~sp_instr_hreturn() = default ;
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
/* This instruction will not be short cut optimized. */
uint opt_shortcut_jump(sp_head *sp, sp_instr *start) override
{
return m_ip;
}
uint opt_mark(sp_head *sp, List<sp_instr> *leads) override;
private :
uint m_frame;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_hreturn : public sp_instr_jump
/**
Get a query text associated with the cursor .
*/
static inline LEX_CSTRING get_cursor_query(const LEX_CSTRING &cursor_stmt)
{
/*
Lexer on processing the clause CURSOR FOR / CURSOR IS doesn ' t
move a pointer on cpp_buf after the token FOR / IS so skip it explicitly
in order to get correct value of cursor ' s query string .
*/
if (strncasecmp(cursor_stmt.str, "FOR" , 3 ) == 0 &&
my_isspace(current_thd->variables.character_set_client,
cursor_stmt.str[3 ]))
return LEX_CSTRING{cursor_stmt.str + 4 , cursor_stmt.length - 4 };
if (strncasecmp(cursor_stmt.str, "IS" , 2 ) == 0 &&
my_isspace(current_thd->variables.character_set_client,
cursor_stmt.str[2 ]))
return LEX_CSTRING{cursor_stmt.str + 3 , cursor_stmt.length - 3 };
return cursor_stmt;
}
/**
This is DECLARE CURSOR
*/
class sp_instr_cpush : public sp_lex_instr, public sp_cursor
{
sp_instr_cpush(const sp_instr_cpush &); /**< Prevent use of these */
void operator =(sp_instr_cpush &);
public :
sp_instr_cpush(uint ip, sp_pcontext *ctx, sp_lex_cursor *lex, uint offset)
: sp_lex_instr(ip, ctx, lex, true ),
m_cursor(offset),
m_metadata_changed(false ),
m_cursor_stmt(lex->get_expr_str())
{}
virtual ~sp_instr_cpush() = default ;
int execute(THD *thd, uint *nextp) override;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
/**
This call is used to cleanup the instruction when a sensitive
cursor is closed . For now stored procedures always use materialized
cursors and the call is not used .
*/
bool cleanup_stmt(bool /*restore_set_statement_vars*/) override
{
return false ;
}
bool is_invalid() const override
{
return m_metadata_changed;
}
void invalidate() override
{
m_metadata_changed= true ;
}
sp_lex_keeper *get_lex_keeper() override
{
return &m_lex_keeper;
}
void get_query(String *sql_query) const override
{
sql_query->append(get_expr_query());
}
sp_instr_cpush *get_push_instr() override { return this ; }
protected :
LEX_CSTRING get_expr_query() const override
{
return get_cursor_query(m_cursor_stmt);
}
bool on_after_expr_parsing(THD *) override
{
m_metadata_changed= false ;
return false ;
}
private :
uint m_cursor; /**< Frame offset (for debugging) */
/**
Flag if a statement ' s metadata has been changed in result of running DDL
on depending database objects used in the statement .
*/
bool m_metadata_changed;
LEX_CSTRING m_cursor_stmt;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_cpush : public sp_instr
class sp_instr_cpop : public sp_instr
{
sp_instr_cpop(const sp_instr_cpop &); /**< Prevent use of these */
void operator =(sp_instr_cpop &);
public :
sp_instr_cpop(uint ip, sp_pcontext *ctx, uint count)
: sp_instr(ip, ctx),
m_count(count)
{}
virtual ~sp_instr_cpop() = default ;
void update_count(uint count)
{
m_count= count;
}
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
private :
uint m_count;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_cpop : public sp_instr
class sp_instr_copen : public sp_instr
{
sp_instr_copen(const sp_instr_copen &); /**< Prevent use of these */
void operator =(sp_instr_copen &);
public :
sp_instr_copen(uint ip, sp_pcontext *ctx, uint c)
: sp_instr(ip, ctx),
m_cursor(c)
{}
virtual ~sp_instr_copen() = default ;
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
private :
uint m_cursor; ///< Stack index
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_copen : public sp_instr_stmt
/**
Initialize the structure of a cursor % ROWTYPE variable
from the LEX containing the cursor SELECT statement .
*/
class sp_instr_cursor_copy_struct: public sp_lex_instr
{
/**< Prevent use of these */
sp_instr_cursor_copy_struct(const sp_instr_cursor_copy_struct &);
void operator =(sp_instr_cursor_copy_struct &);
uint m_cursor;
uint m_var;
/**
Flag to tell whether metadata has been changed and the LEX object should
be reinitialized .
*/
bool m_valid;
LEX_CSTRING m_cursor_stmt;
/**
The pointer to an Item created on parsing the DEFAULT clause of
a cursor declaration . An instance of a class derived from the class Item
created for storing a value of the DEFAULT clause is created once and lives
until the cursor be deallocated on closing a stored routine .
*/
Item *m_def= nullptr;
public :
sp_instr_cursor_copy_struct(uint ip, sp_pcontext *ctx, uint coffs,
sp_lex_cursor *lex, uint voffs,
Item *def= nullptr)
: sp_lex_instr(ip, ctx, lex, false ),
m_cursor(coffs),
m_var(voffs),
m_valid(true ),
m_cursor_stmt(lex->get_expr_str()),
m_def(def)
{}
virtual ~sp_instr_cursor_copy_struct() override
{
if (m_def)
m_def->delete_self();
}
int execute(THD *thd, uint *nextp) override;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
bool is_invalid() const override
{
return !m_valid;
}
void invalidate() override
{
m_valid= false ;
}
void get_query(String *sql_query) const override
{
sql_query->append(get_expr_query());
}
protected :
LEX_CSTRING get_expr_query() const override
{
return get_cursor_query(m_cursor_stmt);
}
bool on_after_expr_parsing(THD *) override
{
m_valid= true ;
return false ;
}
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
};
class sp_instr_cclose : public sp_instr
{
sp_instr_cclose(const sp_instr_cclose &); /**< Prevent use of these */
void operator =(sp_instr_cclose &);
public :
sp_instr_cclose(uint ip, sp_pcontext *ctx, uint c)
: sp_instr(ip, ctx),
m_cursor(c)
{}
virtual ~sp_instr_cclose() = default ;
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
private :
uint m_cursor;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_cclose : public sp_instr
class sp_instr_fetch_cursor: public sp_instr
{
/**< Prevent use of these */
sp_instr_fetch_cursor(const sp_instr_fetch_cursor &) = delete ;
void operator =(sp_instr_fetch_cursor &) = delete ;
public :
sp_instr_fetch_cursor(uint ip, sp_pcontext *ctx,
const List<sp_fetch_target> &target_list,
bool error_on_no_data)
:sp_instr(ip, ctx),
m_fetch_target_list(target_list),
m_error_on_no_data(error_on_no_data)
{ }
protected :
List<sp_fetch_target> m_fetch_target_list;
bool m_error_on_no_data;
};
class sp_instr_cfetch : public sp_instr_fetch_cursor
{
sp_instr_cfetch(const sp_instr_cfetch &); /**< Prevent use of these */
void operator =(sp_instr_cfetch &);
public :
sp_instr_cfetch(uint ip, sp_pcontext *ctx, uint c,
const List<sp_fetch_target> &target_list,
bool error_on_no_data)
:sp_instr_fetch_cursor(ip, ctx, target_list, error_on_no_data),
m_cursor(c)
{ }
virtual ~sp_instr_cfetch() = default ;
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
private :
uint m_cursor;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_cfetch : public sp_instr
/*
This class is created for the special fetch instruction
FETCH GROUP NEXT ROW , used in the user - defined aggregate
functions
*/
class sp_instr_agg_cfetch : public sp_instr
{
sp_instr_agg_cfetch(const sp_instr_cfetch &); /**< Prevent use of these */
void operator =(sp_instr_cfetch &);
public :
sp_instr_agg_cfetch(uint ip, sp_pcontext *ctx)
: sp_instr(ip, ctx)
{}
virtual ~sp_instr_agg_cfetch() = default ;
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_agg_cfetch : public sp_instr
class sp_instr_copen_by_ref : public sp_lex_instr,
public sp_rcontext_ref
{
using SELF= sp_instr_copen_by_ref;
// Prevent use of these
sp_instr_copen_by_ref(const SELF &) = delete ;
void operator =(SELF &) = delete ;
public :
sp_instr_copen_by_ref(uint ip, sp_pcontext *ctx,
const Lex_ident_column &cursor_name,
const sp_rcontext_ref &ref,
sp_lex_cursor *lex,
uint set_ps_placeholder_count)
:sp_lex_instr(ip, ctx, lex, true ),
sp_rcontext_ref(ref),
m_cursor_name(cursor_name),
m_set_ps_placeholder_count(set_ps_placeholder_count),
m_metadata_changed(false ),
m_cursor_stmt(lex->get_expr_str())
{ }
virtual ~sp_instr_copen_by_ref() = default ;
int execute(THD *thd, uint *nextp) override;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
bool is_invalid() const override
{
return m_metadata_changed;
}
void invalidate() override
{
m_metadata_changed= true ;
}
bool on_after_expr_parsing(THD *) override
{
m_metadata_changed= false ;
return false ;
}
void get_query(String *sql_query) const override
{
sql_query->append(get_expr_query());
}
LEX_CSTRING get_expr_query() const override
{
/*
Lexer on processing the clause CURSOR FOR / CURSOR IS doesn ' t
move a pointer on cpp_buf after the token FOR / IS so skip it explicitly
in order to get correct value of cursor ' s query string .
Note , there is possibly a bug below : only the space character is tested
after FOR and IS . If a TAB or NL or CR character follows the keyword
then something can go wrong . Cannot check at the moment because of abother bug :
MDEV - 36079 Stored routine with a cursor crashes on the second execution . . .
*/
if (strncasecmp(m_cursor_stmt.str, "FOR " , 4 ) == 0 )
return LEX_CSTRING{m_cursor_stmt.str + 4 , m_cursor_stmt.length - 4 };
if (strncasecmp(m_cursor_stmt.str, "IS " , 3 ) == 0 )
return LEX_CSTRING{m_cursor_stmt.str + 3 , m_cursor_stmt.length - 3 };
return m_cursor_stmt;
}
private :
const Lex_ident_column &m_cursor_name;
/*
The number of sp_instr_set_ps_placeholder instructions
associated with this OPEN . . USING statement .
*/
uint m_set_ps_placeholder_count;
bool m_metadata_changed;
LEX_CSTRING m_cursor_stmt;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
};
class sp_instr_cclose_by_ref : public sp_instr,
public sp_rcontext_ref
{
using SELF= sp_instr_cclose_by_ref;
// Prevent use of these
sp_instr_cclose_by_ref(const SELF &) = delete ;
void operator =(SELF &) = delete ;
public :
sp_instr_cclose_by_ref(uint ip, sp_pcontext *ctx,
const sp_rcontext_ref &ref)
:sp_instr(ip, ctx),
sp_rcontext_ref(ref)
{ }
virtual ~sp_instr_cclose_by_ref() = default ;
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
};
class sp_instr_cfetch_by_ref : public sp_instr_fetch_cursor,
public sp_rcontext_ref
{
using SELF= sp_instr_cfetch_by_ref;
// Prevent use of these
sp_instr_cfetch_by_ref(const SELF &) = delete ;
void operator =(SELF &) = delete ;
public :
sp_instr_cfetch_by_ref(uint ip, sp_pcontext *ctx,
const sp_rcontext_ref &ref,
const List<sp_fetch_target> &target_list,
bool error_on_no_data)
:sp_instr_fetch_cursor(ip, ctx, target_list, error_on_no_data),
sp_rcontext_ref(ref)
{ }
virtual ~sp_instr_cfetch_by_ref() = default ;
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
};
class sp_instr_error : public sp_instr
{
sp_instr_error(const sp_instr_error &); /**< Prevent use of these */
void operator =(sp_instr_error &);
public :
sp_instr_error(uint ip, sp_pcontext *ctx, int errcode)
: sp_instr(ip, ctx),
m_errcode(errcode)
{}
virtual ~sp_instr_error() = default ;
int execute(THD *thd, uint *nextp) override;
void print(String *str) override;
uint opt_mark(sp_head *sp, List<sp_instr> *leads) override
{
marked= 1 ;
return UINT_MAX;
}
private :
int m_errcode;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_error : public sp_instr
class sp_instr_set_case_expr : public sp_lex_instr, public sp_instr_opt_meta
{
public :
sp_instr_set_case_expr(uint ip, sp_pcontext *ctx, uint case_expr_id,
Item *case_expr, LEX *lex,
const LEX_CSTRING &case_expr_query)
: sp_lex_instr(ip, ctx, lex, true ),
sp_instr_opt_meta(0 ),
m_case_expr_id(case_expr_id),
m_case_expr(case_expr),
m_expr_str(case_expr_query)
{}
virtual ~sp_instr_set_case_expr() = default ;
int execute(THD *thd, uint *nextp) override;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
uint opt_mark(sp_head *sp, List<sp_instr> *leads) override;
void opt_move(uint dst, List<sp_instr_opt_meta> *ibp) override;
uint get_cont_dest() const override
{
return m_cont_dest;
}
void set_destination(uint old_dest, uint new_dest) override
{
if (m_cont_dest == old_dest)
m_cont_dest= new_dest;
}
bool is_invalid() const override
{
return m_case_expr == nullptr;
}
void invalidate() override
{
m_case_expr= nullptr;
}
protected :
LEX_CSTRING get_expr_query() const override
{
return m_expr_str;
}
void adjust_sql_command(LEX *lex) override
{
assert(lex->sql_command == SQLCOM_SELECT);
lex->sql_command= SQLCOM_END;
}
bool on_after_expr_parsing(THD *thd) override
{
DBUG_ASSERT(thd->lex->current_select->item_list.elements == 1 );
m_case_expr= thd->lex->current_select->item_list.head();
DBUG_ASSERT(m_case_expr != nullptr);
// Return error in release version if m_case_expr == nullptr
return m_case_expr == nullptr;
}
private :
uint m_case_expr_id;
Item *m_case_expr;
LEX_CSTRING m_expr_str;
public :
PSI_statement_info* get_psi_info() override { return & psi_info; }
static PSI_statement_info psi_info;
}; // class sp_instr_set_case_expr : public sp_lex_instr,
#endif
Messung V0.5 in Prozent C=94 H=100 G=96
¤ Dauer der Verarbeitung: 0.47 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland