You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
update biz_pbm_organization set short_name_tair=replace(short_name_tair,?,?) where system_code like (?||'%') and short_name_tair like (?||'%') and id<>? and sys_deleted=0
Activity
xlorne commentedon Sep 22, 2026
dbstream-driver 缺陷报告:UPDATE 的 SET 子句含函数调用时生成非法 SQL
com.codingapi.dbstream:dbstream-driver1.0.19(当前最新 release;1.0.19-SNAPSHOT同样未修)8.1.3.26com.dameng:DmJdbcDriver18:8.1.3.140@Modifying @Query批量 UPDATE,SET 值为函数调用1. 现象
单位管理更新「系统简称」时接口报错,日志把错误记在一条看起来完全正常的业务 UPDATE 上:
报错链路:
2. 责任代码
2.1
UpdateSQLParser.getColumnValues()—— 根因取 SET 子句(正则
(?i)\bSET\b\s+(.*?)(?=\bWHERE\b|$))后直接split(","),再对每段取split("=", 2)[0]作列名:它假设「SET 子句里逗号只用于分隔列」,但
REPLACE(col, ?, ?)/CONCAT(a, b)/SUBSTR(s, 1, 2)这类函数调用的参数之间也有逗号,于是被当成列分隔符。2.2
UpdateDBEventParser.latestSQL()/getPrimaryKeyStringValue()—— 拼出非法 SQL用 2.1 的列名拼「前镜像」(旧值)查询:
latestSQL()里 SET 列名与主键列名用String.join(",", ...)拼接,UpdateSQLParser的脏列名就这样被带进 SQL 文本。3. 最小复现(无需数据库,直接调 dbstream 解析器)
拼出的前镜像查询(非法):
对照:SET 里不含函数时解析正常(业务里日志成功的那批前镜像查询就是这种形态):
4. 数据库侧实测
在同一个达梦库上逐条执行(
DmJdbcDriver18直连):SELECT short_name_tair,?,?),ID FROM BIZ_PBM_ORGANIZATION WHERE ID IN (784812337803333) AND 1=1(dbstream 产物)SQLState 42000 / -2007,第 1 行, 第 26 列[)]附近出现错误: 语法分析出错replace(...)+ `like (?SELECT short_name_tair,ID FROM BIZ_PBM_ORGANIZATION WHERE ID IN (784812337803333) AND 1=1(黄金对照)结论:达梦支持
REPLACE、支持?||'%'、支持 SET 里的参数占位符;它报错的唯一原因是第 1 条 SQL 本身非法。第 1 条的报错文案与线上日志逐字一致,可确认线上失败的就是这条 dbstream 内部查询。
另:
IN ()空列表同样非法(见第 6 节)。5. 影响:错误归属误导,排查成本高
前镜像查询的
SQLException在PreparedStatementProxy.executeUpdate()内部(SQLRunningContext.before())抛出并向上传播,调用方 Hibernate 看到的是自己的 UPDATE 失败,于是打印
JDBC exception executing SQL [<业务 UPDATE 原文>] [<dbstream SQL 的报错>]。一条合法 SQL 顶着另一条非法 SQL 的报错,且 dbstream 内部 SQL 不出现在任何日志里(它不经过数据权限拦截器的 SQL 日志),
定位难度很大。
建议:前镜像查询失败时包一层,明确标注「dbstream 前镜像查询失败」并带上内部 SQL 原文,避免误导使用方。
6. 顺带发现的同类问题
WHERE ID IN ():latestSQL()中getPrimaryKeyStringValue()返回空集合时仍会拼出IN (),实测达梦报
第 1 行, 第 49 列[)]附近出现错误。建议prepareList为空时直接跳过前镜像查询与事件生成。DeleteDBEventParser.getWhereSQL()用正则(?i)\bWHERE\b\s+(.*)$直接截取 WHERE 文本,建议一并自查同类边界。7. 修复建议
UpdateSQLParser.getColumnValues()不要用split(","),按括号深度 + 引号状态切分 SET 项(仅在深度为 0 且不在引号内时才切),或直接改用 SQL 解析器(dbstream 生态里已有
net.sf.jsqlparser,解析Update语句取UpdateSet项最稳)。8. 复现脚本
/tmp/dmprobe/DbstreamProof.java/tmp/dmprobe/DmProbe2.java/tmp/dmprobe/DmProbe3.javaSUBSTRING(x FROM n)等)fix: 修复 UPDATE 的 SET 子句含函数调用时生成非法 SQL
chore: 升级版本至 1.0.20