List:Commits« Previous MessageNext Message »
From:Ingo Struewing Date:November 14 2007 7:37pm
Subject:bk commit into 5.1 tree (istruewing:1.2596) BUG#26379
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of istruewing. When istruewing does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-11-14 19:37:36+01:00, istruewing@stripped +16 -0
  Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
              corrupts a MERGE table
  
  Not to be pushed as is.
  Post-review fixes.
  Intermediate patch for review purpose.
  
  Still open issue: Move temporary detach to
  mark_temp_tables_as_free_for_reuse(). This requires a current
  repository.
  
  Not contained in this patch: Reverted creation of new test case
  rpl_merge. It was for CREATE...SELECT only.

  mysql-test/r/create.result@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +1
-1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Fixed test result.

  mysql-test/r/delayed.result@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +0
-5
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Moved test result from here to merge.result.

  mysql-test/r/merge.result@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +226
-80
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Fixed/added test result.

  mysql-test/t/create.test@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +1 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Fixed error number.

  mysql-test/t/delayed.test@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +0 -8
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Moved test from here to merge.test.

  mysql-test/t/merge.test@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +164
-49
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Fixed changes resulting from disabled CREATE...SELECT.
    Added more tests.
    Integrated test moved from delayed.test to here.

  sql/slave.cc@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +8 -2
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted to open_ltable().

  sql/sql_base.cc@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +192 -109
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Moved multi-line conditions into a member function of TABLE.
    Moved MERGE part of unlink_open_table() to new function
    unlink_open_merge().
    Moved MERGE part of reopen_tables() to new function
    reattach_merge().
    Prevented from infinite recursion when MERGE tables try to
    include themselves in a ring.
    Simplified the compatibility check in fix_merge_after_open().

  sql/sql_insert.cc@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +0 -86
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted changes from create_table_from_items().

  sql/sql_parse.cc@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +23 -15
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted changes from check_merge_table_access(). But kept it static.
    Reverted to open_ltable() in mysql_table_dump().
    Disabled use of CREATE...SELECT for MERGE tables.

  sql/sql_table.cc@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +18 -19
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Changed error reporting to asserting in prepare_for_restore()
    and prepare_for_repair().
    Disabled change of child list under LOCK TABLES.

  sql/table.cc@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +19 -0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added member function TABLE::is_children_attached().

  sql/table.h@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +1 -0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added member function TABLE::is_children_attached().

  storage/myisam/ha_myisam.cc@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +0
-22
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted new function myisam_engine_handle() to not implemented.

  storage/myisam/ha_myisam.h@stripped, 2007-11-14 19:37:34+01:00, istruewing@stripped +4
-4
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted new function myisam_engine_handle() to not implemented.
    Added new member function MI_INFO::file_ptr().

  storage/myisammrg/ha_myisammrg.cc@stripped, 2007-11-14 19:37:34+01:00,
istruewing@stripped +11 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Changed from myisam_engine_handle() to file_ptr().

diff -Nrup a/mysql-test/r/create.result b/mysql-test/r/create.result
--- a/mysql-test/r/create.result	2007-10-08 00:02:43 +02:00
+++ b/mysql-test/r/create.result	2007-11-14 19:37:34 +01:00
@@ -594,7 +594,7 @@ create table t1 (a int);
 create table t1 select * from t1;
 ERROR HY000: You can't specify target table 't1' for update in FROM clause
 create table t2 union = (t1) select * from t1;
-ERROR HY000: You can't specify target table 't1' for update in FROM clause
+ERROR HY000: 'test.t2' is not BASE TABLE
 flush tables with read lock;
 unlock tables;
 drop table t1;
diff -Nrup a/mysql-test/r/delayed.result b/mysql-test/r/delayed.result
--- a/mysql-test/r/delayed.result	2007-09-10 11:37:48 +02:00
+++ b/mysql-test/r/delayed.result	2007-11-14 19:37:34 +01:00
@@ -250,11 +250,6 @@ SELECT HEX(a) FROM t1;
 HEX(a)
 1
 DROP TABLE t1;
-CREATE TABLE t1(c1 INT) ENGINE=MyISAM;
-CREATE TABLE t2(c1 INT) ENGINE=MERGE UNION=(t1);
-INSERT DELAYED INTO t2 VALUES(1);
-ERROR HY000: Table storage engine for 't2' doesn't have this option
-DROP TABLE t1, t2;
 DROP TABLE IF EXISTS t1,t2;
 SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO';
 CREATE TABLE `t1` (
diff -Nrup a/mysql-test/r/merge.result b/mysql-test/r/merge.result
--- a/mysql-test/r/merge.result	2007-11-02 17:28:15 +01:00
+++ b/mysql-test/r/merge.result	2007-11-14 19:37:34 +01:00
@@ -615,6 +615,7 @@ create temporary table t3 (a int not nul
 select * from t3;
 ERROR HY000: Unable to open underlying table which is differently defined or of
non-MyISAM type or doesn't exist
 drop table t3, t2, t1;
+# CREATE...SELECT is not implemented for MERGE tables.
 CREATE TEMPORARY TABLE t1 (c1 INT NOT NULL);
 CREATE TEMPORARY TABLE t2 (c1 INT NOT NULL);
 CREATE TABLE t3 (c1 INT NOT NULL);
@@ -622,11 +623,13 @@ INSERT INTO t3 VALUES (3), (33);
 LOCK TABLES t3 READ;
 CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL) ENGINE=MERGE UNION=(t1,t2)
 INSERT_METHOD=LAST SELECT * FROM t3;
+ERROR HY000: 'test.t4' is not BASE TABLE
 SELECT * FROM t4;
-c1
-3
-33
+ERROR HY000: Table 't4' was not locked with LOCK TABLES
 UNLOCK TABLES;
+CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL) ENGINE=MERGE UNION=(t1,t2)
+INSERT_METHOD=LAST;
+INSERT INTO t4 SELECT * FROM t3;
 # Alter temporary MERGE table.
 ALTER TABLE t4 UNION=(t1);
 LOCK TABLES t4 WRITE;
@@ -693,11 +696,11 @@ create table t2 (a int);
 insert into t1 values (0);
 insert into t2 values (1);
 create table t3 engine=merge union=(t1, t2) select * from t1;
-ERROR HY000: You can't specify target table 't1' for update in FROM clause
+ERROR HY000: 'test.t3' is not BASE TABLE
 create table t3 engine=merge union=(t1, t2) select * from t2;
-ERROR HY000: You can't specify target table 't2' for update in FROM clause
+ERROR HY000: 'test.t3' is not BASE TABLE
 create table t3 engine=merge union=(t1, t2) select (select max(a) from t2);
