1
/* Copyright (C) 2000-2006 MySQL AB
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.
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.
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 */
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"
38
#include <boost/algorithm/string.hpp>
39
#include <boost/scoped_ptr.hpp>
46
#include <boost/program_options.hpp>
47
#include <drizzled/module/option_map.h>
49
namespace po= boost::program_options;
52
using namespace drizzled;
54
static const string engine_name("MyISAM");
56
boost::mutex THR_LOCK_myisam;
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;
66
void st_mi_isam_share::setKeyCache()
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);
75
/*****************************************************************************
77
*****************************************************************************/
79
static const char *ha_myisam_exts[] = {
85
class MyisamEngine : public plugin::StorageEngine
88
MyisamEngine(const MyisamEngine&);
89
MyisamEngine& operator=(const MyisamEngine&);
91
explicit MyisamEngine(string name_arg) :
92
plugin::StorageEngine(name_arg,
93
HTON_CAN_INDEX_BLOBS |
94
HTON_STATS_RECORDS_IS_EXACT |
100
HTON_SKIP_STORE_LOCK)
104
virtual ~MyisamEngine()
106
mi_panic(HA_PANIC_CLOSE);
109
virtual Cursor *create(Table &table)
111
return new ha_myisam(*this, table);
114
const char **bas_ext() const {
115
return ha_myisam_exts;
118
int doCreateTable(Session&,
120
const TableIdentifier &identifier,
123
int doRenameTable(Session&, const TableIdentifier &from, const TableIdentifier &to);
125
int doDropTable(Session&, const TableIdentifier &identifier);
127
int doGetTableDefinition(Session& session,
128
const TableIdentifier &identifier,
129
message::Table &table_message);
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; }
135
uint32_t index_flags(enum ha_key_alg) const
137
return (HA_READ_NEXT |
143
bool doDoesTableExist(Session& session, const TableIdentifier &identifier);
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)
151
if (boost::iequals(key, "ROW_FORMAT"))
160
void MyisamEngine::doGetTableIdentifiers(drizzled::CachedDirectory&,
161
const drizzled::SchemaIdentifier&,
162
drizzled::TableIdentifier::vector&)
166
bool MyisamEngine::doDoesTableExist(Session &session, const TableIdentifier &identifier)
168
return session.getMessageCache().doesTableMessageExist(identifier);
171
int MyisamEngine::doGetTableDefinition(Session &session,
172
const TableIdentifier &identifier,
173
message::Table &table_message)
175
if (session.getMessageCache().getTableMessage(identifier, table_message))
181
Convert to push_Warnings if you ever care about this, otherwise, it is a no-op.
184
static void mi_check_print_msg(MI_CHECK *, const char* ,
185
const char *, va_list )
191
Convert Table object to MyISAM key and column definition
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.
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.
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.
214
static int table2myisam(Table *table_arg, MI_KEYDEF **keydef_out,
215
MI_COLUMNDEF **recinfo_out, uint32_t *records_out)
217
uint32_t i, j, recpos, minpos, fieldpos, temp_length, length;
218
enum ha_base_keytype type= HA_KEYTYPE_BINARY;
219
unsigned char *record;
221
MI_COLUMNDEF *recinfo, *recinfo_pos;
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),
230
return(HA_ERR_OUT_OF_MEM);
232
recinfo= *recinfo_out;
233
for (i= 0; i < share->sizeKeys(); i++)
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++)
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;
247
if (options & HA_OPTION_PACK_KEYS ||
248
(pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY |
249
HA_SPACE_PACK_USED)))
251
if (pos->key_part[j].length > 8 &&
252
(type == HA_KEYTYPE_TEXT ||
253
(type == HA_KEYTYPE_BINARY && !field->zero_pack())))
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;
261
else if (j == 0 && (!(pos->flags & HA_NOSAME) || pos->key_length > 16))
262
keydef[i].flag|= HA_BINARY_PACK_KEY;
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;
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());
280
keydef[i].seg[j].null_bit= 0;
281
keydef[i].seg[j].null_pos= 0;
283
if (field->type() == DRIZZLE_TYPE_BLOB)
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);
291
keyseg+= pos->key_parts;
293
if (table_arg->found_next_number_field)
294
keydef[share->next_number_index].flag|= HA_AUTO_KEY;
295
record= table_arg->getInsertRecord();
297
recinfo_pos= recinfo;
298
while (recpos < (uint) share->stored_rec_length)
300
Field **field, *found= 0;
301
minpos= share->getRecordLength();
304
for (field= table_arg->getFields(); *field; field++)
306
if ((fieldpos= (*field)->offset(record)) >= recpos &&
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))
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);
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;
339
recinfo_pos->type= (int) ((length <= 3) ? FIELD_NORMAL : FIELD_SKIP_PRESPACE);
342
recinfo_pos->null_bit= found->null_bit;
343
recinfo_pos->null_pos= (uint) (found->null_ptr -
344
(unsigned char*) table_arg->getInsertRecord());
348
recinfo_pos->null_bit= 0;
349
recinfo_pos->null_pos= 0;
351
(recinfo_pos++)->length= (uint16_t) length;
352
recpos= minpos + length;
354
*records_out= (uint) (recinfo_pos - recinfo);
358
int ha_myisam::reset_auto_increment(uint64_t value)
360
file->s->state.auto_increment= value;
365
Check for underlying table conformance
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
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.
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_*.
390
Otherwise 'strict' flag must be set to 1 and it is not required to pass
391
converted dot-frm definition as t1_*.
394
0 - Equal definitions.
395
1 - Different definitions.
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).
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)
410
if ((strict ? t1_keys != t2_keys : t1_keys > t2_keys))
414
if (t1_recs != t2_recs)
418
for (i= 0; i < t1_keys; i++)
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)
427
for (j= t1_keyinfo[i].keysegs; j--;)
429
uint8_t t1_keysegs_j__type= t1_keysegs[j].type;
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.
437
if ((t1_keysegs[j].flag & HA_BLOB_PART) &&
438
(t2_keysegs[j].flag & HA_BLOB_PART))
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;
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)
457
for (i= 0; i < t1_recs; i++)
459
MI_COLUMNDEF *t1_rec= &t1_recinfo[i];
460
MI_COLUMNDEF *t2_rec= &t2_recinfo[i];
462
FIELD_SKIP_ZERO can be changed to FIELD_NORMAL in mi_create,
463
see NOTE1 in mi_create.c
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)
479
volatile int *killed_ptr(MI_CHECK *param)
481
/* In theory Unsafe conversion, but should be ok for now */
482
return (int*) (((Session *)(param->session))->getKilledPtr());
485
void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
487
param->error_printed|=1;
488
param->out_flag|= O_DATA_LOST;
491
mi_check_print_msg(param, "error", fmt, args);
495
void mi_check_print_info(MI_CHECK *param, const char *fmt,...)
499
mi_check_print_msg(param, "info", fmt, args);
503
void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
505
param->warning_printed=1;
506
param->out_flag|= O_DATA_LOST;
509
mi_check_print_msg(param, "warning", fmt, args);
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).
518
This function is intended to be used when table corruption is detected.
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.
528
void _mi_report_crashed(MI_INFO *file, const char *message,
529
const char *sfile, uint32_t sline)
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,
537
errmsg_printf(ERRMSG_LVL_ERROR, _("Got an error from unknown thread, %s:%d"), sfile, sline);
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())
543
errmsg_printf(ERRMSG_LVL_ERROR, "%s", _("Unknown thread accessing table"));
548
ha_myisam::ha_myisam(plugin::StorageEngine &engine_arg,
550
: Cursor(engine_arg, table_arg),
552
can_enable_indexes(true),
556
Cursor *ha_myisam::clone(memory::Root *mem_root)
558
ha_myisam *new_handler= static_cast <ha_myisam *>(Cursor::clone(mem_root));
560
new_handler->file->state= file->state;
564
const char *ha_myisam::index_type(uint32_t )
569
/* Name is here without an extension */
570
int ha_myisam::doOpen(const drizzled::TableIdentifier &identifier, int mode, uint32_t test_if_locked)
573
MI_COLUMNDEF *recinfo= 0;
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).
592
if (!(file= mi_open(identifier, mode, test_if_locked)))
593
return (errno ? errno : -1);
595
if (!getTable()->getShare()->getType()) /* No need to perform a check for tmp table */
597
if ((errno= table2myisam(getTable(), &keyinfo, &recinfo, &recs)))
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))
605
errno= HA_ERR_CRASHED;
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);
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)
621
keys_with_parts.reset();
622
for (i= 0; i < getTable()->getShare()->sizeKeys(); i++)
624
getTable()->key_info[i].block_size= file->s->keyinfo[i].block_length;
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++)
630
if (!kp->field->part_of_key.test(i))
632
keys_with_parts.set(i);
643
Both recinfo and keydef are allocated by multi_malloc(), thus only
644
recinfo must be freed.
647
free((unsigned char*) recinfo);
651
int ha_myisam::close(void)
655
return mi_close(tmp);
658
int ha_myisam::doInsertRecord(unsigned char *buf)
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.
664
if (getTable()->next_number_field && buf == getTable()->getInsertRecord())
667
if ((error= update_auto_increment()))
670
return mi_write(file,buf);
674
int ha_myisam::repair(Session *session, MI_CHECK ¶m, bool do_optimize)
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;
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
692
if (file->dfile == -1)
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);
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;
706
param.sort_buffer_length= static_cast<size_t>(sort_buffer_size);
707
strcpy(fixed_name,file->filename);
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))
712
mi_check_print_error(¶m,ER(ER_CANT_LOCK),errno);
713
return(HA_ADMIN_FAILED);
717
((file->state->del || share->state.split != file->state->records) &&
718
(!(param.testflag & T_QUICK) ||
719
!(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))))
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))
728
local_testflag|= T_STATISTICS;
729
param.testflag|= T_STATISTICS; // We get this for free
732
session->set_proc_info("Repair by sorting");
733
error = mi_repair_by_sort(¶m, file, fixed_name,
734
param.testflag & T_QUICK);
739
session->set_proc_info("Repair with keycache");
740
param.testflag &= ~T_REP_BY_SORT;
741
error= mi_repair(¶m, file, fixed_name,
742
param.testflag & T_QUICK);
744
param.testflag=testflag;
749
if ((local_testflag & T_SORT_INDEX) &&
750
(share->state.changed & STATE_NOT_SORTED_PAGES))
753
session->set_proc_info("Sorting index");
754
error=mi_sort_index(¶m,file,fixed_name);
756
if (!statistics_done && (local_testflag & T_STATISTICS))
758
if (share->state.changed & STATE_NOT_ANALYZED)
761
session->set_proc_info("Analyzing");
762
error = chk_key(¶m, file);
765
local_testflag&= ~T_STATISTICS; // Don't update statistics
768
session->set_proc_info("Saving state");
771
if ((share->state.changed & STATE_CHANGED) || mi_is_crashed(file))
773
share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
774
STATE_CRASHED_ON_REPAIR);
775
file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
778
the following 'if', thought conceptually wrong,
779
is a useful optimization nevertheless.
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(¶m, file, 1);
786
error = update_state_info(¶m, file,
787
UPDATE_TIME | UPDATE_OPEN_COUNT |
789
T_STATISTICS ? UPDATE_STAT : 0));
790
info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
792
if (rows != file->state->records && ! (param.testflag & T_VERY_SILENT))
794
char llbuff[22],llbuff2[22];
795
mi_check_print_warning(¶m,"Number of rows changed from %s to %s",
796
internal::llstr(rows,llbuff),
797
internal::llstr(file->state->records,llbuff2));
802
mi_mark_crashed_on_repair(file);
803
file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
804
update_state_info(¶m, file, 0);
806
session->set_proc_info(old_proc_info);
807
mi_lock_database(file,F_UNLCK);
809
return(error ? HA_ADMIN_FAILED :
810
!optimize_done ? HA_ADMIN_ALREADY_DONE : HA_ADMIN_OK);
815
Disable indexes, making it persistent if requested.
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
826
HA_KEY_SWITCH_NONUNIQ is not implemented.
827
HA_KEY_SWITCH_ALL_SAVE is not implemented.
831
HA_ERR_WRONG_COMMAND mode not implemented.
834
int ha_myisam::disable_indexes(uint32_t mode)
838
if (mode == HA_KEY_SWITCH_ALL)
840
/* call a storage engine function to switch the key map */
841
error= mi_disable_indexes(file);
843
else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
845
mi_extra(file, HA_EXTRA_NO_KEYS, 0);
846
info(HA_STATUS_CONST); // Read new key info
851
/* mode not implemented */
852
error= HA_ERR_WRONG_COMMAND;
859
Enable indexes, making it persistent if requested.
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
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.
876
HA_KEY_SWITCH_NONUNIQ is not implemented.
877
HA_KEY_SWITCH_ALL_SAVE is not implemented.
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.
886
int ha_myisam::enable_indexes(uint32_t mode)
890
if (mi_is_all_keys_active(file->s->state.key_map, file->s->base.keys))
892
/* All indexes are enabled already. */
896
if (mode == HA_KEY_SWITCH_ALL)
898
error= mi_enable_indexes(file);
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.
905
else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
907
Session *session= getTable()->in_use;
908
boost::scoped_ptr<MI_CHECK> param_ap(new MI_CHECK);
909
MI_CHECK ¶m= *param_ap.get();
910
const char *save_proc_info= session->get_proc_info();
911
session->set_proc_info("Creating index");
912
myisamchk_init(¶m);
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)
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);
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.
932
session->clear_error();
934
info(HA_STATUS_CONST);
935
session->set_proc_info(save_proc_info);
939
/* mode not implemented */
940
error= HA_ERR_WRONG_COMMAND;
947
Test if indexes are disabled.
951
indexes_are_disabled()
956
0 indexes are not disabled
957
1 all indexes are disabled
958
[2 non-unique indexes are disabled - NOT YET IMPLEMENTED]
961
int ha_myisam::indexes_are_disabled(void)
964
return mi_indexes_are_disabled(file);
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
974
start_bulk_insert(rows)
975
rows Rows to be inserted
979
Do not forget to call end_bulk_insert() later!
982
void ha_myisam::start_bulk_insert(ha_rows rows)
984
Session *session= getTable()->in_use;
985
ulong size= session->variables.read_buff_size;
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);
991
can_enable_indexes= mi_is_all_keys_active(file->s->state.key_map,
995
Only disable old index if the table was empty and we are inserting
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.
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);
1004
if (!file->bulk_insert &&
1005
(!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT))
1007
mi_init_bulk_insert(file,
1008
(size_t)session->variables.bulk_insert_buff_size,
1014
end special bulk-insert optimizations,
1015
which have been activated by start_bulk_insert().
1026
int ha_myisam::end_bulk_insert()
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;
1036
int ha_myisam::doUpdateRecord(const unsigned char *old_data, unsigned char *new_data)
1038
return mi_update(file,old_data,new_data);
1041
int ha_myisam::doDeleteRecord(const unsigned char *buf)
1043
return mi_delete(file,buf);
1047
int ha_myisam::doStartIndexScan(uint32_t idx, bool )
1050
//in_range_read= false;
1055
int ha_myisam::doEndIndexScan()
1057
active_index=MAX_KEY;
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)
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;
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)
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;
1083
int ha_myisam::index_read_last_map(unsigned char *buf, const unsigned char *key,
1084
key_part_map keypart_map)
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;
1094
int ha_myisam::index_next(unsigned char *buf)
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;
1103
int ha_myisam::index_prev(unsigned char *buf)
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;
1112
int ha_myisam::index_first(unsigned char *buf)
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;
1121
int ha_myisam::index_last(unsigned char *buf)
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;
1130
int ha_myisam::index_next_same(unsigned char *buf,
1131
const unsigned char *,
1135
assert(inited==INDEX);
1136
ha_statistic_increment(&system_status_var::ha_read_next_count);
1139
error= mi_rnext_same(file,buf);
1140
} while (error == HA_ERR_RECORD_DELETED);
1141
getTable()->status=error ? STATUS_NOT_FOUND: 0;
1145
int ha_myisam::read_range_first(const key_range *start_key,
1146
const key_range *end_key,
1148
bool sorted /* ignored */)
1151
//if (!eq_range_arg)
1152
// in_range_read= true;
1154
res= Cursor::read_range_first(start_key, end_key, eq_range_arg, sorted);
1157
// in_range_read= false;
1162
int ha_myisam::read_range_next()
1164
int res= Cursor::read_range_next();
1166
// in_range_read= false;
1171
int ha_myisam::doStartTableScan(bool scan)
1174
return mi_scan_init(file);
1175
return mi_reset(file); // Free buffers
1178
int ha_myisam::rnd_next(unsigned char *buf)
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;
1186
int ha_myisam::rnd_pos(unsigned char *buf, unsigned char *pos)
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;
1195
void ha_myisam::position(const unsigned char *)
1197
internal::my_off_t row_position= mi_position(file);
1198
internal::my_store_ptr(ref, ref_length, row_position);
1201
int ha_myisam::info(uint32_t flag)
1203
MI_ISAMINFO misam_info;
1204
char name_buff[FN_REFLEN];
1206
(void) mi_status(file,&misam_info,flag);
1207
if (flag & HA_STATUS_VARIABLE)
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;
1217
if (flag & HA_STATUS_CONST)
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 */
1227
set_prefix(share->keys_in_use, share->sizeKeys());
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.
1235
string binary_key_map;
1236
uint64_t num= misam_info.key_map;
1238
* Convert the uint64_t to a binary
1239
* string representation of it.
1243
uint64_t bin_digit= num % 2;
1247
binary_key_map.append(ostr.str());
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.
1255
if (MAX_INDEXES <= 64)
1257
size_t len= 72 - binary_key_map.length();
1258
string all_zeros(len, '0');
1259
binary_key_map.insert(binary_key_map.begin(),
1265
size_t len= (MAX_INDEXES + 7) / 8 * 8;
1266
string all_zeros(len, '0');
1267
binary_key_map.insert(binary_key_map.begin(),
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);
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)
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;
1295
if (flag & HA_STATUS_ERRKEY)
1297
errkey = misam_info.errkey;
1298
internal::my_store_ptr(dup_ref, ref_length, misam_info.dupp_key_pos);
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;
1309
int ha_myisam::extra(enum ha_extra_function operation)
1311
return mi_extra(file, operation, 0);
1314
int ha_myisam::reset(void)
1316
return mi_reset(file);
1319
/* To be used with WRITE_CACHE and EXTRA_CACHE */
1321
int ha_myisam::extra_opt(enum ha_extra_function operation, uint32_t cache_size)
1323
return mi_extra(file, operation, (void*) &cache_size);
1326
int ha_myisam::delete_all_rows()
1328
return mi_delete_all_rows(file);
1331
int MyisamEngine::doDropTable(Session &session,
1332
const TableIdentifier &identifier)
1334
session.getMessageCache().removeTableMessage(identifier);
1336
return mi_delete_table(identifier.getPath().c_str());
1340
int ha_myisam::external_lock(Session *session, int lock_type)
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));
1348
int MyisamEngine::doCreateTable(Session &session,
1350
const TableIdentifier &identifier,
1351
message::Table& create_proto)
1354
uint32_t create_flags= 0, create_records;
1355
char buff[FN_REFLEN];
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)))
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 :
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;
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;
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);
1390
session.getMessageCache().storeTableMessage(identifier, create_proto);
1396
int MyisamEngine::doRenameTable(Session &session, const TableIdentifier &from, const TableIdentifier &to)
1398
session.getMessageCache().renameTableMessage(from, to);
1400
return mi_rename(from.getPath().c_str(), to.getPath().c_str());
1404
void ha_myisam::get_auto_increment(uint64_t ,
1407
uint64_t *first_value,
1408
uint64_t *nb_reserved_values)
1412
unsigned char key[MI_MAX_KEY_LENGTH];
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;
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);
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);
1437
/* Get data from getUpdateRecord() */
1438
nr= ((uint64_t) getTable()->next_number_field->
1439
val_int_offset(getTable()->getShare()->rec_buff_length)+1);
1441
extra(HA_EXTRA_NO_KEYREAD);
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...
1449
*nb_reserved_values= 1;
1454
Find out how many rows there is in the given range
1459
min_key Start of range. Null pointer if from first key
1460
max_key End of range. Null pointer if to last key
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
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
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
1478
ha_rows ha_myisam::records_in_range(uint32_t inx, key_range *min_key,
1481
return (ha_rows) mi_records_in_range(file, (int) inx, min_key, max_key);
1485
uint32_t ha_myisam::checksum() const
1487
return (uint)file->state->checksum;
1490
static int myisam_init(module::Context &context)
1492
context.add(new MyisamEngine(engine_name));
1493
context.registerVariable(new sys_var_constrained_value<size_t>("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>()));
1503
static void init_options(drizzled::module::option_context &context)
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."));
1514
DRIZZLE_DECLARE_PLUGIN
1520
"Default engine as of MySQL 3.23 with great performance",
1522
myisam_init, /* Plugin Init */
1523
NULL, /* system variables */
1524
init_options /* config options */
1526
DRIZZLE_DECLARE_PLUGIN_END;