~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/lock.cc

  • Committer: Brian Aker
  • Date: 2009-01-24 09:43:35 UTC
  • Revision ID: brian@gir-3.local-20090124094335-6qdtvc35gl5fvivz
Adding in an example singe thread scheduler

Show diffs side-by-side

added added

removed removed

Lines of Context:
72
72
  Change to use malloc() ONLY when using LOCK TABLES command or when
73
73
  we are forced to use mysql_lock_merge.
74
74
*/
75
 
#include "config.h"
76
 
#include <fcntl.h>
 
75
#include <drizzled/server_includes.h>
77
76
#include <drizzled/error.h>
78
 
#include <drizzled/my_hash.h>
79
 
#include <drizzled/thr_lock.h>
 
77
#include <mysys/hash.h>
 
78
#include <mysys/thr_lock.h>
80
79
#include <drizzled/session.h>
81
80
#include <drizzled/sql_base.h>
82
81
#include <drizzled/lock.h>
83
 
#include "drizzled/pthread_globals.h"
84
 
#include "drizzled/internal/my_sys.h"
85
 
 
86
 
#include <set>
87
 
#include <vector>
88
 
#include <algorithm>
89
 
#include <functional>
90
 
 
91
 
using namespace std;
92
 
 
93
 
namespace drizzled
94
 
{
95
82
 
96
83
/**
97
84
  @defgroup Locking Locking
100
87
 
101
88
extern HASH open_cache;
102
89
 
 
90
/* flags for get_lock_data */
 
91
#define GET_LOCK_UNLOCK         1
 
92
#define GET_LOCK_STORE_LOCKS    2
 
93
 
103
94
static DRIZZLE_LOCK *get_lock_data(Session *session, Table **table,
104
95
                                   uint32_t count,
105
 
                                   bool should_lock, Table **write_locked);
 
96
                                   uint32_t flags, Table **write_locked);
106
97
static int lock_external(Session *session, Table **table,uint32_t count);
107
98
static int unlock_external(Session *session, Table **table,uint32_t count);
108
99
static void print_lock_error(int error, const char *);
175
166
 
176
167
 
177
168
DRIZZLE_LOCK *mysql_lock_tables(Session *session, Table **tables, uint32_t count,
178
 
                                uint32_t flags, bool *need_reopen)
 
169
                              uint32_t flags, bool *need_reopen)
179
170
{
180
171
  DRIZZLE_LOCK *sql_lock;
181
172
  Table *write_lock_used;
182
 
  vector<plugin::StorageEngine *> involved_engines;
183
173
  int rc;
184
174
 
185
175
  *need_reopen= false;
186
176
 
187
177
  for (;;)
188
178
  {
189
 
    if (! (sql_lock= get_lock_data(session, tables, count, true,
 
179
    if (! (sql_lock= get_lock_data(session, tables, count, GET_LOCK_STORE_LOCKS,
190
180
                                   &write_lock_used)))
191
181
      break;
192
182
 
210
200
        goto retry;
211
201
      }
212
202
    }
213
 
    
214
 
    session->set_proc_info("Notify start statement");
215
 
    /*
216
 
     * Here, we advise all storage engines involved in the
217
 
     * statement that we are starting a new statement
218
 
     */
219
 
    if (sql_lock->table_count)
220
 
    {
221
 
      size_t num_tables= sql_lock->table_count;
222
 
      plugin::StorageEngine *engine;
223
 
      set<size_t> involved_slots;
224
 
      for (size_t x= 1; x <= num_tables; x++, tables++)
225
 
      {
226
 
        engine= (*tables)->cursor->engine;
227
 
        if (involved_slots.count(engine->getId()) > 0)
228
 
          continue; /* already added to involved engines */
229
 
        involved_engines.push_back(engine);
230
 
        involved_slots.insert(engine->getId());
231
 
      }
232
 
 
233
 
      for_each(involved_engines.begin(),
234
 
               involved_engines.end(),
235
 
               bind2nd(mem_fun(&plugin::StorageEngine::startStatement), session));
236
 
    }
237
 
 
238
 
    session->set_proc_info("External lock");
239
 
    /*
240
 
     * Here, the call to lock_external() informs the
241
 
     * all engines for all tables used in this statement
242
 
     * of the type of lock that Drizzle intends to take on a 
243
 
     * specific table.
244
 
     */
 
203
 
 
204
    session->set_proc_info("System lock");
245
205
    if (sql_lock->table_count && lock_external(session, sql_lock->table,
246
206
                                               sql_lock->table_count))
247
207
    {
283
243
    else if (!session->open_tables)
284
244
    {
285
245
      // Only using temporary tables, no need to unlock
286
 
      session->some_tables_deleted= 0;
 
246
      session->some_tables_deleted=0;
287
247
      break;
288
248
    }
289
249
    session->set_proc_info(0);
301
261
      type for other tables preserved.
302
262
    */
303
263
    reset_lock_data_and_free(&sql_lock);
304
 
 
305
 
    /*
306
 
     * Notify all involved engines that the
307
 
     * SQL statement has ended
308
 
     */
309
 
    for_each(involved_engines.begin(),
310
 
             involved_engines.end(),
311
 
             bind2nd(mem_fun(&plugin::StorageEngine::endStatement), session));
312
264
retry:
313
265
    if (flags & DRIZZLE_LOCK_NOTIFY_IF_NEED_REOPEN)
314
266
    {
325
277
    if (sql_lock)
326
278
    {
327
279
      mysql_unlock_tables(session,sql_lock);
328
 
      sql_lock= NULL;
 
280
      sql_lock=0;
329
281
    }
330
282
  }
 
283
 
331
284
  session->set_time_after_lock();
332
285
  return (sql_lock);
333
286
}
346
299
         (*tables)->reginfo.lock_type <= TL_READ_NO_INSERT))
347
300
      lock_type=F_RDLCK;
348
301
 
349
 
    if ((error=(*tables)->cursor->ha_external_lock(session,lock_type)))
 
302
    if ((error=(*tables)->file->ha_external_lock(session,lock_type)))
350
303
    {
351
 
      print_lock_error(error, (*tables)->cursor->engine->getName().c_str());
 
304
      print_lock_error(error, (*tables)->file->table_type());
352
305
      while (--i)
353
306
      {
354
307
        tables--;
355
 
        (*tables)->cursor->ha_external_lock(session, F_UNLCK);
 
308
        (*tables)->file->ha_external_lock(session, F_UNLCK);
356
309
        (*tables)->current_lock=F_UNLCK;
357
310
      }
358
 
      return error;
 
311
      return(error);
359
312
    }
360
313
    else
361
314
    {
363
316
      (*tables)->current_lock= lock_type;
364
317
    }
365
318
  }