-ERROR HY000: You can't specify target table 't2' for update in FROM clause
+ERROR HY000: 'test.t3' is not BASE TABLE
 drop table t1, t2;
 create table t1 (
 a double(14,4),
@@ -1058,71 +1061,31 @@ DROP TABLE t1;
 # Extra tests for Bug#26379 - Combination of FLUSH TABLE and
 #                             REPAIR TABLE corrupts a MERGE table
 #
-# CREATE ... SELECT
+# CREATE ... SELECT is disabled for MERGE tables.
 #
-CREATE TABLE t1 (c1 INT);
-CREATE TABLE t2 (c1 INT);
-CREATE TABLE t3 (c1 INT);
+CREATE TABLE t1(c1 INT);
 INSERT INTO t1 VALUES (1);
-INSERT INTO t2 VALUES (2);
-INSERT INTO t3 VALUES (3);
-# Show that LAST table is opened.
-CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
-INSERT_METHOD=LAST SELECT * FROM t3;
-SHOW CREATE TABLE t4;
-Table	Create Table
-t4	CREATE TABLE `t4` (
-  `c1` int(11) DEFAULT NULL
-) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`)
-SELECT * FROM t4 ORDER BY c1;
-c1
-1
-2
-3
-DROP TABLE t4;
-# Show that FIRST table is opened.
-CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
-INSERT_METHOD=FIRST SELECT * FROM t3;
-SHOW CREATE TABLE t4;
-Table	Create Table
-t4	CREATE TABLE `t4` (
-  `c1` int(11) DEFAULT NULL
-) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=FIRST UNION=(`t1`,`t2`)
-SELECT * FROM t4 ORDER BY c1;
-c1
-1
-2
-3
-3
-DROP TABLE t4;
-# Show that CREATE..SELECT is not possible under LOCK TABLES
-# for non-temporary tables.
-LOCK TABLES t1 WRITE, t2 WRITE, t3 READ;
-# Normal CREATE works.
-CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2);
-DROP TABLE t4;
-# CREATE...SELECT is rejected.
-CREATE TABLE t4 (c1 INT) SELECT * FROM t3;
-ERROR HY000: Table 't4' was not locked with LOCK TABLES
-CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
-INSERT_METHOD=LAST SELECT * FROM t3;
-ERROR HY000: Table 't4' was not locked with LOCK TABLES
-UNLOCK TABLES;
-# LOCK on parent table does not include children.
-CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2);
-LOCK TABLES t4 READ;
-SELECT * from t1, t4 ORDER BY c1;
-ERROR HY000: Table 't1' was not locked with LOCK TABLES
-# Show that dropping a locked MERGE table works.
-DROP TABLE t4;
-UNLOCK TABLES;
-DROP TABLE t3;
+CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
+CREATE TABLE t3 ENGINE=MRG_MYISAM INSERT_METHOD=LAST SELECT * FROM t2;
+ERROR HY000: Table 't3' is read only
+SHOW CREATE TABLE t3;
+ERROR 42S02: Table 'test.t3' doesn't exist
+CREATE TABLE t3 ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST
+SELECT * FROM t2;
+ERROR HY000: 'test.t3' is not BASE TABLE
+SHOW CREATE TABLE t3;
+ERROR 42S02: Table 'test.t3' doesn't exist
+DROP TABLE t1, t2;
 #
 # CREATE ... LIKE
 #
 # 1. Create like.
+CREATE TABLE t1 (c1 INT);
+CREATE TABLE t2 (c1 INT);
 CREATE TABLE t3 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
 INSERT_METHOD=LAST;
+INSERT INTO t1 VALUES (1);
+INSERT INTO t2 VALUES (2);
 INSERT INTO t3 VALUES (3);
 CREATE TABLE t4 LIKE t3;
 SHOW CREATE TABLE t4;
@@ -1175,8 +1138,6 @@ c1
 1
 2
 3
-3
-3
 RENAME TABLE t2 TO t5;
 SELECT * FROM t3 ORDER BY c1;
 ERROR 42S02: Table 'test.t2' doesn't exist
@@ -1186,8 +1147,6 @@ c1
 1
 2
 3
-3
-3
 #
 # 3. Normal rename with locked tables.
 LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE;
@@ -1196,8 +1155,6 @@ c1
 1
 2
 3
-3
-3
 RENAME TABLE t2 TO t5;
 ERROR HY000: Can't execute the given command because you have active locked tables or an
active transaction
 SELECT * FROM t3 ORDER BY c1;
@@ -1205,8 +1162,6 @@ c1
 1
 2
 3
-3
-3
 RENAME TABLE t5 TO t2;
 ERROR HY000: Can't execute the given command because you have active locked tables or an
active transaction
 SELECT * FROM t3 ORDER BY c1;
@@ -1214,8 +1169,6 @@ c1
 1
 2
 3
-3
-3
 UNLOCK TABLES;
 #
 # 4. Alter table rename.
@@ -1228,8 +1181,6 @@ c1
 1
 2
 3
-3
-3
 #
 # 5. Alter table rename with locked tables.
 LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE;
@@ -1245,7 +1196,43 @@ c1
 1
 2
 3
+#
+# Rename parent.
+#
+# 1. Normal rename with locked tables.
+LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE;
+SELECT * FROM t3 ORDER BY c1;
+c1
+1
+2
+3
+RENAME TABLE t3 TO t5;
+ERROR HY000: Can't execute the given command because you have active locked tables or an
active transaction
+SELECT * FROM t3 ORDER BY c1;
+c1
+1
+2
+3
+RENAME TABLE t5 TO t3;
+ERROR HY000: Can't execute the given command because you have active locked tables or an
active transaction
+SELECT * FROM t3 ORDER BY c1;
+c1
+1
+2
 3
+#
+# 5. Alter table rename with locked tables.
+ALTER TABLE t3 RENAME TO t5;
+SELECT * FROM t5 ORDER BY c1;
+ERROR HY000: Table 't5' was not locked with LOCK TABLES
+ALTER TABLE t5 RENAME TO t3;
+ERROR HY000: Table 't5' was not locked with LOCK TABLES
+UNLOCK TABLES;
+ALTER TABLE t5 RENAME TO t3;
+SELECT * FROM t3 ORDER BY c1;
+c1
+1
+2
 3
 DROP TABLE t1, t2, t3;
 #
@@ -1277,16 +1264,69 @@ ERROR 42S02: Table 'test.t1' doesn't exi
 UNLOCK TABLES;
 DROP TABLE t2;
 #
-# ALTER TABLE under LOCK TABLES. Grave change, table re-creation.
+# ALTER TABLE. Change child list.
 #
 CREATE TABLE t1 (c1 INT, INDEX(c1));
 CREATE TABLE t2 (c1 INT, INDEX(c1));
 CREATE TABLE t3 (c1 INT, INDEX(c1));
-CREATE TABLE t4 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2,t3)
-INSERT_METHOD=LAST;
 INSERT INTO t1 VALUES (1);
 INSERT INTO t2 VALUES (2);
 INSERT INTO t3 VALUES (3);
+CREATE TABLE t4 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t3,t2)
+INSERT_METHOD=LAST;
+# Shrink child list.
+ALTER TABLE t4 UNION=(t3);
+SHOW CREATE TABLE t4;
+Table	Create Table
+t4	CREATE TABLE `t4` (
+  `c1` int(11) DEFAULT NULL,
+  KEY `c1` (`c1`)
+) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t3`)
+SELECT * FROM t4 ORDER BY c1;
+c1
+3
+# Extend child list.
+ALTER TABLE t4 UNION=(t3,t2);
+SHOW CREATE TABLE t4;
+Table	Create Table
+t4	CREATE TABLE `t4` (
+  `c1` int(11) DEFAULT NULL,
+  KEY `c1` (`c1`)
+) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t3`,`t2`)
+SELECT * FROM t4 ORDER BY c1;
+c1
+2
+3
+#
+# ALTER TABLE under LOCK TABLES. Change child list.
+#
+LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE;
+# Shrink child list.
+ALTER TABLE t4 UNION=(t3);
+ERROR HY000: Can't execute the given command because you have active locked tables or an
active transaction
+# Extend child list within locked tables.
+ALTER TABLE t4 UNION=(t3,t2);
+ERROR HY000: Can't execute the given command because you have active locked tables or an
active transaction
+# Extend child list beyond locked tables.
+ALTER TABLE t4 UNION=(t3,t2,t1);
+ERROR HY000: Can't execute the given command because you have active locked tables or an
active transaction
+SHOW CREATE TABLE t4;
+Table	Create Table
+t4	CREATE TABLE `t4` (
+  `c1` int(11) DEFAULT NULL,
+  KEY `c1` (`c1`)
+) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t3`,`t2`)
+SELECT * FROM t4 ORDER BY c1;
+c1
+2
+3
+UNLOCK TABLES;
+DROP TABLE t4;
+#
+# ALTER TABLE under LOCK TABLES. Grave change, table re-creation.
+#
+CREATE TABLE t4 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2,t3)
+INSERT_METHOD=LAST;
 # Lock parent first and then children.
 LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE, t1 WRITE;
 ALTER TABLE t4 DROP INDEX c1, ADD UNIQUE INDEX (c1);
@@ -1458,6 +1498,8 @@ UNLOCK TABLES;
 #
 # Triggers
 #
+# Trigger on parent
+DELETE FROM t4 WHERE c1 = 4;
 CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1;
 SET @a=0;
 INSERT INTO t4 VALUES (4);
@@ -1471,7 +1513,7 @@ c1
 3
 4
 DROP TRIGGER t4_ai;
-# Create trigger with LOCK
+# Trigger on parent under LOCK TABLES
 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
 CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1;
 SET @a=0;
@@ -1489,6 +1531,98 @@ c1
 DROP TRIGGER t4_ai;
 UNLOCK TABLES;
 #
