~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/myisam/ha_myisam.cc

  • Committer: Monty Taylor
  • Date: 2009-04-25 19:24:49 UTC
  • mto: (997.2.5 mordred)
  • mto: This revision was merged to the branch mainline in revision 1003.
  • Revision ID: mordred@inaugust.com-20090425192449-0htujbt2r9jzupn5
Moved heap.

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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
15
 
 
16
 
 
17
 
 
18
 
#include "config.h"
19
 
#include "drizzled/internal/my_bit.h"
20
 
#include "myisampack.h"
21
 
#include "ha_myisam.h"
22
 
#include "myisam_priv.h"
23
 
#include "drizzled/option.h"
24
 
#include "drizzled/internal/my_bit.h"
25
 
#include "drizzled/internal/m_string.h"
26
 
#include "drizzled/util/test.h"
27
 
#include "drizzled/error.h"
28
 
#include "drizzled/errmsg_print.h"
29
 
#include "drizzled/gettext.h"
30
 
#include "drizzled/session.h"
31
 
#include "drizzled/plugin.h"
32
 
#include "drizzled/plugin/client.h"
33
 
#include "drizzled/table.h"
34
 
#include "drizzled/field/timestamp.h"
35
 
#include "drizzled/memory/multi_malloc.h"
36
 
#include "drizzled/plugin/daemon.h"
37
 
 
38
 
#include <boost/algorithm/string.hpp>
39
 
#include <boost/scoped_ptr.hpp>
40
 
 
41
 
#include <string>
42
 
#include <sstream>
43
 
#include <map>
44
 
#include <algorithm>
45
 
#include <memory>
46
 
#include <boost/program_options.hpp>
47
 
#include <drizzled/module/option_map.h>
48
 
 
49
 
namespace po= boost::program_options;
50
 
 
51
 
using namespace std;
52
 
using namespace drizzled;
53
 
 
54
 
static const string engine_name("MyISAM");
55
 
 
56
 
boost::mutex THR_LOCK_myisam;
57
 
 
58
 
static uint32_t myisam_key_cache_block_size= KEY_CACHE_BLOCK_SIZE;
59
 
static uint32_t myisam_key_cache_size;
60
 
static uint32_t myisam_key_cache_division_limit;
61
 
static uint32_t myisam_key_cache_age_threshold;
62
 
static uint64_t max_sort_file_size;
63
 
typedef constrained_check<size_t, SIZE_MAX, 1024, 1024> sort_buffer_constraint;
64
 
static sort_buffer_constraint sort_buffer_size;
65
 
 
66
 
void st_mi_isam_share::setKeyCache()
67
 
{
68
 
  (void)init_key_cache(&key_cache,
69
 
                       myisam_key_cache_block_size,
70
 
                       myisam_key_cache_size,
71
 
                       myisam_key_cache_division_limit, 
72
 
                       myisam_key_cache_age_threshold);
73
 
}
74
 
 
75
 
/*****************************************************************************
76
 
** MyISAM tables
77
 
*****************************************************************************/
78
 
 
79
 
static const char *ha_myisam_exts[] = {
80
 
  ".MYI",
81
 
  ".MYD",
82
 
  NULL
83
 
};
84
 
 
85
 
class MyisamEngine : public plugin::StorageEngine
86
 
{
87
 
  MyisamEngine();
88
 
  MyisamEngine(const MyisamEngine&);
89
 
  MyisamEngine& operator=(const MyisamEngine&);
90
 
public:
91
 
  explicit MyisamEngine(string name_arg) :
92
 
    plugin::StorageEngine(name_arg,
93
 
                          HTON_CAN_INDEX_BLOBS |
94
 
                          HTON_STATS_RECORDS_IS_EXACT |
95
 
                          HTON_TEMPORARY_ONLY |
96
 
                          HTON_NULL_IN_KEY |
97
 
                          HTON_HAS_RECORDS |
98
 
                          HTON_DUPLICATE_POS |
99
 
                          HTON_AUTO_PART_KEY |
100
 
                          HTON_SKIP_STORE_LOCK)
101
 
  {
102
 
  }
103
 
 
104
 
  virtual ~MyisamEngine()
105
 
  { 
106
 
    mi_panic(HA_PANIC_CLOSE);
107
 
  }
108
 
 
109
 
  virtual Cursor *create(Table &table)
110
 
  {
111
 
    return new ha_myisam(*this, table);
112
 
  }
113
 
 
114
 
  const char **bas_ext() const {
115
 
    return ha_myisam_exts;
116
 
  }
117
 
 
118
 
  int doCreateTable(Session&,
119
 
                    Table& table_arg,
120
 
                    const TableIdentifier &identifier,
121
 
                    message::Table&);
122
 
 
123
 
  int doRenameTable(Session&, const TableIdentifier &from, const TableIdentifier &to);
124
 
 
125
 
  int doDropTable(Session&, const TableIdentifier &identifier);
126
 
 
127
 
  int doGetTableDefinition(Session& session,
128
 
                           const TableIdentifier &identifier,
129
 
                           message::Table &table_message);
130
 
 
131
 
  uint32_t max_supported_keys()          const { return MI_MAX_KEY; }
132
 
  uint32_t max_supported_key_length()    const { return MI_MAX_KEY_LENGTH; }
133
 
  uint32_t max_supported_key_part_length() const { return MI_MAX_KEY_LENGTH; }
134
 
 
135
 
  uint32_t index_flags(enum  ha_key_alg) const
136
 
  {
137
 
    return (HA_READ_NEXT |
138
 
            HA_READ_PREV |
139
 
            HA_READ_RANGE |
140
 
            HA_READ_ORDER |
141
 
            HA_KEYREAD_ONLY);
142
 
  }
143
 
  bool doDoesTableExist(Session& session, const TableIdentifier &identifier);
144
 
 
145
 
  void doGetTableIdentifiers(drizzled::CachedDirectory &directory,
146
 
                             const drizzled::SchemaIdentifier &schema_identifier,
147
 
                             drizzled::TableIdentifier::vector &set_of_identifiers);
148
 
  bool validateCreateTableOption(const std::string &key, const std::string &state)
149
 
  {
150
 
    (void)state;
151
 
    if (boost::iequals(key, "ROW_FORMAT"))
152
 
    {
153
 
      return true;
154
 
    }
155
 
 
156
 
    return false;
157
 
  }
158
 
};
159
 
 
160
 
void MyisamEngine::doGetTableIdentifiers(drizzled::CachedDirectory&,
161
 
                                         const drizzled::SchemaIdentifier&,
162
 
                                         drizzled::TableIdentifier::vector&)
163
 
{
164
 
}
165
 
 
166
 
bool MyisamEngine::doDoesTableExist(Session &session, const TableIdentifier &identifier)
167
 
{
168
 
  return session.getMessageCache().doesTableMessageExist(identifier);
169
 
}
170
 
 
171
 
int MyisamEngine::doGetTableDefinition(Session &session,
172
 
                                       const TableIdentifier &identifier,
173
 
                                       message::Table &table_message)
174
 
{
175
 
  if (session.getMessageCache().getTableMessage(identifier, table_message))
176
 
    return EEXIST;
177
 
  return ENOENT;
178
 
}
179
 
 
180
 
/* 
181
 
  Convert to push_Warnings if you ever care about this, otherwise, it is a no-op.
182
 
*/
183
 
 
184
 
static void mi_check_print_msg(MI_CHECK *,      const char* ,
185
 
                               const char *, va_list )
186
 
{
187
 
}
188
 
 
189
 
 
190
 
/*
191
 
  Convert Table object to MyISAM key and column definition
192
 
 
193
 
  SYNOPSIS
194
 
    table2myisam()
195
 
      table_arg   in     Table object.
196
 
      keydef_out  out    MyISAM key definition.
197
 
      recinfo_out out    MyISAM column definition.
198
 
      records_out out    Number of fields.
199
 
 
200
 
  DESCRIPTION
201
 
    This function will allocate and initialize MyISAM key and column
202
 
    definition for further use in mi_create or for a check for underlying
203
 
    table conformance in merge engine.
204
 
 
205
 
    The caller needs to free *recinfo_out after use. Since *recinfo_out
206
 
    and *keydef_out are allocated with a multi_malloc, *keydef_out
207
 
    is freed automatically when *recinfo_out is freed.
208
 
 
209
 
  RETURN VALUE
210
 
    0  OK
211
 
    !0 error code
212
 
*/
213
 
 
214
 
