~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/lock.cc

Replace MAX_(DATE|TIME).*_WIDTH defines in definitions.h with real (and correct) static const members to Temporal types.

This fixes the buffer overflow in https://bugs.launchpad.net/drizzle/+bug/373468

It also removes a handwritten snprintf in field/datetime.cc
However... this caused us to have to change Temporal to have a way to not
"convert" the int64_t value (so 20090101 becomes 20090101000000 etc) as it
has already been converted and we just want the Temporal type to do the
to_string conversion.

This still causes a failure in 'metadata' test due to size of timestamp type. I need feedback from Jay on when the usecond code comes into play to know the correct fix for this.

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
175
162
 
176
163
 
177
164
DRIZZLE_LOCK *mysql_lock_tables(Session *session, Table **tables, uint32_t count,
178
 
                                uint32_t flags, bool *need_reopen)
 
165
                              uint32_t flags, bool *need_reopen)
179
166
{
180
167
  DRIZZLE_LOCK *sql_lock;
181
168
  Table *write_lock_used;
182
 
  vector<plugin::StorageEngine *> involved_engines;
183
169
  int rc;
184
170
 
185
171
  *need_reopen= false;
210
196
        goto retry;
211
197
      }
212
198
    }
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
 
     */
 
199
 
 
200
    session->set_proc_info("System lock");
245
201
    if (sql_lock->table_count && lock_external(session, sql_lock->table,
246
202
                                               sql_lock->table_count))
247
203
    {
301
257
      type for other tables preserved.
302
258
    */
303
259
    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
260
retry:
313
261
    if (flags & DRIZZLE_LOCK_NOTIFY_IF_NEED_REOPEN)
314
262
    {
328
276
      sql_lock= NULL;
329
277
    }
330
278
  }
 
279
 
331
280
  session->set_time_after_lock();
332
281
  return (sql_lock);
333
282
}
346
295
         (*tables)->reginfo.lock_type <= TL_READ_NO_INSERT))
347
296
      lock_type=F_RDLCK;
348
297
 
349
 
    if ((error=(*tables)->cursor->ha_external_lock(session,lock_type)))
 
298
    if ((error=(*tables)->file->ha_external_lock(session,lock_type)))
