~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/archive/ha_archive.cc

pandora-build v0.71. Added check for avahi.

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
  virtual Cursor *create(TableShare &table,
 
149
                          MEM_ROOT *mem_root)
 
150
  {
 
151
    return new (mem_root) ha_archive(*this, table);
 
152
  }
 
153
 
 
154
  const char **bas_ext() const {
 
155
    return ha_archive_exts;
 
156
  }
 
157
 
 
158
  int doCreateTable(Session *session, const char *table_name,
 
159
                    Table& table_arg, HA_CREATE_INFO& create_info,
 
160
                    drizzled::message::Table& proto);
 
161
 
 
162
  int doGetTableDefinition(Session& session,
 
163
                           const char* path,
 
164
                           const char *db,
 
165
                           const char *table_name,
 
166
                           const bool is_tmp,
 
167
                           drizzled::message::Table *table_proto);
 
168
 
 
169
  void doGetTableNames(CachedDirectory &directory, string& , set<string>& set_of_names);
 
170
 
 
171
  int doDropTable(Session&, const string table_path);
 
172
};
 
173
 
 
174
 
 
175
void ArchiveEngine::doGetTableNames(CachedDirectory &directory, 
 
176
                                    string&, 
 
177
                                    set<string>& set_of_names)
 
178
{
 
179
  CachedDirectory::Entries entries= directory.getEntries();
 
180
 
 
181
  for (CachedDirectory::Entries::iterator entry_iter= entries.begin(); 
 
182
       entry_iter != entries.end(); ++entry_iter)
 
183
  {
 
184
    CachedDirectory::Entry *entry= *entry_iter;
 
185
    string *filename= &entry->filename;
 
186
 
 
187
    assert(filename->size());
 
188
 
 
189
    const char *ext= strchr(filename->c_str(), '.');
 
190
 
 
191
    if (ext == NULL || my_strcasecmp(system_charset_info, ext, ARZ) ||
 
192
        is_prefix(filename->c_str(), TMP_FILE_PREFIX))
 
193
    {  }
 
194
    else
 
195
    {
 
196
      char uname[NAME_LEN + 1];
 
197
      uint32_t file_name_len;
 
198
 
 
199
      file_name_len= filename_to_tablename(filename->c_str(), uname, sizeof(uname));
 
200
      // TODO: Remove need for memory copy here
 
201
      uname[file_name_len - sizeof(ARZ) + 1]= '\0'; // Subtract ending, place NULL 
 
202
      set_of_names.insert(uname);
 
203
    }
 
204
  }
 
205
}
 
206
 
 
207
 
 
208
int ArchiveEngine::doDropTable(Session&,
 
209
                               const string table_path)
 
210
{
 
211
  string new_path(table_path);
137
212
 
138
213
  new_path+= ARZ;
139
214
 
141
216
 
142
217
  if (error != 0)
143
218
  {
144
 
    error= errno= errno;
 
219
    error= my_errno= errno;
145
220
  }
146
221
 
147
222
  return error;
148
223
}
149
224
 
150
225
int ArchiveEngine::doGetTableDefinition(Session&,
151
 
                                        const TableIdentifier &identifier,
152
 
                                        drizzled::message::Table &table_proto)
 
226
                                        const char* path,
 
227
                                        const char *,
 
228
                                        const char *,
 
229
                                        const bool,
 
230
                                        drizzled::message::Table *table_proto)
153
231
{
154
232
  struct stat stat_info;
155
 
  int error= ENOENT;
 
233
  int error= 0;
156
234
  string proto_path;
157
235
 
158
236
  proto_path.reserve(FN_REFLEN);
159
 
  proto_path.assign(identifier.getPath());
 
237
  proto_path.assign(path);
160
238
 
161
239
  proto_path.append(ARZ);
162
240
 
163
241
  if (stat(proto_path.c_str(),&stat_info))
164
242
    return errno;
165
 
  else
166
 
    error= EEXIST;
167
243
 
 
244
  if (table_proto)
168
245
  {
169
 
    boost::scoped_ptr<azio_stream> proto_stream(new azio_stream);
 
246
    azio_stream proto_stream;
170
247
    char* proto_string;
171
 
    if (azopen(proto_stream.get(), proto_path.c_str(), O_RDONLY, AZ_METHOD_BLOCK) == 0)
 
248
    if(azopen(&proto_stream, proto_path.c_str(), O_RDONLY, AZ_METHOD_BLOCK) == 0)
172
249
      return HA_ERR_CRASHED_ON_USAGE;
173
250
 
174
 
    proto_string= (char*)malloc(sizeof(char) * proto_stream->frm_length);
 
251
    proto_string= (char*)malloc(sizeof(char) * proto_stream.frm_length);
175
252
    if (proto_string == NULL)
176
253
    {
177
 
      azclose(proto_stream.get());
 
254
      azclose(&proto_stream);
178
255
      return ENOMEM;
179
256
    }
180
257
 
181
 
    azread_frm(proto_stream.get(), proto_string);
 
258
    azread_frm(&proto_stream, proto_string);
182
259
 
183
 
    if (table_proto.ParseFromArray(proto_string, proto_stream->frm_length) == false)
 
260
    if(table_proto->ParseFromArray(proto_string, proto_stream.frm_length) == false)
184
261
      error= HA_ERR_CRASHED_ON_USAGE;
185
262
 
186
 
    azclose(proto_stream.get());
 
263
    azclose(&proto_stream);
187
264
    free(proto_string);
188
265
  }
189
266
 
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
 
  return error;
 
267
  return EEXIST;
 
268
}
 
