summaryrefslogtreecommitdiff
path: root/sql/sql_parse.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r--sql/sql_parse.cc313
1 files changed, 260 insertions, 53 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index ba8a4af794a..540d7f132ac 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -26,6 +26,9 @@
#include "ha_innodb.h"
#endif
+#include "sp_head.h"
+#include "sp.h"
+
#ifdef HAVE_OPENSSL
/*
Without SSL the handshake consists of one packet. This packet
@@ -44,6 +47,15 @@
#define MIN_HANDSHAKE_SIZE 6
#endif /* HAVE_OPENSSL */
+/* Used in error handling only */
+#define SP_TYPE_STRING(LP) \
+ ((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
+#define SP_COM_STRING(LP) \
+ ((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \
+ (LP)->sql_command == SQLCOM_ALTER_FUNCTION || \
+ (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
+ "FUNCTION" : "PROCEDURE")
+
extern int yyparse(void *thd);
extern "C" pthread_mutex_t THR_LOCK_keycache;
#ifdef SOLARIS
@@ -833,7 +845,7 @@ pthread_handler_decl(handle_one_connection,arg)
thd->version=refresh_version;
thd->set_time();
thd->init_for_queries();
- while (!net->error && net->vio != 0 && !thd->killed)
+ while (!net->error && net->vio != 0 && !(thd->killed == THD::KILL_CONNECTION))
{
if (do_command(thd))
break;
@@ -1037,6 +1049,9 @@ bool do_command(THD *thd)
}
else
{
+ if (thd->killed == THD::KILL_QUERY)
+ thd->killed= THD::NOT_KILLED;
+
packet=(char*) net->read_pos;
command = (enum enum_server_command) (uchar) packet[0];
if (command >= COM_END)
@@ -1456,7 +1471,7 @@ restore_user:
{
statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status);
ulong id=(ulong) uint4korr(packet);
- kill_one_thread(thd,id);
+ kill_one_thread(thd,id,false);
break;
}
case COM_DEBUG:
@@ -1511,6 +1526,14 @@ restore_user:
thread_running--;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
+
+ /*
+ Clear the SP function cache after each statement (QQ this is a temporary
+ solution; caching will be rehacked later).
+ Note: Must do this before we free_root.
+ */
+ sp_clear_function_cache(thd);
+
free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_RETURN(error);
}
@@ -1565,7 +1588,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
** Execute command saved in thd and current_lex->sql_command
****************************************************************************/
-void
+int
mysql_execute_command(THD *thd)
{
int res= 0;
@@ -1575,6 +1598,13 @@ mysql_execute_command(THD *thd)
SELECT_LEX_UNIT *unit= &lex->unit;
DBUG_ENTER("mysql_execute_command");
+ if (lex->sql_command != SQLCOM_CREATE_PROCEDURE &&
+ lex->sql_command != SQLCOM_CREATE_SPFUNCTION)
+ {
+ if (sp_cache_functions(thd, lex))
+ DBUG_RETURN(-1);
+ }
+
/*
Reset warning count for each query that uses tables
A better approach would be to reset this for any commands
@@ -1597,7 +1627,7 @@ mysql_execute_command(THD *thd)
given and the table list says the query should not be replicated
*/
if (table_rules_on && tables && !tables_ok(thd,tables))
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
#ifndef TO_BE_DELETED
/*
This is a workaround to deal with the shortcoming in 3.23.44-3.23.46
@@ -1631,8 +1661,8 @@ mysql_execute_command(THD *thd)
cursor)))
{
if (res < 0 || thd->net.report_error)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
- DBUG_VOID_RETURN;
+ send_error(thd,thd->killed_errno());
+ DBUG_RETURN(res);
}
}
}
@@ -1645,7 +1675,7 @@ mysql_execute_command(THD *thd)
!tables_ok(thd,tables))
#endif
)
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
statistic_increment(com_stat[lex->sql_command],&LOCK_status);
switch (lex->sql_command) {
@@ -1684,7 +1714,7 @@ mysql_execute_command(THD *thd)
if (!(result= new select_send()))
{
send_error(thd, ER_OUT_OF_RESOURCES);
- DBUG_VOID_RETURN;
+ goto error;
}
else
thd->send_explain_fields(result);
@@ -1947,7 +1977,7 @@ mysql_execute_command(THD *thd)
find_real_table_in_list(tables->next, tables->db, tables->real_name))
{
net_printf(thd,ER_UPDATE_TABLE_USED,tables->real_name);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(-1);
}
if (tables->next)
{
@@ -2029,7 +2059,7 @@ mysql_execute_command(THD *thd)
if (thd->locked_tables || thd->active_transaction())
{
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
- break;
+ goto error;
}
{
LOCK_ACTIVE_MI;
@@ -2042,7 +2072,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_ALTER_TABLE:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- break;
+ goto error;
#else
{
ulong priv=0;
@@ -2134,7 +2164,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (check_global_access(thd, SUPER_ACL))
@@ -2147,7 +2177,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_CREATE:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (check_db_used(thd, tables) ||
@@ -2225,7 +2255,7 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
- DBUG_VOID_RETURN;
+ goto error;
}
res= mysql_update(thd,tables,
select_lex->item_list,
@@ -2246,7 +2276,7 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
- DBUG_VOID_RETURN;
+ goto error;
}
{
const char *msg= 0;
@@ -2282,7 +2312,7 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
- DBUG_VOID_RETURN;
+ goto error;
}
res = mysql_insert(thd,tables,lex->field_list,lex->many_values,
select_lex->item_list, lex->value_list,
@@ -2323,8 +2353,7 @@ mysql_execute_command(THD *thd)
if (find_real_table_in_list(tables->next, tables->db, tables->real_name))
{
- net_printf(thd,ER_UPDATE_TABLE_USED,tables->real_name);
- DBUG_VOID_RETURN;
+ lex->select_lex.options |= OPTION_BUFFER_RESULT;
}
/* Skip first table, which is the table we are inserting in */
@@ -2499,7 +2528,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_DATABASES:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
check_global_access(thd, SHOW_DB_ACL))
@@ -2533,7 +2562,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_LOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (grant_option && check_access(thd, FILE_ACL, any_db))
@@ -2546,7 +2575,7 @@ mysql_execute_command(THD *thd)
/* FALL THROUGH */
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
char *db=select_lex->db ? select_lex->db : thd->db;
@@ -2585,7 +2614,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_FIELDS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
char *db=tables->db;
@@ -2610,7 +2639,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_KEYS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
char *db=tables->db;
@@ -2792,26 +2821,25 @@ mysql_execute_command(THD *thd)
res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
break;
}
- case SQLCOM_CREATE_FUNCTION:
- if (check_access(thd,INSERT_ACL,"mysql",0,1))
- break;
+ case SQLCOM_CREATE_FUNCTION: // UDF function
+ {
+ if (check_access(thd,INSERT_ACL,"mysql",0,1))
+ break;
#ifdef HAVE_DLOPEN
- if (!(res = mysql_create_function(thd,&lex->udf)))
- send_ok(thd);
+ sp_head *sph= sp_find_function(thd, &lex->udf.name);
+ if (sph)
+ {
+ sph->destroy(); // QQ Free memory. Remove this when caching!!!
+ net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str);
+ goto error;
+ }
+ if (!(res = mysql_create_function(thd,&lex->udf)))
+ send_ok(thd);
#else
- res= -1;
+ res= -1;
#endif
- break;
- case SQLCOM_DROP_FUNCTION:
- if (check_access(thd,DELETE_ACL,"mysql",0,1))
break;
-#ifdef HAVE_DLOPEN
- if (!(res = mysql_drop_function(thd,&lex->udf.name)))
- send_ok(thd);
-#else
- res= -1;
-#endif
- break;
+ }
case SQLCOM_REVOKE:
case SQLCOM_GRANT:
{
@@ -2853,14 +2881,11 @@ mysql_execute_command(THD *thd)
goto error;
if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
lex->grant,
- lex->sql_command == SQLCOM_REVOKE)))
+ lex->sql_command == SQLCOM_REVOKE)) &&
+ mysql_bin_log.is_open())
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
- mysql_bin_log.write(&qinfo);
- }
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ mysql_bin_log.write(&qinfo);
}
}
else
@@ -2875,7 +2900,6 @@ mysql_execute_command(THD *thd)
lex->sql_command == SQLCOM_REVOKE);
if (!res)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2900,7 +2924,7 @@ mysql_execute_command(THD *thd)
reload_acl_and_cache(thd, lex->type, tables);
break;
case SQLCOM_KILL:
- kill_one_thread(thd,lex->thread_id);
+ kill_one_thread(thd,lex->thread_id, lex->type & ONLY_KILL_QUERY);
break;
case SQLCOM_SHOW_GRANTS:
res=0;
@@ -2980,16 +3004,200 @@ mysql_execute_command(THD *thd)
res= -1;
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
break;
+ case SQLCOM_CREATE_PROCEDURE:
+ case SQLCOM_CREATE_SPFUNCTION:
+ if (!lex->sphead)
+ {
+ res= -1; // Shouldn't happen
+ break;
+ }
+ else
+ {
+ uint namelen;
+ char *name= lex->sphead->name(&namelen);
+#ifdef HAVE_DLOPEN
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
+ {
+ udf_func *udf = find_udf(name, namelen);
+
+ if (udf)
+ {
+ net_printf(thd, ER_UDF_EXISTS, name);
+ goto error;
+ }
+ }
+#endif
+ res= lex->sphead->create(thd);
+
+ lex->sphead->destroy(); // QQ Free memory. Remove this when caching!!!
+
+ switch (res)
+ {
+ case SP_OK:
+ send_ok(thd);
+ break;
+ case SP_WRITE_ROW_FAILED:
+ net_printf(thd, ER_SP_ALREADY_EXISTS, SP_TYPE_STRING(lex), name);
+ goto error;
+ default:
+ net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name);
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_CALL:
+ {
+ sp_head *sp;
+
+ sp= sp_find_procedure(thd, &lex->udf.name);
+ if (! sp)
+ {
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, "PROCEDURE", lex->udf.name);
+ goto error;
+ }
+ else
+ {
+ uint smrx;
+ LINT_INIT(smrx);
+
+ // In case the arguments are subselects...
+ if (tables && ((res= check_table_access(thd, SELECT_ACL, tables)) ||
+ (res= open_and_lock_tables(thd, tables))))
+ {
+ sp->destroy(); // QQ Free memory. Remove this when caching!!!
+ break;
+ }
+ fix_tables_pointers(lex->all_selects_list);
+
+#ifndef EMBEDDED_LIBRARY
+ // When executing substatements, they're assumed to send_error when
+ // it happens, but not to send_ok.
+ my_bool nsok= thd->net.no_send_ok;
+ thd->net.no_send_ok= TRUE;
+#endif
+ if (sp->m_multi_query)
+ {
+ if (! (thd->client_capabilities & CLIENT_MULTI_QUERIES))
+ {
+ send_error(thd, ER_SP_BADSELECT);
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ sp->destroy(); // QQ Free memory. Remove this when caching!!!
+ goto error;
+ }
+ smrx= thd->server_status & SERVER_MORE_RESULTS_EXISTS;
+ thd->server_status |= SERVER_MORE_RESULTS_EXISTS;
+ }
+
+ res= sp->execute_procedure(thd, &lex->value_list);
+
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ if (sp->m_multi_query)
+ {
+ if (! smrx)
+ thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ }
+
+ sp->destroy(); // QQ Free memory. Remove this when caching!!!
+
+ if (res == 0)
+ send_ok(thd);
+ else
+ goto error; // Substatement should already have sent error
+ }
+ break;
+ }
+ case SQLCOM_ALTER_PROCEDURE:
+ case SQLCOM_ALTER_FUNCTION:
+ {
+ sp_head *sp;
+
+ if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
+ sp= sp_find_procedure(thd, &lex->udf.name);
+ else
+ sp= sp_find_function(thd, &lex->udf.name);
+ if (! sp)
+ {
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),lex->udf.name);
+ goto error;
+ }
+ else
+ {
+ /* QQ This is an no-op right now, since we haven't
+ put the characteristics in yet. */
+ sp->destroy(); // QQ Free memory. Remove this when caching!!!
+ send_ok(thd);
+ }
+ break;
+ }
+ case SQLCOM_DROP_PROCEDURE:
+ case SQLCOM_DROP_FUNCTION:
+ {
+ if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
+ res= sp_drop_procedure(thd, lex->udf.name.str, lex->udf.name.length);
+ else
+ {
+ res= sp_drop_function(thd, lex->udf.name.str, lex->udf.name.length);
+#ifdef HAVE_DLOPEN
+ if (res == SP_KEY_NOT_FOUND)
+ {
+ udf_func *udf = find_udf(lex->udf.name.str, lex->udf.name.length);
+ if (udf)
+ {
+ if (check_access(thd, DELETE_ACL, "mysql", 0, 1))
+ goto error;
+ if (!(res = mysql_drop_function(thd,&lex->udf.name)))
+ {
+ send_ok(thd);
+ break;
+ }
+ }
+ }
+#endif
+ }
+ switch (res)
+ {
+ case SP_OK:
+ send_ok(thd);
+ break;
+ case SP_KEY_NOT_FOUND:
+ if (lex->drop_if_exists)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
+ SP_COM_STRING(lex), lex->udf.name.str);
+ res= 0;
+ send_ok(thd);
+ break;
+ }
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),
+ lex->udf.name.str);
+ goto error;
+ default:
+ net_printf(thd, ER_SP_DROP_FAILED, SP_COM_STRING(lex),
+ lex->udf.name.str);
+ goto error;
+ }
+ break;
+ }
default: /* Impossible */
send_ok(thd);
break;
}
thd->proc_info="query end"; // QQ
+
+ // We end up here if res == 0 and send_ok() has been done,
+ // or res != 0 and no send_error() has yet been done.
if (res < 0)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
+ send_error(thd,thd->killed_errno());
+ DBUG_RETURN(res);
error:
- DBUG_VOID_RETURN;
+ // We end up here if send_error() has already been done.
+ DBUG_RETURN(-1);
}
@@ -3973,7 +4181,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables)
if (options & REFRESH_LOG)
{
mysql_log.new_file(1);
- mysql_update_log.new_file(1);
mysql_bin_log.new_file(1);
#ifdef HAVE_REPLICATION
if (expire_logs_days)
@@ -4072,7 +4279,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables)
This is written such that we have a short lock on LOCK_thread_count
*/
-void kill_one_thread(THD *thd, ulong id)
+void kill_one_thread(THD *thd, ulong id, bool only_kill_query)
{
THD *tmp;
uint error=ER_NO_SUCH_THREAD;
@@ -4092,7 +4299,7 @@ void kill_one_thread(THD *thd, ulong id)
if ((thd->master_access & SUPER_ACL) ||
!strcmp(thd->user,tmp->user))
{
- tmp->awake(1 /*prepare to die*/);
+ tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
error=0;
}
else