~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/archive/ha_archive.cc

  • Committer: Eric Herman
  • Date: 2008-12-06 19:42:46 UTC
  • mto: (656.1.6 devel)
  • mto: This revision was merged to the branch mainline in revision 665.
  • Revision ID: eric@mysql.com-20081206194246-5cdexuu81i366eek
removed trailing whitespace with simple script:

for file in $(find . -name "*.c") $(find . -name "*.cc") $(find . -name "*.h"); do ruby -pe 'gsub(/\s+$/, $/)' < $file > $file.out; mv $file.out $file; done;

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
  This example was written as a test case for a customer who needed
32
32
  a storage engine without indexes that could compress data very well.
33
33
  So, welcome to a completely compressed storage engine. This storage
34
 
  engine only does inserts. No replace, deletes, or updates. All reads are 
 
34
  engine only does inserts. No replace, deletes, or updates. All reads are
35
35
  complete table scans. Compression is done through a combination of packing
36
36
  and making use of the zlib library
37
 
  
 
37
 
38
38
  We keep a file pointer open for each instance of ha_archive for each read
39
39
  but for writes we keep one open file handle just for that. We flush it
40
40
  only if we have a read occur. azip handles compressing lots of records
44
44
  the same time since we would want to flush).
45
45
 
46
46
  A "meta" file is kept alongside the data file. This file serves two purpose.
47
 
  The first purpose is to track the number of rows in the table. The second 
48
 
  purpose is to determine if the table was closed properly or not. When the 
49
 
  meta file is first opened it is marked as dirty. It is opened when the table 
50
 
  itself is opened for writing. When the table is closed the new count for rows 
51
 
  is written to the meta file and the file is marked as clean. If the meta file 
52
 
  is opened and it is marked as dirty, it is assumed that a crash occured. At 
 
47
  The first purpose is to track the number of rows in the table. The second
 
48
  purpose is to determine if the table was closed properly or not. When the
 
49
  meta file is first opened it is marked as dirty. It is opened when the table
 
50
  itself is opened for writing. When the table is closed the new count for rows
 
51
  is written to the meta file and the file is marked as clean. If the meta file
 
52
  is opened and it is marked as dirty, it is assumed that a crash occured. At
53
53
  this point an error occurs and the user is told to rebuild the file.
54
54
  A rebuild scans the rows and rewrites the meta file. If corruption is found
55
55
  in the data file then the meta file is not repaired.
56
56
 
57
57
  At some point a recovery method for such a drastic case needs to be divised.
58
58
 
59
 
  Locks are row level, and you will get a consistant read. 
 
59
  Locks are row level, and you will get a consistant read.
60
60
 
61
61
  For performance as far as table scans go it is quite fast. I don't have
62
62
  good numbers but locally it has out performed both Innodb and MyISAM. For
63
63
  Innodb the question will be if the table can be fit into the buffer
64
64
  pool. For MyISAM its a question of how much the file system caches the
65
65
  MyISAM file. With enough free memory MyISAM is faster. Its only when the OS
66
 
  doesn't have enough memory to cache entire table that archive turns out 
67
 
  to be any faster. 
 
66
  doesn't have enough memory to cache entire table that archive turns out
 
67
  to be any faster.
68
68
 
69
69
  Examples between MyISAM (packed) and Archive.
70
70
 
103
103
 
104
104
 
105
105
/* Static declarations for handerton */
106
 
static handler *archive_create_handler(handlerton *hton, 
107
 
                                       TABLE_SHARE *table, 
 
106
static handler *archive_create_handler(handlerton *hton,
 
107
                                       TABLE_SHARE *table,
108
108
                                       MEM_ROOT *mem_root);
109
 
int archive_discover(handlerton *hton, Session* session, const char *db, 
 
109
int archive_discover(handlerton *hton, Session* session, const char *db,
110
110
                     const char *name,
111
 
                     unsigned char **frmblob, 
 
111
                     unsigned char **frmblob,
112
112
                     size_t *frmlen);
113
113
 
114
114
static bool archive_use_aio= false;
124
124
#define ARCHIVE_ROW_HEADER_SIZE 4
125
125
 
126
126
static handler *archive_create_handler(handlerton *hton,
127
 
                                       TABLE_SHARE *table, 
 
127
                                       TABLE_SHARE *table,
128
128
                                       MEM_ROOT *mem_root)
129
129
{
130
130
  return new (mem_root) ha_archive(hton, table);
220
220
  azio_stream frm_stream;
221
221
  char az_file[FN_REFLEN];
222
222
  char *frm_ptr;
223
 
  struct stat file_stat; 
 
223
  struct stat file_stat;
224
224
 
225
225
  fn_format(az_file, name, db, ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME);
226
226
 
266
266
 
267
267
 
268
268
/*
269
 
  We create the shared memory space that we will use for the open table. 
 
269
  We create the shared memory space that we will use for the open table.
270
270
  No matter what we try to get or create a share. This is so that a repair
271
 
  table operation can occur. 
 
271
  table operation can occur.
272
272
 
273
273
  See ha_example.cc for a longer description.
274
274
*/
289
289
    if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
290
290
                          &share, sizeof(*share),
291
291
                          &tmp_name, length+1,
292
 
                          NULL)) 
 
292
                          NULL))
