@@ -3,7 +3,10 @@ use std::ptr;
33
44use crate :: mdb:: error:: mdb_result;
55use 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+ } ;
710
811/// A read-only transaction.
912pub struct RoTxn < ' e > {
@@ -30,6 +33,108 @@ impl<'e> RoTxn<'e> {
3033 pub ( crate ) fn env_mut_ptr ( & self ) -> * mut ffi:: MDB_env {
3134 self . env . env_mut_ptr ( )
3235 }
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+ }
33138}
34139
35140impl Drop for RoTxn < ' _ > {
@@ -43,12 +148,6 @@ impl Drop for RoTxn<'_> {
43148#[ cfg( feature = "sync-read-txn" ) ]
44149unsafe impl < T > Sync for RoTxn < ' _ > { }
45150
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-
52151/// A read-write transaction.
53152pub struct RwTxn < ' p > {
54153 pub ( crate ) txn : RoTxn < ' p > ,
@@ -76,15 +175,18 @@ impl<'p> RwTxn<'p> {
76175 self . txn . env . env_mut_ptr ( )
77176 }
78177
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 ( )
83185 }
84186
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 ( ) ;
88190 }
89191}
90192
@@ -95,3 +197,9 @@ impl<'p> Deref for RwTxn<'p> {
95197 & self . txn
96198 }
97199}
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