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

2014-11-24 17:02:50 · 作者: · 浏览: 7
ULL; malloc_new_node(node, ((ParseResult*)yyextra)->malloc_pool_, T_DATE, 0); char* dest = strchr(yytext, '\''); dest = parse_strdup(dest + 1, ((ParseResult*)yyextra)->malloc_pool_); // skip left quote check_value(dest); size_t len = strlen(dest); dest[len - 1] = '\0'; //remove final ' node->str_value_ = dest;//字面值 ret = sscanf(dest, "%4d-%2d-%2d", &year, &month, &day); assert(ret == 3); memset(&time, 0, sizeof(struct tm)); time.tm_year = year - 1900; time.tm_mon = month - 1; time.tm_mday = day; time.tm_hour = 0; time.tm_min = 0; time.tm_sec = 0; time.tm_isdst = -1; node->value_ = mktime(&time) * 1000000L;//转成微秒 yylval->node = node; return DATE_VALUE; }

从表达式中可以看到,OceanBase支持直接传入日期时间,OceanBase也支持Time和Timestamp类型。具体如下表:

类型 格式(不区分大小写) 表达式
Date Date 'YYYY-MM-DD' Date{whitespace} '[0-9]{4}(-[0-9]{2}){2}'
Time Time 'HH:MI:SS.FF'或Time 'HH:MI:SS Time{whitespace} '[0-9]{2}(:[0-9]{2}){2}[.][0-9]{1,6}',Time{whitespace} '[0-9]{2}(:[0-9]{2}){2}[.] '
Timestamp Timestamp 'YYYY-MM-DD HH:MI:SS.FF'或 Timestamp 'YYYY-MM-DD HH:MI:SS' Timestamp{whitespace} '[0-9]{4}(-[0-9]{2}){2}[ ][0-9]{2}(:[0-9]{2}){2}[.][0-9]{1,6}',Timestamp{whitespace} '[0-9]{4}(-[0-9]{2}){2}[ ][0-9]{2}(:[0-9]{2}){2}[.] '

如下是一个使用日期的示例:

select id,name from student where createtime <= date '2014-09-12';

语法树中存在日期类型的节点,其str_value_存储时间的字面值,value_存储该时间与基准时间(1970年1月1日0时0分0秒)之差的毫秒值。

OceanBase通过拓展标准的SQL语法支持直接在SQL语句中写入时间 ,在词法分析阶段就识别到时间类型,而 PostgreSQL 是在语法分析阶段才会识别时间 。 相比而言 , OceanBase 这种方式更直观,方便。

在0.4版本的OceanBase中,增加了预编译的功能,需要识别占位符" ",系统变量和用户变量。对应性质如下:

名称 表达式 动作代码返回值
占位符( ) " " QUESTIONMARK
系统变量(system_variable) (@@[A-Za-z_][A_Za-z0-9_]*) SYSTEM_VARIABLE
用户变量(temp_variable) (@[A-Za-z_][A_Za-z0-9_]*) TEMP_VARIABLE

语法分析

OceanBase的SQL语法文件为sql_parser.y。.y语法文件最终由Bison转为可编译的.c文件。其结构与Flex的.l文件类似,分为选项部分,规则部分和代码部分。下面的代码都是去掉了关联的动作代码的规则语法,这样有助于更专注的理解语法的实现。

select语法

在SQL的语句语法中,最复杂的语法莫过于Select语句。我们先来看其在OceanBase中的语法。一个select语句可以使带括号的select语句,也可以使不带括号的select语句:

select_stmt: 
    select_no_parens    %prec UMINUS    /* 不到括号的select语句 */
  | select_with_parens    %prec UMINUS  /* 带括号的select语句 */
  ;

%prec用于交换左右两个标记的优先级和结合性。即select_no_parens与UMINUS互换优先级和结核性,select_with_parens 与UMINUS互换优先级和结核性。UMINUS的定义为%right UMINUS。所有上面两句的结果是select_no_parens和select_with_parens都变成了右结合。

带括号的select语句可以是只带一个括号,也可以带多对括号:

select_with_parens:
    '(' select_no_parens ')'      /*只带一个括号*/
  | '(' select_with_parens ')'    /*带多对括号*/
  ;

不带括号的select语句包括简单的select,带order by排序,带order by排序和limit限制的select语句3种:

select_no_parens:
    simple_select              
  | select_clause order_by
  | select_clause opt_order_by select_limit
  ;

select子句包括简单slect和带括号的select:

select_clause:
    simple_select                 
  | select_with_parens           
  ;

为什么不包括不带括号的select,暂时没想通。

简单select语句包括我们常见的select 语句,但没有order by和limit选项;同时还包括UNION,INTERSECT,EXCEPT三种运算:

simple_select: 
    SELECT opt_distinct select_expr_list 
    FROM from_list
    opt_where opt_groupby opt_having
  | select_clause UNION opt_distinct select_clause
  | select_clause INTERSECT opt_distinct select_clause
  | select_clause EXCEPT opt_distinct select_clause
  ;

select语句的定义相对其他语句很复杂,而且上面这段代码还没有包括无表的select和for update的select。(这两项在0.4版本的OceanBase中已经实现)。

下面这段是从PostgreSQL9.2