Drizzled Public API Documentation

ha_myisam.cc
00001 /* Copyright (C) 2000-2006 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 
00017 
00018 #include <config.h>
00019 #include <drizzled/internal/my_bit.h>
00020 #include "myisampack.h"
00021 #include "ha_myisam.h"
00022 #include "myisam_priv.h"
00023 #include <drizzled/option.h>
00024 #include <drizzled/internal/my_bit.h>
00025 #include <drizzled/internal/m_string.h>
00026 #include <drizzled/util/test.h>
00027 #include <drizzled/error.h>
00028 #include <drizzled/errmsg_print.h>
00029 #include <drizzled/gettext.h>
00030 #include <drizzled/session.h>
00031 #include <drizzled/plugin.h>
00032 #include <drizzled/plugin/client.h>
00033 #include <drizzled/table.h>
00034 #include <drizzled/memory/multi_malloc.h>
00035 #include <drizzled/plugin/daemon.h>
00036 #include <drizzled/session/table_messages.h>
00037 #include <drizzled/plugin/storage_engine.h>
00038 #include <drizzled/key.h>
00039 #include <drizzled/statistics_variables.h>
00040 #include <drizzled/system_variables.h>
00041 
00042 #include <boost/algorithm/string.hpp>
00043 #include <boost/scoped_ptr.hpp>
00044 
00045 #include <string>
00046 #include <sstream>
00047 #include <map>
00048 #include <algorithm>
00049 #include <memory>
00050 #include <boost/program_options.hpp>
00051 #include <drizzled/module/option_map.h>
00052 
00053 namespace po= boost::program_options;
00054 
00055 using namespace std;
00056 using namespace drizzled;
00057 
00058 static const string engine_name("MyISAM");
00059 
00060 boost::mutex THR_LOCK_myisam;
00061 
00062 static uint32_t myisam_key_cache_block_size= KEY_CACHE_BLOCK_SIZE;
00063 static uint64_t max_sort_file_size;
00064 typedef constrained_check<size_t, SIZE_MAX, 1024, 1024> sort_buffer_constraint;
00065 static sort_buffer_constraint sort_buffer_size;
00066 
00067 /*****************************************************************************
00068 ** MyISAM tables
00069 *****************************************************************************/
00070 
00071 static const char *ha_myisam_exts[] = {
00072   ".MYI",
00073   ".MYD",
00074   NULL
00075 };
00076 
00077 class MyisamEngine : public plugin::StorageEngine
00078 {
00079 public:
00080   explicit MyisamEngine(string name_arg) :
00081     plugin::StorageEngine(name_arg,
00082                           HTON_CAN_INDEX_BLOBS |
00083                           HTON_STATS_RECORDS_IS_EXACT |
00084                           HTON_TEMPORARY_ONLY |
00085                           HTON_NULL_IN_KEY |
00086                           HTON_HAS_RECORDS |
00087                           HTON_DUPLICATE_POS |
00088                           HTON_AUTO_PART_KEY |
00089                           HTON_SKIP_STORE_LOCK)
00090   {
00091   }
00092 
00093   virtual ~MyisamEngine()
00094   { 
00095     mi_panic(HA_PANIC_CLOSE);
00096   }
00097 
00098   virtual Cursor *create(Table &table)
00099   {
00100     return new ha_myisam(*this, table);
00101   }
00102 
00103   const char **bas_ext() const {
00104     return ha_myisam_exts;
00105   }
00106 
00107   int doCreateTable(Session&,
00108                     Table& table_arg,
00109                     const identifier::Table &identifier,
00110                     const message::Table&);
00111 
00112   int doRenameTable(Session&, const identifier::Table &from, const identifier::Table &to);
00113 
00114   int doDropTable(Session&, const identifier::Table &identifier);
00115 
00116   int doGetTableDefinition(Session& session,
00117                            const identifier::Table &identifier,
00118                            message::Table &table_message);
00119 
00120   uint32_t max_supported_keys()          const { return MI_MAX_KEY; }
00121   uint32_t max_supported_key_length()    const { return MI_MAX_KEY_LENGTH; }
00122   uint32_t max_supported_key_part_length() const { return MI_MAX_KEY_LENGTH; }
00123 
00124   uint32_t index_flags(enum  ha_key_alg) const
00125   {
00126     return (HA_READ_NEXT |
00127             HA_READ_PREV |
00128             HA_READ_RANGE |
00129             HA_READ_ORDER |
00130             HA_KEYREAD_ONLY);
00131   }
00132   bool doDoesTableExist(Session& session, const identifier::Table &identifier);
00133 
00134   void doGetTableIdentifiers(drizzled::CachedDirectory &directory,
00135                              const drizzled::identifier::Schema &schema_identifier,
00136                              drizzled::identifier::table::vector &set_of_identifiers);
00137   bool validateCreateTableOption(const std::string &key, const std::string &state)
00138   {
00139     (void)state;
00140     if (boost::iequals(key, "ROW_FORMAT"))
00141     {
00142       return true;
00143     }
00144 
00145     return false;
00146   }
00147 };
00148 
00149 void MyisamEngine::doGetTableIdentifiers(drizzled::CachedDirectory&,
00150                                          const drizzled::identifier::Schema&,
00151                                          drizzled::identifier::table::vector&)
00152 {
00153 }
00154 
00155 bool MyisamEngine::doDoesTableExist(Session &session, const identifier::Table &identifier)
00156 {
00157   return session.getMessageCache().doesTableMessageExist(identifier);
00158 }
00159 
00160 int MyisamEngine::doGetTableDefinition(Session &session,
00161                                        const identifier::Table &identifier,
00162                                        message::Table &table_message)
00163 {
00164   if (session.getMessageCache().getTableMessage(identifier, table_message))
00165     return EEXIST;
00166   return ENOENT;
00167 }
00168 
00169 /* 
00170   Convert to push_Warnings if you ever care about this, otherwise, it is a no-op.
00171 */
00172 
00173 static void mi_check_print_msg(MI_CHECK *,  const char* ,
00174                                const char *, va_list )
00175 {
00176 }
00177 
00178 
00179 /*
00180   Convert Table object to MyISAM key and column definition
00181 
00182   SYNOPSIS
00183     table2myisam()
00184       table_arg   in     Table object.
00185       keydef_out  out    MyISAM key definition.
00186       recinfo_out out    MyISAM column definition.
00187       records_out out    Number of fields.
00188 
00189   DESCRIPTION
00190     This function will allocate and initialize MyISAM key and column
00191     definition for further use in mi_create or for a check for underlying
00192     table conformance in merge engine.
00193 
00194     The caller needs to free *recinfo_out after use. Since *recinfo_out
00195     and *keydef_out are allocated with a multi_malloc, *keydef_out
00196     is freed automatically when *recinfo_out is freed.
00197 
00198   RETURN VALUE
00199     0  OK
00200     !0 error code
00201 */
00202 
00203 static int table2myisam(Table *table_arg, MI_KEYDEF **keydef_out,
00204                         MI_COLUMNDEF **recinfo_out, uint32_t *records_out)
00205 {
00206   uint32_t i, j, recpos, minpos, fieldpos, temp_length, length;
00207   enum ha_base_keytype type= HA_KEYTYPE_BINARY;
00208   unsigned char *record;
00209   MI_KEYDEF *keydef;
00210   MI_COLUMNDEF *recinfo, *recinfo_pos;
00211   HA_KEYSEG *keyseg;
00212   TableShare *share= table_arg->getMutableShare();
00213   uint32_t options= share->db_options_in_use;
00214   if (!(memory::multi_malloc(false,
00215           recinfo_out, (share->sizeFields() * 2 + 2) * sizeof(MI_COLUMNDEF),
00216           keydef_out, share->sizeKeys() * sizeof(MI_KEYDEF),
00217           &keyseg, (share->key_parts + share->sizeKeys()) * sizeof(HA_KEYSEG),
00218           NULL)))
00219     return(HA_ERR_OUT_OF_MEM);
00220   keydef= *keydef_out;
00221   recinfo= *recinfo_out;
00222   for (i= 0; i < share->sizeKeys(); i++)
00223   {
00224     KeyInfo *pos= &table_arg->key_info[i];
00225     keydef[i].flag= ((uint16_t) pos->flags & (HA_NOSAME));
00226     keydef[i].key_alg= HA_KEY_ALG_BTREE;
00227     keydef[i].block_length= pos->block_size;
00228     keydef[i].seg= keyseg;
00229     keydef[i].keysegs= pos->key_parts;
00230     for (j= 0; j < pos->key_parts; j++)
00231     {
00232       Field *field= pos->key_part[j].field;
00233       type= field->key_type();
00234       keydef[i].seg[j].flag= pos->key_part[j].key_part_flag;
00235 
00236       if (options & HA_OPTION_PACK_KEYS ||
00237           (pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY |
00238                          HA_SPACE_PACK_USED)))
00239       {
00240         if (pos->key_part[j].length > 8 &&
00241             (type == HA_KEYTYPE_TEXT ||
00242              (type == HA_KEYTYPE_BINARY && !field->zero_pack())))
00243         {
00244           /* No blobs here */
00245           if (j == 0)
00246             keydef[i].flag|= HA_PACK_KEY;
00247           if ((((int) (pos->key_part[j].length - field->decimals())) >= 4))
00248             keydef[i].seg[j].flag|= HA_SPACE_PACK;
00249         }
00250         else if (j == 0 && (!(pos->flags & HA_NOSAME) || pos->key_length > 16))
00251           keydef[i].flag|= HA_BINARY_PACK_KEY;
00252       }
00253       keydef[i].seg[j].type= (int) type;
00254       keydef[i].seg[j].start= pos->key_part[j].offset;
00255       keydef[i].seg[j].length= pos->key_part[j].length;
00256       keydef[i].seg[j].bit_start= keydef[i].seg[j].bit_end=
00257         keydef[i].seg[j].bit_length= 0;
00258       keydef[i].seg[j].bit_pos= 0;
00259       keydef[i].seg[j].language= field->charset()->number;
00260 
00261       if (field->null_ptr)
00262       {
00263         keydef[i].seg[j].null_bit= field->null_bit;
00264         keydef[i].seg[j].null_pos= (uint) (field->null_ptr-
00265                                            (unsigned char*) table_arg->getInsertRecord());
00266       }
00267       else
00268       {
00269         keydef[i].seg[j].null_bit= 0;
00270         keydef[i].seg[j].null_pos= 0;
00271       }
00272       if (field->type() == DRIZZLE_TYPE_BLOB)
00273       {
00274         keydef[i].seg[j].flag|= HA_BLOB_PART;
00275         /* save number of bytes used to pack length */
00276         keydef[i].seg[j].bit_start= (uint) (field->pack_length() -
00277                                             share->sizeBlobPtr());
00278       }
00279     }
00280     keyseg+= pos->key_parts;
00281   }
00282   if (table_arg->found_next_number_field)
00283     keydef[share->next_number_index].flag|= HA_AUTO_KEY;
00284   record= table_arg->getInsertRecord();
00285   recpos= 0;
00286   recinfo_pos= recinfo;
00287 
00288   while (recpos < (uint) share->sizeStoredRecord())
00289   {
00290     Field **field, *found= 0;
00291     minpos= share->getRecordLength();
00292     length= 0;
00293 
00294     for (field= table_arg->getFields(); *field; field++)
00295     {
00296       if ((fieldpos= (*field)->offset(record)) >= recpos &&
00297           fieldpos <= minpos)
00298       {
00299         /* skip null fields */
00300         if (!(temp_length= (*field)->pack_length_in_rec()))
00301           continue; /* Skip null-fields */
00302 
00303         if (! found || fieldpos < minpos ||
00304             (fieldpos == minpos && temp_length < length))
00305         {
00306           minpos= fieldpos;
00307           found= *field;
00308           length= temp_length;
00309         }
00310       }
00311     }
00312     if (recpos != minpos)
00313     { // Reserved space (Null bits?)
00314       memset(recinfo_pos, 0, sizeof(*recinfo_pos));
00315       recinfo_pos->type= (int) FIELD_NORMAL;
00316       recinfo_pos++->length= (uint16_t) (minpos - recpos);
00317     }
00318     if (!found)
00319       break;
00320 
00321     if (found->flags & BLOB_FLAG)
00322       recinfo_pos->type= (int) FIELD_BLOB;
00323     else if (found->type() == DRIZZLE_TYPE_VARCHAR)
00324       recinfo_pos->type= FIELD_VARCHAR;
00325     else if (!(options & HA_OPTION_PACK_RECORD))
00326       recinfo_pos->type= (int) FIELD_NORMAL;
00327     else if (found->zero_pack())
00328       recinfo_pos->type= (int) FIELD_SKIP_ZERO;
00329     else
00330       recinfo_pos->type= (int) ((length <= 3) ?  FIELD_NORMAL : FIELD_SKIP_PRESPACE);
00331     if (found->null_ptr)
00332     {
00333       recinfo_pos->null_bit= found->null_bit;
00334       recinfo_pos->null_pos= (uint) (found->null_ptr -
00335                                      (unsigned char*) table_arg->getInsertRecord());
00336     }
00337     else
00338     {
00339       recinfo_pos->null_bit= 0;
00340       recinfo_pos->null_pos= 0;
00341     }
00342     (recinfo_pos++)->length= (uint16_t) length;
00343     recpos= minpos + length;
00344   }
00345   *records_out= (uint) (recinfo_pos - recinfo);
00346   return(0);
00347 }
00348 
00349 int ha_myisam::reset_auto_increment(uint64_t value)
00350 {
00351   file->s->state.auto_increment= value;
00352   return 0;
00353 }
00354 
00355 /*
00356   Check for underlying table conformance
00357 
00358   SYNOPSIS
00359     check_definition()
00360       t1_keyinfo       in    First table key definition
00361       t1_recinfo       in    First table record definition
00362       t1_keys          in    Number of keys in first table
00363       t1_recs          in    Number of records in first table
00364       t2_keyinfo       in    Second table key definition
00365       t2_recinfo       in    Second table record definition
00366       t2_keys          in    Number of keys in second table
00367       t2_recs          in    Number of records in second table
00368       strict           in    Strict check switch
00369 
00370   DESCRIPTION
00371     This function compares two MyISAM definitions. By intention it was done
00372     to compare merge table definition against underlying table definition.
00373     It may also be used to compare dot-frm and MYI definitions of MyISAM
00374     table as well to compare different MyISAM table definitions.
00375 
00376     For merge table it is not required that number of keys in merge table
00377     must exactly match number of keys in underlying table. When calling this
00378     function for underlying table conformance check, 'strict' flag must be
00379     set to false, and converted merge definition must be passed as t1_*.
00380 
00381     Otherwise 'strict' flag must be set to 1 and it is not required to pass
00382     converted dot-frm definition as t1_*.
00383 
00384   RETURN VALUE
00385     0 - Equal definitions.
00386     1 - Different definitions.
00387 
00388   TODO
00389     - compare FULLTEXT keys;
00390     - compare SPATIAL keys;
00391     - compare FIELD_SKIP_ZERO which is converted to FIELD_NORMAL correctly
00392       (should be corretly detected in table2myisam).
00393 */
00394 
00395 static int check_definition(MI_KEYDEF *t1_keyinfo, MI_COLUMNDEF *t1_recinfo,
00396                             uint32_t t1_keys, uint32_t t1_recs,
00397                             MI_KEYDEF *t2_keyinfo, MI_COLUMNDEF *t2_recinfo,
00398                             uint32_t t2_keys, uint32_t t2_recs, bool strict)
00399 {
00400   uint32_t i, j;
00401   if ((strict ? t1_keys != t2_keys : t1_keys > t2_keys))
00402   {
00403     return(1);
00404   }
00405   if (t1_recs != t2_recs)
00406   {
00407     return(1);
00408   }
00409   for (i= 0; i < t1_keys; i++)
00410   {
00411     HA_KEYSEG *t1_keysegs= t1_keyinfo[i].seg;
00412     HA_KEYSEG *t2_keysegs= t2_keyinfo[i].seg;
00413     if (t1_keyinfo[i].keysegs != t2_keyinfo[i].keysegs ||
00414         t1_keyinfo[i].key_alg != t2_keyinfo[i].key_alg)
00415     {
00416       return(1);
00417     }
00418     for (j=  t1_keyinfo[i].keysegs; j--;)
00419     {
00420       uint8_t t1_keysegs_j__type= t1_keysegs[j].type;
00421 
00422       /*
00423         Table migration from 4.1 to 5.1. In 5.1 a *TEXT key part is
00424         always HA_KEYTYPE_VARTEXT2. In 4.1 we had only the equivalent of
00425         HA_KEYTYPE_VARTEXT1. Since we treat both the same on MyISAM
00426         level, we can ignore a mismatch between these types.
00427       */
00428       if ((t1_keysegs[j].flag & HA_BLOB_PART) &&
00429           (t2_keysegs[j].flag & HA_BLOB_PART))
00430       {
00431         if ((t1_keysegs_j__type == HA_KEYTYPE_VARTEXT2) &&
00432             (t2_keysegs[j].type == HA_KEYTYPE_VARTEXT1))
00433           t1_keysegs_j__type= HA_KEYTYPE_VARTEXT1;
00434         else if ((t1_keysegs_j__type == HA_KEYTYPE_VARBINARY2) &&
00435                  (t2_keysegs[j].type == HA_KEYTYPE_VARBINARY1))
00436           t1_keysegs_j__type= HA_KEYTYPE_VARBINARY1;
00437       }
00438 
00439       if (t1_keysegs_j__type != t2_keysegs[j].type ||
00440           t1_keysegs[j].language != t2_keysegs[j].language ||
00441           t1_keysegs[j].null_bit != t2_keysegs[j].null_bit ||
00442           t1_keysegs[j].length != t2_keysegs[j].length)
00443       {
00444         return(1);
00445       }
00446     }
00447   }
00448   for (i= 0; i < t1_recs; i++)
00449   {
00450     MI_COLUMNDEF *t1_rec= &t1_recinfo[i];
00451     MI_COLUMNDEF *t2_rec= &t2_recinfo[i];
00452     /*
00453       FIELD_SKIP_ZERO can be changed to FIELD_NORMAL in mi_create,
00454       see NOTE1 in mi_create.c
00455     */
00456     if ((t1_rec->type != t2_rec->type &&
00457          !(t1_rec->type == (int) FIELD_SKIP_ZERO &&
00458            t1_rec->length == 1 &&
00459            t2_rec->type == (int) FIELD_NORMAL)) ||
00460         t1_rec->length != t2_rec->length ||
00461         t1_rec->null_bit != t2_rec->null_bit)
00462     {
00463       return(1);
00464     }
00465   }
00466   return(0);
00467 }
00468 
00469 
00470 volatile int *killed_ptr(MI_CHECK *param)
00471 {
00472   /* In theory Unsafe conversion, but should be ok for now */
00473   return (int*) (((Session *)(param->session))->getKilledPtr());
00474 }
00475 
00476 void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
00477 {
00478   param->error_printed|=1;
00479   param->out_flag|= O_DATA_LOST;
00480   va_list args;
00481   va_start(args, fmt);
00482   mi_check_print_msg(param, "error", fmt, args);
00483   va_end(args);
00484 }
00485 
00486 void mi_check_print_info(MI_CHECK *param, const char *fmt,...)
00487 {
00488   va_list args;
00489   va_start(args, fmt);
00490   mi_check_print_msg(param, "info", fmt, args);
00491   va_end(args);
00492 }
00493 
00494 void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
00495 {
00496   param->warning_printed=1;
00497   param->out_flag|= O_DATA_LOST;
00498   va_list args;
00499   va_start(args, fmt);
00500   mi_check_print_msg(param, "warning", fmt, args);
00501   va_end(args);
00502 }
00503 
00519 void _mi_report_crashed(MI_INFO *file, const char *message,
00520                         const char *sfile, uint32_t sline)
00521 {
00522   Session *cur_session;
00523   if ((cur_session= file->in_use))
00524   {
00525     errmsg_printf(error::ERROR, _("Got an error from thread_id=%"PRIu64", %s:%d"),
00526                     cur_session->thread_id,
00527                     sfile, sline);
00528   }
00529   else
00530   {
00531     errmsg_printf(error::ERROR, _("Got an error from unknown thread, %s:%d"), sfile, sline);
00532   }
00533 
00534   if (message)
00535     errmsg_printf(error::ERROR, "%s", message);
00536 
00537   list<Session *>::iterator it= file->s->in_use->begin();
00538   while (it != file->s->in_use->end())
00539   {
00540     errmsg_printf(error::ERROR, "%s", _("Unknown thread accessing table"));
00541     ++it;
00542   }
00543 }
00544 
00545 ha_myisam::ha_myisam(plugin::StorageEngine &engine_arg,
00546                      Table &table_arg)
00547   : Cursor(engine_arg, table_arg),
00548   file(0),
00549   can_enable_indexes(true),
00550   is_ordered(true)
00551 { }
00552 
00553 Cursor *ha_myisam::clone(memory::Root *mem_root)
00554 {
00555   ha_myisam *new_handler= static_cast <ha_myisam *>(Cursor::clone(mem_root));
00556   if (new_handler)
00557     new_handler->file->state= file->state;
00558   return new_handler;
00559 }
00560 
00561 const char *ha_myisam::index_type(uint32_t )
00562 {
00563   return "BTREE";
00564 }
00565 
00566 /* Name is here without an extension */
00567 int ha_myisam::doOpen(const drizzled::identifier::Table &identifier, int mode, uint32_t test_if_locked)
00568 {
00569   MI_KEYDEF *keyinfo;
00570   MI_COLUMNDEF *recinfo= 0;
00571   uint32_t recs;
00572   uint32_t i;
00573 
00574   /*
00575     If the user wants to have memory mapped data files, add an
00576     open_flag. Do not memory map temporary tables because they are
00577     expected to be inserted and thus extended a lot. Memory mapping is
00578     efficient for files that keep their size, but very inefficient for
00579     growing files. Using an open_flag instead of calling mi_extra(...
00580     HA_EXTRA_MMAP ...) after mi_open() has the advantage that the
00581     mapping is not repeated for every open, but just done on the initial
00582     open, when the MyISAM share is created. Everytime the server
00583     requires to open a new instance of a table it calls this method. We
00584     will always supply HA_OPEN_MMAP for a permanent table. However, the
00585     MyISAM storage engine will ignore this flag if this is a secondary
00586     open of a table that is in use by other threads already (if the
00587     MyISAM share exists already).
00588   */
00589   if (!(file= mi_open(identifier, mode, test_if_locked)))
00590     return (errno ? errno : -1);
00591 
00592   if (!getTable()->getShare()->getType()) /* No need to perform a check for tmp table */
00593   {
00594     if ((errno= table2myisam(getTable(), &keyinfo, &recinfo, &recs)))
00595     {
00596       goto err;
00597     }
00598     if (check_definition(keyinfo, recinfo, getTable()->getShare()->sizeKeys(), recs,
00599                          file->s->keyinfo, file->s->rec,
00600                          file->s->base.keys, file->s->base.fields, true))
00601     {
00602       errno= HA_ERR_CRASHED;
00603       goto err;
00604     }
00605   }
00606 
00607   assert(test_if_locked);
00608   if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE))
00609     mi_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0);
00610 
00611   info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
00612   if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
00613     mi_extra(file, HA_EXTRA_WAIT_LOCK, 0);
00614   if (!getTable()->getShare()->db_record_offset)
00615     is_ordered= false;
00616 
00617 
00618   keys_with_parts.reset();
00619   for (i= 0; i < getTable()->getShare()->sizeKeys(); i++)
00620   {
00621     getTable()->key_info[i].block_size= file->s->keyinfo[i].block_length;
00622 
00623     KeyPartInfo *kp= getTable()->key_info[i].key_part;
00624     KeyPartInfo *kp_end= kp + getTable()->key_info[i].key_parts;
00625     for (; kp != kp_end; kp++)
00626     {
00627       if (!kp->field->part_of_key.test(i))
00628       {
00629         keys_with_parts.set(i);
00630         break;
00631       }
00632     }
00633   }
00634   errno= 0;
00635   goto end;
00636  err:
00637   this->close();
00638  end:
00639   /*
00640     Both recinfo and keydef are allocated by multi_malloc(), thus only
00641     recinfo must be freed.
00642   */
00643   if (recinfo)
00644     free((unsigned char*) recinfo);
00645   return errno;
00646 }
00647 
00648 int ha_myisam::close(void)
00649 {
00650   MI_INFO *tmp=file;
00651   file=0;
00652   return mi_close(tmp);
00653 }
00654 
00655 int ha_myisam::doInsertRecord(unsigned char *buf)
00656 {
00657   /*
00658     If we have an auto_increment column and we are writing a changed row
00659     or a new row, then update the auto_increment value in the record.
00660   */
00661   if (getTable()->next_number_field && buf == getTable()->getInsertRecord())
00662   {
00663     int error;
00664     if ((error= update_auto_increment()))
00665       return error;
00666   }
00667   return mi_write(file,buf);
00668 }
00669 
00670 
00671 int ha_myisam::repair(Session *session, MI_CHECK &param, bool do_optimize)
00672 {
00673   int error=0;
00674   uint32_t local_testflag= param.testflag;
00675   bool optimize_done= !do_optimize, statistics_done=0;
00676   const char *old_proc_info= session->get_proc_info();
00677   char fixed_name[FN_REFLEN];
00678   MYISAM_SHARE* share = file->s;
00679   ha_rows rows= file->state->records;
00680 
00681   /*
00682     Normally this method is entered with a properly opened table. If the
00683     repair fails, it can be repeated with more elaborate options. Under
00684     special circumstances it can happen that a repair fails so that it
00685     closed the data file and cannot re-open it. In this case file->dfile
00686     is set to -1. We must not try another repair without an open data
00687     file. (Bug #25289)
00688   */
00689   if (file->dfile == -1)
00690   {
00691     errmsg_printf(error::INFO, "Retrying repair of: '%s' failed. "
00692       "Please try REPAIR EXTENDED or myisamchk",
00693       getTable()->getShare()->getPath());
00694     return(HA_ADMIN_FAILED);
00695   }
00696 
00697   param.db_name=    getTable()->getShare()->getSchemaName();
00698   param.table_name= getTable()->getAlias();
00699   param.tmpfile_createflag = O_RDWR | O_TRUNC;
00700   param.using_global_keycache = 1;
00701   param.session= session;
00702   param.out_flag= 0;
00703   param.sort_buffer_length= static_cast<size_t>(sort_buffer_size);
00704   strcpy(fixed_name,file->filename);
00705 
00706   // Don't lock tables if we have used LOCK Table
00707   if (mi_lock_database(file, getTable()->getShare()->getType() ? F_EXTRA_LCK : F_WRLCK))
00708   {
00709     mi_check_print_error(&param,ER(ER_CANT_LOCK),errno);
00710     return(HA_ADMIN_FAILED);
00711   }
00712 
00713   if (!do_optimize ||
00714       ((file->state->del || share->state.split != file->state->records) &&
00715        (!(param.testflag & T_QUICK) ||
00716   !(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))))
00717   {
00718     uint64_t key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ?
00719       mi_get_mask_all_keys_active(share->base.keys) :
00720       share->state.key_map);
00721     uint32_t testflag=param.testflag;
00722     if (mi_test_if_sort_rep(file,file->state->records,key_map,0) &&
00723   (local_testflag & T_REP_BY_SORT))
00724     {
00725       local_testflag|= T_STATISTICS;
00726       param.testflag|= T_STATISTICS;    // We get this for free
00727       statistics_done=1;
00728       {
00729         session->set_proc_info("Repair by sorting");
00730         error = mi_repair_by_sort(&param, file, fixed_name,
00731             param.testflag & T_QUICK);
00732       }
00733     }
00734     else
00735     {
00736       session->set_proc_info("Repair with keycache");
00737       param.testflag &= ~T_REP_BY_SORT;
00738       error=  mi_repair(&param, file, fixed_name,
00739       param.testflag & T_QUICK);
00740     }
00741     param.testflag=testflag;
00742     optimize_done=1;
00743   }
00744   if (!error)
00745   {
00746     if ((local_testflag & T_SORT_INDEX) &&
00747   (share->state.changed & STATE_NOT_SORTED_PAGES))
00748     {
00749       optimize_done=1;
00750       session->set_proc_info("Sorting index");
00751       error=mi_sort_index(&param,file,fixed_name);
00752     }
00753     if (!statistics_done && (local_testflag & T_STATISTICS))
00754     {
00755       if (share->state.changed & STATE_NOT_ANALYZED)
00756       {
00757   optimize_done=1;
00758   session->set_proc_info("Analyzing");
00759   error = chk_key(&param, file);
00760       }
00761       else
00762   local_testflag&= ~T_STATISTICS;   // Don't update statistics
00763     }
00764   }
00765   session->set_proc_info("Saving state");
00766   if (!error)
00767   {
00768     if ((share->state.changed & STATE_CHANGED) || mi_is_crashed(file))
00769     {
00770       share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
00771              STATE_CRASHED_ON_REPAIR);
00772       file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
00773     }
00774     /*
00775       the following 'if', thought conceptually wrong,
00776       is a useful optimization nevertheless.
00777     */
00778     if (file->state != &file->s->state.state)
00779       file->s->state.state = *file->state;
00780     if (file->s->base.auto_key)
00781       update_auto_increment_key(&param, file, 1);
00782     if (optimize_done)
00783       error = update_state_info(&param, file,
00784         UPDATE_TIME | UPDATE_OPEN_COUNT |
00785         (local_testflag &
00786          T_STATISTICS ? UPDATE_STAT : 0));
00787     info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
00788    HA_STATUS_CONST);
00789     if (rows != file->state->records && ! (param.testflag & T_VERY_SILENT))
00790     {
00791       char llbuff[22],llbuff2[22];
00792       mi_check_print_warning(&param,"Number of rows changed from %s to %s",
00793            internal::llstr(rows,llbuff),
00794            internal::llstr(file->state->records,llbuff2));
00795     }
00796   }
00797   else
00798   {
00799     mi_mark_crashed_on_repair(file);
00800     file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
00801     update_state_info(&param, file, 0);
00802   }
00803   session->set_proc_info(old_proc_info);
00804   mi_lock_database(file,F_UNLCK);
00805 
00806   return(error ? HA_ADMIN_FAILED :
00807         !optimize_done ? HA_ADMIN_ALREADY_DONE : HA_ADMIN_OK);
00808 }
00809 
00810 
00811 /*
00812   Disable indexes, making it persistent if requested.
00813 
00814   SYNOPSIS
00815     disable_indexes()
00816     mode        mode of operation:
00817                 HA_KEY_SWITCH_NONUNIQ      disable all non-unique keys
00818                 HA_KEY_SWITCH_ALL          disable all keys
00819                 HA_KEY_SWITCH_NONUNIQ_SAVE dis. non-uni. and make persistent
00820                 HA_KEY_SWITCH_ALL_SAVE     dis. all keys and make persistent
00821 
00822   IMPLEMENTATION
00823     HA_KEY_SWITCH_NONUNIQ       is not implemented.
00824     HA_KEY_SWITCH_ALL_SAVE      is not implemented.
00825 
00826   RETURN
00827     0  ok
00828     HA_ERR_WRONG_COMMAND  mode not implemented.
00829 */
00830 
00831 int ha_myisam::disable_indexes(uint32_t mode)
00832 {
00833   int error;
00834 
00835   if (mode == HA_KEY_SWITCH_ALL)
00836   {
00837     /* call a storage engine function to switch the key map */
00838     error= mi_disable_indexes(file);
00839   }
00840   else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
00841   {
00842     mi_extra(file, HA_EXTRA_NO_KEYS, 0);
00843     info(HA_STATUS_CONST);                        // Read new key info
00844     error= 0;
00845   }
00846   else
00847   {
00848     /* mode not implemented */
00849     error= HA_ERR_WRONG_COMMAND;
00850   }
00851   return error;
00852 }
00853 
00854 
00855 /*
00856   Enable indexes, making it persistent if requested.
00857 
00858   SYNOPSIS
00859     enable_indexes()
00860     mode        mode of operation:
00861                 HA_KEY_SWITCH_NONUNIQ      enable all non-unique keys
00862                 HA_KEY_SWITCH_ALL          enable all keys
00863                 HA_KEY_SWITCH_NONUNIQ_SAVE en. non-uni. and make persistent
00864                 HA_KEY_SWITCH_ALL_SAVE     en. all keys and make persistent
00865 
00866   DESCRIPTION
00867     Enable indexes, which might have been disabled by disable_index() before.
00868     The modes without _SAVE work only if both data and indexes are empty,
00869     since the MyISAM repair would enable them persistently.
00870     To be sure in these cases, call Cursor::delete_all_rows() before.
00871 
00872   IMPLEMENTATION
00873     HA_KEY_SWITCH_NONUNIQ       is not implemented.
00874     HA_KEY_SWITCH_ALL_SAVE      is not implemented.
00875 
00876   RETURN
00877     0  ok
00878     !=0  Error, among others:
00879     HA_ERR_CRASHED  data or index is non-empty. Delete all rows and retry.
00880     HA_ERR_WRONG_COMMAND  mode not implemented.
00881 */
00882 
00883 int ha_myisam::enable_indexes(uint32_t mode)
00884 {
00885   int error;
00886 
00887   if (mi_is_all_keys_active(file->s->state.key_map, file->s->base.keys))
00888   {
00889     /* All indexes are enabled already. */
00890     return 0;
00891   }
00892 
00893   if (mode == HA_KEY_SWITCH_ALL)
00894   {
00895     error= mi_enable_indexes(file);
00896     /*
00897        Do not try to repair on error,
00898        as this could make the enabled state persistent,
00899        but mode==HA_KEY_SWITCH_ALL forbids it.
00900     */
00901   }
00902   else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
00903   {
00904     Session *session= getTable()->in_use;
00905     boost::scoped_ptr<MI_CHECK> param_ap(new MI_CHECK);
00906     MI_CHECK &param= *param_ap.get();
00907     const char *save_proc_info= session->get_proc_info();
00908     session->set_proc_info("Creating index");
00909     myisamchk_init(&param);
00910     param.op_name= "recreating_index";
00911     param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
00912                      T_CREATE_MISSING_KEYS);
00913     param.myf_rw&= ~MY_WAIT_IF_FULL;
00914     param.sort_buffer_length=  static_cast<size_t>(sort_buffer_size);
00915     param.stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
00916     if ((error= (repair(session,param,0) != HA_ADMIN_OK)) && param.retry_repair)
00917     {
00918       errmsg_printf(error::WARN, "Warning: Enabling keys got errno %d on %s.%s, retrying",
00919                         errno, param.db_name, param.table_name);
00920       /* Repairing by sort failed. Now try standard repair method. */
00921       param.testflag&= ~(T_REP_BY_SORT | T_QUICK);
00922       error= (repair(session,param,0) != HA_ADMIN_OK);
00923       /*
00924         If the standard repair succeeded, clear all error messages which
00925         might have been set by the first repair. They can still be seen
00926         with SHOW WARNINGS then.
00927       */
00928       if (not error)
00929         session->clear_error();
00930     }
00931     info(HA_STATUS_CONST);
00932     session->set_proc_info(save_proc_info);
00933   }
00934   else
00935   {
00936     /* mode not implemented */
00937     error= HA_ERR_WRONG_COMMAND;
00938   }
00939   return error;
00940 }
00941 
00942 
00943 /*
00944   Test if indexes are disabled.
00945 
00946 
00947   SYNOPSIS
00948     indexes_are_disabled()
00949       no parameters
00950 
00951 
00952   RETURN
00953     0  indexes are not disabled
00954     1  all indexes are disabled
00955    [2  non-unique indexes are disabled - NOT YET IMPLEMENTED]
00956 */
00957 
00958 int ha_myisam::indexes_are_disabled(void)
00959 {
00960 
00961   return mi_indexes_are_disabled(file);
00962 }
00963 
00964 
00965 /*
00966   prepare for a many-rows insert operation
00967   e.g. - disable indexes (if they can be recreated fast) or
00968   activate special bulk-insert optimizations
00969 
00970   SYNOPSIS
00971     start_bulk_insert(rows)
00972     rows        Rows to be inserted
00973                 0 if we don't know
00974 
00975   NOTICE
00976     Do not forget to call end_bulk_insert() later!
00977 */
00978 
00979 void ha_myisam::start_bulk_insert(ha_rows rows)
00980 {
00981   Session *session= getTable()->in_use;
00982   ulong size= session->variables.read_buff_size;
00983 
00984   /* don't enable row cache if too few rows */
00985   if (! rows || (rows > MI_MIN_ROWS_TO_USE_WRITE_CACHE))
00986     mi_extra(file, HA_EXTRA_WRITE_CACHE, (void*) &size);
00987 
00988   can_enable_indexes= mi_is_all_keys_active(file->s->state.key_map,
00989                                             file->s->base.keys);
00990 
00991   /*
00992     Only disable old index if the table was empty and we are inserting
00993     a lot of rows.
00994     We should not do this for only a few rows as this is slower and
00995     we don't want to update the key statistics based of only a few rows.
00996   */
00997   if (file->state->records == 0 && can_enable_indexes &&
00998       (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES))
00999     mi_disable_non_unique_index(file,rows);
01000   else
01001     if (!file->bulk_insert &&
01002         (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT))
01003     {
01004       mi_init_bulk_insert(file,
01005                           (size_t)session->variables.bulk_insert_buff_size,
01006                           rows);
01007     }
01008 }
01009 
01010 /*
01011   end special bulk-insert optimizations,
01012   which have been activated by start_bulk_insert().
01013 
01014   SYNOPSIS
01015     end_bulk_insert()
01016     no arguments
01017 
01018   RETURN
01019     0     OK
01020     != 0  Error
01021 */
01022 
01023 int ha_myisam::end_bulk_insert()
01024 {
01025   mi_end_bulk_insert(file);
01026   int err=mi_extra(file, HA_EXTRA_NO_CACHE, 0);
01027   return err ? err : can_enable_indexes ?
01028                      enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE) : 0;
01029 }
01030 
01031 
01032 
01033 int ha_myisam::doUpdateRecord(const unsigned char *old_data, unsigned char *new_data)
01034 {
01035   return mi_update(file,old_data,new_data);
01036 }
01037 
01038 int ha_myisam::doDeleteRecord(const unsigned char *buf)
01039 {
01040   return mi_delete(file,buf);
01041 }
01042 
01043 
01044 int ha_myisam::doStartIndexScan(uint32_t idx, bool )
01045 {
01046   active_index=idx;
01047   //in_range_read= false;
01048   return 0;
01049 }
01050 
01051 
01052 int ha_myisam::doEndIndexScan()
01053 {
01054   active_index=MAX_KEY;
01055   return 0;
01056 }
01057 
01058 
01059 int ha_myisam::index_read_map(unsigned char *buf, const unsigned char *key,
01060                               key_part_map keypart_map,
01061                               enum ha_rkey_function find_flag)
01062 {
01063   assert(inited==INDEX);
01064   ha_statistic_increment(&system_status_var::ha_read_key_count);
01065   int error=mi_rkey(file, buf, active_index, key, keypart_map, find_flag);
01066   getTable()->status=error ? STATUS_NOT_FOUND: 0;
01067   return error;
01068 }
01069 
01070 int ha_myisam::index_read_idx_map(unsigned char *buf, uint32_t index, const unsigned char *key,
01071                                   key_part_map keypart_map,
01072                                   enum ha_rkey_function find_flag)
01073 {
01074   ha_statistic_increment(&system_status_var::ha_read_key_count);
01075   int error=mi_rkey(file, buf, index, key, keypart_map, find_flag);
01076   getTable()->status=error ? STATUS_NOT_FOUND: 0;
01077   return error;
01078 }
01079 
01080 int ha_myisam::index_read_last_map(unsigned char *buf, const unsigned char *key,
01081                                    key_part_map keypart_map)
01082 {
01083   assert(inited==INDEX);
01084   ha_statistic_increment(&system_status_var::ha_read_key_count);
01085   int error=mi_rkey(file, buf, active_index, key, keypart_map,
01086                     HA_READ_PREFIX_LAST);
01087   getTable()->status=error ? STATUS_NOT_FOUND: 0;
01088   return(error);
01089 }
01090 
01091 int ha_myisam::index_next(unsigned char *buf)
01092 {
01093   assert(inited==INDEX);
01094   ha_statistic_increment(&system_status_var::ha_read_next_count);
01095   int error=mi_rnext(file,buf,active_index);
01096   getTable()->status=error ? STATUS_NOT_FOUND: 0;
01097   return error;
01098 }
01099 
01100 int ha_myisam::index_prev(unsigned char *buf)
01101 {
01102   assert(inited==INDEX);
01103   ha_statistic_increment(&system_status_var::ha_read_prev_count);
01104   int error=mi_rprev(file,buf, active_index);
01105   getTable()->status=error ? STATUS_NOT_FOUND: 0;
01106   return error;
01107 }
01108 
01109 int ha_myisam::index_first(unsigned char *buf)
01110 {
01111   assert(inited==INDEX);
01112   ha_statistic_increment(&system_status_var::ha_read_first_count);
01113   int error=mi_rfirst(file, buf, active_index);
01114   getTable()->status=error ? STATUS_NOT_FOUND: 0;
01115   return error;
01116 }
01117 
01118 int ha_myisam::index_last(unsigned char *buf)
01119 {
01120   assert(inited==INDEX);
01121   ha_statistic_increment(&system_status_var::ha_read_last_count);
01122   int error=mi_rlast(file, buf, active_index);
01123   getTable()->status=error ? STATUS_NOT_FOUND: 0;
01124   return error;
01125 }
01126 
01127 int ha_myisam::index_next_same(unsigned char *buf,
01128              const unsigned char *,
01129              uint32_t )
01130 {
01131   int error;
01132   assert(inited==INDEX);
01133   ha_statistic_increment(&system_status_var::ha_read_next_count);
01134   do
01135   {
01136     error= mi_rnext_same(file,buf);
01137   } while (error == HA_ERR_RECORD_DELETED);
01138   getTable()->status=error ? STATUS_NOT_FOUND: 0;
01139   return error;
01140 }
01141 
01142 int ha_myisam::read_range_first(const key_range *start_key,
01143               const key_range *end_key,
01144               bool eq_range_arg,
01145                                 bool sorted /* ignored */)
01146 {
01147   int res;
01148   //if (!eq_range_arg)
01149   //  in_range_read= true;
01150 
01151   res= Cursor::read_range_first(start_key, end_key, eq_range_arg, sorted);
01152 
01153   //if (res)
01154   //  in_range_read= false;
01155   return res;
01156 }
01157 
01158 
01159 int ha_myisam::read_range_next()
01160 {
01161   int res= Cursor::read_range_next();
01162   //if (res)
01163   //  in_range_read= false;
01164   return res;
01165 }
01166 
01167 
01168 int ha_myisam::doStartTableScan(bool scan)
01169 {
01170   if (scan)
01171     return mi_scan_init(file);
01172   return mi_reset(file);                        // Free buffers
01173 }
01174 
01175 int ha_myisam::rnd_next(unsigned char *buf)
01176 {
01177   ha_statistic_increment(&system_status_var::ha_read_rnd_next_count);
01178   int error=mi_scan(file, buf);
01179   getTable()->status=error ? STATUS_NOT_FOUND: 0;
01180   return error;
01181 }
01182 
01183 int ha_myisam::rnd_pos(unsigned char *buf, unsigned char *pos)
01184 {
01185   ha_statistic_increment(&system_status_var::ha_read_rnd_count);
01186   int error=mi_rrnd(file, buf, internal::my_get_ptr(pos,ref_length));
01187   getTable()->status=error ? STATUS_NOT_FOUND: 0;
01188   return error;
01189 }
01190 
01191 
01192 void ha_myisam::position(const unsigned char *)
01193 {
01194   internal::my_off_t row_position= mi_position(file);
01195   internal::my_store_ptr(ref, ref_length, row_position);
01196 }
01197 
01198 int ha_myisam::info(uint32_t flag)
01199 {
01200   MI_ISAMINFO misam_info;
01201   char name_buff[FN_REFLEN];
01202 
01203   (void) mi_status(file,&misam_info,flag);
01204   if (flag & HA_STATUS_VARIABLE)
01205   {
01206     stats.records=           misam_info.records;
01207     stats.deleted=           misam_info.deleted;
01208     stats.data_file_length=  misam_info.data_file_length;
01209     stats.index_file_length= misam_info.index_file_length;
01210     stats.delete_length=     misam_info.delete_length;
01211     stats.check_time=        misam_info.check_time;
01212     stats.mean_rec_length=   misam_info.mean_reclength;
01213   }
01214   if (flag & HA_STATUS_CONST)
01215   {
01216     TableShare *share= getTable()->getMutableShare();
01217     stats.max_data_file_length=  misam_info.max_data_file_length;
01218     stats.max_index_file_length= misam_info.max_index_file_length;
01219     stats.create_time= misam_info.create_time;
01220     ref_length= misam_info.reflength;
01221     share->db_options_in_use= misam_info.options;
01222     stats.block_size= myisam_key_cache_block_size;        /* record block size */
01223 
01224     set_prefix(share->keys_in_use, share->sizeKeys());
01225     /*
01226      * Due to bug 394932 (32-bit solaris build failure), we need
01227      * to convert the uint64_t key_map member of the misam_info
01228      * structure in to a std::bitset so that we can logically and
01229      * it with the share->key_in_use key_map.
01230      */
01231     ostringstream ostr;
01232     string binary_key_map;
01233     uint64_t num= misam_info.key_map;
01234     /*
01235      * Convert the uint64_t to a binary
01236      * string representation of it.
01237      */
01238     while (num > 0)
01239     {
01240       uint64_t bin_digit= num % 2;
01241       ostr << bin_digit;
01242       num/= 2;
01243     }
01244     binary_key_map.append(ostr.str());
01245     /*
01246      * Now we have the binary string representation of the
01247      * flags, we need to fill that string representation out
01248      * with the appropriate number of bits. This is needed
01249      * since key_map is declared as a std::bitset of a certain bit
01250      * width that depends on the MAX_INDEXES variable. 
01251      */
01252     if (MAX_INDEXES <= 64)
01253     {
01254       size_t len= 72 - binary_key_map.length();
01255       string all_zeros(len, '0');
01256       binary_key_map.insert(binary_key_map.begin(),
01257                             all_zeros.begin(),
01258                             all_zeros.end());
01259     }
01260     else
01261     {
01262       size_t len= (MAX_INDEXES + 7) / 8 * 8;
01263       string all_zeros(len, '0');
01264       binary_key_map.insert(binary_key_map.begin(),
01265                             all_zeros.begin(),
01266                             all_zeros.end());
01267     }
01268     key_map tmp_map(binary_key_map);
01269     share->keys_in_use&= tmp_map;
01270     share->keys_for_keyread&= share->keys_in_use;
01271     share->db_record_offset= misam_info.record_offset;
01272     if (share->key_parts)
01273       memcpy(getTable()->key_info[0].rec_per_key,
01274        misam_info.rec_per_key,
01275        sizeof(getTable()->key_info[0].rec_per_key)*share->key_parts);
01276     assert(share->getType() != message::Table::STANDARD);
01277 
01278    /*
01279      Set data_file_name and index_file_name to point at the symlink value
01280      if table is symlinked (Ie;  Real name is not same as generated name)
01281    */
01282     data_file_name= index_file_name= 0;
01283     internal::fn_format(name_buff, file->filename, "", MI_NAME_DEXT,
01284               MY_APPEND_EXT | MY_UNPACK_FILENAME);
01285     if (strcmp(name_buff, misam_info.data_file_name))
01286       data_file_name=misam_info.data_file_name;
01287     internal::fn_format(name_buff, file->filename, "", MI_NAME_IEXT,
01288               MY_APPEND_EXT | MY_UNPACK_FILENAME);
01289     if (strcmp(name_buff, misam_info.index_file_name))
01290       index_file_name=misam_info.index_file_name;
01291   }
01292   if (flag & HA_STATUS_ERRKEY)
01293   {
01294     errkey  = misam_info.errkey;
01295     internal::my_store_ptr(dup_ref, ref_length, misam_info.dupp_key_pos);
01296   }
01297   if (flag & HA_STATUS_TIME)
01298     stats.update_time = misam_info.update_time;
01299   if (flag & HA_STATUS_AUTO)
01300     stats.auto_increment_value= misam_info.auto_increment;
01301 
01302   return 0;
01303 }
01304 
01305 
01306 int ha_myisam::extra(enum ha_extra_function operation)
01307 {
01308   return mi_extra(file, operation, 0);
01309 }
01310 
01311 int ha_myisam::reset(void)
01312 {
01313   return mi_reset(file);
01314 }
01315 
01316 /* To be used with WRITE_CACHE and EXTRA_CACHE */
01317 
01318 int ha_myisam::extra_opt(enum ha_extra_function operation, uint32_t cache_size)
01319 {
01320   return mi_extra(file, operation, (void*) &cache_size);
01321 }
01322 
01323 int ha_myisam::delete_all_rows()
01324 {
01325   return mi_delete_all_rows(file);
01326 }
01327 
01328 int MyisamEngine::doDropTable(Session &session,
01329                               const identifier::Table &identifier)
01330 {
01331   session.getMessageCache().removeTableMessage(identifier);
01332 
01333   return mi_delete_table(identifier.getPath().c_str());
01334 }
01335 
01336 
01337 int ha_myisam::external_lock(Session *session, int lock_type)
01338 {
01339   file->in_use= session;
01340   return mi_lock_database(file, !getTable()->getShare()->getType() ?
01341         lock_type : ((lock_type == F_UNLCK) ?
01342                F_UNLCK : F_EXTRA_LCK));
01343 }
01344 
01345 int MyisamEngine::doCreateTable(Session &session,
01346                                 Table& table_arg,
01347                                 const identifier::Table &identifier,
01348                                 const message::Table& create_proto)
01349 {
01350   int error;
01351   uint32_t create_flags= 0, create_records;
01352   char buff[FN_REFLEN];
01353   MI_KEYDEF *keydef;
01354   MI_COLUMNDEF *recinfo;
01355   MI_CREATE_INFO create_info;
01356   TableShare *share= table_arg.getMutableShare();
01357   uint32_t options= share->db_options_in_use;
01358   if ((error= table2myisam(&table_arg, &keydef, &recinfo, &create_records)))
01359     return(error);
01360   memset(&create_info, 0, sizeof(create_info));
01361   create_info.max_rows= create_proto.options().max_rows();
01362   create_info.reloc_rows= create_proto.options().min_rows();
01363   create_info.with_auto_increment= share->next_number_key_offset == 0;
01364   create_info.auto_increment= (create_proto.options().has_auto_increment_value() ?
01365                                create_proto.options().auto_increment_value() -1 :
01366                                (uint64_t) 0);
01367   create_info.data_file_length= (create_proto.options().max_rows() *
01368                                  create_proto.options().avg_row_length());
01369   create_info.data_file_name= NULL;
01370   create_info.index_file_name=  NULL;
01371   create_info.language= share->table_charset->number;
01372 
01373   if (create_proto.type() == message::Table::TEMPORARY)
01374     create_flags|= HA_CREATE_TMP_TABLE;
01375   if (options & HA_OPTION_PACK_RECORD)
01376     create_flags|= HA_PACK_RECORD;
01377 
01378   /* TODO: Check that the following internal::fn_format is really needed */
01379   error= mi_create(internal::fn_format(buff, identifier.getPath().c_str(), "", "",
01380                                        MY_UNPACK_FILENAME|MY_APPEND_EXT),
01381                    share->sizeKeys(), keydef,
01382                    create_records, recinfo,
01383                    0, (MI_UNIQUEDEF*) 0,
01384                    &create_info, create_flags);
01385   free((unsigned char*) recinfo);
01386 
01387   session.getMessageCache().storeTableMessage(identifier, create_proto);
01388 
01389   return error;
01390 }
01391 
01392 
01393 int MyisamEngine::doRenameTable(Session &session, const identifier::Table &from, const identifier::Table &to)
01394 {
01395   session.getMessageCache().renameTableMessage(from, to);
01396 
01397   return mi_rename(from.getPath().c_str(), to.getPath().c_str());
01398 }
01399 
01400 
01401 void ha_myisam::get_auto_increment(uint64_t ,
01402                                    uint64_t ,
01403                                    uint64_t ,
01404                                    uint64_t *first_value,
01405                                    uint64_t *nb_reserved_values)
01406 {
01407   uint64_t nr;
01408   int error;
01409   unsigned char key[MI_MAX_KEY_LENGTH];
01410 
01411   if (!getTable()->getShare()->next_number_key_offset)
01412   {           // Autoincrement at key-start
01413     ha_myisam::info(HA_STATUS_AUTO);
01414     *first_value= stats.auto_increment_value;
01415     /* MyISAM has only table-level lock, so reserves to +inf */
01416     *nb_reserved_values= UINT64_MAX;
01417     return;
01418   }
01419 
01420   /* it's safe to call the following if bulk_insert isn't on */
01421   mi_flush_bulk_insert(file, getTable()->getShare()->next_number_index);
01422 
01423   (void) extra(HA_EXTRA_KEYREAD);
01424   key_copy(key, getTable()->getInsertRecord(),
01425            &getTable()->key_info[getTable()->getShare()->next_number_index],
01426            getTable()->getShare()->next_number_key_offset);
01427   error= mi_rkey(file, getTable()->getUpdateRecord(), (int) getTable()->getShare()->next_number_index,
01428                  key, make_prev_keypart_map(getTable()->getShare()->next_number_keypart),
01429                  HA_READ_PREFIX_LAST);
01430   if (error)
01431     nr= 1;
01432   else
01433   {
01434     /* Get data from getUpdateRecord() */
01435     nr= ((uint64_t) getTable()->next_number_field->
01436          val_int_offset(getTable()->getShare()->rec_buff_length)+1);
01437   }
01438   extra(HA_EXTRA_NO_KEYREAD);
01439   *first_value= nr;
01440   /*
01441     MySQL needs to call us for next row: assume we are inserting ("a",null)
01442     here, we return 3, and next this statement will want to insert ("b",null):
01443     there is no reason why ("b",3+1) would be the good row to insert: maybe it
01444     already exists, maybe 3+1 is too large...
01445   */
01446   *nb_reserved_values= 1;
01447 }
01448 
01449 
01450 /*
01451   Find out how many rows there is in the given range
01452 
01453   SYNOPSIS
01454     records_in_range()
01455     inx     Index to use
01456     min_key   Start of range.  Null pointer if from first key
01457     max_key   End of range. Null pointer if to last key
01458 
01459   NOTES
01460     min_key.flag can have one of the following values:
01461       HA_READ_KEY_EXACT   Include the key in the range
01462       HA_READ_AFTER_KEY   Don't include key in range
01463 
01464     max_key.flag can have one of the following values:
01465       HA_READ_BEFORE_KEY  Don't include key in range
01466       HA_READ_AFTER_KEY   Include all 'end_key' values in the range
01467 
01468   RETURN
01469    HA_POS_ERROR   Something is wrong with the index tree.
01470    0      There is no matching keys in the given range
01471    number > 0   There is approximately 'number' matching rows in
01472       the range.
01473 */
01474 
01475 ha_rows ha_myisam::records_in_range(uint32_t inx, key_range *min_key,
01476                                     key_range *max_key)
01477 {
01478   return (ha_rows) mi_records_in_range(file, (int) inx, min_key, max_key);
01479 }
01480 
01481 
01482 uint32_t ha_myisam::checksum() const
01483 {
01484   return (uint)file->state->checksum;
01485 }
01486 
01487 static int myisam_init(module::Context &context)
01488 { 
01489   context.add(new MyisamEngine(engine_name));
01490   context.registerVariable(new sys_var_constrained_value<size_t>("sort-buffer-size",
01491                                                                  sort_buffer_size));
01492   context.registerVariable(new sys_var_uint64_t_ptr("max_sort_file_size",
01493                                                     &max_sort_file_size,
01494                                                     context.getOptions()["max-sort-file-size"].as<uint64_t>()));
01495 
01496   return 0;
01497 }
01498 
01499 
01500 static void init_options(drizzled::module::option_context &context)
01501 {
01502   context("max-sort-file-size",
01503           po::value<uint64_t>(&max_sort_file_size)->default_value(INT32_MAX),
01504           _("Don't use the fast sort index method to created index if the temporary file would get bigger than this."));
01505   context("sort-buffer-size",
01506           po::value<sort_buffer_constraint>(&sort_buffer_size)->default_value(8192*1024),
01507           _("The buffer that is allocated when sorting the index when doing a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE."));
01508 }
01509 
01510 
01511 DRIZZLE_DECLARE_PLUGIN
01512 {
01513   DRIZZLE_VERSION_ID,
01514   "MyISAM",
01515   "2.0",
01516   "MySQL AB",
01517   "Default engine as of MySQL 3.23 with great performance",
01518   PLUGIN_LICENSE_GPL,
01519   myisam_init, /* Plugin Init */
01520   NULL,           /* depends */
01521   init_options                        /* config options                  */
01522 }
01523 DRIZZLE_DECLARE_PLUGIN_END;