Close sql.Rows inline so sqlclosecheck can see it (refs #61)

The ten sqlclosecheck findings were not leaks: every one of these
queries already deferred a close through the package-local CloseRows
helper. sqlclosecheck only recognises a Close call on the rows value
in the function that produced it (directly deferred, or inside a
deferred closure), so a call that hands rows to a helper reads as
unhandled.

Rather than keep a helper the linter cannot see through, drop
CloseRows and defer a closure that calls rows.Close() directly at each
of the eighteen call sites, keeping the existing fatal-on-close-error
behaviour byte for byte. The close still runs exactly once, on
function exit, after the rows have been read.

Fatalf stays; it is still used by the transaction helpers.
This commit is contained in:
2026-08-09 01:48:44 +00:00
parent 047bd7f1c4
commit 7a37a66d88
9 changed files with 126 additions and 27 deletions

View File

@@ -57,7 +57,13 @@ func (r *BlobChunkRepository) GetByBlobID(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying blob chunks: %w", err) return nil, fmt.Errorf("querying blob chunks: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
var blobChunks []*BlobChunk var blobChunks []*BlobChunk

View File

@@ -82,7 +82,13 @@ func (r *BlobRepository) GetAll(ctx context.Context) (map[string]*Blob, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("querying blobs: %w", err) return nil, fmt.Errorf("querying blobs: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
out := make(map[string]*Blob) out := make(map[string]*Blob)

View File

@@ -60,7 +60,13 @@ func (r *ChunkFileRepository) GetByChunkHash(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying chunk files: %w", err) return nil, fmt.Errorf("querying chunk files: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
return r.scanChunkFiles(rows) return r.scanChunkFiles(rows)
} }
@@ -80,7 +86,13 @@ func (r *ChunkFileRepository) GetByFilePath(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying chunk files: %w", err) return nil, fmt.Errorf("querying chunk files: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
return r.scanChunkFiles(rows) return r.scanChunkFiles(rows)
} }
@@ -99,7 +111,13 @@ func (r *ChunkFileRepository) GetByFileID(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying chunk files: %w", err) return nil, fmt.Errorf("querying chunk files: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
return r.scanChunkFiles(rows) return r.scanChunkFiles(rows)
} }

View File

@@ -106,7 +106,13 @@ func (r *ChunkRepository) GetByHashes(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying chunks: %w", err) return nil, fmt.Errorf("querying chunks: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
var chunks []*Chunk var chunks []*Chunk
@@ -145,7 +151,13 @@ func (r *ChunkRepository) ListUnpacked(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying unpacked chunks: %w", err) return nil, fmt.Errorf("querying unpacked chunks: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
var chunks []*Chunk var chunks []*Chunk

View File

@@ -17,7 +17,13 @@ func (r *ChunkRepository) List(ctx context.Context) ([]*Chunk, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("querying chunks: %w", err) return nil, fmt.Errorf("querying chunks: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
var chunks []*Chunk var chunks []*Chunk

View File

@@ -1,7 +1,6 @@
package database package database
import ( import (
"database/sql"
"fmt" "fmt"
"os" "os"
) )
@@ -11,11 +10,3 @@ func Fatalf(format string, args ...any) {
fmt.Fprintf(os.Stderr, "FATAL: "+format+"\n", args...) fmt.Fprintf(os.Stderr, "FATAL: "+format+"\n", args...)
os.Exit(1) os.Exit(1)
} }
// CloseRows closes rows and exits on error
func CloseRows(rows *sql.Rows) {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}

View File

@@ -61,7 +61,13 @@ func (r *FileChunkRepository) GetByPath(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying file chunks: %w", err) return nil, fmt.Errorf("querying file chunks: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
return r.scanFileChunks(rows) return r.scanFileChunks(rows)
} }
@@ -81,7 +87,13 @@ func (r *FileChunkRepository) GetByFileID(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying file chunks: %w", err) return nil, fmt.Errorf("querying file chunks: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
return r.scanFileChunks(rows) return r.scanFileChunks(rows)
} }
@@ -104,7 +116,13 @@ func (r *FileChunkRepository) GetByPathTx(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying file chunks: %w", err) return nil, fmt.Errorf("querying file chunks: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
fileChunks, err := r.scanFileChunks(rows) fileChunks, err := r.scanFileChunks(rows)
LogSQL("GetByPathTx", "Complete", path, "count", len(fileChunks)) LogSQL("GetByPathTx", "Complete", path, "count", len(fileChunks))

View File

@@ -168,7 +168,13 @@ func (r *FileRepository) ListModifiedSince(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying files: %w", err) return nil, fmt.Errorf("querying files: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
var files []*File var files []*File
@@ -238,7 +244,13 @@ func (r *FileRepository) ListByPrefix(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying files: %w", err) return nil, fmt.Errorf("querying files: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
var files []*File var files []*File
@@ -266,7 +278,13 @@ func (r *FileRepository) ListAll(ctx context.Context) ([]*File, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("querying files: %w", err) return nil, fmt.Errorf("querying files: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
var files []*File var files []*File

View File

@@ -223,7 +223,13 @@ func (r *SnapshotRepository) ListRecent(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying snapshots: %w", err) return nil, fmt.Errorf("querying snapshots: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
return r.scanSnapshotRows(rows) return r.scanSnapshotRows(rows)
} }
@@ -437,7 +443,13 @@ func (r *SnapshotRepository) GetBlobHashes(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying blob hashes: %w", err) return nil, fmt.Errorf("querying blob hashes: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
var blobs []string var blobs []string
@@ -561,7 +573,13 @@ func (r *SnapshotRepository) GetIncompleteSnapshots(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying incomplete snapshots: %w", err) return nil, fmt.Errorf("querying incomplete snapshots: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
return r.scanSnapshotRows(rows) return r.scanSnapshotRows(rows)
} }
@@ -583,7 +601,13 @@ func (r *SnapshotRepository) GetIncompleteByHostname(
if err != nil { if err != nil {
return nil, fmt.Errorf("querying incomplete snapshots: %w", err) return nil, fmt.Errorf("querying incomplete snapshots: %w", err)
} }
defer CloseRows(rows)
defer func() {
err := rows.Close()
if err != nil {
Fatalf("failed to close rows: %v", err)
}
}()
var snapshots []*Snapshot var snapshots []*Snapshot