简介

在MySQL中, INFORMATION_SCHEMA 信息数据库提供了访问数据库元数据的方式,其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,列的数据类型与访问权限等。在INFORMATION_SCHEMA中,有数个只读表。它们实际上是视图,也不是基本表,因此,你将无法看到与之相关的任何文件。下面将介绍如何新加一个INFORMATION_SCHEMA系统表,以能够通过查询表的方式来查询我们希望得到的元数据信息。

INFORMATION_SCHEMA表是作为MySQL的插件来实现的

INFORMATION_SCHEMA和我们经常讲到的引擎插件(Engine plugin)MYSQL_STORAGE_ENGINE_PLUGIN = 1类似,作为一个MySQL的插件来实现的。INFORMATION_SCHEMA的插件类型是MYSQL_INFORMATION_SCHEMA_PLUGIN = 4 。


1. #define MYSQL_UDF_PLUGIN             0  /* User-defined function        */
2. #define MYSQL_STORAGE_ENGINE_PLUGIN  1  /* Storage Engine               */
3. #define MYSQL_FTPARSER_PLUGIN        2  /* Full-text parser plugin      */
4. #define MYSQL_DAEMON_PLUGIN          3  /* The daemon/raw plugin type */
5. #define MYSQL_INFORMATION_SCHEMA_PLUGIN  4  /* The I_S plugin type */
6. #define MYSQL_AUDIT_PLUGIN           5  /* The Audit plugin type        */
7. #define MYSQL_REPLICATION_PLUGIN     6  /* The replication plugin type */
8. #define MYSQL_AUTHENTICATION_PLUGIN  7  /* The authentication plugin type */
9. #define MYSQL_VALIDATE_PASSWORD_PLUGIN  8   /* validate password plugin type */

INFORMATION_SCHEMA表插件接口

Mysql为要定义的INFORMATION_SCHEMA表提供了如下插件接口。主要通过st_mysql_plugin 结构实现的。


1. struct st_mysql_plugin
2. {
3. int type;             /* the plugin type (a MYSQL_XXX_PLUGIN value)   */
4. void *info;           /* pointer to type-specific plugin descriptor   */
5. const char *name;     /* plugin name                                  */
6. const char *author;   /* plugin author (for I_S.PLUGINS)              */
7. const char *descr;    /* general descriptive text (for I_S.PLUGINS)   */
8. int license;          /* the plugin license (PLUGIN_LICENSE_XXX)      */
9. int (*init)(MYSQL_PLUGIN);  /* the function to invoke when plugin is loaded */
10. int (*deinit)(MYSQL_PLUGIN);/* the function to invoke when plugin is unloaded */
11. unsigned int version; /* plugin version (for I_S.PLUGINS)             */
12. struct st_mysql_show_var *status_vars;
13. struct st_mysql_sys_var **system_vars;
14. void * __reserved1;   /* reserved for dependency checking             */
15. unsigned long flags;  /* flags for plugin */
16. };

此接口要提供这个插件的类型。这里我们要添加一个INFORMATION_SCHEMA表,所以type就是MYSQL_INFORMATION_SCHEMA_PLUGIN; name就是你要定义的表名字, 就像INFORMATION_SCHEMA中的表INNODB_SYS_DATAFILES 一样。 这里比如INNODB_MY_TABLE; 另外比较重要的两个字段就是init和deinit,这两个字段分别是在这个插件在装载和卸载时调用的函数。如果没有特殊的需求deinit函数可以直接使用系统提供的i_s_common_deinit 通用函数。 在st_mysql_plugin结构中,init字段用来提供在这个插件装载时调用的函数。其实现主要是通过为ST_SCHEMA_TABLE结构提供INFORMATION_SCHEMA表结构定义和表填充数据函数。


1. i_s_innodb_my_table_init(
2. void* p)    /*!< in/out: table schema object */
3. {
4. ST_SCHEMA_TABLE*        schema;

6. schema = reinterpret_cast<ST_SCHEMA_TABLE*>(p);
7. schema->fields_info = innodb_my_table_field;
8. schema->fill_table = i_s_innodb_my_table_fill_table;

10. DBUG_RETURN(0);
11. }

其中innodb_my_table_field就是此新加入INFORMATION_SCHEMA表结构定义;innodb_my_table_fill_table就是当我们查询这张表时,其中显示的数据就是通过这个函数提供的。

例如我们定义的新表插件接口如下:


