您的位置:首页 > 其它

16. 编写自定义存储引擎

2011-09-05 14:08 190 查看

16.1.前言

对于MySQL5.1,MySQLAB公司引入了插件式存储引擎体系结构,这样,就能创建新的存储引擎,并将它们添加到正在运行的MySQL服务器上,而不必重新编译服务器本身。该体系结构简化了新存储引擎的开发和部署。本章的意图是作为指南,用于帮助你为新的插件式存储引擎体系结构开发存储引擎。关于MySQL插件式存储引擎体系结构的更多信息,请参见第14章:插件式存储引擎体系结构。

16.2.概述

MySQL服务器采用了模块化风格。图16.1:MySQL体系结构存储引擎负责管理数据存储,以及MySQL的索引管理。通过定义的API,MySQL服务器能够与存储引擎进行通信。每个存储引擎均是1个继承类,每个类实例作为处理程序而被引用。针对需要与特殊表一起工作的每个线程,处理程序是在1个处理程序的基础上实例化的。例如,如果3个连接全都在相同的表上工作,需要创建3个处理程序实例。一旦创建了处理程序实例,MySQL服务器将向处理程序发送命令,以便执行数据存储和检索任务,如打开表、操纵行和管理索引等。能够以累进方式创建定制存储引擎:开发人员能够以只读存储引擎启动,随后添加对INSERT、UPDATE和DELETE操作的支持,甚至能够增加对索引功能、事务和其他高级操作的支持。

16.3.创建存储引擎源文件

实施新存储引擎的最简单方法是,通过拷贝和更改EXAMPLE存储引擎开始。在MySQL5.1源码树的sql/examples/目录下可找到文件ha_example.cc和ha_example.h。关于如何获得5.1源码树的说明,请参见2.8.3节,“从开发源码树安装”。复制文件时,将名称从ha_example.cc和ha_example.h更改为与存储引擎相适应的名称,如ha_foo.cc和ha_foo.h。拷贝并重命名了这些文件后,必须更换所有的EXAMPLE示例,以及具有存储引擎名称的示例。如果你熟悉sed,也能自动完成这些步骤:
seds/EXAMPLE/FOO/gha_example.h|seds/example/foo/gha_foo.h
seds/EXAMPLE/FOO/gha_example.cc|seds/example/foo/gha_foo.cc

16.4.创建handlerton

