1package kivik 2 3import ( 4 "context" 5 "strings" 6 7 "github.com/go-kivik/kivik/driver" 8 "github.com/go-kivik/kivik/errors" 9) 10 11// Rows is an iterator over a a multi-value query. 12type Rows struct { 13 *iter 14 rowsi driver.Rows 15} 16 17// Next prepares the next result value for reading. It returns true on success 18// or false if there are no more results or an error occurs while preparing it. 19// Err should be consulted to distinguish between the two. 20func (r *Rows) Next() bool { 21 return r.iter.Next() 22} 23 24// Err returns the error, if any, that was encountered during iteration. Err may 25// be called after an explicit or implicit Close. 26func (r *Rows) Err() error { 27 return r.iter.Err() 28} 29 30// Close closes the Rows, preventing further enumeration, and freeing any 31// resources (such as the http request body) of the underlying query. If Next is 32// called and there are no further results, Rows is closed automatically and it 33// will suffice to check the result of Err. Close is idempotent and does not 34// affect the result of Err. 35func (r *Rows) Close() error { 36 return r.iter.Close() 37} 38 39type rowsIterator struct{ driver.Rows } 40 41var _ iterator = &rowsIterator{} 42 43func (r *rowsIterator) Next(i interface{}) error { return r.Rows.Next(i.(*driver.Row)) } 44 45func newRows(ctx context.Context, rowsi driver.Rows) *Rows { 46 return &Rows{ 47 iter: newIterator(ctx, &rowsIterator{rowsi}, &driver.Row{}), 48 rowsi: rowsi, 49 } 50} 51 52var ( 53 errNilPtr = errors.Status(StatusBadRequest, "kivik: destination pointer is nil") 54 errNonPtr = errors.Status(StatusBadRequest, "kivik: destination is not a pointer") 55) 56 57// ScanValue copies the data from the result value into the value pointed at by 58// dest. Think of this as a json.Unmarshal into dest. 59// 60// If the dest argument has type *[]byte, Scan stores a copy of the input data. 61// The copy is owned by the caller and can be modified and held indefinitely. 62// 63// The copy can be avoided by using an argument of type *json.RawMessage 64// instead. After a Scaninto a json.RawMessage, the slice is only valid until 65// the next call to Next, Scan, or Close. 66// 67// For all other types, refer to the documentation for json.Unmarshal for type 68// conversion rules. 69func (r *Rows) ScanValue(dest interface{}) error { 70 runlock, err := r.rlock() 71 if err != nil { 72 return err 73 } 74 defer runlock() 75 return scan(dest, r.curVal.(*driver.Row).Value) 76} 77 78// ScanDoc works the same as ScanValue, but on the doc field of the result. It 79// is only valid for results that include documents. 80func (r *Rows) ScanDoc(dest interface{}) error { 81 runlock, err := r.rlock() 82 if err != nil { 83 return err 84 } 85 defer runlock() 86 doc := r.curVal.(*driver.Row).Doc 87 if doc == nil { 88 return errors.Status(StatusBadRequest, "kivik: doc is nil; does the query include docs?") 89 } 90 return scan(dest, doc) 91} 92 93// ScanKey works the same as ScanValue, but on the key field of the result. For 94// simple keys, which are just strings, the Key() method may be easier to use. 95func (r *Rows) ScanKey(dest interface{}) error { 96 runlock, err := r.rlock() 97 if err != nil { 98 return err 99 } 100 defer runlock() 101 return scan(dest, r.curVal.(*driver.Row).Key) 102} 103 104// ID returns the ID of the current result. 105func (r *Rows) ID() string { 106 runlock, err := r.rlock() 107 if err != nil { 108 return "" 109 } 110 defer runlock() 111 return r.curVal.(*driver.Row).ID 112} 113 114// Key returns the Key of the current result as a de-quoted JSON object. For 115// compound keys, the ScanKey() method may be more convenient. 116func (r *Rows) Key() string { 117 runlock, err := r.rlock() 118 if err != nil { 119 return "" 120 } 121 defer runlock() 122 return strings.Trim(string(r.curVal.(*driver.Row).Key), `"`) 123} 124 125// Offset returns the starting offset where the result set started. It is 126// only guaranteed to be set after all result rows have been enumerated through 127// by Next, and thus should only be read after processing all rows in a result 128// set. Calling Close before enumerating will render this value unreliable. 129func (r *Rows) Offset() int64 { 130 return r.rowsi.Offset() 131} 132 133// TotalRows returns the total number of rows in the view which would have been 134// returned if no limiting were used. This value is only guaranteed to be set 135// after all result rows have been enumerated through by Next, and thus should 136// only be read after processing all rows in a result set. Calling Close before 137// enumerating will render this value unreliable. 138func (r *Rows) TotalRows() int64 { 139 return r.rowsi.TotalRows() 140} 141 142// UpdateSeq returns the sequence id of the underlying database the view 143// reflects, if requested in the query. 144func (r *Rows) UpdateSeq() string { 145 return r.rowsi.UpdateSeq() 146} 147 148// Warning returns a warning generated by the query, if any. This value is only 149// guaranteed to be set after all result rows have been enumeratd through by 150// Next. 151func (r *Rows) Warning() string { 152 if w, ok := r.rowsi.(driver.RowsWarner); ok { 153 return w.Warning() 154 } 155 return "" 156} 157 158// Bookmark returns the paging bookmark, if one was provided with the result 159// set. This is intended for use with the Mango /_find interface, with CouchDB 160// 2.1.1 and later. Consult the official CouchDB documentation for detailed 161// usage instructions. http://docs.couchdb.org/en/2.1.1/api/database/find.html#pagination 162func (r *Rows) Bookmark() string { 163 if b, ok := r.rowsi.(driver.Bookmarker); ok { 164 return b.Bookmark() 165 } 166 return "" 167} 168