mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
SET sql_mode=ORACLE;
DELIMITER $$
CREATE OR REPLACE PACKAGE pkg AS
-- Declare a package public data type
TYPE varchar_array IS TABLE OF VARCHAR(2000) INDEX BY INTEGER;
END;
$$
DELIMITER ;
DELIMITER $$
CREATE OR REPLACE PROCEDURE p1 AS
v pkg.varchar_array; -- Use the package public data type
BEGIN
v(0):='test';
SELECT v(0);
END;
$$
DELIMITER ;
Note, the change is done only for sql_mode=ORACLE, because the TYPE
declaration is not available for the default mode.
Where package-wide types are available
--------------------------------------
- Variabe list type:
DECLARE var pkg1.type1;
- RETURN type for a package routine:
CREATE FUNCTION .. RETURN pkg1.type1 ...
- Parameter type for a package routine:
PROCEDURE p1(param1 pkg1.type1);
- Assoc array element type:
TYPE assoc1_t IS TABLE OF pkg1.type1 ...
- REF CURSOR RETURN type:
TYPE cur1_t IS REF CURSOR RETURN pkg1.type1;
Change details
--------------
- Adding a member Lex_length_and_dec_st::m_foreign_module_type
It's set to true when the data type was initialized from a TYPE
in foreign routine (e.g. in PACKAGE spec).
It's needed to prevent use of qualified identifiers in public contexts,
i.e. in schema public routine parameter types and schema publuc function
RETURN types.
Adding a helper method sp_head::check_applicability() which prevents
use of qualified types in public context.
- Adding a helper method sp_head::raise_unknown_data_type().
- Adding methods LEX::set_field_type_typedef_package_spec() for
2-step and 3-step qualified indentifiers.
It's used in field_type_all_with_typedefs which covers cases:
- Variabe list type : DECLARE var pkg1.type1;
- RETURN type : CREATE FUNCTION .. RETURN pkg1.type1 ...
- Parameter type : PROCEDURE p1(param1 pkg1.type1);
- Assoc array element type : TYPE assoc1_t IS TABLE OF pkg1.type1 ...
- Adding a method LEX::declare_type_ref_cursor_return_typedef().
It handles cases when a new TYPE REF CURSOR RETURN is declared,
for both for qualified RETURN types and non-qualified RETURN types:
- TYPE cur0_t IS REF CURSOR RETURN rec1_t;
- TYPE cur0_t IS REF CURSOR RETURN pkg1.rec1_t;
- TYPE cur0_t IS REF CURSOR RETURN db1.pkg1.rec1_t;
The code was moved from LEX::declare_type_ref_cursor() into
LEX::declare_type_ref_cursor_return_typedef() and extended
to cover qualified RETURN types.
- Adding a method Sql_path::find_package_spec_type().
It iterates through all schemas specified in @@path and searches
for the given type in the given package.
- Adding a helper method sp_pcontext::type_defs_add_ref_cursor()
to reuse the code.
- Adding a new method sp_package::get_typedef() to search
for TYPE definitions in PACKAGE specifications.
- Adding a new method sp_head::get_typedef_package_spec()
to search for TYPE definitions used by a PROCEDURE or FUNCTION.
- Adding a helper method
Sp_handler::sp_cache_routine_reentrant_suppress_errors
Adding a method Sp_handler::find_package_spec().
2158 lines
54 KiB
C++
2158 lines
54 KiB
C++
#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
|