用mysql源码进行SQL解析

<?XML:NAMESPACE PREFIX = O />

转自同事的blog

Mysql是通过yacc进行SQL语句解析的,这里介绍一下如何使用mysql的源码进行SQL语句解析。由于Mysql的源代码注释比较少,而且缺少资料,所有些地方研究不够深入。

http://blog.csdn.net/iamstillzhang/archive/2007/04/01/1548377.aspx

1 Filed介绍

MYSQLFiled有以下几种类型:

enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY,

MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG,

MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE,

MYSQL_TYPE_NULL, MYSQL_TYPE_TIMESTAMP,

MYSQL_TYPE_LONGLONG,MYSQL_TYPE_INT24,

MYSQL_TYPE_DATE, MYSQL_TYPE_TIME,

MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR,

MYSQL_TYPE_NEWDATE,

MYSQL_TYPE_ENUM=247,

MYSQL_TYPE_SET=248,

MYSQL_TYPE_TINY_BLOB=249,

MYSQL_TYPE_MEDIUM_BLOB=250,

MYSQL_TYPE_LONG_BLOB=251,

MYSQL_TYPE_BLOB=252,

MYSQL_TYPE_VAR_STRING=253,

MYSQL_TYPE_STRING=254,

MYSQL_TYPE_GEOMETRY=255

};

下图列出几种常用的Filed子类及其关系

<?XML:NAMESPACE PREFIX = V /><shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 414.75pt; HEIGHT: 233.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/still/LOCALS~1/Temp/msohtml1/01/clip_image001.emz"></imagedata></shape>

2 ITEM介绍

MYSQL中,有以下ITEM大类型:

FIELD_ITEM, FUNC_ITEM, SUM_FUNC_ITEM, STRING_ITEM, INT_ITEM, REAL_ITEM, NULL_ITEM, VARBIN_ITEM, COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_VALUE_ITEM, PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM, FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM, SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER, PARAM_ITEM

其中许多ITEM还有小类,如Item_func有如下小类型:

UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC, GE_FUNC,GT_FUNC,FT_FUNC, LIKE_FUNC,NOTLIKE_FUNC,ISNULL_FUNC,ISNOTNULL_FUNC, COND_AND_FUNC, COND_OR_FUNC, COND_XOR_FUNC, BETWEEN, IN_FUNC, INTERVAL_FUNC, ISNOTNULLTEST_FUNC, SP_EQUALS_FUNC, SP_DISJOINT_FUNC,SP_INTERSECTS_FUNC, SP_TOUCHES_FUNC,SP_CROSSES_FUNC,SP_WITHIN_FUNC, SP_CONTAINS_FUNC,SP_OVERLAPS_FUNC, SP_STARTPOINT,SP_ENDPOINT,SP_EXTERIORRING, SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN, NOT_FUNC, NOT_ALL_FUNC, NOW_FUNC, VAR_VALUE_FUNC

下图是对所用的主要的ITEM子类以及关系:

<shape id="_x0000_i1026" style="WIDTH: 415.5pt; HEIGHT: 489.75pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/still/LOCALS~1/Temp/msohtml1/01/clip_image003.emz"></imagedata></shape>

3 FIELDITEM的关系

通过Item类中的tmp_table_field_from_field_type函数将一个Item类转化为一个Filed类返回。并不是所有的Item类返回的Filed都有意义,下面列举几个有意义的转化:

Item_int ->Field_longlong

Item_real->Field_double

Item_string->Field_string

4 解析结果存放实例

例如语句select table1.field1,'test',100 from table1 where table1.field1='field1' and (table1.field2=100 or table1.field2=200)

1) 选择域的解析——table1.field1,'test',<?XML:NAMESPACE PREFIX = ST1 /><chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="100" unitname="”">100<span style="COLOR: windowtext; FONT-FAMILY: 'Times New Roman'; mso-ascii-font-family: 新宋体">”</span></chmetcnv>

