MySQL:如何编写Information Schema Plugin

 

对php内核有一定驾驭的人应有都了解php的优秀就是HashTable,HashTable在php的完毕中无处不在。包蕴php的数组、什么全局变量、局地变量的功用域等等,php的hashtable拆开的话正是四局地:

 

 

hash函数:用的是time33的散列函数,将四个字符串的key转换到一个数字
贰个C数组:用来储存桶(buckets)的
三个双向的链表:第二个双向链表是数组的每个元素(桶bucket)是一个双向链表,那样做是为着缓慢解决hash争辩;第一个双向链表是数组将每一个桶(bucket)连接起来,这里要连接的约等于率先个双向链表的链表头,那样做是为了遍历整个hash表用的,鸟哥有篇blog是讲php的foreach的,这里如此设计就是给foreach用的==>《深入通晓PHP之数组(遍历顺序)》
自家这边不再说hashtable的struct和bucket的struct了,因为上面包车型大巴推荐介绍链接大约都讲了,我不以为本身能描述和说的比她们好,每种人的品位不一致样,小编就以本人今后的本领水平来说述,所以我就只把作者整理的片段事物记录一下

转发请签字:印风

Hash链表的运用比较宽泛,其目标即是为了将分裂的值映射到分裂的岗位,查找的时候一向找到相应的职责,而无需思想的依次遍历或是二分查找,从而实现收缩查询时间的目标。常规的hash是预约义一定的桶(bucket),规定四个hash函数,然后开始展览散列。可是Mysql中的hash未有定点的bucket,hash函数也是动态变化的,本文就进行非深刻介绍。

下边是php中hash达成的八个公文:zend_hash.c
zend_hash.h。那五个公文之中完结了一批的api,也引申出了一批的api,下边是落到实处出来的api的原型

  1. 什么是i_s plugin

 

复制代码 代码如下:

在mysql里面,默许会有三个information
schema(以下简写为i_s)数据库,用于记录一些与元数据或表的方式相关的消息,与其它数据库不一致样,在data目录下,并未为i_s构建文件夹,那表达,i_s并不是大意存在的,而是在急需的时候,才会临时创办。这就足以分解为何i_s库中的表的记录总是不能够删除或改变。

 

ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength)
ZEND_API ulong zend_get_hash_value(const char *arKey, uint
nKeyLength)
ZEND_API int _zend_hash_init(HashTable *ht, uint nSize,
hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool
persistent ZEND_FILE_LINE_DC)
 ZEND_API void zend_hash_set_apply_protection(HashTable *ht,
zend_bool bApplyProtection)
ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char
*arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest,
int flag ZEND_FILE_LINE_DC)
ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const
char *arKey, uint nKeyLength, ulong h, void *pData, uint nDataSize,
void **pDest, int flag ZEND_FILE_LINE_DC)
ZEND_API int _zend_hash_index_update_or_next_insert(HashTable
*ht, ulong h, void *pData, uint nDataSize, void **pDest, int flag
ZEND_FILE_LINE_DC)
ZEND_API int zend_hash_rehash(HashTable *ht)
static int zend_hash_do_resize(HashTable *ht)
ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char
*arKey, uint nKeyLength, ulong h, int flag)
ZEND_API void zend_hash_destroy(HashTable *ht)
ZEND_API void zend_hash_clean(HashTable *ht)
static Bucket *zend_hash_apply_deleter(HashTable *ht, Bucket *p)
ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht)
ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t
apply_func TSRMLS_DC)
ZEND_API void zend_hash_apply_with_argument(HashTable *ht,
apply_func_arg_t apply_func, void *argument TSRMLS_DC)
ZEND_API void zend_hash_apply_with_arguments(HashTable *ht
TSRMLS_DC, apply_func_args_t apply_func, int num_args, …)
ZEND_API void zend_hash_reverse_apply(HashTable *ht, apply_func_t
apply_func TSRMLS_DC)
ZEND_API void zend_hash_copy(HashTable *target, HashTable *source,
copy_ctor_func_t pCopyConstructor, void *tmp, uint size)
ZEND_API void _zend_hash_merge(HashTable *target, HashTable
*source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size,
int overwrite ZEND_FILE_LINE_DC)
static zend_bool zend_hash_replace_checker_wrapper(HashTable
*target, void *source_data, Bucket *p, void *pParam,
merge_checker_func_t merge_checker_func)
ZEND_API void zend_hash_merge_ex(HashTable *target, HashTable
*source, copy_ctor_func_t pCopyConstructor, uint size,
merge_checker_func_t pMergeSource, void *pParam)   
ZEND_API int zend_hash_find(const HashTable *ht, const char *arKey,
uint nKeyLength, void **pData)
ZEND_API int zend_hash_quick_find(const HashTable *ht, const char
*arKey, uint nKeyLength, ulong h, void **pData)
ZEND_API int zend_hash_exists(const HashTable *ht, const char
*arKey, uint nKeyLength)
ZEND_API int zend_hash_quick_exists(const HashTable *ht, const char
*arKey, uint nKeyLength, ulong h)
ZEND_API int zend_hash_index_find(const HashTable *ht, ulong h,
void **pData)
ZEND_API int zend_hash_index_exists(const HashTable *ht, ulong h)
ZEND_API int zend_hash_num_elements(const HashTable *ht)
ZEND_API int zend_hash_get_pointer(const HashTable *ht, HashPointer
*ptr)
ZEND_API int zend_hash_set_pointer(HashTable *ht, const HashPointer
*ptr)
ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht,
HashPosition *pos)
ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht,
HashPosition *pos)
ZEND_API int zend_hash_move_forward_ex(HashTable *ht, HashPosition
*pos)
ZEND_API int zend_hash_move_backwards_ex(HashTable *ht,
HashPosition *pos)
ZEND_API int zend_hash_get_current_key_ex(const HashTable *ht,
char **str_index, uint *str_length, ulong *num_index, zend_bool
duplicate, HashPosition *pos)
ZEND_API int zend_hash_get_current_key_type_ex(HashTable *ht,
HashPosition *pos)
ZEND_API int zend_hash_get_current_data_ex(HashTable *ht, void
**pData, HashPosition *pos)
ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int
key_type, const char *str_index, uint str_length, ulong num_index,
int mode, HashPosition *pos)
ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
compare_func_t compar, int renumber TSRMLS_DC)
ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2,
compare_func_t compar, zend_bool ordered TSRMLS_DC)
ZEND_API int zend_hash_minmax(const HashTable *ht, compare_func_t
compar, int flag, void **pData TSRMLS_DC)
ZEND_API ulong zend_hash_next_free_element(const HashTable *ht)
void zend_hash_display_pListTail(const HashTable *ht)
void zend_hash_display(const HashTable *ht)  

 

 

您恐怕感兴趣的篇章:

  • PHP中对种种加密算法、Hash算法的速度测试对照代码
  • php操作redis中的hash和zset类型数据的办法和代码例子
  • PHP的password_hash()使用实例
  • php的hash算法介绍
  • 深刻PHP中的HashTable结构详解
  • PHP中用hash完成的数组
  • 明白php
    Hash函数,巩固密码安全
  • php 分库分表hash算法
  • php基于mcrypt的加密解密实例
  • PHP中加密解密函数与DES加密解密实例
  • php常用hash加密函数

2.怎么使用i_s plugin

 

虽然i_s中定义了足够的表,但经过i_s
plugin,我们能够将其意义拓展扩展,足够之中的音讯,举例,我们可以把关切音讯以表的花样显示出来,可以经过引进MySQL的基础代码,来监督内核的运作情形,举例锁财富气象、线程状态、table
cache状态等音信。客户端能够通过sql来过滤想要的内容,以至,大家得以在plugin中经过cond来进展过滤,而无需在mysql层管理。

大旨结构体

 

 

3.哪些编写i_s plugin

   
Hash的构造体定义以及有关的函数接口定义在include/hash.h和mysys/hash.c多个文本中。上面是HASH结构体的概念。

1)之前早已介绍过的,这里不在赘述,在plugin间通用的总结:

 

a. plugin的声明;

typedef struct st_hash {

b.加多系统变量(show /setvariables)

  size_t key_offset,key_length;     /* Length of key if const length
*/

c.增多状态变量(show status)

  size_t blength;

 

  ulong records;

2)初始化I_S插件

  uint flags;

函数原型:name_init(void *p)

  DYNAMIC_ARRAY array;              /* Place for hash_keys */

函数用于开首化插件,包罗钦定表的情势、创制表、构造表的函数指针等新闻,指针p会指向二个构造体st_schema_table,如下表:

  my_hash_get_key get_key;

字段

类型

描述

table_name

const char*

mysql会自动对表赋予插件名,因此我们无需直接赋值

fields_info

ST_FIELD_INFO *

ST_FIELD_INFO类型的结构体数组,用于存储表的每一列的信息,如列名及类型等

create_table

TABLE *(*create_table)  (THD *thd, TABLE_LIST *table_list);

函数指针,用来创建TABLE结构体,所有的i_s表基本一致,mysql会自动赋值

fill_table

int (*fill_table) (THD *thd, TABLE_LIST *tables, COND *cond);

函数指针,用于向表中填充记录

old_format

int (*old_format) (THD *thd, struct st_schema_table *schema_table);

用于支持内建i_s表的show功能,无需关注

process_table

int (*process_table) (

THD *thd, TABLE_LIST *tables, TABLE *table,                        bool res,  LEX_STRING *db_name,

LEX_STRING *table_name);

仅用于内建i_s表

idx_field1, idx_field2

int

仅用于内建i_s表

hidden

bool

如果为true,则其中的数据只能通过show展现,由于i_s Plugin不支持show,无需关心此变量

i_s_requested_object

uint

仅用于内建i_s表

  void (*free)(void *);

 

  CHARSET_INFO *charset;

开始化的指标是为着填充结构体st_schema_table,从而显明表的概念,在查询表的时候,调用相应的函数实行记录填充。由于该结构体与内建的i_s表是公用的,因而部分字段我们能够一贯忽略掉。在编写plugin的时候,大家供给填写的开始和结果包蕴:

} HASH;

Ø  Fields_info

 

Ø  Fill_table

成员名 说明
key_offset hash时key的offset,在不指定hash函数的情况下有意义
key_length key的长度,用于计算key值
blength 非常重要的辅助结构,初始为1,动态变化,用于hash函数计算,这里理解为bucket length(其实不是真实的bucket数)
records 实际的记录数
flags 是否允许存在相同的元素,取值为HASH_UNIQUE(1)或者0
array 存储元素的数组
get_key 用户定义的hash函数,可以为NULL
free 析构函数,可以为NULL
charset 字符集

 

 

2).早先化表结构田野s_info

 

Fields_info结构体为st_field_info

<font size=”4″> </font><font
size=”3″>HASH结构体里面含有了贰个动态数组结构体DYNAMIC_A奥德赛RAY,这里就联手介绍了。其定义在<em><strong>include/my_sys.h</strong></em>中。</font>

字段

类型

描述

field_name

const char*

列名,通常用大写表示

field_length

uint

当列的类型为varchar、text时表示字符数,列类型为blob时表示字节数,类型为float 或double时表示数字数,对于decima类型,值为precision*100+scale

field_type

enum enum_field_types

枚举类型,用于指定行类型,包括如下:

MYSQL_TYPE_TINY、

MYSQL_TYPE_SHORT、

MYSQL_TYPE_INT24、

MYSQL_TYPE_LONG、

MYSQL_TYPE_LONGLONG、

MYSQL_TYPE_TIME、

MYSQL_TYPE_DATE、

MYSQL_TYPE_DATETIME、MYSQL_TYPE_TIMESTAMP、MYSQL_TYPE_FLOAT、

