@@ -33,7 +33,10 @@ use crate::*;
33
33
impl Configurator for RedbConfig {
34
34
type Builder = RedbBuilder ;
35
35
fn into_builder ( self ) -> Self :: Builder {
36
- RedbBuilder { config : self }
36
+ RedbBuilder {
37
+ config : self ,
38
+ database : None ,
39
+ }
37
40
}
38
41
}
39
42
@@ -42,16 +45,47 @@ impl Configurator for RedbConfig {
42
45
#[ derive( Default , Debug ) ]
43
46
pub struct RedbBuilder {
44
47
config : RedbConfig ,
48
+
49
+ database : Option < Arc < redb:: Database > > ,
45
50
}
46
51
47
52
impl RedbBuilder {
53
+ /// Set the database for Redb.
54
+ ///
55
+ /// This method should be called when you want to
56
+ /// use multiple tables of one database because
57
+ /// Redb doesn't allow opening a database that have been opened.
58
+ ///
59
+ /// <div class="warning">
60
+ ///
61
+ /// `datadir` and `database` should not be set simultaneously.
62
+ /// If both are set, `database` will take precedence.
63
+ ///
64
+ /// </div>
65
+ pub fn database ( mut self , db : Arc < redb:: Database > ) -> Self {
66
+ self . database = Some ( db) ;
67
+ self
68
+ }
69
+
48
70
/// Set the path to the redb data directory. Will create if not exists.
71
+ ///
72
+ ///
73
+ /// <div class="warning">
74
+ ///
75
+ /// Opening redb database via `datadir` takes away the ability to access multiple redb tables.
76
+ /// If you need to access multiple redb tables, the correct solution is to
77
+ /// create an `Arc<redb::database>` beforehand and then share it via [`database`]
78
+ /// with multiple builders where every builder will open one redb table.
79
+ ///
80
+ /// </div>
81
+ ///
82
+ /// [`database`]: RedbBuilder::database
49
83
pub fn datadir ( mut self , path : & str ) -> Self {
50
84
self . config . datadir = Some ( path. into ( ) ) ;
51
85
self
52
86
}
53
87
54
- /// Set the table name for Redb.
88
+ /// Set the table name for Redb. Will create if not exists.
55
89
pub fn table ( mut self , table : & str ) -> Self {
56
90
self . config . table = Some ( table. into ( ) ) ;
57
91
self
@@ -69,22 +103,30 @@ impl Builder for RedbBuilder {
69
103
type Config = RedbConfig ;
70
104
71
105
fn build ( self ) -> Result < impl Access > {
72
- let datadir_path = self . config . datadir . ok_or_else ( || {
73
- Error :: new ( ErrorKind :: ConfigInvalid , "datadir is required but not set" )
74
- . with_context ( "service" , Scheme :: Redb )
75
- } ) ?;
76
-
77
106
let table_name = self . config . table . ok_or_else ( || {
78
107
Error :: new ( ErrorKind :: ConfigInvalid , "table is required but not set" )
79
108
. with_context ( "service" , Scheme :: Redb )
80
109
} ) ?;
81
110
82
- let db = redb:: Database :: create ( & datadir_path) . map_err ( parse_database_error) ?;
111
+ let ( datadir, db) = if let Some ( db) = self . database {
112
+ ( None , db)
113
+ } else {
114
+ let datadir = self . config . datadir . ok_or_else ( || {
115
+ Error :: new ( ErrorKind :: ConfigInvalid , "datadir is required but not set" )
116
+ . with_context ( "service" , Scheme :: Redb )
117
+ } ) ?;
118
+
119
+ let db = redb:: Database :: create ( & datadir)
120
+ . map_err ( parse_database_error) ?
121
+ . into ( ) ;
83
122
84
- let db = Arc :: new ( db) ;
123
+ ( Some ( datadir) , db)
124
+ } ;
125
+
126
+ create_table ( & db, & table_name) ?;
85
127
86
128
Ok ( RedbBackend :: new ( Adapter {
87
- datadir : datadir_path ,
129
+ datadir,
88
130
table : table_name,
89
131
db,
90
132
} )
@@ -97,7 +139,7 @@ pub type RedbBackend = kv::Backend<Adapter>;
97
139
98
140
#[ derive( Clone ) ]
99
141
pub struct Adapter {
100
- datadir : String ,
142
+ datadir : Option < String > ,
101
143
table : String ,
102
144
db : Arc < redb:: Database > ,
103
145
}
@@ -116,7 +158,7 @@ impl kv::Adapter for Adapter {
116
158
fn info ( & self ) -> kv:: Info {
117
159
kv:: Info :: new (
118
160
Scheme :: Redb ,
119
- & self . datadir ,
161
+ & self . table ,
120
162
Capability {
121
163
read : true ,
122
164
write : true ,
@@ -238,3 +280,35 @@ fn parse_database_error(e: redb::DatabaseError) -> Error {
238
280
fn parse_commit_error ( e : redb:: CommitError ) -> Error {
239
281
Error :: new ( ErrorKind :: Unexpected , "error from redb" ) . set_source ( e)
240
282
}
283
+
284
+ /// Check if a table exists, otherwise create it.
285
+ fn create_table ( db : & redb:: Database , table : & str ) -> Result < ( ) > {
286
+ // Only one `WriteTransaction` is permitted at same time,
287
+ // applying new one will block until it available.
288
+ //
289
+ // So we first try checking table existence via `ReadTransaction`.
290
+ {
291
+ let read_txn = db. begin_read ( ) . map_err ( parse_transaction_error) ?;
292
+
293
+ let table_define: redb:: TableDefinition < & str , & [ u8 ] > = redb:: TableDefinition :: new ( table) ;
294
+
295
+ match read_txn. open_table ( table_define) {
296
+ Ok ( _) => return Ok ( ( ) ) ,
297
+ Err ( redb:: TableError :: TableDoesNotExist ( _) ) => ( ) ,
298
+ Err ( e) => return Err ( parse_table_error ( e) ) ,
299
+ }
300
+ }
301
+
302
+ {
303
+ let write_txn = db. begin_write ( ) . map_err ( parse_transaction_error) ?;
304
+
305
+ let table_define: redb:: TableDefinition < & str , & [ u8 ] > = redb:: TableDefinition :: new ( table) ;
306
+
307
+ write_txn
308
+ . open_table ( table_define)
309
+ . map_err ( parse_table_error) ?;
310
+ write_txn. commit ( ) . map_err ( parse_commit_error) ?;
311
+ }
312
+
313
+ Ok ( ( ) )
314
+ }
0 commit comments