@@ -3,7 +3,10 @@ use std::ptr;
3
3
4
4
use crate :: mdb:: error:: mdb_result;
5
5
use crate :: mdb:: ffi;
6
- use crate :: { Env , Result } ;
6
+ use crate :: {
7
+ assert_eq_env_db_txn, Database , Env , OpenedDatabases , PolyDatabase , RememberedDatabase ,
8
+ RememberedPolyDatabase , Result ,
9
+ } ;
7
10
8
11
/// A read-only transaction.
9
12
pub struct RoTxn < ' e > {
@@ -30,6 +33,108 @@ impl<'e> RoTxn<'e> {
30
33
pub ( crate ) fn env_mut_ptr ( & self ) -> * mut ffi:: MDB_env {
31
34
self . env . env_mut_ptr ( )
32
35
}
36
+
37
+ /// Abort and loose the temporary database handles you opened in this transaction.
38
+ pub fn abort ( mut self ) {
39
+ abort_txn ( self . txn ) ;
40
+ self . txn = ptr:: null_mut ( ) ;
41
+ }
42
+
43
+ /// Keep the database you opened in this transaction by committing them.
44
+ ///
45
+ /// # Example: Opening and commit a Database
46
+ ///
47
+ /// You can open a database with a read transaction, use it in the transaction itself
48
+ /// but you'll need to [`RoTxn::remember_database`] and [`OpenedDatabases::retrieve_database`]
49
+ /// once you committed your transaction to be able to retrieve a global handle that
50
+ /// you can store anywhere.
51
+ ///
52
+ /// ```
53
+ /// # use std::fs;
54
+ /// # use std::path::Path;
55
+ /// # use heed::EnvOpenOptions;
56
+ /// use heed::Database;
57
+ /// use heed::byteorder::BigEndian;
58
+ /// use heed::types::*;
59
+ ///
60
+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
61
+ /// # let dir = tempfile::tempdir()?;
62
+ /// # let env = EnvOpenOptions::new()
63
+ /// # .map_size(10 * 1024 * 1024) // 10MB
64
+ /// # .open(dir.path())?;
65
+ /// type BEU32 = U32<BigEndian>;
66
+ ///
67
+ /// let rtxn = env.read_txn()?;
68
+ /// let photos: Database<BEU32, Str> = env.open_database(&rtxn, None)?.unwrap();
69
+ /// let remembered_photos = rtxn.remember_database(photos);
70
+ /// let opened_databases = rtxn.commit()?;
71
+ ///
72
+ /// // You can retrieve a global handle to your database now that it has been committed.
73
+ /// let photos = opened_databases.retrieve_database(remembered_photos);
74
+ /// let rtxn = env.read_txn()?;
75
+ /// assert_eq!(photos.len(&rtxn)?, 0);
76
+ /// # Ok(()) }
77
+ /// ```
78
+ ///
79
+ /// # How not to use `Database`s
80
+ ///
81
+ /// You must make sure to commit your transaction to be able to retrieve global [`Database`] handles.
82
+ /// The returned temporary handles can only be used in the scope of the transaction, not longer.
83
+ ///
84
+ /// ```compile_fail
85
+ /// # use std::fs;
86
+ /// # use std::path::Path;
87
+ /// # use heed::EnvOpenOptions;
88
+ /// use heed::Database;
89
+ /// use heed::byteorder::BigEndian;
90
+ /// use heed::types::*;
91
+ ///
92
+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
93
+ /// # let dir = tempfile::tempdir()?;
94
+ /// # let env = EnvOpenOptions::new()
95
+ /// # .map_size(10 * 1024 * 1024) // 10MB
96
+ /// # .open(dir.path())?;
97
+ /// type BEU32 = U32<BigEndian>;
98
+ ///
99
+ /// let rtxn = env.read_txn()?;
100
+ /// let photos: Database<BEU32, Str> = env.open_database(&rtxn, None)?.unwrap();
101
+ /// rtxn.abort();
102
+ ///
103
+ /// // You can't use photos has you forget to remember it and commit.
104
+ /// let rtxn = env.read_txn()?;
105
+ /// assert_eq!(photos.len(&rtxn)?, 0);
106
+ /// # Ok(()) }
107
+ /// ```
108
+ pub fn commit ( mut self ) -> Result < OpenedDatabases > {
109
+ // TODO make sure we have a unique identifier between the `OpenedDatabases`
110
+ // and the `RememberedPolyDatabase` types to make sure that we are not trying
111
+ // to globally keep an inalid database.
112
+ let result = unsafe { mdb_result ( ffi:: mdb_txn_commit ( self . txn ) ) } ;
113
+ self . txn = ptr:: null_mut ( ) ;
114
+ result. map_err ( Into :: into) . map ( |( ) | OpenedDatabases { unique_txn_id : 0 } )
115
+ }
116
+
117
+ /// Remember a `PolyDatabase` to be able to retrieve it after a [`RoTxn::commit`]/[`RwTxn::commit`].
118
+ pub fn remember_poly_database < ' t > (
119
+ & ' t self ,
120
+ poly_database : PolyDatabase < ' t > ,
121
+ ) -> RememberedPolyDatabase {
122
+ // TODO test that the database comes from the same txn
123
+ assert_eq_env_db_txn ! ( poly_database, self ) ;
124
+ let PolyDatabase { env_ident, dbi, .. } = poly_database;
125
+ RememberedPolyDatabase { unique_txn_id : 0 , env_ident, dbi }
126
+ }
127
+
128
+ /// Remember a `Database` to be able to retrieve it after a [`RoTxn::commit`]/[`RwTxn::commit`].
129
+ pub fn remember_database < ' t , KC , DC > (
130
+ & ' t self ,
131
+ database : Database < ' t , KC , DC > ,
132
+ ) -> RememberedDatabase < KC , DC > {
133
+ // TODO test that the database comes from the same txn
134
+ assert_eq_env_db_txn ! ( database. dyndb, self ) ;
135
+ let Database { dyndb : PolyDatabase { env_ident, dbi, .. } , marker } = database;
136
+ RememberedDatabase { unique_txn_id : 0 , env_ident, dbi, marker }
137
+ }
33
138
}
34
139
35
140
impl Drop for RoTxn < ' _ > {
@@ -43,12 +148,6 @@ impl Drop for RoTxn<'_> {
43
148
#[ cfg( feature = "sync-read-txn" ) ]
44
149
unsafe impl < T > Sync for RoTxn < ' _ > { }
45
150
46
- fn abort_txn ( txn : * mut ffi:: MDB_txn ) {
47
- // Asserts that the transaction hasn't been already committed.
48
- assert ! ( !txn. is_null( ) ) ;
49
- unsafe { ffi:: mdb_txn_abort ( txn) }
50
- }
51
-
52
151
/// A read-write transaction.
53
152
pub struct RwTxn < ' p > {
54
153
pub ( crate ) txn : RoTxn < ' p > ,
@@ -76,15 +175,18 @@ impl<'p> RwTxn<'p> {
76
175
self . txn . env . env_mut_ptr ( )
77
176
}
78
177
79
- pub fn commit ( mut self ) -> Result < ( ) > {
80
- let result = unsafe { mdb_result ( ffi:: mdb_txn_commit ( self . txn . txn ) ) } ;
81
- self . txn . txn = ptr:: null_mut ( ) ;
82
- result. map_err ( Into :: into)
178
+ /// Keep the changes you made to the environement and database you opened in this transaction
179
+ /// by committing them.
180
+ ///
181
+ /// Read the documentation of the [`RoTxn::commit`] method to learn more
182
+ /// on how to retrieve global database handles.
183
+ pub fn commit ( self ) -> Result < OpenedDatabases > {
184
+ self . txn . commit ( )
83
185
}
84
186
85
- pub fn abort ( mut self ) {
86
- abort_txn ( self . txn . txn ) ;
87
- self . txn . txn = ptr :: null_mut ( ) ;
187
+ /// Abort and loose the temporary database handles you opened in this transaction.
188
+ pub fn abort ( self ) {
189
+ self . txn . abort ( ) ;
88
190
}
89
191
}
90
192
@@ -95,3 +197,9 @@ impl<'p> Deref for RwTxn<'p> {
95
197
& self . txn
96
198
}
97
199
}
200
+
201
+ fn abort_txn ( txn : * mut ffi:: MDB_txn ) {
202
+ // Asserts that the transaction hasn't already been committed.
203
+ assert ! ( !txn. is_null( ) ) ;
204
+ unsafe { ffi:: mdb_txn_abort ( txn) }
205
+ }
0 commit comments