Druid SqlParser理解及使用入门

网站建设4年前发布
35 0 0

以前的项目中很少去思考SQL解析这个事情,即使在saas系统或者分库分表的时候有涉及到也会有专门的处理方案,这些方案也对使用者隐藏了实现细节。,而最近的这个数据项目里面却频繁涉及到了对SQL的处理,原来只是简单地了解Druid的SqlParser模块就可以解决,慢慢地问题变得越来越复杂,直到某天改动自己写的SQL处理的代码很痛苦的时候,意识到似乎有必要更加地了解一下相关的内容才行。,2023030610534263ca41508ee62815a930090233071a40954319210,在了解学习的过程中,发现学习使用SqlParser还是得先了解ast(抽象语法树)这个概念,一搜索相关内容要么是编译原理相关的知识,要么是JavaScript的示例,光看Druid提供的SqlParser相关的Wiki文档又似懂非懂,不知道从哪里下手。,不管怎么样,看了不少碎片化的相关内容以后也收获了一些东西,这里记录下来。,ast全称是abstract syntax tree,中文直译抽象语法树。,原先我觉得要使用SqlParser就照着wiki上的代码步骤拷过来就好了呗,也确实如此,它快速解决了我的问题。可是正如上面所说,希望你的相关代码写得更好一点,或者更理解它是在干吗了解了ast会有不少的帮助。,SQL解析,本质上就是把SQL字符串给解析成ast,也就是说SqlParser的入参是SQL字符串,结果就是一个ast。你怎么使用这个ast结果又是另外一回事,你可以修改ast,也可以添加点东西等等,但整个过程都是围绕着ast这个东西。,20230306105341f24656c89059acb59388357780dba294c81997977,上面提了好几次ast,那ast又是个什么东西呢?,参照维基百科的说法,在计算机科学领域内,ast表示的是你写的编程语言源代码的抽象语法结构。如图:,2023030610543728457e134ca34f82724023300e461752b5b018730,左边是一个非常简单的编程语言源代码:1 + 2,做了一个加法计算,而当它被解析成ast以后如右边的图所示。我们可以看到ast存在三个节点,顶部的 + 表示一个加法节点,这个表达式组合了1、2两个数值节点,由这三个组合在一起的节点就组成了1+2这样的语法结构。,我们看到ast很清晰地用数据结构表示出了字符串源代码,ast的每一个节点均表示源代码当中的一个语法结构。反过来思考一下,我们可以知道源代码解析出来的ast是由很多这样简单的语法结构组合而成的,也就形成了一个复杂的语法树。下面我们看一个稍微复杂一点的,来自维基百科的示例。,源代码:,语法树:,2023030610534316bcf48433ee92ca467534bbef9a6776224538898,这个语法树也清晰地表示的源代码程序,主要由一个while语法和if/else语法以及一些变量之类的组成。,到这里,似乎对源代码和ast有了一个简单的概念,但是还是存在困惑,我为什么要把好好的代码搞成这样?它有什么用?如果只是修改语法,我用正则表达式修改字符串不是简单吗?,确实,有的时候直接处理字符串会是更快速更好的解决方式,但是当源程序语法非常复杂的时候字符串处理的复杂度已经不是一个简单的事了。而ast则把这些字符串变成结构化的数据了,你可以精确地知道一段代码里面有哪些变量名,函数名,参数等,你可以非常精准地处理,相对于字符串处理来说,遍历数据大大降低的处理难度。而ast也常常用在如IDE中错误提示、自动补全、编译器、语法翻译、重构、代码混淆压缩转换等。,我们知道了ast是一种结构化的源代码表示,那针对SQL来说ast就是把SQL语句用结构化的数据来表示了。而SqlParser也就是把SQL解析成ast,这个解析过程则被SqlParser做了隐藏,我们不需要去实现这样一个字符串解析过程。,由此可见,我们需要了解两方面内容:,下面需要一点代码来说明,所以先引入一下maven依赖。,解析语句相对简单,wiki上直接有示例,如:,SQLUtils的parseStatements方法会把你传入的SQL语句给解析成SQLStatement对象集合,每一个SQLStatement代表一条完整的SQL语句,如:,多个SQLStatement,如:,一般上我们只处理一条语句。,SQLStatement表示一条SQL语句,我们知道常见的SQL语句有CRUD四种操作,所以SQLStatement会有四种主要实现类,如:,这里我们以SQLSelectStatement来说明,ast既然是SQL的语法结构表示,我们先看一下ast和SQL select语法的主要对应结构。,SQLSelectStatement包含一个SQLSelect,SQLSelect包含一个SQLSelectQuery,都是组成的关系。SQLSelectQuery有主要的两个派生类,分别是SQLSelectQueryBlock和SQLUnionQuery。,以下是SQLSelectQueryBlock中包含的主要节点:,20230306105344d7a596077fec4d070a6101c9f3862200e5a832860,表格中的这些ast节点都是SQL对应语法的一些表示,相信大家都非常熟悉,根据名字也轻易能了解具体是语法。,这里需要细化一下SQLTableSource这个节点,它有着常见的实现SQLExprTableSource(from的表)、SQLJoinTableSource(join的表)、SQLSubqueryTableSource(子查询的表),如:,另外SQLExpr出现的地方也比较多,比如where语句,join条件,SQLSelectItem中等,因此,也需要细化了解一下,如:,SqlParser定义了完整的ast各个节点对象,一条SQL语句被解析成这些对象的树形结构,而我们要做的就是根据这样的一个树形结构去做相应的处理。以上代码片段摘取了部分wiki上的,并调整了一下顺序,完整的wiki可以到druid的github上查阅。,使用示例:,以上示例中只是简单判断了一下类型,实际项目中你可能需要对整个ast做递归之类的方式来处理节点。其实当SQL语句变成了ast结构以后,我们只要知道这个ast结构存在什么样的节点,获取节点判断类型并做相应的操作即可,至于你是递归还是访问者模式还是别的什么方式去处理ast都可以。

© 版权声明

相关文章