~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/plugin/storage_engine.cc

Removed protocol field flags.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
18
 */
19
19
 
20
 
#include "config.h"
21
 
 
22
 
#include <fcntl.h>
23
 
#include <unistd.h>
24
 
 
25
 
#include <string>
26
 
#include <vector>
27
 
#include <set>
28
 
#include <fstream>
29
 
#include <algorithm>
30
 
#include <functional>
31
 
 
32
 
#include <google/protobuf/io/zero_copy_stream.h>
33
 
#include <google/protobuf/io/zero_copy_stream_impl.h>
34
 
 
35
 
#include "drizzled/cached_directory.h"
36
 
 
 
20
#include <drizzled/server_includes.h>
37
21
#include <drizzled/definitions.h>
38
22
#include <drizzled/base.h>
39
 
#include <drizzled/cursor.h>
 
23
#include <drizzled/handler.h>
40
24
#include <drizzled/plugin/storage_engine.h>
41
25
#include <drizzled/session.h>
42
26
#include <drizzled/error.h>
43
27
#include <drizzled/gettext.h>
 
28
#include <drizzled/registry.h>
44
29
#include <drizzled/unireg.h>
45
30
#include <drizzled/data_home.h>
46
 
#include "drizzled/errmsg_print.h"
47
 
#include "drizzled/xid.h"
48
 
#include "drizzled/sql_table.h"
49
 
#include "drizzled/global_charset_info.h"
50
 
#include "drizzled/charset.h"
51
 
#include "drizzled/internal/my_sys.h"
52
 
#include "drizzled/db.h"
 
31
#include <drizzled/plugin_registry.h>
 
32
#include <string>
53
33
 
54
34
#include <drizzled/table_proto.h>
55
 
#include <drizzled/plugin/event_observer.h>
56
 
 
57
 
#include <drizzled/table/shell.h>
58
 
 
59
 
#include "drizzled/message/cache.h"
60
 
 
61
 
#include <boost/algorithm/string/compare.hpp>
62
 
 
63
 
static bool shutdown_has_begun= false; // Once we put in the container for the vector/etc for engines this will go away.
64
 
 
65
 
namespace drizzled
66
 
{
67
 
 
68
 
namespace plugin
69
 
{
70
 
 
71
 
static EngineVector vector_of_engines;
72
 
static EngineVector vector_of_schema_engines;
73
 
 
74
 
const std::string DEFAULT_STRING("default");
75
 
const std::string UNKNOWN_STRING("UNKNOWN");
76
 
const std::string DEFAULT_DEFINITION_FILE_EXT(".dfe");
77
 
 
78
 
static std::set<std::string> set_of_table_definition_ext;
79
 
 
80
 
EngineVector &StorageEngine::getSchemaEngines()
81
 
{
82
 
  return vector_of_schema_engines;
 
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);
83
55
}
84
56
 
85
57
StorageEngine::StorageEngine(const std::string name_arg,
86
 
                             const std::bitset<HTON_BIT_SIZE> &flags_arg) :
87
 
  Plugin(name_arg, "StorageEngine"),
88
 
  MonitoredInTransaction(), /* This gives the storage engine a "slot" or ID */
89
 
  flags(flags_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)
90
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
  }
91
74
}
92
75
 
 
76
 
93
77
StorageEngine::~StorageEngine()
94
78
{
95
 
}
96
 
 
97
 
void StorageEngine::setTransactionReadWrite(Session& session)
98
 
{
99
 
  TransactionContext &statement_ctx= session.transaction.stmt;
100
 
  statement_ctx.markModifiedNonTransData();
101
 
}
102
 
 
103
 
 
104
 
int StorageEngine::renameTable(Session &session, const TableIdentifier &from, const TableIdentifier &to)
105
 
{
106
 
  int error;
107
 
  setTransactionReadWrite(session);
108
 
 
109
 
  if (unlikely(plugin::EventObserver::beforeRenameTable(session, from, to)))
110
 
  {
111
 
    error= ER_EVENT_OBSERVER_PLUGIN;
112
 
  }
113
 
  else
114
 
  {
115
 
    error =  doRenameTable(session, from, to);
116
 
    if (unlikely(plugin::EventObserver::afterRenameTable(session, from, to, error)))
117
 
    {
118
 
      error= ER_EVENT_OBSERVER_PLUGIN;
119
 
    }
120
 
  }
121
 
  
 
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
//    if (table_share == NULL || table_share->tmp_table == NO_TMP_TABLE)
 
100
      ha_info->set_trx_read_write();
 
101
  }
 
102
}
 
103
 
 
104
 
 
105
/**
 
106
  Return the default storage engine StorageEngine for thread
 
107
 
 
108
  @param ha_default_storage_engine(session)
 
109
  @param session         current thread
 
110
 
 
111
  @return
 
112
    pointer to StorageEngine
 
113
*/
 
114
StorageEngine *ha_default_storage_engine(Session *session)
 
115
{
 
116
  if (session->variables.storage_engine)
 
117
    return session->variables.storage_engine;
 
118
  return global_system_variables.storage_engine;
 
119
}
 
120
 
 
121
 
 
122
/**
 
123
  Return the storage engine StorageEngine for the supplied name
 
124
 
 
125
  @param session         current thread
 
126
  @param name        name of storage engine
 
127
 
 
128
  @return
 
129
    pointer to storage engine plugin handle
 
130
*/
 
131
StorageEngine *ha_resolve_by_name(Session *session, std::string find_str)
 
132
{
 
133
  transform(find_str.begin(), find_str.end(),
 
134
            find_str.begin(), ::tolower);
 
135
  string default_str("default");
 
136
  if (find_str == default_str)
 
137
    return ha_default_storage_engine(session);
 
138
 
 
139
  StorageEngine *engine= all_engines.find(find_str);
 
140
 
 
141
  if (engine && engine->is_user_selectable())
 
142
    return engine;
 
143
 
 
144
  return NULL;
 
145
}
 
146
 
 
147
 
 
148
handler *get_new_handler(TableShare *share, MEM_ROOT *alloc,
 
149
                         StorageEngine *engine)
 
150
{
 
151
  handler *file;
 
152
 
 
153
  if (engine && engine->is_enabled())
 
154
  {
 
155
    if ((file= engine->create(share, alloc)))
 
156
      file->init();
 
157
    return(file);
 
158
  }
 
159
  /*
 
160
    Try the default table type
 
161
    Here the call to current_session() is ok as we call this function a lot of
 
162
    times but we enter this branch very seldom.
 
163
  */
 
164
  return(get_new_handler(share, alloc, ha_default_storage_engine(current_session)));
 
165
}
 
166
 
 
167
class StorageEngineCloseConnection
 
168
  : public unary_function<StorageEngine *, void>
 
169
{
 
170
  Session *session;
 
171
public:
 
172
  StorageEngineCloseConnection(Session *session_arg) : session(session_arg) {}
 
173
  /*
 
174
    there's no need to rollback here as all transactions must
 
175
    be rolled back already
 
176
  */
 
177
  inline result_type operator() (argument_type engine)
 
178
  {
 
179
    if (engine->is_enabled() && 
 
180
      session_get_ha_data(session, engine))
 
181
    engine->close_connection(session);
 
182
  }
 
183
};
 
184
 
 
185
/**
 
186
  @note
 
187
    don't bother to rollback here, it's done already
 
188
*/
 
189
void ha_close_connection(Session* session)
 
190
{
 
191
  for_each(all_engines.begin(), all_engines.end(),
 
192
           StorageEngineCloseConnection(session));
 
193
}
 
194
 
 
195
void ha_drop_database(char* path)
 
196
{
 
197
  for_each(all_engines.begin(), all_engines.end(),
 
198
           bind2nd(mem_fun(&StorageEngine::drop_database),path));
 
199
}
 
200
 
 
201
int ha_commit_or_rollback_by_xid(XID *xid, bool commit)
 
202
{
 
203
  vector<int> results;
 
204
  
 
205
  if (commit)
 
206
    transform(all_engines.begin(), all_engines.end(), results.begin(),
 
207
              bind2nd(mem_fun(&StorageEngine::commit_by_xid),xid));
 
208
  else
 
209
    transform(all_engines.begin(), all_engines.end(), results.begin(),
 
210
              bind2nd(mem_fun(&StorageEngine::rollback_by_xid),xid));
 
211
 
 
212
  if (find_if(results.begin(), results.end(), bind2nd(equal_to<int>(),0))
 
213
         == results.end())
 
214
    return 1;
 
215
  return 0;
 
216
}
 
217
 
 
218
 
 
219
/**
 
220
  @details
 
221
  This function should be called when MySQL sends rows of a SELECT result set
 
222
  or the EOF mark to the client. It releases a possible adaptive hash index
 
223
  S-latch held by session in InnoDB and also releases a possible InnoDB query
 
224
  FIFO ticket to enter InnoDB. To save CPU time, InnoDB allows a session to
 
225
  keep them over several calls of the InnoDB handler interface when a join
 
226
  is executed. But when we let the control to pass to the client they have
 
227
  to be released because if the application program uses mysql_use_result(),
 
228
  it may deadlock on the S-latch if the application on another connection
 
229
  performs another SQL query. In MySQL-4.1 this is even more important because
 
230
  there a connection can have several SELECT queries open at the same time.
 
231
 
 
232
  @param session           the thread handle of the current connection
 
233
 
 
234
  @return
 
235
    always 0
 
236
*/
 
237
int ha_release_temporary_latches(Session *session)
 
238
{
 
239
  for_each(all_engines.begin(), all_engines.end(),
 
240
           bind2nd(mem_fun(&StorageEngine::release_temporary_latches),session));
 
241
  return 0;
 
242
}
 
243
 
 
244
 
 
245
bool ha_flush_logs(StorageEngine *engine)
 
246
{
 
247
  if (engine == NULL)
 
248
  {
 
249
    if (find_if(all_engines.begin(), all_engines.end(),
 
250
            mem_fun(&StorageEngine::flush_logs))
 
251
          != all_engines.begin())
 
252
      return true;
 
253
  }
 
254
  else
 
255
  {
 
256
    if ((!engine->is_enabled()) ||
 
257
        (engine->flush_logs()))
 
258
      return true;
 
259
  }
 
260
  return false;
 
261
}
 
262
 
 
263
/**
 
264
  recover() step of xa.
 
265
 
 
266
  @note
 
267
    there are three modes of operation:
 
268
    - automatic recover after a crash
 
269
    in this case commit_list != 0, tc_heuristic_recover==0
 
270
    all xids from commit_list are committed, others are rolled back
 
271
    - manual (heuristic) recover
 
272
    in this case commit_list==0, tc_heuristic_recover != 0
 
273
    DBA has explicitly specified that all prepared transactions should
 
274
    be committed (or rolled back).
 
275
    - no recovery (MySQL did not detect a crash)
 
276
    in this case commit_list==0, tc_heuristic_recover == 0
 
277
    there should be no prepared transactions in this case.
 
278
*/
 
279
class XARecover : unary_function<StorageEngine *, void>
 
280
{
 
281
  int trans_len, found_foreign_xids, found_my_xids;
 
282
  bool result;
 
283
  XID *trans_list;
 
284
  HASH *commit_list;
 
285
  bool dry_run;
 
286
public:
 
287
  XARecover(XID *trans_list_arg, int trans_len_arg,
 
288
            HASH *commit_list_arg, bool dry_run_arg) 
 
289
    : trans_len(trans_len_arg), found_foreign_xids(0), found_my_xids(0),
 
290
      result(false),
 
291
      trans_list(trans_list_arg), commit_list(commit_list_arg),
 
292
      dry_run(dry_run_arg)
 
293
  {}
 
294
  
 
295
  int getForeignXIDs()
 
296
  {
 
297
    return found_foreign_xids; 
 
298
  }
 
299
 
 
300
  int getMyXIDs()
 
301
  {
 
302
    return found_my_xids; 
 
303
  }
 
304
 
 
305
  result_type operator() (argument_type engine)
 
306
  {
 
307
  
 
308
    int got;
 
309
  
 
310
    if (engine->is_enabled())
 
311
    {
 
312
      while ((got= engine->recover(trans_list, trans_len)) > 0 )
 
313
      {
 
314
        errmsg_printf(ERRMSG_LVL_INFO,
 
315
                      _("Found %d prepared transaction(s) in %s"),
 
316
                      got, engine->getName().c_str());
 
317
        for (int i=0; i < got; i ++)
 
318
        {
 
319
          my_xid x=trans_list[i].get_my_xid();
 
320
          if (!x) // not "mine" - that is generated by external TM
 
321
          {
 
322
            xid_cache_insert(trans_list+i, XA_PREPARED);
 
323
            found_foreign_xids++;
 
324
            continue;
 
325
          }
 
326
          if (dry_run)
 
327
          {
 
328
            found_my_xids++;
 
329
            continue;
 
330
          }
 
331
          // recovery mode
 
332
          if (commit_list ?
 
333
              hash_search(commit_list, (unsigned char *)&x, sizeof(x)) != 0 :
 
334
              tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
 
335
          {
 
336
            engine->commit_by_xid(trans_list+i);
 
337
          }
 
338
          else
 
339
          {
 
340
            engine->rollback_by_xid(trans_list+i);
 
341
          }
 
342
        }
 
343
        if (got < trans_len)
 
344
          break;
 
345
      }
 
346
    }
 
347
  }
 
348
 
 
349
};
 
350
 
 
351
int ha_recover(HASH *commit_list)
 
352
{
 
353
  XID *trans_list= NULL;
 
354
  int trans_len= 0;
 
355
 
 
356
  bool dry_run= (commit_list==0 && tc_heuristic_recover==0);
 
357
 
 
358
  /* commit_list and tc_heuristic_recover cannot be set both */
 
359
  assert(commit_list==0 || tc_heuristic_recover==0);
 
360
 
 
361
  /* if either is set, total_ha_2pc must be set too */
 
362
  if (total_ha_2pc <= 1)
 
363
    return 0;
 
364
 
 
365
 
 
366
#ifndef WILL_BE_DELETED_LATER
 
367
 
 
368
  /*
 
369
    for now, only InnoDB supports 2pc. It means we can always safely
 
370
    rollback all pending transactions, without risking inconsistent data
 
371
  */
 
372
 
 
373
  assert(total_ha_2pc == 2); // only InnoDB and binlog
 
374
  tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
 
375
  dry_run=false;
 
376
#endif
 
377
  for (trans_len= MAX_XID_LIST_SIZE ;
 
378
       trans_list==0 && trans_len > MIN_XID_LIST_SIZE; trans_len/=2)
 
379
  {
 
380
    trans_list=(XID *)malloc(trans_len*sizeof(XID));
 
381
  }
 
382
  if (!trans_list)
 
383
  {
 
384
    errmsg_printf(ERRMSG_LVL_ERROR, ER(ER_OUTOFMEMORY), trans_len*sizeof(XID));
 
385
    return(1);
 
386
  }
 
387
 
 
388
  if (commit_list)
 
389
    errmsg_printf(ERRMSG_LVL_INFO, _("Starting crash recovery..."));
 
390
 
 
391
 
 
392
  XARecover recover_func(trans_list, trans_len, commit_list, dry_run);
 
393
  for_each(all_engines.begin(), all_engines.end(), recover_func);
 
394
  free(trans_list);
 
395
 
 
396
  if (recover_func.getForeignXIDs())
 
397
    errmsg_printf(ERRMSG_LVL_WARN,
 
398
                  _("Found %d prepared XA transactions"),
 
399
                  recover_func.getForeignXIDs());
 
400
  if (dry_run && recover_func.getMyXIDs())
 
401
  {
 
402
    errmsg_printf(ERRMSG_LVL_ERROR,
 
403
                  _("Found %d prepared transactions! It means that drizzled "
 
404
                    "was not shut down properly last time and critical "
 
405
                    "recovery information (last binlog or %s file) was "
 
406
                    "manually deleted after a crash. You have to start "
 
407
                    "drizzled with the --tc-heuristic-recover switch to "
 
408
                    "commit or rollback pending transactions."),
 
409
                    recover_func.getMyXIDs(), opt_tc_log_file);
 
410
    return(1);
 
411
  }
 
412
  if (commit_list)
 
413
    errmsg_printf(ERRMSG_LVL_INFO, _("Crash recovery finished."));
 
414
  return(0);
 
415
}
 
416
 
 
417
int ha_start_consistent_snapshot(Session *session)
 
418
{
 
419
  for_each(all_engines.begin(), all_engines.end(),
 
420
           bind2nd(mem_fun(&StorageEngine::start_consistent_snapshot),session));
 
421
  return 0;
 
422
}
 
423
 
 
424
static int drizzle_read_table_proto(const char* path, drizzled::message::Table* table)
 
425
{
 
426
  int fd= open(path, O_RDONLY);
 
427
 
 
428
  if (fd == -1)
 
429
    return errno;
 
430
 
 
431
  google::protobuf::io::ZeroCopyInputStream* input=
 
432
    new google::protobuf::io::FileInputStream(fd);
 
433
 
 
434
  if (table->ParseFromZeroCopyStream(input) == false)
 
435
  {
 
436
    delete input;
 
437
    close(fd);
 
438
    return -1;
 
439
  }
 
440
 
 
441
  delete input;
 
442
  close(fd);
 
443
  return 0;
 
444
}
 
445
 
 
446
class StorageEngineGetTableProto: public unary_function<StorageEngine *,bool>
 
447
{
 
448
  const char* path;
 
449
  drizzled::message::Table *table_proto;
 
450
  int *err;
 
451
public:
 
452
  StorageEngineGetTableProto(const char* path_arg,
 
453
                             drizzled::message::Table *table_proto_arg,
 
454
                             int *err_arg)
 
455
  :path(path_arg), table_proto(table_proto_arg), err(err_arg) {}
 
456
 
 
457
  result_type operator() (argument_type engine)
 
458
  {
 
459
    int ret= engine->getTableProtoImplementation(path, table_proto);
 
460
 
 
461
    if (ret != ENOENT)
 
462
      *err= ret;
 
463
 
 
464
    return *err == EEXIST;
 
465
  }
 
466
};
 
467
 
 
468
/**
 
469
  Call this function in order to give the handler the possiblity
 
470
  to ask engine if there are any new tables that should be written to disk
 
471
  or any dropped tables that need to be removed from disk
 
472
*/
 
473
int StorageEngine::getTableProto(const char* path,
 
474
                                 drizzled::message::Table *table_proto)
 
475
{
 
476
  int err= ENOENT;
 
477
 
 
478
  drizzled::Registry<StorageEngine *>::iterator iter=
 
479
    find_if(all_engines.begin(), all_engines.end(),
 
480
            StorageEngineGetTableProto(path, table_proto, &err));
 
481
  if (iter == all_engines.end())
 
482
  {
 
483
    string proto_path(path);
 
484
    string file_ext(".dfe");
 
485
    proto_path.append(file_ext);
 
486
 
 
487
    int error= access(proto_path.c_str(), F_OK);
 
488
 
 
489
    if (error == 0)
 
490
      err= EEXIST;
 
491
    else
 
492
      err= errno;
 
493
 
 
494
    if (table_proto)
 
495
    {
 
496
      int read_proto_err= drizzle_read_table_proto(proto_path.c_str(),
 
497
                                                   table_proto);
 
498
 
 
499
      if (read_proto_err)
 
500
        err= read_proto_err;
 
501
    }
 
502
  }
 
503
 
 
504
  return err;
 
505
}
 
506
 
 
507
 
 
508
int StorageEngine::renameTableImplementation(Session *, const char *from, const char *to)
 
509
{
 
510
  int error= 0;
 
511
  for (const char **ext= bas_ext(); *ext ; ext++)
 
512
  {
 
513
    if (rename_file_ext(from, to, *ext))
 
514
    {
 
515
      if ((error=my_errno) != ENOENT)
 
516
        break;
 
517
      error= 0;
 
518
    }
 
519
  }
122
520
  return error;
123
521
}
124
522
 
 
523
 
125
524
/**
126
525
  Delete all files with extension from bas_ext().
127
526
 
128
527
  @param name           Base name of table
129
528
 
130
529
  @note
131
 
    We assume that the Cursor may return more extensions than
 
530
    We assume that the handler may return more extensions than
132
531
    was actually used for the file.
133
532
 
134
533
  @retval
137
536
  @retval
138
537
    !0  Error
139
538
*/
140
 
int StorageEngine::doDropTable(Session&, const TableIdentifier &identifier)
141
 
                               
 
539
int StorageEngine::deleteTableImplementation(Session *, const std::string table_path)
142
540
{
143
541
  int error= 0;
144
542
  int enoent_or_zero= ENOENT;                   // Error if no file was deleted
145
543
  char buff[FN_REFLEN];
146
544
 
147
 
  for (const char **ext= bas_ext(); *ext ; ext++)
 
545
  for (const char **ext=bas_ext(); *ext ; ext++)
148
546
  {
149
 
    internal::fn_format(buff, identifier.getPath().c_str(), "", *ext,
150
 
                        MY_UNPACK_FILENAME|MY_APPEND_EXT);
151
 
    if (internal::my_delete_with_symlink(buff, MYF(0)))
 
547
    fn_format(buff, table_path.c_str(), "", *ext,
 
548
              MY_UNPACK_FILENAME|MY_APPEND_EXT);
 
549
    if (my_delete_with_symlink(buff, MYF(0)))
152
550
    {
153
 
      if ((error= errno) != ENOENT)
154
 
        break;
 
551
      if ((error= my_errno) != ENOENT)
 
552
        break;
155
553
    }
156
554
    else
157
 
    {
158
555
      enoent_or_zero= 0;                        // No error for ENOENT
159
 
    }
160
 
 
161
556
    error= enoent_or_zero;
162
557
  }
163
558
  return error;
164
559
}
165
560
 
166
 
bool StorageEngine::addPlugin(StorageEngine *engine)
167
 
{
168
 
 
169
 
  vector_of_engines.push_back(engine);
170
 
 
171
 
  if (engine->getTableDefinitionFileExtension().length())
172
 
  {
173
 
    assert(engine->getTableDefinitionFileExtension().length() == DEFAULT_DEFINITION_FILE_EXT.length());
174
 
    set_of_table_definition_ext.insert(engine->getTableDefinitionFileExtension());
175
 
  }
176
 
 
177
 
  if (engine->check_flag(HTON_BIT_SCHEMA_DICTIONARY))
178
 
    vector_of_schema_engines.push_back(engine);
179
 
 
180
 
  return false;
181
 
}
182
 
 
183
 
void StorageEngine::removePlugin(StorageEngine *)
184
 
{
185
 
  if (shutdown_has_begun == false)
186
 
  {
187
 
    vector_of_engines.clear();
188
 
    vector_of_schema_engines.clear();
189
 
 
190
 
    shutdown_has_begun= true;
191
 
  }
192
 
}
193
 
 
194
 
class FindEngineByName
195
 
  : public std::unary_function<StorageEngine *, bool>
196
 
{
197
 
  const std::string &predicate;
198
 
 
199
 
public:
200
 
  explicit FindEngineByName(const std::string &target_arg) :
201
 
    predicate(target_arg)
202
 
  {
203
 
  }
204
 
 
205
 
  result_type operator() (argument_type engine)
206
 
  {
207
 
    return boost::iequals(engine->getName(), predicate);
208
 
  }
209
 
};
210
 
 
211
 
StorageEngine *StorageEngine::findByName(const std::string &predicate)
212
 
{
213
 
  EngineVector::iterator iter= std::find_if(vector_of_engines.begin(),
214
 
                                            vector_of_engines.end(),
215
 
                                            FindEngineByName(predicate));
216
 
  if (iter != vector_of_engines.end())
217
 
  {
218
 
    StorageEngine *engine= *iter;
219
 
    if (engine->is_user_selectable())
220
 
      return engine;
221
 
  }
222
 
 
223
 
  return NULL;
224
 
}
225
 
 
226
 
StorageEngine *StorageEngine::findByName(Session& session, const std::string &predicate)
227
 
{
228
 
  if (boost::iequals(predicate, DEFAULT_STRING))
229
 
    return session.getDefaultStorageEngine();
230
 
 
231
 
  EngineVector::iterator iter= std::find_if(vector_of_engines.begin(),
232
 
                                            vector_of_engines.end(),
233
 
                                            FindEngineByName(predicate));
234
 
  if (iter != vector_of_engines.end())
235
 
  {
236
 
    StorageEngine *engine= *iter;
237
 
    if (engine->is_user_selectable())
238
 
      return engine;
239
 
  }
240
 
 
241
 
  return NULL;
242
 
}
243
 
 
244
 
class StorageEngineCloseConnection : public std::unary_function<StorageEngine *, void>
245
 
{
246
 
  Session *session;
247
 
public:
248
 
  StorageEngineCloseConnection(Session *session_arg) : session(session_arg) {}
249
 
  /*
250
 
    there's no need to rollback here as all transactions must
251
 
    be rolled back already
252
 
  */
253
 
  inline result_type operator() (argument_type engine)
254
 
  {
255
 
    if (*session->getEngineData(engine))
256
 
      engine->close_connection(session);
257
 
  }
258
 
};
259
 
 
260
 
/**
261
 
  @note
262
 
    don't bother to rollback here, it's done already
263
 
*/
264
 
void StorageEngine::closeConnection(Session* session)
265
 
{
266
 
  std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
267
 
                StorageEngineCloseConnection(session));
268
 
}
269
 
 
270
 
bool StorageEngine::flushLogs(StorageEngine *engine)
271
 
{
272
 
  if (engine == NULL)
273
 
  {
274
 
    if (std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
275
 
                     std::mem_fun(&StorageEngine::flush_logs))
276
 
        != vector_of_engines.begin())
277
 
      return true;
278
 
  }
279
 
  else
280
 
  {
281
 
    if (engine->flush_logs())
282
 
      return true;
283
 
  }
284
 
  return false;
285
 
}
286
 
 
287
 
