语法分析——YACC
接触过SQL语句的人都会看过这家或者那家的SQL手册,其语法标准应该是从SQL92开始吧,在看SQL92标准的时候,你会发现里面定义的都是一些巴科斯范式(BNF),就是一种语法定义的标准。不管是牛X哄哄的ORACLE,还是不幸被其收购的Mysql,都会遵循里面的标准语法,当然一些扩展的语法除外,比如今天我们就会扩展一个简单的语法^-^。
OK,大家知道了SQL语法的来源,那么如何进行语法解析呢?YACC!!(Yet Another Compiler Compiler),它的书写方式便是BNF,语法解析的利器。YACC接收来自词法分析阶段分解出来的token,然后去匹配那些BNF。今天哥就来揭开它的面纱。(关于YACC的基本使用方法,大家可以看我上一篇中提到IBM的链接,一定要看懂那个先)
继续上一节的语句SELECT @@VERSION_COMMET,为了简单,这里省去后缀limit 1。Mysql的语法文件是sql_yacc.yy,首先给出这条语句涉及到的语法节点(大体浏览下即可):
query:
END_OF_INPUT
{...}
|| verb_clause
{...}
| verb_clause END_OF_INPUT
{
/* Single query, not terminated. */
YYLIP->found_semicolon= NULL;
}
verb_clause:
statement
| begin
;
statement:
alter
| analyze
| backup
| binlog_base64_event
| call
| change
| check
| checksum
| commit
| create
| deallocate
| delete
| describe
| do
| drop
| execute
| flush
| grant
| handler
| help
| insert
| install
| kill
| load
| lock
| optimize
| keycache
| partition_entry
| preload
| prepare
| purge
| release
| rename
| repair
| replace
| reset
| restore
| revoke
| rollback
| savepoint
| select
| set
| show
| slave
| start
| truncate
| uninstall
| unlock
| update
| use
| xa
;
select:
select_init
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SELECT;
}
;
select_init:
SELECT_SYM select_init2
| '(' select_paren ')' union_opt
;
select_init2:
select_part2
{
LEX *lex= Lex;
SELECT_LEX * sel= lex->current_select;
if (lex->current_select->set_braces(0))
{
my_parse_error(ER(ER_SYNTAX_ERROR));
MYSQL_YYABORT;
}
if (sel->linkage == UNION_TYPE &&
sel->master_unit()->first_select()->braces)
{
my_parse_error(ER(ER_SYNTAX_ERROR));
MYSQL_YYABORT;
}
}
union_clause
;
select_part2:
{
LEX *lex= Lex;
SELECT_LEX *sel= lex->current_select;
if (sel->linkage != UNION_TYPE)
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
}
select_options select_item_list
{
Select->parsing_place= NO_MATTER;
}
select_into select_lock_type
;
select_item_list:
select_item_list ',' select_item
| select_item
| '*'
{
THD *thd= YYTHD;
Item *item= new (thd->mem_root)
Item_field(&thd->lex->current_select->context,
NULL, NULL, "*");
if (item == NULL)