Below is the list of changes that have just been committed into a local
4.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-04-28 14:31:55+02:00, istruewing@stripped +22 -0
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Not to be pushed. This is kind of a technology preview.
It is not yet complete. Though it does not seem to make
anything really worse, it turned out that at least another
step is required: When closing and opening the MERGE child
tables, it must be taken into account if a real open/close
or a reopen/close_old_data_files is going on. In the latter
case the child tables must not be deleted, but kept for
reopen. In reopen they must be picked up again.
This bug report revealed three problems:
1. A thread trying to lock a MERGE table performs busy waiting while
REPAIR TABLE or a similar table administration task is ongoing on
one or more of its MyISAM tables.
2. A thread trying to lock a MERGE table performs busy waiting until all
threads that did REPAIR TABLE or similar table administration tasks
on one or more of its MyISAM tables in LOCK TABLES segments do UNLOCK
TABLES. The difference against problem #1 is that the busy waiting
takes place *after* the administration task. It is terminated by
UNLOCK TABLES only.
3. Two FLUSH TABLES within a LOCK TABLES segment can invalidate the
lock. This does *not* require a MERGE table. The first FLUSH TABLES
can be replaced by any statement that requires other threads to
reopen the table. In 5.0 and 5.1 a single FLUSH TABLES can provoke
the problem.
Problem #1 and #2 are fixed by opening the MERGE child tables
through the table cache. So they are visible to other threads
and even for certain statements in the same thread. The child
tables are chained to the parent table, but do not appear in
thd->open_tables nor thd->locked_tables. This does also fix
the bugs 26377, 26867, and 8306 at least. I reverted the
preliminary fix for 8306.
Problem #3 is fixed by setting THD::some_tables_deleted for all
threads with open tables before releasing LOCK_open when waiting
for all threads to close their tables.
No test case. The misbehaviour cannot be repeated reliably by a
deterministic test without sleep()-injection in the code.
Please see the bug report for more information and tests for
manual testing.
include/myisammrg.h@stripped, 2007-04-28 14:31:50+02:00, istruewing@stripped +6 -1
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Added callback elements to MYRG_INFO.
Added callback arguments to myrg_open().
include/raid.h@stripped, 2007-04-28 14:31:50+02:00, istruewing@stripped +2 -2
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Fixed compiler warnings.
myisam/mi_create.c@stripped, 2007-04-28 14:31:50+02:00, istruewing@stripped +0 -15
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Reverted fix for bug 8306. It is implicitly and better fixed with
this patch.
myisam/mi_open.c@stripped, 2007-04-28 14:31:51+02:00, istruewing@stripped +1 -1
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Reverted fix for bug 8306. It is implicitly and better fixed with
this patch.
myisam/myisamdef.h@stripped, 2007-04-28 14:31:51+02:00, istruewing@stripped +0 -1
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Reverted fix for bug 8306. It is implicitly and better fixed with
this patch.
myisammrg/myrg_close.c@stripped, 2007-04-28 14:31:51+02:00, istruewing@stripped +16 -3
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Using callback for closing child tables if set. Otherwise close
MyISAM tables directly.
myisammrg/myrg_open.c@stripped, 2007-04-28 14:31:51+02:00, istruewing@stripped +76 -16
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Using callback for opening child tables if set. Otherwise open MyISAM
tables directly.
Store close_callback and callback_param in MYRG_INFO for use with
myrg_close().
Fixed error handling for failed allocation.
Fixed indentation.
mysql-test/r/merge.result@stripped, 2007-04-28 14:31:51+02:00, istruewing@stripped +18
-1
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Fixed results.
Moved test result for bug 8306 from myisam.result to here.
mysql-test/r/myisam.result@stripped, 2007-04-28 14:31:51+02:00, istruewing@stripped +0
-18
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Reverted fix for bug 8306. It is implicitly and better fixed with
this patch. Moved test result from here to merge.result.
mysql-test/t/merge.test@stripped, 2007-04-28 14:31:51+02:00, istruewing@stripped +29 -1
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Fixed error number.
Moved test for bug 8306 from myisam.test to here.
mysql-test/t/myisam.test@stripped, 2007-04-28 14:31:51+02:00, istruewing@stripped +0 -26
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Reverted fix for bug 8306. It is implicitly and better fixed with
this patch. Moved test from here to merge.test.
mysys/thr_lock.c@stripped, 2007-04-28 14:31:51+02:00, istruewing@stripped +35 -1
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Added DBUG calls.
sql/ha_myisam.cc@stripped, 2007-04-28 14:31:51+02:00, istruewing@stripped +21 -2
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Added new function myisam_engine_handle() to extract a MI_INFO
pointer from a handler object.
sql/ha_myisam.h@stripped, 2007-04-28 14:31:51+02:00, istruewing@stripped +1 -0
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Declared new function myisam_engine_handle() as friend of
ha_myisam class.
sql/ha_myisammrg.cc@stripped, 2007-04-28 14:31:51+02:00, istruewing@stripped +177 -11
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Added callback functions for open and close.
Allowed lock_count() and store_lock() to work on a closed table.
Fixed/added DBUG calls.
sql/ha_myisammrg.h@stripped, 2007-04-28 14:31:52+02:00, istruewing@stripped +9 -0
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Added struct st_myrg_callback_param to use with callback functions.
Added table_ptr() method to use with callback functions.
sql/lock.cc@stripped, 2007-04-28 14:31:52+02:00, istruewing@stripped +30 -2
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Added DBUG calls.
sql/mysql_priv.h@stripped, 2007-04-28 14:31:52+02:00, istruewing@stripped +6 -3
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Added 'is_child' parameter to open_table() for MERGE table childs.
Added 'is_temporary' parameter to open_temporary_table() to tell
the difference between a real temporary table and intermediate
tables, which are used during ALTER TABLE.
Added declaration for myisam_engine_handle() to use with MERGE
childs.
sql/sql_base.cc@stripped, 2007-04-28 14:31:52+02:00, istruewing@stripped +343 -87
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Added awareness of MERGE parent or child tables to several functions.
Added new parameter 'is_child' to open_tables(). If true don't
lock/unlock LOCK_open as it is done for the whole MERGE table (when
handling the parent). When picking the table from unused_tables, set
in_use for children too. Do not link children in open_tables chain.
When reopening a table, fix the mrg_parent links of all children.
Extended table_in_use() to check parent and children too.
Added parameter 'is_temporary' to open_temporary_table(). MERGE
tables need to know if we open "real" temporary tables as the path
names of their temporary children cannot be used to produce names
for open_table(). Luckily there is no need for locking of temporary
tables. So it is fine for temporary MERGE tables to open their
children directly.
When aborting locks, abort them for the parent table, if exists.
sql/sql_delete.cc@stripped, 2007-04-28 14:31:52+02:00, istruewing@stripped +1 -1
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Added argument to new 'is_temporary' parameter of
open_temporary_table().
sql/sql_table.cc@stripped, 2007-04-28 14:31:52+02:00, istruewing@stripped +13 -7
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Added argument to new 'is_temporary' parameter of
open_temporary_table().
Added argument to new 'is_child' parameter of open_table().
Moved locking of LOCK_open before intern_close_table() in
mysql_alter_table(). If old table was MERGE, we may need to close
child tables. This needs LOCK_open.
sql/table.h@stripped, 2007-04-28 14:31:52+02:00, istruewing@stripped +2 -0
Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts
a MERGE table
Added mrg_parent and mrg_child to TABLE for use with MERGE tables.
# 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: istruewing
# Host: chilla.local
# Root: /home/mydev/mysql-4.1-bug26379
--- 1.15/include/myisammrg.h 2007-04-28 14:31:59 +02:00
+++ 1.16/include/myisammrg.h 2007-04-28 14:31:59 +02:00
@@ -73,6 +73,8 @@ typedef struct st_myrg_info
LIST open_list;
QUEUE by_key;
ulong *rec_per_key_part; /* for sql optimizing */
+ void *callback_param; /* open/close callback parameter */
+ void (*close_callback)(); /* Set if open_callback has been used. */
} MYRG_INFO;
@@ -80,7 +82,10 @@ typedef struct st_myrg_info
extern int myrg_close(MYRG_INFO *file);
extern int myrg_delete(MYRG_INFO *file,const byte *buff);
-extern MYRG_INFO *myrg_open(const char *name,int mode,int wait_if_locked);
+extern MYRG_INFO *myrg_open(const char *name,int mode,int wait_if_locked,
+ MI_INFO *(*open_callback)(void*, char*),
+ void (*close_callback)(void*),
+ void *callback_param);
extern int myrg_panic(enum ha_panic_function function);
extern int myrg_rfirst(MYRG_INFO *file,byte *buf,int inx);
extern int myrg_rlast(MYRG_INFO *file,byte *buf,int inx);
--- 1.19/include/raid.h 2007-04-28 14:31:59 +02:00
+++ 1.20/include/raid.h 2007-04-28 14:31:59 +02:00
@@ -141,7 +141,7 @@ class RaidFd {
inline void Calculate()
{
DBUG_ENTER("RaidFd::_Calculate");
- DBUG_PRINT("info",("_position: %lu _raid_chunksize: %d, _size: %lu",
+ DBUG_PRINT("info",("_position: %lu _raid_chunksize: %lu _size: %lu",
(ulong) _position, _raid_chunksize, (ulong) _size));
_total_block = (ulong) (_position / _raid_chunksize);
@@ -149,7 +149,7 @@ class RaidFd {
_remaining_bytes = (uint) (_raid_chunksize -
(_position - _total_block * _raid_chunksize));
DBUG_PRINT("info",
- ("_total_block: %d this_block: %d _remaining_bytes:%d",
+ ("_total_block: %lu this_block: %u _remaining_bytes: %u",
_total_block, _this_block, _remaining_bytes));
DBUG_VOID_RETURN;
}
--- 1.51/myisam/mi_create.c 2007-04-28 14:31:59 +02:00
+++ 1.52/myisam/mi_create.c 2007-04-28 14:31:59 +02:00
@@ -564,21 +564,6 @@ int mi_create(const char *name,uint keys
create_flag=MY_DELETE_OLD;
}
- /*
- If a MRG_MyISAM table is in use, the mapped MyISAM tables are open,
- but no entry is made in the table cache for them.
- A TRUNCATE command checks for the table in the cache only and could
- be fooled to believe, the table is not open.
- Pull the emergency brake in this situation. (Bug #8306)
- */
- if (test_if_reopen(filename))
- {
- my_printf_error(0, "MyISAM table '%s' is in use "
- "(most likely by a MERGE table). Try FLUSH TABLES.",
- MYF(0), name + dirname_length(name));
- goto err;
- }
-
if ((file= my_create_with_symlink(linkname_ptr, filename, 0, create_mode,
MYF(MY_WME | create_flag))) < 0)
goto err;
--- 1.90/myisam/mi_open.c 2007-04-28 14:31:59 +02:00
+++ 1.91/myisam/mi_open.c 2007-04-28 14:31:59 +02:00
@@ -50,7 +50,7 @@ if (pos > end_pos) \
** In MySQL the server will handle version issues.
******************************************************************************/
-MI_INFO *test_if_reopen(char *filename)
+static MI_INFO *test_if_reopen(char *filename)
{
LIST *pos;
--- 1.82/myisam/myisamdef.h 2007-04-28 14:31:59 +02:00
+++ 1.83/myisam/myisamdef.h 2007-04-28 14:31:59 +02:00
@@ -728,7 +728,6 @@ void mi_copy_status(void* to,void *from)
my_bool mi_check_status(void* param);
void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows);
-extern MI_INFO *test_if_reopen(char *filename);
my_bool check_table_is_closed(const char *name, const char *where);
int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, File file_to_dup);
int mi_open_keyfile(MYISAM_SHARE *share);
--- 1.6/myisammrg/myrg_close.c 2007-04-28 14:31:59 +02:00
+++ 1.7/myisammrg/myrg_close.c 2007-04-28 14:31:59 +02:00
@@ -23,10 +23,23 @@ int myrg_close(MYRG_INFO *info)
int error=0,new_error;
MYRG_TABLE *file;
DBUG_ENTER("myrg_close");
+ DBUG_PRINT("info", ("myrg_info: 0x%lx close_cb: 0x%lx cb_param: 0x%lx",
+ (long) info, (long) info->close_callback,
+ (long) info->callback_param));
- for (file=info->open_tables ; file != info->end_table ; file++)
- if ((new_error=mi_close(file->table)))
- error=new_error;
+ /*
+ If there was a non-NULL close_callback argument given with the
+ corresponding myrg_open(), use this function for close instead
+ of closing the MyISAM tables directly.
+ */
+ if (info->close_callback)
+ (*info->close_callback)(info->callback_param);
+ else
+ {
+ for (file=info->open_tables ; file != info->end_table ; file++)
+ if ((new_error=mi_close(file->table)))
+ error=new_error;
+ }
delete_queue(&info->by_key);
pthread_mutex_lock(&THR_LOCK_open);
myrg_open_list=list_delete(myrg_open_list,&info->open_list);
--- 1.32/myisammrg/myrg_open.c 2007-04-28 14:31:59 +02:00
+++ 1.33/myisammrg/myrg_open.c 2007-04-28 14:31:59 +02:00
@@ -23,14 +23,31 @@
#include "mrg_static.c"
#endif
-/*
- open a MyISAM MERGE table
- if handle_locking is 0 then exit with error if some table is locked
- if handle_locking is 1 then wait if table is locked
-*/
+/**
+ @brief Open a MyISAM MERGE table
+
+ @detail If open_callback and close_callback functions are given,
+ they are used for opening and closing the child tables.
+ open_callback() is called for each child table. It opens one table.
+ close_callback() is called once for all child tables. It closes all
+ tables.
+
+ @param[in] name db/table_name
+ @param[in] mode O_RDONLY or O_RDWR
+ @param[in] handle_locking 0: error if some table is locked
+ 1: wait if table is locked
+ @param[in] open_callback function to use for opening a child table
+ @param[in] close_callback function to use for closing all child tables
+ @param[in] callback_param data pointer to give to the callbacks
+ @return pointer to opened MERGE table structure
+ @retval NULL on error
+*/
-MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking)
+MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking,
+ MI_INFO *(*open_callback)(void*, char*),
+ void (*close_callback)(void*),
+ void *callback_param)
{
int save_errno,errpos=0;
uint files= 0, i, dir_length, length, key_parts, min_keys= 0;
@@ -42,18 +59,23 @@ MYRG_INFO *myrg_open(const char *name, i
MI_INFO *isam=0;
uint found_merge_insert_method= 0;
DBUG_ENTER("myrg_open");
+ DBUG_PRINT("enter", ("name: '%s' mode: %d flags: %d",
+ name, mode, handle_locking));
+ DBUG_PRINT("enter", ("open_cb: 0x%lx close_cb: 0x%lx cb_param: 0x%lx",
+ (long) open_callback, (long) close_callback,
+ (long) callback_param));
LINT_INIT(key_parts);
bzero((char*) &file,sizeof(file));
if ((fd=my_open(fn_format(name_buff,name,"",MYRG_NAME_EXT,4),
O_RDONLY | O_SHARE,MYF(0))) < 0)
- goto err;
- errpos=1;
- if (init_io_cache(&file, fd, 4*IO_SIZE, READ_CACHE, 0, 0,
+ goto err;
+ errpos=1;
+ if (init_io_cache(&file, fd, 4*IO_SIZE, READ_CACHE, 0, 0,
MYF(MY_WME | MY_NABP)))
- goto err;
- errpos=2;
+ goto err;
+ errpos=2;
dir_length=dirname_part(name_buff,name);
while ((length=my_b_gets(&file,buff,FN_REFLEN-1)))
{
@@ -88,10 +110,27 @@ MYRG_INFO *myrg_open(const char *name, i
}
else
fn_format(buff, buff, "", "", 0);
- if (!(isam=mi_open(buff,mode,(handle_locking?HA_OPEN_WAIT_IF_LOCKED:0))))
+ DBUG_PRINT("info", ("child: '%s'", buff));
+ /*
+ If a non-NULL open_callback argument is present, use it for
+ opening the MyISAM table instead of opening it directly.
+ */
+ if (open_callback)
{
- my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
- goto err;
+ if (!(isam= (*open_callback)(callback_param, buff)))
+ {
+ DBUG_PRINT("myrg", ("open_callback failed, myerrno: %d", my_errno));
+ goto err;
+ }
+ }
+ else
+ {
+ if (!(isam= mi_open(buff, mode, (handle_locking ?
+ HA_OPEN_WAIT_IF_LOCKED : 0))))
+ {
+ my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+ goto err;
+ }
}
if (!m_info) /* First file */
{
@@ -100,7 +139,18 @@ MYRG_INFO *myrg_open(const char *name, i
files*sizeof(MYRG_TABLE) +
key_parts*sizeof(long),
MYF(MY_WME|MY_ZEROFILL))))
+ {
+ /*
+ Close first file. errpos is still == 2.
+ If a non-NULL close_callback argument is present, use it
+ instead of closing the MyISAM table directly.
+ */
+ if (close_callback)
+ (*close_callback)(callback_param);
+ else
+ mi_close(isam);
goto err;
+ }
if (files)
{
m_info->open_tables=(MYRG_TABLE *) (m_info+1);
@@ -109,6 +159,9 @@ MYRG_INFO *myrg_open(const char *name, i
files= 0;
}
m_info->reclength=isam->s->base.reclength;
+ /* Store callback information for use with myrg_close(). */
+ m_info->close_callback= close_callback;
+ m_info->callback_param= callback_param;
min_keys= isam->s->base.keys;
errpos=3;
}
@@ -163,8 +216,15 @@ err:
save_errno=my_errno;
switch (errpos) {
case 3:
- while (files)
- mi_close(m_info->open_tables[--files].table);
+ /*
+ If a non-NULL close_callback argument is present, use it
+ instead of closing the MyISAM tables directly.
+ */
+ if (close_callback)
+ (*close_callback)(callback_param);
+ else
+ while (files)
+ mi_close(m_info->open_tables[--files].table);
my_free((char*) m_info,MYF(0));
/* Fall through */
case 2:
--- 1.42/mysys/thr_lock.c 2007-04-28 14:31:59 +02:00
+++ 1.43/mysys/thr_lock.c 2007-04-28 14:31:59 +02:00
@@ -137,6 +137,9 @@ static int check_lock(struct st_lock_lis
fprintf(stderr,
"Warning: prev link %d didn't point at previous lock at %s: %s\n",
count, lock_type, where);
+ DBUG_PRINT("warning",(
+ "Warning: prev link %d didn't point at previous lock at %s: %s\n",
+ count, lock_type, where));
return 1;
}
if (same_thread && ! pthread_equal(data->thread,first_thread) &&
@@ -145,6 +148,9 @@ static int check_lock(struct st_lock_lis
fprintf(stderr,
"Warning: Found locks from different threads in %s: %s\n",
lock_type,where);
+ DBUG_PRINT("warning",(
+ "Warning: Found locks from different threads in %s: %s\n",
+ lock_type,where));
return 1;
}
if (no_cond && data->cond)
@@ -152,6 +158,9 @@ static int check_lock(struct st_lock_lis
fprintf(stderr,
"Warning: Found active lock with not reset cond %s: %s\n",
lock_type,where);
+ DBUG_PRINT("warning",(
+ "Warning: Found active lock with not reset cond %s: %s\n",
+ lock_type,where));
return 1;
}
prev= &data->next;
@@ -160,6 +169,8 @@ static int check_lock(struct st_lock_lis
{
fprintf(stderr,"Warning: found too many locks at %s: %s\n",
lock_type,where);
+ DBUG_PRINT("warning",("Warning: found too many locks at %s: %s\n",
+ lock_type,where));
return 1;
}
}
@@ -167,6 +178,8 @@ static int check_lock(struct st_lock_lis
{
fprintf(stderr,"Warning: last didn't point at last lock at %s: %s\n",
lock_type, where);
+ DBUG_PRINT("warning",("Warning: last didn't point at last lock at %s: %s\n",
+ lock_type, where));
return 1;
}
return 0;
@@ -201,6 +214,8 @@ static void check_locks(THR_LOCK *lock,
found_errors++;
fprintf(stderr,
"Warning at '%s': Locks read_no_write_count was %u when it should have been %u\n",
where, lock->read_no_write_count,count);
+ DBUG_PRINT("warning",(
+ "Warning at '%s': Locks read_no_write_count was %u when it should have been %u\n",
where, lock->read_no_write_count,count));
}
if (!lock->write.data)
@@ -212,6 +227,9 @@ static void check_locks(THR_LOCK *lock,
fprintf(stderr,
"Warning at '%s': No locks in use but locks are in wait queue\n",
where);
+ DBUG_PRINT("warning",(
+ "Warning at '%s': No locks in use but locks are in wait queue\n",
+ where));
}
if (!lock->write_wait.data)
{
@@ -221,6 +239,9 @@ static void check_locks(THR_LOCK *lock,
fprintf(stderr,
"Warning at '%s': No write locks and waiting read locks\n",
where);
+ DBUG_PRINT("warning",(
+ "Warning at '%s': No write locks and waiting read locks\n",
+ where));
}
}
else
@@ -236,6 +257,8 @@ static void check_locks(THR_LOCK *lock,
found_errors++;
fprintf(stderr,
"Warning at '%s': Write lock %d waiting while no exclusive read
locks\n",where,(int) lock->write_wait.data->type);
+ DBUG_PRINT("warning",(
+ "Warning at '%s': Write lock %d waiting while no exclusive read
locks\n",where,(int) lock->write_wait.data->type));
}
}
}
@@ -251,6 +274,9 @@ static void check_locks(THR_LOCK *lock,
fprintf(stderr,
"Warning at '%s': Found WRITE_ALLOW_WRITE lock waiting for WRITE_ALLOW_WRITE
lock\n",
where);
+ DBUG_PRINT("warning",(
+ "Warning at '%s': Found WRITE_ALLOW_WRITE lock waiting for WRITE_ALLOW_WRITE
lock\n",
+ where));
}
}
if (lock->read.data)
@@ -283,6 +309,11 @@ static void check_locks(THR_LOCK *lock,
where,
(int) lock->read_wait.data->type,
(int) lock->write.data->type);
+ DBUG_PRINT("warning",(
+ "Warning at '%s': Found read lock of type %d waiting for write lock of type %d\n",
+ where,
+ (int) lock->read_wait.data->type,
+ (int) lock->write.data->type));
}
}
}
@@ -371,6 +402,7 @@ static my_bool wait_for_lock(struct st_l
pthread_cond_t *cond=get_cond();
struct st_my_thread_var *thread_var=my_thread_var;
int result;
+ DBUG_ENTER("wait_for_lock");
if (!in_wait_list)
{
@@ -390,6 +422,8 @@ static my_bool wait_for_lock(struct st_l
if (data->cond != cond)
break;
}
+ DBUG_PRINT("lock", ("aborted: %d changed cond: %d",
+ thread_var->abort, (data->cond != cond)));
if (data->cond || data->type == TL_UNLOCK)
{
@@ -419,7 +453,7 @@ static my_bool wait_for_lock(struct st_l
thread_var->current_mutex= 0;
thread_var->current_cond= 0;
pthread_mutex_unlock(&thread_var->mutex);
- return result;
+ DBUG_RETURN(result);
}
--- 1.167/sql/ha_myisam.cc 2007-04-28 14:31:59 +02:00
+++ 1.168/sql/ha_myisam.cc 2007-04-28 14:31:59 +02:00
@@ -572,9 +572,11 @@ int ha_myisam::open(const char *name, in
int ha_myisam::close(void)
{
- MI_INFO *tmp=file;
+ MI_INFO *myisam= file;
+ DBUG_ENTER("ha_myisam::close");
+
file=0;
- return mi_close(tmp);
+ DBUG_RETURN(mi_close(myisam));
}
int ha_myisam::write_row(byte * buf)
@@ -1819,3 +1821,20 @@ uint ha_myisam::checksum() const
return (uint)file->s->state.checksum;
}
+
+/**
+ @brief Extract the MyISAM table structure pointer from a handler object.
+
+ @param[in] handler_handle pointer to handler object
+
+ @return MyISAM table structure pointer
+ @retval NULL if handler has not a MyISAM table open
+*/
+
+MI_INFO *myisam_engine_handle(handler *handler_handle)
+{
+ DBUG_ENTER("myisam_engine_handle");
+ if (strcmp(handler_handle->table_type(), "MyISAM"))
+ DBUG_RETURN(NULL);
+ DBUG_RETURN(((ha_myisam*) handler_handle)->file);
+}
--- 1.65/sql/ha_myisam.h 2007-04-28 14:31:59 +02:00
+++ 1.66/sql/ha_myisam.h 2007-04-28 14:31:59 +02:00
@@ -133,4 +133,5 @@ class ha_myisam: public handler
int dump(THD* thd, int fd);
int net_read_dump(NET* net);
#endif
+ friend MI_INFO *myisam_engine_handle(handler *handler_handle);
};
--- 1.65/sql/ha_myisammrg.cc 2007-04-28 14:31:59 +02:00
+++ 1.66/sql/ha_myisammrg.cc 2007-04-28 14:31:59 +02:00
@@ -54,6 +54,134 @@ const char *ha_myisammrg::index_type(uin
}
+/**
+ @brief Callback function for opening a MERGE child table.
+
+ @detail When using this function instead of opening the MyISAM table
+ directly, the MyISAM table is opened through the table cache. In a
+ couple of situations the MERGE child table can be found through the
+ table cache as in use. This solves a couple of MERGE problems.
+
+ This is called for each child table. It opens one table.
+
+ @param[in] callback_param data pointer as given to myrg_open()
+ @param[in] filename file name of MyISAM table
+ without extension.
+
+ @return pointer to opened MyISAM table structure
+ @retval NULL on error
+*/
+
+MI_INFO *myisammrg_open_callback(void *callback_param, char *filename)
+{
+ struct st_myrg_callback_param *open_param;
+ TABLE *table;
+ TABLE *parent;
+ MI_INFO *myisam;
+ char *table_name;
+ char *db;
+ uint dirlen;
+ char dir_path[FN_REFLEN];
+ bool not_used;
+ DBUG_ENTER("myisammrg_open_callback");
+
+ /* Extract child table name and database name from filename. */
+ dirlen= dirname_length(filename);
+ if (dirlen >= FN_REFLEN)
+ {
+ my_errno= ENAMETOOLONG;
+ DBUG_RETURN(NULL);
+ }
+ table_name= filename + dirlen;
+ dirlen--; /* Strip trailing '/'. */
+ memcpy(dir_path, filename, dirlen);
+ dir_path[dirlen]= '\0';
+ db= base_name(dir_path);
+ DBUG_PRINT("file", ("open db: '%s' table_name: '%s'", db, table_name));
+
+ /* Extract the MERGE table (parenT9 pointer from callback_param. */
+ open_param= (struct st_myrg_callback_param*) callback_param;
+ parent= open_param->myisammrg->table_ptr();
+
+ /* Open child table through table cache. */
+ table= open_table(open_param->thd, db, table_name, table_name, ¬_used, 1);
+ if (!table)
+ {
+ DBUG_PRINT("file", ("open table '%s'.'%s' failed", db, table_name));
+ my_errno= HA_ERR_LOCK_DEADLOCK;
+ DBUG_RETURN(NULL);
+ }
+ DBUG_PRINT("file", ("opened table: '%s' 0x%lx",
+ table->table_name, (long) table));
+
+ /* Extract the MyISAM table structure pointer from the handler object. */
+ if (!(myisam= myisam_engine_handle(table->file)))
+ {
+ DBUG_PRINT("file", ("no MyISAM handle for table: '%s' 0x%lx",
+ table->table_name, (long) table));
+ table->version= 0; /* Force deletion from cache. */
+ VOID(close_thread_table(open_param->thd, &table));
+ my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+ }
+ else
+ {
+ DBUG_ASSERT(!table->mrg_parent && !table->mrg_child);
+ /* Link parent table to child. */
+ table->mrg_parent= parent;
+ /* Link child table into child chain of parent. */
+ table->mrg_child= parent->mrg_child;
+ parent->mrg_child= table;
+ }
+ DBUG_PRINT("file", ("MyISAM handle: 0x%lx my_errno: %d",
+ (long) myisam, my_errno));
+ DBUG_RETURN(myisam);
+}
+
+
+/**
+ @brief Callback function for closing a MERGE child table.
+
+ @detail If the MERGE child tables were opened through
+ myisammrg_open_callback(), they must be closed through
+ myisammrg_close_callback(). Both function pointers as well as the data
+ pointer 'callback_param' are given to myrg_open().
+
+ This is called once only and closes all child tables.
+
+ @param[in] callback_param data pointer as given to myrg_open()
+*/
+
+void myisammrg_close_callback(void *callback_param)
+{
+ struct st_myrg_callback_param *open_param;
+ TABLE *child;
+ TABLE **list_head_p;
+ DBUG_ENTER("myisammrg_close_callback");
+
+ open_param= (struct st_myrg_callback_param*) callback_param;
+
+ /* Close all tables from the child chain. */
+ list_head_p= &open_param->myisammrg->table_ptr()->mrg_child;
+ while ((child= *list_head_p))
+ {
+ /*
+ Step to next child before closing the current one. We must not
+ access the table object after close any more.
+ */
+ *list_head_p= child->mrg_child;
+ /* Detach child from parent. */
+ child->mrg_parent= NULL;
+ child->mrg_child= NULL;
+ /* Force deletion from cache. */
+ child->version= 0;
+ /* Close child through table cache. */
+ VOID(close_thread_table(open_param->thd, &child));
+ }
+
+ DBUG_VOID_RETURN;
+};
+
+
int ha_myisammrg::open(const char *name, int mode, uint test_if_locked)
{
MI_KEYDEF *keyinfo;
@@ -63,15 +191,30 @@ int ha_myisammrg::open(const char *name,
uint keys= table->keys;
int error;
char name_buff[FN_REFLEN];
-
- DBUG_PRINT("info", ("ha_myisammrg::open"));
- if (!(file=myrg_open(fn_format(name_buff,name,"","",2 | 4), mode,
- test_if_locked)))
+ DBUG_ENTER("ha_myisammrg::open");
+ DBUG_PRINT("enter", ("name: '%s' mode: %d test_if_locked: %u",
+ name, mode, test_if_locked));
+
+ /* callback_param is part of the ha_myisammrg object. */
+ callback_param.thd= current_thd;
+ callback_param.myisammrg= this;
+ /*
+ Temporary tables are not opened through the table cache.
+ They are local to the thread only. So there is no need to let the
+ table cache know about them. Supply callback information only to
+ non-temporary tables.
+ */
+ if (!(file= (test_if_locked & HA_OPEN_TMP_TABLE) ?
+ myrg_open(fn_format(name_buff,name,"","",2 | 4), mode, test_if_locked,
+ NULL, NULL, NULL) :
+ myrg_open(fn_format(name_buff,name,"","",2 | 4), mode, test_if_locked,
+ myisammrg_open_callback, myisammrg_close_callback,
+ &callback_param)))
{
- DBUG_PRINT("info", ("ha_myisammrg::open exit %d", my_errno));
- return (my_errno ? my_errno : -1);
+ DBUG_PRINT("exit", ("my_errno %d", my_errno));
+ DBUG_RETURN(my_errno ? my_errno : -1);
}
- DBUG_PRINT("info", ("ha_myisammrg::open myrg_extrafunc..."))
+ DBUG_PRINT("info", ("calling myrg_extrafunc..."))
myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref);
if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
@@ -116,16 +259,28 @@ int ha_myisammrg::open(const char *name,
goto err;
}
#endif
- return (0);
+ DBUG_RETURN(0);
err:
myrg_close(file);
file=0;
- return (my_errno= error);
+ DBUG_RETURN(my_errno= error);
}
int ha_myisammrg::close(void)
{
- return myrg_close(file);
+ int error;
+ DBUG_ENTER("ha_myisammrg::close");
+
+ DBUG_ASSERT(callback_param.myisammrg == this);
+ error= myrg_close(file);
+ /*
+ It is possible that the table is closed, but ha_myisammrg kept.
+ Avoid any attempt to access the closed engine table.
+ See lock_count() and store_locks().
+ */
+ file= NULL;
+ DBUG_ASSERT(!table->mrg_child);
+ DBUG_RETURN(error);
}
int ha_myisammrg::write_row(byte * buf)
@@ -335,7 +490,11 @@ int ha_myisammrg::external_lock(THD *thd
uint ha_myisammrg::lock_count(void) const
{
- return file->tables;
+ /*
+ It is possible that the table is closed, but ha_myisammrg kept.
+ See close().
+ */
+ return file ? file->tables : 0;
}
@@ -344,6 +503,13 @@ THR_LOCK_DATA **ha_myisammrg::store_lock
enum thr_lock_type lock_type)
{
MYRG_TABLE *open_table;
+
+ /*
+ It is possible that the table is closed, but ha_myisammrg kept.
+ See close().
+ */
+ if (!file)
+ return to;
for (open_table=file->open_tables ;
open_table != file->end_table ;
--- 1.40/sql/ha_myisammrg.h 2007-04-28 14:31:59 +02:00
+++ 1.41/sql/ha_myisammrg.h 2007-04-28 14:31:59 +02:00
@@ -19,6 +19,13 @@
#pragma interface /* gcc class implementation */
#endif
+/* Data to be passed to open_callback() and close_callback(). */
+struct st_myrg_callback_param
+{
+ THD *thd;
+ class ha_myisammrg *myisammrg;
+};
+
/* class for the the myisam merge handler */
#include <myisammrg.h>
@@ -26,6 +33,7 @@
class ha_myisammrg: public handler
{
MYRG_INFO *file;
+ struct st_myrg_callback_param callback_param; /* For open/close callbacks */
public:
ha_myisammrg(TABLE *table): handler(table), file(0) {}
@@ -82,4 +90,5 @@ class ha_myisammrg: public handler
void update_create_info(HA_CREATE_INFO *create_info);
void append_create_info(String *packet);
MYRG_INFO *myrg_info() { return file; }
+ TABLE *table_ptr() { return table; }
};
--- 1.68/sql/lock.cc 2007-04-28 14:31:59 +02:00
+++ 1.69/sql/lock.cc 2007-04-28 14:31:59 +02:00
@@ -111,6 +111,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,
if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
&write_lock_used)))
break;
+ DBUG_PRINT("lock", ("lock sql_lock: 0x%lx count: %u",
+ (long) sql_lock, sql_lock->lock_count));
if (global_read_lock && write_lock_used &&
! (flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))
@@ -173,6 +175,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,
thd->proc_info=0;
/* some table was altered or deleted. reopen tables marked deleted */
+ DBUG_PRINT("lock", ("unlock sql_lock: 0x%lx count: %u",
+ (long) sql_lock, sql_lock->lock_count));
mysql_unlock_tables(thd,sql_lock);
thd->locked=0;
retry:
@@ -234,6 +238,8 @@ static int lock_external(THD *thd, TABLE
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
{
DBUG_ENTER("mysql_unlock_tables");
+ DBUG_PRINT("lock", ("unlock sql_lock: 0x%lx count: %u",
+ (long) sql_lock, sql_lock->lock_count));
if (sql_lock->lock_count)
thr_multi_unlock(sql_lock->locks,sql_lock->lock_count);
if (sql_lock->table_count)
@@ -251,9 +257,12 @@ void mysql_unlock_some_tables(THD *thd,
{
MYSQL_LOCK *sql_lock;
TABLE *write_lock_used;
+ DBUG_ENTER("mysql_unlock_some_tables");
+
if ((sql_lock= get_lock_data(thd, table, count, GET_LOCK_UNLOCK,
&write_lock_used)))
mysql_unlock_tables(thd, sql_lock);
+ DBUG_VOID_RETURN;
}
@@ -321,6 +330,7 @@ void mysql_unlock_read_tables(THD *thd,
void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
{
+ DBUG_ENTER("mysql_lock_remove");
mysql_unlock_some_tables(thd, &table,1);
if (locked)
{
@@ -375,6 +385,7 @@ void mysql_lock_remove(THD *thd, MYSQL_L
}
}
}
+ DBUG_VOID_RETURN;
}
/* abort all other threads waiting to get lock in table */
@@ -383,6 +394,8 @@ void mysql_lock_abort(THD *thd, TABLE *t
{
MYSQL_LOCK *locked;
TABLE *write_lock_used;
+ DBUG_ENTER("mysql_lock_abort");
+
if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
&write_lock_used)))
{
@@ -390,6 +403,7 @@ void mysql_lock_abort(THD *thd, TABLE *t
thr_abort_locks(locked->locks[i]->lock);
my_free((gptr) locked,MYF(0));
}
+ DBUG_VOID_RETURN;
}
@@ -667,7 +681,12 @@ static MYSQL_LOCK *get_lock_data(THD *th
*to++= table;
if (locks)
for ( ; org_locks != locks ; org_locks++)
+ {
(*org_locks)->debug_print_param= (void *) table;
+ DBUG_PRINT("lock", ("table: 0x%lx '%s' lock_count: %u data: 0x%lx",
+ (long) table, table->table_name,
+ table->lock_count, (long) *org_locks));
+ }
}
DBUG_RETURN(sql_lock);
}
@@ -830,6 +849,8 @@ int lock_table_name(THD *thd, TABLE_LIST
my_free((gptr) table,MYF(0));
DBUG_RETURN(-1);
}
+ DBUG_PRINT("exit", ("locked by name table: '%s' 0x%lx",
+ table->table_name, (long) table));
if (remove_table_from_cache(thd, db, table_list->real_name, RTFC_NO_FLAG))
{
@@ -851,12 +872,19 @@ void unlock_table_name(THD *thd, TABLE_L
static bool locked_named_table(THD *thd, TABLE_LIST *table_list)
{
+ DBUG_ENTER("locked_named_table");
for (; table_list ; table_list=table_list->next)
{
+ DBUG_PRINT("lock", ("check locked_named_table: '%s'",
+ table_list->real_name));
if (table_list->table && table_is_used(table_list->table,0))
- return 1;
+ {
+ DBUG_PRINT("lock", ("yes"));
+ DBUG_RETURN(1);
+ }
}
- return 0; // All tables are locked
+ DBUG_PRINT("lock", ("no locked_named_table"));
+ DBUG_RETURN(0); // All tables are locked
}
--- 1.388/sql/mysql_priv.h 2007-04-28 14:31:59 +02:00
+++ 1.389/sql/mysql_priv.h 2007-04-28 14:31:59 +02:00
@@ -621,8 +621,8 @@ int mysql_delete(THD *thd, TABLE_LIST *t
ha_rows rows, ulong options);
int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok);
TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update);
-TABLE *open_table(THD *thd,const char *db,const char *table,const char *alias,
- bool *refresh);
+TABLE *open_table(THD *thd, const char *db, const char *table,
+ const char *alias, bool *refresh, bool is_child);
TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table);
TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
bool reopen_table(TABLE *table,bool locked=0);
@@ -773,7 +773,8 @@ int open_normal_and_derived_tables(THD *
void relink_tables_for_derived(THD *thd);
int lock_tables(THD *thd, TABLE_LIST *tables, uint counter);
TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
- const char *table_name, bool link_in_list);
+ const char *table_name, bool link_in_list,
+ bool is_temporary);
bool rm_temporary_table(enum db_type base, char *path);
void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
@@ -1331,3 +1332,5 @@ bool check_stack_overrun(THD *thd,char *
inline void kill_delayed_threads(void) {}
#define check_stack_overrun(A, B) 0
#endif
+
+MI_INFO *myisam_engine_handle(handler *handler_handle);
--- 1.276/sql/sql_base.cc 2007-04-28 14:31:59 +02:00
+++ 1.277/sql/sql_base.cc 2007-04-28 14:31:59 +02:00
@@ -87,10 +87,12 @@ static void check_unused(void)
DBUG_PRINT("error",("Unused_links aren't connected")); /* purecov: inspected */
}
}
+ /* Count open tables in the table cache. */
for (idx=0 ; idx < open_cache.records ; idx++)
{
TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
- if (!entry->in_use)
+ /* Skip unused tables and MERGE child tables. */
+ if (!entry->in_use && !entry->mrg_parent)
count--;
}
if (count != 0)
@@ -162,7 +164,8 @@ OPEN_TABLE_LIST *list_open_tables(THD *t
if (!strcmp(table->table,entry->real_name) &&
!strcmp(table->db,entry->table_cache_key))
{
- if (entry->in_use)
+ /* Treat MERGE child tables as used. */
+ if (entry->in_use || entry->mrg_parent)
table->in_use++;
if (entry->locked_by_name)
table->locked++;
@@ -181,7 +184,8 @@ OPEN_TABLE_LIST *list_open_tables(THD *t
strmov(((*start_list)->db= (char*) ((*start_list)+1)),
entry->table_cache_key)+1,
entry->real_name);
- (*start_list)->in_use= entry->in_use ? 1 : 0;
+ /* Treat MERGE child tables as used. */
+ (*start_list)->in_use= (entry->in_use || entry->mrg_parent) ? 1 : 0;
(*start_list)->locked= entry->locked_by_name ? 1 : 0;
start_list= &(*start_list)->next;
*start_list=0;
@@ -197,9 +201,13 @@ OPEN_TABLE_LIST *list_open_tables(THD *t
void intern_close_table(TABLE *table)
{ // Free all structures
+ DBUG_ENTER("intern_close_table");
+ DBUG_PRINT("table", ("table: '%s' 0x%lx", table->table_name, (long) table));
+
free_io_cache(table);
if (table->file)
VOID(closefrm(table)); // close file
+ DBUG_VOID_RETURN;
}
/*
@@ -216,10 +224,11 @@ void intern_close_table(TABLE *table)
static void free_cache_entry(TABLE *table)
{
DBUG_ENTER("free_cache_entry");
+ DBUG_PRINT("table", ("table: '%s' 0x%lx", table->table_name, (long) table));
safe_mutex_assert_owner(&LOCK_open);
intern_close_table(table);
- if (!table->in_use)
+ if (!table->in_use && table->next) /* MERGE childs are in no chain. */
{
table->next->prev=table->prev; /* remove from used chain */
table->prev->next=table->next;
@@ -276,6 +285,42 @@ bool close_cached_tables(THD *thd, bool
#endif
}
refresh_version++; // Force close of open tables
+ DBUG_PRINT("table", ("incremented global refresh_version to: %lu",
+ refresh_version));
+ if (if_wait_for_refresh)
+ {
+ /*
+ Other threads could wait in a loop in mysql_lock_table() trying
+ to lock one or more of our tables.
+
+ If they wait for the locks in thr_multi_lock(), their lock
+ request is aborted. If they wait for the table to become unused,
+ they see table->locked_by_flush=1. But if they passed the loop
+ in wait_for_tables(), released LOCK_open, we (the FLUSH TABLES
+ thread) are scheduled at this moment and enter
+ close_cached_tables(), they could awake while we sleep below,
+ waiting for others threads to close their open tables. If this
+ happens, the other threads would find the tables unlocked. They
+ would get the locks, one after the other, and could do their
+ destructive work. This is an issue if we have LOCK TABLES in
+ effect.
+
+ The fix for this problem is to set some_tables_deleted for all
+ threads with open tables. These threads can still get their locks,
+ but will immediately release them again after checking this
+ variable. They will then reenter wait_for_tables(). There they
+ will wait until we update all tables version below.
+
+ Setting some_tables_deleted is done by remove_table_from_cache()
+ in the other branch.
+ */
+ for (uint idx=0 ; idx < open_cache.records ; idx++)
+ {
+ TABLE *table=(TABLE*) hash_element(&open_cache,idx);
+ if (table->in_use)
+ table->in_use->some_tables_deleted= 1;
+ }
+ }
}
else
{
@@ -400,11 +445,12 @@ void close_thread_tables(THD *thd, bool
VOID(pthread_mutex_lock(&LOCK_open));
safe_mutex_assert_owner(&LOCK_open);
- DBUG_PRINT("info", ("thd->open_tables=%p", thd->open_tables));
-
- found_old_table= 0;
+ found_old_table= 0;
while (thd->open_tables)
+ {
+ DBUG_PRINT("info", ("close open_tables: 0x%lx", (long) thd->open_tables));
found_old_table|=close_thread_table(thd, &thd->open_tables);
+ }
thd->some_tables_deleted=0;
/* Free tables to hold down open files */
@@ -426,10 +472,12 @@ void close_thread_tables(THD *thd, bool
bool close_thread_table(THD *thd, TABLE **table_ptr)
{
- DBUG_ENTER("close_thread_table");
-
bool found_old_table= 0;
TABLE *table= *table_ptr;
+ DBUG_ENTER("close_thread_table");
+ DBUG_PRINT("info", ("closing table: '%s' 0x%lx version: %lu",
+ table->table_name, (long) table, table->version));
+
DBUG_ASSERT(table->key_read == 0);
*table_ptr=table->next;
@@ -438,6 +486,7 @@ bool close_thread_table(THD *thd, TABLE
{
VOID(hash_delete(&open_cache,(byte*) table));
found_old_table=1;
+ DBUG_PRINT("info", ("deleted from cache"));
}
else
{
@@ -452,15 +501,40 @@ bool close_thread_table(THD *thd, TABLE
table->file->reset();
}
table->in_use=0;
- if (unused_tables)
+ /*
+ Do not add MERGE child tables to unused tables. Unused tables may
+ be deleted at any time. As long as a MERGE parent table is not
+ deleted, it will need its children when it is reopened. Since we
+ make the children kind of "floating" tables, we need to delete
+ them when the parent table is deleted.
+ */
+ if (table->mrg_parent)
{
- table->next=unused_tables; /* Link in last */
- table->prev=unused_tables->prev;
- unused_tables->prev=table;
- table->prev->next=table;
+ DBUG_PRINT("info", ("set floating merge child"));
+ table->next= table->prev= table;
}
else
- unused_tables=table->next=table->prev=table;
+ {
+ DBUG_PRINT("info", ("set unused"));
+ if (unused_tables)
+ {
+ table->next=unused_tables; /* Link in last */
+ table->prev=unused_tables->prev;
+ unused_tables->prev=table;
+ table->prev->next=table;
+ }
+ else
+ unused_tables=table->next=table->prev=table;
+ }
+ /* If the table has children, clear their in_use too.*/
+ for (TABLE *child= table->mrg_child; child; child= child->mrg_child)
+ {
+ DBUG_PRINT("info", ("set floating merge child: '%s' 0x%lx",
+ child->table_name, (long) child));
+ DBUG_ASSERT(child->in_use == thd);
+ DBUG_ASSERT(child->mrg_parent == table);
+ child->in_use= NULL;
+ }
}
DBUG_RETURN(found_old_table);
}
@@ -758,6 +832,8 @@ TABLE *unlink_open_table(THD *thd, TABLE
char key[MAX_DBKEY_LENGTH];
uint key_length=find->key_length;
TABLE *start=list,**prev,*next;
+ DBUG_ENTER("unlink_open_table");
+
prev= &start;
memcpy(key,find->table_cache_key,key_length);
for (; list ; list=next)
@@ -779,7 +855,7 @@ TABLE *unlink_open_table(THD *thd, TABLE
*prev=0;
// Notify any 'refresh' threads
pthread_cond_broadcast(&COND_refresh);
- return start;
+ DBUG_RETURN(start);
}
@@ -790,6 +866,7 @@ TABLE *unlink_open_table(THD *thd, TABLE
void wait_for_refresh(THD *thd)
{
+ DBUG_ENTER("wait_for_refresh");
safe_mutex_assert_owner(&LOCK_open);
/* Wait until the current table is up to date */
@@ -799,14 +876,18 @@ void wait_for_refresh(THD *thd)
proc_info=thd->proc_info;
thd->proc_info="Waiting for table";
if (!thd->killed)
+ {
+ DBUG_PRINT("pthreads", ("waiting for refresh"));
(void) pthread_cond_wait(&COND_refresh,&LOCK_open);
-
+ DBUG_PRINT("pthreads", ("awoke from refresh"));
+ }
pthread_mutex_unlock(&LOCK_open); // Must be unlocked first
pthread_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
thd->proc_info= proc_info;
pthread_mutex_unlock(&thd->mysys_var->mutex);
+ DBUG_VOID_RETURN;
}
@@ -843,6 +924,8 @@ TABLE *reopen_name_locked_table(THD* thd
pthread_mutex_unlock(&LOCK_open);
table->next = thd->open_tables;
thd->open_tables = table;
+ DBUG_PRINT("table", ("put into open_tables: '%s' 0x%lx",
+ table->table_name, (long) table));
table->tablenr=thd->current_tablenr++;
table->used_fields=0;
table->const_table=0;
@@ -854,24 +937,37 @@ TABLE *reopen_name_locked_table(THD* thd
}
-/******************************************************************************
-** open a table
-** Uses a cache of open tables to find a table not in use.
-** If refresh is a NULL pointer, then the is no version number checking and
-** the table is not put in the thread-open-list
-** If the return value is NULL and refresh is set then one must close
-** all tables and retry the open
-******************************************************************************/
+/**
+ @brief open a table
+
+ @detail Uses a cache of open tables to find a table not in use. If
+ refresh is a NULL pointer, then there is no version number checking
+ and the table is not put in the thread-open-list.
+
+ If the return value is NULL and refresh is set then one must close
+ all tables and retry the open.
+
+ @param[in] thd thread handle
+ @param[in] db database name
+ @param[in] table_name table name
+ @param[in] alias table alias
+ @param[in,out] refresh pointer to refresh flag, may be NULL
+ @param[in] is_child if opening a MERGE child table
+ if TRUE, LOCK_open must be locked before
+ @return pointer to the opened TABLE object
+ @retval NULL on error
+*/
-TABLE *open_table(THD *thd,const char *db,const char *table_name,
- const char *alias,bool *refresh)
+TABLE *open_table(THD *thd, const char *db, const char *table_name,
+ const char *alias, bool *refresh, bool is_child)
{
reg1 TABLE *table;
char key[MAX_DBKEY_LENGTH];
uint key_length;
HASH_SEARCH_STATE state;
DBUG_ENTER("open_table");
+ DBUG_PRINT("enter", ("db: '%s' table_name: '%s'", db, table_name));
/* find a unused table in the open table cache */
if (refresh)
@@ -882,45 +978,52 @@ TABLE *open_table(THD *thd,const char *d
int4store(key + key_length, thd->server_id);
int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
- for (table=thd->temporary_tables; table ; table=table->next)
+ if (!is_child)
{
- if (table->key_length == key_length + TMP_TABLE_KEY_EXTRA &&
- !memcmp(table->table_cache_key, key,
- key_length + TMP_TABLE_KEY_EXTRA))
- {
- if (table->query_id == thd->query_id)
- {
- my_printf_error(ER_CANT_REOPEN_TABLE,
- ER(ER_CANT_REOPEN_TABLE),MYF(0),table->table_name);
- DBUG_RETURN(0);
- }
- table->query_id=thd->query_id;
- table->clear_query_id=1;
- thd->tmp_table_used= 1;
- DBUG_PRINT("info",("Using temporary table"));
- goto reset;
+ for (table=thd->temporary_tables; table ; table=table->next)
+ {
+ if (table->key_length == key_length + TMP_TABLE_KEY_EXTRA &&
+ !memcmp(table->table_cache_key, key,
+ key_length + TMP_TABLE_KEY_EXTRA))
+ {
+ if (table->query_id == thd->query_id)
+ {
+ my_printf_error(ER_CANT_REOPEN_TABLE,
+ ER(ER_CANT_REOPEN_TABLE),MYF(0),table->table_name);
+ DBUG_RETURN(0);
+ }
+ table->query_id=thd->query_id;
+ table->clear_query_id=1;
+ thd->tmp_table_used= 1;
+ DBUG_PRINT("info", ("Using temporary table: 0x%lx", (long) table));
+ goto reset;
+ }
}
- }
- if (thd->locked_tables)
- { // Using table locks
- for (table=thd->open_tables; table ; table=table->next)
- {
- if (table->key_length == key_length &&
- !memcmp(table->table_cache_key,key,key_length) &&
- !my_strcasecmp(system_charset_info, table->table_name, alias) &&
- table->query_id != thd->query_id)
+ if (thd->locked_tables)
+ { // Using table locks
+ for (table=thd->open_tables; table ; table=table->next)
{
- table->query_id=thd->query_id;
- DBUG_PRINT("info",("Using locked table"));
- goto reset;
+ if (table->key_length == key_length &&
+ !memcmp(table->table_cache_key,key,key_length) &&
+ !my_strcasecmp(system_charset_info, table->table_name, alias) &&
+ table->query_id != thd->query_id)
+ {
+ table->query_id=thd->query_id;
+ DBUG_PRINT("info", ("Using locked table: 0x%lx", (long) table));
+ goto reset;
+ }
}
+ my_printf_error(ER_TABLE_NOT_LOCKED,ER(ER_TABLE_NOT_LOCKED),MYF(0),alias);
+ DBUG_RETURN(0);
}
- my_printf_error(ER_TABLE_NOT_LOCKED,ER(ER_TABLE_NOT_LOCKED),MYF(0),alias);
- DBUG_RETURN(0);
- }
- VOID(pthread_mutex_lock(&LOCK_open));
+ VOID(pthread_mutex_lock(&LOCK_open));
+ }
+ /*
+ A MERGE child of an intermediate table (e.g. ALTER TABLE) is opened
+ without LOCK_open. So don't safe_mutex_assert_owner(&LOCK_open).
+ */
if (!thd->open_tables)
thd->version=refresh_version;
@@ -928,21 +1031,32 @@ TABLE *open_table(THD *thd,const char *d
{
/* Someone did a refresh while thread was opening tables */
*refresh=1;
- VOID(pthread_mutex_unlock(&LOCK_open));
+ if (!is_child)
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_PRINT("info", ("Thread needs refresh"));
DBUG_RETURN(0);
}
/* close handler tables which are marked for flush */
mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE, TRUE);
+ /*
+ Find an unused table. But do not use a MERGE child.
+ While stepping through the cache, refresh "old" tables. But stop this
+ when an unused table is found.
+ */
for (table= (TABLE*) hash_first(&open_cache, (byte*) key, key_length,
&state);
- table && table->in_use ;
+ table && (table->in_use || table->mrg_parent);
table= (TABLE*) hash_next(&open_cache, (byte*) key, key_length,
&state))
{
- if (table->version != refresh_version)
+ /* Do not refresh MERGE children. They are refreshed with their parent. */
+ if ((table->version != refresh_version) && !table->mrg_parent)
{
+ DBUG_PRINT("info", ("Version mismatch, table: 0x%lx version: %lu "
+ "refresh_version: %lu", (long) table,
+ table->version, refresh_version));
if (! refresh)
{
/* Ignore flush for now, but force close after usage. */
@@ -956,9 +1070,13 @@ TABLE *open_table(THD *thd,const char *d
*/
close_old_data_files(thd,thd->open_tables,0,0);
if (table->in_use != thd)
+ {
wait_for_refresh(thd);
- else
- VOID(pthread_mutex_unlock(&LOCK_open));
+ if (is_child)
+ VOID(pthread_mutex_lock(&LOCK_open));
+ }
+ else if (!is_child)
+ VOID(pthread_mutex_unlock(&LOCK_open));
if (refresh)
*refresh=1;
DBUG_RETURN(0);
@@ -975,6 +1093,7 @@ TABLE *open_table(THD *thd,const char *d
table->prev->next=table->next; /* Remove from unused list */
table->next->prev=table->prev;
+ DBUG_PRINT("info", ("Unused table: 0x%lx", (long) table));
}
else
{
@@ -985,33 +1104,58 @@ TABLE *open_table(THD *thd,const char *d
/* make a new table */
if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
{
- VOID(pthread_mutex_unlock(&LOCK_open));
+ if (!is_child)
+ VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(NULL);
}
+ /* Initialize MERGE table related pointers. */
+ table->mrg_parent= NULL;
+ table->mrg_child= NULL;
if (open_unireg_entry(thd, table,db,table_name,alias) ||
!(table->table_cache_key=memdup_root(&table->mem_root,(char*) key,
key_length)))
{
table->next=table->prev=table;
free_cache_entry(table);
- VOID(pthread_mutex_unlock(&LOCK_open));
+ if (!is_child)
+ VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(NULL);
}
table->key_length=key_length;
table->version=refresh_version;
table->flush_version=flush_version;
- DBUG_PRINT("info", ("inserting table %p into the cache", table));
+ DBUG_PRINT("info", ("New table 0x%lx in cache", (long) table));
VOID(my_hash_insert(&open_cache,(byte*) table));
}
table->in_use=thd;
+
+ /* If the table has children, set their in_use too. */
+ for (TABLE *child= table->mrg_child; child; child= child->mrg_child)
+ {
+ /*
+ In case of new opened MERGE table, children are already set up
+ through open_unireg_entry().
+ */
+ DBUG_ASSERT(!child->in_use || (child->in_use == thd));
+ DBUG_ASSERT(child->mrg_parent == table);
+ child->in_use= thd;
+ }
check_unused(); // Debugging call
-
- VOID(pthread_mutex_unlock(&LOCK_open));
- if (refresh)
+
+ /* Do not add MERGE children to open_tables. */
+ if (is_child)
+ table->next= NULL;
+ else
{
- table->next=thd->open_tables; /* Link into simple list */
- thd->open_tables=table;
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ if (refresh)
+ {
+ table->next=thd->open_tables; /* Link into simple list */
+ thd->open_tables=table;
+ DBUG_PRINT("table", ("put into open_tables: '%s' 0x%lx",
+ table->table_name, (long) table));
+ }
}
table->reginfo.lock_type=TL_READ; /* Assume read */
@@ -1065,6 +1209,8 @@ TABLE *open_table(THD *thd,const char *d
table->timestamp_field_type= table->timestamp_field->get_auto_set_type();
DBUG_ASSERT(table->key_read == 0);
DBUG_ASSERT(table->insert_values == 0);
+ DBUG_PRINT("exit", ("Opened table: '%s' 0x%lx version: %lu",
+ table->table_name, (long) table, table->version));
DBUG_RETURN(table);
}
@@ -1120,6 +1266,9 @@ bool reopen_table(TABLE *table,bool lock
VOID(pthread_mutex_lock(&LOCK_open));
safe_mutex_assert_owner(&LOCK_open);
+ /* Initialize MERGE table related pointers. */
+ tmp.mrg_parent= NULL;
+ tmp.mrg_child= NULL;
if (open_unireg_entry(current_thd,&tmp,db,table_name,table->table_name))
goto end;
free_io_cache(table);
@@ -1158,8 +1307,13 @@ bool reopen_table(TABLE *table,bool lock
if (table->file)
VOID(closefrm(table)); // close file, free everything
+ /* Copy new TABLE structure to old TABLE structure. */
*table=tmp;
+ /* Fix table pointer in handler object. */
table->file->change_table_ptr(table);
+ /* Fix parent pointers in child tables. */
+ for (TABLE *child= table->mrg_child; child; child= child->mrg_child)
+ child->mrg_parent= table;
DBUG_ASSERT(table->table_name);
for (field=table->field ; *field ; field++)
@@ -1172,6 +1326,8 @@ bool reopen_table(TABLE *table,bool lock
for (part=0 ; part < table->key_info[key].usable_key_parts ; part++)
table->key_info[key].key_part[part].field->table= table;
}
+ DBUG_PRINT("exit", ("Reopened table: '%s' 0x%lx version: %lu",
+ table->table_name, (long) table, table->version));
VOID(pthread_cond_broadcast(&COND_refresh));
error=0;
@@ -1251,7 +1407,20 @@ bool reopen_tables(THD *thd,bool get_loc
*tables_ptr++= table; // need new lock on this
if (in_refresh)
{
- table->version=0;
+ /*
+ Formerly we set table->version=0; here. I do not know what the
+ sense was to clear the freshly assigned version number of a
+ freshly reopened table. I just can say that it made a crash
+ when reopening the same table twice in a thread. For example
+ as a MyISAM table and as a MERGE child table. The table that
+ was reopend as the second one closed the previously reopend
+ table because of its zero version. table->file became NULL
+ again. But there is no means to reopen an already reopened
+ table again. The code after reopen_tables() assumed that all
+ tables are open and happily called table->file->lock_count()...
+ Removing table->version=0; fixed the problem and does not seem
+ to have a negative impact.
+ */
table->locked_by_flush=0;
}
}
@@ -1314,6 +1483,11 @@ void close_old_data_files(THD *thd, TABL
}
+#define TABLE_IN_USE(_t_) ((_t_)->locked_by_flush || \
+ (_t_)->locked_by_name && wait_for_name_lock || \
+ (_t_)->db_stat && (_t_)->version <
refresh_version)
+
+
/*
Wait until all threads has closed the tables in the list
We have also to wait if there is thread that has a lock on this table even
@@ -1322,6 +1496,9 @@ void close_old_data_files(THD *thd, TABL
bool table_is_used(TABLE *table, bool wait_for_name_lock)
{
+ DBUG_ENTER("table_is_used");
+ DBUG_PRINT("table", ("table: '%s' 0x%lx wait_for_name_lock: %d",
+ table->table_name, (long) table, wait_for_name_lock));
do
{
HASH_SEARCH_STATE state;
@@ -1333,13 +1510,24 @@ bool table_is_used(TABLE *table, bool wa
search= (TABLE*) hash_next(&open_cache, (byte*) key,
key_length, &state))
{
- if (search->locked_by_flush ||
- search->locked_by_name && wait_for_name_lock ||
- search->db_stat && search->version < refresh_version)
- return 1; // Table is used
+ if (TABLE_IN_USE(search))
+ DBUG_RETURN(1);
+ /* For a MERGE table check its children too. */
+ for (TABLE *child= search->mrg_child; child; child= child->mrg_child)
+ {
+ if (TABLE_IN_USE(child))
+ DBUG_RETURN(1);
+ }
+ /* For a MERGE child, check its parent too. */
+ if (search->mrg_parent)
+ {
+ if (TABLE_IN_USE(search->mrg_parent))
+ DBUG_RETURN(1);
+ }
}
} while ((table=table->next));
- return 0;
+ DBUG_PRINT("table", ("no table is used"));
+ DBUG_RETURN(0);
}
@@ -1359,7 +1547,9 @@ bool wait_for_tables(THD *thd)
mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE, TRUE);
if (!table_is_used(thd->open_tables,1))
break;
+ DBUG_PRINT("pthreads", ("waiting for refresh"));
(void) pthread_cond_wait(&COND_refresh,&LOCK_open);
+ DBUG_PRINT("pthreads", ("awoke from refresh"));
}
if (thd->killed)
result= 1; // aborted
@@ -1618,7 +1808,7 @@ int open_tables(THD *thd, TABLE_LIST *st
!(tables->table= open_table(thd,
tables->db,
tables->real_name,
- tables->alias, &refresh)))
+ tables->alias, &refresh, 0)))
{
if (refresh) // Refresh in progress
{
@@ -1731,7 +1921,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST
thd->current_tablenr= 0;
while (!(table=open_table(thd,table_list->db,
table_list->real_name,table_list->alias,
- &refresh)) && refresh) ;
+ &refresh, 0)) && refresh) ;
if (table)
{
@@ -1924,16 +2114,46 @@ int lock_tables(THD *thd, TABLE_LIST *ta
/*
- Open a single table without table caching and don't set it in open_list
- Used by alter_table to open a temporary table and when creating
+ @brief Open a single table without table caching and don't set it in open_list
+
+ @detail Used by alter_table to open a intermediate table and when creating
a temporary table with CREATE TEMPORARY ...
+
+ Temporary table can be created anywhere in the file system. The path
+ name is unrelated to the internally used db+table_name. For example
+ CREATE TEMPORARY TABLE db1.tt1 ... will create table files like
+ TMPDIR/#sql-1234-56.* but internally the table has the name 'tt1' and
+ belongs to the database 'db1'.
+
+ Intermediate tables as used by ALTER TABLE have also a "temporary" name
+ like #sql-1234-56, but are located in the same directory as the related
+ permanent table. They are normal tables in every aspect but the name.
+
+ For a MERGE table it is important to know if the new table is a real
+ temporary table. The path names of their temporary children cannot be
+ used to produce names for open_table(). In that case the child tables
+ are not opened through the table cache.
+
+ @param[in] thd thread handle
+ @param[in] path path name for table files (external use)
+ @param[in] db database for internal use
+ @param[in] table_name table name for internal use
+ @param[in] link_in_list if to link in thd->temporary_tables
+ @param[in] is_temporary if it is a temporary table, not an
+ intermediate table as used by ALTER TABLE.
+
+ @return pointer to opened TABLE object
+ @retval NULL on error
*/
TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
- const char *table_name, bool link_in_list)
+ const char *table_name, bool link_in_list,
+ bool is_temporary)
{
TABLE *tmp_table;
DBUG_ENTER("open_temporary_table");
+ DBUG_PRINT("enter", ("path: '%s' db: '%s' name: '%s' link: %d temp: %d",
+ path, db, table_name, link_in_list, is_temporary));
/*
The extra size in my_malloc() is for table_cache_key
@@ -1948,7 +2168,8 @@ TABLE *open_temporary_table(THD *thd, co
DBUG_RETURN(0); /* purecov: inspected */
if (openfrm(path, table_name,
- (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX),
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX |
+ (is_temporary ? HA_OPEN_TEMPORARY : 0)),
READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
ha_open_options,
tmp_table))
@@ -1980,6 +2201,8 @@ TABLE *open_temporary_table(THD *thd, co
if (thd->slave_thread)
slave_open_temp_tables++;
}
+ DBUG_PRINT("info", ("opened temporary table: '%s' 0x%lx",
+ tmp_table->table_name, (long) tmp_table));
DBUG_RETURN(tmp_table);
}
@@ -3064,7 +3287,12 @@ void remove_db_from_cache(const char *db
if (!strcmp(table->table_cache_key,db))
{
table->version=0L; /* Free when thread is ready */
- if (!table->in_use)
+ /*
+ If table is unused, move it first in unused link.
+ Do not do that for MERGE children. They are not in the unused
+ chain. They will be deleted with their parent.
+ */
+ if (!table->in_use && !table->mrg_parent)
relink_unused(table);
}
}
@@ -3109,7 +3337,8 @@ bool remove_table_from_cache(THD *thd, c
TABLE *table;
bool result=0, signalled= 0;
DBUG_ENTER("remove_table_from_cache");
-
+ DBUG_PRINT("enter", ("db: '%s' table_name: '%s' flags: %u",
+ db, table_name, flags));
key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
for (;;)
@@ -3117,6 +3346,7 @@ bool remove_table_from_cache(THD *thd, c
HASH_SEARCH_STATE state;
result= signalled= 0;
+ /* Remove all TABLE objects for this table from cache. */
for (table= (TABLE*) hash_first(&open_cache, (byte*) key, key_length,
&state);
table;
@@ -3124,6 +3354,14 @@ bool remove_table_from_cache(THD *thd, c
&state))
{
THD *in_use;
+
+ /*
+ If a MERGE child is found, switch to the parent. This removes
+ the parent and all of its children, including the found one.
+ */
+ if (table->mrg_parent)
+ table= table->mrg_parent;
+
table->version=0L; /* Free when thread is ready */
if (!(in_use=table->in_use))
{
@@ -3132,6 +3370,8 @@ bool remove_table_from_cache(THD *thd, c
}
else if (in_use != thd)
{
+ DBUG_PRINT("info", ("Table in use: 0x%lx by thread: %ld",
+ (long) table, in_use->dbug_thread_id));
in_use->some_tables_deleted=1;
if (table->db_stat)
result=1;
@@ -3158,12 +3398,22 @@ bool remove_table_from_cache(THD *thd, c
thd_table ;
thd_table= thd_table->next)
{
+ /*
+ For a MERGE child abort the locks of the parent and thus
+ implicitly all children, including this one.
+ */
if (thd_table->db_stat) // If table is open
- signalled|= mysql_lock_abort_for_thread(thd, thd_table);
+ signalled|= mysql_lock_abort_for_thread(thd,
+ thd_table->mrg_parent ?
+ thd_table->mrg_parent :
+ thd_table);
}
}
else
+ {
result= result || (flags & RTFC_OWNED_BY_THD_FLAG);
+ DBUG_PRINT("info", ("Table in use: 0x%lx by self", (long) table));
+ }
}
while (unused_tables && !unused_tables->version)
VOID(hash_delete(&open_cache,(byte*) unused_tables));
@@ -3173,7 +3423,11 @@ bool remove_table_from_cache(THD *thd, c
{
dropping_tables++;
if (likely(signalled))
+ {
+ DBUG_PRINT("pthreads", ("waiting for refresh"));
(void) pthread_cond_wait(&COND_refresh, &LOCK_open);
+ DBUG_PRINT("pthreads", ("awoke from refresh"));
+ }
else
{
struct timespec abstime;
@@ -3188,7 +3442,9 @@ bool remove_table_from_cache(THD *thd, c
remove_table_from_cache routine.
*/
set_timespec(abstime, 10);
+ DBUG_PRINT("pthreads", ("waiting for refresh 10 msec"));
pthread_cond_timedwait(&COND_refresh, &LOCK_open, &abstime);
+ DBUG_PRINT("pthreads", ("awoke from refresh"));
}
dropping_tables--;
continue;
--- 1.143/sql/sql_delete.cc 2007-04-28 14:31:59 +02:00
+++ 1.144/sql/sql_delete.cc 2007-04-28 14:31:59 +02:00
@@ -661,7 +661,7 @@ int mysql_truncate(THD *thd, TABLE_LIST
ha_create_table(path, &create_info,1);
// We don't need to call invalidate() because this table is not in cache
if ((error= (int) !(open_temporary_table(thd, path, table_list->db,
- table_list->real_name, 1))))
+ table_list->real_name, 1, 1))))
(void) rm_temporary_table(table_type, path);
/*
If we return here we will not have logged the truncation to the bin log
--- 1.312/sql/sql_table.cc 2007-04-28 14:31:59 +02:00
+++ 1.313/sql/sql_table.cc 2007-04-28 14:31:59 +02:00
@@ -1514,7 +1514,7 @@ int mysql_create_table(THD *thd,const ch
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
{
/* Open table and put in temporary table list */
- if (!(open_temporary_table(thd, path, db, table_name, 1)))
+ if (!(open_temporary_table(thd, path, db, table_name, 1, 1)))
{
(void) rm_temporary_table(create_info->db_type, path);
goto end;
@@ -1648,7 +1648,7 @@ TABLE *create_table_from_items(THD *thd,
if (!mysql_create_table(thd, db, name, create_info, alter_info,
0, select_field_count))
{
- if (!(table=open_table(thd,db,name,name,(bool*) 0)))
+ if (!(table= open_table(thd, db, name, name, (bool*) 0, 0)))
quick_rm_table(create_info->db_type,db,table_case_name(create_info,name));
}
reenable_binlog(thd);
@@ -1780,6 +1780,7 @@ static void wait_while_table_is_used(THD
static bool close_cached_table(THD *thd, TABLE *table)
{
DBUG_ENTER("close_cached_table");
+ DBUG_PRINT("enter", ("table: '%s'", table->table_name));
wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
/* Close lock if this is not got with LOCK TABLES */
@@ -2490,7 +2491,7 @@ int mysql_create_like_table(THD* thd, TA
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
{
- if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
+ if (err || !open_temporary_table(thd, dst_path, db, table_name, 1, 1))
{
(void) rm_temporary_table(create_info->db_type,
dst_path); /* purecov: inspected */
@@ -3223,12 +3224,12 @@ int mysql_alter_table(THD *thd,char *new
DBUG_RETURN(error);
}
if (table->tmp_table)
- new_table=open_table(thd,new_db,tmp_name,tmp_name,0);
+ new_table= open_table(thd, new_db, tmp_name, tmp_name, 0, 0);
else
{
char path[FN_REFLEN];
build_table_path(path, sizeof(path), new_db, tmp_name, "");
- new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
+ new_table=open_temporary_table(thd, path, new_db, tmp_name, 0, 0);
}
if (!new_table)
{
@@ -3296,9 +3297,13 @@ int mysql_alter_table(THD *thd,char *new
goto end_temporary;
}
+ /*
+ If old table was MERGE, we may need to close child tables. This
+ needs LOCK_open.
+ */
+ VOID(pthread_mutex_lock(&LOCK_open));
intern_close_table(new_table); /* close temporary table */
my_free((gptr) new_table,MYF(0));
- VOID(pthread_mutex_lock(&LOCK_open));
if (error)
{
VOID(quick_rm_table(new_db_type,new_db,tmp_name));
@@ -3442,7 +3447,8 @@ int mysql_alter_table(THD *thd,char *new
*/
char path[FN_REFLEN];
build_table_path(path, sizeof(path), new_db, table_name, "");
- table=open_temporary_table(thd, path, new_db, tmp_name,0);
+ table=open_temporary_table(thd, path, new_db, tmp_name, 0,
+ create_info->options & HA_LEX_CREATE_TMP_TABLE);
if (table)
{
intern_close_table(table);
--- 1.76/sql/table.h 2007-04-28 14:31:59 +02:00
+++ 1.77/sql/table.h 2007-04-28 14:31:59 +02:00
@@ -208,6 +208,8 @@ struct st_table {
uint derived_select_number;
THD *in_use; /* Which thread uses this */
struct st_table *next,*prev;
+ struct st_table *mrg_parent; /* Parent MERGE table. */
+ struct st_table *mrg_child; /* List of MERGE child tables. */
};
--- 1.46/mysql-test/r/merge.result 2007-04-28 14:31:59 +02:00
+++ 1.47/mysql-test/r/merge.result 2007-04-28 14:31:59 +02:00
@@ -779,7 +779,7 @@ ERROR HY000: Unable to open underlying t
DROP TABLE t1, t2;
CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t3);
SELECT * FROM t2;
-ERROR HY000: Unable to open underlying table which is differently defined or of
non-MyISAM type or doesn't exist
+ERROR 42S02: Table 'test.t3' doesn't exist
DROP TABLE t2;
CREATE TABLE t1(a INT, b TEXT);
CREATE TABLE tm1(a TEXT, b INT) ENGINE=MERGE UNION=(t1);
@@ -819,3 +819,20 @@ ALTER TABLE m1 ENGINE=MERGE UNION=(t1);
SELECT * FROM m1;
c1 c2 c3 c4 c5 c6 c7 c8 c9
DROP TABLE t1, m1;
+create table t1 (c1 int, index(c1));
+create table t2 (c1 int, index(c1)) engine=merge union=(t1);
+insert into t1 values (1);
+flush tables;
+select * from t2;
+c1
+1
+flush tables;
+truncate table t1;
+insert into t1 values (1);
+flush tables;
+select * from t2;
+c1
+1
+truncate table t1;
+insert into t1 values (1);
+drop table t1,t2;
--- 1.43/mysql-test/t/merge.test 2007-04-28 14:31:59 +02:00
+++ 1.44/mysql-test/t/merge.test 2007-04-28 14:31:59 +02:00
@@ -397,7 +397,7 @@ CREATE TABLE t2(a INT) ENGINE=MERGE UNIO
SELECT * FROM t2;
DROP TABLE t1, t2;
CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t3);
---error 1168
+--error 1146
SELECT * FROM t2;
DROP TABLE t2;
@@ -453,5 +453,33 @@ CREATE TABLE m1 LIKE t1;
ALTER TABLE m1 ENGINE=MERGE UNION=(t1);
SELECT * FROM m1;
DROP TABLE t1, m1;
+
+#
+# Bug #8306: TRUNCATE leads to index corruption
+# Obsoleted by fix for Bug#26379 (Combination of FLUSH TABLE and
+# REPAIR TABLE corrupts a MERGE table).
+#
+create table t1 (c1 int, index(c1));
+create table t2 (c1 int, index(c1)) engine=merge union=(t1);
+insert into t1 values (1);
+# Close all tables.
+flush tables;
+# Open t2 and (implicitly) t1.
+select * from t2;
+# Truncate after flush works (unless another threads reopens t2 in between).
+flush tables;
+truncate table t1;
+insert into t1 values (1);
+# Close all tables.
+flush tables;
+# Open t2 and (implicitly) t1.
+select * from t2;
+# Truncate t1, wich was not recognized as open without the bugfix.
+# After fix for Bug#8306 and before fix for Bug#26379,
+# it should fail with a table-in-use error message, otherwise succeed.
+truncate table t1;
+# The insert used to fail on the crashed table.
+insert into t1 values (1);
+drop table t1,t2;
# End of 4.1 tests
--- 1.73/mysql-test/r/myisam.result 2007-04-28 14:31:59 +02:00
+++ 1.74/mysql-test/r/myisam.result 2007-04-28 14:31:59 +02:00
@@ -593,24 +593,6 @@ select count(*) from t1 where a is null;
count(*)
2
drop table t1;
-create table t1 (c1 int, index(c1));
-create table t2 (c1 int, index(c1)) engine=merge union=(t1);
-insert into t1 values (1);
-flush tables;
-select * from t2;
-c1
-1
-flush tables;
-truncate table t1;
-insert into t1 values (1);
-flush tables;
-select * from t2;
-c1
-1
-truncate table t1;
-ERROR HY000: MyISAM table 't1' is in use (most likely by a MERGE table). Try FLUSH
TABLES.
-insert into t1 values (1);
-drop table t1,t2;
create table t1 (c1 int, c2 varchar(4) not null default '',
key(c2(3))) default charset=utf8;
insert into t1 values (1,'A'), (2, 'B'), (3, 'A');
--- 1.59/mysql-test/t/myisam.test 2007-04-28 14:31:59 +02:00
+++ 1.60/mysql-test/t/myisam.test 2007-04-28 14:31:59 +02:00
@@ -573,32 +573,6 @@ select count(*) from t1 where a is null;
drop table t1;
#
-# Bug #8306: TRUNCATE leads to index corruption
-#
-create table t1 (c1 int, index(c1));
-create table t2 (c1 int, index(c1)) engine=merge union=(t1);
-insert into t1 values (1);
-# Close all tables.
-flush tables;
-# Open t2 and (implicitly) t1.
-select * from t2;
-# Truncate after flush works (unless another threads reopens t2 in between).
-flush tables;
-truncate table t1;
-insert into t1 values (1);
-# Close all tables.
-flush tables;
-# Open t2 and (implicitly) t1.
-select * from t2;
-# Truncate t1, wich was not recognized as open without the bugfix.
-# Now, it should fail with a table-in-use error message.
---error 1105
-truncate table t1;
-# The insert used to fail on the crashed table.
-insert into t1 values (1);
-drop table t1,t2;
-
-#
# bug9188 - Corruption Can't open file: 'table.MYI' (errno: 145)
#
create table t1 (c1 int, c2 varchar(4) not null default '',
| Thread |
|---|
| • bk commit into 4.1 tree (istruewing:1.2630) BUG#26379 | ingo | 28 Apr |