class StorageEngineGetTableDefinition: public std::unary_function<StorageEngine *,bool>
288
 
{
289
 
  Session& session;
290
 
  const TableIdentifier &identifier;
291
 
  message::Table &table_message;
292
 
  int &err;
293
 
 
294
 
public:
295
 
  StorageEngineGetTableDefinition(Session& session_arg,
296
 
                                  const TableIdentifier &identifier_arg,
297
 
                                  message::Table &table_message_arg,
298
 
                                  int &err_arg) :
299
 
    session(session_arg), 
300
 
    identifier(identifier_arg),
301
 
    table_message(table_message_arg), 
302
 
    err(err_arg) {}
303
 
 
304
 
  result_type operator() (argument_type engine)
305
 
  {
306
 
    int ret= engine->doGetTableDefinition(session, identifier, table_message);
307
 
 
308
 
    if (ret != ENOENT)
309
 
      err= ret;
310
 
 
311
 
    return err == EEXIST || err != ENOENT;
312
 
  }
313
 
};
314
 
 
315
 
class StorageEngineDoesTableExist: public std::unary_function<StorageEngine *, bool>
316
 
{
317
 
  Session& session;
318
 
  const TableIdentifier &identifier;
319
 
 
320
 
public:
321
 
  StorageEngineDoesTableExist(Session& session_arg, const TableIdentifier &identifier_arg) :
322
 
    session(session_arg), 
323
 
    identifier(identifier_arg) 
324
 
  { }
325
 
 
326
 
  result_type operator() (argument_type engine)
327
 
  {
328
 
    return engine->doDoesTableExist(session, identifier);
329
 
  }
330
 
};
331
 
 
332
 
/**
333
 
  Utility method which hides some of the details of getTableDefinition()
334
 
*/
335
 
bool plugin::StorageEngine::doesTableExist(Session &session,
336
 
                                           const TableIdentifier &identifier,
337
 
                                           bool include_temporary_tables)
338
 
{
339
 
  if (include_temporary_tables)
340
 
  {
341
 
    if (session.doDoesTableExist(identifier))
342
 
      return true;
343
 
  }
344
 
 
345
 
  EngineVector::iterator iter=
346
 
    std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
347
 
                 StorageEngineDoesTableExist(session, identifier));
348
 
 
349
 
  if (iter == vector_of_engines.end())
350
 
  {
351
 
    return false;
352
 
  }
353
 
 
354
 
  return true;
355
 
}
356
 
 
357
 
bool plugin::StorageEngine::doDoesTableExist(Session&, const drizzled::TableIdentifier&)
358
 
{
359
 
  std::cerr << " Engine was called for doDoesTableExist() and does not implement it: " << this->getName() << "\n";
360
 
  assert(0);
361
 
  return false;
362
 
}
363
 
 
364
 
/**
365
 
  Call this function in order to give the Cursor the possiblity
366
 
  to ask engine if there are any new tables that should be written to disk
367
 
  or any dropped tables that need to be removed from disk
368
 
*/
369
 
int StorageEngine::getTableDefinition(Session& session,
370
 
                                      const TableIdentifier &identifier,
371
 
                                      message::table::shared_ptr &table_message,
372
 
                                      bool include_temporary_tables)
373
 
{
374
 
  int err= ENOENT;
375
 
 
376
 
  if (include_temporary_tables)
377
 
  {
378
 
    Table *table= session.find_temporary_table(identifier);
379
 
    if (table)
380
 
    {
381
 
      table_message.reset(new message::Table(*table->getShare()->getTableProto()));
382
 
      return EEXIST;
383
 
    }
384
 
  }
385
 
 
386
 
  drizzled::message::table::shared_ptr table_ptr;
387
 
  if ((table_ptr= drizzled::message::Cache::singleton().find(identifier)))
388
 
  {
389
 
    table_message= table_ptr;
390
 
  }
391
 
 
392
 
  message::Table message;
393
 
  EngineVector::iterator iter=
394
 
    std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
395
 
                 StorageEngineGetTableDefinition(session, identifier, message, err));
396
 
 
397
 
  if (iter == vector_of_engines.end())
398
 
  {
399
 
    return ENOENT;
400
 
  }
401
 
  table_message.reset(new message::Table(message));
402
 
 
403
 
 drizzled::message::Cache::singleton().insert(identifier, table_message);
404
 
 
405
 
  return err;
406
 
}
407
 
 
408
561
/**
409
562
  An interceptor to hijack the text of the error message without
410
563
  setting an error in the thread. We need the text to present it
435
588
  return true;
436
589
}
437
590
 
 
591
 
 
592
class DeleteTableStorageEngine
 
593
  : public unary_function<StorageEngine *, void>
 
594
{
 
595
  Session *session;
 
596
  const char *path;
 
597
  handler **file;
 
598
  int *dt_error;
 
599
public:
 
600
  DeleteTableStorageEngine(Session *session_arg, const char *path_arg,
 
601
                           handler **file_arg, int *error_arg)
 
602
    : session(session_arg), path(path_arg), file(file_arg), dt_error(error_arg) {}
 
603
 
 
604
  result_type operator() (argument_type engine)
 
605
  {
 
606
    char tmp_path[FN_REFLEN];
 
607
    handler *tmp_file;
 
608
 
 
609
    if(*dt_error!=ENOENT) /* already deleted table */
 
610
      return;
 
611
 
 
612
    if (!engine)
 
613
      return;
 
614
 
 
615
    if (!engine->is_enabled())
 
616
      return;
 
617
 
 
618
    if ((tmp_file= engine->create(NULL, session->mem_root)))
 
619
      tmp_file->init();
 
620
    else
 
621
      return;
 
622
 
 
623
    path= engine->checkLowercaseNames(path, tmp_path);
 
624
    const std::string table_path(path);
 
625
    int tmp_error= engine->deleteTable(session, table_path);
 
626
 
 
627
    if (tmp_error != ENOENT)
 
628
    {
 
629
      if (tmp_error == 0)
 
630
      {
 
631
        if (engine->check_flag(HTON_BIT_HAS_DATA_DICTIONARY))
 
632
          delete_table_proto_file(path);
 
633
        else
 
634
          tmp_error= delete_table_proto_file(path);
 
635
      }
 
636
 
 
637
      *dt_error= tmp_error;
 
638
      if(*file)
 
639
        delete *file;
 
640
      *file= tmp_file;
 
641
      return;
 
642
    }
 
643
    else
 
644
      delete tmp_file;
 
645
 
 
646
    return;
 
647
  }
 
648
};
 
649
 
438
650
/**
439
 
   returns ENOENT if the file doesn't exists.
 
651
  This should return ENOENT if the file doesn't exists.
 
652
  The .frm file will be deleted only if we return 0 or ENOENT
440
653
*/
441
 
int StorageEngine::dropTable(Session& session,
442
 
                             const TableIdentifier &identifier)
443
 
{
444
 
  int error= 0;
445
 
  int error_proto;
446
 
  message::table::shared_ptr src_proto;
447
 
  StorageEngine *engine;
448
 
 
449
 
  error_proto= StorageEngine::getTableDefinition(session, identifier, src_proto);
450
 
 
451
 
  if (error_proto == ER_CORRUPT_TABLE_DEFINITION)
452
 
  {
453
 
    std::string error_message;
454
 
    identifier.getSQLPath(error_message);
455
 
 
456
 
    error_message.append(" : ");
457
 
    error_message.append(src_proto->InitializationErrorString());
458
 
 
459
 
    my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0), error_message.c_str());
460
 
 
461
 
    return ER_CORRUPT_TABLE_DEFINITION;
462
 
  }
463
 
 
464
 
  if (src_proto)
465
 
    engine= StorageEngine::findByName(session, src_proto->engine().name());
466
 
  else
467
 
    engine= StorageEngine::findByName(session, "");
468
 
 
469
 
  if (not engine)
470
 
  {
471
 
    std::string error_message;
472
 
    identifier.getSQLPath(error_message);
473
 
    my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0), error_message.c_str());
474
 
 
475
 
    return ER_CORRUPT_TABLE_DEFINITION;
476
 
  }
477
 
 
478
 
  error= StorageEngine::dropTable(session, *engine, identifier);
479
 
 
480
 
  if (error_proto && error == 0)
481
 
    return 0;
482
 
 
483
 
  return error;
484
 
}
485
 
 
486
 
int StorageEngine::dropTable(Session& session,
487
 
                             StorageEngine &engine,
488
 
                             const TableIdentifier &identifier)
489
 