static int table2myisam(Table *table_arg, MI_KEYDEF **keydef_out,
215
 
                        MI_COLUMNDEF **recinfo_out, uint32_t *records_out)
216
 
{
217
 
  uint32_t i, j, recpos, minpos, fieldpos, temp_length, length;
218
 
  enum ha_base_keytype type= HA_KEYTYPE_BINARY;
219
 
  unsigned char *record;
220
 
  MI_KEYDEF *keydef;
221
 
  MI_COLUMNDEF *recinfo, *recinfo_pos;
222
 
  HA_KEYSEG *keyseg;
223
 
  TableShare *share= table_arg->getMutableShare();
224
 
  uint32_t options= share->db_options_in_use;
225
 
  if (!(memory::multi_malloc(false,
226
 
          recinfo_out, (share->sizeFields() * 2 + 2) * sizeof(MI_COLUMNDEF),
227
 
          keydef_out, share->sizeKeys() * sizeof(MI_KEYDEF),
228
 
          &keyseg, (share->key_parts + share->sizeKeys()) * sizeof(HA_KEYSEG),
229
 
          NULL)))
230
 
    return(HA_ERR_OUT_OF_MEM);
231
 
  keydef= *keydef_out;
232
 
  recinfo= *recinfo_out;
233
 
  for (i= 0; i < share->sizeKeys(); i++)
234
 
  {
235
 
    KeyInfo *pos= &table_arg->key_info[i];
236
 
    keydef[i].flag= ((uint16_t) pos->flags & (HA_NOSAME));
237
 
    keydef[i].key_alg= HA_KEY_ALG_BTREE;
238
 
    keydef[i].block_length= pos->block_size;
239
 
    keydef[i].seg= keyseg;
240
 
    keydef[i].keysegs= pos->key_parts;
241
 
    for (j= 0; j < pos->key_parts; j++)
242
 
    {
243
 
      Field *field= pos->key_part[j].field;
244
 
      type= field->key_type();
245
 
      keydef[i].seg[j].flag= pos->key_part[j].key_part_flag;
246
 
 
247
 
      if (options & HA_OPTION_PACK_KEYS ||
248
 
          (pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY |
249
 
                         HA_SPACE_PACK_USED)))
250
 
      {
251
 
        if (pos->key_part[j].length > 8 &&
252
 
            (type == HA_KEYTYPE_TEXT ||
253
 
             (type == HA_KEYTYPE_BINARY && !field->zero_pack())))
254
 
        {
255
 
          /* No blobs here */
256
 
          if (j == 0)
257
 
            keydef[i].flag|= HA_PACK_KEY;
258
 
          if ((((int) (pos->key_part[j].length - field->decimals())) >= 4))
259
 
            keydef[i].seg[j].flag|= HA_SPACE_PACK;
260
 
        }
261
 
        else if (j == 0 && (!(pos->flags & HA_NOSAME) || pos->key_length > 16))
262
 
          keydef[i].flag|= HA_BINARY_PACK_KEY;
263
 
      }
264
 
      keydef[i].seg[j].type= (int) type;
265
 
      keydef[i].seg[j].start= pos->key_part[j].offset;
266
 
      keydef[i].seg[j].length= pos->key_part[j].length;
267
 
      keydef[i].seg[j].bit_start= keydef[i].seg[j].bit_end=
268
 
        keydef[i].seg[j].bit_length= 0;
269
 
      keydef[i].seg[j].bit_pos= 0;
270
 
      keydef[i].seg[j].language= field->charset()->number;
271
 
 
272
 
      if (field->null_ptr)
273
 
      {
274
 
        keydef[i].seg[j].null_bit= field->null_bit;
275
 
        keydef[i].seg[j].null_pos= (uint) (field->null_ptr-
276
 
                                           (unsigned char*) table_arg->getInsertRecord());
277
 
      }
278
 
      else
279
 
      {
280
 
        keydef[i].seg[j].null_bit= 0;
281
 
        keydef[i].seg[j].null_pos= 0;
282
 
      }
283
 
      if (field->type() == DRIZZLE_TYPE_BLOB)
284
 
      {
285
 
        keydef[i].seg[j].flag|= HA_BLOB_PART;
286
 
        /* save number of bytes used to pack length */
287
 
        keydef[i].seg[j].bit_start= (uint) (field->pack_length() -
288
 
                                            share->blob_ptr_size);
289
 
      }
290
 
    }
291
 
    keyseg+= pos->key_parts;
292
 
  }
293
 
  if (table_arg->found_next_number_field)
294
 
    keydef[share->next_number_index].flag|= HA_AUTO_KEY;
295
 
  record= table_arg->getInsertRecord();
296
 
  recpos= 0;
297
 
  recinfo_pos= recinfo;
298
 
  while (recpos < (uint) share->stored_rec_length)
299
 
  {
300
 
    Field **field, *found= 0;
301
 
    minpos= share->getRecordLength();
302
 
    length= 0;
303
 
 
304
 
    for (field= table_arg->getFields(); *field; field++)
305
 
    {
306
 
      if ((fieldpos= (*field)->offset(record)) >= recpos &&
307
 
          fieldpos <= minpos)
308
 
      {
309
 
        /* skip null fields */
310
 
        if (!(temp_length= (*field)->pack_length_in_rec()))
311
 
          continue; /* Skip null-fields */
312
 
        if (! found || fieldpos < minpos ||
313
 
            (fieldpos == minpos && temp_length < length))
314
 
        {
315
 
          minpos= fieldpos;
316
 
          found= *field;
317
 
          length= temp_length;
318
 
        }
319
 
      }
320
 
    }
321
 
    if (recpos != minpos)
322
 
    { // Reserved space (Null bits?)
323
 
      memset(recinfo_pos, 0, sizeof(*recinfo_pos));
324
 
      recinfo_pos->type= (int) FIELD_NORMAL;
325
 
      recinfo_pos++->length= (uint16_t) (minpos - recpos);
326
 
    }
327
 
    if (!found)
328
 
      break;
329
 
 
330
 
    if (found->flags & BLOB_FLAG)
331
 
      recinfo_pos->type= (int) FIELD_BLOB;
332
 
    else if (found->type() == DRIZZLE_TYPE_VARCHAR)
333
 
      recinfo_pos->type= FIELD_VARCHAR;
334
 
    else if (!(options & HA_OPTION_PACK_RECORD))
335
 
      recinfo_pos->type= (int) FIELD_NORMAL;
336
 
    else if (found->zero_pack())
337
 
      recinfo_pos->type= (int) FIELD_SKIP_ZERO;
338
 
    else
339
 
      recinfo_pos->type= (int) ((length <= 3) ?  FIELD_NORMAL : FIELD_SKIP_PRESPACE);
340
 
    if (found->null_ptr)
341
 
    {
342
 
      recinfo_pos->null_bit= found->null_bit;
343
 
      recinfo_pos->null_pos= (uint) (found->null_ptr -
344
 
                                     (unsigned char*) table_arg->getInsertRecord());
345
 
    }
346
 
    else
347
 
    {
348
 
      recinfo_pos->null_bit= 0;
349
 
      recinfo_pos->null_pos= 0;
350
 
    }
351
 
    (recinfo_pos++)->length= (uint16_t) length;
352
 
    recpos= minpos + length;
353
 
  }
354
 
  *records_out= (uint) (recinfo_pos - recinfo);
355
 
  return(0);
356
 
}
357
 
 
358
 
int ha_myisam::reset_auto_increment(uint64_t value)
359
 
{
360
 
  file->s->state.auto_increment= value;
361
 
  return 0;
362
 
}
363
 
 
364
 