269
 
 
270
static ArchiveEngine *archive_engine= NULL;
 
271
 
 
272
/*
 
273
  Initialize the archive Cursor.
 
274
 
 
275
  SYNOPSIS
 
276
    archive_db_init()
 
277
    void *
 
278
 
 
279
  RETURN
 
280
    false       OK
 
281
    true        Error
 
282
*/
 
283
 
 
284
static int archive_db_init(drizzled::plugin::Registry &registry)
 
285
{
 
286
 
 
287
  pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST);
 
288
  archive_engine= new ArchiveEngine(engine_name);
 
289
  registry.add(archive_engine);
 
290
 
 
291
  /* When the engine starts up set the first version */
 
292
  global_version= 1;
 
293
 
 
294
  return false;
 
295
}
 
296
 
 
297
/*
 
298
  Release the archive Cursor.
 
299
 
 
300
  SYNOPSIS
 
301
    archive_db_done()
 
302
    void
 
303
 
 
304
  RETURN
 
305
    false       OK
 
306
*/
 
307
 
 
308
static int archive_db_done(drizzled::plugin::Registry &registry)
 
309
{
 
310
  registry.remove(archive_engine);
 
311
  delete archive_engine;
 
312
 
 
313
  pthread_mutex_destroy(&archive_mutex);
 
314
 
 
315
  return 0;
197
316
}
198
317
 
199
318
 
200
319
ha_archive::ha_archive(drizzled::plugin::StorageEngine &engine_arg,
201
 
                       Table &table_arg)
 
320
                       TableShare &table_arg)
202
321
  :Cursor(engine_arg, table_arg), delayed_insert(0), bulk_insert(0)
203
322
{
204
323
  /* Set our original buffer from pre-allocated memory */
205
324
  buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info);
206
325
 
207
326
  /* The size of the offset value we will use for position() */
208
 
  ref_length= sizeof(internal::my_off_t);
 
327
  ref_length= sizeof(my_off_t);
209
328
  archive_reader_open= false;
210
329
}
211
330
 
236
355
{
237
356
  memset(&archive_write, 0, sizeof(azio_stream));     /* Archive file we are working with */
238
357
  table_name.append(name);
239
 
  data_file_name.assign(table_name);
240
 
  data_file_name.append(ARZ);
 
358
  fn_format(data_file_name, table_name.c_str(), "",
 
359
            ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME);
241
360
  /*
242
361
    We will use this lock for rows.
243
362
  */
244
 
  pthread_mutex_init(&_mutex,MY_MUTEX_INIT_FAST);
 
363
  pthread_mutex_init(&mutex,MY_MUTEX_INIT_FAST);
245
364
}
246
365
 
247
366
ArchiveShare::~ArchiveShare()
248
367
{
249
 
  _lock.deinit();
250
 
  pthread_mutex_destroy(&_mutex);
 
368
  thr_lock_delete(&lock);
 
369
  pthread_mutex_destroy(&mutex);
251
370
  /*
252
371
    We need to make sure we don't reset the crashed state.
253
372
    If we open a crashed file, wee need to close it as crashed unless
261
380
 
262
381
bool ArchiveShare::prime(uint64_t *auto_increment)
263
382
{
264
 
  boost::scoped_ptr<azio_stream> archive_tmp(new azio_stream);
 
383
  azio_stream archive_tmp;
265
384
 
266
385
  /*
267
386
    We read the meta file, but do not mark it dirty. Since we are not
269
388
    anything but reading... open it for write and we will generate null
270
389
    compression writes).
271
390
  */
272
 
  if (!(azopen(archive_tmp.get(), data_file_name.c_str(), O_RDONLY,
 
391
  if (!(azopen(&archive_tmp, data_file_name, O_RDONLY,
273
392
               AZ_METHOD_BLOCK)))
274
393
    return false;
275
394
 
276
 
  *auto_increment= archive_tmp->auto_increment + 1;
277
 
  rows_recorded= (ha_rows)archive_tmp->rows;
278
 
  crashed= archive_tmp->dirty;
 
395
  *auto_increment= archive_tmp.auto_increment + 1;
 
396
  rows_recorded= (ha_rows)archive_tmp.rows;
 
397
  crashed= archive_tmp.dirty;
279
398
  if (version < global_version)
280
399
  {
281
400
    version_rows= rows_recorded;
282
401
    version= global_version;
283
402
  }
284
 
  azclose(archive_tmp.get());
 
403
  azclose(&archive_tmp);
285
404
 
286
405
  return true;
287
406
}
296
415
*/
297
416
ArchiveShare *ha_archive::get_share(const char *table_name, int *rc)
298
417
{
299
 
  ArchiveEngine *a_engine= static_cast<ArchiveEngine *>(getEngine());
300
 
 
301
 
  pthread_mutex_lock(&a_engine->mutex());
302
 
 
303
 
  share= a_engine->findOpenTable(table_name);
 
418
  uint32_t length;
 
419
  map<const char *, ArchiveShare *> ::iterator find_iter;
 
420
 
 
421
  pthread_mutex_lock(&archive_mutex);
 
422
  length=(uint) strlen(table_name);
 
423
 
 
424
  find_iter= archive_open_tables.find(table_name);
 
425
 
 
426
  if (find_iter != archive_open_tables.end())
 
427
    share= (*find_iter).second;
 
428
  else
 
429
    share= NULL;
304
430
 
305
431
  if (!share)
306
432
  {
308
434
 
309
435
    if (share == NULL)
310
436
    {
311
 
      pthread_mutex_unlock(&a_engine->mutex());
 
437
      pthread_mutex_unlock(&archive_mutex);
312
438
      *rc= HA_ERR_OUT_OF_MEM;
313
439
      return(NULL);
314
440
    }
315
441
 
316
442
    if (share->prime(&stats.auto_increment_value) == false)
317
443
    {
318
 
      pthread_mutex_unlock(&a_engine->mutex());
 
444
      pthread_mutex_unlock(&archive_mutex);
319
445
      *rc= HA_ERR_CRASHED_ON_REPAIR;
320
446
      delete share;
321
447
 
322
448
      return NULL;
323
449
    }
324
450
 
325
 
    a_engine->addOpenTable(share->table_name, share);
326
 
    thr_lock_init(&share->_lock);
 
451
    archive_open_tables[share->table_name.c_str()]= share; 
 
452
    thr_lock_init(&share->lock);
327
453
  }
328
454
  share->use_count++;
329
 
 
330
455
  if (share->crashed)
331
456
    *rc= HA_ERR_CRASHED_ON_USAGE;
332
 
  pthread_mutex_unlock(&a_engine->mutex());
 
457
  pthread_mutex_unlock(&archive_mutex);
333
458
 
334
459
  return(share);
335
460
}
341
466
*/
342
467
int ha_archive::free_share()
343
468
{
344
 
  ArchiveEngine *a_engine= static_cast<ArchiveEngine *>(getEngine());
345
 
 
346
 
  pthread_mutex_lock(&a_engine->mutex());
 
469
  pthread_mutex_lock(&archive_mutex);
347
470
  if (!--share->use_count)
348
471
  {
349
 
    a_engine->deleteOpenTable(share->table_name);
 
472
    archive_open_tables.erase(share->table_name.c_str());
350
473
    delete share;
351
474
  }
352
 
  pthread_mutex_unlock(&a_engine->mutex());
 
475
  pthread_mutex_unlock(&archive_mutex);
353
476
 
354
477
  return 0;
355
478
}
361
484
    a gzip file that can be both read and written we keep a writer open
362
485
    that is shared amoung all open tables.
363
486
  */
364
 
  if (!(azopen(&(share->archive_write), share->data_file_name.c_str(),
 
487
  if (!(azopen(&(share->archive_write), share->data_file_name,
365
488
               O_RDWR, AZ_METHOD_BLOCK)))
366
489
  {
367
490
    share->crashed= true;
387
510
  {
388
511
    az_method method;
389
512
 
390
 
    if (archive_aio_state())
 
513
    switch (archive_use_aio)
391
514
    {
 
515
    case false:
 
516
      method= AZ_METHOD_BLOCK;
 
517
      break;
 
518
    case true:
392
519
      method= AZ_METHOD_AIO;
393
 
    }
394
 
    else
395
 
    {
 
520
      break;
 
521
    default:
396
522
      method= AZ_METHOD_BLOCK;
397
523
    }
398
 
    if (!(azopen(&archive, share->data_file_name.c_str(), O_RDONLY,
 
524
    if (!(azopen(&archive, share->data_file_name, O_RDONLY,
399
525
                 method)))
400
526
    {
401
527
      share->crashed= true;
413
539
  Init out lock.
414
540
  We open the file we will read from.
415
541
*/
416
 
int ha_archive::doOpen(const TableIdentifier &identifier, int , uint32_t )
 
542
int ha_archive::open(const char *name, int, uint32_t open_options)
417
543
{
418
544
  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)
 
545
  share= get_share(name, &rc);
 
546
 
 
547
  if (rc == HA_ERR_CRASHED_ON_USAGE && !(open_options & HA_OPEN_FOR_REPAIR))
427
548
  {
428
549
    free_share();
429
 
    rc= repair();
430
 
 
431
 
    return 0;
 
550
    return(rc);
432
551
  }
433
552
  else if (rc == HA_ERR_OUT_OF_MEM)
434
553
  {
437
556
 
438
557
  assert(share);
439
558
 
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;
 
559
  record_buffer= create_record_buffer(table->s->reclength +
 
560
                                      ARCHIVE_ROW_HEADER_SIZE);
 
561
 
 
562
  if (!record_buffer)
 
563
  {
 
564
    free_share();
 
565
    return(HA_ERR_OUT_OF_MEM);
 
566
  }
 
567
 
 
568
  thr_lock_data_init(&share->lock, &lock, NULL);
 
569
 
 
570
  if (rc == HA_ERR_CRASHED_ON_USAGE && open_options & HA_OPEN_FOR_REPAIR)
 
571
  {
 
572
    return(0);
 
573
  }
 
574
  else
 
575
    return(rc);
452
576
}
453
577
 
454
578
 
473
597
{
474
598
  int rc= 0;
475
599
 
476
 
  record_buffer.clear();
 
600
  destroy_record_buffer(record_buffer);
477
601
 
478
602
  /* First close stream */
479
603
  if (archive_reader_open == true)
497
621
  of creation.
498
622
*/
499
623
 
500
 
int ArchiveEngine::doCreateTable(Session &,
 
624
int ArchiveEngine::doCreateTable(Session *,
 
625
                                 const char *table_name,
501
626
                                 Table& table_arg,
502
 
                                 const drizzled::TableIdentifier &identifier,
 
627
                                 HA_CREATE_INFO& create_info,
503
628
                                 drizzled::message::Table& proto)
504
629
{
 
630
  char name_buff[FN_REFLEN];
505
631
  int error= 0;
506
 
  boost::scoped_ptr<azio_stream> create_stream(new azio_stream);
 
632
  azio_stream create_stream;            /* Archive file we are working with */
507
633
  uint64_t auto_increment_value;
508
634
  string serialized_proto;
509
635
 
510
 
  auto_increment_value= proto.options().auto_increment_value();
 
636
  auto_increment_value= create_info.auto_increment_value;
511
637
 
512
638
  for (uint32_t key= 0; key < table_arg.sizeKeys(); key++)
513
639
  {
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;
 
640
    KEY *pos= table_arg.key_info+key;
 
641
    KEY_PART_INFO *key_part=     pos->key_part;
 
642
    KEY_PART_INFO *key_part_end= key_part + pos->key_parts;
517
643
 
518
644
    for (; key_part != key_part_end; key_part++)
519
645
    {
521
647
 
522
648
      if (!(field->flags & AUTO_INCREMENT_FLAG))
523
649
      {
524
 
        return -1;
 
650
        error= -1;
 
651
        goto error;
525
652
      }
526
653
    }
527
654
  }
528
655
 
529
 
  std::string named_file= identifier.getPath();
530
 
  named_file.append(ARZ);
 
656
  /*
 
657
    We reuse name_buff since it is available.
 
658
  */
 
659
  fn_format(name_buff, table_name, "", ARZ,
 
660
            MY_REPLACE_EXT | MY_UNPACK_FILENAME);
531
661
 
532
 
  errno= 0;
533
 
  if (azopen(create_stream.get(), named_file.c_str(), O_CREAT|O_RDWR,
 
662
  my_errno= 0;
 
663
  if (azopen(&create_stream, name_buff, O_CREAT|O_RDWR,
534
664
             AZ_METHOD_BLOCK) == 0)
535
665
  {
536
666
    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(),
 
667
    goto error2;
 
668
  }
 
669
 
 
670
  proto.SerializeToString(&serialized_proto);
 
671
 
 
672
  if (azwrite_frm(&create_stream, serialized_proto.c_str(),
553
673
                  serialized_proto.length()))
554
 
  {
555
 
    unlink(named_file.c_str());
556
 
 
557
 
    return(error ? error : -1);
558
 
  }
 
674
    goto error2;
559
675
 
560
676
  if (proto.options().has_comment())
561
677
  {
562
678
    int write_length;
563
679
 
564
 
    write_length= azwrite_comment(create_stream.get(),
 
680
    write_length= azwrite_comment(&create_stream,
565
681
                                  proto.options().comment().c_str(),
566
682
                                  proto.options().comment().length());
567
683
 
568
684
    if (write_length < 0)
569
685
    {
570
686
      error= errno;
571
 
      unlink(named_file.c_str());
572
 
 
573
 
      return(error ? error : -1);
 
687
      goto error2;
574
688
    }
575
689
  }
576
690
 
578
692
    Yes you need to do this, because the starting value
579
693
    for the autoincrement may not be zero.
580
694
  */
581
 
  create_stream->auto_increment= auto_increment_value ?
 
695
  create_stream.auto_increment= auto_increment_value ?
582
696
    auto_increment_value - 1 : 0;
583
697
 
584
 
  if (azclose(create_stream.get()))
 
698
  if (azclose(&create_stream))
585
699
  {
586
700
    error= errno;
587
 
    unlink(named_file.c_str());
588
 
 
589
 
    return(error ? error : -1);
 
701
    goto error2;
590
702
  }
591
703
 
592
704
  return(0);
 
705
 
 
706
error2:
 
707
  unlink(name_buff);
 
708
 
 
709
error:
 
710
  /* Return error number, if we got one */
 
711
  return(error ? error : -1);
593
712
}
594
713
 
595
714
/*
603
722
  /* We pack the row for writing */
604
723
  r_pack_length= pack_row(buf);
605
724
 
606
 
  written= azwrite_row(writer, &record_buffer[0], r_pack_length);
 
725
  written= azwrite_row(writer, record_buffer->buffer, r_pack_length);
607
726
  if (written != r_pack_length)
608
727
  {
609
728
    return(-1);
623
742
 
624
743
uint32_t ha_archive::max_row_length(const unsigned char *)
625
744
{
626
 
  uint32_t length= (uint32_t)(getTable()->getRecordLength() + getTable()->sizeFields()*2);
 
745
  uint32_t length= (uint32_t)(table->getRecordLength() + table->sizeFields()*2);
627
746
  length+= ARCHIVE_ROW_HEADER_SIZE;
628
747
 
629
748
  uint32_t *ptr, *end;
630
 
  for (ptr= getTable()->getBlobField(), end=ptr + getTable()->sizeBlobFields();
 
749
  for (ptr= table->getBlobField(), end=ptr + table->sizeBlobFields();
631
750
       ptr != end ;
632
751
       ptr++)
633
752
  {
634
 
      length += 2 + ((Field_blob*)getTable()->getField(*ptr))->get_length();
 
753
      length += 2 + ((Field_blob*)table->field[*ptr])->get_length();
635
754
  }
636
755
 
637
756
  return length;
646
765
    return(HA_ERR_OUT_OF_MEM);
647
766
 
648
767
  /* Copy null bits */
649
 
  memcpy(&record_buffer[0], record, getTable()->getShare()->null_bytes);
650
 
  ptr= &record_buffer[0] + getTable()->getShare()->null_bytes;
 
768
  memcpy(record_buffer->buffer, record, table->s->null_bytes);
 
769
  ptr= record_buffer->buffer + table->s->null_bytes;
651
770
 
652
 
  for (Field **field=getTable()->getFields() ; *field ; field++)
 
771
  for (Field **field=table->field ; *field ; field++)
653
772
  {
654
773
    if (!((*field)->is_null()))
655
774
      ptr= (*field)->pack(ptr, record + (*field)->offset(record));
656
775
  }
657
776
 
658
 
  return((unsigned int) (ptr - &record_buffer[0]));
 
777
  return((unsigned int) (ptr - record_buffer->buffer));
659
778
}
660
779
 
661
780
 
668
787
  for implementing start_bulk_insert() is that we could skip
669
788
  setting dirty to true each time.
670
789
*/
671
 
int ha_archive::doInsertRecord(unsigned char *buf)
 
790
int ha_archive::write_row(unsigned char *buf)
672
791
{
673
792
  int rc;
674
793
  unsigned char *read_buf= NULL;
675
794
  uint64_t temp_auto;
676
 
  unsigned char *record=  getTable()->getInsertRecord();
 
795
  unsigned char *record=  table->record[0];
677
796
 
678
797
  if (share->crashed)
679
798
    return(HA_ERR_CRASHED_ON_USAGE);
680
799
 
681
 
  pthread_mutex_lock(&share->mutex());
 
800
  ha_statistic_increment(&SSV::ha_write_count);
 
801
  pthread_mutex_lock(&share->mutex);
682
802
 
683
803
  if (share->archive_write_open == false)
684
804
    if (init_archive_writer())
685
805
      return(HA_ERR_CRASHED_ON_USAGE);
686
806
 
687
807
 
688
 
  if (getTable()->next_number_field && record == getTable()->getInsertRecord())
 
808
  if (table->next_number_field && record == table->record[0])
689
809
  {
 
810
    KEY *mkey= &table->s->key_info[0]; // We only support one key right now
690
811
    update_auto_increment();
691
 
    temp_auto= getTable()->next_number_field->val_int();
 
812
    temp_auto= table->next_number_field->val_int();
692
813
 
693
814
    /*
694
815
      We don't support decremening auto_increment. They make the performance
695
816
      just cry.
696
817
    */
697
818
    if (temp_auto <= share->archive_write.auto_increment &&
698
 
        getTable()->getShare()->getKeyInfo(0).flags & HA_NOSAME)
 
819
        mkey->flags & HA_NOSAME)
699
820
    {
700
821
      rc= HA_ERR_FOUND_DUPP_KEY;
701
822
      goto error;
715
836
  share->rows_recorded++;
716
837
  rc= real_write_row(buf,  &(share->archive_write));
717
838
error:
718
 
  pthread_mutex_unlock(&share->mutex());
 
839
  pthread_mutex_unlock(&share->mutex);
719
840
  if (read_buf)
720
841
    free((unsigned char*) read_buf);
721
842
 
730
851
  *first_value= share->archive_write.auto_increment + 1;
731
852
}
732
853
 
733
 
/* Initialized at each key walk (called multiple times unlike doStartTableScan()) */
734
 
int ha_archive::doStartIndexScan(uint32_t keynr, bool)
 
854
/* Initialized at each key walk (called multiple times unlike rnd_init()) */
 
855
int ha_archive::index_init(uint32_t keynr, bool)
735
856
{
736
857
  active_index= keynr;
737
858
  return(0);
743
864
  the optimizer that we have unique indexes, we scan
744
865
*/
745
866
int ha_archive::index_read(unsigned char *buf, const unsigned char *key,
746
 
                             uint32_t key_len, enum ha_rkey_function)
 
867
                             uint32_t key_len, enum ha_rkey_function find_flag)
 
868
{
 
869
  int rc;
 
870
  rc= index_read_idx(buf, active_index, key, key_len, find_flag);
 
871
  return(rc);
 
872
}
 
873
 
 
874
 
 
875
int ha_archive::index_read_idx(unsigned char *buf, uint32_t index, const unsigned char *key,
 
876
                               uint32_t key_len, enum ha_rkey_function)
747
877
{
748
878
  int rc;
749
879
  bool found= 0;
750
 
  current_k_offset= getTable()->getShare()->getKeyInfo(0).key_part->offset;
 
880
  KEY *mkey= &table->s->key_info[index];
 
881
  current_k_offset= mkey->key_part->offset;
751
882
  current_key= key;
752
883
  current_key_len= key_len;
753
884
 
754
 
  rc= doStartTableScan(true);
 
885
  rc= rnd_init(true);
755
886
 
756
887
  if (rc)
757
888
    goto error;
795
926
  we assume the position will be set.
796
927
*/
797
928
 
798
 
int ha_archive::doStartTableScan(bool scan)
 
929
int ha_archive::rnd_init(bool scan)
799
930
{
800
931
  if (share->crashed)
801
932
      return(HA_ERR_CRASHED_ON_USAGE);
832
963
/* Reallocate buffer if needed */
833
964
bool ha_archive::fix_rec_buff(unsigned int length)
834
965
{
835
 
  record_buffer.resize(length);
836
 
 
837
 
  return false;
 
966
  assert(record_buffer->buffer);
 
967
 
 
968
  if (length > record_buffer->length)
 
969
  {
 
970
    unsigned char *newptr;
 
971
    if (!(newptr= (unsigned char *)realloc(record_buffer->buffer, length)))
 
972
      return(1);
 
973
    record_buffer->buffer= newptr;
 
974
    record_buffer->length= length;
 
975
  }
 
976
 
 
977
  assert(length <= record_buffer->length);
 
978
 
 
979
  return(0);
838
980
}
839
981
 
840
982
int ha_archive::unpack_row(azio_stream *file_to_read, unsigned char *record)
852
994
  }
853
995
 
854
996
  /* Copy null bits */
855
 
  memcpy(record, ptr, getTable()->getNullBytes());
856
 
  ptr+= getTable()->getNullBytes();
857
 
  for (Field **field= getTable()->getFields() ; *field ; field++)
 
997
  memcpy(record, ptr, table->getNullBytes());
 
998
  ptr+= table->getNullBytes();
 
999
  for (Field **field=table->field ; *field ; field++)
858
1000
  {
859
1001
    if (!((*field)->is_null()))
860
1002
    {
861
 
      ptr= (*field)->unpack(record + (*field)->offset(getTable()->getInsertRecord()), ptr);
 
1003
      ptr= (*field)->unpack(record + (*field)->offset(table->record[0]), ptr);
862
1004
    }
863
1005
  }
864
1006
  return(0);
889
1031
    return(HA_ERR_END_OF_FILE);
890
1032
  scan_rows--;
891
1033
 
892
 
  ha_statistic_increment(&system_status_var::ha_read_rnd_next_count);
 
1034
  ha_statistic_increment(&SSV::ha_read_rnd_next_count);
893
1035
  current_position= aztell(&archive);
894
1036
  rc= get_row(&archive, buf);
895
1037
 
896
 
  getTable()->status=rc ? STATUS_NOT_FOUND: 0;
 
1038
  table->status=rc ? STATUS_NOT_FOUND: 0;
897
1039
 
898
1040
  return(rc);
899
1041
}
900
1042
 
901
1043
 
902
1044
/*
903
 
  Thanks to the table bool is_ordered this will be called after
 
1045
  Thanks to the table flag HA_REC_NOT_IN_SEQ this will be called after
904
1046
  each call to ha_archive::rnd_next() if an ordering of the rows is
905
1047
  needed.
906
1048
*/
907
1049
 
908
1050
void ha_archive::position(const unsigned char *)
909
1051
{
910
 
  internal::my_store_ptr(ref, ref_length, current_position);
 
1052
  my_store_ptr(ref, ref_length, current_position);
911
1053
  return;
912
1054
}
913
1055
 
921
1063
 
922
1064
int ha_archive::rnd_pos(unsigned char * buf, unsigned char *pos)
923
1065
{
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);
 
1066
  ha_statistic_increment(&SSV::ha_read_rnd_next_count);
 
1067
  current_position= (my_off_t)my_get_ptr(pos, ref_length);
926
1068
  if (azseek(&archive, (size_t)current_position, SEEK_SET) == (size_t)(-1L))
927
1069
    return(HA_ERR_CRASHED_ON_USAGE);
928
1070
  return(get_row(&archive, buf));
933
1075
  rewriting the meta file. Currently it does this by calling optimize with
934
1076
  the extended flag.
935
1077
*/
936
 
int ha_archive::repair()
 
1078
int ha_archive::repair(Session* session, HA_CHECK_OPT* check_opt)
937
1079
{
938
 
  int rc= optimize();
 
1080
  check_opt->flags= T_EXTEND;
 
1081
  int rc= optimize(session, check_opt);
939
1082
 
940
1083
  if (rc)
941
1084
    return(HA_ERR_CRASHED_ON_REPAIR);
948
1091
  The table can become fragmented if data was inserted, read, and then
949
1092
  inserted again. What we do is open up the file and recompress it completely.
950
1093
*/
951
 
int ha_archive::optimize()
 
1094
int ha_archive::optimize(Session *, HA_CHECK_OPT *)
952
1095
{
953
1096
  int rc= 0;
954
 
  boost::scoped_ptr<azio_stream> writer(new azio_stream);
 
1097
  azio_stream writer;
 
1098
  char writer_filename[FN_REFLEN];
955
1099
 
956
1100
  init_archive_reader();
957
1101
 
971
1115
  azread_frm(&archive, proto_string);
972
1116
 
973
1117
  /* Lets create a file to contain the new data */
974
 
  std::string writer_filename= share->table_name;
975
 
  writer_filename.append(ARN);
 
1118
  fn_format(writer_filename, share->table_name.c_str(), "", ARN,
 
1119
            MY_REPLACE_EXT | MY_UNPACK_FILENAME);
976
1120
 
977
 
  if (!(azopen(writer.get(), writer_filename.c_str(), O_CREAT|O_RDWR, AZ_METHOD_BLOCK)))
 
1121
  if (!(azopen(&writer, writer_filename, O_CREAT|O_RDWR, AZ_METHOD_BLOCK)))
978
1122
  {
979
1123
    free(proto_string);
980
1124
    return(HA_ERR_CRASHED_ON_USAGE);
981
1125
  }
982
1126
 
983
 
  azwrite_frm(writer.get(), proto_string, archive.frm_length);
 
1127
  azwrite_frm(&writer, proto_string, archive.frm_length);
984
1128
 
985
1129
  /*
986
1130
    An extended rebuild is a lot more effort. We open up each row and re-record it.
1004
1148
    */
1005
1149
    if (!rc)
1006
1150
    {
 
1151
      uint64_t x;
1007
1152
      uint64_t rows_restored;
1008
1153
      share->rows_recorded= 0;
1009
1154
      stats.auto_increment_value= 1;
1011
1156
 
1012
1157
      rows_restored= archive.rows;
1013
1158
 
1014
 
      for (uint64_t x= 0; x < rows_restored ; x++)
 
1159
      for (x= 0; x < rows_restored ; x++)
1015
1160
      {
1016
 
        rc= get_row(&archive, getTable()->getInsertRecord());
 
1161
        rc= get_row(&archive, table->record[0]);
1017
1162
 
1018
1163
        if (rc != 0)
1019
1164
          break;
1020
1165
 
1021
 
        real_write_row(getTable()->getInsertRecord(), writer.get());
 
1166
        real_write_row(table->record[0], &writer);
1022
1167
        /*
1023
1168
          Long term it should be possible to optimize this so that
1024
1169
          it is not called on each row.
1025
1170
        */
1026
 
        if (getTable()->found_next_number_field)
 
1171
        if (table->found_next_number_field)
1027
1172
        {
1028
 
          Field *field= getTable()->found_next_number_field;
 
1173
          Field *field= table->found_next_number_field;
1029
1174
 
1030
1175
          /* Since we will need to use field to translate, we need to flip its read bit */
1031
1176
          field->setReadSet();
1032
1177
 
1033
1178
          uint64_t auto_value=
1034
 
            (uint64_t) field->val_int_internal(getTable()->getInsertRecord() +
1035
 
                                               field->offset(getTable()->getInsertRecord()));
 
1179
            (uint64_t) field->val_int(table->record[0] +
 
1180
                                       field->offset(table->record[0]));
1036
1181
          if (share->archive_write.auto_increment < auto_value)
1037
1182
            stats.auto_increment_value=
1038
1183
              (share->archive_write.auto_increment= auto_value) + 1;
1039
1184
        }
1040
1185
      }
1041
 
      share->rows_recorded= (ha_rows)writer->rows;
 
1186
      share->rows_recorded= (ha_rows)writer.rows;
1042
1187
    }
1043
1188
 
1044
1189
    if (rc && rc != HA_ERR_END_OF_FILE)
1047
1192
    }
1048
1193
  }
1049
1194
 
1050
 
  azclose(writer.get());
 
1195
  azclose(&writer);
1051
1196
  share->dirty= false;
1052
1197
 
1053
1198
  azclose(&archive);
1054
1199
 
1055
1200
  // 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));
 
1201
  rc = my_rename(writer_filename,share->data_file_name,MYF(0));
1057
1202
 
1058
1203
  free(proto_string);
1059
1204
  return(rc);
1060
1205
error:
1061
1206
  free(proto_string);
1062
 
  azclose(writer.get());
 
1207
  azclose(&writer);
1063
1208
 
1064
1209
  return(rc);
1065
1210
}
1115
1260
    If dirty, we lock, and then reset/flush the data.
1116
1261
    I found that just calling azflush() doesn't always work.
1117
1262
  */
1118
 
  pthread_mutex_lock(&share->mutex());
 
1263
  pthread_mutex_lock(&share->mutex);
1119
1264
  if (share->dirty == true)
1120
1265
  {
1121
1266
    azflush(&(share->archive_write), Z_SYNC_FLUSH);
1134
1279
    cause the number to be inaccurate.
1135
1280
  */
1136
1281
  stats.records= share->rows_recorded;
1137
 
  pthread_mutex_unlock(&share->mutex());
 
1282
  pthread_mutex_unlock(&share->mutex);
1138
1283
 
1139
1284
  scan_rows= stats.records;
1140
1285
  stats.deleted= 0;
1144
1289
  {
1145
1290
    struct stat file_stat;  // Stat information for the data file
1146
1291
 
1147
 
    stat(share->data_file_name.c_str(), &file_stat);
 
1292
    stat(share->data_file_name, &file_stat);
1148
1293
 
1149
 
    stats.mean_rec_length= getTable()->getRecordLength()+ buffer.alloced_length();
 
1294
    stats.mean_rec_length= table->getRecordLength()+ buffer.alloced_length();
1150
1295
    stats.data_file_length= file_stat.st_size;
1151
1296
    stats.create_time= file_stat.st_ctime;
1152
1297
    stats.update_time= file_stat.st_mtime;
1158
1303
  if (flag & HA_STATUS_AUTO)
1159
1304
  {
1160
1305
    init_archive_reader();
1161
 
    pthread_mutex_lock(&share->mutex());
 
1306
    pthread_mutex_lock(&share->mutex);
1162
1307
    azflush(&archive, Z_SYNC_FLUSH);
1163
 
    pthread_mutex_unlock(&share->mutex());
 
1308
    pthread_mutex_unlock(&share->mutex);
1164
1309
    stats.auto_increment_value= archive.auto_increment + 1;
1165
1310
  }
1166
1311
 
1170
1315
 
1171
1316
/*
1172
1317
  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
 
1318
  a flag which will keep write_row from saying that its data is dirty. This in
1174
1319
  turn will keep selects from causing a sync to occur.
1175
1320
  Basically, yet another optimizations to keep compression working well.
1176
1321
*/
1204
1349
}
1205
1350
 
1206
1351
/*
 
1352
  We just return state if asked.
 
1353
*/
 
1354
bool ha_archive::is_crashed() const
 
1355
{
 
1356
  return(share->crashed);
 
1357
}
 
1358
 
 
1359
/*
1207
1360
  Simple scan of the tables to make sure everything is ok.
1208
1361
*/
1209
1362
 
1210
 
int ha_archive::check(Session* session)
 
1363
int ha_archive::check(Session* session, HA_CHECK_OPT *)
1211
1364
{
1212
1365
  int rc= 0;
1213
1366
  const char *old_proc_info;
 
1367
  uint64_t x;
1214
1368
 
1215
1369
  old_proc_info= get_session_proc_info(session);
1216
1370
  set_session_proc_info(session, "Checking table");
1217
1371
  /* Flush any waiting data */
1218
 
  pthread_mutex_lock(&share->mutex());
 
1372
  pthread_mutex_lock(&share->mutex);
1219
1373
  azflush(&(share->archive_write), Z_SYNC_FLUSH);
1220
 
  pthread_mutex_unlock(&share->mutex());
 
1374
  pthread_mutex_unlock(&share->mutex);
1221
1375
 
1222
1376
  /*
1223
1377
    Now we will rewind the archive file so that we are positioned at the
1226
1380
  init_archive_reader();
1227
1381
  azflush(&archive, Z_SYNC_FLUSH);
1228
1382
  read_data_header(&archive);
1229
 
  for (uint64_t x= 0; x < share->archive_write.rows; x++)
 
1383
  for (x= 0; x < share->archive_write.rows; x++)
1230
1384
  {
1231
 
    rc= get_row(&archive, getTable()->getInsertRecord());
 
1385
    rc= get_row(&archive, table->record[0]);
1232
1386
 
1233
1387
    if (rc != 0)
1234
1388
      break;
1247
1401
  }
1248
1402
}
1249
1403
 
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
 
}
 
1404
archive_record_buffer *ha_archive::create_record_buffer(unsigned int length)
 
1405
{
 
1406
  archive_record_buffer *r;
 
1407
  if (!(r= (archive_record_buffer*) malloc(sizeof(archive_record_buffer))))
 
1408
  {
 
1409
    return(NULL);
 
1410
  }
 
1411
  r->length= (int)length;
 
1412
 
 
1413
  if (!(r->buffer= (unsigned char*) malloc(r->length)))
 
1414
  {
 
1415
    free((char*) r);
 
1416
    return(NULL);
 
1417
  }
 
1418
 
 
1419
  return(r);
 
1420
}
 
1421
 
 
1422
void ha_archive::destroy_record_buffer(archive_record_buffer *r)
 
1423
{
 
1424
  free((char*) r->buffer);
 
1425
  free((char*) r);
 
1426
  return;
 
1427
}
 
1428
 
 
1429
static DRIZZLE_SYSVAR_BOOL(aio, archive_use_aio,
 
1430
  PLUGIN_VAR_NOCMDOPT,
 
1431
  "Whether or not to use asynchronous IO.",
 
1432
  NULL, NULL, true);
 
1433
 
 
1434
static struct st_mysql_sys_var* archive_system_variables[]= {
 
1435
  DRIZZLE_SYSVAR(aio),
 
1436
  NULL
 
1437
};
 
1438
 
 
1439
drizzle_declare_plugin
 
1440
{
 
1441
  "ARCHIVE",
 
1442
  "3.5",
 
1443
  "Brian Aker, MySQL AB",
 
1444
  "Archive storage engine",
 
1445
  PLUGIN_LICENSE_GPL,
 
1446
  archive_db_init, /* Plugin Init */
 
1447
  archive_db_done, /* Plugin Deinit */
 
1448
  NULL,                       /* status variables                */
 
1449
  archive_system_variables,   /* system variables                */
 
1450
  NULL                        /* config options                  */
 
1451
}
 
1452
drizzle_declare_plugin_end;
 
1453