List:Internals« Previous MessageNext Message »
From:ingo Date:June 27 2005 5:54pm
Subject:bk commit into 4.0 tree (ingo:1.2116) BUG#5390
View as plain text  
Below is the list of changes that have just been committed into a local
4.0 repository of mydev. When mydev 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
  1.2116 05/06/27 19:54:37 ingo@stripped +8 -0
  BUG#5390 - problems with merge tables
  Fixed two bugs:
  1. CREATE TABLE IF NOT EXISTS ... SELECT is now aborted if the
  existing to be created table is one of the select tables. The
  existing read lock on the table cannot be changed to a write lock.
  2. INSERT ... SELECT with the same table on both sides (hidden
  below a MERGE table) does now work by buffering the select result.
  The duplicate detection works now after open_and_lock_tables() 
  on the locks.

  sql/sql_table.cc
    1.197 05/06/27 19:53:41 ingo@stripped +24 -8
    BUG#5390 - problems with merge tables
    Added a check if an existing to be created table is already locked.
    In this case the CREATE ... SELECT must be aborted. There is no
    way to make a write lock out of a read lock.
    Changed the error handling so that a pre-existing table is not
    dropped on error.

  sql/sql_parse.cc
    1.394 05/06/27 19:53:41 ingo@stripped +6 -5
    BUG#5390 - problems with merge tables
    Changed the duplicate tables detection for INSERT ... SELECT
    to use the new function, which does also work for MERGE tables.

  sql/mysql_priv.h
    1.234 05/06/27 19:53:41 ingo@stripped +2 -0
    BUG#5390 - problems with merge tables
    Added declarations for the new functions.

  sql/lock.cc
    1.52 05/06/27 19:53:41 ingo@stripped +141 -3
    BUG#5390 - problems with merge tables
    Added a new function to find highest lock of own thread in tables.
    Added a new function to find a duplicate lock in a list of tables.

  mysql-test/t/merge.test
    1.23 05/06/27 19:53:40 ingo@stripped +39 -0
    BUG#5390 - problems with merge tables
    Added tests.

  mysql-test/t/create.test
    1.23 05/06/27 19:53:40 ingo@stripped +0 -7
    BUG#5390 - problems with merge tables
    Removed duplicate test.

  mysql-test/r/merge.result
    1.25 05/06/27 19:53:40 ingo@stripped +99 -0
    BUG#5390 - problems with merge tables
    Added test results.

  mysql-test/r/create.result
    1.26 05/06/27 19:53:40 ingo@stripped +0 -11
    BUG#5390 - problems with merge tables
    Removed duplicate test.

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	ingo
# Host:	synthia.local
# Root:	/home/mydev/mysql-4.0-bug5390

--- 1.51/sql/lock.cc	Tue May 31 11:08:12 2005
+++ 1.52/sql/lock.cc	Mon Jun 27 19:53:41 2005
@@ -375,6 +375,143 @@
 }
 
 