/*
365
 
  Check for underlying table conformance
366
 
 
367
 
  SYNOPSIS
368
 
    check_definition()
369
 
      t1_keyinfo       in    First table key definition
370
 
      t1_recinfo       in    First table record definition
371
 
      t1_keys          in    Number of keys in first table
372
 
      t1_recs          in    Number of records in first table
373
 
      t2_keyinfo       in    Second table key definition
374
 
      t2_recinfo       in    Second table record definition
375
 
      t2_keys          in    Number of keys in second table
376
 
      t2_recs          in    Number of records in second table
377
 
      strict           in    Strict check switch
378
 
 
379
 
  DESCRIPTION
380
 
    This function compares two MyISAM definitions. By intention it was done
381
 
    to compare merge table definition against underlying table definition.
382
 
    It may also be used to compare dot-frm and MYI definitions of MyISAM
383
 
    table as well to compare different MyISAM table definitions.
384
 
 
385
 
    For merge table it is not required that number of keys in merge table
386
 
    must exactly match number of keys in underlying table. When calling this
387
 
    function for underlying table conformance check, 'strict' flag must be
388
 
    set to false, and converted merge definition must be passed as t1_*.
389
 
 
390
 
    Otherwise 'strict' flag must be set to 1 and it is not required to pass
391
 
    converted dot-frm definition as t1_*.
392
 
 
393
 
  RETURN VALUE
394
 
    0 - Equal definitions.
395
 
    1 - Different definitions.
396
 
 
397
 
  TODO
398
 
    - compare FULLTEXT keys;
399
 
    - compare SPATIAL keys;
400
 
    - compare FIELD_SKIP_ZERO which is converted to FIELD_NORMAL correctly
401
 
      (should be corretly detected in table2myisam).
402
 
*/
403
 
 
404
 
static int check_definition(MI_KEYDEF *t1_keyinfo, MI_COLUMNDEF *t1_recinfo,
405
 
                            uint32_t t1_keys, uint32_t t1_recs,
406
 
                            MI_KEYDEF *t2_keyinfo, MI_COLUMNDEF *t2_recinfo,
407
 
                            uint32_t t2_keys, uint32_t t2_recs, bool strict)
408
 
{
409
 
  uint32_t i, j;
410
 
  if ((strict ? t1_keys != t2_keys : t1_keys > t2_keys))
411
 
  {
412
 
    return(1);
413
 
  }
414
 
  if (t1_recs != t2_recs)
415
 
  {
416
 
    return(1);
417
 
  }
418
 
  for (i= 0; i < t1_keys; i++)
419
 
  {
420
 
    HA_KEYSEG *t1_keysegs= t1_keyinfo[i].seg;
421
 
    HA_KEYSEG *t2_keysegs= t2_keyinfo[i].seg;
422
 
    if (t1_keyinfo[i].keysegs != t2_keyinfo[i].keysegs ||
423
 
        t1_keyinfo[i].key_alg != t2_keyinfo[i].key_alg)
424
 
    {
425
 
      return(1);
426
 
    }
427
 
    for (j=  t1_keyinfo[i].keysegs; j--;)
428
 
    {
429
 
      uint8_t t1_keysegs_j__type= t1_keysegs[j].type;
430
 
 
431
 
      /*
432
 
        Table migration from 4.1 to 5.1. In 5.1 a *TEXT key part is
433
 
        always HA_KEYTYPE_VARTEXT2. In 4.1 we had only the equivalent of
434
 
        HA_KEYTYPE_VARTEXT1. Since we treat both the same on MyISAM
435
 
        level, we can ignore a mismatch between these types.
436
 
      */
437
 
      if ((t1_keysegs[j].flag & HA_BLOB_PART) &&
438
 
          (t2_keysegs[j].flag & HA_BLOB_PART))
439
 
      {
440
 
        if ((t1_keysegs_j__type == HA_KEYTYPE_VARTEXT2) &&
441
 
            (t2_keysegs[j].type == HA_KEYTYPE_VARTEXT1))
442
 
          t1_keysegs_j__type= HA_KEYTYPE_VARTEXT1;
443
 
        else if ((t1_keysegs_j__type == HA_KEYTYPE_VARBINARY2) &&
444
 
                 (t2_keysegs[j].type == HA_KEYTYPE_VARBINARY1))
445
 
          t1_keysegs_j__type= HA_KEYTYPE_VARBINARY1;
446
 
      }
447
 
 
448
 
      if (t1_keysegs_j__type != t2_keysegs[j].type ||
449
 
          t1_keysegs[j].language != t2_keysegs[j].language ||
450
 
          t1_keysegs[j].null_bit != t2_keysegs[j].null_bit ||
451
 
          t1_keysegs[j].length != t2_keysegs[j].length)
452
 
      {
453
 
        return(1);
454
 
      }
455
 
    }
456
 
  }
457
 
  for (i= 0; i < t1_recs; i++)
458
 
  {
459
 
    MI_COLUMNDEF *t1_rec= &t1_recinfo[i];
460
 
    MI_COLUMNDEF *t2_rec= &t2_recinfo[i];
461
 
    /*
462
 
      FIELD_SKIP_ZERO can be changed to FIELD_NORMAL in mi_create,
463
 
      see NOTE1 in mi_create.c
464
 
    */
465
 
    if ((t1_rec->type != t2_rec->type &&
466
 
         !(t1_rec->type == (int) FIELD_SKIP_ZERO &&
467
 
           t1_rec->length == 1 &&
468
 
           t2_rec->type == (int) FIELD_NORMAL)) ||
469
 
        t1_rec->length != t2_rec->length ||
470
 
        t1_rec->null_bit != t2_rec->null_bit)
471
 
    {
472
 
      return(1);
473
 
    }
474
 
  }
475
 
  return(0);
476
 
}
477
 
 
478
 
 
479
 
volatile int *killed_ptr(MI_CHECK *param)
480
 
{
481
 
  /* In theory Unsafe conversion, but should be ok for now */
482
 
  return (int*) (((Session *)(param->session))->getKilledPtr());
483
 
}
484
 
 
485
 
void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
486
 
{
487
 
  param->error_printed|=1;
488
 
  param->out_flag|= O_DATA_LOST;
489
 
  va_list args;
490
 
  va_start(args, fmt);
491
 
  mi_check_print_msg(param, "error", fmt, args);
492
 
  va_end(args);
493
 
}
494
 
 
495
 
void mi_check_print_info(MI_CHECK *param, const char *fmt,...)
496
 
{
497
 
  va_list args;
498
 
  va_start(args, fmt);
499
 
  mi_check_print_msg(param, "info", fmt, args);
500
 
  va_end(args);
501
 
}
502
 
 
503
 
void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
504
 
{
505
 
  param->warning_printed=1;
506
 
  param->out_flag|= O_DATA_LOST;
507
 
  va_list args;
508
 
  va_start(args, fmt);
509
 
  mi_check_print_msg(param, "warning", fmt, args);
510
 
  va_end(args);
511
 
}
512
 
 
513
 
/**
514
 
  Report list of threads (and queries) accessing a table, thread_id of a
515
 
  thread that detected corruption, ource file name and line number where
516
 
  this corruption was detected, optional extra information (string).
517
 
 
518
 
  This function is intended to be used when table corruption is detected.
519
 
 
520
 
  @param[in] file      MI_INFO object.
521
 
  @param[in] message   Optional error message.
522
 
  @param[in] sfile     Name of source file.
523
 
  @param[in] sline     Line number in source file.
524
 
 
525
 
  @return void
526
 
*/
527
 
 
528
 
void _mi_report_crashed(MI_INFO *file, const char *message,
529
 
                        const char *sfile, uint32_t sline)
530
 
{
531
 
  Session *cur_session;
532
 
  if ((cur_session= file->in_use))
533
 
    errmsg_printf(ERRMSG_LVL_ERROR, _("Got an error from thread_id=%"PRIu64", %s:%d"),
534
 
                    cur_session->thread_id,
535
 
                    sfile, sline);
536
 
  else
537
 
    errmsg_printf(ERRMSG_LVL_ERROR, _("Got an error from unknown thread, %s:%d"), sfile, sline);
538
 
  if (message)
539
 
    errmsg_printf(ERRMSG_LVL_ERROR, "%s", message);
540
 
  list<Session *>::iterator it= file->s->in_use->begin();
541
 
  while (it != file->s->in_use->end())
542
 
  {
543
 
    errmsg_printf(ERRMSG_LVL_ERROR, "%s", _("Unknown thread accessing table"));
544
 
    ++it;
545
 
  }
546
 
}
547
 
 
548
 
ha_myisam::ha_myisam(plugin::StorageEngine &engine_arg,
549
 
                     Table &table_arg)
550
 
  : Cursor(engine_arg, table_arg),
551
 
  file(0),
552
 
  can_enable_indexes(true),
553
 
  is_ordered(true)
554
 
{ }
555
 
 
556
 
Cursor *ha_myisam::clone(memory::Root *mem_root)
557
 
{
558
 
  ha_myisam *new_handler= static_cast <ha_myisam *>(Cursor::clone(mem_root));
559
 
  if (new_handler)
560
 
    new_handler->file->state= file->state;
561
 
  return new_handler;
562
 
}
563
 
 
564
 
