mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 02:46:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			742 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			742 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
 | |
|    Copyright (c) 2009, 2020, MariaDB Corporation.
 | |
| 
 | |
|    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 */
 | |
| 
 | |
| #include "mariadb.h"
 | |
| #include "sql_priv.h"
 | |
| #include "unireg.h"
 | |
| #ifdef USE_PRAGMA_IMPLEMENTATION
 | |
| #pragma implementation
 | |
| #endif
 | |
| 
 | |
| #include "sp_pcontext.h"
 | |
| #include "sp_head.h"
 | |
| 
 | |
| bool sp_condition_value::equals(const sp_condition_value *cv) const
 | |
| {
 | |
|   DBUG_ASSERT(cv);
 | |
| 
 | |
|   /*
 | |
|     The following test disallows duplicate handlers,
 | |
|     including user defined exceptions with the same WHEN clause:
 | |
|       DECLARE
 | |
|         a EXCEPTION;
 | |
|         b EXCEPTION;
 | |
|       BEGIN
 | |
|         RAUSE a;
 | |
|       EXCEPTION
 | |
|         WHEN a THEN RETURN 'a0';
 | |
|         WHEN a THEN RETURN 'a1';
 | |
|       END
 | |
|   */
 | |
|   if (this == cv)
 | |
|     return true;
 | |
| 
 | |
|   /*
 | |
|     The test below considers two conditions of the same type as equal
 | |
|     (except for the user defined exceptions) to avoid declaring duplicate
 | |
|     handlers.
 | |
| 
 | |
|     All user defined conditions have type==SQLSTATE
 | |
|     with the same SQL state and error code.
 | |
|     It's OK to have multiple user defined conditions:
 | |
|     DECLARE
 | |
|       a EXCEPTION;
 | |
|       b EXCEPTION;
 | |
|     BEGIN
 | |
|       RAISE a;
 | |
|     EXCEPTION
 | |
|       WHEN a THEN RETURN 'a';
 | |
|       WHEN b THEN RETURN 'b';
 | |
|     END;
 | |
|   */
 | |
|   if (type != cv->type || m_is_user_defined || cv->m_is_user_defined)
 | |
|     return false;
 | |
| 
 | |
|   switch (type)
 | |
|   {
 | |
|   case sp_condition_value::ERROR_CODE:
 | |
|     return (get_sql_errno() == cv->get_sql_errno());
 | |
| 
 | |
|   case sp_condition_value::SQLSTATE:
 | |
|     return Sql_state::eq(cv);
 | |
| 
 | |
|   default:
 | |
|     return true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void sp_pcontext::init(uint var_offset,
 | |
|                        uint cursor_offset,
 | |
|                        int num_case_expressions)
 | |
| {
 | |
|   m_var_offset= var_offset;
 | |
|   m_cursor_offset= cursor_offset;
 | |
|   m_num_case_exprs= num_case_expressions;
 | |
| 
 | |
|   m_labels.empty();
 | |
|   m_goto_labels.empty();
 | |
| }
 | |
| 
 | |
| 
 | |
| sp_pcontext::sp_pcontext()
 | |
|   : Sql_alloc(),
 | |
|   m_max_var_index(0), m_max_cursor_index(0),
 | |
|   m_parent(NULL), m_pboundary(0),
 | |
|   m_vars(PSI_INSTRUMENT_MEM), m_case_expr_ids(PSI_INSTRUMENT_MEM),
 | |
|   m_conditions(PSI_INSTRUMENT_MEM), m_cursors(PSI_INSTRUMENT_MEM),
 | |
|   m_handlers(PSI_INSTRUMENT_MEM), m_children(PSI_INSTRUMENT_MEM),
 | |
|   m_scope(REGULAR_SCOPE)
 | |
| {
 | |
|   init(0, 0, 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| sp_pcontext::sp_pcontext(sp_pcontext *prev, sp_pcontext::enum_scope scope)
 | |
|   : Sql_alloc(),
 | |
|   m_max_var_index(0), m_max_cursor_index(0),
 | |
|   m_parent(prev), m_pboundary(0),
 | |
|   m_vars(PSI_INSTRUMENT_MEM), m_case_expr_ids(PSI_INSTRUMENT_MEM),
 | |
|   m_conditions(PSI_INSTRUMENT_MEM), m_cursors(PSI_INSTRUMENT_MEM),
 | |
|   m_handlers(PSI_INSTRUMENT_MEM), m_children(PSI_INSTRUMENT_MEM),
 | |
|   m_scope(scope)
 | |
| {
 | |
|   init(prev->m_var_offset + prev->m_max_var_index,
 | |
|        prev->current_cursor_count(),
 | |
|        prev->get_num_case_exprs());
 | |
| }
 | |
| 
 | |
| 
 | |
| sp_pcontext::~sp_pcontext()
 | |
| {
 | |
|   for (size_t i= 0; i < m_children.elements(); ++i)
 | |
|     delete m_children.at(i);
 | |
| }
 | |
| 
 | |
| 
 | |
| sp_pcontext *sp_pcontext::push_context(THD *thd, sp_pcontext::enum_scope scope)
 | |
| {
 | |
|   sp_pcontext *child= new (thd->mem_root) sp_pcontext(this, scope);
 | |
| 
 | |
|   if (child)
 | |
|     m_children.append(child);
 | |
|   return child;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool cmp_labels(sp_label *a, sp_label *b)
 | |
| {
 | |
|   return (lex_string_cmp(system_charset_info, &a->name, &b->name) == 0 &&
 | |
|           a->type == b->type);
 | |
| }
 | |
| 
 | |
| sp_pcontext *sp_pcontext::pop_context()
 | |
| {
 | |
|   m_parent->m_max_var_index+= m_max_var_index;
 | |
| 
 | |
|   uint submax= max_cursor_index();
 | |
|   if (submax > m_parent->m_max_cursor_index)
 | |
|     m_parent->m_max_cursor_index= submax;
 | |
| 
 | |
|   if (m_num_case_exprs > m_parent->m_num_case_exprs)
 | |
|     m_parent->m_num_case_exprs= m_num_case_exprs;
 | |
| 
 | |
|   /*
 | |
|   ** Push unresolved goto label to parent context
 | |
|   */
 | |
|   sp_label *label;
 | |
|   List_iterator_fast<sp_label> li(m_goto_labels);
 | |
|   while ((label= li++))
 | |
|   {
 | |
|     if (label->ip == 0)
 | |
|     {
 | |
|       m_parent->m_goto_labels.add_unique(label, &cmp_labels);
 | |
|     }
 | |
|   }
 | |
|   return m_parent;
 | |
| }
 | |
| 
 | |
| 
 | |
| uint sp_pcontext::diff_handlers(const sp_pcontext *ctx, bool exclusive) const
 | |
| {
 | |
|   uint n= 0;
 | |
|   const sp_pcontext *pctx= this;
 | |
|   const sp_pcontext *last_ctx= NULL;
 | |
| 
 | |
|   while (pctx && pctx != ctx)
 | |
|   {
 | |
|     n+= (uint)pctx->m_handlers.elements();
 | |
|     last_ctx= pctx;
 | |
|     pctx= pctx->parent_context();
 | |
|   }
 | |
|   if (pctx)
 | |
|     return (exclusive && last_ctx ? n -(uint) last_ctx->m_handlers.elements() : n);
 | |
|   return 0;			// Didn't find ctx
 | |
| }
 | |
| 
 | |
| 
 | |
| uint sp_pcontext::diff_cursors(const sp_pcontext *ctx, bool exclusive) const
 | |
| {
 | |
|   uint n= 0;
 | |
|   const sp_pcontext *pctx= this;
 | |
|   const sp_pcontext *last_ctx= NULL;
 | |
| 
 | |
|   while (pctx && pctx != ctx)
 | |
|   {
 | |
|     n+= (uint)pctx->m_cursors.elements();
 | |
|     last_ctx= pctx;
 | |
|     pctx= pctx->parent_context();
 | |
|   }
 | |
|   if (pctx)
 | |
|     return  (exclusive && last_ctx ? (uint)(n - last_ctx->m_cursors.elements()) : n);
 | |
|   return 0;			// Didn't find ctx
 | |
| }
 | |
| 
 | |
| 
 | |
| sp_variable *sp_pcontext::find_variable(const LEX_CSTRING *name,
 | |
|                                         bool current_scope_only) const
 | |
| {
 | |
|   size_t i= m_vars.elements() - m_pboundary;
 | |
| 
 | |
|   while (i--)
 | |
|   {
 | |
|     sp_variable *p= m_vars.at(i);
 | |
| 
 | |
|     if (system_charset_info->strnncoll(name->str, name->length,
 | |
| 		                       p->name.str, p->name.length) == 0)
 | |
|     {
 | |
|       return p;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return (!current_scope_only && m_parent) ?
 | |
|     m_parent->find_variable(name, false) :
 | |
|     NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Find a variable by its run-time offset.
 | |
|   If the variable with a desired run-time offset is not found in this
 | |
|   context frame, it's recursively searched on parent context frames.
 | |
| 
 | |
|   Note, context frames can have holes:
 | |
|     CREATE PROCEDURE p1() AS
 | |
|       x0 INT:=100;
 | |
|       CURSOR cur(p0 INT, p1 INT) IS SELECT p0, p1;
 | |
|       x1 INT:=101;
 | |
|     BEGIN
 | |
|       ...
 | |
|     END;
 | |
|   The variables (x0 and x1) and the cursor parameters (p0 and p1)
 | |
|   reside in separate parse context frames.
 | |
| 
 | |
|   The variables reside on the top level parse context frame:
 | |
|   - x0 has frame offset 0 and run-time offset 0
 | |
|   - x1 has frame offset 1 and run-time offset 3
 | |
| 
 | |
|   The cursor parameters reside on the second level parse context frame:
 | |
|   - p0 has frame offset 0 and run-time offset 1
 | |
|   - p1 has frame offset 1 and run-time offset 2
 | |
| 
 | |
|   Run-time offsets on a frame can have holes, but offsets monotonocally grow,
 | |
|   so run-time offsets of all variables are not greater than the run-time offset
 | |
|   of the very last variable in this frame.
 | |
| */
 | |
| sp_variable *sp_pcontext::find_variable(uint offset) const
 | |
| {
 | |
|   if (m_var_offset <= offset &&
 | |
|       m_vars.elements() &&
 | |
|       offset <= get_last_context_variable()->offset)
 | |
|   {
 | |
|     for (uint i= 0; i < m_vars.elements(); i++)
 | |
|     {
 | |
|       if (m_vars.at(i)->offset == offset)
 | |
|         return m_vars.at(i); // This frame
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return m_parent ?
 | |
|          m_parent->find_variable(offset) :    // Some previous frame
 | |
|          NULL;                                // Index out of bounds
 | |
| }
 | |
| 
 | |
| 
 | |
| sp_variable *sp_pcontext::add_variable(THD *thd, const LEX_CSTRING *name)
 | |
| {
 | |
|   sp_variable *p=
 | |
|     new (thd->mem_root) sp_variable(name, m_var_offset + m_max_var_index);
 | |
| 
 | |
|   if (!p)
 | |
|     return NULL;
 | |
| 
 | |
|   ++m_max_var_index;
 | |
| 
 | |
|   return m_vars.append(p) ? NULL : p;
 | |
| }
 | |
| 
 | |
| sp_label *sp_pcontext::push_label(THD *thd, const LEX_CSTRING *name, uint ip,
 | |
|                                   sp_label::enum_type type,
 | |
|                                   List<sp_label> *list)
 | |
| {
 | |
|   sp_label *label=
 | |
|     new (thd->mem_root) sp_label(name, ip, type, this);
 | |
| 
 | |
|   if (!label)
 | |
|     return NULL;
 | |
| 
 | |
|   list->push_front(label, thd->mem_root);
 | |
| 
 | |
|   return label;
 | |
| }
 | |
| 
 | |
| sp_label *sp_pcontext::find_goto_label(const LEX_CSTRING *name, bool recusive)
 | |
| {
 | |
|   List_iterator_fast<sp_label> li(m_goto_labels);
 | |
|   sp_label *lab;
 | |
| 
 | |
|   while ((lab= li++))
 | |
|   {
 | |
|     if (lex_string_cmp(system_charset_info, name, &lab->name) == 0)
 | |
|       return lab;
 | |
|   }
 | |
| 
 | |
|   if (!recusive)
 | |
|     return NULL;
 | |
| 
 | |
|   /*
 | |
|     Note about exception handlers.
 | |
|     See SQL:2003 SQL/PSM (ISO/IEC 9075-4:2003),
 | |
|     section 13.1 <compound statement>,
 | |
|     syntax rule 4.
 | |
|     In short, a DECLARE HANDLER block can not refer
 | |
|     to labels from the parent context, as they are out of scope.
 | |
|   */
 | |
|   if (m_scope == HANDLER_SCOPE && m_parent)
 | |
|   {
 | |
|     if (m_parent->m_parent)
 | |
|     {
 | |
|       // Skip the parent context
 | |
|       return m_parent->m_parent->find_goto_label(name);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return m_parent && (m_scope == REGULAR_SCOPE) ?
 | |
|          m_parent->find_goto_label(name) :
 | |
|          NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| sp_label *sp_pcontext::find_label(const LEX_CSTRING *name)
 | |
| {
 | |
|   List_iterator_fast<sp_label> li(m_labels);
 | |
|   sp_label *lab;
 | |
| 
 | |
|   while ((lab= li++))
 | |
|   {
 | |
|     if (lex_string_cmp(system_charset_info, name, &lab->name) == 0)
 | |
|       return lab;
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     Note about exception handlers.
 | |
|     See SQL:2003 SQL/PSM (ISO/IEC 9075-4:2003),
 | |
|     section 13.1 <compound statement>,
 | |
|     syntax rule 4.
 | |
|     In short, a DECLARE HANDLER block can not refer
 | |
|     to labels from the parent context, as they are out of scope.
 | |
|   */
 | |
|   return (m_parent && (m_scope == REGULAR_SCOPE)) ?
 | |
|          m_parent->find_label(name) :
 | |
|          NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| sp_label *sp_pcontext::find_label_current_loop_start()
 | |
| {
 | |
|   List_iterator_fast<sp_label> li(m_labels);
 | |
|   sp_label *lab;
 | |
| 
 | |
|   while ((lab= li++))
 | |
|   {
 | |
|     if (lab->type == sp_label::ITERATION)
 | |
|       return lab;
 | |
|   }
 | |
|   // See a comment in sp_pcontext::find_label()
 | |
|   return (m_parent && (m_scope == REGULAR_SCOPE)) ?
 | |
|          m_parent->find_label_current_loop_start() :
 | |
|          NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool sp_pcontext::add_condition(THD *thd,
 | |
|                                 const LEX_CSTRING *name,
 | |
|                                 sp_condition_value *value)
 | |
| {
 | |
|   sp_condition *p= new (thd->mem_root) sp_condition(name, value);
 | |
| 
 | |
|   if (p == NULL)
 | |
|     return true;
 | |
| 
 | |
|   return m_conditions.append(p);
 | |
| }
 | |
| 
 | |
| 
 | |
| sp_condition_value *sp_pcontext::find_condition(const LEX_CSTRING *name,
 | |
|                                                 bool current_scope_only) const
 | |
| {
 | |
|   size_t i= m_conditions.elements();
 | |
| 
 | |
|   while (i--)
 | |
|   {
 | |
|     sp_condition *p= m_conditions.at(i);
 | |
| 
 | |
|     if (p->eq_name(name))
 | |
|     {
 | |
|       return p->value;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return (!current_scope_only && m_parent) ?
 | |
|     m_parent->find_condition(name, false) :
 | |
|     NULL;
 | |
| }
 | |
| 
 | |
| sp_condition_value *
 | |
| sp_pcontext::find_declared_or_predefined_condition(THD *thd,
 | |
|                                                    const LEX_CSTRING *name)
 | |
|                                                    const
 | |
| {
 | |
|   sp_condition_value *p= find_condition(name, false);
 | |
|   if (p)
 | |
|     return p;
 | |
|   if (thd->variables.sql_mode & MODE_ORACLE)
 | |
|     return find_predefined_condition(name);
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| static sp_condition_value
 | |
|   // Warnings
 | |
|   cond_no_data_found(ER_SP_FETCH_NO_DATA, "01000"),
 | |
|   // Errors
 | |
|   cond_invalid_cursor(ER_SP_CURSOR_NOT_OPEN, "24000"),
 | |
|   cond_dup_val_on_index(ER_DUP_ENTRY, "23000"),
 | |
|   cond_dup_val_on_index2(ER_DUP_ENTRY_WITH_KEY_NAME, "23000"),
 | |
|   cond_too_many_rows(ER_TOO_MANY_ROWS, "42000");
 | |
| 
 | |
| 
 | |
| static sp_condition sp_predefined_conditions[]=
 | |
| {
 | |
|   // Warnings
 | |
|   sp_condition(STRING_WITH_LEN("NO_DATA_FOUND"), &cond_no_data_found),
 | |
|   // Errors
 | |
|   sp_condition(STRING_WITH_LEN("INVALID_CURSOR"), &cond_invalid_cursor),
 | |
|   sp_condition(STRING_WITH_LEN("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index),
 | |
|   sp_condition(STRING_WITH_LEN("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index2),
 | |
|   sp_condition(STRING_WITH_LEN("TOO_MANY_ROWS"), &cond_too_many_rows)
 | |
| };
 | |
| 
 | |
| 
 | |
| sp_condition_value *
 | |
| sp_pcontext::find_predefined_condition(const LEX_CSTRING *name) const
 | |
| {
 | |
|   for (uint i= 0; i < array_elements(sp_predefined_conditions) ; i++)
 | |
|   {
 | |
|     if (sp_predefined_conditions[i].eq_name(name))
 | |
|       return sp_predefined_conditions[i].value;
 | |
|   }
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| sp_handler *sp_pcontext::add_handler(THD *thd,
 | |
|                                      sp_handler::enum_type type)
 | |
| {
 | |
|   sp_handler *h= new (thd->mem_root) sp_handler(type);
 | |
| 
 | |
|   if (!h)
 | |
|     return NULL;
 | |
| 
 | |
|   return m_handlers.append(h) ? NULL : h;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool sp_pcontext::check_duplicate_handler(
 | |
|   const sp_condition_value *cond_value) const
 | |
| {
 | |
|   for (size_t i= 0; i < m_handlers.elements(); ++i)
 | |
|   {
 | |
|     sp_handler *h= m_handlers.at(i);
 | |
| 
 | |
|     List_iterator_fast<sp_condition_value> li(h->condition_values);
 | |
|     sp_condition_value *cv;
 | |
| 
 | |
|     while ((cv= li++))
 | |
|     {
 | |
|       if (cond_value->equals(cv))
 | |
|         return true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool sp_condition_value::matches(const Sql_condition_identity &value,
 | |
|                                  const sp_condition_value *found_cv) const
 | |
| {
 | |
|   bool user_value_matched= !value.get_user_condition_value() ||
 | |
|                            this == value.get_user_condition_value();
 | |
| 
 | |
|   switch (type)
 | |
|   {
 | |
|   case sp_condition_value::ERROR_CODE:
 | |
|     return user_value_matched &&
 | |
|            value.get_sql_errno() == get_sql_errno() &&
 | |
|            (!found_cv || found_cv->type > sp_condition_value::ERROR_CODE);
 | |
| 
 | |
|   case sp_condition_value::SQLSTATE:
 | |
|     return user_value_matched &&
 | |
|            Sql_state::eq(&value) &&
 | |
|            (!found_cv || found_cv->type > sp_condition_value::SQLSTATE);
 | |
| 
 | |
|   case sp_condition_value::WARNING:
 | |
|     return user_value_matched &&
 | |
|            (value.Sql_state::is_warning() ||
 | |
|             value.get_level() == Sql_condition::WARN_LEVEL_WARN) &&
 | |
|            !found_cv;
 | |
| 
 | |
|   case sp_condition_value::NOT_FOUND:
 | |
|     return user_value_matched &&
 | |
|            value.Sql_state::is_not_found() &&
 | |
|            !found_cv;
 | |
| 
 | |
|   case sp_condition_value::EXCEPTION:
 | |
|     /*
 | |
|       In sql_mode=ORACLE this construct should catch both errors and warnings:
 | |
|         EXCEPTION
 | |
|           WHEN OTHERS THEN ...;
 | |
|       E.g. NO_DATA_FOUND is more like a warning than an error,
 | |
|       and it should be caught.
 | |
| 
 | |
|       We don't check user_value_matched here.
 | |
|       "WHEN OTHERS" catches all user defined exception.
 | |
|     */
 | |
|     return (((current_thd->variables.sql_mode & MODE_ORACLE) ||
 | |
|            (value.Sql_state::is_exception() &&
 | |
|             value.get_level() == Sql_condition::WARN_LEVEL_ERROR)) &&
 | |
|            !found_cv);
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| sp_handler*
 | |
| sp_pcontext::find_handler(const Sql_condition_identity &value) const
 | |
| {
 | |
|   sp_handler *found_handler= NULL;
 | |
|   sp_condition_value *found_cv= NULL;
 | |
| 
 | |
|   for (size_t i= 0; i < m_handlers.elements(); ++i)
 | |
|   {
 | |
|     sp_handler *h= m_handlers.at(i);
 | |
| 
 | |
|     List_iterator_fast<sp_condition_value> li(h->condition_values);
 | |
|     sp_condition_value *cv;
 | |
| 
 | |
|     while ((cv= li++))
 | |
|     {
 | |
|       if (cv->matches(value, found_cv))
 | |
|       {
 | |
|         found_cv= cv;
 | |
|         found_handler= h;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (found_handler)
 | |
|     return found_handler;
 | |
| 
 | |
| 
 | |
|   // There is no appropriate handler in this parsing context. We need to look up
 | |
|   // in parent contexts. There might be two cases here:
 | |
|   //
 | |
|   // 1. The current context has REGULAR_SCOPE. That means, it's a simple
 | |
|   // BEGIN..END block:
 | |
|   //     ...
 | |
|   //     BEGIN
 | |
|   //       ... # We're here.
 | |
|   //     END
 | |
|   //     ...
 | |
|   // In this case we simply call find_handler() on parent's context recursively.
 | |
|   //
 | |
|   // 2. The current context has HANDLER_SCOPE. That means, we're inside an
 | |
|   // SQL-handler block:
 | |
|   //   ...
 | |
|   //   DECLARE ... HANDLER FOR ...
 | |
|   //   BEGIN
 | |
|   //     ... # We're here.
 | |
|   //   END
 | |
|   //   ...
 | |
|   // In this case we can not just call parent's find_handler(), because
 | |
|   // parent's handler don't catch conditions from this scope. Instead, we should
 | |
|   // try to find first parent context (we might have nested handler
 | |
|   // declarations), which has REGULAR_SCOPE (i.e. which is regular BEGIN..END
 | |
|   // block).
 | |
| 
 | |
|   const sp_pcontext *p= this;
 | |
| 
 | |
|   while (p && p->m_scope == HANDLER_SCOPE)
 | |
|     p= p->m_parent;
 | |
| 
 | |
|   if (!p || !p->m_parent)
 | |
|     return NULL;
 | |
| 
 | |
|   return p->m_parent->find_handler(value);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool sp_pcontext::add_cursor(const LEX_CSTRING *name, sp_pcontext *param_ctx,
 | |
|                              sp_lex_cursor *lex)
 | |
| {
 | |
|   if (m_cursors.elements() == m_max_cursor_index)
 | |
|     ++m_max_cursor_index;
 | |
| 
 | |
|   return m_cursors.append(sp_pcursor(name, param_ctx, lex));
 | |
| }
 | |
| 
 | |
| 
 | |
| const sp_pcursor *sp_pcontext::find_cursor(const LEX_CSTRING *name,
 | |
|                                            uint *poff,
 | |
|                                            bool current_scope_only) const
 | |
| {
 | |
|   uint i= (uint)m_cursors.elements();
 | |
| 
 | |
|   while (i--)
 | |
|   {
 | |
|     LEX_CSTRING n= m_cursors.at(i);
 | |
| 
 | |
|     if (system_charset_info->strnncoll(name->str, name->length,
 | |
| 		                       n.str, n.length) == 0)
 | |
|     {
 | |
|       *poff= m_cursor_offset + i;
 | |
|       return &m_cursors.at(i);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return (!current_scope_only && m_parent) ?
 | |
|     m_parent->find_cursor(name, poff, false) :
 | |
|     NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| void sp_pcontext::retrieve_field_definitions(
 | |
|   List<Spvar_definition> *field_def_lst) const
 | |
| {
 | |
|   /* Put local/context fields in the result list. */
 | |
| 
 | |
|   size_t next_child= 0;
 | |
|   for (size_t i= 0; i < m_vars.elements(); ++i)
 | |
|   {
 | |
|     sp_variable *var_def= m_vars.at(i);
 | |
| 
 | |
|     /*
 | |
|       The context can have holes in run-time offsets,
 | |
|       the missing offsets reside on the children contexts in such cases.
 | |
|       Example:
 | |
|         CREATE PROCEDURE p1() AS
 | |
|           x0 INT:=100;        -- context 0, position 0, run-time 0
 | |
|           CURSOR cur(
 | |
|             p0 INT,           -- context 1, position 0, run-time 1
 | |
|             p1 INT            -- context 1, position 1, run-time 2
 | |
|           ) IS SELECT p0, p1;
 | |
|           x1 INT:=101;        -- context 0, position 1, run-time 3
 | |
|         BEGIN
 | |
|           ...
 | |
|         END;
 | |
|       See more comments in sp_pcontext::find_variable().
 | |
|       We must retrieve the definitions in the order of their run-time offsets.
 | |
|       Check that there are children that should go before the current variable.
 | |
|     */
 | |
|     for ( ; next_child < m_children.elements(); next_child++)
 | |
|     {
 | |
|       sp_pcontext *child= m_children.at(next_child);
 | |
|       if (!child->context_var_count() ||
 | |
|           child->get_context_variable(0)->offset > var_def->offset)
 | |
|         break;
 | |
|       /*
 | |
|         All variables on the embedded context (that fills holes of the parent)
 | |
|         should have the run-time offset strictly less than var_def.
 | |
|       */
 | |
|       DBUG_ASSERT(child->get_context_variable(0)->offset < var_def->offset);
 | |
|       DBUG_ASSERT(child->get_last_context_variable()->offset < var_def->offset);
 | |
|       child->retrieve_field_definitions(field_def_lst);
 | |
|     }
 | |
|     field_def_lst->push_back(&var_def->field_def);
 | |
|   }
 | |
| 
 | |
|   /* Put the fields of the remaining enclosed contexts in the result list. */
 | |
| 
 | |
|   for (size_t i= next_child; i < m_children.elements(); ++i)
 | |
|     m_children.at(i)->retrieve_field_definitions(field_def_lst);
 | |
| }
 | |
| 
 | |
| 
 | |
| const sp_pcursor *sp_pcontext::find_cursor(uint offset) const
 | |
| {
 | |
|   if (m_cursor_offset <= offset &&
 | |
|       offset < m_cursor_offset + m_cursors.elements())
 | |
|   {
 | |
|     return &m_cursors.at(offset - m_cursor_offset);   // This frame
 | |
|   }
 | |
| 
 | |
|   return m_parent ?
 | |
|          m_parent->find_cursor(offset) :  // Some previous frame
 | |
|          NULL;                            // Index out of bounds
 | |
| }
 | |
| 
 | |
| 
 | |
| bool sp_pcursor::check_param_count_with_error(uint param_count) const
 | |
| {
 | |
|   if (param_count != (m_param_context ?
 | |
|                       m_param_context->context_var_count() : 0))
 | |
|   {
 | |
|     my_error(ER_WRONG_PARAMCOUNT_TO_CURSOR, MYF(0), LEX_CSTRING::str);
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| const Spvar_definition *
 | |
| sp_variable::find_row_field(const LEX_CSTRING *var_name,
 | |
|                             const LEX_CSTRING *field_name,
 | |
|                             uint *row_field_offset)
 | |
| {
 | |
|   if (!field_def.is_row())
 | |
|   {
 | |
|     my_printf_error(ER_UNKNOWN_ERROR,
 | |
|                     "'%s' is not a row variable", MYF(0), var_name->str);
 | |
|     return NULL;
 | |
|   }
 | |
|   const Spvar_definition *def;
 | |
|   if ((def= field_def.find_row_field_by_name(field_name, row_field_offset)))
 | |
|     return def;
 | |
|   my_error(ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD, MYF(0),
 | |
|            var_name->str, field_name->str);
 | |
|   return NULL;
 | |
| }
 | 
