~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/plugin/storage_engine.cc

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

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