summaryrefslogtreecommitdiff
path: root/sql/sql_parse.cc
diff options
context:
space:
mode:
authorSergei Golubchik <sergii@pisem.net>2011-11-22 18:51:33 +0100
committerSergei Golubchik <sergii@pisem.net>2011-11-22 18:51:33 +0100
commitfeba65a55495ce5cd9f5d8e6e0af8345485b52a1 (patch)
tree092f039bd213fbd1580a89f003cfd5d5690f5a86 /sql/sql_parse.cc
parentc104d31404c68b5622dc676d53308d02349755d1 (diff)
parentb81d8b2e5ab7baa9623d0af002083057cda28a97 (diff)
downloadmariadb-git-feba65a55495ce5cd9f5d8e6e0af8345485b52a1.tar.gz
merged
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r--sql/sql_parse.cc212
1 files changed, 188 insertions, 24 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index ad5aa281ea7..714542ab0af 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -118,7 +118,8 @@
"FUNCTION" : "PROCEDURE")
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
-static void sql_kill(THD *thd, ulong id, bool only_kill_query);
+static void sql_kill(THD *thd, ulong id, killed_state state);
+static void sql_kill_user(THD *thd, LEX_USER *user, killed_state state);
static bool execute_show_status(THD *, TABLE_LIST *);
static bool execute_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
@@ -1325,7 +1326,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
length= my_snprintf(buff, buff_len - 1,
"Uptime: %lu Threads: %d Questions: %lu "
"Slow queries: %lu Opens: %lu Flush tables: %lu "
- "Open tables: %u Queries per second avg: %u.%u",
+ "Open tables: %u Queries per second avg: %u.%03u",
uptime,
(int) thread_count, (ulong) thd->query_id,
current_global_status_var->long_query_count,
@@ -1362,7 +1363,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
status_var_increment(thd->status_var.com_stat[SQLCOM_KILL]);
ulong id=(ulong) uint4korr(packet);
- sql_kill(thd,id,false);
+ sql_kill(thd,id, KILL_CONNECTION_HARD);
break;
}
case COM_SET_OPTION:
@@ -1443,6 +1444,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
MYSQL_COMMAND_DONE(res);
}
+
+ /* Check that some variables are reset properly */
+ DBUG_ASSERT(thd->abort_on_warning == 0);
DBUG_RETURN(error);
}
@@ -2066,6 +2070,11 @@ mysql_execute_command(THD *thd)
*/
if (stmt_causes_implicit_commit(thd, CF_IMPLICT_COMMIT_BEGIN))
{
+ /*
+ Note that this should never happen inside of stored functions
+ or triggers as all such statements prohibited there.
+ */
+ DBUG_ASSERT(! thd->in_sub_stmt);
/* Commit or rollback the statement transaction. */
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
/* Commit the normal transaction if one is active. */
@@ -2366,6 +2375,12 @@ case SQLCOM_PREPARE:
goto end_with_restore_list;
#endif
/*
+ If no engine type was given, work out the default now
+ rather than at parse-time.
+ */
+ if (!(create_info.used_fields & HA_CREATE_USED_ENGINE))
+ create_info.db_type= ha_default_handlerton(thd);
+ /*
If we are using SET CHARSET without DEFAULT, add an implicit
DEFAULT to not confuse old users. (This may change).
*/
@@ -2399,6 +2414,19 @@ case SQLCOM_PREPARE:
select_result *result;
/*
+ CREATE TABLE...IGNORE/REPLACE SELECT... can be unsafe, unless
+ ORDER BY PRIMARY KEY clause is used in SELECT statement. We therefore
+ use row based logging if mixed or row based logging is available.
+ TODO: Check if the order of the output of the select statement is
+ deterministic. Waiting for BUG#42415
+ */
+ if(lex->ignore)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_IGNORE_SELECT);
+
+ if(lex->duplicates == DUP_REPLACE)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_REPLACE_SELECT);
+
+ /*
If:
a) we inside an SP and there was NAME_CONST substitution,
b) binlogging is on (STMT mode),
@@ -2705,6 +2733,16 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (update_precheck(thd, all_tables))
break;
+
+ /*
+ UPDATE IGNORE can be unsafe. We therefore use row based
+ logging if mixed or row based logging is available.
+ TODO: Check if the order of the output of the select statement is
+ deterministic. Waiting for BUG#42415
+ */
+ if (lex->ignore)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_UPDATE_IGNORE);
+
DBUG_ASSERT(select_lex->offset_limit == 0);
unit->set_limit(select_lex);
MYSQL_UPDATE_START(thd->query());
@@ -2876,6 +2914,23 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if ((res= insert_precheck(thd, all_tables)))
break;
+ /*
+ INSERT...SELECT...ON DUPLICATE KEY UPDATE/REPLACE SELECT/
+ INSERT...IGNORE...SELECT can be unsafe, unless ORDER BY PRIMARY KEY
+ clause is used in SELECT statement. We therefore use row based
+ logging if mixed or row based logging is available.
+ TODO: Check if the order of the output of the select statement is
+ deterministic. Waiting for BUG#42415
+ */
+ if (lex->sql_command == SQLCOM_INSERT_SELECT &&
+ lex->duplicates == DUP_UPDATE)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_SELECT_UPDATE);
+
+ if (lex->sql_command == SQLCOM_INSERT_SELECT && lex->ignore)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_IGNORE_SELECT);
+
+ if (lex->sql_command == SQLCOM_REPLACE_SELECT)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_REPLACE_SELECT);
/* Fix lock for first table */
if (first_table->lock_type == TL_WRITE_DELAYED)
@@ -3602,8 +3657,6 @@ end_with_restore_list:
}
case SQLCOM_KILL:
{
- Item *it= (Item *)lex->value_list.head();
-
if (lex->table_or_sp_used())
{
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
@@ -3611,13 +3664,20 @@ end_with_restore_list:
break;
}
- if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
+ if (lex->kill_type == KILL_TYPE_ID)
{
- my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
- MYF(0));
- goto error;
+ Item *it= (Item *)lex->value_list.head();
+ if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
+ {
+ my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
+ MYF(0));
+ goto error;
+ }
+ sql_kill(thd, (ulong) it->val_int(), lex->kill_signal);
}
- sql_kill(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY);
+ else
+ sql_kill_user(thd, get_current_user(thd, lex->users_list.head()),
+ lex->kill_signal);
break;
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -3689,7 +3749,17 @@ end_with_restore_list:
}
/* Disconnect the current client connection. */
if (tx_release)
- thd->killed= THD::KILL_CONNECTION;
+ {
+ thd->killed= KILL_CONNECTION;
+ if (global_system_variables.log_warnings > 3)
+ {
+ Security_context *sctx= &thd->main_security_ctx;
+ sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
+ thd->thread_id,(thd->db ? thd->db : "unconnected"),
+ sctx->user ? sctx->user : "unauthenticated",
+ sctx->host_or_ip, "RELEASE");
+ }
+ }
my_ok(thd);
break;
}
@@ -3719,7 +3789,7 @@ end_with_restore_list:
}
/* Disconnect the current client connection. */
if (tx_release)
- thd->killed= THD::KILL_CONNECTION;
+ thd->killed= KILL_CONNECTION;
my_ok(thd);
break;
}
@@ -3984,7 +4054,8 @@ create_sp_error:
case SQLCOM_ALTER_FUNCTION:
{
int sp_result;
- int type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ?
+ enum stored_procedure_type type;
+ type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ?
TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str,
@@ -4061,7 +4132,8 @@ create_sp_error:
#endif
int sp_result;
- int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
+ enum stored_procedure_type type;
+ type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
char *db= lex->spname->m_db.str;
char *name= lex->spname->m_name.str;
@@ -4148,7 +4220,7 @@ create_sp_error:
{
#ifndef DBUG_OFF
sp_head *sp;
- int type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ?
+ stored_procedure_type type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ?
TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
if (sp_cache_routine(thd, type, lex->spname, FALSE, &sp))
@@ -4394,9 +4466,9 @@ finish:
if (! thd->stmt_da->is_set())
thd->send_kill_message();
}
- if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
+ if (thd->killed < KILL_CONNECTION)
{
- thd->killed= THD::NOT_KILLED;
+ thd->killed= NOT_KILLED;
thd->mysys_var->abort= 0;
}
if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
@@ -4467,7 +4539,6 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
param->select_limit=
new Item_int((ulonglong) thd->variables.select_limit);
}
- thd->thd_marker.emb_on_expr_nest= NULL;
if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
{
if (lex->describe)
@@ -5367,7 +5438,6 @@ void THD::reset_for_next_command(bool calculate_userstat)
thd->query_plan_flags= QPLAN_INIT;
thd->query_plan_fsort_passes= 0;
- thd->thd_marker.emb_on_expr_nest= NULL;
thd->reset_current_stmt_binlog_format_row();
thd->binlog_unsafe_warning_flags= 0;
@@ -5434,6 +5504,7 @@ mysql_new_select(LEX *lex, bool move_down)
DBUG_RETURN(1);
}
select_lex->nest_level= lex->nest_level;
+ select_lex->nest_level_base= &thd->lex->unit;
if (move_down)
{
SELECT_LEX_UNIT *unit;
@@ -6457,12 +6528,13 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
This is written such that we have a short lock on LOCK_thread_count
*/
-uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
+uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal)
{
THD *tmp;
uint error=ER_NO_SUCH_THREAD;
DBUG_ENTER("kill_one_thread");
- DBUG_PRINT("enter", ("id=%lu only_kill=%d", id, only_kill_query));
+ DBUG_PRINT("enter", ("id: %lu signal: %u", id, (uint) kill_signal));
+
mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
I_List_iterator<THD> it(threads);
while ((tmp=it++))
@@ -6493,12 +6565,16 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
If user of both killer and killee are non-NULL, proceed with
slayage if both are string-equal.
+
+ It's ok to also kill DELAYED threads with KILL_CONNECTION instead of
+ KILL_SYSTEM_THREAD; The difference is that KILL_CONNECTION may be
+ faster and do a harder kill than KILL_SYSTEM_THREAD;
*/
if ((thd->security_ctx->master_access & SUPER_ACL) ||
thd->security_ctx->user_matches(tmp->security_ctx))
{
- tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
+ tmp->awake(kill_signal);
error=0;
}
else
@@ -6510,6 +6586,76 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
}
+/**
+ kill all threads from one user
+
+ @param thd Thread class
+ @param user_name User name for threads we should kill
+ @param only_kill_query Should it kill the query or the connection
+
+ @note
+ This is written such that we have a short lock on LOCK_thread_count
+
+ If we can't kill all threads because of security issues, no threads
+ are killed.
+*/
+
+static uint kill_threads_for_user(THD *thd, LEX_USER *user,
+ killed_state kill_signal, ha_rows *rows)
+{
+ THD *tmp;
+ List<THD> threads_to_kill;
+ DBUG_ENTER("kill_threads_for_user");
+
+ *rows= 0;
+
+ if (thd->is_fatal_error) // If we run out of memory
+ DBUG_RETURN(ER_OUT_OF_RESOURCES);
+
+ DBUG_PRINT("enter", ("user: %s signal: %u", user->user.str,
+ (uint) kill_signal));
+
+ mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
+ I_List_iterator<THD> it(threads);
+ while ((tmp=it++))
+ {
+ if (tmp->command == COM_DAEMON)
+ continue;
+ /*
+ Check that hostname (if given) and user name matches.
+
+ host.str[0] == '%' means that host name was not given. See sql_yacc.yy
+ */
+ if (((user->host.str[0] == '%' && !user->host.str[1]) ||
+ !strcmp(tmp->security_ctx->host, user->host.str)) &&
+ !strcmp(tmp->security_ctx->user, user->user.str))
+ {
+ if (!(thd->security_ctx->master_access & SUPER_ACL) &&
+ !thd->security_ctx->user_matches(tmp->security_ctx))
+ {
+ mysql_mutex_unlock(&LOCK_thread_count);
+ DBUG_RETURN(ER_KILL_DENIED_ERROR);
+ }
+ if (!threads_to_kill.push_back(tmp, tmp->mem_root))
+ mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
+ }
+ }
+ mysql_mutex_unlock(&LOCK_thread_count);
+ if (!threads_to_kill.is_empty())
+ {
+ List_iterator_fast<THD> it(threads_to_kill);
+ THD *ptr;
+ while ((ptr= it++))
+ {
+ ptr->awake(kill_signal);
+ mysql_mutex_unlock(&ptr->LOCK_thd_data);
+ (*rows)++;
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
/*
kills a thread and sends response
@@ -6521,10 +6667,10 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
*/
static
-void sql_kill(THD *thd, ulong id, bool only_kill_query)
+void sql_kill(THD *thd, ulong id, killed_state state)
{
uint error;
- if (!(error= kill_one_thread(thd, id, only_kill_query)))
+ if (!(error= kill_one_thread(thd, id, state)))
{
if (! thd->killed)
my_ok(thd);
@@ -6534,6 +6680,24 @@ void sql_kill(THD *thd, ulong id, bool only_kill_query)
}
+static
+void sql_kill_user(THD *thd, LEX_USER *user, killed_state state)
+{
+ uint error;
+ ha_rows rows;
+ if (!(error= kill_threads_for_user(thd, user, state, &rows)))
+ my_ok(thd, rows);
+ else
+ {
+ /*
+ This is probably ER_OUT_OF_RESOURCES, but in the future we may
+ want to write the name of the user we tried to kill
+ */
+ my_error(error, MYF(0), user->host.str, user->user.str);
+ }
+}
+
+
/** If pointer is not a null pointer, append filename to it. */
bool append_file_to_dir(THD *thd, const char **filename_ptr,