A pre-requisite patch for the fix for Bug#52044.

This patch also fixes Bug#55452 "SET PASSWORD is
replicated twice in RBR mode".

The goal of this patch is to remove the release of 
metadata locks from close_thread_tables().
This is necessary to not mistakenly release
the locks in the course of a multi-step
operation that involves multiple close_thread_tables()
or close_tables_for_reopen().

On the same token, move statement commit outside 
close_thread_tables().

Other cleanups:
Cleanup COM_FIELD_LIST.
Don't call close_thread_tables() in COM_SHUTDOWN -- there
are no open tables there that can be closed (we leave
the locked tables mode in THD destructor, and this
close_thread_tables() won't leave it anyway).

Make open_and_lock_tables() and open_and_lock_tables_derived()
call close_thread_tables() upon failure.
Remove the calls to close_thread_tables() that are now
unnecessary.

Simplify the back off condition in Open_table_context.

Streamline metadata lock handling in LOCK TABLES 
implementation.

Add asserts to ensure correct life cycle of 
statement transaction in a session.

Remove a piece of dead code that has also become redundant
after the fix for Bug 37521.
This commit is contained in:
Konstantin Osipov 2010-07-27 14:25:53 +04:00
parent c67cf159e9
commit ec2c3bf2c1
42 changed files with 590 additions and 468 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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-

View File

@ -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;
###########################################################################

View File

@ -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();

View File

@ -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));
}
@ -933,34 +935,14 @@ 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));

View File

@ -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);

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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; }
};

View File

@ -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

View File

@ -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,25 +3444,7 @@ 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.
*/
@ -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);
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);

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);
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,13 +2816,28 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
DBUG_PRINT("info",("exec_core returned: %d", res));
}
/*
Call after unit->cleanup() to close open table
key read.
*/
if (open_tables)
{
m_lex->unit.cleanup();
thd_proc_info(thd, "closing tables");
/* 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);
if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
thd->mdl_context.release_transactional_locks();
}
if (m_lex->query_tables_own_last)
{
/*

View File

@ -682,6 +682,8 @@ public:
{
if (m_lex_resp)
{
/* Prevent endless recursion. */
m_lex->sphead= NULL;
lex_end(m_lex);
delete m_lex;
}

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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();

View File

@ -39,8 +39,9 @@ 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.
*/
if (! thd->in_sub_stmt)
trans_rollback_stmt(thd);
thd->clear_error(); // DO always is OK
}

View File

@ -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"));

View File

@ -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);

View File

@ -450,6 +450,9 @@ void lex_end(LEX *lex)
}
reset_dynamic(&lex->plugins);
delete lex->sphead;
lex->sphead= NULL;
DBUG_VOID_RETURN;
}

View File

@ -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,14 +1198,16 @@ 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
@ -1218,17 +1221,31 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
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))
if (check_table_access(thd, SELECT_ACL, &table_list,
TRUE, UINT_MAX, FALSE))
break;
if (check_grant(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 (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
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;
/* Commit or rollback the statement transaction. */
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
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 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)))
{
if (! thd->killed)
my_ok(thd);
}
else
my_error(error, MYF(0), id);
}

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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 */

View File

@ -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))
{

View File

@ -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())
@ -8001,9 +8003,6 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
DBUG_RETURN(FALSE);
err:
close_thread_tables(thd); // Shouldn't be needed
if (table)
table->table=0;
DBUG_RETURN(TRUE);
}

View File

@ -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)))

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -49,13 +49,6 @@
#include "lock.h" // MYSQL_LOCK_IGNORE_FLUSH,
// 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.
*/
@ -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: