MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Ingo Struewing Date:October 13 2007 10:03am
Subject:bk commit into 5.1 tree (istruewing:1.2569) 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-10-13 12:03:50+02:00, istruewing@stripped +25 -0
  Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
              corrupts a MERGE table
  
  Not to be pushed. Internal commit. Please ignore.
  Contains more post-review fixes. Will commit a combined patch soon.
  
  Still to be done:
  - Work on review comments regarding the tests.
  - Test if tampering with a child from another thread can require
  to detach (and re-attach later in reopen_tables()) the children in
  close_handle_and_leave_table_as_lock().
  - Test if another thread can break into CREATE...MERGE...SELECT
  as I need to unlock the children in create_table_from_items().
  - Test ALTER TABLE (fast + copy) within LOCK TABLES to see if I
  need to re-attach the children after reopen_table().
  - Decide what value ::reset() should return if children are
  detached.
  
  Forgot to mention in last patch comment:
  Temporary MERGE must have temporary children.
  This is a new behavior. The old behavior was wrong. See
  Bug 19627 - temporary merge table locking.

  include/myisammrg.h@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +1 -0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added comment.

  mysql-test/extra/binlog_tests/blackhole.test@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +9 -0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Preliminarily added new error message with a comment.

  mysql-test/r/merge.result@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +33 -0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added test result.

  mysql-test/suite/binlog/r/binlog_stm_blackhole.result@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +1 -0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Fixed test result.

  mysql-test/t/delayed.test@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +1 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Exchanged error number by symbolic code.

  mysql-test/t/merge.test@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +25 -0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added tests.

  sql/ha_ndbcluster_binlog.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +1 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added 'thd' argument to init_tmp_table_share().

  sql/handler.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +6 -5
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added 'thd' argument to init_tmp_table_share().

  sql/mysql_priv.h@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +19 -5
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Renamed reopen_merge_list() to fix_merge_after_open().
    Renamed open_and_lock_tables() to open_and_lock_tables_derived()
    with additional parameter 'derived'.
    Added inline functions simple_open_n_lock_tables() and
    open_and_lock_tables(), which call open_and_lock_tables_derived()
    and add the argument for 'derived'.
    Added new function open_n_lock_single_table(), which can be used
    as an replacement for open_ltable() in most situations. Internally
    it calls simple_open_n_lock_tables() so hat it is appropriate for
    MERGE tables.
    Added 'thd' argument to init_tmp_table_share().

  sql/slave.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +1 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted open_and_lock_tables() back to simple_open_n_lock_tables().

  sql/sp_head.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +1 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted open_and_lock_tables() back to old interface.

  sql/sql_acl.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +1 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted open_and_lock_tables() back to old interface.

  sql/sql_base.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +223 -160
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Moved comment from close_thread_table() to detach_merge_children().
    Removed confusing comment from close_cached_tables().
    Removed unnecessary detach_merge_children() from close_temporary().
    Added MERGE children to the list of unusable tables in open_table().
    Renamed reopen_merge_list() to fix_merge_after_open().
    Reverted setting of open_placeholder to current table from parent
    table in close_old_data_files().
    Added detach_merge_children() to drop_locked_tables().
    Split out add_merge_table_list() from attach_merge_children().
    Removed children list from open_tables() TABLE_LIST list regardless
    of the type of failure in open_table(). The list might be reused
    in any case.
    Added new function open_n_lock_single_table(), which can be used
    as an replacement for open_ltable() in most situations. Internally
    it calls simple_open_n_lock_tables() so hat it is appropriate for
    MERGE tables.
    Renamed open_and_lock_tables() to open_and_lock_tables_derived()
    with additional parameter 'derived'.
    Added 'thd' argument to init_tmp_table_share().

  sql/sql_delete.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +1 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted open_and_lock_tables() back to old interface.

  sql/sql_insert.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +51 -52
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted open_and_lock_tables() back to old interface.
    Reverted to not calling lex_start before opening the table in
    handle_delayed_insert().
    Replaced open_and_lock_tables() with surrounding code by the new
    function open_n_lock_single_table().
    Added 'thd' argument to init_tmp_table_share().
    Renamed reopen_merge_list() to fix_merge_after_open().
    Asserted that the branch for non-temporary MERGE table in
    create_table_from_items() (CREATE...SELECT) cannot be
    used under LOCK TABLES.
    Added fix_merge_after_open() to the branch for temporary MERGE
    tables in create_table_from_items().
    Reverted back to always call mysql_lock_tables(). This function
    (create_table_from_items()) is not called for non-temporary tables
    under LOCK TABLES. Calling mysql_lock_tables() does not hurt for
    temporary tables.

  sql/sql_load.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +1 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted open_and_lock_tables() back to old interface.

  sql/sql_parse.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +19 -20
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted open_and_lock_tables() back to simple_open_n_lock_tables().
    Removed condition for calling close_thread_tables() from
    dispatch_command() (in COM_QUERY after every mysql_parse() and
    after the big switch).
    Set lock_type to TL_WRITE in check_merge_table_access(). This
    function is called for CREATE or ALTER of a MERGE table only. The
    lock_type is used only in CREATE...SELECT, where the MERGE children
    must be writable.

  sql/sql_select.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +1 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added 'thd' argument to init_tmp_table_share().

  sql/sql_table.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +8 -19
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted open_and_lock_tables() back to old interface.
    Replaced open_and_lock_tables() with surrounding code by the new
    function open_n_lock_single_table().

  sql/sql_view.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +1 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted open_and_lock_tables() back to old interface.

  sql/table.cc@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +8 -2
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added 'thd' argument to init_tmp_table_share().
    Setting table_map_id from query_id in init_tmp_table_share().

  sql/table.h@stripped, 2007-10-13 12:03:47+02:00, istruewing@stripped +10 -5
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Improved commenting of new memebers of TABLE and TABLE_LIST.
    Added new memeber child_table_map_id to TABLE_LIST.

  storage/myisam/ha_myisam.cc@stripped, 2007-10-13 12:03:48+02:00, istruewing@stripped +4 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Changed string comparison of table_type() to integer
    comparison of db_type in myisam_engine_handle().

  storage/myisammrg/ha_myisammrg.cc@stripped, 2007-10-13 12:03:48+02:00, istruewing@stripped +145 -47
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted back from storing results of table2myisam() in
    favor of the new 'child_table_map_id' compatibility checking.

  storage/myisammrg/ha_myisammrg.h@stripped, 2007-10-13 12:03:48+02:00, istruewing@stripped +2 -6
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Reverted back from storing results of table2myisam() in
    favor of the new 'child_table_map_id' compatibility checking.

diff -Nrup a/include/myisammrg.h b/include/myisammrg.h
--- a/include/myisammrg.h	2007-08-13 09:49:47 +02:00
+++ b/include/myisammrg.h	2007-10-13 12:03:47 +02:00
@@ -69,6 +69,7 @@ typedef struct st_myrg_info
   uint	 merge_insert_method;
   uint	 tables,options,reclength,keys;
   my_bool cache_in_use;
+  /* If MERGE children attached to parent. See top comment in ha_myisammrg.cc */
   my_bool children_attached;
   LIST	 open_list;
   QUEUE  by_key;
diff -Nrup a/mysql-test/extra/binlog_tests/blackhole.test b/mysql-test/extra/binlog_tests/blackhole.test
--- a/mysql-test/extra/binlog_tests/blackhole.test	2007-06-15 18:35:09 +02:00
+++ b/mysql-test/extra/binlog_tests/blackhole.test	2007-10-13 12:03:47 +02:00
@@ -134,6 +134,15 @@ drop table t1,t2,t3;
 #             table
 #
 CREATE TABLE t1(a INT) ENGINE=BLACKHOLE;
+# NOTE: After exchanging open_ltable() by open_and_lock_tables() in
+# handle_delayed_insert() to fix problems with MERGE tables (Bug#26379),
+# problems with INSERT DELAYED and BLACKHOLE popped up. open_ltable()
+# does not check if the binlogging capabilities of the statement and the
+# table match. So the below used to succeed. But since INSERT DELAYED
+# switches to row-based logging in mixed-mode and BLACKHOLE cannot do
+# row-based logging, it could not really work. Until this problem is
+# correctly fixed, we have that error here.
+--error ER_BINLOG_LOGGING_IMPOSSIBLE
 INSERT DELAYED INTO t1 VALUES(1);
 DROP TABLE t1;
 
