2004-05-21 03:13:11 +02:00
|
|
|
/* Copyright (C) 2003 MySQL AB
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
|
|
|
|
#ifdef __GNUC__
|
|
|
|
#pragma implementation // gcc: Class implementation
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <mysql_priv.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_ARCHIVE_DB
|
|
|
|
#include "ha_archive.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
First, if you want to understand storage engines you should look at
|
|
|
|
ha_example.cc and ha_example.h.
|
|
|
|
This example was written as a test case for a customer who needed
|
|
|
|
a storage engine without indexes that could compress data very well.
|
|
|
|
So, welcome to a completely compressed storage engine. This storage
|
2004-06-07 11:06:33 +02:00
|
|
|
engine only does inserts. No replace, deletes, or updates. All reads are
|
2004-05-21 03:13:11 +02:00
|
|
|
complete table scans. Compression is done through gzip (bzip compresses
|
|
|
|
better, but only marginally, if someone asks I could add support for
|
|
|
|
it too, but beaware that it costs a lot more in CPU time then gzip).
|
|
|
|
|
|
|
|
We keep a file pointer open for each instance of ha_archive for each read
|
|
|
|
but for writes we keep one open file handle just for that. We flush it
|
|
|
|
only if we have a read occur. gzip handles compressing lots of records
|
|
|
|
at once much better then doing lots of little records between writes.
|
|
|
|
It is possible to not lock on writes but this would then mean we couldn't
|
|
|
|
handle bulk inserts as well (that is if someone was trying to read at
|
|
|
|
the same time since we would want to flush).
|
|
|
|
|
|
|
|
No attempts at durability are made. You can corrupt your data.
|
|
|
|
|
|
|
|
For performance as far as table scans go it is quite fast. I don't have
|
|
|
|
good numbers but locally it has out performed both Innodb and MyISAM. For
|
|
|
|
Innodb the question will be if the table can be fit into the buffer
|
|
|
|
pool. For MyISAM its a question of how much the file system caches the
|
|
|
|
MyISAM file. With enough free memory MyISAM is faster. Its only when the OS
|
|
|
|
doesn't have enough memory to cache entire table that archive turns out
|
|
|
|
to be any faster. For writes it is always a bit slower then MyISAM. It has no
|
|
|
|
internal limits though for row length.
|
|
|
|
|
2004-06-07 11:06:33 +02:00
|
|
|
Examples between MyISAM (packed) and Archive.
|
2004-05-25 22:27:01 +02:00
|
|
|
|
|
|
|
Table with 76695844 identical rows:
|
|
|
|
29680807 a_archive.ARZ
|
|
|
|
920350317 a.MYD
|
|
|
|
|
|
|
|
|
|
|
|
Table with 8991478 rows (all of Slashdot's comments):
|
|
|
|
1922964506 comment_archive.ARZ
|
|
|
|
2944970297 comment_text.MYD
|
|
|
|
|
|
|
|
|
2004-05-21 03:13:11 +02:00
|
|
|
TODO:
|
|
|
|
Add bzip optional support.
|
|
|
|
Allow users to set compression level.
|
|
|
|
Add truncate table command.
|
|
|
|
Implement versioning, should be easy.
|
|
|
|
Implement optimize so we can fix broken tables.
|
|
|
|
Allow for errors, find a way to mark bad rows.
|
|
|
|
See if during an optimize you can make the table smaller.
|
|
|
|
Talk to the gzip guys, come up with a writable format so that updates are doable
|
|
|
|
without switching to a block method.
|
2004-06-07 11:06:33 +02:00
|
|
|
Add optional feature so that rows can be flushed at interval (which will cause less
|
|
|
|
compression but may speed up ordered searches).
|
2004-05-21 03:13:11 +02:00
|
|
|
|
|
|
|
-Brian
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Variables for archive share methods */
|
|
|
|
pthread_mutex_t archive_mutex;
|
|
|
|
static HASH archive_open_tables;
|
|
|
|
static int archive_init= 0;
|
|
|
|
|
|
|
|
/* The file extension */
|
|
|
|
#define ARZ ".ARZ"
|
|
|
|
|
|
|
|
/*
|
|
|
|
Used for hash table that tracks open tables.
|
|
|
|
*/
|
|
|
|
static byte* archive_get_key(ARCHIVE_SHARE *share,uint *length,
|
|
|
|
my_bool not_used __attribute__((unused)))
|
|
|
|
{
|
|
|
|
*length=share->table_name_length;
|
|
|
|
return (byte*) share->table_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Example of simple lock controls.
|
|
|
|
See ha_example.cc for a description.
|
|
|
|
*/
|
|
|
|
static ARCHIVE_SHARE *get_share(const char *table_name, TABLE *table)
|
|
|
|
{
|
|
|
|
ARCHIVE_SHARE *share;
|
|
|
|
uint length;
|
|
|
|
char *tmp_name;
|
|
|
|
|
|
|
|
if (!archive_init)
|
|
|
|
{
|
|
|
|
/* Hijack a mutex for init'ing the storage engine */
|
|
|
|
pthread_mutex_lock(&LOCK_mysql_create_db);
|
|
|
|
if (!archive_init)
|
|
|
|
{
|
|
|
|
archive_init++;
|
|
|
|
VOID(pthread_mutex_init(&archive_mutex,MY_MUTEX_INIT_FAST));
|
|
|
|
(void) hash_init(&archive_open_tables,system_charset_info,32,0,0,
|
|
|
|
(hash_get_key) archive_get_key,0,0);
|
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&LOCK_mysql_create_db);
|
|
|
|
}
|
|
|
|
pthread_mutex_lock(&archive_mutex);
|
|
|
|
length=(uint) strlen(table_name);
|
|
|
|
|
|
|
|
if (!(share=(ARCHIVE_SHARE*) hash_search(&archive_open_tables,
|
|
|
|
(byte*) table_name,
|
|
|
|
length)))
|
|
|
|
{
|
|
|
|
if (!(share=(ARCHIVE_SHARE *)
|
|
|
|
my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
|
|
|
|
&share, sizeof(*share),
|
|
|
|
&tmp_name, length+1,
|
|
|
|
NullS)))
|
|
|
|
{
|
|
|
|
pthread_mutex_unlock(&archive_mutex);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
share->use_count=0;
|
|
|
|
share->table_name_length=length;
|
|
|
|
share->table_name=tmp_name;
|
|
|
|
fn_format(share->data_file_name,table_name,"",ARZ,MY_REPLACE_EXT|MY_UNPACK_FILENAME);
|
|
|
|
strmov(share->table_name,table_name);
|
|
|
|
/*
|
2004-06-07 11:06:33 +02:00
|
|
|
It is expensive to open and close the data files and since you can't have
|
|
|
|
a gzip file that can be both read and written we keep a writer open
|
|
|
|
that is shared amoung all open tables.
|
2004-05-21 03:13:11 +02:00
|
|
|
*/
|
2004-06-07 11:06:33 +02:00
|
|
|
if ((share->archive_write= gzopen(share->data_file_name, "ab")) == NULL)
|
|
|
|
goto error;
|
|
|
|
if (my_hash_insert(&archive_open_tables, (byte*) share))
|
2004-05-21 03:13:11 +02:00
|
|
|
goto error;
|
|
|
|
thr_lock_init(&share->lock);
|
|
|
|
if (pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST))
|
|
|
|
goto error2;
|
|
|
|
}
|
|
|
|
share->use_count++;
|
|
|
|
pthread_mutex_unlock(&archive_mutex);
|
|
|
|
|
|
|
|
return share;
|
|
|
|
|
|
|
|
error2:
|
|
|
|
thr_lock_delete(&share->lock);
|
|
|
|
/* We close, but ignore errors since we already have errors */
|
|
|
|
(void)gzclose(share->archive_write);
|
|
|
|
error:
|
|
|
|
pthread_mutex_unlock(&archive_mutex);
|
|
|
|
my_free((gptr) share, MYF(0));
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Free lock controls.
|
|
|
|
See ha_example.cc for a description.
|
|
|
|
*/
|
|
|
|
static int free_share(ARCHIVE_SHARE *share)
|
|
|
|
{
|
|
|
|
int rc= 0;
|
|
|
|
pthread_mutex_lock(&archive_mutex);
|
|
|
|
if (!--share->use_count)
|
|
|
|
{
|
|
|
|
hash_delete(&archive_open_tables, (byte*) share);
|
|
|
|
thr_lock_delete(&share->lock);
|
|
|
|
pthread_mutex_destroy(&share->mutex);
|
|
|
|
my_free((gptr) share, MYF(0));
|
|
|
|
if (gzclose(share->archive_write) == Z_ERRNO)
|
2004-06-07 11:06:33 +02:00
|
|
|
rc= -1;
|
2004-05-21 03:13:11 +02:00
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&archive_mutex);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
We just implement one additional file extension.
|
|
|
|
*/
|
|
|
|
const char **ha_archive::bas_ext() const
|
|
|
|
{ static const char *ext[]= { ARZ, NullS }; return ext; }
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
When opening a file we:
|
|
|
|
Create/get our shared structure.
|
|
|
|
Init out lock.
|
|
|
|
We open the file we will read from.
|
|
|
|
Set the size of ref_length.
|
|
|
|
*/
|
|
|
|
int ha_archive::open(const char *name, int mode, uint test_if_locked)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::open");
|
|
|
|
|
2004-06-07 11:06:33 +02:00
|
|
|
if (!(share= get_share(name, table)))
|
2004-05-21 03:13:11 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
thr_lock_data_init(&share->lock,&lock,NULL);
|
|
|
|
|
2004-06-07 11:06:33 +02:00
|
|
|
if ((archive= gzopen(share->data_file_name, "rb")) == NULL)
|
|
|
|
{
|
|
|
|
(void)free_share(share); //We void since we already have an error
|
2004-05-21 03:13:11 +02:00
|
|
|
DBUG_RETURN(-1);
|
2004-06-07 11:06:33 +02:00
|
|
|
}
|
2004-05-21 03:13:11 +02:00
|
|
|
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Closes the file. We first close this storage engines file handle to the
|
2004-06-07 11:06:33 +02:00
|
|
|
archive and then remove our reference count to the table (and possibly
|
2004-05-21 03:13:11 +02:00
|
|
|
free it as well).
|
|
|
|
*/
|
|
|
|
int ha_archive::close(void)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::close");
|
|
|
|
int rc= 0;
|
|
|
|
if (gzclose(archive) == Z_ERRNO)
|
|
|
|
rc =-1;
|
|
|
|
rc |= free_share(share);
|
2004-05-25 22:27:01 +02:00
|
|
|
DBUG_RETURN(rc);
|
2004-05-21 03:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
We create our data file here. The format is pretty simple. The first bytes in
|
|
|
|
any file are the version number. Currently we do nothing with this, but in
|
|
|
|
the future this gives us the ability to figure out version if we change the
|
|
|
|
format at all. After the version we starting writing our rows. Unlike other
|
|
|
|
storage engines we do not "pack" our data. Since we are about to do a general
|
|
|
|
compression, packing would just be a waste of CPU time. If the table has blobs
|
|
|
|
they are written after the row in the order of creation.
|
|
|
|
So to read a row we:
|
|
|
|
Read the version
|
|
|
|
Read the record and copy it into buf
|
|
|
|
Loop through any blobs and read them
|
|
|
|
*/
|
|
|
|
int ha_archive::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info)
|
|
|
|
{
|
|
|
|
File create_file;
|
|
|
|
char name_buff[FN_REFLEN];
|
|
|
|
size_t written;
|
|
|
|
DBUG_ENTER("ha_archive::create");
|
|
|
|
|
2004-06-07 11:06:33 +02:00
|
|
|
if ((create_file= my_create(fn_format(name_buff,name,"",ARZ,MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,
|
2004-05-21 03:13:11 +02:00
|
|
|
O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
|
|
|
|
DBUG_RETURN(-1);
|
2004-06-07 11:06:33 +02:00
|
|
|
if ((archive= gzdopen(create_file, "ab")) == NULL)
|
|
|
|
{
|
|
|
|
delete_table(name);
|
2004-05-21 03:13:11 +02:00
|
|
|
DBUG_RETURN(-1);
|
2004-06-07 11:06:33 +02:00
|
|
|
}
|
|
|
|
version= ARCHIVE_VERSION;
|
|
|
|
written= gzwrite(archive, &version, sizeof(version));
|
2004-05-21 03:13:11 +02:00
|
|
|
if (written == 0 || written != sizeof(version))
|
2004-06-07 11:06:33 +02:00
|
|
|
{
|
|
|
|
delete_table(name);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
if (gzclose(archive))
|
|
|
|
{
|
|
|
|
delete_table(name);
|
2004-05-21 03:13:11 +02:00
|
|
|
DBUG_RETURN(-1);
|
2004-06-07 11:06:33 +02:00
|
|
|
}
|
2004-05-21 03:13:11 +02:00
|
|
|
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2004-06-07 11:06:33 +02:00
|
|
|
Look at ha_archive::open() for an explanation of the row format.
|
2004-05-21 03:13:11 +02:00
|
|
|
Here we just write out the row.
|
|
|
|
*/
|
|
|
|
int ha_archive::write_row(byte * buf)
|
|
|
|
{
|
|
|
|
char *pos;
|
|
|
|
z_off_t written;
|
|
|
|
DBUG_ENTER("ha_archive::write_row");
|
|
|
|
|
|
|
|
statistic_increment(ha_write_count,&LOCK_status);
|
|
|
|
if (table->timestamp_default_now)
|
2004-05-25 22:27:01 +02:00
|
|
|
update_timestamp(buf+table->timestamp_default_now-1);
|
2004-06-07 11:06:33 +02:00
|
|
|
written= gzwrite(share->archive_write, buf, table->reclength);
|
2004-05-21 03:13:11 +02:00
|
|
|
share->dirty= true;
|
|
|
|
if (written == 0 || written != table->reclength)
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
|
|
|
|
for (Field_blob **field=table->blob_field ; *field ; field++)
|
|
|
|
{
|
|
|
|
char *ptr;
|
|
|
|
uint32 size= (*field)->get_length();
|
|
|
|
|
|
|
|
(*field)->get_ptr(&ptr);
|
2004-06-07 11:06:33 +02:00
|
|
|
written= gzwrite(share->archive_write, ptr, (unsigned)size);
|
2004-05-21 03:13:11 +02:00
|
|
|
if (written == 0 || written != size)
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
All calls that need to scan the table start with this method. If we are told
|
|
|
|
that it is a table scan we rewind the file to the beginning, otherwise
|
|
|
|
we assume the position will be set.
|
|
|
|
*/
|
|
|
|
int ha_archive::rnd_init(bool scan)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::rnd_init");
|
|
|
|
int read; // gzread() returns int, and we use this to check the header
|
2004-06-07 11:06:33 +02:00
|
|
|
|
2004-05-21 03:13:11 +02:00
|
|
|
/* We rewind the file so that we can read from the beginning if scan */
|
|
|
|
if(scan)
|
2004-06-07 11:06:33 +02:00
|
|
|
{
|
|
|
|
records= 0;
|
2004-05-21 03:13:11 +02:00
|
|
|
if (gzrewind(archive))
|
|
|
|
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
2004-06-07 11:06:33 +02:00
|
|
|
}
|
|
|
|
|
2004-05-21 03:13:11 +02:00
|
|
|
/*
|
|
|
|
If dirty, we lock, and then reset/flush the data.
|
|
|
|
I found that just calling gzflush() doesn't always work.
|
|
|
|
*/
|
|
|
|
if (share->dirty == true)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&share->mutex);
|
|
|
|
if (share->dirty == true)
|
|
|
|
{
|
2004-06-07 11:06:33 +02:00
|
|
|
/* I was having problems with OSX, but it worked for 10.3 so I am wrapping this with and ifdef */
|
|
|
|
#ifdef BROKEN_GZFLUSH
|
2004-05-21 03:13:11 +02:00
|
|
|
gzclose(share->archive_write);
|
2004-06-07 11:06:33 +02:00
|
|
|
if ((share->archive_write= gzopen(share->data_file_name, "ab")) == NULL)
|
2004-05-21 03:13:11 +02:00
|
|
|
{
|
|
|
|
pthread_mutex_unlock(&share->mutex);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2004-06-07 11:06:33 +02:00
|
|
|
#else
|
|
|
|
gzflush(share->archive_write, Z_SYNC_FLUSH);
|
|
|
|
#endif
|
2004-05-21 03:13:11 +02:00
|
|
|
share->dirty= false;
|
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&share->mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
At the moment we just check the size of version to make sure the header is
|
|
|
|
intact.
|
|
|
|
*/
|
2004-06-07 11:06:33 +02:00
|
|
|
if (scan)
|
|
|
|
{
|
|
|
|
read= gzread(archive, &version, sizeof(version));
|
|
|
|
if (read == 0 || read != sizeof(version))
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
|
2004-05-21 03:13:11 +02:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
This is the method that is used to read a row. It assumes that the row is
|
|
|
|
positioned where you want it.
|
|
|
|
*/
|
2004-06-07 11:06:33 +02:00
|
|
|
int ha_archive::get_row(byte *buf)
|
2004-05-21 03:13:11 +02:00
|
|
|
{
|
|
|
|
int read; // Bytes read, gzread() returns int
|
|
|
|
char *last;
|
|
|
|
size_t total_blob_length= 0;
|
2004-06-07 11:06:33 +02:00
|
|
|
DBUG_ENTER("ha_archive::get_row");
|
2004-05-21 03:13:11 +02:00
|
|
|
|
2004-06-07 11:06:33 +02:00
|
|
|
read= gzread(archive, buf, table->reclength);
|
2004-05-21 03:13:11 +02:00
|
|
|
|
|
|
|
/* If we read nothing we are at the end of the file */
|
|
|
|
if (read == 0)
|
|
|
|
DBUG_RETURN(HA_ERR_END_OF_FILE);
|
|
|
|
|
|
|
|
/* If the record is the wrong size, the file is probably damaged */
|
|
|
|
if (read != table->reclength)
|
|
|
|
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
|
|
|
|
|
|
|
/* Calculate blob length, we use this for our buffer */
|
|
|
|
for (Field_blob **field=table->blob_field; *field ; field++)
|
|
|
|
total_blob_length += (*field)->get_length();
|
|
|
|
|
|
|
|
/* Adjust our row buffer if we need be */
|
|
|
|
buffer.alloc(total_blob_length);
|
2004-06-07 11:06:33 +02:00
|
|
|
last= (char *)buffer.ptr();
|
2004-05-21 03:13:11 +02:00
|
|
|
|
2004-06-07 11:06:33 +02:00
|
|
|
/* Loop through our blobs and read them */
|
2004-05-21 03:13:11 +02:00
|
|
|
for (Field_blob **field=table->blob_field; *field ; field++)
|
|
|
|
{
|
|
|
|
size_t size= (*field)->get_length();
|
2004-06-07 11:06:33 +02:00
|
|
|
read= gzread(archive, last, size);
|
2004-05-21 03:13:11 +02:00
|
|
|
if (read == 0 || read != size)
|
|
|
|
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
|
|
|
(*field)->set_ptr(size, last);
|
|
|
|
last += size;
|
|
|
|
}
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Called during ORDER BY. Its position is either from being called sequentially
|
|
|
|
or by having had ha_archive::rnd_pos() called before it is called.
|
|
|
|
*/
|
|
|
|
int ha_archive::rnd_next(byte *buf)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::rnd_next");
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
statistic_increment(ha_read_rnd_next_count,&LOCK_status);
|
2004-06-07 11:06:33 +02:00
|
|
|
current_position= gztell(archive);
|
|
|
|
rc= get_row(buf);
|
2004-05-21 03:13:11 +02:00
|
|
|
if (!(HA_ERR_END_OF_FILE == rc))
|
|
|
|
records++;
|
|
|
|
|
|
|
|
DBUG_RETURN(rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Thanks to the table flag HA_REC_NOT_IN_SEQ this will be called after
|
|
|
|
each call to ha_archive::rnd_next() if an ordering of the rows is
|
|
|
|
needed.
|
|
|
|
*/
|
|
|
|
void ha_archive::position(const byte *record)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::position");
|
|
|
|
ha_store_ptr(ref, ref_length, current_position);
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
This is called after a table scan for each row if the results of the scan need
|
|
|
|
to be ordered. It will take *pos and use it to move the cursor in the file so
|
|
|
|
that the next row that is called is the correctly ordered row.
|
|
|
|
*/
|
|
|
|
int ha_archive::rnd_pos(byte * buf, byte *pos)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::rnd_pos");
|
|
|
|
statistic_increment(ha_read_rnd_count,&LOCK_status);
|
2004-06-07 11:06:33 +02:00
|
|
|
current_position= ha_get_ptr(pos, ref_length);
|
2004-05-21 03:13:11 +02:00
|
|
|
z_off_t seek= gzseek(archive, current_position, SEEK_SET);
|
|
|
|
|
2004-06-07 11:06:33 +02:00
|
|
|
DBUG_RETURN(get_row(buf));
|
2004-05-21 03:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
|
|
|
|
Everything below here is default, please look at ha_example.cc for
|
|
|
|
descriptions.
|
|
|
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
int ha_archive::update_row(const byte * old_data, byte * new_data)
|
|
|
|
{
|
|
|
|
|
|
|
|
DBUG_ENTER("ha_archive::update_row");
|
2004-06-23 21:26:34 +02:00
|
|
|
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
|
2004-05-21 03:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int ha_archive::delete_row(const byte * buf)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::delete_row");
|
2004-06-23 21:26:34 +02:00
|
|
|
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
|
2004-05-21 03:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int ha_archive::index_read(byte * buf, const byte * key,
|
|
|
|
uint key_len __attribute__((unused)),
|
|
|
|
enum ha_rkey_function find_flag
|
|
|
|
__attribute__((unused)))
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::index_read");
|
2004-06-23 21:26:34 +02:00
|
|
|
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
|
2004-05-21 03:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int ha_archive::index_read_idx(byte * buf, uint index, const byte * key,
|
|
|
|
uint key_len __attribute__((unused)),
|
|
|
|
enum ha_rkey_function find_flag
|
|
|
|
__attribute__((unused)))
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::index_read_idx");
|
2004-06-23 21:26:34 +02:00
|
|
|
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
|
2004-05-21 03:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int ha_archive::index_next(byte * buf)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::index_next");
|
2004-06-23 21:26:34 +02:00
|
|
|
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
|
2004-05-21 03:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int ha_archive::index_prev(byte * buf)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::index_prev");
|
2004-06-23 21:26:34 +02:00
|
|
|
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
|
2004-05-21 03:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int ha_archive::index_first(byte * buf)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::index_first");
|
2004-06-23 21:26:34 +02:00
|
|
|
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
|
2004-05-21 03:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int ha_archive::index_last(byte * buf)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::index_last");
|
2004-06-23 21:26:34 +02:00
|
|
|
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
|
2004-05-21 03:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ha_archive::info(uint flag)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::info");
|
2004-06-07 11:06:33 +02:00
|
|
|
|
2004-05-21 03:13:11 +02:00
|
|
|
/* This is a lie, but you don't want the optimizer to see zero or 1 */
|
|
|
|
if (records < 2)
|
2004-06-07 11:06:33 +02:00
|
|
|
records= 2;
|
|
|
|
|
2004-05-21 03:13:11 +02:00
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ha_archive::extra(enum ha_extra_function operation)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::extra");
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ha_archive::reset(void)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::reset");
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int ha_archive::external_lock(THD *thd, int lock_type)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::external_lock");
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
THR_LOCK_DATA **ha_archive::store_lock(THD *thd,
|
|
|
|
THR_LOCK_DATA **to,
|
|
|
|
enum thr_lock_type lock_type)
|
|
|
|
{
|
|
|
|
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
|
|
|
|
lock.type=lock_type;
|
|
|
|
*to++= &lock;
|
|
|
|
return to;
|
|
|
|
}
|
|
|
|
|
|
|
|
ha_rows ha_archive::records_in_range(int inx,
|
|
|
|
const byte *start_key,uint start_key_len,
|
|
|
|
enum ha_rkey_function start_search_flag,
|
|
|
|
const byte *end_key,uint end_key_len,
|
|
|
|
enum ha_rkey_function end_search_flag)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("ha_archive::records_in_range ");
|
2004-06-23 21:26:34 +02:00
|
|
|
DBUG_RETURN(records); // HA_ERR_WRONG_COMMAND
|
2004-05-21 03:13:11 +02:00
|
|
|
}
|
|
|
|
#endif /* HAVE_ARCHIVE_DB */
|