~ubuntu-branches/ubuntu/saucy/drizzle/saucy-proposed

« back to all changes in this revision

Viewing changes to drizzled/plugin/storage_engine.cc

  • Committer: Bazaar Package Importer
  • Author(s): Monty Taylor
  • Date: 2010-03-18 12:12:31 UTC
  • Revision ID: james.westby@ubuntu.com-20100318121231-k6g1xe6cshbwa0f8
Tags: upstream-2010.03.1347
ImportĀ upstreamĀ versionĀ 2010.03.1347

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
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 <algorithm>
 
29
#include <functional>
 
30
 
 
31
#include <google/protobuf/io/zero_copy_stream.h>
 
32
#include <google/protobuf/io/zero_copy_stream_impl.h>
 
33
 
 
34
#include "drizzled/my_hash.h"
 
35
#include "drizzled/cached_directory.h"
 
36
 
 
37
#include <drizzled/definitions.h>
 
38
#include <drizzled/base.h>
 
39
#include <drizzled/cursor.h>
 
40
#include <drizzled/plugin/storage_engine.h>
 
41
#include <drizzled/session.h>
 
42
#include <drizzled/error.h>
 
43
#include <drizzled/gettext.h>
 
44
#include <drizzled/unireg.h>
 
45
#include <drizzled/data_home.h>
 
46
#include "drizzled/errmsg_print.h"
 
47
#include "drizzled/xid.h"
 
48
#include "drizzled/sql_table.h"
 
49
#include "drizzled/global_charset_info.h"
 
50
#include "drizzled/plugin/authorization.h"
 
51
#include "drizzled/charset.h"
 
52
#include "drizzled/internal/my_sys.h"
 
53
#include "drizzled/db.h"
 
54
 
 
55
#include <drizzled/table_proto.h>
 
56
 
 
57
static bool shutdown_has_begun= false; // Once we put in the container for the vector/etc for engines this will go away.
 
58
 
 
59
using namespace std;
 
60
 
 
61
namespace drizzled
 
62
{
 
63
 
 
64
namespace plugin
 
65
{
 
66
 
 
67
static EngineVector vector_of_engines;
 
68
static EngineVector vector_of_schema_engines;
 
69
 
 
70
const std::string UNKNOWN_STRING("UNKNOWN");
 
71
const std::string DEFAULT_DEFINITION_FILE_EXT(".dfe");
 
72
 
 
73
static std::set<std::string> set_of_table_definition_ext;
 
74
 
 
75
StorageEngine::StorageEngine(const string name_arg,
 
76
                             const bitset<HTON_BIT_SIZE> &flags_arg)
 
77
    : Plugin(name_arg, "StorageEngine"),
 
78
      MonitoredInTransaction(), /* This gives the storage engine a "slot" or ID */
 
79
      flags(flags_arg)
 
80
{
 
81
  pthread_mutex_init(&proto_cache_mutex, NULL);
 
82
}
 
83
 
 
84
StorageEngine::~StorageEngine()
 
85
{
 
86
  pthread_mutex_destroy(&proto_cache_mutex);
 
87
}
 
88
 
 
89
void StorageEngine::setTransactionReadWrite(Session& session)
 
90
{
 
91
  TransactionContext &statement_ctx= session.transaction.stmt;
 
92
  statement_ctx.markModifiedNonTransData();
 
93
}
 
94
 
 
95
int StorageEngine::doRenameTable(Session *,
 
96
                                 const char *from,
 
97
                                 const char *to)
 
98
{
 
99
  int error= 0;
 
100
  for (const char **ext= bas_ext(); *ext ; ext++)
 
101
  {
 
102
    if (rename_file_ext(from, to, *ext))
 
103
    {
 
104
      if ((error=errno) != ENOENT)
 
105
        break;
 
106
      error= 0;
 
107
    }
 
108
  }
 
109
  return error;
 
110
}
 
111
 
 
112
 
 
113
/**
 
114
  Delete all files with extension from bas_ext().
 
115
 
 
116
  @param name           Base name of table
 
117
 
 
118
  @note
 
119
    We assume that the Cursor may return more extensions than
 
120
    was actually used for the file.
 
121
 
 
122
  @retval
 
123
    0   If we successfully deleted at least one file from base_ext and
 
124
    didn't get any other errors than ENOENT
 
125
  @retval
 
126
    !0  Error
 
127
*/
 
128
int StorageEngine::doDropTable(Session&,
 
129
                               const string &table_path)
 
130
{
 
131
  int error= 0;
 
132
  int enoent_or_zero= ENOENT;                   // Error if no file was deleted
 
133
  char buff[FN_REFLEN];
 
134
 
 
135
  for (const char **ext= bas_ext(); *ext ; ext++)
 
136
  {
 
137
    internal::fn_format(buff, table_path.c_str(), "", *ext,
 
138
              MY_UNPACK_FILENAME|MY_APPEND_EXT);
 
139
    if (internal::my_delete_with_symlink(buff, MYF(0)))
 
140
    {
 
141
      if ((error= errno) != ENOENT)
 
142
        break;
 
143
    }
 
144
    else
 
145
      enoent_or_zero= 0;                        // No error for ENOENT
 
146
    error= enoent_or_zero;
 
147
  }
 
148
  return error;
 
149
}
 
150
 
 
151
const char *StorageEngine::checkLowercaseNames(const char *path,
 
152
                                                       char *tmp_path)
 
153
{
 
154
  if (flags.test(HTON_BIT_FILE_BASED))
 
155
    return path;
 
156
 
 
157
  /* Ensure that table Cursor get path in lower case */
 
158
  if (tmp_path != path)
 
159
    strcpy(tmp_path, path);
 
160
 
 
161
  /*
 
162
    we only should turn into lowercase database/table part
 
163
    so start the process after homedirectory
 
164
  */
 
165
  if (strstr(tmp_path, drizzle_tmpdir) == tmp_path)
 
166
    my_casedn_str(files_charset_info, tmp_path + strlen(drizzle_tmpdir));
 
167
  else
 
168
    my_casedn_str(files_charset_info, tmp_path + drizzle_data_home_len);
 
169
 
 
170
  return tmp_path;
 
171
}
 
172
 
 
173
 
 
174
bool StorageEngine::addPlugin(StorageEngine *engine)
 
175
{
 
176
 
 
177
  vector_of_engines.push_back(engine);
 
178
 
 
179
  if (engine->getTableDefinitionFileExtension().length())
 
180
  {
 
181
    assert(engine->getTableDefinitionFileExtension().length() == DEFAULT_DEFINITION_FILE_EXT.length());
 
182
    set_of_table_definition_ext.insert(engine->getTableDefinitionFileExtension());
 
183
  }
 
184
 
 
185
  if (engine->check_flag(HTON_BIT_SCHEMA_DICTIONARY))
 
186
    vector_of_schema_engines.push_back(engine);
 
187
 
 
188
  return false;
 
189
}
 
190
 
 
191
void StorageEngine::removePlugin(StorageEngine *)
 
192
{
 
193
  if (shutdown_has_begun == false)
 
194
  {
 
195
    vector_of_engines.clear();
 
196
    vector_of_schema_engines.clear();
 
197
 
 
198
    shutdown_has_begun= true;
 
199
  }
 
200
}
 
201
 
 
202
class FindEngineByName
 
203
  : public unary_function<StorageEngine *, bool>
 
204
{
 
205
  const string target;
 
206
public:
 
207
  explicit FindEngineByName(const string target_arg)
 
208
    : target(target_arg)
 
209
  {}
 
210
  result_type operator() (argument_type engine)
 
211
  {
 
212
    string engine_name(engine->getName());
 
213
 
 
214
    transform(engine_name.begin(), engine_name.end(),
 
215
              engine_name.begin(), ::tolower);
 
216
    return engine_name == target;
 
217
  }
 
218
};
 
219
 
 
220
StorageEngine *StorageEngine::findByName(string find_str)
 
221
{
 
222
  transform(find_str.begin(), find_str.end(),
 
223
            find_str.begin(), ::tolower);
 
224
 
 
225
  
 
226
  EngineVector::iterator iter= find_if(vector_of_engines.begin(),
 
227
                                       vector_of_engines.end(),
 
228
                                       FindEngineByName(find_str));
 
229
  if (iter != vector_of_engines.end())
 
230
  {
 
231
    StorageEngine *engine= *iter;
 
232
    if (engine->is_user_selectable())
 
233
      return engine;
 
234
  }
 
235
 
 
236
  return NULL;
 
237
}
 
238
 
 
239
StorageEngine *StorageEngine::findByName(Session& session,
 
240
                                                         string find_str)
 
241
{
 
242
  
 
243
  transform(find_str.begin(), find_str.end(),
 
244
            find_str.begin(), ::tolower);
 
245
 
 
246
  if (find_str.compare("default") == 0)
 
247
    return session.getDefaultStorageEngine();
 
248
 
 
249
  EngineVector::iterator iter= find_if(vector_of_engines.begin(),
 
250
                                       vector_of_engines.end(),
 
251
                                       FindEngineByName(find_str));
 
252
  if (iter != vector_of_engines.end())
 
253
  {
 
254
    StorageEngine *engine= *iter;
 
255
    if (engine->is_user_selectable())
 
256
      return engine;
 
257
  }
 
258
 
 
259
  return NULL;
 
260
}
 
261
 
 
262
class StorageEngineCloseConnection
 
263
: public unary_function<StorageEngine *, void>
 
264
{
 
265
  Session *session;
 
266
public:
 
267
  StorageEngineCloseConnection(Session *session_arg) : session(session_arg) {}
 
268
  /*
 
269
    there's no need to rollback here as all transactions must
 
270
    be rolled back already
 
271
  */
 
272
  inline result_type operator() (argument_type engine)
 
273
  {
 
274
    if (*session->getEngineData(engine))
 
275
      engine->close_connection(session);
 
276
  }
 
277
};
 
278
 
 
279
/**
 
280
  @note
 
281
    don't bother to rollback here, it's done already
 
282
*/
 
283
void StorageEngine::closeConnection(Session* session)
 
284
{
 
285
  for_each(vector_of_engines.begin(), vector_of_engines.end(),
 
286
           StorageEngineCloseConnection(session));
 
287
}
 
288
 
 
289
bool StorageEngine::flushLogs(StorageEngine *engine)
 
290
{
 
291
  if (engine == NULL)
 
292
  {
 
293
    if (find_if(vector_of_engines.begin(), vector_of_engines.end(),
 
294
                mem_fun(&StorageEngine::flush_logs))
 
295
        != vector_of_engines.begin())
 
296
      return true;
 
297
  }
 
298
  else
 
299
  {
 
300
    if (engine->flush_logs())
 
301
      return true;
 
302
  }
 
303
  return false;
 
304
}
 
305
 
 
306
class StorageEngineGetTableDefinition: public unary_function<StorageEngine *,bool>
 
307
{
 
308
  Session& session;
 
309
  const char* path;
 
310
  const char *db;
 
311
  const char *table_name;
 
312
  const bool is_tmp;
 
313
  message::Table *table_message;
 
314
  int *err;
 
315
 
 
316
public:
 
317
  StorageEngineGetTableDefinition(Session& session_arg,
 
318
                                  const char* path_arg,
 
319
                                  const char *db_arg,
 
320
                                  const char *table_name_arg,
 
321
                                  const bool is_tmp_arg,
 
322
                                  message::Table *table_message_arg,
 
323
                                  int *err_arg) :
 
324
    session(session_arg), 
 
325
    path(path_arg), 
 
326
    db(db_arg),
 
327
    table_name(table_name_arg),
 
328
    is_tmp(is_tmp_arg),
 
329
    table_message(table_message_arg), 
 
330
    err(err_arg) {}
 
331
 
 
332
  result_type operator() (argument_type engine)
 
333
  {
 
334
    int ret= engine->doGetTableDefinition(session,
 
335
                                          path, 
 
336
                                          db,
 
337
                                          table_name,
 
338
                                          is_tmp,
 
339
                                          table_message);
 
340
 
 
341
    if (ret != ENOENT)
 
342
      *err= ret;
 
343
 
 
344
    return *err == EEXIST || *err != ENOENT;
 
345
  }
 
346
};
 
347
 
 
348
/**
 
349
  Utility method which hides some of the details of getTableDefinition()
 
350
*/
 
351
bool plugin::StorageEngine::doesTableExist(Session& session,
 
352
                                           TableIdentifier &identifier,
 
353
                                           bool include_temporary_tables)
 
354
{
 
355
  return (plugin::StorageEngine::getTableDefinition(session, identifier, NULL, include_temporary_tables) == EEXIST);
 
356
}
 
357
 
 
358
/**
 
359
  Call this function in order to give the Cursor the possiblity
 
360
  to ask engine if there are any new tables that should be written to disk
 
361
  or any dropped tables that need to be removed from disk
 
362
*/
 
363
int StorageEngine::getTableDefinition(Session& session,
 
364
                                      TableIdentifier &identifier,
 
365
                                      message::Table *table_message,
 
366
                                      bool include_temporary_tables)
 
367
{
 
368
  return getTableDefinition(session,
 
369
                            identifier.getPath(), identifier.getDBName(), identifier.getTableName(), identifier.isTmp(),
 
370
                            table_message, include_temporary_tables);
 
371
}
 
372
 
 
373
int StorageEngine::getTableDefinition(Session& session,
 
374
                                              const char* path,
 
375
                                              const char *schema_name,
 
376
                                              const char *table_name,
 
377
                                              const bool,
 
378
                                              message::Table *table_message,
 
379
                                              bool include_temporary_tables)
 
380
{
 
381
  int err= ENOENT;
 
382
 
 
383
  if (include_temporary_tables)
 
384
  {
 
385
    if (session.doGetTableDefinition(path, schema_name, table_name, false, table_message) == EEXIST)
 
386
      return EEXIST;
 
387
  }
 
388
 
 
389
  EngineVector::iterator iter=
 
390
    find_if(vector_of_engines.begin(), vector_of_engines.end(),
 
391
            StorageEngineGetTableDefinition(session, path, NULL, NULL, true, table_message, &err));
 
392
 
 
393
  if (iter == vector_of_engines.end())
 
394
  {
 
395
    return ENOENT;
 
396
  }
 
397
 
 
398
  return err;
 
399
}
 
400
 
 
401
/**
 
402
  An interceptor to hijack the text of the error message without
 
403
  setting an error in the thread. We need the text to present it
 
404
  in the form of a warning to the user.
 
405
*/
 
406
 
 
407
class Ha_delete_table_error_handler: public Internal_error_handler
 
408
{
 
409
public:
 
410
  Ha_delete_table_error_handler() : Internal_error_handler() {}
 
411
  virtual bool handle_error(uint32_t sql_errno,
 
412
                            const char *message,
 
413
                            DRIZZLE_ERROR::enum_warning_level level,
 
414
                            Session *session);
 
415
  char buff[DRIZZLE_ERRMSG_SIZE];
 
416
};
 
417
 
 
418
 
 
419
bool
 
420
Ha_delete_table_error_handler::
 
421
handle_error(uint32_t ,
 
422
             const char *message,
 
423
             DRIZZLE_ERROR::enum_warning_level ,
 
424
             Session *)
 
425
{
 
426
  /* Grab the error message */
 
427
  strncpy(buff, message, sizeof(buff)-1);
 
428
  return true;
 
429
}
 
430
 
 
431
class DropTable : 
 
432
  public unary_function<StorageEngine *, void>
 
433
{
 
434
  uint64_t &success_count;
 
435
  TableIdentifier &identifier;
 
436
  Session &session;
 
437
 
 
438
public:
 
439
 
 
440
  DropTable(Session &session_arg, TableIdentifier &arg, uint64_t &count_arg) :
 
441
    success_count(count_arg),
 
442
    identifier(arg),
 
443
    session(session_arg)
 
444
  {
 
445
  }
 
446
 
 
447
  result_type operator() (argument_type engine)
 
448
  {
 
449
    // @todo someday check that at least one engine said "true"
 
450
    std::string path(identifier.getPath());
 
451
    bool success= engine->doDropTable(session, path);
 
452
 
 
453
    if (success)
 
454
      success_count++;
 
455
  }
 
456
};
 
457
 
 
458
 
 
459
/**
 
460
   returns ENOENT if the file doesn't exists.
 
461
*/
 
462
int StorageEngine::dropTable(Session& session,
 
463
                             TableIdentifier &identifier)
 
464
{
 
465
  int error= 0;
 
466
  int error_proto;
 
467
  message::Table src_proto;
 
468
  StorageEngine* engine;
 
469
 
 
470
  error_proto= StorageEngine::getTableDefinition(session,
 
471
                                                 identifier,
 
472
                                                 &src_proto);
 
473
 
 
474
  if (error_proto == ER_CORRUPT_TABLE_DEFINITION)
 
475
  {
 
476
    my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
 
477
             src_proto.InitializationErrorString().c_str());
 
478
    return ER_CORRUPT_TABLE_DEFINITION;
 
479
  }
 
480
 
 
481
  engine= StorageEngine::findByName(session, src_proto.engine().name());
 
482
 
 
483
  if (engine)
 
484
  {
 
485
    std::string path(identifier.getPath());
 
486
    engine->setTransactionReadWrite(session);
 
487
    error= engine->doDropTable(session, path);
 
488
 
 
489
    if (not error)
 
490
    {
 
491
      if (not engine->check_flag(HTON_BIT_HAS_DATA_DICTIONARY))
 
492
      {
 
493
        uint64_t counter; // @todo We need to refactor to check that.
 
494
 
 
495
        for_each(vector_of_schema_engines.begin(), vector_of_schema_engines.end(),
 
496
                 DropTable(session, identifier, counter));
 
497
      }
 
498
    }
 
499
  }
 
500
 
 
501
  if (error_proto && error == 0)
 
502
    return 0;
 
503
 
 
504
  return error;
 
505
}
 
506
 
 
507
/**
 
508
  Initiates table-file and calls appropriate database-creator.
 
509
 
 
510
  @retval
 
511
   0  ok
 
512
  @retval
 
513
   1  error
 
514
 
 
515
   @todo refactor to remove goto
 
516
*/
 
517
int StorageEngine::createTable(Session& session,
 
518
                               TableIdentifier &identifier,
 
519
                               bool update_create_info,
 
520
                               message::Table& table_message)
 
521
{
 
522
  int error= 1;
 
523
  Table table;
 
524
  TableShare share(identifier.getDBName(), 0, identifier.getTableName(), identifier.getPath());
 
525
  message::Table tmp_proto;
 
526
 
 
527
  if (parse_table_proto(session, table_message, &share))
 
528
    goto err;
 
529
 
 
530
  if (open_table_from_share(&session, &share, "", 0, 0,
 
531
                            &table))
 
532
    goto err;
 
533
 
 
534
  if (update_create_info)
 
535
    table.updateCreateInfo(&table_message);
 
536
 
 
537
  /* Check for legal operations against the Engine using the proto (if used) */
 
538
  if (table_message.type() == message::Table::TEMPORARY &&
 
539
      share.storage_engine->check_flag(HTON_BIT_TEMPORARY_NOT_SUPPORTED) == true)
 
540
  {
 
541
    error= HA_ERR_UNSUPPORTED;
 
542
    goto err2;
 
543
  }
 
544
  else if (table_message.type() != message::Table::TEMPORARY &&
 
545
           share.storage_engine->check_flag(HTON_BIT_TEMPORARY_ONLY) == true)
 
546
  {
 
547
    error= HA_ERR_UNSUPPORTED;
 
548
    goto err2;
 
549
  }
 
550
 
 
551
  {
 
552
    char name_buff[FN_REFLEN];
 
553
    const char *table_name_arg;
 
554
 
 
555
    table_name_arg= share.storage_engine->checkLowercaseNames(identifier.getPath(), name_buff);
 
556
 
 
557
    if (not share.storage_engine->check_flag(HTON_BIT_HAS_DATA_DICTIONARY))
 
558
    {
 
559
      int protoerr= StorageEngine::writeDefinitionFromPath(identifier, table_message);
 
560
 
 
561
      if (protoerr)
 
562
      {
 
563
        error= protoerr;
 
564
        goto err2;
 
565
      }
 
566
    }
 
567
 
 
568
    share.storage_engine->setTransactionReadWrite(session);
 
569
 
 
570
    error= share.storage_engine->doCreateTable(&session,
 
571
                                               table_name_arg,
 
572
                                               table,
 
573
                                               table_message);
 
574
  }
 
575
 
 
576
err2:
 
577
  if (error)
 
578
  {
 
579
    if (not share.storage_engine->check_flag(HTON_BIT_HAS_DATA_DICTIONARY))
 
580
      plugin::StorageEngine::deleteDefinitionFromPath(identifier);
 
581
 
 
582
    my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), identifier.getSQLPath().c_str(), error);
 
583
  }
 
584
 
 
585
  table.closefrm(false);
 
586
 
 
587
err:
 
588
  share.free_table_share();
 
589
  return(error != 0);
 
590
}
 
591
 
 
592
Cursor *StorageEngine::getCursor(TableShare &share, memory::Root *alloc)
 
593
{
 
594
  return create(share, alloc);
 
595
}
 
596
 
 
597
/**
 
598
  TODO -> Remove this to force all engines to implement their own file. Solves the "we only looked at dfe" problem.
 
599
*/
 
600
void StorageEngine::doGetTableNames(CachedDirectory&, string&, set<string>&)
 
601
{ }
 
602
 
 
603
class AddTableName : 
 
604
  public unary_function<StorageEngine *, void>
 
605
{
 
606
  string db;
 
607
  CachedDirectory& directory;
 
608
  TableNameList &set_of_names;
 
609
 
 
610
public:
 
611
 
 
612
  AddTableName(CachedDirectory& directory_arg, const string& database_name, set<string>& of_names) :
 
613
    directory(directory_arg),
 
614
    set_of_names(of_names)
 
615
  {
 
616
    db= database_name;
 
617
  }
 
618
 
 
619
  result_type operator() (argument_type engine)
 
620
  {
 
621
    engine->doGetTableNames(directory, db, set_of_names);
 
622
  }
 
623
};
 
624
 
 
625
class AddSchemaNames : 
 
626
  public unary_function<StorageEngine *, void>
 
627
{
 
628
  SchemaNameList &set_of_names;
 
629
 
 
630
public:
 
631
 
 
632
  AddSchemaNames(set<string>& of_names) :
 
633
    set_of_names(of_names)
 
634
  {
 
635
  }
 
636
 
 
637
  result_type operator() (argument_type engine)
 
638
  {
 
639
    engine->doGetSchemaNames(set_of_names);
 
640
  }
 
641
};
 
642
 
 
643
void StorageEngine::getSchemaNames(SchemaNameList &set_of_names)
 
644
{
 
645
  // Add hook here for engines to register schema.
 
646
  for_each(vector_of_schema_engines.begin(), vector_of_schema_engines.end(),
 
647
           AddSchemaNames(set_of_names));
 
648
 
 
649
  plugin::Authorization::pruneSchemaNames(current_session->getSecurityContext(),
 
650
                                          set_of_names);
 
651
}
 
652
 
 
653
class StorageEngineGetSchemaDefinition: public unary_function<StorageEngine *, bool>
 
654
{
 
655
  const std::string &schema_name;
 
656
  message::Schema &schema_proto;
 
657
 
 
658
public:
 
659
  StorageEngineGetSchemaDefinition(const std::string &schema_name_arg,
 
660
                                  message::Schema &schema_proto_arg) :
 
661
    schema_name(schema_name_arg),
 
662
    schema_proto(schema_proto_arg) 
 
663
  { }
 
664
 
 
665
  result_type operator() (argument_type engine)
 
666
  {
 
667
    return engine->doGetSchemaDefinition(schema_name, schema_proto);
 
668
  }
 
669
};
 
670
 
 
671
/*
 
672
  Return value is "if parsed"
 
673
*/
 
674
bool StorageEngine::getSchemaDefinition(const std::string &schema_name, message::Schema &proto)
 
675
{
 
676
  proto.Clear();
 
677
 
 
678
  EngineVector::iterator iter=
 
679
    find_if(vector_of_schema_engines.begin(), vector_of_schema_engines.end(),
 
680
            StorageEngineGetSchemaDefinition(schema_name, proto));
 
681
 
 
682
  if (iter != vector_of_schema_engines.end())
 
683
  {
 
684
    return true;
 
685
  }
 
686
 
 
687
  return false;
 
688
}
 
689
 
 
690
bool StorageEngine::doesSchemaExist(const std::string &schema_name)
 
691
{
 
692
  message::Schema proto;
 
693
 
 
694
  return StorageEngine::getSchemaDefinition(schema_name, proto);
 
695
}
 
696
 
 
697
 
 
698
const CHARSET_INFO *StorageEngine::getSchemaCollation(const std::string &schema_name)
 
699
{
 
700
  message::Schema schmema_proto;
 
701
  bool found;
 
702
 
 
703
  found= StorageEngine::getSchemaDefinition(schema_name, schmema_proto);
 
704
 
 
705
  if (found && schmema_proto.has_collation())
 
706
  {
 
707
    const string buffer= schmema_proto.collation();
 
708
    const CHARSET_INFO* cs= get_charset_by_name(buffer.c_str());
 
709
 
 
710
    if (not cs)
 
711
    {
 
712
      errmsg_printf(ERRMSG_LVL_ERROR,
 
713
                    _("Error while loading database options: '%s':"), schema_name.c_str());
 
714
      errmsg_printf(ERRMSG_LVL_ERROR, ER(ER_UNKNOWN_COLLATION), buffer.c_str());
 
715
 
 
716
      return default_charset_info;
 
717
    }
 
718
 
 
719
    return cs;
 
720
  }
 
721
 
 
722
  return default_charset_info;
 
723
}
 
724
 
 
725
class CreateSchema : 
 
726
  public unary_function<StorageEngine *, void>
 
727
{
 
728
  const drizzled::message::Schema &schema_message;
 
729
 
 
730
public:
 
731
 
 
732
  CreateSchema(const drizzled::message::Schema &arg) :
 
733
    schema_message(arg)
 
734
  {
 
735
  }
 
736
 
 
737
  result_type operator() (argument_type engine)
 
738
  {
 
739
    // @todo eomeday check that at least one engine said "true"
 
740
    (void)engine->doCreateSchema(schema_message);
 
741
  }
 
742
};
 
743
 
 
744
bool StorageEngine::createSchema(const drizzled::message::Schema &schema_message)
 
745
{
 
746
  // Add hook here for engines to register schema.
 
747
  for_each(vector_of_schema_engines.begin(), vector_of_schema_engines.end(),
 
748
           CreateSchema(schema_message));
 
749
 
 
750
  return true;
 
751
}
 
752
 
 
753
class DropSchema : 
 
754
  public unary_function<StorageEngine *, void>
 
755
{
 
756
  uint64_t &success_count;
 
757
  const string &schema_name;
 
758
 
 
759
public:
 
760
 
 
761
  DropSchema(const string &arg, uint64_t &count_arg) :
 
762
    success_count(count_arg),
 
763
    schema_name(arg)
 
764
  {
 
765
  }
 
766
 
 
767
  result_type operator() (argument_type engine)
 
768
  {
 
769
    // @todo someday check that at least one engine said "true"
 
770
    bool success= engine->doDropSchema(schema_name);
 
771
 
 
772
    if (success)
 
773
      success_count++;
 
774
  }
 
775
};
 
776
 
 
777
bool StorageEngine::dropSchema(const string &schema_name)
 
778
{
 
779
  uint64_t counter= 0;
 
780
  // Add hook here for engines to register schema.
 
781
  for_each(vector_of_schema_engines.begin(), vector_of_schema_engines.end(),
 
782
           DropSchema(schema_name, counter));
 
783
 
 
784
  return counter ? true : false;
 
785
}
 
786
 
 
787
class AlterSchema : 
 
788
  public unary_function<StorageEngine *, void>
 
789
{
 
790
  uint64_t &success_count;
 
791
  const drizzled::message::Schema &schema_message;
 
792
 
 
793
public:
 
794
 
 
795
  AlterSchema(const drizzled::message::Schema &arg, uint64_t &count_arg) :
 
796
    success_count(count_arg),
 
797
    schema_message(arg)
 
798
  {
 
799
  }
 
800
 
 
801
  result_type operator() (argument_type engine)
 
802
  {
 
803
    // @todo eomeday check that at least one engine said "true"
 
804
    bool success= engine->doAlterSchema(schema_message);
 
805
 
 
806
    if (success)
 
807
      success_count++;
 
808
  }
 
809
};
 
810
 
 
811
bool StorageEngine::alterSchema(const drizzled::message::Schema &schema_message)
 
812
{
 
813
  uint64_t success_count= 0;
 
814
 
 
815
  for_each(vector_of_schema_engines.begin(), vector_of_schema_engines.end(),
 
816
           AlterSchema(schema_message, success_count));
 
817
 
 
818
  return success_count ? true : false;
 
819
}
 
820
 
 
821
 
 
822
void StorageEngine::getTableNames(const string &schema_name, TableNameList &set_of_names)
 
823
{
 
824
  char tmp_path[FN_REFLEN];
 
825
 
 
826
  build_table_filename(tmp_path, sizeof(tmp_path), schema_name.c_str(), "", false);
 
827
 
 
828
  CachedDirectory directory(tmp_path, set_of_table_definition_ext);
 
829
 
 
830
  if (not schema_name.compare("information_schema"))
 
831
  { }
 
832
  else if (not schema_name.compare("data_dictionary"))
 
833
  { }
 
834
  else
 
835
  {
 
836
    if (directory.fail())
 
837
    {
 
838
      errno= directory.getError();
 
839
      if (errno == ENOENT)
 
840
        my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), schema_name.c_str());
 
841
      else
 
842
        my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), directory.getPath(), errno);
 
843
      return;
 
844
    }
 
845
  }
 
846
 
 
847
  for_each(vector_of_engines.begin(), vector_of_engines.end(),
 
848
           AddTableName(directory, schema_name, set_of_names));
 
849
 
 
850
  Session *session= current_session;
 
851
 
 
852
  session->doGetTableNames(directory, schema_name, set_of_names);
 
853
 
 
854
}
 
855
 
 
856
/* This will later be converted to TableIdentifiers */
 
857
class DropTables: public unary_function<StorageEngine *, void>
 
858
{
 
859
  Session &session;
 
860
  TableNameList &set_of_names;
 
861
 
 
862
public:
 
863
 
 
864
  DropTables(Session &session_arg, set<string>& of_names) :
 
865
    session(session_arg),
 
866
    set_of_names(of_names)
 
867
  { }
 
868
 
 
869
  result_type operator() (argument_type engine)
 
870
  {
 
871
 
 
872
    for (TableNameList::iterator iter= set_of_names.begin();
 
873
         iter != set_of_names.end();
 
874
         iter++)
 
875
    {
 
876
      int error= engine->doDropTable(session, *iter);
 
877
 
 
878
      // On a return of zero we know we found and deleted the table. So we
 
879
      // remove it from our search.
 
880
      if (not error)
 
881
        set_of_names.erase(iter);
 
882
    }
 
883
  }
 
884
};
 
885
 
 
886
/*
 
887
  This only works for engines which use file based DFE.
 
888
 
 
889
  Note-> Unlike MySQL, we do not, on purpose, delete files that do not match any engines. 
 
890
*/
 
891
void StorageEngine::removeLostTemporaryTables(Session &session, const char *directory)
 
892
{
 
893
  CachedDirectory dir(directory, set_of_table_definition_ext);
 
894
  set<string> set_of_table_names;
 
895
 
 
896
  if (dir.fail())
 
897
  {
 
898
    errno= dir.getError();
 
899
    my_error(ER_CANT_READ_DIR, MYF(0), directory, errno);
 
900
 
 
901
    return;
 
902
  }
 
903
 
 
904
  CachedDirectory::Entries files= dir.getEntries();
 
905
 
 
906
  for (CachedDirectory::Entries::iterator fileIter= files.begin();
 
907
       fileIter != files.end(); fileIter++)
 
908
  {
 
909
    size_t length;
 
910
    string path;
 
911
    CachedDirectory::Entry *entry= *fileIter;
 
912
 
 
913
    /* We remove the file extension. */
 
914
    length= entry->filename.length();
 
915
    entry->filename.resize(length - DEFAULT_DEFINITION_FILE_EXT.length());
 
916
 
 
917
    path+= directory;
 
918
    path+= FN_LIBCHAR;
 
919
    path+= entry->filename;
 
920
    set_of_table_names.insert(path);
 
921
  }
 
922
 
 
923
  for_each(vector_of_engines.begin(), vector_of_engines.end(),
 
924
           DropTables(session, set_of_table_names));
 
925
  
 
926
  /*
 
927
    Now we just clean up anything that might left over.
 
928
 
 
929
    We rescan because some of what might have been there should
 
930
    now be all nice and cleaned up.
 
931
  */
 
932
  set<string> all_exts= set_of_table_definition_ext;
 
933
 
 
934
  for (EngineVector::iterator iter= vector_of_engines.begin();
 
935
       iter != vector_of_engines.end() ; iter++)
 
936
  {
 
937
    for (const char **ext= (*iter)->bas_ext(); *ext ; ext++)
 
938
      all_exts.insert(*ext);
 
939
  }
 
940
 
 
941
  CachedDirectory rescan(directory, all_exts);
 
942
 
 
943
  files= rescan.getEntries();
 
944
  for (CachedDirectory::Entries::iterator fileIter= files.begin();
 
945
       fileIter != files.end(); fileIter++)
 
946
  {
 
947
    string path;
 
948
    CachedDirectory::Entry *entry= *fileIter;
 
949
 
 
950
    path+= directory;
 
951
    path+= FN_LIBCHAR;
 
952
    path+= entry->filename;
 
953
 
 
954
    unlink(path.c_str());
 
955
  }
 
956
}
 
957
 
 
958
 
 
959
/**
 
960
  Print error that we got from Cursor function.
 
961
 
 
962
  @note
 
963
    In case of delete table it's only safe to use the following parts of
 
964
    the 'table' structure:
 
965
    - table->s->path
 
966
    - table->alias
 
967
*/
 
968
void StorageEngine::print_error(int error, myf errflag, Table &table)
 
969
{
 
970
  print_error(error, errflag, &table);
 
971
}
 
972
 
 
973
void StorageEngine::print_error(int error, myf errflag, Table *table)
 
