~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/plugin/storage_engine.cc

  • Committer: Eric Day
  • Date: 2009-08-27 07:26:22 UTC
  • mto: This revision was merged to the branch mainline in revision 1131.
  • Revision ID: eday@oddments.org-20090827072622-72te13ua0wdlc2ky
Reworked listen interface to not require binding of TCP ports.

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