const char *ha_myisam::index_type(uint32_t )
565
 
{
566
 
  return "BTREE";
567
 
}
568
 
 
569
 
/* Name is here without an extension */
570
 
int ha_myisam::doOpen(const drizzled::TableIdentifier &identifier, int mode, uint32_t test_if_locked)
571
 
{
572
 
  MI_KEYDEF *keyinfo;
573
 
  MI_COLUMNDEF *recinfo= 0;
574
 
  uint32_t recs;
575
 
  uint32_t i;
576
 
 
577
 
  /*
578
 
    If the user wants to have memory mapped data files, add an
579
 
    open_flag. Do not memory map temporary tables because they are
580
 
    expected to be inserted and thus extended a lot. Memory mapping is
581
 
    efficient for files that keep their size, but very inefficient for
582
 
    growing files. Using an open_flag instead of calling mi_extra(...
583
 
    HA_EXTRA_MMAP ...) after mi_open() has the advantage that the
584
 
    mapping is not repeated for every open, but just done on the initial
585
 
    open, when the MyISAM share is created. Everytime the server
586
 
    requires to open a new instance of a table it calls this method. We
587
 
    will always supply HA_OPEN_MMAP for a permanent table. However, the
588
 
    MyISAM storage engine will ignore this flag if this is a secondary
589
 
    open of a table that is in use by other threads already (if the
590
 
    MyISAM share exists already).
591
 
  */
592
 
  if (!(file= mi_open(identifier, mode, test_if_locked)))
593
 
    return (errno ? errno : -1);
594
 
 
595
 
  if (!getTable()->getShare()->getType()) /* No need to perform a check for tmp table */
596
 
  {
597
 
    if ((errno= table2myisam(getTable(), &keyinfo, &recinfo, &recs)))
598
 
    {
599
 
      goto err;
600
 
    }
601
 
    if (check_definition(keyinfo, recinfo, getTable()->getShare()->sizeKeys(), recs,
602
 
                         file->s->keyinfo, file->s->rec,
603
 
                         file->s->base.keys, file->s->base.fields, true))
604
 
    {
605
 
      errno= HA_ERR_CRASHED;
606
 
      goto err;
607
 
    }
608
 
  }
609
 
 
610
 
  assert(test_if_locked);
611
 
  if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE))
612
 
    mi_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0);
613
 
 
614
 
  info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
615
 
  if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
616
 
    mi_extra(file, HA_EXTRA_WAIT_LOCK, 0);
617
 
  if (!getTable()->getShare()->db_record_offset)
618
 
    is_ordered= false;
619
 
 
620
 
 
621
 
  keys_with_parts.reset();
622
 
  for (i= 0; i < getTable()->getShare()->sizeKeys(); i++)
623
 
  {
624
 
    getTable()->key_info[i].block_size= file->s->keyinfo[i].block_length;
625
 
 
626
 
    KeyPartInfo *kp= getTable()->key_info[i].key_part;
627
 
    KeyPartInfo *kp_end= kp + getTable()->key_info[i].key_parts;
628
 
    for (; kp != kp_end; kp++)
629
 
    {
630
 
      if (!kp->field->part_of_key.test(i))
631
 
      {
632
 
        keys_with_parts.set(i);
633
 
        break;
634
 
      }
635
 
    }
636
 
  }
637
 
  errno= 0;
638
 
  goto end;
639
 
 err:
640
 
  this->close();
641
 
 end:
642
 
  /*
643
 
    Both recinfo and keydef are allocated by multi_malloc(), thus only
644
 
    recinfo must be freed.
645
 
  */
646
 
  if (recinfo)
647
 
    free((unsigned char*) recinfo);
648
 
  return errno;
649
 
}
650
 
 
651
 
int ha_myisam::close(void)
652
 
{
653
 
  MI_INFO *tmp=file;
654
 
  file=0;
655
 
  return mi_close(tmp);
656
 
}
657
 
 
658
 
int ha_myisam::doInsertRecord(unsigned char *buf)
659
 
{
660
 
  /*
661
 
    If we have an auto_increment column and we are writing a changed row
662
 
    or a new row, then update the auto_increment value in the record.
663
 
  */
664
 
  if (getTable()->next_number_field && buf == getTable()->getInsertRecord())
665
 
  {
666
 
    int error;
667
 
    if ((error= update_auto_increment()))
668
 
      return error;
669
 
  }
670
 
  return mi_write(file,buf);
671
 
}
672
 
 
673
 
 
674
 
int ha_myisam::repair(Session *session, MI_CHECK &param, bool do_optimize)
675
 
{
676
 
  int error=0;
677
 
  uint32_t local_testflag= param.testflag;
678
 
  bool optimize_done= !do_optimize, statistics_done=0;
679
 
  const char *old_proc_info= session->get_proc_info();
680
 
  char fixed_name[FN_REFLEN];
681
 
  MYISAM_SHARE* share = file->s;
682
 
  ha_rows rows= file->state->records;
683
 
 
684
 
  /*
685
 
    Normally this method is entered with a properly opened table. If the
686
 
    repair fails, it can be repeated with more elaborate options. Under
687
 
    special circumstances it can happen that a repair fails so that it
688
 
    closed the data file and cannot re-open it. In this case file->dfile
689
 
    is set to -1. We must not try another repair without an open data
690
 
    file. (Bug #25289)
691
 
  */
692
 
  if (file->dfile == -1)
693
 
  {
694
 
    errmsg_printf(ERRMSG_LVL_INFO, "Retrying repair of: '%s' failed. "
695
 
                          "Please try REPAIR EXTENDED or myisamchk",
696
 
                          getTable()->getShare()->getPath());
697
 
    return(HA_ADMIN_FAILED);
698
 
  }
699
 
 
700
 
  param.db_name=    getTable()->getShare()->getSchemaName();
701
 
  param.table_name= getTable()->getAlias();
702
 
  param.tmpfile_createflag = O_RDWR | O_TRUNC;
703
 
  param.using_global_keycache = 1;
704
 
  param.session= session;
705
 
  param.out_flag= 0;
706
 
  param.sort_buffer_length= static_cast<size_t>(sort_buffer_size);
707
 
  strcpy(fixed_name,file->filename);
708
 
 
709
 
  // Don't lock tables if we have used LOCK Table
710
 
  if (mi_lock_database(file, getTable()->getShare()->getType() ? F_EXTRA_LCK : F_WRLCK))
711
 
  {
712
 
    mi_check_print_error(&param,ER(ER_CANT_LOCK),errno);
713
 
    return(HA_ADMIN_FAILED);
714
 
  }
715
 
 
716
 
  if (!do_optimize ||
717
 
      ((file->state->del || share->state.split != file->state->records) &&
718
 
       (!(param.testflag & T_QUICK) ||
719
 
        !(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))))
720
 
  {
721
 
    uint64_t key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ?
722
 
                        mi_get_mask_all_keys_active(share->base.keys) :
723
 
                        share->state.key_map);
724
 
    uint32_t testflag=param.testflag;
725
 
    if (mi_test_if_sort_rep(file,file->state->records,key_map,0) &&
726
 
        (local_testflag & T_REP_BY_SORT))
727
 
    {
728
 
      local_testflag|= T_STATISTICS;
729
 
      param.testflag|= T_STATISTICS;            // We get this for free
730
 
      statistics_done=1;
731
 
      {
732
 
        session->set_proc_info("Repair by sorting");
733
 
        error = mi_repair_by_sort(&param, file, fixed_name,
734
 
            param.testflag & T_QUICK);
735
 
      }
736
 
    }
737
 
    else
738
 
    {
739
 
      session->set_proc_info("Repair with keycache");
740
 
      param.testflag &= ~T_REP_BY_SORT;
741
 
      error=  mi_repair(&param, file, fixed_name,
742
 
                        param.testflag & T_QUICK);
743
 
    }
744
 
    param.testflag=testflag;
745
 
    optimize_done=1;
746
 
  }
747
 
  if (!error)
748
 
  {
749
 
    if ((local_testflag & T_SORT_INDEX) &&
750
 
        (share->state.changed & STATE_NOT_SORTED_PAGES))
751
 
    {
752
 
      optimize_done=1;
753
 
      session->set_proc_info("Sorting index");
754
 
      error=mi_sort_index(&param,file,fixed_name);
755
 
    }
756
 
    if (!statistics_done && (local_testflag & T_STATISTICS))
757
 
    {
758
 
      if (share->state.changed & STATE_NOT_ANALYZED)
759
 
      {
760
 
        optimize_done=1;
761
 
        session->set_proc_info("Analyzing");
762
 
        error = chk_key(&param, file);
763
 
      }
764
 
      else
765
 
        local_testflag&= ~T_STATISTICS;         // Don't update statistics
766
 
    }
767
 
  }