974
{
 
975
  int textno= ER_GET_ERRNO;
 
976
  switch (error) {
 
977
  case EACCES:
 
978
    textno=ER_OPEN_AS_READONLY;
 
979
    break;
 
980
  case EAGAIN:
 
981
    textno=ER_FILE_USED;
 
982
    break;
 
983
  case ENOENT:
 
984
    textno=ER_FILE_NOT_FOUND;
 
985
    break;
 
986
  case HA_ERR_KEY_NOT_FOUND:
 
987
  case HA_ERR_NO_ACTIVE_RECORD:
 
988
  case HA_ERR_END_OF_FILE:
 
989
    textno=ER_KEY_NOT_FOUND;
 
990
    break;
 
991
  case HA_ERR_WRONG_MRG_TABLE_DEF:
 
992
    textno=ER_WRONG_MRG_TABLE;
 
993
    break;
 
994
  case HA_ERR_FOUND_DUPP_KEY:
 
995
  {
 
996
    assert(table);
 
997
    uint32_t key_nr= table->get_dup_key(error);
 
998
    if ((int) key_nr >= 0)
 
999
    {
 
1000
      const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
 
1001
 
 
1002
      if (key_nr == 0 &&
 
1003
          (table->key_info[0].key_part[0].field->flags &
 
1004
           AUTO_INCREMENT_FLAG)
 
1005
          && (current_session)->lex->sql_command == SQLCOM_ALTER_TABLE)
 
1006
      {
 
1007
        err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
 
1008
      }
 
1009
 
 
1010
      print_keydup_error(key_nr, err_msg, *table);
 
1011
      return;
 
1012
    }
 
1013
    textno=ER_DUP_KEY;
 
1014
    break;
 
1015
  }
 
1016
  case HA_ERR_FOREIGN_DUPLICATE_KEY:
 
1017
  {
 
1018
    assert(table);
 
1019
    uint32_t key_nr= table->get_dup_key(error);
 
1020
    if ((int) key_nr >= 0)
 
1021
    {
 
1022
      uint32_t max_length;
 
1023
 
 
1024
      /* Write the key in the error message */
 
1025
      char key[MAX_KEY_LENGTH];
 
1026
      String str(key,sizeof(key),system_charset_info);
 
1027
 
 
1028
      /* Table is opened and defined at this point */
 
1029
      key_unpack(&str,table,(uint32_t) key_nr);
 
1030
      max_length= (DRIZZLE_ERRMSG_SIZE-
 
1031
                   (uint32_t) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
 
1032
      if (str.length() >= max_length)
 
1033
      {
 
1034
        str.length(max_length-4);
 
1035
        str.append(STRING_WITH_LEN("..."));
 
1036
      }
 
1037
      my_error(ER_FOREIGN_DUPLICATE_KEY, MYF(0), table->s->table_name.str,
 
1038
        str.c_ptr(), key_nr+1);
 
1039
      return;
 
1040
    }
 
1041
    textno= ER_DUP_KEY;
 
1042
    break;
 
1043
  }
 
1044
  case HA_ERR_FOUND_DUPP_UNIQUE:
 
1045
    textno=ER_DUP_UNIQUE;
 
1046
    break;
 
1047
  case HA_ERR_RECORD_CHANGED:
 
1048
    textno=ER_CHECKREAD;
 
1049
    break;
 
1050
  case HA_ERR_CRASHED:
 
1051
    textno=ER_NOT_KEYFILE;
 
1052
    break;
 
1053
  case HA_ERR_WRONG_IN_RECORD:
 
1054
    textno= ER_CRASHED_ON_USAGE;
 
1055
    break;
 
1056
  case HA_ERR_CRASHED_ON_USAGE:
 
1057
    textno=ER_CRASHED_ON_USAGE;
 
1058
    break;
 
1059
  case HA_ERR_NOT_A_TABLE:
 
1060
    textno= error;
 
1061
    break;
 
1062
  case HA_ERR_CRASHED_ON_REPAIR:
 
1063
    textno=ER_CRASHED_ON_REPAIR;
 
1064
    break;
 
1065
  case HA_ERR_OUT_OF_MEM:
 
1066
    textno=ER_OUT_OF_RESOURCES;
 
1067
    break;
 
1068
  case HA_ERR_WRONG_COMMAND:
 
1069
    textno=ER_ILLEGAL_HA;
 
1070
    break;
 
1071
  case HA_ERR_OLD_FILE:
 
1072
    textno=ER_OLD_KEYFILE;
 
1073
    break;
 
1074
  case HA_ERR_UNSUPPORTED:
 
1075
    textno=ER_UNSUPPORTED_EXTENSION;
 
1076
    break;
 
1077
  case HA_ERR_RECORD_FILE_FULL:
 
1078
  case HA_ERR_INDEX_FILE_FULL:
 
1079
    textno=ER_RECORD_FILE_FULL;
 
1080
    break;
 
1081
  case HA_ERR_LOCK_WAIT_TIMEOUT:
 
1082
    textno=ER_LOCK_WAIT_TIMEOUT;
 
1083
    break;
 
1084
  case HA_ERR_LOCK_TABLE_FULL:
 
1085
    textno=ER_LOCK_TABLE_FULL;
 
1086
    break;
 
1087
  case HA_ERR_LOCK_DEADLOCK:
 
1088
    textno=ER_LOCK_DEADLOCK;
 
1089
    break;
 
1090
  case HA_ERR_READ_ONLY_TRANSACTION:
 
1091
    textno=ER_READ_ONLY_TRANSACTION;
 
1092
    break;
 
1093
  case HA_ERR_CANNOT_ADD_FOREIGN:
 
1094
    textno=ER_CANNOT_ADD_FOREIGN;
 
1095
    break;
 
1096
  case HA_ERR_ROW_IS_REFERENCED:
 
1097
  {
 
1098
    String str;
 
1099
    get_error_message(error, &str);
 
1100
    my_error(ER_ROW_IS_REFERENCED_2, MYF(0), str.c_ptr_safe());
 
1101
    return;
 
1102
  }
 
1103
  case HA_ERR_NO_REFERENCED_ROW:
 
1104
  {
 
1105
    String str;
 
1106
    get_error_message(error, &str);
 
1107
    my_error(ER_NO_REFERENCED_ROW_2, MYF(0), str.c_ptr_safe());
 
1108
    return;
 
1109
  }
 
1110
  case HA_ERR_TABLE_DEF_CHANGED:
 
1111
    textno=ER_TABLE_DEF_CHANGED;
 
1112
    break;
 
1113
  case HA_ERR_NO_SUCH_TABLE:
 
1114
    assert(table);
 
1115
    my_error(ER_NO_SUCH_TABLE, MYF(0), table->s->getSchemaName(),
 
1116
             table->s->table_name.str);
 
1117
    return;
 
1118
  case HA_ERR_RBR_LOGGING_FAILED:
 
1119
    textno= ER_BINLOG_ROW_LOGGING_FAILED;
 
1120
    break;
 
1121
  case HA_ERR_DROP_INDEX_FK:
 
1122
  {
 
1123
    assert(table);
 
1124
    const char *ptr= "???";
 
1125
    uint32_t key_nr= table->get_dup_key(error);
 
1126
    if ((int) key_nr >= 0)
 
1127
      ptr= table->key_info[key_nr].name;
 
1128
    my_error(ER_DROP_INDEX_FK, MYF(0), ptr);
 
1129
    return;
 
1130
  }
 
1131
  case HA_ERR_TABLE_NEEDS_UPGRADE:
 
1132
    textno=ER_TABLE_NEEDS_UPGRADE;
 
1133
    break;
 
1134
  case HA_ERR_TABLE_READONLY:
 
1135
    textno= ER_OPEN_AS_READONLY;
 
1136
    break;
 
1137
  case HA_ERR_AUTOINC_READ_FAILED:
 
1138
    textno= ER_AUTOINC_READ_FAILED;
 
1139
    break;
 
1140
  case HA_ERR_AUTOINC_ERANGE:
 
1141
    textno= ER_WARN_DATA_OUT_OF_RANGE;
 
1142
    break;
 
1143
  case HA_ERR_LOCK_OR_ACTIVE_TRANSACTION:
 
1144
    my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
 
1145
               ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
 
1146
    return;
 
1147
  default:
 
1148
    {
 
1149
      /* 
 
1150
        The error was "unknown" to this function.
 
1151
        Ask Cursor if it has got a message for this error 
 
1152
      */
 
1153
      bool temporary= false;
 
1154
      String str;
 
1155
      temporary= get_error_message(error, &str);
 
1156
      if (!str.is_empty())
 
1157
      {
 
1158
        const char* engine_name= getName().c_str();
 
1159
        if (temporary)
 
1160
          my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(),
 
1161
                   engine_name);
 
1162
        else
 
1163
          my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine_name);
 
1164
      }
 
1165
      else
 
1166
      {
 
1167
              my_error(ER_GET_ERRNO,errflag,error);
 
1168
      }
 
1169
      return;
 
1170
    }
 
1171
  }
 