handlerton(“单个处理程序”的简称)定义了存储引擎,并包含指向函数的函数指针,它以整体方式作用在引擎上,而函数工作在单独的处理程序实例中。在这类函数的一些示例中,包含用于处理注释和回滚的事务函数。下面给出了一个来自EXAMPLE存储引擎的示例:
handlertonexample_hton={
"EXAMPLE",
SHOW_OPTION_YES,
"Examplestorageengine",
DB_TYPE_EXAMPLE_DB,
NULL,/*Initialize*/
0,/*slot*/
0,/*savepointsize.*/
NULL,/*close_connection*/
NULL,/*savepoint*/
NULL,/*rollbacktosavepoint*/
NULL,/*releasesavepoint*/
NULL,/*commit*/
NULL,/*rollback*/
NULL,/*prepare*/
NULL,/*recover*/
NULL,/*commit_by_xid*/
NULL,/*rollback_by_xid*/
NULL,/*create_cursor_read_view*/
NULL,/*set_cursor_read_view*/
NULL,/*close_cursor_read_view*/
example_create_handler,/*Createanewhandler*/
NULL,/*Dropadatabase*/
NULL,/*Paniccall*/
NULL,/*Releasetemporarylatches*/
NULL,/*UpdateStatistics*/
NULL,/*StartConsistentSnapshot*/
NULL,/*Flushlogs*/
NULL,/*Showstatus*/
NULL,/*ReplicationReportSentBinlog*/
HTON_CAN_RECREATE
};
下面给出了来自handler.h的handlerton定义:
typedefstruct
{
constchar*name;
SHOW_COMP_OPTIONstate;
constchar*comment;
enumdb_typedb_type;
bool(*init)();
uintslot;
uintsavepoint_offset;
int(*close_connection)(THD*thd);
int(*savepoint_set)(THD*thd,void*sv);
int(*savepoint_rollback)(THD*thd,void*sv);
int(*savepoint_release)(THD*thd,void*sv);
int(*commit)(THD*thd,boolall);
int(*rollback)(THD*thd,boolall);
int(*prepare)(THD*thd,boolall);
int(*recover)(XID*xid_list,uintlen);
int(*commit_by_xid)(XID*xid);
int(*rollback_by_xid)(XID*xid);
void*(*create_cursor_read_view)();
void(*set_cursor_read_view)(void*);
void(*close_cursor_read_view)(void*);
handler*(*create)(TABLE*table);
void(*drop_database)(char*path);
int(*panic)(enumha_panic_functionflag);
int(*release_temporary_latches)(THD*thd);
int(*update_statistics)();
int(*start_consistent_snapshot)(THD*thd);
bool(*flush_logs)();
bool(*show_status)(THD*thd,stat_print_fn*print,enumha_stat_typestat);
int(*repl_report_sent_binlog)(THD*thd,char*log_file_name,my_off_tend_offset);
uint32flags;
}handlerton;
共有30个handlerton元素,但只有少量元素是强制性的(明确地讲是前4个元素和第21个元素)。1.存储引擎的名称。这是创建表时将使用的名称(CREATETABLE...ENGINE=FOO;)。2.确定使用SHOWSTORAGEENGINES命令时是否列出存储引擎。3.存储引擎注释,对使用SHOWSTORAGEENGINES命令时显示的存储引擎的描述。4.在MySQL服务器内唯一识别存储引擎的整数。内置存储引擎使用的常数定义在handler.h文件中。作为创建常数的可选方法,可使用大于25的整数。5.指向存储引擎初始化程序的指针。仅当启动服务器时才调用该函数,以便在实例化处理程序之前,存储引擎类能执行必要的内务操作。6.插槽。保存每连接的信息时,每个存储引擎在thd中有自己的内存区域(实际上为指针)。它是作为thd->ha_data[foo_hton.slot]访问的。插槽编号在调用foo_init()后由MySQL初始化。7.保存点偏移。为了保存每个savepoint数据,为存储引擎提供了请求的大小(典型情况下为0)。必须以静态方式初始化savepoint偏移,使其具有所有的内存大小,以便保存每个savepoint的信息。在foo_init之后,它被更改为savepoint存储区域的偏移,存储引擎不需要使用它。8.由事务性存储引擎使用,清理其存储段内分配的内存,和/或回滚任何未完成的事务。9.由事务性存储引擎选择性使用,创建savepoint(保存点),并将其保存到提供的内存中。10.指向处理程序rollback_to_savepoint()函数的函数指针。它用于在事务期间返回savepoint。仅对支持保存点的存储引擎才会填充它。11.指向处理程序release_savepoint()函数的函数指针。它用于在事务期间释放保存点的资源。仅对支持保存点的存储引擎才会填充它。12.指向处理程序commit()函数的函数指针。它用于提交事务。仅对支持事务的存储引擎才会填充它。13.指向处理程序rollback()函数的函数指针。它用于回滚交易。仅对支持事务的存储引擎才会填充它。14.XA事务性存储引擎所需。为提交操作准备事务。将XID与事务关联起来。15.XA事务性存储引擎所需。恢复由XID标识的事务。16.XA事务性存储引擎所需。提交由XID标识的事务。17.XA事务性存储引擎所需。回滚由XID标识的事务。18.与服务器端光标一起使用,尚未实施。19.与服务器端光标一起使用,尚未实施。20.与服务器端光标一起使用,尚未实施。21.MANDATORY:构造并返回处理程序实例。22.撤销方案时,如果存储引擎需要执行特殊步骤时使用(如在使用表空间的存储引擎中使用)。23.清理在服务器关闭和崩溃时调用的函数。24.InnoDB特殊函数。25.在启动SHOWSTATUS时调用InnoDB特殊函数。26.调用InnoDB特殊函数以开始连续读取。27.调用它,指明应将日志刷新为可靠的存储。28.在存储引擎上提供可被人员读取的状态信息。29.InnoDB特殊函数用于复制。30.Handlerton标志,通常与ALTERTABLE相关。可能的值定义于sql/handler.h文件中,并在此列出;
31.#defineHTON_NO_FLAGS0
32.#defineHTON_CLOSE_CURSORS_AT_COMMIT(1<<0)
33.#defineHTON_ALTER_NOT_SUPPORTED(1<<1)
34.#defineHTON_CAN_RECREATE(1<<2)
35.#defineHTON_FLUSH_AFTER_RENAME(1<<3)
36.#defineHTON_NOT_USER_SELECTABLE(1<<4)
HTON_ALTER_NOT_SUPPORTED由FEDERATED存储引擎使用,用以指明存储引擎不接受AFTERTABLE语句。HTON_FLUSH_AFTER_RENAME指明,重命名表后,必须调用FLUSHLOGS。HTON_NOT_USER_SELECTABLE指明存储引擎不能由用户选择,而是用作系统存储引擎,如用于二进制日志的伪存储引擎。

16.5.对处理程序进行实例化处理

调用存储引擎的第1个方法是调用新的处理程序实例。在存储引擎源文件中定义handlerton之前,必须定义用于函数实例化的函数题头。下面给出了1个来自CSV引擎的示例:
statichandler*tina_create_handler(TABLE*table);
正如你所见到的那样,函数接受指向处理程序准备管理的表的指针,并返回处理程序对象。定义了函数题头后,用第21个handlerton元素中的函数指针命名函数,指明函数负责生成新的处理程序实例。下面给出了MyISAM存储引擎的实例化函数示例:
statichandler*myisam_create_handler(TABLE*table)
{
returnnewha_myisam(table);
}
该调用随后与存储引擎的构造程序一起工作。下面给出了来自FEDERATED存储引擎的1个示例:
ha_federated::ha_federated(TABLE*table_arg)
:handler(&federated_hton,table_arg),
mysql(0),stored_result(0),scan_flag(0),
ref_length(sizeof(MYSQL_ROW_OFFSET)),current_position(0)
{}
下面给出了来自EXAMPLE存储引擎的另一个示例:
ha_example::ha_example(TABLE*table_arg)
:handler(&example_hton,table_arg)
{}
FEDERATED示例中的附加元素是处理程序的额外初始化要素。所要求的最低实施是EXAMPLE示例中显示的handler()初始化。

16.6.定义表扩展

就给定的表、数据和索引,要求存储引擎为MySQL服务器提供存储引擎所使用的扩展列表。扩展应采用以Null终结的字符串数组形式。下面给出了CSV引擎使用的数组:
staticconstchar*ha_tina_exts[]={
".CSV",
NullS
};
调用bas_ext()函数时返回该数组。
constchar**ha_tina::bas_ext()const
{
returnha_tina_exts;
}
通过提供扩展信息,你还能忽略DROPTABLE功能的实施,这是因为,通过关闭表并用你指定的扩展删除所有文件,MySQL服务器能实现该功能。

16.7.创建表

