-
Notifications
You must be signed in to change notification settings - Fork 138
/
Copy pathspecial_docs.go
145 lines (125 loc) · 4.4 KB
/
special_docs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright 2012-Present Couchbase, Inc.
//
// Use of this software is governed by the Business Source License included
// in the file licenses/BSL-Couchbase.txt. As of the Change Date specified
// in that file, in accordance with the Business Source License, use of this
// software will be governed by the Apache License, Version 2.0, included in
// the file licenses/APL2.txt.
package db
import (
"fmt"
"net/http"
"github.com/couchbase/sync_gateway/base"
)
const (
DocTypeLocal = "local"
LocalDocPrefix = "_local/"
)
func (c *DatabaseCollection) GetSpecial(doctype string, docid string) (Body, error) {
body := Body{}
bytes, err := c.GetSpecialBytes(doctype, docid)
if err != nil {
return nil, err
}
err = body.Unmarshal(bytes)
if err != nil {
return nil, err
}
return body, err
}
func (c *DatabaseCollection) GetSpecialBytes(doctype string, docid string) ([]byte, error) {
return getSpecialBytes(c.dataStore, doctype, docid, int(c.localDocExpirySecs()))
}
func getWithTouch(dataStore base.DataStore, docID string, expirySecs int) ([]byte, error) {
var rawDocBytes []byte
var err error
if expirySecs > 0 {
expiry := base.SecondsToCbsExpiry(expirySecs)
rawDocBytes, _, err = dataStore.GetAndTouchRaw(docID, expiry)
} else {
rawDocBytes, _, err = dataStore.GetRaw(docID)
}
if err != nil {
return nil, err
}
return rawDocBytes, nil
}
func getSpecialBytes(dataStore base.DataStore, doctype string, docID string, localDocExpirySecs int) ([]byte, error) {
key := RealSpecialDocID(doctype, docID)
if key == "" {
return nil, base.HTTPErrorf(400, "Invalid doc ID")
}
// only local docs should have expiry based on localDocExpirySecs
if doctype != DocTypeLocal {
localDocExpirySecs = 0
}
return getWithTouch(dataStore, key, localDocExpirySecs)
}
// Updates or deletes a document with BodyRev-based version control.
func putDocWithRevision(dataStore base.DataStore, docID string, matchRev string, body Body, expirySecs int) (newRevID string, isNewDoc bool, err error) {
var revid string
var expiry uint32
if expirySecs > 0 {
expiry = base.SecondsToCbsExpiry(expirySecs)
}
// isNewDoc is set to false if the update is being applied on top of an existing document.
isNewDoc = true
_, err = dataStore.Update(docID, expiry, func(value []byte) ([]byte, *uint32, bool, error) {
if len(value) == 0 {
if matchRev != "" || body == nil {
return nil, nil, false, base.HTTPErrorf(http.StatusNotFound, "No previous revision to replace")
}
} else {
isNewDoc = false
prevBody := Body{}
if err := prevBody.Unmarshal(value); err != nil {
return nil, nil, false, err
}
if matchRev != prevBody[BodyRev] {
return nil, nil, false, base.HTTPErrorf(http.StatusConflict, "Document update conflict")
}
}
if body != nil {
// Updating:
var generation uint
if matchRev != "" {
_, _ = fmt.Sscanf(matchRev, "0-%d", &generation)
}
revid = fmt.Sprintf("0-%d", generation+1)
body[BodyRev] = revid
bodyBytes, marshalErr := base.JSONMarshal(body)
return bodyBytes, nil, false, marshalErr
} else {
// Deleting:
return nil, nil, true, nil
}
})
return revid, isNewDoc, err
}
func putSpecial(dataStore base.DataStore, doctype string, docid string, matchRev string, body Body, localDocExpirySecs int) (revID string, isNewDoc bool, err error) {
key := RealSpecialDocID(doctype, docid)
if key == "" {
return "", false, base.HTTPErrorf(400, "Invalid doc ID")
}
// only local docs should have expiry based on localDocExpirySecs
if doctype != DocTypeLocal {
localDocExpirySecs = 0
}
return putDocWithRevision(dataStore, key, matchRev, body, localDocExpirySecs)
}
// Updates or deletes a special document.
func (c *DatabaseCollection) putSpecial(doctype string, docid string, matchRev string, body Body) (revID string, isNewDoc bool, err error) {
return putSpecial(c.dataStore, doctype, docid, matchRev, body, int(c.localDocExpirySecs()))
}
func (c *DatabaseCollection) PutSpecial(doctype string, docid string, body Body) (revID string, isNewDoc bool, err error) {
matchRev, _ := body[BodyRev].(string)
body, _ = stripAllSpecialProperties(body)
return c.putSpecial(doctype, docid, matchRev, body)
}
func (c *DatabaseCollection) DeleteSpecial(doctype string, docid string, revid string) error {
_, _, err := c.putSpecial(doctype, docid, revid, nil)
return err
}
func RealSpecialDocID(doctype string, docid string) string {
return base.SyncDocPrefix + doctype + ":" + docid
}