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