ha_myisam.cc 75.5 KB
Newer Older
1
/*
2
   Copyright (c) 2000, 2011, Oracle and/or its affiliates
3

unknown's avatar
unknown committed
4 5
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
6
   the Free Software Foundation; version 2 of the License.
7

unknown's avatar
unknown committed
8 9 10 11
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
12

unknown's avatar
unknown committed
13 14
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
15
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
unknown's avatar
unknown committed
16 17


18
#ifdef USE_PRAGMA_IMPLEMENTATION
unknown's avatar
unknown committed
19 20 21
#pragma implementation				// gcc: Class implementation
#endif

22
#define MYSQL_SERVER 1
23 24 25
#include "sql_priv.h"
#include "key.h"                                // key_copy
#include "sql_plugin.h"
unknown's avatar
unknown committed
26
#include <m_ctype.h>
27
#include <my_bit.h>
unknown's avatar
unknown committed
28
#include "ha_myisam.h"
29 30
#include "myisamdef.h"
#include "rt_index.h"
31 32
#include "sql_table.h"                          // tablename_to_filename
#include "sql_class.h"                          // THD
unknown's avatar
unknown committed
33

34 35
ulonglong myisam_recover_options;
static ulong opt_myisam_block_size;
36

37
/* bits in myisam_recover_options */
38
const char *myisam_recover_names[] =
Sergei Golubchik's avatar
Sergei Golubchik committed
39
{ "DEFAULT", "BACKUP", "FORCE", "QUICK", "BACKUP_ALL", "OFF", NullS};
40
TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"",
41
				 myisam_recover_names, NULL};
42

43
const char *myisam_stats_method_names[] = {"nulls_unequal", "nulls_equal",
44
                                           "nulls_ignored", NullS};
45 46 47 48
TYPELIB myisam_stats_method_typelib= {
  array_elements(myisam_stats_method_names) - 1, "",
  myisam_stats_method_names, NULL};

49
static MYSQL_SYSVAR_ULONG(block_size, opt_myisam_block_size,
Sergei Golubchik's avatar
Sergei Golubchik committed
50
  PLUGIN_VAR_READONLY | PLUGIN_VAR_RQCMDARG,
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
  "Block size to be used for MyISAM index pages", NULL, NULL,
  MI_KEY_BLOCK_LENGTH, MI_MIN_KEY_BLOCK_LENGTH, MI_MAX_KEY_BLOCK_LENGTH,
  MI_MIN_KEY_BLOCK_LENGTH);

static MYSQL_SYSVAR_ULONG(data_pointer_size, myisam_data_pointer_size,
  PLUGIN_VAR_RQCMDARG, "Default pointer size to be used for MyISAM tables",
  NULL, NULL, 6, 2, 7, 1);

#define MB (1024*1024)
static MYSQL_SYSVAR_ULONGLONG(max_sort_file_size, myisam_max_temp_length,
  PLUGIN_VAR_RQCMDARG, "Don't use the fast sort index method to created "
  "index if the temporary file would get bigger than this", NULL, NULL,
  LONG_MAX/MB*MB, 0, MAX_FILE_SIZE, MB);

static MYSQL_SYSVAR_SET(recover_options, myisam_recover_options,
  PLUGIN_VAR_OPCMDARG|PLUGIN_VAR_READONLY,
  "Syntax: myisam-recover-options[=option[,option...]], where option can be "
Sergei Golubchik's avatar
Sergei Golubchik committed
68
  "DEFAULT, BACKUP, BACKUP_ALL, FORCE, QUICK, or OFF",
69
  NULL, NULL, 1, &myisam_recover_typelib);
70 71 72 73 74 75 76 77 78 79

static MYSQL_THDVAR_ULONG(repair_threads, PLUGIN_VAR_RQCMDARG,
  "If larger than 1, when repairing a MyISAM table all indexes will be "
  "created in parallel, with one thread per index. The value of 1 "
  "disables parallel repair", NULL, NULL,
  1, 1, ULONG_MAX, 1);

static MYSQL_THDVAR_ULONG(sort_buffer_size, PLUGIN_VAR_RQCMDARG,
  "The buffer that is allocated when sorting the index when doing "
  "a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE", NULL, NULL,
80
  8192*1024, (long) (MIN_SORT_BUFFER + MALLOC_OVERHEAD), ULONG_MAX, 1);
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96

static MYSQL_SYSVAR_BOOL(use_mmap, opt_myisam_use_mmap, PLUGIN_VAR_NOCMDARG,
  "Use memory mapping for reading and writing MyISAM tables", NULL, NULL, FALSE);

static MYSQL_SYSVAR_ULONGLONG(mmap_size, myisam_mmap_size,
  PLUGIN_VAR_RQCMDARG|PLUGIN_VAR_READONLY, "Restricts the total memory "
  "used for memory mapping of MySQL tables", NULL, NULL,
  SIZE_T_MAX, MEMMAP_EXTRA_MARGIN, SIZE_T_MAX, 1);

static MYSQL_THDVAR_ENUM(stats_method, PLUGIN_VAR_RQCMDARG,
  "Specifies how MyISAM index statistics collection code should "
  "treat NULLs. Possible values of name are NULLS_UNEQUAL (default "
  "behavior for 4.1 and later), NULLS_EQUAL (emulate 4.0 behavior), "
  "and NULLS_IGNORED", NULL, NULL,
  MI_STATS_METHOD_NULLS_NOT_EQUAL, &myisam_stats_method_typelib);

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
#ifndef DBUG_OFF
/**
  Causes the thread to wait in a spin lock for a query kill signal.
  This function is used by the test frame work to identify race conditions.

  The signal is caught and ignored and the thread is not killed.
*/

static void debug_wait_for_kill(const char *info)
{
  DBUG_ENTER("debug_wait_for_kill");
  const char *prev_info;
  THD *thd;
  thd= current_thd;
  prev_info= thd_proc_info(thd, info);
  while(!thd->killed)
    my_sleep(1000);
  DBUG_PRINT("info", ("Exit debug_wait_for_kill"));
  thd_proc_info(thd, prev_info);
  DBUG_VOID_RETURN;
}
#endif
119

unknown's avatar
unknown committed
120 121 122 123
/*****************************************************************************
** MyISAM tables
*****************************************************************************/

124 125 126
static handler *myisam_create_handler(handlerton *hton,
                                      TABLE_SHARE *table, 
                                      MEM_ROOT *mem_root)
127
{
128
  return new (mem_root) ha_myisam(hton, table);
129 130
}

unknown's avatar
unknown committed
131
// collect errors printed by mi_check routines
132

133
static void mi_check_print_msg(HA_CHECK *param,	const char* msg_type,
unknown's avatar
unknown committed
134 135 136
			       const char *fmt, va_list args)
{
  THD* thd = (THD*)param->thd;
137
  Protocol *protocol= thd->protocol;
138
  size_t length, msg_length;
Sergei Golubchik's avatar
Sergei Golubchik committed
139
  char msgbuf[MYSQL_ERRMSG_SIZE];
140
  char name[NAME_LEN*2+2];
unknown's avatar
unknown committed
141

142
  msg_length= my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
unknown's avatar
unknown committed
143 144
  msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia

unknown's avatar
unknown committed
145 146
  DBUG_PRINT(msg_type,("message: %s",msgbuf));

147
  if (!thd->vio_ok())
unknown's avatar
unknown committed
148
  {
149
    sql_print_error("%s.%s: %s", param->db_name, param->table_name, msgbuf);
unknown's avatar
unknown committed
150 151
    return;
  }
152

153 154
  if (param->testflag & (T_CREATE_MISSING_KEYS | T_SAFE_REPAIR |
			 T_AUTO_REPAIR))
155
  {
156
    my_message(ER_NOT_KEYFILE, msgbuf, MYF(MY_WME));
157
    if (thd->variables.log_warnings > 2 && ! thd->log_all_errors)
158
      sql_print_error("%s.%s: %s", param->db_name, param->table_name, msgbuf);
159 160
    return;
  }
161 162
  length=(uint) (strxmov(name, param->db_name,".",param->table_name,NullS) -
		 name);
163 164 165 166 167 168 169 170
  /*
    TODO: switch from protocol to push_warning here. The main reason we didn't
    it yet is parallel repair. Due to following trace:
    mi_check_print_msg/push_warning/sql_alloc/my_pthread_getspecific_ptr.

    Also we likely need to lock mutex here (in both cases with protocol and
    push_warning).
  */
Sergey Vojtovich's avatar
Sergey Vojtovich committed
171
  if (param->need_print_msg_lock)
Marc Alff's avatar
Marc Alff committed
172
    mysql_mutex_lock(&param->print_msg_mutex);
173

174
  protocol->prepare_for_resend();
175 176 177 178
  protocol->store(name, length, system_charset_info);
  protocol->store(param->op_name, system_charset_info);
  protocol->store(msg_type, system_charset_info);
  protocol->store(msgbuf, msg_length, system_charset_info);
179
  if (protocol->write())
180 181
    sql_print_error("Failed on my_net_write, writing to stderr instead: %s\n",
		    msgbuf);
182
  else if (thd->variables.log_warnings > 2)
183
    sql_print_error("%s.%s: %s", param->db_name, param->table_name, msgbuf);
184

Sergey Vojtovich's avatar
Sergey Vojtovich committed
185
  if (param->need_print_msg_lock)
Marc Alff's avatar
Marc Alff committed
186
    mysql_mutex_unlock(&param->print_msg_mutex);
187

188
  return;
unknown's avatar
unknown committed
189 190
}

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206

/*
  Convert TABLE object to MyISAM key and column definition

  SYNOPSIS
    table2myisam()
      table_arg   in     TABLE object.
      keydef_out  out    MyISAM key definition.
      recinfo_out out    MyISAM column definition.
      records_out out    Number of fields.

  DESCRIPTION
    This function will allocate and initialize MyISAM key and column
    definition for further use in mi_create or for a check for underlying
    table conformance in merge engine.

207 208 209 210
    The caller needs to free *recinfo_out after use. Since *recinfo_out
    and *keydef_out are allocated with a my_multi_malloc, *keydef_out
    is freed automatically when *recinfo_out is freed.

211 212 213 214 215 216 217 218 219 220
  RETURN VALUE
    0  OK
    !0 error code
*/

int table2myisam(TABLE *table_arg, MI_KEYDEF **keydef_out,
                 MI_COLUMNDEF **recinfo_out, uint *records_out)
{
  uint i, j, recpos, minpos, fieldpos, temp_length, length;
  enum ha_base_keytype type= HA_KEYTYPE_BINARY;
221
  uchar *record;
222 223 224 225
  KEY *pos;
  MI_KEYDEF *keydef;
  MI_COLUMNDEF *recinfo, *recinfo_pos;
  HA_KEYSEG *keyseg;
226 227
  TABLE_SHARE *share= table_arg->s;
  uint options= share->db_options_in_use;
228 229
  DBUG_ENTER("table2myisam");
  if (!(my_multi_malloc(MYF(MY_WME),
230 231
          recinfo_out, (share->fields * 2 + 2) * sizeof(MI_COLUMNDEF),
          keydef_out, share->keys * sizeof(MI_KEYDEF),
232
          &keyseg,
233
          (share->key_parts + share->keys) * sizeof(HA_KEYSEG),
234 235 236 237 238
          NullS)))
    DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
  keydef= *keydef_out;
  recinfo= *recinfo_out;
  pos= table_arg->key_info;
239
  for (i= 0; i < share->keys; i++, pos++)
240
  {
unknown's avatar
unknown committed
241
    keydef[i].flag= ((uint16) pos->flags & (HA_NOSAME | HA_FULLTEXT | HA_SPATIAL));
242 243 244
    keydef[i].key_alg= pos->algorithm == HA_KEY_ALG_UNDEF ?
      (pos->flags & HA_SPATIAL ? HA_KEY_ALG_RTREE : HA_KEY_ALG_BTREE) :
      pos->algorithm;
245
    keydef[i].block_length= pos->block_size;
246 247 248 249 250 251
    keydef[i].seg= keyseg;
    keydef[i].keysegs= pos->key_parts;
    for (j= 0; j < pos->key_parts; j++)
    {
      Field *field= pos->key_part[j].field;
      type= field->key_type();
252
      keydef[i].seg[j].flag= pos->key_part[j].key_part_flag;
253 254 255 256 257 258 259 260 261 262 263 264 265 266

      if (options & HA_OPTION_PACK_KEYS ||
          (pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY |
                         HA_SPACE_PACK_USED)))
      {
        if (pos->key_part[j].length > 8 &&
            (type == HA_KEYTYPE_TEXT ||
             type == HA_KEYTYPE_NUM ||
             (type == HA_KEYTYPE_BINARY && !field->zero_pack())))
        {
          /* No blobs here */
          if (j == 0)
            keydef[i].flag|= HA_PACK_KEY;
          if (!(field->flags & ZEROFILL_FLAG) &&
267 268
              (field->type() == MYSQL_TYPE_STRING ||
               field->type() == MYSQL_TYPE_VAR_STRING ||
269 270 271 272 273 274 275 276 277
               ((int) (pos->key_part[j].length - field->decimals())) >= 4))
            keydef[i].seg[j].flag|= HA_SPACE_PACK;
        }
        else if (j == 0 && (!(pos->flags & HA_NOSAME) || pos->key_length > 16))
          keydef[i].flag|= HA_BINARY_PACK_KEY;
      }
      keydef[i].seg[j].type= (int) type;
      keydef[i].seg[j].start= pos->key_part[j].offset;
      keydef[i].seg[j].length= pos->key_part[j].length;
278 279 280
      keydef[i].seg[j].bit_start= keydef[i].seg[j].bit_end=
        keydef[i].seg[j].bit_length= 0;
      keydef[i].seg[j].bit_pos= 0;
281
      keydef[i].seg[j].language= field->charset_for_protocol()->number;
282 283 284 285 286 287 288 289 290 291 292 293

      if (field->null_ptr)
      {
        keydef[i].seg[j].null_bit= field->null_bit;
        keydef[i].seg[j].null_pos= (uint) (field->null_ptr-
                                           (uchar*) table_arg->record[0]);
      }
      else
      {
        keydef[i].seg[j].null_bit= 0;
        keydef[i].seg[j].null_pos= 0;
      }
294 295
      if (field->type() == MYSQL_TYPE_BLOB ||
          field->type() == MYSQL_TYPE_GEOMETRY)
296 297 298 299
      {
        keydef[i].seg[j].flag|= HA_BLOB_PART;
        /* save number of bytes used to pack length */
        keydef[i].seg[j].bit_start= (uint) (field->pack_length() -
300 301
                                            share->blob_ptr_size);
      }
302
      else if (field->type() == MYSQL_TYPE_BIT)
303 304 305 306 307
      {
        keydef[i].seg[j].bit_length= ((Field_bit *) field)->bit_len;
        keydef[i].seg[j].bit_start= ((Field_bit *) field)->bit_ofs;
        keydef[i].seg[j].bit_pos= (uint) (((Field_bit *) field)->bit_ptr -
                                          (uchar*) table_arg->record[0]);
308 309 310 311 312
      }
    }
    keyseg+= pos->key_parts;
  }
  if (table_arg->found_next_number_field)