MYSQL_TYPE_DOUBLE、MYSQL_TYPE_DECIMAL、MYSQL_TYPE_NEWDECIMAL、MYSQL_TYPE_TINY_BLOB、MYSQL_TYPE_MEDIUM_BLOB、MYSQL_TYPE_BLOB、MYSQL_TYPE_LONG_BLOB、MYSQL_TYPE_STRING,虽然类型很多,但在内置的i_s表中,只用到了MYSQL_TYPE_STRING, MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONG, MYSQL_TYPE_DECIMAL,  MYSQL_TYPE_DATETIME这几种类型,为了避免意外的分享,我们也尽量使用这几种。

value

int

未使用

field_flags

uint

用于表示列的属性,为MY_I_S_UNSIGNED表示列为unsigned类型,为MY_I_S_MAYBE_NULL 表示该列的值可能为NULL

这里需要注意,如果定义为MY_I_S_MAYBE_NULL类型,那么在填充表字段信息时,我们总需要首先调用:

(非空)tables->table->field[0]->set_notnull();

(为空)tables->table->field[0]->set_null();

 

old_name

const char*

open_method

uint

仅用于内建的i_s表

typedef struct st_dynamic_array

 

{

习感觉常我们会预订义数组,以NULL列结束:

  uchar *buffer;

ST_FIELD_INFO  is_field[] = {

  uint elements,max_element;

         {……},

  uint alloc_increment;

         ……

  uint size_of_element;

{0, 0, MYSQL_TYPE_NULL, 0, 0, 0, 0}

} DYNAMIC_ARRAY;

}

成员名 说明
buffer 一块连续的地址空间,用于存储数据,可以看成一个数组空间
elements 元素个数
max_element 元素个数上限
alloc_increment 当元素达到上限时,即buffer满时,按照alloc_increment进行扩展
size_of_element 每个元素的长度

 

 

3)fill_table()

 

函数原型:int fill_table(THD *thd, TABLE_LIST *tables, COND *cond);

 

参数描述:

开首化函数

参数名

类型

描述

thd

THD*

当前执行query的线程

tables

TABLE_LIST

当前query所指定的表(在i_s中的表,都是查询时才自动创建的临时表),我们需要把数据放如到tables中

cond

COND*

where条件,我们可以在函数中通过cond来过滤数据,只把需要的记录加入到临时表中。也可以不搭理,让mysql层来处理

 

 

  
Hash开始化函数对外提供四个,my_hash_init和my_hash_init2,其差异就是或不是定义了growth_size(用于安装DYNAMIC_ARRAY的alloc_increment)。代码在mysys/hash.c中。

为了将记录保留到i_s表中,这里不的不涉及八个函数:田野同志类的分子函数store_多种函数和schema_table_store_record(),前者用来积攒数据到表结构体追踪,后者用来将一行存储好的多寡放入到不时表中。

 

store函数是Field类的主意,有5个:

#define my_hash_init(A,B,C,D,E,F,G,H) \

函数

描述

Field::store(const char *to, uint length, CHARSET_INFO *cs)

to:字符串指针;length:字符串长度

cs:字符串的字符集,默认的字符集为system_charset_info

bin类型为my_charset_bin、

latin1类型为 my_charset_latin1,此外,我们还可以通过get_charset()、get_charset_by_name()或者get_charset_by_csname()来获得字符集信息

Field::store(longlong nr, bool unsigned_val)

nr:longlong整数值

unsigned_val:是否为unsigned类型

Field::store(double nr)

存储double类型

Field::store_decimal(const my_decimal *d)

decimal类型

Field::store_time(MYSQL_TIME *ltime, timestamp_type t_type)

时间类型

          _my_hash_init(A,0,B,C,D,E,F,G,H)

 

#define my_hash_init2(A,B,C,D,E,F,G,H,I) \

其中my_declimal类型可能MYSQL_TIME等MySQL代码内故意的系列,大家都得以经过引入相应的代码来营造结构体。

          _my_hash_init(A,B,C,D,E,F,G,H,I)

小心当列被声称为MY_I_S_MAYBE_NULL时,供给做一些相当的拍卖,见前边境海关于st_field_info结构体的牵线。

 