一旦实例化了处理程序,所需的第1个操作很可能是创建表。你的存储引擎必须实现create()虚拟函数:
virtualintcreate(constchar*name,TABLE*form,HA_CREATE_INFO*info)=0;
该函数应创建所有必须的文件,然后关闭表。MySQL服务器将调用随后需打开的表。*name参数是表的名称。*form参数是st_table结构,该结构定义了表并与MySQL服务器已创建的tablename.frm文件的内容匹配。在大多数情况下,存储引擎不需要更改tablename.frm文件,也没有支持该操作的预置功能。*info参数是包含CREATETABLE语句用于创建表所需信息的结构。该结构定义于handler.h文件中,并为了便于参考列于下面:
typedefstructst_ha_create_information
{
CHARSET_INFO*table_charset,*default_table_charset;
LEX_STRINGconnect_string;
constchar*comment,*password;
constchar*data_file_name,*index_file_name;
constchar*alias;
ulonglongmax_rows,min_rows;
ulonglongauto_increment_value;
ulongtable_options;
ulongavg_row_length;
ulongraid_chunksize;
ulongused_fields;
SQL_LISTmerge_list;
enumdb_typedb_type;
enumrow_typerow_type;
uintnull_bits;/*NULLbitsatstartofrecord*/
uintoptions;/*ORofHA_CREATE_options*/
uintraid_type,raid_chunks;
uintmerge_insert_method;
uintextra_size;/*lengthofextradatasegment*/
booltable_existed;/*1increateiftableexisted*/
boolfrm_only;/*1ifnoha_create_table()*/
boolvarchar;/*1iftablehasaVARCHAR*/
}HA_CREATE_INFO;
基本的存储引擎能忽略*form和*info的内容,这是因为,真正所需的是创建存储引擎所使用的数据文件,以及对数据文件的可能初始化操作(假定存储文件是基于文件的)。下面给出了来自CSV存储引擎的实施示例:
intha_tina::create(constchar*name,TABLE*table_arg,
HA_CREATE_INFO*create_info)
{
charname_buff[FN_REFLEN];
Filecreate_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);
}
在前面的例子中,CSV引擎未引用*table_arg或*create_info参数,而是简单地创建了所需的数据文件,关闭它们,并返回。my_create和my_close函数是定义于src/include/my_sys.h文件中的助手函数。

16.8.打开表

在表上执行任何读或写操作之前,MySQL服务器将调用open()方法打开表数据和索引文件(如果存在的话)。
intopen(constchar*name,intmode,inttest_if_locked);
第1个参数是要打开的表的名称。第2个参数确定了要打开的文件或准备执行的操作。它们的值定义于handler.h中,并为了方便起见列在下面:
#defineHA_OPEN_KEYFILE1
#defineHA_OPEN_RNDFILE2
#defineHA_GET_INDEX4
#defineHA_GET_INFO8/*doaha_info()afteropen*/
#defineHA_READ_ONLY16/*Fileopenedasreadonly*/
#defineHA_TRY_READ_ONLY32/*Tryreadonlyifcan'topenwithreadandwrite*/
#defineHA_WAIT_IF_LOCKED64/*Waitiflockedonopen*/
#defineHA_ABORT_IF_LOCKED128/*skipiflockedonopen.*/
#defineHA_BLOCK_LOCK256/*unlockwhenreadingsomerecords*/
#defineHA_OPEN_TEMPORARY512
最后一个选项规定了是否要在打开表之前检查表上的锁定。在典型情况下,存储引擎需要实施某种形式的共享访问控制,以防止在多线程环境下的文件损坏。关于如何实施文件锁定的示例,请参见sql/examples/ha_tina.cc的get_share()和free_share()方法。

16.9.实施基本的表扫描功能

16.9.1.实施store_lock()函数
16.9.2.实施external_lock()函数16.9.3.实施rnd_init()函数
16.9.4.实施info()函数
16.9.5.实施extra()函数
16.9.6.实施rnd_next()函数
最基本的存储引擎能实现只读表扫描功能。这类引擎可用于支持SQL日志查询、以及在MySQL之外填充的其他数据文件。本节介绍的方法实施提供了创建更高级存储引擎的基础。下面给出了在CSV引擎的9行表扫描过程中进行的方法调用:
ha_tina::store_lockha_tina::external_lockha_tina::infoha_tina::rnd_initha_tina::extra-ENUMHA_EXTRA_CACHECacherecordinHA_rrnd()ha_tina::rnd_nextha_tina::rnd_nextha_tina::rnd_nextha_tina::rnd_nextha_tina::rnd_nextha_tina::rnd_nextha_tina::rnd_nextha_tina::rnd_nextha_tina::rnd_nextha_tina::extra-ENUMHA_EXTRA_NO_CACHEEndcacheingofrecords(def)ha_tina::external_lockha_tina::extra-ENUMHA_EXTRA_RESETResetdatabasetoafteropen

16.9.1.实施store_lock()函数

