Drizzled Public API Documentation

storage_engine_api_tester.cc
1 /*
2  Copyright (C) 2010 Stewart Smith
3 
4  This program is free software; you can redistribute it and/or
5  modify it under the terms of the GNU General Public License
6  as published by the Free Software Foundation; either version 2
7  of the License, or (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18 
19 #include <config.h>
20 #include <drizzled/table.h>
21 #include <drizzled/error.h>
22 #include <drizzled/plugin/transactional_storage_engine.h>
23 #include <drizzled/session.h> // for mark_transaction_to_rollback
24 #include <string>
25 #include <map>
26 #include <iostream>
27 #include <fstream>
28 #include <drizzled/message/table.pb.h>
29 #include <drizzled/internal/m_string.h>
30 
31 #include <drizzled/charset.h>
32 
33 #include <boost/unordered_map.hpp>
34 
35 #include "engine_state_history.h"
36 
37 using namespace std;
38 using namespace drizzled;
39 
40 string engine_state;
41 
42 typedef multimap<string, string> state_multimap;
43 typedef multimap<string, string>::value_type state_pair;
44 typedef multimap<string, string>::iterator state_multimap_iter;
45 state_multimap engine_state_transitions;
46 state_multimap cursor_state_transitions;
47 
48 void load_engine_state_transitions(state_multimap &states);
49 void load_cursor_state_transitions(state_multimap &states);
50 
51 uint64_t next_cursor_id;
52 
53 plugin::TransactionalStorageEngine *realEngine;
54 
55 /* ERROR INJECTION For SEAPITESTER
56  -------------------------------
57 
58  IF you add a new error injection, document it here!
59  You test via error_injected variable.
60 
61  Conflicting error inject numbers will lead to tears
62  (not Borsch, Vodka and Tears - that's quite nice).
63 
64  0 - DISABLED
65 
66  1 - doInsertRecord(): every 2nd row, LOCK_WAIT_TIMEOUT.
67  2 - doInsertRecord(): every 2nd row, DEADLOCK.
68  3 - rnd_next(): every 2nd row, LOCK_WAIT_TIMEOUT
69  4 - doStartIndexScan returns an error.
70  */
71 static uint32_t error_injected= 0;
72 
73 #include <drizzled/function/math/int.h>
74 #include <drizzled/plugin/function.h>
75 
77 {
78 public:
79  int64_t val_int();
81 
82  const char *func_name() const
83  {
84  return "seapitester_error_inject";
85  }
86 
87  void fix_length_and_dec()
88  {
89  max_length= 4;
90  }
91 
93  {
94  return (n == 1);
95  }
96 };
97 
98 
100 {
101  assert(fixed == true);
102  uint32_t err_to_inject= args[0]->val_int();
103 
104  error_injected= err_to_inject;
105 
106  return error_injected;
107 }
108 
109 static plugin::TransactionalStorageEngine *getRealEngine()
110 {
111  return down_cast<plugin::TransactionalStorageEngine*>(plugin::StorageEngine::findByName("INNODB"));
112 }
113 
114 static inline void ENGINE_NEW_STATE(const string &new_state)
115 {
116  state_multimap_iter cur= engine_state_transitions.find(engine_state);
117  if (engine_state_transitions.count(engine_state) == 0)
118  {
119  std::cerr << "ERROR: Invalid engine state: " << engine_state << std::endl
120  << "This should *NEVER* happen."
121  << std::endl
122  << "i.e. you've really screwed it up and you should be ashamed of "
123  << "yourself." << std::endl;
124  assert(engine_state_transitions.count(engine_state));
125  }
126 
127  for(cur= engine_state_transitions.lower_bound(engine_state);
128  cur != engine_state_transitions.upper_bound(engine_state);
129  cur++)
130  {
131  if (new_state.compare((*cur).second) == 0)
132  break;
133  }
134 
135  if (cur == engine_state_transitions.end()
136  || new_state.compare((*cur).second))
137  {
138  std::cerr << "ERROR: Invalid Storage Engine state transition!" << std::endl
139  << "Cannot go from " << engine_state << " to " << new_state << std::endl;
140  assert(false);
141  }
142 
143  engine_state= new_state;
144  engine_state_history.push_back(new_state);
145 
146  std::cerr << "\tENGINE STATE : " << engine_state << std::endl;
147 }
148 
149 static const string engine_name("STORAGE_ENGINE_API_TESTER");
150 namespace drizzled {
152 {
153  friend class drizzled::Cursor;
154 public:
155  drizzled::Cursor *realCursor;
156 
158  drizzled::Table &table_arg)
159  : Cursor(engine_arg, table_arg)
160  {
161  cursor_state= "Cursor()";
162  realCursor= NULL;
163  id= ++next_cursor_id;
164  CURSOR_NEW_STATE("Cursor()");
165  }
166 
168  { CURSOR_NEW_STATE("~Cursor()"); delete realCursor;}
169 
170  int close();
171  int rnd_next(unsigned char *buf) {
172  static int count= 0;
173  CURSOR_NEW_STATE("::rnd_next()");
174 
175  if (error_injected == 3 && (count++ % 2))
176  {
177  user_session->markTransactionForRollback(false);
178  return HA_ERR_LOCK_WAIT_TIMEOUT;
179  }
180  return realCursor->rnd_next(buf);
181  }
182 
183  int rnd_pos(unsigned char* buf, unsigned char* pos) { CURSOR_NEW_STATE("::rnd_pos()"); return realCursor->rnd_pos(buf, pos); }
184  void position(const unsigned char *record);
185  int info(uint32_t flag);
186 
187  int reset();
188 
189  void get_auto_increment(uint64_t, uint64_t, uint64_t, uint64_t*, uint64_t*) {}
190  int doStartTableScan(bool scan) { CURSOR_NEW_STATE("::doStartTableScan()"); return realCursor->doStartTableScan(scan); }
191  int doEndTableScan() { CURSOR_NEW_STATE("::doEndTableScan()"); return realCursor->doEndTableScan(); }
192 
193  const char *index_type(uint32_t key_number);
194 
195  int doStartIndexScan(uint32_t, bool);
196  int index_read(unsigned char *buf, const unsigned char *key_ptr,
197  uint32_t key_len, drizzled::ha_rkey_function find_flag);
198  int index_read_idx_map(unsigned char * buf,
199  uint32_t index,
200  const unsigned char * key,
201  drizzled::key_part_map keypart_map,
202  drizzled::ha_rkey_function find_flag);
203 
204  int index_next(unsigned char * buf);
205  int doEndIndexScan();
206  int index_prev(unsigned char * buf);
207  int index_first(unsigned char * buf);
208  int index_last(unsigned char * buf);
209 
210  bool primary_key_is_clustered()
211  {
212  return realCursor->primary_key_is_clustered();
213  }
214 
215 
216  int doOpen(const identifier::Table &identifier, int mode, uint32_t test_if_locked);
217 
219  THR_LOCK_DATA **to,
220  enum thr_lock_type);
221 
222  int external_lock(Session *session, int lock_type);
223 
224  int doInsertRecord(unsigned char *buf)
225  {
226  static int i=0;
227  CURSOR_NEW_STATE("::doInsertRecord()");
228 
229  if (error_injected == 1 && (i++ % 2))
230  {
231  user_session->markTransactionForRollback(false);
232  return HA_ERR_LOCK_WAIT_TIMEOUT;
233  }
234 
235  if (error_injected == 2 && (i++ % 2))
236  {
237  user_session->markTransactionForRollback(true);
238  return HA_ERR_LOCK_DEADLOCK;
239  }
240 
241  return realCursor->doInsertRecord(buf);
242  }
243 
244  int doUpdateRecord(const unsigned char *old_row, unsigned char *new_row)
245  {
246  CURSOR_NEW_STATE("::doUpdateRecord()");
247  return realCursor->doUpdateRecord(old_row, new_row);
248  }
249 
250  double scan_time()
251  {
252  CURSOR_NEW_STATE("::scan_time()");
253  CURSOR_NEW_STATE("locked");
254  return realCursor->scan_time();
255  }
256 
257  int extra(enum ha_extra_function operation)
258  {
259  return realCursor->extra(operation);
260  }
261 
262 private:
263  string cursor_state;
264  void CURSOR_NEW_STATE(const string &new_state);
265  Session* user_session;
266  uint64_t id;
267 };
268 
269 int SEAPITesterCursor::doOpen(const identifier::Table &identifier, int mode, uint32_t test_if_locked)
270 {
271  CURSOR_NEW_STATE("::doOpen()");
272 
273  int r= realCursor->doOpen(identifier, mode, test_if_locked);
274 
275  ref_length= realCursor->ref_length;
276 
277  return r;
278 }
279 
281 {
282  CURSOR_NEW_STATE("::reset()");
283  CURSOR_NEW_STATE("::doOpen()");
284 
285  return realCursor->reset();
286 }
287 
288 int SEAPITesterCursor::close()
289 {
290  CURSOR_NEW_STATE("::close()");
291  CURSOR_NEW_STATE("Cursor()");
292 
293  return realCursor->close();
294 }
295 
296 void SEAPITesterCursor::position(const unsigned char *record)
297 {
298  CURSOR_NEW_STATE("::position()");
299 
300  /* We need to use the correct buffer for upper layer */
301  realCursor->ref= ref;
302 
303  realCursor->position(record);
304 }
305 
306 int SEAPITesterCursor::info(uint32_t flag)
307 {
308  int r;
309  CURSOR_NEW_STATE("::info()");
310  CURSOR_NEW_STATE("locked");
311 
312  r= realCursor->info(flag);
313 
314  if (flag & (HA_STATUS_VARIABLE|HA_STATUS_AUTO|HA_STATUS_CONST))
315  {
316  stats= realCursor->stats;
317  }
318 
319  if (flag & HA_STATUS_ERRKEY)
320  errkey= realCursor->errkey;
321 
322  return r;
323 }
324 
325 const char * SEAPITesterCursor::index_type(uint32_t key_number)
326 {
327  CURSOR_NEW_STATE("::index_type()");
328  return realCursor->index_type(key_number);
329 }
330 
331 int SEAPITesterCursor::doStartIndexScan(uint32_t keynr, bool scan)
332 {
333  int r;
334  CURSOR_NEW_STATE("::doStartIndexScan()");
335 
336  if (error_injected == 4)
337  {
338  CURSOR_NEW_STATE("::doStartIndexScan() ERROR");
339  CURSOR_NEW_STATE("locked");
340  return HA_ERR_LOCK_DEADLOCK;
341  }
342 
343  r= realCursor->doStartIndexScan(keynr, scan);
344 
345  active_index= realCursor->get_index();
346 
347  return r;
348 }
349 
350 int SEAPITesterCursor::index_read(unsigned char *buf,
351  const unsigned char *key_ptr,
352  uint32_t key_len,
353  drizzled::ha_rkey_function find_flag)
354 {
355  CURSOR_NEW_STATE("::index_read()");
356  CURSOR_NEW_STATE("::doStartIndexScan()");
357  return realCursor->index_read(buf, key_ptr, key_len, find_flag);
358 }
359 
361  uint32_t index,
362  const unsigned char * key,
363  drizzled::key_part_map keypart_map,
364  drizzled::ha_rkey_function find_flag)
365 {
366  CURSOR_NEW_STATE("::index_read_idx_map()");
367  CURSOR_NEW_STATE("locked");
368  return realCursor->index_read_idx_map(buf, index, key, keypart_map, find_flag);
369 }
370 
371 int SEAPITesterCursor::index_next(unsigned char * buf)
372 {
373  CURSOR_NEW_STATE("::index_next()");
374  CURSOR_NEW_STATE("::doStartIndexScan()");
375  return realCursor->index_next(buf);
376 }
377 
378 int SEAPITesterCursor::doEndIndexScan()
379 {
380  CURSOR_NEW_STATE("::doEndIndexScan()");
381  CURSOR_NEW_STATE("locked");
382  int r= realCursor->doEndIndexScan();
383 
384  active_index= realCursor->get_index();
385 
386  return r;
387 }
388 
389 int SEAPITesterCursor::index_prev(unsigned char * buf)
390 {
391  CURSOR_NEW_STATE("::index_prev()");
392  CURSOR_NEW_STATE("::doStartIndexScan()");
393  return realCursor->index_prev(buf);
394 }
395 
396 int SEAPITesterCursor::index_first(unsigned char * buf)
397 {
398  CURSOR_NEW_STATE("::index_first()");
399  CURSOR_NEW_STATE("::doStartIndexScan()");
400  return realCursor->index_first(buf);
401 }
402 
403 int SEAPITesterCursor::index_last(unsigned char * buf)
404 {
405  CURSOR_NEW_STATE("::index_last()");
406  CURSOR_NEW_STATE("::doStartIndexScan()");
407  return realCursor->index_last(buf);
408 }
409 
410 int SEAPITesterCursor::external_lock(Session *session, int lock_type)
411 {
412  CURSOR_NEW_STATE("::external_lock()");
413  CURSOR_NEW_STATE("locked");
414 
415  user_session= session;
416 
417  return realCursor->external_lock(session, lock_type);
418 }
419 
421  THR_LOCK_DATA **to,
422  enum thr_lock_type lock_type)
423 
424 {
425  CURSOR_NEW_STATE("::store_lock()");
426 
427  return realCursor->store_lock(session, to, lock_type);
428 }
429 
430 void SEAPITesterCursor::CURSOR_NEW_STATE(const string &new_state)
431 {
432  state_multimap_iter cur= cursor_state_transitions.find(cursor_state);
433  if (cursor_state_transitions.count(cursor_state) == 0)
434  {
435  std::cerr << "ERROR: Invalid Cursor state: " << cursor_state << std::endl
436  << "This should *NEVER* happen."
437  << std::endl
438  << "i.e. you've really screwed it up and you should be ashamed of "
439  << "yourself." << std::endl;
440  assert(cursor_state_transitions.count(cursor_state));
441  }
442 
443  for(cur= cursor_state_transitions.lower_bound(cursor_state);
444  cur != cursor_state_transitions.upper_bound(cursor_state);
445  cur++)
446  {
447  if (new_state.compare((*cur).second) == 0)
448  break;
449  }
450 
451  if (cur == cursor_state_transitions.end()
452  || new_state.compare((*cur).second))
453  {
454  std::cerr << "ERROR: Invalid Cursor state transition!" << std::endl
455  << "Cursor " << this << "Cannot go from "
456  << cursor_state << " to " << new_state << std::endl;
457  assert(false);
458  }
459 
460  cursor_state= new_state;
461 
462  std::string cursor_state_str("Cursor ");
463  char nr[50];
464  snprintf(nr, sizeof(nr), "%"PRIu64, this->id);
465  cursor_state_str.append(nr);
466  cursor_state_str.append(" ");
467  cursor_state_str.append(cursor_state);
468 
469  engine_state_history.push_back(cursor_state_str);
470 
471  std::cerr << "\t\tCursor " << this << " STATE : " << cursor_state << std::endl;
472 }
473 
474 } /* namespace drizzled */
475 
476 static const char *api_tester_exts[] = {
477  NULL
478 };
479 
480 namespace drizzled {
481  namespace plugin {
483 {
484 public:
485  /* BUG: Currently flags are just copy&pasted from innobase. Instead, we
486  need to have a call somewhere.
487  */
488  SEAPITester(const string &name_arg)
490  HTON_NULL_IN_KEY |
491  HTON_CAN_INDEX_BLOBS |
492  HTON_PRIMARY_KEY_IN_READ_INDEX |
493  HTON_PARTIAL_COLUMN_READ |
494  HTON_TABLE_SCAN_ON_INDEX |
495  HTON_HAS_FOREIGN_KEYS |
496  HTON_HAS_DOES_TRANSACTIONS)
497  {
498  ENGINE_NEW_STATE("::SEAPITester()");
499  }
500 
501  ~SEAPITester()
502  {
503  ENGINE_NEW_STATE("::~SEAPITester()");
504  }
505 
506  const char **bas_ext() const {
507  return api_tester_exts;
508  }
509 
510  virtual Cursor *create(Table &table)
511  {
512  SEAPITesterCursor *c= new SEAPITesterCursor(*this, table);
513  Cursor *realCursor= getRealEngine()->create(table);
514  c->realCursor= realCursor;
515 
516  return c;
517  }
518 
519  int doCreateTable(Session&,
520  Table&,
521  const drizzled::identifier::Table &identifier,
522  const drizzled::message::Table& create_proto);
523 
524  int doDropTable(Session&, const identifier::Table &identifier);
525 
526  int doRenameTable(drizzled::Session& session,
527  const drizzled::identifier::Table& from,
528  const drizzled::identifier::Table& to)
529  { return getRealEngine()->renameTable(session, from, to); }
530 
531  int doGetTableDefinition(Session& ,
532  const identifier::Table &,
534 
535  bool doDoesTableExist(Session&, const identifier::Table &identifier);
536 
537  void doGetTableIdentifiers(drizzled::CachedDirectory &,
539  drizzled::identifier::table::vector &);
540 
541  virtual int doStartTransaction(Session *session,
542  start_transaction_option_t options);
543  virtual void doStartStatement(Session *session);
544  virtual void doEndStatement(Session *session);
545 
546  virtual int doSetSavepoint(Session*,
548  {
549  ENGINE_NEW_STATE("SET SAVEPOINT");
550  ENGINE_NEW_STATE("In Transaction");
551  return 0; }
552  virtual int doRollbackToSavepoint(Session*,
554  {
555  ENGINE_NEW_STATE("ROLLBACK TO SAVEPOINT");
556  ENGINE_NEW_STATE("In Transaction");
557  return 0; }
558  virtual int doReleaseSavepoint(Session*,
560  {
561  ENGINE_NEW_STATE("RELEASE SAVEPOINT");
562  ENGINE_NEW_STATE("In Transaction");
563  return 0; }
564  virtual int doCommit(Session*, bool);
565 
566  virtual int doRollback(Session*, bool);
567 
568  uint32_t max_supported_record_length(void) const {
569  ENGINE_NEW_STATE("::max_supported_record_length()");
570  return getRealEngine()->max_supported_record_length();
571  }
572 
573  uint32_t max_supported_keys(void) const {
574  ENGINE_NEW_STATE("::max_supported_keys()");
575  return getRealEngine()->max_supported_keys();
576  }
577 
578  uint32_t max_supported_key_parts(void) const {
579  ENGINE_NEW_STATE("::max_supported_key_parts()");
580  return getRealEngine()->max_supported_key_parts();
581  }
582 
583  uint32_t max_supported_key_length(void) const {
584  ENGINE_NEW_STATE("::max_supported_key_length()");
585  return getRealEngine()->max_supported_key_length();
586  }
587 
588  uint32_t max_supported_key_part_length(void) const {
589  ENGINE_NEW_STATE("::max_supported_key_part_length()");
590  return getRealEngine()->max_supported_key_part_length();
591  }
592 
593  /* just copied from innobase... */
594  uint32_t index_flags(enum ha_key_alg) const
595  {
596  return (HA_READ_NEXT |
597  HA_READ_PREV |
598  HA_READ_RANGE |
599  HA_READ_ORDER |
600  HA_KEYREAD_ONLY);
601  }
602 
603 };
604 
605 bool SEAPITester::doDoesTableExist(Session &session, const identifier::Table &identifier)
606 {
607  return getRealEngine()->doDoesTableExist(session, identifier);
608 }
609 
610 void SEAPITester::doGetTableIdentifiers(drizzled::CachedDirectory &cd,
612  drizzled::identifier::table::vector &ti)
613 {
614  return getRealEngine()->doGetTableIdentifiers(cd, si, ti);
615 }
616 
617 int SEAPITester::doCreateTable(Session& session,
618  Table& table,
619  const drizzled::identifier::Table &identifier,
620  const drizzled::message::Table& create_proto)
621 {
622  ENGINE_NEW_STATE("::doCreateTable()");
623 
624  int r= getRealEngine()->doCreateTable(session, table, identifier, create_proto);
625 
626  ENGINE_NEW_STATE("::SEAPITester()");
627  return r;
628 }
629 
630 int SEAPITester::doDropTable(Session& session, const identifier::Table &identifier)
631 {
632  return getRealEngine()->doDropTable(session, identifier);
633 }
634 
635 int SEAPITester::doGetTableDefinition(Session& session,
636  const identifier::Table &identifier,
638 {
639  return getRealEngine()->doGetTableDefinition(session, identifier, table);
640 }
641 
642 int SEAPITester::doStartTransaction(Session *session,
643  start_transaction_option_t opt)
644 {
645  ENGINE_NEW_STATE("BEGIN");
646  ENGINE_NEW_STATE("In Transaction");
647 
648  return getRealEngine()->startTransaction(session, opt);
649 }
650 
651 void SEAPITester::doStartStatement(Session *session)
652 {
653  ENGINE_NEW_STATE("START STATEMENT");
654  return getRealEngine()->startStatement(session);
655 }
656 
657 void SEAPITester::doEndStatement(Session *session)
658 {
659  ENGINE_NEW_STATE("END STATEMENT");
660  return getRealEngine()->endStatement(session);
661 }
662 
663 int SEAPITester::doCommit(Session *session, bool all)
664 {
665  if (all)
666  {
667  ENGINE_NEW_STATE("COMMIT");
668  ENGINE_NEW_STATE("::SEAPITester()");
669  }
670  else
671  {
672  ENGINE_NEW_STATE("COMMIT STATEMENT");
673  ENGINE_NEW_STATE("In Transaction");
674  }
675  return getRealEngine()->commit(session, all);
676 }
677 
678 int SEAPITester::doRollback(Session *session, bool all)
679 {
680  if (all)
681  {
682  ENGINE_NEW_STATE("ROLLBACK");
683  ENGINE_NEW_STATE("::SEAPITester()");
684  }
685  else
686  {
687  ENGINE_NEW_STATE("ROLLBACK STATEMENT");
688  ENGINE_NEW_STATE("In Transaction");
689  }
690 
691  return getRealEngine()->rollback(session, all);
692 }
693 
694  } /* namespace plugin */
695 } /* namespace drizzled */
696 
697 static int seapi_tester_init(drizzled::module::Context &context)
698 {
699  load_engine_state_transitions(engine_state_transitions);
700  load_cursor_state_transitions(cursor_state_transitions);
701  engine_state= "INIT";
702 
703  context.add(new plugin::SEAPITester(engine_name));
704 
705  context.add(new plugin::Create_function<SEAPITesterErrorInjectFunc>("seapitester_error_inject"));
706 
707  engine_state_history_table_initialize(context);
708 
709  return 0;
710 }
711 
712 DRIZZLE_DECLARE_PLUGIN
713 {
714  DRIZZLE_VERSION_ID,
715  "SEAPITESTER",
716  "1.0",
717  "Stewart Smith",
718  N_("StorageEngine module for testing call order"),
719  PLUGIN_LICENSE_GPL,
720  seapi_tester_init,
721  NULL,
722  NULL
723 }
724 DRIZZLE_DECLARE_PLUGIN_END;
virtual int doSetSavepoint(Session *, drizzled::NamedSavepoint &)
virtual int index_read_idx_map(unsigned char *buf, uint32_t index, const unsigned char *key, key_part_map keypart_map, enum ha_rkey_function find_flag)
Positions an index cursor to the index specified in the handle. Fetches the row if available...
Definition: cursor.cc:1202
int external_lock(Session *session, int lock_type)
virtual int external_lock(Session *, int)
Definition: cursor.h:551
THR_LOCK_DATA ** store_lock(Session *, THR_LOCK_DATA **to, enum thr_lock_type)
virtual int reset()
Definition: cursor.h:527
int index_read_idx_map(unsigned char *buf, uint32_t index, const unsigned char *key, drizzled::key_part_map keypart_map, drizzled::ha_rkey_function find_flag)
Positions an index cursor to the index specified in the handle. Fetches the row if available...
uint32_t ref_length
Definition: cursor.h:159
virtual THR_LOCK_DATA ** store_lock(Session *, THR_LOCK_DATA **to, enum thr_lock_type)
Definition: cursor.h:453