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