热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

MySQL:如何编写InformationSchemaPlugin_MySQL

MySQL:如何编写InformationSchemaPlugin
bitsCN.com

转载请署名:印风

1. 什么是i_s plugin

在mysql里面,默认会有一个information schema(以下简写为i_s)数据库,用于记录一些与元数据或表的模式相关的信息,与其他数据库不一样,在data目录下,并没有为i_s建立文件夹,这说明,i_s并不是物理存在的,而是在需要的时候,才会临时创建。这就可以解释为什么i_s库中的表的记录总是无法删除或修改。

2.为什么使用i_s plugin

虽然i_s中定义了丰富的表,但通过i_s plugin,我们可以将其功能进行扩展,丰富其中的信息,比如,我们可以把关心信息以表的形式展现出来,可以通过引入MySQL的内核代码,来监控内核的运行状态,例如锁资源状态、线程状态、table cache状态等信息。客户端可以通过sql来过滤想要的内容,甚至,我们可以在plugin中通过cond来进行过滤,而无需在mysql层处理。

3.如何编写i_s plugin

1)之前已经介绍过的,这里不在赘述,在plugin间通用的包括:

a. plugin的声明;

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

c.添加状态变量(show status)

2)初始化I_S插件

函数原型:name_init(void *p)

函数用于初始化插件,包括指定表的模式、创建表、构造表的函数指针等信息,指针p会指向一个结构体st_schema_table,如下表:

字段

类型

描述

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表

初始化的目的是为了填充结构体st_schema_table,从而确定表的定义,在查询表的时候,调用相应的函数进行记录填充。由于该结构体与内建的i_s表是公用的,因此一些字段我们可以直接忽略掉。在编写plugin的时候,我们需要填充的内容包括:

Ø Fields_info

Ø Fill_table

2).初始化表结构fields_info

Fields_info结构体为st_field_info

字段

类型

描述

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表

通常我们会预定义数组,以NULL列结束:

ST_FIELD_INFO is_field[] = {

{……},

……

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

}

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层来处理

为了将记录保存到i_s表中,这里不的不提到两个函数:field类的成员函数store_系列函数和schema_table_store_record(),前者用来存储数据到表结构体追踪,后者用来将一行存储好的数据放入到临时表中。

store函数是Field类的方法,有5个:

函数

描述

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_declimal类型或者MYSQL_TIME等MySQL代码内特有的类型,我们都可以通过引入相应的代码来构建结构体。

注意当列被声明为MY_I_S_MAYBE_NULL时,需要做一些额外的处理,见之前关于st_field_info结构体的介绍。

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

boolschema_table_store_record(THD *thd, TABLE *table);

其中thd为当前线程,table为tables->table

为了包含这两个函数,我们需要引入如下头文件:

#include

4)使用COND进行优化

从fill_table的函数原型中,我们可以看到结构体COND,这在MySQL层用作对条件进行记录过滤,实际上在plugin里,我们可以直接进行过滤,只返回到MYSQL层需要的数据。如下图所示:

如果你接触过源代码,会发现COND是一个相当复杂的类型,如果由我们自己编写代码来操作显然要耗费大量的精力,我们可以依葫芦画瓢,找到源代码里是如何使用该结构体的,构造相应的参数,就可以直接调用了,这也是Plugin的诱人之处,我们可以根据需求引用已有的代码。

MySQL层代码大多定义在sql文件夹下,我们在编译时指定相应的目录即可。

当我们的操作对系统影响比较大时,需要尽快的得到结果,例如,内建的I_S表COLUMNS,在填充数据时需要打开所有的表,如果在Plugin层做过滤,那么当我们找到一个不符合条件的表时,尽快关闭,而不是等到MYSQL层来过滤后关闭。

例如函数:

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

其中LOOPUP_FIEDL_VALUES结构体为:

sql/sql_show.cc:

typedef struct st_lookup_field_values

{

LEX_STRING value1, value2;

bool value1_is_wildcard, value2_is_wildcard;

} LOOKUP_FIELD_VALUES;

这个函数用于处理等值的情况,函数将寻找类似field1 = constant1 和field2 = constant2这样的条件, 如果找到了,将被存储在LOOKUP_FIELD_VALUES结构体的value1和value2中:

lookups.value1.str

lookups.value2.str

当我们找到了在COND中定义的条件后,就可以进行字符串匹配了。

该函数用于支持INFORMATION_SCHEMA.TABLES, INFORMATION_ SCHEMA.COLUMNS,和其他类型的内建I_S表,主要用来存储表名和数据库名,也就是说,value值为string类型,并且只支持两个等值操作,如果想实现更复杂的cond遍历,我们需要自己来实现。

示例如下(参考自《mysql plugin development》):

view plain

#include

/*声明相关的结构体和函数*/

typedef struct st_lookup_field_values

{

LEX_STRING value1, value2;

bool value1_is_wildcard,value2_is_wildcard;

} LOOKUP_FIELD_VALUES;

bool calc_lookup_values_from_cond(THD *thd,COND *cond,

TABLE_LIST *table, LOOKUP_FIELD_VALUES*lookups);

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

/*定义列类型

*包括一个整型和一个字符串型

*/

ST_FIELD_INFO cond_push_fields[] =

{

{"NUMBER",10, MYSQL_TYPE_LONG, 0, 0, 0, 0},

{"TEXT",100, MYSQL_TYPE_STRING, 0, 0, 0, 0},

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

}

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

{

/*系统默认字符集:utf-8*/

CHARSET_INFO *cs= system_charset_info;

TABLE *table =tables->table;

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

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

int num;

/*声明变量*/

LOOKUP_FIELD_VALUESlookups;

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

/*调用函数获得COND中定义的条件*/

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

return 0;

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

{

if (lookups.value1.str &&

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

(const uchar*)lookups.value1.str,

lookups.value1.length))

continue;

/*只有满足条件的字符串才会被存储到table中*/

table->field[0]->store(++num);

table->field[1]->store(*ptr, strlen(*ptr), cs);

if (schema_table_store_record(thd, table))

return 1;

}

return 0;

}

/*初始化i_s plugin*/

int cond_push_init(void *p)

{

ST_SCHEMA_TABLE*schema = (ST_SCHEMA_TABLE*) p;

/*指定表定义*/

schema->fields_info= cond_push_fields;

/*指定记录填充函数*/

schema->fill_table= fill_cond_push;

schema->idx_field1= 1;

return 0;

}

struct st_mysql_information_schemacond_push=

{MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION };

mysql_declare_plugin(cond_push)

{

MYSQL_INFORMATION_SCHEMA_PLUGIN,

&cond_push,

"COND_PUSH",

"AndrewHutchings (Andrew.Hutchings@Sun.COM)",

"A simplecondition pushdown demo table",

PLUGIN_LICENSE_GPL,

cond_push_init,

NULL,

0x0010,

NULL,

NULL,

NULL

}

mysql_declare_plugin_end;

5)例子:获取当前query cache中的QUERY信息(摘自网络,略改)

Query_cache中的query 存储在query_cache->queries结构体中,这是一个hash表,我们可以遍历其中的记录还获得想要的数据,代码如下:

view plain

#include

#include

/*内核中一些代码定义在MYSQL_SERVER宏中*/

#ifndef MYSQL_SERVER

#define MYSQL_SERVER

#endif

/*sql_cache.cc中包含了全部跟querycache相关的代码*/

#include

#include

#include

#include

#include

#include

/*创建一个子类,query_cache的成员queries为私有变量

class Accessible_Query_Cache : privateQuery_cache {

public:

HASH *get_queries()

{

return &this->queries; //&query_cache.queries;

}

};

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

#define MAX_STATEMENT_TEXT_LENGTH 32767

#define COLUMN_STATEMENT_ID 0

#define COLUMN_SCHEMA_NAME 1

#define COLUMN_STATEMENT_TEXT 2

#define COLUMN_RESULT_BLOCKS_COUNT 3

#define COLUMN_RESULT_BLOCKS_SIZE 4

#define COLUMN_RESULT_BLOCKS_SIZE_USED 5

/* 定义表结构*/