+# Trigger on child
+DELETE FROM t4 WHERE c1 = 4;
+CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1;
+SET @a=0;
+INSERT INTO t4 VALUES (4);
+SELECT @a;
+@a
+0
+INSERT INTO t3 VALUES (33);
+SELECT @a;
+@a
+1
+SELECT * FROM t4 ORDER BY c1;
+c1
+1
+2
+3
+4
+33
+DROP TRIGGER t3_ai;
+# Trigger on child under LOCK TABLES
+LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
+CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1;
+SET @a=0;
+INSERT INTO t4 VALUES (4);
+SELECT @a;
+@a
+0
+INSERT INTO t3 VALUES (33);
+SELECT @a;
+@a
+1
+SELECT * FROM t4 ORDER BY c1;
+c1
+1
+2
+3
+4
+4
+33
+33
+DELETE FROM t4 WHERE c1 = 33;
+DROP TRIGGER t3_ai;
+#
+# Trigger with table use on child
+DELETE FROM t4 WHERE c1 = 4;
+CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
+INSERT INTO t4 VALUES (4);
+SELECT * FROM t4 ORDER BY c1;
+c1
+1
+2
+3
+4
+INSERT INTO t3 VALUES (33);
+SELECT * FROM t4 ORDER BY c1;
+c1
+1
+2
+3
+4
+22
+33
+DELETE FROM t4 WHERE c1 = 22;
+DELETE FROM t4 WHERE c1 = 33;
+DROP TRIGGER t3_ai;
+# Trigger with table use on child under LOCK TABLES
+LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
+CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
+INSERT INTO t4 VALUES (4);
+SELECT * FROM t4 ORDER BY c1;
+c1
+1
+2
+3
+4
+4
+INSERT INTO t3 VALUES (33);
+SELECT * FROM t4 ORDER BY c1;
+c1
+1
+2
+3
+4
+4
+22
+33
+DROP TRIGGER t3_ai;
+DELETE FROM t4 WHERE c1 = 22;
+DELETE FROM t4 WHERE c1 = 33;
+UNLOCK TABLES;
+#
 # Repair
 #
 REPAIR TABLE t4;
@@ -1585,7 +1719,7 @@ UNLOCK TABLES;
 # Insert delayed
 #
 INSERT DELAYED INTO t4 VALUES(44);
-ERROR HY000: Table storage engine for 't4' doesn't have this option
+DELETE FROM t4 WHERE c1 = 44;
 INSERT DELAYED INTO t3 VALUES(33);
 SELECT * FROM t4 ORDER BY c1;
 c1
@@ -1610,6 +1744,18 @@ c1
 33
 UNLOCK TABLES;
 DROP TABLE t1, t2, t3, t4;
+#
+# Recursive inclusion of merge tables in their union clauses.
+#
+CREATE TABLE t1 (c1 INT, INDEX(c1));
+CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1)
+INSERT_METHOD=LAST;
+CREATE TABLE t3 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t2,t1)
+INSERT_METHOD=LAST;
+ALTER TABLE t2 UNION=(t3,t1);
+SELECT * FROM t2;
+ERROR HY000: Table 't3' is differently defined or of non-MyISAM type or doesn't exist
+DROP TABLE t1, t2, t3;
 CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
 CREATE TABLE t2 (c1 INT) ENGINE= MyISAM;
 CREATE TABLE t3 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1, t2);
diff -Nrup a/mysql-test/t/create.test b/mysql-test/t/create.test
--- a/mysql-test/t/create.test	2007-09-28 23:25:42 +02:00
+++ b/mysql-test/t/create.test	2007-11-14 19:37:34 +01:00
@@ -483,7 +483,7 @@ drop table t1,t2;
 create table t1 (a int);
 --error 1093
 create table t1 select * from t1;
---error 1093
+--error ER_WRONG_OBJECT
 create table t2 union = (t1) select * from t1;
 flush tables with read lock;
 unlock tables;
diff -Nrup a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test
--- a/mysql-test/t/delayed.test	2007-11-02 17:28:15 +01:00
+++ b/mysql-test/t/delayed.test	2007-11-14 19:37:34 +01:00
@@ -244,14 +244,6 @@ SELECT HEX(a) FROM t1;
 DROP TABLE t1;
 
 #
-# Bug#26464 - insert delayed + update + merge = corruption
-#
-CREATE TABLE t1(c1 INT) ENGINE=MyISAM;
-CREATE TABLE t2(c1 INT) ENGINE=MERGE UNION=(t1);
---error ER_ILLEGAL_HA
-INSERT DELAYED INTO t2 VALUES(1);
-DROP TABLE t1, t2;
-#
 # Bug#27358 INSERT DELAYED does not honour SQL_MODE of the client
 #
 --disable_warnings
diff -Nrup a/mysql-test/t/merge.test b/mysql-test/t/merge.test
--- a/mysql-test/t/merge.test	2007-11-02 17:28:15 +01:00
+++ b/mysql-test/t/merge.test	2007-11-14 19:37:34 +01:00
@@ -256,16 +256,21 @@ create temporary table t3 (a int not nul
 --error ER_WRONG_MRG_TABLE
 select * from t3;
 drop table t3, t2, t1;
-# Show CREATE...SELECT under LOCK TABLES:
+--echo # CREATE...SELECT is not implemented for MERGE tables.
 CREATE TEMPORARY TABLE t1 (c1 INT NOT NULL);
 CREATE TEMPORARY TABLE t2 (c1 INT NOT NULL);
 CREATE TABLE t3 (c1 INT NOT NULL);
 INSERT INTO t3 VALUES (3), (33);
 LOCK TABLES t3 READ;
+--error ER_WRONG_OBJECT
 CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL) ENGINE=MERGE UNION=(t1,t2)
   INSERT_METHOD=LAST SELECT * FROM t3;
+--error ER_TABLE_NOT_LOCKED
 SELECT * FROM t4;
 UNLOCK TABLES;
+CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL) ENGINE=MERGE UNION=(t1,t2)
+  INSERT_METHOD=LAST;
+INSERT INTO t4 SELECT * FROM t3;
 --echo # Alter temporary MERGE table.
 ALTER TABLE t4 UNION=(t1);
 LOCK TABLES t4 WRITE;
@@ -332,11 +337,11 @@ create table t1 (a int);
 create table t2 (a int);
 insert into t1 values (0);
 insert into t2 values (1);
---error 1093
+--error ER_WRONG_OBJECT
 create table t3 engine=merge union=(t1, t2) select * from t1;
---error 1093
+--error ER_WRONG_OBJECT
 create table t3 engine=merge union=(t1, t2) select * from t2;
---error 1093
+--error ER_WRONG_OBJECT
 create table t3 engine=merge union=(t1, t2) select (select max(a) from t2);
 drop table t1, t2;
 
@@ -770,58 +775,38 @@ DROP TABLE t1;
 # Cleanup
 disconnect con1;
 disconnect con2;
+#
 --echo #
 --echo # Extra tests for Bug#26379 - Combination of FLUSH TABLE and
 --echo #                             REPAIR TABLE corrupts a MERGE table
+#
 --echo #
---echo # CREATE ... SELECT
+--echo # CREATE ... SELECT is disabled for MERGE tables.
 --echo #
-CREATE TABLE t1 (c1 INT);
-CREATE TABLE t2 (c1 INT);
-CREATE TABLE t3 (c1 INT);
+CREATE TABLE t1(c1 INT);
 INSERT INTO t1 VALUES (1);
-INSERT INTO t2 VALUES (2);
-INSERT INTO t3 VALUES (3);
---echo # Show that LAST table is opened.
-CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
-  INSERT_METHOD=LAST SELECT * FROM t3;
-SHOW CREATE TABLE t4;
-SELECT * FROM t4 ORDER BY c1;
-DROP TABLE t4;
---echo # Show that FIRST table is opened.
-CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
-  INSERT_METHOD=FIRST SELECT * FROM t3;
-SHOW CREATE TABLE t4;
-SELECT * FROM t4 ORDER BY c1;
-DROP TABLE t4;
---echo # Show that CREATE..SELECT is not possible under LOCK TABLES
---echo # for non-temporary tables.
-LOCK TABLES t1 WRITE, t2 WRITE, t3 READ;
---echo # Normal CREATE works.
-CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2);
-DROP TABLE t4;
---echo # CREATE...SELECT is rejected.
---error ER_TABLE_NOT_LOCKED
-CREATE TABLE t4 (c1 INT) SELECT * FROM t3;
---error ER_TABLE_NOT_LOCKED
-CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
-  INSERT_METHOD=LAST SELECT * FROM t3;
-UNLOCK TABLES;
---echo # LOCK on parent table does not include children.
-CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2);
-LOCK TABLES t4 READ;
---error ER_TABLE_NOT_LOCKED
-SELECT * from t1, t4 ORDER BY c1;
---echo # Show that dropping a locked MERGE table works.
-DROP TABLE t4;
-UNLOCK TABLES;
-DROP TABLE t3;
+CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
+--error ER_OPEN_AS_READONLY
+CREATE TABLE t3 ENGINE=MRG_MYISAM INSERT_METHOD=LAST SELECT * FROM t2;
+--error ER_NO_SUCH_TABLE
+SHOW CREATE TABLE t3;
+--error ER_WRONG_OBJECT
+CREATE TABLE t3 ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST
+  SELECT * FROM t2;
+--error ER_NO_SUCH_TABLE
+SHOW CREATE TABLE t3;
+DROP TABLE t1, t2;
+#
 --echo #
 --echo # CREATE ... LIKE
 --echo #
 --echo # 1. Create like.
