diff options
Diffstat (limited to 'sql/lock.cc')
-rw-r--r-- | sql/lock.cc | 103 |
1 files changed, 74 insertions, 29 deletions
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; } |