{
490
 
  int error;
491
 
 
492
 
  engine.setTransactionReadWrite(session);
493
 
  
494
 
  if (unlikely(plugin::EventObserver::beforeDropTable(session, identifier)))
495
 
  {
496
 
    error= ER_EVENT_OBSERVER_PLUGIN;
497
 
  }
498
 
  else
499
 
  {
500
 
    error= engine.doDropTable(session, identifier);
501
 
    if (unlikely(plugin::EventObserver::afterDropTable(session, identifier, error)))
 
654
int ha_delete_table(Session *session, const char *path,
 
655
                    const char *db, const char *alias, bool generate_warning)
 
656
{
 
657
  TableShare dummy_share;
 
658
  Table dummy_table;
 
659
  memset(&dummy_table, 0, sizeof(dummy_table));
 
660
  memset(&dummy_share, 0, sizeof(dummy_share));
 
661
 
 
662
  dummy_table.s= &dummy_share;
 
663
 
 
664
  int error= ENOENT;
 
665
  handler *file= NULL;
 
666
 
 
667
  for_each(all_engines.begin(), all_engines.end(),
 
668
           DeleteTableStorageEngine(session, path, &file, &error));
 
669
 
 
670
  if (error == ENOENT) /* proto may be left behind */
 
671
    error= delete_table_proto_file(path);
 
672
 
 
673
  if (error && generate_warning)
 
674
  {
 
675
    /*
 
676
      Because file->print_error() use my_error() to generate the error message
 
677
      we use an internal error handler to intercept it and store the text
 
678
      in a temporary buffer. Later the message will be presented to user
 
679
      as a warning.
 
680
    */
 
681
    Ha_delete_table_error_handler ha_delete_table_error_handler;
 
682
 
 
683
    /* Fill up strucutures that print_error may need */
 
684
    dummy_share.path.str= (char*) path;
 
685
    dummy_share.path.length= strlen(path);
 
686
    dummy_share.db.str= (char*) db;
 
687
    dummy_share.db.length= strlen(db);
 
688
    dummy_share.table_name.str= (char*) alias;
 
689
    dummy_share.table_name.length= strlen(alias);
 
690
    dummy_table.alias= alias;
 
691
 
 
692
 
 
693
    if(file != NULL)
502
694
    {
503
 
      error= ER_EVENT_OBSERVER_PLUGIN;
 
695
      file->change_table_ptr(&dummy_table, &dummy_share);
 
696
 
 
697
      session->push_internal_handler(&ha_delete_table_error_handler);
 
698
      file->print_error(error, 0);
 
699
 
 
700
      session->pop_internal_handler();
504
701
    }
 
702
    else
 
703
      error= -1; /* General form of fail. maybe bad FRM */
 
704
 
 
705
    /*
 
706
      XXX: should we convert *all* errors to warnings here?
 
707
      What if the error is fatal?
 
708
    */
 
709
    push_warning(session, DRIZZLE_ERROR::WARN_LEVEL_ERROR, error,
 
710
                 ha_delete_table_error_handler.buff);
505
711
  }
506
712
 
507
 
  drizzled::message::Cache::singleton().erase(identifier);
 
713
  if(file)
 
714
    delete file;
508
715
 
509
716
  return error;
510
717
}
511
718
 
512
 
 
513
719
/**
514
720
  Initiates table-file and calls appropriate database-creator.
515
721
 
518
724
  @retval
519
725
   1  error
520
726
*/
521
 
int StorageEngine::createTable(Session &session,
522
 
                               const TableIdentifier &identifier,
523
 
                               message::Table& table_message)
 
727
int ha_create_table(Session *session, const char *path,
 
728
                    const char *db, const char *table_name,
 
729
                    HA_CREATE_INFO *create_info,
 
730
                    bool update_create_info,
 
731
                    drizzled::message::Table *table_proto)
524
732
{
525
733
  int error= 1;
526
 
  TableShare share(identifier);
527
 
  table::Shell table(share);
528
 
  message::Table tmp_proto;
 
734
  Table table;
 
735
  TableShare share(db, 0, table_name, path);
529
736
 
530
 
  if (share.parse_table_proto(session, table_message) || share.open_table_from_share(&session, identifier, "", 0, 0, table))
531
 
  { 
532
 
    // @note Error occured, we should probably do a little more here.
 
737
  if (table_proto)
 
738
  {
 
739
    if (parse_table_proto(session, *table_proto, &share))
 
740
      goto err;
533
741
  }
534
742
  else
535
743
  {
536
 
    /* Check for legal operations against the Engine using the proto (if used) */
537
 
    if (table_message.type() == message::Table::TEMPORARY &&
538
 
        share.storage_engine->check_flag(HTON_BIT_TEMPORARY_NOT_SUPPORTED) == true)
539
 
    {
540
 
      error= HA_ERR_UNSUPPORTED;
541
 
    }
542
 
    else if (table_message.type() != message::Table::TEMPORARY &&
543
 
             share.storage_engine->check_flag(HTON_BIT_TEMPORARY_ONLY) == true)
544
 
    {
545
 
      error= HA_ERR_UNSUPPORTED;
546
 
    }
547
 
    else
548
 
    {
549
 
      share.storage_engine->setTransactionReadWrite(session);
550
 
 
551
 
      error= share.storage_engine->doCreateTable(session,
552
 
                                                 table,
553
 
                                                 identifier,
554
 
                                                 table_message);
555
 
    }
556
 
 
557
 
    if (error)
558
 
    {
559
 
      std::string path;
560
 
      identifier.getSQLPath(path);
561
 
      my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), path.c_str(), error);
562
 
    }
563
 
 
564
 
    table.delete_table();
565
 
  }
566
 
 
 
744
    if (open_table_def(session, &share))
 
745
      goto err;
 
746
  }
 
747
 
 
748
  if (open_table_from_share(session, &share, "", 0, (uint32_t) READ_ALL, 0,
 
749
                            &table, OTM_CREATE))
 
750
    goto err;
 
751
 
 
752
  if (update_create_info)
 
753
    table.updateCreateInfo(create_info);
 
754
 
 
755
  error= share.storage_engine->createTable(session, path, &table,
 
756
                                           create_info, table_proto);
 
757
  table.closefrm(false);
 
758
  if (error)
 
759
  {
 
760
    char name_buff[FN_REFLEN];
 
761
    sprintf(name_buff,"%s.%s",db,table_name);
 
762
    my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name_buff, error);
 
763
  }
 
764
err:
 
765
  share.free_table_share();
567
766
  return(error != 0);
568
767
}
569
768
 
570
 
Cursor *StorageEngine::getCursor(Table &arg)
571
 
{
572
 
  return create(arg);
573
 
}
574
 
 
575
 
class AddTableIdentifier : 
576
 
  public std::unary_function<StorageEngine *, void>
577
 
{
578
 
  CachedDirectory &directory;
579
 
  const SchemaIdentifier &identifier;
580
 
  TableIdentifier::vector &set_of_identifiers;
581
 
 
582
 
public:
583
 
 
584
 
  AddTableIdentifier(CachedDirectory &directory_arg, const SchemaIdentifier &identifier_arg, TableIdentifier::vector &of_names) :
585
 
    directory(directory_arg),
586
 
    identifier(identifier_arg),
587
 
    set_of_identifiers(of_names)
588
 
  {
589
 
  }
590
 
 
591
 
  result_type operator() (argument_type engine)
592
 
  {
593
 
    engine->doGetTableIdentifiers(directory, identifier, set_of_identifiers);
594
 
  }
595
 
};
596
 
 
597
 
 
598
 
void StorageEngine::getIdentifiers(Session &session, const SchemaIdentifier &schema_identifier, TableIdentifier::vector &set_of_identifiers)
599
 
{
600
 
  static SchemaIdentifier INFORMATION_SCHEMA_IDENTIFIER("information_schema");
601
 
  static SchemaIdentifier DATA_DICTIONARY_IDENTIFIER("data_dictionary");
602
 
 
603
 
  CachedDirectory directory(schema_identifier.getPath(), set_of_table_definition_ext);
604
 
 
605
 
  if (schema_identifier == INFORMATION_SCHEMA_IDENTIFIER)
606
 
  { }
607
 
  else if (schema_identifier == DATA_DICTIONARY_IDENTIFIER)
608
 
  { }
609
 
  else
610
 
  {
611
 
    if (directory.fail())
612
 
    {
613
 
      errno= directory.getError();
614
 
      if (errno == ENOENT)
615
 
      {
616
 
        std::string path;
617
 
        schema_identifier.getSQLPath(path);
618
 
        my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), path.c_str());
619
 
      }
620
 
      else
621
 
      {
622
 
        my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), directory.getPath(), errno);
623
 
      }
624
 
 
625
 
      return;
626
 
    }
627
 
  }
628
 
 
629
 
  std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
630
 
                AddTableIdentifier(directory, schema_identifier, set_of_identifiers));
631
 
 
632
 
  session.doGetTableIdentifiers(directory, schema_identifier, set_of_identifiers);
633
 
}
634
 
 
635
 
