home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 January / Chip_2001-01_cd1.bin / tema / mysql / mysql-3.23.28g-win-source.exe / sql / lock.cpp < prev    next >
C/C++ Source or Header  |  2000-11-14  |  13KB  |  480 lines

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