diff --git a/advisor/heuristic.go b/advisor/heuristic.go index 3efb1b42978ea3b922b89435c286c6694f758145..02699f831ac679aea4a8d4c26945c97d4d5d304e 100644 --- a/advisor/heuristic.go +++ b/advisor/heuristic.go @@ -22,6 +22,7 @@ import ( "regexp" "strconv" "strings" + "unicode/utf8" "github.com/XiaoMi/soar/ast" "github.com/XiaoMi/soar/common" @@ -1393,6 +1394,22 @@ func (q *Query4Audit) RulePluralWord() Rule { return rule } +// RuleMultiBytesWord KWR.004 +func (q *Query4Audit) RuleMultiBytesWord() Rule { + // TODO: 目前使用 utf8 字符集检查,其他字符集输入可能会有问题 + var rule = q.RuleOK() + for _, tk := range ast.Tokenize(q.Query) { + switch tk.Type { + case ast.TokenTypeBacktickQuote, ast.TokenTypeWord: + if utf8.RuneCountInString(tk.Val) != len(tk.Val) { + rule = HeuristicRules["KWR.004"] + } + default: + } + } + return rule +} + // RuleInsertSelect LCK.001 func (q *Query4Audit) RuleInsertSelect() Rule { var rule = q.RuleOK() @@ -3177,7 +3194,7 @@ func (q *Query4Audit) RuleStandardName() Rule { rule = HeuristicRules["STA.004"] } case ast.TokenTypeWord: - // TOKEN_TYPE_WORD中处理连续下划线的情况,其他情况容易误伤 + // TOKEN_TYPE_WORD 中处理连续下划线的情况,其他情况容易误伤 if strings.Contains(tk.Val, "__") { rule = HeuristicRules["STA.004"] } diff --git a/advisor/heuristic_test.go b/advisor/heuristic_test.go index 6c0be851cc2d4e103ce9593d62cd21635a1dda42..82c702b460f7a11149519243c110837642bb3f79 100644 --- a/advisor/heuristic_test.go +++ b/advisor/heuristic_test.go @@ -1035,6 +1035,44 @@ func TestRulePluralWord(t *testing.T) { common.Log.Debug("Exiting function: %s", common.GetFunctionName()) } +// KWR.004 +func TestRuleMultiBytesWord(t *testing.T) { + common.Log.Debug("Entering function: %s", common.GetFunctionName()) + sqls := [][]string{ + { + "select col as 列 from tb", + "select col as `列` from tb", + }, + { + "select col as c from tb", + "select '列'", + }, + } + for _, sql := range sqls[0] { + q, err := NewQuery4Audit(sql) + if err == nil { + rule := q.RuleMultiBytesWord() + if rule.Item != "KWR.004" { + t.Error("Rule not match:", rule.Item, "Expect : KWR.004") + } + } else { + t.Error("sqlparser.Parse Error:", err) + } + } + for _, sql := range sqls[1] { + q, err := NewQuery4Audit(sql) + if err == nil { + rule := q.RuleMultiBytesWord() + if rule.Item != "OK" { + t.Error("Rule not match:", rule.Item, "Expect : OK") + } + } else { + t.Error("sqlparser.Parse Error:", err) + } + } + common.Log.Debug("Exiting function: %s", common.GetFunctionName()) +} + // LCK.001 func TestRuleInsertSelect(t *testing.T) { common.Log.Debug("Entering function: %s", common.GetFunctionName()) diff --git a/advisor/rules.go b/advisor/rules.go index b5d080ba6ebf771a2b38f872b9cfd1c8bd4fb914..f4c48588fc0d354d8b7f80daa9ae982a5236b378 100644 --- a/advisor/rules.go +++ b/advisor/rules.go @@ -797,6 +797,14 @@ func init() { Case: "CREATE TABLE tbl ( `books` int )", Func: (*Query4Audit).RulePluralWord, }, + "KWR.004": { + Item: "KWR.004", + Severity: "L1", + Summary: "不建议使用使用多字节编码字符(中文)命名", + Content: `为库、表、列、别名命名时建议使用英文,数字,下划线等字符,不建议使用中文或其他多字节编码字符。`, + Case: "select col as 列 from tb", + Func: (*Query4Audit).RuleMultiBytesWord, + }, "LCK.001": { Item: "LCK.001", Severity: "L3", diff --git a/advisor/testdata/TestListHeuristicRules.golden b/advisor/testdata/TestListHeuristicRules.golden index bf63b17bdd4f840f21a4dfb47c460036465cb189..bf09a2ecaa33c35044401a05f52d7130d1413ab5 100644 --- a/advisor/testdata/TestListHeuristicRules.golden +++ b/advisor/testdata/TestListHeuristicRules.golden @@ -812,6 +812,16 @@ CREATE TABLE tbl ( `select` int ) ```sql CREATE TABLE tbl ( `books` int ) ``` +## 不建议使用使用多字节编码字符(中文)命名 + +* **Item**:KWR.004 +* **Severity**:L1 +* **Content**:为库、表、列、别名命名时建议使用英文,数字,下划线等字符,不建议使用中文或其他多字节编码字符。 +* **Case**: + +```sql +select col as 列 from tb +``` ## INSERT INTO xx SELECT 加锁粒度较大请谨慎 * **Item**:LCK.001 diff --git a/advisor/testdata/TestMergeConflictHeuristicRules.golden b/advisor/testdata/TestMergeConflictHeuristicRules.golden index 3c88360cad219e3c35d2604989066de9e2ed0de3..1f5234919422ab6b7b120bc20bd4b583cf504d2f 100644 --- a/advisor/testdata/TestMergeConflictHeuristicRules.golden +++ b/advisor/testdata/TestMergeConflictHeuristicRules.golden @@ -75,6 +75,7 @@ advisor.Rule{Item:"KEY.009", Severity:"L0", Summary:"添加唯一索引前请注 advisor.Rule{Item:"KWR.001", Severity:"L2", Summary:"SQL_CALC_FOUND_ROWS 效率低下", Content:"因为 SQL_CALC_FOUND_ROWS 不能很好地扩展,所以可能导致性能问题; 建议业务使用其他策略来替代 SQL_CALC_FOUND_ROWS 提供的计数功能,比如:分页结果展示等。", Case:"select SQL_CALC_FOUND_ROWS col from tbl where id>1000", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"KWR.002", Severity:"L2", Summary:"不建议使用 MySQL 关键字做列名或表名", Content:"当使用关键字做为列名或表名时程序需要对列名和表名进行转义,如果疏忽被将导致请求无法执行。", Case:"CREATE TABLE tbl ( `select` int )", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"KWR.003", Severity:"L1", Summary:"不建议使用复数做列名或表名", Content:"表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数形式,符合表达习惯。", Case:"CREATE TABLE tbl ( `books` int )", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} +advisor.Rule{Item:"KWR.004", Severity:"L1", Summary:"不建议使用使用多字节编码字符(中文)命名", Content:"为库、表、列、别名命名时建议使用英文,数字,下划线等字符,不建议使用中文或其他多字节编码字符。", Case:"select col as 列 from tb", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"LCK.001", Severity:"L3", Summary:"INSERT INTO xx SELECT 加锁粒度较大请谨慎", Content:"INSERT INTO xx SELECT 加锁粒度较大请谨慎", Case:"INSERT INTO tbl SELECT * FROM tbl2;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"LCK.002", Severity:"L3", Summary:"请慎用 INSERT ON DUPLICATE KEY UPDATE", Content:"当主键为自增键时使用 INSERT ON DUPLICATE KEY UPDATE 可能会导致主键出现大量不连续快速增长,导致主键快速溢出无法继续写入。极端情况下还有可能导致主从数据不一致。", Case:"INSERT INTO t1(a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=c+1;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} advisor.Rule{Item:"LIT.001", Severity:"L2", Summary:"用字符类型存储IP地址", Content:"字符串字面上看起来像IP地址,但不是 INET_ATON() 的参数,表示数据被存储为字符而不是整数。将IP地址存储为整数更为有效。", Case:"insert into tbl (IP,name) values('10.20.306.122','test')", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}} diff --git a/doc/heuristic.md b/doc/heuristic.md index bf63b17bdd4f840f21a4dfb47c460036465cb189..bf09a2ecaa33c35044401a05f52d7130d1413ab5 100644 --- a/doc/heuristic.md +++ b/doc/heuristic.md @@ -812,6 +812,16 @@ CREATE TABLE tbl ( `select` int ) ```sql CREATE TABLE tbl ( `books` int ) ``` +## 不建议使用使用多字节编码字符(中文)命名 + +* **Item**:KWR.004 +* **Severity**:L1 +* **Content**:为库、表、列、别名命名时建议使用英文,数字,下划线等字符,不建议使用中文或其他多字节编码字符。 +* **Case**: + +```sql +select col as 列 from tb +``` ## INSERT INTO xx SELECT 加锁粒度较大请谨慎 * **Item**:LCK.001 diff --git a/vendor/vendor.json b/vendor/vendor.json index 2d3a6e8577b9be6c771040b220594c44cae82cbf..ce45bcde7a6a86a8a25919c14e41c4a3791d94f3 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1034,68 +1034,68 @@ { "checksumSHA1": "q7Bd5YJHsxvzEpiOBaYn+wEpqyU=", "path": "vitess.io/vitess", - "revision": "b0d2eee6a26cd87bbc748e2bd07fefaa44ef59be", - "revisionTime": "2018-11-19T18:27:10Z" + "revision": "32dd398dd5459e5cf15904db35aba7460519b612", + "revisionTime": "2018-11-22T02:19:16Z" }, { "checksumSHA1": "aKn1oKcY74N8TRLm3Ayt7Q4bbI4=", "path": "vitess.io/vitess/go/bytes2", - "revision": "b0d2eee6a26cd87bbc748e2bd07fefaa44ef59be", - "revisionTime": "2018-11-19T18:27:10Z" + "revision": "32dd398dd5459e5cf15904db35aba7460519b612", + "revisionTime": "2018-11-22T02:19:16Z" }, { "checksumSHA1": "JVCEN4UGRmg3TofIBdzZMZ3G0Ww=", "path": "vitess.io/vitess/go/hack", - "revision": "b0d2eee6a26cd87bbc748e2bd07fefaa44ef59be", - "revisionTime": "2018-11-19T18:27:10Z" + "revision": "32dd398dd5459e5cf15904db35aba7460519b612", + "revisionTime": "2018-11-22T02:19:16Z" }, { "checksumSHA1": "e1WJ7vCnVrlQQQlc6n/FewCDMso=", "path": "vitess.io/vitess/go/sqltypes", - "revision": "b0d2eee6a26cd87bbc748e2bd07fefaa44ef59be", - "revisionTime": "2018-11-19T18:27:10Z" + "revision": "32dd398dd5459e5cf15904db35aba7460519b612", + "revisionTime": "2018-11-22T02:19:16Z" }, { "checksumSHA1": "ntFIQYkBS51G6y+FEkjFW40+HOU=", "path": "vitess.io/vitess/go/vt/log", - "revision": "b0d2eee6a26cd87bbc748e2bd07fefaa44ef59be", - "revisionTime": "2018-11-19T18:27:10Z" + "revision": "32dd398dd5459e5cf15904db35aba7460519b612", + "revisionTime": "2018-11-22T02:19:16Z" }, { "checksumSHA1": "XozR8bmeSR5KTe/nlUJkpJY2HKI=", "path": "vitess.io/vitess/go/vt/proto/query", - "revision": "b0d2eee6a26cd87bbc748e2bd07fefaa44ef59be", - "revisionTime": "2018-11-19T18:27:10Z" + "revision": "32dd398dd5459e5cf15904db35aba7460519b612", + "revisionTime": "2018-11-22T02:19:16Z" }, { "checksumSHA1": "OnWsUHLDKcO3spwH0jD55SvKD24=", "path": "vitess.io/vitess/go/vt/proto/topodata", - "revision": "b0d2eee6a26cd87bbc748e2bd07fefaa44ef59be", - "revisionTime": "2018-11-19T18:27:10Z" + "revision": "32dd398dd5459e5cf15904db35aba7460519b612", + "revisionTime": "2018-11-22T02:19:16Z" }, { "checksumSHA1": "sBAuZ/itMR8U8qbK4yLHxkP6Cpc=", "path": "vitess.io/vitess/go/vt/proto/vtgate", - "revision": "b0d2eee6a26cd87bbc748e2bd07fefaa44ef59be", - "revisionTime": "2018-11-19T18:27:10Z" + "revision": "32dd398dd5459e5cf15904db35aba7460519b612", + "revisionTime": "2018-11-22T02:19:16Z" }, { "checksumSHA1": "pLWM+SPGZs3k+IhjktE/cGUlpM0=", "path": "vitess.io/vitess/go/vt/proto/vtrpc", - "revision": "b0d2eee6a26cd87bbc748e2bd07fefaa44ef59be", - "revisionTime": "2018-11-19T18:27:10Z" + "revision": "32dd398dd5459e5cf15904db35aba7460519b612", + "revisionTime": "2018-11-22T02:19:16Z" }, { "checksumSHA1": "2ZBC/pPjs13cocUf8PoMSvAO5u4=", "path": "vitess.io/vitess/go/vt/sqlparser", - "revision": "b0d2eee6a26cd87bbc748e2bd07fefaa44ef59be", - "revisionTime": "2018-11-19T18:27:10Z" + "revision": "32dd398dd5459e5cf15904db35aba7460519b612", + "revisionTime": "2018-11-22T02:19:16Z" }, { "checksumSHA1": "oF4XzuOzwvj1iduX/lYqNSyY/HM=", "path": "vitess.io/vitess/go/vt/vterrors", - "revision": "b0d2eee6a26cd87bbc748e2bd07fefaa44ef59be", - "revisionTime": "2018-11-19T18:27:10Z" + "revision": "32dd398dd5459e5cf15904db35aba7460519b612", + "revisionTime": "2018-11-22T02:19:16Z" } ], "rootPath": "github.com/XiaoMi/soar"