summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2017-08-18 23:36:42 +0400
committerAlexander Barkov <bar@mariadb.org>2018-02-25 21:08:19 +0400
commit583eb96c2492adb87e88a014b24eb0724fb00257 (patch)
tree501cb4e5e3855400e79df8911ac43ef1f89300b3 /sql
parent83ea839fb15dd5ed616d2b3152ccc5472ee5e5e6 (diff)
downloadmariadb-git-bb-10.3-compatibility.tar.gz
MDEV-11952 Oracle-style packages: stage#5mariadb-10.3.5bb-10.3-compatibility
- CREATE PACKAGE [BODY] statements are now entirely written to mysql.proc with type='PACKAGE' and type='PACKAGE BODY'. - CREATE PACKAGE BODY now supports IF NOT EXISTS - DROP PACKAGE BODY now supports IF EXISTS - CREATE OR REPLACE PACKAGE [BODY] is now supported - CREATE PACKAGE [BODY] now support the DEFINER clause: CREATE DEFINER user@host PACKAGE pkg ... END; CREATE DEFINER user@host PACKAGE BODY pkg ... END; - CREATE PACKAGE [BODY] now supports SQL SECURITY and COMMENT clauses, e.g.: CREATE PACKAGE p1 SQL SECURITY INVOKER COMMENT "comment" AS ... END; - Package routines are now created from the package CREATE PACKAGE BODY statement and don't produce individual records in mysql.proc. - CREATE PACKAGE BODY now supports package-wide variables. Package variables can be read and set inside package routines. Package variables are stored in a separate sp_rcontext, which is cached in THD on the first packate routine call. - CREATE PACKAGE BODY now supports the initialization section. - All public routines (i.e. declared in CREATE PACKAGE) must have implementations in CREATE PACKAGE BODY - Only public package routines are available outside of the package - {CREATE|DROP} PACKAGE [BODY] now respects CREATE ROUTINE and ALTER ROUTINE privileges - "GRANT EXECUTE ON PACKAGE BODY pkg" is now supported - SHOW CREATE PACKAGE [BODY] is now supported - SHOW PACKAGE [BODY] STATUS is now supported - CREATE and DROP for PACKAGE [BODY] now works for non-current databases - mysqldump now supports packages - "SHOW {PROCEDURE|FUNCTION) CODE pkg.routine" now works for package routines - "SHOW PACKAGE BODY CODE pkg" now works (the package initialization section) - A new package body level MDL was added - Recursive calls for package procedures are now possible - Routine forward declarations in CREATE PACKATE BODY are now supported. - Package body variables now work as SP OUT parameters - Package body variables now work as SELECT INTO targets - Package body variables now support ROW, %ROWTYPE, %TYPE
Diffstat (limited to 'sql')
-rw-r--r--sql/item.cc59
-rw-r--r--sql/item.h52
-rw-r--r--sql/item_create.cc17
-rw-r--r--sql/item_func.cc11
-rw-r--r--sql/item_func.h6
-rw-r--r--sql/item_xmlfunc.cc7
-rw-r--r--sql/lex.h2
-rw-r--r--sql/mdl.cc2
-rw-r--r--sql/mdl.h1
-rw-r--r--sql/mysqld.cc11
-rw-r--r--sql/share/errmsg-utf8.txt4
-rw-r--r--sql/sp.cc667
-rw-r--r--sql/sp.h244
-rw-r--r--sql/sp_head.cc361
-rw-r--r--sql/sp_head.h129
-rw-r--r--sql/sp_pcontext.h3
-rw-r--r--sql/sp_rcontext.cc27
-rw-r--r--sql/sp_rcontext.h3
-rw-r--r--sql/sql_acl.cc106
-rw-r--r--sql/sql_base.cc33
-rw-r--r--sql/sql_class.cc24
-rw-r--r--sql/sql_class.h48
-rw-r--r--sql/sql_cmd.h15
-rw-r--r--sql/sql_lex.cc319
-rw-r--r--sql/sql_lex.h47
-rw-r--r--sql/sql_parse.cc33
-rw-r--r--sql/sql_prepare.cc21
-rw-r--r--sql/sql_show.cc28
-rw-r--r--sql/sql_show.h2
-rw-r--r--sql/sql_string.h4
-rw-r--r--sql/sql_trigger.cc3
-rw-r--r--sql/sql_yacc.yy50
-rw-r--r--sql/sql_yacc_ora.yy411
33 files changed, 2529 insertions, 221 deletions
diff --git a/sql/item.cc b/sql/item.cc
index d3345f69a40..9f0280b678c 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1789,13 +1789,16 @@ void Item_sp_variable::make_field(THD *thd, Send_field *field)
Item_splocal methods
*****************************************************************************/
-Item_splocal::Item_splocal(THD *thd, const LEX_CSTRING *sp_var_name,
+Item_splocal::Item_splocal(THD *thd,
+ const Sp_rcontext_handler *rh,
+ const LEX_CSTRING *sp_var_name,
uint sp_var_idx,
const Type_handler *handler,
uint pos_in_q, uint len_in_q):
Item_sp_variable(thd, sp_var_name),
Rewritable_query_parameter(pos_in_q, len_in_q),
Type_handler_hybrid_field_type(handler),
+ m_rcontext_handler(rh),
m_var_idx(sp_var_idx)
{
maybe_null= TRUE;
@@ -1803,9 +1806,21 @@ Item_splocal::Item_splocal(THD *thd, const LEX_CSTRING *sp_var_name,
}
+sp_rcontext *Item_splocal::get_rcontext(sp_rcontext *local_ctx) const
+{
+ return m_rcontext_handler->get_rcontext(local_ctx);
+}
+
+
+Item_field *Item_splocal::get_variable(sp_rcontext *ctx) const
+{
+ return get_rcontext(ctx)->get_variable(m_var_idx);
+}
+
+
bool Item_splocal::fix_fields(THD *thd, Item **ref)
{
- Item_field *item= thd->spcont->get_variable(m_var_idx);
+ Item *item= get_variable(thd->spcont);
set_handler(item->type_handler());
return fix_fields_from_item(thd, ref, item);
}
@@ -1816,7 +1831,7 @@ Item_splocal::this_item()
{
DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
DBUG_ASSERT(fixed);
- return m_thd->spcont->get_variable(m_var_idx);
+ return get_variable(m_thd->spcont);
}
const Item *
@@ -1824,7 +1839,7 @@ Item_splocal::this_item() const
{
DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
DBUG_ASSERT(fixed);
- return m_thd->spcont->get_variable(m_var_idx);
+ return get_variable(m_thd->spcont);
}
@@ -1833,13 +1848,15 @@ Item_splocal::this_item_addr(THD *thd, Item **)
{
DBUG_ASSERT(m_sp == thd->spcont->m_sp);
DBUG_ASSERT(fixed);
- return thd->spcont->get_variable_addr(m_var_idx);
+ return get_rcontext(thd->spcont)->get_variable_addr(m_var_idx);
}
void Item_splocal::print(String *str, enum_query_type)
{
- str->reserve(m_name.length+8);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ str->reserve(m_name.length + 8 + prefix->length);
+ str->append(prefix);
str->append(&m_name);
str->append('@');
str->qs_append(m_var_idx);
@@ -1848,7 +1865,7 @@ void Item_splocal::print(String *str, enum_query_type)
bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it)
{
- return ctx->set_variable(thd, get_var_idx(), it);
+ return get_rcontext(ctx)->set_variable(thd, get_var_idx(), it);
}
@@ -1930,7 +1947,7 @@ bool Item_splocal::check_cols(uint n)
bool Item_splocal_row_field::fix_fields(THD *thd, Item **ref)
{
- Item *item= thd->spcont->get_variable(m_var_idx)->element_index(m_field_idx);
+ Item *item= get_variable(thd->spcont)->element_index(m_field_idx);
return fix_fields_from_item(thd, ref, item);
}
@@ -1940,7 +1957,7 @@ Item_splocal_row_field::this_item()
{
DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
DBUG_ASSERT(fixed);
- return m_thd->spcont->get_variable(m_var_idx)->element_index(m_field_idx);
+ return get_variable(m_thd->spcont)->element_index(m_field_idx);
}
@@ -1949,7 +1966,7 @@ Item_splocal_row_field::this_item() const
{
DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
DBUG_ASSERT(fixed);
- return m_thd->spcont->get_variable(m_var_idx)->element_index(m_field_idx);
+ return get_variable(m_thd->spcont)->element_index(m_field_idx);
}
@@ -1958,13 +1975,15 @@ Item_splocal_row_field::this_item_addr(THD *thd, Item **)
{
DBUG_ASSERT(m_sp == thd->spcont->m_sp);
DBUG_ASSERT(fixed);
- return thd->spcont->get_variable(m_var_idx)->addr(m_field_idx);
+ return get_variable(thd->spcont)->addr(m_field_idx);
}
void Item_splocal_row_field::print(String *str, enum_query_type)
{
- str->reserve(m_name.length + m_field_name.length + 8);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ str->reserve(m_name.length + m_field_name.length + 8 + prefix->length);
+ str->append(prefix);
str->append(&m_name);
str->append('.');
str->append(&m_field_name);
@@ -1978,18 +1997,19 @@ void Item_splocal_row_field::print(String *str, enum_query_type)
bool Item_splocal_row_field::set_value(THD *thd, sp_rcontext *ctx, Item **it)
{
- return ctx->set_variable_row_field(thd, m_var_idx, m_field_idx, it);
+ return get_rcontext(ctx)->set_variable_row_field(thd, m_var_idx, m_field_idx,
+ it);
}
bool Item_splocal_row_field_by_name::fix_fields(THD *thd, Item **it)
{
m_thd= thd;
- if (thd->spcont->find_row_field_by_name_or_error(&m_field_idx,
- m_var_idx,
- m_field_name))
+ if (get_rcontext(thd->spcont)->find_row_field_by_name_or_error(&m_field_idx,
+ m_var_idx,
+ m_field_name))
return true;
- Item *item= thd->spcont->get_variable(m_var_idx)->element_index(m_field_idx);
+ Item *item= get_variable(thd->spcont)->element_index(m_field_idx);
set_handler(item->type_handler());
return fix_fields_from_item(thd, it, item);
}
@@ -1997,9 +2017,12 @@ bool Item_splocal_row_field_by_name::fix_fields(THD *thd, Item **it)
void Item_splocal_row_field_by_name::print(String *str, enum_query_type)
{
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
// +16 should be enough for .NNN@[""]
- if (str->reserve(m_name.length + 2 * m_field_name.length + 16))
+ if (str->reserve(m_name.length + 2 * m_field_name.length +
+ prefix->length + 16))
return;
+ str->qs_append(prefix);
str->qs_append(&m_name);
str->qs_append('.');
str->qs_append(&m_field_name);
diff --git a/sql/item.h b/sql/item.h
index 2a7b92dd601..42442468be9 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -367,6 +367,39 @@ typedef enum monotonicity_info
class sp_rcontext;
+class Sp_rcontext_handler
+{
+public:
+ virtual ~Sp_rcontext_handler() {}
+ virtual const LEX_CSTRING *get_name_prefix() const= 0;
+ virtual sp_rcontext *get_rcontext(sp_rcontext *ctx) const= 0;
+};
+
+
+class Sp_rcontext_handler_local: public Sp_rcontext_handler
+{
+public:
+ const LEX_CSTRING *get_name_prefix() const;
+ sp_rcontext *get_rcontext(sp_rcontext *ctx) const;
+};
+
+
+class Sp_rcontext_handler_package_body: public Sp_rcontext_handler
+{
+public:
+ const LEX_CSTRING *get_name_prefix() const;
+ sp_rcontext *get_rcontext(sp_rcontext *ctx) const;
+};
+
+
+extern MYSQL_PLUGIN_IMPORT
+ Sp_rcontext_handler_local sp_rcontext_handler_local;
+
+
+extern MYSQL_PLUGIN_IMPORT
+ Sp_rcontext_handler_package_body sp_rcontext_handler_package_body;
+
+
class Item_equal;
@@ -2384,13 +2417,20 @@ class Item_splocal :public Item_sp_variable,
public Type_handler_hybrid_field_type
{
protected:
+ const Sp_rcontext_handler *m_rcontext_handler;
+
uint m_var_idx;
Type m_type;
bool append_value_for_log(THD *thd, String *str);
+
+ sp_rcontext *get_rcontext(sp_rcontext *local_ctx) const;
+ Item_field *get_variable(sp_rcontext *ctx) const;
+
public:
- Item_splocal(THD *thd, const LEX_CSTRING *sp_var_name, uint sp_var_idx,
+ Item_splocal(THD *thd, const Sp_rcontext_handler *rh,
+ const LEX_CSTRING *sp_var_name, uint sp_var_idx,
const Type_handler *handler,
uint pos_in_q= 0, uint len_in_q= 0);
@@ -2452,10 +2492,11 @@ class Item_splocal_with_delayed_data_type: public Item_splocal
{
public:
Item_splocal_with_delayed_data_type(THD *thd,
+ const Sp_rcontext_handler *rh,
const LEX_CSTRING *sp_var_name,
uint sp_var_idx,
uint pos_in_q, uint len_in_q)
- :Item_splocal(thd, sp_var_name, sp_var_idx, &type_handler_null,
+ :Item_splocal(thd, rh, sp_var_name, sp_var_idx, &type_handler_null,
pos_in_q, len_in_q)
{ }
};
@@ -2474,13 +2515,13 @@ protected:
bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
public:
Item_splocal_row_field(THD *thd,
+ const Sp_rcontext_handler *rh,
const LEX_CSTRING *sp_var_name,
const LEX_CSTRING *sp_field_name,
uint sp_var_idx, uint sp_field_idx,
const Type_handler *handler,
uint pos_in_q= 0, uint len_in_q= 0)
- :Item_splocal(thd, sp_var_name, sp_var_idx, handler,
- pos_in_q, len_in_q),
+ :Item_splocal(thd, rh, sp_var_name, sp_var_idx, handler, pos_in_q, len_in_q),
m_field_name(*sp_field_name),
m_field_idx(sp_field_idx)
{ }
@@ -2498,12 +2539,13 @@ class Item_splocal_row_field_by_name :public Item_splocal_row_field
bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
public:
Item_splocal_row_field_by_name(THD *thd,
+ const Sp_rcontext_handler *rh,
const LEX_CSTRING *sp_var_name,
const LEX_CSTRING *sp_field_name,
uint sp_var_idx,
const Type_handler *handler,
uint pos_in_q= 0, uint len_in_q= 0)
- :Item_splocal_row_field(thd, sp_var_name, sp_field_name,
+ :Item_splocal_row_field(thd, rh, sp_var_name, sp_field_name,
sp_var_idx, 0 /* field index will be set later */,
handler, pos_in_q, len_in_q)
{ }
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 548ce3bac94..385d8de98fc 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -3399,6 +3399,8 @@ Create_sp_func::create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name,
Item *func= NULL;
LEX *lex= thd->lex;
sp_name *qname;
+ const Sp_handler *sph= &sp_handler_function;
+ Database_qualified_name pkgname(&null_clex_str, &null_clex_str);
if (has_named_parameters(item_list))
{
@@ -3419,13 +3421,18 @@ Create_sp_func::create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name,
arg_count= item_list->elements;
qname= new (thd->mem_root) sp_name(db, name, use_explicit_name);
- sp_handler_function.add_used_routine(lex, thd, qname);
-
+ if (sph->sp_resolve_package_routine(thd, thd->lex->sphead,
+ qname, &sph, &pkgname))
+ return NULL;
+ sph->add_used_routine(lex, thd, qname);
+ if (pkgname.m_name.length)
+ sp_handler_package_body.add_used_routine(lex, thd, &pkgname);
if (arg_count > 0)
- func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(), qname,
- *item_list);
+ func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(),
+ qname, sph, *item_list);
else
- func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(), qname);
+ func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(),
+ qname, sph);
lex->safe_to_cache_query= 0;
return func;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 4e79b45f5d6..52e593edefd 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -6295,16 +6295,17 @@ longlong Item_func_row_count::val_int()
Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg,
- sp_name *name):
- Item_func(thd), Item_sp(thd, context_arg, name)
+ sp_name *name, const Sp_handler *sph):
+ Item_func(thd), Item_sp(thd, context_arg, name), m_handler(sph)
{
maybe_null= 1;
}
Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg,
- sp_name *name_arg, List<Item> &list):
- Item_func(thd, list), Item_sp(thd, context_arg, name_arg)
+ sp_name *name_arg, const Sp_handler *sph,
+ List<Item> &list):
+ Item_func(thd, list), Item_sp(thd, context_arg, name_arg), m_handler(sph)
{
maybe_null= 1;
}
@@ -6443,7 +6444,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
bool res;
DBUG_ENTER("Item_func_sp::fix_fields");
DBUG_ASSERT(fixed == 0);
- sp_head *sp= sp_handler_function.sp_find_routine(thd, m_name, true);
+ sp_head *sp= m_handler->sp_find_routine(thd, m_name, true);
/*
Checking privileges to execute the function while creating view and
diff --git a/sql/item_func.h b/sql/item_func.h
index 38a40e71544..35db77891b1 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -2772,6 +2772,7 @@ class Item_func_sp :public Item_func,
public Item_sp
{
private:
+ const Sp_handler *m_handler;
bool execute();
@@ -2786,10 +2787,11 @@ protected:
}
public:
- Item_func_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name);
+ Item_func_sp(THD *thd, Name_resolution_context *context_arg,
+ sp_name *name, const Sp_handler *sph);
Item_func_sp(THD *thd, Name_resolution_context *context_arg,
- sp_name *name, List<Item> &list);
+ sp_name *name, const Sp_handler *sph, List<Item> &list);
virtual ~Item_func_sp()
{}
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index 5156bd08ef2..0b9e515a61f 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -2630,14 +2630,13 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath)
else
{
sp_variable *spv;
- sp_pcontext *spc;
+ const Sp_rcontext_handler *rh;
LEX *lex;
if ((lex= thd->lex) &&
- (spc= lex->spcont) &&
- (spv= spc->find_variable(&name, false)))
+ (spv= lex->find_variable(&name, &rh)))
{
Item_splocal *splocal= new (thd->mem_root)
- Item_splocal(thd, &name, spv->offset, spv->type_handler(), 0);
+ Item_splocal(thd, rh, &name, spv->offset, spv->type_handler(), 0);
#ifdef DBUG_ASSERT_EXISTS
if (splocal)
splocal->m_sp= lex->sphead;
diff --git a/sql/lex.h b/sql/lex.h
index 41a81c737f7..9833809cd5d 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -90,6 +90,7 @@ static SYMBOL symbols[] = {
{ "BIT", SYM(BIT_SYM)},
{ "BLOB", SYM(BLOB_SYM)},
{ "BLOCK", SYM(BLOCK_SYM)},
+ { "BODY", SYM(BODY_SYM)},
{ "BOOL", SYM(BOOL_SYM)},
{ "BOOLEAN", SYM(BOOLEAN_SYM)},
{ "BOTH", SYM(BOTH)},
@@ -456,6 +457,7 @@ static SYMBOL symbols[] = {
{ "OUTFILE", SYM(OUTFILE)},
{ "OVER", SYM(OVER_SYM)},
{ "OWNER", SYM(OWNER_SYM)},
+ { "PACKAGE", SYM(PACKAGE_SYM)},
{ "PACK_KEYS", SYM(PACK_KEYS_SYM)},
{ "PAGE", SYM(PAGE_SYM)},
{ "PAGE_CHECKSUM", SYM(PAGE_CHECKSUM_SYM)},
diff --git a/sql/mdl.cc b/sql/mdl.cc
index bf708c945f4..f03fc89fcc1 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -85,6 +85,7 @@ PSI_stage_info MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
{0, "Waiting for table metadata lock", 0},
{0, "Waiting for stored function metadata lock", 0},
{0, "Waiting for stored procedure metadata lock", 0},
+ {0, "Waiting for stored package body metadata lock", 0},
{0, "Waiting for trigger metadata lock", 0},
{0, "Waiting for event metadata lock", 0},
{0, "Waiting for commit lock", 0},
@@ -3043,6 +3044,7 @@ const char *wsrep_get_mdl_namespace_name(MDL_key::enum_mdl_namespace ns)
case MDL_key::TABLE : return "TABLE";
case MDL_key::FUNCTION : return "FUNCTION";
case MDL_key::PROCEDURE : return "PROCEDURE";
+ case MDL_key::PACKAGE_BODY: return "PACKAGE BODY";
case MDL_key::TRIGGER : return "TRIGGER";
case MDL_key::EVENT : return "EVENT";
case MDL_key::COMMIT : return "COMMIT";
diff --git a/sql/mdl.h b/sql/mdl.h
index be9cc806ec2..a537022484f 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -299,6 +299,7 @@ public:
TABLE,
FUNCTION,
PROCEDURE,
+ PACKAGE_BODY,
TRIGGER,
EVENT,
COMMIT,
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 4ee9cda70bf..11c8b49d49c 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -3829,6 +3829,8 @@ SHOW_VAR com_status_vars[]= {
{"create_event", STMT_STATUS(SQLCOM_CREATE_EVENT)},
{"create_function", STMT_STATUS(SQLCOM_CREATE_SPFUNCTION)},
{"create_index", STMT_STATUS(SQLCOM_CREATE_INDEX)},
+ {"create_package", STMT_STATUS(SQLCOM_CREATE_PACKAGE)},
+ {"create_package_body", STMT_STATUS(SQLCOM_CREATE_PACKAGE_BODY)},
{"create_procedure", STMT_STATUS(SQLCOM_CREATE_PROCEDURE)},
{"create_role", STMT_STATUS(SQLCOM_CREATE_ROLE)},
{"create_sequence", STMT_STATUS(SQLCOM_CREATE_SEQUENCE)},
@@ -3848,6 +3850,8 @@ SHOW_VAR com_status_vars[]= {
{"drop_function", STMT_STATUS(SQLCOM_DROP_FUNCTION)},
{"drop_index", STMT_STATUS(SQLCOM_DROP_INDEX)},
{"drop_procedure", STMT_STATUS(SQLCOM_DROP_PROCEDURE)},
+ {"drop_package", STMT_STATUS(SQLCOM_DROP_PACKAGE)},
+ {"drop_package_body", STMT_STATUS(SQLCOM_DROP_PACKAGE_BODY)},
{"drop_role", STMT_STATUS(SQLCOM_DROP_ROLE)},
{"drop_server", STMT_STATUS(SQLCOM_DROP_SERVER)},
{"drop_sequence", STMT_STATUS(SQLCOM_DROP_SEQUENCE)},
@@ -3904,6 +3908,8 @@ SHOW_VAR com_status_vars[]= {
{"show_create_db", STMT_STATUS(SQLCOM_SHOW_CREATE_DB)},
{"show_create_event", STMT_STATUS(SQLCOM_SHOW_CREATE_EVENT)},
{"show_create_func", STMT_STATUS(SQLCOM_SHOW_CREATE_FUNC)},
+ {"show_create_package", STMT_STATUS(SQLCOM_SHOW_CREATE_PACKAGE)},
+ {"show_create_package_body",STMT_STATUS(SQLCOM_SHOW_CREATE_PACKAGE_BODY)},
{"show_create_proc", STMT_STATUS(SQLCOM_SHOW_CREATE_PROC)},
{"show_create_table", STMT_STATUS(SQLCOM_SHOW_CREATE)},
{"show_create_trigger", STMT_STATUS(SQLCOM_SHOW_CREATE_TRIGGER)},
@@ -3925,6 +3931,11 @@ SHOW_VAR com_status_vars[]= {
{"show_keys", STMT_STATUS(SQLCOM_SHOW_KEYS)},
{"show_master_status", STMT_STATUS(SQLCOM_SHOW_MASTER_STAT)},
{"show_open_tables", STMT_STATUS(SQLCOM_SHOW_OPEN_TABLES)},
+ {"show_package_status", STMT_STATUS(SQLCOM_SHOW_STATUS_PACKAGE)},
+#ifndef DBUG_OFF
+ {"show_package_body_code", STMT_STATUS(SQLCOM_SHOW_PACKAGE_BODY_CODE)},
+#endif
+ {"show_package_body_status", STMT_STATUS(SQLCOM_SHOW_STATUS_PACKAGE_BODY)},
{"show_plugins", STMT_STATUS(SQLCOM_SHOW_PLUGINS)},
{"show_privileges", STMT_STATUS(SQLCOM_SHOW_PRIVILEGES)},
#ifndef DBUG_OFF
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 3e9e94bbbfd..c725bf39eb0 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -7788,6 +7788,10 @@ ER_WRONG_INSERT_INTO_SEQUENCE
eng "Wrong INSERT into a SEQUENCE. One can only do single table INSERT into a sequence object (like with mysqldump). If you want to change the SEQUENCE, use ALTER SEQUENCE instead."
ER_SP_STACK_TRACE
eng "At line %u in %s"
+ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY
+ eng "Subroutine '%-.192s' is declared in the package specification but is not defined in the package body"
+ER_PACKAGE_ROUTINE_FORWARD_DECLARATION_NOT_DEFINED
+ eng "Subroutine '%-.192s' has a forward declaration but is not defined"
ER_COMPRESSED_COLUMN_USED_AS_KEY
eng "Compressed column '%-.192s' can't be used in key specification"
ER_UNKNOWN_COMPRESSION_METHOD
diff --git a/sql/sp.cc b/sql/sp.cc
index 773a5479199..d75e40af6ce 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -45,6 +45,17 @@ sp_cache **Sp_handler_function::get_cache(THD *thd) const
return &thd->sp_func_cache;
}
+sp_cache **Sp_handler_package_spec::get_cache(THD *thd) const
+{
+ return &thd->sp_package_spec_cache;
+}
+
+sp_cache **Sp_handler_package_body::get_cache(THD *thd) const
+{
+ return &thd->sp_package_body_cache;
+}
+
+
ulong Sp_handler_procedure::recursion_depth(THD *thd) const
{
return thd->variables.max_sp_recursion_depth;
@@ -85,7 +96,23 @@ bool Sp_handler_procedure::add_instr_preturn(THD *thd, sp_head *sp,
Sp_handler_procedure sp_handler_procedure;
Sp_handler_function sp_handler_function;
+Sp_handler_package_spec sp_handler_package_spec;
+Sp_handler_package_body sp_handler_package_body;
Sp_handler_trigger sp_handler_trigger;
+Sp_handler_package_procedure sp_handler_package_procedure;
+Sp_handler_package_function sp_handler_package_function;
+
+
+const Sp_handler *Sp_handler_procedure::package_routine_handler() const
+{
+ return &sp_handler_package_procedure;
+}
+
+
+const Sp_handler *Sp_handler_function::package_routine_handler() const
+{
+ return &sp_handler_package_function;
+}
static const
@@ -690,7 +717,7 @@ Sp_handler::db_find_routine(THD *thd,
table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root,
&params);
- if (type() == TYPE_ENUM_PROCEDURE)
+ if (type() != TYPE_ENUM_FUNCTION)
returns= empty_clex_str;
else if (table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root,
&returns))
@@ -718,7 +745,7 @@ Sp_handler::db_find_routine(THD *thd,
ret= db_load_routine(thd, name, sphp,
sql_mode, params, returns, body, chistics, definer,
- created, modified, creation_ctx);
+ created, modified, NULL, creation_ctx);
done:
/*
Restore the time zone flag as the timezone usage in proc table
@@ -788,6 +815,8 @@ Silence_deprecated_warning::handle_condition(
@param[in] thd Thread handler
@param[in] defstr CREATE... string
@param[in] sql_mode SQL mode
+ @param[in] parent The owner package for package routines,
+ or NULL for standalone routines.
@param[in] creation_ctx Creation context of stored routines
@return Pointer on sp_head struct
@@ -796,6 +825,7 @@ Silence_deprecated_warning::handle_condition(
*/
static sp_head *sp_compile(THD *thd, String *defstr, sql_mode_t sql_mode,
+ sp_package *parent,
Stored_program_creation_ctx *creation_ctx)
{
sp_head *sp;
@@ -816,6 +846,7 @@ static sp_head *sp_compile(THD *thd, String *defstr, sql_mode_t sql_mode,
}
lex_start(thd);
+ thd->lex->sphead= parent;
thd->push_internal_handler(&warning_handler);
thd->spcont= 0;
@@ -886,6 +917,7 @@ Sp_handler::db_load_routine(THD *thd, const Database_qualified_name *name,
const st_sp_chistics &chistics,
const AUTHID &definer,
longlong created, longlong modified,
+ sp_package *parent,
Stored_program_creation_ctx *creation_ctx) const
{
LEX *old_lex= thd->lex, newlex;
@@ -942,7 +974,7 @@ Sp_handler::db_load_routine(THD *thd, const Database_qualified_name *name,
}
{
- *sphp= sp_compile(thd, &defstr, sql_mode, creation_ctx);
+ *sphp= sp_compile(thd, &defstr, sql_mode, parent, creation_ctx);
/*
Force switching back to the saved current database (if changed),
because it may be NULL. In this case, mysql_change_db() would
@@ -967,6 +999,22 @@ Sp_handler::db_load_routine(THD *thd, const Database_qualified_name *name,
(*sphp)->set_info(created, modified, chistics, sql_mode);
(*sphp)->set_creation_ctx(creation_ctx);
(*sphp)->optimize();
+
+ if (type() == TYPE_ENUM_PACKAGE_BODY)
+ {
+ sp_package *package= sphp[0]->get_package();
+ List_iterator<LEX> it(package->m_routine_implementations);
+ for (LEX *lex; (lex= it++); )
+ {
+ DBUG_ASSERT(lex->sphead);
+ lex->sphead->set_definer(&definer.user, &definer.host);
+ lex->sphead->set_suid(package->suid());
+ lex->sphead->m_sql_mode= sql_mode;
+ lex->sphead->set_creation_ctx(creation_ctx);
+ lex->sphead->optimize();
+ }
+ }
+
/*
Not strictly necessary to invoke this method here, since we know
that we've parsed CREATE PROCEDURE/FUNCTION and not an
@@ -1058,6 +1106,32 @@ Sp_handler::sp_drop_routine_internal(THD *thd,
}
+int
+Sp_handler::sp_find_and_drop_routine(THD *thd, TABLE *table,
+ const Database_qualified_name *name) const
+{
+ int ret;
+ if (SP_OK != (ret= db_find_routine_aux(thd, name, table)))
+ return ret;
+ return sp_drop_routine_internal(thd, name, table);
+}
+
+
+int
+Sp_handler_package_spec::
+ sp_find_and_drop_routine(THD *thd, TABLE *table,
+ const Database_qualified_name *name) const
+{
+ int ret;
+ if (SP_OK != (ret= db_find_routine_aux(thd, name, table)))
+ return ret;
+ ret= sp_handler_package_body.sp_find_and_drop_routine(thd, table, name);
+ if (ret != SP_KEY_NOT_FOUND && ret != SP_OK)
+ return ret;
+ return Sp_handler::sp_find_and_drop_routine(thd, table, name);
+}
+
+
/**
Write stored-routine object into mysql.proc.
@@ -1142,7 +1216,22 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
{
if (lex->create_info.or_replace())
{
- if ((ret= sp_drop_routine_internal(thd, sp, table)))
+ switch (type()) {
+ case TYPE_ENUM_PACKAGE:
+ // Drop together with its PACKAGE BODY mysql.proc record
+ ret= sp_handler_package_spec.sp_find_and_drop_routine(thd, table, sp);
+ break;
+ case TYPE_ENUM_PACKAGE_BODY:
+ case TYPE_ENUM_FUNCTION:
+ case TYPE_ENUM_PROCEDURE:
+ ret= sp_drop_routine_internal(thd, sp, table);
+ break;
+ case TYPE_ENUM_TRIGGER:
+ case TYPE_ENUM_PROXY:
+ DBUG_ASSERT(0);
+ ret= SP_OK;
+ }
+ if (ret)
goto done;
}
else if (lex->create_info.if_not_exists())
@@ -1400,6 +1489,49 @@ append_comment(String *buf, const LEX_CSTRING &comment)
}
+static bool
+append_package_chistics(String *buf, const st_sp_chistics &chistics)
+{
+ return append_suid(buf, chistics.suid) ||
+ append_comment(buf, chistics.comment);
+}
+
+
+bool
+Sp_handler_package::show_create_sp(THD *thd, String *buf,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ const st_sp_chistics &chistics,
+ const AUTHID &definer,
+ const DDL_options_st ddl_options,
+ sql_mode_t sql_mode) const
+{
+ sql_mode_t old_sql_mode= thd->variables.sql_mode;
+ thd->variables.sql_mode= sql_mode;
+ bool rc=
+ buf->append(STRING_WITH_LEN("CREATE ")) ||
+ (ddl_options.or_replace() &&
+ buf->append(STRING_WITH_LEN("OR REPLACE "))) ||
+ append_definer(thd, buf, &definer.user, &definer.host) ||
+ buf->append(type_lex_cstring()) ||
+ buf->append(" ", 1) ||
+ (ddl_options.if_not_exists() &&
+ buf->append(STRING_WITH_LEN("IF NOT EXISTS "))) ||
+ (db.length > 0 &&
+ (append_identifier(thd, buf, db.str, db.length) ||
+ buf->append('.'))) ||
+ append_identifier(thd, buf, name.str, name.length) ||
+ append_package_chistics(buf, chistics) ||
+ buf->append(" ", 1) ||
+ buf->append(body.str, body.length);
+ thd->variables.sql_mode= old_sql_mode;
+ return rc;
+}
+
+
/**
Delete the record for the stored routine object from mysql.proc
and do binary logging.
@@ -1433,10 +1565,7 @@ Sp_handler::sp_drop_routine(THD *thd,
if (!(table= open_proc_table_for_update(thd)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
- if ((ret= db_find_routine_aux(thd, name, table)) == SP_OK)
- ret= sp_drop_routine_internal(thd, name, table);
-
- if (ret == SP_OK &&
+ if (SP_OK == (ret= sp_find_and_drop_routine(thd, table, name)) &&
write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
ret= SP_INTERNAL_ERROR;
/*
@@ -1631,9 +1760,12 @@ bool lock_db_routines(THD *thd, const char *db)
longlong sp_type= table->field[MYSQL_PROC_MYSQL_TYPE]->val_int();
MDL_request *mdl_request= new (thd->mem_root) MDL_request;
- mdl_request->init(sp_type == TYPE_ENUM_FUNCTION ?
- MDL_key::FUNCTION : MDL_key::PROCEDURE,
- db, sp_name, MDL_EXCLUSIVE, MDL_TRANSACTION);
+ const Sp_handler *sph= Sp_handler::handler((stored_procedure_type)
+ sp_type);
+ if (!sph)
+ sph= &sp_handler_procedure;
+ mdl_request->init(sph->get_mdl_type(), db, sp_name,
+ MDL_EXCLUSIVE, MDL_TRANSACTION);
mdl_requests.push_front(mdl_request);
} while (! (nxtres= table->file->ha_index_next_same(table->record[0], keybuf, key_len)));
}
@@ -1780,6 +1912,34 @@ Sp_handler::sp_show_create_routine(THD *thd,
/*
+ A helper class to split package name from a dot-qualified name
+ and return it as a 0-terminated string
+ 'pkg.name' -> 'pkg\0'
+*/
+class Prefix_name_buf: public LEX_CSTRING
+{
+ char m_buf[SAFE_NAME_LEN + 1];
+public:
+ Prefix_name_buf(const THD *thd, const LEX_CSTRING &name)
+ {
+ const char *end;
+ if (!(end= strrchr(name.str, '.')))
+ {
+ static_cast<LEX_CSTRING*>(this)[0]= null_clex_str;
+ }
+ else
+ {
+ str= m_buf;
+ length= end - name.str;
+ set_if_smaller(length, sizeof(m_buf) - 1);
+ memcpy(m_buf, name.str, length);
+ m_buf[length]= '\0';
+ }
+ }
+};
+
+
+/*
In case of recursions, we create multiple copies of the same SP.
This methods checks the current recursion depth.
In case if the recursion limit exceeded, it throws an error
@@ -1794,9 +1954,17 @@ Sp_handler::sp_clone_and_link_routine(THD *thd,
sp_head *sp) const
{
DBUG_ENTER("sp_link_routine");
+ int rc;
ulong level;
sp_head *new_sp;
LEX_CSTRING returns= empty_clex_str;
+ Database_qualified_name lname(name->m_db, name->m_name);
+#ifndef DBUG_OFF
+ uint parent_subroutine_count=
+ !sp->m_parent ? 0 :
+ sp->m_parent->m_routine_declarations.elements +
+ sp->m_parent->m_routine_implementations.elements;
+#endif
/*
String buffer for RETURNS data type must have system charset;
@@ -1838,13 +2006,63 @@ Sp_handler::sp_clone_and_link_routine(THD *thd,
sp_returns_type(thd, retstr, sp);
returns= retstr.lex_cstring();
}
- if (db_load_routine(thd, name, &new_sp,
+
+ if (sp->m_parent)
+ {
+ /*
+ If we're cloning a recursively called package routine,
+ we need to take some special measures:
+ 1. Cut the package name prefix from the routine name: 'pkg1.p1' -> 'p1',
+ to have db_load_routine() generate and parse a query like this:
+ CREATE PROCEDURE p1 ...;
+ rether than:
+ CREATE PROCEDURE pkg1.p1 ...;
+ The latter would be misinterpreted by the parser as a standalone
+ routine 'p1' in the database 'pkg1', which is not what we need.
+ 2. We pass m_parent to db_load_routine() to have it set
+ thd->lex->sphead to sp->m_parent before calling parse_sql().
+ These two measures allow to parse a package subroutine using
+ the grammar for standalone routines, e.g.:
+ CREATE PROCEDURE p1 ... END;
+ instead of going through a more complex query, e.g.:
+ CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 ... END;
+ END;
+ */
+ size_t prefix_length= sp->m_parent->m_name.length + 1;
+ DBUG_ASSERT(prefix_length < lname.m_name.length);
+ DBUG_ASSERT(lname.m_name.str[sp->m_parent->m_name.length] == '.');
+ lname.m_name.str+= prefix_length;
+ lname.m_name.length-= prefix_length;
+ sp->m_parent->m_is_cloning_routine= true;
+ }
+
+
+ rc= db_load_routine(thd, &lname, &new_sp,
sp->m_sql_mode, sp->m_params, returns,
sp->m_body, sp->chistics(),
sp->m_definer,
sp->m_created, sp->m_modified,
- sp->get_creation_ctx()) == SP_OK)
+ sp->m_parent,
+ sp->get_creation_ctx());
+ if (sp->m_parent)
+ sp->m_parent->m_is_cloning_routine= false;
+
+ if (rc == SP_OK)
{
+#ifndef DBUG_OFF
+ /*
+ We've just called the parser to clone the routine.
+ In case of a package routine, make sure that the parser
+ has not added any new subroutines directly to the parent package.
+ The cloned subroutine instances get linked below to the first instance,
+ they must have no direct links from the parent package.
+ */
+ DBUG_ASSERT(!sp->m_parent ||
+ parent_subroutine_count ==
+ sp->m_parent->m_routine_declarations.elements +
+ sp->m_parent->m_routine_implementations.elements);
+#endif
sp->m_last_cached_sp->m_next_cached_sp= new_sp;
new_sp->m_recursion_level= level;
new_sp->m_first_instance= sp;
@@ -1878,7 +2096,7 @@ sp_head *
Sp_handler::sp_find_routine(THD *thd, const Database_qualified_name *name,
bool cache_only) const
{
- DBUG_ENTER("sp_find_routine");
+ DBUG_ENTER("Sp_handler::sp_find_routine");
DBUG_PRINT("enter", ("name: %.*s.%.*s type: %s cache only %d",
(int) name->m_db.length, name->m_db.str,
(int) name->m_name.length, name->m_name.str,
@@ -1895,6 +2113,74 @@ Sp_handler::sp_find_routine(THD *thd, const Database_qualified_name *name,
/**
+ Find a package routine.
+ See sp_cache_routine() for more information on parameters and return value.
+
+ @param thd - current THD
+ @param pkgname_str - package name
+ @param name - a mixed qualified name, with:
+ * name->m_db set to the database, e.g. "dbname"
+ * name->m_name set to a package-qualified name,
+ e.g. "pkgname.spname".
+ @param cache_only - don't load mysql.proc if not cached
+ @retval non-NULL - a pointer to an sp_head object
+ @retval NULL - an error happened.
+*/
+sp_head *
+Sp_handler::sp_find_package_routine(THD *thd,
+ const LEX_CSTRING pkgname_str,
+ const Database_qualified_name *name,
+ bool cache_only) const
+{
+ DBUG_ENTER("sp_find_package_routine");
+ Database_qualified_name pkgname(&name->m_db, &pkgname_str);
+ sp_head *ph= sp_cache_lookup(&thd->sp_package_body_cache, &pkgname);
+ if (!ph && !cache_only)
+ sp_handler_package_body.db_find_and_cache_routine(thd, &pkgname, &ph);
+ if (ph)
+ {
+ LEX_CSTRING tmp= name->m_name;
+ const char *dot= strrchr(tmp.str, '.');
+ size_t prefix_length= dot ? dot - tmp.str + 1 : 0;
+ sp_package *pkg= ph->get_package();
+ tmp.str+= prefix_length;
+ tmp.length-= prefix_length;
+ LEX *plex= pkg ? pkg->m_routine_implementations.find(tmp, type()) : NULL;
+ sp_head *sp= plex ? plex->sphead : NULL;
+ if (sp)
+ DBUG_RETURN(sp_clone_and_link_routine(thd, name, sp));
+ }
+ DBUG_RETURN(NULL);
+}
+
+
+/**
+ Find a package routine.
+ See sp_cache_routine() for more information on parameters and return value.
+
+ @param thd - current THD
+ @param name - Qualified name with the following format:
+ * name->m_db is set to the database name, e.g. "dbname"
+ * name->m_name is set to a package-qualified name,
+ e.g. "pkgname.spname", as a single string with a
+ dot character as a separator.
+ @param cache_only - don't load mysql.proc if not cached
+ @retval non-NULL - a pointer to an sp_head object
+ @retval NULL - an error happened
+*/
+sp_head *
+Sp_handler::sp_find_package_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const
+{
+ DBUG_ENTER("Sp_handler::sp_find_package_routine");
+ Prefix_name_buf pkgname(thd, name->m_name);
+ DBUG_ASSERT(pkgname.length);
+ DBUG_RETURN(sp_find_package_routine(thd, pkgname, name, cache_only));
+}
+
+
+/**
This is used by sql_acl.cc:mysql_routine_grant() and is used to find
the routines in 'routines'.
@@ -1977,7 +2263,9 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
*/
bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
- const MDL_key *key, TABLE_LIST *belong_to_view)
+ const MDL_key *key,
+ const Sp_handler *handler,
+ TABLE_LIST *belong_to_view)
{
my_hash_init_opt(&prelocking_ctx->sroutines, system_charset_info,
Query_tables_list::START_SROUTINES_HASH_SIZE,
@@ -1994,6 +2282,7 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
return FALSE;
prelocking_ctx->sroutines_list.link_in_list(rn, &rn->next);
rn->belong_to_view= belong_to_view;
+ rn->m_handler= handler;
rn->m_sp_cache_version= 0;
return TRUE;
}
@@ -2001,6 +2290,268 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
}
+/*
+ Find and cache a routine in a parser-safe reentrant mode.
+
+ If sp_head is not in the cache,
+ its loaded from mysql.proc, parsed using parse_sql(), and cached.
+ Note, as it is called from inside parse_sql() itself,
+ we need to preserve and restore the parser state.
+
+ It's used during parsing of CREATE PACKAGE BODY,
+ to load the corresponding CREATE PACKAGE.
+*/
+int
+Sp_handler::sp_cache_routine_reentrant(THD *thd,
+ const Database_qualified_name *name,
+ sp_head **sp) const
+{
+ int ret;
+ Parser_state *oldps= thd->m_parser_state;
+ thd->m_parser_state= NULL;
+ ret= sp_cache_routine(thd, name, false, sp);
+ thd->m_parser_state= oldps;
+ return ret;
+}
+
+
+/*
+ Check if a routine has a declaration in the CREATE PACKAGE statement,
+ by looking up in thd->sp_package_spec_cache, and by loading from mysql.proc
+ if needed.
+
+ @param thd current thd
+ @param db the database name
+ @param package the package name
+ @param name the routine name
+ @param type the routine type
+ @retval true, if the routine has a declaration
+ @retval false, if the routine does not have a declaration
+
+ This function can be called in arbitrary context:
+ - inside a package routine
+ - inside a standalone routine
+ - inside a anonymous block
+ - outside of any routines
+
+ The state of the package specification (i.e. the CREATE PACKAGE statement)
+ for "package" before the call of this function is not known:
+ it can be cached, or not cached.
+ After the call of this function, the package specification is always cached,
+ unless a fatal error happens.
+*/
+static bool
+is_package_public_routine(THD *thd,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &package,
+ const LEX_CSTRING &routine,
+ stored_procedure_type type)
+{
+ sp_head *sp= NULL;
+ Database_qualified_name tmp(db, package);
+ bool ret= sp_handler_package_spec.
+ sp_cache_routine_reentrant(thd, &tmp, &sp);
+ sp_package *spec= (!ret && sp) ? sp->get_package() : NULL;
+ return spec && spec->m_routine_declarations.find(routine, type);
+}
+
+
+/*
+ Check if a routine has a declaration in the CREATE PACKAGE statement
+ by looking up in sp_package_spec_cache.
+
+ @param thd current thd
+ @param db the database name
+ @param pkgname the package name
+ @param name the routine name
+ @param type the routine type
+ @retval true, if the routine has a declaration
+ @retval false, if the routine does not have a declaration
+
+ This function is called in the middle of CREATE PACKAGE BODY parsing,
+ to lookup the current package routines.
+ The package specification (i.e. the CREATE PACKAGE statement) for
+ the current package body must already be loaded and cached at this point.
+*/
+static bool
+is_package_public_routine_quick(THD *thd,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &pkgname,
+ const LEX_CSTRING &name,
+ stored_procedure_type type)
+{
+ Database_qualified_name tmp(db, pkgname);
+ sp_head *sp= sp_cache_lookup(&thd->sp_package_spec_cache, &tmp);
+ sp_package *pkg= sp ? sp->get_package() : NULL;
+ DBUG_ASSERT(pkg); // Must already be cached
+ return pkg && pkg->m_routine_declarations.find(name, type);
+}
+
+
+/*
+ Check if a qualified name, e.g. "CALL name1.name2",
+ refers to a known routine in the package body "pkg".
+*/
+static bool
+is_package_body_routine(THD *thd, sp_package *pkg,
+ const LEX_CSTRING &name1,
+ const LEX_CSTRING &name2,
+ stored_procedure_type type)
+{
+ return Sp_handler::eq_routine_name(pkg->m_name, name1) &&
+ (pkg->m_routine_declarations.find(name2, type) ||
+ pkg->m_routine_implementations.find(name2, type));
+}
+
+
+/*
+ Resolve a qualified routine reference xxx.yyy(), between:
+ - A standalone routine: xxx.yyy
+ - A package routine: current_database.xxx.yyy
+*/
+bool Sp_handler::
+ sp_resolve_package_routine_explicit(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_handler,
+ Database_qualified_name *pkgname) const
+{
+ sp_package *pkg;
+
+ /*
+ If a qualified routine name was used, e.g. xxx.yyy(),
+ we possibly have a call to a package routine.
+ Rewrite name if name->m_db (xxx) is a known package,
+ and name->m_name (yyy) is a known routine in this package.
+ */
+ LEX_CSTRING tmpdb= thd->db;
+ if (is_package_public_routine(thd, tmpdb, name->m_db, name->m_name, type()) ||
+ // Check if a package routine calls a private routine
+ (caller && caller->m_parent &&
+ is_package_body_routine(thd, caller->m_parent,
+ name->m_db, name->m_name, type())) ||
+ // Check if a package initialization sections calls a private routine
+ (caller && (pkg= caller->get_package()) &&
+ is_package_body_routine(thd, pkg, name->m_db, name->m_name, type())))
+ {
+ pkgname->m_db= tmpdb;
+ pkgname->m_name= name->m_db;
+ *pkg_routine_handler= package_routine_handler();
+ return name->make_package_routine_name(thd->mem_root, tmpdb,
+ name->m_db, name->m_name);
+ }
+ return false;
+}
+
+
+/*
+ Resolve a non-qualified routine reference yyy(), between:
+ - A standalone routine: current_database.yyy
+ - A package routine: current_database.current_package.yyy
+*/
+bool Sp_handler::
+ sp_resolve_package_routine_implicit(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_handler,
+ Database_qualified_name *pkgname) const
+{
+ sp_package *pkg;
+
+ if (!caller || !caller->m_name.length)
+ {
+ /*
+ We are either in a an anonymous block,
+ or not in a routine at all.
+ */
+ return false; // A standalone routine is called
+ }
+
+ if (caller->m_parent)
+ {
+ // A package routine calls a non-qualified routine
+ int ret= SP_OK;
+ Prefix_name_buf pkgstr(thd, caller->m_name);
+ DBUG_ASSERT(pkgstr.length);
+ LEX_CSTRING tmpname; // Non-qualified m_name
+ tmpname.str= caller->m_name.str + pkgstr.length + 1;
+ tmpname.length= caller->m_name.length - pkgstr.length - 1;
+
+ /*
+ We're here if a package routine calls another non-qualified
+ function or procedure, e.g. yyy().
+ We need to distinguish two cases:
+ - yyy() is another routine from the same package
+ - yyy() is a standalone routine from the same database
+ To detect if yyy() is a package (rather than a standalone) routine,
+ we check if:
+ - yyy() recursively calls itself
+ - yyy() is earlier implemented in the current CREATE PACKAGE BODY
+ - yyy() has a forward declaration
+ - yyy() is declared in the corresponding CREATE PACKAGE
+ */
+ if (eq_routine_name(tmpname, name->m_name) ||
+ caller->m_parent->m_routine_implementations.find(name->m_name, type()) ||
+ caller->m_parent->m_routine_declarations.find(name->m_name, type()) ||
+ is_package_public_routine_quick(thd, caller->m_db,
+ pkgstr, name->m_name, type()))
+ {
+ DBUG_ASSERT(ret == SP_OK);
+ pkgname->copy(thd->mem_root, caller->m_db, pkgstr);
+ *pkg_routine_handler= package_routine_handler();
+ if (name->make_package_routine_name(thd->mem_root, pkgstr, name->m_name))
+ return true;
+ }
+ return ret != SP_OK;
+ }
+
+ if ((pkg= caller->get_package()) &&
+ pkg->m_routine_implementations.find(name->m_name, type()))
+ {
+ pkgname->m_db= caller->m_db;
+ pkgname->m_name= caller->m_name;
+ // Package initialization section is calling a non-qualified routine
+ *pkg_routine_handler= package_routine_handler();
+ return name->make_package_routine_name(thd->mem_root,
+ caller->m_name, name->m_name);
+ }
+
+ return false; // A standalone routine is called
+
+}
+
+
+/**
+ Detect cases when a package routine (rather than a standalone routine)
+ is called, and rewrite sp_name accordingly.
+
+ @param thd Current thd
+ @param caller The caller routine (or NULL if outside of a routine)
+ @param [IN/OUT] name The called routine name
+ @param [OUT] pkgname If the routine is found to be a package routine,
+ pkgname is populated with the package name.
+ Otherwise, it's not touched.
+ @retval false on success
+ @retval true on error (e.g. EOM, could not read CREATE PACKAGE)
+*/
+bool
+Sp_handler::sp_resolve_package_routine(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_handler,
+ Database_qualified_name *pkgname) const
+{
+ if (!thd->db.length || !(thd->variables.sql_mode & MODE_ORACLE))
+ return false;
+
+ return name->m_explicit_name ?
+ sp_resolve_package_routine_explicit(thd, caller, name,
+ pkg_routine_handler, pkgname) :
+ sp_resolve_package_routine_implicit(thd, caller, name,
+ pkg_routine_handler, pkgname);
+}
+
+
/**
Add routine which is explicitly used by statement to the set of stored
routines used by this statement.
@@ -2023,7 +2574,7 @@ void Sp_handler::add_used_routine(Query_tables_list *prelocking_ctx,
const Database_qualified_name *rt) const
{
MDL_key key(get_mdl_type(), rt->m_db.str, rt->m_name.str);
- (void) sp_add_used_routine(prelocking_ctx, arena, &key, 0);
+ (void) sp_add_used_routine(prelocking_ctx, arena, &key, this, 0);
prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next;
prelocking_ctx->sroutines_list_own_elements=
prelocking_ctx->sroutines_list.elements;
@@ -2116,7 +2667,8 @@ sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
{
Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i);
(void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
- &rt->mdl_request.key, belong_to_view);
+ &rt->mdl_request.key, rt->m_handler,
+ belong_to_view);
}
}
@@ -2141,7 +2693,8 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
{
for (Sroutine_hash_entry *rt= src->first; rt; rt= rt->next)
(void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
- &rt->mdl_request.key, belong_to_view);
+ &rt->mdl_request.key, rt->m_handler,
+ belong_to_view);
}
@@ -2156,9 +2709,6 @@ int Sroutine_hash_entry::sp_cache_routine(THD *thd,
{
char qname_buff[NAME_LEN*2+1+1];
sp_name name(&mdl_request.key, qname_buff);
- MDL_key::enum_mdl_namespace mdl_type= mdl_request.key.mdl_namespace();
- const Sp_handler *sph= Sp_handler::handler(mdl_type);
- DBUG_ASSERT(sph);
/*
Check that we have an MDL lock on this routine, unless it's a top-level
CALL. The assert below should be unambiguous: the first element
@@ -2167,7 +2717,7 @@ int Sroutine_hash_entry::sp_cache_routine(THD *thd,
*/
DBUG_ASSERT(mdl_request.ticket || this == thd->lex->sroutines_list.first);
- return sph->sp_cache_routine(thd, &name, lookup_only, sp);
+ return m_handler->sp_cache_routine(thd, &name, lookup_only, sp);
}
@@ -2198,7 +2748,7 @@ int Sp_handler::sp_cache_routine(THD *thd,
int ret= 0;
sp_cache **spc= get_cache(thd);
- DBUG_ENTER("sp_cache_routine");
+ DBUG_ENTER("Sp_handler::sp_cache_routine");
DBUG_ASSERT(spc);
@@ -2249,6 +2799,75 @@ int Sp_handler::sp_cache_routine(THD *thd,
/**
+ Cache a package routine using its package name and a qualified name.
+ See sp_cache_routine() for more information on parameters and return values.
+
+ @param thd - current THD
+ @param pkgname_str - package name, e.g. "pkgname"
+ @param name - name with the following format:
+ * name->m_db is a database name, e.g. "dbname"
+ * name->m_name is a package-qualified name,
+ e.g. "pkgname.spname"
+ @param lookup_only - don't load mysql.proc if not cached
+ @param [OUT] sp - the result is returned here.
+ @retval false - loaded or does not exists
+ @retval true - error while loading mysql.proc
+*/
+int
+Sp_handler::sp_cache_package_routine(THD *thd,
+ const LEX_CSTRING &pkgname_cstr,
+ const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const
+{
+ DBUG_ENTER("sp_cache_package_routine");
+ DBUG_ASSERT(type() == TYPE_ENUM_FUNCTION || type() == TYPE_ENUM_PROCEDURE);
+ sp_name pkgname(&name->m_db, &pkgname_cstr, false);
+ sp_head *ph= NULL;
+ int ret= sp_handler_package_body.sp_cache_routine(thd, &pkgname,
+ lookup_only,
+ &ph);
+ if (!ret)
+ {
+ sp_package *pkg= ph ? ph->get_package() : NULL;
+ LEX_CSTRING tmp= name->m_name;
+ const char *dot= strrchr(tmp.str, '.');
+ size_t prefix_length= dot ? dot - tmp.str + 1 : NULL;
+ tmp.str+= prefix_length;
+ tmp.length-= prefix_length;
+ LEX *rlex= pkg ? pkg->m_routine_implementations.find(tmp, type()) : NULL;
+ *sp= rlex ? rlex->sphead : NULL;
+ }
+
+ DBUG_RETURN(ret);
+}
+
+
+/**
+ Cache a package routine by its fully qualified name.
+ See sp_cache_routine() for more information on parameters and return values.
+
+ @param thd - current THD
+ @param name - name with the following format:
+ * name->m_db is a database name, e.g. "dbname"
+ * name->m_name is a package-qualified name,
+ e.g. "pkgname.spname"
+ @param lookup_only - don't load mysql.proc if not cached
+ @param [OUT] sp - the result is returned here
+ @retval false - loaded or does not exists
+ @retval true - error while loading mysql.proc
+*/
+int Sp_handler::sp_cache_package_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const
+{
+ DBUG_ENTER("Sp_handler::sp_cache_package_routine");
+ Prefix_name_buf pkgname(thd, name->m_name);
+ DBUG_ASSERT(pkgname.length);
+ DBUG_RETURN(sp_cache_package_routine(thd, pkgname, name, lookup_only, sp));
+}
+
+
+/**
Generates the CREATE... string from the table information.
@return
@@ -2385,7 +3004,7 @@ Sp_handler::sp_load_for_information_schema(THD *thd, TABLE *proc_table,
thd->lex= &newlex;
newlex.current_select= NULL;
- sp= sp_compile(thd, &defstr, sql_mode, creation_ctx);
+ sp= sp_compile(thd, &defstr, sql_mode, NULL, creation_ctx);
*free_sp_head= 1;
thd->lex->sphead= NULL;
lex_end(thd->lex);
diff --git a/sql/sp.h b/sql/sp.h
index 4177ff05868..48ed7165c55 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -31,7 +31,9 @@ class Sroutine_hash_entry;
class THD;
class sp_cache;
class sp_head;
+class sp_package;
class sp_pcontext;
+class sp_name;
class Database_qualified_name;
struct st_sp_chistics;
class Stored_program_creation_ctx;
@@ -49,13 +51,28 @@ enum stored_procedure_type
{
TYPE_ENUM_FUNCTION=1,
TYPE_ENUM_PROCEDURE=2,
- TYPE_ENUM_TRIGGER=3,
- TYPE_ENUM_PROXY=4
+ TYPE_ENUM_PACKAGE=3,
+ TYPE_ENUM_PACKAGE_BODY=4,
+ TYPE_ENUM_TRIGGER=5,
+ TYPE_ENUM_PROXY=6
};
class Sp_handler
{
+ bool sp_resolve_package_routine_explicit(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_hndlr,
+ Database_qualified_name *pkgname)
+ const;
+ bool sp_resolve_package_routine_implicit(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_hndlr,
+ Database_qualified_name *pkgname)
+ const;
+protected:
int db_find_routine_aux(THD *thd, const Database_qualified_name *name,
TABLE *table) const;
int db_find_routine(THD *thd, const Database_qualified_name *name,
@@ -72,6 +89,7 @@ class Sp_handler
const st_sp_chistics &chistics,
const AUTHID &definer,
longlong created, longlong modified,
+ sp_package *parent,
Stored_program_creation_ctx *creation_ctx) const;
int sp_drop_routine_internal(THD *thd,
const Database_qualified_name *name,
@@ -80,11 +98,37 @@ class Sp_handler
sp_head *sp_clone_and_link_routine(THD *thd,
const Database_qualified_name *name,
sp_head *sp) const;
+ int sp_cache_package_routine(THD *thd,
+ const LEX_CSTRING &pkgname_cstr,
+ const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const;
+ int sp_cache_package_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const;
+ sp_head *sp_find_package_routine(THD *thd,
+ const LEX_CSTRING pkgname_str,
+ const Database_qualified_name *name,
+ bool cache_only) const;
+ sp_head *sp_find_package_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const;
+public: // TODO: make it private or protected
+ virtual int sp_find_and_drop_routine(THD *thd, TABLE *table,
+ const Database_qualified_name *name)
+ const;
+
public:
virtual ~Sp_handler() {}
static const Sp_handler *handler(enum enum_sql_command cmd);
static const Sp_handler *handler(stored_procedure_type type);
static const Sp_handler *handler(MDL_key::enum_mdl_namespace ns);
+ static bool eq_routine_name(const LEX_CSTRING &name1,
+ const LEX_CSTRING &name2)
+ {
+ return my_strnncoll(system_charset_info,
+ (const uchar *) name1.str, name1.length,
+ (const uchar *) name2.str, name2.length) == 0;
+ }
const char *type_str() const { return type_lex_cstring().str; }
virtual const char *show_create_routine_col1_caption() const
{
@@ -96,6 +140,10 @@ public:
DBUG_ASSERT(0);
return "";
}
+ virtual const Sp_handler *package_routine_handler() const
+ {
+ return this;
+ }
virtual stored_procedure_type type() const= 0;
virtual LEX_CSTRING type_lex_cstring() const= 0;
virtual LEX_CSTRING empty_body_lex_cstring() const
@@ -132,12 +180,22 @@ public:
void add_used_routine(Query_tables_list *prelocking_ctx,
Query_arena *arena,
- const Database_qualified_name *rt) const;
-
- sp_head *sp_find_routine(THD *thd, const Database_qualified_name *name,
- bool cache_only) const;
- int sp_cache_routine(THD *thd, const Database_qualified_name *name,
- bool lookup_only, sp_head **sp) const;
+ const Database_qualified_name *name) const;
+
+ bool sp_resolve_package_routine(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_handler,
+ Database_qualified_name *pkgname) const;
+ virtual sp_head *sp_find_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const;
+ virtual int sp_cache_routine(THD *thd, const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const;
+
+ int sp_cache_routine_reentrant(THD *thd,
+ const Database_qualified_name *nm,
+ sp_head **sp) const;
bool sp_exist_routines(THD *thd, TABLE_LIST *procs) const;
bool sp_show_create_routine(THD *thd,
@@ -163,16 +221,17 @@ public:
@retval true on error
@retval false on success
*/
- bool show_create_sp(THD *thd, String *buf,
- const LEX_CSTRING &db,
- const LEX_CSTRING &name,
- const LEX_CSTRING &params,
- const LEX_CSTRING &returns,
- const LEX_CSTRING &body,
- const st_sp_chistics &chistics,
- const AUTHID &definer,
- const DDL_options_st ddl_options,
- sql_mode_t sql_mode) const;
+ virtual bool show_create_sp(THD *thd, String *buf,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ const st_sp_chistics &chistics,
+ const AUTHID &definer,
+ const DDL_options_st ddl_options,
+ sql_mode_t sql_mode) const;
+
};
@@ -202,6 +261,7 @@ public:
{
return MDL_key::PROCEDURE;
}
+ const Sp_handler *package_routine_handler() const;
sp_cache **get_cache(THD *) const;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
HASH *get_priv_hash() const;
@@ -212,6 +272,23 @@ public:
};
+class Sp_handler_package_procedure: public Sp_handler_procedure
+{
+public:
+ int sp_cache_routine(THD *thd, const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const
+ {
+ return sp_cache_package_routine(thd, name, lookup_only, sp);
+ }
+ sp_head *sp_find_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const
+ {
+ return sp_find_package_routine(thd, name, cache_only);
+ }
+};
+
+
class Sp_handler_function: public Sp_handler
{
public:
@@ -238,6 +315,7 @@ public:
{
return MDL_key::FUNCTION;
}
+ const Sp_handler *package_routine_handler() const;
sp_cache **get_cache(THD *) const;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
HASH *get_priv_hash() const;
@@ -247,6 +325,109 @@ public:
};
+class Sp_handler_package_function: public Sp_handler_function
+{
+public:
+ int sp_cache_routine(THD *thd, const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const
+ {
+ return sp_cache_package_routine(thd, name, lookup_only, sp);
+ }
+ sp_head *sp_find_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const
+ {
+ return sp_find_package_routine(thd, name, cache_only);
+ }
+};
+
+
+class Sp_handler_package: public Sp_handler
+{
+public:
+ bool show_create_sp(THD *thd, String *buf,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ const st_sp_chistics &chistics,
+ const AUTHID &definer,
+ const DDL_options_st ddl_options,
+ sql_mode_t sql_mode) const;
+};
+
+
+class Sp_handler_package_spec: public Sp_handler_package
+{
+public: // TODO: make it private or protected
+ int sp_find_and_drop_routine(THD *thd, TABLE *table,
+ const Database_qualified_name *name)
+ const;
+public:
+ stored_procedure_type type() const { return TYPE_ENUM_PACKAGE; }
+ LEX_CSTRING type_lex_cstring() const
+ {
+ static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("PACKAGE")};
+ return m_type_str;
+ }
+ LEX_CSTRING empty_body_lex_cstring() const
+ {
+ static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("BEGIN END")};
+ return m_empty_body;
+ }
+ const char *show_create_routine_col1_caption() const
+ {
+ return "Package";
+ }
+ const char *show_create_routine_col3_caption() const
+ {
+ return "Create Package";
+ }
+ MDL_key::enum_mdl_namespace get_mdl_type() const
+ {
+ return MDL_key::PACKAGE_BODY;
+ }
+ sp_cache **get_cache(THD *) const;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ HASH *get_priv_hash() const;
+#endif
+};
+
+
+class Sp_handler_package_body: public Sp_handler_package
+{
+public:
+ stored_procedure_type type() const { return TYPE_ENUM_PACKAGE_BODY; }
+ LEX_CSTRING type_lex_cstring() const
+ {
+ static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("PACKAGE BODY")};
+ return m_type_str;
+ }
+ LEX_CSTRING empty_body_lex_cstring() const
+ {
+ static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("BEGIN END")};
+ return m_empty_body;
+ }
+ const char *show_create_routine_col1_caption() const
+ {
+ return "Package body";
+ }
+ const char *show_create_routine_col3_caption() const
+ {
+ return "Create Package Body";
+ }
+ MDL_key::enum_mdl_namespace get_mdl_type() const
+ {
+ return MDL_key::PACKAGE_BODY;
+ }
+ sp_cache **get_cache(THD *) const;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ HASH *get_priv_hash() const;
+#endif
+};
+
+
class Sp_handler_trigger: public Sp_handler
{
public:
@@ -266,6 +447,10 @@ public:
extern MYSQL_PLUGIN_IMPORT Sp_handler_function sp_handler_function;
extern MYSQL_PLUGIN_IMPORT Sp_handler_procedure sp_handler_procedure;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_package_spec sp_handler_package_spec;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_package_body sp_handler_package_body;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_package_function sp_handler_package_function;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_package_procedure sp_handler_package_procedure;
extern MYSQL_PLUGIN_IMPORT Sp_handler_trigger sp_handler_trigger;
@@ -286,6 +471,17 @@ inline const Sp_handler *Sp_handler::handler(enum_sql_command cmd)
case SQLCOM_SHOW_CREATE_FUNC:
case SQLCOM_SHOW_STATUS_FUNC:
return &sp_handler_function;
+ case SQLCOM_CREATE_PACKAGE:
+ case SQLCOM_DROP_PACKAGE:
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ return &sp_handler_package_spec;
+ case SQLCOM_CREATE_PACKAGE_BODY:
+ case SQLCOM_DROP_PACKAGE_BODY:
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
+ case SQLCOM_SHOW_PACKAGE_BODY_CODE:
+ return &sp_handler_package_body;
default:
break;
}
@@ -300,6 +496,10 @@ inline const Sp_handler *Sp_handler::handler(stored_procedure_type type)
return &sp_handler_procedure;
case TYPE_ENUM_FUNCTION:
return &sp_handler_function;
+ case TYPE_ENUM_PACKAGE:
+ return &sp_handler_package_spec;
+ case TYPE_ENUM_PACKAGE_BODY:
+ return &sp_handler_package_body;
case TYPE_ENUM_TRIGGER:
return &sp_handler_trigger;
case TYPE_ENUM_PROXY:
@@ -316,6 +516,8 @@ inline const Sp_handler *Sp_handler::handler(MDL_key::enum_mdl_namespace type)
return &sp_handler_function;
case MDL_key::PROCEDURE:
return &sp_handler_procedure;
+ case MDL_key::PACKAGE_BODY:
+ return &sp_handler_package_body;
case MDL_key::GLOBAL:
case MDL_key::SCHEMA:
case MDL_key::TABLE:
@@ -425,12 +627,16 @@ public:
*/
ulong m_sp_cache_version;
+ const Sp_handler *m_handler;
+
int sp_cache_routine(THD *thd, bool lookup_only, sp_head **sp) const;
};
bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
- const MDL_key *key, TABLE_LIST *belong_to_view);
+ const MDL_key *key,
+ const Sp_handler *handler,
+ TABLE_LIST *belong_to_view);
void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx);
bool sp_update_sp_used_routines(HASH *dst, HASH *src);
void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index cdb5e256e46..c088dc6ba12 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -192,6 +192,8 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_CREATE_DB:
case SQLCOM_SHOW_CREATE_FUNC:
case SQLCOM_SHOW_CREATE_PROC:
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
case SQLCOM_SHOW_CREATE_EVENT:
case SQLCOM_SHOW_CREATE_TRIGGER:
case SQLCOM_SHOW_CREATE_USER:
@@ -211,11 +213,14 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_PRIVILEGES:
case SQLCOM_SHOW_PROCESSLIST:
case SQLCOM_SHOW_PROC_CODE:
+ case SQLCOM_SHOW_PACKAGE_BODY_CODE:
case SQLCOM_SHOW_SLAVE_HOSTS:
case SQLCOM_SHOW_SLAVE_STAT:
case SQLCOM_SHOW_STATUS:
case SQLCOM_SHOW_STATUS_FUNC:
case SQLCOM_SHOW_STATUS_PROC:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
case SQLCOM_SHOW_STORAGE_ENGINES:
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TABLE_STATUS:
@@ -260,6 +265,8 @@ sp_get_flags_for_command(LEX *lex)
break;
case SQLCOM_CREATE_INDEX:
case SQLCOM_CREATE_DB:
+ case SQLCOM_CREATE_PACKAGE:
+ case SQLCOM_CREATE_PACKAGE_BODY:
case SQLCOM_CREATE_VIEW:
case SQLCOM_CREATE_TRIGGER:
case SQLCOM_CREATE_USER:
@@ -276,6 +283,8 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_RENAME_USER:
case SQLCOM_DROP_INDEX:
case SQLCOM_DROP_DB:
+ case SQLCOM_DROP_PACKAGE:
+ case SQLCOM_DROP_PACKAGE_BODY:
case SQLCOM_REVOKE_ALL:
case SQLCOM_DROP_USER:
case SQLCOM_DROP_ROLE:
@@ -502,9 +511,10 @@ sp_head::operator delete(void *ptr, size_t size) throw()
}
-sp_head::sp_head(const Sp_handler *sph)
+sp_head::sp_head(sp_package *parent, const Sp_handler *sph)
:Query_arena(&main_mem_root, STMT_INITIALIZED_FOR_SP),
Database_qualified_name(&null_clex_str, &null_clex_str),
+ m_parent(parent),
m_handler(sph),
m_flags(0),
m_tmp_query(NULL),
@@ -529,6 +539,9 @@ sp_head::sp_head(const Sp_handler *sph)
m_param_begin(NULL),
m_param_end(NULL),
m_body_begin(NULL),
+ m_thd_root(NULL),
+ m_thd(NULL),
+ m_pcont(new (&main_mem_root) sp_pcontext()),
m_cont_level(0)
{
m_first_instance= this;
@@ -543,6 +556,7 @@ sp_head::sp_head(const Sp_handler *sph)
m_backpatch_goto.empty();
m_cont_backpatch.empty();
m_lex.empty();
+ my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8, MYF(0));
my_hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
my_hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key,
0, 0);
@@ -551,12 +565,169 @@ sp_head::sp_head(const Sp_handler *sph)
}
+sp_package::sp_package(LEX *top_level_lex,
+ const sp_name *name,
+ const Sp_handler *sph)
+ :sp_head(NULL, sph),
+ m_current_routine(NULL),
+ m_top_level_lex(top_level_lex),
+ m_rcontext(NULL),
+ m_invoked_subroutine_count(0),
+ m_is_instantiated(false),
+ m_is_cloning_routine(false)
+{
+ init_sp_name(name);
+}
+
+
+sp_package::~sp_package()
+{
+ m_routine_implementations.cleanup();
+ m_routine_declarations.cleanup();
+ m_body= null_clex_str;
+ if (m_current_routine)
+ delete m_current_routine->sphead;
+ delete m_rcontext;
+}
+
+
+/*
+ Test if two routines have equal specifications
+*/
+bool sp_head::eq_routine_spec(const sp_head *sp) const
+{
+ // TODO: Add tests for equal return data types (in case of FUNCTION)
+ // TODO: Add tests for equal argument data types
+ return
+ m_handler->type() == sp->m_handler->type() &&
+ m_pcont->context_var_count() == sp->m_pcont->context_var_count();
+}
+
+
+bool sp_package::validate_after_parser(THD *thd)
+{
+ if (m_handler->type() != TYPE_ENUM_PACKAGE_BODY)
+ return false;
+ sp_head *sp= sp_cache_lookup(&thd->sp_package_spec_cache, this);
+ sp_package *spec= sp ? sp->get_package() : NULL;
+ DBUG_ASSERT(spec); // CREATE PACKAGE must already be cached
+ return validate_public_routines(thd, spec) ||
+ validate_private_routines(thd);
+}
+
+
+bool sp_package::validate_public_routines(THD *thd, sp_package *spec)
+{
+ /*
+ Check that all routines declared in CREATE PACKAGE
+ have implementations in CREATE PACKAGE BODY.
+ */
+ List_iterator<LEX> it(spec->m_routine_declarations);
+ for (LEX *lex; (lex= it++); )
+ {
+ bool found= false;
+ DBUG_ASSERT(lex->sphead);
+ List_iterator<LEX> it2(m_routine_implementations);
+ for (LEX *lex2; (lex2= it2++); )
+ {
+ DBUG_ASSERT(lex2->sphead);
+ if (Sp_handler::eq_routine_name(lex2->sphead->m_name,
+ lex->sphead->m_name) &&
+ lex2->sphead->eq_routine_spec(lex->sphead))
+ {
+ found= true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ my_error(ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY, MYF(0),
+ ErrConvDQName(lex->sphead).ptr());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool sp_package::validate_private_routines(THD *thd)
+{
+ /*
+ Check that all forwad declarations in
+ CREATE PACKAGE BODY have implementations.
+ */
+ List_iterator<LEX> it(m_routine_declarations);
+ for (LEX *lex; (lex= it++); )
+ {
+ bool found= false;
+ DBUG_ASSERT(lex->sphead);
+ List_iterator<LEX> it2(m_routine_implementations);
+ for (LEX *lex2; (lex2= it2++); )
+ {
+ DBUG_ASSERT(lex2->sphead);
+ if (Sp_handler::eq_routine_name(lex2->sphead->m_name,
+ lex->sphead->m_name) &&
+ lex2->sphead->eq_routine_spec(lex->sphead))
+ {
+ found= true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ my_error(ER_PACKAGE_ROUTINE_FORWARD_DECLARATION_NOT_DEFINED, MYF(0),
+ ErrConvDQName(lex->sphead).ptr());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+LEX *sp_package::LexList::find(const LEX_CSTRING &name,
+ stored_procedure_type type)
+{
+ List_iterator<LEX> it(*this);
+ for (LEX *lex; (lex= it++); )
+ {
+ DBUG_ASSERT(lex->sphead);
+ const char *dot;
+ if (lex->sphead->m_handler->type() == type &&
+ (dot= strrchr(lex->sphead->m_name.str, '.')))
+ {
+ size_t ofs= dot + 1 - lex->sphead->m_name.str;
+ LEX_CSTRING non_qualified_sphead_name= lex->sphead->m_name;
+ non_qualified_sphead_name.str+= ofs;
+ non_qualified_sphead_name.length-= ofs;
+ if (Sp_handler::eq_routine_name(non_qualified_sphead_name, name))
+ return lex;
+ }
+ }
+ return NULL;
+}
+
+
+LEX *sp_package::LexList::find_qualified(const LEX_CSTRING &name,
+ stored_procedure_type type)
+{
+ List_iterator<LEX> it(*this);
+ for (LEX *lex; (lex= it++); )
+ {
+ DBUG_ASSERT(lex->sphead);
+ if (lex->sphead->m_handler->type() == type &&
+ Sp_handler::eq_routine_name(lex->sphead->m_name, name))
+ return lex;
+ }
+ return NULL;
+}
+
+
void
sp_head::init(LEX *lex)
{
DBUG_ENTER("sp_head::init");
- lex->spcont= m_pcont= new sp_pcontext();
+ lex->spcont= m_pcont;
if (!lex->spcont)
DBUG_VOID_RETURN;
@@ -566,7 +737,6 @@ sp_head::init(LEX *lex)
types of stored procedures to simplify reset_lex()/restore_lex() code.
*/
lex->trg_table_fields.empty();
- my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8, MYF(0));
DBUG_VOID_RETURN;
}
@@ -584,9 +754,6 @@ sp_head::init_sp_name(const sp_name *spname)
/* We have to copy strings to get them into the right memroot. */
Database_qualified_name::copy(&main_mem_root, spname->m_db, spname->m_name);
m_explicit_name= spname->m_explicit_name;
-
- spname->make_qname(&main_mem_root, &m_qname);
-
DBUG_VOID_RETURN;
}
@@ -681,6 +848,17 @@ sp_head::~sp_head()
}
+void sp_package::LexList::cleanup()
+{
+ List_iterator<LEX> it(*this);
+ for (LEX *lex; (lex= it++); )
+ {
+ lex_end(lex);
+ delete lex;
+ }
+}
+
+
/**
This is only used for result fields from functions (both during
fix_length_and_dec() and evaluation).
@@ -992,6 +1170,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
DBUG_ASSERT(!(m_flags & IS_INVOKED));
m_flags|= IS_INVOKED;
+ if (m_parent)
+ m_parent->m_invoked_subroutine_count++;
m_first_instance->m_first_free_instance= m_next_cached_sp;
if (m_next_cached_sp)
{
@@ -1312,6 +1492,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
err_status|= mysql_change_db(thd, (LEX_CSTRING*) &saved_cur_db_name, TRUE);
}
m_flags&= ~IS_INVOKED;
+ if (m_parent)
+ m_parent->m_invoked_subroutine_count--;
DBUG_PRINT("info",
("first free for %p --: %p->%p, level: %lu, flags %x",
m_first_instance,
@@ -1385,8 +1567,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx)
statement that 'may' affect this.
*/
if (*save_ctx &&
- check_routine_access(thd, EXECUTE_ACL,
- &sp->m_db, &sp->m_name, sp->m_handler, false))
+ sp->check_execute_access(thd))
{
sp->m_security_ctx.restore_security_context(thd, *save_ctx);
*save_ctx= 0;
@@ -1400,9 +1581,10 @@ set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx)
bool sp_head::check_execute_access(THD *thd) const
{
- return check_routine_access(thd, EXECUTE_ACL,
- &m_db, &m_name,
- m_handler, false);
+ return m_parent ? m_parent->check_execute_access(thd) :
+ check_routine_access(thd, EXECUTE_ACL,
+ &m_db, &m_name,
+ m_handler, false);
}
@@ -1593,6 +1775,42 @@ err_with_cleanup:
}
+/*
+ Execute the package initialization section.
+*/
+bool sp_package::instantiate_if_needed(THD *thd)
+{
+ List<Item> args;
+ if (m_is_instantiated)
+ return false;
+ /*
+ Set m_is_instantiated to true early, to avoid recursion in case if
+ the package initialization section calls routines from the same package.
+ */
+ m_is_instantiated= true;
+ /*
+ Check that the initialization section doesn't contain Dynamic SQL
+ and doesn't return result sets: such stored procedures can't
+ be called from a function or trigger.
+ */
+ if (thd->in_sub_stmt)
+ {
+ const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
+ "trigger" : "function");
+ if (is_not_allowed_in_function(where))
+ goto err;
+ }
+
+ args.elements= 0;
+ if (execute_procedure(thd, &args))
+ goto err;
+ return false;
+err:
+ m_is_instantiated= false;
+ return true;
+}
+
+
/**
Execute a function.
@@ -1642,6 +1860,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
DBUG_ENTER("sp_head::execute_function");
DBUG_PRINT("info", ("function %s", m_name.str));
+ if (m_parent && m_parent->instantiate_if_needed(thd))
+ DBUG_RETURN(true);
+
/*
Check that the function is called with all specified arguments.
@@ -1869,9 +2090,13 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
sp_rcontext *nctx = NULL;
bool save_enable_slow_log;
bool save_log_general= false;
+ sp_package *pkg= get_package();
DBUG_ENTER("sp_head::execute_procedure");
DBUG_PRINT("info", ("procedure %s", m_name.str));
+ if (m_parent && m_parent->instantiate_if_needed(thd))
+ DBUG_RETURN(true);
+
if (args->elements != params)
{
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "PROCEDURE",
@@ -1895,11 +2120,32 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
thd->spcont->callers_arena= thd;
}
- if (!(nctx= rcontext_create(thd, NULL, args)))
+ if (!pkg)
{
- delete nctx; /* Delete nctx if it was init() that failed. */
- thd->spcont= save_spcont;
- DBUG_RETURN(TRUE);
+ if (!(nctx= rcontext_create(thd, NULL, args)))
+ {
+ delete nctx; /* Delete nctx if it was init() that failed. */
+ thd->spcont= save_spcont;
+ DBUG_RETURN(TRUE);
+ }
+ }
+ else
+ {
+ if (!pkg->m_rcontext)
+ {
+ Query_arena backup_arena;
+ thd->set_n_backup_active_arena(this, &backup_arena);
+ nctx= pkg->rcontext_create(thd, NULL, args);
+ thd->restore_active_arena(this, &backup_arena);
+ if (!nctx)
+ {
+ thd->spcont= save_spcont;
+ DBUG_RETURN(TRUE);
+ }
+ pkg->m_rcontext= nctx;
+ }
+ else
+ nctx= pkg->m_rcontext;
}
if (params > 0)
@@ -2105,7 +2351,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!save_spcont)
delete octx;
- delete nctx;
+ if (!pkg)
+ delete nctx;
thd->spcont= save_spcont;
thd->utime_after_lock= utime_before_sp_exec;
@@ -3366,10 +3613,16 @@ sp_instr_set::execute(THD *thd, uint *nextp)
}
+sp_rcontext *sp_instr_set::get_rcontext(THD *thd) const
+{
+ return m_rcontext_handler->get_rcontext(thd->spcont);
+}
+
+
int
sp_instr_set::exec_core(THD *thd, uint *nextp)
{
- int res= thd->spcont->set_variable(thd, m_offset, &m_value);
+ int res= get_rcontext(thd)->set_variable(thd, m_offset, &m_value);
delete_explain_query(thd->lex);
*nextp = m_ip+1;
return res;
@@ -3381,13 +3634,15 @@ sp_instr_set::print(String *str)
/* set name@offset ... */
size_t rsrv = SP_INSTR_UINT_MAXLEN+6;
sp_variable *var = m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
/* 'var' should always be non-null, but just in case... */
if (var)
- rsrv+= var->name.length;
+ rsrv+= var->name.length + prefix->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix->str, prefix->length);
if (var)
{
str->qs_append(&var->name);
@@ -3407,8 +3662,9 @@ sp_instr_set::print(String *str)
int
sp_instr_set_row_field::exec_core(THD *thd, uint *nextp)
{
- int res= thd->spcont->set_variable_row_field(thd, m_offset, m_field_offset,
- &m_value);
+ int res= get_rcontext(thd)->set_variable_row_field(thd, m_offset,
+ m_field_offset,
+ &m_value);
delete_explain_query(thd->lex);
*nextp= m_ip + 1;
return res;
@@ -3421,16 +3677,18 @@ sp_instr_set_row_field::print(String *str)
/* set name@offset[field_offset] ... */
size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3;
sp_variable *var= m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
DBUG_ASSERT(var);
DBUG_ASSERT(var->field_def.is_row());
const Column_definition *def=
var->field_def.row_field_definitions()->elem(m_field_offset);
DBUG_ASSERT(def);
- rsrv+= var->name.length + def->field_name.length;
+ rsrv+= var->name.length + def->field_name.length + prefix->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix);
str->qs_append(&var->name);
str->qs_append('.');
str->qs_append(&def->field_name);
@@ -3452,9 +3710,9 @@ sp_instr_set_row_field::print(String *str)
int
sp_instr_set_row_field_by_name::exec_core(THD *thd, uint *nextp)
{
- int res= thd->spcont->set_variable_row_field_by_name(thd, m_offset,
- m_field_name,
- &m_value);
+ int res= get_rcontext(thd)->set_variable_row_field_by_name(thd, m_offset,
+ m_field_name,
+ &m_value);
delete_explain_query(thd->lex);
*nextp= m_ip + 1;
return res;
@@ -3467,14 +3725,16 @@ sp_instr_set_row_field_by_name::print(String *str)
/* set name.field@offset["field"] ... */
size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2;
sp_variable *var= m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
DBUG_ASSERT(var);
DBUG_ASSERT(var->field_def.is_table_rowtype_ref() ||
var->field_def.is_cursor_rowtype_ref());
- rsrv+= var->name.length + 2 * m_field_name.length;
+ rsrv+= var->name.length + 2 * m_field_name.length + prefix->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix);
str->qs_append(&var->name);
str->qs_append('.');
str->qs_append(&m_field_name);
@@ -4639,6 +4899,7 @@ Item *sp_head::adjust_assignment_source(THD *thd, Item *val, Item *val2)
bool
sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
sp_variable *spv, Item *val, LEX *lex,
bool responsible_to_free_lex)
{
@@ -4646,7 +4907,7 @@ sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
return true;
sp_instr_set *sp_set= new (thd->mem_root)
- sp_instr_set(instructions(), spcont,
+ sp_instr_set(instructions(), spcont, rh,
spv->offset, val, lex,
responsible_to_free_lex);
@@ -4659,6 +4920,7 @@ sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
*/
bool
sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
sp_variable *spv, uint field_idx,
Item *val, LEX *lex)
{
@@ -4667,7 +4929,7 @@ sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
sp_instr_set_row_field *sp_set= new (thd->mem_root)
sp_instr_set_row_field(instructions(),
- spcont,
+ spcont, rh,
spv->offset,
field_idx, val,
lex, true);
@@ -4677,6 +4939,7 @@ sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
bool
sp_head::set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
sp_variable *spv,
const LEX_CSTRING *field_name,
Item *val, LEX *lex)
@@ -4686,7 +4949,7 @@ sp_head::set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
sp_instr_set_row_field_by_name *sp_set=
new (thd->mem_root) sp_instr_set_row_field_by_name(instructions(),
- spcont,
+ spcont, rh,
spv->offset,
*field_name,
val,
@@ -4766,6 +5029,7 @@ sp_head::add_set_for_loop_cursor_param_variables(THD *thd,
bool last= idx + 1 == parameters->argument_count();
sp_variable *spvar= param_spcont->get_context_variable(idx);
if (set_local_variable(thd, param_spcont,
+ &sp_rcontext_handler_local,
spvar, parameters->arguments()[idx],
param_lex, last))
return true;
@@ -4837,3 +5101,44 @@ bool sp_head::spvar_fill_table_rowtype_reference(THD *thd,
fill_spvar_using_table_rowtype_reference(thd, spvar, ref);
return false;
}
+
+
+/*
+ In Oracle mode stored routines have an optional name
+ at the end of a declaration:
+ PROCEDURE p1 AS
+ BEGIN
+ NULL
+ END p1;
+ Check that the first p1 and the last p1 match.
+*/
+bool sp_head::check_package_routine_end_name(const LEX_CSTRING &end_name) const
+{
+ LEX_CSTRING non_qualified_name= m_name;
+ const char *errpos;
+ size_t ofs;
+ if (!end_name.length)
+ return false; // No end name
+ if (!(errpos= strrchr(m_name.str, '.')))
+ {
+ errpos= m_name.str;
+ goto err;
+ }
+ errpos++;
+ ofs= errpos - m_name.str;
+ non_qualified_name.str+= ofs;
+ non_qualified_name.length-= ofs;
+ if (Sp_handler::eq_routine_name(end_name, non_qualified_name))
+ return false;
+err:
+ my_error(ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0), end_name.str, errpos);
+ return true;
+}
+
+
+ulong sp_head::sp_cache_version() const
+{
+ return m_parent ? m_parent->sp_cache_version() :
+ m_sp_cache_version;
+
+}
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 7f041058829..4cef33a76a7 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -132,6 +132,7 @@ class sp_head :private Query_arena,
sp_head(const sp_head &); /**< Prevent use of these */
void operator=(sp_head &);
+protected:
MEM_ROOT main_mem_root;
public:
/** Possible values of m_flags */
@@ -170,6 +171,7 @@ public:
HAS_AGGREGATE_INSTR= 16384
};
+ sp_package *m_parent;
const Sp_handler *m_handler;
uint m_flags; // Boolean attributes of a stored routine
@@ -204,13 +206,13 @@ public:
/**
Is this routine being executed?
*/
- bool is_invoked() const { return m_flags & IS_INVOKED; }
+ virtual bool is_invoked() const { return m_flags & IS_INVOKED; }
/**
Get the value of the SP cache version, as remembered
when the routine was inserted into the cache.
*/
- ulong sp_cache_version() const { return m_sp_cache_version; }
+ ulong sp_cache_version() const;
/** Set the value of the SP cache version. */
void set_sp_cache_version(ulong version_arg) const
@@ -224,6 +226,7 @@ public:
sp_rcontext *rcontext_create(THD *thd, Field *retval,
Row_definition_list *list,
bool switch_security_ctx);
+ bool eq_routine_spec(const sp_head *) const;
private:
/**
Version of the stored routine cache at the moment when the
@@ -319,7 +322,7 @@ public:
static void
operator delete(void *ptr, size_t size) throw ();
- sp_head(const Sp_handler *handler);
+ sp_head(sp_package *parent, const Sp_handler *handler);
/// Initialize after we have reset mem_root
void
@@ -399,15 +402,19 @@ public:
@retval false - on success
*/
bool set_local_variable(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
sp_variable *spv, Item *val, LEX *lex,
bool responsible_to_free_lex);
bool set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
sp_variable *spv, uint field_idx,
Item *val, LEX *lex);
bool set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
sp_variable *spv,
const LEX_CSTRING *field_name,
Item *val, LEX *lex);
+ bool check_package_routine_end_name(const LEX_CSTRING &end_name) const;
private:
/**
Generate a code to set a single cursor parameter variable.
@@ -430,7 +437,9 @@ private:
*/
DBUG_ASSERT(m_thd->free_list == NULL);
m_thd->free_list= prm->get_free_list();
- if (set_local_variable(thd, param_spcont, spvar, prm->get_item(), prm, true))
+ if (set_local_variable(thd, param_spcont,
+ &sp_rcontext_handler_local,
+ spvar, prm->get_item(), prm, true))
return true;
/*
Safety:
@@ -830,9 +839,19 @@ public:
sp_pcontext *get_parse_context() { return m_pcont; }
+ /*
+ Check EXECUTE access:
+ - in case of a standalone rotuine, for the routine itself
+ - in case of a package routine, for the owner package body
+ */
bool check_execute_access(THD *thd) const;
-private:
+ virtual sp_package *get_package()
+ {
+ return NULL;
+ }
+
+protected:
MEM_ROOT *m_thd_root; ///< Temp. store for thd's mem_root
THD *m_thd; ///< Set if we have reset mem_root
@@ -897,6 +916,92 @@ private:
}; // class sp_head : public Sql_alloc
+class sp_package: public sp_head
+{
+ bool validate_public_routines(THD *thd, sp_package *spec);
+ bool validate_private_routines(THD *thd);
+public:
+ class LexList: public List<LEX>
+ {
+ public:
+ LexList() { elements= 0; }
+ // Find a package routine by a non qualified name
+ LEX *find(const LEX_CSTRING &name, stored_procedure_type type);
+ // Find a package routine by a package-qualified name, e.g. 'pkg.proc'
+ LEX *find_qualified(const LEX_CSTRING &name, stored_procedure_type type);
+ // Check if a routine with the given qualified name already exists
+ bool check_dup_qualified(const LEX_CSTRING &name, const Sp_handler *sph)
+ {
+ if (!find_qualified(name, sph->type()))
+ return false;
+ my_error(ER_SP_ALREADY_EXISTS, MYF(0), sph->type_str(), name.str);
+ return true;
+ }
+ bool check_dup_qualified(const sp_head *sp)
+ {
+ return check_dup_qualified(sp->m_name, sp->m_handler);
+ }
+ void cleanup();
+ };
+ /*
+ The LEX for a new package subroutine is initially assigned to
+ m_current_routine. After scanning parameters, return type and chistics,
+ the parser detects if we have a declaration or a definition, e.g.:
+ PROCEDURE p1(a INT);
+ vs
+ PROCEDURE p1(a INT) AS BEGIN NULL; END;
+ (i.e. either semicolon or the "AS" keyword)
+ m_current_routine is then added either to m_routine_implementations,
+ or m_routine_declarations, and then m_current_routine is set to NULL.
+ */
+ LEX *m_current_routine;
+ LexList m_routine_implementations;
+ LexList m_routine_declarations;
+
+ LEX *m_top_level_lex;
+ sp_rcontext *m_rcontext;
+ uint m_invoked_subroutine_count;
+ bool m_is_instantiated;
+ bool m_is_cloning_routine;
+
+ sp_package(LEX *top_level_lex,
+ const sp_name *name,
+ const Sp_handler *sph);
+ ~sp_package();
+ bool add_routine_declaration(LEX *lex)
+ {
+ return m_routine_declarations.check_dup_qualified(lex->sphead) ||
+ m_routine_declarations.push_back(lex, &main_mem_root);
+ }
+ bool add_routine_implementation(LEX *lex)
+ {
+ return m_routine_implementations.check_dup_qualified(lex->sphead) ||
+ m_routine_implementations.push_back(lex, &main_mem_root);
+ }
+ sp_package *get_package() { return this; }
+ bool is_invoked() const
+ {
+ /*
+ Cannot flush a package out of the SP cache when:
+ - its initialization block is running
+ - one of its subroutine is running
+ */
+ return sp_head::is_invoked() || m_invoked_subroutine_count > 0;
+ }
+ sp_variable *find_package_variable(const LEX_CSTRING *name) const
+ {
+ /*
+ sp_head::m_pcont is a special level for routine parameters.
+ Variables declared inside CREATE PACKAGE BODY reside in m_children.at(0).
+ */
+ sp_pcontext *ctx= m_pcont->child_context(0);
+ return ctx ? ctx->find_variable(name, true) : NULL;
+ }
+ bool validate_after_parser(THD *thd);
+ bool instantiate_if_needed(THD *thd);
+};
+
+
class sp_lex_cursor: public sp_lex_local, public Query_arena
{
LEX_CSTRING m_cursor_name;
@@ -1185,9 +1290,11 @@ class sp_instr_set : public sp_instr
public:
sp_instr_set(uint ip, sp_pcontext *ctx,
+ const Sp_rcontext_handler *rh,
uint offset, Item *val,
LEX *lex, bool lex_resp)
- : sp_instr(ip, ctx), m_offset(offset), m_value(val),
+ : sp_instr(ip, ctx),
+ m_rcontext_handler(rh), m_offset(offset), m_value(val),
m_lex_keeper(lex, lex_resp)
{}
@@ -1201,11 +1308,11 @@ public:
virtual void print(String *str);
protected:
-
+ sp_rcontext *get_rcontext(THD *thd) const;
+ const Sp_rcontext_handler *m_rcontext_handler;
uint m_offset; ///< Frame offset
Item *m_value;
sp_lex_keeper m_lex_keeper;
-
}; // class sp_instr_set : public sp_instr
@@ -1223,10 +1330,11 @@ class sp_instr_set_row_field : public sp_instr_set
public:
sp_instr_set_row_field(uint ip, sp_pcontext *ctx,
+ const Sp_rcontext_handler *rh,
uint offset, uint field_offset,
Item *val,
LEX *lex, bool lex_resp)
- : sp_instr_set(ip, ctx, offset, val, lex, lex_resp),
+ : sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp),
m_field_offset(field_offset)
{}
@@ -1265,10 +1373,11 @@ class sp_instr_set_row_field_by_name : public sp_instr_set
public:
sp_instr_set_row_field_by_name(uint ip, sp_pcontext *ctx,
+ const Sp_rcontext_handler *rh,
uint offset, const LEX_CSTRING &field_name,
Item *val,
LEX *lex, bool lex_resp)
- : sp_instr_set(ip, ctx, offset, val, lex, lex_resp),
+ : sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp),
m_field_name(field_name)
{}
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index 6a6920d89e1..e30af3fcde5 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -394,6 +394,9 @@ public:
sp_pcontext *parent_context() const
{ return m_parent; }
+ sp_pcontext *child_context(uint i) const
+ { return i < m_children.elements() ? m_children.at(i) : NULL; }
+
/// Calculate and return the number of handlers to pop between the given
/// context and this one.
///
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 4009f8dce30..2e9ae23d7f9 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -30,6 +30,33 @@
#include "sql_acl.h" // SELECT_ACL
#include "sql_parse.h" // check_table_access
+
+Sp_rcontext_handler_local sp_rcontext_handler_local;
+Sp_rcontext_handler_package_body sp_rcontext_handler_package_body;
+
+sp_rcontext *Sp_rcontext_handler_local::get_rcontext(sp_rcontext *ctx) const
+{
+ return ctx;
+}
+
+sp_rcontext *Sp_rcontext_handler_package_body::get_rcontext(sp_rcontext *ctx) const
+{
+ return ctx->m_sp->m_parent->m_rcontext;
+}
+
+const LEX_CSTRING *Sp_rcontext_handler_local::get_name_prefix() const
+{
+ return &empty_clex_str;
+}
+
+const LEX_CSTRING *Sp_rcontext_handler_package_body::get_name_prefix() const
+{
+ static const LEX_CSTRING sp_package_body_variable_prefix_clex_str=
+ {C_STRING_WITH_LEN("PACKAGE_BODY.")};
+ return &sp_package_body_variable_prefix_clex_str;
+}
+
+
///////////////////////////////////////////////////////////////////////////
// sp_rcontext implementation.
///////////////////////////////////////////////////////////////////////////
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index c4f4cf182da..33d76e1c85a 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -183,7 +183,8 @@ public:
uint instr_ptr;
/// The stored program for which this runtime context is created. Used for
- /// checking if correct runtime context is used for variable handling.
+ /// checking if correct runtime context is used for variable handling,
+ /// and to access the package run-time context.
/// Also used by slow log.
const sp_head *m_sp;
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 96ed36da755..951471bca29 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -607,6 +607,7 @@ static MEM_ROOT acl_memroot, grant_memroot;
static bool initialized=0;
static bool allow_all_hosts=1;
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
+static HASH package_spec_priv_hash, package_body_priv_hash;
static DYNAMIC_ARRAY acl_wild_hosts;
static Hash_filo<acl_entry> *acl_cache;
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
@@ -650,6 +651,18 @@ HASH *Sp_handler_function::get_priv_hash() const
}
+HASH *Sp_handler_package_spec::get_priv_hash() const
+{
+ return &package_spec_priv_hash;
+}
+
+
+HASH *Sp_handler_package_body::get_priv_hash() const
+{
+ return &package_body_priv_hash;
+}
+
+
/*
Enumeration of ACL/GRANT tables in the mysql database
*/
@@ -1312,6 +1325,8 @@ enum enum_acl_lists
COLUMN_PRIVILEGES_HASH,
PROC_PRIVILEGES_HASH,
FUNC_PRIVILEGES_HASH,
+ PACKAGE_SPEC_PRIVILEGES_HASH,
+ PACKAGE_BODY_PRIVILEGES_HASH,
PROXY_USERS_ACL,
ROLES_MAPPINGS_HASH
};
@@ -5401,7 +5416,10 @@ table_error:
******************************************************************/
struct PRIVS_TO_MERGE
{
- enum what { ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC } what;
+ enum what
+ {
+ ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC, PACKAGE_SPEC, PACKAGE_BODY
+ } what;
const char *db, *name;
};
@@ -5413,6 +5431,10 @@ static enum PRIVS_TO_MERGE::what sp_privs_to_merge(stored_procedure_type type)
return PRIVS_TO_MERGE::FUNC;
case TYPE_ENUM_PROCEDURE:
return PRIVS_TO_MERGE::PROC;
+ case TYPE_ENUM_PACKAGE:
+ return PRIVS_TO_MERGE::PACKAGE_SPEC;
+ case TYPE_ENUM_PACKAGE_BODY:
+ return PRIVS_TO_MERGE::PACKAGE_BODY;
case TYPE_ENUM_TRIGGER:
case TYPE_ENUM_PROXY:
break;
@@ -6231,7 +6253,14 @@ static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)),
if (all || data->what == PRIVS_TO_MERGE::FUNC)
changed|= merge_role_routine_grant_privileges(grantee,
data->db, data->name, &role_hash, &func_priv_hash);
-
+ if (all || data->what == PRIVS_TO_MERGE::PACKAGE_SPEC)
+ changed|= merge_role_routine_grant_privileges(grantee,
+ data->db, data->name, &role_hash,
+ &package_spec_priv_hash);
+ if (all || data->what == PRIVS_TO_MERGE::PACKAGE_BODY)
+ changed|= merge_role_routine_grant_privileges(grantee,
+ data->db, data->name, &role_hash,
+ &package_body_priv_hash);
return !changed; // don't recurse into the subgraph if privs didn't change
}
@@ -7116,6 +7145,8 @@ void grant_free(void)
my_hash_free(&column_priv_hash);
my_hash_free(&proc_priv_hash);
my_hash_free(&func_priv_hash);
+ my_hash_free(&package_spec_priv_hash);
+ my_hash_free(&package_body_priv_hash);
free_root(&grant_memroot,MYF(0));
DBUG_VOID_RETURN;
}
@@ -7182,6 +7213,10 @@ static bool grant_load(THD *thd,
0,0,0, (my_hash_get_key) get_grant_table, 0,0);
(void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
0,0,0, (my_hash_get_key) get_grant_table, 0,0);
+ (void) my_hash_init(&package_spec_priv_hash, &my_charset_utf8_bin,
+ 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
+ (void) my_hash_init(&package_body_priv_hash, &my_charset_utf8_bin,
+ 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
init_sql_alloc(&grant_memroot, "GRANT", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
t_table= tables_priv.table();
@@ -7331,6 +7366,7 @@ static my_bool propagate_role_grants_action(void *role_ptr,
bool grant_reload(THD *thd)
{
HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
+ HASH old_package_spec_priv_hash, old_package_body_priv_hash;
MEM_ROOT old_mem;
int result;
DBUG_ENTER("grant_reload");
@@ -7350,6 +7386,8 @@ bool grant_reload(THD *thd)
old_column_priv_hash= column_priv_hash;
old_proc_priv_hash= proc_priv_hash;
old_func_priv_hash= func_priv_hash;
+ old_package_spec_priv_hash= package_spec_priv_hash;
+ old_package_body_priv_hash= package_body_priv_hash;
/*
Create a new memory pool but save the current memory pool to make an undo
@@ -7367,6 +7405,8 @@ bool grant_reload(THD *thd)
column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
proc_priv_hash= old_proc_priv_hash;
func_priv_hash= old_func_priv_hash;
+ package_spec_priv_hash= old_package_spec_priv_hash;
+ package_body_priv_hash= old_package_body_priv_hash;
grant_memroot= old_mem; /* purecov: deadcode */
}
else
@@ -7374,6 +7414,8 @@ bool grant_reload(THD *thd)
my_hash_free(&old_column_priv_hash);
my_hash_free(&old_proc_priv_hash);
my_hash_free(&old_func_priv_hash);
+ my_hash_free(&old_package_spec_priv_hash);
+ my_hash_free(&old_package_body_priv_hash);
free_root(&old_mem,MYF(0));
}
@@ -8002,7 +8044,9 @@ bool check_grant_db(THD *thd, const char *db)
if (error)
error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
- check_grant_db_routine(thd, db, &func_priv_hash);
+ check_grant_db_routine(thd, db, &func_priv_hash) &&
+ check_grant_db_routine(thd, db, &package_spec_priv_hash) &&
+ check_grant_db_routine(thd, db, &package_body_priv_hash);
mysql_rwlock_unlock(&LOCK_grant);
@@ -8399,6 +8443,14 @@ static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
buff, sizeof(buff)))
return TRUE;
+ if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_spec,
+ buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_body,
+ buff, sizeof(buff)))
+ return TRUE;
+
return FALSE;
}
@@ -8624,6 +8676,14 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
buff, sizeof(buff)))
goto end;
+ if (show_routine_grants(thd, username, hostname, &sp_handler_package_spec,
+ buff, sizeof(buff)))
+ goto end;
+
+ if (show_routine_grants(thd, username, hostname, &sp_handler_package_body,
+ buff, sizeof(buff)))
+ goto end;
+
if (show_proxy_grants(thd, username, hostname, buff, sizeof(buff)))
goto end;
}
@@ -9559,6 +9619,14 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
grant_name_hash= &func_priv_hash;
elements= grant_name_hash->records;
break;
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ grant_name_hash= &package_spec_priv_hash;
+ elements= grant_name_hash->records;
+ break;
+ case PACKAGE_BODY_PRIVILEGES_HASH:
+ grant_name_hash= &package_body_priv_hash;
+ elements= grant_name_hash->records;
+ break;
case PROXY_USERS_ACL:
elements= acl_proxy_users.elements;
break;
@@ -9597,6 +9665,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case COLUMN_PRIVILEGES_HASH:
case PROC_PRIVILEGES_HASH:
case FUNC_PRIVILEGES_HASH:
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ case PACKAGE_BODY_PRIVILEGES_HASH:
grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
user= grant_name->user;
host= grant_name->host.hostname;
@@ -9679,6 +9749,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case COLUMN_PRIVILEGES_HASH:
case PROC_PRIVILEGES_HASH:
case FUNC_PRIVILEGES_HASH:
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ case PACKAGE_BODY_PRIVILEGES_HASH:
my_hash_delete(grant_name_hash, (uchar*) grant_name);
/*
In our HASH implementation on deletion one elements
@@ -9724,6 +9796,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case COLUMN_PRIVILEGES_HASH:
case PROC_PRIVILEGES_HASH:
case FUNC_PRIVILEGES_HASH:
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ case PACKAGE_BODY_PRIVILEGES_HASH:
{
/*
Save old hash key and its length to be able to properly update
@@ -9906,6 +9980,26 @@ static int handle_grant_data(THD *thd, Grant_tables& tables, bool drop,
if (search_only)
goto end;
}
+ /* Handle package spec array. */
+ if ((handle_grant_struct(PACKAGE_SPEC_PRIVILEGES_HASH,
+ drop, user_from, user_to) || found)
+ && ! result)
+ {
+ result= 1; /* At least one record/element found. */
+ /* If search is requested, we do not need to search further. */
+ if (search_only)
+ goto end;
+ }
+ /* Handle package body array. */
+ if ((handle_grant_struct(PACKAGE_BODY_PRIVILEGES_HASH,
+ drop, user_from, user_to) || found)
+ && ! result)
+ {
+ result= 1; /* At least one record/element found. */
+ /* If search is requested, we do not need to search further. */
+ if (search_only)
+ goto end;
+ }
}
/* Handle tables table. */
@@ -10632,7 +10726,9 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
/* Remove procedure access */
if (mysql_revoke_sp_privs(thd, &tables, &sp_handler_function, lex_user) ||
- mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user))
+ mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user) ||
+ mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_spec, lex_user) ||
+ mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_body, lex_user))
result= -1;
ACL_USER_BASE *user_or_role;
@@ -11177,6 +11273,8 @@ SHOW_VAR acl_statistics[] = {
{"database_grants", (char*)&acl_dbs.elements, SHOW_UINT},
{"function_grants", (char*)&func_priv_hash.records, SHOW_ULONG},
{"procedure_grants", (char*)&proc_priv_hash.records, SHOW_ULONG},
+ {"package_spec_grants", (char*)&package_spec_priv_hash.records, SHOW_ULONG},
+ {"package_body_grants", (char*)&package_body_priv_hash.records, SHOW_ULONG},
{"proxy_users", (char*)&acl_proxy_users.elements, SHOW_UINT},
{"role_grants", (char*)&acl_roles_mappings.records, SHOW_ULONG},
{"roles", (char*)&acl_roles.records, SHOW_ULONG},
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 902933ffed8..e7be6ed4e90 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -3169,6 +3169,17 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
switch (mdl_type)
{
+ case MDL_key::PACKAGE_BODY:
+ DBUG_ASSERT(rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first);
+ /*
+ No need to cache the package body itself.
+ It gets cached during open_and_process_routine()
+ for the first used package routine. See the package related code
+ in the "case" below.
+ */
+ if (sp_acquire_mdl(thd, rt, ot_ctx))
+ DBUG_RETURN(TRUE);
+ break;
case MDL_key::FUNCTION:
case MDL_key::PROCEDURE:
{
@@ -3183,11 +3194,13 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
if (rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first ||
mdl_type != MDL_key::PROCEDURE)
{
+ /*
+ TODO: If this is a package routine, we should not put MDL
+ TODO: on the routine itself. We should put only the package MDL.
+ */
if (sp_acquire_mdl(thd, rt, ot_ctx))
DBUG_RETURN(TRUE);
- DEBUG_SYNC(thd, "after_shared_lock_pname");
-
/* Ensures the routine is up-to-date and cached, if exists. */
if (rt->sp_cache_routine(thd, has_prelocking_list, &sp))
DBUG_RETURN(TRUE);
@@ -3202,8 +3215,24 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
*routine_modifies_data= sp->modifies_data();
if (!has_prelocking_list)
+ {
prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp,
need_prelocking);
+ if (sp->m_parent)
+ {
+ /*
+ If it's a package routine, we need also to handle the
+ package body, as its initialization section can use
+ some tables and routine calls.
+ TODO: Only package public routines actually need this.
+ TODO: Skip package body handling for private routines.
+ */
+ *routine_modifies_data|= sp->m_parent->modifies_data();
+ prelocking_strategy->handle_routine(thd, prelocking_ctx, rt,
+ sp->m_parent,
+ need_prelocking);
+ }
+ }
}
}
else
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index c696ee897f2..540777ea605 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -913,6 +913,8 @@ THD::THD(my_thread_id id, bool is_wsrep_applier, bool skip_global_sys_var_lock)
sp_proc_cache= NULL;
sp_func_cache= NULL;
+ sp_package_spec_cache= NULL;
+ sp_package_body_cache= NULL;
/* For user vars replication*/
if (opt_bin_log)
@@ -1478,6 +1480,8 @@ void THD::change_user(void)
(my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC);
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
+ sp_cache_clear(&sp_package_spec_cache);
+ sp_cache_clear(&sp_package_body_cache);
}
/**
@@ -1604,6 +1608,8 @@ void THD::cleanup(void)
my_hash_free(&sequences);
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
+ sp_cache_clear(&sp_package_spec_cache);
+ sp_cache_clear(&sp_package_body_cache);
auto_inc_intervals_forced.empty();
auto_inc_intervals_in_cur_stmt_for_binlog.empty();
@@ -3732,7 +3738,8 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
mvsp->type_handler() == &type_handler_row)
{
// SELECT INTO row_type_sp_variable
- if (thd->spcont->get_variable(mvsp->offset)->cols() != list.elements)
+ if (mvsp->get_rcontext(thd->spcont)->get_variable(mvsp->offset)->cols() !=
+ list.elements)
goto error;
m_var_sp_row= mvsp;
return 0;
@@ -4076,14 +4083,22 @@ bool my_var_user::set(THD *thd, Item *item)
return suv->fix_fields(thd, 0) || suv->update();
}
+
+sp_rcontext *my_var_sp::get_rcontext(sp_rcontext *local_ctx) const
+{
+ return m_rcontext_handler->get_rcontext(local_ctx);
+}
+
+
bool my_var_sp::set(THD *thd, Item *item)
{
- return thd->spcont->set_variable(thd, offset, &item);
+ return get_rcontext(thd->spcont)->set_variable(thd, offset, &item);
}
bool my_var_sp_row_field::set(THD *thd, Item *item)
{
- return thd->spcont->set_variable_row_field(thd, offset, m_field_offset, &item);
+ return get_rcontext(thd->spcont)->
+ set_variable_row_field(thd, offset, m_field_offset, &item);
}
@@ -4118,7 +4133,8 @@ int select_dumpvar::send_data(List<Item> &items)
DBUG_RETURN(1);
}
if (m_var_sp_row ?
- thd->spcont->set_variable_row(thd, m_var_sp_row->offset, items) :
+ m_var_sp_row->get_rcontext(thd->spcont)->
+ set_variable_row(thd, m_var_sp_row->offset, items) :
send_data_to_var_list(items))
DBUG_RETURN(1);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 7e06dcc7d39..a43f4dec0c9 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3166,6 +3166,8 @@ public:
sp_rcontext *spcont; // SP runtime context
sp_cache *sp_proc_cache;
sp_cache *sp_func_cache;
+ sp_cache *sp_package_spec_cache;
+ sp_cache *sp_package_body_cache;
/** number of name_const() substitutions, see sp_head.cc:subst_spvars() */
uint query_name_consts;
@@ -5902,6 +5904,7 @@ public:
};
class my_var_sp: public my_var {
+ const Sp_rcontext_handler *m_rcontext_handler;
const Type_handler *m_type_handler;
public:
uint offset;
@@ -5910,13 +5913,17 @@ public:
runtime context is used for variable handling.
*/
sp_head *sp;
- my_var_sp(const LEX_CSTRING *j, uint o, const Type_handler *type_handler,
+ my_var_sp(const Sp_rcontext_handler *rcontext_handler,
+ const LEX_CSTRING *j, uint o, const Type_handler *type_handler,
sp_head *s)
- : my_var(j, LOCAL_VAR), m_type_handler(type_handler), offset(o), sp(s) { }
+ : my_var(j, LOCAL_VAR),
+ m_rcontext_handler(rcontext_handler),
+ m_type_handler(type_handler), offset(o), sp(s) { }
~my_var_sp() { }
bool set(THD *thd, Item *val);
my_var_sp *get_my_var_sp() { return this; }
const Type_handler *type_handler() const { return m_type_handler; }
+ sp_rcontext *get_rcontext(sp_rcontext *local_ctx) const;
};
/*
@@ -5927,9 +5934,11 @@ class my_var_sp_row_field: public my_var_sp
{
uint m_field_offset;
public:
- my_var_sp_row_field(const LEX_CSTRING *varname, const LEX_CSTRING *fieldname,
+ my_var_sp_row_field(const Sp_rcontext_handler *rcontext_handler,
+ const LEX_CSTRING *varname, const LEX_CSTRING *fieldname,
uint var_idx, uint field_idx, sp_head *s)
- :my_var_sp(varname, var_idx, &type_handler_double/*Not really used*/, s),
+ :my_var_sp(rcontext_handler, varname, var_idx,
+ &type_handler_double/*Not really used*/, s),
m_field_offset(field_idx)
{ }
bool set(THD *thd, Item *val);
@@ -6304,6 +6313,9 @@ public:
Database_qualified_name(const LEX_CSTRING *db, const LEX_CSTRING *name)
:m_db(*db), m_name(*name)
{ }
+ Database_qualified_name(const LEX_CSTRING &db, const LEX_CSTRING &name)
+ :m_db(db), m_name(name)
+ { }
Database_qualified_name(const char *db, size_t db_length,
const char *name, size_t name_length)
{
@@ -6353,6 +6365,34 @@ public:
DBUG_SLOW_ASSERT(ok_for_lower_case_names(m_db.str));
return false;
}
+
+ bool make_package_routine_name(MEM_ROOT *mem_root,
+ const LEX_CSTRING &package,
+ const LEX_CSTRING &routine)
+ {
+ char *tmp;
+ size_t length= package.length + 1 + routine.length + 1;
+ if (!(tmp= (char *) alloc_root(mem_root, length)))
+ return true;
+ m_name.length= my_snprintf(tmp, length, "%.*s.%.*s",
+ (int) package.length, package.str,
+ (int) routine.length, routine.str);
+ m_name.str= tmp;
+ return false;
+ }
+
+ bool make_package_routine_name(MEM_ROOT *mem_root,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &package,
+ const LEX_CSTRING &routine)
+ {
+ if (make_package_routine_name(mem_root, package, routine))
+ return true;
+ if (!(m_db.str= strmake_root(mem_root, db.str, db.length)))
+ return true;
+ m_db.length= db.length;
+ return false;
+ }
};
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index d95b1c828b9..1c4c89eb132 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -99,6 +99,15 @@ enum enum_sql_command {
SQLCOM_CREATE_SEQUENCE,
SQLCOM_DROP_SEQUENCE,
SQLCOM_ALTER_SEQUENCE,
+ SQLCOM_CREATE_PACKAGE,
+ SQLCOM_DROP_PACKAGE,
+ SQLCOM_CREATE_PACKAGE_BODY,
+ SQLCOM_DROP_PACKAGE_BODY,
+ SQLCOM_SHOW_CREATE_PACKAGE,
+ SQLCOM_SHOW_CREATE_PACKAGE_BODY,
+ SQLCOM_SHOW_STATUS_PACKAGE,
+ SQLCOM_SHOW_STATUS_PACKAGE_BODY,
+ SQLCOM_SHOW_PACKAGE_BODY_CODE,
/*
When a command is added here, be sure it's also added in mysqld.cc
@@ -175,8 +184,10 @@ class Sql_cmd_call : public Sql_cmd
{
public:
class sp_name *m_name;
- Sql_cmd_call(class sp_name *name)
- :m_name(name)
+ const class Sp_handler *m_handler;
+ Sql_cmd_call(class sp_name *name, const class Sp_handler *handler)
+ :m_name(name),
+ m_handler(handler)
{}
virtual ~Sql_cmd_call()
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)
{
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 45fbe38e974..5710c990855 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -2343,6 +2343,19 @@ public:
return m_cpp_ptr;
}
+ /**
+ Get the current stream pointer, in the pre-processed buffer,
+ with traling spaces removed.
+ */
+ const char *get_cpp_ptr_rtrim()
+ {
+ const char *p;
+ for (p= m_cpp_ptr;
+ p > m_cpp_buf && my_isspace(system_charset_info, p[-1]);
+ p--)
+ { }
+ return p;
+ }
/** Get the utf8-body string. */
const char *get_body_utf8_str()
{
@@ -3212,15 +3225,10 @@ public:
sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name);
sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name1,
const LEX_CSTRING *name2);
+ sp_name *make_sp_name_package_routine(THD *thd, const LEX_CSTRING *name);
sp_head *make_sp_head(THD *thd, const sp_name *name, const Sp_handler *sph);
sp_head *make_sp_head_no_recursive(THD *thd, const sp_name *name,
- const Sp_handler *sph)
- {
- if (!sphead)
- return make_sp_head(thd, name, sph);
- my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str());
- return NULL;
- }
+ const Sp_handler *sph);
sp_head *make_sp_head_no_recursive(THD *thd,
DDL_options_st options, sp_name *name,
const Sp_handler *sph)
@@ -3231,10 +3239,29 @@ public:
}
bool sp_body_finalize_function(THD *);
bool sp_body_finalize_procedure(THD *);
+ sp_package *create_package_start(THD *thd,
+ enum_sql_command command,
+ const Sp_handler *sph,
+ const sp_name *name,
+ DDL_options_st options);
+ bool create_package_finalize(THD *thd,
+ const sp_name *name,
+ const sp_name *name2,
+ const char *body_start,
+ const char *body_end);
bool call_statement_start(THD *thd, sp_name *name);
bool call_statement_start(THD *thd, const LEX_CSTRING *name);
bool call_statement_start(THD *thd, const LEX_CSTRING *name1,
const LEX_CSTRING *name2);
+ sp_variable *find_variable(const LEX_CSTRING *name,
+ sp_pcontext **ctx,
+ const Sp_rcontext_handler **rh) const;
+ sp_variable *find_variable(const LEX_CSTRING *name,
+ const Sp_rcontext_handler **rh) const
+ {
+ sp_pcontext *not_used_ctx;
+ return find_variable(name, &not_used_ctx, rh);
+ }
bool init_internal_variable(struct sys_var_with_base *variable,
const LEX_CSTRING *name);
bool init_internal_variable(struct sys_var_with_base *variable,
@@ -3318,6 +3345,7 @@ public:
/*
Create an Item corresponding to a ROW field valiable: var.field
@param THD - THD, for mem_root
+ @param rh [OUT] - the rcontext handler (local vs package variables)
@param var - the ROW variable name
@param field - the ROW variable field name
@param spvar - the variable that was previously found by name
@@ -3326,6 +3354,7 @@ public:
@param end - end in the query (for binary log)
*/
Item_splocal *create_item_spvar_row_field(THD *thd,
+ const Sp_rcontext_handler *rh,
const LEX_CSTRING *var,
const LEX_CSTRING *field,
sp_variable *spvar,
@@ -3419,6 +3448,8 @@ public:
Item *make_item_func_replace(THD *thd, Item *org, Item *find, Item *replace);
Item *make_item_func_substr(THD *thd, Item *a, Item *b, Item *c);
Item *make_item_func_substr(THD *thd, Item *a, Item *b);
+ my_var *create_outvar(THD *thd, const LEX_CSTRING *name);
+
/*
Create a my_var instance for a ROW field variable that was used
as an OUT SP parameter: CALL p1(var.field);
@@ -3479,6 +3510,7 @@ public:
bool sp_block_with_exceptions_finalize_exceptions(THD *thd,
uint executable_section_ip,
uint exception_count);
+ bool sp_block_with_exceptions_add_empty(THD *thd);
bool sp_exit_statement(THD *thd, Item *when);
bool sp_exit_statement(THD *thd, const LEX_CSTRING *label_name, Item *item);
bool sp_leave_statement(THD *thd, const LEX_CSTRING *label_name);
@@ -3728,6 +3760,7 @@ public:
{
return create_info.vers_info;
}
+ sp_package *get_sp_package() const;
};
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 67181c6eb5e..f5fe955375d 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -558,6 +558,10 @@ void init_update_queries(void)
CF_INSERTS_DATA;
sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE;
sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE;
+ sql_command_flags[SQLCOM_CREATE_PACKAGE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_PACKAGE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_PACKAGE_BODY]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_PACKAGE_BODY]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
@@ -631,6 +635,8 @@ void init_update_queries(void)
CF_OPTIMIZER_TRACE; // (1)
sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+ sql_command_flags[SQLCOM_SHOW_STATUS_PACKAGE]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+ sql_command_flags[SQLCOM_SHOW_STATUS_PACKAGE_BODY]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_STATUS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_DATABASES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_TRIGGERS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
@@ -665,10 +671,13 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_CREATE_PACKAGE]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_CREATE_PACKAGE_BODY]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_TRIGGER]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_PROC_CODE]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_FUNC_CODE]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_PACKAGE_BODY_CODE]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND;
@@ -829,6 +838,10 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_DROP_INDEX]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_CREATE_DB]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_DROP_DB]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_PACKAGE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_PACKAGE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_PACKAGE_BODY]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_PACKAGE_BODY]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_ALTER_DB]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_CREATE_VIEW]|= CF_DISALLOW_IN_RO_TRANS;
@@ -3124,7 +3137,7 @@ bool Sql_cmd_call::execute(THD *thd)
By this moment all needed SPs should be in cache so no need to look
into DB.
*/
- if (!(sp= sp_handler_procedure.sp_find_routine(thd, m_name, true)))
+ if (!(sp= m_handler->sp_find_routine(thd, m_name, true)))
{
/*
If the routine is not found, let's still check EXECUTE_ACL to decide
@@ -3705,6 +3718,8 @@ mysql_execute_command(THD *thd)
/* fall through */
case SQLCOM_SHOW_STATUS_PROC:
case SQLCOM_SHOW_STATUS_FUNC:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TRIGGERS:
@@ -5859,6 +5874,8 @@ end_with_restore_list:
break;
case SQLCOM_CREATE_PROCEDURE:
case SQLCOM_CREATE_SPFUNCTION:
+ case SQLCOM_CREATE_PACKAGE:
+ case SQLCOM_CREATE_PACKAGE_BODY:
{
if (mysql_create_routine(thd, lex))
goto error;
@@ -5908,6 +5925,8 @@ end_with_restore_list:
}
case SQLCOM_DROP_PROCEDURE:
case SQLCOM_DROP_FUNCTION:
+ case SQLCOM_DROP_PACKAGE:
+ case SQLCOM_DROP_PACKAGE_BODY:
{
#ifdef HAVE_DLOPEN
if (lex->sql_command == SQLCOM_DROP_FUNCTION &&
@@ -6022,6 +6041,8 @@ end_with_restore_list:
}
case SQLCOM_SHOW_CREATE_PROC:
case SQLCOM_SHOW_CREATE_FUNC:
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
{
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
@@ -6031,11 +6052,19 @@ end_with_restore_list:
}
case SQLCOM_SHOW_PROC_CODE:
case SQLCOM_SHOW_FUNC_CODE:
+ case SQLCOM_SHOW_PACKAGE_BODY_CODE:
{
#ifndef DBUG_OFF
+ Database_qualified_name pkgname(&null_clex_str, &null_clex_str);
sp_head *sp;
const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
+ if (sph->sp_resolve_package_routine(thd, thd->lex->sphead,
+ lex->spname, &sph, &pkgname))
+ return true;
if (sph->sp_cache_routine(thd, lex->spname, false, &sp))
goto error;
if (!sp || sp->show_routine_code(thd))
@@ -7997,6 +8026,8 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
THD_STAGE_INFO(thd, stage_freeing_items);
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_spec_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_body_cache, stored_program_cache_size);
thd->end_statement();
thd->cleanup_after_query();
DBUG_ASSERT(thd->Item_change_list::is_empty());
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index dbc03234c44..22b8640d9ee 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2313,6 +2313,8 @@ static bool check_prepared_statement(Prepared_statement *stmt)
case SQLCOM_SHOW_TABLE_STATUS:
case SQLCOM_SHOW_STATUS_PROC:
case SQLCOM_SHOW_STATUS_FUNC:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
case SQLCOM_SELECT:
res= mysql_test_select(stmt, tables);
if (res == 2)
@@ -2385,6 +2387,21 @@ static bool check_prepared_statement(Prepared_statement *stmt)
DBUG_RETURN(FALSE);
}
break;
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ if ((res= mysql_test_show_create_routine(stmt, &sp_handler_package_spec)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
+ if ((res= mysql_test_show_create_routine(stmt,
+ &sp_handler_package_body)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
case SQLCOM_CREATE_VIEW:
if (lex->create_view->mode == VIEW_ALTER)
{
@@ -2589,6 +2606,8 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_spec_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_body_cache, stored_program_cache_size);
/* check_prepared_statemnt sends the metadata packet in case of success */
end:
@@ -3155,6 +3174,8 @@ static void mysql_stmt_execute_common(THD *thd,
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_spec_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_body_cache, stored_program_cache_size);
/* Close connection socket; for use with client testing (Bug#43560). */
DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 2b1b765a5be..fd38d4a7771 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2546,6 +2546,17 @@ static const LEX_CSTRING *view_algorithm(TABLE_LIST *table)
}
}
+
+static bool append_at_host(THD *thd, String *buffer, const LEX_CSTRING *host)
+{
+ if (!host->str || !host->str[0])
+ return false;
+ return
+ buffer->append('@') ||
+ append_identifier(thd, buffer, host);
+}
+
+
/*
Append DEFINER clause to the given buffer.
@@ -2557,17 +2568,14 @@ static const LEX_CSTRING *view_algorithm(TABLE_LIST *table)
definer_host [in] host name part of definer
*/
-void append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user,
+bool append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user,
const LEX_CSTRING *definer_host)
{
- buffer->append(STRING_WITH_LEN("DEFINER="));
- append_identifier(thd, buffer, definer_user);
- if (definer_host->str && definer_host->str[0])
- {
- buffer->append('@');
- append_identifier(thd, buffer, definer_host);
- }
- buffer->append(' ');
+ return
+ buffer->append(STRING_WITH_LEN("DEFINER=")) ||
+ append_identifier(thd, buffer, definer_user) ||
+ append_at_host(thd, buffer, definer_host) ||
+ buffer->append(' ');
}
@@ -9028,7 +9036,7 @@ ST_FIELD_INFO proc_fields_info[]=
SKIP_OPEN_TABLE},
{"ROUTINE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name",
SKIP_OPEN_TABLE},
- {"ROUTINE_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE},
+ {"ROUTINE_TYPE", 13, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE},
{"DATA_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"CHARACTER_MAXIMUM_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE},
{"CHARACTER_OCTET_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE},
diff --git a/sql/sql_show.h b/sql/sql_show.h
index f6d5d4d2c3c..1b959121f89 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -106,7 +106,7 @@ bool mysqld_show_contributors(THD *thd);
bool mysqld_show_privileges(THD *thd);
char *make_backup_log_name(char *buff, const char *name, const char* log_ext);
uint calc_sum_of_all_status(STATUS_VAR *to);
-void append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user,
+bool append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user,
const LEX_CSTRING *definer_host);
int add_status_vars(SHOW_VAR *list);
void remove_status_vars(SHOW_VAR *list);
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 5cabcc02aae..37531429f8d 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -487,6 +487,10 @@ public:
ls->length == strlen(ls->str)));
return append(ls->str, (uint32) ls->length);
}
+ bool append(const LEX_CSTRING &ls)
+ {
+ return append(&ls);
+ }
bool append(const char *s, size_t size);
bool append(const char *s, size_t arg_length, CHARSET_INFO *cs);
bool append_ulonglong(ulonglong val);
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 29bf7aeb205..4c1e2a51fbf 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -2253,7 +2253,8 @@ add_tables_and_routines_for_triggers(THD *thd,
MDL_key key(MDL_key::TRIGGER, trigger->m_db.str, trigger->m_name.str);
if (sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
- &key, table_list->belong_to_view))
+ &key, &sp_handler_trigger,
+ table_list->belong_to_view))
{
trigger->add_used_tables_to_table_list(thd,
&prelocking_ctx->query_tables_last,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 2ebcb057c32..35ec2d29d21 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -378,12 +378,13 @@ Item_splocal*
LEX::create_item_for_sp_var(LEX_CSTRING *name, sp_variable *spvar,
const char *start_in_q, const char *end_in_q)
{
+ const Sp_rcontext_handler *rh;
Item_splocal *item;
uint pos_in_q, len_in_q;
/* If necessary, look for the variable. */
if (spcont && !spvar)
- spvar= spcont->find_variable(name, false);
+ spvar= find_variable(name, &rh);
if (!spvar)
{
@@ -398,7 +399,7 @@ LEX::create_item_for_sp_var(LEX_CSTRING *name, sp_variable *spvar,
len_in_q= (uint)(end_in_q - start_in_q);
item= new (thd->mem_root)
- Item_splocal(thd, name, spvar->offset, spvar->type_handler(),
+ Item_splocal(thd, rh, name, spvar->offset, spvar->type_handler(),
pos_in_q, len_in_q);
#ifdef DBUG_ASSERT_EXISTS
@@ -951,6 +952,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token BIT_XOR /* MYSQL-FUNC */
%token BLOB_SYM /* SQL-2003-R */
%token BLOCK_SYM
+%token BODY_SYM /* Oracle-R */
%token BOOLEAN_SYM /* SQL-2003-R */
%token BOOL_SYM
%token BOTH /* SQL-2003-R */
@@ -1340,6 +1342,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token OUT_SYM /* SQL-2003-R */
%token OVER_SYM
%token OWNER_SYM
+%token PACKAGE_SYM /* Oracle-R */
%token PACK_KEYS_SYM
%token PAGE_SYM
%token PAGE_CHECKSUM_SYM
@@ -12885,14 +12888,8 @@ select_outvar:
}
| ident_or_text
{
- sp_variable *t;
-
- if (!Lex->spcont || !(t= Lex->spcont->find_variable(&$1, false)))
- my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str));
- $$ = Lex->result ? (new (thd->mem_root)
- my_var_sp(&$1, t->offset, t->type_handler(),
- Lex->sphead)) :
- NULL;
+ if (!($$= Lex->create_outvar(thd, &$1)) && Lex->result)
+ MYSQL_YYABORT;
}
| ident '.' ident
{
@@ -13947,6 +13944,18 @@ show_param:
lex->sql_command = SQLCOM_SHOW_CREATE_FUNC;
lex->spname= $3;
}
+ | CREATE PACKAGE_SYM sp_name
+ {
+ LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE;
+ lex->spname= $3;
+ }
+ | CREATE PACKAGE_SYM BODY_SYM sp_name
+ {
+ LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE_BODY;
+ lex->spname= $4;
+ }
| CREATE TRIGGER_SYM sp_name
{
LEX *lex= Lex;
@@ -13979,6 +13988,20 @@ show_param:
if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
MYSQL_YYABORT;
}
+ | PACKAGE_SYM STATUS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_STATUS_PACKAGE;
+ if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
+ MYSQL_YYABORT;
+ }
+ | PACKAGE_SYM BODY_SYM STATUS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_STATUS_PACKAGE_BODY;
+ if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
+ MYSQL_YYABORT;
+ }
| PROCEDURE_SYM CODE_SYM sp_name
{
Lex->sql_command= SQLCOM_SHOW_PROC_CODE;
@@ -13989,6 +14012,11 @@ show_param:
Lex->sql_command= SQLCOM_SHOW_FUNC_CODE;
Lex->spname= $3;
}
+ | PACKAGE_SYM BODY_SYM CODE_SYM sp_name
+ {
+ Lex->sql_command= SQLCOM_SHOW_PACKAGE_BODY_CODE;
+ Lex->spname= $4;
+ }
| CREATE EVENT_SYM sp_name
{
Lex->spname= $3;
@@ -15485,6 +15513,7 @@ keyword_sp_not_data_type:
| AVG_ROW_LENGTH {}
| AVG_SYM {}
| BLOCK_SYM {}
+ | BODY_SYM {}
| BTREE_SYM {}
| CASCADED {}
| CATALOG_NAME_SYM {}
@@ -15660,6 +15689,7 @@ keyword_sp_not_data_type:
| ONE_SYM {}
| ONLINE_SYM {}
| ONLY_SYM {}
+ | PACKAGE_SYM {}
| PACK_KEYS_SYM {}
| PAGE_SYM {}
| PARTIAL {}
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index 1a2b1630d38..c8f93c17dd4 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -237,6 +237,7 @@ void ORAerror(THD *thd, const char *s)
st_trg_execution_order trg_execution_order;
/* enums */
+ enum enum_sp_suid_behaviour sp_suid;
enum enum_view_suid view_suid;
enum sub_select_type unit_type;
enum Condition_information_item::Name cond_info_item_name;
@@ -342,6 +343,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token BIT_XOR /* MYSQL-FUNC */
%token BLOB_SYM /* SQL-2003-R */
%token BLOCK_SYM
+%token BODY_SYM /* Oracle-R */
%token BOOLEAN_SYM /* SQL-2003-R */
%token BOOL_SYM
%token BOTH /* SQL-2003-R */
@@ -731,6 +733,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token OUT_SYM /* SQL-2003-R */
%token OVER_SYM
%token OWNER_SYM
+%token PACKAGE_SYM /* Oracle-R */
%token PACK_KEYS_SYM
%token PAGE_SYM
%token PAGE_CHECKSUM_SYM
@@ -1044,6 +1047,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
sp_opt_label BIN_NUM TEXT_STRING_filesystem ident_or_empty
opt_constraint constraint opt_ident
ident_directly_assignable
+ opt_package_routine_end_name
sp_decl_ident
sp_block_label opt_place opt_db
@@ -1070,7 +1074,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
optionally_qualified_column_ident
%type <simple_string>
- remember_name remember_end remember_tok_start
+ remember_name remember_end remember_end_opt remember_tok_start
wild_and_where
colon_with_pos
@@ -1334,7 +1338,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
opt_extended_describe shutdown
opt_format_json
prepare prepare_src execute deallocate
- statement sp_suid
+ statement
sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
view_list_opt view_list view_select
@@ -1376,17 +1380,28 @@ END_OF_INPUT
%type <num> view_algorithm view_check_option
%type <view_suid> view_suid opt_view_suid
+%type <sp_suid> sp_suid
%type <num> sp_decl_idents sp_decl_idents_init_vars
%type <num> sp_handler_type sp_hcond_list
%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
%type <spblock> sp_decl_body_list opt_sp_decl_body_list
+%type <spblock> sp_decl_vars
%type <spblock> sp_decl_non_handler sp_decl_non_handler_list
%type <spblock> sp_decl_handler sp_decl_handler_list opt_sp_decl_handler_list
+%type <spblock> package_implementation_routine_definition
+%type <spblock> package_implementation_item_declaration
+%type <spblock> package_implementation_declare_section
+%type <spblock> package_implementation_declare_section_list1
+%type <spblock> package_implementation_declare_section_list2
%type <spblock_handlers> sp_block_statements_and_exceptions
+%type <spblock_handlers> package_implementation_executable_section
%type <sp_instr_addr> sp_instr_addr
%type <sp_cursor_name_and_offset> sp_cursor_name_and_offset
%type <num> opt_exception_clause exception_handlers
+%type <lex> remember_lex package_routine_lex
+ package_specification_function
+ package_specification_procedure
%type <spname> sp_name opt_sp_name
%type <spvar> sp_param_name sp_param_name_and_type
%type <for_loop> sp_for_loop_index_and_bounds
@@ -2115,6 +2130,248 @@ create:
| create_or_replace { Lex->set_command(SQLCOM_CREATE_SERVER, $1); }
server_def
{ }
+ | create_or_replace definer_opt PACKAGE_SYM
+ opt_if_not_exists sp_name opt_create_package_chistics_init
+ sp_tail_is
+ remember_name
+ {
+ sp_package *pkg;
+ if (!(pkg= Lex->create_package_start(thd,
+ SQLCOM_CREATE_PACKAGE,
+ &sp_handler_package_spec,
+ $5, $1 | $4)))
+ MYSQL_YYABORT;
+ pkg->set_chistics(Lex->sp_chistics);
+ }
+ opt_package_specification_element_list END
+ remember_end_opt opt_sp_name
+ {
+ if (Lex->create_package_finalize(thd, $5, $13, $8, $12))
+ MYSQL_YYABORT;
+ }
+ | create_or_replace definer_opt PACKAGE_SYM BODY_SYM
+ opt_if_not_exists sp_name opt_create_package_chistics_init
+ sp_tail_is
+ remember_name
+ {
+ sp_package *pkg;
+ if (!(pkg= Lex->create_package_start(thd,
+ SQLCOM_CREATE_PACKAGE_BODY,
+ &sp_handler_package_body,
+ $6, $1 | $5)))
+ MYSQL_YYABORT;
+ pkg->set_chistics(Lex->sp_chistics);
+ Lex->sp_block_init(thd);
+ }
+ package_implementation_declare_section
+ {
+ if (Lex->sp_block_with_exceptions_finalize_declarations(thd))
+ MYSQL_YYABORT;
+ }
+ package_implementation_executable_section
+ {
+ $11.hndlrs+= $13.hndlrs;
+ if (Lex->sp_block_finalize(thd, $11))
+ MYSQL_YYABORT;
+ }
+ remember_end_opt opt_sp_name
+ {
+ if (Lex->create_package_finalize(thd, $6, $16, $9, $15))
+ MYSQL_YYABORT;
+ }
+ ;
+
+package_implementation_executable_section:
+ END
+ {
+ if (Lex->sp_block_with_exceptions_add_empty(thd))
+ MYSQL_YYABORT;
+ $$.init(0);
+ }
+ | BEGIN_SYM sp_block_statements_and_exceptions END { $$= $2; }
+ ;
+
+/*
+ Inside CREATE PACKATE BODY, package-wide items (e.g. variables)
+ must be declared before routine definitions.
+*/
+package_implementation_declare_section:
+ package_implementation_declare_section_list1
+ | package_implementation_declare_section_list2
+ | package_implementation_declare_section_list1
+ package_implementation_declare_section_list2
+ { $$.join($1, $2); }
+ ;
+
+package_implementation_declare_section_list1:
+ package_implementation_item_declaration
+ | package_implementation_declare_section_list1
+ package_implementation_item_declaration
+ { $$.join($1, $2); }
+ ;
+
+package_implementation_declare_section_list2:
+ package_implementation_routine_definition
+ | package_implementation_declare_section_list2
+ package_implementation_routine_definition
+ { $$.join($1, $2); }
+ ;
+
+package_routine_lex:
+ {
+ if (!($$= new (thd->mem_root) sp_lex_local(thd, thd->lex)))
+ MYSQL_YYABORT;
+ thd->m_parser_state->m_yacc.reset_before_substatement();
+ }
+ ;
+
+
+package_specification_function:
+ remember_lex package_routine_lex ident
+ {
+ DBUG_ASSERT($1->sphead->get_package());
+ $2->sql_command= SQLCOM_CREATE_FUNCTION;
+ sp_name *spname= $1->make_sp_name_package_routine(thd, &$3);
+ if (!spname)
+ MYSQL_YYABORT;
+ thd->lex= $2;
+ if (!$2->make_sp_head_no_recursive(thd, spname,
+ &sp_handler_package_function))
+ MYSQL_YYABORT;
+ $1->sphead->get_package()->m_current_routine= $2;
+ (void) is_native_function_with_warn(thd, &$3);
+ }
+ opt_sp_parenthesized_fdparam_list
+ sf_return_type
+ sp_c_chistics
+ {
+ sp_head *sp= thd->lex->sphead;
+ sp->restore_thd_mem_root(thd);
+ thd->lex= $1;
+ $$= $2;
+ }
+ ;
+
+package_specification_procedure:
+ remember_lex package_routine_lex ident
+ {
+ DBUG_ASSERT($1->sphead->get_package());
+ $2->sql_command= SQLCOM_CREATE_PROCEDURE;
+ sp_name *spname= $1->make_sp_name_package_routine(thd, &$3);
+ if (!spname)
+ MYSQL_YYABORT;
+ thd->lex= $2;
+ if (!$2->make_sp_head_no_recursive(thd, spname,
+ &sp_handler_package_procedure))
+ MYSQL_YYABORT;
+ $1->sphead->get_package()->m_current_routine= $2;
+ }
+ opt_sp_parenthesized_pdparam_list
+ sp_c_chistics
+ {
+ sp_head *sp= thd->lex->sphead;
+ sp->restore_thd_mem_root(thd);
+ thd->lex= $1;
+ $$= $2;
+
+ }
+ ;
+
+
+package_implementation_routine_definition:
+ FUNCTION_SYM package_specification_function
+ package_implementation_function_body ';'
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ if (pkg->add_routine_implementation($2))
+ MYSQL_YYABORT;
+ pkg->m_current_routine= NULL;
+ $$.init();
+ }
+ | PROCEDURE_SYM package_specification_procedure
+ package_implementation_procedure_body ';'
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ if (pkg->add_routine_implementation($2))
+ MYSQL_YYABORT;
+ pkg->m_current_routine= NULL;
+ $$.init();
+ }
+ | package_specification_element { $$.init(); }
+ ;
+
+
+package_implementation_function_body:
+ sp_tail_is remember_lex
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ sp_head *sp= pkg->m_current_routine->sphead;
+ thd->lex= pkg->m_current_routine;
+ sp->reset_thd_mem_root(thd);
+ sp->set_body_start(thd, YYLIP->get_cpp_tok_start());
+ }
+ sp_body opt_package_routine_end_name
+ {
+ if (Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR)
+ {
+ my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0)));
+ }
+ Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE);
+ if (thd->lex->sp_body_finalize_function(thd) ||
+ thd->lex->sphead->check_package_routine_end_name($5))
+ MYSQL_YYABORT;
+ thd->lex= $2;
+ }
+ ;
+
+package_implementation_procedure_body:
+ sp_tail_is remember_lex
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ sp_head *sp= pkg->m_current_routine->sphead;
+ thd->lex= pkg->m_current_routine;
+ sp->reset_thd_mem_root(thd);
+ sp->set_body_start(thd, YYLIP->get_cpp_tok_start());
+ }
+ sp_body opt_package_routine_end_name
+ {
+ if (thd->lex->sp_body_finalize_procedure(thd) ||
+ thd->lex->sphead->check_package_routine_end_name($5))
+ MYSQL_YYABORT;
+ thd->lex= $2;
+ }
+ ;
+
+
+package_implementation_item_declaration:
+ sp_decl_vars ';'
+ ;
+
+opt_package_specification_element_list:
+ /* Empty */
+ | package_specification_element_list
+ ;
+
+package_specification_element_list:
+ package_specification_element
+ | package_specification_element_list package_specification_element
+ ;
+
+package_specification_element:
+ FUNCTION_SYM package_specification_function ';'
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ if (pkg->add_routine_declaration($2))
+ MYSQL_YYABORT;
+ pkg->m_current_routine= NULL;
+ }
+ | PROCEDURE_SYM package_specification_procedure ';'
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ if (pkg->add_routine_declaration($2))
+ MYSQL_YYABORT;
+ pkg->m_current_routine= NULL;
+ }
;
create_function_tail:
@@ -2515,7 +2772,29 @@ sp_chistic:
| MODIFIES_SYM SQL_SYM DATA_SYM
{ Lex->sp_chistics.daccess= SP_MODIFIES_SQL_DATA; }
| sp_suid
- {}
+ { Lex->sp_chistics.suid= $1; }
+ ;
+
+create_package_chistic:
+ COMMENT_SYM TEXT_STRING_sys
+ { Lex->sp_chistics.comment= $2; }
+ | sp_suid
+ { Lex->sp_chistics.suid= $1; }
+ ;
+
+create_package_chistics:
+ create_package_chistic {}
+ | create_package_chistics create_package_chistic { }
+ ;
+
+opt_create_package_chistics:
+ /* Empty */
+ | create_package_chistics { }
+ ;
+
+opt_create_package_chistics_init:
+ { Lex->sp_chistics.init(); }
+ opt_create_package_chistics
;
/* Create characteristics */
@@ -2525,14 +2804,8 @@ sp_c_chistic:
;
sp_suid:
- SQL_SYM SECURITY_SYM DEFINER_SYM
- {
- Lex->sp_chistics.suid= SP_IS_SUID;
- }
- | SQL_SYM SECURITY_SYM INVOKER_SYM
- {
- Lex->sp_chistics.suid= SP_IS_NOT_SUID;
- }
+ SQL_SYM SECURITY_SYM DEFINER_SYM { $$= SP_IS_SUID; }
+ | SQL_SYM SECURITY_SYM INVOKER_SYM { $$= SP_IS_NOT_SUID; }
;
call:
@@ -2844,7 +3117,7 @@ sp_decl_idents_init_vars:
}
;
-sp_decl_non_handler:
+sp_decl_vars:
sp_decl_idents_init_vars
type_with_opt_collate
sp_opt_default
@@ -2878,6 +3151,10 @@ sp_decl_non_handler:
MYSQL_YYABORT;
$$.init_using_vars($1);
}
+ ;
+
+sp_decl_non_handler:
+ sp_decl_vars
| ident_directly_assignable CONDITION_SYM FOR_SYM sp_cond
{
if (Lex->spcont->declare_condition(thd, &$1, $4))
@@ -3604,6 +3881,12 @@ sp_proc_stmt_goto:
;
+remember_lex:
+ {
+ $$= thd->lex;
+ }
+ ;
+
assignment_source_lex:
{
DBUG_ASSERT(Lex->sphead);
@@ -8840,6 +9123,15 @@ remember_end:
}
;
+remember_end_opt:
+ {
+ if (yychar == YYEMPTY)
+ $$= (char*) YYLIP->get_cpp_ptr_rtrim();
+ else
+ $$= (char*) YYLIP->get_cpp_tok_end_rtrim();
+ }
+ ;
+
select_alias:
/* empty */ { $$=null_clex_str;}
| AS ident { $$=$2; }
@@ -12347,14 +12639,8 @@ select_outvar:
}
| ident_or_text
{
- sp_variable *t;
-
- if (!Lex->spcont || !(t= Lex->spcont->find_variable(&$1, false)))
- my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str));
- $$ = Lex->result ? (new (thd->mem_root)
- my_var_sp(&$1, t->offset, t->type_handler(),
- Lex->sphead)) :
- NULL;
+ if (!($$= Lex->create_outvar(thd, &$1)) && Lex->result)
+ MYSQL_YYABORT;
}
| ident '.' ident
{
@@ -12454,6 +12740,22 @@ drop:
lex->set_command(SQLCOM_DROP_DB, $3);
lex->name= $4;
}
+ | DROP PACKAGE_SYM opt_if_exists sp_name
+ {
+ LEX *lex= Lex;
+ lex->set_command(SQLCOM_DROP_PACKAGE, $3);
+ if (lex->sphead)
+ my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PACKAGE"));
+ lex->spname= $4;
+ }
+ | DROP PACKAGE_SYM BODY_SYM opt_if_exists sp_name
+ {
+ LEX *lex= Lex;
+ lex->set_command(SQLCOM_DROP_PACKAGE_BODY, $4);
+ if (lex->sphead)
+ my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PACKAGE BODY"));
+ lex->spname= $5;
+ }
| DROP FUNCTION_SYM opt_if_exists ident '.' ident
{
LEX *lex= thd->lex;
@@ -13384,6 +13686,18 @@ show_param:
lex->sql_command = SQLCOM_SHOW_CREATE_FUNC;
lex->spname= $3;
}
+ | CREATE PACKAGE_SYM sp_name
+ {
+ LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE;
+ lex->spname= $3;
+ }
+ | CREATE PACKAGE_SYM BODY_SYM sp_name
+ {
+ LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE_BODY;
+ lex->spname= $4;
+ }
| CREATE TRIGGER_SYM sp_name
{
LEX *lex= Lex;
@@ -13416,6 +13730,20 @@ show_param:
if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
MYSQL_YYABORT;
}
+ | PACKAGE_SYM STATUS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_STATUS_PACKAGE;
+ if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
+ MYSQL_YYABORT;
+ }
+ | PACKAGE_SYM BODY_SYM STATUS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_STATUS_PACKAGE_BODY;
+ if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
+ MYSQL_YYABORT;
+ }
| PROCEDURE_SYM CODE_SYM sp_name
{
Lex->sql_command= SQLCOM_SHOW_PROC_CODE;
@@ -13426,6 +13754,11 @@ show_param:
Lex->sql_command= SQLCOM_SHOW_FUNC_CODE;
Lex->spname= $3;
}
+ | PACKAGE_SYM BODY_SYM CODE_SYM sp_name
+ {
+ Lex->sql_command= SQLCOM_SHOW_PACKAGE_BODY_CODE;
+ Lex->spname= $4;
+ }
| CREATE EVENT_SYM sp_name
{
Lex->spname= $3;
@@ -14915,6 +15248,7 @@ keyword_directly_not_assignable:
keyword_sp:
keyword_sp_data_type
| keyword_sp_not_data_type
+ | FUNCTION_SYM { /* Oracle-PLSQL-R */}
;
@@ -15269,7 +15603,6 @@ keyword_sp_not_data_type:
| TYPES_SYM {}
| TYPE_SYM {}
| UDF_RETURNS_SYM {}
- | FUNCTION_SYM {}
| UNCOMMITTED_SYM {}
| UNDEFINED_SYM {}
| UNDO_BUFFER_SIZE_SYM {}
@@ -15966,6 +16299,18 @@ revoke_command:
if (Lex->add_grant_command(thd, SQLCOM_REVOKE, TYPE_ENUM_PROCEDURE))
MYSQL_YYABORT;
}
+ | grant_privileges ON PACKAGE_SYM grant_ident FROM user_and_role_list
+ {
+ if (Lex->add_grant_command(thd, SQLCOM_REVOKE,
+ TYPE_ENUM_PACKAGE))
+ MYSQL_YYABORT;
+ }
+ | grant_privileges ON PACKAGE_SYM BODY_SYM grant_ident FROM user_and_role_list
+ {
+ if (Lex->add_grant_command(thd, SQLCOM_REVOKE,
+ TYPE_ENUM_PACKAGE_BODY))
+ MYSQL_YYABORT;
+ }
| ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list
{
Lex->sql_command = SQLCOM_REVOKE_ALL;
@@ -16017,6 +16362,20 @@ grant_command:
if (Lex->add_grant_command(thd, SQLCOM_GRANT, TYPE_ENUM_PROCEDURE))
MYSQL_YYABORT;
}
+ | grant_privileges ON PACKAGE_SYM grant_ident TO_SYM grant_list
+ opt_require_clause opt_grant_options
+ {
+ if (Lex->add_grant_command(thd, SQLCOM_GRANT,
+ TYPE_ENUM_PACKAGE))
+ MYSQL_YYABORT;
+ }
+ | grant_privileges ON PACKAGE_SYM BODY_SYM grant_ident TO_SYM grant_list
+ opt_require_clause opt_grant_options
+ {
+ if (Lex->add_grant_command(thd, SQLCOM_GRANT,
+ TYPE_ENUM_PACKAGE_BODY))
+ MYSQL_YYABORT;
+ }
| PROXY_SYM ON user TO_SYM grant_list opt_grant_option
{
LEX *lex= Lex;
@@ -17010,6 +17369,11 @@ sf_tail:
{
if (Lex->sp_body_finalize_function(thd))
MYSQL_YYABORT;
+ if (Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR)
+ {
+ my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0)));
+ }
+ Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE);
}
;
@@ -17055,6 +17419,11 @@ sp_tail_standalone:
}
;
+opt_package_routine_end_name:
+ /* Empty */ { $$= null_clex_str; }
+ | ident { $$= $1; }
+ ;
+
sp_tail_is:
IS
| AS