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/registry.h>
44
42
#include <drizzled/unireg.h>
45
43
#include <drizzled/data_home.h>
46
44
#include "drizzled/errmsg_print.h"
45
#include <drizzled/plugin/registry.h>
47
46
#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"
52
#include "drizzled/db.h"
54
48
#include <drizzled/table_proto.h>
55
#include <drizzled/plugin/event_observer.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)))
56
Registry<plugin::StorageEngine *> all_engines;
58
plugin::StorageEngine::StorageEngine(const string name_arg,
59
const bitset<HTON_BIT_SIZE> &flags_arg,
60
size_t savepoint_offset_arg,
62
: name(name_arg), two_phase_commit(support_2pc), enabled(true),
64
savepoint_offset(savepoint_alloc_size),
65
orig_savepoint_offset(savepoint_offset_arg),
70
savepoint_alloc_size+= orig_savepoint_offset;
78
plugin::StorageEngine::~StorageEngine()
80
savepoint_alloc_size-= orig_savepoint_offset;
83
void plugin::StorageEngine::setTransactionReadWrite(Session* session)
85
Ha_trx_info *ha_info= &session->ha_data[getSlot()].ha_info[0];
87
When a storage engine method is called, the transaction must
88
have been started, unless it's a DDL call, for which the
89
storage engine starts the transaction internally, and commits
90
it internally, without registering in the ha_list.
91
Unfortunately here we can't know know for sure if the engine
92
has registered the transaction or not, so we must check.
94
if (ha_info->is_started())
97
* table_share can be NULL in plugin::StorageEngine::deleteTable().
99
ha_info->set_trx_read_write();
105
int plugin::StorageEngine::renameTableImplementation(Session *,
110
for (const char **ext= bas_ext(); *ext ; ext++)
112
if (rename_file_ext(from, to, *ext))
118
error= ER_EVENT_OBSERVER_PLUGIN;
114
if ((error=my_errno) != ENOENT)
126
124
Delete all files with extension from bas_ext().
128
126
@param name Base name of table
131
We assume that the Cursor may return more extensions than
129
We assume that the handler may return more extensions than
132
130
was actually used for the file.
140
int StorageEngine::doDropTable(Session&, const identifier::Table &identifier)
138
int plugin::StorageEngine::deleteTableImplementation(Session *,
139
const string table_path)
144
142
int enoent_or_zero= ENOENT; // Error if no file was deleted
145
143
char buff[FN_REFLEN];
147
for (const char **ext= bas_ext(); *ext ; ext++)
145
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)))
147
fn_format(buff, table_path.c_str(), "", *ext,
148
MY_UNPACK_FILENAME|MY_APPEND_EXT);
149
if (my_delete_with_symlink(buff, MYF(0)))
153
if ((error= errno) != ENOENT)
151
if ((error= my_errno) != ENOENT)
158
155
enoent_or_zero= 0; // No error for ENOENT
161
156
error= enoent_or_zero;
166
bool StorageEngine::addPlugin(StorageEngine *engine)
169
vector_of_engines.push_back(engine);
171
if (engine->getTableDefinitionFileExtension().length())
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
bool plugin::StorageEngine::addPlugin(plugin::StorageEngine *engine)
186
if (all_engines.add(engine))
173
assert(engine->getTableDefinitionFileExtension().length() == DEFAULT_DEFINITION_FILE_EXT.length());
174
set_of_table_definition_ext.insert(engine->getTableDefinitionFileExtension());
188
errmsg_printf(ERRMSG_LVL_ERROR,
189
_("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>
195
void plugin::StorageEngine::removePlugin(plugin::StorageEngine *engine)
197
all_engines.remove(engine);
200
plugin::StorageEngine *plugin::StorageEngine::findByName(Session *session,
204
transform(find_str.begin(), find_str.end(),
205
find_str.begin(), ::tolower);
206
string default_str("default");
207
if (find_str == default_str)
208
return ha_default_storage_engine(session);
210
plugin::StorageEngine *engine= all_engines.find(find_str);
212
if (engine && engine->is_user_selectable())
218
class StorageEngineCloseConnection
219
: public unary_function<plugin::StorageEngine *, void>
246
221
Session *session;
262
238
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)
240
void plugin::StorageEngine::closeConnection(Session* session)
242
for_each(all_engines.begin(), all_engines.end(),
243
StorageEngineCloseConnection(session));
246
void plugin::StorageEngine::dropDatabase(char* path)
248
for_each(all_engines.begin(), all_engines.end(),
249
bind2nd(mem_fun(&plugin::StorageEngine::drop_database),path));
252
int plugin::StorageEngine::commitOrRollbackByXID(XID *xid, bool commit)
257
transform(all_engines.begin(), all_engines.end(), results.begin(),
258
bind2nd(mem_fun(&plugin::StorageEngine::commit_by_xid),xid));
260
transform(all_engines.begin(), all_engines.end(), results.begin(),
261
bind2nd(mem_fun(&plugin::StorageEngine::rollback_by_xid),xid));
263
if (find_if(results.begin(), results.end(), bind2nd(equal_to<int>(),0))
271
This function should be called when MySQL sends rows of a SELECT result set
272
or the EOF mark to the client. It releases a possible adaptive hash index
273
S-latch held by session in InnoDB and also releases a possible InnoDB query
274
FIFO ticket to enter InnoDB. To save CPU time, InnoDB allows a session to
275
keep them over several calls of the InnoDB handler interface when a join
276
is executed. But when we let the control to pass to the client they have
277
to be released because if the application program uses mysql_use_result(),
278
it may deadlock on the S-latch if the application on another connection
279
performs another SQL query. In MySQL-4.1 this is even more important because
280
there a connection can have several SELECT queries open at the same time.
282
@param session the thread handle of the current connection
287
int plugin::StorageEngine::releaseTemporaryLatches(Session *session)
289
for_each(all_engines.begin(), all_engines.end(),
290
bind2nd(mem_fun(&plugin::StorageEngine::release_temporary_latches),session));
294
bool plugin::StorageEngine::flushLogs(plugin::StorageEngine *engine)
272
296
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())
298
if (find_if(all_engines.begin(), all_engines.end(),
299
mem_fun(&plugin::StorageEngine::flush_logs))
300
!= all_engines.begin())
281
if (engine->flush_logs())
305
if ((!engine->is_enabled()) ||
306
(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);
313
recover() step of xa.
316
there are three modes of operation:
317
- automatic recover after a crash
318
in this case commit_list != 0, tc_heuristic_recover==0
319
all xids from commit_list are committed, others are rolled back
320
- manual (heuristic) recover
321
in this case commit_list==0, tc_heuristic_recover != 0
322
DBA has explicitly specified that all prepared transactions should
323
be committed (or rolled back).
324
- no recovery (MySQL did not detect a crash)
325
in this case commit_list==0, tc_heuristic_recover == 0
326
there should be no prepared transactions in this case.
328
class XARecover : unary_function<plugin::StorageEngine *, void>
330
int trans_len, found_foreign_xids, found_my_xids;
336
XARecover(XID *trans_list_arg, int trans_len_arg,
337
HASH *commit_list_arg, bool dry_run_arg)
338
: trans_len(trans_len_arg), found_foreign_xids(0), found_my_xids(0),
340
trans_list(trans_list_arg), commit_list(commit_list_arg),
346
return found_foreign_xids;
351
return found_my_xids;
354
result_type operator() (argument_type engine)
359
if (engine->is_enabled())
361
while ((got= engine->recover(trans_list, trans_len)) > 0 )
363
errmsg_printf(ERRMSG_LVL_INFO,
364
_("Found %d prepared transaction(s) in %s"),
365
got, engine->getName().c_str());
366
for (int i=0; i < got; i ++)
368
my_xid x=trans_list[i].get_my_xid();
369
if (!x) // not "mine" - that is generated by external TM
371
xid_cache_insert(trans_list+i, XA_PREPARED);
372
found_foreign_xids++;
382
hash_search(commit_list, (unsigned char *)&x, sizeof(x)) != 0 :
383
tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
385
engine->commit_by_xid(trans_list+i);
389
engine->rollback_by_xid(trans_list+i);
399
int plugin::StorageEngine::recover(HASH *commit_list)
401
XID *trans_list= NULL;
404
bool dry_run= (commit_list==0 && tc_heuristic_recover==0);
406
/* commit_list and tc_heuristic_recover cannot be set both */
407
assert(commit_list==0 || tc_heuristic_recover==0);
409
/* if either is set, total_ha_2pc must be set too */
410
if (total_ha_2pc <= 1)
414
#ifndef WILL_BE_DELETED_LATER
417
for now, only InnoDB supports 2pc. It means we can always safely
418
rollback all pending transactions, without risking inconsistent data
421
assert(total_ha_2pc == 2); // only InnoDB and binlog
422
tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
425
for (trans_len= MAX_XID_LIST_SIZE ;
426
trans_list==0 && trans_len > MIN_XID_LIST_SIZE; trans_len/=2)
428
trans_list=(XID *)malloc(trans_len*sizeof(XID));
432
errmsg_printf(ERRMSG_LVL_ERROR, ER(ER_OUTOFMEMORY), trans_len*sizeof(XID));
437
errmsg_printf(ERRMSG_LVL_INFO, _("Starting crash recovery..."));
440
XARecover recover_func(trans_list, trans_len, commit_list, dry_run);
441
for_each(all_engines.begin(), all_engines.end(), recover_func);
444
if (recover_func.getForeignXIDs())
445
errmsg_printf(ERRMSG_LVL_WARN,
446
_("Found %d prepared XA transactions"),
447
recover_func.getForeignXIDs());
448
if (dry_run && recover_func.getMyXIDs())
450
errmsg_printf(ERRMSG_LVL_ERROR,
451
_("Found %d prepared transactions! It means that drizzled "
452
"was not shut down properly last time and critical "
453
"recovery information (last binlog or %s file) was "
454
"manually deleted after a crash. You have to start "
455
"drizzled with the --tc-heuristic-recover switch to "
456
"commit or rollback pending transactions."),
457
recover_func.getMyXIDs(), opt_tc_log_file);
461
errmsg_printf(ERRMSG_LVL_INFO, _("Crash recovery finished."));
465
int plugin::StorageEngine::startConsistentSnapshot(Session *session)
467
for_each(all_engines.begin(), all_engines.end(),
468
bind2nd(mem_fun(&plugin::StorageEngine::start_consistent_snapshot),
473
class StorageEngineGetTableProto: public unary_function<plugin::StorageEngine *,bool>
476
message::Table *table_proto;
479
StorageEngineGetTableProto(const char* path_arg,
480
message::Table *table_proto_arg,
482
:path(path_arg), table_proto(table_proto_arg), err(err_arg) {}
484
result_type operator() (argument_type engine)
486
int ret= engine->getTableProtoImplementation(path, table_proto);
308
488
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);
315
class StorageEngineDoesTableExist: public std::unary_function<StorageEngine *, bool>
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)
328
return engine->doDoesTableExist(session, identifier);
333
Utility method which hides some of the details of getTableDefinition()
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";
365
Call this function in order to give the Cursor the possiblity
491
return *err == EEXIST;
495
static int drizzle_read_table_proto(const char* path, message::Table* table)
497
int fd= open(path, O_RDONLY);
502
google::protobuf::io::ZeroCopyInputStream* input=
503
new google::protobuf::io::FileInputStream(fd);
505
if (table->ParseFromZeroCopyStream(input) == false)
518
Call this function in order to give the handler the possiblity
366
519
to ask engine if there are any new tables that should be written to disk
367
520
or any dropped tables that need to be removed from disk
369
int StorageEngine::getTableDefinition(Session& session,
370
const identifier::Table &identifier,
371
message::table::shared_ptr &table_message,
372
bool include_temporary_tables)
522
int plugin::StorageEngine::getTableProto(const char* path,
523
message::Table *table_proto)
374
drizzled::error_t err= static_cast<drizzled::error_t>(ENOENT);
376
if (include_temporary_tables)
527
::drizzled::Registry<plugin::StorageEngine *>::iterator iter=
528
find_if(all_engines.begin(), all_engines.end(),
529
StorageEngineGetTableProto(path, table_proto, &err));
530
if (iter == all_engines.end())
378
Table *table= session.find_temporary_table(identifier);
532
string proto_path(path);
533
string file_ext(".dfe");
534
proto_path.append(file_ext);
536
int error= access(proto_path.c_str(), F_OK);
381
table_message.reset(new message::Table(*table->getShare()->getTableMessage()));
545
int read_proto_err= drizzle_read_table_proto(proto_path.c_str(),
386
drizzled::message::table::shared_ptr table_ptr;
387
if ((table_ptr= drizzled::message::Cache::singleton().find(identifier)))
389
table_message= table_ptr;
392
message::Table message;
393
EngineVector::iterator iter=
394
std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
395
StorageEngineGetTableDefinition(session, identifier, message, err));
397
if (iter == vector_of_engines.end())
401
table_message.reset(new message::Table(message));
403
drizzled::message::Cache::singleton().insert(identifier, table_message);
408
message::table::shared_ptr StorageEngine::getTableMessage(Session& session,
409
identifier::Table::const_reference identifier,
410
drizzled::error_t &error,
411
bool include_temporary_tables)
413
error= static_cast<drizzled::error_t>(ENOENT);
415
if (include_temporary_tables)
417
Table *table= session.find_temporary_table(identifier);
421
return message::table::shared_ptr(new message::Table(*table->getShare()->getTableMessage()));
425
drizzled::message::table::shared_ptr table_ptr;
426
if ((table_ptr= drizzled::message::Cache::singleton().find(identifier)))
431
message::Table message;
432
EngineVector::iterator iter=
433
std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
434
StorageEngineGetTableDefinition(session, identifier, message, error));
436
if (iter == vector_of_engines.end())
438
error= static_cast<drizzled::error_t>(ENOENT);
439
return message::table::shared_ptr();
441
message::table::shared_ptr table_message(new message::Table(message));
443
drizzled::message::Cache::singleton().insert(identifier, table_message);
445
return table_message;
449
557
An interceptor to hijack the text of the error message without
450
558
setting an error in the thread. We need the text to present it
478
class DropTableByIdentifier: public std::unary_function<EngineVector::value_type, bool>
587
class DeleteTableStorageEngine
588
: public unary_function<plugin::StorageEngine *, void>
480
Session::reference session;
481
identifier::Table::const_reference identifier;
482
drizzled::error_t &error;
486
DropTableByIdentifier(Session::reference session_arg,
487
identifier::Table::const_reference identifier_arg,
488
drizzled::error_t &error_arg) :
489
session(session_arg),
490
identifier(identifier_arg),
595
DeleteTableStorageEngine(Session *session_arg, const char *path_arg,
596
handler **file_arg, int *error_arg)
597
: session(session_arg), path(path_arg), file(file_arg), dt_error(error_arg) {}
494
599
result_type operator() (argument_type engine)
496
if (not engine->doDoesTableExist(session, identifier))
499
int local_error= engine->doDropTable(session, identifier);
507
case HA_ERR_NO_SUCH_TABLE:
509
error= static_cast<drizzled::error_t>(HA_ERR_NO_SUCH_TABLE);
513
error= static_cast<drizzled::error_t>(local_error);
520
bool StorageEngine::dropTable(Session::reference session,
521
identifier::Table::const_reference identifier,
522
drizzled::error_t &error)
526
EngineVector::const_iterator iter= std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
527
DropTableByIdentifier(session, identifier, error));
533
else if (iter == vector_of_engines.end())
535
error= ER_BAD_TABLE_ERROR;
539
drizzled::message::Cache::singleton().erase(identifier);
544
bool StorageEngine::dropTable(Session& session,
545
const identifier::Table &identifier)
547
drizzled::error_t error;
549
if (not dropTable(session, identifier, error))
557
bool StorageEngine::dropTable(Session::reference session,
558
StorageEngine &engine,
559
identifier::Table::const_reference identifier,
560
drizzled::error_t &error)
563
engine.setTransactionReadWrite(session);
565
assert(identifier.isTmp());
567
if (unlikely(plugin::EventObserver::beforeDropTable(session, identifier)))
569
error= ER_EVENT_OBSERVER_PLUGIN;
573
error= static_cast<drizzled::error_t>(engine.doDropTable(session, identifier));
575
if (unlikely(plugin::EventObserver::afterDropTable(session, identifier, error)))
577
error= ER_EVENT_OBSERVER_PLUGIN;
581
drizzled::message::Cache::singleton().erase(identifier);
601
char tmp_path[FN_REFLEN];
604
if(*dt_error!=ENOENT) /* already deleted table */
610
if (!engine->is_enabled())
613
if ((tmp_file= engine->create(NULL, session->mem_root)))
618
path= engine->checkLowercaseNames(path, tmp_path);
619
const string table_path(path);
620
int tmp_error= engine->deleteTable(session, table_path);
622
if (tmp_error != ENOENT)
626
if (engine->check_flag(HTON_BIT_HAS_DATA_DICTIONARY))
627
delete_table_proto_file(path);
629
tmp_error= delete_table_proto_file(path);
632
*dt_error= tmp_error;
647
This should return ENOENT if the file doesn't exists.
648
The .frm file will be deleted only if we return 0 or ENOENT
650
int plugin::StorageEngine::deleteTable(Session *session, const char *path,
651
const char *db, const char *alias,
652
bool generate_warning)
654
TableShare dummy_share;
656
memset(&dummy_table, 0, sizeof(dummy_table));
657
memset(&dummy_share, 0, sizeof(dummy_share));
659
dummy_table.s= &dummy_share;
664
for_each(all_engines.begin(), all_engines.end(),
665
DeleteTableStorageEngine(session, path, &file, &error));
667
if (error == ENOENT) /* proto may be left behind */
668
error= delete_table_proto_file(path);
670
if (error && generate_warning)
673
Because file->print_error() use my_error() to generate the error message
674
we use an internal error handler to intercept it and store the text
675
in a temporary buffer. Later the message will be presented to user
678
Ha_delete_table_error_handler ha_delete_table_error_handler;
680
/* Fill up strucutures that print_error may need */
681
dummy_share.path.str= (char*) path;
682
dummy_share.path.length= strlen(path);
683
dummy_share.db.str= (char*) db;
684
dummy_share.db.length= strlen(db);
685
dummy_share.table_name.str= (char*) alias;
686
dummy_share.table_name.length= strlen(alias);
687
dummy_table.alias= alias;
691
file->change_table_ptr(&dummy_table, &dummy_share);
693
session->push_internal_handler(&ha_delete_table_error_handler);
694
file->print_error(error, 0);
696
session->pop_internal_handler();
699
error= -1; /* General form of fail. maybe bad FRM */
702
XXX: should we convert *all* errors to warnings here?
703
What if the error is fatal?
705
push_warning(session, DRIZZLE_ERROR::WARN_LEVEL_ERROR, error,
706
ha_delete_table_error_handler.buff);
715
class DFETableNameIterator: public plugin::TableNameIteratorImplementation
719
uint32_t current_entry;
722
DFETableNameIterator(const string &database)
723
: plugin::TableNameIteratorImplementation(database),
728
~DFETableNameIterator();
730
int next(string *name);
734
DFETableNameIterator::~DFETableNameIterator()
740
int DFETableNameIterator::next(string *name)
742
char uname[NAME_LEN + 1];
745
uint32_t file_name_len;
746
const char *wild= NULL;
751
char path[FN_REFLEN];
753
build_table_filename(path, sizeof(path), db.c_str(), "", false);
755
dirp = my_dir(path,MYF(dir ? MY_WANT_STAT : 0));
759
if (my_errno == ENOENT)
760
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db.c_str());
762
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
772
if (current_entry == dirp->number_off_files)
779
file= dirp->dir_entry + current_entry;
781
if (my_strcasecmp(system_charset_info, ext=fn_rext(file->name),".dfe") ||
782
is_prefix(file->name, TMP_FILE_PREFIX))
786
file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
788
uname[file_name_len]= '\0';
790
if (wild && wild_compare(uname, wild, 0))
801
plugin::TableNameIterator::TableNameIterator(const string &db)
802
: current_implementation(NULL), database(db)
804
engine_iter= all_engines.begin();
805
default_implementation= new DFETableNameIterator(database);
808
plugin::TableNameIterator::~TableNameIterator()
810
delete current_implementation;
811
if (current_implementation != default_implementation)
813
delete default_implementation;
817
int plugin::TableNameIterator::next(string *name)
822
if (current_implementation == NULL)
824
while(current_implementation == NULL &&
825
(engine_iter != all_engines.end()))
827
plugin::StorageEngine *engine= *engine_iter;
828
current_implementation= engine->tableNameIterator(database);
832
if (current_implementation == NULL &&
833
(engine_iter == all_engines.end()))
835
current_implementation= default_implementation;
839
err= current_implementation->next(name);
843
if (current_implementation != default_implementation)
845
delete current_implementation;
846
current_implementation= NULL;
600
bool StorageEngine::createTable(Session &session,
601
const identifier::Table &identifier,
602
message::Table& table_message)
604
drizzled::error_t error= EE_OK;
606
TableShare share(identifier);
607
table::Shell table(share);
608
message::Table tmp_proto;
610
if (share.parse_table_proto(session, table_message) || share.open_table_from_share(&session, identifier, "", 0, 0, table))
612
// @note Error occured, we should probably do a little more here.
613
// ER_CORRUPT_TABLE_DEFINITION,ER_CORRUPT_TABLE_DEFINITION_ENUM
615
my_error(ER_CORRUPT_TABLE_DEFINITION_UNKNOWN, identifier);
621
/* Check for legal operations against the Engine using the proto (if used) */
622
if (table_message.type() == message::Table::TEMPORARY &&
623
share.storage_engine->check_flag(HTON_BIT_TEMPORARY_NOT_SUPPORTED) == true)
625
error= HA_ERR_UNSUPPORTED;
627
else if (table_message.type() != message::Table::TEMPORARY &&
628
share.storage_engine->check_flag(HTON_BIT_TEMPORARY_ONLY) == true)
630
error= HA_ERR_UNSUPPORTED;
634
share.storage_engine->setTransactionReadWrite(session);
636
error= static_cast<drizzled::error_t>(share.storage_engine->doCreateTable(session,
642
if (error == ER_TABLE_PERMISSION_DENIED)
644
my_error(ER_TABLE_PERMISSION_DENIED, identifier);
649
identifier.getSQLPath(path);
650
my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), path.c_str(), error);
653
table.delete_table();
656
return(error == EE_OK);
659
Cursor *StorageEngine::getCursor(Table &arg)
664
class AddTableIdentifier :
665
public std::unary_function<StorageEngine *, void>
667
CachedDirectory &directory;
668
const identifier::Schema &identifier;
669
identifier::Table::vector &set_of_identifiers;
673
AddTableIdentifier(CachedDirectory &directory_arg, const identifier::Schema &identifier_arg, identifier::Table::vector &of_names) :
674
directory(directory_arg),
675
identifier(identifier_arg),
676
set_of_identifiers(of_names)
680
result_type operator() (argument_type engine)
682
engine->doGetTableIdentifiers(directory, identifier, set_of_identifiers);
687
void StorageEngine::getIdentifiers(Session &session, const identifier::Schema &schema_identifier, identifier::Table::vector &set_of_identifiers)
689
CachedDirectory directory(schema_identifier.getPath(), set_of_table_definition_ext);
691
if (schema_identifier == INFORMATION_SCHEMA_IDENTIFIER)
693
else if (schema_identifier == DATA_DICTIONARY_IDENTIFIER)
697
if (directory.fail())
699
errno= directory.getError();
703
schema_identifier.getSQLPath(path);
704
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), path.c_str());
708
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), directory.getPath(), errno);
715
std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
716
AddTableIdentifier(directory, schema_identifier, set_of_identifiers));
718
session.doGetTableIdentifiers(directory, schema_identifier, set_of_identifiers);
721
class DropTable: public std::unary_function<identifier::Table&, bool>
724
StorageEngine *engine;
728
DropTable(Session &session_arg, StorageEngine *engine_arg) :
729
session(session_arg),
733
result_type operator() (argument_type identifier)
735
return engine->doDropTable(session, identifier) == 0;
739
/* This will later be converted to identifier::Tables */
740
class DropTables: public std::unary_function<StorageEngine *, void>
743
identifier::Table::vector &table_identifiers;
747
DropTables(Session &session_arg, identifier::Table::vector &table_identifiers_arg) :
748
session(session_arg),
749
table_identifiers(table_identifiers_arg)
752
result_type operator() (argument_type engine)
754
// True returning from DropTable means the table has been successfully
755
// deleted, so it should be removed from the list of tables to drop
756
table_identifiers.erase(std::remove_if(table_identifiers.begin(),
757
table_identifiers.end(),
758
DropTable(session, engine)),
759
table_identifiers.end());
764
This only works for engines which use file based DFE.
766
Note-> Unlike MySQL, we do not, on purpose, delete files that do not match any engines.
768
void StorageEngine::removeLostTemporaryTables(Session &session, const char *directory)
770
CachedDirectory dir(directory, set_of_table_definition_ext);
771
identifier::Table::vector table_identifiers;
775
errno= dir.getError();
776
my_error(ER_CANT_READ_DIR, MYF(0), directory, errno);
781
CachedDirectory::Entries files= dir.getEntries();
783
for (CachedDirectory::Entries::iterator fileIter= files.begin();
784
fileIter != files.end(); fileIter++)
788
CachedDirectory::Entry *entry= *fileIter;
790
/* We remove the file extension. */
791
length= entry->filename.length();
792
entry->filename.resize(length - DEFAULT_DEFINITION_FILE_EXT.length());
796
path+= entry->filename;
797
message::Table definition;
798
if (StorageEngine::readTableFile(path, definition))
800
identifier::Table identifier(definition.schema(), definition.name(), path);
801
table_identifiers.push_back(identifier);
805
std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
806
DropTables(session, table_identifiers));
809
Now we just clean up anything that might left over.
811
We rescan because some of what might have been there should
812
now be all nice and cleaned up.
814
std::set<std::string> all_exts= set_of_table_definition_ext;
816
for (EngineVector::iterator iter= vector_of_engines.begin();
817
iter != vector_of_engines.end() ; iter++)
819
for (const char **ext= (*iter)->bas_ext(); *ext ; ext++)
820
all_exts.insert(*ext);
823
CachedDirectory rescan(directory, all_exts);
825
files= rescan.getEntries();
826
for (CachedDirectory::Entries::iterator fileIter= files.begin();
827
fileIter != files.end(); fileIter++)
830
CachedDirectory::Entry *entry= *fileIter;
834
path+= entry->filename;
836
unlink(path.c_str());
842
Print error that we got from Cursor function.
845
In case of delete table it's only safe to use the following parts of
846
the 'table' structure:
847
- table->getShare()->path
850
void StorageEngine::print_error(int error, myf errflag, const Table &table) const
852
drizzled::error_t textno= ER_GET_ERRNO;
855
textno=ER_OPEN_AS_READONLY;
861
textno=ER_FILE_NOT_FOUND;
863
case HA_ERR_KEY_NOT_FOUND:
864
case HA_ERR_NO_ACTIVE_RECORD:
865
case HA_ERR_END_OF_FILE:
866
textno=ER_KEY_NOT_FOUND;
868
case HA_ERR_WRONG_MRG_TABLE_DEF:
869
textno=ER_WRONG_MRG_TABLE;
871
case HA_ERR_FOUND_DUPP_KEY:
873
uint32_t key_nr= table.get_dup_key(error);
874
if ((int) key_nr >= 0)
876
const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
878
print_keydup_error(key_nr, err_msg, table);
885
case HA_ERR_FOREIGN_DUPLICATE_KEY:
887
uint32_t key_nr= table.get_dup_key(error);
888
if ((int) key_nr >= 0)
892
/* Write the key in the error message */
893
char key[MAX_KEY_LENGTH];
894
String str(key,sizeof(key),system_charset_info);
896
/* Table is opened and defined at this point */
897
key_unpack(&str, &table,(uint32_t) key_nr);
898
max_length= (DRIZZLE_ERRMSG_SIZE-
899
(uint32_t) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
900
if (str.length() >= max_length)
902
str.length(max_length-4);
903
str.append(STRING_WITH_LEN("..."));
905
my_error(ER_FOREIGN_DUPLICATE_KEY, MYF(0), table.getShare()->getTableName(),
906
str.c_ptr(), key_nr+1);
912
case HA_ERR_FOUND_DUPP_UNIQUE:
913
textno=ER_DUP_UNIQUE;
915
case HA_ERR_RECORD_CHANGED:
919
textno=ER_NOT_KEYFILE;
921
case HA_ERR_WRONG_IN_RECORD:
922
textno= ER_CRASHED_ON_USAGE;
924
case HA_ERR_CRASHED_ON_USAGE:
925
textno=ER_CRASHED_ON_USAGE;
927
case HA_ERR_NOT_A_TABLE:
928
textno= static_cast<drizzled::error_t>(error);
930
case HA_ERR_CRASHED_ON_REPAIR:
931
textno=ER_CRASHED_ON_REPAIR;
933
case HA_ERR_OUT_OF_MEM:
934
textno=ER_OUT_OF_RESOURCES;
936
case HA_ERR_WRONG_COMMAND:
937
textno=ER_ILLEGAL_HA;
939
case HA_ERR_OLD_FILE:
940
textno=ER_OLD_KEYFILE;
942
case HA_ERR_UNSUPPORTED:
943
textno=ER_UNSUPPORTED_EXTENSION;
945
case HA_ERR_RECORD_FILE_FULL:
946
case HA_ERR_INDEX_FILE_FULL:
947
textno=ER_RECORD_FILE_FULL;
949
case HA_ERR_LOCK_WAIT_TIMEOUT:
950
textno=ER_LOCK_WAIT_TIMEOUT;
952
case HA_ERR_LOCK_TABLE_FULL:
953
textno=ER_LOCK_TABLE_FULL;
955
case HA_ERR_LOCK_DEADLOCK:
956
textno=ER_LOCK_DEADLOCK;
958
case HA_ERR_READ_ONLY_TRANSACTION:
959
textno=ER_READ_ONLY_TRANSACTION;
961
case HA_ERR_CANNOT_ADD_FOREIGN:
962
textno=ER_CANNOT_ADD_FOREIGN;
964
case HA_ERR_ROW_IS_REFERENCED:
967
get_error_message(error, &str);
968
my_error(ER_ROW_IS_REFERENCED_2, MYF(0), str.c_ptr_safe());
971
case HA_ERR_NO_REFERENCED_ROW:
974
get_error_message(error, &str);
975
my_error(ER_NO_REFERENCED_ROW_2, MYF(0), str.c_ptr_safe());
978
case HA_ERR_TABLE_DEF_CHANGED:
979
textno=ER_TABLE_DEF_CHANGED;
981
case HA_ERR_NO_SUCH_TABLE:
983
identifier::Table identifier(table.getShare()->getSchemaName(), table.getShare()->getTableName());
984
my_error(ER_TABLE_UNKNOWN, identifier);
987
case HA_ERR_RBR_LOGGING_FAILED:
988
textno= ER_BINLOG_ROW_LOGGING_FAILED;
990
case HA_ERR_DROP_INDEX_FK:
992
const char *ptr= "???";
993
uint32_t key_nr= table.get_dup_key(error);
994
if ((int) key_nr >= 0)
995
ptr= table.key_info[key_nr].name;
996
my_error(ER_DROP_INDEX_FK, MYF(0), ptr);
999
case HA_ERR_TABLE_NEEDS_UPGRADE:
1000
textno=ER_TABLE_NEEDS_UPGRADE;
1002
case HA_ERR_TABLE_READONLY:
1003
textno= ER_OPEN_AS_READONLY;
1005
case HA_ERR_AUTOINC_READ_FAILED:
1006
textno= ER_AUTOINC_READ_FAILED;
1008
case HA_ERR_AUTOINC_ERANGE:
1009
textno= ER_WARN_DATA_OUT_OF_RANGE;
1011
case HA_ERR_LOCK_OR_ACTIVE_TRANSACTION:
1012
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
1013
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
1018
The error was "unknown" to this function.
1019
Ask Cursor if it has got a message for this error
1021
bool temporary= false;
1023
temporary= get_error_message(error, &str);
1024
if (!str.is_empty())
1026
const char* engine_name= getName().c_str();
1028
my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(),
1031
my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine_name);
1035
my_error(ER_GET_ERRNO,errflag,error);
1041
my_error(textno, errflag, table.getShare()->getTableName(), error);
1046
Return an error message specific to this Cursor.
1048
@param error error code previously returned by Cursor
1049
@param buf pointer to String where to add error message
1052
Returns true if this is a temporary error
1054
bool StorageEngine::get_error_message(int , String* ) const
1060
void StorageEngine::print_keydup_error(uint32_t key_nr, const char *msg, const Table &table) const
1062
/* Write the duplicated key in the error message */
1063
char key[MAX_KEY_LENGTH];
1064
String str(key,sizeof(key),system_charset_info);
1066
if (key_nr == MAX_KEY)
1068
/* Key is unknown */
1069
str.copy("", 0, system_charset_info);
1070
my_printf_error(ER_DUP_ENTRY, msg, MYF(0), str.c_ptr(), "*UNKNOWN*");
1074
/* Table is opened and defined at this point */
1075
key_unpack(&str, &table, (uint32_t) key_nr);
1076
uint32_t max_length=DRIZZLE_ERRMSG_SIZE-(uint32_t) strlen(msg);
1077
if (str.length() >= max_length)
1079
str.length(max_length-4);
1080
str.append(STRING_WITH_LEN("..."));
1082
my_printf_error(ER_DUP_ENTRY, msg,
1083
MYF(0), str.c_ptr(), table.key_info[key_nr].name);
1088
int StorageEngine::deleteDefinitionFromPath(const identifier::Table &identifier)
1090
std::string path(identifier.getPath());
1092
path.append(DEFAULT_DEFINITION_FILE_EXT);
1094
return internal::my_delete(path.c_str(), MYF(0));
1097
int StorageEngine::renameDefinitionFromPath(const identifier::Table &dest, const identifier::Table &src)
1099
message::Table table_message;
1100
std::string src_path(src.getPath());
1101
std::string dest_path(dest.getPath());
1103
src_path.append(DEFAULT_DEFINITION_FILE_EXT);
1104
dest_path.append(DEFAULT_DEFINITION_FILE_EXT);
1106
bool was_read= StorageEngine::readTableFile(src_path.c_str(), table_message);
1113
dest.copyToTableMessage(table_message);
1115
int error= StorageEngine::writeDefinitionFromPath(dest, table_message);
1119
if (unlink(src_path.c_str()))
1120
perror(src_path.c_str());
1126
int StorageEngine::writeDefinitionFromPath(const identifier::Table &identifier, message::Table &table_message)
1128
char definition_file_tmp[FN_REFLEN];
1129
std::string file_name(identifier.getPath());
1131
file_name.append(DEFAULT_DEFINITION_FILE_EXT);
1133
snprintf(definition_file_tmp, sizeof(definition_file_tmp), "%sXXXXXX", file_name.c_str());
1135
int fd= mkstemp(definition_file_tmp);
1139
perror(definition_file_tmp);
1143
google::protobuf::io::ZeroCopyOutputStream* output=
1144
new google::protobuf::io::FileOutputStream(fd);
1150
success= table_message.SerializeToZeroCopyStream(output);
1159
std::string error_message;
1160
identifier.getSQLPath(error_message);
1162
my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
1163
error_message.c_str(),
1164
table_message.InitializationErrorString().c_str());
1167
if (close(fd) == -1)
1168
perror(definition_file_tmp);
1170
if (unlink(definition_file_tmp) == -1)
1171
perror(definition_file_tmp);
1173
return ER_CORRUPT_TABLE_DEFINITION;
1178
if (close(fd) == -1)
1181
perror(definition_file_tmp);
1183
if (unlink(definition_file_tmp))
1184
perror(definition_file_tmp);
1189
if (rename(definition_file_tmp, file_name.c_str()) == -1)
1192
perror(definition_file_tmp);
1194
if (unlink(definition_file_tmp))
1195
perror(definition_file_tmp);
1203
class CanCreateTable: public std::unary_function<StorageEngine *, bool>
1205
const identifier::Table &identifier;
1208
CanCreateTable(const identifier::Table &identifier_arg) :
1209
identifier(identifier_arg)
1212
result_type operator() (argument_type engine)
1214
return not engine->doCanCreateTable(identifier);
1220
@note on success table can be created.
1222
bool StorageEngine::canCreateTable(const identifier::Table &identifier)
1224
EngineVector::iterator iter=
1225
std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
1226
CanCreateTable(identifier));
1228
if (iter == vector_of_engines.end())
1236
bool StorageEngine::readTableFile(const std::string &path, message::Table &table_message)
1238
std::fstream input(path.c_str(), std::ios::in | std::ios::binary);
1243
if (table_message.ParseFromIstream(&input))
1250
my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
1251
table_message.name().empty() ? path.c_str() : table_message.name().c_str(),
1252
table_message.InitializationErrorString().empty() ? "": table_message.InitializationErrorString().c_str());
1257
perror(path.c_str());
1263
std::ostream& operator<<(std::ostream& output, const StorageEngine &engine)
1265
output << "StorageEngine:(";
1266
output << engine.getName();
1272
} /* namespace plugin */
863
int plugin::StorageEngine::createTable(Session *session, const char *path,
864
const char *db, const char *table_name,
865
HA_CREATE_INFO *create_info,
866
bool update_create_info,
867
drizzled::message::Table *table_proto)
871
TableShare share(db, 0, table_name, path);
872
drizzled::message::Table tmp_proto;
876
if (parse_table_proto(session, *table_proto, &share))
881
table_proto= &tmp_proto;
882
if (open_table_def(session, &share))
886
if (open_table_from_share(session, &share, "", 0, (uint32_t) READ_ALL, 0,
890
if (update_create_info)
891
table.updateCreateInfo(create_info, table_proto);
893
error= share.storage_engine->createTable(session, path, &table,
894
create_info, table_proto);
895
table.closefrm(false);
898
char name_buff[FN_REFLEN];
899
sprintf(name_buff,"%s.%s",db,table_name);
900
my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name_buff, error);
903
share.free_table_share();
1273
909
} /* namespace drizzled */
913
handler *get_new_handler(TableShare *share, MEM_ROOT *alloc,
914
drizzled::plugin::StorageEngine *engine)
918
if (engine && engine->is_enabled())
920
if ((file= engine->create(share, alloc)))
925
Try the default table type
926
Here the call to current_session() is ok as we call this function a lot of
927
times but we enter this branch very seldom.
929
return(get_new_handler(share, alloc, ha_default_storage_engine(current_session)));
934
Return the default storage engine plugin::StorageEngine for thread
936
@param ha_default_storage_engine(session)
937
@param session current thread
940
pointer to plugin::StorageEngine
942
drizzled::plugin::StorageEngine *ha_default_storage_engine(Session *session)
944
if (session->variables.storage_engine)
945
return session->variables.storage_engine;
946
return global_system_variables.storage_engine;