~drizzle-trunk/drizzle/development

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