...

Source file src/go.etcd.io/bbolt/cursor_test.go

Documentation: go.etcd.io/bbolt

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

View as plain text