github.com/go-kivik/kivik/v4@v4.3.2/resultset.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 // use this file except in compliance with the License. You may obtain a copy of 3 // the License at 4 // 5 // http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 // License for the specific language governing permissions and limitations under 11 // the License. 12 13 package kivik 14 15 import ( 16 "context" 17 "encoding/json" 18 "errors" 19 "io" 20 "net/http" 21 "reflect" 22 "sync" 23 24 "github.com/go-kivik/kivik/v4/driver" 25 internal "github.com/go-kivik/kivik/v4/int/errors" 26 ) 27 28 // ResultMetadata contains metadata about certain queries. 29 type ResultMetadata struct { 30 // Offset is the starting offset where the result set started. 31 Offset int64 32 33 // TotalRows is the total number of rows in the view which would have been 34 // returned if no limiting were used. 35 TotalRows int64 36 37 // UpdateSeq is the sequence id of the underlying database the view 38 // reflects, if requested in the query. 39 UpdateSeq string 40 41 // Warning is a warning generated by the query, if any. 42 Warning string 43 44 // Bookmark is the paging bookmark, if one was provided with the result 45 // set. This is intended for use with the Mango /_find interface, with 46 // CouchDB 2.1.1 and later. Consult the [CouchDB documentation] for 47 // detailed usage instructions. 48 // 49 // [CouchDB documentation]: http://docs.couchdb.org/en/2.1.1/api/database/find.html#pagination 50 Bookmark string 51 } 52 53 // ResultSet is an iterator over a multi-value query result set. 54 // 55 // Call [ResultSet.Next] to advance the iterator to the next item in the result 56 // set. 57 // 58 // The Scan* methods are expected to be called only once per iteration, as 59 // they may consume data from the network, rendering them unusable a second 60 // time. 61 type ResultSet struct { 62 *iter 63 rowsi driver.Rows 64 } 65 66 func newResultSet(ctx context.Context, onClose func(), rowsi driver.Rows) *ResultSet { 67 return &ResultSet{ 68 iter: newIterator(ctx, onClose, &rowsIterator{Rows: rowsi}, &driver.Row{}), 69 rowsi: rowsi, 70 } 71 } 72 73 // Next prepares the next result value for reading. It returns true on success 74 // or false if there are no more results or an error occurs while preparing it. 75 // [ResultSet.Err] should be consulted to distinguish between the two. 76 // 77 // When Next returns false, and there are no more results/result sets to be 78 // read, the [ResultSet.Close] is called implicitly, negating the need to call 79 // it explicitly. 80 func (r *ResultSet) Next() bool { 81 r.iter.mu.Lock() 82 defer r.iter.mu.Unlock() 83 if r.err != nil { 84 return false 85 } 86 if atts := r.curVal.(*driver.Row).Attachments; atts != nil { 87 _ = atts.Close() 88 } 89 90 return r.iter.next() 91 } 92 93 // NextResultSet prepares the next result set for reading. It returns false if 94 // there is no further result set or if there is an error advancing to it. 95 // [ResultSet.Err] should be consulted to distinguish between the two cases. 96 // 97 // After calling NextResultSet, [ResultSet.Next] must be called to advance to 98 // the first result in the resultset before scanning. 99 func (r *ResultSet) NextResultSet() bool { 100 r.mu.Lock() 101 defer r.mu.Unlock() 102 if r.err != nil { 103 return false 104 } 105 if r.state == stateClosed { 106 return false 107 } 108 if r.state == stateRowReady { 109 r.err = errors.New("must call NextResultSet before Next") 110 return false 111 } 112 r.state = stateResultSetReady 113 return true 114 } 115 116 // Err returns the error, if any, that was encountered during iteration. 117 // [ResultSet.Err] may be called after an explicit or implicit call to 118 // [ResultSet.Close]. 119 func (r *ResultSet) Err() error { 120 return r.iter.Err() 121 } 122 123 // Close closes the result set, preventing further iteration, and freeing 124 // any resources (such as the HTTP request body) of the underlying query. 125 // Close is idempotent and does not affect the result of 126 // [ResultSet.Err]. Close is safe to call concurrently with other ResultSet 127 // operations and will block until all other ResultSet operations finish. 128 func (r *ResultSet) Close() error { 129 return r.iter.Close() 130 } 131 132 // Metadata returns the result metadata for the current query. It must be 133 // called after [ResultSet.Next] returns false or [ResultSetIterator] has been 134 // completely and successfully iterated. Otherwise it will return an error. 135 func (r *ResultSet) Metadata() (*ResultMetadata, error) { 136 for r.iter == nil || (r.state != stateEOQ && r.state != stateClosed) { 137 return nil, &internal.Error{Status: http.StatusBadRequest, Err: errors.New("Metadata must not be called until result set iteration is complete")} 138 } 139 return r.feed.(*rowsIterator).ResultMetadata, nil 140 } 141 142 // ScanValue copies the data from the result value into dest, which must be a 143 // pointer. This acts as a wrapper around [encoding/json.Unmarshal]. 144 // 145 // If the row returned an error, it will be returned rather than unmarshaling 146 // the value, as error rows do not include values. 147 // 148 // Refer to the documentation for [encoding/json.Unmarshal] for unmarshaling 149 // details. 150 func (r *ResultSet) ScanValue(dest interface{}) error { 151 runlock, err := r.makeReady() 152 if err != nil { 153 return err 154 } 155 defer runlock() 156 row := r.curVal.(*driver.Row) 157 if row.Error != nil { 158 return row.Error 159 } 160 if row.Value != nil { 161 return json.NewDecoder(row.Value).Decode(dest) 162 } 163 return nil 164 } 165 166 // ScanDoc works the same as [ResultSet.ScanValue], but on the doc field of 167 // the result. It will return an error if the query does not include 168 // documents. 169 // 170 // If the row returned an error, it will be returned rather than 171 // unmarshaling the doc, as error rows do not include docs. 172 func (r *ResultSet) ScanDoc(dest interface{}) error { 173 runlock, err := r.makeReady() 174 if err != nil { 175 return err 176 } 177 defer runlock() 178 row := r.curVal.(*driver.Row) 179 if err := row.Error; err != nil { 180 return err 181 } 182 if row.Doc != nil { 183 return json.NewDecoder(row.Doc).Decode(dest) 184 } 185 return &internal.Error{Status: http.StatusBadRequest, Message: "kivik: doc is nil; does the query include docs?"} 186 } 187 188 // ScanKey works the same as [ResultSet.ScanValue], but on the key field of the 189 // result. For simple keys, which are just strings, [ResultSet.Key] may be 190 // easier to use. 191 // 192 // Unlike [ResultSet.ScanValue] and [ResultSet.ScanDoc], this may successfully 193 // scan the key, and also return an error, if the row itself represents an error. 194 func (r *ResultSet) ScanKey(dest interface{}) error { 195 runlock, err := r.makeReady() 196 if err != nil { 197 return err 198 } 199 defer runlock() 200 row := r.curVal.(*driver.Row) 201 if err := json.Unmarshal(row.Key, dest); err != nil { 202 return err 203 } 204 return row.Error 205 } 206 207 // ID returns the ID of the most recent result. 208 func (r *ResultSet) ID() (string, error) { 209 runlock, err := r.makeReady() 210 if err != nil { 211 return "", err 212 } 213 defer runlock() 214 row := r.curVal.(*driver.Row) 215 return row.ID, row.Error 216 } 217 218 // Rev returns the document revision, when known. Not all result sets (such 219 // as those from views) include revision IDs, so this will return an empty 220 // string in such cases. 221 func (r *ResultSet) Rev() (string, error) { 222 runlock, err := r.makeReady() 223 if err != nil { 224 return "", err 225 } 226 defer runlock() 227 row := r.curVal.(*driver.Row) 228 return row.Rev, row.Error 229 } 230 231 // Key returns the Key of the most recent result as a raw JSON string. For 232 // compound keys, [ResultSet.ScanKey] may be more convenient. 233 func (r *ResultSet) Key() (string, error) { 234 runlock, err := r.makeReady() 235 if err != nil { 236 return "", err 237 } 238 defer runlock() 239 row := r.curVal.(*driver.Row) 240 return string(row.Key), row.Error 241 } 242 243 // Attachments returns an attachments iterator if the document includes 244 // attachments. 245 func (r *ResultSet) Attachments() (*AttachmentsIterator, error) { 246 runlock, err := r.makeReady() 247 if err != nil { 248 return nil, err 249 } 250 row := r.curVal.(*driver.Row) 251 if row.Error != nil { 252 runlock() 253 return nil, row.Error 254 } 255 if row.Attachments == nil { 256 runlock() 257 return nil, errNoAttachments 258 } 259 return &AttachmentsIterator{ 260 onClose: runlock, 261 atti: row.Attachments, 262 }, nil 263 } 264 265 // makeReady ensures that the iterator is ready to be read from. If i.err is 266 // set, it is returned. 267 func (r *ResultSet) makeReady() (unlock func(), err error) { 268 r.mu.Lock() 269 if r.err != nil { 270 r.mu.Unlock() 271 return nil, r.err 272 } 273 if r.state == stateClosed { 274 r.mu.Unlock() 275 return nil, &internal.Error{Status: http.StatusBadRequest, Message: "kivik: Iterator is closed"} 276 } 277 if !stateIsReady(r.state) { 278 r.mu.Unlock() 279 return nil, &internal.Error{Status: http.StatusBadRequest, Message: "kivik: Iterator access before calling Next"} 280 } 281 var once sync.Once 282 r.wg.Add(1) 283 return func() { 284 once.Do(func() { 285 r.wg.Done() 286 r.mu.Unlock() 287 }) 288 }, nil 289 } 290 291 type rowsIterator struct { 292 driver.Rows 293 *ResultMetadata 294 } 295 296 var _ iterator = &rowsIterator{} 297 298 func (r *rowsIterator) Next(i interface{}) error { 299 row := i.(*driver.Row) 300 row.ID = "" 301 row.Rev = "" 302 row.Key = row.Key[:0] 303 row.Value = nil 304 row.Doc = nil 305 row.Attachments = nil 306 row.Error = nil 307 err := r.Rows.Next(row) 308 if err == io.EOF || err == driver.EOQ { 309 var warning, bookmark string 310 if w, ok := r.Rows.(driver.RowsWarner); ok { 311 warning = w.Warning() 312 } 313 if b, ok := r.Rows.(driver.Bookmarker); ok { 314 bookmark = b.Bookmark() 315 } 316 r.ResultMetadata = &ResultMetadata{ 317 Offset: r.Rows.Offset(), 318 TotalRows: r.Rows.TotalRows(), 319 UpdateSeq: r.Rows.UpdateSeq(), 320 Warning: warning, 321 Bookmark: bookmark, 322 } 323 } 324 return err 325 } 326 327 // ScanAllDocs loops through the remaining documents in the resultset, and scans 328 // them into dest which must be a pointer to a slice or an array. Passing any 329 // other type will result in an error. If dest is an array, scanning will stop 330 // once the array is filled. The iterator is closed by this method. It is 331 // possible that an error will be returned, and that one or more documents were 332 // successfully scanned. 333 func ScanAllDocs(r *ResultSet, dest interface{}) error { 334 return scanAll(r, dest, r.ScanDoc) 335 } 336 337 // ScanAllValues works like [ScanAllDocs], but scans the values rather than docs. 338 func ScanAllValues(r *ResultSet, dest interface{}) error { 339 return scanAll(r, dest, r.ScanValue) 340 } 341 342 func scanAll(r *ResultSet, dest interface{}, scan func(interface{}) error) (err error) { 343 defer func() { 344 closeErr := r.Close() 345 if err == nil { 346 err = closeErr 347 } 348 }() 349 if err := r.Err(); err != nil { 350 return err 351 } 352 353 value := reflect.ValueOf(dest) 354 if value.Kind() != reflect.Ptr { 355 return errors.New("must pass a pointer to ScanAllDocs") 356 } 357 if value.IsNil() { 358 return errors.New("nil pointer passed to ScanAllDocs") 359 } 360 361 direct := reflect.Indirect(value) 362 var limit int 363 364 switch direct.Kind() { 365 case reflect.Array: 366 limit = direct.Len() 367 if limit == 0 { 368 return errors.New("0-length array passed to ScanAllDocs") 369 } 370 case reflect.Slice: 371 default: 372 return errors.New("dest must be a pointer to a slice or array") 373 } 374 375 base := value.Type() 376 if base.Kind() == reflect.Ptr { 377 base = base.Elem() 378 } 379 base = base.Elem() 380 381 for i := 0; r.Next(); i++ { 382 if limit > 0 && i >= limit { 383 return nil 384 } 385 vp := reflect.New(base) 386 err = scan(vp.Interface()) 387 if limit > 0 { // means this is an array 388 direct.Index(i).Set(reflect.Indirect(vp)) 389 } else { 390 direct.Set(reflect.Append(direct, reflect.Indirect(vp))) 391 } 392 } 393 return nil 394 } 395 396 // Row represents a single row in a result set, as returned by 397 // [ResultSet.Iterator] and [ResultSet.NextIterator]. 398 // 399 // !!NOTICE!! This struct is considered experimental, and may change without 400 // notice. 401 type Row struct { 402 dRow *driver.Row 403 } 404 405 // ID returns the document ID of the row, if present in the result. 406 func (r *Row) ID() (string, error) { 407 return r.dRow.ID, r.dRow.Error 408 } 409 410 // Rev returns the view key of the row, if present in the result. 411 func (r *Row) Rev() (string, error) { 412 return r.dRow.Rev, r.dRow.Error 413 } 414 415 // Key returns the raw, JSON-encoded key of the row, if present in the result. 416 func (r *Row) Key() (json.RawMessage, error) { 417 return r.dRow.Key, r.dRow.Error 418 } 419 420 // ScanValue copies the data from the result value into dest, which must be a 421 // pointer. This acts as a wrapper around [encoding/json.Unmarshal]. 422 // 423 // Refer to the documentation for [encoding/json.Unmarshal] for unmarshaling 424 // details. 425 // 426 // If the row returned an error, it will be returned rather than 427 // unmarshaling the doc, as error rows do not include values. 428 func (r *Row) ScanValue(dest interface{}) error { 429 if err := r.dRow.Error; err != nil { 430 return err 431 } 432 return json.NewDecoder(r.dRow.Value).Decode(dest) 433 } 434 435 // ScanKey works the same as [Row.ScanValue], but on the key field of the 436 // result. For simple keys, which are just strings, [Row.Key] may be easier to 437 // use. 438 // 439 // Unlike [Row.ScanValue] and [Row.ScanDoc], this may successfully scan the key, 440 // and also return an error, if the row itself represents an error. 441 func (r *Row) ScanKey(dest interface{}) error { 442 if err := r.dRow.Error; err != nil { 443 return err 444 } 445 return json.Unmarshal(r.dRow.Key, dest) 446 } 447 448 // ScanDoc works the same as [Row.ScanValue], but on the doc field of the 449 // result. It will return an error if the query does not include documents. 450 // 451 // If the row returned an error, it will be returned rather than 452 // unmarshaling the doc, as error rows do not include docs. 453 func (r *Row) ScanDoc(dest interface{}) error { 454 if err := r.dRow.Error; err != nil { 455 return err 456 } 457 return json.NewDecoder(r.dRow.Doc).Decode(dest) 458 } 459 460 // Iterator returns a function that can be used to iterate over all rows in the 461 // result set(s). This function is the analogue to [ResultSet.Next] for Go 462 // 1.23's new range functions. When the iterator returns an error, it means that 463 // iteration has failed. For row-specific errors, check the value of [Row.Err]. 464 // 465 // If your ResultSet contains multiple result sets, this iterator will iterate 466 // over all of them, without distinction. If you need to iterate over each 467 // result set individually, use [ResultSet.NextIterator]. 468 // 469 // !!NOTICE!! This function is considered experimental, and may change without 470 // notice. 471 func (r *ResultSet) Iterator() func(yield func(*Row, error) bool) { 472 return func(yield func(*Row, error) bool) { 473 for r.Next() { 474 row := r.iter.curVal.(*driver.Row) 475 if !yield(&Row{dRow: row}, nil) { 476 _ = r.Close() 477 break 478 } 479 } 480 if err := r.Err(); err != nil { 481 yield(nil, err) 482 } 483 } 484 } 485 486 // NextIterator returns a function that can be used to iterate over the 487 // multiple result sets in a [ResultSet]. This function is the analogue to 488 // [ResultSet.NextResultSet] for Go 1.23's. For queries that return multiple 489 // resultsets, you may call this function once for each result set. 490 // 491 // !!NOTICE!! This function is considered experimental, and may change without 492 // notice. 493 func (r *ResultSet) NextIterator() func(yield func() bool) { 494 return func(yield func() bool) { 495 for r.NextResultSet() { 496 if !yield() { 497 _ = r.Close() 498 break 499 } 500 } 501 } 502 }