example_test.go raw

   1  package walletdb_test
   2  
   3  import (
   4  	"bytes"
   5  	"fmt"
   6  	"os"
   7  	"path/filepath"
   8  	
   9  	"github.com/p9c/p9/pkg/walletdb"
  10  	_ "github.com/p9c/p9/pkg/walletdb/bdb"
  11  )
  12  
  13  // This example demonstrates creating a new database.
  14  func ExampleCreate() {
  15  	// This example assumes the bdb (bolt db) driver is imported.
  16  	//
  17  	// import (
  18  	// 	"github.com/p9c/p9/cmd/wallet/db"
  19  	// 	_ "github.com/p9c/p9/cmd/wallet/db/bdb"
  20  	// )
  21  	//
  22  	// Create a database and schedule it to be closed and removed on exit. Typically you wouldn't want to remove the
  23  	// database right away like this, but it's done here in the example to ensure the example cleans up after itself.
  24  	dbPath := filepath.Join(os.TempDir(), "examplecreate.db")
  25  	db, e := walletdb.Create("bdb", dbPath)
  26  	if E.Chk(e) {
  27  		return
  28  	}
  29  	defer func() {
  30  		if e := os.Remove(dbPath); walletdb.E.Chk(e) {
  31  		}
  32  	}()
  33  	defer func() {
  34  		if e := db.Close(); walletdb.E.Chk(e) {
  35  		}
  36  	}()
  37  	// Output:
  38  }
  39  
  40  // exampleNum is used as a counter in the exampleLoadDB function to provided a unique database name for each example.
  41  var exampleNum = 0
  42  
  43  // exampleLoadDB is used in the examples to elide the setup code.
  44  func exampleLoadDB() (db walletdb.DB, teardownFunc func(), e error) {
  45  	dbName := fmt.Sprintf("exampleload%d.db", exampleNum)
  46  	dbPath := filepath.Join(os.TempDir(), dbName)
  47  	db, e = walletdb.Create("bdb", dbPath)
  48  	if e != nil {
  49  		return nil, nil, e
  50  	}
  51  	teardownFunc = func() {
  52  		if e = db.Close(); walletdb.E.Chk(e) {
  53  		}
  54  		if e = os.Remove(dbPath); walletdb.E.Chk(e) {
  55  		}
  56  	}
  57  	exampleNum++
  58  	return db, teardownFunc, e
  59  }
  60  
  61  // This example demonstrates creating a new top level bucket.
  62  func ExampleDB_createTopLevelBucket() {
  63  	// Load a database for the purposes of this example and schedule it to be closed and removed on exit. See the Create
  64  	// example for more details on what this step is doing.
  65  	db, teardownFunc, e := exampleLoadDB()
  66  	if E.Chk(e) {
  67  		return
  68  	}
  69  	defer teardownFunc()
  70  	dbtx, e := db.BeginReadWriteTx()
  71  	if E.Chk(e) {
  72  		return
  73  	}
  74  	defer func() {
  75  		e = dbtx.Commit()
  76  		if E.Chk(e) {
  77  			return
  78  		}
  79  	}()
  80  	// Get or create a bucket in the database as needed. This bucket is what is typically passed to specific
  81  	// sub-packages so they have their own area to work in without worrying about conflicting keys.
  82  	bucketKey := []byte("walletsubpackage")
  83  	bucket, e := dbtx.CreateTopLevelBucket(bucketKey)
  84  	if E.Chk(e) {
  85  		return
  86  	}
  87  	// Prevent unused error.
  88  	_ = bucket
  89  	// Output:
  90  }
  91  
  92  // This example demonstrates creating a new database, getting a namespace from it, and using a managed read-write
  93  // transaction against the namespace to store and retrieve data.
  94  func Example_basicUsage() {
  95  	// This example assumes the bdb (bolt db) driver is imported.
  96  	//
  97  	// import (
  98  	// 	"github.com/p9c/p9/cmd/wallet/db"
  99  	// 	_ "github.com/p9c/p9/cmd/wallet/db/bdb"
 100  	// )
 101  	//
 102  	// Create a database and schedule it to be closed and removed on exit. Typically you wouldn't want to remove the
 103  	// database right away like this, but it's done here in the example to ensure the example cleans up after itself.
 104  	dbPath := filepath.Join(os.TempDir(), "exampleusage.db")
 105  	db, e := walletdb.Create("bdb", dbPath)
 106  	if E.Chk(e) {
 107  		return
 108  	}
 109  	defer func() {
 110  		if e = os.Remove(dbPath); walletdb.E.Chk(e) {
 111  		}
 112  	}()
 113  	defer func() {
 114  		if e = db.Close(); walletdb.E.Chk(e) {
 115  		}
 116  	}()
 117  	// Get or create a bucket in the database as needed. This bucket is what is typically passed to specific
 118  	// sub-packages so they have their own area to work in without worrying about conflicting keys.
 119  	bucketKey := []byte("walletsubpackage")
 120  	e = walletdb.Update(
 121  		db, func(tx walletdb.ReadWriteTx) (e error) {
 122  			bucket := tx.ReadWriteBucket(bucketKey)
 123  			if bucket == nil {
 124  				_, e = tx.CreateTopLevelBucket(bucketKey)
 125  				if e != nil {
 126  					return e
 127  				}
 128  			}
 129  			return nil
 130  		},
 131  	)
 132  	if E.Chk(e) {
 133  		return
 134  	}
 135  	// Use the Update function of the namespace to perform a managed read-write transaction. The transaction will
 136  	// automatically be rolled back if the supplied inner function returns a non-nil error.
 137  	e = walletdb.Update(
 138  		db, func(tx walletdb.ReadWriteTx) (e error) {
 139  			// All data is stored against the root bucket of the namespace, or nested buckets of the root bucket. It's not
 140  			// really necessary to store it in a separate variable like this, but it has been done here for the purposes of
 141  			// the example to illustrate.
 142  			rootBucket := tx.ReadWriteBucket(bucketKey)
 143  			// Store a key/value pair directly in the root bucket.
 144  			key := []byte("mykey")
 145  			value := []byte("myvalue")
 146  			if e = rootBucket.Put(key, value); E.Chk(e) {
 147  				return e
 148  			}
 149  			// Read the key back and ensure it matches.
 150  			if !bytes.Equal(rootBucket.Get(key), value) {
 151  				return fmt.Errorf("unexpected value for key '%s'", key)
 152  			}
 153  			// Create a new nested bucket under the root bucket.
 154  			nestedBucketKey := []byte("mybucket")
 155  			nestedBucket, e := rootBucket.CreateBucket(nestedBucketKey)
 156  			if e != nil {
 157  				return e
 158  			}
 159  			// The key from above that was set in the root bucket does not exist in this new nested bucket.
 160  			if nestedBucket.Get(key) != nil {
 161  				return fmt.Errorf("key '%s' is not expected nil", key)
 162  			}
 163  			return nil
 164  		},
 165  	)
 166  	if E.Chk(e) {
 167  		return
 168  	}
 169  	// Output:
 170  }
 171