1 package database_test
2 3 import (
4 "bytes"
5 "fmt"
6 "os"
7 "path/filepath"
8 9 "github.com/p9c/p9/pkg/database"
10 _ "github.com/p9c/p9/pkg/database/ffldb"
11 "github.com/p9c/p9/pkg/wire"
12 )
13 14 // This example demonstrates creating a new database.
15 func ExampleCreate() {
16 // This example assumes the ffldb driver is imported.
17 //
18 // import (
19 // "github.com/p9c/p9/pkg/db"
20 // _ "github.com/p9c/p9/pkg/db/ffldb"
21 // )
22 //
23 // Create a database and schedule it to be closed and removed on exit. Typically you wouldn't want to remove the
24 // database right away like this, nor put it in the temp directory, but it's done here to ensure the example cleans
25 // up after itself.
26 dbPath := filepath.Join(os.TempDir(), "examplecreate")
27 db, e := database.Create("ffldb", dbPath, wire.MainNet)
28 if e != nil {
29 return
30 }
31 defer func() {
32 if e := os.RemoveAll(dbPath); database.E.Chk(e) {
33 }
34 }()
35 defer func() {
36 if e := db.Close(); database.E.Chk(e) {
37 }
38 }()
39 // Output:
40 }
41 42 // This example demonstrates creating a new database and using a managed read-write transaction to store and retrieve
43 // metadata.
44 func Example_basicUsage() {
45 // This example assumes the ffldb driver is imported.
46 //
47 // import (
48 // "github.com/p9c/p9/pkg/db"
49 // _ "github.com/p9c/p9/pkg/db/ffldb"
50 // )
51 //
52 // Create a database and schedule it to be closed and removed on exit. Typically you wouldn't want to remove the
53 // database right away like this, nor put it in the temp directory, but it's done here to ensure the example cleans
54 // up after itself.
55 dbPath := filepath.Join(os.TempDir(), "exampleusage")
56 var e error
57 var db database.DB
58 db, e = database.Create("ffldb", dbPath, wire.MainNet)
59 if e != nil {
60 return
61 }
62 defer func() {
63 if e = os.RemoveAll(dbPath); database.E.Chk(e) {
64 }
65 }()
66 defer func() {
67 if e = db.Close(); database.E.Chk(e) {
68 }
69 }()
70 // Use the Update function of the database to perform a managed read-write transaction. The transaction will
71 // automatically be rolled back if the supplied inner function returns a non-nil error.
72 e = db.Update(
73 func(tx database.Tx) (e error) {
74 // Store a key/value pair directly in the metadata bucket. Typically a nested bucket would be used for a given
75 // feature, but this example is using the metadata bucket directly for simplicity.
76 key := []byte("mykey")
77 value := []byte("myvalue")
78 if e = tx.Metadata().Put(key, value); E.Chk(e) {
79 return e
80 }
81 // Read the key back and ensure it matches.
82 if !bytes.Equal(tx.Metadata().Get(key), value) {
83 return fmt.Errorf("unexpected value for key '%s'", key)
84 }
85 // Create a new nested bucket under the metadata bucket.
86 nestedBucketKey := []byte("mybucket")
87 var nestedBucket database.Bucket
88 nestedBucket, e = tx.Metadata().CreateBucket(nestedBucketKey)
89 if e != nil {
90 return e
91 }
92 // The key from above that was set in the metadata bucket does not exist in this new nested bucket.
93 if nestedBucket.Get(key) != nil {
94 return fmt.Errorf("key '%s' is not expected nil", key)
95 }
96 return nil
97 },
98 )
99 if e != nil {
100 return
101 }
102 // Output:
103 }
104 105 // // This example demonstrates creating a new database, using a managed read-write
106 // // transaction to store a block, and using a managed read-only transaction to
107 // // fetch the block.
108 // func Example_blockStorageAndRetrieval() {
109 // // This example assumes the ffldb driver is imported.
110 // //
111 // // import (
112 // // "github.com/p9c/p9/pkg/db"
113 // // _ "github.com/p9c/p9/pkg/db/ffldb"
114 // // )
115 // // Create a database and schedule it to be closed and removed on exit.
116 // // Typically you wouldn't want to remove the database right away like
117 // // this, nor put it in the temp directory, but it's done here to ensure
118 // // the example cleans up after itself.
119 // dbPath := filepath.Join(os.TempDir(), "exampleblkstorage")
120 // db, e := database.Create("ffldb", dbPath, wire.MainNet)
121 // if e != nil {
122 // DB// return
123 // }
124 // defer os.RemoveAll(dbPath)
125 // defer db.Close()
126 // // Use the Update function of the database to perform a managed
127 // // read-write transaction and store a genesis block in the database as
128 // // and example.
129 // e = db.Update(func(tx database.Tx) (e error) {
130 // genesisBlock := chaincfg.MainNetParams.GenesisBlock
131 // return tx.StoreBlock(util.NewBlock(genesisBlock))
132 // })
133 // if e != nil {
134 // DB// return
135 // }
136 // // Use the View function of the database to perform a managed read-only
137 // // transaction and fetch the block stored above.
138 // var loadedBlockBytes []byte
139 // e = db.Update(func(tx database.Tx) (e error) {
140 // genesisHash := chaincfg.MainNetParams.GenesisHash
141 // blockBytes, e := tx.FetchBlock(genesisHash)
142 // if e != nil {
143 // return e
144 // }
145 // // As documented, all data fetched from the database is only
146 // // valid during a database transaction in order to support
147 // // zero-copy backends. Thus, make a copy of the data so it
148 // // can be used outside of the transaction.
149 // loadedBlockBytes = make([]byte, len(blockBytes))
150 // copy(loadedBlockBytes, blockBytes)
151 // return nil
152 // })
153 // if e != nil {
154 // DB// return
155 // }
156 // // Typically at this point, the block could be deserialized via the
157 // // wire.Block.Deserialize function or used in its serialized form
158 // // depending on need. However, for this example, just display the
159 // // number of serialized bytes to show it was loaded as expected.
160 // fmt.Printf("Serialized block size: %d bytes\n", len(loadedBlockBytes))
161 // // Output:
162 // // Serialized block size: 285 bytes
163 // }
164