zoukankan      html  css  js  c++  java
  • 再读simpledb 之 SQL语句解析(1)

    前面的几个部分,基本实现了数据的读写。对于数据库使用者来说,SQL(Structured Query Language)是访问数据库的窗口,这一节就来看下simpledb对SQL语句的解析。

    首先要说的是,simpledb实现了一个SQL-92的子集,具体支持的语法如下所示:

       1:  
       2: <Query>      := SELECT <SelectList> FROM <TableList> [ WHERE <Predicate> ]
       3: <SelectList> := <Field> [ , <SelectList> ]
       4: <TableList>  := TableName [ , <TableList> ]
       5: <Predicate>  := <Term> [ AND <Predicate> ]
       6: <Term>       := <Expression> = <Expression>
       7: <Expression> := <Field> | <Constant>
       8: <Field>      := FieldName
       9: <Constant>   := String | Integer
      10:  
      11: <Modify>     := <Insert> | <Delete> | <Update> 
      12: <Insert>     := INSERT INTO TableName ( <FieldList> ) VALUES ( <ConstList> )
      13: <FieldList>  := <Field> [ , <FieldList> ]
      14: <ConstList>  := <Constant> [ , <Constant> ]
      15: <Delete>     := DELETE FROM TableName [ WHERE <Predicate> ]
      16: <Update>     := UPDATE TableName SET <Field> = <Expression> [ WHERE <Predicate> ]
      17:  
      18: <Create>     := <CreateTable> | <CreateView> | <CreateIndex>
      19: <CreateTable>:= CREATE TABLE TableName ( <FieldDefs> )
      20: <FieldDefs>  := <FieldDef> [ , <FieldDefs> ]
      21: <FieldDef>   := FieldName <TypeDef>
      22: <TypeDef>    := INT | VARCHAR ( Integer )
      23: <CreateView> := CREATE VIEW ViewName AS <Query>
      24: <CreateIndex>:= CREATE INDEX IndexName ON TableName ( <Field> )

    simpledb中的parse模块完成了解析的功能,类图如下:

    image

    image

    图1 parse类图

    在开始之间,先想一下SQL语句的解析,主要的工作是:

    通过对输入SQL语句字符串的分割等工作,

    首先,对输入SQL语句字符串的分割等工作;

    然后,识别出语句执行的是CRUD中的哪个动作,识别出数据库操作关联的表和相关的数据;

    最后,把识别出的表和数据传递给系统的相关接口,完成查询。

    结合上面的类图,可以分三类:lexer、parser工具类,完成了SQL的解析;*Data,SQL解析成果的载体;BadSyntaxException,语法错误异常类。

    下面,先从工具类说起:

    > lexer

    用编译原理的知识说来,这是个词法分析器,主要负责把输入的SQL字符串切割开,

    第一步,将SQL字符串使用指定的分割符进行分割,将结果保存在一个叫tokens的string数组中

    image

    图2 lexer分割SQL示意图

    分割过程如图2所示,利用正则表达式,将SQL字符串分割。

    \s{1}|(,){1}|(=){1}|(\(){1}|(\)){1}|('[^']*'){1}

    图3 SQL to tokens分割过程用到的RE

    image

    图4 实际运行时tokens结果

     

    image

    图5 恶意输入的结果

    ( 在面试的时候,问过我一个问题,如果有用户而已输入,输入了N多个空格,隔断了select语句的field-list,该怎么办?

    select sid,            sname from     students where sid='10001'

    实际上,在上面这个步骤,把所有的单元都独立分出来,如图5所示,每个空格都占一个tokens的位置。但是,lexer有一个nextToken方法,能有效地过滤掉这些干扰:

    private void nextToken()
    {
        position++;
        while (position < tokens.Length && tokens[position].Length == 0)
            position++;
    }

    之后,lexer通过以下的算法,实现对各种字符串片段的处理

    (注意:lexer下有tokens数组的指针position,指向了lexer当前处理的tokens数组中的元素)

    1、定位到下一个处理元素token,使用nextToken()方法
    2、匹配token,使用match*()方法

    3、处理token,使用eat*()方法

    在match阶段,用到了如下的RE来对字符串片段进行匹配:

    private const string intPattern = @"^[-+]?(0|[1-9]\d*)$";
    private const string stringPattern = @"^'[^']*'$";
    private const string idPattern = @"^[a-zA-Z_]\w*$"
    private const string keywordPattern = @"^[a-zA-Z]*$";

    simpledb支持的SQL关键字如下:

    string[] keywords = {"select","from","where","and","insert","into","values",
    "delete","update","set","create","table","varchar","view","as","index","on"};

    在eat阶段,

    总共有5个eat方法:eatDelim(char) eatId() eatIntConstant() eatKeyword(string) eatStringConstant,其中eatDelim和eatKeyword,有参数输入,根据参数的不懂,吃掉相应的分隔符或关键字,其他的,只是利用position,将position指向的token解析对指定的int或string。

    可以说,lexer为SQL的解析完成了第一步的工作。

    明天得早起,先到这里,待续。

  • 相关阅读:
    poj 3321 Apple Tree
    hdu 1520 Anniversary party
    Light OJ 1089 Points in Segments (II)
    Timus 1018 Binary Apple Tree
    zoj 3299 Fall the Brick
    HFUT 1287 法默尔的农场
    Codeforces 159C String Manipulation 1.0
    GraphQL + React Apollo + React Hook 大型项目实战(32 个视频)
    使用 TypeScript & mocha & chai 写测试代码实战(17 个视频)
    GraphQL + React Apollo + React Hook + Express + Mongodb 大型前后端分离项目实战之后端(19 个视频)
  • 原文地址:https://www.cnblogs.com/YFYkuner/p/2679584.html
Copyright © 2011-2022 走看看