Commit e744c687 authored by Monty's avatar Monty

Merge remote-tracking branch 'origin/10.0' into 10.1

parents 5797cbaf a816aa06
......@@ -2174,6 +2174,66 @@ t1 CREATE TABLE `t1` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8
DROP TABLE t1;
#
# MDEV-15308
# Assertion `ha_alter_info->alter_info->drop_list.elements > 0' failed
# in ha_innodb::prepare_inplace_alter_table
#
CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;
ALTER TABLE t1 DROP FOREIGN KEY IF EXISTS fk, DROP COLUMN b;
Warnings:
Note 1091 Can't DROP 'fk'; check that column/key exists
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;
ALTER TABLE t1 DROP INDEX IF EXISTS fk, DROP COLUMN b;
Warnings:
Note 1091 Can't DROP 'fk'; check that column/key exists
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT, c INT, KEY(c)) ENGINE=InnoDB;
ALTER TABLE t1 DROP FOREIGN KEY IF EXISTS fk, DROP COLUMN c;
Warnings:
Note 1091 Can't DROP 'fk'; check that column/key exists
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT, c INT, KEY c1(c)) ENGINE=InnoDB;
ALTER TABLE t1 DROP FOREIGN KEY IF EXISTS fk, DROP INDEX c1;
Warnings:
Note 1091 Can't DROP 'fk'; check that column/key exists
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
`c` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;
ALTER TABLE t1 DROP INDEX IF EXISTS fk, DROP COLUMN IF EXISTS c;
Warnings:
Note 1091 Can't DROP 'fk'; check that column/key exists
Note 1091 Can't DROP 'c'; check that column/key exists
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
DROP TABLE t1;
#
# End of 10.0 tests
#
#
......
......@@ -707,10 +707,9 @@ LOAD INDEX INTO CACHE t3;
Table Op Msg_type Msg_text
mysqltest_db1.t3 preload_keys status OK
#
# RENAME (doesn't work for temporary tables, thus should fail).
# RENAME should work for temporary tables
#
RENAME TABLE t3 TO t3_1;
ERROR 42000: INSERT, CREATE command denied to user 'mysqltest_u1'@'localhost' for table 't3_1'
#
# HANDLER OPEN/READ/CLOSE.
#
......
......@@ -67,3 +67,69 @@ ERROR HY000: 'test.v1' is not BASE TABLE
drop view v1;
drop table t1;
End of 5.0 tests
CREATE OR REPLACE TABLE t1 (a INT);
CREATE OR REPLACE TABLE t2 (a INT);
CREATE OR REPLACE TEMPORARY TABLE t1_tmp (b INT);
CREATE OR REPLACE TEMPORARY TABLE t2_tmp (b INT);
rename table t1 to t2;
ERROR 42S01: Table 't2' already exists
rename table t1 to tmp, tmp to t2;
ERROR 42S01: Table 't2' already exists
rename table t1_tmp to t2_tmp;
ERROR 42S01: Table 't2_tmp' already exists
rename table t1_tmp to tmp, tmp to t2_tmp;
ERROR 42S01: Table 't2_tmp' already exists
show create table t1_tmp;
Table Create Table
t1_tmp CREATE TEMPORARY TABLE `t1_tmp` (
`b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
show create table t2_tmp;
Table Create Table
t2_tmp CREATE TEMPORARY TABLE `t2_tmp` (
`b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
rename table t1 to t1_tmp;
rename table t2_tmp to t2;
rename table t2 to tmp, tmp to t2;
rename table t1_tmp to tmp, tmp to t1_tmp;
show tables;
Tables_in_test
t1_tmp
t2
SHOW CREATE TABLE t1_tmp;
Table Create Table
t1_tmp CREATE TEMPORARY TABLE `t1_tmp` (
`b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1_tmp;
SHOW CREATE TABLE t1_tmp;
Table Create Table
t1_tmp CREATE TABLE `t1_tmp` (
`a` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1_tmp;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TEMPORARY TABLE `t2` (
`b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t2;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t2;
CREATE TABLE t1 (a INT);
insert into t1 values (1);
CREATE TEMPORARY TABLE t1 (b INT);
insert into t1 values (2);
RENAME TABLE t1 TO tmp, t1 TO t2;
select * from tmp;
b
2
select * from t2;
a
1
drop table tmp,t2;
CREATE TABLE t1 (i int);
RENAME TABLE t1 TO t2;
FLUSH TABLES;
DROP TABLE IF EXISTS t1, t2;
Warnings:
Note 1051 Unknown table 'test.t1'
......@@ -99,3 +99,13 @@ f2
3
unlock tables;
DROP TABLE t1,t2,tmp;
#
# MDEV-10378 Assertion `trn' failed in virtual int ha_maria::start_stmt
#
CREATE TABLE t1 (f1 VARCHAR(3), f2 INT, pk INT, PRIMARY KEY (pk)) ENGINE=Aria;
INSERT INTO t1 VALUES ('foo',10,1), ('foo',1,2);
LOCK TABLE t1 WRITE;
ALTER TABLE t1 ADD UNIQUE KEY (f1);
ERROR 23000: Duplicate entry 'foo' for key 'f1'
ALTER TABLE t1 ADD KEY (f2);
DROP TABLE t1;
......@@ -105,3 +105,15 @@ INSERT INTO t2 (f2) SELECT f3 FROM tmp AS tmp_alias;
select * from t2;
unlock tables;
DROP TABLE t1,t2,tmp;
--echo #
--echo # MDEV-10378 Assertion `trn' failed in virtual int ha_maria::start_stmt
--echo #
CREATE TABLE t1 (f1 VARCHAR(3), f2 INT, pk INT, PRIMARY KEY (pk)) ENGINE=Aria;
INSERT INTO t1 VALUES ('foo',10,1), ('foo',1,2);
LOCK TABLE t1 WRITE;
--error ER_DUP_ENTRY
ALTER TABLE t1 ADD UNIQUE KEY (f1);
ALTER TABLE t1 ADD KEY (f2);
DROP TABLE t1;
include/master-slave.inc
[connection master]
#
# MDEV-16229 Replication aborts with ER_VIEW_SELECT_TMPTABLE after
# half-failed RENAME
#
CREATE TABLE t1 (a INT);
CREATE TEMPORARY TABLE t1 (b INT);
RENAME TABLE t1 TO tmp, tmp TO t1;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TEMPORARY TABLE `t1` (
`b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
CREATE VIEW v AS SELECT * FROM t1;
ERROR HY000: View's SELECT refers to a temporary table 't1'
RENAME TABLE t1 TO tmp, t1 TO t2;
SHOW CREATE TABLE tmp;
Table Create Table
tmp CREATE TEMPORARY TABLE `tmp` (
`b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
CREATE VIEW v AS SELECT * FROM tmp;
ERROR HY000: View's SELECT refers to a temporary table 'tmp'
CREATE VIEW v AS SELECT * FROM t2;
DROP VIEW v;
DROP TABLE tmp;
DROP TABLE t2;
include/rpl_end.inc
--source include/have_binlog_format_mixed.inc
--source include/master-slave.inc
--echo #
--echo # MDEV-16229 Replication aborts with ER_VIEW_SELECT_TMPTABLE after
--echo # half-failed RENAME
--echo #
CREATE TABLE t1 (a INT);
CREATE TEMPORARY TABLE t1 (b INT);
RENAME TABLE t1 TO tmp, tmp TO t1;
SHOW CREATE TABLE t1;
--error ER_VIEW_SELECT_TMPTABLE
CREATE VIEW v AS SELECT * FROM t1;
RENAME TABLE t1 TO tmp, t1 TO t2;
SHOW CREATE TABLE tmp;
SHOW CREATE TABLE t2;
--error ER_VIEW_SELECT_TMPTABLE
CREATE VIEW v AS SELECT * FROM tmp;
CREATE VIEW v AS SELECT * FROM t2;
--sync_slave_with_master
# Cleanup
--connection master
DROP VIEW v;
DROP TABLE tmp;
DROP TABLE t2;
--source include/rpl_end.inc
......@@ -1809,6 +1809,37 @@ ALTER TABLE t1 CONVERT TO CHARACTER SET utf8;
SHOW CREATE TABLE t1;
DROP TABLE t1;
--echo #
--echo # MDEV-15308
--echo # Assertion `ha_alter_info->alter_info->drop_list.elements > 0' failed
--echo # in ha_innodb::prepare_inplace_alter_table
--echo #
CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;
ALTER TABLE t1 DROP FOREIGN KEY IF EXISTS fk, DROP COLUMN b;
SHOW CREATE TABLE t1;
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;
ALTER TABLE t1 DROP INDEX IF EXISTS fk, DROP COLUMN b;
SHOW CREATE TABLE t1;
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT, c INT, KEY(c)) ENGINE=InnoDB;
ALTER TABLE t1 DROP FOREIGN KEY IF EXISTS fk, DROP COLUMN c;
SHOW CREATE TABLE t1;
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT, c INT, KEY c1(c)) ENGINE=InnoDB;
ALTER TABLE t1 DROP FOREIGN KEY IF EXISTS fk, DROP INDEX c1;
SHOW CREATE TABLE t1;
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;
ALTER TABLE t1 DROP INDEX IF EXISTS fk, DROP COLUMN IF EXISTS c;
SHOW CREATE TABLE t1;
DROP TABLE t1;
--echo #
--echo # End of 10.0 tests
--echo #
......
......@@ -875,9 +875,8 @@ CACHE INDEX t3 IN keycache1;
LOAD INDEX INTO CACHE t3;
--echo #
--echo # RENAME (doesn't work for temporary tables, thus should fail).
--echo # RENAME should work for temporary tables
--echo #
--error ER_TABLEACCESS_DENIED_ERROR
RENAME TABLE t3 TO t3_1;
--echo #
......
......@@ -95,3 +95,49 @@ drop table t1;
--source include/wait_until_count_sessions.inc
#
# Test of rename with temporary tables
#
CREATE OR REPLACE TABLE t1 (a INT);
CREATE OR REPLACE TABLE t2 (a INT);
CREATE OR REPLACE TEMPORARY TABLE t1_tmp (b INT);
CREATE OR REPLACE TEMPORARY TABLE t2_tmp (b INT);
# Can't rename table over another one
--error ER_TABLE_EXISTS_ERROR
rename table t1 to t2;
--error ER_TABLE_EXISTS_ERROR
rename table t1 to tmp, tmp to t2;
--error ER_TABLE_EXISTS_ERROR
rename table t1_tmp to t2_tmp;
--error ER_TABLE_EXISTS_ERROR
rename table t1_tmp to tmp, tmp to t2_tmp;
show create table t1_tmp;
show create table t2_tmp;
# The following should work
rename table t1 to t1_tmp;
rename table t2_tmp to t2;
rename table t2 to tmp, tmp to t2;
rename table t1_tmp to tmp, tmp to t1_tmp;
show tables;
SHOW CREATE TABLE t1_tmp;
drop table t1_tmp;
SHOW CREATE TABLE t1_tmp;
drop table t1_tmp;
SHOW CREATE TABLE t2;
drop table t2;
SHOW CREATE TABLE t2;
drop table t2;
CREATE TABLE t1 (a INT);
insert into t1 values (1);
CREATE TEMPORARY TABLE t1 (b INT);
insert into t1 values (2);
RENAME TABLE t1 TO tmp, t1 TO t2;
select * from tmp;
select * from t2;
drop table tmp,t2;
#
# MDEV-16123 ASAN heap-use-after-free handler::ha_index_or_rnd_end
# MDEV-13828 Segmentation fault on RENAME TABLE
#
CREATE TABLE t1 (i int);
--connect (con1,localhost,root,,test)
--send
RENAME TABLE t1 TO t2;
--connection default
FLUSH TABLES;
--connection con1
--reap
# Cleanup
--disconnect con1
--connection default
DROP TABLE IF EXISTS t1, t2;
......@@ -302,7 +302,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
HA_CHECK_OPT* check_opt,
const char *operator_name,
thr_lock_type lock_type,
bool open_for_modify,
bool org_open_for_modify,
bool repair_table_use_frm,
uint extra_open_options,
int (*prepare_func)(THD *, TABLE_LIST *,
......@@ -365,10 +365,11 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
for (table= tables; table; table= table->next_local)
{
char table_name[SAFE_NAME_LEN*2+2];
char* db = table->db;
char *db= table->db;
bool fatal_error=0;
bool open_error;
bool collect_eis= FALSE;
bool open_for_modify= org_open_for_modify;
DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
strxmov(table_name, db, ".", table->table_name, NullS);
......@@ -406,8 +407,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
/*
CHECK TABLE command is allowed for views as well. Check on alter flags
to differentiate from ALTER TABLE...CHECK PARTITION on which view is not
allowed.
to differentiate from ALTER TABLE...CHECK PARTITION on which view is
not allowed.
*/
if (lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION ||
view_operator_func == NULL)
......@@ -1102,7 +1103,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
}
}
/* Error path, a admin command failed. */
if (thd->transaction_rollback_request)
if (thd->transaction_rollback_request || fatal_error)
{
/*
Unlikely, but transaction rollback was requested by one of storage
......
......@@ -155,10 +155,11 @@ static void mysql_ha_close_table(SQL_HANDLER *handler)
{
THD *thd= handler->thd;
TABLE *table= handler->table;
DBUG_ENTER("mysql_ha_close_table");
/* check if table was already closed */
if (!table)
return;
DBUG_VOID_RETURN;
if (!table->s->tmp_table)
{
......@@ -184,6 +185,7 @@ static void mysql_ha_close_table(SQL_HANDLER *handler)
}
my_free(handler->lock);
handler->init();
DBUG_VOID_RETURN;
}
/*
......
......@@ -520,6 +520,8 @@ void init_update_queries(void)
There are other statements that deal with temporary tables and open
them, but which are not listed here. The thing is that the order of
pre-opening temporary tables for those statements is somewhat custom.
Note that SQLCOM_RENAME_TABLE should not be in this list!
*/
sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_DROP_TABLE]|= CF_PREOPEN_TMP_TABLES;
......@@ -533,7 +535,6 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_INSERT_SELECT]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_DELETE]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_DELETE_MULTI]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_RENAME_TABLE]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_REPLACE_SELECT]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_SELECT]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_SET_OPTION]|= CF_PREOPEN_TMP_TABLES;
......@@ -5995,6 +5996,60 @@ static bool execute_show_status(THD *thd, TABLE_LIST *all_tables)
}
/*
Find out if a table is a temporary table
A table is a temporary table if it's a temporary table or
there has been before a temporary table that has been renamed
to the current name.
Some examples:
A->B B is a temporary table if and only if A is a temp.
A->B, B->C Second B is temp if A is temp
A->B, A->C Second A can't be temp as if A was temp then B is temp
and Second A can only be a normal table. C is also not temp
*/
static TABLE *find_temporary_table_for_rename(THD *thd,
TABLE_LIST *first_table,
TABLE_LIST *cur_table)
{
TABLE_LIST *table;
TABLE *res= 0;
bool found= 0;
DBUG_ENTER("find_temporary_table_for_rename");
/* Find last instance when cur_table is in TO part */
for (table= first_table;
table != cur_table;
table= table->next_local->next_local)
{
TABLE_LIST *next= table->next_local;
if (!strcmp(table->get_db_name(), cur_table->get_db_name()) &&
!strcmp(table->get_table_name(), cur_table->get_table_name()))
{
/* Table was moved away, can't be same as 'table' */
found= 1;
res= 0; // Table can't be a temporary table
}
if (!strcmp(next->get_db_name(), cur_table->get_db_name()) &&
!strcmp(next->get_table_name(), cur_table->get_table_name()))
{
/*
Table has matching name with new name of this table. cur_table should
have same temporary type as this table.
*/
found= 1;
res= table->table;
}
}
if (!found)
res= find_temporary_table(thd, table);
DBUG_RETURN(res);
}
static bool check_rename_table(THD *thd, TABLE_LIST *first_table,
TABLE_LIST *all_tables)
{
......@@ -6011,13 +6066,19 @@ static bool check_rename_table(THD *thd, TABLE_LIST *first_table,
&table->next_local->grant.m_internal,
0, 0))
return 1;
/* check if these are refering to temporary tables */
table->table= find_temporary_table_for_rename(thd, first_table, table);
table->next_local->table= table->table;
TABLE_LIST old_list, new_list;
/*
we do not need initialize old_list and new_list because we will
come table[0] and table->next[0] there
copy table[0] and table->next[0] there
*/
old_list= table[0];
new_list= table->next_local[0];
if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) ||
(!test_all_bits(table->next_local->grant.privilege,
INSERT_ACL | CREATE_ACL) &&
......
......@@ -222,7 +222,7 @@ do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table,
new_alias= (lower_case_table_names == 2) ? new_table->alias :
new_table->table_name;
if (is_temporary_table(new_table))
if (find_temporary_table(thd, new_table))
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
DBUG_RETURN(1); // This can't be skipped
......
......@@ -1518,7 +1518,8 @@ class Stat_table_write_iter
~Stat_table_write_iter()
{
cleanup();
/* Ensure that cleanup has been run */
DBUG_ASSERT(rowid_buf == 0);
}
};
......
......@@ -5835,10 +5835,28 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info)
List_iterator<Alter_drop> drop_it(alter_info->drop_list);
Alter_drop *drop;
bool remove_drop;
ulonglong left_flags= 0;
while ((drop= drop_it++))
{
ulonglong cur_flag= 0;
switch (drop->type) {
case Alter_drop::COLUMN:
cur_flag= Alter_info::ALTER_DROP_COLUMN;
break;
case Alter_drop::FOREIGN_KEY:
cur_flag= Alter_info::DROP_FOREIGN_KEY;
break;
case Alter_drop::KEY:
cur_flag= Alter_info::ALTER_DROP_INDEX;
break;
default:
break;
}
if (!drop->drop_if_exists)
{
left_flags|= cur_flag;
continue;
}
remove_drop= TRUE;
if (drop->type == Alter_drop::COLUMN)
{
......@@ -5915,12 +5933,15 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info)
ER_THD(thd, ER_CANT_DROP_FIELD_OR_KEY),
drop->name);
drop_it.remove();
if (alter_info->drop_list.is_empty())
alter_info->flags&= ~(Alter_info::ALTER_DROP_COLUMN |
Alter_info::ALTER_DROP_INDEX |
Alter_info::DROP_FOREIGN_KEY);
}
else
left_flags|= cur_flag;
}
/* Reset state to what's left in drop list */
alter_info->flags&= ~(Alter_info::ALTER_DROP_COLUMN |
Alter_info::ALTER_DROP_INDEX |
Alter_info::DROP_FOREIGN_KEY);
alter_info->flags|= left_flags;
}
/* ALTER TABLE ADD KEY IF NOT EXISTS */
......
......@@ -37,6 +37,7 @@ C_MODE_START
#include "ma_checkpoint.h"
#include "ma_recovery.h"
C_MODE_END
#include "ma_trnman.h"
//#include "sql_priv.h"
#include "protocol.h"
......@@ -1382,7 +1383,8 @@ int ha_maria::check(THD * thd, HA_CHECK_OPT * check_opt)
}
/* Reset trn, that may have been set by repair */
_ma_set_trn_for_table(file, old_trn);
if (old_trn && old_trn != file->trn)
_ma_set_trn_for_table(file, old_trn);
thd_proc_info(thd, old_proc_info);
thd_progress_end(thd);
return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
......@@ -1516,7 +1518,8 @@ int ha_maria::zerofill(THD * thd, HA_CHECK_OPT *check_opt)
error=maria_zerofill(param, file, share->open_file_name.str);
/* Reset trn, that may have been set by repair */
_ma_set_trn_for_table(file, old_trn);
if (old_trn && old_trn != file->trn)
_ma_set_trn_for_table(file, old_trn);
if (!error)
{
......@@ -1756,7 +1759,8 @@ int ha_maria::repair(THD *thd, HA_CHECK *param, bool do_optimize)
maria_lock_database(file, F_UNLCK);
/* Reset trn, that may have been set by repair */
_ma_set_trn_for_table(file, old_trn);
if (old_trn && old_trn != file->trn)
_ma_set_trn_for_table(file, old_trn);
error= error ? HA_ADMIN_FAILED :
(optimize_done ?
(write_log_record_for_repair(param, file) ? HA_ADMIN_FAILED :
......@@ -2567,9 +2571,12 @@ int ha_maria::extra(enum ha_extra_function operation)
without calling commit/rollback in between. If file->trn is not set
we can't remove file->share from the transaction list in the extra() call.
We also ensure that we set file->trn to 0 if THD_TRN is 0 as in
this case we have already freed the trn. This can happen when one
implicit_commit() is called as part of alter table.
In current code we don't have to do this for HA_EXTRA_PREPARE_FOR_RENAME
as this is only used the intermediate table used by ALTER TABLE which
is not part of the transaction (it's not in the TRN list). Better to
keep this for now, to not break anything in a stable release.
When HA_EXTRA_PREPARE_FOR_RENAME is not handled below, we can change
the warnings in _ma_remove_table_from_trnman() to asserts.
table->in_use is not set in the case this is a done as part of closefrm()
as part of drop table.
......@@ -2582,7 +2589,7 @@ int ha_maria::extra(enum ha_extra_function operation)
{
THD *thd= table->in_use;
TRN *trn= THD_TRN;
_ma_set_trn_for_table(file, trn);
_ma_set_tmp_trn_for_table(file, trn);
}
DBUG_ASSERT(file->s->base.born_transactional || file->trn == 0 ||
file->trn == &dummy_transaction_object);
......@@ -2698,6 +2705,7 @@ int ha_maria::external_lock(THD *thd, int lock_type)
if (file->trn)
{
/* This can only happen with tables created with clone() */
DBUG_PRINT("info",("file->trn: %p", file->trn));
trnman_increment_locked_tables(file->trn);
}
......@@ -2718,7 +2726,7 @@ int ha_maria::external_lock(THD *thd, int lock_type)
}
else
{
TRN *trn= THD_TRN;
TRN *trn= (file->trn != &dummy_transaction_object ? file->trn : 0);
/* End of transaction */
/*
......@@ -2733,8 +2741,7 @@ int ha_maria::external_lock(THD *thd, int lock_type)
*/
if (_ma_reenable_logging_for_table(file, TRUE))
DBUG_RETURN(1);
/** @todo zero file->trn also in commit and rollback */
_ma_set_trn_for_table(file, NULL); // Safety
_ma_reset_trn_for_table(file);
/*
Ensure that file->state points to the current number of rows. This
is needed if someone calls maria_info() without first doing an
......@@ -2790,13 +2797,6 @@ int ha_maria::start_stmt(THD *thd, thr_lock_type lock_type)
DBUG_ASSERT(lock_type != TL_UNLOCK);
DBUG_ASSERT(file->trn == trn);
/*
If there was an implicit commit under this LOCK TABLES by a previous
statement (like a DDL), at least if that previous statement was about a
different ha_maria than 'this' then this->file->trn is a stale
pointer. We fix it:
*/
_ma_set_trn_for_table(file, trn);
/*
As external_lock() was already called, don't increment locked_tables.
Note that we call the function below possibly several times when
......@@ -2821,6 +2821,23 @@ int ha_maria::start_stmt(THD *thd, thr_lock_type lock_type)
}
/*
Reset THD_TRN and all file->trn related to the transaction
This is needed as some calls, like extra() or external_lock() may access
it before next transaction is started
*/
static void reset_thd_trn(THD *thd, MARIA_HA *first_table)
{
DBUG_ENTER("reset_thd_trn");
THD_TRN= NULL;
for (MARIA_HA *table= first_table; table ;
table= table->trn_next)
_ma_reset_trn_for_table(table);
DBUG_VOID_RETURN;
}
/**
Performs an implicit commit of the Maria transaction and creates a new
one.
......@@ -2844,10 +2861,10 @@ int ha_maria::implicit_commit(THD *thd, bool new_trn)
TRN *trn;
int error;
uint locked_tables;
DYNAMIC_ARRAY used_tables;
extern my_bool plugins_are_initialized;
MARIA_HA *used_tables, *trn_next;
DBUG_ENTER("ha_maria::implicit_commit");
if (!maria_hton || !plugins_are_initialized || !(trn= THD_TRN))
DBUG_RETURN(0);
if (!new_trn && (thd->locked_tables_mode == LTM_LOCK_TABLES ||
......@@ -2865,48 +2882,16 @@ int ha_maria::implicit_commit(THD *thd, bool new_trn)
locked_tables= trnman_has_locked_tables(trn);
if (new_trn && trn && trn->used_tables)
{
MARIA_USED_TABLES *tables;
/*
Save locked tables so that we can move them to another transaction
We are using a dynamic array as locked_tables in some cases can be
smaller than the used_tables list (for example when the server does
early unlock of tables.
*/
my_init_dynamic_array2(&used_tables, sizeof(MARIA_SHARE*), (void*) 0,
locked_tables, 8, MYF(MY_THREAD_SPECIFIC));
for (tables= (MARIA_USED_TABLES*) trn->used_tables;
tables;
tables= tables->next)
{
if (tables->share->base.born_transactional)
{
if (insert_dynamic(&used_tables, (uchar*) &tables->share))
{
error= HA_ERR_OUT_OF_MEM;
goto end_and_free;
}
}
}
}
else
bzero(&used_tables, sizeof(used_tables));
used_tables= (MARIA_HA*) trn->used_instances;
error= 0;
if (unlikely(ma_commit(trn)))
error= 1;
if (!new_trn)
{
/*
To be extra safe, we should also reset file->trn for all open
tables as some calls, like extra() may access it. We take care
of this in extra() by resetting file->trn if THD_TRN is 0.
*/
THD_TRN= NULL;
reset_thd_trn(thd, used_tables);
goto end;
}
/*
We need to create a new transaction and put it in THD_TRN. Indeed,
tables may be under LOCK TABLES, and so they will start the next
......@@ -2916,8 +2901,9 @@ int ha_maria::implicit_commit(THD *thd, bool new_trn)
THD_TRN= trn;
if (unlikely(trn == NULL))
{
reset_thd_trn(thd, used_tables);
error= HA_ERR_OUT_OF_MEM;
goto end_and_free;
goto end;
}
/*
Move all locked tables to the new transaction
......@@ -2927,35 +2913,25 @@ int ha_maria::implicit_commit(THD *thd, bool new_trn)
in check table, we use the table without calling start_stmt().
*/
uint i;
for (i= 0 ; i < used_tables.elements ; i++)
for (MARIA_HA *handler= used_tables; handler ;
handler= trn_next)
{
MARIA_SHARE *share;
LIST *handlers;
trn_next= handler->trn_next;
DBUG_ASSERT(handler->s->base.born_transactional);
share= *(dynamic_element(&used_tables, i, MARIA_SHARE**));
/* Find table instances that was used in this transaction */
for (handlers= share->open_list; handlers; handlers= handlers->next)
/* If handler uses versioning */
if (handler->s->lock_key_trees)
{
MARIA_HA *handler= (MARIA_HA*) handlers->data;
if (handler->external_ref &&
((TABLE*) handler->external_ref)->in_use == thd)
{
_ma_set_trn_for_table(handler, trn);
/* If handler uses versioning */
if (handler->s->lock_key_trees)
{
if (_ma_setup_live_state(handler))
error= HA_ERR_OUT_OF_MEM;
}
}
/* _ma_set_trn_for_table() will be called indirectly */
if (_ma_setup_live_state(handler))
error= HA_ERR_OUT_OF_MEM;
}
else
_ma_set_trn_for_table(handler, trn);
}
/* This is just a commit, tables stay locked if they were: */
trnman_reset_locked_tables(trn, locked_tables);
end_and_free:
delete_dynamic(&used_tables);
end:
DBUG_RETURN(error);
}
......@@ -3328,10 +3304,10 @@ static int maria_commit(handlerton *hton __attribute__ ((unused)),
trnman_set_flags(trn, trnman_get_flags(trn) & ~TRN_STATE_INFO_LOGGED);
/* statement or transaction ? */
if ((thd->variables.option_bits & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && !all)
if ((thd->variables.option_bits & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
!all)
DBUG_RETURN(0); // end of statement
DBUG_PRINT("info", ("THD_TRN set to 0x0"));
THD_TRN= 0;
reset_thd_trn(thd, (MARIA_HA*) trn->used_instances);
DBUG_RETURN(ma_commit(trn)); // end of transaction
}
......@@ -3348,8 +3324,7 @@ static int maria_rollback(handlerton *hton __attribute__ ((unused)),
trnman_rollback_statement(trn);
DBUG_RETURN(0); // end of statement
}
DBUG_PRINT("info", ("THD_TRN set to 0x0"));
THD_TRN= 0;
reset_thd_trn(thd, (MARIA_HA*) trn->used_instances);
DBUG_RETURN(trnman_rollback_trn(trn) ?
HA_ERR_OUT_OF_MEM : 0); // end of transaction
}
......
......@@ -193,6 +193,7 @@ class ha_maria :public handler
private:
DsMrr_impl ds_mrr;
friend ICP_RESULT index_cond_func_maria(void *arg);
friend void reset_thd_trn(THD *thd);
};
#endif /* HA_MARIA_INCLUDED */
......@@ -271,6 +271,7 @@
#include "maria_def.h"
#include "ma_blockrec.h"
#include "trnman.h"
#include "ma_trnman.h"
#include "ma_key_recover.h"
#include "ma_recovery_util.h"
#include <lf.h>
......@@ -7525,7 +7526,7 @@ void maria_ignore_trids(MARIA_HA *info)
if (info->s->base.born_transactional)
{
if (!info->trn)
_ma_set_trn_for_table(info, &dummy_transaction_object);
_ma_set_tmp_trn_for_table(info, &dummy_transaction_object);
/* Ignore transaction id when row is read */
info->trn->min_read_from= ~(TrID) 0;
}
......
......@@ -37,6 +37,8 @@ int maria_close(register MARIA_HA *info)
/* Check that we have unlocked key delete-links properly */
DBUG_ASSERT(info->key_del_used == 0);
/* Check that file is not part of any uncommited transactions */
DBUG_ASSERT(info->trn == 0 || info->trn == &dummy_transaction_object);
if (share->reopen == 1)
{
......
......@@ -15,6 +15,7 @@
#include "maria_def.h"
#include "trnman.h"
#include "ma_trnman.h"
/**
writes a COMMIT record to log and commits transaction in memory
......@@ -43,9 +44,9 @@ int ma_commit(TRN *trn)
COMMIT record) and this is not an issue as
* transaction's updates were not made visible to other transactions
* "commit ok" was not sent to client
Alternatively, Recovery might commit trn (if MY_MIN(rec_lsn) is before COMMIT
record), which is ok too. All in all it means that "trn committed" is not
100% equal to "COMMIT record written".
Alternatively, Recovery might commit trn (if MY_MIN(rec_lsn) is before
COMMIT record), which is ok too. All in all it means that "trn committed"
is not 100% equal to "COMMIT record written".
- if COMMIT record is written after trnman_commit_trn():
if crash happens between the two, trn will be rolled back which is an
issue (transaction's updates were made visible to other transactions).
......@@ -93,7 +94,12 @@ int ma_commit(TRN *trn)
int maria_commit(MARIA_HA *info)
{
return info->s->now_transactional ? ma_commit(info->trn) : 0;
TRN *trn;
if (!info->s->now_transactional)
return 0;
trn= info->trn;
info->trn= 0; /* checked in maria_close() */
return ma_commit(trn);
}
......@@ -120,10 +126,7 @@ int maria_begin(MARIA_HA *info)
TRN *trn= trnman_new_trn(0);
if (unlikely(!trn))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
DBUG_PRINT("info", ("TRN set to 0x%lx", (ulong) trn));
_ma_set_trn_for_table(info, trn);
}
DBUG_RETURN(0);
}
......@@ -345,7 +345,7 @@ int maria_extra(MARIA_HA *info, enum ha_extra_function function,
_ma_decrement_open_count(info, 0);
if (info->trn)
{
_ma_remove_table_from_trnman(share, info->trn);
_ma_remove_table_from_trnman(info);
/* Ensure we don't point to the deleted data in trn */
info->state= info->state_start= &share->state.state;
}
......@@ -408,7 +408,7 @@ int maria_extra(MARIA_HA *info, enum ha_extra_function function,
if (info->trn)
{
mysql_mutex_lock(&share->intern_lock);
_ma_remove_table_from_trnman(share, info->trn);
_ma_remove_table_from_trnman(info);
/* Ensure we don't point to the deleted data in trn */
info->state= info->state_start= &share->state.state;
mysql_mutex_unlock(&share->intern_lock);
......
......@@ -19,6 +19,8 @@
#include "ma_sp_defs.h"
#include "ma_rt_index.h"
#include "ma_blockrec.h"
#include "trnman.h"
#include "ma_trnman.h"
#include <m_ctype.h>
#include "ma_crypt.h"
......@@ -184,7 +186,7 @@ static MARIA_HA *maria_clone_internal(MARIA_SHARE *share,
if (!share->base.born_transactional) /* For transactional ones ... */
{
/* ... force crash if no trn given */
_ma_set_trn_for_table(&info, &dummy_transaction_object);
_ma_set_tmp_trn_for_table(&info, &dummy_transaction_object);
info.state= &share->state.state; /* Change global values by default */
}
else
......
......@@ -66,7 +66,7 @@ my_bool _ma_setup_live_state(MARIA_HA *info)
DBUG_RETURN(1);
trn= info->trn;
for (tables= (MARIA_USED_TABLES*) info->trn->used_tables;
for (tables= (MARIA_USED_TABLES*) trn->used_tables;
tables;
tables= tables->next)
{
......@@ -551,6 +551,7 @@ my_bool _ma_trnman_end_trans_hook(TRN *trn, my_bool commit,
my_free(tables);
}
trn->used_tables= 0;
trn->used_instances= 0;
DBUG_RETURN(error);
}
......@@ -565,18 +566,25 @@ my_bool _ma_trnman_end_trans_hook(TRN *trn, my_bool commit,
share->internal_lock must be locked when function is called
*/
void _ma_remove_table_from_trnman(MARIA_SHARE *share, TRN *trn)
void _ma_remove_table_from_trnman(MARIA_HA *info)
{
MARIA_SHARE *share= info->s;
TRN *trn= info->trn;
MARIA_USED_TABLES *tables, **prev;
MARIA_HA *handler, **prev_file;
DBUG_ENTER("_ma_remove_table_from_trnman");
DBUG_PRINT("enter", ("trn: %p used_tables: %p share: %p in_trans: %d",
trn, trn->used_tables, share, share->in_trans));
mysql_mutex_assert_owner(&share->intern_lock);
if (trn == &dummy_transaction_object)
DBUG_VOID_RETURN;
for (prev= (MARIA_USED_TABLES**) (char*) &trn->used_tables, tables= *prev;
tables;
tables= *prev)
/* First remove share from used_tables */
for (prev= (MARIA_USED_TABLES**) (char*) &trn->used_tables;
(tables= *prev);
prev= &tables->next)
{
if (tables->share == share)
{
......@@ -585,8 +593,36 @@ void _ma_remove_table_from_trnman(MARIA_SHARE *share, TRN *trn)
my_free(tables);
break;
}
prev= &tables->next;
}
if (tables != 0)
{
/*
This can only happens in case of rename of intermediate table as
part of alter table
*/
DBUG_PRINT("warning", ("share: %p where not in used_tables_list", share));
}
/* unlink table from used_instances */
for (prev_file= (MARIA_HA**) &trn->used_instances;
(handler= *prev_file);
prev_file= &handler->trn_next)
{
if (handler == info)
{
*prev_file= info->trn_next;
break;
}
}
if (handler != 0)
{
/*
This can only happens in case of rename of intermediate table as
part of alter table
*/
DBUG_PRINT("warning", ("table: %p where not in used_instances", info));
}
info->trn= 0; /* Not part of trans anymore */
DBUG_VOID_RETURN;
}
......
......@@ -84,5 +84,5 @@ my_bool _ma_row_visible_non_transactional_table(MARIA_HA *info);
my_bool _ma_row_visible_transactional_table(MARIA_HA *info);
void _ma_remove_not_visible_states_with_lock(struct st_maria_share *share,
my_bool all);
void _ma_remove_table_from_trnman(struct st_maria_share *share, TRN *trn);
void _ma_remove_table_from_trnman(MARIA_HA *info);
void _ma_reset_history(struct st_maria_share *share);
/* Copyright (C) 2006-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifndef _ma_trnman_h
#define _ma_trnman_h
/**
Sets table's trn and prints debug information
Links table into used_instances if new_trn is not 0
@param tbl MARIA_HA of table
@param newtrn what to put into tbl->trn
*/
static inline void _ma_set_trn_for_table(MARIA_HA *tbl, TRN *newtrn)
{
DBUG_PRINT("info",("table: %p trn: %p -> %p",
tbl, tbl->trn, newtrn));
/* check that we are not calling this twice in a row */
DBUG_ASSERT(newtrn->used_instances != (void*) tbl);
tbl->trn= newtrn;
/* Link into used list */
tbl->trn_next= (MARIA_HA*) newtrn->used_instances;
newtrn->used_instances= tbl;
}
/*
Same as _ma_set_trn_for_table(), but don't link table into used_instance list
Used when we want to temporary set trn for a table in extra()
*/
static inline void _ma_set_tmp_trn_for_table(MARIA_HA *tbl, TRN *newtrn)
{
DBUG_PRINT("info",("table: %p trn: %p -> %p",
tbl, tbl->trn, newtrn));
tbl->trn= newtrn;
}
/*
Reset TRN in table
*/
static inline void _ma_reset_trn_for_table(MARIA_HA *tbl)
{
DBUG_PRINT("info",("table: %p trn: %p -> NULL", tbl, tbl->trn));
tbl->trn= 0;
}
#endif /* _ma_trnman_h */
......@@ -601,6 +601,7 @@ struct st_maria_handler
{
MARIA_SHARE *s; /* Shared between open:s */
struct st_ma_transaction *trn; /* Pointer to active transaction */
struct st_maria_handler *trn_next;
MARIA_STATUS_INFO *state, state_save;
MARIA_STATUS_INFO *state_start; /* State at start of transaction */
MARIA_USED_TABLES *used_tables;
......@@ -860,19 +861,6 @@ struct st_maria_handler
#define get_pack_length(length) ((length) >= 255 ? 3 : 1)
#define _ma_have_versioning(info) ((info)->row_flag & ROW_FLAG_TRANSID)
/**
Sets table's trn and prints debug information
@param tbl MARIA_HA of table
@param newtrn what to put into tbl->trn
@note cast of newtrn is because %p of NULL gives warning (NULL is int)
*/
#define _ma_set_trn_for_table(tbl, newtrn) do { \
DBUG_PRINT("info",("table: %p trn: %p -> %p", \
(tbl), (tbl)->trn, (void *)(newtrn))); \
(tbl)->trn= (newtrn); \
} while (0)
#define MARIA_MIN_BLOCK_LENGTH 20 /* Because of delete-link */
/* Don't use to small record-blocks */
#define MARIA_EXTEND_BLOCK_LENGTH 20
......
......@@ -357,6 +357,7 @@ TRN *trnman_new_trn(WT_THD *wt)
trn->commit_trid= MAX_TRID;
trn->rec_lsn= trn->undo_lsn= trn->first_undo_lsn= 0;
trn->used_tables= 0;
trn->used_instances= 0;
trn->locked_tables= 0;
trn->flags= 0;
......
......@@ -46,7 +46,8 @@ struct st_ma_transaction
LF_PINS *pins;
WT_THD *wt;
mysql_mutex_t state_lock;
void *used_tables; /**< Tables used by transaction */
void *used_tables; /**< Table shares used by transaction */
void *used_instances; /* table files used by transaction */
TRN *next, *prev;
TrID trid, min_read_from, commit_trid;
LSN rec_lsn, undo_lsn;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment