~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/plugin/storage_engine.cc

  • Committer: Monty Taylor
  • Date: 2010-03-11 18:27:20 UTC
  • mfrom: (1333 staging)
  • mto: This revision was merged to the branch mainline in revision 1348.
  • Revision ID: mordred@inaugust.com-20100311182720-hd1h87y6cb1b1mp0
Merged trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
47
47
#include "drizzled/xid.h"
48
48
#include "drizzled/sql_table.h"
49
49
#include "drizzled/global_charset_info.h"
 
50
#include "drizzled/plugin/authorization.h"
 
51
#include "drizzled/charset.h"
50
52
#include "drizzled/internal/my_sys.h"
51
 
 
 
53
#include "drizzled/db.h"
52
54
 
53
55
#include <drizzled/table_proto.h>
54
56
 
55
 
#include "drizzled/hash.h"
56
 
 
57
57
static bool shutdown_has_begun= false; // Once we put in the container for the vector/etc for engines this will go away.
58
58
 
59
59
using namespace std;
61
61
namespace drizzled
62
62
{
63
63
 
64
 
typedef hash_map<std::string, plugin::StorageEngine *> EngineMap;
65
 
typedef std::vector<plugin::StorageEngine *> EngineVector;
 
64
namespace plugin
 
65
{
66
66
 
67
67
static EngineVector vector_of_engines;
68
 
static EngineVector vector_of_transactional_engines;
 
68
static EngineVector vector_of_schema_engines;
69
69
 
70
 
const std::string plugin::UNKNOWN_STRING("UNKNOWN");
71
 
const std::string plugin::DEFAULT_DEFINITION_FILE_EXT(".dfe");
 
70
const std::string UNKNOWN_STRING("UNKNOWN");
 
71
const std::string DEFAULT_DEFINITION_FILE_EXT(".dfe");
72
72
 
73
73
static std::set<std::string> set_of_table_definition_ext;
74
74
 
75
 
plugin::StorageEngine::StorageEngine(const string name_arg,
76
 
                                     const bitset<HTON_BIT_SIZE> &flags_arg,
77
 
                                     bool support_2pc)
 
75
StorageEngine::StorageEngine(const string name_arg,
 
76
                             const bitset<HTON_BIT_SIZE> &flags_arg)
78
77
    : Plugin(name_arg, "StorageEngine"),
79
 
      two_phase_commit(support_2pc),
80
 
      enabled(true),
81
 
      flags(flags_arg),
82
 
      slot(0)
 
78
      MonitoredInTransaction(), /* This gives the storage engine a "slot" or ID */
 
79
      flags(flags_arg)
83
80
{
84
 
  if (enabled)
85
 
  {
86
 
    slot= total_ha++;
87
 
    if (two_phase_commit)
88
 
        total_ha_2pc++;
89
 
  }
90
81
  pthread_mutex_init(&proto_cache_mutex, NULL);
91
82
}
92
83
 
93
 
 
94
 
plugin::StorageEngine::~StorageEngine()
 
84
StorageEngine::~StorageEngine()
95
85
{
96
86
  pthread_mutex_destroy(&proto_cache_mutex);
97
87
}
98
88
 
99
 
void plugin::StorageEngine::setTransactionReadWrite(Session& session)
 
89
void StorageEngine::setTransactionReadWrite(Session& session)
100
90
{
101
 
  Ha_trx_info *ha_info= session.getEngineInfo(this);
102
 
 
103
 
  /*
104
 
    When a storage engine method is called, the transaction must
105
 
    have been started, unless it's a DDL call, for which the
106
 
    storage engine starts the transaction internally, and commits
107
 
    it internally, without registering in the ha_list.
108
 
    Unfortunately here we can't know know for sure if the engine
109
 
    has registered the transaction or not, so we must check.
110
 
  */
111
 
  if (ha_info->is_started())
112
 
  {
113
 
    /*
114
 
     * table_share can be NULL in plugin::StorageEngine::dropTable().
115
 
     */
116
 
    ha_info->set_trx_read_write();
117
 
  }
 
91
  TransactionContext &statement_ctx= session.transaction.stmt;
 
92
  statement_ctx.markModifiedNonTransData();
118
93
}
119
94
 
120
 
 
121
 
 
122
 
int plugin::StorageEngine::doRenameTable(Session *,
123
 
                                         const char *from,
124
 
                                         const char *to)
 
95
int StorageEngine::doRenameTable(Session *,
 
96
                                 const char *from,
 
97
                                 const char *to)
125
98
{
126
99
  int error= 0;
127
100
  for (const char **ext= bas_ext(); *ext ; ext++)
152
125
  @retval
153
126
    !0  Error
154
127
*/
155
 
int plugin::StorageEngine::doDropTable(Session&,
156
 
                                       const string table_path)
 
128
int StorageEngine::doDropTable(Session&,
 
129
                               const string &table_path)
157
130
{
158
131
  int error= 0;
159
132
  int enoent_or_zero= ENOENT;                   // Error if no file was deleted
175
148
  return error;
176
149
}
177
150
 
178
 
const char *plugin::StorageEngine::checkLowercaseNames(const char *path,
 
151
const char *StorageEngine::checkLowercaseNames(const char *path,
179
152
                                                       char *tmp_path)
180
153
{
181
154
  if (flags.test(HTON_BIT_FILE_BASED))
198
171
}
199
172
 
200
173
 
201
 
bool plugin::StorageEngine::addPlugin(plugin::StorageEngine *engine)
 
174
bool StorageEngine::addPlugin(StorageEngine *engine)
202
175
{
203
176
 
204
177
  vector_of_engines.push_back(engine);
205
178
 
206
 
  if (engine->check_flag(HTON_BIT_DOES_TRANSACTIONS))
207
 
    vector_of_transactional_engines.push_back(engine);
208
 
 
209
179
  if (engine->getTableDefinitionFileExtension().length())
210
180
  {
211
181
    assert(engine->getTableDefinitionFileExtension().length() == DEFAULT_DEFINITION_FILE_EXT.length());
212
182
    set_of_table_definition_ext.insert(engine->getTableDefinitionFileExtension());
213
183
  }
214
184
 
 
185
  if (engine->check_flag(HTON_BIT_SCHEMA_DICTIONARY))
 
186
    vector_of_schema_engines.push_back(engine);
 
187
 
215
188
  return false;
216
189
}
217
190
 
218
 
void plugin::StorageEngine::removePlugin(plugin::StorageEngine *)
 
191
void StorageEngine::removePlugin(StorageEngine *)
219
192
{
220
193
  if (shutdown_has_begun == false)
221
194
  {
222
195
    vector_of_engines.clear();
223
 
    vector_of_transactional_engines.clear();
 
196
    vector_of_schema_engines.clear();
224
197
 
225
198
    shutdown_has_begun= true;
226
199
  }
227
200
}
228
201
 
229
202
class FindEngineByName
230
 
  : public unary_function<plugin::StorageEngine *, bool>
 
203
  : public unary_function<StorageEngine *, bool>
231
204
{
232
205
  const string target;
233
206
public:
244
217
  }
245
218
};
246
219
 
247
 
plugin::StorageEngine *plugin::StorageEngine::findByName(string find_str)
 
220
StorageEngine *StorageEngine::findByName(string find_str)
248
221
{
249
222
  transform(find_str.begin(), find_str.end(),
250
223
            find_str.begin(), ::tolower);
263
236
  return NULL;
264
237
}
265
238
 
266
 
plugin::StorageEngine *plugin::StorageEngine::findByName(Session& session,
 
239
StorageEngine *StorageEngine::findByName(Session& session,
267
240
                                                         string find_str)
268
241
{
269
242
  
287
260
}
288
261
 
289
262
class StorageEngineCloseConnection
290
 
: public unary_function<plugin::StorageEngine *, void>
 
263
: public unary_function<StorageEngine *, void>
291
264
{
292
265
  Session *session;
293
266
public:
298
271
  */
299
272
  inline result_type operator() (argument_type engine)
300
273
  {
301
 
    if (engine->is_enabled() && (*session->getEngineData(engine)))
 
274
    if (*session->getEngineData(engine))
302
275
      engine->close_connection(session);
303
276
  }
304
277
};
307
280
  @note
308
281
    don't bother to rollback here, it's done already
309
282
*/
310
 
void plugin::StorageEngine::closeConnection(Session* session)
 
283
void StorageEngine::closeConnection(Session* session)
311
284
{
312
285
  for_each(vector_of_engines.begin(), vector_of_engines.end(),
313
286
           StorageEngineCloseConnection(session));
314
287
}
315
288
 
316
 
void plugin::StorageEngine::dropDatabase(char* path)
317
 
{
318
 
  for_each(vector_of_engines.begin(), vector_of_engines.end(),
319
 
           bind2nd(mem_fun(&plugin::StorageEngine::drop_database),path));
320
 
}
321
 
 
322
 
int plugin::StorageEngine::commitOrRollbackByXID(XID *xid, bool commit)
323
 
{
324
 
  vector<int> results;
325
 
  
326
 
  if (commit)
327
 
    transform(vector_of_engines.begin(), vector_of_engines.end(), results.begin(),
328
 
              bind2nd(mem_fun(&plugin::StorageEngine::commit_by_xid),xid));
329
 
  else
330
 
    transform(vector_of_engines.begin(), vector_of_engines.end(), results.begin(),
331
 
              bind2nd(mem_fun(&plugin::StorageEngine::rollback_by_xid),xid));
332
 
 
333
 
  if (find_if(results.begin(), results.end(), bind2nd(equal_to<int>(),0))
334
 
         == results.end())
335
 
    return 1;
336
 
  return 0;
337
 
}
338
 
 
339
 
/**
340
 
  @details
341
 
  This function should be called when MySQL sends rows of a SELECT result set
342
 
  or the EOF mark to the client. It releases a possible adaptive hash index
343
 
  S-latch held by session in InnoDB and also releases a possible InnoDB query
344
 
  FIFO ticket to enter InnoDB. To save CPU time, InnoDB allows a session to
345
 
  keep them over several calls of the InnoDB Cursor interface when a join
346
 
  is executed. But when we let the control to pass to the client they have
347
 
  to be released because if the application program uses mysql_use_result(),
348
 
  it may deadlock on the S-latch if the application on another connection
349
 
  performs another SQL query. In MySQL-4.1 this is even more important because
350
 
  there a connection can have several SELECT queries open at the same time.
351
 
 
352
 
  @param session           the thread handle of the current connection
353
 
 
354
 
  @return
355
 
    always 0
356
 
*/
357
 
int plugin::StorageEngine::releaseTemporaryLatches(Session *session)
358
 
{
359
 
  for_each(vector_of_transactional_engines.begin(), vector_of_transactional_engines.end(),
360
 
           bind2nd(mem_fun(&plugin::StorageEngine::release_temporary_latches),session));
361
 
  return 0;
362
 
}
363
 
 
364
 
bool plugin::StorageEngine::flushLogs(plugin::StorageEngine *engine)
 
289
bool StorageEngine::flushLogs(StorageEngine *engine)
365
290
{
366
291
  if (engine == NULL)
367
292
  {
368
293
    if (find_if(vector_of_engines.begin(), vector_of_engines.end(),
369
 
            mem_fun(&plugin::StorageEngine::flush_logs))
370
 
          != vector_of_engines.begin())
 
294
                mem_fun(&StorageEngine::flush_logs))
 
295
        != vector_of_engines.begin())
371
296
      return true;
372
297
  }
373
298
  else
374
299
  {
375
 
    if ((!engine->is_enabled()) ||
376
 
        (engine->flush_logs()))
 
300
    if (engine->flush_logs())
377
301
      return true;
378
302
  }
379
303
  return false;
380
304
}
381
305
 
382
 
/**
383
 
  recover() step of xa.
384
 
 
385
 
  @note
386
 
    there are three modes of operation:
387
 
    - automatic recover after a crash
388
 
    in this case commit_list != 0, tc_heuristic_recover==0
389
 
    all xids from commit_list are committed, others are rolled back
390
 
    - manual (heuristic) recover
391
 
    in this case commit_list==0, tc_heuristic_recover != 0
392
 
    DBA has explicitly specified that all prepared transactions should
393
 
    be committed (or rolled back).
394
 
    - no recovery (MySQL did not detect a crash)
395
 
    in this case commit_list==0, tc_heuristic_recover == 0
396
 
    there should be no prepared transactions in this case.
397
 
*/
398
 
class XARecover : unary_function<plugin::StorageEngine *, void>
399
 
{
400
 
  int trans_len, found_foreign_xids, found_my_xids;
401
 
  bool result;
402
 
  XID *trans_list;
403
 
  HASH *commit_list;
404
 
  bool dry_run;
405
 
public:
406
 
  XARecover(XID *trans_list_arg, int trans_len_arg,
407
 
            HASH *commit_list_arg, bool dry_run_arg) 
408
 
    : trans_len(trans_len_arg), found_foreign_xids(0), found_my_xids(0),
409
 
      result(false),
410
 
      trans_list(trans_list_arg), commit_list(commit_list_arg),
411
 
      dry_run(dry_run_arg)
412
 
  {}
413
 
  
414
 
  int getForeignXIDs()
415
 
  {
416
 
    return found_foreign_xids; 
417
 
  }
418
 
 
419
 
  int getMyXIDs()
420
 
  {
421
 
    return found_my_xids; 
422
 
  }
423
 
 
424
 
  result_type operator() (argument_type engine)
425
 
  {
426
 
  
427
 
    int got;
428
 
  
429
 
    if (engine->is_enabled())
430
 
    {
431
 
      while ((got= engine->recover(trans_list, trans_len)) > 0 )
432
 
      {
433
 
        errmsg_printf(ERRMSG_LVL_INFO,
434
 
                      _("Found %d prepared transaction(s) in %s"),
435
 
                      got, engine->getName().c_str());
436
 
        for (int i=0; i < got; i ++)
437
 
        {
438
 
          my_xid x=trans_list[i].get_my_xid();
439
 
          if (!x) // not "mine" - that is generated by external TM
440
 
          {
441
 
            xid_cache_insert(trans_list+i, XA_PREPARED);
442
 
            found_foreign_xids++;
443
 
            continue;
444
 
          }
445
 
          if (dry_run)
446
 
          {
447
 
            found_my_xids++;
448
 
            continue;
449
 
          }
450
 
          // recovery mode
451
 
          if (commit_list ?
452
 
              hash_search(commit_list, (unsigned char *)&x, sizeof(x)) != 0 :
453
 
              tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
454
 
          {
455
 
            engine->commit_by_xid(trans_list+i);
456
 
          }
457
 
          else
458
 
          {
459
 
            engine->rollback_by_xid(trans_list+i);
460
 
          }
461
 
        }
462
 
        if (got < trans_len)
463
 
          break;
464
 
      }
465
 
    }
466
 
  }
467
 
};
468
 
 
469
 
int plugin::StorageEngine::recover(HASH *commit_list)
470
 
{
471
 
  XID *trans_list= NULL;
472
 
  int trans_len= 0;
473
 
 
474
 
  bool dry_run= (commit_list==0 && tc_heuristic_recover==0);
475
 
 
476
 
  /* commit_list and tc_heuristic_recover cannot be set both */
477
 
  assert(commit_list==0 || tc_heuristic_recover==0);
478
 
 
479
 
  /* if either is set, total_ha_2pc must be set too */
480
 
  if (total_ha_2pc <= 1)
481
 
    return 0;
482
 
 
483
 
 
484
 
#ifndef WILL_BE_DELETED_LATER
485
 
 
486
 
  /*
487
 
    for now, only InnoDB supports 2pc. It means we can always safely
488
 
    rollback all pending transactions, without risking inconsistent data
489
 
  */
490
 
 
491
 
  assert(total_ha_2pc == 2); // only InnoDB and binlog
492
 
  tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
493
 
  dry_run=false;
494
 
#endif
495
 
  for (trans_len= MAX_XID_LIST_SIZE ;
496
 
       trans_list==0 && trans_len > MIN_XID_LIST_SIZE; trans_len/=2)
497
 
  {
498
 
    trans_list=(XID *)malloc(trans_len*sizeof(XID));
499
 
  }
500
 
  if (!trans_list)
501
 
  {
502
 
    errmsg_printf(ERRMSG_LVL_ERROR, ER(ER_OUTOFMEMORY), trans_len*sizeof(XID));
503
 
    return(1);
504
 
  }
505
 
 
506
 
  if (commit_list)
507
 
    errmsg_printf(ERRMSG_LVL_INFO, _("Starting crash recovery..."));
508
 
 
509
 
 
510
 
  XARecover recover_func(trans_list, trans_len, commit_list, dry_run);
511
 
  for_each(vector_of_transactional_engines.begin(), vector_of_transactional_engines.end(),
512
 
           recover_func);
513
 
  free(trans_list);
514
 
 
515
 
  if (recover_func.getForeignXIDs())
516
 
    errmsg_printf(ERRMSG_LVL_WARN,
517
 
                  _("Found %d prepared XA transactions"),
518
 
                  recover_func.getForeignXIDs());
519
 
  if (dry_run && recover_func.getMyXIDs())
520
 
  {
521
 
    errmsg_printf(ERRMSG_LVL_ERROR,
522
 
                  _("Found %d prepared transactions! It means that drizzled "
523
 
                    "was not shut down properly last time and critical "
524
 
                    "recovery information (last binlog or %s file) was "
525
 
                    "manually deleted after a crash. You have to start "
526
 
                    "drizzled with the --tc-heuristic-recover switch to "
527
 
                    "commit or rollback pending transactions."),
528
 
                    recover_func.getMyXIDs(), opt_tc_log_file);
529
 
    return(1);
530
 
  }
531
 
  if (commit_list)
532
 
    errmsg_printf(ERRMSG_LVL_INFO, _("Crash recovery finished."));
533
 
  return(0);
534
 
}
535
 
 
536
 
int plugin::StorageEngine::startConsistentSnapshot(Session *session)
537
 
{
538
 
  for_each(vector_of_engines.begin(), vector_of_engines.end(),
539
 
           bind2nd(mem_fun(&plugin::StorageEngine::start_consistent_snapshot),
540
 
                   session));
541
 
  return 0;
542
 
}
543
 
 
544
 
class StorageEngineGetTableDefinition: public unary_function<plugin::StorageEngine *,bool>
 
306
class StorageEngineGetTableDefinition: public unary_function<StorageEngine *,bool>
545
307
{
546
308
  Session& session;
547
309
  const char* path;
548
310
  const char *db;
549
311
  const char *table_name;
550
312
  const bool is_tmp;
551
 
  message::Table *table_proto;
 
313
  message::Table *table_message;
552
314
  int *err;
553
315
 
554
316
public:
557
319
                                  const char *db_arg,
558
320
                                  const char *table_name_arg,
559
321
                                  const bool is_tmp_arg,
560
 
                                  message::Table *table_proto_arg,
 
322
                                  message::Table *table_message_arg,
561
323
                                  int *err_arg) :
562
324
    session(session_arg), 
563
325
    path(path_arg), 
564
326
    db(db_arg),
565
327
    table_name(table_name_arg),
566
328
    is_tmp(is_tmp_arg),
567
 
    table_proto(table_proto_arg), 
 
329
    table_message(table_message_arg), 
568
330
    err(err_arg) {}
569
331
 
570
332
  result_type operator() (argument_type engine)
574
336
                                          db,
575
337
                                          table_name,
576
338
                                          is_tmp,
577
 
                                          table_proto);
 
339
                                          table_message);
578
340
 
579
341
    if (ret != ENOENT)
580
342
      *err= ret;
583
345
  }