366
 
  return 0;
 
319
  return(0);
367
320
}
368
321
 
369
322
 
383
336
  This will work even if get_lock_data fails (next unlock will free all)
384
337
*/
385
338
 
386
 
void mysql_unlock_some_tables(Session *session, Table **table, uint32_t count)
 
339
void mysql_unlock_some_tables(Session *session, Table **table,uint32_t count)
387
340
{
388
341
  DRIZZLE_LOCK *sql_lock;
389
342
  Table *write_lock_used;
390
 
  if ((sql_lock= get_lock_data(session, table, count, false,
 
343
  if ((sql_lock= get_lock_data(session, table, count, GET_LOCK_UNLOCK,
391
344
                               &write_lock_used)))
392
345
    mysql_unlock_tables(session, sql_lock);
393
346
}
425
378
  for (i=found=0 ; i < sql_lock->table_count ; i++)
426
379
  {
427
380
    assert(sql_lock->table[i]->lock_position == i);
428
 
    if ((uint32_t) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
 
381
    if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
429
382
    {
430
383
      std::swap(*table, sql_lock->table[i]);
431
384
      table++;
473
426
                          effect is desired.
474
427
*/
475
428
 
476
 
void mysql_lock_remove(Session *session, Table *table)
477
 
{
478
 
  mysql_unlock_some_tables(session, &table, /* table count */ 1);
 
429
void mysql_lock_remove(Session *session, DRIZZLE_LOCK *locked,Table *table,
 
430
                       bool always_unlock)
 
431
{
 
432
  if (always_unlock == true)
 
433
    mysql_unlock_some_tables(session, &table, /* table count */ 1);
 
434
  if (locked)
 
435
  {
 
436
    register uint32_t i;
 
437
    for (i=0; i < locked->table_count; i++)
 
438
    {
 
439
      if (locked->table[i] == table)
 
440
      {
 
441
        uint32_t  j, removed_locks, old_tables;
 
442
        Table *tbl;
 
443
        uint32_t lock_data_end;
 
444
 
 
445
        assert(table->lock_position == i);
 
446
 
 
447
        /* Unlock if not yet unlocked */
 
448
        if (always_unlock == false)
 
449
          mysql_unlock_some_tables(session, &table, /* table count */ 1);
 
450
 
 
451
        /* Decrement table_count in advance, making below expressions easier */
 
452
        old_tables= --locked->table_count;
 
453
 
 
454
        /* The table has 'removed_locks' lock data elements in locked->locks */
 
455
        removed_locks= table->lock_count;
 
456
 
 
457
        /* Move down all table pointers above 'i'. */
 
458
        memmove((locked->table+i), (locked->table+i+1),
 
459
                (old_tables - i) * sizeof(Table*));
 
460
 
 
461
        lock_data_end= table->lock_data_start + table->lock_count;
 
462
        /* Move down all lock data pointers above 'table->lock_data_end-1' */
 
463
        memmove((locked->locks + table->lock_data_start),
 
464
                (locked->locks + lock_data_end),
 
465
                (locked->lock_count - lock_data_end) *
 
466
                sizeof(THR_LOCK_DATA*));
 
467
 
 
468
        /*
 
469
          Fix moved table elements.
 
470
          lock_position is the index in the 'locked->table' array,
 
471
          it must be fixed by one.
 
472
          table->lock_data_start is pointer to the lock data for this table
 
473
          in the 'locked->locks' array, they must be fixed by 'removed_locks',
 
474
          the lock data count of the removed table.
 
475
        */
 
476
        for (j= i ; j < old_tables; j++)
 
477
        {
 
478
          tbl= locked->table[j];
 
479
          tbl->lock_position--;
 
480
          assert(tbl->lock_position == j);
 
481
          tbl->lock_data_start-= removed_locks;
 
482
        }
 
483
 
 
484
        /* Finally adjust lock_count. */
 
485
        locked->lock_count-= removed_locks;
 
486
        break;
 
487
      }
 
488
    }
 
489
  }
 
490
}
 
491
 
 
492
/* Downgrade all locks on a table to new WRITE level from WRITE_ONLY */
 
493
 
 
494
void mysql_lock_downgrade_write(Session *session, Table *table,
 
495
                                thr_lock_type new_lock_type)
 
496
{
 
497
  DRIZZLE_LOCK *locked;
 
498
  Table *write_lock_used;
 
499
  if ((locked = get_lock_data(session, &table, 1, GET_LOCK_UNLOCK,
 
500
                              &write_lock_used)))
 
501
  {
 
502
    for (uint32_t i=0; i < locked->lock_count; i++)
 
503
      thr_downgrade_write_lock(locked->locks[i], new_lock_type);
 
504
    free((unsigned char*) locked);
 
505
  }
479
506
}
480
507
 
481
508
 
482
509
/** Abort all other threads waiting to get lock in table. */
483
510
 
484
 
void mysql_lock_abort(Session *session, Table *table)
 
511
void mysql_lock_abort(Session *session, Table *table, bool upgrade_lock)
485
512
{
486
513
  DRIZZLE_LOCK *locked;
487
514
  Table *write_lock_used;
488
515
 
489
 
  if ((locked= get_lock_data(session, &table, 1, false,
 
516
  if ((locked= get_lock_data(session, &table, 1, GET_LOCK_UNLOCK,
490
517
                             &write_lock_used)))
491
518
  {
492
 
    for (uint32_t x= 0; x < locked->lock_count; x++)
493
 
      thr_abort_locks(locked->locks[x]->lock);
 
519
    for (uint32_t i=0; i < locked->lock_count; i++)
 
520
      thr_abort_locks(locked->locks[i]->lock, upgrade_lock);
494
521
    free((unsigned char*) locked);
495
522
  }
 
523
  return;
496
524
}
497
525
 
498
526
 
514
542
  Table *write_lock_used;
515
543
  bool result= false;
516
544
 
517
 
  if ((locked= get_lock_data(session, &table, 1, false,
 
545
  if ((locked= get_lock_data(session, &table, 1, GET_LOCK_UNLOCK,
518
546
                             &write_lock_used)))
519
547
  {
520
548
    for (uint32_t i=0; i < locked->lock_count; i++)
525
553
    }
526
554
    free((unsigned char*) locked);
527
555
  }
528
 
  return result;
529
 
}
 
556
  return(result);
 
557
}
 
558
 
 
559
 
 
560
DRIZZLE_LOCK *mysql_lock_merge(DRIZZLE_LOCK *a,DRIZZLE_LOCK *b)
 
561
{
 
562
  DRIZZLE_LOCK *sql_lock;
 
563
  Table **table, **end_table;
 
564
 
 
565
  if (!(sql_lock= (DRIZZLE_LOCK*)
 
566
        malloc(sizeof(*sql_lock)+
 
567
               sizeof(THR_LOCK_DATA*)*(a->lock_count+b->lock_count)+
 
568
               sizeof(Table*)*(a->table_count+b->table_count))))
 
569
    return(0);                          // Fatal error
 
570
  sql_lock->lock_count=a->lock_count+b->lock_count;
 
571
  sql_lock->table_count=a->table_count+b->table_count;
 
572
  sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
 
573
  sql_lock->table=(Table**) (sql_lock->locks+sql_lock->lock_count);
 
574
  memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks));
 
575
  memcpy(sql_lock->locks+a->lock_count,b->locks,
 
576
         b->lock_count*sizeof(*b->locks));
 
577
  memcpy(sql_lock->table,a->table,a->table_count*sizeof(*a->table));
 
578
  memcpy(sql_lock->table+a->table_count,b->table,
 
579
         b->table_count*sizeof(*b->table));
 
580
 
 
581
  /*
 
582
    Now adjust lock_position and lock_data_start for all objects that was
 
583
    moved in 'b' (as there is now all objects in 'a' before these).
 
584
  */
 
585
  for (table= sql_lock->table + a->table_count,
 
586
         end_table= table + b->table_count;
 
587
       table < end_table;
 
588
       table++)
 
589
  {
 
590
    (*table)->lock_position+=   a->table_count;
 
591
    (*table)->lock_data_start+= a->lock_count;
 
592
  }
 
593
 
 
594
  /* Delete old, not needed locks */
 
595
  free((unsigned char*) a);
 
596
  free((unsigned char*) b);
 
597
  return(sql_lock);
 
598
}
 
599
 
 
600
 
 
601
/**
 
602
  Find duplicate lock in tables.
 
603
 
 
604
  Temporary tables are ignored here like they are ignored in
 
605
  get_lock_data(). If we allow two opens on temporary tables later,
 
606
  both functions should be checked.
 
607
 
 
608
  @param session                 The current thread.
 
609
  @param needle              The table to check for duplicate lock.
 
610
  @param haystack            The list of tables to search for the dup lock.
 
611
 
 
612
  @note
 
613
    This is mainly meant for MERGE tables in INSERT ... SELECT
 
614
    situations. The 'real', underlying tables can be found only after
 
615
    the MERGE tables are opened. This function assumes that the tables are
 
616
    already locked.
 
617
 
 
618
  @retval
 
619
    NULL    No duplicate lock found.
 
620
  @retval
 
621
    !NULL   First table from 'haystack' that matches a lock on 'needle'.
 
622
*/
 
623
 
 
624
TableList *mysql_lock_have_duplicate(Session *session, TableList *needle,
 
625
                                      TableList *haystack)
 
626
{
 
627
  DRIZZLE_LOCK            *mylock;
 
628
  Table                 **lock_tables;
 
629
  Table                 *table;
 
630
  Table                 *table2;
 
631
  THR_LOCK_DATA         **lock_locks;
 
632
  THR_LOCK_DATA         **table_lock_data;
 
633
  THR_LOCK_DATA         **end_data;
 
634
  THR_LOCK_DATA         **lock_data2;
 
635
  THR_LOCK_DATA         **end_data2;
 
636
 
 
637
  /*
 
638
    Table may not be defined for derived or view tables.
 
639
    Table may not be part of a lock for delayed operations.
 
640
  */
 
641
  if (! (table= needle->table) || ! table->lock_count)
 
642
    goto end;
 
643
 
 
644
  /* A temporary table does not have locks. */
 
645
  if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
 
646
    goto end;
 
647
 
 
648
  /* Get command lock or LOCK TABLES lock. Maybe empty for INSERT DELAYED. */
 
649
  if (! (mylock= session->lock ? session->lock : session->locked_tables))
 
650
    goto end;
 
651
 
 
652
  /* If we have less than two tables, we cannot have duplicates. */
 
653
  if (mylock->table_count < 2)
 
654
    goto end;
 
655
 
 
656
  lock_locks=  mylock->locks;
 
657
  lock_tables= mylock->table;
 
658
 
 
659
  /* Prepare table related variables that don't change in loop. */
 
660
  assert((table->lock_position < mylock->table_count) &&
 
661
              (table == lock_tables[table->lock_position]));
 
662
  table_lock_data= lock_locks + table->lock_data_start;
 
663
  end_data= table_lock_data + table->lock_count;
 
664
 
 
665
  for (; haystack; haystack= haystack->next_global)
 
666
  {
 
667
    if (haystack->placeholder())
 
668
      continue;
 
669
    table2= haystack->table;
 
670
    if (table2->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
 
671
      continue;
 
672
 
 
673
    /* All tables in list must be in lock. */
 
674
    assert((table2->lock_position < mylock->table_count) &&
 
675
                (table2 == lock_tables[table2->lock_position]));
 
676
 
 
677
    for (lock_data2=  lock_locks + table2->lock_data_start,
 
678
           end_data2= lock_data2 + table2->lock_count;
 
679
         lock_data2 < end_data2;
 
680
         lock_data2++)
 
681
    {
 
682
      THR_LOCK_DATA **lock_data;
 
683
      THR_LOCK *lock2= (*lock_data2)->lock;
 
684
 
 
685
      for (lock_data= table_lock_data;
 
686
           lock_data < end_data;
 
687
           lock_data++)
 
688
      {
 
689
        if ((*lock_data)->lock == lock2)
 
690
        {
 
691
          return(haystack);
 
692
        }
 
693
      }
 
694
    }
 
695
  }
 
696
 
 
697
 end:
 
698
  return(NULL);
 
699
}
 
700
 
530
701
 
531
702
/** Unlock a set of external. */
532
703
 
540
711
    if ((*table)->current_lock != F_UNLCK)
541
712
    {
542
713
      (*table)->current_lock = F_UNLCK;
543
 
      if ((error=(*table)->cursor->ha_external_lock(session, F_UNLCK)))
 
714
      if ((error=(*table)->file->ha_external_lock(session, F_UNLCK)))
544
715
      {
545
716
        error_code=error;
546
 
        print_lock_error(error_code, (*table)->cursor->engine->getName().c_str());
 
717
        print_lock_error(error_code, (*table)->file->table_type());
547
718
      }
548
719
    }
549
720
    table++;
550
721
  } while (--count);
551
 
  return error_code;
 
722
  return(error_code);
552
723
}
553
724
 
554
725
 
557
728
 
558
729
  @param session                    Thread handler
559
730
  @param table_ptr          Pointer to tables that should be locks
560
 
  @param should_lock                One of:
561
 
           - false      : If we should send TL_IGNORE to store lock
562
 
           - true       : Store lock info in Table
 
731
  @param flags              One of:
 
732
           - GET_LOCK_UNLOCK      : If we should send TL_IGNORE to store lock
 
733
           - GET_LOCK_STORE_LOCKS : Store lock info in Table
563
734
  @param write_lock_used   Store pointer to last table with WRITE_ALLOW_WRITE
564
735
*/
565
736
 
566
737
static DRIZZLE_LOCK *get_lock_data(Session *session, Table **table_ptr, uint32_t count,
567
 
                                 bool should_lock, Table **write_lock_used)
 
738
                                 uint32_t flags, Table **write_lock_used)
568
739
{
569
740
  uint32_t i,tables,lock_count;
570
741
  DRIZZLE_LOCK *sql_lock;
571
742
  THR_LOCK_DATA **locks, **locks_buf, **locks_start;
572
743
  Table **to, **table_buf;
573
744
 
 
745
  assert((flags == GET_LOCK_UNLOCK) || (flags == GET_LOCK_STORE_LOCKS));
 
746
 
574
747
  *write_lock_used=0;
575
 
  for (i= tables= lock_count= 0 ; i < count ; i++)
 
748
  for (i=tables=lock_count=0 ; i < count ; i++)
576
749
  {
577
750
    Table *t= table_ptr[i];
578
751
 
579
 
    if (! (t->getEngine()->check_flag(HTON_BIT_SKIP_STORE_LOCK)))
 
752
    if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE)
580
753
    {
581
 
      tables++;
 
754
      tables+= t->file->lock_count();
582
755
      lock_count++;
583
756
    }
584
757
  }
593
766
        malloc(sizeof(*sql_lock) +
594
767
               sizeof(THR_LOCK_DATA*) * tables * 2 +
595
768
               sizeof(table_ptr) * lock_count)))
596
 
    return NULL;
 
769
    return(0);
597
770
  locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1);
598
771
  to= table_buf= sql_lock->table= (Table**) (locks + tables * 2);
599
 
  sql_lock->table_count= lock_count;
 
772
  sql_lock->table_count=lock_count;
600
773
 
601
774
  for (i=0 ; i < count ; i++)
602
775
  {
603
776
    Table *table;
604
777
    enum thr_lock_type lock_type;
605
778
 
606
 
    if (table_ptr[i]->getEngine()->check_flag(HTON_BIT_SKIP_STORE_LOCK))
 
779
    if ((table=table_ptr[i])->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
607
780
      continue;
608
 
 
609
 
    table= table_ptr[i];
610
781
    lock_type= table->reginfo.lock_type;
611
782
    assert (lock_type != TL_WRITE_DEFAULT);
612
783
    if (lock_type >= TL_WRITE_ALLOW_WRITE)
618
789
        /* Clear the lock type of the lock data that are stored already. */
619
790
        sql_lock->lock_count= locks - sql_lock->locks;
620
791
        reset_lock_data_and_free(&sql_lock);
621
 
        return NULL;
 
792
        return(0);
622
793
      }
623
794
    }
624
795
    locks_start= locks;
625
 
    locks= table->cursor->store_lock(session, locks,
626
 
                                   should_lock == false ? TL_IGNORE : lock_type);
627
 
    if (should_lock)
 
796
    locks= table->file->store_lock(session, locks,
 
797
                                   (flags & GET_LOCK_UNLOCK) ? TL_IGNORE :
 
798
                                   lock_type);
 
799
    if (flags & GET_LOCK_STORE_LOCKS)
628
800
    {
629
 
      table->lock_position=   (uint32_t) (to - table_buf);
630
 
      table->lock_data_start= (uint32_t) (locks_start - locks_buf);
631
 
      table->lock_count=      (uint32_t) (locks - locks_start);
632
 
      assert(table->lock_count == 1);
 
801
      table->lock_position=   (uint) (to - table_buf);
 
802
      table->lock_data_start= (uint) (locks_start - locks_buf);
 
803
      table->lock_count=      (uint) (locks - locks_start);
633
804
    }
634
805
    *to++= table;
635
806
  }
648
819
    And in the FLUSH case, the memory is released quickly anyway.
649
820
  */
650
821
  sql_lock->lock_count= locks - locks_buf;
651
 
 
652
 
  return sql_lock;
 
822
  return(sql_lock);
 
823
}
 
824
 
 
825
 
 
826
/*****************************************************************************
 
827
  Lock table based on the name.
 
828
  This is used when we need total access to a closed, not open table
 
829
*****************************************************************************/
 
830
 
 
831
/**
 
832
  Lock and wait for the named lock.
 
833
 
 
834
  @param session                        Thread handler
 
835
  @param table_list             Lock first table in this list
 
836
 
 
837
 
 
838
  @note
 
839
    Works together with global read lock.
 
840
 
 
841
  @retval
 
842
    0   ok
 
843
  @retval
 
844
    1   error
 
845
*/
 
846
 
 
847
int lock_and_wait_for_table_name(Session *session, TableList *table_list)
 
848
{
 
849
  int lock_retcode;
 
850
  int error= -1;
 
851
 
 
852
  if (wait_if_global_read_lock(session, 0, 1))
 
853
    return(1);
 
854
  pthread_mutex_lock(&LOCK_open);
 
855
  if ((lock_retcode = lock_table_name(session, table_list, true)) < 0)
 
856
    goto end;
 
857
  if (lock_retcode && wait_for_locked_table_names(session, table_list))
 
858
  {
 
859
    unlock_table_name(session, table_list);
 
860
    goto end;
 
861
  }
 
862
  error=0;
 
863
 
 
864
end:
 
865
  pthread_mutex_unlock(&LOCK_open);
 
866
  start_waiting_global_read_lock(session);
 
867
  return(error);
653
868
}
654
869
 
655
870
 
665
880
 
666
881
  @warning
667
882
    If you are going to update the table, you should use
668
 
    lock_and_wait_for_table_name(removed) instead of this function as this works
 
883
    lock_and_wait_for_table_name instead of this function as this works
669
884
    together with 'FLUSH TABLES WITH READ LOCK'
670
885
 
671
886
  @note
689
904
  bool  found_locked_table= false;
690
905
  HASH_SEARCH_STATE state;
691
906
 
692
 
  key_length= table_list->create_table_def_key(key);
 
907
  key_length= create_table_def_key(session, key, table_list, 0);
693
908
 
694
909
  if (check_in_use)
695
910
  {
711
926
      {
712
927
        table->s->version= 0;                  // Ensure no one can use this
713
928
        table->locked_by_name= 1;
714
 
        return 0;
 
929
        return(0);
715
930
      }
716
931
    }
717
932
  }
