00001 /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 00018 #ifdef USE_PRAGMA_IMPLEMENTATION 00019 #pragma implementation // gcc: Class implementation 00020 #endif 00021 00022 #include "mysql_priv.h" 00023 #include <m_ctype.h> 00024 #include "ha_myisammrg.h" 00025 #ifndef MASTER 00026 #include "../srclib/myisammrg/myrg_def.h" 00027 #else 00028 #include "../storage/myisammrg/myrg_def.h" 00029 #endif 00030 00031 #include <mysql/plugin.h> 00032 00033 /***************************************************************************** 00034 ** MyISAM MERGE tables 00035 *****************************************************************************/ 00036 00037 static handler *myisammrg_create_handler(TABLE_SHARE *table, 00038 MEM_ROOT *mem_root); 00039 00040 /* MyISAM MERGE handlerton */ 00041 00042 handlerton myisammrg_hton; 00043 00044 static handler *myisammrg_create_handler(TABLE_SHARE *table, 00045 MEM_ROOT *mem_root) 00046 { 00047 return new (mem_root) ha_myisammrg(table); 00048 } 00049 00050 00051 ha_myisammrg::ha_myisammrg(TABLE_SHARE *table_arg) 00052 :handler(&myisammrg_hton, table_arg), file(0) 00053 {} 00054 00055 static const char *ha_myisammrg_exts[] = { 00056 ".MRG", 00057 NullS 00058 }; 00059 00060 const char **ha_myisammrg::bas_ext() const 00061 { 00062 return ha_myisammrg_exts; 00063 } 00064 00065 00066 const char *ha_myisammrg::index_type(uint key_number) 00067 { 00068 return ((table->key_info[key_number].flags & HA_FULLTEXT) ? 00069 "FULLTEXT" : 00070 (table->key_info[key_number].flags & HA_SPATIAL) ? 00071 "SPATIAL" : 00072 (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ? 00073 "RTREE" : 00074 "BTREE"); 00075 } 00076 00077 00078 int ha_myisammrg::open(const char *name, int mode, uint test_if_locked) 00079 { 00080 char name_buff[FN_REFLEN]; 00081 00082 DBUG_PRINT("info", ("ha_myisammrg::open")); 00083 if (!(file=myrg_open(fn_format(name_buff,name,"","", 00084 MY_UNPACK_FILENAME|MY_APPEND_EXT), 00085 mode, test_if_locked))) 00086 { 00087 DBUG_PRINT("info", ("ha_myisammrg::open exit %d", my_errno)); 00088 return (my_errno ? my_errno : -1); 00089 } 00090 DBUG_PRINT("info", ("ha_myisammrg::open myrg_extrafunc...")); 00091 myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref); 00092 if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED || 00093 test_if_locked == HA_OPEN_ABORT_IF_LOCKED)) 00094 myrg_extra(file,HA_EXTRA_NO_WAIT_LOCK,0); 00095 info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); 00096 if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED)) 00097 myrg_extra(file,HA_EXTRA_WAIT_LOCK,0); 00098 00099 if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length) 00100 { 00101 DBUG_PRINT("error",("reclength: %d mean_rec_length: %d", 00102 table->s->reclength, stats.mean_rec_length)); 00103 goto err; 00104 } 00105 #if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4 00106 /* Merge table has more than 2G rows */ 00107 if (table->s->crashed) 00108 goto err; 00109 #endif 00110 return (0); 00111 err: 00112 myrg_close(file); 00113 file=0; 00114 return (my_errno= HA_ERR_WRONG_MRG_TABLE_DEF); 00115 } 00116 00117 int ha_myisammrg::close(void) 00118 { 00119 return myrg_close(file); 00120 } 00121 00122 int ha_myisammrg::write_row(byte * buf) 00123 { 00124 statistic_increment(table->in_use->status_var.ha_write_count,&LOCK_status); 00125 00126 if (file->merge_insert_method == MERGE_INSERT_DISABLED || !file->tables) 00127 return (HA_ERR_TABLE_READONLY); 00128 00129 if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) 00130 table->timestamp_field->set_time(); 00131 if (table->next_number_field && buf == table->record[0]) 00132 update_auto_increment(); 00133 return myrg_write(file,buf); 00134 } 00135 00136 int ha_myisammrg::update_row(const byte * old_data, byte * new_data) 00137 { 00138 statistic_increment(table->in_use->status_var.ha_update_count,&LOCK_status); 00139 if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) 00140 table->timestamp_field->set_time(); 00141 return myrg_update(file,old_data,new_data); 00142 } 00143 00144 int ha_myisammrg::delete_row(const byte * buf) 00145 { 00146 statistic_increment(table->in_use->status_var.ha_delete_count,&LOCK_status); 00147 return myrg_delete(file,buf); 00148 } 00149 00150 int ha_myisammrg::index_read(byte * buf, const byte * key, 00151 uint key_len, enum ha_rkey_function find_flag) 00152 { 00153 statistic_increment(table->in_use->status_var.ha_read_key_count, 00154 &LOCK_status); 00155 int error=myrg_rkey(file,buf,active_index, key, key_len, find_flag); 00156 table->status=error ? STATUS_NOT_FOUND: 0; 00157 return error; 00158 } 00159 00160 int ha_myisammrg::index_read_idx(byte * buf, uint index, const byte * key, 00161 uint key_len, enum ha_rkey_function find_flag) 00162 { 00163 statistic_increment(table->in_use->status_var.ha_read_key_count, 00164 &LOCK_status); 00165 int error=myrg_rkey(file,buf,index, key, key_len, find_flag); 00166 table->status=error ? STATUS_NOT_FOUND: 0; 00167 return error; 00168 } 00169 00170 int ha_myisammrg::index_read_last(byte * buf, const byte * key, uint key_len) 00171 { 00172 statistic_increment(table->in_use->status_var.ha_read_key_count, 00173 &LOCK_status); 00174 int error=myrg_rkey(file,buf,active_index, key, key_len, 00175 HA_READ_PREFIX_LAST); 00176 table->status=error ? STATUS_NOT_FOUND: 0; 00177 return error; 00178 } 00179 00180 int ha_myisammrg::index_next(byte * buf) 00181 { 00182 statistic_increment(table->in_use->status_var.ha_read_next_count, 00183 &LOCK_status); 00184 int error=myrg_rnext(file,buf,active_index); 00185 table->status=error ? STATUS_NOT_FOUND: 0; 00186 return error; 00187 } 00188 00189 int ha_myisammrg::index_prev(byte * buf) 00190 { 00191 statistic_increment(table->in_use->status_var.ha_read_prev_count, 00192 &LOCK_status); 00193 int error=myrg_rprev(file,buf, active_index); 00194 table->status=error ? STATUS_NOT_FOUND: 0; 00195 return error; 00196 } 00197 00198 int ha_myisammrg::index_first(byte * buf) 00199 { 00200 statistic_increment(table->in_use->status_var.ha_read_first_count, 00201 &LOCK_status); 00202 int error=myrg_rfirst(file, buf, active_index); 00203 table->status=error ? STATUS_NOT_FOUND: 0; 00204 return error; 00205 } 00206 00207 int ha_myisammrg::index_last(byte * buf) 00208 { 00209 statistic_increment(table->in_use->status_var.ha_read_last_count, 00210 &LOCK_status); 00211 int error=myrg_rlast(file, buf, active_index); 00212 table->status=error ? STATUS_NOT_FOUND: 0; 00213 return error; 00214 } 00215 00216 int ha_myisammrg::index_next_same(byte * buf, 00217 const byte *key __attribute__((unused)), 00218 uint length __attribute__((unused))) 00219 { 00220 statistic_increment(table->in_use->status_var.ha_read_next_count, 00221 &LOCK_status); 00222 int error=myrg_rnext_same(file,buf); 00223 table->status=error ? STATUS_NOT_FOUND: 0; 00224 return error; 00225 } 00226 00227 00228 int ha_myisammrg::rnd_init(bool scan) 00229 { 00230 return myrg_reset(file); 00231 } 00232 00233 00234 int ha_myisammrg::rnd_next(byte *buf) 00235 { 00236 statistic_increment(table->in_use->status_var.ha_read_rnd_next_count, 00237 &LOCK_status); 00238 int error=myrg_rrnd(file, buf, HA_OFFSET_ERROR); 00239 table->status=error ? STATUS_NOT_FOUND: 0; 00240 return error; 00241 } 00242 00243 00244 int ha_myisammrg::rnd_pos(byte * buf, byte *pos) 00245 { 00246 statistic_increment(table->in_use->status_var.ha_read_rnd_count, 00247 &LOCK_status); 00248 int error=myrg_rrnd(file, buf, my_get_ptr(pos,ref_length)); 00249 table->status=error ? STATUS_NOT_FOUND: 0; 00250 return error; 00251 } 00252 00253 void ha_myisammrg::position(const byte *record) 00254 { 00255 ulonglong position= myrg_position(file); 00256 my_store_ptr(ref, ref_length, (my_off_t) position); 00257 } 00258 00259 00260 ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key, 00261 key_range *max_key) 00262 { 00263 return (ha_rows) myrg_records_in_range(file, (int) inx, min_key, max_key); 00264 } 00265 00266 00267 void ha_myisammrg::info(uint flag) 00268 { 00269 MYMERGE_INFO info; 00270 (void) myrg_status(file,&info,flag); 00271 /* 00272 The following fails if one has not compiled MySQL with -DBIG_TABLES 00273 and one has more than 2^32 rows in the merge tables. 00274 */ 00275 stats.records = (ha_rows) info.records; 00276 stats.deleted = (ha_rows) info.deleted; 00277 #if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4 00278 if ((info.records >= (ulonglong) 1 << 32) || 00279 (info.deleted >= (ulonglong) 1 << 32)) 00280 table->s->crashed= 1; 00281 #endif 00282 stats.data_file_length=info.data_file_length; 00283 errkey = info.errkey; 00284 table->s->keys_in_use.set_prefix(table->s->keys); 00285 table->s->db_options_in_use= info.options; 00286 stats.mean_rec_length= info.reclength; 00287 00288 /* 00289 The handler::block_size is used all over the code in index scan cost 00290 calculations. It is used to get number of disk seeks required to 00291 retrieve a number of index tuples. 00292 If the merge table has N underlying tables, then (assuming underlying 00293 tables have equal size, the only "simple" approach we can use) 00294 retrieving X index records from a merge table will require N times more 00295 disk seeks compared to doing the same on a MyISAM table with equal 00296 number of records. 00297 In the edge case (file_tables > myisam_block_size) we'll get 00298 block_size==0, and index calculation code will act as if we need one 00299 disk seek to retrieve one index tuple. 00300 00301 TODO: In 5.2 index scan cost calculation will be factored out into a 00302 virtual function in class handler and we'll be able to remove this hack. 00303 */ 00304 stats.block_size= 0; 00305 if (file->tables) 00306 stats.block_size= myisam_block_size / file->tables; 00307 00308 stats.update_time= 0; 00309 #if SIZEOF_OFF_T > 4 00310 ref_length=6; // Should be big enough 00311 #else 00312 ref_length=4; // Can't be > than my_off_t 00313 #endif 00314 if (flag & HA_STATUS_CONST) 00315 { 00316 if (table->s->key_parts && info.rec_per_key) 00317 memcpy((char*) table->key_info[0].rec_per_key, 00318 (char*) info.rec_per_key, 00319 sizeof(table->key_info[0].rec_per_key)*table->s->key_parts); 00320 } 00321 } 00322 00323 00324 int ha_myisammrg::extra(enum ha_extra_function operation) 00325 { 00326 /* As this is just a mapping, we don't have to force the underlying 00327 tables to be closed */ 00328 if (operation == HA_EXTRA_FORCE_REOPEN || 00329 operation == HA_EXTRA_PREPARE_FOR_DELETE) 00330 return 0; 00331 return myrg_extra(file,operation,0); 00332 } 00333 00334 int ha_myisammrg::reset(void) 00335 { 00336 return myrg_reset(file); 00337 } 00338 00339 /* To be used with WRITE_CACHE, EXTRA_CACHE and BULK_INSERT_BEGIN */ 00340 00341 int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size) 00342 { 00343 if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE) 00344 return 0; 00345 return myrg_extra(file, operation, (void*) &cache_size); 00346 } 00347 00348 int ha_myisammrg::external_lock(THD *thd, int lock_type) 00349 { 00350 return myrg_lock_database(file,lock_type); 00351 } 00352 00353 uint ha_myisammrg::lock_count(void) const 00354 { 00355 return file->tables; 00356 } 00357 00358 00359 THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd, 00360 THR_LOCK_DATA **to, 00361 enum thr_lock_type lock_type) 00362 { 00363 MYRG_TABLE *open_table; 00364 00365 for (open_table=file->open_tables ; 00366 open_table != file->end_table ; 00367 open_table++) 00368 { 00369 *(to++)= &open_table->table->lock; 00370 if (lock_type != TL_IGNORE && open_table->table->lock.type == TL_UNLOCK) 00371 open_table->table->lock.type=lock_type; 00372 } 00373 return to; 00374 } 00375 00376 00377 /* Find out database name and table name from a filename */ 00378 00379 static void split_file_name(const char *file_name, 00380 LEX_STRING *db, LEX_STRING *name) 00381 { 00382 uint dir_length, prefix_length; 00383 char buff[FN_REFLEN]; 00384 00385 db->length= 0; 00386 strmake(buff, file_name, sizeof(buff)-1); 00387 dir_length= dirname_length(buff); 00388 if (dir_length > 1) 00389 { 00390 /* Get database */ 00391 buff[dir_length-1]= 0; // Remove end '/' 00392 prefix_length= dirname_length(buff); 00393 db->str= (char*) file_name+ prefix_length; 00394 db->length= dir_length - prefix_length -1; 00395 } 00396 name->str= (char*) file_name+ dir_length; 00397 name->length= (uint) (fn_ext(name->str) - name->str); 00398 } 00399 00400 00401 void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info) 00402 { 00403 DBUG_ENTER("ha_myisammrg::update_create_info"); 00404 00405 if (!(create_info->used_fields & HA_CREATE_USED_UNION)) 00406 { 00407 MYRG_TABLE *open_table; 00408 THD *thd=current_thd; 00409 00410 create_info->merge_list.next= &create_info->merge_list.first; 00411 create_info->merge_list.elements=0; 00412 00413 for (open_table=file->open_tables ; 00414 open_table != file->end_table ; 00415 open_table++) 00416 { 00417 TABLE_LIST *ptr; 00418 LEX_STRING db, name; 00419 00420 if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) 00421 goto err; 00422 split_file_name(open_table->table->filename, &db, &name); 00423 if (!(ptr->table_name= thd->strmake(name.str, name.length))) 00424 goto err; 00425 if (db.length && !(ptr->db= thd->strmake(db.str, db.length))) 00426 goto err; 00427 00428 create_info->merge_list.elements++; 00429 (*create_info->merge_list.next) = (byte*) ptr; 00430 create_info->merge_list.next= (byte**) &ptr->next_local; 00431 } 00432 *create_info->merge_list.next=0; 00433 } 00434 if (!(create_info->used_fields & HA_CREATE_USED_INSERT_METHOD)) 00435 { 00436 create_info->merge_insert_method = file->merge_insert_method; 00437 } 00438 DBUG_VOID_RETURN; 00439 00440 err: 00441 create_info->merge_list.elements=0; 00442 create_info->merge_list.first=0; 00443 DBUG_VOID_RETURN; 00444 } 00445 00446 00447 int ha_myisammrg::create(const char *name, register TABLE *form, 00448 HA_CREATE_INFO *create_info) 00449 { 00450 char buff[FN_REFLEN]; 00451 const char **table_names, **pos; 00452 TABLE_LIST *tables= (TABLE_LIST*) create_info->merge_list.first; 00453 THD *thd= current_thd; 00454 uint dirlgt= dirname_length(name); 00455 DBUG_ENTER("ha_myisammrg::create"); 00456 00457 if (!(table_names= (const char**) 00458 thd->alloc((create_info->merge_list.elements+1) * sizeof(char*)))) 00459 DBUG_RETURN(HA_ERR_OUT_OF_MEM); 00460 for (pos= table_names; tables; tables= tables->next_local) 00461 { 00462 const char *table_name; 00463 TABLE *tbl= 0; 00464 if (create_info->options & HA_LEX_CREATE_TMP_TABLE) 00465 tbl= find_temporary_table(thd, tables); 00466 if (!tbl) 00467 { 00468 /* 00469 Construct the path to the MyISAM table. Try to meet two conditions: 00470 1.) Allow to include MyISAM tables from different databases, and 00471 2.) allow for moving DATADIR around in the file system. 00472 The first means that we need paths in the .MRG file. The second 00473 means that we should not have absolute paths in the .MRG file. 00474 The best, we can do, is to use 'mysql_data_home', which is '.' 00475 in mysqld and may be an absolute path in an embedded server. 00476 This means that it might not be possible to move the DATADIR of 00477 an embedded server without changing the paths in the .MRG file. 00478 */ 00479 uint length= build_table_filename(buff, sizeof(buff), 00480 tables->db, tables->table_name, "", 0); 00481 /* 00482 If a MyISAM table is in the same directory as the MERGE table, 00483 we use the table name without a path. This means that the 00484 DATADIR can easily be moved even for an embedded server as long 00485 as the MyISAM tables are from the same database as the MERGE table. 00486 */ 00487 if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt)) 00488 table_name= tables->table_name; 00489 else 00490 if (! (table_name= thd->strmake(buff, length))) 00491 DBUG_RETURN(HA_ERR_OUT_OF_MEM); 00492 } 00493 else 00494 table_name= tbl->s->path.str; 00495 *pos++= table_name; 00496 } 00497 *pos=0; 00498 DBUG_RETURN(myrg_create(fn_format(buff,name,"","", 00499 MY_RESOLVE_SYMLINKS| 00500 MY_UNPACK_FILENAME|MY_APPEND_EXT), 00501 table_names, 00502 create_info->merge_insert_method, 00503 (my_bool) 0)); 00504 } 00505 00506 00507 void ha_myisammrg::append_create_info(String *packet) 00508 { 00509 const char *current_db; 00510 uint db_length; 00511 THD *thd= current_thd; 00512 MYRG_TABLE *open_table, *first; 00513 00514 if (file->merge_insert_method != MERGE_INSERT_DISABLED) 00515 { 00516 packet->append(STRING_WITH_LEN(" INSERT_METHOD=")); 00517 packet->append(get_type(&merge_insert_method,file->merge_insert_method-1)); 00518 } 00519 packet->append(STRING_WITH_LEN(" UNION=(")); 00520 00521 current_db= table->s->db.str; 00522 db_length= table->s->db.length; 00523 00524 for (first=open_table=file->open_tables ; 00525 open_table != file->end_table ; 00526 open_table++) 00527 { 00528 LEX_STRING db, name; 00529 split_file_name(open_table->table->filename, &db, &name); 00530 if (open_table != first) 00531 packet->append(','); 00532 /* Report database for mapped table if it isn't in current database */ 00533 if (db.length && 00534 (db_length != db.length || 00535 strncmp(current_db, db.str, db.length))) 00536 { 00537 append_identifier(thd, packet, db.str, db.length); 00538 packet->append('.'); 00539 } 00540 append_identifier(thd, packet, name.str, name.length); 00541 } 00542 packet->append(')'); 00543 } 00544 00545 00546 bool ha_myisammrg::check_if_incompatible_data(HA_CREATE_INFO *info, 00547 uint table_changes) 00548 { 00549 /* 00550 For myisammrg, we should always re-generate the mapping file as this 00551 is trivial to do 00552 */ 00553 return COMPATIBLE_DATA_NO; 00554 } 00555 00556 static int myisammrg_init() 00557 { 00558 myisammrg_hton.state=have_merge_db; 00559 myisammrg_hton.db_type=DB_TYPE_MRG_MYISAM; 00560 myisammrg_hton.create=myisammrg_create_handler; 00561 myisammrg_hton.panic=myrg_panic; 00562 myisammrg_hton.flags= HTON_CAN_RECREATE; 00563 return 0; 00564 } 00565 00566 struct st_mysql_storage_engine myisammrg_storage_engine= 00567 { MYSQL_HANDLERTON_INTERFACE_VERSION, &myisammrg_hton }; 00568 00569 mysql_declare_plugin(myisammrg) 00570 { 00571 MYSQL_STORAGE_ENGINE_PLUGIN, 00572 &myisammrg_storage_engine, 00573 "MRG_MYISAM", 00574 "MySQL AB", 00575 "Collection of identical MyISAM tables", 00576 myisammrg_init, /* Plugin Init */ 00577 NULL, /* Plugin Deinit */ 00578 0x0100, /* 1.0 */ 00579 0 00580 } 00581 mysql_declare_plugin_end;
1.4.7

