海山数据库(He3DB)源码解读:海山PG 词法、语法分析

背景

   He3DB for PostgreSQL是受Aurora论文启发,基于开源数据库PostgreSQL 改造的数据库产品。架构上实现计算存储分离,并进一步支持数据的冷热分层,大幅提升产品的性价比。

   He3DB for PostgreSQL中查询编译的主要任务是根据用户的查询语句生成数据库中最优执行计划,在此过程中要考虑视图、规则以及表的连接路径等问题。

一、概述

   查询分析中词法分析和语法分析分别借助词法分析工具Lex和语法分析工具 Yacc来完成各自的工作。涉及的主要函数如下所示:

1.exec_simple_query;  
2.   |->pg_parse_query;  
3.      |->raw_parser;  
  1. exec_simple_query函数(在src/backend/tcop/postgres.c下)调用函数pg_parse_query进入词法分析和语法分析的主过程,函数pg_parse_query再调用词法分析和语法分析的入口函数raw_parser生成分析树;
  2. 函数pg_parse_query返回分析树(raw_parsetree_list)给exec_simple_query;

postgres命令的词法分析和语法分析是由Unix工具Lex和Yacc制作的。
  1)Lex为词法分析工具,用来识别一个一个模式,例如数字、字符串、特殊符号等。
  2)Yacc为语法分析工具,用来识别数字、字符串、特殊符号的组合
   它们依赖的文件定义在src\backend\parser下的scan.l和gram.y。
   其中:Lex词法器在文件 scan.l里定义。负责识别标识符,SQL 关键字等,对于发现的每个关键字或者标识符都会生成一个记号并且传递给分析器;
   Yacc分析器在文件 gram.y里定义。包含一套语法规则和触发规则时执行的动作.
在raw_parser函数(在src/backend/parser/parser.c下)中,主要通过调用Lex和Yacc配合生成的base_yyparse函数来实现词法分析和语法分析的工作。

   重要的文件如下:
在这里插入图片描述

   值得一提的是,如果你想修改postgresql的语法,你要关注下两个文件“gram.y”和“kwlist.h”。简要地说就是将新添加的关键字添加到kwlist.h中,同时修改gram.y文件中的语法规则,然后重新编译即可。

   文件间的调用关系,如下图所示:

在这里插入图片描述

二、scan.l与gram.y文件

   词法分析和语法分析主要通过FlexBison配合实现的。Flex负责做sql中关键字的事别,然后转换成token给Bison使用。Bison 根据token,定义语法并匹配语法,转换成parsetree.

   在解析之前,我们先来了解一下 token 的意义,这对我们理解为何要用 Flex 生成一个词法分析器有着莫大意义。当 Flex 读进一个代表词法分析器规则的输入字符串流,然后输出以C语言编写的词法分析器源代码。利用这个词法分析器可以将一个文本中的单词提取出来,而这其实就是提取编程语言占用的各种保留字、操作符等等语言的元素,我们也将这些元素称为令牌(token)。词法分析器一般以函数(yylex()函数)的形式存在,供语法分析器调用。
   令牌可以是关键字、标识符、常量、字符串值,或者是一个符号。例如,下面的 SQL 语句包括五个令牌:

SELECE name,id FROM student;
SELECT name id FROM student ;
token类型 关键字 标识符 标识符 关键字 标识符 终结符
描述 命令 列名 列名 条款 表名 结束语句

   对于一种编程语言来说,token 的类型是有限的。Flex 工具会帮我们生成一个 yylex() 函数,Bison 通过调用这个函数来得知拿到的 token 是什么类型的,而这,便是 scan.l 和 Flex 的关联之处。Flex 的输入文件一般会被命名成 .l 文件,通过 scan.l 我们得到输出的文件是 scan.cpp,yylex() 函数就在这个文件中。这个过程的结构图大概是这样的,yylex() 函数就是我们所说的词法分析器的主体了:
在这里插入图片描述

2.1 scan.l文件

Flex 文件即scan.l,由%%分为三段:
定义段:此段可以通过%{… %}包含C的代码,这部分代码也会被原样copy到生成的C文件中,还有一些参数项通过%option来设置,还有一些代理项,通过代理可以简化规则段的书写。
规则段:此段的规则会对应到生成的yylex函数中。
代码段:此段被原样copy到生成的C源码文件中即scan.c。

