Class: LMDB::Database

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
ext/lmdb_ext/lmdb_ext.c,
lib/lmdb/database.rb,
ext/lmdb_ext/lmdb_ext.c

Overview

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.

Examples:

Typical usage

env = LMDB.new "databasedir"
db = env.database "databasename"
db.put "key1", "value1"
db.put "key2", "value2"
db.get "key1"              #=> "value1"
env.close

Instance Method Summary collapse

Instance Method Details

#[](key) ⇒ Object

Retrieve the value of a record from a database

Parameters:

  • key

    the record key to retrieve

Returns:

  • value of the record for that key, or nil if there is no record with that key

See Also:

  • #get(key)


29
30
31
# File 'lib/lmdb/database.rb', line 29

def [](key)
  get(key)
end

#[]=(key, value) ⇒ Object

Set (write or update) a record in a database.

Examples:

db['a'] = 'b'     #=> 'b'
db['b'] = 1234    #=> 1234
db['a']           #=> 'b'

Parameters:

  • key

    key for the record

  • value

    the value of the record

Returns:

  • returns the value of the record

See Also:

  • value)


42
43
44
45
# File 'lib/lmdb/database.rb', line 42

def []=(key, value)
  put key, value
  value
end

#cardinality(key) ⇒ Integer

Return the cardinality (number of duplicates) of a given key. Works whether :dupsort is set or not.

Parameters:

  • key (#to_s)

    The key in question.

Returns:

  • (Integer)

    The number of entries under the key.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# 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

#clearObject

Note:

The clear happens transactionally.

Empty out the database

Returns:

  • nil



1051
1052
1053
1054
1055
1056
1057
# File 'ext/lmdb_ext/lmdb_ext.c', line 1051

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

#cursor {|cursor| ... } ⇒ Object

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.

Examples:

db = env.database "abc"
db.cursor do |c|
  key, value = c.next
  puts "#{key}: #{value}"
end

Yields:

  • (cursor)

    A block to be executed with the cursor.

Yield Parameters:

  • cursor (Cursor)

    The cursor to be used to iterate

See Also:



1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
# File 'ext/lmdb_ext/lmdb_ext.c', line 1237

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

#delete(key, value = nil) ⇒ Object

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.

Parameters:

  • key

    The key of the record to delete.

  • value

    The optional value of the record to delete.

Raises:

  • (Error)

    if the specified key/value pair is not in the database.



1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
# File 'ext/lmdb_ext/lmdb_ext.c', line 1164

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?(key, value = nil) ⇒ Boolean

Delete the key (and optional value pair) if it exists; do not complain about missing keys.

Parameters:

  • key (#to_s)

    The key.

  • value (#to_s) (defaults to: nil)

    The optional value.

Returns:

  • (Boolean)


141
142
143
# File 'lib/lmdb/database.rb', line 141

def delete?(key, value = nil)
  delete key, value if has? key, value
end

#dropObject

Note:

The drop happens transactionally.

Remove a database from the environment.

Returns:

  • nil



1037
1038
1039
1040
1041
1042
1043
# File 'ext/lmdb_ext/lmdb_ext.c', line 1037

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

#dupfixed?true, false

Returns whether the database is in :dupfixed mode.

Returns:

  • (true, false)


1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
# File 'ext/lmdb_ext/lmdb_ext.c', line 1020

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

#dupsort?true, false

Returns whether the database is in :dupsort mode.

Returns:

  • (true, false)


1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
# File 'ext/lmdb_ext/lmdb_ext.c', line 1004

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

#each {|i| ... } ⇒ Object

Iterate through the records in a database

Examples:

db.each do |record|
  key, value = record
  puts "at #{key}: #{value}"
end

Yields:

  • (i)

    Gives a record [key, value] to the block

Yield Parameters:

  • i (Array)

    The key, value pair for each record



13
14
15
16
17
18
19
20
21
22
# 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

#each_key(&block) ⇒ Enumerator

Iterate over each key in the database, skipping over duplicate records.

Returns:

  • (Enumerator)

    in lieu of a block.



57
58
59
60
61
62
63
64
65
66
67
# 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

#each_value(key, &block) ⇒ Enumerator

Iterate over the duplicate values of a given key, using an implicit cursor. Works whether :dupsort is set or not.

Parameters:

  • key (#to_s)

    The key in question.

Returns:

  • (Enumerator)

    in lieu of a block.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# 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

#envEnvironment

Returns the environment to which this database belongs.

Returns:

  • (Environment)

    the environment to which this database belongs.



1282
1283
1284
1285
# File 'ext/lmdb_ext/lmdb_ext.c', line 1282

static VALUE database_env(VALUE self) {
        DATABASE(self, database);
        return database->env;
}

#flagsHash

Return the flags used to open the database.

Returns:

  • (Hash)

    The flags.



987
988
989
990
991
992
993
994
995
# File 'ext/lmdb_ext/lmdb_ext.c', line 987

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

#get(key) ⇒ Object

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.

Parameters:

  • key

    The key of the record to retrieve.



1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
# File 'ext/lmdb_ext/lmdb_ext.c', line 1068

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

#has?(key, value = nil) ⇒ Boolean

Test if the database has a given key (or, if opened in :dupsort, value)

Returns:

  • (Boolean)


120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# 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?

  # warn "checking dupsort value `#{value.inspect}` (#{value.class})"

  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

#keysArray

Get the keys as an array.

Returns:

  • (Array)

    of keys.



49
50
51
# File 'lib/lmdb/database.rb', line 49

def keys
  each_key.to_a
end

#put(key, value, options) ⇒ Object

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

Parameters:

  • key

    The key of the record to set

  • value

    The value to insert for this key

Options Hash (options):

  • :nodupdata (Boolean)

    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.

  • :nooverwrite (Boolean)

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

  • :append (Boolean)

    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.

  • :appenddup (Boolean)

    As above, but for sorted dup data.



1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
# File 'ext/lmdb_ext/lmdb_ext.c', line 1118

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

#sizeObject

Returns the number of records in this database.

Returns:

  • the number of records in this database



146
147
148
# File 'lib/lmdb/database.rb', line 146

def size
  stat[:entries]
end

#statHash

Return useful statistics about a database.

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

Returns:

  • (Hash)

    the statistics



971
972
973
974
975
976
977
978
979
980
# File 'ext/lmdb_ext/lmdb_ext.c', line 971

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