~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/plugin/storage_engine.cc

  • Committer: Stewart Smith
  • Date: 2009-10-08 12:39:27 UTC
  • mto: This revision was merged to the branch mainline in revision 1179.
  • Revision ID: stewart@flamingspork.com-20091008123927-qpf9hog04w4xc5aj
make directory_file_name() static to mysys/my_lib.cc

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