Quelle sp_pcontext.cc
Sprache: 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;
}
Messung V0.5 in Prozent C=94 H=83 G=88
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland
2026-10-09
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1867298