313
    keydef[share->next_number_index].flag|= HA_AUTO_KEY;
314
  record= table_arg->record[0];
315 316
  recpos= 0;
  recinfo_pos= recinfo;
317
  while (recpos < (uint) share->stored_rec_length)
318 319
  {
    Field **field, *found= 0;
320
    minpos= share->reclength;
321 322 323 324
    length= 0;

    for (field= table_arg->field; *field; field++)
    {
325
      if ((fieldpos= (*field)->offset(record)) >= recpos &&
326 327 328
          fieldpos <= minpos)
      {
        /* skip null fields */
329
        if (!(temp_length= (*field)->pack_length_in_rec()))
330 331 332 333 334 335 336 337 338 339
          continue; /* Skip null-fields */
        if (! found || fieldpos < minpos ||
            (fieldpos == minpos && temp_length < length))
        {
          minpos= fieldpos;
          found= *field;
          length= temp_length;
        }
      }
    }
340
    DBUG_PRINT("loop", ("found: 0x%lx  recpos: %d  minpos: %d  length: %d",
341 342
                        (long) found, recpos, minpos, length));
    if (recpos != minpos)
343 344
    {
      /* reserve space for null bits */
345
      bzero((char*) recinfo_pos, sizeof(*recinfo_pos));
346
      recinfo_pos->type= FIELD_NORMAL;
347 348 349 350 351 352
      recinfo_pos++->length= (uint16) (minpos - recpos);
    }
    if (!found)
      break;

    if (found->flags & BLOB_FLAG)
353
      recinfo_pos->type= FIELD_BLOB;
354 355
    else if (found->type() == MYSQL_TYPE_TIMESTAMP)
      recinfo_pos->type= FIELD_NORMAL;
356 357
    else if (found->type() == MYSQL_TYPE_VARCHAR)
      recinfo_pos->type= FIELD_VARCHAR;
358
    else if (!(options & HA_OPTION_PACK_RECORD))
359
      recinfo_pos->type= FIELD_NORMAL;
360
    else if (found->zero_pack())
361
      recinfo_pos->type= FIELD_SKIP_ZERO;
362
    else
363 364 365 366 367 368 369
      recinfo_pos->type= ((length <= 3 ||
                           (found->flags & ZEROFILL_FLAG)) ?
                          FIELD_NORMAL :
                          found->type() == MYSQL_TYPE_STRING ||
                          found->type() == MYSQL_TYPE_VAR_STRING ?
                          FIELD_SKIP_ENDSPACE :
                          FIELD_SKIP_PRESPACE);
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
    if (found->null_ptr)
    {
      recinfo_pos->null_bit= found->null_bit;
      recinfo_pos->null_pos= (uint) (found->null_ptr -
                                     (uchar*) table_arg->record[0]);
    }
    else
    {
      recinfo_pos->null_bit= 0;
      recinfo_pos->null_pos= 0;
    }
    (recinfo_pos++)->length= (uint16) length;
    recpos= minpos + length;
    DBUG_PRINT("loop", ("length: %d  type: %d",
                        recinfo_pos[-1].length,recinfo_pos[-1].type));
  }
  *records_out= (uint) (recinfo_pos - recinfo);
  DBUG_RETURN(0);
}


/*
  Check for underlying table conformance

  SYNOPSIS
395
    myisam_check_definition()
396 397 398 399 400 401 402 403 404
      t1_keyinfo       in    First table key definition
      t1_recinfo       in    First table record definition
      t1_keys          in    Number of keys in first table
      t1_recs          in    Number of records in first table
      t2_keyinfo       in    Second table key definition
      t2_recinfo       in    Second table record definition
      t2_keys          in    Number of keys in second table
      t2_recs          in    Number of records in second table
      strict           in    Strict check switch
405
      table            in    handle to the table object
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420

  DESCRIPTION
    This function compares two MyISAM definitions. By intention it was done
    to compare merge table definition against underlying table definition.
    It may also be used to compare dot-frm and MYI definitions of MyISAM
    table as well to compare different MyISAM table definitions.

    For merge table it is not required that number of keys in merge table
    must exactly match number of keys in underlying table. When calling this
    function for underlying table conformance check, 'strict' flag must be
    set to false, and converted merge definition must be passed as t1_*.

    Otherwise 'strict' flag must be set to 1 and it is not required to pass
    converted dot-frm definition as t1_*.

421 422 423 424
    For compatibility reasons we relax some checks, specifically:
    - 4.0 (and earlier versions) always set key_alg to 0.
    - 4.0 (and earlier versions) have the same language for all keysegs.

425 426 427
  RETURN VALUE
    0 - Equal definitions.
    1 - Different definitions.
428 429 430 431 432 433

  TODO
    - compare FULLTEXT keys;
    - compare SPATIAL keys;
    - compare FIELD_SKIP_ZERO which is converted to FIELD_NORMAL correctly
      (should be corretly detected in table2myisam).
434 435 436 437 438
*/

int check_definition(MI_KEYDEF *t1_keyinfo, MI_COLUMNDEF *t1_recinfo,
                     uint t1_keys, uint t1_recs,
                     MI_KEYDEF *t2_keyinfo, MI_COLUMNDEF *t2_recinfo,
439
                     uint t2_keys, uint t2_recs, bool strict, TABLE *table_arg)
440 441 442
{
  uint i, j;
  DBUG_ENTER("check_definition");
443
  my_bool mysql_40_compat= table_arg && table_arg->s->frm_version < FRM_VER_TRUE_VARCHAR;
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
  if ((strict ? t1_keys != t2_keys : t1_keys > t2_keys))
  {
    DBUG_PRINT("error", ("Number of keys differs: t1_keys=%u, t2_keys=%u",
                         t1_keys, t2_keys));
    DBUG_RETURN(1);
  }
  if (t1_recs != t2_recs)
  {
    DBUG_PRINT("error", ("Number of recs differs: t1_recs=%u, t2_recs=%u",
                         t1_recs, t2_recs));
    DBUG_RETURN(1);
  }
  for (i= 0; i < t1_keys; i++)
  {
    HA_KEYSEG *t1_keysegs= t1_keyinfo[i].seg;
    HA_KEYSEG *t2_keysegs= t2_keyinfo[i].seg;
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
    if (t1_keyinfo[i].flag & HA_FULLTEXT && t2_keyinfo[i].flag & HA_FULLTEXT)
      continue;
    else if (t1_keyinfo[i].flag & HA_FULLTEXT ||
             t2_keyinfo[i].flag & HA_FULLTEXT)
    {
       DBUG_PRINT("error", ("Key %d has different definition", i));
       DBUG_PRINT("error", ("t1_fulltext= %d, t2_fulltext=%d",
                            test(t1_keyinfo[i].flag & HA_FULLTEXT),
                            test(t2_keyinfo[i].flag & HA_FULLTEXT)));
       DBUG_RETURN(1);
    }
    if (t1_keyinfo[i].flag & HA_SPATIAL && t2_keyinfo[i].flag & HA_SPATIAL)
      continue;
    else if (t1_keyinfo[i].flag & HA_SPATIAL ||
             t2_keyinfo[i].flag & HA_SPATIAL)
    {
       DBUG_PRINT("error", ("Key %d has different definition", i));
       DBUG_PRINT("error", ("t1_spatial= %d, t2_spatial=%d",
                            test(t1_keyinfo[i].flag & HA_SPATIAL),
                            test(t2_keyinfo[i].flag & HA_SPATIAL)));
       DBUG_RETURN(1);
    }
482
    if ((!mysql_40_compat &&
483 484
        t1_keyinfo[i].key_alg != t2_keyinfo[i].key_alg) ||
        t1_keyinfo[i].keysegs != t2_keyinfo[i].keysegs)
485 486 487 488 489 490 491 492 493 494
    {
      DBUG_PRINT("error", ("Key %d has different definition", i));
      DBUG_PRINT("error", ("t1_keysegs=%d, t1_key_alg=%d",
                           t1_keyinfo[i].keysegs, t1_keyinfo[i].key_alg));
      DBUG_PRINT("error", ("t2_keysegs=%d, t2_key_alg=%d",
                           t2_keyinfo[i].keysegs, t2_keyinfo[i].key_alg));
      DBUG_RETURN(1);
    }
    for (j=  t1_keyinfo[i].keysegs; j--;)
    {
495 496 497 498 499 500 501 502 503 504 505 506 507
      uint8 t1_keysegs_j__type= t1_keysegs[j].type;

      /*
        Table migration from 4.1 to 5.1. In 5.1 a *TEXT key part is
        always HA_KEYTYPE_VARTEXT2. In 4.1 we had only the equivalent of
        HA_KEYTYPE_VARTEXT1. Since we treat both the same on MyISAM
        level, we can ignore a mismatch between these types.
      */
      if ((t1_keysegs[j].flag & HA_BLOB_PART) &&
          (t2_keysegs[j].flag & HA_BLOB_PART))
      {
        if ((t1_keysegs_j__type == HA_KEYTYPE_VARTEXT2) &&
            (t2_keysegs[j].type == HA_KEYTYPE_VARTEXT1))
508
          t1_keysegs_j__type= HA_KEYTYPE_VARTEXT1; /* purecov: tested */
509 510
        else if ((t1_keysegs_j__type == HA_KEYTYPE_VARBINARY2) &&
                 (t2_keysegs[j].type == HA_KEYTYPE_VARBINARY1))
511
          t1_keysegs_j__type= HA_KEYTYPE_VARBINARY1; /* purecov: inspected */
512 513
      }

514
      if ((!mysql_40_compat &&
515 516
          t1_keysegs[j].language != t2_keysegs[j].language) ||
          t1_keysegs_j__type != t2_keysegs[j].type ||
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
          t1_keysegs[j].null_bit != t2_keysegs[j].null_bit ||
          t1_keysegs[j].length != t2_keysegs[j].length)
      {
        DBUG_PRINT("error", ("Key segment %d (key %d) has different "
                             "definition", j, i));
        DBUG_PRINT("error", ("t1_type=%d, t1_language=%d, t1_null_bit=%d, "
                             "t1_length=%d",
                             t1_keysegs[j].type, t1_keysegs[j].language,
                             t1_keysegs[j].null_bit, t1_keysegs[j].length));
        DBUG_PRINT("error", ("t2_type=%d, t2_language=%d, t2_null_bit=%d, "
                             "t2_length=%d",
                             t2_keysegs[j].type, t2_keysegs[j].language,
                             t2_keysegs[j].null_bit, t2_keysegs[j].length));

        DBUG_RETURN(1);
      }
    }
  }
  for (i= 0; i < t1_recs; i++)
  {
    MI_COLUMNDEF *t1_rec= &t1_recinfo[i];
    MI_COLUMNDEF *t2_rec= &t2_recinfo[i];
539 540 541 542 543 544 545 546
    /*
      FIELD_SKIP_ZERO can be changed to FIELD_NORMAL in mi_create,
      see NOTE1 in mi_create.c
    */
    if ((t1_rec->type != t2_rec->type &&
         !(t1_rec->type == (int) FIELD_SKIP_ZERO &&
           t1_rec->length == 1 &&
           t2_rec->type == (int) FIELD_NORMAL)) ||
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
        t1_rec->length != t2_rec->length ||
        t1_rec->null_bit != t2_rec->null_bit)
    {
      DBUG_PRINT("error", ("Field %d has different definition", i));
      DBUG_PRINT("error", ("t1_type=%d, t1_length=%d, t1_null_bit=%d",
                           t1_rec->type, t1_rec->length, t1_rec->null_bit));
      DBUG_PRINT("error", ("t2_type=%d, t2_length=%d, t2_null_bit=%d",
                           t2_rec->type, t2_rec->length, t2_rec->null_bit));
      DBUG_RETURN(1);
    }
  }
  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
562 563
extern "C" {

564
int killed_ptr(HA_CHECK *param)
unknown's avatar
unknown committed
565
{
566
  return thd_killed((THD*)param->thd);
unknown's avatar
unknown committed
567 568
}

569
void mi_check_print_error(HA_CHECK *param, const char *fmt,...)
unknown's avatar
unknown committed
570 571
{
  param->error_printed|=1;
572
  param->out_flag|= O_DATA_LOST;
unknown's avatar
unknown committed
573 574 575 576 577 578
  va_list args;
  va_start(args, fmt);
  mi_check_print_msg(param, "error", fmt, args);
  va_end(args);
}

579
void mi_check_print_info(HA_CHECK *param, const char *fmt,...)
unknown's avatar
unknown committed
580 581 582 583
{
  va_list args;
  va_start(args, fmt);
  mi_check_print_msg(param, "info", fmt, args);
584
  param->note_printed= 1;
unknown's avatar
unknown committed
585 586 587
  va_end(args);
}

588
void mi_check_print_warning(HA_CHECK *param, const char *fmt,...)
unknown's avatar
unknown committed
589 590
{
  param->warning_printed=1;
591
  param->out_flag|= O_DATA_LOST;
unknown's avatar
unknown committed
592 593 594 595 596 597
  va_list args;
  va_start(args, fmt);
  mi_check_print_msg(param, "warning", fmt, args);
  va_end(args);
}

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619

/**
  Report list of threads (and queries) accessing a table, thread_id of a
  thread that detected corruption, ource file name and line number where
  this corruption was detected, optional extra information (string).

  This function is intended to be used when table corruption is detected.

  @param[in] file      MI_INFO object.
  @param[in] message   Optional error message.
  @param[in] sfile     Name of source file.
  @param[in] sline     Line number in source file.

  @return void
*/

void _mi_report_crashed(MI_INFO *file, const char *message,
                        const char *sfile, uint sline)
{
  THD *cur_thd;
  LIST *element;
  char buf[1024];
620
  mysql_mutex_lock(&file->s->intern_lock);
621 622 623 624 625 626 627 628 629 630 631 632 633
  if ((cur_thd= (THD*) file->in_use.data))
    sql_print_error("Got an error from thread_id=%lu, %s:%d", cur_thd->thread_id,
                    sfile, sline);
  else
    sql_print_error("Got an error from unknown thread, %s:%d", sfile, sline);
  if (message)
    sql_print_error("%s", message);
  for (element= file->s->in_use; element; element= list_rest(element))
  {
    THD *thd= (THD*) element->data;
    sql_print_error("%s", thd ? thd_security_context(thd, buf, sizeof(buf), 0)
                              : "Unknown thread accessing table");
  }
634
  mysql_mutex_unlock(&file->s->intern_lock);
635 636
}

637 638 639 640 641 642 643
/* Return 1 if user have requested query to be killed */

my_bool mi_killed_in_mariadb(MI_INFO *info)
{
  return (((TABLE*) (info->external_ref))->in_use->killed != 0);
}

unknown's avatar
unknown committed
644 645
}

646 647
ha_myisam::ha_myisam(handlerton *hton, TABLE_SHARE *table_arg)
  :handler(hton, table_arg), file(0),
648
  int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER |
649
                  HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
Sergei Golubchik's avatar
Sergei Golubchik committed
650
                  HA_CAN_VIRTUAL_COLUMNS |
651 652 653
                  HA_DUPLICATE_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY |
                  HA_FILE_BASED | HA_CAN_GEOMETRY | HA_NO_TRANSACTIONS |
                  HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS |
654
                  HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT | HA_CAN_REPAIR),
655
   can_enable_indexes(1)
656 657
{}

658
handler *ha_myisam::clone(const char *name, MEM_ROOT *mem_root)
unknown's avatar
unknown committed
659
{
660 661
  ha_myisam *new_handler= static_cast <ha_myisam *>(handler::clone(name,
                                                                   mem_root));
unknown's avatar
unknown committed
662 663 664 665 666
  if (new_handler)
    new_handler->file->state= file->state;
  return new_handler;
}

667

unknown's avatar
unknown committed
668 669 670 671 672 673
static const char *ha_myisam_exts[] = {
  ".MYI",
  ".MYD",
  NullS
};

unknown's avatar
unknown committed
674
const char **ha_myisam::bas_ext() const
unknown's avatar
unknown committed
675 676 677
{
  return ha_myisam_exts;
}
unknown's avatar
unknown committed
678 679


680 681
const char *ha_myisam::index_type(uint key_number)
{
682
  return ((table->key_info[key_number].flags & HA_FULLTEXT) ? 
683
	  "FULLTEXT" :
684 685 686 687
	  (table->key_info[key_number].flags & HA_SPATIAL) ?
	  "SPATIAL" :
	  (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ?
	  "RTREE" :
688 689 690
	  "BTREE");
}

691

692
/* Name is here without an extension */
693
int ha_myisam::open(const char *name, int mode, uint test_if_locked)
unknown's avatar
unknown committed
694
{
695 696 697
  MI_KEYDEF *keyinfo;
  MI_COLUMNDEF *recinfo= 0;
  uint recs;
698
  uint i;
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717

  /*
    If the user wants to have memory mapped data files, add an
    open_flag. Do not memory map temporary tables because they are
    expected to be inserted and thus extended a lot. Memory mapping is
    efficient for files that keep their size, but very inefficient for
    growing files. Using an open_flag instead of calling mi_extra(...
    HA_EXTRA_MMAP ...) after mi_open() has the advantage that the
    mapping is not repeated for every open, but just done on the initial
    open, when the MyISAM share is created. Everytime the server
    requires to open a new instance of a table it calls this method. We
    will always supply HA_OPEN_MMAP for a permanent table. However, the
    MyISAM storage engine will ignore this flag if this is a secondary
    open of a table that is in use by other threads already (if the
    MyISAM share exists already).
  */
  if (!(test_if_locked & HA_OPEN_TMP_TABLE) && opt_myisam_use_mmap)
    test_if_locked|= HA_OPEN_MMAP;

718
  if (!(file=mi_open(name, mode, test_if_locked | HA_OPEN_FROM_SQL_LAYER)))
unknown's avatar
unknown committed
719
    return (my_errno ? my_errno : -1);
720 721

  file->s->chst_invalidator= query_cache_invalidate_by_MyISAM_filename_ref;
722 723
  /* Set external_ref, mainly for temporary tables */
  file->external_ref= (void*) table;            // For mi_killed()
724

725 726 727 728 729 730 731 732 733 734 735 736
  if (!table->s->tmp_table) /* No need to perform a check for tmp table */
  {
    if ((my_errno= table2myisam(table, &keyinfo, &recinfo, &recs)))
    {
      /* purecov: begin inspected */
      DBUG_PRINT("error", ("Failed to convert TABLE object to MyISAM "
                           "key and column definition"));
      goto err;
      /* purecov: end */
    }
    if (check_definition(keyinfo, recinfo, table->s->keys, recs,
                         file->s->keyinfo, file->s->rec,
737 738
                         file->s->base.keys, file->s->base.fields,
                         true, table))
739 740 741 742 743 744 745
    {
      /* purecov: begin inspected */
      my_errno= HA_ERR_CRASHED;
      goto err;
      /* purecov: end */
    }
  }
unknown's avatar
unknown committed
746
  
747
  if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE))
Konstantin Osipov's avatar
Konstantin Osipov committed
748
    (void) mi_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0);
unknown's avatar
unknown committed
749

unknown's avatar
unknown committed
750 751
  info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
  if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
Konstantin Osipov's avatar
Konstantin Osipov committed
752
    (void) mi_extra(file, HA_EXTRA_WAIT_LOCK, 0);
753
  if (!table->s->db_record_offset)
754
    int_table_flags|=HA_REC_NOT_IN_SEQ;
unknown's avatar
unknown committed
755
  if (file->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
756 757 758 759 760 761 762 763 764 765 766 767 768
  {
    /*
      Set which type of automatic checksum we have
      The old checksum and new checksum are identical if there is no
      null fields.
      Files with new checksum has the HA_OPTION_NULL_FIELDS bit set.
    */      
    if ((file->s->options & HA_OPTION_NULL_FIELDS) ||
        !file->s->has_null_fields)
      int_table_flags|= HA_HAS_NEW_CHECKSUM;
    if (!(file->s->options & HA_OPTION_NULL_FIELDS))
      int_table_flags|= HA_HAS_OLD_CHECKSUM;
  }
769

770 771 772 773 774 775 776 777 778 779
  /*
    For static size rows, tell MariaDB that we will access all bytes
    in the record when writing it.  This signals MariaDB to initalize
    the full row to ensure we don't get any errors from valgrind and
    that all bytes in the row is properly reset.
  */
  if ((file->s->options & HA_OPTION_PACK_RECORD) &&
      (file->s->has_varchar_fields | file->s->has_null_fields))
    int_table_flags|= HA_RECORD_MUST_BE_CLEAN_ON_WRITE;

780 781
  for (i= 0; i < table->s->keys; i++)
  {
unknown's avatar
unknown committed
782
    plugin_ref parser= table->key_info[i].parser;
783 784
    if (table->key_info[i].flags & HA_USES_PARSER)
      file->s->keyinfo[i].parser=
unknown's avatar
unknown committed
785
        (struct st_mysql_ftparser *)plugin_decl(parser)->info;
786
    table->key_info[i].block_size= file->s->keyinfo[i].block_length;
787
  }
unknown's avatar
unknown committed
788 789 790
  my_errno= 0;
  goto end;
 err:
791
  this->close();
unknown's avatar
unknown committed
792
 end:
793 794 795 796 797
  /*
    Both recinfo and keydef are allocated by my_multi_malloc(), thus only
    recinfo must be freed.
  */
  if (recinfo)
798
    my_free(recinfo);
799
  return my_errno;
unknown's avatar
unknown committed
800 801 802 803 804
}

int ha_myisam::close(void)
{
  MI_INFO *tmp=file;
unknown's avatar
unknown committed
805 806
  if (!tmp)
    return 0;
unknown's avatar
unknown committed
807 808 809 810
  file=0;
  return mi_close(tmp);
}

811
int ha_myisam::write_row(uchar *buf)
unknown's avatar
unknown committed
812
{
unknown's avatar
unknown committed
813
  /* If we have a timestamp column, update it to the current time */
814 815
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
    table->timestamp_field->set_time();
unknown's avatar
unknown committed
816 817 818 819 820

  /*
    If we have an auto_increment column and we are writing a changed row
    or a new row, then update the auto_increment value in the record.
  */
unknown's avatar
unknown committed
821
  if (table->next_number_field && buf == table->record[0])
unknown's avatar
unknown committed
822 823 824 825 826
  {
    int error;
    if ((error= update_auto_increment()))
      return error;
  }
unknown's avatar
unknown committed
827 828 829 830 831
  return mi_write(file,buf);
}

