当前位置: 首页 > 文档资料 > MySQL 中文手册 >

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

优质
小牛编辑
128浏览
2023-12-01
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 - ENUM HA_EXTRA_CACHE   Cache record in HA_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 - ENUM HA_EXTRA_NO_CACHE   End cacheing of records (def)ha_tina::external_lockha_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);
 }