~mysql/mysql-server/mysql-6.0

« back to all changes in this revision

Viewing changes to sql/lock.cc

  • Committer: bk at mysql
  • Date: 2000-07-31 19:29:14 UTC
  • Revision ID: sp1r-bk@work.mysql.com-20000731192914-08846
Import changeset

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
 
2
   
 
3
   This program is free software; you can redistribute it and/or modify
 
4
   it under the terms of the GNU General Public License as published by
 
5
   the Free Software Foundation; either version 2 of the License, or
 
6
   (at your option) any later version.
 
7
   
 
8
   This program is distributed in the hope that it will be useful,
 
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
   GNU General Public License for more details.
 
12
   
 
13
   You should have received a copy of the GNU General Public License
 
14
   along with this program; if not, write to the Free Software
 
15
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
16
 
 
17
 
 
18
/* locking functions for mysql */
 
19
/*
 
20
  Because of the new concurrent inserts, we must first get external locks
 
21
  before getting internal locks.  If we do it in the other order, the status
 
22
  information is not up to date when called from the lock handler.
 
23
 
 
24
TODO:
 
25
  Change to use my_malloc() ONLY when using LOCK TABLES command or when
 
26
  we are forced to use mysql_lock_merge.
 
27
*/
 
28
 
 
29
#include "mysql_priv.h"
 
30
#include <hash.h>
 
31
 
 
32
extern HASH open_cache;
 
33
 
 
34
static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count,
 
35
                                 bool unlock, TABLE **write_locked);
 
36
static int lock_external(TABLE **table,uint count);
 
37
static int unlock_external(THD *thd, TABLE **table,uint count);
 
38
 
 
39
 
 
40
MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count)
 
41
{
 
42
  MYSQL_LOCK *sql_lock;
 
43
  TABLE *write_lock_used;
 
44
  DBUG_ENTER("mysql_lock_tables");
 
45
 
 
46
  for (;;)
 
47
  {
 
48
    if (!(sql_lock = get_lock_data(thd,tables,count, 0,&write_lock_used)))
 
49
      break;
 
50
 
 
51
    if (global_read_lock && write_lock_used)
 
52
    {
 
53
      /*
 
54
        Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
 
55
        Wait until the lock is gone
 
56
      */
 
57
      if (thd->global_read_lock)        // This thread had the read locks
 
58
      {
 
59
        my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0),
 
60
                 write_lock_used->table_name);
 
61
        my_free((gptr) sql_lock,MYF(0));
 
62
        sql_lock=0;
 
63
        break;
 
64
      } 
 
65
 
 
66
      pthread_mutex_lock(&LOCK_open);
 
67
      pthread_mutex_lock(&thd->mysys_var->mutex);
 
68
      thd->mysys_var->current_mutex= &LOCK_open;
 
69
      thd->mysys_var->current_cond= &COND_refresh;
 
70
      thd->proc_info="Waiting for table";
 
71
      pthread_mutex_unlock(&thd->mysys_var->mutex);
 
72
 
 
73
      while (global_read_lock && ! thd->killed ||
 
74
             thd->version != refresh_version)
 
75
      {
 
76
        (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
 
77
      }
 
78
      pthread_mutex_unlock(&LOCK_open);
 
79
      pthread_mutex_lock(&thd->mysys_var->mutex);
 
80
      thd->mysys_var->current_mutex= 0;
 
81
      thd->mysys_var->current_cond= 0;
 
82
      thd->proc_info= 0;
 
83
      pthread_mutex_unlock(&thd->mysys_var->mutex);
 
84
 
 
85
      if (thd->version != refresh_version || thd->killed)
 
86
      {
 
87
        my_free((gptr) sql_lock,MYF(0));
 
88
        goto retry;
 
89
      }
 
90
    }
 
91
 
 
92
    thd->proc_info="System lock";
 
93
    if (lock_external(tables,count))
 
94
    {
 
95
      my_free((gptr) sql_lock,MYF(0));
 
96
      sql_lock=0;
 
97
      thd->proc_info=0;
 
98
      break;
 
99
    }
 
100
    thd->proc_info=0;
 
101
    thd->locked=1;
 
102
    if (thr_multi_lock(sql_lock->locks,sql_lock->lock_count))
 
103
    {
 
104
      thd->some_tables_deleted=1;               // Try again
 
105
      sql_lock->lock_count=0;                   // Locks are alread freed
 
106
    }
 
107
    else if (!thd->some_tables_deleted)
 
108
    {
 
109
      thd->locked=0;
 
110
      break;
 
111
    }
 
112
 
 
113
    /* some table was altered or deleted. reopen tables marked deleted */
 
114
    mysql_unlock_tables(thd,sql_lock);
 
115
    thd->locked=0;
 
116
retry:
 
117
    sql_lock=0;
 
118
    if (wait_for_tables(thd))
 
119
      break;                                    // Couldn't open tables
 
120
  }
 
121
  if (thd->killed)
 
122
  {
 
123
    my_error(ER_SERVER_SHUTDOWN,MYF(0));
 
124
    if (sql_lock)
 
125
    {
 
126
      mysql_unlock_tables(thd,sql_lock);
 
127
      sql_lock=0;
 
128
    }
 
129
  }
 
