mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
SET sql_mode=ORACLE;
DELIMITER $$
CREATE OR REPLACE PACKAGE pkg AS
-- Declare a package public data type
TYPE varchar_array IS TABLE OF VARCHAR(2000) INDEX BY INTEGER;
END;
$$
DELIMITER ;
DELIMITER $$
CREATE OR REPLACE PROCEDURE p1 AS
v pkg.varchar_array; -- Use the package public data type
BEGIN
v(0):='test';
SELECT v(0);
END;
$$
DELIMITER ;
Note, the change is done only for sql_mode=ORACLE, because the TYPE
declaration is not available for the default mode.
Where package-wide types are available
--------------------------------------
- Variabe list type:
DECLARE var pkg1.type1;
- RETURN type for a package routine:
CREATE FUNCTION .. RETURN pkg1.type1 ...
- Parameter type for a package routine:
PROCEDURE p1(param1 pkg1.type1);
- Assoc array element type:
TYPE assoc1_t IS TABLE OF pkg1.type1 ...
- REF CURSOR RETURN type:
TYPE cur1_t IS REF CURSOR RETURN pkg1.type1;
Change details
--------------
- Adding a member Lex_length_and_dec_st::m_foreign_module_type
It's set to true when the data type was initialized from a TYPE
in foreign routine (e.g. in PACKAGE spec).
It's needed to prevent use of qualified identifiers in public contexts,
i.e. in schema public routine parameter types and schema publuc function
RETURN types.
Adding a helper method sp_head::check_applicability() which prevents
use of qualified types in public context.
- Adding a helper method sp_head::raise_unknown_data_type().
- Adding methods LEX::set_field_type_typedef_package_spec() for
2-step and 3-step qualified indentifiers.
It's used in field_type_all_with_typedefs which covers cases:
- Variabe list type : DECLARE var pkg1.type1;
- RETURN type : CREATE FUNCTION .. RETURN pkg1.type1 ...
- Parameter type : PROCEDURE p1(param1 pkg1.type1);
- Assoc array element type : TYPE assoc1_t IS TABLE OF pkg1.type1 ...
- Adding a method LEX::declare_type_ref_cursor_return_typedef().
It handles cases when a new TYPE REF CURSOR RETURN is declared,
for both for qualified RETURN types and non-qualified RETURN types:
- TYPE cur0_t IS REF CURSOR RETURN rec1_t;
- TYPE cur0_t IS REF CURSOR RETURN pkg1.rec1_t;
- TYPE cur0_t IS REF CURSOR RETURN db1.pkg1.rec1_t;
The code was moved from LEX::declare_type_ref_cursor() into
LEX::declare_type_ref_cursor_return_typedef() and extended
to cover qualified RETURN types.
- Adding a method Sql_path::find_package_spec_type().
It iterates through all schemas specified in @@path and searches
for the given type in the given package.
- Adding a helper method sp_pcontext::type_defs_add_ref_cursor()
to reuse the code.
- Adding a new method sp_package::get_typedef() to search
for TYPE definitions in PACKAGE specifications.
- Adding a new method sp_head::get_typedef_package_spec()
to search for TYPE definitions used by a PROCEDURE or FUNCTION.
- Adding a helper method
Sp_handler::sp_cache_routine_reentrant_suppress_errors
Adding a method Sp_handler::find_package_spec().
819 lines
21 KiB
C++
819 lines
21 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"
|
|
#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;
|
|
}
|
|
}
|
|
|
|
|
|
sp_type_def_list::sp_type_def_list()
|
|
:m_type_defs(PSI_INSTRUMENT_MEM)
|
|
{ }
|
|
|
|
|
|
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 a->type == b->type && a->name.streq(b->name);
|
|
}
|
|
|
|
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 (p->name.streq(*name))
|
|
{
|
|
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 monotonically 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
|
|
}
|
|
|
|
|
|
uint sp_pcontext::default_context_var_count() const
|
|
{
|
|
uint default_params= 0;
|
|
for (uint i= 0; i< context_var_count(); i++)
|
|
{
|
|
sp_variable *spvar= get_context_variable(i);
|
|
if (!spvar)
|
|
break;
|
|
|
|
if (spvar->default_value)
|
|
default_params++;
|
|
}
|
|
|
|
return default_params;
|
|
}
|
|
|
|
|
|
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 (lab->name.streq(*name))
|
|
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 (lab->name.streq(*name))
|
|
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_ident_column &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_type_def *sp_pcontext::find_type_def(const LEX_CSTRING &name,
|
|
bool current_scope_only) const
|
|
{
|
|
auto p= sp_type_def_list::find_type_def(name);
|
|
if (p)
|
|
return p;
|
|
|
|
return (!current_scope_only && m_parent) ?
|
|
m_parent->find_type_def(name, false) :
|
|
NULL;
|
|
}
|
|
|
|
|
|
bool sp_pcontext::type_defs_add_ref_cursor(THD *thd,
|
|
const Lex_ident_column &name,
|
|
const Type_handler *th,
|
|
const Spvar_definition &return_def,
|
|
bool is_prepared)
|
|
{
|
|
sp_type_def_ref *tdef= new (thd->mem_root) sp_type_def_ref(name, th,
|
|
return_def, is_prepared);
|
|
if (unlikely(!tdef || type_defs_add(thd, tdef)))
|
|
return true; // EOM
|
|
return false;
|
|
}
|
|
|
|
|
|
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("NO_DATA_FOUND"_Lex_ident_column, &cond_no_data_found),
|
|
// Errors
|
|
sp_condition("INVALID_CURSOR"_Lex_ident_column, &cond_invalid_cursor),
|
|
sp_condition("DUP_VAL_ON_INDEX"_Lex_ident_column, &cond_dup_val_on_index),
|
|
sp_condition("DUP_VAL_ON_INDEX"_Lex_ident_column, &cond_dup_val_on_index2),
|
|
sp_condition("TOO_MANY_ROWS"_Lex_ident_column, &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--)
|
|
{
|
|
if (m_cursors.at(i).streq(*name))
|
|
{
|
|
*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
|
|
{
|
|
size_t next_child_idx= 0;
|
|
size_t next_variable_idx= 0;
|
|
|
|
for ( ; ; )
|
|
{
|
|
if (next_variable_idx >= m_vars.elements())
|
|
{
|
|
// No variables left, just retrieve the remaining children contexts
|
|
for (size_t i= next_child_idx; i < m_children.elements(); ++i)
|
|
m_children.at(i)->retrieve_field_definitions(field_def_lst);
|
|
return;
|
|
}
|
|
if (next_child_idx >= m_children.elements())
|
|
{
|
|
// No child contexts left, just retrieve the remaining variables
|
|
for (size_t i= next_variable_idx; i < m_vars.elements(); ++i)
|
|
field_def_lst->push_back(&m_vars.at(i)->field_def);
|
|
return;
|
|
}
|
|
|
|
sp_variable *next_variable= m_vars.at(next_variable_idx);
|
|
sp_pcontext *next_child= m_children.at(next_child_idx);
|
|
|
|
if (next_variable->offset < next_child->first_variable_offset())
|
|
{
|
|
/*
|
|
The next variable has a smaller offset than variables on the next
|
|
child contexts - retrieve the variable.
|
|
We can be here in the most frequent case:
|
|
BEGIN
|
|
DECLARE x0 INT; -- Rertieve x0 (not the child context with x1)
|
|
BEGIN
|
|
DECLARE x1 INT;
|
|
END;
|
|
END;
|
|
*/
|
|
field_def_lst->push_back(&next_variable->field_def);
|
|
next_variable_idx++;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
The variables on next_child have smaller offsets
|
|
than the next variable - retrieve the child context.
|
|
|
|
We're here e.g. in case of a declaration of a cursor with parameters:
|
|
CREATE PROCEDURE p1() AS
|
|
CURSOR cur(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
|
|
x1 INT:=101;
|
|
BEGIN
|
|
...
|
|
END;
|
|
Now we need to retrieve the child context with cp1/cp2,
|
|
then the variable x1 on the next loop interation.
|
|
|
|
Let's assert that the offsets are in a good order:
|
|
- All variables on next_child (with its children) have smaller
|
|
offsets than next_variable->offset.
|
|
- Additionally, if this is the very last child, then
|
|
next_variable->offset goes immediately after the offset of
|
|
the very last variable on next_child (and its children).
|
|
No holes are possible.
|
|
*/
|
|
DBUG_ASSERT(next_child_idx + 1 < m_children.elements() ?
|
|
next_child->after_last_variable_offset() <= next_variable->offset :
|
|
next_child->after_last_variable_offset() == next_variable->offset);
|
|
next_child->retrieve_field_definitions(field_def_lst);
|
|
next_child_idx++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
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
|
|
{
|
|
/*
|
|
Here we check the number of declared parameters:
|
|
DECLARE c(pa INT) FOR SELECT * FROM t1 WHERE t1.a=pa;
|
|
OPEN c(10);
|
|
It has nothing to do with placeholder parameters.
|
|
They are passed using the <using input clause>:
|
|
DECLARE c FOR prepared_stmt;
|
|
PREPARE c FROM 'SELECT * FROM t1 WHERE a=?';
|
|
OPEN c USING 10;
|
|
*/
|
|
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;
|
|
}
|