~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2003 MySQL AB
2
3
  This program is free software; you can redistribute it and/or modify
4
  it under the terms of the GNU General Public License as published by
5
  the Free Software Foundation; version 2 of the License.
6
7
  This program is distributed in the hope that it will be useful,
8
  but WITHOUT ANY WARRANTY; without even the implied warranty of
9
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
  GNU General Public License for more details.
11
12
  You should have received a copy of the GNU General Public License
13
  along with this program; if not, write to the Free Software
14
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
16
#ifdef USE_PRAGMA_IMPLEMENTATION
17
#pragma implementation        // gcc: Class implementation
18
#endif
19
212.5.39 by Monty Taylor
Phew. Moved my_base and my_global.
20
#include <drizzled/mysql_priv.h>
212.4.2 by Monty Taylor
Fixed the includes in places to make the myisam header file move work.
21
#include <storage/myisam/myisam.h>
1 by brian
clean slate
22
23
#include "ha_archive.h"
24
25
/*
26
  First, if you want to understand storage engines you should look at 
27
  ha_example.cc and ha_example.h. 
28
29
  This example was written as a test case for a customer who needed
30
  a storage engine without indexes that could compress data very well.
31
  So, welcome to a completely compressed storage engine. This storage
32
  engine only does inserts. No replace, deletes, or updates. All reads are 
33
  complete table scans. Compression is done through a combination of packing
34
  and making use of the zlib library
35
  
36
  We keep a file pointer open for each instance of ha_archive for each read
37
  but for writes we keep one open file handle just for that. We flush it
38
  only if we have a read occur. azip handles compressing lots of records
39
  at once much better then doing lots of little records between writes.
40
  It is possible to not lock on writes but this would then mean we couldn't
41
  handle bulk inserts as well (that is if someone was trying to read at
42
  the same time since we would want to flush).
43
44
  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 
51
  this point an error occurs and the user is told to rebuild the file.
52
  A rebuild scans the rows and rewrites the meta file. If corruption is found
53
  in the data file then the meta file is not repaired.
54
55
  At some point a recovery method for such a drastic case needs to be divised.
56
57
  Locks are row level, and you will get a consistant read. 
58
59
  For performance as far as table scans go it is quite fast. I don't have
60
  good numbers but locally it has out performed both Innodb and MyISAM. For
61
  Innodb the question will be if the table can be fit into the buffer
62
  pool. For MyISAM its a question of how much the file system caches the
63
  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. 
66
67
  Examples between MyISAM (packed) and Archive.
68
69
  Table with 76695844 identical rows:
70
  29680807 a_archive.ARZ
71
  920350317 a.MYD
72
73
74
  Table with 8991478 rows (all of Slashdot's comments):
75
  1922964506 comment_archive.ARZ
76
  2944970297 comment_text.MYD
77
78
79
  TODO:
80
   Allow users to set compression level.
81
   Allow adjustable block size.
82
   Implement versioning, should be easy.
83
   Allow for errors, find a way to mark bad rows.
84
   Add optional feature so that rows can be flushed at interval (which will cause less
85
     compression but may speed up ordered searches).
86
   Checkpoint the meta file to allow for faster rebuilds.
87
   Option to allow for dirty reads, this would lower the sync calls, which would make
88
     inserts a lot faster, but would mean highly arbitrary reads.
89
90
    -Brian
91
*/
92
93
/* Variables for archive share methods */
94
pthread_mutex_t archive_mutex;
95
static HASH archive_open_tables;
96
static unsigned int global_version;
97
98
/* The file extension */
99
#define ARZ ".ARZ"               // The data file
100
#define ARN ".ARN"               // Files used during an optimize call
101
#define ARM ".ARM"               // Meta file (deprecated)
102
103
/*
104
  uchar + uchar
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
                     uchar **frmblob, 
116
                     size_t *frmlen);
117
163 by Brian Aker
Merge Monty's code.
118
static my_bool archive_use_aio= false;
1 by brian
clean slate
119
120
/*
121
  Number of rows that will force a bulk insert.
122
*/
123
#define ARCHIVE_MIN_ROWS_TO_USE_BULK_INSERT 2
124
125
/*
126
  Size of header used for row
127
*/
128
#define ARCHIVE_ROW_HEADER_SIZE 4
129
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 uchar* archive_get_key(ARCHIVE_SHARE *share, size_t *length,
141
                             my_bool not_used __attribute__((unused)))
142
{
143
  *length=share->table_name_length;
144
  return (uchar*) share->table_name;
145
}
146
147
148
/*
149
  Initialize the archive handler.
150
151
  SYNOPSIS
152
    archive_db_init()
153
    void *
154
155
  RETURN
163 by Brian Aker
Merge Monty's code.
156
    false       OK
157
    true        Error
1 by brian
clean slate
158
*/
159
160
int archive_db_init(void *p)
161
{
162
  handlerton *archive_hton;
163
164
  archive_hton= (handlerton *)p;
165
  archive_hton->state= SHOW_OPTION_YES;
166
  archive_hton->db_type= DB_TYPE_ARCHIVE_DB;
167
  archive_hton->create= archive_create_handler;
168
  archive_hton->flags= HTON_NO_FLAGS;
169
  archive_hton->discover= archive_discover;
170
171
  /* When the engine starts up set the first version */
172
  global_version= 1;
173
174
  if (pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST))
175
    goto error;
176
  if (hash_init(&archive_open_tables, system_charset_info, 32, 0, 0,
177
                (hash_get_key) archive_get_key, 0, 0))
178
  {
179
    VOID(pthread_mutex_destroy(&archive_mutex));
180
  }
181
  else
182
  {
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
183
    return(false);
1 by brian
clean slate
184
  }
185
error:
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
186
  return(true);
1 by brian
clean slate
187
}
188
189
/*
190
  Release the archive handler.
191
192
  SYNOPSIS
193
    archive_db_done()
194
    void
195
196
  RETURN
163 by Brian Aker
Merge Monty's code.
197
    false       OK
1 by brian
clean slate
198
*/
199
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
200
int archive_db_done(void *p __attribute__((unused)))
1 by brian
clean slate
201
{
202
  hash_free(&archive_open_tables);
203
  VOID(pthread_mutex_destroy(&archive_mutex));
204
205
  return 0;
206
}
207
208
209
ha_archive::ha_archive(handlerton *hton, TABLE_SHARE *table_arg)
210
  :handler(hton, table_arg), delayed_insert(0), bulk_insert(0)
211
{
212
  /* Set our original buffer from pre-allocated memory */
213
  buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info);
214
215
  /* The size of the offset value we will use for position() */
216
  ref_length= sizeof(my_off_t);
163 by Brian Aker
Merge Monty's code.
217
  archive_reader_open= false;
1 by brian
clean slate
218
}
219
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
220
int archive_discover(handlerton *hton __attribute__((unused)),
221
                     THD* thd __attribute__((unused)),
53.2.32 by Monty Taylor
First large swath at getting handler stuff clean.
222
                     const char *db,
1 by brian
clean slate
223
                     const char *name,
53.2.32 by Monty Taylor
First large swath at getting handler stuff clean.
224
                     uchar **frmblob,
1 by brian
clean slate
225
                     size_t *frmlen)
226
{
227
  azio_stream frm_stream;
228
  char az_file[FN_REFLEN];
229
  char *frm_ptr;
15 by brian
Fix for stat, NETWARE removal
230
  struct stat file_stat; 
1 by brian
clean slate
231
232
  fn_format(az_file, name, db, ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME);
233
15 by brian
Fix for stat, NETWARE removal
234
  if (stat(az_file, &file_stat))
1 by brian
clean slate
235
    goto err;
236
237
  if (!(azopen(&frm_stream, az_file, O_RDONLY|O_BINARY, AZ_METHOD_BLOCK)))
238
  {
239
    if (errno == EROFS || errno == EACCES)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
240
      return(my_errno= errno);
241
    return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
242
  }
243
244
  if (frm_stream.frm_length == 0)
245
    goto err;
246
247
  frm_ptr= (char *)my_malloc(sizeof(char) * frm_stream.frm_length, MYF(0));
248
  azread_frm(&frm_stream, frm_ptr);
249
  azclose(&frm_stream);
250
251
  *frmlen= frm_stream.frm_length;
252
  *frmblob= (uchar*) frm_ptr;
253
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
254
  return(0);
1 by brian
clean slate
255
err:
256
  my_errno= 0;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
257
  return(1);
1 by brian
clean slate
258
}
259
260
/*
261
  This method reads the header of a datafile and returns whether or not it was successful.
262
*/
263
int ha_archive::read_data_header(azio_stream *file_to_read)
264
{
265
  if (azread_init(file_to_read) == -1)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
266
    return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
267
268
  if (file_to_read->version >= 3)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
269
    return(0);
1 by brian
clean slate
270
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
271
  return(1);
1 by brian
clean slate
272
}
273
274
275
/*
276
  We create the shared memory space that we will use for the open table. 
277
  No matter what we try to get or create a share. This is so that a repair
278
  table operation can occur. 
279
280
  See ha_example.cc for a longer description.
281
*/
282
ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, int *rc)
283
{
284
  uint length;
285
286
  pthread_mutex_lock(&archive_mutex);
287
  length=(uint) strlen(table_name);
288
289
  if (!(share=(ARCHIVE_SHARE*) hash_search(&archive_open_tables,
290
                                           (uchar*) table_name,
291
                                           length)))
292
  {
293
    char *tmp_name;
294
    azio_stream archive_tmp;
295
296
    if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
297
                          &share, sizeof(*share),
298
                          &tmp_name, length+1,
299
                          NullS)) 
300
    {
301
      pthread_mutex_unlock(&archive_mutex);
302
      *rc= HA_ERR_OUT_OF_MEM;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
303
      return(NULL);
1 by brian
clean slate
304
    }
305
306
    share->use_count= 0;
307
    share->table_name_length= length;
308
    share->table_name= tmp_name;
163 by Brian Aker
Merge Monty's code.
309
    share->crashed= false;
310
    share->archive_write_open= false;
1 by brian
clean slate
311
    fn_format(share->data_file_name, table_name, "",
312
              ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME);
313
    strmov(share->table_name, table_name);
314
    /*
315
      We will use this lock for rows.
316
    */
317
    VOID(pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST));
318
    
319
    /*
320
      We read the meta file, but do not mark it dirty. Since we are not
321
      doing a write we won't mark it dirty (and we won't open it for
322
      anything but reading... open it for write and we will generate null
323
      compression writes).
324
    */
325
    if (!(azopen(&archive_tmp, share->data_file_name, O_RDONLY|O_BINARY,
326
                 AZ_METHOD_BLOCK)))
327
    {
328
      VOID(pthread_mutex_destroy(&share->mutex));
329
      free(share);
330
      pthread_mutex_unlock(&archive_mutex);
331
      *rc= HA_ERR_CRASHED_ON_REPAIR;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
332
      return(NULL);
1 by brian
clean slate
333
    }
334
    stats.auto_increment_value= archive_tmp.auto_increment + 1;
335
    share->rows_recorded= (ha_rows)archive_tmp.rows;
336
    share->crashed= archive_tmp.dirty;
337
    if (share->version < global_version)
338
    {
339
      share->version_rows= share->rows_recorded;
340
      share->version= global_version;
341
    }
342
    azclose(&archive_tmp);
343
344
    VOID(my_hash_insert(&archive_open_tables, (uchar*) share));
345
    thr_lock_init(&share->lock);
346
  }
347
  share->use_count++;
348
  if (share->crashed)
349
    *rc= HA_ERR_CRASHED_ON_USAGE;
350
  pthread_mutex_unlock(&archive_mutex);
351
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
352
  return(share);
1 by brian
clean slate
353
}
354
355
356
/* 
357
  Free the share.
358
  See ha_example.cc for a description.
359
*/
360
int ha_archive::free_share()
361
{
362
  int rc= 0;
363
364
  pthread_mutex_lock(&archive_mutex);
365
  if (!--share->use_count)
366
  {
367
    hash_delete(&archive_open_tables, (uchar*) share);
368
    thr_lock_delete(&share->lock);
369
    VOID(pthread_mutex_destroy(&share->mutex));
370
    /* 
371
      We need to make sure we don't reset the crashed state.
372
      If we open a crashed file, wee need to close it as crashed unless
373
      it has been repaired.
374
      Since we will close the data down after this, we go on and count
375
      the flush on close;
376
    */
163 by Brian Aker
Merge Monty's code.
377
    if (share->archive_write_open == true)
1 by brian
clean slate
378
    {
379
      if (azclose(&(share->archive_write)))
380
        rc= 1;
381
    }
382
    my_free((uchar*) share, MYF(0));
383
  }
384
  pthread_mutex_unlock(&archive_mutex);
385
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
386
  return(rc);
1 by brian
clean slate
387
}
388
389
int ha_archive::init_archive_writer()
390
{
391
  /* 
392
    It is expensive to open and close the data files and since you can't have
393
    a gzip file that can be both read and written we keep a writer open
394
    that is shared amoung all open tables.
395
  */
396
  if (!(azopen(&(share->archive_write), share->data_file_name, 
397
               O_RDWR|O_BINARY, AZ_METHOD_BLOCK)))
398
  {
163 by Brian Aker
Merge Monty's code.
399
    share->crashed= true;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
400
    return(1);
1 by brian
clean slate
401
  }
163 by Brian Aker
Merge Monty's code.
402
  share->archive_write_open= true;
1 by brian
clean slate
403
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
404
  return(0);
1 by brian
clean slate
405
}
406
407
408
/* 
409
  No locks are required because it is associated with just one handler instance
410
*/
411
int ha_archive::init_archive_reader()
412
{
413
  /* 
414
    It is expensive to open and close the data files and since you can't have
415
    a gzip file that can be both read and written we keep a writer open
416
    that is shared amoung all open tables.
417
  */
163 by Brian Aker
Merge Monty's code.
418
  if (archive_reader_open == false)
1 by brian
clean slate
419
  {
420
    az_method method;
421
422
    switch (archive_use_aio)
423
    {
163 by Brian Aker
Merge Monty's code.
424
    case false:
1 by brian
clean slate
425
      method= AZ_METHOD_BLOCK;
426
      break;
163 by Brian Aker
Merge Monty's code.
427
    case true:
1 by brian
clean slate
428
      method= AZ_METHOD_AIO;
429
      break;
430
    default:
431
      method= AZ_METHOD_BLOCK;
432
    }
433
    if (!(azopen(&archive, share->data_file_name, O_RDONLY|O_BINARY, 
434
                 method)))
435
    {
163 by Brian Aker
Merge Monty's code.
436
      share->crashed= true;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
437
      return(1);
1 by brian
clean slate
438
    }
163 by Brian Aker
Merge Monty's code.
439
    archive_reader_open= true;
1 by brian
clean slate
440
  }
441
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
442
  return(0);
1 by brian
clean slate
443
}
444
445
446
/*
447
  We just implement one additional file extension.
448
*/
449
static const char *ha_archive_exts[] = {
450
  ARZ,
451
  NullS
452
};
453
454
const char **ha_archive::bas_ext() const
455
{
456
  return ha_archive_exts;
457
}
458
459
460
/* 
461
  When opening a file we:
462
  Create/get our shared structure.
463
  Init out lock.
464
  We open the file we will read from.
465
*/
53.2.32 by Monty Taylor
First large swath at getting handler stuff clean.
466
int ha_archive::open(const char *name,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
467
                     int mode __attribute__((unused)),
53.2.32 by Monty Taylor
First large swath at getting handler stuff clean.
468
                     uint open_options)
1 by brian
clean slate
469
{
470
  int rc= 0;
471
  share= get_share(name, &rc);
472
473
  if (rc == HA_ERR_CRASHED_ON_USAGE && !(open_options & HA_OPEN_FOR_REPAIR))
474
  {
475
    /* purecov: begin inspected */
476
    free_share();
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
477
    return(rc);
1 by brian
clean slate
478
    /* purecov: end */    
479
  }
480
  else if (rc == HA_ERR_OUT_OF_MEM)
481
  {
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
482
    return(rc);
1 by brian
clean slate
483
  }
484
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
485
  assert(share);
1 by brian
clean slate
486
487
  record_buffer= create_record_buffer(table->s->reclength + 
488
                                      ARCHIVE_ROW_HEADER_SIZE);
489
490
  if (!record_buffer)
491
  {
492
    free_share();
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
493
    return(HA_ERR_OUT_OF_MEM);
1 by brian
clean slate
494
  }
495
496
  thr_lock_data_init(&share->lock, &lock, NULL);
497
498
  if (rc == HA_ERR_CRASHED_ON_USAGE && open_options & HA_OPEN_FOR_REPAIR)
499
  {
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
500
    return(0);
1 by brian
clean slate
501
  }
502
  else
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
503
    return(rc);
1 by brian
clean slate
504
}
505
506
507
/*
508
  Closes the file.
509
510
  SYNOPSIS
511
    close();
512
  
513
  IMPLEMENTATION:
514
515
  We first close this storage engines file handle to the archive and
516
  then remove our reference count to the table (and possibly free it
517
  as well).
518
519
  RETURN
520
    0  ok
521
    1  Error
522
*/
523
524
int ha_archive::close(void)
525
{
526
  int rc= 0;
527
528
  destroy_record_buffer(record_buffer);
529
530
  /* First close stream */
163 by Brian Aker
Merge Monty's code.
531
  if (archive_reader_open == true)
1 by brian
clean slate
532
  {
533
    if (azclose(&archive))
534
      rc= 1;
535
  }
536
  /* then also close share */
537
  rc|= free_share();
538
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
539
  return(rc);
1 by brian
clean slate
540
}
541
542
543
/*
544
  We create our data file here. The format is pretty simple. 
545
  You can read about the format of the data file above.
546
  Unlike other storage engines we do not "pack" our data. Since we 
547
  are about to do a general compression, packing would just be a waste of 
548
  CPU time. If the table has blobs they are written after the row in the order 
549
  of creation.
550
*/
551
552
int ha_archive::create(const char *name, TABLE *table_arg,
553
                       HA_CREATE_INFO *create_info)
554
{
555
  char name_buff[FN_REFLEN];
556
  char linkname[FN_REFLEN];
557
  int error;
558
  azio_stream create_stream;            /* Archive file we are working with */
559
  File frm_file;                   /* File handler for readers */
15 by brian
Fix for stat, NETWARE removal
560
  struct stat file_stat;
1 by brian
clean slate
561
  uchar *frm_ptr;
562
563
  stats.auto_increment_value= create_info->auto_increment_value;
564
565
  for (uint key= 0; key < table_arg->s->keys; key++)
566
  {
567
    KEY *pos= table_arg->key_info+key;
568
    KEY_PART_INFO *key_part=     pos->key_part;
569
    KEY_PART_INFO *key_part_end= key_part + pos->key_parts;
570
571
    for (; key_part != key_part_end; key_part++)
572
    {
573
      Field *field= key_part->field;
574
575
      if (!(field->flags & AUTO_INCREMENT_FLAG))
576
      {
577
        error= -1;
578
        goto error;
579
      }
580
    }
581
  }
582
583
  /* 
584
    We reuse name_buff since it is available.
585
  */
586
  if (create_info->data_file_name && create_info->data_file_name[0] != '#')
587
  {
588
    fn_format(name_buff, create_info->data_file_name, "", ARZ,
589
              MY_REPLACE_EXT | MY_UNPACK_FILENAME);
590
    fn_format(linkname, name, "", ARZ,
591
              MY_REPLACE_EXT | MY_UNPACK_FILENAME);
592
  }
593
  else
594
  {
595
    fn_format(name_buff, name, "", ARZ,
596
              MY_REPLACE_EXT | MY_UNPACK_FILENAME);
597
    linkname[0]= 0;
598
  }
599
600
  /*
601
    There is a chance that the file was "discovered". In this case
602
    just use whatever file is there.
603
  */
15 by brian
Fix for stat, NETWARE removal
604
  if (!stat(name_buff, &file_stat))
1 by brian
clean slate
605
  {
606
    my_errno= 0;
607
    if (!(azopen(&create_stream, name_buff, O_CREAT|O_RDWR|O_BINARY,
608
                 AZ_METHOD_BLOCK)))
609
    {
610
      error= errno;
611
      goto error2;
612
    }
613
614
    if (linkname[0])
615
      my_symlink(name_buff, linkname, MYF(0));
616
    fn_format(name_buff, name, "", ".frm",
617
              MY_REPLACE_EXT | MY_UNPACK_FILENAME);
618
619
    /*
620
      Here is where we open up the frm and pass it to archive to store 
621
    */
622
    if ((frm_file= my_open(name_buff, O_RDONLY, MYF(0))) > 0)
623
    {
15 by brian
Fix for stat, NETWARE removal
624
      if (fstat(frm_file, &file_stat))
1 by brian
clean slate
625
      {
626
        frm_ptr= (uchar *)my_malloc(sizeof(uchar) * file_stat.st_size, MYF(0));
627
        if (frm_ptr)
628
        {
629
          my_read(frm_file, frm_ptr, file_stat.st_size, MYF(0));
630
          azwrite_frm(&create_stream, (char *)frm_ptr, file_stat.st_size);
631
          my_free((uchar*)frm_ptr, MYF(0));
632
        }
633
      }
634
      my_close(frm_file, MYF(0));
635
    }
636
637
    if (create_info->comment.str)
638
      azwrite_comment(&create_stream, create_info->comment.str, 
639
                      (unsigned int)create_info->comment.length);
640
641
    /* 
642
      Yes you need to do this, because the starting value 
643
      for the autoincrement may not be zero.
644
    */
645
    create_stream.auto_increment= stats.auto_increment_value ?
646
                                    stats.auto_increment_value - 1 : 0;
647
    if (azclose(&create_stream))
648
    {
649
      error= errno;
650
      goto error2;
651
    }
652
  }
653
  else
654
    my_errno= 0;
655
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
656
  return(0);
1 by brian
clean slate
657
658
error2:
659
  delete_table(name);
660
error:
661
  /* Return error number, if we got one */
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
662
  return(error ? error : -1);
1 by brian
clean slate
663
}
664
665
/*
666
  This is where the actual row is written out.
667
*/
668
int ha_archive::real_write_row(uchar *buf, azio_stream *writer)
669
{
670
  my_off_t written;
671
  unsigned int r_pack_length;
672
673
  /* We pack the row for writing */
674
  r_pack_length= pack_row(buf);
675
676
  written= azwrite_row(writer, record_buffer->buffer, r_pack_length);
677
  if (written != r_pack_length)
678
  {
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
679
    return(-1);
1 by brian
clean slate
680
  }
681
682
  if (!delayed_insert || !bulk_insert)
163 by Brian Aker
Merge Monty's code.
683
    share->dirty= true;
1 by brian
clean slate
684
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
685
  return(0);
1 by brian
clean slate
686
}
687
688
689
/* 
690
  Calculate max length needed for row. This includes
691
  the bytes required for the length in the header.
692
*/
693
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
694
uint32_t ha_archive::max_row_length(const uchar *buf __attribute__((unused)))
1 by brian
clean slate
695
{
205 by Brian Aker
uint32 -> uin32_t
696
  uint32_t length= (uint32_t)(table->s->reclength + table->s->fields*2);
1 by brian
clean slate
697
  length+= ARCHIVE_ROW_HEADER_SIZE;
698
699
  uint *ptr, *end;
700
  for (ptr= table->s->blob_field, end=ptr + table->s->blob_fields ;
701
       ptr != end ;
702
       ptr++)
703
  {
704
      length += 2 + ((Field_blob*)table->field[*ptr])->get_length();
705
  }
706
707
  return length;
708
}
709
710
711
unsigned int ha_archive::pack_row(uchar *record)
712
{
713
  uchar *ptr;
714
715
  if (fix_rec_buff(max_row_length(record)))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
716
    return(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
1 by brian
clean slate
717
718
  /* Copy null bits */
719
  memcpy(record_buffer->buffer, record, table->s->null_bytes);
720
  ptr= record_buffer->buffer + table->s->null_bytes;
721
722
  for (Field **field=table->field ; *field ; field++)
723
  {
724
    if (!((*field)->is_null()))
725
      ptr= (*field)->pack(ptr, record + (*field)->offset(record));
726
  }
727
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
728
  return((unsigned int) (ptr - record_buffer->buffer));
1 by brian
clean slate
729
}
730
731
732
/* 
733
  Look at ha_archive::open() for an explanation of the row format.
734
  Here we just write out the row.
735
736
  Wondering about start_bulk_insert()? We don't implement it for
737
  archive since it optimizes for lots of writes. The only save
738
  for implementing start_bulk_insert() is that we could skip 
739
  setting dirty to true each time.
740
*/
741
int ha_archive::write_row(uchar *buf)
742
{
743
  int rc;
744
  uchar *read_buf= NULL;
745
  uint64_t temp_auto;
746
  uchar *record=  table->record[0];
747
748
  if (share->crashed)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
749
    return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
750
751
  ha_statistic_increment(&SSV::ha_write_count);
752
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
753
    table->timestamp_field->set_time();
754
  pthread_mutex_lock(&share->mutex);
755
163 by Brian Aker
Merge Monty's code.
756
  if (share->archive_write_open == false)
1 by brian
clean slate
757
    if (init_archive_writer())
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
758
      return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
759
760
761
  if (table->next_number_field && record == table->record[0])
762
  {
763
    KEY *mkey= &table->s->key_info[0]; // We only support one key right now
764
    update_auto_increment();
765
    temp_auto= table->next_number_field->val_int();
766
767
    /*
768
      We don't support decremening auto_increment. They make the performance
769
      just cry.
770
    */
771
    if (temp_auto <= share->archive_write.auto_increment && 
772
        mkey->flags & HA_NOSAME)
773
    {
774
      rc= HA_ERR_FOUND_DUPP_KEY;
775
      goto error;
776
    }
777
#ifdef DEAD_CODE
778
    /*
779
      Bad news, this will cause a search for the unique value which is very 
780
      expensive since we will have to do a table scan which will lock up 
781
      all other writers during this period. This could perhaps be optimized 
782
      in the future.
783
    */
784
    {
785
      /* 
786
        First we create a buffer that we can use for reading rows, and can pass
787
        to get_row().
788
      */
789
      if (!(read_buf= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME))))
790
      {
791
        rc= HA_ERR_OUT_OF_MEM;
792
        goto error;
793
      }
794
       /* 
795
         All of the buffer must be written out or we won't see all of the
796
         data 
797
       */
798
      azflush(&(share->archive_write), Z_SYNC_FLUSH);
799
      /*
800
        Set the position of the local read thread to the beginning postion.
801
      */
802
      if (read_data_header(&archive))
803
      {
804
        rc= HA_ERR_CRASHED_ON_USAGE;
805
        goto error;
806
      }
807
808
      Field *mfield= table->next_number_field;
809
810
      while (!(get_row(&archive, read_buf)))
811
      {
812
        if (!memcmp(read_buf + mfield->offset(record),
813
                    table->next_number_field->ptr,
814
                    mfield->max_display_length()))
815
        {
816
          rc= HA_ERR_FOUND_DUPP_KEY;
817
          goto error;
818
        }
819
      }
820
    }
821
#endif
822
    else
823
    {
824
      if (temp_auto > share->archive_write.auto_increment)
825
        stats.auto_increment_value=
826
          (share->archive_write.auto_increment= temp_auto) + 1;
827
    }
828
  }
829
830
  /*
831
    Notice that the global auto_increment has been increased.
832
    In case of a failed row write, we will never try to reuse the value.
833
  */
834
  share->rows_recorded++;
835
  rc= real_write_row(buf,  &(share->archive_write));
836
error:
837
  pthread_mutex_unlock(&share->mutex);
838
  if (read_buf)
839
    my_free((uchar*) read_buf, MYF(0));
840
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
841
  return(rc);
1 by brian
clean slate
842
}
843
844
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
845
void ha_archive::get_auto_increment(uint64_t offset __attribute__((unused)),
846
                                    uint64_t increment __attribute__((unused)),
847
                                    uint64_t nb_desired_values __attribute__((unused)),
848
                                    uint64_t *first_value __attribute__((unused)),
849
                                    uint64_t *nb_reserved_values __attribute__((unused)))
1 by brian
clean slate
850
{
163 by Brian Aker
Merge Monty's code.
851
  *nb_reserved_values= UINT64_MAX;
1 by brian
clean slate
852
  *first_value= share->archive_write.auto_increment + 1;
853
}
854
855
/* Initialized at each key walk (called multiple times unlike rnd_init()) */
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
856
int ha_archive::index_init(uint keynr, bool sorted __attribute__((unused)))
1 by brian
clean slate
857
{
858
  active_index= keynr;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
859
  return(0);
1 by brian
clean slate
860
}
861
862
863
/*
864
  No indexes, so if we get a request for an index search since we tell
865
  the optimizer that we have unique indexes, we scan
866
*/
867
int ha_archive::index_read(uchar *buf, const uchar *key,
868
                             uint key_len, enum ha_rkey_function find_flag)
869
{
870
  int rc;
871
  rc= index_read_idx(buf, active_index, key, key_len, find_flag);
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
872
  return(rc);
1 by brian
clean slate
873
}
874
875
876
int ha_archive::index_read_idx(uchar *buf, uint index, const uchar *key,
53.2.32 by Monty Taylor
First large swath at getting handler stuff clean.
877
                               uint key_len,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
878
                               enum ha_rkey_function find_flag __attribute__((unused)))
1 by brian
clean slate
879
{
880
  int rc;
881
  bool found= 0;
882
  KEY *mkey= &table->s->key_info[index];
883
  current_k_offset= mkey->key_part->offset;
884
  current_key= key;
885
  current_key_len= key_len;
886
163 by Brian Aker
Merge Monty's code.
887
  rc= rnd_init(true);
1 by brian
clean slate
888
889
  if (rc)
890
    goto error;
891
892
  while (!(get_row(&archive, buf)))
893
  {
894
    if (!memcmp(current_key, buf + current_k_offset, current_key_len))
895
    {
896
      found= 1;
897
      break;
898
    }
899
  }
900
901
  if (found)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
902
    return(0);
1 by brian
clean slate
903
904
error:
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
905
  return(rc ? rc : HA_ERR_END_OF_FILE);
1 by brian
clean slate
906
}
907
908
909
int ha_archive::index_next(uchar * buf) 
910
{ 
911
  bool found= 0;
912
913
  while (!(get_row(&archive, buf)))
914
  {
915
    if (!memcmp(current_key, buf+current_k_offset, current_key_len))
916
    {
917
      found= 1;
918
      break;
919
    }
920
  }
921
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
922
  return(found ? 0 : HA_ERR_END_OF_FILE); 
1 by brian
clean slate
923
}
924
925
/*
926
  All calls that need to scan the table start with this method. If we are told
927
  that it is a table scan we rewind the file to the beginning, otherwise
928
  we assume the position will be set.
929
*/
930
931
int ha_archive::rnd_init(bool scan)
932
{
933
  if (share->crashed)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
934
      return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
935
936
  init_archive_reader();
937
938
  /* We rewind the file so that we can read from the beginning if scan */
939
  if (scan)
940
  {
941
    if (read_data_header(&archive))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
942
      return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
943
  }
944
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
945
  return(0);
1 by brian
clean slate
946
}
947
948
949
/*
950
  This is the method that is used to read a row. It assumes that the row is 
951
  positioned where you want it.
952
*/
953
int ha_archive::get_row(azio_stream *file_to_read, uchar *buf)
954
{
955
  int rc;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
956
1 by brian
clean slate
957
  if (file_to_read->version == ARCHIVE_VERSION)
958
    rc= get_row_version3(file_to_read, buf);
959
  else
960
    rc= -1;
961
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
962
  return(rc);
1 by brian
clean slate
963
}
964
965
/* Reallocate buffer if needed */
966
bool ha_archive::fix_rec_buff(unsigned int length)
967
{
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
968
  assert(record_buffer->buffer);
1 by brian
clean slate
969
970
  if (length > record_buffer->length)
971
  {
972
    uchar *newptr;
973
    if (!(newptr=(uchar*) my_realloc((uchar*) record_buffer->buffer, 
974
                                    length,
975
				    MYF(MY_ALLOW_ZERO_PTR))))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
976
      return(1);
1 by brian
clean slate
977
    record_buffer->buffer= newptr;
978
    record_buffer->length= length;
979
  }
980
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
981
  assert(length <= record_buffer->length);
1 by brian
clean slate
982
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
983
  return(0);
1 by brian
clean slate
984
}
985
986
int ha_archive::unpack_row(azio_stream *file_to_read, uchar *record)
987
{
988
  unsigned int read;
989
  int error;
990
  const uchar *ptr;
991
992
  read= azread_row(file_to_read, &error);
993
  ptr= (const uchar *)file_to_read->row_ptr;
994
995
  if (error || read == 0)
996
  {
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
997
    return(-1);
1 by brian
clean slate
998
  }
999
1000
  /* Copy null bits */
1001
  memcpy(record, ptr, table->s->null_bytes);
1002
  ptr+= table->s->null_bytes;
1003
  for (Field **field=table->field ; *field ; field++)
1004
  {
1005
    if (!((*field)->is_null()))
1006
    {
1007
      ptr= (*field)->unpack(record + (*field)->offset(table->record[0]), ptr);
1008
    }
1009
  }
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1010
  return(0);
1 by brian
clean slate
1011
}
1012
1013
1014
int ha_archive::get_row_version3(azio_stream *file_to_read, uchar *buf)
1015
{
1016
  int returnable= unpack_row(file_to_read, buf);
1017
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1018
  return(returnable);
1 by brian
clean slate
1019
}
1020
1021
1022
/* 
1023
  Called during ORDER BY. Its position is either from being called sequentially
1024
  or by having had ha_archive::rnd_pos() called before it is called.
1025
*/
1026
1027
int ha_archive::rnd_next(uchar *buf)
1028
{
1029
  int rc;
1030
1031
  if (share->crashed)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1032
      return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
1033
1034
  if (!scan_rows)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1035
    return(HA_ERR_END_OF_FILE);
1 by brian
clean slate
1036
  scan_rows--;
1037
1038
  ha_statistic_increment(&SSV::ha_read_rnd_next_count);
1039
  current_position= aztell(&archive);
1040
  rc= get_row(&archive, buf);
1041
1042
  table->status=rc ? STATUS_NOT_FOUND: 0;
1043
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1044
  return(rc);
1 by brian
clean slate
1045
}
1046
1047
1048
/*
1049
  Thanks to the table flag HA_REC_NOT_IN_SEQ this will be called after
1050
  each call to ha_archive::rnd_next() if an ordering of the rows is
1051
  needed.
1052
*/
1053
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
1054
void ha_archive::position(const uchar *record __attribute__((unused)))
1 by brian
clean slate
1055
{
1056
  my_store_ptr(ref, ref_length, current_position);
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1057
  return;
1 by brian
clean slate
1058
}
1059
1060
1061
/*
1062
  This is called after a table scan for each row if the results of the
1063
  scan need to be ordered. It will take *pos and use it to move the
1064
  cursor in the file so that the next row that is called is the
1065
  correctly ordered row.
1066
*/
1067
1068
int ha_archive::rnd_pos(uchar * buf, uchar *pos)
1069
{
1070
  ha_statistic_increment(&SSV::ha_read_rnd_next_count);
1071
  current_position= (my_off_t)my_get_ptr(pos, ref_length);
1072
  if (azseek(&archive, (size_t)current_position, SEEK_SET) == (size_t)(-1L))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1073
    return(HA_ERR_CRASHED_ON_USAGE);
1074
  return(get_row(&archive, buf));
1 by brian
clean slate
1075
}
1076
1077
/*
1078
  This method repairs the meta file. It does this by walking the datafile and 
1079
  rewriting the meta file. Currently it does this by calling optimize with
1080
  the extended flag.
1081
*/
1082
int ha_archive::repair(THD* thd, HA_CHECK_OPT* check_opt)
1083
{
1084
  check_opt->flags= T_EXTEND;
1085
  int rc= optimize(thd, check_opt);
1086
1087
  if (rc)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1088
    return(HA_ERR_CRASHED_ON_REPAIR);
1 by brian
clean slate
1089
163 by Brian Aker
Merge Monty's code.
1090
  share->crashed= false;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1091
  return(0);
1 by brian
clean slate
1092
}
1093
1094
/*
1095
  The table can become fragmented if data was inserted, read, and then
1096
  inserted again. What we do is open up the file and recompress it completely. 
1097
*/
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
1098
int ha_archive::optimize(THD* thd __attribute__((unused)),
1099
                         HA_CHECK_OPT* check_opt __attribute__((unused)))
1 by brian
clean slate
1100
{
1101
  int rc= 0;
1102
  azio_stream writer;
1103
  char writer_filename[FN_REFLEN];
1104
1105
  init_archive_reader();
1106
1107
  // now we close both our writer and our reader for the rename
1108
  if (share->archive_write_open)
1109
  {
1110
    azclose(&(share->archive_write));
163 by Brian Aker
Merge Monty's code.
1111
    share->archive_write_open= false;
1 by brian
clean slate
1112
  }
1113
1114
  /* Lets create a file to contain the new data */
1115
  fn_format(writer_filename, share->table_name, "", ARN, 
1116
            MY_REPLACE_EXT | MY_UNPACK_FILENAME);
1117
1118
  if (!(azopen(&writer, writer_filename, O_CREAT|O_RDWR|O_BINARY, AZ_METHOD_BLOCK)))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1119
    return(HA_ERR_CRASHED_ON_USAGE); 
1 by brian
clean slate
1120
1121
  /* 
1122
    An extended rebuild is a lot more effort. We open up each row and re-record it. 
1123
    Any dead rows are removed (aka rows that may have been partially recorded). 
1124
1125
    As of Archive format 3, this is the only type that is performed, before this
1126
    version it was just done on T_EXTEND
1127
  */
1128
  if (1)
1129
  {
1130
    /*
1131
      Now we will rewind the archive file so that we are positioned at the 
1132
      start of the file.
1133
    */
1134
    azflush(&archive, Z_SYNC_FLUSH);
1135
    rc= read_data_header(&archive);
1136
1137
    /* 
1138
      On success of writing out the new header, we now fetch each row and
1139
      insert it into the new archive file. 
1140
    */
1141
    if (!rc)
1142
    {
53.2.2 by Monty Taylor
Updated everything that needs updating to compile with -std=gnu99 -pedantic
1143
      uint64_t x;
1144
      uint64_t rows_restored;
1 by brian
clean slate
1145
      share->rows_recorded= 0;
1146
      stats.auto_increment_value= 1;
1147
      share->archive_write.auto_increment= 0;
1148
      my_bitmap_map *org_bitmap= dbug_tmp_use_all_columns(table, table->read_set);
1149
1150
      rows_restored= archive.rows;
1151
1152
      for (x= 0; x < rows_restored ; x++)
1153
      {
1154
        rc= get_row(&archive, table->record[0]);
1155
1156
        if (rc != 0)
1157
          break;
1158
1159
        real_write_row(table->record[0], &writer);
1160
        /*
1161
          Long term it should be possible to optimize this so that
1162
          it is not called on each row.
1163
        */
1164
        if (table->found_next_number_field)
1165
        {
1166
          Field *field= table->found_next_number_field;
1167
          uint64_t auto_value=
1168
            (uint64_t) field->val_int(table->record[0] +
1169
                                       field->offset(table->record[0]));
1170
          if (share->archive_write.auto_increment < auto_value)
1171
            stats.auto_increment_value=
1172
              (share->archive_write.auto_increment= auto_value) + 1;
1173
        }
1174
      }
1175
      dbug_tmp_restore_column_map(table->read_set, org_bitmap);
1176
      share->rows_recorded= (ha_rows)writer.rows;
1177
    }
1178
1179
    if (rc && rc != HA_ERR_END_OF_FILE)
1180
    {
1181
      goto error;
1182
    }
1183
  } 
1184
1185
  azclose(&writer);
163 by Brian Aker
Merge Monty's code.
1186
  share->dirty= false;
1 by brian
clean slate
1187
  
1188
  azclose(&archive);
1189
1190
  // make the file we just wrote be our data file
1191
  rc = my_rename(writer_filename,share->data_file_name,MYF(0));
1192
1193
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1194
  return(rc);
1 by brian
clean slate
1195
error:
1196
  azclose(&writer);
1197
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1198
  return(rc); 
1 by brian
clean slate
1199
}
1200
1201
/* 
1202
  Below is an example of how to setup row level locking.
1203
*/
1204
THR_LOCK_DATA **ha_archive::store_lock(THD *thd,
1205
                                       THR_LOCK_DATA **to,
1206
                                       enum thr_lock_type lock_type)
1207
{
1208
  if (lock_type == TL_WRITE_DELAYED)
163 by Brian Aker
Merge Monty's code.
1209
    delayed_insert= true;
1 by brian
clean slate
1210
  else
163 by Brian Aker
Merge Monty's code.
1211
    delayed_insert= false;
1 by brian
clean slate
1212
1213
  if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) 
1214
  {
1215
    /* 
1216
      Here is where we get into the guts of a row level lock.
1217
      If TL_UNLOCK is set 
1218
      If we are not doing a LOCK TABLE or DISCARD/IMPORT
1219
      TABLESPACE, then allow multiple writers 
1220
    */
1221
1222
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
1223
         lock_type <= TL_WRITE) && !thd_in_lock_tables(thd)
1224
        && !thd_tablespace_op(thd))
1225
      lock_type = TL_WRITE_ALLOW_WRITE;
1226
1227
    /* 
1228
      In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
1229
      MySQL would use the lock TL_READ_NO_INSERT on t2, and that
1230
      would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
1231
      to t2. Convert the lock to a normal read lock to allow
1232
      concurrent inserts to t2. 
1233
    */
1234
1235
    if (lock_type == TL_READ_NO_INSERT && !thd_in_lock_tables(thd)) 
1236
      lock_type = TL_READ;
1237
1238
    lock.type=lock_type;
1239
  }
1240
1241
  *to++= &lock;
1242
1243
  return to;
1244
}
1245
1246
void ha_archive::update_create_info(HA_CREATE_INFO *create_info)
1247
{
1248
  ha_archive::info(HA_STATUS_AUTO);
1249
  if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
1250
  {
1251
    create_info->auto_increment_value= stats.auto_increment_value;
1252
  }
1253
1254
  if (!(my_readlink(share->real_path, share->data_file_name, MYF(0))))
1255
    create_info->data_file_name= share->real_path;
1256
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1257
  return;
1 by brian
clean slate
1258
}
1259
1260
1261
/*
1262
  Hints for optimizer, see ha_tina for more information
1263
*/
1264
int ha_archive::info(uint flag)
1265
{
1266
  /* 
1267
    If dirty, we lock, and then reset/flush the data.
1268
    I found that just calling azflush() doesn't always work.
1269
  */
1270
  pthread_mutex_lock(&share->mutex);
163 by Brian Aker
Merge Monty's code.
1271
  if (share->dirty == true)
1 by brian
clean slate
1272
  {
1273
    azflush(&(share->archive_write), Z_SYNC_FLUSH);
1274
    share->rows_recorded= share->archive_write.rows;
163 by Brian Aker
Merge Monty's code.
1275
    share->dirty= false;
1 by brian
clean slate
1276
    if (share->version < global_version)
1277
    {
1278
      share->version_rows= share->rows_recorded;
1279
      share->version= global_version;
1280
    }
1281
1282
  }
1283
1284
  /* 
1285
    This should be an accurate number now, though bulk and delayed inserts can
1286
    cause the number to be inaccurate.
1287
  */
1288
  stats.records= share->rows_recorded;
1289
  pthread_mutex_unlock(&share->mutex);
1290
1291
  scan_rows= stats.records;
1292
  stats.deleted= 0;
1293
1294
  /* Costs quite a bit more to get all information */
1295
  if (flag & HA_STATUS_TIME)
1296
  {
15 by brian
Fix for stat, NETWARE removal
1297
    struct stat file_stat;  // Stat information for the data file
1 by brian
clean slate
1298
15 by brian
Fix for stat, NETWARE removal
1299
    VOID(stat(share->data_file_name, &file_stat));
1 by brian
clean slate
1300
1301
    stats.mean_rec_length= table->s->reclength + buffer.alloced_length();
1302
    stats.data_file_length= file_stat.st_size;
1303
    stats.create_time= file_stat.st_ctime;
1304
    stats.update_time= file_stat.st_mtime;
1305
    stats.max_data_file_length= share->rows_recorded * stats.mean_rec_length;
1306
  }
1307
  stats.delete_length= 0;
1308
  stats.index_file_length=0;
1309
1310
  if (flag & HA_STATUS_AUTO)
1311
  {
1312
    init_archive_reader();
1313
    pthread_mutex_lock(&share->mutex);
1314
    azflush(&archive, Z_SYNC_FLUSH);
1315
    pthread_mutex_unlock(&share->mutex);
1316
    stats.auto_increment_value= archive.auto_increment + 1;
1317
  }
1318
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1319
  return(0);
1 by brian
clean slate
1320
}
1321
1322
1323
/*
1324
  This method tells us that a bulk insert operation is about to occur. We set
1325
  a flag which will keep write_row from saying that its data is dirty. This in
1326
  turn will keep selects from causing a sync to occur.
1327
  Basically, yet another optimizations to keep compression working well.
1328
*/
1329
void ha_archive::start_bulk_insert(ha_rows rows)
1330
{
1331
  if (!rows || rows >= ARCHIVE_MIN_ROWS_TO_USE_BULK_INSERT)
163 by Brian Aker
Merge Monty's code.
1332
    bulk_insert= true;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1333
  return;
1 by brian
clean slate
1334
}
1335
1336
1337
/* 
1338
  Other side of start_bulk_insert, is end_bulk_insert. Here we turn off the bulk insert
1339
  flag, and set the share dirty so that the next select will call sync for us.
1340
*/
1341
int ha_archive::end_bulk_insert()
1342
{
163 by Brian Aker
Merge Monty's code.
1343
  bulk_insert= false;
1344
  share->dirty= true;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1345
  return(0);
1 by brian
clean slate
1346
}
1347
1348
/*
1349
  We cancel a truncate command. The only way to delete an archive table is to drop it.
1350
  This is done for security reasons. In a later version we will enable this by 
1351
  allowing the user to select a different row format.
1352
*/
1353
int ha_archive::delete_all_rows()
1354
{
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1355
  return(HA_ERR_WRONG_COMMAND);
1 by brian
clean slate
1356
}
1357
1358
/*
1359
  We just return state if asked.
1360
*/
1361
bool ha_archive::is_crashed() const 
1362
{
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1363
  return(share->crashed); 
1 by brian
clean slate
1364
}
1365
1366
/*
1367
  Simple scan of the tables to make sure everything is ok.
1368
*/
1369
53.2.32 by Monty Taylor
First large swath at getting handler stuff clean.
1370
int ha_archive::check(THD* thd,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
1371
                      HA_CHECK_OPT* check_opt __attribute__((unused)))