350
299
    {
351
 
      print_lock_error(error, (*tables)->cursor->engine->getName().c_str());
 
300
      print_lock_error(error, (*tables)->file->engine->getName().c_str());
352
301
      while (--i)
353
302
      {
354
303
        tables--;
355
 
        (*tables)->cursor->ha_external_lock(session, F_UNLCK);
 
304
        (*tables)->file->ha_external_lock(session, F_UNLCK);
356
305
        (*tables)->current_lock=F_UNLCK;
357
306
      }
358
307
      return error;
383
332
  This will work even if get_lock_data fails (next unlock will free all)
384
333
*/
385
334
 
386
 
void mysql_unlock_some_tables(Session *session, Table **table, uint32_t count)
 
335
void mysql_unlock_some_tables(Session *session, Table **table,uint32_t count)
387
336
{
388
337
  DRIZZLE_LOCK *sql_lock;
389
338
  Table *write_lock_used;
473
422
                          effect is desired.
474
423
*/
475
424
 
476
 
void mysql_lock_remove(Session *session, Table *table)
 
425
void mysql_lock_remove(Session *session, DRIZZLE_LOCK *locked,Table *table,
 
426
                       bool always_unlock)
477
427
{
478
 
  mysql_unlock_some_tables(session, &table, /* table count */ 1);
 
428
  if (always_unlock == true)
 
429
    mysql_unlock_some_tables(session, &table, /* table count */ 1);
 
430
  if (locked)
 
431
  {
 
432
    register uint32_t i;
 
433
    for (i=0; i < locked->table_count; i++)
 
434
    {
 
435
      if (locked->table[i] == table)
 
436
      {
 
437
        uint32_t  j, removed_locks, old_tables;
 
438
        Table *tbl;
 
439
        uint32_t lock_data_end;
 
440
 
 
441
        assert(table->lock_position == i);
 
442
 
 
443
        /* Unlock if not yet unlocked */
 
444
        if (always_unlock == false)
 
445
          mysql_unlock_some_tables(session, &table, /* table count */ 1);
 
446
 
 
447
        /* Decrement table_count in advance, making below expressions easier */
 
448
        old_tables= --locked->table_count;
 
449
 
 
450
        /* The table has 'removed_locks' lock data elements in locked->locks */
 
451
        removed_locks= table->lock_count;
 
452
 
 
453
        /* Move down all table pointers above 'i'. */
 
454
        memmove((locked->table+i), (locked->table+i+1),
 
455
                (old_tables - i) * sizeof(Table*));
 
456
 
 
457
        lock_data_end= table->lock_data_start + table->lock_count;
 
458
        /* Move down all lock data pointers above 'table->lock_data_end-1' */
 
459
        memmove((locked->locks + table->lock_data_start),
 
460
                (locked->locks + lock_data_end),
 
461
                (locked->lock_count - lock_data_end) *
 
462
                sizeof(THR_LOCK_DATA*));
 
463
 
 
464
        /*
 
465
          Fix moved table elements.
 
466
          lock_position is the index in the 'locked->table' array,
 
467
          it must be fixed by one.
 
468
          table->lock_data_start is pointer to the lock data for this table
 
469
          in the 'locked->locks' array, they must be fixed by 'removed_locks',
 
470
          the lock data count of the removed table.
 
471
        */
 
472
        for (j= i ; j < old_tables; j++)
 
473
        {
 
474
          tbl= locked->table[j];
 
475
          tbl->lock_position--;
 
476
          assert(tbl->lock_position == j);
 
477
          tbl->lock_data_start-= removed_locks;
 
478
        }
 
479
 
 
480
        /* Finally adjust lock_count. */
 
481
        locked->lock_count-= removed_locks;
 
482
        break;
 
483
      }
 
484
    }
 
485
  }
479
486
}
480
487
 
481
488
 
482
489
/** Abort all other threads waiting to get lock in table. */
483
490
 
484
 
void mysql_lock_abort(Session *session, Table *table)
 
491
void mysql_lock_abort(Session *session, Table *table, bool upgrade_lock)
485
492
{
486
493
  DRIZZLE_LOCK *locked;
487
494
  Table *write_lock_used;
489
496
  if ((locked= get_lock_data(session, &table, 1, false,
490
497
                             &write_lock_used)))
491
498
  {
492
 
    for (uint32_t x= 0; x < locked->lock_count; x++)
493
 
      thr_abort_locks(locked->locks[x]->lock);
 
499
    for (uint32_t i=0; i < locked->lock_count; i++)
 
500
      thr_abort_locks(locked->locks[i]->lock, upgrade_lock);
494
501
    free((unsigned char*) locked);
495
502
  }
496
503
}
528
535
  return result;
529
536
}
530
537
 
 
538
 
 
539
DRIZZLE_LOCK *mysql_lock_merge(DRIZZLE_LOCK *a, DRIZZLE_LOCK *b)
 
540
{
 
541
  DRIZZLE_LOCK *sql_lock;
 
542
  Table **table, **end_table;
 
543
 
 
544
  if (!(sql_lock= (DRIZZLE_LOCK*)
 
545
        malloc(sizeof(*sql_lock)+
 
546
               sizeof(THR_LOCK_DATA*)*(a->lock_count+b->lock_count)+
 
547
               sizeof(Table*)*(a->table_count+b->table_count))))
 
548
    return NULL;                                // Fatal error
 
549
  sql_lock->lock_count=a->lock_count+b->lock_count;
 
550
  sql_lock->table_count=a->table_count+b->table_count;
 
551
  sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
 
552
  sql_lock->table=(Table**) (sql_lock->locks+sql_lock->lock_count);
 
553
  memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks));
 
554
  memcpy(sql_lock->locks+a->lock_count,b->locks,
 
555
         b->lock_count*sizeof(*b->locks));
 
556
  memcpy(sql_lock->table,a->table,a->table_count*sizeof(*a->table));
 
557
  memcpy(sql_lock->table+a->table_count,b->table,
 
558
         b->table_count*sizeof(*b->table));
 
559
 
 
560
  /*
 
561
    Now adjust lock_position and lock_data_start for all objects that was
 
562
    moved in 'b' (as there is now all objects in 'a' before these).
 
563
  */
 
564
  for (table= sql_lock->table + a->table_count,
 
565
         end_table= table + b->table_count;
 
566
       table < end_table;
 
567
       table++)
 
568
  {
 
569
    (*table)->lock_position+=   a->table_count;
 
570
    (*table)->lock_data_start+= a->lock_count;
 
571
  }
 
572
 
 
573
  /* Delete old, not needed locks */
 
574
  free((unsigned char*) a);
 
575
  free((unsigned char*) b);
 
576
 
 
577
  return sql_lock;
 
578
}
 