ST_FIELD_INFOmysql_is_cached_queries_fields[]=

{

{"STATEMENT_ID", 21, MYSQL_TYPE_LONG, 0, 0, "Id"},

{"SCHEMA_NAME", 64, MYSQL_TYPE_STRING, 0, 0,"Schema"},

{"STATEMENT_TEXT", MAX_STATEMENT_TEXT_LENGTH,MYSQL_TYPE_STRING, 0, 0, "Statment text"},

{"RESULT_BLOCKS_COUNT", 21, MYSQL_TYPE_LONG, 0, 0, "CountResult Blocks"},

{"RESULT_BLOCKS_SIZE", 21, MYSQL_TYPE_LONGLONG, 0, 0,"Size Result Blocks"},

{"RESULT_BLOCKS_SIZE_USED", 21, MYSQL_TYPE_LONGLONG, 0, 0,"Size Used Result Blocks"},

{0,0, MYSQL_TYPE_STRING, 0, 0, 0}

};

/*填充数据函数*/

static intmysql_is_cached_queries_fill_table(THD *thd, TABLE_LIST *tables, COND *cond)

{

intstatus;

CHARSET_INFO *scs= system_charset_info; /* need this to store field into table */

TABLE *table= (TABLE *)tables->table;

Accessible_Query_Cache *qc;

HASH *queries;

const uchar *query_cache_block_raw;

Query_cache_block* query_cache_block;

Query_cache_query* query_cache_query;

uint result_blocks_count;

ulonglong result_blocks_size;

ulonglong result_blocks_size_used;

Query_cache_block *first_result_block;

Query_cache_block *result_block;

const char *statement_text;

size_t statement_text_length;

const char *key;

size_t key_length;

/*引用query_cache全局变量*/

qc= (Accessible_Query_Cache *)&query_cache;

/*对query_cache加锁*/

query_cache.lock();

/*获取hash*/

queries = qc->get_queries();

/* 遍历hash中的所有记录/

for(uint i= 0; i records; i++)

{

/*根据索引号获取记录*/

query_cache_block_raw = hash_element(queries, i);

query_cache_block = (Query_cache_block*)query_cache_block_raw;

query_cache_query= query_cache_block->query();

table->field[COLUMN_STATEMENT_ID]->store(i+1, 0);

/* 获取sql语句*/

statement_text = (const char*)query_cache_query->query();

statement_text_length = strlen(statement_text);

/*当超出长度时需要截断…*/

table->field[COLUMN_STATEMENT_TEXT]->store( (char*)statement_text

,statement_text_length > MAX_STATEMENT_TEXT_LENGTH?

MAX_STATEMENT_TEXT_LENGTH

:statement_text_length

, scs

);

/* 获取该查询的key*/

key = (const char*)query_cache_query_get_key( query_cache_block_raw

,&key_length , 0 );

key_length =strlen(key+statement_text_length+1)-1;

/*数据库名是key的一部分,适当的偏移key指针可以得到数据库名*/

table->field[COLUMN_SCHEMA_NAME]->store((char*)key+statement_text_length+1

, key_length

,scs );

/*获得结果集所占块的个数*/

first_result_block= query_cache_query->result();

if(first_result_block)

{

/* initialize so we can loop over the result blocks*/

result_block= first_result_block;

result_blocks_count = 1;

result_blocks_size = result_block->length;

result_blocks_size_used = result_block->used;

/* loop over the result blocks*/

while((result_block= result_block->next)!=first_result_block)

{

/* calculate total number of result blocks */

result_blocks_count++;

/* calculate total size of result blocks */

result_blocks_size += result_block->length;

/* calculate total of used size of result blocks */

result_blocks_size_used += result_block->used;

}

}

else

{

result_blocks_count = 0;

result_blocks_size = 0;

result_blocks_size_used = 0;

}

/* 存储块的个数*/

table->field[COLUMN_RESULT_BLOCKS_COUNT]->store( result_blocks_count ,0);

/* 存储总的所占有块的大小*/

table->field[COLUMN_RESULT_BLOCKS_SIZE]->store( result_blocks_size , 0);

/*存储总的已使用块的大小*/

table->field[COLUMN_RESULT_BLOCKS_SIZE_USED]->store(result_blocks_size_used , 0 );

/* 将记录存储到表中*/

status = schema_table_store_record(thd, table);

if (status) {

status= 1;

goto cleanup;

}

}

