-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
register.go
150 lines (128 loc) · 3.67 KB
/
register.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
package ds
import (
"encoding/gob"
"fmt"
"os"
"reflect"
"sync"
"github.com/ecnepsnai/logtic"
"go.etcd.io/bbolt"
)
// The current version of the DS schema. This is not currently used but is reserved for future
// use when we may need to change how we store data in tables from older versions of DS.
var currentDSSchemaVersion = 1
// Register will register an instance of a struct with ds, creating a table (or opening an existing table) for this type
// at the specified file path.
func Register(o interface{}, filePath string, options *Options) (*Table, error) {
typeOf := reflect.TypeOf(o)
if typeOf.Kind() == reflect.Ptr {
return nil, fmt.Errorf("refusing to register table to a pointer")
}
// Gob will panic if you register the same object twice.
// I think this is stupid, so we will recover from this if
// registering the type panics.
registerGobType(o)
registerGobType(Options{})
registerGobType(Config{})
table := Table{
Name: typeOf.Name(),
typeOf: typeOf,
log: logtic.Connect("ds(" + typeOf.Name() + ")"),
lock: &sync.RWMutex{},
}
if options != nil {
table.options = *options
}
primaryKey, indexes, uniques, err := table.getKeysFromFields()
if err != nil {
return nil, err
}
table.primaryKey = primaryKey
table.indexes = indexes
table.uniques = uniques
if err := checkForExistingBoltTable(filePath); err != nil {
table.log.Error("Existing file '%s' that is not recognized as a DS table", filePath)
return nil, fmt.Errorf("bad table file")
}
force := false
if options != nil {
force = options.force
}
if err := table.createTableBuckets(filePath, force); err != nil {
return nil, err
}
table.log.Info("Datastore '%s' opened at '%s'", table.Name, filePath)
return &table, nil
}
func (table *Table) createTableBuckets(filePath string, force bool) error {
data, err := bbolt.Open(filePath, os.ModePerm, nil)
if err != nil {
table.log.Error("Error opening bolt database: %s", err.Error())
return err
}
table.data = data
err = data.Update(func(tx *bbolt.Tx) error {
if err := table.initializeConfig(tx, force); err != nil {
table.log.Error("Error initializing config: %s", err.Error())
return err
}
if _, err = tx.CreateBucketIfNotExists(dataKey); err != nil {
table.log.Error("Error creating bucket '%s: %s", "data", err.Error())
return err
}
if !table.options.DisableSorting {
if _, err = tx.CreateBucketIfNotExists(insertOrderKey); err != nil {
table.log.Error("Error creating bucket '%s: %s", insertOrderKey, err.Error())
return err
}
}
for _, index := range table.indexes {
if _, err = tx.CreateBucketIfNotExists([]byte(indexPrefix + index)); err != nil {
table.log.Error("Error creating bucket '%s: %s", indexPrefix+index, err.Error())
return err
}
}
for _, unique := range table.uniques {
if _, err = tx.CreateBucketIfNotExists([]byte(uniquePrefix + unique)); err != nil {
table.log.Error("Error creating bucket '%s: %s", uniquePrefix+unique, err.Error())
return err
}
}
return nil
})
if err != nil {
table.log.Error("Error preparing bolt database: %s", err.Error())
data.Close()
return err
}
return nil
}
func checkForExistingBoltTable(filePath string) error {
if _, err := os.Stat(filePath); err != nil {
return nil
}
db, err := bbolt.Open(filePath, os.ModePerm, nil)
if err != nil {
return err
}
defer db.Close()
err = db.View(func(tx *bbolt.Tx) error {
defer recover()
bucket := tx.Bucket(configKey)
if bucket == nil {
return fmt.Errorf("no bucket found")
}
return nil
})
if err != nil {
return err
}
return nil
}
func registerGobType(o interface{}) {
defer panicRecovery()
gob.Register(o)
}
func panicRecovery() {
recover()
}