summaryrefslogtreecommitdiff
path: root/sql/sp_pcontext.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sp_pcontext.cc')
-rw-r--r--sql/sp_pcontext.cc406
1 files changed, 323 insertions, 83 deletions
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index 9a6353c9337..d98f8005945 100644
--- a/sql/sp_pcontext.cc
+++ b/sql/sp_pcontext.cc
@@ -27,19 +27,50 @@ 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;
- if (type != cv->type)
+ /*
+ 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 (mysqlerr == cv->mysqlerr);
+ return (get_sql_errno() == cv->get_sql_errno());
case sp_condition_value::SQLSTATE:
- return (strcmp(sql_state, cv->sql_state) == 0);
+ return Sql_state::eq(cv);
default:
return true;
@@ -56,6 +87,7 @@ void sp_pcontext::init(uint var_offset,
m_num_case_exprs= num_case_expressions;
m_labels.empty();
+ m_goto_labels.empty();
}
@@ -98,6 +130,12 @@ sp_pcontext *sp_pcontext::push_context(THD *thd, sp_pcontext::enum_scope scope)
}
+bool cmp_labels(sp_label *a, sp_label *b)
+{
+ return (my_strcasecmp(system_charset_info, a->name.str, b->name.str) == 0
+ && a->type == b->type);
+}
+
sp_pcontext *sp_pcontext::pop_context()
{
m_parent->m_max_var_index+= m_max_var_index;
@@ -109,6 +147,18 @@ sp_pcontext *sp_pcontext::pop_context()
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;
}
@@ -149,7 +199,7 @@ uint sp_pcontext::diff_cursors(const sp_pcontext *ctx, bool exclusive) const
}
-sp_variable *sp_pcontext::find_variable(LEX_STRING name,
+sp_variable *sp_pcontext::find_variable(const LEX_CSTRING *name,
bool current_scope_only) const
{
uint i= m_vars.elements() - m_pboundary;
@@ -159,7 +209,7 @@ sp_variable *sp_pcontext::find_variable(LEX_STRING name,
sp_variable *p= m_vars.at(i);
if (my_strnncoll(system_charset_info,
- (const uchar *)name.str, name.length,
+ (const uchar *)name->str, name->length,
(const uchar *)p->name.str, p->name.length) == 0)
{
return p;
@@ -172,10 +222,46 @@ sp_variable *sp_pcontext::find_variable(LEX_STRING name,
}
+/*
+ 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 && offset < m_var_offset + m_vars.elements())
- return m_vars.at(offset - m_var_offset); // This frame
+ 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
@@ -183,10 +269,10 @@ sp_variable *sp_pcontext::find_variable(uint offset) const
}
-sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name)
+sp_variable *sp_pcontext::add_variable(THD *thd, const LEX_CSTRING *name)
{
sp_variable *p=
- new (thd->mem_root) sp_variable(name, current_var_count());
+ new (thd->mem_root) sp_variable(name, m_var_offset + m_max_var_index);
if (!p)
return NULL;
@@ -196,29 +282,66 @@ sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name)
return m_vars.append(p) ? NULL : p;
}
-
-sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip)
+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, sp_label::IMPLICIT, this);
+ new (thd->mem_root) sp_label(name, ip, type, this);
if (!label)
return NULL;
- m_labels.push_front(label, thd->mem_root);
+ 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 (my_strcasecmp(system_charset_info, name->str, lab->name.str) == 0)
+ return lab;
+ }
-sp_label *sp_pcontext::find_label(LEX_STRING name)
+ 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 (my_strcasecmp(system_charset_info, name.str, lab->name.str) == 0)
+ if (my_strcasecmp(system_charset_info, name->str, lab->name.str) == 0)
return lab;
}
@@ -236,8 +359,25 @@ sp_label *sp_pcontext::find_label(LEX_STRING name)
}
+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,
- LEX_STRING name,
+ const LEX_CSTRING *name,
sp_condition_value *value)
{
sp_condition *p= new (thd->mem_root) sp_condition(name, value);
@@ -249,7 +389,7 @@ bool sp_pcontext::add_condition(THD *thd,
}
-sp_condition_value *sp_pcontext::find_condition(LEX_STRING name,
+sp_condition_value *sp_pcontext::find_condition(const LEX_CSTRING *name,
bool current_scope_only) const
{
uint i= m_conditions.elements();
@@ -258,9 +398,7 @@ sp_condition_value *sp_pcontext::find_condition(LEX_STRING name,
{
sp_condition *p= m_conditions.at(i);
- if (my_strnncoll(system_charset_info,
- (const uchar *) name.str, name.length,
- (const uchar *) p->name.str, p->name.length) == 0)
+ if (p->eq_name(name))
{
return p->value;
}
@@ -272,6 +410,38 @@ sp_condition_value *sp_pcontext::find_condition(LEX_STRING name,
}
+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_too_many_rows(ER_TOO_MANY_ROWS, "42000");
+
+
+static sp_condition sp_predefined_conditions[]=
+{
+ // Warnings
+ sp_condition(C_STRING_WITH_LEN("NO_DATA_FOUND"), &cond_no_data_found),
+ // Errors
+ sp_condition(C_STRING_WITH_LEN("INVALID_CURSOR"), &cond_invalid_cursor),
+ sp_condition(C_STRING_WITH_LEN("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index),
+ sp_condition(C_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)
{
@@ -305,10 +475,57 @@ bool sp_pcontext::check_duplicate_handler(
}
+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 char *sql_state,
- uint sql_errno,
- Sql_condition::enum_warning_level level) const
+sp_pcontext::find_handler(const Sql_condition_identity &value) const
{
sp_handler *found_handler= NULL;
sp_condition_value *found_cv= NULL;
@@ -322,53 +539,10 @@ sp_pcontext::find_handler(const char *sql_state,
while ((cv= li++))
{
- switch (cv->type)
+ if (cv->matches(value, found_cv))
{
- case sp_condition_value::ERROR_CODE:
- if (sql_errno == cv->mysqlerr &&
- (!found_cv ||
- found_cv->type > sp_condition_value::ERROR_CODE))
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
-
- case sp_condition_value::SQLSTATE:
- if (strcmp(sql_state, cv->sql_state) == 0 &&
- (!found_cv ||
- found_cv->type > sp_condition_value::SQLSTATE))
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
-
- case sp_condition_value::WARNING:
- if ((is_sqlstate_warning(sql_state) ||
- level == Sql_condition::WARN_LEVEL_WARN) && !found_cv)
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
-
- case sp_condition_value::NOT_FOUND:
- if (is_sqlstate_not_found(sql_state) && !found_cv)
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
-
- case sp_condition_value::EXCEPTION:
- if (is_sqlstate_exception(sql_state) &&
- level == Sql_condition::WARN_LEVEL_ERROR && !found_cv)
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
+ found_cv= cv;
+ found_handler= h;
}
}
}
@@ -411,64 +585,98 @@ sp_pcontext::find_handler(const char *sql_state,
if (!p || !p->m_parent)
return NULL;
- return p->m_parent->find_handler(sql_state, sql_errno, level);
+ return p->m_parent->find_handler(value);
}
-bool sp_pcontext::add_cursor(LEX_STRING name)
+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(name);
+ return m_cursors.append(sp_pcursor(name, param_ctx, lex));
}
-bool sp_pcontext::find_cursor(LEX_STRING name,
- uint *poff,
- bool current_scope_only) const
+const sp_pcursor *sp_pcontext::find_cursor(const LEX_CSTRING *name,
+ uint *poff,
+ bool current_scope_only) const
{
uint i= m_cursors.elements();
while (i--)
{
- LEX_STRING n= m_cursors.at(i);
+ LEX_CSTRING n= m_cursors.at(i);
if (my_strnncoll(system_charset_info,
- (const uchar *) name.str, name.length,
+ (const uchar *) name->str, name->length,
(const uchar *) n.str, n.length) == 0)
{
*poff= m_cursor_offset + i;
- return true;
+ return &m_cursors.at(i);
}
}
return (!current_scope_only && m_parent) ?
m_parent->find_cursor(name, poff, false) :
- false;
+ NULL;
}
void sp_pcontext::retrieve_field_definitions(
- List<Column_definition> *field_def_lst) const
+ 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 enclosed contexts in the result list. */
+ /* Put the fields of the remaining enclosed contexts in the result list. */
- for (size_t i= 0; i < m_children.elements(); ++i)
+ for (size_t i= next_child; i < m_children.elements(); ++i)
m_children.at(i)->retrieve_field_definitions(field_def_lst);
}
-const LEX_STRING *sp_pcontext::find_cursor(uint offset) const
+const sp_pcursor *sp_pcontext::find_cursor(uint offset) const
{
if (m_cursor_offset <= offset &&
offset < m_cursor_offset + m_cursors.elements())
@@ -480,3 +688,35 @@ const LEX_STRING *sp_pcontext::find_cursor(uint offset) const
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;
+}