class LMDB::Database
An LMDB
Database
is a table of key-value pairs. It is stored as part of the {Environment}.
By default, each key in a Database
maps to one value. However, a Database
can be configured at creation to allow duplicate keys, in which case one key will map to multiple values.
A Database
stores the keys in a sorted order. The order can also be set with options when the database is created.
The basic operations on a database are to {#put}, {#get}, and {#delete} records. One can also iterate through the records in a database using a {Cursor}.
@example Typical usage
env = LMDB.new "databasedir" db = env.database "databasename" db.put "key1", "value1" db.put "key2", "value2" db.get "key1" #=> "value1" env.close
Public Instance Methods
Retrieve the value of a record from a database @param key the record key to retrieve @return value of the record for that key, or nil if there is
no record with that key
@see get(key)
# File lib/lmdb/database.rb, line 29 def [](key) get(key) end
Set (write or update) a record in a database. @param key key for the record @param value the value of the record @return returns the value of the record @see put(key, value)
@example
db['a'] = 'b' #=> 'b' db['b'] = 1234 #=> 1234 db['a'] #=> 'b'
# File lib/lmdb/database.rb, line 42 def []=(key, value) put key, value value end
Return the cardinality (number of duplicates) of a given key. Works whether :dupsort
is set or not. @param key [#to_s] The key in question. @return [Integer] The number of entries under the key.
# File lib/lmdb/database.rb, line 100 def cardinality(key) ret = 0 maybe_txn true do # env.transaction do if get key if dupsort? cursor do |c| c.set key ret = c.count end else ret = 1 end end end ret end
@overload clear
Empty out the database @return nil @note The clear happens transactionally.
static VALUE database_clear(VALUE self) { DATABASE(self, database); if (!active_txn(database->env)) return call_with_transaction(database->env, self, "clear", 0, 0, 0); check(mdb_drop(need_txn(database->env), database->dbi, 0)); return Qnil; }
@overload cursor
Create a cursor to iterate through a database. Uses current transaction, if any. Otherwise, if called with a block, creates a new transaction for the scope of the block. Otherwise, fails. @see Cursor @yield [cursor] A block to be executed with the cursor. @yieldparam cursor [Cursor] The cursor to be used to iterate @example db = env.database "abc" db.cursor do |c| key, value = c.next puts "#{key}: #{value}" end
static VALUE database_cursor(VALUE self) { DATABASE(self, database); if (!active_txn(database->env)) { if (!rb_block_given_p()) { rb_raise(cError, "Must call with block or active transaction."); } return call_with_transaction(database->env, self, "cursor", 0, 0, 0); } MDB_cursor* cur; check(mdb_cursor_open(need_txn(database->env), database->dbi, &cur)); Cursor* cursor; VALUE vcur = Data_Make_Struct(cCursor, Cursor, cursor_mark, cursor_free, cursor); cursor->cur = cur; cursor->db = self; if (rb_block_given_p()) { int exception; VALUE ret = rb_protect(rb_yield, vcur, &exception); if (exception) { cursor_close(vcur); rb_jump_tag(exception); } cursor_close(vcur); return ret; } else { VALUE vtxn = environment_active_txn(database->env); if (NIL_P(vtxn)) { rb_fatal("Internal error: transaction finished unexpectedly."); } else { TRANSACTION(vtxn, txn); rb_ary_push(txn->cursors, vcur); } } return vcur; }
@overload delete(key, value=nil)
Deletes records from the database. This function removes key/data pairs from the database. If the database does not support sorted duplicate data items (:dupsort
) the value parameter is ignored. If the database supports sorted duplicates and the value parameter is nil
, all of the duplicate data items for the key will be deleted. Otherwise, if the data parameter is non-nil only the matching data item will be deleted.
@param key The key of the record to delete. @param value The optional value of the record to delete. @raise [Error] if the specified key/value pair is not in the database.
static VALUE database_delete(int argc, VALUE *argv, VALUE self) { DATABASE(self, database); if (!active_txn(database->env)) return call_with_transaction(database->env, self, "delete", argc, argv, 0); VALUE vkey, vval; rb_scan_args(argc, argv, "11", &vkey, &vval); vkey = StringValue(vkey); MDB_val key; key.mv_size = RSTRING_LEN(vkey); key.mv_data = RSTRING_PTR(vkey); if (NIL_P(vval)) { check(mdb_del(need_txn(database->env), database->dbi, &key, 0)); } else { vval = StringValue(vval); MDB_val value; value.mv_size = RSTRING_LEN(vval); value.mv_data = RSTRING_PTR(vval); check(mdb_del(need_txn(database->env), database->dbi, &key, &value)); } return Qnil; }
Delete the key (and optional value pair) if it exists; do not complain about missing keys. @param key [#to_s] The key. @param value [#to_s] The optional value.
# File lib/lmdb/database.rb, line 139 def delete?(key, value = nil) delete key, value if has? key, value end
@overload drop
Remove a database from the environment. @return nil @note The drop happens transactionally.
static VALUE database_drop(VALUE self) { DATABASE(self, database); if (!active_txn(database->env)) return call_with_transaction(database->env, self, "drop", 0, 0, 0); check(mdb_drop(need_txn(database->env), database->dbi, 1)); return Qnil; }
@overload dupfixed?
Returns whether the database is in +:dupfixed+ mode. @return [true, false]
static VALUE database_is_dupfixed(VALUE self) { DATABASE(self, database); if (!active_txn(database->env)) return call_with_transaction(database->env, self, "dupfixed?", 0, 0, MDB_RDONLY); unsigned int flags; check(mdb_dbi_flags(need_txn(database->env), database->dbi, &flags)); return (flags & MDB_DUPFIXED) == 0 ? Qfalse : Qtrue; }
@overload dupsort?
Returns whether the database is in +:dupsort+ mode. @return [true, false]
static VALUE database_is_dupsort(VALUE self) { DATABASE(self, database); if (!active_txn(database->env)) return call_with_transaction(database->env, self, "dupsort?", 0, 0, MDB_RDONLY); unsigned int flags; check(mdb_dbi_flags(need_txn(database->env), database->dbi, &flags)); return (flags & MDB_DUPSORT) == 0 ? Qfalse : Qtrue; }
Iterate through the records in a database @yield [i] Gives a record [key, value] to the block @yieldparam [Array] i The key, value pair for each record @example
db.each do |record| key, value = record puts "at #{key}: #{value}" end
# File lib/lmdb/database.rb, line 13 def each maybe_txn true do # env.transaction do cursor do |c| while i = c.next yield(i) end end end end
Iterate over each key in the database, skipping over duplicate records.
@yield key [String] the next key in the database. @return [Enumerator] in lieu of a block.
# File lib/lmdb/database.rb, line 57 def each_key(&block) return enum_for :each_key unless block_given? maybe_txn true do #env.transaction do cursor do |c| while (rec = c.next true) yield rec.first end end end end
Iterate over the duplicate values of a given key, using an implicit cursor. Works whether :dupsort
is set or not.
@param key [#to_s] The key in question. @yield value [String] the next value associated with the key. @return [Enumerator] in lieu of a block.
# File lib/lmdb/database.rb, line 75 def each_value(key, &block) return enum_for :each_value, key unless block_given? value = get(key) or return unless dupsort? yield value return end maybe_txn true do # env.transaction do cursor do |c| method = :set while rec = c.send(method, key) method = :next_range yield rec[1] end end end end
@overload env
@return [Environment] the environment to which this database belongs.
static VALUE database_env(VALUE self) { DATABASE(self, database); return database->env; }
@overload flags
Return the flags used to open the database. @return [Hash] The flags.
static VALUE database_get_flags(VALUE self) { DATABASE(self, database); if (!active_txn(database->env)) return call_with_transaction(database->env, self, "flags", 0, 0, MDB_RDONLY); unsigned int flags; check(mdb_dbi_flags(need_txn(database->env), database->dbi, &flags)); return flags2hash(flags); }
@overload get(key)
Retrieves one value associated with this key. This function retrieves key/data pairs from the database. If the database supports duplicate keys (+:dupsort+) then the first data item for the key will be returned. Retrieval of other items requires the use of {#cursor}. @param key The key of the record to retrieve.
static VALUE database_get(VALUE self, VALUE vkey) { DATABASE(self, database); if (!active_txn(database->env)) return call_with_transaction(database->env, self, "get", 1, &vkey, MDB_RDONLY); vkey = StringValue(vkey); MDB_val key, value; key.mv_size = RSTRING_LEN(vkey); key.mv_data = RSTRING_PTR(vkey); int ret = mdb_get(need_txn(database->env), database->dbi, &key, &value); if (ret == MDB_NOTFOUND) return Qnil; check(ret); return rb_str_new(value.mv_data, value.mv_size); }
Test if the database has a given key (or, if opened in :dupsort
, value)
# File lib/lmdb/database.rb, line 120 def has?(key, value = nil) v = get(key) or return false return true if value.nil? or value.to_s == v return false unless dupsort? ret = false # read-only txn was having trouble being nested inside a read-write maybe_txn true do # env.transaction true do # env.transaction do cursor { |c| ret = !!c.set(key, value) } end ret end
Get the keys as an array. @return [Array] of keys.
# File lib/lmdb/database.rb, line 49 def keys each_key.to_a end
@overload put(key, value, options)
Stores items into a database. This function stores key/value pairs in the database. The default behavior is to enter the new key/value pair, replacing any previously existing key if duplicates are disallowed, or adding a duplicate data item if duplicates are allowed (+:dupsort+). @param key The key of the record to set @param value The value to insert for this key @option options [Boolean] :nodupdata Enter the new key/value pair only if it does not already appear in the database. This flag may only be specified if the database was opened with +:dupsort+. The function will raise an {Error} if the key/data pair already appears in the database. @option options [Boolean] :nooverwrite Enter the new key/value pair only if the key does not already appear in the database. The function will raise an {Error] if the key already appears in the database, even if the database supports duplicates (+:dupsort+). @option options [Boolean] :append Append the given key/data pair to the end of the database. No key comparisons are performed. This option allows fast bulk loading when keys are already known to be in the correct order. Loading unsorted keys with this flag will cause data corruption. @option options [Boolean] :appenddup As above, but for sorted dup data.
static VALUE database_put(int argc, VALUE *argv, VALUE self) { DATABASE(self, database); if (!active_txn(database->env)) return call_with_transaction(database->env, self, "put", argc, argv, 0); VALUE vkey, vval, option_hash = Qnil; #ifdef RB_SCAN_ARGS_KEYWORDS rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS, argc, argv, "20:", &vkey, &vval, &option_hash); #else rb_scan_args(argc, argv, "20:", &vkey, &vval, &option_hash); #endif int flags = 0; if (!NIL_P(option_hash)) rb_hash_foreach(option_hash, (int (*)(ANYARGS))database_put_flags, (VALUE)&flags); vkey = StringValue(vkey); vval = StringValue(vval); MDB_val key, value; key.mv_size = RSTRING_LEN(vkey); key.mv_data = RSTRING_PTR(vkey); value.mv_size = RSTRING_LEN(vval); value.mv_data = RSTRING_PTR(vval); check(mdb_put(need_txn(database->env), database->dbi, &key, &value, flags)); return Qnil; }
@return the number of records in this database
# File lib/lmdb/database.rb, line 144 def size stat[:entries] end
@overload stat
Return useful statistics about a database. @return [Hash] the statistics * +:psize+ Size of a database page * +:depth+ Depth (height) of the B-tree * +:branch_pages+ Number of internal (non-leaf) pages * +:leaf_pages+ Number of leaf pages * +:overflow_pages+ Number of overflow pages * +:entries+ Number of data items
static VALUE database_stat(VALUE self) { DATABASE(self, database); if (!active_txn(database->env)) return call_with_transaction(database->env, self, "stat", 0, 0, MDB_RDONLY); MDB_stat stat; check(mdb_stat(need_txn(database->env), database->dbi, &stat)); return stat2hash(&stat); }
Private Instance Methods
having trouble with read-only transactions embedded in read-write for some reason; can’t pin it down to test it yet so going to do this (djt; 2020-02-10)
# File lib/lmdb/database.rb, line 153 def maybe_txn(readonly, &block) if t = env.active_txn yield t else env.transaction !!readonly do |t| yield t end end end