lmdb 0.5.3 → 0.6.1
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 +4 -4
- data/CONTRIBUTORS +1 -0
- data/Rakefile +9 -0
- data/behaviour.org +35 -0
- data/ext/lmdb_ext/extconf.rb +8 -3
- data/ext/lmdb_ext/lmdb_ext.c +314 -200
- data/ext/lmdb_ext/lmdb_ext.h +19 -3
- data/lib/lmdb/database.rb +9 -8
- data/lib/lmdb/version.rb +1 -1
- data/lmdb.gemspec +5 -4
- data/spec/helper.rb +3 -0
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/.gitignore +8 -0
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/COPYRIGHT +1 -1
- data/vendor/libraries/liblmdb/Doxyfile +1631 -0
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/LICENSE +0 -0
- data/vendor/libraries/liblmdb/Makefile +118 -0
- data/vendor/libraries/liblmdb/intro.doc +192 -0
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/lmdb.h +161 -61
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/mdb.c +3244 -1302
- data/vendor/libraries/liblmdb/mdb_copy.1 +61 -0
- data/vendor/libraries/liblmdb/mdb_copy.c +84 -0
- data/vendor/libraries/liblmdb/mdb_drop.1 +40 -0
- data/vendor/libraries/liblmdb/mdb_drop.c +135 -0
- data/vendor/libraries/liblmdb/mdb_dump.1 +81 -0
- data/vendor/libraries/liblmdb/mdb_dump.c +319 -0
- data/vendor/libraries/liblmdb/mdb_load.1 +84 -0
- data/vendor/libraries/liblmdb/mdb_load.c +492 -0
- data/vendor/libraries/liblmdb/mdb_stat.1 +70 -0
- data/vendor/libraries/liblmdb/mdb_stat.c +264 -0
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/midl.c +66 -5
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/midl.h +19 -5
- data/vendor/libraries/liblmdb/mtest.c +177 -0
- data/vendor/libraries/liblmdb/mtest2.c +124 -0
- data/vendor/libraries/liblmdb/mtest3.c +133 -0
- data/vendor/libraries/liblmdb/mtest4.c +168 -0
- data/vendor/libraries/liblmdb/mtest5.c +135 -0
- data/vendor/libraries/liblmdb/mtest6.c +141 -0
- data/vendor/libraries/liblmdb/sample-bdb.txt +73 -0
- data/vendor/libraries/liblmdb/sample-mdb.txt +62 -0
- data/vendor/libraries/liblmdb/tooltag +27 -0
- metadata +53 -18
- data/.gitignore +0 -13
- data/.travis.yml +0 -14
- data/ext/lmdb_ext/liblmdb/CHANGES +0 -112
data/ext/lmdb_ext/lmdb_ext.c
CHANGED
@@ -26,6 +26,7 @@ static void check(int code) {
|
|
26
26
|
|
27
27
|
const char* err = mdb_strerror(code);
|
28
28
|
const char* sep = strchr(err, ':');
|
29
|
+
// increment the offset by two in case there is a colon (plus space)
|
29
30
|
if (sep)
|
30
31
|
err = sep + 2;
|
31
32
|
|
@@ -37,20 +38,21 @@ static void check(int code) {
|
|
37
38
|
}
|
38
39
|
|
39
40
|
static void transaction_free(Transaction* transaction) {
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
41
|
+
if (transaction->txn) {
|
42
|
+
//int id = (int)mdb_txn_id(transaction->txn);
|
43
|
+
//rb_warn(sprintf("Memory leak: Garbage collecting active transaction %d", id));
|
44
|
+
rb_warn("Memory leak: Garbage collecting active transaction");
|
45
|
+
// transaction_abort(transaction);
|
46
|
+
// mdb_txn_abort(transaction->txn);
|
47
|
+
}
|
48
|
+
free(transaction);
|
48
49
|
}
|
49
50
|
|
50
51
|
static void transaction_mark(Transaction* transaction) {
|
51
|
-
|
52
|
-
|
53
|
-
|
52
|
+
rb_gc_mark(transaction->parent);
|
53
|
+
rb_gc_mark(transaction->child);
|
54
|
+
rb_gc_mark(transaction->env);
|
55
|
+
rb_gc_mark(transaction->cursors);
|
54
56
|
}
|
55
57
|
|
56
58
|
/**
|
@@ -130,55 +132,80 @@ static VALUE transaction_env(VALUE self) {
|
|
130
132
|
* @return [false,true] whether the transaction is read-only.
|
131
133
|
*/
|
132
134
|
static VALUE transaction_is_readonly(VALUE self) {
|
133
|
-
|
134
|
-
|
135
|
-
|
135
|
+
TRANSACTION(self, transaction);
|
136
|
+
//MDB_txn* txn = transaction->txn;
|
137
|
+
return (transaction->flags & MDB_RDONLY) ? Qtrue : Qfalse;
|
136
138
|
}
|
137
139
|
|
138
140
|
|
139
141
|
static void transaction_finish(VALUE self, int commit) {
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
142
|
+
TRANSACTION(self, transaction);
|
143
|
+
|
144
|
+
if (!transaction->txn)
|
145
|
+
rb_raise(cError, "Transaction is already terminated");
|
146
|
+
|
147
|
+
if (transaction->thread != rb_thread_current())
|
148
|
+
rb_raise(cError, "The thread closing the transaction "
|
149
|
+
"is not the one that opened it");
|
150
|
+
|
151
|
+
// ensure the transaction being closed is the active one
|
152
|
+
VALUE p = environment_active_txn(transaction->env);
|
153
|
+
while (!NIL_P(p) && p != self) {
|
154
|
+
TRANSACTION(p, txn);
|
155
|
+
p = txn->parent;
|
156
|
+
}
|
157
|
+
// bail out if the transaction `self` is not the active one
|
158
|
+
if (p != self)
|
159
|
+
rb_raise(cError, "Transaction is not active");
|
160
|
+
|
161
|
+
// now eliminate the cursors
|
162
|
+
long i;
|
163
|
+
for (i=0; i<RARRAY_LEN(transaction->cursors); i++) {
|
164
|
+
VALUE cursor = RARRAY_AREF(transaction->cursors, i);
|
165
|
+
cursor_close(cursor);
|
166
|
+
}
|
167
|
+
rb_ary_clear(transaction->cursors);
|
168
|
+
|
169
|
+
// now actually finish the internal transaction
|
170
|
+
int ret = 0;
|
171
|
+
if (commit)
|
172
|
+
ret = mdb_txn_commit(transaction->txn);
|
173
|
+
else
|
174
|
+
mdb_txn_abort(transaction->txn);
|
175
|
+
|
176
|
+
// eliminate child transactions
|
177
|
+
if (transaction->child) {
|
178
|
+
p = self; // again this is a VALUE
|
179
|
+
Transaction* txn = transaction; // and this is the struct
|
180
|
+
|
181
|
+
// descend into deepest child transaction
|
182
|
+
do {
|
183
|
+
p = txn->child;
|
184
|
+
// this is TRANSACTION minus the declaration
|
185
|
+
Data_Get_Struct(txn->child, Transaction, txn);
|
186
|
+
} while (txn->child);
|
187
|
+
|
188
|
+
// now we ascend back up
|
172
189
|
while (p != self) {
|
173
|
-
|
174
|
-
|
175
|
-
|
190
|
+
TRANSACTION(p, txn);
|
191
|
+
txn->txn = 0;
|
192
|
+
p = txn->parent;
|
176
193
|
}
|
177
|
-
|
194
|
+
}
|
195
|
+
transaction->txn = 0;
|
178
196
|
|
179
|
-
|
197
|
+
// no more active read-write transaction; unset the registry
|
198
|
+
if (!(transaction->flags & MDB_RDONLY) && !transaction->parent) {
|
199
|
+
ENVIRONMENT(transaction->env, env);
|
200
|
+
// maybe this should be Qnil, i dunno
|
201
|
+
env->rw_txn_thread = (VALUE)NULL;
|
202
|
+
}
|
180
203
|
|
181
|
-
|
204
|
+
// now set the active transaction to the parent, if there is one
|
205
|
+
environment_set_active_txn(transaction->env, transaction->thread,
|
206
|
+
transaction->parent);
|
207
|
+
|
208
|
+
check(ret);
|
182
209
|
}
|
183
210
|
|
184
211
|
// Ruby 1.8.7 compatibility
|
@@ -219,65 +246,143 @@ static void stop_txn_begin(void *arg)
|
|
219
246
|
txn_args->stop = 1;
|
220
247
|
}
|
221
248
|
|
249
|
+
/**
|
250
|
+
* This is the code that opens transactions. Read-write transactions
|
251
|
+
* have to be called outside the GVL because they will block without
|
252
|
+
*
|
253
|
+
*
|
254
|
+
* Here is the basic problem with LMDB transactions:
|
255
|
+
*
|
256
|
+
* - There can only be one read-write transaction per LMDB
|
257
|
+
* ENVIRONMENT, not process, not thread.
|
258
|
+
*
|
259
|
+
* - Read-write transactions can nest.
|
260
|
+
*
|
261
|
+
* - There can only be one active transaction per thread.
|
262
|
+
*
|
263
|
+
* - Every thread can have an active read-ONLY transaction, but only one.
|
264
|
+
*
|
265
|
+
* - This is because read-only transactions canNOT nest (it is
|
266
|
+
* meaningless to have a nested read-only transaction)
|
267
|
+
*
|
268
|
+
* - Furthermore it is an error to open a read-only transaction under
|
269
|
+
* a read-write transaction.
|
270
|
+
*
|
271
|
+
* - Same goes for opening a read-write transaction in the same thread
|
272
|
+
* as an active read-only transaction.
|
273
|
+
*
|
274
|
+
* Nevertheless, the downstream Ruby user may not be able to
|
275
|
+
* completely control calls to env.transaction (e.g. if they are
|
276
|
+
* wrapping a transaction around a proc or lambda that happens to
|
277
|
+
* contain its own transaction), plus every call in LMDB needs to be
|
278
|
+
* implicitly wrapped in a transaction anyway.
|
279
|
+
*
|
280
|
+
* What this means is that we will, first, have to keep explicit track
|
281
|
+
* of the read-write transaction, if one exists. Second, we will have
|
282
|
+
* to simulate nesting for read-only transactions, so the behaviour in
|
283
|
+
* Ruby is the same as a read-write transaction.
|
284
|
+
*
|
285
|
+
*/
|
286
|
+
|
222
287
|
static VALUE with_transaction(VALUE venv, VALUE(*fn)(VALUE), VALUE arg, int flags) {
|
223
|
-
|
288
|
+
ENVIRONMENT(venv, environment);
|
224
289
|
|
225
|
-
|
226
|
-
|
290
|
+
MDB_txn* txn;
|
291
|
+
TxnArgs txn_args;
|
227
292
|
|
228
|
-
|
229
|
-
|
293
|
+
VALUE thread = rb_thread_current();
|
294
|
+
VALUE vparent = environment_active_txn(venv);
|
230
295
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
txn_args.htxn = &txn;
|
235
|
-
txn_args.result = 0;
|
236
|
-
txn_args.stop = 0;
|
296
|
+
Transaction* tparent = NULL;
|
297
|
+
if (vparent && !NIL_P(vparent))
|
298
|
+
Data_Get_Struct(vparent, Transaction, tparent);
|
237
299
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
300
|
+
// rb_warn("fart lol");
|
301
|
+
|
302
|
+
// XXX note this is a cursed goto loop that could almost certainly
|
303
|
+
// be rewritten as a do-while
|
304
|
+
retry:
|
305
|
+
txn = NULL;
|
306
|
+
|
307
|
+
txn_args.env = environment->env;
|
308
|
+
txn_args.parent = active_txn(venv);
|
309
|
+
txn_args.flags = flags;
|
310
|
+
txn_args.htxn = &txn;
|
311
|
+
txn_args.result = 0;
|
312
|
+
txn_args.stop = 0;
|
313
|
+
|
314
|
+
if (flags & MDB_RDONLY) {
|
315
|
+
if (tparent && tparent->flags & MDB_RDONLY)
|
316
|
+
// this is a no-op: put the same actual transaction in a
|
317
|
+
// different wrapper struct
|
318
|
+
txn = txn_args.parent;
|
319
|
+
else
|
320
|
+
// this will return an error if the parent transaction is
|
321
|
+
// read-write, so we don't need to handle the case explicitly
|
322
|
+
call_txn_begin(&txn_args);
|
323
|
+
}
|
324
|
+
else {
|
325
|
+
if (tparent) {
|
326
|
+
// first we have to determine if we're on the same thread
|
327
|
+
// as the parent, which in turn must be the same as the
|
328
|
+
// environment's registry for which thread has the
|
329
|
+
// read-write transaction
|
330
|
+
if (thread != tparent->thread ||
|
331
|
+
thread != environment->rw_txn_thread)
|
332
|
+
rb_raise(cError,
|
333
|
+
"Attempt to nest transaction on a different thread");
|
255
334
|
}
|
256
335
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
VALUE ret = rb_protect(fn, NIL_P(arg) ? vtxn : arg, &exception);
|
271
|
-
|
272
|
-
if (exception) {
|
273
|
-
//rb_warn("lol got exception");
|
274
|
-
if (vtxn == environment_active_txn(venv))
|
275
|
-
transaction_abort(vtxn);
|
276
|
-
rb_jump_tag(exception);
|
336
|
+
CALL_WITHOUT_GVL(call_txn_begin, &txn_args, stop_txn_begin, &txn_args);
|
337
|
+
|
338
|
+
if (txn_args.stop || !txn) {
|
339
|
+
// !txn is when rb_thread_call_without_gvl2
|
340
|
+
// returns before calling txn_begin
|
341
|
+
if (txn) {
|
342
|
+
mdb_txn_abort(txn);
|
343
|
+
txn_args.result = 0;
|
344
|
+
}
|
345
|
+
|
346
|
+
//rb_warn("got here lol");
|
347
|
+
rb_thread_check_ints();
|
348
|
+
goto retry; // in what cases do we get here?
|
277
349
|
}
|
350
|
+
|
351
|
+
// set the thread
|
352
|
+
environment->rw_txn_thread = thread;
|
353
|
+
}
|
354
|
+
|
355
|
+
// this will raise unless result is zero
|
356
|
+
check(txn_args.result);
|
357
|
+
|
358
|
+
Transaction* transaction;
|
359
|
+
VALUE vtxn = Data_Make_Struct(cTransaction, Transaction, transaction_mark,
|
360
|
+
transaction_free, transaction);
|
361
|
+
transaction->parent = vparent;
|
362
|
+
transaction->env = venv;
|
363
|
+
transaction->txn = txn;
|
364
|
+
transaction->flags = flags;
|
365
|
+
transaction->thread = rb_thread_current();
|
366
|
+
transaction->cursors = rb_ary_new();
|
367
|
+
|
368
|
+
// set the parent's child to self
|
369
|
+
if (tparent) tparent->child = vtxn;
|
370
|
+
|
371
|
+
environment_set_active_txn(venv, transaction->thread, vtxn);
|
372
|
+
|
373
|
+
// now we run the function in the transaction
|
374
|
+
int exception;
|
375
|
+
VALUE ret = rb_protect(fn, NIL_P(arg) ? vtxn : arg, &exception);
|
376
|
+
|
377
|
+
if (exception) {
|
378
|
+
// rb_warn("lol got exception");
|
278
379
|
if (vtxn == environment_active_txn(venv))
|
279
|
-
|
280
|
-
|
380
|
+
transaction_abort(vtxn);
|
381
|
+
rb_jump_tag(exception);
|
382
|
+
}
|
383
|
+
if (vtxn == environment_active_txn(venv))
|
384
|
+
transaction_commit(vtxn);
|
385
|
+
return ret;
|
281
386
|
}
|
282
387
|
|
283
388
|
static void environment_check(Environment* environment) {
|
@@ -496,47 +601,50 @@ static int environment_options(VALUE key, VALUE value, EnvironmentOptions* optio
|
|
496
601
|
* end
|
497
602
|
*/
|
498
603
|
static VALUE environment_new(int argc, VALUE *argv, VALUE klass) {
|
499
|
-
|
604
|
+
VALUE path, option_hash;
|
500
605
|
|
501
606
|
#ifdef RB_SCAN_ARGS_KEYWORDS
|
502
|
-
|
503
|
-
|
607
|
+
rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
|
608
|
+
argc, argv, "1:", &path, &option_hash);
|
504
609
|
#else
|
505
|
-
|
610
|
+
rb_scan_args(argc, argv, "1:", &path, &option_hash);
|
506
611
|
#endif
|
507
612
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
check(
|
533
|
-
VALUE expanded_path = rb_file_expand_path(path, Qnil);
|
534
|
-
check(mdb_env_open(env, StringValueCStr(expanded_path), options.flags, options.mode));
|
613
|
+
EnvironmentOptions options = {
|
614
|
+
.flags = MDB_NOTLS,
|
615
|
+
.maxreaders = -1,
|
616
|
+
.maxdbs = 128,
|
617
|
+
.mapsize = 0,
|
618
|
+
.mode = 0755,
|
619
|
+
};
|
620
|
+
if (!NIL_P(option_hash))
|
621
|
+
rb_hash_foreach(option_hash, (int (*)(ANYARGS))environment_options,
|
622
|
+
(VALUE)&options);
|
623
|
+
|
624
|
+
MDB_env* env;
|
625
|
+
check(mdb_env_create(&env));
|
626
|
+
|
627
|
+
Environment* environment;
|
628
|
+
VALUE venv = Data_Make_Struct(cEnvironment, Environment, environment_mark,
|
629
|
+
environment_free, environment);
|
630
|
+
environment->env = env;
|
631
|
+
environment->thread_txn_hash = rb_hash_new();
|
632
|
+
environment->txn_thread_hash = rb_hash_new();
|
633
|
+
|
634
|
+
if (options.maxreaders > 0)
|
635
|
+
check(mdb_env_set_maxreaders(env, options.maxreaders));
|
636
|
+
if (options.mapsize > 0)
|
637
|
+
check(mdb_env_set_mapsize(env, options.mapsize));
|
535
638
|
|
536
|
-
|
537
|
-
|
639
|
+
check(mdb_env_set_maxdbs(env, options.maxdbs <= 0 ? 1 : options.maxdbs));
|
640
|
+
VALUE expanded_path = rb_file_expand_path(path, Qnil);
|
641
|
+
check(mdb_env_open(env, StringValueCStr(expanded_path), options.flags,
|
642
|
+
options.mode));
|
538
643
|
|
539
|
-
|
644
|
+
if (rb_block_given_p())
|
645
|
+
return rb_ensure(rb_yield, venv, environment_close, venv);
|
646
|
+
|
647
|
+
return venv;
|
540
648
|
}
|
541
649
|
|
542
650
|
/**
|
@@ -674,7 +782,7 @@ static MDB_txn* active_txn(VALUE self) {
|
|
674
782
|
return 0;
|
675
783
|
TRANSACTION(vtxn, transaction);
|
676
784
|
if (!transaction->txn)
|
677
|
-
rb_raise(cError, "Transaction is terminated");
|
785
|
+
rb_raise(cError, "Transaction is already terminated");
|
678
786
|
if (transaction->thread != rb_thread_current())
|
679
787
|
rb_raise(cError, "Wrong thread");
|
680
788
|
return transaction->txn;
|
@@ -773,32 +881,35 @@ static void database_mark(Database* database) {
|
|
773
881
|
* transaction or a read-only environment.
|
774
882
|
*/
|
775
883
|
static VALUE environment_database(int argc, VALUE *argv, VALUE self) {
|
776
|
-
|
777
|
-
|
778
|
-
|
884
|
+
ENVIRONMENT(self, environment);
|
885
|
+
if (!active_txn(self))
|
886
|
+
return call_with_transaction(self, self, "database", argc, argv, 0);
|
779
887
|
|
780
|
-
|
888
|
+
VALUE name, option_hash;
|
781
889
|
#ifdef RB_SCAN_ARGS_KEYWORDS
|
782
|
-
|
783
|
-
|
890
|
+
rb_scan_args_kw(RB_SCAN_ARGS_KEYWORDS,
|
891
|
+
argc, argv, "01:", &name, &option_hash);
|
784
892
|
#else
|
785
|
-
|
893
|
+
rb_scan_args(argc, argv, "01:", &name, &option_hash);
|
786
894
|
#endif
|
787
895
|
|
788
896
|
|
789
|
-
|
790
|
-
|
791
|
-
|
897
|
+
int flags = 0;
|
898
|
+
if (!NIL_P(option_hash))
|
899
|
+
rb_hash_foreach(option_hash, (int (*)(ANYARGS))database_flags,
|
900
|
+
(VALUE)&flags);
|
792
901
|
|
793
|
-
|
794
|
-
|
902
|
+
MDB_dbi dbi;
|
903
|
+
check(mdb_dbi_open(need_txn(self), NIL_P(name) ? 0 : StringValueCStr(name),
|
904
|
+
flags, &dbi));
|
795
905
|
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
906
|
+
Database* database;
|
907
|
+
VALUE vdb = Data_Make_Struct(cDatabase, Database, database_mark, free,
|
908
|
+
database);
|
909
|
+
database->dbi = dbi;
|
910
|
+
database->env = self;
|
800
911
|
|
801
|
-
|
912
|
+
return vdb;
|
802
913
|
}
|
803
914
|
|
804
915
|
/**
|
@@ -960,33 +1071,34 @@ static VALUE database_get(VALUE self, VALUE vkey) {
|
|
960
1071
|
* data.
|
961
1072
|
*/
|
962
1073
|
static VALUE database_put(int argc, VALUE *argv, VALUE self) {
|
963
|
-
|
964
|
-
|
965
|
-
|
1074
|
+
DATABASE(self, database);
|
1075
|
+
if (!active_txn(database->env))
|
1076
|
+
return call_with_transaction(database->env, self, "put", argc, argv, 0);
|
966
1077
|
|
967
|
-
|
1078
|
+
VALUE vkey, vval, option_hash = Qnil;
|
968
1079
|
#ifdef RB_SCAN_ARGS_KEYWORDS
|
969
|
-
|
970
|
-
|
1080
|
+
rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
|
1081
|
+
argc, argv, "20:", &vkey, &vval, &option_hash);
|
971
1082
|
#else
|
972
|
-
|
1083
|
+
rb_scan_args(argc, argv, "20:", &vkey, &vval, &option_hash);
|
973
1084
|
#endif
|
974
1085
|
|
975
|
-
|
976
|
-
|
977
|
-
|
1086
|
+
int flags = 0;
|
1087
|
+
if (!NIL_P(option_hash))
|
1088
|
+
rb_hash_foreach(option_hash, (int (*)(ANYARGS))database_put_flags,
|
1089
|
+
(VALUE)&flags);
|
978
1090
|
|
979
|
-
|
980
|
-
|
1091
|
+
vkey = StringValue(vkey);
|
1092
|
+
vval = StringValue(vval);
|
981
1093
|
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
1094
|
+
MDB_val key, value;
|
1095
|
+
key.mv_size = RSTRING_LEN(vkey);
|
1096
|
+
key.mv_data = RSTRING_PTR(vkey);
|
1097
|
+
value.mv_size = RSTRING_LEN(vval);
|
1098
|
+
value.mv_data = RSTRING_PTR(vval);
|
987
1099
|
|
988
|
-
|
989
|
-
|
1100
|
+
check(mdb_put(need_txn(database->env), database->dbi, &key, &value, flags));
|
1101
|
+
return Qnil;
|
990
1102
|
}
|
991
1103
|
|
992
1104
|
/**
|
@@ -1350,31 +1462,32 @@ static VALUE cursor_get(VALUE self) {
|
|
1350
1462
|
* data.
|
1351
1463
|
*/
|
1352
1464
|
static VALUE cursor_put(int argc, VALUE* argv, VALUE self) {
|
1353
|
-
|
1465
|
+
CURSOR(self, cursor);
|
1354
1466
|
|
1355
|
-
|
1467
|
+
VALUE vkey, vval, option_hash;
|
1356
1468
|
#ifdef RB_SCAN_ARGS_KEYWORDS
|
1357
|
-
|
1358
|
-
|
1469
|
+
rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
|
1470
|
+
argc, argv, "2:", &vkey, &vval, &option_hash);
|
1359
1471
|
#else
|
1360
|
-
|
1472
|
+
rb_scan_args(argc, argv, "2:", &vkey, &vval, &option_hash);
|
1361
1473
|
#endif
|
1362
1474
|
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1475
|
+
int flags = 0;
|
1476
|
+
if (!NIL_P(option_hash))
|
1477
|
+
rb_hash_foreach(option_hash, (int (*)(ANYARGS))cursor_put_flags,
|
1478
|
+
(VALUE)&flags);
|
1366
1479
|
|
1367
|
-
|
1368
|
-
|
1480
|
+
vkey = StringValue(vkey);
|
1481
|
+
vval = StringValue(vval);
|
1369
1482
|
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1483
|
+
MDB_val key, value;
|
1484
|
+
key.mv_size = RSTRING_LEN(vkey);
|
1485
|
+
key.mv_data = RSTRING_PTR(vkey);
|
1486
|
+
value.mv_size = RSTRING_LEN(vval);
|
1487
|
+
value.mv_data = RSTRING_PTR(vval);
|
1375
1488
|
|
1376
|
-
|
1377
|
-
|
1489
|
+
check(mdb_cursor_put(cursor->cur, &key, &value, flags));
|
1490
|
+
return Qnil;
|
1378
1491
|
}
|
1379
1492
|
|
1380
1493
|
#define METHOD cursor_delete_flags
|
@@ -1392,22 +1505,23 @@ static VALUE cursor_put(int argc, VALUE* argv, VALUE self) {
|
|
1392
1505
|
* if the database was opened with +:dupsort+.
|
1393
1506
|
*/
|
1394
1507
|
static VALUE cursor_delete(int argc, VALUE *argv, VALUE self) {
|
1395
|
-
|
1508
|
+
CURSOR(self, cursor);
|
1396
1509
|
|
1397
|
-
|
1510
|
+
VALUE option_hash;
|
1398
1511
|
#ifdef RB_SCAN_ARGS_KEYWORDS
|
1399
|
-
|
1400
|
-
|
1512
|
+
rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
|
1513
|
+
argc, argv, ":", &option_hash);
|
1401
1514
|
#else
|
1402
|
-
|
1515
|
+
rb_scan_args(argc, argv, ":", &option_hash);
|
1403
1516
|
#endif
|
1404
1517
|
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1518
|
+
int flags = 0;
|
1519
|
+
if (!NIL_P(option_hash))
|
1520
|
+
rb_hash_foreach(option_hash, (int (*)(ANYARGS))cursor_delete_flags,
|
1521
|
+
(VALUE)&flags);
|
1408
1522
|
|
1409
|
-
|
1410
|
-
|
1523
|
+
check(mdb_cursor_del(cursor->cur, flags));
|
1524
|
+
return Qnil;
|
1411
1525
|
}
|
1412
1526
|
|
1413
1527
|
/**
|