SQL血缘解析

Druid 作为使用率特别高的的数据库连接池工具,在具备完善的连接池管理功能外,同时Druid 的 SQL解析功能可以用来防止 SQL注入等安全风险。通过对 SQL 语句进行解析和检查,Druid 可以识别并阻止潜在的恶意 SQL 语句执行,黑名单(阻止特定的 SQL 语句执行)、白名单(仅允许特定的 SQL 语句执行)等功能,从而提升数据库的安全性。

可以利用 Druid 的 SQL 解析结果,进行进一步的二次解析和组合,从而形成脚本的血缘关系和影响关系
在这里插入图片描述

测试访问地址:http://43.139.19.48:8888/#/
Gitee:https://gitee.com/javalxy/sql-lineage-show

前置知识

AST抽象语法树

官方文档链接:Druid_SQL_AST · alibaba/druid Wiki · GitHub

抽象语法树(AST, Abstract Syntax Tree) 是编程语言解析和编译过程中产生的一种树状数据结构,它表示源代码的语法结构。AST 抽象出代码的语法特征,同时剔除不必要的语法细节,提供了程序结构的抽象表示。

SQLTableSource

常见的SQLTableSource包括SQLExprTableSource、SQLJoinTableSource、SQLSubqueryTableSource、SQLUnionQuery

主要为表的关联类型,针对与不同的SQL,分支处理。

if (tableSource instanceof SQLExprTableSource) {
   
    SQLExpr sqlExpr = ((SQLExprTableSource) tableSource).getExpr();
    processTableExpression(tableName, sqlExpr);
} else if (tableSource instanceof SQLSubqueryTableSource) {
   
    List<SQLSelectItem> selectList = queryBlock.getSelectList();
    selectList.forEach(item -> processTableExpression(tableName, item.getExpr()));
} else if (tableSource instanceof SQLUnionQuery) {
   
    SQLUnionQuery sqlUnionQuery = (SQLUnionQuery) tableSource;
    List<SQLSelectQuery> relations = sqlUnionQuery.getRelations();

    for (SQLSelectQuery relation : relations) {
   
        if (relation instanceof SQLSelectQueryBlock) {
   
            SQLTableSource nestedTableSource = ((SQLSelectQueryBlock) relation).getFrom();

            if (nestedTableSource instanceof SQLExprTableSource) {
   
                SQLExpr sqlExpr = ((SQLExprTableSource) nestedTableSource).getExpr();
                processTableExpression(tableName, sqlExpr);
            }
        }
    }
}

SQLSelect & SQLSelectQuery

SQLSelectStatement包含一个SQLSelect,SQLSelect包含一个SQLSelectQuery,都是组成的关系。SQLSelectQuery有主要的两个派生类,分别是SQLSelectQueryBlockSQLUnionQuery

此对象是一个查询主体,其主要的关联关系就是查询的关系。这种关系由查询字段衍生出来,涵盖了查询字段、关系字段、函数、组成等(也就是可解析的字段类型,后文会详细说明,即:SQLExpr)。除此之外,还有表关系(如上文提到的 SQLTableSource)、分组关系等。这些关系共同构成了 SQL 语句的结构和逻辑。此对象也可看做是解析的中流砥柱,很多向下的衍生关系也都通过此封装。

if (sqlSelectQuery instanceof SQLSelectQueryBlock) {
   
    disposeSelectQuery((SQLSelectQueryBlock) sqlSelectQuery, sqlItems, fieldModels);
} else if (sqlSelectQuery instanceof SQLUnionQuery) {
   
    disposeSQLUnionQuery((SQLUnionQuery) sqlSelectQuery, sqlItems, fieldModels);
}

SQLExpr

解析代码中大约 80% 的内容可能都围绕着 SQLExpr 进行。在所有的 SQLExpr 子对象中,通常存在多层次的继承关系。因此,无论表达式多么复杂,都可以通过递归层层向下,找到基本类型进行处理。这种处理方式相当繁琐,因为需要针对每种类型进行细致的拆解和处理。对于几种基本类型,可以直接进行处理;但对于聚合函数或二元操作这样的复杂类型,则需要逐层拆解,获取其下层类型后再判断并处理,如此反复,直至解析到基本类型为止,形成一个完整的闭环。

目前已处理类型如下:

SQLAggregateExpr、SQLBinaryOpExpr、SQLPropertyExpr、SQLMethodInvokeExpr、SQLAllColumnExpr、SQLIdentifierExpr、SQLCaseExpr、SQLInListExpr、SQLCastExpr、SQLBetweenExpr、SQLQueryExpr、SQLCharExpr、SQLNullExpr、SQLIntegerExpr、SQLNumberExpr

其中也存在无法处理的情况,如常量类型,All类型(*),需要其他逻辑支持。

if (sqlExpr instanceof SQLAggregateExpr) {
   
    // 处理聚合函数表达式
    dispose((SQLAggregateExpr) sqlExpr, itemFieldModels);
} else if (sqlExpr instanceof SQLBinaryOpExpr) {
   
    // 处理二元操作表达式
    dispose((SQLBinaryOpExpr) sqlExpr, itemFieldModels);
} else if (sqlExpr instanceof SQLPropertyExpr) {
   
    // 处理属性表达式
    dispose((SQLPropertyExpr) sqlExpr, itemFieldModels);
} else if (sqlExpr instanceof SQLMethodInvokeExpr) {
   
    // 处理方法调用表达式
    dispose((SQLMethodInvokeExpr) sqlExpr, itemFieldModels);
} else if (sqlExpr instanceof SQLAllColumnExpr) {
   
    // 处理所有列(*)表达式
    dispose((SQLAllColumnExpr) sqlExpr, itemFieldModels);
} else if (sqlExpr instanceof SQLIdentifierExpr) {
   
    // 处理标识符表达式
    dispose((SQLIdentifierExpr) sqlExpr, itemFieldModels);
}
// 略...

示例

此示例和druid解析无关系。

SELECT name, age FROM users WHERE age > 18;

对应的 AST

这个 SQL 查询的 AST(抽象语法树)可以表示如下:

SelectStatement
 ├── SelectClause
 │   ├── Column (name)
 │   └── Column (age)
 ├── FromClause
 │   └── Table (users)
 └── WhereClause
     └── BinaryExpression (>)
         ├── Column (age)
         └── Literal (18)

一条正确的 SQL 语句可以通过抽象语法树(AST)进行概括(通常使用 ANTLR 作为底层支持,排除 g4 模板解析失败的情况)。对于较复杂的语句,如关联查询、子查询和函数处理等,这些语句基本上是层层嵌套的。理论上,通过支持各种类型的解析后,可以使用递归方式遍历整个抽象语法树,从而完整地获取字段结构并进行二次处理。

二次处理Druid AST抽象语法树

使用Druid解析SQL

List
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值