From d4d3ca204f9d36b01be2c4dad15d0cc5b078c6e6 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Fri, 18 May 2012 16:02:11 +0300 Subject: [PATCH] Fixed lp:997460 Truncate table on partitioned Aria table fails with ER_ILLEGAL_HA Fix is done by doing an autocommit in truncate table inside Aria storage/maria/ha_maria.cc: Force a commit for TRUNCATE TABLE inside lock tables Check that we don't call TRUNCATE with concurrent inserts going on. Make ha_maria::implict_commit faster when we don't have Aria tables in the transaction. (Most of the patch is just re-indentation because I removed an if level) --- mysql-test/suite/maria/maria.result | 6 +- mysql-test/suite/maria/maria.test | 4 + mysql-test/suite/maria/truncate.result | 37 ++++++++ mysql-test/suite/maria/truncate.test | 47 ++++++++++ storage/maria/ha_maria.cc | 114 +++++++++++++------------ 5 files changed, 152 insertions(+), 56 deletions(-) create mode 100644 mysql-test/suite/maria/truncate.result create mode 100644 mysql-test/suite/maria/truncate.test diff --git a/mysql-test/suite/maria/maria.result b/mysql-test/suite/maria/maria.result index c68a8871f83..888c1c10c5e 100644 --- a/mysql-test/suite/maria/maria.result +++ b/mysql-test/suite/maria/maria.result @@ -2542,8 +2542,12 @@ drop table t1; create table t1 (a int) engine=aria transactional=1; insert into t1 values (1); lock table t1 write concurrent; -delete from t1; +delete from t1 where a>0; ERROR 42000: The storage engine for the table doesn't support DELETE in WRITE CONCURRENT +delete from t1; +ERROR 42000: The storage engine for the table doesn't support TRUNCATE in WRITE CONCURRENT +truncate t1; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated unlock tables; drop table t1; create table t1 (p int primary key, i int, a char(10), key k1(i), key k2(a)) diff --git a/mysql-test/suite/maria/maria.test b/mysql-test/suite/maria/maria.test index b371394b2b3..6ebcf08d952 100644 --- a/mysql-test/suite/maria/maria.test +++ b/mysql-test/suite/maria/maria.test @@ -1805,7 +1805,11 @@ insert into t1 values (1); lock table t1 write concurrent; # should be fixed with fully implemented versioning --error ER_CHECK_NOT_IMPLEMENTED +delete from t1 where a>0; +--error ER_CHECK_NOT_IMPLEMENTED delete from t1; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +truncate t1; unlock tables; drop table t1; diff --git a/mysql-test/suite/maria/truncate.result b/mysql-test/suite/maria/truncate.result new file mode 100644 index 00000000000..467e0f915dd --- /dev/null +++ b/mysql-test/suite/maria/truncate.result @@ -0,0 +1,37 @@ +CREATE TABLE t1 ( i INT ) ENGINE=Aria PARTITION BY HASH(i) PARTITIONS 2; +SET AUTOCOMMIT = 0; +TRUNCATE TABLE t1; +DROP TABLE t1; +CREATE TABLE t1 ( i INT ) ENGINE=Aria; +CREATE TABLE t2 ( i INT ) ENGINE=Aria; +insert into t1 values(1); +lock table t1 write; +truncate table t1; +select count(*) from t1; +count(*) +0 +insert into t1 values(2); +select count(*) from t1; +count(*) +1 +truncate table t1; +select count(*) from t1; +count(*) +0 +insert into t1 values(3); +select count(*) from t1; +count(*) +1 +select * from t2; +ERROR HY000: Table 't2' was not locked with LOCK TABLES +unlock tables; +insert into t1 values(4); +select * from t1; +i +3 +4 +truncate t1; +select count(*) from t1; +count(*) +0 +drop table t1,t2; diff --git a/mysql-test/suite/maria/truncate.test b/mysql-test/suite/maria/truncate.test new file mode 100644 index 00000000000..3d6e70d9db6 --- /dev/null +++ b/mysql-test/suite/maria/truncate.test @@ -0,0 +1,47 @@ +# +# Testing of potential problems in Aria +# + +-- source include/have_maria.inc +-- source include/have_partition.inc + +--disable_warnings +--disable_query_log +drop table if exists t1,t2; +--enable_query_log +--enable_warnings + +# +# LP:997460 truncate table on partitioned Aria table fails with ER_ILLEGAL_HA +# + +CREATE TABLE t1 ( i INT ) ENGINE=Aria PARTITION BY HASH(i) PARTITIONS 2; +SET AUTOCOMMIT = 0; +TRUNCATE TABLE t1; +DROP TABLE t1; + +# +# Other truncate tests +# + +CREATE TABLE t1 ( i INT ) ENGINE=Aria; +CREATE TABLE t2 ( i INT ) ENGINE=Aria; +insert into t1 values(1); +lock table t1 write; +truncate table t1; +select count(*) from t1; +insert into t1 values(2); +select count(*) from t1; +truncate table t1; +select count(*) from t1; +insert into t1 values(3); +select count(*) from t1; +# Check that locking is still working +--error 1100 +select * from t2; +unlock tables; +insert into t1 values(4); +select * from t1; +truncate t1; +select count(*) from t1; +drop table t1,t2; diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index ae7e686d2f0..2b67f29f9f8 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -2537,9 +2537,10 @@ int ha_maria::extra_opt(enum ha_extra_function operation, ulong cache_size) int ha_maria::delete_all_rows() { -#ifdef EXTRA_DEBUG THD *thd= table->in_use; TRN *trn= file->trn; + CHECK_UNTIL_WE_FULLY_IMPLEMENTED_VERSIONING("TRUNCATE in WRITE CONCURRENT"); +#ifdef EXTRA_DEBUG if (trn && ! (trnman_get_flags(trn) & TRN_STATE_INFO_LOGGED)) { trnman_set_flags(trn, trnman_get_flags(trn) | TRN_STATE_INFO_LOGGED | @@ -2548,16 +2549,19 @@ int ha_maria::delete_all_rows() (uchar*) thd->query(), thd->query_length()); } #endif - if (file->s->now_transactional && - ((table->in_use->variables.option_bits & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) || - table->in_use->locked_tables_mode)) + /* + If we are under LOCK TABLES, we have to do a commit as + delete_all_rows() can't be rolled back + */ + if (table->in_use->locked_tables_mode && trn && + trnman_has_locked_tables(trn)) { - /* - We are not in autocommit mode or user have done LOCK TABLES. - We must do the delete row by row to be able to rollback the command - */ - return HA_ERR_WRONG_COMMAND; + int error; + if ((error= implicit_commit(thd, 1))) + return error; } + + /* Note that this can't be rolled back */ return maria_delete_all_rows(file); } @@ -2775,10 +2779,11 @@ int ha_maria::implicit_commit(THD *thd, bool new_trn) #error this method should be removed #endif TRN *trn; - int error= 0; + int error; + uint locked_tables; TABLE *table; DBUG_ENTER("ha_maria::implicit_commit"); - if (!maria_hton) + if (!maria_hton || !(trn= THD_TRN)) DBUG_RETURN(0); if (!new_trn && (thd->locked_tables_mode == LTM_LOCK_TABLES || thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) @@ -2792,60 +2797,59 @@ int ha_maria::implicit_commit(THD *thd, bool new_trn) DBUG_PRINT("info", ("locked_tables, skipping")); DBUG_RETURN(0); } - if ((trn= THD_TRN) != NULL) + locked_tables= trnman_has_locked_tables(trn); + error= 0; + if (unlikely(ma_commit(trn))) + error= 1; + if (!new_trn) { - uint locked_tables= trnman_has_locked_tables(trn); - 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; - 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 - statement assuming they have a trn (see ha_maria::start_stmt()). + 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. */ - trn= trnman_new_trn(& thd->transaction.wt); - THD_TRN= trn; - if (unlikely(trn == NULL)) + THD_TRN= NULL; + 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 + statement assuming they have a trn (see ha_maria::start_stmt()). + */ + trn= trnman_new_trn(& thd->transaction.wt); + THD_TRN= trn; + if (unlikely(trn == NULL)) + { + error= HA_ERR_OUT_OF_MEM; + goto end; + } + /* + Move all locked tables to the new transaction + We must do it here as otherwise file->thd and file->state may be + stale pointers. We can't do this in start_stmt() as we don't know + when we should call _ma_setup_live_state() and in some cases, like + in check table, we use the table without calling start_stmt(). + */ + for (table=thd->open_tables; table ; table=table->next) + { + if (table->db_stat && table->file->ht == maria_hton) { - error= HA_ERR_OUT_OF_MEM; - goto end; - } - /* - Move all locked tables to the new transaction - We must do it here as otherwise file->thd and file->state may be - stale pointers. We can't do this in start_stmt() as we don't know - when we should call _ma_setup_live_state() and in some cases, like - in check table, we use the table without calling start_stmt(). - */ - for (table=thd->open_tables; table ; table=table->next) - { - if (table->db_stat && table->file->ht == maria_hton) + MARIA_HA *handler= ((ha_maria*) table->file)->file; + if (handler->s->base.born_transactional) { - MARIA_HA *handler= ((ha_maria*) table->file)->file; - if (handler->s->base.born_transactional) + _ma_set_trn_for_table(handler, trn); + /* If handler uses versioning */ + if (handler->s->lock_key_trees) { - _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; - } + if (_ma_setup_live_state(handler)) + error= HA_ERR_OUT_OF_MEM; } } } - /* This is just a commit, tables stay locked if they were: */ - trnman_reset_locked_tables(trn, locked_tables); } + /* This is just a commit, tables stay locked if they were: */ + trnman_reset_locked_tables(trn, locked_tables); + end: DBUG_RETURN(error); }