summaryrefslogtreecommitdiff
path: root/sql/sp.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sp.cc')
-rw-r--r--sql/sp.cc1717
1 files changed, 1195 insertions, 522 deletions
diff --git a/sql/sp.cc b/sql/sp.cc
index 207ece47356..f9cfc9ce6c8 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -15,11 +15,12 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sp.h"
#include "sql_base.h" // close_thread_tables
+#include "sql_lex.h" // empty_clex_str
#include "sql_parse.h" // parse_sql
#include "key.h" // key_copy
#include "sql_show.h" // append_definer, append_identifier
@@ -34,96 +35,163 @@
#include <my_user.h>
-/* Used in error handling only */
-#define SP_TYPE_STRING(type) \
- (type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
-
-static int
-db_load_routine(THD *thd, stored_procedure_type type, sp_name *name,
- sp_head **sphp,
- sql_mode_t sql_mode, const char *params, const char *returns,
- const char *body, st_sp_chistics &chistics,
- LEX_STRING *definer_user_name, LEX_STRING *definer_host_name,
- longlong created, longlong modified,
- Stored_program_creation_ctx *creation_ctx);
+sp_cache **Sp_handler_procedure::get_cache(THD *thd) const
+{
+ return &thd->sp_proc_cache;
+}
+
+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;
+}
+
+
+bool Sp_handler::add_instr_freturn(THD *thd, sp_head *sp,
+ sp_pcontext *spcont,
+ Item *item, LEX *lex) const
+{
+ my_error(ER_SP_BADRETURN, MYF(0));
+ return true;
+}
+
+
+bool Sp_handler::add_instr_preturn(THD *thd, sp_head *sp,
+ sp_pcontext *spcont) const
+{
+ thd->parse_error();
+ return true;
+}
+
+
+bool Sp_handler_function::add_instr_freturn(THD *thd, sp_head *sp,
+ sp_pcontext *spcont,
+ Item *item, LEX *lex) const
+{
+ return sp->add_instr_freturn(thd, spcont, item, lex);
+}
+
+
+bool Sp_handler_procedure::add_instr_preturn(THD *thd, sp_head *sp,
+ sp_pcontext *spcont) const
+{
+ return sp->add_instr_preturn(thd, spcont);
+}
+
+
+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
TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] =
{
{
- { C_STRING_WITH_LEN("db") },
- { C_STRING_WITH_LEN("char(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("db") },
+ { STRING_WITH_LEN("char(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("name") },
- { C_STRING_WITH_LEN("char(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("name") },
+ { STRING_WITH_LEN("char(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("type") },
- { C_STRING_WITH_LEN("enum('FUNCTION','PROCEDURE')") },
+ { STRING_WITH_LEN("type") },
+ { STRING_WITH_LEN("enum('FUNCTION','PROCEDURE')") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("specific_name") },
- { C_STRING_WITH_LEN("char(64)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("specific_name") },
+ { STRING_WITH_LEN("char(64)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("language") },
- { C_STRING_WITH_LEN("enum('SQL')") },
+ { STRING_WITH_LEN("language") },
+ { STRING_WITH_LEN("enum('SQL')") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("sql_data_access") },
- { C_STRING_WITH_LEN("enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA')") },
+ { STRING_WITH_LEN("sql_data_access") },
+ { STRING_WITH_LEN("enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA')") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("is_deterministic") },
- { C_STRING_WITH_LEN("enum('YES','NO')") },
+ { STRING_WITH_LEN("is_deterministic") },
+ { STRING_WITH_LEN("enum('YES','NO')") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("security_type") },
- { C_STRING_WITH_LEN("enum('INVOKER','DEFINER')") },
+ { STRING_WITH_LEN("security_type") },
+ { STRING_WITH_LEN("enum('INVOKER','DEFINER')") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("param_list") },
- { C_STRING_WITH_LEN("blob") },
+ { STRING_WITH_LEN("param_list") },
+ { STRING_WITH_LEN("blob") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("returns") },
- { C_STRING_WITH_LEN("longblob") },
+ { STRING_WITH_LEN("returns") },
+ { STRING_WITH_LEN("longblob") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("body") },
- { C_STRING_WITH_LEN("longblob") },
+ { STRING_WITH_LEN("body") },
+ { STRING_WITH_LEN("longblob") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("definer") },
- { C_STRING_WITH_LEN("char(") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("definer") },
+ { STRING_WITH_LEN("char(") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("created") },
- { C_STRING_WITH_LEN("timestamp") },
+ { STRING_WITH_LEN("created") },
+ { STRING_WITH_LEN("timestamp") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("modified") },
- { C_STRING_WITH_LEN("timestamp") },
+ { STRING_WITH_LEN("modified") },
+ { STRING_WITH_LEN("timestamp") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("sql_mode") },
- { C_STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
+ { STRING_WITH_LEN("sql_mode") },
+ { STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
"'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY',"
"'NO_UNSIGNED_SUBTRACTION',"
"'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
@@ -131,32 +199,38 @@ TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] =
"'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
"'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
"'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
- "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH')") },
+ "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH',"
+ "'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT')") },
{ NULL, 0 }
},
{
- { C_STRING_WITH_LEN("comment") },
- { C_STRING_WITH_LEN("text") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("comment") },
+ { STRING_WITH_LEN("text") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("character_set_client") },
- { C_STRING_WITH_LEN("char(32)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("character_set_client") },
+ { STRING_WITH_LEN("char(32)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("collation_connection") },
- { C_STRING_WITH_LEN("char(32)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("collation_connection") },
+ { STRING_WITH_LEN("char(32)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("db_collation") },
- { C_STRING_WITH_LEN("char(32)") },
- { C_STRING_WITH_LEN("utf8") }
+ { STRING_WITH_LEN("db_collation") },
+ { STRING_WITH_LEN("char(32)") },
+ { STRING_WITH_LEN("utf8") }
},
{
- { C_STRING_WITH_LEN("body_utf8") },
- { C_STRING_WITH_LEN("longblob") },
+ { STRING_WITH_LEN("body_utf8") },
+ { STRING_WITH_LEN("longblob") },
+ { NULL, 0 }
+ },
+ {
+ { STRING_WITH_LEN("aggregate") },
+ { STRING_WITH_LEN("enum('NONE','GROUP')") },
{ NULL, 0 }
}
};
@@ -176,7 +250,7 @@ class Stored_routine_creation_ctx : public Stored_program_creation_ctx,
{
public:
static Stored_routine_creation_ctx *
- load_from_db(THD *thd, const sp_name *name, TABLE *proc_tbl);
+ load_from_db(THD *thd, const Database_qualified_name *name, TABLE *proc_tbl);
public:
virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root)
@@ -214,15 +288,16 @@ bool load_charset(MEM_ROOT *mem_root,
CHARSET_INFO *dflt_cs,
CHARSET_INFO **cs)
{
- String cs_name;
+ LEX_CSTRING cs_name;
- if (get_field(mem_root, field, &cs_name))
+ if (field->val_str_nopad(mem_root, &cs_name))
{
*cs= dflt_cs;
return TRUE;
}
- *cs= get_charset_by_csname(cs_name.c_ptr(), MY_CS_PRIMARY, MYF(0));
+ DBUG_ASSERT(cs_name.str[cs_name.length] == 0);
+ *cs= get_charset_by_csname(cs_name.str, MY_CS_PRIMARY, MYF(0));
if (*cs == NULL)
{
@@ -240,15 +315,16 @@ bool load_collation(MEM_ROOT *mem_root,
CHARSET_INFO *dflt_cl,
CHARSET_INFO **cl)
{
- String cl_name;
+ LEX_CSTRING cl_name;
- if (get_field(mem_root, field, &cl_name))
+ if (field->val_str_nopad(mem_root, &cl_name))
{
*cl= dflt_cl;
return TRUE;
}
- *cl= get_charset_by_name(cl_name.c_ptr(), MYF(0));
+ DBUG_ASSERT(cl_name.str[cl_name.length] == 0);
+ *cl= get_charset_by_name(cl_name.str, MYF(0));
if (*cl == NULL)
{
@@ -263,8 +339,8 @@ bool load_collation(MEM_ROOT *mem_root,
Stored_routine_creation_ctx *
Stored_routine_creation_ctx::load_from_db(THD *thd,
- const sp_name *name,
- TABLE *proc_tbl)
+ const Database_qualified_name *name,
+ TABLE *proc_tbl)
{
/* Load character set/collation attributes. */
@@ -405,7 +481,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup)
DBUG_ENTER("open_proc_table_for_read");
- table.init_one_table("mysql", 5, "proc", 4, "proc", TL_READ);
+ table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROC_NAME, NULL, TL_READ);
if (open_system_tables_for_read(thd, &table, backup))
DBUG_RETURN(NULL);
@@ -440,7 +516,7 @@ static TABLE *open_proc_table_for_update(THD *thd)
MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("open_proc_table_for_update");
- table_list.init_one_table("mysql", 5, "proc", 4, "proc", TL_WRITE);
+ table_list.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROC_NAME, NULL, TL_WRITE);
if (!(table= open_system_table_for_update(thd, &table_list)))
DBUG_RETURN(NULL);
@@ -459,7 +535,6 @@ static TABLE *open_proc_table_for_update(THD *thd)
Find row in open mysql.proc table representing stored routine.
@param thd Thread context
- @param type Type of routine to find (function or procedure)
@param name Name of routine
@param table TABLE object for open mysql.proc table.
@@ -469,14 +544,16 @@ static TABLE *open_proc_table_for_update(THD *thd)
SP_KEY_NOT_FOUND No routine with given name
*/
-static int
-db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name,
- TABLE *table)
+int
+Sp_handler::db_find_routine_aux(THD *thd,
+ const Database_qualified_name *name,
+ TABLE *table) const
{
uchar key[MAX_KEY_LENGTH]; // db, name, optional key length type
DBUG_ENTER("db_find_routine_aux");
- DBUG_PRINT("enter", ("type: %d name: %.*s",
- type, (int) name->m_name.length, name->m_name.str));
+ DBUG_PRINT("enter", ("type: %s name: %.*s",
+ type_str(),
+ (int) name->m_name.length, name->m_name.str));
/*
Create key to find row. We have to use field->store() to be able to
@@ -487,10 +564,9 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name,
*/
if (name->m_name.length > table->field[1]->field_length)
DBUG_RETURN(SP_KEY_NOT_FOUND);
- table->field[0]->store(name->m_db.str, name->m_db.length, &my_charset_bin);
- table->field[1]->store(name->m_name.str, name->m_name.length,
- &my_charset_bin);
- table->field[2]->store((longlong) type, TRUE);
+ table->field[0]->store(name->m_db, &my_charset_bin);
+ table->field[1]->store(name->m_name, &my_charset_bin);
+ table->field[2]->store((longlong) type(), true);
key_copy(key, table->record[0], table->key_info,
table->key_info->key_length);
@@ -503,12 +579,83 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name,
}
+bool st_sp_chistics::read_from_mysql_proc_row(THD *thd, TABLE *table)
+{
+ LEX_CSTRING str;
+
+ if (table->field[MYSQL_PROC_FIELD_ACCESS]->val_str_nopad(thd->mem_root,
+ &str))
+ return true;
+
+ switch (str.str[0]) {
+ case 'N':
+ daccess= SP_NO_SQL;
+ break;
+ case 'C':
+ daccess= SP_CONTAINS_SQL;
+ break;
+ case 'R':
+ daccess= SP_READS_SQL_DATA;
+ break;
+ case 'M':
+ daccess= SP_MODIFIES_SQL_DATA;
+ break;
+ default:
+ daccess= SP_DEFAULT_ACCESS_MAPPING;
+ }
+
+ if (table->field[MYSQL_PROC_FIELD_DETERMINISTIC]->val_str_nopad(thd->mem_root,
+ &str))
+ return true;
+ detistic= str.str[0] == 'N' ? false : true;
+
+ if (table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->val_str_nopad(thd->mem_root,
+ &str))
+ return true;
+ suid= str.str[0] == 'I' ? SP_IS_NOT_SUID : SP_IS_SUID;
+
+ if (table->field[MYSQL_PROC_FIELD_AGGREGATE]->val_str_nopad(thd->mem_root,
+ &str))
+ return true;
+
+ switch (str.str[0]) {
+ case 'N':
+ agg_type= NOT_AGGREGATE;
+ break;
+ case 'G':
+ agg_type= GROUP_AGGREGATE;
+ break;
+ default:
+ agg_type= DEFAULT_AGGREGATE;
+ }
+
+
+ if (table->field[MYSQL_PROC_FIELD_COMMENT]->val_str_nopad(thd->mem_root,
+ &comment))
+ return true;
+
+ return false;
+}
+
+
+bool AUTHID::read_from_mysql_proc_row(THD *thd, TABLE *table)
+{
+ LEX_CSTRING str;
+ if (table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root,
+ &str))
+ return true;
+ parse(str.str, str.length);
+ if (user.str[user.length])
+ ((char *) user.str)[user.length]= '\0'; // 0-terminate if was truncated
+ return false;
+}
+
+
/**
Find routine definition in mysql.proc table and create corresponding
sp_head object for it.
@param thd Thread context
- @param type Type of routine (TYPE_ENUM_PROCEDURE/...)
@param name Name of routine
@param sphp Out parameter in which pointer to created sp_head
object is returned (0 in case of error).
@@ -523,33 +670,27 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name,
non-0 Error (may be one of special codes like SP_KEY_NOT_FOUND)
*/
-static int
-db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
- sp_head **sphp)
+int
+Sp_handler::db_find_routine(THD *thd,
+ const Database_qualified_name *name,
+ sp_head **sphp) const
{
TABLE *table;
- const char *params, *returns, *body;
+ LEX_CSTRING params, returns, body;
int ret;
- const char *definer;
longlong created;
longlong modified;
- st_sp_chistics chistics;
- char *ptr;
- uint length;
- char buff[65];
- String str(buff, sizeof(buff), &my_charset_bin);
+ Sp_chistics chistics;
bool saved_time_zone_used= thd->time_zone_used;
sql_mode_t sql_mode, saved_mode= thd->variables.sql_mode;
Open_tables_backup open_tables_state_backup;
Stored_program_creation_ctx *creation_ctx;
- char definer_user_name_holder[USERNAME_LENGTH + 1];
- LEX_STRING definer_user_name= { definer_user_name_holder, USERNAME_LENGTH };
- char definer_host_name_holder[HOSTNAME_LENGTH + 1];
- LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH };
+ AUTHID definer;
DBUG_ENTER("db_find_routine");
- DBUG_PRINT("enter", ("type: %d name: %.*s",
- type, (int) name->m_name.length, name->m_name.str));
+ DBUG_PRINT("enter", ("type: %s name: %.*s",
+ type_str(),
+ (int) name->m_name.length, name->m_name.str));
*sphp= 0; // In case of errors
if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup)))
@@ -558,7 +699,7 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
/* Reset sql_mode during data dictionary operations. */
thd->variables.sql_mode= 0;
- if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK)
+ if ((ret= db_find_routine_aux(thd, name, table)) != SP_OK)
goto done;
if (table->s->fields < MYSQL_PROC_FIELD_COUNT)
@@ -567,107 +708,44 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
goto done;
}
- bzero((char *)&chistics, sizeof(chistics));
- if ((ptr= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_ACCESS])) == NULL)
+ if (chistics.read_from_mysql_proc_row(thd, table) ||
+ definer.read_from_mysql_proc_row(thd, table))
{
ret= SP_GET_FIELD_FAILED;
goto done;
}
- switch (ptr[0]) {
- case 'N':
- chistics.daccess= SP_NO_SQL;
- break;
- case 'C':
- chistics.daccess= SP_CONTAINS_SQL;
- break;
- case 'R':
- chistics.daccess= SP_READS_SQL_DATA;
- break;
- case 'M':
- chistics.daccess= SP_MODIFIES_SQL_DATA;
- break;
- default:
- chistics.daccess= SP_DEFAULT_ACCESS_MAPPING;
- }
-
- if ((ptr= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_DETERMINISTIC])) == NULL)
- {
- ret= SP_GET_FIELD_FAILED;
- goto done;
- }
- chistics.detistic= (ptr[0] == 'N' ? FALSE : TRUE);
-
- if ((ptr= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_SECURITY_TYPE])) == NULL)
- {
- ret= SP_GET_FIELD_FAILED;
- goto done;
- }
- chistics.suid= (ptr[0] == 'I' ? SP_IS_NOT_SUID : SP_IS_SUID);
- if ((params= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_PARAM_LIST])) == NULL)
- {
- params= "";
- }
-
- if (type == TYPE_ENUM_PROCEDURE)
- returns= "";
- else if ((returns= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_RETURNS])) == NULL)
+ table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root,
+ &params);
+ if (type() != TYPE_ENUM_FUNCTION)
+ returns= empty_clex_str;
+ else if (table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root,
+ &returns))
{
ret= SP_GET_FIELD_FAILED;
goto done;
}
- if ((body= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_BODY])) == NULL)
+ if (table->field[MYSQL_PROC_FIELD_BODY]->val_str_nopad(thd->mem_root,
+ &body))
{
ret= SP_GET_FIELD_FAILED;
goto done;
}
// Get additional information
- if ((definer= get_field(thd->mem_root,
- table->field[MYSQL_PROC_FIELD_DEFINER])) == NULL)
- {
- ret= SP_GET_FIELD_FAILED;
- goto done;
- }
-
modified= table->field[MYSQL_PROC_FIELD_MODIFIED]->val_int();
created= table->field[MYSQL_PROC_FIELD_CREATED]->val_int();
-
- sql_mode= (ulong) table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int();
-
- table->field[MYSQL_PROC_FIELD_COMMENT]->val_str(&str, &str);
-
- ptr= 0;
- if ((length= str.length()))
- ptr= thd->strmake(str.ptr(), length);
- chistics.comment.str= ptr;
- chistics.comment.length= length;
+ sql_mode= (sql_mode_t) table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int();
creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, name, table);
close_system_tables(thd, &open_tables_state_backup);
table= 0;
- if (parse_user(definer, strlen(definer),
- definer_user_name.str, &definer_user_name.length,
- definer_host_name.str, &definer_host_name.length) &&
- definer_user_name.length && !definer_host_name.length)
- {
- // 'user@' -> 'user@%'
- definer_host_name= host_not_specified;
- }
-
- ret= db_load_routine(thd, type, name, sphp,
- sql_mode, params, returns, body, chistics,
- &definer_user_name, &definer_host_name,
- created, modified, creation_ctx);
+ ret= db_load_routine(thd, name, sphp,
+ sql_mode, params, returns, body, chistics, definer,
+ created, modified, NULL, creation_ctx);
done:
/*
Restore the time zone flag as the timezone usage in proc table
@@ -681,6 +759,23 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
}
+int
+Sp_handler::db_find_and_cache_routine(THD *thd,
+ const Database_qualified_name *name,
+ sp_head **sp) const
+{
+ int rc= db_find_routine(thd, name, sp);
+ if (rc == SP_OK)
+ {
+ sp_cache_insert(get_cache(thd), *sp);
+ DBUG_PRINT("info", ("added new: %p, level: %lu, flags %x",
+ sp[0], sp[0]->m_recursion_level,
+ sp[0]->m_flags));
+ }
+ return rc;
+}
+
+
/**
Silence DEPRECATED SYNTAX warnings when loading a stored procedure
into the cache.
@@ -720,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
@@ -728,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;
@@ -748,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;
@@ -808,14 +907,18 @@ Bad_db_error_handler::handle_condition(THD *thd,
}
-static int
-db_load_routine(THD *thd, stored_procedure_type type,
- sp_name *name, sp_head **sphp,
- sql_mode_t sql_mode, const char *params, const char *returns,
- const char *body, st_sp_chistics &chistics,
- LEX_STRING *definer_user_name, LEX_STRING *definer_host_name,
- longlong created, longlong modified,
- Stored_program_creation_ctx *creation_ctx)
+int
+Sp_handler::db_load_routine(THD *thd, const Database_qualified_name *name,
+ sp_head **sphp,
+ sql_mode_t sql_mode,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ 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;
String defstr;
@@ -829,8 +932,6 @@ db_load_routine(THD *thd, stored_procedure_type type,
thd->lex= &newlex;
newlex.current_select= NULL;
- // Resetting REPLACE and EXIST flags in create_info, for show_create_sp()
- newlex.create_info.DDL_options_st::init();
defstr.set_charset(creation_ctx->get_client_cs());
@@ -840,15 +941,10 @@ db_load_routine(THD *thd, stored_procedure_type type,
definition for SHOW CREATE PROCEDURE later.
*/
- if (!show_create_sp(thd, &defstr,
- type,
- NULL, 0,
- name->m_name.str, name->m_name.length,
- params, strlen(params),
- returns, strlen(returns),
- body, strlen(body),
- &chistics, definer_user_name, definer_host_name,
- sql_mode))
+ if (show_create_sp(thd, &defstr,
+ null_clex_str, name->m_name,
+ params, returns, body,
+ chistics, definer, DDL_options(), sql_mode))
{
ret= SP_INTERNAL_ERROR;
goto end;
@@ -878,14 +974,16 @@ db_load_routine(THD *thd, stored_procedure_type type,
}
{
- *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
generate an error.
*/
- if (cur_db_changed && mysql_change_db(thd, &saved_cur_db_name, TRUE))
+ if (cur_db_changed && mysql_change_db(thd,
+ (LEX_CSTRING*) &saved_cur_db_name,
+ TRUE))
{
ret= SP_INTERNAL_ERROR;
goto end;
@@ -897,10 +995,26 @@ db_load_routine(THD *thd, stored_procedure_type type,
goto end;
}
- (*sphp)->set_definer(definer_user_name, definer_host_name);
- (*sphp)->set_info(created, modified, &chistics, sql_mode);
+ (*sphp)->set_definer(&definer.user, &definer.host);
+ (*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
@@ -921,7 +1035,7 @@ end:
void
-sp_returns_type(THD *thd, String &result, sp_head *sp)
+sp_returns_type(THD *thd, String &result, const sp_head *sp)
{
TABLE table;
TABLE_SHARE share;
@@ -957,7 +1071,6 @@ sp_returns_type(THD *thd, String &result, sp_head *sp)
and invalidates the stored-routine cache.
@param thd Thread context.
- @param type Stored routine type (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
@param name Stored routine name.
@param table A pointer to the opened mysql.proc table
@@ -965,9 +1078,10 @@ sp_returns_type(THD *thd, String &result, sp_head *sp)
@return SP_OK on success, or SP_DELETE_ROW_FAILED on error.
used to indicate about errors.
*/
-static int
-sp_drop_routine_internal(THD *thd, stored_procedure_type type,
- sp_name *name, TABLE *table)
+int
+Sp_handler::sp_drop_routine_internal(THD *thd,
+ const Database_qualified_name *name,
+ TABLE *table) const
{
DBUG_ENTER("sp_drop_routine_internal");
@@ -984,15 +1098,40 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type,
local cache.
*/
sp_head *sp;
- sp_cache **spc= (type == TYPE_ENUM_FUNCTION ?
- &thd->sp_func_cache : &thd->sp_proc_cache);
- sp= sp_cache_lookup(spc, name);
- if (sp)
+ sp_cache **spc= get_cache(thd);
+ DBUG_ASSERT(spc);
+ if ((sp= sp_cache_lookup(spc, name)))
sp_cache_flush_obsolete(spc, &sp);
DBUG_RETURN(SP_OK);
}
+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.
@@ -1000,8 +1139,6 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type,
the mysql.proc.
@param thd Thread context.
- @param type Stored routine type
- (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION).
@param sp Stored routine object to store.
@note Opens and closes the thread tables. Therefore assumes
@@ -1018,16 +1155,14 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type,
*/
bool
-sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
+Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
{
LEX *lex= thd->lex;
bool ret= TRUE;
TABLE *table;
char definer_buf[USER_HOST_BUFF_SIZE];
- LEX_STRING definer;
+ LEX_CSTRING definer;
sql_mode_t saved_mode= thd->variables.sql_mode;
- MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
- MDL_key::FUNCTION : MDL_key::PROCEDURE;
CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str);
@@ -1035,15 +1170,15 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
bool store_failed= FALSE;
DBUG_ENTER("sp_create_routine");
- DBUG_PRINT("enter", ("type: %d name: %.*s", (int) type,
+ DBUG_PRINT("enter", ("type: %s name: %.*s",
+ type_str(),
(int) sp->m_name.length,
sp->m_name.str));
+ MDL_key::enum_mdl_namespace mdl_type= get_mdl_type();
+ LEX_CSTRING returns= empty_clex_str;
String retstr(64);
retstr.set_charset(system_charset_info);
- DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
- type == TYPE_ENUM_FUNCTION);
-
/* Grab an exclusive MDL lock. */
if (lock_object_name(thd, mdl_type, sp->m_db.str, sp->m_name.str))
{
@@ -1071,17 +1206,32 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
if (!(table= open_proc_table_for_update(thd)))
{
- my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(type),sp->m_name.str);
+ my_error(ER_SP_STORE_FAILED, MYF(0), type_str(), sp->m_name.str);
goto done;
}
else
{
/* Checking if the routine already exists */
- if (db_find_routine_aux(thd, type, lex->spname, table) == SP_OK)
+ if (db_find_routine_aux(thd, sp, table) == SP_OK)
{
if (lex->create_info.or_replace())
{
- if ((ret= sp_drop_routine_internal(thd, type, lex->spname, 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())
@@ -1089,20 +1239,21 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SP_ALREADY_EXISTS,
ER_THD(thd, ER_SP_ALREADY_EXISTS),
- SP_TYPE_STRING(type),
- lex->spname->m_name.str);
+ type_str(), sp->m_name.str);
ret= FALSE;
// Setting retstr as it is used for logging.
- if (sp->m_type == TYPE_ENUM_FUNCTION)
+ if (type() == TYPE_ENUM_FUNCTION)
+ {
sp_returns_type(thd, retstr, sp);
+ returns= retstr.lex_cstring();
+ }
goto log;
}
else
{
- my_error(ER_SP_ALREADY_EXISTS, MYF(0),
- SP_TYPE_STRING(type), sp->m_name.str);
+ my_error(ER_SP_ALREADY_EXISTS, MYF(0), type_str(), sp->m_name.str);
goto done;
}
}
@@ -1114,8 +1265,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
if (table->s->fields < MYSQL_PROC_FIELD_COUNT)
{
- my_error(ER_SP_STORE_FAILED, MYF(0),
- SP_TYPE_STRING(type), sp->m_name.str);
+ my_error(ER_SP_STORE_FAILED, MYF(0), type_str(), sp->m_name.str);
goto done;
}
@@ -1135,45 +1285,53 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
store_failed=
table->field[MYSQL_PROC_FIELD_DB]->
- store(sp->m_db.str, sp->m_db.length, system_charset_info);
+ store(sp->m_db, system_charset_info);
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_NAME]->
- store(sp->m_name.str, sp->m_name.length, system_charset_info);
+ store(sp->m_name, system_charset_info);
+
+ if (sp->agg_type() != DEFAULT_AGGREGATE)
+ {
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_AGGREGATE]->
+ store((longlong)sp->agg_type(),TRUE);
+ }
store_failed= store_failed ||
table->field[MYSQL_PROC_MYSQL_TYPE]->
- store((longlong)type, TRUE);
+ store((longlong) type(), true);
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]->
- store(sp->m_name.str, sp->m_name.length, system_charset_info);
+ store(sp->m_name, system_charset_info);
- if (sp->m_chistics->daccess != SP_DEFAULT_ACCESS)
+ if (sp->daccess() != SP_DEFAULT_ACCESS)
{
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_ACCESS]->
- store((longlong)sp->m_chistics->daccess, TRUE);
+ store((longlong)sp->daccess(), TRUE);
}
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_DETERMINISTIC]->
- store((longlong)(sp->m_chistics->detistic ? 1 : 2), TRUE);
+ store((longlong)(sp->detistic() ? 1 : 2), TRUE);
- if (sp->m_chistics->suid != SP_IS_DEFAULT_SUID)
+ if (sp->suid() != SP_IS_DEFAULT_SUID)
{
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->
- store((longlong)sp->m_chistics->suid, TRUE);
+ store((longlong)sp->suid(), TRUE);
}
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_PARAM_LIST]->
- store(sp->m_params.str, sp->m_params.length, system_charset_info);
+ store(sp->m_params, system_charset_info);
- if (sp->m_type == TYPE_ENUM_FUNCTION)
+ if (type() == TYPE_ENUM_FUNCTION)
{
sp_returns_type(thd, retstr, sp);
+ returns= retstr.lex_cstring();
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_RETURNS]->
@@ -1182,11 +1340,11 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_BODY]->
- store(sp->m_body.str, sp->m_body.length, system_charset_info);
+ store(sp->m_body, system_charset_info);
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_DEFINER]->
- store(definer.str, definer.length, system_charset_info);
+ store(definer, system_charset_info);
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time();
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
@@ -1195,26 +1353,25 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
table->field[MYSQL_PROC_FIELD_SQL_MODE]->
store((longlong)saved_mode, TRUE);
- if (sp->m_chistics->comment.str)
+ if (sp->comment().str)
{
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_COMMENT]->
- store(sp->m_chistics->comment.str, sp->m_chistics->comment.length,
- system_charset_info);
+ store(sp->comment(), system_charset_info);
}
- if ((sp->m_type == TYPE_ENUM_FUNCTION) &&
+ if (type() == TYPE_ENUM_FUNCTION &&
!trust_function_creators && mysql_bin_log.is_open())
{
- if (!sp->m_chistics->detistic)
+ if (!sp->detistic())
{
/*
Note that this test is not perfect; one could use
a non-deterministic read-only function in an update statement.
*/
enum enum_sp_data_access access=
- (sp->m_chistics->daccess == SP_DEFAULT_ACCESS) ?
- SP_DEFAULT_ACCESS_MAPPING : sp->m_chistics->daccess;
+ (sp->daccess() == SP_DEFAULT_ACCESS) ?
+ SP_DEFAULT_ACCESS_MAPPING : sp->daccess();
if (access == SP_CONTAINS_SQL ||
access == SP_MODIFIES_SQL_DATA)
{
@@ -1251,7 +1408,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
table->field[MYSQL_PROC_FIELD_BODY_UTF8]->set_notnull();
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_BODY_UTF8]->store(
- sp->m_body_utf8.str, sp->m_body_utf8.length, system_charset_info);
+ sp->m_body_utf8, system_charset_info);
if (store_failed)
{
@@ -1261,8 +1418,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
if (table->file->ha_write_row(table->record[0]))
{
- my_error(ER_SP_ALREADY_EXISTS, MYF(0),
- SP_TYPE_STRING(type), sp->m_name.str);
+ my_error(ER_SP_ALREADY_EXISTS, MYF(0), type_str(), sp->m_name.str);
goto done;
}
/* Make change permanent and avoid 'table is marked as crashed' errors */
@@ -1279,16 +1435,13 @@ log:
StringBuffer<128> log_query(thd->variables.character_set_client);
DBUG_ASSERT(log_query.charset()->mbminlen == 1);
- if (!show_create_sp(thd, &log_query,
- sp->m_type,
- (sp->m_explicit_name ? sp->m_db.str : NULL),
- (sp->m_explicit_name ? sp->m_db.length : 0),
- sp->m_name.str, sp->m_name.length,
- sp->m_params.str, sp->m_params.length,
- retstr.ptr(), retstr.length(),
- sp->m_body.str, sp->m_body.length,
- sp->m_chistics, &(thd->lex->definer->user),
- &(thd->lex->definer->host),
+ if (show_create_sp(thd, &log_query,
+ sp->m_explicit_name ? sp->m_db : null_clex_str,
+ sp->m_name,
+ sp->m_params, returns, sp->m_body,
+ sp->chistics(),
+ thd->lex->definer[0],
+ thd->lex->create_info,
saved_mode))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
@@ -1316,6 +1469,69 @@ done:
}
+static bool
+append_suid(String *buf, enum_sp_suid_behaviour suid)
+{
+ return suid == SP_IS_NOT_SUID &&
+ buf->append(STRING_WITH_LEN(" SQL SECURITY INVOKER\n"));
+}
+
+
+static bool
+append_comment(String *buf, const LEX_CSTRING &comment)
+{
+ if (!comment.length)
+ return false;
+ if (buf->append(STRING_WITH_LEN(" COMMENT ")))
+ return true;
+ append_unescaped(buf, comment.str, comment.length);
+ return buf->append('\n');
+}
+
+
+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.
@@ -1324,8 +1540,6 @@ done:
from the mysql.proc table and invalidates the stored-routine cache.
@param thd Thread context.
- @param type Stored routine type
- (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
@param name Stored routine name.
@return Error code. SP_OK is returned on success. Other SP_ constants are
@@ -1333,18 +1547,16 @@ done:
*/
int
-sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
+Sp_handler::sp_drop_routine(THD *thd,
+ const Database_qualified_name *name) const
{
TABLE *table;
int ret;
- MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
- MDL_key::FUNCTION : MDL_key::PROCEDURE;
DBUG_ENTER("sp_drop_routine");
- DBUG_PRINT("enter", ("type: %d name: %.*s",
- type, (int) name->m_name.length, name->m_name.str));
-
- DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
- type == TYPE_ENUM_FUNCTION);
+ DBUG_PRINT("enter", ("type: %s name: %.*s",
+ type_str(),
+ (int) name->m_name.length, name->m_name.str));
+ MDL_key::enum_mdl_namespace mdl_type= get_mdl_type();
/* Grab an exclusive MDL lock. */
if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str))
@@ -1353,10 +1565,7 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
if (!(table= open_proc_table_for_update(thd)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
- if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
- ret= sp_drop_routine_internal(thd, type, 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;
/*
@@ -1377,8 +1586,6 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
successful update, the cache is invalidated.
@param thd Thread context.
- @param type Stored routine type
- (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
@param name Stored routine name.
@param chistics New values of stored routine attributes to write.
@@ -1387,20 +1594,16 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
*/
int
-sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
- st_sp_chistics *chistics)
+Sp_handler::sp_update_routine(THD *thd, const Database_qualified_name *name,
+ const st_sp_chistics *chistics) const
{
TABLE *table;
int ret;
- MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
- MDL_key::FUNCTION : MDL_key::PROCEDURE;
DBUG_ENTER("sp_update_routine");
- DBUG_PRINT("enter", ("type: %d name: %.*s",
- (int) type,
+ DBUG_PRINT("enter", ("type: %s name: %.*s",
+ type_str(),
(int) name->m_name.length, name->m_name.str));
-
- DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
- type == TYPE_ENUM_FUNCTION);
+ MDL_key::enum_mdl_namespace mdl_type= get_mdl_type();
/* Grab an exclusive MDL lock. */
if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str))
@@ -1409,9 +1612,9 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
if (!(table= open_proc_table_for_update(thd)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
- if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
+ if ((ret= db_find_routine_aux(thd, name, table)) == SP_OK)
{
- if (type == TYPE_ENUM_FUNCTION && ! trust_function_creators &&
+ if (type() == TYPE_ENUM_FUNCTION && ! trust_function_creators &&
mysql_bin_log.is_open() &&
(chistics->daccess == SP_CONTAINS_SQL ||
chistics->daccess == SP_MODIFIES_SQL_DATA))
@@ -1444,9 +1647,11 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
table->field[MYSQL_PROC_FIELD_ACCESS]->
store((longlong)chistics->daccess, TRUE);
if (chistics->comment.str)
- table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment.str,
- chistics->comment.length,
+ table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment,
system_charset_info);
+ if (chistics->agg_type != DEFAULT_AGGREGATE)
+ table->field[MYSQL_PROC_FIELD_AGGREGATE]->
+ store((longlong)chistics->agg_type, TRUE);
if ((ret= table->file->ha_update_row(table->record[1],table->record[0])) &&
ret != HA_ERR_RECORD_IS_THE_SAME)
ret= SP_WRITE_ROW_FAILED;
@@ -1502,7 +1707,7 @@ public:
cases.
*/
-bool lock_db_routines(THD *thd, char *db)
+bool lock_db_routines(THD *thd, const char *db)
{
TABLE *table;
uint key_len;
@@ -1512,7 +1717,7 @@ bool lock_db_routines(THD *thd, char *db)
uchar keybuf[MAX_KEY_LENGTH];
DBUG_ENTER("lock_db_routines");
- DBUG_ASSERT(ok_for_lower_case_names(db));
+ DBUG_SLOW_ASSERT(ok_for_lower_case_names(db));
/*
mysql.proc will be re-opened during deletion, so we can ignore
@@ -1555,9 +1760,12 @@ bool lock_db_routines(THD *thd, 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)));
}
@@ -1588,7 +1796,7 @@ bool lock_db_routines(THD *thd, char *db)
*/
int
-sp_drop_db_routines(THD *thd, char *db)
+sp_drop_db_routines(THD *thd, const char *db)
{
TABLE *table;
int ret;
@@ -1661,8 +1869,6 @@ err:
calls sp_head::show_create_routine() for the object.
@param thd Thread context.
- @param type Stored routine type
- (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
@param name Stored routine name.
@return Error status.
@@ -1671,18 +1877,16 @@ err:
*/
bool
-sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name)
+Sp_handler::sp_show_create_routine(THD *thd,
+ const Database_qualified_name *name) const
{
sp_head *sp;
DBUG_ENTER("sp_show_create_routine");
- DBUG_PRINT("enter", ("name: %.*s",
+ DBUG_PRINT("enter", ("type: %s name: %.*s",
+ type_str(),
(int) name->m_name.length,
name->m_name.str));
-
- DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
- type == TYPE_ENUM_FUNCTION);
-
/*
@todo: Consider using prelocking for this code as well. Currently
SHOW CREATE PROCEDURE/FUNCTION is a dirty read of the data
@@ -1690,18 +1894,16 @@ sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name)
It is "safe" to do as long as it doesn't affect the results
of the binary log or the query cache, which currently it does not.
*/
- if (sp_cache_routine(thd, type, name, FALSE, &sp))
+ if (sp_cache_routine(thd, name, false, &sp))
DBUG_RETURN(TRUE);
- if (sp == NULL || sp->show_create_routine(thd, type))
+ if (sp == NULL || sp->show_create_routine(thd, this))
{
/*
If we have insufficient privileges, pretend the routine
does not exist.
*/
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
- type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE",
- name->m_name.str);
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), type_str(), name->m_name.str);
DBUG_RETURN(TRUE);
}
@@ -1709,12 +1911,176 @@ sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name)
}
+/*
+ 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
+ and returns NULL.
+ Otherwise, depending on the current recursion level, it:
+ - either returns the original SP,
+ - or makes and returns a new clone of SP
+*/
+sp_head *
+Sp_handler::sp_clone_and_link_routine(THD *thd,
+ const Database_qualified_name *name,
+ 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;
+ 64 -- size of "returns" column of mysql.proc.
+ */
+ String retstr(64);
+ retstr.set_charset(sp->get_creation_ctx()->get_client_cs());
+
+ DBUG_PRINT("info", ("found: %p", sp));
+ if (sp->m_first_free_instance)
+ {
+ DBUG_PRINT("info", ("first free: %p level: %lu flags %x",
+ sp->m_first_free_instance,
+ sp->m_first_free_instance->m_recursion_level,
+ sp->m_first_free_instance->m_flags));
+ DBUG_ASSERT(!(sp->m_first_free_instance->m_flags & sp_head::IS_INVOKED));
+ if (sp->m_first_free_instance->m_recursion_level > recursion_depth(thd))
+ {
+ recursion_level_error(thd, sp);
+ DBUG_RETURN(0);
+ }
+ DBUG_RETURN(sp->m_first_free_instance);
+ }
+ /*
+ Actually depth could be +1 than the actual value in case a SP calls
+ SHOW CREATE PROCEDURE. Hence, the linked list could hold up to one more
+ instance.
+ */
+
+ level= sp->m_last_cached_sp->m_recursion_level + 1;
+ if (level > recursion_depth(thd))
+ {
+ recursion_level_error(thd, sp);
+ DBUG_RETURN(0);
+ }
+
+ if (type() == TYPE_ENUM_FUNCTION)
+ {
+ sp_returns_type(thd, retstr, sp);
+ returns= retstr.lex_cstring();
+ }
+
+ 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->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;
+ sp->m_last_cached_sp= sp->m_first_free_instance= new_sp;
+ DBUG_PRINT("info", ("added level: %p, level: %lu, flags %x",
+ new_sp, new_sp->m_recursion_level,
+ new_sp->m_flags));
+ DBUG_RETURN(new_sp);
+ }
+ DBUG_RETURN(0);
+}
+
+
/**
Obtain object representing stored procedure/function by its name from
stored procedures cache and looking into mysql.proc if needed.
@param thd thread context
- @param type type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE)
@param name name of procedure
@param cp hash to look routine in
@param cache_only if true perform cache-only lookup
@@ -1727,94 +2093,90 @@ sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name)
*/
sp_head *
-sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
- sp_cache **cp, bool cache_only)
+Sp_handler::sp_find_routine(THD *thd, const Database_qualified_name *name,
+ bool cache_only) const
{
- sp_head *sp;
- ulong depth= (type == TYPE_ENUM_PROCEDURE ?
- thd->variables.max_sp_recursion_depth :
- 0);
- DBUG_ENTER("sp_find_routine");
- DBUG_PRINT("enter", ("name: %.*s.%.*s type: %d cache only %d",
+ 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,
- type, cache_only));
+ type_str(), cache_only));
+ sp_cache **cp= get_cache(thd);
+ sp_head *sp;
if ((sp= sp_cache_lookup(cp, name)))
- {
- ulong level;
- sp_head *new_sp;
- const char *returns= "";
+ DBUG_RETURN(sp_clone_and_link_routine(thd, name, sp));
+ if (!cache_only)
+ db_find_and_cache_routine(thd, name, &sp);
+ DBUG_RETURN(sp);
+}
- /*
- String buffer for RETURNS data type must have system charset;
- 64 -- size of "returns" column of mysql.proc.
- */
- String retstr(64);
- retstr.set_charset(sp->get_creation_ctx()->get_client_cs());
- DBUG_PRINT("info", ("found:%p", sp));
- if (sp->m_first_free_instance)
- {
- DBUG_PRINT("info", ("first free:%p level: %lu flags %x",
- sp->m_first_free_instance,
- sp->m_first_free_instance->m_recursion_level,
- sp->m_first_free_instance->m_flags));
- DBUG_ASSERT(!(sp->m_first_free_instance->m_flags & sp_head::IS_INVOKED));
- if (sp->m_first_free_instance->m_recursion_level > depth)
- {
- sp->recursion_level_error(thd);
- DBUG_RETURN(0);
- }
- DBUG_RETURN(sp->m_first_free_instance);
- }
- /*
- Actually depth could be +1 than the actual value in case a SP calls
- SHOW CREATE PROCEDURE. Hence, the linked list could hold up to one more
- instance.
- */
+/**
+ 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);
+}
- level= sp->m_last_cached_sp->m_recursion_level + 1;
- if (level > depth)
- {
- sp->recursion_level_error(thd);
- DBUG_RETURN(0);
- }
- if (type == TYPE_ENUM_FUNCTION)
- {
- sp_returns_type(thd, retstr, sp);
- returns= retstr.ptr();
- }
- if (db_load_routine(thd, type, name, &new_sp,
- sp->m_sql_mode, sp->m_params.str, returns,
- sp->m_body.str, *sp->m_chistics,
- &sp->m_definer_user, &sp->m_definer_host,
- sp->m_created, sp->m_modified,
- sp->get_creation_ctx()) == SP_OK)
- {
- sp->m_last_cached_sp->m_next_cached_sp= new_sp;
- new_sp->m_recursion_level= level;
- new_sp->m_first_instance= sp;
- sp->m_last_cached_sp= sp->m_first_free_instance= new_sp;
- DBUG_PRINT("info", ("added level:%p, level: %lu, flags %x",
- new_sp, new_sp->m_recursion_level,
- new_sp->m_flags));
- DBUG_RETURN(new_sp);
- }
- DBUG_RETURN(0);
- }
- if (!cache_only)
- {
- if (db_find_routine(thd, type, name, &sp) == SP_OK)
- {
- sp_cache_insert(cp, sp);
- DBUG_PRINT("info", ("added new:%p, level: %lu, flags %x",
- sp, sp->m_recursion_level,
- sp->m_flags));
- }
- }
- DBUG_RETURN(sp);
+/**
+ 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));
}
@@ -1824,8 +2186,6 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
@param thd Thread handler
@param routines List of needles in the hay stack
- @param is_proc Indicates whether routines in the list are procedures
- or functions.
@return
@retval FALSE Found.
@@ -1833,7 +2193,7 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
*/
bool
-sp_exist_routines(THD *thd, TABLE_LIST *routines, bool is_proc)
+Sp_handler::sp_exist_routines(THD *thd, TABLE_LIST *routines) const
{
TABLE_LIST *routine;
bool sp_object_found;
@@ -1841,25 +2201,18 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool is_proc)
for (routine= routines; routine; routine= routine->next_global)
{
sp_name *name;
- LEX_STRING lex_db;
- LEX_STRING lex_name;
- lex_db.length= strlen(routine->db);
- lex_name.length= strlen(routine->table_name);
- lex_db.str= thd->strmake(routine->db, lex_db.length);
- lex_name.str= thd->strmake(routine->table_name, lex_name.length);
- name= new sp_name(lex_db, lex_name, true);
- name->init_qname(thd);
- sp_object_found= is_proc ? sp_find_routine(thd, TYPE_ENUM_PROCEDURE,
- name, &thd->sp_proc_cache,
- FALSE) != NULL :
- sp_find_routine(thd, TYPE_ENUM_FUNCTION,
- name, &thd->sp_func_cache,
- FALSE) != NULL;
+ LEX_CSTRING lex_db;
+ LEX_CSTRING lex_name;
+ thd->make_lex_string(&lex_db, routine->db.str, routine->db.length);
+ thd->make_lex_string(&lex_name, routine->table_name.str,
+ routine->table_name.length);
+ name= new sp_name(&lex_db, &lex_name, true);
+ sp_object_found= sp_find_routine(thd, name, false) != NULL;
thd->get_stmt_da()->clear_warning_info(thd->query_id);
if (! sp_object_found)
{
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION or PROCEDURE",
- routine->table_name);
+ routine->table_name.str);
DBUG_RETURN(TRUE);
}
}
@@ -1910,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,
@@ -1927,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;
}
@@ -1934,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.
@@ -1945,20 +2563,18 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
@param arena Arena in which memory for new element of the set
will be allocated
@param rt Routine name
- @param rt_type Routine type (one of TYPE_ENUM_PROCEDURE/...)
@note
Will also add element to end of 'Query_tables_list::sroutines_list' list
(and will take into account that this is an explicitly used routine).
*/
-void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
- sp_name *rt, enum stored_procedure_type rt_type)
+void Sp_handler::add_used_routine(Query_tables_list *prelocking_ctx,
+ Query_arena *arena,
+ const Database_qualified_name *rt) const
{
- MDL_key key((rt_type == TYPE_ENUM_FUNCTION) ? MDL_key::FUNCTION :
- MDL_key::PROCEDURE,
- rt->m_db.str, rt->m_name.str);
- (void)sp_add_used_routine(prelocking_ctx, arena, &key, 0);
+ MDL_key key(get_mdl_type(), rt->m_db.str, rt->m_name.str);
+ (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;
@@ -2051,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);
}
}
@@ -2076,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);
}
@@ -2085,24 +2703,21 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
prelocking until 'sp_name' is eradicated as a class.
*/
-int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt,
- bool lookup_only, sp_head **sp)
+int Sroutine_hash_entry::sp_cache_routine(THD *thd,
+ bool lookup_only,
+ sp_head **sp) const
{
char qname_buff[NAME_LEN*2+1+1];
- sp_name name(&rt->mdl_request.key, qname_buff);
- MDL_key::enum_mdl_namespace mdl_type= rt->mdl_request.key.mdl_namespace();
- stored_procedure_type type= ((mdl_type == MDL_key::FUNCTION) ?
- TYPE_ENUM_FUNCTION : TYPE_ENUM_PROCEDURE);
-
+ sp_name name(&mdl_request.key, qname_buff);
/*
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
in sroutines_list has an MDL lock unless it's a top-level call, or a
trigger, but triggers can't occur here (see the preceding assert).
*/
- DBUG_ASSERT(rt->mdl_request.ticket || rt == thd->lex->sroutines_list.first);
+ DBUG_ASSERT(mdl_request.ticket || this == thd->lex->sroutines_list.first);
- return sp_cache_routine(thd, type, &name, lookup_only, sp);
+ return m_handler->sp_cache_routine(thd, &name, lookup_only, sp);
}
@@ -2113,7 +2728,6 @@ int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt,
loading.
@param[in] thd Thread context.
- @param[in] type Type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE).
@param[in] name Name of routine.
@param[in] lookup_only Only check that the routine is in the cache.
If it's not, don't try to load. If it is present,
@@ -2126,16 +2740,17 @@ int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt,
@retval non-0 Error while loading routine from mysql,proc table.
*/
-int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name,
- bool lookup_only, sp_head **sp)
+int Sp_handler::sp_cache_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool lookup_only,
+ sp_head **sp) const
{
int ret= 0;
- sp_cache **spc= (type == TYPE_ENUM_FUNCTION ?
- &thd->sp_func_cache : &thd->sp_proc_cache);
+ sp_cache **spc= get_cache(thd);
- DBUG_ENTER("sp_cache_routine");
+ DBUG_ENTER("Sp_handler::sp_cache_routine");
- DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE);
+ DBUG_ASSERT(spc);
*sp= sp_cache_lookup(spc, name);
@@ -2149,10 +2764,9 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name,
DBUG_RETURN(SP_OK);
}
- switch ((ret= db_find_routine(thd, type, name, sp)))
+ switch ((ret= db_find_and_cache_routine(thd, name, sp)))
{
case SP_OK:
- sp_cache_insert(spc, *sp);
break;
case SP_KEY_NOT_FOUND:
ret= SP_OK;
@@ -2175,20 +2789,8 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name,
*/
if (! thd->is_error())
{
- /*
- SP allows full NAME_LEN chars thus he have to allocate enough
- size in bytes. Otherwise there is stack overrun could happen
- if multibyte sequence is `name`. `db` is still safe because the
- rest of the server checks agains NAME_LEN bytes and not chars.
- Hence, the overrun happens only if the name is in length > 32 and
- uses multibyte (cyrillic, greek, etc.)
- */
- char n[NAME_LEN*2+2];
-
- /* m_qname.str is not always \0 terminated */
- memcpy(n, name->m_qname.str, name->m_qname.length);
- n[name->m_qname.length]= '\0';
- my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret);
+ my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0),
+ ErrConvDQName(name).ptr(), ret);
}
break;
}
@@ -2197,59 +2799,135 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name,
/**
+ 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
- Returns TRUE on success, FALSE on (alloc) failure.
+ Returns false on success, true on (alloc) failure.
*/
bool
-show_create_sp(THD *thd, String *buf,
- stored_procedure_type type,
- const char *db, ulong dblen,
- const char *name, ulong namelen,
- const char *params, ulong paramslen,
- const char *returns, ulong returnslen,
- const char *body, ulong bodylen,
- st_sp_chistics *chistics,
- const LEX_STRING *definer_user,
- const LEX_STRING *definer_host,
- sql_mode_t sql_mode)
+Sp_handler::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;
+ size_t agglen= (chistics.agg_type == GROUP_AGGREGATE)? 10 : 0;
+ LEX_CSTRING tmp;
+
/* Make some room to begin with */
- if (buf->alloc(100 + dblen + 1 + namelen + paramslen + returnslen + bodylen +
- chistics->comment.length + 10 /* length of " DEFINER= "*/ +
- USER_HOST_BUFF_SIZE))
- return FALSE;
+ if (buf->alloc(100 + db.length + 1 + name.length +
+ params.length + returns.length +
+ chistics.comment.length + 10 /* length of " DEFINER= "*/ +
+ agglen + USER_HOST_BUFF_SIZE))
+ return true;
thd->variables.sql_mode= sql_mode;
buf->append(STRING_WITH_LEN("CREATE "));
- if (thd->lex->create_info.or_replace())
+ if (ddl_options.or_replace())
buf->append(STRING_WITH_LEN("OR REPLACE "));
- append_definer(thd, buf, definer_user, definer_host);
- if (type == TYPE_ENUM_FUNCTION)
- buf->append(STRING_WITH_LEN("FUNCTION "));
- else
- buf->append(STRING_WITH_LEN("PROCEDURE "));
- if (thd->lex->create_info.if_not_exists())
+ append_definer(thd, buf, &definer.user, &definer.host);
+ if (chistics.agg_type == GROUP_AGGREGATE)
+ buf->append(STRING_WITH_LEN("AGGREGATE "));
+ tmp= type_lex_cstring();
+ buf->append(&tmp);
+ buf->append(STRING_WITH_LEN(" "));
+ if (ddl_options.if_not_exists())
buf->append(STRING_WITH_LEN("IF NOT EXISTS "));
- if (dblen > 0)
+ if (db.length > 0)
{
- append_identifier(thd, buf, db, dblen);
+ append_identifier(thd, buf, &db);
buf->append('.');
}
- append_identifier(thd, buf, name, namelen);
+ append_identifier(thd, buf, &name);
buf->append('(');
- buf->append(params, paramslen);
+ buf->append(&params);
buf->append(')');
- if (type == TYPE_ENUM_FUNCTION)
+ if (type() == TYPE_ENUM_FUNCTION)
{
- buf->append(STRING_WITH_LEN(" RETURNS "));
- buf->append(returns, returnslen);
+ if (sql_mode & MODE_ORACLE)
+ buf->append(STRING_WITH_LEN(" RETURN "));
+ else
+ buf->append(STRING_WITH_LEN(" RETURNS "));
+ buf->append(&returns);
}
buf->append('\n');
- switch (chistics->daccess) {
+ switch (chistics.daccess) {
case SP_NO_SQL:
buf->append(STRING_WITH_LEN(" NO SQL\n"));
break;
@@ -2264,19 +2942,13 @@ show_create_sp(THD *thd, String *buf,
/* Do nothing */
break;
}
- if (chistics->detistic)
+ if (chistics.detistic)
buf->append(STRING_WITH_LEN(" DETERMINISTIC\n"));
- if (chistics->suid == SP_IS_NOT_SUID)
- buf->append(STRING_WITH_LEN(" SQL SECURITY INVOKER\n"));
- if (chistics->comment.length)
- {
- buf->append(STRING_WITH_LEN(" COMMENT "));
- append_unescaped(buf, chistics->comment.str, chistics->comment.length);
- buf->append('\n');
- }
- buf->append(body, bodylen);
+ append_suid(buf, chistics.suid);
+ append_comment(buf, chistics.comment);
+ buf->append(body.str, body.length); // Not \0 terminated
thd->variables.sql_mode= old_sql_mode;
- return TRUE;
+ return false;
}
@@ -2301,28 +2973,19 @@ show_create_sp(THD *thd, String *buf,
*/
sp_head *
-sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db,
- String *name, sql_mode_t sql_mode,
- stored_procedure_type type,
- const char *returns, const char *params,
- bool *free_sp_head)
+Sp_handler::sp_load_for_information_schema(THD *thd, TABLE *proc_table,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ sql_mode_t sql_mode,
+ bool *free_sp_head) const
{
- const char *sp_body;
String defstr;
- struct st_sp_chistics sp_chistics;
- const LEX_STRING definer_user= {(char*)STRING_WITH_LEN("")};
- const LEX_STRING definer_host= {(char*)STRING_WITH_LEN("")};
- LEX_STRING sp_db_str;
- LEX_STRING sp_name_str;
+ const AUTHID definer= {{STRING_WITH_LEN("")}, {STRING_WITH_LEN("")}};
sp_head *sp;
- sp_cache **spc= ((type == TYPE_ENUM_PROCEDURE) ?
- &thd->sp_proc_cache : &thd->sp_func_cache);
- sp_db_str.str= db->c_ptr();
- sp_db_str.length= db->length();
- sp_name_str.str= name->c_ptr();
- sp_name_str.length= name->length();
- sp_name sp_name_obj(sp_db_str, sp_name_str, true);
- sp_name_obj.init_qname(thd);
+ sp_cache **spc= get_cache(thd);
+ sp_name sp_name_obj(&db, &name, true); // This can change "name"
*free_sp_head= 0;
if ((sp= sp_cache_lookup(spc, &sp_name_obj)))
{
@@ -2332,21 +2995,16 @@ sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db,
LEX *old_lex= thd->lex, newlex;
Stored_program_creation_ctx *creation_ctx=
Stored_routine_creation_ctx::load_from_db(thd, &sp_name_obj, proc_table);
- sp_body= (type == TYPE_ENUM_FUNCTION ? "RETURN NULL" : "BEGIN END");
- bzero((char*) &sp_chistics, sizeof(sp_chistics));
defstr.set_charset(creation_ctx->get_client_cs());
- if (!show_create_sp(thd, &defstr, type,
- sp_db_str.str, sp_db_str.length,
- sp_name_obj.m_name.str, sp_name_obj.m_name.length,
- params, strlen(params),
- returns, strlen(returns),
- sp_body, strlen(sp_body),
- &sp_chistics, &definer_user, &definer_host, sql_mode))
+ if (show_create_sp(thd, &defstr,
+ sp_name_obj.m_db, sp_name_obj.m_name,
+ params, returns, empty_body_lex_cstring(sql_mode),
+ Sp_chistics(), definer, DDL_options(), sql_mode))
return 0;
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);
@@ -2354,3 +3012,18 @@ sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db,
return sp;
}
+
+LEX_CSTRING Sp_handler_procedure::empty_body_lex_cstring(sql_mode_t mode) const
+{
+ static LEX_CSTRING m_empty_body_std= {C_STRING_WITH_LEN("BEGIN END")};
+ static LEX_CSTRING m_empty_body_ora= {C_STRING_WITH_LEN("AS BEGIN NULL; END")};
+ return mode & MODE_ORACLE ? m_empty_body_ora : m_empty_body_std;
+}
+
+
+LEX_CSTRING Sp_handler_function::empty_body_lex_cstring(sql_mode_t mode) const
+{
+ static LEX_CSTRING m_empty_body_std= {C_STRING_WITH_LEN("RETURN NULL")};
+ static LEX_CSTRING m_empty_body_ora= {C_STRING_WITH_LEN("AS BEGIN RETURN NULL; END")};
+ return mode & MODE_ORACLE ? m_empty_body_ora : m_empty_body_std;
+}