Drizzled Public API Documentation

schema.cc
1 /* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3  *
4  * Copyright (C) 2010 Brian Aker
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; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include <config.h>
22 
23 #include <drizzled/error.h>
24 #include <plugin/schema_engine/schema.h>
25 #include <drizzled/schema.h>
26 #include <drizzled/sql_table.h>
27 #include <drizzled/charset.h>
28 #include <drizzled/cursor.h>
29 #include <drizzled/data_home.h>
30 #include <drizzled/message/catalog.h>
31 
32 #include <drizzled/pthread_globals.h>
33 
34 #include <drizzled/execute.h>
35 
36 #include <drizzled/internal/my_sys.h>
38 
39 #include <fcntl.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 
43 #include <boost/foreach.hpp>
44 #include <google/protobuf/io/zero_copy_stream.h>
45 #include <google/protobuf/io/zero_copy_stream_impl.h>
46 
47 #include <iostream>
48 #include <fstream>
49 #include <string>
50 
51 using namespace std;
52 using namespace drizzled;
53 
54 const char* MY_DB_OPT_FILE= "db.opt";
55 const char* DEFAULT_FILE_EXTENSION= ".dfe"; // Deep Fried Elephant
56 
57 static const char* g_schema_exts[] =
58 {
59  NULL
60 };
61 
62 Schema::Schema() :
63  drizzled::plugin::StorageEngine("SchemaEngine",
64  HTON_ALTER_NOT_SUPPORTED |
65  HTON_HAS_SCHEMA_DICTIONARY |
66  HTON_SKIP_STORE_LOCK |
67  HTON_TEMPORARY_NOT_SUPPORTED),
68  schema_cache_filled(false)
69 {
70  table_definition_ext= DEFAULT_FILE_EXTENSION;
71 }
72 
73 void Schema::prime_catalog(identifier::Catalog &catalog_identifier)
74 {
75  CachedDirectory directory(catalog_identifier.getPath(),
76  CachedDirectory::DIRECTORY, true);
77 
78  CachedDirectory::Entries files= directory.getEntries();
79  boost::unique_lock<boost::shared_mutex> scopedLock(mutex);
80 
81  BOOST_FOREACH(CachedDirectory::Entries::reference entry, files)
82  {
83  if (not entry->filename.compare(GLOBAL_TEMPORARY_EXT))
84  continue;
85  message::Schema schema_message;
86 
87  std::string filename= catalog_identifier.getPath();
88  filename+= FN_LIBCHAR;
89  filename+= entry->filename;
90 
91  if (readSchemaFile(filename, schema_message))
92  {
93 
94  identifier::Schema schema_identifier(catalog_identifier,
95  schema_message.name());
96 
97  if (! schema_message.has_catalog())
98  {
99  schema_message.set_catalog(catalog_identifier.name());
100  }
101 
102  pair<SchemaCache::iterator, bool> ret=
103  schema_cache.insert(make_pair(schema_identifier.getPath(), new message::Schema(schema_message)));
104 
105  assert(ret.second); // If this has happened, something really bad is going down.
106  }
107  }
108 }
109 
110 void Schema::prime()
111 {
112  drizzled::CachedDirectory directory(drizzled::getDataHome().file_string(),
113  drizzled::CachedDirectory::DIRECTORY,
114  true);
115  drizzled::CachedDirectory::Entries files= directory.getEntries();
116 
117  for (drizzled::CachedDirectory::Entries::iterator fileIter= files.begin();
118  fileIter != files.end(); fileIter++)
119  {
120  drizzled::CachedDirectory::Entry *entry= *fileIter;
121  drizzled::message::catalog::shared_ptr message;
122 
123  if (not entry->filename.compare(GLOBAL_TEMPORARY_EXT))
124  continue;
125 
126  drizzled::identifier::Catalog identifier(entry->filename);
127 
128  prime_catalog(identifier);
129  }
130 }
131 
132 void Schema::doGetSchemaIdentifiers(identifier::schema::vector &set_of_names)
133 {
134  mutex.lock_shared();
135  BOOST_FOREACH(SchemaCache::reference iter, schema_cache)
136  set_of_names.push_back(identifier::Schema(identifier::Catalog(iter.second->catalog()),
137  iter.second->name()));
138  mutex.unlock_shared();
139 }
140 
141 drizzled::message::schema::shared_ptr Schema::doGetSchemaDefinition(const identifier::Schema &schema_identifier)
142 {
143  mutex.lock_shared();
144  SchemaCache::iterator iter= schema_cache.find(schema_identifier.getPath());
145  if (iter != schema_cache.end())
146  {
147  drizzled::message::schema::shared_ptr schema_message= iter->second;
148  mutex.unlock_shared();
149  return schema_message;
150  }
151  mutex.unlock_shared();
152  return drizzled::message::schema::shared_ptr();
153 }
154 
155 
156 bool Schema::doCreateSchema(const drizzled::message::Schema &schema_message)
157 {
158  identifier::Schema schema_identifier(identifier::Catalog(schema_message.catalog()),
159  schema_message.name());
160 
161  if (mkdir(schema_identifier.getPath().c_str(), 0777) == -1)
162  {
163  sql_perror(schema_identifier.getPath().c_str());
164  return false;
165  }
166 
167  if (not writeSchemaFile(schema_identifier, schema_message))
168  {
169  rmdir(schema_identifier.getPath().c_str());
170 
171  return false;
172  }
173 
174  boost::unique_lock<boost::shared_mutex> scopedLock(mutex);
175  pair<SchemaCache::iterator, bool> ret=
176  schema_cache.insert(make_pair(schema_identifier.getPath(), new message::Schema(schema_message)));
177 
178  assert(ret.second); // If this has happened, something really bad is going down.
179  return true;
180 }
181 
182 bool Schema::doDropSchema(const identifier::Schema &schema_identifier)
183 {
184  string schema_file(schema_identifier.getPath());
185  schema_file.append(1, FN_LIBCHAR);
186  schema_file.append(MY_DB_OPT_FILE);
187 
188  if (not doGetSchemaDefinition(schema_identifier))
189  return false;
190 
191  // No db.opt file, no love from us.
192  if (access(schema_file.c_str(), F_OK))
193  {
194  sql_perror(schema_file.c_str());
195  return false;
196  }
197 
198  if (unlink(schema_file.c_str()))
199  {
200  sql_perror(schema_file.c_str());
201  return false;
202  }
203 
204  if (rmdir(schema_identifier.getPath().c_str()))
205  {
206  sql_perror(schema_identifier.getPath().c_str());
207  //@todo If this happens, we want a report of it. For the moment I dump
208  //to stderr so I can catch it in Hudson.
209  CachedDirectory dir(schema_identifier.getPath());
210  cerr << dir;
211  }
212 
213  boost::unique_lock<boost::shared_mutex> scopedLock(mutex);
214  schema_cache.erase(schema_identifier.getPath());
215 
216  return true;
217 }
218 
219 bool Schema::doAlterSchema(const drizzled::message::Schema &schema_message)
220 {
221  identifier::Schema schema_identifier(identifier::Catalog(schema_message.catalog()),
222  schema_message.name());
223 
224  if (access(schema_identifier.getPath().c_str(), F_OK))
225  return false;
226 
227  if (writeSchemaFile(schema_identifier, schema_message))
228  {
229  boost::unique_lock<boost::shared_mutex> scopedLock(mutex);
230  schema_cache.erase(schema_identifier.getPath());
231 
232  pair<SchemaCache::iterator, bool> ret=
233  schema_cache.insert(make_pair(schema_identifier.getPath(), new message::Schema(schema_message)));
234 
235  assert(ret.second); // If this has happened, something really bad is going down.
236  }
237 
238  return true;
239 }
240 
246 bool Schema::writeSchemaFile(const identifier::Schema &schema_identifier, const message::Schema &db)
247 {
248  char schema_file_tmp[FN_REFLEN];
249  string schema_file(schema_identifier.getPath());
250 
251 
252  schema_file.append(1, FN_LIBCHAR);
253  schema_file.append(MY_DB_OPT_FILE);
254 
255  snprintf(schema_file_tmp, FN_REFLEN, "%sXXXXXX", schema_file.c_str());
256 
257  int fd= mkstemp(schema_file_tmp);
258 
259  if (fd == -1)
260  {
261  sql_perror(schema_file_tmp);
262 
263  return false;
264  }
265 
266  bool success;
267 
268  try {
269  success= db.SerializeToFileDescriptor(fd);
270  }
271  catch (...)
272  {
273  success= false;
274  }
275 
276  if (not success)
277  {
278  my_error(ER_CORRUPT_SCHEMA_DEFINITION, MYF(0), schema_file.c_str(),
279  db.InitializationErrorString().empty() ? "unknown" : db.InitializationErrorString().c_str());
280 
281  if (close(fd) == -1)
282  sql_perror(schema_file_tmp);
283 
284  if (unlink(schema_file_tmp))
285  sql_perror(schema_file_tmp);
286 
287  return false;
288  }
289 
290  if (close(fd) == -1)
291  {
292  sql_perror(schema_file_tmp);
293 
294  if (unlink(schema_file_tmp))
295  sql_perror(schema_file_tmp);
296 
297  return false;
298  }
299 
300  if (rename(schema_file_tmp, schema_file.c_str()) == -1)
301  {
302  if (unlink(schema_file_tmp))
303  sql_perror(schema_file_tmp);
304 
305  return false;
306  }
307 
308  return true;
309 }
310 
311 
312 bool Schema::readSchemaFile(const drizzled::identifier::Schema &schema_identifier, drizzled::message::Schema &schema)
313 {
314  return readSchemaFile(schema_identifier.getPath(), schema);
315 }
316 
317 bool Schema::readSchemaFile(std::string db_opt_path, drizzled::message::Schema &schema)
318 {
319  /*
320  Pass an empty file name, and the database options file name as extension
321  to avoid table name to file name encoding.
322  */
323  db_opt_path.append(1, FN_LIBCHAR);
324  db_opt_path.append(MY_DB_OPT_FILE);
325 
326  fstream input(db_opt_path.c_str(), ios::in | ios::binary);
327 
333  if (input.good())
334  {
335  if (schema.ParseFromIstream(&input))
336  {
337  return true;
338  }
339 
340  my_error(ER_CORRUPT_SCHEMA_DEFINITION, MYF(0), db_opt_path.c_str(),
341  schema.InitializationErrorString().empty() ? "unknown" : schema.InitializationErrorString().c_str());
342  }
343  else
344  {
345  sql_perror(db_opt_path.c_str());
346  }
347 
348  return false;
349 }
350 
351 void Schema::doGetTableIdentifiers(drizzled::CachedDirectory&,
353  drizzled::identifier::table::vector&)
354 {
355 }
356 
357 const char** Schema::bas_ext() const
358 {
359  return g_schema_exts;
360 }