summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/variables.result22
-rw-r--r--mysql-test/r/view.result6
-rw-r--r--mysql-test/suite/rpl/r/rpl_row_implicit_commit_binlog.result4
-rw-r--r--mysql-test/t/variables.test26
-rw-r--r--sql/event_data_objects.cc18
-rw-r--r--sql/event_db_repository.cc74
-rw-r--r--sql/event_db_repository.h5
-rw-r--r--sql/events.cc14
-rw-r--r--sql/ha_ndbcluster.cc3
-rw-r--r--sql/ha_ndbcluster_binlog.cc19
-rw-r--r--sql/handler.cc4
-rw-r--r--sql/handler.h1
-rw-r--r--sql/log.cc2
-rw-r--r--sql/log_event.cc60
-rw-r--r--sql/rpl_rli.cc16
-rw-r--r--sql/set_var.cc1
-rw-r--r--sql/slave.cc1
-rw-r--r--sql/sp.cc16
-rw-r--r--sql/sp_head.cc47
-rw-r--r--sql/sp_head.h2
-rw-r--r--sql/sql_acl.cc46
-rw-r--r--sql/sql_base.cc155
-rw-r--r--sql/sql_base.h1
-rw-r--r--sql/sql_class.cc7
-rw-r--r--sql/sql_cursor.cc2
-rw-r--r--sql/sql_do.cc5
-rw-r--r--sql/sql_handler.cc21
-rw-r--r--sql/sql_insert.cc34
-rw-r--r--sql/sql_lex.cc3
-rw-r--r--sql/sql_parse.cc214
-rw-r--r--sql/sql_parse.h1
-rw-r--r--sql/sql_partition.cc3
-rw-r--r--sql/sql_plugin.cc4
-rw-r--r--sql/sql_prepare.cc42
-rw-r--r--sql/sql_priv.h10
-rw-r--r--sql/sql_servers.cc17
-rw-r--r--sql/sql_table.cc65
-rw-r--r--sql/sql_trigger.cc27
-rw-r--r--sql/sql_udf.cc4
-rw-r--r--sql/sys_vars.cc15
-rw-r--r--sql/transaction.cc27
-rw-r--r--sql/tztime.cc12
42 files changed, 589 insertions, 467 deletions
diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result
index b6ad1ff31bf..be81afe1a43 100644
--- a/mysql-test/r/variables.result
+++ b/mysql-test/r/variables.result
@@ -1677,3 +1677,25 @@ SET @@sql_quote_show_create = @sql_quote_show_create_saved;
# End of Bug#34828.
+# Make sure we can manipulate with autocommit in the
+# along with other variables.
+drop table if exists t1;
+drop function if exists t1_max;
+drop function if exists t1_min;
+create table t1 (a int) engine=innodb;
+insert into t1(a) values (0), (1);
+create function t1_max() returns int return (select max(a) from t1);
+create function t1_min() returns int return (select min(a) from t1);
+select t1_min();
+t1_min()
+0
+select t1_max();
+t1_max()
+1
+set @@session.autocommit=t1_min(), @@session.autocommit=t1_max(),
+@@session.autocommit=t1_min(), @@session.autocommit=t1_max(),
+@@session.autocommit=t1_min(), @@session.autocommit=t1_max();
+# Cleanup.
+drop table t1;
+drop function t1_min;
+drop function t1_max;
diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result
index e1c1d6f4128..96b45f0d5bb 100644
--- a/mysql-test/r/view.result
+++ b/mysql-test/r/view.result
@@ -1955,15 +1955,15 @@ CHECK TABLE v1, v2, v3, v4, v5, v6;
Table Op Msg_type Msg_text
test.v1 check Error FUNCTION test.f1 does not exist
test.v1 check Error View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
-test.v1 check status Operation failed
+test.v1 check error Corrupt
test.v2 check status OK
test.v3 check Error FUNCTION test.f1 does not exist
test.v3 check Error View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
-test.v3 check status Operation failed
+test.v3 check error Corrupt
test.v4 check status OK
test.v5 check Error FUNCTION test.f1 does not exist
test.v5 check Error View 'test.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
-test.v5 check status Operation failed
+test.v5 check error Corrupt
test.v6 check status OK
create function f1 () returns int return (select max(col1) from t1);
DROP TABLE t1;
diff --git a/mysql-test/suite/rpl/r/rpl_row_implicit_commit_binlog.result b/mysql-test/suite/rpl/r/rpl_row_implicit_commit_binlog.result
index 896ba90b865..459dc83e01d 100644
--- a/mysql-test/suite/rpl/r/rpl_row_implicit_commit_binlog.result
+++ b/mysql-test/suite/rpl/r/rpl_row_implicit_commit_binlog.result
@@ -165,10 +165,6 @@ master-bin.000001 # Table_map # # table_id: # (test.tt_1)
master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Query # # use `test`; SET PASSWORD FOR 'user'@'localhost'='*D8DECEC305209EEFEC43008E1D420E1AA06B19E0'
-master-bin.000001 # Query # # BEGIN
-master-bin.000001 # Table_map # # table_id: # (mysql.user)
-master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F
-master-bin.000001 # Query # # COMMIT
-e-e-e-e-e-e-e-e-e-e-e- >> << -e-e-e-e-e-e-e-e-e-e-e-
-b-b-b-b-b-b-b-b-b-b-b- >> << -b-b-b-b-b-b-b-b-b-b-b-
diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test
index d865851841f..75099523062 100644
--- a/mysql-test/t/variables.test
+++ b/mysql-test/t/variables.test
@@ -1405,4 +1405,30 @@ SET @@sql_quote_show_create = @sql_quote_show_create_saved;
--echo # End of Bug#34828.
--echo
+--echo # Make sure we can manipulate with autocommit in the
+--echo # along with other variables.
+
+
+--disable_warnings
+drop table if exists t1;
+drop function if exists t1_max;
+drop function if exists t1_min;
+--enable_warnings
+
+create table t1 (a int) engine=innodb;
+insert into t1(a) values (0), (1);
+create function t1_max() returns int return (select max(a) from t1);
+create function t1_min() returns int return (select min(a) from t1);
+select t1_min();
+select t1_max();
+set @@session.autocommit=t1_min(), @@session.autocommit=t1_max(),
+ @@session.autocommit=t1_min(), @@session.autocommit=t1_max(),
+ @@session.autocommit=t1_min(), @@session.autocommit=t1_max();
+
+--echo # Cleanup.
+drop table t1;
+drop function t1_min;
+drop function t1_max;
+
+
###########################################################################
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
index dd1845b29bc..52c509621ac 100644
--- a/sql/event_data_objects.cc
+++ b/sql/event_data_objects.cc
@@ -1402,6 +1402,8 @@ Event_job_data::execute(THD *thd, bool drop)
*/
thd->set_db(dbname.str, dbname.length);
+ lex_start(thd);
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (event_sctx.change_security_context(thd,
&definer_user, &definer_host,
@@ -1411,7 +1413,7 @@ Event_job_data::execute(THD *thd, bool drop)
"[%s].[%s.%s] execution failed, "
"failed to authenticate the user.",
definer.str, dbname.str, name.str);
- goto end_no_lex_start;
+ goto end;
}
#endif
@@ -1427,11 +1429,11 @@ Event_job_data::execute(THD *thd, bool drop)
"[%s].[%s.%s] execution failed, "
"user no longer has EVENT privilege.",
definer.str, dbname.str, name.str);
- goto end_no_lex_start;
+ goto end;
}
if (construct_sp_sql(thd, &sp_sql))
- goto end_no_lex_start;
+ goto end;
/*
Set up global thread attributes to reflect the properties of
@@ -1451,8 +1453,6 @@ Event_job_data::execute(THD *thd, bool drop)
if (parser_state.init(thd, thd->query(), thd->query_length()))
goto end;
- lex_start(thd);
-
if (parse_sql(thd, & parser_state, creation_ctx))
{
sql_print_error("Event Scheduler: "
@@ -1484,13 +1484,6 @@ Event_job_data::execute(THD *thd, bool drop)
}
end:
- if (thd->lex->sphead) /* NULL only if a parse error */
- {
- delete thd->lex->sphead;
- thd->lex->sphead= NULL;
- }
-
-end_no_lex_start:
if (drop && !thd->is_fatal_error)
{
/*
@@ -1529,7 +1522,6 @@ end_no_lex_start:
if (save_sctx)
event_sctx.restore_security_context(thd, save_sctx);
#endif
- lex_end(thd->lex);
thd->lex->unit.cleanup();
thd->end_statement();
thd->cleanup_after_query();
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index b7c01f10066..d47f1641bb0 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -518,17 +518,20 @@ Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
*/
bool
-Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables,
+Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table,
const char *db)
{
- TABLE *schema_table= tables->table;
- TABLE *event_table= NULL;
+ TABLE *schema_table= i_s_table->table;
+ Open_tables_backup open_tables_backup;
+ TABLE_LIST event_table;
int ret= 0;
DBUG_ENTER("Event_db_repository::fill_schema_events");
DBUG_PRINT("info",("db=%s", db? db:"(null)"));
- if (open_event_table(thd, TL_READ, &event_table))
+ event_table.init_one_table("mysql", 5, "event", 5, "event", TL_READ);
+
+ if (open_system_tables_for_read(thd, &event_table, &open_tables_backup))
DBUG_RETURN(TRUE);
/*
@@ -541,11 +544,11 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables,
every single row's `db` with the schema which we show.
*/
if (db)
- ret= index_read_for_db_for_i_s(thd, schema_table, event_table, db);
+ ret= index_read_for_db_for_i_s(thd, schema_table, event_table.table, db);
else
- ret= table_scan_all_for_i_s(thd, schema_table, event_table);
+ ret= table_scan_all_for_i_s(thd, schema_table, event_table.table);
- close_thread_tables(thd);
+ close_system_tables(thd, &open_tables_backup);
DBUG_PRINT("info", ("Return code=%d", ret));
DBUG_RETURN(ret);
@@ -584,10 +587,7 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
tables.init_one_table("mysql", 5, "event", 5, "event", lock_type);
if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
- {
- close_thread_tables(thd);
DBUG_RETURN(TRUE);
- }
*table= tables.table;
tables.table->use_all_columns();
@@ -700,7 +700,8 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
end:
if (table)
- close_thread_tables(thd);
+ close_mysql_tables(thd);
+
thd->variables.sql_mode= saved_mode;
DBUG_RETURN(test(ret));
}
@@ -811,7 +812,8 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
end:
if (table)
- close_thread_tables(thd);
+ close_mysql_tables(thd);
+
thd->variables.sql_mode= saved_mode;
DBUG_RETURN(test(ret));
}
@@ -865,7 +867,7 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
end:
if (table)
- close_thread_tables(thd);
+ close_mysql_tables(thd);
DBUG_RETURN(test(ret));
}
@@ -934,33 +936,13 @@ Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name,
void
Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
{
- DBUG_ENTER("Event_db_repository::drop_schema_events");
- drop_events_by_field(thd, ET_FIELD_DB, schema);
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Drops all events which have a specific value of a field.
-
- @pre The thread handle has no open tables.
-
- @param[in,out] thd Thread
- @param[in,out] table mysql.event TABLE
- @param[in] field Which field of the row to use for matching
- @param[in] field_value The value that should match
-*/
-
-void
-Event_db_repository::drop_events_by_field(THD *thd,
- enum enum_events_table_field field,
- LEX_STRING field_value)
-{
int ret= 0;
TABLE *table= NULL;
READ_RECORD read_record_info;
- DBUG_ENTER("Event_db_repository::drop_events_by_field");
- DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str));
+ enum enum_events_table_field field= ET_FIELD_DB;
+ MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ DBUG_ENTER("Event_db_repository::drop_schema_events");
+ DBUG_PRINT("enter", ("field=%d schema=%s", field, schema.str));
if (open_event_table(thd, TL_WRITE, &table))
DBUG_VOID_RETURN;
@@ -979,7 +961,7 @@ Event_db_repository::drop_events_by_field(THD *thd,
get_field(thd->mem_root,
table->field[ET_FIELD_NAME])));
- if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info))
+ if (!sortcmp_lex_string(et_field_lex, schema, system_charset_info))
{
DBUG_PRINT("info", ("Dropping"));
if ((ret= table->file->ha_delete_row(table->record[0])))
@@ -989,6 +971,11 @@ Event_db_repository::drop_events_by_field(THD *thd,
}
end_read_record(&read_record_info);
close_thread_tables(thd);
+ /*
+ Make sure to only release the MDL lock on mysql.event, not other
+ metadata locks DROP DATABASE might have acquired.
+ */
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
DBUG_VOID_RETURN;
}
@@ -1026,7 +1013,7 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
else if ((ret= etn->load_from_row(thd, table)))
my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
- close_thread_tables(thd);
+ close_mysql_tables(thd);
}
thd->variables.sql_mode= saved_mode;
@@ -1104,7 +1091,8 @@ update_timing_fields_for_event(THD *thd,
end:
if (table)
- close_thread_tables(thd);
+ close_mysql_tables(thd);
+
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -1151,7 +1139,7 @@ Event_db_repository::check_system_tables(THD *thd)
if (table_intact.check(tables.table, &mysql_db_table_def))
ret= 1;
- close_thread_tables(thd);
+ close_mysql_tables(thd);
}
/* Check mysql.user */
tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ);
@@ -1171,7 +1159,7 @@ Event_db_repository::check_system_tables(THD *thd)
event_priv_column_position);
ret= 1;
}
- close_thread_tables(thd);
+ close_mysql_tables(thd);
}
/* Check mysql.event */
tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ);
@@ -1185,7 +1173,7 @@ Event_db_repository::check_system_tables(THD *thd)
{
if (table_intact.check(tables.table, &event_table_def))
ret= 1;
- close_thread_tables(thd);
+ close_mysql_tables(thd);
}
DBUG_RETURN(test(ret));
diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h
index ef778407d1e..ea7f3bbac0e 100644
--- a/sql/event_db_repository.h
+++ b/sql/event_db_repository.h
@@ -91,7 +91,7 @@ public:
bool
load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et);
- bool
+ static bool
open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
bool
@@ -109,9 +109,6 @@ public:
static bool
check_system_tables(THD *thd);
private:
- void
- drop_events_by_field(THD *thd, enum enum_events_table_field field,
- LEX_STRING field_value);
bool
index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table,
const char *db);
diff --git a/sql/events.cc b/sql/events.cc
index a548bda53e2..5379ec2c9eb 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -16,7 +16,7 @@
#include "sql_priv.h"
#include "unireg.h"
#include "sql_parse.h" // check_access
-#include "sql_base.h" // close_thread_tables
+#include "sql_base.h" // close_mysql_tables
#include "sql_show.h" // append_definer
#include "events.h"
#include "sql_db.h" // check_db_dir_existence
@@ -754,7 +754,6 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
{
char *db= NULL;
int ret;
- Open_tables_backup open_tables_backup;
DBUG_ENTER("Events::fill_schema_events");
if (check_if_system_tables_error())
@@ -773,15 +772,7 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
DBUG_RETURN(1);
db= thd->lex->select_lex.db;
}
- /*
- Reset and backup of the currently open tables in this thread
- is a way to allow SELECTs from INFORMATION_SCHEMA.events under
- LOCK TABLES and in pre-locked mode. See also
- Events::show_create_event for additional comments.
- */
- thd->reset_n_backup_open_tables_state(&open_tables_backup);
ret= db_repository->fill_schema_events(thd, tables, db);
- thd->restore_backup_open_tables_state(&open_tables_backup);
DBUG_RETURN(ret);
}
@@ -1161,8 +1152,7 @@ Events::load_events_from_db(THD *thd)
end:
end_read_record(&read_record_info);
- close_thread_tables(thd);
-
+ close_mysql_tables(thd);
DBUG_RETURN(ret);
}
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index ecf2984a4c0..7cac8373bc4 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -7417,7 +7417,8 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
FALSE, /* drop_temporary */
FALSE, /* drop_view */
TRUE /* dont_log_query*/);
-
+ trans_commit_implicit(thd); /* Safety, should be unnecessary. */
+ thd->mdl_context.release_transactional_locks();
/* Clear error message that is returned when table is deleted */
thd->clear_error();
}
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index 4f8bb66fcb0..b610687496e 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -298,13 +298,6 @@ static void run_query(THD *thd, char *buf, char *end,
thd_ndb->m_error_code,
(int) thd->is_error(), thd->is_slave_error);
}
-
- /*
- After executing statement we should unlock and close tables open
- by it as well as release meta-data locks obtained by it.
- */
- close_thread_tables(thd);
-
/*
XXX: this code is broken. mysql_parse()/mysql_reset_thd_for_next_command()
can not be called from within a statement, and
@@ -2422,7 +2415,11 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row)
}
add_ndb_binlog_index_err:
+ thd->stmt_da->can_overwrite_status= TRUE;
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
ndb_binlog_index= 0;
thd->variables.option_bits= saved_options;
return error;
@@ -3969,7 +3966,9 @@ restart:
{
if (ndb_binlog_index->s->needs_reopen())
{
+ trans_commit_stmt(thd);
close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
ndb_binlog_index= 0;
}
}
@@ -4280,7 +4279,9 @@ restart:
if (do_ndbcluster_binlog_close_connection == BCCC_restart)
{
ndb_binlog_tables_inited= FALSE;
+ trans_commit_stmt(thd);
close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
ndb_binlog_index= 0;
goto restart;
}
@@ -4288,7 +4289,11 @@ err:
sql_print_information("Stopping Cluster Binlog");
DBUG_PRINT("info",("Shutting down cluster binlog thread"));
thd->proc_info= "Shutting down";
+ thd->stmt_da->can_overwrite_status= TRUE;
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
mysql_mutex_lock(&injector_mutex);
/* don't mess with the injector_ndb anymore from other threads */
injector_thd= 0;
diff --git a/sql/handler.cc b/sql/handler.cc
index b42840c7b1b..e67f5d6862c 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1145,6 +1145,7 @@ int ha_commit_trans(THD *thd, bool all)
if (thd->in_sub_stmt)
{
+ DBUG_ASSERT(0);
/*
Since we don't support nested statement transactions in 5.0,
we can't commit or rollback stmt transactions while we are inside
@@ -1159,7 +1160,6 @@ int ha_commit_trans(THD *thd, bool all)
bail out with error even before ha_commit_trans() call. To be 100% safe
let us throw error in non-debug builds.
*/
- DBUG_ASSERT(0);
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
DBUG_RETURN(2);
}
@@ -1342,6 +1342,7 @@ int ha_rollback_trans(THD *thd, bool all)
if (thd->in_sub_stmt)
{
+ DBUG_ASSERT(0);
/*
If we are inside stored function or trigger we should not commit or
rollback current statement transaction. See comment in ha_commit_trans()
@@ -1349,7 +1350,6 @@ int ha_rollback_trans(THD *thd, bool all)
*/
if (!all)
DBUG_RETURN(0);
- DBUG_ASSERT(0);
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
DBUG_RETURN(1);
}
diff --git a/sql/handler.h b/sql/handler.h
index 96095798d18..9f262542a4f 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -846,6 +846,7 @@ struct THD_TRANS
bool modified_non_trans_table;
void reset() { no_2pc= FALSE; modified_non_trans_table= FALSE; }
+ bool is_empty() const { return ha_list == NULL; }
};
diff --git a/sql/log.cc b/sql/log.cc
index b398cb1e73f..6eab16bc3a4 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -27,7 +27,7 @@
#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "log.h"
-#include "sql_base.h" // close_thread_tables
+#include "sql_base.h" // open_log_table
#include "sql_repl.h"
#include "sql_delete.h" // mysql_truncate
#include "sql_parse.h" // command_name
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 5236a2794cf..8f70282ce79 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -3332,6 +3332,19 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
thd->table_map_for_update= (table_map)table_map_for_update;
thd->set_invoker(&user, &host);
+ /*
+ Flag if we need to rollback the statement transaction on
+ slave if it by chance succeeds.
+ If we expected a non-zero error code and get nothing and,
+ it is a concurrency issue or ignorable issue, effects
+ of the statement should be rolled back.
+ */
+ if (expected_error &&
+ (ignored_error_code(expected_error) ||
+ concurrency_error_code(expected_error)))
+ {
+ thd->variables.option_bits|= OPTION_MASTER_SQL_ERROR;
+ }
/* Execute the query (note that we bypass dispatch_command()) */
Parser_state parser_state;
if (!parser_state.init(thd, thd->query(), thd->query_length()))
@@ -3340,6 +3353,8 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
log_slow_statement(thd);
}
+ thd->variables.option_bits&= ~OPTION_MASTER_SQL_ERROR;
+
/*
Resetting the enable_slow_log thd variable.
@@ -3382,7 +3397,6 @@ START SLAVE; . Query: '%s'", expected_error, thd->query());
general_log_write(thd, COM_QUERY, thd->query(), thd->query_length());
compare_errors:
-
/*
In the slave thread, we may sometimes execute some DROP / * 40005
TEMPORARY * / TABLE that come from parts of binlogs (likely if we
@@ -3430,26 +3444,8 @@ Default database: '%s'. Query: '%s'",
DBUG_PRINT("info",("error ignored"));
clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
thd->killed= THD::NOT_KILLED;
- /*
- When an error is expected and matches the actual error the
- slave does not report any error and by consequence changes
- on transactional tables are not rolled back in the function
- close_thread_tables(). For that reason, we explicitly roll
- them back here.
- */
- if (expected_error && expected_error == actual_error)
- trans_rollback_stmt(thd);
}
/*
- If we expected a non-zero error code and get nothing and, it is a concurrency
- issue or should be ignored.
- */
- else if (expected_error && !actual_error &&
- (concurrency_error_code(expected_error) ||
- ignored_error_code(expected_error)))
- trans_rollback_stmt(thd);
-
- /*
Other cases: mostly we expected no error and get one.
*/
else if (thd->is_slave_error || thd->is_fatal_error)
@@ -3516,7 +3512,6 @@ end:
thd->set_db(NULL, 0); /* will free the current database */
thd->set_query(NULL, 0);
DBUG_PRINT("info", ("end: query= 0"));
- close_thread_tables(thd);
/*
As a disk space optimization, future masters will not log an event for
LAST_INSERT_ID() if that function returned 0 (and thus they will be able
@@ -4946,7 +4941,22 @@ error:
thd->catalog= 0;
thd->set_db(NULL, 0); /* will free the current database */
thd->set_query(NULL, 0);
+ thd->stmt_da->can_overwrite_status= TRUE;
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
close_thread_tables(thd);
+ /*
+ - If inside a multi-statement transaction,
+ defer the release of metadata locks until the current
+ transaction is either committed or rolled back. This prevents
+ other statements from modifying the table for the entire
+ duration of this transaction. This provides commit ordering
+ and guarantees serializability across multiple transactions.
+ - If in autocommit mode, or outside a transactional context,
+ automatically release metadata locks of the current statement.
+ */
+ if (! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error",
thd->is_slave_error= 0; thd->is_fatal_error= 1;);
@@ -5531,11 +5541,9 @@ int Xid_log_event::do_apply_event(Relay_log_info const *rli)
/* For a slave Xid_log_event is COMMIT */
general_log_print(thd, COM_QUERY,
"COMMIT /* implicit, from Xid_log_event */");
- if (!(res= trans_commit(thd)))
- {
- close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
- }
+ res= trans_commit(thd); /* Automatically rolls back on error. */
+ thd->mdl_context.release_transactional_locks();
+
return res;
}
@@ -7610,8 +7618,6 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
We should not honour --slave-skip-errors at this point as we are
having severe errors which should not be skiped.
*/
- mysql_unlock_tables(thd, thd->lock);
- thd->lock= 0;
thd->is_slave_error= 1;
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(ERR_BAD_TABLE_DEF);
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 03c369394bf..af9b452acd8 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -1257,7 +1257,23 @@ void Relay_log_info::clear_tables_to_lock()
void Relay_log_info::slave_close_thread_tables(THD *thd)
{
+ thd->stmt_da->can_overwrite_status= TRUE;
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
+
close_thread_tables(thd);
+ /*
+ - If inside a multi-statement transaction,
+ defer the release of metadata locks until the current
+ transaction is either committed or rolled back. This prevents
+ other statements from modifying the table for the entire
+ duration of this transaction. This provides commit ordering
+ and guarantees serializability across multiple transactions.
+ - If in autocommit mode, or outside a transactional context,
+ automatically release metadata locks of the current statement.
+ */
+ if (! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
clear_tables_to_lock();
}
#endif
diff --git a/sql/set_var.cc b/sql/set_var.cc
index ec9c09f02a3..9daaf883ea8 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -27,7 +27,6 @@
#include "mysqld.h" // lc_messages_dir
#include "sys_vars_shared.h"
#include "transaction.h"
-#include "sql_base.h" // close_thread_tables
#include "sql_locale.h" // my_locale_by_number,
// my_locale_by_name
#include "strfunc.h" // find_set_from_flags, find_set
diff --git a/sql/slave.cc b/sql/slave.cc
index d41d0479dde..a6199854e48 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -3044,7 +3044,6 @@ err:
change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE);
DBUG_ASSERT(thd->net.buff != 0);
net_end(&thd->net); // destructor will not free it, because net.vio is 0
- close_thread_tables(thd);
mysql_mutex_lock(&LOCK_thread_count);
THD_CHECK_SENTRY(thd);
delete thd;
diff --git a/sql/sp.cc b/sql/sp.cc
index 5328471f4c0..0265ef45a2a 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -450,10 +450,7 @@ static TABLE *open_proc_table_for_update(THD *thd)
if (!proc_table_intact.check(table, &proc_table_def))
DBUG_RETURN(table);
- close_thread_tables(thd);
-
DBUG_RETURN(NULL);
-
}
@@ -856,6 +853,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
}
end:
+ thd->lex->sphead= NULL;
lex_end(thd->lex);
thd->lex= old_lex;
return ret;
@@ -1159,8 +1157,6 @@ sp_create_routine(THD *thd, int type, sp_head *sp)
done:
thd->count_cuted_fields= saved_count_cuted_fields;
thd->variables.sql_mode= saved_mode;
-
- close_thread_tables(thd);
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -1239,8 +1235,6 @@ sp_drop_routine(THD *thd, int type, sp_name *name)
sp_cache_flush_obsolete(spc, &sp);
}
}
-
- close_thread_tables(thd);
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -1348,7 +1342,6 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics)
sp_cache_invalidate();
}
err:
- close_thread_tables(thd);
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -1370,6 +1363,7 @@ sp_drop_db_routines(THD *thd, char *db)
TABLE *table;
int ret;
uint key_len;
+ MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("sp_drop_db_routines");
DBUG_PRINT("enter", ("db: %s", db));
@@ -1410,6 +1404,11 @@ sp_drop_db_routines(THD *thd, char *db)
table->file->ha_index_end();
close_thread_tables(thd);
+ /*
+ Make sure to only release the MDL lock on mysql.proc, not other
+ metadata locks DROP DATABASE might have acquired.
+ */
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
err:
DBUG_RETURN(ret);
@@ -2142,6 +2141,7 @@ sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db,
newlex.current_select= NULL;
sp= sp_compile(thd, &defstr, sql_mode, creation_ctx);
*free_sp_head= 1;
+ thd->lex->sphead= NULL;
lex_end(thd->lex);
thd->lex= old_lex;
return sp;
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index f75acf11984..11f138e67be 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -38,6 +38,7 @@
#include "set_var.h"
#include "sql_parse.h" // cleanup_items
#include "sql_base.h" // close_thread_tables
+#include "transaction.h" // trans_commit_stmt
/*
Sufficient max length of printed destinations and frame offsets (all uints).
@@ -795,6 +796,7 @@ sp_head::~sp_head()
while ((lex= (LEX *)m_lex.pop()))
{
THD *thd= lex->thd;
+ thd->lex->sphead= NULL;
lex_end(thd->lex);
delete thd->lex;
thd->lex= lex;
@@ -1995,16 +1997,23 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
arguments evaluation. If arguments evaluation required prelocking mode,
we'll leave it here.
*/
+ thd->lex->unit.cleanup();
+
if (!thd->in_sub_stmt)
{
- thd->lex->unit.cleanup();
+ thd->stmt_da->can_overwrite_status= TRUE;
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
+ }
- thd_proc_info(thd, "closing tables");
- close_thread_tables(thd);
- thd_proc_info(thd, 0);
+ thd_proc_info(thd, "closing tables");
+ close_thread_tables(thd);
+ thd_proc_info(thd, 0);
- thd->rollback_item_tree_changes();
- }
+ if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
+
+ thd->rollback_item_tree_changes();
DBUG_PRINT("info",(" %.*s: eval args done", (int) m_name.length,
m_name.str));
@@ -2197,6 +2206,7 @@ sp_head::restore_lex(THD *thd)
merge_table_list(thd, sublex->query_tables, sublex);
if (! sublex->sp_lex_in_use)
{
+ sublex->sphead= NULL;
lex_end(sublex);
delete sublex;
}
@@ -2806,12 +2816,27 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
DBUG_PRINT("info",("exec_core returned: %d", res));
}
- m_lex->unit.cleanup();
+ /*
+ Call after unit->cleanup() to close open table
+ key read.
+ */
+ if (open_tables)
+ {
+ m_lex->unit.cleanup();
+ /* Here we also commit or rollback the current statement. */
+ if (! thd->in_sub_stmt)
+ {
+ thd->stmt_da->can_overwrite_status= TRUE;
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
+ }
+ thd_proc_info(thd, "closing tables");
+ close_thread_tables(thd);
+ thd_proc_info(thd, 0);
- thd_proc_info(thd, "closing tables");
- /* Here we also commit or rollback the current statement. */
- close_thread_tables(thd);
- thd_proc_info(thd, 0);
+ if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
+ }
if (m_lex->query_tables_own_last)
{
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 9796c49fdfb..b2446c8f680 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -682,6 +682,8 @@ public:
{
if (m_lex_resp)
{
+ /* Prevent endless recursion. */
+ m_lex->sphead= NULL;
lex_end(m_lex);
delete m_lex;
}
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index d4c4f464884..0008968de2a 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -27,7 +27,7 @@
#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "sql_acl.h" // MYSQL_DB_FIELD_COUNT, ACL_ACCESS
-#include "sql_base.h" // close_thread_tables
+#include "sql_base.h" // close_mysql_tables
#include "key.h" // key_copy, key_cmp_if_same, key_restore
#include "sql_show.h" // append_identifier
#include "sql_table.h" // build_table_filename
@@ -730,9 +730,7 @@ my_bool acl_reload(THD *thd)
if (old_initialized)
mysql_mutex_unlock(&acl_cache->lock);
end:
- trans_commit_implicit(thd);
- close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ close_mysql_tables(thd);
DBUG_RETURN(return_val);
}
@@ -1585,6 +1583,7 @@ bool change_password(THD *thd, const char *host, const char *user,
/* Buffer should be extended when password length is extended. */
char buff[512];
ulong query_length;
+ bool save_binlog_row_based;
uint new_password_len= (uint) strlen(new_password);
bool result= 1;
DBUG_ENTER("change_password");
@@ -1614,10 +1613,17 @@ bool change_password(THD *thd, const char *host, const char *user,
DBUG_RETURN(0);
}
#endif
-
if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
DBUG_RETURN(1);
+ /*
+ This statement will be replicated as a statement, even when using
+ row-based replication. The flag will be reset at the end of the
+ statement.
+ */
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
+
mysql_mutex_lock(&acl_cache->lock);
ACL_USER *acl_user;
if (!(acl_user= find_acl_user(host, user, TRUE)))
@@ -1652,7 +1658,13 @@ bool change_password(THD *thd, const char *host, const char *user,
FALSE, FALSE, FALSE, 0);
}
end:
- close_thread_tables(thd);
+ close_mysql_tables(thd);
+
+ /* Restore the state of binlog format */
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
+
DBUG_RETURN(result);
}
@@ -3082,7 +3094,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
DBUG_RETURN(TRUE);
column_priv|= column->rights;
}
- close_thread_tables(thd);
+ close_mysql_tables(thd);
}
else
{
@@ -3172,7 +3184,6 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
thd->lex->sql_command= backup.sql_command;
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{ // Should never happen
- close_thread_tables(thd); /* purecov: deadcode */
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -3398,7 +3409,6 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{ // Should never happen
- close_thread_tables(thd);
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -3553,7 +3563,6 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{ // This should never happen
- close_thread_tables(thd); /* purecov: deadcode */
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -3613,7 +3622,6 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
}
mysql_rwlock_unlock(&LOCK_grant);
- close_thread_tables(thd);
if (!result)
my_ok(thd);
@@ -3874,10 +3882,7 @@ static my_bool grant_reload_procs_priv(THD *thd)
table.open_type= OT_BASE_ONLY;
if (open_and_lock_tables(thd, &table, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
- {
- close_thread_tables(thd);
DBUG_RETURN(TRUE);
- }
mysql_rwlock_wrlock(&LOCK_grant);
/* Save a copy of the current hash if we need to undo the grant load */
@@ -3899,7 +3904,7 @@ static my_bool grant_reload_procs_priv(THD *thd)
}
mysql_rwlock_unlock(&LOCK_grant);
- close_thread_tables(thd);
+ close_mysql_tables(thd);
DBUG_RETURN(return_val);
}
@@ -3970,9 +3975,7 @@ my_bool grant_reload(THD *thd)
free_root(&old_mem,MYF(0));
}
mysql_rwlock_unlock(&LOCK_grant);
- trans_commit_implicit(thd);
- close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ close_mysql_tables(thd);
/*
It is OK failing to load procs_priv table because we may be
@@ -5250,7 +5253,6 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{ // This should never happen
- close_thread_tables(thd);
DBUG_RETURN(-1);
}
@@ -5890,7 +5892,6 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
mysql_rwlock_unlock(&LOCK_grant);
- close_thread_tables(thd);
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -5975,7 +5976,6 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
mysql_rwlock_unlock(&LOCK_grant);
- close_thread_tables(thd);
thd->variables.sql_mode= old_sql_mode;
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
@@ -6072,7 +6072,6 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
mysql_rwlock_unlock(&LOCK_grant);
- close_thread_tables(thd);
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -6270,8 +6269,6 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
write_bin_log(thd, FALSE, thd->query(), thd->query_length());
mysql_rwlock_unlock(&LOCK_grant);
- close_thread_tables(thd);
-
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -6418,7 +6415,6 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
mysql_mutex_unlock(&acl_cache->lock);
mysql_rwlock_unlock(&LOCK_grant);
- close_thread_tables(thd);
thd->pop_internal_handler();
/* Restore the state of binlog format */
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 790898baae6..7a59fefdddd 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1402,6 +1402,9 @@ void close_thread_tables(THD *thd)
DEBUG_SYNC(thd, "before_close_thread_tables");
#endif
+ DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt ||
+ (thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
+
/* Detach MERGE children after every statement. Even under LOCK TABLES. */
for (table= thd->open_tables; table; table= table->next)
{
@@ -1446,28 +1449,6 @@ void close_thread_tables(THD *thd)
Mark all temporary tables used by this statement as free for reuse.
*/
mark_temp_tables_as_free_for_reuse(thd);
- /*
- Let us commit transaction for statement. Since in 5.0 we only have
- one statement transaction and don't allow several nested statement
- transactions this call will do nothing if we are inside of stored
- function or trigger (i.e. statement transaction is already active and
- does not belong to statement for which we do close_thread_tables()).
- TODO: This should be fixed in later releases.
- */
- if (!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
- {
- thd->stmt_da->can_overwrite_status= TRUE;
- thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
- thd->stmt_da->can_overwrite_status= FALSE;
-
- /*
- Reset transaction state, but only if we're not inside a
- sub-statement of a prelocked statement.
- */
- if (thd->locked_tables_mode <= LTM_LOCK_TABLES ||
- thd->lex->requires_prelocking())
- thd->transaction.stmt.reset();
- }
if (thd->locked_tables_mode)
{
@@ -1528,26 +1509,6 @@ void close_thread_tables(THD *thd)
if (thd->open_tables)
close_open_tables(thd);
- /*
- - If inside a multi-statement transaction,
- defer the release of metadata locks until the current
- transaction is either committed or rolled back. This prevents
- other statements from modifying the table for the entire
- duration of this transaction. This provides commit ordering
- and guarantees serializability across multiple transactions.
- - If closing a system table, defer the release of metadata locks
- to the caller. We have no sentinel in MDL subsystem to guard
- transactional locks from system tables locks, so don't know
- which locks are which here.
- - If in autocommit mode, or outside a transactional context,
- automatically release metadata locks of the current statement.
- */
- if (! thd->in_multi_stmt_transaction_mode() &&
- ! (thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
- {
- thd->mdl_context.release_transactional_locks();
- }
-
DBUG_VOID_RETURN;
}
@@ -1562,7 +1523,14 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
DBUG_ASSERT(table->key_read == 0);
DBUG_ASSERT(!table->file || table->file->inited == handler::NONE);
mysql_mutex_assert_not_owner(&LOCK_open);
-
+ /*
+ The metadata lock must be released after giving back
+ the table to the table cache.
+ */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE,
+ table->s->db.str,
+ table->s->table_name.str,
+ MDL_SHARED));
table->mdl_ticket= NULL;
mysql_mutex_lock(&thd->LOCK_thd_data);
@@ -3188,6 +3156,7 @@ Locked_tables_list::init_locked_tables(THD *thd)
return FALSE;
}
+
/**
Leave LTM_LOCK_TABLES mode if it's been entered.
@@ -3224,7 +3193,12 @@ Locked_tables_list::unlock_locked_tables(THD *thd)
}
thd->leave_locked_tables_mode();
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
close_thread_tables(thd);
+ /*
+ We rely on the caller to implicitly commit the
+ transaction and release transactional locks.
+ */
}
/*
After closing tables we can free memory used for storing lock
@@ -3810,9 +3784,7 @@ Open_table_context::Open_table_context(THD *thd, uint flags)
LONG_TIMEOUT : thd->variables.lock_wait_timeout),
m_flags(flags),
m_action(OT_NO_ACTION),
- m_has_locks((thd->in_multi_stmt_transaction_mode() &&
- thd->mdl_context.has_locks()) ||
- thd->mdl_context.trans_sentinel())
+ m_has_locks(thd->mdl_context.has_locks())
{}
@@ -5264,6 +5236,8 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
table= 0;
end:
+ if (table == NULL)
+ close_thread_tables(thd);
thd_proc_info(thd, 0);
DBUG_RETURN(table);
}
@@ -5282,7 +5256,8 @@ end:
should work for this statement.
@note
- The lock will automaticaly be freed by close_thread_tables()
+ The thr_lock locks will automatically be freed by
+ close_thread_tables().
@retval FALSE OK.
@retval TRUE Error
@@ -5293,11 +5268,12 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
Prelocking_strategy *prelocking_strategy)
{
uint counter;
+ MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("open_and_lock_tables");
DBUG_PRINT("enter", ("derived handling: %d", derived));
if (open_tables(thd, &tables, &counter, flags, prelocking_strategy))
- DBUG_RETURN(TRUE);
+ goto err;
DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", {
const char *old_proc_info= thd->proc_info;
@@ -5306,15 +5282,22 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
thd->proc_info= old_proc_info;});
if (lock_tables(thd, tables, counter, flags))
- DBUG_RETURN(TRUE);
+ goto err;
if (derived &&
(mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
(thd->fill_derived_tables() &&
mysql_handle_derived(thd->lex, &mysql_derived_filling))))
- DBUG_RETURN(TRUE); /* purecov: inspected */
+ goto err;
DBUG_RETURN(FALSE);
+err:
+ if (! thd->in_sub_stmt)
+ trans_rollback_stmt(thd); /* Necessary if derived handling failed. */
+ close_thread_tables(thd);
+ /* Don't keep locks for a failed statement. */
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ DBUG_RETURN(TRUE);
}
@@ -5340,13 +5323,24 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags)
{
+ DML_prelocking_strategy prelocking_strategy;
uint counter;
+ MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("open_normal_and_derived_tables");
DBUG_ASSERT(!thd->fill_derived_tables());
- if (open_tables(thd, &tables, &counter, flags) ||
+ if (open_tables(thd, &tables, &counter, flags, &prelocking_strategy) ||
mysql_handle_derived(thd->lex, &mysql_derived_prepare))
- DBUG_RETURN(TRUE); /* purecov: inspected */
+ goto end;
+
DBUG_RETURN(0);
+end:
+ /* No need to rollback statement transaction, it's not started. */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ close_thread_tables(thd);
+ /* Don't keep locks for a failed statement. */
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+
+ DBUG_RETURN(TRUE); /* purecov: inspected */
}
@@ -5607,6 +5601,14 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
/* We have to cleanup translation tables of views. */
tmp->cleanup_items();
}
+ /*
+ No need to commit/rollback the statement transaction: it's
+ either not started or we're filling in an INFORMATION_SCHEMA
+ table on the fly, and thus mustn't manipulate with the
+ transaction of the enclosing statement.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty() ||
+ (thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(start_of_statement_svp);
}
@@ -9034,7 +9036,8 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
MYSQL_LOCK_IGNORE_TIMEOUT))
{
lex->restore_backup_query_tables_list(&query_tables_list_backup);
- goto error;
+ thd->restore_backup_open_tables_state(backup);
+ DBUG_RETURN(TRUE);
}
for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global)
@@ -9045,11 +9048,6 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
lex->restore_backup_query_tables_list(&query_tables_list_backup);
DBUG_RETURN(FALSE);
-
-error:
- close_system_tables(thd, backup);
-
- DBUG_RETURN(TRUE);
}
@@ -9072,6 +9070,38 @@ close_system_tables(THD *thd, Open_tables_backup *backup)
}
+/**
+ A helper function to close a mysql.* table opened
+ in an auxiliary THD during bootstrap or in the main
+ connection, when we know that there are no locks
+ held by the connection due to a preceding implicit
+ commit.
+
+ This function assumes that there is no
+ statement transaction started for the operation
+ itself, since mysql.* tables are not transactional
+ and when they are used the binlog is off (DDL
+ binlogging is always statement-based.
+
+ We need this function since we'd like to not
+ just close the system table, but also release
+ the metadata lock on it.
+
+ Note, that in LOCK TABLES mode this function
+ does not release the metadata lock. But in this
+ mode the table can be opened only if it is locked
+ explicitly with LOCK TABLES.
+*/
+
+void
+close_mysql_tables(THD *thd)
+{
+ /* No need to commit/rollback statement transaction, it's not started. */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+}
+
/*
Open and lock one system table for update.
@@ -9143,16 +9173,7 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup)
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
}
else
- {
- /*
- If error in mysql_lock_tables(), open_ltable doesn't close the
- table. Thread kill during mysql_lock_tables() is such error. But
- open tables cannot be accepted when restoring the open tables
- state.
- */
- close_thread_tables(thd);
thd->restore_backup_open_tables_state(backup);
- }
thd->utime_after_lock= save_utime_after_lock;
DBUG_RETURN(table);
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 24669bd2597..b912f80d44f 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -240,6 +240,7 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
Open_tables_backup *backup);
void close_system_tables(THD *thd, Open_tables_backup *backup);
+void close_mysql_tables(THD *thd);
TABLE *open_system_table_for_update(THD *thd, TABLE_LIST *one_table);
TABLE *open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup);
void close_log_table(THD *thd, Open_tables_backup *backup);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index eb4d353db81..f3ef440c2f0 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -29,7 +29,6 @@
#include "sql_priv.h"
#include "unireg.h" // REQUIRED: for other includes
#include "sql_class.h"
-#include "lock.h" // unlock_global_read_lock, mysql_unlock_tables
#include "sql_cache.h" // query_cache_abort
#include "sql_base.h" // close_thread_tables
#include "sql_time.h" // date_time_format_copy
@@ -1817,12 +1816,6 @@ bool select_send::send_eof()
*/
ha_release_temporary_latches(thd);
- /* Unlock tables before sending packet to gain some speed */
- if (thd->lock && ! thd->locked_tables_mode)
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock=0;
- }
/*
Don't send EOF if we're in error condition (which implies we've already
sent or are sending an error)
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
index ca724ec262f..28d2bd8b4de 100644
--- a/sql/sql_cursor.cc
+++ b/sql/sql_cursor.cc
@@ -22,6 +22,7 @@
#include "sql_select.h"
#include "probes_mysql.h"
#include "sql_parse.h" // mysql_execute_command
+#include "sql_base.h"
/****************************************************************************
Declarations.
@@ -523,6 +524,7 @@ Sensitive_cursor::close()
thd->derived_tables= derived_tables;
thd->lock= lock;
+ close_thread_tables(thd);
/* Is expected to at least close tables and empty thd->change_list */
stmt_arena->cleanup_stmt();
diff --git a/sql/sql_do.cc b/sql/sql_do.cc
index 79e488ac2a5..085473c24b8 100644
--- a/sql/sql_do.cc
+++ b/sql/sql_do.cc
@@ -39,9 +39,10 @@ bool mysql_do(THD *thd, List<Item> &values)
/*
Rollback the effect of the statement, since next instruction
will clear the error and the rollback in the end of
- dispatch_command() won't work.
+ mysql_execute_command() won't work.
*/
- trans_rollback_stmt(thd);
+ if (! thd->in_sub_stmt)
+ trans_rollback_stmt(thd);
thd->clear_error(); // DO always is OK
}
my_ok(thd);
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 8e38cf5eed0..f1dddbb2eb5 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -59,7 +59,7 @@
#include "key.h" // key_copy
#include "sql_base.h" // insert_fields
#include "sql_select.h"
-#include <assert.h>
+#include "transaction.h"
#define HANDLER_TABLES_HASH_SIZE 120
@@ -309,9 +309,15 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
}
if (error)
{
+ /*
+ No need to rollback statement transaction, it's not started.
+ If called with reopen flag, no need to rollback either,
+ it will be done at statement end.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
close_thread_tables(thd);
- thd->set_open_tables(backup_open_tables);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ thd->set_open_tables(backup_open_tables);
if (!reopen)
my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
else
@@ -578,6 +584,11 @@ retry:
if (sql_handler_lock_error.need_reopen())
{
DBUG_ASSERT(!lock && !thd->is_error());
+ /*
+ Always close statement transaction explicitly,
+ so that the engine doesn't have to count locks.
+ */
+ trans_rollback_stmt(thd);
mysql_ha_close_table(thd, hash_tables);
goto retry;
}
@@ -764,12 +775,18 @@ retry:
num_rows++;
}
ok:
+ /*
+ Always close statement transaction explicitly,
+ so that the engine doesn't have to count locks.
+ */
+ trans_commit_stmt(thd);
mysql_unlock_tables(thd,lock);
my_eof(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);
err:
+ trans_rollback_stmt(thd);
mysql_unlock_tables(thd,lock);
err0:
DBUG_PRINT("exit",("ERROR"));
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index c783d74b363..cc0b6ba3a2d 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1862,7 +1862,10 @@ public:
while ((row=rows.get()))
delete row;
if (table)
+ {
close_thread_tables(&thd);
+ thd.mdl_context.release_transactional_locks();
+ }
mysql_mutex_lock(&LOCK_thread_count);
mysql_mutex_destroy(&mutex);
mysql_cond_destroy(&cond);
@@ -2414,6 +2417,8 @@ bool Delayed_insert::open_and_lock_table()
}
if (!(table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED))
{
+ /* To rollback InnoDB statement transaction. */
+ trans_rollback_stmt(&thd);
my_error(ER_DELAYED_NOT_SUPPORTED, MYF(ME_FATALERROR),
table_list.table_name);
return TRUE;
@@ -2480,12 +2485,6 @@ pthread_handler_t handle_delayed_insert(void *arg)
goto err;
}
- /*
- Open table requires an initialized lex in case the table is
- partitioned. The .frm file contains a partial SQL string which is
- parsed using a lex, that depends on initialized thd->lex.
- */
- lex_start(thd);
thd->lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock()
/*
Statement-based replication of INSERT DELAYED has problems with RAND()
@@ -2619,28 +2618,11 @@ pthread_handler_t handle_delayed_insert(void *arg)
}
err:
- /*
- mysql_lock_tables() can potentially start a transaction and write
- a table map. In the event of an error, that transaction has to be
- rolled back. We only need to roll back a potential statement
- transaction, since real transactions are rolled back in
- close_thread_tables().
-
- TODO: This is not true any more, table maps are generated on the
- first call to ha_*_row() instead. Remove code that are used to
- cover for the case outlined above.
- */
- trans_rollback_stmt(thd);
-
DBUG_LEAVE;
}
- /*
- di should be unlinked from the thread handler list and have no active
- clients
- */
-
close_thread_tables(thd); // Free the table
+ thd->mdl_context.release_transactional_locks();
di->table=0;
thd->killed= THD::KILL_CONNECTION; // If error
mysql_cond_broadcast(&di->cond_client); // Safety
@@ -2648,6 +2630,10 @@ pthread_handler_t handle_delayed_insert(void *arg)
mysql_mutex_lock(&LOCK_delayed_create); // Because of delayed_get_table
mysql_mutex_lock(&LOCK_delayed_insert);
+ /*
+ di should be unlinked from the thread handler list and have no active
+ clients
+ */
delete di;
mysql_mutex_unlock(&LOCK_delayed_insert);
mysql_mutex_unlock(&LOCK_delayed_create);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index aefddc0b6a5..eef139d5698 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -450,6 +450,9 @@ void lex_end(LEX *lex)
}
reset_dynamic(&lex->plugins);
+ delete lex->sphead;
+ lex->sphead= NULL;
+
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 8a94aea8aa7..53c2ca6fa39 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -115,6 +115,7 @@
"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);
const char *any_db="*any*"; // Special symbol for check_access
@@ -413,6 +414,9 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_FLUSH]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_RESET]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CHECK]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_SERVER]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_ALTER_SERVER]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_SERVER]= CF_AUTO_COMMIT_TRANS;
}
bool sqlcom_can_generate_row_events(const THD *thd)
@@ -568,7 +572,6 @@ static void handle_bootstrap_impl(THD *thd)
}
mysql_parse(thd, thd->query(), length, &parser_state);
- close_thread_tables(thd); // Free tables
bootstrap_error= thd->is_error();
thd->protocol->end_statement();
@@ -1139,13 +1142,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
char *beginning_of_next_stmt= (char*)
parser_state.m_lip.found_semicolon;
-
- thd->protocol->end_statement();
- query_cache_end_of_result(thd);
/*
Multiple queries exits, execute them individually
*/
- close_thread_tables(thd);
+ thd->protocol->end_statement();
+ query_cache_end_of_result(thd);
ulong length= (ulong)(packet_end - beginning_of_next_stmt);
log_slow_statement(thd);
@@ -1197,38 +1198,54 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
char *fields, *packet_end= packet + packet_length, *arg_end;
/* Locked closure of all tables */
TABLE_LIST table_list;
- LEX_STRING conv_name;
-
- /* used as fields initializator */
- lex_start(thd);
+ LEX_STRING table_name;
+ LEX_STRING db;
+ /*
+ SHOW statements should not add the used tables to the list of tables
+ used in a transaction.
+ */
+ MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]);
- bzero((char*) &table_list,sizeof(table_list));
- if (thd->copy_db_to(&table_list.db, &table_list.db_length))
+ if (thd->copy_db_to(&db.str, &db.length))
break;
/*
We have name + wildcard in packet, separated by endzero
*/
arg_end= strend(packet);
uint arg_length= arg_end - packet;
-
+
/* Check given table name length. */
if (arg_length >= packet_length || arg_length > NAME_LEN)
{
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
break;
}
- thd->convert_string(&conv_name, system_charset_info,
+ thd->convert_string(&table_name, system_charset_info,
packet, arg_length, thd->charset());
- if (check_table_name(conv_name.str, conv_name.length, FALSE))
+ if (check_table_name(table_name.str, table_name.length, FALSE))
{
/* this is OK due to convert_string() null-terminating the string */
- my_error(ER_WRONG_TABLE_NAME, MYF(0), conv_name.str);
+ my_error(ER_WRONG_TABLE_NAME, MYF(0), table_name.str);
break;
}
-
- table_list.alias= table_list.table_name= conv_name.str;
packet= arg_end + 1;
+ mysql_reset_thd_for_next_command(thd);
+ lex_start(thd);
+ /* Must be before we init the table list. */
+ if (lower_case_table_names)
+ table_name.length= my_casedn_str(files_charset_info, table_name.str);
+ table_list.init_one_table(db.str, db.length, table_name.str,
+ table_name.length, table_name.str, TL_READ);
+ /*
+ Init TABLE_LIST members necessary when the undelrying
+ table is view.
+ */
+ table_list.select_lex= &(thd->lex->select_lex);
+ thd->lex->
+ select_lex.table_list.link_in_list(&table_list,
+ &table_list.next_local);
+ thd->lex->add_to_query_tables(&table_list);
if (is_infoschema_db(table_list.db, table_list.db_length))
{
@@ -1242,32 +1259,23 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
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);
- if (check_access(thd, SELECT_ACL, table_list.db,
- &table_list.grant.privilege,
- &table_list.grant.m_internal,
- 0, 0))
- break;
- if (check_grant(thd, SELECT_ACL, &table_list, TRUE, UINT_MAX, FALSE))
+ if (check_table_access(thd, SELECT_ACL, &table_list,
+ TRUE, UINT_MAX, FALSE))
break;
- /* init structures for VIEW processing */
- table_list.select_lex= &(thd->lex->select_lex);
-
- lex_start(thd);
- mysql_reset_thd_for_next_command(thd);
-
- thd->lex->
- select_lex.table_list.link_in_list(&table_list,
- &table_list.next_local);
- thd->lex->add_to_query_tables(&table_list);
- init_mdl_requests(&table_list);
-
- /* switch on VIEW optimisation: do not fill temporary tables */
+ /*
+ Turn on an optimization relevant if the underlying table
+ is a view: do not fill derived tables.
+ */
thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
+
mysqld_list_fields(thd,&table_list,fields);
thd->lex->unit.cleanup();
+ /* No need to rollback statement transaction, it's not started. */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+
thd->cleanup_after_query();
break;
}
@@ -1315,7 +1323,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
ulong options= (ulong) (uchar) packet[0];
if (trans_commit_implicit(thd))
break;
- close_thread_tables(thd);
thd->mdl_context.release_transactional_locks();
if (check_global_access(thd,RELOAD_ACL))
break;
@@ -1377,7 +1384,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
DBUG_PRINT("quit",("Got shutdown command for level %u", level));
general_log_print(thd, command, NullS);
my_eof(thd);
- close_thread_tables(thd); // Free before kill
kill_mysql();
error=TRUE;
break;
@@ -1480,29 +1486,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
break;
}
-
- /* report error issued during command execution */
- if (thd->killed_errno())
- {
- if (! thd->stmt_da->is_set())
- thd->send_kill_message();
- }
- if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
- {
- thd->killed= THD::NOT_KILLED;
- thd->mysys_var->abort= 0;
- }
-
- /* If commit fails, we should be able to reset the OK status. */
- thd->stmt_da->can_overwrite_status= TRUE;
- thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
- thd->stmt_da->can_overwrite_status= FALSE;
-
- thd->transaction.stmt.reset();
-
- thd->proc_info= "closing tables";
- /* Free tables */
- close_thread_tables(thd);
+ DBUG_ASSERT(thd->derived_tables == NULL &&
+ (thd->open_tables == NULL ||
+ (thd->locked_tables_mode == LTM_LOCK_TABLES)));
thd->protocol->end_statement();
query_cache_end_of_result(thd);
@@ -1715,6 +1701,9 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
In brief: take exclusive locks, expel tables from the table
cache, reopen the tables, enter the 'LOCKED TABLES' mode,
downgrade the locks.
+ Note: the function is written to be called from
+ mysql_execute_command(), it is not reusable in arbitrary
+ execution context.
Required privileges
-------------------
@@ -1816,9 +1805,9 @@ static bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
&lock_tables_prelocking_strategy) ||
thd->locked_tables_list.init_locked_tables(thd))
{
- close_thread_tables(thd);
goto error;
}
+ thd->variables.option_bits|= OPTION_TABLE_LOCK;
/*
Downgrade the exclusive locks.
@@ -2041,6 +2030,7 @@ mysql_execute_command(THD *thd)
thd->work_part_info= 0;
#endif
+ DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt);
/*
In many cases first table of main SELECT_LEX have special meaning =>
check that it is first table in global list and relink it first in
@@ -2222,8 +2212,7 @@ mysql_execute_command(THD *thd)
/* Commit the normal transaction if one is active. */
if (trans_commit_implicit(thd))
goto error;
- /* Close tables and release metadata locks. */
- close_thread_tables(thd);
+ /* Release metadata locks acquired in this transaction. */
thd->mdl_context.release_transactional_locks();
}
@@ -3536,24 +3525,27 @@ end_with_restore_list:
done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes
false, mysqldump will not work.
*/
- thd->locked_tables_list.unlock_locked_tables(thd);
if (thd->variables.option_bits & OPTION_TABLE_LOCK)
{
- trans_commit_implicit(thd);
+ res= trans_commit_implicit(thd);
+ thd->locked_tables_list.unlock_locked_tables(thd);
thd->mdl_context.release_transactional_locks();
thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
}
if (thd->global_read_lock.is_acquired())
thd->global_read_lock.unlock_global_read_lock(thd);
+ if (res)
+ goto error;
my_ok(thd);
break;
case SQLCOM_LOCK_TABLES:
+ /* We must end the transaction first, regardless of anything */
+ res= trans_commit_implicit(thd);
thd->locked_tables_list.unlock_locked_tables(thd);
- /* we must end the trasaction first, regardless of anything */
- if (trans_commit_implicit(thd))
- goto error;
- /* release transactional metadata locks. */
+ /* Release transactional metadata locks. */
thd->mdl_context.release_transactional_locks();
+ if (res)
+ goto error;
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
FALSE, UINT_MAX, FALSE))
goto error;
@@ -3576,17 +3568,14 @@ end_with_restore_list:
if (res)
{
+ trans_rollback_stmt(thd);
/*
Need to end the current transaction, so the storage engine (InnoDB)
can free its locks if LOCK TABLES locked some tables before finding
that it can't lock a table in its list
*/
- trans_rollback_stmt(thd);
trans_commit_implicit(thd);
- /*
- Close tables and release metadata locks otherwise a later call to
- close_thread_tables might not release the locks if autocommit is off.
- */
+ /* Close tables and release metadata locks. */
close_thread_tables(thd);
DBUG_ASSERT(!thd->locked_tables_mode);
thd->mdl_context.release_transactional_locks();
@@ -4205,9 +4194,7 @@ end_with_restore_list:
locks in the MDL context, so there is no risk to
deadlock.
*/
- trans_commit_implicit(thd);
- close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ close_mysql_tables(thd);
/*
Check if the definer exists on slave,
then use definer privilege to insert routine privileges to mysql.procs_priv.
@@ -4484,9 +4471,7 @@ create_sp_error:
locks in the MDL context, so there is no risk to
deadlock.
*/
- trans_commit_implicit(thd);
- close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ close_mysql_tables(thd);
if (sp_automatic_privileges && !opt_noacl &&
sp_revoke_privileges(thd, db, name,
@@ -4778,17 +4763,60 @@ finish:
DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() ||
thd->in_multi_stmt_transaction_mode());
+
+ if (! thd->in_sub_stmt)
+ {
+ /* report error issued during command execution */
+ if (thd->killed_errno())
+ {
+ if (! thd->stmt_da->is_set())
+ thd->send_kill_message();
+ }
+ if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
+ {
+ thd->killed= THD::NOT_KILLED;
+ thd->mysys_var->abort= 0;
+ }
+ if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
+ trans_rollback_stmt(thd);
+ else
+ {
+ /* If commit fails, we should be able to reset the OK status. */
+ thd->stmt_da->can_overwrite_status= TRUE;
+ trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
+ }
+ }
+
+ lex->unit.cleanup();
+ /* Free tables */
+ thd_proc_info(thd, "closing tables");
+ close_thread_tables(thd);
+ thd_proc_info(thd, 0);
+
if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
{
+ /* No transaction control allowed in sub-statements. */
+ DBUG_ASSERT(! thd->in_sub_stmt);
/* If commit fails, we should be able to reset the OK status. */
thd->stmt_da->can_overwrite_status= TRUE;
- /* 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. */
trans_commit_implicit(thd);
thd->stmt_da->can_overwrite_status= FALSE;
- /* Close tables and release metadata locks. */
- close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
+ {
+ /*
+ - If inside a multi-statement transaction,
+ defer the release of metadata locks until the current
+ transaction is either committed or rolled back. This prevents
+ other statements from modifying the table for the entire
+ duration of this transaction. This provides commit ordering
+ and guarantees serializability across multiple transactions.
+ - If in autocommit mode, or outside a transactional context,
+ automatically release metadata locks of the current statement.
+ */
thd->mdl_context.release_transactional_locks();
}
@@ -5886,12 +5914,6 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
query_cache_abort(&thd->query_cache_tls);
}
- if (thd->lex->sphead)
- {
- delete thd->lex->sphead;
- thd->lex->sphead= 0;
- }
- lex->unit.cleanup();
thd_proc_info(thd, "freeing items");
thd->end_statement();
thd->cleanup_after_query();
@@ -6997,11 +7019,15 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
only_kill_query Should it kill the query or the connection
*/
+static
void sql_kill(THD *thd, ulong id, bool only_kill_query)
{
uint error;
if (!(error= kill_one_thread(thd, id, only_kill_query)))
- my_ok(thd);
+ {
+ if (! thd->killed)
+ my_ok(thd);
+ }
else
my_error(error, MYF(0), id);
}
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index 475811d45b7..7304836ed0f 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -51,7 +51,6 @@ bool parse_sql(THD *thd,
Object_creation_ctx *creation_ctx);
uint kill_one_thread(THD *thd, ulong id, bool only_kill_query);
-void sql_kill(THD *thd, ulong id, bool only_kill_query);
void free_items(Item *item);
void cleanup_items(Item *item);
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index d7ff753dfd0..2c8b5d67d04 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -59,7 +59,7 @@
#include "my_md5.h"
#include "transaction.h"
-#include "sql_base.h" // close_thread_tables
+#include "sql_base.h" // close_all_tables_for_name
#include "sql_table.h" // build_table_filename,
// build_table_shadow_filename,
// table_to_filename
@@ -6758,7 +6758,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
table_list, FALSE, NULL,
written_bin_log));
err:
- close_thread_tables(thd);
DBUG_RETURN(TRUE);
}
#endif
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 2b6be403fc6..27cd03d8edd 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -21,7 +21,7 @@
#include "sql_locale.h"
#include "sql_plugin.h"
#include "sql_parse.h" // check_table_access
-#include "sql_base.h" // close_thread_tables
+#include "sql_base.h" // close_mysql_tables
#include "key.h" // key_copy
#include "sql_show.h" // remove_status_vars, add_status_vars
#include "strfunc.h" // find_set
@@ -1511,8 +1511,8 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
sql_print_error(ER(ER_GET_ERRNO), my_errno);
end_read_record(&read_record_info);
table->m_needs_reopen= TRUE; // Force close to free memory
+ close_mysql_tables(new_thd);
end:
- close_thread_tables(new_thd);
/* Remember that we don't have a THD */
my_pthread_setspecific_ptr(THR_THD, 0);
DBUG_VOID_RETURN;
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 089e751900e..151a135125e 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -90,7 +90,7 @@ When one supplies long data for a placeholder:
#include "set_var.h"
#include "sql_prepare.h"
#include "sql_parse.h" // insert_precheck, update_precheck, delete_precheck
-#include "sql_base.h" // close_thread_tables
+#include "sql_base.h" // open_normal_and_derived_tables
#include "sql_cache.h" // query_cache_*
#include "sql_view.h" // create_view_precheck
#include "sql_delete.h" // mysql_prepare_delete
@@ -2989,12 +2989,6 @@ Execute_sql_statement::execute_server_code(THD *thd)
error= mysql_execute_command(thd);
- if (thd->killed_errno())
- {
- if (! thd->stmt_da->is_set())
- thd->send_kill_message();
- }
-
/* report error issued during command execution */
if (error == 0 && thd->spcont == NULL)
general_log_write(thd, COM_STMT_EXECUTE,
@@ -3102,13 +3096,8 @@ void Prepared_statement::cleanup_stmt()
DBUG_ENTER("Prepared_statement::cleanup_stmt");
DBUG_PRINT("enter",("stmt: 0x%lx", (long) this));
- delete lex->sphead;
- lex->sphead= 0;
- /* The order is important */
- lex->unit.cleanup();
cleanup_items(free_list);
thd->cleanup_after_query();
- close_thread_tables(thd);
thd->rollback_item_tree_changes();
DBUG_VOID_RETURN;
@@ -3272,21 +3261,16 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
to PREPARE stmt FROM "CREATE PROCEDURE ..."
*/
DBUG_ASSERT(lex->sphead == NULL || error != 0);
- if (lex->sphead)
- {
- delete lex->sphead;
- lex->sphead= NULL;
- }
+ /* The order is important */
+ lex->unit.cleanup();
+
+ /* No need to commit statement transaction, it's not started. */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
lex_end(lex);
cleanup_stmt();
- /*
- If not inside a multi-statement transaction, the metadata
- locks have already been released and our savepoint points
- to ticket which has been released as well.
- */
- if (thd->in_multi_stmt_transaction_mode())
- thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
thd->restore_backup_statement(this, &stmt_backup);
thd->stmt_arena= old_stmt_arena;
@@ -3393,11 +3377,6 @@ Prepared_statement::set_parameters(String *expanded_query,
and execute of a new statement. If this happens repeatedly
more than MAX_REPREPARE_ATTEMPTS times, we give up.
- In future we need to be able to keep the metadata locks between
- prepare and execute, but right now open_and_lock_tables(), as
- well as close_thread_tables() are buried deep inside
- execution code (mysql_execute_command()).
-
@return TRUE if an error, FALSE if success
@retval TRUE either MAX_REPREPARE_ATTEMPTS has been reached,
or some general error
@@ -3484,11 +3463,6 @@ Prepared_statement::execute_server_runnable(Server_runnable *server_runnable)
error= server_runnable->execute_server_code(thd);
- delete lex->sphead;
- lex->sphead= 0;
- /* The order is important */
- lex->unit.cleanup();
- close_thread_tables(thd);
thd->cleanup_after_query();
thd->restore_active_arena(this, &stmt_backup);
diff --git a/sql/sql_priv.h b/sql/sql_priv.h
index 604890ffbe5..708608fc2f1 100644
--- a/sql/sql_priv.h
+++ b/sql/sql_priv.h
@@ -135,6 +135,16 @@ extern char err_shared_dir[];
Type of locks to be acquired is specified directly.
*/
#define SELECT_HIGH_PRIORITY (1ULL << 34) // SELECT, user
+/**
+ Is set in slave SQL thread when there was an
+ error on master, which, when is not reproducible
+ on slave (i.e. the query succeeds on slave),
+ is not terminal to the state of repliation,
+ and should be ignored. The slave SQL thread,
+ however, needs to rollback the effects of the
+ succeeded statement to keep replication consistent.
+*/
+#define OPTION_MASTER_SQL_ERROR (1ULL << 35)
/* The rest of the file is included in the server only */
diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc
index c7f9cf0b132..cfbf8e96719 100644
--- a/sql/sql_servers.cc
+++ b/sql/sql_servers.cc
@@ -36,7 +36,7 @@
#include "sql_priv.h"
#include "sql_servers.h"
#include "unireg.h"
-#include "sql_base.h" // close_thread_tables
+#include "sql_base.h" // close_mysql_tables
#include "records.h" // init_read_record, end_read_record
#include "hash_filo.h"
#include <m_ctype.h>
@@ -280,9 +280,7 @@ bool servers_reload(THD *thd)
}
end:
- trans_commit_implicit(thd);
- close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ close_mysql_tables(thd);
DBUG_PRINT("info", ("unlocking servers_cache"));
mysql_rwlock_unlock(&THR_LOCK_servers);
DBUG_RETURN(return_val);
@@ -535,6 +533,7 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server)
{
int error;
DBUG_ENTER("insert_server_record");
+ tmp_disable_binlog(table->in_use);
table->use_all_columns();
empty_record(table);
@@ -571,6 +570,8 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server)
}
else
error= ER_FOREIGN_SERVER_EXISTS;
+
+ reenable_binlog(table->in_use);
DBUG_RETURN(error);
}
@@ -625,7 +626,7 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
error= delete_server_record(table, name.str, name.length);
/* close the servers table before we call closed_cached_connection_tables */
- close_thread_tables(thd);
+ close_mysql_tables(thd);
if (close_cached_connection_tables(thd, TRUE, &name))
{
@@ -880,6 +881,7 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server)
{
int error=0;
DBUG_ENTER("update_server_record");
+ tmp_disable_binlog(table->in_use);
table->use_all_columns();
/* set the field that's the PK to the value we're looking for */
table->field[0]->store(server->server_name,
@@ -913,6 +915,7 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server)
}
end:
+ reenable_binlog(table->in_use);
DBUG_RETURN(error);
}
@@ -938,6 +941,7 @@ delete_server_record(TABLE *table,
{
int error;
DBUG_ENTER("delete_server_record");
+ tmp_disable_binlog(table->in_use);
table->use_all_columns();
/* set the field that's the PK to the value we're looking for */
@@ -959,6 +963,7 @@ delete_server_record(TABLE *table,
table->file->print_error(error, MYF(0));
}
+ reenable_binlog(table->in_use);
DBUG_RETURN(error);
}
@@ -1050,7 +1055,7 @@ int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
error= update_server(thd, existing, altered);
/* close the servers table before we call closed_cached_connection_tables */
- close_thread_tables(thd);
+ close_mysql_tables(thd);
if (close_cached_connection_tables(thd, FALSE, &name))
{
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index cacfcedff23..42b3dfaad83 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -2285,18 +2285,13 @@ err:
{
/*
Under LOCK TABLES we should release meta-data locks on the tables
- which were dropped. Otherwise we can rely on close_thread_tables()
- doing this. Unfortunately in this case we are likely to get more
- false positives in try_acquire_lock() function. So
- it makes sense to remove exclusive meta-data locks in all cases.
+ which were dropped.
Leave LOCK TABLES mode if we managed to drop all tables which were
locked. Additional check for 'non_temp_tables_count' is to avoid
leaving LOCK TABLES mode if we have dropped only temporary tables.
*/
- if (! thd->locked_tables_mode)
- thd->mdl_context.release_transactional_locks();
- else
+ if (thd->locked_tables_mode)
{
if (thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0)
{
@@ -2305,7 +2300,8 @@ err:
}
for (table= tables; table; table= table->next_local)
{
- if (table->mdl_request.ticket)
+ /* Drop locks for all successfully dropped tables. */
+ if (table->table == NULL && table->mdl_request.ticket)
{
/*
Under LOCK TABLES we may have several instances of table open
@@ -2316,6 +2312,10 @@ err:
}
}
}
+ /*
+ Rely on the caller to implicitly commit the transaction
+ and release metadata locks.
+ */
}
end:
@@ -4214,8 +4214,14 @@ warn:
}
-/*
- Database and name-locking aware wrapper for mysql_create_table_no_lock(),
+/**
+ Implementation of SQLCOM_CREATE_TABLE.
+
+ Take the metadata locks (including a shared lock on the affected
+ schema) and create the table. Is written to be called from
+ mysql_execute_command(), to which it delegates the common parts
+ with other commands (i.e. implicit commit before and after,
+ close of thread tables.
*/
bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
@@ -4231,7 +4237,7 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
if (open_and_lock_tables(thd, thd->lex->query_tables, FALSE, 0))
{
result= TRUE;
- goto unlock;
+ goto end;
}
/* Got lock. */
@@ -4253,16 +4259,7 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
!(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
- {
- /*
- close_thread_tables() takes care about both closing open tables (which
- might be still around in case of error) and releasing metadata locks.
- */
- close_thread_tables(thd);
- }
-
-unlock:
+end:
DBUG_RETURN(result);
}
@@ -4752,6 +4749,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
trans_rollback_stmt(thd);
trans_rollback(thd);
close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
DBUG_PRINT("admin", ("simple error, admin next table"));
continue;
case -1: // error, message could be written to net
@@ -5038,11 +5036,11 @@ send_result_message:
trans_commit_stmt(thd);
trans_commit(thd);
close_thread_tables(thd);
- table->table= NULL;
thd->mdl_context.release_transactional_locks();
+ table->table= NULL;
if (!result_code) // recreation went ok
{
- /* Clear the ticket released in close_thread_tables(). */
+ /* Clear the ticket released above. */
table->mdl_request.ticket= NULL;
DEBUG_SYNC(thd, "ha_admin_open_ltable");
table->mdl_request.set_type(MDL_SHARED_WRITE);
@@ -6729,13 +6727,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
goto err;
DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
- /* COND_refresh will be signaled in close_thread_tables() */
break;
case DISABLE:
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
goto err;
error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
- /* COND_refresh will be signaled in close_thread_tables() */
break;
default:
DBUG_ASSERT(FALSE);
@@ -6821,8 +6817,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{
/*
Under LOCK TABLES we should adjust meta-data locks before finishing
- statement. Otherwise we can rely on close_thread_tables() releasing
- them.
+ statement. Otherwise we can rely on them being released
+ along with the implicit commit.
*/
if (new_name != table_name || new_db != db)
{
@@ -7360,8 +7356,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
5) Write statement to the binary log.
6) If we are under LOCK TABLES and do ALTER TABLE ... RENAME we
remove placeholders and release metadata locks.
- 7) If we are not not under LOCK TABLES we rely on close_thread_tables()
- call to remove placeholders and releasing metadata locks.
+ 7) If we are not not under LOCK TABLES we rely on the caller
+ (mysql_execute_command()) to release metadata locks.
*/
thd_proc_info(thd, "rename result table");
@@ -7990,7 +7986,13 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
}
}
thd->clear_error();
+ if (! thd->in_sub_stmt)
+ trans_rollback_stmt(thd);
close_thread_tables(thd);
+ /*
+ Don't release metadata locks, this will be done at
+ statement end.
+ */
table->table=0; // For query cache
}
if (protocol->write())
@@ -8000,10 +8002,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
my_eof(thd);
DBUG_RETURN(FALSE);
- err:
- close_thread_tables(thd); // Shouldn't be needed
- if (table)
- table->table=0;
+err:
DBUG_RETURN(TRUE);
}
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 2f084c369b6..a5664b00287 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -540,9 +540,9 @@ end:
}
/*
- If we are under LOCK TABLES we should restore original state of meta-data
- locks. Otherwise call to close_thread_tables() will take care about both
- TABLE instance created by open_n_lock_single_table() and metadata lock.
+ If we are under LOCK TABLES we should restore original state of
+ meta-data locks. Otherwise all locks will be released along
+ with the implicit commit.
*/
if (thd->locked_tables_mode && tables && lock_upgrade_done)
mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
@@ -1321,6 +1321,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
thd->reset_db((char*) db, strlen(db));
while ((trg_create_str= it++))
{
+ sp_head *sp;
trg_sql_mode= itm++;
LEX_STRING *trg_definer= it_definer++;
@@ -1357,13 +1358,14 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
*/
lex.set_trg_event_type_for_tables();
- lex.sphead->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode);
-
int event= lex.trg_chistics.event;
int action_time= lex.trg_chistics.action_time;
- lex.sphead->set_creation_ctx(creation_ctx);
- triggers->bodies[event][action_time]= lex.sphead;
+ sp= triggers->bodies[event][action_time]= lex.sphead;
+ lex.sphead= NULL; /* Prevent double cleanup. */
+
+ sp->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode);
+ sp->set_creation_ctx(creation_ctx);
if (!trg_definer->length)
{
@@ -1376,27 +1378,26 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRG_NO_DEFINER, ER(ER_TRG_NO_DEFINER),
(const char*) db,
- (const char*) lex.sphead->m_name.str);
+ (const char*) sp->m_name.str);
/*
Set definer to the '' to correct displaying in the information
schema.
*/
- lex.sphead->set_definer((char*) "", 0);
+ sp->set_definer((char*) "", 0);
/*
Triggers without definer information are executed under the
authorization of the invoker.
*/
- lex.sphead->m_chistics->suid= SP_IS_NOT_SUID;
+ sp->m_chistics->suid= SP_IS_NOT_SUID;
}
else
- lex.sphead->set_definer(trg_definer->str, trg_definer->length);
+ sp->set_definer(trg_definer->str, trg_definer->length);
- if (triggers->names_list.push_back(&lex.sphead->m_name,
- &table->mem_root))
+ if (triggers->names_list.push_back(&sp->m_name, &table->mem_root))
goto err_with_lex_cleanup;
if (!(on_table_name= alloc_lex_string(&table->mem_root)))
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index 3d197303fb1..ba1d0ceadeb 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -33,7 +33,7 @@
#include "sql_priv.h"
#include "unireg.h"
-#include "sql_base.h" // close_thread_tables
+#include "sql_base.h" // close_mysql_tables
#include "sql_parse.h" // check_identifier_name
#include "sql_table.h" // write_bin_log
#include "records.h" // init_read_record, end_read_record
@@ -251,7 +251,7 @@ void udf_init()
table->m_needs_reopen= TRUE; // Force close to free memory
end:
- close_thread_tables(new_thd);
+ close_mysql_tables(new_thd);
delete new_thd;
/* Remember that we don't have a THD */
my_pthread_setspecific_ptr(THR_THD, 0);
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 6e95961ebb0..9e212fb95e9 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -2203,14 +2203,21 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type)
thd->variables.option_bits & OPTION_NOT_AUTOCOMMIT)
{ // activating autocommit
- if (trans_commit(thd))
+ if (trans_commit_stmt(thd) || trans_commit(thd))
{
thd->variables.option_bits&= ~OPTION_AUTOCOMMIT;
return true;
}
- close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
-
+ /*
+ Don't close thread tables or release metadata locks: if we do so, we
+ risk releasing locks/closing tables of expressions used to assign
+ other variables, as in:
+ set @var=my_stored_function1(), @@autocommit=1, @var2=(select max(a)
+ from my_table), ...
+ The locks will be released at statement end anyway, as SET
+ statement that assigns autocommit is marked to commit
+ transaction implicitly at the end (@sa stmt_causes_implicitcommit()).
+ */
thd->variables.option_bits&=
~(OPTION_BEGIN | OPTION_KEEP_LOG | OPTION_NOT_AUTOCOMMIT);
thd->transaction.all.modified_non_trans_table= false;
diff --git a/sql/transaction.cc b/sql/transaction.cc
index f6786f20dcf..a28fba8805d 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -28,6 +28,12 @@ static bool trans_check(THD *thd)
enum xa_states xa_state= thd->transaction.xid_state.xa_state;
DBUG_ENTER("trans_check");
+ /*
+ Always commit statement transaction before manipulating with
+ the normal one.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+
if (unlikely(thd->in_sub_stmt))
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
if (xa_state != XA_NOTR)
@@ -252,6 +258,14 @@ bool trans_commit_stmt(THD *thd)
{
DBUG_ENTER("trans_commit_stmt");
int res= FALSE;
+ /*
+ We currently don't invoke commit/rollback at end of
+ a sub-statement. In future, we perhaps should take
+ a savepoint for each nested statement, and release the
+ savepoint when statement has succeeded.
+ */
+ DBUG_ASSERT(! thd->in_sub_stmt);
+
if (thd->transaction.stmt.ha_list)
{
res= ha_commit_trans(thd, FALSE);
@@ -267,6 +281,9 @@ bool trans_commit_stmt(THD *thd)
RUN_HOOK(transaction, after_rollback, (thd, FALSE));
else
RUN_HOOK(transaction, after_commit, (thd, FALSE));
+
+ thd->transaction.stmt.reset();
+
DBUG_RETURN(test(res));
}
@@ -283,6 +300,14 @@ bool trans_rollback_stmt(THD *thd)
{
DBUG_ENTER("trans_rollback_stmt");
+ /*
+ We currently don't invoke commit/rollback at end of
+ a sub-statement. In future, we perhaps should take
+ a savepoint for each nested statement, and release the
+ savepoint when statement has succeeded.
+ */
+ DBUG_ASSERT(! thd->in_sub_stmt);
+
if (thd->transaction.stmt.ha_list)
{
ha_rollback_trans(thd, FALSE);
@@ -294,6 +319,8 @@ bool trans_rollback_stmt(THD *thd)
RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+ thd->transaction.stmt.reset();
+
DBUG_RETURN(FALSE);
}
diff --git a/sql/tztime.cc b/sql/tztime.cc
index af8574c38f1..43d43123158 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -50,13 +50,6 @@
// MYSQL_LOCK_IGNORE_TIMEOUT
/*
- This forward declaration is needed because including sql_base.h
- causes further includes. [TODO] Eliminate this forward declaration
- and include a file with the prototype instead.
-*/
-extern void close_thread_tables(THD *thd);
-
-/*
Now we don't use abbreviations in server but we will do this in future.
*/
#if defined(TZINFO2SQL) || defined(TESTTIME)
@@ -1784,10 +1777,7 @@ end_with_setting_default_tz:
end_with_close:
if (time_zone_tables_exist)
- {
- close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
- }
+ close_mysql_tables(thd);
end_with_cleanup: