~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/plugin/storage_engine.cc

  • Committer: Padraig O'Sullivan
  • Date: 2009-08-08 04:22:33 UTC
  • mto: (1115.3.4 captain)
  • mto: This revision was merged to the branch mainline in revision 1117.
  • Revision ID: osullivan.padraig@gmail.com-20090808042233-q0z88zc490z3f3r7
Renamed the Command class to be Statement. Renamed the command directory to
statement and also the command header file to statement. Updated various
source files to reflect this renaming.

Show diffs side-by-side

added added

removed removed

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