int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
{
832
  if (!file) return HA_ADMIN_INTERNAL_ERROR;
unknown's avatar
unknown committed
833
  int error;
834
  HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
unknown's avatar
unknown committed
835
  MYISAM_SHARE* share = file->s;
unknown's avatar
unknown committed
836
  const char *old_proc_info=thd->proc_info;
837

838 839 840
  if (!&param)
    return HA_ADMIN_INTERNAL_ERROR;

841
  thd_proc_info(thd, "Checking table");
unknown's avatar
unknown committed
842 843
  myisamchk_init(&param);
  param.thd = thd;
844
  param.op_name =   "check";
unknown's avatar
unknown committed
845
  param.db_name=    table->s->db.str;
846
  param.table_name= table->alias.c_ptr();
847
  param.testflag = check_opt->flags | T_CHECK | T_SILENT;
Sergei Golubchik's avatar
Sergei Golubchik committed
848
  param.stats_method= (enum_handler_stats_method)THDVAR(thd, stats_method);
849

unknown's avatar
unknown committed
850 851 852 853
  if (!(table->db_stat & HA_READ_ONLY))
    param.testflag|= T_STATISTICS;
  param.using_global_keycache = 1;

854 855
  if (!mi_is_crashed(file) &&
      (((param.testflag & T_CHECK_ONLY_CHANGED) &&
856 857
	!(share->state.changed & (STATE_CHANGED | STATE_CRASHED |
				  STATE_CRASHED_ON_REPAIR)) &&
858
	share->state.open_count == 0) ||
859
       ((param.testflag & T_FAST) && (share->state.open_count ==
860
				      (uint) (share->global_changed ? 1 : 0)))))
861
    return HA_ADMIN_ALREADY_DONE;
862

863
  error = chk_status(&param, file);		// Not fatal
unknown's avatar
unknown committed
864
  error = chk_size(&param, file);
865 866 867 868 869
  if (!error)
    error |= chk_del(&param, file, param.testflag);
  if (!error)
    error = chk_key(&param, file);
  if (!error)
unknown's avatar
unknown committed
870
  {
unknown's avatar
unknown committed
871
    if ((!(param.testflag & T_QUICK) &&
872 873 874
	 ((share->options &
	   (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
	  (param.testflag & (T_EXTEND | T_MEDIUM)))) ||
875
	mi_is_crashed(file))
unknown's avatar
unknown committed
876
    {
877
      ulonglong old_testflag= param.testflag;
878
      param.testflag|=T_MEDIUM;
879 880 881 882
      if (!(error= init_io_cache(&param.read_cache, file->dfile,
                                 my_default_record_cache_size, READ_CACHE,
                                 share->pack.header_length, 1, MYF(MY_WME))))
      {
883
        error= chk_data_link(&param, file, test(param.testflag & T_EXTEND));
884 885 886
        end_io_cache(&(param.read_cache));
      }
      param.testflag= old_testflag;
unknown's avatar
unknown committed
887 888 889
    }
  }
  if (!error)
890
  {
unknown's avatar
unknown committed
891
    if ((share->state.changed & (STATE_CHANGED |
892 893
				 STATE_CRASHED_ON_REPAIR |
				 STATE_CRASHED | STATE_NOT_ANALYZED)) ||
894 895
	(param.testflag & T_STATISTICS) ||
	mi_is_crashed(file))
unknown's avatar
unknown committed
896 897
    {
      file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
Marc Alff's avatar
Marc Alff committed
898
      mysql_mutex_lock(&share->intern_lock);
899 900
      share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
			       STATE_CRASHED_ON_REPAIR);
unknown's avatar
unknown committed
901
      if (!(table->db_stat & HA_READ_ONLY))
902 903
	error=update_state_info(&param,file,UPDATE_TIME | UPDATE_OPEN_COUNT |
				UPDATE_STAT);
Marc Alff's avatar
Marc Alff committed
904
      mysql_mutex_unlock(&share->intern_lock);
905 906
      info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
	   HA_STATUS_CONST);
unknown's avatar
unknown committed
907 908
    }
  }
909
  else if (!mi_is_crashed(file) && !thd->killed)
unknown's avatar
unknown committed
910 911 912 913
  {
    mi_mark_crashed(file);
    file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
  }
914

915
  thd_proc_info(thd, old_proc_info);
916
  return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
unknown's avatar
unknown committed
917 918 919 920 921 922 923 924 925
}


/*
  analyze the key distribution in the table
  As the table may be only locked for read, we have to take into account that
  two threads may do an analyze at the same time!
*/

926
int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt)
unknown's avatar
unknown committed
927
{
928
  int error=0;
929
  HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
unknown's avatar
unknown committed
930
  MYISAM_SHARE* share = file->s;
931

932 933 934
  if (!&param)
    return HA_ADMIN_INTERNAL_ERROR;

unknown's avatar
unknown committed
935 936
  myisamchk_init(&param);
  param.thd = thd;
937
  param.op_name=    "analyze";
unknown's avatar
unknown committed
938
  param.db_name=    table->s->db.str;
939
  param.table_name= table->alias.c_ptr();
940 941
  param.testflag= (T_FAST | T_CHECK | T_SILENT | T_STATISTICS |
                   T_DONT_CHECK_CHECKSUM);
unknown's avatar
unknown committed
942
  param.using_global_keycache = 1;
Sergei Golubchik's avatar
Sergei Golubchik committed
943
  param.stats_method= (enum_handler_stats_method)THDVAR(thd, stats_method);
unknown's avatar
unknown committed
944

945 946 947 948 949
  if (!(share->state.changed & STATE_NOT_ANALYZED))
    return HA_ADMIN_ALREADY_DONE;

  error = chk_key(&param, file);
  if (!error)
950
  {
Marc Alff's avatar
Marc Alff committed
951
    mysql_mutex_lock(&share->intern_lock);
952
    error=update_state_info(&param,file,UPDATE_STAT);
Marc Alff's avatar
Marc Alff committed
953
    mysql_mutex_unlock(&share->intern_lock);
unknown's avatar
unknown committed
954
  }
955
  else if (!mi_is_crashed(file) && !thd->killed)
956 957
    mi_mark_crashed(file);
  return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
unknown's avatar
unknown committed
958 959
}

960

961
int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
962
{
963
  int error;
964
  HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
965
  ha_rows start_records;
966

967
  if (!file || !&param) return HA_ADMIN_INTERNAL_ERROR;
unknown's avatar
unknown committed
968

unknown's avatar
unknown committed
969 970
  myisamchk_init(&param);
  param.thd = thd;
971 972 973 974
  param.op_name=  "repair";
  param.testflag= ((check_opt->flags & ~(T_EXTEND)) |
                   T_SILENT | T_FORCE_CREATE | T_CALC_CHECKSUM |
                   (check_opt->flags & T_EXTEND ? T_REP : T_REP_BY_SORT));
975
  param.sort_buffer_length=  THDVAR(thd, sort_buffer_size);
976
  param.backup_time= check_opt->start_time;
977 978
  start_records=file->state->records;
  while ((error=repair(thd,param,0)) && param.retry_repair)
979
  {
980
    param.retry_repair=0;
981 982
    if (test_all_bits(param.testflag,
		      (uint) (T_RETRY_WITHOUT_QUICK | T_QUICK)))
983
    {
984 985 986
      param.testflag&= ~(T_RETRY_WITHOUT_QUICK | T_QUICK);
      /* Ensure we don't loose any rows when retrying without quick */
      param.testflag|= T_SAFE_REPAIR;
987
      sql_print_information("Retrying repair of: '%s' including modifying data file",
988
                            table->s->path.str);
989 990
      continue;
    }
unknown's avatar
unknown committed
991
    param.testflag&= ~T_QUICK;
992 993
    if ((param.testflag & T_REP_BY_SORT))
    {
unknown's avatar
unknown committed
994
      param.testflag= (param.testflag & ~T_REP_BY_SORT) | T_REP;
unknown's avatar
unknown committed
995
      sql_print_information("Retrying repair of: '%s' with keycache",
996
                            table->s->path.str);
997 998 999 1000
      continue;
    }
    break;
  }
unknown's avatar
unknown committed
1001 1002
  if (!error && start_records != file->state->records &&
      !(check_opt->flags & T_VERY_SILENT))
1003 1004
  {
    char llbuff[22],llbuff2[22];
unknown's avatar
unknown committed
1005 1006 1007
    sql_print_information("Found %s of %s rows when repairing '%s'",
                          llstr(file->state->records, llbuff),
                          llstr(start_records, llbuff2),
1008
                          table->s->path.str);
1009
  }
1010
  return error;
1011 1012 1013 1014
}

int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt)
{
1015
  int error;
1016 1017 1018
  HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));

  if (!file || !&param) return HA_ADMIN_INTERNAL_ERROR;
1019 1020 1021

  myisamchk_init(&param);
  param.thd = thd;
1022 1023 1024
  param.op_name= "optimize";
  param.testflag= (check_opt->flags | T_SILENT | T_FORCE_CREATE |
                   T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX);
1025
  param.sort_buffer_length=  THDVAR(thd, sort_buffer_size);
1026 1027
  if ((error= repair(thd,param,1)) && param.retry_repair)
  {
1028 1029
    sql_print_warning("Warning: Optimize table got errno %d on %s.%s, retrying",
                      my_errno, param.db_name, param.table_name);
1030 1031 1032 1033
    param.testflag&= ~T_REP_BY_SORT;
    error= repair(thd,param,1);
  }
  return error;
1034 1035 1036
}


1037
int ha_myisam::repair(THD *thd, HA_CHECK &param, bool do_optimize)
1038
{
1039
  int error=0;
1040
  ulonglong local_testflag= param.testflag;
1041
  bool optimize_done= !do_optimize, statistics_done=0;
1042
  const char *old_proc_info=thd->proc_info;
1043
  char fixed_name[FN_REFLEN];
1044
  MYISAM_SHARE* share = file->s;
unknown's avatar
unknown committed
1045
  ha_rows rows= file->state->records;
unknown's avatar
unknown committed
1046
  DBUG_ENTER("ha_myisam::repair");
1047

unknown's avatar
unknown committed
1048
  param.db_name=    table->s->db.str;
1049
  param.table_name= table->alias.c_ptr();
unknown's avatar
unknown committed
1050 1051
  param.tmpfile_createflag = O_RDWR | O_TRUNC;
  param.using_global_keycache = 1;
1052 1053 1054
  param.thd= thd;
  param.tmpdir= &mysql_tmpdir_list;
  param.out_flag= 0;
1055
  strmov(fixed_name,file->filename);
1056

1057 1058 1059
  // Release latches since this can take a long time
  ha_release_temporary_latches(thd);

unknown's avatar
unknown committed
1060
  // Don't lock tables if we have used LOCK TABLE
Konstantin Osipov's avatar
Konstantin Osipov committed
1061
  if (! thd->locked_tables_mode &&
1062
      mi_lock_database(file, table->s->tmp_table ? F_EXTRA_LCK : F_WRLCK))
unknown's avatar
unknown committed
1063 1064 1065 1066 1067
  {
    mi_check_print_error(&param,ER(ER_CANT_LOCK),my_errno);
    DBUG_RETURN(HA_ADMIN_FAILED);
  }

1068
  if (!do_optimize ||
1069
      ((file->state->del || share->state.split != file->state->records) &&
unknown's avatar
unknown committed
1070
       (!(param.testflag & T_QUICK) ||
1071
	!(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))))
1072
  {
unknown's avatar
unknown committed
1073
    ulonglong key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ?
1074
			mi_get_mask_all_keys_active(share->base.keys) :
1075
			share->state.key_map);
1076
    ulonglong testflag= param.testflag;
1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088
#ifdef HAVE_MMAP
    bool remap= test(share->file_map);
    /*
      mi_repair*() functions family use file I/O even if memory
      mapping is available.

      Since mixing mmap I/O and file I/O may cause various artifacts,
      memory mapping must be disabled.
    */
    if (remap)
      mi_munmap_file(file);
#endif
1089
    if (mi_test_if_sort_rep(file,file->state->records,key_map,0) &&
1090
	(local_testflag & T_REP_BY_SORT))
1091
    {
1092
      local_testflag|= T_STATISTICS;
1093
      param.testflag|= T_STATISTICS;		// We get this for free
1094
      statistics_done=1;
1095
      if (THDVAR(thd, repair_threads)>1)
1096 1097 1098 1099
      {
        char buf[40];
        /* TODO: respect myisam_repair_threads variable */
        my_snprintf(buf, 40, "Repair with %d threads", my_count_bits(key_map));
1100
        thd_proc_info(thd, buf);
1101
        error = mi_repair_parallel(&param, file, fixed_name,
1102
                                   test(param.testflag & T_QUICK));
1103
        thd_proc_info(thd, "Repair done"); // to reset proc_info, as
1104 1105 1106 1107
                                      // it was pointing to local buffer
      }
      else
      {
1108
        thd_proc_info(thd, "Repair by sorting");
1109
        error = mi_repair_by_sort(&param, file, fixed_name,
1110
                                  test(param.testflag & T_QUICK));
1111
      }
1112 1113 1114
    }
    else
    {
1115
      thd_proc_info(thd, "Repair with keycache");
1116
      param.testflag &= ~T_REP_BY_SORT;
unknown's avatar
unknown committed
1117
      error=  mi_repair(&param, file, fixed_name,
1118
			test(param.testflag & T_QUICK));
1119
    }
1120
    param.testflag= testflag | (param.testflag & T_RETRY_WITHOUT_QUICK);
1121 1122 1123 1124
#ifdef HAVE_MMAP
    if (remap)
      mi_dynmap_file(file, file->state->data_file_length);
#endif
1125
    optimize_done=1;
1126 1127 1128
  }
  if (!error)
  {
1129
    if ((local_testflag & T_SORT_INDEX) &&
1130 1131 1132
	(share->state.changed & STATE_NOT_SORTED_PAGES))
    {
      optimize_done=1;
1133
      thd_proc_info(thd, "Sorting index");
1134 1135
      error=mi_sort_index(&param,file,fixed_name);
    }
1136
    if (!statistics_done && (local_testflag & T_STATISTICS))
1137
    {
1138 1139 1140
      if (share->state.changed & STATE_NOT_ANALYZED)
      {
	optimize_done=1;
1141
	thd_proc_info(thd, "Analyzing");
1142 1143 1144 1145
	error = chk_key(&param, file);
      }
      else
	local_testflag&= ~T_STATISTICS;		// Don't update statistics
1146 1147
    }
  }
1148
  thd_proc_info(thd, "Saving state");
unknown's avatar
unknown committed
1149
  if (!error)
1150
  {
1151
    if ((share->state.changed & STATE_CHANGED) || mi_is_crashed(file))
unknown's avatar
unknown committed
1152
    {
1153 1154
      share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
			       STATE_CRASHED_ON_REPAIR);
unknown's avatar
unknown committed
1155 1156
      file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
    }
unknown's avatar
unknown committed
1157 1158 1159 1160
    /*
      the following 'if', thought conceptually wrong,
      is a useful optimization nevertheless.
    */
unknown's avatar
unknown committed
1161
    if (file->state != &file->s->state.state)