在执行任何读取或写操作之前,调用store_lock()函数。将锁定添加到表锁定处理程序之前(请参见thr_lock.c),mysqld将用请求的锁调用存储锁定。目前,存储锁定能将写锁定更改为读锁定(或其他锁定),忽略锁定(如果不打算使用MySQL锁定的话),或为很多表添加锁定(就像使用MERGE处理程序时作的那样)。例如,BerkeleyDB能将所有的WRITE锁定更改为TL_WRITE_ALLOW_WRITE(表示我们正在执行WRITES,但我们仍允许其他人员进行操作)。释放锁定时,也将调用store_lock(),在这种情况下,通常不需做任何事。在某些特殊情况下,MySQL可能会发送对TL_IGNORE的请求。这意味着我们正在请求与上次相同的锁定,这也应被忽略(当我们打开了表的某一部分时,如果其他人执行了表刷新操作,就会出现该情况,此时,mysqld将关闭并再次打开表,然后获取与上次相同的锁定)。我们打算在将来删除该特性。可能的锁定类型定义于includes/thr_lock.h中,并列在下面:
enumthr_lock_type
{
TL_IGNORE=-1,
TL_UNLOCK,/*UNLOCKANYLOCK*/
TL_READ,/*Readlock*/
TL_READ_WITH_SHARED_LOCKS,
TL_READ_HIGH_PRIORITY,/*Highprior.thanTL_WRITE.Allowconcurrentinsert*/
TL_READ_NO_INSERT,/*READ,Don'tallowconcurrentinsert*/
TL_WRITE_ALLOW_WRITE,/*Writelock,butallowotherthreadstoread/write.*/
TL_WRITE_ALLOW_READ,/*Writelock,butallowotherthreadstoread/write.*/
TL_WRITE_CONCURRENT_INSERT,/*WRITElockusedbyconcurrentinsert.*/
TL_WRITE_DELAYED,/*WriteusedbyINSERTDELAYED.AllowsREADlocks*/
TL_WRITE_LOW_PRIORITY,/*WRITElockthathaslowerprioritythanTL_READ*/
TL_WRITE,/*NormalWRITElock*/
TL_WRITE_ONLY/*Abortnewlockrequestwithanerror*/
};
实际的锁定处理因锁定实施的不同而不同,你可以选择某些请求的锁定类型或不选择任何锁定类型,并根据情况恰当地代入你自己的方法。下面给出了1个CSV存储引擎实施示例:
THR_LOCK_DATA**ha_tina::store_lock(THD*thd,
THR_LOCK_DATA**to,
enumthr_lock_typelock_type)
{
if(lock_type!=TL_IGNORE&&lock.type==TL_UNLOCK)
lock.type=lock_type;
*to++=&lock;
returnto;
}

16.9.2.实施external_lock()函数

external_lock()函数是在事务开始时调用的,或发出LOCKTABLES语句时调用的,用于事务性存储引擎。在sql/ha_innodb.cc和sql/ha_berkeley.cc文件中,可找到使用external_lock()的示例,但大多数存储引擎简单地返回0,就像EXAMPLE存储引擎那样:
intha_example::external_lock(THD*thd,intlock_type)
{
DBUG_ENTER("ha_example::external_lock");
DBUG_RETURN(0);
}

16.9.3.实施rnd_init()函数

在任何表扫描之前调用的函数是rnd_init()函数。函数rnd_init()用于为表扫描作准备,将计数器和指针复位为表的开始状态。下述示例来自CSV存储引擎:
intha_tina::rnd_init(boolscan){DBUG_ENTER("ha_tina::rnd_init");current_position=next_position=0;records=0;chain_ptr=chain;DBUG_RETURN(0);}

16.9.4.实施info()函数

执行表扫描操作之前,将调用info()函数,以便为优化程序提供额外信息。优化程序所需的信息不是通过返回值给定的,你需填充存储引擎类的特定属性,当info()调用返回后,优化程序将读取存储引擎类。除了供优化程序使用外,在调用info()函数期间,很多值集合还将用于SHOWTABLESTATUS语句。在sql/handler.h中列出了完整的公共属性,下面给出了一些常见的属性:
ulonglongdata_file_length;/*Lengthoffdatafile*/
ulonglongmax_data_file_length;/*Lengthoffdatafile*/
ulonglongindex_file_length;
ulonglongmax_index_file_length;
ulonglongdelete_length;/*Freebytes*/
ulonglongauto_increment_value;
ha_rowsrecords;/*Recordsintable*/
ha_rowsdeleted;/*Deletedrecords*/
ulongraid_chunksize;
ulongmean_rec_length;/*physicalreclength*/
time_tcreate_time;/*Whentablewascreated*/
time_tcheck_time;
time_tupdate_time;
对于表扫描,最重要的属性是“records”,它指明了表中的记录数。当存储引擎指明表中有0或1行时,或有2行以上时,在这两种情况下,优化程序的执行方式不同。因此,当你在执行表扫描之前不清楚表中有多少行时,应返回大于等于2的值,这很重要(例如,数据是在外部填充的)。

16.9.5.实施extra()函数

执行某些操作之前,应调用extra()函数,以便为存储引擎就如何执行特定操作予以提示。额外调用中的提示实施不是强制性的,大多数存储引擎均返回0:
intha_tina::extra(enumha_extra_functionoperation){DBUG_ENTER("ha_tina::extra");DBUG_RETURN(0);}

16.9.6.实施rnd_next()函数

完成表的初始化操作后,MySQL服务器将调用处理程序的rnd_next()函数,每两个扫描行调用1次,直至满足了服务器的搜索条件或到达文件结尾为止,在后一种情况下,处理程序将返回HA_ERR_END_OF_FILE。rnd_next()函数有一个名为*buf的单字节数组参数。对于*buf参数,必须按内部MySQL格式用表行的内容填充它。服务器采用了三种数据格式:固定长度行,可变长度行,以及具有BLOB指针的可变长度行。对于每种格式,各列将按照它们由CREATETABLE语句定义的顺序显示(表定义保存在.frm文件中,优化程序和处理程序均能从相同的源,即TABLE结构,访问表的元数据)。每种格式以每列1比特的"NULLbitmap"开始。对于含6个列的表,其bitmap为1字节,对于含9~16列的表,其bitmap为2字节,依此类推。要想指明特定的值是NULL,应将该列NULL位设置为1。当NULLbitmap逐个进入列后,每列将具有MySQL手册的“MySQL数据类型”一节中指定的大小。在服务器中,列的数据类型定义在sql/field.cc文件中。对于固定长度行格式,列将简单地逐个放置。对于可变长度行,VARCHAR列将被编码为1字节长,后跟字符串。对于具有BLOB列的可变长度行,每个blob由两部分表示:首先是表示BLOB实际大小的整数,然后是指向内存中BLOB的指针。在任何表处理程序中从rnd_next()开始,可找到行转换(或“包装”)的示例。例如,在ha_tina.cc中,find_current_row()内的代码给出了使用TABLE结构(由表指向的)和字符串对象(命名缓冲)包装字符数据(来自CSV文件)的方法。将行写回磁盘需要反向转换,从内部格式解包。下述示例来自CSV存储引擎:
intha_tina::rnd_next(byte*buf)
{
DBUG_ENTER("ha_tina::rnd_next");
statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,&LOCK_status);
current_position=next_position;
if(!share->mapped_file)
DBUG_RETURN(HA_ERR_END_OF_FILE);
if(HA_ERR_END_OF_FILE==find_current_row(buf))
DBUG_RETURN(HA_ERR_END_OF_FILE);
records++;
DBUG_RETURN(0);
}
对于从内部行格式到CSV行格式的转换,它是在find_current_row()函数中执行的。
intha_tina::find_current_row(byte*buf)
{
byte*mapped_ptr=(byte*)share->mapped_file+current_position;
byte*end_ptr;
DBUG_ENTER("ha_tina::find_current_row");
/*EOFshouldbecountedasnewline*/
if((end_ptr=find_eoln(share->mapped_file,current_position,
share->file_stat.st_size))==0)
DBUG_RETURN(HA_ERR_END_OF_FILE);
for(Field**field=table->field;*field;field++)
{
buffer.length(0);
mapped_ptr++;//Incrementpastthefirstquote
for(;mapped_ptr!=end_ptr;mapped_ptr++)
{
//Needtoconvertlinefeeds!
if(*mapped_ptr=='"'&&
(((mapped_ptr[1]==',')&&(mapped_ptr[2]=='"'))||
(mapped_ptr==end_ptr-1)))
{
mapped_ptr+=2;//Movepastthe,andthe"
break;
}
if(*mapped_ptr=='\\'&&mapped_ptr!=(end_ptr-1))
{
mapped_ptr++;
if(*mapped_ptr=='r')
buffer.append('\r');
elseif(*mapped_ptr=='n')
buffer.append('\n');
elseif((*mapped_ptr=='\\')||(*mapped_ptr=='"'))
buffer.append(*mapped_ptr);
else/*Thiscouldonlyhappedwithanexternallycreatedfile*/
{
buffer.append('\\');
buffer.append(*mapped_ptr);
}
}
else
buffer.append(*mapped_ptr);
}
(*field)->store(buffer.ptr(),buffer.length(),system_charset_info);
}
next_position=(end_ptr-share->mapped_file)+1;
/*Maybeuse\Nfornull?*/
memset(buf,0,table->s->null_bytes);/*Wedonotimplementnulls!*/
DBUG_RETURN(0);
}

16.10.关闭表

当MySQL服务器完成表操作时,它将调用close()方法关闭文件指针并释放任何其他资源。对于使用共享访问方法的存储引擎(如CSV引擎和其他示例引擎中显示的方法),必须将它们自己从共享结构中删除:
intha_tina::close(void)
{
DBUG_ENTER("ha_tina::close");
DBUG_RETURN(free_share(share));
}
对于使用其自己共享管理系统的存储引擎,应使用任何所需的方法,在它们的处理程序中,从已打开表的共享区删除处理程序实例。

16.11.为存储引擎添加对INSERT的支持

一旦在你的存储引擎中有了读支持,下一个需要实施的特性是对INSERT语句的支持。有了INSERT支持,存储引擎就能处理WORM(写一次,读多次)应用程序,如用于以后分析的日志和归档应用等。所有的INSERT操作均是通过write_row()函数予以处理的:
intha_foo::write_row(byte*buf)
*buf参数包含将要插入的行,采用内部MySQL格式。基本的存储引擎将简单地前进到数据文件末尾,并直接在末尾处添加缓冲的内容,这样就能使行读取变得简单,这是因为,你可以读取行并将其直接传递到rnd_next()函数的缓冲参数中。写入行的进程与读取行的进程相反:从MySQL内部行格式获取数据,并将其写入数据文件。下述示例来自CSV存储引擎:
intha_tina::write_row(byte*buf)
{
intsize;
DBUG_ENTER("ha_tina::write_row");
statistic_increment(table->in_use->status_var.ha_write_count,&LOCK_status);
if(table->timestamp_field_type&TIMESTAMP_AUTO_SET_ON_INSERT)
table->timestamp_field->set_time();
size=encode_quote(buf);
if(my_write(share->data_file,buffer.ptr(),size,MYF(MY_WME|MY_NABP)))
DBUG_RETURN(-1);
if(get_mmap(share,0)>0)
DBUG_RETURN(-1);
DBUG_RETURN(0);
}
前述示例中的两条注释包括,更新关于写入操作的表统计,以及在写入行之前设置时间戳。

16.12.为存储引擎添加对UPDATE的支持

通过执行表扫描操作,在找到与UPDATE语句的WHERE子句匹配的行后,MySQL服务器将执行UPDATE语句,然后调用update_row()函数:
intha_foo::update_row(constbyte*old_data,byte*new_data)
*old_data参数包含更新前位于行中的数据,而*new_data参数包含行的新内容(采用MySQL内部行格式)。更新的执行取决于行格式和存储实施方式。某些存储引擎将替换恰当位置的数据,而其他实施方案则会删除已有的行,并在数据文件末尾添加新行。非事务性存储引擎通常会忽略*old_data参数的内容,仅处理*new_data缓冲。事务性存储引擎可能需要比较缓冲,以确定在上次回滚中出现了什么变化。如果正在更新的表中包含时间戳列,对时间戳的更新将由update_row()调用管理。下述示例来自CSV引擎:
intha_tina::update_row(constbyte*old_data,byte*new_data)
{
intsize;
DBUG_ENTER("ha_tina::update_row");
statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
&LOCK_status);
if(table->timestamp_field_type&TIMESTAMP_AUTO_SET_ON_UPDATE)
table->timestamp_field->set_time();
size=encode_quote(new_data);
if(chain_append())
DBUG_RETURN(-1);
if(my_write(share->data_file,buffer.ptr(),size,MYF(MY_WME|MY_NABP)))
DBUG_RETURN(-1);
DBUG_RETURN(0);
}
请注意上例中的时间戳设置。

