精华内容
下载资源
问答
  • 【SQL】SQL语法树

    万次阅读 2018-04-08 22:32:57
    1. 为什么会出现SQL语法树? 假设有一个SQL语句 select name,age,count(name) as count_name, count(id) as count_id from mytable where id = ? and name = ? 这是一条sql语句,如果你想运行这个语句(下面写...

    在这里插入图片描述

    1. 为什么会出现SQL语法树?

    假设有一个SQL语句

    select name,age,count(name) as count_name,
    count(id) as count_id from  mytable where id = ?  and name = ?
    

    这是一条sql语句,如果你想运行这个语句(下面写伪代码,具体实现肯定不是这样的但是这是人实现的思想)
    1.找到数据库表mytable这里要获取表名简单一个if判断
    2.假设里面有1000条数据,那么我们先获取1000条

    id name 	age  	sex  	birthday
    1	梁川川  	23  	男		2015-04-08
    2	梁川川1  23  	男		2015-04-08
    3	梁川川2  23  	男		2015-04-08
    。。。。。
    

    3.然后根据语句获取我们需要的列,根据name,age,count(name) as count_name,count(id) as count_id获得id name age,这里我们可能需要些很多if去判断

    id name 	age  	
    1	梁川川  	23  	
    2	梁川川1  23  	
    3	梁川川2  23  	
    。。。。。
    

    4.然后开始过滤 ,后面的就不说了

    对于这个问题,开始的设想比较简单,大致过程是:把Sql语句中不相同的关键字和函数名替换掉,如Oracle中的To_Date换成SqlServer的Convert,就可以在SqlServer上执行了.对一些简单的Sql语句这样确实可以,可是对复杂的应用来说,Sql语句可能多层嵌套,函数也有多层嵌套,如果只是简单的替换,代码中必然会有无数的if else,并且出错后的修改和调试几乎是不可能的。

    通过对Oracle和SqlServer两种数据库的Sql语法的研究比较,认为必须采用语法分析,把Sql语句解析为一棵语法树,然后再按照语法的转换规则把sql语句转换到SqlServer上可执行的语句。

    例如一个Select语句的结构如下
      
    这里写图片描述

    从这张结构图中可以看到,Sql语句可能出现非常复杂的组合结构,如果不使用语法树表示,很难实现不同数据库平台的转换。

    案例2

    一则简单的例子
    如果我们需要让计算机帮忙算一下 「1加2再乘以3」 的结果,该怎么表达呢?
    现在我们大多数的现代编程语言,都是使用「中缀表达式」的方式来编写运算,比如JavaScript:

    (1 + 2) * 3
    

    而FORTH语言则使用「后缀表达式」,这基本上与日语中的语序是一致的:

    1 2 + 3 *
    

    LISP语言使用的「前缀表达式」:

    ( * (+ 1 2) 3)
    

    我们再看一下这三种表达式的语法树:

    这里写图片描述

    可以看出,对于这三种简单的语言,它们只是在这个语法树上按不同的规则遍历而已。三者的代码看起来差别很大,但实际上所用的树结构是相同的。

    总结:

    1. 一条sql语句能否在不同的语言和数据库执行,首先都要生成语法树
    2. 语法树是运行的前提,(还记得数据结构中的二叉树吗)

    2.有了Sql语法树,我们可以做什么?

    看到这里你可能会问,知道语法是又有什么用呢?跟我日常编写代码貌似半毛钱关系都没有。其实语法树还是很有用的,想一下如果想做「语法高亮」、「关键字匹配」、「作用域判断」、以及「代码压缩」等等,都是最好把代码解构成语法树之后再去各种操作,当然仅仅解构还不够,还需要提供各种函数去遍历与修改语法树。

    展开全文
  • 淘宝数据库OceanBase SQL编译器部分 源码阅读--解析SQL语法树 OceanBase是阿里巴巴集团自主研发的可扩展的关系型数据库,实现了跨行跨表的事务,支持数千亿条记录、数百TB数据上的SQL操作。在阿里...

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

    OceanBase是阿里巴巴集团自主研发的可扩展的关系型数据库,实现了跨行跨表的事务,支持数千亿条记录、数百TB数据上的SQL操作。在阿里巴巴集团下,OceanBase数据库支持了多个重要业务的数据存储,包括收藏夹、直通车报表、天猫评价等。截止到2013年4月份,OceanBase线上业务的数据量已经超过一千亿条。

    看起来挺厉害的,今天我们来研究下它的源代码。关于OceanBase的架构描述有很多文档,这篇笔记也不打算涉及这些东西,只讨论OceanBase的SQL编译部分的代码。

    OceanBase是一个开源的数据库,托管在github上,点击下载。本文讨论的源码路径对应为:oceanbase_0.3/src/sql。最新的版本为0.4,本文讨论的代码基于0.3版本的OceanBase.目前OceanBase的能解析的SQL还比较少,包括Select,Insert,Update,Delete,Show,Explain等.

    选择OceanBase 0.3 版本进行学习,基于几个原因:

    • OceanBase 的高质量,高可读性
    • OceanBase 版本低,没有历史负担
    • OceanBase SQL解析相对简单,更容易窥见全貌,利于理解设计开发中要解决的主要问题。
    • 与其他数据库的SQL解析部分进行对比,深入理解问题本质

    该部分主要功能包括了,SQL语句解析,逻辑计划的生成,物理操作符运算等。

    入口:ObSql类

    本部分的入口函数在ob_sql.h中,调用函数ObSql::direct_execute可以直接执行SQL语句,并返回结果集ObResultSet。函数stmt_prepare用于解析要预编译的SQL语句,stmt_execute则用于执行Prepare过的SQL语句。

      class ObSql
        {
          public:
            ObSql(){}
            ~ObSql(){}
            int direct_execute(const common::ObString &stmt, ObResultSet &result)
            int stmt_prepare(const common::ObString &stmt, ObStmtPrepareResult &result);
            int stmt_execute(const uint64_t stmt_id, const common::ObArray<common::ObObj> params, ObResultSet &result);
            int stmt_close(const uint64_t stmt_id);
        };
    

    在0.4版本中,direct_execute,stmt_prepare,stmt_execute等函数都被声明为static函数,意味着调用SQL语句执行时可以直接ObSql::direct_execute可以执行SQL语句,而不必再先定义一个ObSql对象。OceanBase还有年轻,还存在不足,我们阅读源码时应该带着批判思考的精神
    直接进入direct_execute函数,可以看到整个执行的过程,函数中有很多的if,else语句,主要是因为OceanBase有一个编码规范要求:一个函数只能有一个返回出口.按单出口的规范写代码会使得写代码的思路非常清晰,不容易出现内存泄露等问题,在大型项目中还是应该尽量保持函数单出口.当然,我觉得保持一个函数功能简洁、简单易懂也是非常重要的。

    在你阅读源码的过程中,遇到的大部分函数都会是这个样.刨去其他干扰信息,结合注释,可以看到,SQL执行分为5个步骤:

    1. 初始化 
      parse_init(&parse_res)
    2. 解析SQL语法树 
      parse_sql(&parse_res, stmt.ptr(), static_cast<size_t>(stmt.length()));
    3. 制定逻辑计划 
      resolve(&logical_plan, parse_res.result_tree_) 
      ObMultiPlan* multi_plan = static_cast<ObMultiPlan*>(logical_plan.plan_tree_);
    4. 生成物理计划: 
      trans.add_logical_plans(multi_plan); 
      physical_plan = trans.get_physical_plan(0)
    5. 执行物理计划: 
      exec_plan->open()

    初始化仅仅是初始化一个缓冲区,可以略过来研究后面关键的4步。

    解析SQL语法树

    像PostgreSQL,MySQl等一样,OceanBase采用lex和yacc系的词法和语法解析工具生成语法树。GNU下的词法和语法工具为Flex和Bison.Flex利用正则表达式识别SQL语句中的所有词语,Bison则根据类似BNF的语法规则识别SQL语义,从而构建语法树。不熟悉Flex与Bison的同学推荐阅读《FLEX与BISON》(貌似我也没找到其他类似的书籍,^_^),里面也有专门的一章讲述利用Flex与Bison实现一个简单的SQL分析器。

    OceanBase的SQL语法树与PostgreSQL更为相似,但是设计上也有很多区别。

    节点设计

    语法树由一系列的节点串连而成。我选取较为简单的Update语句作为示例,下面是一个例句:
    Update student set sex="M" where name="小明";
    其SQL语法树可以表示为:

    |--Update Stmt
      |--Table:student
      |--TargeList:
        |--sex = "M"
      |--Qualifications:    |--name="小明"

    语法解析的作用就是如何用数据结构来表示上面这棵语法树。不同的数据库有不同的方案。为加强对比,我选取了PostgreSQL,RedBase与OceanBase作为参照。

    PostgreSQL语法树的节点设计

    PostgreSQL中每一种语法都会有一个对应的结构体,比如更新语句Update对应的结构体为UpdateStmt:

        typedef struct UpdateStmt
    {
        NodeTag        type;           /* 枚举类型 */
        RangeVar   *relation;        /* 要更新的关系 */
        List       *targetList;        /* 要更新的项 */
        Node       *whereClause;    /* 更新条件 */
        List       *fromClause;        /* optional from clause for more tables */
        List       *returningList;    /* 返回的表达式列表 */
        WithClause *withClause;        /* WITH clause */
    } UpdateStmt;
    

    其中type是个枚举值,表示结构体的类型,在UpdateStmt中为T_UpdateStmt。其他字段分别对应UPdate语句的各个部分,该结构体可以支持更复杂的Update语句。
    PostgreSQL中还有一个基础的结构体:

    typedef struct Node
    {
        NodeTag        type;
    } Node;
    

    用于语法解析的结构体都可以强制转换成Node * 类型。PostgreSQL中传递语法结构体是都会转化成Node *类型,只有在需要明确类型的时候根据type枚举值转换成需要的类型。Node *的作用有的类似于void * ,但是更利于调试。我们也可以简单的认为:诸如UpdateStmt的语法解析结构体们都继承自Node

    由于每个语法对应一个结构体,因此在PostgreSQL中存在很多类似的结构体,包括SelectStmt,InsertStmt,DeleteStmt等。最终这些结构体还会被统一转换成Query结构体。即Query是统一的语法树结构体。
    在PostgreSQL中,示例中的SQL语法树可表示为:

    |--UpdateStmt
      |--type: T_UpdateStmt
      |--relation: student
      |--targetList:
        |--targest[0]:
          |--name: sex
          |--val: "M"
      |--whereClause:
        |--expr: =
          |--left: name
          |--right: "小明"

    RedBase的语法树的节点设计

    RedBase是斯坦福的数据库系统实现这门课程(cs346)的一个项目。RedBase比起PostgreSQL,OceanBase这样的复杂数据库而言,十分的简单。但是其语法树的节点设计与其他数据库不同,因此提出来做对比。
    
    typedef struct node{
       NODEKIND kind;/*枚举类型*/
    
       union{
          /* SM component nodes */
          /* create table node */
          struct{
             char *relname;
             struct node *attrlist;
          } CREATETABLE;
    
         /*此处省略n多个结构体...*/
    
          /* QL component nodes */
          /* query node */
          ...
    
          /* update node */
          struct{
             char *relname;                 /* 关系名 */
             struct node *relattr;          /* 属性 */
             struct node *relorvalue;       /* 修改后值 */
             struct node *conditionlist;    /* 条件列表 */
          } UPDATE;
    
         /*此处省略n多个结构体...*/
       } u;
    } NODE;
    

    RedBase数据库的语法树结构体只有一个,就是NODE,但是这个NODE结构体的声明有150多行(^-^).NODE包括一个枚举类型,作用于PostgreSQL中的type一样。所有的语法结构如UPDATE,SELECT,CREATETABLE等构成巨大的联合体。针对Update语句的结构体包括了关系名,属性,修改后的值,条件列表等字段,显然这种设计只能支持简单的Update语句。
    RedBase采用“巨型”联合体取代PostgreSQL中的多个结构体,免去了类型转换(语法结构体到Node*的转换)。如果把PostgreSQL语法树节点看成是“继承”结构,那么RedBase的语法树节点可以看成是“组合”结构。
    在RedBase中,示例中的SQL语法树可表示为:

    |--NODE:
      |--kind: N_UPDATE
      |--u:UPDATE
        |--relname: student    |--relattr:
          |--kind: N_RELATTR
          |--u:RELATTR
            |--relname: (null)
            |--attrname: sex
            |--relorvalue:
              |--kind: N_RELATTR_OR_VALUE
              |--u:RELATTR_OR_VALUE
                |--relattr: (null)
                |--value:
                  |--kind:N_VALUE
                  |--u:VALUE
                    |--sval = "M"
         |--conditionlist:
           |--kind:N_LIST
           |--u: LIST
             |--next: (null)
             |--curr:
               |--kind: N_CONDITION
               |--u: CONDITION
                 |--lhsRelattr:
                   |--kind: N_RELATTR
                   |--u:RELATTR
                      |--relname: (null)
                      |--attrname: name
                 |--op:
                   |--kind: N_EQ
                 |--rhsRelattr:(null)
                 |--rhsValue:
                   |--kind:N_VALUE  
                   |--u:VALUE                 |--sval = "M"

    OceanBase的法树的节点设计

    OceanBase 的语法树节点结构体也只有一个,该结构体包括一个枚举类型变量type_,和PostgreSQL与RedBase一样,代表该结构体对应的类型。还有两组属性,对应终止符节点,只能使用vakue_和str_value_两个字段,分别对应64位整形值和字符串值;非终止符节点使用最后两个字段,num_child_表示子节点的个数,children_指向子节点数组的首地址。

    typedef struct _ParseNode
    {
      ObItemType   type_;
    
      /* 终止符节点的真实的值 */
      int64_t      value_;
      const char*  str_value_;
    
      /* 非终止符节点的孩子节点*/
      int32_t      num_child_; /*孩子节点的个数*/
      struct _ParseNode** children_; 
    
      // BuildPlanFunc m_fnBuildPlan;
    } ParseNode;
    

    对应一个节点而言,要么是终止符节点要么是非终止符节点,它只会使用两组属性中的一组。int,long,float,double,string等都是终止符类型,可以看出int,long都是用64位整形int64表示。float,double,string则用char *字符串表示。终止符的num_child_为0,children_为null.

    PostgreSQL的子节点都是有名字的子节点,可以使用名字进行访问,如在PostgreSQL中,Update语句的where条件可以通过 updatestmt.whereClause 来访问 。 但在OceanBase中不行 , 所有的子节点都是匿名的 , 只能通过下标来访问。

    打个比方,在PostgreSQL和RedBase中,孩子是有名字的,可以叫小明、小红等,根据名字你大概可以知道这个孩子是男是女;但是在OceanBase家,他们分别叫老大,老二,老三,听名字完全听不出是男是女的。OceanBase家有点不讲究^-^。

    可以在运行时查看语法树的结构,也可以在代码中可以推各个子节点代表的类型,但是不如PostgreSQL和RedBase方便。在sql_parser.y文件中,定义了SQL的语法规则,同时也规定了各种类型的子节点的结构。

    update_stmt: 
        UPDATE relation_factor SET update_asgn_list opt_where
        {
          ParseNode* assign_list = merge_tree(result->malloc_pool_, T_ASSIGN_LIST, $4);
          $$ = new_non_terminal_node(result->malloc_pool_, T_UPDATE, 3, $2, assign_list, $5);
        }
      ;
    

    从上述代码可以看出,Update语法结构体中有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 = NULL;
      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.3中截取的Select语法:

    SelectStmt: select_no_parens      %prec UMINUS
          | select_with_parens    %prec UMINUS
        ;
    
    select_with_parens:
          '(' select_no_parens ')'       
          | '(' select_with_parens ')'   
        ;
    select_no_parens:
          simple_select           
          | select_clause sort_clause
          | select_clause opt_sort_clause for_locking_clause opt_select_limit
          | select_clause opt_sort_clause select_limit opt_for_locking_clause
          | with_clause select_clause
          | with_clause select_clause sort_clause
          | with_clause select_clause opt_sort_clause for_locking_clause opt_select_limit
          | with_clause select_clause opt_sort_clause select_limit opt_for_locking_clause
        ;
    
    select_clause:
          simple_select             
          | select_with_parens      
        ;
    simple_select:
          SELECT opt_distinct target_list
          into_clause from_clause where_clause
          group_clause having_clause window_clause
          | values_clause             
          | TABLE relation_expr
          | select_clause UNION opt_all select_clause
          | select_clause INTERSECT opt_all select_clause
          | select_clause EXCEPT opt_all select_clause
        ;
    

    不知道你是不是有种似曾相识的感觉。没错,在这里,OceanBase借鉴了PostgreSQL的语法分析方式。PostgreSQL号称世界上最优秀的开源数据库,绝非浪得虚名。

    update语法

    为保证示例的完整性,我们在来分析下update的语法。

    /*0.3版本的OceanBase*/
    update_stmt: 
        UPDATE relation_factor SET update_asgn_list opt_where
      ;
    
    update_asgn_list: 
        update_asgn_factor
      | update_asgn_list ',' update_asgn_factor
      ;
    
    update_asgn_factor:
        NAME COMP_EQ expr
      ;
    

    上面为0.3版本中的update的语法,支持常见的update的SQL语句,如果将它和0.4版本的update的语法相比,就会发现:0.4版本的update在set时可以使用保留关键字作为列名,支持hint语法,支持when子句。随着OceanBase逐渐完善,其解析的语法也必然会越来越复杂。在学习的时候,从一个简单的版本开始,由易到难,就显得很重要。

    /*0.4版本的OceanBase*/
    update_stmt:
        UPDATE opt_hint relation_factor SET update_asgn_list opt_where opt_when
      ;
    update_asgn_factor:
        column_name COMP_EQ expr
      ;
    column_name:
        NAME
      | unreserved_keyword
      ;
    opt_when:
        /* EMPTY */
      | WHEN expr
      ;
    

    除了select,比较复杂还有create table,alter table,expr等。本节最后只讨论表达式的语法问题。其他的不再讨论。

    表达式语法

    expression 是指表达列名或者是一个能够给出真假的布尔表达式。如
    a+3 > b
    a is not null
    等等。表达式最常出现在where子句中,用于过滤数据行。

    表达式的语法之所以复杂,是因为表达式可以嵌套多个子表达式,多个子表达式出现后可能会出现语法上的歧义。BETWEEN ... AND 用于测试给的表达式是否在给定的范围内,这个操作符中的AND 与逻辑与的AND有歧义。需要使用%prec来改变其优先级和结合性,该方法能奏效,但是PostgreSQL和OceanBase都选择从语法树规则上完全消除该歧义。

    OceanBase中表达式有3种类型,分别为expr , arith_expr , simple_expr。其中

    • expr :expr为表达式的核心,是不受约束的表达式,可以直接用于where,having等子句中。
    • arith_expr :arith_expr是可以用于between ... and之间的表达式,是受约束的表达式。因此arith_expr是expr的一个真子集。
    • simple_expr :simple_expr是简单表达式,不能直接进行自我递归的表达式。是expr和aritharith_expr的共有的部分。
    expr_const:
        STRING 
      | DATE_VALUE 
      | INTNUM 
      | APPROXNUM 
      | BOOL 
      | NULLX 
      | QUESTIONMARK 
      | TEMP_VARIABLE 
      | SYSTEM_VARIABLE 
      | SESSION_ALIAS '.' column_name 
      ;
    
    simple_expr:
        column_ref
      | expr_const
      | '(' expr ')'
      | '(' expr_list ',' expr ')'
      | case_expr
      | func_expr
      | when_func
      | select_with_parens        %prec UMINUS
      | EXISTS select_with_parens
      ;
    
    /* used by the expression that use range value, e.g. between and */
    arith_expr:
        simple_expr   
      | '+' arith_expr %prec UMINUS
      | '-' arith_expr %prec UMINUS
      | arith_expr '+' arith_expr 
      | arith_expr '-' arith_expr 
      | arith_expr '*' arith_expr 
      | arith_expr '/' arith_expr 
      | arith_expr '%' arith_expr 
      | arith_expr '^' arith_expr 
      | arith_expr MOD arith_expr 
    
    expr:
        simple_expr   
      | '+' expr %prec UMINUS
      | '-' expr %prec UMINUS
      | expr '+' expr 
      | expr '-' expr 
      | expr '*' expr 
      | expr '/' expr 
      | expr '%' expr 
      | expr '^' expr 
      | expr MOD expr 
      | expr COMP_LE expr 
      | expr COMP_LT expr 
      | expr COMP_EQ expr 
      | expr COMP_GE expr 
      | expr COMP_GT expr 
      | expr COMP_NE expr 
      | expr LIKE expr 
      | expr NOT LIKE expr 
      | expr AND expr %prec AND
      | expr OR expr %prec OR
      | NOT expr
      | expr IS NULLX
      | expr IS NOT NULLX
      | expr IS BOOL
      | expr IS NOT BOOL
      | expr IS UNKNOWN
      | expr IS NOT UNKNOWN
      | expr BETWEEN arith_expr AND arith_expr        %prec BETWEEN
      | expr NOT BETWEEN arith_expr AND arith_expr      %prec BETWEEN
      | expr IN in_expr
      | expr NOT IN in_expr
      | expr CNNOP expr
      ;
    

    simple_expr为简单表达式,并不是说它很简单,因为simple_expr也包含了select_with_parens(带括号的select语句)等语句,只能说simple_expr不能像arith_expr和expr等一样递归调用。

    expr:expr BETWEEN arith_expr AND arith_expr        %prec BETWEEN
      | expr NOT BETWEEN arith_expr AND arith_expr      %prec BETWEEN
      ;
    

    arith_expr用于between ... and 之间,只包含了算术表达式,没有is,like 以及比较关系表达式。

    expr,arith_expr,simple_expr实际上等同于PostgreSQL中的a_expr,b_expr,c_expr。

    总之,在语法树规则和词法分析方面,OceanBase大量的借鉴了PostgreSQL。

    节点的创建

    确定了节点的结构和语法树的形状,就到了实际创建语法树的过程。通常,1条SQL语句的语法树存在多个节点,数据库中要高效的执行多条语句,就会遇到大量的节点创建问题。如果每次创建都调用new/malloc等系统函数必然会浪费大量的时,而且还会造成内存碎片。这种问题常常会出现在现实中,各个数据库都有自己不同的解决办法,但基本原理是一次性向系统申请较大的内存,然后在需要的时候自行分配。
    接下来我将对比PostgreSQL,RedBase,OceanBase 3个数据库是各自如何解决这一问题的。

    PostgreSQL创建语法树节点

    PostgreSQL使用两个宏完成节点的创建,即makeNode和newNode.

    #define makeNode(_type_)  ((_type_ *) newNode(sizeof(_type_),T_##_type_))
    
    /* 针对gcc版本的newNode */
    #define newNode(size, tag) \
    ({    Node *_result; \
        AssertMacro((size) >= sizeof(Node));/* 检测申请的内存大小,>>=sizeof(Node) */ \
        _result = (Node *) palloc0fast(size); /* 申请内存 */ \
        _result->type = (tag); /*设置TypeTag */ \
        _result; /*返回值*/\
    })
    
    #define palloc0fast(sz) \
        ( MemSetTest(0, sz) ? \
            MemoryContextAllocZeroAligned(CurrentMemoryContext, sz) : \
            MemoryContextAllocZero(CurrentMemoryContext, sz) )
    

    注意:要避免直接使用newNode来创建节点,因为节点的大小在不同的环境下可能是不同的。使用makeNode即可,如:
    Stmt *s = makeNode(Stmt);

    我们知道,PostgreSQL中继承自NODE*的结构体们各自大小不一,都大于等于NODE 的大小。newNode中使用palloc0fast申请内存,它仍然是个宏,最终调用的是PostgreSQL中的MemoryContextAllocZeroAligned或者MemoryContextAllocZero函数,这两个函数来自PostgreSQL中内存管理模块--内存上下文,该模块能够很好的应对内存碎片,向数据库进程提供缓冲区管理。

    RedBase创建语法树节点

    RedBase没有提供向PostgreSQL一样复杂的内存管理机制,而是采用简单的方法来解决频繁创建节点的问题。因为RedBase中语法树的节点都是NODE类型的,所有RedBase使用一个NODE的数组来作为缓冲区,默认最大值为100,超过这个值就会创建出错,并最终退出程序。如果你的SQL语句需要的节点超过100,需要在编译之前修改最大节点数目MAXNODE以适应需求。

    #define MAXNODE        100 //最大节点数目
    
    static NODE nodepool[MAXNODE];
    static int nodeptr = 0;
    
    /*
     * newnode: allocates a new node of the specified kind and returns a pointer
     * to it on success.  Returns NULL on error.
     */
    NODE *newnode(NODEKIND kind)
    {
        NODE *n;
    
        /* if we've used up all of the nodes then error */
        if(nodeptr == MAXNODE){
        fprintf(stderr, "out of memory\n");
        exit(1);
        }
    
        /* get the next node */
        n = nodepool + nodeptr;
        ++nodeptr;
    
        /* initialize the `kind' field */
        n -> kind = kind;
        return n;
    }
    

    OceanBase创建语法树节点

    OceanBase创建语法树节点的方式与PostgreSQL类似,上层调用,底层有负责内存池管理的模块来负责真正的创建,不过不是内存上下文,而是一个类ObStringBuf.

    OceanBase的new_node函数申请内存时调用的是parse_malloc函数,该函数专用于语法树结构体的创建,从obStringBuf中获取内存,直到obStringBuf线程退出时才会真正释放这些内存。

    大型的数据库都会有专门的内存管理模块来负责语法程序运行过程中的内存申请和释放,小型的数据库则结合自身特点,量身定制轻量的内存管理机制。目的都只有一个:降低频繁创建和销毁语法树节点的开销

    总结

    SQL的执行包括了解析语法树,制定逻辑查询计划,执行物理查询计划等步骤。通过对比3个不同的数据库的源代码,得出在解析语法树中遇到的一些共性问题:

    • 如何表示一个语法树的节点
    • 利用Bison定义语法树的结构时的一些难点,如select,expr等
    • 如何降低频繁创建和销毁语法树节点的开销

    如果你要自己来写一个SQL解析程序的话,这些问题同样难以避免。

    展开全文
  • 简单写了个小工具,用来获取SQL中的数据来源表 代码如下: package com.app.utils; import java.util.ArrayList; import java.util.List; import java.util.Map; import com.alibaba.druid.sql.SQLUtils; import ...

    简单写了个小工具,用来获取SQL中的数据来源表
    代码如下:

    package com.app.utils;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    import com.alibaba.druid.sql.SQLUtils;
    import com.alibaba.druid.sql.ast.SQLStatement;
    import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;
    import com.alibaba.druid.stat.TableStat;
    import com.alibaba.druid.util.JdbcConstants;
    
    public class AnalysisSQL {
        public static List<String> analysisSQL(String sql){
            if(sql == null || sql.length() < 1) return null;
            List<String> res = new ArrayList<>();
            String dbType = JdbcConstants.MYSQL;
    
            //格式化sql
            String result = SQLUtils.format(sql, dbType);
            List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
    
            //循环执行多个语句
            for (int i = 0; i < stmtList.size(); i++) {
                SQLStatement stmt = stmtList.get(i);
                MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
                stmt.accept(visitor);
                //获取SQL中的表名和操作方法名的map
                for (Map.Entry<TableStat.Name, TableStat> entry : visitor.getTables().entrySet()) {
                    res.add(entry.getKey().toString());
                }
            }
            return res;
        }
    }
    

    maven依赖

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.0.17</version>
       </dependency>
    
    展开全文
  • sql语法树构建

    2014-04-18 17:07:53
    现在想做一个sql语法树,然后能够匹配输入的sql语句,不知道怎么下手,希望各位大侠帮忙指导指导
  • C++ SQL生成语法树

    2011-06-08 23:55:52
    对用户输入的sql语句进行解析,生成语法树,用到了一些设计模式相关的东西,还不错,具体实现了对select 的解析,大家有兴趣一起学习下
  • sql抽象语法树 我已经订阅了jOOQ竞争数据库抽象工具的各种用户组。 其中之一是ActiveJDBC ,这是Active Record设计模式的Java实现。 它的维护者Igor Polevoy最近声称: SQL注入是一个Web应用程序问题,与ORM没有...

    sql抽象语法树

    我已经订阅了jOOQ竞争数据库抽象工具的各种用户组。 其中之一是ActiveJDBC ,这是Active Record设计模式的Java实现。 它的维护者Igor Polevoy最近声称:

    SQL注入是一个Web应用程序问题,与ORM没有直接关系。 ActiveJDBC将处理传递给它的任何SQL。

    (请参阅此处的讨论: https : //groups.google.com/d/topic/activejdbc-group/5D2jhWuW4Sg/discussion


    是真的吗 数据库抽象层是否应该将SQL注入预防委托给客户端应用程序?  

    SQL注入背景

    SQL注入是我们大多数开发人员在职业生涯中不得不一次或多次处理的问题。 维基百科很好地解释了这个问题 给定以下一段Java代码(或任何其他语言):

    statement = "SELECT * FROM users WHERE name = '" + userName + "';"

    假设“ userName”是从HTTP请求中获取的变量。 盲目粘贴HTTP请求参数可以让攻击变得简单,如下所示:

    -- attacker sends this code in the userName field:
    userName = "a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't"
    
    -- resulting in the following statement:
    statement = "SELECT * FROM users WHERE name = 'a';"
              + "DROP TABLE users;" +
              + "SELECT * FROM userinfo WHERE 't' = 't';"

    这不是发生在你身上吗?

    也许不会。 但是问题经常在Stack Overflow上看到。 搜索“ SQL注入”时有2000多个结果: http : //stackoverflow.com/search?q= sql+injection。 因此,即使您知道如何预防,您团队中的某人也可能不会。 好的但是 …

    如果500个语句中的一个语句被某个忽略了这种威胁的程序员糟糕地写了,那还不那么糟糕吗?

    再想一想。 您是否听说过称为sqlmap的工具? 根据注入问题的严重程度,此工具将在几秒钟/几分钟/几小时内找到应用程序中任何有问题的页面。 不仅如此,一旦发现有问题的页面,它就能从数据库中提取所有类型的数据。 我的意思是所有数据。 多种sqlmap功能:

    • 支持枚举用户,密码哈希,特权,角色,数据库,表和列
    • 支持搜索特定数据库名称,所有数据库中的特定表或所有数据库表中的特定列 例如,这对于识别包含自定义应用程序凭据的表很有用,其中相关列的名称包含诸如name和pass之类的字符串。
    • 当数据库软件是MySQL,PostgreSQL或Microsoft SQL Server时,支持从数据库服务器基础文件系统下载和上传任何文件
    • 当数据库软件为MySQL,PostgreSQL或Microsoft SQL Server时,支持在数据库操作系统底层操作系统上执行任意命令并检索其标准输出


    是! 如果您遭受SQL注入不安全代码的困扰,在某些情况下攻击者可以抢占您的服务器! 在我们公司中,我们已经在沙盒环境中针对某些已知漏洞的开源博客软件尝试了sqlmap。 我们已经成功地立即占领了服务器,而没有编写任何一行SQL  

    数据库抽象和SQL注入

    好吧,现在我引起了您的注意,让我们再考虑一下Igor Polevoy所说的话:

    SQL注入是一个Web应用程序问题,与ORM没有直接关系。 ActiveJDBC将处理传递给它的任何SQL。

    是的,他可能是正确的。 鉴于ActiveJDBC是JDBC的瘦身包装,因此可以进行一些不错的CRUD简化,例如(从其网站获取):

    List<Employee> people =
    Employee.where("department = ? and hire_date > ? ", "IT", hireDate)
            .offset(21)
            .limit(10)
            .orderBy("hire_date asc");

    您发现SQL注入的风险了吗? 对。 即使它为基础的PreparedStatement使用绑定值,此工具也像JDBC一样不安全。 如果小心,可以避免SQL注入。 或者,您可以开始串联字符串。 但是您必须意识到这一点! jOOQ如何处理此类情况? jOOQ手册介绍了如何显式或隐式处理绑定值 这里有些例子:

    // Implicitly creating a bind value for "Poe"
    create.select()
          .from(T_AUTHOR)
          .where(LAST_NAME.equal("Poe"));
    
    // Explicitly creating a (named) bind value for "Poe"
    create.select()
          .from(T_AUTHOR)
          .where(LAST_NAME.equal(param("lastName", "Poe")));
    
    // Explicitly inlining "Poe" in the generated SQL string
    create.select()
          .from(T_AUTHOR)
          .where(LAST_NAME.equal(inline("Poe")));

    上面的例子将产生

    SELECT * FROM T_AUTHOR WHERE LAST_NAME = ?
    SELECT * FROM T_AUTHOR WHERE LAST_NAME = ?
    SELECT * FROM T_AUTHOR WHERE LAST_NAME = 'Poe'

    在内联“ Poe”的情况下,转义由jOOQ处理,以防止语法错误和SQL注入。 但是jOOQ还支持在生成SQL中直接注入SQL字符串 例如:

    // Inject plain SQL into jOOQ
    create.select()
          .from(T_AUTHOR)
          .where("LAST_NAME = 'Poe'");

    在这种情况下,SQL注入可能与JDBC一样发生  

    结论

    本质上,伊戈尔是对的。 (客户端)应用程序开发人员的责任是意识到由其代码创建SQL注入问题。 但是,如果建立在JDBC之上的数据库抽象框架可以在其API中尽可能避免SQL注入,那就更好了。 从SQL注入的角度来看,数据库抽象框架可以分为三类:

    • 简单的实用程序 这些包括Spring的JdbcTemplateApache的DbUtils 它们实际上只是通过便利性(更少的异常处理,更少的冗长性,更简单的变量绑定,更简单的数据提取)增强了JDBC API。 当然,这些工具不会阻止SQL注入
    • 完整SQL抽象 这些包括jOOQJaQuQueryDSLJPA的CriteriaQuery等。 它们的正常操作模式将始终在生成SQL中呈现绑定值。 在大多数情况下,这可以防止SQL注入。
    • 其他的 许多其他框架(包括ActiveJDBCHibernate )主要基于(SQL或HQL)字符串操作。 尽管它们抽象了许多与SQL有关的内容,但它们根本不会阻止SQL注入。

    因此,当您在Java应用程序中选择任何SQL抽象工具时,请注意SQL注入的严重性。 并且要注意一个事实,无论您的工具是否可以帮助您防止它!

    参考:来自JAVA,SQL和JOOQ博客的JCG合作伙伴 Lukas Eder的数据库抽象和SQL注入


    翻译自: https://www.javacodegeeks.com/2012/07/database-abstraction-and-sql-injection.html

    sql抽象语法树

    展开全文
  • 基于 《回溯》 一文介绍的思路,我们利用 JS 实现一个微型 SQL 解析器,并介绍如何生成语法树,如何在 JS SQL 引擎实现语法树生成功能! 解析目标是: select name, version from my_table; 复制代码文法: const ro...
  • HiveQL是一个非标准的sql语言,实现了sql的大部分规范,...用户编写语言的词法规则和语法规则,然后通过antlr提供的运行时库将语言转换成抽象语法树。antlr支持java、c、c++、Python等多种语言。Antlr3是antlr的第..
  • DruidSQL解析引擎主要分为三个部分,AST抽象语法树,词法解析,语法解析。 AST抽象语法树,语法解析的结果,由众多AST语法对象组成一颗树,而词法分析就是解析出一个一个关键字,作为语法解析的输入。 Druid SQL...
  • 将简单SQL语句解析为抽象语法树(AST),然后将其转换回SQL。 用法 为SQL语句创建AST const { Parser } = require ( 'flora-sql-parser' ) ; const parser = new Parser ( ) ; const ast = parser . parse ( '...
  • 1.1. Sql语法树 ast 如下图锁死1 2. SQL语句解析的思路和过程3 2.1. lexer作为一个工具,完成了对SQL字符串的切割,将语句转化成一个tokens数组。3 2.2. Parser完成了SQL解析的后序部分:使用一个lexer对象...
  • Flink SQL中使用Calcite作为sql语法解析、校验、优化工具,本篇是实操篇,介绍一下calcite做sql语法解析使用方式。 sql经过calcite解析之后,得到一棵抽象语法树,也就是我们说的AST,这棵语法树是由不同的节点组成...
  • SQL 语法整理

    2020-08-21 23:59:40
    近期工作中多少会涉及到SQL 相关的内容,尤其是leader在做分布式存储引擎底座的设计过程中提到:各种分布式事务,二级索引,还有剖析B+的实现 ,并和我们rocksdb 的LSM实现差异等等,真的是让之前对分布式存储领域...
  • sql 解析,编译,ast 抽象语法树

    万次阅读 2018-03-06 01:35:35
    Day4: 《将sql转换为es的DSL》 ...
  • SQL语法的麻烦

    2012-02-21 16:25:05
    最近几天还得搞回以前写的SQL语法分析器, 来支持join和sub select. 花了两天时间终于调完了. 在我自己写过的pascal/javascript/sql这三种语法分析器中, 最麻烦的就算SQL了, 所以以前只是实现了SQL92的lexer和SQL89...
  • Sql语句解析为一棵语法树 相关

    万次阅读 2012-07-03 13:49:37
    》》 http://www.bianceng.cn/Sql%20server/sql99.htm 》》http://www.open-open.com/doc/view/ab3fcde50b4a429faa0c4365760c73dd
  • Mybatis基本sql语法

    万次阅读 多人点赞 2018-05-08 17:24:16
    Mapper XML 文件 MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。... SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序): ...
  • SQL语法大全

    千次阅读 2004-08-08 12:59:00
    SQL基础 本章内容SQL介绍使用SELECT 语句从表中取数据创建新表字段属性 向表中添加数据删除和修改表 为了建立交互站点,你需要使用数据库来存储来自访问者的信息。例如,你要建立一个职业介绍服务的站点,你就需要...
  • 本文主要内容如下: 介绍 Flink SQL 引擎:Calcite 简述 Flink Table/SQL 执行...flink 语法扩展 Flink SQL 引擎:Calcite 简述 Flink Table/SQL 执行流程 以 Flink SQL Demo 为切入,结合调试过程,深入理...
  • SQL语法基础篇(一)

    2019-09-02 20:49:30
    SQL语法基础篇(上)01丨了解SQL:一门半衰期很长的语言02丨DBMS的前世今生03丨学会用数据库的方式思考SQL是如何执行的 01丨了解SQL:一门半衰期很长的语言 入门SQL并不难 我们可以把 SQL 语言按照功能划分成以下的...
  • 构建 语法树 来解析 数学表达式

    千次阅读 2018-11-30 12:53:04
    这个主要是用来学习语法树的构建,数学表达式只是一个引子,本项目可以引申到 HTTP 请求报文解析,SQL语句的解析的诸如此类的拥有固定规则的字符串的解析。 思考 我们想想,对于 1 + 2 * 3 这个表达式,它的值是7。...
  • LINQ To SQL 语法及实例大全

    千次阅读 2014-08-28 09:28:50
    LINQ To SQL 语法及实例大全 LINQ to SQL语句(1)之Where Where操作 适用场景:实现过滤,查询等功能。 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的 ,而判断条件就是它后面所接的...
  • 前段时间意外见到... 它可以把SQL语句转换为Java对象,由于JsqlParser是使用JavaCC做语法分析的,而本身JavaCC就支持JJTree...如是就写了个小工具SQLParser,将生成的对象以的形式呈现出来^-^  先上软件,...
  • SQL语法分析-基础篇

    千次阅读 2018-08-19 17:21:28
    先上个简单的SQL语句(Mysql): “SELECT c1,c1+3 as c2,c3 aliasC3,”字符串” FROM table1 WHERE id&amp;gt;1 and id&amp;lt;100” 从表table1中查询id大于1小于100的记录。 查询的字段为列c1,根据c1...
  • C#--Link to sql语法大全

    千次阅读 2018-05-10 16:26:29
    LINQ to SQL语句(1)之Where Where操作 适用场景:实现过滤,查询等功能。 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子句。 Where操作包括3种形式,分别...
  • 在阅读java ORM框架spring data jpa的源码时,发现Hibernate(spring data jpa依赖Hibernate核心代码)在底层使用了AST抽象语法树,将hql转换为sql,这激发了我研究AST的兴趣。 AST概述     AST...
  • SQL语句语法

    千次阅读 2009-10-27 20:26:00
    SQL语句语法目录13.1. 数据定义语句 13.1.1. ALTER DATABASE语法 13.1.2. ALTER TABLE语法 13.1.3. CREATE DATABASE语法 13.1.4. CREATE INDEX语法 13.1.5. CREATE TABLE语法 13.1.6. DROP DATABASE语法 13.1.7. ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 60,482
精华内容 24,192
关键字:

sql语法树