unknown's avatar
unknown committed
1162
      file->s->state.state = *file->state;
unknown's avatar
unknown committed
1163 1164
    if (file->s->base.auto_key)
      update_auto_increment_key(&param, file, 1);
1165 1166 1167 1168 1169
    if (optimize_done)
      error = update_state_info(&param, file,
				UPDATE_TIME | UPDATE_OPEN_COUNT |
				(local_testflag &
				 T_STATISTICS ? UPDATE_STAT : 0));
1170 1171
    info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
	 HA_STATUS_CONST);
1172
    if (rows != file->state->records && ! (param.testflag & T_VERY_SILENT))
unknown's avatar
unknown committed
1173 1174 1175 1176 1177 1178
    {
      char llbuff[22],llbuff2[22];
      mi_check_print_warning(&param,"Number of rows changed from %s to %s",
			     llstr(rows,llbuff),
			     llstr(file->state->records,llbuff2));
    }
unknown's avatar
unknown committed
1179
  }
unknown's avatar
unknown committed
1180
  else
unknown's avatar
unknown committed
1181
  {
unknown's avatar
unknown committed
1182
    mi_mark_crashed_on_repair(file);
unknown's avatar
unknown committed
1183
    file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
unknown's avatar
unknown committed
1184
    update_state_info(&param, file, 0);
unknown's avatar
unknown committed
1185
  }
1186
  thd_proc_info(thd, old_proc_info);
Konstantin Osipov's avatar
Konstantin Osipov committed
1187
  if (! thd->locked_tables_mode)
unknown's avatar
unknown committed
1188
    mi_lock_database(file,F_UNLCK);
unknown's avatar
unknown committed
1189 1190
  DBUG_RETURN(error ? HA_ADMIN_FAILED :
	      !optimize_done ? HA_ADMIN_ALREADY_DONE : HA_ADMIN_OK);
unknown's avatar
unknown committed
1191 1192 1193
}


unknown's avatar
unknown committed
1194
/*
1195
  Assign table indexes to a specific key cache.
unknown's avatar
unknown committed
1196 1197 1198 1199
*/

int ha_myisam::assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt)
{
unknown's avatar
unknown committed
1200
  KEY_CACHE *new_key_cache= check_opt->key_cache;
1201
  const char *errmsg= 0;
unknown's avatar
unknown committed
1202
  int error= HA_ADMIN_OK;
1203
  ulonglong map;
unknown's avatar
unknown committed
1204 1205 1206
  TABLE_LIST *table_list= table->pos_in_table_list;
  DBUG_ENTER("ha_myisam::assign_to_keycache");

1207 1208 1209
  table->keys_in_use_for_query.clear_all();

  if (table_list->process_index_hints(table))
1210
    DBUG_RETURN(HA_ADMIN_FAILED);
1211 1212 1213 1214
  map= ~(ulonglong) 0;
  if (!table->keys_in_use_for_query.is_clear_all())
    /* use all keys if there's no list specified by the user through hints */
    map= table->keys_in_use_for_query.to_ulonglong();
unknown's avatar
unknown committed
1215

1216
  if ((error= mi_assign_to_key_cache(file, map, new_key_cache)))
unknown's avatar
unknown committed
1217
  { 
unknown's avatar
unknown committed
1218
    char buf[STRING_BUFFER_USUAL_SIZE];
unknown's avatar
unknown committed
1219 1220 1221
    my_snprintf(buf, sizeof(buf),
		"Failed to flush to index file (errno: %d)", error);
    errmsg= buf;
unknown's avatar
unknown committed
1222
    error= HA_ADMIN_CORRUPT;
unknown's avatar
unknown committed
1223
  }
unknown's avatar
unknown committed
1224

1225
  if (error != HA_ADMIN_OK)
unknown's avatar
unknown committed
1226
  {
1227
    /* Send error to user */
1228 1229 1230 1231
    HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
    if (!&param)
      return HA_ADMIN_INTERNAL_ERROR;

unknown's avatar
unknown committed
1232 1233
    myisamchk_init(&param);
    param.thd= thd;
1234
    param.op_name=    "assign_to_keycache";
unknown's avatar
unknown committed
1235 1236
    param.db_name=    table->s->db.str;
    param.table_name= table->s->table_name.str;
unknown's avatar
unknown committed
1237 1238 1239
    param.testflag= 0;
    mi_check_print_error(&param, errmsg);
  }
unknown's avatar
unknown committed
1240
  DBUG_RETURN(error);
unknown's avatar
unknown committed
1241 1242 1243
}


unknown's avatar
unknown committed
1244 1245 1246 1247 1248 1249 1250 1251
/*
  Preload pages of the index file for a table into the key cache.
*/

int ha_myisam::preload_keys(THD* thd, HA_CHECK_OPT *check_opt)
{
  int error;
  const char *errmsg;
1252
  ulonglong map;
unknown's avatar
unknown committed
1253 1254
  TABLE_LIST *table_list= table->pos_in_table_list;
  my_bool ignore_leaves= table_list->ignore_leaves;
1255
  char buf[MYSQL_ERRMSG_SIZE];
unknown's avatar
unknown committed
1256 1257 1258

  DBUG_ENTER("ha_myisam::preload_keys");

1259 1260 1261
  table->keys_in_use_for_query.clear_all();

  if (table_list->process_index_hints(table))
1262
    DBUG_RETURN(HA_ADMIN_FAILED);
1263

1264 1265 1266 1267 1268 1269
  map= ~(ulonglong) 0;
  /* Check validity of the index references */
  if (!table->keys_in_use_for_query.is_clear_all())
    /* use all keys if there's no list specified by the user through hints */
    map= table->keys_in_use_for_query.to_ulonglong();

unknown's avatar
unknown committed
1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
  mi_extra(file, HA_EXTRA_PRELOAD_BUFFER_SIZE,
           (void *) &thd->variables.preload_buff_size);

  if ((error= mi_preload(file, map, ignore_leaves)))
  {
    switch (error) {
    case HA_ERR_NON_UNIQUE_BLOCK_SIZE:
      errmsg= "Indexes use different block sizes";
      break;
    case HA_ERR_OUT_OF_MEM:
      errmsg= "Failed to allocate buffer";
      break;
1282
    default:
1283
      my_snprintf(buf, sizeof(buf),
unknown's avatar
unknown committed
1284 1285 1286 1287 1288 1289
                  "Failed to read from index file (errno: %d)", my_errno);
      errmsg= buf;
    }
    error= HA_ADMIN_FAILED;
    goto err;
  }
1290

unknown's avatar
unknown committed
1291 1292 1293 1294
  DBUG_RETURN(HA_ADMIN_OK);

 err:
  {
1295 1296 1297
    HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
    if (!&param)
      return HA_ADMIN_INTERNAL_ERROR;
unknown's avatar
unknown committed
1298 1299
    myisamchk_init(&param);
    param.thd= thd;
1300
    param.op_name=    "preload_keys";
unknown's avatar
unknown committed
1301 1302
    param.db_name=    table->s->db.str;
    param.table_name= table->s->table_name.str;
1303
    param.testflag=   0;
unknown's avatar
unknown committed
1304 1305 1306 1307 1308
    mi_check_print_error(&param, errmsg);
    DBUG_RETURN(error);
  }
}

1309

1310
/*
1311 1312
  Disable indexes, making it persistent if requested.

1313
  SYNOPSIS
1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327
    disable_indexes()
    mode        mode of operation:
                HA_KEY_SWITCH_NONUNIQ      disable all non-unique keys
                HA_KEY_SWITCH_ALL          disable all keys
                HA_KEY_SWITCH_NONUNIQ_SAVE dis. non-uni. and make persistent
                HA_KEY_SWITCH_ALL_SAVE     dis. all keys and make persistent

  IMPLEMENTATION
    HA_KEY_SWITCH_NONUNIQ       is not implemented.
    HA_KEY_SWITCH_ALL_SAVE      is not implemented.

  RETURN
    0  ok
    HA_ERR_WRONG_COMMAND  mode not implemented.
1328
*/
1329 1330

int ha_myisam::disable_indexes(uint mode)
1331
{
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350
  int error;

  if (mode == HA_KEY_SWITCH_ALL)
  {
    /* call a storage engine function to switch the key map */
    error= mi_disable_indexes(file);
  }
  else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
  {
    mi_extra(file, HA_EXTRA_NO_KEYS, 0);
    info(HA_STATUS_CONST);                        // Read new key info
    error= 0;
  }
  else
  {
    /* mode not implemented */
    error= HA_ERR_WRONG_COMMAND;
  }
  return error;
1351 1352
}

1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382

/*
  Enable indexes, making it persistent if requested.

  SYNOPSIS
    enable_indexes()
    mode        mode of operation:
                HA_KEY_SWITCH_NONUNIQ      enable all non-unique keys
                HA_KEY_SWITCH_ALL          enable all keys
                HA_KEY_SWITCH_NONUNIQ_SAVE en. non-uni. and make persistent
                HA_KEY_SWITCH_ALL_SAVE     en. all keys and make persistent

  DESCRIPTION
    Enable indexes, which might have been disabled by disable_index() before.
    The modes without _SAVE work only if both data and indexes are empty,
    since the MyISAM repair would enable them persistently.
    To be sure in these cases, call handler::delete_all_rows() before.

  IMPLEMENTATION
    HA_KEY_SWITCH_NONUNIQ       is not implemented.
    HA_KEY_SWITCH_ALL_SAVE      is not implemented.

  RETURN
    0  ok
    !=0  Error, among others:
    HA_ERR_CRASHED  data or index is non-empty. Delete all rows and retry.
    HA_ERR_WRONG_COMMAND  mode not implemented.
*/

int ha_myisam::enable_indexes(uint mode)
1383
{
1384
  int error;
1385
  DBUG_ENTER("ha_myisam::enable_indexes");
1386

1387 1388 1389
  DBUG_EXECUTE_IF("wait_in_enable_indexes",
                  debug_wait_for_kill("wait_in_enable_indexes"); );

1390
  if (mi_is_all_keys_active(file->s->state.key_map, file->s->base.keys))
1391 1392
  {
    /* All indexes are enabled already. */
1393
    DBUG_RETURN(0);
1394
  }
1395

1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
  if (mode == HA_KEY_SWITCH_ALL)
  {
    error= mi_enable_indexes(file);
    /*
       Do not try to repair on error,
       as this could make the enabled state persistent,
       but mode==HA_KEY_SWITCH_ALL forbids it.
    */
  }
  else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
  {
1407
    THD *thd= table->in_use;
1408
    HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
1409
    const char *save_proc_info=thd->proc_info;
1410 1411

    if (!&param)
1412
      DBUG_RETURN(HA_ADMIN_INTERNAL_ERROR);
1413

1414
    thd_proc_info(thd, "Creating index");
1415
    myisamchk_init(&param);
1416 1417 1418
    param.op_name= "recreating_index";
    param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
                     T_CREATE_MISSING_KEYS);
1419
    param.myf_rw&= ~MY_WAIT_IF_FULL;
1420
    param.sort_buffer_length=  THDVAR(thd, sort_buffer_size);
Sergei Golubchik's avatar
Sergei Golubchik committed
1421
    param.stats_method= (enum_handler_stats_method)THDVAR(thd, stats_method);
1422
    param.tmpdir=&mysql_tmpdir_list;
1423 1424
    if ((error= (repair(thd,param,0) != HA_ADMIN_OK)) && param.retry_repair)
    {
1425 1426
      sql_print_warning("Warning: Enabling keys got errno %d on %s.%s, retrying",
                        my_errno, param.db_name, param.table_name);
1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437
      /*
        Repairing by sort failed. Now try standard repair method.
        Still we want to fix only index file. If data file corruption
        was detected (T_RETRY_WITHOUT_QUICK), we shouldn't do much here.
        Let implicit repair do this job.
      */
      if (!(param.testflag & T_RETRY_WITHOUT_QUICK))
      {
        param.testflag&= ~T_REP_BY_SORT;
        error= (repair(thd,param,0) != HA_ADMIN_OK);
      }
1438 1439 1440 1441 1442 1443 1444
      /*
        If the standard repair succeeded, clear all error messages which
        might have been set by the first repair. They can still be seen
        with SHOW WARNINGS then.
      */
      if (! error)
        thd->clear_error();
1445
    }
1446
    info(HA_STATUS_CONST);
1447
    thd_proc_info(thd, save_proc_info);
1448 1449 1450 1451 1452 1453
  }
  else
  {
    /* mode not implemented */
    error= HA_ERR_WRONG_COMMAND;
  }
1454
  DBUG_RETURN(error);
1455 1456
}

1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479

/*
  Test if indexes are disabled.


  SYNOPSIS
    indexes_are_disabled()
      no parameters


  RETURN
    0  indexes are not disabled
    1  all indexes are disabled
   [2  non-unique indexes are disabled - NOT YET IMPLEMENTED]
*/

int ha_myisam::indexes_are_disabled(void)
{
  
  return mi_indexes_are_disabled(file);
}


1480
/*
1481 1482 1483
  prepare for a many-rows insert operation
  e.g. - disable indexes (if they can be recreated fast) or
  activate special bulk-insert optimizations
1484 1485

  SYNOPSIS
1486 1487 1488
    start_bulk_insert(rows)
    rows        Rows to be inserted
                0 if we don't know
1489 1490 1491

  NOTICE
    Do not forget to call end_bulk_insert() later!
1492
*/
1493

