lmdb 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,124 @@
1
+ #ifndef _LMDB_EXT_H
2
+ #define _LMDB_EXT_H
3
+
4
+ #include "ruby.h"
5
+ #include "lmdb.h"
6
+
7
+ #define ENV_FLAGS ( \
8
+ MDB_FIXEDMAP | \
9
+ MDB_NOSUBDIR | \
10
+ MDB_NOSYNC | \
11
+ MDB_RDONLY | \
12
+ MDB_NOMETASYNC | \
13
+ MDB_WRITEMAP | \
14
+ MDB_MAPASYNC | \
15
+ MDB_NOTLS)
16
+
17
+ #define ENVIRONMENT(var, var_env) \
18
+ Environment* var_env; \
19
+ Data_Get_Struct(var, Environment, var_env); \
20
+ environment_check(var_env)
21
+
22
+ #define DATABASE(var, var_db) \
23
+ Database* var_db; \
24
+ Data_Get_Struct(var, Database, var_db);
25
+
26
+ #define TRANSACTION(var, var_txn) \
27
+ Transaction* var_txn; \
28
+ Data_Get_Struct(var, Transaction, var_txn)
29
+
30
+ #define CURSOR(var, var_cur) \
31
+ Cursor* var_cur; \
32
+ Data_Get_Struct(var, Cursor, var_cur); \
33
+ cursor_check(var_cur)
34
+
35
+ typedef struct Transaction Transaction;
36
+
37
+ typedef struct Transaction {
38
+ VALUE env;
39
+ VALUE parent;
40
+ MDB_txn* txn;
41
+ int refcount;
42
+ } Transaction;
43
+
44
+ typedef struct {
45
+ MDB_env* env;
46
+ VALUE txn;
47
+ int refcount;
48
+ } Environment;
49
+
50
+ typedef struct {
51
+ VALUE env;
52
+ MDB_dbi dbi;
53
+ int refcount;
54
+ } Database;
55
+
56
+ typedef struct {
57
+ VALUE db;
58
+ MDB_cursor* cur;
59
+ } Cursor;
60
+
61
+ typedef struct {
62
+ VALUE self;
63
+ const char* name;
64
+ int argc;
65
+ const VALUE* argv;
66
+ } HelperArgs;
67
+
68
+ static VALUE cEnvironment, cDatabase, cTransaction, cCursor, cError;
69
+
70
+ #define ERROR(name) static VALUE cError_##name;
71
+ #include "errors.h"
72
+ #undef ERROR
73
+
74
+ // BEGIN PROTOTYPES
75
+ void Init_lmdb_ext();
76
+ static VALUE call_with_transaction(VALUE venv, VALUE self, const char* name, int argc, const VALUE* argv, int flags);
77
+ static VALUE call_with_transaction_helper(VALUE arg);
78
+ static void check(int code);
79
+ static void cursor_check(Cursor* cursor);
80
+ static VALUE cursor_close(VALUE self);
81
+ static VALUE cursor_count(VALUE self);
82
+ static VALUE cursor_delete(int argc, VALUE *argv, VALUE self);
83
+ static VALUE cursor_first(VALUE self);
84
+ static void cursor_free(Cursor* cursor);
85
+ static VALUE cursor_get(VALUE self);
86
+ static void cursor_mark(Cursor* cursor);
87
+ static VALUE cursor_next(VALUE self);
88
+ static VALUE cursor_prev(VALUE self);
89
+ static VALUE cursor_put(int argc, VALUE* argv, VALUE self);
90
+ static VALUE cursor_set(VALUE self, VALUE vkey);
91
+ static VALUE cursor_set_range(VALUE self, VALUE vkey);
92
+ static VALUE database_clear(VALUE self);
93
+ static VALUE database_cursor(VALUE self);
94
+ static VALUE database_delete(int argc, VALUE *argv, VALUE self);
95
+ static void database_deref(Database* database);
96
+ static VALUE database_drop(VALUE self);
97
+ static VALUE database_get(VALUE self, VALUE vkey);
98
+ static void database_mark(Database* database);
99
+ static VALUE database_put(int argc, VALUE *argv, VALUE self);
100
+ static VALUE database_stat(VALUE self);
101
+ static void environment_check(Environment* environment);
102
+ static VALUE environment_close(VALUE self);
103
+ static VALUE environment_copy(VALUE self, VALUE path);
104
+ static VALUE environment_database(int argc, VALUE *argv, VALUE self);
105
+ static void environment_deref(Environment *environment);
106
+ static VALUE environment_flags(VALUE self);
107
+ static VALUE environment_info(VALUE self);
108
+ static MDB_txn* environment_need_txn(VALUE self);
109
+ static VALUE environment_open(int argc, VALUE *argv, VALUE klass);
110
+ static VALUE environment_path(VALUE self);
111
+ static VALUE environment_set_flags(VALUE self, VALUE vflags);
112
+ static VALUE environment_stat(VALUE self);
113
+ static VALUE environment_sync(int argc, VALUE *argv, VALUE self);
114
+ static VALUE environment_transaction(int argc, VALUE *argv, VALUE self);
115
+ static MDB_txn* environment_txn(VALUE self);
116
+ static VALUE stat2hash(const MDB_stat* stat);
117
+ static VALUE transaction_abort(VALUE self);
118
+ static VALUE transaction_commit(VALUE self);
119
+ static void transaction_deref(Transaction* transaction);
120
+ static void transaction_mark(Transaction* transaction);
121
+ static VALUE with_transaction(VALUE venv, VALUE(*fn)(VALUE), VALUE arg, int flags);
122
+ // END PROTOTYPES
123
+
124
+ #endif
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+ ctags -f - lmdb_ext.c | grep -P '\tf\t|f$' | sed -e 's#.*/\^##' -e 's#\$/.*##' -e 's# {#;#' > prototypes.h
3
+ sed -i -ne '/BEGIN PROTOTYPES/ {p; r prototypes.h' -e ':a; n; /END PROTOTYPES/ {p; b}; ba}; p' lmdb_ext.h
4
+ rm prototypes.h
@@ -1,14 +1,29 @@
1
1
  require 'lmdb_ext'