当store数据到Field结构体后,大家还亟需将其储存到表中,API函数如下:

/**

boolschema_table_store_record(THD *thd, TABLE *table);

  @brief Initialize the hash

内部thd为当前线程,table为tables->table

 

 

  @details

为了包含那七个函数,大家需求引进如上边文件:

 

#include <mysql_priv.h>

  Initialize the hash, by defining and giving valid values for

 

  its elements. The failure to allocate memory for the

4)使用COND进行优化

  hash->array element will not result in a fatal failure. The

从fill_table的函数原型中,大家能够见到结构体COND,那在MySQL层用作对规则实行记录过滤,实际上在plugin里,大家能够直接进行过滤,只重返到MYSQL层要求的多少。如下图所示:

  dynamic array that is part of the hash will allocate memory

 

  as required during insertion.

设若您接触过源代码,会发觉COND是贰个卓殊复杂的花色,若是由大家温馨编排代码来操作显明要成本大批量的生命力,大家可以依葫芦画瓢,找到源代码里是怎么利用该结构体的,构造相应的参数,就能够直接调用了,那也是Plugin的使人迷恋之处,大家得以依赖必要引用已有个别代码。

 

MySQL层代码多数定义在sql文件夹下,大家在编写翻译时钦点相应的目录就能够。

  @param[in,out] hash         The hash that is initialized

当我们的操作对系统影响一点都十分大时,要求赶紧的获得结果,举个例子,内建的I_S表COLUMNS,在填充数据时索要开垦全体的表,倘诺在Plugin层做过滤,那么当我们找到多个不符合条件的表时,尽快关闭,而不是等到MYSQL层来过滤后关闭。

  @param[in]     charset      The charater set information

举个例子说函数:

  @param[in]     size         The hash size

bool calc_lookup_values_from_cond(THD *thd,COND *cond, TABLE_LIST
*table, LOOKUP_FIELD_VALUES *lookups);

  @param[in]     key_offest   The key offset for the hash

其中LOOPUP_FIEDL_VALUES结构体为:

  @param[in]     key_length   The length of the key used in

sql/sql_show.cc:

                              the hash

typedef struct st_lookup_field_values

  @param[in]     get_key      get the key for the hash

{

  @param[in]     free_element pointer to the function that

LEX_STRING value1, value2;

                              does cleanup

bool value1_is_wildcard, value2_is_wildcard;

  @return        inidicates success or failure of initialization

} LOOKUP_FIELD_VALUES;

    @retval 0 success

 

    @retval 1 failure

本条函数用于拍卖等值的情况,函数将寻觅类似田野1 = constant1 和田野先生2 =
constant2如此的原则,
借使找到了,将被积攒在LOOKUP_FIELD_VALUES结构体的value1和value第22中学:

*/

lookups.value1.str

my_bool

lookups.value2.str

_my_hash_init(HASH *hash, uint growth_size, CHARSET_INFO
*charset,

当大家找到了在COND中定义的尺度后,就能够进行字符串匹配了。

              ulong size, size_t key_offset, size_t key_length,

该函数用于扶助INFORMATION_SCHEMA.TABLES, INFORMATION_
SCHEMA.COLUMNS,和其余门类的内建I_S表,主要用来存款和储蓄表名和数据库名,也正是说,value值为string类型,并且只帮助三个等值操作,要是想达成更复杂的cond遍历,大家要求自个儿来贯彻。

              my_hash_get_key get_key,

 

              void (*free_element)(void*), uint flags)

演示如下(参谋自《mysql plugin development》):

{

 

  DBUG_ENTER(“my_hash_init”);

 

  DBUG_PRINT(“enter”,(“hash: 0x%lx  size: %u”, (long) hash, (uint)
size));

 

 

 

  hash->records=0;

 

  hash->key_offset=key_offset;

view plain

  hash->key_length=key_length;

#include <mysql_priv.h> 

  hash->blength=1;

  

  hash->get_key=get_key;

/*宣示相关的结构体和函数*/ 

  hash->free=free_element;

typedef struct st_lookup_field_values 

  hash->flags=flags;

  hash->charset=charset;

LEX_STRING value1, value2; 

  DBUG_RETURN(my_init_dynamic_array_ci(&hash->array,

bool value1_is_wildcard,value2_is_wildcard; 

                                       sizeof(HASH_LINK), size,
growth_size));

} LOOKUP_FIELD_VALUES; 

}

