~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/plugin/storage_engine.cc

Merged in latest plugin-slot-reorg.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 */
19
19
 
20
20
#include <drizzled/server_includes.h>
 
21
 
 
22
#include CSTDINT_H
 
23
#include <string>
 
24
#include <vector>
 
25
#include <algorithm>
 
26
#include <functional>
 
27
 
 
28
#include <google/protobuf/io/zero_copy_stream.h>
 
29
#include <google/protobuf/io/zero_copy_stream_impl.h>
 
30
 
 
31
#include "mysys/my_dir.h"
 
32
#include "mysys/hash.h"
 
33
 
21
34
#include <drizzled/definitions.h>
22
35
#include <drizzled/base.h>
23
36
#include <drizzled/handler.h>
28
41
#include <drizzled/registry.h>
29
42
#include <drizzled/unireg.h>
30
43
#include <drizzled/data_home.h>
 
44
#include "drizzled/errmsg_print.h"
31
45
#include <drizzled/plugin/registry.h>
32
 
#include <string>
 
46
#include "drizzled/xid.h"
33
47
 
34
48
#include <drizzled/table_proto.h>
35
49
 
36
 
#include <mysys/my_dir.h>
37
 
 
38
 
#include CSTDINT_H
39
 
 
40
50
using namespace std;
41
 
using namespace drizzled;
 
51
 
 
52
namespace drizzled
 
53
{
 
54
 
 
55
Registry<plugin::StorageEngine *> all_engines;
42
56
 
43
57
plugin::StorageEngine::StorageEngine(const string name_arg,
44
58
                                     const bitset<HTON_BIT_SIZE> &flags_arg,
87
101
}
88
102
 
89
103
 
90
 
/**
91
 
  Return the default storage engine plugin::StorageEngine for thread
92
 
 
93
 
  @param ha_default_storage_engine(session)
94
 
  @param session         current thread
95
 
 
96
 
  @return
97
 
    pointer to plugin::StorageEngine
98
 
*/
99
 
plugin::StorageEngine *ha_default_storage_engine(Session *session)
100
 
{
101
 
  if (session->variables.storage_engine)
102
 
    return session->variables.storage_engine;
103
 
  return global_system_variables.storage_engine;
104
 
}
105
 
 
106
 
 
107
 
handler *get_new_handler(TableShare *share, MEM_ROOT *alloc,
108
 
                         plugin::StorageEngine *engine)
109
 
{
110
 
  handler *file;
111
 
 
112
 
  if (engine && engine->is_enabled())
113
 
  {
114
 
    if ((file= engine->create(share, alloc)))
115
 
      file->init();
116
 
    return(file);
117
 
  }
118
 
  /*
119
 
    Try the default table type
120
 
    Here the call to current_session() is ok as we call this function a lot of
121
 
    times but we enter this branch very seldom.
122
 
  */
123
 
  return(get_new_handler(share, alloc, ha_default_storage_engine(current_session)));
124
 
}
125
 
 
126
 
 
127
 
 
128
 
 
129
 
int plugin::StorageEngine::renameTableImplementation(Session *, const char *from, const char *to)
 
104
 
 
105
int plugin::StorageEngine::renameTableImplementation(Session *,
 
106
                                                     const char *from,
 
107
                                                     const char *to)
130
108
{
131
109
  int error= 0;
132
110
  for (const char **ext= bas_ext(); *ext ; ext++)
157
135
  @retval
158
136
    !0  Error
159
137
*/
160
 
int plugin::StorageEngine::deleteTableImplementation(Session *, const std::string table_path)
 
138
int plugin::StorageEngine::deleteTableImplementation(Session *,
 
139
                                                     const string table_path)
161
140
{
162
141
  int error= 0;
163
142
  int enoent_or_zero= ENOENT;                   // Error if no file was deleted
179
158
  return error;
180
159
}
181
160
 
 
161
const char *plugin::StorageEngine::checkLowercaseNames(const char *path,
 
162
                                                       char *tmp_path)
 
163
{
 
164
  if (flags.test(HTON_BIT_FILE_BASED))
 
165
    return path;
 
166
 
 
167
  /* Ensure that table handler get path in lower case */
 
168
  if (tmp_path != path)
 
169
    strcpy(tmp_path, path);
 
170
 
 
171
  /*
 
172
    we only should turn into lowercase database/table part
 
173
    so start the process after homedirectory
 
174
  */
 
175
  if (strstr(tmp_path, drizzle_tmpdir) == tmp_path)
 
176
    my_casedn_str(files_charset_info, tmp_path + strlen(drizzle_tmpdir));
 
177
  else
 
178
    my_casedn_str(files_charset_info, tmp_path + drizzle_data_home_len);
 
179
 
 
180
  return tmp_path;
 
181
}
 
182
 
 
183
 
 
184
void plugin::StorageEngine::add(plugin::StorageEngine *engine)
 
185
{
 
186
  all_engines.add(engine);
 
187
}
 
188
 
 
189
void plugin::StorageEngine::remove(plugin::StorageEngine *engine)
 
190
{
 
191
  all_engines.remove(engine);
 
192
}
 
193
 
 
194
plugin::StorageEngine *plugin::StorageEngine::findByName(Session *session,
 
195
                                                         string find_str)
 
196
{
 
197
  
 
198
  transform(find_str.begin(), find_str.end(),
 
199
            find_str.begin(), ::tolower);
 
200
  string default_str("default");
 
201
  if (find_str == default_str)
 
202
    return ha_default_storage_engine(session);
 
203
 
 
204
  plugin::StorageEngine *engine= all_engines.find(find_str);
 
205
 
 
206
  if (engine && engine->is_user_selectable())
 
207
    return engine;
 
208
 
 
209
  return NULL;
 
210
}
 
211
 
 
212
class StorageEngineCloseConnection
 
213
  : public unary_function<plugin::StorageEngine *, void>
 
214
{
 
215
  Session *session;
 
216
public:
 
217
  StorageEngineCloseConnection(Session *session_arg) : session(session_arg) {}
 
218
  /*
 
219
    there's no need to rollback here as all transactions must
 
220
    be rolled back already
 
221
  */
 
222
  inline result_type operator() (argument_type engine)
 
223
  {
 
224
    if (engine->is_enabled() && 
 
225
      session_get_ha_data(session, engine))
 
226
    engine->close_connection(session);
 
227
  }
 
228
};
 
229
 
 
230
/**
 
231
  @note
 
232
    don't bother to rollback here, it's done already
 
233
*/
 
234
void plugin::StorageEngine::closeConnection(Session* session)
 
235
{
 
236
  for_each(all_engines.begin(), all_engines.end(),
 
237
           StorageEngineCloseConnection(session));
 
238
}
 
239
 
 
240
void plugin::StorageEngine::dropDatabase(char* path)
 
241
{
 
242
  for_each(all_engines.begin(), all_engines.end(),
 
243
           bind2nd(mem_fun(&plugin::StorageEngine::drop_database),path));
 
244
}
 
245
 
 
246
int plugin::StorageEngine::commitOrRollbackByXID(XID *xid, bool commit)
 
247
{
 
248
  vector<int> results;
 
249
  
 
250
  if (commit)
 
251
    transform(all_engines.begin(), all_engines.end(), results.begin(),
 
252
              bind2nd(mem_fun(&plugin::StorageEngine::commit_by_xid),xid));
 
253
  else
 
254
    transform(all_engines.begin(), all_engines.end(), results.begin(),
 
255
              bind2nd(mem_fun(&plugin::StorageEngine::rollback_by_xid),xid));
 
256
 
 
257
  if (find_if(results.begin(), results.end(), bind2nd(equal_to<int>(),0))
 
258
         == results.end())
 
259
    return 1;
 
260
  return 0;
 
261
}
 
262
 
 
263
/**
 
264
  @details
 
265
  This function should be called when MySQL sends rows of a SELECT result set
 
266
  or the EOF mark to the client. It releases a possible adaptive hash index
 
267
  S-latch held by session in InnoDB and also releases a possible InnoDB query
 
268
  FIFO ticket to enter InnoDB. To save CPU time, InnoDB allows a session to
 
269
  keep them over several calls of the InnoDB handler interface when a join
 
270
  is executed. But when we let the control to pass to the client they have
 
271
  to be released because if the application program uses mysql_use_result(),
 
272
  it may deadlock on the S-latch if the application on another connection
 
273
  performs another SQL query. In MySQL-4.1 this is even more important because
 
274
  there a connection can have several SELECT queries open at the same time.
 
275
 
 
276
  @param session           the thread handle of the current connection
 
277
 
 
278
  @return
 
279
    always 0
 
280
*/
 
281
int plugin::StorageEngine::releaseTemporaryLatches(Session *session)
 
282
{
 
283
  for_each(all_engines.begin(), all_engines.end(),
 
284
           bind2nd(mem_fun(&plugin::StorageEngine::release_temporary_latches),session));
 
285
  return 0;
 
286
}
 
287
 
 
288
bool plugin::StorageEngine::flushLogs(plugin::StorageEngine *engine)
 
289
{
 
290
  if (engine == NULL)
 
291
  {
 
292
    if (find_if(all_engines.begin(), all_engines.end(),
 
293
            mem_fun(&plugin::StorageEngine::flush_logs))
 
294
          != all_engines.begin())
 
295
      return true;
 
296
  }
 
297
  else
 
298
  {
 
299
    if ((!engine->is_enabled()) ||
 
300
        (engine->flush_logs()))
 
301
      return true;
 
302
  }
 
303
  return false;
 
304
}
 
305
 
 
306
/**
 
307
  recover() step of xa.
 
308
 
 
309
  @note
 
310
    there are three modes of operation:
 
311
    - automatic recover after a crash
 
312
    in this case commit_list != 0, tc_heuristic_recover==0
 
313
    all xids from commit_list are committed, others are rolled back
 
314
    - manual (heuristic) recover
 
315
    in this case commit_list==0, tc_heuristic_recover != 0
 
316
    DBA has explicitly specified that all prepared transactions should
 
317
    be committed (or rolled back).
 
318
    - no recovery (MySQL did not detect a crash)
 
319
    in this case commit_list==0, tc_heuristic_recover == 0
 
320
    there should be no prepared transactions in this case.
 
321
*/
 
322
class XARecover : unary_function<plugin::StorageEngine *, void>
 
323
{
 
324
  int trans_len, found_foreign_xids, found_my_xids;
 
325
  bool result;
 
326
  XID *trans_list;
 
327
  HASH *commit_list;
 
328
  bool dry_run;
 
329
public:
 
330
  XARecover(XID *trans_list_arg, int trans_len_arg,
 
331
            HASH *commit_list_arg, bool dry_run_arg) 
 
332
    : trans_len(trans_len_arg), found_foreign_xids(0), found_my_xids(0),
 
333
      result(false),
 
334
      trans_list(trans_list_arg), commit_list(commit_list_arg),
 
335
      dry_run(dry_run_arg)
 
336
  {}
 
337
  
 
338
  int getForeignXIDs()
 
339
  {
 
340
    return found_foreign_xids; 
 
341
  }
 
342
 
 
343
  int getMyXIDs()
 
344
  {
 
345
    return found_my_xids; 
 
346
  }
 
347
 
 
348
  result_type operator() (argument_type engine)
 
349
  {
 
350
  
 
351
    int got;
 
352
  
 
353
    if (engine->is_enabled())
 
354
    {
 
355
      while ((got= engine->recover(trans_list, trans_len)) > 0 )
 
356
      {
 
357
        errmsg_printf(ERRMSG_LVL_INFO,
 
358
                      _("Found %d prepared transaction(s) in %s"),
 
359
                      got, engine->getName().c_str());
 
360
        for (int i=0; i < got; i ++)
 
361
        {
 
362
          my_xid x=trans_list[i].get_my_xid();
 
363
          if (!x) // not "mine" - that is generated by external TM
 
364
          {
 
365
            xid_cache_insert(trans_list+i, XA_PREPARED);
 
366
            found_foreign_xids++;
 
367
            continue;
 
368
          }
 
369
          if (dry_run)
 
370
          {
 
371
            found_my_xids++;
 
372
            continue;
 
373
          }
 
374
          // recovery mode
 
375
          if (commit_list ?
 
376
              hash_search(commit_list, (unsigned char *)&x, sizeof(x)) != 0 :
 
377
              tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
 
378
          {
 
379
            engine->commit_by_xid(trans_list+i);
 
380
          }
 
381
          else
 
382
          {
 
383
            engine->rollback_by_xid(trans_list+i);
 
384
          }
 
385
        }
 
386
        if (got < trans_len)
 
387
          break;
 
388
      }
 
389
    }
 
390
  }
 
391
};
 
392
 
 
393
int plugin::StorageEngine::recover(HASH *commit_list)
 
394
{
 
395
  XID *trans_list= NULL;
 
396
  int trans_len= 0;
 
397
 
 
398
  bool dry_run= (commit_list==0 && tc_heuristic_recover==0);
 
399
 
 
400
  /* commit_list and tc_heuristic_recover cannot be set both */
 
401
  assert(commit_list==0 || tc_heuristic_recover==0);
 
402
 
 
403
  /* if either is set, total_ha_2pc must be set too */
 
404
  if (total_ha_2pc <= 1)
 
405
    return 0;
 
406
 
 
407
 
 
408
#ifndef WILL_BE_DELETED_LATER
 
409
 
 
410
  /*
 
411
    for now, only InnoDB supports 2pc. It means we can always safely
 
412
    rollback all pending transactions, without risking inconsistent data
 
413
  */
 
414
 
 
415
  assert(total_ha_2pc == 2); // only InnoDB and binlog
 
416
  tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
 
417
  dry_run=false;
 
418
#endif
 
419
  for (trans_len= MAX_XID_LIST_SIZE ;
 
420
       trans_list==0 && trans_len > MIN_XID_LIST_SIZE; trans_len/=2)
 
421
  {
 
422
    trans_list=(XID *)malloc(trans_len*sizeof(XID));
 
423
  }
 
424
  if (!trans_list)
 
425
  {
 
426
    errmsg_printf(ERRMSG_LVL_ERROR, ER(ER_OUTOFMEMORY), trans_len*sizeof(XID));
 
427
    return(1);
 
428
  }
 
429
 
 
430
  if (commit_list)
 
431
    errmsg_printf(ERRMSG_LVL_INFO, _("Starting crash recovery..."));
 
432
 
 
433
 
 
434
  XARecover recover_func(trans_list, trans_len, commit_list, dry_run);
 
435
  for_each(all_engines.begin(), all_engines.end(), recover_func);
 
436
  free(trans_list);
 
437
 
 
438
  if (recover_func.getForeignXIDs())
 
439
    errmsg_printf(ERRMSG_LVL_WARN,
 
440
                  _("Found %d prepared XA transactions"),
 
441
                  recover_func.getForeignXIDs());
 
442
  if (dry_run && recover_func.getMyXIDs())
 
443
  {
 
444
    errmsg_printf(ERRMSG_LVL_ERROR,
 
445
                  _("Found %d prepared transactions! It means that drizzled "
 
446
                    "was not shut down properly last time and critical "
 
447
                    "recovery information (last binlog or %s file) was "
 
448
                    "manually deleted after a crash. You have to start "
 
449
                    "drizzled with the --tc-heuristic-recover switch to "
 
450
                    "commit or rollback pending transactions."),
 
451
                    recover_func.getMyXIDs(), opt_tc_log_file);
 
452
    return(1);
 
453
  }
 
454
  if (commit_list)
 
455
    errmsg_printf(ERRMSG_LVL_INFO, _("Crash recovery finished."));
 
456
  return(0);
 
457
}
 
458
 
 
459
int plugin::StorageEngine::startConsistentSnapshot(Session *session)
 
460
{
 
461
  for_each(all_engines.begin(), all_engines.end(),
 
462
           bind2nd(mem_fun(&plugin::StorageEngine::start_consistent_snapshot),
 
463
                   session));
 
464
  return 0;
 
465
}
 
466
 
 
467
class StorageEngineGetTableProto: public unary_function<plugin::StorageEngine *,bool>
 
468
{
 
469
  const char* path;
 
470
  message::Table *table_proto;
 
471
  int *err;
 
472
public:
 
473
  StorageEngineGetTableProto(const char* path_arg,
 
474
                             message::Table *table_proto_arg,
 
475
                             int *err_arg)
 
476
  :path(path_arg), table_proto(table_proto_arg), err(err_arg) {}
 
477
 
 
478
  result_type operator() (argument_type engine)
 
479
  {
 
480
    int ret= engine->getTableProtoImplementation(path, table_proto);
 
481
 
 
482
    if (ret != ENOENT)
 
483
      *err= ret;
 
484
 
 
485
    return *err == EEXIST;
 
486
  }
 
487
};
 
488
 
 
489
static int drizzle_read_table_proto(const char* path, message::Table* table)
 
490
{
 
491
  int fd= open(path, O_RDONLY);
 
492
 
 
493
  if (fd == -1)
 
494
    return errno;
 
495
 
 
496
  google::protobuf::io::ZeroCopyInputStream* input=
 
497
    new google::protobuf::io::FileInputStream(fd);
 
498
 
 
499
  if (table->ParseFromZeroCopyStream(input) == false)
 
500
  {
 
501
    delete input;
 
502
    close(fd);
 
503
    return -1;
 
504
  }
 
505
 
 
506
  delete input;
 
507
  close(fd);
 
508
  return 0;
 
509
}
 
510
 
 
511
/**
 
512
  Call this function in order to give the handler the possiblity
 
513
  to ask engine if there are any new tables that should be written to disk
 
514
  or any dropped tables that need to be removed from disk
 
515
*/
 
516
int plugin::StorageEngine::getTableProto(const char* path,
 
517
                                         message::Table *table_proto)
 
518
{
 
519
  int err= ENOENT;
 
520
 
 
521
  ::drizzled::Registry<plugin::StorageEngine *>::iterator iter=
 
522
    find_if(all_engines.begin(), all_engines.end(),
 
523
            StorageEngineGetTableProto(path, table_proto, &err));
 
524
  if (iter == all_engines.end())
 
525
  {
 
526
    string proto_path(path);
 
527
    string file_ext(".dfe");
 
528
    proto_path.append(file_ext);
 
529
 
 
530
    int error= access(proto_path.c_str(), F_OK);
 
531
 
 
532
    if (error == 0)
 
533
      err= EEXIST;
 
534
    else
 
535
      err= errno;
 
536
 
 
537
    if (table_proto)
 
538
    {
 
539
      int read_proto_err= drizzle_read_table_proto(proto_path.c_str(),
 
540
                                                   table_proto);
 
541
 
 
542
      if (read_proto_err)
 
543
        err= read_proto_err;
 
544
    }
 
545
  }
 
546
 
 
547
  return err;
 
548
}
 
549
 
 
550
/**
 
551
  An interceptor to hijack the text of the error message without
 
552
  setting an error in the thread. We need the text to present it
 
553
  in the form of a warning to the user.
 
554
*/
 
555
 
 
556
class Ha_delete_table_error_handler: public Internal_error_handler
 
557
{
 
558
public:
 
559
  Ha_delete_table_error_handler() : Internal_error_handler() {}
 
560
  virtual bool handle_error(uint32_t sql_errno,
 
561
                            const char *message,
 
562
                            DRIZZLE_ERROR::enum_warning_level level,
 
563
                            Session *session);
 
564
  char buff[DRIZZLE_ERRMSG_SIZE];
 
565
};
 
566
 
 
567
 
 
568
bool
 
569
Ha_delete_table_error_handler::
 
570
handle_error(uint32_t ,
 
571
             const char *message,
 
572
             DRIZZLE_ERROR::enum_warning_level ,
 
573
             Session *)
 
574
{
 
575
  /* Grab the error message */
 
576
  strncpy(buff, message, sizeof(buff)-1);
 
577
  return true;
 
578
}
 
579
 
 
580
 
 
581
class DeleteTableStorageEngine
 
582
  : public unary_function<plugin::StorageEngine *, void>
 
583
{
 
584
  Session *session;
 
585
  const char *path;
 
586
  handler **file;
 
587
  int *dt_error;
 
588
public:
 
589
  DeleteTableStorageEngine(Session *session_arg, const char *path_arg,
 
590
                           handler **file_arg, int *error_arg)
 
591
    : session(session_arg), path(path_arg), file(file_arg), dt_error(error_arg) {}
 
592
 
 
593
  result_type operator() (argument_type engine)
 
594
  {
 
595
    char tmp_path[FN_REFLEN];
 
596
    handler *tmp_file;
 
597
 
 
598
    if(*dt_error!=ENOENT) /* already deleted table */
 
599
      return;
 
600
 
 
601
    if (!engine)
 
602
      return;
 
603
 
 
604
    if (!engine->is_enabled())
 
605
      return;
 
606
 
 
607
    if ((tmp_file= engine->create(NULL, session->mem_root)))
 
608
      tmp_file->init();
 
609
    else
 
610
      return;
 
611
 
 
612
    path= engine->checkLowercaseNames(path, tmp_path);
 
613
    const string table_path(path);
 
614
    int tmp_error= engine->deleteTable(session, table_path);
 
615
 
 
616
    if (tmp_error != ENOENT)
 
617
    {
 
618
      if (tmp_error == 0)
 
619
      {
 
620
        if (engine->check_flag(HTON_BIT_HAS_DATA_DICTIONARY))
 
621
          delete_table_proto_file(path);
 
622
        else
 
623
          tmp_error= delete_table_proto_file(path);
 
624
      }
 
625
 
 
626
      *dt_error= tmp_error;
 
627
      if(*file)
 
628
        delete *file;
 
629
      *file= tmp_file;
 
630
      return;
 
631
    }
 
632
    else
 
633
      delete tmp_file;
 
634
 
 
635
    return;
 
636
  }
 
637
};
 
638
 
 
639
 
 
640
/**
 
641
  This should return ENOENT if the file doesn't exists.
 
642
  The .frm file will be deleted only if we return 0 or ENOENT
 
643
*/
 
644
int plugin::StorageEngine::deleteTable(Session *session, const char *path,
 
645
                                       const char *db, const char *alias,
 
646
                                       bool generate_warning)
 
647
{
 
648
  TableShare dummy_share;
 
649
  Table dummy_table;
 
650
  memset(&dummy_table, 0, sizeof(dummy_table));
 
651
  memset(&dummy_share, 0, sizeof(dummy_share));
 
652
 
 
653
  dummy_table.s= &dummy_share;
 
654
 
 
655
  int error= ENOENT;
 
656
  handler *file= NULL;
 
657
 
 
658
  for_each(all_engines.begin(), all_engines.end(),
 
659
           DeleteTableStorageEngine(session, path, &file, &error));
 
660
 
 
661
  if (error == ENOENT) /* proto may be left behind */
 
662
    error= delete_table_proto_file(path);
 
663
 
 
664
  if (error && generate_warning)
 
665
  {
 
666
    /*
 
667
      Because file->print_error() use my_error() to generate the error message
 
668
      we use an internal error handler to intercept it and store the text
 
669
      in a temporary buffer. Later the message will be presented to user
 
670
      as a warning.
 
671
    */
 
672
    Ha_delete_table_error_handler ha_delete_table_error_handler;
 
673
 
 
674
    /* Fill up strucutures that print_error may need */
 
675
    dummy_share.path.str= (char*) path;
 
676
    dummy_share.path.length= strlen(path);
 
677
    dummy_share.db.str= (char*) db;
 
678
    dummy_share.db.length= strlen(db);
 
679
    dummy_share.table_name.str= (char*) alias;
 
680
    dummy_share.table_name.length= strlen(alias);
 
681
    dummy_table.alias= alias;
 
682
 
 
683
    if(file != NULL)
 
684
    {
 
685
      file->change_table_ptr(&dummy_table, &dummy_share);
 
686
 
 
687
      session->push_internal_handler(&ha_delete_table_error_handler);
 
688
      file->print_error(error, 0);
 
689
 
 
690
      session->pop_internal_handler();
 
691
    }
 
692
    else
 
693
      error= -1; /* General form of fail. maybe bad FRM */
 
694
 
 
695
    /*
 
696
      XXX: should we convert *all* errors to warnings here?
 
697
      What if the error is fatal?
 
698
    */
 
699
    push_warning(session, DRIZZLE_ERROR::WARN_LEVEL_ERROR, error,
 
700
                 ha_delete_table_error_handler.buff);
 
701
  }
 
702
 
 
703
  if(file)
 
704
    delete file;
 
705
 
 
706
  return error;
 
707
}
 
708
 
 
709
class DFETableNameIterator: public plugin::TableNameIteratorImplementation
 
710
{
 
711
private:
 
712
  MY_DIR *dirp;
 
713
  uint32_t current_entry;
 
714
 
 
715
public:
 
716
  DFETableNameIterator(const string &database)
 
717
  : plugin::TableNameIteratorImplementation(database),
 
718
    dirp(NULL),
 
719
    current_entry(-1)
 
720
    {};
 
721
 
 
722
  ~DFETableNameIterator();
 
723
 
 
724
  int next(string *name);
 
725
 
 
726
};
 
727
 
 
728
DFETableNameIterator::~DFETableNameIterator()
 
729
{
 
730
  if (dirp)
 
731
    my_dirend(dirp);
 
732
}
 
733
 
 
734
int DFETableNameIterator::next(string *name)
 
735
{
 
736
  char uname[NAME_LEN + 1];
 
737
  FILEINFO *file;
 
738
  char *ext;
 
739
  uint32_t file_name_len;
 
740
  const char *wild= NULL;
 
741
 
 
742
  if (dirp == NULL)
 
743
  {
 
744
    bool dir= false;
 
745
    char path[FN_REFLEN];
 
746
 
 
747
    build_table_filename(path, sizeof(path), db.c_str(), "", false);
 
748
 
 
749
    dirp = my_dir(path,MYF(dir ? MY_WANT_STAT : 0));
 
750
 
 
751
    if (dirp == NULL)
 
752
    {
 
753
      if (my_errno == ENOENT)
 
754
        my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db.c_str());
 
755
      else
 
756
        my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
 
757
      return(ENOENT);
 
758
    }
 
759
    current_entry= -1;
 
760
  }
 
761
 
 
762
  while(true)
 
763
  {
 
764
    current_entry++;
 
765
 
 
766
    if (current_entry == dirp->number_off_files)
 
767
    {
 
768
      my_dirend(dirp);
 
769
      dirp= NULL;
 
770
      return -1;
 
771
    }
 
772
 
 
773
    file= dirp->dir_entry + current_entry;
 
774
 
 
775
    if (my_strcasecmp(system_charset_info, ext=fn_rext(file->name),".dfe") ||
 
776
        is_prefix(file->name, TMP_FILE_PREFIX))
 
777
      continue;
 
778
    *ext=0;
 
779
 
 
780
    file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
 
781
 
 
782
    uname[file_name_len]= '\0';
 
783
 
 
784
    if (wild && wild_compare(uname, wild, 0))
 
785
      continue;
 
786
 
 
787
    if (name)
 
788
      name->assign(uname);
 
789
 
 
790
    return 0;
 
791
  }
 
792
}
 
793
 
 
794
 
 
795
plugin::TableNameIterator::TableNameIterator(const string &db)
 
796
  : current_implementation(NULL), database(db)
 
797
{
 
798
  engine_iter= all_engines.begin();
 
799
  default_implementation= new DFETableNameIterator(database);
 
800
}
 
801
 
 
802
plugin::TableNameIterator::~TableNameIterator()
 
803
{
 
804
  delete current_implementation;
 
805
}
 
806
 
 
807
int plugin::TableNameIterator::next(string *name)
 
808
{
 
809
  int err= 0;
 
810
 
 
811
next:
 
812
  if (current_implementation == NULL)
 
813
  {
 
814
    while(current_implementation == NULL &&
 
815
          (engine_iter != all_engines.end()))
 
816
    {
 
817
      plugin::StorageEngine *engine= *engine_iter;
 
818
      current_implementation= engine->tableNameIterator(database);
 
819
      engine_iter++;
 
820
    }
 
821
 
 
822
    if (current_implementation == NULL &&
 
823
        (engine_iter == all_engines.end()))
 
824
    {
 
825
      current_implementation= default_implementation;
 
826
    }
 
827
  }
 
828
 
 
829
  err= current_implementation->next(name);
 
830
 
 
831
  if (err == -1)
 
832
  {
 
833
    if (current_implementation != default_implementation)
 
834
    {
 
835
      delete current_implementation;
 
836
      current_implementation= NULL;
 
837
      goto next;
 
838
    }
 
839
  }
 
840
 
 
841
  return err;
 
842
}
 
843
 
 
844
 
 
845
} /* namespace drizzled */
 
846
 
 
847
 
 
848
 
 
849
handler *get_new_handler(TableShare *share, MEM_ROOT *alloc,
 
850
                         drizzled::plugin::StorageEngine *engine)
 
851
{
 
852
  handler *file;
 
853
 
 
854
  if (engine && engine->is_enabled())
 
855
  {
 
856
    if ((file= engine->create(share, alloc)))
 
857
      file->init();
 
858
    return(file);
 
859
  }
 
860
  /*
 
861
    Try the default table type
 
862
    Here the call to current_session() is ok as we call this function a lot of
 
863
    times but we enter this branch very seldom.
 
864
  */
 
865
  return(get_new_handler(share, alloc, ha_default_storage_engine(current_session)));
 
866
}
 
867
 
 
868
 
 
869
/**
 
870
  Return the default storage engine plugin::StorageEngine for thread
 
871
 
 
872
  @param ha_default_storage_engine(session)
 
873
  @param session         current thread
 
874
 
 
875
  @return
 
876
    pointer to plugin::StorageEngine
 
877
*/
 
878
drizzled::plugin::StorageEngine *ha_default_storage_engine(Session *session)
 
879
{
 
880
  if (session->variables.storage_engine)
 
881
    return session->variables.storage_engine;
 
882
  return global_system_variables.storage_engine;
 
883
}
 
884
 
182
885
/**
183
886
  Initiates table-file and calls appropriate database-creator.
184
887
 
191
894
                    const char *db, const char *table_name,
192
895
                    HA_CREATE_INFO *create_info,
193
896
                    bool update_create_info,
194
 
                    message::Table *table_proto)
 
897
                    drizzled::message::Table *table_proto)
195
898
{
196
899
  int error= 1;
197
900
  Table table;
198
901
  TableShare share(db, 0, table_name, path);
199
 
  message::Table tmp_proto;
 
902
  drizzled::message::Table tmp_proto;
200
903
 
201
904
  if (table_proto)
202
905
  {
231
934
  return(error != 0);
232
935
}
233
936
 
234
 
 
235
 
const string ha_resolve_storage_engine_name(const plugin::StorageEngine *engine)
236
 
{
237
 
  return engine == NULL ? string("UNKNOWN") : engine->getName();
238
 
}
239
 
 
240
 
const char *plugin::StorageEngine::checkLowercaseNames(const char *path, char *tmp_path)
241
 
{
242
 
  if (flags.test(HTON_BIT_FILE_BASED))
243
 
    return path;
244
 
 
245
 
  /* Ensure that table handler get path in lower case */
246
 
  if (tmp_path != path)
247
 
    strcpy(tmp_path, path);
248
 
 
249
 
  /*
250
 
    we only should turn into lowercase database/table part
251
 
    so start the process after homedirectory
252
 
  */
253
 
  if (strstr(tmp_path, drizzle_tmpdir) == tmp_path)
254
 
    my_casedn_str(files_charset_info, tmp_path + strlen(drizzle_tmpdir));
255
 
  else
256
 
    my_casedn_str(files_charset_info, tmp_path + drizzle_data_home_len);
257
 
 
258
 
  return tmp_path;
259
 
}
260