2
2
 
3
3
  module LMDB
4
+ class Database
5
+ include Enumerable
4
6
 
5
- # @see LMDB::Environment#new
6
- def self.new(*a, &b)
7
- LMDB::Environment.new(*a, &b)
8
- end
7
+ def each
8
+ cursor do |c|
9
+ while i = c.next
10
+ yield(i)
11
+ end
12
+ end
13
+ end
9
14
 
10
- end
15
+ def [](key)
16
+ get(key)
17
+ end
18
+
19
+ def []=(key, value)
20
+ put(key, value)
21
+ value
22
+ end
11
23
 
12
- %w|environment database|.each do |name|
13
- require "lmdb/#{name}"
24
+ def size
25
+ stat[:entries]
26
+ end
27
+ end
14
28
  end
29
+
@@ -1,22 +1,22 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  Gem::Specification.new do |s|
4
- s.name = File.basename(__FILE__, ".gemspec")
5
- s.version = "0.1.0"
4
+ s.name = File.basename(__FILE__, '.gemspec')
5
+ s.version = '0.2.0'
6
6
  s.platform = Gem::Platform::RUBY
7
- s.licenses = ["MIT"]
8
- s.summary = "Ruby bindings to Lightning MDB"
9
- s.email = "mail@daniel-mendler.de"
10
- s.homepage = "https://github.com/minad/mdb"
11
- s.description = "imdb is a Ruby binding to OpenLDAP Lightning MDB."
12
- s.authors = ["Daniel Mendler", "Black Square Media"]
13
- s.extensions = Dir["ext/**/extconf.rb"]
7
+ s.licenses = ['MIT']
8
+ s.summary = 'Ruby bindings to Lightning MDB'
9
+ s.email = 'mail@daniel-mendler.de'
10
+ s.homepage = 'https://github.com/minad/lmdb'
11
+ s.description = 'imdb is a Ruby binding to OpenLDAP Lightning MDB.'
12
+ s.authors = ['Daniel Mendler']
13
+ s.extensions = Dir['ext/**/extconf.rb']
14
14
 
15
15
  s.files = `git ls-files`.split("\n")
16
16
  s.test_files = `git ls-files -- spec/*`.split("\n")
17
- s.require_paths = ["lib"]
17
+ s.require_paths = ['lib']
18
18
 
19
- s.add_development_dependency "rake"
20
- s.add_development_dependency "rake-compiler"
21
- s.add_development_dependency "rspec"
19
+ s.add_development_dependency 'rake'
20
+ s.add_development_dependency 'rake-compiler'
21
+ s.add_development_dependency 'rspec'
22
22
  end
