diff --git a/.ruby-version b/.ruby-version index 37c2961..1effb00 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.2 +2.7 diff --git a/ext/mdbx_ext/database.c b/ext/mdbx_ext/database.c index 1ebf17b..2bc2afa 100644 --- a/ext/mdbx_ext/database.c +++ b/ext/mdbx_ext/database.c @@ -135,11 +135,7 @@ rmdbx_key_for( VALUE key, MDBX_val *ckey ) void rmdbx_val_for( VALUE self, VALUE val, MDBX_val *data ) { - VALUE serialize_proc = rb_iv_get( self, "@serializer" ); - - if ( ! NIL_P( serialize_proc ) ) - val = rb_funcall( serialize_proc, rb_intern("call"), 1, val ); - + val = rb_funcall( self, rb_intern("serialize"), 1, val ); Check_Type( val, T_STRING ); data->iov_len = RSTRING_LEN( val ); @@ -148,20 +144,6 @@ rmdbx_val_for( VALUE self, VALUE val, MDBX_val *data ) } -/* - * Deserialize and return a value. - */ -VALUE -rmdbx_deserialize( VALUE self, VALUE val ) -{ - VALUE deserialize_proc = rb_iv_get( self, "@deserializer" ); - if ( ! NIL_P( deserialize_proc ) ) - val = rb_funcall( deserialize_proc, rb_intern("call"), 1, val ); - - return val; -} - - /* * Open the DB environment handle. * @@ -355,7 +337,7 @@ rmdbx_get_val( VALUE self, VALUE key ) switch ( rc ) { case MDBX_SUCCESS: rv = rb_str_new( data.iov_base, data.iov_len ); - return rmdbx_deserialize( self, rv ); + return rb_funcall( self, rb_intern("deserialize"), 1, rv ); case MDBX_NOTFOUND: return Qnil; @@ -439,13 +421,17 @@ rmdbx_set_subdb( VALUE self, VALUE name ) if ( db->txn ) rb_raise( rmdbx_eDatabaseError, "Unable to change collection: transaction open" ); - xfree( db->subdb ); + xfree( db->subdb ); db->subdb = NULL; if ( ! NIL_P(name) ) { size_t len = RSTRING_LEN( name ) + 1; db->subdb = malloc( len ); strlcpy( db->subdb, StringValuePtr(name), len ); + rmdbx_log_obj( self, "debug", "setting subdb: %s", RSTRING_PTR(name) ); + } + else { + rmdbx_log_obj( self, "debug", "clearing subdb" ); } /* Reset the db handle and issue a single transaction to reify @@ -644,11 +630,11 @@ rmdbx_each_value_i( VALUE self ) if ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_FIRST ) == MDBX_SUCCESS ) { VALUE rv = rb_str_new( data.iov_base, data.iov_len ); - rb_yield( rmdbx_deserialize( self, rv ) ); + rb_yield( rb_funcall( self, rb_intern("deserialize"), 1, rv ) ); while ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_NEXT ) == MDBX_SUCCESS ) { rv = rb_str_new( data.iov_base, data.iov_len ); - rb_yield( rmdbx_deserialize( self, rv ) ); + rb_yield( rb_funcall( self, rb_intern("deserialize"), 1, rv ) ); } } @@ -694,12 +680,15 @@ rmdbx_each_pair_i( VALUE self ) if ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_FIRST ) == MDBX_SUCCESS ) { VALUE rkey = rb_str_new( key.iov_base, key.iov_len ); VALUE rval = rb_str_new( data.iov_base, data.iov_len ); - rb_yield( rb_assoc_new( rkey, rmdbx_deserialize( self, rval ) ) ); + rval = rb_funcall( self, rb_intern("deserialize"), 1, rval ); + rb_yield( rb_assoc_new( rkey, rval ) ); while ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_NEXT ) == MDBX_SUCCESS ) { rkey = rb_str_new( key.iov_base, key.iov_len ); rval = rb_str_new( data.iov_base, data.iov_len ); - rb_yield( rb_assoc_new( rkey, rmdbx_deserialize( self, rval ) ) ); + rval = rb_funcall( self, rb_intern("deserialize"), 1, rval ); + + rb_yield( rb_assoc_new( rkey, rval ) ); } } diff --git a/lib/mdbx/database.rb b/lib/mdbx/database.rb index 4f53dfc..43ae417 100644 --- a/lib/mdbx/database.rb +++ b/lib/mdbx/database.rb @@ -364,6 +364,34 @@ class MDBX::Database protected ######### + + ### Safely serialize a value, closing any open transaction and re-raising + ### if necessary. + ### + def serialize( val ) + return val unless self.serializer + return self.serializer.call( val ) + + rescue => err + self.close_transaction( false ) + raise err + end + + + ### Safely deserialize a value, closing any open transaction and re-raising + ### if necessary. + ### + def deserialize( val ) + return val unless self.deserializer + return self.deserializer.call( val ) + + rescue => err + self.close_transaction( false ) + raise err + end + + + ### Yield and return the block, opening a snapshot first if ### there isn't already a transaction in progress. Closes ### the snapshot if this method opened it. diff --git a/spec/mdbx/database_spec.rb b/spec/mdbx/database_spec.rb index e10fe0e..e228937 100644 --- a/spec/mdbx/database_spec.rb +++ b/spec/mdbx/database_spec.rb @@ -265,12 +265,22 @@ RSpec.describe( MDBX::Database ) do it "reverts back to previous collection if the block raises an exception" do expect( db.collection ).to be_nil - begin + expect { db.collection( 'bucket1' ) do db.collection( 'bucket2' ) { raise "ka-bloooey!" } end - rescue - end + }.to raise_error( RuntimeError, /ka-bloooey!/ ) + expect( db.collection ).to be_nil + end + + it "reverts back to previous collection if serialization fails" do + db.serializer = ->( v ) { raise "ka-bloooey!" } + expect( db.collection ).to be_nil + expect { + db.collection( 'bucket1' ) do + db.collection( 'bucket2' ) {|sdb| sdb['foo'] = 1 } + end + }.to raise_error( RuntimeError, /ka-bloooey!/ ) expect( db.collection ).to be_nil end