1. UNIV_INTERN struct st_mysql_plugin      i_s_innodb_my_table =
2. {
3. /* the plugin type (a MYSQL_XXX_PLUGIN value) */
4. /* int */
5. STRUCT_FLD(type, MYSQL_INFORMATION_SCHEMA_PLUGIN),

7. /* pointer to type-specific plugin descriptor */
8. /* void* */
9. STRUCT_FLD(info, &i_s_info),

11. /* plugin name */
12. /* const char* */
13. STRUCT_FLD(name, "INNODB_MY_TABLE”),

15. /* plugin author (for SHOW PLUGINS) */
16. /* const char* */
17. STRUCT_FLD(author, "Alibaba"),

19. /* general descriptive text (for SHOW PLUGINS) */
20. /* const char* */
21. STRUCT_FLD(descr, "InnoDB My table info.”),

23. /* the plugin license (PLUGIN_LICENSE_XXX) */
24. /* int */
25. STRUCT_FLD(license, PLUGIN_LICENSE_GPL),

27. /* the function to invoke when plugin is loaded */
28. /* int (*)(void*); */
29. STRUCT_FLD(init, i_s_innodb_my_table_init),

31. /* the function to invoke when plugin is unloaded */
32. /* int (*)(void*); */
33. STRUCT_FLD(deinit, i_s_common_deinit),
34. /* plugin version (for SHOW PLUGINS) */
35. /* unsigned int */
36. STRUCT_FLD(version, INNODB_VERSION_SHORT),

38. /* struct st_mysql_show_var* */
39. STRUCT_FLD(status_vars, NULL),

41. /* struct st_mysql_sys_var** */
42. STRUCT_FLD(system_vars, NULL),

44. /* reserved for dependency checking */
45. /* void* */
46. STRUCT_FLD(__reserved1, NULL),

48. /* Plugin flags */
49. /* unsigned long */
50. STRUCT_FLD(flags, 0UL),
51. };

INFORMATION_SCHEMA表的结构定义

表结构定义在一个ST_FIELD_INFO结构中,这个结构定义了要添加到INFORMATION_SCHEMA表的各个字段,包括字段名、字段类型和字段长度等信息。这里就涉及到如何将这里定义的各个字段与在st_mysql_plugin 结构中定的表信息关联起来。这里就要用到在st_mysql_plugin 结构里定义的初始化函数有关了。实现init函数的参数,是一个void类型的输入参数,这个参数在information schema的插件中,就是一个ST_SCHEMA_TABLE结构指针,就在这个结构中要提供这个表的字段信息,以及查询表时调用填充数据的函数。


1. static ST_FIELD_INFO innodb_my_table_field[] =
2. {
3. #define IDX_MY_TABLE_FIELD_0        0
4. {STRUCT_FLD(field_name,         “field0”),
5. STRUCT_FLD(field_length,       MY_INT64_NUM_DECIMAL_DIGITS),
6. STRUCT_FLD(field_type,         MYSQL_TYPE_LONGLONG),
7. STRUCT_FLD(value,              0),
8. STRUCT_FLD(field_flags,        MY_I_S_UNSIGNED),
9. STRUCT_FLD(old_name,           ""),
10. STRUCT_FLD(open_method,        SKIP_OPEN_TABLE)},

13. #define IDX_MY_TABLE_FIELD_1        1
14. {STRUCT_FLD(field_name,         “field1”),
15. STRUCT_FLD(field_length,       MY_INT64_NUM_DECIMAL_DIGITS),
16. STRUCT_FLD(field_type,         MYSQL_TYPE_LONGLONG),
17. STRUCT_FLD(value,              0),
18. STRUCT_FLD(field_flags,        MY_I_S_UNSIGNED),
19. STRUCT_FLD(old_name,           ""),
20. STRUCT_FLD(open_method,        SKIP_OPEN_TABLE)},
21. ….
22. }

填充函数就是用来实现往INFORMATION_SCHEMA里填充数据的函数,可以根据具体的需求和要填充的数据,根据实际情况读取引擎内部的状态信息,写入到这个表中。比如上面在初始化装载函数中赋值给field_table字段的函数i_s_innodb_my_table_fill_table()。

加表到INFORMATION_SCHEMA元数据库

通过上述几步就把这个表定义出来了。 如何把这个表真正的加入到INFORMATION_SCHEMA里,客户端可以通过查询语句查询这张表呢?为了实现这个目标就要把这张表加入到ha_innodb.cc文件里,从mysql_declare_plugin(innobase) 到mysql_declare_plugin_end 之间的结构里。在这里我们可以看到已经定义了一系列的INFORMATION_SCHEMA表,包含常见到的i_s_innodb_trx、i_s_innodb_locks和i_s_innodb_sys_tables等表,只要把我们新实现的插件接口i_s_innodb_my_table加入到这个结构中,就成功把这张表加入了INFORMATION_SCHEMA元数据库中了。

原文:http://mysql.taobao.org/monthly/2018/02/08/