summaryrefslogtreecommitdiffstats
path: root/sql/sp_head.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sql/sp_head.h2188
1 files changed, 2188 insertions, 0 deletions
diff --git a/sql/sp_head.h b/sql/sp_head.h
new file mode 100644
index 00000000..da4e5763
--- /dev/null
+++ b/sql/sp_head.h
@@ -0,0 +1,2188 @@
+/* -*- C++ -*- */
+/*
+ Copyright (c) 2002, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2020, 2022, MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#ifndef _SP_HEAD_H_
+#define _SP_HEAD_H_
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ are dependencies on include order for set_var.h and item.h. This
+ will be resolved later.
+*/
+#include "sql_class.h" // THD, set_var.h: THD
+#include "set_var.h" // Item
+#include "sp_pcontext.h" // sp_pcontext
+#include <stddef.h>
+#include "sp.h"
+
+/**
+ @defgroup Stored_Routines Stored Routines
+ @ingroup Runtime_Environment
+ @{
+*/
+
+uint
+sp_get_flags_for_command(LEX *lex);
+
+class sp_instr;
+class sp_instr_opt_meta;
+class sp_instr_jump_if_not;
+
+/**
+ Number of PSI_statement_info instruments
+ for internal stored programs statements.
+*/
+#ifdef HAVE_PSI_INTERFACE
+void init_sp_psi_keys(void);
+#endif
+
+/*************************************************************************/
+
+/**
+ Stored_program_creation_ctx -- base class for creation context of stored
+ programs (stored routines, triggers, events).
+*/
+
+class Stored_program_creation_ctx :public Default_object_creation_ctx
+{
+public:
+ CHARSET_INFO *get_db_cl()
+ {
+ return m_db_cl;
+ }
+
+public:
+ virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root) = 0;
+
+protected:
+ Stored_program_creation_ctx(THD *thd)
+ : Default_object_creation_ctx(thd),
+ m_db_cl(thd->variables.collation_database)
+ { }
+
+ Stored_program_creation_ctx(CHARSET_INFO *client_cs,
+ CHARSET_INFO *connection_cl,
+ CHARSET_INFO *db_cl)
+ : Default_object_creation_ctx(client_cs, connection_cl),
+ m_db_cl(db_cl)
+ { }
+
+protected:
+ virtual void change_env(THD *thd) const
+ {
+ thd->variables.collation_database= m_db_cl;
+
+ Default_object_creation_ctx::change_env(thd);
+ }
+
+protected:
+ /**
+ db_cl stores the value of the database collation. Both character set
+ and collation attributes are used.
+
+ Database collation is included into the context because it defines the
+ default collation for stored-program variables.
+ */
+ CHARSET_INFO *m_db_cl;
+};
+
+/*************************************************************************/
+
+class sp_name : public Sql_alloc,
+ public Database_qualified_name
+{
+public:
+ bool m_explicit_name; /**< Prepend the db name? */
+
+ sp_name(const LEX_CSTRING *db, const LEX_CSTRING *name,
+ bool use_explicit_name)
+ : Database_qualified_name(db, name), m_explicit_name(use_explicit_name)
+ {
+ if (lower_case_table_names && m_db.length)
+ m_db.length= my_casedn_str(files_charset_info, (char*) m_db.str);
+ }
+
+ /** Create temporary sp_name object from MDL key. Store in qname_buff */
+ sp_name(const MDL_key *key, char *qname_buff);
+
+ ~sp_name() = default;
+};
+
+
+bool
+check_routine_name(const LEX_CSTRING *ident);
+
+class sp_head :private Query_arena,
+ public Database_qualified_name,
+ public Sql_alloc
+{
+ sp_head(const sp_head &)= delete;
+ void operator=(sp_head &)= delete;
+
+protected:
+ MEM_ROOT main_mem_root;
+#ifdef PROTECT_STATEMENT_MEMROOT
+ /*
+ The following data member is wholly for debugging purpose.
+ It can be used for possible crash analysis to determine how many times
+ the stored routine was executed before the mem_root marked read_only
+ was requested for a memory chunk. Additionally, a value of this data
+ member is output to the log with DBUG_PRINT.
+ */
+ ulong executed_counter;
+#endif
+public:
+ /** Possible values of m_flags */
+ enum {
+ HAS_RETURN= 1, // For FUNCTIONs only: is set if has RETURN
+ MULTI_RESULTS= 8, // Is set if a procedure with SELECT(s)
+ CONTAINS_DYNAMIC_SQL= 16, // Is set if a procedure with PREPARE/EXECUTE
+ IS_INVOKED= 32, // Is set if this sp_head is being used
+ HAS_SET_AUTOCOMMIT_STMT= 64,// Is set if a procedure with 'set autocommit'
+ /* Is set if a procedure with COMMIT (implicit or explicit) | ROLLBACK */
+ HAS_COMMIT_OR_ROLLBACK= 128,
+ LOG_SLOW_STATEMENTS= 256, // Used by events
+ LOG_GENERAL_LOG= 512, // Used by events
+ HAS_SQLCOM_RESET= 1024,
+ HAS_SQLCOM_FLUSH= 2048,
+
+ /**
+ Marks routines that directly (i.e. not by calling other routines)
+ change tables. Note that this flag is set automatically based on
+ type of statements used in the stored routine and is different
+ from routine characteristic provided by user in a form of CONTAINS
+ SQL, READS SQL DATA, MODIFIES SQL DATA clauses. The latter are
+ accepted by parser but pretty much ignored after that.
+ We don't rely on them:
+ a) for compatibility reasons.
+ b) because in CONTAINS SQL case they don't provide enough
+ information anyway.
+ */
+ MODIFIES_DATA= 4096,
+ /*
+ Marks routines that have column type references: DECLARE a t1.a%TYPE;
+ */
+ HAS_COLUMN_TYPE_REFS= 8192,
+ /* Set if has FETCH GROUP NEXT ROW instr. Used to ensure that only
+ functions with AGGREGATE keyword use the instr. */
+ HAS_AGGREGATE_INSTR= 16384
+ };
+
+ sp_package *m_parent;
+ const Sp_handler *m_handler;
+ uint m_flags; // Boolean attributes of a stored routine
+
+ /**
+ Instrumentation interface for SP.
+ */
+ PSI_sp_share *m_sp_share;
+
+ Column_definition m_return_field_def; /**< This is used for FUNCTIONs only. */
+
+ const char *m_tmp_query; ///< Temporary pointer to sub query string
+private:
+ /*
+ Private to guarantee that m_chistics.comment is properly set to:
+ - a string which is alloced on this->mem_root
+ - or (NULL,0)
+ set_chistics() makes sure this.
+ */
+ Sp_chistics m_chistics;
+ void set_chistics(const st_sp_chistics &chistics);
+ inline void set_chistics_agg_type(enum enum_sp_aggregate_type type)
+ {
+ m_chistics.agg_type= type;
+ }
+public:
+ sql_mode_t m_sql_mode; ///< For SHOW CREATE and execution
+ bool m_explicit_name; /**< Prepend the db name? */
+ LEX_CSTRING m_qname; ///< db.name
+ LEX_CSTRING m_params;
+ LEX_CSTRING m_body;
+ LEX_CSTRING m_body_utf8;
+ LEX_CSTRING m_defstr;
+ AUTHID m_definer;
+
+ const st_sp_chistics &chistics() const { return m_chistics; }
+ const LEX_CSTRING &comment() const { return m_chistics.comment; }
+ void set_suid(enum_sp_suid_behaviour suid) { m_chistics.suid= suid; }
+ enum_sp_suid_behaviour suid() const { return m_chistics.suid; }
+ bool detistic() const { return m_chistics.detistic; }
+ enum_sp_data_access daccess() const { return m_chistics.daccess; }
+ enum_sp_aggregate_type agg_type() const { return m_chistics.agg_type; }
+ /**
+ Is this routine being executed?
+ */
+ virtual bool is_invoked() const { return m_flags & IS_INVOKED; }
+
+ /**
+ Get the value of the SP cache version, as remembered
+ when the routine was inserted into the cache.
+ */
+ ulong sp_cache_version() const;
+
+ /** Set the value of the SP cache version. */
+ void set_sp_cache_version(ulong version_arg) const
+ {
+ m_sp_cache_version= version_arg;
+ }
+
+ sp_rcontext *rcontext_create(THD *thd, Field *retval, List<Item> *args);
+ sp_rcontext *rcontext_create(THD *thd, Field *retval,
+ Item **args, uint arg_count);
+ sp_rcontext *rcontext_create(THD *thd, Field *retval,
+ Row_definition_list *list,
+ bool switch_security_ctx);
+ bool eq_routine_spec(const sp_head *) const;
+private:
+ /**
+ Version of the stored routine cache at the moment when the
+ routine was added to it. Is used only for functions and
+ procedures, not used for triggers or events. When sp_head is
+ created, its version is 0. When it's added to the cache, the
+ version is assigned the global value 'Cversion'.
+ If later on Cversion is incremented, we know that the routine
+ is obsolete and should not be used --
+ sp_cache_flush_obsolete() will purge it.
+ */
+ mutable ulong m_sp_cache_version;
+ Stored_program_creation_ctx *m_creation_ctx;
+ /**
+ Boolean combination of (1<<flag), where flag is a member of
+ LEX::enum_binlog_stmt_unsafe.
+ */
+ uint32 unsafe_flags;
+
+public:
+ inline Stored_program_creation_ctx *get_creation_ctx()
+ {
+ return m_creation_ctx;
+ }
+
+ inline void set_creation_ctx(Stored_program_creation_ctx *creation_ctx)
+ {
+ m_creation_ctx= creation_ctx->clone(mem_root);
+ }
+
+ longlong m_created;
+ longlong m_modified;
+ /** Recursion level of the current SP instance. The levels are numbered from 0 */
+ ulong m_recursion_level;
+ /**
+ A list of diferent recursion level instances for the same procedure.
+ For every recursion level we have a sp_head instance. This instances
+ connected in the list. The list ordered by increasing recursion level
+ (m_recursion_level).
+ */
+ sp_head *m_next_cached_sp;
+ /**
+ Pointer to the first element of the above list
+ */
+ sp_head *m_first_instance;
+ /**
+ Pointer to the first free (non-INVOKED) routine in the list of
+ cached instances for this SP. This pointer is set only for the first
+ SP in the list of instences (see above m_first_cached_sp pointer).
+ The pointer equal to 0 if we have no free instances.
+ For non-first instance value of this pointer meanless (point to itself);
+ */
+ sp_head *m_first_free_instance;
+ /**
+ Pointer to the last element in the list of instances of the SP.
+ For non-first instance value of this pointer meanless (point to itself);
+ */
+ sp_head *m_last_cached_sp;
+ /**
+ Set containing names of stored routines used by this routine.
+ Note that unlike elements of similar set for statement elements of this
+ set are not linked in one list. Because of this we are able save memory
+ by using for this set same objects that are used in 'sroutines' sets
+ for statements of which this stored routine consists.
+ */
+ HASH m_sroutines;
+ // Pointers set during parsing
+ const char *m_param_begin;
+ const char *m_param_end;
+
+private:
+ /*
+ A pointer to the body start inside the cpp buffer.
+ Used only during parsing. Should be removed eventually.
+ The affected functions/methods should be fixed to get the cpp body start
+ as a parameter, rather than through this member.
+ */
+ const char *m_cpp_body_begin;
+
+public:
+ /*
+ Security context for stored routine which should be run under
+ definer privileges.
+ */
+ Security_context m_security_ctx;
+
+ /**
+ List of all items (Item_trigger_field objects) representing fields in
+ old/new version of row in trigger. We use this list for checking whenever
+ all such fields are valid at trigger creation time and for binding these
+ fields to TABLE object at table open (although for latter pointer to table
+ being opened is probably enough).
+ */
+ SQL_I_List<Item_trigger_field> m_trg_table_fields;
+
+protected:
+ sp_head(MEM_ROOT *mem_root, sp_package *parent, const Sp_handler *handler,
+ enum_sp_aggregate_type agg_type);
+ virtual ~sp_head();
+public:
+ static void destroy(sp_head *sp);
+ static sp_head *create(sp_package *parent, const Sp_handler *handler,
+ enum_sp_aggregate_type agg_type);
+
+ /// Initialize after we have reset mem_root
+ void
+ init(LEX *lex);
+
+ /** Copy sp name from parser. */
+ void
+ init_sp_name(const sp_name *spname);
+
+ /** Set the body-definition start position. */
+ void
+ set_body_start(THD *thd, const char *cpp_body_start);
+
+ /** Set the statement-definition (body-definition) end position. */
+ void
+ set_stmt_end(THD *thd, const char *cpp_body_end);
+
+ bool
+ execute_trigger(THD *thd,
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name,
+ GRANT_INFO *grant_info);
+
+ bool
+ execute_function(THD *thd, Item **args, uint argcount, Field *return_fld,
+ sp_rcontext **nctx, Query_arena *call_arena);
+
+ bool
+ execute_procedure(THD *thd, List<Item> *args);
+
+ static void
+ show_create_routine_get_fields(THD *thd, const Sp_handler *sph,
+ List<Item> *fields);
+
+ bool
+ show_create_routine(THD *thd, const Sp_handler *sph);
+
+ MEM_ROOT *get_main_mem_root() { return &main_mem_root; }
+
+ int
+ add_instr(sp_instr *instr);
+
+ bool
+ add_instr_jump(THD *thd, sp_pcontext *spcont);
+
+ bool
+ add_instr_jump(THD *thd, sp_pcontext *spcont, uint dest);
+
+ bool
+ add_instr_jump_forward_with_backpatch(THD *thd, sp_pcontext *spcont,
+ sp_label *lab);
+ bool
+ add_instr_jump_forward_with_backpatch(THD *thd, sp_pcontext *spcont)
+ {
+ return add_instr_jump_forward_with_backpatch(thd, spcont,
+ spcont->last_label());
+ }
+
+ bool
+ add_instr_freturn(THD *thd, sp_pcontext *spcont, Item *item, LEX *lex);
+
+ bool
+ add_instr_preturn(THD *thd, sp_pcontext *spcont);
+
+ Item *adjust_assignment_source(THD *thd, Item *val, Item *val2);
+ /**
+ @param thd - the current thd
+ @param spcont - the current parse context
+ @param spv - the SP variable
+ @param val - the value to be assigned to the variable
+ @param lex - the LEX that was used to create "val"
+ @param responsible_to_free_lex - if the generated sp_instr_set should
+ free "lex".
+ @retval true - on error
+ @retval false - on success
+ */
+ bool set_local_variable(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
+ sp_variable *spv, Item *val, LEX *lex,
+ bool responsible_to_free_lex);
+ bool set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
+ sp_variable *spv, uint field_idx,
+ Item *val, LEX *lex);
+ bool set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
+ sp_variable *spv,
+ const LEX_CSTRING *field_name,
+ Item *val, LEX *lex);
+ bool check_package_routine_end_name(const LEX_CSTRING &end_name) const;
+ bool check_standalone_routine_end_name(const sp_name *end_name) const;
+ bool check_group_aggregate_instructions_function() const;
+ bool check_group_aggregate_instructions_forbid() const;
+ bool check_group_aggregate_instructions_require() const;
+private:
+ /**
+ Generate a code to set a single cursor parameter variable.
+ @param thd - current thd, for mem_root allocations.
+ @param param_spcont - the context of the parameter block
+ @param idx - the index of the parameter
+ @param prm - the actual parameter (contains information about
+ the assignment source expression Item,
+ its free list, and its LEX)
+ */
+ bool add_set_cursor_param_variable(THD *thd,
+ sp_pcontext *param_spcont, uint idx,
+ sp_assignment_lex *prm)
+ {
+ DBUG_ASSERT(idx < param_spcont->context_var_count());
+ sp_variable *spvar= param_spcont->get_context_variable(idx);
+ /*
+ add_instr() gets free_list from m_thd->free_list.
+ Initialize it before the set_local_variable() call.
+ */
+ DBUG_ASSERT(m_thd->free_list == NULL);
+ m_thd->free_list= prm->get_free_list();
+ if (set_local_variable(thd, param_spcont,
+ &sp_rcontext_handler_local,
+ spvar, prm->get_item(), prm, true))
+ return true;
+ /*
+ Safety:
+ The item and its free_list are now fully owned by the sp_instr_set
+ instance, created by set_local_variable(). The sp_instr_set instance
+ is now responsible for freeing the item and the free_list.
+ Reset the "item" and the "free_list" members of "prm",
+ to avoid double pointers to the same objects from "prm" and
+ from the sp_instr_set instance.
+ */
+ prm->set_item_and_free_list(NULL, NULL);
+ return false;
+ }
+
+ /**
+ Generate a code to set all cursor parameter variables.
+ This method is called only when parameters exists,
+ and the number of formal parameters matches the number of actual
+ parameters. See also comments to add_open_cursor().
+ */
+ bool add_set_cursor_param_variables(THD *thd, sp_pcontext *param_spcont,
+ List<sp_assignment_lex> *parameters)
+ {
+ DBUG_ASSERT(param_spcont->context_var_count() == parameters->elements);
+ sp_assignment_lex *prm;
+ List_iterator<sp_assignment_lex> li(*parameters);
+ for (uint idx= 0; (prm= li++); idx++)
+ {
+ if (add_set_cursor_param_variable(thd, param_spcont, idx, prm))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ Generate a code to set all cursor parameter variables for a FOR LOOP, e.g.:
+ FOR index IN cursor(1,2,3)
+ @param
+ */
+ bool add_set_for_loop_cursor_param_variables(THD *thd,
+ sp_pcontext *param_spcont,
+ sp_assignment_lex *param_lex,
+ Item_args *parameters);
+
+ bool bind_input_param(THD *thd,
+ Item *arg_item,
+ uint arg_no,
+ sp_rcontext *nctx,
+ bool is_function);
+
+ bool bind_output_param(THD *thd,
+ Item *arg_item,
+ uint arg_no,
+ sp_rcontext *octx,
+ sp_rcontext *nctx);
+
+public:
+ /**
+ Generate a code for an "OPEN cursor" statement.
+ @param thd - current thd, for mem_root allocations
+ @param spcont - the context of the cursor
+ @param offset - the offset of the cursor
+ @param param_spcont - the context of the cursor parameter block
+ @param parameters - the list of the OPEN actual parameters
+
+ The caller must make sure that the number of local variables
+ in "param_spcont" (formal parameters) matches the number of list elements
+ in "parameters" (actual parameters).
+ NULL in either of them means 0 parameters.
+ */
+ bool add_open_cursor(THD *thd, sp_pcontext *spcont,
+ uint offset,
+ sp_pcontext *param_spcont,
+ List<sp_assignment_lex> *parameters);
+
+ /**
+ Generate an initiation code for a CURSOR FOR LOOP, e.g.:
+ FOR index IN cursor -- cursor without parameters
+ FOR index IN cursor(1,2,3) -- cursor with parameters
+
+ The code generated by this method does the following during SP run-time:
+ - Sets all cursor parameter vartiables from "parameters"
+ - Initializes the index ROW-type variable from the cursor
+ (the structure is copied from the cursor to the index variable)
+ - The cursor gets opened
+ - The first records is fetched from the cursor to the variable "index".
+
+ @param thd - the current thread (for mem_root and error reporting)
+ @param spcont - the current parse context
+ @param index - the loop "index" ROW-type variable
+ @param pcursor - the cursor
+ @param coffset - the cursor offset
+ @param param_lex - the LEX that owns Items in "parameters"
+ @param parameters - the cursor parameters Item array
+ @retval true - on error (EOM)
+ @retval false - on success
+ */
+ bool add_for_loop_open_cursor(THD *thd, sp_pcontext *spcont,
+ sp_variable *index,
+ const sp_pcursor *pcursor, uint coffset,
+ sp_assignment_lex *param_lex,
+ Item_args *parameters);
+ /**
+ Returns true if any substatement in the routine directly
+ (not through another routine) modifies data/changes table.
+
+ @sa Comment for MODIFIES_DATA flag.
+ */
+ bool modifies_data() const
+ { return m_flags & MODIFIES_DATA; }
+
+ inline uint instructions()
+ { return (uint)m_instr.elements; }
+
+ inline sp_instr *
+ last_instruction()
+ {
+ sp_instr *i;
+
+ get_dynamic(&m_instr, (uchar*)&i, m_instr.elements-1);
+ return i;
+ }
+
+ bool replace_instr_to_nop(THD *thd, uint ip);
+
+ /*
+ Resets lex in 'thd' and keeps a copy of the old one.
+
+ @todo Conflicting comment in sp_head.cc
+ */
+ bool
+ reset_lex(THD *thd);
+
+ bool
+ reset_lex(THD *thd, sp_lex_local *sublex);
+
+ /**
+ Merge two LEX instances.
+ @param oldlex - the upper level LEX we're going to restore to.
+ @param sublex - the local lex that have just parsed some substatement.
+ @returns - false on success, true on error (e.g. failed to
+ merge the routine list or the table list).
+ This method is shared by:
+ - restore_lex(), when the old LEX is popped by sp_head::m_lex.pop()
+ - THD::restore_from_local_lex_to_old_lex(), when the old LEX
+ is stored in the caller's local variable.
+ */
+ bool
+ merge_lex(THD *thd, LEX *oldlex, LEX *sublex);
+
+ /**
+ Restores lex in 'thd' from our copy, but keeps some status from the
+ one in 'thd', like ptr, tables, fields, etc.
+
+ @todo Conflicting comment in sp_head.cc
+ */
+ bool
+ restore_lex(THD *thd)
+ {
+ DBUG_ENTER("sp_head::restore_lex");
+ /*
+ There is no a need to free the current thd->lex here.
+ - In the majority of the cases restore_lex() is called
+ on success and thd->lex does not need to be deleted.
+ - In cases when restore_lex() is called on error,
+ e.g. from sp_create_assignment_instr(), thd->lex is
+ already linked to some sp_instr_xxx (using sp_lex_keeper).
+
+ Note, we don't get to here in case of a syntax error
+ when the current thd->lex is not yet completely
+ initialized and linked. It gets automatically deleted
+ by the Bison %destructor in sql_yacc.yy.
+ */
+ LEX *oldlex= (LEX *) m_lex.pop();
+ if (!oldlex)
+ DBUG_RETURN(false); // Nothing to restore
+ // This restores thd->lex and thd->stmt_lex
+ DBUG_RETURN(thd->restore_from_local_lex_to_old_lex(oldlex));
+ }
+
+ /**
+ Iterate through the LEX stack from the top (the newest) to the bottom
+ (the oldest) and find the one that contains a non-zero spname.
+ @returns - the address of spname, or NULL of no spname found.
+ */
+ const sp_name *find_spname_recursive()
+ {
+ uint count= m_lex.elements;
+ for (uint i= 0; i < count; i++)
+ {
+ const LEX *tmp= m_lex.elem(count - i - 1);
+ if (tmp->spname)
+ return tmp->spname;
+ }
+ return NULL;
+ }
+
+ /// Put the instruction on the backpatch list, associated with the label.
+ int
+ push_backpatch(THD *thd, sp_instr *, sp_label *);
+ int
+ push_backpatch_goto(THD *thd, sp_pcontext *ctx, sp_label *lab);
+
+ /// Update all instruction with this label in the backpatch list to
+ /// the current position.
+ void
+ backpatch(sp_label *);
+ void
+ backpatch_goto(THD *thd, sp_label *, sp_label *);
+
+ /// Check for unresolved goto label
+ bool
+ check_unresolved_goto();
+
+ /// Start a new cont. backpatch level. If 'i' is NULL, the level is just incr.
+ int
+ new_cont_backpatch(sp_instr_opt_meta *i);
+
+ /// Add an instruction to the current level
+ int
+ add_cont_backpatch(sp_instr_opt_meta *i);
+
+ /// Backpatch (and pop) the current level to the current position.
+ void
+ do_cont_backpatch();
+
+ /// Add cpush instructions for all cursors declared in the current frame
+ bool sp_add_instr_cpush_for_cursors(THD *thd, sp_pcontext *pcontext);
+
+ const LEX_CSTRING *name() const
+ { return &m_name; }
+
+ char *create_string(THD *thd, ulong *lenp);
+
+ Field *create_result_field(uint field_max_length, const LEX_CSTRING *field_name,
+ TABLE *table) const;
+
+
+ /**
+ Check and prepare an instance of Column_definition for field creation
+ (fill all necessary attributes), for variables, parameters and
+ function return values.
+
+ @param[in] thd Thread handle
+ @param[in] lex Yacc parsing context
+ @param[out] field_def An instance of create_field to be filled
+
+ @retval false on success
+ @retval true on error
+ */
+ bool fill_field_definition(THD *thd, Column_definition *field_def)
+ {
+ const Type_handler *h= field_def->type_handler();
+ return h->Column_definition_fix_attributes(field_def) ||
+ field_def->sp_prepare_create_field(thd, mem_root);
+ }
+ bool row_fill_field_definitions(THD *thd, Row_definition_list *row)
+ {
+ /*
+ Prepare all row fields. This will (among other things)
+ - convert VARCHAR lengths from character length to octet length
+ - calculate interval lengths for SET and ENUM
+ */
+ List_iterator<Spvar_definition> it(*row);
+ for (Spvar_definition *def= it++; def; def= it++)
+ {
+ if (fill_spvar_definition(thd, def))
+ return true;
+ }
+ return false;
+ }
+ /**
+ Check and prepare a Column_definition for a variable or a parameter.
+ */
+ bool fill_spvar_definition(THD *thd, Column_definition *def)
+ {
+ if (fill_field_definition(thd, def))
+ return true;
+ def->pack_flag|= FIELDFLAG_MAYBE_NULL;
+ return false;
+ }
+ bool fill_spvar_definition(THD *thd, Column_definition *def,
+ LEX_CSTRING *name)
+ {
+ def->field_name= *name;
+ return fill_spvar_definition(thd, def);
+ }
+
+private:
+ /**
+ Set a column type reference for a parameter definition
+ */
+ void fill_spvar_using_type_reference(sp_variable *spvar,
+ Qualified_column_ident *ref)
+ {
+ spvar->field_def.set_column_type_ref(ref);
+ spvar->field_def.field_name= spvar->name;
+ m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
+ }
+
+ void fill_spvar_using_table_rowtype_reference(THD *thd,
+ sp_variable *spvar,
+ Table_ident *ref)
+ {
+ spvar->field_def.set_table_rowtype_ref(ref);
+ spvar->field_def.field_name= spvar->name;
+ fill_spvar_definition(thd, &spvar->field_def);
+ m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
+ }
+
+public:
+ bool spvar_fill_row(THD *thd, sp_variable *spvar, Row_definition_list *def);
+ bool spvar_fill_type_reference(THD *thd, sp_variable *spvar,
+ const LEX_CSTRING &table,
+ const LEX_CSTRING &column);
+ bool spvar_fill_type_reference(THD *thd, sp_variable *spvar,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &table,
+ const LEX_CSTRING &column);
+ bool spvar_fill_table_rowtype_reference(THD *thd, sp_variable *spvar,
+ const LEX_CSTRING &table);
+ bool spvar_fill_table_rowtype_reference(THD *thd, sp_variable *spvar,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &table);
+
+ void set_c_chistics(const st_sp_chistics &chistics);
+ void set_info(longlong created, longlong modified,
+ const st_sp_chistics &chistics, sql_mode_t sql_mode);
+
+ void set_definer(const char *definer, size_t definerlen)
+ {
+ AUTHID tmp;
+ tmp.parse(definer, definerlen);
+ m_definer.copy(mem_root, &tmp.user, &tmp.host);
+ }
+ void set_definer(const LEX_CSTRING *user_name, const LEX_CSTRING *host_name)
+ {
+ m_definer.copy(mem_root, user_name, host_name);
+ }
+
+ void reset_thd_mem_root(THD *thd);
+
+ void restore_thd_mem_root(THD *thd);
+
+ /**
+ Optimize the code.
+ */
+ void optimize();
+
+ /**
+ Helper used during flow analysis during code optimization.
+ See the implementation of <code>opt_mark()</code>.
+ @param ip the instruction to add to the leads list
+ @param leads the list of remaining paths to explore in the graph that
+ represents the code, during flow analysis.
+ */
+ void add_mark_lead(uint ip, List<sp_instr> *leads);
+
+ inline sp_instr *
+ get_instr(uint i)
+ {
+ sp_instr *ip;
+
+ if (i < m_instr.elements)
+ get_dynamic(&m_instr, (uchar*)&ip, i);
+ else
+ ip= NULL;
+ return ip;
+ }
+
+#ifdef PROTECT_STATEMENT_MEMROOT
+ int has_all_instrs_executed();
+ void reset_instrs_executed_counter();
+#endif
+
+ /* Add tables used by routine to the table list. */
+ bool add_used_tables_to_table_list(THD *thd,
+ TABLE_LIST ***query_tables_last_ptr,
+ TABLE_LIST *belong_to_view);
+
+ /**
+ Check if this stored routine contains statements disallowed
+ in a stored function or trigger, and set an appropriate error message
+ if this is the case.
+ */
+ bool is_not_allowed_in_function(const char *where)
+ {
+ if (m_flags & CONTAINS_DYNAMIC_SQL)
+ my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "Dynamic SQL");
+ else if (m_flags & MULTI_RESULTS)
+ my_error(ER_SP_NO_RETSET, MYF(0), where);
+ else if (m_flags & HAS_SET_AUTOCOMMIT_STMT)
+ my_error(ER_SP_CANT_SET_AUTOCOMMIT, MYF(0));
+ else if (m_flags & HAS_COMMIT_OR_ROLLBACK)
+ my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
+ else if (m_flags & HAS_SQLCOM_RESET)
+ my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "RESET");
+ else if (m_flags & HAS_SQLCOM_FLUSH)
+ my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH");
+
+ return MY_TEST(m_flags &
+ (CONTAINS_DYNAMIC_SQL | MULTI_RESULTS |
+ HAS_SET_AUTOCOMMIT_STMT | HAS_COMMIT_OR_ROLLBACK |
+ HAS_SQLCOM_RESET | HAS_SQLCOM_FLUSH));
+ }
+
+#ifndef DBUG_OFF
+ int show_routine_code(THD *thd);
+#endif
+
+ /*
+ This method is intended for attributes of a routine which need
+ to propagate upwards to the Query_tables_list of the caller (when
+ a property of a sp_head needs to "taint" the calling statement).
+ */
+ void propagate_attributes(Query_tables_list *prelocking_ctx)
+ {
+ DBUG_ENTER("sp_head::propagate_attributes");
+ /*
+ If this routine needs row-based binary logging, the entire top statement
+ too (we cannot switch from statement-based to row-based only for this
+ routine, as in statement-based the top-statement may be binlogged and
+ the substatements not).
+ */
+ DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x",
+ prelocking_ctx->get_stmt_unsafe_flags()));
+ DBUG_PRINT("info", ("sp_head(%p=%s)->unsafe_flags: 0x%x",
+ this, name()->str, unsafe_flags));
+ prelocking_ctx->set_stmt_unsafe_flags(unsafe_flags);
+ DBUG_VOID_RETURN;
+ }
+
+ sp_pcontext *get_parse_context() { return m_pcont; }
+
+ /*
+ Check EXECUTE access:
+ - in case of a standalone rotuine, for the routine itself
+ - in case of a package routine, for the owner package body
+ */
+ bool check_execute_access(THD *thd) const;
+
+ virtual sp_package *get_package()
+ {
+ return NULL;
+ }
+
+ virtual void init_psi_share();
+
+protected:
+
+ MEM_ROOT *m_thd_root; ///< Temp. store for thd's mem_root
+ THD *m_thd; ///< Set if we have reset mem_root
+
+ sp_pcontext *m_pcont; ///< Parse context
+ List<LEX> m_lex; ///< Temp. store for the other lex
+ DYNAMIC_ARRAY m_instr; ///< The "instructions"
+
+ enum backpatch_instr_type { GOTO, CPOP, HPOP };
+ typedef struct
+ {
+ sp_label *lab;
+ sp_instr *instr;
+ backpatch_instr_type instr_type;
+ } bp_t;
+ List<bp_t> m_backpatch; ///< Instructions needing backpatching
+ List<bp_t> m_backpatch_goto; // Instructions needing backpatching (for goto)
+
+ /**
+ We need a special list for backpatching of instructions with a continue
+ destination (in the case of a continue handler catching an error in
+ the test), since it would otherwise interfere with the normal backpatch
+ mechanism - e.g. jump_if_not instructions have two different destinations
+ which are to be patched differently.
+ Since these occur in a more restricted way (always the same "level" in
+ the code), we don't need the label.
+ */
+ List<sp_instr_opt_meta> m_cont_backpatch;
+ uint m_cont_level; // The current cont. backpatch level
+
+ /**
+ Multi-set representing optimized list of tables to be locked by this
+ routine. Does not include tables which are used by invoked routines.
+
+ @note
+ For prelocking-free SPs this multiset is constructed too.
+ We do so because the same instance of sp_head may be called both
+ in prelocked mode and in non-prelocked mode.
+ */
+ HASH m_sptabs;
+
+ bool
+ execute(THD *thd, bool merge_da_on_success);
+
+ /**
+ Perform a forward flow analysis in the generated code.
+ Mark reachable instructions, for the optimizer.
+ */
+ void opt_mark();
+
+ /**
+ Merge the list of tables used by query into the multi-set of tables used
+ by routine.
+ */
+ bool merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check);
+
+ /// Put the instruction on the a backpatch list, associated with the label.
+ int
+ push_backpatch(THD *thd, sp_instr *, sp_label *, List<bp_t> *list,
+ backpatch_instr_type itype);
+
+}; // class sp_head : public Sql_alloc
+
+
+class sp_package: public sp_head
+{
+ bool validate_public_routines(THD *thd, sp_package *spec);
+ bool validate_private_routines(THD *thd);
+public:
+ class LexList: public List<LEX>
+ {
+ public:
+ LexList() { elements= 0; }
+ // Find a package routine by a non qualified name
+ LEX *find(const LEX_CSTRING &name, enum_sp_type type);
+ // Find a package routine by a package-qualified name, e.g. 'pkg.proc'
+ LEX *find_qualified(const LEX_CSTRING &name, enum_sp_type type);
+ // Check if a routine with the given qualified name already exists
+ bool check_dup_qualified(const LEX_CSTRING &name, const Sp_handler *sph)
+ {
+ if (!find_qualified(name, sph->type()))
+ return false;
+ my_error(ER_SP_ALREADY_EXISTS, MYF(0), sph->type_str(), name.str);
+ return true;
+ }
+ bool check_dup_qualified(const sp_head *sp)
+ {
+ return check_dup_qualified(sp->m_name, sp->m_handler);
+ }
+ void cleanup();
+ };
+ /*
+ The LEX for a new package subroutine is initially assigned to
+ m_current_routine. After scanning parameters, return type and chistics,
+ the parser detects if we have a declaration or a definition, e.g.:
+ PROCEDURE p1(a INT);
+ vs
+ PROCEDURE p1(a INT) AS BEGIN NULL; END;
+ (i.e. either semicolon or the "AS" keyword)
+ m_current_routine is then added either to m_routine_implementations,
+ or m_routine_declarations, and then m_current_routine is set to NULL.
+ */
+ LEX *m_current_routine;
+ LexList m_routine_implementations;
+ LexList m_routine_declarations;
+
+ LEX *m_top_level_lex;
+ sp_rcontext *m_rcontext;
+ uint m_invoked_subroutine_count;
+ bool m_is_instantiated;
+ bool m_is_cloning_routine;
+
+private:
+ sp_package(MEM_ROOT *mem_root,
+ LEX *top_level_lex,
+ const sp_name *name,
+ const Sp_handler *sph);
+ ~sp_package();
+public:
+ static sp_package *create(LEX *top_level_lex, const sp_name *name,
+ const Sp_handler *sph);
+
+ bool add_routine_declaration(LEX *lex)
+ {
+ return m_routine_declarations.check_dup_qualified(lex->sphead) ||
+ m_routine_declarations.push_back(lex, &main_mem_root);
+ }
+ bool add_routine_implementation(LEX *lex)
+ {
+ return m_routine_implementations.check_dup_qualified(lex->sphead) ||
+ m_routine_implementations.push_back(lex, &main_mem_root);
+ }
+ sp_package *get_package() { return this; }
+ void init_psi_share();
+ bool is_invoked() const
+ {
+ /*
+ Cannot flush a package out of the SP cache when:
+ - its initialization block is running
+ - one of its subroutine is running
+ */
+ return sp_head::is_invoked() || m_invoked_subroutine_count > 0;
+ }
+ sp_variable *find_package_variable(const LEX_CSTRING *name) const
+ {
+ /*
+ sp_head::m_pcont is a special level for routine parameters.
+ Variables declared inside CREATE PACKAGE BODY reside in m_children.at(0).
+ */
+ sp_pcontext *ctx= m_pcont->child_context(0);
+ return ctx ? ctx->find_variable(name, true) : NULL;
+ }
+ bool validate_after_parser(THD *thd);
+ bool instantiate_if_needed(THD *thd);
+};
+
+
+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)
+#ifdef PROTECT_STATEMENT_MEMROOT
+ , m_has_been_run(false)
+#endif
+ {}
+
+ virtual ~sp_instr()
+ { free_items(); }
+
+
+ /**
+ Execute this instruction
+
+
+ @param thd Thread handle
+ @param[out] nextp index of the next instruction to execute. (For most
+ instructions this will be the instruction following this
+ one). Note that this parameter is undefined in case of
+ errors, use get_cont_dest() to find the continuation
+ instruction for CONTINUE error handlers.
+
+ @retval 0 on success,
+ @retval other if some error occurred
+ */
+
+ virtual int execute(THD *thd, uint *nextp) = 0;
+
+ /**
+ Execute <code>open_and_lock_tables()</code> for this statement.
+ Open and lock the tables used by this statement, as a pre-requisite
+ to execute the core logic of this instruction with
+ <code>exec_core()</code>.
+ @param thd the current thread
+ @param tables the list of tables to open and lock
+ @return zero on success, non zero on failure.
+ */
+ int exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables);
+
+ /**
+ Get the continuation destination of this instruction.
+ @return the continuation destination
+ */
+ virtual uint get_cont_dest() const;
+
+ /*
+ Execute core function of instruction after all preparations (e.g.
+ setting of proper LEX, saving part of the thread context have been
+ done).
+
+ Should be implemented for instructions using expressions or whole
+ statements (thus having to have own LEX). Used in concert with
+ sp_lex_keeper class and its descendants (there are none currently).
+ */
+ virtual int exec_core(THD *thd, uint *nextp);
+
+ virtual void print(String *str) = 0;
+
+ 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;
+
+#ifdef PROTECT_STATEMENT_MEMROOT
+ bool has_been_run() const
+ {
+ return m_has_been_run;
+ }
+
+ void mark_as_run()
+ {
+ m_has_been_run= true;
+ }
+
+ void mark_as_not_run()
+ {
+ m_has_been_run= false;
+ }
+
+private:
+ bool m_has_been_run;
+#endif
+}; // 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
+bool
+sp_change_security_context(THD *thd, sp_head *sp,
+ Security_context **backup);
+void
+sp_restore_security_context(THD *thd, Security_context *backup);
+
+bool
+set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx);
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+
+TABLE_LIST *
+sp_add_to_query_tables(THD *thd, LEX *lex,
+ const LEX_CSTRING *db, const LEX_CSTRING *name,
+ thr_lock_type locktype,
+ enum_mdl_type mdl_type);
+
+/**
+ @} (end of group Stored_Routines)
+*/
+
+#endif /* _SP_HEAD_H_ */