sql_handler.cpp
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:23k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /* Copyright (C) 2000-2004 MySQL AB
  2.    This program is free software; you can redistribute it and/or modify
  3.    it under the terms of the GNU General Public License as published by
  4.    the Free Software Foundation; either version 2 of the License, or
  5.    (at your option) any later version.
  6.    This program is distributed in the hope that it will be useful,
  7.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  8.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  9.    GNU General Public License for more details.
  10.    You should have received a copy of the GNU General Public License
  11.    along with this program; if not, write to the Free Software
  12.    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
  13. /* HANDLER ... commands - direct access to ISAM */
  14. /* TODO:
  15.   HANDLER blabla OPEN [ AS foobar ] [ (column-list) ]
  16.   the most natural (easiest, fastest) way to do it is to
  17.   compute List<Item> field_list not in mysql_ha_read
  18.   but in mysql_ha_open, and then store it in TABLE structure.
  19.   The problem here is that mysql_parse calls free_item to free all the
  20.   items allocated at the end of every query. The workaround would to
  21.   keep two item lists per THD - normal free_list and handler_items.
  22.   The second is to be freeed only on thread end. mysql_ha_open should
  23.   then do { handler_items=concat(handler_items, free_list); free_list=0; }
  24.   But !!! do_command calls free_root at the end of every query and frees up
  25.   all the sql_alloc'ed memory. It's harder to work around...
  26. */
  27. /*
  28.   There are two containers holding information about open handler tables.
  29.   The first is 'thd->handler_tables'. It is a linked list of TABLE objects.
  30.   It is used like 'thd->open_tables' in the table cache. The trick is to
  31.   exchange these two lists during open and lock of tables. Thus the normal
  32.   table cache code can be used.
  33.   The second container is a HASH. It holds objects of the type TABLE_LIST.
  34.   Despite its name, no lists of tables but only single structs are hashed
  35.   (the 'next' pointer is always NULL). The reason for theis second container
  36.   is, that we want handler tables to survive FLUSH TABLE commands. A table
  37.   affected by FLUSH TABLE must be closed so that other threads are not
  38.   blocked by handler tables still in use. Since we use the normal table cache
  39.   functions with 'thd->handler_tables', the closed tables are removed from
  40.   this list. Hence we need the original open information for the handler
  41.   table in the case that it is used again. This information is handed over
  42.   to mysql_ha_open() as a TABLE_LIST. So we store this information in the
  43.   second container, where it is not affected by FLUSH TABLE. The second
  44.   container is implemented as a hash for performance reasons. Consequently,
  45.   we use it not only for re-opening a handler table, but also for the
  46.   HANDLER ... READ commands. For this purpose, we store a pointer to the
  47.   TABLE structure (in the first container) in the TBALE_LIST object in the
  48.   second container. When the table is flushed, the pointer is cleared.
  49. */
  50. #include "mysql_priv.h"
  51. #include "sql_select.h"
  52. #include <assert.h>
  53. #define HANDLER_TABLES_HASH_SIZE 120
  54. static enum enum_ha_read_modes rkey_to_rnext[]=
  55.     { RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
  56. #define HANDLER_TABLES_HACK(thd) {      
  57.   TABLE *tmp=thd->open_tables;          
  58.   thd->open_tables=thd->handler_tables; 
  59.   thd->handler_tables=tmp; }
  60. static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags);
  61. /*
  62.   Get hash key and hash key length.
  63.   SYNOPSIS
  64.     mysql_ha_hash_get_key()
  65.     tables                      Pointer to the hash object.
  66.     key_len_p   (out)           Pointer to the result for key length.
  67.     first                       Unused.
  68.   DESCRIPTION
  69.     The hash object is an TABLE_LIST struct.
  70.     The hash key is the alias name.
  71.     The hash key length is the alias name length plus one for the
  72.     terminateing NUL character.
  73.   RETURN
  74.     Pointer to the TABLE_LIST struct.
  75. */
  76. static char *mysql_ha_hash_get_key(TABLE_LIST *tables, uint *key_len_p,
  77.                                    my_bool first __attribute__((unused)))
  78. {
  79.   *key_len_p= strlen(tables->alias) + 1 ; /* include '' in comparisons */
  80.   return tables->alias;
  81. }
  82. /*
  83.   Free an hash object.
  84.   SYNOPSIS
  85.     mysql_ha_hash_free()
  86.     tables                      Pointer to the hash object.
  87.   DESCRIPTION
  88.     The hash object is an TABLE_LIST struct.
  89.   RETURN
  90.     Nothing
  91. */
  92. static void mysql_ha_hash_free(TABLE_LIST *tables)
  93. {
  94.   my_free((char*) tables, MYF(0));
  95. }
  96. /*
  97.   Open a HANDLER table.
  98.   SYNOPSIS
  99.     mysql_ha_open()
  100.     thd                         Thread identifier.
  101.     tables                      A list of tables with the first entry to open.
  102.     reopen                      Re-open a previously opened handler table.
  103.   DESCRIPTION
  104.     Though this function takes a list of tables, only the first list entry
  105.     will be opened.
  106.     'reopen' is set when a handler table is to be re-opened. In this case,
  107.     'tables' is the pointer to the hashed TABLE_LIST object which has been
  108.     saved on the original open.
  109.     'reopen' is also used to suppress the sending of an 'ok' message or
  110.     error messages.
  111.   RETURN
  112.     0    ok
  113.     != 0 error
  114. */
  115. int mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
  116. {
  117.   TABLE_LIST    *hash_tables;
  118.   char          *db, *name, *alias;
  119.   uint          dblen, namelen, aliaslen, counter;
  120.   int           err;
  121.   DBUG_ENTER("mysql_ha_open");
  122.   DBUG_PRINT("enter",("'%s'.'%s' as '%s'  reopen: %d",
  123.                       tables->db, tables->real_name, tables->alias,
  124.                       (int) reopen));
  125.   if (! hash_inited(&thd->handler_tables_hash))
  126.   {
  127.     /*
  128.       HASH entries are of type TABLE_LIST.
  129.     */
  130.     if (hash_init(&thd->handler_tables_hash, &my_charset_latin1,
  131.                   HANDLER_TABLES_HASH_SIZE, 0, 0,
  132.                   (hash_get_key) mysql_ha_hash_get_key,
  133.                   (hash_free_key) mysql_ha_hash_free, 0))
  134.       goto err;
  135.   }
  136.   else if (! reopen) /* Otherwise we have 'tables' already. */
  137.   {
  138.     if (hash_search(&thd->handler_tables_hash, (byte*) tables->alias,
  139.                     strlen(tables->alias) + 1))
  140.     {
  141.       DBUG_PRINT("info",("duplicate '%s'", tables->alias));
  142.       if (! reopen)
  143.         my_printf_error(ER_NONUNIQ_TABLE, ER(ER_NONUNIQ_TABLE),
  144.                         MYF(0), tables->alias);
  145.       goto err;
  146.     }
  147.   }
  148.   /*
  149.     open_tables() will set 'tables->table' if successful.
  150.     It must be NULL for a real open when calling open_tables().
  151.   */
  152.   DBUG_ASSERT(! tables->table);
  153.   HANDLER_TABLES_HACK(thd);
  154.   err=open_tables(thd, tables, &counter);
  155.   HANDLER_TABLES_HACK(thd);
  156.   if (err)
  157.     goto err;
  158.   /* There can be only one table in '*tables'. */
  159.   if (! (tables->table->file->table_flags() & HA_CAN_SQL_HANDLER))
  160.   {
  161.     if (! reopen)
  162.       my_printf_error(ER_ILLEGAL_HA,ER(ER_ILLEGAL_HA),MYF(0), tables->alias);
  163.     mysql_ha_close(thd, tables);
  164.     goto err;
  165.   }
  166.   if (! reopen)
  167.   {
  168.     /* copy the TABLE_LIST struct */
  169.     dblen= strlen(tables->db) + 1;
  170.     namelen= strlen(tables->real_name) + 1;
  171.     aliaslen= strlen(tables->alias) + 1;
  172.     if (!(my_multi_malloc(MYF(MY_WME),
  173.                           &hash_tables, sizeof(*hash_tables),
  174.                           &db, dblen,
  175.                           &name, namelen,
  176.                           &alias, aliaslen,
  177.                           NullS)))
  178.       goto err;
  179.     /* structure copy */
  180.     *hash_tables= *tables;
  181.     hash_tables->db= db;
  182.     hash_tables->real_name= name;
  183.     hash_tables->alias= alias;
  184.     memcpy(hash_tables->db, tables->db, dblen);
  185.     memcpy(hash_tables->real_name, tables->real_name, namelen);
  186.     memcpy(hash_tables->alias, tables->alias, aliaslen);
  187.     /* add to hash */
  188.     if (my_hash_insert(&thd->handler_tables_hash, (byte*) hash_tables))
  189.     {
  190.       mysql_ha_close(thd, tables);
  191.       goto err;
  192.     }
  193.   }
  194.   if (! reopen)
  195.     send_ok(thd);
  196.   DBUG_PRINT("exit",("OK"));
  197.   DBUG_RETURN(0);
  198. err:
  199.   DBUG_PRINT("exit",("ERROR"));
  200.   DBUG_RETURN(-1);
  201. }
  202. /*
  203.   Close a HANDLER table.
  204.   SYNOPSIS
  205.     mysql_ha_close()
  206.     thd                         Thread identifier.
  207.     tables                      A list of tables with the first entry to close.
  208.   DESCRIPTION
  209.     Though this function takes a list of tables, only the first list entry
  210.     will be closed. Broadcasts a COND_refresh condition.
  211.   RETURN
  212.     0    ok
  213.     != 0 error
  214. */
  215. int mysql_ha_close(THD *thd, TABLE_LIST *tables)
  216. {
  217.   TABLE_LIST    *hash_tables;
  218.   TABLE         **table_ptr;
  219.   DBUG_ENTER("mysql_ha_close");
  220.   DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
  221.                       tables->db, tables->real_name, tables->alias));
  222.   if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
  223.                                               (byte*) tables->alias,
  224.                                               strlen(tables->alias) + 1)))
  225.   {
  226.     /*
  227.       Though we could take the table pointer from hash_tables->table,
  228.       we must follow the thd->handler_tables chain anyway, as we need the
  229.       address of the 'next' pointer referencing this table
  230.       for close_thread_table().
  231.     */
  232.     for (table_ptr= &(thd->handler_tables);
  233.          *table_ptr && (*table_ptr != hash_tables->table);
  234.            table_ptr= &(*table_ptr)->next);
  235. #if MYSQL_VERSION_ID < 40100
  236.     if (*tables->db && strcmp(hash_tables->db, tables->db))
  237.     {
  238.       DBUG_PRINT("info",("wrong db"));
  239.       hash_tables= NULL;
  240.     }
  241.     else
  242. #endif
  243.     {
  244.       if (*table_ptr)
  245.       {
  246.         (*table_ptr)->file->ha_index_or_rnd_end();
  247.         VOID(pthread_mutex_lock(&LOCK_open));
  248.         if (close_thread_table(thd, table_ptr))
  249.         {
  250.           /* Tell threads waiting for refresh that something has happened */
  251.           VOID(pthread_cond_broadcast(&COND_refresh));
  252.         }
  253.         VOID(pthread_mutex_unlock(&LOCK_open));
  254.       }
  255.       hash_delete(&thd->handler_tables_hash, (byte*) hash_tables);
  256.     }
  257.   }
  258.   if (! hash_tables)
  259.   {
  260. #if MYSQL_VERSION_ID < 40100
  261.     char buff[MAX_DBKEY_LENGTH];
  262.     if (*tables->db)
  263.       strxnmov(buff, sizeof(buff), tables->db, ".", tables->real_name, NullS);
  264.     else
  265.       strncpy(buff, tables->alias, sizeof(buff));
  266.     my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0),
  267.                     buff, "HANDLER");
  268. #else
  269.     my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0),
  270.                     tables->alias, "HANDLER");
  271. #endif
  272.     DBUG_PRINT("exit",("ERROR"));
  273.     DBUG_RETURN(-1);
  274.   }
  275.   send_ok(thd);
  276.   DBUG_PRINT("exit", ("OK"));
  277.   DBUG_RETURN(0);
  278. }
  279. /*
  280.   Read from a HANDLER table.
  281.   SYNOPSIS
  282.     mysql_ha_read()
  283.     thd                         Thread identifier.
  284.     tables                      A list of tables with the first entry to read.
  285.     mode
  286.     keyname
  287.     key_expr
  288.     ha_rkey_mode
  289.     cond
  290.     select_limit
  291.     offset_limit
  292.   RETURN
  293.     0    ok
  294.     != 0 error
  295. */
  296.  
  297. int mysql_ha_read(THD *thd, TABLE_LIST *tables,
  298.     enum enum_ha_read_modes mode, char *keyname, List<Item> *key_expr,
  299.     enum ha_rkey_function ha_rkey_mode, Item *cond,
  300.     ha_rows select_limit,ha_rows offset_limit)
  301. {
  302.   TABLE_LIST    *hash_tables;
  303.   TABLE         **table_ptr;
  304.   TABLE         *table;
  305.   MYSQL_LOCK    *lock;
  306.   List<Item> list;
  307.   Protocol *protocol= thd->protocol;
  308.   char buff[MAX_FIELD_WIDTH];
  309.   String buffer(buff, sizeof(buff), system_charset_info);
  310.   int           err, keyno= -1;
  311.   uint          num_rows;
  312.   byte *key;
  313.   uint key_len;
  314.   DBUG_ENTER("mysql_ha_read");
  315.   DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
  316.                       tables->db, tables->real_name, tables->alias));
  317.   LINT_INIT(key);
  318.   LINT_INIT(key_len);
  319.   list.push_front(new Item_field(NULL,NULL,"*"));
  320.   List_iterator<Item> it(list);
  321.   it++;
  322.   if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
  323.                                               (byte*) tables->alias,
  324.                                               strlen(tables->alias) + 1)))
  325.   {
  326.     table= hash_tables->table;
  327.     DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' tab %p",
  328.                                hash_tables->db, hash_tables->real_name,
  329.                                hash_tables->alias, table));
  330.     /* Table might have been flushed. */
  331.     if (table && (table->version != refresh_version))
  332.     {
  333.       /*
  334.         We must follow the thd->handler_tables chain, as we need the
  335.         address of the 'next' pointer referencing this table
  336.         for close_thread_table().
  337.       */
  338.       for (table_ptr= &(thd->handler_tables);
  339.            *table_ptr && (*table_ptr != table);
  340.            table_ptr= &(*table_ptr)->next)
  341.       {}
  342.       VOID(pthread_mutex_lock(&LOCK_open));
  343.       if (close_thread_table(thd, table_ptr))
  344.       {
  345.         /* Tell threads waiting for refresh that something has happened */
  346.         VOID(pthread_cond_broadcast(&COND_refresh));
  347.       }
  348.       VOID(pthread_mutex_unlock(&LOCK_open));
  349.       table= hash_tables->table= NULL;
  350.     }
  351.     if (!table)
  352.     {
  353.       /*
  354.         The handler table has been closed. Re-open it.
  355.       */
  356.       if (mysql_ha_open(thd, hash_tables, 1))
  357.       {
  358.         DBUG_PRINT("exit",("reopen failed"));
  359.         goto err0;
  360.       }
  361.       table= hash_tables->table;
  362.       DBUG_PRINT("info",("re-opened '%s'.'%s' as '%s' tab %p",
  363.                          hash_tables->db, hash_tables->real_name,
  364.                          hash_tables->alias, table));
  365.     }
  366. #if MYSQL_VERSION_ID < 40100
  367.     if (*tables->db && strcmp(table->table_cache_key, tables->db))
  368.     {
  369.       DBUG_PRINT("info",("wrong db"));
  370.       table= NULL;
  371.     }
  372. #endif
  373.   }
  374.   else
  375.     table= NULL;
  376.   if (!table)
  377.   {
  378. #if MYSQL_VERSION_ID < 40100
  379.     char buff[MAX_DBKEY_LENGTH];
  380.     if (*tables->db)
  381.       strxnmov(buff, sizeof(buff), tables->db, ".", tables->real_name, NullS);
  382.     else
  383.       strncpy(buff, tables->alias, sizeof(buff));
  384.     my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0),
  385.                     buff, "HANDLER");
  386. #else
  387.     my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0),
  388.                     tables->alias, "HANDLER");
  389. #endif
  390.     goto err0;
  391.   }
  392.   tables->table=table;
  393.   if (cond && ((!cond->fixed &&
  394.               cond->fix_fields(thd, tables, &cond)) || cond->check_cols(1)))
  395.     goto err0;
  396.   if (keyname)
  397.   {
  398.     if ((keyno=find_type(keyname, &table->keynames, 1+2)-1)<0)
  399.     {
  400.       my_printf_error(ER_KEY_DOES_NOT_EXITS,ER(ER_KEY_DOES_NOT_EXITS),MYF(0),
  401.           keyname,tables->alias);
  402.       goto err0;
  403.     }
  404.   }
  405.   if (insert_fields(thd,tables,tables->db,tables->alias,&it))
  406.     goto err0;
  407.   select_limit+=offset_limit;
  408.   protocol->send_fields(&list,1);
  409.   HANDLER_TABLES_HACK(thd);
  410.   lock= mysql_lock_tables(thd, &tables->table, 1, 0);
  411.   HANDLER_TABLES_HACK(thd);
  412.   if (!lock)
  413.      goto err0; // mysql_lock_tables() printed error message already
  414.   /*
  415.     In ::external_lock InnoDB resets the fields which tell it that
  416.     the handle is used in the HANDLER interface. Tell it again that
  417.     we are using it for HANDLER.
  418.   */
  419.   table->file->init_table_handle_for_HANDLER();
  420.   for (num_rows=0; num_rows < select_limit; )
  421.   {
  422.     switch (mode) {
  423.     case RNEXT:
  424.       if (table->file->inited != handler::NONE)
  425.       {
  426.         err=keyname ?
  427.   table->file->index_next(table->record[0]) :
  428.   table->file->rnd_next(table->record[0]);
  429.         break;
  430.       }
  431.       /* else fall through */
  432.     case RFIRST:
  433.       if (keyname)
  434.       {
  435.         table->file->ha_index_or_rnd_end();
  436.         table->file->ha_index_init(keyno);
  437.         err=table->file->index_first(table->record[0]);
  438.       }
  439.       else
  440.       {
  441.         table->file->ha_index_or_rnd_end();
  442. if (!(err=table->file->ha_rnd_init(1)))
  443.           err=table->file->rnd_next(table->record[0]);
  444.       }
  445.       mode=RNEXT;
  446.       break;
  447.     case RPREV:
  448.       DBUG_ASSERT(keyname != 0);
  449.       if (table->file->inited != handler::NONE)
  450.       {
  451.         err=table->file->index_prev(table->record[0]);
  452.         break;
  453.       }
  454.       /* else fall through */
  455.     case RLAST:
  456.       DBUG_ASSERT(keyname != 0);
  457.       table->file->ha_index_or_rnd_end();
  458.       table->file->ha_index_init(keyno);
  459.       err=table->file->index_last(table->record[0]);
  460.       mode=RPREV;
  461.       break;
  462.     case RNEXT_SAME:
  463.       /* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...)  */
  464.       DBUG_ASSERT(keyname != 0);
  465.       err= table->file->index_next_same(table->record[0], key, key_len);
  466.       break;
  467.     case RKEY:
  468.     {
  469.       DBUG_ASSERT(keyname != 0);
  470.       KEY *keyinfo=table->key_info+keyno;
  471.       KEY_PART_INFO *key_part=keyinfo->key_part;
  472.       if (key_expr->elements > keyinfo->key_parts)
  473.       {
  474. my_printf_error(ER_TOO_MANY_KEY_PARTS,ER(ER_TOO_MANY_KEY_PARTS),
  475. MYF(0),keyinfo->key_parts);
  476. goto err;
  477.       }
  478.       List_iterator<Item> it_ke(*key_expr);
  479.       Item *item;
  480.       for (key_len=0 ; (item=it_ke++) ; key_part++)
  481.       {
  482. // 'item' can be changed by fix_fields() call
  483. if ((!item->fixed &&
  484.              item->fix_fields(thd, tables, it_ke.ref())) ||
  485.     (item= *it_ke.ref())->check_cols(1))
  486.   goto err;
  487. if (item->used_tables() & ~RAND_TABLE_BIT)
  488.         {
  489.           my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
  490.   goto err;
  491.         }
  492. (void) item->save_in_field(key_part->field, 1);
  493. key_len+=key_part->store_length;
  494.       }
  495.       if (!(key= (byte*) thd->calloc(ALIGN_SIZE(key_len))))
  496.       {
  497. send_error(thd,ER_OUTOFMEMORY);
  498. goto err;
  499.       }
  500.       key_copy(key, table, keyno, key_len);
  501.       table->file->ha_index_or_rnd_end();
  502.       table->file->ha_index_init(keyno);
  503.       err=table->file->index_read(table->record[0],
  504.   key,key_len,ha_rkey_mode);
  505.       mode=rkey_to_rnext[(int)ha_rkey_mode];
  506.       break;
  507.     }
  508.     default:
  509.       send_error(thd,ER_ILLEGAL_HA);
  510.       goto err;
  511.     }
  512.     if (err == HA_ERR_RECORD_DELETED)
  513.       continue;
  514.     if (err)
  515.     {
  516.       if (err != HA_ERR_KEY_NOT_FOUND && err != HA_ERR_END_OF_FILE)
  517.       {
  518.         sql_print_error("mysql_ha_read: Got error %d when reading table '%s'",
  519.                         err, tables->real_name);
  520.         table->file->print_error(err,MYF(0));
  521.         goto err;
  522.       }
  523.       goto ok;
  524.     }
  525.     if (cond && !cond->val_int())
  526.       continue;
  527.     if (num_rows >= offset_limit)
  528.     {
  529.       Item *item;
  530.       protocol->prepare_for_resend();
  531.       it.rewind();
  532.       while ((item=it++))
  533.       {
  534. if (item->send(thd->protocol, &buffer))
  535. {
  536.   protocol->free();                             // Free used
  537.   my_error(ER_OUT_OF_RESOURCES,MYF(0));
  538.   goto err;
  539. }
  540.       }
  541.       protocol->write();
  542.     }
  543.     num_rows++;
  544.   }
  545. ok:
  546.   mysql_unlock_tables(thd,lock);
  547.   send_eof(thd);
  548.   DBUG_PRINT("exit",("OK"));
  549.   DBUG_RETURN(0);
  550. err:
  551.   mysql_unlock_tables(thd,lock);
  552. err0:
  553.   DBUG_PRINT("exit",("ERROR"));
  554.   DBUG_RETURN(-1);
  555. }
  556. /*
  557.   Flush (close) a list of HANDLER tables.
  558.   SYNOPSIS
  559.     mysql_ha_flush()
  560.     thd                         Thread identifier.
  561.     tables                      The list of tables to close. If NULL,
  562.                                 close all HANDLER tables [marked as flushed].
  563.     mode_flags                  MYSQL_HA_CLOSE_FINAL finally close the table.
  564.                                 MYSQL_HA_REOPEN_ON_USAGE mark for reopen.
  565.                                 MYSQL_HA_FLUSH_ALL flush all tables, not only
  566.                                 those marked for flush.
  567.     is_locked                   If LOCK_open is locked.
  568.   DESCRIPTION
  569.     The list of HANDLER tables may be NULL, in which case all HANDLER
  570.     tables are closed (if MYSQL_HA_FLUSH_ALL) is set.
  571.     If 'tables' is NULL and MYSQL_HA_FLUSH_ALL is not set,
  572.     all HANDLER tables marked for flush are closed.
  573.     Broadcasts a COND_refresh condition, for every table closed.
  574.   NOTE
  575.     Since mysql_ha_flush() is called when the base table has to be closed,
  576.     we compare real table names, not aliases. Hence, database names matter.
  577.   RETURN
  578.     0  ok
  579. */
  580. int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags,
  581.                    bool is_locked)
  582. {
  583.   TABLE_LIST    *tmp_tables;
  584.   TABLE         **table_ptr;
  585.   bool          did_lock= FALSE;
  586.   DBUG_ENTER("mysql_ha_flush");
  587.   DBUG_PRINT("enter", ("tables: %p  mode_flags: 0x%02x", tables, mode_flags));
  588.   if (tables)
  589.   {
  590.     /* Close all tables in the list. */
  591.     for (tmp_tables= tables ; tmp_tables; tmp_tables= tmp_tables->next)
  592.     {
  593.       DBUG_PRINT("info-in-tables-list",("'%s'.'%s' as '%s'",
  594.                                         tmp_tables->db, tmp_tables->real_name,
  595.                                         tmp_tables->alias));
  596.       /* Close all currently open handler tables with the same base table. */
  597.       table_ptr= &(thd->handler_tables);
  598.       while (*table_ptr)
  599.       {
  600.         if ((! *tmp_tables->db ||
  601.              ! my_strcasecmp(&my_charset_latin1, (*table_ptr)->table_cache_key,
  602.                              tmp_tables->db)) &&
  603.             ! my_strcasecmp(&my_charset_latin1, (*table_ptr)->real_name,
  604.                             tmp_tables->real_name))
  605.         {
  606.           DBUG_PRINT("info",("*table_ptr '%s'.'%s' as '%s'",
  607.                              (*table_ptr)->table_cache_key,
  608.                              (*table_ptr)->real_name,
  609.                              (*table_ptr)->table_name));
  610.           /* The first time it is required, lock for close_thread_table(). */
  611.           if (! did_lock && ! is_locked)
  612.           {
  613.             VOID(pthread_mutex_lock(&LOCK_open));
  614.             did_lock= TRUE;
  615.           }
  616.           mysql_ha_flush_table(thd, table_ptr, mode_flags);
  617.           continue;
  618.         }
  619.         table_ptr= &(*table_ptr)->next;
  620.       }
  621.       /* end of handler_tables list */
  622.     }
  623.     /* end of flush tables list */
  624.   }
  625.   else
  626.   {
  627.     /* Close all currently open tables [which are marked for flush]. */
  628.     table_ptr= &(thd->handler_tables);
  629.     while (*table_ptr)
  630.     {
  631.       if ((mode_flags & MYSQL_HA_FLUSH_ALL) ||
  632.           ((*table_ptr)->version != refresh_version))
  633.       {
  634.         /* The first time it is required, lock for close_thread_table(). */
  635.         if (! did_lock && ! is_locked)
  636.         {
  637.           VOID(pthread_mutex_lock(&LOCK_open));
  638.           did_lock= TRUE;
  639.         }
  640.         mysql_ha_flush_table(thd, table_ptr, mode_flags);
  641.         continue;
  642.       }
  643.       table_ptr= &(*table_ptr)->next;
  644.     }
  645.   }
  646.   /* Release the lock if it was taken by this function. */
  647.   if (did_lock)
  648.     VOID(pthread_mutex_unlock(&LOCK_open));
  649.   DBUG_RETURN(0);
  650. }
  651. /*
  652.   Flush (close) a table.
  653.   SYNOPSIS
  654.     mysql_ha_flush_table()
  655.     thd                         Thread identifier.
  656.     table                       The table to close.
  657.     mode_flags                  MYSQL_HA_CLOSE_FINAL finally close the table.
  658.                                 MYSQL_HA_REOPEN_ON_USAGE mark for reopen.
  659.   DESCRIPTION
  660.     Broadcasts a COND_refresh condition, for every table closed.
  661.     The caller must lock LOCK_open.
  662.   RETURN
  663.     0  ok
  664. */
  665. static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags)
  666. {
  667.   TABLE_LIST    *hash_tables;
  668.   TABLE         *table= *table_ptr;
  669.   DBUG_ENTER("mysql_ha_flush_table");
  670.   DBUG_PRINT("enter",("'%s'.'%s' as '%s'  flags: 0x%02x",
  671.                       table->table_cache_key, table->real_name,
  672.                       table->table_name, mode_flags));
  673.   if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
  674.                                               (byte*) table->table_name,
  675.                                               strlen(table->table_name) + 1)))
  676.   {
  677.     if (! (mode_flags & MYSQL_HA_REOPEN_ON_USAGE))
  678.     {
  679.       /* This is a final close. Remove from hash. */
  680.       hash_delete(&thd->handler_tables_hash, (byte*) hash_tables);
  681.     }
  682.     else
  683.     {
  684.       /* Mark table as closed, ready for re-open. */
  685.       hash_tables->table= NULL;
  686.     }
  687.   }    
  688.   safe_mutex_assert_owner(&LOCK_open);
  689.   (*table_ptr)->file->ha_index_or_rnd_end();
  690.   if (close_thread_table(thd, table_ptr))
  691.   {
  692.     /* Tell threads waiting for refresh that something has happened */
  693.     VOID(pthread_cond_broadcast(&COND_refresh));
  694.   }
  695.   DBUG_RETURN(0);
  696. }