17
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32
#include <google/protobuf/io/zero_copy_stream.h>
33
#include <google/protobuf/io/zero_copy_stream_impl.h>
35
#include "drizzled/cached_directory.h"
20
#include <drizzled/server_includes.h>
37
21
#include <drizzled/definitions.h>
38
22
#include <drizzled/base.h>
39
#include <drizzled/cursor.h>
23
#include <drizzled/handler.h>
40
24
#include <drizzled/plugin/storage_engine.h>
41
25
#include <drizzled/session.h>
42
26
#include <drizzled/error.h>
43
27
#include <drizzled/gettext.h>
28
#include <drizzled/registry.h>
44
29
#include <drizzled/unireg.h>
45
30
#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"
52
#include "drizzled/db.h"
31
#include <drizzled/plugin_registry.h>
54
34
#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;
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);
85
57
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 */
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;
93
77
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 TableIdentifier &from, const TableIdentifier &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)))
118
error= ER_EVENT_OBSERVER_PLUGIN;
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
// if (table_share == NULL || table_share->tmp_table == NO_TMP_TABLE)
100
ha_info->set_trx_read_write();
106
Return the default storage engine StorageEngine for thread
108
@param ha_default_storage_engine(session)
109
@param session current thread
112
pointer to StorageEngine
114
StorageEngine *ha_default_storage_engine(Session *session)
116
if (session->variables.storage_engine)
117
return session->variables.storage_engine;
118
return global_system_variables.storage_engine;
123
Return the storage engine StorageEngine for the supplied name
125
@param session current thread
126
@param name name of storage engine
129
pointer to storage engine plugin handle
131
StorageEngine *ha_resolve_by_name(Session *session, std::string find_str)
133
transform(find_str.begin(), find_str.end(),
134
find_str.begin(), ::tolower);
135
string default_str("default");
136
if (find_str == default_str)
137
return ha_default_storage_engine(session);
139
StorageEngine *engine= all_engines.find(find_str);
141
if (engine && engine->is_user_selectable())
148
handler *get_new_handler(TableShare *share, MEM_ROOT *alloc,
149
StorageEngine *engine)
153
if (engine && engine->is_enabled())
155
if ((file= engine->create(share, alloc)))
160
Try the default table type
161
Here the call to current_session() is ok as we call this function a lot of
162
times but we enter this branch very seldom.
164
return(get_new_handler(share, alloc, ha_default_storage_engine(current_session)));
167
class StorageEngineCloseConnection
168
: public unary_function<StorageEngine *, void>
172
StorageEngineCloseConnection(Session *session_arg) : session(session_arg) {}
174
there's no need to rollback here as all transactions must
175
be rolled back already
177
inline result_type operator() (argument_type engine)
179
if (engine->is_enabled() &&
180
session_get_ha_data(session, engine))
181
engine->close_connection(session);
187
don't bother to rollback here, it's done already
189
void ha_close_connection(Session* session)
191
for_each(all_engines.begin(), all_engines.end(),
192
StorageEngineCloseConnection(session));
195
void ha_drop_database(char* path)
197
for_each(all_engines.begin(), all_engines.end(),
198
bind2nd(mem_fun(&StorageEngine::drop_database),path));
201
int ha_commit_or_rollback_by_xid(XID *xid, bool commit)
206
transform(all_engines.begin(), all_engines.end(), results.begin(),
207
bind2nd(mem_fun(&StorageEngine::commit_by_xid),xid));
209
transform(all_engines.begin(), all_engines.end(), results.begin(),
210
bind2nd(mem_fun(&StorageEngine::rollback_by_xid),xid));
212
if (find_if(results.begin(), results.end(), bind2nd(equal_to<int>(),0))
221
This function should be called when MySQL sends rows of a SELECT result set
222
or the EOF mark to the client. It releases a possible adaptive hash index
223
S-latch held by session in InnoDB and also releases a possible InnoDB query
224
FIFO ticket to enter InnoDB. To save CPU time, InnoDB allows a session to
225
keep them over several calls of the InnoDB handler interface when a join
226
is executed. But when we let the control to pass to the client they have
227
to be released because if the application program uses mysql_use_result(),
228
it may deadlock on the S-latch if the application on another connection
229
performs another SQL query. In MySQL-4.1 this is even more important because
230
there a connection can have several SELECT queries open at the same time.
232
@param session the thread handle of the current connection
237
int ha_release_temporary_latches(Session *session)
239
for_each(all_engines.begin(), all_engines.end(),
240
bind2nd(mem_fun(&StorageEngine::release_temporary_latches),session));
245
bool ha_flush_logs(StorageEngine *engine)
249
if (find_if(all_engines.begin(), all_engines.end(),
250
mem_fun(&StorageEngine::flush_logs))
251
!= all_engines.begin())
256
if ((!engine->is_enabled()) ||
257
(engine->flush_logs()))
264
recover() step of xa.
267
there are three modes of operation:
268
- automatic recover after a crash
269
in this case commit_list != 0, tc_heuristic_recover==0
270
all xids from commit_list are committed, others are rolled back
271
- manual (heuristic) recover
272
in this case commit_list==0, tc_heuristic_recover != 0
273
DBA has explicitly specified that all prepared transactions should
274
be committed (or rolled back).
275
- no recovery (MySQL did not detect a crash)
276
in this case commit_list==0, tc_heuristic_recover == 0
277
there should be no prepared transactions in this case.
279
class XARecover : unary_function<StorageEngine *, void>
281
int trans_len, found_foreign_xids, found_my_xids;
287
XARecover(XID *trans_list_arg, int trans_len_arg,
288
HASH *commit_list_arg, bool dry_run_arg)
289
: trans_len(trans_len_arg), found_foreign_xids(0), found_my_xids(0),
291
trans_list(trans_list_arg), commit_list(commit_list_arg),
297
return found_foreign_xids;
302
return found_my_xids;
305
result_type operator() (argument_type engine)
310
if (engine->is_enabled())
312
while ((got= engine->recover(trans_list, trans_len)) > 0 )
314
errmsg_printf(ERRMSG_LVL_INFO,
315
_("Found %d prepared transaction(s) in %s"),
316
got, engine->getName().c_str());
317
for (int i=0; i < got; i ++)
319
my_xid x=trans_list[i].get_my_xid();
320
if (!x) // not "mine" - that is generated by external TM
322
xid_cache_insert(trans_list+i, XA_PREPARED);
323
found_foreign_xids++;
333
hash_search(commit_list, (unsigned char *)&x, sizeof(x)) != 0 :
334
tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
336
engine->commit_by_xid(trans_list+i);
340
engine->rollback_by_xid(trans_list+i);
351
int ha_recover(HASH *commit_list)
353
XID *trans_list= NULL;
356
bool dry_run= (commit_list==0 && tc_heuristic_recover==0);
358
/* commit_list and tc_heuristic_recover cannot be set both */
359
assert(commit_list==0 || tc_heuristic_recover==0);
361
/* if either is set, total_ha_2pc must be set too */
362
if (total_ha_2pc <= 1)
366
#ifndef WILL_BE_DELETED_LATER
369
for now, only InnoDB supports 2pc. It means we can always safely
370
rollback all pending transactions, without risking inconsistent data
373
assert(total_ha_2pc == 2); // only InnoDB and binlog
374
tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
377
for (trans_len= MAX_XID_LIST_SIZE ;
378
trans_list==0 && trans_len > MIN_XID_LIST_SIZE; trans_len/=2)
380
trans_list=(XID *)malloc(trans_len*sizeof(XID));
384
errmsg_printf(ERRMSG_LVL_ERROR, ER(ER_OUTOFMEMORY), trans_len*sizeof(XID));
389
errmsg_printf(ERRMSG_LVL_INFO, _("Starting crash recovery..."));
392
XARecover recover_func(trans_list, trans_len, commit_list, dry_run);
393
for_each(all_engines.begin(), all_engines.end(), recover_func);
396
if (recover_func.getForeignXIDs())
397
errmsg_printf(ERRMSG_LVL_WARN,
398
_("Found %d prepared XA transactions"),
399
recover_func.getForeignXIDs());
400
if (dry_run && recover_func.getMyXIDs())
402
errmsg_printf(ERRMSG_LVL_ERROR,
403
_("Found %d prepared transactions! It means that drizzled "
404
"was not shut down properly last time and critical "
405
"recovery information (last binlog or %s file) was "
406
"manually deleted after a crash. You have to start "
407
"drizzled with the --tc-heuristic-recover switch to "
408
"commit or rollback pending transactions."),
409
recover_func.getMyXIDs(), opt_tc_log_file);
413
errmsg_printf(ERRMSG_LVL_INFO, _("Crash recovery finished."));
417
int ha_start_consistent_snapshot(Session *session)
419
for_each(all_engines.begin(), all_engines.end(),
420
bind2nd(mem_fun(&StorageEngine::start_consistent_snapshot),session));
424
static int drizzle_read_table_proto(const char* path, drizzled::message::Table* table)
426
int fd= open(path, O_RDONLY);
431
google::protobuf::io::ZeroCopyInputStream* input=
432
new google::protobuf::io::FileInputStream(fd);
434
if (table->ParseFromZeroCopyStream(input) == false)
446
class StorageEngineGetTableProto: public unary_function<StorageEngine *,bool>
449
drizzled::message::Table *table_proto;
452
StorageEngineGetTableProto(const char* path_arg,
453
drizzled::message::Table *table_proto_arg,
455
:path(path_arg), table_proto(table_proto_arg), err(err_arg) {}
457
result_type operator() (argument_type engine)
459
int ret= engine->getTableProtoImplementation(path, table_proto);
464
return *err == EEXIST;
469
Call this function in order to give the handler the possiblity
470
to ask engine if there are any new tables that should be written to disk
471
or any dropped tables that need to be removed from disk
473
int StorageEngine::getTableProto(const char* path,
474
drizzled::message::Table *table_proto)
478
drizzled::Registry<StorageEngine *>::iterator iter=
479
find_if(all_engines.begin(), all_engines.end(),
480
StorageEngineGetTableProto(path, table_proto, &err));
481
if (iter == all_engines.end())
483
string proto_path(path);
484
string file_ext(".dfe");
485
proto_path.append(file_ext);
487
int error= access(proto_path.c_str(), F_OK);
496
int read_proto_err= drizzle_read_table_proto(proto_path.c_str(),
508
int StorageEngine::renameTableImplementation(Session *, const char *from, const char *to)
511
for (const char **ext= bas_ext(); *ext ; ext++)
513
if (rename_file_ext(from, to, *ext))
515
if ((error=my_errno) != ENOENT)
126
525
Delete all files with extension from bas_ext().
128
527
@param name Base name of table
131
We assume that the Cursor may return more extensions than
530
We assume that the handler may return more extensions than
132
531
was actually used for the file.
140
int StorageEngine::doDropTable(Session&, const TableIdentifier &identifier)
539
int StorageEngine::deleteTableImplementation(Session *, const std::string table_path)
144
542
int enoent_or_zero= ENOENT; // Error if no file was deleted
145
543
char buff[FN_REFLEN];
147
for (const char **ext= bas_ext(); *ext ; ext++)
545
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)))
547
fn_format(buff, table_path.c_str(), "", *ext,
548
MY_UNPACK_FILENAME|MY_APPEND_EXT);
549
if (my_delete_with_symlink(buff, MYF(0)))
153
if ((error= errno) != ENOENT)
551
if ((error= my_errno) != ENOENT)
158
555
enoent_or_zero= 0; // No error for ENOENT
161
556
error= enoent_or_zero;
166
bool StorageEngine::addPlugin(StorageEngine *engine)
169
vector_of_engines.push_back(engine);
171
if (engine->getTableDefinitionFileExtension().length())
173
assert(engine->getTableDefinitionFileExtension().length() == DEFAULT_DEFINITION_FILE_EXT.length());
174
set_of_table_definition_ext.insert(engine->getTableDefinitionFileExtension());
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>
248
StorageEngineCloseConnection(Session *session_arg) : session(session_arg) {}
250
there's no need to rollback here as all transactions must
251
be rolled back already
253
inline result_type operator() (argument_type engine)
255
if (*session->getEngineData(engine))
256
engine->close_connection(session);
262
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)
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())
281
if (engine->flush_logs())
287
class StorageEngineGetTableDefinition: public std::unary_function<StorageEngine *,bool>
290
const TableIdentifier &identifier;
291
message::Table &table_message;
295
StorageEngineGetTableDefinition(Session& session_arg,
296
const TableIdentifier &identifier_arg,
297
message::Table &table_message_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);
311
return err == EEXIST || err != ENOENT;
315
class StorageEngineDoesTableExist: public std::unary_function<StorageEngine *, bool>
318
const TableIdentifier &identifier;
321
StorageEngineDoesTableExist(Session& session_arg, const TableIdentifier &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 TableIdentifier &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::TableIdentifier&)
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
366
to ask engine if there are any new tables that should be written to disk
367
or any dropped tables that need to be removed from disk
369
int StorageEngine::getTableDefinition(Session& session,
370
const TableIdentifier &identifier,
371
message::table::shared_ptr &table_message,
372
bool include_temporary_tables)
376
if (include_temporary_tables)
378
Table *table= session.find_temporary_table(identifier);
381
table_message.reset(new message::Table(*table->getShare()->getTableProto()));
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);
409
562
An interceptor to hijack the text of the error message without
410
563
setting an error in the thread. We need the text to present it
592
class DeleteTableStorageEngine
593
: public unary_function<StorageEngine *, void>
600
DeleteTableStorageEngine(Session *session_arg, const char *path_arg,
601
handler **file_arg, int *error_arg)
602
: session(session_arg), path(path_arg), file(file_arg), dt_error(error_arg) {}
604
result_type operator() (argument_type engine)
606
char tmp_path[FN_REFLEN];
609
if(*dt_error!=ENOENT) /* already deleted table */
615
if (!engine->is_enabled())
618
if ((tmp_file= engine->create(NULL, session->mem_root)))
623
path= engine->checkLowercaseNames(path, tmp_path);
624
const std::string table_path(path);
625
int tmp_error= engine->deleteTable(session, table_path);
627
if (tmp_error != ENOENT)
631
if (engine->check_flag(HTON_BIT_HAS_DATA_DICTIONARY))
632
delete_table_proto_file(path);
634
tmp_error= delete_table_proto_file(path);
637
*dt_error= tmp_error;
439
returns ENOENT if the file doesn't exists.
651
This should return ENOENT if the file doesn't exists.
652
The .frm file will be deleted only if we return 0 or ENOENT
441
int StorageEngine::dropTable(Session& session,
442
const TableIdentifier &identifier)
446
message::table::shared_ptr src_proto;
447
StorageEngine *engine;
449
error_proto= StorageEngine::getTableDefinition(session, identifier, src_proto);
451
if (error_proto == ER_CORRUPT_TABLE_DEFINITION)
453
std::string error_message;
454
identifier.getSQLPath(error_message);
456
error_message.append(" : ");
457
error_message.append(src_proto->InitializationErrorString());
459
my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0), error_message.c_str());
461
return ER_CORRUPT_TABLE_DEFINITION;
465
engine= StorageEngine::findByName(session, src_proto->engine().name());
467
engine= StorageEngine::findByName(session, "");
471
std::string error_message;
472
identifier.getSQLPath(error_message);
473
my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0), error_message.c_str());
475
return ER_CORRUPT_TABLE_DEFINITION;
478
error= StorageEngine::dropTable(session, *engine, identifier);
480
if (error_proto && error == 0)
486
int StorageEngine::dropTable(Session& session,
487
StorageEngine &engine,
488
const TableIdentifier &identifier)
492
engine.setTransactionReadWrite(session);
494
if (unlikely(plugin::EventObserver::beforeDropTable(session, identifier)))
496
error= ER_EVENT_OBSERVER_PLUGIN;
500
error= engine.doDropTable(session, identifier);
501
if (unlikely(plugin::EventObserver::afterDropTable(session, identifier, error)))
654
int ha_delete_table(Session *session, const char *path,
655
const char *db, const char *alias, bool generate_warning)
657
TableShare dummy_share;
659
memset(&dummy_table, 0, sizeof(dummy_table));
660
memset(&dummy_share, 0, sizeof(dummy_share));
662
dummy_table.s= &dummy_share;
667
for_each(all_engines.begin(), all_engines.end(),
668
DeleteTableStorageEngine(session, path, &file, &error));
670
if (error == ENOENT) /* proto may be left behind */
671
error= delete_table_proto_file(path);
673
if (error && generate_warning)
676
Because file->print_error() use my_error() to generate the error message
677
we use an internal error handler to intercept it and store the text
678
in a temporary buffer. Later the message will be presented to user
681
Ha_delete_table_error_handler ha_delete_table_error_handler;
683
/* Fill up strucutures that print_error may need */
684
dummy_share.path.str= (char*) path;
685
dummy_share.path.length= strlen(path);
686
dummy_share.db.str= (char*) db;
687
dummy_share.db.length= strlen(db);
688
dummy_share.table_name.str= (char*) alias;
689
dummy_share.table_name.length= strlen(alias);
690
dummy_table.alias= alias;
503
error= ER_EVENT_OBSERVER_PLUGIN;
695
file->change_table_ptr(&dummy_table, &dummy_share);
697
session->push_internal_handler(&ha_delete_table_error_handler);
698
file->print_error(error, 0);
700
session->pop_internal_handler();
703
error= -1; /* General form of fail. maybe bad FRM */
706
XXX: should we convert *all* errors to warnings here?
707
What if the error is fatal?
709
push_warning(session, DRIZZLE_ERROR::WARN_LEVEL_ERROR, error,
710
ha_delete_table_error_handler.buff);
507
drizzled::message::Cache::singleton().erase(identifier);
514
720
Initiates table-file and calls appropriate database-creator.
521
int StorageEngine::createTable(Session &session,
522
const TableIdentifier &identifier,
523
message::Table& table_message)
727
int ha_create_table(Session *session, const char *path,
728
const char *db, const char *table_name,
729
HA_CREATE_INFO *create_info,
730
bool update_create_info,
731
drizzled::message::Table *table_proto)
526
TableShare share(identifier);
527
table::Shell table(share);
528
message::Table tmp_proto;
735
TableShare share(db, 0, table_name, path);
530
if (share.parse_table_proto(session, table_message) || share.open_table_from_share(&session, identifier, "", 0, 0, table))
532
// @note Error occured, we should probably do a little more here.
739
if (parse_table_proto(session, *table_proto, &share))
536
/* Check for legal operations against the Engine using the proto (if used) */
537
if (table_message.type() == message::Table::TEMPORARY &&
538
share.storage_engine->check_flag(HTON_BIT_TEMPORARY_NOT_SUPPORTED) == true)
540
error= HA_ERR_UNSUPPORTED;
542
else if (table_message.type() != message::Table::TEMPORARY &&
543
share.storage_engine->check_flag(HTON_BIT_TEMPORARY_ONLY) == true)
545
error= HA_ERR_UNSUPPORTED;
549
share.storage_engine->setTransactionReadWrite(session);
551
error= share.storage_engine->doCreateTable(session,
560
identifier.getSQLPath(path);
561
my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), path.c_str(), error);
564
table.delete_table();
744
if (open_table_def(session, &share))
748
if (open_table_from_share(session, &share, "", 0, (uint32_t) READ_ALL, 0,
752
if (update_create_info)
753
table.updateCreateInfo(create_info);
755
error= share.storage_engine->createTable(session, path, &table,
756
create_info, table_proto);
757
table.closefrm(false);
760
char name_buff[FN_REFLEN];
761
sprintf(name_buff,"%s.%s",db,table_name);
762
my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name_buff, error);
765
share.free_table_share();
567
766
return(error != 0);
570
Cursor *StorageEngine::getCursor(Table &arg)
575
class AddTableIdentifier :
576
public std::unary_function<StorageEngine *, void>
578
CachedDirectory &directory;
579
const SchemaIdentifier &identifier;
580
TableIdentifier::vector &set_of_identifiers;
584
AddTableIdentifier(CachedDirectory &directory_arg, const SchemaIdentifier &identifier_arg, TableIdentifier::vector &of_names) :
585
directory(directory_arg),
586
identifier(identifier_arg),
587
set_of_identifiers(of_names)
591
result_type operator() (argument_type engine)
593
engine->doGetTableIdentifiers(directory, identifier, set_of_identifiers);
598
void StorageEngine::getIdentifiers(Session &session, const SchemaIdentifier &schema_identifier, TableIdentifier::vector &set_of_identifiers)
600
static SchemaIdentifier INFORMATION_SCHEMA_IDENTIFIER("information_schema");
601
static SchemaIdentifier DATA_DICTIONARY_IDENTIFIER("data_dictionary");
603
CachedDirectory directory(schema_identifier.getPath(), set_of_table_definition_ext);
605
if (schema_identifier == INFORMATION_SCHEMA_IDENTIFIER)
607
else if (schema_identifier == DATA_DICTIONARY_IDENTIFIER)
611
if (directory.fail())
613
errno= directory.getError();
617
schema_identifier.getSQLPath(path);
618
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), path.c_str());
622
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), directory.getPath(), errno);
629
std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
630
AddTableIdentifier(directory, schema_identifier, set_of_identifiers));
632
session.doGetTableIdentifiers(directory, schema_identifier, set_of_identifiers);
635
class DropTable: public std::unary_function<TableIdentifier&, bool>
638
StorageEngine *engine;
642
DropTable(Session &session_arg, StorageEngine *engine_arg) :
643
session(session_arg),
647
result_type operator() (argument_type identifier)
649
return engine->doDropTable(session, identifier) == 0;
653
/* This will later be converted to TableIdentifiers */
654
class DropTables: public std::unary_function<StorageEngine *, void>
657
TableIdentifier::vector &table_identifiers;
661
DropTables(Session &session_arg, TableIdentifier::vector &table_identifiers_arg) :
662
session(session_arg),
663
table_identifiers(table_identifiers_arg)
666
result_type operator() (argument_type engine)
668
// True returning from DropTable means the table has been successfully
669
// deleted, so it should be removed from the list of tables to drop
670
table_identifiers.erase(std::remove_if(table_identifiers.begin(),
671
table_identifiers.end(),
672
DropTable(session, engine)),
673
table_identifiers.end());
678
This only works for engines which use file based DFE.
680
Note-> Unlike MySQL, we do not, on purpose, delete files that do not match any engines.
682
void StorageEngine::removeLostTemporaryTables(Session &session, const char *directory)
684
CachedDirectory dir(directory, set_of_table_definition_ext);
685
TableIdentifier::vector table_identifiers;
689
errno= dir.getError();
690
my_error(ER_CANT_READ_DIR, MYF(0), directory, errno);
695
CachedDirectory::Entries files= dir.getEntries();
697
for (CachedDirectory::Entries::iterator fileIter= files.begin();
698
fileIter != files.end(); fileIter++)
702
CachedDirectory::Entry *entry= *fileIter;
704
/* We remove the file extension. */
705
length= entry->filename.length();
706
entry->filename.resize(length - DEFAULT_DEFINITION_FILE_EXT.length());
710
path+= entry->filename;
711
message::Table definition;
712
if (StorageEngine::readTableFile(path, definition))
714
TableIdentifier identifier(definition.schema(), definition.name(), path);
715
table_identifiers.push_back(identifier);
719
std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
720
DropTables(session, table_identifiers));
770
const string ha_resolve_storage_engine_name(const StorageEngine *engine)
772
return engine == NULL ? string("UNKNOWN") : engine->getName();
775
const char *StorageEngine::checkLowercaseNames(const char *path, char *tmp_path)
777
if (flags.test(HTON_BIT_FILE_BASED))
780
/* Ensure that table handler get path in lower case */
781
if (tmp_path != path)
782
strcpy(tmp_path, path);
723
Now we just clean up anything that might left over.
725
We rescan because some of what might have been there should
726
now be all nice and cleaned up.
785
we only should turn into lowercase database/table part
786
so start the process after homedirectory
728
std::set<std::string> all_exts= set_of_table_definition_ext;
730
for (EngineVector::iterator iter= vector_of_engines.begin();
731
iter != vector_of_engines.end() ; iter++)
733
for (const char **ext= (*iter)->bas_ext(); *ext ; ext++)
734
all_exts.insert(*ext);
737
CachedDirectory rescan(directory, all_exts);
739
files= rescan.getEntries();
740
for (CachedDirectory::Entries::iterator fileIter= files.begin();
741
fileIter != files.end(); fileIter++)
744
CachedDirectory::Entry *entry= *fileIter;
748
path+= entry->filename;
750
unlink(path.c_str());
756
Print error that we got from Cursor function.
759
In case of delete table it's only safe to use the following parts of
760
the 'table' structure:
761
- table->getShare()->path
764
void StorageEngine::print_error(int error, myf errflag, Table &table)
766
print_error(error, errflag, &table);
769
void StorageEngine::print_error(int error, myf errflag, Table *table)
771
int textno= ER_GET_ERRNO;
774
textno=ER_OPEN_AS_READONLY;
780
textno=ER_FILE_NOT_FOUND;
782
case HA_ERR_KEY_NOT_FOUND:
783
case HA_ERR_NO_ACTIVE_RECORD:
784
case HA_ERR_END_OF_FILE:
785
textno=ER_KEY_NOT_FOUND;
787
case HA_ERR_WRONG_MRG_TABLE_DEF:
788
textno=ER_WRONG_MRG_TABLE;
790
case HA_ERR_FOUND_DUPP_KEY:
793
uint32_t key_nr= table->get_dup_key(error);
794
if ((int) key_nr >= 0)
796
const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
798
print_keydup_error(key_nr, err_msg, *table);
805
case HA_ERR_FOREIGN_DUPLICATE_KEY:
808
uint32_t key_nr= table->get_dup_key(error);
809
if ((int) key_nr >= 0)
813
/* Write the key in the error message */
814
char key[MAX_KEY_LENGTH];
815
String str(key,sizeof(key),system_charset_info);
817
/* Table is opened and defined at this point */
818
key_unpack(&str,table,(uint32_t) key_nr);
819
max_length= (DRIZZLE_ERRMSG_SIZE-
820
(uint32_t) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
821
if (str.length() >= max_length)
823
str.length(max_length-4);
824
str.append(STRING_WITH_LEN("..."));
826
my_error(ER_FOREIGN_DUPLICATE_KEY, MYF(0), table->getShare()->getTableName(),
827
str.c_ptr(), key_nr+1);
833
case HA_ERR_FOUND_DUPP_UNIQUE:
834
textno=ER_DUP_UNIQUE;
836
case HA_ERR_RECORD_CHANGED:
840
textno=ER_NOT_KEYFILE;
842
case HA_ERR_WRONG_IN_RECORD:
843
textno= ER_CRASHED_ON_USAGE;
845
case HA_ERR_CRASHED_ON_USAGE:
846
textno=ER_CRASHED_ON_USAGE;
848
case HA_ERR_NOT_A_TABLE:
851
case HA_ERR_CRASHED_ON_REPAIR:
852
textno=ER_CRASHED_ON_REPAIR;
854
case HA_ERR_OUT_OF_MEM:
855
textno=ER_OUT_OF_RESOURCES;
857
case HA_ERR_WRONG_COMMAND:
858
textno=ER_ILLEGAL_HA;
860
case HA_ERR_OLD_FILE:
861
textno=ER_OLD_KEYFILE;
863
case HA_ERR_UNSUPPORTED:
864
textno=ER_UNSUPPORTED_EXTENSION;
866
case HA_ERR_RECORD_FILE_FULL:
867
case HA_ERR_INDEX_FILE_FULL:
868
textno=ER_RECORD_FILE_FULL;
870
case HA_ERR_LOCK_WAIT_TIMEOUT:
871
textno=ER_LOCK_WAIT_TIMEOUT;
873
case HA_ERR_LOCK_TABLE_FULL:
874
textno=ER_LOCK_TABLE_FULL;
876
case HA_ERR_LOCK_DEADLOCK:
877
textno=ER_LOCK_DEADLOCK;
879
case HA_ERR_READ_ONLY_TRANSACTION:
880
textno=ER_READ_ONLY_TRANSACTION;
882
case HA_ERR_CANNOT_ADD_FOREIGN:
883
textno=ER_CANNOT_ADD_FOREIGN;
885
case HA_ERR_ROW_IS_REFERENCED:
888
get_error_message(error, &str);
889
my_error(ER_ROW_IS_REFERENCED_2, MYF(0), str.c_ptr_safe());
892
case HA_ERR_NO_REFERENCED_ROW:
895
get_error_message(error, &str);
896
my_error(ER_NO_REFERENCED_ROW_2, MYF(0), str.c_ptr_safe());
899
case HA_ERR_TABLE_DEF_CHANGED:
900
textno=ER_TABLE_DEF_CHANGED;
902
case HA_ERR_NO_SUCH_TABLE:
904
my_error(ER_NO_SUCH_TABLE, MYF(0), table->getShare()->getSchemaName(),
905
table->getShare()->getTableName());
907
case HA_ERR_RBR_LOGGING_FAILED:
908
textno= ER_BINLOG_ROW_LOGGING_FAILED;
910
case HA_ERR_DROP_INDEX_FK:
913
const char *ptr= "???";
914
uint32_t key_nr= table->get_dup_key(error);
915
if ((int) key_nr >= 0)
916
ptr= table->key_info[key_nr].name;
917
my_error(ER_DROP_INDEX_FK, MYF(0), ptr);
920
case HA_ERR_TABLE_NEEDS_UPGRADE:
921
textno=ER_TABLE_NEEDS_UPGRADE;
923
case HA_ERR_TABLE_READONLY:
924
textno= ER_OPEN_AS_READONLY;
926
case HA_ERR_AUTOINC_READ_FAILED:
927
textno= ER_AUTOINC_READ_FAILED;
929
case HA_ERR_AUTOINC_ERANGE:
930
textno= ER_WARN_DATA_OUT_OF_RANGE;
932
case HA_ERR_LOCK_OR_ACTIVE_TRANSACTION:
933
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
934
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
939
The error was "unknown" to this function.
940
Ask Cursor if it has got a message for this error
942
bool temporary= false;
944
temporary= get_error_message(error, &str);
947
const char* engine_name= getName().c_str();
949
my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(),
952
my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine_name);
956
my_error(ER_GET_ERRNO,errflag,error);
961
my_error(textno, errflag, table->getShare()->getTableName(), error);
966
Return an error message specific to this Cursor.
968
@param error error code previously returned by Cursor
969
@param buf pointer to String where to add error message
972
Returns true if this is a temporary error
974
bool StorageEngine::get_error_message(int , String* )
980
void StorageEngine::print_keydup_error(uint32_t key_nr, const char *msg, Table &table)
982
/* Write the duplicated key in the error message */
983
char key[MAX_KEY_LENGTH];
984
String str(key,sizeof(key),system_charset_info);
986
if (key_nr == MAX_KEY)
989
str.copy("", 0, system_charset_info);
990
my_printf_error(ER_DUP_ENTRY, msg, MYF(0), str.c_ptr(), "*UNKNOWN*");
994
/* Table is opened and defined at this point */
995
key_unpack(&str, &table, (uint32_t) key_nr);
996
uint32_t max_length=DRIZZLE_ERRMSG_SIZE-(uint32_t) strlen(msg);
997
if (str.length() >= max_length)
999
str.length(max_length-4);
1000
str.append(STRING_WITH_LEN("..."));
1002
my_printf_error(ER_DUP_ENTRY, msg,
1003
MYF(0), str.c_ptr(), table.key_info[key_nr].name);
1008
int StorageEngine::deleteDefinitionFromPath(const TableIdentifier &identifier)
1010
std::string path(identifier.getPath());
1012
path.append(DEFAULT_DEFINITION_FILE_EXT);
1014
return internal::my_delete(path.c_str(), MYF(0));
1017
int StorageEngine::renameDefinitionFromPath(const TableIdentifier &dest, const TableIdentifier &src)
1019
message::Table table_message;
1020
std::string src_path(src.getPath());
1021
std::string dest_path(dest.getPath());
1023
src_path.append(DEFAULT_DEFINITION_FILE_EXT);
1024
dest_path.append(DEFAULT_DEFINITION_FILE_EXT);
1026
bool was_read= StorageEngine::readTableFile(src_path.c_str(), table_message);
1033
dest.copyToTableMessage(table_message);
1035
int error= StorageEngine::writeDefinitionFromPath(dest, table_message);
1039
if (unlink(src_path.c_str()))
1040
perror(src_path.c_str());
1046
int StorageEngine::writeDefinitionFromPath(const TableIdentifier &identifier, message::Table &table_message)
1048
char definition_file_tmp[FN_REFLEN];
1049
std::string file_name(identifier.getPath());
1051
file_name.append(DEFAULT_DEFINITION_FILE_EXT);
1053
snprintf(definition_file_tmp, sizeof(definition_file_tmp), "%sXXXXXX", file_name.c_str());
1055
int fd= mkstemp(definition_file_tmp);
1059
perror(definition_file_tmp);
1063
google::protobuf::io::ZeroCopyOutputStream* output=
1064
new google::protobuf::io::FileOutputStream(fd);
1070
success= table_message.SerializeToZeroCopyStream(output);
1079
my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
1080
table_message.InitializationErrorString().c_str());
1083
if (close(fd) == -1)
1084
perror(definition_file_tmp);
1086
if (unlink(definition_file_tmp) == -1)
1087
perror(definition_file_tmp);
1089
return ER_CORRUPT_TABLE_DEFINITION;
1094
if (close(fd) == -1)
1097
perror(definition_file_tmp);
1099
if (unlink(definition_file_tmp))
1100
perror(definition_file_tmp);
1105
if (rename(definition_file_tmp, file_name.c_str()) == -1)
1108
perror(definition_file_tmp);
1110
if (unlink(definition_file_tmp))
1111
perror(definition_file_tmp);
1119
class CanCreateTable: public std::unary_function<StorageEngine *, bool>
1121
const TableIdentifier &identifier;
788
my_casedn_str(files_charset_info, tmp_path + drizzle_data_home_len);
793
class DFETableNameIterator: public TableNameIteratorImplementation
797
uint32_t current_entry;
1124
CanCreateTable(const TableIdentifier &identifier_arg) :
1125
identifier(identifier_arg)
1128
result_type operator() (argument_type engine)
1130
return not engine->doCanCreateTable(identifier);
800
DFETableNameIterator(const std::string &database)
801
: TableNameIteratorImplementation(database),
806
~DFETableNameIterator();
808
int next(std::string *name);
1136
@note on success table can be created.
1138
bool StorageEngine::canCreateTable(const TableIdentifier &identifier)
1140
EngineVector::iterator iter=
1141
std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
1142
CanCreateTable(identifier));
1144
if (iter == vector_of_engines.end())
1152
bool StorageEngine::readTableFile(const std::string &path, message::Table &table_message)
1154
std::fstream input(path.c_str(), std::ios::in | std::ios::binary);
1159
if (table_message.ParseFromIstream(&input))
1166
my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
1167
table_message.InitializationErrorString().empty() ? "": table_message.InitializationErrorString().c_str());
1172
perror(path.c_str());
1180
} /* namespace plugin */
1181
} /* namespace drizzled */
812
DFETableNameIterator::~DFETableNameIterator()
818
int DFETableNameIterator::next(string *name)
820
char uname[NAME_LEN + 1];
823
uint32_t file_name_len;
824
const char *wild= NULL;
829
char path[FN_REFLEN];
831
build_table_filename(path, sizeof(path), db.c_str(), "", false);
833
dirp = my_dir(path,MYF(dir ? MY_WANT_STAT : 0));
837
if (my_errno == ENOENT)
838
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db.c_str());
840
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
850
if (current_entry == dirp->number_off_files)
857
file= dirp->dir_entry + current_entry;
859
if (my_strcasecmp(system_charset_info, ext=fn_rext(file->name),".dfe") ||
860
is_prefix(file->name, TMP_FILE_PREFIX))
864
file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
866
uname[file_name_len]= '\0';
868
if (wild && wild_compare(uname, wild, 0))
878
TableNameIterator::TableNameIterator(const std::string &db)
879
: current_implementation(NULL), database(db)
881
engine_iter= all_engines.begin();
882
default_implementation= new DFETableNameIterator(database);
885
TableNameIterator::~TableNameIterator()
887
delete current_implementation;
890
int TableNameIterator::next(std::string *name)
895
if (current_implementation == NULL)
897
while(current_implementation == NULL && engine_iter != all_engines.end())
899
StorageEngine *engine= *engine_iter;
900
current_implementation= engine->tableNameIterator(database);
904
if (current_implementation == NULL && engine_iter == all_engines.end())
906
current_implementation= default_implementation;
910
err= current_implementation->next(name);
914
if (current_implementation != default_implementation)
916
delete current_implementation;
917
current_implementation= NULL;