lock.cc 47.6 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000-2006 MySQL AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
3 4
   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
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6

unknown's avatar
unknown committed
7 8 9 10
   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.
unknown's avatar
unknown committed
11

unknown's avatar
unknown committed
12 13 14 15 16 17 18 19 20 21 22
   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.

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
  GENERAL DESCRIPTION OF LOCKING

  When not using LOCK TABLES:

  - For each SQL statement mysql_lock_tables() is called for all involved
    tables.
    - mysql_lock_tables() will call
      table_handler->external_lock(thd,locktype) for each table.
      This is followed by a call to thr_multi_lock() for all tables.

  - When statement is done, we call mysql_unlock_tables().
    This will call thr_multi_unlock() followed by
    table_handler->external_lock(thd, F_UNLCK) for each table.

  - Note that mysql_unlock_tables() may be called several times as
    MySQL in some cases can free some tables earlier than others.

  - The above is true both for normal and temporary tables.

  - Temporary non transactional tables are never passed to thr_multi_lock()
    and we never call external_lock(thd, F_UNLOCK) on these.

  When using LOCK TABLES:

  - LOCK TABLE will call mysql_lock_tables() for all tables.
    mysql_lock_tables() will call
    table_handler->external_lock(thd,locktype) for each table.
    This is followed by a call to thr_multi_lock() for all tables.

  - For each statement, we will call table_handler->start_stmt(THD)
    to inform the table handler that we are using the table.

    The tables used can only be tables used in LOCK TABLES or a
    temporary table.

  - When statement is done, we will call ha_commit_stmt(thd);

  - When calling UNLOCK TABLES we call mysql_unlock_tables() for all
    tables used in LOCK TABLES

63 64 65 66 67
  If table_handler->external_lock(thd, locktype) fails, we call
  table_handler->external_lock(thd, F_UNLCK) for each table that was locked,
  excluding one that caused failure. That means handler must cleanup itself
  in case external_lock() fails.

unknown's avatar
unknown committed
68 69 70 71 72 73 74
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>
75
#include <assert.h>
unknown's avatar
unknown committed
76

77 78 79 80 81
/**
  @defgroup Locking Locking
  @{
*/

unknown's avatar
unknown committed
82 83
extern HASH open_cache;

84 85 86
/* flags for get_lock_data */
#define GET_LOCK_UNLOCK         1
#define GET_LOCK_STORE_LOCKS    2
unknown's avatar
unknown committed
87 88

static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count,
89
				 uint flags, TABLE **write_locked);
90
static void reset_lock_data(MYSQL_LOCK *sql_lock);
91
static int lock_external(THD *thd, TABLE **table,uint count);
unknown's avatar
unknown committed
92
static int unlock_external(THD *thd, TABLE **table,uint count);
93
static void print_lock_error(int error, const char *);
unknown's avatar
unknown committed
94

95 96 97 98 99 100 101 102 103 104
/*
  Lock tables.

  SYNOPSIS
    mysql_lock_tables()
    thd                         The current thread.
    tables                      An array of pointers to the tables to lock.
    count                       The number of tables to lock.
    flags                       Options:
      MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK      Ignore a global read lock
105
      MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY      Ignore SET GLOBAL READ_ONLY
106
      MYSQL_LOCK_IGNORE_FLUSH                 Ignore a flush tables.
107 108 109 110 111 112 113
      MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN        Instead of reopening altered
                                              or dropped tables by itself,
                                              mysql_lock_tables() should
                                              notify upper level and rely
                                              on caller doing this.
    need_reopen                 Out parameter, TRUE if some tables were altered
                                or deleted and should be reopened by caller.
114 115 116

  RETURN
    A lock structure pointer on success.
117
    NULL on error or if some tables should be reopen.
118 119
*/

120
/* Map the return value of thr_lock to an error from errmsg.txt */
unknown's avatar
unknown committed
121 122 123
static int thr_lock_errno_to_mysql[]=
{ 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };

unknown's avatar
unknown committed
124 125 126 127 128 129 130 131 132
/**
  Perform semantic checks for mysql_lock_tables.
  @param thd The current thread
  @param tables The tables to lock
  @param count The number of tables to lock
  @param flags Lock flags
  @return 0 if all the check passed, non zero if a check failed.
*/
int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
unknown's avatar
unknown committed
133
{
134 135
  bool log_table_write_query;
  uint system_count;
unknown's avatar
unknown committed
136
  uint i;
137

unknown's avatar
unknown committed
138
  DBUG_ENTER("mysql_lock_tables_check");
unknown's avatar
unknown committed
139

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
  system_count= 0;
  log_table_write_query= (is_log_table_write_query(thd->lex->sql_command)
                         || ((flags & MYSQL_LOCK_PERF_SCHEMA) != 0));

  for (i=0 ; i<count; i++)
  {
    TABLE *t= tables[i];

    /* Protect against 'fake' partially initialized TABLE_SHARE */
    DBUG_ASSERT(t->s->table_category != TABLE_UNKNOWN_CATEGORY);

    /*
      Table I/O to performance schema tables is performed
      only internally by the server implementation.
      When a user is requesting a lock, the following
      constraints are enforced:
    */
    if (t->s->require_write_privileges() &&
        ! log_table_write_query)
    {
      /*
        A user should not be able to prevent writes,
        or hold any type of lock in a session,
        since this would be a DOS attack.
      */
      if ((t->reginfo.lock_type >= TL_READ_NO_INSERT)
          || (thd->lex->sql_command == SQLCOM_LOCK_TABLES))
      {
        my_error(ER_CANT_LOCK_LOG_TABLE, MYF(0));
unknown's avatar
unknown committed
169
        DBUG_RETURN(1);
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
      }
    }

    if ((t->s->table_category == TABLE_CATEGORY_SYSTEM) &&
        (t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE))
    {
      system_count++;
    }
  }

  /*
    Locking of system tables is restricted:
    locking a mix of system and non-system tables in the same lock
    is prohibited, to prevent contention.
  */
  if ((system_count > 0) && (system_count < count))
  {
    my_error(ER_WRONG_LOCK_OF_SYSTEM_TABLE, MYF(0));
unknown's avatar
unknown committed
188
    DBUG_RETURN(1);
189
  }
190

unknown's avatar
unknown committed
191 192 193
  DBUG_RETURN(0);
}

194 195
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
                              uint flags, bool *need_reopen)
unknown's avatar
unknown committed
196 197 198
{
  MYSQL_LOCK *sql_lock;
  TABLE *write_lock_used;
199
  int rc;
unknown's avatar
unknown committed
200

unknown's avatar
unknown committed
201 202
  DBUG_ENTER("mysql_lock_tables");

203 204
  *need_reopen= FALSE;

unknown's avatar
unknown committed
205 206 207
  if (mysql_lock_tables_check(thd, tables, count, flags))
    DBUG_RETURN (NULL);

unknown's avatar
unknown committed
208 209
  for (;;)
  {
210 211
    if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
                                   &write_lock_used)))
unknown's avatar
unknown committed
212 213
      break;

214 215
    if (global_read_lock && write_lock_used &&
        ! (flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))
