Clean up API.
Ben Johnson
10 years ago
0 | 0 | package bolt |
1 | 1 | |
2 | const Version = 1 | |
2 | const version = 1 | |
3 | 3 | |
4 | 4 | const ( |
5 | MaxKeySize = 0x8000 | |
6 | MaxDataSize = 0xffffffff | |
5 | MaxBucketNameSize = 255 | |
6 | MaxKeySize = 32768 | |
7 | MaxDataSize = 4294967295 | |
7 | 8 | ) |
12 | 12 | ) |
13 | 13 | |
14 | 14 | const minPageSize = 0x1000 |
15 | ||
16 | var ( | |
17 | DatabaseNotOpenError = &Error{"db is not open", nil} | |
18 | DatabaseAlreadyOpenedError = &Error{"db already open", nil} | |
19 | TransactionInProgressError = &Error{"writable transaction is already in progress", nil} | |
20 | ) | |
21 | 15 | |
22 | 16 | type DB struct { |
23 | 17 | sync.Mutex |
156 | 150 | // Initialize the meta page. |
157 | 151 | m := p.meta() |
158 | 152 | m.magic = magic |
159 | m.version = Version | |
153 | m.version = version | |
160 | 154 | m.pageSize = uint32(db.pageSize) |
161 | m.version = Version | |
155 | m.version = version | |
162 | 156 | m.free = 2 |
163 | 157 | m.sys = 3 |
164 | 158 | m.pgid = 4 |
238 | 232 | return t, nil |
239 | 233 | } |
240 | 234 | |
235 | // Bucket retrieves a reference to a bucket. | |
236 | func (db *DB) Bucket(name string) (*Bucket, error) { | |
237 | t, err := db.Transaction() | |
238 | if err != nil { | |
239 | return nil, err | |
240 | } | |
241 | defer t.Close() | |
242 | return t.Bucket(name), nil | |
243 | } | |
244 | ||
245 | // Buckets retrieves a list of all buckets in the database. | |
246 | func (db *DB) Buckets() ([]*Bucket, error) { | |
247 | t, err := db.Transaction() | |
248 | if err != nil { | |
249 | return nil, err | |
250 | } | |
251 | defer t.Close() | |
252 | return t.Buckets(), nil | |
253 | } | |
254 | ||
255 | // CreateBucket creates a new bucket in the database. | |
256 | func (db *DB) CreateBucket(name string) error { | |
257 | t, err := db.RWTransaction() | |
258 | if err != nil { | |
259 | return err | |
260 | } | |
261 | ||
262 | if err := t.CreateBucket(name); err != nil { | |
263 | t.Rollback() | |
264 | return err | |
265 | } | |
266 | ||
267 | return t.Commit() | |
268 | } | |
269 | ||
270 | // DeleteBucket removes a bucket from the database. | |
271 | func (db *DB) DeleteBucket(name string) error { | |
272 | t, err := db.RWTransaction() | |
273 | if err != nil { | |
274 | return err | |
275 | } | |
276 | ||
277 | if err := t.DeleteBucket(name); err != nil { | |
278 | t.Rollback() | |
279 | return err | |
280 | } | |
281 | ||
282 | return t.Commit() | |
283 | } | |
284 | ||
285 | // Get retrieves the value for a key in a bucket. | |
286 | func (db *DB) Get(name string, key []byte) ([]byte, error) { | |
287 | t, err := db.Transaction() | |
288 | if err != nil { | |
289 | return nil, err | |
290 | } | |
291 | defer t.Close() | |
292 | return t.Get(name, key), nil | |
293 | } | |
294 | ||
295 | // Put sets the value for a key in a bucket. | |
296 | func (db *DB) Put(name string, key []byte, value []byte) error { | |
297 | t, err := db.RWTransaction() | |
298 | if err != nil { | |
299 | return err | |
300 | } | |
301 | if err := t.Put(name, key, value); err != nil { | |
302 | t.Rollback() | |
303 | return err | |
304 | } | |
305 | return t.Commit() | |
306 | } | |
307 | ||
308 | // Delete removes a key from a bucket. | |
309 | func (db *DB) Delete(name string, key []byte) error { | |
310 | t, err := db.RWTransaction() | |
311 | if err != nil { | |
312 | return err | |
313 | } | |
314 | if err := t.Delete(name, key); err != nil { | |
315 | t.Rollback() | |
316 | return err | |
317 | } | |
318 | return t.Commit() | |
319 | } | |
320 | ||
241 | 321 | // page retrieves a page reference from the mmap based on the current page size. |
242 | 322 | func (db *DB) page(id pgid) *page { |
243 | 323 | return (*page)(unsafe.Pointer(&db.data[id*pgid(db.pageSize)])) |
122 | 122 | withMockDB(func(db *DB, mockos *mockos, mocksyscall *mocksyscall, path string) { |
123 | 123 | var m meta |
124 | 124 | m.magic = magic |
125 | m.version = Version | |
125 | m.version = version | |
126 | 126 | m.pageSize = 0x8000 |
127 | 127 | |
128 | 128 | // Create a file with bad magic. |
129 | 129 | b := make([]byte, 0x10000) |
130 | 130 | p0, p1 := (*page)(unsafe.Pointer(&b[0x0000])), (*page)(unsafe.Pointer(&b[0x8000])) |
131 | 131 | p0.meta().magic = 0 |
132 | p0.meta().version = Version | |
132 | p0.meta().version = version | |
133 | 133 | p1.meta().magic = magic |
134 | p1.meta().version = Version | |
134 | p1.meta().version = version | |
135 | 135 | |
136 | 136 | // Mock file access. |
137 | 137 | file, metafile := &mockfile{}, &mockfile{} |
0 | 0 | package bolt |
1 | ||
2 | var ( | |
3 | InvalidError = &Error{"Invalid database", nil} | |
4 | VersionMismatchError = &Error{"version mismatch", nil} | |
5 | DatabaseNotOpenError = &Error{"db is not open", nil} | |
6 | DatabaseAlreadyOpenedError = &Error{"db already open", nil} | |
7 | TransactionInProgressError = &Error{"writable transaction is already in progress", nil} | |
8 | InvalidTransactionError = &Error{"txn is invalid", nil} | |
9 | BucketAlreadyExistsError = &Error{"bucket already exists", nil} | |
10 | ) | |
1 | 11 | |
2 | 12 | type Error struct { |
3 | 13 | message string |
0 | 0 | package bolt |
1 | 1 | |
2 | var ( | |
3 | InvalidError = &Error{"Invalid database", nil} | |
4 | VersionMismatchError = &Error{"version mismatch", nil} | |
5 | ) | |
6 | ||
7 | 2 | const magic uint32 = 0xDEADC0DE |
8 | const version uint32 = 1 | |
9 | 3 | |
10 | 4 | type meta struct { |
11 | 5 | magic uint32 |
21 | 15 | func (m *meta) validate() error { |
22 | 16 | if m.magic != magic { |
23 | 17 | return InvalidError |
24 | } else if m.version != Version { | |
18 | } else if m.version != version { | |
25 | 19 | return VersionMismatchError |
26 | 20 | } |
27 | 21 | return nil |
78 | 78 | return nil |
79 | 79 | } |
80 | 80 | |
81 | func (t *RWTransaction) Delete(key []byte) error { | |
81 | func (t *RWTransaction) Delete(name string, key []byte) error { | |
82 | 82 | // TODO: Traverse to the correct node. |
83 | 83 | // TODO: If missing, exit. |
84 | 84 | // TODO: Remove node from page. |
115 | 115 | return nil |
116 | 116 | } |
117 | 117 | |
118 | func (t *RWTransaction) Rollback() error { | |
119 | return t.close() | |
120 | } | |
121 | ||
122 | func (t *RWTransaction) close() error { | |
118 | func (t *RWTransaction) Rollback() { | |
119 | t.close() | |
120 | } | |
121 | ||
122 | func (t *RWTransaction) close() { | |
123 | 123 | // Clear temporary pages. |
124 | 124 | t.leafs = nil |
125 | 125 | |
126 | 126 | // TODO: Release writer lock. |
127 | ||
128 | return nil | |
129 | 127 | } |
130 | 128 | |
131 | 129 | // allocate returns a contiguous block of memory starting at a given page. |
18 | 18 | func TestTransactionCreateBucket(t *testing.T) { |
19 | 19 | withOpenDB(func(db *DB, path string) { |
20 | 20 | // Create a bucket. |
21 | txn, _ := db.RWTransaction() | |
22 | err := txn.CreateBucket("widgets") | |
21 | err := db.CreateBucket("widgets") | |
23 | 22 | assert.NoError(t, err) |
24 | 23 | |
25 | // Commit the transaction. | |
26 | err = txn.Commit() | |
24 | // Read the bucket through a separate transaction. | |
25 | b, err := db.Bucket("widgets") | |
26 | assert.NotNil(t, b) | |
27 | 27 | assert.NoError(t, err) |
28 | ||
29 | // Open a separate read-only transaction. | |
30 | rtxn, err := db.Transaction() | |
31 | assert.NotNil(t, txn) | |
32 | assert.NoError(t, err) | |
33 | ||
34 | b := rtxn.Bucket("widgets") | |
35 | assert.NoError(t, err) | |
36 | if assert.NotNil(t, b) { | |
37 | assert.Equal(t, b.Name(), "widgets") | |
38 | } | |
39 | 28 | }) |
40 | 29 | } |
41 | ||
42 | // Ensure that an existing bucket cannot be created. | |
43 | func TestTransactionCreateExistingBucket(t *testing.T) { | |
44 | t.Skip("pending") | |
45 | } |
0 | 0 | package bolt |
1 | ||
2 | var ( | |
3 | InvalidTransactionError = &Error{"txn is invalid", nil} | |
4 | BucketAlreadyExistsError = &Error{"bucket already exists", nil} | |
5 | ) | |
6 | 1 | |
7 | 2 | const ( |
8 | 3 | ps_modify = 1 |
31 | 26 | t.sys.read(t.page(t.meta.sys)) |
32 | 27 | } |
33 | 28 | |
34 | func (t *Transaction) Close() error { | |
29 | func (t *Transaction) Close() { | |
35 | 30 | // TODO: Close buckets. |
36 | return nil | |
37 | 31 | } |
38 | 32 | |
39 | 33 | func (t *Transaction) DB() *DB { |
55 | 49 | } |
56 | 50 | } |
57 | 51 | |
52 | // Buckets retrieves a list of all buckets. | |
53 | func (t *Transaction) Buckets() []*Bucket { | |
54 | warn("[pending] Transaction.Buckets()") // TODO | |
55 | return nil | |
56 | } | |
57 | ||
58 | 58 | // Cursor creates a cursor associated with a given bucket. |
59 | 59 | func (t *Transaction) Cursor(name string) *Cursor { |
60 | 60 | b := t.Bucket(name) |
73 | 73 | return c.Get(key) |
74 | 74 | } |
75 | 75 | |
76 | // Stat returns information about a bucket's internal structure. | |
77 | func (t *Transaction) Stat(name string) *Stat { | |
76 | // stat returns information about a bucket's internal structure. | |
77 | func (t *Transaction) stat(name string) *Stat { | |
78 | 78 | // TODO |
79 | 79 | return nil |
80 | 80 | } |