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]
}
相关推荐
AI绘画小338 分钟前
【网络安全】Wireshark 抓包过滤:源 / 目的 IP 过滤 + 命令大全
数据库·tcp/ip·测试工具·安全·web安全·wireshark
数据知道27 分钟前
FastAPI项目:构建打字速度测试网站(MySQL版本)
数据库·python·mysql·fastapi·python项目
姓蔡小朋友1 小时前
redis GEO数据结构、实现附近商铺功能
数据结构·数据库·redis
冉冰学姐1 小时前
SSM农贸市场摊位管理系统c22ux(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架·农贸市场·摊位管理系统
面向星辰1 小时前
SQL LIKE 相似信息查找语句
数据库·sql
数据库学啊1 小时前
时序数据库选型
数据库·时序数据库
TDengine (老段)1 小时前
强杀服务、重启系统及断电对 TDengine 影响
运维·服务器·数据库·物联网·时序数据库·tdengine·涛思数据
数据库学啊1 小时前
时序数据库怎么选
数据库·时序数据库
baivfhpwxf20232 小时前
SQL Server 创建一个删除分表的作业,每月执行一次,删除表的逻辑放到存储过程里
数据库
不知更鸟7 小时前
Django 项目是什么
数据库·sqlite