1
2
3
4
5
6
7
8
9
10
11
12
13 package memorydb
14
15 import (
16 "bytes"
17 "context"
18 "errors"
19 "fmt"
20 "io"
21 "net/http"
22 "regexp"
23 "strings"
24
25 "github.com/go-kivik/kivik/v4"
26 "github.com/go-kivik/kivik/v4/driver"
27 )
28
29 var notYetImplemented = statusError{status: http.StatusNotImplemented, error: errors.New("kivik: not yet implemented in memory driver")}
30
31
32 type db struct {
33 *client
34 dbName string
35 db *database
36 }
37
38 func (d *db) Query(context.Context, string, string, driver.Options) (driver.Rows, error) {
39
40 return nil, notYetImplemented
41 }
42
43 func (d *db) Get(ctx context.Context, docID string, options driver.Options) (*driver.Document, error) {
44 if exists, _ := d.client.DBExists(ctx, d.dbName, nil); !exists {
45 return nil, statusError{status: http.StatusPreconditionFailed, error: errors.New("database does not exist")}
46 }
47 if !d.db.docExists(docID) {
48 return nil, statusError{status: http.StatusNotFound, error: errors.New("missing")}
49 }
50 opts := map[string]interface{}{}
51 options.Apply(opts)
52 if rev, ok := opts["rev"].(string); ok {
53 if doc, found := d.db.getRevision(docID, rev); found {
54 return &driver.Document{
55 Rev: rev,
56 Body: io.NopCloser(bytes.NewReader(doc.data)),
57 }, nil
58 }
59 return nil, statusError{status: http.StatusNotFound, error: errors.New("missing")}
60 }
61 last, _ := d.db.latestRevision(docID)
62 if last.Deleted {
63 return nil, statusError{status: http.StatusNotFound, error: errors.New("missing")}
64 }
65 return &driver.Document{
66 Rev: fmt.Sprintf("%d-%s", last.ID, last.Rev),
67 Body: io.NopCloser(bytes.NewReader(last.data)),
68 }, nil
69 }
70
71 func (d *db) Put(ctx context.Context, docID string, doc interface{}, _ driver.Options) (rev string, err error) {
72 if exists, _ := d.client.DBExists(ctx, d.dbName, nil); !exists {
73 return "", statusError{status: http.StatusPreconditionFailed, error: errors.New("database does not exist")}
74 }
75 isLocal := strings.HasPrefix(docID, "_local/")
76 if !isLocal && docID[0] == '_' && !strings.HasPrefix(docID, "_design/") {
77 return "", statusError{status: http.StatusBadRequest, error: errors.New("only reserved document ids may start with underscore")}
78 }
79 couchDoc, err := toCouchDoc(doc)
80 if err != nil {
81 return "", err
82 }
83 couchDoc["_id"] = docID
84
85 delete(couchDoc, "_attachments")
86
87 if last, ok := d.db.latestRevision(docID); ok {
88 if !last.Deleted && !isLocal && couchDoc.Rev() != fmt.Sprintf("%d-%s", last.ID, last.Rev) {
89 return "", statusError{status: http.StatusConflict, error: errors.New("document update conflict")}
90 }
91 return d.db.addRevision(couchDoc), nil
92 }
93
94 if couchDoc.Rev() != "" {
95
96 return "", statusError{status: http.StatusConflict, error: errors.New("document update conflict")}
97 }
98 return d.db.addRevision(couchDoc), nil
99 }
100
101 var revRE = regexp.MustCompile("^[0-9]+-[a-f0-9]{32}$")
102
103 func validRev(rev string) bool {
104 return revRE.MatchString(rev)
105 }
106
107 func (d *db) Delete(ctx context.Context, docID string, options driver.Options) (newRev string, err error) {
108 if exists, _ := d.client.DBExists(ctx, d.dbName, kivik.Params(nil)); !exists {
109 return "", statusError{status: http.StatusPreconditionFailed, error: errors.New("database does not exist")}
110 }
111 opts := map[string]interface{}{}
112 options.Apply(opts)
113 rev, _ := opts["rev"].(string)
114 if !strings.HasPrefix(docID, "_local/") && !validRev(rev) {
115 return "", statusError{status: http.StatusBadRequest, error: errors.New("invalid rev format")}
116 }
117 if !d.db.docExists(docID) {
118 return "", statusError{status: http.StatusNotFound, error: errors.New("missing")}
119 }
120 return d.Put(ctx, docID, map[string]interface{}{
121 "_id": docID,
122 "_rev": rev,
123 "_deleted": true,
124 }, nil)
125 }
126
127 func (d *db) Stats(_ context.Context) (*driver.DBStats, error) {
128 return &driver.DBStats{
129 Name: d.dbName,
130
131
132
133
134
135
136 }, nil
137 }
138
139 func (c *client) Compact(_ context.Context) error {
140
141 return notYetImplemented
142 }
143
144 func (d *db) CompactView(_ context.Context, _ string) error {
145
146 return notYetImplemented
147 }
148
149 func (d *db) ViewCleanup(_ context.Context) error {
150
151 return notYetImplemented
152 }
153
154 func (d *db) Changes(context.Context, driver.Options) (driver.Changes, error) {
155
156 return nil, notYetImplemented
157 }
158
159 func (d *db) PutAttachment(context.Context, string, *driver.Attachment, driver.Options) (string, error) {
160
161 return "", notYetImplemented
162 }
163
164 func (d *db) GetAttachment(context.Context, string, string, driver.Options) (*driver.Attachment, error) {
165
166 return nil, notYetImplemented
167 }
168
169 func (d *db) DeleteAttachment(context.Context, string, string, driver.Options) (newRev string, err error) {
170
171 return "", notYetImplemented
172 }
173
174 func (d *db) Close() error { return nil }
175
View as plain text