+CREATE TABLE t1 (c1 INT);
+CREATE TABLE t2 (c1 INT);
 CREATE TABLE t3 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
   INSERT_METHOD=LAST;
+INSERT INTO t1 VALUES (1);
+INSERT INTO t2 VALUES (2);
 INSERT INTO t3 VALUES (3);
 CREATE TABLE t4 LIKE t3;
 SHOW CREATE TABLE t4;
@@ -841,6 +826,7 @@ SHOW CREATE TABLE t4;
 --error ER_OPEN_AS_READONLY
 INSERT INTO t4 VALUES (4);
 DROP TABLE t4;
+#
 --echo #
 --echo # Rename child.
 --echo #
@@ -890,7 +876,31 @@ ALTER TABLE t5 RENAME TO t2;
 UNLOCK TABLES;
 ALTER TABLE t5 RENAME TO t2;
 SELECT * FROM t3 ORDER BY c1;
+#
+--echo #
+--echo # Rename parent.
+--echo #
+--echo # 1. Normal rename with locked tables.
+LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE;
+SELECT * FROM t3 ORDER BY c1;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
+RENAME TABLE t3 TO t5;
+SELECT * FROM t3 ORDER BY c1;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
+RENAME TABLE t5 TO t3;
+SELECT * FROM t3 ORDER BY c1;
+--echo #
+--echo # 5. Alter table rename with locked tables.
+ALTER TABLE t3 RENAME TO t5;
+--error ER_TABLE_NOT_LOCKED
+SELECT * FROM t5 ORDER BY c1;
+--error ER_TABLE_NOT_LOCKED
+ALTER TABLE t5 RENAME TO t3;
+UNLOCK TABLES;
+ALTER TABLE t5 RENAME TO t3;
+SELECT * FROM t3 ORDER BY c1;
 DROP TABLE t1, t2, t3;
+#
 --echo #
 --echo # Drop locked tables.
 --echo #
@@ -917,17 +927,50 @@ SELECT * FROM t2;
 SELECT * FROM t1;
 UNLOCK TABLES;
 DROP TABLE t2;
+#
 --echo #
---echo # ALTER TABLE under LOCK TABLES. Grave change, table re-creation.
+--echo # ALTER TABLE. Change child list.
 --echo #
 CREATE TABLE t1 (c1 INT, INDEX(c1));
 CREATE TABLE t2 (c1 INT, INDEX(c1));
 CREATE TABLE t3 (c1 INT, INDEX(c1));
-CREATE TABLE t4 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2,t3)
-  INSERT_METHOD=LAST;
 INSERT INTO t1 VALUES (1);
 INSERT INTO t2 VALUES (2);
 INSERT INTO t3 VALUES (3);
+CREATE TABLE t4 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t3,t2)
+  INSERT_METHOD=LAST;
+--echo # Shrink child list.
+ALTER TABLE t4 UNION=(t3);
+SHOW CREATE TABLE t4;
+SELECT * FROM t4 ORDER BY c1;
+--echo # Extend child list.
+ALTER TABLE t4 UNION=(t3,t2);
+SHOW CREATE TABLE t4;
+SELECT * FROM t4 ORDER BY c1;
+#
+--echo #
+--echo # ALTER TABLE under LOCK TABLES. Change child list.
+--echo #
+LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE;
+--echo # Shrink child list.
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
+ALTER TABLE t4 UNION=(t3);
+--echo # Extend child list within locked tables.
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
+ALTER TABLE t4 UNION=(t3,t2);
+--echo # Extend child list beyond locked tables.
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
+ALTER TABLE t4 UNION=(t3,t2,t1);
+SHOW CREATE TABLE t4;
+SELECT * FROM t4 ORDER BY c1;
+UNLOCK TABLES;
+DROP TABLE t4;
+#
+--echo #
+--echo # ALTER TABLE under LOCK TABLES. Grave change, table re-creation.
+--echo #
+CREATE TABLE t4 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2,t3)
+  INSERT_METHOD=LAST;
 --echo # Lock parent first and then children.
 LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE, t1 WRITE;
 ALTER TABLE t4 DROP INDEX c1, ADD UNIQUE INDEX (c1);
@@ -950,6 +993,7 @@ ALTER TABLE t2 DROP INDEX c1, ADD UNIQUE
 SELECT * FROM t4 ORDER BY c1;
 UNLOCK TABLES;
 DROP TABLE t1, t2, t3, t4;
+#
 --echo #
 --echo # ALTER TABLE under LOCK TABLES. Simple change, no re-creation.
 --echo #
@@ -982,6 +1026,7 @@ SELECT * FROM t4 ORDER BY c1;
 ALTER TABLE t2 ALTER COLUMN c1 SET DEFAULT 22;
 SELECT * FROM t4 ORDER BY c1;
 UNLOCK TABLES;
+#
 --echo #
 --echo # FLUSH TABLE under LOCK TABLES.
 --echo #
@@ -1012,16 +1057,19 @@ SELECT * FROM t4 ORDER BY c1;
 FLUSH TABLES;
 SELECT * FROM t4 ORDER BY c1;
 UNLOCK TABLES;
+#
 --echo #
 --echo # Triggers
 --echo #
+--echo # Trigger on parent
+DELETE FROM t4 WHERE c1 = 4;
 CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1;
 SET @a=0;
 INSERT INTO t4 VALUES (4);
 SELECT @a;
 SELECT * FROM t4 ORDER BY c1;
 DROP TRIGGER t4_ai;
---echo # Create trigger with LOCK
+--echo # Trigger on parent under LOCK TABLES
 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
 CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1;
 SET @a=0;
@@ -1031,6 +1079,51 @@ SELECT * FROM t4 ORDER BY c1;
 DROP TRIGGER t4_ai;
 UNLOCK TABLES;
 --echo #
+--echo # Trigger on child
+DELETE FROM t4 WHERE c1 = 4;
+CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1;
+SET @a=0;
+INSERT INTO t4 VALUES (4);
+SELECT @a;
+INSERT INTO t3 VALUES (33);
+SELECT @a;
+SELECT * FROM t4 ORDER BY c1;
+DROP TRIGGER t3_ai;
+--echo # Trigger on child under LOCK TABLES
+LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
+CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1;
+SET @a=0;
+INSERT INTO t4 VALUES (4);
+SELECT @a;
+INSERT INTO t3 VALUES (33);
+SELECT @a;
+SELECT * FROM t4 ORDER BY c1;
+DELETE FROM t4 WHERE c1 = 33;
+DROP TRIGGER t3_ai;
+--echo #
+--echo # Trigger with table use on child
+DELETE FROM t4 WHERE c1 = 4;
+CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
+INSERT INTO t4 VALUES (4);
+SELECT * FROM t4 ORDER BY c1;
+INSERT INTO t3 VALUES (33);
+SELECT * FROM t4 ORDER BY c1;
+DELETE FROM t4 WHERE c1 = 22;
+DELETE FROM t4 WHERE c1 = 33;
+DROP TRIGGER t3_ai;
+--echo # Trigger with table use on child under LOCK TABLES
+LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
+CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
+INSERT INTO t4 VALUES (4);
+SELECT * FROM t4 ORDER BY c1;
+INSERT INTO t3 VALUES (33);
+SELECT * FROM t4 ORDER BY c1;
+DROP TRIGGER t3_ai;
+DELETE FROM t4 WHERE c1 = 22;
+DELETE FROM t4 WHERE c1 = 33;
+UNLOCK TABLES;
+#
+--echo #
 --echo # Repair
 --echo #
 REPAIR TABLE t4;
@@ -1041,6 +1134,7 @@ REPAIR TABLE t4;
 REPAIR TABLE t2;
 SELECT * FROM t4 ORDER BY c1;
 UNLOCK TABLES;
+#
 --echo #
 --echo # Optimize
 --echo #
@@ -1052,6 +1146,7 @@ OPTIMIZE TABLE t4;
 OPTIMIZE TABLE t2;
 SELECT * FROM t4 ORDER BY c1;
 UNLOCK TABLES;
+#
 --echo #
 --echo # Checksum
 --echo #
@@ -1063,11 +1158,18 @@ CHECKSUM TABLE t4;
 CHECKSUM TABLE t2;
 SELECT * FROM t4 ORDER BY c1;
 UNLOCK TABLES;
+#
 --echo #
 --echo # Insert delayed
 --echo #
---error ER_ILLEGAL_HA
+# See also Bug#26464 - insert delayed + update + merge = corruption
+# Succeeds in embedded server - is converted to normal insert
+# Fails in normal server, ps-protocol - not supported by engine
+# Fails in normal server, normal protocol - not a base table
+--error 0, ER_ILLEGAL_HA, ER_WRONG_OBJECT
 INSERT DELAYED INTO t4 VALUES(44);
