CUBRID的语法分析和检测

本文介绍了CUBRID数据库系统中SQL查询语句的解析流程,包括词法分析、语法分析及语义检测三个关键步骤,并提供了具体实现的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作者:王东

 

就像是我们使用编译器编译c, cpp文件一样, 对查询语句的编译过程也分为了如下几部分:

l  词法分析;

l  语法分析;

l  语义分析和检测;

l  查询重写;

l  生成查询计划;

l  挑选和优化查询计划;

 

这里主要是针对 前三步 进行说明。

 

当我们输入一个SQL语句时, CUBRID的查询编译器会按照类似代码编译器的方式进行工作。

1.         首先进行词法分析,词法分析的目标是将SQL语句进行扫描并分割成为一系列记号(Token),在CUBRID,是使用Flex生成的文件进行词法分析的。Flex(fast lexical analyser generator)是一个自动的词法分析器生成器(参考附1)。在CUBRID中,有一个描述SQL词法的.l 文件,该文件描述了SQL语句中用的词汇。通过使用Flex,并输入.l 文件,将生成对应的可以用的编译的.c文件。词法分析的结果产生了各种记号(Token), 例如:关键字,标识符,字符串等等;

2.         接下来进行语法分析。语法分析是将词法分析产生的记号(Token)按照语法要求生成语法树(Syntax Tree)CUBRID 中采用bison来进行语法分析(参考附录2)。类似于编译器中的表达式一样,不同的SQL语句,将被看作为不同表达式(Expression)CUBRID中有一个描述语法的.y文件,该文件中包含表达式树是如何组织起来的代码。通过bison,并输入.y文件,生成对应可以用于编译的.c.h文件。这样SQL就被组织成语法树的结构了。

3.         接下来就是对语法树进行语义检测(Semantic analysis and check)。因为语法分析中只是完成了对表达式语法层面的分析,并没有对真正这条语句的内容是否合法进行检测。 CUBRID 里面的Semantic check, 是对生成的语法树进行遍历和检查。由于树的节点不同,对每个节点的遍历算法也是不同的。语义检测的结果是对不满足输入的SQL语句进行过滤(提示报错),对语法树进行适当的扩充和删减,便与接下来的生成查询计划等工作。

 

接下来就进行查询重写,生成查询计划其他了操作。这里暂且不提。

下面就这三部分给出CUBRID中实现的三个例子。

 

例子1  CUBRID词法分析

.l 文件是由正则表达式和c语法组成的。

//定义段代码

%{

//定义include文件,宏,辅助函数,flex会跳过对这些的处理。

#include "csql_grammar.h"

#include "parse_tree.h"

#define CSQL_MAXNAME 256

static int parser_yyinput_single_line(char* buff, int max_size);

 

extern int yyline;

//……

%}

// 词法规则段代码

%%

[sS][eE][lL][eE][cC][tT]    { begin_token(yytext);   return SELECT; }

([a-zA-Z_#]|(/xa1[/xa2-/xee/xf3-/xfe])|([/xa2-/xfe][/xa1-/xfe])|(/x8e[/xa1-/xfe]))([a-zA-Z_#0-9]|(/xa1[/xa2-/xfe])|([/xa2-/xfe][/xa1-/xfe])|(/x8e[/xa1-/xfe]))*

{ begin_token(yytext);

                     if (strlen(yytext) >= 254)

                         yytext[254] = 0;

                     csql_yylval.cptr = pt_makename(yytext);

                     return IdName;

                  }

%%

通过编辑.l文件,我们可以定义我们感兴趣的token

生成的c代码,如下:

case 338:

YY_RULE_SETUP

#line 537 "../../src/parser/csql_lexer.l"

{ begin_token(yytext);   return SELECT; }

    YY_BREAK

 

 

例子2 CUBRID语法分析

.y 文件定义语法树表达方式, 其中包含了Token, rule type, rule的组织方式. 例子中,select 语句的节点中就包含很多内容。

 

/* Token define */

/*{{{*/

%token SELECT

%token IdName

/* define rule type (node) */

/*{{{*/

%type <node> stmt_

%type <node> create_stmt

%type <node> select_stmt

 

stmt_

    : create_stmt

       { $$ = $1; }

    | select_stmt

       { $$ = $1; }

    ;

 

select_stmt

    : SELECT                     /* $1 */

       {{                       /* $2 发现是select语句,就创建select节点*/

           PT_NODE *node = parser_new_node (this_parser, PT_SELECT);

           

       DBG_PRINT}}

   

    select_list                  /* $5 */

       {{                       /* $6 创建select 选项的列表节点*/

           PT_NODE *node = parser_top_select_stmt_node ();

           if (node)

             {

               node->info.query.q.select.list = $5;

             }

       DBG_PRINT}}

    opt_select_param_list       /* $7 */

    FROM                         /* $8 */

   

    opt_where_clause             /* $11 where语句对应的节点*/

   

    opt_groupby_clause           /* $14 group语句对应的节点*/

    opt_having_clause            /* $16 having语句对应的节点*/

   

       {{

           if (node)

             {

               node->info.query.into_list = $7;    /* param_list */

               node->info.query.q.select.where = $11;

               node->info.query.q.select.group_by = $14;

               node->info.query.q.select.having = $16;

             

             }

           $$ = node;

DBG_PRINT}}

    ;

}}

 