class DropTable: public std::unary_function<TableIdentifier&, bool>
636
 
{
637
 
  Session &session;
638
 
  StorageEngine *engine;
639
 
 
640
 
public:
641
 
 
642
 
  DropTable(Session &session_arg, StorageEngine *engine_arg) :
643
 
    session(session_arg),
644
 
    engine(engine_arg)
645
 
  { }
646
 
 
647
 
  result_type operator() (argument_type identifier)
648
 
  {
649
 
    return engine->doDropTable(session, identifier) == 0;
650
 
  } 
651
 
};
652
 
 
653
 
/* This will later be converted to TableIdentifiers */
654
 
class DropTables: public std::unary_function<StorageEngine *, void>
655
 
{
656
 
  Session &session;
657
 
  TableIdentifier::vector &table_identifiers;
658
 
 
659
 
public:
660
 
 
661
 
  DropTables(Session &session_arg, TableIdentifier::vector &table_identifiers_arg) :
662
 
    session(session_arg),
663
 
    table_identifiers(table_identifiers_arg)
664
 
  { }
665
 
 
666
 
  result_type operator() (argument_type engine)
667
 
  {
668
 
    // True returning from DropTable means the table has been successfully
669
 
    // deleted, so it should be removed from the list of tables to drop
670
 
    table_identifiers.erase(std::remove_if(table_identifiers.begin(),
671
 
                                           table_identifiers.end(),
672
 
                                           DropTable(session, engine)),
673
 
                            table_identifiers.end());
674
 
  }
675
 
};
676
 
 
677
 
/*
678
 
  This only works for engines which use file based DFE.
679
 
 
680
 
  Note-> Unlike MySQL, we do not, on purpose, delete files that do not match any engines. 
681
 
*/
682
 
void StorageEngine::removeLostTemporaryTables(Session &session, const char *directory)
683
 
{
684
 
  CachedDirectory dir(directory, set_of_table_definition_ext);
685
 
  TableIdentifier::vector table_identifiers;
686
 
 
687
 
  if (dir.fail())
688
 
  {
689
 
    errno= dir.getError();
690
 
    my_error(ER_CANT_READ_DIR, MYF(0), directory, errno);
691
 
 
692
 
    return;
693
 
  }
694
 
 
695
 
  CachedDirectory::Entries files= dir.getEntries();
696
 
 
697
 
  for (CachedDirectory::Entries::iterator fileIter= files.begin();
698
 
       fileIter != files.end(); fileIter++)
699
 
  {
700
 
    size_t length;
701
 
    std::string path;
702
 
    CachedDirectory::Entry *entry= *fileIter;
703
 
 
704
 
    /* We remove the file extension. */
705
 
    length= entry->filename.length();
706
 
    entry->filename.resize(length - DEFAULT_DEFINITION_FILE_EXT.length());
707
 
 
708
 
    path+= directory;
709
 
    path+= FN_LIBCHAR;
710
 
    path+= entry->filename;
711
 
    message::Table definition;
712
 
    if (StorageEngine::readTableFile(path, definition))
713
 
    {
714
 
      TableIdentifier identifier(definition.schema(), definition.name(), path);
715
 
      table_identifiers.push_back(identifier);
716
 
    }
717
 
  }
718
 
 
719
 
  std::for_each(vector_of_engines.begin(), vector_of_engines.end(),
720
 
                DropTables(session, table_identifiers));
 
769
 
 
770
const string ha_resolve_storage_engine_name(const StorageEngine *engine)
 
771
{
 
772
  return engine == NULL ? string("UNKNOWN") : engine->getName();
 
773
}
 
774
 
 
775
const char *StorageEngine::checkLowercaseNames(const char *path, char *tmp_path)
 
776
{
 
777
  if (flags.test(HTON_BIT_FILE_BASED))
 
778
    return path;
 
779
 
 
780
  /* Ensure that table handler get path in lower case */
 
781
  if (tmp_path != path)
 
782
    strcpy(tmp_path, path);
721
783
 
722
784
  /*
723
 
    Now we just clean up anything that might left over.
724
 
 
725
 
    We rescan because some of what might have been there should
726
 
    now be all nice and cleaned up.
 
785
    we only should turn into lowercase database/table part
 
786
    so start the process after homedirectory
727
787
  */
728
 
  std::set<std::string> all_exts= set_of_table_definition_ext;
729
 
 
730
 
  for (EngineVector::iterator iter= vector_of_engines.begin();
731
 
       iter != vector_of_engines.end() ; iter++)
732
 
  {
733
 
    for (const char **ext= (*iter)->bas_ext(); *ext ; ext++)
734
 
      all_exts.insert(*ext);
735
 
  }
736
 
 
737
 
  CachedDirectory rescan(directory, all_exts);
738
 
 
739
 
  files= rescan.getEntries();
740
 
  for (CachedDirectory::Entries::iterator fileIter= files.begin();
741
 
       fileIter != files.end(); fileIter++)
742
 
  {
743
 
    std::string path;
744
 
    CachedDirectory::Entry *entry= *fileIter;
745
 
 
746
 
    path+= directory;
747
 
    path+= FN_LIBCHAR;
748
 
    path+= entry->filename;
749
 
 
750
 
    unlink(path.c_str());
751
 
  }
752
 
}
753
 
 
754
 
 
755
 
/**
756
 
  Print error that we got from Cursor function.
757
 
 
758
 
  @note
759
 
    In case of delete table it's only safe to use the following parts of
760
 
    the 'table' structure:
761
 
    - table->getShare()->path
762
 
    - table->alias
763
 
*/
764
 
void StorageEngine::print_error(int error, myf errflag, Table &table)
765
 
{
766
 
  print_error(error, errflag, &table);
767
 
}
768
 
 
769
 
void StorageEngine::print_error(int error, myf errflag, Table *table)
770
 
{
771
 
  int textno= ER_GET_ERRNO;
772
 
  switch (error) {
773
 
  case EACCES:
774
 
    textno=ER_OPEN_AS_READONLY;
775
 
    break;
776
 
  case EAGAIN:
777
 
    textno=ER_FILE_USED;
778
 
    break;
779
 
  case ENOENT:
780
 
    textno=ER_FILE_NOT_FOUND;
781
 
    break;
782
 
  case HA_ERR_KEY_NOT_FOUND:
783
 
  case HA_ERR_NO_ACTIVE_RECORD:
784
 
  case HA_ERR_END_OF_FILE:
785
 
    textno=ER_KEY_NOT_FOUND;
786
 
    break;
787
 
  case HA_ERR_WRONG_MRG_TABLE_DEF:
788
 
    textno=ER_WRONG_MRG_TABLE;
789
 
    break;
790
 
  case HA_ERR_FOUND_DUPP_KEY:
791
 
  {
792
 
    assert(table);
793
 
    uint32_t key_nr= table->get_dup_key(error);
794
 
    if ((int) key_nr >= 0)
795
 
    {
796
 
      const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
797
 
 
798
 
      print_keydup_error(key_nr, err_msg, *table);
799
 
 
800
 
      return;
801
 
    }
802
 
    textno=ER_DUP_KEY;
803
 
    break;
804
 
  }
805
 
  case HA_ERR_FOREIGN_DUPLICATE_KEY:
806
 
  {
807
 
    assert(table);
808
 
    uint32_t key_nr= table->get_dup_key(error);
809
 
    if ((int) key_nr >= 0)
810
 
    {
811
 
      uint32_t max_length;
812
 
 
813
 
      /* Write the key in the error message */
814
 
      char key[MAX_KEY_LENGTH];
815
 
      String str(key,sizeof(key),system_charset_info);
816
 
 
817
 
      /* Table is opened and defined at this point */
818
 
      key_unpack(&str,table,(uint32_t) key_nr);
819
 
      max_length= (DRIZZLE_ERRMSG_SIZE-
820
 
                   (uint32_t) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
821
 
      if (str.length() >= max_length)
822
 
      {
823
 
        str.length(max_length-4);
824
 
        str.append(STRING_WITH_LEN("..."));
825
 
      }
826
 
      my_error(ER_FOREIGN_DUPLICATE_KEY, MYF(0), table->getShare()->getTableName(),
827
 
        str.c_ptr(), key_nr+1);
828
 
      return;
829
 
    }
830
 
    textno= ER_DUP_KEY;
831
 
    break;
832
 
  }
833
 
  case HA_ERR_FOUND_DUPP_UNIQUE:
834
 
    textno=ER_DUP_UNIQUE;
835
 
    break;
836
 
  case HA_ERR_RECORD_CHANGED:
837
 
    textno=ER_CHECKREAD;
838
 
    break;
839
 
  case HA_ERR_CRASHED:
840
 
    textno=ER_NOT_KEYFILE;
841
 
    break;
842
 
  case HA_ERR_WRONG_IN_RECORD:
843
 
    textno= ER_CRASHED_ON_USAGE;
844
 
    break;
845
 
  case HA_ERR_CRASHED_ON_USAGE:
846
 
    textno=ER_CRASHED_ON_USAGE;
847
 
    break;
848
 
  case HA_ERR_NOT_A_TABLE:
849
 
    textno= error;
850
 
    break;
851
 
  case HA_ERR_CRASHED_ON_REPAIR:
852
 
    textno=ER_CRASHED_ON_REPAIR;
853
 
    break;
854
 
  case HA_ERR_OUT_OF_MEM:
855
 
    textno=ER_OUT_OF_RESOURCES;
856
 
    break;
857
 
  case HA_ERR_WRONG_COMMAND:
858
 
    textno=ER_ILLEGAL_HA;
859
 
    break;
860
 
  case HA_ERR_OLD_FILE:
861
 
    textno=ER_OLD_KEYFILE;
862
 
    break;
863
 
  case HA_ERR_UNSUPPORTED:
864
 
    textno=ER_UNSUPPORTED_EXTENSION;
865
 
    break;
866
 
  case HA_ERR_RECORD_FILE_FULL:
867
 
  case HA_ERR_INDEX_FILE_FULL:
868
 
    textno=ER_RECORD_FILE_FULL;
869
 
    break;
870
 
  case HA_ERR_LOCK_WAIT_TIMEOUT:
871
 
    textno=ER_LOCK_WAIT_TIMEOUT;
872
 
    break;
873
 
  case HA_ERR_LOCK_TABLE_FULL:
874
 
    textno=ER_LOCK_TABLE_FULL;
875
 
    break;
876
 
  case HA_ERR_LOCK_DEADLOCK:
877
 
    textno=ER_LOCK_DEADLOCK;
878
 
    break;
879
 
  case HA_ERR_READ_ONLY_TRANSACTION:
880
 
    textno=ER_READ_ONLY_TRANSACTION;
881
 
    break;
882
 
  case HA_ERR_CANNOT_ADD_FOREIGN:
883
 
    textno=ER_CANNOT_ADD_FOREIGN;
884
 
    break;
885
 
  case HA_ERR_ROW_IS_REFERENCED:
886
 
  {
887
 
    String str;
888
 
    get_error_message(error, &str);
889
 
    my_error(ER_ROW_IS_REFERENCED_2, MYF(0), str.c_ptr_safe());
890
 
    return;
891
 
  }
892
 
  case HA_ERR_NO_REFERENCED_ROW:
893
 
  {
894
 
    String str;
895
 
    get_error_message(error, &str);
896
 
    my_error(ER_NO_REFERENCED_ROW_2, MYF(0), str.c_ptr_safe());
897
 
    return;
898
 
  }
899
 
  case HA_ERR_TABLE_DEF_CHANGED:
900
 
    textno=ER_TABLE_DEF_CHANGED;
901
 
    break;
902
 
  case HA_ERR_NO_SUCH_TABLE:
903
 
    assert(table);
904
 
    my_error(ER_NO_SUCH_TABLE, MYF(0), table->getShare()->getSchemaName(),
905
 
             table->getShare()->getTableName());
906
 
    return;
907
 
  case HA_ERR_RBR_LOGGING_FAILED:
908
 
    textno= ER_BINLOG_ROW_LOGGING_FAILED;
909
 
    break;
910
 
  case HA_ERR_DROP_INDEX_FK:
911
 
  {
912
 
    assert(table);
913
 
    const char *ptr= "???";
914
 
    uint32_t key_nr= table->get_dup_key(error);
915
 
    if ((int) key_nr >= 0)
916
 
      ptr= table->key_info[key_nr].name;
917
 
    my_error(ER_DROP_INDEX_FK, MYF(0), ptr);
918
 
    return;
919
 
  }
920
 
  case HA_ERR_TABLE_NEEDS_UPGRADE:
921
 
    textno=ER_TABLE_NEEDS_UPGRADE;
922
 
    break;
923
 
  case HA_ERR_TABLE_READONLY:
924
 
    textno= ER_OPEN_AS_READONLY;
925
 
    break;
926
 
  case HA_ERR_AUTOINC_READ_FAILED:
927
 
    textno= ER_AUTOINC_READ_FAILED;
928
 
    break;
929
 
  case HA_ERR_AUTOINC_ERANGE:
930
 
    textno= ER_WARN_DATA_OUT_OF_RANGE;
931
 
    break;
932
 
  case HA_ERR_LOCK_OR_ACTIVE_TRANSACTION:
933
 
    my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
934
 
               ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
935
 
    return;
936
 
  default:
937
 
    {
938
 
      /* 
939
 
        The error was "unknown" to this function.
940
 
        Ask Cursor if it has got a message for this error 
941
 
      */
942
 
      bool temporary= false;
943
 
      String str;
944
 
      temporary= get_error_message(error, &str);
945
 
      if (!str.is_empty())
946
 
      {
947
 
        const char* engine_name= getName().c_str();
948
 
        if (temporary)
949
 
          my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(),
950
 
                   engine_name);
951
 
        else
952
 
          my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine_name);
953
 
      }
954
 
      else
955
 
      {
956
 
              my_error(ER_GET_ERRNO,errflag,error);
957
 
      }
958
 
      return;
959
 
    }
960
 
  }
961
 
  my_error(textno, errflag, table->getShare()->getTableName(), error);
962
 
}
963
 
 
964
 
 
965
 
