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

Merge branch 'dev' of github.com:XiaoMi/soar into dev

...@@ -832,7 +832,7 @@ func (q *Query4Audit) RuleAddDefaultValue() Rule { ...@@ -832,7 +832,7 @@ func (q *Query4Audit) RuleAddDefaultValue() Rule {
} }
switch c.Tp.Tp { switch c.Tp.Tp {
case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeJSON:
colDefault = true colDefault = true
} }
...@@ -855,7 +855,7 @@ func (q *Query4Audit) RuleAddDefaultValue() Rule { ...@@ -855,7 +855,7 @@ func (q *Query4Audit) RuleAddDefaultValue() Rule {
} }
switch c.Tp.Tp { switch c.Tp.Tp {
case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeJSON:
colDefault = true colDefault = true
} }
...@@ -2729,8 +2729,14 @@ func (q *Query4Audit) RuleAlterCharset() Rule { ...@@ -2729,8 +2729,14 @@ func (q *Query4Audit) RuleAlterCharset() Rule {
for _, option := range spec.Options { for _, option := range spec.Options {
if option.Tp == tidb.TableOptionCharset || if option.Tp == tidb.TableOptionCharset ||
option.Tp == tidb.TableOptionCollate { option.Tp == tidb.TableOptionCollate {
rule = HeuristicRules["ALT.001"] //增加CONVERT TO的判断
break convertReg, _ := regexp.Compile("convert to")
if convertReg.Match([]byte(strings.ToLower(q.Query))) {
break
} else {
rule = HeuristicRules["ALT.001"]
break
}
} }
} }
} }
...@@ -2807,7 +2813,7 @@ func (q *Query4Audit) RuleBLOBNotNull() Rule { ...@@ -2807,7 +2813,7 @@ func (q *Query4Audit) RuleBLOBNotNull() Rule {
continue continue
} }
switch col.Tp.Tp { switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeJSON:
for _, opt := range col.Options { for _, opt := range col.Options {
if opt.Tp == tidb.ColumnOptionNotNull { if opt.Tp == tidb.ColumnOptionNotNull {
rule = HeuristicRules["COL.012"] rule = HeuristicRules["COL.012"]
...@@ -2830,7 +2836,7 @@ func (q *Query4Audit) RuleBLOBNotNull() Rule { ...@@ -2830,7 +2836,7 @@ func (q *Query4Audit) RuleBLOBNotNull() Rule {
continue continue
} }
switch col.Tp.Tp { switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeJSON:
for _, opt := range col.Options { for _, opt := range col.Options {
if opt.Tp == tidb.ColumnOptionNotNull { if opt.Tp == tidb.ColumnOptionNotNull {
rule = HeuristicRules["COL.012"] rule = HeuristicRules["COL.012"]
...@@ -3157,7 +3163,8 @@ func (q *Query4Audit) RuleColumnWithCharset() Rule { ...@@ -3157,7 +3163,8 @@ func (q *Query4Audit) RuleColumnWithCharset() Rule {
for _, tk := range tks { for _, tk := range tks {
if tk.Type == ast.TokenTypeWord { if tk.Type == ast.TokenTypeWord {
switch strings.TrimSpace(strings.ToLower(tk.Val)) { switch strings.TrimSpace(strings.ToLower(tk.Val)) {
case "national", "nvarchar", "nchar", "nvarchar(", "nchar(", "character": //character移到后面检查
case "national", "nvarchar", "nchar", "nvarchar(", "nchar(":
rule = HeuristicRules["COL.014"] rule = HeuristicRules["COL.014"]
return rule return rule
} }
...@@ -3173,6 +3180,16 @@ func (q *Query4Audit) RuleColumnWithCharset() Rule { ...@@ -3173,6 +3180,16 @@ func (q *Query4Audit) RuleColumnWithCharset() Rule {
continue continue
} }
if col.Tp.Charset != "" || col.Tp.Collate != "" { if col.Tp.Charset != "" || col.Tp.Collate != "" {
if col.Tp.Charset == "binary" || col.Tp.Collate == "binary" {
continue
} else {
rule = HeuristicRules["COL.014"]
break
}
}
//在这里检查character
characterReg, _ := regexp.Compile("character set")
if characterReg.Match([]byte(strings.ToLower(q.Query))) {
rule = HeuristicRules["COL.014"] rule = HeuristicRules["COL.014"]
break break
} }
...@@ -3187,6 +3204,15 @@ func (q *Query4Audit) RuleColumnWithCharset() Rule { ...@@ -3187,6 +3204,15 @@ func (q *Query4Audit) RuleColumnWithCharset() Rule {
continue continue
} }
if col.Tp.Charset != "" || col.Tp.Collate != "" { if col.Tp.Charset != "" || col.Tp.Collate != "" {
if col.Tp.Charset == "binary" || col.Tp.Collate == "binary" {
continue
} else {
rule = HeuristicRules["COL.014"]
break
}
}
characterReg, _ := regexp.Compile("character set")
if characterReg.Match([]byte(strings.ToLower(q.Query))) {
rule = HeuristicRules["COL.014"] rule = HeuristicRules["COL.014"]
break break
} }
...@@ -3391,7 +3417,7 @@ func (q *Query4Audit) RuleBlobDefaultValue() Rule { ...@@ -3391,7 +3417,7 @@ func (q *Query4Audit) RuleBlobDefaultValue() Rule {
continue continue
} }
switch col.Tp.Tp { switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeMediumBlob, mysql.TypeTinyBlob, mysql.TypeLongBlob: case mysql.TypeBlob, mysql.TypeMediumBlob, mysql.TypeTinyBlob, mysql.TypeLongBlob, mysql.TypeJSON:
for _, opt := range col.Options { for _, opt := range col.Options {
if opt.Tp == tidb.ColumnOptionDefaultValue && opt.Expr.GetType().Tp != mysql.TypeNull { if opt.Tp == tidb.ColumnOptionDefaultValue && opt.Expr.GetType().Tp != mysql.TypeNull {
rule = HeuristicRules["COL.015"] rule = HeuristicRules["COL.015"]
...@@ -3410,7 +3436,7 @@ func (q *Query4Audit) RuleBlobDefaultValue() Rule { ...@@ -3410,7 +3436,7 @@ func (q *Query4Audit) RuleBlobDefaultValue() Rule {
continue continue
} }
switch col.Tp.Tp { switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeMediumBlob, mysql.TypeTinyBlob, mysql.TypeLongBlob: case mysql.TypeBlob, mysql.TypeMediumBlob, mysql.TypeTinyBlob, mysql.TypeLongBlob, mysql.TypeJSON:
for _, opt := range col.Options { for _, opt := range col.Options {
if opt.Tp == tidb.ColumnOptionDefaultValue && opt.Expr.GetType().Tp != mysql.TypeNull { if opt.Tp == tidb.ColumnOptionDefaultValue && opt.Expr.GetType().Tp != mysql.TypeNull {
rule = HeuristicRules["COL.015"] rule = HeuristicRules["COL.015"]
......
...@@ -3265,17 +3265,24 @@ func TestRuleBlobDefaultValue(t *testing.T) { ...@@ -3265,17 +3265,24 @@ func TestRuleBlobDefaultValue(t *testing.T) {
sqls := [][]string{ sqls := [][]string{
{ {
"CREATE TABLE `tb` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `c` blob NOT NULL DEFAULT '', PRIMARY KEY (`id`));", "CREATE TABLE `tb` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `c` blob NOT NULL DEFAULT '', PRIMARY KEY (`id`));",
"CREATE TABLE `tb` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `c` json NOT NULL DEFAULT '', PRIMARY KEY (`id`));",
"alter table `tb` add column `c` blob NOT NULL DEFAULT '';", "alter table `tb` add column `c` blob NOT NULL DEFAULT '';",
"alter table `tb` add column `c` json NOT NULL DEFAULT '';",
}, },
{ {
"CREATE TABLE `tb` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `c` blob NOT NULL, PRIMARY KEY (`id`));", "CREATE TABLE `tb` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `c` blob NOT NULL, PRIMARY KEY (`id`));",
"CREATE TABLE `tb` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `c` json NOT NULL, PRIMARY KEY (`id`));",
"CREATE TABLE `tb` (`col` text NOT NULL);", "CREATE TABLE `tb` (`col` text NOT NULL);",
"alter table `tb` add column `c` blob NOT NULL;", "alter table `tb` add column `c` blob NOT NULL;",
"alter table `tb` add column `c` json NOT NULL;",
"ALTER TABLE tb ADD COLUMN a BLOB DEFAULT NULL", "ALTER TABLE tb ADD COLUMN a BLOB DEFAULT NULL",
"ALTER TABLE tb ADD COLUMN a JSON DEFAULT NULL",
"CREATE TABLE tb ( a BLOB DEFAULT NULL)", "CREATE TABLE tb ( a BLOB DEFAULT NULL)",
"CREATE TABLE tb ( a JSON DEFAULT NULL)",
"alter TABLE `tbl` add column `c` longblob;", "alter TABLE `tbl` add column `c` longblob;",
"alter TABLE `tbl` add column `c` text;", "alter TABLE `tbl` add column `c` text;",
"alter TABLE `tbl` add column `c` blob;", "alter TABLE `tbl` add column `c` blob;",
"alter TABLE `tbl` add column `c` json;",
}, },
} }
......
...@@ -523,8 +523,8 @@ func init() { ...@@ -523,8 +523,8 @@ func init() {
"COL.012": { "COL.012": {
Item: "COL.012", Item: "COL.012",
Severity: "L5", Severity: "L5",
Summary: "BLOB 和 TEXT 类型的字段不建议设置为 NOT NULL", Summary: "TEXT、BLOB 和 JSON 类型的字段不建议设置为 NOT NULL",
Content: `BLOB 和 TEXT 类型的字段无法指定非 NULL 的默认值,如果添加了 NOT NULL 限制,写入数据时又未对该字段指定值可能导致写入失败。`, Content: `TEXT、BLOB 和 JSON 类型的字段无法指定非 NULL 的默认值,如果添加了 NOT NULL 限制,写入数据时又未对该字段指定值可能导致写入失败。`,
Case: "CREATE TABLE `tb`(`c` longblob NOT NULL);", Case: "CREATE TABLE `tb`(`c` longblob NOT NULL);",
Func: (*Query4Audit).RuleBLOBNotNull, Func: (*Query4Audit).RuleBLOBNotNull,
}, },
...@@ -548,8 +548,8 @@ func init() { ...@@ -548,8 +548,8 @@ func init() {
"COL.015": { "COL.015": {
Item: "COL.015", Item: "COL.015",
Severity: "L4", Severity: "L4",
Summary: "TEXT 和 BLOB 类型的字段不可指定非 NULL 的默认值", Summary: "TEXT、BLOB 和 JSON 类型的字段不可指定非 NULL 的默认值",
Content: `MySQL 数据库中 TEXT 和 BLOB 类型的字段不可指定非 NULL 的默认值。TEXT最大长度为2^16-1个字符,MEDIUMTEXT最大长度为2^32-1个字符,LONGTEXT最大长度为2^64-1个字符。`, Content: `MySQL 数据库中 TEXT、BLOB 和 JSON 类型的字段不可指定非 NULL 的默认值。TEXT最大长度为2^16-1个字符,MEDIUMTEXT最大长度为2^32-1个字符,LONGTEXT最大长度为2^64-1个字符。`,
Case: "CREATE TABLE `tbl` (`c` blob DEFAULT NULL);", Case: "CREATE TABLE `tbl` (`c` blob DEFAULT NULL);",
Func: (*Query4Audit).RuleBlobDefaultValue, Func: (*Query4Audit).RuleBlobDefaultValue,
}, },
......
...@@ -476,7 +476,7 @@ select c1,c2,c3 from tbl where c4 is null or c4 <> 1 ...@@ -476,7 +476,7 @@ select c1,c2,c3 from tbl where c4 is null or c4 <> 1
* **Item**:COL.012 * **Item**:COL.012
* **Severity**:L5 * **Severity**:L5
* **Content**:BLOB 和 TEXT 类型的字段无法指定非 NULL 的默认值,如果添加了 NOT NULL 限制,写入数据时又未对该字段指定值可能导致写入失败。 * **Content**:TEXT、BLOB 和 JSON 类型的字段无法指定非 NULL 的默认值,如果添加了 NOT NULL 限制,写入数据时又未对该字段指定值可能导致写入失败。
* **Case**: * **Case**:
```sql ```sql
...@@ -506,7 +506,7 @@ CREATE TABLE `tb2` ( `id` int(11) DEFAULT NULL, `col` char(10) CHARACTER SET utf ...@@ -506,7 +506,7 @@ CREATE TABLE `tb2` ( `id` int(11) DEFAULT NULL, `col` char(10) CHARACTER SET utf
* **Item**:COL.015 * **Item**:COL.015
* **Severity**:L4 * **Severity**:L4
* **Content**:MySQL 数据库中 TEXT 和 BLOB 类型的字段不可指定非 NULL 的默认值。TEXT最大长度为2^16-1个字符,MEDIUMTEXT最大长度为2^32-1个字符,LONGTEXT最大长度为2^64-1个字符。 * **Content**:MySQL 数据库中 TEXT、BLOB 和 JSON 类型的字段不可指定非 NULL 的默认值。TEXT最大长度为2^16-1个字符,MEDIUMTEXT最大长度为2^32-1个字符,LONGTEXT最大长度为2^64-1个字符。
* **Case**: * **Case**:
```sql ```sql
......
...@@ -40,6 +40,7 @@ tb; ...@@ -40,6 +40,7 @@ tb;
20 select /*!50000 1,*/ 1; 20 select /*!50000 1,*/ 1;
21 UPDATE xxx SET c1=' LOGGER.error(""); }' WHERE id = 2 ; 21 UPDATE xxx SET c1=' LOGGER.error(""); }' WHERE id = 2 ;
22 UPDATE `xxx` SET aaa='a;' WHERE `id` = 15; 22 UPDATE `xxx` SET aaa='a;' WHERE `id` = 15;
23 UPDATE `xxx` SET aaa='a -- b' WHERE `id` = 15;
0 select * from test\G 0 select * from test\G
1 select 'hello\Gworld', col from test\G 1 select 'hello\Gworld', col from test\G
2 -- select * from test\Ghello 2 -- select * from test\Ghello
......
...@@ -862,12 +862,12 @@ func SplitStatement(buf []byte, delimiter []byte) (string, string, []byte) { ...@@ -862,12 +862,12 @@ func SplitStatement(buf []byte, delimiter []byte) (string, string, []byte) {
b := buf[i] b := buf[i]
// single line comment // single line comment
if b == '-' { if b == '-' {
if i+2 < len(buf) && buf[i+1] == '-' && buf[i+2] == ' ' { if !quoted && i+2 < len(buf) && buf[i+1] == '-' && buf[i+2] == ' ' {
singleLineComment = true singleLineComment = true
i = i + 2 i = i + 2
continue continue
} }
if i+2 < len(buf) && i == 0 && buf[i+1] == '-' && (buf[i+2] == '\n' || buf[i+2] == '\r') { if !quoted && i+2 < len(buf) && i == 0 && buf[i+1] == '-' && (buf[i+2] == '\n' || buf[i+2] == '\r') {
sql = "--\n" sql = "--\n"
break break
} }
......
...@@ -173,7 +173,8 @@ select col from tb; ...@@ -173,7 +173,8 @@ select col from tb;
[]byte(`select /*!50000 1,*/ 1;`), // 20 []byte(`select /*!50000 1,*/ 1;`), // 20
[]byte(`UPDATE xxx SET c1=' LOGGER.error(""); }' WHERE id = 2 ;`), // 21 []byte(`UPDATE xxx SET c1=' LOGGER.error(""); }' WHERE id = 2 ;`), // 21
[]byte("UPDATE `xxx` SET aaa='a;' WHERE `id` = 15;"), // 22 []byte("UPDATE `xxx` SET aaa='a;' WHERE `id` = 15;"), // 22
// []byte(`/* comment here */ SET MAX_JOIN_SIZE=#`), // 23 []byte("UPDATE `xxx` SET aaa='a -- b' WHERE `id` = 15; UPDATE `xxx` SET aaa='c -- d' WHERE `id` = 16;"), // 23
// []byte(`/* comment here */ SET MAX_JOIN_SIZE=#`), // 24
} }
// \G 分隔符 // \G 分隔符
buf2s := [][]byte{ buf2s := [][]byte{
......
...@@ -70,6 +70,15 @@ type tableStatusRow struct { ...@@ -70,6 +70,15 @@ type tableStatusRow struct {
Comment []byte // 注释 Comment []byte // 注释
} }
// 记录去除逗号类型是外健还是分区表
type deleteComaType int8
const (
_ deleteComaType = iota
CS
PART
)
// newTableStat 构造 table Stat 对象 // newTableStat 构造 table Stat 对象
func newTableStat(tableName string) *TableStatInfo { func newTableStat(tableName string) *TableStatInfo {
return &TableStatInfo{ return &TableStatInfo{
...@@ -478,21 +487,37 @@ func (db *Connector) ShowCreateTable(tableName string) (string, error) { ...@@ -478,21 +487,37 @@ func (db *Connector) ShowCreateTable(tableName string) (string, error) {
if len(lines) > 2 { if len(lines) > 2 {
var noConstraint []string var noConstraint []string
relationReg, _ := regexp.Compile("CONSTRAINT") relationReg, _ := regexp.Compile("CONSTRAINT")
partitionReg, _ := regexp.Compile("PARTITIONS")
var DeleteComaT deleteComaType
for _, line := range lines[1 : len(lines)-1] { for _, line := range lines[1 : len(lines)-1] {
if relationReg.Match([]byte(line)) { if relationReg.Match([]byte(line)) {
DeleteComaT = CS
continue continue
} else if partitionReg.Match([]byte(line)) {
DeleteComaT = PART
} }
line = strings.TrimSuffix(line, ",") line = strings.TrimSuffix(line, ",")
noConstraint = append(noConstraint, line) noConstraint = append(noConstraint, line)
} }
// 去除外键语句会使DDL中多一个','导致语法错误,要把多余的逗号去除 // 去除外键语句会使DDL中多一个','导致语法错误,要把多余的逗号去除
ddl = fmt.Sprint( // len(lines) > 2的判断方式有问题,如果是分区表也会判断成为外键语句,导致建表语句的逗号错乱
lines[0], "\n", if DeleteComaT == CS {
strings.Join(noConstraint, ",\n"), "\n", ddl = fmt.Sprint(
lines[len(lines)-1], lines[0], "\n",
) strings.Join(noConstraint, ",\n"), "\n",
lines[len(lines)-1],
)
} else if DeleteComaT == PART {
ddl = fmt.Sprint(
lines[0], "\n",
strings.Join(noConstraint, ",\n"), "\n",
lines[len(lines)-3],
)
}
} }
return ddl, err return ddl, err
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册