sqle icon indicating copy to clipboard operation
sqle copied to clipboard

MySQL规则00108优化

Open waterdrink opened this issue 9 months ago • 1 comments

版本信息(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)

问题原因

解决方案

变更影响面

受影响的模块或功能

外部引用的潜在问题或风险

版本兼容性

测试建议

waterdrink avatar Mar 24 '25 02:03 waterdrink

问题原因

当前的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 子句中嵌套子查询的情况。

huajianxiaowanzi avatar Aug 13 '25 16:08 huajianxiaowanzi