718
933
 
719
 
  if (!(table= session->table_cache_insert_placeholder(key, key_length)))
720
 
    return -1;
 
934
  if (session->locked_tables && session->locked_tables->table_count &&
 
935
      ! find_temporary_table(session, table_list->db, table_list->table_name))
 
936
  {
 
937
    if (found_locked_table)
 
938
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_list->alias);
 
939
    else
 
940
      my_error(ER_TABLE_NOT_LOCKED, MYF(0), table_list->alias);
 
941
 
 
942
    return(-1);
 
943
  }
 
944
 
 
945
  if (!(table= table_cache_insert_placeholder(session, key, key_length)))
 
946
    return(-1);
721
947
 
722
948
  table_list->table=table;
723
949
 
727
953
}
728
954
 
729
955
 
730
 
void unlock_table_name(TableList *table_list)
 
956
void unlock_table_name(Session *session __attribute__((unused)),
 
957
                       TableList *table_list)
731
958
{
732
959
  if (table_list->table)
733
960
  {
737
964
}
738
965
 
739
966
 
740
 
static bool locked_named_table(TableList *table_list)
 
967
static bool locked_named_table(Session *session __attribute__((unused)),
 
968
                               TableList *table_list)
741
969
{
742
970
  for (; table_list ; table_list=table_list->next_local)
743
971
  {
759
987
 
760
988
bool wait_for_locked_table_names(Session *session, TableList *table_list)
761
989
{
762
 
  bool result= false;
 
990
  bool result=0;
763
991
 
764
992
  safe_mutex_assert_owner(&LOCK_open);
765
993
 
766
 
  while (locked_named_table(table_list))
 
994
  while (locked_named_table(session,table_list))
767
995
  {
768
996
    if (session->killed)
769
997
    {
770
998
      result=1;
771
999
      break;
772
1000
    }
773
 
    session->wait_for_condition(&LOCK_open, &COND_refresh);
774
 
    pthread_mutex_lock(&LOCK_open); /* Wait for a table to unlock and then lock it */
 
1001
    wait_for_condition(session, &LOCK_open, &COND_refresh);
 
1002
    pthread_mutex_lock(&LOCK_open);
775
1003
  }
776
 
  return result;
 
1004
  return(result);
777
1005
}
778
1006
 
779
1007
 
783
1011
  REQUIREMENTS
784
1012
  - One must have a lock on LOCK_open when calling this
785
1013
 
 
1014
  @param session                        Thread handle
786
1015
  @param table_list             Names of tables to lock
787
1016
 
 
1017
  @note
 
1018
    If you are just locking one table, you should use
 
1019
    lock_and_wait_for_table_name().
 
1020
 
788
1021
  @retval
789
1022
    0   ok
790
1023
  @retval
799
1032
  for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
800
1033
  {
801
1034
    int got_lock;
802
 
    if ((got_lock= lock_table_name(session,lock_table, true)) < 0)
 
1035
    if ((got_lock=lock_table_name(session,lock_table, true)) < 0)
803
1036
      goto end;                                 // Fatal error
804
1037
    if (got_lock)
805
1038
      got_all_locks=0;                          // Someone is using table
808
1041
  /* If some table was in use, wait until we got the lock */
809
1042
  if (!got_all_locks && wait_for_locked_table_names(session, table_list))
810
1043
    goto end;
811
 
  return false;
 
1044
  return 0;
812
1045
 
813
1046
end:
814
 
  unlock_table_names(table_list, lock_table);
815
 
 
816
 
  return true;
 
1047
  unlock_table_names(session, table_list, lock_table);
 
1048
  return 1;
817
1049
}
818
1050
 
819
1051
 
854
1086
 
855
1087
 
856
1088
/**
 
1089
  Test is 'table' is protected by an exclusive name lock.
 
1090
 
 
1091
  @param[in] session        The current thread handler
 
1092
  @param[in] table_list Table container containing the single table to be
 
1093
                        tested
 
1094
 
 
1095
  @note Needs to be protected by LOCK_open mutex.
 
1096
 
 
1097
  @return Error status code
 
1098
    @retval TRUE Table is protected
 
1099
    @retval FALSE Table is not protected
 
1100
*/
 
1101
 
 
1102
bool
 
1103
is_table_name_exclusively_locked_by_this_thread(Session *session,
 
1104
                                                TableList *table_list)
 
1105
{
 
1106
  char  key[MAX_DBKEY_LENGTH];
 
1107
  uint32_t  key_length;
 
1108
 
 
1109
  key_length= create_table_def_key(session, key, table_list, 0);
 
1110
 
 
1111
  return is_table_name_exclusively_locked_by_this_thread(session, (unsigned char *)key,
 
1112
                                                         key_length);
 
1113
}
 
1114
 
 
1115
 
 
1116
/**
 
1117
  Test is 'table key' is protected by an exclusive name lock.
 
1118
 
 
1119
  @param[in] session        The current thread handler.
 
1120
  @param[in] key
 
1121
  @param[in] key_length
 
1122
 
 
1123
  @note Needs to be protected by LOCK_open mutex
 
1124
 
 
1125
  @retval TRUE Table is protected
 
1126
  @retval FALSE Table is not protected
 
1127
 */
 
1128
 
 
1129
bool
 
1130
is_table_name_exclusively_locked_by_this_thread(Session *session, unsigned char *key,
 
1131
                                                int key_length)
 
1132
{
 
1133
  HASH_SEARCH_STATE state;
 
1134
  Table *table;
 
1135
 
 
1136
  for (table= (Table*) hash_first(&open_cache, key,
 
1137
                                  key_length, &state);
 
1138
       table ;
 
1139
       table= (Table*) hash_next(&open_cache, key,
 
1140
                                 key_length, &state))
 
1141
  {
 
1142
    if (table->in_use == session &&
 
1143
        table->open_placeholder == 1 &&
 
1144
        table->s->version == 0)
 
1145
      return true;
 
1146
  }
 
1147
 
 
1148
  return false;
 
1149
}
 
1150
 
 
1151
/**
857
1152
  Unlock all tables in list with a name lock.
858
1153
 
859
1154
  @param
 
1155
    session                     Thread handle
 
1156
  @param
860
1157
    table_list          Names of tables to unlock
861
1158
  @param
862
1159
    last_table          Don't unlock any tables after this one.
875
1172
    1   Fatal error (end of memory ?)
876
1173
*/
877
1174
 
878
 
void unlock_table_names(TableList *table_list, TableList *last_table)
 
1175
void unlock_table_names(Session *session, TableList *table_list,
 
1176
                        TableList *last_table)
879
1177
{
880
1178
  for (TableList *table= table_list;
881
1179
       table != last_table;
882
1180
       table= table->next_local)
883
 
    unlock_table_name(table);
 
1181
    unlock_table_name(session,table);
884
1182
  broadcast_refresh();
 
1183
  return;
885
1184
}
886
1185
 
887
1186
 
911
1210
    my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), table);
912
1211
  else
913
1212
    my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), error);
 