status = 0;

cleanup:

query_cache.unlock();

return status;

}

static intmysql_is_cached_queries_plugin_init(void *p)

{

ST_SCHEMA_TABLE *schema= (ST_SCHEMA_TABLE *)p;

schema->fields_info= mysql_is_cached_queries_fields;

schema->fill_table= mysql_is_cached_queries_fill_table;

return 0;

}

static int mysql_is_cached_queries_plugin_deinit(void*p)

{

return0;

}

struct st_mysql_information_schemamysql_is_cached_queries_plugin=

{MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION };

/*

Plugin library descriptor

*/

mysql_declare_plugin(mysql_is_cached_queries)

{

MYSQL_INFORMATION_SCHEMA_PLUGIN,

&mysql_is_cached_queries_plugin,

"MYSQL_CACHED_QUERIES",

"Roland Bouman",

"Lists all queries in the query cache.",

PLUGIN_LICENSE_GPL,

mysql_is_cached_queries_plugin_init, /* Plugin Init */

mysql_is_cached_queries_plugin_deinit, /* Plugin Deinit */

0x0010 /* 1.0 */,

NULL, /*status variables */

NULL, /*system variables */

NULL /*config options */

}

mysql_declare_plugin_end;

view plain

 

view plain

参考

view plain

1.《MySQL Plugin Development》

view plain

2. MySQL5.1.48源代码

view plain

bitsCN.com
推荐阅读
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 推荐一个ASP的内容管理框架(ASP Nuke)的优势和适用场景
    本文推荐了一个ASP的内容管理框架ASP Nuke,并介绍了其主要功能和特点。ASP Nuke支持文章新闻管理、投票、论坛等主要内容,并可以自定义模块。最新版本为0.8,虽然目前仍处于Alpha状态,但作者表示会继续更新完善。文章还分析了使用ASP的原因,包括ASP相对较小、易于部署和较简单等优势,适用于建立门户、网站的组织和小公司等场景。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了如何在MySQL中将零值替换为先前的非零值的方法,包括使用内联查询和更新查询。同时还提供了选择正确值的方法。 ... [详细]
  • 在数据分析工作中,我们通常会遇到这样的问题,一个业务部门由若干业务组构成,需要筛选出每个业务组里业绩前N名的业务员。这其实是一个分组排序的 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 本文由编程笔记小编整理,介绍了PHP中的MySQL函数库及其常用函数,包括mysql_connect、mysql_error、mysql_select_db、mysql_query、mysql_affected_row、mysql_close等。希望对读者有一定的参考价值。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 2018年人工智能大数据的爆发,学Java还是Python?
    本文介绍了2018年人工智能大数据的爆发以及学习Java和Python的相关知识。在人工智能和大数据时代,Java和Python这两门编程语言都很优秀且火爆。选择学习哪门语言要根据个人兴趣爱好来决定。Python是一门拥有简洁语法的高级编程语言,容易上手。其特色之一是强制使用空白符作为语句缩进,使得新手可以快速上手。目前,Python在人工智能领域有着广泛的应用。如果对Java、Python或大数据感兴趣,欢迎加入qq群458345782。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 学习SLAM的女生,很酷
    本文介绍了学习SLAM的女生的故事,她们选择SLAM作为研究方向,面临各种学习挑战,但坚持不懈,最终获得成功。文章鼓励未来想走科研道路的女生勇敢追求自己的梦想,同时提到了一位正在英国攻读硕士学位的女生与SLAM结缘的经历。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
author-avatar
董可芳妍_731
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有