未验证 提交 c1b21d95 编写于 作者: X xiyangxixian 提交者: GitHub

Merge pull request #2 from XiaoMi/master

merge by xiaomi
......@@ -293,7 +293,7 @@ func (idxAdv *IndexAdvisor) RuleImplicitConversion() Rule {
"date", "time", "datetime", "timestamp", "year",
},
sqlparser.IntVal: {
"tinyint", "smallint", "mediumint", "int", "integer", "bigint", "timestamp", "year",
"tinyint", "smallint", "mediumint", "int", "integer", "bigint", "timestamp", "year", "bit",
},
sqlparser.FloatVal: {
"float", "double", "real", "decimal",
......@@ -328,7 +328,7 @@ func (idxAdv *IndexAdvisor) RuleImplicitConversion() Rule {
continue
}
c := fmt.Sprintf("%s.%s definition is %s not %s",
c := fmt.Sprintf("%s表中列%s的定义是 %s 而不是 %s",
colList[0].Table, colList[0].Name, colList[0].DataType, typNameMap[val.Type])
common.Log.Debug("Implicit data type conversion: %s", c)
......@@ -1308,6 +1308,42 @@ func (q *Query4Audit) RuleLoadFile() Rule {
return rule
}
// RuleMultiCompare RES.009
func (q *Query4Audit) RuleMultiCompare() Rule {
var rule = q.RuleOK()
if q.TiStmt != nil {
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.SelectStmt:
switch where := node.Where.(type) {
case *tidb.BinaryOperationExpr:
switch where.L.(type) {
case *tidb.BinaryOperationExpr:
rule = HeuristicRules["RES.009"]
}
}
case *tidb.UpdateStmt:
switch where := node.Where.(type) {
case *tidb.BinaryOperationExpr:
switch where.L.(type) {
case *tidb.BinaryOperationExpr:
rule = HeuristicRules["RES.009"]
}
}
case *tidb.DeleteStmt:
switch where := node.Where.(type) {
case *tidb.BinaryOperationExpr:
switch where.L.(type) {
case *tidb.BinaryOperationExpr:
rule = HeuristicRules["RES.009"]
}
}
}
}
}
return rule
}
// RuleStandardINEQ STA.001
func (q *Query4Audit) RuleStandardINEQ() Rule {
var rule = q.RuleOK()
......@@ -3246,6 +3282,84 @@ 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
}
// RuleMaxTextColsCount COL.007 checking for existed table
func (idxAdv *IndexAdvisor) RuleMaxTextColsCount() Rule {
rule := HeuristicRules["OK"]
// 未开启测试环境不进行检查
if common.Config.TestDSN.Disable {
return rule
}
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch stmt := node.(type) {
case *sqlparser.DDL:
if stmt.Action != "alter" {
return true, nil
}
tb := stmt.Table
// 此处的检查需要在测试环境中的临时数据库中进行检查
// 需要将测试环境 DSN 的数据库暂时指向临时数据库
// 为了防止影响切换数据库环境会影响接下来的测试,需要在检查完后将原配置还原
dbTmp := idxAdv.vEnv.Database
idxAdv.vEnv.Database = idxAdv.vEnv.DBRef[idxAdv.vEnv.Database]
defer func() {
idxAdv.vEnv.Database = dbTmp
}()
// 添加字段的语句会在初始化环境的时候被执行
// 只需要获取该标的 CREAET 语句,后再对该语句进行检查即可
ddl, err := idxAdv.vEnv.ShowCreateTable(tb.Name.String())
if err != nil {
common.Log.Error("RuleMaxTextColsCount create statement got failed: %s", err.Error())
return false, err
}
q, err := NewQuery4Audit(ddl)
if err != nil {
return false, err
}
r := q.RuleMaxTextColsCount()
if r.Item != "OK" {
rule = r
return false, nil
}
}
return true, nil
}, idxAdv.Ast)
common.LogIfError(err, "")
return rule
}
// RuleAllowEngine TBL.002
func (q *Query4Audit) RuleAllowEngine() Rule {
var rule = q.RuleOK()
......
......@@ -19,11 +19,14 @@ package advisor
import (
"errors"
"sort"
"strings"
"testing"
"github.com/XiaoMi/soar/common"
"github.com/XiaoMi/soar/env"
"github.com/kr/pretty"
"vitess.io/vitess/go/vt/sqlparser"
)
// ALI.001
......@@ -938,6 +941,46 @@ func TestRuleLoadFile(t *testing.T) {
common.Log.Debug("Exiting function: %s", common.GetFunctionName())
}
// RES.009
func TestRuleMultiCompare(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := [][]string{
{
"SELECT * FROM tbl WHERE col = col = 'abc'",
"UPDATE tbl set col = 1 WHERE col = col = 'abc'",
"DELETE FROM tbl WHERE col = col = 'abc'",
},
{
"SELECT * FROM tbl WHERE col = 'abc'",
},
}
for _, sql := range sqls[0] {
q, err := NewQuery4Audit(sql)
if err == nil {
rule := q.RuleMultiCompare()
if rule.Item != "RES.009" {
t.Error("Rule not match:", rule.Item, "Expect : RES.009, SQL: ", sql)
}
} else {
t.Error("sqlparser.Parse Error:", err)
}
}
for _, sql := range sqls[1] {
q, err := NewQuery4Audit(sql)
if err == nil {
rule := q.RuleMultiCompare()
if rule.Item != "OK" {
t.Error("Rule not match:", rule.Item, "Expect : OK, SQL: ", sql)
}
} else {
t.Error("sqlparser.Parse Error:", err)
}
}
common.Log.Debug("Exiting function: %s", common.GetFunctionName())
}
// STA.001
func TestRuleStandardINEQ(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
......@@ -3091,6 +3134,71 @@ 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())
}
// COL.007
func TestRuleMaxTextColsCountWithEnv(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
dsn := common.Config.OnlineDSN
common.Config.OnlineDSN = common.Config.TestDSN
vEnv, rEnv := env.BuildEnv()
defer vEnv.CleanUp()
initSQLs := []string{
`CREATE TABLE t1 (id int, title text, content blob);`,
"alter table t1 add column other text;",
}
for _, sql := range initSQLs {
vEnv.BuildVirtualEnv(rEnv, sql)
if !strings.HasPrefix(strings.ToLower(sql), "alter") {
continue
}
stmt, syntaxErr := sqlparser.Parse(sql)
if syntaxErr != nil {
common.Log.Critical("Syntax Error: %v, SQL: %s", syntaxErr, sql)
}
q := &Query4Audit{Query: sql, Stmt: stmt}
idxAdvisor, err := NewAdvisor(vEnv, *rEnv, *q)
if err != nil {
t.Error("NewAdvisor Error: ", err, "SQL: ", sql)
}
if idxAdvisor != nil {
rule := idxAdvisor.RuleMaxTextColsCount()
if rule.Item != "COL.007" {
t.Error("Rule not match:", rule, "Expect : COL.007, SQL:", sql)
}
}
}
common.Log.Debug("Exiting function: %s", common.GetFunctionName())
common.Config.OnlineDSN = dsn
}
// TBL.002
func TestRuleAllowEngine(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
......
......@@ -111,7 +111,11 @@ func NewAdvisor(env *env.VirtualEnv, rEnv database.Connector, q Query4Audit) (*I
}
}
return nil, nil
return &IndexAdvisor{
vEnv: env,
rEnv: rEnv,
Ast: q.Stmt,
}, nil
case *sqlparser.DBDDL:
// 忽略建库语句
......@@ -1011,11 +1015,12 @@ func (idxAdv *IndexAdvisor) HeuristicCheck(q Query4Audit) map[string]Rule {
}
ruleFuncs := []func(*IndexAdvisor) Rule{
(*IndexAdvisor).RuleMaxTextColsCount, // COL.007
(*IndexAdvisor).RuleImplicitConversion, // ARG.003
(*IndexAdvisor).RuleGroupByConst, // CLA.004
(*IndexAdvisor).RuleOrderByConst, // CLA.005
(*IndexAdvisor).RuleUpdatePrimaryKey, // CLA.016
// (*IndexAdvisor).RuleImpossibleOuterJoin, // TODO: JOI.003, JOI.004
(*IndexAdvisor).RuleGroupByConst, // CLA.004
(*IndexAdvisor).RuleOrderByConst, // CLA.005
(*IndexAdvisor).RuleUpdatePrimaryKey, // CLA.016
}
for _, f := range ruleFuncs {
......
......@@ -59,19 +59,27 @@ func TestRuleImplicitConversion(t *testing.T) {
`CREATE TABLE t1 (id int, title varchar(255) CHARSET utf8 COLLATE utf8_general_ci);`,
`CREATE TABLE t2 (id int, title varchar(255) CHARSET utf8mb4);`,
`CREATE TABLE t3 (id int, title varchar(255) CHARSET utf8 COLLATE utf8_bin);`,
`CREATE TABLE t4 (id int, col bit(1));`,
}
for _, sql := range initSQLs {
vEnv.BuildVirtualEnv(rEnv, sql)
}
sqls := []string{
"SELECT * FROM t1 WHERE title >= 60;",
"SELECT * FROM t1, t2 WHERE t1.title = t2.title;",
"SELECT * FROM t1, t3 WHERE t1.title = t3.title;",
"SELECT * FROM t1 WHERE title in (60, '60');",
"SELECT * FROM t1 WHERE title in (60);",
sqls := [][]string{
{
"SELECT * FROM t1 WHERE title >= 60;",
"SELECT * FROM t1, t2 WHERE t1.title = t2.title;",
"SELECT * FROM t1, t3 WHERE t1.title = t3.title;",
"SELECT * FROM t1 WHERE title in (60, '60');",
"SELECT * FROM t1 WHERE title in (60);",
"SELECT * FROM t4 WHERE col = '1'",
},
{
// https://github.com/XiaoMi/soar/issues/151
"SELECT * FROM t4 WHERE col = 1",
},
}
for _, sql := range sqls {
for _, sql := range sqls[0] {
stmt, syntaxErr := sqlparser.Parse(sql)
if syntaxErr != nil {
common.Log.Critical("Syntax Error: %v, SQL: %s", syntaxErr, sql)
......@@ -91,6 +99,27 @@ func TestRuleImplicitConversion(t *testing.T) {
}
}
}
for _, sql := range sqls[1] {
stmt, syntaxErr := sqlparser.Parse(sql)
if syntaxErr != nil {
common.Log.Critical("Syntax Error: %v, SQL: %s", syntaxErr, sql)
}
q := &Query4Audit{Query: sql, Stmt: stmt}
idxAdvisor, err := NewAdvisor(vEnv, *rEnv, *q)
if err != nil {
t.Error("NewAdvisor Error: ", err, "SQL: ", sql)
}
if idxAdvisor != nil {
rule := idxAdvisor.RuleImplicitConversion()
if rule.Item != "OK" {
t.Error("Rule not match:", rule, "Expect : OK, SQL:", sql)
}
}
}
common.Log.Debug("Exiting function: %s", common.GetFunctionName())
common.Config.OnlineDSN = dsn
}
......
......@@ -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",
......@@ -957,6 +965,14 @@ func init() {
Case: "LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table;",
Func: (*Query4Audit).RuleLoadFile,
},
"RES.009": {
Item: "RES.009",
Severity: "L2",
Summary: "不建议使用连续判断",
Content: "类似这样的 SELECT * FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。",
Case: "SELECT * FROM tbl WHERE col = col = 'abc'",
Func: (*Query4Audit).RuleMultiCompare,
},
"SEC.001": {
Item: "SEC.001",
Severity: "L0",
......
......@@ -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 ....);
```
......@@ -1012,6 +1022,16 @@ select * from tbl where 1 = 1;
```sql
LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table;
```
## 不建议使用连续判断
* **Item**:RES.009
* **Severity**:L2
* **Content**:类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
* **Case**:
```sql
SELECT * FROM tbl WHERE col = col = 'abc'
```
## 请谨慎使用TRUNCATE操作
* **Item**:SEC.001
......
......@@ -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 {...}}
......@@ -96,6 +97,7 @@ advisor.Rule{Item:"RES.005", Severity:"L4", Summary:"UPDATE 语句可能存在
advisor.Rule{Item:"RES.006", Severity:"L4", Summary:"永远不真的比较条件", Content:"查询条件永远非真,如果该条件出现在 where 中可能导致查询无匹配到的结果。", Case:"select * from tbl where 1 != 1;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"RES.007", Severity:"L4", Summary:"永远为真的比较条件", Content:"查询条件永远为真,可能导致 WHERE 条件失效进行全表查询。", Case:"select * from tbl where 1 = 1;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"RES.008", Severity:"L2", Summary:"不建议使用LOAD DATA/SELECT ... INTO OUTFILE", Content:"SELECT INTO OUTFILE 需要授予 FILE 权限,这通过会引入安全问题。LOAD DATA 虽然可以提高数据导入速度,但同时也可能导致从库同步延迟过大。", Case:"LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table;", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"RES.009", Severity:"L2", Summary:"不建议使用连续判断", Content:"类似这样的 SELECT * FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。", Case:"SELECT * FROM tbl WHERE col = col = 'abc'", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"SEC.001", Severity:"L0", Summary:"请谨慎使用TRUNCATE操作", Content:"一般来说想清空一张表最快速的做法就是使用TRUNCATE TABLE tbl_name;语句。但TRUNCATE操作也并非是毫无代价的,TRUNCATE TABLE无法返回被删除的准确行数,如果需要返回被删除的行数建议使用DELETE语法。TRUNCATE 操作还会重置 AUTO_INCREMENT,如果不想重置该值建议使用 DELETE FROM tbl_name WHERE 1;替代。TRUNCATE 操作会对数据字典添加源数据锁(MDL),当一次需要 TRUNCATE 很多表时会影响整个实例的所有请求,因此如果要 TRUNCATE 多个表建议用 DROP+CREATE 的方式以减少锁时长。", Case:"TRUNCATE TABLE tbl_name", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"SEC.002", Severity:"L0", Summary:"不使用明文存储密码", Content:"使用明文存储密码或者使用明文在网络上传递密码都是不安全的。如果攻击者能够截获您用来插入密码的SQL语句,他们就能直接读到密码。另外,将用户输入的字符串以明文的形式插入到纯SQL语句中,也会让攻击者发现它。如果您能够读取密码,黑客也可以。解决方案是使用单向哈希函数对原始密码进行加密编码。哈希是指将输入字符串转化成另一个新的、不可识别的字符串的函数。对密码加密表达式加点随机串来防御“字典攻击”。不要将明文密码输入到SQL查询语句中。在应用程序代码中计算哈希串,只在SQL查询中使用哈希串。", Case:"create table test(id int,name varchar(20) not null,password varchar(200)not null)", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
advisor.Rule{Item:"SEC.003", Severity:"L0", Summary:"使用DELETE/DROP/TRUNCATE等操作时注意备份", Content:"在执行高危操作之前对数据进行备份是十分有必要的。", Case:"delete from table where col = 'condition'", Position:0, Func:func(*advisor.Query4Audit) advisor.Rule {...}}
......
......@@ -210,7 +210,7 @@
}
[]ast.Token{
{Type:57348, Val:"select", i:0},
{Type:57589, Val:"sql_calc_found_rows", i:0},
{Type:57590, Val:"sql_calc_found_rows", i:0},
{Type:57396, Val:"col", i:0},
{Type:57353, Val:"from", i:0},
{Type:57396, Val:"tbl", i:0},
......
......@@ -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,
......@@ -513,7 +515,7 @@ func readCmdFlags() error {
// +++++++++++++++日志相关+++++++++++++++++
logLevel := flag.Int("log-level", Config.LogLevel, "LogLevel, 日志级别, [0:Emergency, 1:Alert, 2:Critical, 3:Error, 4:Warning, 5:Notice, 6:Informational, 7:Debug]")
logOutput := flag.String("log-output", Config.LogOutput, "LogOutput, 日志输出位置")
reportType := flag.String("report-type", Config.ReportType, "ReportType, 化建议输出格式,目前支持: json, text, markdown, html等")
reportType := flag.String("report-type", Config.ReportType, "ReportType, 化建议输出格式,目前支持: json, text, markdown, html等")
reportCSS := flag.String("report-css", Config.ReportCSS, "ReportCSS, 当 ReportType 为 html 格式时使用的 css 风格,如不指定会提供一个默认风格。CSS可以是本地文件,也可以是一个URL")
reportJavascript := flag.String("report-javascript", Config.ReportJavascript, "ReportJavascript, 当 ReportType 为 html 格式时使用的javascript脚本,如不指定默认会加载SQL pretty 使用的 javascript。像CSS一样可以是本地文件,也可以是一个URL")
reportTitle := flag.String("report-title", Config.ReportTitle, "ReportTitle, 当 ReportType 为 html 格式时,HTML 的 title")
......@@ -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,14 +43,14 @@ 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)))
logConfigJSON, _ := json.Marshal(logConfig)
err := Log.SetLogger(logs.AdapterFile, string(logConfigJSON))
if err != nil {
fmt.Println(err.Error())
}
......
......@@ -48,6 +48,7 @@ max-join-table-count: 5
max-group-by-cols-count: 5
max-distinct-count: 5
max-index-cols-count: 5
max-text-cols-count: 2
max-total-rows: 9999999
max-query-cost: 9999
spaghetti-query-length: 2048
......
......@@ -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 ....);
```
......@@ -1012,6 +1022,16 @@ select * from tbl where 1 = 1;
```sql
LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table;
```
## 不建议使用连续判断
* **Item**:RES.009
* **Severity**:L2
* **Content**:类似这样的 SELECT \* FROM tbl WHERE col = col = 'abc' 语句可能是书写错误,您可能想表达的含义是 col = 'abc'。如果确实是业务需求建议修改为 col = col and col = 'abc'。
* **Case**:
```sql
SELECT * FROM tbl WHERE col = col = 'abc'
```
## 请谨慎使用TRUNCATE操作
* **Item**:SEC.001
......
......@@ -91,11 +91,11 @@ func BuildEnv() (*VirtualEnv, *database.Connector) {
}
// 检查线上环境可用性版本
rEnvVersion, err := vEnv.Version()
rEnvVersion, err := conn.Version()
common.Config.OnlineDSN.Version = rEnvVersion
if err != nil {
common.Log.Warn("BuildEnv OnlineDSN: %s:********@%s/%s not available , Error: %s",
vEnv.User, vEnv.Addr, vEnv.Database, err.Error())
conn.User, conn.Addr, conn.Database, err.Error())
common.Config.TestDSN.Disable = true
}
......
......@@ -550,6 +550,7 @@ type DropTableStmt struct {
IfExists bool
Tables []*TableName
IsView bool
}
// Restore implements Recoverable interface.
......@@ -887,6 +888,7 @@ const (
AlterTableAddPartitions
AlterTableCoalescePartitions
AlterTableDropPartition
AlterTableTruncatePartition
// TODO: Add more actions
)
......
......@@ -605,7 +605,8 @@ type IsNullExpr struct {
// Restore implements Recoverable interface.
func (n *IsNullExpr) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
n.Format(sb)
return nil
}
// Format the ExprNode into a Writer.
......
......@@ -94,6 +94,7 @@ func ValidCharsetAndCollation(cs string, co string) bool {
if co == "" {
return true
}
co = strings.ToLower(co)
_, ok = c.Collations[co]
if !ok {
return false
......
......@@ -29,55 +29,59 @@ type ActionType byte
// List DDL actions.
const (
ActionNone ActionType = 0
ActionCreateSchema ActionType = 1
ActionDropSchema ActionType = 2
ActionCreateTable ActionType = 3
ActionDropTable ActionType = 4
ActionAddColumn ActionType = 5
ActionDropColumn ActionType = 6
ActionAddIndex ActionType = 7
ActionDropIndex ActionType = 8
ActionAddForeignKey ActionType = 9
ActionDropForeignKey ActionType = 10
ActionTruncateTable ActionType = 11
ActionModifyColumn ActionType = 12
ActionRebaseAutoID ActionType = 13
ActionRenameTable ActionType = 14
ActionSetDefaultValue ActionType = 15
ActionShardRowID ActionType = 16
ActionModifyTableComment ActionType = 17
ActionRenameIndex ActionType = 18
ActionAddTablePartition ActionType = 19
ActionDropTablePartition ActionType = 20
ActionCreateView ActionType = 21
ActionNone ActionType = 0
ActionCreateSchema ActionType = 1
ActionDropSchema ActionType = 2
ActionCreateTable ActionType = 3
ActionDropTable ActionType = 4
ActionAddColumn ActionType = 5
ActionDropColumn ActionType = 6
ActionAddIndex ActionType = 7
ActionDropIndex ActionType = 8
ActionAddForeignKey ActionType = 9
ActionDropForeignKey ActionType = 10
ActionTruncateTable ActionType = 11
ActionModifyColumn ActionType = 12
ActionRebaseAutoID ActionType = 13
ActionRenameTable ActionType = 14
ActionSetDefaultValue ActionType = 15
ActionShardRowID ActionType = 16
ActionModifyTableComment ActionType = 17
ActionRenameIndex ActionType = 18
ActionAddTablePartition ActionType = 19
ActionDropTablePartition ActionType = 20
ActionCreateView ActionType = 21
ActionModifyTableCharsetAndCollate ActionType = 22
ActionTruncateTablePartition ActionType = 23
)
// AddIndexStr is a string related to the operation of "add index".
const AddIndexStr = "add index"
var actionMap = map[ActionType]string{
ActionCreateSchema: "create schema",
ActionDropSchema: "drop schema",
ActionCreateTable: "create table",
ActionDropTable: "drop table",
ActionAddColumn: "add column",
ActionDropColumn: "drop column",
ActionAddIndex: AddIndexStr,
ActionDropIndex: "drop index",
ActionAddForeignKey: "add foreign key",
ActionDropForeignKey: "drop foreign key",
ActionTruncateTable: "truncate table",
ActionModifyColumn: "modify column",
ActionRebaseAutoID: "rebase auto_increment ID",
ActionRenameTable: "rename table",
ActionSetDefaultValue: "set default value",
ActionShardRowID: "shard row ID",
ActionModifyTableComment: "modify table comment",
ActionRenameIndex: "rename index",
ActionAddTablePartition: "add partition",
ActionDropTablePartition: "drop table partition",
ActionCreateView: "create view",
ActionCreateSchema: "create schema",
ActionDropSchema: "drop schema",
ActionCreateTable: "create table",
ActionDropTable: "drop table",
ActionAddColumn: "add column",
ActionDropColumn: "drop column",
ActionAddIndex: AddIndexStr,
ActionDropIndex: "drop index",
ActionAddForeignKey: "add foreign key",
ActionDropForeignKey: "drop foreign key",
ActionTruncateTable: "truncate table",
ActionModifyColumn: "modify column",
ActionRebaseAutoID: "rebase auto_increment ID",
ActionRenameTable: "rename table",
ActionSetDefaultValue: "set default value",
ActionShardRowID: "shard row ID",
ActionModifyTableComment: "modify table comment",
ActionRenameIndex: "rename index",
ActionAddTablePartition: "add partition",
ActionDropTablePartition: "drop partition",
ActionCreateView: "create view",
ActionModifyTableCharsetAndCollate: "modify table charset and collate",
ActionTruncateTablePartition: "truncate partition",
}
// String return current ddl action in string
......
......@@ -1084,6 +1084,13 @@ AlterTableSpec:
Name: $3,
}
}
| "TRUNCATE" "PARTITION" Identifier
{
$$ = &ast.AlterTableSpec{
Tp: ast.AlterTableTruncatePartition,
Name: $3,
}
}
| "DROP" KeyOrIndex Identifier
{
$$ = &ast.AlterTableSpec{
......@@ -2349,17 +2356,22 @@ DropIndexStmt:
DropTableStmt:
"DROP" TableOrTables TableNameList RestrictOrCascadeOpt
{
$$ = &ast.DropTableStmt{Tables: $3.([]*ast.TableName)}
$$ = &ast.DropTableStmt{Tables: $3.([]*ast.TableName), IsView: false}
}
| "DROP" TableOrTables "IF" "EXISTS" TableNameList RestrictOrCascadeOpt
{
$$ = &ast.DropTableStmt{IfExists: true, Tables: $5.([]*ast.TableName)}
$$ = &ast.DropTableStmt{IfExists: true, Tables: $5.([]*ast.TableName), IsView: false}
}
DropViewStmt:
"DROP" "VIEW" "IF" "EXISTS" TableNameList
"DROP" "VIEW" TableNameList RestrictOrCascadeOpt
{
$$ = &ast.DropTableStmt{Tables: $3.([]*ast.TableName), IsView: true}
}
|
"DROP" "VIEW" "IF" "EXISTS" TableNameList RestrictOrCascadeOpt
{
$$ = &ast.DoStmt{}
$$ = &ast.DropTableStmt{IfExists: true, Tables: $5.([]*ast.TableName), IsView: true}
}
DropUserStmt:
......
......@@ -71,6 +71,14 @@ type StatementContext struct {
histogramsNotLoad bool
execDetails execdetails.ExecDetails
}
// PrevAffectedRows is the affected-rows value(DDL is 0, DML is the number of affected rows).
PrevAffectedRows int64
// PrevLastInsertID is the last insert ID of previous statement.
PrevLastInsertID uint64
// LastInsertID is the auto-generated ID in the current statement.
LastInsertID uint64
// InsertID is the given insert ID of an auto_increment column.
InsertID uint64
// Copied from SessionVars.TimeZone.
TimeZone *time.Location
......@@ -241,6 +249,8 @@ func (sc *StatementContext) ResetForRetry() {
sc.mu.foundRows = 0
sc.mu.warnings = nil
sc.mu.Unlock()
sc.TableIDs = sc.TableIDs[:0]
sc.IndexIDs = sc.IndexIDs[:0]
}
// MergeExecDetails merges a single region execution details into self, used to print
......
......@@ -40,6 +40,12 @@ func NewFieldType(tp byte) *FieldType {
}
}
// CloneFieldType clones the given FieldType.
func CloneFieldType(src *FieldType) *FieldType {
ft := *src
return &ft
}
// AggFieldType aggregates field types for a multi-argument function like `IF`, `IFNULL`, `COALESCE`
// whose return type is determined by the arguments' FieldTypes.
// Aggregation is performed by MergeFieldType function.
......@@ -123,11 +129,14 @@ func setTypeFlag(flag *uint, flagItem uint, on bool) {
func DefaultParamTypeForValue(value interface{}, tp *FieldType) {
switch value.(type) {
case nil:
tp.Tp = mysql.TypeUnspecified
tp.Tp = mysql.TypeVarString
tp.Flen = UnspecifiedLength
tp.Decimal = UnspecifiedLength
default:
DefaultTypeForValue(value, tp)
if tp.Tp == mysql.TypeUnspecified {
tp.Tp = mysql.TypeVarString
}
}
}
......
......@@ -105,106 +105,106 @@
"revisionTime": "2018-10-24T15:10:47Z"
},
{
"checksumSHA1": "V/4P8kb4QDozn9w++U8G+Kvt0+g=",
"checksumSHA1": "xbV0lm0Qw8rFC82Dttxbf5ypBjA=",
"path": "github.com/pingcap/parser",
"revision": "c563561800a23fd1edeae4a592400cc2d0b47f5f",
"revisionTime": "2018-12-05T02:39:50Z"
"revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
"revisionTime": "2018-12-11T02:45:40Z"
},
{
"checksumSHA1": "Fj3tju6yXL0IHhhu6pYwvU5xzJo=",
"checksumSHA1": "zrZ2JfaxdfwpArtuyiPjgH9GKeY=",
"path": "github.com/pingcap/parser/ast",
"revision": "c563561800a23fd1edeae4a592400cc2d0b47f5f",
"revisionTime": "2018-12-05T02:39:50Z"
"revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
"revisionTime": "2018-12-11T02:45:40Z"
},
{
"checksumSHA1": "skWGV4FNvD3vr+5olepaPPnylUw=",
"path": "github.com/pingcap/parser/auth",
"revision": "c563561800a23fd1edeae4a592400cc2d0b47f5f",
"revisionTime": "2018-12-05T02:39:50Z"
"revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
"revisionTime": "2018-12-11T02:45:40Z"
},
{
"checksumSHA1": "grkBf/zf8cTJRtI64P1jV6B+p/4=",
"checksumSHA1": "t4UHo966WzU9Z0IJkyGHRp0loOk=",
"path": "github.com/pingcap/parser/charset",
"revision": "c563561800a23fd1edeae4a592400cc2d0b47f5f",
"revisionTime": "2018-12-05T02:39:50Z"
"revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
"revisionTime": "2018-12-11T02:45:40Z"
},
{
"checksumSHA1": "SInoXbsRe0tnBwmatmtZYfSFbdk=",
"path": "github.com/pingcap/parser/format",
"revision": "c563561800a23fd1edeae4a592400cc2d0b47f5f",
"revisionTime": "2018-12-05T02:39:50Z"
"revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
"revisionTime": "2018-12-11T02:45:40Z"
},
{
"checksumSHA1": "+rJd1MX+3A1gwbrFZHFWbC8l8ao=",
"checksumSHA1": "reRV2qecd6NpB7tIW3JeK46K/sk=",
"path": "github.com/pingcap/parser/model",
"revision": "c563561800a23fd1edeae4a592400cc2d0b47f5f",
"revisionTime": "2018-12-05T02:39:50Z"
"revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
"revisionTime": "2018-12-11T02:45:40Z"
},
{
"checksumSHA1": "QBa9yiMDQNl2cLLwqlRoNTpCPNg=",
"path": "github.com/pingcap/parser/mysql",
"revision": "c563561800a23fd1edeae4a592400cc2d0b47f5f",
"revisionTime": "2018-12-05T02:39:50Z"
"revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
"revisionTime": "2018-12-11T02:45:40Z"
},
{
"checksumSHA1": "oNBCSwJRykKuzIKgPCttatB9hAo=",
"path": "github.com/pingcap/parser/opcode",
"revision": "c563561800a23fd1edeae4a592400cc2d0b47f5f",
"revisionTime": "2018-12-05T02:39:50Z"
"revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
"revisionTime": "2018-12-11T02:45:40Z"
},
{
"checksumSHA1": "XvnUllvwMYd6HrMvMiKnn4cGN2M=",
"path": "github.com/pingcap/parser/terror",
"revision": "c563561800a23fd1edeae4a592400cc2d0b47f5f",
"revisionTime": "2018-12-05T02:39:50Z"
"revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
"revisionTime": "2018-12-11T02:45:40Z"
},
{
"checksumSHA1": "s96v2EoeGKcWHO3mpMOQk/z2iaI=",
"path": "github.com/pingcap/parser/types",
"revision": "c563561800a23fd1edeae4a592400cc2d0b47f5f",
"revisionTime": "2018-12-05T02:39:50Z"
"revision": "4e6d047fcaae221376638de5f44c07cb6bf3eb44",
"revisionTime": "2018-12-11T02:45:40Z"
},
{
"checksumSHA1": "kO63T5plq+V7HWnkzB9WlOnp9Iw=",
"checksumSHA1": "fWqL/7jTYOiqDNmiUcQi3u45Hw0=",
"path": "github.com/pingcap/tidb/sessionctx/stmtctx",
"revision": "350a046975a1f86fa6f05f177e81ae838388f4bb",
"revisionTime": "2018-12-04T14:10:08Z"
"revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
"revisionTime": "2018-12-10T15:48:53Z"
},
{
"checksumSHA1": "SH/wISM1ueh/ENPJhiq1KssPLDA=",
"checksumSHA1": "U/TFas5WBPWG2DARj51bcfoN0xQ=",
"path": "github.com/pingcap/tidb/types",
"revision": "350a046975a1f86fa6f05f177e81ae838388f4bb",
"revisionTime": "2018-12-04T14:10:08Z"
"revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
"revisionTime": "2018-12-10T15:48:53Z"
},
{
"checksumSHA1": "DWVD7+ygtT66IQ+cqXmMJ5OVqUk=",
"path": "github.com/pingcap/tidb/types/json",
"revision": "350a046975a1f86fa6f05f177e81ae838388f4bb",
"revisionTime": "2018-12-04T14:10:08Z"
"revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
"revisionTime": "2018-12-10T15:48:53Z"
},
{
"checksumSHA1": "Zp5ME8OXNTmHnYTwJJUZlydN4/U=",
"path": "github.com/pingcap/tidb/types/parser_driver",
"revision": "350a046975a1f86fa6f05f177e81ae838388f4bb",
"revisionTime": "2018-12-04T14:10:08Z"
"revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
"revisionTime": "2018-12-10T15:48:53Z"
},
{
"checksumSHA1": "s709bhSrG2Ec35406mGtrySid4s=",
"path": "github.com/pingcap/tidb/util/execdetails",
"revision": "350a046975a1f86fa6f05f177e81ae838388f4bb",
"revisionTime": "2018-12-04T14:10:08Z"
"revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
"revisionTime": "2018-12-10T15:48:53Z"
},
{
"checksumSHA1": "nUC7zVoAMNR2a+z2iGqHoN2AkFE=",
"path": "github.com/pingcap/tidb/util/hack",
"revision": "350a046975a1f86fa6f05f177e81ae838388f4bb",
"revisionTime": "2018-12-04T14:10:08Z"
"revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
"revisionTime": "2018-12-10T15:48:53Z"
},
{
"checksumSHA1": "xSyepiuqsoaaeDch7cXeumvVHKM=",
"path": "github.com/pingcap/tidb/util/memory",
"revision": "350a046975a1f86fa6f05f177e81ae838388f4bb",
"revisionTime": "2018-12-04T14:10:08Z"
"revision": "ef0ad26da8f99044a741fc0a781a0c4791446e8b",
"revisionTime": "2018-12-10T15:48:53Z"
},
{
"checksumSHA1": "SmYeIK/fIYXNu8IKxD6HOVQVTuU=",
......@@ -401,62 +401,62 @@
{
"checksumSHA1": "aKn1oKcY74N8TRLm3Ayt7Q4bbI4=",
"path": "vitess.io/vitess/go/bytes2",
"revision": "5bec5a560c87897b5d973bbced81764f3cf0f330",
"revisionTime": "2018-12-05T00:00:15Z"
"revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
"revisionTime": "2018-12-10T16:22:31Z"
},
{
"checksumSHA1": "JVCEN4UGRmg3TofIBdzZMZ3G0Ww=",
"path": "vitess.io/vitess/go/hack",
"revision": "5bec5a560c87897b5d973bbced81764f3cf0f330",
"revisionTime": "2018-12-05T00:00:15Z"
"revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
"revisionTime": "2018-12-10T16:22:31Z"
},
{
"checksumSHA1": "e1WJ7vCnVrlQQQlc6n/FewCDMso=",
"path": "vitess.io/vitess/go/sqltypes",
"revision": "5bec5a560c87897b5d973bbced81764f3cf0f330",
"revisionTime": "2018-12-05T00:00:15Z"
"revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
"revisionTime": "2018-12-10T16:22:31Z"
},
{
"checksumSHA1": "ntFIQYkBS51G6y+FEkjFW40+HOU=",
"path": "vitess.io/vitess/go/vt/log",
"revision": "5bec5a560c87897b5d973bbced81764f3cf0f330",
"revisionTime": "2018-12-05T00:00:15Z"
"revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
"revisionTime": "2018-12-10T16:22:31Z"
},
{
"checksumSHA1": "tPQFPwbMdjuX0qjNl4Zl8zc37JQ=",
"path": "vitess.io/vitess/go/vt/proto/query",
"revision": "5bec5a560c87897b5d973bbced81764f3cf0f330",
"revisionTime": "2018-12-05T00:00:15Z"
"revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
"revisionTime": "2018-12-10T16:22:31Z"
},
{
"checksumSHA1": "o0tR/c7lgr0pLkxk7CdvjiNDAKU=",
"path": "vitess.io/vitess/go/vt/proto/topodata",
"revision": "5bec5a560c87897b5d973bbced81764f3cf0f330",
"revisionTime": "2018-12-05T00:00:15Z"
"revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
"revisionTime": "2018-12-10T16:22:31Z"
},
{
"checksumSHA1": "77UojBqi0yyeQvR70j7C3kcKclQ=",
"path": "vitess.io/vitess/go/vt/proto/vtgate",
"revision": "5bec5a560c87897b5d973bbced81764f3cf0f330",
"revisionTime": "2018-12-05T00:00:15Z"
"revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
"revisionTime": "2018-12-10T16:22:31Z"
},
{
"checksumSHA1": "QpWGhoVDwM+8+sgYLI/YU+95iGU=",
"path": "vitess.io/vitess/go/vt/proto/vtrpc",
"revision": "5bec5a560c87897b5d973bbced81764f3cf0f330",
"revisionTime": "2018-12-05T00:00:15Z"
"revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
"revisionTime": "2018-12-10T16:22:31Z"
},
{
"checksumSHA1": "CZ0WbR7TBTgF9HoY+aRxzOzVlSs=",
"checksumSHA1": "lENrUY/YyxwYFHYN+21TBH92P3U=",
"path": "vitess.io/vitess/go/vt/sqlparser",
"revision": "5bec5a560c87897b5d973bbced81764f3cf0f330",
"revisionTime": "2018-12-05T00:00:15Z"
"revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
"revisionTime": "2018-12-10T16:22:31Z"
},
{
"checksumSHA1": "oF4XzuOzwvj1iduX/lYqNSyY/HM=",
"path": "vitess.io/vitess/go/vt/vterrors",
"revision": "5bec5a560c87897b5d973bbced81764f3cf0f330",
"revisionTime": "2018-12-05T00:00:15Z"
"revision": "be9745195a139422cd62fd32bdc3b7e0c28c9427",
"revisionTime": "2018-12-10T16:22:31Z"
}
],
"rootPath": "github.com/XiaoMi/soar"
......
......@@ -746,15 +746,18 @@ type DDL struct {
// DDL strings.
const (
CreateStr = "create"
AlterStr = "alter"
DropStr = "drop"
RenameStr = "rename"
TruncateStr = "truncate"
FlushStr = "flush"
CreateVindexStr = "create vindex"
AddColVindexStr = "add vindex"
DropColVindexStr = "drop vindex"
CreateStr = "create"
AlterStr = "alter"
DropStr = "drop"
RenameStr = "rename"
TruncateStr = "truncate"
FlushStr = "flush"
CreateVindexStr = "create vindex"
DropVindexStr = "drop vindex"
AddVschemaTableStr = "add vschema table"
DropVschemaTableStr = "drop vschema table"
AddColVindexStr = "on table add vindex"
DropColVindexStr = "on table drop vindex"
// Vindex DDL param to specify the owner of a vindex
VindexOwnerStr = "owner"
......@@ -791,9 +794,15 @@ func (node *DDL) Format(buf *TrackedBuffer) {
case FlushStr:
buf.Myprintf("%s", node.Action)
case CreateVindexStr:
buf.Myprintf("%s %v %v", node.Action, node.VindexSpec.Name, node.VindexSpec)
buf.Myprintf("alter vschema create vindex %v %v", node.VindexSpec.Name, node.VindexSpec)
case DropVindexStr:
buf.Myprintf("alter vschema drop vindex %v", node.VindexSpec.Name)
case AddVschemaTableStr:
buf.Myprintf("alter vschema add table %v", node.Table)
case DropVschemaTableStr:
buf.Myprintf("alter vschema drop table %v", node.Table)
case AddColVindexStr:
buf.Myprintf("alter table %v %s %v (", node.Table, node.Action, node.VindexSpec.Name)
buf.Myprintf("alter vschema on %v add vindex %v (", node.Table, node.VindexSpec.Name)
for i, col := range node.VindexCols {
if i != 0 {
buf.Myprintf(", %v", col)
......@@ -806,7 +815,7 @@ func (node *DDL) Format(buf *TrackedBuffer) {
buf.Myprintf(" %v", node.VindexSpec)
}
case DropColVindexStr:
buf.Myprintf("alter table %v %s %v", node.Table, node.Action, node.VindexSpec.Name)
buf.Myprintf("alter vschema on %v drop vindex %v", node.Table, node.VindexSpec.Name)
default:
buf.Myprintf("%s table %v", node.Action, node.Table)
}
......
......@@ -180,7 +180,7 @@ func skipToEnd(yylex interface{}) {
%token <bytes> NULLX AUTO_INCREMENT APPROXNUM SIGNED UNSIGNED ZEROFILL
// Supported SHOW tokens
%token <bytes> COLLATION DATABASES TABLES VITESS_KEYSPACES VITESS_SHARDS VITESS_TABLETS VSCHEMA_TABLES VITESS_TARGET FULL PROCESSLIST COLUMNS FIELDS ENGINES PLUGINS
%token <bytes> COLLATION DATABASES TABLES VITESS_KEYSPACES VITESS_SHARDS VITESS_TABLETS VSCHEMA VSCHEMA_TABLES VITESS_TARGET FULL PROCESSLIST COLUMNS FIELDS ENGINES PLUGINS
// SET tokens
%token <bytes> NAMES CHARSET GLOBAL SESSION ISOLATION LEVEL READ WRITE ONLY REPEATABLE COMMITTED UNCOMMITTED SERIALIZABLE
......@@ -570,14 +570,6 @@ create_statement:
{
$$ = &DDL{Action: CreateStr, Table: $5.ToViewName()}
}
| CREATE VINDEX sql_id vindex_type_opt vindex_params_opt
{
$$ = &DDL{Action: CreateVindexStr, VindexSpec: &VindexSpec{
Name: $3,
Type: $4,
Params: $5,
}}
}
| CREATE DATABASE not_exists_opt ID ddl_skip_to_end
{
$$ = &DBDDL{Action: CreateStr, DBName: string($4)}
......@@ -1300,7 +1292,47 @@ alter_statement:
{
$$ = &DDL{Action: AlterStr, Table: $4}
}
| ALTER ignore_opt TABLE table_name ADD VINDEX sql_id '(' column_list ')' vindex_type_opt vindex_params_opt
| ALTER ignore_opt TABLE table_name RENAME to_opt table_name
{
// Change this to a rename statement
$$ = &DDL{Action: RenameStr, FromTables: TableNames{$4}, ToTables: TableNames{$7}}
}
| ALTER ignore_opt TABLE table_name RENAME index_opt skip_to_end
{
// Rename an index can just be an alter
$$ = &DDL{Action: AlterStr, Table: $4}
}
| ALTER VIEW table_name ddl_skip_to_end
{
$$ = &DDL{Action: AlterStr, Table: $3.ToViewName()}
}
| ALTER ignore_opt TABLE table_name partition_operation
{
$$ = &DDL{Action: AlterStr, Table: $4, PartitionSpec: $5}
}
| ALTER VSCHEMA CREATE VINDEX sql_id vindex_type_opt vindex_params_opt
{
$$ = &DDL{Action: CreateVindexStr, VindexSpec: &VindexSpec{
Name: $5,
Type: $6,
Params: $7,
}}
}
| ALTER VSCHEMA DROP VINDEX sql_id
{
$$ = &DDL{Action: DropVindexStr, VindexSpec: &VindexSpec{
Name: $5,
}}
}
| ALTER VSCHEMA ADD TABLE table_name
{
$$ = &DDL{Action: AddVschemaTableStr, Table: $5}
}
| ALTER VSCHEMA DROP TABLE table_name
{
$$ = &DDL{Action: DropVschemaTableStr, Table: $5}
}
| ALTER VSCHEMA ON table_name ADD VINDEX sql_id '(' column_list ')' vindex_type_opt vindex_params_opt
{
$$ = &DDL{
Action: AddColVindexStr,
......@@ -1313,7 +1345,7 @@ alter_statement:
VindexCols: $9,
}
}
| ALTER ignore_opt TABLE table_name DROP VINDEX sql_id
| ALTER VSCHEMA ON table_name DROP VINDEX sql_id
{
$$ = &DDL{
Action: DropColVindexStr,
......@@ -1323,24 +1355,6 @@ alter_statement:
},
}
}
| ALTER ignore_opt TABLE table_name RENAME to_opt table_name
{
// Change this to a rename statement
$$ = &DDL{Action: RenameStr, FromTables: TableNames{$4}, ToTables: TableNames{$7}}
}
| ALTER ignore_opt TABLE table_name RENAME index_opt skip_to_end
{
// Rename an index can just be an alter
$$ = &DDL{Action: AlterStr, Table: $4}
}
| ALTER VIEW table_name ddl_skip_to_end
{
$$ = &DDL{Action: AlterStr, Table: $3.ToViewName()}
}
| ALTER ignore_opt TABLE table_name partition_operation
{
$$ = &DDL{Action: AlterStr, Table: $4, PartitionSpec: $5}
}
alter_object_type:
COLUMN
......@@ -1535,10 +1549,6 @@ show_statement:
{
$$ = &Show{Scope: $2, Type: string($3)}
}
| SHOW VINDEXES
{
$$ = &Show{Type: string($2)}
}
| SHOW COLLATION
{
$$ = &Show{Type: string($2)}
......@@ -1549,10 +1559,6 @@ show_statement:
showCollationFilterOpt := $4
$$ = &Show{Type: string($2), ShowCollationFilterOpt: &showCollationFilterOpt}
}
| SHOW VINDEXES ON table_name
{
$$ = &Show{Type: string($2), OnTable: $4}
}
| SHOW VITESS_KEYSPACES
{
$$ = &Show{Type: string($2)}
......@@ -1569,9 +1575,17 @@ show_statement:
{
$$ = &Show{Type: string($2)}
}
| SHOW VSCHEMA_TABLES
| SHOW VSCHEMA TABLES
{
$$ = &Show{Type: string($2)}
$$ = &Show{Type: string($2) + " " + string($3)}
}
| SHOW VSCHEMA VINDEXES
{
$$ = &Show{Type: string($2) + " " + string($3)}
}
| SHOW VSCHEMA VINDEXES ON table_name
{
$$ = &Show{Type: string($2) + " " + string($3), OnTable: $5}
}
| SHOW WARNINGS
{
......@@ -3351,6 +3365,7 @@ non_reserved_keyword:
| VITESS_KEYSPACES
| VITESS_SHARDS
| VITESS_TABLETS
| VSCHEMA
| VSCHEMA_TABLES
| VITESS_TARGET
| WARNINGS
......
......@@ -393,6 +393,7 @@ var keywords = map[string]int{
"vitess_shards": VITESS_SHARDS,
"vitess_tablets": VITESS_TABLETS,
"vitess_target": VITESS_TARGET,
"vschema": VSCHEMA,
"vschema_tables": VSCHEMA_TABLES,
"warnings": WARNINGS,
"when": WHEN,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册