提交 c64c6a7d 编写于 作者: martianzhang's avatar martianzhang

add new heuristic rule RuleColumnNotAllowType COL.018

上级 3783b6c0
......@@ -3179,6 +3179,38 @@ func (q *Query4Audit) RuleVarcharLength() Rule {
return rule
}
// RuleColumnNotAllowType COL.018
func (q *Query4Audit) RuleColumnNotAllowType() Rule {
var rule = q.RuleOK()
if len(common.Config.ColumnNotAllowType) == 0 {
return rule
}
switch s := q.Stmt.(type) {
case *sqlparser.DDL:
switch s.Action {
case "create", "alter":
tks := ast.Tokenize(q.Query)
for _, tk := range tks {
if tk.Type == ast.TokenTypeWord {
for _, tp := range common.Config.ColumnNotAllowType {
if len(tk.Val) <= len(tp)+1 &&
strings.HasPrefix(strings.ToLower(tk.Val), strings.ToLower(tp)) {
rule = HeuristicRules["COL.018"]
break
}
}
}
if rule.Item != "OK" {
break
}
}
}
}
return rule
}
// RuleNoOSCKey KEY.002
func (q *Query4Audit) RuleNoOSCKey() Rule {
var rule = q.RuleOK()
......
......@@ -2988,6 +2988,48 @@ func TestRuleVarcharLength(t *testing.T) {
common.Log.Debug("Exiting function: %s", common.GetFunctionName())
}
// COL.018
func TestRuleColumnNotAllowType(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := [][]string{
{
"CREATE TABLE tab (a BOOLEAN);",
"CREATE TABLE tab (a BOOLEAN );",
"ALTER TABLE `tb` add column `a` BOOLEAN;",
},
{
"CREATE TABLE `tb` ( `id` varchar(1024));",
"ALTER TABLE `tb` add column `id` varchar(35);",
},
}
for _, sql := range sqls[0] {
q, err := NewQuery4Audit(sql)
if err == nil {
rule := q.RuleColumnNotAllowType()
if rule.Item != "COL.018" {
t.Error("Rule not match:", rule.Item, "Expect : COL.018")
}
} else {
t.Error("sqlparser.Parse Error:", err)
}
}
for _, sql := range sqls[1] {
q, err := NewQuery4Audit(sql)
if err == nil {
rule := q.RuleColumnNotAllowType()
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())
}
// KEY.002
func TestRuleNoOSCKey(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
......
......@@ -553,6 +553,14 @@ func init() {
Case: "CREATE TABLE tab (a varchar(3500));",
Func: (*Query4Audit).RuleVarcharLength,
},
"COL.018": {
Item: "COL.018",
Severity: "L1",
Summary: "建表语句中使用了不推荐的字段类型",
Content: "以下字段类型不被推荐使用:" + strings.Join(common.Config.ColumnNotAllowType, ","),
Case: "CREATE TABLE tab (a BOOLEAN);",
Func: (*Query4Audit).RuleColumnNotAllowType,
},
"DIS.001": {
Item: "DIS.001",
Severity: "L1",
......
......@@ -512,6 +512,16 @@ CREATE TABLE tab (a INT(1));
```sql
CREATE TABLE tab (a varchar(3500));
```
## 建表语句中使用了不推荐的字段类型
* **Item**:COL.018
* **Severity**:L1
* **Content**:以下字段类型不被推荐使用:boolean
* **Case**:
```sql
CREATE TABLE tab (a BOOLEAN);
```
## 消除不必要的 DISTINCT 条件
* **Item**:DIS.001
......
......@@ -48,6 +48,7 @@ advisor.Rule{Item:"COL.014", Severity:"L5", Summary:"为列指定了字符集",
advisor.Rule{Item:"COL.015", Severity:"L4", Summary:"TEXT 和 BLOB 类型的字段不可指定非 NULL 的默认值", Content:"MySQL 数据库中 TEXT 和 BLOB 类型的字段不可指定非 NULL 的默认值。TEXT最大长度为2^16-1个字符,MEDIUMTEXT最大长度为2^32-1个字符,LONGTEXT最大长度为2^64-1个字符。", Case:"CREATE TABLE `tbl` (`c` blob DEFAULT NULL);", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"COL.016", Severity:"L1", Summary:"整型定义建议采用 INT(10) 或 BIGINT(20)", Content:"INT(M) 在 integer 数据类型中,M 表示最大显示宽度。 在 INT(M) 中,M 的值跟 INT(M) 所占多少存储空间并无任何关系。 INT(3)、INT(4)、INT(8) 在磁盘上都是占用 4 bytes 的存储空间。", Case:"CREATE TABLE tab (a INT(1));", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"COL.017", Severity:"L2", Summary:"VARCHAR 定义长度过长", Content:"varchar 是可变长字符串,不预先分配存储空间,长度不要超过1024,如果存储长度过长 MySQL 将定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。", Case:"CREATE TABLE tab (a varchar(3500));", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"COL.018", Severity:"L1", Summary:"建表语句中使用了不推荐的字段类型", Content:"以下字段类型不被推荐使用:boolean", Case:"CREATE TABLE tab (a BOOLEAN);", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"DIS.001", Severity:"L1", Summary:"消除不必要的 DISTINCT 条件", Content:"太多DISTINCT条件是复杂的裹脚布式查询的症状。考虑将复杂查询分解成许多简单的查询,并减少DISTINCT条件的数量。如果主键列是列的结果集的一部分,则DISTINCT条件可能没有影响。", Case:"SELECT DISTINCT c.c_id,count(DISTINCT c.c_name),count(DISTINCT c.c_e),count(DISTINCT c.c_n),count(DISTINCT c.c_me),c.c_d FROM (select distinct id, name from B) as e WHERE e.country_id = c.country_id", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"DIS.002", Severity:"L3", Summary:"COUNT(DISTINCT) 多列时结果可能和你预想的不同", Content:"COUNT(DISTINCT col) 计算该列除NULL之外的不重复行数,注意 COUNT(DISTINCT col, col2) 如果其中一列全为 NULL 那么即使另一列有不同的值,也返回0。", Case:"SELECT COUNT(DISTINCT col, col2) FROM tbl;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"DIS.003", Severity:"L3", Summary:"DISTINCT * 对有主键的表没有意义", Content:"当表已经有主键时,对所有列进行 DISTINCT 的输出结果与不进行 DISTINCT 操作的结果相同,请不要画蛇添足。", Case:"SELECT DISTINCT * FROM film;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
......
......@@ -107,6 +107,7 @@ type Configuration struct {
UkPrefix string `yaml:"unique-key-prefix"` // 唯一键建议使用的前缀
MaxSubqueryDepth int `yaml:"max-subquery-depth"` // 子查询最大尝试
MaxVarcharLength int `yaml:"max-varchar-length"` // varchar最大长度
ColumnNotAllowType []string `yaml:"column-not-allow-type"` // 字段不允许使用的数据类型
// ++++++++++++++EXPLAIN检查项+++++++++++++
ExplainSQLReportType string `yaml:"explain-sql-report-type"` // EXPLAIN markdown 格式输出 SQL 样式,支持 sample, fingerprint, pretty 等
......@@ -189,6 +190,7 @@ var Config = &Configuration{
UkPrefix: "uk_",
MaxSubqueryDepth: 5,
MaxVarcharLength: 1024,
ColumnNotAllowType: []string{"boolean"},
MarkdownExtensions: 94,
MarkdownHTMLFlags: 0,
......@@ -542,6 +544,7 @@ func readCmdFlags() error {
ukPrefix := flag.String("unique-key-prefix", Config.UkPrefix, "UkPrefix")
maxSubqueryDepth := flag.Int("max-subquery-depth", Config.MaxSubqueryDepth, "MaxSubqueryDepth")
maxVarcharLength := flag.Int("max-varchar-length", Config.MaxVarcharLength, "MaxVarcharLength")
columnNotAllowType := flag.String("column-not-allow-type", strings.Join(Config.ColumnNotAllowType, ","), "ColumnNotAllowType")
// ++++++++++++++EXPLAIN检查项+++++++++++++
explainSQLReportType := flag.String("explain-sql-report-type", strings.ToLower(Config.ExplainSQLReportType), "ExplainSQLReportType [pretty, sample, fingerprint]")
explainType := flag.String("explain-type", strings.ToLower(Config.ExplainType), "ExplainType [extended, partitions, traditional]")
......@@ -674,6 +677,9 @@ func readCmdFlags() error {
Config.DryRun = *dryrun
Config.MaxPrettySQLLength = *maxPrettySQLLength
Config.MaxVarcharLength = *maxVarcharLength
if *columnNotAllowType != "" {
Config.ColumnNotAllowType = strings.Split(strings.ToLower(*columnNotAllowType), ",")
}
PrintVersion = *printVersion
PrintConfig = *printConfig
......
......@@ -68,6 +68,8 @@ index-prefix: idx_
unique-key-prefix: uk_
max-subquery-depth: 5
max-varchar-length: 1024
column-not-allow-type:
- boolean
explain-sql-report-type: pretty
explain-type: extended
explain-format: traditional
......
......@@ -48,6 +48,16 @@ bash: ./soar.linux-amd64: cannot execute binary file
请注意您操作系统类型,`soar.linux-amd64` 为 Linux 系统使用的二进制文件,`soar.darwin-amd64` 为苹果系统使用的二进制文件,`soar.windows-amd64` 是微软用户使用的二进制文件。下载文件后 Linux 和苹果用户需要为文件添加可执行权限 `chmod a+x filename`
## 命令无法找到
```bash
bash: soar: command not found
```
直接执行 `soar` 命令提示命令无法找到,请先将 soar 文件添加可执行权限 `chmod a+x soar` 然后将可以将 soar 所在路径加到[PATH](https://linuxconfig.org/linux-path-environment-variable)中,也可以将 soar 移动到已有 PATH 中。
当然在 Linux 环境下,在 soar 二进制文件所在路径运行 `./soar` 也同样可以解决您的问题。
## 提示语法错误
* 请检查SQL语句中是否出现了不配对的引号,如 `, ", '
......
......@@ -512,6 +512,16 @@ CREATE TABLE tab (a INT(1));
```sql
CREATE TABLE tab (a varchar(3500));
```
## 建表语句中使用了不推荐的字段类型
* **Item**:COL.018
* **Severity**:L1
* **Content**:以下字段类型不被推荐使用:boolean
* **Case**:
```sql
CREATE TABLE tab (a BOOLEAN);
```
## 消除不必要的 DISTINCT 条件
* **Item**:DIS.001
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册