1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
4
* Copyright (C) 2008 Sun Microsystems
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; version 2 of the License.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
#include <drizzled/server_includes.h>
21
#include <drizzled/definitions.h>
22
#include <drizzled/base.h>
23
#include <drizzled/handler.h>
24
#include <drizzled/plugin/storage_engine.h>
25
#include <drizzled/session.h>
26
#include <drizzled/error.h>
27
#include <drizzled/gettext.h>
28
#include <drizzled/registry.h>
29
#include <drizzled/unireg.h>
30
#include <drizzled/data_home.h>
31
#include <drizzled/plugin/registry.h>
34
#include <drizzled/table_proto.h>
36
#include <google/protobuf/io/zero_copy_stream.h>
37
#include <google/protobuf/io/zero_copy_stream_impl.h>
39
#include <mysys/my_dir.h>
45
drizzled::Registry<StorageEngine *> all_engines;
47
void add_storage_engine(StorageEngine *engine)
49
all_engines.add(engine);
52
void remove_storage_engine(StorageEngine *engine)
54
all_engines.remove(engine);
57
StorageEngine::StorageEngine(const std::string name_arg,
58
const std::bitset<HTON_BIT_SIZE> &flags_arg,
59
size_t savepoint_offset_arg,
61
: name(name_arg), two_phase_commit(support_2pc), enabled(true),
63
savepoint_offset(savepoint_alloc_size),
64
orig_savepoint_offset(savepoint_offset_arg),
69
savepoint_alloc_size+= orig_savepoint_offset;
77
StorageEngine::~StorageEngine()
79
savepoint_alloc_size-= orig_savepoint_offset;
82
void StorageEngine::setTransactionReadWrite(Session* session)
84
Ha_trx_info *ha_info= &session->ha_data[getSlot()].ha_info[0];
86
When a storage engine method is called, the transaction must
87
have been started, unless it's a DDL call, for which the
88
storage engine starts the transaction internally, and commits
89
it internally, without registering in the ha_list.
90
Unfortunately here we can't know know for sure if the engine
91
has registered the transaction or not, so we must check.
93
if (ha_info->is_started())
96
table_share can be NULL in ha_delete_table(). See implementation
97
of standalone function ha_delete_table() in sql_base.cc.
99
ha_info->set_trx_read_write();
105
Return the default storage engine StorageEngine for thread
107
@param ha_default_storage_engine(session)
108
@param session current thread
111
pointer to StorageEngine
113
StorageEngine *ha_default_storage_engine(Session *session)
115
if (session->variables.storage_engine)
116
return session->variables.storage_engine;
117
return global_system_variables.storage_engine;
122
Return the storage engine StorageEngine for the supplied name
124
@param session current thread
125
@param name name of storage engine
128
pointer to storage engine plugin handle
130
StorageEngine *ha_resolve_by_name(Session *session, std::string find_str)
132
transform(find_str.begin(), find_str.end(),
133
find_str.begin(), ::tolower);
134
string default_str("default");
135
if (find_str == default_str)
136
return ha_default_storage_engine(session);
138
StorageEngine *engine= all_engines.find(find_str);
140
if (engine && engine->is_user_selectable())
147
handler *get_new_handler(TableShare *share, MEM_ROOT *alloc,
148
StorageEngine *engine)
152
if (engine && engine->is_enabled())
154
if ((file= engine->create(share, alloc)))
159
Try the default table type
160
Here the call to current_session() is ok as we call this function a lot of
161
times but we enter this branch very seldom.
163
return(get_new_handler(share, alloc, ha_default_storage_engine(current_session)));
166
class StorageEngineCloseConnection
167
: public unary_function<StorageEngine *, void>
171
StorageEngineCloseConnection(Session *session_arg) : session(session_arg) {}
173
there's no need to rollback here as all transactions must
174
be rolled back already
176
inline result_type operator() (argument_type engine)
178
if (engine->is_enabled() &&
179
session_get_ha_data(session, engine))
180
engine->close_connection(session);
186
don't bother to rollback here, it's done already
188
void ha_close_connection(Session* session)
190
for_each(all_engines.begin(), all_engines.end(),
191
StorageEngineCloseConnection(session));
194
void ha_drop_database(char* path)
196
for_each(all_engines.begin(), all_engines.end(),
197
bind2nd(mem_fun(&StorageEngine::drop_database),path));
200
int ha_commit_or_rollback_by_xid(XID *xid, bool commit)
205
transform(all_engines.begin(), all_engines.end(), results.begin(),
206
bind2nd(mem_fun(&StorageEngine::commit_by_xid),xid));
208
transform(all_engines.begin(), all_engines.end(), results.begin(),
209
bind2nd(mem_fun(&StorageEngine::rollback_by_xid),xid));
211
if (find_if(results.begin(), results.end(), bind2nd(equal_to<int>(),0))
220
This function should be called when MySQL sends rows of a SELECT result set
221
or the EOF mark to the client. It releases a possible adaptive hash index
222
S-latch held by session in InnoDB and also releases a possible InnoDB query
223
FIFO ticket to enter InnoDB. To save CPU time, InnoDB allows a session to
224
keep them over several calls of the InnoDB handler interface when a join
225
is executed. But when we let the control to pass to the client they have
226
to be released because if the application program uses mysql_use_result(),
227
it may deadlock on the S-latch if the application on another connection
228
performs another SQL query. In MySQL-4.1 this is even more important because
229
there a connection can have several SELECT queries open at the same time.
231
@param session the thread handle of the current connection
236
int ha_release_temporary_latches(Session *session)
238
for_each(all_engines.begin(), all_engines.end(),
239
bind2nd(mem_fun(&StorageEngine::release_temporary_latches),session));
244
bool ha_flush_logs(StorageEngine *engine)
248
if (find_if(all_engines.begin(), all_engines.end(),
249
mem_fun(&StorageEngine::flush_logs))
250
!= all_engines.begin())
255
if ((!engine->is_enabled()) ||
256
(engine->flush_logs()))
263
recover() step of xa.
266
there are three modes of operation:
267
- automatic recover after a crash
268
in this case commit_list != 0, tc_heuristic_recover==0
269
all xids from commit_list are committed, others are rolled back
270
- manual (heuristic) recover
271
in this case commit_list==0, tc_heuristic_recover != 0
272
DBA has explicitly specified that all prepared transactions should
273
be committed (or rolled back).
274
- no recovery (MySQL did not detect a crash)
275
in this case commit_list==0, tc_heuristic_recover == 0
276
there should be no prepared transactions in this case.
278
class XARecover : unary_function<StorageEngine *, void>
280
int trans_len, found_foreign_xids, found_my_xids;
286
XARecover(XID *trans_list_arg, int trans_len_arg,
287
HASH *commit_list_arg, bool dry_run_arg)
288
: trans_len(trans_len_arg), found_foreign_xids(0), found_my_xids(0),
290
trans_list(trans_list_arg), commit_list(commit_list_arg),
296
return found_foreign_xids;
301
return found_my_xids;
304
result_type operator() (argument_type engine)
309
if (engine->is_enabled())
311
while ((got= engine->recover(trans_list, trans_len)) > 0 )
313
errmsg_printf(ERRMSG_LVL_INFO,
314
_("Found %d prepared transaction(s) in %s"),
315
got, engine->getName().c_str());
316
for (int i=0; i < got; i ++)
318
my_xid x=trans_list[i].get_my_xid();
319
if (!x) // not "mine" - that is generated by external TM
321
xid_cache_insert(trans_list+i, XA_PREPARED);
322
found_foreign_xids++;
332
hash_search(commit_list, (unsigned char *)&x, sizeof(x)) != 0 :
333
tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
335
engine->commit_by_xid(trans_list+i);
339
engine->rollback_by_xid(trans_list+i);
350
int ha_recover(HASH *commit_list)
352
XID *trans_list= NULL;
355
bool dry_run= (commit_list==0 && tc_heuristic_recover==0);
357
/* commit_list and tc_heuristic_recover cannot be set both */
358
assert(commit_list==0 || tc_heuristic_recover==0);
360
/* if either is set, total_ha_2pc must be set too */
361
if (total_ha_2pc <= 1)
365
#ifndef WILL_BE_DELETED_LATER
368
for now, only InnoDB supports 2pc. It means we can always safely
369
rollback all pending transactions, without risking inconsistent data
372
assert(total_ha_2pc == 2); // only InnoDB and binlog
373
tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
376
for (trans_len= MAX_XID_LIST_SIZE ;
377
trans_list==0 && trans_len > MIN_XID_LIST_SIZE; trans_len/=2)
379
trans_list=(XID *)malloc(trans_len*sizeof(XID));
383
errmsg_printf(ERRMSG_LVL_ERROR, ER(ER_OUTOFMEMORY), trans_len*sizeof(XID));
388
errmsg_printf(ERRMSG_LVL_INFO, _("Starting crash recovery..."));
391
XARecover recover_func(trans_list, trans_len, commit_list, dry_run);
392
for_each(all_engines.begin(), all_engines.end(), recover_func);
395
if (recover_func.getForeignXIDs())
396
errmsg_printf(ERRMSG_LVL_WARN,
397
_("Found %d prepared XA transactions"),
398
recover_func.getForeignXIDs());
399
if (dry_run && recover_func.getMyXIDs())
401
errmsg_printf(ERRMSG_LVL_ERROR,
402
_("Found %d prepared transactions! It means that drizzled "
403
"was not shut down properly last time and critical "
404
"recovery information (last binlog or %s file) was "
405
"manually deleted after a crash. You have to start "
406
"drizzled with the --tc-heuristic-recover switch to "
407
"commit or rollback pending transactions."),
408
recover_func.getMyXIDs(), opt_tc_log_file);
412
errmsg_printf(ERRMSG_LVL_INFO, _("Crash recovery finished."));
416
int ha_start_consistent_snapshot(Session *session)
418
for_each(all_engines.begin(), all_engines.end(),
419
bind2nd(mem_fun(&StorageEngine::start_consistent_snapshot),session));
423
static int drizzle_read_table_proto(const char* path, drizzled::message::Table* table)
425
int fd= open(path, O_RDONLY);
430
google::protobuf::io::ZeroCopyInputStream* input=
431
new google::protobuf::io::FileInputStream(fd);
433
if (table->ParseFromZeroCopyStream(input) == false)
445
class StorageEngineGetTableProto: public unary_function<StorageEngine *,bool>
448
drizzled::message::Table *table_proto;
451
StorageEngineGetTableProto(const char* path_arg,
452
drizzled::message::Table *table_proto_arg,
454
:path(path_arg), table_proto(table_proto_arg), err(err_arg) {}
456
result_type operator() (argument_type engine)
458
int ret= engine->getTableProtoImplementation(path, table_proto);
463
return *err == EEXIST;
468
Call this function in order to give the handler the possiblity
469
to ask engine if there are any new tables that should be written to disk
470
or any dropped tables that need to be removed from disk
472
int StorageEngine::getTableProto(const char* path,
473
drizzled::message::Table *table_proto)
477
drizzled::Registry<StorageEngine *>::iterator iter=
478
find_if(all_engines.begin(), all_engines.end(),
479
StorageEngineGetTableProto(path, table_proto, &err));
480
if (iter == all_engines.end())
482
string proto_path(path);
483
string file_ext(".dfe");
484
proto_path.append(file_ext);
486
int error= access(proto_path.c_str(), F_OK);
495
int read_proto_err= drizzle_read_table_proto(proto_path.c_str(),
507
int StorageEngine::renameTableImplementation(Session *, const char *from, const char *to)
510
for (const char **ext= bas_ext(); *ext ; ext++)
512
if (rename_file_ext(from, to, *ext))
514
if ((error=my_errno) != ENOENT)
524
Delete all files with extension from bas_ext().
526
@param name Base name of table
529
We assume that the handler may return more extensions than
530
was actually used for the file.
533
0 If we successfully deleted at least one file from base_ext and
534
didn't get any other errors than ENOENT
538
int StorageEngine::deleteTableImplementation(Session *, const std::string table_path)
541
int enoent_or_zero= ENOENT; // Error if no file was deleted
542
char buff[FN_REFLEN];
544
for (const char **ext=bas_ext(); *ext ; ext++)
546
fn_format(buff, table_path.c_str(), "", *ext,
547
MY_UNPACK_FILENAME|MY_APPEND_EXT);
548
if (my_delete_with_symlink(buff, MYF(0)))
550
if ((error= my_errno) != ENOENT)
554
enoent_or_zero= 0; // No error for ENOENT
555
error= enoent_or_zero;
561
An interceptor to hijack the text of the error message without
562
setting an error in the thread. We need the text to present it
563
in the form of a warning to the user.
566
class Ha_delete_table_error_handler: public Internal_error_handler
569
Ha_delete_table_error_handler() : Internal_error_handler() {}
570
virtual bool handle_error(uint32_t sql_errno,
572
DRIZZLE_ERROR::enum_warning_level level,
574
char buff[DRIZZLE_ERRMSG_SIZE];
579
Ha_delete_table_error_handler::
580
handle_error(uint32_t ,
582
DRIZZLE_ERROR::enum_warning_level ,
585
/* Grab the error message */
586
strncpy(buff, message, sizeof(buff)-1);
591
class DeleteTableStorageEngine
592
: public unary_function<StorageEngine *, void>
599
DeleteTableStorageEngine(Session *session_arg, const char *path_arg,
600
handler **file_arg, int *error_arg)
601
: session(session_arg), path(path_arg), file(file_arg), dt_error(error_arg) {}
603
result_type operator() (argument_type engine)
605
char tmp_path[FN_REFLEN];
608
if(*dt_error!=ENOENT) /* already deleted table */
614
if (!engine->is_enabled())
617
if ((tmp_file= engine->create(NULL, session->mem_root)))
622
path= engine->checkLowercaseNames(path, tmp_path);
623
const std::string table_path(path);
624
int tmp_error= engine->deleteTable(session, table_path);
626
if (tmp_error != ENOENT)
630
if (engine->check_flag(HTON_BIT_HAS_DATA_DICTIONARY))
631
delete_table_proto_file(path);
633
tmp_error= delete_table_proto_file(path);
636
*dt_error= tmp_error;
650
This should return ENOENT if the file doesn't exists.
651
The .frm file will be deleted only if we return 0 or ENOENT
653
int ha_delete_table(Session *session, const char *path,
654
const char *db, const char *alias, bool generate_warning)
656
TableShare dummy_share;
658
memset(&dummy_table, 0, sizeof(dummy_table));
659
memset(&dummy_share, 0, sizeof(dummy_share));
661
dummy_table.s= &dummy_share;
666
for_each(all_engines.begin(), all_engines.end(),
667
DeleteTableStorageEngine(session, path, &file, &error));
669
if (error == ENOENT) /* proto may be left behind */
670
error= delete_table_proto_file(path);
672
if (error && generate_warning)
675
Because file->print_error() use my_error() to generate the error message
676
we use an internal error handler to intercept it and store the text
677
in a temporary buffer. Later the message will be presented to user
680
Ha_delete_table_error_handler ha_delete_table_error_handler;
682
/* Fill up strucutures that print_error may need */
683
dummy_share.path.str= (char*) path;
684
dummy_share.path.length= strlen(path);
685
dummy_share.db.str= (char*) db;
686
dummy_share.db.length= strlen(db);
687
dummy_share.table_name.str= (char*) alias;
688
dummy_share.table_name.length= strlen(alias);
689
dummy_table.alias= alias;
694
file->change_table_ptr(&dummy_table, &dummy_share);
696
session->push_internal_handler(&ha_delete_table_error_handler);
697
file->print_error(error, 0);
699
session->pop_internal_handler();
702
error= -1; /* General form of fail. maybe bad FRM */
705
XXX: should we convert *all* errors to warnings here?
706
What if the error is fatal?
708
push_warning(session, DRIZZLE_ERROR::WARN_LEVEL_ERROR, error,
709
ha_delete_table_error_handler.buff);
719
Initiates table-file and calls appropriate database-creator.
726
int ha_create_table(Session *session, const char *path,
727
const char *db, const char *table_name,
728
HA_CREATE_INFO *create_info,
729
bool update_create_info,
730
drizzled::message::Table *table_proto)
734
TableShare share(db, 0, table_name, path);
738
if (parse_table_proto(session, *table_proto, &share))
743
if (open_table_def(session, &share))
747
if (open_table_from_share(session, &share, "", 0, (uint32_t) READ_ALL, 0,
751
if (update_create_info)
752
table.updateCreateInfo(create_info);
754
error= share.storage_engine->createTable(session, path, &table,
755
create_info, table_proto);
756
table.closefrm(false);
759
char name_buff[FN_REFLEN];
760
sprintf(name_buff,"%s.%s",db,table_name);
761
my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name_buff, error);
764
share.free_table_share();
769
const string ha_resolve_storage_engine_name(const StorageEngine *engine)
771
return engine == NULL ? string("UNKNOWN") : engine->getName();
774
const char *StorageEngine::checkLowercaseNames(const char *path, char *tmp_path)
776
if (flags.test(HTON_BIT_FILE_BASED))
779
/* Ensure that table handler get path in lower case */
780
if (tmp_path != path)
781
strcpy(tmp_path, path);
784
we only should turn into lowercase database/table part
785
so start the process after homedirectory
787
if (strstr(tmp_path, drizzle_tmpdir) == tmp_path)
788
my_casedn_str(files_charset_info, tmp_path + strlen(drizzle_tmpdir));
790
my_casedn_str(files_charset_info, tmp_path + drizzle_data_home_len);
795
class DFETableNameIterator: public TableNameIteratorImplementation
799
uint32_t current_entry;
802
DFETableNameIterator(const std::string &database)
803
: TableNameIteratorImplementation(database),
808
~DFETableNameIterator();
810
int next(std::string *name);
814
DFETableNameIterator::~DFETableNameIterator()
820
int DFETableNameIterator::next(string *name)
822
char uname[NAME_LEN + 1];
825
uint32_t file_name_len;
826
const char *wild= NULL;
831
char path[FN_REFLEN];
833
build_table_filename(path, sizeof(path), db.c_str(), "", false);
835
dirp = my_dir(path,MYF(dir ? MY_WANT_STAT : 0));
839
if (my_errno == ENOENT)
840
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db.c_str());
842
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
852
if (current_entry == dirp->number_off_files)
859
file= dirp->dir_entry + current_entry;
861
if (my_strcasecmp(system_charset_info, ext=fn_rext(file->name),".dfe") ||
862
is_prefix(file->name, TMP_FILE_PREFIX))
866
file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
868
uname[file_name_len]= '\0';
870
if (wild && wild_compare(uname, wild, 0))
880
TableNameIterator::TableNameIterator(const std::string &db)
881
: current_implementation(NULL), database(db)
883
engine_iter= all_engines.begin();
884
default_implementation= new DFETableNameIterator(database);
887
TableNameIterator::~TableNameIterator()
889
delete current_implementation;
892
int TableNameIterator::next(std::string *name)
897
if (current_implementation == NULL)
899
while(current_implementation == NULL && engine_iter != all_engines.end())
901
StorageEngine *engine= *engine_iter;
902
current_implementation= engine->tableNameIterator(database);
906
if (current_implementation == NULL && engine_iter == all_engines.end())
908
current_implementation= default_implementation;
912
err= current_implementation->next(name);
916
if (current_implementation != default_implementation)
918
delete current_implementation;
919
current_implementation= NULL;