文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>MySQL 5.1参考手册 :: 16. 编写自定义存储引擎(2)

MySQL 5.1参考手册 :: 16. 编写自定义存储引擎(2)

时间:2008-05-11  来源:sdccf

16.8. 打开表

在表上执行任何读或写操作之前,MySQL服务器将调用open()方法打开表数据和索引文件(如果存在的话)。

int open(const char *name, int mode, int test_if_locked);

第1个参数是要打开的表的名称。第2个参数确定了要打开的文件或准备执行的操作。它们的值定义于handler.h中,并为了方便起见列在下面:

#define HA_OPEN_KEYFILE                 1
#define HA_OPEN_RNDFILE                 2
#define HA_GET_INDEX               4
#define HA_GET_INFO                  8     /* do a ha_info() after open */
#define HA_READ_ONLY               16    /* File opened as readonly */
#define HA_TRY_READ_ONLY         32    /* Try readonly if can't open with read and write */
#define HA_WAIT_IF_LOCKED        64      /* Wait if locked on open */
#define HA_ABORT_IF_LOCKED     128       /* skip if locked on open.*/
#define HA_BLOCK_LOCK              256   /* unlock when reading some records */
#define HA_OPEN_TEMPORARY        512

最后一个选项规定了是否要在打开表之前检查表上的锁定。

在典型情况下,存储引擎需要实施某种形式的共享访问控制,以防止在多线程环境下的文件损坏。关于如何实施文件锁定的示例,请参见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_lock ha_tina::external_lock ha_tina::info ha_tina::rnd_init ha_tina::extra - ENUM HA_EXTRA_CACHE Cache record in HA_rrnd() ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::extra - ENUM HA_EXTRA_NO_CACHE End cacheing of records (def) ha_tina::external_lock ha_tina::extra - ENUM HA_EXTRA_RESET Reset database to after open 

16.9.1. 实施store_lock()函数

在执行任何读取或写操作之前,调用store_lock()函数。

将锁定添加到表锁定处理程序之前(请参见thr_lock.c),mysqld将用请求的锁调用存储锁定。目前,存储锁定能将写锁定更改为读锁定(或其他锁定),忽略锁定(如果不打算使用MySQL锁定的话),或为很多表添加锁定(就像使用MERGE处理程序时作的那样)。

例如,Berkeley DB能将所有的WRITE锁定更改为TL_WRITE_ALLOW_WRITE(表示我们正在执行WRITES,但我们仍允许其他人员进行操作)。

释放锁定时,也将调用store_lock(),在这种情况下,通常不需做任何事。

在某些特殊情况下,MySQL可能会发送对TL_IGNORE的请求。这意味着我们正在请求与上次相同的锁定,这也应被忽略(当我们打开了表的某一部分时,如果其他人执行了表刷新操作,就会出现该情况,此时,mysqld将关闭并再次打开表,然后获取与上次相同的锁定)。我们打算在将来删除该特性。

可能的锁定类型定义于includes/thr_lock.h中,并列在下面:

enum thr_lock_type
{
         TL_IGNORE=-1,
                    TL_UNLOCK,                             /* UNLOCK ANY LOCK */
                    TL_READ,                                 /* Read lock */
                    TL_READ_WITH_SHARED_LOCKS,  
         TL_READ_HIGH_PRIORITY,      /* High prior. than TL_WRITE. Allow concurrent insert */
         TL_READ_NO_INSERT,                /* READ, Don't allow concurrent insert */
         TL_WRITE_ALLOW_WRITE,                   /*   Write lock, but allow other threads to read / write. */
         TL_WRITE_ALLOW_READ,        /*       Write lock, but allow other threads to read / write. */
         TL_WRITE_CONCURRENT_INSERT, /* WRITE lock used by concurrent insert. */
         TL_WRITE_DELAYED,                 /* Write used by INSERT DELAYED.  Allows READ locks */
         TL_WRITE_LOW_PRIORITY,            /* WRITE lock that has lower priority than TL_READ */
         TL_WRITE,                         /* Normal WRITE lock */
         TL_WRITE_ONLY               /* Abort new lock request with an error */
};  

实际的锁定处理因锁定实施的不同而不同,你可以选择某些请求的锁定类型或不选择任何锁定类型,并根据情况恰当地代入你自己的方法。下面给出了1个CSV存储引擎实施示例:

THR_LOCK_DATA **ha_tina::store_lock(THD *thd,
                                     THR_LOCK_DATA **to,
                                     enum thr_lock_type lock_type)
 {
   if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
     lock.type=lock_type;
   *to++= &lock;
   return to;
 }  

16.9.2. 实施external_lock()函数

external_lock()函数是在事务开始时调用的,或发出LOCK TABLES语句时调用的,用于事务性存储引擎。

在sql/ha_innodb.cc和sql/ha_berkeley.cc文件中,可找到使用external_lock()的示例,但大多数存储引擎简单地返回0,就像EXAMPLE存储引擎那样:

int ha_example::external_lock(THD *thd, int lock_type)
 {
   DBUG_ENTER("ha_example::external_lock");
   DBUG_RETURN(0);
 }

16.9.3. 实施rnd_init()函数

在任何表扫描之前调用的函数是rnd_init()函数。函数rnd_init()用于为表扫描作准备,将计数器和指针复位为表的开始状态。

下述示例来自CSV存储引擎:

 int ha_tina::rnd_init(bool scan) { 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()函数期间,很多值集合还将用于SHOW TABLE STATUS语句。

在sql/handler.h中列出了完整的公共属性,下面给出了一些常见的属性:

ulonglong data_file_length;           /* Length off data file */
ulonglong max_data_file_length;       /* Length off data file */
ulonglong index_file_length;
ulonglong max_index_file_length;
ulonglong delete_length;              /* Free bytes */
ulonglong auto_increment_value;
ha_rows records;                      /* Records in table */
ha_rows deleted;                      /* Deleted records */
ulong raid_chunksize;
ulong mean_rec_length;         /* physical reclength */
time_t create_time;                   /* When table was created */
time_t check_time;
time_t update_time;  

对于表扫描,最重要的属性是“records”,它指明了表中的记录数。当存储引擎指明表中有0或1行时,或有2行以上时,在这两种情况下,优化程序的执行方式不同。因此,当你在执行表扫描之前不清楚表中有多少行时,应返回大于等于2的值,这很重要(例如,数据是在外部填充的)。

16.9.5. 实施extra()函数

执行某些操作之前,应调用extra()函数,以便为存储引擎就如何执行特定操作予以提示。

额外调用中的提示实施不是强制性的,大多数存储引擎均返回0:

int ha_tina::extra(enum ha_extra_function operation) { 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指针的可变长度行。对于每种格式,各列将按照它们由CREATE TABLE语句定义的顺序显示(表定义保存在.frm文件中,优化程序和处理程序均能从相同的源,即TABLE结构,访问表的元数据)。

每种格式以每列1比特的"NULL bitmap"开始。对于含6个列的表,其bitmap为1字节,对于含9~16列的表,其bitmap为2字节,依此类推。要想指明特定的值是NULL,应将该列NULL位设置为1。

当NULL bitmap逐个进入列后,每列将具有MySQL手册的“MySQL数据类型”一节中指定的大小。在服务器中,列的数据类型定义在sql/field.cc文件中。对于固定长度行格式,列将简单地逐个放置。对于可变长度行,VARCHAR列将被编码为1字节长,后跟字符串。对于具有BLOB列的可变长度行,每个blob由两部分表示:首先是表示BLOB实际大小的整数,然后是指向内存中BLOB的指针。

在任何表处理程序中从rnd_next()开始,可找到行转换(或“包装”)的示例。例如,在ha_tina.cc中,find_current_row()内的代码给出了使用TABLE结构(由表指向的)和字符串对象(命名缓冲)包装字符数据(来自CSV文件)的方法。将行写回磁盘需要反向转换,从内部格式解包。

下述示例来自CSV存储引擎:

int ha_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()函数中执行的。

int ha_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");
 
   /* EOF should be counted as new line */
   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++; // Increment past the first quote
     for(;mapped_ptr != end_ptr; mapped_ptr++)
     {
       // Need to convert line feeds!
       if (*mapped_ptr == '"' &&
           (((mapped_ptr[1] == ',') && (mapped_ptr[2] == '"')) ||
            (mapped_ptr == end_ptr -1 )))
       {
         mapped_ptr += 2; // Move past the , and the "
         break;
       }
       if (*mapped_ptr == '\\' && mapped_ptr != (end_ptr - 1))
       {
         mapped_ptr++;
         if (*mapped_ptr == 'r')
           buffer.append('\r');
         else if (*mapped_ptr == 'n' )
           buffer.append('\n');
         else if ((*mapped_ptr == '\\') || (*mapped_ptr == '"'))
           buffer.append(*mapped_ptr);
         else  /* This could only happed with an externally created file */
         {
           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;
   /* Maybe use \N for null? */
   memset(buf, 0, table->s->null_bytes); /* We do not implement nulls! */
 
   DBUG_RETURN(0);
 }  

16.10. 关闭表

当MySQL服务器完成表操作时,它将调用close()方法关闭文件指针并释放任何其他资源。

对于使用共享访问方法的存储引擎(如CSV引擎和其他示例引擎中显示的方法),必须将它们自己从共享结构中删除:

int ha_tina::close(void)
 {
   DBUG_ENTER("ha_tina::close");
   DBUG_RETURN(free_share(share));
 }  

对于使用其自己共享管理系统的存储引擎,应使用任何所需的方法,在它们的处理程序中,从已打开表的共享区删除处理程序实例。

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

一旦在你的存储引擎中有了读支持,下一个需要实施的特性是对INSERT语句的支持。有了INSERT支持,存储引擎就能处理WORM(写一次,读多次)应用程序,如用于以后分析的日志和归档应用等。

所有的INSERT操作均是通过write_row()函数予以处理的:

int ha_foo::write_row(byte *buf)  

*buf参数包含将要插入的行,采用内部MySQL格式。基本的存储引擎将简单地前进到数据文件末尾,并直接在末尾处添加缓冲的内容,这样就能使行读取变得简单,这是因为,你可以读取行并将其直接传递到rnd_next()函数的缓冲参数中。

写入行的进程与读取行的进程相反:从MySQL内部行格式获取数据,并将其写入数据文件。下述示例来自CSV存储引擎:

int ha_tina::write_row(byte * buf)
 {
   int size;
   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()函数:

int ha_foo::update_row(const byte *old_data, byte *new_data)

*old_data参数包含更新前位于行中的数据,而*new_data参数包含行的新内容(采用MySQL内部行格式)。

更新的执行取决于行格式和存储实施方式。某些存储引擎将替换恰当位置的数据,而其他实施方案则会删除已有的行,并在数据文件末尾添加新行。

非事务性存储引擎通常会忽略*old_data参数的内容,仅处理*new_data缓冲。事务性存储引擎可能需要比较缓冲,以确定在上次回滚中出现了什么变化。

如果正在更新的表中包含时间戳列,对时间戳的更新将由update_row()调用管理。下述示例来自CSV引擎:

int ha_tina::update_row(const byte * old_data, byte * new_data)
 {
   int size;
   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()函数删除行。

int ha_foo::delete_row(const byte *buf)

*buf参数包含要删除行的内容。对于大多数存储引擎,该参数可被忽略,但事务性存储引擎可能需要保存删除的数据,以供回滚操作使用。

下述示例来自CSV存储引擎:

int ha_tina::delete_row(const byte * 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统计,并记录计数。

相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载