1172
  my_error(textno, errflag, table->s->table_name.str, error);
 
1173
}
 
1174
 
 
1175
 
 
1176
/**
 
1177
  Return an error message specific to this Cursor.
 
1178
 
 
1179
  @param error  error code previously returned by Cursor
 
1180
  @param buf    pointer to String where to add error message
 
1181
 
 
1182
  @return
 
1183
    Returns true if this is a temporary error
 
1184
*/
 
1185
bool StorageEngine::get_error_message(int , String* )
 
1186
{
 
1187
  return false;
 
1188
}
 
1189
 
 
1190
 
 
1191
void StorageEngine::print_keydup_error(uint32_t key_nr, const char *msg, Table &table)
 
1192
{
 
1193
  /* Write the duplicated key in the error message */
 
1194
  char key[MAX_KEY_LENGTH];
 
1195
  String str(key,sizeof(key),system_charset_info);
 
1196
 
 
1197
  if (key_nr == MAX_KEY)
 
1198
  {
 
1199
    /* Key is unknown */
 
1200
    str.copy("", 0, system_charset_info);
 
1201
    my_printf_error(ER_DUP_ENTRY, msg, MYF(0), str.c_ptr(), "*UNKNOWN*");
 
1202
  }
 
1203
  else
 
1204
  {
 
1205
    /* Table is opened and defined at this point */
 
1206
    key_unpack(&str, &table, (uint32_t) key_nr);
 
1207
    uint32_t max_length=DRIZZLE_ERRMSG_SIZE-(uint32_t) strlen(msg);
 
1208
    if (str.length() >= max_length)
 
1209
    {
 
1210
      str.length(max_length-4);
 
1211
      str.append(STRING_WITH_LEN("..."));
 
1212
    }
 
1213
    my_printf_error(ER_DUP_ENTRY, msg,
 
1214
                    MYF(0), str.c_ptr(), table.key_info[key_nr].name);
 
1215
  }
 
1216
}
 
1217
 
 
1218
 
 
1219
int StorageEngine::deleteDefinitionFromPath(TableIdentifier &identifier)
 
1220
{
 
1221
  string path(identifier.getPath());
 
1222
 
 
1223
  path.append(DEFAULT_DEFINITION_FILE_EXT);
 
1224
 
 
1225
  return internal::my_delete(path.c_str(), MYF(0));
 
1226
}
 
1227
 
 
1228
int StorageEngine::renameDefinitionFromPath(TableIdentifier &dest, TableIdentifier &src)
 
1229
{
 
1230
  string src_path(src.getPath());
 
1231
  string dest_path(dest.getPath());
 
1232
 
 
1233
  src_path.append(DEFAULT_DEFINITION_FILE_EXT);
 
1234
  dest_path.append(DEFAULT_DEFINITION_FILE_EXT);
 
1235
 
 
1236
  return internal::my_rename(src_path.c_str(), dest_path.c_str(), MYF(MY_WME));
 
1237
}
 
1238
 
 
1239
int StorageEngine::writeDefinitionFromPath(TableIdentifier &identifier, message::Table &table_message)
 
1240
{
 
1241
  string file_name(identifier.getPath());
 
1242
 
 
1243
  file_name.append(DEFAULT_DEFINITION_FILE_EXT);
 
1244
 
 
1245
  int fd= open(file_name.c_str(), O_RDWR|O_CREAT|O_TRUNC, internal::my_umask);
 
1246
 
 
1247
  if (fd == -1)
 
1248
    return errno;
 
1249
 
 
1250
  google::protobuf::io::ZeroCopyOutputStream* output=
 
1251
    new google::protobuf::io::FileOutputStream(fd);
 
1252
 
 
1253
  if (table_message.SerializeToZeroCopyStream(output) == false)
 
1254
  {
 
1255
    delete output;
 
1256
    close(fd);
 
1257
    return errno;
 
1258
  }
 
1259
 
 
1260
  delete output;
 
1261
  close(fd);
 
1262
  return 0;
 
1263
}
 
1264
 
 
1265
class CanCreateTable: public unary_function<StorageEngine *, bool>
 
1266
{
 
1267
  const TableIdentifier &identifier;
 
1268
 
 
1269
public:
 
1270
  CanCreateTable(const TableIdentifier &identifier_arg) :
 
1271
    identifier(identifier_arg)
 
1272
  { }
 
1273
 
 
1274
  result_type operator() (argument_type engine)
 
1275
  {
 
1276
    return not engine->doCanCreateTable(identifier);
 
1277
  }
 
1278
};
 
1279
 
 
1280
 
 
1281
/**
 
1282
  @note on success table can be created.
 
1283
*/
 
1284
bool StorageEngine::canCreateTable(drizzled::TableIdentifier &identifier)
 
1285
{
 
1286
  EngineVector::iterator iter=
 
1287
    find_if(vector_of_engines.begin(), vector_of_engines.end(),
 
1288
            CanCreateTable(identifier));
 
1289
 
 
1290
  if (iter == vector_of_engines.end())
 
1291
  {
 
1292
    return true;
 
1293
  }
 
1294
 
 
1295
  return false;
 
1296
}
 
1297
 
 
1298
} /* namespace plugin */
 
1299
} /* namespace drizzled */