@@ -0,0 +1,28 @@
1
+ require 'lmdb'
2
+ require 'rspec'
3
+ require 'fileutils'
4
+
5
+ SPEC_ROOT = File.dirname(__FILE__)
6
+ TEMP_ROOT = File.join(SPEC_ROOT, 'tmp')
7
+
8
+ module LMDB::SpecHelper
9
+ def mkpath(name = 'env')
10
+ path = File.join(TEMP_ROOT, name)
11
+ FileUtils.mkpath(path)
12
+ path
13
+ end
14
+
15
+ def path
16
+ @path ||= mkpath
17
+ end
18
+
19
+ def env
20
+ @env ||= LMDB::Environment.new path: path
21
+ end
22
+ end
23
+
24
+ RSpec.configure do |c|
25
+ c.include LMDB::SpecHelper
26
+ c.after { FileUtils.rm_rf TEMP_ROOT }
27
+ end
28
+
@@ -1,41 +1,266 @@
1
- require 'spec_helper'
1
+ # coding: binary
2
+ require 'helper'
2
3
 
3
4
  describe LMDB do
5
+ let(:env) { LMDB.open(path) }
6
+ after { env.close rescue nil }
4
7
 
5
- describe "constants" do
6
- it 'has version constants' do
7
- LMDB::VERSION_MAJOR.should be_instance_of(Fixnum)
8
- LMDB::VERSION_MINOR.should be_instance_of(Fixnum)
9
- LMDB::VERSION_PATCH.should be_instance_of(Fixnum)
10
- LMDB::VERSION.should be_instance_of(String)
11
- end
12
-
13
- it 'has environment flags' do
14
- LMDB::FIXEDMAP.should be_instance_of(Fixnum)
15
- LMDB::NOSUBDIR.should be_instance_of(Fixnum)
16
- LMDB::NOSYNC.should be_instance_of(Fixnum)
17
- LMDB::RDONLY.should be_instance_of(Fixnum)
18
- LMDB::NOMETASYNC.should be_instance_of(Fixnum)
19
- LMDB::WRITEMAP.should be_instance_of(Fixnum)
20
- LMDB::MAPASYNC.should be_instance_of(Fixnum)
21
- end
22
-
23
- it 'has database flags' do
24
- LMDB::REVERSEKEY.should be_instance_of(Fixnum)
25
- LMDB::DUPSORT.should be_instance_of(Fixnum)
26
- LMDB::INTEGERKEY.should be_instance_of(Fixnum)
27
- LMDB::DUPFIXED.should be_instance_of(Fixnum)
28
- LMDB::INTEGERDUP.should be_instance_of(Fixnum)
29
- LMDB::REVERSEDUP.should be_instance_of(Fixnum)
30
- LMDB::CREATE.should be_instance_of(Fixnum)
31
- LMDB::NOOVERWRITE.should be_instance_of(Fixnum)
32
- LMDB::NODUPDATA.should be_instance_of(Fixnum)
33
- LMDB::CURRENT.should be_instance_of(Fixnum)
34
- LMDB::RESERVE.should be_instance_of(Fixnum)
35
- LMDB::APPEND.should be_instance_of(Fixnum)
36
- LMDB::APPENDDUP.should be_instance_of(Fixnum)
37
- LMDB::MULTIPLE.should be_instance_of(Fixnum)
8
+ let(:db) { env.database }
9
+
10
+ it 'has version constants' do
11
+ LMDB::VERSION_MAJOR.should be_instance_of(Fixnum)
12
+ LMDB::VERSION_MINOR.should be_instance_of(Fixnum)
13
+ LMDB::VERSION_PATCH.should be_instance_of(Fixnum)
14
+ LMDB::VERSION.should be_instance_of(String)
15
+ end
16
+
17
+ it 'has environment flags' do
18
+ LMDB::FIXEDMAP.should be_instance_of(Fixnum)
19
+ LMDB::NOSUBDIR.should be_instance_of(Fixnum)
20
+ LMDB::NOSYNC.should be_instance_of(Fixnum)
21
+ LMDB::RDONLY.should be_instance_of(Fixnum)
22
+ LMDB::NOMETASYNC.should be_instance_of(Fixnum)
23
+ LMDB::WRITEMAP.should be_instance_of(Fixnum)
24
+ LMDB::MAPASYNC.should be_instance_of(Fixnum)
25
+ LMDB::NOTLS.should be_instance_of(Fixnum)
26
+ end
27
+
28
+ it 'has database flags' do
29
+ LMDB::REVERSEKEY.should be_instance_of(Fixnum)
30
+ LMDB::DUPSORT.should be_instance_of(Fixnum)
31
+ LMDB::INTEGERKEY.should be_instance_of(Fixnum)
32
+ LMDB::DUPFIXED.should be_instance_of(Fixnum)
33
+ LMDB::INTEGERDUP.should be_instance_of(Fixnum)
34
+ LMDB::REVERSEDUP.should be_instance_of(Fixnum)
35
+ LMDB::CREATE.should be_instance_of(Fixnum)
36
+ LMDB::NOOVERWRITE.should be_instance_of(Fixnum)
37
+ LMDB::NODUPDATA.should be_instance_of(Fixnum)
38
+ LMDB::CURRENT.should be_instance_of(Fixnum)
39
+ LMDB::RESERVE.should be_instance_of(Fixnum)
40
+ LMDB::APPEND.should be_instance_of(Fixnum)
41
+ LMDB::APPENDDUP.should be_instance_of(Fixnum)
42
+ LMDB::MULTIPLE.should be_instance_of(Fixnum)
43
+ end
44
+
45
+ describe LMDB::Environment do
46
+ subject { env }
47
+
48
+ its(:path) { should == path }
49
+ its(:flags) { should == 0 }
50
+
51
+ describe 'open' do
52
+ it 'returns environment' do
53
+ env = LMDB::Environment.open(path)
54
+ env.should be_instance_of(described_class::Environment)
55
+ env.close
56
+ end
57
+
58
+ it 'accepts block' do
59
+ LMDB::Environment.open(path) do |env|
60
+ env.should be_instance_of(described_class::Environment)
61
+ 42
62
+ end.should == 42
63
+ end
64
+
65
+ it 'accepts options' do
66
+ env = LMDB::Environment.open(path, :flags => LMDB::NOSYNC, :mode => 0777, :maxreaders => 777, :mapsize => 111111, :maxdbs => 666)
67
+ env.should be_instance_of(described_class::Environment)
68
+ env.info[:maxreaders].should == 777
69
+ env.info[:mapsize].should == 111111
70
+ env.close
71
+ end
72
+ end
73
+
74
+ it 'should return stat' do
75
+ stat = env.stat
76
+ stat[:psize].should be_instance_of(Fixnum)
77
+ stat[:depth].should be_instance_of(Fixnum)
78
+ stat[:branch_pages].should be_instance_of(Fixnum)
79
+ stat[:leaf_pages].should be_instance_of(Fixnum)
80
+ stat[:overflow_pages].should be_instance_of(Fixnum)
81
+ stat[:entries].should be_instance_of(Fixnum)
82
+ end
83
+
84
+ it 'should return info' do
85
+ info = env.info
86
+ info[:mapaddr].should be_instance_of(Fixnum)
87
+ info[:mapsize].should be_instance_of(Fixnum)
88
+ info[:last_pgno].should be_instance_of(Fixnum)
89
+ info[:last_txnid].should be_instance_of(Fixnum)
90
+ info[:maxreaders].should be_instance_of(Fixnum)
91
+ info[:numreaders].should be_instance_of(Fixnum)
92
+ end
93
+
94
+ it 'should copy' do
95
+ target = mkpath('copy')
96
+ subject.copy(target).should be_nil
97
+ end
98
+
99
+ it 'should sync' do
100
+ subject.sync.should be_nil
101
+ end
102
+
103
+ it 'should force-sync' do
104
+ subject.sync(true).should be_nil
105
+ end
106
+
107
+ it 'should accept custom flags' do
108
+ (subject.flags = LMDB::NOSYNC).should == LMDB::NOSYNC
109
+ subject.flags.should == LMDB::NOSYNC
110
+
111
+ (subject.flags = 0).should == 0
112
+ subject.flags.should == 0
113
+ end
114
+
115
+ describe 'transaction' do
116
+ subject { env}
117
+
118
+ it 'should create transactions' do
119
+ subject.transaction do |txn|
120
+ txn.should be_instance_of(described_class::Transaction)
121
+ txn.abort
122
+ end
123
+ end
124
+
125
+ it 'should create read-only transactions' do
126
+ subject.transaction(true) do |txn|
127
+ txn.should be_instance_of(described_class::Transaction)
128
+ txn.abort
129
+ end
130
+ end
131
+
132
+ it 'can create child transactions' do
133
+ env.transaction do |txn|
134
+ txn.should be_instance_of(described_class::Transaction)
135
+ env.transaction do |ctxn|
136
+ ctxn.should be_instance_of(described_class::Transaction)
137
+ ctxn.abort
138
+ end
139
+ end
140
+ end
141
+
142
+ it 'should support aborting parent transaction' do
143
+ env.transaction do |txn|
144
+ env.transaction do |ctxn|
145
+ db['key'] = 'value'
146
+ txn.abort
147
+ end
148
+ end
149
+ db['key'].should be(nil)
150
+ end
151
+
152
+ it 'should support comitting parent transaction' do
153
+ env.transaction do |txn|
154
+ env.transaction do |ctxn|
155
+ db['key'] = 'value'
156
+ txn.commit
157
+ end
158
+ end
159
+ db['key'].should == 'value'
160
+ end
38
161
  end