unknown's avatar
unknown committed
216 217 218 219 220
    {
      /*
	Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
	Wait until the lock is gone
      */
221
      if (wait_if_global_read_lock(thd, 1, 1))
unknown's avatar
unknown committed
222
      {
223 224
        /* Clear the lock type of all lock data to avoid reusage. */
        reset_lock_data(sql_lock);
225
	my_free((uchar*) sql_lock,MYF(0));
unknown's avatar
unknown committed
226 227
	sql_lock=0;
	break;
228
      }
229
      if (thd->version != refresh_version)
unknown's avatar
unknown committed
230
      {
231 232
        /* Clear the lock type of all lock data to avoid reusage. */
        reset_lock_data(sql_lock);
233
	my_free((uchar*) sql_lock,MYF(0));
unknown's avatar
unknown committed
234 235 236 237
	goto retry;
      }
    }

238 239 240 241 242
    if (!(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY) &&
        write_lock_used &&
        opt_readonly &&
        !(thd->security_ctx->master_access & SUPER_ACL) &&
        !thd->slave_thread)
unknown's avatar
unknown committed
243 244 245 246 247 248
    {
      /*
	Someone has issued SET GLOBAL READ_ONLY=1 and we want a write lock.
        We do not wait for READ_ONLY=0, and fail.
      */
      reset_lock_data(sql_lock);
249
      my_free((uchar*) sql_lock, MYF(0));
unknown's avatar
unknown committed
250 251 252 253 254
      sql_lock=0;
      my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
      break;
    }

unknown's avatar
unknown committed
255
    thd->proc_info="System lock";
unknown's avatar
unknown committed
256
    DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
257 258
    if (sql_lock->table_count && lock_external(thd, sql_lock->table,
                                               sql_lock->table_count))
unknown's avatar
unknown committed
259
    {
260 261
      /* Clear the lock type of all lock data to avoid reusage. */
      reset_lock_data(sql_lock);
262
      my_free((uchar*) sql_lock,MYF(0));
unknown's avatar
unknown committed
263 264 265
      sql_lock=0;
      break;
    }
266
    thd->proc_info="Table lock";
unknown's avatar
unknown committed
267
    DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
unknown's avatar
unknown committed
268
    thd->locked=1;
269 270 271 272
    /* Copy the lock data array. thr_multi_lock() reorders its contens. */
    memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
           sql_lock->lock_count * sizeof(*sql_lock->locks));
    /* Lock on the copied half of the lock data array. */
273 274
    rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
                                                     sql_lock->lock_count,
275 276 277 278
                                                     sql_lock->lock_count,
                                                     thd->lock_id)];
    if (rc > 1)                                 /* a timeout or a deadlock */
    {
279 280
      if (sql_lock->table_count)
        VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count));
281
      my_error(rc, MYF(0));
282
      my_free((uchar*) sql_lock,MYF(0));
283 284 285 286
      sql_lock= 0;
      break;
    }
    else if (rc == 1)                           /* aborted */
unknown's avatar
unknown committed
287
    {
288 289 290 291 292 293 294
      /*
        reset_lock_data is required here. If thr_multi_lock fails it
        resets lock type for tables, which were locked before (and
        including) one that caused error. Lock type for other tables
        preserved.
      */
      reset_lock_data(sql_lock);
unknown's avatar
unknown committed
295
      thd->some_tables_deleted=1;		// Try again
296
      sql_lock->lock_count= 0;                  // Locks are already freed
unknown's avatar
unknown committed
297
    }
298
    else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
unknown's avatar
unknown committed
299
    {
300 301 302 303
      /*
        Thread was killed or lock aborted. Let upper level close all
        used tables and retry or give error.
      */
unknown's avatar
unknown committed
304 305 306
      thd->locked=0;
      break;
    }
307 308 309 310 311 312 313
    else if (!thd->open_tables)
    {
      // Only using temporary tables, no need to unlock
      thd->some_tables_deleted=0;
      thd->locked=0;
      break;
    }
314
    thd->proc_info=0;
unknown's avatar
unknown committed
315 316 317 318 319 320

    /* some table was altered or deleted. reopen tables marked deleted */
    mysql_unlock_tables(thd,sql_lock);
    thd->locked=0;
retry:
    sql_lock=0;
321 322 323 324 325
    if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
    {
      *need_reopen= TRUE;
      break;
    }
unknown's avatar
unknown committed
326 327 328
    if (wait_for_tables(thd))
      break;					// Couldn't open tables
  }
329
  thd->proc_info=0;
unknown's avatar
unknown committed
330 331
  if (thd->killed)
  {
unknown's avatar
SCRUM  
unknown committed
332
    thd->send_kill_message();
unknown's avatar
unknown committed
333 334 335 336 337 338
    if (sql_lock)
    {
      mysql_unlock_tables(thd,sql_lock);
      sql_lock=0;
    }
  }
unknown's avatar
unknown committed
339

340
  thd->set_time_after_lock();
unknown's avatar
unknown committed
341 342 343 344
  DBUG_RETURN (sql_lock);
}


345
static int lock_external(THD *thd, TABLE **tables, uint count)
unknown's avatar
unknown committed
346 347 348 349 350
{
  reg1 uint i;
  int lock_type,error;
  DBUG_ENTER("lock_external");

unknown's avatar
unknown committed
351
  DBUG_PRINT("info", ("count %d", count));
unknown's avatar
unknown committed
352 353
  for (i=1 ; i <= count ; i++, tables++)
  {
unknown's avatar
unknown committed
354
    DBUG_ASSERT((*tables)->reginfo.lock_type >= TL_READ);
unknown's avatar
unknown committed
355 356 357 358 359 360
    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;

361
    if ((error=(*tables)->file->ha_external_lock(thd,lock_type)))
unknown's avatar
unknown committed
362
    {
363
      print_lock_error(error, (*tables)->file->table_type());
364
      while (--i)
unknown's avatar
unknown committed
365
      {
366
        tables--;
367
	(*tables)->file->ha_external_lock(thd, F_UNLCK);
unknown's avatar
unknown committed
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
	(*tables)->current_lock=F_UNLCK;
      }
      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");
unknown's avatar
unknown committed
385 386
  if (sql_lock->lock_count)
    thr_multi_unlock(sql_lock->locks,sql_lock->lock_count);
387
  if (sql_lock->table_count)
unknown's avatar
unknown committed
388
    VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
389
  my_free((uchar*) sql_lock,MYF(0));
unknown's avatar
unknown committed
390 391 392 393 394 395 396 397 398 399 400 401
  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;
402 403
  if ((sql_lock= get_lock_data(thd, table, count, GET_LOCK_UNLOCK,
                               &write_lock_used)))
unknown's avatar
unknown committed
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
    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)
    {
423
      swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]);
unknown's avatar
unknown committed
424 425 426 427 428 429 430 431
      lock++;
      found++;
    }
  }
  /* unlock the read locked tables */
  if (i != found)
  {
    thr_multi_unlock(lock,i-found);
unknown's avatar
unknown committed
432
    sql_lock->lock_count= found;
unknown's avatar
unknown committed
433 434
  }

435
  /* Then do the same for the external locks */
