Skip to content

Commit f6bc14a

Browse files
authored
Add support for sqlx NamedStmt (#26)
1 parent 1116461 commit f6bc14a

File tree

9 files changed

+46
-23
lines changed

9 files changed

+46
-23
lines changed

pgx_examples_results.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# github.com/ryanrolds/sqlclosecheck/testdata/pgx_examples
2-
testdata/pgx_examples/missing_close.go:8:26: Rows/Stmt was not closed
3-
testdata/pgx_examples/missing_close.go:17:28: Rows/Stmt was not closed
4-
testdata/pgx_examples/missing_close.go:26:28: Rows/Stmt was not closed
2+
testdata/pgx_examples/missing_close.go:8:26: Rows/Stmt/NamedStmt was not closed
3+
testdata/pgx_examples/missing_close.go:17:28: Rows/Stmt/NamedStmt was not closed
4+
testdata/pgx_examples/missing_close.go:26:28: Rows/Stmt/NamedStmt was not closed

pkg/analyzer/analyzer.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import (
99
)
1010

1111
const (
12-
rowsName = "Rows"
13-
stmtName = "Stmt"
14-
closeMethod = "Close"
12+
rowsName = "Rows"
13+
stmtName = "Stmt"
14+
namedStmtName = "NamedStmt"
15+
closeMethod = "Close"
1516
)
1617

1718
type action uint8
@@ -39,7 +40,7 @@ var (
3940
func NewAnalyzer() *analysis.Analyzer {
4041
return &analysis.Analyzer{
4142
Name: "sqlclosecheck",
42-
Doc: "Checks that sql.Rows, sql.Stmt, pgx.Query are closed.",
43+
Doc: "Checks that sql.Rows, sql.Stmt, sqlx.NamedStmt, pgx.Query are closed.",
4344
Run: run,
4445
Requires: []*analysis.Analyzer{
4546
buildssa.Analyzer,
@@ -76,7 +77,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
7677
refs := (*targetValue.value).Referrers()
7778
isClosed := checkClosed(refs, targetTypes)
7879
if !isClosed {
79-
pass.Reportf((targetValue.instr).Pos(), "Rows/Stmt was not closed")
80+
pass.Reportf((targetValue.instr).Pos(), "Rows/Stmt/NamedStmt was not closed")
8081
}
8182

8283
checkDeferred(pass, refs, targetTypes, false)
@@ -112,6 +113,11 @@ func getTargetTypes(pssa *buildssa.SSA, targetPackages []string) []any {
112113
if stmtType != nil {
113114
targets = append(targets, stmtType)
114115
}
116+
117+
namedStmtType := getTypePointerFromName(pkg, namedStmtName)
118+
if namedStmtType != nil {
119+
targets = append(targets, namedStmtType)
120+
}
115121
}
116122

117123
return targets
@@ -120,7 +126,7 @@ func getTargetTypes(pssa *buildssa.SSA, targetPackages []string) []any {
120126
func getTypePointerFromName(pkg *ssa.Package, name string) *types.Pointer {
121127
pkgType := pkg.Type(name)
122128
if pkgType == nil {
123-
// this package does not use Rows/Stmt
129+
// this package does not use Rows/Stmt/NamedStmt
124130
return nil
125131
}
126132

pkg/analyzer/testdata/pgx/missing_close.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
)
66

77
func missingCloseTx() {
8-
rows, err := pgxTx.Query(ctx, "SELECT username FROM users") // want "Rows/Stmt was not closed"
8+
rows, err := pgxTx.Query(ctx, "SELECT username FROM users") // want "Rows/Stmt/NamedStmt was not closed"
99
if err != nil {
1010
log.Fatal(err)
1111
}
@@ -14,7 +14,7 @@ func missingCloseTx() {
1414
}
1515

1616
func missingCloseConn() {
17-
rows, err := pgxConn.Query(ctx, "SELECT username FROM users") // want "Rows/Stmt was not closed"
17+
rows, err := pgxConn.Query(ctx, "SELECT username FROM users") // want "Rows/Stmt/NamedStmt was not closed"
1818
if err != nil {
1919
log.Fatal(err)
2020
}
@@ -23,7 +23,7 @@ func missingCloseConn() {
2323
}
2424

2525
func missingClosePgxPool() {
26-
rows, err := pgxPool.Query(ctx, "SELECT username FROM users") // want "Rows/Stmt was not closed"
26+
rows, err := pgxPool.Query(ctx, "SELECT username FROM users") // want "Rows/Stmt/NamedStmt was not closed"
2727
if err != nil {
2828
log.Fatal(err)
2929
}

pkg/analyzer/testdata/rows/missing_close.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77

88
func missingClose() {
99
age := 27
10-
rows, err := db.QueryContext(ctx, "SELECT name FROM users WHERE age=?", age) // want "Rows/Stmt was not closed"
10+
rows, err := db.QueryContext(ctx, "SELECT name FROM users WHERE age=?", age) // want "Rows/Stmt/NamedStmt was not closed"
1111
if err != nil {
1212
log.Fatal(err)
1313
}

pkg/analyzer/testdata/stmt/missing_close.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77

88
func missingClose() {
99
// In normal use, create one Stmt when your process starts.
10-
stmt, err := db.PrepareContext(ctx, "SELECT username FROM users WHERE id = ?") // want "Rows/Stmt was not closed"
10+
stmt, err := db.PrepareContext(ctx, "SELECT username FROM users WHERE id = ?") // want "Rows/Stmt/NamedStmt was not closed"
1111
if err != nil {
1212
log.Fatal(err)
1313
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# github.com/ryanrolds/sqlclosecheck/testdata/pgx_examples
2-
testdata/pgx_examples/missing_close.go:8:26: Rows/Stmt was not closed
3-
testdata/pgx_examples/missing_close.go:17:28: Rows/Stmt was not closed
4-
testdata/pgx_examples/missing_close.go:26:28: Rows/Stmt was not closed
2+
testdata/pgx_examples/missing_close.go:8:26: Rows/Stmt/NamedStmt was not closed
3+
testdata/pgx_examples/missing_close.go:17:28: Rows/Stmt/NamedStmt was not closed
4+
testdata/pgx_examples/missing_close.go:26:28: Rows/Stmt/NamedStmt was not closed

testdata/pgx_examples/missing_close.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
)
66

77
func missingCloseTx() {
8-
rows, err := pgxTx.Query(ctx, "SELECT username FROM users") // want "Rows/Stmt was not closed"
8+
rows, err := pgxTx.Query(ctx, "SELECT username FROM users") // want "Rows/Stmt/NamedStmt was not closed"
99
if err != nil {
1010
log.Fatal(err)
1111
}
@@ -14,7 +14,7 @@ func missingCloseTx() {
1414
}
1515

1616
func missingCloseConn() {
17-
rows, err := pgxConn.Query(ctx, "SELECT username FROM users") // want "Rows/Stmt was not closed"
17+
rows, err := pgxConn.Query(ctx, "SELECT username FROM users") // want "Rows/Stmt/NamedStmt was not closed"
1818
if err != nil {
1919
log.Fatal(err)
2020
}
@@ -23,7 +23,7 @@ func missingCloseConn() {
2323
}
2424

2525
func missingClosePgxPool() {
26-
rows, err := pgxPool.Query(ctx, "SELECT username FROM users") // want "Rows/Stmt was not closed"
26+
rows, err := pgxPool.Query(ctx, "SELECT username FROM users") // want "Rows/Stmt/NamedStmt was not closed"
2727
if err != nil {
2828
log.Fatal(err)
2929
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# github.com/ryanrolds/sqlclosecheck/testdata/sqlx_examples
2-
testdata/sqlx_examples/failure_generics.go:6:21: Rows/Stmt was not closed
3-
testdata/sqlx_examples/failure_generics.go:13:21: Rows/Stmt was not closed
4-
testdata/sqlx_examples/missing_close.go:10:24: Rows/Stmt was not closed
2+
testdata/sqlx_examples/failure_generics.go:6:21: Rows/Stmt/NamedStmt was not closed
3+
testdata/sqlx_examples/failure_generics.go:13:21: Rows/Stmt/NamedStmt was not closed
4+
testdata/sqlx_examples/missing_close.go:10:24: Rows/Stmt/NamedStmt was not closed
5+
testdata/sqlx_examples/missing_close_named_stmt.go:8:30: Rows/Stmt/NamedStmt was not closed
56
testdata/sqlx_examples/non_defer_close.go:30:12: Close should use defer
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package sqlx_examples
2+
3+
import (
4+
"log"
5+
)
6+
7+
func missingCloseNamedStmt() {
8+
stmt, err := db.PrepareNamed("SELECT * FROM users WHERE id = :id")
9+
if err != nil {
10+
log.Fatal(err)
11+
}
12+
13+
// defer stmt.Close()
14+
15+
_ = stmt // No need to use stmt
16+
}

0 commit comments

Comments
 (0)