-
Notifications
You must be signed in to change notification settings - Fork 0
/
designdoc.go
132 lines (122 loc) · 3.64 KB
/
designdoc.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
// Copyright 2023-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 rosmar
import (
"context"
"database/sql"
"reflect"
sgbucket "github.com/couchbase/sg-bucket"
)
func (c *Collection) GetDDocs() (ddocs map[string]sgbucket.DesignDoc, err error) {
traceEnter("GetDDocs", "")
return c.getDDocs(c.db())
}
func (c *Collection) getDDocs(q queryable) (ddocs map[string]sgbucket.DesignDoc, err error) {
var rows *sql.Rows
rows, err = q.Query(`SELECT designDocs.name, views.name, views.mapFn, views.reduceFn
FROM views RIGHT JOIN designDocs ON views.designDoc=designDocs.id
WHERE designDocs.collection=?1`, c.id)
if err != nil {
return
}
ddocs = map[string]sgbucket.DesignDoc{}
for rows.Next() {
var ddocName string
var vName, mapFn, reduceFn sql.NullString
if err = rows.Scan(&ddocName, &vName, &mapFn, &reduceFn); err != nil {
return
}
ddoc, found := ddocs[ddocName]
if !found {
ddoc.Language = "javascript"
ddoc.Views = sgbucket.ViewMap{}
}
if vName.Valid {
ddoc.Views[vName.String] = sgbucket.ViewDef{Map: mapFn.String, Reduce: reduceFn.String}
}
ddocs[ddocName] = ddoc
}
return ddocs, rows.Close()
}
func (c *Collection) GetDDoc(designDoc string) (ddoc sgbucket.DesignDoc, err error) {
traceEnter("GetDDoc", "%q", designDoc)
ddoc, err = c.getDDoc(c.db(), designDoc)
traceExit("GetDDoc", err, "ok")
return
}
func (c *Collection) getDDoc(q queryable, designDoc string) (ddoc sgbucket.DesignDoc, err error) {
ddocs, err := c.getDDocs(q)
if err != nil {
return
}
ddoc, found := ddocs[designDoc]
if !found {
err = sgbucket.MissingError{Key: designDoc}
}
return
}
func (c *Collection) PutDDoc(_ context.Context, designDoc string, ddoc *sgbucket.DesignDoc) error {
traceEnter("PutDDoc", "%q, %d views", designDoc, len(ddoc.Views))
err := c.bucket.inTransaction(func(txn *sql.Tx) error {
if existing, err := c.getDDoc(txn, designDoc); err == nil {
if reflect.DeepEqual(ddoc, &existing) {
return nil // unchanged
}
}
_, err := txn.Exec(`DELETE FROM designDocs WHERE collection=?1 AND name=?2`,
c.id, designDoc)
if err != nil {
return err
}
result, err := txn.Exec(`INSERT INTO designDocs (collection,name) VALUES (?1,?2)`,
c.id, designDoc)
if err != nil {
return err
}
ddocID, _ := result.LastInsertId()
for name, view := range ddoc.Views {
_, err := txn.Exec(`INSERT INTO views (designDoc,name,mapFn,reduceFn)
VALUES(?1, ?2, ?3, ?4)`,
ddocID, name, view.Map, view.Reduce)
if err != nil {
return err
}
}
// Remove in-memory view objects for the affected views:
for name := range c.viewCache {
if name.designDoc == designDoc {
delete(c.viewCache, name)
}
}
c.forgetCachedViews(designDoc)
return nil
})
traceExit("PutDDoc", err, "ok")
return err
}
func (c *Collection) DeleteDDoc(designDoc string) error {
traceEnter("DeleteDDoc", "%q", designDoc)
err := c.bucket.inTransaction(func(txn *sql.Tx) error {
result, err := txn.Exec(`DELETE FROM designDocs WHERE collection=?1 AND name=?2`,
c.id, designDoc)
if err == nil {
if n, err2 := result.RowsAffected(); n == 0 && err2 == nil {
err = sgbucket.MissingError{Key: designDoc}
} else {
c.forgetCachedViews(designDoc)
}
}
return err
})
traceExit("DeleteDDoc", err, "ok")
return err
}
var (
// Enforce interface conformance:
_ sgbucket.ViewStore = &Collection{}
)