768
 
  session->set_proc_info("Saving state");
769
 
  if (!error)
770
 
  {
771
 
    if ((share->state.changed & STATE_CHANGED) || mi_is_crashed(file))
772
 
    {
773
 
      share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
774
 
                               STATE_CRASHED_ON_REPAIR);
775
 
      file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
776
 
    }
777
 
    /*
778
 
      the following 'if', thought conceptually wrong,
779
 
      is a useful optimization nevertheless.
780
 
    */
781
 
    if (file->state != &file->s->state.state)
782
 
      file->s->state.state = *file->state;
783
 
    if (file->s->base.auto_key)
784
 
      update_auto_increment_key(&param, file, 1);
785
 
    if (optimize_done)
786
 
      error = update_state_info(&param, file,
787
 
                                UPDATE_TIME | UPDATE_OPEN_COUNT |
788
 
                                (local_testflag &
789
 
                                 T_STATISTICS ? UPDATE_STAT : 0));
790
 
    info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
791
 
         HA_STATUS_CONST);
792
 
    if (rows != file->state->records && ! (param.testflag & T_VERY_SILENT))
793
 
    {
794
 
      char llbuff[22],llbuff2[22];
795
 
      mi_check_print_warning(&param,"Number of rows changed from %s to %s",
796
 
                             internal::llstr(rows,llbuff),
797
 
                             internal::llstr(file->state->records,llbuff2));
798
 
    }
799
 
  }
800
 
  else
801
 
  {
802
 
    mi_mark_crashed_on_repair(file);
803
 
    file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
804
 
    update_state_info(&param, file, 0);
805
 
  }
806
 
  session->set_proc_info(old_proc_info);
807
 
  mi_lock_database(file,F_UNLCK);
808
 
 
809
 
  return(error ? HA_ADMIN_FAILED :
810
 
              !optimize_done ? HA_ADMIN_ALREADY_DONE : HA_ADMIN_OK);
811
 
}
812
 
 
813
 
 
814
 
/*
815
 
  Disable indexes, making it persistent if requested.
816
 
 
817
 
  SYNOPSIS
818
 
    disable_indexes()
819
 
    mode        mode of operation:
820
 
                HA_KEY_SWITCH_NONUNIQ      disable all non-unique keys
821
 
                HA_KEY_SWITCH_ALL          disable all keys
822
 
                HA_KEY_SWITCH_NONUNIQ_SAVE dis. non-uni. and make persistent
823
 
                HA_KEY_SWITCH_ALL_SAVE     dis. all keys and make persistent
824
 
 
825
 
  IMPLEMENTATION
826
 
    HA_KEY_SWITCH_NONUNIQ       is not implemented.
827
 
    HA_KEY_SWITCH_ALL_SAVE      is not implemented.
828
 
 
829
 
  RETURN
830
 
    0  ok
831
 
    HA_ERR_WRONG_COMMAND  mode not implemented.
832
 
*/
833
 
 
834
 
int ha_myisam::disable_indexes(uint32_t mode)
835
 
{
836
 
  int error;
837
 
 
838
 
  if (mode == HA_KEY_SWITCH_ALL)
839
 
  {
840
 
    /* call a storage engine function to switch the key map */
841
 
    error= mi_disable_indexes(file);
842
 
  }
843
 
  else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
844
 
  {
845
 
    mi_extra(file, HA_EXTRA_NO_KEYS, 0);
846
 
    info(HA_STATUS_CONST);                        // Read new key info
847
 
    error= 0;
848
 
  }
849
 
  else
850
 
  {
851
 
    /* mode not implemented */
852
 
    error= HA_ERR_WRONG_COMMAND;
853
 
  }
854
 
  return error;
855
 
}
856
 
 
857
 
 
858
 
/*
859
 
  Enable indexes, making it persistent if requested.
860
 
 
861
 
  SYNOPSIS
862
 
    enable_indexes()
863
 
    mode        mode of operation:
864
 
                HA_KEY_SWITCH_NONUNIQ      enable all non-unique keys
865
 
                HA_KEY_SWITCH_ALL          enable all keys
866
 
                HA_KEY_SWITCH_NONUNIQ_SAVE en. non-uni. and make persistent
867
 
                HA_KEY_SWITCH_ALL_SAVE     en. all keys and make persistent
868
 
 
869
 
  DESCRIPTION
870
 
    Enable indexes, which might have been disabled by disable_index() before.
871
 
    The modes without _SAVE work only if both data and indexes are empty,
872
 
    since the MyISAM repair would enable them persistently.
873
 
    To be sure in these cases, call Cursor::delete_all_rows() before.
874
 
 
875
 
  IMPLEMENTATION
876
 
    HA_KEY_SWITCH_NONUNIQ       is not implemented.
877
 
    HA_KEY_SWITCH_ALL_SAVE      is not implemented.
878
 
 
879
 
  RETURN
880
 
    0  ok
881
 
    !=0  Error, among others:
882
 
    HA_ERR_CRASHED  data or index is non-empty. Delete all rows and retry.
883
 
    HA_ERR_WRONG_COMMAND  mode not implemented.
884
 
*/
885
 
 
886
 
int ha_myisam::enable_indexes(uint32_t mode)
887
 
{
888
 
  int error;
889
 
 
890
 
  if (mi_is_all_keys_active(file->s->state.key_map, file->s->base.keys))
891
 
  {
892
 
    /* All indexes are enabled already. */
893
 
    return 0;
894
 
  }
895
 
 
896
 
  if (mode == HA_KEY_SWITCH_ALL)
897
 
  {
898
 
    error= mi_enable_indexes(file);
899
 
    /*
900
 
       Do not try to repair on error,
901
 
       as this could make the enabled state persistent,
902
 
       but mode==HA_KEY_SWITCH_ALL forbids it.
903
 
    */
904
 
  }
905
 
  else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
906
 
  {
907
 
    Session *session= getTable()->in_use;
908
 
    boost::scoped_ptr<MI_CHECK> param_ap(new MI_CHECK);
909
 
    MI_CHECK &param= *param_ap.get();
910
 
    const char *save_proc_info= session->get_proc_info();
911
 
    session->set_proc_info("Creating index");
912
 
    myisamchk_init(&param);
913
 
    param.op_name= "recreating_index";
914
 
    param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
915
 
                     T_CREATE_MISSING_KEYS);
916
 
    param.myf_rw&= ~MY_WAIT_IF_FULL;
917
 
    param.sort_buffer_length=  static_cast<size_t>(sort_buffer_size);
918
 
    param.stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
919
 
    if ((error= (repair(session,param,0) != HA_ADMIN_OK)) && param.retry_repair)
920
 
    {
921
 
      errmsg_printf(ERRMSG_LVL_WARN, "Warning: Enabling keys got errno %d on %s.%s, retrying",
922
 
                        errno, param.db_name, param.table_name);
923
 
      /* Repairing by sort failed. Now try standard repair method. */
924
 
      param.testflag&= ~(T_REP_BY_SORT | T_QUICK);
925
 
      error= (repair(session,param,0) != HA_ADMIN_OK);
926
 
      /*
927
 
        If the standard repair succeeded, clear all error messages which
928
 
        might have been set by the first repair. They can still be seen
929
 
        with SHOW WARNINGS then.
930
 
      */
931
 
      if (! error)
932
 
        session->clear_error();
933
 
    }
934
 
    info(HA_STATUS_CONST);
935
 
    session->set_proc_info(save_proc_info);
936
 
  }
937
 
  else
938
 
  {
939
 
    /* mode not implemented */
940
 
    error= HA_ERR_WRONG_COMMAND;
941
 
  }
942
 
  return error;
943
 
}
944
 
 
945
 
 
946
 
/*
947
 
  Test if indexes are disabled.
948
 
 
949
 
 
950
 
  SYNOPSIS
951
 
    indexes_are_disabled()
952
 
      no parameters
953
 
 
954
 
 
955
 
  RETURN
956
 
    0  indexes are not disabled
957
 
    1  all indexes are disabled
958
 
   [2  non-unique indexes are disabled - NOT YET IMPLEMENTED]
959
 
*/
960
 
 
961
 
