提交 a90d2064 编写于 作者: L liipx

add COL.007

上级 1bdc431a
......@@ -3246,6 +3246,32 @@ func (q *Query4Audit) RuleTooManyFields() Rule {
return rule
}
// RuleMaxTextColsCount COL.007
func (q *Query4Audit) RuleMaxTextColsCount() Rule {
var textColsCount int
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeLongBlob, mysql.TypeMediumBlob, mysql.TypeTinyBlob:
textColsCount++
}
}
}
}
}
if textColsCount > common.Config.MaxTextColsCount {
rule = HeuristicRules["COL.007"]
}
return rule
}
// RuleAllowEngine TBL.002
func (q *Query4Audit) RuleAllowEngine() Rule {
var rule = q.RuleOK()
......
......@@ -3091,6 +3091,28 @@ func TestRuleTooManyFields(t *testing.T) {
common.Log.Debug("Exiting function: %s", common.GetFunctionName())
}
// COL.007
func TestRuleMaxTextColsCount(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := []string{
"create table tbl (a int, b text, c blob, d text);",
}
common.Config.MaxColCount = 0
for _, sql := range sqls {
q, err := NewQuery4Audit(sql)
if err == nil {
rule := q.RuleMaxTextColsCount()
if rule.Item != "COL.007" {
t.Error("Rule not match:", rule.Item, "Expect : COL.007")
}
} else {
t.Error("sqlparser.Parse Error:", err)
}
}
common.Log.Debug("Exiting function: %s", common.GetFunctionName())
}
// TBL.002
func TestRuleAllowEngine(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
......
......@@ -471,6 +471,14 @@ func init() {
Case: "CREATE TABLE tbl ( cols ....);",
Func: (*Query4Audit).RuleTooManyFields,
},
"COL.007": {
Item: "COL.007",
Severity: "L3",
Summary: "表中包含有太多的 text/blob 列",
Content: fmt.Sprintf(`表中包含超过%d个的 text/blob 列`, common.Config.MaxTextColsCount),
Case: "CREATE TABLE tbl ( cols ....);",
Func: (*Query4Audit).RuleTooManyFields,
},
"COL.008": {
Item: "COL.008",
Severity: "L1",
......
......@@ -409,6 +409,16 @@ CREATE TABLE tbl (col int) ENGINE=InnoDB;
* **Content**:表中包含有太多的列
* **Case**:
```sql
CREATE TABLE tbl ( cols ....);
```
## 表中包含有太多的 text/blob 列
* **Item**:COL.007
* **Severity**:L3
* **Content**:表中包含超过2个的 text/blob 列
* **Case**:
```sql
CREATE TABLE tbl ( cols ....);
```
......
......@@ -38,6 +38,7 @@ advisor.Rule{Item:"COL.003", Severity:"L2", Summary:"建议修改自增 ID 为
advisor.Rule{Item:"COL.004", Severity:"L1", Summary:"请为列添加默认值", Content:"请为列添加默认值,如果是 ALTER 操作,请不要忘记将原字段的默认值写上。字段无默认值,当表较大时无法在线变更表结构。", Case:"CREATE TABLE tbl (col int) ENGINE=InnoDB;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"COL.005", Severity:"L1", Summary:"列未添加注释", Content:"建议对表中每个列添加注释,来明确每个列在表中的含义及作用。", Case:"CREATE TABLE tbl (col int) ENGINE=InnoDB;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"COL.006", Severity:"L3", Summary:"表中包含有太多的列", Content:"表中包含有太多的列", Case:"CREATE TABLE tbl ( cols ....);", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"COL.007", Severity:"L3", Summary:"表中包含有太多的 text/blob 列", Content:"表中包含超过2个的 text/blob 列", Case:"CREATE TABLE tbl ( cols ....);", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"COL.008", Severity:"L1", Summary:"可使用 VARCHAR 代替 CHAR, VARBINARY 代替 BINARY", Content:"为首先变长字段存储空间小,可以节省存储空间。其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。", Case:"create table t1(id int,name char(20),last_time date)", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"COL.009", Severity:"L2", Summary:"建议使用精确的数据类型", Content:"实际上,任何使用 FLOAT, REAL 或 DOUBLE PRECISION 数据类型的设计都有可能是反模式。大多数应用程序使用的浮点数的取值范围并不需要达到IEEE 754标准所定义的最大/最小区间。在计算总量时,非精确浮点数所积累的影响是严重的。使用 SQL 中的 NUMERIC 或 DECIMAL 类型来代替 FLOAT 及其类似的数据类型进行固定精度的小数存储。这些数据类型精确地根据您定义这一列时指定的精度来存储数据。尽可能不要使用浮点数。", Case:"CREATE TABLE tab2 (p_id BIGINT UNSIGNED NOT NULL,a_id BIGINT UNSIGNED NOT NULL,hours float not null,PRIMARY KEY (p_id, a_id))", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"COL.010", Severity:"L2", Summary:"不建议使用 ENUM 数据类型", Content:"ENUM 定义了列中值的类型,使用字符串表示 ENUM 里的值时,实际存储在列中的数据是这些值在定义时的序数。因此,这列的数据是字节对齐的,当您进行一次排序查询时,结果是按照实际存储的序数值排序的,而不是按字符串值的字母顺序排序的。这可能不是您所希望的。没有什么语法支持从 ENUM 或者 check 约束中添加或删除一个值;您只能使用一个新的集合重新定义这一列。如果您打算废弃一个选项,您可能会为历史数据而烦恼。作为一种策略,改变元数据——也就是说,改变表和列的定义——应该是不常见的,并且要注意测试和质量保证。有一个更好的解决方案来约束一列中的可选值:创建一张检查表,每一行包含一个允许在列中出现的候选值;然后在引用新表的旧表上声明一个外键约束。", Case:"create table tab1(status ENUM('new','in progress','fixed'))", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
......
......@@ -91,6 +91,7 @@ type Configuration struct {
MaxGroupByColsCount int `yaml:"max-group-by-cols-count"` // 单条 SQL 中 GroupBy 包含列的最大数量
MaxDistinctCount int `yaml:"max-distinct-count"` // 单条 SQL 中 Distinct 的最大数量
MaxIdxColsCount int `yaml:"max-index-cols-count"` // 复合索引中包含列的最大数量
MaxTextColsCount int `yaml:"max-text-cols-count"` // 表中含有的 text/blob 列的最大数量
MaxTotalRows int64 `yaml:"max-total-rows"` // 计算散粒度时,当数据行数大于 MaxTotalRows 即开启数据库保护模式,散粒度返回结果可信度下降
MaxQueryCost int64 `yaml:"max-query-cost"` // last_query_cost 超过该值时将给予警告
SpaghettiQueryLength int `yaml:"spaghetti-query-length"` // SQL最大长度警告,超过该长度会给警告
......@@ -167,6 +168,7 @@ var Config = &Configuration{
MaxGroupByColsCount: 5,
MaxDistinctCount: 5,
MaxIdxColsCount: 5,
MaxTextColsCount: 2,
MaxIdxBytesPerColumn: 767,
MaxIdxBytes: 3072,
MaxTotalRows: 9999999,
......@@ -528,6 +530,7 @@ func readCmdFlags() error {
maxGroupByColsCount := flag.Int("max-group-by-cols-count", Config.MaxGroupByColsCount, "MaxGroupByColsCount, 单条 SQL 中 GroupBy 包含列的最大数量")
maxDistinctCount := flag.Int("max-distinct-count", Config.MaxDistinctCount, "MaxDistinctCount, 单条 SQL 中 Distinct 的最大数量")
maxIdxColsCount := flag.Int("max-index-cols-count", Config.MaxIdxColsCount, "MaxIdxColsCount, 复合索引中包含列的最大数量")
maxTextColsCount := flag.Int("max-texst-cols-count", Config.MaxTextColsCount, "MaxTextColsCount, 表中含有的 text/blob 列的最大数量")
maxTotalRows := flag.Int64("max-total-rows", Config.MaxTotalRows, "MaxTotalRows, 计算散粒度时,当数据行数大于MaxTotalRows即开启数据库保护模式,不计算散粒度")
maxQueryCost := flag.Int64("max-query-cost", Config.MaxQueryCost, "MaxQueryCost, last_query_cost 超过该值时将给予警告")
spaghettiQueryLength := flag.Int("spaghetti-query-length", Config.SpaghettiQueryLength, "SpaghettiQueryLength, SQL最大长度警告,超过该长度会给警告")
......@@ -626,6 +629,7 @@ func readCmdFlags() error {
Config.MaxIdxColsCount = 16
}
Config.MaxTextColsCount = *maxTextColsCount
Config.MaxIdxBytesPerColumn = *maxIdxBytesPerColumn
Config.MaxIdxBytes = *maxIdxBytes
if *allowCharsets != "" {
......
......@@ -43,11 +43,11 @@ func LoggerInit() {
func() { _ = Log.DelLogger(logs.AdapterFile) }()
logConfig := map[string]interface{}{
"filename": Config.LogOutput,
"level": 7,
"level": 7,
"maxlines": 0,
"maxsize": 0,
"daily": false,
"maxdays": 0,
"maxsize": 0,
"daily": false,
"maxdays": 0,
}
logConfigJson, _ := json.Marshal(logConfig)
err := Log.SetLogger(logs.AdapterFile, fmt.Sprintf(string(logConfigJson)))
......
......@@ -409,6 +409,16 @@ CREATE TABLE tbl (col int) ENGINE=InnoDB;
* **Content**:表中包含有太多的列
* **Case**:
```sql
CREATE TABLE tbl ( cols ....);
```
## 表中包含有太多的 text/blob 列
* **Item**:COL.007
* **Severity**:L3
* **Content**:表中包含超过2个的 text/blob 列
* **Case**:
```sql
CREATE TABLE tbl ( cols ....);
```
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册