Поскольку шаблон example описывает только интерфейс и не выполняет никаких операций, то в этом примере мы добавим в него реализацию CRUD-операций на основе одно-связного списка. Для этого необходимо написать всего 4 функции:
- int rnd_init(bool scan);
- int rnd_next(uchar *buf);
- int write_row(uchar *buf);
- int delete_row(const uchar *buf);
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
Начнём с того, что добавим заготовку для односвязного списка и несколько указателей — на голову, текущий, предыдущий и следующий элемент списка (в 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; } }
Теперь перейдём к операции записи – write_row(uchar *buf). В качестве аргумента ей передаётся buf, который содержит в себе содержимое добавляемой строки во внутреннем представлении:
Поскольку мы будем сохранять данные в память без предварительной обработки, то можно просто скопировать содержимое строки целиком. Длину строки с метаданными можно получить из дескриптора таблицы 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); }
При чтении данных из таблицы используется последовательное сканирование с помощью связки — rnd_init и rnd_next. Оно используется если движок не поддерживает индексов или первичных ключей. Для начала в rnd_init() необходимо подготовить указатели.
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); }
Булевый аргумент scan сообщает о намерении БД произвольного (непоследовательного) чтения таблицы. Мы можем его спокойно проигнорировать, т.к. при попытке вызвать нереализованную команду rnd_pos(), БД получит в ответ HA_ERR_WRONG_COMMAND и догадается, что придётся читать попорядку.
После одиночного rnd_init() БД будет последовательно вызывать rnd_next(), пока не дойдет до конца таблицы — HA_ERR_END_OF_FILE. После каждого вызова память по адресу buf должна быть заполнена содержимым строки во внутреннем представлении.
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); }
И наконец завершим написанием delete_row() путём удаления текущего элемента из списка:
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); }
Вот и всё, теперь можно испытать мини-движок в деле.
Если сама MySQL не установлена или установлена другая версия, то необходимо дополнительно установить БД с помощью 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;
Помимо INSERT и SELECT можно выполнять даже более сложные SQL-запросы, использующие операторы LIKE, INNER JOIN и пр., ведь оптимизация SQL – запросов реализована на уровне ядра БД до передачи управления движку, но её эффективность конечно же будет зависеть от поддерживаемого движком функционала.
Надеюсь этот материал позволит немного познакомиться с интерфейсом подключаемых модулей MySQL. Полностью код проекта можно скачать на github.
Для тех, кто хочет более серъезно изучить внутренности движков MySQL, но не рискует пока подступиться к InnoDB, советую обратить внимание на CSV или HEAP (Новое название — MEMORY). CSV добавляет возможность сохранения данных на диск в одноименном формате. А HEAP как следует из названия организует данные в кучу и реализует поддержку хэш-индексов. В обоих случаях приходится решать вспомогательную проблему высвобождения свободных участков памяти после удаления строк, которую нам удалось избежать за счёт использования односвязного списка.
P.S. И напоследок хочу порекомендовать утилиту ctags для тех, кто с ней не знаком. С её помощью довольно удобно изучать код, разбросанный по разным файлам, например она позволяет прыгнуть к определению функции или переменной по CTRL+]. Есть поддержка vim, emacs и других популярных редакторов. Здесь можно найти краткое руководство.
ссылка на оригинал статьи http://habrahabr.ru/post/210068/
Добавить комментарий