1213
 
 
1214
  return;
914
1215
}
915
1216
 
916
1217
 
1013
1314
    if (session->killed)
1014
1315
    {
1015
1316
      session->exit_cond(old_message);
1016
 
      return true;
 
1317
      return(1);
1017
1318
    }
1018
1319
    session->global_read_lock= GOT_GLOBAL_READ_LOCK;
1019
1320
    global_read_lock++;
1027
1328
    forbid it before, or we can have a 3-thread deadlock if 2 do SELECT FOR
1028
1329
    UPDATE and one does FLUSH TABLES WITH READ LOCK).
1029
1330
  */
1030
 
  return false;
 
1331
  return(0);
1031
1332
}
1032
1333
 
1033
1334
 
1046
1347
    pthread_cond_broadcast(&COND_global_read_lock);
1047
1348
  }
1048
1349
  session->global_read_lock= 0;
1049
 
}
1050
 
 
1051
 
static inline bool must_wait(bool is_not_commit)
1052
 
{
1053
 
  return (global_read_lock &&
1054
 
          (is_not_commit ||
1055
 
          global_read_lock_blocks_commit));
1056
 
}
 
1350
 
 
1351
  return;
 
1352
}
 
1353
 
 
1354
#define must_wait (global_read_lock &&                             \
 
