MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ha_myisam.cc
1 /*
2  Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
16 
17 
18 #define MYSQL_SERVER 1
19 #include "sql_priv.h"
20 #include "probes_mysql.h"
21 #include "key.h" // key_copy
22 #include "sql_plugin.h"
23 #include <m_ctype.h>
24 #include <my_bit.h>
25 #include <myisampack.h>
26 #include "ha_myisam.h"
27 #include <stdarg.h>
28 #include "myisamdef.h"
29 #include "rt_index.h"
30 #include "sql_table.h" // tablename_to_filename
31 #include "sql_class.h" // THD
32 
33 #include <algorithm>
34 
35 using std::min;
36 using std::max;
37 
38 ulonglong myisam_recover_options;
39 static ulong opt_myisam_block_size;
40 
41 /* Interface to mysqld, to check system tables supported by SE */
42 static bool myisam_is_supported_system_table(const char *db,
43  const char *table_name,
44  bool is_sql_layer_system_table);
45 
46 /* bits in myisam_recover_options */
47 const char *myisam_recover_names[] =
48 { "DEFAULT", "BACKUP", "FORCE", "QUICK", "OFF", NullS};
49 TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"",
50  myisam_recover_names, NULL};
51 
52 const char *myisam_stats_method_names[] = {"nulls_unequal", "nulls_equal",
53  "nulls_ignored", NullS};
54 TYPELIB myisam_stats_method_typelib= {
55  array_elements(myisam_stats_method_names) - 1, "",
56  myisam_stats_method_names, NULL};
57 
58 static MYSQL_SYSVAR_ULONG(block_size, opt_myisam_block_size,
59  PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_RQCMDARG,
60  "Block size to be used for MyISAM index pages", NULL, NULL,
61  MI_KEY_BLOCK_LENGTH, MI_MIN_KEY_BLOCK_LENGTH, MI_MAX_KEY_BLOCK_LENGTH,
62  MI_MIN_KEY_BLOCK_LENGTH);
63 
64 static MYSQL_SYSVAR_ULONG(data_pointer_size, myisam_data_pointer_size,
65  PLUGIN_VAR_RQCMDARG, "Default pointer size to be used for MyISAM tables",
66  NULL, NULL, 6, 2, 7, 1);
67 
68 #define MB (1024*1024)
69 static MYSQL_SYSVAR_ULONGLONG(max_sort_file_size, myisam_max_temp_length,
70  PLUGIN_VAR_RQCMDARG, "Don't use the fast sort index method to created "
71  "index if the temporary file would get bigger than this", NULL, NULL,
72  LONG_MAX/MB*MB, 0, MAX_FILE_SIZE, MB);
73 
74 static MYSQL_SYSVAR_SET(recover_options, myisam_recover_options,
75  PLUGIN_VAR_OPCMDARG|PLUGIN_VAR_READONLY,
76  "Syntax: myisam-recover-options[=option[,option...]], where option can be "
77  "DEFAULT, BACKUP, FORCE, QUICK, or OFF",
78  NULL, NULL, 0, &myisam_recover_typelib);
79 
80 static MYSQL_THDVAR_ULONG(repair_threads, PLUGIN_VAR_RQCMDARG,
81  "If larger than 1, when repairing a MyISAM table all indexes will be "
82  "created in parallel, with one thread per index. The value of 1 "
83  "disables parallel repair", NULL, NULL,
84  1, 1, ULONG_MAX, 1);
85 
86 static MYSQL_THDVAR_ULONGLONG(sort_buffer_size, PLUGIN_VAR_RQCMDARG,
87  "The buffer that is allocated when sorting the index when doing "
88  "a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE", NULL, NULL,
89  8192 * 1024, (long) (MIN_SORT_BUFFER + MALLOC_OVERHEAD), SIZE_T_MAX, 1);
90 
91 static MYSQL_SYSVAR_BOOL(use_mmap, opt_myisam_use_mmap, PLUGIN_VAR_NOCMDARG,
92  "Use memory mapping for reading and writing MyISAM tables", NULL, NULL, FALSE);
93 
94 static MYSQL_SYSVAR_ULONGLONG(mmap_size, myisam_mmap_size,
95  PLUGIN_VAR_RQCMDARG|PLUGIN_VAR_READONLY, "Restricts the total memory "
96  "used for memory mapping of MySQL tables", NULL, NULL,
97  SIZE_T_MAX, MEMMAP_EXTRA_MARGIN, SIZE_T_MAX, 1);
98 
99 static MYSQL_THDVAR_ENUM(stats_method, PLUGIN_VAR_RQCMDARG,
100  "Specifies how MyISAM index statistics collection code should "
101  "treat NULLs. Possible values of name are NULLS_UNEQUAL (default "
102  "behavior for 4.1 and later), NULLS_EQUAL (emulate 4.0 behavior), "
103  "and NULLS_IGNORED", NULL, NULL,
104  MI_STATS_METHOD_NULLS_NOT_EQUAL, &myisam_stats_method_typelib);
105 
106 #ifndef DBUG_OFF
107 
114 static void debug_wait_for_kill(const char *info)
115 {
116  DBUG_ENTER("debug_wait_for_kill");
117  const char *prev_info;
118  THD *thd;
119  thd= current_thd;
120  prev_info= thd_proc_info(thd, info);
121  while(!thd->killed)
122  my_sleep(1000);
123  DBUG_PRINT("info", ("Exit debug_wait_for_kill"));
124  thd_proc_info(thd, prev_info);
125  DBUG_VOID_RETURN;
126 }
127 #endif
128 
129 /*****************************************************************************
130 ** MyISAM tables
131 *****************************************************************************/
132 
133 static handler *myisam_create_handler(handlerton *hton,
134  TABLE_SHARE *table,
135  MEM_ROOT *mem_root)
136 {
137  return new (mem_root) ha_myisam(hton, table);
138 }
139 
140 // collect errors printed by mi_check routines
141 
142 static void mi_check_print_msg(MI_CHECK *param, const char* msg_type,
143  const char *fmt, va_list args)
144 {
145  THD* thd = (THD*)param->thd;
146  Protocol *protocol= thd->protocol;
147  size_t length, msg_length;
148  char msgbuf[MI_MAX_MSG_BUF];
149  char name[NAME_LEN*2+2];
150 
151  msg_length= my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
152  msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia
153 
154  DBUG_PRINT(msg_type,("message: %s",msgbuf));
155 
156  if (!thd->vio_ok())
157  {
158  sql_print_error("%s", msgbuf);
159  return;
160  }
161 
162  if (param->testflag & (T_CREATE_MISSING_KEYS | T_SAFE_REPAIR |
163  T_AUTO_REPAIR))
164  {
165  my_message(ER_NOT_KEYFILE,msgbuf,MYF(MY_WME));
166  return;
167  }
168  length=(uint) (strxmov(name, param->db_name,".",param->table_name,NullS) -
169  name);
170  /*
171  TODO: switch from protocol to push_warning here. The main reason we didn't
172  it yet is parallel repair. Due to following trace:
173  mi_check_print_msg/push_warning/sql_alloc/my_pthread_getspecific_ptr.
174 
175  Also we likely need to lock mutex here (in both cases with protocol and
176  push_warning).
177  */
178  if (param->need_print_msg_lock)
179  mysql_mutex_lock(&param->print_msg_mutex);
180 
181  protocol->prepare_for_resend();
182  protocol->store(name, length, system_charset_info);
183  protocol->store(param->op_name, system_charset_info);
184  protocol->store(msg_type, system_charset_info);
185  protocol->store(msgbuf, msg_length, system_charset_info);
186  if (protocol->write())
187  sql_print_error("Failed on my_net_write, writing to stderr instead: %s\n",
188  msgbuf);
189 
190  if (param->need_print_msg_lock)
191  mysql_mutex_unlock(&param->print_msg_mutex);
192 
193  return;
194 }
195 
196 
197 /*
198  Convert TABLE object to MyISAM key and column definition
199 
200  SYNOPSIS
201  table2myisam()
202  table_arg in TABLE object.
203  keydef_out out MyISAM key definition.
204  recinfo_out out MyISAM column definition.
205  records_out out Number of fields.
206 
207  DESCRIPTION
208  This function will allocate and initialize MyISAM key and column
209  definition for further use in mi_create or for a check for underlying
210  table conformance in merge engine.
211 
212  The caller needs to free *recinfo_out after use. Since *recinfo_out
213  and *keydef_out are allocated with a my_multi_malloc, *keydef_out
214  is freed automatically when *recinfo_out is freed.
215 
216  RETURN VALUE
217  0 OK
218  !0 error code
219 */
220 
221 int table2myisam(TABLE *table_arg, MI_KEYDEF **keydef_out,
222  MI_COLUMNDEF **recinfo_out, uint *records_out)
223 {
224  uint i, j, recpos, minpos, fieldpos, temp_length, length;
225  enum ha_base_keytype type= HA_KEYTYPE_BINARY;
226  uchar *record;
227  KEY *pos;
228  MI_KEYDEF *keydef;
229  MI_COLUMNDEF *recinfo, *recinfo_pos;
230  HA_KEYSEG *keyseg;
231  TABLE_SHARE *share= table_arg->s;
232  uint options= share->db_options_in_use;
233  DBUG_ENTER("table2myisam");
234  if (!(my_multi_malloc(MYF(MY_WME),
235  recinfo_out, (share->fields * 2 + 2) * sizeof(MI_COLUMNDEF),
236  keydef_out, share->keys * sizeof(MI_KEYDEF),
237  &keyseg,
238  (share->key_parts + share->keys) * sizeof(HA_KEYSEG),
239  NullS)))
240  DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
241  keydef= *keydef_out;
242  recinfo= *recinfo_out;
243  pos= table_arg->key_info;
244  for (i= 0; i < share->keys; i++, pos++)
245  {
246  keydef[i].flag= ((uint16) pos->flags & (HA_NOSAME | HA_FULLTEXT | HA_SPATIAL));
247  keydef[i].key_alg= pos->algorithm == HA_KEY_ALG_UNDEF ?
248  (pos->flags & HA_SPATIAL ? HA_KEY_ALG_RTREE : HA_KEY_ALG_BTREE) :
249  pos->algorithm;
250  keydef[i].block_length= pos->block_size;
251  keydef[i].seg= keyseg;
252  keydef[i].keysegs= pos->user_defined_key_parts;
253  for (j= 0; j < pos->user_defined_key_parts; j++)
254  {
255  Field *field= pos->key_part[j].field;
256  type= field->key_type();
257  keydef[i].seg[j].flag= pos->key_part[j].key_part_flag;
258 
259  if (options & HA_OPTION_PACK_KEYS ||
260  (pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY |
261  HA_SPACE_PACK_USED)))
262  {
263  if (pos->key_part[j].length > 8 &&
264  (type == HA_KEYTYPE_TEXT ||
265  type == HA_KEYTYPE_NUM ||
266  (type == HA_KEYTYPE_BINARY && !field->zero_pack())))
267  {
268  /* No blobs here */
269  if (j == 0)
270  keydef[i].flag|= HA_PACK_KEY;
271  if (!(field->flags & ZEROFILL_FLAG) &&
272  (field->type() == MYSQL_TYPE_STRING ||
273  field->type() == MYSQL_TYPE_VAR_STRING ||
274  ((int) (pos->key_part[j].length - field->decimals())) >= 4))
275  keydef[i].seg[j].flag|= HA_SPACE_PACK;
276  }
277  else if (j == 0 && (!(pos->flags & HA_NOSAME) || pos->key_length > 16))
278  keydef[i].flag|= HA_BINARY_PACK_KEY;
279  }
280  keydef[i].seg[j].type= (int) type;
281  keydef[i].seg[j].start= pos->key_part[j].offset;
282  keydef[i].seg[j].length= pos->key_part[j].length;
283  keydef[i].seg[j].bit_start= keydef[i].seg[j].bit_end=
284  keydef[i].seg[j].bit_length= 0;
285  keydef[i].seg[j].bit_pos= 0;
286  keydef[i].seg[j].language= field->charset_for_protocol()->number;
287 
288  if (field->real_maybe_null())
289  {
290  keydef[i].seg[j].null_bit= field->null_bit;
291  keydef[i].seg[j].null_pos= field->null_offset();
292  }
293  else
294  {
295  keydef[i].seg[j].null_bit= 0;
296  keydef[i].seg[j].null_pos= 0;
297  }
298  if (field->type() == MYSQL_TYPE_BLOB ||
299  field->type() == MYSQL_TYPE_GEOMETRY)
300  {
301  keydef[i].seg[j].flag|= HA_BLOB_PART;
302  /* save number of bytes used to pack length */
303  keydef[i].seg[j].bit_start= (uint) (field->pack_length() -
304  portable_sizeof_char_ptr);
305  }
306  else if (field->type() == MYSQL_TYPE_BIT)
307  {
308  keydef[i].seg[j].bit_length= ((Field_bit *) field)->bit_len;
309  keydef[i].seg[j].bit_start= ((Field_bit *) field)->bit_ofs;
310  keydef[i].seg[j].bit_pos= (uint) (((Field_bit *) field)->bit_ptr -
311  (uchar*) table_arg->record[0]);
312  }
313  }
314  keyseg+= pos->user_defined_key_parts;
315  }
316  if (table_arg->found_next_number_field)
317  keydef[share->next_number_index].flag|= HA_AUTO_KEY;
318  record= table_arg->record[0];
319  recpos= 0;
320  recinfo_pos= recinfo;
321  while (recpos < (uint) share->reclength)
322  {
323  Field **field, *found= 0;
324  minpos= share->reclength;
325  length= 0;
326 
327  for (field= table_arg->field; *field; field++)
328  {
329  if ((fieldpos= (*field)->offset(record)) >= recpos &&
330  fieldpos <= minpos)
331  {
332  /* skip null fields */
333  if (!(temp_length= (*field)->pack_length_in_rec()))
334  continue; /* Skip null-fields */
335  if (! found || fieldpos < minpos ||
336  (fieldpos == minpos && temp_length < length))
337  {
338  minpos= fieldpos;
339  found= *field;
340  length= temp_length;
341  }
342  }
343  }
344  DBUG_PRINT("loop", ("found: 0x%lx recpos: %d minpos: %d length: %d",
345  (long) found, recpos, minpos, length));
346  if (recpos != minpos)
347  { // Reserved space (Null bits?)
348  memset(recinfo_pos, 0, sizeof(*recinfo_pos));
349  recinfo_pos->type= (int) FIELD_NORMAL;
350  recinfo_pos++->length= (uint16) (minpos - recpos);
351  }
352  if (!found)
353  break;
354 
355  if (found->flags & BLOB_FLAG)
356  recinfo_pos->type= (int) FIELD_BLOB;
357  else if (found->type() == MYSQL_TYPE_VARCHAR)
358  recinfo_pos->type= FIELD_VARCHAR;
359  else if (!(options & HA_OPTION_PACK_RECORD))
360  recinfo_pos->type= (int) FIELD_NORMAL;
361  else if (found->zero_pack())
362  recinfo_pos->type= (int) FIELD_SKIP_ZERO;
363  else
364  recinfo_pos->type= (int) ((length <= 3 ||
365  (found->flags & ZEROFILL_FLAG)) ?
366  FIELD_NORMAL :
367  found->type() == MYSQL_TYPE_STRING ||
368  found->type() == MYSQL_TYPE_VAR_STRING ?
369  FIELD_SKIP_ENDSPACE :
370  FIELD_SKIP_PRESPACE);
371  if (found->real_maybe_null())
372  {
373  recinfo_pos->null_bit= found->null_bit;
374  recinfo_pos->null_pos= found->null_offset();
375  }
376  else
377  {
378  recinfo_pos->null_bit= 0;
379  recinfo_pos->null_pos= 0;
380  }
381  (recinfo_pos++)->length= (uint16) length;
382  recpos= minpos + length;
383  DBUG_PRINT("loop", ("length: %d type: %d",
384  recinfo_pos[-1].length,recinfo_pos[-1].type));
385  }
386  *records_out= (uint) (recinfo_pos - recinfo);
387  DBUG_RETURN(0);
388 }
389 
390 
391 /*
392  Check for underlying table conformance
393 
394  SYNOPSIS
395  check_definition()
396  t1_keyinfo in First table key definition
397  t1_recinfo in First table record definition
398  t1_keys in Number of keys in first table
399  t1_recs in Number of records in first table
400  t2_keyinfo in Second table key definition
401  t2_recinfo in Second table record definition
402  t2_keys in Number of keys in second table
403  t2_recs in Number of records in second table
404  strict in Strict check switch
405  table in handle to the table object
406 
407  DESCRIPTION
408  This function compares two MyISAM definitions. By intention it was done
409  to compare merge table definition against underlying table definition.
410  It may also be used to compare dot-frm and MYI definitions of MyISAM
411  table as well to compare different MyISAM table definitions.
412 
413  For merge table it is not required that number of keys in merge table
414  must exactly match number of keys in underlying table. When calling this
415  function for underlying table conformance check, 'strict' flag must be
416  set to false, and converted merge definition must be passed as t1_*.
417 
418  Otherwise 'strict' flag must be set to 1 and it is not required to pass
419  converted dot-frm definition as t1_*.
420 
421  For compatibility reasons we relax some checks, specifically:
422  - 4.0 (and earlier versions) always set key_alg to 0.
423  - 4.0 (and earlier versions) have the same language for all keysegs.
424 
425  RETURN VALUE
426  0 - Equal definitions.
427  1 - Different definitions.
428 
429  TODO
430  - compare FULLTEXT keys;
431  - compare SPATIAL keys;
432  - compare FIELD_SKIP_ZERO which is converted to FIELD_NORMAL correctly
433  (should be corretly detected in table2myisam).
434 */
435 
436 int check_definition(MI_KEYDEF *t1_keyinfo, MI_COLUMNDEF *t1_recinfo,
437  uint t1_keys, uint t1_recs,
438  MI_KEYDEF *t2_keyinfo, MI_COLUMNDEF *t2_recinfo,
439  uint t2_keys, uint t2_recs, bool strict, TABLE *table_arg)
440 {
441  uint i, j;
442  DBUG_ENTER("check_definition");
443  my_bool mysql_40_compat= table_arg && table_arg->s->frm_version < FRM_VER_TRUE_VARCHAR;
444  if ((strict ? t1_keys != t2_keys : t1_keys > t2_keys))
445  {
446  DBUG_PRINT("error", ("Number of keys differs: t1_keys=%u, t2_keys=%u",
447  t1_keys, t2_keys));
448  DBUG_RETURN(1);
449  }
450  if (t1_recs != t2_recs)
451  {
452  DBUG_PRINT("error", ("Number of recs differs: t1_recs=%u, t2_recs=%u",
453  t1_recs, t2_recs));
454  DBUG_RETURN(1);
455  }
456  for (i= 0; i < t1_keys; i++)
457  {
458  HA_KEYSEG *t1_keysegs= t1_keyinfo[i].seg;
459  HA_KEYSEG *t2_keysegs= t2_keyinfo[i].seg;
460  if (t1_keyinfo[i].flag & HA_FULLTEXT && t2_keyinfo[i].flag & HA_FULLTEXT)
461  continue;
462  else if (t1_keyinfo[i].flag & HA_FULLTEXT ||
463  t2_keyinfo[i].flag & HA_FULLTEXT)
464  {
465  DBUG_PRINT("error", ("Key %d has different definition", i));
466  DBUG_PRINT("error", ("t1_fulltext= %d, t2_fulltext=%d",
467  test(t1_keyinfo[i].flag & HA_FULLTEXT),
468  test(t2_keyinfo[i].flag & HA_FULLTEXT)));
469  DBUG_RETURN(1);
470  }
471  if (t1_keyinfo[i].flag & HA_SPATIAL && t2_keyinfo[i].flag & HA_SPATIAL)
472  continue;
473  else if (t1_keyinfo[i].flag & HA_SPATIAL ||
474  t2_keyinfo[i].flag & HA_SPATIAL)
475  {
476  DBUG_PRINT("error", ("Key %d has different definition", i));
477  DBUG_PRINT("error", ("t1_spatial= %d, t2_spatial=%d",
478  test(t1_keyinfo[i].flag & HA_SPATIAL),
479  test(t2_keyinfo[i].flag & HA_SPATIAL)));
480  DBUG_RETURN(1);
481  }
482  if ((!mysql_40_compat &&
483  t1_keyinfo[i].key_alg != t2_keyinfo[i].key_alg) ||
484  t1_keyinfo[i].keysegs != t2_keyinfo[i].keysegs)
485  {
486  DBUG_PRINT("error", ("Key %d has different definition", i));
487  DBUG_PRINT("error", ("t1_keysegs=%d, t1_key_alg=%d",
488  t1_keyinfo[i].keysegs, t1_keyinfo[i].key_alg));
489  DBUG_PRINT("error", ("t2_keysegs=%d, t2_key_alg=%d",
490  t2_keyinfo[i].keysegs, t2_keyinfo[i].key_alg));
491  DBUG_RETURN(1);
492  }
493  for (j= t1_keyinfo[i].keysegs; j--;)
494  {
495  uint8 t1_keysegs_j__type= t1_keysegs[j].type;
496 
497  /*
498  Table migration from 4.1 to 5.1. In 5.1 a *TEXT key part is
499  always HA_KEYTYPE_VARTEXT2. In 4.1 we had only the equivalent of
500  HA_KEYTYPE_VARTEXT1. Since we treat both the same on MyISAM
501  level, we can ignore a mismatch between these types.
502  */
503  if ((t1_keysegs[j].flag & HA_BLOB_PART) &&
504  (t2_keysegs[j].flag & HA_BLOB_PART))
505  {
506  if ((t1_keysegs_j__type == HA_KEYTYPE_VARTEXT2) &&
507  (t2_keysegs[j].type == HA_KEYTYPE_VARTEXT1))
508  t1_keysegs_j__type= HA_KEYTYPE_VARTEXT1; /* purecov: tested */
509  else if ((t1_keysegs_j__type == HA_KEYTYPE_VARBINARY2) &&
510  (t2_keysegs[j].type == HA_KEYTYPE_VARBINARY1))
511  t1_keysegs_j__type= HA_KEYTYPE_VARBINARY1; /* purecov: inspected */
512  }
513 
514  if ((!mysql_40_compat &&
515  t1_keysegs[j].language != t2_keysegs[j].language) ||
516  t1_keysegs_j__type != t2_keysegs[j].type ||
517  t1_keysegs[j].null_bit != t2_keysegs[j].null_bit ||
518  t1_keysegs[j].length != t2_keysegs[j].length ||
519  t1_keysegs[j].start != t2_keysegs[j].start)
520  {
521  DBUG_PRINT("error", ("Key segment %d (key %d) has different "
522  "definition", j, i));
523  DBUG_PRINT("error", ("t1_type=%d, t1_language=%d, t1_null_bit=%d, "
524  "t1_length=%d",
525  t1_keysegs[j].type, t1_keysegs[j].language,
526  t1_keysegs[j].null_bit, t1_keysegs[j].length));
527  DBUG_PRINT("error", ("t2_type=%d, t2_language=%d, t2_null_bit=%d, "
528  "t2_length=%d",
529  t2_keysegs[j].type, t2_keysegs[j].language,
530  t2_keysegs[j].null_bit, t2_keysegs[j].length));
531 
532  DBUG_RETURN(1);
533  }
534  }
535  }
536  for (i= 0; i < t1_recs; i++)
537  {
538  MI_COLUMNDEF *t1_rec= &t1_recinfo[i];
539  MI_COLUMNDEF *t2_rec= &t2_recinfo[i];
540  /*
541  FIELD_SKIP_ZERO can be changed to FIELD_NORMAL in mi_create,
542  see NOTE1 in mi_create.c
543  */
544  if ((t1_rec->type != t2_rec->type &&
545  !(t1_rec->type == (int) FIELD_SKIP_ZERO &&
546  t1_rec->length == 1 &&
547  t2_rec->type == (int) FIELD_NORMAL)) ||
548  t1_rec->length != t2_rec->length ||
549  t1_rec->null_bit != t2_rec->null_bit)
550  {
551  DBUG_PRINT("error", ("Field %d has different definition", i));
552  DBUG_PRINT("error", ("t1_type=%d, t1_length=%d, t1_null_bit=%d",
553  t1_rec->type, t1_rec->length, t1_rec->null_bit));
554  DBUG_PRINT("error", ("t2_type=%d, t2_length=%d, t2_null_bit=%d",
555  t2_rec->type, t2_rec->length, t2_rec->null_bit));
556  DBUG_RETURN(1);
557  }
558  }
559  DBUG_RETURN(0);
560 }
561 
562 
563 extern "C" {
564 
565 volatile int *killed_ptr(MI_CHECK *param)
566 {
567  /* In theory Unsafe conversion, but should be ok for now */
568  return (int*) &(((THD *)(param->thd))->killed);
569 }
570 
571 void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
572 {
573  param->error_printed|=1;
574  param->out_flag|= O_DATA_LOST;
575  va_list args;
576  va_start(args, fmt);
577  mi_check_print_msg(param, "error", fmt, args);
578  va_end(args);
579 }
580 
581 void mi_check_print_info(MI_CHECK *param, const char *fmt,...)
582 {
583  va_list args;
584  va_start(args, fmt);
585  mi_check_print_msg(param, "info", fmt, args);
586  va_end(args);
587 }
588 
589 void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
590 {
591  param->warning_printed=1;
592  param->out_flag|= O_DATA_LOST;
593  va_list args;
594  va_start(args, fmt);
595  mi_check_print_msg(param, "warning", fmt, args);
596  va_end(args);
597 }
598 
599 
615 void _mi_report_crashed(MI_INFO *file, const char *message,
616  const char *sfile, uint sline)
617 {
618  THD *cur_thd;
619  LIST *element;
620  char buf[1024];
621  mysql_mutex_lock(&file->s->intern_lock);
622  if ((cur_thd= (THD*) file->in_use.data))
623  sql_print_error("Got an error from thread_id=%lu, %s:%d", cur_thd->thread_id,
624  sfile, sline);
625  else
626  sql_print_error("Got an error from unknown thread, %s:%d", sfile, sline);
627  if (message)
628  sql_print_error("%s", message);
629  for (element= file->s->in_use; element; element= list_rest(element))
630  {
631  THD *thd= (THD*) element->data;
632  sql_print_error("%s", thd ? thd_security_context(thd, buf, sizeof(buf), 0)
633  : "Unknown thread accessing table");
634  }
635  mysql_mutex_unlock(&file->s->intern_lock);
636 }
637 
638 }
639 
640 
641 ha_myisam::ha_myisam(handlerton *hton, TABLE_SHARE *table_arg)
642  :handler(hton, table_arg), file(0),
643  int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER |
644  HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
645  HA_DUPLICATE_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY |
646  HA_FILE_BASED | HA_CAN_GEOMETRY | HA_NO_TRANSACTIONS |
647  HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS |
648  HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT | HA_CAN_REPAIR),
649  can_enable_indexes(1)
650 {}
651 
652 handler *ha_myisam::clone(const char *name, MEM_ROOT *mem_root)
653 {
654  ha_myisam *new_handler= static_cast <ha_myisam *>(handler::clone(name,
655  mem_root));
656  if (new_handler)
657  new_handler->file->state= file->state;
658  return new_handler;
659 }
660 
661 
662 static const char *ha_myisam_exts[] = {
663  ".MYI",
664  ".MYD",
665  NullS
666 };
667 
668 const char **ha_myisam::bas_ext() const
669 {
670  return ha_myisam_exts;
671 }
672 
693 static bool myisam_is_supported_system_table(const char *db,
694  const char *table_name,
695  bool is_sql_layer_system_table)
696 {
697  // Does MYISAM support "ALL" SQL layer system tables ?
698  if (is_sql_layer_system_table)
699  return true;
700 
701  /*
702  Currently MYISAM does not support any other SE specific
703  system tables. If in future it does, please see ha_example.cc
704  for reference implementation
705  */
706 
707  return false;
708 }
709 
710 const char *ha_myisam::index_type(uint key_number)
711 {
712  return ((table->key_info[key_number].flags & HA_FULLTEXT) ?
713  "FULLTEXT" :
714  (table->key_info[key_number].flags & HA_SPATIAL) ?
715  "SPATIAL" :
716  (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ?
717  "RTREE" :
718  "BTREE");
719 }
720 
721 
722 /* Name is here without an extension */
723 int ha_myisam::open(const char *name, int mode, uint test_if_locked)
724 {
725  MI_KEYDEF *keyinfo;
726  MI_COLUMNDEF *recinfo= 0;
727  uint recs;
728  uint i;
729 
730  /*
731  If the user wants to have memory mapped data files, add an
732  open_flag. Do not memory map temporary tables because they are
733  expected to be inserted and thus extended a lot. Memory mapping is
734  efficient for files that keep their size, but very inefficient for
735  growing files. Using an open_flag instead of calling mi_extra(...
736  HA_EXTRA_MMAP ...) after mi_open() has the advantage that the
737  mapping is not repeated for every open, but just done on the initial
738  open, when the MyISAM share is created. Everytime the server
739  requires to open a new instance of a table it calls this method. We
740  will always supply HA_OPEN_MMAP for a permanent table. However, the
741  MyISAM storage engine will ignore this flag if this is a secondary
742  open of a table that is in use by other threads already (if the
743  MyISAM share exists already).
744  */
745  if (!(test_if_locked & HA_OPEN_TMP_TABLE) && opt_myisam_use_mmap)
746  test_if_locked|= HA_OPEN_MMAP;
747 
748  if (!(file=mi_open(name, mode, test_if_locked | HA_OPEN_FROM_SQL_LAYER)))
749  return (my_errno ? my_errno : -1);
750  if (!table->s->tmp_table) /* No need to perform a check for tmp table */
751  {
752  if ((my_errno= table2myisam(table, &keyinfo, &recinfo, &recs)))
753  {
754  /* purecov: begin inspected */
755  DBUG_PRINT("error", ("Failed to convert TABLE object to MyISAM "
756  "key and column definition"));
757  goto err;
758  /* purecov: end */
759  }
760  if (check_definition(keyinfo, recinfo, table->s->keys, recs,
761  file->s->keyinfo, file->s->rec,
762  file->s->base.keys, file->s->base.fields,
763  true, table))
764  {
765  /* purecov: begin inspected */
766  my_errno= HA_ERR_CRASHED;
767  goto err;
768  /* purecov: end */
769  }
770  }
771 
772  if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE))
773  (void) mi_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0);
774 
775  info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
776  if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
777  (void) mi_extra(file, HA_EXTRA_WAIT_LOCK, 0);
778  if (!table->s->db_record_offset)
779  int_table_flags|=HA_REC_NOT_IN_SEQ;
780  if (file->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
781  int_table_flags|=HA_HAS_CHECKSUM;
782 
783  for (i= 0; i < table->s->keys; i++)
784  {
785  plugin_ref parser= table->key_info[i].parser;
786  if (table->key_info[i].flags & HA_USES_PARSER)
787  file->s->keyinfo[i].parser=
788  (struct st_mysql_ftparser *)plugin_decl(parser)->info;
789  table->key_info[i].block_size= file->s->keyinfo[i].block_length;
790  }
791  my_errno= 0;
792  goto end;
793  err:
794  this->close();
795  end:
796  /*
797  Both recinfo and keydef are allocated by my_multi_malloc(), thus only
798  recinfo must be freed.
799  */
800  if (recinfo)
801  my_free(recinfo);
802  return my_errno;
803 }
804 
805 int ha_myisam::close(void)
806 {
807  MI_INFO *tmp=file;
808  file=0;
809  return mi_close(tmp);
810 }
811 
812 int ha_myisam::write_row(uchar *buf)
813 {
814  ha_statistic_increment(&SSV::ha_write_count);
815 
816  /*
817  If we have an auto_increment column and we are writing a changed row
818  or a new row, then update the auto_increment value in the record.
819  */
820  if (table->next_number_field && buf == table->record[0])
821  {
822  int error;
823  if ((error= update_auto_increment()))
824  return error;
825  }
826  return mi_write(file,buf);
827 }
828 
829 int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
830 {
831  if (!file) return HA_ADMIN_INTERNAL_ERROR;
832  int error;
833  MI_CHECK param;
834  MYISAM_SHARE* share = file->s;
835  const char *old_proc_info=thd->proc_info;
836 
837  thd_proc_info(thd, "Checking table");
838  myisamchk_init(&param);
839  param.thd = thd;
840  param.op_name = "check";
841  param.db_name= table->s->db.str;
842  param.table_name= table->alias;
843  param.testflag = check_opt->flags | T_CHECK | T_SILENT;
844  param.stats_method= (enum_mi_stats_method)THDVAR(thd, stats_method);
845 
846  if (!(table->db_stat & HA_READ_ONLY))
847  param.testflag|= T_STATISTICS;
848  param.using_global_keycache = 1;
849 
850  if (!mi_is_crashed(file) &&
851  (((param.testflag & T_CHECK_ONLY_CHANGED) &&
852  !(share->state.changed & (STATE_CHANGED | STATE_CRASHED |
853  STATE_CRASHED_ON_REPAIR)) &&
854  share->state.open_count == 0) ||
855  ((param.testflag & T_FAST) && (share->state.open_count ==
856  (uint) (share->global_changed ? 1 : 0)))))
857  return HA_ADMIN_ALREADY_DONE;
858 
859  error = chk_status(&param, file); // Not fatal
860  error = chk_size(&param, file);
861  if (!error)
862  error |= chk_del(&param, file, param.testflag);
863  if (!error)
864  error = chk_key(&param, file);
865  if (!error)
866  {
867  if ((!(param.testflag & T_QUICK) &&
868  ((share->options &
869  (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
870  (param.testflag & (T_EXTEND | T_MEDIUM)))) ||
871  mi_is_crashed(file))
872  {
873  uint old_testflag=param.testflag;
874  param.testflag|=T_MEDIUM;
875  if (!(error= init_io_cache(&param.read_cache, file->dfile,
876  my_default_record_cache_size, READ_CACHE,
877  share->pack.header_length, 1, MYF(MY_WME))))
878  {
879  error= chk_data_link(&param, file, param.testflag & T_EXTEND);
880  end_io_cache(&(param.read_cache));
881  }
882  param.testflag= old_testflag;
883  }
884  }
885  if (!error)
886  {
887  if ((share->state.changed & (STATE_CHANGED |
888  STATE_CRASHED_ON_REPAIR |
889  STATE_CRASHED | STATE_NOT_ANALYZED)) ||
890  (param.testflag & T_STATISTICS) ||
891  mi_is_crashed(file))
892  {
893  file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
894  mysql_mutex_lock(&share->intern_lock);
895  share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
896  STATE_CRASHED_ON_REPAIR);
897  if (!(table->db_stat & HA_READ_ONLY))
898  error=update_state_info(&param,file,UPDATE_TIME | UPDATE_OPEN_COUNT |
899  UPDATE_STAT);
900  mysql_mutex_unlock(&share->intern_lock);
901  info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
902  HA_STATUS_CONST);
903  }
904  }
905  else if (!mi_is_crashed(file) && !thd->killed)
906  {
907  mi_mark_crashed(file);
908  file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
909  }
910 
911  thd_proc_info(thd, old_proc_info);
912  return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
913 }
914 
915 
916 /*
917  analyze the key distribution in the table
918  As the table may be only locked for read, we have to take into account that
919  two threads may do an analyze at the same time!
920 */
921 
922 int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt)
923 {
924  int error=0;
925  MI_CHECK param;
926  MYISAM_SHARE* share = file->s;
927 
928  myisamchk_init(&param);
929  param.thd = thd;
930  param.op_name= "analyze";
931  param.db_name= table->s->db.str;
932  param.table_name= table->alias;
933  param.testflag= (T_FAST | T_CHECK | T_SILENT | T_STATISTICS |
934  T_DONT_CHECK_CHECKSUM);
935  param.using_global_keycache = 1;
936  param.stats_method= (enum_mi_stats_method)THDVAR(thd, stats_method);
937 
938  if (!(share->state.changed & STATE_NOT_ANALYZED))
939  return HA_ADMIN_ALREADY_DONE;
940 
941  error = chk_key(&param, file);
942  if (!error)
943  {
944  mysql_mutex_lock(&share->intern_lock);
945  error=update_state_info(&param,file,UPDATE_STAT);
946  mysql_mutex_unlock(&share->intern_lock);
947  }
948  else if (!mi_is_crashed(file) && !thd->killed)
949  mi_mark_crashed(file);
950  return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
951 }
952 
953 
954 int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt)
955 {
956  int error;
957  MI_CHECK param;
958  ha_rows start_records;
959 
960  if (!file) return HA_ADMIN_INTERNAL_ERROR;
961 
962  myisamchk_init(&param);
963  param.thd = thd;
964  param.op_name= "repair";
965  param.testflag= ((check_opt->flags & ~(T_EXTEND)) |
966  T_SILENT | T_FORCE_CREATE | T_CALC_CHECKSUM |
967  (check_opt->flags & T_EXTEND ? T_REP : T_REP_BY_SORT));
968  param.sort_buffer_length= THDVAR(thd, sort_buffer_size);
969  start_records=file->state->records;
970  while ((error=repair(thd,param,0)) && param.retry_repair)
971  {
972  param.retry_repair=0;
973  if (test_all_bits(param.testflag,
974  (uint) (T_RETRY_WITHOUT_QUICK | T_QUICK)))
975  {
976  param.testflag&= ~T_RETRY_WITHOUT_QUICK;
977  sql_print_information("Retrying repair of: '%s' without quick",
978  table->s->path.str);
979  continue;
980  }
981  param.testflag&= ~T_QUICK;
982  if ((param.testflag & T_REP_BY_SORT))
983  {
984  param.testflag= (param.testflag & ~T_REP_BY_SORT) | T_REP;
985  sql_print_information("Retrying repair of: '%s' with keycache",
986  table->s->path.str);
987  continue;
988  }
989  break;
990  }
991  if (!error && start_records != file->state->records &&
992  !(check_opt->flags & T_VERY_SILENT))
993  {
994  char llbuff[22],llbuff2[22];
995  sql_print_information("Found %s of %s rows when repairing '%s'",
996  llstr(file->state->records, llbuff),
997  llstr(start_records, llbuff2),
998  table->s->path.str);
999  }
1000  return error;
1001 }
1002 
1003 int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt)
1004 {
1005  int error;
1006  if (!file) return HA_ADMIN_INTERNAL_ERROR;
1007  MI_CHECK param;
1008 
1009  myisamchk_init(&param);
1010  param.thd = thd;
1011  param.op_name= "optimize";
1012  param.testflag= (check_opt->flags | T_SILENT | T_FORCE_CREATE |
1013  T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX);
1014  param.sort_buffer_length= THDVAR(thd, sort_buffer_size);
1015  if ((error= repair(thd,param,1)) && param.retry_repair)
1016  {
1017  sql_print_warning("Warning: Optimize table got errno %d on %s.%s, retrying",
1018  my_errno, param.db_name, param.table_name);
1019  param.testflag&= ~T_REP_BY_SORT;
1020  error= repair(thd,param,1);
1021  }
1022  return error;
1023 }
1024 
1025 
1026 int ha_myisam::repair(THD *thd, MI_CHECK &param, bool do_optimize)
1027 {
1028  int error=0;
1029  uint local_testflag=param.testflag;
1030  bool optimize_done= !do_optimize, statistics_done=0;
1031  bool has_old_locks= thd->locked_tables_mode || file->lock_type != F_UNLCK;
1032  const char *old_proc_info=thd->proc_info;
1033  char fixed_name[FN_REFLEN];
1034  MYISAM_SHARE* share = file->s;
1035  ha_rows rows= file->state->records;
1036  DBUG_ENTER("ha_myisam::repair");
1037 
1038  param.db_name= table->s->db.str;
1039  param.table_name= table->alias;
1040  param.tmpfile_createflag = O_RDWR | O_TRUNC;
1041  param.using_global_keycache = 1;
1042  param.thd= thd;
1043  param.tmpdir= &mysql_tmpdir_list;
1044  param.out_flag= 0;
1045  strmov(fixed_name,file->filename);
1046 
1047  // Release latches since this can take a long time
1049 
1050  // Don't lock tables if we have used LOCK TABLE or already locked.
1051  if (!has_old_locks &&
1052  mi_lock_database(file, table->s->tmp_table ? F_EXTRA_LCK : F_WRLCK))
1053  {
1054  char errbuf[MYSYS_STRERROR_SIZE];
1055  mi_check_print_error(&param, ER(ER_CANT_LOCK), my_errno,
1056  my_strerror(errbuf, sizeof(errbuf), my_errno));
1057  DBUG_RETURN(HA_ADMIN_FAILED);
1058  }
1059 
1060  if (!do_optimize ||
1061  ((file->state->del || share->state.split != file->state->records) &&
1062  (!(param.testflag & T_QUICK) ||
1063  !(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))))
1064  {
1065  ulonglong key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ?
1066  mi_get_mask_all_keys_active(share->base.keys) :
1067  share->state.key_map);
1068  uint testflag=param.testflag;
1069 #ifdef HAVE_MMAP
1070  bool remap= test(share->file_map);
1071  /*
1072  mi_repair*() functions family use file I/O even if memory
1073  mapping is available.
1074 
1075  Since mixing mmap I/O and file I/O may cause various artifacts,
1076  memory mapping must be disabled.
1077  */
1078  if (remap)
1079  mi_munmap_file(file);
1080 #endif
1081  if (mi_test_if_sort_rep(file,file->state->records,key_map,0) &&
1082  (local_testflag & T_REP_BY_SORT))
1083  {
1084  local_testflag|= T_STATISTICS;
1085  param.testflag|= T_STATISTICS; // We get this for free
1086  statistics_done=1;
1087  if (THDVAR(thd, repair_threads)>1)
1088  {
1089  char buf[40];
1090  /* TODO: respect myisam_repair_threads variable */
1091  my_snprintf(buf, 40, "Repair with %d threads", my_count_bits(key_map));
1092  thd_proc_info(thd, buf);
1093  error = mi_repair_parallel(&param, file, fixed_name,
1094  param.testflag & T_QUICK);
1095  thd_proc_info(thd, "Repair done"); // to reset proc_info, as
1096  // it was pointing to local buffer
1097  }
1098  else
1099  {
1100  thd_proc_info(thd, "Repair by sorting");
1101  error = mi_repair_by_sort(&param, file, fixed_name,
1102  param.testflag & T_QUICK);
1103  }
1104  }
1105  else
1106  {
1107  thd_proc_info(thd, "Repair with keycache");
1108  param.testflag &= ~T_REP_BY_SORT;
1109  error= mi_repair(&param, file, fixed_name,
1110  param.testflag & T_QUICK);
1111  }
1112 #ifdef HAVE_MMAP
1113  if (remap)
1114  mi_dynmap_file(file, file->state->data_file_length);
1115 #endif
1116  param.testflag=testflag;
1117  optimize_done=1;
1118  }
1119  if (!error)
1120  {
1121  if ((local_testflag & T_SORT_INDEX) &&
1122  (share->state.changed & STATE_NOT_SORTED_PAGES))
1123  {
1124  optimize_done=1;
1125  thd_proc_info(thd, "Sorting index");
1126  error=mi_sort_index(&param,file,fixed_name);
1127  }
1128  if (!statistics_done && (local_testflag & T_STATISTICS))
1129  {
1130  if (share->state.changed & STATE_NOT_ANALYZED)
1131  {
1132  optimize_done=1;
1133  thd_proc_info(thd, "Analyzing");
1134  error = chk_key(&param, file);
1135  }
1136  else
1137  local_testflag&= ~T_STATISTICS; // Don't update statistics
1138  }
1139  }
1140  thd_proc_info(thd, "Saving state");
1141  if (!error)
1142  {
1143  if ((share->state.changed & STATE_CHANGED) || mi_is_crashed(file))
1144  {
1145  share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
1146  STATE_CRASHED_ON_REPAIR);
1147  file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
1148  }
1149  /*
1150  the following 'if', thought conceptually wrong,
1151  is a useful optimization nevertheless.
1152  */
1153  if (file->state != &file->s->state.state)
1154  file->s->state.state = *file->state;
1155  if (file->s->base.auto_key)
1156  update_auto_increment_key(&param, file, 1);
1157  if (optimize_done)
1158  error = update_state_info(&param, file,
1159  UPDATE_TIME | UPDATE_OPEN_COUNT |
1160  (local_testflag &
1161  T_STATISTICS ? UPDATE_STAT : 0));
1162  info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
1163  HA_STATUS_CONST);
1164  if (rows != file->state->records && ! (param.testflag & T_VERY_SILENT))
1165  {
1166  char llbuff[22],llbuff2[22];
1167  mi_check_print_warning(&param,"Number of rows changed from %s to %s",
1168  llstr(rows,llbuff),
1169  llstr(file->state->records,llbuff2));
1170  }
1171  }
1172  else
1173  {
1174  mi_mark_crashed_on_repair(file);
1175  file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
1176  update_state_info(&param, file, 0);
1177  }
1178  thd_proc_info(thd, old_proc_info);
1179  if (!has_old_locks)
1180  mi_lock_database(file,F_UNLCK);
1181  DBUG_RETURN(error ? HA_ADMIN_FAILED :
1182  !optimize_done ? HA_ADMIN_ALREADY_DONE : HA_ADMIN_OK);
1183 }
1184 
1185 
1186 /*
1187  Assign table indexes to a specific key cache.
1188 */
1189 
1190 int ha_myisam::assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt)
1191 {
1192  KEY_CACHE *new_key_cache= check_opt->key_cache;
1193  const char *errmsg= 0;
1194  int error= HA_ADMIN_OK;
1195  ulonglong map;
1196  TABLE_LIST *table_list= table->pos_in_table_list;
1197  DBUG_ENTER("ha_myisam::assign_to_keycache");
1198 
1199  table->keys_in_use_for_query.clear_all();
1200 
1201  if (table_list->process_index_hints(table))
1202  DBUG_RETURN(HA_ADMIN_FAILED);
1203  map= ~(ulonglong) 0;
1204  if (!table->keys_in_use_for_query.is_clear_all())
1205  /* use all keys if there's no list specified by the user through hints */
1206  map= table->keys_in_use_for_query.to_ulonglong();
1207 
1208  if ((error= mi_assign_to_key_cache(file, map, new_key_cache)))
1209  {
1210  char buf[STRING_BUFFER_USUAL_SIZE];
1211  my_snprintf(buf, sizeof(buf),
1212  "Failed to flush to index file (errno: %d)", error);
1213  errmsg= buf;
1214  error= HA_ADMIN_CORRUPT;
1215  }
1216 
1217  if (error != HA_ADMIN_OK)
1218  {
1219  /* Send error to user */
1220  MI_CHECK param;
1221  myisamchk_init(&param);
1222  param.thd= thd;
1223  param.op_name= "assign_to_keycache";
1224  param.db_name= table->s->db.str;
1225  param.table_name= table->s->table_name.str;
1226  param.testflag= 0;
1227  mi_check_print_error(&param, errmsg);
1228  }
1229  DBUG_RETURN(error);
1230 }
1231 
1232 
1233 /*
1234  Preload pages of the index file for a table into the key cache.
1235 */
1236 
1237 int ha_myisam::preload_keys(THD* thd, HA_CHECK_OPT *check_opt)
1238 {
1239  int error;
1240  const char *errmsg;
1241  ulonglong map;
1242  TABLE_LIST *table_list= table->pos_in_table_list;
1243  my_bool ignore_leaves= table_list->ignore_leaves;
1244  char buf[MYSQL_ERRMSG_SIZE];
1245 
1246  DBUG_ENTER("ha_myisam::preload_keys");
1247 
1248  table->keys_in_use_for_query.clear_all();
1249 
1250  if (table_list->process_index_hints(table))
1251  DBUG_RETURN(HA_ADMIN_FAILED);
1252 
1253  map= ~(ulonglong) 0;
1254  /* Check validity of the index references */
1255  if (!table->keys_in_use_for_query.is_clear_all())
1256  /* use all keys if there's no list specified by the user through hints */
1257  map= table->keys_in_use_for_query.to_ulonglong();
1258 
1259  mi_extra(file, HA_EXTRA_PRELOAD_BUFFER_SIZE,
1260  (void *) &thd->variables.preload_buff_size);
1261 
1262  if ((error= mi_preload(file, map, ignore_leaves)))
1263  {
1264  switch (error) {
1265  case HA_ERR_NON_UNIQUE_BLOCK_SIZE:
1266  errmsg= "Indexes use different block sizes";
1267  break;
1268  case HA_ERR_OUT_OF_MEM:
1269  errmsg= "Failed to allocate buffer";
1270  break;
1271  default:
1272  my_snprintf(buf, sizeof(buf),
1273  "Failed to read from index file (errno: %d)", my_errno);
1274  errmsg= buf;
1275  }
1276  error= HA_ADMIN_FAILED;
1277  goto err;
1278  }
1279 
1280  DBUG_RETURN(HA_ADMIN_OK);
1281 
1282  err:
1283  {
1284  MI_CHECK param;
1285  myisamchk_init(&param);
1286  param.thd= thd;
1287  param.op_name= "preload_keys";
1288  param.db_name= table->s->db.str;
1289  param.table_name= table->s->table_name.str;
1290  param.testflag= 0;
1291  mi_check_print_error(&param, errmsg);
1292  DBUG_RETURN(error);
1293  }
1294 }
1295 
1296 
1297 /*
1298  Disable indexes, making it persistent if requested.
1299 
1300  SYNOPSIS
1301  disable_indexes()
1302  mode mode of operation:
1303  HA_KEY_SWITCH_NONUNIQ disable all non-unique keys
1304  HA_KEY_SWITCH_ALL disable all keys
1305  HA_KEY_SWITCH_NONUNIQ_SAVE dis. non-uni. and make persistent
1306  HA_KEY_SWITCH_ALL_SAVE dis. all keys and make persistent
1307 
1308  IMPLEMENTATION
1309  HA_KEY_SWITCH_NONUNIQ is not implemented.
1310  HA_KEY_SWITCH_ALL_SAVE is not implemented.
1311 
1312  RETURN
1313  0 ok
1314  HA_ERR_WRONG_COMMAND mode not implemented.
1315 */
1316 
1317 int ha_myisam::disable_indexes(uint mode)
1318 {
1319  int error;
1320 
1321  if (mode == HA_KEY_SWITCH_ALL)
1322  {
1323  /* call a storage engine function to switch the key map */
1324  error= mi_disable_indexes(file);
1325  }
1326  else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
1327  {
1328  mi_extra(file, HA_EXTRA_NO_KEYS, 0);
1329  info(HA_STATUS_CONST); // Read new key info
1330  error= 0;
1331  }
1332  else
1333  {
1334  /* mode not implemented */
1335  error= HA_ERR_WRONG_COMMAND;
1336  }
1337  return error;
1338 }
1339 
1340 
1341 /*
1342  Enable indexes, making it persistent if requested.
1343 
1344  SYNOPSIS
1345  enable_indexes()
1346  mode mode of operation:
1347  HA_KEY_SWITCH_NONUNIQ enable all non-unique keys
1348  HA_KEY_SWITCH_ALL enable all keys
1349  HA_KEY_SWITCH_NONUNIQ_SAVE en. non-uni. and make persistent
1350  HA_KEY_SWITCH_ALL_SAVE en. all keys and make persistent
1351 
1352  DESCRIPTION
1353  Enable indexes, which might have been disabled by disable_index() before.
1354  The modes without _SAVE work only if both data and indexes are empty,
1355  since the MyISAM repair would enable them persistently.
1356  To be sure in these cases, call handler::delete_all_rows() before.
1357 
1358  IMPLEMENTATION
1359  HA_KEY_SWITCH_NONUNIQ is not implemented.
1360  HA_KEY_SWITCH_ALL_SAVE is not implemented.
1361 
1362  RETURN
1363  0 ok
1364  !=0 Error, among others:
1365  HA_ERR_CRASHED data or index is non-empty. Delete all rows and retry.
1366  HA_ERR_WRONG_COMMAND mode not implemented.
1367 */
1368 
1369 int ha_myisam::enable_indexes(uint mode)
1370 {
1371  int error;
1372 
1373  DBUG_EXECUTE_IF("wait_in_enable_indexes",
1374  debug_wait_for_kill("wait_in_enable_indexes"); );
1375 
1376  if (mi_is_all_keys_active(file->s->state.key_map, file->s->base.keys))
1377  {
1378  /* All indexes are enabled already. */
1379  return 0;
1380  }
1381 
1382  if (mode == HA_KEY_SWITCH_ALL)
1383  {
1384  error= mi_enable_indexes(file);
1385  /*
1386  Do not try to repair on error,
1387  as this could make the enabled state persistent,
1388  but mode==HA_KEY_SWITCH_ALL forbids it.
1389  */
1390  }
1391  else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
1392  {
1393  THD *thd=current_thd;
1394  MI_CHECK param;
1395  const char *save_proc_info=thd->proc_info;
1396  thd_proc_info(thd, "Creating index");
1397  myisamchk_init(&param);
1398  param.op_name= "recreating_index";
1399  param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
1400  T_CREATE_MISSING_KEYS);
1401  param.myf_rw&= ~MY_WAIT_IF_FULL;
1402  param.sort_buffer_length= THDVAR(thd, sort_buffer_size);
1403  param.stats_method= (enum_mi_stats_method)THDVAR(thd, stats_method);
1404  param.tmpdir=&mysql_tmpdir_list;
1405  if ((error= (repair(thd,param,0) != HA_ADMIN_OK)) && param.retry_repair)
1406  {
1407  sql_print_warning("Warning: Enabling keys got errno %d on %s.%s, retrying",
1408  my_errno, param.db_name, param.table_name);
1409  /*
1410  Repairing by sort failed. Now try standard repair method.
1411  Still we want to fix only index file. If data file corruption
1412  was detected (T_RETRY_WITHOUT_QUICK), we shouldn't do much here.
1413  Let implicit repair do this job.
1414  */
1415  if (!(param.testflag & T_RETRY_WITHOUT_QUICK))
1416  {
1417  param.testflag&= ~T_REP_BY_SORT;
1418  error= (repair(thd,param,0) != HA_ADMIN_OK);
1419  }
1420  /*
1421  If the standard repair succeeded, clear all error messages which
1422  might have been set by the first repair. They can still be seen
1423  with SHOW WARNINGS then.
1424  */
1425  if (! error)
1426  thd->clear_error();
1427  }
1428  info(HA_STATUS_CONST);
1429  thd_proc_info(thd, save_proc_info);
1430  }
1431  else
1432  {
1433  /* mode not implemented */
1434  error= HA_ERR_WRONG_COMMAND;
1435  }
1436  return error;
1437 }
1438 
1439 
1440 /*
1441  Test if indexes are disabled.
1442 
1443 
1444  SYNOPSIS
1445  indexes_are_disabled()
1446  no parameters
1447 
1448 
1449  RETURN
1450  0 indexes are not disabled
1451  1 all indexes are disabled
1452  [2 non-unique indexes are disabled - NOT YET IMPLEMENTED]
1453 */
1454 
1455 int ha_myisam::indexes_are_disabled(void)
1456 {
1457 
1458  return mi_indexes_are_disabled(file);
1459 }
1460 
1461 
1462 /*
1463  prepare for a many-rows insert operation
1464  e.g. - disable indexes (if they can be recreated fast) or
1465  activate special bulk-insert optimizations
1466 
1467  SYNOPSIS
1468  start_bulk_insert(rows)
1469  rows Rows to be inserted
1470  0 if we don't know
1471 
1472  NOTICE
1473  Do not forget to call end_bulk_insert() later!
1474 */
1475 
1476 void ha_myisam::start_bulk_insert(ha_rows rows)
1477 {
1478  DBUG_ENTER("ha_myisam::start_bulk_insert");
1479  THD *thd= current_thd;
1480  ulong size= min(thd->variables.read_buff_size,
1481  (ulong) (table->s->avg_row_length*rows));
1482  DBUG_PRINT("info",("start_bulk_insert: rows %lu size %lu",
1483  (ulong) rows, size));
1484 
1485  /* don't enable row cache if too few rows */
1486  if (! rows || (rows > MI_MIN_ROWS_TO_USE_WRITE_CACHE))
1487  mi_extra(file, HA_EXTRA_WRITE_CACHE, (void*) &size);
1488 
1489  can_enable_indexes= mi_is_all_keys_active(file->s->state.key_map,
1490  file->s->base.keys);
1491 
1492  /*
1493  Only disable old index if the table was empty and we are inserting
1494  a lot of rows.
1495  Note that in end_bulk_insert() we may truncate the table if
1496  enable_indexes() failed, thus it's essential that indexes are
1497  disabled ONLY for an empty table.
1498  */
1499  if (file->state->records == 0 && can_enable_indexes &&
1500  (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES))
1501  mi_disable_non_unique_index(file,rows);
1502  else
1503  if (!file->bulk_insert &&
1504  (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT))
1505  {
1506  mi_init_bulk_insert(file, thd->variables.bulk_insert_buff_size, rows);
1507  }
1508  DBUG_VOID_RETURN;
1509 }
1510 
1511 /*
1512  end special bulk-insert optimizations,
1513  which have been activated by start_bulk_insert().
1514 
1515  SYNOPSIS
1516  end_bulk_insert()
1517  no arguments
1518 
1519  RETURN
1520  0 OK
1521  != 0 Error
1522 */
1523 
1524 int ha_myisam::end_bulk_insert()
1525 {
1526  mi_end_bulk_insert(file);
1527  int err=mi_extra(file, HA_EXTRA_NO_CACHE, 0);
1528  if (!err)
1529  {
1530  if (can_enable_indexes)
1531  {
1532  /*
1533  Truncate the table when enable index operation is killed.
1534  After truncating the table we don't need to enable the
1535  indexes, because the last repair operation is aborted after
1536  setting the indexes as active and trying to recreate them.
1537  */
1538 
1539  if (((err= enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE)) != 0) &&
1540  current_thd->killed)
1541  {
1542  delete_all_rows();
1543  /* not crashed, despite being killed during repair */
1544  file->s->state.changed&= ~(STATE_CRASHED|STATE_CRASHED_ON_REPAIR);
1545  }
1546  }
1547  }
1548  return err;
1549 }
1550 
1551 
1552 bool ha_myisam::check_and_repair(THD *thd)
1553 {
1554  int error=0;
1555  int marked_crashed;
1556  HA_CHECK_OPT check_opt;
1557  DBUG_ENTER("ha_myisam::check_and_repair");
1558 
1559  check_opt.init();
1560  check_opt.flags= T_MEDIUM | T_AUTO_REPAIR;
1561  // Don't use quick if deleted rows
1562  if (!file->state->del && (myisam_recover_options & HA_RECOVER_QUICK))
1563  check_opt.flags|=T_QUICK;
1564  sql_print_warning("Checking table: '%s'",table->s->path.str);
1565 
1566  const CSET_STRING query_backup= thd->query_string;
1567  thd->set_query(table->s->table_name.str,
1568  (uint) table->s->table_name.length, system_charset_info);
1569 
1570  if ((marked_crashed= mi_is_crashed(file)) || check(thd, &check_opt))
1571  {
1572  sql_print_warning("Recovering table: '%s'",table->s->path.str);
1573  check_opt.flags=
1574  ((myisam_recover_options & HA_RECOVER_BACKUP ? T_BACKUP_DATA : 0) |
1575  (marked_crashed ? 0 : T_QUICK) |
1576  (myisam_recover_options & HA_RECOVER_FORCE ? 0 : T_SAFE_REPAIR) |
1577  T_AUTO_REPAIR);
1578  if (repair(thd, &check_opt))
1579  error=1;
1580  }
1581  thd->set_query(query_backup);
1582  DBUG_RETURN(error);
1583 }
1584 
1585 bool ha_myisam::is_crashed() const
1586 {
1587  return (file->s->state.changed & STATE_CRASHED ||
1588  (my_disable_locking && file->s->state.open_count));
1589 }
1590 
1591 int ha_myisam::update_row(const uchar *old_data, uchar *new_data)
1592 {
1593  ha_statistic_increment(&SSV::ha_update_count);
1594  return mi_update(file,old_data,new_data);
1595 }
1596 
1597 int ha_myisam::delete_row(const uchar *buf)
1598 {
1599  ha_statistic_increment(&SSV::ha_delete_count);
1600  return mi_delete(file,buf);
1601 }
1602 
1603 C_MODE_START
1604 
1605 ICP_RESULT index_cond_func_myisam(void *arg)
1606 {
1607  ha_myisam *h= (ha_myisam*)arg;
1608 
1609  if (h->end_range && h->compare_key_icp(h->end_range) > 0)
1610  return ICP_OUT_OF_RANGE; /* caller should return HA_ERR_END_OF_FILE already */
1611 
1612  return (ICP_RESULT) test(h->pushed_idx_cond->val_int());
1613 }
1614 
1615 C_MODE_END
1616 
1617 
1618 int ha_myisam::index_init(uint idx, bool sorted)
1619 {
1620  active_index=idx;
1621  if (pushed_idx_cond_keyno == idx)
1622  mi_set_index_cond_func(file, index_cond_func_myisam, this);
1623  return 0;
1624 }
1625 
1626 
1627 int ha_myisam::index_end()
1628 {
1629  active_index=MAX_KEY;
1630  //pushed_idx_cond_keyno= MAX_KEY;
1631  mi_set_index_cond_func(file, NULL, 0);
1632  in_range_check_pushed_down= FALSE;
1633  ds_mrr.dsmrr_close();
1634  return 0;
1635 }
1636 
1637 int ha_myisam::rnd_end()
1638 {
1639  ds_mrr.dsmrr_close();
1640  return 0;
1641 }
1642 
1643 int ha_myisam::index_read_map(uchar *buf, const uchar *key,
1644  key_part_map keypart_map,
1645  enum ha_rkey_function find_flag)
1646 {
1647  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
1648  DBUG_ASSERT(inited==INDEX);
1649  ha_statistic_increment(&SSV::ha_read_key_count);
1650  int error=mi_rkey(file, buf, active_index, key, keypart_map, find_flag);
1651  table->status=error ? STATUS_NOT_FOUND: 0;
1652  MYSQL_INDEX_READ_ROW_DONE(error);
1653  return error;
1654 }
1655 
1656 int ha_myisam::index_read_idx_map(uchar *buf, uint index, const uchar *key,
1657  key_part_map keypart_map,
1658  enum ha_rkey_function find_flag)
1659 {
1660  DBUG_ASSERT(pushed_idx_cond == NULL);
1661  DBUG_ASSERT(pushed_idx_cond_keyno == MAX_KEY);
1662  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
1663  ha_statistic_increment(&SSV::ha_read_key_count);
1664  int error=mi_rkey(file, buf, index, key, keypart_map, find_flag);
1665  table->status=error ? STATUS_NOT_FOUND: 0;
1666  MYSQL_INDEX_READ_ROW_DONE(error);
1667  return error;
1668 }
1669 
1670 int ha_myisam::index_read_last_map(uchar *buf, const uchar *key,
1671  key_part_map keypart_map)
1672 {
1673  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
1674  DBUG_ENTER("ha_myisam::index_read_last");
1675  DBUG_ASSERT(inited==INDEX);
1676  ha_statistic_increment(&SSV::ha_read_key_count);
1677  int error=mi_rkey(file, buf, active_index, key, keypart_map,
1678  HA_READ_PREFIX_LAST);
1679  table->status=error ? STATUS_NOT_FOUND: 0;
1680  MYSQL_INDEX_READ_ROW_DONE(error);
1681  DBUG_RETURN(error);
1682 }
1683 
1684 int ha_myisam::index_next(uchar *buf)
1685 {
1686  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
1687  DBUG_ASSERT(inited==INDEX);
1688  ha_statistic_increment(&SSV::ha_read_next_count);
1689  int error=mi_rnext(file,buf,active_index);
1690  table->status=error ? STATUS_NOT_FOUND: 0;
1691  MYSQL_INDEX_READ_ROW_DONE(error);
1692  return error;
1693 }
1694 
1695 int ha_myisam::index_prev(uchar *buf)
1696 {
1697  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
1698  DBUG_ASSERT(inited==INDEX);
1699  ha_statistic_increment(&SSV::ha_read_prev_count);
1700  int error=mi_rprev(file,buf, active_index);
1701  table->status=error ? STATUS_NOT_FOUND: 0;
1702  MYSQL_INDEX_READ_ROW_DONE(error);
1703  return error;
1704 }
1705 
1706 int ha_myisam::index_first(uchar *buf)
1707 {
1708  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
1709  DBUG_ASSERT(inited==INDEX);
1710  ha_statistic_increment(&SSV::ha_read_first_count);
1711  int error=mi_rfirst(file, buf, active_index);
1712  table->status=error ? STATUS_NOT_FOUND: 0;
1713  MYSQL_INDEX_READ_ROW_DONE(error);
1714  return error;
1715 }
1716 
1717 int ha_myisam::index_last(uchar *buf)
1718 {
1719  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
1720  DBUG_ASSERT(inited==INDEX);
1721  ha_statistic_increment(&SSV::ha_read_last_count);
1722  int error=mi_rlast(file, buf, active_index);
1723  table->status=error ? STATUS_NOT_FOUND: 0;
1724  MYSQL_INDEX_READ_ROW_DONE(error);
1725  return error;
1726 }
1727 
1729  const uchar *key __attribute__((unused)),
1730  uint length __attribute__((unused)))
1731 {
1732  int error;
1733  DBUG_ASSERT(inited==INDEX);
1734  MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
1735  ha_statistic_increment(&SSV::ha_read_next_count);
1736  do
1737  {
1738  error= mi_rnext_same(file,buf);
1739  } while (error == HA_ERR_RECORD_DELETED);
1740  table->status=error ? STATUS_NOT_FOUND: 0;
1741  MYSQL_INDEX_READ_ROW_DONE(error);
1742  return error;
1743 }
1744 
1745 
1746 int ha_myisam::rnd_init(bool scan)
1747 {
1748  if (scan)
1749  return mi_scan_init(file);
1750  return mi_reset(file); // Free buffers
1751 }
1752 
1753 int ha_myisam::rnd_next(uchar *buf)
1754 {
1755  MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str,
1756  TRUE);
1757  ha_statistic_increment(&SSV::ha_read_rnd_next_count);
1758  int error=mi_scan(file, buf);
1759  table->status=error ? STATUS_NOT_FOUND: 0;
1760  MYSQL_READ_ROW_DONE(error);
1761  return error;
1762 }
1763 
1764 int ha_myisam::restart_rnd_next(uchar *buf, uchar *pos)
1765 {
1766  return rnd_pos(buf,pos);
1767 }
1768 
1769 int ha_myisam::rnd_pos(uchar *buf, uchar *pos)
1770 {
1771  MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str,
1772  FALSE);
1773  ha_statistic_increment(&SSV::ha_read_rnd_count);
1774  int error=mi_rrnd(file, buf, my_get_ptr(pos,ref_length));
1775  table->status=error ? STATUS_NOT_FOUND: 0;
1776  MYSQL_READ_ROW_DONE(error);
1777  return error;
1778 }
1779 
1780 
1781 void ha_myisam::position(const uchar *record)
1782 {
1783  my_off_t row_position= mi_position(file);
1784  my_store_ptr(ref, ref_length, row_position);
1785 }
1786 
1787 int ha_myisam::info(uint flag)
1788 {
1789  MI_ISAMINFO misam_info;
1790  char name_buff[FN_REFLEN];
1791 
1792  (void) mi_status(file,&misam_info,flag);
1793  if (flag & HA_STATUS_VARIABLE)
1794  {
1795  stats.records= misam_info.records;
1796  stats.deleted= misam_info.deleted;
1797  stats.data_file_length= misam_info.data_file_length;
1798  stats.index_file_length= misam_info.index_file_length;
1799  stats.delete_length= misam_info.delete_length;
1800  stats.check_time= (ulong) misam_info.check_time;
1801  stats.mean_rec_length= misam_info.mean_reclength;
1802  }
1803  if (flag & HA_STATUS_CONST)
1804  {
1805  TABLE_SHARE *share= table->s;
1806  stats.max_data_file_length= misam_info.max_data_file_length;
1807  stats.max_index_file_length= misam_info.max_index_file_length;
1808  stats.create_time= misam_info.create_time;
1809  /*
1810  We want the value of stats.mrr_length_per_rec to be platform independent.
1811  The size of the chunk at the end of the join buffer used for MRR needs
1812  is calculated now basing on the values passed in the stats structure.
1813  The remaining part of the join buffer is used for records. A different
1814  number of records in the buffer results in a different number of buffer
1815  refills and in a different order of records in the result set.
1816  */
1817  stats.mrr_length_per_rec= misam_info.reflength + 8; // 8=max(sizeof(void *))
1818  ref_length= misam_info.reflength;
1819  share->db_options_in_use= misam_info.options;
1820  stats.block_size= myisam_block_size; /* record block size */
1821 
1822  /*
1823  Update share.
1824  lock_shared_ha_data is slighly abused here, since there is no other
1825  way of locking the TABLE_SHARE.
1826  */
1828  share->keys_in_use.set_prefix(share->keys);
1829  share->keys_in_use.intersect_extended(misam_info.key_map);
1830  share->keys_for_keyread.intersect(share->keys_in_use);
1831  share->db_record_offset= misam_info.record_offset;
1833  if (share->key_parts)
1834  memcpy((char*) table->key_info[0].rec_per_key,
1835  (char*) misam_info.rec_per_key,
1836  sizeof(table->key_info[0].rec_per_key[0])*share->key_parts);
1837 
1838  /*
1839  Set data_file_name and index_file_name to point at the symlink value
1840  if table is symlinked (Ie; Real name is not same as generated name)
1841  */
1842  data_file_name= index_file_name= 0;
1843  fn_format(name_buff, file->filename, "", MI_NAME_DEXT,
1844  MY_APPEND_EXT | MY_UNPACK_FILENAME);
1845  if (strcmp(name_buff, misam_info.data_file_name))
1846  data_file_name=misam_info.data_file_name;
1847  fn_format(name_buff, file->filename, "", MI_NAME_IEXT,
1848  MY_APPEND_EXT | MY_UNPACK_FILENAME);
1849  if (strcmp(name_buff, misam_info.index_file_name))
1850  index_file_name=misam_info.index_file_name;
1851  }
1852  if (flag & HA_STATUS_ERRKEY)
1853  {
1854  errkey = misam_info.errkey;
1855  my_store_ptr(dup_ref, ref_length, misam_info.dupp_key_pos);
1856  }
1857  if (flag & HA_STATUS_TIME)
1858  stats.update_time = (ulong) misam_info.update_time;
1859  if (flag & HA_STATUS_AUTO)
1860  stats.auto_increment_value= misam_info.auto_increment;
1861 
1862  return 0;
1863 }
1864 
1865 
1866 int ha_myisam::extra(enum ha_extra_function operation)
1867 {
1868  if (operation == HA_EXTRA_MMAP && !opt_myisam_use_mmap)
1869  return 0;
1870  return mi_extra(file, operation, 0);
1871 }
1872 
1874 {
1875  /* Reset MyISAM specific part for index condition pushdown */
1876  DBUG_ASSERT(pushed_idx_cond == NULL);
1877  DBUG_ASSERT(pushed_idx_cond_keyno == MAX_KEY);
1878  mi_set_index_cond_func(file, NULL, 0);
1879  ds_mrr.reset();
1880  return mi_reset(file);
1881 }
1882 
1883 /* To be used with WRITE_CACHE and EXTRA_CACHE */
1884 
1885 int ha_myisam::extra_opt(enum ha_extra_function operation, ulong cache_size)
1886 {
1887  return mi_extra(file, operation, (void*) &cache_size);
1888 }
1889 
1891 {
1892  return mi_delete_all_rows(file);
1893 }
1894 
1895 
1896 /*
1897  Intended to support partitioning.
1898  Allows a particular partition to be truncated.
1899 */
1900 
1902 {
1903  int error= delete_all_rows();
1904  return error ? error : reset_auto_increment(0);
1905 }
1906 
1908 {
1909  file->s->state.auto_increment= value;
1910  return 0;
1911 }
1912 
1913 int ha_myisam::delete_table(const char *name)
1914 {
1915  return mi_delete_table(name);
1916 }
1917 
1918 
1919 int ha_myisam::external_lock(THD *thd, int lock_type)
1920 {
1921  file->in_use.data= thd;
1922  return mi_lock_database(file, !table->s->tmp_table ?
1923  lock_type : ((lock_type == F_UNLCK) ?
1924  F_UNLCK : F_EXTRA_LCK));
1925 }
1926 
1928  THR_LOCK_DATA **to,
1929  enum thr_lock_type lock_type)
1930 {
1931  if (lock_type != TL_IGNORE && file->lock.type == TL_UNLOCK)
1932  file->lock.type=lock_type;
1933  *to++= &file->lock;
1934  return to;
1935 }
1936 
1937 void ha_myisam::update_create_info(HA_CREATE_INFO *create_info)
1938 {
1939  ha_myisam::info(HA_STATUS_AUTO | HA_STATUS_CONST);
1940  if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
1941  {
1942  create_info->auto_increment_value= stats.auto_increment_value;
1943  }
1944  create_info->data_file_name=data_file_name;
1945  create_info->index_file_name=index_file_name;
1946 }
1947 
1948 
1949 int ha_myisam::create(const char *name, register TABLE *table_arg,
1950  HA_CREATE_INFO *ha_create_info)
1951 {
1952  int error;
1953  uint create_flags= 0, records, i;
1954  char buff[FN_REFLEN];
1955  MI_KEYDEF *keydef;
1956  MI_COLUMNDEF *recinfo;
1957  MI_CREATE_INFO create_info;
1958  TABLE_SHARE *share= table_arg->s;
1959  uint options= share->db_options_in_use;
1960  DBUG_ENTER("ha_myisam::create");
1961  for (i= 0; i < share->keys; i++)
1962  {
1963  if (table_arg->key_info[i].flags & HA_USES_PARSER)
1964  {
1965  create_flags|= HA_CREATE_RELIES_ON_SQL_LAYER;
1966  break;
1967  }
1968  }
1969  if ((error= table2myisam(table_arg, &keydef, &recinfo, &records)))
1970  DBUG_RETURN(error); /* purecov: inspected */
1971  memset(&create_info, 0, sizeof(create_info));
1972  create_info.max_rows= share->max_rows;
1973  create_info.reloc_rows= share->min_rows;
1974  create_info.with_auto_increment= share->next_number_key_offset == 0;
1975  create_info.auto_increment= (ha_create_info->auto_increment_value ?
1976  ha_create_info->auto_increment_value -1 :
1977  (ulonglong) 0);
1978  create_info.data_file_length= ((ulonglong) share->max_rows *
1979  share->avg_row_length);
1980  create_info.language= share->table_charset->number;
1981 
1982 #ifdef HAVE_READLINK
1983  if (my_use_symdir)
1984  {
1985  create_info.data_file_name= ha_create_info->data_file_name;
1986  create_info.index_file_name= ha_create_info->index_file_name;
1987  }
1988  else
1989 #endif /* HAVE_READLINK */
1990  {
1991  if (ha_create_info->data_file_name)
1992  push_warning_printf(table_arg->in_use, Sql_condition::WARN_LEVEL_WARN,
1993  WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
1994  "DATA DIRECTORY");
1995  if (ha_create_info->index_file_name)
1996  push_warning_printf(table_arg->in_use, Sql_condition::WARN_LEVEL_WARN,
1997  WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
1998  "INDEX DIRECTORY");
1999  }
2000 
2001  if (ha_create_info->options & HA_LEX_CREATE_TMP_TABLE)
2002  create_flags|= HA_CREATE_TMP_TABLE;
2003  if (ha_create_info->options & HA_CREATE_KEEP_FILES)
2004  create_flags|= HA_CREATE_KEEP_FILES;
2005  if (options & HA_OPTION_PACK_RECORD)
2006  create_flags|= HA_PACK_RECORD;
2007  if (options & HA_OPTION_CHECKSUM)
2008  create_flags|= HA_CREATE_CHECKSUM;
2009  if (options & HA_OPTION_DELAY_KEY_WRITE)
2010  create_flags|= HA_CREATE_DELAY_KEY_WRITE;
2011 
2012  /* TODO: Check that the following fn_format is really needed */
2013  error= mi_create(fn_format(buff, name, "", "",
2014  MY_UNPACK_FILENAME|MY_APPEND_EXT),
2015  share->keys, keydef,
2016  records, recinfo,
2017  0, (MI_UNIQUEDEF*) 0,
2018  &create_info, create_flags);
2019  my_free(recinfo);
2020  DBUG_RETURN(error);
2021 }
2022 
2023 
2024 int ha_myisam::rename_table(const char * from, const char * to)
2025 {
2026  return mi_rename(from,to);
2027 }
2028 
2029 
2030 void ha_myisam::get_auto_increment(ulonglong offset, ulonglong increment,
2031  ulonglong nb_desired_values,
2032  ulonglong *first_value,
2033  ulonglong *nb_reserved_values)
2034 {
2035  ulonglong nr;
2036  int error;
2037  uchar key[MI_MAX_KEY_LENGTH];
2038 
2039  if (!table->s->next_number_key_offset)
2040  { // Autoincrement at key-start
2041  ha_myisam::info(HA_STATUS_AUTO);
2042  *first_value= stats.auto_increment_value;
2043  /* MyISAM has only table-level lock, so reserves to +inf */
2044  *nb_reserved_values= ULONGLONG_MAX;
2045  return;
2046  }
2047 
2048  /* it's safe to call the following if bulk_insert isn't on */
2049  mi_flush_bulk_insert(file, table->s->next_number_index);
2050 
2051  (void) extra(HA_EXTRA_KEYREAD);
2052  key_copy(key, table->record[0],
2053  table->key_info + table->s->next_number_index,
2054  table->s->next_number_key_offset);
2055  error= mi_rkey(file, table->record[1], (int) table->s->next_number_index,
2056  key, make_prev_keypart_map(table->s->next_number_keypart),
2057  HA_READ_PREFIX_LAST);
2058  if (error)
2059  nr= 1;
2060  else
2061  {
2062  /* Get data from record[1] */
2063  nr= ((ulonglong) table->next_number_field->
2064  val_int_offset(table->s->rec_buff_length)+1);
2065  }
2066  extra(HA_EXTRA_NO_KEYREAD);
2067  *first_value= nr;
2068  /*
2069  MySQL needs to call us for next row: assume we are inserting ("a",null)
2070  here, we return 3, and next this statement will want to insert ("b",null):
2071  there is no reason why ("b",3+1) would be the good row to insert: maybe it
2072  already exists, maybe 3+1 is too large...
2073  */
2074  *nb_reserved_values= 1;
2075 }
2076 
2077 
2078 /*
2079  Find out how many rows there is in the given range
2080 
2081  SYNOPSIS
2082  records_in_range()
2083  inx Index to use
2084  min_key Start of range. Null pointer if from first key
2085  max_key End of range. Null pointer if to last key
2086 
2087  NOTES
2088  min_key.flag can have one of the following values:
2089  HA_READ_KEY_EXACT Include the key in the range
2090  HA_READ_AFTER_KEY Don't include key in range
2091 
2092  max_key.flag can have one of the following values:
2093  HA_READ_BEFORE_KEY Don't include key in range
2094  HA_READ_AFTER_KEY Include all 'end_key' values in the range
2095 
2096  RETURN
2097  HA_POS_ERROR Something is wrong with the index tree.
2098  0 There is no matching keys in the given range
2099  number > 0 There is approximately 'number' matching rows in
2100  the range.
2101 */
2102 
2103 ha_rows ha_myisam::records_in_range(uint inx, key_range *min_key,
2104  key_range *max_key)
2105 {
2106  return (ha_rows) mi_records_in_range(file, (int) inx, min_key, max_key);
2107 }
2108 
2109 
2110 int ha_myisam::ft_read(uchar *buf)
2111 {
2112  int error;
2113 
2114  if (!ft_handler)
2115  return -1;
2116 
2117  thread_safe_increment(table->in_use->status_var.ha_read_next_count,
2118  &LOCK_status); // why ?
2119 
2120  error=ft_handler->please->read_next(ft_handler,(char*) buf);
2121 
2122  table->status=error ? STATUS_NOT_FOUND: 0;
2123  return error;
2124 }
2125 
2126 uint ha_myisam::checksum() const
2127 {
2128  return (uint)file->state->checksum;
2129 }
2130 
2131 
2133  uint table_changes)
2134 {
2135  uint options= table->s->db_options_in_use;
2136 
2137  if (info->auto_increment_value != stats.auto_increment_value ||
2138  info->data_file_name != data_file_name ||
2139  info->index_file_name != index_file_name ||
2140  table_changes == IS_EQUAL_NO ||
2141  table_changes & IS_EQUAL_PACK_LENGTH) // Not implemented yet
2142  return COMPATIBLE_DATA_NO;
2143 
2144  if ((options & (HA_OPTION_PACK_RECORD | HA_OPTION_CHECKSUM |
2145  HA_OPTION_DELAY_KEY_WRITE)) !=
2146  (info->table_options & (HA_OPTION_PACK_RECORD | HA_OPTION_CHECKSUM |
2147  HA_OPTION_DELAY_KEY_WRITE)))
2148  return COMPATIBLE_DATA_NO;
2149  return COMPATIBLE_DATA_YES;
2150 }
2151 
2152 extern int mi_panic(enum ha_panic_function flag);
2153 int myisam_panic(handlerton *hton, ha_panic_function flag)
2154 {
2155  return mi_panic(flag);
2156 }
2157 
2158 static int myisam_init(void *p)
2159 {
2160  handlerton *myisam_hton;
2161 
2162 #ifdef HAVE_PSI_INTERFACE
2163  init_myisam_psi_keys();
2164 #endif
2165 
2166  /* Set global variables based on startup options */
2167  if (myisam_recover_options)
2168  ha_open_options|=HA_OPEN_ABORT_IF_CRASHED;
2169  else
2170  myisam_recover_options= HA_RECOVER_OFF;
2171 
2172  myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size);
2173 
2174  myisam_hton= (handlerton *)p;
2175  myisam_hton->state= SHOW_OPTION_YES;
2176  myisam_hton->db_type= DB_TYPE_MYISAM;
2177  myisam_hton->create= myisam_create_handler;
2178  myisam_hton->panic= myisam_panic;
2179  myisam_hton->flags= HTON_CAN_RECREATE | HTON_SUPPORT_LOG_TABLES;
2180  myisam_hton->is_supported_system_table= myisam_is_supported_system_table;
2181 
2182  return 0;
2183 }
2184 
2185 
2186 /****************************************************************************
2187  * MyISAM MRR implementation: use DS-MRR
2188  ***************************************************************************/
2189 
2190 int ha_myisam::multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
2191  uint n_ranges, uint mode,
2192  HANDLER_BUFFER *buf)
2193 {
2194  return ds_mrr.dsmrr_init(this, seq, seq_init_param, n_ranges, mode, buf);
2195 }
2196 
2197 int ha_myisam::multi_range_read_next(char **range_info)
2198 {
2199  return ds_mrr.dsmrr_next(range_info);
2200 }
2201 
2203  void *seq_init_param,
2204  uint n_ranges, uint *bufsz,
2205  uint *flags, Cost_estimate *cost)
2206 {
2207  /*
2208  This call is here because there is no location where this->table would
2209  already be known.
2210  TODO: consider moving it into some per-query initialization call.
2211  */
2212  ds_mrr.init(this, table);
2213  return ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges, bufsz,
2214  flags, cost);
2215 }
2216 
2217 ha_rows ha_myisam::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
2218  uint *bufsz, uint *flags,
2219  Cost_estimate *cost)
2220 {
2221  ds_mrr.init(this, table);
2222  return ds_mrr.dsmrr_info(keyno, n_ranges, keys, bufsz, flags, cost);
2223 }
2224 
2225 /* MyISAM MRR implementation ends */
2226 
2227 
2228 /* Index condition pushdown implementation*/
2229 
2230 
2231 Item *ha_myisam::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
2232 {
2233  /*
2234  Check if the key contains a blob field. If it does then MyISAM
2235  should not accept the pushed index condition since MyISAM will not
2236  read the blob field from the index entry during evaluation of the
2237  pushed index condition and the BLOB field might be part of the
2238  range evaluation done by the ICP code.
2239  */
2240  const KEY *key= &table_share->key_info[keyno_arg];
2241 
2242  for (uint k= 0; k < key->user_defined_key_parts; ++k)
2243  {
2244  const KEY_PART_INFO *key_part= &key->key_part[k];
2245  if (key_part->key_part_flag & HA_BLOB_PART)
2246  {
2247  /* Let the server handle the index condition */
2248  return idx_cond_arg;
2249  }
2250  }
2251 
2252  pushed_idx_cond_keyno= keyno_arg;
2253  pushed_idx_cond= idx_cond_arg;
2254  in_range_check_pushed_down= TRUE;
2255  if (active_index == pushed_idx_cond_keyno)
2256  mi_set_index_cond_func(file, index_cond_func_myisam, this);
2257  return NULL;
2258 }
2259 
2260 
2261 static struct st_mysql_sys_var* myisam_sysvars[]= {
2262  MYSQL_SYSVAR(block_size),
2263  MYSQL_SYSVAR(data_pointer_size),
2264  MYSQL_SYSVAR(max_sort_file_size),
2265  MYSQL_SYSVAR(recover_options),
2266  MYSQL_SYSVAR(repair_threads),
2267  MYSQL_SYSVAR(sort_buffer_size),
2268  MYSQL_SYSVAR(use_mmap),
2269  MYSQL_SYSVAR(mmap_size),
2270  MYSQL_SYSVAR(stats_method),
2271  0
2272 };
2273 
2274 struct st_mysql_storage_engine myisam_storage_engine=
2275 { MYSQL_HANDLERTON_INTERFACE_VERSION };
2276 
2277 mysql_declare_plugin(myisam)
2278 {
2279  MYSQL_STORAGE_ENGINE_PLUGIN,
2280  &myisam_storage_engine,
2281  "MyISAM",
2282  "MySQL AB",
2283  "MyISAM storage engine",
2284  PLUGIN_LICENSE_GPL,
2285  myisam_init, /* Plugin Init */
2286  NULL, /* Plugin Deinit */
2287  0x0100, /* 1.0 */
2288  NULL, /* status variables */
2289  myisam_sysvars, /* system variables */
2290  NULL,
2291  0,
2292 }
2293 mysql_declare_plugin_end;
2294 
2295 
2296 #ifdef HAVE_QUERY_CACHE
2297 
2317 my_bool ha_myisam::register_query_cache_table(THD *thd, char *table_name,
2318  uint table_name_len,
2319  qc_engine_callback
2320  *engine_callback,
2321  ulonglong *engine_data)
2322 {
2323  DBUG_ENTER("ha_myisam::register_query_cache_table");
2324  /*
2325  No call back function is needed to determine if a cached statement
2326  is valid or not.
2327  */
2328  *engine_callback= 0;
2329 
2330  /*
2331  No engine data is needed.
2332  */
2333  *engine_data= 0;
2334 
2335  if (file->s->concurrent_insert)
2336  {
2337  /*
2338  If a concurrent INSERT has happened just before the currently
2339  processed SELECT statement, the total size of the table is
2340  unknown.
2341 
2342  To determine if the table size is known, the current thread's snap
2343  shot of the table size with the actual table size are compared.
2344 
2345  If the table size is unknown the SELECT statement can't be cached.
2346 
2347  When concurrent inserts are disabled at table open, mi_open()
2348  does not assign a get_status() function. In this case the local
2349  ("current") status is never updated. We would wrongly think that
2350  we cannot cache the statement.
2351  */
2352  ulonglong actual_data_file_length;
2353  ulonglong current_data_file_length;
2354 
2355  /*
2356  POSIX visibility rules specify that "2. Whatever memory values a
2357  thread can see when it unlocks a mutex <...> can also be seen by any
2358  thread that later locks the same mutex". In this particular case,
2359  concurrent insert thread had modified the data_file_length in
2360  MYISAM_SHARE before it has unlocked (or even locked)
2361  structure_guard_mutex. So, here we're guaranteed to see at least that
2362  value after we've locked the same mutex. We can see a later value
2363  (modified by some other thread) though, but it's ok, as we only want
2364  to know if the variable was changed, the actual new value doesn't matter
2365  */
2366  actual_data_file_length= file->s->state.state.data_file_length;
2367  current_data_file_length= file->save_state.data_file_length;
2368 
2369  if (current_data_file_length != actual_data_file_length)
2370  {
2371  /* Don't cache current statement. */
2372  DBUG_RETURN(FALSE);
2373  }
2374  }
2375 
2376  /*
2377  This query execution might have started after the query cache was flushed
2378  by a concurrent INSERT. In this case, don't cache this statement as the
2379  data file length difference might not be visible yet if the tables haven't
2380  been unlocked by the concurrent insert thread.
2381  */
2382  if (file->state->uncacheable)
2383  DBUG_RETURN(FALSE);
2384 
2385  /* It is ok to try to cache current statement. */
2386  DBUG_RETURN(TRUE);
2387 }
2388 #endif