130
  DBUG_RETURN (sql_lock);
 
131
}
 
132
 
 
133
 
 
134
static int lock_external(TABLE **tables,uint count)
 
135
{
 
136
  reg1 uint i;
 
137
  int lock_type,error;
 
138
  THD *thd=current_thd;
 
139
  DBUG_ENTER("lock_external");
 
140
 
 
141
  for (i=1 ; i <= count ; i++, tables++)
 
142
  {
 
143
    lock_type=F_WRLCK;                          /* Lock exclusive */
 
144
    if ((*tables)->db_stat & HA_READ_ONLY ||
 
145
        ((*tables)->reginfo.lock_type >= TL_READ &&
 
146
         (*tables)->reginfo.lock_type <= TL_READ_NO_INSERT))
 
147
      lock_type=F_RDLCK;
 
148
 
 
149
    if ((error=(*tables)->file->external_lock(thd,lock_type)))
 
150
    {
 
151
      for ( ; i-- ; tables--)
 
152
      {
 
153
        (*tables)->file->external_lock(thd, F_UNLCK);
 
154
        (*tables)->current_lock=F_UNLCK;
 
155
      }
 
156
      my_error(ER_CANT_LOCK,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error);
 
157
      DBUG_RETURN(error);
 
158
    }
 
159
    else
 
160
    {
 
161
      (*tables)->db_stat &= ~ HA_BLOCK_LOCK;
 
162
      (*tables)->current_lock= lock_type;
 
163
    }
 
164
  }
 
165
  DBUG_RETURN(0);
 
166
}
 
167
 
 
168
 
 
169
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
 
170
{
 
171
  DBUG_ENTER("mysql_unlock_tables");
 
172
  thr_multi_unlock(sql_lock->locks,sql_lock->lock_count);
 
173
  VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
 
174
  my_free((gptr) sql_lock,MYF(0));
 
175
  DBUG_VOID_RETURN;
 
176
}
 
177
 
 
178
/*
 
179
  Unlock some of the tables locked by mysql_lock_tables
 
180
  This will work even if get_lock_data fails (next unlock will free all)
 
181
  */
 
182
 
 
183
void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count)
 
184
{
 
185
  MYSQL_LOCK *sql_lock;
 
186
  TABLE *write_lock_used;
 
187
  if ((sql_lock = get_lock_data(thd, table, count, 1, &write_lock_used)))
 
188
    mysql_unlock_tables(thd, sql_lock);
 
189
}
 
190
 
 
191
 
 
192
/*
 
193
** unlock all tables locked for read.
 
194
*/
 
195
 
 
196
void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
 
197
{
 
198
  uint i,found;
 
199
  DBUG_ENTER("mysql_unlock_read_tables");
 
200
 
 
201
  /* Move all write locks first */
 
202
  THR_LOCK_DATA **lock=sql_lock->locks;
 
203
  for (i=found=0 ; i < sql_lock->lock_count ; i++)
 
204
  {
 
205
    if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
 
206
    {
 
207
      swap(THR_LOCK_DATA *,*lock,sql_lock->locks[i]);
 
208
      lock++;
 
209
      found++;
 
210
    }
 
211
  }
 
212
  /* unlock the read locked tables */
 
213
  if (i != found)
 
214
  {
 
215
    thr_multi_unlock(lock,i-found);
 
216
    sql_lock->lock_count-=found;
 
217
  }
 
218
 
 
219
  /* Then to the same for the external locks */
 
220
  /* Move all write locked tables first */
 
221
  TABLE **table=sql_lock->table;
 
222
  for (i=found=0 ; i < sql_lock->table_count ; i++)
 
223
  {
 
224
    if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
 
225
    {
 
226
      swap(TABLE *,*table,sql_lock->table[i]);
 
227
      table++;
 
228
      found++;
 
229
    }
 
230
  }
 
231
  /* Unlock all read locked tables */
 
232
  if (i != found)
 
233
  {
 
234
    VOID(unlock_external(thd,table,i-found));
 
235
    sql_lock->table_count-=found;
 
236
  }
 
237
  DBUG_VOID_RETURN;
 
238
}
 
239
 
 
240
 
 
241
 
 
242
void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
 
243
{
 
244
  mysql_unlock_some_tables(thd, &table,1);
 
245
  if (locked)
 
246
  {
 
247
    reg1 uint i;
 
248
    for (i=0; i < locked->table_count; i++)
 
249
    {
 
250
      if (locked->table[i] == table)
 
251
      {
 
252
        locked->table_count--;
 
253
        bmove((char*) (locked->table+i),
 
254
              (char*) (locked->table+i+1),
 
255
              (locked->table_count-i)* sizeof(TABLE*));
 
256
        break;
 
257
      }
 
258
    }
 
259
    THR_LOCK_DATA **prev=locked->locks;
 
260
    for (i=0 ; i < locked->lock_count ; i++)
 
261
    {
 
262
      if (locked->locks[i]->type != TL_UNLOCK)
 
263
        *prev++ = locked->locks[i];
 
264
    }
 
265
    locked->lock_count=(prev - locked->locks);
 
266
  }
 
267
}
 
