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/my_hash.h"
36
#include "drizzled/cached_directory.h"
31
#include "mysys/my_dir.h"
32
#include "mysys/hash.h"
38
34
#include <drizzled/definitions.h>
39
35
#include <drizzled/base.h>
40
#include <drizzled/cursor.h>
36
#include <drizzled/handler.h>
41
37
#include <drizzled/plugin/storage_engine.h>
42
38
#include <drizzled/session.h>
43
39
#include <drizzled/error.h>
44
40
#include <drizzled/gettext.h>
41
#include <drizzled/registry.h>
45
42
#include <drizzled/unireg.h>
46
43
#include <drizzled/data_home.h>
47
44
#include "drizzled/errmsg_print.h"
45
#include <drizzled/plugin/registry.h>
48
46
#include "drizzled/xid.h"
49
#include "drizzled/sql_table.h"
50
#include "drizzled/global_charset_info.h"
51
#include "drizzled/charset.h"
52
#include "drizzled/internal/my_sys.h"
53
#include "drizzled/db.h"
55
48
#include <drizzled/table_proto.h>
57
static bool shutdown_has_begun= false; // Once we put in the container for the vector/etc for engines this will go away.
59
50
using namespace std;
67
static EngineVector vector_of_engines;
68
static EngineVector vector_of_schema_engines;
70
const std::string UNKNOWN_STRING("UNKNOWN");
71
const std::string DEFAULT_DEFINITION_FILE_EXT(".dfe");
73
static std::set<std::string> set_of_table_definition_ext;
75
EngineVector &StorageEngine::getSchemaEngines()
77
return vector_of_schema_engines;
80
StorageEngine::StorageEngine(const string name_arg,
81
const bitset<HTON_BIT_SIZE> &flags_arg) :
82
Plugin(name_arg, "StorageEngine"),
83
MonitoredInTransaction(), /* This gives the storage engine a "slot" or ID */
88
StorageEngine::~StorageEngine()
92
void StorageEngine::setTransactionReadWrite(Session& session)
94
TransactionContext &statement_ctx= session.transaction.stmt;
95
statement_ctx.markModifiedNonTransData();
99
int StorageEngine::renameTable(Session &session, TableIdentifier &from, TableIdentifier &to)
101
setTransactionReadWrite(session);
103
return doRenameTable(session, from, to);
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))
114
if ((error=my_errno) != ENOENT)
107
124
Delete all files with extension from bas_ext().
121
int StorageEngine::doDropTable(Session&, TableIdentifier &identifier)
138
int plugin::StorageEngine::deleteTableImplementation(Session *,
139
const string table_path)
125
142
int enoent_or_zero= ENOENT; // Error if no file was deleted
126
143
char buff[FN_REFLEN];
128
for (const char **ext= bas_ext(); *ext ; ext++)
145
for (const char **ext=bas_ext(); *ext ; ext++)
130
internal::fn_format(buff, identifier.getPath().c_str(), "", *ext,
131
MY_UNPACK_FILENAME|MY_APPEND_EXT);
132
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)))
134
if ((error= errno) != ENOENT)
151
if ((error= my_errno) != ENOENT)
139
155
enoent_or_zero= 0; // No error for ENOENT
142
156
error= enoent_or_zero;
147
bool StorageEngine::addPlugin(StorageEngine *engine)
150
vector_of_engines.push_back(engine);
152
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))
154
assert(engine->getTableDefinitionFileExtension().length() == DEFAULT_DEFINITION_FILE_EXT.length());
155
set_of_table_definition_ext.insert(engine->getTableDefinitionFileExtension());
188
errmsg_printf(ERRMSG_LVL_ERROR,
189
_("Couldn't add StorageEngine"));
158
if (engine->check_flag(HTON_BIT_SCHEMA_DICTIONARY))
159
vector_of_schema_engines.push_back(engine);
164
void StorageEngine::removePlugin(StorageEngine *)
195
void plugin::StorageEngine::removePlugin(plugin::StorageEngine *engine)
166
if (shutdown_has_begun == false)
168
vector_of_engines.clear();
169
vector_of_schema_engines.clear();
171
shutdown_has_begun= true;
197
all_engines.remove(engine);
175
class FindEngineByName
176
: public unary_function<StorageEngine *, bool>
178
const string ⌖
181
explicit FindEngineByName(const string &target_arg) :
185
result_type operator() (argument_type engine)
187
string engine_name(engine->getName());
189
transform(engine_name.begin(), engine_name.end(),
190
engine_name.begin(), ::tolower);
191
return engine_name == target;
195
StorageEngine *StorageEngine::findByName(const string &find_str)
197
string search_string(find_str);
198
transform(search_string.begin(), search_string.end(),
199
search_string.begin(), ::tolower);
200
plugin::StorageEngine *plugin::StorageEngine::findByName(Session *session,
202
EngineVector::iterator iter= find_if(vector_of_engines.begin(),
203
vector_of_engines.end(),
204
FindEngineByName(search_string));
205
if (iter != vector_of_engines.end())
207
StorageEngine *engine= *iter;
208
if (engine->is_user_selectable())
215
StorageEngine *StorageEngine::findByName(Session& session, const string &find_str)
217
string search_string(find_str);
218
transform(search_string.begin(), search_string.end(),
219
search_string.begin(), ::tolower);
221
if (search_string.compare("default") == 0)
222
return session.getDefaultStorageEngine();
224
EngineVector::iterator iter= find_if(vector_of_engines.begin(),
225
vector_of_engines.end(),
226
FindEngineByName(search_string));
227
if (iter != vector_of_engines.end())
229
StorageEngine *engine= *iter;
230
if (engine->is_user_selectable())
237
class StorageEngineCloseConnection : public unary_function<StorageEngine *, void>
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>
239
221
Session *session;
255
238
don't bother to rollback here, it's done already
257
void StorageEngine::closeConnection(Session* session)
240
void plugin::StorageEngine::closeConnection(Session* session)
259
for_each(vector_of_engines.begin(), vector_of_engines.end(),
242
for_each(all_engines.begin(), all_engines.end(),
260
243
StorageEngineCloseConnection(session));
263
bool StorageEngine::flushLogs(StorageEngine *engine)
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)
265
296
if (engine == NULL)
267
if (find_if(vector_of_engines.begin(), vector_of_engines.end(),
268
mem_fun(&StorageEngine::flush_logs))
269
!= 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())
274
if (engine->flush_logs())
305
if ((!engine->is_enabled()) ||
306
(engine->flush_logs()))
280
class StorageEngineGetTableDefinition: public unary_function<StorageEngine *,bool>
283
TableIdentifier &identifier;
284
message::Table &table_message;
288
StorageEngineGetTableDefinition(Session& session_arg,
289
TableIdentifier &identifier_arg,
290
message::Table &table_message_arg,
292
session(session_arg),
293
identifier(identifier_arg),
294
table_message(table_message_arg),
297
result_type operator() (argument_type engine)
299
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);
301
488
if (ret != ENOENT)
304
return err == EEXIST || err != ENOENT;
308
class StorageEngineDoesTableExist: public unary_function<StorageEngine *, bool>
311
TableIdentifier &identifier;
314
StorageEngineDoesTableExist(Session& session_arg, TableIdentifier &identifier_arg) :
315
session(session_arg),
316
identifier(identifier_arg)
319
result_type operator() (argument_type engine)
321
return engine->doDoesTableExist(session, identifier);
326
Utility method which hides some of the details of getTableDefinition()
328
bool plugin::StorageEngine::doesTableExist(Session &session,
329
TableIdentifier &identifier,
330
bool include_temporary_tables)
332
if (include_temporary_tables)
334
if (session.doDoesTableExist(identifier))
338
EngineVector::iterator iter=
339
find_if(vector_of_engines.begin(), vector_of_engines.end(),
340
StorageEngineDoesTableExist(session, identifier));
342
if (iter == vector_of_engines.end())
350
bool plugin::StorageEngine::doDoesTableExist(Session&, TableIdentifier&)
352
cerr << " Engine was called for doDoesTableExist() and does not implement it: " << this->getName() << "\n";
358
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
359
519
to ask engine if there are any new tables that should be written to disk
360
520
or any dropped tables that need to be removed from disk
362
int StorageEngine::getTableDefinition(Session& session,
363
TableIdentifier &identifier,
364
message::Table &table_message,
365
bool include_temporary_tables)
522
int plugin::StorageEngine::getTableProto(const char* path,
523
message::Table *table_proto)
369
if (include_temporary_tables)
371
if (session.doGetTableDefinition(identifier, table_message) == EEXIST)
375
EngineVector::iterator iter=
376
find_if(vector_of_engines.begin(), vector_of_engines.end(),
377
StorageEngineGetTableDefinition(session, identifier, table_message, err));
379
if (iter == vector_of_engines.end())
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())
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);
545
int read_proto_err= drizzle_read_table_proto(proto_path.c_str(),
587
class DeleteTableStorageEngine
588
: public unary_function<plugin::StorageEngine *, void>
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) {}
599
result_type operator() (argument_type engine)
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;
418
returns ENOENT if the file doesn't exists.
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
420
int StorageEngine::dropTable(Session& session,
421
TableIdentifier &identifier)
425
message::Table src_proto;
426
StorageEngine *engine;
428
error_proto= StorageEngine::getTableDefinition(session, identifier, src_proto);
430
if (error_proto == ER_CORRUPT_TABLE_DEFINITION)
432
string error_message;
434
error_message.append(identifier.getSQLPath());
435
error_message.append(" : ");
436
error_message.append(src_proto.InitializationErrorString());
438
my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0), error_message.c_str());
440
return ER_CORRUPT_TABLE_DEFINITION;
443
engine= StorageEngine::findByName(session, src_proto.engine().name());
447
my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0), identifier.getSQLPath().c_str());
449
return ER_CORRUPT_TABLE_DEFINITION;
452
error= StorageEngine::dropTable(session, *engine, identifier);
454
if (error_proto && error == 0)
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))
460
int StorageEngine::dropTable(Session& session,
461
StorageEngine &engine,
462
TableIdentifier &identifier)
466
engine.setTransactionReadWrite(session);
467
error= engine.doDropTable(session, identifier);
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;
481
int StorageEngine::createTable(Session &session,
482
TableIdentifier &identifier,
483
bool update_create_info,
484
message::Table& table_message)
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)
488
TableShare share(identifier.getSchemaName().c_str(), 0, identifier.getTableName().c_str(), identifier.getPath().c_str());
489
message::Table tmp_proto;
871
TableShare share(db, 0, table_name, path);
872
drizzled::message::Table tmp_proto;
491
if (parse_table_proto(session, table_message, &share) || open_table_from_share(&session, &share, "", 0, 0, &table))
493
// @note Error occured, we should probably do a little more here.
876
if (parse_table_proto(session, *table_proto, &share))
497
if (update_create_info)
498
table.updateCreateInfo(&table_message);
500
/* Check for legal operations against the Engine using the proto (if used) */
501
if (table_message.type() == message::Table::TEMPORARY &&
502
share.storage_engine->check_flag(HTON_BIT_TEMPORARY_NOT_SUPPORTED) == true)
504
error= HA_ERR_UNSUPPORTED;
506
else if (table_message.type() != message::Table::TEMPORARY &&
507
share.storage_engine->check_flag(HTON_BIT_TEMPORARY_ONLY) == true)
509
error= HA_ERR_UNSUPPORTED;
513
share.storage_engine->setTransactionReadWrite(session);
515
error= share.storage_engine->doCreateTable(session,
523
my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), identifier.getSQLPath().c_str(), error);
526
table.closefrm(false);
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);
529
903
share.free_table_share();
530
904
return(error != 0);
533
Cursor *StorageEngine::getCursor(TableShare &share, memory::Root *alloc)
535
return create(share, alloc);
539
public unary_function<StorageEngine *, void>
541
CachedDirectory &directory;
542
SchemaIdentifier &identifier;
543
TableNameList &set_of_names;
547
AddTableName(CachedDirectory &directory_arg, SchemaIdentifier &identifier_arg, set<string>& of_names) :
548
directory(directory_arg),
549
identifier(identifier_arg),
550
set_of_names(of_names)
554
result_type operator() (argument_type engine)
556
engine->doGetTableNames(directory, identifier, set_of_names);
560
class AddTableIdentifier :
561
public unary_function<StorageEngine *, void>
563
CachedDirectory &directory;
564
SchemaIdentifier &identifier;
565
TableIdentifiers &set_of_identifiers;
569
AddTableIdentifier(CachedDirectory &directory_arg, SchemaIdentifier &identifier_arg, TableIdentifiers &of_names) :
570
directory(directory_arg),
571
identifier(identifier_arg),
572
set_of_identifiers(of_names)
576
result_type operator() (argument_type engine)
578
engine->doGetTableIdentifiers(directory, identifier, set_of_identifiers);
583
static SchemaIdentifier INFORMATION_SCHEMA_IDENTIFIER("information_schema");
584
static SchemaIdentifier DATA_DICTIONARY_IDENTIFIER("data_dictionary");
586
void StorageEngine::getTableNames(Session &session, SchemaIdentifier &schema_identifier, TableNameList &set_of_names)
588
CachedDirectory directory(schema_identifier.getPath(), set_of_table_definition_ext);
590
if (schema_identifier == INFORMATION_SCHEMA_IDENTIFIER)
592
else if (schema_identifier == DATA_DICTIONARY_IDENTIFIER)
596
if (directory.fail())
598
errno= directory.getError();
600
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), schema_identifier.getSQLPath().c_str());
602
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), directory.getPath(), errno);
607
for_each(vector_of_engines.begin(), vector_of_engines.end(),
608
AddTableName(directory, schema_identifier, set_of_names));
610
session.doGetTableNames(directory, schema_identifier, set_of_names);
613
void StorageEngine::getTableIdentifiers(Session &session, SchemaIdentifier &schema_identifier, TableIdentifiers &set_of_identifiers)
615
CachedDirectory directory(schema_identifier.getPath(), set_of_table_definition_ext);
617
if (schema_identifier == INFORMATION_SCHEMA_IDENTIFIER)
619
else if (schema_identifier == DATA_DICTIONARY_IDENTIFIER)
623
if (directory.fail())
625
errno= directory.getError();
627
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), schema_identifier.getSQLPath().c_str());
629
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), directory.getPath(), errno);
634
for_each(vector_of_engines.begin(), vector_of_engines.end(),
635
AddTableIdentifier(directory, schema_identifier, set_of_identifiers));
637
session.doGetTableIdentifiers(directory, schema_identifier, set_of_identifiers);
640
/* This will later be converted to TableIdentifiers */
641
class DropTables: public unary_function<StorageEngine *, void>
644
TableIdentifierList &table_identifiers;
648
DropTables(Session &session_arg, TableIdentifierList &table_identifiers_arg) :
649
session(session_arg),
650
table_identifiers(table_identifiers_arg)
653
result_type operator() (argument_type engine)
655
for (TableIdentifierList::iterator iter= table_identifiers.begin();
656
iter != table_identifiers.end();
659
int error= engine->doDropTable(session, const_cast<TableIdentifier&>(*iter));
661
// On a return of zero we know we found and deleted the table. So we
662
// remove it from our search.
664
table_identifiers.erase(iter);
670
This only works for engines which use file based DFE.
672
Note-> Unlike MySQL, we do not, on purpose, delete files that do not match any engines.
674
void StorageEngine::removeLostTemporaryTables(Session &session, const char *directory)
676
CachedDirectory dir(directory, set_of_table_definition_ext);
677
TableIdentifierList table_identifiers;
681
errno= dir.getError();
682
my_error(ER_CANT_READ_DIR, MYF(0), directory, errno);
687
CachedDirectory::Entries files= dir.getEntries();
689
for (CachedDirectory::Entries::iterator fileIter= files.begin();
690
fileIter != files.end(); fileIter++)
694
CachedDirectory::Entry *entry= *fileIter;
696
/* We remove the file extension. */
697
length= entry->filename.length();
698
entry->filename.resize(length - DEFAULT_DEFINITION_FILE_EXT.length());
702
path+= entry->filename;
703
message::Table definition;
704
if (StorageEngine::readTableFile(path, definition))
706
TableIdentifier identifier(definition.schema(), definition.name(), path);
707
table_identifiers.push_back(identifier);
711
for_each(vector_of_engines.begin(), vector_of_engines.end(),
712
DropTables(session, table_identifiers));
715
Now we just clean up anything that might left over.
717
We rescan because some of what might have been there should
718
now be all nice and cleaned up.
720
set<string> all_exts= set_of_table_definition_ext;
722
for (EngineVector::iterator iter= vector_of_engines.begin();
723
iter != vector_of_engines.end() ; iter++)
725
for (const char **ext= (*iter)->bas_ext(); *ext ; ext++)
726
all_exts.insert(*ext);
729
CachedDirectory rescan(directory, all_exts);
731
files= rescan.getEntries();
732
for (CachedDirectory::Entries::iterator fileIter= files.begin();
733
fileIter != files.end(); fileIter++)
736
CachedDirectory::Entry *entry= *fileIter;
740
path+= entry->filename;
742
unlink(path.c_str());
748
Print error that we got from Cursor function.
751
In case of delete table it's only safe to use the following parts of
752
the 'table' structure:
756
void StorageEngine::print_error(int error, myf errflag, Table &table)
758
print_error(error, errflag, &table);
761
void StorageEngine::print_error(int error, myf errflag, Table *table)
763
int textno= ER_GET_ERRNO;
766
textno=ER_OPEN_AS_READONLY;
772
textno=ER_FILE_NOT_FOUND;
774
case HA_ERR_KEY_NOT_FOUND:
775
case HA_ERR_NO_ACTIVE_RECORD:
776
case HA_ERR_END_OF_FILE:
777
textno=ER_KEY_NOT_FOUND;
779
case HA_ERR_WRONG_MRG_TABLE_DEF:
780
textno=ER_WRONG_MRG_TABLE;
782
case HA_ERR_FOUND_DUPP_KEY:
785
uint32_t key_nr= table->get_dup_key(error);
786
if ((int) key_nr >= 0)
788
const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
791
(table->key_info[0].key_part[0].field->flags &
793
&& (current_session)->lex->sql_command == SQLCOM_ALTER_TABLE)
795
err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
798
print_keydup_error(key_nr, err_msg, *table);
804
case HA_ERR_FOREIGN_DUPLICATE_KEY:
807
uint32_t key_nr= table->get_dup_key(error);
808
if ((int) key_nr >= 0)
812
/* Write the key in the error message */
813
char key[MAX_KEY_LENGTH];
814
String str(key,sizeof(key),system_charset_info);
816
/* Table is opened and defined at this point */
817
key_unpack(&str,table,(uint32_t) key_nr);
818
max_length= (DRIZZLE_ERRMSG_SIZE-
819
(uint32_t) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
820
if (str.length() >= max_length)
822
str.length(max_length-4);
823
str.append(STRING_WITH_LEN("..."));
825
my_error(ER_FOREIGN_DUPLICATE_KEY, MYF(0), table->s->table_name.str,
826
str.c_ptr(), key_nr+1);
832
case HA_ERR_FOUND_DUPP_UNIQUE:
833
textno=ER_DUP_UNIQUE;
835
case HA_ERR_RECORD_CHANGED:
839
textno=ER_NOT_KEYFILE;
841
case HA_ERR_WRONG_IN_RECORD:
842
textno= ER_CRASHED_ON_USAGE;
844
case HA_ERR_CRASHED_ON_USAGE:
845
textno=ER_CRASHED_ON_USAGE;
847
case HA_ERR_NOT_A_TABLE:
850
case HA_ERR_CRASHED_ON_REPAIR:
851
textno=ER_CRASHED_ON_REPAIR;
853
case HA_ERR_OUT_OF_MEM:
854
textno=ER_OUT_OF_RESOURCES;
856
case HA_ERR_WRONG_COMMAND:
857
textno=ER_ILLEGAL_HA;
859
case HA_ERR_OLD_FILE:
860
textno=ER_OLD_KEYFILE;
862
case HA_ERR_UNSUPPORTED:
863
textno=ER_UNSUPPORTED_EXTENSION;
865
case HA_ERR_RECORD_FILE_FULL:
866
case HA_ERR_INDEX_FILE_FULL:
867
textno=ER_RECORD_FILE_FULL;
869
case HA_ERR_LOCK_WAIT_TIMEOUT:
870
textno=ER_LOCK_WAIT_TIMEOUT;
872
case HA_ERR_LOCK_TABLE_FULL:
873
textno=ER_LOCK_TABLE_FULL;
875
case HA_ERR_LOCK_DEADLOCK:
876
textno=ER_LOCK_DEADLOCK;
878
case HA_ERR_READ_ONLY_TRANSACTION:
879
textno=ER_READ_ONLY_TRANSACTION;
881
case HA_ERR_CANNOT_ADD_FOREIGN:
882
textno=ER_CANNOT_ADD_FOREIGN;
884
case HA_ERR_ROW_IS_REFERENCED:
887
get_error_message(error, &str);
888
my_error(ER_ROW_IS_REFERENCED_2, MYF(0), str.c_ptr_safe());
891
case HA_ERR_NO_REFERENCED_ROW:
894
get_error_message(error, &str);
895
my_error(ER_NO_REFERENCED_ROW_2, MYF(0), str.c_ptr_safe());
898
case HA_ERR_TABLE_DEF_CHANGED:
899
textno=ER_TABLE_DEF_CHANGED;
901
case HA_ERR_NO_SUCH_TABLE:
903
my_error(ER_NO_SUCH_TABLE, MYF(0), table->s->getSchemaName(),
904
table->s->table_name.str);
906
case HA_ERR_RBR_LOGGING_FAILED:
907
textno= ER_BINLOG_ROW_LOGGING_FAILED;
909
case HA_ERR_DROP_INDEX_FK:
912
const char *ptr= "???";
913
uint32_t key_nr= table->get_dup_key(error);
914
if ((int) key_nr >= 0)
915
ptr= table->key_info[key_nr].name;
916
my_error(ER_DROP_INDEX_FK, MYF(0), ptr);
919
case HA_ERR_TABLE_NEEDS_UPGRADE:
920
textno=ER_TABLE_NEEDS_UPGRADE;
922
case HA_ERR_TABLE_READONLY:
923
textno= ER_OPEN_AS_READONLY;
925
case HA_ERR_AUTOINC_READ_FAILED:
926
textno= ER_AUTOINC_READ_FAILED;
928
case HA_ERR_AUTOINC_ERANGE:
929
textno= ER_WARN_DATA_OUT_OF_RANGE;
931
case HA_ERR_LOCK_OR_ACTIVE_TRANSACTION:
932
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
933
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
938
The error was "unknown" to this function.
939
Ask Cursor if it has got a message for this error
941
bool temporary= false;
943
temporary= get_error_message(error, &str);
946
const char* engine_name= getName().c_str();
948
my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(),
951
my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine_name);
955
my_error(ER_GET_ERRNO,errflag,error);
960
my_error(textno, errflag, table->s->table_name.str, error);
965
Return an error message specific to this Cursor.
967
@param error error code previously returned by Cursor
968
@param buf pointer to String where to add error message
971
Returns true if this is a temporary error
973
bool StorageEngine::get_error_message(int , String* )
979
void StorageEngine::print_keydup_error(uint32_t key_nr, const char *msg, Table &table)
981
/* Write the duplicated key in the error message */
982
char key[MAX_KEY_LENGTH];
983
String str(key,sizeof(key),system_charset_info);
985
if (key_nr == MAX_KEY)
988
str.copy("", 0, system_charset_info);
989
my_printf_error(ER_DUP_ENTRY, msg, MYF(0), str.c_ptr(), "*UNKNOWN*");
993
/* Table is opened and defined at this point */
994
key_unpack(&str, &table, (uint32_t) key_nr);
995
uint32_t max_length=DRIZZLE_ERRMSG_SIZE-(uint32_t) strlen(msg);
996
if (str.length() >= max_length)
998
str.length(max_length-4);
999
str.append(STRING_WITH_LEN("..."));
1001
my_printf_error(ER_DUP_ENTRY, msg,
1002
MYF(0), str.c_ptr(), table.key_info[key_nr].name);
1007
int StorageEngine::deleteDefinitionFromPath(TableIdentifier &identifier)
1009
string path(identifier.getPath());
1011
path.append(DEFAULT_DEFINITION_FILE_EXT);
1013
return internal::my_delete(path.c_str(), MYF(0));
1016
int StorageEngine::renameDefinitionFromPath(TableIdentifier &dest, TableIdentifier &src)
1018
message::Table table_message;
1019
string src_path(src.getPath());
1020
string dest_path(dest.getPath());
1022
src_path.append(DEFAULT_DEFINITION_FILE_EXT);
1023
dest_path.append(DEFAULT_DEFINITION_FILE_EXT);
1025
bool was_read= StorageEngine::readTableFile(src_path.c_str(), table_message);
1032
dest.copyToTableMessage(table_message);
1034
int error= StorageEngine::writeDefinitionFromPath(dest, table_message);
1038
if (unlink(src_path.c_str()))
1039
perror(src_path.c_str());
1045
int StorageEngine::writeDefinitionFromPath(TableIdentifier &identifier, message::Table &table_message)
1047
char definition_file_tmp[FN_REFLEN];
1048
string file_name(identifier.getPath());
1050
file_name.append(DEFAULT_DEFINITION_FILE_EXT);
1052
snprintf(definition_file_tmp, sizeof(definition_file_tmp), "%sXXXXXX", file_name.c_str());
1054
int fd= mkstemp(definition_file_tmp);
1058
perror(definition_file_tmp);
1062
google::protobuf::io::ZeroCopyOutputStream* output=
1063
new google::protobuf::io::FileOutputStream(fd);
1065
if (not table_message.SerializeToZeroCopyStream(output))
1067
my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
1068
table_message.InitializationErrorString().c_str());
1071
if (close(fd) == -1)
1072
perror(definition_file_tmp);
1074
if (unlink(definition_file_tmp) == -1)
1075
perror(definition_file_tmp);
1077
return ER_CORRUPT_TABLE_DEFINITION;
1082
if (close(fd) == -1)
1085
perror(definition_file_tmp);
1087
if (unlink(definition_file_tmp))
1088
perror(definition_file_tmp);
1093
if (rename(definition_file_tmp, file_name.c_str()) == -1)
1096
perror(definition_file_tmp);
1098
if (unlink(definition_file_tmp))
1099
perror(definition_file_tmp);
1107
class CanCreateTable: public unary_function<StorageEngine *, bool>
1109
TableIdentifier &identifier;
1112
CanCreateTable(TableIdentifier &identifier_arg) :
1113
identifier(identifier_arg)
1116
result_type operator() (argument_type engine)
1118
return not engine->doCanCreateTable(identifier);
1124
@note on success table can be created.
1126
bool StorageEngine::canCreateTable(drizzled::TableIdentifier &identifier)
1128
EngineVector::iterator iter=
1129
find_if(vector_of_engines.begin(), vector_of_engines.end(),
1130
CanCreateTable(identifier));
1132
if (iter == vector_of_engines.end())
1140
bool StorageEngine::readTableFile(const std::string &path, message::Table &table_message)
1142
fstream input(path.c_str(), ios::in | ios::binary);
1146
if (table_message.ParseFromIstream(&input))
1151
my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
1152
table_message.InitializationErrorString().c_str());
1156
perror(path.c_str());
1164
} /* namespace plugin */
1165
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;