579
 
 
580
 
 
581
/**
 
582
  Find duplicate lock in tables.
 
583
 
 
584
  Temporary tables are ignored here like they are ignored in
 
585
  get_lock_data(). If we allow two opens on temporary tables later,
 
586
  both functions should be checked.
 
587
 
 
588
  @param session                 The current thread.
 
589
  @param needle              The table to check for duplicate lock.
 
590
  @param haystack            The list of tables to search for the dup lock.
 
591
 
 
592
  @note
 
593
    This is mainly meant for MERGE tables in INSERT ... SELECT
 
594
    situations. The 'real', underlying tables can be found only after
 
595
    the MERGE tables are opened. This function assumes that the tables are
 
596
    already locked.
 
597
 
 
598
  @retval
 
599
    NULL    No duplicate lock found.
 
600
  @retval
 
601
    !NULL   First table from 'haystack' that matches a lock on 'needle'.
 
602
*/
 
603
 
 
604
TableList *mysql_lock_have_duplicate(Session *session, TableList *needle,
 
605
                                      TableList *haystack)
 
606
{
 
607
  DRIZZLE_LOCK            *mylock;
 
608
  Table                 **lock_tables;
 
609
  Table                 *table;
 
610
  Table                 *table2;
 
611
  THR_LOCK_DATA         **lock_locks;
 
612
  THR_LOCK_DATA         **table_lock_data;
 
613
  THR_LOCK_DATA         **end_data;
 
614
  THR_LOCK_DATA         **lock_data2;
 
615
  THR_LOCK_DATA         **end_data2;
 
616
 
 
617
  /*
 
618
    Table may not be defined for derived or view tables.
 
619
    Table may not be part of a lock for delayed operations.
 
620
  */
 
621
  if (! (table= needle->table) || ! table->lock_count)
 
622
    goto end;
 
623
 
 
624
  /* A temporary table does not have locks. */
 
625
  if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
 
626
    goto end;
 
627
 
 
628
  /* Get command lock or LOCK TABLES lock. Maybe empty for INSERT DELAYED. */
 
629
  if (!(mylock= session->lock))
 
630
    goto end;
 
631
 
 
632
  /* If we have less than two tables, we cannot have duplicates. */
 
633
  if (mylock->table_count < 2)
 
634
    goto end;
 
635
 
 
636
  lock_locks=  mylock->locks;
 
637
  lock_tables= mylock->table;
 
638
 
 
639
  /* Prepare table related variables that don't change in loop. */
 
640
  assert((table->lock_position < mylock->table_count) &&
 
641
              (table == lock_tables[table->lock_position]));
 
642
  table_lock_data= lock_locks + table->lock_data_start;
 
643
  end_data= table_lock_data + table->lock_count;
 
644
 
 
645
  for (; haystack; haystack= haystack->next_global)
 
646
  {
 
647
    if (haystack->placeholder())
 
648
      continue;
 
649
    table2= haystack->table;
 
650
    if (table2->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
 
651
      continue;
 
652
 
 
653
    /* All tables in list must be in lock. */
 
654
    assert((table2->lock_position < mylock->table_count) &&
 
655
                (table2 == lock_tables[table2->lock_position]));
 
656
 
 
657
    for (lock_data2=  lock_locks + table2->lock_data_start,
 
658
           end_data2= lock_data2 + table2->lock_count;
 
659
         lock_data2 < end_data2;
 
660
         lock_data2++)
 
661
    {
 
662
      THR_LOCK_DATA **lock_data;
 
663
      THR_LOCK *lock2= (*lock_data2)->lock;
 
664
 
 
665
      for (lock_data= table_lock_data;
 
666
           lock_data < end_data;
 
667
           lock_data++)
 
668
      {
 
669
        if ((*lock_data)->lock == lock2)
 
670
          return haystack;
 
671
      }
 
672
    }
 
673
  }
 
674
 
 
675
 end:
 
676
  return NULL;
 
677
}
 
678
 
 
679
 
531
680
/** Unlock a set of external. */
532
681
 
533
682
static int unlock_external(Session *session, Table **table,uint32_t count)
540
689
    if ((*table)->current_lock != F_UNLCK)
541
690
    {
542
691
      (*table)->current_lock = F_UNLCK;
543
 
      if ((error=(*table)->cursor->ha_external_lock(session, F_UNLCK)))
 
692
      if ((error=(*table)->file->ha_external_lock(session, F_UNLCK)))
544
693
      {
545
694
        error_code=error;
546
 
        print_lock_error(error_code, (*table)->cursor->engine->getName().c_str());
 
695
        print_lock_error(error_code, (*table)->file->engine->getName().c_str());
547
696
      }
548
697
    }
549
698
    table++;
576
725
  {
577
726
    Table *t= table_ptr[i];
578
727
 
579
 
    if (! (t->getEngine()->check_flag(HTON_BIT_SKIP_STORE_LOCK)))
 
728
    if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE)
580
729
    {
581
730
      tables++;
582
731
      lock_count++;
603
752
    Table *table;
604
753
    enum thr_lock_type lock_type;
605
754
 
606
 
    if (table_ptr[i]->getEngine()->check_flag(HTON_BIT_SKIP_STORE_LOCK))
 
755
    if ((table=table_ptr[i])->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
607
756
      continue;
608
 
 
609
 
    table= table_ptr[i];
610
757
    lock_type= table->reginfo.lock_type;
611
758
    assert (lock_type != TL_WRITE_DEFAULT);
612
759
    if (lock_type >= TL_WRITE_ALLOW_WRITE)
622
769
      }
623
770
    }
624
771
    locks_start= locks;
625
 
    locks= table->cursor->store_lock(session, locks,
 
772
    locks= table->file->store_lock(session, locks,
626
773
                                   should_lock == false ? TL_IGNORE : lock_type);
627
774
    if (should_lock)
628
775
    {
653
800
}
654
801
 
655
802
 
 
803
/*****************************************************************************
 
804
  Lock table based on the name.
 
805
  This is used when we need total access to a closed, not open table
 
806
*****************************************************************************/
 
807
 
 
808
/**
 
809
  Lock and wait for the named lock.
 
810
 
 
811
  @param session                        Thread handler
 
812
  @param table_list             Lock first table in this list
 
813
 
 
814
 
 
815
  @note
 
816
    Works together with global read lock.
 
817
 
 
818
  @retval
 
819
    0   ok
 
820
  @retval
 
821
    1   error
 
822
*/
 
823
 
 
824
int lock_and_wait_for_table_name(Session *session, TableList *table_list)
 
825
{
 
826
  int lock_retcode;
 
827
  int error= -1;
 
828
 
 
829
  if (wait_if_global_read_lock(session, 0, 1))
 
830
    return 1;
 
831
  pthread_mutex_lock(&LOCK_open); /* lock and wait for table when we need total access to table */
 
832
  if ((lock_retcode = lock_table_name(session, table_list, true)) < 0)
 
833
    goto end;
 
834
  if (lock_retcode && wait_for_locked_table_names(session, table_list))
 
835
  {
 
836
    unlock_table_name(table_list);
 
837
    goto end;
 
838
  }
 
839
  error=0;
 
840
 
 
841
end:
 
842
  pthread_mutex_unlock(&LOCK_open);
 
843
  start_waiting_global_read_lock(session);
 
844
  return error;
 
845
}
 
846
 
 
847
 
656
848
/**
657
849
  Put a not open table with an old refresh version in the table cache.
658
850
 
665
857
 
666
858
  @warning
667
859
    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
 
860
    lock_and_wait_for_table_name instead of this function as this works
669
861
    together with 'FLUSH TABLES WITH READ LOCK'
670
862
 
671
863
  @note
785
977
 
786
978
  @param table_list             Names of tables to lock
787
979
 
 
980
  @note
 
981
    If you are just locking one table, you should use
 
982
    lock_and_wait_for_table_name().
 
983
 
788
984
  @retval
789
985
    0   ok
790
986
  @retval
1048
1244
  session->global_read_lock= 0;
1049
1245
}
1050
1246
 
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
 
}
 
1247
#define must_wait (global_read_lock &&                             \
 
1248
                   (is_not_commit ||                               \
 
1249
                    global_read_lock_blocks_commit))
1057
1250
 
1058
1251
bool wait_if_global_read_lock(Session *session, bool abort_on_refresh,
1059
1252
                              bool is_not_commit)
1069
1262
  safe_mutex_assert_not_owner(&LOCK_open);
1070
1263
 
1071
1264
  (void) pthread_mutex_lock(&LOCK_global_read_lock);
1072
 
  if ((need_exit_cond= must_wait(is_not_commit)))
 
1265
  if ((need_exit_cond= must_wait))
1073
1266
  {
1074
1267
    if (session->global_read_lock)              // This thread had the read locks
1075
1268
    {
1086
1279
    }
1087
1280
    old_message=session->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
1088
1281
                                "Waiting for release of readlock");
1089
 
    while (must_wait(is_not_commit) && ! session->killed &&
 
1282
    while (must_wait && ! session->killed &&
1090
1283
           (!abort_on_refresh || session->version == refresh_version))
1091
1284
    {
1092
1285
      (void) pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
1178
1371
/**
1179
1372
  @} (end of group Locking)
1180
1373
*/
1181
 
 
1182
 
} /* namespace drizzled */