@@ -2071,14 +2071,21 @@ type Rows struct {
2071
2071
dc * driverConn // owned; must call releaseConn when closed to release
2072
2072
releaseConn func (error )
2073
2073
rowsi driver.Rows
2074
+ cancel func () // called when Rows is closed, may be nil.
2075
+ closeStmt * driverStmt // if non-nil, statement to Close on close
2074
2076
2075
- // closed value is 1 when the Rows is closed.
2076
- // Use atomic operations on value when checking value.
2077
- closed int32
2078
- cancel func () // called when Rows is closed, may be nil.
2079
- lastcols []driver.Value
2080
- lasterr error // non-nil only if closed is true
2081
- closeStmt * driverStmt // if non-nil, statement to Close on close
2077
+ // closemu prevents Rows from closing while there
2078
+ // is an active streaming result. It is held for read during non-close operations
2079
+ // and exclusively during close.
2080
+ //
2081
+ // closemu guards lasterr and closed.
2082
+ closemu sync.RWMutex
2083
+ closed bool
2084
+ lasterr error // non-nil only if closed is true
2085
+
2086
+ // lastcols is only used in Scan, Next, and NextResultSet which are expected
2087
+ // not not be called concurrently.
2088
+ lastcols []driver.Value
2082
2089
}
2083
2090
2084
2091
func (rs * Rows ) initContextClose (ctx context.Context ) {
@@ -2089,7 +2096,7 @@ func (rs *Rows) initContextClose(ctx context.Context) {
2089
2096
// awaitDone blocks until the rows are closed or the context canceled.
2090
2097
func (rs * Rows ) awaitDone (ctx context.Context ) {
2091
2098
<- ctx .Done ()
2092
- rs .Close ( )
2099
+ rs .close ( ctx . Err () )
2093
2100
}
2094
2101
2095
2102
// Next prepares the next result row for reading with the Scan method. It
@@ -2099,8 +2106,19 @@ func (rs *Rows) awaitDone(ctx context.Context) {
2099
2106
//
2100
2107
// Every call to Scan, even the first one, must be preceded by a call to Next.
2101
2108
func (rs * Rows ) Next () bool {
2102
- if rs .isClosed () {
2103
- return false
2109
+ var doClose , ok bool
2110
+ withLock (rs .closemu .RLocker (), func () {
2111
+ doClose , ok = rs .nextLocked ()
2112
+ })
2113
+ if doClose {
2114
+ rs .Close ()
2115
+ }
2116
+ return ok
2117
+ }
2118
+
2119
+ func (rs * Rows ) nextLocked () (doClose , ok bool ) {
2120
+ if rs .closed {
2121
+ return false , false
2104
2122
}
2105
2123
if rs .lastcols == nil {
2106
2124
rs .lastcols = make ([]driver.Value , len (rs .rowsi .Columns ()))
@@ -2109,23 +2127,21 @@ func (rs *Rows) Next() bool {
2109
2127
if rs .lasterr != nil {
2110
2128
// Close the connection if there is a driver error.
2111
2129
if rs .lasterr != io .EOF {
2112
- rs .Close ()
2113
- return false
2130
+ return true , false
2114
2131
}
2115
2132
nextResultSet , ok := rs .rowsi .(driver.RowsNextResultSet )
2116
2133
if ! ok {
2117
- rs .Close ()
2118
- return false
2134
+ return true , false
2119
2135
}
2120
2136
// The driver is at the end of the current result set.
2121
2137
// Test to see if there is another result set after the current one.
2122
2138
// Only close Rows if there is no further result sets to read.
2123
2139
if ! nextResultSet .HasNextResultSet () {
2124
- rs . Close ()
2140
+ doClose = true
2125
2141
}
2126
- return false
2142
+ return doClose , false
2127
2143
}
2128
- return true
2144
+ return false , true
2129
2145
}
2130
2146
2131
2147
// NextResultSet prepares the next result set for reading. It returns true if
@@ -2137,18 +2153,28 @@ func (rs *Rows) Next() bool {
2137
2153
// scanning. If there are further result sets they may not have rows in the result
2138
2154
// set.
2139
2155
func (rs * Rows ) NextResultSet () bool {
2140
- if rs .isClosed () {
2156
+ var doClose bool
2157
+ defer func () {
2158
+ if doClose {
2159
+ rs .Close ()
2160
+ }
2161
+ }()
2162
+ rs .closemu .RLock ()
2163
+ defer rs .closemu .RUnlock ()
2164
+
2165
+ if rs .closed {
2141
2166
return false
2142
2167
}
2168
+
2143
2169
rs .lastcols = nil
2144
2170
nextResultSet , ok := rs .rowsi .(driver.RowsNextResultSet )
2145
2171
if ! ok {
2146
- rs . Close ()
2172
+ doClose = true
2147
2173
return false
2148
2174
}
2149
2175
rs .lasterr = nextResultSet .NextResultSet ()
2150
2176
if rs .lasterr != nil {
2151
- rs . Close ()
2177
+ doClose = true
2152
2178
return false
2153
2179
}
2154
2180
return true
@@ -2157,6 +2183,8 @@ func (rs *Rows) NextResultSet() bool {
2157
2183
// Err returns the error, if any, that was encountered during iteration.
2158
2184
// Err may be called after an explicit or implicit Close.
2159
2185
func (rs * Rows ) Err () error {
2186
+ rs .closemu .RLock ()
2187
+ defer rs .closemu .RUnlock ()
2160
2188
if rs .lasterr == io .EOF {
2161
2189
return nil
2162
2190
}
@@ -2167,7 +2195,9 @@ func (rs *Rows) Err() error {
2167
2195
// Columns returns an error if the rows are closed, or if the rows
2168
2196
// are from QueryRow and there was a deferred error.
2169
2197
func (rs * Rows ) Columns () ([]string , error ) {
2170
- if rs .isClosed () {
2198
+ rs .closemu .RLock ()
2199
+ defer rs .closemu .RUnlock ()
2200
+ if rs .closed {
2171
2201
return nil , errors .New ("sql: Rows are closed" )
2172
2202
}
2173
2203
if rs .rowsi == nil {
@@ -2179,7 +2209,9 @@ func (rs *Rows) Columns() ([]string, error) {
2179
2209
// ColumnTypes returns column information such as column type, length,
2180
2210
// and nullable. Some information may not be available from some drivers.
2181
2211
func (rs * Rows ) ColumnTypes () ([]* ColumnType , error ) {
2182
- if rs .isClosed () {
2212
+ rs .closemu .RLock ()
2213
+ defer rs .closemu .RUnlock ()
2214
+ if rs .closed {
2183
2215
return nil , errors .New ("sql: Rows are closed" )
2184
2216
}
2185
2217
if rs .rowsi == nil {
@@ -2329,9 +2361,13 @@ func rowsColumnInfoSetup(rowsi driver.Rows) []*ColumnType {
2329
2361
// For scanning into *bool, the source may be true, false, 1, 0, or
2330
2362
// string inputs parseable by strconv.ParseBool.
2331
2363
func (rs * Rows ) Scan (dest ... interface {}) error {
2332
- if rs .isClosed () {
2364
+ rs .closemu .RLock ()
2365
+ if rs .closed {
2366
+ rs .closemu .RUnlock ()
2333
2367
return errors .New ("sql: Rows are closed" )
2334
2368
}
2369
+ rs .closemu .RUnlock ()
2370
+
2335
2371
if rs .lastcols == nil {
2336
2372
return errors .New ("sql: Scan called without calling Next" )
2337
2373
}
@@ -2351,20 +2387,28 @@ func (rs *Rows) Scan(dest ...interface{}) error {
2351
2387
// hook through a test only mutex.
2352
2388
var rowsCloseHook = func () func (* Rows , * error ) { return nil }
2353
2389
2354
- func (rs * Rows ) isClosed () bool {
2355
- return atomic .LoadInt32 (& rs .closed ) != 0
2356
- }
2357
-
2358
2390
// Close closes the Rows, preventing further enumeration. If Next is called
2359
2391
// and returns false and there are no further result sets,
2360
2392
// the Rows are closed automatically and it will suffice to check the
2361
2393
// result of Err. Close is idempotent and does not affect the result of Err.
2362
2394
func (rs * Rows ) Close () error {
2363
- if ! atomic .CompareAndSwapInt32 (& rs .closed , 0 , 1 ) {
2395
+ return rs .close (nil )
2396
+ }
2397
+
2398
+ func (rs * Rows ) close (err error ) error {
2399
+ rs .closemu .Lock ()
2400
+ defer rs .closemu .Unlock ()
2401
+
2402
+ if rs .closed {
2364
2403
return nil
2365
2404
}
2405
+ rs .closed = true
2406
+
2407
+ if rs .lasterr == nil {
2408
+ rs .lasterr = err
2409
+ }
2366
2410
2367
- err : = rs .rowsi .Close ()
2411
+ err = rs .rowsi .Close ()
2368
2412
if fn := rowsCloseHook (); fn != nil {
2369
2413
fn (rs , & err )
2370
2414
}
0 commit comments