Drizzled Public API Documentation

storage_engine.cc
1 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3  *
4  * Copyright (C) 2008 Sun Microsystems, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <config.h>
21 
22 #include <fcntl.h>
23 #include <unistd.h>
24 
25 #include <string>
26 #include <vector>
27 #include <set>
28 #include <fstream>
29 #include <algorithm>
30 #include <functional>
31 
32 #include <google/protobuf/io/zero_copy_stream.h>
33 #include <google/protobuf/io/zero_copy_stream_impl.h>
34 
36 #include <drizzled/definitions.h>
37 #include <drizzled/base.h>
38 #include <drizzled/cursor.h>
39 #include <drizzled/plugin/storage_engine.h>
40 #include <drizzled/session.h>
41 #include <drizzled/error.h>
42 #include <drizzled/gettext.h>
43 #include <drizzled/data_home.h>
44 #include <drizzled/errmsg_print.h>
45 #include <drizzled/xid.h>
46 #include <drizzled/sql_table.h>
47 #include <drizzled/charset.h>
48 #include <drizzled/internal/my_sys.h>
49 #include <drizzled/table_proto.h>
50 #include <drizzled/plugin/event_observer.h>
51 #include <drizzled/table/shell.h>
52 #include <drizzled/message/cache.h>
53 #include <drizzled/key.h>
54 #include <drizzled/session/transactions.h>
55 #include <drizzled/open_tables_state.h>
56 
57 #include <boost/algorithm/string/compare.hpp>
58 
59 static bool shutdown_has_begun= false; // Once we put in the container for the vector/etc for engines this will go away.
60 
61 namespace drizzled {
62 namespace plugin {
63 
64 static EngineVector g_engines;
65 static EngineVector g_schema_engines;
66 
67 const std::string DEFAULT_STRING("default");
68 const std::string UNKNOWN_STRING("UNKNOWN");
69 const std::string DEFAULT_DEFINITION_FILE_EXT(".dfe");
70 
71 static std::set<std::string> set_of_table_definition_ext;
72 
73 EngineVector &StorageEngine::getSchemaEngines()
74 {
75  return g_schema_engines;
76 }
77 
78 StorageEngine::StorageEngine(const std::string &name_arg,
79  const std::bitset<HTON_BIT_SIZE> &flags_arg) :
80  Plugin(name_arg, "StorageEngine"),
81  MonitoredInTransaction(), /* This gives the storage engine a "slot" or ID */
82  flags(flags_arg)
83 {
84 }
85 
86 StorageEngine::~StorageEngine()
87 {
88 }
89 
90 void StorageEngine::setTransactionReadWrite(Session& session)
91 {
92  TransactionContext &statement_ctx= session.transaction.stmt;
93  statement_ctx.markModifiedNonTransData();
94 }
95 
96 int StorageEngine::renameTable(Session &session, const identifier::Table &from, const identifier::Table &to)
97 {
98  setTransactionReadWrite(session);
99  if (unlikely(plugin::EventObserver::beforeRenameTable(session, from, to)))
100  return ER_EVENT_OBSERVER_PLUGIN;
101  int error= doRenameTable(session, from, to);
102  if (unlikely(plugin::EventObserver::afterRenameTable(session, from, to, error)))
103  error= ER_EVENT_OBSERVER_PLUGIN;
104  return error;
105 }
106 
123 {
124  int error= 0;
125  int enoent_or_zero= ENOENT; // Error if no file was deleted
126  char buff[FN_REFLEN];
127 
128  for (const char **ext= bas_ext(); *ext ; ext++)
129  {
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)))
133  {
134  if ((error= errno) != ENOENT)
135  break;
136  }
137  else
138  {
139  enoent_or_zero= 0; // No error for ENOENT
140  }
141 
142  error= enoent_or_zero;
143  }
144  return error;
145 }
146 
147 bool StorageEngine::addPlugin(StorageEngine *engine)
148 {
149  g_engines.push_back(engine);
150 
151  if (engine->getTableDefinitionFileExtension().length())
152  {
153  assert(engine->getTableDefinitionFileExtension().length() == DEFAULT_DEFINITION_FILE_EXT.length());
154  set_of_table_definition_ext.insert(engine->getTableDefinitionFileExtension());
155  }
156 
157  if (engine->check_flag(HTON_BIT_SCHEMA_DICTIONARY))
158  g_schema_engines.push_back(engine);
159 
160  return false;
161 }
162 
163 void StorageEngine::removePlugin(StorageEngine *)
164 {
165  if (shutdown_has_begun)
166  return;
167  shutdown_has_begun= true;
168  g_engines.clear();
169  g_schema_engines.clear();
170 }
171 
172 StorageEngine *StorageEngine::findByName(const std::string &predicate)
173 {
174  BOOST_FOREACH(EngineVector::reference it, g_engines)
175  {
176  if (not boost::iequals(it->getName(), predicate))
177  continue;
178  if (it->is_user_selectable())
179  return it;
180  break;
181  }
182  return NULL;
183 }
184 
185 StorageEngine *StorageEngine::findByName(Session& session, const std::string &predicate)
186 {
187  if (boost::iequals(predicate, DEFAULT_STRING))
188  return session.getDefaultStorageEngine();
189  return findByName(predicate);
190 }
191 
197 {
198  BOOST_FOREACH(EngineVector::reference it, g_engines)
199  {
200  if (*session.getEngineData(it))
201  it->close_connection(&session);
202  }
203 }
204 
205 bool StorageEngine::flushLogs(StorageEngine *engine)
206 {
207  if (not engine)
208  {
209  if (std::find_if(g_engines.begin(), g_engines.end(), std::mem_fun(&StorageEngine::flush_logs))
210  != g_engines.begin()) // Shouldn't this be .end()?
211  return true;
212  }
213  else if (engine->flush_logs())
214  return true;
215  return false;
216 }
217 
218 class StorageEngineGetTableDefinition: public std::unary_function<StorageEngine *,bool>
219 {
220  Session& session;
221  const identifier::Table &identifier;
222  message::Table &table_message;
223  drizzled::error_t &err;
224 
225 public:
227  const identifier::Table &identifier_arg,
228  message::Table &table_message_arg,
229  drizzled::error_t &err_arg) :
230  session(session_arg),
231  identifier(identifier_arg),
232  table_message(table_message_arg),
233  err(err_arg) {}
234 
235  result_type operator() (argument_type engine)
236  {
237  int ret= engine->doGetTableDefinition(session, identifier, table_message);
238 
239  if (ret != ENOENT)
240  err= static_cast<drizzled::error_t>(ret);
241 
242  return err == static_cast<drizzled::error_t>(EEXIST) or err != static_cast<drizzled::error_t>(ENOENT);
243  }
244 };
245 
250  const identifier::Table &identifier,
251  bool include_temporary_tables)
252 {
253  if (include_temporary_tables && session.open_tables.doDoesTableExist(identifier))
254  return true;
255  BOOST_FOREACH(EngineVector::reference it, g_engines)
256  {
257  if (it->doDoesTableExist(session, identifier))
258  return true;
259  }
260  return false;
261 }
262 
263 bool plugin::StorageEngine::doDoesTableExist(Session&, const drizzled::identifier::Table&)
264 {
265  std::cerr << " Engine was called for doDoesTableExist() and does not implement it: " << getName() << "\n";
266  assert(0);
267  return false;
268 }
269 
270 message::table::shared_ptr StorageEngine::getTableMessage(Session& session,
271  const identifier::Table& identifier,
272  bool include_temporary_tables)
273 {
274  drizzled::error_t error= static_cast<drizzled::error_t>(ENOENT);
275  if (include_temporary_tables)
276  {
277  if (Table *table= session.open_tables.find_temporary_table(identifier))
278  {
279  return message::table::shared_ptr(new message::Table(*table->getShare()->getTableMessage()));
280  }
281  }
282 
283  drizzled::message::table::shared_ptr table_ptr;
284  if ((table_ptr= drizzled::message::Cache::singleton().find(identifier)))
285  {
286  (void)table_ptr;
287  }
288 
289  message::Table message;
290  EngineVector::iterator iter= std::find_if(g_engines.begin(), g_engines.end(),
291  StorageEngineGetTableDefinition(session, identifier, message, error));
292 
293  if (iter == g_engines.end())
294  {
295  return message::table::shared_ptr();
296  }
297  message::table::shared_ptr table_message(new message::Table(message));
298 
299  drizzled::message::Cache::singleton().insert(identifier, table_message);
300 
301  return table_message;
302 }
303 
304 class DropTableByIdentifier: public std::unary_function<EngineVector::value_type, bool>
305 {
306  Session& session;
307  const identifier::Table& identifier;
308  drizzled::error_t &error;
309 
310 public:
311 
312  DropTableByIdentifier(Session& session_arg,
313  const identifier::Table& identifier_arg,
314  drizzled::error_t &error_arg) :
315  session(session_arg),
316  identifier(identifier_arg),
317  error(error_arg)
318  { }
319 
320  result_type operator() (argument_type engine)
321  {
322  if (not engine->doDoesTableExist(session, identifier))
323  return false;
324 
325  int local_error= engine->doDropTable(session, identifier);
326 
327 
328  if (not local_error)
329  return true;
330 
331  switch (local_error)
332  {
333  case HA_ERR_NO_SUCH_TABLE:
334  case ENOENT:
335  error= static_cast<drizzled::error_t>(HA_ERR_NO_SUCH_TABLE);
336  return false;
337 
338  default:
339  error= static_cast<drizzled::error_t>(local_error);
340  return true;
341  }
342  }
343 };
344 
345 
346 bool StorageEngine::dropTable(Session& session,
347  const identifier::Table& identifier,
348  drizzled::error_t &error)
349 {
350  error= EE_OK;
351 
352  EngineVector::const_iterator iter= std::find_if(g_engines.begin(), g_engines.end(),
353  DropTableByIdentifier(session, identifier, error));
354 
355  if (error)
356  {
357  return false;
358  }
359  else if (iter == g_engines.end())
360  {
361  error= ER_BAD_TABLE_ERROR;
362  return false;
363  }
364 
365  drizzled::message::Cache::singleton().erase(identifier);
366 
367  return true;
368 }
369 
370 bool StorageEngine::dropTable(Session& session,
371  const identifier::Table &identifier)
372 {
373  drizzled::error_t error;
374  return dropTable(session, identifier, error);
375 }
376 
377 bool StorageEngine::dropTable(Session& session,
378  StorageEngine &engine,
379  const identifier::Table& identifier,
380  drizzled::error_t &error)
381 {
382  error= EE_OK;
383  engine.setTransactionReadWrite(session);
384 
385  assert(identifier.isTmp());
386 
387  if (unlikely(plugin::EventObserver::beforeDropTable(session, identifier)))
388  {
389  error= ER_EVENT_OBSERVER_PLUGIN;
390  }
391  else
392  {
393  error= static_cast<drizzled::error_t>(engine.doDropTable(session, identifier));
394 
395  if (unlikely(plugin::EventObserver::afterDropTable(session, identifier, error)))
396  {
397  error= ER_EVENT_OBSERVER_PLUGIN;
398  }
399  }
400  drizzled::message::Cache::singleton().erase(identifier);
401  return not error;
402 }
403 
404 
414  const identifier::Table &identifier,
415  message::Table& table_message)
416 {
417  drizzled::error_t error= EE_OK;
418 
419  TableShare share(identifier);
420  table::Shell table(share);
421  message::Table tmp_proto;
422 
423  if (share.parse_table_proto(session, table_message) || share.open_table_from_share(&session, identifier, "", 0, 0, table))
424  {
425  // @note Error occured, we should probably do a little more here.
426  // ER_CORRUPT_TABLE_DEFINITION,ER_CORRUPT_TABLE_DEFINITION_ENUM
427 
428  my_error(ER_CORRUPT_TABLE_DEFINITION_UNKNOWN, identifier);
429 
430  return false;
431  }
432  else
433  {
434  /* Check for legal operations against the Engine using the proto (if used) */
435  if (table_message.type() == message::Table::TEMPORARY &&
436  share.storage_engine->check_flag(HTON_BIT_TEMPORARY_NOT_SUPPORTED) == true)
437  {
438  error= HA_ERR_UNSUPPORTED;
439  }
440  else if (table_message.type() != message::Table::TEMPORARY &&
441  share.storage_engine->check_flag(HTON_BIT_TEMPORARY_ONLY) == true)
442  {
443  error= HA_ERR_UNSUPPORTED;
444  }
445  else
446  {
447  share.storage_engine->setTransactionReadWrite(session);
448 
449  error= static_cast<drizzled::error_t>(share.storage_engine->doCreateTable(session,
450  table,
451  identifier,
452  table_message));
453  }
454 
455  if (error == ER_TABLE_PERMISSION_DENIED)
456  my_error(ER_TABLE_PERMISSION_DENIED, identifier);
457  else if (error)
458  my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), identifier.getSQLPath().c_str(), error);
459  table.delete_table();
460  }
461  return(error == EE_OK);
462 }
463 
464 Cursor *StorageEngine::getCursor(Table &arg)
465 {
466  return create(arg);
467 }
468 
469 void StorageEngine::getIdentifiers(Session &session, const identifier::Schema &schema_identifier, identifier::table::vector &set_of_identifiers)
470 {
471  CachedDirectory directory(schema_identifier.getPath(), set_of_table_definition_ext);
472 
473  if (schema_identifier == INFORMATION_SCHEMA_IDENTIFIER)
474  {
475  }
476  else if (schema_identifier == DATA_DICTIONARY_IDENTIFIER)
477  {
478  }
479  else if (directory.fail())
480  {
481  errno= directory.getError();
482  if (errno == ENOENT)
483  my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), schema_identifier.getSQLPath().c_str());
484  else
485  my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), directory.getPath(), errno);
486  return;
487  }
488 
489  BOOST_FOREACH(EngineVector::reference it, g_engines)
490  it->doGetTableIdentifiers(directory, schema_identifier, set_of_identifiers);
491 
492  session.open_tables.doGetTableIdentifiers(directory, schema_identifier, set_of_identifiers);
493 }
494 
495 class DropTable: public std::unary_function<identifier::Table&, bool>
496 {
497  Session &session;
498  StorageEngine *engine;
499 
500 public:
501 
502  DropTable(Session &session_arg, StorageEngine *engine_arg) :
503  session(session_arg),
504  engine(engine_arg)
505  { }
506 
507  result_type operator() (argument_type identifier)
508  {
509  return engine->doDropTable(session, identifier) == 0;
510  }
511 };
512 
513 /*
514  This only works for engines which use file based DFE.
515 
516  Note-> Unlike MySQL, we do not, on purpose, delete files that do not match any engines.
517 */
518 void StorageEngine::removeLostTemporaryTables(Session &session, const char *directory)
519 {
520  CachedDirectory dir(directory, set_of_table_definition_ext);
521  identifier::table::vector table_identifiers;
522 
523  if (dir.fail())
524  {
525  errno= dir.getError();
526  my_error(ER_CANT_READ_DIR, MYF(0), directory, errno);
527 
528  return;
529  }
530 
531  CachedDirectory::Entries files= dir.getEntries();
532 
533  for (CachedDirectory::Entries::iterator fileIter= files.begin();
534  fileIter != files.end(); fileIter++)
535  {
536  size_t length;
537  std::string path;
538  CachedDirectory::Entry *entry= *fileIter;
539 
540  /* We remove the file extension. */
541  length= entry->filename.length();
542  entry->filename.resize(length - DEFAULT_DEFINITION_FILE_EXT.length());
543 
544  path+= directory;
545  path+= FN_LIBCHAR;
546  path+= entry->filename;
547  message::Table definition;
548  if (StorageEngine::readTableFile(path, definition))
549  {
550  identifier::Table identifier(definition.schema(), definition.name(), path);
551  table_identifiers.push_back(identifier);
552  }
553  }
554 
555  BOOST_FOREACH(EngineVector::reference it, g_engines)
556  {
557  table_identifiers.erase(std::remove_if(table_identifiers.begin(), table_identifiers.end(), DropTable(session, it)),
558  table_identifiers.end());
559  }
560 
561  /*
562  Now we just clean up anything that might left over.
563 
564  We rescan because some of what might have been there should
565  now be all nice and cleaned up.
566  */
567  std::set<std::string> all_exts= set_of_table_definition_ext;
568 
569  for (EngineVector::iterator iter= g_engines.begin();
570  iter != g_engines.end() ; iter++)
571  {
572  for (const char **ext= (*iter)->bas_ext(); *ext ; ext++)
573  all_exts.insert(*ext);
574  }
575 
576  CachedDirectory rescan(directory, all_exts);
577 
578  files= rescan.getEntries();
579  for (CachedDirectory::Entries::iterator fileIter= files.begin();
580  fileIter != files.end(); fileIter++)
581  {
582  std::string path;
583  CachedDirectory::Entry *entry= *fileIter;
584 
585  path+= directory;
586  path+= FN_LIBCHAR;
587  path+= entry->filename;
588 
589  unlink(path.c_str());
590  }
591 }
592 
593 
603 void StorageEngine::print_error(int error, myf errflag, const Table &table) const
604 {
605  drizzled::error_t textno= ER_GET_ERRNO;
606  switch (error) {
607  case EACCES:
608  textno=ER_OPEN_AS_READONLY;
609  break;
610  case EAGAIN:
611  textno=ER_FILE_USED;
612  break;
613  case ENOENT:
614  textno=ER_FILE_NOT_FOUND;
615  break;
616  case HA_ERR_KEY_NOT_FOUND:
617  case HA_ERR_NO_ACTIVE_RECORD:
618  case HA_ERR_END_OF_FILE:
619  textno=ER_KEY_NOT_FOUND;
620  break;
621  case HA_ERR_WRONG_MRG_TABLE_DEF:
622  textno=ER_WRONG_MRG_TABLE;
623  break;
624  case HA_ERR_FOUND_DUPP_KEY:
625  {
626  uint32_t key_nr= table.get_dup_key(error);
627  if ((int) key_nr >= 0)
628  {
629  const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
630 
631  print_keydup_error(key_nr, err_msg, table);
632 
633  return;
634  }
635  textno=ER_DUP_KEY;
636  break;
637  }
638  case HA_ERR_FOREIGN_DUPLICATE_KEY:
639  {
640  uint32_t key_nr= table.get_dup_key(error);
641  if ((int) key_nr >= 0)
642  {
643  uint32_t max_length;
644 
645  /* Write the key in the error message */
646  char key[MAX_KEY_LENGTH];
647  String str(key,sizeof(key),system_charset_info);
648 
649  /* Table is opened and defined at this point */
650  key_unpack(&str, &table,(uint32_t) key_nr);
651  max_length= (DRIZZLE_ERRMSG_SIZE-
652  (uint32_t) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
653  if (str.length() >= max_length)
654  {
655  str.length(max_length-4);
656  str.append(STRING_WITH_LEN("..."));
657  }
658  my_error(ER_FOREIGN_DUPLICATE_KEY, MYF(0), table.getShare()->getTableName(),
659  str.c_ptr(), key_nr+1);
660  return;
661  }
662  textno= ER_DUP_KEY;
663  break;
664  }
665  case HA_ERR_FOUND_DUPP_UNIQUE:
666  textno=ER_DUP_UNIQUE;
667  break;
668  case HA_ERR_RECORD_CHANGED:
669  textno=ER_CHECKREAD;
670  break;
671  case HA_ERR_CRASHED:
672  textno=ER_NOT_KEYFILE;
673  break;
674  case HA_ERR_WRONG_IN_RECORD:
675  textno= ER_CRASHED_ON_USAGE;
676  break;
677  case HA_ERR_CRASHED_ON_USAGE:
678  textno=ER_CRASHED_ON_USAGE;
679  break;
680  case HA_ERR_NOT_A_TABLE:
681  textno= static_cast<drizzled::error_t>(error);
682  break;
683  case HA_ERR_CRASHED_ON_REPAIR:
684  textno=ER_CRASHED_ON_REPAIR;
685  break;
686  case HA_ERR_OUT_OF_MEM:
687  textno=ER_OUT_OF_RESOURCES;
688  break;
689  case HA_ERR_WRONG_COMMAND:
690  textno=ER_ILLEGAL_HA;
691  break;
692  case HA_ERR_OLD_FILE:
693  textno=ER_OLD_KEYFILE;
694  break;
695  case HA_ERR_UNSUPPORTED:
696  textno=ER_UNSUPPORTED_EXTENSION;
697  break;
698  case HA_ERR_RECORD_FILE_FULL:
699  case HA_ERR_INDEX_FILE_FULL:
700  textno=ER_RECORD_FILE_FULL;
701  break;
702  case HA_ERR_LOCK_WAIT_TIMEOUT:
703  textno=ER_LOCK_WAIT_TIMEOUT;
704  break;
705  case HA_ERR_LOCK_TABLE_FULL:
706  textno=ER_LOCK_TABLE_FULL;
707  break;
708  case HA_ERR_LOCK_DEADLOCK:
709  textno=ER_LOCK_DEADLOCK;
710  break;
711  case HA_ERR_READ_ONLY_TRANSACTION:
712  textno=ER_READ_ONLY_TRANSACTION;
713  break;
714  case HA_ERR_CANNOT_ADD_FOREIGN:
715  textno=ER_CANNOT_ADD_FOREIGN;
716  break;
717  case HA_ERR_ROW_IS_REFERENCED:
718  {
719  String str;
720  get_error_message(error, &str);
721  my_error(ER_ROW_IS_REFERENCED_2, MYF(0), str.c_str());
722  return;
723  }
724  case HA_ERR_NO_REFERENCED_ROW:
725  {
726  String str;
727  get_error_message(error, &str);
728  my_error(ER_NO_REFERENCED_ROW_2, MYF(0), str.c_str());
729  return;
730  }
731  case HA_ERR_TABLE_DEF_CHANGED:
732  textno=ER_TABLE_DEF_CHANGED;
733  break;
734  case HA_ERR_NO_SUCH_TABLE:
735  {
736  identifier::Table identifier(table.getShare()->getSchemaName(), table.getShare()->getTableName());
737  my_error(ER_TABLE_UNKNOWN, identifier);
738  return;
739  }
740  case HA_ERR_LOG_ROW_FOR_REPLICATION_FAILED:
741  textno= ER_LOG_ROW_FOR_REPLICATION_FAILED;
742  break;
743  case HA_ERR_DROP_INDEX_FK:
744  {
745  const char *ptr= "???";
746  uint32_t key_nr= table.get_dup_key(error);
747  if ((int) key_nr >= 0)
748  ptr= table.key_info[key_nr].name;
749  my_error(ER_DROP_INDEX_FK, MYF(0), ptr);
750  return;
751  }
752  case HA_ERR_TABLE_NEEDS_UPGRADE:
753  textno=ER_TABLE_NEEDS_UPGRADE;
754  break;
755  case HA_ERR_TABLE_READONLY:
756  textno= ER_OPEN_AS_READONLY;
757  break;
758  case HA_ERR_AUTOINC_READ_FAILED:
759  textno= ER_AUTOINC_READ_FAILED;
760  break;
761  case HA_ERR_AUTOINC_ERANGE:
762  textno= ER_WARN_DATA_OUT_OF_RANGE;
763  break;
764  case HA_ERR_LOCK_OR_ACTIVE_TRANSACTION:
765  my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
766  return;
767  default:
768  {
769  /*
770  The error was "unknown" to this function.
771  Ask Cursor if it has got a message for this error
772  */
773  bool temporary= false;
774  String str;
775  temporary= get_error_message(error, &str);
776  if (!str.empty())
777  {
778  const char* engine_name= getName().c_str();
779  if (temporary)
780  my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(), engine_name);
781  else
782  my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine_name);
783  }
784  else
785  {
786  my_error(ER_GET_ERRNO,errflag,error);
787  }
788  return;
789  }
790  }
791 
792  my_error(textno, errflag, table.getShare()->getTableName(), error);
793 }
794 
795 
806 {
807  return false;
808 }
809 
810 
811 void StorageEngine::print_keydup_error(uint32_t key_nr, const char *msg, const Table &table) const
812 {
813  /* Write the duplicated key in the error message */
814  char key[MAX_KEY_LENGTH];
815  String str(key,sizeof(key),system_charset_info);
816 
817  if (key_nr == MAX_KEY)
818  {
819  /* Key is unknown */
820  str.copy("", 0, system_charset_info);
821  my_printf_error(ER_DUP_ENTRY, msg, MYF(0), str.c_ptr(), "*UNKNOWN*");
822  }
823  else
824  {
825  /* Table is opened and defined at this point */
826  key_unpack(&str, &table, (uint32_t) key_nr);
827  uint32_t max_length=DRIZZLE_ERRMSG_SIZE-(uint32_t) strlen(msg);
828  if (str.length() >= max_length)
829  {
830  str.length(max_length-4);
831  str.append(STRING_WITH_LEN("..."));
832  }
833  my_printf_error(ER_DUP_ENTRY, msg,
834  MYF(0), str.c_ptr(), table.key_info[key_nr].name);
835  }
836 }
837 
838 
839 int StorageEngine::deleteDefinitionFromPath(const identifier::Table &identifier)
840 {
841  std::string path(identifier.getPath());
842 
843  path.append(DEFAULT_DEFINITION_FILE_EXT);
844 
845  return internal::my_delete(path.c_str(), MYF(0));
846 }
847 
848 int StorageEngine::renameDefinitionFromPath(const identifier::Table &dest, const identifier::Table &src)
849 {
850  message::Table table_message;
851  std::string src_path(src.getPath());
852  std::string dest_path(dest.getPath());
853 
854  src_path.append(DEFAULT_DEFINITION_FILE_EXT);
855  dest_path.append(DEFAULT_DEFINITION_FILE_EXT);
856 
857  bool was_read= StorageEngine::readTableFile(src_path.c_str(), table_message);
858 
859  if (not was_read)
860  {
861  return ENOENT;
862  }
863 
864  dest.copyToTableMessage(table_message);
865 
866  int error= StorageEngine::writeDefinitionFromPath(dest, table_message);
867 
868  if (not error)
869  {
870  if (unlink(src_path.c_str()))
871  perror(src_path.c_str());
872  }
873 
874  return error;
875 }
876 
877 int StorageEngine::writeDefinitionFromPath(const identifier::Table &identifier, const message::Table &table_message)
878 {
879  char definition_file_tmp[FN_REFLEN];
880  std::string file_name(identifier.getPath());
881 
882  file_name.append(DEFAULT_DEFINITION_FILE_EXT);
883 
884  snprintf(definition_file_tmp, sizeof(definition_file_tmp), "%sXXXXXX", file_name.c_str());
885 
886  int fd= mkstemp(definition_file_tmp);
887 
888  if (fd == -1)
889  {
890  perror(definition_file_tmp);
891  return errno;
892  }
893 
894  google::protobuf::io::ZeroCopyOutputStream* output=
895  new google::protobuf::io::FileOutputStream(fd);
896 
897  bool success;
898 
899  try
900  {
901  success= table_message.SerializeToZeroCopyStream(output);
902  }
903  catch (...)
904  {
905  success= false;
906  }
907 
908  if (not success)
909  {
910  my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0), identifier.getSQLPath().c_str(), table_message.InitializationErrorString().c_str());
911  delete output;
912 
913  if (close(fd) == -1)
914  perror(definition_file_tmp);
915 
916  if (unlink(definition_file_tmp) == -1)
917  perror(definition_file_tmp);
918 
919  return ER_CORRUPT_TABLE_DEFINITION;
920  }
921 
922  delete output;
923 
924  if (close(fd) == -1)
925  {
926  int error= errno;
927  perror(definition_file_tmp);
928 
929  if (unlink(definition_file_tmp))
930  perror(definition_file_tmp);
931 
932  return error;
933  }
934 
935  if (rename(definition_file_tmp, file_name.c_str()) == -1)
936  {
937  int error= errno;
938  perror(definition_file_tmp);
939 
940  if (unlink(definition_file_tmp))
941  perror(definition_file_tmp);
942 
943  return error;
944  }
945 
946  return 0;
947 }
948 
953 {
954  BOOST_FOREACH(EngineVector::reference it, g_engines)
955  {
956  if (not it->doCanCreateTable(identifier))
957  return false;
958  }
959  return true;
960 }
961 
962 bool StorageEngine::readTableFile(const std::string &path, message::Table &table_message)
963 {
964  std::fstream input(path.c_str(), std::ios::in | std::ios::binary);
965 
966  if (input.good())
967  {
968  try {
969  if (table_message.ParseFromIstream(&input))
970  {
971  return true;
972  }
973  }
974  catch (...)
975  {
976  my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
977  table_message.name().empty() ? path.c_str() : table_message.name().c_str(),
978  table_message.InitializationErrorString().empty() ? "": table_message.InitializationErrorString().c_str());
979  }
980  }
981  else
982  {
983  perror(path.c_str());
984  }
985 
986  return false;
987 }
988 
989 std::ostream& operator<<(std::ostream& output, const StorageEngine &engine)
990 {
991  return output << "StorageEngine:(" << engine.getName() << ")";
992 }
993 
994 } /* namespace plugin */
995 } /* namespace drizzled */