584
346
};
585
347
 
586
 
static int drizzle_read_table_proto(const char* path, message::Table* table)
 
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)
587
354
{
588
 
  int fd= open(path, O_RDONLY);
589
 
 
590
 
  if (fd == -1)
591
 
    return errno;
592
 
 
593
 
  google::protobuf::io::ZeroCopyInputStream* input=
594
 
    new google::protobuf::io::FileInputStream(fd);
595
 
 
596
 
  if (table->ParseFromZeroCopyStream(input) == false)
597
 
  {
598
 
    delete input;
599
 
    close(fd);
600
 
    return -1;
601
 
  }
602
 
 
603
 
  delete input;
604
 
  close(fd);
605
 
  return 0;
 
355
  return (plugin::StorageEngine::getTableDefinition(session, identifier, NULL, include_temporary_tables) == EEXIST);
606
356
}
607
357
 
608
358
/**
610
360
  to ask engine if there are any new tables that should be written to disk
611
361
  or any dropped tables that need to be removed from disk
612
362
*/
613
 
int plugin::StorageEngine::getTableDefinition(Session& session,
614
 
                                              TableIdentifier &identifier,
615
 
                                              message::Table *table_proto)
 
363
int StorageEngine::getTableDefinition(Session& session,
 
364
                                      TableIdentifier &identifier,
 
365
                                      message::Table *table_message,
 
366
                                      bool include_temporary_tables)