opt_where_clause

    : /* empty */

       {{

           $$ = NULL;

       DBG_PRINT}}

    | WHERE search_condition 

       {{

           parser_restore_ic ();

           parser_restore_sysc ();

           parser_restore_prc ();

           parser_restore_cbrc ();

           $$ = $2;

       DBG_PRINT}}

    ;

 

看到这里大家应该明白了,这种类似递归的方式传递构造语法树的过程。最终构建的是一个以select节点为根节点的语法树。其中包含了q.select.list,wheregroup_by,having 等节点。

 

 

例子3 CUBRID语义检测

语法检测阶段拿到的就是一个语法树的指针。在CUBRID中这个语法树就是一个PT_NODE。该结构是包含了许多类型的节点,例如创建类型,select类型等等。PT_NODE 的类型和info 是匹配的。例如:如果Type = PT_CREATE_ENTITY, Info 则是PT_CREATE_ENITITY_INFO结构体。

其中PT_NODE类型的节点用于记录节点的类型,PT_XXX_INFO的节点是用于存储信息,里面可以包含数据,也可以再包含PT_NODE节点。

这样整个语法树就递归起来。每个节点本身是PT_NODE, 每个节点的Info 里面可以包含PT_NODE

最终整个语法树的叶子节点必然是一个PT_NODE, 它的Info中不包含任何PT_NODEPT_NODE节点。

PT_NODE 在内存中的布局就类似如下:

 

 

简单的语义检测 可以直接操作这个语法树,例如CUBRIDpartition table by Hash, 其中partition的个数不能超过1024个。

 

复杂的语言检测有时候需要遍历语法树,可以采用递归的深度优先算法进行遍历。

CUBRID的遍历语法树的算法的主要逻辑是:

1 刚进入节点时:处理 进入前回调函数:

2 根据PT_NODE 调用自己的节点对应的 遍历函数;

    通常遍历函数,就是依次遍历该节点包含的子节点,由于节点不同,每个节点的子节点个数都不一样。

    根据需要遍历Next节点等等;

    3 离开节点时: 处理 离开前回调函数

 

例如:检测遍历PT_NODE中提到的table都是必须数据库中存在的。可以遍历语法树,关注type=PT_SPEC的节点即可。

 

到此为止,相信大家已经明白CUBRID语法分析和检测的过程了。如果感兴趣的,请查看我们的开源项目CUBRID源代码(参考1).

 

 

附录:

1:什么是Flex?

Flex是一个生成扫描器的工具,能够识别文本中的词法模式。Flex读入给定的输入文件,该文件为需要生成的扫描器的描述。此描述叫做 规则,由正则表达式和 C代码对组成。当运行可执行文件的时候,它分析输入文件,为每一个正则表达式寻找匹配。当发现一个匹配时,它执行与此正则表达式相关的C代码。Flex 不是GNU工程,但是GNUFlex 写了手册。

 

2: 什么是bison?

GNU bison 是属于 GNU 项目的一个语法分析器生成器。Bison 把一个关于“向前查看 从左到右 最右”(LALR) 上下文无关文法的描述转化成可以分析该文法的 C C++ 程序。它也可以为二义文法生成 “通用的 从左到右 最右” (GLR)语法分析器。

Bison 基本上与 Yacc 兼容,并且在 Yacc 之上进行了改进。 此软件的源代码是可自由获得的,在 GPL 下发布。

 

Flex Bison 常常结合一起使用。

 

参考:

1.         CUBRID 官方站点: http://www.cubrid.org

2.         Flex 介绍: http://en.wikipedia.org/wiki/Flex_lexical_analyser

3.         Yacc 介绍: http://zh.wikipedia.org/zh-cn/Yacc

4.         GUN 项目里bison主页: http://www.gnu.org/software/bison/

5.         Bison中文手册: http://blog.youkuaiyun.com/sirouni2003/archive/2005/06/22/400672.aspx

6.         FlexBison入门: http://code.google.com/p/msys-cn/wiki/ChapterFour

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值