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