1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package pouchdb
16
17 import (
18 "bytes"
19 "context"
20 "encoding/json"
21 "io"
22 "net/http"
23 "sync/atomic"
24
25 "github.com/gopherjs/gopherjs/js"
26
27 "github.com/go-kivik/kivik/v4/driver"
28 internal "github.com/go-kivik/kivik/v4/int/errors"
29 "github.com/go-kivik/kivik/v4/pouchdb/bindings"
30 )
31
32 type db struct {
33 db *bindings.DB
34
35 client *client
36
37
38
39 compacting uint32
40 viewCleanup uint32
41 }
42
43 var _ driver.DB = (*db)(nil)
44
45 func (d *db) AllDocs(ctx context.Context, options driver.Options) (driver.Rows, error) {
46 opts := map[string]interface{}{}
47 options.Apply(opts)
48 result, err := d.db.AllDocs(ctx, opts)
49 if err != nil {
50 return nil, err
51 }
52 return &rows{
53 Object: result,
54 }, nil
55 }
56
57 func (d *db) Query(ctx context.Context, ddoc, view string, options driver.Options) (driver.Rows, error) {
58 opts := map[string]interface{}{}
59 options.Apply(opts)
60 result, err := d.db.Query(ctx, ddoc, view, opts)
61 if err != nil {
62 return nil, err
63 }
64 return &rows{
65 Object: result,
66 }, nil
67 }
68
69 type document struct {
70 id string
71 rev string
72 body io.Reader
73 done bool
74 }
75
76 func (d *document) Next(row *driver.Row) error {
77 if d.done {
78 return io.EOF
79 }
80 d.done = true
81 row.ID = d.id
82 row.Rev = d.rev
83 row.Doc = d.body
84 return nil
85 }
86
87 func (d *document) Close() error {
88 d.done = true
89 return nil
90 }
91
92 func (d *document) UpdateSeq() string { return "" }
93 func (d *document) Offset() int64 { return 0 }
94 func (d *document) TotalRows() int64 { return 0 }
95
96 func (d *db) Get(ctx context.Context, docID string, options driver.Options) (*driver.Document, error) {
97 opts := map[string]interface{}{}
98 options.Apply(opts)
99 doc, rev, err := d.db.Get(ctx, docID, opts)
100 if err != nil {
101 return nil, err
102 }
103 return &driver.Document{
104 Rev: rev,
105 Body: io.NopCloser(bytes.NewReader(doc)),
106 }, nil
107 }
108
109 func (d *db) CreateDoc(ctx context.Context, doc interface{}, options driver.Options) (docID, rev string, err error) {
110 jsonDoc, err := json.Marshal(doc)
111 if err != nil {
112 return "", "", err
113 }
114 jsDoc := js.Global.Get("JSON").Call("parse", string(jsonDoc))
115 opts := map[string]interface{}{}
116 options.Apply(opts)
117 return d.db.Post(ctx, jsDoc, opts)
118 }
119
120 func (d *db) Put(ctx context.Context, docID string, doc interface{}, options driver.Options) (rev string, err error) {
121 jsonDoc, err := json.Marshal(doc)
122 if err != nil {
123 return "", err
124 }
125 jsDoc := js.Global.Get("JSON").Call("parse", string(jsonDoc))
126 if id := jsDoc.Get("_id"); id != js.Undefined {
127 if id.String() != docID {
128 return "", &internal.Error{Status: http.StatusBadRequest, Message: "id argument must match _id field in document"}
129 }
130 }
131 opts := map[string]interface{}{}
132 options.Apply(opts)
133 jsDoc.Set("_id", docID)
134 return d.db.Put(ctx, jsDoc, opts)
135 }
136
137 func (d *db) Delete(ctx context.Context, docID string, options driver.Options) (newRev string, err error) {
138 opts := map[string]interface{}{}
139 options.Apply(opts)
140 rev, _ := opts["rev"].(string)
141 return d.db.Delete(ctx, docID, rev, opts)
142 }
143
144 func (d *db) Stats(ctx context.Context) (*driver.DBStats, error) {
145 i, err := d.db.Info(ctx)
146 return &driver.DBStats{
147 Name: i.Name,
148 CompactRunning: atomic.LoadUint32(&d.compacting) == 1 || atomic.LoadUint32(&d.viewCleanup) == 1,
149 DocCount: i.DocCount,
150 UpdateSeq: i.UpdateSeq,
151 }, err
152 }
153
154 func (d *db) Compact(context.Context) error {
155 if atomic.LoadUint32(&d.compacting) == 1 {
156 return &internal.Error{Status: http.StatusTooManyRequests, Message: "kivik: compaction already running"}
157 }
158 atomic.StoreUint32(&d.compacting, 1)
159 defer atomic.StoreUint32(&d.compacting, 0)
160 return d.db.Compact()
161 }
162
163
164 func (d *db) CompactView(context.Context, string) error {
165 return nil
166 }
167
168 func (d *db) ViewCleanup(context.Context) error {
169 if atomic.LoadUint32(&d.viewCleanup) == 1 {
170 return &internal.Error{Status: http.StatusTooManyRequests, Message: "kivik: view cleanup already running"}
171 }
172 atomic.StoreUint32(&d.viewCleanup, 1)
173 defer atomic.StoreUint32(&d.viewCleanup, 0)
174 return d.db.ViewCleanup()
175 }
176
177 func (d *db) Close() error {
178 return d.db.Close()
179 }
180
181 var _ driver.Purger = &db{}
182
183 func (d *db) Purge(ctx context.Context, docRevMap map[string][]string) (*driver.PurgeResult, error) {
184 result := &driver.PurgeResult{
185 Purged: make(map[string][]string, len(docRevMap)),
186 }
187 for docID, revs := range docRevMap {
188 for _, rev := range revs {
189 delRevs, err := d.db.Purge(ctx, docID, rev)
190 if err != nil {
191 return result, err
192 }
193 if result.Purged[docID] == nil {
194 result.Purged[docID] = delRevs
195 } else {
196 result.Purged[docID] = append(result.Purged[docID], delRevs...)
197 }
198 }
199 }
200 return result, nil
201 }
202
View as plain text