Commit c34ac8ee authored by Konstantin Osipov's avatar Konstantin Osipov

Backport of:

------------------------------------------------------------
revno: 2630.13.16
committer: Davi Arnaut <Davi.Arnaut@Sun.COM>
branch nick: WL#4284
timestamp: Sat 2008-07-26 13:38:20 -0300
message:
WL#4284: Transactional DDL locking

SQL statements' effect on transactions.

Currently the MySQL server and its storage engines are not
capable of rolling back operations that define or modify data
structures (also known as DDL statements) or operations that
alter any of the system tables (the mysql database). Allowing
these group of statements to participate in transactions
is unfeasible at this time (since rollback has no effect
whatsoever on them) and goes against the design of our metadata
locking subsystem.

The solution is to issue implicit commits before and after
those statements execution. This effectively confines each of
those statements to its own special transaction and ensures
that metadata locks taken during this special transaction
are not leaked into posterior statements/transactions.


mysql-test/include/commit.inc:
  Alter table rename was not committing the normal transaction at the
  end of its execution, and as a consequence, the commit was being
  issued in the next DDL command (rename table) that happened to end
  the active transaction. Other changes are to take into account the
  implicit commits issued before and after the DDL command execution.
mysql-test/include/implicit_commit_helper.inc:
  Add auxiliary test that shows if a statement issued a 
  implicit commit.
mysql-test/r/commit_1innodb.result:
  
  Update test case result.
mysql-test/r/implicit_commit.result:
  Test implicit commit behavior of some SQL commands.
mysql-test/t/implicit_commit.test:
  Test implicit commit behavior of some SQL commands.
sql/events.cc:
  Transaction is now ended before the command execution.
sql/mysql_priv.h:
  Add flags array for server commands and remove historical 
  left over.
sql/sql_class.h:
  Add flags to control when to issue implicit commits before and
  after a command execution.
sql/sql_delete.cc:
  A implicit commit is issued at the end of truncate
  statements.
sql/sql_parse.cc:
  Mark commands that need implicit commits before and
  after their executions. The implicit commits of the
  statement and the normal transaction are now issued
  regardless of the user access privileges.
sql/sql_table.cc:
  A implicit commit is now issued before admin commands.
tests/mysql_client_test.c:
  Test that COM_REFRESH issues a implicit commit.
