📜 ⬆️ ⬇️

MySQL engine in 5 minutes

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:


Environment preparation
The 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; //   node* next; //     node(){ data=NULL; next=NULL; } ~node(){ free(data); } }; node *first, *cur, *prev, *next; int row_count, cur_pos; void append_node(node* n){ if (first==NULL){ first=n; }else{ node* iter=first; while (iter->next!=NULL){iter=iter->next;} iter->next=n; } } 

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.
Installation
MySQL 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.

 #   mysql-5.5.35: cmake . cd storage/smalldb make cp ha_smalldb.so /opt/mysql/server-5.5.35/lib/plugin/ #  MySQL: INSTALL PLUGIN smalldb SONAME "ha_smalldb.so"; CREATE DATABASE small; USE small; CREATE TABLE tbl (a VARCHAR(255), b VARCHAR(255)) ENGINE=smalldb; INSERT INTO tbl values ("Hello","Habr"); SELECT * FROM tbl; 



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 .

Source: https://habr.com/ru/post/210068/


All Articles