+/*
+  Find highest lock of own thread in tables.
+
+  SYNOPSIS
+    mysql_lock_get_highest()
+    thd                         The current thread.
+    tables                      An array of pointers to the tables to lock.
+    count                       The number of tables to lock.
+
+  RETURN
+    The highest lock of own thread in tables.
+    TL_UNLOCK if no own lock exists.
+    TL_IGNORE on error.
+*/
+
+enum thr_lock_type mysql_lock_get_highest(THD *thd, TABLE **tables, uint count)
+{
+  MYSQL_LOCK            *sql_lock;
+  TABLE                 *write_lock_used;
+  THR_LOCK_DATA         **lock_data;
+  THR_LOCK_DATA         **end_data;
+  THR_LOCK_DATA         *cur_data;
+  enum thr_lock_type    highest;
+  DBUG_ENTER("mysql_lock_get_highest");
+
+  if (! (sql_lock= get_lock_data(thd, tables, count, 1, &write_lock_used)))
+    DBUG_RETURN(TL_IGNORE);
+
+  highest= TL_UNLOCK;
+  for (lock_data= sql_lock->locks, end_data= lock_data + sql_lock->lock_count;
+       lock_data < end_data;
+       lock_data++)
+  {
+    for (cur_data= (*lock_data)->lock->read.data;
+         cur_data;
+         cur_data= cur_data->next)
+    {
+      if (cur_data->type > highest)
+        highest= cur_data->type;
+    }
+    for (cur_data= (*lock_data)->lock->write.data;
+         cur_data;
+         cur_data= cur_data->next)
+    {
+      if (cur_data->type > highest)
+        highest= cur_data->type;
+    }
+  }
+
+  my_free((gptr) sql_lock, MYF(0));
+  DBUG_PRINT("exit", ("highest: %d", highest));
+  DBUG_RETURN(highest);
+}
+
+
+/*
+  Find duplicate lock in tables.
+
+  SYNOPSIS
+    mysql_lock_have_duplicate()
+    thd                         The current thread.
+    table                       The table to check for duplicate lock.
+    tables                      The list of tables to search for the dup lock.
+
+  NOTE
+    This is mainly meant for MERGE tables in INSERT ... SELECT
+    situations. The 'real', underlying tables can be found only after
+    the table is opened. The easier way is to check this after the
+    tables are locked.
+
+  RETURN
+    1           A table from 'tables' matches a lock on 'table'.
+    0           No duplicate lock is present.
+    -1          Error.
+*/
+
+int mysql_lock_have_duplicate(THD *thd, TABLE *table, TABLE_LIST *tables)
+{
+  uint                  count;
+  MYSQL_LOCK            *sql_lock1;
+  MYSQL_LOCK            *sql_lock2;
+  TABLE                 **tables1= &table;
+  TABLE                 **tables2;
+  TABLE                 **table_ptr;
+  TABLE_LIST            *tablist2;
+  TABLE                 *write_lock_used;
+  THR_LOCK_DATA         **lock_data1;
+  THR_LOCK_DATA         **end_data1;
+  THR_LOCK_DATA         **lock_data2;
+  THR_LOCK_DATA         **end_data2;
+  THR_LOCK              *lock1;
+  DBUG_ENTER("mysql_lock_have_duplicate");
+
+  if (! (sql_lock1= get_lock_data(thd, tables1, 1, 1, &write_lock_used)))
+    goto err0;
+
+  count=0;
+  for (tablist2 = tables; tablist2; tablist2= tablist2->next)
+    count++;
+  if (! (tables2= (TABLE**) sql_alloc(sizeof(TABLE*) * count)))
+    goto err1;
+  table_ptr= tables2;
+  for (tablist2 = tables; tablist2; tablist2= tablist2->next)
+    *(table_ptr++)= tablist2->table;
+  if (! (sql_lock2= get_lock_data(thd, tables2, count, 1, &write_lock_used)))
+    goto err1;
+
+  count= 1;
+  for (lock_data1= sql_lock1->locks,
+         end_data1= lock_data1 + sql_lock1->lock_count;
+       lock_data1 < end_data1;
+       lock_data1++)
+  {
+    lock1= (*lock_data1)->lock;
+    for (lock_data2= sql_lock2->locks,
+           end_data2= lock_data2 + sql_lock2->lock_count;
+         lock_data2 < end_data2;
+         lock_data2++)
+    {
+      if ((*lock_data2)->lock == lock1)
+        goto end;
+    }
+  }
+  count= 0;
+
+ end:
+  my_free((gptr) sql_lock2, MYF(0));
+  my_free((gptr) sql_lock1, MYF(0));
+  DBUG_RETURN(count);
+
+ err1:
+  my_free((gptr) sql_lock1, MYF(0));
+ err0:
+  DBUG_RETURN(-1);
+}
+
+
 	/* unlock a set of external */
 
 static int unlock_external(THD *thd, TABLE **table,uint count)
@@ -411,6 +548,7 @@
   MYSQL_LOCK *sql_lock;
   THR_LOCK_DATA **locks;
   TABLE **to;
+  DBUG_ENTER("get_lock_data");
 
   *write_lock_used=0;
   for (i=tables=lock_count=0 ; i < count ; i++)
@@ -426,7 +564,7 @@
 	my_malloc(sizeof(*sql_lock)+
 		  sizeof(THR_LOCK_DATA*)*tables+sizeof(table_ptr)*lock_count,
 		  MYF(0))))
-    return 0;
+    DBUG_RETURN(0);
   locks=sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
   to=sql_lock->table=(TABLE**) (locks+tables);
   sql_lock->table_count=lock_count;
@@ -446,13 +584,13 @@
       {
 	my_error(ER_OPEN_AS_READONLY,MYF(0),table->table_name);
 	my_free((gptr) sql_lock,MYF(0));
-	return 0;
+	DBUG_RETURN(0);
       }
     }
     locks=table->file->store_lock(thd, locks, get_old_locks ? TL_IGNORE :
 				  lock_type);
   }
