~jaypipes/drizzle/bug534806

« back to all changes in this revision

Viewing changes to plugin/schema_engine/schema.cc

  • Committer: Brian Aker
  • Date: 2010-02-26 19:01:18 UTC
  • mfrom: (1311.1.4 build)
  • Revision ID: brian@gaz-20100226190118-gn4wtrvp6hur1t5t
Merge of Brian

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
#include "plugin/schema_engine/schema.h"
24
24
#include "drizzled/db.h"
25
25
#include "drizzled/sql_table.h"
 
26
#include "drizzled/global_charset_info.h"
 
27
#include "drizzled/charset.h"
 
28
#include "drizzled/charset_info.h"
 
29
#include "drizzled/cursor.h"
26
30
 
 
31
#include <fcntl.h>
27
32
#include <sys/stat.h>
28
33
#include <sys/types.h>
29
34
 
 
35
#include <google/protobuf/io/zero_copy_stream.h>
 
36
#include <google/protobuf/io/zero_copy_stream_impl.h>
 
37
 
30
38
#include <iostream>
31
39
#include <fstream>
32
40
#include <string>
35
43
using namespace drizzled;
36
44
 
37
45
#define MY_DB_OPT_FILE "db.opt"
 
46
#define DEFAULT_FILE_EXTENSION ".dfe" // Deep Fried Elephant
38
47
 
39
48
Schema::Schema():
40
49
  drizzled::plugin::StorageEngine("schema",
42
51
                                  HTON_HAS_DATA_DICTIONARY |
43
52
                                  HTON_HAS_SCHEMA_DICTIONARY |
44
53
                                  HTON_SKIP_STORE_LOCK |
45
 
                                  HTON_TEMPORARY_NOT_SUPPORTED)
46
 
{
 
54
                                  HTON_TEMPORARY_NOT_SUPPORTED),
 
55
  schema_cache_filled(false)
 
56
{
 
57
  table_definition_ext= DEFAULT_FILE_EXTENSION;
 
58
  pthread_rwlock_init(&schema_lock, NULL);
 
59
  prime();
 
60
}
 
61
 
 
62
Schema::~Schema()
 
63
{
 
64
  pthread_rwlock_destroy(&schema_lock);
 
65
}
 
66
 
 
67
int Schema::doGetTableDefinition(Session &,
 
68
                                 const char *path,
 
69
                                 const char *,
 
70
                                 const char *,
 
71
                                 const bool,
 
72
                                 message::Table *table_proto)
 
73
{
 
74
  string proto_path(path);
 
75
  proto_path.append(DEFAULT_FILE_EXTENSION);
 
76
 
 
77
  if (access(proto_path.c_str(), F_OK))
 
78
  {
 
79
    return errno;
 
80
  }
 
81
 
 
82
  if (table_proto)
 
83
  {
 
84
    if (readTableFile(proto_path, *table_proto))
 
85
      return EEXIST;
 
86
 
 
87
    return -1;
 
88
  }
 
89
 
 
90
  return EEXIST;
 
91
}
 
92
 
 
93
void Schema::doGetTableNames(CachedDirectory &directory, string&, set<string>& set_of_names)
 
94
{
 
95
  CachedDirectory::Entries entries= directory.getEntries();
 
96
 
 
97
  for (CachedDirectory::Entries::iterator entry_iter= entries.begin(); 
 
98
       entry_iter != entries.end(); ++entry_iter)
 
99
  {
 
100
    CachedDirectory::Entry *entry= *entry_iter;
 
101
    const string *filename= &entry->filename;
 
102
 
 
103
    assert(filename->size());
 
104
 
 
105
    const char *ext= strchr(filename->c_str(), '.');
 
106
 
 
107
    if (ext == NULL || my_strcasecmp(system_charset_info, ext, DEFAULT_FILE_EXTENSION) ||
 
108
        (filename->compare(0, strlen(TMP_FILE_PREFIX), TMP_FILE_PREFIX) == 0))
 
109
    { }
 
110
    else
 
111
    {
 
112
      char uname[NAME_LEN + 1];
 
113
      uint32_t file_name_len;
 
114
 
 
115
      file_name_len= filename_to_tablename(filename->c_str(), uname, sizeof(uname));
 
116
      // TODO: Remove need for memory copy here
 
117
      uname[file_name_len - sizeof(DEFAULT_FILE_EXTENSION) + 1]= '\0'; // Subtract ending, place NULL 
 
118
      set_of_names.insert(uname);
 
119
    }
 
120
  }
 
121
}
 
122
 
 
123
void Schema::prime()
 