unknown's avatar
unknown committed
436 437 438 439
  /* Move all write locked tables first */
  TABLE **table=sql_lock->table;
  for (i=found=0 ; i < sql_lock->table_count ; i++)
  {
440
    DBUG_ASSERT(sql_lock->table[i]->lock_position == i);
unknown's avatar
unknown committed
441 442
    if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
    {
443
      swap_variables(TABLE *, *table, sql_lock->table[i]);
unknown's avatar
unknown committed
444 445 446 447 448 449 450 451
      table++;
      found++;
    }
  }
  /* Unlock all read locked tables */
  if (i != found)
  {
    VOID(unlock_external(thd,table,i-found));
unknown's avatar
unknown committed
452
    sql_lock->table_count=found;
unknown's avatar
unknown committed
453
  }
454 455 456 457 458 459 460 461 462 463 464
  /* Fix the lock positions in TABLE */
  table= sql_lock->table;
  found= 0;
  for (i= 0; i < sql_lock->table_count; i++)
  {
    TABLE *tbl= *table;
    tbl->lock_position= table - sql_lock->table;
    tbl->lock_data_start= found;
    found+= tbl->lock_count;
    table++;
  }
unknown's avatar
unknown committed
465 466 467 468
  DBUG_VOID_RETURN;
}


469 470 471
/**
  Try to find the table in the list of locked tables.
  In case of success, unlock the table and remove it from this list.
unknown's avatar
unknown committed
472

473 474 475 476 477 478 479 480 481
  @note This function has a legacy side effect: the table is
  unlocked even if it is not found in the locked list.
  It's not clear if this side effect is intentional or still
  desirable. It might lead to unmatched calls to
  unlock_external(). Moreover, a discrepancy can be left
  unnoticed by the storage engine, because in
  unlock_external() we call handler::external_lock(F_UNLCK) only
  if table->current_lock is not F_UNLCK.

unknown's avatar
unknown committed
482 483 484
  @param  thd             thread context
  @param  locked          list of locked tables
  @param  table           the table to unlock
485 486 487
  @param  always_unlock   specify explicitly if the legacy side
                          effect is desired.
*/
unknown's avatar
unknown committed
488

489 490
void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
                       bool always_unlock)
unknown's avatar
unknown committed
491
{
492 493
  if (always_unlock == TRUE)
    mysql_unlock_some_tables(thd, &table, /* table count */ 1);
unknown's avatar
unknown committed
494 495 496 497 498 499 500
  if (locked)
  {
    reg1 uint i;
    for (i=0; i < locked->table_count; i++)
    {
      if (locked->table[i] == table)
      {
501 502 503 504 505 506
        uint  j, removed_locks, old_tables;
        TABLE *tbl;
        uint lock_data_end;

        DBUG_ASSERT(table->lock_position == i);

507 508 509 510
        /* Unlock if not yet unlocked */
        if (always_unlock == FALSE)
          mysql_unlock_some_tables(thd, &table, /* table count */ 1);

511 512 513 514 515 516 517
        /* Decrement table_count in advance, making below expressions easier */
        old_tables= --locked->table_count;

        /* The table has 'removed_locks' lock data elements in locked->locks */
        removed_locks= table->lock_count;

        /* Move down all table pointers above 'i'. */
unknown's avatar
unknown committed
518 519
	bmove((char*) (locked->table+i),
	      (char*) (locked->table+i+1),
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
	      (old_tables - i) * sizeof(TABLE*));

        lock_data_end= table->lock_data_start + table->lock_count;
        /* Move down all lock data pointers above 'table->lock_data_end-1' */
        bmove((char*) (locked->locks + table->lock_data_start),
              (char*) (locked->locks + lock_data_end),
              (locked->lock_count - lock_data_end) *
              sizeof(THR_LOCK_DATA*));

        /*
          Fix moved table elements.
          lock_position is the index in the 'locked->table' array,
          it must be fixed by one.
          table->lock_data_start is pointer to the lock data for this table
          in the 'locked->locks' array, they must be fixed by 'removed_locks',
          the lock data count of the removed table.
        */
        for (j= i ; j < old_tables; j++)
        {
          tbl= locked->table[j];
          tbl->lock_position--;
          DBUG_ASSERT(tbl->lock_position == j);
          tbl->lock_data_start-= removed_locks;
        }

        /* Finally adjust lock_count. */
        locked->lock_count-= removed_locks;
unknown's avatar
unknown committed
547 548 549 550 551 552
	break;
      }
    }
  }
}

unknown's avatar
unknown committed
553 554 555 556 557 558 559
/* Downgrade all locks on a table to new WRITE level from WRITE_ONLY */

void mysql_lock_downgrade_write(THD *thd, TABLE *table,
                                thr_lock_type new_lock_type)
{
  MYSQL_LOCK *locked;
  TABLE *write_lock_used;
560 561
  if ((locked = get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
                              &write_lock_used)))
unknown's avatar
unknown committed
562 563 564
  {
    for (uint i=0; i < locked->lock_count; i++)
      thr_downgrade_write_lock(locked->locks[i], new_lock_type);
565
    my_free((uchar*) locked,MYF(0));
unknown's avatar
unknown committed
566 567 568 569
  }
}


unknown's avatar
unknown committed
570 571
/* abort all other threads waiting to get lock in table */

unknown's avatar
unknown committed
572
void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock)
unknown's avatar
unknown committed
573 574 575
{
  MYSQL_LOCK *locked;
  TABLE *write_lock_used;
unknown's avatar
unknown committed
576 577
  DBUG_ENTER("mysql_lock_abort");

578 579
  if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
                             &write_lock_used)))
unknown's avatar
unknown committed
580 581
  {
    for (uint i=0; i < locked->lock_count; i++)
unknown's avatar
unknown committed
582
      thr_abort_locks(locked->locks[i]->lock, upgrade_lock);
583
    my_free((uchar*) locked,MYF(0));
unknown's avatar
unknown committed
584
  }
unknown's avatar
unknown committed
585
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
586 587 588
}


unknown's avatar
unknown committed
589 590 591 592 593 594 595 596 597 598 599 600
/*
  Abort one thread / table combination

  SYNOPSIS
    mysql_lock_abort_for_thread()
    thd		Thread handler
    table	Table that should be removed from lock queue

  RETURN
    0  Table was not locked by another thread
    1  Table was locked by at least one other thread
*/
601

unknown's avatar
unknown committed
602
bool mysql_lock_abort_for_thread(THD *thd, TABLE *table)
603 604 605
{
  MYSQL_LOCK *locked;
  TABLE *write_lock_used;
unknown's avatar
unknown committed
606
  bool result= FALSE;
607 608
  DBUG_ENTER("mysql_lock_abort_for_thread");

609 610
  if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
                             &write_lock_used)))
611 612
  {
    for (uint i=0; i < locked->lock_count; i++)
unknown's avatar
unknown committed
613
    {
unknown's avatar
unknown committed
614
      if (thr_abort_locks_for_thread(locked->locks[i]->lock,
unknown's avatar
unknown committed
615
                                     table->in_use->thread_id))
unknown's avatar
unknown committed
616
        result= TRUE;
unknown's avatar
unknown committed
617
    }
618
    my_free((uchar*) locked,MYF(0));
619
  }
