淘宝数据库OceanBaseSQL编译器部分源码阅读--解析SQL语法树(四)

2014-11-24 17:02:50 · 作者: · 浏览: 4
date语法结构体中有3个子节点,第一个表示表名,第二个表示要更新列表项,第三个表示更新的条件语句。

示例中的Update语句在OceanBase中可以表示为如下形式:

|--ParseNode

|--type: T_UPDATE

|--num_child: 3

|--children[0]:

|--type: T_IDENT

|--str_value: student

|--children[1]:

|--type: T_ASSIGN_LIST

|--num_child:1

|--children[0]:

|--type: T_ASSIGN_ITEM

|--children[0]:

|--type: T_IDENT

|--str_value: sex

|children[1]:

|--type: T_EXPR

|--children[0]:

|--type: T_STRING

|--str_value: "M"

|--children[2]:

|--type: T_OP_EQ

|--num_child: 2

|--children[0]:

|--type: T_IDENT

|--str_value: name

|--children[1]:

|--type: T_IDENT

|--str_value: "小明"

OceanBase中采用的这种方式缺点很明显,就是使用这些结构体时必须要仔细辨别各个子节点代表的意义,否则容易出错。优点同样也很明显,可以非常灵活的构建出语法树。

语法树的节点的设计,主要是为了解决如何表达语法结构。不同的数据库有不同的具体实现。OceanBase采用终止符和非终止符分类,使用OceanBase的设计极具灵活性,但使用时需要仔细验证,避免出错。

构建语法树

SQL的语法规则很多 , SELECT , INSERT , UPDATE , DELETE , CREATE TABLE 等 dml , ddl 等语句都有对应的语法树。在上一节节中我们已经看到了UPDATE语句在内存中的语法树形状。本节需要些Flex和Bison的基础知识,如果之前没有接触过Flex与Bison的话,可以阅读《Flex与Bison中文版》或者GNU Bison 2.1 中文手册。

SQL全称为结构化查询语言,有独立于各数据库厂家的SQL标准,各数据库基本上都会大体上遵循该标准。像PostgreSQL数据库兼容某些版本的SQL标准,同时也有些自己独有的语句。每个数据库都有自己的擅长之处,这些细微的区别有时候就体现在查询语言的细微区别上。OceanBase在0.3版本仅支持有限的几条SQL语句,按照其官方的介绍,其目标之一是兼容MySQL的语法,让我们拭目以待。

词法分析

利用Flex进行词法分析是构建语法树的第一个。Flex源文件包含3部分:选项定义、词法规则、代码部分。选项定义部分定义了该词法分析器使用的特性和一些自定义的函数。词法规则部分为匹配语法+匹配动作。匹配语法为正则表达式,flex从上往下按顺序对每个规则进行匹配,一旦匹配成功则执行该项后面大括号内对应的动作代码;代码部分则是C语言代码。

OceanBase的词法文件为sql_parser.l。其中定义了一些函数,选取部分来看。

下面这两个函数可以使得OceanBase支持转义字符,包括:\b,\f,\n,\r,\t.

inline unsigned char escaped_char(unsigned char c);

/* quote_type: 0 - single quotes; 1 - double quotation marks */
int64_t parse_string(const char* src, char* dest, int64_t len, int quote_type);

OceanBase中的字符串用双引号括起来的表示,而且需要进行转义处理。

\"(\\.|\"\"|[^"\\\n])*\" {
  ParseNode* node = NULL;
  malloc_new_node(node, ((ParseResult*)yyextra)->malloc_pool_, T_IDENT, 0);
  yylval->node = node;
  char* src = yytext+1;
  int len = strlen(src) - 1; //remove last quote charactor
  char* dest = (char*) parse_malloc(len + 1, ((ParseResult*)yyextra)->malloc_pool_);
  check_value(dest);
  node->str_value_ = dest;
  node->value_ = parse_string(src, dest, len, 1);/*字符串转义处理*/
  return NAME;
}

当匹配到NULL时,返回的值类型不能为NULL,因为NULL是c语言的保留字,所以需要换成其他名字,此处将NULL的类型定义为NULLX。

NULL   {
  /* yylval->node = new_node(((ParseResult*)yyextra)->malloc_pool_, T_NULL, 0); */
  malloc_new_node(yylval->node, ((ParseResult*)yyextra)->malloc_pool_, T_NULL, 0);
  return NULLX;
}

再来看关系符号的规则代码,每个符号分别返回一个值。

"||" {return CNNOP;}
"=" {return COMP_EQ;}
">=" {return COMP_GE;}
">" {return COMP_GT;}
"<=" {return COMP_LE;}
"<" {return COMP_LT;}
"!="|"<>" {return COMP_NE;}

在《Flex与Bison》一书中,作者让所有关系符号返回同一个值,通过yylval的成员值来标记不同符号。代码类型如下这段。

"=" { yylval.subtok = EXPR_EQ; return COMPARISON; }
"<=>"   { yylval.subtok = EXPR_COMP; return COMPARISON; }
">="    { yylval.subtok = EXPR_GE; return COMPARISON; }
">" { yylval.subtok = EXPR_GT; return COMPARISON; }
"<="    { yylval.subtok = EXPR_LE; return COMPARISON; }
"<" { yylval.subtok = EXPR_LT; return COMPARISON; }
"!="    |
"<>"    { yylval.subtok = EXPR_NE; return COMPARISON; }

虽然都能完成相同的功能,但是从个人喜好来讲,我更喜欢OceanBase这种做法,因为够直接。

整数的值保留在node->value_中返回,为long long 类型 ( 64位 ) 。 而浮点数的值字面值则保存在node->str_value_ 中。

接下来,看日期时间的提取。

Date{whitespace} '[0-9]{4}(-[0-9]{2}){2}' {
  int year, month, day;
  struct  tm time;
  int ret = 0;

  /* ParseNode* node = new_node(((ParseResult*)yyextra)->malloc_pool_, T_DATE, 0); */
  ParseNode* node = N