summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorMichael Widenius <monty@askmonty.org>2010-11-02 17:22:57 +0200
committerMichael Widenius <monty@askmonty.org>2010-11-02 17:22:57 +0200
commit20acfbf30da2eca66f9e5d602d50ac18e38272b8 (patch)
treefb1ae595de49f155045afa021b4037b07305096c /sql
parentc6b19ea001965b350df1248c33f709127d2c7e47 (diff)
downloadmariadb-git-20acfbf30da2eca66f9e5d602d50ac18e38272b8.tar.gz
Fix for: LP #634955: Assert in _ma_update_at_original_place()
Added locking of lock mutex when updating status in external_unlock() for Aria and MyISAM tables. Fixed that 'source' command doesn't cause mysql command line tool to exit on error. DEBUG_EXECUTE() and DEBUG_EVALUATE_IF() should not execute things based on wildcards. (Allows one to run --debug with mysql-test-run scripts that uses @debug) Fixed several core dump, deadlock and crashed table bugs in handling of LOCK TABLE with MERGE tables: - Added priority of locks to avoid crashes with MERGE tables. - Added thr_lock_merge() to allow one to merge two results of thr_lock(). Fixed 'not found row' bug in REPLACE with Aria tables. Mark MyISAM tables that are part of MERGE with HA_OPEN_MERGE_TABLE and set the locks to have priority THR_LOCK_MERGE_PRIV. - By sorting MERGE tables last in thr_multi_unlock() it's safer to release and relock them many times (can happen when TRIGGERS are created) Avoid printing (null) in debug file (to easier find out wrong NULL pointer usage with %s). client/mysql.cc: Fixed that 'source' command doesn't cause mysql command line tool to exit on error. client/mysqltest.cc: Don't send NULL to fn_format(). (Can cause crash on Solaris when using --debug) dbug/dbug.c: DEBUG_EXECUTE() and DEBUG_EVALUATE_IF() should not execute things based on wildcards. include/my_base.h: Added flag to signal if one opens a MERGE table. Added extra() command to signal that one is not part of a MERGE table anymore. include/thr_lock.h: Added priority for locks (needed to fix bug in thr_lock when using MERGE tables) Added option to thr_unlock() if get_status() should be called. Added prototype for thr_merge_locks(). mysql-test/mysql-test-run.pl: Ignore crashed table warnings for tables named 'crashed'. mysql-test/r/merge.result: Renamed triggers to make debugging easier. Added some CHECK TABLES to catch errors earlier. Additional tests. mysql-test/r/merge_debug.result: Test of error handling when reopening MERGE tables. mysql-test/r/udf_query_cache.result: Added missing flush status mysql-test/suite/parts/r/partition_repair_myisam.result: Update results mysql-test/t/merge.test: Renamed triggers to make debugging easier. Added some CHECK TABLES to catch errors earlier. Additional tests. mysql-test/t/merge_debug.test: Test of error handling when reopening MERGE tables. mysql-test/t/udf_query_cache.test: Added missing flush status mysys/my_getopt.c: Removed not used variable mysys/my_symlink2.c: Changed (null) to (NULL) to make it easier to find NULL arguments to DBUG_PRINT() functions. (On linux, NULL to sprintf is printed 'null') mysys/thr_lock.c: Added priority of locks to avoid crashes with MERGE tables. Added thr_lock_merge() to allow one to merge two results of thr_lock(). - This is needed for MyISAM as all locked table must share the same status. If not, you will not see newly inserted rows in other instances of the table. If calling thr_unlock() with THR_UNLOCK_UPDATE_STATUS, call update_status() and restore_status() for the locks. This is needed in some rare cases where we call thr_unlock() followed by thr_lock() without calling external_unlock/external_lock in between. Simplify loop in thr_multi_lock(). Added 'start_trans', which is called at end of thr_multi_lock() when all locks are taken. - This was needed by Aria to ensure that transaction is started when we got all locks, not at get_status(). Without this, some rows could not be visible when we lock two tables at the same time, causing REPLACE using two tables to fail unexpectedly. sql/handler.cc: Add an assert() in handler::print_error() for "impossible errors" (like table is crashed) when --debug-assert-if-crashed-table is used. sql/lock.cc: Simplify mysql_lock_tables() code if get_lock_data() returns 0 locks. Added new parameter to thr_multi_unlock() In mysql_unlock_read_tables(), call first externa_unlock(), then thr_multi_unlock(); This is same order as we do in mysql_unlock_tables(). Don't abort locks in mysql_lock_abort() for merged tables when a MERGE table is deleted; Would cause a spin lock. Added call to thr_merge_locks() in mysql_lock_merge() to ensure consistency in thr_locks(). - New locks of same type and table is stored after the old lock to ensure that we get the status from the original lock. sql/mysql_priv.h: Added debug_assert_if_crashed_table sql/mysqld.cc: Added --debug-assert-if-crashed-table sql/parse_file.cc: Don't print '(null)' in DBUG_PRINT of no dir given sql/set_var.cc: Increase default size of buffer for @debug variable. sql/sql_base.cc: In case of error from reopen_table() in reopen_tables(), call unlock_open_table() and restart loop. - This fixed bug when we twice deleted same table from open_cache. Don't take name lock for already name locked table in open_unireg_entry(). - Fixed bug when doing repair in reopen_table(). - In detach_merge_children(), always detach if 'clear_refs' is given. We can't trust parent->children_attached as this function can be called twice, first time with clear_refs set to 0. sql/sql_class.cc: Changed printing of (null) to "" in set_thd_proc_info() sql/sql_parse.cc: Added DBUG sql/sql_trigger.cc: Don't call unlink_open_table() if reopen_table() fails as the table may already be freed. storage/maria/ma_bitmap.c: Fixed DBUG_ASSERT() in allocate_tail() storage/maria/ma_blockrec.c: Fixed wrong calculation of row length for very small rows in undo_row_update(). - Fixes ASSERT() when doing undo. storage/maria/ma_blockrec.h: Added _ma_block_start_trans() and _ma_block_start_trans_no_versioning() storage/maria/ma_locking.c: Call _ma_update_status_with_lock() when releasing write locks. - Fixes potential problem with updating status without the proper lock. storage/maria/ma_open.c: Changed to use start_trans() instead of get_status() to ensure that we see all rows in all locked tables when we got the locks. - Fixed 'not found row' bug in REPLACE with Aria tables. storage/maria/ma_state.c: Added _ma_update_status_with_lock() and _ma_block_start_trans(). This is to ensure that we see all rows in all locked tables when we got the locks. storage/maria/ma_state.h: Added _ma_update_status_with_lock() storage/maria/ma_write.c: More DBUG_PRINT storage/myisam/mi_check.c: Fixed error message storage/myisam/mi_extra.c: Added HA_EXTRA_DETACH_CHILD: - Detach MyISAM table to not be part of MERGE table (remove flag & lock priority). storage/myisam/mi_locking.c: Call mi_update_status_with_lock() when releasing write locks. - Fixes potential problem with updating status without the proper lock. Change to use new HA_OPEN_MERGE_TABLE flag to test if MERGE table. Added mi_fix_status(), called by thr_merge(). storage/myisam/mi_open.c: Added marker if part of MERGE table. Call mi_fix_status() in thr_lock() for transactional tables. storage/myisam/myisamdef.h: Change my_once_flag to uint, as it stored different values than just 0/1 Added 'open_flag' to store state given to mi_open() storage/myisammrg/ha_myisammrg.cc: Add THR_LOCK_MERGE_PRIV to THR_LOCK_DATA to get MERGE locks sorted after other types of locks. storage/myisammrg/myrg_locking.c: Remove windows specific code. storage/myisammrg/myrg_open.c: Use HA_OPEN_MERGE_TABLE to mi_open(). Set HA_OPEN_MERGE_TABLE for linked MyISAM tables. storage/xtradb/buf/buf0buf.c: Fixed compiler warning storage/xtradb/buf/buf0lru.c: Initialize variable that could be used not initialized.
Diffstat (limited to 'sql')
-rw-r--r--sql/handler.cc26
-rw-r--r--sql/lock.cc103
-rw-r--r--sql/mysql_priv.h2
-rw-r--r--sql/mysqld.cc8
-rw-r--r--sql/parse_file.cc2
-rw-r--r--sql/set_var.cc2
-rw-r--r--sql/sql_base.cc91
-rw-r--r--sql/sql_class.cc2
-rw-r--r--sql/sql_parse.cc4
-rw-r--r--sql/sql_trigger.cc3
10 files changed, 170 insertions, 73 deletions
diff --git a/sql/handler.cc b/sql/handler.cc
index a5b74e65328..c331a3bb855 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -2631,8 +2631,18 @@ void handler::print_keydup_error(uint key_nr, const char *msg)
- table->s->path
- table->alias
*/
+
+#ifndef DBUG_OFF
+#define SET_FATAL_ERROR fatal_error=1
+#else
+#define SET_FATAL_ERROR
+#endif
+
void handler::print_error(int error, myf errflag)
{
+#ifndef DBUG_OFF
+ bool fatal_error= 0;
+#endif
DBUG_ENTER("handler::print_error");
DBUG_PRINT("enter",("error: %d",error));
@@ -2650,6 +2660,13 @@ void handler::print_error(int error, myf errflag)
case HA_ERR_KEY_NOT_FOUND:
case HA_ERR_NO_ACTIVE_RECORD:
case HA_ERR_END_OF_FILE:
+ /*
+ This errors is not not normally fatal (for example for reads). However
+ if you get it during an update or delete, then its fatal.
+ As the user is calling print_error() (which is not done on read), we
+ assume something when wrong with the update or delete.
+ */
+ SET_FATAL_ERROR;
textno=ER_KEY_NOT_FOUND;
break;
case HA_ERR_WRONG_MRG_TABLE_DEF:
@@ -2701,21 +2718,26 @@ void handler::print_error(int error, myf errflag)
textno=ER_DUP_UNIQUE;
break;
case HA_ERR_RECORD_CHANGED:
+ SET_FATAL_ERROR;
textno=ER_CHECKREAD;
break;
case HA_ERR_CRASHED:
+ SET_FATAL_ERROR;
textno=ER_NOT_KEYFILE;
break;
case HA_ERR_WRONG_IN_RECORD:
+ SET_FATAL_ERROR;
textno= ER_CRASHED_ON_USAGE;
break;
case HA_ERR_CRASHED_ON_USAGE:
+ SET_FATAL_ERROR;
textno=ER_CRASHED_ON_USAGE;
break;
case HA_ERR_NOT_A_TABLE:
textno= error;
break;
case HA_ERR_CRASHED_ON_REPAIR:
+ SET_FATAL_ERROR;
textno=ER_CRASHED_ON_REPAIR;
break;
case HA_ERR_OUT_OF_MEM:
@@ -2814,7 +2836,10 @@ void handler::print_error(int error, myf errflag)
if (temporary)
my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(), engine);
else
+ {
+ SET_FATAL_ERROR;
my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine);
+ }
}
else
my_error(ER_GET_ERRNO,errflag,error);
@@ -2822,6 +2847,7 @@ void handler::print_error(int error, myf errflag)
}
}
my_error(textno, errflag, table_share->table_name.str, error);
+ DBUG_ASSERT(!fatal_error || !debug_assert_if_crashed_table);
DBUG_VOID_RETURN;
}
diff --git a/sql/lock.cc b/sql/lock.cc
index 1908e68dbd2..566275c5ea2 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -211,7 +211,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
for (;;)
{
if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
- &write_lock_used)))
+ &write_lock_used)) ||
+ ! sql_lock->table_count)
break;
if (global_read_lock && write_lock_used &&
@@ -257,8 +258,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
thd_proc_info(thd, "System lock");
DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
- if (sql_lock->table_count && lock_external(thd, sql_lock->table,
- sql_lock->table_count))
+ if (lock_external(thd, sql_lock->table, sql_lock->table_count))
{
/* Clear the lock type of all lock data to avoid reusage. */
reset_lock_data(sql_lock);
@@ -279,8 +279,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
thd->lock_id)];
if (rc > 1) /* a timeout or a deadlock */
{
- if (sql_lock->table_count)
- VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count));
+ VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count));
my_error(rc, MYF(0));
my_free((uchar*) sql_lock,MYF(0));
sql_lock= 0;
@@ -388,7 +387,7 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
if (sql_lock->table_count)
VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
if (sql_lock->lock_count)
- thr_multi_unlock(sql_lock->locks,sql_lock->lock_count);
+ thr_multi_unlock(sql_lock->locks,sql_lock->lock_count, 0);
my_free((uchar*) sql_lock,MYF(0));
DBUG_VOID_RETURN;
}
@@ -418,25 +417,8 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
uint i,found;
DBUG_ENTER("mysql_unlock_read_tables");
- /* Move all write locks first */
- THR_LOCK_DATA **lock=sql_lock->locks;
- for (i=found=0 ; i < sql_lock->lock_count ; i++)
- {
- if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
- {
- swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]);
- lock++;
- found++;
- }
- }
- /* unlock the read locked tables */
- if (i != found)
- {
- thr_multi_unlock(lock,i-found);
- sql_lock->lock_count= found;
- }
+ /* Call external lock for all tables to be unlocked */
- /* Then do the same for the external locks */
/* Move all write locked tables first */
TABLE **table=sql_lock->table;
for (i=found=0 ; i < sql_lock->table_count ; i++)
@@ -455,6 +437,27 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
VOID(unlock_external(thd,table,i-found));
sql_lock->table_count=found;
}
+
+ /* Call thr_unlock() for all tables to be unlocked */
+
+ /* Move all write locks first */
+ THR_LOCK_DATA **lock=sql_lock->locks;
+ for (i=found=0 ; i < sql_lock->lock_count ; i++)
+ {
+ if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
+ {
+ swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]);
+ lock++;
+ found++;
+ }
+ }
+ /* unlock the read locked tables */
+ if (i != found)
+ {
+ thr_multi_unlock(lock, i-found, 0);
+ sql_lock->lock_count= found;
+ }
+
/* Fix the lock positions in TABLE */
table= sql_lock->table;
found= 0;
@@ -582,8 +585,21 @@ void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock)
if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
&write_lock_used)))
{
- for (uint i=0; i < locked->lock_count; i++)
- thr_abort_locks(locked->locks[i]->lock, upgrade_lock);
+ if (table->children_attached)
+ {
+ /*
+ Don't abort locks for underlying tables just because merge table
+ is deleted. Doing would cause anyone accessing these tables to
+ spin in open_table/close_table forever until lock is released.
+ */
+ thr_multi_unlock(locked->locks, locked->lock_count,
+ THR_UNLOCK_UPDATE_STATUS);
+ }
+ else
+ {
+ for (uint i=0; i < locked->lock_count; i++)
+ thr_abort_locks(locked->locks[i]->lock, upgrade_lock);
+ }
my_free((uchar*) locked,MYF(0));
}
DBUG_VOID_RETURN;
@@ -624,21 +640,36 @@ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table)
}
+/**
+ Merge two thr_lock:s
+ mysql_lock_merge()
+
+ @param a Original locks
+ @param b New locks
+
+ @retval New lock structure that contains a and b
+
+ @note
+ a and b are freed with my_free()
+*/
+
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
{
MYSQL_LOCK *sql_lock;
TABLE **table, **end_table;
DBUG_ENTER("mysql_lock_merge");
+ DBUG_PRINT("enter", ("a->lock_count: %u b->lock_count: %u",
+ a->lock_count, b->lock_count));
if (!(sql_lock= (MYSQL_LOCK*)
my_malloc(sizeof(*sql_lock)+
- sizeof(THR_LOCK_DATA*)*(a->lock_count+b->lock_count)+
+ sizeof(THR_LOCK_DATA*)*((a->lock_count+b->lock_count)*2) +
sizeof(TABLE*)*(a->table_count+b->table_count),MYF(MY_WME))))
DBUG_RETURN(0); // Fatal error
sql_lock->lock_count=a->lock_count+b->lock_count;
sql_lock->table_count=a->table_count+b->table_count;
sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
- sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count);
+ sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count*2);
memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks));
memcpy(sql_lock->locks+a->lock_count,b->locks,
b->lock_count*sizeof(*b->locks));
@@ -659,6 +690,18 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
(*table)->lock_data_start+= a->lock_count;
}
+ /*
+ Ensure that locks of the same tables share same data structures if we
+ reopen a table that is already open. This can happen for example with
+ MERGE tables.
+ */
+
+ /* Copy the lock data array. thr_merge_lock() reorders its content */
+ memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
+ sql_lock->lock_count * sizeof(*sql_lock->locks));
+ thr_merge_locks(sql_lock->locks + sql_lock->lock_count,
+ a->lock_count, b->lock_count);
+
/* Delete old, not needed locks */
my_free((uchar*) a,MYF(0));
my_free((uchar*) b,MYF(0));
@@ -832,7 +875,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
/*
Allocating twice the number of pointers for lock data for use in
- thr_mulit_lock(). This function reorders the lock data, but cannot
+ thr_multi_lock(). This function reorders the lock data, but cannot
update the table values. So the second part of the array is copied
from the first part immediately before calling thr_multi_lock().
*/
@@ -1062,11 +1105,13 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
void unlock_table_name(THD *thd, TABLE_LIST *table_list)
{
+ DBUG_ENTER("unlock_table_name");
if (table_list->table)
{
hash_delete(&open_cache, (uchar*) table_list->table);
broadcast_refresh();
}
+ DBUG_VOID_RETURN;
}
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index efb92108781..8b2cba4f9e6 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1989,7 +1989,7 @@ extern my_bool opt_slave_compressed_protocol, use_temp_pool;
extern ulong slave_exec_mode_options;
extern my_bool opt_readonly, lower_case_file_system;
extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
-extern my_bool opt_secure_auth;
+extern my_bool opt_secure_auth, debug_assert_if_crashed_table;
extern char* opt_secure_file_priv;
extern my_bool opt_log_slow_admin_statements, opt_log_slow_slave_statements;
extern my_bool sp_automatic_privileges, opt_noacl;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 97b405b6354..9e7b88dc170 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -455,7 +455,7 @@ static pthread_cond_t COND_thread_cache, COND_flush_thread_cache;
/* Global variables */
bool opt_update_log, opt_bin_log, opt_ignore_builtin_innodb= 0;
-my_bool opt_log, opt_slow_log;
+my_bool opt_log, opt_slow_log, debug_assert_if_crashed_table;
ulong log_output_options;
my_bool opt_log_queries_not_using_indexes= 0;
bool opt_error_log= IF_WIN(1,0);
@@ -5962,7 +5962,7 @@ enum options_mysqld
OPT_SECURE_FILE_PRIV,
OPT_MIN_EXAMINED_ROW_LIMIT,
OPT_LOG_SLOW_SLAVE_STATEMENTS,
- OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_OLD_MODE,
+ OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_DEBUG_ASSERT_IF_CRASHED_TABLE, OPT_OLD_MODE,
OPT_TEST_IGNORE_WRONG_OPTIONS, OPT_TEST_RESTART,
#if defined(ENABLED_DEBUG_SYNC)
OPT_DEBUG_SYNC_TIMEOUT,
@@ -6131,6 +6131,10 @@ struct my_option my_long_options[] =
0, GET_ULONG, REQUIRED_ARG, 0, 0, ~(ulong) 0L, 0, 0, 0},
{"debug-flush", OPT_DEBUG_FLUSH, "Default debug log with flush after write",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"debug-assert-if-crashed-table", OPT_DEBUG_ASSERT_IF_CRASHED_TABLE,
+ "Do an assert in handler::print_error() if we get a crashed table",
+ &debug_assert_if_crashed_table, &debug_assert_if_crashed_table,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
#endif
{"default-character-set", OPT_DEFAULT_CHARACTER_SET_OLD,
"Set the default character set (deprecated option, use --character-set-server instead).",
diff --git a/sql/parse_file.cc b/sql/parse_file.cc
index 3d65fa1de31..5bc16e55ec0 100644
--- a/sql/parse_file.cc
+++ b/sql/parse_file.cc
@@ -216,7 +216,7 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
File_option *param;
DBUG_ENTER("sql_create_definition_file");
DBUG_PRINT("enter", ("Dir: %s, file: %s, base 0x%lx",
- dir ? dir->str : "(null)",
+ dir ? dir->str : "",
file_name->str, (ulong) base));
if (dir)
diff --git a/sql/set_var.cc b/sql/set_var.cc
index c4e41bbf261..9e5cf7ab7dd 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -4341,7 +4341,7 @@ bool sys_var_thd_dbug::update(THD *thd, set_var *var)
uchar *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b)
{
- char buf[256];
+ char buf[1024];
if (type == OPT_GLOBAL)
{
DBUG_EXPLAIN_INITIAL(buf, sizeof(buf));
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 33ea834247e..708b4d6126b 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -2032,6 +2032,8 @@ static void unlink_open_merge(THD *thd, TABLE *table, TABLE ***prev_pp)
Remove parent from open_tables list and close it.
This includes detaching and hence clearing parent references.
*/
+ DBUG_PRINT("info", ("Closing parent to '%s'.'%s'",
+ table->s->db.str, table->s->table_name.str));
close_thread_table(thd, prv_p);
}
}
@@ -3061,8 +3063,9 @@ bool reopen_table(TABLE *table)
TABLE_LIST table_list;
THD *thd= table->in_use;
DBUG_ENTER("reopen_table");
- DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
- table->s->table_name.str, (long) table));
+ DBUG_PRINT("tcache", ("table: '%s'.'%s' table: 0x%lx share: 0x%lx",
+ table->s->db.str, table->s->table_name.str,
+ (long) table, (long) table->s));
DBUG_ASSERT(table->s->ref_count == 0);
DBUG_ASSERT(!table->sort.io_cache);
@@ -3349,7 +3352,8 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old)
merge_table_found= TRUE;
if (!tables || (!db_stat && reopen_table(table)))
{
- my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
+ my_error(ER_CANT_REOPEN_TABLE, MYF(0),
+ table->alias ? table->alias : table->s->table_name.str);
/*
If we could not allocate 'tables', we may close open tables
here. If a MERGE table is affected, detach the children first.
@@ -3359,9 +3363,10 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old)
that they cannot be moved into the unused_tables chain with
these pointers set.
*/
- if (table->child_l || table->parent)
- detach_merge_children(table, TRUE);
- VOID(hash_delete(&open_cache,(uchar*) table));
+ unlink_open_table(thd, table, 0);
+ /* Restart loop */
+ prev= &thd->open_tables;
+ next= *prev;
error=1;
}
else
@@ -3396,7 +3401,7 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old)
}
DBUG_PRINT("tcache", ("open tables to lock: %u",
(uint) (tables_ptr - tables)));
- if (tables != tables_ptr) // Should we get back old locks
+ if (tables != tables_ptr) // Should we get back old locks
{
MYSQL_LOCK *lock;
/*
@@ -3545,7 +3550,7 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock)
char *key= table->s->table_cache_key.str;
uint key_length= table->s->table_cache_key.length;
- DBUG_PRINT("loop", ("table_name: %s", table->alias));
+ DBUG_PRINT("loop", ("table_name: %s", table->alias ? table->alias : ""));
HASH_SEARCH_STATE state;
for (TABLE *search= (TABLE*) hash_first(&open_cache, (uchar*) key,
key_length, &state);
@@ -3883,6 +3888,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list,
int error;
TABLE_SHARE *share;
uint discover_retry_count= 0;
+ bool locked_table;
DBUG_ENTER("open_unireg_entry");
safe_mutex_assert_owner(&LOCK_open);
@@ -4003,8 +4009,10 @@ retry:
}
if (!entry->s || !entry->s->crashed)
goto err;
- // Code below is for repairing a crashed file
- if ((error= lock_table_name(thd, table_list, TRUE)))
+
+ // Code below is for repairing a crashed file
+ locked_table= table_list->table != 0;
+ if (! locked_table && (error= lock_table_name(thd, table_list, TRUE)))
{
if (error < 0)
goto err;
@@ -4038,12 +4046,13 @@ retry:
else
thd->clear_error(); // Clear error message
pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, table_list);
+ if (!locked_table)
+ unlock_table_name(thd, table_list);
if (error)
goto err;
break;
- }
+ }
if (Table_triggers_list::check_n_load(thd, share->db.str,
share->table_name.str, entry, 0))
@@ -4289,7 +4298,7 @@ void detach_merge_children(TABLE *table, bool clear_refs)
children attached yet. Also this is called for every child and the
parent from close_thread_tables().
*/
- if ((first_detach= parent->children_attached))
+ if (parent->children_attached)
{
VOID(parent->file->extra(HA_EXTRA_DETACH_CHILDREN));
parent->children_attached= FALSE;
@@ -4301,38 +4310,50 @@ void detach_merge_children(TABLE *table, bool clear_refs)
if (clear_refs)
{
- /* In any case clear the own parent reference. (***) */
- table->parent= NULL;
+ if (table->parent)
+ {
+ /* In any case clear the own parent reference. (***) */
+ table->parent= NULL;
+ table->file->extra(HA_EXTRA_DETACH_CHILD);
+ }
/*
- On the first detach, clear all references. If this table is the
- parent, we still may need to clear the child references. The first
- detach might not have done this.
+ Clear all references. If this table is the parent, we still may
+ need to clear the child references. The first detach might not
+ have done this.
*/
- if (first_detach || (table == parent))
+ for (child_l= parent->child_l; ; child_l= child_l->next_global)
{
- /* Clear TABLE references to force new assignment at next open. */
- for (child_l= parent->child_l; ; child_l= child_l->next_global)
+ /*
+ Do not DBUG_ASSERT(child_l->table); open_tables might be
+ incomplete or we may have been called twice.
+
+ Clear the parent reference of the children only on the first
+ detach. The children might already be closed. They will clear
+ it themselves when this function is called for them with
+ 'clear_refs' true. See above "(***)".
+ */
+ if (child_l->table)
{
+ if (child_l->table->parent)
+ {
+ child_l->table->parent= NULL;
+ if (child_l->table->db_stat)
+ child_l->table->file->extra(HA_EXTRA_DETACH_CHILD);
+ }
/*
- Do not DBUG_ASSERT(child_l->table); open_tables might be
- incomplete.
-
- Clear the parent reference of the children only on the first
- detach. The children might already be closed. They will clear
- it themseves when this function is called for them with
- 'clear_refs' true. See above "(***)".
+ Set alias to "" to ensure that table is not used if we are in
+ LOCK TABLES
*/
- if (first_detach && child_l->table)
- child_l->table->parent= NULL;
+ ((char*) child_l->table->alias)[0]= 0;
/* Clear the table reference to force new assignment at next open. */
child_l->table= NULL;
-
- /* Break when this was the last child. */
- if (&child_l->next_global == parent->child_last_l)
- break;
}
+
+ /* Break when this was the last child. */
+ if (&child_l->next_global == parent->child_last_l)
+ break;
}
}
@@ -5129,9 +5150,11 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags)
static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table)
{
+ DBUG_ENTER("mark_real_tables_as_free_for_reuse");
for (; table; table= table->next_global)
if (!table->placeholder())
table->table->query_id= 0;
+ DBUG_VOID_RETURN;
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 2b102e47abe..34ef4cfa066 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -273,7 +273,7 @@ const char *set_thd_proc_info(THD *thd, const char *info,
const char *old_info= thd->proc_info;
DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line,
- (info != NULL) ? info : "(null)"));
+ (info != NULL) ? info : ""));
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.status_change(info, calling_function, calling_file, calling_line);
#endif
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index a031a921c60..db96b6fc9a2 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -7999,6 +7999,8 @@ bool parse_sql(THD *thd,
Object_creation_ctx *creation_ctx)
{
bool mysql_parse_status;
+ DBUG_ENTER("parse_sql");
+
DBUG_ASSERT(thd->m_parser_state == NULL);
/* Backup creation context. */
@@ -8032,7 +8034,7 @@ bool parse_sql(THD *thd,
/* That's it. */
- return mysql_parse_status || thd->is_fatal_error;
+ DBUG_RETURN(mysql_parse_status || thd->is_fatal_error);
}
/**
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index e8a382ca8f6..f6864add19f 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -498,9 +498,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
thd->in_lock_tables= 1;
if (reopen_tables(thd, 1, 1))
{
- /* To be safe remove this table from the set of LOCKED TABLES */
- unlink_open_table(thd, tables->table, FALSE);
-
/*
Ignore reopen_tables errors for now. It's better not leave master/slave
in a inconsistent state.