int ha_myisam::indexes_are_disabled(void)
962
 
{
963
 
 
964
 
  return mi_indexes_are_disabled(file);
965
 
}
966
 
 
967
 
 
968
 
/*
969
 
  prepare for a many-rows insert operation
970
 
  e.g. - disable indexes (if they can be recreated fast) or
971
 
  activate special bulk-insert optimizations
972
 
 
973
 
  SYNOPSIS
974
 
    start_bulk_insert(rows)
975
 
    rows        Rows to be inserted
976
 
                0 if we don't know
977
 
 
978
 
  NOTICE
979
 
    Do not forget to call end_bulk_insert() later!
980
 
*/
981
 
 
982
 
void ha_myisam::start_bulk_insert(ha_rows rows)
983
 
{
984
 
  Session *session= getTable()->in_use;
985
 
  ulong size= session->variables.read_buff_size;
986
 
 
987
 
  /* don't enable row cache if too few rows */
988
 
  if (! rows || (rows > MI_MIN_ROWS_TO_USE_WRITE_CACHE))
989
 
    mi_extra(file, HA_EXTRA_WRITE_CACHE, (void*) &size);
990
 
 
991
 
  can_enable_indexes= mi_is_all_keys_active(file->s->state.key_map,
992
 
                                            file->s->base.keys);
993
 
 
994
 
  /*
995
 
    Only disable old index if the table was empty and we are inserting
996
 
    a lot of rows.
997
 
    We should not do this for only a few rows as this is slower and
998
 
    we don't want to update the key statistics based of only a few rows.
999
 
  */
1000
 
  if (file->state->records == 0 && can_enable_indexes &&
1001
 
      (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES))
1002
 
    mi_disable_non_unique_index(file,rows);
1003
 
  else
1004
 
    if (!file->bulk_insert &&
1005
 
        (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT))
1006
 
    {
1007
 
      mi_init_bulk_insert(file,
1008
 
                          (size_t)session->variables.bulk_insert_buff_size,
1009
 
                          rows);
1010
 
    }
1011
 
}
1012
 
 
1013
 
/*
1014
 
  end special bulk-insert optimizations,
1015
 
  which have been activated by start_bulk_insert().
1016
 
 
1017
 
  SYNOPSIS
1018
 
    end_bulk_insert()
1019
 
    no arguments
1020
 
 
1021
 
  RETURN
1022
 
    0     OK
1023
 
    != 0  Error
1024
 
*/
1025
 
 
1026
 
int ha_myisam::end_bulk_insert()
1027
 
{
1028
 
  mi_end_bulk_insert(file);
1029
 
  int err=mi_extra(file, HA_EXTRA_NO_CACHE, 0);
1030
 
  return err ? err : can_enable_indexes ?
1031
 
                     enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE) : 0;
1032
 
}
1033
 
 
1034
 
 
1035
 
 
1036
 
int ha_myisam::doUpdateRecord(const unsigned char *old_data, unsigned char *new_data)
1037
 
{
1038
 
  return mi_update(file,old_data,new_data);
1039
 
}
1040
 
 
1041
 
int ha_myisam::doDeleteRecord(const unsigned char *buf)
1042
 
{
1043
 
  return mi_delete(file,buf);
1044
 
}
1045
 
 
1046
 
 
1047
 
int ha_myisam::doStartIndexScan(uint32_t idx, bool )
1048
 
{
1049
 
  active_index=idx;
1050
 
  //in_range_read= false;
1051
 
  return 0;
1052
 
}
1053
 
 
1054
 
 
1055
 
int ha_myisam::doEndIndexScan()
1056
 
{
1057
 
  active_index=MAX_KEY;
1058
 
  return 0;
1059
 
}
1060
 
 
1061
 
 
1062
 
int ha_myisam::index_read_map(unsigned char *buf, const unsigned char *key,
1063
 
                              key_part_map keypart_map,
1064
 
                              enum ha_rkey_function find_flag)
1065
 
{
1066
 
  assert(inited==INDEX);
1067
 
  ha_statistic_increment(&system_status_var::ha_read_key_count);
1068
 
  int error=mi_rkey(file, buf, active_index, key, keypart_map, find_flag);
1069
 
  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1070
 
  return error;
1071
 
}
1072
 
 
1073
 
int ha_myisam::index_read_idx_map(unsigned char *buf, uint32_t index, const unsigned char *key,
1074
 
                                  key_part_map keypart_map,
1075
 
                                  enum ha_rkey_function find_flag)
1076
 
{
1077
 
  ha_statistic_increment(&system_status_var::ha_read_key_count);
1078
 
  int error=mi_rkey(file, buf, index, key, keypart_map, find_flag);
1079
 
  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1080
 
  return error;
1081
 
}
1082
 
 
1083
 
int ha_myisam::index_read_last_map(unsigned char *buf, const unsigned char *key,
1084
 
                                   key_part_map keypart_map)
1085
 
{
1086
 
  assert(inited==INDEX);
1087
 
  ha_statistic_increment(&system_status_var::ha_read_key_count);
1088
 
  int error=mi_rkey(file, buf, active_index, key, keypart_map,
1089
 
                    HA_READ_PREFIX_LAST);
1090
 
  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1091
 
  return(error);
1092
 
}
1093
 
 
1094
 
int ha_myisam::index_next(unsigned char *buf)
1095
 
{
1096
 
  assert(inited==INDEX);
1097
 
  ha_statistic_increment(&system_status_var::ha_read_next_count);
1098
 
  int error=mi_rnext(file,buf,active_index);
1099
 
  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1100
 
  return error;
1101
 
}
1102
 
 
1103
 
int ha_myisam::index_prev(unsigned char *buf)
1104
 
{
1105
 
  assert(inited==INDEX);
1106
 
  ha_statistic_increment(&system_status_var::ha_read_prev_count);
1107
 
  int error=mi_rprev(file,buf, active_index);
1108
 
  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1109
 
  return error;
1110
 
}
1111
 
 
1112
 
int ha_myisam::index_first(unsigned char *buf)
1113
 
{
1114
 
  assert(inited==INDEX);
1115
 
  ha_statistic_increment(&system_status_var::ha_read_first_count);
1116
 
  int error=mi_rfirst(file, buf, active_index);
1117
 
  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1118
 
  return error;
1119
 
}
1120
 
 
1121
 
int ha_myisam::index_last(unsigned char *buf)
1122
 
{
1123
 
  assert(inited==INDEX);
1124
 
  ha_statistic_increment(&system_status_var::ha_read_last_count);
1125
 
  int error=mi_rlast(file, buf, active_index);
1126
 
  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1127
 
  return error;
1128
 
}
1129
 
 
1130
 
int ha_myisam::index_next_same(unsigned char *buf,
1131
 
                               const unsigned char *,
1132
 
                               uint32_t )
1133
 
{
1134
 
  int error;
1135
 
  assert(inited==INDEX);
1136
 
  ha_statistic_increment(&system_status_var::ha_read_next_count);
1137
 
  do
1138
 
  {
1139
 
    error= mi_rnext_same(file,buf);
1140
 
  } while (error == HA_ERR_RECORD_DELETED);
1141
 
  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1142
 
  return error;
1143
 
}
1144
 
 
1145
 
int ha_myisam::read_range_first(const key_range *start_key,
1146
 
                                const key_range *end_key,
1147
 
                                bool eq_range_arg,
1148
 
                                bool sorted /* ignored */)
1149
 
{
1150
 
  int res;
1151
 
  //if (!eq_range_arg)
1152
 
  //  in_range_read= true;
1153
 
 
1154
 
  res= Cursor::read_range_first(start_key, end_key, eq_range_arg, sorted);
1155
 
 
1156
 
  //if (res)
1157
 
  //  in_range_read= false;
1158
 
  return res;
1159
 
}
1160
 
 
1161
 
 
1162
 
int ha_myisam::read_range_next()
1163
 
{
1164
 
  int res= Cursor::read_range_next();
1165
 
  //if (res)
1166
 
  //  in_range_read= false;
1167
 
  return res;
1168
 
}
1169
 
 
1170
 
 
1171
 
int ha_myisam::doStartTableScan(bool scan)
1172
 
{
1173
 
  if (scan)
1174
 
    return mi_scan_init(file);
1175
 
  return mi_reset(file);                        // Free buffers
1176
 
}
1177
 
 
1178
 