1494
void ha_myisam::start_bulk_insert(ha_rows rows)
1495
{
1496
  DBUG_ENTER("ha_myisam::start_bulk_insert");
1497 1498
  THD *thd= current_thd;
  ulong size= min(thd->variables.read_buff_size,
1499
                  (ulong) (table->s->avg_row_length*rows));
1500 1501
  DBUG_PRINT("info",("start_bulk_insert: rows %lu size %lu",
                     (ulong) rows, size));
unknown's avatar
unknown committed
1502

1503
  /* don't enable row cache if too few rows */
1504
  if (! rows || (rows > MI_MIN_ROWS_TO_USE_WRITE_CACHE))
1505
    mi_extra(file, HA_EXTRA_WRITE_CACHE, (void*) &size);
unknown's avatar
unknown committed
1506

1507 1508
  can_enable_indexes= mi_is_all_keys_active(file->s->state.key_map,
                                            file->s->base.keys);
unknown's avatar
unknown committed
1509

1510
  if (!(specialflag & SPECIAL_SAFE_MODE))
1511
  {
1512 1513 1514
    /*
      Only disable old index if the table was empty and we are inserting
      a lot of rows.
1515 1516 1517
      Note that in end_bulk_insert() we may truncate the table if
      enable_indexes() failed, thus it's essential that indexes are
      disabled ONLY for an empty table.
1518 1519 1520
    */
    if (file->state->records == 0 && can_enable_indexes &&
        (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES))
1521 1522 1523 1524 1525 1526 1527 1528 1529
    {
      if (file->open_flag & HA_OPEN_INTERNAL_TABLE)
      {
        file->update|= HA_STATE_CHANGED;
        mi_clear_all_keys_active(file->s->state.key_map);
      }
      else
        mi_disable_non_unique_index(file,rows);
    }
1530 1531 1532
    else
    if (!file->bulk_insert &&
        (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT))
unknown's avatar
unknown committed
1533
    {
unknown's avatar
unknown committed
1534
      mi_init_bulk_insert(file, thd->variables.bulk_insert_buff_size, rows);
unknown's avatar
unknown committed
1535
    }
1536
  }
1537
  DBUG_VOID_RETURN;
1538 1539
}

1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552
/*
  end special bulk-insert optimizations,
  which have been activated by start_bulk_insert().

  SYNOPSIS
    end_bulk_insert()
    no arguments

  RETURN
    0     OK
    != 0  Error
*/

1553
int ha_myisam::end_bulk_insert()
1554
{
1555
  DBUG_ENTER("ha_myisam::end_bulk_insert");
unknown's avatar
unknown committed
1556
  mi_end_bulk_insert(file);
unknown's avatar
unknown committed
1557
  int err=mi_extra(file, HA_EXTRA_NO_CACHE, 0);
1558
  if (!err && !file->s->deleting)
1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577
  {
    if (can_enable_indexes)
    {
      /* 
        Truncate the table when enable index operation is killed. 
        After truncating the table we don't need to enable the 
        indexes, because the last repair operation is aborted after 
        setting the indexes as active and  trying to recreate them. 
     */
   
      if (((err= enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE)) != 0) && 
                                                  current_thd->killed)
      {
        delete_all_rows();
        /* not crashed, despite being killed during repair */
        file->s->state.changed&= ~(STATE_CRASHED|STATE_CRASHED_ON_REPAIR);
      }
    } 
  }
1578
  DBUG_RETURN(err);
1579
}
unknown's avatar
unknown committed
1580

1581

1582
bool ha_myisam::check_and_repair(THD *thd)
1583 1584
{
  int error=0;
unknown's avatar
unknown committed
1585
  int marked_crashed;
1586
  HA_CHECK_OPT check_opt;
1587
  DBUG_ENTER("ha_myisam::check_and_repair");
1588 1589

  check_opt.init();
1590 1591 1592
  check_opt.flags= T_MEDIUM | T_AUTO_REPAIR;
  // Don't use quick if deleted rows
  if (!file->state->del && (myisam_recover_options & HA_RECOVER_QUICK))
unknown's avatar
unknown committed
1593
    check_opt.flags|=T_QUICK;
1594
  sql_print_warning("Checking table:   '%s'",table->s->path.str);
1595

1596
  const CSET_STRING query_backup= thd->query_string;
Gleb Shchepa's avatar
Gleb Shchepa committed
1597
  thd->set_query(table->s->table_name.str,
1598
                 (uint) table->s->table_name.length, system_charset_info);
1599 1600

  if ((marked_crashed= mi_is_crashed(file)) || check(thd, &check_opt))
1601
  {
1602
    bool save_log_all_errors;
1603
    sql_print_warning("Recovering table: '%s'",table->s->path.str);
1604
    save_log_all_errors= thd->log_all_errors;
1605
    thd->log_all_errors|= (thd->variables.log_warnings > 2);
1606
    if (myisam_recover_options & HA_RECOVER_FULL_BACKUP)
1607 1608 1609
    {
      char buff[MY_BACKUP_NAME_EXTRA_LENGTH+1];
      my_create_backup_name(buff, "", check_opt.start_time);
1610 1611
      sql_print_information("Making backup of index file with extension '%s'",
                            buff);
1612 1613
      mi_make_backup_of_index(file, check_opt.start_time,
                              MYF(MY_WME | ME_JUST_WARNING));
1614
    }
unknown's avatar
unknown committed
1615
    check_opt.flags=
1616 1617
      (((myisam_recover_options &
         (HA_RECOVER_BACKUP | HA_RECOVER_FULL_BACKUP)) ? T_BACKUP_DATA : 0) |
1618 1619 1620
       (marked_crashed                             ? 0 : T_QUICK) |
       (myisam_recover_options & HA_RECOVER_FORCE  ? 0 : T_SAFE_REPAIR) |
       T_AUTO_REPAIR);
1621 1622
    if (repair(thd, &check_opt))
      error=1;
1623
    thd->log_all_errors= save_log_all_errors;
1624
  }
1625
  thd->set_query(query_backup);
1626 1627 1628
  DBUG_RETURN(error);
}

1629 1630 1631 1632 1633
bool ha_myisam::is_crashed() const
{
  return (file->s->state.changed & STATE_CRASHED ||
	  (my_disable_locking && file->s->state.open_count));
}
1634

1635
int ha_myisam::update_row(const uchar *old_data, uchar *new_data)
unknown's avatar
unknown committed
1636
{
1637 1638
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
    table->timestamp_field->set_time();
unknown's avatar
unknown committed
1639 1640 1641
  return mi_update(file,old_data,new_data);
}

1642
int ha_myisam::delete_row(const uchar *buf)
unknown's avatar
unknown committed
1643 1644 1645 1646
{
  return mi_delete(file,buf);
}

1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674

C_MODE_START

ICP_RESULT index_cond_func_myisam(void *arg)
{
  ha_myisam *h= (ha_myisam*)arg;
  if (h->end_range)
  {
    if (h->compare_key2(h->end_range) > 0)
      return ICP_OUT_OF_RANGE; /* caller should return HA_ERR_END_OF_FILE already */
  }
  return (ICP_RESULT) test(h->pushed_idx_cond->val_int());
}

C_MODE_END


int ha_myisam::index_init(uint idx, bool sorted)
{ 
  active_index=idx;
  if (pushed_idx_cond_keyno == idx)
    mi_set_index_cond_func(file, index_cond_func_myisam, this);
  return 0; 
}


int ha_myisam::index_end()
{
1675
  DBUG_ENTER("ha_myisam::index_end");
1676 1677 1678 1679 1680
  active_index=MAX_KEY;
  //pushed_idx_cond_keyno= MAX_KEY;
  mi_set_index_cond_func(file, NULL, 0);
  in_range_check_pushed_down= FALSE;
  ds_mrr.dsmrr_close();
1681 1682 1683 1684
#if !defined(DBUG_OFF) && defined(SQL_SELECT_FIXED_FOR_UPDATE)
  file->update&= ~HA_STATE_AKTIV;               // Forget active row
#endif
  DBUG_RETURN(0);
1685 1686 1687 1688
}

int ha_myisam::rnd_end()
{
1689
  DBUG_ENTER("ha_myisam::rnd_end");
1690
  ds_mrr.dsmrr_close();
1691 1692 1693 1694
#if !defined(DBUG_OFF) && defined(SQL_SELECT_FIXED_FOR_UPDATE)
  file->update&= ~HA_STATE_AKTIV;               // Forget active row
#endif
  DBUG_RETURN(0);
1695 1696
}

