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