summaryrefslogtreecommitdiff
path: root/sql/sql_lex.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_lex.cc')
-rw-r--r--sql/sql_lex.cc319
1 files changed, 286 insertions, 33 deletions
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index a2a9e5b2f16..b169b9e0b27 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2964,9 +2964,28 @@ void LEX::cleanup_lex_after_parse_error(THD *thd)
*/
if (thd->lex->sphead)
{
+ sp_package *pkg;
thd->lex->sphead->restore_thd_mem_root(thd);
- delete thd->lex->sphead;
- thd->lex->sphead= NULL;
+ if ((pkg= thd->lex->sphead->m_parent))
+ {
+ /*
+ If a syntax error happened inside a package routine definition,
+ then thd->lex points to the routine sublex. We need to restore to
+ the top level LEX.
+ */
+ DBUG_ASSERT(pkg->m_top_level_lex);
+ DBUG_ASSERT(pkg == pkg->m_top_level_lex->sphead);
+ pkg->restore_thd_mem_root(thd);
+ LEX *top= pkg->m_top_level_lex;
+ delete pkg;
+ thd->lex= top;
+ thd->lex->sphead= NULL;
+ }
+ else
+ {
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
}
}
@@ -5198,13 +5217,50 @@ void LEX::set_stmt_init()
};
+/**
+ Find a local or a package body variable by name.
+ @param IN name - the variable name
+ @param OUT ctx - NULL, if the variable was not found,
+ or LEX::spcont (if a local variable was found)
+ or the package top level context
+ (if a package variable was found)
+ @param OUT handler - NULL, if the variable was not found,
+ or a pointer to rcontext handler
+ @retval - the variable (if found), or NULL otherwise.
+*/
+sp_variable *
+LEX::find_variable(const LEX_CSTRING *name,
+ sp_pcontext **ctx,
+ const Sp_rcontext_handler **rh) const
+{
+ sp_variable *spv;
+ if (spcont && (spv= spcont->find_variable(name, false)))
+ {
+ *ctx= spcont;
+ *rh= &sp_rcontext_handler_local;
+ return spv;
+ }
+ sp_package *pkg= sphead ? sphead->m_parent : NULL;
+ if (pkg && (spv= pkg->find_package_variable(name)))
+ {
+ *ctx= pkg->get_parse_context()->child_context(0);
+ *rh= &sp_rcontext_handler_package_body;
+ return spv;
+ }
+ *ctx= NULL;
+ *rh= NULL;
+ return NULL;
+}
+
+
bool LEX::init_internal_variable(struct sys_var_with_base *variable,
const LEX_CSTRING *name)
{
sp_variable *spv;
+ const Sp_rcontext_handler *rh;
/* Best effort lookup for system variable. */
- if (!spcont || !(spv = spcont->find_variable(name, false)))
+ if (!(spv= find_variable(name, &rh)))
{
struct sys_var_with_base tmp= {NULL, *name};
@@ -5323,7 +5379,8 @@ bool LEX::sp_variable_declarations_set_default(THD *thd, int nvars,
/* The last instruction is responsible for freeing LEX. */
sp_instr_set *is= new (this->thd->mem_root)
sp_instr_set(sphead->instructions(),
- spcont, spvar->offset, dflt_value_item,
+ spcont, &sp_rcontext_handler_local,
+ spvar->offset, dflt_value_item,
this, last);
if (is == NULL || sphead->add_instr(is))
return true;
@@ -5619,7 +5676,8 @@ sp_variable *LEX::sp_add_for_loop_variable(THD *thd, const LEX_CSTRING *name,
spvar->default_value= value;
sp_instr_set *is= new (this->thd->mem_root)
sp_instr_set(sphead->instructions(),
- spcont, spvar->offset, value,
+ spcont, &sp_rcontext_handler_local,
+ spvar->offset, value,
this, true);
if (is == NULL || sphead->add_instr(is))
return NULL;
@@ -5697,7 +5755,8 @@ bool LEX::sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop)
{
sp_variable *src= i == 0 ? loop.m_index : loop.m_upper_bound;
args[i]= new (thd->mem_root)
- Item_splocal(thd, &src->name, src->offset, src->type_handler());
+ Item_splocal(thd, &sp_rcontext_handler_local,
+ &src->name, src->offset, src->type_handler());
if (args[i] == NULL)
return true;
#ifdef DBUG_ASSERT_EXISTS
@@ -5830,7 +5889,8 @@ bool LEX::sp_for_loop_cursor_declarations(THD *thd,
bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop)
{
Item_splocal *splocal= new (thd->mem_root)
- Item_splocal(thd, &loop.m_index->name, loop.m_index->offset,
+ Item_splocal(thd, &sp_rcontext_handler_local,
+ &loop.m_index->name, loop.m_index->offset,
loop.m_index->type_handler());
if (splocal == NULL)
return true;
@@ -5842,7 +5902,8 @@ bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop)
return true;
Item *expr= new (thd->mem_root) Item_func_plus(thd, splocal, inc);
if (!expr ||
- sphead->set_local_variable(thd, spcont, loop.m_index, expr, this, true))
+ sphead->set_local_variable(thd, spcont, &sp_rcontext_handler_local,
+ loop.m_index, expr, this, true))
return true;
return false;
}
@@ -6042,6 +6103,31 @@ sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name)
}
+/**
+ When a package routine name is stored in memory in Database_qualified_name,
+ the dot character is used to delimit package name from the routine name,
+ e.g.:
+ m_db= 'test'; -- database 'test'
+ m_name= 'p1.p1'; -- package 'p1', routine 'p1'
+ See database_qualified_name::make_package_routine_name() for details.
+ Disallow package routine names with dots,
+ to avoid ambiguity when interpreting m_name='p1.p1.p1', between:
+ a. package 'p1.p1' + routine 'p1'
+ b. package 'p1' + routine 'p1.p1'
+ m_name='p1.p1.p1' will always mean (a).
+*/
+sp_name *LEX::make_sp_name_package_routine(THD *thd, const LEX_CSTRING *name)
+{
+ sp_name *res= make_sp_name(thd, name);
+ if (res && strchr(res->m_name.str, '.'))
+ {
+ my_error(ER_SP_WRONG_NAME, MYF(0), res->m_name.str);
+ res= NULL;
+ }
+ return res;
+}
+
+
sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name1,
const LEX_CSTRING *name2)
{
@@ -6064,15 +6150,25 @@ sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name1,
sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
const Sp_handler *sph)
{
+ sp_package *package= get_sp_package();
sp_head *sp;
/* Order is important here: new - reset - init */
- if ((sp= new sp_head(sph)))
+ if ((sp= new sp_head(package, sph)))
{
sp->reset_thd_mem_root(thd);
sp->init(this);
if (name)
- sp->init_sp_name(name);
+ {
+ if (package)
+ sp->make_package_routine_name(sp->get_main_mem_root(),
+ package->m_db,
+ package->m_name,
+ name->m_name);
+ else
+ sp->init_sp_name(name);
+ sp->make_qname(sp->get_main_mem_root(), &sp->m_qname);
+ }
sphead= sp;
}
sp_chistics.init();
@@ -6080,6 +6176,31 @@ sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
}
+sp_head *LEX::make_sp_head_no_recursive(THD *thd, const sp_name *name,
+ const Sp_handler *sph)
+{
+ sp_package *package= thd->lex->get_sp_package();
+ /*
+ Sp_handler::sp_clone_and_link_routine() generates a standalone-alike
+ statement to clone package routines for recursion, e.g.:
+ CREATE PROCEDURE p1 AS BEGIN NULL; END;
+ Translate a standalone routine handler to the corresponding
+ package routine handler if we're cloning a package routine, e.g.:
+ sp_handler_procedure -> sp_handler_package_procedure
+ sp_handler_function -> sp_handler_package_function
+ */
+ if (package && package->m_is_cloning_routine)
+ sph= sph->package_routine_handler();
+ if (!sphead ||
+ (package &&
+ (sph == &sp_handler_package_procedure ||
+ sph == &sp_handler_package_function)))
+ return make_sp_head(thd, name, sph);
+ my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str());
+ return NULL;
+}
+
+
bool LEX::sp_body_finalize_procedure(THD *thd)
{
if (sphead->check_unresolved_goto())
@@ -6170,6 +6291,14 @@ LEX::sp_block_with_exceptions_finalize_exceptions(THD *thd,
}
+bool LEX::sp_block_with_exceptions_add_empty(THD *thd)
+{
+ uint ip= sphead->instructions();
+ return sp_block_with_exceptions_finalize_executable_section(thd, ip) ||
+ sp_block_with_exceptions_finalize_exceptions(thd, ip, 0);
+}
+
+
bool LEX::sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive)
{
uint n;
@@ -6588,6 +6717,7 @@ Item *LEX::create_item_ident_nospvar(THD *thd,
Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
+ const Sp_rcontext_handler *rh,
const LEX_CSTRING *a,
const LEX_CSTRING *b,
sp_variable *spv,
@@ -6606,7 +6736,7 @@ Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
spv->field_def.is_cursor_rowtype_ref())
{
if (!(item= new (thd->mem_root)
- Item_splocal_row_field_by_name(thd, a, b, spv->offset,
+ Item_splocal_row_field_by_name(thd, rh, a, b, spv->offset,
&type_handler_null,
pos.pos(), pos.length())))
return NULL;
@@ -6619,7 +6749,7 @@ Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
return NULL;
if (!(item= new (thd->mem_root)
- Item_splocal_row_field(thd, a, b,
+ Item_splocal_row_field(thd, rh, a, b,
spv->offset, row_field_offset,
def->type_handler(),
pos.pos(), pos.length())))
@@ -6633,12 +6763,27 @@ Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
}
+my_var *LEX::create_outvar(THD *thd, const LEX_CSTRING *name)
+{
+ const Sp_rcontext_handler *rh;
+ sp_variable *spv;
+ if ((spv= find_variable(name, &rh)))
+ return result ? new (thd->mem_root)
+ my_var_sp(rh, name, spv->offset,
+ spv->type_handler(), sphead) :
+ NULL /* EXPLAIN */;
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), name->str);
+ return NULL;
+}
+
+
my_var *LEX::create_outvar(THD *thd,
const LEX_CSTRING *a,
const LEX_CSTRING *b)
{
+ const Sp_rcontext_handler *rh;
sp_variable *t;
- if (!spcont || !(t= spcont->find_variable(a, false)))
+ if (!(t= find_variable(a, &rh)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), a->str);
return NULL;
@@ -6647,9 +6792,9 @@ my_var *LEX::create_outvar(THD *thd,
if (!t->find_row_field(a, b, &row_field_offset))
return NULL;
return result ?
- new (thd->mem_root) my_var_sp_row_field(a, b, t->offset,
+ new (thd->mem_root) my_var_sp_row_field(rh, a, b, t->offset,
row_field_offset, sphead) :
- NULL;
+ NULL /* EXPLAIN */;
}
@@ -6719,12 +6864,13 @@ Item *LEX::create_item_ident(THD *thd,
const LEX_CSTRING *b,
const char *start, const char *end)
{
+ const Sp_rcontext_handler *rh;
sp_variable *spv;
- if (spcont && (spv= spcont->find_variable(a, false)) &&
+ if ((spv= find_variable(a, &rh)) &&
(spv->field_def.is_row() ||
spv->field_def.is_table_rowtype_ref() ||
spv->field_def.is_cursor_rowtype_ref()))
- return create_item_spvar_row_field(thd, a, b, spv, start, end);
+ return create_item_spvar_row_field(thd, rh, a, b, spv, start, end);
if ((thd->variables.sql_mode & MODE_ORACLE) && b->length == 7)
{
@@ -6780,8 +6926,9 @@ Item *LEX::create_item_limit(THD *thd,
const LEX_CSTRING *a,
const char *start, const char *end)
{
+ const Sp_rcontext_handler *rh;
sp_variable *spv;
- if (!spcont || !(spv= spcont->find_variable(a, false)))
+ if (!(spv= find_variable(a, &rh)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), a->str);
return NULL;
@@ -6789,7 +6936,7 @@ Item *LEX::create_item_limit(THD *thd,
Query_fragment pos(thd, sphead, start, end);
Item_splocal *item;
- if (!(item= new (thd->mem_root) Item_splocal(thd, a,
+ if (!(item= new (thd->mem_root) Item_splocal(thd, rh, a,
spv->offset, spv->type_handler(),
pos.pos(), pos.length())))
return NULL;
@@ -6813,8 +6960,9 @@ Item *LEX::create_item_limit(THD *thd,
const LEX_CSTRING *b,
const char *start, const char *end)
{
+ const Sp_rcontext_handler *rh;
sp_variable *spv;
- if (!spcont || !(spv= spcont->find_variable(a, false)))
+ if (!(spv= find_variable(a, &rh)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), a->str);
return NULL;
@@ -6822,7 +6970,7 @@ Item *LEX::create_item_limit(THD *thd,
// Qualified %TYPE variables are not possible
DBUG_ASSERT(!spv->field_def.column_type_ref());
Item_splocal *item;
- if (!(item= create_item_spvar_row_field(thd, a, b, spv, start, end)))
+ if (!(item= create_item_spvar_row_field(thd, rh, a, b, spv, start, end)))
return NULL;
if (item->type() != Item::INT_ITEM)
{
@@ -6868,10 +7016,13 @@ bool LEX::set_variable(struct sys_var_with_base *variable, Item *item)
was previously checked by init_internal_variable().
*/
DBUG_ASSERT(spcont);
- sp_variable *spv= spcont->find_variable(&variable->base_name, false);
+ const Sp_rcontext_handler *rh;
+ sp_pcontext *ctx;
+ sp_variable *spv= find_variable(&variable->base_name, &ctx, &rh);
DBUG_ASSERT(spv);
- /* It is a local variable. */
- return sphead->set_local_variable(thd, spcont, spv, item, this, true);
+ DBUG_ASSERT(ctx);
+ DBUG_ASSERT(rh);
+ return sphead->set_local_variable(thd, ctx, rh, spv, item, this, true);
}
@@ -6891,10 +7042,11 @@ Item *LEX::create_item_ident_sp(THD *thd, LEX_CSTRING *name,
const char *start,
const char *end)
{
+ const Sp_rcontext_handler *rh;
sp_variable *spv;
DBUG_ASSERT(spcont);
DBUG_ASSERT(sphead);
- if ((spv= spcont->find_variable(name, false)))
+ if ((spv= find_variable(name, &rh)))
{
/* We're compiling a stored procedure and found a variable */
if (!parsing_options.allows_variable)
@@ -6905,11 +7057,11 @@ Item *LEX::create_item_ident_sp(THD *thd, LEX_CSTRING *name,
Query_fragment pos(thd, sphead, start, end);
Item_splocal *splocal= spv->field_def.is_column_type_ref() ?
- new (thd->mem_root) Item_splocal_with_delayed_data_type(thd, name,
+ new (thd->mem_root) Item_splocal_with_delayed_data_type(thd, rh, name,
spv->offset,
pos.pos(),
pos.length()) :
- new (thd->mem_root) Item_splocal(thd, name,
+ new (thd->mem_root) Item_splocal(thd, rh, name,
spv->offset, spv->type_handler(),
pos.pos(), pos.length());
if (splocal == NULL)
@@ -6940,18 +7092,21 @@ bool LEX::set_variable(const LEX_CSTRING *name1,
const LEX_CSTRING *name2,
Item *item)
{
+ const Sp_rcontext_handler *rh;
+ sp_pcontext *ctx;
sp_variable *spv;
- if (spcont && (spv= spcont->find_variable(name1, false)))
+ if (spcont && (spv= find_variable(name1, &ctx, &rh)))
{
if (spv->field_def.is_table_rowtype_ref() ||
spv->field_def.is_cursor_rowtype_ref())
- return sphead->set_local_variable_row_field_by_name(thd, spcont,
+ return sphead->set_local_variable_row_field_by_name(thd, ctx,
+ rh,
spv, name2,
item, this);
// A field of a ROW variable
uint row_field_offset;
return !spv->find_row_field(name1, name2, &row_field_offset) ||
- sphead->set_local_variable_row_field(thd, spcont,
+ sphead->set_local_variable_row_field(thd, ctx, rh,
spv, row_field_offset,
item, this);
}
@@ -7416,12 +7571,18 @@ bool LEX::add_create_view(THD *thd, DDL_options_st ddl,
bool LEX::call_statement_start(THD *thd, sp_name *name)
{
+ Database_qualified_name pkgname(&null_clex_str, &null_clex_str);
+ const Sp_handler *sph= &sp_handler_procedure;
sql_command= SQLCOM_CALL;
- spname= name;
value_list.empty();
- if (!(m_sql_cmd= new (thd->mem_root) Sql_cmd_call(name)))
+ if (sph->sp_resolve_package_routine(thd, thd->lex->sphead,
+ name, &sph, &pkgname))
+ return true;
+ if (!(m_sql_cmd= new (thd->mem_root) Sql_cmd_call(name, sph)))
return true;
- sp_handler_procedure.add_used_routine(this, thd, name);
+ sph->add_used_routine(this, thd, name);
+ if (pkgname.m_name.length)
+ sp_handler_package_body.add_used_routine(this, thd, &pkgname);
return false;
}
@@ -7441,6 +7602,98 @@ bool LEX::call_statement_start(THD *thd, const LEX_CSTRING *name1,
}
+sp_package *LEX::get_sp_package() const
+{
+ return sphead ? sphead->get_package() : NULL;
+}
+
+
+sp_package *LEX::create_package_start(THD *thd,
+ enum_sql_command command,
+ const Sp_handler *sph,
+ const sp_name *name_arg,
+ DDL_options_st options)
+{
+ sp_package *pkg;
+ if (sphead)
+ {
+ my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str());
+ return NULL;
+ }
+ if (set_command_with_check(command, options))
+ return NULL;
+ if (sph->type() == TYPE_ENUM_PACKAGE_BODY)
+ {
+ /*
+ If we start parsing a "CREATE PACKAGE BODY", we need to load
+ the corresponding "CREATE PACKAGE", for the following reasons:
+ 1. "CREATE PACKAGE BODY" is allowed only if "CREATE PACKAGE"
+ was done earlier for the same package name.
+ So if "CREATE PACKAGE" does not exist, we throw an error here.
+ 2. When parsing "CREATE PACKAGE BODY", we need to know all package
+ public and private routine names, to translate procedure and
+ function calls correctly.
+ For example, this statement inside a package routine:
+ CALL p;
+ can be translated to:
+ CALL db.pkg.p; -- p is a known (public or private) package routine
+ CALL db.p; -- p is not a known package routine
+ */
+ sp_head *spec;
+ int ret= sp_handler_package_spec.
+ sp_cache_routine_reentrant(thd, name_arg, &spec);
+ if (!spec)
+ {
+ if (!ret)
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+ "PACKAGE", ErrConvDQName(name_arg).ptr());
+ return 0;
+ }
+ }
+ if (!(pkg= new sp_package(this, name_arg, sph)))
+ return NULL;
+ pkg->reset_thd_mem_root(thd);
+ pkg->init(this);
+ pkg->make_qname(pkg->get_main_mem_root(), &pkg->m_qname);
+ sphead= pkg;
+ return pkg;
+}
+
+
+bool LEX::create_package_finalize(THD *thd,
+ const sp_name *name,
+ const sp_name *name2,
+ const char *body_start,
+ const char *body_end)
+{
+ if (name2 &&
+ (name2->m_explicit_name != name->m_explicit_name ||
+ strcmp(name2->m_db.str, name->m_db.str) ||
+ !Sp_handler::eq_routine_name(name2->m_name, name->m_name)))
+ {
+ bool exp= name2->m_explicit_name || name->m_explicit_name;
+ my_error(ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0),
+ exp ? ErrConvDQName(name2).ptr() : name2->m_name.str,
+ exp ? ErrConvDQName(name).ptr() : name->m_name.str);
+ return true;
+ }
+ sphead->m_body.length= body_end - body_start;
+ if (!(sphead->m_body.str= thd->strmake(body_start, sphead->m_body.length)))
+ return true;
+
+ size_t not_used;
+ Lex_input_stream *lip= & thd->m_parser_state->m_lip;
+ sphead->m_defstr.length= lip->get_cpp_ptr() - lip->get_cpp_buf();
+ sphead->m_defstr.str= thd->strmake(lip->get_cpp_buf(), sphead->m_defstr.length);
+ trim_whitespace(thd->charset(), &sphead->m_defstr, &not_used);
+
+ sphead->restore_thd_mem_root(thd);
+ sp_package *pkg= sphead->get_package();
+ DBUG_ASSERT(pkg);
+ return pkg->validate_after_parser(thd);
+}
+
+
bool LEX::add_grant_command(THD *thd, enum_sql_command sql_command_arg,
stored_procedure_type type_arg)
{