16.13.为存储引擎添加对DELETE的支持

MySQL服务器采用了与INSERT语句相同的方法来执行DELETE语句:服务器使用rnd_next()函数跳到要删除的行,然后调用delete_row()函数删除行。
intha_foo::delete_row(constbyte*buf)
*buf参数包含要删除行的内容。对于大多数存储引擎,该参数可被忽略,但事务性存储引擎可能需要保存删除的数据,以供回滚操作使用。下述示例来自CSV存储引擎:
intha_tina::delete_row(constbyte*buf)
{
DBUG_ENTER("ha_tina::delete_row");
statistic_increment(table->in_use->status_var.ha_delete_count,
&LOCK_status);
if(chain_append())
DBUG_RETURN(-1);
--records;
DBUG_RETURN(0);
}
前述示例的步骤是更新delete_count统计,并记录计数。

16.14.API引用

16.14.1.bas_ext16.14.2.close16.14.3.create16.14.4.delete_row16.14.5.delete_table16.14.6.external_lock16.14.7.extra16.14.8.info16.14.9.open16.14.10.rnd_init16.14.11.rnd_next16.14.12.store_lock16.14.13.update_row16.14.14.write_row

16.14.1.bas_ext

目的

定义存储引擎所使用的文件扩展。

概要

virtualconstchar**bas_ext();
;

描述

这是bas_ext方法。调用它,可为MySQL服务器提供存储引擎所使用的文件扩展列表。该列表将返回以Null终结的字符串数组。通过提供扩展列表,在很多情况下,存储引擎能省略delete_table()函数,这是因为MySQL服务器将关闭所有对表的引用,并使用指定的扩展删除所有文件。

参数

该函数无参数。

返回值

返回值是存储引擎扩展的以Null终结的字符串数组。下面给出了CSV引擎的示例:
staticconstchar*ha_tina_exts[]={".CSV",NullS};

用法

staticconstchar*ha_tina_exts[]={".CSV",NullS};constchar**ha_tina::bas_ext()const{returnha_tina_exts;}

默认实施

staticconstchar*ha_example_exts[]={NullS};constchar**ha_example::bas_ext()const{returnha_example_exts;}

16.14.2.close

目的

关闭打开的表。

概要

virtualintclose(void);
void;

描述

这是close方法。关闭表。这是释放任何已分配资源的恰当时机。从sql_base.cc、sql_select.cc和table.cc调用它。在sql_select.cc中,它仅用于关闭临时表,或在将临时表转换为myisam表的过程中关闭表。关于sql_base.cc,请查看close_data_tables()。

参数

void

返回值

无返回值。

用法

取自CSV引擎的示例:
intha_example::close(void){DBUG_ENTER("ha_example::close");DBUG_RETURN(free_share(share));}

16.14.3.create

目的

创建新表。

概要

virtualintcreate(name,
form,
info);
constchar*name;
TABLE*form;
HA_CREATE_INFO*info;

描述

这是create方法。调用create()以创建表。变量名称为表的名称。调用create()时,不需要打开表。此外,由于已创建了.frm文件,不推荐调整create_info。由ha_create_table()从handle.cc中调用。

参数

nameforminfo

返回值

无返回值。

用法

CSV搜索引擎示例:
intha_tina::create(constchar*name,TABLE*table_arg,HA_CREATE_INFO*create_info){charname_buff[FN_REFLEN];Filecreate_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);}

16.14.4.delete_row

目的

删除行。

概要

virtualintdelete_row(buf);
constbyte*buf;

描述

这是delete_row方法。Buf包含删除行的副本。调用了当前行后,服务器将立刻调用它(通过前一个rnd_next()或索引调用)。如果存在指向上一行的指针,或能够访问主键,删除操作将更为容易。请记住,服务器不保证连续删除。可以使用ORDERBY子句。在sql_acl.cc和sql_udf.cc中调用,以管理内部的表信息。在sql_delete.cc、sql_insert.cc和sql_select.cc中调用。在sql_select中,它用于删除副本,而在插入操作中,它用于REPLACE调用。

参数

buf

返回值

无返回值。

用法

默认实施

{returnHA_ERR_WRONG_COMMAND;}

16.14.5.delete_table

目的

用来自bas_ext()的扩展删除所有文件。

概要

virtualintdelete_table(name);
constchar*name;

描述

这是delete_table方法。用于删除表。调用delete_table()时,所有已打开的对该表的引用均将被关闭(并释放全局共享的引用)。变量名称为表名。此时,需要删除任何已创建的文件。如果未实施它,将从handler.cc调用默认的delete_table(),并用bas_ext()返回的文件扩展删除所有文件。假定处理程序返回的扩展比文件实际使用的多。由delete_table和ha_create_table()从handler.cc调用。如果为存储引擎指定了table_flagHA_DROP_BEFORE_CREATE,仅在创建过程中使用。

参数

name:表的基本名称

返回值

·如果成功地从base_ext删除了至少1个文件而且未出现除ENOENT之外的错误,返回0。·#:Error

用法

