1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

Merge mysqldev@production.mysql.com:my/mysql-5.1-release

into  mysql.com:/home/bk/w3023-mysql-5.1-new
This commit is contained in:
mats@mysql.com
2006-02-24 16:31:38 +01:00
69 changed files with 1248 additions and 990 deletions

View File

@@ -1685,6 +1685,7 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli, const char *query
DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
clear_all_errors(thd, rli);
rli->clear_tables_to_lock();
/*
Note: We do not need to execute reset_one_shot_variables() if this
@@ -5063,17 +5064,19 @@ char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format)
Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
MY_BITMAP const *cols, bool is_transactional)
: Log_event(thd_arg, 0, is_transactional),
m_row_count(0),
m_table(tbl_arg),
m_table_id(tid),
m_width(tbl_arg->s->fields),
m_rows_buf((byte*)my_malloc(opt_binlog_rows_event_max_size *
sizeof(*m_rows_buf), MYF(MY_WME))),
m_rows_cur(m_rows_buf),
m_rows_end(m_rows_buf + opt_binlog_rows_event_max_size),
m_width(tbl_arg ? tbl_arg->s->fields : 1),
m_rows_buf(0), m_rows_cur(0), m_rows_end(0),
m_flags(0)
{
DBUG_ASSERT(m_table && m_table->s);
DBUG_ASSERT(m_table_id != ULONG_MAX);
/*
We allow a special form of dummy event when the table, and cols
are null and the table id is ULONG_MAX.
*/
DBUG_ASSERT(tbl_arg && tbl_arg->s && tid != ULONG_MAX ||
!tbl_arg && !cols && tid == ULONG_MAX);
if (thd_arg->options & OPTION_NO_FOREIGN_KEY_CHECKS)
set_flags(NO_FOREIGN_KEY_CHECKS_F);
@@ -5084,7 +5087,11 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
(m_width + 7) & ~7UL,
false)))
memcpy(m_cols.bitmap, cols->bitmap, no_bytes_in_map(cols));
{
/* Cols can be zero if this is a dummy binrows event */
if (likely(cols != NULL))
memcpy(m_cols.bitmap, cols->bitmap, no_bytes_in_map(cols));
}
else
m_cols.bitmap= 0; // to not free it
}
@@ -5095,6 +5102,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
const Format_description_log_event
*description_event)
: Log_event(buf, description_event),
m_row_count(0),
m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
{
DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
@@ -5120,8 +5128,6 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
post_start+= RW_FLAGS_OFFSET;
}
DBUG_ASSERT(m_table_id != ULONG_MAX);
m_flags= uint2korr(post_start);
byte const *const var_start= (const byte *)buf + common_header_len +
@@ -5178,7 +5184,7 @@ int Rows_log_event::do_add_row_data(byte *const row_data,
DBUG_DUMP("row_data", (const char*)row_data, min(length, 32));
DBUG_ASSERT(m_rows_buf <= m_rows_cur);
DBUG_ASSERT(m_rows_buf < m_rows_end);
DBUG_ASSERT(!m_rows_buf || m_rows_end && m_rows_buf < m_rows_end);
DBUG_ASSERT(m_rows_cur <= m_rows_end);
/* The cast will always work since m_rows_cur <= m_rows_end */
@@ -5186,12 +5192,12 @@ int Rows_log_event::do_add_row_data(byte *const row_data,
{
my_size_t const block_size= 1024;
my_ptrdiff_t const old_alloc= m_rows_end - m_rows_buf;
my_ptrdiff_t const new_alloc=
old_alloc + block_size * (length / block_size + block_size - 1);
my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf;
my_ptrdiff_t const new_alloc=
block_size * ((cur_size + length) / block_size + block_size - 1);
byte* const new_buf=
(byte*)my_realloc((gptr)m_rows_buf, new_alloc, MYF(MY_WME));
byte* const new_buf= (byte*)my_realloc((gptr)m_rows_buf, new_alloc,
MYF(MY_ALLOW_ZERO_PTR|MY_WME));
if (unlikely(!new_buf))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
@@ -5212,6 +5218,7 @@ int Rows_log_event::do_add_row_data(byte *const row_data,
DBUG_ASSERT(m_rows_cur + length < m_rows_end);
memcpy(m_rows_cur, row_data, length);
m_rows_cur+= length;
m_row_count++;
DBUG_RETURN(0);
}
#endif
@@ -5255,10 +5262,29 @@ static char const *unpack_row(TABLE *table,
int Rows_log_event::exec_event(st_relay_log_info *rli)
{
DBUG_ENTER("Rows_log_event::exec_event(st_relay_log_info*)");
DBUG_ASSERT(m_table_id != ULONG_MAX);
int error= 0;
char const *row_start= (char const *)m_rows_buf;
TABLE* table= rli->m_table_map.get_table(m_table_id);
/*
If m_table_id == ULONG_MAX, then we have a dummy event that does
not contain any data. In that case, we just remove all tables in
the tables_to_lock list, close the thread tables, step the relay
log position, and return with success.
*/
if (m_table_id == ULONG_MAX)
{
/*
This one is supposed to be set: just an extra check so that
nothing strange has happened.
*/
DBUG_ASSERT(get_flags(STMT_END_F));
rli->clear_tables_to_lock();
close_thread_tables(thd);
thd->clear_error();
rli->inc_event_relay_log_pos();
DBUG_RETURN(0);
}
/*
'thd' has been set by exec_relay_log_event(), just before calling
@@ -5267,85 +5293,51 @@ int Rows_log_event::exec_event(st_relay_log_info *rli)
DBUG_ASSERT(rli->sql_thd == thd);
/*
lock_tables() reads the contents of thd->lex, so they must be
initialized, so we should call lex_start(); to be even safer, we call
mysql_init_query() which does a more complete set of inits.
If there is no locks taken, this is the first binrow event seen
after the table map events. We should then lock all the tables
used in the transaction and proceed with execution of the actual
event.
*/
mysql_init_query(thd, NULL, 0);
if (table)
if (!thd->lock)
{
bool need_reopen= 1; /* To execute the first lap of the loop below */
/*
table == NULL means that this table should not be
replicated (this was set up by Table_map_log_event::exec_event() which
tested replicate-* rules).
lock_tables() reads the contents of thd->lex, so they must be
initialized, so we should call lex_start(); to be even safer, we
call mysql_init_query() which does a more complete set of inits.
*/
TABLE_LIST table_list;
bool need_reopen;
uint count= 1;
bzero(&table_list, sizeof(table_list));
table_list.lock_type= TL_WRITE;
table_list.next_global= table_list.next_local= 0;
table_list.table= table;
mysql_init_query(thd, NULL, 0);
for ( ; ; )
while ((error= lock_tables(thd, rli->tables_to_lock,
rli->tables_to_lock_count, &need_reopen)))
{
table_list.db= const_cast<char*>(table->s->db.str);
table_list.alias= table_list.table_name=
const_cast<char*>(table->s->table_name.str);
if ((error= lock_tables(thd, &table_list, count, &need_reopen)) == 0)
break;
if (!need_reopen)
{
slave_print_msg(ERROR_LEVEL, rli, error,
"Error in %s event: error during table %s.%s lock",
get_type_str(), table->s->db.str,
table->s->table_name.str);
"Error in %s event: when locking tables",
get_type_str());
DBUG_RETURN(error);
}
/*
we need to store a local copy of the table names since the table object
will become invalid after close_tables_for_reopen
*/
char *db= my_strdup(table->s->db.str, MYF(MY_WME));
char *table_name= my_strdup(table->s->table_name.str, MYF(MY_WME));
if (db == 0 || table_name == 0)
{
/*
Since the lock_tables() failed, the table is not locked, so
we don't need to unlock them.
*/
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
}
/*
We also needs to flush the pending RBR event, since it keeps a
pointer to an open table.
So we need to reopen the tables.
ALTERNATIVE SOLUTION: Extract a pointer to the pending RBR
event and reset the table pointer after the tables has been
reopened.
We need to flush the pending RBR event, since it keeps a
pointer to an open table.
ALTERNATIVE SOLUTION (not implemented): Extract a pointer to
the pending RBR event and reset the table pointer after the
tables has been reopened.
NOTE: For this new scheme there should be no pending event:
need to add code to assert that is the case.
*/
thd->binlog_flush_pending_rows_event(false);
close_tables_for_reopen(thd, rli->tables_to_lock);
close_tables_for_reopen(thd, &table_list);
/* open the table again, same as in Table_map_event::exec_event */
table_list.db= const_cast<char*>(db);
table_list.alias= table_list.table_name= const_cast<char*>(table_name);
table_list.updating= 1;
TABLE_LIST *tables= &table_list;
if ((error= open_tables(thd, &tables, &count, 0)) == 0)
{
/* reset some variables for the table list*/
table_list.updating= 0;
/* retrieve the new table reference and update the table map */
table= table_list.table;
error= rli->m_table_map.set_table(m_table_id, table);
}
else /* error in open_tables */
if ((error= open_tables(thd, &rli->tables_to_lock,
&rli->tables_to_lock_count, 0)))
{
if (thd->query_error || thd->is_fatal_error)
{
@@ -5355,19 +5347,41 @@ int Rows_log_event::exec_event(st_relay_log_info *rli)
*/
uint actual_error= thd->net.last_errno;
slave_print_msg(ERROR_LEVEL, rli, actual_error,
"Error '%s' on reopening table `%s`.`%s`",
"Error '%s' on reopening tables",
(actual_error ? thd->net.last_error :
"unexpected success or fatal error"),
db, table_name);
"unexpected success or fatal error"));
thd->query_error= 1;
}
}
my_free((char*) db, MYF(MY_ALLOW_ZERO_PTR));
my_free((char*) table_name, MYF(MY_ALLOW_ZERO_PTR));
if (error)
DBUG_RETURN(error);
}
}
/*
When the open and locking succeeded, we add all the tables to
the table map and remove them from tables to lock.
*/
TABLE_LIST *ptr= rli->tables_to_lock;
while (ptr)
{
rli->m_table_map.set_table(ptr->table_id, ptr->table);
rli->touching_table(ptr->db, ptr->table_name, ptr->table_id);
char *to_free= reinterpret_cast<char*>(ptr);
ptr= ptr->next_global;
my_free(to_free, MYF(MY_WME));
}
rli->tables_to_lock= 0;
rli->tables_to_lock_count= 0;
}
TABLE* table= rli->m_table_map.get_table(m_table_id);
if (table)
{
/*
table == NULL means that this table should not be replicated
(this was set up by Table_map_log_event::exec_event() which
tested replicate-* rules).
*/
/*
It's not needed to set_time() but
@@ -5401,12 +5415,16 @@ int Rows_log_event::exec_event(st_relay_log_info *rli)
DBUG_ASSERT(row_end != NULL); // cannot happen
DBUG_ASSERT(row_end <= (const char*)m_rows_end);
#if 0
/* in_use can have been set to NULL in close_tables_for_reopen */
THD* old_thd= table->in_use;
if (!table->in_use)
table->in_use= thd;
#endif
error= do_exec_row(table);
#if 0
table->in_use = old_thd;
#endif
switch (error)
{
/* Some recoverable errors */
@@ -5520,52 +5538,25 @@ int Rows_log_event::exec_event(st_relay_log_info *rli)
DBUG_RETURN(error);
}
if (table)
if (table && (table->s->primary_key == MAX_KEY) && !cache_stmt)
{
/*
As "table" is not NULL, we did a successful lock_tables(), without any
prior LOCK TABLES and are not in prelocked mode, so this assertion should
be true.
------------ Temporary fix until WL#2975 is implemented ---------
This event is not the last one (no STMT_END_F). If we stop now
(in case of terminate_slave_thread()), how will we restart? We
have to restart from Table_map_log_event, but as this table is
not transactional, the rows already inserted will still be
present, and idempotency is not guaranteed (no PK) so we risk
that repeating leads to double insert. So we desperately try to
continue, hope we'll eventually leave this buggy situation (by
executing the final Rows_log_event). If we are in a hopeless
wait (reached end of last relay log and nothing gets appended
there), we timeout after one minute, and notify DBA about the
problem. When WL#2975 is implemented, just remove the member
st_relay_log_info::unsafe_to_stop_at and all its occurences.
*/
DBUG_ASSERT(thd->lock);
/*
If we are here, there are more events to come which may use our mappings
and our table. So don't clear mappings or close tables, just unlock
tables.
Why don't we lock the table once for all in
Table_map_log_event::exec_event() ? Because we could have in binlog:
BEGIN;
Table_map t1 -> 1
Write_rows to id 1
Table_map t2 -> 2
Write_rows to id 2
Xid_log_event
So we cannot lock t1 when executing the first Table_map, because at that
moment we don't know we'll also have to lock t2, and all tables must be
locked at once in MySQL.
*/
mysql_unlock_tables(thd, thd->lock);
thd->lock= 0;
if ((table->s->primary_key == MAX_KEY) &&
!cache_stmt)
{
/*
------------ Temporary fix until WL#2975 is implemented ---------
This event is not the last one (no STMT_END_F). If we stop now (in
case of terminate_slave_thread()), how will we restart? We have to
restart from Table_map_log_event, but as this table is not
transactional, the rows already inserted will still be present, and
idempotency is not guaranteed (no PK) so we risk that repeating leads
to double insert. So we desperately try to continue, hope we'll
eventually leave this buggy situation (by executing the final
Rows_log_event). If we are in a hopeless wait (reached end of last
relay log and nothing gets appended there), we timeout after one
minute, and notify DBA about the problem.
When WL#2975 is implemented, just remove the member
st_relay_log_info::unsafe_to_stop_at and all its occurences.
*/
rli->unsafe_to_stop_at= time(0);
}
rli->unsafe_to_stop_at= time(0);
}
DBUG_ASSERT(error == 0);
@@ -5579,7 +5570,6 @@ int Rows_log_event::exec_event(st_relay_log_info *rli)
#ifndef MYSQL_CLIENT
bool Rows_log_event::write_data_header(IO_CACHE *file)
{
DBUG_ASSERT(m_table_id != ULONG_MAX);
byte buf[ROWS_HEADER_LEN]; // No need to init the buffer
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
{
@@ -5611,16 +5601,15 @@ bool Rows_log_event::write_data_body(IO_CACHE*file)
}
#endif
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) && defined(DBUG_RBR)
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
void Rows_log_event::pack_info(Protocol *protocol)
{
char buf[256];
char const *const flagstr= get_flags(STMT_END_F) ? "STMT_END_F" : "";
char const *const dbnam= m_table->s->db.str;
char const *const tblnam= m_table->s->table_name.str;
my_size_t bytes= snprintf(buf, sizeof(buf),
"%s.%s - %s", dbnam, tblnam, flagstr);
protocol->store(buf, bytes, &my_charset_bin);
char buf[256];
char const *const flagstr=
get_flags(STMT_END_F) ? " flags: STMT_END_F" : "";
my_size_t bytes= snprintf(buf, sizeof(buf),
"table_id: %lu%s", m_table_id, flagstr);
protocol->store(buf, bytes, &my_charset_bin);
}
#endif
@@ -5763,59 +5752,6 @@ Table_map_log_event::~Table_map_log_event()
my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
}
/*
Find a table based on database name and table name.
DESCRIPTION
Currently, only the first table of the 'table_list' is located. If the
table is found in the list of open tables for the thread, the 'table'
field of 'table_list' is filled in.
PARAMETERS
thd Thread structure
table_list List of tables to locate in the thd->open_tables list.
count Pointer to a variable that will be set to the number of
tables found. If the pointer is NULL, nothing will be stored.
RETURN VALUE
The number of tables found.
TO DO
Replace the list of table searches with a hash based on the combined
database and table name. The handler_tables_hash is inappropriate since
it hashes on the table alias. At the same time, the function can be
extended to handle a full list of table names, in the same spirit as
open_tables() and lock_tables().
*/
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
static uint find_tables(THD *thd, TABLE_LIST *table_list, uint *count)
{
uint result= 0;
/* we verify that the caller knows our limitation */
DBUG_ASSERT(table_list->next_global == 0);
for (TABLE *table= thd->open_tables; table ; table= table->next)
{
if (strcmp(table->s->db.str, table_list->db) == 0
&& strcmp(table->s->table_name.str, table_list->table_name) == 0)
{
/* Copy the table pointer into the table list. */
table_list->table= table;
result= 1;
break;
}
}
if (count)
*count= result;
return result;
}
#endif
/*
Return value is an error code, one of:
@@ -5839,20 +5775,37 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli)
thd->query_id= next_query_id();
pthread_mutex_unlock(&LOCK_thread_count);
TABLE_LIST table_list;
TABLE_LIST *table_list;
char *db_mem, *tname_mem;
void *const memory=
my_multi_malloc(MYF(MY_WME),
&table_list, sizeof(TABLE_LIST),
&db_mem, NAME_LEN + 1,
&tname_mem, NAME_LEN + 1,
NULL);
/*
If memory is allocated, it the pointer to it should be stored in
table_list. If this is not true, the memory will not be correctly
free:ed later.
*/
DBUG_ASSERT(memory == NULL || memory == table_list);
uint32 dummy_len;
bzero(&table_list, sizeof(table_list));
table_list.db= const_cast<char *>
(rpl_filter->get_rewrite_db(m_dbnam, &dummy_len));
table_list.alias= table_list.table_name= const_cast<char*>(m_tblnam);
table_list.lock_type= TL_WRITE;
table_list.next_global= table_list.next_local= 0;
table_list.updating= 1;
bzero(table_list, sizeof(*table_list));
table_list->db = db_mem;
table_list->alias= table_list->table_name = tname_mem;
table_list->lock_type= TL_WRITE;
table_list->next_global= table_list->next_local= 0;
table_list->table_id= m_table_id;
table_list->updating= 1;
strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len));
strmov(table_list->table_name, m_tblnam);
int error= 0;
if (rpl_filter->db_ok(table_list.db) &&
(!rpl_filter->is_on() || rpl_filter->tables_ok("", &table_list)))
if (rpl_filter->db_ok(table_list->db) &&
(!rpl_filter->is_on() || rpl_filter->tables_ok("", table_list)))
{
/*
Check if the slave is set to use SBR. If so, the slave should
@@ -5878,36 +5831,32 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli)
be a no-op.
*/
uint count;
if (find_tables(thd, &table_list, &count) == 0)
/*
open_tables() reads the contents of thd->lex, so they must be
initialized, so we should call lex_start(); to be even safer, we
call mysql_init_query() which does a more complete set of inits.
*/
mysql_init_query(thd, NULL, 0);
if ((error= open_tables(thd, &table_list, &count, 0)))
{
/*
open_tables() reads the contents of thd->lex, so they must be
initialized, so we should call lex_start(); to be even safer, we call
mysql_init_query() which does a more complete set of inits.
*/
mysql_init_query(thd, NULL, 0);
TABLE_LIST *tables= &table_list;
if ((error= open_tables(thd, &tables, &count, 0)))
if (thd->query_error || thd->is_fatal_error)
{
if (thd->query_error || thd->is_fatal_error)
{
/*
Error reporting borrowed from Query_log_event with many excessive
simplifications (we don't honour --slave-skip-errors)
*/
uint actual_error= thd->net.last_errno;
slave_print_msg(ERROR_LEVEL, rli, actual_error,
"Error '%s' on opening table `%s`.`%s`",
(actual_error ? thd->net.last_error :
"unexpected success or fatal error"),
table_list.db, table_list.table_name);
thd->query_error= 1;
}
DBUG_RETURN(error);
/*
Error reporting borrowed from Query_log_event with many excessive
simplifications (we don't honour --slave-skip-errors)
*/
uint actual_error= thd->net.last_errno;
slave_print_msg(ERROR_LEVEL, rli, actual_error,
"Error '%s' on opening table `%s`.`%s`",
(actual_error ? thd->net.last_error :
"unexpected success or fatal error"),
table_list->db, table_list->table_name);
thd->query_error= 1;
}
DBUG_RETURN(error);
}
m_table= table_list.table;
m_table= table_list->table;
/*
This will fail later otherwise, the 'in_use' field should be
@@ -5985,19 +5934,16 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli)
}
/*
We record in the slave's information that the number m_table_id is
mapped to the m_table object
We record in the slave's information that the table should be
locked by linking the table into the list of tables to lock, and
tell the RLI that we are touching a table.
*/
if (!error)
error= rli->m_table_map.set_table(m_table_id, m_table);
/*
Tell the RLI that we are touching a table.
TODO: Maybe we can combine this with the previous operation?
*/
if (!error)
rli->touching_table(m_dbnam, m_tblnam, m_table_id);
{
table_list->next_global= table_list->next_local= rli->tables_to_lock;
rli->tables_to_lock= table_list;
rli->tables_to_lock_count++;
}
}
/*
@@ -6064,12 +6010,17 @@ bool Table_map_log_event::write_data_body(IO_CACHE *file)
field.
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
void Table_map_log_event::pack_info(Protocol *protocol)
{
char buf[256];
my_size_t bytes= my_snprintf(buf, sizeof(buf), "%s.%s", m_dbnam, m_tblnam);
my_size_t bytes= snprintf(buf, sizeof(buf),
"table_id: %lu (%s.%s)",
m_table_id, m_dbnam, m_tblnam);
protocol->store(buf, bytes, &my_charset_bin);
}
#endif
#endif