124
{
 
125
  CachedDirectory directory(drizzle_data_home, CachedDirectory::DIRECTORY);
 
126
  CachedDirectory::Entries files= directory.getEntries();
 
127
 
 
128
  pthread_rwlock_wrlock(&schema_lock);
 
129
 
 
130
  for (CachedDirectory::Entries::iterator fileIter= files.begin();
 
131
       fileIter != files.end(); fileIter++)
 
132
  {
 
133
    CachedDirectory::Entry *entry= *fileIter;
 
134
    message::Schema schema_message;
 
135
 
 
136
    if (readSchemaFile(entry->filename, schema_message))
 
137
    {
 
138
      pair<SchemaCache::iterator, bool> ret=
 
139
        schema_cache.insert(make_pair(schema_message.name(), schema_message));
 
140
 
 
141
      cerr << "Caching " << schema_message.name() << "\n";
 
142
 
 
143
      if (ret.second == false)
 
144
      {
 
145
        abort(); // If this has happened, something really bad is going down.
 
146
      }
 
147
    }
 
148
  }
 
149
  pthread_rwlock_unlock(&schema_lock);
47
150
}
48
151
 
49
152
void Schema::doGetSchemaNames(std::set<std::string>& set_of_names)
50
153
{
 
154
  if (not pthread_rwlock_rdlock(&schema_lock))
 
155
  {
 
156
    for (SchemaCache::iterator iter= schema_cache.begin();
 
157
         iter != schema_cache.end();
 
158
         iter++)
 
159
    {
 
160
      set_of_names.insert((*iter).first);
 
161
    }
 
162
    pthread_rwlock_unlock(&schema_lock);
 
163
 
 
164
    return;
 
165
  }
 
166
 
 
167
  // If for some reason getting a lock should fail, we resort to disk
 
168
 
51
169
  CachedDirectory directory(drizzle_data_home, CachedDirectory::DIRECTORY);
52
170
 
53
171
  CachedDirectory::Entries files= directory.getEntries();
62
180
 
63
181
bool Schema::doGetSchemaDefinition(const std::string &schema_name, message::Schema &schema_message)
64
182
{
65
 
  char db_opt_path[FN_REFLEN];
66
 
  size_t length;
67
 
 
68
 
  /*
69
 
    Pass an empty file name, and the database options file name as extension
70
 
    to avoid table name to file name encoding.
71
 
  */
72
 
  length= build_table_filename(db_opt_path, sizeof(db_opt_path),
73
 
                               schema_name.c_str(), "", false);
74
 
  strcpy(db_opt_path + length, MY_DB_OPT_FILE);
75
 
 
76
 
  fstream input(db_opt_path, ios::in | ios::binary);
77
 
 
78
 
  /**
79
 
    @note If parsing fails, either someone has done a "mkdir" or has deleted their opt file.
80
 
    So what do we do? We muddle through the adventure by generating 
81
 
    one with a name in it, and the charset set to the default.
82
 
  */
83
 
  if (input.good())
 
183
  if (not pthread_rwlock_rdlock(&schema_lock))
84
184
  {
85
 
    if (schema_message.ParseFromIstream(&input))
 
185
    SchemaCache::iterator iter= schema_cache.find(schema_name);
 
186
    if (iter != schema_cache.end())
86
187
    {
 
188
      schema_message.CopyFrom(((*iter).second));
 
189
      pthread_rwlock_unlock(&schema_lock);
87
190
      return true;
88
191
    }
89
 
  }
90
 
  else
91
 
  {
92
 
    perror(db_opt_path);
93
 
  }
94
 
 
95
 
  return false;
 
192
    pthread_rwlock_unlock(&schema_lock);
 
193
 
 
194
    return false;
 
195
  }
 
196
 
 
197
  // Fail to disk based means
 
198
  return readSchemaFile(schema_name, schema_message);
96
199
}
97
200
 
98
201
bool Schema::doCreateSchema(const drizzled::message::Schema &schema_message)
99
202
{
100
203
  char   path[FN_REFLEN+16];
101
204
  uint32_t path_len;
102
 
  int error_erno;
 
205
 
103
206
  path_len= drizzled::build_table_filename(path, sizeof(path), schema_message.name().c_str(), "", false);
104
207
  path[path_len-1]= 0;                    // remove last '/' from path
105
208
 
106
209
  if (mkdir(path, 0777) == -1)
107
210
    return false;
108
211
 
109
 
  error_erno= write_schema_file(path, schema_message);
110
 
  if (error_erno && error_erno != EEXIST)
 
212
  if (not writeSchemaFile(path, schema_message))
111
213
  {
112
214
    rmdir(path);
113
215
 
114
216
    return false;
115
217
  }
116
218
 
 
219
  if (not pthread_rwlock_wrlock(&schema_lock))
 
220
  {
 
221
      pair<SchemaCache::iterator, bool> ret=
 
222
        schema_cache.insert(make_pair(schema_message.name(), schema_message));
 
223
 
 
224
 
 
225
      if (ret.second == false)
 
226
      {
 
227
        abort(); // If this has happened, something really bad is going down.
 
228
      }
 
229
    pthread_rwlock_unlock(&schema_lock);
 
230
  }
 
231
 
117
232
  return true;
118
233
}
119
234
 
