1
2
3
4
5
6
7
8
9
10
11
12
13 package kivik
14
15 import (
16 "context"
17 "encoding/json"
18 "fmt"
19 "net/http"
20 "sync"
21
22 "github.com/go-kivik/kivik/v4/driver"
23 internal "github.com/go-kivik/kivik/v4/int/errors"
24 "github.com/go-kivik/kivik/v4/internal/registry"
25 )
26
27
28 type Client struct {
29 dsn string
30 driverName string
31 driverClient driver.Client
32
33 closed bool
34 mu sync.Mutex
35 wg sync.WaitGroup
36 }
37
38
39
40 func Register(name string, driver driver.Driver) {
41 registry.Register(name, driver)
42 }
43
44
45
46
47
48
49 func New(driverName, dataSourceName string, options ...Option) (*Client, error) {
50 driveri := registry.Driver(driverName)
51 if driveri == nil {
52 return nil, &internal.Error{Status: http.StatusBadRequest, Message: fmt.Sprintf("kivik: unknown driver %q (forgotten import?)", driverName)}
53 }
54 client, err := driveri.NewClient(dataSourceName, multiOptions(options))
55 if err != nil {
56 return nil, err
57 }
58 return &Client{
59 dsn: dataSourceName,
60 driverName: driverName,
61 driverClient: client,
62 }, nil
63 }
64
65
66 func (c *Client) Driver() string {
67 return c.driverName
68 }
69
70
71 func (c *Client) DSN() string {
72 return c.dsn
73 }
74
75
76 type ServerVersion struct {
77
78 Version string
79
80 Vendor string
81
82
83 Features []string
84
85
86
87
88
89 RawResponse json.RawMessage
90 }
91
92 func (c *Client) startQuery() (end func(), _ error) {
93 c.mu.Lock()
94 defer c.mu.Unlock()
95 if c.closed {
96 return nil, ErrClientClosed
97 }
98 var once sync.Once
99 c.wg.Add(1)
100 return func() {
101 once.Do(func() {
102 c.mu.Lock()
103 c.wg.Done()
104 c.mu.Unlock()
105 })
106 }, nil
107 }
108
109
110 func (c *Client) Version(ctx context.Context) (*ServerVersion, error) {
111 endQuery, err := c.startQuery()
112 if err != nil {
113 return nil, err
114 }
115 defer endQuery()
116 ver, err := c.driverClient.Version(ctx)
117 if err != nil {
118 return nil, err
119 }
120 v := &ServerVersion{}
121 *v = ServerVersion(*ver)
122 return v, nil
123 }
124
125
126
127
128 func (c *Client) DB(dbName string, options ...Option) *DB {
129 db, err := c.driverClient.DB(dbName, multiOptions(options))
130 return &DB{
131 client: c,
132 name: dbName,
133 driverDB: db,
134 err: err,
135 }
136 }
137
138
139 func (c *Client) AllDBs(ctx context.Context, options ...Option) ([]string, error) {
140 endQuery, err := c.startQuery()
141 if err != nil {
142 return nil, err
143 }
144 defer endQuery()
145 return c.driverClient.AllDBs(ctx, multiOptions(options))
146 }
147
148
149 func (c *Client) DBExists(ctx context.Context, dbName string, options ...Option) (bool, error) {
150 endQuery, err := c.startQuery()
151 if err != nil {
152 return false, err
153 }
154 defer endQuery()
155 return c.driverClient.DBExists(ctx, dbName, multiOptions(options))
156 }
157
158
159 func (c *Client) CreateDB(ctx context.Context, dbName string, options ...Option) error {
160 endQuery, err := c.startQuery()
161 if err != nil {
162 return err
163 }
164 defer endQuery()
165 return c.driverClient.CreateDB(ctx, dbName, multiOptions(options))
166 }
167
168
169 func (c *Client) DestroyDB(ctx context.Context, dbName string, options ...Option) error {
170 endQuery, err := c.startQuery()
171 if err != nil {
172 return err
173 }
174 defer endQuery()
175 return c.driverClient.DestroyDB(ctx, dbName, multiOptions(options))
176 }
177
178 func missingArg(arg string) error {
179 return &internal.Error{Status: http.StatusBadRequest, Message: fmt.Sprintf("kivik: %s required", arg)}
180 }
181
182
183 func (c *Client) DBsStats(ctx context.Context, dbnames []string) ([]*DBStats, error) {
184 endQuery, err := c.startQuery()
185 if err != nil {
186 return nil, err
187 }
188 defer endQuery()
189 dbstats, err := c.nativeDBsStats(ctx, dbnames)
190 switch HTTPStatus(err) {
191 case http.StatusNotFound, http.StatusNotImplemented:
192 return c.fallbackDBsStats(ctx, dbnames)
193 }
194 return dbstats, err
195 }
196
197 func (c *Client) fallbackDBsStats(ctx context.Context, dbnames []string) ([]*DBStats, error) {
198 dbstats := make([]*DBStats, len(dbnames))
199 for i, dbname := range dbnames {
200 stat, err := c.DB(dbname).Stats(ctx)
201 switch {
202 case HTTPStatus(err) == http.StatusNotFound:
203 continue
204 case err != nil:
205 return nil, err
206 default:
207 dbstats[i] = stat
208 }
209 }
210 return dbstats, nil
211 }
212
213 func (c *Client) nativeDBsStats(ctx context.Context, dbnames []string) ([]*DBStats, error) {
214 statser, ok := c.driverClient.(driver.DBsStatser)
215 if !ok {
216 return nil, &internal.Error{Status: http.StatusNotImplemented, Message: "kivik: not supported by driver"}
217 }
218 stats, err := statser.DBsStats(ctx, dbnames)
219 if err != nil {
220 return nil, err
221 }
222 dbstats := make([]*DBStats, len(stats))
223 for i, stat := range stats {
224 if stat != nil {
225 dbstats[i] = driverStats2kivikStats(stat)
226 }
227 }
228 return dbstats, nil
229 }
230
231
232
233
234
235
236
237
238 func (c *Client) AllDBsStats(ctx context.Context, options ...Option) ([]*DBStats, error) {
239 endQuery, err := c.startQuery()
240 if err != nil {
241 return nil, err
242 }
243 defer endQuery()
244 dbstats, err := c.nativeAllDBsStats(ctx, options...)
245 switch HTTPStatus(err) {
246 case http.StatusMethodNotAllowed, http.StatusNotImplemented:
247 return c.fallbackAllDBsStats(ctx, options...)
248 }
249 return dbstats, err
250 }
251
252 func (c *Client) nativeAllDBsStats(ctx context.Context, options ...Option) ([]*DBStats, error) {
253 statser, ok := c.driverClient.(driver.AllDBsStatser)
254 if !ok {
255 return nil, &internal.Error{Status: http.StatusNotImplemented, Message: "kivik: not supported by driver"}
256 }
257 stats, err := statser.AllDBsStats(ctx, multiOptions(options))
258 if err != nil {
259 return nil, err
260 }
261 dbstats := make([]*DBStats, len(stats))
262 for i, stat := range stats {
263 dbstats[i] = driverStats2kivikStats(stat)
264 }
265 return dbstats, nil
266 }
267
268 func (c *Client) fallbackAllDBsStats(ctx context.Context, options ...Option) ([]*DBStats, error) {
269 dbs, err := c.AllDBs(ctx, options...)
270 if err != nil {
271 return nil, err
272 }
273 return c.DBsStats(ctx, dbs)
274 }
275
276
277 func (c *Client) Ping(ctx context.Context) (bool, error) {
278 endQuery, err := c.startQuery()
279 if err != nil {
280 return false, err
281 }
282 defer endQuery()
283 if pinger, ok := c.driverClient.(driver.Pinger); ok {
284 return pinger.Ping(ctx)
285 }
286 _, err = c.driverClient.Version(ctx)
287 return err == nil, err
288 }
289
290
291
292
293
294 func (c *Client) Close() error {
295 c.mu.Lock()
296 c.closed = true
297 c.mu.Unlock()
298 c.wg.Wait()
299 if closer, ok := c.driverClient.(driver.ClientCloser); ok {
300 return closer.Close()
301 }
302 return nil
303 }
304
View as plain text