将被解析为一个List<Item>

其中List的第一个元素Item的子类Item_field,表示表中的列。

第二个元素为Item_string,表示字符串。由val_str方法可获得stringtest,也可用tmp_table_field_from_field_type方法返回一个Field的子类Field_string的指针。

第三个元素为Item_int,表示整数。由val_int获得int100,也可以用tmp_table_field_from_field_type方法返回一个Field的子类Field_longlong的指针。

2) Where域的解析——where table1.field1='field1' and (table1.field2=100 or table1.field2=200)

将被解析为一个Item对象,这个对象的层次结构如下:

<shape id="_x0000_i1027" style="WIDTH: 415.5pt; HEIGHT: 261pt" type="#_x0000_t75" o:ole=""><imagedata o:title="" src="file:///C:/DOCUME~1/still/LOCALS~1/Temp/msohtml1/01/clip_image005.emz"></imagedata></shape>

5 对各类型SQL语句的解析

MYSQL的语句类型有如下类型:

enum enum_sql_command {

SQLCOM_SELECT, SQLCOM_CREATE_TABLE, SQLCOM_CREATE_INDEX, SQLCOM_ALTER_TABLE,

SQLCOM_UPDATE, SQLCOM_INSERT, SQLCOM_INSERT_SELECT,

SQLCOM_DELETE, SQLCOM_TRUNCATE, SQLCOM_DROP_TABLE, SQLCOM_DROP_INDEX,

SQLCOM_SHOW_DATABASES, SQLCOM_SHOW_TABLES, SQLCOM_SHOW_FIELDS,

SQLCOM_SHOW_KEYS, SQLCOM_SHOW_VARIABLES, SQLCOM_SHOW_LOGS, SQLCOM_SHOW_STATUS,

SQLCOM_SHOW_INNODB_STATUS,SQLCOM_SHOW_NDBCLUSTER_STATUS,

SQLCOM_SHOW_PROCESSLIST, SQLCOM_SHOW_MASTER_STAT, SQLCOM_SHOW_SLAVE_STAT,

SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS,

SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB,

SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES,

SQLCOM_GRANT,

SQLCOM_CHANGE_DB, SQLCOM_CREATE_DB, SQLCOM_DROP_DB, SQLCOM_ALTER_DB,

SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT,

SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION,

SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK,

SQLCOM_ASSIGN_TO_KEYCACHE, SQLCOM_PRELOAD_KEYS,

SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE,

SQLCOM_ROLLBACK, SQLCOM_ROLLBACK_TO_SAVEPOINT,

SQLCOM_COMMIT, SQLCOM_SAVEPOINT,

SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP,

SQLCOM_BEGIN, SQLCOM_LOAD_MASTER_TABLE, SQLCOM_CHANGE_MASTER,

SQLCOM_RENAME_TABLE, SQLCOM_BACKUP_TABLE, SQLCOM_RESTORE_TABLE,

SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_PURGE_BEFORE, SQLCOM_SHOW_BINLOGS,

SQLCOM_SHOW_OPEN_TABLES, SQLCOM_LOAD_MASTER_DATA,

SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ,

SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_UPDATE_MULTI,

SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO,

SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS,

SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,

SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,

SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE,

SQLCOM_END

};

下文只对常用的select(SQLCOM_SELECT), update(SQLCOM_UPDATE), insert(SQLCOM_INSERT), delete(SQLCOM_DELETE)做一下介绍。

5.1 Select语句

select类型的语句解析后,将结果存放在SELECT_LEX类中

其中:

选择域存放在SELECT_LEX::item_list中,类型为LIST<Item>

where域存放在SELECT_LEX::wheret中,类型为Item*

having域存放在SELECT_LEX::having中,类型为Item*

order域存放在SELECT_LEX::order_list中,实际类型为ORDER*

group域存放在SELECT_LEX::group_list中,实际类型为ORDER*

