From 9eaf85b974eeb4754d9c5226a9de91798d89936a Mon Sep 17 00:00:00 2001 From: Jonathan Hall Date: Tue, 25 Jun 2019 12:18:44 +0200 Subject: [PATCH] Convert RevsDiff to a stream producer --- db.go | 20 +++++++++++++------- db_test.go | 32 ++++++++++++++++++++------------ driver/driver.go | 4 +++- mock/db.go | 4 ++-- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/db.go b/db.go index 8bb7b98d8..3ed27efee 100644 --- a/db.go +++ b/db.go @@ -661,18 +661,24 @@ type Diffs map[string]RevDiff // and is normally never needed otherwise. revMap must marshal to the expected // format. // +// Uses Rows.ScanValue to access each element in the returned list. The +// value will be of the JSON format: +// +// { +// "{doc_id}": { +// "missing": ["rev1",...], +// "possible_ancestors": ["revA",...] +// } +// } +// // See http://docs.couchdb.org/en/stable/api/database/misc.html#db-revs-diff -func (db *DB) RevsDiff(ctx context.Context, revMap interface{}) (Diffs, error) { +func (db *DB) RevsDiff(ctx context.Context, revMap interface{}) (*Rows, error) { if rd, ok := db.driverDB.(driver.RevsDiffer); ok { - result, err := rd.RevsDiff(ctx, revMap) + rowsi, err := rd.RevsDiff(ctx, revMap) if err != nil { return nil, err } - diffs := make(Diffs, len(result)) - for k, v := range result { - diffs[k] = RevDiff(v) - } - return diffs, nil + return newRows(ctx, rowsi), nil } return nil, &Error{HTTPStatus: http.StatusNotImplemented, Message: "kivik: _revs_diff not supported by driver"} } diff --git a/db_test.go b/db_test.go index adec6cc8e..46b01a561 100644 --- a/db_test.go +++ b/db_test.go @@ -1964,10 +1964,11 @@ func TestClientClose(t *testing.T) { func TestRevsDiff(t *testing.T) { type tt struct { - db *DB - revMap interface{} - status int - err string + db *DB + revMap interface{} + status int + err string + expected interface{} } tests := testy.NewTable() tests.Add("non-DBReplicator", tt{ @@ -1977,7 +1978,7 @@ func TestRevsDiff(t *testing.T) { }) tests.Add("network error", tt{ db: &DB{driverDB: &mock.RevsDiffer{ - RevsDiffFunc: func(_ context.Context, revMap interface{}) (map[string]driver.RevDiff, error) { + RevsDiffFunc: func(_ context.Context, revMap interface{}) (driver.Rows, error) { return nil, errors.New("net error") }, }}, @@ -1986,19 +1987,26 @@ func TestRevsDiff(t *testing.T) { }) tests.Add("success", tt{ db: &DB{driverDB: &mock.RevsDiffer{ - RevsDiffFunc: func(_ context.Context, revMap interface{}) (map[string]driver.RevDiff, error) { - return map[string]driver.RevDiff{ - "foo": {Missing: []string{"1", "2"}}, - "bar": {PossibleAncestors: []string{"3", "4"}}, - }, nil + RevsDiffFunc: func(_ context.Context, revMap interface{}) (driver.Rows, error) { + return &mock.Rows{ID: "a"}, nil }, }}, + expected: &Rows{ + iter: &iter{ + feed: &rowsIterator{ + Rows: &mock.Rows{ID: "a"}, + }, + curVal: &driver.Row{}, + }, + rowsi: &mock.Rows{ID: "a"}, + }, }) tests.Run(t, func(t *testing.T, tt tt) { - result, err := tt.db.RevsDiff(context.Background(), tt.revMap) + rows, err := tt.db.RevsDiff(context.Background(), tt.revMap) testy.StatusError(t, tt.err, tt.status, err) - if d := diff.AsJSON(&diff.File{Path: "testdata/" + testy.Stub(t)}, result); d != nil { + rows.cancel = nil // Determinism + if d := diff.Interface(tt.expected, rows); d != nil { t.Error(d) } }) diff --git a/driver/driver.go b/driver/driver.go index 301d890a0..bba8f93b7 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -403,5 +403,7 @@ type RevDiff struct { // RevsDiffer is an optional interface that may be implemented by a DB. type RevsDiffer interface { - RevsDiff(ctx context.Context, revMap interface{}) (map[string]RevDiff, error) + // RevsDiff returns a Rows iterator, which should populate the Value + // field, and nothing else. + RevsDiff(ctx context.Context, revMap interface{}) (Rows, error) } diff --git a/mock/db.go b/mock/db.go index 01ff12e9e..b4f65dff8 100644 --- a/mock/db.go +++ b/mock/db.go @@ -267,12 +267,12 @@ func (db *DBCloser) Close(ctx context.Context) error { // RevsDiffer mocks a driver.DB and driver.RevsDiffer. type RevsDiffer struct { *BulkDocer - RevsDiffFunc func(context.Context, interface{}) (map[string]driver.RevDiff, error) + RevsDiffFunc func(context.Context, interface{}) (driver.Rows, error) } var _ driver.RevsDiffer = &RevsDiffer{} // RevsDiff calls db.RevsDiffFunc -func (db *RevsDiffer) RevsDiff(ctx context.Context, revMap interface{}) (map[string]driver.RevDiff, error) { +func (db *RevsDiffer) RevsDiff(ctx context.Context, revMap interface{}) (driver.Rows, error) { return db.RevsDiffFunc(ctx, revMap) }