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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTORS +1 -0
  3. data/Rakefile +9 -0
  4. data/behaviour.org +35 -0
  5. data/ext/lmdb_ext/extconf.rb +8 -3
  6. data/ext/lmdb_ext/lmdb_ext.c +314 -200
  7. data/ext/lmdb_ext/lmdb_ext.h +19 -3
  8. data/lib/lmdb/database.rb +9 -8
  9. data/lib/lmdb/version.rb +1 -1
  10. data/lmdb.gemspec +5 -4
  11. data/spec/helper.rb +3 -0
  12. data/{ext/lmdb_ext → vendor/libraries}/liblmdb/.gitignore +8 -0
  13. data/{ext/lmdb_ext → vendor/libraries}/liblmdb/COPYRIGHT +1 -1
  14. data/vendor/libraries/liblmdb/Doxyfile +1631 -0
  15. data/{ext/lmdb_ext → vendor/libraries}/liblmdb/LICENSE +0 -0
  16. data/vendor/libraries/liblmdb/Makefile +118 -0
  17. data/vendor/libraries/liblmdb/intro.doc +192 -0
  18. data/{ext/lmdb_ext → vendor/libraries}/liblmdb/lmdb.h +161 -61
  19. data/{ext/lmdb_ext → vendor/libraries}/liblmdb/mdb.c +3244 -1302
  20. data/vendor/libraries/liblmdb/mdb_copy.1 +61 -0
  21. data/vendor/libraries/liblmdb/mdb_copy.c +84 -0
  22. data/vendor/libraries/liblmdb/mdb_drop.1 +40 -0
  23. data/vendor/libraries/liblmdb/mdb_drop.c +135 -0
  24. data/vendor/libraries/liblmdb/mdb_dump.1 +81 -0
  25. data/vendor/libraries/liblmdb/mdb_dump.c +319 -0
  26. data/vendor/libraries/liblmdb/mdb_load.1 +84 -0
  27. data/vendor/libraries/liblmdb/mdb_load.c +492 -0
  28. data/vendor/libraries/liblmdb/mdb_stat.1 +70 -0
  29. data/vendor/libraries/liblmdb/mdb_stat.c +264 -0
  30. data/{ext/lmdb_ext → vendor/libraries}/liblmdb/midl.c +66 -5
  31. data/{ext/lmdb_ext → vendor/libraries}/liblmdb/midl.h +19 -5
  32. data/vendor/libraries/liblmdb/mtest.c +177 -0
  33. data/vendor/libraries/liblmdb/mtest2.c +124 -0
  34. data/vendor/libraries/liblmdb/mtest3.c +133 -0
  35. data/vendor/libraries/liblmdb/mtest4.c +168 -0
  36. data/vendor/libraries/liblmdb/mtest5.c +135 -0
  37. data/vendor/libraries/liblmdb/mtest6.c +141 -0
  38. data/vendor/libraries/liblmdb/sample-bdb.txt +73 -0
  39. data/vendor/libraries/liblmdb/sample-mdb.txt +62 -0
  40. data/vendor/libraries/liblmdb/tooltag +27 -0
  41. metadata +53 -18
  42. data/.gitignore +0 -13
  43. data/.travis.yml +0 -14
  44. data/ext/lmdb_ext/liblmdb/CHANGES +0 -112
@@ -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
- if (transaction->txn) {
41
- //int id = (int)mdb_txn_id(transaction->txn);
42
- //rb_warn(sprintf("Memory leak: Garbage collecting active transaction %d", id));
43
- rb_warn("Memory leak: Garbage collecting active transaction");
44
- // transaction_abort(transaction);
45
- // mdb_txn_abort(transaction->txn);
46
- }
47
- free(transaction);
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
- rb_gc_mark(transaction->parent);
52
- rb_gc_mark(transaction->env);
53
- rb_gc_mark(transaction->cursors);
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
- TRANSACTION(self, transaction);
134
- //MDB_txn* txn = transaction->txn;
135
- return (transaction->flags & MDB_RDONLY) ? Qtrue : Qfalse;
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
- TRANSACTION(self, transaction);
141
-
142
- if (!transaction->txn)
143
- rb_raise(cError, "Transaction is terminated");
144
-
145
- if (transaction->thread != rb_thread_current())
146
- rb_raise(cError, "Wrong thread");
147
-
148
- // Check nesting
149
- VALUE p = environment_active_txn(transaction->env);
150
- while (!NIL_P(p) && p != self) {
151
- TRANSACTION(p, txn);
152
- p = txn->parent;
153
- }
154
- if (p != self)
155
- rb_raise(cError, "Transaction is not active");
156
-
157
- int ret = 0;
158
- if (commit)
159
- ret = mdb_txn_commit(transaction->txn);
160
- else
161
- mdb_txn_abort(transaction->txn);
162
-
163
- long i;
164
- for (i=0; i<RARRAY_LEN(transaction->cursors); i++) {
165
- VALUE cursor = RARRAY_AREF(transaction->cursors, i);
166
- cursor_close(cursor);
167
- }
168
- rb_ary_clear(transaction->cursors);
169
-
170
- // Mark child transactions as closed
171
- p = environment_active_txn(transaction->env);
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
- TRANSACTION(p, txn);
174
- txn->txn = 0;
175
- p = txn->parent;
190
+ TRANSACTION(p, txn);
191
+ txn->txn = 0;
192
+ p = txn->parent;
176
193
  }
