Class: MDBX::Database

Inherits:
Data
  • Object
show all
Defined in:
lib/mdbx/database.rb,
ext/mdbx_ext/database.c

Overview

The primary class for interacting with an MDBX database.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#deserializerObject

A Proc for automatically deserializing values. Defaults to Marshal.load.



128
129
130
# File 'lib/mdbx/database.rb', line 128

def deserializer
  @deserializer
end

#optionsObject (readonly)

Options used when instantiating this database handle.



117
118
119
# File 'lib/mdbx/database.rb', line 117

def options
  @options
end

#pathObject (readonly)

The path on disk of the database.



120
121
122
# File 'lib/mdbx/database.rb', line 120

def path
  @path
end

#serializerObject

A Proc for automatically serializing values. Defaults to Marshal.dump.



124
125
126
# File 'lib/mdbx/database.rb', line 124

def serializer
  @serializer
end

Class Method Details

.open(*args, &block) ⇒ Object

call-seq:

MDBX::Database.open( path ) => db
MDBX::Database.open( path, options ) => db

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.

Options

Unless otherwise mentioned, option keys are symbols, and values are boolean.

:mode

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.

:max_collections

Set the maximum number of “subdatabase” collections allowed. By default, collection support is disabled.

:max_readers

Set the maximum number of allocated simultaneous reader slots.

:max_size

Set an upper boundary (in bytes) for the database map size. The default is 10485760 bytes.

:nosubdir

When creating a new database, don’t put the data and lock file under a dedicated subdirectory.

:readonly

Reject any write attempts while using this database handle.

:exclusive

Access is restricted to the first opening process. Other attempts to use this database (even in readonly mode) are denied.

:compat

Skip compatibility checks when opening an in-use database with unknown or mismatched flag values.

:writemap

Trade safety for speed for databases that fit within available memory. (See MDBX documentation for details.)

:no_threadlocal

Parallelize read-only transactions across threads. Writes are always thread local. (See MDBX documentatoin for details.)

:no_readahead

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.

:no_memory_init

Skip initializing malloc’ed memory to zeroes before writing.

:coalesce

Attempt to coalesce items for the garbage collector, potentialy increasing the chance of unallocating storage earlier.

:lifo_reclaim

Recycle garbage collected items via LIFO, instead of FIFO. Depending on underlying hardware (disk write-back cache), this could increase write performance.

:no_metasync

A system crash may sacrifice the last commit for a potentially large write performance increase. Database integrity is maintained.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/mdbx/database.rb', line 94

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

Instance Method Details

#[]('key') ⇒ Object

Return a single value for key immediately.



607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
# File 'ext/mdbx_ext/database.c', line 607

VALUE
rmdbx_get_val( VALUE self, VALUE key )
{
  int rc;
  UNWRAP_DB( self, db );

  CHECK_HANDLE;
  rmdbx_open_txn( db, MDBX_TXN_RDONLY );

  MDBX_val ckey = rmdbx_key_for( key );
  MDBX_val data;
  VALUE rv;
  rc = mdbx_get( db->txn, db->dbi, &ckey, &data );
  rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );

  switch ( rc ) {
    case MDBX_SUCCESS:
      rv = rb_str_new( data.iov_base, data.iov_len );
      return rmdbx_deserialize( self, rv );

    case MDBX_NOTFOUND:
      return Qnil;

    default:
      rmdbx_close( self );
      rb_raise( rmdbx_eDatabaseError, "Unable to fetch value: (%d) %s", rc, mdbx_strerror(rc) );
  }
}

#[]=('key') ⇒ Object

Set a single value for key. If the value is nil, the key is removed.



643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
# File 'ext/mdbx_ext/database.c', line 643

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 );

  // FIXME: DUPSORT is enabled -- different api?
  // See:  MDBX_NODUPDATA / MDBX_NOOVERWRITE
  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 );
    rc = mdbx_replace( db->txn, db->dbi, &ckey, &data, &old, 0 );
  }

  rmdbx_close_txn( db, RMDBX_TXN_COMMIT );

  switch ( rc ) {
    case MDBX_SUCCESS:
      return val;
    case MDBX_NOTFOUND:
      return Qnil;
    default:
      rb_raise( rmdbx_eDatabaseError, "Unable to store value: (%d) %s", rc, mdbx_strerror(rc) );
  }
}

#clearObject

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.



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'ext/mdbx_ext/database.c', line 282

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;
}

#closeObject

Cleanly close an opened database.



83
84
85
86
87
88
89
# File 'ext/mdbx_ext/database.c', line 83

VALUE
rmdbx_close( VALUE self )
{
  UNWRAP_DB( self, db );
  rmdbx_close_all( db );
  return Qtrue;
}

#closed?false

Predicate: return true if the database environment is closed.

Returns:

  • (false)


98
99
100
101
102
103
# File 'ext/mdbx_ext/database.c', line 98

VALUE
rmdbx_closed_p( VALUE self )
{
  UNWRAP_DB( self, db );
  return db->state.open == 1 ? Qfalse : Qtrue;
}

#collection(name = nil) ⇒ Object Also known as: namespace

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


147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/mdbx/database.rb', line 147

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

#commitObject Also known as: save

Close any open transaction, writing all changes.



217
218
219
# File 'lib/mdbx/database.rb', line 217