unknown's avatar
unknown committed
620
  DBUG_RETURN(result);
621 622 623
}


unknown's avatar
unknown committed
624 625 626
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
{
  MYSQL_LOCK *sql_lock;
627
  TABLE **table, **end_table;
unknown's avatar
unknown committed
628
  DBUG_ENTER("mysql_lock_merge");
629

unknown's avatar
unknown committed
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
  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));
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659

  /*
    Now adjust lock_position and lock_data_start for all objects that was
    moved in 'b' (as there is now all objects in 'a' before these).
  */
  for (table= sql_lock->table + a->table_count,
         end_table= table + b->table_count;
       table < end_table;
       table++)
  {
    (*table)->lock_position+=   a->table_count;
    (*table)->lock_data_start+= a->lock_count;
  }

  /* Delete old, not needed locks */
660 661
  my_free((uchar*) a,MYF(0));
  my_free((uchar*) b,MYF(0));
unknown's avatar
unknown committed
662 663 664 665
  DBUG_RETURN(sql_lock);
}


666 667 668 669 670 671 672 673 674 675 676 677
/*
  Find duplicate lock in tables.

  SYNOPSIS
    mysql_lock_have_duplicate()
    thd                         The current thread.
    needle                      The table to check for duplicate lock.
    haystack                    The list of tables to search for the dup lock.

  NOTE
    This is mainly meant for MERGE tables in INSERT ... SELECT
    situations. The 'real', underlying tables can be found only after
678 679 680 681 682 683
    the MERGE tables are opened. This function assumes that the tables are
    already locked.

    Temporary tables are ignored here like they are ignored in
    get_lock_data(). If we allow two opens on temporary tables later,
    both functions should be checked.
684 685

  RETURN
686 687
    NULL        No duplicate lock found.
    ! NULL      First table from 'haystack' that matches a lock on 'needle'.
688 689 690 691 692
*/

TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle,
                                      TABLE_LIST *haystack)
{
693 694 695 696 697 698 699
  MYSQL_LOCK            *mylock;
  TABLE                 **lock_tables;
  TABLE                 *table;
  TABLE                 *table2;
  THR_LOCK_DATA         **lock_locks;
  THR_LOCK_DATA         **table_lock_data;
  THR_LOCK_DATA         **end_data;
700 701 702 703 704
  THR_LOCK_DATA         **lock_data2;
  THR_LOCK_DATA         **end_data2;
  DBUG_ENTER("mysql_lock_have_duplicate");

  /*
705 706
    Table may not be defined for derived or view tables.
    Table may not be part of a lock for delayed operations.
707
  */
708
  if (! (table= needle->table) || ! table->lock_count)
709 710 711
    goto end;

  /* A temporary table does not have locks. */
712
  if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
713 714 715 716 717 718 719 720 721 722 723 724 725 726
    goto end;

  /* Get command lock or LOCK TABLES lock. Maybe empty for INSERT DELAYED. */
  if (! (mylock= thd->lock ? thd->lock : thd->locked_tables))
    goto end;

  /* If we have less than two tables, we cannot have duplicates. */
  if (mylock->table_count < 2)
    goto end;

  lock_locks=  mylock->locks;
  lock_tables= mylock->table;

  /* Prepare table related variables that don't change in loop. */
727 728
  DBUG_ASSERT((table->lock_position < mylock->table_count) &&
              (table == lock_tables[table->lock_position]));
729 730 731 732
  table_lock_data= lock_locks + table->lock_data_start;
  end_data= table_lock_data + table->lock_count;

  for (; haystack; haystack= haystack->next_global)
733
  {
734
    if (haystack->placeholder())
735 736
      continue;
    table2= haystack->table;
737
    if (table2->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
738 739 740
      continue;

    /* All tables in list must be in lock. */
741 742
    DBUG_ASSERT((table2->lock_position < mylock->table_count) &&
                (table2 == lock_tables[table2->lock_position]));
743 744 745

    for (lock_data2=  lock_locks + table2->lock_data_start,
           end_data2= lock_data2 + table2->lock_count;
746 747 748
         lock_data2 < end_data2;
         lock_data2++)
    {
749 750 751 752 753 754
      THR_LOCK_DATA **lock_data;
      THR_LOCK *lock2= (*lock_data2)->lock;

      for (lock_data= table_lock_data;
           lock_data < end_data;
           lock_data++)
755
      {
756 757 758 759 760
        if ((*lock_data)->lock == lock2)
        {
          DBUG_PRINT("info", ("haystack match: '%s'", haystack->table_name));
          DBUG_RETURN(haystack);
        }
761 762 763 764 765
      }
    }
  }

 end:
766 767
  DBUG_PRINT("info", ("no duplicate found"));
  DBUG_RETURN(NULL);
768 769 770
}


unknown's avatar
unknown committed
771 772 773 774 775 776 777 778
	/* 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;
779
  do
unknown's avatar
unknown committed
780 781 782 783
  {
    if ((*table)->current_lock != F_UNLCK)
    {
      (*table)->current_lock = F_UNLCK;
784
      if ((error=(*table)->file->ha_external_lock(thd, F_UNLCK)))
785
      {
unknown's avatar
unknown committed
786
	error_code=error;
787 788
	print_lock_error(error_code, (*table)->file->table_type());
      }
unknown's avatar
unknown committed
789
    }
790 791
    table++;
  } while (--count);
unknown's avatar
unknown committed
792 793 794 795 796
  DBUG_RETURN(error_code);
}


/*
797 798 799 800 801 802 803 804 805 806 807
  Get lock structures from table structs and initialize locks

  SYNOPSIS
    get_lock_data()
    thd			Thread handler
    table_ptr		Pointer to tables that should be locks
    flags		One of:
			GET_LOCK_UNLOCK:      If we should send TL_IGNORE to
                        		      store lock
			GET_LOCK_STORE_LOCKS: Store lock info in TABLE
    write_lock_used	Store pointer to last table with WRITE_ALLOW_WRITE
unknown's avatar
unknown committed
808 809 810 811
*/


static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
812
				 uint flags, TABLE **write_lock_used)
unknown's avatar
unknown committed
813 814 815
{
  uint i,tables,lock_count;
  MYSQL_LOCK *sql_lock;
816 817
  THR_LOCK_DATA **locks, **locks_buf, **locks_start;
  TABLE **to, **table_buf;
unknown's avatar
unknown committed
818
  DBUG_ENTER("get_lock_data");
819

820 821
  DBUG_ASSERT((flags == GET_LOCK_UNLOCK) || (flags == GET_LOCK_STORE_LOCKS));

unknown's avatar
unknown committed
822
  DBUG_PRINT("info", ("count %d", count));
unknown's avatar
unknown committed
823 824 825
  *write_lock_used=0;
  for (i=tables=lock_count=0 ; i < count ; i++)
  {
826 827 828
    TABLE *t= table_ptr[i];

    if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE)
unknown's avatar
unknown committed
829
    {
830
      tables+= t->file->lock_count();
unknown's avatar
unknown committed
831 832 833 834
      lock_count++;
    }
  }