39
162
  end
40
163
 
41
- end
164
+ describe LMDB::Database do
165
+ subject { db }
166
+
167
+ it 'should get/put data' do
168
+ subject.get('cat').should be_nil
169
+ subject.put('cat', 'garfield').should be_nil
170
+ subject.get('cat').should == 'garfield'
171
+ end
172
+
173
+ it 'stores key/values in same transaction' do
174
+ db.put('key', 'value').should be_nil
175
+ db.get('key').should == 'value'
176
+ end
177
+
178
+ it 'stores key/values in different transactions' do
179
+ env.transaction do
180
+ db.put('key', 'value').should be_nil
181
+ db.put('key2', 'value2').should be_nil
182
+ env.transaction do
183
+ db.put('key3', 'value3').should be_nil
184
+ end
185
+ end
186
+
187
+ env.transaction do
188
+ db.get('key').should == 'value'
189
+ db.get('key2').should == 'value2'
190
+ env.transaction do
191
+ db.get('key3').should == 'value3'
192
+ end
193
+ end
194
+ end
195
+
196
+ it 'should return stat' do
197
+ db.stat.should be_instance_of(Hash)
198
+ end
199
+
200
+ it 'should return size' do
201
+ db.size.should == 0
202
+ db.put('key', 'value')
203
+ db.size.should == 1
204
+ db.put('key2', 'value2')
205
+ db.size.should == 2
206
+ end
207
+
208
+ it 'should be enumerable' do
209
+ db['k1'] = 'v1'
210
+ db['k2'] = 'v2'
211
+ db.to_a.should == [['k1', 'v1'], ['k2', 'v2']]
212
+ end
213
+
214
+ it 'should have shortcuts' do
215
+ db['key'] = 'value'
216
+ db['key'].should == 'value'
217
+ end
218
+
219
+ it 'should store binary' do
220
+ bin1 = "\xAAx\BB\xCC1"
221
+ bin2 = "\xAAx\BB\xCC2"
222
+ db[bin1] = bin2
223
+ db['key'] = bin2
224
+ db[bin1].should == bin2
225
+ db['key'].should == bin2
226
+ end
227
+ end
228
+
229
+ describe LMDB::Cursor do
230
+ before do
231
+ db.put('key1', 'value1')
232
+ db.put('key2', 'value2')
233
+ end
234
+
235
+ it 'should get next key/value' do
236
+ db.cursor do |c|
237
+ c.first.should == ['key1', 'value1']
238
+ end
239
+ end
240
+
241
+ it 'should get next key/value' do
242
+ db.cursor do |c|
243
+ c.first
244
+ c.next.should == ['key2', 'value2']
245
+ end
246
+ end
247
+
248
+ it 'should seek to key' do
249
+ db.cursor do |c|
250
+ c.set('key1').should == ['key1', 'value1']
251
+ end
252
+ end
253
+
254
+ it 'should seek to closest key' do
255
+ db.cursor do |c|
256
+ c.set_range('key0').should == ['key1', 'value1']
257
+ end
258
+ end
259
+
260
+ it 'should seek to key with nuls' do
261
+ db.cursor do |c|
262
+ c.set_range('\x00').should == ['key1', 'value1']
263
+ end
264
+ end
265
+ end
266
+ end