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/catalog/local.h>
30 
31 #include <drizzled/pthread_globals.h>
32 
33 #include <drizzled/execute.h>
34 
35 #include <drizzled/internal/my_sys.h>
37 
38 #include <fcntl.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 
42 #include <boost/foreach.hpp>
43 #include <google/protobuf/io/zero_copy_stream.h>
44 #include <google/protobuf/io/zero_copy_stream_impl.h>
45 
46 #include <iostream>
47 #include <fstream>
48 #include <string>
49 
50 using namespace std;
51 using namespace drizzled;
52 
53 const char* MY_DB_OPT_FILE= "db.opt";
54 const char* DEFAULT_FILE_EXTENSION= ".dfe"; // Deep Fried Elephant
55 
56 static const char* g_schema_exts[] =
57 {
58  NULL
59 };
60 
61 Schema::Schema() :
62  drizzled::plugin::StorageEngine("SchemaEngine",
63  HTON_ALTER_NOT_SUPPORTED |
64  HTON_HAS_SCHEMA_DICTIONARY |
65  HTON_SKIP_STORE_LOCK |
66  HTON_TEMPORARY_NOT_SUPPORTED),
67  schema_cache_filled(false)
68 {
69  table_definition_ext= DEFAULT_FILE_EXTENSION;
70 }
71 
72 void Schema::prime()
73 {
74  CachedDirectory directory(catalog::local_identifier().getPath(),
75  CachedDirectory::DIRECTORY, true);
76 
77  CachedDirectory::Entries files= directory.getEntries();
78  boost::unique_lock<boost::shared_mutex> scopedLock(mutex);
79 
80  BOOST_FOREACH(CachedDirectory::Entries::reference entry, files)
81  {
82  if (not entry->filename.compare(GLOBAL_TEMPORARY_EXT))
83  continue;
84  message::Schema schema_message;
85 
86  std::string filename= catalog::local_identifier().getPath();
87  filename+= FN_LIBCHAR;
88  filename+= entry->filename;
89 
90  if (readSchemaFile(filename, schema_message))
91  {
92  identifier::Schema schema_identifier(schema_message.name());
93 
94  pair<SchemaCache::iterator, bool> ret=
95  schema_cache.insert(make_pair(schema_identifier.getPath(), new message::Schema(schema_message)));
96 
97  assert(ret.second); // If this has happened, something really bad is going down.
98  }
99  }
100 }
101 
102 void Schema::doGetSchemaIdentifiers(identifier::schema::vector &set_of_names)
103 {
104  mutex.lock_shared();
105  BOOST_FOREACH(SchemaCache::reference iter, schema_cache)
106  set_of_names.push_back(identifier::Schema(iter.second->name()));
107  mutex.unlock_shared();
108 }
109 
110 drizzled::message::schema::shared_ptr Schema::doGetSchemaDefinition(const identifier::Schema &schema_identifier)
111 {
112  mutex.lock_shared();
113  SchemaCache::iterator iter= schema_cache.find(schema_identifier.getPath());
114  if (iter != schema_cache.end())
115  {
116  drizzled::message::schema::shared_ptr schema_message= iter->second;
117  mutex.unlock_shared();
118  return schema_message;
119  }
120  mutex.unlock_shared();
121  return drizzled::message::schema::shared_ptr();
122 }
123 
124 
125 bool Schema::doCreateSchema(const drizzled::message::Schema &schema_message)
126 {
127  identifier::Schema schema_identifier(schema_message.name());
128 
129  if (mkdir(schema_identifier.getPath().c_str(), 0777) == -1)
130  {
131  sql_perror(schema_identifier.getPath().c_str());
132  return false;
133  }
134 
135  if (not writeSchemaFile(schema_identifier, schema_message))
136  {
137  rmdir(schema_identifier.getPath().c_str());
138 
139  return false;
140  }
141 
142  boost::unique_lock<boost::shared_mutex> scopedLock(mutex);
143  pair<SchemaCache::iterator, bool> ret=
144  schema_cache.insert(make_pair(schema_identifier.getPath(), new message::Schema(schema_message)));
145 
146  assert(ret.second); // If this has happened, something really bad is going down.
147  return true;
148 }
149 
150 bool Schema::doDropSchema(const identifier::Schema &schema_identifier)
151 {
152  string schema_file(schema_identifier.getPath());
153  schema_file.append(1, FN_LIBCHAR);
154  schema_file.append(MY_DB_OPT_FILE);
155 
156  if (not doGetSchemaDefinition(schema_identifier))
157  return false;
158 
159  // No db.opt file, no love from us.
160  if (access(schema_file.c_str(), F_OK))
161  {
162  sql_perror(schema_file.c_str());
163  return false;
164  }
165 
166  if (unlink(schema_file.c_str()))
167  {
168  sql_perror(schema_file.c_str());
169  return false;
170  }
171 
172  if (rmdir(schema_identifier.getPath().c_str()))
173  {
174  sql_perror(schema_identifier.getPath().c_str());
175  //@todo If this happens, we want a report of it. For the moment I dump
176  //to stderr so I can catch it in Hudson.
177  CachedDirectory dir(schema_identifier.getPath());
178  cerr << dir;
179  }
180 
181  boost::unique_lock<boost::shared_mutex> scopedLock(mutex);
182  schema_cache.erase(schema_identifier.getPath());
183 
184  return true;
185 }
186 
187 bool Schema::doAlterSchema(const drizzled::message::Schema &schema_message)
188 {
189  identifier::Schema schema_identifier(schema_message.name());
190 
191  if (access(schema_identifier.getPath().c_str(), F_OK))
192  return false;
193 
194  if (writeSchemaFile(schema_identifier, schema_message))
195  {
196  boost::unique_lock<boost::shared_mutex> scopedLock(mutex);
197  schema_cache.erase(schema_identifier.getPath());
198 
199  pair<SchemaCache::iterator, bool> ret=
200  schema_cache.insert(make_pair(schema_identifier.getPath(), new message::Schema(schema_message)));
201 
202  assert(ret.second); // If this has happened, something really bad is going down.
203  }
204 
205  return true;
206 }
207 
213 bool Schema::writeSchemaFile(const identifier::Schema &schema_identifier, const message::Schema &db)
214 {
215  char schema_file_tmp[FN_REFLEN];
216  string schema_file(schema_identifier.getPath());
217 
218 
219  schema_file.append(1, FN_LIBCHAR);
220  schema_file.append(MY_DB_OPT_FILE);
221 
222  snprintf(schema_file_tmp, FN_REFLEN, "%sXXXXXX", schema_file.c_str());
223 
224  int fd= mkstemp(schema_file_tmp);
225 
226  if (fd == -1)
227  {
228  sql_perror(schema_file_tmp);
229 
230  return false;
231  }
232 
233  bool success;
234 
235  try {
236  success= db.SerializeToFileDescriptor(fd);
237  }
238  catch (...)
239  {
240  success= false;
241  }
242 
243  if (not success)
244  {
245  my_error(ER_CORRUPT_SCHEMA_DEFINITION, MYF(0), schema_file.c_str(),
246  db.InitializationErrorString().empty() ? "unknown" : db.InitializationErrorString().c_str());
247 
248  if (close(fd) == -1)
249  sql_perror(schema_file_tmp);
250 
251  if (unlink(schema_file_tmp))
252  sql_perror(schema_file_tmp);
253 
254  return false;
255  }
256 
257  if (close(fd) == -1)
258  {
259  sql_perror(schema_file_tmp);
260 
261  if (unlink(schema_file_tmp))
262  sql_perror(schema_file_tmp);
263 
264  return false;
265  }
266 
267  if (rename(schema_file_tmp, schema_file.c_str()) == -1)
268  {
269  if (unlink(schema_file_tmp))
270  sql_perror(schema_file_tmp);
271 
272  return false;
273  }
274 
275  return true;
276 }
277 
278 
279 bool Schema::readSchemaFile(const drizzled::identifier::Schema &schema_identifier, drizzled::message::Schema &schema)
280 {
281  return readSchemaFile(schema_identifier.getPath(), schema);
282 }
283 
284 bool Schema::readSchemaFile(std::string db_opt_path, drizzled::message::Schema &schema)
285 {
286  /*
287  Pass an empty file name, and the database options file name as extension
288  to avoid table name to file name encoding.
289  */
290  db_opt_path.append(1, FN_LIBCHAR);
291  db_opt_path.append(MY_DB_OPT_FILE);
292 
293  fstream input(db_opt_path.c_str(), ios::in | ios::binary);
294 
300  if (input.good())
301  {
302  if (schema.ParseFromIstream(&input))
303  {
304  return true;
305  }
306 
307  my_error(ER_CORRUPT_SCHEMA_DEFINITION, MYF(0), db_opt_path.c_str(),
308  schema.InitializationErrorString().empty() ? "unknown" : schema.InitializationErrorString().c_str());
309  }
310  else
311  {
312  sql_perror(db_opt_path.c_str());
313  }
314 
315  return false;
316 }
317 
318 void Schema::doGetTableIdentifiers(drizzled::CachedDirectory&,
320  drizzled::identifier::table::vector&)
321 {
322 }
323 
324 const char** Schema::bas_ext() const
325 {
326  return g_schema_exts;
327 }