• 再读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的解析完成了第一步的工作。

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

  • 相关阅读:
    Windows自带Android模拟器启动失败
    Xamarin.Android提示找不到mono.Android.Support.v4
    Xamarin提示Build-tools版本过老
    Xamarin Android布局文件没有智能提示
    Xamarin.iOS模拟器调试找不到资源文件
    彻底卸载 RAD Studio 2009/2010/XE+ 的步骤
    Delphi版本号对照
    RAD Studio 2010 环境设置(转)
    C语言写的俄罗斯方块
    字符编解码的故事–ASCII,ANSI,Unicode,Utf-8区别
  • 原文地址:https://www.cnblogs.com/YFYkuner/p/2679584.html
Copyright © 2020-2023  润新知