Skip to content

Commit 1f1b54f

Browse files
authored
fix(shortcuts): fix premature rows closure in QueryIterContext (#545)
The previous implementation called defer rows.Close() before returning the iterator, which caused rows to be closed immediately when the function returned, making the iterator unusable. Now the iterator wraps the close logic, ensuring rows are closed only after iteration completes or stops.
1 parent 24db333 commit 1f1b54f

File tree

1 file changed

+15
-2
lines changed

1 file changed

+15
-2
lines changed

shortcuts.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ func QueryList2Context[T any](ctx context.Context, statement, param any) (result
7979
// QueryIterContext executes a query and returns an iterator over T.
8080
// Rows are automatically closed when iteration completes or stops.
8181
// (ctx must contain a Manager via ManagerFromContext)
82+
//
83+
// IMPORTANT: The returned iterator MUST be iterated over (even partially),
84+
// otherwise the underlying database rows will not be closed, leading to resource leaks.
8285
func QueryIterContext[T any](ctx context.Context, statement, param any) (sqllib.Iterator[T], error) {
8386
manager, err := ManagerFromContext(ctx)
8487
if err != nil {
@@ -88,6 +91,16 @@ func QueryIterContext[T any](ctx context.Context, statement, param any) (sqllib.
8891
if err != nil {
8992
return nil, err
9093
}
91-
defer func() { _ = rows.Close() }()
92-
return sqllib.Iter[T](rows)
94+
95+
iterator, err := sqllib.Iter[T](rows)
96+
if err != nil {
97+
_ = rows.Close()
98+
return nil, err
99+
}
100+
101+
// Wrap the iterator to ensure rows are closed after iteration
102+
return func(yield func(T, error) bool) {
103+
defer func() { _ = rows.Close() }()
104+
iterator(yield)
105+
}, nil
93106
}

0 commit comments

Comments
 (0)