From 54a492ecac792a13f374978ba0584d3e7eb9ab77 Mon Sep 17 00:00:00 2001 From: "tsmith@ramayana.hindu.god" <> Date: Mon, 14 Jan 2008 22:55:50 -0700 Subject: [PATCH] Apply innodb-5.1-ss2146 and innodb-5.1-ss2178 snapshots. Fixes: Bug #18942: DROP DATABASE does not drop an orphan FOREIGN KEY constraint Fix Bug#18942 by dropping all foreign key constraints at the end of DROP DATABASE. Usually, by then, there are no foreign constraints left because all of them are dropped when the relevant tables are dropped. This code is to ensure that any orphaned FKs are wiped too. Bug #29157: UPDATE, changed rows incorrect Return HA_ERR_RECORD_IS_THE_SAME from ha_innobase::update_row() if no columns were updated. Bug #32440: InnoDB free space info does not appear in SHOW TABLE STATUS or I_S Put information about the free space in a tablespace in INFORMATION_SCHEMA.TABLES.DATA_FREE. This information was previously available in INFORMATION_SCHEMA.TABLES.TABLE_COMMENT, but MySQL has removed it from there recently. The stored value is in kilobytes. This can be considered as a permanent workaround to http://bugs.mysql.com/32440. "Workaround" becasue that bug is about the data missing from TABLE_COMMENT and this is actually not solved. --- mysql-test/r/bdb_notembedded.result | 35 +++++++++++++ mysql-test/r/innodb.result | 13 +++++ mysql-test/r/partition_innodb.result | 12 ++--- mysql-test/t/bdb_notembedded.test | 38 ++++++++++++++ mysql-test/t/innodb.test | 21 ++++++++ mysql-test/t/partition_innodb.test | 18 +++++++ storage/innobase/handler/ha_innodb.cc | 29 +++++++++-- storage/innobase/row/row0mysql.c | 73 +++++++++++++++++++++++++++ storage/innobase/trx/trx0trx.c | 1 + 9 files changed, 229 insertions(+), 11 deletions(-) create mode 100644 mysql-test/r/bdb_notembedded.result create mode 100644 mysql-test/t/bdb_notembedded.test diff --git a/mysql-test/r/bdb_notembedded.result b/mysql-test/r/bdb_notembedded.result new file mode 100644 index 00000000000..14cb5fad915 --- /dev/null +++ b/mysql-test/r/bdb_notembedded.result @@ -0,0 +1,35 @@ +set autocommit=1; +reset master; +create table bug16206 (a int); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4 +f n Query 1 n use `test`; create table bug16206 (a int) +f n Query 1 n use `test`; insert into bug16206 values(1) +f n Query 1 n use `test`; insert into bug16206 values(2) +drop table bug16206; +reset master; +create table bug16206 (a int) engine= bdb; +insert into bug16206 values(0); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +insert into bug16206 values(3); +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4 +f n Query 1 n use `test`; create table bug16206 (a int) engine= bdb +f n Query 1 n use `test`; insert into bug16206 values(0) +f n Query 1 n use `test`; insert into bug16206 values(1) +f n Query 1 n use `test`; BEGIN +f n Query 1 n use `test`; insert into bug16206 values(2) +f n Query 1 n use `test`; COMMIT +f n Query 1 n use `test`; insert into bug16206 values(3) +drop table bug16206; +set autocommit=0; +End of 5.0 tests diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index d0c586de8d4..0a2cb61b18d 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -3218,3 +3218,16 @@ a 2 DROP TABLE t1; DROP TABLE t2; +create table t1 (i int, j int) engine=innodb; +insert into t1 (i, j) values (1, 1), (2, 2); +update t1 set j = 2; +affected rows: 1 +info: Rows matched: 2 Changed: 1 Warnings: 0 +drop table t1; +create table t1 (id int) comment='this is a comment' engine=innodb; +select table_comment, data_free > 0 as data_free_is_set +from information_schema.tables +where table_schema='test' and table_name = 't1'; +table_comment data_free_is_set +this is a comment 1 +drop table t1; diff --git a/mysql-test/r/partition_innodb.result b/mysql-test/r/partition_innodb.result index 5cbe34c94ca..8cb8bc7c13f 100644 --- a/mysql-test/r/partition_innodb.result +++ b/mysql-test/r/partition_innodb.result @@ -13,33 +13,33 @@ DROP TABLE t1; create table t1 (a int) engine=innodb partition by hash(a) ; show table status like 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 InnoDB 10 Compact 2 8192 16384 0 0 0 NULL NULL NULL NULL latin1_swedish_ci NULL partitioned +t1 InnoDB 10 Compact 2 8192 16384 0 0 # NULL NULL NULL NULL latin1_swedish_ci NULL partitioned drop table t1; create table t1 (a int) engine = innodb partition by key (a); show table status; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 InnoDB 10 Compact 2 8192 16384 0 0 0 NULL NULL NULL NULL latin1_swedish_ci NULL partitioned +t1 InnoDB 10 Compact 2 8192 16384 0 0 # NULL NULL NULL NULL latin1_swedish_ci NULL partitioned insert into t1 values (0), (1), (2), (3); show table status; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 InnoDB 10 Compact 4 4096 16384 0 0 0 NULL NULL NULL NULL latin1_swedish_ci NULL partitioned +t1 InnoDB 10 Compact 4 4096 16384 0 0 # NULL NULL NULL NULL latin1_swedish_ci NULL partitioned drop table t1; create table t1 (a int auto_increment primary key) engine = innodb partition by key (a); show table status; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 InnoDB 10 Compact 2 8192 16384 0 0 0 1 NULL NULL NULL latin1_swedish_ci NULL partitioned +t1 InnoDB 10 Compact 2 8192 16384 0 0 # 1 NULL NULL NULL latin1_swedish_ci NULL partitioned insert into t1 values (NULL), (NULL), (NULL), (NULL); show table status; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 InnoDB 10 Compact 4 4096 16384 0 0 0 5 NULL NULL NULL latin1_swedish_ci NULL partitioned +t1 InnoDB 10 Compact 4 4096 16384 0 0 # 5 NULL NULL NULL latin1_swedish_ci NULL partitioned insert into t1 values (NULL), (NULL), (NULL), (NULL); show table status; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 InnoDB 10 Compact 8 2048 16384 0 0 0 9 NULL NULL NULL latin1_swedish_ci NULL partitioned +t1 InnoDB 10 Compact 8 2048 16384 0 0 # 9 NULL NULL NULL latin1_swedish_ci NULL partitioned drop table t1; create table t1 (a int) partition by key (a) diff --git a/mysql-test/t/bdb_notembedded.test b/mysql-test/t/bdb_notembedded.test new file mode 100644 index 00000000000..24e64ebbfb2 --- /dev/null +++ b/mysql-test/t/bdb_notembedded.test @@ -0,0 +1,38 @@ +-- source include/not_embedded.inc +-- source include/have_bdb.inc + +# +# Bug #16206: Superfluous COMMIT event in binlog when updating BDB in autocommit mode +# +set autocommit=1; + +let $VERSION=`select version()`; + +reset master; +create table bug16206 (a int); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +--replace_result $VERSION VERSION +--replace_column 1 f 2 n 5 n +show binlog events; +drop table bug16206; + +reset master; +create table bug16206 (a int) engine= bdb; +insert into bug16206 values(0); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +insert into bug16206 values(3); +--replace_result $VERSION VERSION +--replace_column 1 f 2 n 5 n +show binlog events; +drop table bug16206; + +set autocommit=0; + + +--echo End of 5.0 tests diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index 8fdbbfcde79..bfe7d4ea9b4 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -2382,6 +2382,27 @@ DROP TABLE t1; DROP TABLE t2; DISCONNECT c1; DISCONNECT c2; +CONNECTION default; + +# +# Bug #29157 UPDATE, changed rows incorrect +# +create table t1 (i int, j int) engine=innodb; +insert into t1 (i, j) values (1, 1), (2, 2); +--enable_info +update t1 set j = 2; +--disable_info +drop table t1; + +# +# Bug #32440 InnoDB free space info does not appear in SHOW TABLE STATUS or +# I_S +# +create table t1 (id int) comment='this is a comment' engine=innodb; +select table_comment, data_free > 0 as data_free_is_set + from information_schema.tables + where table_schema='test' and table_name = 't1'; +drop table t1; ####################################################################### # # diff --git a/mysql-test/t/partition_innodb.test b/mysql-test/t/partition_innodb.test index 58010f85ecf..76a54b36942 100644 --- a/mysql-test/t/partition_innodb.test +++ b/mysql-test/t/partition_innodb.test @@ -20,6 +20,9 @@ DROP TABLE t1; # Bug #14673: Wrong InnoDB default row format # create table t1 (a int) engine=innodb partition by hash(a) ; +# Data_free for InnoDB tablespace varies depending on which +# tests have been run before this one +--replace_column 10 # show table status like 't1'; drop table t1; @@ -29,18 +32,33 @@ drop table t1; create table t1 (a int) engine = innodb partition by key (a); +# Data_free for InnoDB tablespace varies depending on which +# tests have been run before this one +--replace_column 10 # show table status; insert into t1 values (0), (1), (2), (3); +# Data_free for InnoDB tablespace varies depending on which +# tests have been run before this one +--replace_column 10 # show table status; drop table t1; create table t1 (a int auto_increment primary key) engine = innodb partition by key (a); +# Data_free for InnoDB tablespace varies depending on which +# tests have been run before this one +--replace_column 10 # show table status; insert into t1 values (NULL), (NULL), (NULL), (NULL); +# Data_free for InnoDB tablespace varies depending on which +# tests have been run before this one +--replace_column 10 # show table status; insert into t1 values (NULL), (NULL), (NULL), (NULL); +# Data_free for InnoDB tablespace varies depending on which +# tests have been run before this one +--replace_column 10 # show table status; drop table t1; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 205cfc91e52..e66d5b84d19 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -600,7 +600,9 @@ convert_error_code_to_mysql( tell it also to MySQL so that MySQL knows to empty the cached binlog for this transaction */ - thd_mark_transaction_to_rollback(thd, TRUE); + if (thd) { + thd_mark_transaction_to_rollback(thd, TRUE); + } return(HA_ERR_LOCK_DEADLOCK); } else if (error == (int) DB_LOCK_WAIT_TIMEOUT) { @@ -609,8 +611,10 @@ convert_error_code_to_mysql( latest SQL statement in a lock wait timeout. Previously, we rolled back the whole transaction. */ - thd_mark_transaction_to_rollback(thd, - (bool)row_rollback_on_timeout); + if (thd) { + thd_mark_transaction_to_rollback( + thd, (bool)row_rollback_on_timeout); + } return(HA_ERR_LOCK_WAIT_TIMEOUT); @@ -662,7 +666,9 @@ convert_error_code_to_mysql( tell it also to MySQL so that MySQL knows to empty the cached binlog for this transaction */ - thd_mark_transaction_to_rollback(thd, TRUE); + if (thd) { + thd_mark_transaction_to_rollback(thd, TRUE); + } return(HA_ERR_LOCK_TABLE_FULL); } else if (error == DB_TOO_MANY_CONCURRENT_TRXS) { @@ -3769,6 +3775,16 @@ ha_innobase::update_row( error = convert_error_code_to_mysql(error, user_thd); + if (error == 0 /* success */ + && uvect->n_fields == 0 /* no columns were updated */) { + + /* This is the same as success, but instructs + MySQL that the row is not really updated and it + should not increase the count of updated rows. + This is fix for http://bugs.mysql.com/29157 */ + error = HA_ERR_RECORD_IS_THE_SAME; + } + /* Tell InnoDB server that there might be work for utility threads: */ @@ -5720,7 +5736,9 @@ ha_innobase::info( stats.index_file_length = ((ulonglong) ib_table->stat_sum_of_other_index_sizes) * UNIV_PAGE_SIZE; - stats.delete_length = 0; + stats.delete_length = + fsp_get_available_space_in_free_extents( + ib_table->space); stats.check_time = 0; if (stats.records == 0) { @@ -7246,6 +7264,7 @@ On return if there is no error then the tables AUTOINC lock is locked.*/ ulong ha_innobase::innobase_get_auto_increment( +/*=====================================*/ ulonglong* value) /* out: autoinc value */ { ulong error; diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index b3cf20b78b8..9348cfa7342 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -3312,6 +3312,66 @@ funct_exit: return((int) err); } +/*********************************************************************** +Drop all foreign keys in a database, see Bug#18942. +Called at the end of row_drop_database_for_mysql(). */ +static +ulint +drop_all_foreign_keys_in_db( +/*========================*/ + /* out: error code or DB_SUCCESS */ + const char* name, /* in: database name which ends to '/' */ + trx_t* trx) /* in: transaction handle */ +{ + pars_info_t* pinfo; + ulint err; + + ut_a(name[strlen(name) - 1] == '/'); + + pinfo = pars_info_create(); + + pars_info_add_str_literal(pinfo, "dbname", name); + +/* true if for_name is not prefixed with dbname */ +#define TABLE_NOT_IN_THIS_DB \ +"SUBSTR(for_name, 0, LENGTH(:dbname)) <> :dbname" + + err = que_eval_sql(pinfo, + "PROCEDURE DROP_ALL_FOREIGN_KEYS_PROC () IS\n" + "foreign_id CHAR;\n" + "for_name CHAR;\n" + "found INT;\n" + "DECLARE CURSOR cur IS\n" + "SELECT ID, FOR_NAME FROM SYS_FOREIGN\n" + "WHERE FOR_NAME >= :dbname\n" + "LOCK IN SHARE MODE\n" + "ORDER BY FOR_NAME;\n" + "BEGIN\n" + "found := 1;\n" + "OPEN cur;\n" + "WHILE found = 1 LOOP\n" + " FETCH cur INTO foreign_id, for_name;\n" + " IF (SQL % NOTFOUND) THEN\n" + " found := 0;\n" + " ELSIF (" TABLE_NOT_IN_THIS_DB ") THEN\n" + " found := 0;\n" + " ELSIF (1=1) THEN\n" + " DELETE FROM SYS_FOREIGN_COLS\n" + " WHERE ID = foreign_id;\n" + " DELETE FROM SYS_FOREIGN\n" + " WHERE ID = foreign_id;\n" + " END IF;\n" + "END LOOP;\n" + "CLOSE cur;\n" + "COMMIT WORK;\n" + "END;\n", + FALSE, /* do not reserve dict mutex, + we are already holding it */ + trx); + + return(err); +} + /************************************************************************* Drops a database for MySQL. */ @@ -3382,6 +3442,19 @@ loop: } } + if (err == DB_SUCCESS) { + /* after dropping all tables try to drop all leftover + foreign keys in case orphaned ones exist */ + err = (int) drop_all_foreign_keys_in_db(name, trx); + + if (err != DB_SUCCESS) { + fputs("InnoDB: DROP DATABASE ", stderr); + ut_print_name(stderr, trx, TRUE, name); + fprintf(stderr, " failed with error %d while " + "dropping all foreign keys", err); + } + } + trx_commit_for_mysql(trx); row_mysql_unlock_data_dictionary(trx); diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c index a278ad51984..55b244abb0a 100644 --- a/storage/innobase/trx/trx0trx.c +++ b/storage/innobase/trx/trx0trx.c @@ -933,6 +933,7 @@ trx_commit_off_kernel( trx->rseg = NULL; trx->undo_no = ut_dulint_zero; trx->last_sql_stat_start.least_undo_no = ut_dulint_zero; + trx->mysql_query_str = NULL; ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0); ut_ad(UT_LIST_GET_LEN(trx->trx_locks) == 0);