This is the bas_ext method. It is called to
provide the MySQL server with a list of file extensions used
by the storage engine. The list returned is a null-terminated
string array.
By providing a list of extensions, storage engines can in many
cases omit the
[custom-engine.html#custom-engine-api-reference-delete_table
delete_table()] method as the MySQL server
will close all references to the table and delete all files
with the specified extension.
Return value is a null-terminated string array of storage
engine extensions. The following is an example from the
CSV engine:
static const char *ha_tina_exts[] =
{
".CSV",
NullS
};
static const char *ha_tina_exts[] =
{
".CSV",
NullS
};
const char **ha_tina::bas_ext() const
{
return ha_tina_exts;
}
This is the close method.
Closes a table. A good time to free any resources that we have allocated.
Called from sql_base.cc, sql_select.cc, and table.cc. In
sql_select.cc it is only used to close up temporary tables or
during the process where a temporary table is converted over
to being a MyISAM table. For sql_base.cc
look at close_data_tables().
|
|
|
|
||
|
const char * |
|
TABLE * |
|
HA_CREATE_INFO * |
|
This is the create method.
create() is called to create a table. The
variable name will have the name of the table. When
create() is called you do not need to open
the table. Also, the .frm file will have
already been created so adjusting
create_info is not recommended.
Called from handler.cc by
ha_create_table().
Example from the CSV storage engine:
int ha_tina::create(const char *name, TABLE *table_arg,
HA_CREATE_INFO *create_info)
{
char name_buff[FN_REFLEN];
File create_file;
DBUG_ENTER("ha_tina::create");
if ((create_file= my_create(fn_format(name_buff, name, "", ".CSV",
MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,
O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
DBUG_RETURN(-1);
my_close(create_file,MYF(0));
DBUG_RETURN(0);
}
This is the delete_row method.
buf will contain a copy of the row to be
deleted. The server will call this right after the current row
has been called (from either a previous
rnd_next() or index call). If you keep a
pointer to the last row or can access a primary key it will
make doing the deletion quite a bit easier. Keep in mind that
the server does not guarantee consecutive deletions.
ORDER BY clauses can be used.
Called in sql_acl.cc and
sql_udf.cc to manage internal table
information. Called in sql_delete.cc,
sql_insert.cc, and
sql_select.cc. In
sql_select it is used for removing
duplicates, while in insert it is used for
REPLACE calls.
Delete all files with extension from
[custom-engine.html#custom-engine-api-reference-bas_ext
bas_ext()].
This is the delete_table method.
Used to delete a table. By the time
delete_table() has been called all opened
references to this table will have been closed (and your
globally shared references released). The variable name will
be the name of the table. You will need to remove any files
you have created at this point.
If you do not implement this, the default
delete_table() is called from
handler.cc, and it will delete all files
with the file extensions returned by
bas_ext(). We assume that the handler may
return more extensions than were actually used for the file.
Called from handler.cc by
delete_table and
ha_create_table(). Only used during create
if the
table_flagHA_DROP_BEFORE_CREATE
was specified for the storage engine.
0 if we successfully deleted at least
one file from base_ext and didn't get
any other errors than ENOENT
# : Error
This is the external_lock method.
The locking methods for mysql section in
lock.cc has additional comments on this
topic that may be useful to read.
This creates a lock on the table. If you are implementing a
storage engine that can handle transactions, look at
ha_innodb.cc to see how you will want to go
about doing this. Otherwise you should consider calling
flock() here.
Called from lock.cc by
lock_external() and
unlock_external(). Also called from
sql_table.cc by
copy_data_between_tables().
This is the extra method.
extra() is called whenever the server
wishes to send a hint to the storage engine. The
MyISAM engine implements the most hints.
ha_innodb.cc has the most exhaustive list
of these hints.
This section is yet to be written.
This is the index_end method. Generally it
is used as a counterpart to the index_init
method, cleaning up any resources allocated for index
scanning.
This is the index_first method.
index_first() asks for the first key in the
index.
Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
Signals the storage engine that an index scan is about to occur. Storage engine should allocate any resources needed.
This is the index_init method. This method
is called before an index scan, allowing the storage engine to
allocate resources and make preparations.
This is the index_last method.
index_last() asks for the last key in the
index.
Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
|
|
|
|
||
|
||
|
byte * |
|
const byte * |
|
ulonglong |
|
enum ha_rkey_function |
|
This is the index_read method.
Positions an index cursor to the index specified in the handle. Fetches the row if available. If the key value is null, begin at the first key of the index.
|
|
|
|
||
|
||
|
||
|
byte * |
|
uint |
|
const byte * |
|
ulonglong |
|
enum ha_rkey_function |
|
This is the index_read_idx method.
Positions an index cursor to the index specified in key. Fetches the row if any. This is only used to read whole keys.
|
|
|
|
||
|
byte * |
|
const byte * |
|
ulonglong |
|
This is the info method.
info() is used to return information to the optimizer. Currently, this table handler doesn't implement most of the fields really needed. SHOW also makes use of this data Another note, you will probably want to have the following in your code: if (records < 2) records = The reason is that the server will optimize for cases of only a single record. If in a table scan you don't know the number of records it will probably be better to set records to two so you can return as many records as you need. Along with records a few more variables you may wish to set are: records deleted data_file_length index_file_length delete_length check_time See public variables in handler.h for more information.
Called in: filesort.cc ha_heap.cc item_sum.cc opt_sum.cc sql_delete.cc sql_delete.cc sql_derived.cc sql_select.cc sql_select.cc sql_select.cc sql_select.cc sql_select.cc sql_show.cc sql_show.cc sql_show.cc sql_show.cc sql_table.cc sql_union.cc sql_update.cc
|
|
|
|
||
|
const char * |
|
int |
|
uint |
|
This is the open method.
Used for opening tables. The name will be the name of the file. A table is opened when it needs to be opened. For instance when a request comes in for a select on the table (tables are not open and closed for each request, they are cached).
Called from handler.cc by handler::ha_open(). The server opens all tables by calling ha_open() which then calls the handler specific open().
A handler object is opened as part of its initialization and before being used for normal queries (not before meta-data changes always.) If the object was opened it will also be closed before being deleted.
This is the open method.
open is called to open a database table.
The first parameter is the name of the table to be opened. The
second parameter determines what file to open or what
operation to take. The values are defined in
handler.h and are copied here for your
convenience:
#define HA_OPEN_KEYFILE 1 #define HA_OPEN_RNDFILE 2 #define HA_GET_INDEX 4 #define HA_GET_INFO 8 /* do a ha_info() after open */ #define HA_READ_ONLY 16 /* File opened as readonly */ #define HA_TRY_READ_ONLY 32 /* Try readonly if can't open with read and write */ #define HA_WAIT_IF_LOCKED 64 /* Wait if locked on open */ #define HA_ABORT_IF_LOCKED 128 /* skip if locked on open.*/ #define HA_BLOCK_LOCK 256 /* unlock when reading some records */ #define HA_OPEN_TEMPORARY 512
The final option dictates whether the handler should check for a lock on the table before opening it.
Typically your storage engine will need to implement some form
of shared access control to prevent file corruption is a
multi-threaded environment. For an example of how to implement
file locking, see the get_share() and
free_share() methods of
sql/examples/ha_tina.cc.
This example is from the CSV storage
engine:
int ha_tina::open(const char *name, int mode, uint test_if_locked)
{
DBUG_ENTER("ha_tina::open");
if (!(share= get_share(name, table)))
DBUG_RETURN(1);
thr_lock_data_init(&share->lock,&lock,NULL);
ref_length=sizeof(off_t);
DBUG_RETURN(0);
}
This is the position method.
position() is called after each call to rnd_next() if the data needs to be ordered. You can do something like the following to store the position: my_store_ptr(ref, ref_length, current_position);
The server uses ref to store data. ref_length in the above case is the size needed to store current_position. ref is just a byte array that the server will maintain. If you are using offsets to mark rows, then current_position should be the offset. If it is a primary key, then it needs to be a primary key.
Called from filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc.
|
|
|
|
||
|
uint |
|
key_range * |
|
key_range * |
|
This is the records_in_range method.
Given a starting key, and an ending key estimate the number of rows that will exist between the two. end_key may be empty which in case determine if start_key matches any rows.
Used by optimizer to calculate cost of using a particular index.
Called from opt_range.cc by check_quick_keys().
This is the rnd_init method.
rnd_init() is called when the system wants the storage engine to do a table scan.
Unlike index_init(), rnd_init() can be called two times without rnd_end() in between (it only makes sense if scan=1). then the second call should prepare for the new table scan (e.g if rnd_init allocates the cursor, second call should position it to the start of the table, no need to deallocate and allocate it again
Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc, and sql_update.cc.
This is the rnd_next method.
This is called for each row of the table scan. When you run out of records you should return HA_ERR_END_OF_FILE. Fill buff up with the row information. The Field structure for the table is the key to getting data into buf in a manner that will allow the server to understand it.
Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc, and sql_update.cc.
This example is from the ARCHIVE storage
engine:
int ha_archive::rnd_next(byte *buf)
{
int rc;
DBUG_ENTER("ha_archive::rnd_next");
if (share->crashed)
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
if (!scan_rows)
DBUG_RETURN(HA_ERR_END_OF_FILE);
scan_rows--;
statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
&LOCK_status);
current_position= gztell(archive);
rc= get_row(archive, buf);
if (rc != HA_ERR_END_OF_FILE)
records++;
DBUG_RETURN(rc);
}
This is the rnd_pos method.
Used for finding row previously marked with position. This is useful for large sorts.
This is like rnd_next, but you are given a position to use to determine the row. The position will be of the type that you stored in ref. You can use ha_get_ptr(pos,ref_length) to retrieve whatever key or position you saved when position() was called. Called from filesort.cc records.cc sql_insert.cc sql_select.cc sql_update.cc.
|
|
|
|
THD * |
|
thr_lock_type |
|
This is the start_stmt method.
When table is locked a statement is started by calling start_stmt instead of external_lock
Make any preparations needed for a transaction start (if there is no current running transaction).
|
|
|
|
||
|
THD * |
|
THR_LOCK_DATA ** |
|
enum thr_lock_type |
|
This is the store_lock method.
The idea with handler::store_lock() is the following:
The statement decided which locks we should need for the table for updates/deletes/inserts we get WRITE locks, for SELECT... we get read locks.
Before adding the lock into the table lock handler mysqld calls store lock with the requested locks. Store lock can modify the lock level, for example, change blocking write lock to non-blocking, ignore the lock (if we don't want to use MySQL table locks at all), or add locks for many tables (like we do when we are using a MERGE handler).
When releasing locks, store_lock() are also called. In this case one usually doesn't have to do anything.
If the argument of store_lock is TL_IGNORE, it means that MySQL requests the handler to store the same lock level as the last time.
Called from lock.cc by get_lock_data().
The following example is from the ARCHIVE
storage engine:
/*
Below is an example of how to setup row level locking.
*/
THR_LOCK_DATA **ha_archive::store_lock(THD *thd,
THR_LOCK_DATA **to,
enum thr_lock_type lock_type)
{
if (lock_type == TL_WRITE_DELAYED)
delayed_insert= TRUE;
else
delayed_insert= FALSE;
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
{
/*
Here is where we get into the guts of a row level lock.
If TL_UNLOCK is set
If we are not doing a LOCK TABLE or DISCARD/IMPORT
TABLESPACE, then allow multiple writers
*/
if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
lock_type <= TL_WRITE) && !thd->in_lock_tables
&& !thd->tablespace_op)
lock_type = TL_WRITE_ALLOW_WRITE;
/*
In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
MySQL would use the lock TL_READ_NO_INSERT on t2, and that
would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
to t2. Convert the lock to a normal read lock to allow
concurrent inserts to t2.
*/
if (lock_type == TL_READ_NO_INSERT && !thd->in_lock_tables)
lock_type = TL_READ;
lock.type=lock_type;
}
*to++= &lock;
return to;
}
The following is the minimal implementation, for a storage engine that does not need to downgrade locks:
THR_LOCK_DATA **ha_tina::store_lock(THD *thd,
THR_LOCK_DATA **to,
enum thr_lock_type lock_type)
{
/* Note that if the lock type is TL_IGNORE we don't update lock.type,
preserving the previous lock level */
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
lock.type=lock_type;
/* the heart of the store_lock() method and it's main purpose -
storing the (possibly changed) lock level into the provided
memory */
*to++= &lock;
return to;
}
See also ha_myisammrg::store_lock() for
more complex implementation
|
|
|
|
const byte * |
|
byte * |
|
This is the update_row method.
old_data will have the previous row record in it, while new_data will have the newest data in it.
The server can do updates based on ordering if an ORDER BY clause was used. Consecutive ordering is not guaranteed.
Currently, new_data will not have an updated auto_increament record, or and updated timestamp field. You can do these for example by doing these: if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) table->timestamp_field->set_time(); if (table->next_number_field && record == table->record[0]) update_auto_increment();
Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
This is the write_row method.
write_row() inserts a row. No
[custom-engine.html#custom-engine-api-reference-extra
extra()] hint is given currently if a bulk
load is happening. buf is a byte array of data with a size of
table->s->reclength
You can use the field information to extract the data from the native byte array type. Example of this would be: for (Field **field=table->field ; *field ; field++) { ... }
BLOBs must be handled specially:
for (ptr= table->s->blob_field, end= ptr + table->s->blob_fields ; ptr != end ; ptr++)
{
char *data_ptr;
uint32 size= ((Field_blob*)table->field[*ptr])->get_length();
((Field_blob*)table->field[*ptr])->get_ptr(&data_ptr);
...
}
See ha_tina.cc for an example of extracting all of the data as strings.
See the note for update_row() on
auto_increments and timestamps. This case also applied to
write_row().
Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc, sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
