Codebase list golang-github-boltdb-bolt / 8ad59ed
API Documentation. Ben Johnson 10 years ago
19 changed file(s) with 249 addition(s) and 113 deletion(s). Raw diff Collapse all Expand all
00 package bolt
11
2 // Bucket represents a collection of key/value pairs inside the database.
3 // All keys inside the bucket are unique. The Bucket type is not typically used
4 // directly. Instead the bucket name is typically passed into the Get(), Put(),
5 // or Delete() functions.
26 type Bucket struct {
37 *bucket
48 name string
59 transaction *Transaction
610 }
711
12 // bucket represents the on-file representation of a bucket.
813 type bucket struct {
914 root pgid
1015 }
1212
1313 // size returns the size of the page after serialization.
1414 func (b *buckets) size() int {
15 var size int = pageHeaderSize
16 for key, _ := range b.items {
15 var size = pageHeaderSize
16 for key := range b.items {
1717 size += int(unsafe.Sizeof(bucket{})) + len(key)
1818 }
1919 return size
6969 // write writes the items onto a page.
7070 func (b *buckets) write(p *page) {
7171 // Initialize page.
72 p.flags |= p_buckets
72 p.flags |= bucketsPageFlag
7373 p.count = uint16(len(b.items))
7474
7575 // Sort keys.
7676 var keys []string
77 for key, _ := range b.items {
77 for key := range b.items {
7878 keys = append(keys, key)
7979 }
8080 sort.StringSlice(keys).Sort()
22 const version = 1
33
44 const (
5 // MaxBucketNameSize is the maximum length of a bucket name, in bytes.
56 MaxBucketNameSize = 255
6 MaxKeySize = 32768
7 MaxDataSize = 4294967295
7
8 // MaxKeySize is the maximum length of a key, in bytes.
9 MaxKeySize = 32768
10
11 // MaxValueSize is the maximum length of a value, in bytes.
12 MaxValueSize = 4294967295
813 )
44 "sort"
55 )
66
7 // Cursor represents an iterator that can traverse over all key/value pairs in a bucket in sorted order.
8 // Cursors can be obtained from a Transaction and are valid as long as the Transaction is open.
79 type Cursor struct {
810 transaction *Transaction
911 root pgid
1012 stack []pageElementRef
1113 }
1214
13 // First moves the cursor to the first item in the bucket and returns its key and data.
14 func (c *Cursor) First() ([]byte, []byte) {
15 // First moves the cursor to the first item in the bucket and returns its key and value.
16 // If the bucket is empty then a nil key is returned.
17 func (c *Cursor) First() (key []byte, value []byte) {
1518 if len(c.stack) > 0 {
1619 c.stack = c.stack[:0]
1720 }
2023 return c.keyValue()
2124 }
2225
23 // Move the cursor to the next key/value.
24 func (c *Cursor) Next() ([]byte, []byte) {
26 // Next moves the cursor to the next item in the bucket and returns its key and value.
27 // If the cursor is at the end of the bucket then a nil key returned.
28 func (c *Cursor) Next() (key []byte, value []byte) {
2529 // Attempt to move over one element until we're successful.
2630 // Move up the stack as we hit the end of each page in our stack.
2731 for i := len(c.stack) - 1; i >= 0; i-- {
4347 return c.keyValue()
4448 }
4549
46 // Get positions the cursor at a specific key and returns the its value.
47 func (c *Cursor) Get(key []byte) []byte {
50 // Get moves the cursor to a given key and returns its value.
51 // If the key does not exist then the cursor is left at the closest key and a nil key is returned.
52 func (c *Cursor) Get(key []byte) (value []byte) {
4853 // Start from root page and traverse to correct page.
4954 c.stack = c.stack[:0]
5055 c.search(key, c.transaction.page(c.root))
6368 return c.element().value()
6469 }
6570
66 // first moves the cursor to the first leaf element under a page.
71 // first moves the cursor to the first leaf element under the last page in the stack.
6772 func (c *Cursor) first() {
6873 p := c.stack[len(c.stack)-1].page
6974 for {
7075 // Exit when we hit a leaf page.
71 if (p.flags & p_leaf) != 0 {
76 if (p.flags & leafPageFlag) != 0 {
7277 break
7378 }
7479
7883 }
7984 }
8085
86 // search recursively performs a binary search against a given page until it finds a given key.
8187 func (c *Cursor) search(key []byte, p *page) {
82 _assert((p.flags&(p_branch|p_leaf)) != 0, "invalid page type: "+p.typ())
88 _assert((p.flags&(branchPageFlag|leafPageFlag)) != 0, "invalid page type: "+p.typ())
8389 e := pageElementRef{page: p}
8490 c.stack = append(c.stack, e)
8591
8692 // If we're on a leaf page then find the specific node.
87 if (p.flags & p_leaf) != 0 {
93 if (p.flags & leafPageFlag) != 0 {
8894 c.nsearch(key, p)
8995 return
9096 }
+31
-16
db.go less more
77 "unsafe"
88 )
99
10 const (
11 db_nosync = iota
12 db_nometasync
13 )
14
15 const minPageSize = 0x1000
16
10 // The smallest size that the mmap can be.
1711 const minMmapSize = 1 << 22 // 4MB
12
13 // The largest step that can be taken when remapping the mmap.
1814 const maxMmapStep = 1 << 30 // 1GB
1915
16 // DB represents a collection of buckets persisted to a file on disk.
17 // All data access is performed through transactions which can be obtained through the DB.
18 // All the functions on DB will return a DatabaseNotOpenError if accessed before Open() is called.
2019 type DB struct {
2120 os _os
2221 syscall _syscall
6564
6665 // Exit if the database is currently open.
6766 if db.opened {
68 return DatabaseAlreadyOpenedError
67 return DatabaseOpenError
6968 }
7069
7170 // Open data file and separate sync handler for metadata writes.
8988 }
9089 } else {
9190 // Read the first meta page to determine the page size.
92 var buf [minPageSize]byte
91 var buf [0x1000]byte
9392 if _, err := db.file.ReadAt(buf[:], 0); err == nil {
9493 m := db.pageInBuffer(buf[:], 0).meta()
9594 if err := m.validate(); err != nil {
201200 for i := 0; i < 2; i++ {
202201 p := db.pageInBuffer(buf[:], pgid(i))
203202 p.id = pgid(i)
204 p.flags = p_meta
203 p.flags = metaPageFlag
205204
206205 // Initialize the meta page.
207206 m := p.meta()
218217 // Write an empty freelist at page 3.
219218 p := db.pageInBuffer(buf[:], pgid(2))
220219 p.id = pgid(2)
221 p.flags = p_freelist
220 p.flags = freelistPageFlag
222221 p.count = 0
223222
224223 // Write an empty leaf page at page 4.
225224 p = db.pageInBuffer(buf[:], pgid(3))
226225 p.id = pgid(3)
227 p.flags = p_buckets
226 p.flags = bucketsPageFlag
228227 p.count = 0
229228
230229 // Write the buffer to our data file.
235234 return nil
236235 }
237236
238 // Close releases all resources related to the database.
237 // Close releases all database resources.
238 // All transactions must be closed before closing the database.
239239 func (db *DB) Close() {
240240 db.metalock.Lock()
241241 defer db.metalock.Unlock()
249249
250250 // TODO(benbjohnson): Undo everything in Open().
251251 db.freelist = nil
252 db.path = ""
252253
253254 db.munmap()
254255 }
255256
256257 // Transaction creates a read-only transaction.
257258 // Multiple read-only transactions can be used concurrently.
259 //
260 // IMPORTANT: You must close the transaction after you are finished or else the database will not reclaim old pages.
258261 func (db *DB) Transaction() (*Transaction, error) {
259262 db.metalock.Lock()
260263 defer db.metalock.Unlock()
281284
282285 // RWTransaction creates a read/write transaction.
283286 // Only one read/write transaction is allowed at a time.
287 // You must call Commit() or Rollback() on the transaction to close it.
284288 func (db *DB) RWTransaction() (*RWTransaction, error) {
285289 db.metalock.Lock()
286290 defer db.metalock.Unlock()
331335 }
332336
333337 // Bucket retrieves a reference to a bucket.
338 // This is typically useful for checking the existence of a bucket.
334339 func (db *DB) Bucket(name string) (*Bucket, error) {
335340 t, err := db.Transaction()
336341 if err != nil {
350355 return t.Buckets(), nil
351356 }
352357
353 // CreateBucket creates a new bucket in the database.
358 // CreateBucket creates a new bucket with the given name.
359 // This function can return an error if the bucket already exists, if the name
360 // is blank, or the bucket name is too long.
354361 func (db *DB) CreateBucket(name string) error {
355362 t, err := db.RWTransaction()
356363 if err != nil {
366373 }
367374
368375 // DeleteBucket removes a bucket from the database.
376 // Returns an error if the bucket does not exist.
369377 func (db *DB) DeleteBucket(name string) error {
370378 t, err := db.RWTransaction()
371379 if err != nil {
381389 }
382390
383391 // Get retrieves the value for a key in a bucket.
392 // Returns an error if the key does not exist.
384393 func (db *DB) Get(name string, key []byte) ([]byte, error) {
385394 t, err := db.Transaction()
386395 if err != nil {
387396 return nil, err
388397 }
389398 defer t.Close()
390 return t.Get(name, key), nil
399 return t.Get(name, key)
391400 }
392401
393402 // Put sets the value for a key in a bucket.
403 // Returns an error if the bucket is not found, if key is blank, if the key is too large, or if the value is too large.
394404 func (db *DB) Put(name string, key []byte, value []byte) error {
395405 t, err := db.RWTransaction()
396406 if err != nil {
404414 }
405415
406416 // Delete removes a key from a bucket.
417 // Returns an error if the bucket cannot be found.
407418 func (db *DB) Delete(name string, key []byte) error {
408419 t, err := db.RWTransaction()
409420 if err != nil {
417428 }
418429
419430 // Copy writes the entire database to a writer.
431 // A reader transaction is maintained during the copy so it is safe to continue
432 // using the database while a copy is in progress.
420433 func (db *DB) Copy(w io.Writer) error {
421434 if !db.opened {
422435 return DatabaseNotOpenError
444457 }
445458
446459 // CopyFile copies the entire database to file at the given path.
460 // A reader transaction is maintained during the copy so it is safe to continue
461 // using the database while a copy is in progress.
447462 func (db *DB) CopyFile(path string) error {
448463 f, err := os.Create(path)
449464 if err != nil {
502517 // sync flushes the file descriptor to disk.
503518 func (db *DB) sync(force bool) error {
504519 if db.opened {
505 return DatabaseAlreadyOpenedError
520 return DatabaseNotOpenError
506521 }
507522 if err := syscall.Fsync(int(db.file.Fd())); err != nil {
508523 return err
2626 withDB(func(db *DB, path string) {
2727 db.Open(path, 0666)
2828 err := db.Open(path, 0666)
29 assert.Equal(t, err, DatabaseAlreadyOpenedError)
29 assert.Equal(t, err, DatabaseOpenError)
3030 })
3131 }
3232
0 /*
1 Package bolt implements a low-level key/value store in pure Go. It supports
2 fully serializable transactions, ACID semantics, and lock-free MVCC with
3 multiple readers and a single writer. Bolt can be used for projects that
4 want a simple data store without the need to add large dependencies such as
5 Postgres or MySQL.
6
7 Bolt is a single-level, zero-copy, B+tree data store. This means that Bolt is
8 optimized for fast read access and does not require recovery in the event of a
9 system crash. Transactions which have not finished committing will simply be
10 rolled back in the event of a crash.
11
12 The design of Bolt is based on Howard Chu's LMDB database project.
13
14 Basics
15
16 There are only a few types in Bolt: DB, Bucket, Transaction, RWTransaction, and
17 Cursor. The DB is a collection of buckets and is represented by a single file
18 on disk. A bucket is a collection of unique keys that are associated with values.
19
20 Transactions provide read-only access to data inside the database. They can
21 retrieve key/value pairs and can use Cursors to iterate over the entire dataset.
22 RWTransactions provide read-write access to the database. They can create and
23 delete buckets and they can insert and remove keys. Only one RWTransaction is
24 allowed at a time.
25
26
27 Caveats
28
29 The database uses a read-only, memory-mapped data file to ensure that
30 applications cannot corrupt the database, however, this means that keys and
31 values returned from Bolt cannot be changed. Writing to a read-only byte slice
32 will cause Go to panic. If you need to work with data returned from a Get() you
33 need to first copy it to a new byte slice.
34
35 Bolt currently works on Mac OS and Linux. Windows support is coming soon.
36
37 */
038 package bolt
1
2 // TODO(benbjohnson)
00 package bolt
11
22 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}
3 // InvalidError is returned when a data file is not a Bolt-formatted database.
4 InvalidError = &Error{"Invalid database", nil}
5
6 // VersionMismatchError is returned when the data file was created with a
7 // different version of Bolt.
8 VersionMismatchError = &Error{"version mismatch", nil}
9
10 // DatabaseNotOpenError is returned when a DB instance is accessed before it
11 // is opened or after it is closed.
12 DatabaseNotOpenError = &Error{"database not open", nil}
13
14 // DatabaseOpenError is returned when opening a database that is
15 // already open.
16 DatabaseOpenError = &Error{"database already open", nil}
17
18 // BucketNotFoundError is returned when trying to access a bucket that has
19 // not been created yet.
20 BucketNotFoundError = &Error{"bucket not found", nil}
21
22 // BucketExistsError is returned when creating a bucket that already exists.
23 BucketExistsError = &Error{"bucket already exists", nil}
24
25 // BucketNameRequiredError is returned when creating a bucket with a blank name.
26 BucketNameRequiredError = &Error{"bucket name required", nil}
27
28 // BucketNameTooLargeError is returned when creating a bucket with a name
29 // that is longer than MaxBucketNameSize.
30 BucketNameTooLargeError = &Error{"bucket name too large", nil}
31
32 // KeyRequiredError is returned when inserting a zero-length key.
33 KeyRequiredError = &Error{"key required", nil}
34
35 // KeyTooLargeError is returned when inserting a key that is larger than MaxKeySize.
36 KeyTooLargeError = &Error{"key too large", nil}
37
38 // ValueTooLargeError is returned when inserting a value that is larger than MaxValueSize.
39 ValueTooLargeError = &Error{"value too large", nil}
1040 )
1141
42 // Error represents an error condition caused by Bolt.
1243 type Error struct {
1344 message string
1445 cause error
1546 }
1647
48 // Error returns a string representation of the error.
1749 func (e *Error) Error() string {
1850 if e.cause != nil {
1951 return e.message + ": " + e.cause.Error()
2828 // If a contiguous block cannot be found then 0 is returned.
2929 func (f *freelist) allocate(n int) pgid {
3030 var count int
31 var previd pgid = 0
31 var previd pgid
3232 for i, id := range f.ids {
3333 // Reset count if this is not contiguous.
3434 if previd == 0 || previd-id != 1 {
8181 // become free.
8282 func (f *freelist) write(p *page) {
8383 ids := f.all()
84 p.flags |= p_freelist
84 p.flags |= freelistPageFlag
8585 p.count = uint16(len(ids))
8686 copy(((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[:], ids)
8787 }
5151 // Create a page.
5252 var buf [4096]byte
5353 page := (*page)(unsafe.Pointer(&buf[0]))
54 page.flags = p_freelist
54 page.flags = freelistPageFlag
5555 page.count = 2
5656
5757 // Insert 2 page ids.
3737 func (m *meta) write(p *page) {
3838 // Page id is either going to be 0 or 1 which we can determine by the Txn ID.
3939 p.id = pgid(m.txnid % 2)
40 p.flags |= p_meta
40 p.flags |= metaPageFlag
4141
4242 m.copy(p.meta())
4343 }
2727
2828 // size returns the size of the node after serialization.
2929 func (n *node) size() int {
30 var elementSize int = n.pageElementSize()
31
32 var size int = pageHeaderSize
30 var elementSize = n.pageElementSize()
31
32 var size = pageHeaderSize
3333 for _, item := range n.inodes {
3434 size += elementSize + len(item.key) + len(item.value)
3535 }
131131 // read initializes the node from a page.
132132 func (n *node) read(p *page) {
133133 n.pgid = p.id
134 n.isLeaf = ((p.flags & p_leaf) != 0)
134 n.isLeaf = ((p.flags & leafPageFlag) != 0)
135135 n.inodes = make(inodes, int(p.count))
136136
137137 for i := 0; i < int(p.count); i++ {
159159 func (n *node) write(p *page) {
160160 // Initialize page.
161161 if n.isLeaf {
162 p.flags |= p_leaf
162 p.flags |= leafPageFlag
163163 } else {
164 p.flags |= p_branch
164 p.flags |= branchPageFlag
165165 }
166166 p.count = uint16(len(n.inodes))
167167
343343 copy(key, n.key)
344344 n.key = key
345345
346 for i, _ := range n.inodes {
346 for i := range n.inodes {
347347 inode := &n.inodes[i]
348348
349349 key := make([]byte, len(inode.key))
2727 // Create a page.
2828 var buf [4096]byte
2929 page := (*page)(unsafe.Pointer(&buf[0]))
30 page.flags = p_leaf
30 page.flags = leafPageFlag
3131 page.count = 2
3232
3333 // Insert 2 elements at the beginning. sizeof(leafPageElement) == 16
1515 const leafPageElementSize = int(unsafe.Sizeof(leafPageElement{}))
1616
1717 const (
18 p_branch = 0x01
19 p_leaf = 0x02
20 p_meta = 0x04
21 p_buckets = 0x08
22 p_freelist = 0x10
18 branchPageFlag = 0x01
19 leafPageFlag = 0x02
20 metaPageFlag = 0x04
21 bucketsPageFlag = 0x08
22 freelistPageFlag = 0x10
2323 )
2424
2525 type pgid uint64
4040
4141 // typ returns a human readable page type string used for debugging.
4242 func (p *page) typ() string {
43 if (p.flags & p_branch) != 0 {
43 if (p.flags & branchPageFlag) != 0 {
4444 return "branch"
45 } else if (p.flags & p_leaf) != 0 {
45 } else if (p.flags & leafPageFlag) != 0 {
4646 return "leaf"
47 } else if (p.flags & p_meta) != 0 {
47 } else if (p.flags & metaPageFlag) != 0 {
4848 return "meta"
49 } else if (p.flags & p_buckets) != 0 {
49 } else if (p.flags & bucketsPageFlag) != 0 {
5050 return "buckets"
51 } else if (p.flags & p_freelist) != 0 {
51 } else if (p.flags & freelistPageFlag) != 0 {
5252 return "freelist"
5353 }
5454 return fmt.Sprintf("unknown<%02x>", p.flags)
66
77 // Ensure that the page type can be returned in human readable format.
88 func TestPageTyp(t *testing.T) {
9 assert.Equal(t, (&page{flags: p_branch}).typ(), "branch")
10 assert.Equal(t, (&page{flags: p_leaf}).typ(), "leaf")
11 assert.Equal(t, (&page{flags: p_meta}).typ(), "meta")
12 assert.Equal(t, (&page{flags: p_buckets}).typ(), "buckets")
13 assert.Equal(t, (&page{flags: p_freelist}).typ(), "freelist")
9 assert.Equal(t, (&page{flags: branchPageFlag}).typ(), "branch")
10 assert.Equal(t, (&page{flags: leafPageFlag}).typ(), "leaf")
11 assert.Equal(t, (&page{flags: metaPageFlag}).typ(), "meta")
12 assert.Equal(t, (&page{flags: bucketsPageFlag}).typ(), "buckets")
13 assert.Equal(t, (&page{flags: freelistPageFlag}).typ(), "freelist")
1414 assert.Equal(t, (&page{flags: 20000}).typ(), "unknown<4e20>")
1515 }
1616
55 )
66
77 // RWTransaction represents a transaction that can read and write data.
8 // Only one read/write transaction can be active for a DB at a time.
8 // Only one read/write transaction can be active for a database at a time.
9 // RWTransaction is composed of a read-only Transaction so it can also use
10 // functions provided by Transaction.
911 type RWTransaction struct {
1012 Transaction
1113 nodes map[pgid]*node
2426 }
2527
2628 // CreateBucket creates a new bucket.
29 // Returns an error if the bucket already exists, if the bucket name is blank, or if the bucket name is too long.
2730 func (t *RWTransaction) CreateBucket(name string) error {
2831 // Check if bucket already exists.
2932 if b := t.Bucket(name); b != nil {
30 return &Error{"bucket already exists", nil}
33 return BucketExistsError
3134 } else if len(name) == 0 {
32 return &Error{"bucket name cannot be blank", nil}
35 return BucketNameRequiredError
3336 } else if len(name) > MaxBucketNameSize {
34 return &Error{"bucket name too long", nil}
37 return BucketNameTooLargeError
3538 }
3639
3740 // Create a blank root leaf page.
3942 if err != nil {
4043 return err
4144 }
42 p.flags = p_leaf
45 p.flags = leafPageFlag
4346
4447 // Add bucket to buckets page.
4548 t.buckets.put(name, &bucket{root: p.id})
4750 return nil
4851 }
4952
50 // DropBucket deletes a bucket.
53 // DeleteBucket deletes a bucket.
54 // Returns an error if the bucket cannot be found.
5155 func (t *RWTransaction) DeleteBucket(name string) error {
56 if b := t.Bucket(name); b == nil {
57 return BucketNotFoundError
58 }
59
5260 // Remove from buckets page.
5361 t.buckets.del(name)
5462
5563 // TODO(benbjohnson): Free all pages.
56 return nil
57 }
58
64
65 return nil
66 }
67
68 // Put sets the value for a key inside of the named bucket.
69 // If the key exist then its previous value will be overwritten.
70 // Returns an error if the bucket is not found, if the key is blank, if the key is too large, or if the value is too large.
5971 func (t *RWTransaction) Put(name string, key []byte, value []byte) error {
6072 b := t.Bucket(name)
6173 if b == nil {
62 return &Error{"bucket not found", nil}
74 return BucketNotFoundError
6375 }
6476
6577 // Validate the key and data size.
6678 if len(key) == 0 {
67 return &Error{"key required", nil}
79 return KeyRequiredError
6880 } else if len(key) > MaxKeySize {
69 return &Error{"key too large", nil}
70 } else if len(value) > MaxDataSize {
71 return &Error{"data too large", nil}
81 return KeyTooLargeError
82 } else if len(value) > MaxValueSize {
83 return ValueTooLargeError
7284 }
7385
7486 // Move cursor to correct position.
8193 return nil
8294 }
8395
96 // Delete removes a key from the named bucket.
97 // If the key does not exist then nothing is done and a nil error is returned.
98 // Returns an error if the bucket cannot be found.
8499 func (t *RWTransaction) Delete(name string, key []byte) error {
85100 b := t.Bucket(name)
86101 if b == nil {
87 return &Error{"bucket not found", nil}
102 return BucketNotFoundError
88103 }
89104
90105 // Move cursor to correct position.
97112 return nil
98113 }
99114
100 // Commit writes all changes to disk.
115 // Commit writes all changes to disk and updates the meta page.
116 // Returns an error if a disk write error occurs.
101117 func (t *RWTransaction) Commit() error {
102118 defer t.close()
103119
130146 return nil
131147 }
132148
149 // Rollback closes the transaction and ignores all previous updates.
133150 func (t *RWTransaction) Rollback() {
134151 t.close()
135152 }
4343
4444 // Create the same bucket again.
4545 err = db.CreateBucket("widgets")
46 assert.Equal(t, err, &Error{"bucket already exists", nil})
46 assert.Equal(t, err, BucketExistsError)
4747 })
4848 }
4949
5151 func TestRWTransactionCreateBucketWithoutName(t *testing.T) {
5252 withOpenDB(func(db *DB, path string) {
5353 err := db.CreateBucket("")
54 assert.Equal(t, err, &Error{"bucket name cannot be blank", nil})
54 assert.Equal(t, err, BucketNameRequiredError)
5555 })
5656 }
5757
6262 assert.NoError(t, err)
6363
6464 err = db.CreateBucket(strings.Repeat("X", 256))
65 assert.Equal(t, err, &Error{"bucket name too long", nil})
65 assert.Equal(t, err, BucketNameTooLargeError)
6666 })
6767 }
6868
151151 // Verify all items exist.
152152 txn, _ := db.Transaction()
153153 for _, item := range items {
154 if !assert.Equal(t, item.Value, txn.Get("widgets", item.Key)) {
154 value, err := txn.Get("widgets", item.Key)
155 assert.NoError(t, err)
156 if !assert.Equal(t, item.Value, value) {
155157 db.CopyFile("/tmp/bolt.put.multiple.db")
156158 t.FailNow()
157159 }
187189 txn, _ := db.Transaction()
188190 for j, exp := range items {
189191 if j > i {
190 if !assert.Equal(t, exp.Value, txn.Get("widgets", exp.Key)) {
192 value, err := txn.Get("widgets", exp.Key)
193 assert.NoError(t, err)
194 if !assert.Equal(t, exp.Value, value) {
191195 t.FailNow()
192196 }
193197 } else {
194 if !assert.Nil(t, txn.Get("widgets", exp.Key)) {
198 value, err := txn.Get("widgets", exp.Key)
199 assert.NoError(t, err)
200 if !assert.Nil(t, value) {
195201 t.FailNow()
196202 }
197203 }
00 package bolt
11
2 const (
3 ps_modify = 1
4 ps_rootonly = 2
5 ps_first = 4
6 ps_last = 8
7 )
8
9 type txnid uint64
10
2 // Transaction represents a read-only transaction on the database.
3 // It can be used for retrieving values for keys as well as creating cursors for
4 // iterating over the data.
5 //
6 // IMPORTANT: You must close transactions when you are done with them. Pages
7 // can not be reclaimed by the writer until no more transactions are using them.
8 // A long running read transaction can cause the database to quickly grow.
119 type Transaction struct {
1210 db *DB
1311 meta *meta
1412 buckets *buckets
1513 pages map[pgid]*page
1614 }
15
16 // txnid represents the internal transaction identifier.
17 type txnid uint64
1718
1819 // init initializes the transaction and associates it with a database.
1920 func (t *Transaction) init(db *DB) {
3031 return t.meta.txnid
3132 }
3233
34 // Close closes the transaction and releases any pages it is using.
3335 func (t *Transaction) Close() {
3436 t.db.removeTransaction(t)
3537 }
3638
39 // DB returns a reference to the database that created the transaction.
3740 func (t *Transaction) DB() *DB {
3841 return t.db
3942 }
4043
4144 // Bucket retrieves a bucket by name.
45 // Returns nil if the bucket does not exist.
4246 func (t *Transaction) Bucket(name string) *Bucket {
4347 b := t.buckets.get(name)
4448 if b == nil {
5963 }
6064
6165 // Cursor creates a cursor associated with a given bucket.
62 func (t *Transaction) Cursor(name string) *Cursor {
66 // The cursor is only valid as long as the Transaction is open.
67 // Do not use a cursor after the transaction is closed.
68 func (t *Transaction) Cursor(name string) (*Cursor, error) {
6369 b := t.Bucket(name)
6470 if b == nil {
65 return nil
71 return nil, BucketNotFoundError
6672 }
67 return b.cursor()
73 return b.cursor(), nil
6874 }
6975
7076 // Get retrieves the value for a key in a named bucket.
71 func (t *Transaction) Get(name string, key []byte) []byte {
72 c := t.Cursor(name)
73 if c == nil {
74 return nil
77 // Returns a nil value if the key does not exist.
78 // Returns an error if the bucket does not exist.
79 func (t *Transaction) Get(name string, key []byte) (value []byte, err error) {
80 c, err := t.Cursor(name)
81 if err != nil {
82 return nil, err
7583 }
76 return c.Get(key)
84 return c.Get(key), nil
7785 }
7886
7987 // page returns a reference to the page with a given id.
4242 withOpenDB(func(db *DB, path string) {
4343 db.CreateBucket("widgets")
4444 txn, _ := db.Transaction()
45 c := txn.Cursor("widgets")
45 c, err := txn.Cursor("widgets")
46 assert.NoError(t, err)
4647 k, v := c.First()
4748 assert.Nil(t, k)
4849 assert.Nil(t, v)
5556 withOpenDB(func(db *DB, path string) {
5657 db.CreateBucket("widgets")
5758 txn, _ := db.Transaction()
58 assert.Nil(t, txn.Cursor("woojits"))
59 c, err := txn.Cursor("woojits")
60 assert.Nil(t, c)
61 assert.Equal(t, err, BucketNotFoundError)
5962 txn.Close()
6063 })
6164 }
6871 db.Put("widgets", []byte("foo"), []byte{0})
6972 db.Put("widgets", []byte("bar"), []byte{1})
7073 txn, _ := db.Transaction()
71 c := txn.Cursor("widgets")
74 c, err := txn.Cursor("widgets")
75 assert.NoError(t, err)
7276
7377 k, v := c.First()
7478 assert.Equal(t, string(k), "bar")
102106 db.Put("widgets", []byte("foo"), []byte{})
103107
104108 txn, _ := db.Transaction()
105 c := txn.Cursor("widgets")
109 c, err := txn.Cursor("widgets")
110 assert.NoError(t, err)
106111
107112 k, _ := c.First()
108113 assert.Equal(t, string(k), "bar")
138143 // Iterate over all items and check consistency.
139144 var index = 0
140145 txn, _ := db.Transaction()
141 c := txn.Cursor("widgets")
146 c, err := txn.Cursor("widgets")
147 assert.NoError(t, err)
142148 for k, v := c.First(); k != nil && index < len(items); k, v = c.Next() {
143149 assert.Equal(t, k, items[index].Key)
144150 assert.Equal(t, v, items[index].Value)