177
- transaction->txn = 0;
194
+ }
195
+ transaction->txn = 0;
178
196
 
179
- environment_set_active_txn(transaction->env, transaction->thread, transaction->parent);
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
- check(ret);
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
- ENVIRONMENT(venv, environment);
288
+ ENVIRONMENT(venv, environment);
224
289
 
225
- MDB_txn* txn;
226
- TxnArgs txn_args;
290
+ MDB_txn* txn;
291
+ TxnArgs txn_args;
227
292
 
228
- retry:
229
- txn = NULL;
293
+ VALUE thread = rb_thread_current();
294
+ VALUE vparent = environment_active_txn(venv);
230
295
 
231
- txn_args.env = environment->env;
232
- txn_args.parent = active_txn(venv);
233
- txn_args.flags = flags;
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
- if (flags & MDB_RDONLY) {
239
- call_txn_begin(&txn_args);
240
- }
241
- else {
242
- CALL_WITHOUT_GVL(
243
- call_txn_begin, &txn_args,
244
- stop_txn_begin, &txn_args);
245
-
246
- if (txn_args.stop || !txn) {
247
- // !txn is when rb_thread_call_without_gvl2
248
- // returns before calling txn_begin
249
- if (txn) mdb_txn_abort(txn);
250
-
251
- //rb_warn("got here lol");
252
- rb_thread_check_ints();
253
- goto retry; // in what cases do we get here?
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
- check(txn_args.result);
258
-
259
- Transaction* transaction;
260
- VALUE vtxn = Data_Make_Struct(cTransaction, Transaction, transaction_mark, transaction_free, transaction);
261
- transaction->parent = environment_active_txn(venv);
262
- transaction->env = venv;
263
- transaction->txn = txn;
264
- transaction->flags = flags;
265
- transaction->thread = rb_thread_current();
266
- transaction->cursors = rb_ary_new();
267
- environment_set_active_txn(venv, transaction->thread, vtxn);
268
-
269
- int exception;
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
- transaction_commit(vtxn);
280
- return ret;
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
- VALUE path, option_hash;
604
+ VALUE path, option_hash;
500
605
 
501
606
  #ifdef RB_SCAN_ARGS_KEYWORDS
502
- rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
503
- argc, argv, "1:", &path, &option_hash);
607
+ rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
608
+ argc, argv, "1:", &path, &option_hash);
504
609
  #else
505
- rb_scan_args(argc, argv, "1:", &path, &option_hash);
610
+ rb_scan_args(argc, argv, "1:", &path, &option_hash);
506
611
  #endif
507
612
 
508
- EnvironmentOptions options = {
509
- .flags = MDB_NOTLS,
510
- .maxreaders = -1,
511
- .maxdbs = 128,
512
- .mapsize = 0,
513
- .mode = 0755,
514
- };
515
- if (!NIL_P(option_hash))
516
- rb_hash_foreach(option_hash, environment_options, (VALUE)&options);
517
-
518
- MDB_env* env;
519
- check(mdb_env_create(&env));
520
-
521
- Environment* environment;
522
- VALUE venv = Data_Make_Struct(cEnvironment, Environment, environment_mark, environment_free, environment);
523
- environment->env = env;
524
- environment->thread_txn_hash = rb_hash_new();
525
- environment->txn_thread_hash = rb_hash_new();
526
-
527
- if (options.maxreaders > 0)
528
- check(mdb_env_set_maxreaders(env, options.maxreaders));
529
- if (options.mapsize > 0)
530
- check(mdb_env_set_mapsize(env, options.mapsize));
531
-
532
- check(mdb_env_set_maxdbs(env, options.maxdbs <= 0 ? 1 : options.maxdbs));
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
- if (rb_block_given_p())
537
- return rb_ensure(rb_yield, venv, environment_close, venv);
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
- return venv;
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
- ENVIRONMENT(self, environment);
777
- if (!active_txn(self))
778
- return call_with_transaction(self, self, "database", argc, argv, 0);
884
+ ENVIRONMENT(self, environment);
885
+ if (!active_txn(self))
886
+ return call_with_transaction(self, self, "database", argc, argv, 0);
779
887
 
780
- VALUE name, option_hash;
888
+ VALUE name, option_hash;
781
889
  #ifdef RB_SCAN_ARGS_KEYWORDS
782
- rb_scan_args_kw(RB_SCAN_ARGS_KEYWORDS,
783
- argc, argv, "01:", &name, &option_hash);
890
+ rb_scan_args_kw(RB_SCAN_ARGS_KEYWORDS,
891
+ argc, argv, "01:", &name, &option_hash);
784
892
  #else
