MySQL规则00108优化
版本信息(Version)
v4.25.02
问题描述(Describe)
以下sql期望触发MySQL规则,实际没有
SELECT postid AS Post_Link, mdi.numimages AS numMarkdownImages, hi.numimages AS NumHtmlImages, hi.viewcount AS ViewCount, hi.score AS Score, hi.creationdate AS CreationDate FROM ( SELECT postid, LENGTH(markdown) - LENGTH(REPLACE(markdown, '![', 'X')) AS numimages FROM ( SELECT postid, markdown FROM ( SELECT ph.postid AS postid, ph.text AS markdown FROM posthistory AS ph WHERE ph.postid IN ( SELECT postid FROM ( SELECT postid FROM posthistory WHERE posthistorytypeid IN (2,5,8) ) AS sub1 ) ) AS sub2 ) AS sub3 ) AS mdi JOIN ( SELECT id, creationdate, score, viewcount, LENGTH(body) - LENGTH(REPLACE(body, '<img src', '<img sr')) AS numimages FROM ( SELECT * FROM posts ) AS sub4 ) AS hi ON mdi.postid = hi.id WHERE mdi.numimages > 0 AND mdi.numimages > hi.numimages
截图或日志(Log)
如何复现(To Reproduce)
问题原因
解决方案
变更影响面
受影响的模块或功能
外部引用的潜在问题或风险
版本兼容性
测试建议
问题原因
当前的GetSubquery函数无法提取 FROM 子句中嵌套的子查询,原因是解析器不认为 FROM 子句中嵌套的子查询是子查询。
解决方案
对于 FROM 子句中的子查询,判断是否是 ast.TableSource 类型,并进一步检查是否包含 ast.SelectStmt。如果符合条件,将 SelectStmt 包装为 ast.SubqueryExpr 形式并统一处理。这样修改后,GetSubquery函数就可以提取出FROM 子句中的子查询了。
变更影响面
受影响的模块或功能 MYSQL审核
外部引用的潜在问题或风险 影响了所有带有子查询的规则。修改后有两个单元测试失败
1.rule_00108_test.go 中的case 16
runAIRuleCase(rule, t, "case 16: SELECT语句中使用JOIN的ON条件中嵌套子查询6层, 但是实际扫描表的子查询只有2次,因此不算违规",
"SELECT st1.id FROM st1 JOIN st_class ON st1.cid in (SELECT cid FROM (SELECT cid FROM (SELECT cid FROM (SELECT cid FROM (SELECT cid FROM st_class WHERE cname in (SELECT cname FROM st_class WHERE cname = 'class2')) AS sub1) AS sub2) AS sub3) AS sub4);",
session.NewAIMockContext().WithSQL("CREATE TABLE st1 (id INT, cid INT); CREATE TABLE st_class (cid INT, cname VARCHAR(50));"),
nil, newTestResult())
分析: 这条语句中有6层嵌套子查询,但是根据实际扫描表的子查询只有2次,所以认为不算违规,这显然不正确。子查询的定义就是SQL语句中包含另一个SELECT查询, 并不在乎这个子查询是否扫描了表。所以这个单元测试应改成违规。
2.rule_00109_test.go 中的case 9
// 特殊情况:解析器好像对from中嵌套的子查询 ,会被视作为属于非实际路由的子查询,因此没有累计(相当于只是个外壳嵌套而已,因此这里可以视作为不违规
runAIRuleCase(rule, t, "case 9: SELECT ... UNION ALL SELECT语句中的子查询使用LIMIT,但是这里子查询其实是可以直接push到外层的,可以不算做子查询",
"SELECT * FROM (SELECT id FROM products LIMIT 1) AS sub UNION ALL SELECT * FROM products;",
session.NewAIMockContext().
WithSQL("CREATE TABLE products (id INT, name VARCHAR(100));"),
nil, newTestResult())
分析:rule_00109是禁止在子查询中使用LIMIT,这个sql 的FROM字句中的子查询中使用了LIMIT,应该视作违规。
版本兼容性 兼容
测试建议 在测试 SQL 中,针对带有子查询的规则,建议加入 FROM 子句中嵌套子查询的情况。