-  return sql_lock;
+  DBUG_RETURN(sql_lock);
 }
 
 /*****************************************************************************

--- 1.233/sql/mysql_priv.h	Tue May 31 11:08:12 2005
+++ 1.234/sql/mysql_priv.h	Mon Jun 27 19:53:41 2005
@@ -778,6 +778,8 @@
 void mysql_lock_abort(THD *thd, TABLE *table);
 void mysql_lock_abort_for_thread(THD *thd, TABLE *table);
 MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
+enum thr_lock_type mysql_lock_get_highest(THD *thd, TABLE **tables, uint count);
+int mysql_lock_have_duplicate(THD *thd, TABLE *table, TABLE_LIST *tables);
 bool lock_global_read_lock(THD *thd);
 void unlock_global_read_lock(THD *thd);
 bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, bool is_not_commit);

--- 1.393/sql/sql_parse.cc	Tue May 31 11:08:12 2005
+++ 1.394/sql/sql_parse.cc	Mon Jun 27 19:53:41 2005
@@ -2078,11 +2078,6 @@
     if (thd->select_limit < select_lex->select_limit)
       thd->select_limit= HA_POS_ERROR;		// No limit
 
-    if (check_dup(tables->db, tables->real_name, tables->next))
-    {
-      /* Using same table for INSERT and SELECT */
-      select_lex->options |= OPTION_BUFFER_RESULT;
-    }
     {
       /* TODO: Delete the following loop when locks is set by sql_yacc */
       TABLE_LIST *table;
@@ -2095,6 +2090,12 @@
       (byte*) (((TABLE_LIST *) lex->select_lex.table_list.first)->next);
     if (!(res=open_and_lock_tables(thd, tables)))
     {
+      /* MERGE sub-tables can only be detected after open. */
+      if (mysql_lock_have_duplicate(thd, tables->table, tables->next))
+      {
+        /* Using same table for INSERT and SELECT */
+        select_lex->options |= OPTION_BUFFER_RESULT;
+      }
       if ((result=new select_insert(tables->table,&lex->field_list,
 				    lex->duplicates)))
 	res=handle_select(thd,lex,result);

--- 1.196/sql/sql_table.cc	Tue May 31 11:08:12 2005
+++ 1.197/sql/sql_table.cc	Mon Jun 27 19:53:41 2005
@@ -846,9 +846,9 @@
 {
   TABLE tmp_table;		// Used during 'create_field()'
   TABLE *table= 0;
-  tmp_table.table_name=0;
   DBUG_ENTER("create_table_from_items");
 
+  tmp_table.table_name= 0;
   /* Add selected items to field list */
   List_iterator_fast<Item> it(*items);
   Item *item;
@@ -895,17 +895,33 @@
   reenable_binlog(thd);
   if (!table)
     DBUG_RETURN(0);
-  table->reginfo.lock_type=TL_WRITE;
-  if (! ((*lock)= mysql_lock_tables(thd, &table, 1, MYSQL_LOCK_IGNORE_FLUSH)))
+  /*
+    In a CREATE TABLE IF NOT EXISTS ... SELECT the create table might
+    exist and also appear (perhaps as a MERGE sub-table) as a select
+    table. We must not try to write lock a table, which has already a
+    read lock on it. Since we do no lock escalation in MySQL, we need to
+    stop here.
+  */
+  if (create_info->table_existed &&
+      (mysql_lock_get_highest(thd, &table, 1) > TL_UNLOCK))
   {
-    VOID(pthread_mutex_lock(&LOCK_open));
-    hash_delete(&open_cache,(byte*) table);
-    VOID(pthread_mutex_unlock(&LOCK_open));
-    quick_rm_table(create_info->db_type,db,table_case_name(create_info, name));
-    DBUG_RETURN(0);
+    my_error(ER_INSERT_TABLE_USED, MYF(0), table->real_name);
+    goto err;
   }
+  table->reginfo.lock_type= TL_WRITE;
+  if (! ((*lock)= mysql_lock_tables(thd, &table, 1, MYSQL_LOCK_IGNORE_FLUSH)))
+    goto err;
   table->file->extra(HA_EXTRA_WRITE_CACHE);
   DBUG_RETURN(table);
+
+ err:
+  VOID(pthread_mutex_lock(&LOCK_open));
+  hash_delete(&open_cache, (byte*) table);
+  VOID(pthread_mutex_unlock(&LOCK_open));
+  if (! create_info->table_existed)
+    quick_rm_table(create_info->db_type, db,
+                   table_case_name(create_info, name));
+  DBUG_RETURN(0);
 }
 
 

--- 1.25/mysql-test/r/create.result	Tue May 31 11:08:12 2005
+++ 1.26/mysql-test/r/create.result	Mon Jun 27 19:53:40 2005
@@ -189,17 +189,6 @@
 0	1	2
 0	0	1
 drop table t1;
-create table t1 select 1,2,3;
-create table if not exists t1 select 1,2;
-create table if not exists t1 select 1,2,3,4;
-Column count doesn't match value count at row 1
-create table if not exists t1 select 1;
-select * from t1;
-1	2	3
-1	2	3
-0	1	2
-0	0	1
-drop table t1;
 create table t1 (a int not null, b int, primary key (a));
 insert into t1 values (1,1);
 create table if not exists t1 select 2;

--- 1.24/mysql-test/r/merge.result	Fri Aug 27 13:42:58 2004
+++ 1.25/mysql-test/r/merge.result	Mon Jun 27 19:53:40 2005
@@ -619,3 +619,102 @@
 create table t3 engine=merge union=(t1, t2) select * from t2;
 INSERT TABLE 't2' isn't allowed in FROM table list
 drop table t1, t2;
+create table t1(a int);
+create table t2(a int);
+insert into t1 values (1);
+insert into t2 values (2);
+create table t3 (a int) engine=merge union=(t1, t2) insert_method=first;
+select * from t3;
+a
+1
+2
+insert t2 select * from t2;
+select * from t2;
+a
+2
+2
+insert t3 select * from t1;
+select * from t3;
+a
+1
+1
+2
+2
+insert t1 select * from t3;
+select * from t1;
+a
+1
+1
+1
+1
+2
+2
+select * from t2;
+a
+2
+2
+select * from t3;
+a
+1
+1
+1
+1
+2
+2
+2
+2
+check table t1, t2;
+Table	Op	Msg_type	Msg_text
+test.t1	check	status	OK
+test.t2	check	status	OK
+create table if not exists t4 select * from t3;
+select * from t4;
+a
+1
+1
+1
+1
+2
+2
+2
+2
+create table if not exists t4 select * from t3;
+select * from t4;
+a
+1
+1
+1
+1
+2
+2
+2
+2
+1
+1
+1
+1
+2
+2
+2
+2
+create table t1 select * from t3;
+Table 't1' already exists
+select * from t1;
+a
+1
+1
+1
+1
+2
+2
+create table if not exists t1 select * from t3;
+INSERT TABLE 't1' isn't allowed in FROM table list
+select * from t1;
+a
+1
+1
+1
+1
+2
+2
+drop table if exists t1, t2, t3, t4;

--- 1.22/mysql-test/t/create.test	Tue May 31 11:08:12 2005
+++ 1.23/mysql-test/t/create.test	Mon Jun 27 19:53:40 2005
@@ -158,13 +158,6 @@
 create table if not exists t1 select 1;
 select * from t1;
 drop table t1;
-create table t1 select 1,2,3;
-create table if not exists t1 select 1,2;
---error 1136
-create table if not exists t1 select 1,2,3,4;
-create table if not exists t1 select 1;
-select * from t1;
-drop table t1;
 
 #
 # Test create table if not exists with duplicate key error

--- 1.22/mysql-test/t/merge.test	Fri Aug 27 13:42:58 2004
+++ 1.23/mysql-test/t/merge.test	Mon Jun 27 19:53:40 2005
@@ -264,3 +264,42 @@
 --error 1093
 create table t3 engine=merge union=(t1, t2) select * from t2;
 drop table t1, t2;
+
+#
+# BUG#5390 - problems with merge tables
+#
+#drop table if exists t1, t2, t3, t4;
+create table t1(a int);
+create table t2(a int);
+insert into t1 values (1);
+insert into t2 values (2);
+create table t3 (a int) engine=merge union=(t1, t2) insert_method=first;
+select * from t3;
+#
+insert t2 select * from t2;
+select * from t2;
+#
+insert t3 select * from t1;
+select * from t3;
+#
+insert t1 select * from t3;
+select * from t1;
+select * from t2;
+select * from t3;
+check table t1, t2;
+#
+create table if not exists t4 select * from t3;
+select * from t4;
+#
+create table if not exists t4 select * from t3;
+select * from t4;
+#
+--error 1050
+create table t1 select * from t3;
+select * from t1;
+#
+--error 1093
+create table if not exists t1 select * from t3;
+select * from t1;
+drop table if exists t1, t2, t3, t4;
+
Thread
bk commit into 4.0 tree (ingo:1.2116) BUG#5390ingo27 Jun