在编写dbscale的sql解析器的过程中,耗时最多的对select语法分支的冲突处理。
主要的问题来自:
1. select可能是个子查询
2. select支持 select:(select),即可以自由嵌套小括号
3. 对union语句的支持
问题1的关键在于如何区分第一个碰到的“SELECT”是一个select语句起始的“SELECT”,还是一个子查询的“SELECT”。
解决的办法是所有其他可能带有子查询语法分支在进入子查询分支之前必须先把语句类型标识出来。
例如:
stmt: delete_stmt;
delete_stmt: DELETE {pp->st->type = ST_DELETE;} delete_sql;
delete_sql:...
这样在碰到第一个SELECT的时候只需要去判别 变量pp->st->type是否被设置,如果没有设置那么将它设置为ST_SELECT并且按照select语句来处理,否则就是按照子查询来处理了。
问题2的关键在于在括号中的子查询如何处理,例如 expr: expr in ( sub_query);
bison无法区分这个括号是属于 in这个操作符的还是select语句自己带的。 为了解决这个问题,我参考了postgresql的解析器代码.
具体的解决办法是把select语句分为2种,不带扩号的select和带括号的select,并且定义select语句的优先级低于普通的括号。
...
%right
USELECTMINUS
%left
'(' ')'
...
select_stmt: select_no_parens
%prec USELECTMINUS
|
select_with_parens
%prec USELECTMINUS
;
...
在这个基础上postgresql将所有 ( sub_query) 看作 select_with_parens。
这么做主要是为了避免冲突,但由于我的语法树和postgresql的不一样,我只需要配置select语句的优先级低于普通括号的优先级就可以了。
问题3主要的问题在于语句 “select a from t1 union select b from t2 limit3 ”需要被解析成:
“(select a from t1 union select b from t2) limit 3”
而不是 :
“(select a from t1) union (select b from t2 limit 3)”
这个问题目前我没有太完美的解决它,一个折中的方案是 union后面的那个select语句必须是带括号的select语句,即select_with_parens。
以下是具体的select语法定义:
...
%right
USELECTMINUS
%left
UNION
%left
'(' ')'
...
select_stmt: select_no_parens
%prec USELECTMINUS
|
select_with_parens
%prec USELECTMINUS
|
select_union
;
select_union: select_stmt UNION opt_all select_with_parens opt_groupby opt_having opt_orderby opt_limit %prec UNION;
select_with_parens: '(' select_no_parens ')'
|
'(' select_with_parens ')'
;
subquery_stmt_no_parens: select_no_parens;
subquery_stmt: select_stmt;
select_no_parens: select_query
;
select_query: SELECT
{
} query_part
;
query_part: select_opts select_expr_list opt_limit
|
select_opts select_expr_list
FROM table_references
opt_where opt_groupby opt_having opt_orderby opt_limit
opt_into_list
|
select_opts select_expr_list INTO column_list
FROM table_references
opt_where opt_groupby opt_having opt_orderby opt_limit
;
....
expr: expr IN '(' subquery_stmt ')';
....
转载请注明出自高孝鑫的博客。
主要的问题来自:
1. select可能是个子查询
2. select支持 select:(select),即可以自由嵌套小括号
3. 对union语句的支持
问题1的关键在于如何区分第一个碰到的“SELECT”是一个select语句起始的“SELECT”,还是一个子查询的“SELECT”。
解决的办法是所有其他可能带有子查询语法分支在进入子查询分支之前必须先把语句类型标识出来。
例如:
stmt: delete_stmt;
delete_stmt: DELETE {pp->st->type = ST_DELETE;} delete_sql;
delete_sql:...
这样在碰到第一个SELECT的时候只需要去判别 变量pp->st->type是否被设置,如果没有设置那么将它设置为ST_SELECT并且按照select语句来处理,否则就是按照子查询来处理了。
问题2的关键在于在括号中的子查询如何处理,例如 expr: expr in ( sub_query);
bison无法区分这个括号是属于 in这个操作符的还是select语句自己带的。 为了解决这个问题,我参考了postgresql的解析器代码.
具体的解决办法是把select语句分为2种,不带扩号的select和带括号的select,并且定义select语句的优先级低于普通的括号。
...
%right
%left
...
select_stmt: select_no_parens
...
在这个基础上postgresql将所有 ( sub_query) 看作 select_with_parens。
这么做主要是为了避免冲突,但由于我的语法树和postgresql的不一样,我只需要配置select语句的优先级低于普通括号的优先级就可以了。
问题3主要的问题在于语句 “select a from t1 union select b from t2 limit3 ”需要被解析成:
而不是 :
“(select a from t1) union (select b from t2 limit 3)”
这个问题目前我没有太完美的解决它,一个折中的方案是 union后面的那个select语句必须是带括号的select语句,即select_with_parens。
以下是具体的select语法定义:
...
%right
%left
%left
...
select_stmt: select_no_parens
select_union: select_stmt UNION opt_all select_with_parens opt_groupby opt_having opt_orderby opt_limit %prec UNION;
select_with_parens: '(' select_no_parens ')'
subquery_stmt_no_parens: select_no_parens;
subquery_stmt: select_stmt;
select_no_parens: select_query
select_query: SELECT
{
} query_part
query_part: select_opts select_expr_list opt_limit
....
expr: expr IN '(' subquery_stmt ')';
....
转载请注明出自高孝鑫的博客。