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