1 by brian
clean slate
1372
{
1373
  int rc= 0;
1374
  const char *old_proc_info;
53.2.2 by Monty Taylor
Updated everything that needs updating to compile with -std=gnu99 -pedantic
1375
  uint64_t x;
1 by brian
clean slate
1376
1377
  old_proc_info= thd_proc_info(thd, "Checking table");
1378
  /* Flush any waiting data */
1379
  pthread_mutex_lock(&share->mutex);
1380
  azflush(&(share->archive_write), Z_SYNC_FLUSH);
1381
  pthread_mutex_unlock(&share->mutex);
1382
1383
  /*
1384
    Now we will rewind the archive file so that we are positioned at the 
1385
    start of the file.
1386
  */
1387
  init_archive_reader();
1388
  azflush(&archive, Z_SYNC_FLUSH);
1389
  read_data_header(&archive);
1390
  for (x= 0; x < share->archive_write.rows; x++)
1391
  {
1392
    rc= get_row(&archive, table->record[0]);
1393
1394
    if (rc != 0)
1395
      break;
1396
  }
1397
1398
  thd_proc_info(thd, old_proc_info);
1399
1400
  if ((rc && rc != HA_ERR_END_OF_FILE))  
1401
  {
163 by Brian Aker
Merge Monty's code.
1402
    share->crashed= false;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1403
    return(HA_ADMIN_CORRUPT);
1 by brian
clean slate
1404
  }
1405
  else
1406
  {
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1407
    return(HA_ADMIN_OK);
1 by brian
clean slate
1408
  }
1409
}
1410
1411
/*
1412
  Check and repair the table if needed.
1413
*/
1414
bool ha_archive::check_and_repair(THD *thd) 
1415
{
1416
  HA_CHECK_OPT check_opt;
1417
1418
  check_opt.init();
1419
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1420
  return(repair(thd, &check_opt));
1 by brian
clean slate
1421
}
1422
1423
archive_record_buffer *ha_archive::create_record_buffer(unsigned int length) 
1424
{
1425
  archive_record_buffer *r;
1426
  if (!(r= 
1427
        (archive_record_buffer*) my_malloc(sizeof(archive_record_buffer),
1428
                                           MYF(MY_WME))))
1429
  {
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1430
    return(NULL); /* purecov: inspected */
1 by brian
clean slate
1431
  }
1432
  r->length= (int)length;
1433
1434
  if (!(r->buffer= (uchar*) my_malloc(r->length,
1435
                                    MYF(MY_WME))))
1436
  {
1437
    my_free((char*) r, MYF(MY_ALLOW_ZERO_PTR));
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1438
    return(NULL); /* purecov: inspected */
1 by brian
clean slate
1439
  }
1440
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1441
  return(r);
1 by brian
clean slate
1442
}
1443
1444
void ha_archive::destroy_record_buffer(archive_record_buffer *r) 
1445
{
1446
  my_free((char*) r->buffer, MYF(MY_ALLOW_ZERO_PTR));
1447
  my_free((char*) r, MYF(MY_ALLOW_ZERO_PTR));
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1448
  return;
1 by brian
clean slate
1449
}
1450
1451
static MYSQL_SYSVAR_BOOL(aio, archive_use_aio,
1452
  PLUGIN_VAR_NOCMDOPT,
1453
  "Whether or not to use asynchronous IO.",
163 by Brian Aker
Merge Monty's code.
1454
  NULL, NULL, true);
1 by brian
clean slate
1455
1456
static struct st_mysql_sys_var* archive_system_variables[]= {
1457
  MYSQL_SYSVAR(aio),
1458
  NULL
1459
};
1460
1461
mysql_declare_plugin(archive)
1462
{
1463
  MYSQL_STORAGE_ENGINE_PLUGIN,
1464
  "ARCHIVE",
177.4.3 by mark
ripped out more plugin ABI and API version checking, and plugin versions are now strings
1465
  "3.5",
1 by brian
clean slate
1466
  "Brian Aker, MySQL AB",
1467
  "Archive storage engine",
1468
  PLUGIN_LICENSE_GPL,
1469
  archive_db_init, /* Plugin Init */
1470
  archive_db_done, /* Plugin Deinit */
1471
  NULL,                       /* status variables                */
1472
  archive_system_variables,   /* system variables                */
1473
  NULL                        /* config options                  */
1474
}
1475
mysql_declare_plugin_end;
1476