Commit 1bfe0bdf authored by guilhem@gbichot3.local's avatar guilhem@gbichot3.local

Merge gbichot3.local:/home/mysql_src/mysql-5.1-new-19630

into  gbichot3.local:/home/mysql_src/mysql-5.1
parents b3977379 fdb0f85a
This diff is collapsed.
......@@ -426,7 +426,9 @@ Item *create_func_unhex(Item* a)
Item *create_func_uuid(void)
{
THD *thd= current_thd;
thd->lex->binlog_row_based_if_mixed= 1;
#ifdef HAVE_ROW_BASED_REPLICATION
thd->lex->binlog_row_based_if_mixed= TRUE;
#endif
return new(thd->mem_root) Item_func_uuid();
}
......
......@@ -1343,9 +1343,9 @@ bool sys_var_thd_binlog_format::is_readonly() const
return 1;
}
/*
if in a stored function, it's too late to change mode
if in a stored function/trigger, it's too late to change mode
*/
if (thd->spcont && thd->prelocked_mode)
if (thd->in_sub_stmt)
{
my_error(ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT, MYF(0));
return 1;
......
......@@ -1632,6 +1632,7 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
sp->add_used_tables_to_table_list(thd, &lex->query_tables_last,
rt->belong_to_view);
}
sp->propagate_attributes(lex);
}
first= FALSE;
}
......@@ -1729,14 +1730,16 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
{
for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
{
if (triggers->bodies[i][j])
sp_head *trigger_body= triggers->bodies[i][j];
if (trigger_body)
{
(void)triggers->bodies[i][j]->
add_used_tables_to_table_list(thd, &lex->query_tables_last,
table->belong_to_view);
(void)trigger_body->
add_used_tables_to_table_list(thd, &lex->query_tables_last,
table->belong_to_view);
sp_update_stmt_used_routines(thd, lex,
&triggers->bodies[i][j]->m_sroutines,
&trigger_body->m_sroutines,
table->belong_to_view);
trigger_body->propagate_attributes(lex);
}
}
}
......
......@@ -1675,6 +1675,16 @@ sp_head::restore_lex(THD *thd)
oldlex->next_state= sublex->next_state;
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
#ifdef HAVE_ROW_BASED_REPLICATION
/*
If this substatement needs row-based, the entire routine does too (we
cannot switch from statement-based to row-based only for this
substatement).
*/
if (sublex->binlog_row_based_if_mixed)
m_flags|= BINLOG_ROW_BASED_IF_MIXED;
#endif
/*
Add routines which are used by statement to respective set for
this routine.
......
......@@ -126,7 +126,8 @@ class sp_head :private Query_arena
/* Is set if a procedure with COMMIT (implicit or explicit) | ROLLBACK */
HAS_COMMIT_OR_ROLLBACK= 128,
LOG_SLOW_STATEMENTS= 256, // Used by events
LOG_GENERAL_LOG= 512 // Used by events
LOG_GENERAL_LOG= 512, // Used by events
BINLOG_ROW_BASED_IF_MIXED= 1024
};
/* TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */
......@@ -351,6 +352,25 @@ class sp_head :private Query_arena
int show_routine_code(THD *thd);
#endif
/*
This method is intended for attributes of a routine which need
to propagate upwards to the LEX of the caller (when a property of a
sp_head needs to "taint" the caller).
*/
void propagate_attributes(LEX *lex)
{
#ifdef HAVE_ROW_BASED_REPLICATION
/*
If this routine needs row-based binary logging, the entire top statement
too (we cannot switch from statement-based to row-based only for this
routine, as in statement-based the top-statement may be binlogged and
the substatements not).
*/
if (m_flags & BINLOG_ROW_BASED_IF_MIXED)
lex->binlog_row_based_if_mixed= TRUE;
#endif
}
private:
......
......@@ -49,6 +49,8 @@ static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
static void close_old_data_files(THD *thd, TABLE *table, bool abort_locks,
bool send_refresh);
static bool reopen_table(TABLE *table);
static bool
has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables);
extern "C" byte *table_cache_key(const byte *record,uint *length,
......@@ -3315,6 +3317,18 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
*need_reopen= FALSE;
#ifdef HAVE_ROW_BASED_REPLICATION
/*
CREATE ... SELECT UUID() locks no tables, we have to test here.
Note that we will not do the resetting if inside a stored
function/trigger, because the binlogging of those is decided earlier (by
the caller) and can't be changed afterwards.
*/
thd->reset_current_stmt_binlog_row_based();
if (thd->lex->binlog_row_based_if_mixed)
thd->set_current_stmt_binlog_row_based_if_mixed();
#endif /*HAVE_ROW_BASED_REPLICATION*/
if (!tables)
DBUG_RETURN(0);
......@@ -3345,6 +3359,19 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
{
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
#ifdef HAVE_ROW_BASED_REPLICATION
/*
If we have >= 2 different tables to update with auto_inc columns,
statement-based binlogging won't work. We can solve this problem in
mixed mode by switching to row-based binlogging:
*/
if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
has_two_write_locked_tables_with_auto_increment(tables))
{
thd->lex->binlog_row_based_if_mixed= TRUE;
thd->set_current_stmt_binlog_row_based_if_mixed();
}
#endif
}
if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
......@@ -6477,3 +6504,46 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table
DBUG_VOID_RETURN;
}
/*
Tells if two (or more) tables have auto_increment columns and we want to
lock those tables with a write lock.
SYNOPSIS
has_two_write_locked_tables_with_auto_increment
tables Table list
NOTES:
Call this function only when you have established the list of all tables
which you'll want to update (including stored functions, triggers, views
inside your statement).
RETURN
0 No
1 Yes
*/
static bool
has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables)
{
char *first_table_name= NULL, *first_db;
for (TABLE_LIST *table= tables; table; table= table->next_global)
{
/* we must do preliminary checks as table->table may be NULL */
if (!table->placeholder() && !table->schema_table &&
table->table->found_next_number_field &&
(table->lock_type >= TL_WRITE_ALLOW_WRITE))
{
if (first_table_name == NULL)
{
first_table_name= table->table_name;
first_db= table->db;
DBUG_ASSERT(first_db);
}
else if (strcmp(first_db, table->db) ||
strcmp(first_table_name, table->table_name))
return 1;
}
}
return 0;
}
......@@ -1418,7 +1418,17 @@ class THD :public Statement,
inline void set_current_stmt_binlog_row_based_if_mixed()
{
#ifdef HAVE_ROW_BASED_REPLICATION
if (variables.binlog_format == BINLOG_FORMAT_MIXED)
/*
If in a stored/function trigger, the caller should already have done the
change. We test in_sub_stmt to prevent introducing bugs where people
wouldn't ensure that, and would switch to row-based mode in the middle
of executing a stored function/trigger (which is too late, see also
reset_current_stmt_binlog_row_based()); this condition will make their
tests fail and so force them to propagate the
lex->binlog_row_based_if_mixed upwards to the caller.
*/
if ((variables.binlog_format == BINLOG_FORMAT_MIXED) &&
(in_sub_stmt == 0))
current_stmt_binlog_row_based= TRUE;
#endif
}
......@@ -1437,8 +1447,26 @@ class THD :public Statement,
inline void reset_current_stmt_binlog_row_based()
{
#ifdef HAVE_ROW_BASED_REPLICATION
current_stmt_binlog_row_based=
test(variables.binlog_format == BINLOG_FORMAT_ROW);
/*
If there are temporary tables, don't reset back to
statement-based. Indeed it could be that:
CREATE TEMPORARY TABLE t SELECT UUID(); # row-based
# and row-based does not store updates to temp tables
# in the binlog.
INSERT INTO u SELECT * FROM t; # stmt-based
and then the INSERT will fail as data inserted into t was not logged.
So we continue with row-based until the temp table is dropped.
If we are in a stored function or trigger, we mustn't reset in the
middle of its execution (as the binary logging way of a stored function
or trigger is decided when it starts executing, depending for example on
the caller (for a stored function: if caller is SELECT or
INSERT/UPDATE/DELETE...).
*/
if ((temporary_tables == NULL) && (in_sub_stmt == 0))
{
current_stmt_binlog_row_based=
test(variables.binlog_format == BINLOG_FORMAT_ROW);
}
#else
current_stmt_binlog_row_based= FALSE;
#endif
......
......@@ -183,7 +183,6 @@ void lex_start(THD *thd, const uchar *buf, uint length)
lex->nest_level=0 ;
lex->allow_sum_func= 0;
lex->in_sum_func= NULL;
lex->binlog_row_based_if_mixed= 0;
DBUG_VOID_RETURN;
}
......@@ -1625,6 +1624,9 @@ void Query_tables_list::reset_query_tables_list(bool init)
sroutines_list.empty();
sroutines_list_own_last= sroutines_list.next;
sroutines_list_own_elements= 0;
#ifdef HAVE_ROW_BASED_REPLICATION
binlog_row_based_if_mixed= FALSE;
#endif
}
......
......@@ -793,6 +793,16 @@ class Query_tables_list
byte **sroutines_list_own_last;
uint sroutines_list_own_elements;
#ifdef HAVE_ROW_BASED_REPLICATION
/*
Tells if the parsing stage detected that some items require row-based
binlogging to give a reliable binlog/replication, or if we will use
stored functions or triggers which themselves need require row-based
binlogging.
*/
bool binlog_row_based_if_mixed;
#endif
/*
These constructor and destructor serve for creation/destruction
of Query_tables_list instances which are used as backup storage.
......@@ -970,11 +980,7 @@ typedef struct st_lex : public Query_tables_list
uint8 create_view_check;
bool drop_if_exists, drop_temporary, local_file, one_shot_set;
bool in_comment, ignore_space, verbose, no_write_to_binlog;
/*
binlog_row_based_if_mixed tells if the parsing stage detected that some
items require row-based binlogging to give a reliable binlog/replication.
*/
bool tx_chain, tx_release, binlog_row_based_if_mixed;
bool tx_chain, tx_release;
/*
Special JOIN::prepare mode: changing of query is prohibited.
When creating a view, we need to just check its syntax omitting
......
......@@ -2503,11 +2503,6 @@ mysql_execute_command(THD *thd)
statistic_increment(thd->status_var.com_stat[lex->sql_command],
&LOCK_status);
#ifdef HAVE_ROW_BASED_REPLICATION
if (lex->binlog_row_based_if_mixed)
thd->set_current_stmt_binlog_row_based_if_mixed();
#endif /*HAVE_ROW_BASED_REPLICATION*/
switch (lex->sql_command) {
case SQLCOM_SHOW_EVENTS:
if ((res= check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0,
......@@ -5166,9 +5161,6 @@ mysql_execute_command(THD *thd)
*/
if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION)
reset_one_shot_variables(thd);
#ifdef HAVE_ROW_BASED_REPLICATION
thd->reset_current_stmt_binlog_row_based();
#endif /*HAVE_ROW_BASED_REPLICATION*/
/*
The return value for ROW_COUNT() is "implementation dependent" if the
......@@ -5846,6 +5838,11 @@ void mysql_reset_thd_for_next_command(THD *thd)
thd->rand_used= 0;
thd->sent_row_count= thd->examined_row_count= 0;
}
#ifdef HAVE_ROW_BASED_REPLICATION
/* If in a routine, we reset only at end of top statement. */
thd->reset_current_stmt_binlog_row_based();
#endif /*HAVE_ROW_BASED_REPLICATION*/
DBUG_VOID_RETURN;
}
......
......@@ -997,6 +997,15 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table)
table->next_global= view_tables;
}
#ifdef HAVE_ROW_BASED_REPLICATION
/*
If the view's body needs row-based binlogging (e.g. the VIEW is created
from SELECT UUID()), the top statement also needs it.
*/
if (lex->binlog_row_based_if_mixed)
old_lex->binlog_row_based_if_mixed= TRUE;
#endif
/*
If we are opening this view as part of implicit LOCK TABLES, then
this view serves as simple placeholder and we should not continue
......
......@@ -6374,7 +6374,7 @@ simple_expr:
if (udf->type == UDFTYPE_AGGREGATE)
Select->in_sum_expr--;
Lex->binlog_row_based_if_mixed= 1;
Lex->binlog_row_based_if_mixed= TRUE;
switch (udf->returns) {
case STRING_RESULT:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment