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 */
16
#ifdef USE_PRAGMA_IMPLEMENTATION
17
#pragma implementation // gcc: Class implementation
20
#include <drizzled/common_includes.h>
21
#include <storage/myisam/myisam.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"
23
25
#include "ha_archive.h"
26
First, if you want to understand storage engines you should look at
27
ha_example.cc and ha_example.h.
38
First, if you want to understand storage engines you should look at
39
ha_example.cc and ha_example.h.
29
41
This example was written as a test case for a customer who needed
30
42
a storage engine without indexes that could compress data very well.
31
43
So, welcome to a completely compressed storage engine. This storage
32
engine only does inserts. No replace, deletes, or updates. All reads are
44
engine only does inserts. No replace, deletes, or updates. All reads are
33
45
complete table scans. Compression is done through a combination of packing
34
46
and making use of the zlib library
36
48
We keep a file pointer open for each instance of ha_archive for each read
37
49
but for writes we keep one open file handle just for that. We flush it
38
50
only if we have a read occur. azip handles compressing lots of records
42
54
the same time since we would want to flush).
44
56
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
57
The first purpose is to track the number of rows in the table. The second
58
purpose is to determine if the table was closed properly or not. When the
59
meta file is first opened it is marked as dirty. It is opened when the table
60
itself is opened for writing. When the table is closed the new count for rows
61
is written to the meta file and the file is marked as clean. If the meta file
62
is opened and it is marked as dirty, it is assumed that a crash occured. At
51
63
this point an error occurs and the user is told to rebuild the file.
52
64
A rebuild scans the rows and rewrites the meta file. If corruption is found
53
65
in the data file then the meta file is not repaired.
55
67
At some point a recovery method for such a drastic case needs to be divised.
57
Locks are row level, and you will get a consistant read.
69
Locks are row level, and you will get a consistant read.
59
71
For performance as far as table scans go it is quite fast. I don't have
60
72
good numbers but locally it has out performed both Innodb and MyISAM. For
61
73
Innodb the question will be if the table can be fit into the buffer
62
74
pool. For MyISAM its a question of how much the file system caches the
63
75
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
76
doesn't have enough memory to cache entire table that archive turns out
67
79
Examples between MyISAM (packed) and Archive.
93
105
/* Variables for archive share methods */
94
pthread_mutex_t archive_mutex;
95
static HASH archive_open_tables;
106
pthread_mutex_t archive_mutex= PTHREAD_MUTEX_INITIALIZER;
96
108
static unsigned int global_version;
98
110
/* The file extension */
99
#define ARZ ".ARZ" // The data file
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)
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
109
/* Static declarations for handerton */
110
static handler *archive_create_handler(handlerton *hton,
113
int archive_discover(handlerton *hton, THD* thd, const char *db,
118
116
static bool archive_use_aio= false;
128
126
#define ARCHIVE_ROW_HEADER_SIZE 4
130
static handler *archive_create_handler(handlerton *hton,
134
return new (mem_root) ha_archive(hton, table);
138
Used for hash table that tracks open tables.
129
We just implement one additional file extension.
140
static uchar* archive_get_key(ARCHIVE_SHARE *share, size_t *length,
141
bool not_used __attribute__((unused)))
143
*length=share->table_name_length;
144
return (uchar*) share->table_name;
131
static const char *ha_archive_exts[] = {
136
class ArchiveEngine : public drizzled::plugin::StorageEngine
138
typedef std::map<string, ArchiveShare*> ArchiveMap;
139
ArchiveMap archive_open_tables;
142
ArchiveEngine(const string &name_arg)
143
: drizzled::plugin::StorageEngine(name_arg,
145
HTON_STATS_RECORDS_IS_EXACT |
147
HTON_HAS_DATA_DICTIONARY),
148
archive_open_tables()
150
table_definition_ext= ARZ;
153
virtual Cursor *create(TableShare &table,
154
drizzled::memory::Root *mem_root)
156
return new (mem_root) ha_archive(*this, table);
159
const char **bas_ext() const {
160
return ha_archive_exts;
163
int doCreateTable(Session *session, const char *table_name,
165
drizzled::message::Table& proto);
167
int doGetTableDefinition(Session& session,
170
const char *table_name,
172
drizzled::message::Table *table_proto);
174
void doGetTableNames(drizzled::CachedDirectory &directory, string& , set<string>& set_of_names);
176
int doDropTable(Session&, const string table_path);
177
ArchiveShare *findOpenTable(const string table_name);
178
void addOpenTable(const string &table_name, ArchiveShare *);
179
void deleteOpenTable(const string &table_name);
181
uint32_t max_supported_keys() const { return 1; }
182
uint32_t max_supported_key_length() const { return sizeof(uint64_t); }
183
uint32_t max_supported_key_part_length() const { return sizeof(uint64_t); }
185
uint32_t index_flags(enum ha_key_alg) const
187
return HA_ONLY_WHOLE_INDEX;
191
ArchiveShare *ArchiveEngine::findOpenTable(const string table_name)
193
ArchiveMap::iterator find_iter=
194
archive_open_tables.find(table_name);
196
if (find_iter != archive_open_tables.end())
197
return (*find_iter).second;
202
void ArchiveEngine::addOpenTable(const string &table_name, ArchiveShare *share)
204
archive_open_tables[table_name]= share;
207
void ArchiveEngine::deleteOpenTable(const string &table_name)
209
archive_open_tables.erase(table_name);
213
void ArchiveEngine::doGetTableNames(drizzled::CachedDirectory &directory,
215
set<string>& set_of_names)
217
drizzled::CachedDirectory::Entries entries= directory.getEntries();
219
for (drizzled::CachedDirectory::Entries::iterator entry_iter= entries.begin();
220
entry_iter != entries.end(); ++entry_iter)
222
drizzled::CachedDirectory::Entry *entry= *entry_iter;
223
const string *filename= &entry->filename;
225
assert(filename->size());
227
const char *ext= strchr(filename->c_str(), '.');
229
if (ext == NULL || my_strcasecmp(system_charset_info, ext, ARZ) ||
230
(filename->compare(0, strlen(TMP_FILE_PREFIX), TMP_FILE_PREFIX) == 0))
234
char uname[NAME_LEN + 1];
235
uint32_t file_name_len;
237
file_name_len= filename_to_tablename(filename->c_str(), uname, sizeof(uname));
238
// TODO: Remove need for memory copy here
239
uname[file_name_len - sizeof(ARZ) + 1]= '\0'; // Subtract ending, place NULL
240
set_of_names.insert(uname);
246
int ArchiveEngine::doDropTable(Session&,
247
const string table_path)
249
string new_path(table_path);
253
int error= unlink(new_path.c_str());
263
int ArchiveEngine::doGetTableDefinition(Session&,
268
drizzled::message::Table *table_proto)
270
struct stat stat_info;
274
proto_path.reserve(FN_REFLEN);
275
proto_path.assign(path);
277
proto_path.append(ARZ);
279
if (stat(proto_path.c_str(),&stat_info))
286
azio_stream proto_stream;
288
if (azopen(&proto_stream, proto_path.c_str(), O_RDONLY, AZ_METHOD_BLOCK) == 0)
289
return HA_ERR_CRASHED_ON_USAGE;
291
proto_string= (char*)malloc(sizeof(char) * proto_stream.frm_length);
292
if (proto_string == NULL)
294
azclose(&proto_stream);
298
azread_frm(&proto_stream, proto_string);
300
if (table_proto->ParseFromArray(proto_string, proto_stream.frm_length) == false)
301
error= HA_ERR_CRASHED_ON_USAGE;
303
azclose(&proto_stream);
310
static ArchiveEngine *archive_engine= NULL;
149
Initialize the archive handler.
313
Initialize the archive Cursor.
152
316
archive_db_init()
160
int archive_db_init(void *p)
324
static int archive_db_init(drizzled::plugin::Registry ®istry)
162
handlerton *archive_hton;
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;
327
pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST);
328
archive_engine= new ArchiveEngine("ARCHIVE");
329
registry.add(archive_engine);
171
331
/* When the engine starts up set the first version */
172
332
global_version= 1;
174
if (pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST))
176
if (hash_init(&archive_open_tables, system_charset_info, 32, 0, 0,
177
(hash_get_key) archive_get_key, 0, 0))
179
VOID(pthread_mutex_destroy(&archive_mutex));
190
Release the archive handler.
338
Release the archive Cursor.
193
341
archive_db_done()
200
int archive_db_done(void *p __attribute__((unused)))
348
static int archive_db_done(drizzled::plugin::Registry ®istry)
202
hash_free(&archive_open_tables);
203
VOID(pthread_mutex_destroy(&archive_mutex));
350
registry.remove(archive_engine);
351
delete archive_engine;
353
pthread_mutex_destroy(&archive_mutex);
209
ha_archive::ha_archive(handlerton *hton, TABLE_SHARE *table_arg)
210
:handler(hton, table_arg), delayed_insert(0), bulk_insert(0)
359
ha_archive::ha_archive(drizzled::plugin::StorageEngine &engine_arg,
360
TableShare &table_arg)
361
:Cursor(engine_arg, table_arg), delayed_insert(0), bulk_insert(0)
212
363
/* Set our original buffer from pre-allocated memory */
213
364
buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info);
217
368
archive_reader_open= false;
220
int archive_discover(handlerton *hton __attribute__((unused)),
221
THD* thd __attribute__((unused)),
227
azio_stream frm_stream;
228
char az_file[FN_REFLEN];
230
struct stat file_stat;
232
fn_format(az_file, name, db, ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME);
234
if (stat(az_file, &file_stat))
237
if (!(azopen(&frm_stream, az_file, O_RDONLY|O_BINARY, AZ_METHOD_BLOCK)))
239
if (errno == EROFS || errno == EACCES)
240
return(my_errno= errno);
241
return(HA_ERR_CRASHED_ON_USAGE);
244
if (frm_stream.frm_length == 0)
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);
251
*frmlen= frm_stream.frm_length;
252
*frmblob= (uchar*) frm_ptr;
261
372
This method reads the header of a datafile and returns whether or not it was successful.
385
ArchiveShare::ArchiveShare():
386
use_count(0), archive_write_open(false), dirty(false), crashed(false),
387
mean_rec_length(0), version(0), rows_recorded(0), version_rows(0)
392
ArchiveShare::ArchiveShare(const char *name):
393
use_count(0), archive_write_open(false), dirty(false), crashed(false),
394
mean_rec_length(0), version(0), rows_recorded(0), version_rows(0)
396
memset(&archive_write, 0, sizeof(azio_stream)); /* Archive file we are working with */
397
table_name.append(name);
398
fn_format(data_file_name, table_name.c_str(), "",
399
ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME);
401
We will use this lock for rows.
403
pthread_mutex_init(&mutex,MY_MUTEX_INIT_FAST);
406
ArchiveShare::~ArchiveShare()
408
thr_lock_delete(&lock);
409
pthread_mutex_destroy(&mutex);
411
We need to make sure we don't reset the crashed state.
412
If we open a crashed file, wee need to close it as crashed unless
413
it has been repaired.
414
Since we will close the data down after this, we go on and count
417
if (archive_write_open == true)
418
(void)azclose(&archive_write);
421
bool ArchiveShare::prime(uint64_t *auto_increment)
423
azio_stream archive_tmp;
426
We read the meta file, but do not mark it dirty. Since we are not
427
doing a write we won't mark it dirty (and we won't open it for
428
anything but reading... open it for write and we will generate null
431
if (!(azopen(&archive_tmp, data_file_name, O_RDONLY,
435
*auto_increment= archive_tmp.auto_increment + 1;
436
rows_recorded= (ha_rows)archive_tmp.rows;
437
crashed= archive_tmp.dirty;
438
if (version < global_version)
440
version_rows= rows_recorded;
441
version= global_version;
443
azclose(&archive_tmp);
276
We create the shared memory space that we will use for the open table.
450
We create the shared memory space that we will use for the open table.
277
451
No matter what we try to get or create a share. This is so that a repair
278
table operation can occur.
452
table operation can occur.
280
454
See ha_example.cc for a longer description.
282
ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, int *rc)
456
ArchiveShare *ha_archive::get_share(const char *table_name, int *rc)
286
458
pthread_mutex_lock(&archive_mutex);
287
length=(uint) strlen(table_name);
289
if (!(share=(ARCHIVE_SHARE*) hash_search(&archive_open_tables,
460
ArchiveEngine *a_engine= static_cast<ArchiveEngine *>(engine);
461
share= a_engine->findOpenTable(table_name);
294
azio_stream archive_tmp;
465
share= new ArchiveShare(table_name);
296
if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
297
&share, sizeof(*share),
301
469
pthread_mutex_unlock(&archive_mutex);
302
470
*rc= HA_ERR_OUT_OF_MEM;
307
share->table_name_length= length;
308
share->table_name= tmp_name;
309
share->crashed= false;
310
share->archive_write_open= false;
311
fn_format(share->data_file_name, table_name, "",
312
ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME);
313
stpcpy(share->table_name, table_name);
315
We will use this lock for rows.
317
VOID(pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST));
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
325
if (!(azopen(&archive_tmp, share->data_file_name, O_RDONLY|O_BINARY,
474
if (share->prime(&stats.auto_increment_value) == false)
328
VOID(pthread_mutex_destroy(&share->mutex));
330
476
pthread_mutex_unlock(&archive_mutex);
331
477
*rc= HA_ERR_CRASHED_ON_REPAIR;
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)
339
share->version_rows= share->rows_recorded;
340
share->version= global_version;
342
azclose(&archive_tmp);
344
VOID(my_hash_insert(&archive_open_tables, (uchar*) share));
483
a_engine->addOpenTable(share->table_name, share);
345
484
thr_lock_init(&share->lock);
347
486
share->use_count++;
348
488
if (share->crashed)
349
489
*rc= HA_ERR_CRASHED_ON_USAGE;
350
490
pthread_mutex_unlock(&archive_mutex);
358
498
See ha_example.cc for a description.
360
500
int ha_archive::free_share()
364
502
pthread_mutex_lock(&archive_mutex);
365
503
if (!--share->use_count)
367
hash_delete(&archive_open_tables, (uchar*) share);
368
thr_lock_delete(&share->lock);
369
VOID(pthread_mutex_destroy(&share->mutex));
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
377
if (share->archive_write_open == true)
379
if (azclose(&(share->archive_write)))
382
my_free((uchar*) share, MYF(0));
505
ArchiveEngine *a_engine= static_cast<ArchiveEngine *>(engine);
506
a_engine->deleteOpenTable(share->table_name);
384
509
pthread_mutex_unlock(&archive_mutex);
389
514
int ha_archive::init_archive_writer()
392
517
It is expensive to open and close the data files and since you can't have
393
518
a gzip file that can be both read and written we keep a writer open
394
519
that is shared amoung all open tables.
396
if (!(azopen(&(share->archive_write), share->data_file_name,
397
O_RDWR|O_BINARY, AZ_METHOD_BLOCK)))
521
if (!(azopen(&(share->archive_write), share->data_file_name,
522
O_RDWR, AZ_METHOD_BLOCK)))
399
524
share->crashed= true;
447
We just implement one additional file extension.
449
static const char *ha_archive_exts[] = {
454
const char **ha_archive::bas_ext() const
456
return ha_archive_exts;
461
571
When opening a file we:
462
572
Create/get our shared structure.
464
574
We open the file we will read from.
466
int ha_archive::open(const char *name,
467
int mode __attribute__((unused)),
576
int ha_archive::open(const char *name, int, uint32_t)
471
579
share= get_share(name, &rc);
473
if (rc == HA_ERR_CRASHED_ON_USAGE && !(open_options & HA_OPEN_FOR_REPAIR))
582
We either fix it ourselves, or we just take it offline
584
@todo Create some documentation in the recovery tools shipped with the engine.
586
if (rc == HA_ERR_CRASHED_ON_USAGE)
475
/* purecov: begin inspected */
480
593
else if (rc == HA_ERR_OUT_OF_MEM)
544
We create our data file here. The format is pretty simple.
652
We create our data file here. The format is pretty simple.
545
653
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
654
Unlike other storage engines we do not "pack" our data. Since we
655
are about to do a general compression, packing would just be a waste of
656
CPU time. If the table has blobs they are written after the row in the order
552
int ha_archive::create(const char *name, TABLE *table_arg,
553
HA_CREATE_INFO *create_info)
660
int ArchiveEngine::doCreateTable(Session *,
661
const char *table_name,
663
drizzled::message::Table& proto)
555
665
char name_buff[FN_REFLEN];
556
char linkname[FN_REFLEN];
558
667
azio_stream create_stream; /* Archive file we are working with */
559
File frm_file; /* File handler for readers */
560
struct stat file_stat;
563
stats.auto_increment_value= create_info->auto_increment_value;
565
for (uint key= 0; key < table_arg->s->keys; key++)
668
uint64_t auto_increment_value;
669
string serialized_proto;
671
auto_increment_value= proto.options().auto_increment_value();
673
for (uint32_t key= 0; key < table_arg.sizeKeys(); key++)
567
KEY *pos= table_arg->key_info+key;
675
KEY *pos= table_arg.key_info+key;
568
676
KEY_PART_INFO *key_part= pos->key_part;
569
677
KEY_PART_INFO *key_part_end= key_part + pos->key_parts;
584
692
We reuse name_buff since it is available.
586
if (create_info->data_file_name && create_info->data_file_name[0] != '#')
694
fn_format(name_buff, table_name, "", ARZ,
695
MY_REPLACE_EXT | MY_UNPACK_FILENAME);
698
if (azopen(&create_stream, name_buff, O_CREAT|O_RDWR,
699
AZ_METHOD_BLOCK) == 0)
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);
705
proto.SerializeToString(&serialized_proto);
707
if (azwrite_frm(&create_stream, serialized_proto.c_str(),
708
serialized_proto.length()))
711
if (proto.options().has_comment())
595
fn_format(name_buff, name, "", ARZ,
596
MY_REPLACE_EXT | MY_UNPACK_FILENAME);
715
write_length= azwrite_comment(&create_stream,
716
proto.options().comment().c_str(),
717
proto.options().comment().length());
719
if (write_length < 0)
601
There is a chance that the file was "discovered". In this case
602
just use whatever file is there.
727
Yes you need to do this, because the starting value
728
for the autoincrement may not be zero.
604
if (!stat(name_buff, &file_stat))
730
create_stream.auto_increment= auto_increment_value ?
731
auto_increment_value - 1 : 0;
733
if (azclose(&create_stream))
607
if (!(azopen(&create_stream, name_buff, O_CREAT|O_RDWR|O_BINARY,
615
my_symlink(name_buff, linkname, MYF(0));
616
fn_format(name_buff, name, "", ".frm",
617
MY_REPLACE_EXT | MY_UNPACK_FILENAME);
620
Here is where we open up the frm and pass it to archive to store
622
if ((frm_file= my_open(name_buff, O_RDONLY, MYF(0))) > 0)
624
if (fstat(frm_file, &file_stat))
626
frm_ptr= (uchar *)my_malloc(sizeof(uchar) * file_stat.st_size, MYF(0));
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));
634
my_close(frm_file, MYF(0));
637
if (create_info->comment.str)
638
azwrite_comment(&create_stream, create_info->comment.str,
639
(unsigned int)create_info->comment.length);
642
Yes you need to do this, because the starting value
643
for the autoincrement may not be zero.
645
create_stream.auto_increment= stats.auto_increment_value ?
646
stats.auto_increment_value - 1 : 0;
647
if (azclose(&create_stream))
661
745
/* Return error number, if we got one */
662
746
return(error ? error : -1);
733
817
Look at ha_archive::open() for an explanation of the row format.
734
818
Here we just write out the row.
736
820
Wondering about start_bulk_insert()? We don't implement it for
737
821
archive since it optimizes for lots of writes. The only save
738
for implementing start_bulk_insert() is that we could skip
822
for implementing start_bulk_insert() is that we could skip
739
823
setting dirty to true each time.
741
int ha_archive::write_row(uchar *buf)
825
int ha_archive::write_row(unsigned char *buf)
744
uchar *read_buf= NULL;
828
unsigned char *read_buf= NULL;
745
829
uint64_t temp_auto;
746
uchar *record= table->record[0];
830
unsigned char *record= table->record[0];
748
832
if (share->crashed)
749
833
return(HA_ERR_CRASHED_ON_USAGE);
751
835
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
836
pthread_mutex_lock(&share->mutex);
756
838
if (share->archive_write_open == false)
768
850
We don't support decremening auto_increment. They make the performance
771
if (temp_auto <= share->archive_write.auto_increment &&
853
if (temp_auto <= share->archive_write.auto_increment &&
772
854
mkey->flags & HA_NOSAME)
774
856
rc= HA_ERR_FOUND_DUPP_KEY;
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
786
First we create a buffer that we can use for reading rows, and can pass
789
if (!(read_buf= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME))))
791
rc= HA_ERR_OUT_OF_MEM;
795
All of the buffer must be written out or we won't see all of the
798
azflush(&(share->archive_write), Z_SYNC_FLUSH);
800
Set the position of the local read thread to the beginning postion.
802
if (read_data_header(&archive))
804
rc= HA_ERR_CRASHED_ON_USAGE;
808
Field *mfield= table->next_number_field;
810
while (!(get_row(&archive, read_buf)))
812
if (!memcmp(read_buf + mfield->offset(record),
813
table->next_number_field->ptr,
814
mfield->max_display_length()))
816
rc= HA_ERR_FOUND_DUPP_KEY;
824
861
if (temp_auto > share->archive_write.auto_increment)
837
874
pthread_mutex_unlock(&share->mutex);
839
my_free((uchar*) read_buf, MYF(0));
876
free((unsigned char*) read_buf);
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)))
882
void ha_archive::get_auto_increment(uint64_t, uint64_t, uint64_t,
883
uint64_t *first_value, uint64_t *nb_reserved_values)
851
885
*nb_reserved_values= UINT64_MAX;
852
886
*first_value= share->archive_write.auto_increment + 1;
855
889
/* Initialized at each key walk (called multiple times unlike rnd_init()) */
856
int ha_archive::index_init(uint keynr, bool sorted __attribute__((unused)))
890
int ha_archive::index_init(uint32_t keynr, bool)
858
892
active_index= keynr;
1111
1140
share->archive_write_open= false;
1144
proto_string= (char*)malloc(sizeof(char) * archive.frm_length);
1145
if (proto_string == NULL)
1149
azread_frm(&archive, proto_string);
1114
1151
/* Lets create a file to contain the new data */
1115
fn_format(writer_filename, share->table_name, "", ARN,
1152
fn_format(writer_filename, share->table_name.c_str(), "", ARN,
1116
1153
MY_REPLACE_EXT | MY_UNPACK_FILENAME);
1118
if (!(azopen(&writer, writer_filename, O_CREAT|O_RDWR|O_BINARY, AZ_METHOD_BLOCK)))
1119
return(HA_ERR_CRASHED_ON_USAGE);
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).
1155
if (!(azopen(&writer, writer_filename, O_CREAT|O_RDWR, AZ_METHOD_BLOCK)))
1158
return(HA_ERR_CRASHED_ON_USAGE);
1161
azwrite_frm(&writer, proto_string, archive.frm_length);
1164
An extended rebuild is a lot more effort. We open up each row and re-record it.
1165
Any dead rows are removed (aka rows that may have been partially recorded).
1125
1167
As of Archive format 3, this is the only type that is performed, before this
1126
1168
version it was just done on T_EXTEND
1185
1229
azclose(&writer);
1186
1230
share->dirty= false;
1188
1232
azclose(&archive);
1190
1234
// make the file we just wrote be our data file
1191
1235
rc = my_rename(writer_filename,share->data_file_name,MYF(0));
1196
1241
azclose(&writer);
1202
1247
Below is an example of how to setup row level locking.
1204
THR_LOCK_DATA **ha_archive::store_lock(THD *thd,
1249
THR_LOCK_DATA **ha_archive::store_lock(Session *session,
1205
1250
THR_LOCK_DATA **to,
1206
1251
enum thr_lock_type lock_type)
1208
if (lock_type == TL_WRITE_DELAYED)
1209
delayed_insert= true;
1211
delayed_insert= false;
1253
delayed_insert= false;
1213
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
1255
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
1216
1258
Here is where we get into the guts of a row level lock.
1218
If we are not doing a LOCK TABLE or DISCARD/IMPORT
1219
TABLESPACE, then allow multiple writers
1260
If we are not doing a LOCK Table or DISCARD/IMPORT
1261
TABLESPACE, then allow multiple writers
1222
1264
if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
1223
lock_type <= TL_WRITE) && !thd_in_lock_tables(thd)
1224
&& !thd_tablespace_op(thd))
1265
lock_type <= TL_WRITE)
1266
&& !session_tablespace_op(session))
1225
1267
lock_type = TL_WRITE_ALLOW_WRITE;
1228
1270
In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
1229
1271
MySQL would use the lock TL_READ_NO_INSERT on t2, and that
1230
1272
would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
1231
1273
to t2. Convert the lock to a normal read lock to allow
1232
concurrent inserts to t2.
1274
concurrent inserts to t2.
1235
if (lock_type == TL_READ_NO_INSERT && !thd_in_lock_tables(thd))
1277
if (lock_type == TL_READ_NO_INSERT)
1236
1278
lock_type = TL_READ;
1238
1280
lock.type=lock_type;
1359
We just return state if asked.
1361
bool ha_archive::is_crashed() const
1363
return(share->crashed);
1367
1386
Simple scan of the tables to make sure everything is ok.
1370
int ha_archive::check(THD* thd,
1371
HA_CHECK_OPT* check_opt __attribute__((unused)))
1389
int ha_archive::check(Session* session)
1374
1392
const char *old_proc_info;
1377
old_proc_info= thd_proc_info(thd, "Checking table");
1395
old_proc_info= get_session_proc_info(session);
1396
set_session_proc_info(session, "Checking table");
1378
1397
/* Flush any waiting data */
1379
1398
pthread_mutex_lock(&share->mutex);
1380
1399
azflush(&(share->archive_write), Z_SYNC_FLUSH);
1381
1400
pthread_mutex_unlock(&share->mutex);
1384
Now we will rewind the archive file so that we are positioned at the
1403
Now we will rewind the archive file so that we are positioned at the
1385
1404
start of the file.
1387
1406
init_archive_reader();
1412
Check and repair the table if needed.
1414
bool ha_archive::check_and_repair(THD *thd)
1416
HA_CHECK_OPT check_opt;
1420
return(repair(thd, &check_opt));
1423
archive_record_buffer *ha_archive::create_record_buffer(unsigned int length)
1430
archive_record_buffer *ha_archive::create_record_buffer(unsigned int length)
1425
1432
archive_record_buffer *r;
1427
(archive_record_buffer*) my_malloc(sizeof(archive_record_buffer),
1433
if (!(r= (archive_record_buffer*) malloc(sizeof(archive_record_buffer))))
1430
return(NULL); /* purecov: inspected */
1432
1437
r->length= (int)length;
1434
if (!(r->buffer= (uchar*) my_malloc(r->length,
1439
if (!(r->buffer= (unsigned char*) malloc(r->length)))
1437
my_free((char*) r, MYF(MY_ALLOW_ZERO_PTR));
1438
return(NULL); /* purecov: inspected */
1444
void ha_archive::destroy_record_buffer(archive_record_buffer *r)
1448
void ha_archive::destroy_record_buffer(archive_record_buffer *r)
1446
my_free((char*) r->buffer, MYF(MY_ALLOW_ZERO_PTR));
1447
my_free((char*) r, MYF(MY_ALLOW_ZERO_PTR));
1450
free((char*) r->buffer);