~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/archive/ha_archive.cc

  • Committer: Brian Aker
  • Date: 2009-05-11 17:50:22 UTC
  • Revision ID: brian@gaz-20090511175022-y35q9ky6uh9ldcjt
Replacing Sun employee copyright headers (aka... anything done by a Sun
employee is copyright by Sun).

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
  along with this program; if not, write to the Free Software
14
14
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
15
 
16
 
#ifdef USE_PRAGMA_IMPLEMENTATION
17
 
#pragma implementation        // gcc: Class implementation
18
 
#endif
19
16
 
20
 
#include <drizzled/common_includes.h>
21
 
#include <storage/myisam/myisam.h>
 
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"
22
24
 
23
25
#include "ha_archive.h"
24
26
 
 
27
#include <stdio.h>
 
28
#include <string>
 
29
#include <map>
 
30
 
 
31
using namespace std;
 
32
 
 
33
static const string engine_name("ARCHIVE");
 
34
 
25
35
/*
26
 
  First, if you want to understand storage engines you should look at 
27
 
  ha_example.cc and ha_example.h. 
 
36
  First, if you want to understand storage engines you should look at
 
37
  ha_example.cc and ha_example.h.
28
38
 
29
39
  This example was written as a test case for a customer who needed
30
40
  a storage engine without indexes that could compress data very well.
31
41
  So, welcome to a completely compressed storage engine. This storage
32
 
  engine only does inserts. No replace, deletes, or updates. All reads are 
 
42
  engine only does inserts. No replace, deletes, or updates. All reads are
33
43
  complete table scans. Compression is done through a combination of packing
34
44
  and making use of the zlib library
35
 
  
 
45
 
36
46
  We keep a file pointer open for each instance of ha_archive for each read
37
47
  but for writes we keep one open file handle just for that. We flush it
38
48
  only if we have a read occur. azip handles compressing lots of records
42
52
  the same time since we would want to flush).
43
53
 
44
54
  A "meta" file is kept alongside the data file. This file serves two purpose.
45
 
  The first purpose is to track the number of rows in the table. The second 
46
 
  purpose is to determine if the table was closed properly or not. When the 
47
 
  meta file is first opened it is marked as dirty. It is opened when the table 
48
 
  itself is opened for writing. When the table is closed the new count for rows 
49
 
  is written to the meta file and the file is marked as clean. If the meta file 
50
 
  is opened and it is marked as dirty, it is assumed that a crash occured. At 
 
55
  The first purpose is to track the number of rows in the table. The second
 
56
  purpose is to determine if the table was closed properly or not. When the
 
57
  meta file is first opened it is marked as dirty. It is opened when the table
 
58
  itself is opened for writing. When the table is closed the new count for rows
 
59
  is written to the meta file and the file is marked as clean. If the meta file
 
60
  is opened and it is marked as dirty, it is assumed that a crash occured. At
51
61
  this point an error occurs and the user is told to rebuild the file.
52
62
  A rebuild scans the rows and rewrites the meta file. If corruption is found
53
63
  in the data file then the meta file is not repaired.
54
64
 
55
65
  At some point a recovery method for such a drastic case needs to be divised.
56
66
 
57
 
  Locks are row level, and you will get a consistant read. 
 
67
  Locks are row level, and you will get a consistant read.
58
68
 
59
69
  For performance as far as table scans go it is quite fast. I don't have
60
70
  good numbers but locally it has out performed both Innodb and MyISAM. For
61
71
  Innodb the question will be if the table can be fit into the buffer
62
72
  pool. For MyISAM its a question of how much the file system caches the
63
73
  MyISAM file. With enough free memory MyISAM is faster. Its only when the OS
64
 
  doesn't have enough memory to cache entire table that archive turns out 
65
 
  to be any faster. 
 
74
  doesn't have enough memory to cache entire table that archive turns out
 
75
  to be any faster.
66
76
 
67
77
  Examples between MyISAM (packed) and Archive.
68
78
 
91
101
*/
92
102
 
93
103
/* Variables for archive share methods */
94
 
pthread_mutex_t archive_mutex;
95
 
static HASH archive_open_tables;
 
104
pthread_mutex_t archive_mutex= PTHREAD_MUTEX_INITIALIZER;
 
105
 
 
106
std::map<const char *, ArchiveShare *> archive_open_tables;
 
107
 
96
108
static unsigned int global_version;
97
109
 
98
110
/* The file extension */
99
111
#define ARZ ".ARZ"               // The data file
100
112
#define ARN ".ARN"               // Files used during an optimize call
101
 
#define ARM ".ARM"               // Meta file (deprecated)
102
 
 
103
 
/*
104
 
  unsigned char + unsigned char
105
 
*/
106
 
#define DATA_BUFFER_SIZE 2       // Size of the data used in the data file
107
 
#define ARCHIVE_CHECK_HEADER 254 // The number we use to determine corruption
108
 
 
109
 
/* Static declarations for handerton */
110
 
static handler *archive_create_handler(handlerton *hton, 
111
 
                                       TABLE_SHARE *table, 
112
 
                                       MEM_ROOT *mem_root);
113
 
int archive_discover(handlerton *hton, THD* thd, const char *db, 
114
 
                     const char *name,
115
 
                     unsigned char **frmblob, 
116
 
                     size_t *frmlen);
 
113
 
 
114
 
117
115
 
118
116
static bool archive_use_aio= false;
119
117
 
127
125
*/
128
126
#define ARCHIVE_ROW_HEADER_SIZE 4
129
127
 
130
 
static handler *archive_create_handler(handlerton *hton,
131
 
                                       TABLE_SHARE *table, 
132
 
                                       MEM_ROOT *mem_root)
133
 
{
134
 
  return new (mem_root) ha_archive(hton, table);
135
 
}
136
 
 
137
 
/*
138
 
  Used for hash table that tracks open tables.
139
 
*/
140
 
static unsigned char* archive_get_key(ARCHIVE_SHARE *share, size_t *length,
141
 
                             bool not_used __attribute__((unused)))
142
 
{
143
 
  *length=share->table_name_length;
144
 
  return (unsigned char*) share->table_name;
145
 
}
146
 
 
 
128
class ArchiveEngine : public StorageEngine
 
129
{
 
130
public:
 
131
  ArchiveEngine(const string &name_arg) : StorageEngine(name_arg) {}
 
132
  virtual handler *create(TableShare *table,
 
133
                          MEM_ROOT *mem_root)
 
134
  {
 
135
    return new (mem_root) ha_archive(this, table);
 
136
  }
 
137
};
 
138
 
 
139
static ArchiveEngine *archive_engine= NULL;
147
140
 
148
141
/*
149
142
  Initialize the archive handler.
157
150
    true        Error
158
151
*/
159
152
 
160
 
int archive_db_init(void *p)
 
153
int archive_db_init(PluginRegistry &registry)
161
154
{
162
 
  handlerton *archive_hton;
163
155
 
164
 
  archive_hton= (handlerton *)p;
165
 
  archive_hton->state= SHOW_OPTION_YES;
166
 
  archive_hton->create= archive_create_handler;
167
 
  archive_hton->flags= HTON_NO_FLAGS;
168
 
  archive_hton->discover= archive_discover;
 
156
  pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST);
 
157
  archive_engine= new ArchiveEngine(engine_name);
 
158
  registry.add(archive_engine);
169
159
 
170
160
  /* When the engine starts up set the first version */
171
161
  global_version= 1;
172
162
 
173
 
  if (pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST))
174
 
    goto error;
175
 
  if (hash_init(&archive_open_tables, system_charset_info, 32, 0, 0,
176
 
                (hash_get_key) archive_get_key, 0, 0))
177
 
  {
178
 
    pthread_mutex_destroy(&archive_mutex);
179
 
  }
180
 
  else
181
 
  {
182
 
    return(false);
183
 
  }
184
 
error:
185
 
  return(true);
 
163
  return false;
186
164
}
187
165
 
188
166
/*
196
174
    false       OK
197
175
*/
198
176
 
199
 
int archive_db_done(void *p __attribute__((unused)))
 
177
int archive_db_done(PluginRegistry &registry)
200
178
{
201
 
  hash_free(&archive_open_tables);
 
179
  registry.remove(archive_engine);
 
180
  delete archive_engine;
 
181
 
202
182
  pthread_mutex_destroy(&archive_mutex);
203
183
 
204
184
  return 0;
205
185
}
206
186
 
207
187
 
208
 
ha_archive::ha_archive(handlerton *hton, TABLE_SHARE *table_arg)
209
 
  :handler(hton, table_arg), delayed_insert(0), bulk_insert(0)
 
188
ha_archive::ha_archive(StorageEngine *engine_arg, TableShare *table_arg)
 
189
  :handler(engine_arg, table_arg), delayed_insert(0), bulk_insert(0)
210
190
{
211
191
  /* Set our original buffer from pre-allocated memory */
212
192
  buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info);
216
196
  archive_reader_open= false;
217
197
}
218
198
 
219
 
int archive_discover(handlerton *hton __attribute__((unused)),
220
 
                     THD* thd __attribute__((unused)),
221
 
                     const char *db,
222
 
                     const char *name,
223
 
                     unsigned char **frmblob,
224
 
                     size_t *frmlen)
225
 
{
226
 
  azio_stream frm_stream;
227
 
  char az_file[FN_REFLEN];
228
 
  char *frm_ptr;
229
 
  struct stat file_stat; 
230
 
 
231
 
  fn_format(az_file, name, db, ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME);
232
 
 
233
 
  if (stat(az_file, &file_stat))
234
 
    goto err;
235
 
 
236
 
  if (!(azopen(&frm_stream, az_file, O_RDONLY|O_BINARY, AZ_METHOD_BLOCK)))
237
 
  {
238
 
    if (errno == EROFS || errno == EACCES)
239
 
      return(my_errno= errno);
240
 
    return(HA_ERR_CRASHED_ON_USAGE);
241
 
  }
242
 
 
243
 
  if (frm_stream.frm_length == 0)
244
 
    goto err;
245
 
 
246
 
  frm_ptr= (char *)my_malloc(sizeof(char) * frm_stream.frm_length, MYF(0));
247
 
  azread_frm(&frm_stream, frm_ptr);
248
 
  azclose(&frm_stream);
249
 
 
250
 
  *frmlen= frm_stream.frm_length;
251
 
  *frmblob= (unsigned char*) frm_ptr;
252
 
 
253
 
  return(0);
254
 
err:
255
 
  my_errno= 0;
256
 
  return(1);
257
 
}
258
 
 
259
199
/*
260
200
  This method reads the header of a datafile and returns whether or not it was successful.
261
201
*/
270
210
  return(1);
271
211
}
272
212
 
 
213
ArchiveShare::ArchiveShare():
 
214
  use_count(0), archive_write_open(false), dirty(false), crashed(false),
 
215
  mean_rec_length(0), version(0), rows_recorded(0), version_rows(0)
 
216
{
 
217
  assert(1);
 
218
}
 
219
 
 
220
ArchiveShare::ArchiveShare(const char *name):
 
221
  use_count(0), archive_write_open(false), dirty(false), crashed(false),
 
222
  mean_rec_length(0), version(0), rows_recorded(0), version_rows(0)
 
223
{
 
224
  memset(&archive_write, 0, sizeof(azio_stream));     /* Archive file we are working with */
 
225
  table_name.append(name);
 
226
  fn_format(data_file_name, table_name.c_str(), "",
 
227
            ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME);
 
228
  /*
 
229
    We will use this lock for rows.
 
230
  */
 
231
  pthread_mutex_init(&mutex,MY_MUTEX_INIT_FAST);
 
232
}
 
233
 
 
234
ArchiveShare::~ArchiveShare()
 
235
{
 
236
  thr_lock_delete(&lock);
 
237
  pthread_mutex_destroy(&mutex);
 
238
  /*
 
239
    We need to make sure we don't reset the crashed state.
 
240
    If we open a crashed file, wee need to close it as crashed unless
 
241
    it has been repaired.
 
242
    Since we will close the data down after this, we go on and count
 
243
    the flush on close;
 
244
  */
 
245
  if (archive_write_open == true)
 
246
    (void)azclose(&archive_write);
 
247
}
 
248
 
 
249
bool ArchiveShare::prime(uint64_t *auto_increment)
 
250
{
 
251
  azio_stream archive_tmp;
 
252
 
 
253
  /*
 
254
    We read the meta file, but do not mark it dirty. Since we are not
 
255
    doing a write we won't mark it dirty (and we won't open it for
 
256
    anything but reading... open it for write and we will generate null
 
257
    compression writes).
 
258
  */
 
259
  if (!(azopen(&archive_tmp, data_file_name, O_RDONLY,
 
260
               AZ_METHOD_BLOCK)))
 
261
    return false;
 
262
 
 
263
  *auto_increment= archive_tmp.auto_increment + 1;
 
264
  rows_recorded= (ha_rows)archive_tmp.rows;
 
265
  crashed= archive_tmp.dirty;
 
266
  if (version < global_version)
 
267
  {
 
268
    version_rows= rows_recorded;
 
269
    version= global_version;
 
270
  }
 
271
  azclose(&archive_tmp);
 
272
 
 
273
  return true;
 
274
}
 
275
 
273
276
 
274
277
/*
275
 
  We create the shared memory space that we will use for the open table. 
 
278
  We create the shared memory space that we will use for the open table.
276
279
  No matter what we try to get or create a share. This is so that a repair
277
 
  table operation can occur. 
 
280
  table operation can occur.
278
281
 
279
282
  See ha_example.cc for a longer description.
280
283
*/
281
 
ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, int *rc)
 
284
ArchiveShare *ha_archive::get_share(const char *table_name, int *rc)
282
285
{
283
286
  uint32_t length;
 
287
  map<const char *, ArchiveShare *> ::iterator find_iter;
284
288
 
285
289
  pthread_mutex_lock(&archive_mutex);
286
290
  length=(uint) strlen(table_name);
287
291
 
288
 
  if (!(share=(ARCHIVE_SHARE*) hash_search(&archive_open_tables,
289
 
                                           (unsigned char*) table_name,
290
 
                                           length)))
 
292
  find_iter= archive_open_tables.find(table_name);
 
293
 
 
294
  if (find_iter != archive_open_tables.end())
 
295
    share= (*find_iter).second;
 
296
  else
 
297
    share= NULL;
 
298
 
 
299
  if (!share)
291
300
  {
292
 
    char *tmp_name;
293
 
    azio_stream archive_tmp;
 
301
    share= new ArchiveShare(table_name);
294
302
 
295
 
    if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
296
 
                          &share, sizeof(*share),
297
 
                          &tmp_name, length+1,
298
 
                          NULL)) 
 
303
    if (share == NULL)
299
304
    {
300
305
      pthread_mutex_unlock(&archive_mutex);
301
306
      *rc= HA_ERR_OUT_OF_MEM;
302
307
      return(NULL);
303
308
    }
304
309
 
305
 
    share->use_count= 0;
306
 
    share->table_name_length= length;
307
 
    share->table_name= tmp_name;
308
 
    share->crashed= false;
309
 
    share->archive_write_open= false;
310
 
    fn_format(share->data_file_name, table_name, "",
311
 
              ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME);
312
 
    my_stpcpy(share->table_name, table_name);
313
 
    /*
314
 
      We will use this lock for rows.
315
 
    */
316
 
    pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
317
 
    
318
 
    /*
319
 
      We read the meta file, but do not mark it dirty. Since we are not
320
 
      doing a write we won't mark it dirty (and we won't open it for
321
 
      anything but reading... open it for write and we will generate null
322
 
      compression writes).
323
 
    */
324
 
    if (!(azopen(&archive_tmp, share->data_file_name, O_RDONLY|O_BINARY,
325
 
                 AZ_METHOD_BLOCK)))
 
310
    if (share->prime(&stats.auto_increment_value) == false)
326
311
    {
327
 
      pthread_mutex_destroy(&share->mutex);
328
 
      free(share);
329
312
      pthread_mutex_unlock(&archive_mutex);
330
313
      *rc= HA_ERR_CRASHED_ON_REPAIR;
331
 
      return(NULL);
332
 
    }
333
 
    stats.auto_increment_value= archive_tmp.auto_increment + 1;
334
 
    share->rows_recorded= (ha_rows)archive_tmp.rows;
335
 
    share->crashed= archive_tmp.dirty;
336
 
    if (share->version < global_version)
337
 
    {
338
 
      share->version_rows= share->rows_recorded;
339
 
      share->version= global_version;
340
 
    }
341
 
    azclose(&archive_tmp);
342
 
 
343
 
    my_hash_insert(&archive_open_tables, (unsigned char*) share);
 
314
      delete share;
 
315
 
 
316
      return NULL;
 
317
    }
 
318
 
 
319
    archive_open_tables[share->table_name.c_str()]= share; 
344
320
    thr_lock_init(&share->lock);
345
321
  }
346
322
  share->use_count++;
352
328
}
353
329
 
354
330
 
355
 
/* 
 
331
/*
356
332
  Free the share.
357
333
  See ha_example.cc for a description.
358
334
*/
359
335
int ha_archive::free_share()
360
336
{
361
 
  int rc= 0;
362
 
 
363
337
  pthread_mutex_lock(&archive_mutex);
364
338
  if (!--share->use_count)
365
339
  {
366
 
    hash_delete(&archive_open_tables, (unsigned char*) share);
367
 
    thr_lock_delete(&share->lock);
368
 
    pthread_mutex_destroy(&share->mutex);
369
 
    /* 
370
 
      We need to make sure we don't reset the crashed state.
371
 
      If we open a crashed file, wee need to close it as crashed unless
372
 
      it has been repaired.
373
 
      Since we will close the data down after this, we go on and count
374
 
      the flush on close;
375
 
    */
376
 
    if (share->archive_write_open == true)
377
 
    {
378
 
      if (azclose(&(share->archive_write)))
379
 
        rc= 1;
380
 
    }
381
 
    free((unsigned char*) share);
 
340
    archive_open_tables.erase(share->table_name.c_str());
 
341
    delete share;
382
342
  }
383
343
  pthread_mutex_unlock(&archive_mutex);
384
344
 
385
 
  return(rc);
 
345
  return 0;
386
346
}
387
347
 
388
348
int ha_archive::init_archive_writer()
389
349
{
390
 
  /* 
 
350
  /*
391
351
    It is expensive to open and close the data files and since you can't have
392
352
    a gzip file that can be both read and written we keep a writer open
393
353
    that is shared amoung all open tables.
394
354
  */
395
 
  if (!(azopen(&(share->archive_write), share->data_file_name, 
396
 
               O_RDWR|O_BINARY, AZ_METHOD_BLOCK)))
 
355
  if (!(azopen(&(share->archive_write), share->data_file_name,
 
356
               O_RDWR, AZ_METHOD_BLOCK)))
397
357
  {
398
358
    share->crashed= true;
399
359
    return(1);
404
364
}
405
365
 
406
366
 
407
 
/* 
 
367
/*
408
368
  No locks are required because it is associated with just one handler instance
409
369
*/
410
370
int ha_archive::init_archive_reader()
411
371
{
412
 
  /* 
 
372
  /*
413
373
    It is expensive to open and close the data files and since you can't have
414
374
    a gzip file that can be both read and written we keep a writer open
415
375
    that is shared amoung all open tables.
429
389
    default:
430
390
      method= AZ_METHOD_BLOCK;
431
391
    }
432
 
    if (!(azopen(&archive, share->data_file_name, O_RDONLY|O_BINARY, 
 
392
    if (!(azopen(&archive, share->data_file_name, O_RDONLY,
433
393
                 method)))
434
394
    {
435
395
      share->crashed= true;
456
416
}
457
417
 
458
418
 
459
 
/* 
 
419
/*
460
420
  When opening a file we:
461
421
  Create/get our shared structure.
462
422
  Init out lock.
463
423
  We open the file we will read from.
464
424
*/
465
 
int ha_archive::open(const char *name,
466
 
                     int mode __attribute__((unused)),
467
 
                     uint32_t open_options)
 
425
int ha_archive::open(const char *name, int, uint32_t open_options)
468
426
{
469
427
  int rc= 0;
470
428
  share= get_share(name, &rc);
474
432
    /* purecov: begin inspected */
475
433
    free_share();
476
434
    return(rc);
477
 
    /* purecov: end */    
 
435
    /* purecov: end */
478
436
  }
479
437
  else if (rc == HA_ERR_OUT_OF_MEM)
480
438
  {
483
441
 
484
442
  assert(share);
485
443
 
486
 
  record_buffer= create_record_buffer(table->s->reclength + 
 
444
  record_buffer= create_record_buffer(table->s->reclength +
487
445
                                      ARCHIVE_ROW_HEADER_SIZE);
488
446
 
489
447
  if (!record_buffer)
508
466
 
509
467
  SYNOPSIS
510
468
    close();
511
 
  
 
469
 
512
470
  IMPLEMENTATION:
513
471
 
514
472
  We first close this storage engines file handle to the archive and
540
498
 
541
499
 
542
500
/*
543
 
  We create our data file here. The format is pretty simple. 
 
501
  We create our data file here. The format is pretty simple.
544
502
  You can read about the format of the data file above.
545
 
  Unlike other storage engines we do not "pack" our data. Since we 
546
 
  are about to do a general compression, packing would just be a waste of 
547
 
  CPU time. If the table has blobs they are written after the row in the order 
 
503
  Unlike other storage engines we do not "pack" our data. Since we
 
504
  are about to do a general compression, packing would just be a waste of
 
505
  CPU time. If the table has blobs they are written after the row in the order
548
506
  of creation.
549
507
*/
550
508
 
553
511
{
554
512
  char name_buff[FN_REFLEN];
555
513
  char linkname[FN_REFLEN];
556
 
  int error;
 
514
  int error= 0;
557
515
  azio_stream create_stream;            /* Archive file we are working with */
558
 
  File frm_file;                   /* File handler for readers */
 
516
  FILE *frm_file;                   /* File handler for readers */
559
517
  struct stat file_stat;
560
518
  unsigned char *frm_ptr;
 
519
  int r;
561
520
 
562
521
  stats.auto_increment_value= create_info->auto_increment_value;
563
522
 
579
538
    }
580
539
  }
581
540
 
582
 
  /* 
 
541
  /*
583
542
    We reuse name_buff since it is available.
584
543
  */
585
544
  if (create_info->data_file_name && create_info->data_file_name[0] != '#')
600
559
    There is a chance that the file was "discovered". In this case
601
560
    just use whatever file is there.
602
561
  */
603
 
  if (!stat(name_buff, &file_stat))
604
 
  {
605
 
    my_errno= 0;
606
 
    if (!(azopen(&create_stream, name_buff, O_CREAT|O_RDWR|O_BINARY,
607
 
                 AZ_METHOD_BLOCK)))
608
 
    {
609
 
      error= errno;
610
 
      goto error2;
611
 
    }
612
 
 
613
 
    if (linkname[0])
614
 
      my_symlink(name_buff, linkname, MYF(0));
615
 
    fn_format(name_buff, name, "", ".frm",
616
 
              MY_REPLACE_EXT | MY_UNPACK_FILENAME);
617
 
 
618
 
    /*
619
 
      Here is where we open up the frm and pass it to archive to store 
620
 
    */
621
 
    if ((frm_file= my_open(name_buff, O_RDONLY, MYF(0))) > 0)
622
 
    {
623
 
      if (fstat(frm_file, &file_stat))
624
 
      {
625
 
        frm_ptr= (unsigned char *)my_malloc(sizeof(unsigned char) * file_stat.st_size, MYF(0));
626
 
        if (frm_ptr)
627
 
        {
628
 
          my_read(frm_file, frm_ptr, file_stat.st_size, MYF(0));
629
 
          azwrite_frm(&create_stream, (char *)frm_ptr, file_stat.st_size);
630
 
          free((unsigned char*)frm_ptr);
631
 
        }
632
 
      }
633
 
      my_close(frm_file, MYF(0));
634
 
    }
635
 
 
636
 
    if (create_info->comment.str)
637
 
      azwrite_comment(&create_stream, create_info->comment.str, 
638
 
                      (unsigned int)create_info->comment.length);
639
 
 
640
 
    /* 
641
 
      Yes you need to do this, because the starting value 
642
 
      for the autoincrement may not be zero.
643
 
    */
644
 
    create_stream.auto_increment= stats.auto_increment_value ?
645
 
                                    stats.auto_increment_value - 1 : 0;
646
 
    if (azclose(&create_stream))
647
 
    {
648
 
      error= errno;
649
 
      goto error2;
650
 
    }
651
 
  }
652
 
  else
653
 
    my_errno= 0;
 
562
  r= stat(name_buff, &file_stat);
 
563
  if (r == -1 && errno!=ENOENT)
 
564
  {
 
565
    return errno;
 
566
  }
 
567
  if (!r)
 
568
    return HA_ERR_TABLE_EXIST;
 
569
 
 
570
  my_errno= 0;
 
571
  if (!(azopen(&create_stream, name_buff, O_CREAT|O_RDWR,
 
572
               AZ_METHOD_BLOCK)))
 
573
  {
 
574
    error= errno;
 
575
    goto error2;
 
576
  }
 
577
 
 
578
  if (linkname[0])
 
579
    my_symlink(name_buff, linkname, MYF(0));
 
580
  fn_format(name_buff, name, "", ".frm",
 
581
            MY_REPLACE_EXT | MY_UNPACK_FILENAME);
 
582
 
 
583
  /*
 
584
    Here is where we open up the frm and pass it to archive to store
 
585
  */
 
586
  if ((frm_file= fopen(name_buff, "r")) > 0)
 
587
  {
 
588
    if (fstat(fileno(frm_file), &file_stat))
 
589
    {
 
590
      if ((uint64_t)file_stat.st_size > SIZE_MAX)
 
591
      {
 
592
        error= ENOMEM;
 
593
        goto error2;
 
594
      }
 
595
      frm_ptr= (unsigned char *)malloc((size_t)file_stat.st_size);
 
596
      if (frm_ptr)
 
597
      {
 
598
        size_t length_io;
 
599
        length_io= read(fileno(frm_file), frm_ptr, (size_t)file_stat.st_size);
 
600
 
 
601
        if (length_io != (size_t)file_stat.st_size)
 
602
        {
 
603
          free(frm_ptr);
 
604
          goto error2;
 
605
        }
 
606
 
 
607
        length_io= azwrite_frm(&create_stream, (char *)frm_ptr, (size_t)file_stat.st_size);
 
608
 
 
609
        if (length_io != (size_t)file_stat.st_size)
 
610
        {
 
611
          free(frm_ptr);
 
612
          goto error2;
 
613
        }
 
614
 
 
615
        free(frm_ptr);
 
616
      }
 
617
    }
 
618
    fclose(frm_file);
 
619
  }
 
620
 
 
621
  if (create_info->comment.str)
 
622
  {
 
623
    size_t write_length;
 
624
 
 
625
    write_length= azwrite_comment(&create_stream, create_info->comment.str,
 
626
                                  (unsigned int)create_info->comment.length);
 
627
 
 
628
    if (write_length == (size_t)create_info->comment.length)
 
629
      goto error2;
 
630
  }
 
631
 
 
632
  /*
 
633
    Yes you need to do this, because the starting value
 
634
    for the autoincrement may not be zero.
 
635
  */
 
636
  create_stream.auto_increment= stats.auto_increment_value ?
 
637
    stats.auto_increment_value - 1 : 0;
 
638
  if (azclose(&create_stream))
 
639
  {
 
640
    error= errno;
 
641
    goto error2;
 
642
  }
654
643
 
655
644
  return(0);
656
645
 
666
655
*/
667
656
int ha_archive::real_write_row(unsigned char *buf, azio_stream *writer)
668
657
{
669
 
  my_off_t written;
 
658
  off_t written;
670
659
  unsigned int r_pack_length;
671
660
 
672
661
  /* We pack the row for writing */
685
674
}
686
675
 
687
676
 
688
 
/* 
 
677
/*
689
678
  Calculate max length needed for row. This includes
690
679
  the bytes required for the length in the header.
691
680
*/
692
681
 
693
 
uint32_t ha_archive::max_row_length(const unsigned char *buf __attribute__((unused)))
 
682
uint32_t ha_archive::max_row_length(const unsigned char *)
694
683
{
695
684
  uint32_t length= (uint32_t)(table->getRecordLength() + table->sizeFields()*2);
696
685
  length+= ARCHIVE_ROW_HEADER_SIZE;
728
717
}
729
718
 
730
719
 
731
 
/* 
 
720
/*
732
721
  Look at ha_archive::open() for an explanation of the row format.
733
722
  Here we just write out the row.
734
723
 
735
724
  Wondering about start_bulk_insert()? We don't implement it for
736
725
  archive since it optimizes for lots of writes. The only save
737
 
  for implementing start_bulk_insert() is that we could skip 
 
726
  for implementing start_bulk_insert() is that we could skip
738
727
  setting dirty to true each time.
739
728
*/
740
729
int ha_archive::write_row(unsigned char *buf)
748
737
    return(HA_ERR_CRASHED_ON_USAGE);
749
738
 
750
739
  ha_statistic_increment(&SSV::ha_write_count);
751
 
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
752
 
    table->timestamp_field->set_time();
753
740
  pthread_mutex_lock(&share->mutex);
754
741
 
755
742
  if (share->archive_write_open == false)
767
754
      We don't support decremening auto_increment. They make the performance
768
755
      just cry.
769
756
    */
770
 
    if (temp_auto <= share->archive_write.auto_increment && 
 
757
    if (temp_auto <= share->archive_write.auto_increment &&
771
758
        mkey->flags & HA_NOSAME)
772
759
    {
773
760
      rc= HA_ERR_FOUND_DUPP_KEY;
774
761
      goto error;
775
762
    }
776
 
#ifdef DEAD_CODE
777
 
    /*
778
 
      Bad news, this will cause a search for the unique value which is very 
779
 
      expensive since we will have to do a table scan which will lock up 
780
 
      all other writers during this period. This could perhaps be optimized 
781
 
      in the future.
782
 
    */
783
 
    {
784
 
      /* 
785
 
        First we create a buffer that we can use for reading rows, and can pass
786
 
        to get_row().
787
 
      */
788
 
      if (!(read_buf= (unsigned char*) my_malloc(table->s->reclength, MYF(MY_WME))))
789
 
      {
790
 
        rc= HA_ERR_OUT_OF_MEM;
791
 
        goto error;
792
 
      }
793
 
       /* 
794
 
         All of the buffer must be written out or we won't see all of the
795
 
         data 
796
 
       */
797
 
      azflush(&(share->archive_write), Z_SYNC_FLUSH);
798
 
      /*
799
 
        Set the position of the local read thread to the beginning postion.
800
 
      */
801
 
      if (read_data_header(&archive))
802
 
      {
803
 
        rc= HA_ERR_CRASHED_ON_USAGE;
804
 
        goto error;
805
 
      }
806
 
 
807
 
      Field *mfield= table->next_number_field;
808
 
 
809
 
      while (!(get_row(&archive, read_buf)))
810
 
      {
811
 
        if (!memcmp(read_buf + mfield->offset(record),
812
 
                    table->next_number_field->ptr,
813
 
                    mfield->max_display_length()))
814
 
        {
815
 
          rc= HA_ERR_FOUND_DUPP_KEY;
816
 
          goto error;
817
 
        }
818
 
      }
819
 
    }
820
 
#endif
821
763
    else
822
764
    {
823
765
      if (temp_auto > share->archive_write.auto_increment)
841
783
}
842
784
 
843
785
 
844
 
void ha_archive::get_auto_increment(uint64_t offset __attribute__((unused)),
845
 
                                    uint64_t increment __attribute__((unused)),
846
 
                                    uint64_t nb_desired_values __attribute__((unused)),
847
 
                                    uint64_t *first_value __attribute__((unused)),
848
 
                                    uint64_t *nb_reserved_values __attribute__((unused)))
 
786
void ha_archive::get_auto_increment(uint64_t, uint64_t, uint64_t,
 
787
                                    uint64_t *first_value, uint64_t *nb_reserved_values)
849
788
{
850
789
  *nb_reserved_values= UINT64_MAX;
851
790
  *first_value= share->archive_write.auto_increment + 1;
852
791
}
853
792
 
854
793
/* Initialized at each key walk (called multiple times unlike rnd_init()) */
855
 
int ha_archive::index_init(uint32_t keynr, bool sorted __attribute__((unused)))
 
794
int ha_archive::index_init(uint32_t keynr, bool)
856
795
{
857
796
  active_index= keynr;
858
797
  return(0);
873
812
 
874
813
 
875
814
int ha_archive::index_read_idx(unsigned char *buf, uint32_t index, const unsigned char *key,
876
 
                               uint32_t key_len,
877
 
                               enum ha_rkey_function find_flag __attribute__((unused)))
 
815
                               uint32_t key_len, enum ha_rkey_function)
878
816
{
879
817
  int rc;
880
818
  bool found= 0;
905
843
}
906
844
 
907
845
 
908
 
int ha_archive::index_next(unsigned char * buf) 
909
 
 
846
int ha_archive::index_next(unsigned char * buf)
 
847
{
910
848
  bool found= 0;
911
849
 
912
850
  while (!(get_row(&archive, buf)))
918
856
    }
919
857
  }
920
858
 
921
 
  return(found ? 0 : HA_ERR_END_OF_FILE); 
 
859
  return(found ? 0 : HA_ERR_END_OF_FILE);
922
860
}
923
861
 
924
862
/*
946
884
 
947
885
 
948
886
/*
949
 
  This is the method that is used to read a row. It assumes that the row is 
 
887
  This is the method that is used to read a row. It assumes that the row is
950
888
  positioned where you want it.
951
889
*/
952
890
int ha_archive::get_row(azio_stream *file_to_read, unsigned char *buf)
969
907
  if (length > record_buffer->length)
970
908
  {
971
909
    unsigned char *newptr;
972
 
    if (!(newptr=(unsigned char*) my_realloc((unsigned char*) record_buffer->buffer, 
973
 
                                    length,
974
 
                                    MYF(MY_ALLOW_ZERO_PTR))))
 
910
    if (!(newptr= (unsigned char *)realloc(record_buffer->buffer, length)))
975
911
      return(1);
976
912
    record_buffer->buffer= newptr;
977
913
    record_buffer->length= length;
1018
954
}
1019
955
 
1020
956
 
1021
 
/* 
 
957
/*
1022
958
  Called during ORDER BY. Its position is either from being called sequentially
1023
959
  or by having had ha_archive::rnd_pos() called before it is called.
1024
960
*/
1050
986
  needed.
1051
987
*/
1052
988
 
1053
 
void ha_archive::position(const unsigned char *record __attribute__((unused)))
 
989
void ha_archive::position(const unsigned char *)
1054
990
{
1055
991
  my_store_ptr(ref, ref_length, current_position);
1056
992
  return;
1074
1010
}
1075
1011
 
1076
1012
/*
1077
 
  This method repairs the meta file. It does this by walking the datafile and 
 
1013
  This method repairs the meta file. It does this by walking the datafile and
1078
1014
  rewriting the meta file. Currently it does this by calling optimize with
1079
1015
  the extended flag.
1080
1016
*/
1081
 
int ha_archive::repair(THD* thd, HA_CHECK_OPT* check_opt)
 
1017
int ha_archive::repair(Session* session, HA_CHECK_OPT* check_opt)
1082
1018
{
1083
1019
  check_opt->flags= T_EXTEND;
1084
 
  int rc= optimize(thd, check_opt);
 
1020
  int rc= optimize(session, check_opt);
1085
1021
 
1086
1022
  if (rc)
1087
1023
    return(HA_ERR_CRASHED_ON_REPAIR);
1092
1028
 
1093
1029
/*
1094
1030
  The table can become fragmented if data was inserted, read, and then
1095
 
  inserted again. What we do is open up the file and recompress it completely. 
 
1031
  inserted again. What we do is open up the file and recompress it completely.
1096
1032
*/
1097
 
int ha_archive::optimize(THD* thd __attribute__((unused)),
1098
 
                         HA_CHECK_OPT* check_opt __attribute__((unused)))
 
1033
int ha_archive::optimize(Session *, HA_CHECK_OPT *)
1099
1034
{
1100
1035
  int rc= 0;
1101
1036
  azio_stream writer;
1111
1046
  }
1112
1047
 
1113
1048
  /* Lets create a file to contain the new data */
1114
 
  fn_format(writer_filename, share->table_name, "", ARN, 
 
1049
  fn_format(writer_filename, share->table_name.c_str(), "", ARN,
1115
1050
            MY_REPLACE_EXT | MY_UNPACK_FILENAME);
1116
1051
 
1117
 
  if (!(azopen(&writer, writer_filename, O_CREAT|O_RDWR|O_BINARY, AZ_METHOD_BLOCK)))
1118
 
    return(HA_ERR_CRASHED_ON_USAGE); 
 
1052
  if (!(azopen(&writer, writer_filename, O_CREAT|O_RDWR, AZ_METHOD_BLOCK)))
 
1053
    return(HA_ERR_CRASHED_ON_USAGE);
1119
1054
 
1120
 
  /* 
1121
 
    An extended rebuild is a lot more effort. We open up each row and re-record it. 
1122
 
    Any dead rows are removed (aka rows that may have been partially recorded). 
 
1055
  /*
 
1056
    An extended rebuild is a lot more effort. We open up each row and re-record it.
 
1057
    Any dead rows are removed (aka rows that may have been partially recorded).
1123
1058
 
1124
1059
    As of Archive format 3, this is the only type that is performed, before this
1125
1060
    version it was just done on T_EXTEND
1127
1062
  if (1)
1128
1063
  {
1129
1064
    /*
1130
 
      Now we will rewind the archive file so that we are positioned at the 
 
1065
      Now we will rewind the archive file so that we are positioned at the
1131
1066
      start of the file.
1132
1067
    */
1133
1068
    azflush(&archive, Z_SYNC_FLUSH);
1134
1069
    rc= read_data_header(&archive);
1135
1070
 
1136
 
    /* 
 
1071
    /*
1137
1072
      On success of writing out the new header, we now fetch each row and
1138
 
      insert it into the new archive file. 
 
1073
      insert it into the new archive file.
1139
1074
    */
1140
1075
    if (!rc)
1141
1076
    {
1177
1112
    {
1178
1113
      goto error;
1179
1114
    }
1180
 
  } 
 
1115
  }
1181
1116
 
1182
1117
  azclose(&writer);
1183
1118
  share->dirty= false;
1184
 
  
 
1119
 
1185
1120
  azclose(&archive);
1186
1121
 
1187
1122
  // make the file we just wrote be our data file
1192
1127
error:
1193
1128
  azclose(&writer);
1194
1129
 
1195
 
  return(rc); 
 
1130
  return(rc);
1196
1131
}
1197
1132
 
1198
 
/* 
 
1133
/*
1199
1134
  Below is an example of how to setup row level locking.
1200
1135
*/
1201
 
THR_LOCK_DATA **ha_archive::store_lock(THD *thd,
 
1136
THR_LOCK_DATA **ha_archive::store_lock(Session *session,
1202
1137
                                       THR_LOCK_DATA **to,
1203
1138
                                       enum thr_lock_type lock_type)
1204
1139
{
1207
1142
  else
1208
1143
    delayed_insert= false;
1209
1144
 
1210
 
  if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) 
 
1145
  if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
1211
1146
  {
1212
 
    /* 
 
1147
    /*
1213
1148
      Here is where we get into the guts of a row level lock.
1214
 
      If TL_UNLOCK is set 
 
1149
      If TL_UNLOCK is set
1215
1150
      If we are not doing a LOCK Table or DISCARD/IMPORT
1216
 
      TABLESPACE, then allow multiple writers 
 
1151
      TABLESPACE, then allow multiple writers
1217
1152
    */
1218
1153
 
1219
1154
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
1220
 
         lock_type <= TL_WRITE) && !thd_in_lock_tables(thd)
1221
 
        && !thd_tablespace_op(thd))
 
1155
         lock_type <= TL_WRITE) && !session_in_lock_tables(session)
 
1156
        && !session_tablespace_op(session))
1222
1157
      lock_type = TL_WRITE_ALLOW_WRITE;
1223
1158
 
1224
 
    /* 
 
1159
    /*
1225
1160
      In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
1226
1161
      MySQL would use the lock TL_READ_NO_INSERT on t2, and that
1227
1162
      would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
1228
1163
      to t2. Convert the lock to a normal read lock to allow
1229
 
      concurrent inserts to t2. 
 
1164
      concurrent inserts to t2.
1230
1165
    */
1231
1166
 
1232
 
    if (lock_type == TL_READ_NO_INSERT && !thd_in_lock_tables(thd)) 
 
1167
    if (lock_type == TL_READ_NO_INSERT && !session_in_lock_tables(session))
1233
1168
      lock_type = TL_READ;
1234
1169
 
1235
1170
    lock.type=lock_type;
1260
1195
*/
1261
1196
int ha_archive::info(uint32_t flag)
1262
1197
{
1263
 
  /* 
 
1198
  /*
1264
1199
    If dirty, we lock, and then reset/flush the data.
1265
1200
    I found that just calling azflush() doesn't always work.
1266
1201
  */
1278
1213
 
1279
1214
  }
1280
1215
 
1281
 
  /* 
 
1216
  /*
1282
1217
    This should be an accurate number now, though bulk and delayed inserts can
1283
1218
    cause the number to be inaccurate.
1284
1219
  */
1331
1266
}
1332
1267
 
1333
1268
 
1334
 
/* 
 
1269
/*
1335
1270
  Other side of start_bulk_insert, is end_bulk_insert. Here we turn off the bulk insert
1336
1271
  flag, and set the share dirty so that the next select will call sync for us.
1337
1272
*/
1344
1279
 
1345
1280
/*
1346
1281
  We cancel a truncate command. The only way to delete an archive table is to drop it.
1347
 
  This is done for security reasons. In a later version we will enable this by 
 
1282
  This is done for security reasons. In a later version we will enable this by
1348
1283
  allowing the user to select a different row format.
1349
1284
*/
1350
1285
int ha_archive::delete_all_rows()
1355
1290
/*
1356
1291
  We just return state if asked.
1357
1292
*/
1358
 
bool ha_archive::is_crashed() const 
 
1293
bool ha_archive::is_crashed() const
1359
1294
{
1360
 
  return(share->crashed); 
 
1295
  return(share->crashed);
1361
1296
}
1362
1297
 
1363
1298
/*
1364
1299
  Simple scan of the tables to make sure everything is ok.
1365
1300
*/
1366
1301
 
1367
 
int ha_archive::check(THD* thd,
1368
 
                      HA_CHECK_OPT* check_opt __attribute__((unused)))
 
1302
int ha_archive::check(Session* session, HA_CHECK_OPT *)
1369
1303
{
1370
1304
  int rc= 0;
1371
1305
  const char *old_proc_info;
1372
1306
  uint64_t x;
1373
1307
 
1374
 
  old_proc_info= thd_proc_info(thd, "Checking table");
 
1308
  old_proc_info= get_session_proc_info(session);
 
1309
  set_session_proc_info(session, "Checking table");
1375
1310
  /* Flush any waiting data */
1376
1311
  pthread_mutex_lock(&share->mutex);
1377
1312
  azflush(&(share->archive_write), Z_SYNC_FLUSH);
1378
1313
  pthread_mutex_unlock(&share->mutex);
1379
1314
 
1380
1315
  /*
1381
 
    Now we will rewind the archive file so that we are positioned at the 
 
1316
    Now we will rewind the archive file so that we are positioned at the
1382
1317
    start of the file.
1383
1318
  */
1384
1319
  init_archive_reader();
1392
1327
      break;
1393
1328
  }
1394
1329
 
1395
 
  thd_proc_info(thd, old_proc_info);
 
1330
  set_session_proc_info(session, old_proc_info);
1396
1331
 
1397
 
  if ((rc && rc != HA_ERR_END_OF_FILE))  
 