diff -Nrup a/mysql-test/r/merge.result b/mysql-test/r/merge.result
--- a/mysql-test/r/merge.result	2007-09-12 22:11:44 +02:00
+++ b/mysql-test/r/merge.result	2007-10-13 12:03:47 +02:00
@@ -615,6 +615,19 @@ 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 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 (1), (2);
+LOCK TABLES t3 READ;
+CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL) ENGINE=MERGE UNION=(t1,t2)
+INSERT_METHOD=LAST SELECT * FROM t3;
+SELECT * FROM t4;
+c1
+1
+2
+UNLOCK TABLES;
+DROP TABLE t4, t3, t2, t1;
 CREATE TABLE t1 (
 fileset_id tinyint(3) unsigned NOT NULL default '0',
 file_code varchar(32) NOT NULL default '',
@@ -953,6 +966,20 @@ c1
 1
 2
 DROP TABLE t4;
+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;
+c1
+1
+2
+1
+2
+DROP TABLE t4;
 LOCK TABLES t1 WRITE, t2 WRITE, t3 READ;
 CREATE TABLE t4 (c1 INT);
 DROP TABLE t4;
@@ -961,6 +988,12 @@ ERROR HY000: Table 't4' was not locked w
 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;
+CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2);
+LOCK TABLES t4 READ;
+SELECT * from t1, t4;
+ERROR HY000: Table 't1' was not locked with LOCK TABLES
+DROP TABLE t4;
 UNLOCK TABLES;
 DROP TABLE t1, t2, t3;
 CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
diff -Nrup a/mysql-test/suite/binlog/r/binlog_stm_blackhole.result b/mysql-test/suite/binlog/r/binlog_stm_blackhole.result
--- a/mysql-test/suite/binlog/r/binlog_stm_blackhole.result	2007-06-27 14:27:27 +02:00
+++ b/mysql-test/suite/binlog/r/binlog_stm_blackhole.result	2007-10-13 12:03:47 +02:00
@@ -124,6 +124,7 @@ master-bin.000001	#	Query	#	#	use `test`
 drop table t1,t2,t3;
 CREATE TABLE t1(a INT) ENGINE=BLACKHOLE;
 INSERT DELAYED INTO t1 VALUES(1);
+ERROR HY000: Binary logging not possible. Message: Row-based format required for this statement, but not allowed by this combination of engines
 DROP TABLE t1;
 CREATE TABLE t1(a INT, b INT) ENGINE=BLACKHOLE;
 DELETE FROM t1 WHERE a=10;
diff -Nrup a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test
--- a/mysql-test/t/delayed.test	2007-06-06 19:48:06 +02:00
+++ b/mysql-test/t/delayed.test	2007-10-13 12:03:47 +02:00
@@ -248,7 +248,7 @@ DROP TABLE t1;
 #
 CREATE TABLE t1(c1 INT) ENGINE=MyISAM;
 CREATE TABLE t2(c1 INT) ENGINE=MERGE UNION=(t1);
---error 1031
+--error ER_ILLEGAL_HA
 INSERT DELAYED INTO t2 VALUES(1);
 DROP TABLE t1, t2;
 
diff -Nrup a/mysql-test/t/merge.test b/mysql-test/t/merge.test
--- a/mysql-test/t/merge.test	2007-09-12 22:11:44 +02:00
+++ b/mysql-test/t/merge.test	2007-10-13 12:03:47 +02:00
@@ -256,6 +256,17 @@ 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:
+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 (1), (2);
+LOCK TABLES t3 READ;
+CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL) ENGINE=MERGE UNION=(t1,t2)
+  INSERT_METHOD=LAST SELECT * FROM t3;
+SELECT * FROM t4;
+UNLOCK TABLES;
+DROP TABLE t4, t3, t2, t1;
 
 #
 # testing merge::records_in_range and optimizer
@@ -676,11 +687,18 @@ CREATE TABLE t1 (c1 INT);
 CREATE TABLE t2 (c1 INT);
 CREATE TABLE t3 (c1 INT);
 INSERT INTO t3 VALUES (1), (2);
+# 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;
 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;
+SELECT * FROM t4;
+DROP TABLE t4;
 LOCK TABLES t1 WRITE, t2 WRITE, t3 READ;
 CREATE TABLE t4 (c1 INT);
 DROP TABLE t4;
@@ -689,6 +707,13 @@ CREATE TABLE t4 (c1 INT) SELECT * FROM t
 --error ER_TABLE_NOT_LOCKED
 CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
   INSERT_METHOD=LAST SELECT * FROM t3;
+UNLOCK TABLES;
+CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2);
+LOCK TABLES t4 READ;
+--error ER_TABLE_NOT_LOCKED
+SELECT * from t1, t4;
+# Show that dropping a lcked MERGE table works.
+DROP TABLE t4;
 UNLOCK TABLES;
 DROP TABLE t1, t2, t3;
 
diff -Nrup a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
--- a/sql/ha_ndbcluster_binlog.cc	2007-09-07 11:15:04 +02:00
+++ b/sql/ha_ndbcluster_binlog.cc	2007-10-13 12:03:47 +02:00
@@ -321,7 +321,7 @@ ndbcluster_binlog_open_table(THD *thd, N
   DBUG_ENTER("ndbcluster_binlog_open_table");
   
   safe_mutex_assert_owner(&LOCK_open);
-  init_tmp_table_share(table_share, share->db, 0, share->table_name, 
+  init_tmp_table_share(thd, table_share, share->db, 0, share->table_name, 
                        share->key);
   if ((error= open_table_def(thd, table_share, 0)))
   {
diff -Nrup a/sql/handler.cc b/sql/handler.cc
--- a/sql/handler.cc	2007-08-31 08:57:57 +02:00
+++ b/sql/handler.cc	2007-10-13 12:03:47 +02:00
@@ -2582,7 +2582,7 @@ int ha_create_table(THD *thd, const char
   TABLE_SHARE share;
   DBUG_ENTER("ha_create_table");
   
-  init_tmp_table_share(&share, db, 0, table_name, path);
+  init_tmp_table_share(thd, &share, db, 0, table_name, path);
   if (open_table_def(thd, &share, 0) ||
       open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, 0, &table,
                             TRUE))
@@ -2649,7 +2649,7 @@ int ha_create_table_from_engine(THD* thd
   if (error)
     DBUG_RETURN(2);
 
-  init_tmp_table_share(&share, db, 0, name, path);
+  init_tmp_table_share(thd, &share, db, 0, name, path);
   if (open_table_def(thd, &share, 0))
   {
     DBUG_RETURN(3);
@@ -3655,11 +3655,12 @@ int handler::ha_reset()
 int handler::ha_write_row(uchar *buf)
 {
   int error;
+  DBUG_ENTER("handler::ha_write_row");
   if (unlikely(error= write_row(buf)))
-    return error;
+    DBUG_RETURN(error);
   if (unlikely(error= binlog_log_row<Write_rows_log_event>(table, 0, buf)))
-    return error;
-  return 0;
+    DBUG_RETURN(error);
+  DBUG_RETURN(0);
 }
 
 
diff -Nrup a/sql/mysql_priv.h b/sql/mysql_priv.h
--- a/sql/mysql_priv.h	2007-09-12 22:11:44 +02:00
+++ b/sql/mysql_priv.h	2007-10-13 12:03:47 +02:00
@@ -1142,8 +1142,8 @@ TABLE *table_cache_insert_placeholder(TH
 bool lock_table_name_if_not_cached(THD *thd, const char *db,
                                    const char *table_name, TABLE **table);
 TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
-bool reopen_merge_list(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
-                       TABLE_LIST *new_child_list, TABLE_LIST **new_last);
+bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
+                          TABLE_LIST *new_child_list, TABLE_LIST **new_last);
 bool reopen_table(TABLE *table);
 bool reopen_tables(THD *thd,bool get_locks,bool in_refresh);
 void close_data_files_and_morph_locks(THD *thd, const char *db,
@@ -1394,8 +1394,21 @@ int init_ftfuncs(THD *thd, SELECT_LEX* s
 void wait_for_condition(THD *thd, pthread_mutex_t *mutex,
                         pthread_cond_t *cond);
 int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
-int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
-bool open_and_lock_tables(THD *thd,TABLE_LIST *tables, bool no_derived= 0);
+/* open_and_lock_tables with optional derived handling */
+bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived);
+/* simple open_and_lock_tables without derived handling */
+inline bool simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
+{
+  return open_and_lock_tables_derived(thd, tables, FALSE);
+}
+/* open_and_lock_tables with derived handling */
+inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
+{
+  return open_and_lock_tables_derived(thd, tables, TRUE);
+}
+/* simple open_and_lock_tables without derived handling for single table */
+TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
+                                thr_lock_type lock_type);
 bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
 int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen);
 int decide_logging_format(THD *thd, TABLE_LIST *tables);
@@ -1971,7 +1984,8 @@ int format_number(uint inputflag,uint ma
 /* table.cc */
 TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
                                uint key_length);
-void init_tmp_table_share(TABLE_SHARE *share, const char *key, uint key_length,
+void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
+                          uint key_length,
                           const char *table_name, const char *path);
 void free_table_share(TABLE_SHARE *share);
 int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags);
diff -Nrup a/sql/slave.cc b/sql/slave.cc
--- a/sql/slave.cc	2007-09-12 22:11:44 +02:00
+++ b/sql/slave.cc	2007-10-13 12:03:47 +02:00
@@ -1016,7 +1016,7 @@ static int create_table_from_dump(THD* t
   tables.table= NULL; /* was set by mysql_rm_table(). */
   tables.lock_type = TL_WRITE;
   tables.required_type= FRMTYPE_TABLE;
-  if (open_and_lock_tables(thd, &tables, TRUE))
+  if (simple_open_n_lock_tables(thd, &tables))
   {
     sql_print_error("create_table_from_dump: could not open created table");
     goto err;
diff -Nrup a/sql/sp_head.cc b/sql/sp_head.cc
--- a/sql/sp_head.cc	2007-09-12 22:11:44 +02:00
+++ b/sql/sp_head.cc	2007-10-13 12:03:47 +02:00
@@ -2664,7 +2664,7 @@ int sp_instr::exec_open_and_lock_tables(
     and open and lock them before executing instructions core function.
   */
   if (check_table_access(thd, SELECT_ACL, tables, 0)
-      || open_and_lock_tables(thd, tables, FALSE))
+      || open_and_lock_tables(thd, tables))
     result= -1;
   else
     result= 0;
diff -Nrup a/sql/sql_acl.cc b/sql/sql_acl.cc
--- a/sql/sql_acl.cc	2007-09-12 22:11:44 +02:00
+++ b/sql/sql_acl.cc	2007-10-13 12:03:47 +02:00
@@ -2948,7 +2948,7 @@ bool mysql_table_grant(THD *thd, TABLE_L
       class LEX_COLUMN *column;
       List_iterator <LEX_COLUMN> column_iter(columns);
 
-      if (open_and_lock_tables(thd, table_list, FALSE))
+      if (open_and_lock_tables(thd, table_list))
         DBUG_RETURN(TRUE);
 
       while ((column = column_iter++))
diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
--- a/sql/sql_base.cc	2007-10-04 20:24:08 +02:00
+++ b/sql/sql_base.cc	2007-10-13 12:03:47 +02:00
@@ -674,8 +674,14 @@ TABLE_SHARE *get_cached_table_share(cons
 /**
   @brief Detach MERGE children from the parent.
 
-  @note Detach must not touch the children in any way.
-    They may have been closed at ths point already.
+  @note When closing thread tables at end of statement, both parent and
+    children are in thd->open_tables and will be closed. In most cases
+    the children will be closed before the parent. They are opened after
+    the parent and thus stacked into thd->open_tables before it.
+
+    Hence it is vital that detach does not touch the children in any
+    way.
+
     All references to the children should be removed.
 
   @param[in]    table           the TABLE object of the parent
@@ -1032,7 +1038,6 @@ bool close_cached_tables(THD *thd, bool 
     /* Set version for table */
     for (TABLE *table=thd->open_tables; table ; table= table->next)
       table->s->version= refresh_version;
-    /* No need to attach MERGE children here. */
   }
   if (!have_lock)
     VOID(pthread_mutex_unlock(&LOCK_open));
@@ -1337,10 +1342,6 @@ bool close_thread_table(THD *thd, TABLE 
   *table_ptr=table->next;
   /*
     When closing a MERGE parent table, detach its children first.
-    The children are in thd->open_tables and will be closed too.
-    In most cases they will be closed already at this point. They are
-    opened after the parent and thus stacked into thd->open_tables
-    before it. Hence detach must not touch the children in any way.
     Clear child table references to force new assignment at next open.
   */
   if (table->child_l)
@@ -1850,8 +1851,6 @@ void close_temporary(TABLE *table, bool 
   DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
                           table->s->db.str, table->s->table_name.str));
 
-  if (table->child_l)
-    detach_merge_children(table, FALSE);
   free_io_cache(table);
   closefrm(table, 0);
   if (delete_table)
@@ -2437,9 +2436,14 @@ TABLE *open_table(THD *thd, TABLE_LIST *
                    table->s->table_name.str);
           DBUG_RETURN(0);
         }
+        /*
+          When looking for a usable TABLE, ignore MERGE children, as they
+          belong to their parent and cannot be used explicitly.
+        */
         if (!my_strcasecmp(system_charset_info, table->alias, alias) &&
             table->query_id != thd->query_id && /* skip tables already used */
-            !(thd->prelocked_mode && table->query_id))
+            !(thd->prelocked_mode && table->query_id) &&
+            !table->parent)
         {
           int distance= ((int) table->reginfo.lock_type -
                          (int) table_list->lock_type);
@@ -2820,14 +2824,18 @@ TABLE *find_locked_table(THD *thd, const
 
 
 /**
-  @brief Fix MERGE children after reopen.
+  @brief Fix MERGE children after open.
 
   @param[in]    old_child_list  first list member from original table
   @param[in]    old_last        pointer to &next_global of last list member
   @param[in]    new_child_list  first list member from freshly opened table
   @param[in]    new_last        pointer to &next_global of last list member
 
-  @detail Main action is to copy TABLE reference fro each member of
+  @return       mismatch
+    @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.
@@ -2836,11 +2844,11 @@ TABLE *find_locked_table(THD *thd, const
     the UNION list did not change.
 */
 
-bool reopen_merge_list(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
-                       TABLE_LIST *new_child_list, TABLE_LIST **new_last)
+bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
+                          TABLE_LIST *new_child_list, TABLE_LIST **new_last)
 {
   bool mismatch= FALSE;
-  DBUG_ENTER("reopen_merge_list");
+  DBUG_ENTER("fix_merge_after_open");
   DBUG_PRINT("myrg", ("old last addr: 0x%lx  new last addr: 0x%lx",
                       (long) old_last, (long) new_last));
 
@@ -2985,8 +2993,8 @@ bool reopen_table(TABLE *table)
   tmp.parent=           table->parent;
   /* Fix MERGE child list and check for unchanged union. */
   if ((table->child_l || tmp.child_l) &&
-      reopen_merge_list(table->child_l, table->child_last_l,
-                        tmp.child_l, tmp.child_last_l))
+      fix_merge_after_open(table->child_l, table->child_last_l,
+                           tmp.child_l, tmp.child_last_l))
   {
     VOID(closefrm(&tmp, 1)); // close file, free everything
     goto end;
@@ -3013,7 +3021,11 @@ bool reopen_table(TABLE *table)
   }
   if (table->triggers)
     table->triggers->set_table(table);
-  /* No need to attach MERGE children here. */
+  /*
+    Do not attach MERGE children here. The children might be reopened
+    after the parent. Do it after reopening all tables that require
+    reopen. See for example reopen_tables().
+  */
 
   broadcast_refresh();
   error=0;
@@ -3264,12 +3276,12 @@ void close_old_data_files(THD *thd, TABL
             mysql_lock_abort(thd, ulcktbl, TRUE);
             mysql_lock_remove(thd, thd->locked_tables, ulcktbl, TRUE);
             ulcktbl->lock_count= 0;
-            /*
-              We want to protect the table from concurrent DDL operations
-              (like RENAME TABLE) until we will re-open and re-lock it.
-            */
-            ulcktbl->open_placeholder= 1;
           }
+          /*
+            We want to protect the table from concurrent DDL operations
+            (like RENAME TABLE) until we will re-open and re-lock it.
+          */
+          table->open_placeholder= 1;
 	}
         close_handle_and_leave_table_as_lock(table);
       }
@@ -3419,6 +3431,12 @@ TABLE *drop_locked_tables(THD *thd,const
         if (table->db_stat)
         {
           table->db_stat= 0;
+          /*
+            When closing a MERGE parent table, detach its children first.
+            Clear child table references in case this object is opened again.
+          */
+          if (table->child_l)
+            detach_merge_children(table, TRUE);
           table->file->close();
         }
       }
@@ -3492,7 +3510,8 @@ void abort_locked_tables(THD *thd,const 
     share->table_map_id is given a value that with a high certainty is
     not used by any other table (the only case where a table id can be
     reused is on wrap-around, which means more than 4 billion table
-    shares open at the same time).
+    share opens have been executed while one table was open all the
+    time).
 
     share->table_map_id is not ~0UL.
  */
@@ -3732,16 +3751,74 @@ err:
 
 
 /**
-  @brief Attach MERGE children to the parent.
+  @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.
 
-    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
+
+  @return status
+    @retval     0               OK
+    @retval     != 0            Error
+*/
+
+static int add_merge_table_list(TABLE_LIST *tlist)
+{
+  TABLE       *parent= tlist->table;
+  TABLE_LIST  *child_l;
+  DBUG_ENTER("add_merge_table_list");
+  DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", parent->s->db.str,
+                      parent->s->table_name.str, (long) parent));
+
+  /* Must not call this with attached children. */
+  DBUG_ASSERT(!parent->children_attached);
+  /* Must not call this with children list in place. */
+  DBUG_ASSERT(tlist->next_global != parent->child_l);
+
+  /* Fix children.*/
+  for (child_l= parent->child_l; ; child_l= child_l->next_global)
+  {
+    /*
+      Note: child_l->table may still be set if this parent was taken
+      from the unused_tables chain. Ignore this fact here. The
+      reference will be replaced by the handler in
+      ::extra(HA_EXTRA_ATTACH_CHILDREN).
+    */
+
+    /* Set lock type. */
+    child_l->lock_type= tlist->lock_type;
+
+    /* Set parent reference. */
+    child_l->parent_l= tlist;
+
+    /* Break when this was the last child. */
+    if (&child_l->next_global == parent->child_last_l)
+      break;
+  }
+
+  /* Insert children into the table list. */
+  *parent->child_last_l= tlist->next_global;
+  tlist->next_global= parent->child_l;
+
+  /*
+    Do not fix the prev_global pointers. We will remove the
+    chain soon anyway.
+  */
+
+  DBUG_RETURN(0);
+}
+
+
+/**
+  @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
 
@@ -3752,80 +3829,46 @@ err:
 
 static int attach_merge_children(TABLE_LIST *tlist)
 {
+  TABLE *parent= tlist->parent_l->table;
+  int error;
   DBUG_ENTER("attach_merge_children");
-  DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", tlist->table->s->db.str,
-                      tlist->table->s->table_name.str, (long) tlist->table));
+  DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", parent->s->db.str,
+                      parent->s->table_name.str, (long) parent));
 
-  if (tlist->table->child_l)
-  {
-    /* 'table' is a MERGE parent table. */
-    TABLE *parent= tlist->table;
-    TABLE_LIST *child_l;
-    DBUG_PRINT("myrg", ("MERGE parent opened, add children to list"));
-    DBUG_ASSERT(!parent->children_attached);
+  /* Must not call this with attached children. */
+  DBUG_ASSERT(!parent->children_attached);
+  /* Must call this with children list in place. */
+  DBUG_ASSERT(tlist->parent_l->next_global == parent->child_l);
 
-    /* Fix children.*/
-    for (child_l= parent->child_l; ; child_l= child_l->next_global)
-    {
-      /*
-        Note: child_l->table may still be set if this parent was taken
-        from the unused_tables chain. Ignore this fact here. The
-        reference will be replaced by the handler in
-        ::extra(HA_EXTRA_ATTACH_CHILDREN).
-      */
-      /* Set lock type. */
-      child_l->lock_type= tlist->lock_type;
-      /* Set parent reference. */
-      child_l->parent_l= tlist;
-      /* Break when this was the last child. */
-      if (&child_l->next_global == parent->child_last_l)
-        break;
-    }
+  /* Attach MyISAM tables to MERGE table. */
+  error= parent->file->extra(HA_EXTRA_ATTACH_CHILDREN);
 
-    /* Insert children into the table list. */
-    *parent->child_last_l= tlist->next_global;
-    tlist->next_global= parent->child_l;
-    /*
-      Do not fix the prev_global pointers. We will remove the
-      chain soon anyway.
-    */
-  }
-  else if (tlist->parent_l && (&tlist->next_global ==
-                               tlist->parent_l->table->child_last_l))
+  /*
+    Remove children from the table list. Even in case of an error.
+    This should prevent tampering with them.
+  */
+  tlist->parent_l->next_global= *parent->child_last_l;
+
+  /*
+    Do not fix the last childs next_global pointer. It is needed for
+    stepping to the next table in the enclosing loop in open_tables().
+    Do not fix prev_global pointers. We did not set them.
+  */
+
+  if (error)
   {
-    /* 'tlist' lists the last MERGE child, attach children to parent. */
-    TABLE *parent= tlist->parent_l->table;
-    int error;
-    DBUG_PRINT("myrg", ("last MERGE child opened, attach children"));
-    DBUG_ASSERT(!parent->children_attached);
+    DBUG_PRINT("error", ("attaching MERGE children failed: %d", my_errno));
+    parent->file->print_error(error, MYF(0));
+    DBUG_RETURN(1);
+  }
 