293
293
    {
294
294
      pthread_mutex_unlock(&archive_mutex);
295
295
      *rc= HA_ERR_OUT_OF_MEM;
308
308
      We will use this lock for rows.
309
309
    */
310
310
    pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
311
 
    
 
311
 
312
312
    /*
313
313
      We read the meta file, but do not mark it dirty. Since we are not
314
314
      doing a write we won't mark it dirty (and we won't open it for
346
346
}
347
347
 
348
348
 
349
 
/* 
 
349
/*
350
350
  Free the share.
351
351
  See ha_example.cc for a description.
352
352
*/
360
360
    hash_delete(&archive_open_tables, (unsigned char*) share);
361
361
    thr_lock_delete(&share->lock);
362
362
    pthread_mutex_destroy(&share->mutex);
363
 
    /* 
 
363
    /*
364
364
      We need to make sure we don't reset the crashed state.
365
365
      If we open a crashed file, wee need to close it as crashed unless
366
366
      it has been repaired.
381
381
 
382
382
int ha_archive::init_archive_writer()
383
383
{
384
 
  /* 
 
384
  /*
385
385
    It is expensive to open and close the data files and since you can't have
386
386
    a gzip file that can be both read and written we keep a writer open
387
387
    that is shared amoung all open tables.
388
388
  */
389
 
  if (!(azopen(&(share->archive_write), share->data_file_name, 
 
389
  if (!(azopen(&(share->archive_write), share->data_file_name,
390
390
               O_RDWR, AZ_METHOD_BLOCK)))
391
391
  {
392
392
    share->crashed= true;
398
398
}
399
399
 
400
400
 
401
 
/* 
 
401
/*
402
402
  No locks are required because it is associated with just one handler instance
403
403
*/
404
404
int ha_archive::init_archive_reader()
405
405
{
406
 
  /* 
 
406
  /*
407
407
    It is expensive to open and close the data files and since you can't have
408
408
    a gzip file that can be both read and written we keep a writer open
409
409
    that is shared amoung all open tables.
423
423
    default:
424
424
      method= AZ_METHOD_BLOCK;
425
425
    }
426
 
    if (!(azopen(&archive, share->data_file_name, O_RDONLY, 
 
426
    if (!(azopen(&archive, share->data_file_name, O_RDONLY,
427
427
                 method)))
428
428
    {
429
429
      share->crashed= true;
450
450
}
451
451
 
452
452
 
453
 
/* 
 
453
/*
454
454
  When opening a file we:
455
455
  Create/get our shared structure.
456
456
  Init out lock.
466
466
    /* purecov: begin inspected */
467
467
    free_share();
468
468
    return(rc);
469
 
    /* purecov: end */    
 
469
    /* purecov: end */
470
470
  }
471
471
  else if (rc == HA_ERR_OUT_OF_MEM)
472
472
  {
475
475
 
476
476
  assert(share);
477
477
 
478
 
  record_buffer= create_record_buffer(table->s->reclength + 
 
478
  record_buffer= create_record_buffer(table->s->reclength +
479
479
                                      ARCHIVE_ROW_HEADER_SIZE);
480
480
 
481
481
  if (!record_buffer)
500
500
 
501
501
  SYNOPSIS
502
502
    close();
503
 
  
 
503
 
504
504
  IMPLEMENTATION:
505
505
 
506
506
  We first close this storage engines file handle to the archive and
532
532
 
533
533
 
534
534
/*
535
 
  We create our data file here. The format is pretty simple. 
 
535
  We create our data file here. The format is pretty simple.
536
536
  You can read about the format of the data file above.
537
 
  Unlike other storage engines we do not "pack" our data. Since we 
538
 
  are about to do a general compression, packing would just be a waste of 
539
 
  CPU time. If the table has blobs they are written after the row in the order 
 
537
  Unlike other storage engines we do not "pack" our data. Since we
 
538
  are about to do a general compression, packing would just be a waste of
 
539
  CPU time. If the table has blobs they are written after the row in the order
540
540
  of creation.
541
541
*/
542
542
 
571
571
    }
572
572
  }
573
573
 
574
 
  /* 
 
574
  /*
575
575
    We reuse name_buff since it is available.
576
576
  */
577
577
  if (create_info->data_file_name && create_info->data_file_name[0] != '#')
608
608
              MY_REPLACE_EXT | MY_UNPACK_FILENAME);
609
609
 
610
610
    /*
611
 
      Here is where we open up the frm and pass it to archive to store 
 
611
      Here is where we open up the frm and pass it to archive to store
612
612
    */
613
613
    if ((frm_file= my_open(name_buff, O_RDONLY, MYF(0))) > 0)
614
614
    {
626
626
    }
627
627
 
628
628
    if (create_info->comment.str)
629
 
      azwrite_comment(&create_stream, create_info->comment.str, 
 
629
      azwrite_comment(&create_stream, create_info->comment.str,
630
630
                      (unsigned int)create_info->comment.length);
631
631
 
632
 
    /* 
633
 
      Yes you need to do this, because the starting value 
 
632
    /*
 
633
      Yes you need to do this, because the starting value
634
634
      for the autoincrement may not be zero.
635
635
    */
636
636
    create_stream.auto_increment= stats.auto_increment_value ?
677
677
}
678
678
 
679
679
 
680
 
/* 
 
680
/*
681
681
  Calculate max length needed for row. This includes
682
682
  the bytes required for the length in the header.
683
683
*/
720
720
}
721
721
 
722
722
 
723
 
/* 
 
723
/*
724
724
  Look at ha_archive::open() for an explanation of the row format.
725
725
  Here we just write out the row.
726
726
 
727
727
  Wondering about start_bulk_insert()? We don't implement it for
728
728
  archive since it optimizes for lots of writes. The only save
729
 
  for implementing start_bulk_insert() is that we could skip 
 
729
  for implementing start_bulk_insert() is that we could skip
730
730
  setting dirty to true each time.
731
731
*/
732
732
int ha_archive::write_row(unsigned char *buf)
759
759
      We don't support decremening auto_increment. They make the performance
760
760
      just cry.
761
761
    */
762
 
    if (temp_auto <= share->archive_write.auto_increment && 
 
762
    if (temp_auto <= share->archive_write.auto_increment &&
763
763
        mkey->flags & HA_NOSAME)
764
764
    {
765
765
      rc= HA_ERR_FOUND_DUPP_KEY;
767
767
    }
768
768
#ifdef DEAD_CODE
769
769
    /*
770
 
      Bad news, this will cause a search for the unique value which is very 
771
 
      expensive since we will have to do a table scan which will lock up 
772
 
      all other writers during this period. This could perhaps be optimized 
 
770
      Bad news, this will cause a search for the unique value which is very
 
771
      expensive since we will have to do a table scan which will lock up
 
772
      all other writers during this period. This could perhaps be optimized
773
773
      in the future.
774
774
    */
775
775
    {
776
 
      /* 
 
776
      /*
777
777
        First we create a buffer that we can use for reading rows, and can pass
778
778
        to get_row().
779
779
      */
782
782
        rc= HA_ERR_OUT_OF_MEM;
783
783
        goto error;
784
784
      }
785
 
       /* 
 
785
       /*
786
786
         All of the buffer must be written out or we won't see all of the
787
 
         data 
 
787
         data
788
788
       */
789
789
      azflush(&(share->archive_write), Z_SYNC_FLUSH);
790
790
      /*
893
893
}
894
894
 
895
895
 
896
 
int ha_archive::index_next(unsigned char * buf) 
897
 
 
896
int ha_archive::index_next(unsigned char * buf)
 
897
{
898
898
  bool found= 0;
899
899
 
900
900
  while (!(get_row(&archive, buf)))
906
906
    }
907
907
  }
908
908
 
909
 
  return(found ? 0 : HA_ERR_END_OF_FILE); 
 
909
  return(found ? 0 : HA_ERR_END_OF_FILE);
910
910
}
911
911
 
912
912
/*
934
934
 
935
935
 
936
936
/*
937
 
  This is the method that is used to read a row. It assumes that the row is 
 
937
  This is the method that is used to read a row. It assumes that the row is
938
938
  positioned where you want it.
939
939
*/
940
940
int ha_archive::get_row(azio_stream *file_to_read, unsigned char *buf)
957
957
  if (length > record_buffer->length)
958
958
  {
959
959
    unsigned char *newptr;
960
 
    if (!(newptr=(unsigned char*) my_realloc((unsigned char*) record_buffer->buffer, 
 
960
    if (!(newptr=(unsigned char*) my_realloc((unsigned char*) record_buffer->buffer,
961
961
                                    length,
962
962
                                    MYF(MY_ALLOW_ZERO_PTR))))
963
963
      return(1);
1006
1006
}
1007
1007
 
1008
1008
 
1009
 
/* 
 
1009
/*
1010
1010
  Called during ORDER BY. Its position is either from being called sequentially
1011
1011
  or by having had ha_archive::rnd_pos() called before it is called.
1012
1012
*/
1062
1062
}
1063
1063
 
1064
1064
/*
1065
 
  This method repairs the meta file. It does this by walking the datafile and 
 
1065
  This method repairs the meta file. It does this by walking the datafile and
1066
1066
  rewriting the meta file. Currently it does this by calling optimize with
1067
1067
  the extended flag.
1068
1068
*/
1080
1080
 
1081
1081
/*
1082
1082
  The table can become fragmented if data was inserted, read, and then
1083
 
  inserted again. What we do is open up the file and recompress it completely. 
 
1083
  inserted again. What we do is open up the file and recompress it completely.
1084
1084
*/
1085
1085
int ha_archive::optimize(Session *, HA_CHECK_OPT *)
1086
1086
{
1098
1098
  }
1099
1099
 
1100
1100
  /* Lets create a file to contain the new data */
1101
 
  fn_format(writer_filename, share->table_name, "", ARN, 
 
1101
  fn_format(writer_filename, share->table_name, "", ARN,
1102
1102
            MY_REPLACE_EXT | MY_UNPACK_FILENAME);
1103
1103
 
1104
1104
  if (!(azopen(&writer, writer_filename, O_CREAT|O_RDWR, AZ_METHOD_BLOCK)))
1105
 
    return(HA_ERR_CRASHED_ON_USAGE); 
 
1105
    return(HA_ERR_CRASHED_ON_USAGE);
1106
1106
 
1107
 
  /* 
1108
 
    An extended rebuild is a lot more effort. We open up each row and re-record it. 
1109
 
    Any dead rows are removed (aka rows that may have been partially recorded). 
 
1107
  /*
 
1108
    An extended rebuild is a lot more effort. We open up each row and re-record it.
 
1109
    Any dead rows are removed (aka rows that may have been partially recorded).
1110
1110
 
1111
1111
    As of Archive format 3, this is the only type that is performed, before this
1112
1112
    version it was just done on T_EXTEND
1114
1114
  if (1)
1115
1115
  {
1116
1116
    /*
1117
 
      Now we will rewind the archive file so that we are positioned at the 
 
1117
      Now we will rewind the archive file so that we are positioned at the
1118
1118
      start of the file.
1119
1119
    */
1120
1120
    azflush(&archive, Z_SYNC_FLUSH);
1121
1121
    rc= read_data_header(&archive);
1122
1122
 
1123
 
    /* 
 
1123
    /*
1124
1124
      On success of writing out the new header, we now fetch each row and
1125
 
      insert it into the new archive file. 
 
1125
      insert it into the new archive file.
1126
1126
    */
1127
1127
    if (!rc)
1128
1128
    {
1164
1164
    {
1165
1165
      goto error;
1166
1166
    }
1167
 
  } 
 
1167
  }
1168
1168
 
1169
1169
  azclose(&writer);
1170
1170
  share->dirty= false;
1171
 
  
 
1171
 
1172
1172
  azclose(&archive);
1173
1173
 
1174
1174
  // make the file we just wrote be our data file
1179
1179
error:
1180
1180
  azclose(&writer);
1181
1181
 
1182
 
  return(rc); 
 
1182
  return(rc);
1183
1183
}
1184
1184
 
1185
 
/* 
 
1185
/*
1186
1186
  Below is an example of how to setup row level locking.
1187
1187
*/
1188
1188
THR_LOCK_DATA **ha_archive::store_lock(Session *session,
1194
1194
  else
1195
1195
    delayed_insert= false;
1196
1196
 
1197
 
  if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) 
 
1197
  if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
1198
1198
  {
1199
 
    /* 
 
1199
    /*
1200
1200
      Here is where we get into the guts of a row level lock.
1201
 
      If TL_UNLOCK is set 
 
1201
      If TL_UNLOCK is set
1202
1202
      If we are not doing a LOCK Table or DISCARD/IMPORT
1203
 
      TABLESPACE, then allow multiple writers 
 
1203
      TABLESPACE, then allow multiple writers
1204
1204
    */
1205
1205
 
1206
1206
    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
1208
1208
        && !session_tablespace_op(session))
1209
1209
      lock_type = TL_WRITE_ALLOW_WRITE;
1210
1210
 
1211
 
    /* 
 
1211
    /*
1212
1212
      In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
1213
1213
      MySQL would use the lock TL_READ_NO_INSERT on t2, and that
1214
1214
      would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
1215
1215
      to t2. Convert the lock to a normal read lock to allow
1216
 
      concurrent inserts to t2. 
 
1216
      concurrent inserts to t2.
1217
1217
    */
1218
1218
 
1219
 
    if (lock_type == TL_READ_NO_INSERT && !session_in_lock_tables(session)) 
 
1219
    if (lock_type == TL_READ_NO_INSERT && !session_in_lock_tables(session))
1220
1220
      lock_type = TL_READ;
1221
1221
 
1222
1222
    lock.type=lock_type;
1247
1247
*/
1248
1248
int ha_archive::info(uint32_t flag)
1249
1249
{
1250
 
  /* 
 
1250
  /*
1251
1251
    If dirty, we lock, and then reset/flush the data.
1252
1252
    I found that just calling azflush() doesn't always work.
1253
1253
  */
1265
1265
 
1266
1266
  }
1267
1267
 
1268
 
  /* 
 
1268
  /*
1269
1269
    This should be an accurate number now, though bulk and delayed inserts can
1270
1270
    cause the number to be inaccurate.
1271
1271
  */
1318
1318
}
1319
1319
 
1320
1320
 
1321
 
/* 
 
1321
/*
1322
1322
  Other side of start_bulk_insert, is end_bulk_insert. Here we turn off the bulk insert
1323
1323
  flag, and set the share dirty so that the next select will call sync for us.
1324
1324
*/
1331
1331
 
1332
1332
/*
1333
1333
  We cancel a truncate command. The only way to delete an archive table is to drop it.
1334
 
  This is done for security reasons. In a later version we will enable this by 
 
1334
  This is done for security reasons. In a later version we will enable this by
1335
1335
  allowing the user to select a different row format.
1336
1336
*/
1337
1337
int ha_archive::delete_all_rows()
1342
1342
/*
1343
1343
  We just return state if asked.
1344
1344
*/
1345
 
bool ha_archive::is_crashed() const 
 
1345
bool ha_archive::is_crashed() const
1346
1346
{
1347
 
  return(share->crashed); 
 
1347
  return(share->crashed);
1348
1348
}
1349
1349
 
1350
1350
/*
1365
1365
  pthread_mutex_unlock(&share->mutex);
1366
1366
 
1367
1367
  /*
1368
 
    Now we will rewind the archive file so that we are positioned at the 
 
1368
    Now we will rewind the archive file so that we are positioned at the
1369
1369
    start of the file.
1370
1370
  */
1371
1371
  init_archive_reader();
1381
1381
 
1382
1382
  set_session_proc_info(session, old_proc_info);
1383
1383
 
1384
 
  if ((rc && rc != HA_ERR_END_OF_FILE))  
 
1384
  if ((rc && rc != HA_ERR_END_OF_FILE))
1385
1385
  {
1386
1386
    share->crashed= false;
1387
1387
    return(HA_ADMIN_CORRUPT);
1395
1395
/*
1396
1396
  Check and repair the table if needed.
1397
1397
*/
1398
 
bool ha_archive::check_and_repair(Session *session) 
 
1398
bool ha_archive::check_and_repair(Session *session)
1399
1399
{
1400
1400
  HA_CHECK_OPT check_opt;
1401
1401
 
1404
1404
  return(repair(session, &check_opt));
1405
1405
}
1406
1406
 
1407
 
archive_record_buffer *ha_archive::create_record_buffer(unsigned int length) 
 
1407
archive_record_buffer *ha_archive::create_record_buffer(unsigned int length)
1408
1408
{
1409
1409
  archive_record_buffer *r;
1410
 
  if (!(r= 
 
1410
  if (!(r=
1411
1411
        (archive_record_buffer*) my_malloc(sizeof(archive_record_buffer),
1412
1412
                                           MYF(MY_WME))))
1413
1413
  {
1425
1425
  return(r);
1426
1426
}
1427
1427
 
1428
 
void ha_archive::destroy_record_buffer(archive_record_buffer *r) 
 
1428
void ha_archive::destroy_record_buffer(archive_record_buffer *r)
1429
1429
{
1430
1430
  free((char*) r->buffer);
1431
1431
  free((char*) r);