const (
TYPE_ERROR = 0
TYPE_BYTES = 1
TYPE_INT64 = 2
)
// table cell
type Value struct {
Type uint32
I64 int64
Str []byte
}
// table row
type Record struct {
Cols []string
Vals []Value
}
func (rec *Record) AddStr(key string, val []byte) *Record
func (rec *Record) AddInt64(key string, val int64) *Record
func (rec *Record) Get(key string) *Value
type DB struct {
Path string
// internals
kv KV
tables map[string]*TableDef // cached table definition
}
// table definition
type TableDef struct {
// user defined
Name string
Types []uint32 // column types
Cols []string // column names
PKeys int // the first `PKeys` columns are the primary key
// auto-assigned B-tree key prefixes for different tables
Prefix uint32
}
// get a single row by the primary key
func dbGet(db *DB, tdef *TableDef, rec *Record) (bool, error) {
values, err := checkRecord(tdef, *rec, tdef.PKeys)
if err != nil {
return false, err
}
key := encodeKey(nil, tdef.Prefix, values[:tdef.PKeys])
val, ok := db.kv.Get(key)
if !ok {
return false, nil
}
for i := tdef.PKeys; i < len(tdef.Cols); i++ {
values[i].Type = tdef.Types[i]
}
decodeValues(val, values[tdef.PKeys:])
rec.Cols = append(rec.Cols, tdef.Cols[tdef.PKeys:]...)
rec.Vals = append(rec.Vals, values[tdef.PKeys:]...)
return true, nil
}
// reorder a record and check for missing columns.
// n == tdef.PKeys: record is exactly a primary key
// n == len(tdef.Cols): record contains all columns
func checkRecord(tdef *TableDef, rec Record, n int) ([]Value, error) {
// omitted...
}
func encodeValues(out []byte, vals []Value) []byte
func decodeValues(in []byte, out []Value)
// for primary keys
func encodeKey(out []byte, prefix uint32, vals []Value) []byte {
var buf [4]byte
binary.BigEndian.PutUint32(buf[:], prefix)
out = append(out, buf[:]...)
out = encodeValues(out, vals)
return out
}
// get a single row by the primary key
func (db *DB) Get(table string, rec *Record) (bool, error) {
tdef := getTableDef(db, table)
if tdef == nil {
return false, fmt.Errorf("table not found: %s", table)
}
return dbGet(db, tdef, rec)
}
// get the table definition by name
func getTableDef(db *DB, name string) *TableDef {
tdef, ok := db.tables[name]
if !ok {
if db.tables == nil {
db.tables = map[string]*TableDef{}
}
tdef = getTableDefDB(db, name)
if tdef != nil {
db.tables[name] = tdef
}
}
return tdef
}
func getTableDefDB(db *DB, name string) *TableDef {
rec := (&Record{}).AddStr("name", []byte(name))
ok, err := dbGet(db, TDEF_TABLE, rec)
assert(err == nil)
if !ok {
return nil
}
tdef := &TableDef{}
err = json.Unmarshal(rec.Get("def").Str, tdef)
assert(err == nil)
return tdef
}
// modes of the updates
const (
MODE_UPSERT = 0 // insert or replace
MODE_UPDATE_ONLY = 1 // update existing keys
MODE_INSERT_ONLY = 2 // only add new keys
)
type InsertReq struct {
tree *BTree
// out
Added bool // added a new key
// in
Key []byte
Val []byte
Mode int
}
func (tree *BTree) InsertEx(req *InsertReq)
func (db *KV) Update(key []byte, val []byte, mode int) (bool, error)
// add a row to the table
func dbUpdate(db *DB, tdef *TableDef, rec Record, mode int) (bool, error) {
values, err := checkRecord(tdef, rec, len(tdef.Cols))
if err != nil {
return false, err
}
key := encodeKey(nil, tdef.Prefix, values[:tdef.PKeys])
val := encodeValues(nil, values[tdef.PKeys:])
return db.kv.Update(key, val, mode)
}