835 836 837 838 839 840
  /*
    Allocating twice the number of pointers for lock data for use in
    thr_mulit_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().
  */
unknown's avatar
unknown committed
841
  if (!(sql_lock= (MYSQL_LOCK*)
842 843 844
	my_malloc(sizeof(*sql_lock) +
		  sizeof(THR_LOCK_DATA*) * tables * 2 +
                  sizeof(table_ptr) * lock_count,
unknown's avatar
unknown committed
845
		  MYF(0))))
unknown's avatar
unknown committed
846
    DBUG_RETURN(0);
847
  locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1);
848
  to= table_buf= sql_lock->table= (TABLE**) (locks + tables * 2);
unknown's avatar
unknown committed
849 850
  sql_lock->table_count=lock_count;
  sql_lock->lock_count=tables;
unknown's avatar
unknown committed
851 852
  DBUG_PRINT("info", ("sql_lock->table_count %d sql_lock->lock_count %d",
                      sql_lock->table_count, sql_lock->lock_count));
unknown's avatar
unknown committed
853 854 855 856

  for (i=0 ; i < count ; i++)
  {
    TABLE *table;
857 858
    enum thr_lock_type lock_type;

859
    if ((table=table_ptr[i])->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
unknown's avatar
unknown committed
860
      continue;
861
    lock_type= table->reginfo.lock_type;
862
    DBUG_ASSERT (lock_type != TL_WRITE_DEFAULT);
unknown's avatar
unknown committed
863 864 865 866 867
    if (lock_type >= TL_WRITE_ALLOW_WRITE)
    {
      *write_lock_used=table;
      if (table->db_stat & HA_READ_ONLY)
      {
868
	my_error(ER_OPEN_AS_READONLY,MYF(0),table->alias);
869 870 871
        /* Clear the lock type of the lock data that are stored already. */
        sql_lock->lock_count= locks - sql_lock->locks;
        reset_lock_data(sql_lock);
872
	my_free((uchar*) sql_lock,MYF(0));
unknown's avatar
unknown committed
873
	DBUG_RETURN(0);
unknown's avatar
unknown committed
874 875
      }
    }
unknown's avatar
unknown committed
876
    THR_LOCK_DATA **org_locks = locks;
877 878 879 880 881 882 883 884 885 886 887
    locks_start= locks;
    locks= table->file->store_lock(thd, locks,
                                   (flags & GET_LOCK_UNLOCK) ? TL_IGNORE :
                                   lock_type);
    if (flags & GET_LOCK_STORE_LOCKS)
    {
      table->lock_position=   (uint) (to - table_buf);
      table->lock_data_start= (uint) (locks_start - locks_buf);
      table->lock_count=      (uint) (locks - locks_start);
    }
    *to++= table;
unknown's avatar
unknown committed
888 889 890
    if (locks)
      for ( ; org_locks != locks ; org_locks++)
	(*org_locks)->debug_print_param= (void *) table;
unknown's avatar
unknown committed
891
  }
unknown's avatar
unknown committed
892
  DBUG_RETURN(sql_lock);
unknown's avatar
unknown committed
893
}
894

895

896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
/*
  Reset lock type in lock data.

  SYNOPSIS
    reset_lock_data()
      sql_lock                  The MySQL lock.

  DESCRIPTION

    After a locking error we want to quit the locking of the table(s).
    The test case in the bug report for Bug #18544 has the following
    cases: 1. Locking error in lock_external() due to InnoDB timeout.
    2. Locking error in get_lock_data() due to missing write permission.
    3. Locking error in wait_if_global_read_lock() due to lock conflict.

    In all these cases we have already set the lock type into the lock
    data of the open table(s). If the table(s) are in the open table
    cache, they could be reused with the non-zero lock type set. This
    could lead to ignoring a different lock type with the next lock.

    Clear the lock type of all lock data. This ensures that the next
    lock request will set its lock type properly.

  RETURN
    void
*/

static void reset_lock_data(MYSQL_LOCK *sql_lock)
{
  THR_LOCK_DATA **ldata;
  THR_LOCK_DATA **ldata_end;

  for (ldata= sql_lock->locks, ldata_end= ldata + sql_lock->lock_count;
       ldata < ldata_end;
       ldata++)
  {
    /* Reset lock type. */
    (*ldata)->type= TL_UNLOCK;
  }
}


938
/*****************************************************************************
939 940
  Lock table based on the name.
  This is used when we need total access to a closed, not open table
941 942
*****************************************************************************/

943 944
/*
  Lock and wait for the named lock.
945 946 947 948 949 950 951 952 953 954 955 956 957

  SYNOPSIS
    lock_and_wait_for_table_name()
    thd			Thread handler
    table_list		Lock first table in this list


  NOTES
    Works together with global read lock.

  RETURN
    0	ok
    1	error
958 959 960 961 962 963 964 965
*/

int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list)
{
  int lock_retcode;
  int error= -1;
  DBUG_ENTER("lock_and_wait_for_table_name");

966
  if (wait_if_global_read_lock(thd, 0, 1))
967 968
    DBUG_RETURN(1);
  VOID(pthread_mutex_lock(&LOCK_open));
969
  if ((lock_retcode = lock_table_name(thd, table_list, TRUE)) < 0)
970 971 972 973 974 975 976 977 978 979
    goto end;
  if (lock_retcode && wait_for_locked_table_names(thd, table_list))
  {
    unlock_table_name(thd, table_list);
    goto end;
  }
  error=0;

end:
  pthread_mutex_unlock(&LOCK_open);
unknown's avatar
unknown committed
980
  start_waiting_global_read_lock(thd);
981 982 983 984
  DBUG_RETURN(error);
}


985 986
/*
  Put a not open table with an old refresh version in the table cache.
987 988 989 990 991

  SYNPOSIS
    lock_table_name()
    thd			Thread handler
    table_list		Lock first table in this list
992
    check_in_use        Do we need to check if table already in use by us
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009

  WARNING
    If you are going to update the table, you should use
    lock_and_wait_for_table_name instead of this function as this works
    together with 'FLUSH TABLES WITH READ LOCK'

  NOTES
    This will force any other threads that uses the table to release it
    as soon as possible.

  REQUIREMENTS
    One must have a lock on LOCK_open !

  RETURN:
    < 0 error
    == 0 table locked
    > 0  table locked, but someone is using it
1010 1011
*/

