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