【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下含controller、service/impl、mapper、model、threadpool等,承载 HTTP → Service → DAO 全链路。gauss、mysql、autoexplain、context、handler等包实现规则引擎、依赖分析与 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:/tasksCRUD、/tasks/{task_id}/sqls列表、/export、/restart、统计接口/statistic/objects | risk-sqls | violation-rules、异常对象查询等。SingleSqlAuditController:/audit-sql提交单条 SQL,/sqls/history查询与DELETE清理历史。AuditDatabaseController:/datasources列表、创建、校验重名、测试连接、删除、拉取 schema 名等。AuditRuleController、AuditTemplateController、AuditTemplateRuleController:提供规则/模板查询、导入导出、增删改(模板相关操作需要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-sqls与re-evaluation,调用 AutoExplain/PLSQL 解析。- V2 控制器
CommonTaskControllerV2、SingleSqlAuditControllerV2切到/v2/ugo-audit-service,主要给 UGO 其它模块复用,权限依旧是common。核心服务与数据流
- 任务创建
CommonTaskController#createAuditTask组装CreateAuditTaskRequestBody(含项目、模板、数据库 ID、上传 SQL 文件等),交给AuditTaskServiceImpl.createAuditTask。- Service 先写入
AUDIT_TASK(AuditTaskMapper.xml),再批量写入 SQL 文本到AUDIT_SQL/AUDIT_TASK_SQL,并把任务置为new/wait/run状态。
- 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_VIOLATION、AUDIT_SQL_DEPENDENCE、AUDIT_SQL_META_INFO等表,同时更新AUDIT_TASK_SQL状态(new → done/analyze_error)。
- 结果查询与导出
- AuditTaskSqlMapper.xml/AuditSqlMapper.xml 提供多维过滤(状态、SQL 文本、严重级别、SQL 类型、Violation 规则 Top N 等)。
- 导出接口将查询结果分页写 Excel,受
exportPerExcelMaxRecord、exportExcelSqlMaxLength限制。StatisticServiceImpl(由CommonTaskController调用)组合AUDIT_TASK_SQL与AUDIT_SQL_VIOLATION统计对象数量、风险级别分布、违规规则排行榜。
- 单 SQL 审核/历史
SingleSqlAuditServiceImpl.processAuditResult直接在请求线程中构建SqlAuditContext、运行规则并返回结果,同时把请求写入AUDIT_SINGLE_SQL_HISTORY,供历史查询/删除使用。
- Kafka/Pipeline 任务
- Kafka 入口经
KafkaAuditController上传 truststore(字段CERT_FILE_JKS)并验证服务器、SASL、Topic 名;AuditTaskCaptureDao将AUDIT_TASK_CAPTURE与AUDIT_TASK绑定,用KafkaCaptureHandle+KafkaClient消费 JSON(CapTureJsonParser),再复用同一套SqlAuditContext流程。- Pipeline API 则面向自动化迁移场景:可批量触发任务并查询状态。
- PL/SQL Explain
PlsqlExplainTaskController通过IAuditPlsqlSimplifyDdlService和IAuditAutoExplainService拉取 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 堆栈等。- 数据库特定的
AbstractAuditSourceDbInfo、CommonSqlAuditRule、TableStructureInfo等 POJO 在context、gauss/model、mysql/model下定义,供规则引擎访问。数据持久化 & MyBatis
主要表与 mapper:
AUDIT_TASK/AUDIT_TASK_SQL/AUDIT_SQL(AuditTaskMapper.xml, AuditTaskSqlMapper.xml, AuditSqlMapper.xml):记录任务、单条 SQL、状态与 SQL 文本。AUDIT_SQL_VIOLATION、AUDIT_SQL_DEPENDENCE、AUDIT_SQL_META_INFO(AuditSqlViolationMapper.xml, AuditSqlDependenceMapper.xml,AuditSqlMetaInfoMapper.xml, AuditSqlDependValueMapper.xml):存储命中规则片段、对象依赖及其附加值。AUDIT_DATABASES(AuditDatabaseDao.xml):数据源(IP/Port/User/SSL/状态)管理;含测试连接时间、ServiceName、SID 等字段。AUDIT_RULES、AUDIT_TEMPLATES/AUDIT_USER_TEMPLATES、AUDIT_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_RULE、AUDIT_SQL_DEPEND_VALUE、AUDIT_PROJECT等,用于文件导入与统计。
MyBatis 自定义CustomEnumTypeHandler将DbType/AuditStatus/Severity/AuditObjectType等枚举和字符串互转,SQL 广泛使用sysdate()、INSTR、limit/offset(推测底层是 MariaDB/MySQL)。配置、线程与运行参数
核心配置位于 application.properties:
- 审核任务线程池:
auditThreadPool*(核心/最大/队列/keepAlive)、auditTaskSqlThreadPool*,以及用于任务删除的deleteTaskThreadPoolSize。- IO 限制:上传 ZIP/其他文件大小、压缩包内 SQL 数量 (
fileUploadCountInZip)、maxSqlTextSize=102400、sqlDescLength=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(无代码变更,无需执行用例)。
后续建议
- 配置可观测:将 application.properties 中的线程池/文件阈值暴露到配置中心,便于不同部署环境按需缩放;同时监控 Kafka
PARTITION_OFFSET与任务队列长度。- 规则可视化:结合
AUDIT_TEMPLATE_RULES与rules/*.xml,提供导出的 JSON/报告,方便跨环境对齐自定义规则。- 清理策略:
deleteTaskOperationCron与deleteTaskIdStep固定,长期运行时建议结合任务总量动态调节,或暴露内部 API 供运维触发。- 安全:虽然
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-api、parser-common等通用库,一起构成 SqlAudit 的语法解析层。结构拆解
com/huawei/gauss/dsc/sqlparser/gaussdb/- 大量
a$xx.class、b.class、c.class等文件是 ANTLR 生成的 Lexer/Parser 及其内部状态机,名称被混淆,但可视为词法/语法自动机的各个状态节点。GaussLexer.class是词法分析入口,负责把 SQL 字符流切成 Token。.../stmt/common与.../stmt/gauss- 这些是“语句对象模型”,每个类代表一种 SQL 语句或片段,例如
GaussSelectStatement、GaussCreateTableStatement、GaussGrantStatement、GaussLoopStatement等。stmt/common提供跨方言复用的基础类型(如GaussColumnDefinition、GaussDataType),stmt/gauss则实现 GaussDB 特有的语法元素(控制块、存储过程、分区语句等)。.../nodes- 这里放的是更细粒度的语法节点(Node/Clause),用于描述列约束、分区、触发器、角色权限等。解析后,语句会引用这些节点来呈现树状结构。
.../factory与.../walkerGaussSqlStatementParser负责 orchestrate:创建GaussLexer、挂载GaussParserErrorListener、使用CustomGaussCommonTokenStream(自定义 TokenStream,用于处理大小写敏感、关键字冲突等问题),最后驱动语法树遍历。walker/GaussParserListenerImpl是 ANTLR Listener,用来把解析树转换成自定义节点/语句对象。.../stmntbuilder- 这一层把 Listener 得到的节点组装成最终的 Statement 对象。
handle/*主要负责表达式、DML、DDL 等基础构建;stmntbuilder/gauss/*处理 GaussDB 特定语法,如包、函数、分区、存储过程等。- 常见模式:
GaussParserListenerImpl获得 parse tree → 调用对应的 handler(如ExpressionHandler、DMLStatementHandler)→ 生成stmt/gauss下的语句类。.../gaussutil- 工具类(例如关键字、内置函数映射、大小写规则)。
核心逻辑流程
- 词法分析:
GaussLexer根据 GaussDB 语法定义识别关键字、标识符、常量、注释等;Lexer 的各个内部状态(a$xx.class)决定非法字符/组合的报错策略。- 语法分析:由 Parser(
gaussdb/a.class及其内部类)把 Token 序列构造成 parse tree。GaussParserErrorListener捕获语法错误,抛出GaussSQLSyntaxError。- 自定义 Token Stream:
CustomGaussCommonTokenStream对原始 Token 进行再处理,解决 GaussDB 特定的关键字歧义或大小写问题,保证后续 Builder 获取的 Token 序列一致。- Parse Tree → 对象模型:
GaussParserListenerImpl按 Listener 回调顺序遍历 parse tree,将节点缓存到StatementContainer;stmntbuilder中的 handler 识别不同语句类型并创建Gauss*Statement对象。- 语义补充:很多节点(如
GaussTableOption、GaussPartitionClause)会在 Builder 阶段设置默认值、做简单校验,保证生成的语句对象含义完整。- 输出接口:最终
GaussSqlStatementParser会返回语句对象或抛出异常,供上层(如 SqlAudit 的规则引擎)分析 SQL 结构、抽取对象依赖、判断安全规则。实际用途(结合 SqlAudit)
- SqlAudit 在抽取待审核的 SQL 时,会根据数据库类型选择对应 parser(Gauss=本 Jar)。
- 解析结果用于:
- 规则引擎:判断是否使用了禁用语法、未加 where、未命名表空间等。
- 元数据采集:抽取表/列/函数依赖,匹配
system_table.yaml。- 自动修复建议:借助语句对象树,定位问题位置并生成建议 SQL。
说明与建议
- Jar 已被混淆,若需深度理解具体算法,可用
javap -c或反编译器查看GaussSqlStatementParser、GaussParserListenerImpl、stmntbuilder/gauss/*。- 在调试 SqlAudit 时,确认
UGO_lib目录已加进 classpath,否则 Gauss SQL 将无法解析。- 如果要扩展新规则,可以直接消费该 Jar 输出的
Gauss*Statement模型,不必重写语法解析。
声明
暂不确定如果第三方直接引用GaussDB UGO的这些语法解析jar包是否会被华为告侵权,因此不建议商业软件在未经授权许可的情况下,直接引入这些jar包。但是UGO的这个设计思路是值得参考的。