1332
  if ((rc && rc != HA_ERR_END_OF_FILE))
1398
1333
  {
1399
1334
    share->crashed= false;
1400
1335
    return(HA_ADMIN_CORRUPT);
1408
1343
/*
1409
1344
  Check and repair the table if needed.
1410
1345
*/
1411
 
bool ha_archive::check_and_repair(THD *thd) 
 
1346
bool ha_archive::check_and_repair(Session *session)
1412
1347
{
1413
1348
  HA_CHECK_OPT check_opt;
1414
1349
 
1415
1350
  check_opt.init();
1416
1351
 
1417
 
  return(repair(thd, &check_opt));
 
1352
  return(repair(session, &check_opt));
1418
1353
}
1419
1354
 
1420
 
archive_record_buffer *ha_archive::create_record_buffer(unsigned int length) 
 
1355
archive_record_buffer *ha_archive::create_record_buffer(unsigned int length)
1421
1356
{
1422
1357
  archive_record_buffer *r;
1423
 
  if (!(r= 
1424
 
        (archive_record_buffer*) my_malloc(sizeof(archive_record_buffer),
1425
 
                                           MYF(MY_WME))))
 
1358
  if (!(r= (archive_record_buffer*) malloc(sizeof(archive_record_buffer))))
1426
1359
  {
1427
1360
    return(NULL); /* purecov: inspected */
1428
1361
  }
1429
1362
  r->length= (int)length;
1430
1363
 
1431
 
  if (!(r->buffer= (unsigned char*) my_malloc(r->length,
1432
 
                                    MYF(MY_WME))))
 
1364
  if (!(r->buffer= (unsigned char*) malloc(r->length)))
1433
1365
  {
1434
1366
    free((char*) r);
1435
1367
    return(NULL); /* purecov: inspected */
1438
1370
  return(r);
1439
1371
}
1440
1372
 
1441
 
void ha_archive::destroy_record_buffer(archive_record_buffer *r) 
 
1373
void ha_archive::destroy_record_buffer(archive_record_buffer *r)
1442
1374
{
1443
1375
  free((char*) r->buffer);
1444
1376
  free((char*) r);
1455
1387
  NULL
1456
1388
};
1457
1389
 
1458
 
mysql_declare_plugin(archive)
 
1390
drizzle_declare_plugin(archive)
1459
1391
{
1460
 
  DRIZZLE_STORAGE_ENGINE_PLUGIN,
1461
1392
  "ARCHIVE",
1462
1393
  "3.5",
1463
1394
  "Brian Aker, MySQL AB",
1469
1400
  archive_system_variables,   /* system variables                */
1470
1401
  NULL                        /* config options                  */
1471
1402
}
1472
 
mysql_declare_plugin_end;
 
1403
drizzle_declare_plugin_end;
1473
1404