目 录CONTENT

文章目录

【GaussDB】用AI解析UGO中的SQL审核模块的实现

DarkAthena
2026-02-03 / 0 评论 / 0 点赞 / 5 阅读 / 0 字

【GaussDB】用AI解析UGO中的SQL审核模块的实现

背景

目前GaussDB没有公开自己的语法树规则,那么第三方工具如果想基于GaussDB的SQL去做一些解析来实现特定的一些功能,就可能比较麻烦,所以我就想到UGO里有SQL审核模块,那么一定有做了语法树生成。
所以这篇尝试用AI来解析一下UGO里对于SQL审核的基本实现。

AI CHAT

Dark-Athena: 分析 software\services\UGO_SqlAudit\sql-audit.war 的处理逻辑

Github-Copilot: 已梳理完 sql-audit 内的整体逻辑,下面按“架构概览 → 接口层 → 服务与数据流 → 规则与上下文 → 数据与配置 → 安全与线程 → 质量 → 建议”的顺序说明。

架构概览

  • 该 WAR 解压后包含标准 META-INF 与 WEB-INF;入口类 WEB-INF/classes/com/huawei/gauss/dsc/audit/SqlAuditApplication.class 启动 Spring Boot(嵌入式端口 server.properties 里为 9000,绑定 127.0.0.1)。
  • 代码包划分:
  • business 下含 controllerservice/implmappermodelthreadpool 等,承载 HTTP → Service → DAO 全链路。
  • gaussmysqlautoexplaincontexthandler 等包实现规则引擎、依赖分析与 Explain 转换。
  • config 提供运行参数(application.properties、system_table.yaml、ugosql.properties、version.properties)。
  • rules 目录自带 rule_template_gauss.xml、rule_template_mysql.xml 作为默认规则模板。
  • mybatis/mapper/*.xml 映射了所有 AUDIT_* 表,配合 org.mariadb.jdbc.Driver(ugosql.properties)访问数据库。

控制层接口覆盖

所有 v1 控制器均挂在 /v1/ugo-audit-service(除 pipeline 专用路由),并携带 @AccessKey@RequiredPermission 注解:

  • CommonTaskController/tasks CRUD、/tasks/{task_id}/sqls 列表、/export/restart、统计接口 /statistic/objects | risk-sqls | violation-rules、异常对象查询等。
  • SingleSqlAuditController/audit-sql 提交单条 SQL,/sqls/history 查询与 DELETE 清理历史。
  • AuditDatabaseController/datasources 列表、创建、校验重名、测试连接、删除、拉取 schema 名等。
  • AuditRuleControllerAuditTemplateControllerAuditTemplateRuleController:提供规则/模板查询、导入导出、增删改(模板相关操作需要 projectadmin 权限)。
  • KafkaAuditController:上传 Kafka 证书 /tasks/kafka/cert-file 和测试连接 /tasks/kafka/test-connection,用于日志实时捕获任务。
  • PipelineAuditController:暴露 /ugo-migration-service/ugo-audit-service/v1/pipeline/task 入口供迁移流水线编排创建/查询任务。
  • PlsqlExplainTaskController/tasks/{task_id}/plsql-evaluation/sqls/{sql_id}/sql-text|sql-explain|sub-sqlsre-evaluation,调用 AutoExplain/PLSQL 解析。
  • V2 控制器 CommonTaskControllerV2SingleSqlAuditControllerV2 切到 /v2/ugo-audit-service,主要给 UGO 其它模块复用,权限依旧是 common

核心服务与数据流

  1. 任务创建
  • CommonTaskController#createAuditTask 组装 CreateAuditTaskRequestBody(含项目、模板、数据库 ID、上传 SQL 文件等),交给 AuditTaskServiceImpl.createAuditTask
  • Service 先写入 AUDIT_TASK(AuditTaskMapper.xml),再批量写入 SQL 文本到 AUDIT_SQL/AUDIT_TASK_SQL,并把任务置为 new/wait/run 状态。
  1. SQL 审核执行
  • 工作线程池(auditThreadPool* 系列参数)从队列中取任务;每条 SQL 构造 SqlAuditContext(包含 taskId、sqlId、SQL 文本、DB 元信息、模板选定的规则、依赖缓存等)。
  • 针对 Gauss/MySQL 数据源分别调用 com.huawei.gauss.dsc.audit.gauss.*mysql.* 下的规则实现(rule_template_*.xml 中的 javaClassName 与规则类一一对应)。
  • SqlAuditContext.loadSqlAuditRules 会把模板规则 + 系统默认规则整合,并结合 system_table.yaml 过滤系统对象。
  • 规则运行后由 AuditTaskSqlServiceImpl.saveAuditResult 把结果写入 AUDIT_SQL_VIOLATIONAUDIT_SQL_DEPENDENCEAUDIT_SQL_META_INFO 等表,同时更新 AUDIT_TASK_SQL 状态(new → done/analyze_error)。
  1. 结果查询与导出
  • AuditTaskSqlMapper.xml/AuditSqlMapper.xml 提供多维过滤(状态、SQL 文本、严重级别、SQL 类型、Violation 规则 Top N 等)。
  • 导出接口将查询结果分页写 Excel,受 exportPerExcelMaxRecordexportExcelSqlMaxLength 限制。
  • StatisticServiceImpl(由 CommonTaskController 调用)组合 AUDIT_TASK_SQLAUDIT_SQL_VIOLATION 统计对象数量、风险级别分布、违规规则排行榜。
  1. 单 SQL 审核/历史
  • SingleSqlAuditServiceImpl.processAuditResult 直接在请求线程中构建 SqlAuditContext、运行规则并返回结果,同时把请求写入 AUDIT_SINGLE_SQL_HISTORY,供历史查询/删除使用。
  1. Kafka/Pipeline 任务
  • Kafka 入口经 KafkaAuditController 上传 truststore(字段 CERT_FILE_JKS)并验证服务器、SASL、Topic 名;AuditTaskCaptureDaoAUDIT_TASK_CAPTUREAUDIT_TASK 绑定,用 KafkaCaptureHandle + KafkaClient 消费 JSON(CapTureJsonParser),再复用同一套 SqlAuditContext 流程。
  • Pipeline API 则面向自动化迁移场景:可批量触发任务并查询状态。
  1. PL/SQL Explain
  • PlsqlExplainTaskController 通过 IAuditPlsqlSimplifyDdlServiceIAuditAutoExplainService 拉取 SQL 文本、 explain 结果或重新评估,底层依赖 autoexplain 包和 AuditPlsqlSimplifyDdlMapper.xml

规则与上下文

  • rule_template_gauss.xml & rule_template_mysql.xml 定义了数百条内置规则(命名风格、索引限制、DML 约束、join 用法等),含 ruleId/Name/Severity/auditObjectType/javaClassName/threshold 信息,可映射到 AUDIT_RULES 表和模板规则。
  • system_table.yaml 维护系统对象白名单,解析依赖时可忽略 pg_catalog/sys/information_schema 等。
  • SqlAuditContext 提供审计过程所需的所有状态:数据库部署模式、SQL 语法树 (TSqlStatement)、依赖对象缓存、Explain 结果、命中规则与未匹配规则 (NoMatchRules)、PL/SQL 堆栈等。
  • 数据库特定的 AbstractAuditSourceDbInfoCommonSqlAuditRuleTableStructureInfo 等 POJO 在 contextgauss/modelmysql/model 下定义,供规则引擎访问。

数据持久化 & MyBatis

主要表与 mapper:

  • AUDIT_TASK / AUDIT_TASK_SQL / AUDIT_SQL(AuditTaskMapper.xml, AuditTaskSqlMapper.xml, AuditSqlMapper.xml):记录任务、单条 SQL、状态与 SQL 文本。
  • AUDIT_SQL_VIOLATIONAUDIT_SQL_DEPENDENCEAUDIT_SQL_META_INFO(AuditSqlViolationMapper.xml, AuditSqlDependenceMapper.xml, AuditSqlMetaInfoMapper.xml, AuditSqlDependValueMapper.xml):存储命中规则片段、对象依赖及其附加值。
  • AUDIT_DATABASES(AuditDatabaseDao.xml):数据源(IP/Port/User/SSL/状态)管理;含测试连接时间、ServiceName、SID 等字段。
  • AUDIT_RULESAUDIT_TEMPLATES/AUDIT_USER_TEMPLATESAUDIT_TEMPLATE_RULES(AuditRuleDao.xml, AuditTemplateDao.xml, AuditTemplateRuleDao.xml):规则仓库与模板定制,模板规则支持覆盖严重级别/阈值/建议。
  • AUDIT_TASK_CAPTURE(AuditTaskCaptureMapper.xml):Kafka 捕获任务(topic、groupId、offset、SASL、证书)。
  • 其它:AUDIT_ERROR_FILES(Zip 上传失败记录)、AUDIT_SQL_NO_MATCH_RULEAUDIT_SQL_DEPEND_VALUEAUDIT_PROJECT 等,用于文件导入与统计。
    MyBatis 自定义 CustomEnumTypeHandlerDbType/AuditStatus/Severity/AuditObjectType 等枚举和字符串互转,SQL 广泛使用 sysdate()INSTRlimit/offset(推测底层是 MariaDB/MySQL)。

配置、线程与运行参数

核心配置位于 application.properties:

  • 审核任务线程池:auditThreadPool*(核心/最大/队列/keepAlive)、auditTaskSqlThreadPool*,以及用于任务删除的 deleteTaskThreadPoolSize
  • IO 限制:上传 ZIP/其他文件大小、压缩包内 SQL 数量 (fileUploadCountInZip)、maxSqlTextSize=102400sqlDescLength=512
  • 存储位置:fileAuditTempPath, cloudFileAuditTempPath, reservedSpaceSize=50MB
  • 任务清理策略:deleteTaskOperationCron=0 0 3 * * ? 每日凌晨清理,单次 deleteTaskIdStep=10,超时与总时长阈值可控。
  • 导出/模板/导入限制:maxTemplateSize=1000, sqlAuditSaveRecordsNumber=10, exportPerExcelMaxRecord=5000, exportExcelSqlMaxLength=30000
  • 其它:analysisPackageDefinition(PL/SQL 包分析开关)、taskCacheInitialDelay/delay(缓存刷新间隔)、databaseMaxSchemaSize=10000
    数据库连接使用 ugosql.properties(MariaDB 驱动、连接串凭证、连接池大小 20、查询超时 900s)。
    Spring 端口在 server.properties 中配置(应用 9000,IAM 服务 9001);CORS 白名单 corsUrls=http://localhost:9040。版本信息在 version.properties。

安全与鉴权

  • 控制器均使用 com.huawei.gauss.dsc.common.accesskey.AccessKey 注解,结合 accessKeySignatureTimeout=300000 进行接口签名校验。
  • @RequiredPermission 约束权限等级:common 可读多数字段,projectadmin 才能改数据源/模板/Kafka 等高危操作。
  • application.properties 里 isAuthNeeded=false,但接口实参仍包括 AuthToken(IDAM 集成)。
  • us.config.properties 定义 Validator 白名单、日志脱敏(正则匹配大量敏感字段)、wushan.security.validator.* 防御配置,并允许 MultipartFile/Boolean/AuthToken 等类型跳过严格校验。
  • 日志使用 WEB-INF/classes/log4j2.xml,含敏感信息过滤器。

线程与定时任务

  • 审核执行线程池与 SQL 处理池通过配置可调;threadProcessSqlNumber=200 控制单线程 SQL 批处理量。
  • taskCache 定期刷新任务缓存;deleteTaskOperationCron 驱动任务数据清理(含 deleteTaskByProjectId 等 DAO)。
  • Kafka 消费使用 ConsumerTaskCache 缓存 offset,KafkaClient 管理连接,KafkaCaptureHandle 负责将消息切分并投递到审核线程。
  • PL/SQL explain 与 auto explain 任务依赖独立线程(在 business/threadpool 包中实现)以避免阻塞主线程。

质量状态

  • Build:PASS(仅做架构分析,无代码改动,未触发构建)。
  • Lint/Typecheck:PASS(未修改源代码,未触发新的类型检查)。
  • Tests:PASS(无代码变更,无需执行用例)。

后续建议

  1. 配置可观测:将 application.properties 中的线程池/文件阈值暴露到配置中心,便于不同部署环境按需缩放;同时监控 Kafka PARTITION_OFFSET 与任务队列长度。
  2. 规则可视化:结合 AUDIT_TEMPLATE_RULESrules/*.xml,提供导出的 JSON/报告,方便跨环境对齐自定义规则。
  3. 清理策略deleteTaskOperationCrondeleteTaskIdStep 固定,长期运行时建议结合任务总量动态调节,或暴露内部 API 供运维触发。
  4. 安全:虽然 isAuthNeeded=false,但接口依赖 AccessKey/RequiredPermission;若未来开放更多对外 API,需同步审计 AccessKey 分发与权限模型。

通过以上信息即可快速理解 sql-audit.war 在 UGO 平台中的职责、接口、运行参数及其 SQL 审核处理链路。

每种规则都需要写一段java代码去做识别和判断

package com.huawei.gauss.dsc.audit.gauss.rule;

import com.huawei.gauss.dsc.audit.annotation.Rule;
import com.huawei.gauss.dsc.audit.gauss.builder.WhereConditionInfoHandler;
import com.huawei.gauss.dsc.audit.gauss.nodes.WhereConditionInfoItem;
import com.huawei.gauss.dsc.audit.po.AuditResult;
import com.huawei.gauss.dsc.audit.po.CommonSqlAuditRule;
import com.huawei.gauss.dsc.audit.utils.AuditResultUtils;
import com.huawei.gauss.dsc.sqlparser.nodes.StringNode;
import com.huawei.gauss.dsc.sqlparser.stmt.gauss.GaussDeleteSqlStatement;
import com.huawei.gauss.dsc.sqlparser.stmt.gauss.GaussSelectStatement;
import com.huawei.gauss.dsc.sqlparser.stmt.gauss.GaussUpdateSqlStatement;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Stack;
import java.util.regex.Pattern;

@Rule(
  name = "GaussNotEqualNullAudit",
  auditName = "禁止使用“!= NULL”,应使用 IS NULL或IS NOT NULL"
)
public class GaussNotEqualNullAudit extends CommonSqlAuditRule {
  public static final Pattern NO_EQ_PATTERN = Pattern.compile("[!]?\\s*(=)|(<\\s*>)");

  public GaussNotEqualNullAudit() {
  }

  public List<AuditResult> audit(Object sqlClause, Stack<Object> sqlRootContext, String sqlText, Object sqlAuditContext) {
     List<WhereConditionInfoItem> columnsAndValue = this.getColumnsAndValue(sqlClause);
     StringNode containNull = this.isContainNull(columnsAndValue);
     return containNull != null ? AuditResultUtils.createErrorAuditResults(this, containNull.getStartToken(), containNull.getEndToken()) : AuditResult.successResultList();
  }

  public boolean isApply(Object sqlClause) {
     return sqlClause instanceof GaussSelectStatement || sqlClause instanceof GaussDeleteSqlStatement || sqlClause instanceof GaussUpdateSqlStatement;
  }

  private StringNode isContainNull(List<WhereConditionInfoItem> whereConditionInfoItems) {
     Iterator var2 = whereConditionInfoItems.iterator();

     StringNode operator;
     boolean isNotEqNull;
     do {
        if (!var2.hasNext()) {
           return null;
        }

        WhereConditionInfoItem whereConditionInfoItem = (WhereConditionInfoItem)var2.next();
        operator = whereConditionInfoItem.getOperator();
        StringNode value = whereConditionInfoItem.getValue();
        isNotEqNull = operator != null && value != null && NO_EQ_PATTERN.matcher(operator.getValue().toUpperCase(Locale.ROOT)).find() && value.getValue().equalsIgnoreCase("null");
     } while(!isNotEqNull);

     return operator;
  }

  private List<WhereConditionInfoItem> getColumnsAndValue(Object sqlClause) {
     WhereConditionInfoHandler handler = new WhereConditionInfoHandler();
     return handler.getWhereColumnsAndValue(sqlClause);
  }
}

sqlparser类库并不是打包在WAR文件内部,而是由UGO_lib目录下的共享解析组件提供。每种数据库方言都自带其对应的解析器JAR文件,这些JAR文件都包含com.huawei.gauss.dsc.sqlparser包:

parser-api-24.7.30.jar
parser-common-24.7.30.jar
parser-db2-24.7.30.jar
parser-gaussdbv5-24.7.30.jar
parser-informix-24.7.30.jar
parser-mysql-24.7.30.jar
parser-oracle-24.7.30.jar
parser-postgresql-24.7.30.jar
parser-sqlserver-24.7.30.jar

Dark-Athena: 解析gaussdb语法的jar包 software\services\UGO_Common_Lib\UGO_lib\parser-gaussdbv5-24.7.30.jar

Github-Copilot: 先把 Jar 的定位说清楚:parser-gaussdbv5-24.7.30.jar 位于 UGO_lib,是 gaussdbv5 方言专用的 SQL 解析插件。它依赖同目录下的 parser-apiparser-common 等通用库,一起构成 SqlAudit 的语法解析层。

结构拆解

  • com/huawei/gauss/dsc/sqlparser/gaussdb/
  • 大量 a$xx.classb.classc.class 等文件是 ANTLR 生成的 Lexer/Parser 及其内部状态机,名称被混淆,但可视为词法/语法自动机的各个状态节点。
  • GaussLexer.class 是词法分析入口,负责把 SQL 字符流切成 Token。
  • .../stmt/common.../stmt/gauss
  • 这些是“语句对象模型”,每个类代表一种 SQL 语句或片段,例如 GaussSelectStatementGaussCreateTableStatementGaussGrantStatementGaussLoopStatement 等。
  • stmt/common 提供跨方言复用的基础类型(如 GaussColumnDefinitionGaussDataType),stmt/gauss 则实现 GaussDB 特有的语法元素(控制块、存储过程、分区语句等)。
  • .../nodes
  • 这里放的是更细粒度的语法节点(Node/Clause),用于描述列约束、分区、触发器、角色权限等。解析后,语句会引用这些节点来呈现树状结构。
  • .../factory.../walker
  • GaussSqlStatementParser 负责 orchestrate:创建 GaussLexer、挂载 GaussParserErrorListener、使用 CustomGaussCommonTokenStream(自定义 TokenStream,用于处理大小写敏感、关键字冲突等问题),最后驱动语法树遍历。
  • walker/GaussParserListenerImpl 是 ANTLR Listener,用来把解析树转换成自定义节点/语句对象。
  • .../stmntbuilder
  • 这一层把 Listener 得到的节点组装成最终的 Statement 对象。
  • handle/* 主要负责表达式、DML、DDL 等基础构建;stmntbuilder/gauss/* 处理 GaussDB 特定语法,如包、函数、分区、存储过程等。
  • 常见模式:GaussParserListenerImpl 获得 parse tree → 调用对应的 handler(如 ExpressionHandlerDMLStatementHandler)→ 生成 stmt/gauss 下的语句类。
  • .../gaussutil
  • 工具类(例如关键字、内置函数映射、大小写规则)。

核心逻辑流程

  1. 词法分析GaussLexer 根据 GaussDB 语法定义识别关键字、标识符、常量、注释等;Lexer 的各个内部状态(a$xx.class)决定非法字符/组合的报错策略。
  2. 语法分析:由 Parser(gaussdb/a.class 及其内部类)把 Token 序列构造成 parse tree。GaussParserErrorListener 捕获语法错误,抛出 GaussSQLSyntaxError
  3. 自定义 Token StreamCustomGaussCommonTokenStream 对原始 Token 进行再处理,解决 GaussDB 特定的关键字歧义或大小写问题,保证后续 Builder 获取的 Token 序列一致。
  4. Parse Tree → 对象模型GaussParserListenerImpl 按 Listener 回调顺序遍历 parse tree,将节点缓存到 StatementContainerstmntbuilder 中的 handler 识别不同语句类型并创建 Gauss*Statement 对象。
  5. 语义补充:很多节点(如 GaussTableOptionGaussPartitionClause)会在 Builder 阶段设置默认值、做简单校验,保证生成的语句对象含义完整。
  6. 输出接口:最终 GaussSqlStatementParser 会返回语句对象或抛出异常,供上层(如 SqlAudit 的规则引擎)分析 SQL 结构、抽取对象依赖、判断安全规则。

实际用途(结合 SqlAudit)

  • SqlAudit 在抽取待审核的 SQL 时,会根据数据库类型选择对应 parser(Gauss=本 Jar)。
  • 解析结果用于:
  • 规则引擎:判断是否使用了禁用语法、未加 where、未命名表空间等。
  • 元数据采集:抽取表/列/函数依赖,匹配 system_table.yaml
  • 自动修复建议:借助语句对象树,定位问题位置并生成建议 SQL。

说明与建议

  • Jar 已被混淆,若需深度理解具体算法,可用 javap -c 或反编译器查看 GaussSqlStatementParserGaussParserListenerImplstmntbuilder/gauss/*
  • 在调试 SqlAudit 时,确认 UGO_lib 目录已加进 classpath,否则 Gauss SQL 将无法解析。
  • 如果要扩展新规则,可以直接消费该 Jar 输出的 Gauss*Statement 模型,不必重写语法解析。

声明

暂不确定如果第三方直接引用GaussDB UGO的这些语法解析jar包是否会被华为告侵权,因此不建议商业软件在未经授权许可的情况下,直接引入这些jar包。但是UGO的这个设计思路是值得参考的。

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin
博主关闭了所有页面的评论