2.1.1 定义段
  1. %top{ ... } 这部分,括号中内容将被原样copy到生成的scan.c中,并且位于C文件的最顶部。此部分的作用就是加入一些此文件描述注释,以及需要include的头文件。

  2. %{ ... %}这部分是的代码会被原样copy到生成的C文件中。在这里可以重定义一些Flex中的宏,如YYSTYPE, 以及一些在规则段使用的函数声明,和结构体声明和定义。

  3. %option此部分是Flex支持的一些参数,通过%option 来设置
    %option reentrant 可重入词法分析器,传统词法分析器只能一次处理一个输入流,所以很多变量都定义的为静态变量这样分析器才能记住上次分析的地方继而可以继续分析。但是不能同时处理多个输入流。为了解决这个问题引入了可重入词法分析器。通过参数reentrant来控制。这样通过一个yyscan_t类型变量来保存中间状态。yyscan_t为void ,但是在yylex_init初始化的时候是分配了sizeof(yyguts_t)大小的空间,yyguts_t 是一个结构体,保存了所有需要记录的中间的状态。通过这个结构体里的变量替换了原来的静态变量。即在yylex中这个yygut_t类型的变量被传入,这样就通过这个结构体保存了所有原来很多静态变量保存的值。实现了多个输入流同时被分析。只要针对每个输入流,创建一个yyscan_t类型的scanner.传入到yylex中。
    %option prefix="core_yy" 通过加入前缀,可以将原来的yylex等函数 变成core_yylex.这样可以在一个程序中建立多个词法分析器。用来分析不同的输入流。
    %option bison-bridge ,bison桥模式,为什么会有这个模式呢,因为bison的发展和flex的发展沟通并不是很密切,导致了一个不好的情况,即在bison调用yylex的时候是yylex(YYSTYPE yylvalp); 即必须传入一个yylval的指针,但是flex中定义的yylex函数为int yylex(yyscan_t scaninfo).这样两者就不一样了。就无法互相协作的工作了。所以在flex中提拱了桥模式,如果按%option bison-bridge做了声明,那么在flex中yylex将被声明为int yylex(YYSTYPE lvalp, yyscan_t scaninfo);这样就兼容了bison。
    %option bison-locations 此模式同上面参数同时使用,如果做了此声明,yylex 将被声明为int yylex (YYSTYPE
    lvalp, YYLTYPE* llocp, yyscan_t scaninfo);加入了location参数。而在flex中yylex 中宏yylval 和 yylloc其实就是lvalp 和llocp的一个拷贝。

  4. %x定义 开始状态,开始状态代表进入一个特定的状态,在规则段只有定义了特定状态的规则才会匹配,这种规则通
    < start stat >来标识。例如 定义段定义了 %x xb 则在规则段只有开头的规则才会匹配,其他的的规则则不会被匹配。

  5. 代理器, 代理器可以为一些要匹配的表达式命名,这样在规则段可以用这个代理名子,来代替这个表达式。例如space [ \t\n\r\f],给[ \t\n\r\f] 命名为space, 后面在规则段即可使用{space}来代替[ \t\n\r\f],同时代理也可以被嵌套,如whitespace ({space}+|{comment}) 这里定义了新的代理whitespace, 它代理了 ({space}+|{comment}) 其中{space}就被嵌套代理了。

2.1.2 规则段
{
   whitespace}    {
   
                    /* ignore */
                } //这里的意思是凡是whitespace, 则忽略

{
   xcstart}        {
    //这里的意思是当匹配xcstart时,要做后面的操作。
                    /* Set location in case of syntax error in comment */
                    SET_YYLLOC();
                    xcdepth = 0;
                    BEGIN(xc);
                    /* Put back any characters past slash-star; see above */
                    yyless(2);
                }

<xc>{
   xcstart}    {
   
                    xcdepth++;
                    /* Put back any characters past slash-star; see above */
                    yyless(2);
                }
  1. 通过{代理器} 来表示要匹配的字符, 后面跟着{ ... }大括号中的内容就是要生成的C代码,即前面的字符配匹配后,则会对应调用后面大括号内的C代码。同时后面大括号内容可以为空,即表示忽略这个匹配的字符。这些规则将被生成scan.c中的yylex函数。

  2. BEGIN, Flex 初始状态为 INITIAL, BEGIN (start stat) 意思是开始进入一个新的状态. 进入这个状态后,只有定义了对应状态的规则才会被匹配。例如:
    上面调用BEGIN(xc)之后,即匹配xcstart之后,进入xc状态,所以只有{xcstart} { … }等开头带的代理器才会被匹配。其他的任何规则都不会被匹配。

  3. yyless(), 此函数意思是保留参数个数的字符流,其他的返回给输入流。但是匹配的字符流长度,为匹配的总长度。例如:
    如果yyless(2),这个时候匹配的字符是5个字符,则保留前两个字符,后面三

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值