616
367
{
617
368
  return getTableDefinition(session,
618
369
                            identifier.getPath(), identifier.getDBName(), identifier.getTableName(), identifier.isTmp(),
619
 
                            table_proto);
 
370
                            table_message, include_temporary_tables);
620
371
}
621
372
 
622
 
int plugin::StorageEngine::getTableDefinition(Session& session,
 
373
int StorageEngine::getTableDefinition(Session& session,
623
374
                                              const char* path,
624
 
                                              const char *,
625
 
                                              const char *,
 
375
                                              const char *schema_name,
 
376
                                              const char *table_name,
626
377
                                              const bool,
627
 
                                              message::Table *table_proto)
 
378
                                              message::Table *table_message,
 
379
                                              bool include_temporary_tables)
628
380
{
629
381
  int err= ENOENT;
630
382
 
631
 
  vector<plugin::StorageEngine *>::iterator iter=
 
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=
632
390
    find_if(vector_of_engines.begin(), vector_of_engines.end(),
633
 
            StorageEngineGetTableDefinition(session, path, NULL, NULL, true, table_proto, &err));
 
391
            StorageEngineGetTableDefinition(session, path, NULL, NULL, true, table_message, &err));
634
392
 
635
393
  if (iter == vector_of_engines.end())
636
394
  {
637
 
    string proto_path(path);
638
 
    string file_ext(".dfe");
639
 
    proto_path.append(file_ext);
640
 
 
641
 
    int error= access(proto_path.c_str(), F_OK);
642
 
 
643
 
    if (error == 0)
644
 
      err= EEXIST;
645
 
    else
646
 
      err= errno;
647
 
 
648
 
    if (table_proto)
649
 
    {
650
 
      int read_proto_err= drizzle_read_table_proto(proto_path.c_str(),
651
 
                                                   table_proto);
652
 
 
653
 
      if (read_proto_err)
654
 
        err= read_proto_err;
655
 
    }
 
395
    return ENOENT;
656
396
  }
657
397
 
658
398
  return err;
688
428
  return true;
689
429
}
690
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
 
691
458
 
692
459
/**
693
460
   returns ENOENT if the file doesn't exists.
694
461
*/
695
 
int plugin::StorageEngine::dropTable(Session& session,
696
 
                                     TableIdentifier &identifier,
697
 
                                     bool generate_warning)
 
462
int StorageEngine::dropTable(Session& session,
 
463
                             TableIdentifier &identifier)
698
464
{
699
465
  int error= 0;
700
466
  int error_proto;
701
467
  message::Table src_proto;
702
 
  plugin::StorageEngine* engine;
 
468
  StorageEngine* engine;
703
469
 
704
 
  error_proto= plugin::StorageEngine::getTableDefinition(session,
705
 
                                                         identifier,
706
 
                                                         &src_proto);
 
470
  error_proto= StorageEngine::getTableDefinition(session,
 
471
                                                 identifier,
 
472
                                                 &src_proto);
707
473
 
708
474
  if (error_proto == ER_CORRUPT_TABLE_DEFINITION)
709
475
  {
712
478
    return ER_CORRUPT_TABLE_DEFINITION;
713
479
  }
714
480
 
715
 
  engine= plugin::StorageEngine::findByName(session,
716
 
                                            src_proto.engine().name());
 
481
  engine= StorageEngine::findByName(session, src_proto.engine().name());
717
482
 
718
483
  if (engine)
719
484
  {
 
485
    std::string path(identifier.getPath());
720
486
    engine->setTransactionReadWrite(session);
721
 
    error= engine->doDropTable(session, identifier.getPath());
722
 
  }
 
487
    error= engine->doDropTable(session, path);
723
488
 
724
 
  if (error != ENOENT)
725
 
  {
726
 
    if (error == 0)
 
489
    if (not error)
727
490
    {
728
 
      if (engine && engine->check_flag(HTON_BIT_HAS_DATA_DICTIONARY))
729
 
      {
730
 
        deleteDefinitionFromPath(identifier);
731
 
      }
732
 
      else
733
 
      {
734
 
        error= deleteDefinitionFromPath(identifier);
 
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));
735
497
      }
736
498
    }
737
499
  }
739
501
  if (error_proto && error == 0)
740
502
    return 0;
741
503
 
742
 
  if (((error_proto != EEXIST && error_proto != ENOENT)
743
 
      && !engine && generate_warning)
744
 
      | ( error && !engine && generate_warning))
745
 
  {
746
 
    my_error(ER_GET_ERRNO, MYF(0), error_proto);
747
 
    return error_proto;
748
 
  }
749
 
 
750
 
  if (error && generate_warning)
751
 
  {
752
 
    /*
753
 
      Because engine->print_error() use my_error() to generate the error message
754
 
      we use an internal error Cursor to intercept it and store the text
755
 
      in a temporary buffer. Later the message will be presented to user
756
 
      as a warning.
757
 
    */
758
 
    Ha_delete_table_error_handler ha_delete_table_error_handler;
759
 
 
760
 
    session.push_internal_handler(&ha_delete_table_error_handler);
761
 
    engine->print_error(error, 0);
762
 
 
763
 
    session.pop_internal_handler();
764
 
 
765
 
    /*
766
 
      XXX: should we convert *all* errors to warnings here?
767
 
      What if the error is fatal?
768
 
    */
769
 
    push_warning(&session, DRIZZLE_ERROR::WARN_LEVEL_ERROR, error,
770
 
                 ha_delete_table_error_handler.buff);
771
 
  }
772
 
 
773
504
  return error;
774
505
}
775
506
 
783
514
 
784
515
   @todo refactor to remove goto
785
516
*/
786
 
int plugin::StorageEngine::createTable(Session& session,
787
 
                                       TableIdentifier &identifier,
788
 
                                       bool update_create_info,
789
 
                                       message::Table& table_proto, bool proto_used)
 
517
int StorageEngine::createTable(Session& session,
 
518
                               TableIdentifier &identifier,
 
519
                               bool update_create_info,
 
520
                               message::Table& table_message)
790
521
{
791
522
  int error= 1;
792
523
  Table table;
793
524
  TableShare share(identifier.getDBName(), 0, identifier.getTableName(), identifier.getPath());
794
525
  message::Table tmp_proto;
795
526
 
796
 
  if (proto_used)
797
 
  {
798
 
    if (parse_table_proto(session, table_proto, &share))
799
 
      goto err;
800
 
  }
801
 
  else
802
 
  {
803
 
    if (open_table_def(session, &share))
804
 
      goto err;
805
 
  }
 
527
  if (parse_table_proto(session, table_message, &share))
 
528
    goto err;
806
529
 
807
530
  if (open_table_from_share(&session, &share, "", 0, 0,
808
531
                            &table))
809
532
    goto err;
810
533
 
811
534
  if (update_create_info)
812
 
    table.updateCreateInfo(&table_proto);
 
535
    table.updateCreateInfo(&table_message);
813
536
 
814
537
  /* Check for legal operations against the Engine using the proto (if used) */
815
 
  if (proto_used)
816
 
  {
817
 
    if (table_proto.type() == message::Table::TEMPORARY &&
818
 
        share.storage_engine->check_flag(HTON_BIT_TEMPORARY_NOT_SUPPORTED) == true)
819
 
    {
820
 
      error= HA_ERR_UNSUPPORTED;
821
 
      goto err2;
822
 
    }
823
 
    else if (table_proto.type() != message::Table::TEMPORARY &&
824
 
             share.storage_engine->check_flag(HTON_BIT_TEMPORARY_ONLY) == true)
825
 
    {
826
 
      error= HA_ERR_UNSUPPORTED;
827
 
      goto err2;
828
 
    }
829
 
  }
830
 
 
831
 
  if (! share.storage_engine->is_enabled())
832
 
  {
833
 
    error= HA_ERR_UNSUPPORTED;
834
 
    goto err2;
835
 
  }
836
 
 
 
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
  }
837
550
 
838
551
  {
839
552
    char name_buff[FN_REFLEN];
841
554
 
842
555
    table_name_arg= share.storage_engine->checkLowercaseNames(identifier.getPath(), name_buff);
843
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
 
844
568
    share.storage_engine->setTransactionReadWrite(session);
845
569
 
846
570
    error= share.storage_engine->doCreateTable(&session,
847
571
                                               table_name_arg,
848
572
                                               table,
849
 
                                               table_proto);
 
573
                                               table_message);