1012
int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
1013 1014 1015
{
  TABLE *table;
  char  key[MAX_DBKEY_LENGTH];
unknown's avatar
unknown committed
1016
  char *db= table_list->db;
1017
  uint  key_length;
1018
  HASH_SEARCH_STATE state;
unknown's avatar
unknown committed
1019
  DBUG_ENTER("lock_table_name");
1020
  DBUG_PRINT("enter",("db: %s  name: %s", db, table_list->table_name));
1021

unknown's avatar
unknown committed
1022
  key_length= create_table_def_key(thd, key, table_list, 0);
1023

1024
  if (check_in_use)
unknown's avatar
unknown committed
1025
  {
1026
    /* Only insert the table if we haven't insert it already */
1027
    for (table=(TABLE*) hash_first(&open_cache, (uchar*)key,
1028 1029
                                   key_length, &state);
         table ;
1030
         table = (TABLE*) hash_next(&open_cache,(uchar*) key,
1031
                                    key_length, &state))
unknown's avatar
unknown committed
1032
    {
1033 1034 1035 1036 1037 1038 1039
      if (table->in_use == thd)
      {
        DBUG_PRINT("info", ("Table is in use"));
        table->s->version= 0;                  // Ensure no one can use this
        table->locked_by_name= 1;
        DBUG_RETURN(0);
      }
unknown's avatar
unknown committed
1040 1041
    }
  }
1042

unknown's avatar
unknown committed
1043
  if (!(table= table_cache_insert_placeholder(thd, key, key_length)))
unknown's avatar
unknown committed
1044
    DBUG_RETURN(-1);
unknown's avatar
unknown committed
1045 1046

  table_list->table=table;
1047

unknown's avatar
unknown committed
1048
  /* Return 1 if table is in use */
unknown's avatar
unknown committed
1049
  DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name,
1050
             check_in_use ? RTFC_NO_FLAG : RTFC_WAIT_OTHER_THREAD_FLAG)));
1051 1052
}

1053

1054 1055 1056
void unlock_table_name(THD *thd, TABLE_LIST *table_list)
{
  if (table_list->table)
unknown's avatar
unknown committed
1057
  {
1058
    hash_delete(&open_cache, (uchar*) table_list->table);
1059
    broadcast_refresh();
unknown's avatar
unknown committed
1060
  }
1061 1062
}

1063

1064 1065
static bool locked_named_table(THD *thd, TABLE_LIST *table_list)
{
unknown's avatar
VIEW  
unknown committed
1066
  for (; table_list ; table_list=table_list->next_local)
1067
  {
unknown's avatar
unknown committed
1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
    TABLE *table= table_list->table;
    if (table)
    {
      TABLE *save_next= table->next;
      bool result;
      table->next= 0;
      result= table_is_used(table_list->table, 0);
      table->next= save_next;
      if (result)
        return 1;
    }
1079 1080 1081 1082 1083 1084 1085 1086
  }
  return 0;					// All tables are locked
}


bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list)
{
  bool result=0;
unknown's avatar
unknown committed
1087
  DBUG_ENTER("wait_for_locked_table_names");
unknown's avatar
unknown committed
1088

1089
  safe_mutex_assert_owner(&LOCK_open);
1090 1091 1092 1093 1094 1095 1096 1097

  while (locked_named_table(thd,table_list))
  {
    if (thd->killed)
    {
      result=1;
      break;
    }
unknown's avatar
unknown committed
1098
    wait_for_condition(thd, &LOCK_open, &COND_refresh);
unknown's avatar
unknown committed
1099
    pthread_mutex_lock(&LOCK_open);
1100
  }
unknown's avatar
unknown committed
1101
  DBUG_RETURN(result);
1102
}
1103

1104 1105 1106 1107 1108 1109 1110 1111 1112 1113

/*
  Lock all tables in list with a name lock

  SYNOPSIS
    lock_table_names()
    thd			Thread handle
    table_list		Names of tables to lock

  NOTES
1114 1115 1116 1117
    If you are just locking one table, you should use
    lock_and_wait_for_table_name().

  REQUIREMENTS
1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
    One must have a lock on LOCK_open when calling this

  RETURN
    0	ok
    1	Fatal error (end of memory ?)
*/

bool lock_table_names(THD *thd, TABLE_LIST *table_list)
{
  bool got_all_locks=1;
  TABLE_LIST *lock_table;

unknown's avatar
VIEW  
unknown committed
1130
  for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
1131 1132
  {
    int got_lock;
1133
    if ((got_lock=lock_table_name(thd,lock_table, TRUE)) < 0)
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
      goto end;					// Fatal error
    if (got_lock)
      got_all_locks=0;				// Someone is using table
  }

  /* If some table was in use, wait until we got the lock */
  if (!got_all_locks && wait_for_locked_table_names(thd, table_list))
    goto end;
  return 0;

end:
  unlock_table_names(thd, table_list, lock_table);
  return 1;
}


1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
/**
  @brief Lock all tables in list with an exclusive table name lock.

  @param thd Thread handle.
  @param table_list Names of tables to lock.

  @note This function needs to be protected by LOCK_open. If we're 
    under LOCK TABLES, this function does not work as advertised. Namely,
    it does not exclude other threads from using this table and does not
    put an exclusive name lock on this table into the table cache.

  @see lock_table_names
  @see unlock_table_names

  @retval TRUE An error occured.
  @retval FALSE Name lock successfully acquired.
*/

bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list)
{
  if (lock_table_names(thd, table_list))
    return TRUE;

  /*
    Upgrade the table name locks from semi-exclusive to exclusive locks.
  */
  for (TABLE_LIST *table= table_list; table; table= table->next_global)
  {
    if (table->table)
      table->table->open_placeholder= 1;
  }
  return FALSE;
}


/**
  @brief Test is 'table' is protected by an exclusive name lock.

unknown's avatar
unknown committed
1188 1189 1190
  @param[in] thd        The current thread handler
  @param[in] table_list Table container containing the single table to be
                        tested
1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215

  @note Needs to be protected by LOCK_open mutex.

  @return Error status code
    @retval TRUE Table is protected
    @retval FALSE Table is not protected
*/

bool
is_table_name_exclusively_locked_by_this_thread(THD *thd,
                                                TABLE_LIST *table_list)
{
  char  key[MAX_DBKEY_LENGTH];
  uint  key_length;

  key_length= create_table_def_key(thd, key, table_list, 0);

  return is_table_name_exclusively_locked_by_this_thread(thd, (uchar *)key,
                                                         key_length);
}


/**
  @brief Test is 'table key' is protected by an exclusive name lock.

unknown's avatar
unknown committed
1216 1217 1218
  @param[in] thd        The current thread handler.
  @param[in] key
  @param[in] key_length
1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247

  @note Needs to be protected by LOCK_open mutex

  @retval TRUE Table is protected
  @retval FALSE Table is not protected
 */

bool
is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key,
                                                int key_length)
{
  HASH_SEARCH_STATE state;
  TABLE *table;

  for (table= (TABLE*) hash_first(&open_cache, key,
                                  key_length, &state);
       table ;
       table= (TABLE*) hash_next(&open_cache, key,
                                 key_length, &state))
  {
    if (table->in_use == thd &&
        table->open_placeholder == 1 &&
        table->s->version == 0)
      return TRUE;
  }

  return FALSE;
}

1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258
/*
  Unlock all tables in list with a name lock

  SYNOPSIS
    unlock_table_names()
    thd			Thread handle
    table_list		Names of tables to unlock
    last_table		Don't unlock any tables after this one.
			(default 0, which will unlock all tables)

  NOTES
1259 1260 1261
    One must have a lock on LOCK_open when calling this.
    This function will broadcast refresh signals to inform other threads
    that the name locks are removed.
1262 1263 1264 1265 1266 1267 1268 1269 1270

  RETURN
    0	ok
    1	Fatal error (end of memory ?)
*/

