~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/plugin/storage_engine.cc

  • Committer: Monty Taylor
  • Date: 2009-10-06 19:40:45 UTC
  • mto: This revision was merged to the branch mainline in revision 1184.
  • Revision ID: mordred@inaugust.com-20091006194045-ojptaq2sx6ck6q63
No more server_includes.h in headers.

Show diffs side-by-side

added added

removed removed

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