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
MySQL SQL 拆分器在扫描存储过程时,使用「IF 后紧跟 ( 即视为 IF() 函数」的启发式规则区分控制流 IF 与函数调用。
当存储过程条件带括号时,例如:
IF ( v_lng IS NULLOR v_lat IS NULL ) AND Arg0 IS NOT NULL THEN
...
END IF;
IF 后的下一个 token 同样是 (,被误判为 IF() 函数,IfEndIfBlock 未入栈。后续在 END IF 处错误弹出外层 BeginEndBlock,END IF; 的 ; 成为语句分隔符,最外层 END; 被切到下一条 SQL。
现象:
审核/建单阶段:一条 CREATE PROCEDURE 被拆成多条(如 DROP + 残缺过程 + 孤立 END;)
上线阶段:第 1 条残缺 SQL 执行触发 MySQL 1064 语法错误,整单上线失败
临时规避: 使用 DELIMITER $$ ... END$$ 包裹存储过程定义。
修复
移除「IF 后紧跟 ( 即函数」的脆弱启发式
新增 isThenToken() 辅助函数识别 THEN 关键字
新增 ifIsControlFlowStatement():从 IF 之后前向扫描,跟踪括号深度,在深度 0 处遇到 THEN 则判定为控制流 IF 并入块栈
前向扫描结束后复位 scanner 游标,保证后续 END 检测逻辑正确
补充 TestIfControlFlowStatementInProcedure 回归用例
涉及文件:
sqle/driver/mysql/splitter/block.go
sqle/driver/mysql/splitter/splitter.go
sqle/driver/mysql/splitter/splitter_test.go
方案
flowchart TD
A["扫描到 IF token"] --> B["前向扫描: 跟踪括号深度"]
B --> C{"深度 0 处遇到 THEN?"}
C -->|是| D["控制流 IF: 压入 IfEndIfBlock"]
C -->|否| E["IF() 函数: 不入栈"]
D --> F["复位 scanner 到 IF 之后"]
E --> F
F --> G["继续块栈扫描, 体内 ; 不作为分隔符"]
Loading
判定覆盖:
场景
判定
IF (cond) AND ... THEN
控制流 IF(入栈)
IF x > 0 THEN
控制流 IF(入栈)
IF(a, b, c)
函数(不入栈)
IF(CASE WHEN x THEN 1 END, a, b)
函数(THEN 在深度 ≥ 1 被忽略)
测试结果
单元测试
go test ./sqle/driver/mysql/splitter/...
结果:通过。覆盖带括号条件、ELSEIF、触发器内 IF() 函数、嵌套 CASE ... THEN 参数等场景。
问题
MySQL SQL 拆分器在扫描存储过程时,使用「
IF后紧跟(即视为IF()函数」的启发式规则区分控制流IF与函数调用。当存储过程条件带括号时,例如:
IF后的下一个 token 同样是(,被误判为IF()函数,IfEndIfBlock未入栈。后续在END IF处错误弹出外层BeginEndBlock,END IF;的;成为语句分隔符,最外层END;被切到下一条 SQL。现象:
CREATE PROCEDURE被拆成多条(如 DROP + 残缺过程 + 孤立END;)临时规避: 使用
DELIMITER $$ ... END$$包裹存储过程定义。修复
IF后紧跟(即函数」的脆弱启发式isThenToken()辅助函数识别THEN关键字ifIsControlFlowStatement():从IF之后前向扫描,跟踪括号深度,在深度 0 处遇到THEN则判定为控制流IF并入块栈END检测逻辑正确TestIfControlFlowStatementInProcedure回归用例涉及文件:
sqle/driver/mysql/splitter/block.gosqle/driver/mysql/splitter/splitter.gosqle/driver/mysql/splitter/splitter_test.go方案
flowchart TD A["扫描到 IF token"] --> B["前向扫描: 跟踪括号深度"] B --> C{"深度 0 处遇到 THEN?"} C -->|是| D["控制流 IF: 压入 IfEndIfBlock"] C -->|否| E["IF() 函数: 不入栈"] D --> F["复位 scanner 到 IF 之后"] E --> F F --> G["继续块栈扫描, 体内 ; 不作为分隔符"]判定覆盖:
IF (cond) AND ... THENIF x > 0 THENIF(a, b, c)IF(CASE WHEN x THEN 1 END, a, b)THEN在深度 ≥ 1 被忽略)测试结果
单元测试
go test ./sqle/driver/mysql/splitter/...结果:通过。覆盖带括号条件、
ELSEIF、触发器内IF()函数、嵌套CASE ... THEN参数等场景。环境验证(DMS
http://10.186.63.138:21004/,容器dms-server-main-ee)换包验证后已回退环境至原
sqled二进制。关联 PR
#3336