void unlock_table_names(THD *thd, TABLE_LIST *table_list,
			TABLE_LIST *last_table)
{
1271
  DBUG_ENTER("unlock_table_names");
unknown's avatar
VIEW  
unknown committed
1272 1273 1274
  for (TABLE_LIST *table= table_list;
       table != last_table;
       table= table->next_local)
1275
    unlock_table_name(thd,table);
1276
  broadcast_refresh();
1277
  DBUG_VOID_RETURN;
1278 1279 1280
}


1281
static void print_lock_error(int error, const char *table)
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292
{
  int textno;
  DBUG_ENTER("print_lock_error");

  switch (error) {
  case HA_ERR_LOCK_WAIT_TIMEOUT:
    textno=ER_LOCK_WAIT_TIMEOUT;
    break;
  case HA_ERR_READ_ONLY_TRANSACTION:
    textno=ER_READ_ONLY_TRANSACTION;
    break;
1293 1294 1295
  case HA_ERR_LOCK_DEADLOCK:
    textno=ER_LOCK_DEADLOCK;
    break;
1296 1297 1298
  case HA_ERR_WRONG_COMMAND:
    textno=ER_ILLEGAL_HA;
    break;
1299 1300 1301 1302
  default:
    textno=ER_CANT_LOCK;
    break;
  }
1303 1304 1305 1306 1307 1308

  if ( textno == ER_ILLEGAL_HA )
    my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), table);
  else
    my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), error);

1309 1310 1311
  DBUG_VOID_RETURN;
}

1312 1313 1314 1315

/****************************************************************************
  Handling of global read locks

1316 1317 1318 1319
  Taking the global read lock is TWO steps (2nd step is optional; without
  it, COMMIT of existing transactions will be allowed):
  lock_global_read_lock() THEN make_global_read_lock_block_commit().

1320 1321
  The global locks are handled through the global variables:
  global_read_lock
1322 1323
    count of threads which have the global read lock (i.e. have completed at
    least the first step above)
1324
  global_read_lock_blocks_commit
1325
    count of threads which have the global read lock and block
1326
    commits (i.e. are in or have completed the second step above)
1327 1328
  waiting_for_read_lock
    count of threads which want to take a global read lock but cannot
1329
  protect_against_global_read_lock
1330 1331
    count of threads which have set protection against global read lock.

unknown's avatar
unknown committed
1332 1333
  access to them is protected with a mutex LOCK_global_read_lock

1334 1335 1336 1337 1338 1339 1340
  (XXX: one should never take LOCK_open if LOCK_global_read_lock is
  taken, otherwise a deadlock may occur. Other mutexes could be a
  problem too - grep the code for global_read_lock if you want to use
  any other mutex here) Also one must not hold LOCK_open when calling
  wait_if_global_read_lock(). When the thread with the global read lock
  tries to close its tables, it needs to take LOCK_open in
  close_thread_table().
unknown's avatar
unknown committed
1341

1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378
  How blocking of threads by global read lock is achieved: that's
  advisory. Any piece of code which should be blocked by global read lock must
  be designed like this:
  - call to wait_if_global_read_lock(). When this returns 0, no global read
  lock is owned; if argument abort_on_refresh was 0, none can be obtained.
  - job
  - if abort_on_refresh was 0, call to start_waiting_global_read_lock() to
  allow other threads to get the global read lock. I.e. removal of the
  protection.
  (Note: it's a bit like an implementation of rwlock).

  [ I am sorry to mention some SQL syntaxes below I know I shouldn't but found
  no better descriptive way ]

  Why does FLUSH TABLES WITH READ LOCK need to block COMMIT: because it's used
  to read a non-moving SHOW MASTER STATUS, and a COMMIT writes to the binary
  log.

  Why getting the global read lock is two steps and not one. Because FLUSH
  TABLES WITH READ LOCK needs to insert one other step between the two:
  flushing tables. So the order is
  1) lock_global_read_lock() (prevents any new table write locks, i.e. stalls
  all new updates)
  2) close_cached_tables() (the FLUSH TABLES), which will wait for tables
  currently opened and being updated to close (so it's possible that there is
  a moment where all new updates of server are stalled *and* FLUSH TABLES WITH
  READ LOCK is, too).
  3) make_global_read_lock_block_commit().
  If we have merged 1) and 3) into 1), we would have had this deadlock:
  imagine thread 1 and 2, in non-autocommit mode, thread 3, and an InnoDB
  table t.
  thd1: SELECT * FROM t FOR UPDATE;
  thd2: UPDATE t SET a=1; # blocked by row-level locks of thd1
  thd3: FLUSH TABLES WITH READ LOCK; # blocked in close_cached_tables() by the
  table instance of thd2
  thd1: COMMIT; # blocked by thd3.
  thd1 blocks thd2 which blocks thd3 which blocks thd1: deadlock.
unknown's avatar
unknown committed
1379

1380 1381 1382 1383 1384 1385
  Note that we need to support that one thread does
  FLUSH TABLES WITH READ LOCK; and then COMMIT;
  (that's what innobackup does, for some good reason).
  So in this exceptional case the COMMIT should not be blocked by the FLUSH
  TABLES WITH READ LOCK.

1386 1387 1388
****************************************************************************/

volatile uint global_read_lock=0;
1389
volatile uint global_read_lock_blocks_commit=0;
1390 1391 1392
static volatile uint protect_against_global_read_lock=0;
static volatile uint waiting_for_read_lock=0;

1393 1394 1395
#define GOT_GLOBAL_READ_LOCK               1
#define MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT 2

1396 1397 1398 1399 1400 1401
bool lock_global_read_lock(THD *thd)
{
  DBUG_ENTER("lock_global_read_lock");

  if (!thd->global_read_lock)
  {
unknown's avatar
unknown committed
1402
    const char *old_message;
1403
    (void) pthread_mutex_lock(&LOCK_global_read_lock);
unknown's avatar
unknown committed
1404 1405
    old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
                                "Waiting to get readlock");
unknown's avatar
unknown committed
1406 1407 1408 1409
    DBUG_PRINT("info",
	       ("waiting_for: %d  protect_against: %d",
		waiting_for_read_lock, protect_against_global_read_lock));

1410 1411
    waiting_for_read_lock++;
    while (protect_against_global_read_lock && !thd->killed)
unknown's avatar
unknown committed
1412
      pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
1413 1414 1415
    waiting_for_read_lock--;
    if (thd->killed)
    {
unknown's avatar
unknown committed
1416
      thd->exit_cond(old_message);
1417 1418
      DBUG_RETURN(1);
    }
1419
    thd->global_read_lock= GOT_GLOBAL_READ_LOCK;
1420
    global_read_lock++;
unknown's avatar
unknown committed
1421
    thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
1422
  }
1423 1424 1425 1426 1427 1428 1429 1430
  /*
    We DON'T set global_read_lock_blocks_commit now, it will be set after
    tables are flushed (as the present function serves for FLUSH TABLES WITH
    READ LOCK only). Doing things in this order is necessary to avoid
    deadlocks (we must allow COMMIT until all tables are closed; we should not
    forbid it before, or we can have a 3-thread deadlock if 2 do SELECT FOR
    UPDATE and one does FLUSH TABLES WITH READ LOCK).
  */
1431 1432 1433
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
1434