def commit
  return self.close_transaction( true )
end

#delete(key, &block) ⇒ Object

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.



279
280
281
282
283
284
285
# File 'lib/mdbx/database.rb', line 279

def delete( key, &block )
  val = self[ key ]
  return block.call( key ) if block_given? && val.nil?

  self[ key ] = nil
  return val
end

#drop(collection) ⇒ Object

Destroy a collection. You must be in the top level database to call this method.



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'ext/mdbx_ext/database.c', line 306

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;
}

#each_key {|key| ... } ⇒ self

Calls the block once for each key, returning self. A transaction must be opened prior to use.

Yields:

  • (key)

Returns:

  • (self)


422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
# File 'ext/mdbx_ext/database.c', line 422

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;
}

#each_pair {|key, value| ... } ⇒ self Also known as: each

Calls the block once for each key and value, returning self. A transaction must be opened prior to use.

Yields:

  • (key, value)

Returns:

  • (self)


522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
# File 'ext/mdbx_ext/database.c', line 522

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;
}

#each_value {|value| ... } ⇒ self

Calls the block once for each value, returning self. A transaction must be opened prior to use.

Yields:

  • (value)

Returns:

  • (self)


471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# File 'ext/mdbx_ext/database.c', line 471

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;
}

#empty?Boolean

Returns true if the current collection has no data.

Returns:

  • (Boolean)


248
249
250
# File 'lib/mdbx/database.rb', line 248

def empty?
  return self.size.zero?
end

#fetch(key, &block) ⇒ Object

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.



258
259
260
261
262
263
264
265
266
# File 'lib/mdbx/database.rb', line 258

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

#in_transaction?false

Predicate: return true if a transaction (or snapshot) is currently open.

Returns:

  • (false)


113
114
115
116
117
118
# File 'ext/mdbx_ext/database.c', line 113

VALUE
rmdbx_in_transaction_p( VALUE self )
{
  UNWRAP_DB( self, db );
  return db->txn ? Qtrue : Qfalse;
}

#include?('key') ⇒ Boolean Also known as: has_key?

Returns true if the current collection contains key.

Returns:

  • (Boolean)


574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
# File 'ext/mdbx_ext/database.c', line 574

VALUE
rmdbx_include( VALUE self, VALUE key )
{
  int rc;
  UNWRAP_DB( self, db );

  CHECK_HANDLE;
  rmdbx_open_txn( db, MDBX_TXN_RDONLY );

  MDBX_val ckey = rmdbx_key_for( key );
  MDBX_val data;
  rc = mdbx_get( db->txn, db->dbi, &ckey, &data );
  rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );

  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) );
  }
}

#keysObject

Returns a new Array containing all keys in the collection.



290
291
292
293
294
# File 'lib/mdbx/database.rb', line 290

def keys
  return self.conditional_snapshot do
    self.each_key.to_a
  end
end

#lengthInteger Also known as: size

Returns the count of keys in the currently selected collection.

Returns:

  • (Integer)


549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
# File 'ext/mdbx_ext/database.c', line 549

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;
}

#mainObject

Switch to the top-level collection.



164
165
166
# File 'lib/mdbx/database.rb', line 164

def main
  return self.set_subdb( nil )
end

#reopenObject

Open the DB environment handle.



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'ext/mdbx_ext/database.c', line 125

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;
}

#rollbackObject Also known as: abort

Close any open transaction, abandoning all changes.



209
210
211
# File 'lib/mdbx/database.rb', line 209

def rollback
  return self.close_transaction( false )
end

#slice(*keys) ⇒ Object

Returns a new Hash object containing the entries for the given keys. Any given keys that are not found are ignored.



300
301
302
303
304
305
306
307
# File 'lib/mdbx/database.rb', line 300

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

#snapshot(&block) ⇒ Object

Open a new mdbx read only snapshot. In block form, the snapshot is automatically closed when the block ends.



202
203
204
# File 'lib/mdbx/database.rb', line 202

def snapshot( &block )
  self.transaction( commit: false, &block )
end

#statisticsObject

Return a hash of various metadata for the current database.



336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/mdbx/database.rb', line 336

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

#to_aObject

Return the entirety of database contents as an Array of array pairs.



230
231
232
233
234
# File 'lib/mdbx/database.rb', line 230

def to_a
  return self.conditional_snapshot do
    self.each_pair.to_a
  end
end

#to_hObject

Return the entirety of database contents as a Hash.



239
240
241
242
243
# File 'lib/mdbx/database.rb', line 239

def to_h
  return self.conditional_snapshot do
    self.each_pair.to_h
  end
end

#transaction(commit: true, &block) ⇒ Object

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.



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/mdbx/database.rb', line 179

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

#valuesObject

Returns a new Array containing all values in the collection.



312
313
314
315
316
# File 'lib/mdbx/database.rb', line 312

def values
  return self.conditional_snapshot do
    self.each_value.to_a
  end
end

#values_at(*keys) ⇒ Object

Returns a new Array containing values for the given keys.



321
322
323
324
325
326
327
# File 'lib/mdbx/database.rb', line 321

def values_at( *keys )
  return self.conditional_snapshot do
    keys.each_with_object( [] ) do |key, acc|
      acc << self[ key ]
    end
  end
end