268
 
 
269
/* abort all other threads waiting to get lock in table */
 
270
 
 
271
void mysql_lock_abort(THD *thd, TABLE *table)
 
272
{
 
273
  MYSQL_LOCK *locked;
 
274
  TABLE *write_lock_used;
 
275
  if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used)))
 
276
  {
 
277
    for (uint i=0; i < locked->lock_count; i++)
 
278
      thr_abort_locks(locked->locks[i]->lock);
 
279
    my_free((gptr) locked,MYF(0));
 
280
  }
 
281
}
 
282
 
 
283
 
 
284
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
 
285
{
 
286
  MYSQL_LOCK *sql_lock;
 
287
  DBUG_ENTER("mysql_lock_merge");
 
288
  if (!(sql_lock= (MYSQL_LOCK*)
 
289
        my_malloc(sizeof(*sql_lock)+
 
290
                  sizeof(THR_LOCK_DATA*)*(a->lock_count+b->lock_count)+
 
291
                  sizeof(TABLE*)*(a->table_count+b->table_count),MYF(MY_WME))))
 
292
    DBUG_RETURN(0);                             // Fatal error
 
293
  sql_lock->lock_count=a->lock_count+b->lock_count;
 
294
  sql_lock->table_count=a->table_count+b->table_count;
 
295
  sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
 
296
  sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count);
 
297
  memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks));
 
298
  memcpy(sql_lock->locks+a->lock_count,b->locks,
 
299
         b->lock_count*sizeof(*b->locks));
 
300
  memcpy(sql_lock->table,a->table,a->table_count*sizeof(*a->table));
 
301
  memcpy(sql_lock->table+a->table_count,b->table,
 
302
         b->table_count*sizeof(*b->table));
 
303
  my_free((gptr) a,MYF(0));
 
304
  my_free((gptr) b,MYF(0));
 
305
  DBUG_RETURN(sql_lock);
 
306
}
 
307
 
 
308
 
 
309
        /* unlock a set of external */
 
310
 
 
311
static int unlock_external(THD *thd, TABLE **table,uint count)
 
312
{
 
313
  int error,error_code;
 
314
  DBUG_ENTER("unlock_external");
 
315
 
 
316
  error_code=0;
 
317
  for (; count-- ; table++)
 
318
  {
 
319
    if ((*table)->current_lock != F_UNLCK)
 
320
    {
 
321
      (*table)->current_lock = F_UNLCK;
 
322
      if ((error=(*table)->file->external_lock(thd, F_UNLCK)))
 
323
        error_code=error;
 
324
    }
 
325
  }
 
326
  if (error_code)
 
327
    my_error(ER_CANT_LOCK,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error_code);
 
328
  DBUG_RETURN(error_code);
 
329
}
 
330
 
 
331
 
 
332
/*
 
333
** Get lock structures from table structs and initialize locks
 
334
*/
 
335
 
 
336
 
 
337
static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
 
338
                                 bool get_old_locks, TABLE **write_lock_used)
 
339
{
 
340
  uint i,tables,lock_count;
 
341
  MYSQL_LOCK *sql_lock;
 
342
  THR_LOCK_DATA **locks;
 
343
  TABLE **to;
 
344
 
 
345
  *write_lock_used=0;
 
346
  for (i=tables=lock_count=0 ; i < count ; i++)
 
347
  {
 
348
    if (!table_ptr[i]->tmp_table)
 
349
    {
 
350
      tables+=table_ptr[i]->file->lock_count();
 
351
      lock_count++;
 
352
    }
 
353
  }
 
354
 
 
355
  if (!(sql_lock= (MYSQL_LOCK*)
 
356
        my_malloc(sizeof(*sql_lock)+
 
357
                  sizeof(THR_LOCK_DATA*)*tables+sizeof(table_ptr)*lock_count,
 
358
                  MYF(0))))
 
359
    return 0;
 
360
  locks=sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
 
361
  to=sql_lock->table=(TABLE**) (locks+tables);
 
362
  sql_lock->table_count=lock_count;
 
363
  sql_lock->lock_count=tables;
 
364
 
 
365
  for (i=0 ; i < count ; i++)
 
366
  {
 
367
    TABLE *table;
 
368
    if ((table=table_ptr[i])->tmp_table)
 
369
      continue;
 
370
    *to++=table;
 
371
    enum thr_lock_type lock_type= table->reginfo.lock_type;
 
372
    if (lock_type >= TL_WRITE_ALLOW_WRITE)
 
373
    {
 
374
      *write_lock_used=table;
 
375
      if (table->db_stat & HA_READ_ONLY)
 
376
      {
 
377
        my_error(ER_OPEN_AS_READONLY,MYF(0),table->table_name);
 
378
        my_free((gptr) sql_lock,MYF(0));
 
379
        return 0;
 
380
      }
 
381
    }
 
382
    locks=table->file->store_lock(thd, locks, get_old_locks ? TL_IGNORE :
 
383
                                  lock_type);
 
384
  }
 
385
  return sql_lock;
 
386
}