1355
                   (is_not_commit ||                               \
 
1356
                    global_read_lock_blocks_commit))
1057
1357
 
1058
1358
bool wait_if_global_read_lock(Session *session, bool abort_on_refresh,
1059
1359
                              bool is_not_commit)
1069
1369
  safe_mutex_assert_not_owner(&LOCK_open);
1070
1370
 
1071
1371
  (void) pthread_mutex_lock(&LOCK_global_read_lock);
1072
 
  if ((need_exit_cond= must_wait(is_not_commit)))
 
1372
  if ((need_exit_cond= must_wait))
1073
1373
  {
1074
1374
    if (session->global_read_lock)              // This thread had the read locks
1075
1375
    {
1082
1382
        This allowance is needed to not break existing versions of innobackup
1083
1383
        which do a BEGIN; INSERT; FLUSH TABLES WITH READ LOCK; COMMIT.
1084
1384
      */
1085
 
      return is_not_commit;
 
1385
      return(is_not_commit);
1086
1386
    }
1087
1387
    old_message=session->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
1088
1388
                                "Waiting for release of readlock");
1089
 
    while (must_wait(is_not_commit) && ! session->killed &&
 
1389
    while (must_wait && ! session->killed &&
1090
1390
           (!abort_on_refresh || session->version == refresh_version))
1091
1391
    {
1092
1392
      (void) pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
1104
1404
    session->exit_cond(old_message); // this unlocks LOCK_global_read_lock
1105
1405
  else
1106
1406
    pthread_mutex_unlock(&LOCK_global_read_lock);
1107
 
  return result;
 
1407
  return(result);
1108
1408
}
1109
1409
 
1110
1410
 
1132
1432
    make_global_read_lock_block_commit(), do nothing.
1133
1433
  */
1134
1434
  if (session->global_read_lock != GOT_GLOBAL_READ_LOCK)
1135
 
    return false;
 
1435
    return(0);
1136
1436
  pthread_mutex_lock(&LOCK_global_read_lock);
1137
1437
  /* increment this BEFORE waiting on cond (otherwise race cond) */
1138
1438
  global_read_lock_blocks_commit++;
1145
1445
  else
1146
1446
    session->global_read_lock= MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT;
1147
1447
  session->exit_cond(old_message); // this unlocks LOCK_global_read_lock
1148
 
  return error;
 
1448
  return(error);
1149
1449
}
1150
1450
 