850
574
  }
851
575
 
852
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
 
853
585
  table.closefrm(false);
854
586
 
855
 
  if (error)
856
 
  {
857
 
    char name_buff[FN_REFLEN];
858
 
    sprintf(name_buff,"%s.%s", identifier.getDBName(), identifier.getTableName());
859
 
    my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name_buff, error);
860
 
  }
861
587
err:
862
588
  share.free_table_share();
863
589
  return(error != 0);
864
590
}
865
591
 
866
 
Cursor *plugin::StorageEngine::getCursor(TableShare &share, memory::Root *alloc)
 
592
Cursor *StorageEngine::getCursor(TableShare &share, memory::Root *alloc)
867
593
{
868
 
  assert(enabled);
869
594
  return create(share, alloc);
870
595
}
871
596
 
872
597
/**
873
598
  TODO -> Remove this to force all engines to implement their own file. Solves the "we only looked at dfe" problem.
874
599
*/
875
 
void plugin::StorageEngine::doGetTableNames(CachedDirectory &directory, string&, set<string>& set_of_names)
876
 
{
877
 
  CachedDirectory::Entries entries= directory.getEntries();
878
 
 
879
 
  for (CachedDirectory::Entries::iterator entry_iter= entries.begin(); 
880
 
       entry_iter != entries.end(); ++entry_iter)
881
 
  {
882
 
    CachedDirectory::Entry *entry= *entry_iter;
883
 
    const string *filename= &entry->filename;
884
 
 
885
 
    assert(filename->size());
886
 
 
887
 
    const char *ext= strchr(filename->c_str(), '.');
888
 
 
889
 
    if (ext == NULL || my_strcasecmp(system_charset_info, ext, DEFAULT_DEFINITION_FILE_EXT.c_str()) ||
890
 
        (filename->compare(0, strlen(TMP_FILE_PREFIX), TMP_FILE_PREFIX) == 0))
891
 
    { }
892
 
    else
893
 
    {
894
 
      char uname[NAME_LEN + 1];
895
 
      uint32_t file_name_len;
896
 
 
897
 
      file_name_len= filename_to_tablename(filename->c_str(), uname, sizeof(uname));
898
 
      // TODO: Remove need for memory copy here
899
 
      uname[file_name_len - sizeof(".dfe") + 1]= '\0'; // Subtract ending, place NULL 
900
 
      set_of_names.insert(uname);
901
 
    }
902
 
  }
903
 
}
 
600
void StorageEngine::doGetTableNames(CachedDirectory&, string&, set<string>&)
 
601
{ }
904
602
 
905
603
class AddTableName : 
906
 
  public unary_function<plugin::StorageEngine *, void>
 
604
  public unary_function<StorageEngine *, void>
907
605
{
908
606
  string db;
909
607
  CachedDirectory& directory;
910
 
  set<string>& set_of_names;
 
608
  TableNameList &set_of_names;
911
609
 
912
610
public:
913
611
 
914
 
  AddTableName(CachedDirectory& directory_arg, string& database_name, set<string>& of_names) :
 
612
  AddTableName(CachedDirectory& directory_arg, const string& database_name, set<string>& of_names) :
915
613
    directory(directory_arg),
916
614
    set_of_names(of_names)
917
615
  {
924
622
  }
925
623
};
926
624
 
927
 
void plugin::StorageEngine::getTableNames(string& db, set<string>& set_of_names)
 
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)
928
823
{
929
824
  char tmp_path[FN_REFLEN];
930
825
 
931
 
  build_table_filename(tmp_path, sizeof(tmp_path), db.c_str(), "", false);
 
826
  build_table_filename(tmp_path, sizeof(tmp_path), schema_name.c_str(), "", false);
932
827
 
933
828
  CachedDirectory directory(tmp_path, set_of_table_definition_ext);
934
829
 
935
 
  if (db.compare("information_schema"))
 
830
  if (not schema_name.compare("information_schema"))
 
831
  { }
 
832
  else if (not schema_name.compare("data_dictionary"))
 
833
  { }
 
834
  else
936
835
  {
937
836
    if (directory.fail())
938
837
    {
939
838
      errno= directory.getError();
940
839
      if (errno == ENOENT)
941
 
        my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db.c_str());
 
840
        my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), schema_name.c_str());
942
841
      else
943
842
        my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), directory.getPath(), errno);
944
843
      return;
946
845
  }
947
846
 
948
847
  for_each(vector_of_engines.begin(), vector_of_engines.end(),
949
 
           AddTableName(directory, db, set_of_names));
 
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
 
950
854
}
951
855
 
952
856
/* This will later be converted to TableIdentifiers */
953
 
class DropTables: public unary_function<plugin::StorageEngine *, void>
 