/**
966
 
  Return an error message specific to this Cursor.
967
 
 
968
 
  @param error  error code previously returned by Cursor
969
 
  @param buf    pointer to String where to add error message
970
 
 
971
 
  @return
972
 
    Returns true if this is a temporary error
973
 
*/
974
 
bool StorageEngine::get_error_message(int , String* )
975
 
{
976
 
  return false;
977
 
}
978
 
 
979
 
 
980
 
void StorageEngine::print_keydup_error(uint32_t key_nr, const char *msg, Table &table)
981
 
{
982
 
  /* Write the duplicated key in the error message */
983
 
  char key[MAX_KEY_LENGTH];
984
 
  String str(key,sizeof(key),system_charset_info);
985
 
 
986
 
  if (key_nr == MAX_KEY)
987
 
  {
988
 
    /* Key is unknown */
989
 
    str.copy("", 0, system_charset_info);
990
 
    my_printf_error(ER_DUP_ENTRY, msg, MYF(0), str.c_ptr(), "*UNKNOWN*");
991
 
  }
992
 
  else
993
 
  {
994
 
    /* Table is opened and defined at this point */
995
 
    key_unpack(&str, &table, (uint32_t) key_nr);
996
 
    uint32_t max_length=DRIZZLE_ERRMSG_SIZE-(uint32_t) strlen(msg);
997
 
    if (str.length() >= max_length)
998
 
    {
999
 
      str.length(max_length-4);
1000
 
      str.append(STRING_WITH_LEN("..."));
1001
 
    }
1002
 
    my_printf_error(ER_DUP_ENTRY, msg,
1003
 
                    MYF(0), str.c_ptr(), table.key_info[key_nr].name);
1004
 
  }
1005
 
}
1006
 
 
1007
 
 
1008
 
int StorageEngine::deleteDefinitionFromPath(const TableIdentifier &identifier)
1009
 
{
1010
 
  std::string path(identifier.getPath());
1011
 
 
1012
 
  path.append(DEFAULT_DEFINITION_FILE_EXT);
1013
 
 
1014
 
  return internal::my_delete(path.c_str(), MYF(0));
1015
 
}
1016
 
 
1017
 
int StorageEngine::renameDefinitionFromPath(const TableIdentifier &dest, const TableIdentifier &src)
1018
 
{
1019
 
  message::Table table_message;
1020
 
  std::string src_path(src.getPath());
1021
 
  std::string dest_path(dest.getPath());
1022
 
 
1023
 
  src_path.append(DEFAULT_DEFINITION_FILE_EXT);
1024
 
  dest_path.append(DEFAULT_DEFINITION_FILE_EXT);
1025
 
 
1026
 
  bool was_read= StorageEngine::readTableFile(src_path.c_str(), table_message);
1027
 
 
1028
 
  if (not was_read)
1029
 
  {
1030
 
    return ENOENT;
1031
 
  }
1032
 
 
1033
 
  dest.copyToTableMessage(table_message);
1034
 
 
1035
 
  int error= StorageEngine::writeDefinitionFromPath(dest, table_message);
1036
 
 
1037
 
  if (not error)
1038
 
  {
1039
 
    if (unlink(src_path.c_str()))
1040
 
      perror(src_path.c_str());
1041
 
  }
1042
 
 
1043
 
  return error;
1044
 
}
1045
 
 
1046
 
int StorageEngine::writeDefinitionFromPath(const TableIdentifier &identifier, message::Table &table_message)
1047
 
