# File lib/mdbx/database.rb, line 166
def main
return self.set_subdb( nil )
end
The primary class for interacting with an MDBX
database.
A Proc for automatically deserializing values. Defaults to Marshal.load
.
Options used when instantiating this database handle.
The path on disk of the database.
A Proc for automatically serializing values. Defaults to Marshal.dump
.
Open an existing (or create a new) mdbx database at filesystem path
. In block form, the database is automatically closed when the block exits.
MDBX::Database.open( path, options ) do |db| db[ 'key' ] = value end # closed!
Passing options modify various database behaviors. See the libmdbx documentation for detailed information.
Unless otherwise mentioned, option keys are symbols, and values are boolean.
Attempt to coalesce items for the garbage collector, potentialy increasing the chance of unallocating storage earlier.
Skip compatibility checks when opening an in-use database with unknown or mismatched flag values.
Access is restricted to the first opening process. Other attempts to use this database (even in readonly mode) are denied.
Recycle garbage collected items via LIFO, instead of FIFO. Depending on underlying hardware (disk write-back cache), this could increase write performance.
Set the maximum number of “subdatabase” collections allowed. By default, collection support is disabled.
Set the maximum number of allocated simultaneous reader slots.
Set an upper boundary (in bytes) for the database map size. The default is 10485760 bytes.
Whe creating a new database, set permissions to this 4 digit octal number. Defaults to ‘0644`. Set to `0` to never automatically create a new file, only opening existing databases.
Skip initializing malloc’ed memory to zeroes before writing.
A system crash may sacrifice the last commit for a potentially large write performance increase. Database
integrity is maintained.
When creating a new database, don’t put the data and lock file under a dedicated subdirectory.
Disable all use of OS readahead. Potentially useful for random reads wunder low memory conditions. Default behavior is to dynamically choose when to use or omit readahead.
Parallelize read-only transactions across threads. Writes are always thread local. (See MDBX
documentatoin for details.)
Reject any write attempts while using this database handle.
Trade safety for speed for databases that fit within available memory. (See MDBX
documentation for details.)
# File lib/mdbx/database.rb, line 96
def self::open( *args, &block )
db = new( *args )
db.serializer = ->( v ) { Marshal.dump( v ) }
db.deserializer = ->( v ) { Marshal.load( v ) }
if block_given?
begin
yield db
ensure
db.close
end
end
return db
end
Return a single value for key
immediately.
VALUE
rmdbx_get_val( VALUE self, VALUE key )
{
UNWRAP_DB( self, db );
CHECK_HANDLE();
rmdbx_open_txn( db, MDBX_TXN_RDONLY );
MDBX_val ckey;
MDBX_val data;
rmdbx_key_for( key, &ckey );
int rc = mdbx_get( db->txn, db->dbi, &ckey, &data );
rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
xfree( ckey.iov_base );
VALUE rv;
switch ( rc ) {
case MDBX_SUCCESS:
rv = rb_str_new( data.iov_base, data.iov_len );
return rb_funcall( self, rb_intern("deserialize"), 1, rv );
case MDBX_NOTFOUND:
return Qnil;
default:
rmdbx_close( self );
rb_raise( rmdbx_eDatabaseError, "Unable to fetch value: (%d) %s", rc, mdbx_strerror(rc) );
}
}
Set a single value for key
. If the value is nil
, the key is removed.
VALUE
rmdbx_put_val( VALUE self, VALUE key, VALUE val )
{
int rc;
UNWRAP_DB( self, db );
CHECK_HANDLE();
rmdbx_open_txn( db, MDBX_TXN_READWRITE );
MDBX_val ckey;
rmdbx_key_for( key, &ckey );
if ( NIL_P(val) ) { /* remove if set to nil */
rc = mdbx_del( db->txn, db->dbi, &ckey, NULL );
}
else {
MDBX_val old;
MDBX_val data;
rmdbx_val_for( self, val, &data );
rc = mdbx_replace( db->txn, db->dbi, &ckey, &data, &old, 0 );
xfree( data.iov_base );
}
rmdbx_close_txn( db, RMDBX_TXN_COMMIT );
xfree( ckey.iov_base );
switch ( rc ) {
case MDBX_SUCCESS:
return val;
case MDBX_NOTFOUND:
return Qnil;
default:
rb_raise( rmdbx_eDatabaseError, "Unable to update value: (%d) %s", rc, mdbx_strerror(rc) );
}
}
Empty the current collection on disk. If collections are not enabled or the database handle is set to the top-level (main) db - this deletes *all records* from the database.
VALUE
rmdbx_clear( VALUE self )
{
UNWRAP_DB( self, db );
rmdbx_open_txn( db, MDBX_TXN_READWRITE );
int rc = mdbx_drop( db->txn, db->dbi, false );
if ( rc != MDBX_SUCCESS )
rb_raise( rmdbx_eDatabaseError, "mdbx_drop: (%d) %s", rc, mdbx_strerror(rc) );
rmdbx_close_txn( db, RMDBX_TXN_COMMIT );
return Qnil;
}
Cleanly close an opened database.
VALUE
rmdbx_close( VALUE self )
{
UNWRAP_DB( self, db );
rmdbx_close_all( db );
return Qtrue;
}
Predicate: return true if the database environment is closed.
VALUE
rmdbx_closed_p( VALUE self )
{
UNWRAP_DB( self, db );
return db->state.open == 1 ? Qfalse : Qtrue;
}
Gets or sets the sub-database “collection” that read/write operations apply to. If a block is passed, the collection automatically reverts to the prior collection when it exits.
db.collection #=> (collection name, or nil if in main) db.collection( 'collection_name' ) #=> db db.collection( 'collection_name' ) do [ ... ] end # reverts to the previous collection name
# File lib/mdbx/database.rb, line 149
def collection( name=nil )
current = self.get_subdb
return current unless name
self.set_subdb( name.to_s )
yield( self ) if block_given?
return self
ensure
self.set_subdb( current ) if name && block_given?
end
Close any open transaction, writing all changes.
# File lib/mdbx/database.rb, line 219
def commit
return self.close_transaction( true )
end
Deletes the entry for the given key and returns its associated value. If no block is given and key is found, deletes the entry and returns the associated value. If no block given and key is not found, returns nil.
If a block is given and key is found, ignores the block, deletes the entry, and returns the associated value. If a block is given and key is not found, calls the block and returns the block’s return value.
# File lib/mdbx/database.rb, line 281
def delete( key, &block )
val = self[ key ]
return block.call( key ) if block_given? && val.nil?
self[ key ] = nil
return val
end
Destroy a collection. You must be in the top level database to call this method.
VALUE
rmdbx_drop( VALUE self, VALUE name )
{
UNWRAP_DB( self, db );
/* Provide a friendlier error message if max_collections is 0. */
if ( db->settings.max_collections == 0 )
rb_raise( rmdbx_eDatabaseError, "Unable to drop collection: collections are not enabled." );
/* All transactions must be closed when dropping a database. */
if ( db->txn )
rb_raise( rmdbx_eDatabaseError, "Unable to drop collection: transaction open" );
/* A drop can only be performed from the top-level database. */
if ( db->subdb != NULL )
rb_raise( rmdbx_eDatabaseError, "Unable to drop collection: switch to top-level db first" );
name = rb_funcall( name, rb_intern("to_s"), 0 );
db->subdb = StringValueCStr( name );
rmdbx_close_dbi( db ); /* ensure we're reopening within the new subdb */
rmdbx_open_txn( db, MDBX_TXN_READWRITE );
int rc = mdbx_drop( db->txn, db->dbi, true );
if ( rc != MDBX_SUCCESS )
rb_raise( rmdbx_eDatabaseError, "mdbx_drop: (%d) %s", rc, mdbx_strerror(rc) );
rmdbx_close_txn( db, RMDBX_TXN_COMMIT );
/* Reset the current collection to the top level. */
db->subdb = NULL;
rmdbx_close_dbi( db ); /* ensure next access is not in the defunct subdb */
return self;
}
Calls the block once for each key, returning self. A transaction must be opened prior to use.
VALUE
rmdbx_each_key( VALUE self )
{
UNWRAP_DB( self, db );
int state;
CHECK_HANDLE();
rmdbx_open_cursor( db );
RETURN_ENUMERATOR( self, 0, 0 );
rb_protect( rmdbx_each_key_i, self, &state );
mdbx_cursor_close( db->cursor );
db->cursor = NULL;
if ( state ) rb_jump_tag( state );
return self;
}
Calls the block once for each key and value, returning self. A transaction must be opened prior to use.
VALUE
rmdbx_each_pair( VALUE self )
{
UNWRAP_DB( self, db );
int state;
CHECK_HANDLE();
rmdbx_open_cursor( db );
RETURN_ENUMERATOR( self, 0, 0 );
rb_protect( rmdbx_each_pair_i, self, &state );
mdbx_cursor_close( db->cursor );
db->cursor = NULL;
if ( state ) rb_jump_tag( state );
return self;
}
Calls the block once for each value, returning self. A transaction must be opened prior to use.
VALUE
rmdbx_each_value( VALUE self )
{
UNWRAP_DB( self, db );
int state;
CHECK_HANDLE();
rmdbx_open_cursor( db );
RETURN_ENUMERATOR( self, 0, 0 );
rb_protect( rmdbx_each_value_i, self, &state );
mdbx_cursor_close( db->cursor );
db->cursor = NULL;
if ( state ) rb_jump_tag( state );
return self;
}
Returns true
if the current collection has no data.
# File lib/mdbx/database.rb, line 250
def empty?
return self.size.zero?
end
Returns the value for the given key, if found. If key is not found and no block was given, returns nil. If key is not found and a block was given, yields key to the block and returns the block’s return value.
# File lib/mdbx/database.rb, line 260
def fetch( key, &block )
val = self[ key ]
if block_given?
return block.call( key ) if val.nil?
else
return val if val
raise KeyError, "key not found: %p" % [ key ]
end
end
Predicate: return true if a transaction (or snapshot) is currently open.
VALUE
rmdbx_in_transaction_p( VALUE self )
{
UNWRAP_DB( self, db );
return db->txn ? Qtrue : Qfalse;
}
Returns true if the current collection contains key
.
VALUE
rmdbx_include( VALUE self, VALUE key )
{
UNWRAP_DB( self, db );
CHECK_HANDLE();
rmdbx_open_txn( db, MDBX_TXN_RDONLY );
MDBX_val ckey;
MDBX_val data;
rmdbx_key_for( key, &ckey );
int rc = mdbx_get( db->txn, db->dbi, &ckey, &data );
rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
xfree( ckey.iov_base );
switch ( rc ) {
case MDBX_SUCCESS:
return Qtrue;
case MDBX_NOTFOUND:
return Qfalse;
default:
rmdbx_close( self );
rb_raise( rmdbx_eDatabaseError, "Unable to fetch key: (%d) %s", rc, mdbx_strerror(rc) );
}
}
Returns a new Array containing all keys in the collection.
# File lib/mdbx/database.rb, line 292
def keys
return self.conditional_snapshot do
self.each_key.to_a
end
end
Returns the count of keys in the currently selected collection.
VALUE
rmdbx_length( VALUE self )
{
UNWRAP_DB( self, db );
MDBX_stat mstat;
CHECK_HANDLE();
rmdbx_open_txn( db, MDBX_TXN_RDONLY );
int rc = mdbx_dbi_stat( db->txn, db->dbi, &mstat, sizeof(mstat) );
if ( rc != MDBX_SUCCESS )
rb_raise( rmdbx_eDatabaseError, "mdbx_dbi_stat: (%d) %s", rc, mdbx_strerror(rc) );
VALUE rv = LONG2FIX( mstat.ms_entries );
rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
return rv;
}
Switch to the top-level collection.
# File lib/mdbx/database.rb, line 166
def main
return self.set_subdb( nil )
end
Open the DB environment handle.
VALUE
rmdbx_open_env( VALUE self )
{
int rc;
UNWRAP_DB( self, db );
rmdbx_close_all( db );
/* Allocate an mdbx environment.
*/
rc = mdbx_env_create( &db->env );
if ( rc != MDBX_SUCCESS )
rb_raise( rmdbx_eDatabaseError, "mdbx_env_create: (%d) %s", rc, mdbx_strerror(rc) );
/* Set the maximum number of named databases for the environment. */
mdbx_env_set_maxdbs( db->env, db->settings.max_collections );
/* Customize the maximum number of simultaneous readers. */
if ( db->settings.max_readers )
mdbx_env_set_maxreaders( db->env, db->settings.max_readers );
/* Set an upper boundary (in bytes) for the database map size. */
if ( db->settings.max_size )
mdbx_env_set_geometry( db->env, -1, -1, db->settings.max_size, -1, -1, -1 );
rc = mdbx_env_open( db->env, db->path, db->settings.env_flags, db->settings.mode );
if ( rc != MDBX_SUCCESS ) {
rmdbx_close_all( db );
rb_raise( rmdbx_eDatabaseError, "mdbx_env_open: (%d) %s", rc, mdbx_strerror(rc) );
}
db->state.open = 1;
return Qtrue;
}
Close any open transaction, abandoning all changes.
# File lib/mdbx/database.rb, line 211
def rollback
return self.close_transaction( false )
end
Returns a new Hash object containing the entries for the given keys. Any given keys that are not found are ignored.
# File lib/mdbx/database.rb, line 302
def slice( *keys )
return self.conditional_snapshot do
keys.each_with_object( {} ) do |key, acc|
val = self[ key ]
acc[ key ] = val if val
end
end
end
Open a new mdbx read only snapshot. In block form, the snapshot is automatically closed when the block ends.
# File lib/mdbx/database.rb, line 204
def snapshot( &block )
self.transaction( commit: false, &block )
end
Return a hash of various metadata for the current database.
# File lib/mdbx/database.rb, line 338
def statistics
raw = self.raw_stats
# Place build options in their own hash.
#
build_opts = raw.delete( :build_options ).split.each_with_object( {} ) do |opt, acc|
key, val = opt.split( '=' )
acc[ key.to_sym ] = Integer( val ) rescue val
end
stats = {
build: {
compiler: raw.delete( :build_compiler ),
flags: raw.delete( :build_flags ),
options: build_opts,
target: raw.delete( :build_target )
}
}
stats.merge!( raw )
return stats
end
Return the entirety of database contents as an Array of array pairs.
# File lib/mdbx/database.rb, line 232
def to_a
return self.conditional_snapshot do
self.each_pair.to_a
end
end
Return the entirety of database contents as a Hash.
# File lib/mdbx/database.rb, line 241
def to_h
return self.conditional_snapshot do
self.each_pair.to_h
end
end
Open a new mdbx read/write transaction. In block form, the transaction is automatically committed when the block ends.
Raising a MDBX::Rollback
exception from within the block automatically rolls the transaction back.
# File lib/mdbx/database.rb, line 181
def transaction( commit: true, &block )
self.open_transaction( commit )
yield self if block_given?
return self
rescue MDBX::Rollback
commit = false
self.rollback
rescue
commit = false
self.rollback
raise
ensure
if block_given?
commit ? self.commit : self.rollback
end
end
Returns a new Array containing all values in the collection.
# File lib/mdbx/database.rb, line 314
def values
return self.conditional_snapshot do
self.each_value.to_a
end
end
Returns a new Array containing values for the given keys
.
# File lib/mdbx/database.rb, line 323
def values_at( *keys )
return self.conditional_snapshot do
keys.each_with_object( [] ) do |key, acc|
acc << self[ key ]
end
end
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.
# File lib/mdbx/database.rb, line 398
def conditional_snapshot
in_txn = self.in_transaction?
self.snapshot unless in_txn
return yield
ensure
self.abort unless in_txn
end
Safely deserialize a value, closing any open transaction and re-raising if necessary.
# File lib/mdbx/database.rb, line 383
def deserialize( val )
return val unless self.deserializer
return self.deserializer.call( val )
rescue => err
self.close_transaction( false )
raise err
end
Safely serialize a value, closing any open transaction and re-raising if necessary.
# File lib/mdbx/database.rb, line 370
def serialize( val )
return val unless self.serializer
return self.serializer.call( val )
rescue => err
self.close_transaction( false )
raise err
end