大多数存储引擎均会忽略该函数的实施。

16.14.6.external_lock

目的

为事务处理表锁定。

概要

virtualintexternal_lock(thd,
lock_type);
THD*thd;
intlock_type;

描述

这是external_lock方法。在lock.cc中“用于mysql的锁定函数”一节,给出了关于该议题的额外注释,值的一读。在表上创建锁定。如果实施了能处理事务的存储引擎,请查看ha_berkely.cc,以了解如何执行该操作的方法。否则,应考虑在此调用flock()。由lock_external()和unlock_external()从lock.cc中调用。也能由copy_data_between_tables()从sql_table.cc中调用。

参数

thdlock_type

返回值

无返回值。

默认实施

{return0;}

16.14.7.extra

目的

将提示从服务器传递给存储引擎。

概要

virtualintextra(operation);
enumha_extra_functionoperation;

描述

这是extra方法。无论何时,当服务器希望将提示发送到存储引擎时,将调用extra()。MyISAM引擎实现了大多数提示。ha_innodb.cc给出了最详尽的提示列表。

参数

operation

返回值

无返回值。

用法

默认实施

默认情况下,存储引擎倾向于不实施任何提示。
{return0;}

16.14.8.info

目的

提示存储引擎通报统计信息。

概要

virtualvoidinfo(uint);
uint;

描述

这是info方法。::info()用于将信息返回给优化程序。目前,该表处理程序未实施实际需要的大多数字段。SHOW也能利用该数据。注意,或许你打算在你的代码中包含下述内容“if(records>2)records=2”。原因在于,服务器仅优化具有一条记录的情形。如果在表扫描过程中,你不清楚记录的数目,最好将记录数设为2,以便能够返回尽可能多的所需记录。除了记录外,你或许还希望设置其他变量,包括:删除的记录,data_file_length,index_file_length,delete_length,check_time。更多信息,请参见handler.h中的公共变量。在下述文件中调用:filesort.ccha_heap.ccitem_sum.ccopt_sum.ccsql_delete.ccsql_delete.ccsql_derived.ccsql_select.ccsql_select.ccsql_select.ccsql_select.ccsql_select.ccsql_show.ccsql_show.ccsql_show.ccsql_show.ccsql_table.ccsql_union.ccsql_update.cc

参数

uint

返回值

无返回值。

用法

该示例取自CSV存储引擎:
voidha_tina::info(uintflag){DBUG_ENTER("ha_tina::info");/*Thisisalie,butyoudon'twanttheoptimizertoseezeroor1*/if(records<2)records=2;DBUG_VOID_RETURN;}

16.14.9.open

目的

打开表。

概要

virtualintopen(name,
mode,
test_if_locked);
constchar*name;
intmode;
uinttest_if_locked;

描述

这是open方法。用于打开表。名称是文件的名称。在需要打开表时打开它。例如,当请求在表上执行选择操作时(对于每一请求,表未打开并被关闭,对其进行高速缓冲处理)。由handler::ha_open()从handler.cc中调用。通过调用ha_open(),然后调用处理程序相关的open(),服务器打开所有表。对于处理程序对象,将作为初始化的一部分并在将其用于正常查询之前打开它(并非总在元数据变化之前)。如果打开了对象,在删除之前还将关闭它。这是open方法。调用open以打开数据库表。第1个参数是要打开的表的名称。第2个参数决定了要打开的文件或将要执行的操作。这类值定义于handler.h中,为了方便起见在此列出:
#defineHA_OPEN_KEYFILE1
#defineHA_OPEN_RNDFILE2
#defineHA_GET_INDEX4
#defineHA_GET_INFO8/*doaha_info()afteropen*/
#defineHA_READ_ONLY16/*Fileopenedasreadonly*/
#defineHA_TRY_READ_ONLY32/*Tryreadonlyifcan'topenwithreadandwrite*/
#defineHA_WAIT_IF_LOCKED64/*Waitiflockedonopen*/
#defineHA_ABORT_IF_LOCKED128/*skipiflockedonopen.*/
#defineHA_BLOCK_LOCK256/*unlockwhenreadingsomerecords*/
#defineHA_OPEN_TEMPORARY512
最后的选项规定了在打开表之前是否应检查表上的锁定。典型情况下,存储引擎需要实现某种形式的共享访问控制,以防止多线程环境下的文件损坏。关于如何实现文件锁定的示例,请参见sql/examples/ha_tina.cc的get_share()和free_share()方法。

参数

namemodetest_if_locked

返回值

无返回值。

用法

该示例取自CSV存储引擎:
intha_tina::open(constchar*name,intmode,uinttest_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);}

16.14.10.rnd_init

目的

为表扫描功能初始化处理程序。

概要

virtualintrnd_init(scan);
boolscan;

描述

这是rnd_init方法。当系统希望存储引擎执行表扫描时,将调用rnd_init()。与index_init()不同,rnd_init()可以调用两次,两次调用之间不使用rnd_end()(仅当scan=1时才有意义)。随后,第2次调用应准备好新的表扫描。例如,如果rnd_init分配了光标,第2次调用应将光标定位于表的开始部分,不需要撤销分配并再次分配。从下述文件调用:filesort.cc,records.cc,sql_handler.cc,sql_select.cc,sql_table.cc,和sql_update.cc。

参数

scan

返回值

无返回值。

用法

该示例取自CSV存储引擎:
intha_tina::rnd_init(boolscan){DBUG_ENTER("ha_tina::rnd_init");current_position=next_position=0;records=0;chain_ptr=chain;DBUG_RETURN(0);}

16.14.11.rnd_next

目的

从表中读取下一行,并将其返回服务器。

概要

virtualintrnd_next(buf);
byte*buf;

描述