limit域存放在SELECT_LEX::select_limit中,unsigned long

table名字域存放在SELECT_LEX::table_list中,实际类型为TABLE_LIST*

(其中选择域的结构请见上文中的4(1),where域和having域的解构请见上文中的4(2), 其他几个域的解构类似于链表)

5.2 Update语句

update类型的语句解析后,将结果存放在SELECT_LEX类和LEX类中

其中:

更新域存放在SELECT_LEX::item_list中,类型为LIST<Item>

值域存放在LEX::value_list,类型为LIST<Item>

where域存放在SELECT_LEX::wheret中,类型为Item*

table名字域存放在SELECT_LEX::table_list中,实际类型为TABLE_LIST*

(其中更新域和值域的结构请见上文中的4(1),where域的解构请见上文中的4(2), table名字域的解构类似于链表)

5.3 Insert语句

insert类型的语句解析后,将结果存放在SELECT_LEX类和LEX类中

其中:

插入域存放在LEX::item_list中,类型为LIST<Item>

值域存放在LEX::many_values,类型为LIST<LIST<Item>>

table名字域存放在SELECT_LEX::table_list中,实际类型为TABLE_LIST*

(其中插入域的结构请见上文中的4(1), 值域可以含有多个LIST<Item>, table名字域的解构类似于链表)

5.4 Delete语句

delete类型的语句解析后,将结果存放在SELECT_LEX类中

其中:

where域存放在SELECT_LEX::wheret中,类型为Item*

limit域存放在SELECT_LEX::select_limit中,unsigned long

table名字域存放在SELECT_LEX::table_list中,实际类型为TABLE_LIST*

(其中选择域的结构请见上文中的4(1),where域和having域的解构请见上文中的4(2), 其他几个域的解构类似于链表)

6 如何使用

由于用到了mysql的源码,必须先下载到mysql的源码(建议版本<chsdate w:st="on" year="1899" month="12" day="30" islunardate="False" isrocdate="False"><span lang="EN-US">4.1.22</span></chsdate>),编译mysql源码,然后将makefile中的MYSQL_SRC_PATH改为实际的mysql的源码位置,然后编译。

所用到的文件:<shape id="_x0000_i1028" style="WIDTH: 72.75pt; HEIGHT: 38.25pt" type="#_x0000_t75" o:ole=""><imagedata o:title="" src="file:///C:/DOCUME~1/still/LOCALS~1/Temp/msohtml1/01/clip_image007.wmz"></imagedata></shape><shape id="_x0000_i1029" style="WIDTH: 37.5pt; HEIGHT: 38.25pt" type="#_x0000_t75" o:ole=""><imagedata o:title="" src="file:///C:/DOCUME~1/still/LOCALS~1/Temp/msohtml1/01/clip_image009.wmz"></imagedata></shape>

其中parse_base.cpp是对mysql源码的一个简单的封装,test_parse.cpp中是需要编写修改的代码。

下面对重要的部分做一下简单介绍:

// yyparse是用yaccsql进行解析

if (!yyparse((void *)&thd) && ! thd.is_fatal_error)

{

if(thd.lex == NULL

|| thd.lex->select_lex.table_list.first == NULL)

{

cout<<"内部错误"<<endl;

iRet = -1;

}

else

{

//获取LEXSELECT_LEX对象

LEX *lex= thd.lex;

SELECT_LEX *select_lex= &lex->select_lex;

//打印table的名字

TABLE_LIST* tables= (TABLE_LIST*) select_lex->table_list.first;

if(tables != NULL)

{

cout<<"table name:"<<tables->real_name<<endl;

}

enum_sql_command command_type;

command_type = lex->sql_command;

switch(command_type)

{

case SQLCOM_SELECT:

//选择

break;

case SQLCOM_DELETE:

//删除

break;

case SQLCOM_INSERT:

//插入

break;

case SQLCOM_UPDATE:

//更新

break;

default:

//其他

iRet = -1;

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值