List:Commits« Previous MessageNext Message »
From:Dmitry Lenev Date:August 19 2008 2:29pm
Subject:bzr commit into mysql-6.1-fk branch (dlenev:2682) Bug#35526, Bug#37371,
Bug#22909, WL#148
View as plain text  
#At file:///home/dlenev/src/bzr/mysql-6.1-mil7-ctl/

 2682 Dmitry Lenev	2008-08-19
      Patch for the 7th milestone of WL#148 "Foreign keys"
      ("DDL checks and changes CREATE, CREATE TABLE SELECT,
      CREATE TABLE LIKE") implementing necessary changes in
      CREATE TABLE LIKE (and thus fixing bug #35526 "Foreign
      keys: CREATE TABLE LIKE copies what it shouldn't").
      
      Per LLD CREATE TABLE LIKE statement should not copy foreign keys
      from source table to the new table.
      
      Since we store information about foreign keys in .FRM files
      and there is no simple way to modify this information without
      recreating .FRM file we have to change our implementation of
      CREATE TABLE LIKE. Instead of directly copying .FRMs file we
      now use the same code as simple CREATE TABLE.
      I.e. we generate structures describing table being created
      from source table and the pass these structures after minor
      tweaks to the mysql_create_table_no_lock() function.
      
      A side effect of this change is that CREATE TABLE LIKE now
      follows the same rules as CREATE/ALTER TABLE and thus bug
      #22909 "Using CREATE ... LIKE is possible to create field
      with invalid default value" and bug #37371 "CREATE TABLE
      LIKE merge loses UNION parameter" are solved.
      Another similar side-effects are that CREATE TABLE LIKE no
      longer preserves .FRM version and converts old varchar fields
      to new varchar fields.
modified:
  mysql-test/r/create.result
  mysql-test/r/foreign_key_all_engines.result
  mysql-test/r/foreign_key_all_engines_2.result
  mysql-test/r/merge.result
  mysql-test/t/create.test
  mysql-test/t/foreign_key_all_engines.test
  mysql-test/t/foreign_key_all_engines_2.test
  mysql-test/t/merge.test
  sql/mysql_priv.h
  sql/sql_insert.cc
  sql/sql_parse.cc
  sql/sql_partition.cc
  sql/sql_table.cc

per-file messages:
  mysql-test/r/create.result
    Added test coverage for CREATE TABLE LIKE behavior under LOCK
    TABLES and bug #22909 "Using CREATE ... LIKE is possible to
    create field with invalid default value".
  mysql-test/r/foreign_key_all_engines.result
    Added test coverage for handling of foreign keys by CREATE TABLE
    LIKE statement in --foreign-key-all-engines mode.
  mysql-test/r/foreign_key_all_engines_2.result
    Added test coverage for handling of foreign keys by CREATE TABLE
    LIKE statement in --foreign-key-all-engines mode.
  mysql-test/r/merge.result
    Added test for bug #37371 "CREATE TABLE LIKE merge loses UNION
    parameter".
  mysql-test/t/create.test
    Added test coverage for CREATE TABLE LIKE behavior under LOCK
    TABLES and bug #22909 "Using CREATE ... LIKE is possible to
    create field with invalid default value".
  mysql-test/t/foreign_key_all_engines.test
    Added test coverage for handling of foreign keys by CREATE TABLE
    LIKE statement in --foreign-key-all-engines mode.
  mysql-test/t/foreign_key_all_engines_2.test
    Added test coverage for handling of foreign keys by CREATE TABLE
    LIKE statement in --foreign-key-all-engines mode.
  mysql-test/t/merge.test
    Added test for bug #37371 "CREATE TABLE LIKE merge loses UNION
    parameter".
  sql/mysql_priv.h
    Got rid of two unused mysql_create_table() arguments.
  sql/sql_insert.cc
    Since mysql_create_table_no_lock() no longer writes statement to
    the binary log there is no reason to disable this functionality
    in CREATE TABLE SELECT implementation.
  sql/sql_parse.cc
    New metadata locking logic in CREATE TABLE LIKE statement
    requires table to be created present in statement's table
    list (in case if we create non-temporary table) with special
    flag which says that this table should be either open or we
    should obtain an exclusive metadata lock on it.
  sql/sql_partition.cc
    Since CREATE TABLE LIKE implementation now uses the same code
    as ordinary CREATE TABLE we no longer need special branch in
    mysql_unpack_partition() that handles this statement.
  sql/sql_table.cc
    mysql_create_table_no_lock():
      Make logging statement to the binary log responsibility
      of the caller of this function.
    mysql_create_table():
      Log statement to the binary log if it succeeds.
    mysql_create_like_table():
      Changed implementation of CREATE TABLE LIKE to use mostly
      the same code as ordinary CREATE TABLE. To do this we
      generate structures describing table being created from
      source table and the pass these structures (after minor
      tweaks, particularly removing info about foreign keys) to
      the mysql_create_table_no_lock() function.
      Also disallowed creation of non-temporary table under
      LOCK TABLES as in ordinary CREATE TABLE. This allowed
      to simplify metadata locking logic for this statement.
=== modified file 'mysql-test/r/create.result'
--- a/mysql-test/r/create.result	2008-08-03 10:13:01 +0000
+++ b/mysql-test/r/create.result	2008-08-19 12:29:01 +0000
@@ -385,6 +385,17 @@ ERROR 42S01: Table 't3' already exists
 drop table t1, t2, t3;
 drop table t3;
 drop database mysqltest;
+create table t1 (i int);
+create table t2 (j int);
+lock tables t1 read;
+create table t3 like t1;
+ERROR HY000: Can't execute the given command because you have active locked tables or an
active transaction
+create temporary table t3 like t1;
+drop temporary table t3;
+create temporary table t3 like t2;
+ERROR HY000: Table 't2' was not locked with LOCK TABLES
+unlock tables;
+drop tables t1, t2;
 SET SESSION storage_engine="heap";
 SELECT @@storage_engine;
 @@storage_engine
@@ -1892,3 +1903,39 @@ DROP TABLE t1;
 # -- End of Bug#34274
 
 End of 5.1 tests
+# 
+# Bug #22909 "Using CREATE ... LIKE is possible to create field
+#             with invalid default value"
+#
+# Altough original bug report suggests to use older version of MySQL
+# for producing .FRM with invalid defaults we use sql_mode to achieve
+# the same effect.
+drop tables if exists t1, t2;
+# Attempt to create table with invalid default should fail in normal mode
+create table t1 (dt datetime default '2008-02-31 00:00:00');
+ERROR 42000: Invalid default value for 'dt'
+set @old_mode= @@sql_mode;
+set @@sql_mode='ALLOW_INVALID_DATES';
+# The same should be possible in relaxed mode
+create table t1 (dt datetime default '2008-02-31 00:00:00');
+set @@sql_mode= @old_mode;
+# In normal mode attempt to create copy of table with invalid
+# default should fail
+create table t2 like t1;
+ERROR 42000: Invalid default value for 'dt'
+set @@sql_mode='ALLOW_INVALID_DATES';
+# But should work in relaxed mode
+create table t2 like t1;
+# Check that table definitions match
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `dt` datetime DEFAULT '2008-02-31 00:00:00'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+show create table t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `dt` datetime DEFAULT '2008-02-31 00:00:00'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+set @@sql_mode= @old_mode;
+drop tables t1, t2;

=== modified file 'mysql-test/r/foreign_key_all_engines.result'
--- a/mysql-test/r/foreign_key_all_engines.result	2008-06-27 05:11:56 +0000
+++ b/mysql-test/r/foreign_key_all_engines.result	2008-08-19 12:29:01 +0000
@@ -280,6 +280,16 @@ drop table t3;
 use test;
 drop database mysqltest;
 drop table t1, t2;
+drop tables if exists t1, t2, t3;
+create table t1 (pk int primary key);
+create table t2 (fk int constraint c references t1 (pk));
+create table t3 like t2;
+show create table t3;
+Table	Create Table
+t3	CREATE TABLE `t3` (
+  `fk` int(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+drop table t3, t2, t1;
 set @@rand_seed1=10000000,@@rand_seed2=1000000;
 drop tables if exists t1, t2;
 create table t2 (a int primary key);

=== modified file 'mysql-test/r/foreign_key_all_engines_2.result'
--- a/mysql-test/r/foreign_key_all_engines_2.result	2008-08-06 08:09:35 +0000
+++ b/mysql-test/r/foreign_key_all_engines_2.result	2008-08-19 12:29:01 +0000
@@ -134,6 +134,19 @@ delete from t1 where pk=2;
 unlock tables;
 # Switching to connection 'default'
 drop tables t1, t2, t3;
+drop tables if exists t1, t2, t3;
+create table t1 (pk int primary key);
+create table t2 (fk int constraint c references t1 (pk) on delete set null);
+create table t3 like t1;
+insert into t3 values (1);
+# Switching to connection 'blocker'
+lock tables t2 read;
+# Switching to connection 'default'
+delete from t3 where pk = 1;
+# Switching to connection 'blocker'
+unlock tables;
+# Switching to connection 'default'
+drop table t3, t2, t1;
 drop tables if exists t1, t2;
 create table t1 (s1 int primary key) engine=innodb;
 create table t2 (s1 int, foreign key (s1) references t1 (s1)) engine=innodb;

=== modified file 'mysql-test/r/merge.result'
--- a/mysql-test/r/merge.result	2008-05-22 22:42:32 +0000
+++ b/mysql-test/r/merge.result	2008-08-19 12:29:01 +0000
@@ -2058,3 +2058,23 @@ deallocate prepare stmt;
 #
 drop table t_parent;
 set @@global.table_definition_cache=@save_table_definition_cache;
+End of 6.0 tests
+#
+# Test for bug #37371 "CREATE TABLE LIKE merge loses UNION parameter"
+#
+drop tables if exists t1, m1, m2;
+create table t1 (i int) engine=myisam;
+create table m1 (i int) engine=mrg_myisam union=(t1) insert_method=first;
+create table m2 like m1;
+# Table definitions should match
+show create table m1;
+Table	Create Table
+m1	CREATE TABLE `m1` (
+  `i` int(11) DEFAULT NULL
+) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=FIRST UNION=(`t1`)
+show create table m2;
+Table	Create Table
+m2	CREATE TABLE `m2` (
+  `i` int(11) DEFAULT NULL
+) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=FIRST UNION=(`t1`)
+drop tables m1, m2, t1;

=== modified file 'mysql-test/t/create.test'
--- a/mysql-test/t/create.test	2008-08-03 10:13:01 +0000
+++ b/mysql-test/t/create.test	2008-08-19 12:29:01 +0000
@@ -331,6 +331,26 @@ create temporary table t3 like t1;
 drop table t1, t2, t3;
 drop table t3;
 drop database mysqltest;
+#
+# CREATE TABLE LIKE under LOCK TABLES
+#
+# Similarly to ordinary CREATE TABLE we don't allow creation of
+# non-temporary tables under LOCK TABLES. Also we require source
+# table to be locked.
+create table t1 (i int);
+create table t2 (j int);
+lock tables t1 read;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
+create table t3 like t1;
+# OTOH creating of temporary table should be OK
+create temporary table t3 like t1;
+drop temporary table t3;
+# Source table should be locked
+--error ER_TABLE_NOT_LOCKED
+create temporary table t3 like t2;
+unlock tables;
+drop tables t1, t2;
+
 
 #
 # Test default table type
@@ -1533,3 +1553,34 @@ DROP TABLE t1;
 
 --echo
 --echo End of 5.1 tests
+
+--echo # 
+--echo # Bug #22909 "Using CREATE ... LIKE is possible to create field
+--echo #             with invalid default value"
+--echo #
+--echo # Altough original bug report suggests to use older version of MySQL
+--echo # for producing .FRM with invalid defaults we use sql_mode to achieve
+--echo # the same effect.
+--disable_warnings
+drop tables if exists t1, t2;
+--enable_warnings
+--echo # Attempt to create table with invalid default should fail in normal mode
+--error ER_INVALID_DEFAULT
+create table t1 (dt datetime default '2008-02-31 00:00:00');
+set @old_mode= @@sql_mode;
+set @@sql_mode='ALLOW_INVALID_DATES';
+--echo # The same should be possible in relaxed mode
+create table t1 (dt datetime default '2008-02-31 00:00:00');
+set @@sql_mode= @old_mode;
+--echo # In normal mode attempt to create copy of table with invalid
+--echo # default should fail
+--error ER_INVALID_DEFAULT
+create table t2 like t1;
+set @@sql_mode='ALLOW_INVALID_DATES';
+--echo # But should work in relaxed mode
+create table t2 like t1;
+--echo # Check that table definitions match
+show create table t1;
+show create table t2;
+set @@sql_mode= @old_mode;
+drop tables t1, t2;

=== modified file 'mysql-test/t/foreign_key_all_engines.test'
--- a/mysql-test/t/foreign_key_all_engines.test	2008-06-27 05:11:56 +0000
+++ b/mysql-test/t/foreign_key_all_engines.test	2008-08-19 12:29:01 +0000
@@ -195,6 +195,28 @@ drop table t1, t2;
 
 
 #
+# Coverage for the 7th milestone of WL#148 "Foreign keys" ("DDL checks
+# and changes CREATE, CREATE TABLE SELECT, CREATE TABLE LIKE").
+#
+--disable_warnings
+drop tables if exists t1, t2, t3;
+--enable_warnings
+#
+# Test for foreign key handling in CREATE TABLE LIKE statement
+# (also test for bug #35526 "Foreign keys: CREATE TABLE LIKE copies
+# what it shouldn't")
+#
+# Check that we don't copy foreign key information when creating
+# table like a child table. See also similar test for parent table
+# in foreign_key_all_engines_2.test.
+create table t1 (pk int primary key);
+create table t2 (fk int constraint c references t1 (pk));
+create table t3 like t2;
+show create table t3;
+drop table t3, t2, t1;
+
+
+#
 # Test for bug #35522 "Foreign keys: 'foreign key without name' errors"
 # In --foreign-key-all-engines mode we should not replace names of FKs
 # in error messages with 'foreign key without name' even if though names

=== modified file 'mysql-test/t/foreign_key_all_engines_2.test'
--- a/mysql-test/t/foreign_key_all_engines_2.test	2008-08-06 08:09:35 +0000
+++ b/mysql-test/t/foreign_key_all_engines_2.test	2008-08-19 12:29:01 +0000
@@ -275,6 +275,38 @@ drop tables t1, t2, t3;
 
 
 #
+# Additional coverage for the 7th milestone of WL#148 "Foreign keys"
+# ("DDL checks and changes CREATE, CREATE TABLE SELECT, CREATE TABLE LIKE").
+#
+--disable_warnings
+drop tables if exists t1, t2, t3;
+--enable_warnings
+# Check how CREATE TABLE LIKE statement works with table which is parent
+# in a foreign key.
+# Since we can't directly check that newly created table does not contain
+# information about our foreign key, we have to resort to check relying on
+# prelocking algorithm.
+create table t1 (pk int primary key);
+create table t2 (fk int constraint c references t1 (pk) on delete set null);
+create table t3 like t1;
+insert into t3 values (1);
+--echo # Switching to connection 'blocker'
+connection blocker;
+lock tables t2 read;
+--echo # Switching to connection 'default'
+connection default;
+# Should not be blocked by above LOCK TABLES since t3's .FRM should
+# not contain record about referencing foreign key.
+delete from t3 where pk = 1;
+--echo # Switching to connection 'blocker'
+connection blocker;
+unlock tables;
+--echo # Switching to connection 'default'
+connection default;
+drop table t3, t2, t1;
+
+
+#
 # Bug #35519 "Foreign keys: create fails with InnoDB"
 # 
 # In --foreign-key-all-engines mode attempt to create foreign key on

=== modified file 'mysql-test/t/merge.test'
--- a/mysql-test/t/merge.test	2008-05-22 22:42:32 +0000
+++ b/mysql-test/t/merge.test	2008-08-19 12:29:01 +0000
@@ -1471,3 +1471,19 @@ while ($1)
 --enable_query_log
 drop table t_parent;
 set @@global.table_definition_cache=@save_table_definition_cache;
+
+--echo End of 6.0 tests
+
+--echo #
+--echo # Test for bug #37371 "CREATE TABLE LIKE merge loses UNION parameter"
+--echo #
+--disable_warnings
+drop tables if exists t1, m1, m2;
+--enable_warnings
+create table t1 (i int) engine=myisam;
+create table m1 (i int) engine=mrg_myisam union=(t1) insert_method=first;
+create table m2 like m1;
+--echo # Table definitions should match
+show create table m1;
+show create table m2;
+drop tables m1, m2, t1;

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2008-08-06 08:09:35 +0000
+++ b/sql/mysql_priv.h	2008-08-19 12:29:01 +0000
@@ -1260,8 +1260,7 @@ int prepare_create_field(Create_field *s
 bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
                         TABLE_LIST *fkey_tables,
                         HA_CREATE_INFO *create_info,
-                        Alter_info *alter_info,
-                        bool tmp_table, uint select_field_count);
+                        Alter_info *alter_info);
 bool mysql_create_table_no_lock(THD *thd, const char *db,
                                 const char *table_name,
                                 HA_CREATE_INFO *create_info,

=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc	2008-06-27 05:11:56 +0000
+++ b/sql/sql_insert.cc	2008-08-19 12:29:01 +0000
@@ -3442,7 +3442,6 @@ static TABLE *create_table_from_items(TH
     open_table().
   */
   {
-    tmp_disable_binlog(thd);
     if (!mysql_create_table_no_lock(thd, create_table->db,
                                     create_table->table_name,
                                     create_info, alter_info, 0,
@@ -3498,7 +3497,6 @@ static TABLE *create_table_from_items(TH
           table= create_table->table;
       }
     }
-    reenable_binlog(thd);
     if (!table)                                   // open failed
       DBUG_RETURN(0);
   }

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2008-08-03 10:13:01 +0000
+++ b/sql/sql_parse.cc	2008-08-19 12:29:01 +0000
@@ -2431,15 +2431,27 @@ mysql_execute_command(THD *thd)
       /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
       if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
         thd->options|= OPTION_KEEP_LOG;
-      /* regular create */
       if (create_info.options & HA_LEX_CREATE_TABLE_LIKE)
+      {
+        /* CREATE TABLE ... LIKE ... */
+        if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
+        {
+          lex->link_first_table_back(create_table, link_to_local);
+          create_table->open_type= TABLE_LIST::OPEN_OR_CREATE;
+        }
+
         res= mysql_create_like_table(thd, create_table, select_tables,
                                      &create_info);
+
+        if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
+          create_table= lex->unlink_first_table(&link_to_local);
+      }
       else
       {
+        /* Regular CREATE TABLE */
         res= mysql_create_table(thd, create_table,
                                 lex->create_foreign_key_tables,
-                                &create_info, &alter_info, 0, 0);
+                                &create_info, &alter_info);
       }
       if (!res)
 	my_ok(thd);

=== modified file 'sql/sql_partition.cc'
--- a/sql/sql_partition.cc	2008-06-11 11:49:58 +0000
+++ b/sql/sql_partition.cc	2008-08-19 12:29:01 +0000
@@ -3812,39 +3812,22 @@ bool mysql_unpack_partition(THD *thd,
              ha_resolve_storage_engine_name(default_db_type)));
   if (is_create_table_ind && old_lex->sql_command == SQLCOM_CREATE_TABLE)
   {
-    if (old_lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
-    {
-      /*
-        This code is executed when we create table in CREATE TABLE t1 LIKE t2.
-        old_lex->query_tables contains table list element for t2 and the table
-        we are opening has name t1.
-      */
-      if (partition_default_handling(table, part_info, FALSE,
-                                    
old_lex->query_tables->table->s->path.str))
-      {
-        result= TRUE;
-        goto end;
-      }
-    }
-    else
-    {
-      /*
-        When we come here we are doing a create table. In this case we
-        have already done some preparatory work on the old part_info
-        object. We don't really need this new partition_info object.
-        Thus we go back to the old partition info object.
-        We need to free any memory objects allocated on item_free_list
-        by the parser since we are keeping the old info from the first
-        parser call in CREATE TABLE.
-        We'll ensure that this object isn't put into table cache also
-        just to ensure we don't get into strange situations with the
-        item objects.
-      */
-      thd->free_items();
-      part_info= thd->work_part_info;
-      table->s->version= 0UL;
-      *work_part_info_used= true;
-    }
+    /*
+      When we come here we are doing a create table. In this case we
+      have already done some preparatory work on the old part_info
+      object. We don't really need this new partition_info object.
+      Thus we go back to the old partition info object.
+      We need to free any memory objects allocated on item_free_list
+      by the parser since we are keeping the old info from the first
+      parser call in CREATE TABLE.
+      We'll ensure that this object isn't put into table cache also
+      just to ensure we don't get into strange situations with the
+      item objects.
+    */
+    thd->free_items();
+    part_info= thd->work_part_info;
+    table->s->version= 0UL;
+    *work_part_info_used= true;
   }
   table->part_info= part_info;
   part_info->table= table;

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2008-08-06 08:09:35 +0000
+++ b/sql/sql_table.cc	2008-08-19 12:29:01 +0000
@@ -3325,11 +3325,12 @@ void sp_prepare_create_field(THD *thd, C
     way to ensure that concurrent operations won't intervene.
     mysql_create_table() is a wrapper that can be used for this.
 
-    no_log is needed for the case of CREATE ... SELECT,
-    as the logging will be done later in sql_insert.cc
     select_field_count is also used for CREATE ... SELECT,
     and must be zero for standard create of table.
 
+    Note that it is responsibility of a caller to write (or not to write)
+    statement which involves creation of this table into binlog.
+
   RETURN VALUES
     FALSE OK
     TRUE  error
@@ -3709,18 +3710,6 @@ bool mysql_create_table_no_lock(THD *thd
     thd->thread_specific_used= TRUE;
   }
 
-  /*
-    Don't write statement if:
-    - It is an internal temporary table,
-    - Row-based logging is used and it we are creating a temporary table, or
-    - The binary log is not open.
-    Otherwise, the statement shall be binlogged.
-   */
-  if (!internal_tmp_table &&
-      (!thd->current_stmt_binlog_row_based ||
-       (thd->current_stmt_binlog_row_based &&
-        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
-    write_bin_log(thd, TRUE, thd->query, thd->query_length);
   error= FALSE;
 unlock_and_end:
   pthread_mutex_unlock(&LOCK_open);
@@ -4030,16 +4019,14 @@ error_remove_new_frm:
 
 
 /**
-   Database locking and foreign keys aware wrapper for
+   Database locking, foreign keys and binary log aware wrapper for
    mysql_create_table_no_lock().
 */
 
 bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
                         TABLE_LIST *fkey_tables,
                         HA_CREATE_INFO *create_info,
-                        Alter_info *alter_info,
-                        bool internal_tmp_table,
-                        uint select_field_count)
+                        Alter_info *alter_info)
 {
   TABLE_LIST *tab;
   bool result;
@@ -4113,8 +4100,7 @@ bool mysql_create_table(THD *thd, TABLE_
                                      create_table->table_name,
                                      create_info,
                                      alter_info,
-                                     internal_tmp_table,
-                                     select_field_count);
+                                     FALSE, 0);
 
   if (opt_fk_all_engines &&
       !(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
@@ -4126,6 +4112,20 @@ bool mysql_create_table(THD *thd, TABLE_
     result= TRUE;
   }
 
+  /*
+    Don't write statement if:
+    - Table creation has failed
+    - Table has already existed
+    - Row-based logging is used and we are creating a temporary table
+    Otherwise, the statement shall be binlogged.
+  */
+  if (!result &&
+      !create_info->table_existed &&
+      (!thd->current_stmt_binlog_row_based ||
+       (thd->current_stmt_binlog_row_based &&
+        !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
+    write_bin_log(thd, TRUE, thd->query, thd->query_length);
+
 close_and_unlock:
   if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
   {
@@ -5031,58 +5031,6 @@ bool mysql_preload_keys(THD* thd, TABLE_
 }
 
 
-
-/**
-  @brief          Create frm file based on I_S table
-
-  @param[in]      thd                      thread handler
-  @param[in]      schema_table             I_S table
-  @param[in]      dst_path                 path where frm should be created
-  @param[in]      create_info              Create info
-
-  @return         Operation status
-    @retval       0                        success
-    @retval       1                        error
-*/
-
-
-bool mysql_create_like_schema_frm(THD* thd,
-                                  TABLE_LIST* schema_table,
-                                  char *dst_path, HA_CREATE_INFO *create_info)
-{
-  HA_CREATE_INFO local_create_info;
-  Alter_info alter_info;
-  bool tmp_table= (create_info->options & HA_LEX_CREATE_TMP_TABLE);
-  uint keys= schema_table->table->s->keys;
-  uint db_options= 0;
-  DBUG_ENTER("mysql_create_like_schema_frm");
-
-  bzero((char*) &local_create_info, sizeof(local_create_info));
-  local_create_info.db_type= schema_table->table->s->db_type();
-  local_create_info.row_type= schema_table->table->s->row_type;
-  local_create_info.default_table_charset=default_charset_info;
-  alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
-  schema_table->table->use_all_columns();
-  if (mysql_prepare_alter_table(thd, schema_table, &local_create_info,
-                                &alter_info))
-    DBUG_RETURN(1);
-  if (mysql_prepare_create_table(thd, &local_create_info, &alter_info,
-                                 tmp_table, &db_options,
-                                 schema_table->table->file,
-                                 &schema_table->table->s->key_info,
&keys, 0))
-    DBUG_RETURN(1);
-  local_create_info.max_rows= 0;
-  if (mysql_create_frm(thd, dst_path, NullS, NullS,
-                       &local_create_info, alter_info.create_list,
-                       keys, schema_table->table->s->key_info,
-                       alter_info.foreign_key_list,
-                       alter_info.parent_foreign_key_list,
-                       schema_table->table->file))
-    DBUG_RETURN(1);
-  DBUG_RETURN(0);
-}
-
-
 /*
   Create a table identical to the specified table
 
@@ -5101,142 +5049,75 @@ bool mysql_create_like_schema_frm(THD* t
 bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
                              HA_CREATE_INFO *create_info)
 {
-  MDL_LOCK_DATA *target_lock_data= 0;
-  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
-  uint dst_path_length;
-  char *db= table->db;
-  char *table_name= table->table_name;
-  int  err;
+  HA_CREATE_INFO local_create_info;
+  Alter_info local_alter_info;
   bool res= TRUE;
   uint not_used;
-#ifdef WITH_PARTITION_STORAGE_ENGINE
-  char tmp_path[FN_REFLEN];
-#endif
   DBUG_ENTER("mysql_create_like_table");
 
-
-  /*
-    By opening source table and thus acquiring shared metadata lock on it
-    we guarantee that it exists and no concurrent DDL operation will mess
-    with it. Later we also take an exclusive metadata lock on target table
-    name, which makes copying of .frm file, call to ha_create_table() and
-    binlogging atomic against concurrent DML and DDL operations on target
-    table. Thus by holding both these "locks" we ensure that our statement
-    is properly isolated from all concurrent operations which matter.
-  */
-  if (open_tables(thd, &src_table, &not_used, 0))
-    DBUG_RETURN(TRUE);
-
-  strxmov(src_path, src_table->table->s->path.str, reg_ext, NullS);
-
-  DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););
-
-  /* 
-    Check that destination tables does not exist. Note that its name
-    was already checked when it was added to the table list.
-  */
-  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
+  if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
+      thd->locked_tables_mode)
   {
-    if (find_temporary_table(thd, db, table_name))
-      goto table_exists;
-    dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
-    create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
-  }
-  else
-  {
-    if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_data))
-      goto err;
-    if (!target_lock_data)
-      goto table_exists;
-    dst_path_length= build_table_filename(dst_path, sizeof(dst_path),
-                                          db, table_name, reg_ext, 0);
-    if (!access(dst_path, F_OK))
-      goto table_exists;
     /*
-      Make the metadata lock available to open_table() called to
-      reopen the table down the road.
+      Since under LOCK TABLES attempt to acquire exclusive metadata lock
+      on the table to be created (and thus not locked) can lead to deadlock
+      we prohibit creation of non-temporary tables under LOCK TABLES.
     */
-    table->mdl_lock_data= target_lock_data;
-  }
-
-  DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););
-
-  /*
-    Create a new table by copying from source table
-
-    TODO: Obtaining LOCK_open mutex here is actually a legacy from the
-          times when some operations (e.g. I_S implementation) ignored
-          exclusive metadata lock on target table. Also some engines
-          (e.g. NDB cluster) require that LOCK_open should be held
-          during the call to ha_create_table() (See bug #28614 for more
-          info). So we should double check and probably fix this code
-          to not acquire this mutex.
-  */
-  pthread_mutex_lock(&LOCK_open);
-  if (src_table->schema_table)
-  {
-    if (mysql_create_like_schema_frm(thd, src_table, dst_path,
-                                     create_info))
-    {
-      pthread_mutex_unlock(&LOCK_open);
-      goto err;
-    }
-  }
-  else if (my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)))
-  {
-    if (my_errno == ENOENT)
-      my_error(ER_BAD_DB_ERROR,MYF(0),db);
-    else
-      my_error(ER_CANT_CREATE_FILE,MYF(0),dst_path,my_errno);
-    pthread_mutex_unlock(&LOCK_open);
+    my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
     goto err;
   }
 
   /*
-    As mysql_truncate don't work on a new table at this stage of
-    creation, instead create the table directly (for both normal
-    and temporary tables).
+    We the open source table to get its description in HA_CREATE_INFO
+    and Alter_info objects. This also acquires a shared metadata lock
+    on this table which ensures that no concurrent DDL operation will
+    mess with it.
+    Also in case when we create non-temporary table open_tables()
+    call obtains an exclusive metadata lock on target table ensuring
+    that we can safely perform table creation.
+    Thus by holding both these locks we ensure that our statement is
+    properly isolated from all concurrent operations which matter.
   */
-#ifdef WITH_PARTITION_STORAGE_ENGINE
-  /*
-    For partitioned tables we need to copy the .par file as well since
-    it is used in open_table_def to even be able to create a new handler.
-    There is no way to find out here if the original table is a
-    partitioned table so we copy the file and ignore any errors.
-  */
-  fn_format(tmp_path, dst_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
-  strmov(dst_path, tmp_path);
-  fn_format(tmp_path, src_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
-  strmov(src_path, tmp_path);
-  my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE));
-#endif
-
-  DBUG_EXECUTE_IF("sleep_create_like_before_ha_create", my_sleep(6000000););
+  if (open_tables(thd, &thd->lex->query_tables, &not_used, 0))
+    goto err;
+  src_table->table->use_all_columns();
 
-  dst_path[dst_path_length - reg_ext_length]= '\0';  // Remove .frm
-  if (thd->variables.keep_files_on_create)
-    create_info->options|= HA_CREATE_KEEP_FILES;
-  err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
-  pthread_mutex_unlock(&LOCK_open);
+  /* Fill HA_CREATE_INFO and Alter_info with description of source table. */
+  bzero((char*) &local_create_info, sizeof(local_create_info));
+  local_create_info.db_type= src_table->table->s->db_type();
+  local_create_info.row_type= src_table->table->s->row_type;
+  if (mysql_prepare_alter_table(thd, src_table, &local_create_info,
+                                &local_alter_info))
+    goto err;
+  /* Partition info is not handled by mysql_prepare_alter_table() call. */
+  if (src_table->table->part_info)
+    thd->work_part_info= src_table->table->part_info->get_clone();
 
-  if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
-  {
-    if (err || !open_temporary_table(thd, dst_path, db, table_name, 1,
-                                     OTM_OPEN))
-    {
-      (void) rm_temporary_table(create_info->db_type,
-				dst_path, false); /* purecov: inspected */
-      goto err;     /* purecov: inspected */
-    }
-  }
-  else if (err)
-  {
-    (void) quick_rm_table(create_info->db_type, db,
-			  table_name, 0); /* purecov: inspected */
-    goto err;	    /* purecov: inspected */
-  }
+  /*
+    Adjust description of source table before using it for creation of
+    target table.
 
-  DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000););
+    Similarly to SHOW CREATE TABLE we ignore MAX_ROWS attribute of
+    temporary table which represents I_S table.
+  */
+  if (src_table->schema_table)
+    local_create_info.max_rows= 0;
+  /* Set IF NOT EXISTS option as in the CREATE TABLE LIKE statement. */
+  local_create_info.options|= create_info->options&HA_LEX_CREATE_IF_NOT_EXISTS;
+  /* Replace type of source table with one specified in the statement. */
+  local_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE;
+  local_create_info.options|= create_info->options & HA_LEX_CREATE_TMP_TABLE;
+  /* Reset auto-increment counter for the new table. */
+  local_create_info.auto_increment_value= 0;
+  /* Remove info about foreign keys in which original table participates. */
+  local_alter_info.foreign_key_list.empty();
+  local_alter_info.parent_foreign_key_list.empty();
+
+  if ((res= mysql_create_table_no_lock(thd, table->db, table->table_name,
+                                       &local_create_info, &local_alter_info,
+                                       FALSE, 0)) ||
+      create_info->table_existed)
+    goto err;
 
   /*
     We have to write the query before we unlock the tables.
@@ -5302,28 +5183,7 @@ bool mysql_create_like_table(THD* thd, T
   else
     write_bin_log(thd, TRUE, thd->query, thd->query_length);
 
-  res= FALSE;
-  goto err;
-
-table_exists:
-  if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
-  {
-    char warn_buff[MYSQL_ERRMSG_SIZE];
-    my_snprintf(warn_buff, sizeof(warn_buff),
-		ER(ER_TABLE_EXISTS_ERROR), table_name);
-    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
-		 ER_TABLE_EXISTS_ERROR,warn_buff);
-    res= FALSE;
-  }
-  else
-    my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
-
 err:
-  if (target_lock_data)
-  {
-    mdl_release_lock(&thd->mdl_context, target_lock_data);
-    mdl_remove_lock(&thd->mdl_context, target_lock_data);
-  }
   DBUG_RETURN(res);
 }
 
@@ -5990,12 +5850,9 @@ int create_temporary_table(THD *thd,
   /*
     Create a table with a temporary name.
     With create_info->frm_only == 1 this creates a .frm file only.
-    We don't log the statement, it will be logged later.
   */
-  tmp_disable_binlog(thd);
   error= mysql_create_table_no_lock(thd, new_db, tmp_name,
                                     create_info, alter_info, 1, 0);
-  reenable_binlog(thd);
 
   DBUG_RETURN(error);
 }

Thread
bzr commit into mysql-6.1-fk branch (dlenev:2682) Bug#35526, Bug#37371,Bug#22909, WL#148Dmitry Lenev19 Aug
  • Re: bzr commit into mysql-6.1-fk branch (dlenev:2682) Bug#35526,Bug#37371, Bug#22909, WL#148Konstantin Osipov26 Sep