Drizzled Public API Documentation

schema.cc
00001 /* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2010 Brian Aker
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00021 #include <config.h>
00022 
00023 #include <drizzled/error.h>
00024 #include <plugin/schema_engine/schema.h>
00025 #include <drizzled/schema.h>
00026 #include <drizzled/sql_table.h>
00027 #include <drizzled/charset.h>
00028 #include <drizzled/cursor.h>
00029 #include <drizzled/catalog/local.h>
00030 
00031 #include <drizzled/pthread_globals.h>
00032 
00033 #include <drizzled/execute.h>
00034 
00035 #include <drizzled/internal/my_sys.h>
00036 #include <drizzled/cached_directory.h>
00037 
00038 #include <fcntl.h>
00039 #include <sys/stat.h>
00040 #include <sys/types.h>
00041 
00042 #include <boost/foreach.hpp>
00043 #include <google/protobuf/io/zero_copy_stream.h>
00044 #include <google/protobuf/io/zero_copy_stream_impl.h>
00045 
00046 #include <iostream>
00047 #include <fstream>
00048 #include <string>
00049 
00050 using namespace std;
00051 using namespace drizzled;
00052 
00053 const char* MY_DB_OPT_FILE= "db.opt";
00054 const char* DEFAULT_FILE_EXTENSION= ".dfe"; // Deep Fried Elephant
00055 
00056 static const char* g_schema_exts[] = 
00057 {
00058   NULL
00059 };
00060 
00061 Schema::Schema() :
00062   drizzled::plugin::StorageEngine("schema",
00063                                   HTON_ALTER_NOT_SUPPORTED |
00064                                   HTON_HAS_SCHEMA_DICTIONARY |
00065                                   HTON_SKIP_STORE_LOCK |
00066                                   HTON_TEMPORARY_NOT_SUPPORTED),
00067   schema_cache_filled(false)
00068 {
00069   table_definition_ext= DEFAULT_FILE_EXTENSION;
00070 }
00071 
00072 void Schema::prime()
00073 {
00074   CachedDirectory directory(catalog::local_identifier().getPath(),
00075                             CachedDirectory::DIRECTORY, true);
00076 
00077   CachedDirectory::Entries files= directory.getEntries();
00078   boost::unique_lock<boost::shared_mutex> scopedLock(mutex);
00079 
00080   BOOST_FOREACH(CachedDirectory::Entries::reference entry, files)
00081   {
00082     if (not entry->filename.compare(GLOBAL_TEMPORARY_EXT))
00083       continue;
00084     message::Schema schema_message;
00085 
00086     std::string filename= catalog::local_identifier().getPath();
00087     filename+= FN_LIBCHAR;
00088     filename+= entry->filename;
00089 
00090     if (readSchemaFile(filename, schema_message))
00091     {
00092       identifier::Schema schema_identifier(schema_message.name());
00093 
00094       pair<SchemaCache::iterator, bool> ret=
00095         schema_cache.insert(make_pair(schema_identifier.getPath(), new message::Schema(schema_message)));
00096 
00097       assert(ret.second); // If this has happened, something really bad is going down.
00098     }
00099   }
00100 }
00101 
00102 void Schema::doGetSchemaIdentifiers(identifier::schema::vector &set_of_names)
00103 {
00104   mutex.lock_shared();
00105   BOOST_FOREACH(SchemaCache::reference iter, schema_cache)
00106     set_of_names.push_back(identifier::Schema(iter.second->name()));
00107   mutex.unlock_shared();
00108 }
00109 
00110 drizzled::message::schema::shared_ptr Schema::doGetSchemaDefinition(const identifier::Schema &schema_identifier)
00111 {
00112   mutex.lock_shared();
00113   SchemaCache::iterator iter= schema_cache.find(schema_identifier.getPath());
00114   if (iter != schema_cache.end())
00115   {
00116     drizzled::message::schema::shared_ptr schema_message= iter->second;
00117     mutex.unlock_shared();
00118     return schema_message;
00119   }
00120   mutex.unlock_shared();
00121   return drizzled::message::schema::shared_ptr();
00122 }
00123 
00124 
00125 bool Schema::doCreateSchema(const drizzled::message::Schema &schema_message)
00126 {
00127   identifier::Schema schema_identifier(schema_message.name());
00128 
00129   if (mkdir(schema_identifier.getPath().c_str(), 0777) == -1)
00130   {
00131     sql_perror(schema_identifier.getPath().c_str());
00132     return false;
00133   }
00134 
00135   if (not writeSchemaFile(schema_identifier, schema_message))
00136   {
00137     rmdir(schema_identifier.getPath().c_str());
00138 
00139     return false;
00140   }
00141 
00142   boost::unique_lock<boost::shared_mutex> scopedLock(mutex);
00143   pair<SchemaCache::iterator, bool> ret= 
00144     schema_cache.insert(make_pair(schema_identifier.getPath(), new message::Schema(schema_message)));
00145 
00146   assert(ret.second); // If this has happened, something really bad is going down.
00147   return true;
00148 }
00149 
00150 bool Schema::doDropSchema(const identifier::Schema &schema_identifier)
00151 {
00152   string schema_file(schema_identifier.getPath());
00153   schema_file.append(1, FN_LIBCHAR);
00154   schema_file.append(MY_DB_OPT_FILE);
00155 
00156   if (not doGetSchemaDefinition(schema_identifier))
00157     return false;
00158 
00159   // No db.opt file, no love from us.
00160   if (access(schema_file.c_str(), F_OK))
00161   {
00162     sql_perror(schema_file.c_str());
00163     return false;
00164   }
00165 
00166   if (unlink(schema_file.c_str()))
00167   {
00168     sql_perror(schema_file.c_str());
00169     return false;
00170   }
00171 
00172   if (rmdir(schema_identifier.getPath().c_str()))
00173   {
00174     sql_perror(schema_identifier.getPath().c_str());
00175     //@todo If this happens, we want a report of it. For the moment I dump
00176     //to stderr so I can catch it in Hudson.
00177     CachedDirectory dir(schema_identifier.getPath());
00178     cerr << dir;
00179   }
00180 
00181   boost::unique_lock<boost::shared_mutex> scopedLock(mutex);
00182   schema_cache.erase(schema_identifier.getPath());
00183 
00184   return true;
00185 }
00186 
00187 bool Schema::doAlterSchema(const drizzled::message::Schema &schema_message)
00188 {
00189   identifier::Schema schema_identifier(schema_message.name());
00190 
00191   if (access(schema_identifier.getPath().c_str(), F_OK))
00192     return false;
00193 
00194   if (writeSchemaFile(schema_identifier, schema_message))
00195   {
00196     boost::unique_lock<boost::shared_mutex> scopedLock(mutex);
00197     schema_cache.erase(schema_identifier.getPath());
00198 
00199     pair<SchemaCache::iterator, bool> ret=
00200       schema_cache.insert(make_pair(schema_identifier.getPath(), new message::Schema(schema_message)));
00201 
00202     assert(ret.second); // If this has happened, something really bad is going down.
00203   }
00204 
00205   return true;
00206 }
00207 
00213 bool Schema::writeSchemaFile(const identifier::Schema &schema_identifier, const message::Schema &db)
00214 {
00215   char schema_file_tmp[FN_REFLEN];
00216   string schema_file(schema_identifier.getPath());
00217 
00218 
00219   schema_file.append(1, FN_LIBCHAR);
00220   schema_file.append(MY_DB_OPT_FILE);
00221 
00222   snprintf(schema_file_tmp, FN_REFLEN, "%sXXXXXX", schema_file.c_str());
00223 
00224   int fd= mkstemp(schema_file_tmp);
00225 
00226   if (fd == -1)
00227   {
00228     sql_perror(schema_file_tmp);
00229 
00230     return false;
00231   }
00232 
00233   bool success;
00234 
00235   try {
00236     success= db.SerializeToFileDescriptor(fd);
00237   }
00238   catch (...)
00239   {
00240     success= false;
00241   }
00242 
00243   if (not success)
00244   {
00245     my_error(ER_CORRUPT_SCHEMA_DEFINITION, MYF(0), schema_file.c_str(),
00246              db.InitializationErrorString().empty() ? "unknown" :  db.InitializationErrorString().c_str());
00247 
00248     if (close(fd) == -1)
00249       sql_perror(schema_file_tmp);
00250 
00251     if (unlink(schema_file_tmp))
00252       sql_perror(schema_file_tmp);
00253 
00254     return false;
00255   }
00256 
00257   if (close(fd) == -1)
00258   {
00259     sql_perror(schema_file_tmp);
00260 
00261     if (unlink(schema_file_tmp))
00262       sql_perror(schema_file_tmp);
00263 
00264     return false;
00265   }
00266 
00267   if (rename(schema_file_tmp, schema_file.c_str()) == -1)
00268   {
00269     if (unlink(schema_file_tmp))
00270       sql_perror(schema_file_tmp);
00271 
00272     return false;
00273   }
00274 
00275   return true;
00276 }
00277 
00278 
00279 bool Schema::readSchemaFile(const drizzled::identifier::Schema &schema_identifier, drizzled::message::Schema &schema)
00280 {
00281   return readSchemaFile(schema_identifier.getPath(), schema); 
00282 }
00283 
00284 bool Schema::readSchemaFile(std::string db_opt_path, drizzled::message::Schema &schema)
00285 {
00286   /*
00287     Pass an empty file name, and the database options file name as extension
00288     to avoid table name to file name encoding.
00289   */
00290   db_opt_path.append(1, FN_LIBCHAR);
00291   db_opt_path.append(MY_DB_OPT_FILE);
00292 
00293   fstream input(db_opt_path.c_str(), ios::in | ios::binary);
00294 
00300   if (input.good())
00301   {
00302     if (schema.ParseFromIstream(&input))
00303     {
00304       return true;
00305     }
00306 
00307     my_error(ER_CORRUPT_SCHEMA_DEFINITION, MYF(0), db_opt_path.c_str(),
00308              schema.InitializationErrorString().empty() ? "unknown" :  schema.InitializationErrorString().c_str());
00309   }
00310   else
00311   {
00312     sql_perror(db_opt_path.c_str());
00313   }
00314 
00315   return false;
00316 }
00317 
00318 void Schema::doGetTableIdentifiers(drizzled::CachedDirectory&,
00319                                    const drizzled::identifier::Schema&,
00320                                    drizzled::identifier::table::vector&)
00321 {
00322 }
00323 
00324 const char** Schema::bas_ext() const
00325 {
00326   return g_schema_exts;
00327 }