1151
1451
 
1175
1475
}
1176
1476
 
1177
1477
 
 
1478
/*
 
1479
  Try to get transactional table locks for the tables in the list.
 
1480
 
 
1481
  SYNOPSIS
 
1482
    try_transactional_lock()
 
1483
      session                       Thread handle
 
1484
      table_list                List of tables to lock
 
1485
 
 
1486
  DESCRIPTION
 
1487
    This is called if transactional table locks are requested for all
 
1488
    tables in table_list and no non-transactional locks pre-exist.
 
1489
 
 
1490
  RETURN
 
1491
    0                   OK. All tables are transactional locked.
 
1492
    1                   Error: must fall back to non-transactional locks.
 
1493
    -1                  Error: no recovery possible.
 
1494
*/
 
1495
 
 
1496
int try_transactional_lock(Session *session, TableList *table_list)
 
1497
{
 
1498
  uint32_t          dummy_counter;
 
1499
  int           error;
 
1500
  int           result= 0;
 
1501
 
 
1502
  /* Need to open the tables to be able to access engine methods. */
 
1503
  if (open_tables(session, &table_list, &dummy_counter, 0))
 
1504
  {
 
1505
    /* purecov: begin tested */
 
1506
    return(-1);
 
1507
    /* purecov: end */
 
1508
  }
 
1509
 
 
1510
  /* Required by InnoDB. */
 
1511
  session->in_lock_tables= true;
 
1512
 
 
1513
  if ((error= set_handler_table_locks(session, table_list, true)))
 
1514
  {
 
1515
    /*
 
1516
      Not all transactional locks could be taken. If the error was
 
1517
      something else but "unsupported by storage engine", abort the
 
1518
      execution of this statement.
 
1519
    */
 
1520
    if (error != HA_ERR_WRONG_COMMAND)
 
1521
    {
 
1522
      result= -1;
 
1523
      goto err;
 
1524
    }
 
1525
    /*
 
1526
      Fall back to non-transactional locks because transactional locks
 
1527
      are unsupported by a storage engine. No need to unlock the
 
1528
      successfully taken transactional locks. They go away at end of
 
1529
      transaction anyway.
 
1530
    */
 
1531
    result= 1;
 
1532
  }
 
1533
 
 
1534
 err:
 
1535
  /* We need to explicitly commit if autocommit mode is active. */
 
1536
  (void) ha_autocommit_or_rollback(session, 0);
 
1537
  /* Close the tables. The locks (if taken) persist in the storage engines. */
 
1538
  close_tables_for_reopen(session, &table_list);
 
1539
  session->in_lock_tables= false;
 
1540
  return(result);
 
1541
}
 
