6
6
7
7
package dbutil
8
8
9
- import "errors"
9
+ import (
10
+ "errors"
11
+ "fmt"
12
+ "runtime"
13
+
14
+ "go.mau.fi/util/exzerolog"
15
+ )
10
16
11
17
var ErrAlreadyIterated = errors .New ("this iterator has been already iterated" )
12
18
@@ -46,17 +52,31 @@ type rowIterImpl[T any] struct {
46
52
Rows
47
53
ConvertRow ConvertRowFn [T ]
48
54
49
- err error
55
+ iterated bool
56
+ caller string
57
+ err error
50
58
}
51
59
52
60
// NewRowIter creates a new RowIter from the given Rows and scanner function.
53
61
func NewRowIter [T any ](rows Rows , convertFn ConvertRowFn [T ]) RowIter [T ] {
54
- return & rowIterImpl [ T ]{ Rows : rows , ConvertRow : convertFn }
62
+ return newRowIterWithError ( rows , convertFn , nil )
55
63
}
56
64
57
65
// NewRowIterWithError creates a new RowIter from the given Rows and scanner function with default error. If not nil, it will be returned without calling iterator function.
58
66
func NewRowIterWithError [T any ](rows Rows , convertFn ConvertRowFn [T ], err error ) RowIter [T ] {
59
- return & rowIterImpl [T ]{Rows : rows , ConvertRow : convertFn , err : err }
67
+ return newRowIterWithError (rows , convertFn , err )
68
+ }
69
+
70
+ func newRowIterWithError [T any ](rows Rows , convertFn ConvertRowFn [T ], err error ) RowIter [T ] {
71
+ ri := & rowIterImpl [T ]{Rows : rows , ConvertRow : convertFn , err : err }
72
+ if err == nil {
73
+ callerSkip := 2
74
+ if pc , file , line , ok := runtime .Caller (callerSkip ); ok {
75
+ ri .caller = exzerolog .CallerWithFunctionName (pc , file , line )
76
+ }
77
+ runtime .SetFinalizer (ri , (* rowIterImpl [T ]).destroy )
78
+ }
79
+ return ri
60
80
}
61
81
62
82
func ScanSingleColumn [T any ](rows Scannable ) (val T , err error ) {
@@ -74,13 +94,22 @@ func ScanDataStruct[T NewableDataStruct[T]](rows Scannable) (T, error) {
74
94
return val .New ().Scan (rows )
75
95
}
76
96
97
+ func (i * rowIterImpl [T ]) destroy () {
98
+ if ! i .iterated {
99
+ panic (fmt .Errorf ("RowIter created at %s wasn't iterated" , i .caller ))
100
+ }
101
+ }
102
+
77
103
func (i * rowIterImpl [T ]) Iter (fn func (T ) (bool , error )) error {
78
104
if i == nil {
79
105
return nil
80
106
} else if i .Rows == nil || i .err != nil {
81
107
return i .err
82
108
}
83
- defer i .Rows .Close ()
109
+ defer func () {
110
+ _ = i .Rows .Close ()
111
+ i .iterated = true
112
+ }()
84
113
85
114
for i .Rows .Next () {
86
115
if item , err := i .ConvertRow (i .Rows ); err != nil {
0 commit comments