1697 1698 1699
int ha_myisam::index_read_map(uchar *buf, const uchar *key,
                              key_part_map keypart_map,
                              enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
1700
{
unknown's avatar
unknown committed
1701
  DBUG_ASSERT(inited==INDEX);
1702
  int error=mi_rkey(file, buf, active_index, key, keypart_map, find_flag);
unknown's avatar
unknown committed
1703 1704 1705
  return error;
}

1706 1707 1708
int ha_myisam::index_read_idx_map(uchar *buf, uint index, const uchar *key,
                                  key_part_map keypart_map,
                                  enum ha_rkey_function find_flag)
unknown's avatar
unknown committed
1709
{
1710 1711
  int res;
  /* Use the pushed index condition if it matches the index we're scanning */
1712
  end_range= NULL;
1713 1714 1715 1716 1717
  if (index == pushed_idx_cond_keyno)
    mi_set_index_cond_func(file, index_cond_func_myisam, this);
  res= mi_rkey(file, buf, index, key, keypart_map, find_flag);
  mi_set_index_cond_func(file, NULL, 0);
  return res;
unknown's avatar
unknown committed
1718 1719
}

1720
int ha_myisam::index_next(uchar *buf)
unknown's avatar
unknown committed
1721
{
unknown's avatar
unknown committed
1722
  DBUG_ASSERT(inited==INDEX);
unknown's avatar
unknown committed
1723 1724 1725 1726
  int error=mi_rnext(file,buf,active_index);
  return error;
}

1727
int ha_myisam::index_prev(uchar *buf)
unknown's avatar
unknown committed
1728
{
unknown's avatar
unknown committed
1729
  DBUG_ASSERT(inited==INDEX);
unknown's avatar
unknown committed
1730 1731 1732
  int error=mi_rprev(file,buf, active_index);
  return error;
}
1733

1734
int ha_myisam::index_first(uchar *buf)
unknown's avatar
unknown committed
1735
{
unknown's avatar
unknown committed
1736
  DBUG_ASSERT(inited==INDEX);
unknown's avatar
unknown committed
1737 1738 1739 1740
  int error=mi_rfirst(file, buf, active_index);
  return error;
}

1741
int ha_myisam::index_last(uchar *buf)
unknown's avatar
unknown committed
1742
{
unknown's avatar
unknown committed
1743
  DBUG_ASSERT(inited==INDEX);
unknown's avatar
unknown committed
1744 1745 1746 1747
  int error=mi_rlast(file, buf, active_index);
  return error;
}

1748 1749
int ha_myisam::index_next_same(uchar *buf,
			       const uchar *key __attribute__((unused)),
unknown's avatar
unknown committed
1750 1751
			       uint length __attribute__((unused)))
{
1752
  int error;
unknown's avatar
unknown committed
1753
  DBUG_ASSERT(inited==INDEX);
1754 1755 1756 1757
  do
  {
    error= mi_rnext_same(file,buf);
  } while (error == HA_ERR_RECORD_DELETED);
unknown's avatar
unknown committed
1758 1759 1760 1761 1762 1763 1764 1765
  return error;
}


int ha_myisam::rnd_init(bool scan)
{
  if (scan)
    return mi_scan_init(file);
1766
  return mi_reset(file);                        // Free buffers
unknown's avatar
unknown committed
1767 1768
}

1769
int ha_myisam::rnd_next(uchar *buf)
unknown's avatar
unknown committed
1770 1771 1772 1773 1774
{
  int error=mi_scan(file, buf);
  return error;
}

1775
int ha_myisam::remember_rnd_pos()
unknown's avatar
unknown committed
1776
{
1777 1778 1779 1780 1781
  position((uchar*) 0);
  return 0;
}

int ha_myisam::restart_rnd_next(uchar *buf)
unknown's avatar
unknown committed
1782
{
1783
  return rnd_pos(buf, ref);
unknown's avatar
unknown committed
1784 1785
}

1786
int ha_myisam::rnd_pos(uchar *buf, uchar *pos)
unknown's avatar
unknown committed
1787
{
unknown's avatar
unknown committed
1788
  int error=mi_rrnd(file, buf, my_get_ptr(pos,ref_length));
unknown's avatar
unknown committed
1789 1790 1791
  return error;
}

1792
void ha_myisam::position(const uchar *record)
unknown's avatar
unknown committed
1793
{
1794 1795
  my_off_t row_position= mi_position(file);
  my_store_ptr(ref, ref_length, row_position);
1796
  file->update|= HA_STATE_AKTIV;               // Row can be updated
unknown's avatar
unknown committed
1797 1798
}

1799
int ha_myisam::info(uint flag)
unknown's avatar
unknown committed
1800
{
1801
  MI_ISAMINFO misam_info;
1802 1803
  char name_buff[FN_REFLEN];

1804 1805 1806
  if (!table)
    return 1;

1807
  (void) mi_status(file,&misam_info,flag);
unknown's avatar
unknown committed
1808 1809
  if (flag & HA_STATUS_VARIABLE)
  {
unknown's avatar
unknown committed
1810 1811 1812 1813 1814
    stats.records=           misam_info.records;
    stats.deleted=           misam_info.deleted;
    stats.data_file_length=  misam_info.data_file_length;
    stats.index_file_length= misam_info.index_file_length;
    stats.delete_length=     misam_info.delete_length;
1815
    stats.check_time=        (ulong) misam_info.check_time;
unknown's avatar
unknown committed
1816
    stats.mean_rec_length=   misam_info.mean_reclength;
unknown's avatar
unknown committed
1817 1818 1819
  }
  if (flag & HA_STATUS_CONST)
  {
1820
    TABLE_SHARE *share= table->s;
unknown's avatar
unknown committed
1821 1822
    stats.max_data_file_length=  misam_info.max_data_file_length;
    stats.max_index_file_length= misam_info.max_index_file_length;
1823
    stats.create_time= (ulong) misam_info.create_time;
1824 1825 1826 1827 1828 1829 1830 1831 1832 1833
    /* 
      We want the value of stats.mrr_length_per_rec to be platform independent.
      The size of the chunk at the end of the join buffer used for MRR needs
      is calculated now basing on the values passed in the stats structure.
      The remaining part of the join buffer is used for records. A different
      number of records in the buffer results in a different number of buffer
      refills and in a different order of records in the result set.
    */
    stats.mrr_length_per_rec= misam_info.reflength + 8; // 8=max(sizeof(void *))

1834 1835
    ref_length= misam_info.reflength;
    share->db_options_in_use= misam_info.options;
1836
    stats.block_size= myisam_block_size;        /* record block size */
unknown's avatar
unknown committed
1837 1838 1839

    /* Update share */
    if (share->tmp_table == NO_TMP_TABLE)
1840
      mysql_mutex_lock(&share->LOCK_ha_data);
1841
    share->keys_in_use.set_prefix(share->keys);
1842
    share->keys_in_use.intersect_extended(misam_info.key_map);
1843
    share->keys_for_keyread.intersect(share->keys_in_use);
1844
    share->db_record_offset= misam_info.record_offset;
1845
    if (share->key_parts)
unknown's avatar
unknown committed
1846
      memcpy((char*) table->key_info[0].rec_per_key,
1847
	     (char*) misam_info.rec_per_key,
1848
             sizeof(table->key_info[0].rec_per_key[0])*share->key_parts);
unknown's avatar
unknown committed
1849
    if (share->tmp_table == NO_TMP_TABLE)
1850
      mysql_mutex_unlock(&share->LOCK_ha_data);
unknown's avatar
unknown committed
1851

1852 1853
   /*
     Set data_file_name and index_file_name to point at the symlink value
1854
     if table is symlinked (Ie;  Real name is not same as generated name)
1855
   */
unknown's avatar
unknown committed
1856
    data_file_name= index_file_name= 0;
1857 1858
    fn_format(name_buff, file->filename, "", MI_NAME_DEXT,
              MY_APPEND_EXT | MY_UNPACK_FILENAME);
1859
    if (strcmp(name_buff, misam_info.data_file_name))
unknown's avatar
unknown committed
1860
      data_file_name=misam_info.data_file_name;
1861 1862
    fn_format(name_buff, file->filename, "", MI_NAME_IEXT,
              MY_APPEND_EXT | MY_UNPACK_FILENAME);
1863
    if (strcmp(name_buff, misam_info.index_file_name))
unknown's avatar
unknown committed
1864
      index_file_name=misam_info.index_file_name;
unknown's avatar
unknown committed
1865 1866 1867
  }
  if (flag & HA_STATUS_ERRKEY)
  {
1868
    errkey  = misam_info.errkey;
unknown's avatar
unknown committed
1869
    my_store_ptr(dup_ref, ref_length, misam_info.dupp_key_pos);
unknown's avatar
unknown committed
1870 1871
  }
  if (flag & HA_STATUS_TIME)
1872
    stats.update_time = (ulong) misam_info.update_time;
unknown's avatar
unknown committed
1873
  if (flag & HA_STATUS_AUTO)
unknown's avatar
unknown committed
1874
    stats.auto_increment_value= misam_info.auto_increment;
1875 1876

  return 0;
unknown's avatar
unknown committed
1877 1878 1879 1880 1881
}


int ha_myisam::extra(enum ha_extra_function operation)
{
unknown's avatar
unknown committed
1882 1883
  if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_KEYREAD)
    return 0;
1884 1885
  if (operation == HA_EXTRA_MMAP && !opt_myisam_use_mmap)
    return 0;
unknown's avatar
unknown committed
1886 1887 1888
  return mi_extra(file, operation, 0);
}

1889

1890 1891
int ha_myisam::reset(void)
{
1892 1893
  mi_set_index_cond_func(file, NULL, 0);
  ds_mrr.dsmrr_close();
1894 1895
  return mi_reset(file);
}
unknown's avatar
unknown committed
1896

unknown's avatar
unknown committed
1897
/* To be used with WRITE_CACHE and EXTRA_CACHE */
unknown's avatar
unknown committed
1898 1899 1900

int ha_myisam::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
unknown's avatar
unknown committed
1901
  if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE)
unknown's avatar
unknown committed
1902
    return 0;
unknown's avatar
unknown committed
1903
  return mi_extra(file, operation, (void*) &cache_size);
unknown's avatar
unknown committed
1904 1905 1906 1907 1908 1909 1910
}

int ha_myisam::delete_all_rows()
{
  return mi_delete_all_rows(file);
}

1911

1912 1913 1914 1915 1916 1917
int ha_myisam::reset_auto_increment(ulonglong value)
{
  file->s->state.auto_increment= value;
  return 0;
}

unknown's avatar
unknown committed
1918 1919 1920 1921 1922
int ha_myisam::delete_table(const char *name)
{
  return mi_delete_table(name);
}

1923 1924 1925 1926 1927 1928 1929
void ha_myisam::change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
{
  handler::change_table_ptr(table_arg, share);
  if (file)
    file->external_ref= table_arg;
}

1930

unknown's avatar
unknown committed
1931 1932
int ha_myisam::external_lock(THD *thd, int lock_type)
{
1933
  file->in_use.data= thd;
1934
  file->external_ref= (void*) table;            // For mi_killed()
1935
  return mi_lock_database(file, !table->s->tmp_table ?
unknown's avatar
unknown committed
1936
			  lock_type : ((lock_type == F_UNLCK) ?
1937
				       F_UNLCK : F_EXTRA_LCK));
1938
}
unknown's avatar
unknown committed
1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951

THR_LOCK_DATA **ha_myisam::store_lock(THD *thd,
				      THR_LOCK_DATA **to,
				      enum thr_lock_type lock_type)
{
  if (lock_type != TL_IGNORE && file->lock.type == TL_UNLOCK)
    file->lock.type=lock_type;
  *to++= &file->lock;
  return to;
}

void ha_myisam::update_create_info(HA_CREATE_INFO *create_info)
{
1952
  ha_myisam::info(HA_STATUS_AUTO | HA_STATUS_CONST);
unknown's avatar
unknown committed
1953 1954
  if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
  {
1955
    create_info->auto_increment_value= stats.auto_increment_value;
unknown's avatar
unknown committed
1956
  }
1957 1958
  create_info->data_file_name=data_file_name;
  create_info->index_file_name=index_file_name;
unknown's avatar
unknown committed
1959 1960 1961
}


1962
int ha_myisam::create(const char *name, register TABLE *table_arg,
1963
		      HA_CREATE_INFO *ha_create_info)
unknown's avatar
unknown committed
1964 1965
{
  int error;
1966
  uint create_flags= 0, records, i;
unknown's avatar
unknown committed
1967 1968
  char buff[FN_REFLEN];
  MI_KEYDEF *keydef;
1969
  MI_COLUMNDEF *recinfo;
unknown's avatar
unknown committed
1970
  MI_CREATE_INFO create_info;
unknown's avatar
unknown committed
1971
  TABLE_SHARE *share= table_arg->s;
1972
  uint options= share->db_options_in_use;
unknown's avatar
unknown committed
1973
  DBUG_ENTER("ha_myisam::create");
1974
  for (i= 0; i < share->keys; i++)
1975
  {
1976
    if (table_arg->key_info[i].flags & HA_USES_PARSER)
unknown's avatar
unknown committed
1977
    {
1978
      create_flags|= HA_CREATE_RELIES_ON_SQL_LAYER;
unknown's avatar
unknown committed
1979 1980 1981
      break;
    }
  }
1982 1983 1984
  if ((error= table2myisam(table_arg, &keydef, &recinfo, &records)))
    DBUG_RETURN(error); /* purecov: inspected */
  bzero((char*) &create_info, sizeof(create_info));
1985 1986
  create_info.max_rows= share->max_rows;
  create_info.reloc_rows= share->min_rows;
1987
  create_info.with_auto_increment= share->next_number_key_offset == 0;
unknown's avatar
unknown committed
1988 1989
  create_info.auto_increment= (ha_create_info->auto_increment_value ?
                               ha_create_info->auto_increment_value -1 :
1990
                               (ulonglong) 0);
1991
  create_info.data_file_length= ((ulonglong) share->max_rows *
1992
                                 share->avg_row_length);
1993
  create_info.data_file_name= ha_create_info->data_file_name;
1994
  create_info.index_file_name= ha_create_info->index_file_name;
1995
  create_info.language= share->table_charset->number;
unknown's avatar
unknown committed
1996

1997
  if (ha_create_info->options & HA_LEX_CREATE_TMP_TABLE)
1998
    create_flags|= HA_CREATE_TMP_TABLE;
unknown's avatar
unknown committed
1999 2000
  if (ha_create_info->options & HA_CREATE_KEEP_FILES)
    create_flags|= HA_CREATE_KEEP_FILES;
2001 2002 2003 2004 2005 2006 2007
  if (options & HA_OPTION_PACK_RECORD)
    create_flags|= HA_PACK_RECORD;
  if (options & HA_OPTION_CHECKSUM)
    create_flags|= HA_CREATE_CHECKSUM;
  if (options & HA_OPTION_DELAY_KEY_WRITE)
    create_flags|= HA_CREATE_DELAY_KEY_WRITE;

2008
  /* TODO: Check that the following fn_format is really needed */
2009
  error= mi_create(fn_format(buff, name, "", "",
2010
                             MY_UNPACK_FILENAME|MY_APPEND_EXT),
2011
                   share->keys, keydef,
2012 2013 2014
                   records, recinfo,
                   0, (MI_UNIQUEDEF*) 0,
                   &create_info, create_flags);
2015
  my_free(recinfo);
unknown's avatar
unknown committed
2016 2017 2018 2019 2020 2021 2022 2023 2024 2025
  DBUG_RETURN(error);
}


int ha_myisam::rename_table(const char * from, const char * to)
{
  return mi_rename(from,to);
}


2026 2027 2028 2029
void ha_myisam::get_auto_increment(ulonglong offset, ulonglong increment,
                                   ulonglong nb_desired_values,
                                   ulonglong *first_value,
                                   ulonglong *nb_reserved_values)
unknown's avatar
unknown committed
2030
{
2031 2032
  ulonglong nr;
  int error;
unknown's avatar
unknown committed
2033
  uchar key[HA_MAX_KEY_LENGTH];
2034

2035
  if (!table->s->next_number_key_offset)
unknown's avatar
unknown committed
2036 2037
  {						// Autoincrement at key-start
    ha_myisam::info(HA_STATUS_AUTO);
2038
    *first_value= stats.auto_increment_value;
2039 2040 2041
    /* MyISAM has only table-level lock, so reserves to +inf */
    *nb_reserved_values= ULONGLONG_MAX;
    return;
unknown's avatar
unknown committed
2042 2043
  }

2044
  /* it's safe to call the following if bulk_insert isn't on */
2045
  mi_flush_bulk_insert(file, table->s->next_number_index);
2046

unknown's avatar
unknown committed
2047
  (void) extra(HA_EXTRA_KEYREAD);
2048
  key_copy(key, table->record[0],
2049 2050
           table->key_info + table->s->next_number_index,
           table->s->next_number_key_offset);
2051 2052 2053
  error= mi_rkey(file, table->record[1], (int) table->s->next_number_index,
                 key, make_prev_keypart_map(table->s->next_number_keypart),
                 HA_READ_PREFIX_LAST);
unknown's avatar
unknown committed
2054
  if (error)
2055
    nr= 1;
unknown's avatar
unknown committed
2056
  else
2057 2058
  {
    /* Get data from record[1] */
2059
    nr= ((ulonglong) table->next_number_field->
2060 2061
         val_int_offset(table->s->rec_buff_length)+1);
  }
unknown's avatar
unknown committed
2062
  extra(HA_EXTRA_NO_KEYREAD);
2063 2064 2065 2066 2067 2068 2069 2070
  *first_value= nr;
  /*
    MySQL needs to call us for next row: assume we are inserting ("a",null)
    here, we return 3, and next this statement will want to insert ("b",null):
    there is no reason why ("b",3+1) would be the good row to insert: maybe it
    already exists, maybe 3+1 is too large...
  */
  *nb_reserved_values= 1;
unknown's avatar
unknown committed
2071 2072 2073
}


