-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathejdb.go
380 lines (335 loc) · 12.7 KB
/
ejdb.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
package goejdb
// #cgo LDFLAGS: -lejdb
// #include <ejdb/ejdb.h>
import "C"
import (
"errors"
"fmt"
"unsafe"
)
// Database open modes
const (
// Open as a reader.
JBOREADER = C.JBOREADER
// Open as a writer.
JBOWRITER = C.JBOWRITER
// Create if db file not exists.
JBOCREAT = C.JBOCREAT
// Truncate db on open.
JBOTRUNC = C.JBOTRUNC
// Open without locking.
JBONOLCK = C.JBONOLCK
// Lock without blocking.
JBOLCKNB = C.JBOLCKNB
// Synchronize every transaction.
JBOTSYNC = C.JBOTSYNC
)
// Error codes
const (
// Invalid collection name.
JBEINVALIDCOLNAME = C.JBEINVALIDCOLNAME
// Invalid bson object.
JBEINVALIDBSON = C.JBEINVALIDBSON
// Invalid bson object id.
JBEINVALIDBSONPK = C.JBEINVALIDBSONPK
// Invalid query control field starting with '$'.
JBEQINVALIDQCONTROL = C.JBEQINVALIDQCONTROL
// $strand, $stror, $in, $nin, $bt keys requires not empty array value.
JBEQINOPNOTARRAY = C.JBEQINOPNOTARRAY
// Inconsistent database metadata.
JBEMETANVALID = C.JBEMETANVALID
// Invalid field path value.
JBEFPATHINVALID = C.JBEFPATHINVALID
// Invalid query regexp value.
JBEQINVALIDQRX = C.JBEQINVALIDQRX
// Result set sorting error.
JBEQRSSORTING = C.JBEQRSSORTING
// Query generic error.
JBEQERROR = C.JBEQERROR
// Updating failed.
JBEQUPDFAILED = C.JBEQUPDFAILED
// Only one $elemMatch allowed in the fieldpath.
JBEQONEEMATCH = C.JBEQONEEMATCH
// $fields hint cannot mix include and exclude fields
JBEQINCEXCL = C.JBEQINCEXCL
// action key in $do block can only be one of: $join
JBEQACTKEY = C.JBEQACTKEY
// Exceeded the maximum number of collections per database
JBEMAXNUMCOLS = C.JBEMAXNUMCOLS
)
const maxslice = 1<<31 - 1
// An EJDB database
type Ejdb struct {
ptr *[0]byte
}
type EjdbError struct {
// Error code returned by EJDB
ErrorCode int
error
}
// EJDB collection tuning options
type EjCollOpts struct {
// Large collection. It can be larger than 2GB. Default false
Large bool
// Collection records will be compressed with DEFLATE compression. Default: false
Compressed bool
// Expected records number in the collection. Default: 128K
Records int
// Maximum number of cached records. Default: 0
CachedRecords int
}
func new_ejdb() *Ejdb {
ejdb := new(Ejdb)
ejdb.ptr = (*[0]byte)(unsafe.Pointer(C.ejdbnew()))
if ejdb.ptr == nil {
return nil
}
return ejdb
}
// Returns EJDB library version string. Eg: "1.1.13"
func Version() string {
cs := C.ejdbversion()
return C.GoString(cs)
}
// Return true if passed `oid` string cat be converted to valid 12 bit BSON object identifier (OID).
func IsValidOidStr(oid string) bool {
c_oid := C.CString(oid)
res := C.ejdbisvalidoidstr(c_oid)
C.free(unsafe.Pointer(c_oid))
return bool(res)
}
// Returns a new open EJDB database.
// path is the path to the database file.
// options specify the open mode bitmask flags.
func Open(path string, options int) (*Ejdb, *EjdbError) {
ejdb := new_ejdb()
if ejdb != nil {
c_path := C.CString(path)
defer C.free(unsafe.Pointer(c_path))
C.ejdbopen((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)), c_path, C.int(options))
}
return ejdb, ejdb.check_error()
}
func (ejdb *Ejdb) check_error() *EjdbError {
ecode := C.ejdbecode((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)))
if ecode == 0 {
return nil
}
c_msg := C.ejdberrmsg(ecode)
msg := C.GoString(c_msg)
return &EjdbError{int(ecode), errors.New(fmt.Sprintf("EJDB error: %v", msg))}
}
// Return true if database is in open state, false otherwise
func (ejdb *Ejdb) IsOpen() bool {
ret := C.ejdbisopen((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)))
return bool(ret)
}
// Delete database object. If the database is not closed, it is closed implicitly.
// Note that the deleted object and its derivatives can not be used anymore
func (ejdb *Ejdb) Del() {
C.ejdbdel((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)))
}
// Close a table database object. If a writer opens a database but does not close it appropriately, the database will be broken.
// If successful return true, otherwise return false.
func (ejdb *Ejdb) Close() *EjdbError {
C.ejdbclose((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)))
return ejdb.check_error()
}
// Retrieve collection handle for collection specified `collname`.
// If collection with specified name does't exists it will return nil.
func (ejdb *Ejdb) GetColl(colname string) (*EjColl, *EjdbError) {
c_colname := C.CString(colname)
defer C.free(unsafe.Pointer(c_colname))
ejcoll := new(EjColl)
ejcoll.ejdb = ejdb
ejcoll.ptr = (*[0]byte)(unsafe.Pointer(C.ejdbgetcoll((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)), c_colname)))
return ejcoll, ejdb.check_error()
}
// Return a slice containing shallow copies of all collection handles (EjColl) currently open.
func (ejdb *Ejdb) GetColls() ([]*EjColl, *EjdbError) {
ret := make([]*EjColl, 0)
lst := C.ejdbgetcolls((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)))
if lst == nil {
return ret, ejdb.check_error()
}
for i := int(lst.start); i < int(lst.start)+int(lst.num); i++ {
ptr := uintptr(unsafe.Pointer(lst.array)) + unsafe.Sizeof(C.TCLISTDATUM{})*uintptr(i)
datum := (*C.TCLISTDATUM)(unsafe.Pointer(ptr))
datum_ptr := unsafe.Pointer(datum.ptr)
ret = append(ret, &EjColl{(*[0]byte)(datum_ptr), ejdb})
}
return ret, nil
}
// Same as GetColl() but automatically creates new collection if it doesn't exists.
func (ejdb *Ejdb) CreateColl(colname string, opts *EjCollOpts) (*EjColl, *EjdbError) {
c_colname := C.CString(colname)
defer C.free(unsafe.Pointer(c_colname))
ret := new(EjColl)
ret.ejdb = ejdb
if opts != nil {
var c_opts C.EJCOLLOPTS
c_opts.large = C._Bool(opts.Large)
c_opts.compressed = C._Bool(opts.Large)
c_opts.records = C.int64_t(opts.Records)
c_opts.cachedrecords = C.int(opts.CachedRecords)
ret.ptr = (*[0]byte)(unsafe.Pointer(C.ejdbcreatecoll((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)), c_colname, &c_opts)))
} else {
ret.ptr = (*[0]byte)(unsafe.Pointer(C.ejdbcreatecoll((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)), c_colname, nil)))
}
if ret.ptr != nil {
return ret, nil
}
return nil, ejdb.check_error()
}
// Removes collections specified by `colname`.
// If `unlinkfile` is true the collection db file and all of its index files will be removed.
// If removal was successful return true, otherwise return false.
func (ejdb *Ejdb) RmColl(colname string, unlinkfile bool) (bool, *EjdbError) {
c_colname := C.CString(colname)
defer C.free(unsafe.Pointer(c_colname))
res := C.ejdbrmcoll((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)), c_colname, C._Bool(unlinkfile))
if res {
return bool(res), nil
}
return bool(res), ejdb.check_error()
}
// Synchronize entire EJDB database and all of its collections with storage.
func (ejdb *Ejdb) Sync() (bool, *EjdbError) {
ret := C.ejdbsyncdb((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)))
if ret {
return bool(ret), nil
}
return bool(ret), ejdb.check_error()
}
// Gets description of EJDB database and its collections as a BSON object.
func (ejdb *Ejdb) Meta() ([]byte, *EjdbError) {
bson := C.ejdbmeta((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)))
err := ejdb.check_error()
if err != nil {
return make([]byte, 0), err
}
defer C.bson_del(bson)
return bson_to_byte_slice(bson), nil
}
// Imports previously exported collections data into ejdb.
// Global database write lock will be applied during import operation.
//
// NOTE: Only data exported as BSONs can be imported with `Ejdb.Import()`
//
// `path` is the directory path in which data resides.
// `cnames` is a list of collection names to import. `nil` implies that all collections found in `path` will be imported.
// `flags` can be one of:
// `JBIMPORTUPDATE` Update existing collection entries with imported ones.
// Existing collections will not be recreated.
// For existing collections options will not be imported.
//
// `JBIMPORTREPLACE` Recreate existing collections and replace
// all their data with imported entries.
// Collections options will be imported.
//
// `0` Implies `JBIMPORTUPDATE`
// Import() returns the log for the operation as a string
func (ejdb *Ejdb) Import(path string, cnames *[]string, flags int) (log string, err *EjdbError) {
c_path := C.CString(path)
defer C.free(unsafe.Pointer(c_path))
c_log := C.tcxstrnew()
defer C.tcxstrdel(c_log)
if cnames != nil {
tclist := C.tclistnew2(C.int(len(*cnames)))
defer C.tclistdel(tclist)
for i := 0; i < len(*cnames); i++ {
cname := C.CString((*cnames)[i])
defer C.free(unsafe.Pointer(cname))
C.tclistpush2(tclist, cname)
}
C.ejdbimport((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)), c_path, tclist, C.int(flags), c_log)
} else {
C.ejdbimport((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)), c_path, nil, C.int(flags), c_log)
}
c_chars := C.tcxstrptr(c_log)
ret := C.GoString((*C.char)(c_chars))
return ret, ejdb.check_error()
}
// Exports database collections data to the specified directory.
// Database read lock will be taken on each collection.
//
// NOTE: Only data exported as BSONs can be imported with `Ejdb.Import()`
//
// `path` is the directory path in which data will be exported.
// `cnames` is a list of collection names to export. `nil` implies that all existing collections will be exported.
// `flags` can be set to `JBJSONEXPORT` in order to export data as JSON files instead exporting into BSONs.
// Export() returns the log for the operation as a string
func (ejdb *Ejdb) Export(path string, cnames *[]string, flags int) (log string, err *EjdbError) {
c_path := C.CString(path)
defer C.free(unsafe.Pointer(c_path))
c_log := C.tcxstrnew()
defer C.tcxstrdel(c_log)
if cnames != nil {
tclist := C.tclistnew2(C.int(len(*cnames)))
defer C.tclistdel(tclist)
for i := 0; i < len(*cnames); i++ {
cname := C.CString((*cnames)[i])
defer C.free(unsafe.Pointer(cname))
C.tclistpush2(tclist, cname)
}
C.ejdbexport((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)), c_path, tclist, C.int(flags), c_log)
} else {
C.ejdbexport((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)), c_path, nil, C.int(flags), c_log)
}
c_chars := C.tcxstrptr(c_log)
ret := C.GoString((*C.char)(c_chars))
return ret, ejdb.check_error()
}
// Execute ejdb database command.
//
// Supported commands:
//
//
// 1) Exports database collections data. See ejdbexport() method.
//
// "export" : {
// "path" : string, //Exports database collections data
// "cnames" : [string array]|null, //List of collection names to export
// "mode" : int|null //Values: null|`JBJSONEXPORT` See ejdbexport() method
// }
//
// Command response:
// {
// "log" : string, //Diagnostic log about executing this command
// "error" : string|null, //ejdb error message
// "errorCode" : int|0, //ejdb error code
// }
//
// 2) Imports previously exported collections data into ejdb.
//
// "import" : {
// "path" : string //The directory path in which data resides
// "cnames" : [string array]|null, //List of collection names to import
// "mode" : int|null //Values: null|`JBIMPORTUPDATE`|`JBIMPORTREPLACE` See ejdbimport() method
// }
//
// Command response:
// {
// "log" : string, //Diagnostic log about executing this command
// "error" : string|null, //ejdb error message
// "errorCode" : int|0, //ejdb error code
// }
func (ejdb *Ejdb) Command(bson []byte) (*[]byte, *EjdbError) {
c_bson := bson_from_byte_slice(bson)
defer C.bson_destroy(c_bson)
return ejdb.command(c_bson)
}
func (ejdb *Ejdb) JsonCommand(json string) (*[]byte, *EjdbError) {
c_bson := bson_from_json(json)
defer C.bson_destroy(c_bson)
return ejdb.command(c_bson)
}
func (ejdb *Ejdb) command(c_bson *C.bson) (*[]byte, *EjdbError) {
out_c_bson := C.ejdbcommand((*C.struct_EJDB)(unsafe.Pointer(ejdb.ptr)), c_bson)
if out_c_bson == nil {
return nil, ejdb.check_error()
}
defer C.bson_del(out_c_bson)
out_bson := bson_to_byte_slice(out_c_bson)
return &out_bson, nil
}