summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Shulga <dmitry.shulga@mariadb.com>2023-03-16 18:10:26 +0700
committerDmitry Shulga <dmitry.shulga@mariadb.com>2023-03-16 18:40:27 +0700
commit21d47e68eccf622017fcd2aa511e9718ffeaba55 (patch)
tree59c16e4371f7fa297c016959c6b223db67fd690f
parent08ad5de7781178bf990a01f10c99ad8f7b41c485 (diff)
downloadmariadb-git-21d47e68eccf622017fcd2aa511e9718ffeaba55.tar.gz
MDEV-5816: Stored programs: validation of stored program statements
This is the prerequisite patch to move the sp_instr class and classes derived from it into the files sp_instr.cc/sp_instr.h. The classes sp_lex_cursor and sp_lex_keeper are also moved to the files files sp_instr.cc/sp_instr.h. Additionally, * all occurrences of macroses NULL, FALSE, TRUE are replaced with the corresponding C++ keywords nullptr, false, true. * the keyword 'override' is added in and the keyword 'virtual' is removed from signatures of every virtual method implemented in classes derived from the base class sp_instr. * the keyword 'final' is added into declaration of the class sp_lex_keeper since this class shouldn't have a derived class by design. * the function cmp_rqp_locations is made static since it is not called outside the file sp_instr.cc. * the function subst_spvars() is moved into the file sp_instr.cc since this function used only by the class sp_instr_stmt whose implementation is located in the file sp_instr.cc.
-rw-r--r--libmysqld/CMakeLists.txt1
-rw-r--r--sql/CMakeLists.txt1
-rw-r--r--sql/sp_head.cc1655
-rw-r--r--sql/sp_head.h1059
-rw-r--r--sql/sp_instr.cc1663
-rw-r--r--sql/sp_instr.h1107
-rw-r--r--sql/sp_rcontext.cc1
-rw-r--r--sql/sql_lex.cc1
-rw-r--r--sql/sql_yacc.yy1
9 files changed, 2776 insertions, 2713 deletions
diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt
index 5d5cc35e1be..6223fb781bd 100644
--- a/libmysqld/CMakeLists.txt
+++ b/libmysqld/CMakeLists.txt
@@ -148,6 +148,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
../sql/xa.cc
../sql/json_table.cc
../sql/opt_histogram_json.cc
+ ../sql/sp_instr.cc
${GEN_SOURCES}
${MYSYS_LIBWRAP_SOURCE}
)
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 4938f8da02b..a0e50cd99b3 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -163,6 +163,7 @@ SET (SQL_SOURCE
rpl_gtid.cc rpl_parallel.cc
semisync.cc semisync_master.cc semisync_slave.cc
semisync_master_ack_receiver.cc
+ sp_instr.cc
sql_schema.cc
lex_charset.cc
sql_type.cc sql_mode.cc sql_type_json.cc
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 35bdd4f9f1c..d3e7fe2ec5f 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -34,6 +34,7 @@
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation
#endif
+#include "sp_instr.h"
#include "sp_head.h"
#include "sp.h"
#include "sp_pcontext.h"
@@ -50,12 +51,6 @@
#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
-/*
- Sufficient max length of printed destinations and frame offsets (all uints).
-*/
-#define SP_INSTR_UINT_MAXLEN 8
-#define SP_STMT_PRINT_MAXLEN 40
-
#include <my_user.h>
#include "mysql/psi/mysql_statement.h"
#include "mysql/psi/mysql_sp.h"
@@ -1014,171 +1009,6 @@ sp_head::create_result_field(uint field_max_length, const LEX_CSTRING *field_nam
}
-int cmp_rqp_locations(Rewritable_query_parameter * const *a,
- Rewritable_query_parameter * const *b)
-{
- return (int)((*a)->pos_in_query - (*b)->pos_in_query);
-}
-
-
-/*
- StoredRoutinesBinlogging
- This paragraph applies only to statement-based binlogging. Row-based
- binlogging does not need anything special like this.
-
- Top-down overview:
-
- 1. Statements
-
- Statements that have is_update_query(stmt) == TRUE are written into the
- binary log verbatim.
- Examples:
- UPDATE tbl SET tbl.x = spfunc_w_side_effects()
- UPDATE tbl SET tbl.x=1 WHERE spfunc_w_side_effect_that_returns_false(tbl.y)
-
- Statements that have is_update_query(stmt) == FALSE (e.g. SELECTs) are not
- written into binary log. Instead we catch function calls the statement
- makes and write it into binary log separately (see #3).
-
- 2. PROCEDURE calls
-
- CALL statements are not written into binary log. Instead
- * Any FUNCTION invocation (in SET, IF, WHILE, OPEN CURSOR and other SP
- instructions) is written into binlog separately.
-
- * Each statement executed in SP is binlogged separately, according to rules
- in #1, with the exception that we modify query string: we replace uses
- of SP local variables with NAME_CONST('spvar_name', <spvar-value>) calls.
- This substitution is done in subst_spvars().
-
- 3. FUNCTION calls
-
- In sp_head::execute_function(), we check
- * If this function invocation is done from a statement that is written
- into the binary log.
- * If there were any attempts to write events to the binary log during
- function execution (grep for start_union_events and stop_union_events)
-
- If the answers are No and Yes, we write the function call into the binary
- log as "SELECT spfunc(<param1value>, <param2value>, ...)"
-
-
- 4. Miscellaneous issues.
-
- 4.1 User variables.
-
- When we call mysql_bin_log.write() for an SP statement, thd->user_var_events
- must hold set<{var_name, value}> pairs for all user variables used during
- the statement execution.
- This set is produced by tracking user variable reads during statement
- execution.
-
- For SPs, this has the following implications:
- 1) thd->user_var_events may contain events from several SP statements and
- needs to be valid after exection of these statements was finished. In
- order to achieve that, we
- * Allocate user_var_events array elements on appropriate mem_root (grep
- for user_var_events_alloc).
- * Use is_query_in_union() to determine if user_var_event is created.
-
- 2) We need to empty thd->user_var_events after we have wrote a function
- call. This is currently done by making
- reset_dynamic(&thd->user_var_events);
- calls in several different places. (TODO cosider moving this into
- mysql_bin_log.write() function)
-
- 4.2 Auto_increment storage in binlog
-
- As we may write two statements to binlog from one single logical statement
- (case of "SELECT func1(),func2()": it is binlogged as "SELECT func1()" and
- then "SELECT func2()"), we need to reset auto_increment binlog variables
- after each binlogged SELECT. Otherwise, the auto_increment value of the
- first SELECT would be used for the second too.
-*/
-
-
-/**
- Replace thd->query{_length} with a string that one can write to
- the binlog.
-
- The binlog-suitable string is produced by replacing references to SP local
- variables with NAME_CONST('sp_var_name', value) calls.
-
- @param thd Current thread.
- @param instr Instruction (we look for Item_splocal instances in
- instr->free_list)
- @param query_str Original query string
-
- @return
- - FALSE on success.
- thd->query{_length} either has been appropriately replaced or there
- is no need for replacements.
- - TRUE out of memory error.
-*/
-
-static bool
-subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
-{
- DBUG_ENTER("subst_spvars");
-
- Dynamic_array<Rewritable_query_parameter*> rewritables(PSI_INSTRUMENT_MEM);
- char *pbuf;
- StringBuffer<512> qbuf;
- Copy_query_with_rewrite acc(thd, query_str->str, query_str->length, &qbuf);
-
- /* Find rewritable Items used in this statement */
- for (Item *item= instr->free_list; item; item= item->next)
- {
- Rewritable_query_parameter *rqp= item->get_rewritable_query_parameter();
- if (rqp && rqp->pos_in_query)
- rewritables.append(rqp);
- }
- if (!rewritables.elements())
- DBUG_RETURN(FALSE);
-
- rewritables.sort(cmp_rqp_locations);
-
- thd->query_name_consts= (uint)rewritables.elements();
-
- for (Rewritable_query_parameter **rqp= rewritables.front();
- rqp <= rewritables.back(); rqp++)
- {
- if (acc.append(*rqp))
- DBUG_RETURN(TRUE);
- }
- if (acc.finalize())
- DBUG_RETURN(TRUE);
-
- /*
- Allocate additional space at the end of the new query string for the
- query_cache_send_result_to_client function.
-
- The query buffer layout is:
- buffer :==
- <statement> The input statement(s)
- '\0' Terminating null char
- <length> Length of following current database name 2
- <db_name> Name of current database
- <flags> Flags struct
- */
- size_t buf_len= (qbuf.length() + 1 + QUERY_CACHE_DB_LENGTH_SIZE +
- thd->db.length + QUERY_CACHE_FLAGS_SIZE + 1);
- if ((pbuf= (char *) alloc_root(thd->mem_root, buf_len)))
- {
- char *ptr= pbuf + qbuf.length();
- memcpy(pbuf, qbuf.ptr(), qbuf.length());
- *ptr= 0;
- int2store(ptr+1, thd->db.length);
- }
- else
- DBUG_RETURN(TRUE);
-
- thd->set_query(pbuf, qbuf.length());
-
- DBUG_RETURN(FALSE);
-}
-
-
void Sp_handler_procedure::recursion_level_error(THD *thd,
const sp_head *sp) const
{
@@ -3483,1489 +3313,6 @@ sp_head::show_routine_code(THD *thd)
#endif // ifndef DBUG_OFF
-/**
- Prepare LEX and thread for execution of instruction, if requested open
- and lock LEX's tables, execute instruction's core function, perform
- cleanup afterwards.
-
- @param thd thread context
- @param nextp out - next instruction
- @param open_tables if TRUE then 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 sp_instr instruction for which we prepare context, and which core
- function execute by calling its exec_core() method.
-
- @note
- We are not saving/restoring some parts of THD which may need this because
- we do this once for whole routine execution in sp_head::execute().
-
- @return
- 0/non-0 - Success/Failure
-*/
-
-int
-sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
- bool open_tables, sp_instr* instr)
-{
- int res= 0;
- DBUG_ENTER("reset_lex_and_exec_core");
-
- /*
- The flag is saved at the entry to the following substatement.
- It's reset further in the common code part.
- It's merged with the saved parent's value at the exit of this func.
- */
- bool parent_modified_non_trans_table=
- thd->transaction->stmt.modified_non_trans_table;
- unsigned int parent_unsafe_rollback_flags=
- thd->transaction->stmt.m_unsafe_rollback_flags;
- thd->transaction->stmt.modified_non_trans_table= FALSE;
- thd->transaction->stmt.m_unsafe_rollback_flags= 0;
-
- DBUG_ASSERT(!thd->derived_tables);
- DBUG_ASSERT(thd->Item_change_list::is_empty());
- /*
- Use our own lex.
- We should not save old value since it is saved/restored in
- sp_head::execute() when we are entering/leaving routine.
- */
- thd->lex= m_lex;
-
- thd->set_query_id(next_query_id());
-
- if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
- {
- /*
- This statement will enter/leave prelocked mode on its own.
- Entering prelocked mode changes table list and related members
- of LEX, so we'll need to restore them.
- */
- if (lex_query_tables_own_last)
- {
- /*
- We've already entered/left prelocked mode with this statement.
- Attach the list of tables that need to be prelocked and mark m_lex
- as having such list attached.
- */
- *lex_query_tables_own_last= prelocking_tables;
- m_lex->mark_as_requiring_prelocking(lex_query_tables_own_last);
- }
- }
-
- reinit_stmt_before_use(thd, m_lex);
-
-#ifndef EMBEDDED_LIBRARY
- /*
- If there was instruction which changed tracking state,
- the result of changed tracking state send to client in OK packed.
- So it changes result sent to client and probably can be different
- independent on query text. So we can't cache such results.
- */
- if ((thd->client_capabilities & CLIENT_SESSION_TRACK) &&
- (thd->server_status & SERVER_SESSION_STATE_CHANGED))
- thd->lex->safe_to_cache_query= 0;
-#endif
-
- Opt_trace_start ots(thd);
- ots.init(thd, m_lex->query_tables, SQLCOM_SELECT, &m_lex->var_list,
- NULL, 0, thd->variables.character_set_client);
-
- Json_writer_object trace_command(thd);
- Json_writer_array trace_command_steps(thd, "steps");
- if (open_tables)
- res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
-
- if (likely(!res))
- {
- res= instr->exec_core(thd, nextp);
- DBUG_PRINT("info",("exec_core returned: %d", res));
- }
-
- /*
- Call after unit->cleanup() to close open table
- key read.
- */
- if (open_tables)
- {
- m_lex->unit.cleanup();
- /* Here we also commit or rollback the current statement. */
- if (! thd->in_sub_stmt)
- {
- thd->get_stmt_da()->set_overwrite_status(true);
- thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
- thd->get_stmt_da()->set_overwrite_status(false);
- }
- close_thread_tables(thd);
- thd_proc_info(thd, 0);
-
- if (! thd->in_sub_stmt)
- {
- if (thd->transaction_rollback_request)
- {
- trans_rollback_implicit(thd);
- thd->release_transactional_locks();
- }
- else if (! thd->in_multi_stmt_transaction_mode())
- thd->release_transactional_locks();
- else
- thd->mdl_context.release_statement_locks();
- }
- }
- //TODO: why is this here if log_slow_query is in sp_instr_stmt::execute?
- delete_explain_query(m_lex);
-
- if (m_lex->query_tables_own_last)
- {
- /*
- We've entered and left prelocking mode when executing statement
- stored in m_lex.
- m_lex->query_tables(->next_global)* list now has a 'tail' - a list
- of tables that are added for prelocking. (If this is the first
- execution, the 'tail' was added by open_tables(), otherwise we've
- attached it above in this function).
- Now we'll save the 'tail', and detach it.
- */
- lex_query_tables_own_last= m_lex->query_tables_own_last;
- prelocking_tables= *lex_query_tables_own_last;
- *lex_query_tables_own_last= NULL;
- m_lex->query_tables_last= m_lex->query_tables_own_last;
- m_lex->mark_as_requiring_prelocking(NULL);
- }
- thd->rollback_item_tree_changes();
- /*
- Update the state of the active arena if no errors on
- open_tables stage.
- */
- if (likely(!res) || likely(!thd->is_error()))
- thd->stmt_arena->state= Query_arena::STMT_EXECUTED;
-
- /*
- Merge here with the saved parent's values
- what is needed from the substatement gained
- */
- thd->transaction->stmt.modified_non_trans_table |= parent_modified_non_trans_table;
- thd->transaction->stmt.m_unsafe_rollback_flags |= parent_unsafe_rollback_flags;
-
- TRANSACT_TRACKER(add_trx_state_from_thd(thd));
-
- /*
- Unlike for PS we should not call Item's destructors for newly created
- items after execution of each instruction in stored routine. This is
- because SP often create Item (like Item_int, Item_string etc...) when
- they want to store some value in local variable, pass return value and
- etc... So their life time should be longer than one instruction.
-
- cleanup_items() is called in sp_head::execute()
- */
- thd->lex->restore_set_statement_var();
- DBUG_RETURN(res || thd->is_error());
-}
-
-
-int sp_lex_keeper::cursor_reset_lex_and_exec_core(THD *thd, uint *nextp,
- bool open_tables,
- sp_instr *instr)
-{
- Query_arena *old_arena= thd->stmt_arena;
- /*
- Get the Query_arena from the cursor statement LEX, which contains
- the free_list of the query, so new items (if any) are stored in
- the right free_list, and we can cleanup after each cursor operation,
- e.g. open or cursor_copy_struct (for cursor%ROWTYPE variables).
- */
- thd->stmt_arena= m_lex->query_arena();
- int res= reset_lex_and_exec_core(thd, nextp, open_tables, instr);
- cleanup_items(thd->stmt_arena->free_list);
- thd->stmt_arena= old_arena;
- return res;
-}
-
-
-/*
- sp_instr class functions
-*/
-
-int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
-{
- int result;
-
- /*
- Check whenever we have access to tables for this statement
- and open and lock them before executing instructions core function.
- */
- if (thd->open_temporary_tables(tables) ||
- check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)
- || open_and_lock_tables(thd, tables, TRUE, 0))
- result= -1;
- else
- result= 0;
- /* Prepare all derived tables/views to catch possible errors. */
- if (!result)
- result= mysql_handle_derived(thd->lex, DT_PREPARE) ? -1 : 0;
-
- return result;
-}
-
-uint sp_instr::get_cont_dest() const
-{
- return (m_ip+1);
-}
-
-
-int sp_instr::exec_core(THD *thd, uint *nextp)
-{
- DBUG_ASSERT(0);
- return 0;
-}
-
-/*
- sp_instr_stmt class functions
-*/
-
-PSI_statement_info sp_instr_stmt::psi_info=
-{ 0, "stmt", 0};
-
-int
-sp_instr_stmt::execute(THD *thd, uint *nextp)
-{
- int res;
- bool save_enable_slow_log;
- const CSET_STRING query_backup= thd->query_string;
- Sub_statement_state backup_state;
- DBUG_ENTER("sp_instr_stmt::execute");
- DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command()));
-
- MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, m_query.str, static_cast<uint>(m_query.length));
-
-#if defined(ENABLED_PROFILING)
- /* This s-p instr is profilable and will be captured. */
- thd->profiling.set_query_source(m_query.str, m_query.length);
-#endif
-
- save_enable_slow_log= thd->enable_slow_log;
- thd->store_slow_query_state(&backup_state);
-
- if (!(res= alloc_query(thd, m_query.str, m_query.length)) &&
- !(res=subst_spvars(thd, this, &m_query)))
- {
- /*
- (the order of query cache and subst_spvars calls is irrelevant because
- queries with SP vars can't be cached)
- */
- general_log_write(thd, COM_QUERY, thd->query(), thd->query_length());
-
- if (query_cache_send_result_to_client(thd, thd->query(),
- thd->query_length()) <= 0)
- {
- thd->reset_slow_query_state();
- res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);
- bool log_slow= !res && thd->enable_slow_log;
-
- /* Finalize server status flags after executing a statement. */
- if (log_slow || thd->get_stmt_da()->is_eof())
- thd->update_server_status();
-
- if (thd->get_stmt_da()->is_eof())
- thd->protocol->end_statement();
-
- query_cache_end_of_result(thd);
-
- mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
- thd->get_stmt_da()->is_error() ?
- thd->get_stmt_da()->sql_errno() : 0,
- command_name[COM_QUERY].str);
-
- if (log_slow)
- log_slow_statement(thd);
-
- /*
- Restore enable_slow_log, that can be changed by a admin or call
- command
- */
- thd->enable_slow_log= save_enable_slow_log;
-
- /* Add the number of rows to thd for the 'call' statistics */
- thd->add_slow_query_state(&backup_state);
- }
- else
- {
- /* change statistics */
- enum_sql_command save_sql_command= thd->lex->sql_command;
- thd->lex->sql_command= SQLCOM_SELECT;
- status_var_increment(thd->status_var.com_stat[SQLCOM_SELECT]);
- thd->update_stats();
- thd->lex->sql_command= save_sql_command;
- *nextp= m_ip+1;
- }
- thd->set_query(query_backup);
- thd->query_name_consts= 0;
-
- if (likely(!thd->is_error()))
- {
- res= 0;
- thd->get_stmt_da()->reset_diagnostics_area();
- }
- }
-
- DBUG_RETURN(res || thd->is_error());
-}
-
-
-void
-sp_instr_stmt::print(String *str)
-{
- size_t i, len;
-
- /* stmt CMD "..." */
- if (str->reserve(SP_STMT_PRINT_MAXLEN+SP_INSTR_UINT_MAXLEN+8))
- return;
- str->qs_append(STRING_WITH_LEN("stmt "));
- str->qs_append((uint)m_lex_keeper.sql_command());
- str->qs_append(STRING_WITH_LEN(" \""));
- len= m_query.length;
- /*
- Print the query string (but not too much of it), just to indicate which
- statement it is.
- */
- if (len > SP_STMT_PRINT_MAXLEN)
- len= SP_STMT_PRINT_MAXLEN-3;
- /* Copy the query string and replace '\n' with ' ' in the process */
- for (i= 0 ; i < len ; i++)
- {
- char c= m_query.str[i];
- if (c == '\n')
- c= ' ';
- str->qs_append(c);
- }
- if (m_query.length > SP_STMT_PRINT_MAXLEN)
- str->qs_append(STRING_WITH_LEN("...")); /* Indicate truncated string */
- str->qs_append('"');
-}
-
-
-int
-sp_instr_stmt::exec_core(THD *thd, uint *nextp)
-{
- MYSQL_QUERY_EXEC_START(thd->query(),
- thd->thread_id,
- thd->get_db(),
- &thd->security_ctx->priv_user[0],
- (char *)thd->security_ctx->host_or_ip,
- 3);
- int res= mysql_execute_command(thd);
- MYSQL_QUERY_EXEC_DONE(res);
- *nextp= m_ip+1;
- return res;
-}
-
-
-/*
- sp_instr_set class functions
-*/
-
-PSI_statement_info sp_instr_set::psi_info=
-{ 0, "set", 0};
-
-int
-sp_instr_set::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_set::execute");
- DBUG_PRINT("info", ("offset: %u", m_offset));
-
- DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
-}
-
-
-sp_rcontext *sp_instr_set::get_rcontext(THD *thd) const
-{
- return m_rcontext_handler->get_rcontext(thd->spcont);
-}
-
-
-int
-sp_instr_set::exec_core(THD *thd, uint *nextp)
-{
- int res= get_rcontext(thd)->set_variable(thd, m_offset, &m_value);
- delete_explain_query(thd->lex);
- *nextp = m_ip+1;
- return res;
-}
-
-void
-sp_instr_set::print(String *str)
-{
- /* set name@offset ... */
- size_t rsrv = SP_INSTR_UINT_MAXLEN+6;
- sp_variable *var = m_ctx->find_variable(m_offset);
- const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
-
- /* 'var' should always be non-null, but just in case... */
- if (var)
- rsrv+= var->name.length + prefix->length;
- if (str->reserve(rsrv))
- return;
- str->qs_append(STRING_WITH_LEN("set "));
- str->qs_append(prefix->str, prefix->length);
- if (var)
- {
- str->qs_append(&var->name);
- str->qs_append('@');
- }
- str->qs_append(m_offset);
- str->qs_append(' ');
- m_value->print(str, enum_query_type(QT_ORDINARY |
- QT_ITEM_ORIGINAL_FUNC_NULLIF));
-}
-
-
-/*
- sp_instr_set_field class functions
-*/
-
-int
-sp_instr_set_row_field::exec_core(THD *thd, uint *nextp)
-{
- int res= get_rcontext(thd)->set_variable_row_field(thd, m_offset,
- m_field_offset,
- &m_value);
- delete_explain_query(thd->lex);
- *nextp= m_ip + 1;
- return res;
-}
-
-
-void
-sp_instr_set_row_field::print(String *str)
-{
- /* set name@offset[field_offset] ... */
- size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3;
- sp_variable *var= m_ctx->find_variable(m_offset);
- const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
- DBUG_ASSERT(var);
- DBUG_ASSERT(var->field_def.is_row());
- const Column_definition *def=
- var->field_def.row_field_definitions()->elem(m_field_offset);
- DBUG_ASSERT(def);
-
- rsrv+= var->name.length + def->field_name.length + prefix->length;
- if (str->reserve(rsrv))
- return;
- str->qs_append(STRING_WITH_LEN("set "));
- str->qs_append(prefix);
- str->qs_append(&var->name);
- str->qs_append('.');
- str->qs_append(&def->field_name);
- str->qs_append('@');
- str->qs_append(m_offset);
- str->qs_append('[');
- str->qs_append(m_field_offset);
- str->qs_append(']');
- str->qs_append(' ');
- m_value->print(str, enum_query_type(QT_ORDINARY |
- QT_ITEM_ORIGINAL_FUNC_NULLIF));
-}
-
-
-/*
- sp_instr_set_field_by_name class functions
-*/
-
-int
-sp_instr_set_row_field_by_name::exec_core(THD *thd, uint *nextp)
-{
- int res= get_rcontext(thd)->set_variable_row_field_by_name(thd, m_offset,
- m_field_name,
- &m_value);
- delete_explain_query(thd->lex);
- *nextp= m_ip + 1;
- return res;
-}
-
-
-void
-sp_instr_set_row_field_by_name::print(String *str)
-{
- /* set name.field@offset["field"] ... */
- size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2;
- sp_variable *var= m_ctx->find_variable(m_offset);
- const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
- DBUG_ASSERT(var);
- DBUG_ASSERT(var->field_def.is_table_rowtype_ref() ||
- var->field_def.is_cursor_rowtype_ref());
-
- rsrv+= var->name.length + 2 * m_field_name.length + prefix->length;
- if (str->reserve(rsrv))
- return;
- str->qs_append(STRING_WITH_LEN("set "));
- str->qs_append(prefix);
- str->qs_append(&var->name);
- str->qs_append('.');
- str->qs_append(&m_field_name);
- str->qs_append('@');
- str->qs_append(m_offset);
- str->qs_append("[\"",2);
- str->qs_append(&m_field_name);
- str->qs_append("\"]",2);
- str->qs_append(' ');
- m_value->print(str, enum_query_type(QT_ORDINARY |
- QT_ITEM_ORIGINAL_FUNC_NULLIF));
-}
-
-
-/*
- sp_instr_set_trigger_field class functions
-*/
-
-PSI_statement_info sp_instr_set_trigger_field::psi_info=
-{ 0, "set_trigger_field", 0};
-
-int
-sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_set_trigger_field::execute");
- thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
- DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
-}
-
-
-int
-sp_instr_set_trigger_field::exec_core(THD *thd, uint *nextp)
-{
- Abort_on_warning_instant_set aws(thd, thd->is_strict_mode() && !thd->lex->ignore);
- const int res= (trigger_field->set_value(thd, &value) ? -1 : 0);
- *nextp = m_ip+1;
- return res;
-}
-
-void
-sp_instr_set_trigger_field::print(String *str)
-{
- str->append(STRING_WITH_LEN("set_trigger_field "));
- trigger_field->print(str, enum_query_type(QT_ORDINARY |
- QT_ITEM_ORIGINAL_FUNC_NULLIF));
- str->append(STRING_WITH_LEN(":="));
- value->print(str, enum_query_type(QT_ORDINARY |
- QT_ITEM_ORIGINAL_FUNC_NULLIF));
-}
-
-/*
- sp_instr_opt_meta
-*/
-
-uint sp_instr_opt_meta::get_cont_dest() const
-{
- return m_cont_dest;
-}
-
-
-/*
- sp_instr_jump class functions
-*/
-
-PSI_statement_info sp_instr_jump::psi_info=
-{ 0, "jump", 0};
-
-int
-sp_instr_jump::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_jump::execute");
- DBUG_PRINT("info", ("destination: %u", m_dest));
-
- *nextp= m_dest;
- DBUG_RETURN(0);
-}
-
-void
-sp_instr_jump::print(String *str)
-{
- /* jump dest */
- if (str->reserve(SP_INSTR_UINT_MAXLEN+5))
- return;
- str->qs_append(STRING_WITH_LEN("jump "));
- str->qs_append(m_dest);
-}
-
-uint
-sp_instr_jump::opt_mark(sp_head *sp, List<sp_instr> *leads)
-{
- m_dest= opt_shortcut_jump(sp, this);
- if (m_dest != m_ip+1) /* Jumping to following instruction? */
- marked= 1;
- m_optdest= sp->get_instr(m_dest);
- return m_dest;
-}
-
-uint
-sp_instr_jump::opt_shortcut_jump(sp_head *sp, sp_instr *start)
-{
- uint dest= m_dest;
- sp_instr *i;
-
- while ((i= sp->get_instr(dest)))
- {
- uint ndest;
-
- if (start == i || this == i)
- break;
- ndest= i->opt_shortcut_jump(sp, start);
- if (ndest == dest)
- break;
- dest= ndest;
- }
- return dest;
-}
-
-void
-sp_instr_jump::opt_move(uint dst, List<sp_instr> *bp)
-{
- if (m_dest > m_ip)
- bp->push_back(this); // Forward
- else if (m_optdest)
- m_dest= m_optdest->m_ip; // Backward
- m_ip= dst;
-}
-
-
-/*
- sp_instr_jump_if_not class functions
-*/
-
-PSI_statement_info sp_instr_jump_if_not::psi_info=
-{ 0, "jump_if_not", 0};
-
-int
-sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_jump_if_not::execute");
- DBUG_PRINT("info", ("destination: %u", m_dest));
- DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
-}
-
-
-int
-sp_instr_jump_if_not::exec_core(THD *thd, uint *nextp)
-{
- Item *it;
- int res;
-
- it= thd->sp_prepare_func_item(&m_expr, 1);
- if (! it)
- {
- res= -1;
- }
- else
- {
- res= 0;
- if (! it->val_bool())
- *nextp = m_dest;
- else
- *nextp = m_ip+1;
- }
-
- return res;
-}
-
-
-void
-sp_instr_jump_if_not::print(String *str)
-{
- /* jump_if_not dest(cont) ... */
- if (str->reserve(2*SP_INSTR_UINT_MAXLEN+14+32)) // Add some for the expr. too
- return;
- str->qs_append(STRING_WITH_LEN("jump_if_not "));
- str->qs_append(m_dest);
- str->qs_append('(');
- str->qs_append(m_cont_dest);
- str->qs_append(STRING_WITH_LEN(") "));
- m_expr->print(str, enum_query_type(QT_ORDINARY |
- QT_ITEM_ORIGINAL_FUNC_NULLIF));
-}
-
-
-uint
-sp_instr_jump_if_not::opt_mark(sp_head *sp, List<sp_instr> *leads)
-{
- sp_instr *i;
-
- marked= 1;
- if ((i= sp->get_instr(m_dest)))
- {
- m_dest= i->opt_shortcut_jump(sp, this);
- m_optdest= sp->get_instr(m_dest);
- }
- sp->add_mark_lead(m_dest, leads);
- if ((i= sp->get_instr(m_cont_dest)))
- {
- m_cont_dest= i->opt_shortcut_jump(sp, this);
- m_cont_optdest= sp->get_instr(m_cont_dest);
- }
- sp->add_mark_lead(m_cont_dest, leads);
- return m_ip+1;
-}
-
-void
-sp_instr_jump_if_not::opt_move(uint dst, List<sp_instr> *bp)
-{
- /*
- cont. destinations may point backwards after shortcutting jumps
- during the mark phase. If it's still pointing forwards, only
- push this for backpatching if sp_instr_jump::opt_move() will not
- do it (i.e. if the m_dest points backwards).
- */
- if (m_cont_dest > m_ip)
- { // Forward
- if (m_dest < m_ip)
- bp->push_back(this);
- }
- else if (m_cont_optdest)
- m_cont_dest= m_cont_optdest->m_ip; // Backward
- /* This will take care of m_dest and m_ip */
- sp_instr_jump::opt_move(dst, bp);
-}
-
-
-/*
- sp_instr_freturn class functions
-*/
-
-PSI_statement_info sp_instr_freturn::psi_info=
-{ 0, "freturn", 0};
-
-int
-sp_instr_freturn::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_freturn::execute");
- DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
-}
-
-
-int
-sp_instr_freturn::exec_core(THD *thd, uint *nextp)
-{
- /*
- RETURN is a "procedure statement" (in terms of the SQL standard).
- That means, Diagnostics Area should be clean before its execution.
- */
-
- if (!(thd->variables.sql_mode & MODE_ORACLE))
- {
- /*
- Don't clean warnings in ORACLE mode,
- as they are needed for SQLCODE and SQLERRM:
- BEGIN
- SELECT a INTO a FROM t1;
- RETURN 'No exception ' || SQLCODE || ' ' || SQLERRM;
- EXCEPTION WHEN NO_DATA_FOUND THEN
- RETURN 'Exception ' || SQLCODE || ' ' || SQLERRM;
- END;
- */
- Diagnostics_area *da= thd->get_stmt_da();
- da->clear_warning_info(da->warning_info_id());
- }
-
- /*
- Change <next instruction pointer>, so that this will be the last
- instruction in the stored function.
- */
-
- *nextp= UINT_MAX;
-
- /*
- Evaluate the value of return expression and store it in current runtime
- context.
-
- NOTE: It's necessary to evaluate result item right here, because we must
- do it in scope of execution the current context/block.
- */
-
- return thd->spcont->set_return_value(thd, &m_value);
-}
-
-void
-sp_instr_freturn::print(String *str)
-{
- /* freturn type expr... */
- if (str->reserve(1024+8+32)) // Add some for the expr. too
- return;
- str->qs_append(STRING_WITH_LEN("freturn "));
- LEX_CSTRING name= m_type_handler->name().lex_cstring();
- str->qs_append(&name);
- str->qs_append(' ');
- m_value->print(str, enum_query_type(QT_ORDINARY |
- QT_ITEM_ORIGINAL_FUNC_NULLIF));
-}
-
-/*
- sp_instr_preturn class functions
-*/
-
-PSI_statement_info sp_instr_preturn::psi_info=
-{ 0, "preturn", 0};
-
-int
-sp_instr_preturn::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_preturn::execute");
- *nextp= UINT_MAX;
- DBUG_RETURN(0);
-}
-
-void
-sp_instr_preturn::print(String *str)
-{
- str->append(STRING_WITH_LEN("preturn"));
-}
-
-/*
- sp_instr_hpush_jump class functions
-*/
-
-PSI_statement_info sp_instr_hpush_jump::psi_info=
-{ 0, "hpush_jump", 0};
-
-int
-sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_hpush_jump::execute");
-
- int ret= thd->spcont->push_handler(this);
-
- *nextp= m_dest;
-
- DBUG_RETURN(ret);
-}
-
-
-void
-sp_instr_hpush_jump::print(String *str)
-{
- /* hpush_jump dest fsize type */
- if (str->reserve(SP_INSTR_UINT_MAXLEN*2 + 21))
- return;
-
- str->qs_append(STRING_WITH_LEN("hpush_jump "));
- str->qs_append(m_dest);
- str->qs_append(' ');
- str->qs_append(m_frame);
-
- switch (m_handler->type) {
- case sp_handler::EXIT:
- str->qs_append(STRING_WITH_LEN(" EXIT"));
- break;
- case sp_handler::CONTINUE:
- str->qs_append(STRING_WITH_LEN(" CONTINUE"));
- break;
- default:
- // The handler type must be either CONTINUE or EXIT.
- DBUG_ASSERT(0);
- }
-}
-
-
-uint
-sp_instr_hpush_jump::opt_mark(sp_head *sp, List<sp_instr> *leads)
-{
- sp_instr *i;
-
- marked= 1;
- if ((i= sp->get_instr(m_dest)))
- {
- m_dest= i->opt_shortcut_jump(sp, this);
- m_optdest= sp->get_instr(m_dest);
- }
- sp->add_mark_lead(m_dest, leads);
-
- /*
- For continue handlers, all instructions in the scope of the handler
- are possible leads. For example, the instruction after freturn might
- be executed if the freturn triggers the condition handled by the
- continue handler.
-
- m_dest marks the start of the handler scope. It's added as a lead
- above, so we start on m_dest+1 here.
- m_opt_hpop is the hpop marking the end of the handler scope.
- */
- if (m_handler->type == sp_handler::CONTINUE)
- {
- for (uint scope_ip= m_dest+1; scope_ip <= m_opt_hpop; scope_ip++)
- sp->add_mark_lead(scope_ip, leads);
- }
-
- return m_ip+1;
-}
-
-
-/*
- sp_instr_hpop class functions
-*/
-
-PSI_statement_info sp_instr_hpop::psi_info=
-{ 0, "hpop", 0};
-
-int
-sp_instr_hpop::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_hpop::execute");
- thd->spcont->pop_handlers(m_count);
- *nextp= m_ip+1;
- DBUG_RETURN(0);
-}
-
-void
-sp_instr_hpop::print(String *str)
-{
- /* hpop count */
- if (str->reserve(SP_INSTR_UINT_MAXLEN+5))
- return;
- str->qs_append(STRING_WITH_LEN("hpop "));
- str->qs_append(m_count);
-}
-
-
-/*
- sp_instr_hreturn class functions
-*/
-
-PSI_statement_info sp_instr_hreturn::psi_info=
-{ 0, "hreturn", 0};
-
-int
-sp_instr_hreturn::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_hreturn::execute");
-
- uint continue_ip= thd->spcont->exit_handler(thd->get_stmt_da());
-
- *nextp= m_dest ? m_dest : continue_ip;
-
- DBUG_RETURN(0);
-}
-
-
-void
-sp_instr_hreturn::print(String *str)
-{
- /* hreturn framesize dest */
- if (str->reserve(SP_INSTR_UINT_MAXLEN*2 + 9))
- return;
- str->qs_append(STRING_WITH_LEN("hreturn "));
- if (m_dest)
- {
- // NOTE: this is legacy: hreturn instruction for EXIT handler
- // should print out 0 as frame index.
- str->qs_append(STRING_WITH_LEN("0 "));
- str->qs_append(m_dest);
- }
- else
- {
- str->qs_append(m_frame);
- }
-}
-
-
-uint
-sp_instr_hreturn::opt_mark(sp_head *sp, List<sp_instr> *leads)
-{
- marked= 1;
-
- if (m_dest)
- {
- /*
- This is an EXIT handler; next instruction step is in m_dest.
- */
- return m_dest;
- }
-
- /*
- This is a CONTINUE handler; next instruction step will come from
- the handler stack and not from opt_mark.
- */
- return UINT_MAX;
-}
-
-
-/*
- sp_instr_cpush class functions
-*/
-
-PSI_statement_info sp_instr_cpush::psi_info=
-{ 0, "cpush", 0};
-
-int
-sp_instr_cpush::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_cpush::execute");
-
- sp_cursor::reset(thd, &m_lex_keeper);
- m_lex_keeper.disable_query_cache();
- thd->spcont->push_cursor(this);
-
- *nextp= m_ip+1;
-
- DBUG_RETURN(false);
-}
-
-
-void
-sp_instr_cpush::print(String *str)
-{
- const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
-
- /* cpush name@offset */
- size_t rsrv= SP_INSTR_UINT_MAXLEN+7;
-
- if (cursor_name)
- rsrv+= cursor_name->length;
- if (str->reserve(rsrv))
- return;
- str->qs_append(STRING_WITH_LEN("cpush "));
- if (cursor_name)
- {
- str->qs_append(cursor_name->str, cursor_name->length);
- str->qs_append('@');
- }
- str->qs_append(m_cursor);
-}
-
-
-/*
- sp_instr_cpop class functions
-*/
-
-PSI_statement_info sp_instr_cpop::psi_info=
-{ 0, "cpop", 0};
-
-int
-sp_instr_cpop::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_cpop::execute");
- thd->spcont->pop_cursors(thd, m_count);
- *nextp= m_ip+1;
- DBUG_RETURN(0);
-}
-
-
-void
-sp_instr_cpop::print(String *str)
-{
- /* cpop count */
- if (str->reserve(SP_INSTR_UINT_MAXLEN+5))
- return;
- str->qs_append(STRING_WITH_LEN("cpop "));
- str->qs_append(m_count);
-}
-
-
-/*
- sp_instr_copen class functions
-*/
-
-/**
- @todo
- Assert that we either have an error or a cursor
-*/
-
-PSI_statement_info sp_instr_copen::psi_info=
-{ 0, "copen", 0};
-
-int
-sp_instr_copen::execute(THD *thd, uint *nextp)
-{
- /*
- We don't store a pointer to the cursor in the instruction to be
- able to reuse the same instruction among different threads in future.
- */
- sp_cursor *c= thd->spcont->get_cursor(m_cursor);
- int res;
- DBUG_ENTER("sp_instr_copen::execute");
-
- if (! c)
- res= -1;
- else
- {
- sp_lex_keeper *lex_keeper= c->get_lex_keeper();
- res= lex_keeper->cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this);
- /* TODO: Assert here that we either have an error or a cursor */
- }
- DBUG_RETURN(res);
-}
-
-
-int
-sp_instr_copen::exec_core(THD *thd, uint *nextp)
-{
- sp_cursor *c= thd->spcont->get_cursor(m_cursor);
- int res= c->open(thd);
- *nextp= m_ip+1;
- return res;
-}
-
-void
-sp_instr_copen::print(String *str)
-{
- const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
-
- /* copen name@offset */
- size_t rsrv= SP_INSTR_UINT_MAXLEN+7;
-
- if (cursor_name)
- rsrv+= cursor_name->length;
- if (str->reserve(rsrv))
- return;
- str->qs_append(STRING_WITH_LEN("copen "));
- if (cursor_name)
- {
- str->qs_append(cursor_name->str, cursor_name->length);
- str->qs_append('@');
- }
- str->qs_append(m_cursor);
-}
-
-
-/*
- sp_instr_cclose class functions
-*/
-
-PSI_statement_info sp_instr_cclose::psi_info=
-{ 0, "cclose", 0};
-
-int
-sp_instr_cclose::execute(THD *thd, uint *nextp)
-{
- sp_cursor *c= thd->spcont->get_cursor(m_cursor);
- int res;
- DBUG_ENTER("sp_instr_cclose::execute");
-
- if (! c)
- res= -1;
- else
- res= c->close(thd);
- *nextp= m_ip+1;
- DBUG_RETURN(res);
-}
-
-
-void
-sp_instr_cclose::print(String *str)
-{
- const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
-
- /* cclose name@offset */
- size_t rsrv= SP_INSTR_UINT_MAXLEN+8;
-
- if (cursor_name)
- rsrv+= cursor_name->length;
- if (str->reserve(rsrv))
- return;
- str->qs_append(STRING_WITH_LEN("cclose "));
- if (cursor_name)
- {
- str->qs_append(cursor_name->str, cursor_name->length);
- str->qs_append('@');
- }
- str->qs_append(m_cursor);
-}
-
-
-/*
- sp_instr_cfetch class functions
-*/
-
-PSI_statement_info sp_instr_cfetch::psi_info=
-{ 0, "cfetch", 0};
-
-int
-sp_instr_cfetch::execute(THD *thd, uint *nextp)
-{
- sp_cursor *c= thd->spcont->get_cursor(m_cursor);
- int res;
- Query_arena backup_arena;
- DBUG_ENTER("sp_instr_cfetch::execute");
-
- res= c ? c->fetch(thd, &m_varlist, m_error_on_no_data) : -1;
-
- *nextp= m_ip+1;
- DBUG_RETURN(res);
-}
-
-
-void
-sp_instr_cfetch::print(String *str)
-{
- List_iterator_fast<sp_variable> li(m_varlist);
- sp_variable *pv;
- const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
-
- /* cfetch name@offset vars... */
- size_t rsrv= SP_INSTR_UINT_MAXLEN+8;
-
- if (cursor_name)
- rsrv+= cursor_name->length;
- if (str->reserve(rsrv))
- return;
- str->qs_append(STRING_WITH_LEN("cfetch "));
- if (cursor_name)
- {
- str->qs_append(cursor_name->str, cursor_name->length);
- str->qs_append('@');
- }
- str->qs_append(m_cursor);
- while ((pv= li++))
- {
- if (str->reserve(pv->name.length+SP_INSTR_UINT_MAXLEN+2))
- return;
- str->qs_append(' ');
- str->qs_append(&pv->name);
- str->qs_append('@');
- str->qs_append(pv->offset);
- }
-}
-
-/*
- sp_instr_agg_cfetch class functions
-*/
-
-PSI_statement_info sp_instr_agg_cfetch::psi_info=
-{ 0, "agg_cfetch", 0};
-
-int
-sp_instr_agg_cfetch::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_agg_cfetch::execute");
- int res= 0;
- if (!thd->spcont->instr_ptr)
- {
- *nextp= m_ip+1;
- thd->spcont->instr_ptr= m_ip + 1;
- }
- else if (!thd->spcont->pause_state)
- thd->spcont->pause_state= TRUE;
- else
- {
- thd->spcont->pause_state= FALSE;
- if (thd->server_status & SERVER_STATUS_LAST_ROW_SENT)
- {
- my_message(ER_SP_FETCH_NO_DATA,
- ER_THD(thd, ER_SP_FETCH_NO_DATA), MYF(0));
- res= -1;
- thd->spcont->quit_func= TRUE;
- }
- else
- *nextp= m_ip + 1;
- }
- DBUG_RETURN(res);
-}
-
-void
-sp_instr_agg_cfetch::print(String *str)
-{
-
- uint rsrv= SP_INSTR_UINT_MAXLEN+11;
-
- if (str->reserve(rsrv))
- return;
- str->qs_append(STRING_WITH_LEN("agg_cfetch"));
-}
-
-/*
- sp_instr_cursor_copy_struct class functions
-*/
-
-/**
- This methods processes cursor %ROWTYPE declarations, e.g.:
- CURSOR cur IS SELECT * FROM t1;
- rec cur%ROWTYPE;
- and does the following:
- - opens the cursor without copying data (materialization).
- - copies the cursor structure to the associated %ROWTYPE variable.
-*/
-
-PSI_statement_info sp_instr_cursor_copy_struct::psi_info=
-{ 0, "cursor_copy_struct", 0};
-
-int
-sp_instr_cursor_copy_struct::exec_core(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_cursor_copy_struct::exec_core");
- int ret= 0;
- Item_field_row *row= (Item_field_row*) thd->spcont->get_variable(m_var);
- DBUG_ASSERT(row->type_handler() == &type_handler_row);
-
- /*
- Copy structure only once. If the cursor%ROWTYPE variable is declared
- inside a LOOP block, it gets its structure on the first loop interation
- and remembers the structure for all consequent loop iterations.
- It we recreated the structure on every iteration, we would get
- potential memory leaks, and it would be less efficient.
- */
- if (!row->arguments())
- {
- sp_cursor tmp(thd, &m_lex_keeper, true);
- // Open the cursor without copying data
- if (!(ret= tmp.open(thd)))
- {
- Row_definition_list defs;
- /*
- Create row elements on the caller arena.
- It's the same arena that was used during sp_rcontext::create().
- This puts cursor%ROWTYPE elements on the same mem_root
- where explicit ROW elements and table%ROWTYPE reside:
- - tmp.export_structure() allocates new Spvar_definition instances
- and their components (such as TYPELIBs).
- - row->row_create_items() creates new Item_field instances.
- They all are created on the same mem_root.
- */
- Query_arena current_arena;
- thd->set_n_backup_active_arena(thd->spcont->callers_arena, &current_arena);
- if (!(ret= tmp.export_structure(thd, &defs)))
- row->row_create_items(thd, &defs);
- thd->restore_active_arena(thd->spcont->callers_arena, &current_arena);
- tmp.close(thd);
- }
- }
- *nextp= m_ip + 1;
- DBUG_RETURN(ret);
-}
-
-
-int
-sp_instr_cursor_copy_struct::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_cursor_copy_struct::execute");
- int ret= m_lex_keeper.cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this);
- DBUG_RETURN(ret);
-}
-
-
-void
-sp_instr_cursor_copy_struct::print(String *str)
-{
- sp_variable *var= m_ctx->find_variable(m_var);
- const LEX_CSTRING *name= m_ctx->find_cursor(m_cursor);
- str->append(STRING_WITH_LEN("cursor_copy_struct "));
- str->append(name);
- str->append(' ');
- str->append(&var->name);
- str->append('@');
- str->append_ulonglong(m_var);
-}
-
-
-/*
- sp_instr_error class functions
-*/
-
-PSI_statement_info sp_instr_error::psi_info=
-{ 0, "error", 0};
-
-int
-sp_instr_error::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_error::execute");
- my_message(m_errcode, ER_THD(thd, m_errcode), MYF(0));
- WSREP_DEBUG("sp_instr_error: %s %d", ER_THD(thd, m_errcode), thd->is_error());
- *nextp= m_ip+1;
- DBUG_RETURN(-1);
-}
-
-
-void
-sp_instr_error::print(String *str)
-{
- /* error code */
- if (str->reserve(SP_INSTR_UINT_MAXLEN+6))
- return;
- str->qs_append(STRING_WITH_LEN("error "));
- str->qs_append(m_errcode);
-}
-
-
-/**************************************************************************
- sp_instr_set_case_expr class implementation
-**************************************************************************/
-
-PSI_statement_info sp_instr_set_case_expr::psi_info=
-{ 0, "set_case_expr", 0};
-
-int
-sp_instr_set_case_expr::execute(THD *thd, uint *nextp)
-{
- DBUG_ENTER("sp_instr_set_case_expr::execute");
-
- DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
-}
-
-
-int
-sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp)
-{
- int res= thd->spcont->set_case_expr(thd, m_case_expr_id, &m_case_expr);
-
- if (res && !thd->spcont->get_case_expr(m_case_expr_id))
- {
- /*
- Failed to evaluate the value, the case expression is still not
- initialized. Set to NULL so we can continue.
- */
-
- Item *null_item= new (thd->mem_root) Item_null(thd);
-
- if (!null_item ||
- thd->spcont->set_case_expr(thd, m_case_expr_id, &null_item))
- {
- /* If this also failed, we have to abort. */
- my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL));
- }
- }
- else
- *nextp= m_ip+1;
-
- return res;
-}
-
-
-void
-sp_instr_set_case_expr::print(String *str)
-{
- /* set_case_expr (cont) id ... */
- str->reserve(2*SP_INSTR_UINT_MAXLEN+18+32); // Add some extra for expr too
- str->qs_append(STRING_WITH_LEN("set_case_expr ("));
- str->qs_append(m_cont_dest);
- str->qs_append(STRING_WITH_LEN(") "));
- str->qs_append(m_case_expr_id);
- str->qs_append(' ');
- m_case_expr->print(str, enum_query_type(QT_ORDINARY |
- QT_ITEM_ORIGINAL_FUNC_NULLIF));
-}
-
-uint
-sp_instr_set_case_expr::opt_mark(sp_head *sp, List<sp_instr> *leads)
-{
- sp_instr *i;
-
- marked= 1;
- if ((i= sp->get_instr(m_cont_dest)))
- {
- m_cont_dest= i->opt_shortcut_jump(sp, this);
- m_cont_optdest= sp->get_instr(m_cont_dest);
- }
- sp->add_mark_lead(m_cont_dest, leads);
- return m_ip+1;
-}
-
-void
-sp_instr_set_case_expr::opt_move(uint dst, List<sp_instr> *bp)
-{
- if (m_cont_dest > m_ip)
- bp->push_back(this); // Forward
- else if (m_cont_optdest)
- m_cont_dest= m_cont_optdest->m_ip; // Backward
- m_ip= dst;
-}
-
-
/* ------------------------------------------------------------------ */
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 5b4aa36e518..1f53520adfe 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -1057,1065 +1057,6 @@ public:
};
-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)
- { }
- 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)
- { }
- ~sp_lex_cursor() { free_items(); }
- virtual 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);
- 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= NULL;
- return false;
- }
-};
-
-
-//
-// "Instructions"...
-//
-
-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)
- {}
-
- 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;
-
- 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> *ibp)
- {
- m_ip= dst;
- }
- virtual PSI_statement_info* get_psi_info() = 0;
-
-}; // class sp_instr : public Sql_alloc
-
-
-/**
- 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
-{
- /** 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),
- lex_query_tables_own_last(NULL)
- {
- lex->sp_lex_in_use= TRUE;
- }
- virtual ~sp_lex_keeper()
- {
- if (m_lex_resp)
- {
- /* Prevent endless recursion. */
- m_lex->sphead= NULL;
- 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);
-
- int cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
- sp_instr *instr);
-
- inline uint sql_command() const
- {
- return (uint)m_lex->sql_command;
- }
-
- void disable_query_cache()
- {
- m_lex->safe_to_cache_query= 0;
- }
-
-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;
-};
-
-
-/**
- Call out to some prepared SQL statement.
-*/
-class sp_instr_stmt : public sp_instr
-{
- sp_instr_stmt(const sp_instr_stmt &); /**< Prevent use of these */
- void operator=(sp_instr_stmt &);
-
-public:
-
- LEX_STRING m_query; ///< For thd->query
-
- sp_instr_stmt(uint ip, sp_pcontext *ctx, LEX *lex)
- : sp_instr(ip, ctx), m_lex_keeper(lex, TRUE)
- {
- m_query.str= 0;
- m_query.length= 0;
- }
-
- virtual ~sp_instr_stmt() = default;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual int exec_core(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
-private:
-
- sp_lex_keeper m_lex_keeper;
-
-public:
- virtual PSI_statement_info* get_psi_info() { return & psi_info; }
- static PSI_statement_info psi_info;
-
-}; // class sp_instr_stmt : public sp_instr
-
-
-class sp_instr_set : public sp_instr
-{
- 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)
- : sp_instr(ip, ctx),
- m_rcontext_handler(rh), m_offset(offset), m_value(val),
- m_lex_keeper(lex, lex_resp)
- {}
-
- virtual ~sp_instr_set() = default;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual int exec_core(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
-protected:
- sp_rcontext *get_rcontext(THD *thd) const;
- const Sp_rcontext_handler *m_rcontext_handler;
- uint m_offset; ///< Frame offset
- Item *m_value;
- sp_lex_keeper m_lex_keeper;
-
-public:
- virtual PSI_statement_info* get_psi_info() { return & psi_info; }
- static PSI_statement_info psi_info;
-}; // class sp_instr_set : public sp_instr
-
-
-/*
- 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)
- : sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp),
- m_field_offset(field_offset)
- {}
-
- virtual ~sp_instr_set_row_field() = default;
-
- virtual int exec_core(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-}; // 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_row_field_by_name searches for ROW fields by name,
- while sp_instr_set_row_field (see above) searches for ROW fields by index.
-*/
-class sp_instr_set_row_field_by_name : public sp_instr_set
-{
- // Prevent use of this
- sp_instr_set_row_field_by_name(const sp_instr_set_row_field &);
- void operator=(sp_instr_set_row_field_by_name &);
- const LEX_CSTRING m_field_name;
-
-public:
-
- sp_instr_set_row_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)
- : sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp),
- m_field_name(field_name)
- {}
-
- virtual ~sp_instr_set_row_field_by_name() = default;
-
- virtual int exec_core(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-}; // class sp_instr_set_field_by_name : public sp_instr_set
-
-
-/**
- Set NEW/OLD row field value instruction. Used in triggers.
-*/
-class sp_instr_set_trigger_field : public sp_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)
- : sp_instr(ip, ctx),
- trigger_field(trg_fld),
- value(val), m_lex_keeper(lex, TRUE)
- {}
-
- virtual ~sp_instr_set_trigger_field() = default;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual int exec_core(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
-private:
- Item_trigger_field *trigger_field;
- Item *value;
- sp_lex_keeper m_lex_keeper;
-
-public:
- virtual PSI_statement_info* get_psi_info() { return & psi_info; }
- static PSI_statement_info psi_info;
-}; // class sp_instr_trigger_field : public sp_instr
-
-
-/**
- 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 sp_instr
-{
-public:
-
- uint m_dest; ///< Where we will go
- uint m_cont_dest; ///< Where continue handlers will go
-
- sp_instr_opt_meta(uint ip, sp_pcontext *ctx)
- : sp_instr(ip, ctx),
- m_dest(0), m_cont_dest(0), m_optdest(0), m_cont_optdest(0)
- {}
-
- sp_instr_opt_meta(uint ip, sp_pcontext *ctx, uint dest)
- : sp_instr(ip, ctx),
- 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;
-
- virtual uint get_cont_dest() const;
-
-protected:
-
- sp_instr *m_optdest; ///< Used during optimization
- sp_instr *m_cont_optdest; ///< Used during optimization
-
-}; // class sp_instr_opt_meta : public sp_instr
-
-class sp_instr_jump : 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_opt_meta(ip, ctx)
- {}
-
- sp_instr_jump(uint ip, sp_pcontext *ctx, uint dest)
- : sp_instr_opt_meta(ip, ctx, dest)
- {}
-
- virtual ~sp_instr_jump() = default;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
- virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
-
- virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start);
-
- virtual void opt_move(uint dst, List<sp_instr> *ibp);
-
- virtual void backpatch(uint dest, sp_pcontext *dst_ctx)
- {
- /* Calling backpatch twice is a logic flaw in jump resolution. */
- DBUG_ASSERT(m_dest == 0);
- m_dest= dest;
- }
-
- /**
- Update the destination; used by the optimizer.
- */
- virtual void set_destination(uint old_dest, uint new_dest)
- {
- if (m_dest == old_dest)
- m_dest= new_dest;
- }
-
-public:
- virtual PSI_statement_info* get_psi_info() { return & psi_info; }
- static PSI_statement_info psi_info;
-}; // class sp_instr_jump : public sp_instr_opt_meta
-
-
-class sp_instr_jump_if_not : public sp_instr_jump
-{
- sp_instr_jump_if_not(const sp_instr_jump_if_not &); /**< Prevent use of these */
- void operator=(sp_instr_jump_if_not &);
-
-public:
-
- sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i, LEX *lex)
- : sp_instr_jump(ip, ctx), m_expr(i),
- m_lex_keeper(lex, TRUE)
- {}
-
- sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i, uint dest, LEX *lex)
- : sp_instr_jump(ip, ctx, dest), m_expr(i),
- m_lex_keeper(lex, TRUE)
- {}
-
- virtual ~sp_instr_jump_if_not() = default;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual int exec_core(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
- virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
-
- /** Override sp_instr_jump's shortcut; we stop here */
- virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
- {
- return m_ip;
- }
-
- virtual void opt_move(uint dst, List<sp_instr> *ibp);
-
- virtual void set_destination(uint old_dest, uint new_dest)
- {
- sp_instr_jump::set_destination(old_dest, new_dest);
- if (m_cont_dest == old_dest)
- m_cont_dest= new_dest;
- }
-
-private:
-
- Item *m_expr; ///< The condition
- sp_lex_keeper m_lex_keeper;
-
-public:
- virtual PSI_statement_info* get_psi_info() { return & psi_info; }
- static PSI_statement_info psi_info;
-}; // class sp_instr_jump_if_not : public sp_instr_jump
-
-
-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;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
- virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads)
- {
- marked= 1;
- return UINT_MAX;
- }
-
-public:
- virtual PSI_statement_info* get_psi_info() { return & psi_info; }
- static PSI_statement_info psi_info;
-}; // class sp_instr_preturn : public sp_instr
-
-
-class sp_instr_freturn : public sp_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, LEX *lex)
- : sp_instr(ip, ctx), m_value(val), m_type_handler(handler),
- m_lex_keeper(lex, TRUE)
- {}
-
- virtual ~sp_instr_freturn() = default;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual int exec_core(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
- virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads)
- {
- marked= 1;
- return UINT_MAX;
- }
-
-protected:
-
- Item *m_value;
- const Type_handler *m_type_handler;
- sp_lex_keeper m_lex_keeper;
-
-public:
- virtual PSI_statement_info* get_psi_info() { return & psi_info; }
- static PSI_statement_info psi_info;
-}; // class sp_instr_freturn : public sp_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);
- }
-
- virtual ~sp_instr_hpush_jump()
- {
- m_handler->condition_values.empty();
- m_handler= NULL;
- }
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
- virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
-
- /** Override sp_instr_jump's shortcut; we stop here. */
- virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
- {
- return m_ip;
- }
-
- virtual void backpatch(uint dest, sp_pcontext *dst_ctx)
- {
- 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:
- virtual PSI_statement_info* get_psi_info() { 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;
- }
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
-private:
-
- uint m_count;
-
-public:
- virtual PSI_statement_info* get_psi_info() { 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;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
- /* This instruction will not be short cut optimized. */
- virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
- {
- return m_ip;
- }
-
- virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
-
-private:
-
- uint m_frame;
-
-public:
- virtual PSI_statement_info* get_psi_info() { return & psi_info; }
- static PSI_statement_info psi_info;
-}; // class sp_instr_hreturn : public sp_instr_jump
-
-
-/** This is DECLARE CURSOR */
-class sp_instr_cpush : public sp_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, LEX *lex, uint offset)
- : sp_instr(ip, ctx), m_lex_keeper(lex, TRUE), m_cursor(offset)
- {}
-
- virtual ~sp_instr_cpush() = default;
-
- int execute(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.
- */
- virtual bool cleanup_stmt(bool /*restore_set_statement_vars*/) override
- { return false; }
-private:
-
- sp_lex_keeper m_lex_keeper;
- uint m_cursor; /**< Frame offset (for debugging) */
-
-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;
- }
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
-private:
-
- uint m_count;
-
-public:
- virtual PSI_statement_info* get_psi_info() { 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;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual int exec_core(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
-private:
-
- uint m_cursor; ///< Stack index
-
-public:
- virtual PSI_statement_info* get_psi_info() { 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_instr
-{
- /**< Prevent use of these */
- sp_instr_cursor_copy_struct(const sp_instr_cursor_copy_struct &);
- void operator=(sp_instr_cursor_copy_struct &);
- sp_lex_keeper m_lex_keeper;
- uint m_cursor;
- uint m_var;
-public:
- sp_instr_cursor_copy_struct(uint ip, sp_pcontext *ctx, uint coffs,
- sp_lex_cursor *lex, uint voffs)
- : sp_instr(ip, ctx), m_lex_keeper(lex, FALSE),
- m_cursor(coffs),
- m_var(voffs)
- {}
- virtual ~sp_instr_cursor_copy_struct() = default;
- virtual int execute(THD *thd, uint *nextp);
- virtual int exec_core(THD *thd, uint *nextp);
- virtual void print(String *str);
-
-public:
- virtual PSI_statement_info* get_psi_info() { 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;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
-private:
-
- uint m_cursor;
-
-public:
- virtual PSI_statement_info* get_psi_info() { return & psi_info; }
- static PSI_statement_info psi_info;
-}; // class sp_instr_cclose : public sp_instr
-
-
-class sp_instr_cfetch : public sp_instr
-{
- 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, bool error_on_no_data)
- : sp_instr(ip, ctx), m_cursor(c), m_error_on_no_data(error_on_no_data)
- {
- m_varlist.empty();
- }
-
- virtual ~sp_instr_cfetch() = default;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
- void add_to_varlist(sp_variable *var)
- {
- m_varlist.push_back(var);
- }
-
-private:
-
- uint m_cursor;
- List<sp_variable> m_varlist;
- bool m_error_on_no_data;
-
-public:
- virtual PSI_statement_info* get_psi_info() { 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;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
-public:
- virtual PSI_statement_info* get_psi_info() { return & psi_info; }
- static PSI_statement_info psi_info;
-}; // class sp_instr_agg_cfetch : public sp_instr
-
-
-
-
-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;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
- virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads)
- {
- marked= 1;
- return UINT_MAX;
- }
-
-private:
-
- int m_errcode;
-
-public:
- virtual PSI_statement_info* get_psi_info() { return & psi_info; }
- static PSI_statement_info psi_info;
-}; // class sp_instr_error : public sp_instr
-
-
-class sp_instr_set_case_expr : 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)
- : sp_instr_opt_meta(ip, ctx),
- m_case_expr_id(case_expr_id), m_case_expr(case_expr),
- m_lex_keeper(lex, TRUE)
- {}
-
- virtual ~sp_instr_set_case_expr() = default;
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual int exec_core(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
- virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
-
- virtual void opt_move(uint dst, List<sp_instr> *ibp);
-
- virtual void set_destination(uint old_dest, uint new_dest)
- {
- if (m_cont_dest == old_dest)
- m_cont_dest= new_dest;
- }
-
-private:
-
- uint m_case_expr_id;
- Item *m_case_expr;
- sp_lex_keeper m_lex_keeper;
-
-public:
- virtual PSI_statement_info* get_psi_info() { return & psi_info; }
- static PSI_statement_info psi_info;
-}; // class sp_instr_set_case_expr : public sp_instr_opt_meta
-
bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
diff --git a/sql/sp_instr.cc b/sql/sp_instr.cc
new file mode 100644
index 00000000000..44615a8d732
--- /dev/null
+++ b/sql/sp_instr.cc
@@ -0,0 +1,1663 @@
+#include "sp_instr.h"
+
+#include "opt_trace.h" // class Opt_trace_start
+#include "sql_array.h" // class Dynamic_array
+#include "sql_audit.h" // mysql_audit_general
+#include "sql_base.h" // open_and_lock_tables
+#include "sql_derived.h" // mysql_handle_derived
+#include "sp_head.h" // class sp_head
+#include "sql_parse.h" // check_table_access
+#include "sp_rcontext.h" // class sp_rcontext
+#include "sql_prepare.h" // reinit_stmt_before_use
+#include "transaction.h" // trans_commit_stmt, trans_rollback_stmt, ...
+
+/*
+ Sufficient max length of printed destinations.
+*/
+static const int SP_STMT_PRINT_MAXLEN= 40;
+
+static int cmp_rqp_locations(Rewritable_query_parameter * const *a,
+ Rewritable_query_parameter * const *b)
+{
+ return (int)((*a)->pos_in_query - (*b)->pos_in_query);
+}
+
+
+/*
+ StoredRoutinesBinlogging
+ This paragraph applies only to statement-based binlogging. Row-based
+ binlogging does not need anything special like this.
+
+ Top-down overview:
+
+ 1. Statements
+
+ Statements that have is_update_query(stmt) == true are written into the
+ binary log verbatim.
+ Examples:
+ UPDATE tbl SET tbl.x = spfunc_w_side_effects()
+ UPDATE tbl SET tbl.x=1 WHERE spfunc_w_side_effect_that_returns_false(tbl.y)
+
+ Statements that have is_update_query(stmt) == false (e.g. SELECTs) are not
+ written into binary log. Instead we catch function calls the statement
+ makes and write it into binary log separately (see #3).
+
+ 2. PROCEDURE calls
+
+ CALL statements are not written into binary log. Instead
+ * Any FUNCTION invocation (in SET, IF, WHILE, OPEN CURSOR and other SP
+ instructions) is written into binlog separately.
+
+ * Each statement executed in SP is binlogged separately, according to rules
+ in #1, with the exception that we modify query string: we replace uses
+ of SP local variables with NAME_CONST('spvar_name', <spvar-value>) calls.
+ This substitution is done in subst_spvars().
+
+ 3. FUNCTION calls
+
+ In sp_head::execute_function(), we check
+ * If this function invocation is done from a statement that is written
+ into the binary log.
+ * If there were any attempts to write events to the binary log during
+ function execution (grep for start_union_events and stop_union_events)
+
+ If the answers are No and Yes, we write the function call into the binary
+ log as "SELECT spfunc(<param1value>, <param2value>, ...)"
+
+
+ 4. Miscellaneous issues.
+
+ 4.1 User variables.
+
+ When we call mysql_bin_log.write() for an SP statement, thd->user_var_events
+ must hold set<{var_name, value}> pairs for all user variables used during
+ the statement execution.
+ This set is produced by tracking user variable reads during statement
+ execution.
+
+ For SPs, this has the following implications:
+ 1) thd->user_var_events may contain events from several SP statements and
+ needs to be valid after exection of these statements was finished. In
+ order to achieve that, we
+ * Allocate user_var_events array elements on appropriate mem_root (grep
+ for user_var_events_alloc).
+ * Use is_query_in_union() to determine if user_var_event is created.
+
+ 2) We need to empty thd->user_var_events after we have wrote a function
+ call. This is currently done by making
+ reset_dynamic(&thd->user_var_events);
+ calls in several different places. (TODO cosider moving this into
+ mysql_bin_log.write() function)
+
+ 4.2 Auto_increment storage in binlog
+
+ As we may write two statements to binlog from one single logical statement
+ (case of "SELECT func1(),func2()": it is binlogged as "SELECT func1()" and
+ then "SELECT func2()"), we need to reset auto_increment binlog variables
+ after each binlogged SELECT. Otherwise, the auto_increment value of the
+ first SELECT would be used for the second too.
+*/
+
+
+/**
+ Replace thd->query{_length} with a string that one can write to
+ the binlog.
+
+ The binlog-suitable string is produced by replacing references to SP local
+ variables with NAME_CONST('sp_var_name', value) calls.
+
+ @param thd Current thread.
+ @param instr Instruction (we look for Item_splocal instances in
+ instr->free_list)
+ @param query_str Original query string
+
+ @return
+ - false on success.
+ thd->query{_length} either has been appropriately replaced or there
+ is no need for replacements.
+ - true out of memory error.
+*/
+
+static bool
+subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
+{
+ DBUG_ENTER("subst_spvars");
+
+ Dynamic_array<Rewritable_query_parameter*> rewritables(PSI_INSTRUMENT_MEM);
+ char *pbuf;
+ StringBuffer<512> qbuf;
+ Copy_query_with_rewrite acc(thd, query_str->str, query_str->length, &qbuf);
+
+ /* Find rewritable Items used in this statement */
+ for (Item *item= instr->free_list; item; item= item->next)
+ {
+ Rewritable_query_parameter *rqp= item->get_rewritable_query_parameter();
+ if (rqp && rqp->pos_in_query)
+ rewritables.append(rqp);
+ }
+ if (!rewritables.elements())
+ DBUG_RETURN(false);
+
+ rewritables.sort(cmp_rqp_locations);
+
+ thd->query_name_consts= (uint)rewritables.elements();
+
+ for (Rewritable_query_parameter **rqp= rewritables.front();
+ rqp <= rewritables.back(); rqp++)
+ {
+ if (acc.append(*rqp))
+ DBUG_RETURN(true);
+ }
+ if (acc.finalize())
+ DBUG_RETURN(true);
+
+ /*
+ Allocate additional space at the end of the new query string for the
+ query_cache_send_result_to_client function.
+
+ The query buffer layout is:
+ buffer :==
+ <statement> The input statement(s)
+ '\0' Terminating null char
+ <length> Length of following current database name 2
+ <db_name> Name of current database
+ <flags> Flags struct
+ */
+ size_t buf_len= (qbuf.length() + 1 + QUERY_CACHE_DB_LENGTH_SIZE +
+ thd->db.length + QUERY_CACHE_FLAGS_SIZE + 1);
+ if ((pbuf= (char *) alloc_root(thd->mem_root, buf_len)))
+ {
+ char *ptr= pbuf + qbuf.length();
+ memcpy(pbuf, qbuf.ptr(), qbuf.length());
+ *ptr= 0;
+ int2store(ptr+1, thd->db.length);
+ }
+ else
+ DBUG_RETURN(true);
+
+ thd->set_query(pbuf, qbuf.length());
+
+ DBUG_RETURN(false);
+}
+
+/**
+ Prepare LEX and thread for execution of instruction, if requested open
+ and lock LEX's tables, execute instruction's core function, perform
+ cleanup afterwards.
+
+ @param thd thread context
+ @param nextp out - next instruction
+ @param open_tables if true then 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 sp_instr instruction for which we prepare context, and which core
+ function execute by calling its exec_core() method.
+
+ @note
+ We are not saving/restoring some parts of THD which may need this because
+ we do this once for whole routine execution in sp_head::execute().
+
+ @return
+ 0/non-0 - Success/Failure
+*/
+
+int
+sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
+ bool open_tables, sp_instr* instr)
+{
+ int res= 0;
+ DBUG_ENTER("reset_lex_and_exec_core");
+
+ /*
+ The flag is saved at the entry to the following substatement.
+ It's reset further in the common code part.
+ It's merged with the saved parent's value at the exit of this func.
+ */
+ bool parent_modified_non_trans_table=
+ thd->transaction->stmt.modified_non_trans_table;
+ unsigned int parent_unsafe_rollback_flags=
+ thd->transaction->stmt.m_unsafe_rollback_flags;
+ thd->transaction->stmt.modified_non_trans_table= false;
+ thd->transaction->stmt.m_unsafe_rollback_flags= 0;
+
+ DBUG_ASSERT(!thd->derived_tables);
+ DBUG_ASSERT(thd->Item_change_list::is_empty());
+ /*
+ Use our own lex.
+ We should not save old value since it is saved/restored in
+ sp_head::execute() when we are entering/leaving routine.
+ */
+ thd->lex= m_lex;
+
+ thd->set_query_id(next_query_id());
+
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
+ {
+ /*
+ This statement will enter/leave prelocked mode on its own.
+ Entering prelocked mode changes table list and related members
+ of LEX, so we'll need to restore them.
+ */
+ if (lex_query_tables_own_last)
+ {
+ /*
+ We've already entered/left prelocked mode with this statement.
+ Attach the list of tables that need to be prelocked and mark m_lex
+ as having such list attached.
+ */
+ *lex_query_tables_own_last= prelocking_tables;
+ m_lex->mark_as_requiring_prelocking(lex_query_tables_own_last);
+ }
+ }
+
+ reinit_stmt_before_use(thd, m_lex);
+
+#ifndef EMBEDDED_LIBRARY
+ /*
+ If there was instruction which changed tracking state,
+ the result of changed tracking state send to client in OK packed.
+ So it changes result sent to client and probably can be different
+ independent on query text. So we can't cache such results.
+ */
+ if ((thd->client_capabilities & CLIENT_SESSION_TRACK) &&
+ (thd->server_status & SERVER_SESSION_STATE_CHANGED))
+ thd->lex->safe_to_cache_query= 0;
+#endif
+
+ Opt_trace_start ots(thd);
+ ots.init(thd, m_lex->query_tables, SQLCOM_SELECT, &m_lex->var_list,
+ nullptr, 0, thd->variables.character_set_client);
+
+ Json_writer_object trace_command(thd);
+ Json_writer_array trace_command_steps(thd, "steps");
+ if (open_tables)
+ res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
+
+ if (likely(!res))
+ {
+ res= instr->exec_core(thd, nextp);
+ DBUG_PRINT("info",("exec_core returned: %d", res));
+ }
+
+ /*
+ Call after unit->cleanup() to close open table
+ key read.
+ */
+ if (open_tables)
+ {
+ m_lex->unit.cleanup();
+ /* Here we also commit or rollback the current statement. */
+ if (! thd->in_sub_stmt)
+ {
+ thd->get_stmt_da()->set_overwrite_status(true);
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->get_stmt_da()->set_overwrite_status(false);
+ }
+ close_thread_tables(thd);
+ thd_proc_info(thd, 0);
+
+ if (! thd->in_sub_stmt)
+ {
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
+ thd->release_transactional_locks();
+ else
+ thd->mdl_context.release_statement_locks();
+ }
+ }
+ //TODO: why is this here if log_slow_query is in sp_instr_stmt::execute?
+ delete_explain_query(m_lex);
+
+ if (m_lex->query_tables_own_last)
+ {
+ /*
+ We've entered and left prelocking mode when executing statement
+ stored in m_lex.
+ m_lex->query_tables(->next_global)* list now has a 'tail' - a list
+ of tables that are added for prelocking. (If this is the first
+ execution, the 'tail' was added by open_tables(), otherwise we've
+ attached it above in this function).
+ Now we'll save the 'tail', and detach it.
+ */
+ lex_query_tables_own_last= m_lex->query_tables_own_last;
+ prelocking_tables= *lex_query_tables_own_last;
+ *lex_query_tables_own_last= nullptr;
+ m_lex->query_tables_last= m_lex->query_tables_own_last;
+ m_lex->mark_as_requiring_prelocking(nullptr);
+ }
+ thd->rollback_item_tree_changes();
+ /*
+ Update the state of the active arena if no errors on
+ open_tables stage.
+ */
+ if (likely(!res) || likely(!thd->is_error()))
+ thd->stmt_arena->state= Query_arena::STMT_EXECUTED;
+
+ /*
+ Merge here with the saved parent's values
+ what is needed from the substatement gained
+ */
+ thd->transaction->stmt.modified_non_trans_table |= parent_modified_non_trans_table;
+ thd->transaction->stmt.m_unsafe_rollback_flags |= parent_unsafe_rollback_flags;
+
+ TRANSACT_TRACKER(add_trx_state_from_thd(thd));
+
+ /*
+ Unlike for PS we should not call Item's destructors for newly created
+ items after execution of each instruction in stored routine. This is
+ because SP often create Item (like Item_int, Item_string etc...) when
+ they want to store some value in local variable, pass return value and
+ etc... So their life time should be longer than one instruction.
+
+ cleanup_items() is called in sp_head::execute()
+ */
+ thd->lex->restore_set_statement_var();
+ DBUG_RETURN(res || thd->is_error());
+}
+
+
+int sp_lex_keeper::cursor_reset_lex_and_exec_core(THD *thd, uint *nextp,
+ bool open_tables,
+ sp_instr *instr)
+{
+ Query_arena *old_arena= thd->stmt_arena;
+ /*
+ Get the Query_arena from the cursor statement LEX, which contains
+ the free_list of the query, so new items (if any) are stored in
+ the right free_list, and we can cleanup after each cursor operation,
+ e.g. open or cursor_copy_struct (for cursor%ROWTYPE variables).
+ */
+ thd->stmt_arena= m_lex->query_arena();
+ int res= reset_lex_and_exec_core(thd, nextp, open_tables, instr);
+ cleanup_items(thd->stmt_arena->free_list);
+ thd->stmt_arena= old_arena;
+ return res;
+}
+
+
+/*
+ sp_instr class functions
+*/
+
+int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
+{
+ int result;
+
+ /*
+ Check whenever we have access to tables for this statement
+ and open and lock them before executing instructions core function.
+ */
+ if (thd->open_temporary_tables(tables) ||
+ check_table_access(thd, SELECT_ACL, tables, false, UINT_MAX, false)
+ || open_and_lock_tables(thd, tables, true, 0))
+ result= -1;
+ else
+ result= 0;
+ /* Prepare all derived tables/views to catch possible errors. */
+ if (!result)
+ result= mysql_handle_derived(thd->lex, DT_PREPARE) ? -1 : 0;
+
+ return result;
+}
+
+uint sp_instr::get_cont_dest() const
+{
+ return (m_ip+1);
+}
+
+
+int sp_instr::exec_core(THD *thd, uint *nextp)
+{
+ DBUG_ASSERT(0);
+ return 0;
+}
+
+/*
+ sp_instr_stmt class functions
+*/
+
+PSI_statement_info sp_instr_stmt::psi_info=
+{ 0, "stmt", 0};
+
+int
+sp_instr_stmt::execute(THD *thd, uint *nextp)
+{
+ int res;
+ bool save_enable_slow_log;
+ const CSET_STRING query_backup= thd->query_string;
+ Sub_statement_state backup_state;
+ DBUG_ENTER("sp_instr_stmt::execute");
+ DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command()));
+
+ MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, m_query.str, static_cast<uint>(m_query.length));
+
+#if defined(ENABLED_PROFILING)
+ /* This s-p instr is profilable and will be captured. */
+ thd->profiling.set_query_source(m_query.str, m_query.length);
+#endif
+
+ save_enable_slow_log= thd->enable_slow_log;
+ thd->store_slow_query_state(&backup_state);
+
+ if (!(res= alloc_query(thd, m_query.str, m_query.length)) &&
+ !(res=subst_spvars(thd, this, &m_query)))
+ {
+ /*
+ (the order of query cache and subst_spvars calls is irrelevant because
+ queries with SP vars can't be cached)
+ */
+ general_log_write(thd, COM_QUERY, thd->query(), thd->query_length());
+
+ if (query_cache_send_result_to_client(thd, thd->query(),
+ thd->query_length()) <= 0)
+ {
+ thd->reset_slow_query_state();
+ res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, false, this);
+ bool log_slow= !res && thd->enable_slow_log;
+
+ /* Finalize server status flags after executing a statement. */
+ if (log_slow || thd->get_stmt_da()->is_eof())
+ thd->update_server_status();
+
+ if (thd->get_stmt_da()->is_eof())
+ thd->protocol->end_statement();
+
+ query_cache_end_of_result(thd);
+
+ mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
+ thd->get_stmt_da()->is_error() ?
+ thd->get_stmt_da()->sql_errno() : 0,
+ command_name[COM_QUERY].str);
+
+ if (log_slow)
+ log_slow_statement(thd);
+
+ /*
+ Restore enable_slow_log, that can be changed by a admin or call
+ command
+ */
+ thd->enable_slow_log= save_enable_slow_log;
+
+ /* Add the number of rows to thd for the 'call' statistics */
+ thd->add_slow_query_state(&backup_state);
+ }
+ else
+ {
+ /* change statistics */
+ enum_sql_command save_sql_command= thd->lex->sql_command;
+ thd->lex->sql_command= SQLCOM_SELECT;
+ status_var_increment(thd->status_var.com_stat[SQLCOM_SELECT]);
+ thd->update_stats();
+ thd->lex->sql_command= save_sql_command;
+ *nextp= m_ip+1;
+ }
+ thd->set_query(query_backup);
+ thd->query_name_consts= 0;
+
+ if (likely(!thd->is_error()))
+ {
+ res= 0;
+ thd->get_stmt_da()->reset_diagnostics_area();
+ }
+ }
+
+ DBUG_RETURN(res || thd->is_error());
+}
+
+
+void
+sp_instr_stmt::print(String *str)
+{
+ size_t i, len;
+
+ /* stmt CMD "..." */
+ if (str->reserve(SP_STMT_PRINT_MAXLEN+SP_INSTR_UINT_MAXLEN+8))
+ return;
+ str->qs_append(STRING_WITH_LEN("stmt "));
+ str->qs_append((uint)m_lex_keeper.sql_command());
+ str->qs_append(STRING_WITH_LEN(" \""));
+ len= m_query.length;
+ /*
+ Print the query string (but not too much of it), just to indicate which
+ statement it is.
+ */
+ if (len > SP_STMT_PRINT_MAXLEN)
+ len= SP_STMT_PRINT_MAXLEN-3;
+ /* Copy the query string and replace '\n' with ' ' in the process */
+ for (i= 0 ; i < len ; i++)
+ {
+ char c= m_query.str[i];
+ if (c == '\n')
+ c= ' ';
+ str->qs_append(c);
+ }
+ if (m_query.length > SP_STMT_PRINT_MAXLEN)
+ str->qs_append(STRING_WITH_LEN("...")); /* Indicate truncated string */
+ str->qs_append('"');
+}
+
+
+int
+sp_instr_stmt::exec_core(THD *thd, uint *nextp)
+{
+ MYSQL_QUERY_EXEC_START(thd->query(),
+ thd->thread_id,
+ thd->get_db(),
+ &thd->security_ctx->priv_user[0],
+ (char *)thd->security_ctx->host_or_ip,
+ 3);
+ int res= mysql_execute_command(thd);
+ MYSQL_QUERY_EXEC_DONE(res);
+ *nextp= m_ip+1;
+ return res;
+}
+
+
+/*
+ sp_instr_set class functions
+*/
+
+PSI_statement_info sp_instr_set::psi_info=
+{ 0, "set", 0};
+
+int
+sp_instr_set::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_set::execute");
+ DBUG_PRINT("info", ("offset: %u", m_offset));
+
+ DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, true, this));
+}
+
+
+sp_rcontext *sp_instr_set::get_rcontext(THD *thd) const
+{
+ return m_rcontext_handler->get_rcontext(thd->spcont);
+}
+
+
+int
+sp_instr_set::exec_core(THD *thd, uint *nextp)
+{
+ int res= get_rcontext(thd)->set_variable(thd, m_offset, &m_value);
+ delete_explain_query(thd->lex);
+ *nextp = m_ip+1;
+ return res;
+}
+
+void
+sp_instr_set::print(String *str)
+{
+ /* set name@offset ... */
+ size_t rsrv = SP_INSTR_UINT_MAXLEN+6;
+ sp_variable *var = m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+
+ /* 'var' should always be non-null, but just in case... */
+ if (var)
+ rsrv+= var->name.length + prefix->length;
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix->str, prefix->length);
+ if (var)
+ {
+ str->qs_append(&var->name);
+ str->qs_append('@');
+ }
+ str->qs_append(m_offset);
+ str->qs_append(' ');
+ m_value->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+}
+
+
+/*
+ sp_instr_set_field class functions
+*/
+
+int
+sp_instr_set_row_field::exec_core(THD *thd, uint *nextp)
+{
+ int res= get_rcontext(thd)->set_variable_row_field(thd, m_offset,
+ m_field_offset,
+ &m_value);
+ delete_explain_query(thd->lex);
+ *nextp= m_ip + 1;
+ return res;
+}
+
+
+void
+sp_instr_set_row_field::print(String *str)
+{
+ /* set name@offset[field_offset] ... */
+ size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3;
+ sp_variable *var= m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ DBUG_ASSERT(var);
+ DBUG_ASSERT(var->field_def.is_row());
+ const Column_definition *def=
+ var->field_def.row_field_definitions()->elem(m_field_offset);
+ DBUG_ASSERT(def);
+
+ rsrv+= var->name.length + def->field_name.length + prefix->length;
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix);
+ str->qs_append(&var->name);
+ str->qs_append('.');
+ str->qs_append(&def->field_name);
+ str->qs_append('@');
+ str->qs_append(m_offset);
+ str->qs_append('[');
+ str->qs_append(m_field_offset);
+ str->qs_append(']');
+ str->qs_append(' ');
+ m_value->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+}
+
+
+/*
+ sp_instr_set_field_by_name class functions
+*/
+
+int
+sp_instr_set_row_field_by_name::exec_core(THD *thd, uint *nextp)
+{
+ int res= get_rcontext(thd)->set_variable_row_field_by_name(thd, m_offset,
+ m_field_name,
+ &m_value);
+ delete_explain_query(thd->lex);
+ *nextp= m_ip + 1;
+ return res;
+}
+
+
+void
+sp_instr_set_row_field_by_name::print(String *str)
+{
+ /* set name.field@offset["field"] ... */
+ size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2;
+ sp_variable *var= m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ DBUG_ASSERT(var);
+ DBUG_ASSERT(var->field_def.is_table_rowtype_ref() ||
+ var->field_def.is_cursor_rowtype_ref());
+
+ rsrv+= var->name.length + 2 * m_field_name.length + prefix->length;
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix);
+ str->qs_append(&var->name);
+ str->qs_append('.');
+ str->qs_append(&m_field_name);
+ str->qs_append('@');
+ str->qs_append(m_offset);
+ str->qs_append("[\"",2);
+ str->qs_append(&m_field_name);
+ str->qs_append("\"]",2);
+ str->qs_append(' ');
+ m_value->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+}
+
+
+/*
+ sp_instr_set_trigger_field class functions
+*/
+
+PSI_statement_info sp_instr_set_trigger_field::psi_info=
+{ 0, "set_trigger_field", 0};
+
+int
+sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_set_trigger_field::execute");
+ thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
+ DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, true, this));
+}
+
+
+int
+sp_instr_set_trigger_field::exec_core(THD *thd, uint *nextp)
+{
+ Abort_on_warning_instant_set aws(thd, thd->is_strict_mode() && !thd->lex->ignore);
+ const int res= (trigger_field->set_value(thd, &value) ? -1 : 0);
+ *nextp = m_ip+1;
+ return res;
+}
+
+void
+sp_instr_set_trigger_field::print(String *str)
+{
+ str->append(STRING_WITH_LEN("set_trigger_field "));
+ trigger_field->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+ str->append(STRING_WITH_LEN(":="));
+ value->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+}
+
+/*
+ sp_instr_opt_meta
+*/
+
+uint sp_instr_opt_meta::get_cont_dest() const
+{
+ return m_cont_dest;
+}
+
+
+/*
+ sp_instr_jump class functions
+*/
+
+PSI_statement_info sp_instr_jump::psi_info=
+{ 0, "jump", 0};
+
+int
+sp_instr_jump::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_jump::execute");
+ DBUG_PRINT("info", ("destination: %u", m_dest));
+
+ *nextp= m_dest;
+ DBUG_RETURN(0);
+}
+
+void
+sp_instr_jump::print(String *str)
+{
+ /* jump dest */
+ if (str->reserve(SP_INSTR_UINT_MAXLEN+5))
+ return;
+ str->qs_append(STRING_WITH_LEN("jump "));
+ str->qs_append(m_dest);
+}
+
+uint
+sp_instr_jump::opt_mark(sp_head *sp, List<sp_instr> *leads)
+{
+ m_dest= opt_shortcut_jump(sp, this);
+ if (m_dest != m_ip+1) /* Jumping to following instruction? */
+ marked= 1;
+ m_optdest= sp->get_instr(m_dest);
+ return m_dest;
+}
+
+uint
+sp_instr_jump::opt_shortcut_jump(sp_head *sp, sp_instr *start)
+{
+ uint dest= m_dest;
+ sp_instr *i;
+
+ while ((i= sp->get_instr(dest)))
+ {
+ uint ndest;
+
+ if (start == i || this == i)
+ break;
+ ndest= i->opt_shortcut_jump(sp, start);
+ if (ndest == dest)
+ break;
+ dest= ndest;
+ }
+ return dest;
+}
+
+void
+sp_instr_jump::opt_move(uint dst, List<sp_instr> *bp)
+{
+ if (m_dest > m_ip)
+ bp->push_back(this); // Forward
+ else if (m_optdest)
+ m_dest= m_optdest->m_ip; // Backward
+ m_ip= dst;
+}
+
+
+/*
+ sp_instr_jump_if_not class functions
+*/
+
+PSI_statement_info sp_instr_jump_if_not::psi_info=
+{ 0, "jump_if_not", 0};
+
+int
+sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_jump_if_not::execute");
+ DBUG_PRINT("info", ("destination: %u", m_dest));
+ DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, true, this));
+}
+
+
+int
+sp_instr_jump_if_not::exec_core(THD *thd, uint *nextp)
+{
+ Item *it;
+ int res;
+
+ it= thd->sp_prepare_func_item(&m_expr, 1);
+ if (! it)
+ {
+ res= -1;
+ }
+ else
+ {
+ res= 0;
+ if (! it->val_bool())
+ *nextp = m_dest;
+ else
+ *nextp = m_ip+1;
+ }
+
+ return res;
+}
+
+
+void
+sp_instr_jump_if_not::print(String *str)
+{
+ /* jump_if_not dest(cont) ... */
+ if (str->reserve(2*SP_INSTR_UINT_MAXLEN+14+32)) // Add some for the expr. too
+ return;
+ str->qs_append(STRING_WITH_LEN("jump_if_not "));
+ str->qs_append(m_dest);
+ str->qs_append('(');
+ str->qs_append(m_cont_dest);
+ str->qs_append(STRING_WITH_LEN(") "));
+ m_expr->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+}
+
+
+uint
+sp_instr_jump_if_not::opt_mark(sp_head *sp, List<sp_instr> *leads)
+{
+ sp_instr *i;
+
+ marked= 1;
+ if ((i= sp->get_instr(m_dest)))
+ {
+ m_dest= i->opt_shortcut_jump(sp, this);
+ m_optdest= sp->get_instr(m_dest);
+ }
+ sp->add_mark_lead(m_dest, leads);
+ if ((i= sp->get_instr(m_cont_dest)))
+ {
+ m_cont_dest= i->opt_shortcut_jump(sp, this);
+ m_cont_optdest= sp->get_instr(m_cont_dest);
+ }
+ sp->add_mark_lead(m_cont_dest, leads);
+ return m_ip+1;
+}
+
+void
+sp_instr_jump_if_not::opt_move(uint dst, List<sp_instr> *bp)
+{
+ /*
+ cont. destinations may point backwards after shortcutting jumps
+ during the mark phase. If it's still pointing forwards, only
+ push this for backpatching if sp_instr_jump::opt_move() will not
+ do it (i.e. if the m_dest points backwards).
+ */
+ if (m_cont_dest > m_ip)
+ { // Forward
+ if (m_dest < m_ip)
+ bp->push_back(this);
+ }
+ else if (m_cont_optdest)
+ m_cont_dest= m_cont_optdest->m_ip; // Backward
+ /* This will take care of m_dest and m_ip */
+ sp_instr_jump::opt_move(dst, bp);
+}
+
+
+/*
+ sp_instr_freturn class functions
+*/
+
+PSI_statement_info sp_instr_freturn::psi_info=
+{ 0, "freturn", 0};
+
+int
+sp_instr_freturn::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_freturn::execute");
+ DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, true, this));
+}
+
+
+int
+sp_instr_freturn::exec_core(THD *thd, uint *nextp)
+{
+ /*
+ RETURN is a "procedure statement" (in terms of the SQL standard).
+ That means, Diagnostics Area should be clean before its execution.
+ */
+
+ if (!(thd->variables.sql_mode & MODE_ORACLE))
+ {
+ /*
+ Don't clean warnings in ORACLE mode,
+ as they are needed for SQLCODE and SQLERRM:
+ BEGIN
+ SELECT a INTO a FROM t1;
+ RETURN 'No exception ' || SQLCODE || ' ' || SQLERRM;
+ EXCEPTION WHEN NO_DATA_FOUND THEN
+ RETURN 'Exception ' || SQLCODE || ' ' || SQLERRM;
+ END;
+ */
+ Diagnostics_area *da= thd->get_stmt_da();
+ da->clear_warning_info(da->warning_info_id());
+ }
+
+ /*
+ Change <next instruction pointer>, so that this will be the last
+ instruction in the stored function.
+ */
+
+ *nextp= UINT_MAX;
+
+ /*
+ Evaluate the value of return expression and store it in current runtime
+ context.
+
+ NOTE: It's necessary to evaluate result item right here, because we must
+ do it in scope of execution the current context/block.
+ */
+
+ return thd->spcont->set_return_value(thd, &m_value);
+}
+
+void
+sp_instr_freturn::print(String *str)
+{
+ /* freturn type expr... */
+ if (str->reserve(1024+8+32)) // Add some for the expr. too
+ return;
+ str->qs_append(STRING_WITH_LEN("freturn "));
+ LEX_CSTRING name= m_type_handler->name().lex_cstring();
+ str->qs_append(&name);
+ str->qs_append(' ');
+ m_value->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+}
+
+/*
+ sp_instr_preturn class functions
+*/
+
+PSI_statement_info sp_instr_preturn::psi_info=
+{ 0, "preturn", 0};
+
+int
+sp_instr_preturn::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_preturn::execute");
+ *nextp= UINT_MAX;
+ DBUG_RETURN(0);
+}
+
+void
+sp_instr_preturn::print(String *str)
+{
+ str->append(STRING_WITH_LEN("preturn"));
+}
+
+/*
+ sp_instr_hpush_jump class functions
+*/
+
+PSI_statement_info sp_instr_hpush_jump::psi_info=
+{ 0, "hpush_jump", 0};
+
+int
+sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_hpush_jump::execute");
+
+ int ret= thd->spcont->push_handler(this);
+
+ *nextp= m_dest;
+
+ DBUG_RETURN(ret);
+}
+
+
+void
+sp_instr_hpush_jump::print(String *str)
+{
+ /* hpush_jump dest fsize type */
+ if (str->reserve(SP_INSTR_UINT_MAXLEN*2 + 21))
+ return;
+
+ str->qs_append(STRING_WITH_LEN("hpush_jump "));
+ str->qs_append(m_dest);
+ str->qs_append(' ');
+ str->qs_append(m_frame);
+
+ switch (m_handler->type) {
+ case sp_handler::EXIT:
+ str->qs_append(STRING_WITH_LEN(" EXIT"));
+ break;
+ case sp_handler::CONTINUE:
+ str->qs_append(STRING_WITH_LEN(" CONTINUE"));
+ break;
+ default:
+ // The handler type must be either CONTINUE or EXIT.
+ DBUG_ASSERT(0);
+ }
+}
+
+
+uint
+sp_instr_hpush_jump::opt_mark(sp_head *sp, List<sp_instr> *leads)
+{
+ sp_instr *i;
+
+ marked= 1;
+ if ((i= sp->get_instr(m_dest)))
+ {
+ m_dest= i->opt_shortcut_jump(sp, this);
+ m_optdest= sp->get_instr(m_dest);
+ }
+ sp->add_mark_lead(m_dest, leads);
+
+ /*
+ For continue handlers, all instructions in the scope of the handler
+ are possible leads. For example, the instruction after freturn might
+ be executed if the freturn triggers the condition handled by the
+ continue handler.
+
+ m_dest marks the start of the handler scope. It's added as a lead
+ above, so we start on m_dest+1 here.
+ m_opt_hpop is the hpop marking the end of the handler scope.
+ */
+ if (m_handler->type == sp_handler::CONTINUE)
+ {
+ for (uint scope_ip= m_dest+1; scope_ip <= m_opt_hpop; scope_ip++)
+ sp->add_mark_lead(scope_ip, leads);
+ }
+
+ return m_ip+1;
+}
+
+
+/*
+ sp_instr_hpop class functions
+*/
+
+PSI_statement_info sp_instr_hpop::psi_info=
+{ 0, "hpop", 0};
+
+int
+sp_instr_hpop::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_hpop::execute");
+ thd->spcont->pop_handlers(m_count);
+ *nextp= m_ip+1;
+ DBUG_RETURN(0);
+}
+
+void
+sp_instr_hpop::print(String *str)
+{
+ /* hpop count */
+ if (str->reserve(SP_INSTR_UINT_MAXLEN+5))
+ return;
+ str->qs_append(STRING_WITH_LEN("hpop "));
+ str->qs_append(m_count);
+}
+
+
+/*
+ sp_instr_hreturn class functions
+*/
+
+PSI_statement_info sp_instr_hreturn::psi_info=
+{ 0, "hreturn", 0};
+
+int
+sp_instr_hreturn::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_hreturn::execute");
+
+ uint continue_ip= thd->spcont->exit_handler(thd->get_stmt_da());
+
+ *nextp= m_dest ? m_dest : continue_ip;
+
+ DBUG_RETURN(0);
+}
+
+
+void
+sp_instr_hreturn::print(String *str)
+{
+ /* hreturn framesize dest */
+ if (str->reserve(SP_INSTR_UINT_MAXLEN*2 + 9))
+ return;
+ str->qs_append(STRING_WITH_LEN("hreturn "));
+ if (m_dest)
+ {
+ // NOTE: this is legacy: hreturn instruction for EXIT handler
+ // should print out 0 as frame index.
+ str->qs_append(STRING_WITH_LEN("0 "));
+ str->qs_append(m_dest);
+ }
+ else
+ {
+ str->qs_append(m_frame);
+ }
+}
+
+
+uint
+sp_instr_hreturn::opt_mark(sp_head *sp, List<sp_instr> *leads)
+{
+ marked= 1;
+
+ if (m_dest)
+ {
+ /*
+ This is an EXIT handler; next instruction step is in m_dest.
+ */
+ return m_dest;
+ }
+
+ /*
+ This is a CONTINUE handler; next instruction step will come from
+ the handler stack and not from opt_mark.
+ */
+ return UINT_MAX;
+}
+
+
+/*
+ sp_instr_cpush class functions
+*/
+
+PSI_statement_info sp_instr_cpush::psi_info=
+{ 0, "cpush", 0};
+
+int
+sp_instr_cpush::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cpush::execute");
+
+ sp_cursor::reset(thd, &m_lex_keeper);
+ m_lex_keeper.disable_query_cache();
+ thd->spcont->push_cursor(this);
+
+ *nextp= m_ip+1;
+
+ DBUG_RETURN(false);
+}
+
+
+void
+sp_instr_cpush::print(String *str)
+{
+ const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
+
+ /* cpush name@offset */
+ size_t rsrv= SP_INSTR_UINT_MAXLEN+7;
+
+ if (cursor_name)
+ rsrv+= cursor_name->length;
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("cpush "));
+ if (cursor_name)
+ {
+ str->qs_append(cursor_name->str, cursor_name->length);
+ str->qs_append('@');
+ }
+ str->qs_append(m_cursor);
+}
+
+
+/*
+ sp_instr_cpop class functions
+*/
+
+PSI_statement_info sp_instr_cpop::psi_info=
+{ 0, "cpop", 0};
+
+int
+sp_instr_cpop::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cpop::execute");
+ thd->spcont->pop_cursors(thd, m_count);
+ *nextp= m_ip+1;
+ DBUG_RETURN(0);
+}
+
+
+void
+sp_instr_cpop::print(String *str)
+{
+ /* cpop count */
+ if (str->reserve(SP_INSTR_UINT_MAXLEN+5))
+ return;
+ str->qs_append(STRING_WITH_LEN("cpop "));
+ str->qs_append(m_count);
+}
+
+
+/*
+ sp_instr_copen class functions
+*/
+
+/**
+ @todo
+ Assert that we either have an error or a cursor
+*/
+
+PSI_statement_info sp_instr_copen::psi_info=
+{ 0, "copen", 0};
+
+int
+sp_instr_copen::execute(THD *thd, uint *nextp)
+{
+ /*
+ We don't store a pointer to the cursor in the instruction to be
+ able to reuse the same instruction among different threads in future.
+ */
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res;
+ DBUG_ENTER("sp_instr_copen::execute");
+
+ if (! c)
+ res= -1;
+ else
+ {
+ sp_lex_keeper *lex_keeper= c->get_lex_keeper();
+ res= lex_keeper->cursor_reset_lex_and_exec_core(thd, nextp, false, this);
+ /* TODO: Assert here that we either have an error or a cursor */
+ }
+ DBUG_RETURN(res);
+}
+
+
+int
+sp_instr_copen::exec_core(THD *thd, uint *nextp)
+{
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res= c->open(thd);
+ *nextp= m_ip+1;
+ return res;
+}
+
+void
+sp_instr_copen::print(String *str)
+{
+ const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
+
+ /* copen name@offset */
+ size_t rsrv= SP_INSTR_UINT_MAXLEN+7;
+
+ if (cursor_name)
+ rsrv+= cursor_name->length;
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("copen "));
+ if (cursor_name)
+ {
+ str->qs_append(cursor_name->str, cursor_name->length);
+ str->qs_append('@');
+ }
+ str->qs_append(m_cursor);
+}
+
+
+/*
+ sp_instr_cclose class functions
+*/
+
+PSI_statement_info sp_instr_cclose::psi_info=
+{ 0, "cclose", 0};
+
+int
+sp_instr_cclose::execute(THD *thd, uint *nextp)
+{
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res;
+ DBUG_ENTER("sp_instr_cclose::execute");
+
+ if (! c)
+ res= -1;
+ else
+ res= c->close(thd);
+ *nextp= m_ip+1;
+ DBUG_RETURN(res);
+}
+
+
+void
+sp_instr_cclose::print(String *str)
+{
+ const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
+
+ /* cclose name@offset */
+ size_t rsrv= SP_INSTR_UINT_MAXLEN+8;
+
+ if (cursor_name)
+ rsrv+= cursor_name->length;
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("cclose "));
+ if (cursor_name)
+ {
+ str->qs_append(cursor_name->str, cursor_name->length);
+ str->qs_append('@');
+ }
+ str->qs_append(m_cursor);
+}
+
+
+/*
+ sp_instr_cfetch class functions
+*/
+
+PSI_statement_info sp_instr_cfetch::psi_info=
+{ 0, "cfetch", 0};
+
+int
+sp_instr_cfetch::execute(THD *thd, uint *nextp)
+{
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res;
+ Query_arena backup_arena;
+ DBUG_ENTER("sp_instr_cfetch::execute");
+
+ res= c ? c->fetch(thd, &m_varlist, m_error_on_no_data) : -1;
+
+ *nextp= m_ip+1;
+ DBUG_RETURN(res);
+}
+
+
+void
+sp_instr_cfetch::print(String *str)
+{
+ List_iterator_fast<sp_variable> li(m_varlist);
+ sp_variable *pv;
+ const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
+
+ /* cfetch name@offset vars... */
+ size_t rsrv= SP_INSTR_UINT_MAXLEN+8;
+
+ if (cursor_name)
+ rsrv+= cursor_name->length;
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("cfetch "));
+ if (cursor_name)
+ {
+ str->qs_append(cursor_name->str, cursor_name->length);
+ str->qs_append('@');
+ }
+ str->qs_append(m_cursor);
+ while ((pv= li++))
+ {
+ if (str->reserve(pv->name.length+SP_INSTR_UINT_MAXLEN+2))
+ return;
+ str->qs_append(' ');
+ str->qs_append(&pv->name);
+ str->qs_append('@');
+ str->qs_append(pv->offset);
+ }
+}
+
+/*
+ sp_instr_agg_cfetch class functions
+*/
+
+PSI_statement_info sp_instr_agg_cfetch::psi_info=
+{ 0, "agg_cfetch", 0};
+
+int
+sp_instr_agg_cfetch::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_agg_cfetch::execute");
+ int res= 0;
+ if (!thd->spcont->instr_ptr)
+ {
+ *nextp= m_ip+1;
+ thd->spcont->instr_ptr= m_ip + 1;
+ }
+ else if (!thd->spcont->pause_state)
+ thd->spcont->pause_state= true;
+ else
+ {
+ thd->spcont->pause_state= false;
+ if (thd->server_status & SERVER_STATUS_LAST_ROW_SENT)
+ {
+ my_message(ER_SP_FETCH_NO_DATA,
+ ER_THD(thd, ER_SP_FETCH_NO_DATA), MYF(0));
+ res= -1;
+ thd->spcont->quit_func= true;
+ }
+ else
+ *nextp= m_ip + 1;
+ }
+ DBUG_RETURN(res);
+}
+
+void
+sp_instr_agg_cfetch::print(String *str)
+{
+
+ uint rsrv= SP_INSTR_UINT_MAXLEN+11;
+
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("agg_cfetch"));
+}
+
+/*
+ sp_instr_cursor_copy_struct class functions
+*/
+
+/**
+ This methods processes cursor %ROWTYPE declarations, e.g.:
+ CURSOR cur IS SELECT * FROM t1;
+ rec cur%ROWTYPE;
+ and does the following:
+ - opens the cursor without copying data (materialization).
+ - copies the cursor structure to the associated %ROWTYPE variable.
+*/
+
+PSI_statement_info sp_instr_cursor_copy_struct::psi_info=
+{ 0, "cursor_copy_struct", 0};
+
+int
+sp_instr_cursor_copy_struct::exec_core(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cursor_copy_struct::exec_core");
+ int ret= 0;
+ Item_field_row *row= (Item_field_row*) thd->spcont->get_variable(m_var);
+ DBUG_ASSERT(row->type_handler() == &type_handler_row);
+
+ /*
+ Copy structure only once. If the cursor%ROWTYPE variable is declared
+ inside a LOOP block, it gets its structure on the first loop interation
+ and remembers the structure for all consequent loop iterations.
+ It we recreated the structure on every iteration, we would get
+ potential memory leaks, and it would be less efficient.
+ */
+ if (!row->arguments())
+ {
+ sp_cursor tmp(thd, &m_lex_keeper, true);
+ // Open the cursor without copying data
+ if (!(ret= tmp.open(thd)))
+ {
+ Row_definition_list defs;
+ /*
+ Create row elements on the caller arena.
+ It's the same arena that was used during sp_rcontext::create().
+ This puts cursor%ROWTYPE elements on the same mem_root
+ where explicit ROW elements and table%ROWTYPE reside:
+ - tmp.export_structure() allocates new Spvar_definition instances
+ and their components (such as TYPELIBs).
+ - row->row_create_items() creates new Item_field instances.
+ They all are created on the same mem_root.
+ */
+ Query_arena current_arena;
+ thd->set_n_backup_active_arena(thd->spcont->callers_arena, &current_arena);
+ if (!(ret= tmp.export_structure(thd, &defs)))
+ row->row_create_items(thd, &defs);
+ thd->restore_active_arena(thd->spcont->callers_arena, &current_arena);
+ tmp.close(thd);
+ }
+ }
+ *nextp= m_ip + 1;
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_instr_cursor_copy_struct::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cursor_copy_struct::execute");
+ int ret= m_lex_keeper.cursor_reset_lex_and_exec_core(thd, nextp, false, this);
+ DBUG_RETURN(ret);
+}
+
+
+void
+sp_instr_cursor_copy_struct::print(String *str)
+{
+ sp_variable *var= m_ctx->find_variable(m_var);
+ const LEX_CSTRING *name= m_ctx->find_cursor(m_cursor);
+ str->append(STRING_WITH_LEN("cursor_copy_struct "));
+ str->append(name);
+ str->append(' ');
+ str->append(&var->name);
+ str->append('@');
+ str->append_ulonglong(m_var);
+}
+
+
+/*
+ sp_instr_error class functions
+*/
+
+PSI_statement_info sp_instr_error::psi_info=
+{ 0, "error", 0};
+
+int
+sp_instr_error::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_error::execute");
+ my_message(m_errcode, ER_THD(thd, m_errcode), MYF(0));
+ WSREP_DEBUG("sp_instr_error: %s %d", ER_THD(thd, m_errcode), thd->is_error());
+ *nextp= m_ip+1;
+ DBUG_RETURN(-1);
+}
+
+
+void
+sp_instr_error::print(String *str)
+{
+ /* error code */
+ if (str->reserve(SP_INSTR_UINT_MAXLEN+6))
+ return;
+ str->qs_append(STRING_WITH_LEN("error "));
+ str->qs_append(m_errcode);
+}
+
+
+/**************************************************************************
+ sp_instr_set_case_expr class implementation
+**************************************************************************/
+
+PSI_statement_info sp_instr_set_case_expr::psi_info=
+{ 0, "set_case_expr", 0};
+
+int
+sp_instr_set_case_expr::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_set_case_expr::execute");
+
+ DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, true, this));
+}
+
+
+int
+sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp)
+{
+ int res= thd->spcont->set_case_expr(thd, m_case_expr_id, &m_case_expr);
+
+ if (res && !thd->spcont->get_case_expr(m_case_expr_id))
+ {
+ /*
+ Failed to evaluate the value, the case expression is still not
+ initialized. Set to NULL so we can continue.
+ */
+
+ Item *null_item= new (thd->mem_root) Item_null(thd);
+
+ if (!null_item ||
+ thd->spcont->set_case_expr(thd, m_case_expr_id, &null_item))
+ {
+ /* If this also failed, we have to abort. */
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL));
+ }
+ }
+ else
+ *nextp= m_ip+1;
+
+ return res;
+}
+
+
+void
+sp_instr_set_case_expr::print(String *str)
+{
+ /* set_case_expr (cont) id ... */
+ str->reserve(2*SP_INSTR_UINT_MAXLEN+18+32); // Add some extra for expr too
+ str->qs_append(STRING_WITH_LEN("set_case_expr ("));
+ str->qs_append(m_cont_dest);
+ str->qs_append(STRING_WITH_LEN(") "));
+ str->qs_append(m_case_expr_id);
+ str->qs_append(' ');
+ m_case_expr->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+}
+
+uint
+sp_instr_set_case_expr::opt_mark(sp_head *sp, List<sp_instr> *leads)
+{
+ sp_instr *i;
+
+ marked= 1;
+ if ((i= sp->get_instr(m_cont_dest)))
+ {
+ m_cont_dest= i->opt_shortcut_jump(sp, this);
+ m_cont_optdest= sp->get_instr(m_cont_dest);
+ }
+ sp->add_mark_lead(m_cont_dest, leads);
+ return m_ip+1;
+}
+
+void
+sp_instr_set_case_expr::opt_move(uint dst, List<sp_instr> *bp)
+{
+ if (m_cont_dest > m_ip)
+ bp->push_back(this); // Forward
+ else if (m_cont_optdest)
+ m_cont_dest= m_cont_optdest->m_ip; // Backward
+ m_ip= dst;
+}
diff --git a/sql/sp_instr.h b/sql/sp_instr.h
new file mode 100644
index 00000000000..a92c9256e35
--- /dev/null
+++ b/sql/sp_instr.h
@@ -0,0 +1,1107 @@
+#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)
+ {}
+
+ 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)
+ {}
+
+ ~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);
+ 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;
+ }
+};
+
+
+//
+// "Instructions"...
+//
+
+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)
+ {}
+
+ 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;
+
+ 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> *ibp)
+ {
+ m_ip= dst;
+ }
+
+ virtual PSI_statement_info* get_psi_info() = 0;
+
+}; // class sp_instr : public Sql_alloc
+
+
+/**
+ 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)
+ {
+ lex->sp_lex_in_use= true;
+ }
+
+ ~sp_lex_keeper()
+ {
+ if (m_lex_resp)
+ {
+ /* Prevent endless recursion. */
+ m_lex->sphead= nullptr;
+ 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);
+
+ int cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
+ sp_instr *instr);
+
+ inline uint sql_command() const
+ {
+ return (uint)m_lex->sql_command;
+ }
+
+ void disable_query_cache()
+ {
+ m_lex->safe_to_cache_query= 0;
+ }
+
+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;
+};
+
+
+/**
+ Call out to some prepared SQL statement.
+*/
+
+class sp_instr_stmt : public sp_instr
+{
+ sp_instr_stmt(const sp_instr_stmt &); /**< Prevent use of these */
+ void operator=(sp_instr_stmt &);
+
+public:
+ LEX_STRING m_query; ///< For thd->query
+
+ sp_instr_stmt(uint ip, sp_pcontext *ctx, LEX *lex)
+ : sp_instr(ip, ctx),
+ m_lex_keeper(lex, true)
+ {
+ m_query.str= 0;
+ m_query.length= 0;
+ }
+
+ 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;
+
+private:
+ sp_lex_keeper m_lex_keeper;
+
+public:
+ PSI_statement_info* get_psi_info() override { return & psi_info; }
+ static PSI_statement_info psi_info;
+}; // class sp_instr_stmt : public sp_instr
+
+
+class sp_instr_set : public sp_instr
+{
+ 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)
+ : sp_instr(ip, ctx),
+ m_rcontext_handler(rh),
+ m_offset(offset),
+ m_value(val),
+ m_lex_keeper(lex, lex_resp)
+ {}
+
+ 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;
+
+protected:
+ sp_rcontext *get_rcontext(THD *thd) const;
+ const Sp_rcontext_handler *m_rcontext_handler;
+ uint m_offset; ///< Frame offset
+ Item *m_value;
+ sp_lex_keeper m_lex_keeper;
+
+public:
+ PSI_statement_info* get_psi_info() override { return & psi_info; }
+ static PSI_statement_info psi_info;
+}; // class sp_instr_set : public sp_instr
+
+
+/*
+ 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)
+ : sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp),
+ 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_row_field_by_name searches for ROW fields by name,
+ while sp_instr_set_row_field (see above) searches for ROW fields by index.
+*/
+
+class sp_instr_set_row_field_by_name : public sp_instr_set
+{
+ // Prevent use of this
+ sp_instr_set_row_field_by_name(const sp_instr_set_row_field &);
+ void operator=(sp_instr_set_row_field_by_name &);
+ const LEX_CSTRING m_field_name;
+
+public:
+
+ sp_instr_set_row_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)
+ : sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp),
+ m_field_name(field_name)
+ {}
+
+ virtual ~sp_instr_set_row_field_by_name() = default;
+
+ int exec_core(THD *thd, uint *nextp) override;
+
+ void print(String *str) override;
+}; // class sp_instr_set_field_by_name : public sp_instr_set
+
+
+/**
+ Set NEW/OLD row field value instruction. Used in triggers.
+*/
+
+class sp_instr_set_trigger_field : public sp_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)
+ : sp_instr(ip, ctx),
+ trigger_field(trg_fld),
+ value(val),
+ m_lex_keeper(lex, true)
+ {}
+
+ 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;
+
+private:
+ Item_trigger_field *trigger_field;
+ Item *value;
+ sp_lex_keeper m_lex_keeper;
+
+public:
+ PSI_statement_info* get_psi_info() override { return & psi_info; }
+ static PSI_statement_info psi_info;
+}; // class sp_instr_trigger_field : public sp_instr
+
+
+/**
+ 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 sp_instr
+{
+public:
+ uint m_dest; ///< Where we will go
+ uint m_cont_dest; ///< Where continue handlers will go
+
+ sp_instr_opt_meta(uint ip, sp_pcontext *ctx)
+ : sp_instr(ip, ctx),
+ m_dest(0),
+ m_cont_dest(0),
+ m_optdest(0),
+ m_cont_optdest(0)
+ {}
+
+ sp_instr_opt_meta(uint ip, sp_pcontext *ctx, uint dest)
+ : sp_instr(ip, ctx),
+ 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;
+
+ uint get_cont_dest() const override;
+
+protected:
+ sp_instr *m_optdest; ///< Used during optimization
+ sp_instr *m_cont_optdest; ///< Used during optimization
+}; // class sp_instr_opt_meta : public sp_instr
+
+
+class sp_instr_jump : 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_opt_meta(ip, ctx)
+ {}
+
+ sp_instr_jump(uint ip, sp_pcontext *ctx, uint dest)
+ : sp_instr_opt_meta(ip, ctx, 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> *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;
+ }
+
+ /**
+ 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_opt_meta
+
+
+class sp_instr_jump_if_not : public sp_instr_jump
+{
+ /**< 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)
+ : sp_instr_jump(ip, ctx),
+ m_expr(i),
+ m_lex_keeper(lex, true)
+ {}
+
+ sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i, uint dest, LEX *lex)
+ : sp_instr_jump(ip, ctx, dest),
+ m_expr(i),
+ m_lex_keeper(lex, true)
+ {}
+
+ 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> *ibp) override;
+
+ void set_destination(uint old_dest, uint new_dest) override
+ {
+ sp_instr_jump::set_destination(old_dest, new_dest);
+ if (m_cont_dest == old_dest)
+ m_cont_dest= new_dest;
+ }
+
+private:
+ Item *m_expr; ///< The condition
+ sp_lex_keeper m_lex_keeper;
+
+public:
+ PSI_statement_info* get_psi_info() override { return & psi_info; }
+ static PSI_statement_info psi_info;
+}; // class sp_instr_jump_if_not : public sp_instr_jump
+
+
+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_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, LEX *lex)
+ : sp_instr(ip, ctx),
+ m_value(val),
+ m_type_handler(handler),
+ m_lex_keeper(lex, true)
+ {}
+
+ 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;
+ }
+
+protected:
+ Item *m_value;
+ const Type_handler *m_type_handler;
+ sp_lex_keeper m_lex_keeper;
+
+public:
+ PSI_statement_info* get_psi_info() override { return & psi_info; }
+ static PSI_statement_info psi_info;
+}; // class sp_instr_freturn : public sp_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
+
+
+/**
+ This is DECLARE CURSOR
+*/
+
+class sp_instr_cpush : public sp_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, LEX *lex, uint offset)
+ : sp_instr(ip, ctx),
+ m_lex_keeper(lex, true),
+ m_cursor(offset)
+ {}
+
+ virtual ~sp_instr_cpush() = default;
+
+ int execute(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;
+ }
+
+private:
+ sp_lex_keeper m_lex_keeper;
+ uint m_cursor; /**< Frame offset (for debugging) */
+
+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;
+
+ int exec_core(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_instr
+{
+ /**< Prevent use of these */
+ sp_instr_cursor_copy_struct(const sp_instr_cursor_copy_struct &);
+ void operator=(sp_instr_cursor_copy_struct &);
+ sp_lex_keeper m_lex_keeper;
+ uint m_cursor;
+ uint m_var;
+
+public:
+ sp_instr_cursor_copy_struct(uint ip, sp_pcontext *ctx, uint coffs,
+ sp_lex_cursor *lex, uint voffs)
+ : sp_instr(ip, ctx),
+ m_lex_keeper(lex, false),
+ m_cursor(coffs),
+ m_var(voffs)
+ {}
+ virtual ~sp_instr_cursor_copy_struct() = default;
+ int execute(THD *thd, uint *nextp) override;
+ int exec_core(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_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_cfetch : public sp_instr
+{
+ 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, bool error_on_no_data)
+ : sp_instr(ip, ctx),
+ m_cursor(c),
+ m_error_on_no_data(error_on_no_data)
+ {
+ m_varlist.empty();
+ }
+
+ virtual ~sp_instr_cfetch() = default;
+
+ int execute(THD *thd, uint *nextp) override;
+
+ void print(String *str) override;
+
+ void add_to_varlist(sp_variable *var)
+ {
+ m_varlist.push_back(var);
+ }
+
+private:
+ uint m_cursor;
+ List<sp_variable> m_varlist;
+ bool m_error_on_no_data;
+
+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_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_instr_opt_meta
+{
+public:
+ sp_instr_set_case_expr(uint ip, sp_pcontext *ctx, uint case_expr_id,
+ Item *case_expr, LEX *lex)
+ : sp_instr_opt_meta(ip, ctx),
+ m_case_expr_id(case_expr_id),
+ m_case_expr(case_expr),
+ m_lex_keeper(lex, true)
+ {}
+
+ 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> *ibp) override;
+
+ void set_destination(uint old_dest, uint new_dest) override
+ {
+ if (m_cont_dest == old_dest)
+ m_cont_dest= new_dest;
+ }
+
+private:
+ uint m_case_expr_id;
+ Item *m_case_expr;
+ sp_lex_keeper m_lex_keeper;
+
+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_instr_opt_meta
+
+#endif
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index d2fe53a2431..de355093c88 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -23,6 +23,7 @@
#include "mysql.h"
#include "sp_head.h"
#include "sql_cursor.h"
+#include "sp_instr.h" // class sp_instr, ...
#include "sp_rcontext.h"
#include "sp_pcontext.h"
#include "sql_select.h" // create_virtual_tmp_table
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index ab16a9d0b8b..f3c0ab06edf 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -28,6 +28,7 @@
#include <hash.h>
#include "sp_head.h"
#include "sp.h"
+#include "sp_instr.h" // class sp_instr, ...
#include "sql_select.h"
#include "sql_cte.h"
#include "sql_signal.h"
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 2927b6ce8de..929f53035f7 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -43,6 +43,7 @@
#include "lex_symbol.h"
#include "item_create.h"
#include "sp_head.h"
+#include "sp_instr.h" // classes sp_instr,...
#include "sp_rcontext.h"
#include "sp.h"
#include "sql_show.h"