~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/archive/ha_archive.cc

  • Committer: Brian Aker
  • Date: 2009-11-18 22:58:22 UTC
  • mto: (1223.1.1 push) (1226.1.2 push)
  • mto: This revision was merged to the branch mainline in revision 1224.
  • Revision ID: brian@gaz-20091118225822-4ryr9rviir23o0kr
Second pass through bugs related to CREATE TABLE LIKE

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* Copyright (C) 2003 MySQL AB
2
 
   Copyright (C) 2010 Brian Aker
3
2
 
4
3
  This program is free software; you can redistribute it and/or modify
5
4
  it under the terms of the GNU General Public License as published by
12
11
 
13
12
  You should have received a copy of the GNU General Public License
14
13
  along with this program; if not, write to the Free Software
15
 
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
16
 
 
17
 
 
18
 
#include "config.h"
19
 
 
20
 
#include "plugin/archive/archive_engine.h"
21
 
#include <memory>
22
 
#include <boost/scoped_ptr.hpp>
 
14
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
15
 
 
16
 
 
17
#include "drizzled/server_includes.h"
 
18
#include "drizzled/field.h"
 
19
#include "drizzled/field/blob.h"
 
20
#include "drizzled/field/timestamp.h"
 
21
#include "plugin/myisam/myisam.h"
 
22
#include "drizzled/table.h"
 
23
#include "drizzled/session.h"
 
24
#include <mysys/my_dir.h>
 
25
 
 
26
#include "ha_archive.h"
 
27
 
 
28
#include <stdio.h>
 
29
#include <string>
 
30
#include <map>
23
31
 
24
32
using namespace std;
25
 
using namespace drizzled;
26
33
 
 
34
static const string engine_name("ARCHIVE");
27
35
 
28
36
/*
29
37
  First, if you want to understand storage engines you should look at
93
101
    -Brian
94
102
*/
95
103
 
96
 
/* When the engine starts up set the first version */
97
 
static uint64_t global_version= 1;
98
 
 
99
 
// We use this to find out the state of the archive aio option.
100
 
extern bool archive_aio_state(void);
 
104
/* Variables for archive share methods */
 
105
pthread_mutex_t archive_mutex= PTHREAD_MUTEX_INITIALIZER;
 
106
 
 
107
std::map<const char *, ArchiveShare *> archive_open_tables;
 
108
 
 
109
static unsigned int global_version;
 
110
 
 
111
/* The file extension */
 
112
#define ARZ ".arz"               // The data file
 
113
#define ARN ".ARN"               // Files used during an optimize call
 
114
 
 
115
 
 
116
 
 
117
static bool archive_use_aio= false;
101
118
 
102
119
/*
103
120
  Number of rows that will force a bulk insert.
109
126
*/
110
127
#define ARCHIVE_ROW_HEADER_SIZE 4
111
128
 
112
 
ArchiveShare *ArchiveEngine::findOpenTable(const string table_name)
113
 
{
114
 
  ArchiveMap::iterator find_iter=
115
 
    archive_open_tables.find(table_name);
116
 
 
117
 
  if (find_iter != archive_open_tables.end())
118
 
    return (*find_iter).second;
119
 
  else
120
 
    return NULL;
121
 
}
122
 
 
123
 
void ArchiveEngine::addOpenTable(const string &table_name, ArchiveShare *share)
124
 
{
125
 
  archive_open_tables[table_name]= share;
126
 
}
127
 
 
128
 
void ArchiveEngine::deleteOpenTable(const string &table_name)
129
 
{
130
 
  archive_open_tables.erase(table_name);
131
 
}
132
 
 
133
 
 
134
 
int ArchiveEngine::doDropTable(Session&, const TableIdentifier &identifier)
135
 
{
136
 
  string new_path(identifier.getPath());
 
129
/*
 
130
  We just implement one additional file extension.
 
131
*/
 
132
static const char *ha_archive_exts[] = {
 
133
  ARZ,
 
134
  NULL
 
135
};
 
136
 
 
137
class ArchiveEngine : public drizzled::plugin::StorageEngine
 
138
{
 
139
public:
 
140
  ArchiveEngine(const string &name_arg)
 
141
   : drizzled::plugin::StorageEngine(name_arg,
 
142
                                     HTON_FILE_BASED
 
143
                                      | HTON_HAS_DATA_DICTIONARY) 
 
144
  {
 
145
    table_definition_ext= ARZ;
 
146
  }
 
147
 
 
148
  uint64_t table_flags() const
 
149
  {
 
150
    return (HA_NO_TRANSACTIONS |
 
151
            HA_STATS_RECORDS_IS_EXACT |
 
152
            HA_HAS_RECORDS);
 
153
  }
 
154
 
 
155
  virtual Cursor *create(TableShare &table,
 
156
                          MEM_ROOT *mem_root)
 
157
  {
 
158
    return new (mem_root) ha_archive(*this, table);
 
159
  }
 
160
 
 
161
  const char **bas_ext() const {
 
162
    return ha_archive_exts;
 
163
  }
 
164
 
 
165
  int doCreateTable(Session *session, const char *table_name,
 
166
                    Table& table_arg, HA_CREATE_INFO& create_info,
 
167
                    drizzled::message::Table& proto);
 
168
 
 
169
  int doGetTableDefinition(Session& session,
 
170
                           const char* path,
 
171
                           const char *db,
 
172
                           const char *table_name,
 
173
                           const bool is_tmp,
 
174
                           drizzled::message::Table *table_proto);
 
175
 
 
176
  void doGetTableNames(CachedDirectory &directory, string& , set<string>& set_of_names);
 
177
 
 
178
  int doDropTable(Session&, const string table_path);
 
179
};
 
180
 
 
181
 
 
182
void ArchiveEngine::doGetTableNames(CachedDirectory &directory, 
 
183
                                    string&, 
 
184
                                    set<string>& set_of_names)
 
185
{
 
186
  CachedDirectory::Entries entries= directory.getEntries();
 
187
 
 
188
  for (CachedDirectory::Entries::iterator entry_iter= entries.begin(); 
 
189
       entry_iter != entries.end(); ++entry_iter)
 
190
  {
 
191
    CachedDirectory::Entry *entry= *entry_iter;
 
192
    string *filename= &entry->filename;
 
193
 
 
194
    assert(filename->size());
 
195
 
 
196
    const char *ext= strchr(filename->c_str(), '.');
 
197
 
 
198
    if (ext == NULL || my_strcasecmp(system_charset_info, ext, ARZ) ||
 
199
        is_prefix(filename->c_str(), TMP_FILE_PREFIX))
 
200
    {  }
 
201
    else
 
202
    {
 
203
      char uname[NAME_LEN + 1];
 
204
      uint32_t file_name_len;
 
205
 
 
206
      file_name_len= filename_to_tablename(filename->c_str(), uname, sizeof(uname));
 
207
      // TODO: Remove need for memory copy here
 
208
      uname[file_name_len - sizeof(ARZ) + 1]= '\0'; // Subtract ending, place NULL 
 
209
      set_of_names.insert(uname);
 
210
    }
 
211
  }
 
212
}
 
213
 
 
214
 
 
215
int ArchiveEngine::doDropTable(Session&,
 
216
                               const string table_path)
 
217
{
 
218
  string new_path(table_path);
137
219
 
138
220
  new_path+= ARZ;
139
221
 
141
223
 
142
224
  if (error != 0)
143
225
  {
144
 
    error= errno= errno;
 
226
    error= my_errno= errno;
145
227
  }
146
228
 
147
229
  return error;
148
230
}
149
231
 
150
232
int ArchiveEngine::doGetTableDefinition(Session&,
151
 
                                        const TableIdentifier &identifier,
152
 
                                        drizzled::message::Table &table_proto)
 
233
                                        const char* path,
 
234
                                        const char *,
 
235
                                        const char *,
 
236
                                        const bool,
 
237
                                        drizzled::message::Table *table_proto)
153
238
{
154
239
  struct stat stat_info;
155
240
  int error= ENOENT;
156
241
  string proto_path;
157
242
 
158
243
  proto_path.reserve(FN_REFLEN);
159
 
  proto_path.assign(identifier.getPath());
 
244
  proto_path.assign(path);
160
245
 
161
246
  proto_path.append(ARZ);
162
247
 
165
250
  else
166
251
    error= EEXIST;
167
252
 
 
253
  if (table_proto)
168
254
  {
169
 
    boost::scoped_ptr<azio_stream> proto_stream(new azio_stream);
 
255
    azio_stream proto_stream;
170
256
    char* proto_string;
171
 
    if (azopen(proto_stream.get(), proto_path.c_str(), O_RDONLY, AZ_METHOD_BLOCK) == 0)
 
257
    if (azopen(&proto_stream, proto_path.c_str(), O_RDONLY, AZ_METHOD_BLOCK) == 0)
172
258
      return HA_ERR_CRASHED_ON_USAGE;
173
259
 
174
 
    proto_string= (char*)malloc(sizeof(char) * proto_stream->frm_length);
 
260
    proto_string= (char*)malloc(sizeof(char) * proto_stream.frm_length);
175
261
    if (proto_string == NULL)
176
262
    {
177
 
      azclose(proto_stream.get());
 
263
      azclose(&proto_stream);
178
264
      return ENOMEM;
179
265
    }
180
266
 
181
 
    azread_frm(proto_stream.get(), proto_string);
 
267
    azread_frm(&proto_stream, proto_string);
182
268
 
183
 
    if (table_proto.ParseFromArray(proto_string, proto_stream->frm_length) == false)
 
269
    if (table_proto->ParseFromArray(proto_string, proto_stream.frm_length) == false)
184
270
      error= HA_ERR_CRASHED_ON_USAGE;
185
271
 
186
 
    azclose(proto_stream.get());
 
272
    azclose(&proto_stream);
187
273
    free(proto_string);
188
274
  }
189
275
 
190
 
  /* We set the name from what we've asked for as in RENAME TABLE for ARCHIVE
191
 
     we do not rewrite the table proto (as it's wedged in the file header)
192
 
  */
193
 
  table_proto.set_schema(identifier.getSchemaName());
194
 
  table_proto.set_name(identifier.getTableName());
195
 
 
196
276
  return error;
197
277
}
198
278
 
 
279
static ArchiveEngine *archive_engine= NULL;
 
280
 
 
281
/*
 
282
  Initialize the archive Cursor.
 
283
 
 
284
  SYNOPSIS
 
285
    archive_db_init()
 
286
    void *
 
287
 
 
288
  RETURN
 
289
    false       OK
 
290
    true        Error
 
291
*/
 
292
 
 
293
static int archive_db_init(drizzled::plugin::Registry &registry)
 
294
{
 
295
 
 
296
  pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST);
 
297
  archive_engine= new ArchiveEngine(engine_name);
 
298
  registry.add(archive_engine);
 
299
 
 
300
  /* When the engine starts up set the first version */
 
301
  global_version= 1;
 
302
 
 
303
  return false;
 
304
}
 
305
 
 
306
/*
 
307
  Release the archive Cursor.
 
308
 
 
309
  SYNOPSIS
 
310
    archive_db_done()
 
311
    void
 
312
 
 
313
  RETURN
 
314
    false       OK
 
315
*/
 
316
 
 
317
static int archive_db_done(drizzled::plugin::Registry &registry)
 
318
{
 
319
  registry.remove(archive_engine);
 
320
  delete archive_engine;
 
321
 
 
322
  pthread_mutex_destroy(&archive_mutex);
 
323
 
 
324
  return 0;
 
325
}
 
326
 
199
327
 
200
328
ha_archive::ha_archive(drizzled::plugin::StorageEngine &engine_arg,
201
 
                       Table &table_arg)
 
329
                       TableShare &table_arg)
202
330
  :Cursor(engine_arg, table_arg), delayed_insert(0), bulk_insert(0)
203
331
{
204
332
  /* Set our original buffer from pre-allocated memory */
205
333
  buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info);
206
334
 
207
335
  /* The size of the offset value we will use for position() */
208
 
  ref_length= sizeof(internal::my_off_t);
 
336
  ref_length= sizeof(my_off_t);
209
337
  archive_reader_open= false;
210
338
}
211
339
 
236
364
{
237
365
  memset(&archive_write, 0, sizeof(azio_stream));     /* Archive file we are working with */
238
366
  table_name.append(name);
239
 
  data_file_name.assign(table_name);
240
 
  data_file_name.append(ARZ);
 
367
  fn_format(data_file_name, table_name.c_str(), "",
 
368
            ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME);
241
369
  /*
242
370
    We will use this lock for rows.
243
371
  */
244
 
  pthread_mutex_init(&_mutex,MY_MUTEX_INIT_FAST);
 
372
  pthread_mutex_init(&mutex,MY_MUTEX_INIT_FAST);
245
373
}
246
374
 
247
375
ArchiveShare::~ArchiveShare()
248
376
{
249
 
  _lock.deinit();
250
 
  pthread_mutex_destroy(&_mutex);
 
377
  thr_lock_delete(&lock);
 
378
  pthread_mutex_destroy(&mutex);
251
379
  /*
252
380
    We need to make sure we don't reset the crashed state.
253
381
    If we open a crashed file, wee need to close it as crashed unless
261
389
 
262
390
bool ArchiveShare::prime(uint64_t *auto_increment)
263
391
{
264
 
  boost::scoped_ptr<azio_stream> archive_tmp(new azio_stream);
 
392
  azio_stream archive_tmp;
265
393
 
266
394
  /*
267
395
    We read the meta file, but do not mark it dirty. Since we are not
269
397
    anything but reading... open it for write and we will generate null
270
398
    compression writes).
271
399
  */
272
 
  if (!(azopen(archive_tmp.get(), data_file_name.c_str(), O_RDONLY,
 
400
  if (!(azopen(&archive_tmp, data_file_name, O_RDONLY,
273
401
               AZ_METHOD_BLOCK)))
274
402
    return false;
275
403
 
276
 
  *auto_increment= archive_tmp->auto_increment + 1;
277
 
  rows_recorded= (ha_rows)archive_tmp->rows;
278
 
  crashed= archive_tmp->dirty;
 
404
  *auto_increment= archive_tmp.auto_increment + 1;
 
405
  rows_recorded= (ha_rows)archive_tmp.rows;
 
406
  crashed= archive_tmp.dirty;
279
407
  if (version < global_version)
280
408
  {
281
409
    version_rows= rows_recorded;
282
410
    version= global_version;
283
411
  }
284
 
  azclose(archive_tmp.get());
 
412
  azclose(&archive_tmp);
285
413
 
286
414
  return true;
287
415
}
296
424
*/
297
425
ArchiveShare *ha_archive::get_share(const char *table_name, int *rc)
298
426
{
299
 
  ArchiveEngine *a_engine= static_cast<ArchiveEngine *>(getEngine());
300
 
 
301
 
  pthread_mutex_lock(&a_engine->mutex());
302
 
 
303
 
  share= a_engine->findOpenTable(table_name);
 
427
  uint32_t length;
 
428
  map<const char *, ArchiveShare *> ::iterator find_iter;
 
429
 
 
430
  pthread_mutex_lock(&archive_mutex);
 
431
  length=(uint) strlen(table_name);
 
432
 
 
433
  find_iter= archive_open_tables.find(table_name);
 
434
 
 
435
  if (find_iter != archive_open_tables.end())
 
436
    share= (*find_iter).second;
 
437
  else
 
438
    share= NULL;
304
439
 
305
440
  if (!share)
306
441
  {
308
443
 
309
444
    if (share == NULL)
310
445
    {
311
 
      pthread_mutex_unlock(&a_engine->mutex());
 
446
      pthread_mutex_unlock(&archive_mutex);
312
447
      *rc= HA_ERR_OUT_OF_MEM;
313
448
      return(NULL);
314
449
    }
315
450
 
316
451
    if (share->prime(&stats.auto_increment_value) == false)
317
452
    {
318
 
      pthread_mutex_unlock(&a_engine->mutex());
 
453
      pthread_mutex_unlock(&archive_mutex);
319
454
      *rc= HA_ERR_CRASHED_ON_REPAIR;
320
455
      delete share;
321
456
 
322
457
      return NULL;
323
458
    }
324
459
 
325
 
    a_engine->addOpenTable(share->table_name, share);
326
 
    thr_lock_init(&share->_lock);
 
460
    archive_open_tables[share->table_name.c_str()]= share; 
 
461
    thr_lock_init(&share->lock);
327
462
  }
328
463
  share->use_count++;
329
 
 
330
464
  if (share->crashed)
331
465
    *rc= HA_ERR_CRASHED_ON_USAGE;
332
 
  pthread_mutex_unlock(&a_engine->mutex());
 
466
  pthread_mutex_unlock(&archive_mutex);
333
467
 
334
468
  return(share);
335
469
}
341
475
*/
342
476
int ha_archive::free_share()
343
477
{
344
 
  ArchiveEngine *a_engine= static_cast<ArchiveEngine *>(getEngine());
345
 
 
346
 
  pthread_mutex_lock(&a_engine->mutex());
 
478
  pthread_mutex_lock(&archive_mutex);
347
479
  if (!--share->use_count)
348
480
  {
349
 
    a_engine->deleteOpenTable(share->table_name);
 
481
    archive_open_tables.erase(share->table_name.c_str());
350
482
    delete share;
351
483
  }
352
 
  pthread_mutex_unlock(&a_engine->mutex());
 
484
  pthread_mutex_unlock(&archive_mutex);
353
485
 
354
486
  return 0;
355
487
}
361
493
    a gzip file that can be both read and written we keep a writer open
362
494
    that is shared amoung all open tables.
363
495
  */
364
 
  if (!(azopen(&(share->archive_write), share->data_file_name.c_str(),
 
496
  if (!(azopen(&(share->archive_write), share->data_file_name,
365
497
               O_RDWR, AZ_METHOD_BLOCK)))
366
498
  {
367
499
    share->crashed= true;
387
519
  {
388
520
    az_method method;
389
521
 
390
 
    if (archive_aio_state())
 
522
    switch (archive_use_aio)
391
523
    {
 
524
    case false:
 
525
      method= AZ_METHOD_BLOCK;
 
526
      break;
 
527
    case true:
392
528
      method= AZ_METHOD_AIO;
393
 
    }
394
 
    else
395
 
    {
 
529
      break;
 
530
    default:
396
531
      method= AZ_METHOD_BLOCK;
397
532
    }
398
 
    if (!(azopen(&archive, share->data_file_name.c_str(), O_RDONLY,
 
533
    if (!(azopen(&archive, share->data_file_name, O_RDONLY,
399
534
                 method)))
400
535
    {
401
536
      share->crashed= true;
413
548
  Init out lock.
414
549
  We open the file we will read from.
415
550
*/
416
 
int ha_archive::doOpen(const TableIdentifier &identifier, int , uint32_t )
 
551
int ha_archive::open(const char *name, int, uint32_t open_options)
417
552
{
418
553
  int rc= 0;
419
 
  share= get_share(identifier.getPath().c_str(), &rc);
420
 
 
421
 
  /** 
422
 
    We either fix it ourselves, or we just take it offline 
423
 
 
424
 
    @todo Create some documentation in the recovery tools shipped with the engine.
425
 
  */
426
 
  if (rc == HA_ERR_CRASHED_ON_USAGE)
 
554
  share= get_share(name, &rc);
 
555
 
 
556
  if (rc == HA_ERR_CRASHED_ON_USAGE && !(open_options & HA_OPEN_FOR_REPAIR))
427
557
  {
428
558
    free_share();
429
 
    rc= repair();
430
 
 
431
 
    return 0;
 
559
    return(rc);
432
560
  }
433
561
  else if (rc == HA_ERR_OUT_OF_MEM)
434
562
  {
437
565
 
438
566
  assert(share);
439
567
 
440
 
  record_buffer.resize(getTable()->getShare()->getRecordLength() + ARCHIVE_ROW_HEADER_SIZE);
441
 
 
442
 
  lock.init(&share->_lock);
443
 
 
444
 
  return(rc);
445
 
}
446
 
 
447
 
// Should never be called
448
 
int ha_archive::open(const char *, int, uint32_t)
449
 
{
450
 
  assert(0);
451
 
  return -1;
 
568
  record_buffer= create_record_buffer(table->s->reclength +
 
569
                                      ARCHIVE_ROW_HEADER_SIZE);
 
570
 
 
571
  if (!record_buffer)
 
572
  {
 
573
    free_share();
 
574
    return(HA_ERR_OUT_OF_MEM);
 
575
  }
 
576
 
 
577
  thr_lock_data_init(&share->lock, &lock, NULL);
 
578
 
 
579
  if (rc == HA_ERR_CRASHED_ON_USAGE && open_options & HA_OPEN_FOR_REPAIR)
 
580
  {
 
581
    return(0);
 
582
  }
 
583
  else
 
584
    return(rc);
452
585
}
453
586
 
454
587
 
473
606
{
474
607
  int rc= 0;
475
608
 
476
 
  record_buffer.clear();
 
609
  destroy_record_buffer(record_buffer);
477
610
 
478
611
  /* First close stream */
479
612
  if (archive_reader_open == true)
497
630
  of creation.
498
631
*/
499
632
 
500
 
int ArchiveEngine::doCreateTable(Session &,
 
633
int ArchiveEngine::doCreateTable(Session *,
 
634
                                 const char *table_name,
501
635
                                 Table& table_arg,
502
 
                                 const drizzled::TableIdentifier &identifier,
 
636
                                 HA_CREATE_INFO& create_info,
503
637
                                 drizzled::message::Table& proto)
504
638
{
 
639
  char name_buff[FN_REFLEN];
505
640
  int error= 0;
506
 
  boost::scoped_ptr<azio_stream> create_stream(new azio_stream);
 
641
  azio_stream create_stream;            /* Archive file we are working with */
507
642
  uint64_t auto_increment_value;
508
643
  string serialized_proto;
509
644
 
510
 
  auto_increment_value= proto.options().auto_increment_value();
 
645
  auto_increment_value= create_info.auto_increment_value;
511
646
 
512
647
  for (uint32_t key= 0; key < table_arg.sizeKeys(); key++)
513
648
  {
514
 
    KeyInfo *pos= &table_arg.key_info[key];
515
 
    KeyPartInfo *key_part=     pos->key_part;
516
 
    KeyPartInfo *key_part_end= key_part + pos->key_parts;
 
649
    KEY *pos= table_arg.key_info+key;
 
650
    KEY_PART_INFO *key_part=     pos->key_part;
 
651
    KEY_PART_INFO *key_part_end= key_part + pos->key_parts;
517
652
 
518
653
    for (; key_part != key_part_end; key_part++)
519
654
    {
521
656
 
522
657
      if (!(field->flags & AUTO_INCREMENT_FLAG))
523
658
      {
524
 
        return -1;
 
659
        error= -1;
 
660
        goto error;
525
661
      }
526
662
    }
527
663
  }
528
664
 
529
 
  std::string named_file= identifier.getPath();
530
 
  named_file.append(ARZ);
 
665
  /*
 
666
    We reuse name_buff since it is available.
 
667
  */
 
668
  fn_format(name_buff, table_name, "", ARZ,
 
669
            MY_REPLACE_EXT | MY_UNPACK_FILENAME);
531
670
 
532
 
  errno= 0;
533
 
  if (azopen(create_stream.get(), named_file.c_str(), O_CREAT|O_RDWR,
 
671
  my_errno= 0;
 
672
  if (azopen(&create_stream, name_buff, O_CREAT|O_RDWR,
534
673
             AZ_METHOD_BLOCK) == 0)
535
674
  {
536
675
    error= errno;
537
 
    unlink(named_file.c_str());
538
 
 
539
 
    return(error ? error : -1);
540
 
  }
541
 
 
542
 
  try {
543
 
    proto.SerializeToString(&serialized_proto);
544
 
  }
545
 
  catch (...)
546
 
  {
547
 
    unlink(named_file.c_str());
548
 
 
549
 
    return(error ? error : -1);
550
 
  }
551
 
 
552
 
  if (azwrite_frm(create_stream.get(), serialized_proto.c_str(),
 
676
    goto error2;
 
677
  }
 
678
 
 
679
  proto.SerializeToString(&serialized_proto);
 
680
 
 
681
  if (azwrite_frm(&create_stream, serialized_proto.c_str(),
553
682
                  serialized_proto.length()))
554
 
  {
555
 
    unlink(named_file.c_str());
556
 
 
557
 
    return(error ? error : -1);
558
 
  }
 
683
    goto error2;
559
684
 
560
685
  if (proto.options().has_comment())
561
686
  {
562
687
    int write_length;
563
688
 
564
 
    write_length= azwrite_comment(create_stream.get(),
 
689
    write_length= azwrite_comment(&create_stream,
565
690
                                  proto.options().comment().c_str(),
566
691
                                  proto.options().comment().length());
567
692
 
568
693
    if (write_length < 0)
569
694
    {
570
695
      error= errno;
571
 
      unlink(named_file.c_str());
572
 
 
573
 
      return(error ? error : -1);
 
696
      goto error2;
574
697
    }
575
698
  }
576
699
 
578
701
    Yes you need to do this, because the starting value
579
702
    for the autoincrement may not be zero.
580
703
  */
581
 
  create_stream->auto_increment= auto_increment_value ?
 
704
  create_stream.auto_increment= auto_increment_value ?
582
705
    auto_increment_value - 1 : 0;
583
706
 
584
 
  if (azclose(create_stream.get()))
 
707
  if (azclose(&create_stream))
585
708
  {
586
709
    error= errno;
587
 
    unlink(named_file.c_str());
588
 
 
589
 
    return(error ? error : -1);
 
710
    goto error2;
590
711
  }
591
712
 
592
713
  return(0);
 
714
 
 
715
error2:
 
716
  unlink(name_buff);
 
717
 
 
718
error:
 
719
  /* Return error number, if we got one */
 
720
  return(error ? error : -1);
593
721
}
594
722
 
595
723
/*
603
731
  /* We pack the row for writing */
604
732
  r_pack_length= pack_row(buf);
605
733
 
606
 
  written= azwrite_row(writer, &record_buffer[0], r_pack_length);
 
734
  written= azwrite_row(writer, record_buffer->buffer, r_pack_length);
607
735
  if (written != r_pack_length)
608
736
  {
609
737
    return(-1);
623
751
 
624
752
uint32_t ha_archive::max_row_length(const unsigned char *)
625
753
{
626
 
  uint32_t length= (uint32_t)(getTable()->getRecordLength() + getTable()->sizeFields()*2);
 
754
  uint32_t length= (uint32_t)(table->getRecordLength() + table->sizeFields()*2);
627
755
  length+= ARCHIVE_ROW_HEADER_SIZE;
628
756
 
629
757
  uint32_t *ptr, *end;
630
 
  for (ptr= getTable()->getBlobField(), end=ptr + getTable()->sizeBlobFields();
 
758
  for (ptr= table->getBlobField(), end=ptr + table->sizeBlobFields();
631
759
       ptr != end ;
632
760
       ptr++)
633
761
  {
634
 
      length += 2 + ((Field_blob*)getTable()->getField(*ptr))->get_length();
 
762
      length += 2 + ((Field_blob*)table->field[*ptr])->get_length();
635
763
  }
636
764
 
637
765
  return length;
646
774
    return(HA_ERR_OUT_OF_MEM);
647
775
 
648
776
  /* Copy null bits */
649
 
  memcpy(&record_buffer[0], record, getTable()->getShare()->null_bytes);
650
 
  ptr= &record_buffer[0] + getTable()->getShare()->null_bytes;
 
777
  memcpy(record_buffer->buffer, record, table->s->null_bytes);
 
778
  ptr= record_buffer->buffer + table->s->null_bytes;
651
779
 
652
 
  for (Field **field=getTable()->getFields() ; *field ; field++)
 
780
  for (Field **field=table->field ; *field ; field++)
653
781
  {
654
782
    if (!((*field)->is_null()))
655
783
      ptr= (*field)->pack(ptr, record + (*field)->offset(record));
656
784
  }
657
785
 
658
 
  return((unsigned int) (ptr - &record_buffer[0]));
 
786
  return((unsigned int) (ptr - record_buffer->buffer));
659
787
}
660
788
 
661
789
 
668
796
  for implementing start_bulk_insert() is that we could skip
669
797
  setting dirty to true each time.
670
798
*/
671
 
int ha_archive::doInsertRecord(unsigned char *buf)
 
799
int ha_archive::write_row(unsigned char *buf)
672
800
{
673
801
  int rc;
674
802
  unsigned char *read_buf= NULL;
675
803
  uint64_t temp_auto;
676
 
  unsigned char *record=  getTable()->getInsertRecord();
 
804
  unsigned char *record=  table->record[0];
677
805
 
678
806
  if (share->crashed)
679
807
    return(HA_ERR_CRASHED_ON_USAGE);
680
808
 
681
 
  pthread_mutex_lock(&share->mutex());
 
809
  ha_statistic_increment(&SSV::ha_write_count);
 
810
  pthread_mutex_lock(&share->mutex);
682
811
 
683
812
  if (share->archive_write_open == false)
684
813
    if (init_archive_writer())
685
814
      return(HA_ERR_CRASHED_ON_USAGE);
686
815
 
687
816
 
688
 
  if (getTable()->next_number_field && record == getTable()->getInsertRecord())
 
817
  if (table->next_number_field && record == table->record[0])
689
818
  {
 
819
    KEY *mkey= &table->s->key_info[0]; // We only support one key right now
690
820
    update_auto_increment();
691
 
    temp_auto= getTable()->next_number_field->val_int();
 
821
    temp_auto= table->next_number_field->val_int();
692
822
 
693
823
    /*
694
824
      We don't support decremening auto_increment. They make the performance
695
825
      just cry.
696
826
    */
697
827
    if (temp_auto <= share->archive_write.auto_increment &&
698
 
        getTable()->getShare()->getKeyInfo(0).flags & HA_NOSAME)
 
828
        mkey->flags & HA_NOSAME)
699
829
    {
700
830
      rc= HA_ERR_FOUND_DUPP_KEY;
701
831
      goto error;
715
845
  share->rows_recorded++;
716
846
  rc= real_write_row(buf,  &(share->archive_write));
717
847
error:
718
 
  pthread_mutex_unlock(&share->mutex());
 
848
  pthread_mutex_unlock(&share->mutex);
719
849
  if (read_buf)
720
850
    free((unsigned char*) read_buf);
721
851
 
730
860
  *first_value= share->archive_write.auto_increment + 1;
731
861
}
732
862
 
733
 
/* Initialized at each key walk (called multiple times unlike doStartTableScan()) */
734
 
int ha_archive::doStartIndexScan(uint32_t keynr, bool)
 
863
/* Initialized at each key walk (called multiple times unlike rnd_init()) */
 
864
int ha_archive::index_init(uint32_t keynr, bool)
735
865
{
736
866
  active_index= keynr;
737
867
  return(0);
743
873
  the optimizer that we have unique indexes, we scan
744
874
*/
745
875
int ha_archive::index_read(unsigned char *buf, const unsigned char *key,
746
 
                             uint32_t key_len, enum ha_rkey_function)
 
876
                             uint32_t key_len, enum ha_rkey_function find_flag)
 
877
{
 
878
  int rc;
 
879
  rc= index_read_idx(buf, active_index, key, key_len, find_flag);
 
880
  return(rc);
 
881
}
 
882
 
 
883
 
 
884
int ha_archive::index_read_idx(unsigned char *buf, uint32_t index, const unsigned char *key,
 
885
                               uint32_t key_len, enum ha_rkey_function)
747
886
{
748
887
  int rc;
749
888
  bool found= 0;
750
 
  current_k_offset= getTable()->getShare()->getKeyInfo(0).key_part->offset;
 
889
  KEY *mkey= &table->s->key_info[index];
 
890
  current_k_offset= mkey->key_part->offset;
751
891
  current_key= key;
752
892
  current_key_len= key_len;
753
893
 
754
 
  rc= doStartTableScan(true);
 
894
  rc= rnd_init(true);
755
895
 
756
896
  if (rc)
757
897
    goto error;
795
935
  we assume the position will be set.
796
936
*/
797
937
 
798
 
int ha_archive::doStartTableScan(bool scan)
 
938
int ha_archive::rnd_init(bool scan)
799
939
{
800
940
  if (share->crashed)
801
941
      return(HA_ERR_CRASHED_ON_USAGE);
832
972
/* Reallocate buffer if needed */
833
973
bool ha_archive::fix_rec_buff(unsigned int length)
834
974
{
835
 
  record_buffer.resize(length);
836
 
 
837
 
  return false;
 
975
  assert(record_buffer->buffer);
 
976
 
 
977
  if (length > record_buffer->length)
 
978
  {
 
979
    unsigned char *newptr;
 
980
    if (!(newptr= (unsigned char *)realloc(record_buffer->buffer, length)))
 
981
      return(1);
 
982
    record_buffer->buffer= newptr;
 
983
    record_buffer->length= length;
 
984
  }
 
985
 
 
986
  assert(length <= record_buffer->length);
 
987
 
 
988
  return(0);
838
989
}
839
990
 
840
991
int ha_archive::unpack_row(azio_stream *file_to_read, unsigned char *record)
852
1003
  }
853
1004
 
854
1005
  /* Copy null bits */
855
 
  memcpy(record, ptr, getTable()->getNullBytes());
856
 
  ptr+= getTable()->getNullBytes();
857
 
  for (Field **field= getTable()->getFields() ; *field ; field++)
 
1006
  memcpy(record, ptr, table->getNullBytes());
 
1007
  ptr+= table->getNullBytes();
 
1008
  for (Field **field=table->field ; *field ; field++)
858
1009
  {
859
1010
    if (!((*field)->is_null()))
860
1011
    {
861
 
      ptr= (*field)->unpack(record + (*field)->offset(getTable()->getInsertRecord()), ptr);
 
1012
      ptr= (*field)->unpack(record + (*field)->offset(table->record[0]), ptr);
862
1013
    }
863
1014
  }
864
1015
  return(0);
889
1040
    return(HA_ERR_END_OF_FILE);
890
1041
  scan_rows--;
891
1042
 
892
 
  ha_statistic_increment(&system_status_var::ha_read_rnd_next_count);
 
1043
  ha_statistic_increment(&SSV::ha_read_rnd_next_count);
893
1044
  current_position= aztell(&archive);
894
1045
  rc= get_row(&archive, buf);
895
1046
 
896
 
  getTable()->status=rc ? STATUS_NOT_FOUND: 0;
 
1047
  table->status=rc ? STATUS_NOT_FOUND: 0;
897
1048
 
898
1049
  return(rc);
899
1050
}
907
1058
 
908
1059
void ha_archive::position(const unsigned char *)
909
1060
{
910
 
  internal::my_store_ptr(ref, ref_length, current_position);
 
1061
  my_store_ptr(ref, ref_length, current_position);
911
1062
  return;
912
1063
}
913
1064
 
921
1072
 
922
1073
int ha_archive::rnd_pos(unsigned char * buf, unsigned char *pos)
923
1074
{
924
 
  ha_statistic_increment(&system_status_var::ha_read_rnd_next_count);
925
 
  current_position= (internal::my_off_t)internal::my_get_ptr(pos, ref_length);
 
1075
  ha_statistic_increment(&SSV::ha_read_rnd_next_count);
 
1076
  current_position= (my_off_t)my_get_ptr(pos, ref_length);
926
1077
  if (azseek(&archive, (size_t)current_position, SEEK_SET) == (size_t)(-1L))
927
1078
    return(HA_ERR_CRASHED_ON_USAGE);
928
1079
  return(get_row(&archive, buf));
933
1084
  rewriting the meta file. Currently it does this by calling optimize with
934
1085
  the extended flag.
935
1086
*/
936
 
int ha_archive::repair()
 
1087
int ha_archive::repair(Session* session, HA_CHECK_OPT* check_opt)
937
1088
{
938
 
  int rc= optimize();
 
1089
  check_opt->flags= T_EXTEND;
 
1090
  int rc= optimize(session, check_opt);
939
1091
 
940
1092
  if (rc)
941
1093
    return(HA_ERR_CRASHED_ON_REPAIR);
948
1100
  The table can become fragmented if data was inserted, read, and then
949
1101
  inserted again. What we do is open up the file and recompress it completely.
950
1102
*/
951
 
int ha_archive::optimize()
 
1103
int ha_archive::optimize(Session *, HA_CHECK_OPT *)
952
1104
{
953
1105
  int rc= 0;
954
 
  boost::scoped_ptr<azio_stream> writer(new azio_stream);
 
1106
  azio_stream writer;
 
1107
  char writer_filename[FN_REFLEN];
955
1108
 
956
1109
  init_archive_reader();
957
1110
 
971
1124
  azread_frm(&archive, proto_string);
972
1125
 
973
1126
  /* Lets create a file to contain the new data */
974
 
  std::string writer_filename= share->table_name;
975
 
  writer_filename.append(ARN);
 
1127
  fn_format(writer_filename, share->table_name.c_str(), "", ARN,
 
1128
            MY_REPLACE_EXT | MY_UNPACK_FILENAME);
976
1129
 
977
 
  if (!(azopen(writer.get(), writer_filename.c_str(), O_CREAT|O_RDWR, AZ_METHOD_BLOCK)))
 
1130
  if (!(azopen(&writer, writer_filename, O_CREAT|O_RDWR, AZ_METHOD_BLOCK)))
978
1131
  {
979
1132
    free(proto_string);
980
1133
    return(HA_ERR_CRASHED_ON_USAGE);
981
1134
  }
982
1135
 
983
 
  azwrite_frm(writer.get(), proto_string, archive.frm_length);
 
1136
  azwrite_frm(&writer, proto_string, archive.frm_length);
984
1137
 
985
1138
  /*
986
1139
    An extended rebuild is a lot more effort. We open up each row and re-record it.
1004
1157
    */
1005
1158
    if (!rc)
1006
1159
    {
 
1160
      uint64_t x;
1007
1161
      uint64_t rows_restored;
1008
1162
      share->rows_recorded= 0;
1009
1163
      stats.auto_increment_value= 1;
1011
1165
 
1012
1166
      rows_restored= archive.rows;
1013
1167
 
1014
 
      for (uint64_t x= 0; x < rows_restored ; x++)
 
1168
      for (x= 0; x < rows_restored ; x++)
1015
1169
      {
1016
 
        rc= get_row(&archive, getTable()->getInsertRecord());
 
1170
        rc= get_row(&archive, table->record[0]);
1017
1171
 
1018
1172
        if (rc != 0)
1019
1173
          break;
1020
1174
 
1021
 
        real_write_row(getTable()->getInsertRecord(), writer.get());
 
1175
        real_write_row(table->record[0], &writer);
1022
1176
        /*
1023
1177
          Long term it should be possible to optimize this so that
1024
1178
          it is not called on each row.
1025
1179
        */
1026
 
        if (getTable()->found_next_number_field)
 
1180
        if (table->found_next_number_field)
1027
1181
        {
1028
 
          Field *field= getTable()->found_next_number_field;
 
1182
          Field *field= table->found_next_number_field;
1029
1183
 
1030
1184
          /* Since we will need to use field to translate, we need to flip its read bit */
1031
1185
          field->setReadSet();
1032
1186
 
1033
1187
          uint64_t auto_value=
1034
 
            (uint64_t) field->val_int_internal(getTable()->getInsertRecord() +
1035
 
                                               field->offset(getTable()->getInsertRecord()));
 
1188
            (uint64_t) field->val_int(table->record[0] +
 
1189
                                       field->offset(table->record[0]));
1036
1190
          if (share->archive_write.auto_increment < auto_value)
1037
1191
            stats.auto_increment_value=
1038
1192
              (share->archive_write.auto_increment= auto_value) + 1;
1039
1193
        }
1040
1194
      }
1041
 
      share->rows_recorded= (ha_rows)writer->rows;
 
1195
      share->rows_recorded= (ha_rows)writer.rows;
1042
1196
    }
1043
1197
 
1044
1198
    if (rc && rc != HA_ERR_END_OF_FILE)
1047
1201
    }
1048
1202
  }
1049
1203
 
1050
 
  azclose(writer.get());
 
1204
  azclose(&writer);
1051
1205
  share->dirty= false;
1052
1206
 
1053
1207
  azclose(&archive);
1054
1208
 
1055
1209
  // make the file we just wrote be our data file
1056
 
  rc = internal::my_rename(writer_filename.c_str(), share->data_file_name.c_str(), MYF(0));
 
1210
  rc = my_rename(writer_filename,share->data_file_name,MYF(0));
1057
1211
 
1058
1212
  free(proto_string);
1059
1213
  return(rc);
1060
1214
error:
1061
1215
  free(proto_string);
1062
 
  azclose(writer.get());
 
1216
  azclose(&writer);
1063
1217
 
1064
1218
  return(rc);
1065
1219
}
1115
1269
    If dirty, we lock, and then reset/flush the data.
1116
1270
    I found that just calling azflush() doesn't always work.
1117
1271
  */
1118
 
  pthread_mutex_lock(&share->mutex());
 
1272
  pthread_mutex_lock(&share->mutex);
1119
1273
  if (share->dirty == true)
1120
1274
  {
1121
1275
    azflush(&(share->archive_write), Z_SYNC_FLUSH);
1134
1288
    cause the number to be inaccurate.
1135
1289
  */
1136
1290
  stats.records= share->rows_recorded;
1137
 
  pthread_mutex_unlock(&share->mutex());
 
1291
  pthread_mutex_unlock(&share->mutex);
1138
1292
 
1139
1293
  scan_rows= stats.records;
1140
1294
  stats.deleted= 0;
1144
1298
  {
1145
1299
    struct stat file_stat;  // Stat information for the data file
1146
1300
 
1147
 
    stat(share->data_file_name.c_str(), &file_stat);
 
1301
    stat(share->data_file_name, &file_stat);
1148
1302
 
1149
 
    stats.mean_rec_length= getTable()->getRecordLength()+ buffer.alloced_length();
 
1303
    stats.mean_rec_length= table->getRecordLength()+ buffer.alloced_length();
1150
1304
    stats.data_file_length= file_stat.st_size;
1151
1305
    stats.create_time= file_stat.st_ctime;
1152
1306
    stats.update_time= file_stat.st_mtime;
1158
1312
  if (flag & HA_STATUS_AUTO)
1159
1313
  {
1160
1314
    init_archive_reader();
1161
 
    pthread_mutex_lock(&share->mutex());
 
1315
    pthread_mutex_lock(&share->mutex);
1162
1316
    azflush(&archive, Z_SYNC_FLUSH);
1163
 
    pthread_mutex_unlock(&share->mutex());
 
1317
    pthread_mutex_unlock(&share->mutex);
1164
1318
    stats.auto_increment_value= archive.auto_increment + 1;
1165
1319
  }
1166
1320
 
1170
1324
 
1171
1325
/*
1172
1326
  This method tells us that a bulk insert operation is about to occur. We set
1173
 
  a flag which will keep doInsertRecord from saying that its data is dirty. This in
 
1327
  a flag which will keep write_row from saying that its data is dirty. This in
1174
1328
  turn will keep selects from causing a sync to occur.
1175
1329
  Basically, yet another optimizations to keep compression working well.
1176
1330
*/
1204
1358
}
1205
1359
 
1206
1360
/*
 
1361
  We just return state if asked.
 
1362
*/
 
1363
bool ha_archive::is_crashed() const
 
1364
{
 
1365
  return(share->crashed);
 
1366
}
 
1367
 
 
1368
/*
1207
1369
  Simple scan of the tables to make sure everything is ok.
1208
1370
*/
1209
1371
 
1210
 
int ha_archive::check(Session* session)
 
1372
int ha_archive::check(Session* session, HA_CHECK_OPT *)
1211
1373
{
1212
1374
  int rc= 0;
1213
1375
  const char *old_proc_info;
 
1376
  uint64_t x;
1214
1377
 
1215
1378
  old_proc_info= get_session_proc_info(session);
1216
1379
  set_session_proc_info(session, "Checking table");
1217
1380
  /* Flush any waiting data */
1218
 
  pthread_mutex_lock(&share->mutex());
 
1381
  pthread_mutex_lock(&share->mutex);
1219
1382
  azflush(&(share->archive_write), Z_SYNC_FLUSH);
1220
 
  pthread_mutex_unlock(&share->mutex());
 
1383
  pthread_mutex_unlock(&share->mutex);
1221
1384
 
1222
1385
  /*
1223
1386
    Now we will rewind the archive file so that we are positioned at the
1226
1389
  init_archive_reader();
1227
1390
  azflush(&archive, Z_SYNC_FLUSH);
1228
1391
  read_data_header(&archive);
1229
 
  for (uint64_t x= 0; x < share->archive_write.rows; x++)
 
1392
  for (x= 0; x < share->archive_write.rows; x++)
1230
1393
  {
1231
 
    rc= get_row(&archive, getTable()->getInsertRecord());
 
1394
    rc= get_row(&archive, table->record[0]);
1232
1395
 
1233
1396
    if (rc != 0)
1234
1397
      break;
1247
1410
  }
1248
1411
}
1249
1412
 
1250
 
int ArchiveEngine::doRenameTable(Session&, const TableIdentifier &from, const TableIdentifier &to)
1251
 
{
1252
 
  int error= 0;
1253
 
 
1254
 
  for (const char **ext= bas_ext(); *ext ; ext++)
1255
 
  {
1256
 
    if (rename_file_ext(from.getPath().c_str(), to.getPath().c_str(), *ext))
1257
 
    {
1258
 
      if ((error=errno) != ENOENT)
1259
 
        break;
1260
 
      error= 0;
1261
 
    }
1262
 
  }
1263
 
 
1264
 
  return error;
1265
 
}
1266
 
 
1267
 
bool ArchiveEngine::doDoesTableExist(Session&,
1268
 
                                     const TableIdentifier &identifier)
1269
 
{
1270
 
  string proto_path(identifier.getPath());
1271
 
  proto_path.append(ARZ);
1272
 
 
1273
 
  if (access(proto_path.c_str(), F_OK))
1274
 
  {
1275
 
    return false;
1276
 
  }
1277
 
 
1278
 
  return true;
1279
 
}
1280
 
 
1281
 
void ArchiveEngine::doGetTableIdentifiers(drizzled::CachedDirectory &directory,
1282
 
                                          const drizzled::SchemaIdentifier &schema_identifier,
1283
 
                                          drizzled::TableIdentifier::vector &set_of_identifiers)
1284
 
{
1285
 
  drizzled::CachedDirectory::Entries entries= directory.getEntries();
1286
 
 
1287
 
  for (drizzled::CachedDirectory::Entries::iterator entry_iter= entries.begin(); 
1288
 
       entry_iter != entries.end(); ++entry_iter)
1289
 
  {
1290
 
    drizzled::CachedDirectory::Entry *entry= *entry_iter;
1291
 
    const string *filename= &entry->filename;
1292
 
 
1293
 
    assert(filename->size());
1294
 
 
1295
 
    const char *ext= strchr(filename->c_str(), '.');
1296
 
 
1297
 
    if (ext == NULL || my_strcasecmp(system_charset_info, ext, ARZ) ||
1298
 
        (filename->compare(0, strlen(TMP_FILE_PREFIX), TMP_FILE_PREFIX) == 0))
1299
 
    {  }
1300
 
    else
1301
 
    {
1302
 
      char uname[NAME_LEN + 1];
1303
 
      uint32_t file_name_len;
1304
 
 
1305
 
      file_name_len= TableIdentifier::filename_to_tablename(filename->c_str(), uname, sizeof(uname));
1306
 
      // TODO: Remove need for memory copy here
1307
 
      uname[file_name_len - sizeof(ARZ) + 1]= '\0'; // Subtract ending, place NULL 
1308
 
 
1309
 
      set_of_identifiers.push_back(TableIdentifier(schema_identifier, uname));
1310
 
    }
1311
 
  }
1312
 
}
 
1413
archive_record_buffer *ha_archive::create_record_buffer(unsigned int length)
 
1414
{
 
1415
  archive_record_buffer *r;
 
1416
  if (!(r= (archive_record_buffer*) malloc(sizeof(archive_record_buffer))))
 
1417
  {
 
1418
    return(NULL);
 
1419
  }
 
1420
  r->length= (int)length;
 
1421
 
 
1422
  if (!(r->buffer= (unsigned char*) malloc(r->length)))
 
1423
  {
 
1424
    free((char*) r);
 
1425
    return(NULL);
 
1426
  }
 
1427
 
 
1428
  return(r);
 
1429
}
 
1430
 
 
1431
void ha_archive::destroy_record_buffer(archive_record_buffer *r)
 
1432
{
 
1433
  free((char*) r->buffer);
 
1434
  free((char*) r);
 
1435
  return;
 
1436
}
 
1437
 
 
1438
static DRIZZLE_SYSVAR_BOOL(aio, archive_use_aio,
 
1439
  PLUGIN_VAR_NOCMDOPT,
 
1440
  "Whether or not to use asynchronous IO.",
 
1441
  NULL, NULL, true);
 
1442
 
 
1443
static struct st_mysql_sys_var* archive_system_variables[]= {
 
1444
  DRIZZLE_SYSVAR(aio),
 
1445
  NULL
 
1446
};
 
1447
 
 
1448
drizzle_declare_plugin
 
1449
{
 
1450
  "ARCHIVE",
 
1451
  "3.5",
 
1452
  "Brian Aker, MySQL AB",
 
1453
  "Archive storage engine",
 
1454
  PLUGIN_LICENSE_GPL,
 
1455
  archive_db_init, /* Plugin Init */
 
1456
  archive_db_done, /* Plugin Deinit */
 
1457
  NULL,                       /* status variables                */
 
1458
  archive_system_variables,   /* system variables                */
 
1459
  NULL                        /* config options                  */
 
1460
}
 
1461
drizzle_declare_plugin_end;
 
1462