Merge from 5.0

******
manual merge 5.0-bugteam --> 5.1-bugteam (bug 38816)
This commit is contained in:
Gleb Shchepa 2009-07-24 21:04:55 +05:00
commit 2bc6b6a800
13 changed files with 110 additions and 119 deletions

View file

@ -1430,13 +1430,7 @@ Event_job_data::execute(THD *thd, bool drop)
thd->variables.sql_mode= sql_mode;
thd->variables.time_zone= time_zone;
/*
Peculiar initialization order is a crutch to avoid races in SHOW
PROCESSLIST which reads thd->{query/query_length} without a mutex.
*/
thd->query_length= 0;
thd->query= sp_sql.c_ptr_safe();
thd->query_length= sp_sql.length();
thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length());
{
Parser_state parser_state(thd, thd->query, thd->query_length);
@ -1497,13 +1491,8 @@ end_no_lex_start:
else
{
ulong saved_master_access;
/*
Peculiar initialization order is a crutch to avoid races in SHOW
PROCESSLIST which reads thd->{query/query_length} without a mutex.
*/
thd->query_length= 0;
thd->query= sp_sql.c_ptr_safe();
thd->query_length= sp_sql.length();
thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length());
/*
NOTE: even if we run in read-only mode, we should be able to lock
@ -1528,8 +1517,7 @@ end_no_lex_start:
thd->end_statement();
thd->cleanup_after_query();
/* Avoid races with SHOW PROCESSLIST */
thd->query_length= 0;
thd->query= NULL;
thd->set_query(NULL, 0);
DBUG_PRINT("info", ("EXECUTED %s.%s ret: %d", dbname.str, name.str, ret));

View file

@ -625,13 +625,13 @@ Event_scheduler::stop()
DBUG_PRINT("info", ("Scheduler thread has id %lu",
scheduler_thd->thread_id));
/* Lock from delete */
pthread_mutex_lock(&scheduler_thd->LOCK_delete);
pthread_mutex_lock(&scheduler_thd->LOCK_thd_data);
/* This will wake up the thread if it waits on Queue's conditional */
sql_print_information("Event Scheduler: Killing the scheduler thread, "
"thread id %lu",
scheduler_thd->thread_id);
scheduler_thd->awake(THD::KILL_CONNECTION);
pthread_mutex_unlock(&scheduler_thd->LOCK_delete);
pthread_mutex_unlock(&scheduler_thd->LOCK_thd_data);
/* thd could be 0x0, when shutting down */
sql_print_information("Event Scheduler: "

View file

@ -253,8 +253,7 @@ static void run_query(THD *thd, char *buf, char *end,
const char* found_semicolon= NULL;
bzero((char*) &thd->net, sizeof(NET));
thd->query_length= end - buf;
thd->query= buf;
thd->set_query(buf, (uint) (end - buf));
thd->variables.pseudo_thread_id= thread_id;
thd->transaction.stmt.modified_non_trans_table= FALSE;
if (disable_binlog)
@ -297,8 +296,7 @@ static void run_query(THD *thd, char *buf, char *end,
thd->main_da.reset_diagnostics_area();
thd->options= save_thd_options;
thd->query_length= save_thd_query_length;
thd->query= save_thd_query;
thd->set_query(save_thd_query, save_thd_query_length);
thd->variables.pseudo_thread_id= save_thread_id;
thd->status_var= save_thd_status_var;
thd->transaction.all= save_thd_transaction_all;

View file

@ -3025,8 +3025,7 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
rpl_filter->db_ok(thd->db))
{
thd->set_time((time_t)when);
thd->query_length= q_len_arg;
thd->query= (char*)query_arg;
thd->set_query((char*)query_arg, q_len_arg);
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->query_id = next_query_id();
VOID(pthread_mutex_unlock(&LOCK_thread_count));
@ -3231,7 +3230,6 @@ Default database: '%s'. Query: '%s'",
} /* End of if (db_ok(... */
end:
VOID(pthread_mutex_lock(&LOCK_thread_count));
/*
Probably we have set thd->query, thd->db, thd->catalog to point to places
in the data_buf of this event. Now the event is going to be deleted
@ -3244,10 +3242,8 @@ end:
*/
thd->catalog= 0;
thd->set_db(NULL, 0); /* will free the current database */
thd->set_query(NULL, 0);
DBUG_PRINT("info", ("end: query= 0"));
thd->query= 0; // just to be sure
thd->query_length= 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
close_thread_tables(thd);
/*
As a disk space optimization, future masters will not log an event for
@ -4557,8 +4553,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
print_query(FALSE, load_data_query, &end, (char **)&thd->lex->fname_start,
(char **)&thd->lex->fname_end);
*end= 0;
thd->query_length= end - load_data_query;
thd->query= load_data_query;
thd->set_query(load_data_query, (uint) (end - load_data_query));
if (sql_ex.opt_flags & REPLACE_FLAG)
{
@ -4664,12 +4659,9 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
error:
thd->net.vio = 0;
const char *remember_db= thd->db;
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->catalog= 0;
thd->set_db(NULL, 0); /* will free the current database */
thd->query= 0;
thd->query_length= 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->set_query(NULL, 0);
close_thread_tables(thd);
DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error",

View file

@ -512,7 +512,7 @@ terminate_slave_thread(THD *thd,
int error;
DBUG_PRINT("loop", ("killing slave thread"));
pthread_mutex_lock(&thd->LOCK_delete);
pthread_mutex_lock(&thd->LOCK_thd_data);
#ifndef DONT_USE_THR_ALARM
/*
Error codes from pthread_kill are:
@ -523,7 +523,7 @@ terminate_slave_thread(THD *thd,
DBUG_ASSERT(err != EINVAL);
#endif
thd->awake(THD::NOT_KILLED);
pthread_mutex_unlock(&thd->LOCK_delete);
pthread_mutex_unlock(&thd->LOCK_thd_data);
/*
There is a small chance that slave thread might miss the first
@ -1250,15 +1250,13 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
DBUG_RETURN(1);
}
thd->command = COM_TABLE_DUMP;
thd->query_length= packet_len;
/* Note that we should not set thd->query until the area is initalized */
if (!(query = thd->strmake((char*) net->read_pos, packet_len)))
{
sql_print_error("create_table_from_dump: out of memory");
my_message(ER_GET_ERRNO, "Out of memory", MYF(0));
DBUG_RETURN(1);
}
thd->query= query;
thd->set_query(query, packet_len);
thd->is_slave_error = 0;
bzero((char*) &tables,sizeof(tables));
@ -2755,10 +2753,8 @@ err:
// print the current replication position
sql_print_information("Slave I/O thread exiting, read up to log '%s', position %s",
IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff));
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->query = thd->db = 0; // extra safety
thd->query_length= thd->db_length= 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->set_query(NULL, 0);
thd->reset_db(NULL, 0);
if (mysql)
{
/*
@ -3110,15 +3106,14 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
must "proactively" clear playgrounds:
*/
rli->cleanup_context(thd, 1);
VOID(pthread_mutex_lock(&LOCK_thread_count));
/*
Some extra safety, which should not been needed (normally, event deletion
should already have done these assignments (each event which sets these
variables is supposed to set them to 0 before terminating)).
*/
thd->query= thd->db= thd->catalog= 0;
thd->query_length= thd->db_length= 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->catalog= 0;
thd->set_query(NULL, 0);
thd->reset_db(NULL, 0);
thd_proc_info(thd, "Waiting for slave mutex on exit");
pthread_mutex_lock(&rli->run_lock);
/* We need data_lock, at least to wake up any waiting master_pos_wait() */

View file

@ -1012,8 +1012,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
else
DBUG_RETURN(TRUE);
thd->query= pbuf;
thd->query_length= qbuf.length();
thd->set_query(pbuf, qbuf.length());
DBUG_RETURN(FALSE);
}
@ -2858,8 +2857,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
}
else
*nextp= m_ip+1;
thd->query= query;
thd->query_length= query_length;
thd->set_query(query, query_length);
thd->query_name_consts= 0;
if (!thd->is_error())

View file

@ -627,7 +627,7 @@ THD::THD()
#ifdef SIGNAL_WITH_VIO_CLOSE
active_vio = 0;
#endif
pthread_mutex_init(&LOCK_delete, MY_MUTEX_INIT_FAST);
pthread_mutex_init(&LOCK_thd_data, MY_MUTEX_INIT_FAST);
/* Variables with default values */
proc_info="login";
@ -911,8 +911,8 @@ THD::~THD()
THD_CHECK_SENTRY(this);
DBUG_ENTER("~THD()");
/* Ensure that no one is using THD */
pthread_mutex_lock(&LOCK_delete);
pthread_mutex_unlock(&LOCK_delete);
pthread_mutex_lock(&LOCK_thd_data);
pthread_mutex_unlock(&LOCK_thd_data);
add_to_status(&global_status_var, &status_var);
/* Close connection */
@ -939,7 +939,7 @@ THD::~THD()
free_root(&transaction.mem_root,MYF(0));
#endif
mysys_var=0; // Safety (shouldn't be needed)
pthread_mutex_destroy(&LOCK_delete);
pthread_mutex_destroy(&LOCK_thd_data);
#ifndef DBUG_OFF
dbug_sentry= THD_SENTRY_GONE;
#endif
@ -1012,7 +1012,7 @@ void THD::awake(THD::killed_state state_to_set)
DBUG_ENTER("THD::awake");
DBUG_PRINT("enter", ("this: 0x%lx", (long) this));
THD_CHECK_SENTRY(this);
safe_mutex_assert_owner(&LOCK_delete);
safe_mutex_assert_owner(&LOCK_thd_data);
killed= state_to_set;
if (state_to_set != THD::KILL_QUERY)
@ -1409,7 +1409,7 @@ int THD::send_explain_fields(select_result *result)
void THD::close_active_vio()
{
DBUG_ENTER("close_active_vio");
safe_mutex_assert_owner(&LOCK_delete);
safe_mutex_assert_owner(&LOCK_thd_data);
#ifndef EMBEDDED_LIBRARY
if (active_vio)
{
@ -3055,6 +3055,25 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
}
void THD::set_statement(Statement *stmt)
{
pthread_mutex_lock(&LOCK_thd_data);
Statement::set_statement(stmt);
pthread_mutex_unlock(&LOCK_thd_data);
}
/** Assign a new value to thd->query. */
void THD::set_query(char *query_arg, uint32 query_length_arg)
{
pthread_mutex_lock(&LOCK_thd_data);
query= query_arg;
query_length= query_length_arg;
pthread_mutex_unlock(&LOCK_thd_data);
}
/**
Mark transaction to rollback and mark error as fatal to a sub-statement.

View file

@ -631,22 +631,16 @@ public:
we need to declare it char * because all table handlers are written
in C and need to point to it.
Note that (A) if we set query = NULL, we must at the same time set
query_length = 0, and protect the whole operation with the
LOCK_thread_count mutex. And (B) we are ONLY allowed to set query to a
non-NULL value if its previous value is NULL. We do not need to protect
operation (B) with any mutex. To avoid crashes in races, if we do not
know that thd->query cannot change at the moment, one should print
Note that if we set query = NULL, we must at the same time set
query_length = 0, and protect the whole operation with
LOCK_thd_data mutex. To avoid crashes in races, if we do not
know that thd->query cannot change at the moment, we should print
thd->query like this:
(1) reserve the LOCK_thread_count mutex;
(2) check if thd->query is NULL;
(3) if not NULL, then print at most thd->query_length characters from
it. We will see the query_length field as either 0, or the right value
for it.
Assuming that the write and read of an n-bit memory field in an n-bit
computer is atomic, we can avoid races in the above way.
This printing is needed at least in SHOW PROCESSLIST and SHOW INNODB
STATUS.
(1) reserve the LOCK_thd_data mutex;
(2) print or copy the value of query and query_length
(3) release LOCK_thd_data mutex.
This printing is needed at least in SHOW PROCESSLIST and SHOW
ENGINE INNODB STATUS.
*/
char *query;
uint32 query_length; // current query length
@ -678,7 +672,7 @@ public:
virtual ~Statement();
/* Assign execution context (note: not all members) of given stmt to self */
void set_statement(Statement *stmt);
virtual void set_statement(Statement *stmt);
void set_n_backup_statement(Statement *stmt, Statement *backup);
void restore_backup_statement(Statement *stmt, Statement *backup);
/* return class type */
@ -1298,7 +1292,15 @@ public:
THR_LOCK_OWNER main_lock_id; // To use for conventional queries
THR_LOCK_OWNER *lock_id; // If not main_lock_id, points to
// the lock_id of a cursor.
pthread_mutex_t LOCK_delete; // Locked before thd is deleted
/**
Protects THD data accessed from other threads:
- thd->query and thd->query_length (used by SHOW ENGINE
INNODB STATUS and SHOW PROCESSLIST
- thd->mysys_var (used by KILL statement and shutdown).
Is locked when THD is deleted.
*/
pthread_mutex_t LOCK_thd_data;
/* all prepared statements and cursors of this connection */
Statement_map stmt_map;
/*
@ -1890,15 +1892,15 @@ public:
#ifdef SIGNAL_WITH_VIO_CLOSE
inline void set_active_vio(Vio* vio)
{
pthread_mutex_lock(&LOCK_delete);
pthread_mutex_lock(&LOCK_thd_data);
active_vio = vio;
pthread_mutex_unlock(&LOCK_delete);
pthread_mutex_unlock(&LOCK_thd_data);
}
inline void clear_active_vio()
{
pthread_mutex_lock(&LOCK_delete);
pthread_mutex_lock(&LOCK_thd_data);
active_vio = 0;
pthread_mutex_unlock(&LOCK_delete);
pthread_mutex_unlock(&LOCK_thd_data);
}
void close_active_vio();
#endif
@ -2271,6 +2273,14 @@ public:
*/
void pop_internal_handler();
/** Overloaded to guard query/query_length fields */
virtual void set_statement(Statement *stmt);
/**
Assign a new value to thd->query.
Protected with LOCK_thd_data mutex.
*/
void set_query(char *query_arg, uint32 query_length_arg);
private:
/** The current internal error handler for this thread, or NULL. */
Internal_error_handler *m_internal_handler;

View file

@ -1909,7 +1909,7 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
thread_count++;
pthread_mutex_unlock(&LOCK_thread_count);
di->thd.set_db(table_list->db, (uint) strlen(table_list->db));
di->thd.query= my_strdup(table_list->table_name, MYF(MY_WME));
di->thd.set_query(my_strdup(table_list->table_name, MYF(MY_WME)), 0);
if (di->thd.db == NULL || di->thd.query == NULL)
{
/* The error is reported */

View file

@ -457,6 +457,7 @@ pthread_handler_t handle_bootstrap(void *arg)
thd->init_for_queries();
while (fgets(buff, thd->net.max_packet, file))
{
char *query;
/* strlen() can't be deleted because fgets() doesn't return length */
ulong length= (ulong) strlen(buff);
while (buff[length-1] != '\n' && !feof(file))
@ -489,11 +490,10 @@ pthread_handler_t handle_bootstrap(void *arg)
if (strncmp(buff, STRING_WITH_LEN("delimiter")) == 0)
continue;
thd->query_length=length;
thd->query= (char*) thd->memdup_w_gap(buff, length+1,
thd->db_length+1+
QUERY_CACHE_FLAGS_SIZE);
thd->query[length] = '\0';
query= (char *) thd->memdup_w_gap(buff, length + 1,
thd->db_length + 1 +
QUERY_CACHE_FLAGS_SIZE);
thd->set_query(query, length);
DBUG_PRINT("query",("%-.4096s",thd->query));
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.start_new_query();
@ -658,8 +658,7 @@ int mysql_table_dump(THD *thd, LEX_STRING *db, char *tbl_name)
if (check_one_table_access(thd, SELECT_ACL, table_list))
goto err;
thd->free_list = 0;
thd->query_length=(uint) strlen(tbl_name);
thd->query = tbl_name;
thd->set_query(tbl_name, (uint) strlen(tbl_name));
if ((error = mysqld_dump_create_info(thd, table_list, -1)))
{
my_error(ER_GET_ERRNO, MYF(0), my_errno);
@ -1239,9 +1238,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->profiling.set_query_source(beginning_of_next_stmt, length);
#endif
thd->set_query(beginning_of_next_stmt, length);
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->query_length= length;
thd->query= beginning_of_next_stmt;
/*
Count each statement from the client.
*/
@ -1294,9 +1292,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
table_list.schema_table= schema_table;
}
thd->query_length= (uint) (packet_end - packet); // Don't count end \0
if (!(thd->query=fields= (char*) thd->memdup(packet,thd->query_length+1)))
uint query_length= (uint) (packet_end - packet); // Don't count end \0
if (!(fields= (char *) thd->memdup(packet, query_length + 1)))
break;
thd->set_query(fields, query_length);
general_log_print(thd, command, "%s %s", table_list.table_name, fields);
if (lower_case_table_names)
my_casedn_str(files_charset_info, table_list.table_name);
@ -1589,13 +1588,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
log_slow_statement(thd);
thd_proc_info(thd, "cleaning up");
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
thd_proc_info(thd, 0);
thd->set_query(NULL, 0);
thd->command=COM_SLEEP;
thd->query=0;
thd->query_length=0;
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
thread_running--;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd_proc_info(thd, 0);
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_RETURN(error);
@ -1788,6 +1786,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
bool alloc_query(THD *thd, const char *packet, uint packet_length)
{
char *query;
/* Remove garbage at start and end of query */
while (packet_length > 0 && my_isspace(thd->charset(), packet[0]))
{
@ -1802,14 +1801,13 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length)
packet_length--;
}
/* We must allocate some extra memory for query cache */
thd->query_length= 0; // Extra safety: Avoid races
if (!(thd->query= (char*) thd->memdup_w_gap((uchar*) (packet),
packet_length,
thd->db_length+ 1 +
QUERY_CACHE_FLAGS_SIZE)))
return TRUE;
thd->query[packet_length]=0;
thd->query_length= packet_length;
if (! (query= (char*) thd->memdup_w_gap(packet,
packet_length,
1 + thd->db_length +
QUERY_CACHE_FLAGS_SIZE)))
return TRUE;
query[packet_length]= '\0';
thd->set_query(query, packet_length);
/* Reclaim some memory */
thd->packet.shrink(thd->variables.net_buffer_length);
@ -6950,7 +6948,7 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
continue;
if (tmp->thread_id == id)
{
pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete
pthread_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
break;
}
}
@ -6983,7 +6981,7 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
}
else
error=ER_KILL_DENIED_ERROR;
pthread_mutex_unlock(&tmp->LOCK_delete);
pthread_mutex_unlock(&tmp->LOCK_thd_data);
}
DBUG_PRINT("exit", ("%d", error));
DBUG_RETURN(error);

View file

@ -1102,7 +1102,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
if (tmp->command == COM_BINLOG_DUMP &&
tmp->server_id == slave_server_id)
{
pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete
pthread_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
break;
}
}
@ -1115,7 +1115,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
again. We just to do kill the thread ourselves.
*/
tmp->awake(THD::KILL_QUERY);
pthread_mutex_unlock(&tmp->LOCK_delete);
pthread_mutex_unlock(&tmp->LOCK_thd_data);
}
}

