Drizzled Public API Documentation

storage_engine_api_tester.cc

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