1435 1436 1437
void unlock_global_read_lock(THD *thd)
{
  uint tmp;
unknown's avatar
unknown committed
1438 1439 1440 1441 1442
  DBUG_ENTER("unlock_global_read_lock");
  DBUG_PRINT("info",
             ("global_read_lock: %u  global_read_lock_blocks_commit: %u",
              global_read_lock, global_read_lock_blocks_commit));

1443
  pthread_mutex_lock(&LOCK_global_read_lock);
1444
  tmp= --global_read_lock;
1445 1446
  if (thd->global_read_lock == MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT)
    --global_read_lock_blocks_commit;
1447
  pthread_mutex_unlock(&LOCK_global_read_lock);
1448 1449
  /* Send the signal outside the mutex to avoid a context switch */
  if (!tmp)
unknown's avatar
unknown committed
1450 1451 1452 1453
  {
    DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock"));
    pthread_cond_broadcast(&COND_global_read_lock);
  }
1454
  thd->global_read_lock= 0;
unknown's avatar
unknown committed
1455 1456

  DBUG_VOID_RETURN;
1457 1458
}

1459 1460 1461
#define must_wait (global_read_lock &&                             \
                   (is_not_commit ||                               \
                    global_read_lock_blocks_commit))
1462

1463 1464
bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
                              bool is_not_commit)
1465 1466
{
  const char *old_message;
unknown's avatar
unknown committed
1467
  bool result= 0, need_exit_cond;
1468 1469
  DBUG_ENTER("wait_if_global_read_lock");

unknown's avatar
unknown committed
1470
  LINT_INIT(old_message);
1471 1472 1473 1474 1475 1476 1477
  /*
    Assert that we do not own LOCK_open. If we would own it, other
    threads could not close their tables. This would make a pretty
    deadlock.
  */
  safe_mutex_assert_not_owner(&LOCK_open);

1478
  (void) pthread_mutex_lock(&LOCK_global_read_lock);
1479
  if ((need_exit_cond= must_wait))
1480 1481 1482
  {
    if (thd->global_read_lock)		// This thread had the read locks
    {
1483
      if (is_not_commit)
unknown's avatar
merge  
unknown committed
1484 1485
        my_message(ER_CANT_UPDATE_WITH_READLOCK,
                   ER(ER_CANT_UPDATE_WITH_READLOCK), MYF(0));
1486
      (void) pthread_mutex_unlock(&LOCK_global_read_lock);
1487 1488 1489 1490 1491 1492
      /*
        We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
        This allowance is needed to not break existing versions of innobackup
        which do a BEGIN; INSERT; FLUSH TABLES WITH READ LOCK; COMMIT.
      */
      DBUG_RETURN(is_not_commit);
unknown's avatar
unknown committed
1493
    }
unknown's avatar
unknown committed
1494
    old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
1495
				"Waiting for release of readlock");
1496
    while (must_wait && ! thd->killed &&
1497
	   (!abort_on_refresh || thd->version == refresh_version))
unknown's avatar
unknown committed
1498 1499 1500 1501 1502
    {
      DBUG_PRINT("signal", ("Waiting for COND_global_read_lock"));
      (void) pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
      DBUG_PRINT("signal", ("Got COND_global_read_lock"));
    }
1503 1504 1505 1506 1507
    if (thd->killed)
      result=1;
  }
  if (!abort_on_refresh && !result)
    protect_against_global_read_lock++;
1508 1509 1510 1511
  /*
    The following is only true in case of a global read locks (which is rare)
    and if old_message is set
  */
unknown's avatar
unknown committed
1512 1513
  if (unlikely(need_exit_cond))
    thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
unknown's avatar
unknown committed
1514
  else
1515
    pthread_mutex_unlock(&LOCK_global_read_lock);
1516 1517 1518 1519 1520 1521 1522
  DBUG_RETURN(result);
}


void start_waiting_global_read_lock(THD *thd)
{
  bool tmp;
unknown's avatar
unknown committed
1523
  DBUG_ENTER("start_waiting_global_read_lock");
1524 1525
  if (unlikely(thd->global_read_lock))
    DBUG_VOID_RETURN;
1526
  (void) pthread_mutex_lock(&LOCK_global_read_lock);
1527 1528
  tmp= (!--protect_against_global_read_lock &&
        (waiting_for_read_lock || global_read_lock_blocks_commit));
1529
  (void) pthread_mutex_unlock(&LOCK_global_read_lock);
1530
  if (tmp)
unknown's avatar
unknown committed
1531
    pthread_cond_broadcast(&COND_global_read_lock);
unknown's avatar
unknown committed
1532
  DBUG_VOID_RETURN;
1533
}
1534 1535


1536
bool make_global_read_lock_block_commit(THD *thd)
1537
{
1538 1539 1540
  bool error;
  const char *old_message;
  DBUG_ENTER("make_global_read_lock_block_commit");
1541 1542 1543 1544 1545
  /*
    If we didn't succeed lock_global_read_lock(), or if we already suceeded
    make_global_read_lock_block_commit(), do nothing.
  */
  if (thd->global_read_lock != GOT_GLOBAL_READ_LOCK)
1546
    DBUG_RETURN(0);
1547
  pthread_mutex_lock(&LOCK_global_read_lock);
1548 1549
  /* increment this BEFORE waiting on cond (otherwise race cond) */
  global_read_lock_blocks_commit++;
1550 1551 1552
  /* For testing we set up some blocking, to see if we can be killed */
  DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
                  protect_against_global_read_lock++;);
unknown's avatar
unknown committed
1553
  old_message= thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
1554 1555
                               "Waiting for all running commits to finish");
  while (protect_against_global_read_lock && !thd->killed)
unknown's avatar
unknown committed
1556
    pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
1557 1558
  DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
                  protect_against_global_read_lock--;);
unknown's avatar
unknown committed
1559
  if ((error= test(thd->killed)))
1560 1561 1562
    global_read_lock_blocks_commit--; // undo what we did
  else
    thd->global_read_lock= MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT;
unknown's avatar
unknown committed
1563
  thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
1564
  DBUG_RETURN(error);
1565
}
1566

1567

1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600
/*
  Broadcast COND_refresh and COND_global_read_lock.

  SYNOPSIS
    broadcast_refresh()
      void                      No parameters.

  DESCRIPTION
    Due to a bug in a threading library it could happen that a signal
    did not reach its target. A condition for this was that the same
    condition variable was used with different mutexes in
    pthread_cond_wait(). Some time ago we changed LOCK_open to
    LOCK_global_read_lock in global read lock handling. So COND_refresh
    was used with LOCK_open and LOCK_global_read_lock.

    We did now also change from COND_refresh to COND_global_read_lock
    in global read lock handling. But now it is necessary to signal
    both conditions at the same time.

  NOTE
    When signalling COND_global_read_lock within the global read lock
    handling, it is not necessary to also signal COND_refresh.

  RETURN
    void
*/

void broadcast_refresh(void)
{
  VOID(pthread_cond_broadcast(&COND_refresh));
  VOID(pthread_cond_broadcast(&COND_global_read_lock));
}

1601 1602 1603
/**
  @} (end of group Locking)
*/