161
const char *plugin::StorageEngine::checkLowercaseNames(const char *path,
164
if (flags.test(HTON_BIT_FILE_BASED))
167
/* Ensure that table handler get path in lower case */
168
if (tmp_path != path)
169
strcpy(tmp_path, path);
172
we only should turn into lowercase database/table part
173
so start the process after homedirectory
175
if (strstr(tmp_path, drizzle_tmpdir) == tmp_path)
176
my_casedn_str(files_charset_info, tmp_path + strlen(drizzle_tmpdir));
178
my_casedn_str(files_charset_info, tmp_path + drizzle_data_home_len);
184
void plugin::StorageEngine::add(plugin::StorageEngine *engine)
186
all_engines.add(engine);
189
void plugin::StorageEngine::remove(plugin::StorageEngine *engine)
191
all_engines.remove(engine);
194
plugin::StorageEngine *plugin::StorageEngine::findByName(Session *session,
198
transform(find_str.begin(), find_str.end(),
199
find_str.begin(), ::tolower);
200
string default_str("default");
201
if (find_str == default_str)
202
return ha_default_storage_engine(session);
204
plugin::StorageEngine *engine= all_engines.find(find_str);
206
if (engine && engine->is_user_selectable())
212
class StorageEngineCloseConnection
213
: public unary_function<plugin::StorageEngine *, void>
217
StorageEngineCloseConnection(Session *session_arg) : session(session_arg) {}
219
there's no need to rollback here as all transactions must
220
be rolled back already
222
inline result_type operator() (argument_type engine)
224
if (engine->is_enabled() &&
225
session_get_ha_data(session, engine))
226
engine->close_connection(session);
232
don't bother to rollback here, it's done already
234
void plugin::StorageEngine::closeConnection(Session* session)
236
for_each(all_engines.begin(), all_engines.end(),
237
StorageEngineCloseConnection(session));
240
void plugin::StorageEngine::dropDatabase(char* path)
242
for_each(all_engines.begin(), all_engines.end(),
243
bind2nd(mem_fun(&plugin::StorageEngine::drop_database),path));
246
int plugin::StorageEngine::commitOrRollbackByXID(XID *xid, bool commit)
251
transform(all_engines.begin(), all_engines.end(), results.begin(),
252
bind2nd(mem_fun(&plugin::StorageEngine::commit_by_xid),xid));
254
transform(all_engines.begin(), all_engines.end(), results.begin(),
255
bind2nd(mem_fun(&plugin::StorageEngine::rollback_by_xid),xid));
257
if (find_if(results.begin(), results.end(), bind2nd(equal_to<int>(),0))
265
This function should be called when MySQL sends rows of a SELECT result set
266
or the EOF mark to the client. It releases a possible adaptive hash index
267
S-latch held by session in InnoDB and also releases a possible InnoDB query
268
FIFO ticket to enter InnoDB. To save CPU time, InnoDB allows a session to
269
keep them over several calls of the InnoDB handler interface when a join
270
is executed. But when we let the control to pass to the client they have
271
to be released because if the application program uses mysql_use_result(),
272
it may deadlock on the S-latch if the application on another connection
273
performs another SQL query. In MySQL-4.1 this is even more important because
274
there a connection can have several SELECT queries open at the same time.
276
@param session the thread handle of the current connection
281
int plugin::StorageEngine::releaseTemporaryLatches(Session *session)
283
for_each(all_engines.begin(), all_engines.end(),
284
bind2nd(mem_fun(&plugin::StorageEngine::release_temporary_latches),session));
288
bool plugin::StorageEngine::flushLogs(plugin::StorageEngine *engine)
292
if (find_if(all_engines.begin(), all_engines.end(),
293
mem_fun(&plugin::StorageEngine::flush_logs))
294
!= all_engines.begin())
299
if ((!engine->is_enabled()) ||
300
(engine->flush_logs()))
307
recover() step of xa.
310
there are three modes of operation:
311
- automatic recover after a crash
312
in this case commit_list != 0, tc_heuristic_recover==0
313
all xids from commit_list are committed, others are rolled back
314
- manual (heuristic) recover
315
in this case commit_list==0, tc_heuristic_recover != 0
316
DBA has explicitly specified that all prepared transactions should
317
be committed (or rolled back).
318
- no recovery (MySQL did not detect a crash)
319
in this case commit_list==0, tc_heuristic_recover == 0
320
there should be no prepared transactions in this case.
322
class XARecover : unary_function<plugin::StorageEngine *, void>
324
int trans_len, found_foreign_xids, found_my_xids;
330
XARecover(XID *trans_list_arg, int trans_len_arg,
331
HASH *commit_list_arg, bool dry_run_arg)
332
: trans_len(trans_len_arg), found_foreign_xids(0), found_my_xids(0),
334
trans_list(trans_list_arg), commit_list(commit_list_arg),
340
return found_foreign_xids;
345
return found_my_xids;
348
result_type operator() (argument_type engine)
353
if (engine->is_enabled())
355
while ((got= engine->recover(trans_list, trans_len)) > 0 )
357
errmsg_printf(ERRMSG_LVL_INFO,
358
_("Found %d prepared transaction(s) in %s"),
359
got, engine->getName().c_str());
360
for (int i=0; i < got; i ++)
362
my_xid x=trans_list[i].get_my_xid();
363
if (!x) // not "mine" - that is generated by external TM
365
xid_cache_insert(trans_list+i, XA_PREPARED);
366
found_foreign_xids++;
376
hash_search(commit_list, (unsigned char *)&x, sizeof(x)) != 0 :
377
tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
379
engine->commit_by_xid(trans_list+i);
383
engine->rollback_by_xid(trans_list+i);
393
int plugin::StorageEngine::recover(HASH *commit_list)
395
XID *trans_list= NULL;
398
bool dry_run= (commit_list==0 && tc_heuristic_recover==0);
400
/* commit_list and tc_heuristic_recover cannot be set both */
401
assert(commit_list==0 || tc_heuristic_recover==0);
403
/* if either is set, total_ha_2pc must be set too */
404
if (total_ha_2pc <= 1)
408
#ifndef WILL_BE_DELETED_LATER
411
for now, only InnoDB supports 2pc. It means we can always safely
412
rollback all pending transactions, without risking inconsistent data
415
assert(total_ha_2pc == 2); // only InnoDB and binlog
416
tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
419
for (trans_len= MAX_XID_LIST_SIZE ;
420
trans_list==0 && trans_len > MIN_XID_LIST_SIZE; trans_len/=2)
422
trans_list=(XID *)malloc(trans_len*sizeof(XID));
426
errmsg_printf(ERRMSG_LVL_ERROR, ER(ER_OUTOFMEMORY), trans_len*sizeof(XID));
431
errmsg_printf(ERRMSG_LVL_INFO, _("Starting crash recovery..."));
434
XARecover recover_func(trans_list, trans_len, commit_list, dry_run);
435
for_each(all_engines.begin(), all_engines.end(), recover_func);
438
if (recover_func.getForeignXIDs())
439
errmsg_printf(ERRMSG_LVL_WARN,
440
_("Found %d prepared XA transactions"),
441
recover_func.getForeignXIDs());
442
if (dry_run && recover_func.getMyXIDs())
444
errmsg_printf(ERRMSG_LVL_ERROR,
445
_("Found %d prepared transactions! It means that drizzled "
446
"was not shut down properly last time and critical "
447
"recovery information (last binlog or %s file) was "
448
"manually deleted after a crash. You have to start "
449
"drizzled with the --tc-heuristic-recover switch to "
450
"commit or rollback pending transactions."),
451
recover_func.getMyXIDs(), opt_tc_log_file);
455
errmsg_printf(ERRMSG_LVL_INFO, _("Crash recovery finished."));
459
int plugin::StorageEngine::startConsistentSnapshot(Session *session)
461
for_each(all_engines.begin(), all_engines.end(),
462
bind2nd(mem_fun(&plugin::StorageEngine::start_consistent_snapshot),
467
class StorageEngineGetTableProto: public unary_function<plugin::StorageEngine *,bool>
470
message::Table *table_proto;
473
StorageEngineGetTableProto(const char* path_arg,
474
message::Table *table_proto_arg,
476
:path(path_arg), table_proto(table_proto_arg), err(err_arg) {}
478
result_type operator() (argument_type engine)
480
int ret= engine->getTableProtoImplementation(path, table_proto);
485
return *err == EEXIST;
489
static int drizzle_read_table_proto(const char* path, message::Table* table)
491
int fd= open(path, O_RDONLY);
496
google::protobuf::io::ZeroCopyInputStream* input=
497
new google::protobuf::io::FileInputStream(fd);
499
if (table->ParseFromZeroCopyStream(input) == false)
512
Call this function in order to give the handler the possiblity
513
to ask engine if there are any new tables that should be written to disk
514
or any dropped tables that need to be removed from disk
516
int plugin::StorageEngine::getTableProto(const char* path,
517
message::Table *table_proto)
521
::drizzled::Registry<plugin::StorageEngine *>::iterator iter=
522
find_if(all_engines.begin(), all_engines.end(),
523
StorageEngineGetTableProto(path, table_proto, &err));
524
if (iter == all_engines.end())
526
string proto_path(path);
527
string file_ext(".dfe");
528
proto_path.append(file_ext);
530
int error= access(proto_path.c_str(), F_OK);
539
int read_proto_err= drizzle_read_table_proto(proto_path.c_str(),
551
An interceptor to hijack the text of the error message without
552
setting an error in the thread. We need the text to present it
553
in the form of a warning to the user.
556
class Ha_delete_table_error_handler: public Internal_error_handler
559
Ha_delete_table_error_handler() : Internal_error_handler() {}
560
virtual bool handle_error(uint32_t sql_errno,
562
DRIZZLE_ERROR::enum_warning_level level,
564
char buff[DRIZZLE_ERRMSG_SIZE];
569
Ha_delete_table_error_handler::
570
handle_error(uint32_t ,
572
DRIZZLE_ERROR::enum_warning_level ,
575
/* Grab the error message */
576
strncpy(buff, message, sizeof(buff)-1);
581
class DeleteTableStorageEngine
582
: public unary_function<plugin::StorageEngine *, void>
589
DeleteTableStorageEngine(Session *session_arg, const char *path_arg,
590
handler **file_arg, int *error_arg)
591
: session(session_arg), path(path_arg), file(file_arg), dt_error(error_arg) {}
593
result_type operator() (argument_type engine)
595
char tmp_path[FN_REFLEN];
598
if(*dt_error!=ENOENT) /* already deleted table */
604
if (!engine->is_enabled())
607
if ((tmp_file= engine->create(NULL, session->mem_root)))
612
path= engine->checkLowercaseNames(path, tmp_path);
613
const string table_path(path);
614
int tmp_error= engine->deleteTable(session, table_path);
616
if (tmp_error != ENOENT)
620
if (engine->check_flag(HTON_BIT_HAS_DATA_DICTIONARY))
621
delete_table_proto_file(path);
623
tmp_error= delete_table_proto_file(path);
626
*dt_error= tmp_error;
641
This should return ENOENT if the file doesn't exists.
642
The .frm file will be deleted only if we return 0 or ENOENT
644
int plugin::StorageEngine::deleteTable(Session *session, const char *path,
645
const char *db, const char *alias,
646
bool generate_warning)
648
TableShare dummy_share;
650
memset(&dummy_table, 0, sizeof(dummy_table));
651
memset(&dummy_share, 0, sizeof(dummy_share));
653
dummy_table.s= &dummy_share;
658
for_each(all_engines.begin(), all_engines.end(),
659
DeleteTableStorageEngine(session, path, &file, &error));
661
if (error == ENOENT) /* proto may be left behind */
662
error= delete_table_proto_file(path);
664
if (error && generate_warning)
667
Because file->print_error() use my_error() to generate the error message
668
we use an internal error handler to intercept it and store the text
669
in a temporary buffer. Later the message will be presented to user
672
Ha_delete_table_error_handler ha_delete_table_error_handler;
674
/* Fill up strucutures that print_error may need */
675
dummy_share.path.str= (char*) path;
676
dummy_share.path.length= strlen(path);
677
dummy_share.db.str= (char*) db;
678
dummy_share.db.length= strlen(db);
679
dummy_share.table_name.str= (char*) alias;
680
dummy_share.table_name.length= strlen(alias);
681
dummy_table.alias= alias;
685
file->change_table_ptr(&dummy_table, &dummy_share);
687
session->push_internal_handler(&ha_delete_table_error_handler);
688
file->print_error(error, 0);
690
session->pop_internal_handler();
693
error= -1; /* General form of fail. maybe bad FRM */
696
XXX: should we convert *all* errors to warnings here?
697
What if the error is fatal?
699
push_warning(session, DRIZZLE_ERROR::WARN_LEVEL_ERROR, error,
700
ha_delete_table_error_handler.buff);
709
class DFETableNameIterator: public plugin::TableNameIteratorImplementation
713
uint32_t current_entry;
716
DFETableNameIterator(const string &database)
717
: plugin::TableNameIteratorImplementation(database),
722
~DFETableNameIterator();
724
int next(string *name);
728
DFETableNameIterator::~DFETableNameIterator()
734
int DFETableNameIterator::next(string *name)
736
char uname[NAME_LEN + 1];
739
uint32_t file_name_len;
740
const char *wild= NULL;
745
char path[FN_REFLEN];
747
build_table_filename(path, sizeof(path), db.c_str(), "", false);
749
dirp = my_dir(path,MYF(dir ? MY_WANT_STAT : 0));
753
if (my_errno == ENOENT)
754
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db.c_str());
756
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
766
if (current_entry == dirp->number_off_files)
773
file= dirp->dir_entry + current_entry;
775
if (my_strcasecmp(system_charset_info, ext=fn_rext(file->name),".dfe") ||
776
is_prefix(file->name, TMP_FILE_PREFIX))
780
file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
782
uname[file_name_len]= '\0';
784
if (wild && wild_compare(uname, wild, 0))
795
plugin::TableNameIterator::TableNameIterator(const string &db)
796
: current_implementation(NULL), database(db)
798
engine_iter= all_engines.begin();
799
default_implementation= new DFETableNameIterator(database);
802
plugin::TableNameIterator::~TableNameIterator()
804
delete current_implementation;
807
int plugin::TableNameIterator::next(string *name)
812
if (current_implementation == NULL)
814
while(current_implementation == NULL &&
815
(engine_iter != all_engines.end()))
817
plugin::StorageEngine *engine= *engine_iter;
818
current_implementation= engine->tableNameIterator(database);
822
if (current_implementation == NULL &&
823
(engine_iter == all_engines.end()))
825
current_implementation= default_implementation;
829
err= current_implementation->next(name);
833
if (current_implementation != default_implementation)
835
delete current_implementation;
836
current_implementation= NULL;
845
} /* namespace drizzled */
849
handler *get_new_handler(TableShare *share, MEM_ROOT *alloc,
850
drizzled::plugin::StorageEngine *engine)
854
if (engine && engine->is_enabled())
856
if ((file= engine->create(share, alloc)))
861
Try the default table type
862
Here the call to current_session() is ok as we call this function a lot of
863
times but we enter this branch very seldom.
865
return(get_new_handler(share, alloc, ha_default_storage_engine(current_session)));
870
Return the default storage engine plugin::StorageEngine for thread
872
@param ha_default_storage_engine(session)
873
@param session current thread
876
pointer to plugin::StorageEngine
878
drizzled::plugin::StorageEngine *ha_default_storage_engine(Session *session)
880
if (session->variables.storage_engine)
881
return session->variables.storage_engine;
882
return global_system_variables.storage_engine;
183
886
Initiates table-file and calls appropriate database-creator.