Starting with version 5.1, MySQL supports dynamic plugin support. A distribution contains an approximate skeleton of the code called -
example . It describes the interface and structure of the base handler - the
handler , a copy of which is created separately for each connection to the database. It also passes a pointer to the
TABLE * table table descriptor and the
TABLE_SHARE * share auxiliary vector used to synchronize with other handlers. The development of a plug-in can be carried out according to a modular principle, realizing only the necessary functions in the first place and closing more complex operations with plugs.
Since the
example template describes only the interface and does not perform any operations, in this example we will add to it the implementation of CRUD operations based on a single-linked list.
For this you need to write a total of 4 functions:
- int rnd_init (bool scan);
- int rnd_next (uchar * buf);
- int write_row (uchar * buf);
- int delete_row (const uchar * buf);
Environment preparationThe
example template is located in two files
ha_example.h and
ha_example.cc in the
storage / example directory from the MySQL source code. In this example,
MySQL Community 5.5.35 will be used. First, copy the
example and rename it to
smalldb .
wget http://downloads.mysql.com/archives/get/file/mysql-5.5.35.tar.gz tar -zxvf mysql-5.5.35.tar.gz cd mysql-5.5.35 cp -rf storage/example storage/smalldb cd storage/smalldb sed -e 's/EXAMPLE/SMALLDB/g' -e 's/example/smalldb/g' ha_example.h > ha_smalldb.h sed -e 's/EXAMPLE/SMALLDB/g' -e 's/example/smalldb/g' ha_example.cc > ha_smalldb.cc sed -i 's/EXAMPLE/SMALLDB/g;s/example/smalldb/g' CMakeLists.txt
Let's start with the fact that we add a blank for a single-linked list and a few pointers to the head, the current, the previous and the next list item (in
ha_smalldb.h ):
class node{ public: uchar* data;
We now turn to the write operation -
write_row (uchar * buf) . As an argument, it is passed
buf , which contains the contents of the added string in the internal representation:
')

Since we will save the data to memory without preliminary processing, you can simply copy the contents of the entire line. The length of the row with metadata can be obtained from the table descriptor
table-> s-> reclength .
int ha_smalldb::write_row(uchar *buf) { DBUG_ENTER("ha_smalldb::write_row"); node* n=new node(); n->data = (uchar*) malloc(sizeof(uchar)*table->s->reclength); memcpy(n->data,buf,table->s->reclength); append_node(n); row_count++; DBUG_RETURN(0); }
When reading data from a table, sequential scanning with the help of a bundle -
rnd_init and
rnd_next is used . It is used if the engine does not support indexes or primary keys. First, in
rnd_init () you need to prepare pointers.
int ha_smalldb::rnd_init(bool scan) { DBUG_ENTER("ha_smalldb::rnd_init"); cur=NULL; prev=NULL; next=first; cur_pos=0; DBUG_RETURN(0); }
The boolean argument
scan reports the intent of the database for an arbitrary (non-sequential) reading of the table. We can safely ignore it, because If you try to call the unimplemented
rnd_pos () command, the database will receive HA_ERR_WRONG_COMMAND in response and will guess that you will have to read it in a row.
After a single
rnd_init (), the database will consistently call
rnd_next () until it reaches the end of the table - HA_ERR_END_OF_FILE. After each call, the memory at
buf should be filled with the contents of the line in the internal representation.
int ha_smalldb::rnd_next(uchar *buf) { int rc; DBUG_ENTER("ha_smalldb::rnd_next"); MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str, TRUE); if (next!=NULL){ prev=cur; cur=next; next=cur->next; memcpy(buf,cur->data,table->s->reclength); cur_pos++; rc=0; }else{ rc= HA_ERR_END_OF_FILE; } MYSQL_READ_ROW_DONE(rc); DBUG_RETURN(rc); }
Finally, we finish writing
delete_row () by deleting the current item from the list:
int ha_smalldb::delete_row(const uchar *buf) { DBUG_ENTER("ha_smalldb::delete_row"); if (cur!=first){ free(cur); prev->next=next; }else{ free(cur); cur=NULL; first=next; } row_count--; DBUG_RETURN(0); }
That's all, now you can try the mini-engine in action.
InstallationMySQL 5.5 uses cmake to build code. To install, just compile the .so file and copy it to the directory with the plugins of the installed version of MySQL. You can find it out using the command - SHOW VARIABLES LIKE "% plugin_dir%";
If MySQL itself is not installed or
another version is installed , then you must additionally install the database using make install.

In addition to INSERT and SELECT, you can perform even more complex SQL queries using
LIKE ,
INNER JOIN and other operators, because SQL query optimization is implemented at the database level before transferring control to the engine, but its efficiency will of course depend on the functionality supported by the engine.
I hope this material will allow a little to get acquainted with the interface of MySQL plugins. The complete project code can be downloaded on
github .
For those who want to more seriously study the insides of the MySQL engines, but do not risk getting closer to InnoDB, I advise you to pay attention to CSV or HEAP (New name - MEMORY). CSV adds the ability to save data to disk in the same format. And HEAP, as the name implies, organizes data into a heap and implements support for hash indexes. In both cases, we have to solve the auxiliary problem of freeing up free memory areas after deleting rows, which we managed to avoid by using a single-linked list.
More detailed guidance on writing your own engines can be found in chapter 22 of the official
MySQL Internals Manual documentation
. Chapter 22. Writing a Custom Storage Engine .
PS And finally, I would like to recommend the
ctags utility for those who are not familiar with it. Using it is quite convenient to study the code scattered across different files, for example, it allows you to jump to the definition of a function or variable by CTRL +]. There is support for vim, emacs and other popular editors. Find a
quick guide here .