lmdb 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/CHANGES +3 -0
- data/CONTRIBUTORS +3 -0
- data/Gemfile +2 -0
- data/README.md +37 -0
- data/Rakefile +14 -0
- data/ext/lmdb_ext/errors.h +15 -0
- data/ext/lmdb_ext/extconf.rb +24 -0
- data/ext/lmdb_ext/liblmdb/.gitignore +16 -0
- data/ext/lmdb_ext/liblmdb/CHANGES +41 -0
- data/ext/lmdb_ext/liblmdb/COPYRIGHT +20 -0
- data/ext/lmdb_ext/liblmdb/LICENSE +47 -0
- data/ext/lmdb_ext/liblmdb/lmdb.h +1367 -0
- data/ext/lmdb_ext/liblmdb/mdb.c +8354 -0
- data/ext/lmdb_ext/liblmdb/midl.c +348 -0
- data/ext/lmdb_ext/liblmdb/midl.h +177 -0
- data/ext/lmdb_ext/lmdb_ext.c +725 -0
- data/lib/lmdb.rb +14 -0
- data/lib/lmdb/database.rb +66 -0
- data/lib/lmdb/environment.rb +135 -0
- data/lmdb.gemspec +22 -0
- data/spec/lmdb/database_spec.rb +23 -0
- data/spec/lmdb/environment_spec.rb +22 -0
- data/spec/lmdb/lmdb_ext_spec.rb +253 -0
- data/spec/lmdb_spec.rb +41 -0
- data/spec/spec_helper.rb +33 -0
- metadata +118 -0
@@ -0,0 +1,725 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include "lmdb.h"
|
3
|
+
|
4
|
+
/*-----------------------------------------------------------------------------
|
5
|
+
* Macros
|
6
|
+
*----------------------------------------------------------------------------*/
|
7
|
+
|
8
|
+
#define ENV_FLAGS ( \
|
9
|
+
MDB_FIXEDMAP | \
|
10
|
+
MDB_NOSUBDIR | \
|
11
|
+
MDB_NOSYNC | \
|
12
|
+
MDB_RDONLY | \
|
13
|
+
MDB_NOMETASYNC | \
|
14
|
+
MDB_WRITEMAP | \
|
15
|
+
MDB_MAPASYNC)
|
16
|
+
|
17
|
+
#define ENVIRONMENT(var, var_env) \
|
18
|
+
Environment* var_env; \
|
19
|
+
Data_Get_Struct(var, Environment, var_env); \
|
20
|
+
do { if (!var_env->env) rb_raise(cError, "Environment is closed"); } while(0)
|
21
|
+
|
22
|
+
#define TRANSACTION(var, var_txn, var_env) \
|
23
|
+
Transaction* var_txn; \
|
24
|
+
Data_Get_Struct(var, Transaction, var_txn); \
|
25
|
+
{ \
|
26
|
+
Transaction* parent = var_txn; \
|
27
|
+
for (;;) { \
|
28
|
+
if (!parent->txn) \
|
29
|
+
rb_raise(cError, "Transaction is terminated"); \
|
30
|
+
if (NIL_P(parent->parent)) \
|
31
|
+
break; \
|
32
|
+
Data_Get_Struct(parent->parent, Transaction, parent); \
|
33
|
+
} \
|
34
|
+
} \
|
35
|
+
ENVIRONMENT(transaction->environment, var_env)
|
36
|
+
|
37
|
+
#define DATABASE(var, var_db, var_env) \
|
38
|
+
Database* var_db; \
|
39
|
+
Data_Get_Struct(var, Database, var_db); \
|
40
|
+
if (!var_db->open) rb_raise(cError, "Database is closed"); \
|
41
|
+
ENVIRONMENT(database->environment, var_env)
|
42
|
+
|
43
|
+
#define DATABASE_TRANSACTION(dbvar, txvar, var_db, var_txn, var_env) \
|
44
|
+
DATABASE(dbvar, var_db, tmp_env); \
|
45
|
+
TRANSACTION(txvar, var_txn, var_env); \
|
46
|
+
do { if (var_env != tmp_env) rb_raise(cError, "Different environments"); } while(0) \
|
47
|
+
|
48
|
+
#define CURSOR(var, var_cur, var_db, var_txn, var_env) \
|
49
|
+
Cursor* var_cur; \
|
50
|
+
Data_Get_Struct(var, Cursor, var_cur); \
|
51
|
+
if (!cursor->cur) rb_raise(cError, "Cursor is closed"); \
|
52
|
+
DATABASE_TRANSACTION(var_cur->database, var_cur->transaction, var_db, var_txn, var_env)
|
53
|
+
|
54
|
+
/*-----------------------------------------------------------------------------
|
55
|
+
* Static
|
56
|
+
*----------------------------------------------------------------------------*/
|
57
|
+
|
58
|
+
/* Classes */
|
59
|
+
static VALUE cEnvironment, cStat, cInfo, cDatabase, cTransaction, cCursor, cError;
|
60
|
+
|
61
|
+
/* Error Classes */
|
62
|
+
#define ERROR(name) static VALUE cError_##name;
|
63
|
+
#include "errors.h"
|
64
|
+
#undef ERROR
|
65
|
+
|
66
|
+
/*-----------------------------------------------------------------------------
|
67
|
+
* Structs
|
68
|
+
*----------------------------------------------------------------------------*/
|
69
|
+
|
70
|
+
typedef struct {
|
71
|
+
MDB_env* env;
|
72
|
+
} Environment;
|
73
|
+
|
74
|
+
typedef struct {
|
75
|
+
VALUE environment;
|
76
|
+
MDB_dbi dbi;
|
77
|
+
int open;
|
78
|
+
} Database;
|
79
|
+
|
80
|
+
typedef struct {
|
81
|
+
VALUE environment;
|
82
|
+
VALUE parent;
|
83
|
+
MDB_txn* txn;
|
84
|
+
} Transaction;
|
85
|
+
|
86
|
+
typedef struct {
|
87
|
+
VALUE transaction;
|
88
|
+
VALUE database;
|
89
|
+
MDB_cursor* cur;
|
90
|
+
} Cursor;
|
91
|
+
|
92
|
+
/*-----------------------------------------------------------------------------
|
93
|
+
* Prototypes
|
94
|
+
*----------------------------------------------------------------------------*/
|
95
|
+
|
96
|
+
static void transaction_mark(Transaction* transaction);
|
97
|
+
static void transaction_free(Transaction *transaction);
|
98
|
+
|
99
|
+
/*-----------------------------------------------------------------------------
|
100
|
+
* Helpers
|
101
|
+
*----------------------------------------------------------------------------*/
|
102
|
+
|
103
|
+
#define F_STAT(name) \
|
104
|
+
static VALUE stat_##name(VALUE self) { \
|
105
|
+
MDB_stat* stat; \
|
106
|
+
Data_Get_Struct(self, MDB_stat, stat); \
|
107
|
+
return INT2NUM(stat->ms_##name); \
|
108
|
+
}
|
109
|
+
F_STAT(psize)
|
110
|
+
F_STAT(depth)
|
111
|
+
F_STAT(branch_pages)
|
112
|
+
F_STAT(leaf_pages)
|
113
|
+
F_STAT(overflow_pages)
|
114
|
+
F_STAT(entries)
|
115
|
+
#undef F_STAT
|
116
|
+
|
117
|
+
#define F_INFO(name) \
|
118
|
+
static VALUE info_##name(VALUE self) { \
|
119
|
+
MDB_envinfo* info; \
|
120
|
+
Data_Get_Struct(self, MDB_envinfo, info); \
|
121
|
+
return INT2NUM((size_t)info->me_##name); \
|
122
|
+
}
|
123
|
+
F_INFO(mapaddr)
|
124
|
+
F_INFO(mapsize)
|
125
|
+
F_INFO(last_pgno)
|
126
|
+
F_INFO(last_txnid)
|
127
|
+
F_INFO(maxreaders)
|
128
|
+
F_INFO(numreaders)
|
129
|
+
#undef F_INFO
|
130
|
+
|
131
|
+
static void check(int code) {
|
132
|
+
const char *err, *sep;
|
133
|
+
if (!code) return;
|
134
|
+
|
135
|
+
err = mdb_strerror(code);
|
136
|
+
sep = strchr(err, ':');
|
137
|
+
if (sep) err = sep + 2;
|
138
|
+
|
139
|
+
#define ERROR(name) if (code == MDB_##name) rb_raise(cError_##name, "%s", err);
|
140
|
+
#include "errors.h"
|
141
|
+
#undef ERROR
|
142
|
+
|
143
|
+
rb_raise(cError, "%s", err); /* fallback */
|
144
|
+
|
145
|
+
}
|
146
|
+
|
147
|
+
/*-----------------------------------------------------------------------------
|
148
|
+
* Environment functions
|
149
|
+
*----------------------------------------------------------------------------*/
|
150
|
+
|
151
|
+
static void environment_free(Environment *environment) {
|
152
|
+
if (environment->env)
|
153
|
+
mdb_env_close(environment->env);
|
154
|
+
free(environment);
|
155
|
+
}
|
156
|
+
|
157
|
+
static VALUE environment_close(VALUE self) {
|
158
|
+
ENVIRONMENT(self, environment);
|
159
|
+
mdb_env_close(environment->env);
|
160
|
+
environment->env = 0;
|
161
|
+
return Qnil;
|
162
|
+
}
|
163
|
+
|
164
|
+
static VALUE environment_stat(VALUE self) {
|
165
|
+
MDB_stat* stat;
|
166
|
+
VALUE vstat;
|
167
|
+
|
168
|
+
ENVIRONMENT(self, environment);
|
169
|
+
vstat = Data_Make_Struct(cStat, MDB_stat, 0, -1, stat);
|
170
|
+
check(mdb_env_stat(environment->env, stat));
|
171
|
+
return vstat;
|
172
|
+
}
|
173
|
+
|
174
|
+
static VALUE environment_info(VALUE self) {
|
175
|
+
MDB_envinfo* info;
|
176
|
+
VALUE vinfo;
|
177
|
+
|
178
|
+
ENVIRONMENT(self, environment);
|
179
|
+
vinfo = Data_Make_Struct(cInfo, MDB_envinfo, 0, -1, info);
|
180
|
+
check(mdb_env_info(environment->env, info));
|
181
|
+
return vinfo;
|
182
|
+
}
|
183
|
+
|
184
|
+
static VALUE environment_copy(VALUE self, VALUE path) {
|
185
|
+
ENVIRONMENT(self, environment);
|
186
|
+
check(mdb_env_copy(environment->env, StringValueCStr(path)));
|
187
|
+
return Qnil;
|
188
|
+
}
|
189
|
+
|
190
|
+
static VALUE environment_sync(int argc, VALUE *argv, VALUE self) {
|
191
|
+
VALUE force;
|
192
|
+
int n;
|
193
|
+
|
194
|
+
ENVIRONMENT(self, environment);
|
195
|
+
n = rb_scan_args(argc, argv, "01", &force);
|
196
|
+
check(mdb_env_sync(environment->env, n == 1 && RTEST(force) ? 0 : 1));
|
197
|
+
return Qnil;
|
198
|
+
}
|
199
|
+
|
200
|
+
static VALUE environment_open(int argc, VALUE *argv, VALUE klass) {
|
201
|
+
VALUE path, options, venv;
|
202
|
+
MDB_env* env;
|
203
|
+
Environment* environment;
|
204
|
+
|
205
|
+
int n = rb_scan_args(argc, argv, "11", &path, &options);
|
206
|
+
|
207
|
+
int flags = 0, maxreaders = -1, maxdbs = 10;
|
208
|
+
size_t mapsize = 0;
|
209
|
+
mode_t mode = 0755;
|
210
|
+
if (n == 2) {
|
211
|
+
VALUE value = rb_hash_aref(options, ID2SYM(rb_intern("flags")));
|
212
|
+
if (!NIL_P(value))
|
213
|
+
flags = NUM2INT(value);
|
214
|
+
|
215
|
+
value = rb_hash_aref(options, ID2SYM(rb_intern("mode")));
|
216
|
+
if (!NIL_P(value))
|
217
|
+
mode = NUM2INT(value);
|
218
|
+
|
219
|
+
value = rb_hash_aref(options, ID2SYM(rb_intern("maxreaders")));
|
220
|
+
if (!NIL_P(value))
|
221
|
+
maxreaders = NUM2INT(value);
|
222
|
+
|
223
|
+
value = rb_hash_aref(options, ID2SYM(rb_intern("maxdbs")));
|
224
|
+
if (!NIL_P(value))
|
225
|
+
maxdbs = NUM2INT(value);
|
226
|
+
|
227
|
+
value = rb_hash_aref(options, ID2SYM(rb_intern("mapsize")));
|
228
|
+
if (!NIL_P(value))
|
229
|
+
mapsize = NUM2SIZET(value);
|
230
|
+
}
|
231
|
+
|
232
|
+
check(mdb_env_create(&env));
|
233
|
+
|
234
|
+
venv = Data_Make_Struct(cEnvironment, Environment, 0, environment_free, environment);
|
235
|
+
environment->env = env;
|
236
|
+
|
237
|
+
if (maxreaders > 0)
|
238
|
+
check(mdb_env_set_maxreaders(environment->env, maxreaders));
|
239
|
+
if (mapsize > 0)
|
240
|
+
check(mdb_env_set_mapsize(environment->env, mapsize));
|
241
|
+
|
242
|
+
check(mdb_env_set_maxdbs(environment->env, maxdbs <= 0 ? 1 : maxdbs));
|
243
|
+
check(mdb_env_open(environment->env, StringValueCStr(path), flags, mode));
|
244
|
+
if (rb_block_given_p())
|
245
|
+
return rb_ensure(rb_yield, venv, environment_close, venv);
|
246
|
+
return venv;
|
247
|
+
}
|
248
|
+
|
249
|
+
static VALUE environment_flags(VALUE self) {
|
250
|
+
unsigned int flags;
|
251
|
+
ENVIRONMENT(self, environment);
|
252
|
+
check(mdb_env_get_flags(environment->env, &flags));
|
253
|
+
return INT2NUM(flags & ENV_FLAGS);
|
254
|
+
}
|
255
|
+
|
256
|
+
static VALUE environment_path(VALUE self) {
|
257
|
+
const char* path;
|
258
|
+
ENVIRONMENT(self, environment);
|
259
|
+
check(mdb_env_get_path(environment->env, &path));
|
260
|
+
return rb_str_new2(path);
|
261
|
+
}
|
262
|
+
|
263
|
+
static VALUE environment_set_flags(VALUE self, VALUE vflags) {
|
264
|
+
unsigned int flags = NUM2INT(vflags), oldflags;
|
265
|
+
ENVIRONMENT(self, environment);
|
266
|
+
check(mdb_env_get_flags(environment->env, &oldflags));
|
267
|
+
check(mdb_env_set_flags(environment->env, oldflags & ENV_FLAGS, 0));
|
268
|
+
check(mdb_env_set_flags(environment->env, flags, 1));
|
269
|
+
return environment_flags(self);
|
270
|
+
}
|
271
|
+
|
272
|
+
static VALUE environment_transaction(int argc, VALUE *argv, VALUE self) {
|
273
|
+
VALUE readonly, vtxn;
|
274
|
+
MDB_txn* txn;
|
275
|
+
unsigned int flags;
|
276
|
+
Transaction* transaction;
|
277
|
+
|
278
|
+
ENVIRONMENT(self, environment);
|
279
|
+
flags = (rb_scan_args(argc, argv, "01", &readonly) == 1 && !NIL_P(readonly)) ? MDB_RDONLY : 0;
|
280
|
+
check(mdb_txn_begin(environment->env, 0, flags, &txn));
|
281
|
+
|
282
|
+
vtxn = Data_Make_Struct(cTransaction, Transaction, transaction_mark, transaction_free, transaction);
|
283
|
+
transaction->txn = txn;
|
284
|
+
transaction->environment = self;
|
285
|
+
transaction->parent = Qnil;
|
286
|
+
|
287
|
+
if (rb_block_given_p()) {
|
288
|
+
int exception;
|
289
|
+
VALUE result = rb_protect(rb_yield, vtxn, &exception);
|
290
|
+
if (exception) {
|
291
|
+
mdb_txn_abort(transaction->txn);
|
292
|
+
transaction->txn = 0;
|
293
|
+
rb_jump_tag(exception);
|
294
|
+
}
|
295
|
+
mdb_txn_commit(transaction->txn);
|
296
|
+
transaction->txn = 0;
|
297
|
+
return result;
|
298
|
+
}
|
299
|
+
return vtxn;
|
300
|
+
}
|
301
|
+
|
302
|
+
/*-----------------------------------------------------------------------------
|
303
|
+
* Transaction functions
|
304
|
+
*----------------------------------------------------------------------------*/
|
305
|
+
|
306
|
+
static VALUE transaction_environment(VALUE self) {
|
307
|
+
TRANSACTION(self, transaction, environment);
|
308
|
+
return transaction->environment;
|
309
|
+
}
|
310
|
+
|
311
|
+
static VALUE transaction_parent(VALUE self) {
|
312
|
+
TRANSACTION(self, transaction, environment);
|
313
|
+
return transaction->parent;
|
314
|
+
}
|
315
|
+
|
316
|
+
static void transaction_mark(Transaction* transaction) {
|
317
|
+
rb_gc_mark(transaction->environment);
|
318
|
+
rb_gc_mark(transaction->parent);
|
319
|
+
}
|
320
|
+
|
321
|
+
static void transaction_free(Transaction *transaction) {
|
322
|
+
if (transaction->txn)
|
323
|
+
mdb_txn_abort(transaction->txn);
|
324
|
+
free(transaction);
|
325
|
+
}
|
326
|
+
|
327
|
+
VALUE transaction_abort(VALUE self) {
|
328
|
+
TRANSACTION(self, transaction, environment);
|
329
|
+
mdb_txn_abort(transaction->txn);
|
330
|
+
transaction->txn = 0;
|
331
|
+
return Qnil;
|
332
|
+
}
|
333
|
+
|
334
|
+
VALUE transaction_commit(VALUE self) {
|
335
|
+
TRANSACTION(self, transaction, environment);
|
336
|
+
mdb_txn_commit(transaction->txn);
|
337
|
+
transaction->txn = 0;
|
338
|
+
return Qnil;
|
339
|
+
}
|
340
|
+
|
341
|
+
VALUE transaction_renew(VALUE self) {
|
342
|
+
TRANSACTION(self, transaction, environment);
|
343
|
+
mdb_txn_renew(transaction->txn);
|
344
|
+
return Qnil;
|
345
|
+
}
|
346
|
+
|
347
|
+
VALUE transaction_reset(VALUE self) {
|
348
|
+
TRANSACTION(self, transaction, environment);
|
349
|
+
mdb_txn_reset(transaction->txn);
|
350
|
+
return Qnil;
|
351
|
+
}
|
352
|
+
|
353
|
+
static VALUE transaction_transaction(VALUE self) {
|
354
|
+
MDB_txn* txn;
|
355
|
+
Transaction* child;
|
356
|
+
VALUE vtxn;
|
357
|
+
|
358
|
+
TRANSACTION(self, transaction, environment);
|
359
|
+
check(mdb_txn_begin(environment->env, transaction->txn, 0, &txn));
|
360
|
+
|
361
|
+
vtxn = Data_Make_Struct(cTransaction, Transaction, transaction_mark, transaction_free, child);
|
362
|
+
child->txn = txn;
|
363
|
+
child->environment = transaction->environment;
|
364
|
+
child->parent = self;
|
365
|
+
|
366
|
+
if (rb_block_given_p()) {
|
367
|
+
int exception;
|
368
|
+
VALUE result = rb_protect(rb_yield, vtxn, &exception);
|
369
|
+
if (exception) {
|
370
|
+
mdb_txn_abort(child->txn);
|
371
|
+
child->txn = 0;
|
372
|
+
rb_jump_tag(exception);
|
373
|
+
}
|
374
|
+
mdb_txn_commit(child->txn);
|
375
|
+
child->txn = 0;
|
376
|
+
return result;
|
377
|
+
}
|
378
|
+
return vtxn;
|
379
|
+
}
|
380
|
+
|
381
|
+
/*-----------------------------------------------------------------------------
|
382
|
+
* Database functions
|
383
|
+
*----------------------------------------------------------------------------*/
|
384
|
+
|
385
|
+
static VALUE database_environment(VALUE self) {
|
386
|
+
DATABASE(self, database, environment);
|
387
|
+
return database->environment;
|
388
|
+
}
|
389
|
+
|
390
|
+
static void database_free(Database* database) {
|
391
|
+
if (database->open) {
|
392
|
+
ENVIRONMENT(database->environment, environment);
|
393
|
+
mdb_dbi_close(environment->env, database->dbi);
|
394
|
+
}
|
395
|
+
free(database);
|
396
|
+
}
|
397
|
+
|
398
|
+
static void database_mark(Database* database) {
|
399
|
+
rb_gc_mark(database->environment);
|
400
|
+
}
|
401
|
+
|
402
|
+
static VALUE database_open(int argc, VALUE *argv, VALUE self) {
|
403
|
+
ENVIRONMENT(self, environment);
|
404
|
+
|
405
|
+
VALUE vtxn, vname, vflags;
|
406
|
+
int n = rb_scan_args(argc, argv, "21", &vtxn, &vname, &vflags);
|
407
|
+
|
408
|
+
TRANSACTION(vtxn, transaction, txn_environment);
|
409
|
+
if (environment != txn_environment)
|
410
|
+
rb_raise(cError, "Different environments");
|
411
|
+
|
412
|
+
MDB_dbi dbi;
|
413
|
+
check(mdb_dbi_open(transaction->txn, StringValueCStr(vname), n == 3 ? NUM2INT(vflags) : 0, &dbi));
|
414
|
+
|
415
|
+
Database* database;
|
416
|
+
VALUE vdb = Data_Make_Struct(cDatabase, Database, database_mark, database_free, database);
|
417
|
+
database->dbi = dbi;
|
418
|
+
database->environment = self;
|
419
|
+
database->open = 1;
|
420
|
+
|
421
|
+
return vdb;
|
422
|
+
}
|
423
|
+
|
424
|
+
static VALUE database_close(VALUE self) {
|
425
|
+
DATABASE(self, database, environment);
|
426
|
+
mdb_dbi_close(environment->env, database->dbi);
|
427
|
+
database->open = 0;
|
428
|
+
return Qnil;
|
429
|
+
}
|
430
|
+
|
431
|
+
static VALUE database_stat(VALUE self, VALUE vtxn) {
|
432
|
+
DATABASE_TRANSACTION(self, vtxn, database, transaction, environment);
|
433
|
+
MDB_stat* stat;
|
434
|
+
VALUE vstat = Data_Make_Struct(cStat, MDB_stat, 0, -1, stat);
|
435
|
+
check(mdb_stat(transaction->txn, database->dbi, stat));
|
436
|
+
return vstat;
|
437
|
+
}
|
438
|
+
|
439
|
+
static VALUE database_drop(VALUE self, VALUE vtxn) {
|
440
|
+
DATABASE_TRANSACTION(self, vtxn, database, transaction, environment);
|
441
|
+
check(mdb_drop(transaction->txn, database->dbi, 1));
|
442
|
+
database->open = 0;
|
443
|
+
return Qnil;
|
444
|
+
}
|
445
|
+
|
446
|
+
static VALUE database_clear(VALUE self, VALUE vtxn) {
|
447
|
+
DATABASE_TRANSACTION(self, vtxn, database, transaction, environment);
|
448
|
+
check(mdb_drop(transaction->txn, database->dbi, 0));
|
449
|
+
return Qnil;
|
450
|
+
}
|
451
|
+
|
452
|
+
static VALUE database_get(VALUE self, VALUE vtxn, VALUE vkey) {
|
453
|
+
DATABASE_TRANSACTION(self, vtxn, database, transaction, environment);
|
454
|
+
vkey = StringValue(vkey);
|
455
|
+
MDB_val key, value;
|
456
|
+
key.mv_size = RSTRING_LEN(vkey);
|
457
|
+
key.mv_data = RSTRING_PTR(vkey);
|
458
|
+
check(mdb_get(transaction->txn, database->dbi, &key, &value));
|
459
|
+
return rb_str_new(value.mv_data, value.mv_size);
|
460
|
+
}
|
461
|
+
|
462
|
+
static VALUE database_put(int argc, VALUE *argv, VALUE self) {
|
463
|
+
VALUE vtxn, vkey, vval, vflags;
|
464
|
+
int n = rb_scan_args(argc, argv, "31", &vtxn, &vkey, &vval, &vflags);
|
465
|
+
|
466
|
+
DATABASE_TRANSACTION(self, vtxn, database, transaction, environment);
|
467
|
+
|
468
|
+
vkey = StringValue(vkey);
|
469
|
+
vval = StringValue(vval);
|
470
|
+
|
471
|
+
MDB_val key, value;
|
472
|
+
key.mv_size = RSTRING_LEN(vkey);
|
473
|
+
key.mv_data = RSTRING_PTR(vkey);
|
474
|
+
value.mv_size = RSTRING_LEN(vval);
|
475
|
+
value.mv_data = RSTRING_PTR(vval);
|
476
|
+
|
477
|
+
check(mdb_put(transaction->txn, database->dbi, &key, &value, n == 4 ? NUM2INT(vflags) : 0));
|
478
|
+
return Qnil;
|
479
|
+
}
|
480
|
+
|
481
|
+
static VALUE database_delete(int argc, VALUE *argv, VALUE self) {
|
482
|
+
VALUE vtxn, vkey, vval;
|
483
|
+
int n = rb_scan_args(argc, argv, "21", &vtxn, &vkey, &vval);
|
484
|
+
|
485
|
+
DATABASE_TRANSACTION(self, vtxn, database, transaction, environment);
|
486
|
+
|
487
|
+
vkey = StringValue(vkey);
|
488
|
+
|
489
|
+
MDB_val key;
|
490
|
+
key.mv_size = RSTRING_LEN(vkey);
|
491
|
+
key.mv_data = RSTRING_PTR(vkey);
|
492
|
+
|
493
|
+
if (n == 3) {
|
494
|
+
vval = StringValue(vval);
|
495
|
+
MDB_val value;
|
496
|
+
value.mv_size = RSTRING_LEN(vval);
|
497
|
+
value.mv_data = RSTRING_PTR(vval);
|
498
|
+
check(mdb_del(transaction->txn, database->dbi, &key, &value));
|
499
|
+
} else {
|
500
|
+
check(mdb_del(transaction->txn, database->dbi, &key, 0));
|
501
|
+
}
|
502
|
+
|
503
|
+
return Qnil;
|
504
|
+
}
|
505
|
+
|
506
|
+
/*-----------------------------------------------------------------------------
|
507
|
+
* Cursor functions
|
508
|
+
*----------------------------------------------------------------------------*/
|
509
|
+
|
510
|
+
static void cursor_free(Cursor* cursor) {
|
511
|
+
if (cursor->cur)
|
512
|
+
mdb_cursor_close(cursor->cur);
|
513
|
+
free(cursor);
|
514
|
+
}
|
515
|
+
|
516
|
+
static void cursor_mark(Cursor* cursor) {
|
517
|
+
rb_gc_mark(cursor->database);
|
518
|
+
rb_gc_mark(cursor->transaction);
|
519
|
+
}
|
520
|
+
|
521
|
+
static VALUE database_cursor(VALUE self, VALUE vtxn) {
|
522
|
+
DATABASE_TRANSACTION(self, vtxn, database, transaction, environment);
|
523
|
+
|
524
|
+
MDB_cursor* cur;
|
525
|
+
check(mdb_cursor_open(transaction->txn, database->dbi, &cur));
|
526
|
+
|
527
|
+
Cursor* cursor;
|
528
|
+
VALUE vcur = Data_Make_Struct(cCursor, Cursor, cursor_mark, cursor_free, cursor);
|
529
|
+
cursor->cur = cur;
|
530
|
+
cursor->database = self;
|
531
|
+
cursor->transaction = vtxn;
|
532
|
+
|
533
|
+
return vcur;
|
534
|
+
}
|
535
|
+
|
536
|
+
static VALUE cursor_transaction(VALUE self) {
|
537
|
+
CURSOR(self, cursor, database, transaction, environment);
|
538
|
+
return cursor->transaction;
|
539
|
+
}
|
540
|
+
|
541
|
+
static VALUE cursor_database(VALUE self) {
|
542
|
+
CURSOR(self, cursor, database, transaction, environment);
|
543
|
+
return cursor->database;
|
544
|
+
}
|
545
|
+
|
546
|
+
static VALUE cursor_close(VALUE self) {
|
547
|
+
CURSOR(self, cursor, database, transaction, environment);
|
548
|
+
mdb_cursor_close(cursor->cur);
|
549
|
+
cursor->cur = 0;
|
550
|
+
return Qnil;
|
551
|
+
}
|
552
|
+
|
553
|
+
static VALUE cursor_first(VALUE self) {
|
554
|
+
CURSOR(self, cursor, database, transaction, environment);
|
555
|
+
MDB_val key, value;
|
556
|
+
|
557
|
+
check(mdb_cursor_get(cursor->cur, &key, &value, MDB_FIRST));
|
558
|
+
return rb_assoc_new(rb_str_new(key.mv_data, key.mv_size), rb_str_new(value.mv_data, value.mv_size));
|
559
|
+
}
|
560
|
+
|
561
|
+
static VALUE cursor_next(VALUE self) {
|
562
|
+
CURSOR(self, cursor, database, transaction, environment);
|
563
|
+
MDB_val key, value;
|
564
|
+
|
565
|
+
check(mdb_cursor_get(cursor->cur, &key, &value, MDB_NEXT));
|
566
|
+
return rb_assoc_new(rb_str_new(key.mv_data, key.mv_size), rb_str_new(value.mv_data, value.mv_size));
|
567
|
+
}
|
568
|
+
|
569
|
+
static VALUE cursor_set(VALUE self, VALUE inkey) {
|
570
|
+
CURSOR(self, cursor, database, transaction, environment);
|
571
|
+
MDB_val key, value;
|
572
|
+
|
573
|
+
key.mv_size = RSTRING_LEN(inkey);
|
574
|
+
key.mv_data = StringValuePtr(inkey);
|
575
|
+
|
576
|
+
check(mdb_cursor_get(cursor->cur, &key, &value, MDB_SET));
|
577
|
+
return rb_assoc_new(rb_str_new(key.mv_data, key.mv_size), rb_str_new(value.mv_data, value.mv_size));
|
578
|
+
}
|
579
|
+
|
580
|
+
static VALUE cursor_set_range(VALUE self, VALUE inkey) {
|
581
|
+
CURSOR(self, cursor, database, transaction, environment);
|
582
|
+
MDB_val key, value;
|
583
|
+
|
584
|
+
key.mv_size = RSTRING_LEN(inkey);
|
585
|
+
key.mv_data = StringValuePtr(inkey);
|
586
|
+
|
587
|
+
check(mdb_cursor_get(cursor->cur, &key, &value, MDB_SET_RANGE));
|
588
|
+
return rb_assoc_new(rb_str_new(key.mv_data, key.mv_size), rb_str_new(value.mv_data, value.mv_size));
|
589
|
+
}
|
590
|
+
|
591
|
+
static VALUE cursor_get(VALUE self) {
|
592
|
+
CURSOR(self, cursor, database, transaction, environment);
|
593
|
+
// TODO
|
594
|
+
return Qnil;
|
595
|
+
}
|
596
|
+
|
597
|
+
static VALUE cursor_put(VALUE self) {
|
598
|
+
CURSOR(self, cursor, database, transaction, environment);
|
599
|
+
// TODO
|
600
|
+
return Qnil;
|
601
|
+
}
|
602
|
+
|
603
|
+
static VALUE cursor_delete(int argc, VALUE *argv, VALUE self) {
|
604
|
+
CURSOR(self, cursor, database, transaction, environment);
|
605
|
+
VALUE flags;
|
606
|
+
int n = rb_scan_args(argc, argv, "01", &flags);
|
607
|
+
check(mdb_cursor_del(cursor->cur, n == 1 ? NUM2INT(flags) : 0));
|
608
|
+
return Qnil;
|
609
|
+
}
|
610
|
+
|
611
|
+
static VALUE cursor_count(VALUE self) {
|
612
|
+
CURSOR(self, cursor, database, transaction, environment);
|
613
|
+
size_t count;
|
614
|
+
check(mdb_cursor_count(cursor->cur, &count));
|
615
|
+
return INT2NUM(count);
|
616
|
+
}
|
617
|
+
|
618
|
+
|
619
|
+
void Init_lmdb_ext() {
|
620
|
+
VALUE mLMDB, mExt;
|
621
|
+
|
622
|
+
mLMDB = rb_define_module("LMDB");
|
623
|
+
rb_define_const(mLMDB, "VERSION", rb_str_new2(MDB_VERSION_STRING));
|
624
|
+
|
625
|
+
mExt = rb_define_module_under(mLMDB, "Ext");
|
626
|
+
rb_define_singleton_method(mExt, "open", environment_open, -1);
|
627
|
+
|
628
|
+
#define NUM_CONST(name) rb_define_const(mLMDB, #name, INT2NUM(MDB_##name))
|
629
|
+
|
630
|
+
// Versions
|
631
|
+
NUM_CONST(VERSION_MAJOR);
|
632
|
+
NUM_CONST(VERSION_MINOR);
|
633
|
+
NUM_CONST(VERSION_PATCH);
|
634
|
+
|
635
|
+
// Environment flags
|
636
|
+
NUM_CONST(FIXEDMAP);
|
637
|
+
NUM_CONST(NOSUBDIR);
|
638
|
+
NUM_CONST(NOSYNC);
|
639
|
+
NUM_CONST(RDONLY);
|
640
|
+
NUM_CONST(NOMETASYNC);
|
641
|
+
NUM_CONST(WRITEMAP);
|
642
|
+
NUM_CONST(MAPASYNC);
|
643
|
+
|
644
|
+
// Database flags
|
645
|
+
NUM_CONST(REVERSEKEY);
|
646
|
+
NUM_CONST(DUPSORT);
|
647
|
+
NUM_CONST(INTEGERKEY);
|
648
|
+
NUM_CONST(DUPFIXED);
|
649
|
+
NUM_CONST(INTEGERDUP);
|
650
|
+
NUM_CONST(REVERSEDUP);
|
651
|
+
NUM_CONST(CREATE);
|
652
|
+
NUM_CONST(NOOVERWRITE);
|
653
|
+
NUM_CONST(NODUPDATA);
|
654
|
+
NUM_CONST(CURRENT);
|
655
|
+
NUM_CONST(RESERVE);
|
656
|
+
NUM_CONST(APPEND);
|
657
|
+
NUM_CONST(APPENDDUP);
|
658
|
+
NUM_CONST(MULTIPLE);
|
659
|
+
|
660
|
+
cError = rb_define_class_under(mExt, "Error", rb_eRuntimeError);
|
661
|
+
#define ERROR(name) cError_##name = rb_define_class_under(cError, #name, cError);
|
662
|
+
#include "errors.h"
|
663
|
+
#undef ERROR
|
664
|
+
|
665
|
+
cStat = rb_define_class_under(mExt, "Stat", rb_cObject);
|
666
|
+
rb_define_method(cStat, "psize", stat_psize, 0);
|
667
|
+
rb_define_method(cStat, "depth", stat_depth, 0);
|
668
|
+
rb_define_method(cStat, "branch_pages", stat_branch_pages, 0);
|
669
|
+
rb_define_method(cStat, "leaf_pages", stat_leaf_pages, 0);
|
670
|
+
rb_define_method(cStat, "overflow_pages", stat_overflow_pages, 0);
|
671
|
+
rb_define_method(cStat, "entries", stat_entries, 0);
|
672
|
+
|
673
|
+
cInfo = rb_define_class_under(mExt, "Info", rb_cObject);
|
674
|
+
rb_define_method(cInfo, "mapaddr", info_mapaddr, 0);
|
675
|
+
rb_define_method(cInfo, "mapsize", info_mapsize, 0);
|
676
|
+
rb_define_method(cInfo, "last_pgno", info_last_pgno, 0);
|
677
|
+
rb_define_method(cInfo, "last_txnid", info_last_txnid, 0);
|
678
|
+
rb_define_method(cInfo, "maxreaders", info_maxreaders, 0);
|
679
|
+
rb_define_method(cInfo, "numreaders", info_numreaders, 0);
|
680
|
+
|
681
|
+
cEnvironment = rb_define_class_under(mExt, "Environment", rb_cObject);
|
682
|
+
rb_define_singleton_method(cEnvironment, "open", environment_open, -1);
|
683
|
+
rb_define_method(cEnvironment, "close", environment_close, 0);
|
684
|
+
rb_define_method(cEnvironment, "stat", environment_stat, 0);
|
685
|
+
rb_define_method(cEnvironment, "info", environment_info, 0);
|
686
|
+
rb_define_method(cEnvironment, "copy", environment_copy, 1);
|
687
|
+
rb_define_method(cEnvironment, "sync", environment_sync, -1);
|
688
|
+
rb_define_method(cEnvironment, "flags=", environment_set_flags, 1);
|
689
|
+
rb_define_method(cEnvironment, "flags", environment_flags, 0);
|
690
|
+
rb_define_method(cEnvironment, "path", environment_path, 0);
|
691
|
+
rb_define_method(cEnvironment, "transaction", environment_transaction, -1);
|
692
|
+
rb_define_method(cEnvironment, "open", database_open, -1);
|
693
|
+
|
694
|
+
cDatabase = rb_define_class_under(mExt, "Database", rb_cObject);
|
695
|
+
rb_define_method(cDatabase, "close", database_close, 0);
|
696
|
+
rb_define_method(cDatabase, "stat", database_stat, 1);
|
697
|
+
rb_define_method(cDatabase, "drop", database_drop, 1);
|
698
|
+
rb_define_method(cDatabase, "clear", database_clear, 1);
|
699
|
+
rb_define_method(cDatabase, "get", database_get, 2);
|
700
|
+
rb_define_method(cDatabase, "put", database_put, -1);
|
701
|
+
rb_define_method(cDatabase, "delete", database_delete, -1);
|
702
|
+
rb_define_method(cDatabase, "cursor", database_cursor, 1);
|
703
|
+
rb_define_method(cDatabase, "environment", database_environment, 0);
|
704
|
+
|
705
|
+
cTransaction = rb_define_class_under(mExt, "Transaction", rb_cObject);
|
706
|
+
rb_define_method(cTransaction, "abort", transaction_abort, 0);
|
707
|
+
rb_define_method(cTransaction, "commit", transaction_commit, 0);
|
708
|
+
rb_define_method(cTransaction, "reset", transaction_reset, 0);
|
709
|
+
rb_define_method(cTransaction, "renew", transaction_renew, 0);
|
710
|
+
rb_define_method(cTransaction, "transaction", transaction_transaction, 0);
|
711
|
+
rb_define_method(cTransaction, "environment", transaction_environment, 0);
|
712
|
+
rb_define_method(cTransaction, "parent", transaction_parent, 0);
|
713
|
+
|
714
|
+
cCursor = rb_define_class_under(mExt, "Cursor", rb_cObject);
|
715
|
+
rb_define_method(cCursor, "close", cursor_close, 0);
|
716
|
+
rb_define_method(cCursor, "get", cursor_get, 0);
|
717
|
+
rb_define_method(cCursor, "first", cursor_first, 0);
|
718
|
+
rb_define_method(cCursor, "next", cursor_next, 0);
|
719
|
+
rb_define_method(cCursor, "set", cursor_set, 1);
|
720
|
+
rb_define_method(cCursor, "set_range", cursor_set_range, 1);
|
721
|
+
rb_define_method(cCursor, "put", cursor_put, 0);
|
722
|
+
rb_define_method(cCursor, "delete", cursor_delete, 0);
|
723
|
+
rb_define_method(cCursor, "database", cursor_database, 0);
|
724
|
+
rb_define_method(cCursor, "transaction", cursor_transaction, 0);
|
725
|
+
}
|