summaryrefslogtreecommitdiff
path: root/sql/lock.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/lock.cc')
-rw-r--r--sql/lock.cc386
1 files changed, 386 insertions, 0 deletions
diff --git a/sql/lock.cc b/sql/lock.cc
new file mode 100644
index 00000000000..b65ae5ddc3e
--- /dev/null
+++ b/sql/lock.cc
@@ -0,0 +1,386 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* locking functions for mysql */
+/*
+ Because of the new concurrent inserts, we must first get external locks
+ before getting internal locks. If we do it in the other order, the status
+ information is not up to date when called from the lock handler.
+
+TODO:
+ Change to use my_malloc() ONLY when using LOCK TABLES command or when
+ we are forced to use mysql_lock_merge.
+*/
+
+#include "mysql_priv.h"
+#include <hash.h>
+
+extern HASH open_cache;
+
+static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count,
+ bool unlock, TABLE **write_locked);
+static int lock_external(TABLE **table,uint count);
+static int unlock_external(THD *thd, TABLE **table,uint count);
+
+
+MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count)
+{
+ MYSQL_LOCK *sql_lock;
+ TABLE *write_lock_used;
+ DBUG_ENTER("mysql_lock_tables");
+
+ for (;;)
+ {
+ if (!(sql_lock = get_lock_data(thd,tables,count, 0,&write_lock_used)))
+ break;
+
+ if (global_read_lock && write_lock_used)
+ {
+ /*
+ Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
+ Wait until the lock is gone
+ */
+ if (thd->global_read_lock) // This thread had the read locks
+ {
+ my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0),
+ write_lock_used->table_name);
+ my_free((gptr) sql_lock,MYF(0));
+ sql_lock=0;
+ break;
+ }
+
+ pthread_mutex_lock(&LOCK_open);
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->mysys_var->current_mutex= &LOCK_open;
+ thd->mysys_var->current_cond= &COND_refresh;
+ thd->proc_info="Waiting for table";
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+
+ while (global_read_lock && ! thd->killed ||
+ thd->version != refresh_version)
+ {
+ (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
+ }
+ pthread_mutex_unlock(&LOCK_open);
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->mysys_var->current_mutex= 0;
+ thd->mysys_var->current_cond= 0;
+ thd->proc_info= 0;
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+
+ if (thd->version != refresh_version || thd->killed)
+ {
+ my_free((gptr) sql_lock,MYF(0));
+ goto retry;
+ }
+ }
+
+ thd->proc_info="System lock";
+ if (lock_external(tables,count))
+ {
+ my_free((gptr) sql_lock,MYF(0));
+ sql_lock=0;
+ thd->proc_info=0;
+ break;
+ }
+ thd->proc_info=0;
+ thd->locked=1;
+ if (thr_multi_lock(sql_lock->locks,sql_lock->lock_count))
+ {
+ thd->some_tables_deleted=1; // Try again
+ sql_lock->lock_count=0; // Locks are alread freed
+ }
+ else if (!thd->some_tables_deleted)
+ {
+ thd->locked=0;
+ break;
+ }
+
+ /* some table was altered or deleted. reopen tables marked deleted */
+ mysql_unlock_tables(thd,sql_lock);
+ thd->locked=0;
+retry:
+ sql_lock=0;
+ if (wait_for_tables(thd))
+ break; // Couldn't open tables
+ }
+ if (thd->killed)
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ if (sql_lock)
+ {
+ mysql_unlock_tables(thd,sql_lock);
+ sql_lock=0;
+ }
+ }
+ DBUG_RETURN (sql_lock);
+}
+
+
+static int lock_external(TABLE **tables,uint count)
+{
+ reg1 uint i;
+ int lock_type,error;
+ THD *thd=current_thd;
+ DBUG_ENTER("lock_external");
+
+ for (i=1 ; i <= count ; i++, tables++)
+ {
+ lock_type=F_WRLCK; /* Lock exclusive */
+ if ((*tables)->db_stat & HA_READ_ONLY ||
+ ((*tables)->reginfo.lock_type >= TL_READ &&
+ (*tables)->reginfo.lock_type <= TL_READ_NO_INSERT))
+ lock_type=F_RDLCK;
+
+ if ((error=(*tables)->file->external_lock(thd,lock_type)))
+ {
+ for ( ; i-- ; tables--)
+ {
+ (*tables)->file->external_lock(thd, F_UNLCK);
+ (*tables)->current_lock=F_UNLCK;
+ }
+ my_error(ER_CANT_LOCK,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error);
+ DBUG_RETURN(error);
+ }
+ else
+ {
+ (*tables)->db_stat &= ~ HA_BLOCK_LOCK;
+ (*tables)->current_lock= lock_type;
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
+{
+ DBUG_ENTER("mysql_unlock_tables");
+ thr_multi_unlock(sql_lock->locks,sql_lock->lock_count);
+ VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
+ my_free((gptr) sql_lock,MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Unlock some of the tables locked by mysql_lock_tables
+ This will work even if get_lock_data fails (next unlock will free all)
+ */
+
+void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count)
+{
+ MYSQL_LOCK *sql_lock;
+ TABLE *write_lock_used;
+ if ((sql_lock = get_lock_data(thd, table, count, 1, &write_lock_used)))
+ mysql_unlock_tables(thd, sql_lock);
+}
+
+
+/*
+** unlock all tables locked for read.
+*/
+
+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(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;
+ }
+
+ /* Then to 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++)
+ {
+ if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
+ {
+ swap(TABLE *,*table,sql_lock->table[i]);
+ table++;
+ found++;
+ }
+ }
+ /* Unlock all read locked tables */
+ if (i != found)
+ {
+ VOID(unlock_external(thd,table,i-found));
+ sql_lock->table_count-=found;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+
+void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
+{
+ mysql_unlock_some_tables(thd, &table,1);
+ if (locked)
+ {
+ reg1 uint i;
+ for (i=0; i < locked->table_count; i++)
+ {
+ if (locked->table[i] == table)
+ {
+ locked->table_count--;
+ bmove((char*) (locked->table+i),
+ (char*) (locked->table+i+1),
+ (locked->table_count-i)* sizeof(TABLE*));
+ break;
+ }
+ }
+ THR_LOCK_DATA **prev=locked->locks;
+ for (i=0 ; i < locked->lock_count ; i++)
+ {
+ if (locked->locks[i]->type != TL_UNLOCK)
+ *prev++ = locked->locks[i];
+ }
+ locked->lock_count=(prev - locked->locks);
+ }
+}
+
+/* abort all other threads waiting to get lock in table */
+
+void mysql_lock_abort(THD *thd, TABLE *table)
+{
+ MYSQL_LOCK *locked;
+ TABLE *write_lock_used;
+ if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used)))
+ {
+ for (uint i=0; i < locked->lock_count; i++)
+ thr_abort_locks(locked->locks[i]->lock);
+ my_free((gptr) locked,MYF(0));
+ }
+}
+
+
+MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
+{
+ MYSQL_LOCK *sql_lock;
+ DBUG_ENTER("mysql_lock_merge");
+ if (!(sql_lock= (MYSQL_LOCK*)
+ my_malloc(sizeof(*sql_lock)+
+ sizeof(THR_LOCK_DATA*)*(a->lock_count+b->lock_count)+
+ 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);
+ 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));
+ memcpy(sql_lock->table,a->table,a->table_count*sizeof(*a->table));
+ memcpy(sql_lock->table+a->table_count,b->table,
+ b->table_count*sizeof(*b->table));
+ my_free((gptr) a,MYF(0));
+ my_free((gptr) b,MYF(0));
+ DBUG_RETURN(sql_lock);
+}
+
+
+ /* unlock a set of external */
+
+static int unlock_external(THD *thd, TABLE **table,uint count)
+{
+ int error,error_code;
+ DBUG_ENTER("unlock_external");
+
+ error_code=0;
+ for (; count-- ; table++)
+ {
+ if ((*table)->current_lock != F_UNLCK)
+ {
+ (*table)->current_lock = F_UNLCK;
+ if ((error=(*table)->file->external_lock(thd, F_UNLCK)))
+ error_code=error;
+ }
+ }
+ if (error_code)
+ my_error(ER_CANT_LOCK,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error_code);
+ DBUG_RETURN(error_code);
+}
+
+
+/*
+** Get lock structures from table structs and initialize locks
+*/
+
+
+static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
+ bool get_old_locks, TABLE **write_lock_used)
+{
+ uint i,tables,lock_count;
+ MYSQL_LOCK *sql_lock;
+ THR_LOCK_DATA **locks;
+ TABLE **to;
+
+ *write_lock_used=0;
+ for (i=tables=lock_count=0 ; i < count ; i++)
+ {
+ if (!table_ptr[i]->tmp_table)
+ {
+ tables+=table_ptr[i]->file->lock_count();
+ lock_count++;
+ }
+ }
+
+ if (!(sql_lock= (MYSQL_LOCK*)
+ my_malloc(sizeof(*sql_lock)+
+ sizeof(THR_LOCK_DATA*)*tables+sizeof(table_ptr)*lock_count,
+ MYF(0))))
+ return 0;
+ locks=sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
+ to=sql_lock->table=(TABLE**) (locks+tables);
+ sql_lock->table_count=lock_count;
+ sql_lock->lock_count=tables;
+
+ for (i=0 ; i < count ; i++)
+ {
+ TABLE *table;
+ if ((table=table_ptr[i])->tmp_table)
+ continue;
+ *to++=table;
+ enum thr_lock_type lock_type= table->reginfo.lock_type;
+ if (lock_type >= TL_WRITE_ALLOW_WRITE)
+ {
+ *write_lock_used=table;
+ if (table->db_stat & HA_READ_ONLY)
+ {
+ my_error(ER_OPEN_AS_READONLY,MYF(0),table->table_name);
+ my_free((gptr) sql_lock,MYF(0));
+ return 0;
+ }
+ }
+ locks=table->file->store_lock(thd, locks, get_old_locks ? TL_IGNORE :
+ lock_type);
+ }
+ return sql_lock;
+}