1542
 
 
1543
 
 
1544
/*
 
1545
  Check if lock method conversion was done and was allowed.
 
1546
 
 
1547
  SYNOPSIS
 
1548
    check_transactional_lock()
 
1549
      session                       Thread handle
 
1550
      table_list                List of tables to lock
 
1551
 
 
1552
  DESCRIPTION
 
1553
 
 
1554
    Lock method conversion can be done during parsing if one of the
 
1555
    locks is non-transactional. It can also happen if non-transactional
 
1556
    table locks exist when the statement is executed or if a storage
 
1557
    engine does not support transactional table locks.
 
1558
 
 
1559
    Check if transactional table locks have been converted to
 
1560
    non-transactional and if this was allowed. In a running transaction
 
1561
    or in strict mode lock method conversion is not allowed - report an
 
1562
    error. Otherwise it is allowed - issue a warning.
 
1563
 
 
1564
  RETURN
 
1565
    0                   OK. Proceed with non-transactional locks.
 
1566
    -1                  Error: Lock conversion is prohibited.
 
1567
*/
 
1568
 
 
1569
int check_transactional_lock(Session *session, TableList *table_list)
 
1570
{
 
1571
  TableList    *tlist;
 
1572
  int           result= 0;
 
1573
  char          warn_buff[DRIZZLE_ERRMSG_SIZE];
 
1574
 
 
1575
  for (tlist= table_list; tlist; tlist= tlist->next_global)
 
1576
  {
 
1577
 
 
1578
    /*
 
1579
      Unfortunately we cannot use tlist->placeholder() here. This method
 
1580
      returns TRUE if the table is not open, which is always the case
 
1581
      here. Whenever the definition of TableList::placeholder() is
 
1582
      changed, probably this condition needs to be changed too.
 
1583
    */
 
1584
    if (tlist->derived || tlist->schema_table || !tlist->lock_transactional)
 
1585
    {
 
1586
      continue;
 
1587
    }
 
1588
 
 
1589
    /* We must not convert the lock method in strict mode. */
 
1590
    {
 
1591
      my_error(ER_NO_AUTO_CONVERT_LOCK_STRICT, MYF(0),
 
1592
               tlist->alias ? tlist->alias : tlist->table_name);
 
1593
      result= -1;
 
1594
      continue;
 
1595
    }
 
1596
 
 
1597
    /* We must not convert the lock method within an active transaction. */
 
1598
    if (session->active_transaction())
 
1599
    {
 
1600
      my_error(ER_NO_AUTO_CONVERT_LOCK_TRANSACTION, MYF(0),
 
1601
               tlist->alias ? tlist->alias : tlist->table_name);
 
1602
      result= -1;
 
1603
      continue;
 
1604
    }
 
1605
 
 
1606
    /* Warn about the conversion. */
 
1607
    snprintf(warn_buff, sizeof(warn_buff), ER(ER_WARN_AUTO_CONVERT_LOCK),
 
1608
             tlist->alias ? tlist->alias : tlist->table_name);
 
1609
    push_warning(session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
 
1610
                 ER_WARN_AUTO_CONVERT_LOCK, warn_buff);
 
1611
  }
 
1612
 
 
1613
  return(result);
 
1614
}
 
