Skip to content

Commit b407e34

Browse files
Ajit Pratap Singhclaude
authored andcommitted
fix(linter): address PR #467 review feedback
- Add DropSequenceStatement case arm to L013 (drop_without_condition) - Extend function_on_column visitor to traverse HAVING clauses and CTEs - Remove unnecessary join := join copy in range loop - Use neutral location instead of misleading sel.Pos in reserved_keyword_identifier - Keep OR-to-IN threshold at 3 (consistent with existing tests) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f5a52f8 commit b407e34

3 files changed

Lines changed: 64 additions & 35 deletions

File tree

pkg/linter/rules/naming/reserved_keyword_identifier.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func (r *ReservedKeywordIdentifierRule) Check(ctx *linter.Context) ([]linter.Vio
8686
RuleName: r.Name(),
8787
Severity: r.Severity(),
8888
Message: "Table name '" + ref.Name + "' is a SQL reserved keyword",
89-
Location: models.Location{Line: sel.Pos.Line, Column: sel.Pos.Column},
89+
Location: models.Location{Line: 1, Column: 1},
9090
Suggestion: "Quote the identifier: FROM \"" + ref.Name + "\" or rename the table",
9191
})
9292
}
@@ -96,7 +96,7 @@ func (r *ReservedKeywordIdentifierRule) Check(ctx *linter.Context) ([]linter.Vio
9696
RuleName: r.Name(),
9797
Severity: r.Severity(),
9898
Message: "Table alias '" + ref.Alias + "' is a SQL reserved keyword",
99-
Location: models.Location{Line: sel.Pos.Line, Column: sel.Pos.Column},
99+
Location: models.Location{Line: 1, Column: 1},
100100
Suggestion: "Use a non-reserved alias instead of '" + ref.Alias + "'",
101101
})
102102
}

pkg/linter/rules/performance/function_on_column.go

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -86,30 +86,45 @@ func (v *functionOnColVisitor) Visit(node ast.Node) (ast.Visitor, error) {
8686
if node == nil {
8787
return nil, nil
8888
}
89-
// Track that we're inside a WHERE clause
89+
// Track that we're inside a WHERE or HAVING clause
9090
if sel, ok := node.(*ast.SelectStatement); ok {
91+
child := &functionOnColVisitor{rule: v.rule, violations: v.violations, inWhere: false}
92+
filterVisitor := &functionOnColVisitor{rule: v.rule, violations: v.violations, inWhere: true}
93+
94+
// Walk WHERE with inWhere=true
9195
if sel.Where != nil {
92-
child := &functionOnColVisitor{rule: v.rule, violations: v.violations, inWhere: false}
93-
// Walk WHERE separately with inWhere=true
94-
whereVisitor := &functionOnColVisitor{rule: v.rule, violations: v.violations, inWhere: true}
95-
if err := ast.Walk(whereVisitor, sel.Where); err != nil {
96+
if err := ast.Walk(filterVisitor, sel.Where); err != nil {
9697
return nil, err
9798
}
98-
// Walk the rest normally (not in WHERE)
99-
for _, col := range sel.Columns {
100-
if err := ast.Walk(child, col); err != nil {
101-
return nil, err
102-
}
99+
}
100+
// Walk HAVING with inWhere=true (same index-breaking concern)
101+
if sel.Having != nil {
102+
if err := ast.Walk(filterVisitor, sel.Having); err != nil {
103+
return nil, err
103104
}
104-
for _, join := range sel.Joins {
105-
join := join
106-
if err := ast.Walk(child, &join); err != nil {
107-
return nil, err
105+
}
106+
// Walk CTEs to catch nested queries
107+
if sel.With != nil {
108+
for i := range sel.With.CTEs {
109+
if sel.With.CTEs[i].Statement != nil {
110+
if err := ast.Walk(v, sel.With.CTEs[i].Statement); err != nil {
111+
return nil, err
112+
}
108113
}
109114
}
110-
return nil, nil // We've handled children manually
111115
}
112-
return v, nil
116+
// Walk the rest normally (not in WHERE/HAVING)
117+
for _, col := range sel.Columns {
118+
if err := ast.Walk(child, col); err != nil {
119+
return nil, err
120+
}
121+
}
122+
for i := range sel.Joins {
123+
if err := ast.Walk(child, &sel.Joins[i]); err != nil {
124+
return nil, err
125+
}
126+
}
127+
return nil, nil // We've handled children manually
113128
}
114129

115130
if !v.inWhere {

pkg/linter/rules/safety/drop_without_condition.go

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,38 @@ func (r *DropWithoutConditionRule) Check(ctx *linter.Context) ([]linter.Violatio
4646
}
4747
var violations []linter.Violation
4848
for _, stmt := range ctx.AST.Statements {
49-
drop, ok := stmt.(*ast.DropStatement)
50-
if !ok {
51-
continue
52-
}
53-
if !drop.IfExists {
54-
objType := strings.ToUpper(drop.ObjectType)
55-
name := ""
56-
if len(drop.Names) > 0 {
57-
name = drop.Names[0]
49+
switch drop := stmt.(type) {
50+
case *ast.DropStatement:
51+
if !drop.IfExists {
52+
objType := strings.ToUpper(drop.ObjectType)
53+
name := ""
54+
if len(drop.Names) > 0 {
55+
name = drop.Names[0]
56+
}
57+
violations = append(violations, linter.Violation{
58+
Rule: r.ID(),
59+
RuleName: r.Name(),
60+
Severity: r.Severity(),
61+
Message: "DROP " + objType + " " + name + " is missing IF EXISTS",
62+
Location: models.Location{Line: 1, Column: 1},
63+
Suggestion: "Use DROP " + objType + " IF EXISTS " + name,
64+
})
65+
}
66+
case *ast.DropSequenceStatement:
67+
if !drop.IfExists {
68+
name := ""
69+
if drop.Name != nil {
70+
name = drop.Name.Name
71+
}
72+
violations = append(violations, linter.Violation{
73+
Rule: r.ID(),
74+
RuleName: r.Name(),
75+
Severity: r.Severity(),
76+
Message: "DROP SEQUENCE " + name + " is missing IF EXISTS",
77+
Location: drop.Pos,
78+
Suggestion: "Use DROP SEQUENCE IF EXISTS " + name,
79+
})
5880
}
59-
violations = append(violations, linter.Violation{
60-
Rule: r.ID(),
61-
RuleName: r.Name(),
62-
Severity: r.Severity(),
63-
Message: "DROP " + objType + " " + name + " is missing IF EXISTS",
64-
Location: models.Location{Line: 1, Column: 1},
65-
Suggestion: "Use DROP " + objType + " IF EXISTS " + name,
66-
})
6781
}
6882
}
6983
return violations, nil

0 commit comments

Comments
 (0)