2015-03-14 05:57:18 +03:00
|
|
|
// +build lmdb
|
|
|
|
|
2015-02-03 05:17:15 +03:00
|
|
|
package mdb
|
|
|
|
|
|
|
|
/*
|
|
|
|
#cgo CFLAGS: -pthread -W -Wall -Wno-unused-parameter -Wbad-function-cast -O2 -g
|
|
|
|
#cgo freebsd CFLAGS: -DMDB_DSYNC=O_SYNC
|
|
|
|
#cgo openbsd CFLAGS: -DMDB_DSYNC=O_SYNC
|
|
|
|
#cgo netbsd CFLAGS: -DMDB_DSYNC=O_SYNC
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "lmdb.h"
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math"
|
|
|
|
"runtime"
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
// DBIOpen Database Flags
|
|
|
|
const (
|
|
|
|
REVERSEKEY = C.MDB_REVERSEKEY // use reverse string keys
|
|
|
|
DUPSORT = C.MDB_DUPSORT // use sorted duplicates
|
|
|
|
INTEGERKEY = C.MDB_INTEGERKEY // numeric keys in native byte order. The keys must all be of the same size.
|
|
|
|
DUPFIXED = C.MDB_DUPFIXED // with DUPSORT, sorted dup items have fixed size
|
|
|
|
INTEGERDUP = C.MDB_INTEGERDUP // with DUPSORT, dups are numeric in native byte order
|
|
|
|
REVERSEDUP = C.MDB_REVERSEDUP // with DUPSORT, use reverse string dups
|
|
|
|
CREATE = C.MDB_CREATE // create DB if not already existing
|
|
|
|
)
|
|
|
|
|
|
|
|
// put flags
|
|
|
|
const (
|
|
|
|
NODUPDATA = C.MDB_NODUPDATA
|
|
|
|
NOOVERWRITE = C.MDB_NOOVERWRITE
|
|
|
|
RESERVE = C.MDB_RESERVE
|
|
|
|
APPEND = C.MDB_APPEND
|
|
|
|
APPENDDUP = C.MDB_APPENDDUP
|
|
|
|
)
|
|
|
|
|
|
|
|
// Txn is Opaque structure for a transaction handle.
|
|
|
|
// All database operations require a transaction handle.
|
|
|
|
// Transactions may be read-only or read-write.
|
|
|
|
type Txn struct {
|
|
|
|
_txn *C.MDB_txn
|
|
|
|
}
|
|
|
|
|
|
|
|
func (env *Env) BeginTxn(parent *Txn, flags uint) (*Txn, error) {
|
|
|
|
var _txn *C.MDB_txn
|
|
|
|
var ptxn *C.MDB_txn
|
|
|
|
if parent == nil {
|
|
|
|
ptxn = nil
|
|
|
|
} else {
|
|
|
|
ptxn = parent._txn
|
|
|
|
}
|
|
|
|
if flags&RDONLY == 0 {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
}
|
|
|
|
ret := C.mdb_txn_begin(env._env, ptxn, C.uint(flags), &_txn)
|
|
|
|
if ret != SUCCESS {
|
|
|
|
runtime.UnlockOSThread()
|
|
|
|
return nil, errno(ret)
|
|
|
|
}
|
|
|
|
return &Txn{_txn}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) Commit() error {
|
|
|
|
ret := C.mdb_txn_commit(txn._txn)
|
|
|
|
runtime.UnlockOSThread()
|
2015-03-14 05:57:18 +03:00
|
|
|
// The transaction handle is freed if there was no error
|
|
|
|
if ret == C.MDB_SUCCESS {
|
|
|
|
txn._txn = nil
|
|
|
|
}
|
2015-02-03 05:17:15 +03:00
|
|
|
return errno(ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) Abort() {
|
|
|
|
if txn._txn == nil {
|
2015-03-14 05:57:18 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
C.mdb_txn_abort(txn._txn)
|
2015-02-03 05:17:15 +03:00
|
|
|
runtime.UnlockOSThread()
|
2015-03-14 05:57:18 +03:00
|
|
|
// The transaction handle is always freed.
|
2015-02-03 05:17:15 +03:00
|
|
|
txn._txn = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) Reset() {
|
|
|
|
C.mdb_txn_reset(txn._txn)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) Renew() error {
|
|
|
|
ret := C.mdb_txn_renew(txn._txn)
|
|
|
|
return errno(ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) DBIOpen(name *string, flags uint) (DBI, error) {
|
|
|
|
var _dbi C.MDB_dbi
|
|
|
|
var cname *C.char
|
|
|
|
if name == nil {
|
|
|
|
cname = nil
|
|
|
|
} else {
|
|
|
|
cname = C.CString(*name)
|
|
|
|
defer C.free(unsafe.Pointer(cname))
|
|
|
|
}
|
|
|
|
ret := C.mdb_dbi_open(txn._txn, cname, C.uint(flags), &_dbi)
|
|
|
|
if ret != SUCCESS {
|
|
|
|
return DBI(math.NaN()), errno(ret)
|
|
|
|
}
|
|
|
|
return DBI(_dbi), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) Stat(dbi DBI) (*Stat, error) {
|
|
|
|
var _stat C.MDB_stat
|
|
|
|
ret := C.mdb_stat(txn._txn, C.MDB_dbi(dbi), &_stat)
|
|
|
|
if ret != SUCCESS {
|
|
|
|
return nil, errno(ret)
|
|
|
|
}
|
|
|
|
stat := Stat{PSize: uint(_stat.ms_psize),
|
|
|
|
Depth: uint(_stat.ms_depth),
|
|
|
|
BranchPages: uint64(_stat.ms_branch_pages),
|
|
|
|
LeafPages: uint64(_stat.ms_leaf_pages),
|
|
|
|
OverflowPages: uint64(_stat.ms_overflow_pages),
|
|
|
|
Entries: uint64(_stat.ms_entries)}
|
|
|
|
return &stat, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) Drop(dbi DBI, del int) error {
|
|
|
|
ret := C.mdb_drop(txn._txn, C.MDB_dbi(dbi), C.int(del))
|
|
|
|
return errno(ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) Get(dbi DBI, key []byte) ([]byte, error) {
|
|
|
|
val, err := txn.GetVal(dbi, key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return val.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) GetVal(dbi DBI, key []byte) (Val, error) {
|
|
|
|
ckey := Wrap(key)
|
|
|
|
var cval Val
|
|
|
|
ret := C.mdb_get(txn._txn, C.MDB_dbi(dbi), (*C.MDB_val)(&ckey), (*C.MDB_val)(&cval))
|
|
|
|
return cval, errno(ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) Put(dbi DBI, key []byte, val []byte, flags uint) error {
|
|
|
|
ckey := Wrap(key)
|
|
|
|
cval := Wrap(val)
|
|
|
|
ret := C.mdb_put(txn._txn, C.MDB_dbi(dbi), (*C.MDB_val)(&ckey), (*C.MDB_val)(&cval), C.uint(flags))
|
|
|
|
return errno(ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) Del(dbi DBI, key, val []byte) error {
|
|
|
|
ckey := Wrap(key)
|
|
|
|
if val == nil {
|
|
|
|
ret := C.mdb_del(txn._txn, C.MDB_dbi(dbi), (*C.MDB_val)(&ckey), nil)
|
|
|
|
return errno(ret)
|
|
|
|
}
|
|
|
|
cval := Wrap(val)
|
|
|
|
ret := C.mdb_del(txn._txn, C.MDB_dbi(dbi), (*C.MDB_val)(&ckey), (*C.MDB_val)(&cval))
|
|
|
|
return errno(ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
type Cursor struct {
|
|
|
|
_cursor *C.MDB_cursor
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) CursorOpen(dbi DBI) (*Cursor, error) {
|
|
|
|
var _cursor *C.MDB_cursor
|
|
|
|
ret := C.mdb_cursor_open(txn._txn, C.MDB_dbi(dbi), &_cursor)
|
|
|
|
if ret != SUCCESS {
|
|
|
|
return nil, errno(ret)
|
|
|
|
}
|
|
|
|
return &Cursor{_cursor}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (txn *Txn) CursorRenew(cursor *Cursor) error {
|
|
|
|
ret := C.mdb_cursor_renew(txn._txn, cursor._cursor)
|
|
|
|
return errno(ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
type CmpFunc func(a, b []byte) int
|
|
|
|
|
|
|
|
func (txn *Txn) SetCompare(dbi DBI, cmp CmpFunc) error {
|
|
|
|
f := func(a, b *C.MDB_val) C.int {
|
|
|
|
ga := C.GoBytes(a.mv_data, C.int(a.mv_size))
|
|
|
|
gb := C.GoBytes(a.mv_data, C.int(a.mv_size))
|
|
|
|
return C.int(cmp(ga, gb))
|
|
|
|
}
|
|
|
|
ret := C.mdb_set_compare(txn._txn, C.MDB_dbi(dbi), *unsafe.Pointer(&f))
|
|
|
|
return errno(ret)
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
// func (txn *Txn) SetDupSort(dbi DBI, comp *C.MDB_comp_func) error
|
|
|
|
// func (txn *Txn) SetRelFunc(dbi DBI, rel *C.MDB_rel_func) error
|
|
|
|
// func (txn *Txn) SetRelCtx(dbi DBI, void *) error
|