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