unknown's avatar
unknown committed
2074 2075 2076 2077 2078 2079
/*
  Find out how many rows there is in the given range

  SYNOPSIS
    records_in_range()
    inx			Index to use
unknown's avatar
unknown committed
2080 2081
    min_key		Start of range.  Null pointer if from first key
    max_key		End of range. Null pointer if to last key
unknown's avatar
unknown committed
2082 2083

  NOTES
unknown's avatar
unknown committed
2084
    min_key.flag can have one of the following values:
unknown's avatar
unknown committed
2085 2086 2087
      HA_READ_KEY_EXACT		Include the key in the range
      HA_READ_AFTER_KEY		Don't include key in range

unknown's avatar
unknown committed
2088
    max_key.flag can have one of the following values:  
unknown's avatar
unknown committed
2089 2090 2091 2092 2093 2094 2095 2096 2097 2098
      HA_READ_BEFORE_KEY	Don't include key in range
      HA_READ_AFTER_KEY		Include all 'end_key' values in the range

  RETURN
   HA_POS_ERROR		Something is wrong with the index tree.
   0			There is no matching keys in the given range
   number > 0		There is approximately 'number' matching rows in
			the range.
*/

unknown's avatar
unknown committed
2099 2100
ha_rows ha_myisam::records_in_range(uint inx, key_range *min_key,
                                    key_range *max_key)
unknown's avatar
unknown committed
2101
{
unknown's avatar
unknown committed
2102
  return (ha_rows) mi_records_in_range(file, (int) inx, min_key, max_key);
unknown's avatar
unknown committed
2103 2104
}

unknown's avatar
unknown committed
2105

2106
int ha_myisam::ft_read(uchar *buf)
unknown's avatar
unknown committed
2107 2108 2109
{
  int error;

2110 2111 2112
  if (!ft_handler)
    return -1;

2113
  thread_safe_increment(table->in_use->status_var.ha_read_next_count,
2114
			&LOCK_status); // why ?
unknown's avatar
unknown committed
2115

unknown's avatar
unknown committed
2116
  error=ft_handler->please->read_next(ft_handler,(char*) buf);
unknown's avatar
unknown committed
2117 2118
  return error;
}
unknown's avatar
unknown committed
2119 2120 2121

uint ha_myisam::checksum() const
{
2122
  return (uint)file->state->checksum;
unknown's avatar
unknown committed
2123 2124
}

unknown's avatar
unknown committed
2125 2126 2127 2128 2129 2130

bool ha_myisam::check_if_incompatible_data(HA_CREATE_INFO *info,
					   uint table_changes)
{
  uint options= table->s->db_options_in_use;

2131
  if (info->auto_increment_value != stats.auto_increment_value ||
unknown's avatar
unknown committed
2132 2133
      info->data_file_name != data_file_name ||
      info->index_file_name != index_file_name ||
2134 2135
      table_changes == IS_EQUAL_NO ||
      table_changes & IS_EQUAL_PACK_LENGTH) // Not implemented yet
unknown's avatar
unknown committed
2136 2137 2138 2139 2140 2141 2142 2143 2144
    return COMPATIBLE_DATA_NO;

  if ((options & (HA_OPTION_PACK_RECORD | HA_OPTION_CHECKSUM |
		  HA_OPTION_DELAY_KEY_WRITE)) !=
      (info->table_options & (HA_OPTION_PACK_RECORD | HA_OPTION_CHECKSUM |
			      HA_OPTION_DELAY_KEY_WRITE)))
    return COMPATIBLE_DATA_NO;
  return COMPATIBLE_DATA_YES;
}
unknown's avatar
unknown committed
2145

2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166

/**
  Check if a table is incompatible with the current version.

  The cases are:
  - Table has checksum, varchars and are not of dynamic record type
*/

int ha_myisam::check_for_upgrade(HA_CHECK_OPT *check_opt)
{
  if (!(file->s->options & HA_OPTION_NULL_FIELDS) &&
      !(file->s->options & HA_OPTION_PACK_RECORD) &&
      file->s->has_varchar_fields)
  {
    /* We need alter there to get the HA_OPTION_NULL_FIELDS flag to be set */
    return HA_ADMIN_NEEDS_ALTER;
  }
  return HA_ADMIN_OK;
}


2167 2168 2169 2170 2171
extern int mi_panic(enum ha_panic_function flag);
int myisam_panic(handlerton *hton, ha_panic_function flag)
{
  return mi_panic(flag);
}
unknown's avatar
unknown committed
2172

2173
static int myisam_init(void *p)
unknown's avatar
unknown committed
2174
{
2175 2176
  handlerton *myisam_hton;

Marc Alff's avatar
Marc Alff committed
2177 2178 2179 2180
#ifdef HAVE_PSI_INTERFACE
  init_myisam_psi_keys();
#endif

2181
  /* Set global variables based on startup options */
2182
  if (myisam_recover_options && myisam_recover_options != HA_RECOVER_OFF)
2183 2184 2185 2186 2187 2188
    ha_open_options|=HA_OPEN_ABORT_IF_CRASHED;
  else
    myisam_recover_options= HA_RECOVER_OFF;

  myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size);

2189
  myisam_hton= (handlerton *)p;
2190 2191 2192
  myisam_hton->state= SHOW_OPTION_YES;
  myisam_hton->db_type= DB_TYPE_MYISAM;
  myisam_hton->create= myisam_create_handler;
2193
  myisam_hton->panic= myisam_panic;
2194
  myisam_hton->flags= HTON_CAN_RECREATE | HTON_SUPPORT_LOG_TABLES;
2195
  mi_killed= mi_killed_in_mariadb;
unknown's avatar
unknown committed
2196 2197 2198
  return 0;
}

2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211
static struct st_mysql_sys_var* myisam_sysvars[]= {
  MYSQL_SYSVAR(block_size),
  MYSQL_SYSVAR(data_pointer_size),
  MYSQL_SYSVAR(max_sort_file_size),
  MYSQL_SYSVAR(recover_options),
  MYSQL_SYSVAR(repair_threads),
  MYSQL_SYSVAR(sort_buffer_size),
  MYSQL_SYSVAR(use_mmap),
  MYSQL_SYSVAR(mmap_size),
  MYSQL_SYSVAR(stats_method),
  0
};

2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222
/****************************************************************************
 * MyISAM MRR implementation: use DS-MRR
 ***************************************************************************/

int ha_myisam::multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
                                     uint n_ranges, uint mode, 
                                     HANDLER_BUFFER *buf)
{
  return ds_mrr.dsmrr_init(this, seq, seq_init_param, n_ranges, mode, buf);
}

2223
int ha_myisam::multi_range_read_next(range_id_t *range_info)
2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243
{
  return ds_mrr.dsmrr_next(range_info);
}

ha_rows ha_myisam::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
                                               void *seq_init_param, 
                                               uint n_ranges, uint *bufsz,
                                               uint *flags, COST_VECT *cost)
{
  /*
    This call is here because there is no location where this->table would
    already be known.
    TODO: consider moving it into some per-query initialization call.
  */
  ds_mrr.init(this, table);
  return ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges, bufsz,
                                 flags, cost);
}

ha_rows ha_myisam::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
2244 2245
                                         uint key_parts, uint *bufsz, 
                                         uint *flags, COST_VECT *cost)
2246 2247
{
  ds_mrr.init(this, table);
2248
  return ds_mrr.dsmrr_info(keyno, n_ranges, keys, key_parts, bufsz, flags, cost);
2249 2250
}

2251 2252 2253 2254 2255

int ha_myisam::multi_range_read_explain_info(uint mrr_mode, char *str, 
                                             size_t size)
{
  return ds_mrr.dsmrr_explain_info(mrr_mode, str, size);
2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273
}

/* MyISAM MRR implementation ends */


/* Index condition pushdown implementation*/


Item *ha_myisam::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
{
  pushed_idx_cond_keyno= keyno_arg;
  pushed_idx_cond= idx_cond_arg;
  in_range_check_pushed_down= TRUE;
  if (active_index == pushed_idx_cond_keyno)
    mi_set_index_cond_func(file, index_cond_func_myisam, this);
  return NULL;
}

unknown's avatar
unknown committed
2274
struct st_mysql_storage_engine myisam_storage_engine=
2275
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
unknown's avatar
unknown committed
2276 2277 2278 2279

mysql_declare_plugin(myisam)
{
  MYSQL_STORAGE_ENGINE_PLUGIN,
unknown's avatar
unknown committed
2280 2281
  &myisam_storage_engine,
  "MyISAM",
unknown's avatar
unknown committed
2282
  "MySQL AB",
2283
  "MyISAM storage engine",
2284
  PLUGIN_LICENSE_GPL,
unknown's avatar
unknown committed
2285
  myisam_init, /* Plugin Init */
unknown's avatar
unknown committed
2286
  NULL, /* Plugin Deinit */
unknown's avatar
unknown committed
2287
  0x0100, /* 1.0 */
2288
  NULL,                       /* status variables                */
2289
  myisam_sysvars,             /* system variables                */
2290 2291
  NULL,
  0,
unknown's avatar
unknown committed
2292 2293
}
mysql_declare_plugin_end;
unknown's avatar
unknown committed
2294 2295 2296 2297 2298 2299
maria_declare_plugin(myisam)
{
  MYSQL_STORAGE_ENGINE_PLUGIN,
  &myisam_storage_engine,
  "MyISAM",
  "MySQL AB",
2300
  "MyISAM storage engine",
unknown's avatar
unknown committed
2301 2302 2303 2304 2305
  PLUGIN_LICENSE_GPL,
  myisam_init, /* Plugin Init */
  NULL, /* Plugin Deinit */
  0x0100, /* 1.0 */
  NULL,                       /* status variables                */
2306
  myisam_sysvars,             /* system variables                */
unknown's avatar
unknown committed
2307 2308 2309 2310
  "1.0",                      /* string version */
  MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
}
maria_declare_plugin_end;
unknown's avatar
unknown committed
2311

2312

2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339
#ifdef HAVE_QUERY_CACHE
/**
  @brief Register a named table with a call back function to the query cache.

  @param thd The thread handle
  @param table_key A pointer to the table name in the table cache
  @param key_length The length of the table name
  @param[out] engine_callback The pointer to the storage engine call back
    function, currently 0
  @param[out] engine_data Engine data will be set to 0.

  @note Despite the name of this function, it is used to check each statement
    before it is cached and not to register a table or callback function.

  @see handler::register_query_cache_table

  @return The error code. The engine_data and engine_callback will be set to 0.
    @retval TRUE Success
    @retval FALSE An error occured
*/

my_bool ha_myisam::register_query_cache_table(THD *thd, char *table_name,
                                              uint table_name_len,
                                              qc_engine_callback
                                              *engine_callback,
                                              ulonglong *engine_data)
{
2340
  DBUG_ENTER("ha_myisam::register_query_cache_table");
2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351
  /*
    No call back function is needed to determine if a cached statement
    is valid or not.
  */
  *engine_callback= 0;

  /*
    No engine data is needed.
  */
  *engine_data= 0;

2352 2353 2354 2355 2356 2357
  if (file->s->concurrent_insert)
  {
    /*
      If a concurrent INSERT has happened just before the currently
      processed SELECT statement, the total size of the table is
      unknown.
2358

2359 2360
      To determine if the table size is known, the current thread's snap
      shot of the table size with the actual table size are compared.
2361

2362
      If the table size is unknown the SELECT statement can't be cached.
2363

2364 2365 2366 2367 2368 2369 2370
      When concurrent inserts are disabled at table open, mi_open()
      does not assign a get_status() function. In this case the local
      ("current") status is never updated. We would wrongly think that
      we cannot cache the statement.
    */
    ulonglong actual_data_file_length;
    ulonglong current_data_file_length;
2371

2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390
    /*
      POSIX visibility rules specify that "2. Whatever memory values a
      thread can see when it unlocks a mutex <...> can also be seen by any
      thread that later locks the same mutex". In this particular case,
      concurrent insert thread had modified the data_file_length in
      MYISAM_SHARE before it has unlocked (or even locked)
      structure_guard_mutex. So, here we're guaranteed to see at least that
      value after we've locked the same mutex. We can see a later value
      (modified by some other thread) though, but it's ok, as we only want
      to know if the variable was changed, the actual new value doesn't matter
    */
    actual_data_file_length= file->s->state.state.data_file_length;
    current_data_file_length= file->save_state.data_file_length;

    if (current_data_file_length != actual_data_file_length)
    {
      /* Don't cache current statement. */
      DBUG_RETURN(FALSE);
    }
2391 2392
  }

2393 2394 2395 2396 2397 2398 2399 2400 2401
  /*
    This query execution might have started after the query cache was flushed
    by a concurrent INSERT. In this case, don't cache this statement as the
    data file length difference might not be visible yet if the tables haven't
    been unlocked by the concurrent insert thread.
  */
  if (file->state->uncacheable)
    DBUG_RETURN(FALSE);

2402
  /* It is ok to try to cache current statement. */
2403
  DBUG_RETURN(TRUE);
2404 2405
}
#endif