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