diff options
author | unknown <brian@brian-akers-computer.local> | 2004-05-11 15:59:20 -0700 |
---|---|---|
committer | unknown <brian@brian-akers-computer.local> | 2004-05-11 15:59:20 -0700 |
commit | 8351c87f5e1ab1547b238267cf3f2fc8050d8dc6 (patch) | |
tree | ed5ed1eece3a912b188b2071bb9b7563328b809e /sql | |
parent | fabcf36968045a2df20f336f2082ccea65708a10 (diff) | |
download | mariadb-git-8351c87f5e1ab1547b238267cf3f2fc8050d8dc6.tar.gz |
Added comments to all methods. Added explanation for a sequential read through a storage engine.
sql/examples/ha_example.cc:
Documentation updates (lots of comments in the code).
sql/examples/ha_example.h:
Documenation update, lots of comments in the code.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/examples/ha_example.cc | 370 | ||||
-rw-r--r-- | sql/examples/ha_example.h | 40 |
2 files changed, 401 insertions, 9 deletions
diff --git a/sql/examples/ha_example.cc b/sql/examples/ha_example.cc index e2463761e67..b8ae5967475 100644 --- a/sql/examples/ha_example.cc +++ b/sql/examples/ha_example.cc @@ -14,6 +14,55 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + ha_example is a stubbed storage engine. It does nothing at this point. It + will let you create/open/delete tables but that is all. You can enable it + in your buld by doing the following during your build process: + ./configure --with-example-storage-engine + + Once this is done mysql will let you create tables with: + CREATE TABLE A (...) ENGINE=EXAMPLE; + + The example is setup to use table locks. It implements an example "SHARE" + that is inserted into a hash by table name. You can use this to store + information of state that any example handler object will be able to see + if it is using the same table. + + Please read the object definition in ha_example.h before reading the rest + if this file. + + To get an idea of what occurs here is an example select that would do a + scan of an entire table: + ha_example::store_lock + ha_example::external_lock + ha_example::info + ha_example::rnd_init + ha_example::extra + ENUM HA_EXTRA_CACHE Cash record in HA_rrnd() + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::extra + ENUM HA_EXTRA_NO_CACHE End cacheing of records (def) + ha_example::external_lock + ha_example::extra + ENUM HA_EXTRA_RESET Reset database to after open + + In the above example has 9 row called before rnd_next signalled that it was + at the end of its data. In the above example the table was already opened + (or you would have seen a call to ha_example::open(). Calls to + ha_example::extra() are hints as to what will be occuring to the request. + + Happy coding! + -Brian +*/ + #ifdef __GNUC__ #pragma implementation // gcc: Class implementation #endif @@ -24,10 +73,14 @@ #include "ha_example.h" /* Variables for example share methods */ -pthread_mutex_t example_mutex; -static HASH example_open_tables; -static int example_init= 0; +static HASH example_open_tables; // Hash used to track open tables +pthread_mutex_t example_mutex; // This is the mutex we use to init the hash +static int example_init= 0; // Variable for checking the init state of hash + +/* + Function we use in the creation of our hash to get key. +*/ static byte* example_get_key(EXAMPLE_SHARE *share,uint *length, my_bool not_used __attribute__((unused))) { @@ -37,7 +90,9 @@ static byte* example_get_key(EXAMPLE_SHARE *share,uint *length, /* - Example of simple lock controls. + Example of simple lock controls. The "share" it creates is structure we will + pass to each example handler. Do you have to have one of these? Well, you have + pieces that are used for locking, and they are needed to function. */ static EXAMPLE_SHARE *get_share(const char *table_name, TABLE *table) { @@ -45,6 +100,12 @@ static EXAMPLE_SHARE *get_share(const char *table_name, TABLE *table) uint length; char *tmp_name; + /* + So why does this exist? There is no way currently to init a storage engine. + Innodb and BDB both have modifications to the server to allow them to + do this. Since you will not want to do this, this is probably the next + best method. + */ if (!example_init) { /* Hijack a mutex for init'ing the storage engine */ @@ -101,7 +162,8 @@ error: /* - Free lock controls. + Free lock controls. We call this whenever we close a table. If the table had + the last reference to the share then we free memory associated with it. */ static int free_share(EXAMPLE_SHARE *share) { @@ -119,10 +181,24 @@ static int free_share(EXAMPLE_SHARE *share) } +/* + If frm_error() is called then we will use this to to find out what file extentions + exist for the storage engine. This is also used by the default rename_table and + delete_table method in handler.cc. +*/ const char **ha_example::bas_ext() const { static const char *ext[]= { NullS }; return ext; } +/* + 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(). +*/ int ha_example::open(const char *name, int mode, uint test_if_locked) { DBUG_ENTER("ha_example::open"); @@ -134,18 +210,66 @@ int ha_example::open(const char *name, int mode, uint test_if_locked) DBUG_RETURN(0); } + +/* + Closes a table. We call the free_share() function to free any resources + that we have allocated in the "shared" structure. + + 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(). +*/ int ha_example::close(void) { DBUG_ENTER("ha_example::close"); DBUG_RETURN(free_share(share)); } + +/* + write_row() inserts a row. No extra() hint is given currently if a bulk load + is happeneding. buf() is a byte array of data. 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++) + { + ... + } + + See ha_tina.cc for an example of extracting all of the data as strings. + ha_berekly.cc has an example of how to store it intact by "packing" it + for ha_berkeley's own native storage type. + + 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. +*/ int ha_example::write_row(byte * buf) { DBUG_ENTER("ha_example::write_row"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + Yes, update_row() does what you expect, it updates a row. old_data will have + the previous row record in it, while new_data will have the newest data in + it. + Keep in mind that the server can do updates based on ordering if an ORDER BY + clause was used. Consecutive ordering is not guarenteed. + 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_on_update_now) + update_timestamp(new_row+table->timestamp_on_update_now-1); + 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. +*/ int ha_example::update_row(const byte * old_data, byte * new_data) { @@ -153,12 +277,32 @@ int ha_example::update_row(const byte * old_data, byte * new_data) DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + This will delete a row. 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 no guarentee 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. +*/ int ha_example::delete_row(const byte * buf) { DBUG_ENTER("ha_example::delete_row"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + 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. +*/ int ha_example::index_read(byte * buf, const byte * key, uint key_len __attribute__((unused)), enum ha_rkey_function find_flag @@ -168,6 +312,11 @@ int ha_example::index_read(byte * buf, const byte * key, DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + Positions an index cursor to the index specified in key. Fetches the + row if any. This is only used to read whole keys. +*/ int ha_example::index_read_idx(byte * buf, uint index, const byte * key, uint key_len __attribute__((unused)), enum ha_rkey_function find_flag @@ -178,66 +327,187 @@ int ha_example::index_read_idx(byte * buf, uint index, const byte * key, } +/* + Used to read forward through the index. +*/ int ha_example::index_next(byte * buf) { DBUG_ENTER("ha_example::index_next"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + Used to read backwards through the index. +*/ int ha_example::index_prev(byte * buf) { DBUG_ENTER("ha_example::index_prev"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + 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. +*/ int ha_example::index_first(byte * buf) { DBUG_ENTER("ha_example::index_first"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + 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. +*/ int ha_example::index_last(byte * buf) { DBUG_ENTER("ha_example::index_last"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + rnd_init() is called when the system wants the storage engine to do a table + scan. + See the example in the introduction at the top of this file to see when + rnd_init() is called. + + Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc, + and sql_update.cc. +*/ int ha_example::rnd_init(bool scan) { DBUG_ENTER("ha_example::rnd_init"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + 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. +*/ int ha_example::rnd_next(byte *buf) { DBUG_ENTER("ha_example::rnd_next"); DBUG_RETURN(HA_ERR_END_OF_FILE); } + +/* + 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: + ha_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 like in + BDB, then it needs to be a primary key. + + Called from filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc. +*/ void ha_example::position(const byte *record) { DBUG_ENTER("ha_example::position"); DBUG_VOID_RETURN; } + +/* + 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. +*/ int ha_example::rnd_pos(byte * buf, byte *pos) { DBUG_ENTER("ha_example::rnd_pos"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + ::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 = 2; + 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 + Take a look at the 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 + +*/ void ha_example::info(uint flag) { DBUG_ENTER("ha_example::info"); DBUG_VOID_RETURN; } + +/* + 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. +*/ int ha_example::extra(enum ha_extra_function operation) { DBUG_ENTER("ha_example::extra"); DBUG_RETURN(0); } + +/* + Deprecated and likely to be removed in the future. Storage engines normally + just make a call like: + ha_example::extra(HA_EXTRA_RESET); + to handle it. +*/ int ha_example::reset(void) { DBUG_ENTER("ha_example::reset"); @@ -245,18 +515,71 @@ int ha_example::reset(void) } +/* + Used to delete all rows in a table. Both for cases of truncate and + for cases where the optimizer realizes that all rows will be + removed as a result of a SQL statement. + + Called from item_sum.cc by Item_func_group_concat::clear(), + Item_sum_count_distinct::clear(), and Item_func_group_concat::clear(). + Called from sql_delete.cc by mysql_delete(). + Called from sql_select.cc by JOIN::reinit(). + Called from sql_union.cc by st_select_lex_unit::exec(). +*/ int ha_example::delete_all_rows() { DBUG_ENTER("ha_example::delete_all_rows"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + First you should go read the section "locking functions for mysql" in + lock.cc to understand this. + This create a lock on the table. If you are implementing a storage engine + that can handle transacations look at ha_berkely.cc to see how you will + want to goo 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(). +*/ int ha_example::external_lock(THD *thd, int lock_type) { DBUG_ENTER("ha_example::external_lock"); DBUG_RETURN(0); } + +/* + 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 (see thr_lock.c) + mysqld calls store lock with the requested locks. Store lock can now + modify a write lock to a read lock (or some other lock), 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). + + Berkeley DB for example changes all WRITE locks to TL_WRITE_ALLOW_WRITE + (which signals that we are doing WRITES, but we are still allowing other + reader's and writer's. + + When releasing locks, store_lock() are also called. In this case one + usually doesn't have to do anything. + + In some exceptional cases MySQL may send a request for a TL_IGNORE; + This means that we are requesting the same lock as last time and this + should also be ignored. (This may happen when someone does a flush + table when we have opened a part of the tables, in which case mysqld + closes and reopens the tables and tries to get the same locks at last + time). In the future we will probably try to remove this. + + Called from lock.cc by get_lock_data(). +*/ THR_LOCK_DATA **ha_example::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) @@ -267,6 +590,16 @@ THR_LOCK_DATA **ha_example::store_lock(THD *thd, return to; } +/* + 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 just be the name of + the table. You will need to remove any files you have created at this point. + + Called from handler.cc by delete_table and ha_create_table(). Only used + during create if the table_flag HA_DROP_BEFORE_CREATE was specified for + the storage engine. +*/ int ha_example::delete_table(const char *name) { DBUG_ENTER("ha_example::delete_table"); @@ -274,12 +607,24 @@ int ha_example::delete_table(const char *name) DBUG_RETURN(0); } +/* + Renames a table from one name to another from alter table call. + + Called from sql_table.cc by mysql_rename_table(). +*/ int ha_example::rename_table(const char * from, const char * to) { DBUG_ENTER("ha_example::rename_table "); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } +/* + 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. + + Called from opt_range.cc by check_quick_keys(). +*/ ha_rows ha_example::records_in_range(int inx, const byte *start_key,uint start_key_len, enum ha_rkey_function start_search_flag, @@ -287,11 +632,22 @@ ha_rows ha_example::records_in_range(int inx, enum ha_rkey_function end_search_flag) { DBUG_ENTER("ha_example::records_in_range "); - DBUG_RETURN(records); // HA_ERR_NOT_IMPLEMENTED + DBUG_RETURN(records); } -int ha_example::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info) +/* + create() is called to create a database. The variable name will have the name + of the table. When create() is called you do not need to worry about opening + the table. Also, the FRM file will have already been created so adjusting + create_info will not do you any good. You can overwrite the frm file at this + point if you wish to change the table definition, but there are no methods + currently provided for doing that. + + Called from handle.cc by ha_create_table(). +*/ +int ha_example::create(const char *name, TABLE *table_arg, + HA_CREATE_INFO *create_info) { DBUG_ENTER("ha_example::create"); /* This is not implemented but we want someone to be able that it works. */ diff --git a/sql/examples/ha_example.h b/sql/examples/ha_example.h index 466632a1795..ffc4f5b941c 100644 --- a/sql/examples/ha_example.h +++ b/sql/examples/ha_example.h @@ -14,6 +14,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + Please read ha_exmple.cc before reading this file. + Please keep in mind that the example storage engine implements all methods + that are required to be implemented. handler.h has a full list of methods + that you can implement. +*/ + +/* + EXAMPLE_SHARE is a structure that will be shared amoung all open handlers + The example implements the minimum of what you will probably need. +*/ typedef struct st_example_share { char *table_name; uint table_name_length,use_count; @@ -21,6 +32,9 @@ typedef struct st_example_share { THR_LOCK lock; } EXAMPLE_SHARE; +/* + Class definition for the storage engine +*/ class ha_example: public handler { THR_LOCK_DATA lock; /* MySQL lock */ @@ -33,17 +47,34 @@ public: ~ha_example() { } - const char *table_type() const { return "EXAMPLE"; } + /* The name that will be used for display purposes */ + const char *table_type() const { return "EXAMPLE"; } + /* The name of the index type that will be used for display */ const char *index_type(uint inx) { return "NONE"; } const char **bas_ext() const; + /* + This is a list of flags that says what the storage engine + implements. The current table flags are documented in + table_flags. + */ ulong table_flags() const { return 0; } + /* + This is a list of flags that says how the storage engine + implements indexes. The current index flags are documented in + handler.h. If you do not implement indexes, just return zero + here. + */ ulong index_flags(uint inx) const { return 0; } + /* + unireg.cc will call the following to make sure that the storage engine can + handle the data it is about to send. + */ uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return 0; } uint max_key_parts() const { return 0; } @@ -52,10 +83,15 @@ public: Called in test_quick_select to determine if indexes should be used. */ virtual double scan_time() { return (double) (records+deleted) / 20.0+10; } - /* The next method will never be called */ + /* + The next method will never be called if you do not implement indexes. + */ virtual double read_time(ha_rows rows) { return (double) rows / 20.0+1; } virtual bool fast_key_read() { return 1;} + /* + Everything below are methods that we implment in ha_example.cc. + */ int open(const char *name, int mode, uint test_if_locked); int close(void); int write_row(byte * buf); |