{
1048
 
  char definition_file_tmp[FN_REFLEN];
1049
 
  std::string file_name(identifier.getPath());
1050
 
 
1051
 
  file_name.append(DEFAULT_DEFINITION_FILE_EXT);
1052
 
 
1053
 
  snprintf(definition_file_tmp, sizeof(definition_file_tmp), "%sXXXXXX", file_name.c_str());
1054
 
 
1055
 
  int fd= mkstemp(definition_file_tmp);
1056
 
 
1057
 
  if (fd == -1)
1058
 
  {
1059
 
    perror(definition_file_tmp);
1060
 
    return errno;
1061
 
  }
1062
 
 
1063
 
  google::protobuf::io::ZeroCopyOutputStream* output=
1064
 
    new google::protobuf::io::FileOutputStream(fd);
1065
 
 
1066
 
  bool success;
1067
 
 
1068
 
  try
1069
 
  {
1070
 
    success= table_message.SerializeToZeroCopyStream(output);
1071
 
  }
1072
 
  catch (...)
1073
 
  {
1074
 
    success= false;
1075
 
  }
1076
 
 
1077
 
  if (not success)
1078
 
  {
1079
 
    my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
1080
 
             table_message.InitializationErrorString().c_str());
1081
 
    delete output;
1082
 
 
1083
 
    if (close(fd) == -1)
1084
 
      perror(definition_file_tmp);
1085
 
 
1086
 
    if (unlink(definition_file_tmp) == -1)
1087
 
      perror(definition_file_tmp);
1088
 
 
1089
 
    return ER_CORRUPT_TABLE_DEFINITION;
1090
 
  }
1091
 
 
1092
 
  delete output;
1093
 
 
1094
 
  if (close(fd) == -1)
1095
 
  {
1096
 
    int error= errno;
1097
 
    perror(definition_file_tmp);
1098
 
 
1099
 
    if (unlink(definition_file_tmp))
1100
 
      perror(definition_file_tmp);
1101
 
 
1102
 
    return error;
1103
 
  }
1104
 
 
1105
 
  if (rename(definition_file_tmp, file_name.c_str()) == -1)
1106
 
  {
1107
 
    int error= errno;
1108
 
    perror(definition_file_tmp);
1109
 
 
1110
 
    if (unlink(definition_file_tmp))
1111
 
      perror(definition_file_tmp);
1112
 
 
1113
 
    return error;
1114
 
  }
1115
 
 
1116
 
  return 0;
1117
 
}
1118
 
 
1119
 
class CanCreateTable: public std::unary_function<StorageEngine *, bool>
1120
 
{
1121
 
  const TableIdentifier &identifier;
 
788
  my_casedn_str(files_charset_info, tmp_path + drizzle_data_home_len);
 
789
 
 
790
  return tmp_path;
 
791
}
 
792
 
 
793
class DFETableNameIterator: public TableNameIteratorImplementation
 
794
{
 
795
private:
 
796
  MY_DIR *dirp;
 
797
  uint32_t current_entry;
1122
798
 
1123
799
public:
1124
 
  CanCreateTable(const TableIdentifier &identifier_arg) :
1125
 
    identifier(identifier_arg)
1126
 
  { }
1127
 
 
1128
 
  result_type operator() (argument_type engine)
1129
 
  {
1130
 
    return not engine->doCanCreateTable(identifier);
1131
 
  }
 
800
  DFETableNameIterator(const std::string &database)
 
801
  : TableNameIteratorImplementation(database),
 
802
    dirp(NULL),
 
803
    current_entry(-1)
 
804
    {};
 
805
 
 
806
  ~DFETableNameIterator();
 
807
 
 
808
  int next(std::string *name);
 
809
 
1132
810
};
1133
811
 
1134
 
 
1135
 
/**
1136
 
  @note on success table can be created.
1137
 
*/
1138
 
bool StorageEngine::canCreateTable(const TableIdentifier &identifier)
1139
 
{
1140
 
  EngineVector::iterator iter=
1141
 
    std::find_if(vector_of_engines.begin(), vector_of_engines.end(),
1142
 
                 CanCreateTable(identifier));
1143
 
 
1144
 
  if (iter == vector_of_engines.end())
1145
 
  {
1146
 
    return true;
1147
 
  }
1148
 
 
1149
 
  return false;
1150
 
}
1151
 
 
1152
 
bool StorageEngine::readTableFile(const std::string &path, message::Table &table_message)
1153
 
{
1154
 
  std::fstream input(path.c_str(), std::ios::in | std::ios::binary);
1155
 
 
1156
 
  if (input.good())
1157
 
  {
1158
 
    try {
1159
 
      if (table_message.ParseFromIstream(&input))
1160
 
      {
1161
 
        return true;
1162
 
      }
1163
 
    }
1164
 
    catch (...)
1165
 
    {
1166
 
      my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
1167
 
               table_message.InitializationErrorString().empty() ? "": table_message.InitializationErrorString().c_str());
1168
 
    }
1169
 
  }
1170
 
  else
1171
 
  {
1172
 
    perror(path.c_str());
1173
 
  }
1174
 
 
1175
 
  return false;
1176
 
}
1177
 
 
1178
 
 
1179
 
 
1180
 
} /* namespace plugin */
1181
 
} /* namespace drizzled */
 
812
DFETableNameIterator::~DFETableNameIterator()
 
813
{
 
814
  if (dirp)
 
815
    my_dirend(dirp);
 
816
}
 
817
 
 
818
int DFETableNameIterator::next(string *name)
 
819
{
 
820
  char uname[NAME_LEN + 1];
 
821
  FILEINFO *file;
 
822
  char *ext;
 
823
  uint32_t file_name_len;
 
824
  const char *wild= NULL;
 
825
 
 
826
  if (dirp == NULL)
 
827
  {
 
828
    bool dir= false;
 
829
    char path[FN_REFLEN];
 
830
 
 
831
    build_table_filename(path, sizeof(path), db.c_str(), "", false);
 
832
 
 
833
    dirp = my_dir(path,MYF(dir ? MY_WANT_STAT : 0));
 
834
 
 
835
    if (dirp == NULL)
 
836
    {
 
837
      if (my_errno == ENOENT)
 
838
        my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db.c_str());
 
839
      else
 
840
        my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
 
841
      return(ENOENT);
 
842
    }
 
843
    current_entry= -1;
 
844
  }
 
845
 
 
846
  while(true)
 
847
  {
 
848
    current_entry++;
 
849
 
 
850
    if (current_entry == dirp->number_off_files)
 
851
    {
 
852
      my_dirend(dirp);
 
853
      dirp= NULL;
 
854
      return -1;
 
855
    }
 
856
 
 
857
    file= dirp->dir_entry + current_entry;
 
858
 
 
859
    if (my_strcasecmp(system_charset_info, ext=fn_rext(file->name),".dfe") ||
 
860
        is_prefix(file->name, TMP_FILE_PREFIX))
 
861
      continue;
 
862
    *ext=0;
 
863
 
 
864
    file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
 
865
 
 
866
    uname[file_name_len]= '\0';
 
867
 
 
868
    if (wild && wild_compare(uname, wild, 0))
 
869
      continue;
 
870
 
 
871
    if (name)
 
872
      name->assign(uname);
 
873
 
 
874
    return 0;
 
875
  }
 
876
}
 
877
 
 
878
TableNameIterator::TableNameIterator(const std::string &db)
 
879
  : current_implementation(NULL), database(db)
 
880
{
 
881
  engine_iter= all_engines.begin();
 
882
  default_implementation= new DFETableNameIterator(database);
 
883
}
 
884
 
 
885
TableNameIterator::~TableNameIterator()
 
886
{
 
887
  delete current_implementation;
 
888
}
 
889
 
 
890
int TableNameIterator::next(std::string *name)
 
891
{
 
892
  int err= 0;
 
893
 
 
894
next:
 
895
  if (current_implementation == NULL)
 
896
  {
 
897
    while(current_implementation == NULL && engine_iter != all_engines.end())
 
898
    {
 
899
      StorageEngine *engine= *engine_iter;
 
900
      current_implementation= engine->tableNameIterator(database);
 
901
      engine_iter++;
 
902
    }
 
903
 
 
904
    if (current_implementation == NULL && engine_iter == all_engines.end())
 
905
    {
 
906
      current_implementation= default_implementation;
 
907
    }
 
908
  }
 
909
 
 
910
  err= current_implementation->next(name);
 
911
 
 
912
  if (err == -1)
 
913
  {
 
914
    if (current_implementation != default_implementation)
 
915
    {
 
916
      delete current_implementation;
 
917
      current_implementation= NULL;
 
918
      goto next;
 
919
    }
 
920
  }
 
921
 
 
922
  return err;
 
923
}
 
924