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