1package bbolt_test
2
3import (
4	"bytes"
5	"encoding/binary"
6	"fmt"
7	"log"
8	"os"
9	"reflect"
10	"sort"
11	"testing"
12	"testing/quick"
13
14	bolt "go.etcd.io/bbolt"
15)
16
17// Ensure that a cursor can return a reference to the bucket that created it.
18func TestCursor_Bucket(t *testing.T) {
19	db := MustOpenDB()
20	defer db.MustClose()
21	if err := db.Update(func(tx *bolt.Tx) error {
22		b, err := tx.CreateBucket([]byte("widgets"))
23		if err != nil {
24			t.Fatal(err)
25		}
26		if cb := b.Cursor().Bucket(); !reflect.DeepEqual(cb, b) {
27			t.Fatal("cursor bucket mismatch")
28		}
29		return nil
30	}); err != nil {
31		t.Fatal(err)
32	}
33}
34
35// Ensure that a Tx cursor can seek to the appropriate keys.
36func TestCursor_Seek(t *testing.T) {
37	db := MustOpenDB()
38	defer db.MustClose()
39	if err := db.Update(func(tx *bolt.Tx) error {
40		b, err := tx.CreateBucket([]byte("widgets"))
41		if err != nil {
42			t.Fatal(err)
43		}
44		if err := b.Put([]byte("foo"), []byte("0001")); err != nil {
45			t.Fatal(err)
46		}
47		if err := b.Put([]byte("bar"), []byte("0002")); err != nil {
48			t.Fatal(err)
49		}
50		if err := b.Put([]byte("baz"), []byte("0003")); err != nil {
51			t.Fatal(err)
52		}
53
54		if _, err := b.CreateBucket([]byte("bkt")); err != nil {
55			t.Fatal(err)
56		}
57		return nil
58	}); err != nil {
59		t.Fatal(err)
60	}
61
62	if err := db.View(func(tx *bolt.Tx) error {
63		c := tx.Bucket([]byte("widgets")).Cursor()
64
65		// Exact match should go to the key.
66		if k, v := c.Seek([]byte("bar")); !bytes.Equal(k, []byte("bar")) {
67			t.Fatalf("unexpected key: %v", k)
68		} else if !bytes.Equal(v, []byte("0002")) {
69			t.Fatalf("unexpected value: %v", v)
70		}
71
72		// Inexact match should go to the next key.
73		if k, v := c.Seek([]byte("bas")); !bytes.Equal(k, []byte("baz")) {
74			t.Fatalf("unexpected key: %v", k)
75		} else if !bytes.Equal(v, []byte("0003")) {
76			t.Fatalf("unexpected value: %v", v)
77		}
78
79		// Low key should go to the first key.
80		if k, v := c.Seek([]byte("")); !bytes.Equal(k, []byte("bar")) {
81			t.Fatalf("unexpected key: %v", k)
82		} else if !bytes.Equal(v, []byte("0002")) {
83			t.Fatalf("unexpected value: %v", v)
84		}
85
86		// High key should return no key.
87		if k, v := c.Seek([]byte("zzz")); k != nil {
88			t.Fatalf("expected nil key: %v", k)
89		} else if v != nil {
90			t.Fatalf("expected nil value: %v", v)
91		}
92
93		// Buckets should return their key but no value.
94		if k, v := c.Seek([]byte("bkt")); !bytes.Equal(k, []byte("bkt")) {
95			t.Fatalf("unexpected key: %v", k)
96		} else if v != nil {
97			t.Fatalf("expected nil value: %v", v)
98		}
99
100		return nil
101	}); err != nil {
102		t.Fatal(err)
103	}
104}
105
106func TestCursor_Delete(t *testing.T) {
107	db := MustOpenDB()
108	defer db.MustClose()
109
110	const count = 1000
111
112	// Insert every other key between 0 and $count.
113	if err := db.Update(func(tx *bolt.Tx) error {
114		b, err := tx.CreateBucket([]byte("widgets"))
115		if err != nil {
116			t.Fatal(err)
117		}
118		for i := 0; i < count; i += 1 {
119			k := make([]byte, 8)
120			binary.BigEndian.PutUint64(k, uint64(i))
121			if err := b.Put(k, make([]byte, 100)); err != nil {
122				t.Fatal(err)
123			}
124		}
125		if _, err := b.CreateBucket([]byte("sub")); err != nil {
126			t.Fatal(err)
127		}
128		return nil
129	}); err != nil {
130		t.Fatal(err)
131	}
132
133	if err := db.Update(func(tx *bolt.Tx) error {
134		c := tx.Bucket([]byte("widgets")).Cursor()
135		bound := make([]byte, 8)
136		binary.BigEndian.PutUint64(bound, uint64(count/2))
137		for key, _ := c.First(); bytes.Compare(key, bound) < 0; key, _ = c.Next() {
138			if err := c.Delete(); err != nil {
139				t.Fatal(err)
140			}
141		}
142
143		c.Seek([]byte("sub"))
144		if err := c.Delete(); err != bolt.ErrIncompatibleValue {
145			t.Fatalf("unexpected error: %s", err)
146		}
147
148		return nil
149	}); err != nil {
150		t.Fatal(err)
151	}
152
153	if err := db.View(func(tx *bolt.Tx) error {
154		stats := tx.Bucket([]byte("widgets")).Stats()
155		if stats.KeyN != count/2+1 {
156			t.Fatalf("unexpected KeyN: %d", stats.KeyN)
157		}
158		return nil
159	}); err != nil {
160		t.Fatal(err)
161	}
162}
163
164// Ensure that a Tx cursor can seek to the appropriate keys when there are a
165// large number of keys. This test also checks that seek will always move
166// forward to the next key.
167//
168// Related: https://github.com/boltdb/bolt/pull/187
169func TestCursor_Seek_Large(t *testing.T) {
170	db := MustOpenDB()
171	defer db.MustClose()
172
173	var count = 10000
174
175	// Insert every other key between 0 and $count.
176	if err := db.Update(func(tx *bolt.Tx) error {
177		b, err := tx.CreateBucket([]byte("widgets"))
178		if err != nil {
179			t.Fatal(err)
180		}
181
182		for i := 0; i < count; i += 100 {
183			for j := i; j < i+100; j += 2 {
184				k := make([]byte, 8)
185				binary.BigEndian.PutUint64(k, uint64(j))
186				if err := b.Put(k, make([]byte, 100)); err != nil {
187					t.Fatal(err)
188				}
189			}
190		}
191		return nil
192	}); err != nil {
193		t.Fatal(err)
194	}
195
196	if err := db.View(func(tx *bolt.Tx) error {
197		c := tx.Bucket([]byte("widgets")).Cursor()
198		for i := 0; i < count; i++ {
199			seek := make([]byte, 8)
200			binary.BigEndian.PutUint64(seek, uint64(i))
201
202			k, _ := c.Seek(seek)
203
204			// The last seek is beyond the end of the the range so
205			// it should return nil.
206			if i == count-1 {
207				if k != nil {
208					t.Fatal("expected nil key")
209				}
210				continue
211			}
212
213			// Otherwise we should seek to the exact key or the next key.
214			num := binary.BigEndian.Uint64(k)
215			if i%2 == 0 {
216				if num != uint64(i) {
217					t.Fatalf("unexpected num: %d", num)
218				}
219			} else {
220				if num != uint64(i+1) {
221					t.Fatalf("unexpected num: %d", num)
222				}
223			}
224		}
225
226		return nil
227	}); err != nil {
228		t.Fatal(err)
229	}
230}
231
232// Ensure that a cursor can iterate over an empty bucket without error.
233func TestCursor_EmptyBucket(t *testing.T) {
234	db := MustOpenDB()
235	defer db.MustClose()
236	if err := db.Update(func(tx *bolt.Tx) error {
237		_, err := tx.CreateBucket([]byte("widgets"))
238		return err
239	}); err != nil {
240		t.Fatal(err)
241	}
242
243	if err := db.View(func(tx *bolt.Tx) error {
244		c := tx.Bucket([]byte("widgets")).Cursor()
245		k, v := c.First()
246		if k != nil {
247			t.Fatalf("unexpected key: %v", k)
248		} else if v != nil {
249			t.Fatalf("unexpected value: %v", v)
250		}
251		return nil
252	}); err != nil {
253		t.Fatal(err)
254	}
255}
256
257// Ensure that a Tx cursor can reverse iterate over an empty bucket without error.
258func TestCursor_EmptyBucketReverse(t *testing.T) {
259	db := MustOpenDB()
260	defer db.MustClose()
261
262	if err := db.Update(func(tx *bolt.Tx) error {
263		_, err := tx.CreateBucket([]byte("widgets"))
264		return err
265	}); err != nil {
266		t.Fatal(err)
267	}
268	if err := db.View(func(tx *bolt.Tx) error {
269		c := tx.Bucket([]byte("widgets")).Cursor()
270		k, v := c.Last()
271		if k != nil {
272			t.Fatalf("unexpected key: %v", k)
273		} else if v != nil {
274			t.Fatalf("unexpected value: %v", v)
275		}
276		return nil
277	}); err != nil {
278		t.Fatal(err)
279	}
280}
281
282// Ensure that a Tx cursor can iterate over a single root with a couple elements.
283func TestCursor_Iterate_Leaf(t *testing.T) {
284	db := MustOpenDB()
285	defer db.MustClose()
286
287	if err := db.Update(func(tx *bolt.Tx) error {
288		b, err := tx.CreateBucket([]byte("widgets"))
289		if err != nil {
290			t.Fatal(err)
291		}
292		if err := b.Put([]byte("baz"), []byte{}); err != nil {
293			t.Fatal(err)
294		}
295		if err := b.Put([]byte("foo"), []byte{0}); err != nil {
296			t.Fatal(err)
297		}
298		if err := b.Put([]byte("bar"), []byte{1}); err != nil {
299			t.Fatal(err)
300		}
301		return nil
302	}); err != nil {
303		t.Fatal(err)
304	}
305	tx, err := db.Begin(false)
306	if err != nil {
307		t.Fatal(err)
308	}
309	defer func() { _ = tx.Rollback() }()
310
311	c := tx.Bucket([]byte("widgets")).Cursor()
312
313	k, v := c.First()
314	if !bytes.Equal(k, []byte("bar")) {
315		t.Fatalf("unexpected key: %v", k)
316	} else if !bytes.Equal(v, []byte{1}) {
317		t.Fatalf("unexpected value: %v", v)
318	}
319
320	k, v = c.Next()
321	if !bytes.Equal(k, []byte("baz")) {
322		t.Fatalf("unexpected key: %v", k)
323	} else if !bytes.Equal(v, []byte{}) {
324		t.Fatalf("unexpected value: %v", v)
325	}
326
327	k, v = c.Next()
328	if !bytes.Equal(k, []byte("foo")) {
329		t.Fatalf("unexpected key: %v", k)
330	} else if !bytes.Equal(v, []byte{0}) {
331		t.Fatalf("unexpected value: %v", v)
332	}
333
334	k, v = c.Next()
335	if k != nil {
336		t.Fatalf("expected nil key: %v", k)
337	} else if v != nil {
338		t.Fatalf("expected nil value: %v", v)
339	}
340
341	k, v = c.Next()
342	if k != nil {
343		t.Fatalf("expected nil key: %v", k)
344	} else if v != nil {
345		t.Fatalf("expected nil value: %v", v)
346	}
347
348	if err := tx.Rollback(); err != nil {
349		t.Fatal(err)
350	}
351}
352
353// Ensure that a Tx cursor can iterate in reverse over a single root with a couple elements.
354func TestCursor_LeafRootReverse(t *testing.T) {
355	db := MustOpenDB()
356	defer db.MustClose()
357
358	if err := db.Update(func(tx *bolt.Tx) error {
359		b, err := tx.CreateBucket([]byte("widgets"))
360		if err != nil {
361			t.Fatal(err)
362		}
363		if err := b.Put([]byte("baz"), []byte{}); err != nil {
364			t.Fatal(err)
365		}
366		if err := b.Put([]byte("foo"), []byte{0}); err != nil {
367			t.Fatal(err)
368		}
369		if err := b.Put([]byte("bar"), []byte{1}); err != nil {
370			t.Fatal(err)
371		}
372		return nil
373	}); err != nil {
374		t.Fatal(err)
375	}
376	tx, err := db.Begin(false)
377	if err != nil {
378		t.Fatal(err)
379	}
380	c := tx.Bucket([]byte("widgets")).Cursor()
381
382	if k, v := c.Last(); !bytes.Equal(k, []byte("foo")) {
383		t.Fatalf("unexpected key: %v", k)
384	} else if !bytes.Equal(v, []byte{0}) {
385		t.Fatalf("unexpected value: %v", v)
386	}
387
388	if k, v := c.Prev(); !bytes.Equal(k, []byte("baz")) {
389		t.Fatalf("unexpected key: %v", k)
390	} else if !bytes.Equal(v, []byte{}) {
391		t.Fatalf("unexpected value: %v", v)
392	}
393
394	if k, v := c.Prev(); !bytes.Equal(k, []byte("bar")) {
395		t.Fatalf("unexpected key: %v", k)
396	} else if !bytes.Equal(v, []byte{1}) {
397		t.Fatalf("unexpected value: %v", v)
398	}
399
400	if k, v := c.Prev(); k != nil {
401		t.Fatalf("expected nil key: %v", k)
402	} else if v != nil {
403		t.Fatalf("expected nil value: %v", v)
404	}
405
406	if k, v := c.Prev(); k != nil {
407		t.Fatalf("expected nil key: %v", k)
408	} else if v != nil {
409		t.Fatalf("expected nil value: %v", v)
410	}
411
412	if err := tx.Rollback(); err != nil {
413		t.Fatal(err)
414	}
415}
416
417// Ensure that a Tx cursor can restart from the beginning.
418func TestCursor_Restart(t *testing.T) {
419	db := MustOpenDB()
420	defer db.MustClose()
421
422	if err := db.Update(func(tx *bolt.Tx) error {
423		b, err := tx.CreateBucket([]byte("widgets"))
424		if err != nil {
425			t.Fatal(err)
426		}
427		if err := b.Put([]byte("bar"), []byte{}); err != nil {
428			t.Fatal(err)
429		}
430		if err := b.Put([]byte("foo"), []byte{}); err != nil {
431			t.Fatal(err)
432		}
433		return nil
434	}); err != nil {
435		t.Fatal(err)
436	}
437
438	tx, err := db.Begin(false)
439	if err != nil {
440		t.Fatal(err)
441	}
442	c := tx.Bucket([]byte("widgets")).Cursor()
443
444	if k, _ := c.First(); !bytes.Equal(k, []byte("bar")) {
445		t.Fatalf("unexpected key: %v", k)
446	}
447	if k, _ := c.Next(); !bytes.Equal(k, []byte("foo")) {
448		t.Fatalf("unexpected key: %v", k)
449	}
450
451	if k, _ := c.First(); !bytes.Equal(k, []byte("bar")) {
452		t.Fatalf("unexpected key: %v", k)
453	}
454	if k, _ := c.Next(); !bytes.Equal(k, []byte("foo")) {
455		t.Fatalf("unexpected key: %v", k)
456	}
457
458	if err := tx.Rollback(); err != nil {
459		t.Fatal(err)
460	}
461}
462
463// Ensure that a cursor can skip over empty pages that have been deleted.
464func TestCursor_First_EmptyPages(t *testing.T) {
465	db := MustOpenDB()
466	defer db.MustClose()
467
468	// Create 1000 keys in the "widgets" bucket.
469	if err := db.Update(func(tx *bolt.Tx) error {
470		b, err := tx.CreateBucket([]byte("widgets"))
471		if err != nil {
472			t.Fatal(err)
473		}
474
475		for i := 0; i < 1000; i++ {
476			if err := b.Put(u64tob(uint64(i)), []byte{}); err != nil {
477				t.Fatal(err)
478			}
479		}
480
481		return nil
482	}); err != nil {
483		t.Fatal(err)
484	}
485
486	// Delete half the keys and then try to iterate.
487	if err := db.Update(func(tx *bolt.Tx) error {
488		b := tx.Bucket([]byte("widgets"))
489		for i := 0; i < 600; i++ {
490			if err := b.Delete(u64tob(uint64(i))); err != nil {
491				t.Fatal(err)
492			}
493		}
494
495		c := b.Cursor()
496		var n int
497		for k, _ := c.First(); k != nil; k, _ = c.Next() {
498			n++
499		}
500		if n != 400 {
501			t.Fatalf("unexpected key count: %d", n)
502		}
503
504		return nil
505	}); err != nil {
506		t.Fatal(err)
507	}
508}
509
510// Ensure that a Tx can iterate over all elements in a bucket.
511func TestCursor_QuickCheck(t *testing.T) {
512	f := func(items testdata) bool {
513		db := MustOpenDB()
514		defer db.MustClose()
515
516		// Bulk insert all values.
517		tx, err := db.Begin(true)
518		if err != nil {
519			t.Fatal(err)
520		}
521		b, err := tx.CreateBucket([]byte("widgets"))
522		if err != nil {
523			t.Fatal(err)
524		}
525		for _, item := range items {
526			if err := b.Put(item.Key, item.Value); err != nil {
527				t.Fatal(err)
528			}
529		}
530		if err := tx.Commit(); err != nil {
531			t.Fatal(err)
532		}
533
534		// Sort test data.
535		sort.Sort(items)
536
537		// Iterate over all items and check consistency.
538		var index = 0
539		tx, err = db.Begin(false)
540		if err != nil {
541			t.Fatal(err)
542		}
543
544		c := tx.Bucket([]byte("widgets")).Cursor()
545		for k, v := c.First(); k != nil && index < len(items); k, v = c.Next() {
546			if !bytes.Equal(k, items[index].Key) {
547				t.Fatalf("unexpected key: %v", k)
548			} else if !bytes.Equal(v, items[index].Value) {
549				t.Fatalf("unexpected value: %v", v)
550			}
551			index++
552		}
553		if len(items) != index {
554			t.Fatalf("unexpected item count: %v, expected %v", len(items), index)
555		}
556
557		if err := tx.Rollback(); err != nil {
558			t.Fatal(err)
559		}
560
561		return true
562	}
563	if err := quick.Check(f, qconfig()); err != nil {
564		t.Error(err)
565	}
566}
567
568// Ensure that a transaction can iterate over all elements in a bucket in reverse.
569func TestCursor_QuickCheck_Reverse(t *testing.T) {
570	f := func(items testdata) bool {
571		db := MustOpenDB()
572		defer db.MustClose()
573
574		// Bulk insert all values.
575		tx, err := db.Begin(true)
576		if err != nil {
577			t.Fatal(err)
578		}
579		b, err := tx.CreateBucket([]byte("widgets"))
580		if err != nil {
581			t.Fatal(err)
582		}
583		for _, item := range items {
584			if err := b.Put(item.Key, item.Value); err != nil {
585				t.Fatal(err)
586			}
587		}
588		if err := tx.Commit(); err != nil {
589			t.Fatal(err)
590		}
591
592		// Sort test data.
593		sort.Sort(revtestdata(items))
594
595		// Iterate over all items and check consistency.
596		var index = 0
597		tx, err = db.Begin(false)
598		if err != nil {
599			t.Fatal(err)
600		}
601		c := tx.Bucket([]byte("widgets")).Cursor()
602		for k, v := c.Last(); k != nil && index < len(items); k, v = c.Prev() {
603			if !bytes.Equal(k, items[index].Key) {
604				t.Fatalf("unexpected key: %v", k)
605			} else if !bytes.Equal(v, items[index].Value) {
606				t.Fatalf("unexpected value: %v", v)
607			}
608			index++
609		}
610		if len(items) != index {
611			t.Fatalf("unexpected item count: %v, expected %v", len(items), index)
612		}
613
614		if err := tx.Rollback(); err != nil {
615			t.Fatal(err)
616		}
617
618		return true
619	}
620	if err := quick.Check(f, qconfig()); err != nil {
621		t.Error(err)
622	}
623}
624
625// Ensure that a Tx cursor can iterate over subbuckets.
626func TestCursor_QuickCheck_BucketsOnly(t *testing.T) {
627	db := MustOpenDB()
628	defer db.MustClose()
629
630	if err := db.Update(func(tx *bolt.Tx) error {
631		b, err := tx.CreateBucket([]byte("widgets"))
632		if err != nil {
633			t.Fatal(err)
634		}
635		if _, err := b.CreateBucket([]byte("foo")); err != nil {
636			t.Fatal(err)
637		}
638		if _, err := b.CreateBucket([]byte("bar")); err != nil {
639			t.Fatal(err)
640		}
641		if _, err := b.CreateBucket([]byte("baz")); err != nil {
642			t.Fatal(err)
643		}
644		return nil
645	}); err != nil {
646		t.Fatal(err)
647	}
648
649	if err := db.View(func(tx *bolt.Tx) error {
650		var names []string
651		c := tx.Bucket([]byte("widgets")).Cursor()
652		for k, v := c.First(); k != nil; k, v = c.Next() {
653			names = append(names, string(k))
654			if v != nil {
655				t.Fatalf("unexpected value: %v", v)
656			}
657		}
658		if !reflect.DeepEqual(names, []string{"bar", "baz", "foo"}) {
659			t.Fatalf("unexpected names: %+v", names)
660		}
661		return nil
662	}); err != nil {
663		t.Fatal(err)
664	}
665}
666
667// Ensure that a Tx cursor can reverse iterate over subbuckets.
668func TestCursor_QuickCheck_BucketsOnly_Reverse(t *testing.T) {
669	db := MustOpenDB()
670	defer db.MustClose()
671
672	if err := db.Update(func(tx *bolt.Tx) error {
673		b, err := tx.CreateBucket([]byte("widgets"))
674		if err != nil {
675			t.Fatal(err)
676		}
677		if _, err := b.CreateBucket([]byte("foo")); err != nil {
678			t.Fatal(err)
679		}
680		if _, err := b.CreateBucket([]byte("bar")); err != nil {
681			t.Fatal(err)
682		}
683		if _, err := b.CreateBucket([]byte("baz")); err != nil {
684			t.Fatal(err)
685		}
686		return nil
687	}); err != nil {
688		t.Fatal(err)
689	}
690
691	if err := db.View(func(tx *bolt.Tx) error {
692		var names []string
693		c := tx.Bucket([]byte("widgets")).Cursor()
694		for k, v := c.Last(); k != nil; k, v = c.Prev() {
695			names = append(names, string(k))
696			if v != nil {
697				t.Fatalf("unexpected value: %v", v)
698			}
699		}
700		if !reflect.DeepEqual(names, []string{"foo", "baz", "bar"}) {
701			t.Fatalf("unexpected names: %+v", names)
702		}
703		return nil
704	}); err != nil {
705		t.Fatal(err)
706	}
707}
708
709func ExampleCursor() {
710	// Open the database.
711	db, err := bolt.Open(tempfile(), 0666, nil)
712	if err != nil {
713		log.Fatal(err)
714	}
715	defer os.Remove(db.Path())
716
717	// Start a read-write transaction.
718	if err := db.Update(func(tx *bolt.Tx) error {
719		// Create a new bucket.
720		b, err := tx.CreateBucket([]byte("animals"))
721		if err != nil {
722			return err
723		}
724
725		// Insert data into a bucket.
726		if err := b.Put([]byte("dog"), []byte("fun")); err != nil {
727			log.Fatal(err)
728		}
729		if err := b.Put([]byte("cat"), []byte("lame")); err != nil {
730			log.Fatal(err)
731		}
732		if err := b.Put([]byte("liger"), []byte("awesome")); err != nil {
733			log.Fatal(err)
734		}
735
736		// Create a cursor for iteration.
737		c := b.Cursor()
738
739		// Iterate over items in sorted key order. This starts from the
740		// first key/value pair and updates the k/v variables to the
741		// next key/value on each iteration.
742		//
743		// The loop finishes at the end of the cursor when a nil key is returned.
744		for k, v := c.First(); k != nil; k, v = c.Next() {
745			fmt.Printf("A %s is %s.\n", k, v)
746		}
747
748		return nil
749	}); err != nil {
750		log.Fatal(err)
751	}
752
753	if err := db.Close(); err != nil {
754		log.Fatal(err)
755	}
756
757	// Output:
758	// A cat is lame.
759	// A dog is fun.
760	// A liger is awesome.
761}
762
763func ExampleCursor_reverse() {
764	// Open the database.
765	db, err := bolt.Open(tempfile(), 0666, nil)
766	if err != nil {
767		log.Fatal(err)
768	}
769	defer os.Remove(db.Path())
770
771	// Start a read-write transaction.
772	if err := db.Update(func(tx *bolt.Tx) error {
773		// Create a new bucket.
774		b, err := tx.CreateBucket([]byte("animals"))
775		if err != nil {
776			return err
777		}
778
779		// Insert data into a bucket.
780		if err := b.Put([]byte("dog"), []byte("fun")); err != nil {
781			log.Fatal(err)
782		}
783		if err := b.Put([]byte("cat"), []byte("lame")); err != nil {
784			log.Fatal(err)
785		}
786		if err := b.Put([]byte("liger"), []byte("awesome")); err != nil {
787			log.Fatal(err)
788		}
789
790		// Create a cursor for iteration.
791		c := b.Cursor()
792
793		// Iterate over items in reverse sorted key order. This starts
794		// from the last key/value pair and updates the k/v variables to
795		// the previous key/value on each iteration.
796		//
797		// The loop finishes at the beginning of the cursor when a nil key
798		// is returned.
799		for k, v := c.Last(); k != nil; k, v = c.Prev() {
800			fmt.Printf("A %s is %s.\n", k, v)
801		}
802
803		return nil
804	}); err != nil {
805		log.Fatal(err)
806	}
807
808	// Close the database to release the file lock.
809	if err := db.Close(); err != nil {
810		log.Fatal(err)
811	}
812
813	// Output:
814	// A liger is awesome.
815	// A dog is fun.
816	// A cat is lame.
817}
818