+# Get rid of row in embedded server
+DELETE FROM t4 WHERE c1 = 44;
 INSERT DELAYED INTO t3 VALUES(33);
   let $wait_cmd= SHOW STATUS LIKE 'Not_flushed_delayed_rows';
   let $run= query_get_value($wait_cmd, Value, 1);
@@ -1084,6 +1186,19 @@ INSERT DELAYED INTO t3 VALUES(333);
 SELECT * FROM t4 ORDER BY c1;
 UNLOCK TABLES;
 DROP TABLE t1, t2, t3, t4;
+#
+--echo #
+--echo # Recursive inclusion of merge tables in their union clauses.
+--echo #
+CREATE TABLE t1 (c1 INT, INDEX(c1));
+CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1)
+  INSERT_METHOD=LAST;
+CREATE TABLE t3 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t2,t1)
+  INSERT_METHOD=LAST;
+ALTER TABLE t2 UNION=(t3,t1);
+--error ER_ADMIN_WRONG_MRG_TABLE
+SELECT * FROM t2;
+DROP TABLE t1, t2, t3;
 
 
 #
diff -Nrup a/sql/slave.cc b/sql/slave.cc
--- a/sql/slave.cc	2007-11-02 17:28:15 +01:00
+++ b/sql/slave.cc	2007-11-14 19:37:34 +01:00
@@ -1013,8 +1013,14 @@ static int create_table_from_dump(THD* t
     goto err;                   // mysql_parse took care of the error send
 
   thd->proc_info = "Opening master dump table";
-  tables.table= NULL; /* was set by mysql_rm_table(). */
-  if (!open_n_lock_single_table(thd, &tables, TL_WRITE))
+  /*
+    Note: If this function starts to fail for MERGE tables,
+    change the next two lines to these:
+    tables.table= NULL; // was set by mysql_rm_table()
+    if (!open_n_lock_single_table(thd, &tables, TL_WRITE))
+  */
+  tables.lock_type = TL_WRITE;
+  if (!open_ltable(thd, &tables, TL_WRITE, 0))
   {
     sql_print_error("create_table_from_dump: could not open created table");
     goto err;
diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
--- a/sql/sql_base.cc	2007-11-02 17:28:15 +01:00
+++ b/sql/sql_base.cc	2007-11-14 19:37:34 +01:00
@@ -684,7 +684,7 @@ TABLE_SHARE *get_cached_table_share(cons
 
     thd->killed will be set if we run out of memory
 
-    If closing a MERGE child, the calling fuction has to take care for
+    If closing a MERGE child, the calling function has to take care for
     closing the parent too, if necessary.
 */
 
@@ -848,9 +848,7 @@ static void free_cache_entry(TABLE *tabl
   DBUG_ENTER("free_cache_entry");
 
   /* Assert that MERGE children are not attached before final close. */
-  DBUG_ASSERT((!table->child_l && !table->parent) ||
-              (table->child_l && !table->children_attached) ||
-              (table->parent && !table->parent->children_attached));
+  DBUG_ASSERT(!table->is_children_attached());
 
   intern_close_table(table);
   if (!table->in_use)
@@ -1382,9 +1380,7 @@ bool close_thread_table(THD *thd, TABLE 
     DBUG_ASSERT(!table->open_placeholder);
 
     /* Assert that MERGE children are not attached in unused_tables. */
-    DBUG_ASSERT((!table->child_l && !table->parent) ||
-                (table->child_l && !table->children_attached) ||
-                (table->parent && !table->parent->children_attached));
+    DBUG_ASSERT(!table->is_children_attached());
 
     /* Free memory and reset for next loop */
     table->file->ha_reset();
@@ -1935,9 +1931,7 @@ bool rename_temporary_table(THD* thd, TA
 static void relink_unused(TABLE *table)
 {
   /* Assert that MERGE children are not attached in unused_tables. */
-  DBUG_ASSERT((!table->child_l && !table->parent) ||
-              (table->child_l && !table->children_attached) ||
-              (table->parent && !table->parent->children_attached));
+  DBUG_ASSERT(!table->is_children_attached());
 
   if (table != unused_tables)
   {
@@ -1954,6 +1948,77 @@ static void relink_unused(TABLE *table)
 
 
 /**
+  @brief Prepare an open merge table for close.
+
+  @param[in]     thd             thread context
+  @param[in]     table           table to prepare
+  @param[in,out] prev_pp         pointer to pointer of previous table
+
+  @detail
+    If the table is a MERGE parent, just detach the children.
+    If the table is a MERGE child, close the parent (incl. detach).
+*/
+
+static void unlink_open_merge(THD *thd, TABLE *table, TABLE ***prev_pp)
+{
+  DBUG_ENTER("unlink_open_merge");
+
+  if (table->parent)
+  {
+    /*
+      If MERGE child, close parent too. Closing includes detaching.
+
+      This is used for example in ALTER TABLE t1 RENAME TO t5 under
+      LOCK TABLES where t1 is a MERGE child:
+      CREATE TABLE t1 (c1 INT);
+      CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1);
+      LOCK TABLES t1 WRITE, t2 WRITE;
+      ALTER TABLE t1 RENAME TO t5;
+    */
+    TABLE *parent= table->parent;
+    TABLE **prv_p;
+
+    /* Find parent in open_tables list. */
+    for (prv_p= &thd->open_tables;
+         *prv_p && (*prv_p != parent);
+         prv_p= &(*prv_p)->next) {}
+    if (*prv_p)
+    {
+      /* Special treatment required if child follows parent in list. */
+      if (*prev_pp == &parent->next)
+        *prev_pp= prv_p;
+      /*
+        Remove parent from open_tables list and close it.
+        This includes detaching and hence clearing parent references.
+      */
+      close_thread_table(thd, prv_p);
+    }
+  }
+  else if (table->child_l)
+  {
+    /*
+      When closing a MERGE parent, detach the children first. It is
+      not necessary to clear the child or parent table reference of
+      this table because the TABLE is freed. But we need to clear
+      the child or parent references of the other belonging tables
+      so that they cannot be moved into the unused_tables chain with
+      these pointers set.
+
+      This is used for example in ALTER TABLE t2 RENAME TO t5 under
+      LOCK TABLES where t2 is a MERGE parent:
+      CREATE TABLE t1 (c1 INT);
+      CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1);
+      LOCK TABLES t1 WRITE, t2 WRITE;
+      ALTER TABLE t2 RENAME TO t5;
+    */
+    detach_merge_children(table, TRUE);
+  }
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
     @brief  Remove all instances of table from thread's open list and
             table cache.
 
@@ -1995,37 +2060,9 @@ void unlink_open_table(THD *thd, TABLE *
       if (unlock && thd->locked_tables)
         mysql_lock_remove(thd, thd->locked_tables,
                           list->parent ? list->parent : list, TRUE);
-      if (list->parent)
-      {
-        /* If MERGE child, close parent too. Closing includes detaching. */
-        TABLE *parent= list->parent;
-        TABLE **prv_p;
-
-        /* Find parent in open_tables list. */
-        for (prv_p= &thd->open_tables;
-             *prv_p && (*prv_p != parent);
-             prv_p= &(*prv_p)->next) {}
-        if (*prv_p)
-        {
-          /* Special treatment required if child follows parent in list. */
-          if (prev == &parent->next)
-            prev= prv_p;
-          /* Remove parent from open_tables list and close it. */
-          close_thread_table(thd, prv_p);
-        }
-      }
-      else if (list->child_l)
-      {
-        /*
-          When closing a MERGE parent, detach the children first. It is
-          not necessary to clear the child or parent table reference of
-          this table because the TABLE is freed. But we need to clear
-          the child or parent references of the other belonging tables
-          so that they cannot be moved into the unused_tables chain with
-          these pointers set.
-        */
-        detach_merge_children(list, TRUE);
-      }
+
+      /* Prepare MERGE table for close. Close parent if necessary. */
+      unlink_open_merge(thd, list, &prev);
 
       /* Remove table from open_tables list. */
       *prev= list->next;
@@ -3102,6 +3139,62 @@ void close_data_files_and_morph_locks(TH
 
 
 /**
+  @brief Reattach MERGE children after reopen.
+
+  @param[in]     thd            thread context
+  @param[in,out] err_tables_p   pointer to pointer of tables in error
+
+  @return       status
+    @retval     FALSE           OK, err_tables_p unchanged
+    @retval     TRUE            Error, err_tables_p contains table(s)
+*/
+
+static bool reattach_merge(THD *thd, TABLE **err_tables_p)
+{
+  TABLE *table;
+  TABLE *next;
+  TABLE **prv_p= &thd->open_tables;
+  bool error= FALSE;
+  DBUG_ENTER("reattach_merge");
+
+  for (table= thd->open_tables; table; table= next)
+  {
+    next= table->next;
+    DBUG_PRINT("tcache", ("check table: '%s'.'%s' 0x%lx  next: 0x%lx",
+                          table->s->db.str, table->s->table_name.str,
+                          (long) table, (long) next));
+    /* Reattach children for MERGE tables with "closed data files" only. */
+    if (table->child_l && !table->children_attached)
+    {
+      DBUG_PRINT("tcache", ("MERGE parent, attach children"));
+      if(table->file->extra(HA_EXTRA_ATTACH_CHILDREN))
+      {
+        my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
+        error= TRUE;
+        /* Remove table from open_tables. */
+        *prv_p= next;
+        if (next)
+          prv_p= &next->next;
+        /* Stack table on error list. */
+        table->next= *err_tables_p;
+        *err_tables_p= table;
+        continue;
+      }
+      else
+      {
+        table->children_attached= TRUE;
+        DBUG_PRINT("myrg", ("attached parent: '%s'.'%s' 0x%lx",
+                            table->s->db.str,
+                            table->s->table_name.str, (long) table));
+      }
+    }
+    prv_p= &table->next;
+  }
+  DBUG_RETURN(error);
+}
+
+
+/**
     @brief Reopen all tables with closed data files.
 
     @param thd         Thread context
@@ -3125,6 +3218,7 @@ bool reopen_tables(THD *thd,bool get_loc
 {
   TABLE *table,*next,**prev;
   TABLE **tables,**tables_ptr;			// For locks
+  TABLE *err_tables= NULL;
   bool error=0, not_used;
   bool merge_table_found= FALSE;
   DBUG_ENTER("reopen_tables");
@@ -3194,37 +3288,18 @@ bool reopen_tables(THD *thd,bool get_loc
       }
     }
   }
+  *prev=0;
   /*
     When all tables are open again, we can re-attach MERGE children to
     their parents. All TABLE objects are still present.
   */
   DBUG_PRINT("tcache", ("re-attaching MERGE tables: %d", merge_table_found));
-  if (!error && merge_table_found)
+  if (!error && merge_table_found && reattach_merge(thd,
&err_tables))
   {
-    for (table= thd->open_tables; table; table= next)
+    while (err_tables)
     {
-      next= table->next;
-      DBUG_PRINT("tcache", ("check table: '%s'.'%s' 0x%lx  next: 0x%lx",
-                            table->s->db.str, table->s->table_name.str,
-                            (long) table, (long) next));
-      /* Reattach children for MERGE tables with "closed data files" only. */
-      if (table->child_l && !table->children_attached)
-      {
-        DBUG_PRINT("tcache", ("MERGE parent, attach children"));
-        if(table->file->extra(HA_EXTRA_ATTACH_CHILDREN))
-        {
-          my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
-          VOID(hash_delete(&open_cache, (uchar*) table));
-          error=1;
-        }
-        else
-        {
-          table->children_attached= TRUE;
-          DBUG_PRINT("myrg", ("attached parent: '%s'.'%s' 0x%lx",
-                              table->s->db.str,
-                              table->s->table_name.str, (long) table));
-        }
-      }
+      VOID(hash_delete(&open_cache, (uchar*) err_tables));
+      err_tables= err_tables->next;
     }
   }
   DBUG_PRINT("tcache", ("open tables to lock: %u",
@@ -3259,7 +3334,6 @@ bool reopen_tables(THD *thd,bool get_loc
     my_afree((uchar*) tables);
   }
   broadcast_refresh();
-  *prev=0;
   DBUG_RETURN(error);
 }
 
@@ -3816,16 +3890,17 @@ err:
 /**
   @brief Add list of MERGE children to a TABLE_LIST list.
 
-  @detail When a MERGE parent table has just been opened, insert the
-    TABLE_LIST chain from the MERGE handle into the table list used for
-    opening tables for this statement. This lets the children be opened
-    too.
-
-  @param[in]    tlist           the TABLE_LIST object just opened
+  @param[in]    tlist           the parent TABLE_LIST object just opened
 
   @return status
     @retval     0               OK
     @retval     != 0            Error
+
+  @detail
+    When a MERGE parent table has just been opened, insert the
+    TABLE_LIST chain from the MERGE handle into the table list used for
+    opening tables for this statement. This lets the children be opened
+    too.
 */
 
 static int add_merge_table_list(TABLE_LIST *tlist)
@@ -3840,6 +3915,12 @@ static int add_merge_table_list(TABLE_LI
   DBUG_ASSERT(!parent->children_attached);
   /* Must not call this with children list in place. */
   DBUG_ASSERT(tlist->next_global != parent->child_l);
+  /* Prevent inclusion of another MERGE table. Could make infinite recursion. */
+  if (tlist->parent_l)
+  {
+    my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), tlist->alias);
+    DBUG_RETURN(1);
+  }
 
   /* Fix children.*/
   for (child_l= parent->child_l; ; child_l= child_l->next_global)
@@ -3878,16 +3959,17 @@ static int add_merge_table_list(TABLE_LI
 /**
   @brief Attach MERGE children to the parent.
 
-  @detail When the last MERGE child has just been opened, let the
-    handler attach the MyISAM tables to the MERGE table. Remove MERGE
-    TABLE_LIST chain from the statement list so that it cannot be
-    changed or freed.
-
-  @param[in]    tlist           the TABLE_LIST object just opened
+  @param[in]    tlist           the child TABLE_LIST object just opened
 
   @return status
     @retval     0               OK
     @retval     != 0            Error
+
+  @note
+    This is called when the last MERGE child has just been opened, let
+    the handler attach the MyISAM tables to the MERGE table. Remove
+    MERGE TABLE_LIST chain from the statement list so that it cannot be
+    changed or freed.
 */
 
 static int attach_merge_children(TABLE_LIST *tlist)
@@ -4055,13 +4137,27 @@ void detach_merge_children(TABLE *table,
     @retval     FALSE           OK, no mismatch
     @retval     TRUE            Error, lists mismatch
 
-  @detail Main action is to copy TABLE reference for each member of
-    original child list to new child list. After a fresh open these
-    references are NULL. Assign the old children to the new table.
-    Some of them might also be reopened or will be reopened soon.
+  @detail
+    Main action is to copy TABLE reference for each member of original
+    child list to new child list. After a fresh open these references
+    are NULL. Assign the old children to the new table. Some of them
+    might also be reopened or will be reopened soon.
 
     Other action is to verify that the table definition with respect to
     the UNION list did not change.
+
+  @note
+    This function terminates the child list if the respective '*_last'
+    pointer is non-NULL. Do not call it from a place where the list is
+    embedded in another list and this would break it.
+
+    Terminating the list is required for example in the first
+    reopen_table() after open_tables(). open_tables() requires the end
+    of the list not to be terminated because other tables could follow
+    behind the child list.
+
+    If a '*_last' pointer is NULL, the respective list is assumed to be
+    NULL terminated.
 */
 
 bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
@@ -4072,6 +4168,12 @@ bool fix_merge_after_open(TABLE_LIST *ol
   DBUG_PRINT("myrg", ("old last addr: 0x%lx  new last addr: 0x%lx",
                       (long) old_last, (long) new_last));
 
+  /* Terminate the lists for easier check of list end. */
+  if (old_last)
+    *old_last= NULL;
+  if (new_last)
+    *new_last= NULL;
+
   for (;;)
   {
     DBUG_PRINT("myrg", ("old list item: 0x%lx  new list item: 0x%lx",
@@ -4089,10 +4191,7 @@ bool fix_merge_after_open(TABLE_LIST *ol
     /* Child db.table names must match. */
     if (strcmp(old_child_list->table_name, new_child_list->table_name) ||
         strcmp(old_child_list->db,         new_child_list->db))
-    {
-      mismatch= TRUE;
       break;
-    }
     /*
       Copy TABLE reference. Child TABLE objects are still in place
       though not necessarily open yet.
@@ -4101,35 +4200,19 @@ bool fix_merge_after_open(TABLE_LIST *ol
                         (long) old_child_list->table,
                         (long) new_child_list->table));
     new_child_list->table= old_child_list->table;
-    /*
-      Break if one of the lists is at its last child. When the child
-      list is part of the statement list, the list may continue after
-      the last child. The test for NULL is insufficient.
-    */
-    DBUG_PRINT("myrg", ("old next addr: 0x%lx  new next addr: 0x%lx",
-                        (long) &old_child_list->next_global,
-                        (long) &new_child_list->next_global));
-    if ((old_last && (&old_child_list->next_global == old_last)) ||
-        (new_last && (&new_child_list->next_global == new_last)))
-      break;
     /* Step both lists. */
     old_child_list= old_child_list->next_global;
     new_child_list= new_child_list->next_global;
   }
   DBUG_PRINT("myrg", ("end of list, mismatch: %d", mismatch));
-  if (!mismatch &&
-      (/* old list at end */
-       (!old_child_list || (!old_last && !old_child_list->next_global) ||
-        (old_last && (&old_child_list->next_global == old_last)))
&&
-       /* and new list not at end */
-       (new_child_list && new_child_list->next_global &&
-        (!new_last || (&new_child_list->next_global != new_last)))) ||
-      (/* new list at end */
-       (!new_child_list || (!new_last && !new_child_list->next_global) ||
-        (new_last && (&new_child_list->next_global == new_last)))
&&
-       /* and old list not at end */
-       (old_child_list && old_child_list->next_global &&
-        (!old_last || (&old_child_list->next_global != old_last)))))
+  /*
+    If the list pointers are not both NULL after the loop, then the
+    lists differ. If the are both identical, but not NULL, then they
+    have at least one table in common and hence the rest of the list
+    would be identical too. But in this case the loop woul run until the
+    list end, where both pointers would become NULL.
+  */
+  if (old_child_list != new_child_list)
     mismatch= TRUE;
   if (mismatch)
     my_error(ER_TABLE_DEF_CHANGED, MYF(0));
diff -Nrup a/sql/sql_insert.cc b/sql/sql_insert.cc
--- a/sql/sql_insert.cc	2007-11-02 17:28:15 +01:00
+++ b/sql/sql_insert.cc	2007-11-14 19:37:34 +01:00
@@ -3372,7 +3372,6 @@ static TABLE *create_table_from_items(TH
 
       if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
       {
-        /* Non-temporary table. */
         VOID(pthread_mutex_lock(&LOCK_open));
         if (reopen_name_locked_table(thd, create_table, FALSE))
         {
@@ -3383,50 +3382,9 @@ static TABLE *create_table_from_items(TH
         else
           table= create_table->table;
         VOID(pthread_mutex_unlock(&LOCK_open));
-
-        /*
-          Non-temporary MERGE parent and children need be open, attached
-          and locked for select. The children have been opened and
-          locked at statement begin through the UNION=() clause of the
-          CREATE TABLE part of the statement. A simple CREATE TABLE does
-          not open any table. But CREATE...SELECT opens all tables,
-          including the MERGE child tables.
-
-          We cannot use the existing locks of the children directly. For
-          replication we need an 'extra_lock' on the parent table. We
-          cannot first unlock the children and then lock them through
-          the parent. This would allow other threads to step in.
-
-          So we need new TABLE objects for the children so that they can
-          be locked through the parent. Later we remove the locks from
-          the original child TABLEs.
-        */
-        if (table && table->child_l)
-        {
-          uint counter;
-          /* Assert that child list is terminated. */
-          DBUG_ASSERT(!*table->child_last_l);
-          if (open_tables(thd, &create_table->table->child_l, &counter,
-                          MYSQL_LOCK_IGNORE_FLUSH) ||
-              table->file->extra(HA_EXTRA_ATTACH_CHILDREN))
-          {
-            if (!create_info->table_existed)
-              drop_open_table(thd, table, create_table->db,
-                              create_table->table_name);
-            table= NULL;
-          }
-          else
-          {
-            table->children_attached= TRUE;
-            DBUG_PRINT("myrg", ("attached parent: '%s'.'%s' 0x%lx",
-                                table->s->db.str,
-                                table->s->table_name.str, (long) table));
-          }
-        }
       }
       else
       {
-        /* Temporary table. */
         if (!(table= open_table(thd, create_table, thd->mem_root, (bool*) 0,
                                 MYSQL_OPEN_TEMPORARY_ONLY)) &&
             !create_info->table_existed)
@@ -3438,37 +3396,6 @@ static TABLE *create_table_from_items(TH
           */
           close_temporary_table(thd, create_table);
         }
-
-        /*
-          Temporary MERGE parent and children need be open and attached
-          for select. The children have been opened at statement begin
-          through the UNION=() clause of the CREATE TABLE part of the
-          statement. A simple CREATE TABLE does not open any table. But
-          CREATE...SELECT opens all tables, including the MERGE child
-          tables.
-
-          We can just use the open children since we do not lock
-          temporary tables. Note that a temporary MERGE table must have
-          temporary children. So grab open MERGE children from
-          create_info->merge_list.
-        */
-        if (table && table->child_l)
-        {
-          if (fix_merge_after_open((TABLE_LIST*) create_info->merge_list.first,
-                                   NULL, table->child_l, table->child_last_l) ||
-              table->file->extra(HA_EXTRA_ATTACH_CHILDREN))
-          {
-            close_temporary_table(thd, create_table);
-            table= NULL;
-          }
-          else
-          {
-            table->children_attached= TRUE;
-            DBUG_PRINT("myrg", ("attached parent: '%s'.'%s' 0x%lx",
-                                table->s->db.str,
-                                table->s->table_name.str, (long) table));
-          }
-        }
       }
     }
     reenable_binlog(thd);
@@ -3497,19 +3424,6 @@ static TABLE *create_table_from_items(TH
     if (!create_info->table_existed)
       drop_open_table(thd, table, create_table->db, create_table->table_name);
     DBUG_RETURN(0);
-  }
-
-  /*
-    For a non-temporary MERGE table we have now extra locks on the
-    children through the parent. We need to remove the locks from the
-    original child TABLEs.
-  */
-  if (table->child_l && !(create_info->options &
HA_LEX_CREATE_TMP_TABLE))
-  {
-    TABLE_LIST *tlist= (TABLE_LIST*) create_info->merge_list.first;
-    for (; tlist; tlist= tlist->next_global)
-      mysql_lock_remove(thd, thd->lock ? thd->lock : thd->locked_tables,
-                        tlist->table, FALSE);
   }
   DBUG_RETURN(table);
 }
diff -Nrup a/sql/sql_parse.cc b/sql/sql_parse.cc
--- a/sql/sql_parse.cc	2007-11-02 17:28:15 +01:00
+++ b/sql/sql_parse.cc	2007-11-14 19:37:34 +01:00
@@ -488,8 +488,8 @@ end:
     every child. Set 'db' for every child if not present.
 */
 
-static bool check_merge_table_access_fix_locks(THD *thd, char *db,
-                                               TABLE_LIST *table_list)
+static bool check_merge_table_access(THD *thd, char *db,
+                                     TABLE_LIST *table_list)
 {
   int error= 0;
 
@@ -500,12 +500,6 @@ static bool check_merge_table_access_fix
 
     for (tlist= table_list; tlist; tlist= tlist->next_local)
     {
-      /*
-        We create or alter a MERGE table. In both cases the MERGE child
-        tables must be locked in write mode.
-      */
-      tlist->lock_type= TL_WRITE;
-
       if (!tlist->db || !tlist->db[0])
         tlist->db= db; /* purecov: inspected */
     }
@@ -587,7 +581,8 @@ int mysql_table_dump(THD *thd, LEX_STRIN
   if (lower_case_table_names)
     my_casedn_str(files_charset_info, tbl_name);
 
-  if (!(table= open_n_lock_single_table(thd, table_list, TL_READ_NO_INSERT)))
+  if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT, 0)))
+    //if (!(table= open_n_lock_single_table(thd, table_list, TL_READ_NO_INSERT)))
     DBUG_RETURN(1);
 
   if (check_one_table_access(thd, SELECT_ACL, table_list))
@@ -2300,6 +2295,19 @@ mysql_execute_command(THD *thd)
       select_lex->options|= SELECT_NO_UNLOCK;
       unit->set_limit(select_lex);
 
+      /*
+        Disable non-empty MERGE tables with CREATE...SELECT. Too
+        complicated. See Bug #26379. Empty MERGE tables are read-only
+        and don't allow CREATE...SELECT anyway.
+      */
+      if (create_info.used_fields & HA_CREATE_USED_UNION)
+      {
+        my_error(ER_WRONG_OBJECT, MYF(0), create_table->db,
+                 create_table->table_name, "BASE TABLE");
+        res= 1;
+        goto end_with_restore_list;
+      }
+
       if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
       {
         lex->link_first_table_back(create_table, link_to_local);
@@ -2496,9 +2504,9 @@ end_with_restore_list:
                        test(first_table->schema_table)) ||
 	  check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0,
                        is_schema_db(select_lex->db))||
-          check_merge_table_access_fix_locks(thd, first_table->db,
-                                             (TABLE_LIST *)
-                                             create_info.merge_list.first))
+          check_merge_table_access(thd, first_table->db,
+                                   (TABLE_LIST *)
+                                   create_info.merge_list.first))
 	goto error;				/* purecov: inspected */
       if (check_grant(thd, priv_needed, all_tables, 0, UINT_MAX, 0))
         goto error;
@@ -7004,9 +7012,9 @@ bool create_table_precheck(THD *thd, TAB
   if (check_access(thd, want_priv, create_table->db,
 		   &create_table->grant.privilege, 0, 0,
                    test(create_table->schema_table)) ||
-      check_merge_table_access_fix_locks(thd, create_table->db,
-                                         (TABLE_LIST *)
-                                         lex->create_info.merge_list.first))
+      check_merge_table_access(thd, create_table->db,
+                               (TABLE_LIST *)
+                               lex->create_info.merge_list.first))
     goto err;
   if (want_priv != CREATE_TMP_ACL &&
       check_grant(thd, want_priv, create_table, 0, 1, 0))
diff -Nrup a/sql/sql_table.cc b/sql/sql_table.cc
--- a/sql/sql_table.cc	2007-11-02 17:28:16 +01:00
+++ b/sql/sql_table.cc	2007-11-14 19:37:34 +01:00
@@ -3827,16 +3827,8 @@ static int prepare_for_restore(THD* thd,
     DBUG_RETURN(send_check_errmsg(thd, table, "restore",
                                   "Failed to open partially restored table"));
   }
-  if (table->table && table->table->child_l)
-  {
-    /* A MERGE table must not come here. */
-    /* purecov: begin inspected */
-    unlock_table_name(thd, table);
-    pthread_mutex_unlock(&LOCK_open);
-    DBUG_RETURN(send_check_errmsg(thd, table, "restore",
-                                  "Not supported for MERGE tables"));
-    /* purecov: end */
-  }
+  /* A MERGE table must not come here. */
+  DBUG_ASSERT(!table->table || !table->table->child_l);
   pthread_mutex_unlock(&LOCK_open);
   DBUG_RETURN(0);
 }
@@ -3880,15 +3872,8 @@ static int prepare_for_repair(THD *thd, 
     pthread_mutex_unlock(&LOCK_open);
   }
 
-  if (table->child_l)
-  {
-    /* A MERGE table must not come here. */
-    /* purecov: begin inspected */
-    error= send_check_errmsg(thd, table_list, "repair",
-                             "Not supported for MERGE tables");
-    goto end;
-    /* purecov: end */
-  }
+  /* A MERGE table must not come here. */
+  DBUG_ASSERT(!table->child_l);
 
   /*
     REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
@@ -5870,6 +5855,20 @@ view_err:
   if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ)))
     DBUG_RETURN(TRUE);
   table->use_all_columns();
+
+  /*
+    Prohibit changing of the UNION list of a non-temporary MERGE table
+    under LOCK tables. It would be quite difficult to reuse a shrinked
+    set of tables from the old table or to open a new TABLE object for
+    an extended list and verify that they belong to locked tables.
+  */
+  if (thd->locked_tables &&
+      (create_info->used_fields & HA_CREATE_USED_UNION) &&
+      (table->s->tmp_table == NO_TMP_TABLE))
+  {
+    my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+    DBUG_RETURN(TRUE);
+  }
 
   /* Check that we are not trying to rename to an existing table */
   if (new_name)
diff -Nrup a/sql/table.cc b/sql/table.cc
--- a/sql/table.cc	2007-11-02 17:28:16 +01:00
+++ b/sql/table.cc	2007-11-14 19:37:34 +01:00
@@ -4477,6 +4477,25 @@ void st_table::mark_columns_needed_for_i
     mark_auto_increment_column();
 }
 
+
+/**
+  @brief Check if this is part of a MERGE table with attached children.
+
+  @return       status
+    @retval     TRUE            children are attached
+    @retval     FALSE           no MERGE part or children not attached
+
+  @detail
+    A MERGE table consists of a parent TABLE and zero or more child
+    TABLEs. Each of these TABLEs is called a part of a MERGE table.
+*/
+
+bool st_table::is_children_attached(void)
+{
+  return((child_l && children_attached) ||
+         (parent && parent->children_attached));
+}
+
 /*
   Cleanup this table for re-execution.
 
diff -Nrup a/sql/table.h b/sql/table.h
--- a/sql/table.h	2007-11-02 17:28:16 +01:00
+++ b/sql/table.h	2007-11-14 19:37:34 +01:00
@@ -668,6 +668,7 @@ struct st_table {
   */
   inline bool needs_reopen_or_name_lock()
   { return s->version != refresh_version; }
+  bool is_children_attached(void);
 };
 
 enum enum_schema_table_state
diff -Nrup a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
--- a/storage/myisam/ha_myisam.cc	2007-11-02 17:28:16 +01:00
+++ b/storage/myisam/ha_myisam.cc	2007-11-14 19:37:34 +01:00
@@ -2124,25 +2124,3 @@ my_bool ha_myisam::register_query_cache_
 }
 #endif
 
-
-/**
-  @brief Extract the MyISAM table structure pointer from a handler object.
-
-  @param[in]    handler_handle  pointer to handler object
-
-  @return       MyISAM table structure pointer
-    @retval     !=NULL          Ok, returning pointer
-    @retval     NULL            Error: handler has not a MyISAM table open
-*/
-
-MI_INFO *myisam_engine_handle(handler *handler_handle)
-{
-  DBUG_ENTER("myisam_engine_handle");
-  if (handler_handle->ht->db_type != DB_TYPE_MYISAM)
-  {
-    DBUG_PRINT("error", ("not a MyISAM table"));
-    DBUG_RETURN(NULL);
-  }
-  DBUG_RETURN(((ha_myisam*) handler_handle)->file);
-}
-
diff -Nrup a/storage/myisam/ha_myisam.h b/storage/myisam/ha_myisam.h
--- a/storage/myisam/ha_myisam.h	2007-11-02 17:28:16 +01:00
+++ b/storage/myisam/ha_myisam.h	2007-11-14 19:37:34 +01:00
@@ -33,8 +33,6 @@ extern ulong myisam_sort_buffer_size;
 extern TYPELIB myisam_recover_typelib;
 extern ulong myisam_recover_options;
 
-extern MI_INFO *myisam_engine_handle(handler *handler_handle);
-
 class ha_myisam: public handler
 {
   MI_INFO *file;
@@ -142,6 +140,8 @@ class ha_myisam: public handler
                                      *engine_callback,
                                      ulonglong *engine_data);
 #endif
-
-  friend MI_INFO *myisam_engine_handle(handler *handler_handle);
+  MI_INFO *file_ptr(void)
+  {
+    return file;
+  }
 };
diff -Nrup a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc
--- a/storage/myisammrg/ha_myisammrg.cc	2007-11-02 17:28:16 +01:00
+++ b/storage/myisammrg/ha_myisammrg.cc	2007-11-14 19:37:34 +01:00
@@ -46,6 +46,12 @@
   tables. The children are opened as independent MyISAM tables, right as
   if they are used by the SQL statement.
 
+  TABLE_LIST::parent_l is required to find the parent 1. when the last
+  child has been opened and children are to be attached, and 2. when an
+  error happens during child open and the child list must be removed
+  from the queuery list. In these cases the current child does not have
+  TABLE::parent set or does not have a TABLE at all respectively.
+
   When the last child is open, attach_merge_children() is called. It
   removes the list of children from the open list. Then the children are
   "attached" to the parent. All required references between parent and
@@ -57,6 +63,9 @@
 
   Every child TABLE::parent references the parent TABLE object. That way
   TABLE objects belonging to a MERGE table can be identified.
+  TABLE::parent is required because the parent and child TABLE objects
+  can live longer than the parent TABLE_LIST object. So the path
+  child->pos_in_table_list->parent_l->table can be broken.
 
   If necessary, the compatibility of parent and children is checked.
   This check is necessary when any of the objects are reopend. This is
@@ -361,7 +370,8 @@ static MI_INFO *myisammrg_attach_childre
   }
 
   /* Extract the MyISAM table structure pointer from the handler object. */
-  if (!(myisam= myisam_engine_handle(child->file)))
+  if ((child->file->ht->db_type != DB_TYPE_MYISAM) ||
+      !(myisam= ((ha_myisam*) child->file)->file_ptr()))
   {
     DBUG_PRINT("error", ("no MyISAM handle for child table: '%s'.'%s' 0x%lx",
                          child->s->db.str, child->s->table_name.str,
Thread
bk commit into 5.1 tree (istruewing:1.2596) BUG#26379Ingo Struewing14 Nov