17
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
#include <drizzled/server_includes.h>
29
25
#include <algorithm>
30
26
#include <functional>
32
28
#include <google/protobuf/io/zero_copy_stream.h>
33
29
#include <google/protobuf/io/zero_copy_stream_impl.h>
35
#include <drizzled/cached_directory.h>
31
#include "mysys/my_dir.h"
32
#include "mysys/hash.h"
37
34
#include <drizzled/definitions.h>
38
35
#include <drizzled/base.h>
39
#include <drizzled/cursor.h>
36
#include <drizzled/handler.h>
40
37
#include <drizzled/plugin/storage_engine.h>
41
38
#include <drizzled/session.h>
42
39
#include <drizzled/error.h>
43
40
#include <drizzled/gettext.h>
41
#include <drizzled/name_map.h>
44
42
#include <drizzled/unireg.h>
45
43
#include <drizzled/data_home.h>
46
#include <drizzled/errmsg_print.h>
47
#include <drizzled/xid.h>
48
#include <drizzled/sql_table.h>
49
#include <drizzled/global_charset_info.h>
50
#include <drizzled/charset.h>
51
#include <drizzled/internal/my_sys.h>
44
#include "drizzled/errmsg_print.h"
45
#include "drizzled/name_map.h"
46
#include "drizzled/xid.h"
53
48
#include <drizzled/table_proto.h>
54
#include <drizzled/plugin/event_observer.h>
55
#include <drizzled/internal_error_handler.h>
57
#include <drizzled/table/shell.h>
59
#include <drizzled/message/cache.h>
61
#include <boost/algorithm/string/compare.hpp>
63
static bool shutdown_has_begun= false; // Once we put in the container for the vector/etc for engines this will go away.
71
static EngineVector vector_of_engines;
72
static EngineVector vector_of_schema_engines;
74
const std::string DEFAULT_STRING("default");
75
const std::string UNKNOWN_STRING("UNKNOWN");
76
const std::string DEFAULT_DEFINITION_FILE_EXT(".dfe");
78
static std::set<std::string> set_of_table_definition_ext;
80
EngineVector &StorageEngine::getSchemaEngines()
82
return vector_of_schema_engines;
85
StorageEngine::StorageEngine(const std::string name_arg,
86
const std::bitset<HTON_BIT_SIZE> &flags_arg) :
87
Plugin(name_arg, "StorageEngine"),
88
MonitoredInTransaction(), /* This gives the storage engine a "slot" or ID */
93
StorageEngine::~StorageEngine()
97
void StorageEngine::setTransactionReadWrite(Session& session)
99
TransactionContext &statement_ctx= session.transaction.stmt;
100
statement_ctx.markModifiedNonTransData();
104
int StorageEngine::renameTable(Session &session, const identifier::Table &from, const identifier::Table &to)
107
setTransactionReadWrite(session);
109
if (unlikely(plugin::EventObserver::beforeRenameTable(session, from, to)))
111
error= ER_EVENT_OBSERVER_PLUGIN;
115
error = doRenameTable(session, from, to);
116
if (unlikely(plugin::EventObserver::afterRenameTable(session, from, to, error)))
55
NameMap<plugin::StorageEngine *> all_engines;
57
plugin::StorageEngine::StorageEngine(const string name_arg,
58
const bitset<HTON_BIT_SIZE> &flags_arg,
59
size_t savepoint_offset_arg,
62
two_phase_commit(support_2pc),
65
savepoint_offset(savepoint_alloc_size),
66
orig_savepoint_offset(savepoint_offset_arg),
71
savepoint_alloc_size+= orig_savepoint_offset;
79
plugin::StorageEngine::~StorageEngine()
81
savepoint_alloc_size-= orig_savepoint_offset;
84
void plugin::StorageEngine::setTransactionReadWrite(Session* session)
86
Ha_trx_info *ha_info= &session->ha_data[getSlot()].ha_info[0];
88
When a storage engine method is called, the transaction must
89
have been started, unless it's a DDL call, for which the
90
storage engine starts the transaction internally, and commits
91
it internally, without registering in the ha_list.
92
Unfortunately here we can't know know for sure if the engine
93
has registered the transaction or not, so we must check.
95
if (ha_info->is_started())
98
* table_share can be NULL in plugin::StorageEngine::deleteTable().
100
ha_info->set_trx_read_write();
106
int plugin::StorageEngine::renameTableImplementation(Session *,
111
for (const char **ext= bas_ext(); *ext ; ext++)
113
if (rename_file_ext(from, to, *ext))
118
error= ER_EVENT_OBSERVER_PLUGIN;
115
if ((error=my_errno) != ENOENT)
126
125
Delete all files with extension from bas_ext().
128
127
@param name Base name of table
131
We assume that the Cursor may return more extensions than
130
We assume that the handler may return more extensions than
132
131
was actually used for the file.
140
int StorageEngine::doDropTable(Session&, const identifier::Table &identifier)
139
int plugin::StorageEngine::deleteTableImplementation(Session *,
140
const string table_path)
144
143
int enoent_or_zero= ENOENT; // Error if no file was deleted
145
144
char buff[FN_REFLEN];
147
for (const char **ext= bas_ext(); *ext ; ext++)
146
for (const char **ext=bas_ext(); *ext ; ext++)
149
internal::fn_format(buff, identifier.getPath().c_str(), "", *ext,
150
MY_UNPACK_FILENAME|MY_APPEND_EXT);
151
if (internal::my_delete_with_symlink(buff, MYF(0)))
148
fn_format(buff, table_path.c_str(), "", *ext,
149
MY_UNPACK_FILENAME|MY_APPEND_EXT);
150
if (my_delete_with_symlink(buff, MYF(0)))
153
if ((error= errno) != ENOENT)
152
if ((error= my_errno) != ENOENT)
158
156
enoent_or_zero= 0; // No error for ENOENT
161
157
error= enoent_or_zero;
166
bool StorageEngine::addPlugin(StorageEngine *engine)
169
vector_of_engines.push_back(engine);
171
if (engine->getTableDefinitionFileExtension().length())
162
const char *plugin::StorageEngine::checkLowercaseNames(const char *path,
165
if (flags.test(HTON_BIT_FILE_BASED))
168
/* Ensure that table handler get path in lower case */
169
if (tmp_path != path)
170
strcpy(tmp_path, path);
173
we only should turn into lowercase database/table part
174
so start the process after homedirectory
176
if (strstr(tmp_path, drizzle_tmpdir) == tmp_path)
177
my_casedn_str(files_charset_info, tmp_path + strlen(drizzle_tmpdir));
179
my_casedn_str(files_charset_info, tmp_path + drizzle_data_home_len);
185
bool plugin::StorageEngine::addPlugin(plugin::StorageEngine *engine)
187
if (all_engines.add(engine))
173
assert(engine->getTableDefinitionFileExtension().length() == DEFAULT_DEFINITION_FILE_EXT.length());
174
set_of_table_definition_ext.insert(engine->getTableDefinitionFileExtension());
189
errmsg_printf(ERRMSG_LVL_ERROR,
190
_("Couldn't add StorageEngine"));
177
if (engine->check_flag(HTON_BIT_SCHEMA_DICTIONARY))
178
vector_of_schema_engines.push_back(engine);
183
void StorageEngine::removePlugin(StorageEngine *)
185
if (shutdown_has_begun == false)
187
vector_of_engines.clear();
188
vector_of_schema_engines.clear();
190
shutdown_has_begun= true;
194
class FindEngineByName
195
: public std::unary_function<StorageEngine *, bool>
197
const std::string &predicate;
200
explicit FindEngineByName(const std::string &target_arg) :
201
predicate(target_arg)
205
result_type operator() (argument_type engine)
207
return boost::iequals(engine->getName(), predicate);
211
StorageEngine *StorageEngine::findByName(const std::string &predicate)
213
EngineVector::iterator iter= std::find_if(vector_of_engines.begin(),
214
vector_of_engines.end(),
215
FindEngineByName(predicate));
216
if (iter != vector_of_engines.end())
218
StorageEngine *engine= *iter;
219
if (engine->is_user_selectable())
226
StorageEngine *StorageEngine::findByName(Session& session, const std::string &predicate)
228
if (boost::iequals(predicate, DEFAULT_STRING))
229
return session.getDefaultStorageEngine();
231
EngineVector::iterator iter= std::find_if(vector_of_engines.begin(),
232
vector_of_engines.end(),
233
FindEngineByName(predicate));
234
if (iter != vector_of_engines.end())
236
StorageEngine *engine= *iter;
237
if (engine->is_user_selectable())
244
class StorageEngineCloseConnection : public std::unary_function<StorageEngine *, void>
196
void plugin::StorageEngine::removePlugin(plugin::StorageEngine *engine)
198
all_engines.remove(engine);
201
plugin::StorageEngine *plugin::StorageEngine::findByName(Session *session,
205
transform(find_str.begin(), find_str.end(),
206
find_str.begin(), ::tolower);
207
string default_str("default");
208
if (find_str == default_str)
209
return plugin::StorageEngine::defaultStorageEngine(session);
211
plugin::StorageEngine *engine= all_engines.find(find_str);
213
if (engine && engine->is_user_selectable())
219
class StorageEngineCloseConnection
220
: public unary_function<plugin::StorageEngine *, void>
246
222
Session *session;
262
239
don't bother to rollback here, it's done already
264
void StorageEngine::closeConnection(Session* session)
266
std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
267
StorageEngineCloseConnection(session));
270
bool StorageEngine::flushLogs(StorageEngine *engine)
241
void plugin::StorageEngine::closeConnection(Session* session)
243
for_each(all_engines.begin(), all_engines.end(),
244
StorageEngineCloseConnection(session));
247
void plugin::StorageEngine::dropDatabase(char* path)
249
for_each(all_engines.begin(), all_engines.end(),
250
bind2nd(mem_fun(&plugin::StorageEngine::drop_database),path));
253
int plugin::StorageEngine::commitOrRollbackByXID(XID *xid, bool commit)
258
transform(all_engines.begin(), all_engines.end(), results.begin(),
259
bind2nd(mem_fun(&plugin::StorageEngine::commit_by_xid),xid));
261
transform(all_engines.begin(), all_engines.end(), results.begin(),
262
bind2nd(mem_fun(&plugin::StorageEngine::rollback_by_xid),xid));
264
if (find_if(results.begin(), results.end(), bind2nd(equal_to<int>(),0))
272
This function should be called when MySQL sends rows of a SELECT result set
273
or the EOF mark to the client. It releases a possible adaptive hash index
274
S-latch held by session in InnoDB and also releases a possible InnoDB query
275
FIFO ticket to enter InnoDB. To save CPU time, InnoDB allows a session to
276
keep them over several calls of the InnoDB handler interface when a join
277
is executed. But when we let the control to pass to the client they have
278
to be released because if the application program uses mysql_use_result(),
279
it may deadlock on the S-latch if the application on another connection
280
performs another SQL query. In MySQL-4.1 this is even more important because
281
there a connection can have several SELECT queries open at the same time.
283
@param session the thread handle of the current connection
288
int plugin::StorageEngine::releaseTemporaryLatches(Session *session)
290
for_each(all_engines.begin(), all_engines.end(),
291
bind2nd(mem_fun(&plugin::StorageEngine::release_temporary_latches),session));
295
bool plugin::StorageEngine::flushLogs(plugin::StorageEngine *engine)
272
297
if (engine == NULL)
274
if (std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
275
std::mem_fun(&StorageEngine::flush_logs))
276
!= vector_of_engines.begin())
299
if (find_if(all_engines.begin(), all_engines.end(),
300
mem_fun(&plugin::StorageEngine::flush_logs))
301
!= all_engines.begin())
281
if (engine->flush_logs())
306
if ((!engine->is_enabled()) ||
307
(engine->flush_logs()))
287
class StorageEngineGetTableDefinition: public std::unary_function<StorageEngine *,bool>
290
const identifier::Table &identifier;
291
message::Table &table_message;
292
drizzled::error_t &err;
295
StorageEngineGetTableDefinition(Session& session_arg,
296
const identifier::Table &identifier_arg,
297
message::Table &table_message_arg,
298
drizzled::error_t &err_arg) :
299
session(session_arg),
300
identifier(identifier_arg),
301
table_message(table_message_arg),
304
result_type operator() (argument_type engine)
306
int ret= engine->doGetTableDefinition(session, identifier, table_message);
314
recover() step of xa.
317
there are three modes of operation:
318
- automatic recover after a crash
319
in this case commit_list != 0, tc_heuristic_recover==0
320
all xids from commit_list are committed, others are rolled back
321
- manual (heuristic) recover
322
in this case commit_list==0, tc_heuristic_recover != 0
323
DBA has explicitly specified that all prepared transactions should
324
be committed (or rolled back).
325
- no recovery (MySQL did not detect a crash)
326
in this case commit_list==0, tc_heuristic_recover == 0
327
there should be no prepared transactions in this case.
329
class XARecover : unary_function<plugin::StorageEngine *, void>
331
int trans_len, found_foreign_xids, found_my_xids;
337
XARecover(XID *trans_list_arg, int trans_len_arg,
338
HASH *commit_list_arg, bool dry_run_arg)
339
: trans_len(trans_len_arg), found_foreign_xids(0), found_my_xids(0),
341
trans_list(trans_list_arg), commit_list(commit_list_arg),
347
return found_foreign_xids;
352
return found_my_xids;
355
result_type operator() (argument_type engine)
360
if (engine->is_enabled())
362
while ((got= engine->recover(trans_list, trans_len)) > 0 )
364
errmsg_printf(ERRMSG_LVL_INFO,
365
_("Found %d prepared transaction(s) in %s"),
366
got, engine->getName().c_str());
367
for (int i=0; i < got; i ++)
369
my_xid x=trans_list[i].get_my_xid();
370
if (!x) // not "mine" - that is generated by external TM
372
xid_cache_insert(trans_list+i, XA_PREPARED);
373
found_foreign_xids++;
383
hash_search(commit_list, (unsigned char *)&x, sizeof(x)) != 0 :
384
tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
386
engine->commit_by_xid(trans_list+i);
390
engine->rollback_by_xid(trans_list+i);
400
int plugin::StorageEngine::recover(HASH *commit_list)
402
XID *trans_list= NULL;
405
bool dry_run= (commit_list==0 && tc_heuristic_recover==0);
407
/* commit_list and tc_heuristic_recover cannot be set both */
408
assert(commit_list==0 || tc_heuristic_recover==0);
410
/* if either is set, total_ha_2pc must be set too */
411
if (total_ha_2pc <= 1)
415
#ifndef WILL_BE_DELETED_LATER
418
for now, only InnoDB supports 2pc. It means we can always safely
419
rollback all pending transactions, without risking inconsistent data
422
assert(total_ha_2pc == 2); // only InnoDB and binlog
423
tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
426
for (trans_len= MAX_XID_LIST_SIZE ;
427
trans_list==0 && trans_len > MIN_XID_LIST_SIZE; trans_len/=2)
429
trans_list=(XID *)malloc(trans_len*sizeof(XID));
433
errmsg_printf(ERRMSG_LVL_ERROR, ER(ER_OUTOFMEMORY), trans_len*sizeof(XID));
438
errmsg_printf(ERRMSG_LVL_INFO, _("Starting crash recovery..."));
441
XARecover recover_func(trans_list, trans_len, commit_list, dry_run);
442
for_each(all_engines.begin(), all_engines.end(), recover_func);
445
if (recover_func.getForeignXIDs())
446
errmsg_printf(ERRMSG_LVL_WARN,
447
_("Found %d prepared XA transactions"),
448
recover_func.getForeignXIDs());
449
if (dry_run && recover_func.getMyXIDs())
451
errmsg_printf(ERRMSG_LVL_ERROR,
452
_("Found %d prepared transactions! It means that drizzled "
453
"was not shut down properly last time and critical "
454
"recovery information (last binlog or %s file) was "
455
"manually deleted after a crash. You have to start "
456
"drizzled with the --tc-heuristic-recover switch to "
457
"commit or rollback pending transactions."),
458
recover_func.getMyXIDs(), opt_tc_log_file);
462
errmsg_printf(ERRMSG_LVL_INFO, _("Crash recovery finished."));
466
int plugin::StorageEngine::startConsistentSnapshot(Session *session)
468
for_each(all_engines.begin(), all_engines.end(),
469
bind2nd(mem_fun(&plugin::StorageEngine::start_consistent_snapshot),
474
class StorageEngineGetTableProto: public unary_function<plugin::StorageEngine *,bool>
477
message::Table *table_proto;
480
StorageEngineGetTableProto(const char* path_arg,
481
message::Table *table_proto_arg,
483
:path(path_arg), table_proto(table_proto_arg), err(err_arg) {}
485
result_type operator() (argument_type engine)
487
int ret= engine->getTableProtoImplementation(path, table_proto);
308
489
if (ret != ENOENT)
309
err= static_cast<drizzled::error_t>(ret);
311
return err == static_cast<drizzled::error_t>(EEXIST) or err != static_cast<drizzled::error_t>(ENOENT);
492
return *err == EEXIST;
315
class StorageEngineDoesTableExist: public std::unary_function<StorageEngine *, bool>
496
static int drizzle_read_table_proto(const char* path, message::Table* table)
318
const identifier::Table &identifier;
321
StorageEngineDoesTableExist(Session& session_arg, const identifier::Table &identifier_arg) :
322
session(session_arg),
323
identifier(identifier_arg)
326
result_type operator() (argument_type engine)
498
int fd= open(path, O_RDONLY);
503
google::protobuf::io::ZeroCopyInputStream* input=
504
new google::protobuf::io::FileInputStream(fd);
506
if (table->ParseFromZeroCopyStream(input) == false)
328
return engine->doDoesTableExist(session, identifier);
333
Utility method which hides some of the details of getTableDefinition()
519
Call this function in order to give the handler the possiblity
520
to ask engine if there are any new tables that should be written to disk
521
or any dropped tables that need to be removed from disk
335
bool plugin::StorageEngine::doesTableExist(Session &session,
336
const identifier::Table &identifier,
337
bool include_temporary_tables)
339
if (include_temporary_tables)
341
if (session.doDoesTableExist(identifier))
345
EngineVector::iterator iter=
346
std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
347
StorageEngineDoesTableExist(session, identifier));
349
if (iter == vector_of_engines.end())
357
bool plugin::StorageEngine::doDoesTableExist(Session&, const drizzled::identifier::Table&)
359
std::cerr << " Engine was called for doDoesTableExist() and does not implement it: " << this->getName() << "\n";
364
message::table::shared_ptr StorageEngine::getTableMessage(Session& session,
365
identifier::Table::const_reference identifier,
366
bool include_temporary_tables)
368
drizzled::error_t error;
369
error= static_cast<drizzled::error_t>(ENOENT);
371
if (include_temporary_tables)
373
Table *table= session.find_temporary_table(identifier);
523
int plugin::StorageEngine::getTableProto(const char* path,
524
message::Table *table_proto)
528
NameMap<plugin::StorageEngine *>::iterator iter=
529
find_if(all_engines.begin(), all_engines.end(),
530
StorageEngineGetTableProto(path, table_proto, &err));
531
if (iter == all_engines.end())
533
string proto_path(path);
534
string file_ext(".dfe");
535
proto_path.append(file_ext);
537
int error= access(proto_path.c_str(), F_OK);
376
return message::table::shared_ptr(new message::Table(*table->getShare()->getTableMessage()));
546
int read_proto_err= drizzle_read_table_proto(proto_path.c_str(),
380
drizzled::message::table::shared_ptr table_ptr;
381
if ((table_ptr= drizzled::message::Cache::singleton().find(identifier)))
386
message::Table message;
387
EngineVector::iterator iter=
388
std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
389
StorageEngineGetTableDefinition(session, identifier, message, error));
391
if (iter == vector_of_engines.end())
393
return message::table::shared_ptr();
395
message::table::shared_ptr table_message(new message::Table(message));
397
drizzled::message::Cache::singleton().insert(identifier, table_message);
399
return table_message;
432
class DropTableByIdentifier: public std::unary_function<EngineVector::value_type, bool>
588
class DeleteTableStorageEngine
589
: public unary_function<plugin::StorageEngine *, void>
434
Session::reference session;
435
identifier::Table::const_reference identifier;
436
drizzled::error_t &error;
440
DropTableByIdentifier(Session::reference session_arg,
441
identifier::Table::const_reference identifier_arg,
442
drizzled::error_t &error_arg) :
443
session(session_arg),
444
identifier(identifier_arg),
596
DeleteTableStorageEngine(Session *session_arg, const char *path_arg,
597
handler **file_arg, int *error_arg)
598
: session(session_arg), path(path_arg), file(file_arg), dt_error(error_arg) {}
448
600
result_type operator() (argument_type engine)
450
if (not engine->doDoesTableExist(session, identifier))
453
int local_error= engine->doDropTable(session, identifier);
461
case HA_ERR_NO_SUCH_TABLE:
463
error= static_cast<drizzled::error_t>(HA_ERR_NO_SUCH_TABLE);
467
error= static_cast<drizzled::error_t>(local_error);
474
bool StorageEngine::dropTable(Session::reference session,
475
identifier::Table::const_reference identifier,
476
drizzled::error_t &error)
480
EngineVector::const_iterator iter= std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
481
DropTableByIdentifier(session, identifier, error));
487
else if (iter == vector_of_engines.end())
489
error= ER_BAD_TABLE_ERROR;
493
drizzled::message::Cache::singleton().erase(identifier);
498
bool StorageEngine::dropTable(Session& session,
499
const identifier::Table &identifier)
501
drizzled::error_t error;
503
if (not dropTable(session, identifier, error))
511
bool StorageEngine::dropTable(Session::reference session,
512
StorageEngine &engine,
513
identifier::Table::const_reference identifier,
514
drizzled::error_t &error)
517
engine.setTransactionReadWrite(session);
519
assert(identifier.isTmp());
521
if (unlikely(plugin::EventObserver::beforeDropTable(session, identifier)))
523
error= ER_EVENT_OBSERVER_PLUGIN;
527
error= static_cast<drizzled::error_t>(engine.doDropTable(session, identifier));
529
if (unlikely(plugin::EventObserver::afterDropTable(session, identifier, error)))
531
error= ER_EVENT_OBSERVER_PLUGIN;
535
drizzled::message::Cache::singleton().erase(identifier);
602
char tmp_path[FN_REFLEN];
605
if(*dt_error!=ENOENT) /* already deleted table */
611
if (!engine->is_enabled())
614
if ((tmp_file= engine->create(NULL, session->mem_root)))
619
path= engine->checkLowercaseNames(path, tmp_path);
620
const string table_path(path);
621
int tmp_error= engine->deleteTable(session, table_path);
623
if (tmp_error != ENOENT)
627
if (engine->check_flag(HTON_BIT_HAS_DATA_DICTIONARY))
628
delete_table_proto_file(path);
630
tmp_error= delete_table_proto_file(path);
633
*dt_error= tmp_error;
648
This should return ENOENT if the file doesn't exists.
649
The .frm file will be deleted only if we return 0 or ENOENT
651
int plugin::StorageEngine::deleteTable(Session *session, const char *path,
652
const char *db, const char *alias,
653
bool generate_warning)
655
TableShare dummy_share;
657
memset(&dummy_table, 0, sizeof(dummy_table));
658
memset(&dummy_share, 0, sizeof(dummy_share));
660
dummy_table.s= &dummy_share;
665
for_each(all_engines.begin(), all_engines.end(),
666
DeleteTableStorageEngine(session, path, &file, &error));
668
if (error == ENOENT) /* proto may be left behind */
669
error= delete_table_proto_file(path);
671
if (error && generate_warning)
674
Because file->print_error() use my_error() to generate the error message
675
we use an internal error handler to intercept it and store the text
676
in a temporary buffer. Later the message will be presented to user
679
Ha_delete_table_error_handler ha_delete_table_error_handler;
681
/* Fill up strucutures that print_error may need */
682
dummy_share.path.str= (char*) path;
683
dummy_share.path.length= strlen(path);
684
dummy_share.db.str= (char*) db;
685
dummy_share.db.length= strlen(db);
686
dummy_share.table_name.str= (char*) alias;
687
dummy_share.table_name.length= strlen(alias);
688
dummy_table.alias= alias;
692
file->change_table_ptr(&dummy_table, &dummy_share);
694
session->push_internal_handler(&ha_delete_table_error_handler);
695
file->print_error(error, 0);
697
session->pop_internal_handler();
700
error= -1; /* General form of fail. maybe bad FRM */
703
XXX: should we convert *all* errors to warnings here?
704
What if the error is fatal?
706
push_warning(session, DRIZZLE_ERROR::WARN_LEVEL_ERROR, error,
707
ha_delete_table_error_handler.buff);
716
class DFETableNameIterator: public plugin::TableNameIteratorImplementation
720
uint32_t current_entry;
723
DFETableNameIterator(const string &database)
724
: plugin::TableNameIteratorImplementation(database),
729
~DFETableNameIterator();
731
int next(string *name);
735
DFETableNameIterator::~DFETableNameIterator()
741
int DFETableNameIterator::next(string *name)
743
char uname[NAME_LEN + 1];
746
uint32_t file_name_len;
747
const char *wild= NULL;
752
char path[FN_REFLEN];
754
build_table_filename(path, sizeof(path), db.c_str(), "", false);
756
dirp = my_dir(path,MYF(dir ? MY_WANT_STAT : 0));
760
if (my_errno == ENOENT)
761
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db.c_str());
763
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
773
if (current_entry == dirp->number_off_files)
780
file= dirp->dir_entry + current_entry;
782
if (my_strcasecmp(system_charset_info, ext=fn_rext(file->name),".dfe") ||
783
is_prefix(file->name, TMP_FILE_PREFIX))
787
file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
789
uname[file_name_len]= '\0';
791
if (wild && wild_compare(uname, wild, 0))
802
plugin::TableNameIterator::TableNameIterator(const string &db)
803
: current_implementation(NULL), database(db)
805
engine_iter= all_engines.begin();
806
default_implementation= new DFETableNameIterator(database);
809
plugin::TableNameIterator::~TableNameIterator()
811
delete current_implementation;
812
if (current_implementation != default_implementation)
814
delete default_implementation;
818
int plugin::TableNameIterator::next(string *name)
823
if (current_implementation == NULL)
825
while(current_implementation == NULL &&
826
(engine_iter != all_engines.end()))
828
plugin::StorageEngine *engine= *engine_iter;
829
current_implementation= engine->tableNameIterator(database);
833
if (current_implementation == NULL &&
834
(engine_iter == all_engines.end()))
836
current_implementation= default_implementation;
840
err= current_implementation->next(name);
844
if (current_implementation != default_implementation)
846
delete current_implementation;
847
current_implementation= NULL;
856
handler *plugin::StorageEngine::getNewHandler(TableShare *share,
858
plugin::StorageEngine *engine)
862
if (engine && engine->is_enabled())
864
if ((file= engine->create(share, alloc)))
869
Try the default table type
870
Here the call to current_session() is ok as we call this function a lot of
871
times but we enter this branch very seldom.
873
return(plugin::StorageEngine::getNewHandler(share, alloc, plugin::StorageEngine::defaultStorageEngine(current_session)));
878
Return the default storage engine plugin::StorageEngine for thread
880
defaultStorageEngine(session)
881
@param session current thread
884
pointer to plugin::StorageEngine
886
plugin::StorageEngine *plugin::StorageEngine::defaultStorageEngine(Session *session)
888
if (session->variables.storage_engine)
889
return session->variables.storage_engine;
890
return global_system_variables.storage_engine;
547
894
Initiates table-file and calls appropriate database-creator.
554
bool StorageEngine::createTable(Session &session,
555
const identifier::Table &identifier,
556
message::Table& table_message)
901
int plugin::StorageEngine::createTable(Session *session, const char *path,
902
const char *db, const char *table_name,
903
HA_CREATE_INFO *create_info,
904
bool update_create_info,
905
message::Table *table_proto)
558
drizzled::error_t error= EE_OK;
560
TableShare share(identifier);
561
table::Shell table(share);
909
TableShare share(db, 0, table_name, path);
562
910
message::Table tmp_proto;
564
if (share.parse_table_proto(session, table_message) || share.open_table_from_share(&session, identifier, "", 0, 0, table))
566
// @note Error occured, we should probably do a little more here.
567
// ER_CORRUPT_TABLE_DEFINITION,ER_CORRUPT_TABLE_DEFINITION_ENUM
569
my_error(ER_CORRUPT_TABLE_DEFINITION_UNKNOWN, identifier);
575
/* Check for legal operations against the Engine using the proto (if used) */
576
if (table_message.type() == message::Table::TEMPORARY &&
577
share.storage_engine->check_flag(HTON_BIT_TEMPORARY_NOT_SUPPORTED) == true)
579
error= HA_ERR_UNSUPPORTED;
581
else if (table_message.type() != message::Table::TEMPORARY &&
582
share.storage_engine->check_flag(HTON_BIT_TEMPORARY_ONLY) == true)
584
error= HA_ERR_UNSUPPORTED;
588
share.storage_engine->setTransactionReadWrite(session);
590
error= static_cast<drizzled::error_t>(share.storage_engine->doCreateTable(session,
596
if (error == ER_TABLE_PERMISSION_DENIED)
598
my_error(ER_TABLE_PERMISSION_DENIED, identifier);
603
identifier.getSQLPath(path);
604
my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), path.c_str(), error);
607
table.delete_table();
610
return(error == EE_OK);
613
Cursor *StorageEngine::getCursor(Table &arg)
618
class AddTableIdentifier :
619
public std::unary_function<StorageEngine *, void>
621
CachedDirectory &directory;
622
const identifier::Schema &identifier;
623
identifier::Table::vector &set_of_identifiers;
627
AddTableIdentifier(CachedDirectory &directory_arg, const identifier::Schema &identifier_arg, identifier::Table::vector &of_names) :
628
directory(directory_arg),
629
identifier(identifier_arg),
630
set_of_identifiers(of_names)
634
result_type operator() (argument_type engine)
636
engine->doGetTableIdentifiers(directory, identifier, set_of_identifiers);
641
void StorageEngine::getIdentifiers(Session &session, const identifier::Schema &schema_identifier, identifier::Table::vector &set_of_identifiers)
643
CachedDirectory directory(schema_identifier.getPath(), set_of_table_definition_ext);
645
if (schema_identifier == INFORMATION_SCHEMA_IDENTIFIER)
647
else if (schema_identifier == DATA_DICTIONARY_IDENTIFIER)
651
if (directory.fail())
653
errno= directory.getError();
657
schema_identifier.getSQLPath(path);
658
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), path.c_str());
662
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), directory.getPath(), errno);
669
std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
670
AddTableIdentifier(directory, schema_identifier, set_of_identifiers));
672
session.doGetTableIdentifiers(directory, schema_identifier, set_of_identifiers);
675
class DropTable: public std::unary_function<identifier::Table&, bool>
678
StorageEngine *engine;
682
DropTable(Session &session_arg, StorageEngine *engine_arg) :
683
session(session_arg),
687
result_type operator() (argument_type identifier)
689
return engine->doDropTable(session, identifier) == 0;
693
/* This will later be converted to identifier::Tables */
694
class DropTables: public std::unary_function<StorageEngine *, void>
697
identifier::Table::vector &table_identifiers;
701
DropTables(Session &session_arg, identifier::Table::vector &table_identifiers_arg) :
702
session(session_arg),
703
table_identifiers(table_identifiers_arg)
706
result_type operator() (argument_type engine)
708
// True returning from DropTable means the table has been successfully
709
// deleted, so it should be removed from the list of tables to drop
710
table_identifiers.erase(std::remove_if(table_identifiers.begin(),
711
table_identifiers.end(),
712
DropTable(session, engine)),
713
table_identifiers.end());
718
This only works for engines which use file based DFE.
720
Note-> Unlike MySQL, we do not, on purpose, delete files that do not match any engines.
722
void StorageEngine::removeLostTemporaryTables(Session &session, const char *directory)
724
CachedDirectory dir(directory, set_of_table_definition_ext);
725
identifier::Table::vector table_identifiers;
729
errno= dir.getError();
730
my_error(ER_CANT_READ_DIR, MYF(0), directory, errno);
735
CachedDirectory::Entries files= dir.getEntries();
737
for (CachedDirectory::Entries::iterator fileIter= files.begin();
738
fileIter != files.end(); fileIter++)
742
CachedDirectory::Entry *entry= *fileIter;
744
/* We remove the file extension. */
745
length= entry->filename.length();
746
entry->filename.resize(length - DEFAULT_DEFINITION_FILE_EXT.length());
750
path+= entry->filename;
751
message::Table definition;
752
if (StorageEngine::readTableFile(path, definition))
754
identifier::Table identifier(definition.schema(), definition.name(), path);
755
table_identifiers.push_back(identifier);
759
std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
760
DropTables(session, table_identifiers));
763
Now we just clean up anything that might left over.
765
We rescan because some of what might have been there should
766
now be all nice and cleaned up.
768
std::set<std::string> all_exts= set_of_table_definition_ext;
770
for (EngineVector::iterator iter= vector_of_engines.begin();
771
iter != vector_of_engines.end() ; iter++)
773
for (const char **ext= (*iter)->bas_ext(); *ext ; ext++)
774
all_exts.insert(*ext);
777
CachedDirectory rescan(directory, all_exts);
779
files= rescan.getEntries();
780
for (CachedDirectory::Entries::iterator fileIter= files.begin();
781
fileIter != files.end(); fileIter++)
784
CachedDirectory::Entry *entry= *fileIter;
788
path+= entry->filename;
790
unlink(path.c_str());
796
Print error that we got from Cursor function.
799
In case of delete table it's only safe to use the following parts of
800
the 'table' structure:
801
- table->getShare()->path
804
void StorageEngine::print_error(int error, myf errflag, const Table &table) const
806
drizzled::error_t textno= ER_GET_ERRNO;
809
textno=ER_OPEN_AS_READONLY;
815
textno=ER_FILE_NOT_FOUND;
817
case HA_ERR_KEY_NOT_FOUND:
818
case HA_ERR_NO_ACTIVE_RECORD:
819
case HA_ERR_END_OF_FILE:
820
textno=ER_KEY_NOT_FOUND;
822
case HA_ERR_WRONG_MRG_TABLE_DEF:
823
textno=ER_WRONG_MRG_TABLE;
825
case HA_ERR_FOUND_DUPP_KEY:
827
uint32_t key_nr= table.get_dup_key(error);
828
if ((int) key_nr >= 0)
830
const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
832
print_keydup_error(key_nr, err_msg, table);
839
case HA_ERR_FOREIGN_DUPLICATE_KEY:
841
uint32_t key_nr= table.get_dup_key(error);
842
if ((int) key_nr >= 0)
846
/* Write the key in the error message */
847
char key[MAX_KEY_LENGTH];
848
String str(key,sizeof(key),system_charset_info);
850
/* Table is opened and defined at this point */
851
key_unpack(&str, &table,(uint32_t) key_nr);
852
max_length= (DRIZZLE_ERRMSG_SIZE-
853
(uint32_t) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
854
if (str.length() >= max_length)
856
str.length(max_length-4);
857
str.append(STRING_WITH_LEN("..."));
859
my_error(ER_FOREIGN_DUPLICATE_KEY, MYF(0), table.getShare()->getTableName(),
860
str.c_ptr(), key_nr+1);
866
case HA_ERR_FOUND_DUPP_UNIQUE:
867
textno=ER_DUP_UNIQUE;
869
case HA_ERR_RECORD_CHANGED:
873
textno=ER_NOT_KEYFILE;
875
case HA_ERR_WRONG_IN_RECORD:
876
textno= ER_CRASHED_ON_USAGE;
878
case HA_ERR_CRASHED_ON_USAGE:
879
textno=ER_CRASHED_ON_USAGE;
881
case HA_ERR_NOT_A_TABLE:
882
textno= static_cast<drizzled::error_t>(error);
884
case HA_ERR_CRASHED_ON_REPAIR:
885
textno=ER_CRASHED_ON_REPAIR;
887
case HA_ERR_OUT_OF_MEM:
888
textno=ER_OUT_OF_RESOURCES;
890
case HA_ERR_WRONG_COMMAND:
891
textno=ER_ILLEGAL_HA;
893
case HA_ERR_OLD_FILE:
894
textno=ER_OLD_KEYFILE;
896
case HA_ERR_UNSUPPORTED:
897
textno=ER_UNSUPPORTED_EXTENSION;
899
case HA_ERR_RECORD_FILE_FULL:
900
case HA_ERR_INDEX_FILE_FULL:
901
textno=ER_RECORD_FILE_FULL;
903
case HA_ERR_LOCK_WAIT_TIMEOUT:
904
textno=ER_LOCK_WAIT_TIMEOUT;
906
case HA_ERR_LOCK_TABLE_FULL:
907
textno=ER_LOCK_TABLE_FULL;
909
case HA_ERR_LOCK_DEADLOCK:
910
textno=ER_LOCK_DEADLOCK;
912
case HA_ERR_READ_ONLY_TRANSACTION:
913
textno=ER_READ_ONLY_TRANSACTION;
915
case HA_ERR_CANNOT_ADD_FOREIGN:
916
textno=ER_CANNOT_ADD_FOREIGN;
918
case HA_ERR_ROW_IS_REFERENCED:
921
get_error_message(error, &str);
922
my_error(ER_ROW_IS_REFERENCED_2, MYF(0), str.c_ptr_safe());
925
case HA_ERR_NO_REFERENCED_ROW:
928
get_error_message(error, &str);
929
my_error(ER_NO_REFERENCED_ROW_2, MYF(0), str.c_ptr_safe());
932
case HA_ERR_TABLE_DEF_CHANGED:
933
textno=ER_TABLE_DEF_CHANGED;
935
case HA_ERR_NO_SUCH_TABLE:
937
identifier::Table identifier(table.getShare()->getSchemaName(), table.getShare()->getTableName());
938
my_error(ER_TABLE_UNKNOWN, identifier);
941
case HA_ERR_RBR_LOGGING_FAILED:
942
textno= ER_BINLOG_ROW_LOGGING_FAILED;
944
case HA_ERR_DROP_INDEX_FK:
946
const char *ptr= "???";
947
uint32_t key_nr= table.get_dup_key(error);
948
if ((int) key_nr >= 0)
949
ptr= table.key_info[key_nr].name;
950
my_error(ER_DROP_INDEX_FK, MYF(0), ptr);
953
case HA_ERR_TABLE_NEEDS_UPGRADE:
954
textno=ER_TABLE_NEEDS_UPGRADE;
956
case HA_ERR_TABLE_READONLY:
957
textno= ER_OPEN_AS_READONLY;
959
case HA_ERR_AUTOINC_READ_FAILED:
960
textno= ER_AUTOINC_READ_FAILED;
962
case HA_ERR_AUTOINC_ERANGE:
963
textno= ER_WARN_DATA_OUT_OF_RANGE;
965
case HA_ERR_LOCK_OR_ACTIVE_TRANSACTION:
966
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
967
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
972
The error was "unknown" to this function.
973
Ask Cursor if it has got a message for this error
975
bool temporary= false;
977
temporary= get_error_message(error, &str);
980
const char* engine_name= getName().c_str();
982
my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(),
985
my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine_name);
989
my_error(ER_GET_ERRNO,errflag,error);
995
my_error(textno, errflag, table.getShare()->getTableName(), error);
1000
Return an error message specific to this Cursor.
1002
@param error error code previously returned by Cursor
1003
@param buf pointer to String where to add error message
1006
Returns true if this is a temporary error
1008
bool StorageEngine::get_error_message(int , String* ) const
1014
void StorageEngine::print_keydup_error(uint32_t key_nr, const char *msg, const Table &table) const
1016
/* Write the duplicated key in the error message */
1017
char key[MAX_KEY_LENGTH];
1018
String str(key,sizeof(key),system_charset_info);
1020
if (key_nr == MAX_KEY)
1022
/* Key is unknown */
1023
str.copy("", 0, system_charset_info);
1024
my_printf_error(ER_DUP_ENTRY, msg, MYF(0), str.c_ptr(), "*UNKNOWN*");
1028
/* Table is opened and defined at this point */
1029
key_unpack(&str, &table, (uint32_t) key_nr);
1030
uint32_t max_length=DRIZZLE_ERRMSG_SIZE-(uint32_t) strlen(msg);
1031
if (str.length() >= max_length)
1033
str.length(max_length-4);
1034
str.append(STRING_WITH_LEN("..."));
1036
my_printf_error(ER_DUP_ENTRY, msg,
1037
MYF(0), str.c_ptr(), table.key_info[key_nr].name);
1042
int StorageEngine::deleteDefinitionFromPath(const identifier::Table &identifier)
1044
std::string path(identifier.getPath());
1046
path.append(DEFAULT_DEFINITION_FILE_EXT);
1048
return internal::my_delete(path.c_str(), MYF(0));
1051
int StorageEngine::renameDefinitionFromPath(const identifier::Table &dest, const identifier::Table &src)
1053
message::Table table_message;
1054
std::string src_path(src.getPath());
1055
std::string dest_path(dest.getPath());
1057
src_path.append(DEFAULT_DEFINITION_FILE_EXT);
1058
dest_path.append(DEFAULT_DEFINITION_FILE_EXT);
1060
bool was_read= StorageEngine::readTableFile(src_path.c_str(), table_message);
1067
dest.copyToTableMessage(table_message);
1069
int error= StorageEngine::writeDefinitionFromPath(dest, table_message);
1073
if (unlink(src_path.c_str()))
1074
perror(src_path.c_str());
1080
int StorageEngine::writeDefinitionFromPath(const identifier::Table &identifier, message::Table &table_message)
1082
char definition_file_tmp[FN_REFLEN];
1083
std::string file_name(identifier.getPath());
1085
file_name.append(DEFAULT_DEFINITION_FILE_EXT);
1087
snprintf(definition_file_tmp, sizeof(definition_file_tmp), "%sXXXXXX", file_name.c_str());
1089
int fd= mkstemp(definition_file_tmp);
1093
perror(definition_file_tmp);
1097
google::protobuf::io::ZeroCopyOutputStream* output=
1098
new google::protobuf::io::FileOutputStream(fd);
1104
success= table_message.SerializeToZeroCopyStream(output);
1113
std::string error_message;
1114
identifier.getSQLPath(error_message);
1116
my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
1117
error_message.c_str(),
1118
table_message.InitializationErrorString().c_str());
1121
if (close(fd) == -1)
1122
perror(definition_file_tmp);
1124
if (unlink(definition_file_tmp) == -1)
1125
perror(definition_file_tmp);
1127
return ER_CORRUPT_TABLE_DEFINITION;
1132
if (close(fd) == -1)
1135
perror(definition_file_tmp);
1137
if (unlink(definition_file_tmp))
1138
perror(definition_file_tmp);
1143
if (rename(definition_file_tmp, file_name.c_str()) == -1)
1146
perror(definition_file_tmp);
1148
if (unlink(definition_file_tmp))
1149
perror(definition_file_tmp);
1157
class CanCreateTable: public std::unary_function<StorageEngine *, bool>
1159
const identifier::Table &identifier;
1162
CanCreateTable(const identifier::Table &identifier_arg) :
1163
identifier(identifier_arg)
1166
result_type operator() (argument_type engine)
1168
return not engine->doCanCreateTable(identifier);
1174
@note on success table can be created.
1176
bool StorageEngine::canCreateTable(const identifier::Table &identifier)
1178
EngineVector::iterator iter=
1179
std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
1180
CanCreateTable(identifier));
1182
if (iter == vector_of_engines.end())
1190
bool StorageEngine::readTableFile(const std::string &path, message::Table &table_message)
1192
std::fstream input(path.c_str(), std::ios::in | std::ios::binary);
1197
if (table_message.ParseFromIstream(&input))
1204
my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
1205
table_message.name().empty() ? path.c_str() : table_message.name().c_str(),
1206
table_message.InitializationErrorString().empty() ? "": table_message.InitializationErrorString().c_str());
1211
perror(path.c_str());
1217
std::ostream& operator<<(std::ostream& output, const StorageEngine &engine)
1219
output << "StorageEngine:(";
1220
output << engine.getName();
1226
} /* namespace plugin */
914
if (parse_table_proto(session, *table_proto, &share))
919
table_proto= &tmp_proto;
920
if (open_table_def(session, &share))
924
if (open_table_from_share(session, &share, "", 0, (uint32_t) READ_ALL, 0,
928
if (update_create_info)
929
table.updateCreateInfo(create_info, table_proto);
931
error= share.storage_engine->createTable(session, path, &table,
932
create_info, table_proto);
933
table.closefrm(false);
936
char name_buff[FN_REFLEN];
937
sprintf(name_buff,"%s.%s",db,table_name);
938
my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name_buff, error);
941
share.free_table_share();
1227
945
} /* namespace drizzled */