00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include <config.h>
00019 #include <drizzled/internal/my_bit.h>
00020 #include "myisampack.h"
00021 #include "ha_myisam.h"
00022 #include "myisam_priv.h"
00023 #include <drizzled/option.h>
00024 #include <drizzled/internal/my_bit.h>
00025 #include <drizzled/internal/m_string.h>
00026 #include <drizzled/util/test.h>
00027 #include <drizzled/error.h>
00028 #include <drizzled/errmsg_print.h>
00029 #include <drizzled/gettext.h>
00030 #include <drizzled/session.h>
00031 #include <drizzled/plugin.h>
00032 #include <drizzled/plugin/client.h>
00033 #include <drizzled/table.h>
00034 #include <drizzled/memory/multi_malloc.h>
00035 #include <drizzled/plugin/daemon.h>
00036 #include <drizzled/session/table_messages.h>
00037 #include <drizzled/plugin/storage_engine.h>
00038 #include <drizzled/key.h>
00039 #include <drizzled/statistics_variables.h>
00040 #include <drizzled/system_variables.h>
00041
00042 #include <boost/algorithm/string.hpp>
00043 #include <boost/scoped_ptr.hpp>
00044
00045 #include <string>
00046 #include <sstream>
00047 #include <map>
00048 #include <algorithm>
00049 #include <memory>
00050 #include <boost/program_options.hpp>
00051 #include <drizzled/module/option_map.h>
00052
00053 namespace po= boost::program_options;
00054
00055 using namespace std;
00056 using namespace drizzled;
00057
00058 static const string engine_name("MyISAM");
00059
00060 boost::mutex THR_LOCK_myisam;
00061
00062 static uint32_t myisam_key_cache_block_size= KEY_CACHE_BLOCK_SIZE;
00063 static uint64_t max_sort_file_size;
00064 typedef constrained_check<size_t, SIZE_MAX, 1024, 1024> sort_buffer_constraint;
00065 static sort_buffer_constraint sort_buffer_size;
00066
00067
00068
00069
00070
00071 static const char *ha_myisam_exts[] = {
00072 ".MYI",
00073 ".MYD",
00074 NULL
00075 };
00076
00077 class MyisamEngine : public plugin::StorageEngine
00078 {
00079 public:
00080 explicit MyisamEngine(string name_arg) :
00081 plugin::StorageEngine(name_arg,
00082 HTON_CAN_INDEX_BLOBS |
00083 HTON_STATS_RECORDS_IS_EXACT |
00084 HTON_TEMPORARY_ONLY |
00085 HTON_NULL_IN_KEY |
00086 HTON_HAS_RECORDS |
00087 HTON_DUPLICATE_POS |
00088 HTON_AUTO_PART_KEY |
00089 HTON_SKIP_STORE_LOCK)
00090 {
00091 }
00092
00093 virtual ~MyisamEngine()
00094 {
00095 mi_panic(HA_PANIC_CLOSE);
00096 }
00097
00098 virtual Cursor *create(Table &table)
00099 {
00100 return new ha_myisam(*this, table);
00101 }
00102
00103 const char **bas_ext() const {
00104 return ha_myisam_exts;
00105 }
00106
00107 int doCreateTable(Session&,
00108 Table& table_arg,
00109 const identifier::Table &identifier,
00110 const message::Table&);
00111
00112 int doRenameTable(Session&, const identifier::Table &from, const identifier::Table &to);
00113
00114 int doDropTable(Session&, const identifier::Table &identifier);
00115
00116 int doGetTableDefinition(Session& session,
00117 const identifier::Table &identifier,
00118 message::Table &table_message);
00119
00120 uint32_t max_supported_keys() const { return MI_MAX_KEY; }
00121 uint32_t max_supported_key_length() const { return MI_MAX_KEY_LENGTH; }
00122 uint32_t max_supported_key_part_length() const { return MI_MAX_KEY_LENGTH; }
00123
00124 uint32_t index_flags(enum ha_key_alg) const
00125 {
00126 return (HA_READ_NEXT |
00127 HA_READ_PREV |
00128 HA_READ_RANGE |
00129 HA_READ_ORDER |
00130 HA_KEYREAD_ONLY);
00131 }
00132 bool doDoesTableExist(Session& session, const identifier::Table &identifier);
00133
00134 void doGetTableIdentifiers(drizzled::CachedDirectory &directory,
00135 const drizzled::identifier::Schema &schema_identifier,
00136 drizzled::identifier::table::vector &set_of_identifiers);
00137 bool validateCreateTableOption(const std::string &key, const std::string &state)
00138 {
00139 (void)state;
00140 if (boost::iequals(key, "ROW_FORMAT"))
00141 {
00142 return true;
00143 }
00144
00145 return false;
00146 }
00147 };
00148
00149 void MyisamEngine::doGetTableIdentifiers(drizzled::CachedDirectory&,
00150 const drizzled::identifier::Schema&,
00151 drizzled::identifier::table::vector&)
00152 {
00153 }
00154
00155 bool MyisamEngine::doDoesTableExist(Session &session, const identifier::Table &identifier)
00156 {
00157 return session.getMessageCache().doesTableMessageExist(identifier);
00158 }
00159
00160 int MyisamEngine::doGetTableDefinition(Session &session,
00161 const identifier::Table &identifier,
00162 message::Table &table_message)
00163 {
00164 if (session.getMessageCache().getTableMessage(identifier, table_message))
00165 return EEXIST;
00166 return ENOENT;
00167 }
00168
00169
00170
00171
00172
00173 static void mi_check_print_msg(MI_CHECK *, const char* ,
00174 const char *, va_list )
00175 {
00176 }
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203 static int table2myisam(Table *table_arg, MI_KEYDEF **keydef_out,
00204 MI_COLUMNDEF **recinfo_out, uint32_t *records_out)
00205 {
00206 uint32_t i, j, recpos, minpos, fieldpos, temp_length, length;
00207 enum ha_base_keytype type= HA_KEYTYPE_BINARY;
00208 unsigned char *record;
00209 MI_KEYDEF *keydef;
00210 MI_COLUMNDEF *recinfo, *recinfo_pos;
00211 HA_KEYSEG *keyseg;
00212 TableShare *share= table_arg->getMutableShare();
00213 uint32_t options= share->db_options_in_use;
00214 if (!(memory::multi_malloc(false,
00215 recinfo_out, (share->sizeFields() * 2 + 2) * sizeof(MI_COLUMNDEF),
00216 keydef_out, share->sizeKeys() * sizeof(MI_KEYDEF),
00217 &keyseg, (share->key_parts + share->sizeKeys()) * sizeof(HA_KEYSEG),
00218 NULL)))
00219 return(HA_ERR_OUT_OF_MEM);
00220 keydef= *keydef_out;
00221 recinfo= *recinfo_out;
00222 for (i= 0; i < share->sizeKeys(); i++)
00223 {
00224 KeyInfo *pos= &table_arg->key_info[i];
00225 keydef[i].flag= ((uint16_t) pos->flags & (HA_NOSAME));
00226 keydef[i].key_alg= HA_KEY_ALG_BTREE;
00227 keydef[i].block_length= pos->block_size;
00228 keydef[i].seg= keyseg;
00229 keydef[i].keysegs= pos->key_parts;
00230 for (j= 0; j < pos->key_parts; j++)
00231 {
00232 Field *field= pos->key_part[j].field;
00233 type= field->key_type();
00234 keydef[i].seg[j].flag= pos->key_part[j].key_part_flag;
00235
00236 if (options & HA_OPTION_PACK_KEYS ||
00237 (pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY |
00238 HA_SPACE_PACK_USED)))
00239 {
00240 if (pos->key_part[j].length > 8 &&
00241 (type == HA_KEYTYPE_TEXT ||
00242 (type == HA_KEYTYPE_BINARY && !field->zero_pack())))
00243 {
00244
00245 if (j == 0)
00246 keydef[i].flag|= HA_PACK_KEY;
00247 if ((((int) (pos->key_part[j].length - field->decimals())) >= 4))
00248 keydef[i].seg[j].flag|= HA_SPACE_PACK;
00249 }
00250 else if (j == 0 && (!(pos->flags & HA_NOSAME) || pos->key_length > 16))
00251 keydef[i].flag|= HA_BINARY_PACK_KEY;
00252 }
00253 keydef[i].seg[j].type= (int) type;
00254 keydef[i].seg[j].start= pos->key_part[j].offset;
00255 keydef[i].seg[j].length= pos->key_part[j].length;
00256 keydef[i].seg[j].bit_start= keydef[i].seg[j].bit_end=
00257 keydef[i].seg[j].bit_length= 0;
00258 keydef[i].seg[j].bit_pos= 0;
00259 keydef[i].seg[j].language= field->charset()->number;
00260
00261 if (field->null_ptr)
00262 {
00263 keydef[i].seg[j].null_bit= field->null_bit;
00264 keydef[i].seg[j].null_pos= (uint) (field->null_ptr-
00265 (unsigned char*) table_arg->getInsertRecord());
00266 }
00267 else
00268 {
00269 keydef[i].seg[j].null_bit= 0;
00270 keydef[i].seg[j].null_pos= 0;
00271 }
00272 if (field->type() == DRIZZLE_TYPE_BLOB)
00273 {
00274 keydef[i].seg[j].flag|= HA_BLOB_PART;
00275
00276 keydef[i].seg[j].bit_start= (uint) (field->pack_length() -
00277 share->sizeBlobPtr());
00278 }
00279 }
00280 keyseg+= pos->key_parts;
00281 }
00282 if (table_arg->found_next_number_field)
00283 keydef[share->next_number_index].flag|= HA_AUTO_KEY;
00284 record= table_arg->getInsertRecord();
00285 recpos= 0;
00286 recinfo_pos= recinfo;
00287
00288 while (recpos < (uint) share->sizeStoredRecord())
00289 {
00290 Field **field, *found= 0;
00291 minpos= share->getRecordLength();
00292 length= 0;
00293
00294 for (field= table_arg->getFields(); *field; field++)
00295 {
00296 if ((fieldpos= (*field)->offset(record)) >= recpos &&
00297 fieldpos <= minpos)
00298 {
00299
00300 if (!(temp_length= (*field)->pack_length_in_rec()))
00301 continue;
00302
00303 if (! found || fieldpos < minpos ||
00304 (fieldpos == minpos && temp_length < length))
00305 {
00306 minpos= fieldpos;
00307 found= *field;
00308 length= temp_length;
00309 }
00310 }
00311 }
00312 if (recpos != minpos)
00313 {
00314 memset(recinfo_pos, 0, sizeof(*recinfo_pos));
00315 recinfo_pos->type= (int) FIELD_NORMAL;
00316 recinfo_pos++->length= (uint16_t) (minpos - recpos);
00317 }
00318 if (!found)
00319 break;
00320
00321 if (found->flags & BLOB_FLAG)
00322 recinfo_pos->type= (int) FIELD_BLOB;
00323 else if (found->type() == DRIZZLE_TYPE_VARCHAR)
00324 recinfo_pos->type= FIELD_VARCHAR;
00325 else if (!(options & HA_OPTION_PACK_RECORD))
00326 recinfo_pos->type= (int) FIELD_NORMAL;
00327 else if (found->zero_pack())
00328 recinfo_pos->type= (int) FIELD_SKIP_ZERO;
00329 else
00330 recinfo_pos->type= (int) ((length <= 3) ? FIELD_NORMAL : FIELD_SKIP_PRESPACE);
00331 if (found->null_ptr)
00332 {
00333 recinfo_pos->null_bit= found->null_bit;
00334 recinfo_pos->null_pos= (uint) (found->null_ptr -
00335 (unsigned char*) table_arg->getInsertRecord());
00336 }
00337 else
00338 {
00339 recinfo_pos->null_bit= 0;
00340 recinfo_pos->null_pos= 0;
00341 }
00342 (recinfo_pos++)->length= (uint16_t) length;
00343 recpos= minpos + length;
00344 }
00345 *records_out= (uint) (recinfo_pos - recinfo);
00346 return(0);
00347 }
00348
00349 int ha_myisam::reset_auto_increment(uint64_t value)
00350 {
00351 file->s->state.auto_increment= value;
00352 return 0;
00353 }
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395 static int check_definition(MI_KEYDEF *t1_keyinfo, MI_COLUMNDEF *t1_recinfo,
00396 uint32_t t1_keys, uint32_t t1_recs,
00397 MI_KEYDEF *t2_keyinfo, MI_COLUMNDEF *t2_recinfo,
00398 uint32_t t2_keys, uint32_t t2_recs, bool strict)
00399 {
00400 uint32_t i, j;
00401 if ((strict ? t1_keys != t2_keys : t1_keys > t2_keys))
00402 {
00403 return(1);
00404 }
00405 if (t1_recs != t2_recs)
00406 {
00407 return(1);
00408 }
00409 for (i= 0; i < t1_keys; i++)
00410 {
00411 HA_KEYSEG *t1_keysegs= t1_keyinfo[i].seg;
00412 HA_KEYSEG *t2_keysegs= t2_keyinfo[i].seg;
00413 if (t1_keyinfo[i].keysegs != t2_keyinfo[i].keysegs ||
00414 t1_keyinfo[i].key_alg != t2_keyinfo[i].key_alg)
00415 {
00416 return(1);
00417 }
00418 for (j= t1_keyinfo[i].keysegs; j--;)
00419 {
00420 uint8_t t1_keysegs_j__type= t1_keysegs[j].type;
00421
00422
00423
00424
00425
00426
00427
00428 if ((t1_keysegs[j].flag & HA_BLOB_PART) &&
00429 (t2_keysegs[j].flag & HA_BLOB_PART))
00430 {
00431 if ((t1_keysegs_j__type == HA_KEYTYPE_VARTEXT2) &&
00432 (t2_keysegs[j].type == HA_KEYTYPE_VARTEXT1))
00433 t1_keysegs_j__type= HA_KEYTYPE_VARTEXT1;
00434 else if ((t1_keysegs_j__type == HA_KEYTYPE_VARBINARY2) &&
00435 (t2_keysegs[j].type == HA_KEYTYPE_VARBINARY1))
00436 t1_keysegs_j__type= HA_KEYTYPE_VARBINARY1;
00437 }
00438
00439 if (t1_keysegs_j__type != t2_keysegs[j].type ||
00440 t1_keysegs[j].language != t2_keysegs[j].language ||
00441 t1_keysegs[j].null_bit != t2_keysegs[j].null_bit ||
00442 t1_keysegs[j].length != t2_keysegs[j].length)
00443 {
00444 return(1);
00445 }
00446 }
00447 }
00448 for (i= 0; i < t1_recs; i++)
00449 {
00450 MI_COLUMNDEF *t1_rec= &t1_recinfo[i];
00451 MI_COLUMNDEF *t2_rec= &t2_recinfo[i];
00452
00453
00454
00455
00456 if ((t1_rec->type != t2_rec->type &&
00457 !(t1_rec->type == (int) FIELD_SKIP_ZERO &&
00458 t1_rec->length == 1 &&
00459 t2_rec->type == (int) FIELD_NORMAL)) ||
00460 t1_rec->length != t2_rec->length ||
00461 t1_rec->null_bit != t2_rec->null_bit)
00462 {
00463 return(1);
00464 }
00465 }
00466 return(0);
00467 }
00468
00469
00470 volatile int *killed_ptr(MI_CHECK *param)
00471 {
00472
00473 return (int*) (((Session *)(param->session))->getKilledPtr());
00474 }
00475
00476 void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
00477 {
00478 param->error_printed|=1;
00479 param->out_flag|= O_DATA_LOST;
00480 va_list args;
00481 va_start(args, fmt);
00482 mi_check_print_msg(param, "error", fmt, args);
00483 va_end(args);
00484 }
00485
00486 void mi_check_print_info(MI_CHECK *param, const char *fmt,...)
00487 {
00488 va_list args;
00489 va_start(args, fmt);
00490 mi_check_print_msg(param, "info", fmt, args);
00491 va_end(args);
00492 }
00493
00494 void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
00495 {
00496 param->warning_printed=1;
00497 param->out_flag|= O_DATA_LOST;
00498 va_list args;
00499 va_start(args, fmt);
00500 mi_check_print_msg(param, "warning", fmt, args);
00501 va_end(args);
00502 }
00503
00519 void _mi_report_crashed(MI_INFO *file, const char *message,
00520 const char *sfile, uint32_t sline)
00521 {
00522 Session *cur_session;
00523 if ((cur_session= file->in_use))
00524 {
00525 errmsg_printf(error::ERROR, _("Got an error from thread_id=%"PRIu64", %s:%d"),
00526 cur_session->thread_id,
00527 sfile, sline);
00528 }
00529 else
00530 {
00531 errmsg_printf(error::ERROR, _("Got an error from unknown thread, %s:%d"), sfile, sline);
00532 }
00533
00534 if (message)
00535 errmsg_printf(error::ERROR, "%s", message);
00536
00537 list<Session *>::iterator it= file->s->in_use->begin();
00538 while (it != file->s->in_use->end())
00539 {
00540 errmsg_printf(error::ERROR, "%s", _("Unknown thread accessing table"));
00541 ++it;
00542 }
00543 }
00544
00545 ha_myisam::ha_myisam(plugin::StorageEngine &engine_arg,
00546 Table &table_arg)
00547 : Cursor(engine_arg, table_arg),
00548 file(0),
00549 can_enable_indexes(true),
00550 is_ordered(true)
00551 { }
00552
00553 Cursor *ha_myisam::clone(memory::Root *mem_root)
00554 {
00555 ha_myisam *new_handler= static_cast <ha_myisam *>(Cursor::clone(mem_root));
00556 if (new_handler)
00557 new_handler->file->state= file->state;
00558 return new_handler;
00559 }
00560
00561 const char *ha_myisam::index_type(uint32_t )
00562 {
00563 return "BTREE";
00564 }
00565
00566
00567 int ha_myisam::doOpen(const drizzled::identifier::Table &identifier, int mode, uint32_t test_if_locked)
00568 {
00569 MI_KEYDEF *keyinfo;
00570 MI_COLUMNDEF *recinfo= 0;
00571 uint32_t recs;
00572 uint32_t i;
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589 if (!(file= mi_open(identifier, mode, test_if_locked)))
00590 return (errno ? errno : -1);
00591
00592 if (!getTable()->getShare()->getType())
00593 {
00594 if ((errno= table2myisam(getTable(), &keyinfo, &recinfo, &recs)))
00595 {
00596 goto err;
00597 }
00598 if (check_definition(keyinfo, recinfo, getTable()->getShare()->sizeKeys(), recs,
00599 file->s->keyinfo, file->s->rec,
00600 file->s->base.keys, file->s->base.fields, true))
00601 {
00602 errno= HA_ERR_CRASHED;
00603 goto err;
00604 }
00605 }
00606
00607 assert(test_if_locked);
00608 if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE))
00609 mi_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0);
00610
00611 info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
00612 if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
00613 mi_extra(file, HA_EXTRA_WAIT_LOCK, 0);
00614 if (!getTable()->getShare()->db_record_offset)
00615 is_ordered= false;
00616
00617
00618 keys_with_parts.reset();
00619 for (i= 0; i < getTable()->getShare()->sizeKeys(); i++)
00620 {
00621 getTable()->key_info[i].block_size= file->s->keyinfo[i].block_length;
00622
00623 KeyPartInfo *kp= getTable()->key_info[i].key_part;
00624 KeyPartInfo *kp_end= kp + getTable()->key_info[i].key_parts;
00625 for (; kp != kp_end; kp++)
00626 {
00627 if (!kp->field->part_of_key.test(i))
00628 {
00629 keys_with_parts.set(i);
00630 break;
00631 }
00632 }
00633 }
00634 errno= 0;
00635 goto end;
00636 err:
00637 this->close();
00638 end:
00639
00640
00641
00642
00643 if (recinfo)
00644 free((unsigned char*) recinfo);
00645 return errno;
00646 }
00647
00648 int ha_myisam::close(void)
00649 {
00650 MI_INFO *tmp=file;
00651 file=0;
00652 return mi_close(tmp);
00653 }
00654
00655 int ha_myisam::doInsertRecord(unsigned char *buf)
00656 {
00657
00658
00659
00660
00661 if (getTable()->next_number_field && buf == getTable()->getInsertRecord())
00662 {
00663 int error;
00664 if ((error= update_auto_increment()))
00665 return error;
00666 }
00667 return mi_write(file,buf);
00668 }
00669
00670
00671 int ha_myisam::repair(Session *session, MI_CHECK ¶m, bool do_optimize)
00672 {
00673 int error=0;
00674 uint32_t local_testflag= param.testflag;
00675 bool optimize_done= !do_optimize, statistics_done=0;
00676 const char *old_proc_info= session->get_proc_info();
00677 char fixed_name[FN_REFLEN];
00678 MYISAM_SHARE* share = file->s;
00679 ha_rows rows= file->state->records;
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689 if (file->dfile == -1)
00690 {
00691 errmsg_printf(error::INFO, "Retrying repair of: '%s' failed. "
00692 "Please try REPAIR EXTENDED or myisamchk",
00693 getTable()->getShare()->getPath());
00694 return(HA_ADMIN_FAILED);
00695 }
00696
00697 param.db_name= getTable()->getShare()->getSchemaName();
00698 param.table_name= getTable()->getAlias();
00699 param.tmpfile_createflag = O_RDWR | O_TRUNC;
00700 param.using_global_keycache = 1;
00701 param.session= session;
00702 param.out_flag= 0;
00703 param.sort_buffer_length= static_cast<size_t>(sort_buffer_size);
00704 strcpy(fixed_name,file->filename);
00705
00706
00707 if (mi_lock_database(file, getTable()->getShare()->getType() ? F_EXTRA_LCK : F_WRLCK))
00708 {
00709 mi_check_print_error(¶m,ER(ER_CANT_LOCK),errno);
00710 return(HA_ADMIN_FAILED);
00711 }
00712
00713 if (!do_optimize ||
00714 ((file->state->del || share->state.split != file->state->records) &&
00715 (!(param.testflag & T_QUICK) ||
00716 !(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))))
00717 {
00718 uint64_t key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ?
00719 mi_get_mask_all_keys_active(share->base.keys) :
00720 share->state.key_map);
00721 uint32_t testflag=param.testflag;
00722 if (mi_test_if_sort_rep(file,file->state->records,key_map,0) &&
00723 (local_testflag & T_REP_BY_SORT))
00724 {
00725 local_testflag|= T_STATISTICS;
00726 param.testflag|= T_STATISTICS;
00727 statistics_done=1;
00728 {
00729 session->set_proc_info("Repair by sorting");
00730 error = mi_repair_by_sort(¶m, file, fixed_name,
00731 param.testflag & T_QUICK);
00732 }
00733 }
00734 else
00735 {
00736 session->set_proc_info("Repair with keycache");
00737 param.testflag &= ~T_REP_BY_SORT;
00738 error= mi_repair(¶m, file, fixed_name,
00739 param.testflag & T_QUICK);
00740 }
00741 param.testflag=testflag;
00742 optimize_done=1;
00743 }
00744 if (!error)
00745 {
00746 if ((local_testflag & T_SORT_INDEX) &&
00747 (share->state.changed & STATE_NOT_SORTED_PAGES))
00748 {
00749 optimize_done=1;
00750 session->set_proc_info("Sorting index");
00751 error=mi_sort_index(¶m,file,fixed_name);
00752 }
00753 if (!statistics_done && (local_testflag & T_STATISTICS))
00754 {
00755 if (share->state.changed & STATE_NOT_ANALYZED)
00756 {
00757 optimize_done=1;
00758 session->set_proc_info("Analyzing");
00759 error = chk_key(¶m, file);
00760 }
00761 else
00762 local_testflag&= ~T_STATISTICS;
00763 }
00764 }
00765 session->set_proc_info("Saving state");
00766 if (!error)
00767 {
00768 if ((share->state.changed & STATE_CHANGED) || mi_is_crashed(file))
00769 {
00770 share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
00771 STATE_CRASHED_ON_REPAIR);
00772 file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
00773 }
00774
00775
00776
00777
00778 if (file->state != &file->s->state.state)
00779 file->s->state.state = *file->state;
00780 if (file->s->base.auto_key)
00781 update_auto_increment_key(¶m, file, 1);
00782 if (optimize_done)
00783 error = update_state_info(¶m, file,
00784 UPDATE_TIME | UPDATE_OPEN_COUNT |
00785 (local_testflag &
00786 T_STATISTICS ? UPDATE_STAT : 0));
00787 info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
00788 HA_STATUS_CONST);
00789 if (rows != file->state->records && ! (param.testflag & T_VERY_SILENT))
00790 {
00791 char llbuff[22],llbuff2[22];
00792 mi_check_print_warning(¶m,"Number of rows changed from %s to %s",
00793 internal::llstr(rows,llbuff),
00794 internal::llstr(file->state->records,llbuff2));
00795 }
00796 }
00797 else
00798 {
00799 mi_mark_crashed_on_repair(file);
00800 file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
00801 update_state_info(¶m, file, 0);
00802 }
00803 session->set_proc_info(old_proc_info);
00804 mi_lock_database(file,F_UNLCK);
00805
00806 return(error ? HA_ADMIN_FAILED :
00807 !optimize_done ? HA_ADMIN_ALREADY_DONE : HA_ADMIN_OK);
00808 }
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830
00831 int ha_myisam::disable_indexes(uint32_t mode)
00832 {
00833 int error;
00834
00835 if (mode == HA_KEY_SWITCH_ALL)
00836 {
00837
00838 error= mi_disable_indexes(file);
00839 }
00840 else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
00841 {
00842 mi_extra(file, HA_EXTRA_NO_KEYS, 0);
00843 info(HA_STATUS_CONST);
00844 error= 0;
00845 }
00846 else
00847 {
00848
00849 error= HA_ERR_WRONG_COMMAND;
00850 }
00851 return error;
00852 }
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882
00883 int ha_myisam::enable_indexes(uint32_t mode)
00884 {
00885 int error;
00886
00887 if (mi_is_all_keys_active(file->s->state.key_map, file->s->base.keys))
00888 {
00889
00890 return 0;
00891 }
00892
00893 if (mode == HA_KEY_SWITCH_ALL)
00894 {
00895 error= mi_enable_indexes(file);
00896
00897
00898
00899
00900
00901 }
00902 else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
00903 {
00904 Session *session= getTable()->in_use;
00905 boost::scoped_ptr<MI_CHECK> param_ap(new MI_CHECK);
00906 MI_CHECK ¶m= *param_ap.get();
00907 const char *save_proc_info= session->get_proc_info();
00908 session->set_proc_info("Creating index");
00909 myisamchk_init(¶m);
00910 param.op_name= "recreating_index";
00911 param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
00912 T_CREATE_MISSING_KEYS);
00913 param.myf_rw&= ~MY_WAIT_IF_FULL;
00914 param.sort_buffer_length= static_cast<size_t>(sort_buffer_size);
00915 param.stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
00916 if ((error= (repair(session,param,0) != HA_ADMIN_OK)) && param.retry_repair)
00917 {
00918 errmsg_printf(error::WARN, "Warning: Enabling keys got errno %d on %s.%s, retrying",
00919 errno, param.db_name, param.table_name);
00920
00921 param.testflag&= ~(T_REP_BY_SORT | T_QUICK);
00922 error= (repair(session,param,0) != HA_ADMIN_OK);
00923
00924
00925
00926
00927
00928 if (not error)
00929 session->clear_error();
00930 }
00931 info(HA_STATUS_CONST);
00932 session->set_proc_info(save_proc_info);
00933 }
00934 else
00935 {
00936
00937 error= HA_ERR_WRONG_COMMAND;
00938 }
00939 return error;
00940 }
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957
00958 int ha_myisam::indexes_are_disabled(void)
00959 {
00960
00961 return mi_indexes_are_disabled(file);
00962 }
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979 void ha_myisam::start_bulk_insert(ha_rows rows)
00980 {
00981 Session *session= getTable()->in_use;
00982 ulong size= session->variables.read_buff_size;
00983
00984
00985 if (! rows || (rows > MI_MIN_ROWS_TO_USE_WRITE_CACHE))
00986 mi_extra(file, HA_EXTRA_WRITE_CACHE, (void*) &size);
00987
00988 can_enable_indexes= mi_is_all_keys_active(file->s->state.key_map,
00989 file->s->base.keys);
00990
00991
00992
00993
00994
00995
00996
00997 if (file->state->records == 0 && can_enable_indexes &&
00998 (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES))
00999 mi_disable_non_unique_index(file,rows);
01000 else
01001 if (!file->bulk_insert &&
01002 (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT))
01003 {
01004 mi_init_bulk_insert(file,
01005 (size_t)session->variables.bulk_insert_buff_size,
01006 rows);
01007 }
01008 }
01009
01010
01011
01012
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023 int ha_myisam::end_bulk_insert()
01024 {
01025 mi_end_bulk_insert(file);
01026 int err=mi_extra(file, HA_EXTRA_NO_CACHE, 0);
01027 return err ? err : can_enable_indexes ?
01028 enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE) : 0;
01029 }
01030
01031
01032
01033 int ha_myisam::doUpdateRecord(const unsigned char *old_data, unsigned char *new_data)
01034 {
01035 return mi_update(file,old_data,new_data);
01036 }
01037
01038 int ha_myisam::doDeleteRecord(const unsigned char *buf)
01039 {
01040 return mi_delete(file,buf);
01041 }
01042
01043
01044 int ha_myisam::doStartIndexScan(uint32_t idx, bool )
01045 {
01046 active_index=idx;
01047
01048 return 0;
01049 }
01050
01051
01052 int ha_myisam::doEndIndexScan()
01053 {
01054 active_index=MAX_KEY;
01055 return 0;
01056 }
01057
01058
01059 int ha_myisam::index_read_map(unsigned char *buf, const unsigned char *key,
01060 key_part_map keypart_map,
01061 enum ha_rkey_function find_flag)
01062 {
01063 assert(inited==INDEX);
01064 ha_statistic_increment(&system_status_var::ha_read_key_count);
01065 int error=mi_rkey(file, buf, active_index, key, keypart_map, find_flag);
01066 getTable()->status=error ? STATUS_NOT_FOUND: 0;
01067 return error;
01068 }
01069
01070 int ha_myisam::index_read_idx_map(unsigned char *buf, uint32_t index, const unsigned char *key,
01071 key_part_map keypart_map,
01072 enum ha_rkey_function find_flag)
01073 {
01074 ha_statistic_increment(&system_status_var::ha_read_key_count);
01075 int error=mi_rkey(file, buf, index, key, keypart_map, find_flag);
01076 getTable()->status=error ? STATUS_NOT_FOUND: 0;
01077 return error;
01078 }
01079
01080 int ha_myisam::index_read_last_map(unsigned char *buf, const unsigned char *key,
01081 key_part_map keypart_map)
01082 {
01083 assert(inited==INDEX);
01084 ha_statistic_increment(&system_status_var::ha_read_key_count);
01085 int error=mi_rkey(file, buf, active_index, key, keypart_map,
01086 HA_READ_PREFIX_LAST);
01087 getTable()->status=error ? STATUS_NOT_FOUND: 0;
01088 return(error);
01089 }
01090
01091 int ha_myisam::index_next(unsigned char *buf)
01092 {
01093 assert(inited==INDEX);
01094 ha_statistic_increment(&system_status_var::ha_read_next_count);
01095 int error=mi_rnext(file,buf,active_index);
01096 getTable()->status=error ? STATUS_NOT_FOUND: 0;
01097 return error;
01098 }
01099
01100 int ha_myisam::index_prev(unsigned char *buf)
01101 {
01102 assert(inited==INDEX);
01103 ha_statistic_increment(&system_status_var::ha_read_prev_count);
01104 int error=mi_rprev(file,buf, active_index);
01105 getTable()->status=error ? STATUS_NOT_FOUND: 0;
01106 return error;
01107 }
01108
01109 int ha_myisam::index_first(unsigned char *buf)
01110 {
01111 assert(inited==INDEX);
01112 ha_statistic_increment(&system_status_var::ha_read_first_count);
01113 int error=mi_rfirst(file, buf, active_index);
01114 getTable()->status=error ? STATUS_NOT_FOUND: 0;
01115 return error;
01116 }
01117
01118 int ha_myisam::index_last(unsigned char *buf)
01119 {
01120 assert(inited==INDEX);
01121 ha_statistic_increment(&system_status_var::ha_read_last_count);
01122 int error=mi_rlast(file, buf, active_index);
01123 getTable()->status=error ? STATUS_NOT_FOUND: 0;
01124 return error;
01125 }
01126
01127 int ha_myisam::index_next_same(unsigned char *buf,
01128 const unsigned char *,
01129 uint32_t )
01130 {
01131 int error;
01132 assert(inited==INDEX);
01133 ha_statistic_increment(&system_status_var::ha_read_next_count);
01134 do
01135 {
01136 error= mi_rnext_same(file,buf);
01137 } while (error == HA_ERR_RECORD_DELETED);
01138 getTable()->status=error ? STATUS_NOT_FOUND: 0;
01139 return error;
01140 }
01141
01142 int ha_myisam::read_range_first(const key_range *start_key,
01143 const key_range *end_key,
01144 bool eq_range_arg,
01145 bool sorted )
01146 {
01147 int res;
01148
01149
01150
01151 res= Cursor::read_range_first(start_key, end_key, eq_range_arg, sorted);
01152
01153
01154
01155 return res;
01156 }
01157
01158
01159 int ha_myisam::read_range_next()
01160 {
01161 int res= Cursor::read_range_next();
01162
01163
01164 return res;
01165 }
01166
01167
01168 int ha_myisam::doStartTableScan(bool scan)
01169 {
01170 if (scan)
01171 return mi_scan_init(file);
01172 return mi_reset(file);
01173 }
01174
01175 int ha_myisam::rnd_next(unsigned char *buf)
01176 {
01177 ha_statistic_increment(&system_status_var::ha_read_rnd_next_count);
01178 int error=mi_scan(file, buf);
01179 getTable()->status=error ? STATUS_NOT_FOUND: 0;
01180 return error;
01181 }
01182
01183 int ha_myisam::rnd_pos(unsigned char *buf, unsigned char *pos)
01184 {
01185 ha_statistic_increment(&system_status_var::ha_read_rnd_count);
01186 int error=mi_rrnd(file, buf, internal::my_get_ptr(pos,ref_length));
01187 getTable()->status=error ? STATUS_NOT_FOUND: 0;
01188 return error;
01189 }
01190
01191
01192 void ha_myisam::position(const unsigned char *)
01193 {
01194 internal::my_off_t row_position= mi_position(file);
01195 internal::my_store_ptr(ref, ref_length, row_position);
01196 }
01197
01198 int ha_myisam::info(uint32_t flag)
01199 {
01200 MI_ISAMINFO misam_info;
01201 char name_buff[FN_REFLEN];
01202
01203 (void) mi_status(file,&misam_info,flag);
01204 if (flag & HA_STATUS_VARIABLE)
01205 {
01206 stats.records= misam_info.records;
01207 stats.deleted= misam_info.deleted;
01208 stats.data_file_length= misam_info.data_file_length;
01209 stats.index_file_length= misam_info.index_file_length;
01210 stats.delete_length= misam_info.delete_length;
01211 stats.check_time= misam_info.check_time;
01212 stats.mean_rec_length= misam_info.mean_reclength;
01213 }
01214 if (flag & HA_STATUS_CONST)
01215 {
01216 TableShare *share= getTable()->getMutableShare();
01217 stats.max_data_file_length= misam_info.max_data_file_length;
01218 stats.max_index_file_length= misam_info.max_index_file_length;
01219 stats.create_time= misam_info.create_time;
01220 ref_length= misam_info.reflength;
01221 share->db_options_in_use= misam_info.options;
01222 stats.block_size= myisam_key_cache_block_size;
01223
01224 set_prefix(share->keys_in_use, share->sizeKeys());
01225
01226
01227
01228
01229
01230
01231 ostringstream ostr;
01232 string binary_key_map;
01233 uint64_t num= misam_info.key_map;
01234
01235
01236
01237
01238 while (num > 0)
01239 {
01240 uint64_t bin_digit= num % 2;
01241 ostr << bin_digit;
01242 num/= 2;
01243 }
01244 binary_key_map.append(ostr.str());
01245
01246
01247
01248
01249
01250
01251
01252 if (MAX_INDEXES <= 64)
01253 {
01254 size_t len= 72 - binary_key_map.length();
01255 string all_zeros(len, '0');
01256 binary_key_map.insert(binary_key_map.begin(),
01257 all_zeros.begin(),
01258 all_zeros.end());
01259 }
01260 else
01261 {
01262 size_t len= (MAX_INDEXES + 7) / 8 * 8;
01263 string all_zeros(len, '0');
01264 binary_key_map.insert(binary_key_map.begin(),
01265 all_zeros.begin(),
01266 all_zeros.end());
01267 }
01268 key_map tmp_map(binary_key_map);
01269 share->keys_in_use&= tmp_map;
01270 share->keys_for_keyread&= share->keys_in_use;
01271 share->db_record_offset= misam_info.record_offset;
01272 if (share->key_parts)
01273 memcpy(getTable()->key_info[0].rec_per_key,
01274 misam_info.rec_per_key,
01275 sizeof(getTable()->key_info[0].rec_per_key)*share->key_parts);
01276 assert(share->getType() != message::Table::STANDARD);
01277
01278
01279
01280
01281
01282 data_file_name= index_file_name= 0;
01283 internal::fn_format(name_buff, file->filename, "", MI_NAME_DEXT,
01284 MY_APPEND_EXT | MY_UNPACK_FILENAME);
01285 if (strcmp(name_buff, misam_info.data_file_name))
01286 data_file_name=misam_info.data_file_name;
01287 internal::fn_format(name_buff, file->filename, "", MI_NAME_IEXT,
01288 MY_APPEND_EXT | MY_UNPACK_FILENAME);
01289 if (strcmp(name_buff, misam_info.index_file_name))
01290 index_file_name=misam_info.index_file_name;
01291 }
01292 if (flag & HA_STATUS_ERRKEY)
01293 {
01294 errkey = misam_info.errkey;
01295 internal::my_store_ptr(dup_ref, ref_length, misam_info.dupp_key_pos);
01296 }
01297 if (flag & HA_STATUS_TIME)
01298 stats.update_time = misam_info.update_time;
01299 if (flag & HA_STATUS_AUTO)
01300 stats.auto_increment_value= misam_info.auto_increment;
01301
01302 return 0;
01303 }
01304
01305
01306 int ha_myisam::extra(enum ha_extra_function operation)
01307 {
01308 return mi_extra(file, operation, 0);
01309 }
01310
01311 int ha_myisam::reset(void)
01312 {
01313 return mi_reset(file);
01314 }
01315
01316
01317
01318 int ha_myisam::extra_opt(enum ha_extra_function operation, uint32_t cache_size)
01319 {
01320 return mi_extra(file, operation, (void*) &cache_size);
01321 }
01322
01323 int ha_myisam::delete_all_rows()
01324 {
01325 return mi_delete_all_rows(file);
01326 }
01327
01328 int MyisamEngine::doDropTable(Session &session,
01329 const identifier::Table &identifier)
01330 {
01331 session.getMessageCache().removeTableMessage(identifier);
01332
01333 return mi_delete_table(identifier.getPath().c_str());
01334 }
01335
01336
01337 int ha_myisam::external_lock(Session *session, int lock_type)
01338 {
01339 file->in_use= session;
01340 return mi_lock_database(file, !getTable()->getShare()->getType() ?
01341 lock_type : ((lock_type == F_UNLCK) ?
01342 F_UNLCK : F_EXTRA_LCK));
01343 }
01344
01345 int MyisamEngine::doCreateTable(Session &session,
01346 Table& table_arg,
01347 const identifier::Table &identifier,
01348 const message::Table& create_proto)
01349 {
01350 int error;
01351 uint32_t create_flags= 0, create_records;
01352 char buff[FN_REFLEN];
01353 MI_KEYDEF *keydef;
01354 MI_COLUMNDEF *recinfo;
01355 MI_CREATE_INFO create_info;
01356 TableShare *share= table_arg.getMutableShare();
01357 uint32_t options= share->db_options_in_use;
01358 if ((error= table2myisam(&table_arg, &keydef, &recinfo, &create_records)))
01359 return(error);
01360 memset(&create_info, 0, sizeof(create_info));
01361 create_info.max_rows= create_proto.options().max_rows();
01362 create_info.reloc_rows= create_proto.options().min_rows();
01363 create_info.with_auto_increment= share->next_number_key_offset == 0;
01364 create_info.auto_increment= (create_proto.options().has_auto_increment_value() ?
01365 create_proto.options().auto_increment_value() -1 :
01366 (uint64_t) 0);
01367 create_info.data_file_length= (create_proto.options().max_rows() *
01368 create_proto.options().avg_row_length());
01369 create_info.data_file_name= NULL;
01370 create_info.index_file_name= NULL;
01371 create_info.language= share->table_charset->number;
01372
01373 if (create_proto.type() == message::Table::TEMPORARY)
01374 create_flags|= HA_CREATE_TMP_TABLE;
01375 if (options & HA_OPTION_PACK_RECORD)
01376 create_flags|= HA_PACK_RECORD;
01377
01378
01379 error= mi_create(internal::fn_format(buff, identifier.getPath().c_str(), "", "",
01380 MY_UNPACK_FILENAME|MY_APPEND_EXT),
01381 share->sizeKeys(), keydef,
01382 create_records, recinfo,
01383 0, (MI_UNIQUEDEF*) 0,
01384 &create_info, create_flags);
01385 free((unsigned char*) recinfo);
01386
01387 session.getMessageCache().storeTableMessage(identifier, create_proto);
01388
01389 return error;
01390 }
01391
01392
01393 int MyisamEngine::doRenameTable(Session &session, const identifier::Table &from, const identifier::Table &to)
01394 {
01395 session.getMessageCache().renameTableMessage(from, to);
01396
01397 return mi_rename(from.getPath().c_str(), to.getPath().c_str());
01398 }
01399
01400
01401 void ha_myisam::get_auto_increment(uint64_t ,
01402 uint64_t ,
01403 uint64_t ,
01404 uint64_t *first_value,
01405 uint64_t *nb_reserved_values)
01406 {
01407 uint64_t nr;
01408 int error;
01409 unsigned char key[MI_MAX_KEY_LENGTH];
01410
01411 if (!getTable()->getShare()->next_number_key_offset)
01412 {
01413 ha_myisam::info(HA_STATUS_AUTO);
01414 *first_value= stats.auto_increment_value;
01415
01416 *nb_reserved_values= UINT64_MAX;
01417 return;
01418 }
01419
01420
01421 mi_flush_bulk_insert(file, getTable()->getShare()->next_number_index);
01422
01423 (void) extra(HA_EXTRA_KEYREAD);
01424 key_copy(key, getTable()->getInsertRecord(),
01425 &getTable()->key_info[getTable()->getShare()->next_number_index],
01426 getTable()->getShare()->next_number_key_offset);
01427 error= mi_rkey(file, getTable()->getUpdateRecord(), (int) getTable()->getShare()->next_number_index,
01428 key, make_prev_keypart_map(getTable()->getShare()->next_number_keypart),
01429 HA_READ_PREFIX_LAST);
01430 if (error)
01431 nr= 1;
01432 else
01433 {
01434
01435 nr= ((uint64_t) getTable()->next_number_field->
01436 val_int_offset(getTable()->getShare()->rec_buff_length)+1);
01437 }
01438 extra(HA_EXTRA_NO_KEYREAD);
01439 *first_value= nr;
01440
01441
01442
01443
01444
01445
01446 *nb_reserved_values= 1;
01447 }
01448
01449
01450
01451
01452
01453
01454
01455
01456
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467
01468
01469
01470
01471
01472
01473
01474
01475 ha_rows ha_myisam::records_in_range(uint32_t inx, key_range *min_key,
01476 key_range *max_key)
01477 {
01478 return (ha_rows) mi_records_in_range(file, (int) inx, min_key, max_key);
01479 }
01480
01481
01482 uint32_t ha_myisam::checksum() const
01483 {
01484 return (uint)file->state->checksum;
01485 }
01486
01487 static int myisam_init(module::Context &context)
01488 {
01489 context.add(new MyisamEngine(engine_name));
01490 context.registerVariable(new sys_var_constrained_value<size_t>("sort-buffer-size",
01491 sort_buffer_size));
01492 context.registerVariable(new sys_var_uint64_t_ptr("max_sort_file_size",
01493 &max_sort_file_size,
01494 context.getOptions()["max-sort-file-size"].as<uint64_t>()));
01495
01496 return 0;
01497 }
01498
01499
01500 static void init_options(drizzled::module::option_context &context)
01501 {
01502 context("max-sort-file-size",
01503 po::value<uint64_t>(&max_sort_file_size)->default_value(INT32_MAX),
01504 _("Don't use the fast sort index method to created index if the temporary file would get bigger than this."));
01505 context("sort-buffer-size",
01506 po::value<sort_buffer_constraint>(&sort_buffer_size)->default_value(8192*1024),
01507 _("The buffer that is allocated when sorting the index when doing a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE."));
01508 }
01509
01510
01511 DRIZZLE_DECLARE_PLUGIN
01512 {
01513 DRIZZLE_VERSION_ID,
01514 "MyISAM",
01515 "2.0",
01516 "MySQL AB",
01517 "Default engine as of MySQL 3.23 with great performance",
01518 PLUGIN_LICENSE_GPL,
01519 myisam_init,
01520 NULL,
01521 init_options
01522 }
01523 DRIZZLE_DECLARE_PLUGIN_END;