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



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

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:



1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
# File 'ext/lmdb_ext/lmdb_ext.c', line 1190

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.



1117
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
# File 'ext/lmdb_ext/lmdb_ext.c', line 1117

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)


139
140
141
# File 'lib/lmdb/database.rb', line 139

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



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

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)


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

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)


957
958
959
960
961
962
963
964
965
966
# File 'ext/lmdb_ext/lmdb_ext.c', line 957

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.



1235
1236
1237
1238
# File 'ext/lmdb_ext/lmdb_ext.c', line 1235

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.



940
941
942
943
944
945
946
947
948
# File 'ext/lmdb_ext/lmdb_ext.c', line 940

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.



1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
# File 'ext/lmdb_ext/lmdb_ext.c', line 1021

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

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



1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
# File 'ext/lmdb_ext/lmdb_ext.c', line 1071

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



144
145
146
# File 'lib/lmdb/database.rb', line 144

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



924
925
926
927
928
929
930
931
932
933
# File 'ext/lmdb_ext/lmdb_ext.c', line 924

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