-    error= parent->file->extra(HA_EXTRA_ATTACH_CHILDREN);
+  parent->children_attached= TRUE;
 
-    /*
-      Remove children from the table list. Even in case of an error.
-      This should prevent tampering with them.
-    */
-    tlist->parent_l->next_global= *parent->child_last_l;
-    /*
-      Do not fix the last childs next_global pointer. It is needed for
-      stepping to the next table in the enclosing loop in open_tables().
-      Do not fix prev_global pointers. We did not set them.
-    */
+  /*
+    Note that we have the cildren in the thd->open_tables list at this
+    point.
+  */
 
-    if (error)
-    {
-      DBUG_PRINT("error", ("attaching MERGE children failed: %d", my_errno));
-      parent->file->print_error(error, MYF(0));
-      DBUG_RETURN(1);
-    }
-    parent->children_attached= TRUE;
-    /*
-      Note that we have the cildren in the thd->open_tables list
-      at this point.
-    */
-  }
-  else
-    DBUG_PRINT("myrg", ("neither MERGE parent nor last MERGE child opened"));
   DBUG_RETURN(0);
 }
 
@@ -4009,6 +4052,19 @@ int open_tables(THD *thd, TABLE_LIST **s
 	goto process_view_routines;
       }
 
+      /*
+        If in a MERGE table open, we need to remove the children list
+        from statement table list before restarting. Otherwise the list
+        will be inserted another time.
+      */
+      if (tables->parent_l)
+      {
+        TABLE_LIST *parent_l= tables->parent_l;
+        /* The parent table should be correctly open at this point. */
+        DBUG_ASSERT(parent_l->table);
+        parent_l->next_global= *parent_l->table->child_last_l;
+      }
+
       if (refresh)				// Refresh in progress
       {
         /*
@@ -4027,18 +4083,6 @@ int open_tables(THD *thd, TABLE_LIST **s
         */
         if (query_tables_last_own)
           thd->lex->mark_as_requiring_prelocking(query_tables_last_own);
-        /*
-          If in a MERGE table open, we need to remove the children list
-          from statement table list before restarting. Otherwise the list
-          will be inserted another time.
-        */
-        if (tables->parent_l)
-        {
-          TABLE_LIST *parent_l= tables->parent_l;
-          /* The parent table should be correctly open at this point. */
-          DBUG_ASSERT(parent_l->table);
-          parent_l->next_global= *parent_l->table->child_last_l;
-        }
         close_tables_for_reopen(thd, start);
 	goto restart;
       }
@@ -4095,9 +4139,13 @@ int open_tables(THD *thd, TABLE_LIST **s
                           test(tables->parent_l)));
     DBUG_PRINT("tcache", ("in lock tables: %d  in prelock mode: %d",
                           test(thd->locked_tables), test(thd->prelocked_mode)));
-    if ((tables->table->child_l || tables->parent_l) &&
-        !(thd->locked_tables || thd->prelocked_mode) &&
-        attach_merge_children(tables))
+    if (((!thd->locked_tables && !thd->prelocked_mode) ||
+         tables->table->s->tmp_table) &&
+        ((tables->table->child_l &&
+          add_merge_table_list(tables)) ||
+         (tables->parent_l &&
+          (&tables->next_global == tables->parent_l->table->child_last_l) &&
+          attach_merge_children(tables))))
     {
       /*
         Some functions determine success as (tables->table != NULL).
@@ -4179,6 +4227,54 @@ static bool check_lock_and_start_stmt(TH
 }
 
 
+/**
+  @brief Open and lock one table
+
+  @param[in]    thd             thread handle
+  @param[in]    table_l         table to open is first table in this list
+  @param[in]    lock_type       lock to use for table
+
+  @return       table
+    @retval     != NULL         OK, opened table returned
+    @retval     NULL            Error
+
+  @note
+    If ok, the following are also set:
+      table_list->lock_type 	lock_type
+      table_list->table		table
+
+  @note
+    If table_l is a list, not a single table, the list is temporarily
+    broken.
+*/
+
+TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
+                                thr_lock_type lock_type)
+{
+  TABLE_LIST *save_next_global;
+  DBUG_ENTER("open_n_lock_single_table");
+
+  /* Remember old 'next' pointer. */
+  save_next_global= table_l->next_global;
+  /* Break list. */
+  table_l->next_global= NULL;
+
+  /* Set requested lock type. */
+  table_l->lock_type= lock_type;
+  /* Allow to open real tables only. */
+  table_l->required_type= FRMTYPE_TABLE;
+
+  /* Open the table. */
+  if (simple_open_n_lock_tables(thd, table_l))
+    table_l->table= NULL; /* Just to be sure. */
+
+  /* Restore list. */
+  table_l->next_global= save_next_global;
+
+  DBUG_RETURN(table_l->table);
+}
+
+
 /*
   Open and lock one table
 
@@ -4254,51 +4350,13 @@ TABLE *open_ltable(THD *thd, TABLE_LIST 
 
 
 /*
-  Open all tables in list and locks them for read without derived
-  tables processing.
+  Open all tables in list, locks them and optionally process derived tables.
 
   SYNOPSIS
-    simple_open_n_lock_tables()
+    open_and_lock_tables_derived()
     thd		- thread handler
     tables	- list of tables for open&locking
-
-  RETURN
-    0  - ok
-    -1 - error
-
-  NOTE
-    The lock will automaticaly be freed by close_thread_tables()
-*/
-
-int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
-{
-  uint counter;
-  bool need_reopen;
-  DBUG_ENTER("simple_open_n_lock_tables");
-
-  for ( ; ; ) 
-  {
-    if (open_tables(thd, &tables, &counter, 0))
-      DBUG_RETURN(-1);
-    if (!lock_tables(thd, tables, counter, &need_reopen))
-      break;
-    if (!need_reopen)
-      DBUG_RETURN(-1);
-    close_tables_for_reopen(thd, &tables);
-  }
-  DBUG_RETURN(0);
-}
-
-
-/*
-  Open all tables in list, locks them and process derived tables
-  tables processing.
-
-  SYNOPSIS
-    open_and_lock_tables()
-    thd		- thread handler
-    tables	- list of tables for open&locking
-    no_derived  - if not to handle derived tables
+    derived     - if to handle derived tables
 
   RETURN
     FALSE - ok
@@ -4306,13 +4364,20 @@ int simple_open_n_lock_tables(THD *thd, 
 
   NOTE
     The lock will automaticaly be freed by close_thread_tables()
+
+  NOTE
+    There are two convenience functions:
+    - simple_open_n_lock_tables(thd, tables)  without derived handling
+    - open_and_lock_tables(thd, tables)       with derived handling
+    Both inline functions call open_and_lock_tables_derived() with
+    the third argument set appropriately.
 */
 
-bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, bool no_derived)
+bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived)
 {
   uint counter;
   bool need_reopen;
-  DBUG_ENTER("open_and_lock_tables");
+  DBUG_ENTER("open_and_lock_tables_derived");
 
   for ( ; ; ) 
   {
@@ -4324,11 +4389,7 @@ bool open_and_lock_tables(THD *thd, TABL
       DBUG_RETURN(-1);
     close_tables_for_reopen(thd, &tables);
   }
-  /*
-    If this is called as a replacement for open_ltable(), we do not want
-    to handle derived tables.
-  */
-  if (!no_derived &&
+  if (derived &&
       (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
        (thd->fill_derived_tables() &&
         mysql_handle_derived(thd->lex, &mysql_derived_filling))))
@@ -4679,7 +4740,9 @@ int lock_tables(THD *thd, TABLE_LIST *ta
   else
   {
     TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
-    for (table= tables; table != first_not_own; table= table->next_global)
+    for (table= tables;
+         table && table != first_not_own;
+         table= table->next_global)
     {
       if (!table->placeholder() &&
 	  check_lock_and_start_stmt(thd, table->table, table->lock_type))
@@ -4783,7 +4846,7 @@ TABLE *open_temporary_table(THD *thd, co
   saved_cache_key= strmov(tmp_path, path)+1;
   memcpy(saved_cache_key, cache_key, key_length);
 
-  init_tmp_table_share(share, saved_cache_key, key_length,
+  init_tmp_table_share(thd, share, saved_cache_key, key_length,
                        strend(saved_cache_key)+1, tmp_path);
 
   if (open_table_def(thd, share, 0) ||
@@ -7499,7 +7562,7 @@ my_bool mysql_rm_tmp_tables(void)
           /* We should cut file extention before deleting of table */
           memcpy(filePathCopy, filePath, filePath_len - ext_len);
           filePathCopy[filePath_len - ext_len]= 0;
-          init_tmp_table_share(&share, "", 0, "", filePathCopy);
+          init_tmp_table_share(thd, &share, "", 0, "", filePathCopy);
           if (!open_table_def(thd, &share, 0) &&
               ((handler_file= get_new_handler(&share, thd->mem_root,
                                               share.db_type()))))
diff -Nrup a/sql/sql_delete.cc b/sql/sql_delete.cc
--- a/sql/sql_delete.cc	2007-09-12 22:11:44 +02:00
+++ b/sql/sql_delete.cc	2007-10-13 12:03:47 +02:00
@@ -40,7 +40,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *
   SELECT_LEX   *select_lex= &thd->lex->select_lex;
   DBUG_ENTER("mysql_delete");
 
-  if (open_and_lock_tables(thd, table_list, FALSE))
+  if (open_and_lock_tables(thd, table_list))
     DBUG_RETURN(TRUE);
   if (!(table= table_list->table))
   {
diff -Nrup a/sql/sql_insert.cc b/sql/sql_insert.cc
--- a/sql/sql_insert.cc	2007-09-12 22:11:45 +02:00
+++ b/sql/sql_insert.cc	2007-10-13 12:03:47 +02:00
@@ -513,7 +513,7 @@ bool open_and_lock_for_insert_delayed(TH
       Open tables used for sub-selects or in stored functions, will also
       cache these functions.
     */
-    if (open_and_lock_tables(thd, table_list->next_global, FALSE))
+    if (open_and_lock_tables(thd, table_list->next_global))
     {
       end_delayed_insert(thd);
       DBUG_RETURN(TRUE);
@@ -537,7 +537,7 @@ bool open_and_lock_for_insert_delayed(TH
     Use a normal insert.
   */
   table_list->lock_type= TL_WRITE;
-  DBUG_RETURN(open_and_lock_tables(thd, table_list, FALSE));
+  DBUG_RETURN(open_and_lock_tables(thd, table_list));
 }
 
 
@@ -608,7 +608,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
   }
   else
   {
-    if (open_and_lock_tables(thd, table_list, FALSE))
+    if (open_and_lock_tables(thd, table_list))
       DBUG_RETURN(TRUE);
   }
 
@@ -2263,18 +2263,10 @@ pthread_handler_t handle_delayed_insert(
     strmov(thd->net.last_error,ER(thd->net.last_errno=ER_OUT_OF_RESOURCES));
     goto err;
   }
-  /*
-    Usually lex_start() is called by mysql_parse(), but we need it here
-    as this thread does not call mysql_parse().
-  */
-  lex_start(thd);
 
   /* open table */
-  di->table_list.next_global= NULL;
-  di->table_list.lock_type= TL_WRITE_DELAYED;
-  di->table_list.required_type= FRMTYPE_TABLE;
-  if (open_and_lock_tables(thd, &di->table_list, TRUE) ||
-      !(di->table= di->table_list.table))
+  if (!(di->table= open_n_lock_single_table(thd, &di->table_list,
+                                            TL_WRITE_DELAYED)))
   {
     thd->fatal_error();				// Abort waiting inserts
     goto err;
@@ -3302,7 +3294,7 @@ static TABLE *create_table_from_items(TH
   tmp_table.alias= 0;
   tmp_table.timestamp_field= 0;
   tmp_table.s= &share;
-  init_tmp_table_share(&share, "", 0, "", "");
+  init_tmp_table_share(thd, &share, "", 0, "", "");
 
   tmp_table.s->db_create_options=0;
   tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr;
@@ -3382,34 +3374,40 @@ static TABLE *create_table_from_items(TH
         else
           table= create_table->table;
 
-        /* MERGE children need be open for select. */
+        /*
+          MERGE parent and children need be open and attached 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.
+        */
         if (table->child_l)
         {
           TABLE_LIST *tlist;
 
-          if (reopen_merge_list((TABLE_LIST*) create_info->merge_list.first,
-                                NULL, table->child_l, table->child_last_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))
             DBUG_RETURN(NULL);
           table->children_attached= TRUE;
           /*
             Need to unlock children for locking through parent.
-            If we are under LOCK TABLES, children must not be unlocked.
+            We do never come here under LOCK TABLES. It is safe to unlock.
           */
-          if (!thd->locked_tables)
+          DBUG_ASSERT(!thd->locked_tables);
+          *table->child_last_l= NULL;
+          for (tlist= table->child_l; tlist; tlist=tlist->next_global)
           {
-            *table->child_last_l= NULL;
-            for (tlist= table->child_l; tlist; tlist=tlist->next_global)
-            {
-              /*
-                Wake up threads waiting for table-level lock on this table
-                so they won't sneak in when we will temporarily remove our
-                lock on it. This will also give them a chance to close their
-                instances of this table.
-              */
-              mysql_lock_abort(thd, tlist->table, TRUE);
-              mysql_lock_remove(thd, thd->lock, tlist->table, TRUE);
-            }
+            DBUG_ASSERT(tlist->table->reginfo.lock_type == TL_WRITE);
+            /*
+              Wake up threads waiting for table-level lock on this table
+              so they won't sneak in when we will temporarily remove our
+              lock on it. This will also give them a chance to close their
+              instances of this table.
+            */
+            mysql_lock_abort(thd, tlist->table, TRUE);
+            mysql_lock_remove(thd, thd->lock, tlist->table, TRUE);
           }
         }
         VOID(pthread_mutex_unlock(&LOCK_open));
@@ -3427,6 +3425,16 @@ static TABLE *create_table_from_items(TH
           */
           close_temporary_table(thd, create_table);
         }
+        /* MERGE children need be open for select. */
+        if (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);
+          else
+            table->children_attached= TRUE;
+        }
       }
     }
     reenable_binlog(thd);
@@ -3436,30 +3444,21 @@ static TABLE *create_table_from_items(TH
 
   DBUG_EXECUTE_IF("sleep_create_select_before_lock", my_sleep(6000000););
 
-  /*
-    If this is a MERGE table and we are under LOCK TABLES, then all
-    children must be locked already and we must neither unlock them nor
-    try to lock them again. Instead we will just rely on their
-    independent locks.
-  */
-  if (!table->child_l || !thd->locked_tables)
+  table->reginfo.lock_type= TL_WRITE;
+  hooks->prelock(&table, 1);                    // Call prelock hooks
+  if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
+                                    MYSQL_LOCK_IGNORE_FLUSH, &not_used)) ||
+      hooks->postlock(&table, 1))
   {
-    table->reginfo.lock_type= TL_WRITE;
-    hooks->prelock(&table, 1);                    // Call prelock hooks
-    if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
-                                      MYSQL_LOCK_IGNORE_FLUSH, &not_used)) ||
-        hooks->postlock(&table, 1))
+    if (*lock)
     {
-      if (*lock)
-      {
-        mysql_unlock_tables(thd, *lock);
-        *lock= 0;
-      }
-
-      if (!create_info->table_existed)
-        drop_open_table(thd, table, create_table->db, create_table->table_name);
-      DBUG_RETURN(0);
+      mysql_unlock_tables(thd, *lock);
+      *lock= 0;
     }
+
+    if (!create_info->table_existed)
+      drop_open_table(thd, table, create_table->db, create_table->table_name);
+    DBUG_RETURN(0);
   }
   DBUG_RETURN(table);
 }
diff -Nrup a/sql/sql_load.cc b/sql/sql_load.cc
--- a/sql/sql_load.cc	2007-09-12 22:11:45 +02:00
+++ b/sql/sql_load.cc	2007-10-13 12:03:47 +02:00
@@ -146,7 +146,7 @@ bool mysql_load(THD *thd,sql_exchange *e
 	       MYF(0));
     DBUG_RETURN(TRUE);
   }
-  if (open_and_lock_tables(thd, table_list, FALSE))
+  if (open_and_lock_tables(thd, table_list))
     DBUG_RETURN(TRUE);
   if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
                                     &thd->lex->select_lex.top_join_list,
diff -Nrup a/sql/sql_parse.cc b/sql/sql_parse.cc
--- a/sql/sql_parse.cc	2007-10-04 20:24:09 +02:00
+++ b/sql/sql_parse.cc	2007-10-13 12:03:47 +02:00
@@ -543,7 +543,7 @@ int mysql_table_dump(THD *thd, LEX_STRIN
     my_casedn_str(files_charset_info, tbl_name);
 
   table_list->required_type= FRMTYPE_TABLE;
-  if (open_and_lock_tables(thd, table_list, TRUE))
+  if (simple_open_n_lock_tables(thd, table_list))
     DBUG_RETURN(1);
   table= table_list->table;
 
@@ -963,12 +963,7 @@ bool dispatch_command(enum enum_server_c
     {
       char *next_packet= (char*) found_semicolon;
       net->no_send_error= 0;
-      /*
-        Multiple queries exits, execute them individually
-      */
-      if (thd->lock || thd->open_tables || thd->derived_tables ||
-          thd->prelocked_mode)
-        close_thread_tables(thd);
+      close_thread_tables(thd);
       ulong length= (ulong)(packet_end - next_packet);
 
       log_slow_statement(thd);
@@ -1305,12 +1300,10 @@ bool dispatch_command(enum enum_server_c
     my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
     break;
   }
-  if (thd->lock || thd->open_tables || thd->derived_tables ||
-      thd->prelocked_mode)
-  {
-    thd->proc_info="closing tables";
-    close_thread_tables(thd);			/* Free tables */
-  }
+
+  thd->proc_info= "closing tables";
+  close_thread_tables(thd);                     /* Free tables */
+
   /*
     assume handlers auto-commit (if some doesn't - transaction handling
     in MySQL should be redesigned to support it; it's a big change,
@@ -1904,7 +1897,7 @@ mysql_execute_command(THD *thd)
   }
   case SQLCOM_DO:
     if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
-        open_and_lock_tables(thd, all_tables, FALSE))
+        open_and_lock_tables(thd, all_tables))
       goto error;
 
     res= mysql_do(thd, *lex->insert_list);
@@ -2237,7 +2230,7 @@ mysql_execute_command(THD *thd)
         create_table->create= TRUE;
       }
 
-      if (!(res= open_and_lock_tables(thd, lex->query_tables, FALSE)))
+      if (!(res= open_and_lock_tables(thd, lex->query_tables)))
       {
         /*
           Is table which we are changing used somewhere in other parts
@@ -2773,7 +2766,7 @@ end_with_restore_list:
       break;
     }
 
-    if (!(res= open_and_lock_tables(thd, all_tables, FALSE)))
+    if (!(res= open_and_lock_tables(thd, all_tables)))
     {
       /* Skip first table, which is the table we are inserting in */
       TABLE_LIST *second_table= first_table->next_local;
@@ -2892,7 +2885,7 @@ end_with_restore_list:
       goto error;
 
     thd->proc_info="init";
-    if ((res= open_and_lock_tables(thd, all_tables, FALSE)))
+    if ((res= open_and_lock_tables(thd, all_tables)))
       break;
 
     if ((res= mysql_multi_delete_prepare(thd)))
@@ -3024,7 +3017,7 @@ end_with_restore_list:
   {
     List<set_var_base> *lex_var_list= &lex->var_list;
     if ((check_table_access(thd, SELECT_ACL, all_tables, 0) ||
-	 open_and_lock_tables(thd, all_tables, FALSE)))
+	 open_and_lock_tables(thd, all_tables)))
       goto error;
     if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
     {
@@ -3815,7 +3808,7 @@ create_sp_error:
         required for execution.
       */
       if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
-	  open_and_lock_tables(thd, all_tables, FALSE))
+	  open_and_lock_tables(thd, all_tables))
        goto error;
 
       /*
@@ -4516,7 +4509,7 @@ static bool execute_sqlcom_select(THD *t
       param->select_limit=
         new Item_int((ulonglong) thd->variables.select_limit);
   }
-  if (!(res= open_and_lock_tables(thd, all_tables, FALSE)))
+  if (!(res= open_and_lock_tables(thd, all_tables)))
   {
     if (lex->describe)
     {
@@ -5084,6 +5077,12 @@ bool check_merge_table_access(THD *thd, 
     TABLE_LIST *tmp;
     for (tmp= table_list; tmp; tmp= tmp->next_local)
     {
+      /*
+        We create or alter a MERGE table. In both cases the MERGE child
+        tables must be locked in write mode.
+      */
+      tmp->lock_type= TL_WRITE;
+
       if (!tmp->db || !tmp->db[0])
 	tmp->db=db;
     }
diff -Nrup a/sql/sql_select.cc b/sql/sql_select.cc
--- a/sql/sql_select.cc	2007-09-21 10:14:59 +02:00
+++ b/sql/sql_select.cc	2007-10-13 12:03:47 +02:00
@@ -9578,7 +9578,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARA
   table->keys_in_use_for_query.init();
 
   table->s= share;
-  init_tmp_table_share(share, "", 0, tmpname, tmpname);
+  init_tmp_table_share(thd, share, "", 0, tmpname, tmpname);
   share->blob_field= blob_field;
   share->blob_ptr_size= mi_portable_sizeof_char_ptr;
   share->db_low_byte_first=1;                // True for HEAP and MyISAM
diff -Nrup a/sql/sql_table.cc b/sql/sql_table.cc
--- a/sql/sql_table.cc	2007-09-12 22:11:45 +02:00
+++ b/sql/sql_table.cc	2007-10-13 12:03:47 +02:00
@@ -4042,7 +4042,7 @@ static bool mysql_admin_table(THD* thd, 
       if (view_operator_func == NULL)
         table->required_type=FRMTYPE_TABLE;
 
-      open_and_lock_tables(thd, table, FALSE);
+      open_and_lock_tables(thd, table);
       thd->no_warnings_for_error= 0;
       table->next_global= save_next_global;
       table->next_local= save_next_local;
@@ -5672,7 +5672,6 @@ bool mysql_alter_table(THD *thd,char *ne
                        uint order_num, ORDER *order, bool ignore)
 {
   TABLE *table, *new_table= 0, *name_lock= 0;
-  TABLE_LIST *save_next_global;
   int error= 0;
   char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
   char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
@@ -5826,14 +5825,9 @@ view_err:
     start_waiting_global_read_lock(thd);
     DBUG_RETURN(error);
   }
-  save_next_global= table_list->next_global;
-  table_list->next_global= NULL;
-  table_list->lock_type= TL_WRITE_ALLOW_READ;
-  table_list->required_type= FRMTYPE_TABLE;
-  if (open_and_lock_tables(thd, table_list, TRUE))
+
+  if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ)))
     DBUG_RETURN(TRUE);
-  table_list->next_global= save_next_global;
-  table= table_list->table;
   table->use_all_columns();
 
   /* Check that we are not trying to rename to an existing table */
@@ -6582,7 +6576,10 @@ view_err:
       if (reopen_table(table))
         goto err_with_placeholders;
       t_table= table;
-      /* No need to attach MERGE children here. */
+      /*
+        No need to attach MERGE children here. Table will not be used
+        during this statement any more.
+      */
     }
     /* Tell the handler that a new frm file is in place. */
     if (t_table->file->create_handler_files(path, NULL, CHF_INDEX_FLAG,
@@ -6610,7 +6607,6 @@ view_err:
     thd->in_lock_tables= 0;
     if (error)
       goto err_with_placeholders;
-    /* No need to attach MERGE children here. */
   }
   VOID(pthread_mutex_unlock(&LOCK_open));
 
@@ -7004,17 +7000,10 @@ bool mysql_checksum_table(THD *thd, TABL
   {
     char table_name[NAME_LEN*2+2];
     TABLE *t;
-    TABLE_LIST *save_next_global;
 
     strxmov(table_name, table->db ,".", table->table_name, NullS);
 
-    save_next_global= table->next_global;
-    table->next_global= NULL;
-    table->lock_type= TL_READ;
-    table->required_type=FRMTYPE_TABLE;
-    open_and_lock_tables(thd, table, TRUE);
-    t= table->table;
-    table->next_global= save_next_global;
+    t= table->table= open_n_lock_single_table(thd, table, TL_READ);
     thd->clear_error();			// these errors shouldn't get client
 
     protocol->prepare_for_resend();
diff -Nrup a/sql/sql_view.cc b/sql/sql_view.cc
--- a/sql/sql_view.cc	2007-10-04 20:24:09 +02:00
+++ b/sql/sql_view.cc	2007-10-13 12:03:47 +02:00
@@ -408,7 +408,7 @@ bool mysql_create_view(THD *thd, TABLE_L
   }
 #endif
 
-  if (open_and_lock_tables(thd, tables, FALSE))
+  if (open_and_lock_tables(thd, tables))
   {
     res= TRUE;
     goto err;
diff -Nrup a/sql/table.cc b/sql/table.cc
--- a/sql/table.cc	2007-08-31 11:55:53 +02:00
+++ b/sql/table.cc	2007-10-13 12:03:47 +02:00
@@ -329,6 +329,7 @@ TABLE_SHARE *alloc_table_share(TABLE_LIS
 
   SYNOPSIS
     init_tmp_table_share()
+    thd         thread handle
     share	Share to fill
     key		Table_cache_key, as generated from create_table_def_key.
 		must start with db name.    
@@ -346,7 +347,7 @@ TABLE_SHARE *alloc_table_share(TABLE_LIS
     use key_length= 0 as neither table_cache_key or key_length will be used).
 */
 
-void init_tmp_table_share(TABLE_SHARE *share, const char *key,
+void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
                           uint key_length, const char *table_name,
                           const char *path)
 {
@@ -373,8 +374,13 @@ void init_tmp_table_share(TABLE_SHARE *s
     anyway to be able to catch errors.
    */
   share->table_map_version= ~(ulonglong)0;
-  share->table_map_id= ~0UL;
   share->cached_row_logging_check= -1;
+
+  /*
+    table_map_id is also used for MERGE tables to suppress repeated
+    compatibility checks.
+  */
+  share->table_map_id= (ulong) thd->query_id;
 
   DBUG_VOID_RETURN;
 }
diff -Nrup a/sql/table.h b/sql/table.h
--- a/sql/table.h	2007-09-12 22:11:45 +02:00
+++ b/sql/table.h	2007-10-13 12:03:47 +02:00
@@ -454,9 +454,11 @@ struct st_table {
   struct st_table *open_next, **open_prev;	/* Link to open tables */
 #endif
   struct st_table *next, *prev;
-  struct st_table *parent;              /* Set in MERGE child tables */
-  TABLE_LIST      *child_l;             /* Set in MERGE parent tables */
-  TABLE_LIST      **child_last_l;       /* Set in MERGE parent tables */
+
+  /* For the below MERGE related members see top comment in ha_myisammrg.cc */
+  struct st_table *parent;          /* Set in MERGE child.  Ptr to parent */
+  TABLE_LIST      *child_l;         /* Set in MERGE parent. List of children */
+  TABLE_LIST      **child_last_l;   /* Set in MERGE parent. End of list */
 
   THD	*in_use;                        /* Which thread uses this */
   Field **field;			/* Pointer to fields */
@@ -607,7 +609,8 @@ struct st_table {
   my_bool insert_or_update;             /* Can be used by the handler */
   my_bool alias_name_used;		/* true if table_name is alias */
   my_bool get_fields_in_item_tree;      /* Signal to fix_field */
-  my_bool children_attached;            /* Used in MERGE parent tables */
+  /* If MERGE children attached to parent. See top comment in ha_myisammrg.cc */
+  my_bool children_attached;
 
   REGINFO reginfo;			/* field connections */
   MEM_ROOT mem_root;
@@ -982,7 +985,7 @@ struct TABLE_LIST
     (non-zero only for merged underlying tables of a view).
   */
   TABLE_LIST	*referencing_view;
-  /* Set in MERGE child tables. */
+  /* Ptr to parent MERGE table list item. See top comment in ha_myisammrg.cc */
   TABLE_LIST    *parent_l;
   /*
     Security  context (non-zero only for tables which belong
@@ -1016,6 +1019,8 @@ struct TABLE_LIST
   ulonglong	algorithm;		/* 0 any, 1 tmp tables , 2 merging */
   ulonglong     view_suid;              /* view is suid (TRUE dy default) */
   ulonglong     with_check;             /* WITH CHECK OPTION */
+  /* Remembered MERGE child table_map_id.  See top comment in ha_myisammrg.cc */
+  ulong         child_table_map_id;
   /*
     effective value of WITH CHECK OPTION (differ for temporary table
     algorithm)
diff -Nrup a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
--- a/storage/myisam/ha_myisam.cc	2007-10-04 20:24:09 +02:00
+++ b/storage/myisam/ha_myisam.cc	2007-10-13 12:03:48 +02:00
@@ -2137,8 +2137,11 @@ my_bool ha_myisam::register_query_cache_
 MI_INFO *myisam_engine_handle(handler *handler_handle)
 {
   DBUG_ENTER("myisam_engine_handle");
-  if (strcmp(handler_handle->table_type(), "MyISAM"))
+  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/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc
--- a/storage/myisammrg/ha_myisammrg.cc	2007-09-12 22:11:45 +02:00
+++ b/storage/myisammrg/ha_myisammrg.cc	2007-10-13 12:03:48 +02:00
@@ -14,6 +14,61 @@
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
 
+/*
+  MyISAM MERGE tables
+
+  A MyISAM MERGE table is kind of a union of zero or more MyISAM tables.
+
+  Besides the normal form file (.frm) a MERGE table has a meta file
+  (.MRG) with a list of tables. These are paths to the MyISAM table
+  files. The last two components of the path contain the database name
+  and the table name respectively.
+
+  When a MERGE table is open, there exists an TABLE object for the MERGE
+  table itself and a TABLE object for each of the MyISAM tables. For
+  abbreviated writing, I call the MERGE table object "parent" and the
+  MyISAM table objects "children".
+
+  A MERGE table is almost always opened through open_and_lock_tables()
+  and hence through open_tables(). When the parent appears in the list
+  of tables to open, the initial open of the handler does nothing but
+  read the meta file and collect a list of TABLE_LIST objects for the
+  children. This list is attached to the parent TABLE object as
+  TABLE::child_l. The end of the children list is saved in
+  TABLE::child_last_l.
+
+  Back in open_tables(), add_merge_table_list() is called. It updates
+  each list member with the lock type and a back pointer to the parent
+  TABLE_LIST object TABLE_LIST::parent_l. The list is then inserted in
+  the list of tables to open, right behind the parent. Consequently,
+  open_tables() opens the children, one after the other. The TABLE
+  references of the TABLE_LIST objects are implicitly set to the open
+  tables. The children are opened as independent MyISAM tables, right as
+  if they are used by the SQL statement.
+
+  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
+  children are set up.
+
+  The MERGE storage engine sets up an array with references to the
+  low-level MyISAM table objects (MI_INFO). It remembers the state of
+  the table in MYRG_INFO::children_attached.
+
+  Every child TABLE::parent references the parent TABLE object. That way
+  TABLE objects belonging to a MERGE table can be identified.
+
+  If necessary, the compatibility of parent and children is checked.
+  This check is necessary when any of the objects are reopend. This is
+  detected by comparing the current table_map_id against the remembered
+  TABLE_LIST::child_table_map_id. On parent open, the list members are
+  initialized to an "impossible"/"undefined" map id value. So the check
+  is always executed on the first attach.
+
+  Finally the parent TABLE::children_attached is set.
+
+*/
+
 #ifdef USE_PRAGMA_IMPLEMENTATION
 #pragma implementation				// gcc: Class implementation
 #endif
@@ -27,10 +82,6 @@
 #include "myrg_def.h"
 
 
-/*****************************************************************************
-** MyISAM MERGE tables
-*****************************************************************************/
-
 static handler *myisammrg_create_handler(handlerton *hton,
                                          TABLE_SHARE *table,
                                          MEM_ROOT *mem_root)
@@ -47,24 +98,18 @@ static handler *myisammrg_create_handler
 */
 
 ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg)
-  :handler(hton, table_arg), file(0), recinfo(0)
+  :handler(hton, table_arg), file(0)
 {}
 
 
 /**
   @brief Destructor
 
-  @detail Free allocated memory.
+  @detail Empty currently.
 */
 
 ha_myisammrg::~ha_myisammrg(void)
-{
-  /*
-    Both recinfo and keyinfo are allocated by my_multi_malloc(),
-    thus only recinfo must be freed.
-  */
-  my_free((uchar*) recinfo, MYF(MY_ALLOW_ZERO_PTR));
-}
+{}
 
 
 static const char *ha_myisammrg_exts[] = {
@@ -203,6 +248,9 @@ static int myisammrg_parent_open_callbac
   /* Set alias. */
   child_l->alias= child_l->table_name;
 
+  /* Initialize table map to 'undefined'. */
+  child_l->child_table_map_id= ~0UL;
+
   /* Link TABLE_LIST object into the parent list. */
   if (!parent->child_last_l)
   {
@@ -269,12 +317,34 @@ static MI_INFO *myisammrg_attach_childre
   /* Set parent reference. */
   child->parent= parent;
 
-  /* If parent is temporary, children must be temporary too and vice versa. */
+  /*
+    Do a quick compatibility check. TABLE::s::table_map_id is set when
+    the table share is created. TABLE_LIST::child_table_map_id is copied
+    from TABLE::s::table_map_id after a sucessful compatibility check.
+    We need to repeat the compatibility check only if a child is opened
+    from a different share than last time it was used with this MERGE
+    table.
+  */
+  DBUG_PRINT("myrg", ("table_map_id last: %lu  current: %lu",
+                      (ulong) child_l->child_table_map_id,
+                      (ulong) child->s->table_map_id));
+  if (child_l->child_table_map_id != child->s->table_map_id)
+    ha_myrg->need_compat_check= TRUE;
+
+  /*
+    If parent is temporary, children must be temporary too and vice
+    versa. This check must be done for every child on every open because
+    the table_map_id can overlap between temporary and non-temporary
+    tables. We need to detect the case where a non-temporary table has
+    been replaced with a temporary table of the same table_map_id. Or
+    vice versa. A very unlikely case, but it could happen.
+  */
   if (child->s->tmp_table != parent->s->tmp_table)
   {
     DBUG_PRINT("error", ("temporary table mismatch parent: %d  child: %d",
                          parent->s->tmp_table, child->s->tmp_table));
     my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+    goto err;
   }
 
   /* Extract the MyISAM table structure pointer from the handler object. */
@@ -287,6 +357,8 @@ static MI_INFO *myisammrg_attach_childre
   }
   DBUG_PRINT("myrg", ("MyISAM handle: 0x%lx  my_errno: %d",
                       (long) myisam, my_errno));
+
+ err:
   DBUG_RETURN(my_errno ? NULL : myisam);
 }
 
@@ -353,9 +425,12 @@ int ha_myisammrg::open(const char *name,
 
 int ha_myisammrg::attach_children(void)
 {
-  MYRG_TABLE *u_table;
-  uint keys= table->s->keys;
-  int error;
+  MYRG_TABLE    *u_table;
+  MI_COLUMNDEF  *recinfo;
+  MI_KEYDEF     *keyinfo;
+  uint          recs;
+  uint          keys= table->s->keys;
+  int           error;
   DBUG_ENTER("ha_myisammrg::attach_children");
   DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
                       table->s->table_name.str, (long) table));
@@ -363,6 +438,7 @@ int ha_myisammrg::attach_children(void)
   DBUG_ASSERT(!this->file->children_attached);
 
   next_child_attach= table->child_l;
+  need_compat_check= FALSE;
   my_errno= 0;
   if (myrg_attach_children(this->file, this->test_if_locked |
                            current_thd->open_options,
@@ -380,23 +456,31 @@ int ha_myisammrg::attach_children(void)
   if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
     myrg_extra(file,HA_EXTRA_WAIT_LOCK,0);
 
-  if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length)
-  {
-    DBUG_PRINT("error",("reclength: %lu  mean_rec_length: %lu",
-			table->s->reclength, stats.mean_rec_length));
-    if (test_if_locked & HA_OPEN_FOR_REPAIR)
-      myrg_print_wrong_table(file->open_tables->table->filename);
-    error= HA_ERR_WRONG_MRG_TABLE_DEF;
-    goto err;
-  }
-  if (!this->recinfo)
+  /*
+    The compatibility check is required only if one or more children
+    do not match their 'table_map_id' from the last check. This will
+    always happen at the first attach because the reference
+    'child_table_map_id' is initialized to 'undefined' at open.
+  */
+  DBUG_PRINT("myrg", ("need_compat_check: %d", need_compat_check));
+  if (need_compat_check)
   {
+    TABLE_LIST *child_l;
+
+    if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length)
+    {
+      DBUG_PRINT("error",("reclength: %lu  mean_rec_length: %lu",
+                          table->s->reclength, stats.mean_rec_length));
+      if (test_if_locked & HA_OPEN_FOR_REPAIR)
+        myrg_print_wrong_table(file->open_tables->table->filename);
+      error= HA_ERR_WRONG_MRG_TABLE_DEF;
+      goto err;
+    }
     /*
       Both recinfo and keyinfo are allocated by my_multi_malloc(), thus
       only recinfo must be freed.
     */
-    if ((error= table2myisam(table, &this->keyinfo,
-                             &this->recinfo, &this->recs)))
+    if ((error= table2myisam(table, &keyinfo, &recinfo, &recs)))
     {
       /* purecov: begin inspected */
       DBUG_PRINT("error", ("failed to convert TABLE object to MyISAM "
@@ -404,24 +488,37 @@ int ha_myisammrg::attach_children(void)
       goto err;
       /* purecov: end */
     }
-  }
-  for (u_table= file->open_tables; u_table < file->end_table; u_table++)
-  {
-    if (check_definition(keyinfo, recinfo, keys, recs,
-                         u_table->table->s->keyinfo, u_table->table->s->rec,
-                         u_table->table->s->base.keys,
-                         u_table->table->s->base.fields, false))
+    for (u_table= file->open_tables; u_table < file->end_table; u_table++)
     {
-      DBUG_PRINT("error", ("table definition mismatch: '%s'",
-                           u_table->table->filename));
-      error= HA_ERR_WRONG_MRG_TABLE_DEF;
-      if (!(this->test_if_locked & HA_OPEN_FOR_REPAIR))
-        goto err;
-      myrg_print_wrong_table(u_table->table->filename);
+      if (check_definition(keyinfo, recinfo, keys, recs,
+                           u_table->table->s->keyinfo, u_table->table->s->rec,
+                           u_table->table->s->base.keys,
+                           u_table->table->s->base.fields, false))
+      {
+        DBUG_PRINT("error", ("table definition mismatch: '%s'",
+                             u_table->table->filename));
+        error= HA_ERR_WRONG_MRG_TABLE_DEF;
+        if (!(this->test_if_locked & HA_OPEN_FOR_REPAIR))
+        {
+          my_free((uchar*) recinfo, MYF(0));
+          goto err;
+        }
+        myrg_print_wrong_table(u_table->table->filename);
+      }
+    }
+    my_free((uchar*) recinfo, MYF(0));
+    if (error == HA_ERR_WRONG_MRG_TABLE_DEF)
+      goto err;
+
+    /* All checks passed so far. Now update child_table_map_id. */
+    for (child_l= table->child_l; ; child_l= child_l->next_global)
+    {
+      child_l->child_table_map_id= child_l->table->s->table_map_id;
+
+      if (&child_l->next_global == table->child_last_l)
+        break;
     }
   }
-  if (error == HA_ERR_WRONG_MRG_TABLE_DEF)
-    goto err;
 #if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
   /* Merge table has more than 2G rows */
   if (table->s->crashed)
@@ -493,11 +590,12 @@ int ha_myisammrg::close(void)
 
 int ha_myisammrg::write_row(uchar * buf)
 {
+  DBUG_ENTER("ha_myisammrg::write_row");
   DBUG_ASSERT(this->file->children_attached);
   ha_statistic_increment(&SSV::ha_write_count);
 
   if (file->merge_insert_method == MERGE_INSERT_DISABLED || !file->tables)
-    return (HA_ERR_TABLE_READONLY);
+    DBUG_RETURN(HA_ERR_TABLE_READONLY);
 
   if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
     table->timestamp_field->set_time();
@@ -505,9 +603,9 @@ int ha_myisammrg::write_row(uchar * buf)
   {
     int error;
     if ((error= update_auto_increment()))
-      return error;
+      DBUG_RETURN(error);
   }
-  return myrg_write(file,buf);
+  DBUG_RETURN(myrg_write(file,buf));
 }
 
 int ha_myisammrg::update_row(const uchar * old_data, uchar * new_data)
diff -Nrup a/storage/myisammrg/ha_myisammrg.h b/storage/myisammrg/ha_myisammrg.h
--- a/storage/myisammrg/ha_myisammrg.h	2007-09-11 19:42:01 +02:00
+++ b/storage/myisammrg/ha_myisammrg.h	2007-10-13 12:03:48 +02:00
@@ -27,13 +27,9 @@ class ha_myisammrg: public handler
   MYRG_INFO *file;
 
  public:
-  uint          test_if_locked;         /* flags from ::open() */
   TABLE_LIST    *next_child_attach;     /* next child to attach */
-
-  /* For checking table definitions. */
-  MI_COLUMNDEF  *recinfo;
-  MI_KEYDEF     *keyinfo;
-  uint          recs;
+  uint          test_if_locked;         /* flags from ::open() */
+  bool          need_compat_check;      /* if need compatibility check */
 
   ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg);
   ~ha_myisammrg();
Thread
bk commit into 5.1 tree (istruewing:1.2569) BUG#26379Ingo Struewing13 Oct