Drizzled Public API Documentation

ha_myisam.cc
1 /* Copyright (C) 2000-2006 MySQL AB
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 
17 
18 #include <config.h>
19 #include <drizzled/internal/my_bit.h>
20 #include "myisampack.h"
21 #include "ha_myisam.h"
22 #include "myisam_priv.h"
23 #include <drizzled/option.h>
24 #include <drizzled/internal/my_bit.h>
25 #include <drizzled/internal/m_string.h>
26 #include <drizzled/util/test.h>
27 #include <drizzled/error.h>
28 #include <drizzled/errmsg_print.h>
29 #include <drizzled/gettext.h>
30 #include <drizzled/session.h>
31 #include <drizzled/plugin.h>
32 #include <drizzled/plugin/client.h>
33 #include <drizzled/table.h>
34 #include <drizzled/memory/multi_malloc.h>
35 #include <drizzled/plugin/daemon.h>
36 #include <drizzled/session/table_messages.h>
37 #include <drizzled/plugin/storage_engine.h>
38 #include <drizzled/key.h>
39 #include <drizzled/statistics_variables.h>
40 #include <drizzled/system_variables.h>
41 
42 #include <boost/algorithm/string.hpp>
43 #include <boost/scoped_ptr.hpp>
44 
45 #include <string>
46 #include <sstream>
47 #include <map>
48 #include <algorithm>
49 #include <memory>
50 #include <boost/program_options.hpp>
52 
53 namespace po= boost::program_options;
54 
55 using namespace std;
56 using namespace drizzled;
57 
58 static const string engine_name("MyISAM");
59 
60 boost::mutex THR_LOCK_myisam;
61 
62 static uint32_t myisam_key_cache_block_size= KEY_CACHE_BLOCK_SIZE;
63 static uint64_t max_sort_file_size;
65 static sort_buffer_constraint sort_buffer_size;
66 
67 /*****************************************************************************
68 ** MyISAM tables
69 *****************************************************************************/
70 
71 static const char *ha_myisam_exts[] = {
72  ".MYI",
73  ".MYD",
74  NULL
75 };
76 
77 class MyisamEngine : public plugin::StorageEngine
78 {
79 public:
80  explicit MyisamEngine(string name_arg) :
81  plugin::StorageEngine(name_arg,
82  HTON_CAN_INDEX_BLOBS |
83  HTON_STATS_RECORDS_IS_EXACT |
84  HTON_TEMPORARY_ONLY |
85  HTON_NULL_IN_KEY |
86  HTON_HAS_RECORDS |
87  HTON_DUPLICATE_POS |
88  HTON_AUTO_PART_KEY |
89  HTON_SKIP_STORE_LOCK)
90  {
91  }
92 
93  virtual ~MyisamEngine()
94  {
95  mi_panic(HA_PANIC_CLOSE);
96  }
97 
98  virtual Cursor *create(Table &table)
99  {
100  return new ha_myisam(*this, table);
101  }
102 
103  const char **bas_ext() const {
104  return ha_myisam_exts;
105  }
106 
107  int doCreateTable(Session&,
108  Table& table_arg,
109  const identifier::Table &identifier,
110  const message::Table&);
111 
112  int doRenameTable(Session&, const identifier::Table &from, const identifier::Table &to);
113 
114  int doDropTable(Session&, const identifier::Table &identifier);
115 
116  int doGetTableDefinition(Session& session,
117  const identifier::Table &identifier,
118  message::Table &table_message);
119 
120  uint32_t max_supported_keys() const { return MI_MAX_KEY; }
121  uint32_t max_supported_key_length() const { return MI_MAX_KEY_LENGTH; }
122  uint32_t max_supported_key_part_length() const { return MI_MAX_KEY_LENGTH; }
123 
124  uint32_t index_flags(enum ha_key_alg) const
125  {
126  return (HA_READ_NEXT |
127  HA_READ_PREV |
128  HA_READ_RANGE |
129  HA_READ_ORDER |
130  HA_KEYREAD_ONLY);
131  }
132  bool doDoesTableExist(Session& session, const identifier::Table &identifier);
133 
134  void doGetTableIdentifiers(drizzled::CachedDirectory &directory,
135  const drizzled::identifier::Schema &schema_identifier,
136  drizzled::identifier::table::vector &set_of_identifiers);
137  bool validateCreateTableOption(const std::string &key, const std::string &state)
138  {
139  (void)state;
140  if (boost::iequals(key, "ROW_FORMAT"))
141  {
142  return true;
143  }
144 
145  return false;
146  }
147 };
148 
149 void MyisamEngine::doGetTableIdentifiers(drizzled::CachedDirectory&,
151  drizzled::identifier::table::vector&)
152 {
153 }
154 
155 bool MyisamEngine::doDoesTableExist(Session &session, const identifier::Table &identifier)
156 {
157  return session.getMessageCache().doesTableMessageExist(identifier);
158 }
159 
160 int MyisamEngine::doGetTableDefinition(Session &session,
161  const identifier::Table &identifier,
162  message::Table &table_message)
163 {
164  if (session.getMessageCache().getTableMessage(identifier, table_message))
165  return EEXIST;
166  return ENOENT;
167 }
168 
169 /*
170  Convert to push_Warnings if you ever care about this, otherwise, it is a no-op.
171 */
172 
173 static void mi_check_print_msg(MI_CHECK *, const char* ,
174  const char *, va_list )
175 {
176 }
177 
178 
179 /*
180  Convert Table object to MyISAM key and column definition
181 
182  SYNOPSIS
183  table2myisam()
184  table_arg in Table object.
185  keydef_out out MyISAM key definition.
186  recinfo_out out MyISAM column definition.
187  records_out out Number of fields.
188 
189  DESCRIPTION
190  This function will allocate and initialize MyISAM key and column
191  definition for further use in mi_create or for a check for underlying
192  table conformance in merge engine.
193 
194  The caller needs to free *recinfo_out after use. Since *recinfo_out
195  and *keydef_out are allocated with a multi_malloc, *keydef_out
196  is freed automatically when *recinfo_out is freed.
197 
198  RETURN VALUE
199  0 OK
200  !0 error code
201 */
202 
203 static int table2myisam(Table *table_arg, MI_KEYDEF **keydef_out,
204  MI_COLUMNDEF **recinfo_out, uint32_t *records_out)
205 {
206  uint32_t i, j, recpos, minpos, fieldpos, temp_length, length;
207  enum ha_base_keytype type= HA_KEYTYPE_BINARY;
208  unsigned char *record;
209  MI_KEYDEF *keydef;
210  MI_COLUMNDEF *recinfo, *recinfo_pos;
211  HA_KEYSEG *keyseg;
212  TableShare *share= table_arg->getMutableShare();
213  uint32_t options= share->db_options_in_use;
214  if (!(memory::multi_malloc(false,
215  recinfo_out, (share->sizeFields() * 2 + 2) * sizeof(MI_COLUMNDEF),
216  keydef_out, share->sizeKeys() * sizeof(MI_KEYDEF),
217  &keyseg, (share->key_parts + share->sizeKeys()) * sizeof(HA_KEYSEG),
218  NULL)))
219  return(HA_ERR_OUT_OF_MEM);
220  keydef= *keydef_out;
221  recinfo= *recinfo_out;
222  for (i= 0; i < share->sizeKeys(); i++)
223  {
224  KeyInfo *pos= &table_arg->key_info[i];
225  keydef[i].flag= ((uint16_t) pos->flags & (HA_NOSAME));
226  keydef[i].key_alg= HA_KEY_ALG_BTREE;
227  keydef[i].block_length= pos->block_size;
228  keydef[i].seg= keyseg;
229  keydef[i].keysegs= pos->key_parts;
230  for (j= 0; j < pos->key_parts; j++)
231  {
232  Field *field= pos->key_part[j].field;
233  type= field->key_type();
234  keydef[i].seg[j].flag= pos->key_part[j].key_part_flag;
235 
236  if (options & HA_OPTION_PACK_KEYS ||
237  (pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY |
238  HA_SPACE_PACK_USED)))
239  {
240  if (pos->key_part[j].length > 8 &&
241  (type == HA_KEYTYPE_TEXT ||
242  (type == HA_KEYTYPE_BINARY && !field->zero_pack())))
243  {
244  /* No blobs here */
245  if (j == 0)
246  keydef[i].flag|= HA_PACK_KEY;
247  if ((((int) (pos->key_part[j].length - field->decimals())) >= 4))
248  keydef[i].seg[j].flag|= HA_SPACE_PACK;
249  }
250  else if (j == 0 && (!(pos->flags & HA_NOSAME) || pos->key_length > 16))
251  keydef[i].flag|= HA_BINARY_PACK_KEY;
252  }
253  keydef[i].seg[j].type= (int) type;
254  keydef[i].seg[j].start= pos->key_part[j].offset;
255  keydef[i].seg[j].length= pos->key_part[j].length;
256  keydef[i].seg[j].bit_start= keydef[i].seg[j].bit_end=
257  keydef[i].seg[j].bit_length= 0;
258  keydef[i].seg[j].bit_pos= 0;
259  keydef[i].seg[j].language= field->charset()->number;
260 
261  if (field->null_ptr)
262  {
263  keydef[i].seg[j].null_bit= field->null_bit;
264  keydef[i].seg[j].null_pos= (uint) (field->null_ptr-
265  (unsigned char*) table_arg->getInsertRecord());
266  }
267  else
268  {
269  keydef[i].seg[j].null_bit= 0;
270  keydef[i].seg[j].null_pos= 0;
271  }
272  if (field->type() == DRIZZLE_TYPE_BLOB)
273  {
274  keydef[i].seg[j].flag|= HA_BLOB_PART;
275  /* save number of bytes used to pack length */
276  keydef[i].seg[j].bit_start= (uint) (field->pack_length() -
277  share->sizeBlobPtr());
278  }
279  }
280  keyseg+= pos->key_parts;
281  }
282  if (table_arg->found_next_number_field)
283  keydef[share->next_number_index].flag|= HA_AUTO_KEY;
284  record= table_arg->getInsertRecord();
285  recpos= 0;
286  recinfo_pos= recinfo;
287 
288  while (recpos < (uint) share->sizeStoredRecord())
289  {
290  Field **field, *found= 0;
291  minpos= share->getRecordLength();
292  length= 0;
293 
294  for (field= table_arg->getFields(); *field; field++)
295  {
296  if ((fieldpos= (*field)->offset(record)) >= recpos &&
297  fieldpos <= minpos)
298  {
299  /* skip null fields */
300  if (!(temp_length= (*field)->pack_length_in_rec()))
301  continue; /* Skip null-fields */
302 
303  if (! found || fieldpos < minpos ||
304  (fieldpos == minpos && temp_length < length))
305  {
306  minpos= fieldpos;
307  found= *field;
308  length= temp_length;
309  }
310  }
311  }
312  if (recpos != minpos)
313  { // Reserved space (Null bits?)
314  memset(recinfo_pos, 0, sizeof(*recinfo_pos));
315  recinfo_pos->type= (int) FIELD_NORMAL;
316  recinfo_pos++->length= (uint16_t) (minpos - recpos);
317  }
318  if (!found)
319  break;
320 
321  if (found->flags & BLOB_FLAG)
322  recinfo_pos->type= (int) FIELD_BLOB;
323  else if (found->type() == DRIZZLE_TYPE_VARCHAR)
324  recinfo_pos->type= FIELD_VARCHAR;
325  else if (!(options & HA_OPTION_PACK_RECORD))
326  recinfo_pos->type= (int) FIELD_NORMAL;
327  else if (found->zero_pack())
328  recinfo_pos->type= (int) FIELD_SKIP_ZERO;
329  else
330  recinfo_pos->type= (int) ((length <= 3) ? FIELD_NORMAL : FIELD_SKIP_PRESPACE);
331  if (found->null_ptr)
332  {
333  recinfo_pos->null_bit= found->null_bit;
334  recinfo_pos->null_pos= (uint) (found->null_ptr -
335  (unsigned char*) table_arg->getInsertRecord());
336  }
337  else
338  {
339  recinfo_pos->null_bit= 0;
340  recinfo_pos->null_pos= 0;
341  }
342  (recinfo_pos++)->length= (uint16_t) length;
343  recpos= minpos + length;
344  }
345  *records_out= (uint) (recinfo_pos - recinfo);
346  return(0);
347 }
348 
350 {
351  file->s->state.auto_increment= value;
352  return 0;
353 }
354 
355 /*
356  Check for underlying table conformance
357 
358  SYNOPSIS
359  check_definition()
360  t1_keyinfo in First table key definition
361  t1_recinfo in First table record definition
362  t1_keys in Number of keys in first table
363  t1_recs in Number of records in first table
364  t2_keyinfo in Second table key definition
365  t2_recinfo in Second table record definition
366  t2_keys in Number of keys in second table
367  t2_recs in Number of records in second table
368  strict in Strict check switch
369 
370  DESCRIPTION
371  This function compares two MyISAM definitions. By intention it was done
372  to compare merge table definition against underlying table definition.
373  It may also be used to compare dot-frm and MYI definitions of MyISAM
374  table as well to compare different MyISAM table definitions.
375 
376  For merge table it is not required that number of keys in merge table
377  must exactly match number of keys in underlying table. When calling this
378  function for underlying table conformance check, 'strict' flag must be
379  set to false, and converted merge definition must be passed as t1_*.
380 
381  Otherwise 'strict' flag must be set to 1 and it is not required to pass
382  converted dot-frm definition as t1_*.
383 
384  RETURN VALUE
385  0 - Equal definitions.
386  1 - Different definitions.
387 
388  TODO
389  - compare FULLTEXT keys;
390  - compare SPATIAL keys;
391  - compare FIELD_SKIP_ZERO which is converted to FIELD_NORMAL correctly
392  (should be corretly detected in table2myisam).
393 */
394 
395 static int check_definition(MI_KEYDEF *t1_keyinfo, MI_COLUMNDEF *t1_recinfo,
396  uint32_t t1_keys, uint32_t t1_recs,
397  MI_KEYDEF *t2_keyinfo, MI_COLUMNDEF *t2_recinfo,
398  uint32_t t2_keys, uint32_t t2_recs, bool strict)
399 {
400  uint32_t i, j;
401  if ((strict ? t1_keys != t2_keys : t1_keys > t2_keys))
402  {
403  return(1);
404  }
405  if (t1_recs != t2_recs)
406  {
407  return(1);
408  }
409  for (i= 0; i < t1_keys; i++)
410  {
411  HA_KEYSEG *t1_keysegs= t1_keyinfo[i].seg;
412  HA_KEYSEG *t2_keysegs= t2_keyinfo[i].seg;
413  if (t1_keyinfo[i].keysegs != t2_keyinfo[i].keysegs ||
414  t1_keyinfo[i].key_alg != t2_keyinfo[i].key_alg)
415  {
416  return(1);
417  }
418  for (j= t1_keyinfo[i].keysegs; j--;)
419  {
420  uint8_t t1_keysegs_j__type= t1_keysegs[j].type;
421 
422  /*
423  Table migration from 4.1 to 5.1. In 5.1 a *TEXT key part is
424  always HA_KEYTYPE_VARTEXT2. In 4.1 we had only the equivalent of
425  HA_KEYTYPE_VARTEXT1. Since we treat both the same on MyISAM
426  level, we can ignore a mismatch between these types.
427  */
428  if ((t1_keysegs[j].flag & HA_BLOB_PART) &&
429  (t2_keysegs[j].flag & HA_BLOB_PART))
430  {
431  if ((t1_keysegs_j__type == HA_KEYTYPE_VARTEXT2) &&
432  (t2_keysegs[j].type == HA_KEYTYPE_VARTEXT1))
433  t1_keysegs_j__type= HA_KEYTYPE_VARTEXT1;
434  else if ((t1_keysegs_j__type == HA_KEYTYPE_VARBINARY2) &&
435  (t2_keysegs[j].type == HA_KEYTYPE_VARBINARY1))
436  t1_keysegs_j__type= HA_KEYTYPE_VARBINARY1;
437  }
438 
439  if (t1_keysegs_j__type != t2_keysegs[j].type ||
440  t1_keysegs[j].language != t2_keysegs[j].language ||
441  t1_keysegs[j].null_bit != t2_keysegs[j].null_bit ||
442  t1_keysegs[j].length != t2_keysegs[j].length)
443  {
444  return(1);
445  }
446  }
447  }
448  for (i= 0; i < t1_recs; i++)
449  {
450  MI_COLUMNDEF *t1_rec= &t1_recinfo[i];
451  MI_COLUMNDEF *t2_rec= &t2_recinfo[i];
452  /*
453  FIELD_SKIP_ZERO can be changed to FIELD_NORMAL in mi_create,
454  see NOTE1 in mi_create.c
455  */
456  if ((t1_rec->type != t2_rec->type &&
457  !(t1_rec->type == (int) FIELD_SKIP_ZERO &&
458  t1_rec->length == 1 &&
459  t2_rec->type == (int) FIELD_NORMAL)) ||
460  t1_rec->length != t2_rec->length ||
461  t1_rec->null_bit != t2_rec->null_bit)
462  {
463  return(1);
464  }
465  }
466  return(0);
467 }
468 
469 
470 volatile int *killed_ptr(MI_CHECK *param)
471 {
472  /* In theory Unsafe conversion, but should be ok for now */
473  return (int*) (((Session *)(param->session))->getKilledPtr());
474 }
475 
476 void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
477 {
478  param->error_printed|=1;
479  param->out_flag|= O_DATA_LOST;
480  va_list args;
481  va_start(args, fmt);
482  mi_check_print_msg(param, "error", fmt, args);
483  va_end(args);
484 }
485 
486 void mi_check_print_info(MI_CHECK *param, const char *fmt,...)
487 {
488  va_list args;
489  va_start(args, fmt);
490  mi_check_print_msg(param, "info", fmt, args);
491  va_end(args);
492 }
493 
494 void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
495 {
496  param->warning_printed=1;
497  param->out_flag|= O_DATA_LOST;
498  va_list args;
499  va_start(args, fmt);
500  mi_check_print_msg(param, "warning", fmt, args);
501  va_end(args);
502 }
503 
519 void _mi_report_crashed(MI_INFO *file, const char *message,
520  const char *sfile, uint32_t sline)
521 {
522  Session *cur_session;
523  if ((cur_session= file->in_use))
524  {
525  errmsg_printf(error::ERROR, _("Got an error from thread_id=%"PRIu64", %s:%d"),
526  cur_session->thread_id,
527  sfile, sline);
528  }
529  else
530  {
531  errmsg_printf(error::ERROR, _("Got an error from unknown thread, %s:%d"), sfile, sline);
532  }
533 
534  if (message)
535  errmsg_printf(error::ERROR, "%s", message);
536 
537  list<Session *>::iterator it= file->s->in_use->begin();
538  while (it != file->s->in_use->end())
539  {
540  errmsg_printf(error::ERROR, "%s", _("Unknown thread accessing table"));
541  ++it;
542  }
543 }
544 
545 ha_myisam::ha_myisam(plugin::StorageEngine &engine_arg,
546  Table &table_arg)
547  : Cursor(engine_arg, table_arg),
548  file(0),
549  can_enable_indexes(true),
550  is_ordered(true)
551 { }
552 
553 Cursor *ha_myisam::clone(memory::Root *mem_root)
554 {
555  ha_myisam *new_handler= static_cast <ha_myisam *>(Cursor::clone(mem_root));
556  if (new_handler)
557  new_handler->file->state= file->state;
558  return new_handler;
559 }
560 
561 const char *ha_myisam::index_type(uint32_t )
562 {
563  return "BTREE";
564 }
565 
566 /* Name is here without an extension */
567 int ha_myisam::doOpen(const drizzled::identifier::Table &identifier, int mode, uint32_t test_if_locked)
568 {
569  MI_KEYDEF *keyinfo;
570  MI_COLUMNDEF *recinfo= 0;
571  uint32_t recs;
572  uint32_t i;
573 
574  /*
575  If the user wants to have memory mapped data files, add an
576  open_flag. Do not memory map temporary tables because they are
577  expected to be inserted and thus extended a lot. Memory mapping is
578  efficient for files that keep their size, but very inefficient for
579  growing files. Using an open_flag instead of calling mi_extra(...
580  HA_EXTRA_MMAP ...) after mi_open() has the advantage that the
581  mapping is not repeated for every open, but just done on the initial
582  open, when the MyISAM share is created. Everytime the server
583  requires to open a new instance of a table it calls this method. We
584  will always supply HA_OPEN_MMAP for a permanent table. However, the
585  MyISAM storage engine will ignore this flag if this is a secondary
586  open of a table that is in use by other threads already (if the
587  MyISAM share exists already).
588  */
589  if (!(file= mi_open(identifier, mode, test_if_locked)))
590  return (errno ? errno : -1);
591 
592  if (!getTable()->getShare()->getType()) /* No need to perform a check for tmp table */
593  {
594  if ((errno= table2myisam(getTable(), &keyinfo, &recinfo, &recs)))
595  {
596  goto err;
597  }
598  if (check_definition(keyinfo, recinfo, getTable()->getShare()->sizeKeys(), recs,
599  file->s->keyinfo, file->s->rec,
600  file->s->base.keys, file->s->base.fields, true))
601  {
602  errno= HA_ERR_CRASHED;
603  goto err;
604  }
605  }
606 
607  assert(test_if_locked);
608  if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE))
609  mi_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0);
610 
611  info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
612  if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
613  mi_extra(file, HA_EXTRA_WAIT_LOCK, 0);
614  if (!getTable()->getShare()->db_record_offset)
615  is_ordered= false;
616 
617 
618  keys_with_parts.reset();
619  for (i= 0; i < getTable()->getShare()->sizeKeys(); i++)
620  {
621  getTable()->key_info[i].block_size= file->s->keyinfo[i].block_length;
622 
623  KeyPartInfo *kp= getTable()->key_info[i].key_part;
624  KeyPartInfo *kp_end= kp + getTable()->key_info[i].key_parts;
625  for (; kp != kp_end; kp++)
626  {
627  if (!kp->field->part_of_key.test(i))
628  {
629  keys_with_parts.set(i);
630  break;
631  }
632  }
633  }
634  errno= 0;
635  goto end;
636  err:
637  this->close();
638  end:
639  /*
640  Both recinfo and keydef are allocated by multi_malloc(), thus only
641  recinfo must be freed.
642  */
643  if (recinfo)
644  free((unsigned char*) recinfo);
645  return errno;
646 }
647 
648 int ha_myisam::close(void)
649 {
650  MI_INFO *tmp=file;
651  file=0;
652  return mi_close(tmp);
653 }
654 
655 int ha_myisam::doInsertRecord(unsigned char *buf)
656 {
657  /*
658  If we have an auto_increment column and we are writing a changed row
659  or a new row, then update the auto_increment value in the record.
660  */
661  if (getTable()->next_number_field && buf == getTable()->getInsertRecord())
662  {
663  int error;
664  if ((error= update_auto_increment()))
665  return error;
666  }
667  return mi_write(file,buf);
668 }
669 
670 
671 int ha_myisam::repair(Session *session, MI_CHECK &param, bool do_optimize)
672 {
673  int error=0;
674  uint32_t local_testflag= param.testflag;
675  bool optimize_done= !do_optimize, statistics_done=0;
676  const char *old_proc_info= session->get_proc_info();
677  char fixed_name[FN_REFLEN];
678  MYISAM_SHARE* share = file->s;
679  ha_rows rows= file->state->records;
680 
681  /*
682  Normally this method is entered with a properly opened table. If the
683  repair fails, it can be repeated with more elaborate options. Under
684  special circumstances it can happen that a repair fails so that it
685  closed the data file and cannot re-open it. In this case file->dfile
686  is set to -1. We must not try another repair without an open data
687  file. (Bug #25289)
688  */
689  if (file->dfile == -1)
690  {
691  errmsg_printf(error::INFO, "Retrying repair of: '%s' failed. "
692  "Please try REPAIR EXTENDED or myisamchk",
693  getTable()->getShare()->getPath());
694  return(HA_ADMIN_FAILED);
695  }
696 
697  param.db_name= getTable()->getShare()->getSchemaName();
698  param.table_name= getTable()->getAlias();
699  param.tmpfile_createflag = O_RDWR | O_TRUNC;
700  param.using_global_keycache = 1;
701  param.session= session;
702  param.out_flag= 0;
703  param.sort_buffer_length= static_cast<size_t>(sort_buffer_size);
704  strcpy(fixed_name,file->filename);
705 
706  // Don't lock tables if we have used LOCK Table
707  if (mi_lock_database(file, getTable()->getShare()->getType() ? F_EXTRA_LCK : F_WRLCK))
708  {
709  mi_check_print_error(&param,ER(ER_CANT_LOCK),errno);
710  return(HA_ADMIN_FAILED);
711  }
712 
713  if (!do_optimize ||
714  ((file->state->del || share->state.split != file->state->records) &&
715  (!(param.testflag & T_QUICK) ||
716  !(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))))
717  {
718  uint64_t key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ?
719  mi_get_mask_all_keys_active(share->base.keys) :
720  share->state.key_map);
721  uint32_t testflag=param.testflag;
722  if (mi_test_if_sort_rep(file,file->state->records,key_map,0) &&
723  (local_testflag & T_REP_BY_SORT))
724  {
725  local_testflag|= T_STATISTICS;
726  param.testflag|= T_STATISTICS; // We get this for free
727  statistics_done=1;
728  {
729  session->set_proc_info("Repair by sorting");
730  error = mi_repair_by_sort(&param, file, fixed_name,
731  param.testflag & T_QUICK);
732  }
733  }
734  else
735  {
736  session->set_proc_info("Repair with keycache");
737  param.testflag &= ~T_REP_BY_SORT;
738  error= mi_repair(&param, file, fixed_name,
739  param.testflag & T_QUICK);
740  }
741  param.testflag=testflag;
742  optimize_done=1;
743  }
744  if (!error)
745  {
746  if ((local_testflag & T_SORT_INDEX) &&
747  (share->state.changed & STATE_NOT_SORTED_PAGES))
748  {
749  optimize_done=1;
750  session->set_proc_info("Sorting index");
751  error=mi_sort_index(&param,file,fixed_name);
752  }
753  if (!statistics_done && (local_testflag & T_STATISTICS))
754  {
755  if (share->state.changed & STATE_NOT_ANALYZED)
756  {
757  optimize_done=1;
758  session->set_proc_info("Analyzing");
759  error = chk_key(&param, file);
760  }
761  else
762  local_testflag&= ~T_STATISTICS; // Don't update statistics
763  }
764  }
765  session->set_proc_info("Saving state");
766  if (!error)
767  {
768  if ((share->state.changed & STATE_CHANGED) || mi_is_crashed(file))
769  {
770  share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
771  STATE_CRASHED_ON_REPAIR);
772  file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
773  }
774  /*
775  the following 'if', thought conceptually wrong,
776  is a useful optimization nevertheless.
777  */
778  if (file->state != &file->s->state.state)
779  file->s->state.state = *file->state;
780  if (file->s->base.auto_key)
781  update_auto_increment_key(&param, file, 1);
782  if (optimize_done)
783  error = update_state_info(&param, file,
784  UPDATE_TIME | UPDATE_OPEN_COUNT |
785  (local_testflag &
786  T_STATISTICS ? UPDATE_STAT : 0));
787  info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
788  HA_STATUS_CONST);
789  if (rows != file->state->records && ! (param.testflag & T_VERY_SILENT))
790  {
791  char llbuff[22],llbuff2[22];
792  mi_check_print_warning(&param,"Number of rows changed from %s to %s",
793  internal::llstr(rows,llbuff),
794  internal::llstr(file->state->records,llbuff2));
795  }
796  }
797  else
798  {
799  mi_mark_crashed_on_repair(file);
800  file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
801  update_state_info(&param, file, 0);
802  }
803  session->set_proc_info(old_proc_info);
804  mi_lock_database(file,F_UNLCK);
805 
806  return(error ? HA_ADMIN_FAILED :
807  !optimize_done ? HA_ADMIN_ALREADY_DONE : HA_ADMIN_OK);
808 }
809 
810 
811 /*
812  Disable indexes, making it persistent if requested.
813 
814  SYNOPSIS
815  disable_indexes()
816  mode mode of operation:
817  HA_KEY_SWITCH_NONUNIQ disable all non-unique keys
818  HA_KEY_SWITCH_ALL disable all keys
819  HA_KEY_SWITCH_NONUNIQ_SAVE dis. non-uni. and make persistent
820  HA_KEY_SWITCH_ALL_SAVE dis. all keys and make persistent
821 
822  IMPLEMENTATION
823  HA_KEY_SWITCH_NONUNIQ is not implemented.
824  HA_KEY_SWITCH_ALL_SAVE is not implemented.
825 
826  RETURN
827  0 ok
828  HA_ERR_WRONG_COMMAND mode not implemented.
829 */
830 
831 int ha_myisam::disable_indexes(uint32_t mode)
832 {
833  int error;
834 
835  if (mode == HA_KEY_SWITCH_ALL)
836  {
837  /* call a storage engine function to switch the key map */
838  error= mi_disable_indexes(file);
839  }
840  else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
841  {
842  mi_extra(file, HA_EXTRA_NO_KEYS, 0);
843  info(HA_STATUS_CONST); // Read new key info
844  error= 0;
845  }
846  else
847  {
848  /* mode not implemented */
849  error= HA_ERR_WRONG_COMMAND;
850  }
851  return error;
852 }
853 
854 
855 /*
856  Enable indexes, making it persistent if requested.
857 
858  SYNOPSIS
859  enable_indexes()
860  mode mode of operation:
861  HA_KEY_SWITCH_NONUNIQ enable all non-unique keys
862  HA_KEY_SWITCH_ALL enable all keys
863  HA_KEY_SWITCH_NONUNIQ_SAVE en. non-uni. and make persistent
864  HA_KEY_SWITCH_ALL_SAVE en. all keys and make persistent
865 
866  DESCRIPTION
867  Enable indexes, which might have been disabled by disable_index() before.
868  The modes without _SAVE work only if both data and indexes are empty,
869  since the MyISAM repair would enable them persistently.
870  To be sure in these cases, call Cursor::delete_all_rows() before.
871 
872  IMPLEMENTATION
873  HA_KEY_SWITCH_NONUNIQ is not implemented.
874  HA_KEY_SWITCH_ALL_SAVE is not implemented.
875 
876  RETURN
877  0 ok
878  !=0 Error, among others:
879  HA_ERR_CRASHED data or index is non-empty. Delete all rows and retry.
880  HA_ERR_WRONG_COMMAND mode not implemented.
881 */
882 
883 int ha_myisam::enable_indexes(uint32_t mode)
884 {
885  int error;
886 
887  if (mi_is_all_keys_active(file->s->state.key_map, file->s->base.keys))
888  {
889  /* All indexes are enabled already. */
890  return 0;
891  }
892 
893  if (mode == HA_KEY_SWITCH_ALL)
894  {
895  error= mi_enable_indexes(file);
896  /*
897  Do not try to repair on error,
898  as this could make the enabled state persistent,
899  but mode==HA_KEY_SWITCH_ALL forbids it.
900  */
901  }
902  else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
903  {
904  Session *session= getTable()->in_use;
905  boost::scoped_ptr<MI_CHECK> param_ap(new MI_CHECK);
906  MI_CHECK &param= *param_ap.get();
907  const char *save_proc_info= session->get_proc_info();
908  session->set_proc_info("Creating index");
909  myisamchk_init(&param);
910  param.op_name= "recreating_index";
911  param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
912  T_CREATE_MISSING_KEYS);
913  param.myf_rw&= ~MY_WAIT_IF_FULL;
914  param.sort_buffer_length= static_cast<size_t>(sort_buffer_size);
915  param.stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
916  if ((error= (repair(session,param,0) != HA_ADMIN_OK)) && param.retry_repair)
917  {
918  errmsg_printf(error::WARN, "Warning: Enabling keys got errno %d on %s.%s, retrying",
919  errno, param.db_name, param.table_name);
920  /* Repairing by sort failed. Now try standard repair method. */
921  param.testflag&= ~(T_REP_BY_SORT | T_QUICK);
922  error= (repair(session,param,0) != HA_ADMIN_OK);
923  /*
924  If the standard repair succeeded, clear all error messages which
925  might have been set by the first repair. They can still be seen
926  with SHOW WARNINGS then.
927  */
928  if (not error)
929  session->clear_error();
930  }
931  info(HA_STATUS_CONST);
932  session->set_proc_info(save_proc_info);
933  }
934  else
935  {
936  /* mode not implemented */
937  error= HA_ERR_WRONG_COMMAND;
938  }
939  return error;
940 }
941 
942 
943 /*
944  Test if indexes are disabled.
945 
946 
947  SYNOPSIS
948  indexes_are_disabled()
949  no parameters
950 
951 
952  RETURN
953  0 indexes are not disabled
954  1 all indexes are disabled
955  [2 non-unique indexes are disabled - NOT YET IMPLEMENTED]
956 */
957 
958 int ha_myisam::indexes_are_disabled(void)
959 {
960 
961  return mi_indexes_are_disabled(file);
962 }
963 
964 
965 /*
966  prepare for a many-rows insert operation
967  e.g. - disable indexes (if they can be recreated fast) or
968  activate special bulk-insert optimizations
969 
970  SYNOPSIS
971  start_bulk_insert(rows)
972  rows Rows to be inserted
973  0 if we don't know
974 
975  NOTICE
976  Do not forget to call end_bulk_insert() later!
977 */
978 
979 void ha_myisam::start_bulk_insert(ha_rows rows)
980 {
981  Session *session= getTable()->in_use;
982  ulong size= session->variables.read_buff_size;
983 
984  /* don't enable row cache if too few rows */
985  if (! rows || (rows > MI_MIN_ROWS_TO_USE_WRITE_CACHE))
986  mi_extra(file, HA_EXTRA_WRITE_CACHE, (void*) &size);
987 
988  can_enable_indexes= mi_is_all_keys_active(file->s->state.key_map,
989  file->s->base.keys);
990 
991  /*
992  Only disable old index if the table was empty and we are inserting
993  a lot of rows.
994  We should not do this for only a few rows as this is slower and
995  we don't want to update the key statistics based of only a few rows.
996  */
997  if (file->state->records == 0 && can_enable_indexes &&
998  (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES))
999  mi_disable_non_unique_index(file,rows);
1000  else
1001  if (!file->bulk_insert &&
1002  (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT))
1003  {
1004  mi_init_bulk_insert(file,
1005  (size_t)session->variables.bulk_insert_buff_size,
1006  rows);
1007  }
1008 }
1009 
1010 /*
1011  end special bulk-insert optimizations,
1012  which have been activated by start_bulk_insert().
1013 
1014  SYNOPSIS
1015  end_bulk_insert()
1016  no arguments
1017 
1018  RETURN
1019  0 OK
1020  != 0 Error
1021 */
1022 
1023 int ha_myisam::end_bulk_insert()
1024 {
1025  mi_end_bulk_insert(file);
1026  int err=mi_extra(file, HA_EXTRA_NO_CACHE, 0);
1027  return err ? err : can_enable_indexes ?
1028  enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE) : 0;
1029 }
1030 
1031 
1032 
1033 int ha_myisam::doUpdateRecord(const unsigned char *old_data, unsigned char *new_data)
1034 {
1035  return mi_update(file,old_data,new_data);
1036 }
1037 
1038 int ha_myisam::doDeleteRecord(const unsigned char *buf)
1039 {
1040  return mi_delete(file,buf);
1041 }
1042 
1043 
1044 int ha_myisam::doStartIndexScan(uint32_t idx, bool )
1045 {
1046  active_index=idx;
1047  //in_range_read= false;
1048  return 0;
1049 }
1050 
1051 
1052 int ha_myisam::doEndIndexScan()
1053 {
1054  active_index=MAX_KEY;
1055  return 0;
1056 }
1057 
1058 
1059 int ha_myisam::index_read_map(unsigned char *buf, const unsigned char *key,
1060  key_part_map keypart_map,
1061  enum ha_rkey_function find_flag)
1062 {
1063  assert(inited==INDEX);
1064  ha_statistic_increment(&system_status_var::ha_read_key_count);
1065  int error=mi_rkey(file, buf, active_index, key, keypart_map, find_flag);
1066  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1067  return error;
1068 }
1069 
1070 int ha_myisam::index_read_idx_map(unsigned char *buf, uint32_t index, const unsigned char *key,
1071  key_part_map keypart_map,
1072  enum ha_rkey_function find_flag)
1073 {
1074  ha_statistic_increment(&system_status_var::ha_read_key_count);
1075  int error=mi_rkey(file, buf, index, key, keypart_map, find_flag);
1076  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1077  return error;
1078 }
1079 
1080 int ha_myisam::index_read_last_map(unsigned char *buf, const unsigned char *key,
1081  key_part_map keypart_map)
1082 {
1083  assert(inited==INDEX);
1084  ha_statistic_increment(&system_status_var::ha_read_key_count);
1085  int error=mi_rkey(file, buf, active_index, key, keypart_map,
1086  HA_READ_PREFIX_LAST);
1087  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1088  return(error);
1089 }
1090 
1091 int ha_myisam::index_next(unsigned char *buf)
1092 {
1093  assert(inited==INDEX);
1094  ha_statistic_increment(&system_status_var::ha_read_next_count);
1095  int error=mi_rnext(file,buf,active_index);
1096  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1097  return error;
1098 }
1099 
1100 int ha_myisam::index_prev(unsigned char *buf)
1101 {
1102  assert(inited==INDEX);
1103  ha_statistic_increment(&system_status_var::ha_read_prev_count);
1104  int error=mi_rprev(file,buf, active_index);
1105  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1106  return error;
1107 }
1108 
1109 int ha_myisam::index_first(unsigned char *buf)
1110 {
1111  assert(inited==INDEX);
1112  ha_statistic_increment(&system_status_var::ha_read_first_count);
1113  int error=mi_rfirst(file, buf, active_index);
1114  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1115  return error;
1116 }
1117 
1118 int ha_myisam::index_last(unsigned char *buf)
1119 {
1120  assert(inited==INDEX);
1121  ha_statistic_increment(&system_status_var::ha_read_last_count);
1122  int error=mi_rlast(file, buf, active_index);
1123  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1124  return error;
1125 }
1126 
1127 int ha_myisam::index_next_same(unsigned char *buf,
1128  const unsigned char *,
1129  uint32_t )
1130 {
1131  int error;
1132  assert(inited==INDEX);
1133  ha_statistic_increment(&system_status_var::ha_read_next_count);
1134  do
1135  {
1136  error= mi_rnext_same(file,buf);
1137  } while (error == HA_ERR_RECORD_DELETED);
1138  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1139  return error;
1140 }
1141 
1143  const key_range *end_key,
1144  bool eq_range_arg,
1145  bool sorted /* ignored */)
1146 {
1147  int res;
1148  //if (!eq_range_arg)
1149  // in_range_read= true;
1150 
1151  res= Cursor::read_range_first(start_key, end_key, eq_range_arg, sorted);
1152 
1153  //if (res)
1154  // in_range_read= false;
1155  return res;
1156 }
1157 
1158 
1160 {
1161  int res= Cursor::read_range_next();
1162  //if (res)
1163  // in_range_read= false;
1164  return res;
1165 }
1166 
1167 
1169 {
1170  if (scan)
1171  return mi_scan_init(file);
1172  return mi_reset(file); // Free buffers
1173 }
1174 
1175 int ha_myisam::rnd_next(unsigned char *buf)
1176 {
1177  ha_statistic_increment(&system_status_var::ha_read_rnd_next_count);
1178  int error=mi_scan(file, buf);
1179  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1180  return error;
1181 }
1182 
1183 int ha_myisam::rnd_pos(unsigned char *buf, unsigned char *pos)
1184 {
1185  ha_statistic_increment(&system_status_var::ha_read_rnd_count);
1186  int error=mi_rrnd(file, buf, internal::my_get_ptr(pos,ref_length));
1187  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1188  return error;
1189 }
1190 
1191 
1192 void ha_myisam::position(const unsigned char *)
1193 {
1194  internal::my_off_t row_position= mi_position(file);
1195  internal::my_store_ptr(ref, ref_length, row_position);
1196 }
1197 
1198 int ha_myisam::info(uint32_t flag)
1199 {
1200  MI_ISAMINFO misam_info;
1201  char name_buff[FN_REFLEN];
1202 
1203  (void) mi_status(file,&misam_info,flag);
1204  if (flag & HA_STATUS_VARIABLE)
1205  {
1206  stats.records= misam_info.records;
1207  stats.deleted= misam_info.deleted;
1208  stats.data_file_length= misam_info.data_file_length;
1209  stats.index_file_length= misam_info.index_file_length;
1210  stats.delete_length= misam_info.delete_length;
1211  stats.check_time= misam_info.check_time;
1212  stats.mean_rec_length= misam_info.mean_reclength;
1213  }
1214  if (flag & HA_STATUS_CONST)
1215  {
1216  TableShare *share= getTable()->getMutableShare();
1217  stats.max_data_file_length= misam_info.max_data_file_length;
1218  stats.max_index_file_length= misam_info.max_index_file_length;
1219  stats.create_time= misam_info.create_time;
1220  ref_length= misam_info.reflength;
1221  share->db_options_in_use= misam_info.options;
1222  stats.block_size= myisam_key_cache_block_size; /* record block size */
1223 
1224  set_prefix(share->keys_in_use, share->sizeKeys());
1225  /*
1226  * Due to bug 394932 (32-bit solaris build failure), we need
1227  * to convert the uint64_t key_map member of the misam_info
1228  * structure in to a std::bitset so that we can logically and
1229  * it with the share->key_in_use key_map.
1230  */
1231  ostringstream ostr;
1232  string binary_key_map;
1233  uint64_t num= misam_info.key_map;
1234  /*
1235  * Convert the uint64_t to a binary
1236  * string representation of it.
1237  */
1238  while (num > 0)
1239  {
1240  uint64_t bin_digit= num % 2;
1241  ostr << bin_digit;
1242  num/= 2;
1243  }
1244  binary_key_map.append(ostr.str());
1245  /*
1246  * Now we have the binary string representation of the
1247  * flags, we need to fill that string representation out
1248  * with the appropriate number of bits. This is needed
1249  * since key_map is declared as a std::bitset of a certain bit
1250  * width that depends on the MAX_INDEXES variable.
1251  */
1252  if (MAX_INDEXES <= 64)
1253  {
1254  size_t len= 72 - binary_key_map.length();
1255  string all_zeros(len, '0');
1256  binary_key_map.insert(binary_key_map.begin(),
1257  all_zeros.begin(),
1258  all_zeros.end());
1259  }
1260  else
1261  {
1262  size_t len= (MAX_INDEXES + 7) / 8 * 8;
1263  string all_zeros(len, '0');
1264  binary_key_map.insert(binary_key_map.begin(),
1265  all_zeros.begin(),
1266  all_zeros.end());
1267  }
1268  key_map tmp_map(binary_key_map);
1269  share->keys_in_use&= tmp_map;
1270  share->keys_for_keyread&= share->keys_in_use;
1271  share->db_record_offset= misam_info.record_offset;
1272  if (share->key_parts)
1273  memcpy(getTable()->key_info[0].rec_per_key,
1274  misam_info.rec_per_key,
1275  sizeof(getTable()->key_info[0].rec_per_key)*share->key_parts);
1276  assert(share->getType() != message::Table::STANDARD);
1277 
1278  /*
1279  Set data_file_name and index_file_name to point at the symlink value
1280  if table is symlinked (Ie; Real name is not same as generated name)
1281  */
1282  data_file_name= index_file_name= 0;
1283  internal::fn_format(name_buff, file->filename, "", MI_NAME_DEXT,
1284  MY_APPEND_EXT | MY_UNPACK_FILENAME);
1285  if (strcmp(name_buff, misam_info.data_file_name))
1286  data_file_name=misam_info.data_file_name;
1287  internal::fn_format(name_buff, file->filename, "", MI_NAME_IEXT,
1288  MY_APPEND_EXT | MY_UNPACK_FILENAME);
1289  if (strcmp(name_buff, misam_info.index_file_name))
1290  index_file_name=misam_info.index_file_name;
1291  }
1292  if (flag & HA_STATUS_ERRKEY)
1293  {
1294  errkey = misam_info.errkey;
1295  internal::my_store_ptr(dup_ref, ref_length, misam_info.dupp_key_pos);
1296  }
1297  if (flag & HA_STATUS_TIME)
1298  stats.update_time = misam_info.update_time;
1299  if (flag & HA_STATUS_AUTO)
1300  stats.auto_increment_value= misam_info.auto_increment;
1301 
1302  return 0;
1303 }
1304 
1305 
1306 int ha_myisam::extra(enum ha_extra_function operation)
1307 {
1308  return mi_extra(file, operation, 0);
1309 }
1310 
1312 {
1313  return mi_reset(file);
1314 }
1315 
1316 /* To be used with WRITE_CACHE and EXTRA_CACHE */
1317 
1318 int ha_myisam::extra_opt(enum ha_extra_function operation, uint32_t cache_size)
1319 {
1320  return mi_extra(file, operation, (void*) &cache_size);
1321 }
1322 
1324 {
1325  return mi_delete_all_rows(file);
1326 }
1327 
1328 int MyisamEngine::doDropTable(Session &session,
1329  const identifier::Table &identifier)
1330 {
1331  session.getMessageCache().removeTableMessage(identifier);
1332 
1333  return mi_delete_table(identifier.getPath().c_str());
1334 }
1335 
1336 
1337 int ha_myisam::external_lock(Session *session, int lock_type)
1338 {
1339  file->in_use= session;
1340  return mi_lock_database(file, !getTable()->getShare()->getType() ?
1341  lock_type : ((lock_type == F_UNLCK) ?
1342  F_UNLCK : F_EXTRA_LCK));
1343 }
1344 
1345 int MyisamEngine::doCreateTable(Session &session,
1346  Table& table_arg,
1347  const identifier::Table &identifier,
1348  const message::Table& create_proto)
1349 {
1350  int error;
1351  uint32_t create_flags= 0, create_records;
1352  char buff[FN_REFLEN];
1353  MI_KEYDEF *keydef;
1354  MI_COLUMNDEF *recinfo;
1355  MI_CREATE_INFO create_info;
1356  TableShare *share= table_arg.getMutableShare();
1357  uint32_t options= share->db_options_in_use;
1358  if ((error= table2myisam(&table_arg, &keydef, &recinfo, &create_records)))
1359  return(error);
1360  memset(&create_info, 0, sizeof(create_info));
1361  create_info.max_rows= create_proto.options().max_rows();
1362  create_info.reloc_rows= create_proto.options().min_rows();
1363  create_info.with_auto_increment= share->next_number_key_offset == 0;
1364  create_info.auto_increment= (create_proto.options().has_auto_increment_value() ?
1365  create_proto.options().auto_increment_value() -1 :
1366  (uint64_t) 0);
1367  create_info.data_file_length= (create_proto.options().max_rows() *
1368  create_proto.options().avg_row_length());
1369  create_info.data_file_name= NULL;
1370  create_info.index_file_name= NULL;
1371  create_info.language= share->table_charset->number;
1372 
1373  if (create_proto.type() == message::Table::TEMPORARY)
1374  create_flags|= HA_CREATE_TMP_TABLE;
1375  if (options & HA_OPTION_PACK_RECORD)
1376  create_flags|= HA_PACK_RECORD;
1377 
1378  /* TODO: Check that the following internal::fn_format is really needed */
1379  error= mi_create(internal::fn_format(buff, identifier.getPath().c_str(), "", "",
1380  MY_UNPACK_FILENAME|MY_APPEND_EXT),
1381  share->sizeKeys(), keydef,
1382  create_records, recinfo,
1383  0, (MI_UNIQUEDEF*) 0,
1384  &create_info, create_flags);
1385  free((unsigned char*) recinfo);
1386 
1387  session.getMessageCache().storeTableMessage(identifier, create_proto);
1388 
1389  return error;
1390 }
1391 
1392 
1393 int MyisamEngine::doRenameTable(Session &session, const identifier::Table &from, const identifier::Table &to)
1394 {
1395  session.getMessageCache().renameTableMessage(from, to);
1396 
1397  return mi_rename(from.getPath().c_str(), to.getPath().c_str());
1398 }
1399 
1400 
1401 void ha_myisam::get_auto_increment(uint64_t ,
1402  uint64_t ,
1403  uint64_t ,
1404  uint64_t *first_value,
1405  uint64_t *nb_reserved_values)
1406 {
1407  uint64_t nr;
1408  int error;
1409  unsigned char key[MI_MAX_KEY_LENGTH];
1410 
1411  if (!getTable()->getShare()->next_number_key_offset)
1412  { // Autoincrement at key-start
1413  ha_myisam::info(HA_STATUS_AUTO);
1414  *first_value= stats.auto_increment_value;
1415  /* MyISAM has only table-level lock, so reserves to +inf */
1416  *nb_reserved_values= UINT64_MAX;
1417  return;
1418  }
1419 
1420  /* it's safe to call the following if bulk_insert isn't on */
1421  mi_flush_bulk_insert(file, getTable()->getShare()->next_number_index);
1422 
1423  (void) extra(HA_EXTRA_KEYREAD);
1424  key_copy(key, getTable()->getInsertRecord(),
1425  &getTable()->key_info[getTable()->getShare()->next_number_index],
1426  getTable()->getShare()->next_number_key_offset);
1427  error= mi_rkey(file, getTable()->getUpdateRecord(), (int) getTable()->getShare()->next_number_index,
1428  key, make_prev_keypart_map(getTable()->getShare()->next_number_keypart),
1429  HA_READ_PREFIX_LAST);
1430  if (error)
1431  nr= 1;
1432  else
1433  {
1434  /* Get data from getUpdateRecord() */
1435  nr= ((uint64_t) getTable()->next_number_field->
1436  val_int_offset(getTable()->getShare()->rec_buff_length)+1);
1437  }
1438  extra(HA_EXTRA_NO_KEYREAD);
1439  *first_value= nr;
1440  /*
1441  MySQL needs to call us for next row: assume we are inserting ("a",null)
1442  here, we return 3, and next this statement will want to insert ("b",null):
1443  there is no reason why ("b",3+1) would be the good row to insert: maybe it
1444  already exists, maybe 3+1 is too large...
1445  */
1446  *nb_reserved_values= 1;
1447 }
1448 
1449 
1450 /*
1451  Find out how many rows there is in the given range
1452 
1453  SYNOPSIS
1454  records_in_range()
1455  inx Index to use
1456  min_key Start of range. Null pointer if from first key
1457  max_key End of range. Null pointer if to last key
1458 
1459  NOTES
1460  min_key.flag can have one of the following values:
1461  HA_READ_KEY_EXACT Include the key in the range
1462  HA_READ_AFTER_KEY Don't include key in range
1463 
1464  max_key.flag can have one of the following values:
1465  HA_READ_BEFORE_KEY Don't include key in range
1466  HA_READ_AFTER_KEY Include all 'end_key' values in the range
1467 
1468  RETURN
1469  HA_POS_ERROR Something is wrong with the index tree.
1470  0 There is no matching keys in the given range
1471  number > 0 There is approximately 'number' matching rows in
1472  the range.
1473 */
1474 
1475 ha_rows ha_myisam::records_in_range(uint32_t inx, key_range *min_key,
1476  key_range *max_key)
1477 {
1478  return (ha_rows) mi_records_in_range(file, (int) inx, min_key, max_key);
1479 }
1480 
1481 
1482 uint32_t ha_myisam::checksum() const
1483 {
1484  return (uint)file->state->checksum;
1485 }
1486 
1487 static int myisam_init(module::Context &context)
1488 {
1489  context.add(new MyisamEngine(engine_name));
1490  context.registerVariable(new sys_var_constrained_value<size_t>("sort-buffer-size",
1491  sort_buffer_size));
1492  context.registerVariable(new sys_var_uint64_t_ptr("max_sort_file_size",
1493  &max_sort_file_size,
1494  context.getOptions()["max-sort-file-size"].as<uint64_t>()));
1495 
1496  return 0;
1497 }
1498 
1499 
1500 static void init_options(drizzled::module::option_context &context)
1501 {
1502  context("max-sort-file-size",
1503  po::value<uint64_t>(&max_sort_file_size)->default_value(INT32_MAX),
1504  _("Don't use the fast sort index method to created index if the temporary file would get bigger than this."));
1505  context("sort-buffer-size",
1506  po::value<sort_buffer_constraint>(&sort_buffer_size)->default_value(8192*1024),
1507  _("The buffer that is allocated when sorting the index when doing a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE."));
1508 }
1509 
1510 
1511 DRIZZLE_DECLARE_PLUGIN
1512 {
1513  DRIZZLE_VERSION_ID,
1514  "myisam",
1515  "2.0",
1516  "MySQL AB",
1517  N_("MyISAM storage engine: non-transactional, legacy, deprecated"),
1518  PLUGIN_LICENSE_GPL,
1519  myisam_init,
1520  NULL,
1521  init_options
1522 }
1523 DRIZZLE_DECLARE_PLUGIN_END;