int ha_myisam::rnd_next(unsigned char *buf)
1179
 
{
1180
 
  ha_statistic_increment(&system_status_var::ha_read_rnd_next_count);
1181
 
  int error=mi_scan(file, buf);
1182
 
  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1183
 
  return error;
1184
 
}
1185
 
 
1186
 
int ha_myisam::rnd_pos(unsigned char *buf, unsigned char *pos)
1187
 
{
1188
 
  ha_statistic_increment(&system_status_var::ha_read_rnd_count);
1189
 
  int error=mi_rrnd(file, buf, internal::my_get_ptr(pos,ref_length));
1190
 
  getTable()->status=error ? STATUS_NOT_FOUND: 0;
1191
 
  return error;
1192
 
}
1193
 
 
1194
 
 
1195
 
void ha_myisam::position(const unsigned char *)
1196
 
{
1197
 
  internal::my_off_t row_position= mi_position(file);
1198
 
  internal::my_store_ptr(ref, ref_length, row_position);
1199
 
}
1200
 
 
1201
 
int ha_myisam::info(uint32_t flag)
1202
 
{
1203
 
  MI_ISAMINFO misam_info;
1204
 
  char name_buff[FN_REFLEN];
1205
 
 
1206
 
  (void) mi_status(file,&misam_info,flag);
1207
 
  if (flag & HA_STATUS_VARIABLE)
1208
 
  {
1209
 
    stats.records=           misam_info.records;
1210
 
    stats.deleted=           misam_info.deleted;
1211
 
    stats.data_file_length=  misam_info.data_file_length;
1212
 
    stats.index_file_length= misam_info.index_file_length;
1213
 
    stats.delete_length=     misam_info.delete_length;
1214
 
    stats.check_time=        misam_info.check_time;
1215
 
    stats.mean_rec_length=   misam_info.mean_reclength;
1216
 
  }
1217
 
  if (flag & HA_STATUS_CONST)
1218
 
  {
1219
 
    TableShare *share= getTable()->getMutableShare();
1220
 
    stats.max_data_file_length=  misam_info.max_data_file_length;
1221
 
    stats.max_index_file_length= misam_info.max_index_file_length;
1222
 
    stats.create_time= misam_info.create_time;
1223
 
    ref_length= misam_info.reflength;
1224
 
    share->db_options_in_use= misam_info.options;
1225
 
    stats.block_size= myisam_key_cache_block_size;        /* record block size */
1226
 
 
1227
 
    set_prefix(share->keys_in_use, share->sizeKeys());
1228
 
    /*
1229
 
     * Due to bug 394932 (32-bit solaris build failure), we need
1230
 
     * to convert the uint64_t key_map member of the misam_info
1231
 
     * structure in to a std::bitset so that we can logically and
1232
 
     * it with the share->key_in_use key_map.
1233
 
     */
1234
 
    ostringstream ostr;
1235
 
    string binary_key_map;
1236
 
    uint64_t num= misam_info.key_map;
1237
 
    /*
1238
 
     * Convert the uint64_t to a binary
1239
 
     * string representation of it.
1240
 
     */
1241
 
    while (num > 0)
1242
 
    {
1243
 
      uint64_t bin_digit= num % 2;
1244
 
      ostr << bin_digit;
1245
 
      num/= 2;
1246
 
    }
1247
 
    binary_key_map.append(ostr.str());
1248
 
    /*
1249
 
     * Now we have the binary string representation of the
1250
 
     * flags, we need to fill that string representation out
1251
 
     * with the appropriate number of bits. This is needed
1252
 
     * since key_map is declared as a std::bitset of a certain bit
1253
 
     * width that depends on the MAX_INDEXES variable. 
1254
 
     */
1255
 
    if (MAX_INDEXES <= 64)
1256
 
    {
1257
 
      size_t len= 72 - binary_key_map.length();
1258
 
      string all_zeros(len, '0');
1259
 
      binary_key_map.insert(binary_key_map.begin(),
1260
 
                            all_zeros.begin(),
1261
 
                            all_zeros.end());
1262
 
    }
1263
 
    else
1264
 
    {
1265
 
      size_t len= (MAX_INDEXES + 7) / 8 * 8;
1266
 
      string all_zeros(len, '0');
1267
 
      binary_key_map.insert(binary_key_map.begin(),
1268
 
                            all_zeros.begin(),
1269
 
                            all_zeros.end());
1270
 
    }
1271
 
    key_map tmp_map(binary_key_map);
1272
 
    share->keys_in_use&= tmp_map;
1273
 
    share->keys_for_keyread&= share->keys_in_use;
1274
 
    share->db_record_offset= misam_info.record_offset;
1275
 
    if (share->key_parts)
1276
 
      memcpy(getTable()->key_info[0].rec_per_key,
1277
 
             misam_info.rec_per_key,
1278
 
             sizeof(getTable()->key_info[0].rec_per_key)*share->key_parts);
1279
 
    assert(share->getType() != message::Table::STANDARD);
1280
 
 
1281
 
   /*
1282
 
     Set data_file_name and index_file_name to point at the symlink value
1283
 
     if table is symlinked (Ie;  Real name is not same as generated name)
1284
 
   */
1285
 
    data_file_name= index_file_name= 0;
1286
 
    internal::fn_format(name_buff, file->filename, "", MI_NAME_DEXT,
1287
 
              MY_APPEND_EXT | MY_UNPACK_FILENAME);
1288
 
    if (strcmp(name_buff, misam_info.data_file_name))
1289
 
      data_file_name=misam_info.data_file_name;
1290
 
    internal::fn_format(name_buff, file->filename, "", MI_NAME_IEXT,
1291
 
              MY_APPEND_EXT | MY_UNPACK_FILENAME);
1292
 
    if (strcmp(name_buff, misam_info.index_file_name))
1293
 
      index_file_name=misam_info.index_file_name;
1294
 
  }
1295
 
  if (flag & HA_STATUS_ERRKEY)
1296
 
  {
1297
 
    errkey  = misam_info.errkey;
1298
 
    internal::my_store_ptr(dup_ref, ref_length, misam_info.dupp_key_pos);
1299
 
  }
1300
 
  if (flag & HA_STATUS_TIME)
1301
 
    stats.update_time = misam_info.update_time;
1302
 
  if (flag & HA_STATUS_AUTO)
1303
 
    stats.auto_increment_value= misam_info.auto_increment;
1304
 
 
1305
 
  return 0;
1306
 
}
1307
 
 
1308
 
 
1309
 
int ha_myisam::extra(enum ha_extra_function operation)
1310
 
{
1311
 
  return mi_extra(file, operation, 0);
1312
 
}
1313
 
 
1314
 
int ha_myisam::reset(void)
1315
 
{
1316
 
  return mi_reset(file);
1317
 
}
1318
 
 
1319
 
/* To be used with WRITE_CACHE and EXTRA_CACHE */
1320
 
 
1321
 
int ha_myisam::extra_opt(enum ha_extra_function operation, uint32_t cache_size)
1322
 
{
1323
 
  return mi_extra(file, operation, (void*) &cache_size);
1324
 
}
1325
 
 
1326
 
int ha_myisam::delete_all_rows()
1327
 
{
1328
 
  return mi_delete_all_rows(file);
1329
 
}
1330
 
 
1331
 
int MyisamEngine::doDropTable(Session &session,
1332
 
                              const TableIdentifier &identifier)
1333
 
{
1334
 
  session.getMessageCache().removeTableMessage(identifier);
1335
 
 
1336
 
  return mi_delete_table(identifier.getPath().c_str());
1337
 
}
1338
 
 
1339
 
 
1340
 
int ha_myisam::external_lock(Session *session, int lock_type)
1341
 
{
1342
 
  file->in_use= session;
1343
 
  return mi_lock_database(file, !getTable()->getShare()->getType() ?
1344
 
                          lock_type : ((lock_type == F_UNLCK) ?
1345
 
                                       F_UNLCK : F_EXTRA_LCK));
1346
 
}
1347
 
 
1348
 
int MyisamEngine::doCreateTable(Session &session,
1349
 
                                Table& table_arg,
1350
 
                                const TableIdentifier &identifier,
1351
 
                                message::Table& create_proto)
1352
 
