fetch and delete may be passed destination buffer
[ruby-tdb.git] / ext / tdb / tdb.c
blob31f71c821b8cadc23eb6e31967377c1259d39129
1 #include "rbtdb.h"
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <errno.h>
6 #ifdef HAVE_RUBY_ST_H
7 # include <ruby/st.h>
8 #else
9 # include <st.h>
10 #endif
11 #include <pthread.h>
13 /* this protects the global list of tdb objects maintained by libtdb */
14 static pthread_mutex_t big_lock = PTHREAD_MUTEX_INITIALIZER;
15 static VALUE cTDB, cERR;
16 static st_table *exc_hash;
17 static VALUE hashes;
19 /* must be a macro to prevent GC from killing converted 'val's */
20 #define TO_TDB_DATA(data,val) do { \
21 StringValue(val); \
22 (data).dptr = (unsigned char *)RSTRING_PTR(val); \
23 (data).dsize = RSTRING_LEN(val); \
24 } while (0)
26 static void init_exc(enum TDB_ERROR ecode, const char *name)
28 VALUE exc = rb_define_class_under(cERR, name, cERR);
29 st_insert(exc_hash, (st_data_t)ecode, (st_data_t)exc);
32 static void init_errors(void)
34 cERR = rb_define_class_under(cTDB, "ERR", rb_eStandardError);
35 exc_hash = st_init_numtable();
37 init_exc(TDB_ERR_CORRUPT, "CORRUPT");
38 init_exc(TDB_ERR_IO, "IO");
39 init_exc(TDB_ERR_LOCK, "LOCK");
40 init_exc(TDB_ERR_OOM, "OOM");
41 init_exc(TDB_ERR_EXISTS, "EXISTS"),
42 init_exc(TDB_ERR_NOLOCK, "NOLOCK");
43 init_exc(TDB_ERR_LOCK_TIMEOUT, "LOCK_TIMEOUT");
44 init_exc(TDB_ERR_EINVAL, "EINVAL");
45 init_exc(TDB_ERR_NOEXIST, "NOEXIST");
46 init_exc(TDB_ERR_RDONLY, "RDONLY");
47 #ifdef HAVE_CONST_TDB_ERR_NESTING
48 init_exc(TDB_ERR_NESTING, "NESTING");
49 #endif /* HAVE_CONST_TDB_ERR_NESTING */
52 static void my_raise(struct tdb_context *tdb)
54 enum TDB_ERROR ecode = tdb_error(tdb);
55 const char *str = tdb_errorstr(tdb);
56 VALUE exc;
58 switch (ecode) {
59 case TDB_SUCCESS:
60 rb_bug("attempted to raise with no error");
61 case TDB_ERR_CORRUPT:
62 case TDB_ERR_IO:
63 case TDB_ERR_LOCK:
64 case TDB_ERR_OOM:
65 case TDB_ERR_EXISTS:
66 case TDB_ERR_NOLOCK:
67 case TDB_ERR_LOCK_TIMEOUT:
68 case TDB_ERR_EINVAL:
69 case TDB_ERR_NOEXIST:
70 case TDB_ERR_RDONLY:
71 #ifdef HAVE_CONST_TDB_ERR_NESTING
72 case TDB_ERR_NESTING:
73 #endif /* HAVE_CONST_TDB_ERR_NESTING */
74 if (!st_lookup(exc_hash, (st_data_t)ecode, (st_data_t *)&exc))
75 rb_bug("no-existent exception: %s\n", str);
77 rb_raise(exc, str);
80 static void init_hashes(void)
82 #define HF(x) \
83 rb_hash_aset(hashes,ID2SYM(rb_intern(#x)),ULONG2NUM((unsigned long)rbtdb_##x))
84 HF(murmur1);
85 HF(murmur1_aligned);
86 HF(murmur2);
87 HF(murmur2a);
88 HF(murmur2_neutral);
89 HF(murmur2_aligned);
90 HF(fnv1a);
91 HF(djb2);
92 HF(djb3);
93 HF(jenkins_lookup3);
94 HF(default);
97 #ifndef HAVE_RB_THREAD_BLOCKING_REGION
98 /* (very) partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
99 # include <rubysig.h>
100 typedef VALUE rb_blocking_function_t(void *);
101 static VALUE my_tbr(rb_blocking_function_t *fn, void *data)
103 VALUE rv;
105 TRAP_BEG;
106 rv = fn(data);
107 TRAP_END;
109 return rv;
111 #else
112 static VALUE my_tbr(rb_blocking_function_t *fn, void *data)
114 return rb_thread_blocking_region(fn, data, RUBY_UBF_IO, 0);
116 #endif /* HAVE_RUBY_THREAD_BLOCKING_REGION */
118 static void gcfree(void *ptr)
120 struct tdb_context *tdb = ptr;
122 /* no error checking in GC :< */
123 if (tdb) {
124 (void)pthread_mutex_lock(&big_lock);
125 (void)tdb_close(tdb);
126 (void)pthread_mutex_unlock(&big_lock);
130 static VALUE alloc(VALUE klass)
132 return Data_Wrap_Struct(klass, NULL, gcfree, NULL);
135 static struct tdb_context *db(VALUE self, int check_opened)
137 struct tdb_context *tdb;
139 Data_Get_Struct(self, struct tdb_context, tdb);
141 if (!tdb && check_opened)
142 rb_raise(rb_eIOError, "closed database");
144 return tdb;
147 struct open_args {
148 const char *name;
149 int hash_size;
150 int tdb_flags;
151 int open_flags;
152 mode_t mode;
153 struct tdb_logging_context *log_ctx;
154 tdb_hash_func hash_fn;
157 static VALUE nogvl_open(void *ptr)
159 struct open_args *o = ptr;
160 struct tdb_context *tdb;
162 pthread_mutex_lock(&big_lock);
163 tdb = tdb_open_ex(o->name, o->hash_size, o->tdb_flags,
164 o->open_flags, o->mode, o->log_ctx, o->hash_fn);
165 pthread_mutex_unlock(&big_lock);
167 return (VALUE)tdb;
170 static void set_args(VALUE self, struct open_args *o, VALUE opts)
172 VALUE tmp;
174 o->name = NULL;
175 o->hash_size = 0; /* default */
176 o->tdb_flags = TDB_DEFAULT;
177 o->open_flags = O_RDWR | O_CREAT;
178 o->mode = 0666;
179 o->log_ctx = NULL;
180 o->hash_fn = NULL;
182 if (NIL_P(opts))
183 return;
184 Check_Type(opts, T_HASH);
186 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("hash_size")));
187 if (!NIL_P(tmp))
188 o->hash_size = NUM2INT(tmp);
190 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("mode")));
191 if (!NIL_P(tmp))
192 o->mode = NUM2UINT(tmp);
194 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("open_flags")));
195 if (!NIL_P(tmp))
196 o->open_flags = NUM2INT(tmp);
198 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("tdb_flags")));
199 if (!NIL_P(tmp))
200 o->tdb_flags = NUM2INT(tmp);
202 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("hash")));
203 if (!NIL_P(tmp)) {
204 VALUE num = rb_hash_aref(hashes, tmp);
206 if (NIL_P(num)) {
207 tmp = rb_inspect(tmp);
208 rb_raise(rb_eArgError,
209 "`%s' is not a valid hash function",
210 StringValuePtr(tmp));
213 o->hash_fn = (tdb_hash_func)NUM2ULONG(num);
216 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("threadsafe")));
217 if (RTEST(tmp))
218 rb_funcall(self, rb_intern("threadsafe!"), 0);
222 * :call-seq:
224 * TDB.new("/path/to/file") -> TDB
225 * TDB.new("/path/to/file", :hash_size => 666) -> TDB
226 * TDB.new("/path/to/file", :hash => :murmur2) -> TDB
227 * TDB.new("/path/to/file", :open_flags => IO::RDONLY) -> TDB
228 * TDB.new("/path/to/file", :tdb_flags => TDB::NOSYNC) -> TDB
230 * Initializes a TDB context. It takes several options.
232 * :hash_size - the number of buckets, this is the most important tuning
233 * parameter when creating large databases. This parameter only affects
234 * the creation of new databases.
236 * :open_flags - a bit mask of IO flags passed directly to open(2),
237 * File.open-compatible flags are accepted.
239 * :hash - any of the hashes described in Hash_Functions.
240 * This must remain the same for all clients.
242 * :tdb_flags - a bitmask of any combination of TDB::CLEAR_IF_FIRST,
243 * TDB::INTERNAL, TDB::NOLOCK, TDB::NOMMAP, TDB::CONVERT,
244 * TDB::BIGENDIAN, TDB::NOSYNC, TDB::SEQNUM, TDB::VOLATILE,
245 * TDB::ALLOW_NESTING, TDB::DISALLOW_NESTING, TDB::INCOMPATIBLE_HASH
247 * :mode - octal mode mask passed to open(2)
249 static VALUE init(int argc, VALUE *argv, VALUE self)
251 struct tdb_context *tdb = db(self, 0);
252 VALUE path, opts;
253 struct open_args o;
255 if (tdb)
256 rb_raise(rb_eRuntimeError, "TDB already initialized");
257 rb_scan_args(argc, argv, "11", &path, &opts);
258 set_args(self, &o, opts);
260 if (NIL_P(path))
261 o.tdb_flags |= TDB_INTERNAL;
262 else
263 o.name = StringValuePtr(path);
265 tdb = (struct tdb_context *)my_tbr(nogvl_open, &o);
266 if (!tdb) {
267 switch (errno) {
268 case ENOMEM:
269 case EMFILE:
270 case ENFILE:
271 rb_gc();
272 tdb = (struct tdb_context *)my_tbr(nogvl_open, &o);
274 if (!tdb)
275 rb_sys_fail("tdb_open_ex");
277 DATA_PTR(self) = tdb;
279 return self;
282 /* tdb_close can do a lot, including cancel transactions an munmap */
283 static VALUE nogvl_close(void *ptr)
285 struct tdb_context *tdb = ptr;
286 VALUE rv;
288 pthread_mutex_lock(&big_lock);
289 rv = (VALUE)tdb_close(tdb);
290 pthread_mutex_unlock(&big_lock);
292 return rv;
295 static VALUE tdbclose(VALUE self)
297 struct tdb_context *tdb = db(self, 1);
299 DATA_PTR(self) = NULL;
301 if ((int)my_tbr(nogvl_close, tdb) == -1)
302 rb_sys_fail("tdb_close");
304 return Qnil;
307 static VALUE closed(VALUE self)
309 struct tdb_context *tdb = db(self, 0);
311 return tdb ? Qfalse : Qtrue;
314 #ifdef HAVE_RB_THREAD_CALL_WITH_GVL
315 /* missing prototype in ruby.h: */
316 void *rb_thread_call_with_gvl(void *(*func)(void *), void *data);
317 #else
318 static void * my_rb_thread_call_with_gvl(void *(*func)(void *), void *data)
320 return (*func)(data);
322 #define rb_thread_call_with_gvl my_rb_thread_call_with_gvl
323 #endif /* !HAVE_RB_THREAD_CALL_WITH_GVL */
326 * We avoid the extra malloc/free pair enforced by tdb_fetch. We
327 * use tdb_parse_record to give us pointers to (hopefully) mmap-ed
328 * regions and create a String object directly off that region.
330 struct fetch_parse_args {
331 struct tdb_context *tdb;
332 union {
333 TDB_DATA key;
334 long value_len;
335 char *value_ptr;
336 VALUE value;
337 } as;
338 VALUE value;
341 static VALUE str_new_tdb_data(TDB_DATA *val)
343 return rb_str_new((const char *)val->dptr, val->dsize);
346 static void *gvl_str_resize(void *data)
348 struct fetch_parse_args *f = data;
350 rb_str_resize(f->value, f->as.value_len);
351 f->as.value_ptr = RSTRING_PTR(f->value);
353 return NULL;
356 static int fetch_parse(TDB_DATA key, TDB_DATA val, void *data)
358 struct fetch_parse_args *f = data;
360 f->as.value_len = val.dsize;
361 (void)rb_thread_call_with_gvl(gvl_str_resize, data);
362 memcpy(f->as.value_ptr, val.dptr, val.dsize);
363 f->as.value = f->value;
365 return 0;
368 static VALUE nogvl_parse_record(void *ptr)
370 struct fetch_parse_args *f = ptr;
372 if (tdb_parse_record(f->tdb, f->as.key, fetch_parse, ptr) == -1)
373 return Qnil;
375 return f->value == f->as.value ? f->value : Qnil;
378 static VALUE fetch(int argc, VALUE *argv, VALUE self)
380 struct fetch_parse_args f;
381 VALUE key;
383 rb_scan_args(argc, argv, "11", &key, &f.value);
384 if (NIL_P(f.value)) {
385 f.value = rb_str_new(0, 0);
386 } else {
387 StringValue(f.value);
388 rb_str_set_len(f.value, 0);
391 f.tdb = db(self, 1);
392 TO_TDB_DATA(f.as.key, key);
394 return my_tbr(nogvl_parse_record, &f);
397 struct store_args {
398 struct tdb_context *tdb;
399 TDB_DATA key;
400 TDB_DATA val;
401 int flag;
404 static VALUE nogvl_store(void *ptr)
406 struct store_args *s = ptr;
408 return (VALUE)tdb_store(s->tdb, s->key, s->val, s->flag);
411 static VALUE rbtdb_store(VALUE self, VALUE key, VALUE val, int flag, int soft)
413 struct store_args s;
415 s.tdb = db(self, 1);
416 TO_TDB_DATA(s.key, key);
417 TO_TDB_DATA(s.val, val);
418 s.flag = flag;
420 if ((int)my_tbr(nogvl_store, &s) == -1) {
421 if (soft) {
422 int ecode = tdb_error(s.tdb);
424 if ((flag == TDB_INSERT) && (ecode == TDB_ERR_EXISTS))
425 return Qnil;
426 if ((flag == TDB_MODIFY) && (ecode == TDB_ERR_NOEXIST))
427 return Qnil;
429 my_raise(s.tdb);
432 return val;
435 static VALUE store(VALUE self, VALUE key, VALUE val)
437 return rbtdb_store(self, key, val, 0, 0);
440 static VALUE insert_bang(VALUE self, VALUE key, VALUE val)
442 return rbtdb_store(self, key, val, TDB_INSERT, 0);
445 static VALUE insert(VALUE self, VALUE key, VALUE val)
447 return rbtdb_store(self, key, val, TDB_INSERT, 1);
450 static VALUE modify_bang(VALUE self, VALUE key, VALUE val)
452 return rbtdb_store(self, key, val, TDB_MODIFY, 0);
455 static VALUE modify(VALUE self, VALUE key, VALUE val)
457 return rbtdb_store(self, key, val, TDB_MODIFY, 1);
460 struct exists_args {
461 struct tdb_context *tdb;
462 TDB_DATA key;
465 static VALUE nogvl_exists(void *ptr)
467 struct exists_args *e = ptr;
469 return tdb_exists(e->tdb, e->key) == 0 ? Qfalse : Qtrue;
472 static VALUE has_key(VALUE self, VALUE key)
474 struct exists_args e;
476 e.tdb = db(self, 1);
477 TO_TDB_DATA(e.key, key);
479 return my_tbr(nogvl_exists, &e);
482 struct traverse_args {
483 struct tdb_context *tdb;
484 TDB_DATA key;
485 TDB_DATA val;
486 int state;
489 static VALUE protected_yield(VALUE val)
491 VALUE *kv = (VALUE *)val;
493 return rb_yield_values(2, kv[0], kv[1]);
496 static void *my_yield(void *data)
498 struct traverse_args *t = data;
499 VALUE kv[2];
501 kv[0] = str_new_tdb_data(&t->key);
502 kv[1] = str_new_tdb_data(&t->val);
504 rb_protect(protected_yield, (VALUE)kv, &t->state);
506 return NULL;
509 static int
510 traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA val, void *data)
512 struct traverse_args *t = data;
514 t->key = key;
515 t->val = val;
516 (void)rb_thread_call_with_gvl(my_yield, t);
518 return t->state;
521 static VALUE nogvl_traverse(void *ptr)
523 struct traverse_args *t = ptr;
525 (void)tdb_traverse(t->tdb, traverse_fn, t);
527 return Qfalse;
530 static VALUE each(VALUE self)
532 struct traverse_args t;
534 t.tdb = db(self, 1);
535 t.state = 0;
537 my_tbr(nogvl_traverse, &t);
538 if (t.state)
539 rb_jump_tag(t.state);
540 return self;
543 struct delete_args {
544 struct tdb_context *tdb;
545 TDB_DATA key;
548 static VALUE nogvl_delete(void *ptr)
550 struct delete_args *d = ptr;
552 return tdb_delete(d->tdb, d->key) == 0 ? Qtrue : Qfalse;
555 static VALUE nuke(VALUE self, VALUE key)
557 struct delete_args d;
559 d.tdb = db(self, 1);
560 TO_TDB_DATA(d.key, key);
562 return my_tbr(nogvl_delete, &d);
565 static VALUE aref(VALUE self, VALUE key)
567 return fetch(1, &key, self);
570 static VALUE delete(int argc, VALUE *argv, VALUE self)
572 VALUE rc = fetch(argc, argv, self);
574 if (! NIL_P(rc))
575 if (nuke(self, argv[0]) == Qfalse)
576 return Qnil;
577 return rc;
580 static VALUE lockall(VALUE self)
582 struct tdb_context *tdb = db(self, 1);
583 if ((int)my_tbr((rb_blocking_function_t *)tdb_lockall, tdb))
584 my_raise(tdb);
586 return Qtrue;
589 static VALUE trylockall(VALUE self)
591 struct tdb_context *tdb = db(self, 1);
592 void *fn = tdb_lockall_nonblock;
594 if ((int)my_tbr((rb_blocking_function_t *)fn, tdb)) {
595 if (tdb_error(tdb) == TDB_ERR_LOCK)
596 return Qfalse;
597 my_raise(tdb);
599 return Qtrue;
602 static VALUE unlockall(VALUE self)
604 struct tdb_context *tdb = db(self, 1);
605 if ((int)my_tbr((rb_blocking_function_t *)tdb_unlockall, tdb))
606 my_raise(tdb);
607 return Qtrue;
610 static VALUE lockall_read(VALUE self)
612 struct tdb_context *tdb = db(self, 1);
613 if ((int)my_tbr((rb_blocking_function_t *)tdb_lockall_read, tdb))
614 my_raise(tdb);
615 return Qtrue;
618 static VALUE trylockall_read(VALUE self)
620 struct tdb_context *tdb = db(self, 1);
621 void *fn = tdb_lockall_read_nonblock;
622 if ((int)my_tbr((rb_blocking_function_t *)fn, tdb)) {
623 if (tdb_error(tdb) == TDB_ERR_LOCK)
624 return Qfalse;
625 my_raise(tdb);
627 return Qtrue;
630 static VALUE unlockall_read(VALUE self)
632 struct tdb_context *tdb = db(self, 1);
633 if ((int)my_tbr((rb_blocking_function_t *)tdb_unlockall_read, tdb))
634 my_raise(tdb);
635 return Qtrue;
638 static VALUE lockall_mark(VALUE self)
640 struct tdb_context *tdb = db(self, 1);
641 if ((int)my_tbr((rb_blocking_function_t *)tdb_lockall_mark, tdb))
642 my_raise(tdb);
643 return Qtrue;
646 static VALUE lockall_unmark(VALUE self)
648 struct tdb_context *tdb = db(self, 1);
649 if ((int)my_tbr((rb_blocking_function_t *)tdb_lockall_unmark, tdb))
650 my_raise(tdb);
651 return Qtrue;
655 * clears out the database
657 static VALUE clear(VALUE self)
659 struct tdb_context *tdb = db(self, 1);
660 if ((int)my_tbr((rb_blocking_function_t *)tdb_wipe_all, tdb))
661 my_raise(tdb);
662 return self;
665 #ifdef HAVE_TDB_REPACK
666 /* repacks a database to reduce fragmentation, available with tdb 1.2.x+ */
667 static VALUE repack(VALUE self)
669 struct tdb_context *tdb = db(self, 1);
670 if ((int)my_tbr((rb_blocking_function_t *)tdb_repack, tdb))
671 my_raise(tdb);
672 return self;
674 #endif /* HAVE_TDB_REPACK */
676 void Init_tdb_ext(void)
678 cTDB = rb_define_class("TDB", rb_cObject);
680 hashes = rb_hash_new();
683 * Available hash functions, the key is the name of the hash
684 * and the value is a pointer for internal for usage.
686 rb_define_const(cTDB, "HASHES", hashes);
688 rb_define_alloc_func(cTDB, alloc);
689 rb_include_module(cTDB, rb_mEnumerable);
691 rb_define_method(cTDB, "initialize", init, -1);
692 rb_define_method(cTDB, "close", tdbclose, 0);
693 rb_define_method(cTDB, "closed?", closed, 0);
695 rb_define_method(cTDB, "fetch", fetch, -1);
696 rb_define_method(cTDB, "[]", aref, 1);
697 rb_define_method(cTDB, "store", store, 2);
698 rb_define_method(cTDB, "[]=", store, 2);
699 rb_define_method(cTDB, "insert!", insert_bang, 2);
700 rb_define_method(cTDB, "modify!", modify_bang, 2);
701 rb_define_method(cTDB, "insert", insert, 2);
702 rb_define_method(cTDB, "modify", modify, 2);
704 rb_define_method(cTDB, "key?", has_key, 1);
705 rb_define_method(cTDB, "has_key?", has_key, 1);
706 rb_define_method(cTDB, "include?", has_key, 1);
707 rb_define_method(cTDB, "member?", has_key, 1);
708 rb_define_method(cTDB, "each", each, 0);
709 rb_define_method(cTDB, "nuke!", nuke, 1);
710 rb_define_method(cTDB, "delete", delete, -1);
712 rb_define_method(cTDB, "lockall", lockall, 0);
713 rb_define_method(cTDB, "trylockall", trylockall, 0);
714 rb_define_method(cTDB, "unlockall", unlockall, 0);
715 rb_define_method(cTDB, "lockall_read", lockall_read, 0);
716 rb_define_method(cTDB, "trylockall_read", trylockall_read, 0);
717 rb_define_method(cTDB, "unlockall_read", unlockall_read, 0);
718 rb_define_method(cTDB, "lockall_mark", lockall_mark, 0);
719 rb_define_method(cTDB, "lockall_unmark", lockall_unmark, 0);
720 rb_define_method(cTDB, "clear", clear, 0);
721 #ifdef HAVE_TDB_REPACK
722 rb_define_method(cTDB, "repack", repack, 0);
723 #endif /* HAVE_TDB_REPACK */
725 init_errors();
726 init_hashes();
728 /* just a readability place holder */
729 rb_define_const(cTDB, "DEFAULT", UINT2NUM(TDB_DEFAULT));
731 /* clear database if we are the only one with it open */
732 rb_define_const(cTDB, "CLEAR_IF_FIRST", UINT2NUM(TDB_CLEAR_IF_FIRST));
734 /* don't store on disk, use in-memory database */
735 rb_define_const(cTDB, "INTERNAL", UINT2NUM(TDB_INTERNAL));
737 /* don't do any locking */
738 rb_define_const(cTDB, "NOLOCK", UINT2NUM(TDB_NOLOCK));
740 /* don't use mmap */
741 rb_define_const(cTDB, "NOMMAP", UINT2NUM(TDB_NOMMAP));
743 /* convert endian (internal use) */
744 rb_define_const(cTDB, "CONVERT", UINT2NUM(TDB_CONVERT));
746 /* header is big-endian (internal use) */
747 rb_define_const(cTDB, "BIGENDIAN", UINT2NUM(TDB_BIGENDIAN));
749 /* don't use synchronous transactions */
750 rb_define_const(cTDB, "NOSYNC", UINT2NUM(TDB_NOSYNC));
752 /* maintain a sequence number */
753 rb_define_const(cTDB, "SEQNUM", UINT2NUM(TDB_SEQNUM));
755 /* Activate the per-hashchain freelist, default 5 */
756 rb_define_const(cTDB, "VOLATILE", UINT2NUM(TDB_VOLATILE));
758 #ifdef TDB_ALLOW_NESTING
759 /* Allow transactions to nest */
760 rb_define_const(cTDB, "ALLOW_NESTING", UINT2NUM(TDB_ALLOW_NESTING));
761 #endif
763 #ifdef TDB_DISALLOW_NESTING
764 /* Disallow transactions to nest */
765 rb_define_const(cTDB, "DISALLOW_NESTING", UINT2NUM(TDB_DISALLOW_NESTING));
766 #endif
768 #ifdef TDB_INCOMPATIBLE_HASH
769 /* Better hashing, but can't be opened by tdb < 1.2.6. */
770 rb_define_const(cTDB, "INCOMPATIBLE_HASH", UINT2NUM(TDB_INCOMPATIBLE_HASH));
771 #endif
772 rbtdb_init_tdb_hash_functions();
776 * Document-class: TDB
778 * <code>
779 * tdb = TDB.new("/path/to/file", flags => IO::RDWR|IO::CREAT)
780 * tdb.store("HELLO", "world")
781 * tdb.fetch("HELLO") -> "world"
782 * tdb.delete("HELLO") -> "world"
783 * </code>