bool calc_lookup_values_from_cond(THD *thd,COND *cond, 

<font size=”3″>  
可以看看,_my_hash_init函数首倘若伊始化HASH结构体和hash->array(DYNAMIC_ARRAY结构体)。</font>

TABLE_LIST *table, LOOKUP_FIELD_VALUES*lookups); 

动态HASH函数

bool schema_table_store_record(THD *thd,TABLE *table); 

 

  

<font size=”3″>   大家率先来看下hash函数的概念:</font>

/*概念列类型

static inline char*

*总结叁个整型和二个字符串型

my_hash_key(const HASH *hash, const uchar *record, size_t *length,

*/ 

            my_bool first)

ST_FIELD_INFO cond_push_fields[] = 

{

  if (hash->get_key)

{“NUMBER”,10, MYSQL_TYPE_LONG, 0, 0, 0, 0}, 

    return (char*) (*hash->get_key)(record,length,first);

{“TEXT”,100, MYSQL_TYPE_STRING, 0, 0, 0, 0}, 

  *length=hash->key_length;

{0, 0,MYSQL_TYPE_NULL, 0, 0, 0, 0} 

  return (char*) record+hash->key_offset;

}

  

 

int fill_cond_push(THD *thd, TABLE_LIST*tables, COND *cond) 

static uint my_hash_mask(my_hash_value_type hashnr, size_t
buffmax,

                         size_t maxlength)

         /*系统暗许字符集:utf-8*/ 

{

CHARSET_INFO *cs= system_charset_info; 

  if ((hashnr & (buffmax-1)) < maxlength) return (hashnr &
(buffmax-1));

TABLE *table =tables->table; 

  return (hashnr & ((buffmax >> 1) -1));

  

}

/*字符串数组output,用于测试只回去符合条件的字符串*/ 

 

const char**ptr, *output[] = {“hello”, “world”, “this”, “is”,”a”,
“test”, 0}; 

 

int num; 

my_hash_key参数 说明
hash HASH链表结构
record 带插入的元素的值
length 带插入元素的值长度
first 辅助参数

  

<font size=``"3"``>   </font>

/*宣示变量*/ 

 

LOOKUP_FIELD_VALUESlookups; 

my_hash_mask参数 说明
hashnr my_hash_key的计算结果
buffmax hash结构体中的blength
maxlength 实际桶的个数

bzero((char*)&lookups, sizeof(lookups)); 

   
你或者要问作者怎么有四个?其实那和我们通常应用的几近,第五个函数my_hash_key是基于我们的值举办Hash Key总计,一般大家在计算后,会对hash
key进行一遍模运算,以便计算结果在大家的bucket中。即my_hash_key的结果作为my_hash_mask的率先个输入参数。其实到那边都以可怜好精晓的,唯一让本人蛋疼的是my_hash_mask的兑现,其计算结果是和第二和第八个参数有关,即Hash结构体中的blength和records有关。动态变化的,小编去..

/*调用函数获得COND中定义的规格*/ 

    

if (calc_lookup_values_from_cond(thd, cond, tables,&lookups)) 

 

return 0; 

 

for (num = 0,ptr = output; *ptr; ptr++) 

见到此间作者吸引了,作者上网经过各样百度,谷歌(Google),终于让自个儿找到了一封Mysql
Expert的回信:

 

if (lookups.value1.str && 

Hi!

my_strnncoll(cs, (const uchar*)*ptr, strlen(*ptr), 

“Yan” == Yan Yu
<[email protected]>
writes:

(const uchar*)lookups.value1.str, 

 

lookups.value1.length)) 

Yan> Dear MySQL experts:

continue; 

Yan>      Thank you so much for your reply to my previous Qs, they
are very