146
261
  if (rmdir(path))
147
262
    perror(path);
148
263
 
 
264
  if (not pthread_rwlock_wrlock(&schema_lock))
 
265
  {
 
266
    schema_cache.erase(schema_message.name());
 
267
    pthread_rwlock_unlock(&schema_lock);
 
268
  }
 
269
 
149
270
  return true;
150
271
}
151
272
 
153
274
{
154
275
  char   path[FN_REFLEN+16];
155
276
  uint32_t path_len;
156
 
  int error_erno;
157
277
  path_len= drizzled::build_table_filename(path, sizeof(path), schema_message.name().c_str(), "", false);
158
278
  path[path_len-1]= 0;                    // remove last '/' from path
159
279
 
160
280
  if (access(path, F_OK))
161
281
    return false;
162
282
 
163
 
  error_erno= write_schema_file(path, schema_message);
164
 
  if (error_erno && error_erno != EEXIST)
 
283
  if (writeSchemaFile(path, schema_message))
165
284
  {
166
 
    return false;
 
285
    if (not pthread_rwlock_wrlock(&schema_lock))
 
286
    {
 
287
      schema_cache.erase(schema_message.name());
 
288
 
 
289
      pair<SchemaCache::iterator, bool> ret=
 
290
        schema_cache.insert(make_pair(schema_message.name(), schema_message));
 
291
 
 
292
      if (ret.second == false)
 
293
      {
 
294
        abort(); // If this has happened, something really bad is going down.
 
295
      }
 
296
 
 
297
      pthread_rwlock_unlock(&schema_lock);
 
298
    }
 
299
    else
 
300
    {
 
301
      abort(); // This would leave us out of sync, suck.
 
302
    }
167
303
  }
168
304
 
169
305
  return true;
174
310
 
175
311
  @note we do the rename to make it crash safe.
176
312
*/
177
 
int Schema::write_schema_file(const char *path, const message::Schema &db)
 
313
bool Schema::writeSchemaFile(const char *path, const message::Schema &db)
178
314
{
179
315
  char schema_file_tmp[FN_REFLEN];
180
316
  string schema_file(path);
187
323
  int fd= mkstemp(schema_file_tmp);
188
324
 
189
325
  if (fd == -1)
190
 
    return errno;
 
326
    return false;
191
327
 
192
328
  if (not db.SerializeToFileDescriptor(fd))
193
329
  {
194
330
    close(fd);
195
331
    unlink(schema_file_tmp);
196
 
    return -1;
 
332
 
 
333
    return false;
197
334
  }
198
335
 
199
336
  if (rename(schema_file_tmp, schema_file.c_str()) == -1)
200
337
  {
201
338
    close(fd);
202
 
    return errno;
 
339
 
 
340
    return false;
203
341
  }
204
342
  close(fd);
205
343
 
206
 
  return 0;
 
344
  return true;
 
345
}
 
346
 
 
347
 
 
348
bool Schema::readTableFile(const std::string &path, message::Table &table_message)
 
349
{
 
350
  fstream input(path.c_str(), ios::in | ios::binary);
 
351
 
 
352
  if (input.good())
 
353
  {
 
354
    if (table_message.ParseFromIstream(&input))
 
355
    {
 
356
      return true;
 
357
    }
 
358
  }
 
359
  else
 
360
  {
 
361
    perror(path.c_str());
 
362
  }
 
363
 
 
364
  return false;
 
365
}
 
366
 
 
367
 
 
368
bool Schema::readSchemaFile(const std::string &schema_name, drizzled::message::Schema &schema_message)
 
369
{
 
370
  char db_opt_path[FN_REFLEN];
 
371
  size_t length;
 
372
 
 
373
  /*
 
374
    Pass an empty file name, and the database options file name as extension
 
375
    to avoid table name to file name encoding.
 
376
  */
 
377
  length= build_table_filename(db_opt_path, sizeof(db_opt_path),
 
378
                               schema_name.c_str(), "", false);
 
379
  strcpy(db_opt_path + length, MY_DB_OPT_FILE);
 
380
 
 
381
  fstream input(db_opt_path, ios::in | ios::binary);
 
382
 
 
383
  /**
 
384
    @note If parsing fails, either someone has done a "mkdir" or has deleted their opt file.
 
385
    So what do we do? We muddle through the adventure by generating 
 
386
    one with a name in it, and the charset set to the default.
 
387
  */
 
388
  if (input.good())
 
389
  {
 
390
    if (schema_message.ParseFromIstream(&input))
 
391
    {
 
392
      return true;
 
393
    }
 
394
  }
 
395
  else
 
396
  {
 
397
    perror(db_opt_path);
 
398
  }
 
399
 
 
400
  return false;
207
401
}