#include "bdb.h" static void bdb_cursor_free(bdb_DBC *dbcst) { bdb_DB *dbst; if (dbcst->dbc && BDB_VALID(dbcst->db, T_DATA)) { Data_Get_Struct(dbcst->db, bdb_DB, dbst); if (dbst->dbp) { dbcst->dbc->c_close(dbcst->dbc); } dbcst->dbc = NULL; dbcst->db = 0; } free(dbcst); } static VALUE bdb_cursor(int argc, VALUE *argv, VALUE obj) { bdb_DB *dbst; DB_TXN *txnid; bdb_DBC *dbcst; DBC *dbc; VALUE a; int flags; INIT_TXN(txnid, obj, dbst); flags = 0; if (argc && TYPE(argv[argc - 1]) == T_HASH) { VALUE g, f = argv[argc - 1]; if ((g = rb_hash_aref(f, rb_intern("flags"))) != RHASH(f)->ifnone || (g = rb_hash_aref(f, rb_str_new2("flags"))) != RHASH(f)->ifnone) { flags = NUM2INT(g); } argc--; } if (argc) { flags = NUM2INT(argv[0]); } #if BDB_VERSION < 20600 bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbc)); #else bdb_test_error(dbst->dbp->cursor(dbst->dbp, txnid, &dbc, flags)); #endif a = Data_Make_Struct(bdb_cCursor, bdb_DBC, 0, bdb_cursor_free, dbcst); dbcst->dbc = dbc; dbcst->db = obj; return a; } static VALUE bdb_write_cursor(VALUE obj) { VALUE f; #if BDB_VERSION >= 30000 f = INT2NUM(DB_WRITECURSOR); #else f = INT2NUM(DB_RMW); #endif return bdb_cursor(1, &f, obj); } static VALUE bdb_cursor_close(VALUE obj) { bdb_DBC *dbcst; bdb_DB *dbst; if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4) rb_raise(rb_eSecurityError, "Insecure: can't close the cursor"); GetCursorDB(obj, dbcst, dbst); bdb_test_error(dbcst->dbc->c_close(dbcst->dbc)); dbcst->dbc = NULL; return Qtrue; } static VALUE bdb_cursor_del(VALUE obj) { int flags = 0; bdb_DBC *dbcst; bdb_DB *dbst; rb_secure(4); GetCursorDB(obj, dbcst, dbst); bdb_test_error(dbcst->dbc->c_del(dbcst->dbc, flags)); return Qtrue; } #if BDB_VERSION >= 30000 static VALUE bdb_cursor_dup(int argc, VALUE *argv, VALUE obj) { int flags = 0; VALUE a, b; bdb_DBC *dbcst, *dbcstdup; bdb_DB *dbst; DBC *dbcdup; if (rb_scan_args(argc, argv, "01", &a)) flags = NUM2INT(a); GetCursorDB(obj, dbcst, dbst); bdb_test_error(dbcst->dbc->c_dup(dbcst->dbc, &dbcdup, flags)); b = Data_Make_Struct(bdb_cCursor, bdb_DBC, 0, bdb_cursor_free, dbcstdup); dbcstdup->dbc = dbcdup; dbcstdup->db = dbcst->db; return b; } #endif static VALUE bdb_cursor_count(VALUE obj) { #if BDB_VERSION < 20600 rb_raise(bdb_eFatal, "DB_NEXT_DUP needs Berkeley DB 2.6 or later"); #else #if BDB_VERSION < 30100 DBT key, data; DBT key_o, data_o; int ret; #endif bdb_DBC *dbcst; bdb_DB *dbst; db_recno_t count; GetCursorDB(obj, dbcst, dbst); #if BDB_VERSION >= 30100 bdb_test_error(dbcst->dbc->c_count(dbcst->dbc, &count, 0)); return INT2NUM(count); #else MEMZERO(&key, DBT, 1); key.flags |= DB_DBT_MALLOC; MEMZERO(&data, DBT, 1); data.flags |= DB_DBT_MALLOC; MEMZERO(&key_o, DBT, 1); key_o.flags |= DB_DBT_MALLOC; MEMZERO(&data_o, DBT, 1); data_o.flags |= DB_DBT_MALLOC; SET_PARTIAL(dbst, data); ret = bdb_test_error(dbcst->dbc->c_get(dbcst->dbc, &key_o, &data_o, DB_CURRENT)); if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY) return INT2NUM(0); count = 1; while (1) { ret = bdb_test_error(dbcst->dbc->c_get(dbcst->dbc, &key, &data, DB_NEXT_DUP)); if (ret == DB_NOTFOUND) { bdb_test_error(dbcst->dbc->c_get(dbcst->dbc, &key_o, &data_o, DB_SET)); FREE_KEY(dbst, key_o); free(data_o.data); return INT2NUM(count); } if (ret == DB_KEYEMPTY) continue; FREE_KEY(dbst, key); free(data.data); count++; } return INT2NUM(-1); #endif #endif } static VALUE bdb_cursor_get_common(int argc, VALUE *argv, VALUE obj, int c_pget) { volatile VALUE a = Qnil; VALUE b = Qnil; VALUE c; int flags, cnt, ret; DBT key, data, pkey; bdb_DBC *dbcst; bdb_DB *dbst; db_recno_t recno; cnt = rb_scan_args(argc, argv, "12", &a, &b, &c); flags = NUM2INT(a); MEMZERO(&key, DBT, 1); MEMZERO(&pkey, DBT, 1); pkey.flags |= DB_DBT_MALLOC; MEMZERO(&data, DBT, 1); GetCursorDB(obj, dbcst, dbst); if (flags == DB_SET_RECNO) { if (dbst->type != DB_BTREE || !(dbst->flags & DB_RECNUM)) { rb_raise(bdb_eFatal, "database must be Btree with RECNUM for SET_RECNO"); } if (cnt != 2) rb_raise(bdb_eFatal, "invalid number of arguments"); recno = NUM2INT(b); key.data = &recno; key.size = sizeof(db_recno_t); key.flags |= DB_DBT_MALLOC; data.flags |= DB_DBT_MALLOC; } else if (flags == DB_SET || flags == DB_SET_RANGE) { if (cnt != 2) rb_raise(bdb_eFatal, "invalid number of arguments"); b = bdb_test_recno(dbcst->db, &key, &recno, b); data.flags |= DB_DBT_MALLOC; } #if BDB_VERSION >= 20600 else if (flags == DB_GET_BOTH) { if (cnt != 3) rb_raise(bdb_eFatal, "invalid number of arguments"); b = bdb_test_recno(dbcst->db, &key, &recno, b); a = bdb_test_dump(dbcst->db, &data, c, FILTER_VALUE); } #endif else { if (cnt != 1) { rb_raise(bdb_eFatal, "invalid number of arguments"); } key.flags |= DB_DBT_MALLOC; data.flags |= DB_DBT_MALLOC; } SET_PARTIAL(dbst, data); #if BDB_VERSION >= 30300 if (c_pget) { if (dbst->secondary != Qnil) { rb_raise(bdb_eFatal, "pget must be used with a secondary index"); } ret = bdb_test_error(dbcst->dbc->c_pget(dbcst->dbc, &key, &pkey, &data, flags)); } else #endif { ret = bdb_test_error(dbcst->dbc->c_get(dbcst->dbc, &key, &data, flags)); } if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY) return Qnil; if (c_pget) { return bdb_assoc3(dbcst->db, &key, &pkey, &data); } else { return bdb_assoc_dyna(dbcst->db, &key, &data); } } static VALUE bdb_cursor_get(int argc, VALUE *argv, VALUE obj) { return bdb_cursor_get_common(argc, argv, obj, 0); } #if BDB_VERSION >= 30300 static VALUE bdb_cursor_pget(int argc, VALUE *argv, VALUE obj) { return bdb_cursor_get_common(argc, argv, obj, 1); } #endif static VALUE bdb_cursor_set_xxx(VALUE obj, VALUE a, int flag) { VALUE *b; b = ALLOCA_N(VALUE, 2); b[0] = INT2NUM(flag); b[1] = a; return bdb_cursor_get(2, b, obj); } static VALUE bdb_cursor_set(VALUE obj, VALUE a) { return bdb_cursor_set_xxx(obj, a, DB_SET); } static VALUE bdb_cursor_set_range(VALUE obj, VALUE a) { return bdb_cursor_set_xxx(obj, a, DB_SET_RANGE); } static VALUE bdb_cursor_set_recno(VALUE obj, VALUE a) { return bdb_cursor_set_xxx(obj, a, DB_SET_RECNO); } static VALUE bdb_cursor_xxx(VALUE obj, int val) { VALUE b; b = INT2NUM(val); return bdb_cursor_get(1, &b, obj); } static VALUE bdb_cursor_next(VALUE obj) { return bdb_cursor_xxx(obj, DB_NEXT); } #if BDB_VERSION >= 20600 static VALUE bdb_cursor_next_dup(VALUE obj) { return bdb_cursor_xxx(obj, DB_NEXT_DUP); } #endif static VALUE bdb_cursor_prev(VALUE obj) { return bdb_cursor_xxx(obj, DB_PREV); } static VALUE bdb_cursor_first(VALUE obj) { return bdb_cursor_xxx(obj, DB_FIRST); } static VALUE bdb_cursor_last(VALUE obj) { return bdb_cursor_xxx(obj, DB_LAST); } static VALUE bdb_cursor_current(VALUE obj) { return bdb_cursor_xxx(obj, DB_CURRENT); } static VALUE bdb_cursor_put(int argc, VALUE *argv, VALUE obj) { int flags, cnt; DBT key, data; bdb_DBC *dbcst; bdb_DB *dbst; VALUE a, b, c, f; volatile VALUE d = Qnil; volatile VALUE e = Qnil; db_recno_t recno; int ret; rb_secure(4); MEMZERO(&key, DBT, 1); MEMZERO(&data, DBT, 1); cnt = rb_scan_args(argc, argv, "21", &a, &b, &c); GetCursorDB(obj, dbcst, dbst); flags = NUM2INT(a); if (flags == DB_KEYFIRST || flags == DB_KEYLAST) { if (cnt != 3) rb_raise(bdb_eFatal, "invalid number of arguments"); d = bdb_test_recno(dbcst->db, &key, &recno, b); e = bdb_test_dump(dbcst->db, &data, c, FILTER_VALUE); f = c; } else { e = bdb_test_dump(dbcst->db, &data, b, FILTER_VALUE); f = b; } SET_PARTIAL(dbst, data); ret = bdb_test_error(dbcst->dbc->c_put(dbcst->dbc, &key, &data, flags)); if (cnt == 3) { FREE_KEY(dbst, key); } if (data.flags & DB_DBT_MALLOC) free(data.data); if (ret == DB_KEYEXIST) { return Qfalse; } else { if (dbst->partial) { return bdb_cursor_current(obj); } else { return bdb_test_ret(obj, e, f, FILTER_VALUE); } } } void bdb_init_cursor() { rb_define_method(bdb_cCommon, "db_cursor", bdb_cursor, -1); rb_define_method(bdb_cCommon, "cursor", bdb_cursor, -1); rb_define_method(bdb_cCommon, "db_write_cursor", bdb_write_cursor, 0); rb_define_method(bdb_cCommon, "write_cursor", bdb_write_cursor, 0); bdb_cCursor = rb_define_class_under(bdb_mDb, "Cursor", rb_cObject); #ifdef HAVE_RB_DEFINE_ALLOC_FUNC rb_undef_alloc_func(bdb_cCursor); #else rb_undef_method(CLASS_OF(bdb_cCursor), "allocate"); #endif rb_undef_method(CLASS_OF(bdb_cCursor), "new"); rb_define_method(bdb_cCursor, "close", bdb_cursor_close, 0); rb_define_method(bdb_cCursor, "c_close", bdb_cursor_close, 0); rb_define_method(bdb_cCursor, "c_del", bdb_cursor_del, 0); rb_define_method(bdb_cCursor, "del", bdb_cursor_del, 0); rb_define_method(bdb_cCursor, "delete", bdb_cursor_del, 0); #if BDB_VERSION >= 30000 rb_define_method(bdb_cCursor, "dup", bdb_cursor_dup, -1); rb_define_method(bdb_cCursor, "c_dup", bdb_cursor_dup, -1); rb_define_method(bdb_cCursor, "clone", bdb_cursor_dup, -1); rb_define_method(bdb_cCursor, "c_clone", bdb_cursor_dup, -1); #endif rb_define_method(bdb_cCursor, "count", bdb_cursor_count, 0); rb_define_method(bdb_cCursor, "c_count", bdb_cursor_count, 0); rb_define_method(bdb_cCursor, "get", bdb_cursor_get, -1); rb_define_method(bdb_cCursor, "c_get", bdb_cursor_get, -1); #if BDB_VERSION >= 30300 rb_define_method(bdb_cCursor, "pget", bdb_cursor_pget, -1); rb_define_method(bdb_cCursor, "c_pget", bdb_cursor_pget, -1); #endif rb_define_method(bdb_cCursor, "put", bdb_cursor_put, -1); rb_define_method(bdb_cCursor, "c_put", bdb_cursor_put, -1); rb_define_method(bdb_cCursor, "c_next", bdb_cursor_next, 0); rb_define_method(bdb_cCursor, "next", bdb_cursor_next, 0); #if BDB_VERSION >= 20600 rb_define_method(bdb_cCursor, "c_next_dup", bdb_cursor_next_dup, 0); rb_define_method(bdb_cCursor, "next_dup", bdb_cursor_next_dup, 0); #endif rb_define_method(bdb_cCursor, "c_first", bdb_cursor_first, 0); rb_define_method(bdb_cCursor, "first", bdb_cursor_first, 0); rb_define_method(bdb_cCursor, "c_last", bdb_cursor_last, 0); rb_define_method(bdb_cCursor, "last", bdb_cursor_last, 0); rb_define_method(bdb_cCursor, "c_current", bdb_cursor_current, 0); rb_define_method(bdb_cCursor, "current", bdb_cursor_current, 0); rb_define_method(bdb_cCursor, "c_prev", bdb_cursor_prev, 0); rb_define_method(bdb_cCursor, "prev", bdb_cursor_prev, 0); rb_define_method(bdb_cCursor, "c_set", bdb_cursor_set, 1); rb_define_method(bdb_cCursor, "set", bdb_cursor_set, 1); rb_define_method(bdb_cCursor, "c_set_range", bdb_cursor_set_range, 1); rb_define_method(bdb_cCursor, "set_range", bdb_cursor_set_range, 1); rb_define_method(bdb_cCursor, "c_set_recno", bdb_cursor_set_recno, 1); rb_define_method(bdb_cCursor, "set_recno", bdb_cursor_set_recno, 1); }