00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <config.h>
00020 #include <drizzled/table.h>
00021 #include <drizzled/error.h>
00022 #include <drizzled/plugin/transactional_storage_engine.h>
00023 #include <drizzled/session.h>
00024 #include <string>
00025 #include <map>
00026 #include <iostream>
00027 #include <fstream>
00028 #include <drizzled/message/table.pb.h>
00029 #include <drizzled/internal/m_string.h>
00030
00031 #include <drizzled/charset.h>
00032
00033 #include <boost/unordered_map.hpp>
00034
00035 #include "engine_state_history.h"
00036
00037 using namespace std;
00038 using namespace drizzled;
00039
00040 string engine_state;
00041
00042 typedef multimap<string, string> state_multimap;
00043 typedef multimap<string, string>::value_type state_pair;
00044 typedef multimap<string, string>::iterator state_multimap_iter;
00045 state_multimap engine_state_transitions;
00046 state_multimap cursor_state_transitions;
00047
00048 void load_engine_state_transitions(state_multimap &states);
00049 void load_cursor_state_transitions(state_multimap &states);
00050
00051 uint64_t next_cursor_id;
00052
00053 plugin::TransactionalStorageEngine *realEngine;
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071 static uint32_t error_injected= 0;
00072
00073 #include <drizzled/function/math/int.h>
00074 #include <drizzled/plugin/function.h>
00075
00076 class SEAPITesterErrorInjectFunc :public Item_int_func
00077 {
00078 public:
00079 int64_t val_int();
00080 SEAPITesterErrorInjectFunc() :Item_int_func() {}
00081
00082 const char *func_name() const
00083 {
00084 return "seapitester_error_inject";
00085 }
00086
00087 void fix_length_and_dec()
00088 {
00089 max_length= 4;
00090 }
00091
00092 bool check_argument_count(int n)
00093 {
00094 return (n == 1);
00095 }
00096 };
00097
00098
00099 int64_t SEAPITesterErrorInjectFunc::val_int()
00100 {
00101 assert(fixed == true);
00102 uint32_t err_to_inject= args[0]->val_int();
00103
00104 error_injected= err_to_inject;
00105
00106 return error_injected;
00107 }
00108
00109 static plugin::TransactionalStorageEngine *getRealEngine()
00110 {
00111 return down_cast<plugin::TransactionalStorageEngine*>(plugin::StorageEngine::findByName("INNODB"));
00112 }
00113
00114 static inline void ENGINE_NEW_STATE(const string &new_state)
00115 {
00116 state_multimap_iter cur= engine_state_transitions.find(engine_state);
00117 if (engine_state_transitions.count(engine_state) == 0)
00118 {
00119 std::cerr << "ERROR: Invalid engine state: " << engine_state << std::endl
00120 << "This should *NEVER* happen."
00121 << std::endl
00122 << "i.e. you've really screwed it up and you should be ashamed of "
00123 << "yourself." << std::endl;
00124 assert(engine_state_transitions.count(engine_state));
00125 }
00126
00127 for(cur= engine_state_transitions.lower_bound(engine_state);
00128 cur != engine_state_transitions.upper_bound(engine_state);
00129 cur++)
00130 {
00131 if (new_state.compare((*cur).second) == 0)
00132 break;
00133 }
00134
00135 if (cur == engine_state_transitions.end()
00136 || new_state.compare((*cur).second))
00137 {
00138 std::cerr << "ERROR: Invalid Storage Engine state transition!" << std::endl
00139 << "Cannot go from " << engine_state << " to " << new_state << std::endl;
00140 assert(false);
00141 }
00142
00143 engine_state= new_state;
00144 engine_state_history.push_back(new_state);
00145
00146 std::cerr << "\tENGINE STATE : " << engine_state << std::endl;
00147 }
00148
00149 static const string engine_name("STORAGE_ENGINE_API_TESTER");
00150 namespace drizzled {
00151 class SEAPITesterCursor : public drizzled::Cursor
00152 {
00153 friend class drizzled::Cursor;
00154 public:
00155 drizzled::Cursor *realCursor;
00156
00157 SEAPITesterCursor(drizzled::plugin::StorageEngine &engine_arg,
00158 drizzled::Table &table_arg)
00159 : Cursor(engine_arg, table_arg)
00160 {
00161 cursor_state= "Cursor()";
00162 realCursor= NULL;
00163 id= ++next_cursor_id;
00164 CURSOR_NEW_STATE("Cursor()");
00165 }
00166
00167 ~SEAPITesterCursor()
00168 { CURSOR_NEW_STATE("~Cursor()"); delete realCursor;}
00169
00170 int close();
00171 int rnd_next(unsigned char *buf) {
00172 static int count= 0;
00173 CURSOR_NEW_STATE("::rnd_next()");
00174
00175 if (error_injected == 3 && (count++ % 2))
00176 {
00177 user_session->markTransactionForRollback(false);
00178 return HA_ERR_LOCK_WAIT_TIMEOUT;
00179 }
00180 return realCursor->rnd_next(buf);
00181 }
00182
00183 int rnd_pos(unsigned char* buf, unsigned char* pos) { CURSOR_NEW_STATE("::rnd_pos()"); return realCursor->rnd_pos(buf, pos); }
00184 void position(const unsigned char *record);
00185 int info(uint32_t flag);
00186
00187 int reset();
00188
00189 void get_auto_increment(uint64_t, uint64_t, uint64_t, uint64_t*, uint64_t*) {}
00190 int doStartTableScan(bool scan) { CURSOR_NEW_STATE("::doStartTableScan()"); return realCursor->doStartTableScan(scan); }
00191 int doEndTableScan() { CURSOR_NEW_STATE("::doEndTableScan()"); return realCursor->doEndTableScan(); }
00192
00193 const char *index_type(uint32_t key_number);
00194
00195 int doStartIndexScan(uint32_t, bool);
00196 int index_read(unsigned char *buf, const unsigned char *key_ptr,
00197 uint32_t key_len, drizzled::ha_rkey_function find_flag);
00198 int index_read_idx_map(unsigned char * buf,
00199 uint32_t index,
00200 const unsigned char * key,
00201 drizzled::key_part_map keypart_map,
00202 drizzled::ha_rkey_function find_flag);
00203
00204 int index_next(unsigned char * buf);
00205 int doEndIndexScan();
00206 int index_prev(unsigned char * buf);
00207 int index_first(unsigned char * buf);
00208 int index_last(unsigned char * buf);
00209
00210 bool primary_key_is_clustered()
00211 {
00212 return realCursor->primary_key_is_clustered();
00213 }
00214
00215
00216 int doOpen(const identifier::Table &identifier, int mode, uint32_t test_if_locked);
00217
00218 THR_LOCK_DATA **store_lock(Session *,
00219 THR_LOCK_DATA **to,
00220 enum thr_lock_type);
00221
00222 int external_lock(Session *session, int lock_type);
00223
00224 int doInsertRecord(unsigned char *buf)
00225 {
00226 static int i=0;
00227 CURSOR_NEW_STATE("::doInsertRecord()");
00228
00229 if (error_injected == 1 && (i++ % 2))
00230 {
00231 user_session->markTransactionForRollback(false);
00232 return HA_ERR_LOCK_WAIT_TIMEOUT;
00233 }
00234
00235 if (error_injected == 2 && (i++ % 2))
00236 {
00237 user_session->markTransactionForRollback(true);
00238 return HA_ERR_LOCK_DEADLOCK;
00239 }
00240
00241 return realCursor->doInsertRecord(buf);
00242 }
00243
00244 int doUpdateRecord(const unsigned char *old_row, unsigned char *new_row)
00245 {
00246 CURSOR_NEW_STATE("::doUpdateRecord()");
00247 return realCursor->doUpdateRecord(old_row, new_row);
00248 }
00249
00250 double scan_time()
00251 {
00252 CURSOR_NEW_STATE("::scan_time()");
00253 CURSOR_NEW_STATE("locked");
00254 return realCursor->scan_time();
00255 }
00256
00257 int extra(enum ha_extra_function operation)
00258 {
00259 return realCursor->extra(operation);
00260 }
00261
00262 private:
00263 string cursor_state;
00264 void CURSOR_NEW_STATE(const string &new_state);
00265 Session* user_session;
00266 uint64_t id;
00267 };
00268
00269 int SEAPITesterCursor::doOpen(const identifier::Table &identifier, int mode, uint32_t test_if_locked)
00270 {
00271 CURSOR_NEW_STATE("::doOpen()");
00272
00273 int r= realCursor->doOpen(identifier, mode, test_if_locked);
00274
00275 ref_length= realCursor->ref_length;
00276
00277 return r;
00278 }
00279
00280 int SEAPITesterCursor::reset()
00281 {
00282 CURSOR_NEW_STATE("::reset()");
00283 CURSOR_NEW_STATE("::doOpen()");
00284
00285 return realCursor->reset();
00286 }
00287
00288 int SEAPITesterCursor::close()
00289 {
00290 CURSOR_NEW_STATE("::close()");
00291 CURSOR_NEW_STATE("Cursor()");
00292
00293 return realCursor->close();
00294 }
00295
00296 void SEAPITesterCursor::position(const unsigned char *record)
00297 {
00298 CURSOR_NEW_STATE("::position()");
00299
00300
00301 realCursor->ref= ref;
00302
00303 realCursor->position(record);
00304 }
00305
00306 int SEAPITesterCursor::info(uint32_t flag)
00307 {
00308 int r;
00309 CURSOR_NEW_STATE("::info()");
00310 CURSOR_NEW_STATE("locked");
00311
00312 r= realCursor->info(flag);
00313
00314 if (flag & (HA_STATUS_VARIABLE|HA_STATUS_AUTO|HA_STATUS_CONST))
00315 {
00316 stats= realCursor->stats;
00317 }
00318
00319 if (flag & HA_STATUS_ERRKEY)
00320 errkey= realCursor->errkey;
00321
00322 return r;
00323 }
00324
00325 const char * SEAPITesterCursor::index_type(uint32_t key_number)
00326 {
00327 CURSOR_NEW_STATE("::index_type()");
00328 return realCursor->index_type(key_number);
00329 }
00330
00331 int SEAPITesterCursor::doStartIndexScan(uint32_t keynr, bool scan)
00332 {
00333 int r;
00334 CURSOR_NEW_STATE("::doStartIndexScan()");
00335
00336 if (error_injected == 4)
00337 {
00338 CURSOR_NEW_STATE("::doStartIndexScan() ERROR");
00339 CURSOR_NEW_STATE("locked");
00340 return HA_ERR_LOCK_DEADLOCK;
00341 }
00342
00343 r= realCursor->doStartIndexScan(keynr, scan);
00344
00345 active_index= realCursor->get_index();
00346
00347 return r;
00348 }
00349
00350 int SEAPITesterCursor::index_read(unsigned char *buf,
00351 const unsigned char *key_ptr,
00352 uint32_t key_len,
00353 drizzled::ha_rkey_function find_flag)
00354 {
00355 CURSOR_NEW_STATE("::index_read()");
00356 CURSOR_NEW_STATE("::doStartIndexScan()");
00357 return realCursor->index_read(buf, key_ptr, key_len, find_flag);
00358 }
00359
00360 int SEAPITesterCursor::index_read_idx_map(unsigned char * buf,
00361 uint32_t index,
00362 const unsigned char * key,
00363 drizzled::key_part_map keypart_map,
00364 drizzled::ha_rkey_function find_flag)
00365 {
00366 CURSOR_NEW_STATE("::index_read_idx_map()");
00367 CURSOR_NEW_STATE("locked");
00368 return realCursor->index_read_idx_map(buf, index, key, keypart_map, find_flag);
00369 }
00370
00371 int SEAPITesterCursor::index_next(unsigned char * buf)
00372 {
00373 CURSOR_NEW_STATE("::index_next()");
00374 CURSOR_NEW_STATE("::doStartIndexScan()");
00375 return realCursor->index_next(buf);
00376 }
00377
00378 int SEAPITesterCursor::doEndIndexScan()
00379 {
00380 CURSOR_NEW_STATE("::doEndIndexScan()");
00381 CURSOR_NEW_STATE("locked");
00382 int r= realCursor->doEndIndexScan();
00383
00384 active_index= realCursor->get_index();
00385
00386 return r;
00387 }
00388
00389 int SEAPITesterCursor::index_prev(unsigned char * buf)
00390 {
00391 CURSOR_NEW_STATE("::index_prev()");
00392 CURSOR_NEW_STATE("::doStartIndexScan()");
00393 return realCursor->index_prev(buf);
00394 }
00395
00396 int SEAPITesterCursor::index_first(unsigned char * buf)
00397 {
00398 CURSOR_NEW_STATE("::index_first()");
00399 CURSOR_NEW_STATE("::doStartIndexScan()");
00400 return realCursor->index_first(buf);
00401 }
00402
00403 int SEAPITesterCursor::index_last(unsigned char * buf)
00404 {
00405 CURSOR_NEW_STATE("::index_last()");
00406 CURSOR_NEW_STATE("::doStartIndexScan()");
00407 return realCursor->index_last(buf);
00408 }
00409
00410 int SEAPITesterCursor::external_lock(Session *session, int lock_type)
00411 {
00412 CURSOR_NEW_STATE("::external_lock()");
00413 CURSOR_NEW_STATE("locked");
00414
00415 user_session= session;
00416
00417 return realCursor->external_lock(session, lock_type);
00418 }
00419
00420 THR_LOCK_DATA **SEAPITesterCursor::store_lock(Session *session,
00421 THR_LOCK_DATA **to,
00422 enum thr_lock_type lock_type)
00423
00424 {
00425 CURSOR_NEW_STATE("::store_lock()");
00426
00427 return realCursor->store_lock(session, to, lock_type);
00428 }
00429
00430 void SEAPITesterCursor::CURSOR_NEW_STATE(const string &new_state)
00431 {
00432 state_multimap_iter cur= cursor_state_transitions.find(cursor_state);
00433 if (cursor_state_transitions.count(cursor_state) == 0)
00434 {
00435 std::cerr << "ERROR: Invalid Cursor state: " << cursor_state << std::endl
00436 << "This should *NEVER* happen."
00437 << std::endl
00438 << "i.e. you've really screwed it up and you should be ashamed of "
00439 << "yourself." << std::endl;
00440 assert(cursor_state_transitions.count(cursor_state));
00441 }
00442
00443 for(cur= cursor_state_transitions.lower_bound(cursor_state);
00444 cur != cursor_state_transitions.upper_bound(cursor_state);
00445 cur++)
00446 {
00447 if (new_state.compare((*cur).second) == 0)
00448 break;
00449 }
00450
00451 if (cur == cursor_state_transitions.end()
00452 || new_state.compare((*cur).second))
00453 {
00454 std::cerr << "ERROR: Invalid Cursor state transition!" << std::endl
00455 << "Cursor " << this << "Cannot go from "
00456 << cursor_state << " to " << new_state << std::endl;
00457 assert(false);
00458 }
00459
00460 cursor_state= new_state;
00461
00462 std::string cursor_state_str("Cursor ");
00463 char nr[50];
00464 snprintf(nr, sizeof(nr), "%"PRIu64, this->id);
00465 cursor_state_str.append(nr);
00466 cursor_state_str.append(" ");
00467 cursor_state_str.append(cursor_state);
00468
00469 engine_state_history.push_back(cursor_state_str);
00470
00471 std::cerr << "\t\tCursor " << this << " STATE : " << cursor_state << std::endl;
00472 }
00473
00474 }
00475
00476 static const char *api_tester_exts[] = {
00477 NULL
00478 };
00479
00480 namespace drizzled {
00481 namespace plugin {
00482 class SEAPITester : public drizzled::plugin::TransactionalStorageEngine
00483 {
00484 public:
00485
00486
00487
00488 SEAPITester(const string &name_arg)
00489 : drizzled::plugin::TransactionalStorageEngine(name_arg,
00490 HTON_NULL_IN_KEY |
00491 HTON_CAN_INDEX_BLOBS |
00492 HTON_PRIMARY_KEY_IN_READ_INDEX |
00493 HTON_PARTIAL_COLUMN_READ |
00494 HTON_TABLE_SCAN_ON_INDEX |
00495 HTON_HAS_FOREIGN_KEYS |
00496 HTON_HAS_DOES_TRANSACTIONS)
00497 {
00498 ENGINE_NEW_STATE("::SEAPITester()");
00499 }
00500
00501 ~SEAPITester()
00502 {
00503 ENGINE_NEW_STATE("::~SEAPITester()");
00504 }
00505
00506 const char **bas_ext() const {
00507 return api_tester_exts;
00508 }
00509
00510 virtual Cursor *create(Table &table)
00511 {
00512 SEAPITesterCursor *c= new SEAPITesterCursor(*this, table);
00513 Cursor *realCursor= getRealEngine()->create(table);
00514 c->realCursor= realCursor;
00515
00516 return c;
00517 }
00518
00519 int doCreateTable(Session&,
00520 Table&,
00521 const drizzled::identifier::Table &identifier,
00522 const drizzled::message::Table& create_proto);
00523
00524 int doDropTable(Session&, const identifier::Table &identifier);
00525
00526 int doRenameTable(drizzled::Session& session,
00527 const drizzled::identifier::Table& from,
00528 const drizzled::identifier::Table& to)
00529 { return getRealEngine()->renameTable(session, from, to); }
00530
00531 int doGetTableDefinition(Session& ,
00532 const identifier::Table &,
00533 drizzled::message::Table &);
00534
00535 bool doDoesTableExist(Session&, const identifier::Table &identifier);
00536
00537 void doGetTableIdentifiers(drizzled::CachedDirectory &,
00538 const drizzled::identifier::Schema &,
00539 drizzled::identifier::table::vector &);
00540
00541 virtual int doStartTransaction(Session *session,
00542 start_transaction_option_t options);
00543 virtual void doStartStatement(Session *session);
00544 virtual void doEndStatement(Session *session);
00545
00546 virtual int doSetSavepoint(Session*,
00547 drizzled::NamedSavepoint &)
00548 {
00549 ENGINE_NEW_STATE("SET SAVEPOINT");
00550 ENGINE_NEW_STATE("In Transaction");
00551 return 0; }
00552 virtual int doRollbackToSavepoint(Session*,
00553 drizzled::NamedSavepoint &)
00554 {
00555 ENGINE_NEW_STATE("ROLLBACK TO SAVEPOINT");
00556 ENGINE_NEW_STATE("In Transaction");
00557 return 0; }
00558 virtual int doReleaseSavepoint(Session*,
00559 drizzled::NamedSavepoint &)
00560 {
00561 ENGINE_NEW_STATE("RELEASE SAVEPOINT");
00562 ENGINE_NEW_STATE("In Transaction");
00563 return 0; }
00564 virtual int doCommit(Session*, bool);
00565
00566 virtual int doRollback(Session*, bool);
00567
00568 uint32_t max_supported_record_length(void) const {
00569 ENGINE_NEW_STATE("::max_supported_record_length()");
00570 return getRealEngine()->max_supported_record_length();
00571 }
00572
00573 uint32_t max_supported_keys(void) const {
00574 ENGINE_NEW_STATE("::max_supported_keys()");
00575 return getRealEngine()->max_supported_keys();
00576 }
00577
00578 uint32_t max_supported_key_parts(void) const {
00579 ENGINE_NEW_STATE("::max_supported_key_parts()");
00580 return getRealEngine()->max_supported_key_parts();
00581 }
00582
00583 uint32_t max_supported_key_length(void) const {
00584 ENGINE_NEW_STATE("::max_supported_key_length()");
00585 return getRealEngine()->max_supported_key_length();
00586 }
00587
00588 uint32_t max_supported_key_part_length(void) const {
00589 ENGINE_NEW_STATE("::max_supported_key_part_length()");
00590 return getRealEngine()->max_supported_key_part_length();
00591 }
00592
00593
00594 uint32_t index_flags(enum ha_key_alg) const
00595 {
00596 return (HA_READ_NEXT |
00597 HA_READ_PREV |
00598 HA_READ_RANGE |
00599 HA_READ_ORDER |
00600 HA_KEYREAD_ONLY);
00601 }
00602
00603 };
00604
00605 bool SEAPITester::doDoesTableExist(Session &session, const identifier::Table &identifier)
00606 {
00607 return getRealEngine()->doDoesTableExist(session, identifier);
00608 }
00609
00610 void SEAPITester::doGetTableIdentifiers(drizzled::CachedDirectory &cd,
00611 const drizzled::identifier::Schema &si,
00612 drizzled::identifier::table::vector &ti)
00613 {
00614 return getRealEngine()->doGetTableIdentifiers(cd, si, ti);
00615 }
00616
00617 int SEAPITester::doCreateTable(Session& session,
00618 Table& table,
00619 const drizzled::identifier::Table &identifier,
00620 const drizzled::message::Table& create_proto)
00621 {
00622 ENGINE_NEW_STATE("::doCreateTable()");
00623
00624 int r= getRealEngine()->doCreateTable(session, table, identifier, create_proto);
00625
00626 ENGINE_NEW_STATE("::SEAPITester()");
00627 return r;
00628 }
00629
00630 int SEAPITester::doDropTable(Session& session, const identifier::Table &identifier)
00631 {
00632 return getRealEngine()->doDropTable(session, identifier);
00633 }
00634
00635 int SEAPITester::doGetTableDefinition(Session& session,
00636 const identifier::Table &identifier,
00637 drizzled::message::Table &table)
00638 {
00639 return getRealEngine()->doGetTableDefinition(session, identifier, table);
00640 }
00641
00642 int SEAPITester::doStartTransaction(Session *session,
00643 start_transaction_option_t opt)
00644 {
00645 ENGINE_NEW_STATE("BEGIN");
00646 ENGINE_NEW_STATE("In Transaction");
00647
00648 return getRealEngine()->startTransaction(session, opt);
00649 }
00650
00651 void SEAPITester::doStartStatement(Session *session)
00652 {
00653 ENGINE_NEW_STATE("START STATEMENT");
00654 return getRealEngine()->startStatement(session);
00655 }
00656
00657 void SEAPITester::doEndStatement(Session *session)
00658 {
00659 ENGINE_NEW_STATE("END STATEMENT");
00660 return getRealEngine()->endStatement(session);
00661 }
00662
00663 int SEAPITester::doCommit(Session *session, bool all)
00664 {
00665 if (all)
00666 {
00667 ENGINE_NEW_STATE("COMMIT");
00668 ENGINE_NEW_STATE("::SEAPITester()");
00669 }
00670 else
00671 {
00672 ENGINE_NEW_STATE("COMMIT STATEMENT");
00673 ENGINE_NEW_STATE("In Transaction");
00674 }
00675 return getRealEngine()->commit(session, all);
00676 }
00677
00678 int SEAPITester::doRollback(Session *session, bool all)
00679 {
00680 if (all)
00681 {
00682 ENGINE_NEW_STATE("ROLLBACK");
00683 ENGINE_NEW_STATE("::SEAPITester()");
00684 }
00685 else
00686 {
00687 ENGINE_NEW_STATE("ROLLBACK STATEMENT");
00688 ENGINE_NEW_STATE("In Transaction");
00689 }
00690
00691 return getRealEngine()->rollback(session, all);
00692 }
00693
00694 }
00695 }
00696
00697 static int seapi_tester_init(drizzled::module::Context &context)
00698 {
00699 load_engine_state_transitions(engine_state_transitions);
00700 load_cursor_state_transitions(cursor_state_transitions);
00701 engine_state= "INIT";
00702
00703 context.add(new plugin::SEAPITester(engine_name));
00704
00705 context.add(new plugin::Create_function<SEAPITesterErrorInjectFunc>("seapitester_error_inject"));
00706
00707 engine_state_history_table_initialize(context);
00708
00709 return 0;
00710 }
00711
00712 DRIZZLE_DECLARE_PLUGIN
00713 {
00714 DRIZZLE_VERSION_ID,
00715 "SEAPITESTER",
00716 "1.0",
00717 "Stewart Smith",
00718 "Test the Storage Engine API callls are in correct order",
00719 PLUGIN_LICENSE_GPL,
00720 seapi_tester_init,
00721 NULL,
00722 NULL
00723 }
00724 DRIZZLE_DECLARE_PLUGIN_END;