parent 98d50a3a
......@@ -725,15 +725,15 @@ call p_verify_status_increment(4, 4, 4, 4);
alter table t3 add column (b int);
call p_verify_status_increment(2, 0, 2, 0);
alter table t3 rename t4;
call p_verify_status_increment(2, 2, 2, 2);
call p_verify_status_increment(4, 4, 4, 4);
rename table t4 to t3;
call p_verify_status_increment(2, 2, 2, 2);
call p_verify_status_increment(0, 0, 0, 0);
truncate table t3;
call p_verify_status_increment(4, 4, 4, 4);
create view v1 as select * from t2;
call p_verify_status_increment(1, 0, 1, 0);
call p_verify_status_increment(2, 0, 2, 0);
check table t1;
call p_verify_status_increment(3, 0, 3, 0);
call p_verify_status_increment(2, 0, 2, 0);
--echo # Sic: after this bug is fixed, CHECK leaves no pending transaction
commit;
call p_verify_status_increment(0, 0, 0, 0);
......
INSERT INTO db1.trans (a) VALUES (1);
--disable_result_log
eval $statement;
--enable_result_log
CALL db1.test_if_commit();
......@@ -841,11 +841,11 @@ call p_verify_status_increment(2, 0, 2, 0);
SUCCESS
alter table t3 rename t4;
call p_verify_status_increment(2, 2, 2, 2);
call p_verify_status_increment(4, 4, 4, 4);
SUCCESS
rename table t4 to t3;
call p_verify_status_increment(2, 2, 2, 2);
call p_verify_status_increment(0, 0, 0, 0);
SUCCESS
truncate table t3;
......@@ -853,13 +853,13 @@ call p_verify_status_increment(4, 4, 4, 4);
SUCCESS
create view v1 as select * from t2;
call p_verify_status_increment(1, 0, 1, 0);
call p_verify_status_increment(2, 0, 2, 0);
SUCCESS
check table t1;
Table Op Msg_type Msg_text
test.t1 check status OK
call p_verify_status_increment(3, 0, 3, 0);
call p_verify_status_increment(2, 0, 2, 0);
SUCCESS
# Sic: after this bug is fixed, CHECK leaves no pending transaction
......
This diff is collapsed.
This diff is collapsed.
......@@ -391,15 +391,6 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
int ret;
DBUG_ENTER("Events::create_event");
/*
Let's commit the transaction first - MySQL manual specifies
that a DDL issues an implicit commit, and it doesn't say "successful
DDL", so that an implicit commit is a property of any successfully
parsed DDL statement.
*/
if (end_active_trans(thd))
DBUG_RETURN(TRUE);
if (check_if_system_tables_error())
DBUG_RETURN(TRUE);
......@@ -512,13 +503,6 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
DBUG_ENTER("Events::update_event");
/*
For consistency, implicit COMMIT should be the first thing in the
execution chain.
*/
if (end_active_trans(thd))
DBUG_RETURN(TRUE);
if (check_if_system_tables_error())
DBUG_RETURN(TRUE);
......@@ -635,20 +619,6 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
int ret;
DBUG_ENTER("Events::drop_event");
/*
In MySQL, DDL must always commit: since mysql.* tables are
non-transactional, we must modify them outside a transaction
to not break atomicity.
But the second and more important reason to commit here
regardless whether we're actually changing mysql.event table
or not is replication: end_active_trans syncs the binary log,
and unless we run DDL in it's own transaction it may simply
never appear on the slave in case the outside transaction
rolls back.
*/
if (end_active_trans(thd))
DBUG_RETURN(TRUE);
if (check_if_system_tables_error())
DBUG_RETURN(TRUE);
......
......@@ -2014,8 +2014,8 @@ extern struct my_option my_long_options[];
extern const LEX_STRING view_type;
extern scheduler_functions thread_scheduler;
extern TYPELIB thread_handling_typelib;
extern uint8 uc_update_queries[SQLCOM_END+1];
extern uint sql_command_flags[];
extern uint server_command_flags[];
extern TYPELIB log_output_typelib;
/* optional things, have_* variables */
......
......@@ -3152,6 +3152,36 @@ class select_dumpvar :public select_result_interceptor {
joins are currently prohibited in these statements.
*/
#define CF_REEXECUTION_FRAGILE (1U << 5)
/**
Implicitly commit before the SQL statement is executed.
Statements marked with this flag will cause any active
transaction to end (commit) before proceeding with the
command execution.
This flag should be set for statements that probably can't
be rolled back or that do not expect any previously metadata
locked tables.
*/
#define CF_IMPLICT_COMMIT_BEGIN (1U << 6)
/**
Implicitly commit after the SQL statement.
Statements marked with this flag are automatically committed
at the end of the statement.
This flag should be set for statements that will implicitly
open and take metadata locks on system tables that should not
be carried for the whole duration of a active transaction.
*/
#define CF_IMPLICIT_COMMIT_END (1U << 7)
/**
CF_IMPLICT_COMMIT_BEGIN and CF_IMPLICIT_COMMIT_END are used
to ensure that the active transaction is implicitly committed
before and after every DDL statement and any statement that
modifies our currently non-transactional system tables.
*/
#define CF_AUTO_COMMIT_TRANS (CF_IMPLICT_COMMIT_BEGIN | CF_IMPLICIT_COMMIT_END)
/**
Diagnostic statement.
......@@ -3163,6 +3193,23 @@ class select_dumpvar :public select_result_interceptor {
*/
#define CF_DIAGNOSTIC_STMT (1U << 8)
/* Bits in server_command_flags */
/**
Skip the increase of the global query id counter. Commonly set for
commands that are stateless (won't cause any change on the server
internal states).
*/
#define CF_SKIP_QUERY_ID (1U << 0)
/**
Skip the increase of the number of statements that clients have
sent to the server. Commonly used for commands that will cause
a statement to be executed but the statement might have not been
sent by the user (ie: stored procedure).
*/
#define CF_SKIP_QUESTIONS (1U << 1)
/* Functions in sql_class.cc */
void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
......
......@@ -1062,9 +1062,18 @@ static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list)
table_list->lock_type= TL_WRITE;
mysql_init_select(thd->lex);
thd->clear_current_stmt_binlog_row_based();
/* Delete all rows from table */
error= mysql_delete(thd, table_list, NULL, NULL, HA_POS_ERROR, LL(0), TRUE);
ha_autocommit_or_rollback(thd, error);
end_trans(thd, error ? ROLLBACK : COMMIT);
/*
All effects of a TRUNCATE TABLE operation are rolled back if a row by row
deletion fails. Otherwise, operation is automatically committed at the end.
*/
if (error)
{
DBUG_ASSERT(thd->stmt_da->is_error());
ha_autocommit_or_rollback(thd, TRUE);
end_active_trans(thd);
}
thd->current_stmt_binlog_row_based= save_binlog_row_based;
DBUG_RETURN(error);
}
......
This diff is collapsed.
......@@ -4565,8 +4565,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
int result_code;
DBUG_ENTER("mysql_admin_table");
if (end_active_trans(thd))
DBUG_RETURN(1);
field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
item->maybe_null = 1;
field_list.push_back(item = new Item_empty_string("Op", 10));
......
......@@ -18436,6 +18436,59 @@ static void test_bug36004()
DBUG_VOID_RETURN;
}
/**
Test that COM_REFRESH issues a implicit commit.
*/
static void test_wl4284_1()
{
int rc;
MYSQL_ROW row;
MYSQL_RES *result;
DBUG_ENTER("test_wl4284_1");
myheader("test_wl4284_1");
/* set AUTOCOMMIT to OFF */
rc= mysql_autocommit(mysql, FALSE);
myquery(rc);
rc= mysql_query(mysql, "DROP TABLE IF EXISTS trans");
myquery(rc);
rc= mysql_query(mysql, "CREATE TABLE trans (a INT) ENGINE= InnoDB");
myquery(rc);
rc= mysql_query(mysql, "INSERT INTO trans VALUES(1)");
myquery(rc);
rc= mysql_refresh(mysql, REFRESH_GRANT | REFRESH_TABLES);
myquery(rc);
rc= mysql_rollback(mysql);
myquery(rc);
rc= mysql_query(mysql, "SELECT * FROM trans");
myquery(rc);
result= mysql_use_result(mysql);
mytest(result);
row= mysql_fetch_row(result);
mytest(row);
mysql_free_result(result);
/* set AUTOCOMMIT to OFF */
rc= mysql_autocommit(mysql, FALSE);
myquery(rc);
rc= mysql_query(mysql, "DROP TABLE trans");
myquery(rc);
DBUG_VOID_RETURN;
}
static void test_bug38486(void)
{
......@@ -19197,6 +19250,7 @@ static struct my_tests_st my_tests[]= {
{ "test_wl4166_3", test_wl4166_3 },
{ "test_wl4166_4", test_wl4166_4 },
{ "test_bug36004", test_bug36004 },
{ "test_wl4284_1", test_wl4284_1 },
{ "test_wl4435", test_wl4435 },
{ "test_wl4435_2", test_wl4435_2 },
{ "test_bug38486", test_bug38486 },
......
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