857
class DropTables: public unary_function<StorageEngine *, void>
954
858
{
955
859
  Session &session;
956
 
  set<string>& set_of_names;
 
860
  TableNameList &set_of_names;
957
861
 
958
862
public:
959
863
 
965
869
  result_type operator() (argument_type engine)
966
870
  {
967
871
 
968
 
    for (set<string>::iterator iter= set_of_names.begin();
 
872
    for (TableNameList::iterator iter= set_of_names.begin();
969
873
         iter != set_of_names.end();
970
874
         iter++)
971
875
    {
973
877
 
974
878
      // On a return of zero we know we found and deleted the table. So we
975
879
      // remove it from our search.
976
 
      if (! error)
 
880
      if (not error)
977
881
        set_of_names.erase(iter);
978
882
    }
979
883
  }
984
888
 
985
889
  Note-> Unlike MySQL, we do not, on purpose, delete files that do not match any engines. 
986
890
*/
987
 
void plugin::StorageEngine::removeLostTemporaryTables(Session &session, const char *directory)
 
891
void StorageEngine::removeLostTemporaryTables(Session &session, const char *directory)
988
892
{
989
893
  CachedDirectory dir(directory, set_of_table_definition_ext);
990
894
  set<string> set_of_table_names;
1027
931
  */
1028
932
  set<string> all_exts= set_of_table_definition_ext;
1029
933
 
1030
 
  for (vector<plugin::StorageEngine *>::iterator iter= vector_of_engines.begin();
 
934
  for (EngineVector::iterator iter= vector_of_engines.begin();
1031
935
       iter != vector_of_engines.end() ; iter++)
1032
936
  {
1033
937
    for (const char **ext= (*iter)->bas_ext(); *ext ; ext++)
1061
965
    - table->s->path
1062
966
    - table->alias
1063
967
*/
1064
 
void plugin::StorageEngine::print_error(int error, myf errflag, Table &table)
 
968
void StorageEngine::print_error(int error, myf errflag, Table &table)
1065
969
{
1066
970
  print_error(error, errflag, &table);
1067
971
}
1068
972
 
1069
 
void plugin::StorageEngine::print_error(int error, myf errflag, Table *table)
 
973
void StorageEngine::print_error(int error, myf errflag, Table *table)
1070
974
{
1071
975
  int textno= ER_GET_ERRNO;
1072
976
  switch (error) {
1278
1182
  @return
1279
1183
    Returns true if this is a temporary error
1280
1184
*/
1281
 
bool plugin::StorageEngine::get_error_message(int , String* )
 
1185
bool StorageEngine::get_error_message(int , String* )
1282
1186
{
1283
1187
  return false;
1284
1188
}
1285
1189
 
1286
1190
 
1287
 
void plugin::StorageEngine::print_keydup_error(uint32_t key_nr, const char *msg, Table &table)
 
1191
void StorageEngine::print_keydup_error(uint32_t key_nr, const char *msg, Table &table)
1288
1192
{
1289
1193
  /* Write the duplicated key in the error message */
1290
1194
  char key[MAX_KEY_LENGTH];
1312
1216
}
1313
1217
 
1314
1218
 
1315
 
int plugin::StorageEngine::deleteDefinitionFromPath(TableIdentifier &identifier)
 
1219
int StorageEngine::deleteDefinitionFromPath(TableIdentifier &identifier)
1316
1220
{
1317
1221
  string path(identifier.getPath());
1318
1222
 
1321
1225
  return internal::my_delete(path.c_str(), MYF(0));
1322
1226
}
1323
1227
 
1324
 
int plugin::StorageEngine::renameDefinitionFromPath(TableIdentifier &dest, TableIdentifier &src)
 
1228
int StorageEngine::renameDefinitionFromPath(TableIdentifier &dest, TableIdentifier &src)
1325
1229
{
1326
1230
  string src_path(src.getPath());
1327
1231
  string dest_path(dest.getPath());
1332
1236
  return internal::my_rename(src_path.c_str(), dest_path.c_str(), MYF(MY_WME));
1333
1237
}
1334
1238
 
1335
 
int plugin::StorageEngine::writeDefinitionFromPath(TableIdentifier &identifier, message::Table &table_proto)
 
1239
int StorageEngine::writeDefinitionFromPath(TableIdentifier &identifier, message::Table &table_message)
1336
1240
{
1337
1241
  string file_name(identifier.getPath());
1338
1242
 
1346
1250
  google::protobuf::io::ZeroCopyOutputStream* output=
1347
1251
    new google::protobuf::io::FileOutputStream(fd);
1348
1252
 
1349
 
  if (table_proto.SerializeToZeroCopyStream(output) == false)
 
1253
  if (table_message.SerializeToZeroCopyStream(output) == false)
1350
1254
  {
1351
1255
    delete output;
1352
1256
    close(fd);
1358
1262
  return 0;
1359
1263
}
1360
1264
 
1361
 
 
 
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 */
1362
1299
} /* namespace drizzled */