{
1353
 
  int error;
1354
 
  uint32_t create_flags= 0, create_records;
1355
 
  char buff[FN_REFLEN];
1356
 
  MI_KEYDEF *keydef;
1357
 
  MI_COLUMNDEF *recinfo;
1358
 
  MI_CREATE_INFO create_info;
1359
 
  TableShare *share= table_arg.getMutableShare();
1360
 
  uint32_t options= share->db_options_in_use;
1361
 
  if ((error= table2myisam(&table_arg, &keydef, &recinfo, &create_records)))
1362
 
    return(error);
1363
 
  memset(&create_info, 0, sizeof(create_info));
1364
 
  create_info.max_rows= create_proto.options().max_rows();
1365
 
  create_info.reloc_rows= create_proto.options().min_rows();
1366
 
  create_info.with_auto_increment= share->next_number_key_offset == 0;
1367
 
  create_info.auto_increment= (create_proto.options().has_auto_increment_value() ?
1368
 
                               create_proto.options().auto_increment_value() -1 :
1369
 
                               (uint64_t) 0);
1370
 
  create_info.data_file_length= (create_proto.options().max_rows() *
1371
 
                                 create_proto.options().avg_row_length());
1372
 
  create_info.data_file_name= NULL;
1373
 
  create_info.index_file_name=  NULL;
1374
 
  create_info.language= share->table_charset->number;
1375
 
 
1376
 
  if (create_proto.type() == message::Table::TEMPORARY)
1377
 
    create_flags|= HA_CREATE_TMP_TABLE;
1378
 
  if (options & HA_OPTION_PACK_RECORD)
1379
 
    create_flags|= HA_PACK_RECORD;
1380
 
 
1381
 
  /* TODO: Check that the following internal::fn_format is really needed */
1382
 
  error= mi_create(internal::fn_format(buff, identifier.getPath().c_str(), "", "",
1383
 
                                       MY_UNPACK_FILENAME|MY_APPEND_EXT),
1384
 
                   share->sizeKeys(), keydef,
1385
 
                   create_records, recinfo,
1386
 
                   0, (MI_UNIQUEDEF*) 0,
1387
 
                   &create_info, create_flags);
1388
 
  free((unsigned char*) recinfo);
1389
 
 
1390
 
  session.getMessageCache().storeTableMessage(identifier, create_proto);
1391
 
 
1392
 
  return error;
1393
 
}
1394
 
 
1395
 
 
1396
 
int MyisamEngine::doRenameTable(Session &session, const TableIdentifier &from, const TableIdentifier &to)
1397
 
{
1398
 
  session.getMessageCache().renameTableMessage(from, to);
1399
 
 
1400
 
  return mi_rename(from.getPath().c_str(), to.getPath().c_str());
1401
 
}
1402
 
 
1403
 
 
1404
 
void ha_myisam::get_auto_increment(uint64_t ,
1405
 
                                   uint64_t ,
1406
 
                                   uint64_t ,
1407
 
                                   uint64_t *first_value,
1408
 
                                   uint64_t *nb_reserved_values)
1409
 
{
1410
 
  uint64_t nr;
1411
 
  int error;
1412
 
  unsigned char key[MI_MAX_KEY_LENGTH];
1413
 
 
1414
 
  if (!getTable()->getShare()->next_number_key_offset)
1415
 
  {                                             // Autoincrement at key-start
1416
 
    ha_myisam::info(HA_STATUS_AUTO);
1417
 
    *first_value= stats.auto_increment_value;
1418
 
    /* MyISAM has only table-level lock, so reserves to +inf */
1419
 
    *nb_reserved_values= UINT64_MAX;
1420
 
    return;
1421
 
  }
1422
 
 
1423
 
  /* it's safe to call the following if bulk_insert isn't on */
1424
 
  mi_flush_bulk_insert(file, getTable()->getShare()->next_number_index);
1425
 
 
1426
 
  (void) extra(HA_EXTRA_KEYREAD);
1427
 
  key_copy(key, getTable()->getInsertRecord(),
1428
 
           &getTable()->key_info[getTable()->getShare()->next_number_index],
1429
 
           getTable()->getShare()->next_number_key_offset);
1430
 
  error= mi_rkey(file, getTable()->getUpdateRecord(), (int) getTable()->getShare()->next_number_index,
1431
 
                 key, make_prev_keypart_map(getTable()->getShare()->next_number_keypart),
1432
 
                 HA_READ_PREFIX_LAST);
1433
 
  if (error)
1434
 
    nr= 1;
1435
 
  else
1436
 
  {
1437
 
    /* Get data from getUpdateRecord() */
1438
 
    nr= ((uint64_t) getTable()->next_number_field->
1439
 
         val_int_offset(getTable()->getShare()->rec_buff_length)+1);
1440
 
  }
1441
 
  extra(HA_EXTRA_NO_KEYREAD);
1442
 
  *first_value= nr;
1443
 
  /*
1444
 
    MySQL needs to call us for next row: assume we are inserting ("a",null)
1445
 
    here, we return 3, and next this statement will want to insert ("b",null):
1446
 
    there is no reason why ("b",3+1) would be the good row to insert: maybe it
1447
 
    already exists, maybe 3+1 is too large...
1448
 
  */
1449
 
  *nb_reserved_values= 1;
1450
 
}
1451
 
 
1452
 
 
1453
 
/*
1454
 
  Find out how many rows there is in the given range
1455
 
 
1456
 
  SYNOPSIS
1457
 
    records_in_range()
1458
 
    inx                 Index to use
1459
 
    min_key             Start of range.  Null pointer if from first key
1460
 
    max_key             End of range. Null pointer if to last key
1461
 
 
1462
 
  NOTES
1463
 
    min_key.flag can have one of the following values:
1464
 
      HA_READ_KEY_EXACT         Include the key in the range
1465
 
      HA_READ_AFTER_KEY         Don't include key in range
1466
 
 
1467
 
    max_key.flag can have one of the following values:
1468
 
      HA_READ_BEFORE_KEY        Don't include key in range
1469
 
      HA_READ_AFTER_KEY         Include all 'end_key' values in the range
1470
 
 
1471
 
  RETURN
1472
 
   HA_POS_ERROR         Something is wrong with the index tree.
1473
 
   0                    There is no matching keys in the given range
1474
 
   number > 0           There is approximately 'number' matching rows in
1475
 
                        the range.
1476
 
*/
1477
 
 
1478
 
ha_rows ha_myisam::records_in_range(uint32_t inx, key_range *min_key,
1479
 
                                    key_range *max_key)
1480
 
{
1481
 
  return (ha_rows) mi_records_in_range(file, (int) inx, min_key, max_key);
1482
 
}
1483
 
 
1484
 
 
1485
 
uint32_t ha_myisam::checksum() const
1486
 
{
1487
 
  return (uint)file->state->checksum;
1488
 
}
1489
 
 
1490
 
static int myisam_init(module::Context &context)
1491
 
1492
 
  context.add(new MyisamEngine(engine_name));
1493
 
  context.registerVariable(new sys_var_constrained_value<size_t>("sort-buffer-size",
1494
 
                                                                 sort_buffer_size));
1495
 
  context.registerVariable(new sys_var_uint64_t_ptr("max_sort_file_size",
1496
 
                                                    &max_sort_file_size,
1497
 
                                                    context.getOptions()["max-sort-file-size"].as<uint64_t>()));
1498
 
 
1499
 
  return 0;
1500
 
}
1501
 
 
1502
 
 
1503
 
static void init_options(drizzled::module::option_context &context)
1504
 
{
1505
 
  context("max-sort-file-size",
1506
 
          po::value<uint64_t>(&max_sort_file_size)->default_value(INT32_MAX),
1507
 
          N_("Don't use the fast sort index method to created index if the temporary file would get bigger than this."));
1508
 
  context("sort-buffer-size",
1509
 
          po::value<sort_buffer_constraint>(&sort_buffer_size)->default_value(8192*1024),
1510
 
          N_("The buffer that is allocated when sorting the index when doing a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE."));
1511
 
}
1512
 
 
1513
 
 
1514
 
DRIZZLE_DECLARE_PLUGIN
1515
 
{
1516
 
  DRIZZLE_VERSION_ID,
1517
 
  "MyISAM",
1518
 
  "2.0",
1519
 
  "MySQL AB",
1520
 
  "Default engine as of MySQL 3.23 with great performance",
1521
 
  PLUGIN_LICENSE_GPL,
1522
 
  myisam_init, /* Plugin Init */
1523
 
  NULL,           /* system variables */
1524
 
  init_options                        /* config options                  */
1525
 
}
1526
 
DRIZZLE_DECLARE_PLUGIN_END;