这是rnd_next方法。对于表扫描的每一行调用它。耗尽记录时,应返回HA_ERR_END_OF_FILE。用行信息填充buff。表的字段结构是以服务器能理解的方式将数据保存到buf中的键。从下述文件调用:filesort.cc,records.cc,sql_handler.cc,sql_select.cc,sql_table.cc,和sql_update.cc。

参数

buf

返回值

无返回值。

用法

下述示例取自ARCHIVE存储引擎:
intha_archive::rnd_next(byte*buf){intrc;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);}

16.14.12.store_lock

目的

创建和释放表锁定。

概要

virtualTHR_LOCK_DATA**store_lock(thd,
to,
lock_type);
THD*thd;
THR_LOCK_DATA**to;
enumthr_lock_typelock_type;

描述

这是store_lock方法。下面介绍了关于handler::store_lock()的概念:该语句决定了在表上需要何种锁定。对于updates/deletes/inserts,我们得到WRITE锁定;对于SELECT...,我们得到读锁定。将锁定添加到表锁定处理程序之前(请参见thr_lock.c),mysqld将用请求的锁定调用存储锁定。目前,存储锁定能将写锁定更改为读锁定(或某些其他锁定),忽略锁定(如果不打算使用MySQL表锁定),或为很多表添加锁定(就像使用MERGE处理程序时那样)。例如,BerkeleyDB能够将所有的WRITE锁定更改为TL_WRITE_ALLOW_WRITE(表明正在执行WRITES操作,但我们仍允许其他人执行操作)。释放锁定时,也将调用store_lock()。在这种情况下,通常不需要作任何事。在某些特殊情况下,MySQL可能会发送对TL_IGNORE的请求。这意味着我们正在请求与上次相同的锁定,这也应被忽略(当我们打开了表的某一部分时,如果其他人执行了表刷新操作,就会出现该情况,此时,mysqld将关闭并再次打开表,然后获取与上次相同的锁定)。我们打算在将来删除该特性。由get_lock_data()从lock.cc中调用。

参数

thdtolock_type

返回值

无返回值。

用法

下述示例取自ARCHIVE存储引擎:
/*Belowisanexampleofhowtosetuprowlevellocking.*/THR_LOCK_DATA**ha_archive::store_lock(THD*thd,THR_LOCK_DATA**to,enumthr_lock_typelock_type){if(lock_type==TL_WRITE_DELAYED)delayed_insert=TRUE;elsedelayed_insert=FALSE;if(lock_type!=TL_IGNORE&&lock.type==TL_UNLOCK){/*Hereiswherewegetintothegutsofarowlevellock.IfTL_UNLOCKissetIfwearenotdoingaLOCKTABLEorDISCARD/IMPORTTABLESPACE,thenallowmultiplewriters*/if((lock_type>=TL_WRITE_CONCURRENT_INSERT&&lock_type<=TL_WRITE)&&!thd->in_lock_tables&&!thd->tablespace_op)lock_type=TL_WRITE_ALLOW_WRITE;/*InqueriesoftypeINSERTINTOt1SELECT...FROMt2...MySQLwouldusethelockTL_READ_NO_INSERTont2,andthatwouldconflictwithTL_WRITE_ALLOW_WRITE,blockingallinsertstot2.Convertthelocktoanormalreadlocktoallowconcurrentinsertstot2.*/if(lock_type==TL_READ_NO_INSERT&&!thd->in_lock_tables)lock_type=TL_READ;lock.type=lock_type;}*to++=&lock;returnto;}

16.14.13.update_row

目的

更新已有行的内容。

概要

virtualintupdate_row(old_data,
new_data);
constbyte*old_data;
byte*new_data;

描述

这是update_row方法。old_data将保存前一行的记录,而new_data将保存最新的数据。如果使用了ORDERBY子句,服务器能够根据排序执行更新操作。不保证连续排序。目前,new_data不会拥有已更新的auto_increament记录,或已更新的时间戳字段。你可以通过下述方式(例如)完成该操作: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();从sql_select.cc,sql_acl.cc,sql_update.cc和sql_insert.cc调用。

参数

old_datanew_data

返回值

无返回值。

用法

默认实施

{returnHA_ERR_WRONG_COMMAND;}

16.14.14.write_row

目的

为表添加新行。

概要

virtualintwrite_row(buf);
byte*buf;

描述

这是write_row方法。write_row()用于插入行。目前,如果出现大量加载,不会给出任何extra()提示。buf是数据的字节数组,大小为table->s->reclength。可以使用字段信息从本地字节数组类型提取数据。例如:for(Field**field=table->field;*field;field++){...}BLOB必须特殊处理:
for(ptr=table->s->blob_field,end=ptr+table->s->blob_fields;ptr!=end;ptr++)
{
char*data_ptr;
uint32size=((Field_blob*)table->field[*ptr])->get_length();
((Field_blob*)table->field[*ptr])->get_ptr(&data_ptr);
...
}
关于以字符串形式提取所有数据的示例,请参见ha_tina.cc。在ha_berkeley.cc中,对于ha_berkeley自己的本地存储类型,给出了一个通过“包装功能”完整保存它的例子。请参见update_row()关于auto_increments和时间戳的注释。该情形也适用于write_row()。从item_sum.cc、item_sum.cc、sql_acl.cc、sql_insert.cc、sql_insert.cc、sql_select.cc、sql_table.cc、sql_udf.cc、以及sql_update.cc调用。

参数

数据的buf字节数组

返回值

无返回值。

用法

默认实施

{returnHA_ERR_WRONG_COMMAND;}
这是MySQL参考手册的翻译版本,关于MySQL参考手册,请访问dev.mysql.com。原始参考手册为英文版,与英文版参考手册相比,本翻译版可能不是最新的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