1615
 
 
1616
 
 
1617
/*
 
1618
  Set table locks in the table handler.
 
1619
 
 
1620
  SYNOPSIS
 
1621
    set_handler_table_locks()
 
1622
      session                       Thread handle
 
1623
      table_list                List of tables to lock
 
1624
      transactional             If to lock transactional or non-transactional
 
1625
 
 
1626
  RETURN
 
1627
    0                   OK.
 
1628
    != 0                Error code from handler::lock_table().
 
1629
*/
 
1630
 
 
1631
int set_handler_table_locks(Session *session, TableList *table_list,
 
1632
                            bool transactional)
 
1633
{
 
1634
  TableList    *tlist;
 
1635
  int           error= 0;
 
1636
 
 
1637
  for (tlist= table_list; tlist; tlist= tlist->next_global)
 
1638
  {
 
1639
    int lock_type;
 
1640
    int lock_timeout= -1; /* Use default for non-transactional locks. */
 
1641
 
 
1642
    if (tlist->placeholder())
 
1643
      continue;
 
1644
 
 
1645
    assert((tlist->lock_type == TL_READ) ||
 
1646
                (tlist->lock_type == TL_READ_NO_INSERT) ||
 
1647
                (tlist->lock_type == TL_WRITE_DEFAULT) ||
 
1648
                (tlist->lock_type == TL_WRITE) ||
 
1649
                (tlist->lock_type == TL_WRITE_LOW_PRIORITY));
 
1650
 
 
1651
    /*
 
1652
      Every tlist object has a proper lock_type set. Even if it came in
 
1653
      the list as a base table from a view only.
 
1654
    */
 
1655
    lock_type= ((tlist->lock_type <= TL_READ_NO_INSERT) ?
 
1656
                HA_LOCK_IN_SHARE_MODE : HA_LOCK_IN_EXCLUSIVE_MODE);
 
1657
 
 
1658
    if (transactional)
 
1659
    {
 
1660
      /*
 
1661
        The lock timeout is not set if this table belongs to a view. We
 
1662
        need to take it from the top-level view. After this loop
 
1663
        iteration, lock_timeout is not needed any more. Not even if the
 
1664
        locks are converted to non-transactional locks later.
 
1665
        Non-transactional locks do not support a lock_timeout.
 
1666
      */
 
1667
      lock_timeout= tlist->top_table()->lock_timeout;
 
1668
 
 
1669
      /*
 
1670
        For warning/error reporting we need to set the intended lock
 
1671
        method in the TableList object. It will be used later by
 
1672
        check_transactional_lock(). The lock method is not set if this
 
1673
        table belongs to a view. We can safely set it to transactional
 
1674
        locking here. Even for non-view tables. This function is not
 
1675
        called if non-transactional locking was requested for any
 
1676
        object.
 
1677
      */
 
1678
      tlist->lock_transactional= true;
 
1679
    }
 
1680
 
 
1681
    /*
 
1682
      Because we need to set the lock method (see above) for all
 
1683
      involved tables, we cannot break the loop on an error.
 
1684
      But we do not try more locks after the first error.
 
1685
      However, for non-transactional locking handler::lock_table() is
 
1686
      a hint only. So we continue to call it for other tables.
 
1687
    */
 
1688
    if (!error || !transactional)
 
1689
    {
 
1690
      error= tlist->table->file->lock_table(session, lock_type, lock_timeout);
 
1691
      if (error && transactional && (error != HA_ERR_WRONG_COMMAND))
 
1692
        tlist->table->file->print_error(error, MYF(0));
 
1693
    }
 
1694
  }
 
1695
 
 
1696
  return(error);
 
1697
}
 
1698
 
 
1699
 
1178
1700
/**
1179
1701
  @} (end of group Locking)
1180
1702
*/
1181
 
 
1182
 
} /* namespace drizzled */