From f3d3fe362dee306e89d05a6fb640c6982c12b1bb Mon Sep 17 00:00:00 2001 From: bianyucheng Date: Mon, 11 Sep 2023 17:11:00 +0800 Subject: [PATCH 1/7] check union index --- sqle/driver/mysql/audit_test.go | 90 ++++++++++++++++++++++++++++ sqle/driver/mysql/rule/rule.go | 66 +++++++++++++++++++- sqle/driver/mysql/session/context.go | 23 +++++++ 3 files changed, 178 insertions(+), 1 deletion(-) diff --git a/sqle/driver/mysql/audit_test.go b/sqle/driver/mysql/audit_test.go index 67b557b77a..c4f532fa99 100644 --- a/sqle/driver/mysql/audit_test.go +++ b/sqle/driver/mysql/audit_test.go @@ -5679,3 +5679,93 @@ func TestDMLCheckIndexSelectivity(t *testing.T) { } +func TestDDLCheckCompositeIndexDistinction(t *testing.T) { + rule := rulepkg.RuleHandlerMap[rulepkg.DDLCheckCompositeIndexDistinction].Rule + e, handler, err := executor.NewMockExecutor() + assert.NoError(t, err) + + inspect1 := NewMockInspect(e) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("50.0000")) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("30.0000")) + runSingleRuleInspectCase(rule, t, "", inspect1, "CREATE INDEX idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult()) + + inspect2 := NewMockInspect(e) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("50.0000")) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("80.0000")) + runSingleRuleInspectCase(rule, t, "", inspect2, "CREATE INDEX idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) + + inspect3 := NewMockInspect(e) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("50.0000")) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("80.0000")) + runSingleRuleInspectCase(rule, t, "", inspect3, "ALTER TABLE exist_db.exist_tb_1 ADD INDEX index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) + + inspect4 := NewMockInspect(e) + runSingleRuleInspectCase(rule, t, "", inspect4, "ALTER TABLE exist_db.exist_tb_1 drop INDEX idx_1;", newTestResult()) + + inspect5 := NewMockInspect(e) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("50.0000")) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("30.0000")) + runSingleRuleInspectCase(rule, t, "", inspect5, "ALTER TABLE exist_db.exist_tb_1 ADD INDEX index_name ( v1, v2);", newTestResult()) + + inspect6 := NewMockInspect(e) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("50.0000")) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("80.0000")) + runSingleRuleInspectCase(rule, t, "", inspect6, "ALTER TABLE exist_db.exist_tb_1 ADD unique INDEX index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) + + inspect7 := NewMockInspect(e) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("50.0000")) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("30.0000")) + runSingleRuleInspectCase(rule, t, "", inspect7, "CREATE index idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult()) + + inspect8 := NewMockInspect(e) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("50.0000")) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("80.0000")) + runSingleRuleInspectCase(rule, t, "", inspect8, "CREATE index idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) + + inspect9 := NewMockInspect(e) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("50.0000")) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("80.0000")) + runSingleRuleInspectCase(rule, t, "", inspect9, "ALTER TABLE exist_db.exist_tb_1 ADD key index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) + + inspect10 := NewMockInspect(e) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("50.0000")) + handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). + WillReturnRows(sqlmock.NewRows([]string{"v1"}). + AddRow("80.0000")) + runSingleRuleInspectCase(rule, t, "", inspect10, "ALTER TABLE exist_db.exist_tb_1 ADD unique key index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) + +} diff --git a/sqle/driver/mysql/rule/rule.go b/sqle/driver/mysql/rule/rule.go index c74c3e5f66..891984aa34 100644 --- a/sqle/driver/mysql/rule/rule.go +++ b/sqle/driver/mysql/rule/rule.go @@ -11,6 +11,8 @@ import ( "strings" "unicode" + "math" + "github.com/actiontech/sqle/sqle/driver/mysql/executor" "github.com/actiontech/sqle/sqle/driver/mysql/keyword" "github.com/actiontech/sqle/sqle/driver/mysql/session" @@ -114,6 +116,7 @@ const ( DDLCheckAutoIncrementFieldNum = "ddl_check_auto_increment_field_num" DDLCheckAllIndexNotNullConstraint = "ddl_check_all_index_not_null_constraint" DDLCheckColumnNotNULL = "ddl_check_column_not_null" + DDLCheckCompositeIndexDistinction = "ddl_check_composite_index_distinction" ) // inspector DML rules @@ -2155,6 +2158,18 @@ var RuleHandlers = []RuleHandler{ Message: "索引:%v,未超过区分度阈值:%v,建议使用超过阈值的索引。", Func: checkIndexSelectivity, }, + { + Rule: driverV2.Rule{ + Name: DDLCheckCompositeIndexDistinction, + Desc: "建议在组合索引中将区分度高的字段靠前放", + Annotation: "将区分度高的字段靠前放置在组合索引中有助于提高索引的查询性能,因为它能更快地减小数据范围,提高检索效率。", + Level: driverV2.RuleLevelNotice, + Category: RuleTypeDDLConvention, + }, + AllowOffline: false, + Message: "联合索引中,字段:%v区分度较高应放在左侧", + Func: checkCompositeIndexSelectivity, + }, } func checkFieldNotNUllMustContainDefaultValue(input *RuleHandlerInput) error { @@ -6120,7 +6135,6 @@ func checkColumnNotNull(input *RuleHandlerInput) error { return nil } - func getColumnFromIndexesInfoByIndexName(indexesInfo []*executor.TableIndexesInfo, indexName string) []string { indexColumns := []string{} for _, info := range indexesInfo { @@ -6175,3 +6189,53 @@ func checkIndexSelectivity(input *RuleHandlerInput) error { } return nil } + +func checkCompositeIndexSelectivity(input *RuleHandlerInput) error { + indexSlice := []string{} + schema := input.Ctx.CurrentSchema() + table := "" + switch stmt := input.Node.(type) { + case *ast.CreateIndexStmt: + for _, indexPart := range stmt.IndexPartSpecifications { + indexSlice = append(indexSlice, indexPart.Column.Name.O) + } + if stmt.Table.Schema.O != "" { + schema = stmt.Table.Schema.O + } + table = stmt.Table.Name.O + case *ast.AlterTableStmt: + for _, spec := range stmt.Specs { + if spec.Constraint == nil { + continue + } + if spec.Constraint.Tp != ast.ConstraintIndex && spec.Constraint.Tp != ast.ConstraintUniq { + continue + } + for _, key := range spec.Constraint.Keys { + indexSlice = append(indexSlice, key.Column.Name.O) + } + if stmt.Table.Schema.O != "" { + schema = stmt.Table.Schema.O + } + table = stmt.Table.Name.O + } + } + currentSelectivityValue := math.Inf(1) + for _, indexColumn := range indexSlice { + selectivityValue, err := input.Ctx.GetIndexSelectivityValue(schema, table, indexColumn) + if err != nil { + return err + } + // 区分度为0,代表数据表中没有数据 + if selectivityValue == float64(0) { + return nil + } + if selectivityValue > currentSelectivityValue { + addResult(input.Res, input.Rule, input.Rule.Name, indexColumn) + return nil + } + currentSelectivityValue = selectivityValue + } + + return nil +} diff --git a/sqle/driver/mysql/session/context.go b/sqle/driver/mysql/session/context.go index e14aa39b02..f7ce68612d 100644 --- a/sqle/driver/mysql/session/context.go +++ b/sqle/driver/mysql/session/context.go @@ -902,3 +902,26 @@ func (c *Context) GetExecutor() *executor.Executor { func (c *Context) GetTableIndexesInfo(schema, tableName string) ([]*executor.TableIndexesInfo, error) { return c.e.GetTableIndexesInfo(schema, tableName) } + +func (c *Context) GetIndexSelectivityValue(schema, table, column string) (float64, error) { + columnField := fmt.Sprintf("COUNT( DISTINCT ( %v ) ) / COUNT( * ) * 100", column) + tableWithSchema := fmt.Sprintf("%v.%v", schema, table) + result, err := c.e.Db.Query(fmt.Sprintf("SELECT %v FROM %v", columnField, tableWithSchema)) + if err != nil { + return -1, fmt.Errorf("query index distinction for table error: %v", err) + } + indexSelectivityValue := -1.0 + for _, r := range result { + for _, value := range r { + // 当表里没数据时上面的SQL查出来的结果为Null + if value.String == "" { + value.String = "0" + } + indexSelectivityValue, err = strconv.ParseFloat(value.String, 64) + if err != nil { + return -1, err + } + } + } + return indexSelectivityValue, nil +} From 2ef7b20f37297a669da07fcaed8879b075d2b4e0 Mon Sep 17 00:00:00 2001 From: bianyucheng Date: Tue, 12 Sep 2023 19:08:20 +0800 Subject: [PATCH 2/7] change unit test --- sqle/driver/mysql/audit_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sqle/driver/mysql/audit_test.go b/sqle/driver/mysql/audit_test.go index c4f532fa99..b4293be1c8 100644 --- a/sqle/driver/mysql/audit_test.go +++ b/sqle/driver/mysql/audit_test.go @@ -132,6 +132,7 @@ func runDefaultRulesInspectCase(t *testing.T, desc string, i *MysqlDriverImpl, s rulepkg.DDLCheckAllIndexNotNullConstraint: {}, rulepkg.DMLCheckAggregate: {}, rulepkg.DDLCheckColumnNotNULL: {}, + rulepkg.DDLCheckCompositeIndexDistinction: {}, } for i := range rulepkg.RuleHandlers { handler := rulepkg.RuleHandlers[i] From 32c67bc797e1eb775c78484cb5f29a66df9e737e Mon Sep 17 00:00:00 2001 From: bianyucheng Date: Wed, 13 Sep 2023 16:36:31 +0800 Subject: [PATCH 3/7] use GetTableRowCount and GetColumnCardinality instead GetIndexSelectivityValue --- sqle/driver/mysql/rule/rule.go | 32 +++++++++++++++------------- sqle/driver/mysql/session/context.go | 23 -------------------- 2 files changed, 17 insertions(+), 38 deletions(-) diff --git a/sqle/driver/mysql/rule/rule.go b/sqle/driver/mysql/rule/rule.go index 891984aa34..5cf86e22d9 100644 --- a/sqle/driver/mysql/rule/rule.go +++ b/sqle/driver/mysql/rule/rule.go @@ -6192,17 +6192,13 @@ func checkIndexSelectivity(input *RuleHandlerInput) error { func checkCompositeIndexSelectivity(input *RuleHandlerInput) error { indexSlice := []string{} - schema := input.Ctx.CurrentSchema() - table := "" + table := &ast.TableName{} switch stmt := input.Node.(type) { case *ast.CreateIndexStmt: for _, indexPart := range stmt.IndexPartSpecifications { indexSlice = append(indexSlice, indexPart.Column.Name.O) } - if stmt.Table.Schema.O != "" { - schema = stmt.Table.Schema.O - } - table = stmt.Table.Name.O + table = stmt.Table case *ast.AlterTableStmt: for _, spec := range stmt.Specs { if spec.Constraint == nil { @@ -6214,22 +6210,28 @@ func checkCompositeIndexSelectivity(input *RuleHandlerInput) error { for _, key := range spec.Constraint.Keys { indexSlice = append(indexSlice, key.Column.Name.O) } - if stmt.Table.Schema.O != "" { - schema = stmt.Table.Schema.O - } - table = stmt.Table.Name.O + table = stmt.Table } } + exist, err := input.Ctx.IsTableExist(table) + if err != nil { + return err + } + if !exist { + return nil + } + currentSelectivityValue := math.Inf(1) + tableRows, err := input.Ctx.GetTableRowCount(table) + if err != nil { + return err + } for _, indexColumn := range indexSlice { - selectivityValue, err := input.Ctx.GetIndexSelectivityValue(schema, table, indexColumn) + columnCardinality, err := input.Ctx.GetColumnCardinality(table, indexColumn) if err != nil { - return err - } - // 区分度为0,代表数据表中没有数据 - if selectivityValue == float64(0) { return nil } + selectivityValue := float64(columnCardinality) / float64(tableRows) if selectivityValue > currentSelectivityValue { addResult(input.Res, input.Rule, input.Rule.Name, indexColumn) return nil diff --git a/sqle/driver/mysql/session/context.go b/sqle/driver/mysql/session/context.go index f7ce68612d..e14aa39b02 100644 --- a/sqle/driver/mysql/session/context.go +++ b/sqle/driver/mysql/session/context.go @@ -902,26 +902,3 @@ func (c *Context) GetExecutor() *executor.Executor { func (c *Context) GetTableIndexesInfo(schema, tableName string) ([]*executor.TableIndexesInfo, error) { return c.e.GetTableIndexesInfo(schema, tableName) } - -func (c *Context) GetIndexSelectivityValue(schema, table, column string) (float64, error) { - columnField := fmt.Sprintf("COUNT( DISTINCT ( %v ) ) / COUNT( * ) * 100", column) - tableWithSchema := fmt.Sprintf("%v.%v", schema, table) - result, err := c.e.Db.Query(fmt.Sprintf("SELECT %v FROM %v", columnField, tableWithSchema)) - if err != nil { - return -1, fmt.Errorf("query index distinction for table error: %v", err) - } - indexSelectivityValue := -1.0 - for _, r := range result { - for _, value := range r { - // 当表里没数据时上面的SQL查出来的结果为Null - if value.String == "" { - value.String = "0" - } - indexSelectivityValue, err = strconv.ParseFloat(value.String, 64) - if err != nil { - return -1, err - } - } - } - return indexSelectivityValue, nil -} From 0cbe0ba797702e0c1f673d54cf5ab568a6c5d3df Mon Sep 17 00:00:00 2001 From: bianyucheng Date: Wed, 13 Sep 2023 16:55:31 +0800 Subject: [PATCH 4/7] update unit test --- sqle/driver/mysql/audit_test.go | 108 ++++++++++++++++---------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/sqle/driver/mysql/audit_test.go b/sqle/driver/mysql/audit_test.go index b4293be1c8..a239a935ea 100644 --- a/sqle/driver/mysql/audit_test.go +++ b/sqle/driver/mysql/audit_test.go @@ -5686,87 +5686,87 @@ func TestDDLCheckCompositeIndexDistinction(t *testing.T) { assert.NoError(t, err) inspect1 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("50.0000")) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("30.0000")) + handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). + WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) runSingleRuleInspectCase(rule, t, "", inspect1, "CREATE INDEX idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult()) inspect2 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("50.0000")) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("80.0000")) + handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). + WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) runSingleRuleInspectCase(rule, t, "", inspect2, "CREATE INDEX idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) inspect3 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("50.0000")) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("80.0000")) + handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). + WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) runSingleRuleInspectCase(rule, t, "", inspect3, "ALTER TABLE exist_db.exist_tb_1 ADD INDEX index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) inspect4 := NewMockInspect(e) runSingleRuleInspectCase(rule, t, "", inspect4, "ALTER TABLE exist_db.exist_tb_1 drop INDEX idx_1;", newTestResult()) inspect5 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("50.0000")) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("30.0000")) + handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). + WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("90")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) runSingleRuleInspectCase(rule, t, "", inspect5, "ALTER TABLE exist_db.exist_tb_1 ADD INDEX index_name ( v1, v2);", newTestResult()) inspect6 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("50.0000")) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("80.0000")) + handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). + WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) runSingleRuleInspectCase(rule, t, "", inspect6, "ALTER TABLE exist_db.exist_tb_1 ADD unique INDEX index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) inspect7 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("50.0000")) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("30.0000")) + handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). + WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("90")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) runSingleRuleInspectCase(rule, t, "", inspect7, "CREATE index idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult()) inspect8 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("50.0000")) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("80.0000")) + handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). + WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) runSingleRuleInspectCase(rule, t, "", inspect8, "CREATE index idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) inspect9 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("50.0000")) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("80.0000")) + handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). + WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) runSingleRuleInspectCase(rule, t, "", inspect9, "ALTER TABLE exist_db.exist_tb_1 ADD key index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) inspect10 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v1 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("50.0000")) - handler.ExpectQuery(regexp.QuoteMeta("SELECT COUNT( DISTINCT ( v2 ) ) / COUNT( * ) * 100 FROM exist_db.exist_tb_1")). - WillReturnRows(sqlmock.NewRows([]string{"v1"}). - AddRow("80.0000")) + handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). + WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) runSingleRuleInspectCase(rule, t, "", inspect10, "ALTER TABLE exist_db.exist_tb_1 ADD unique key index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) } From 74214a79f5fcdf9e7f307fa1204fd5de69b048d8 Mon Sep 17 00:00:00 2001 From: bianyucheng Date: Wed, 13 Sep 2023 17:03:21 +0800 Subject: [PATCH 5/7] change error code --- sqle/driver/mysql/rule/rule.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqle/driver/mysql/rule/rule.go b/sqle/driver/mysql/rule/rule.go index b4b795124a..6ae4fba25a 100644 --- a/sqle/driver/mysql/rule/rule.go +++ b/sqle/driver/mysql/rule/rule.go @@ -6287,7 +6287,7 @@ func checkCompositeIndexSelectivity(input *RuleHandlerInput) error { for _, indexColumn := range indexSlice { columnCardinality, err := input.Ctx.GetColumnCardinality(table, indexColumn) if err != nil { - return nil + return err } selectivityValue := float64(columnCardinality) / float64(tableRows) if selectivityValue > currentSelectivityValue { From f8a93e97ca60e9bde703ff8b9d2f0ce2cc6fcf0f Mon Sep 17 00:00:00 2001 From: bianyucheng Date: Wed, 13 Sep 2023 20:19:36 +0800 Subject: [PATCH 6/7] add CreateTableStmt && change message --- sqle/driver/mysql/rule/rule.go | 85 +++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/sqle/driver/mysql/rule/rule.go b/sqle/driver/mysql/rule/rule.go index 6ae4fba25a..08536650bb 100644 --- a/sqle/driver/mysql/rule/rule.go +++ b/sqle/driver/mysql/rule/rule.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "math" "reflect" "regexp" "sort" @@ -2192,7 +2191,7 @@ var RuleHandlers = []RuleHandler{ Category: RuleTypeDDLConvention, }, AllowOffline: false, - Message: "联合索引中,字段:%v区分度较高应放在左侧", + Message: "建议在组合索引中将区分度高的字段靠前放,%v", Func: checkCompositeIndexSelectivity, }, } @@ -6249,13 +6248,18 @@ func checkTableRows(input *RuleHandlerInput) error { } func checkCompositeIndexSelectivity(input *RuleHandlerInput) error { - indexSlice := []string{} + indexSlices := [][]string{} table := &ast.TableName{} switch stmt := input.Node.(type) { case *ast.CreateIndexStmt: + singleIndexSlice := []string{} + if len(stmt.IndexPartSpecifications) == 1 { + return nil + } for _, indexPart := range stmt.IndexPartSpecifications { - indexSlice = append(indexSlice, indexPart.Column.Name.O) + singleIndexSlice = append(singleIndexSlice, indexPart.Column.Name.O) } + indexSlices = append(indexSlices, singleIndexSlice) table = stmt.Table case *ast.AlterTableStmt: for _, spec := range stmt.Specs { @@ -6265,11 +6269,34 @@ func checkCompositeIndexSelectivity(input *RuleHandlerInput) error { if spec.Constraint.Tp != ast.ConstraintIndex && spec.Constraint.Tp != ast.ConstraintUniq { continue } + singleIndexSlice := []string{} + if len(spec.Constraint.Keys) == 1 { + continue + } for _, key := range spec.Constraint.Keys { - indexSlice = append(indexSlice, key.Column.Name.O) + singleIndexSlice = append(singleIndexSlice, key.Column.Name.O) + } + indexSlices = append(indexSlices, singleIndexSlice) + } + table = stmt.Table + case *ast.CreateTableStmt: + if stmt.Constraints == nil { + return nil + } + for _, con := range stmt.Constraints { + if con.Tp != ast.ConstraintIndex && con.Tp != ast.ConstraintUniq { + continue + } + singleIndexSlice := []string{} + if len(con.Keys) == 1 { + continue + } + for _, key := range con.Keys { + singleIndexSlice = append(singleIndexSlice, key.Column.Name.O) } - table = stmt.Table + indexSlices = append(indexSlices, singleIndexSlice) } + table = stmt.Table } exist, err := input.Ctx.IsTableExist(table) if err != nil { @@ -6279,23 +6306,39 @@ func checkCompositeIndexSelectivity(input *RuleHandlerInput) error { return nil } - currentSelectivityValue := math.Inf(1) - tableRows, err := input.Ctx.GetTableRowCount(table) - if err != nil { - return err - } - for _, indexColumn := range indexSlice { - columnCardinality, err := input.Ctx.GetColumnCardinality(table, indexColumn) - if err != nil { - return err + noticeInfos := []string{} + for _, singleIndexSlice := range indexSlices { + var indexSelectValueSlice []struct { + Index string + Value int } - selectivityValue := float64(columnCardinality) / float64(tableRows) - if selectivityValue > currentSelectivityValue { - addResult(input.Res, input.Rule, input.Rule.Name, indexColumn) - return nil + sortIndexes := make([]string, len(singleIndexSlice)) + for _, indexColumn := range singleIndexSlice { + columnCardinality, err := input.Ctx.GetColumnCardinality(table, indexColumn) + if err != nil { + return err + } + selectivityValue := columnCardinality + indexSelectValueSlice = append(indexSelectValueSlice, struct { + Index string + Value int + }{indexColumn, selectivityValue}) + } + sort.Slice(indexSelectValueSlice, func(i, j int) bool { + return indexSelectValueSlice[i].Value > indexSelectValueSlice[j].Value + }) + for i, kv := range indexSelectValueSlice { + sortIndexes[i] = kv.Index + } + for ind, indexColumn := range singleIndexSlice { + if indexColumn != indexSelectValueSlice[ind].Index { + noticeInfos = append(noticeInfos, fmt.Sprintf("(%s)可调整为(%s)", strings.Join(singleIndexSlice, ","), strings.Join(sortIndexes, ","))) + break + } } - currentSelectivityValue = selectivityValue } - + if len(noticeInfos) > 0 { + addResult(input.Res, input.Rule, input.Rule.Name, strings.Join(noticeInfos, ",")) + } return nil } From f38817da5df55bcee8cb8bd1947cb266dcc888ff Mon Sep 17 00:00:00 2001 From: bianyucheng Date: Wed, 13 Sep 2023 20:19:50 +0800 Subject: [PATCH 7/7] update unit test --- sqle/driver/mysql/audit_test.go | 73 +++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/sqle/driver/mysql/audit_test.go b/sqle/driver/mysql/audit_test.go index 767eb3f604..05cb0aedd5 100644 --- a/sqle/driver/mysql/audit_test.go +++ b/sqle/driver/mysql/audit_test.go @@ -113,7 +113,7 @@ func NewMockInspectWithIsExecutedSQL(e *executor.Executor) *MysqlDriverImpl { DDLOSCMinSize: 16, DDLGhostMinSize: 16, DMLRollbackMaxRows: 1000, - isExecutedSQL: true, + isExecutedSQL: true, }, dbConn: e, } @@ -156,8 +156,6 @@ func runDefaultRulesInspectCase(t *testing.T, desc string, i *MysqlDriverImpl, s rulepkg.DDLCheckColumnNotNULL: {}, rulepkg.DDLCheckTableRows: {}, rulepkg.DDLCheckCompositeIndexDistinction: {}, - - } for i := range rulepkg.RuleHandlers { handler := rulepkg.RuleHandlers[i] @@ -5730,8 +5728,6 @@ func TestDDLCheckCompositeIndexDistinction(t *testing.T) { assert.NoError(t, err) inspect1 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). - WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). @@ -5739,29 +5735,20 @@ func TestDDLCheckCompositeIndexDistinction(t *testing.T) { runSingleRuleInspectCase(rule, t, "", inspect1, "CREATE INDEX idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult()) inspect2 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). - WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) - runSingleRuleInspectCase(rule, t, "", inspect2, "CREATE INDEX idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) + runSingleRuleInspectCase(rule, t, "", inspect2, "CREATE INDEX idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "(v1,v2)可调整为(v2,v1)")) inspect3 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). - WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) - runSingleRuleInspectCase(rule, t, "", inspect3, "ALTER TABLE exist_db.exist_tb_1 ADD INDEX index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) - - inspect4 := NewMockInspect(e) - runSingleRuleInspectCase(rule, t, "", inspect4, "ALTER TABLE exist_db.exist_tb_1 drop INDEX idx_1;", newTestResult()) + runSingleRuleInspectCase(rule, t, "", inspect3, "ALTER TABLE exist_db.exist_tb_1 ADD INDEX index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "(v1,v2)可调整为(v2,v1)")) inspect5 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). - WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("90")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). @@ -5769,17 +5756,13 @@ func TestDDLCheckCompositeIndexDistinction(t *testing.T) { runSingleRuleInspectCase(rule, t, "", inspect5, "ALTER TABLE exist_db.exist_tb_1 ADD INDEX index_name ( v1, v2);", newTestResult()) inspect6 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). - WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) - runSingleRuleInspectCase(rule, t, "", inspect6, "ALTER TABLE exist_db.exist_tb_1 ADD unique INDEX index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) + runSingleRuleInspectCase(rule, t, "", inspect6, "ALTER TABLE exist_db.exist_tb_1 ADD unique INDEX index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "(v1,v2)可调整为(v2,v1)")) inspect7 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). - WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("90")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). @@ -5787,30 +5770,60 @@ func TestDDLCheckCompositeIndexDistinction(t *testing.T) { runSingleRuleInspectCase(rule, t, "", inspect7, "CREATE index idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult()) inspect8 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). - WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) - runSingleRuleInspectCase(rule, t, "", inspect8, "CREATE index idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) + runSingleRuleInspectCase(rule, t, "", inspect8, "CREATE index idx_union1 ON exist_db.exist_tb_1 (v1,v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "(v1,v2)可调整为(v2,v1)")) inspect9 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). - WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) - runSingleRuleInspectCase(rule, t, "", inspect9, "ALTER TABLE exist_db.exist_tb_1 ADD key index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) + runSingleRuleInspectCase(rule, t, "", inspect9, "ALTER TABLE exist_db.exist_tb_1 ADD key index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "(v1,v2)可调整为(v2,v1)")) inspect10 := NewMockInspect(e) - handler.ExpectQuery(regexp.QuoteMeta("show table status from exist_db where name = 'exist_tb_1'")). - WillReturnRows(sqlmock.NewRows([]string{"Rows"}).AddRow("100")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) - runSingleRuleInspectCase(rule, t, "", inspect10, "ALTER TABLE exist_db.exist_tb_1 ADD unique key index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "v2")) + runSingleRuleInspectCase(rule, t, "", inspect10, "ALTER TABLE exist_db.exist_tb_1 ADD unique key index_name ( v1, v2);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "(v1,v2)可调整为(v2,v1)")) + + inspect11 := NewMockInspect(e) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_1`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) + runSingleRuleInspectCase(rule, t, "", inspect11, "ALTER TABLE exist_db.exist_tb_1 ADD unique key index_name (v1, v2),ADD unique key index_name1 (v1);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "(v1,v2)可调整为(v2,v1)")) + inspect12 := NewMockInspect(e) + runSingleRuleInspectCase(rule, t, "", inspect12, "ALTER TABLE exist_db.exist_tb_1 ADD unique key index_name1 (v1);", newTestResult()) + + inspect13 := NewMockInspect(e) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_3`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("100")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_3`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v3`) as cardinality from `exist_db`.`exist_tb_3`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) + runSingleRuleInspectCase(rule, t, "", inspect13, "ALTER TABLE exist_db.exist_tb_3 ADD index index_name (v1, v2, v3), add index index_name1(v3,v2,v1);", newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "(v1,v2,v3)可调整为(v1,v3,v2),(v3,v2,v1)可调整为(v1,v3,v2)")) + + inspect14 := NewMockInspectWithIsExecutedSQL(e) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v1`) as cardinality from `exist_db`.`exist_tb_3`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("100")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v2`) as cardinality from `exist_db`.`exist_tb_3`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("60")) + handler.ExpectQuery(regexp.QuoteMeta("select count(distinct `v3`) as cardinality from `exist_db`.`exist_tb_3`")). + WillReturnRows(sqlmock.NewRows([]string{"cardinality"}).AddRow("80")) + runSingleRuleInspectCase(rule, t, "", inspect14, ` + CREATE TABLE exist_db.exist_tb_3 ( + id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT "unit test", + v1 varchar(255) NOT NULL COMMENT "unit test", + v2 varchar(255) COMMENT "unit test", + v3 int COMMENT "unit test", + Index index_name (v1, v2, v3), + Index index_name1(v3,v2,v1) + )ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT="uint test";`, + newTestResult().addResult(rulepkg.DDLCheckCompositeIndexDistinction, "(v1,v2,v3)可调整为(v1,v3,v2),(v3,v2,v1)可调整为(v1,v3,v2)")) }