785
- rb_scan_args(argc, argv, "01:", &name, &option_hash);
893
+ rb_scan_args(argc, argv, "01:", &name, &option_hash);
786
894
  #endif
787
895
 
788
896
 
789
- int flags = 0;
790
- if (!NIL_P(option_hash))
791
- rb_hash_foreach(option_hash, database_flags, (VALUE)&flags);
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
- MDB_dbi dbi;
794
- check(mdb_dbi_open(need_txn(self), NIL_P(name) ? 0 : StringValueCStr(name), flags, &dbi));
902
+ MDB_dbi dbi;
903
+ check(mdb_dbi_open(need_txn(self), NIL_P(name) ? 0 : StringValueCStr(name),
904
+ flags, &dbi));
795
905
 
796
- Database* database;
797
- VALUE vdb = Data_Make_Struct(cDatabase, Database, database_mark, free, database);
798
- database->dbi = dbi;
799
- database->env = self;
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
- return vdb;
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
- DATABASE(self, database);
964
- if (!active_txn(database->env))
965
- return call_with_transaction(database->env, self, "put", argc, argv, 0);
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
- VALUE vkey, vval, option_hash = Qnil;
1078
+ VALUE vkey, vval, option_hash = Qnil;
968
1079
  #ifdef RB_SCAN_ARGS_KEYWORDS
969
- rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
970
- argc, argv, "20:", &vkey, &vval, &option_hash);
1080
+ rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
1081
+ argc, argv, "20:", &vkey, &vval, &option_hash);
971
1082
  #else
972
- rb_scan_args(argc, argv, "20:", &vkey, &vval, &option_hash);
1083
+ rb_scan_args(argc, argv, "20:", &vkey, &vval, &option_hash);
973
1084
  #endif
974
1085
 
975
- int flags = 0;
976
- if (!NIL_P(option_hash))
977
- rb_hash_foreach(option_hash, database_put_flags, (VALUE)&flags);
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
- vkey = StringValue(vkey);
980
- vval = StringValue(vval);
1091
+ vkey = StringValue(vkey);
1092
+ vval = StringValue(vval);
981
1093
 
982
- MDB_val key, value;
983
- key.mv_size = RSTRING_LEN(vkey);
984
- key.mv_data = RSTRING_PTR(vkey);
985
- value.mv_size = RSTRING_LEN(vval);
986
- value.mv_data = RSTRING_PTR(vval);
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
- check(mdb_put(need_txn(database->env), database->dbi, &key, &value, flags));
989
- return Qnil;
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
- CURSOR(self, cursor);
1465
+ CURSOR(self, cursor);
1354
1466
 
1355
- VALUE vkey, vval, option_hash;
1467
+ VALUE vkey, vval, option_hash;
1356
1468
  #ifdef RB_SCAN_ARGS_KEYWORDS
1357
- rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
1358
- argc, argv, "2:", &vkey, &vval, &option_hash);
1469
+ rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
1470
+ argc, argv, "2:", &vkey, &vval, &option_hash);
1359
1471
  #else
1360
- rb_scan_args(argc, argv, "2:", &vkey, &vval, &option_hash);
1472
+ rb_scan_args(argc, argv, "2:", &vkey, &vval, &option_hash);
1361
1473
  #endif
1362
1474
 
1363
- int flags = 0;
1364
- if (!NIL_P(option_hash))
1365
- rb_hash_foreach(option_hash, cursor_put_flags, (VALUE)&flags);
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
- vkey = StringValue(vkey);
1368
- vval = StringValue(vval);
1480
+ vkey = StringValue(vkey);
1481
+ vval = StringValue(vval);
1369
1482
 
1370
- MDB_val key, value;
1371
- key.mv_size = RSTRING_LEN(vkey);
1372
- key.mv_data = RSTRING_PTR(vkey);
1373
- value.mv_size = RSTRING_LEN(vval);
1374
- value.mv_data = RSTRING_PTR(vval);
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
- check(mdb_cursor_put(cursor->cur, &key, &value, flags));
1377
- return Qnil;
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
- CURSOR(self, cursor);
1508
+ CURSOR(self, cursor);
1396
1509
 
1397
- VALUE option_hash;
1510
+ VALUE option_hash;
1398
1511
  #ifdef RB_SCAN_ARGS_KEYWORDS
1399
- rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
1400
- argc, argv, ":", &option_hash);
1512
+ rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
1513
+ argc, argv, ":", &option_hash);
1401
1514
  #else
1402
- rb_scan_args(argc, argv, ":", &option_hash);
1515
+ rb_scan_args(argc, argv, ":", &option_hash);
1403
1516
  #endif
1404
1517
 
1405
- int flags = 0;
1406
- if (!NIL_P(option_hash))
1407
- rb_hash_foreach(option_hash, cursor_delete_flags, (VALUE)&flags);
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
- check(mdb_cursor_del(cursor->cur, flags));
1410
- return Qnil;
1523
+ check(mdb_cursor_del(cursor->cur, flags));
1524
+ return Qnil;
1411
1525
  }
1412
1526
 
1413
1527
  /**