Class: MDBX::Database

Inherits:
Object
  • Object
show all
Extended by:
Loggability
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.



126
127
128
# File 'lib/mdbx/database.rb', line 126

def deserializer
  @deserializer
end

#optionsObject (readonly)

Options used when instantiating this database handle.



115
116
117
# File 'lib/mdbx/database.rb', line 115

def options
  @options
end

#pathObject (readonly)

The path on disk of the database.



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

def path
  @path
end

#serializerObject

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



122
123
124
# File 'lib/mdbx/database.rb', line 122

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.

:compatible

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

:exclusive

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

:lifo_reclaim

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

: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.

: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.

:no_memory_init

Skip initializing malloc’ed memory to zeroes before writing.

:no_metasync

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

:no_subdir

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

: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_stickythreads

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

:readonly

Reject any write attempts while using this database handle.

:writemap

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



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

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.



326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'ext/mdbx_ext/database.c', line 326

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

#[]=('key') ⇒ Object

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



364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'ext/mdbx_ext/database.c', line 364

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 data;
    rmdbx_val_for( self, val, &data );
    rc = mdbx_put( db->txn, db->dbi, &ckey, &data, 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) );
  }
}

#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.



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'ext/mdbx_ext/database.c', line 190

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 ) {
    rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
    rb_raise( rmdbx_eDatabaseError, "mdbx_drop: (%d) %s", rc, mdbx_strerror(rc) );
  }

  rmdbx_close_txn( db, RMDBX_TXN_COMMIT );

  return Qnil;
}

#closetrue

Cleanly close an opened database.

Returns:

  • (true)


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

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)


96
97
98
99
100
101
# File 'ext/mdbx_ext/database.c', line 96

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


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

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.



215
216
217
# File 'lib/mdbx/database.rb', line 215

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.



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

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.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'ext/mdbx_ext/database.c', line 216

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 ) {
    rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
    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 */

  /* Force populate the new db->dbi handle.  Under 0.12.x, getting a
   * 'permission denied' doing this for the first access with a RDONLY
   * for some reason. */
  rmdbx_open_txn( db, MDBX_TXN_READWRITE );
  rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );

  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)


637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
# File 'ext/mdbx_ext/database.c', line 637

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)


740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
# File 'ext/mdbx_ext/database.c', line 740

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)


686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
# File 'ext/mdbx_ext/database.c', line 686

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)


246
247
248
# File 'lib/mdbx/database.rb', line 246

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.



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

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)


460
461
462
463
464
465
# File 'ext/mdbx_ext/database.c', line 460

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)


291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'ext/mdbx_ext/database.c', line 291

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

#keysObject

Returns a new Array containing all keys in the collection.



288
289
290
291
292
# File 'lib/mdbx/database.rb', line 288

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)


266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'ext/mdbx_ext/database.c', line 266

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.



162
163
164
# File 'lib/mdbx/database.rb', line 162

def main
  return self.set_subdb( nil )
end

#reopenObject

Open the DB environment handle.



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'ext/mdbx_ext/database.c', line 141

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

  /* Force populate the db->dbi handle.  Under 0.12.x, getting a
   * 'permission denied' doing this for the first access with a RDONLY
   * for some reason. */
  rmdbx_open_txn( db, MDBX_TXN_READWRITE );
  rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );

  db->state.open = 1;
  return Qtrue;
}

#rollbackObject Also known as: abort

Close any open transaction, abandoning all changes.



207
208
209
# File 'lib/mdbx/database.rb', line 207

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.



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

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.



200
201
202
# File 'lib/mdbx/database.rb', line 200

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

#statisticsObject

Return a hash of various metadata for the current database.



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

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.



228
229
230
231
232
# File 'lib/mdbx/database.rb', line 228

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.



237
238
239
240
241
# File 'lib/mdbx/database.rb', line 237

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.



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

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.



310
311
312
313
314
# File 'lib/mdbx/database.rb', line 310

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.



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

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