Sql语句解析工具类

需求:

项目 web-sql模块,需要根据 sql 解析获取数据库表,然后对(金库)表权限进行校验。

金库表:用户查询该表前需要审批。

一、Druid (推荐)

添加依赖:

xml 复制代码
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.2.8</version>
</dependency>

工具类:

java 复制代码
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.*;

import java.util.ArrayList;
import java.util.List;

/**
 * @author NanNan Wang
 */
public class SqlUtil {

    public static List<String> getTableNamesFromSQL(String sql, String dbType) {
        List<String> tableNames = new ArrayList<>();

        // 解析 SQL 语句
        List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);

        for (SQLStatement stmt : stmtList) {
            if (stmt instanceof SQLSelectStatement) {
                SQLSelectStatement selectStatement = (SQLSelectStatement) stmt;
                SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) selectStatement.getSelect().getQuery();

                extractTableNames(queryBlock.getFrom(), tableNames);
            } else if (stmt instanceof SQLInsertStatement) {
                SQLInsertStatement insertStatement = (SQLInsertStatement) stmt;
                tableNames.add(insertStatement.getTableName().getSimpleName());
            } else if (stmt instanceof SQLUpdateStatement) {
                SQLUpdateStatement updateStatement = (SQLUpdateStatement) stmt;
                tableNames.add(updateStatement.getTableName().getSimpleName());
            } else if (stmt instanceof SQLDeleteStatement) {
                SQLDeleteStatement deleteStatement = (SQLDeleteStatement) stmt;
                tableNames.add(deleteStatement.getTableName().getSimpleName());
            }
        }
        return tableNames;
    }

    private static void extractTableNames(SQLTableSource tableSource, List<String> tableNames) {
        if (tableSource instanceof SQLExprTableSource) {
            // 如果是简单表名,直接添加
            SQLExprTableSource exprTableSource = (SQLExprTableSource) tableSource;
            tableNames.add(exprTableSource.getTableName());
        } else if (tableSource instanceof SQLJoinTableSource) {
            // 如果是 JOIN,递归获取左表和右表
            SQLJoinTableSource joinTableSource = (SQLJoinTableSource) tableSource;
            extractTableNames(joinTableSource.getLeft(), tableNames);
            extractTableNames(joinTableSource.getRight(), tableNames);
        } else if (tableSource instanceof SQLSubqueryTableSource) {
            // 如果是子查询,递归处理子查询中的表
            SQLSelect subSelect = ((SQLSubqueryTableSource) tableSource).getSelect();
            extractTableNames(subSelect.getQueryBlock().getFrom(), tableNames);
        }
    }
}

测试样例

java 复制代码
 @Test
 void getTableNamesFromPGSQL() {
     String sql = "SELECT e.id, e.name, d.name AS dept_name FROM schema.employee e " +
             "LEFT JOIN schema.department d ON e.dept_id = d.id WHERE e.salary > 1000";
     List<String> tableNames = SqlUtil.getTableNamesFromSQL(sql, JdbcConstants.POSTGRESQL.name());

     System.out.println("Tables: " + tableNames); //Tables: [employee, department]
 }

二、JSqlParser

添加依赖:

xml 复制代码
<dependency>
  <groupId>com.github.jsqlparser</groupId>
  <artifactId>jsqlparser</artifactId>
  <version>4.6</version>
</dependency>

工具类:

java 复制代码
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.util.TablesNamesFinder;

import java.util.ArrayList;
import java.util.List;

public class SqlUtil {

     /**
     * 使用 JSqlParser 根据 SQL 和数据库类型解析 SQL 中的所有表名
     * @param sql SQL 语句
     * @return 表名列表
     * @throws Exception 解析异常
     */
    public static List<String> extractTableNames(String sql) {
        // 使用 JSqlParser 解析 SQL 语句
        Statement statement = null;
        try {
            statement = CCJSqlParserUtil.parse(sql);
        } catch (JSQLParserException e) {
            throw new RuntimeException(e);
        }

        // 使用 TablesNamesFinder 提取表名
        TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
        return tablesNamesFinder.getTableList(statement);
    }
}

重要:要注意如果是 PostgreSQL 这里支持 模式(schema) 的需要进一步处理!

例如:

java 复制代码
@Test
void extractPgTableNames () {
    String sql = "SELECT e.id, e.name, d.name AS dept_name FROM schema.employee e " +
            "LEFT JOIN schema.department d ON e.dept_id = d.id WHERE e.salary > 1000";
    List<String> tableNames = SqlUtil.extractTableNames(sql);
    System.out.println("Tables: " + tableNames); //Tables: [schema.employee, schema.department]
}
相关推荐
数据知道21 分钟前
PostgreSQL 故障排查:如何找出数据库中最耗时的 SQL 语句
数据库·sql·postgresql
qq_124987075321 分钟前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计
枷锁—sha23 分钟前
【SRC】SQL注入WAF 绕过应对策略(二)
网络·数据库·python·sql·安全·网络安全
Coder_Boy_27 分钟前
基于SpringAI的在线考试系统-考试系统开发流程案例
java·数据库·人工智能·spring boot·后端
Gain_chance33 分钟前
35-学习笔记尚硅谷数仓搭建-DWS层最近n日汇总表及历史至今汇总表建表语句
数据库·数据仓库·hive·笔记·学习
此生只爱蛋1 小时前
【Redis】主从复制
数据库·redis
马猴烧酒.1 小时前
【面试八股|JAVA多线程】JAVA多线程常考面试题详解
java·服务器·数据库
天天爱吃肉82182 小时前
跟着创意天才周杰伦学新能源汽车研发测试!3年从工程师到领域专家的成长秘籍!
数据库·python·算法·分类·汽车
大巨头2 小时前
sql2008 数据库分页语句
数据库
m0_715575342 小时前
使用PyTorch构建你的第一个神经网络
jvm·数据库·python