View file

@ -1768,16 +1768,14 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
thd_info->start_time= tmp->start_time;
thd_info->query=0;
/* Lock THD mutex that protects its data when looking at it. */
pthread_mutex_lock(&tmp->LOCK_thd_data);
if (tmp->query)
{
/*
query_length is always set to 0 when we set query = NULL; see
the comment in sql_class.h why this prevents crashes in possible
races with query_length
*/
uint length= min(max_query_length, tmp->query_length);
thd_info->query=(char*) thd->strmake(tmp->query,length);
}
pthread_mutex_unlock(&tmp->LOCK_thd_data);
thread_infos.append(thd_info);
}
}

View file

@ -1612,10 +1612,8 @@ bool ha_myisam::check_and_repair(THD *thd)
old_query= thd->query;
old_query_length= thd->query_length;
pthread_mutex_lock(&LOCK_thread_count);
thd->query= table->s->table_name.str;
thd->query_length= (uint) table->s->table_name.length;
pthread_mutex_unlock(&LOCK_thread_count);
thd->set_query(table->s->table_name.str,
(uint) table->s->table_name.length);
if ((marked_crashed= mi_is_crashed(file)) || check(thd, &check_opt))
{
@ -1628,10 +1626,7 @@ bool ha_myisam::check_and_repair(THD *thd)
if (repair(thd, &check_opt))
error=1;
}
pthread_mutex_lock(&LOCK_thread_count);
thd->query= old_query;
thd->query_length= old_query_length;
pthread_mutex_unlock(&LOCK_thread_count);
thd->set_query(old_query, old_query_length);
DBUG_RETURN(error);
}