From a0b7b2426f01271627d39ae586cd409ce50da69e Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 5 Sep 2006 16:52:05 +0200 Subject: [PATCH] Fix for BUG#11151 "LOAD DATA INFILE commits transaction in 5.0". In 5.0 we made LOAD DATA INFILE autocommit in all engines, while only NDB wanted that. Users and trainers complained that it affected InnoDB and was a change compared to 4.1 where only NDB autocommitted. To revert to the behaviour of 4.1, we move the autocommit logic out of mysql_load() into ha_ndbcluster::external_lock(). The result is that LOAD DATA INFILE commits all uncommitted changes of NDB if this is an NDB table, its own changes if this is an NDB table, but does not affect other engines. Note: even though there is no "commit the full transaction at end" anymore, LOAD DATA INFILE stays disabled in routines (re-entrency problems per a comment of Pem). Note: ha_ndbcluster::has_transactions() does not give reliable results because it says "yes" even if transactions are disabled in this engine... sql/ha_ndbcluster.cc: NDB wants to do autocommit if this is LOAD DATA INFILE. For this to not affect all other engines, we move the logic inside ha_ndbcluster. sql/sql_load.cc: This ha_enable_transaction() in mysql_load() forced an autocommit in all engines, while only NDB wants to do that. So we move the logic inside ha_ndbcluster.cc. mysql-test/include/loaddata_autocom.inc: test for engines to see if they autocommit or not in LOAD DATA INFILE mysql-test/r/loaddata_autocom_innodb.result: result for InnoDB (no autocommit) mysql-test/r/loaddata_autocom_ndb.result: result for NDB (autocommit) mysql-test/r/rpl_ndb_innodb_trans.result: result for InnoDB+NDB transactions. Observe that when ROLLBACK cannot rollback the LOAD DATA INFILE in NDB it issues warning 1196 as appropriate. mysql-test/t/loaddata_autocom_innodb.test: test that InnoDB does not autocommit in LOAD DATA INFILE. mysql-test/t/loaddata_autocom_ndb.test: test that NDB does autocommit in LOAD DATA INFIL mysql-test/t/rpl_ndb_innodb_trans-slave.opt: need to tell the slave to use innodb mysql-test/t/rpl_ndb_innodb_trans.test: test of transactions mixing NDB and InnoDB. To see if ROLLBACK rolls back in both engines, with the exception of LOAD DATA INFILE which does not roll back NDB: we see that a LOAD DATA INFILE in NDB commits all what has been done in NDB so far, commits its changes, but does not commit in other engines. --- mysql-test/include/loaddata_autocom.inc | 21 ++++ mysql-test/r/loaddata_autocom_innodb.result | 21 ++++ mysql-test/r/loaddata_autocom_ndb.result | 23 +++++ mysql-test/r/rpl_ndb_innodb_trans.result | 103 ++++++++++++++++++++ mysql-test/t/loaddata_autocom_innodb.test | 4 + mysql-test/t/loaddata_autocom_ndb.test | 4 + mysql-test/t/rpl_ndb_innodb_trans-slave.opt | 1 + mysql-test/t/rpl_ndb_innodb_trans.test | 66 +++++++++++++ sql/ha_ndbcluster.cc | 9 +- sql/sql_load.cc | 5 - 10 files changed, 251 insertions(+), 6 deletions(-) create mode 100644 mysql-test/include/loaddata_autocom.inc create mode 100644 mysql-test/r/loaddata_autocom_innodb.result create mode 100644 mysql-test/r/loaddata_autocom_ndb.result create mode 100644 mysql-test/r/rpl_ndb_innodb_trans.result create mode 100644 mysql-test/t/loaddata_autocom_innodb.test create mode 100644 mysql-test/t/loaddata_autocom_ndb.test create mode 100644 mysql-test/t/rpl_ndb_innodb_trans-slave.opt create mode 100644 mysql-test/t/rpl_ndb_innodb_trans.test diff --git a/mysql-test/include/loaddata_autocom.inc b/mysql-test/include/loaddata_autocom.inc new file mode 100644 index 00000000000..e5071c73c49 --- /dev/null +++ b/mysql-test/include/loaddata_autocom.inc @@ -0,0 +1,21 @@ +# Test if the engine does autocommit in LOAD DATA INFILE, or not +# (NDB wants to do, others don't). + +eval SET SESSION STORAGE_ENGINE = $engine_type; + +--disable_warnings +drop table if exists t1; +--enable_warnings + +create table t1 (a text, b text); +start transaction; +load data infile '../std_data_ln/loaddata2.dat' into table t1 fields terminated by ',' enclosed by ''''; +commit; +select count(*) from t1; +truncate table t1; +start transaction; +load data infile '../std_data_ln/loaddata2.dat' into table t1 fields terminated by ',' enclosed by ''''; +rollback; +select count(*) from t1; + +drop table t1; diff --git a/mysql-test/r/loaddata_autocom_innodb.result b/mysql-test/r/loaddata_autocom_innodb.result new file mode 100644 index 00000000000..10da6b5dde7 --- /dev/null +++ b/mysql-test/r/loaddata_autocom_innodb.result @@ -0,0 +1,21 @@ +SET SESSION STORAGE_ENGINE = InnoDB; +drop table if exists t1; +create table t1 (a text, b text); +start transaction; +load data infile '../std_data_ln/loaddata2.dat' into table t1 fields terminated by ',' enclosed by ''''; +Warnings: +Warning 1261 Row 3 doesn't contain data for all columns +commit; +select count(*) from t1; +count(*) +4 +truncate table t1; +start transaction; +load data infile '../std_data_ln/loaddata2.dat' into table t1 fields terminated by ',' enclosed by ''''; +Warnings: +Warning 1261 Row 3 doesn't contain data for all columns +rollback; +select count(*) from t1; +count(*) +0 +drop table t1; diff --git a/mysql-test/r/loaddata_autocom_ndb.result b/mysql-test/r/loaddata_autocom_ndb.result new file mode 100644 index 00000000000..94e5f825fa2 --- /dev/null +++ b/mysql-test/r/loaddata_autocom_ndb.result @@ -0,0 +1,23 @@ +SET SESSION STORAGE_ENGINE = ndbcluster; +drop table if exists t1; +create table t1 (a text, b text); +start transaction; +load data infile '../std_data_ln/loaddata2.dat' into table t1 fields terminated by ',' enclosed by ''''; +Warnings: +Warning 1261 Row 3 doesn't contain data for all columns +commit; +select count(*) from t1; +count(*) +4 +truncate table t1; +start transaction; +load data infile '../std_data_ln/loaddata2.dat' into table t1 fields terminated by ',' enclosed by ''''; +Warnings: +Warning 1261 Row 3 doesn't contain data for all columns +rollback; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +select count(*) from t1; +count(*) +4 +drop table t1; diff --git a/mysql-test/r/rpl_ndb_innodb_trans.result b/mysql-test/r/rpl_ndb_innodb_trans.result new file mode 100644 index 00000000000..148e6247b03 --- /dev/null +++ b/mysql-test/r/rpl_ndb_innodb_trans.result @@ -0,0 +1,103 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +create table t1 (a int, unique(a)) engine=ndbcluster; +create table t2 (a int, unique(a)) engine=innodb; +begin; +insert into t1 values(1); +insert into t2 values(1); +rollback; +select count(*) from t1; +count(*) +0 +select count(*) from t2; +count(*) +0 +select count(*) from t1; +count(*) +0 +select count(*) from t2; +count(*) +0 +begin; +load data infile '../std_data_ln/rpl_loaddata.dat' into table t2; +Warnings: +Warning 1262 Row 1 was truncated; it contained more data than there were input columns +Warning 1262 Row 2 was truncated; it contained more data than there were input columns +load data infile '../std_data_ln/rpl_loaddata.dat' into table t1; +Warnings: +Warning 1262 Row 1 was truncated; it contained more data than there were input columns +Warning 1262 Row 2 was truncated; it contained more data than there were input columns +rollback; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +select count(*) from t1; +count(*) +2 +select count(*) from t2; +count(*) +0 +select count(*) from t1; +count(*) +2 +select count(*) from t2; +count(*) +0 +delete from t1; +delete from t2; +begin; +load data infile '../std_data_ln/rpl_loaddata.dat' into table t2; +Warnings: +Warning 1262 Row 1 was truncated; it contained more data than there were input columns +Warning 1262 Row 2 was truncated; it contained more data than there were input columns +load data infile '../std_data_ln/rpl_loaddata.dat' into table t1; +Warnings: +Warning 1262 Row 1 was truncated; it contained more data than there were input columns +Warning 1262 Row 2 was truncated; it contained more data than there were input columns +rollback; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +select count(*) from t1; +count(*) +2 +select count(*) from t2; +count(*) +0 +select count(*) from t1; +count(*) +2 +select count(*) from t2; +count(*) +0 +delete from t1; +delete from t2; +begin; +insert into t2 values(3),(4); +insert into t1 values(3),(4); +load data infile '../std_data_ln/rpl_loaddata.dat' into table t2; +Warnings: +Warning 1262 Row 1 was truncated; it contained more data than there were input columns +Warning 1262 Row 2 was truncated; it contained more data than there were input columns +load data infile '../std_data_ln/rpl_loaddata.dat' into table t1; +Warnings: +Warning 1262 Row 1 was truncated; it contained more data than there were input columns +Warning 1262 Row 2 was truncated; it contained more data than there were input columns +rollback; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +select count(*) from t1; +count(*) +4 +select count(*) from t2; +count(*) +0 +select count(*) from t1; +count(*) +4 +select count(*) from t2; +count(*) +0 +drop table t1,t2; diff --git a/mysql-test/t/loaddata_autocom_innodb.test b/mysql-test/t/loaddata_autocom_innodb.test new file mode 100644 index 00000000000..d7f152cb286 --- /dev/null +++ b/mysql-test/t/loaddata_autocom_innodb.test @@ -0,0 +1,4 @@ +--source include/have_innodb.inc +let $engine_type= InnoDB; + +--source include/loaddata_autocom.inc diff --git a/mysql-test/t/loaddata_autocom_ndb.test b/mysql-test/t/loaddata_autocom_ndb.test new file mode 100644 index 00000000000..f4a6743aabe --- /dev/null +++ b/mysql-test/t/loaddata_autocom_ndb.test @@ -0,0 +1,4 @@ +--source include/have_ndb.inc +let $engine_type=ndbcluster; + +--source include/loaddata_autocom.inc diff --git a/mysql-test/t/rpl_ndb_innodb_trans-slave.opt b/mysql-test/t/rpl_ndb_innodb_trans-slave.opt new file mode 100644 index 00000000000..627becdbfb5 --- /dev/null +++ b/mysql-test/t/rpl_ndb_innodb_trans-slave.opt @@ -0,0 +1 @@ +--innodb diff --git a/mysql-test/t/rpl_ndb_innodb_trans.test b/mysql-test/t/rpl_ndb_innodb_trans.test new file mode 100644 index 00000000000..127c2464570 --- /dev/null +++ b/mysql-test/t/rpl_ndb_innodb_trans.test @@ -0,0 +1,66 @@ +# Test of a transaction mixing the two engines + +-- source include/have_ndb.inc +-- source include/have_innodb.inc +-- source include/master-slave.inc + +create table t1 (a int, unique(a)) engine=ndbcluster; +create table t2 (a int, unique(a)) engine=innodb; + + +begin; +insert into t1 values(1); +insert into t2 values(1); +rollback; + +select count(*) from t1; +select count(*) from t2; +sync_slave_with_master; +select count(*) from t1; +select count(*) from t2; +connection master; + +begin; +load data infile '../std_data_ln/rpl_loaddata.dat' into table t2; +load data infile '../std_data_ln/rpl_loaddata.dat' into table t1; +rollback; + +select count(*) from t1; +select count(*) from t2; +sync_slave_with_master; +select count(*) from t1; +select count(*) from t2; +connection master; + +delete from t1; +delete from t2; +begin; +load data infile '../std_data_ln/rpl_loaddata.dat' into table t2; +load data infile '../std_data_ln/rpl_loaddata.dat' into table t1; +rollback; + +select count(*) from t1; +select count(*) from t2; +sync_slave_with_master; +select count(*) from t1; +select count(*) from t2; +connection master; + +delete from t1; +delete from t2; +begin; +insert into t2 values(3),(4); +insert into t1 values(3),(4); +load data infile '../std_data_ln/rpl_loaddata.dat' into table t2; +load data infile '../std_data_ln/rpl_loaddata.dat' into table t1; +rollback; + +select count(*) from t1; +select count(*) from t2; +sync_slave_with_master; +select count(*) from t1; +select count(*) from t2; +connection master; + +drop table t1,t2; +sync_slave_with_master; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index a753e6e0be0..9c4a2c20ca0 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -3526,7 +3526,14 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) if (lock_type != F_UNLCK) { DBUG_PRINT("info", ("lock_type != F_UNLCK")); - if (!thd->transaction.on) + if (thd->lex->sql_command == SQLCOM_LOAD) + { + m_transaction_on= FALSE; + /* Would be simpler if has_transactions() didn't always say "yes" */ + thd->options|= OPTION_STATUS_NO_TRANS_UPDATE; + thd->no_trans_update= TRUE; + } + else if (!thd->transaction.on) m_transaction_on= FALSE; else m_transaction_on= thd->variables.ndb_use_transactions; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index b1ba2e96651..d5faf6ee7e9 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -147,10 +147,6 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, MYF(0)); DBUG_RETURN(TRUE); } - /* - This needs to be done before external_lock - */ - ha_enable_transaction(thd, FALSE); if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(TRUE); if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, @@ -394,7 +390,6 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); table->next_number_field=0; } - ha_enable_transaction(thd, TRUE); if (file >= 0) my_close(file,MYF(0)); free_blobs(table); /* if pack_blob was used */