~stewart/drizzle/embedded-innodb-create-select-transaction-arrgh

« back to all changes in this revision

Viewing changes to storage/myisam/ha_myisam.cc

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

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