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