diff --git a/config/lint.go b/config/lint.go index 7019d4fff..00da7bfe2 100644 --- a/config/lint.go +++ b/config/lint.go @@ -11,14 +11,17 @@ import ( // Lint is the struct for lint config type Lint struct { - RequireTableComment RequireTableComment `yaml:"requireTableComment"` - RequireColumnComment RequireColumnComment `yaml:"requireColumnComment"` - UnrelatedTable UnrelatedTable `yaml:"unrelatedTable"` - ColumnCount ColumnCount `yaml:"columnCount"` - RequireColumns RequireColumns `yaml:"requireColumns"` - DuplicateRelations DuplicateRelations `yaml:"duplicateRelations"` - RequireForeignKeyIndex RequireForeignKeyIndex `yaml:"requireForeignKeyIndex"` - LabelStyleBigQuery LabelStyleBigQuery `yaml:"labelStyleBigQuery"` + RequireTableComment RequireTableComment `yaml:"requireTableComment"` + RequireColumnComment RequireColumnComment `yaml:"requireColumnComment"` + RequireIndexComment RequireIndexComment `yaml:"requireIndexComment"` + RequireConstraintComment RequireConstraintComment `yaml:"requireConstraintComment"` + RequireTriggerComment RequireTriggerComment `yaml:"requireTriggerComment"` + UnrelatedTable UnrelatedTable `yaml:"unrelatedTable"` + ColumnCount ColumnCount `yaml:"columnCount"` + RequireColumns RequireColumns `yaml:"requireColumns"` + DuplicateRelations DuplicateRelations `yaml:"duplicateRelations"` + RequireForeignKeyIndex RequireForeignKeyIndex `yaml:"requireForeignKeyIndex"` + LabelStyleBigQuery LabelStyleBigQuery `yaml:"labelStyleBigQuery"` } // RuleWarn is struct of Rule error @@ -114,6 +117,138 @@ func (r RequireColumnComment) Check(s *schema.Schema, exclude []string) []RuleWa return warns } +// RequireIndexComment checks index comment +type RequireIndexComment struct { + Enabled bool `yaml:"enabled"` + Exclude []string `yaml:"exclude"` + ExcludeTables []string `yaml:"excludeTables"` +} + +// IsEnabled return Rule is enabled or not +func (r RequireIndexComment) IsEnabled() bool { + return r.Enabled +} + +// Check index comment +func (r RequireIndexComment) Check(s *schema.Schema, exclude []string) []RuleWarn { + warns := []RuleWarn{} + if !r.IsEnabled() { + return warns + } + msg := "index comment required." + + nt := s.NormalizeTableNames(r.ExcludeTables) + for _, t := range s.Tables { + if contains(exclude, t.Name) { + continue + } + if contains(nt, t.Name) { + continue + } + for _, i := range t.Indexes { + target := fmt.Sprintf("%s.%s", t.Name, i.Name) + if contains(r.Exclude, i.Name) || contains(r.Exclude, target) { + continue + } + if i.Comment == "" { + warns = append(warns, RuleWarn{ + Target: target, + Message: msg, + }) + } + } + } + return warns +} + +// RequireConstraintComment checks constraint comment +type RequireConstraintComment struct { + Enabled bool `yaml:"enabled"` + Exclude []string `yaml:"exclude"` + ExcludeTables []string `yaml:"excludeTables"` +} + +// IsEnabled return Rule is enabled or not +func (r RequireConstraintComment) IsEnabled() bool { + return r.Enabled +} + +// Check constraint comment +func (r RequireConstraintComment) Check(s *schema.Schema, exclude []string) []RuleWarn { + warns := []RuleWarn{} + if !r.IsEnabled() { + return warns + } + msg := "constraint comment required." + + nt := s.NormalizeTableNames(r.ExcludeTables) + for _, t := range s.Tables { + if contains(exclude, t.Name) { + continue + } + if contains(nt, t.Name) { + continue + } + for _, c := range t.Constraints { + target := fmt.Sprintf("%s.%s", t.Name, c.Name) + if contains(r.Exclude, c.Name) || contains(r.Exclude, target) { + continue + } + if c.Comment == "" { + warns = append(warns, RuleWarn{ + Target: target, + Message: msg, + }) + } + } + } + return warns +} + +// RequireTriggerComment checks trigger comment +type RequireTriggerComment struct { + Enabled bool `yaml:"enabled"` + Exclude []string `yaml:"exclude"` + ExcludeTables []string `yaml:"excludeTables"` +} + +// IsEnabled return Rule is enabled or not +func (r RequireTriggerComment) IsEnabled() bool { + return r.Enabled +} + +// Check trigger comment +func (r RequireTriggerComment) Check(s *schema.Schema, exclude []string) []RuleWarn { + warns := []RuleWarn{} + if !r.IsEnabled() { + return warns + } + msg := "trigger comment required." + + nt := s.NormalizeTableNames(r.ExcludeTables) + for _, t := range s.Tables { + if contains(exclude, t.Name) { + continue + } + if contains(nt, t.Name) { + continue + } + for _, trig := range t.Triggers { + target := fmt.Sprintf("%s.%s", t.Name, trig.Name) + if contains(r.Exclude, trig.Name) || contains(r.Exclude, target) { + continue + } + if trig.Comment == "" { + warns = append(warns, RuleWarn{ + Target: target, + Message: msg, + }) + } + } + } + return warns +} + // UnrelatedTable checks isolated table type UnrelatedTable struct { Enabled bool `yaml:"enabled"` diff --git a/config/lint_test.go b/config/lint_test.go index c3531f0b9..d9ecd9fe8 100644 --- a/config/lint_test.go +++ b/config/lint_test.go @@ -55,8 +55,8 @@ func TestRequireColumnComment(t *testing.T) { for i, tt := range tests { r := RequireColumnComment{ - Enabled: tt.enabled, - Exclude: tt.exclude, + Enabled: tt.enabled, + Exclude: tt.exclude, ExcludeTables: tt.excludeTables, } s := newTestSchema() @@ -67,6 +67,96 @@ func TestRequireColumnComment(t *testing.T) { } } +func TestRequireIndexComment(t *testing.T) { + tests := []struct { + enabled bool + lintExclude []string + exclude []string + excludeTables []string + want int + }{ + {true, []string{}, []string{}, []string{}, 1}, + {false, []string{}, []string{}, []string{}, 0}, + {true, []string{"table_a"}, []string{}, []string{}, 0}, + {true, []string{}, []string{"a2_idx"}, []string{}, 0}, + {true, []string{}, []string{"table_a.a2_idx"}, []string{}, 0}, + {true, []string{}, []string{}, []string{"table_a"}, 0}, + } + + for i, tt := range tests { + r := RequireIndexComment{ + Enabled: tt.enabled, + Exclude: tt.exclude, + ExcludeTables: tt.excludeTables, + } + s := newTestSchema() + warns := r.Check(s, tt.lintExclude) + if len(warns) != tt.want { + t.Errorf("TestRequireIndexComment(%d): got %v\nwant %v", i, len(warns), tt.want) + } + } +} + +func TestRequireConstraintComment(t *testing.T) { + tests := []struct { + enabled bool + lintExclude []string + exclude []string + excludeTables []string + want int + }{ + {true, []string{}, []string{}, []string{}, 2}, + {false, []string{}, []string{}, []string{}, 0}, + {true, []string{"table_a"}, []string{}, []string{}, 0}, + {true, []string{}, []string{"a1_b1_fk"}, []string{}, 1}, + {true, []string{}, []string{"a1_unique"}, []string{}, 1}, + {true, []string{}, []string{"table_a.a1_b1_fk"}, []string{}, 1}, + } + + for i, tt := range tests { + r := RequireConstraintComment{ + Enabled: tt.enabled, + Exclude: tt.exclude, + ExcludeTables: tt.excludeTables, + } + s := newTestSchema() + warns := r.Check(s, tt.lintExclude) + if len(warns) != tt.want { + t.Errorf("TestRequireConstraintComment(%d): got %v\nwant %v", i, len(warns), tt.want) + } + } +} + +func TestRequireTriggerComment(t *testing.T) { + tests := []struct { + enabled bool + lintExclude []string + exclude []string + excludeTables []string + want int + }{ + {true, []string{}, []string{}, []string{}, 1}, + {false, []string{}, []string{}, []string{}, 0}, + {true, []string{"table_a"}, []string{}, []string{}, 0}, + {true, []string{}, []string{"update_table_a_column_a2"}, []string{}, 0}, + {true, []string{}, []string{"table_a.update_table_a_column_a2"}, []string{}, 0}, + {true, []string{}, []string{}, []string{"table_a"}, 0}, + } + + for i, tt := range tests { + r := RequireTriggerComment{ + Enabled: tt.enabled, + Exclude: tt.exclude, + ExcludeTables: tt.excludeTables, + } + s := newTestSchema() + warns := r.Check(s, tt.lintExclude) + if len(warns) != tt.want { + t.Errorf("TestRequireTriggerComment(%d): got %v\nwant %v", i, len(warns), tt.want) + } + } +} + func TestUnrelatedTable(t *testing.T) { tests := []struct { enabled bool @@ -403,6 +493,18 @@ func newTestSchema() *schema.Schema { }, } + ta.Triggers = []*schema.Trigger{ + &schema.Trigger{ + Name: "update_table_a_column_a1", + Def: "CREATE CONSTRAINT TRIGGER update_table_a_column_a1 AFTER INSERT OR UPDATE ON table_a", + Comment: "Update column_a1 when update table", + }, + &schema.Trigger{ + Name: "update_table_a_column_a2", + Def: "CREATE CONSTRAINT TRIGGER update_table_a_column_a2 AFTER INSERT OR UPDATE ON table_a", + }, + } + s := &schema.Schema{ Name: "testschema", Labels: schema.Labels{