From c9bc7ae17f981cca5ee90651653e8981066dab17 Mon Sep 17 00:00:00 2001 From: danj Date: Fri, 21 Apr 2006 20:45:54 +0000 Subject: [PATCH] added transactions, cleanup of finalizers, addition of pget, in process of adding many other API calls --- bdb.c | 781 +++++++++++++++++++++++++++++++++++++++++++++++++++------- bdb.h | 36 ++- 2 files changed, 719 insertions(+), 98 deletions(-) diff --git a/bdb.c b/bdb.c index 2b33e7b..c894ea7 100644 --- a/bdb.c +++ b/bdb.c @@ -2,6 +2,7 @@ #include #define LMEMFLAG 0 +#define NOFLAGS 0 #ifdef HAVE_STDARG_PROTOTYPES #include @@ -16,9 +17,11 @@ VALUE cDb; /* DBT class */ VALUE cEnv; /* Environment class */ VALUE cTxn; /* Transaction class */ VALUE cCursor; /* Cursors */ +VALUE cTxnStat; /* Transaction Status class */ +VALUE cTxnStatActive; /* Active Transaction Status class */ VALUE eDbError; -static ID fv_txn, fv_call, fv_err_new,fv_err_code,fv_err_msg; +static ID fv_call, fv_err_new,fv_err_code,fv_err_msg; static void #ifdef HAVE_STDARG_PROTOTYPES @@ -58,31 +61,35 @@ VALUE err_code(VALUE obj) return rb_ivar_get(obj,fv_err_code); } -static void db_free(void *p) +static void db_free(t_dbh *dbh) { - t_dbh *dbh; - dbh=(t_dbh *)p; #ifdef DEBUG_DB if ( RTEST(ruby_debug) ) fprintf(stderr,"%s/%d %s 0x%x\n",__FILE__,__LINE__,"db_free cleanup!",p); #endif if ( dbh ) { - if ( dbh->dbp ) { - dbh->dbp->close(dbh->dbp,0); + if ( dbh->db ) { + dbh->db->close(dbh->db,NOFLAGS); } - if ( dbh->aproc ) { - rb_gc_unregister_address(&(dbh->aproc)); - } - free(p); + free(dbh); } } -static void db_mark(void *p) +static void db_mark(t_dbh *dbh) { - t_dbh *dbh; if ( dbh->aproc ) rb_gc_mark(dbh->aproc); + if ( dbh->env ) + rb_gc_mark(dbh->env->self); + if ( ! NIL_P(dbh->adbc) ) + rb_gc_mark(dbh->adbc); +} + +static void dbc_mark(t_dbch *dbch) +{ + if (dbch->db) + rb_gc_mark(dbch->db->self); } static void dbc_free(void *p) { @@ -109,35 +116,38 @@ static void dbc_free(void *p) VALUE db_alloc(VALUE klass) { - return Data_Wrap_Struct(klass,0,db_free,0); + return Data_Wrap_Struct(klass,db_mark,db_free,0); } -VALUE db_init_aux(VALUE obj,DB_ENV *env) +VALUE db_init_aux(VALUE obj,t_envh * eh) { DB *db; t_dbh *dbh; int rv; - rv = db_create(&db,env,0); + /* This excludes possible use of X/Open Transaction Mgr */ + rv = db_create(&db,(eh)?eh->env:NULL,NOFLAGS); if (rv != 0) { raise_error(rv, "db_new failure: %s",db_strerror(rv)); } - /* - if ( env == NULL ) - dbh->dbp->set_alloc(dbh->dbp,(void *(*)(size_t))xmalloc, - (void *(*)(void *,size_t))xrealloc, - xfree); - */ #ifdef DEBUG_DB - dbh->dbp->set_errfile(dbh->dbp,stderr); + dbh->db->set_errfile(dbh->db,stderr); #endif - rb_ivar_set(obj,fv_txn,Qnil); + dbh=ALLOC(t_dbh); db_free(DATA_PTR(obj)); DATA_PTR(obj)=dbh; - dbh->dbp=db; - dbh->dbinst=obj; + dbh->db=db; + dbh->self=obj; + dbh->env=eh; + + if (eh) { + rb_ary_push(eh->adb,obj); + } + + dbh->adbc=rb_ary_new(); + return obj; } @@ -146,25 +156,23 @@ VALUE db_initialize(VALUE obj) return db_init_aux(obj,NULL); } -VALUE db_open(VALUE obj, VALUE vdisk_file, VALUE vlogical_db, +VALUE db_open(VALUE obj, VALUE vtxn, VALUE vdisk_file, VALUE vlogical_db, VALUE vdbtype, VALUE vflags, VALUE vmode) { t_dbh *dbh; int rv; - VALUE vtxn; - DB_TXN *txn=NULL; + t_txnh *txn=NOTXN; u_int32_t flags=0; DBTYPE dbtype=DB_UNKNOWN; char *logical_db=NULL; long len; int mode=0; - vtxn=rb_ivar_get(obj,fv_txn); if ( ! NIL_P(vflags) ) flags=NUM2INT(vflags); if ( ! NIL_P(vtxn) ) - txn=NOTXN; /* XXX when implemented */ + Data_Get_Struct(vtxn,t_txnh,txn); if ( TYPE(vlogical_db)==T_STRING && RSTRING(vlogical_db)->len > 0 ) logical_db=StringValueCStr(vlogical_db); @@ -186,10 +194,14 @@ VALUE db_open(VALUE obj, VALUE vdisk_file, VALUE vlogical_db, mode=NUM2INT(vmode); Data_Get_Struct(obj,t_dbh,dbh); - rv = dbh->dbp->open(dbh->dbp,txn,StringValueCStr(vdisk_file),logical_db, - dbtype,flags,mode); + if ( NIL_P(dbh->adbc) ) + raise_error(0,"db handle already used and closed"); + + rv = dbh->db->open(dbh->db,txn?txn->txn:NULL,StringValueCStr(vdisk_file), + logical_db, + dbtype,flags,mode); if (rv != 0) { - raise_error(rv,"db_open failure: %s",db_strerror(rv)); + raise_error(rv,"db_open failure: %s(%d)",db_strerror(rv),rv); } filename_copy(dbh->filename,vdisk_file) return obj; @@ -203,44 +215,66 @@ VALUE db_flags_set(VALUE obj, VALUE vflags) flags=NUM2INT(vflags); Data_Get_Struct(obj,t_dbh,dbh); - rv = dbh->dbp->set_flags(dbh->dbp,flags); + rv = dbh->db->set_flags(dbh->db,flags); if ( rv != 0 ) { raise_error(rv, "db_flag_set failure: %s",db_strerror(rv)); } return vflags; } +VALUE dbc_close(VALUE); + VALUE db_close(VALUE obj, VALUE vflags) { t_dbh *dbh; int rv; u_int32_t flags; + VALUE cur; flags=NUM2INT(vflags); Data_Get_Struct(obj,t_dbh,dbh); + if ( dbh->db==NULL ) + return Qnil; + + if (RARRAY(dbh->adbc)->len > 0 ) { + rb_warning("%s/%d %s",__FILE__,__LINE__, + "cursor handles still open"); + while ( (cur=rb_ary_pop(dbh->adbc)) != Qnil ) { + dbc_close(cur); + } + } + if ( RTEST(ruby_debug) ) rb_warning("%s/%d %s 0x%x %s",__FILE__,__LINE__,"db_close!",dbh, dbh->filename); - rv = dbh->dbp->close(dbh->dbp,flags); + rv = dbh->db->close(dbh->db,flags); if ( rv != 0 ) { raise_error(rv, "db_close failure: %s",db_strerror(rv)); } - dbh->dbp=NULL; + dbh->db=NULL; dbh->aproc=(VALUE)NULL; + if ( dbh->env ) { + rb_ary_delete(dbh->env->adb,obj); + } + dbh->adbc=Qnil; return obj; } -VALUE db_put(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags) +VALUE db_put(VALUE obj, VALUE vtxn, VALUE vkey, VALUE vdata, VALUE vflags) { t_dbh *dbh; int rv; u_int32_t flags=0; DBT key,data; + t_txnh *txn=NULL; memset(&key,0,sizeof(DBT)); memset(&data,0,sizeof(DBT)); + if ( ! NIL_P(vtxn) ) + Data_Get_Struct(vtxn,t_txnh,txn); + if ( ! NIL_P(vflags) ) flags=NUM2INT(vflags); @@ -256,7 +290,7 @@ VALUE db_put(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags) data.size = RSTRING(vdata)->len; data.flags = LMEMFLAG; - rv = dbh->dbp->put(dbh->dbp,NULL,&key,&data,flags); + rv = dbh->db->put(dbh->db,txn?txn->txn:NULL,&key,&data,flags); /* if (rv == DB_KEYEXIST) return Qnil; @@ -267,17 +301,21 @@ VALUE db_put(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags) return obj; } -VALUE db_get(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags) +VALUE db_get(VALUE obj, VALUE vtxn, VALUE vkey, VALUE vdata, VALUE vflags) { t_dbh *dbh; int rv; u_int32_t flags=0; DBT key,data; VALUE str; + t_txnh *txn=NULL; memset(&key,0,sizeof(DBT)); memset(&data,0,sizeof(DBT)); + if ( ! NIL_P(vtxn) ) + Data_Get_Struct(vtxn,t_txnh,txn); + if ( ! NIL_P(vflags) ) { rb_warning("flags nil"); flags=NUM2INT(vflags); @@ -298,7 +336,7 @@ VALUE db_get(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags) data.flags = LMEMFLAG; } - rv = dbh->dbp->get(dbh->dbp,NULL,&key,&data,flags); + rv = dbh->db->get(dbh->db,txn?txn->txn:NULL,&key,&data,flags); if ( rv == 0 ) { return rb_str_new(data.data,data.size); } else if (rv == DB_NOTFOUND) { @@ -309,13 +347,64 @@ VALUE db_get(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags) return Qnil; } +VALUE db_pget(VALUE obj, VALUE vtxn, VALUE vkey, VALUE vdata, VALUE vflags) +{ + t_dbh *dbh; + int rv; + u_int32_t flags=0; + DBT key,data,pkey; + VALUE str; + t_txnh *txn=NULL; + + memset(&key,0,sizeof(DBT)); + memset(&data,0,sizeof(DBT)); + memset(&pkey,0,sizeof(DBT)); + + if ( ! NIL_P(vtxn) ) + Data_Get_Struct(vtxn,t_txnh,txn); + + if ( ! NIL_P(vflags) ) { + rb_warning("flags nil"); + flags=NUM2INT(vflags); + } + + Data_Get_Struct(obj,t_dbh,dbh); + + StringValue(vkey); + + key.data = RSTRING(vkey)->ptr; + key.size = RSTRING(vkey)->len; + key.flags = LMEMFLAG; + + if ( ! NIL_P(vdata) ) { + StringValue(vdata); + data.data = RSTRING(vdata)->ptr; + data.size = RSTRING(vdata)->len; + data.flags = LMEMFLAG; + } + + rv = dbh->db->pget(dbh->db,txn?txn->txn:NULL,&key,&pkey,&data,flags); + if ( rv == 0 ) { + return + rb_ary_new3(2, + rb_str_new(pkey.data,pkey.size), + rb_str_new(data.data,data.size)); + + } else if (rv == DB_NOTFOUND) { + return Qnil; + } else { + raise_error(rv, "db_pget failure: %s",db_strerror(rv)); + } + return Qnil; +} + VALUE db_aget(VALUE obj, VALUE vkey) { - return db_get(obj,vkey,Qnil,Qnil); + return db_get(obj,Qnil,vkey,Qnil,Qnil); } VALUE db_aset(VALUE obj, VALUE vkey, VALUE vdata) { - return db_put(obj,vkey,vdata,Qnil); + return db_put(obj,Qnil,vkey,vdata,Qnil); } VALUE db_join(VALUE obj, VALUE vacurs, VALUE vflags) { @@ -335,26 +424,103 @@ VALUE db_join(VALUE obj, VALUE vacurs, VALUE vflags) curs[i]=dbch->dbc; } curs[i]=NULL; - jcurs=Data_Make_Struct(cCursor,t_dbch,0,dbc_free,dbch); - rv = dbh->dbp->join(dbh->dbp,curs,&(dbch->dbc),flags); + jcurs=Data_Make_Struct(cCursor,t_dbch,dbc_mark,dbc_free,dbch); + rv = dbh->db->join(dbh->db,curs,&(dbch->dbc),flags); if (rv) { raise_error(rv, "db_join: %s",db_strerror(rv)); } - + dbch->db=dbh; + rb_ary_push(dbch->db->adbc,jcurs); rb_obj_call_init(jcurs,0,NULL); return jcurs; } -VALUE db_del(VALUE obj, VALUE vkey, VALUE vflags) +#if DB_VERSION_MINOR > 3 +VALUE db_compact(VALUE obj, VALUE vtxn, VALUE vstart_key, + VALUE vstop_key, VALUE db_compact, + VALUE vflags) +{ + t_dbh *dbh; + int flags; + t_txnh *txn=NULL; + DBT start_key, stop_key, end_key; + int rv; + + flags=NUM2INT(vflags); + Data_Get_Struct(obj,t_dbh,dbh); + + memset(&start_key,0,sizeof(DBT)); + memset(&stop_key,0,sizeof(DBT)); + memset(&end_key,0,sizeof(DBT)); + + if ( ! NIL_P(vstart_key) ) { + StringValue(vstart_key); + start_key.data=RSTRING(vstart_key)->ptr; + start_key.size=RSTRING(vstart_key)->len; + start_key.flags= LMEMFLAG; + } + if ( ! NIL_P(vstop_key) ) { + StringValue(vstop_key); + stop_key.data=RSTRING(vstop_key)->ptr; + stop_key.size=RSTRING(vstop_key)->len; + stop_key.flags= LMEMFLAG; + } + if ( ! NIL_P(vtxn) ) + Data_Get_Struct(vtxn,t_txnh,txn); + + rv=dbh->db->compact(dbh->db,txn?txn->txn:NULL, + &start_key, + &stop_key, + NULL, + flags, + &end_key); + if (rv) + raise_error(rv,"db_compact failure: %s",db_strerror(rv)); + + return rb_str_new(end_key.data,end_key.size); + +} +#endif +VALUE db_get_byteswapped(VALUE obj) +{ + t_dbh *dbh; + int rv; + int is_swapped; + + Data_Get_Struct(obj,t_dbh,dbh); + rv=dbh->db->get_byteswapped(dbh->db,&is_swapped); + if (rv) + raise_error(rv,"db_get_byteswapped failed: %s",db_strerror(rv)); + return INT2FIX(is_swapped); +} + +VALUE db_get_type(VALUE obj) +{ + t_dbh *dbh; + int rv; + DBTYPE dbtype; + + Data_Get_Struct(obj,t_dbh,dbh); + rv=dbh->db->get_type(dbh->db,&dbtype); + if (rv) + raise_error(rv,"db_get_type failed: %s",db_strerror(rv)); + return INT2FIX(dbtype); +} + +VALUE db_del(VALUE obj, VALUE vtxn, VALUE vkey, VALUE vflags) { t_dbh *dbh; int rv; u_int32_t flags; DBT key; VALUE str; + t_txnh *txn=NULL; memset(&key,0,sizeof(DBT)); + if ( ! NIL_P(vtxn) ) + Data_Get_Struct(vtxn,t_txnh,txn); + flags=NUM2INT(vflags); Data_Get_Struct(obj,t_dbh,dbh); @@ -363,7 +529,7 @@ VALUE db_del(VALUE obj, VALUE vkey, VALUE vflags) key.size = RSTRING(vkey)->len; key.flags = LMEMFLAG; - rv = dbh->dbp->del(dbh->dbp,NOTXN,&key,flags); + rv = dbh->db->del(dbh->db,txn?txn->txn:NULL,&key,flags); if ( rv == DB_NOTFOUND ) { return Qnil; } else if (rv != 0) { @@ -392,7 +558,7 @@ int assoc_callback(DB *secdb,const DBT* key, const DBT* data, DBT* result) dbh=dbassoc[fdp]; args[0]=dbh->aproc; - args[1]=dbh->dbinst; + args[1]=dbh->self; args[2]=rb_str_new(key->data,key->size); args[3]=rb_str_new(data->data,data->size); @@ -412,26 +578,32 @@ int assoc_callback(DB *secdb,const DBT* key, const DBT* data, DBT* result) return 0; } -VALUE db_associate(VALUE obj, VALUE osecdb, VALUE vflags, VALUE cb_proc) +VALUE db_associate(VALUE obj, VALUE vtxn, VALUE osecdb, + VALUE vflags, VALUE cb_proc) { t_dbh *sdbh,*pdbh; int rv; u_int32_t flags,flagsp,flagss; int fdp; + t_txnh *txn=NOTXN; flags=NUM2INT(vflags); Data_Get_Struct(obj,t_dbh,pdbh); Data_Get_Struct(osecdb,t_dbh,sdbh); + if ( ! NIL_P(vtxn) ) + Data_Get_Struct(vtxn,t_txnh,txn); + if ( cb_proc == Qnil ) { rb_warning("db_associate: no association may be applied"); - pdbh->dbp->get_open_flags(pdbh->dbp,&flagsp); - sdbh->dbp->get_open_flags(sdbh->dbp,&flagss); + pdbh->db->get_open_flags(pdbh->db,&flagsp); + sdbh->db->get_open_flags(sdbh->db,&flagss); if ( flagsp & DB_RDONLY & flagss ) { - rv=pdbh->dbp->associate(pdbh->dbp,NOTXN,sdbh->dbp,NULL,flags); + rv=pdbh->db->associate(pdbh->db,txn?txn->txn:NULL, + sdbh->db,NULL,flags); if (rv) raise_error(rv,"db_associate: %s",db_strerror(rv)); - + return Qtrue; } else { raise_error(0,"db_associate empty associate only available when both DBs opened with DB_RDONLY"); } @@ -439,19 +611,14 @@ VALUE db_associate(VALUE obj, VALUE osecdb, VALUE vflags, VALUE cb_proc) raise_error(0, "db_associate proc required"); } - sdbh->dbp->fd(sdbh->dbp,&fdp); + sdbh->db->fd(sdbh->db,&fdp); sdbh->aproc=cb_proc; - /* - * I do not think this is necessary since GC knows about sdbh and its - * size, so it will find this address, I think. - */ -#ifdef DEBUG_DB - fprintf(stderr,"registering 0x%x 0x%x\n",&(sdbh->aproc),sdbh->aproc); -#endif - rb_gc_register_address(&(sdbh->aproc)); + /* No register is needed since this is just a way to + * get back to a real object + */ dbassoc[fdp]=sdbh; - rv=pdbh->dbp->associate(pdbh->dbp,NOTXN,sdbh->dbp,assoc_callback,flags); + rv=pdbh->db->associate(pdbh->db,txn?txn->txn:NULL,sdbh->db,assoc_callback,flags); #ifdef DEBUG_DB fprintf(stderr,"file is %d\n",fdp); fprintf(stderr,"assoc done 0x%x 0x%x\n",sdbh,dbassoc[fdp]); @@ -462,27 +629,31 @@ VALUE db_associate(VALUE obj, VALUE osecdb, VALUE vflags, VALUE cb_proc) return Qtrue; } - - -VALUE db_cursor(VALUE obj, VALUE vflags) +VALUE db_cursor(VALUE obj, VALUE vtxn, VALUE vflags) { t_dbh *dbh; int rv; u_int32_t flags; DBC *dbc; + t_txnh *txn=NOTXN; VALUE c_obj; t_dbch *dbch; flags=NUM2INT(vflags); Data_Get_Struct(obj,t_dbh,dbh); - c_obj=Data_Make_Struct(cCursor, t_dbch, 0, dbc_free, dbch); + c_obj=Data_Make_Struct(cCursor, t_dbch, dbc_mark, dbc_free, dbch); - rv=dbh->dbp->cursor(dbh->dbp,NOTXN,&(dbch->dbc),flags); + if ( ! NIL_P(vtxn) ) + Data_Get_Struct(vtxn,t_txnh,txn); + + rv=dbh->db->cursor(dbh->db,txn?txn->txn:NULL,&(dbch->dbc),flags); if (rv) raise_error(rv,"db_cursor: %s",db_strerror(rv)); filename_dup(dbch->filename,dbh->filename); + dbch->db=dbh; + rb_ary_push(dbch->db->adbc,c_obj); rb_obj_call_init(c_obj,0,NULL); return c_obj; } @@ -497,6 +668,8 @@ VALUE dbc_close(VALUE obj) if (rv) raise_error(rv,"dbc_close: %s",db_strerror(rv)); + rb_ary_delete(dbch->db->adbc,obj); + dbch->db=NULL; dbch->dbc=NULL; } return Qnil; @@ -541,6 +714,49 @@ VALUE dbc_get(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags) } return Qnil; } +VALUE dbc_pget(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags) +{ + t_dbch *dbch; + u_int32_t flags; + DBT key,data,pkey; + VALUE rar; + int rv; + + flags=NUM2INT(vflags); + Data_Get_Struct(obj,t_dbch,dbch); + + memset(&key,0,sizeof(DBT)); + memset(&data,0,sizeof(DBT)); + memset(&pkey,0,sizeof(DBT)); + + if ( ! NIL_P(vkey) ) { + StringValue(vkey); + key.data = RSTRING(vkey)->ptr; + key.size = RSTRING(vkey)->len; + key.flags = LMEMFLAG; + } + if ( ! NIL_P(vdata) ) { + StringValue(vdata); + data.data = RSTRING(vdata)->ptr; + data.size = RSTRING(vdata)->len; + data.flags = LMEMFLAG; + } + + rv = dbch->dbc->c_pget(dbch->dbc,&key,&pkey,&data,flags); + if ( rv == 0 ) { + rar = rb_ary_new3(3, + rb_str_new(key.data,key.size), + rb_str_new(pkey.data,pkey.size), + rb_str_new(data.data,data.size)); + return rar; + } else if (rv == DB_NOTFOUND) { + return Qnil; + } else { + raise_error(rv, "dbc_pget %s",db_strerror(rv)); + } + return Qnil; +} + VALUE dbc_put(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags) { t_dbch *dbch; @@ -582,7 +798,7 @@ VALUE dbc_del(VALUE obj) int rv; Data_Get_Struct(obj,t_dbch,dbch); - rv = dbch->dbc->c_del(dbch->dbc,0); + rv = dbch->dbc->c_del(dbch->dbc,NOFLAGS); if (rv == DB_KEYEMPTY) return Qnil; else if (rv != 0) { @@ -598,7 +814,7 @@ VALUE dbc_count(VALUE obj) db_recno_t count; Data_Get_Struct(obj,t_dbch,dbch); - rv = dbch->dbc->c_count(dbch->dbc,&count,0); + rv = dbch->dbc->c_count(dbch->dbc,&count,NOFLAGS); if (rv != 0) raise_error(rv, "db_count failure: %s",db_strerror(rv)); @@ -617,29 +833,35 @@ static void env_free(void *p) if ( eh ) { if ( eh->env ) { - eh->env->close(eh->env,0); + eh->env->close(eh->env,NOFLAGS); } free(p); } } -VALUE env_new(VALUE class) +static void env_mark(t_envh *eh) +{ + rb_gc_mark(eh->adb); + rb_gc_mark(eh->atxn); +} +VALUE env_new(VALUE class, VALUE vflags) { t_envh *eh; int rv; + u_int32_t flags=0; VALUE obj; - obj=Data_Make_Struct(class,t_envh,NULL,env_free,eh); - rv=db_env_create(&(eh->env),0); + if ( ! NIL_P(vflags) ) + flags=NUM2INT(vflags); + + obj=Data_Make_Struct(class,t_envh,env_mark,env_free,eh); + rv=db_env_create(&(eh->env),flags); if ( rv != 0 ) { raise_error(rv,"env_new: %s",db_strerror(rv)); return Qnil; } - /* - eh->env->set_alloc(eh->env,(void *(*)(size_t))xmalloc, - (void *(*)(void *,size_t))xrealloc, - xfree); - */ - rb_ivar_set(obj,fv_txn,Qnil); + eh->self=obj; + eh->adb = rb_ary_new(); + eh->atxn = rb_ary_new(); rb_obj_call_init(obj,0,NULL); return obj; } @@ -656,6 +878,9 @@ VALUE env_open(VALUE obj, VALUE vhome, VALUE vflags, VALUE vmode) if ( ! NIL_P(vmode) ) mode=NUM2INT(vmode); Data_Get_Struct(obj,t_envh,eh); + if ( NIL_P(eh->adb) ) + raise_error(0,"env handle already used and closed"); + rv = eh->env->open(eh->env,StringValueCStr(vhome),flags,mode); if (rv != 0) { raise_error(rv, "env_open failure: %s",db_strerror(rv)); @@ -663,22 +888,44 @@ VALUE env_open(VALUE obj, VALUE vhome, VALUE vflags, VALUE vmode) return obj; } +VALUE txn_abort(VALUE); + VALUE env_close(VALUE obj) { t_envh *eh; + VALUE db; int rv; Data_Get_Struct(obj,t_envh,eh); + if ( eh->env==NULL ) + return Qnil; + + if (RARRAY(eh->adb)->len > 0) { + rb_warning("%s/%d %s",__FILE__,__LINE__, + "database handles still open"); + while ( (db=rb_ary_pop(eh->adb)) != Qnil ) { + db_close(db,INT2FIX(0)); + } + } + if (RARRAY(eh->atxn)->len > 0) { + rb_warning("%s/%d %s",__FILE__,__LINE__, + "database transactions still open"); + while ( (db=rb_ary_pop(eh->atxn)) != Qnil ) { + txn_abort(db); + } + } if ( RTEST(ruby_debug) ) rb_warning("%s/%d %s 0x%x",__FILE__,__LINE__,"env_close!",eh); - rv = eh->env->close(eh->env,0); + rv = eh->env->close(eh->env,NOFLAGS); if ( rv != 0 ) { raise_error(rv, "env_close failure: %s",db_strerror(rv)); return Qnil; } eh->env=NULL; + eh->adb=Qnil; + eh->atxn=Qnil; return obj; } @@ -688,9 +935,9 @@ VALUE env_db(VALUE obj) VALUE dbo; Data_Get_Struct(obj,t_envh,eh); - dbo = Data_Wrap_Struct(cDb,0,db_free,0); + dbo = Data_Wrap_Struct(cDb,db_mark,db_free,0); - return db_init_aux(dbo,eh->env); + return db_init_aux(dbo,eh); } VALUE env_set_cachesize(VALUE obj, VALUE size) @@ -750,11 +997,334 @@ VALUE env_set_flags_off(VALUE obj, VALUE vflags) { return env_set_flags(obj,vflags,0); } +VALUE env_list_dbs(VALUE obj) +{ + t_envh *eh; + Data_Get_Struct(obj,t_envh,eh); + return eh->adb; +} +static void txn_mark(t_txnh *txn) +{ + if (txn->env) + rb_gc_mark(txn->env->self); +} +static void txn_free(t_txnh *txn) +{ + if ( RTEST(ruby_debug) ) + fprintf(stderr,"%s/%d %s 0x%x\n",__FILE__,__LINE__,"txn_free",txn); -simple_set(txn); + if (txn && txn->txn) { + if (txn->txn) + txn->txn->abort(txn->txn); + txn->txn=NULL; + rb_ary_delete(txn->env->atxn,txn->self); + txn->env=NULL; + } +} + +VALUE env_txn_begin(VALUE obj, VALUE vtxn_parent, VALUE vflags) +{ + t_txnh *parent=NULL, *txn=NULL; + u_int32_t flags=0; + int rv; + t_envh *eh; + VALUE t_obj; + + if ( ! NIL_P(vflags)) + flags=NUM2INT(vflags); + if ( ! NIL_P(vtxn_parent) ) + Data_Get_Struct(vtxn_parent,t_txnh,parent); + + Data_Get_Struct(obj,t_envh,eh); + t_obj=Data_Make_Struct(cTxn,t_txnh,txn_mark,txn_free,txn); + + rv=eh->env->txn_begin(eh->env,parent?parent->txn:NULL, + &(txn->txn),flags); + + if ( rv != 0 ) { + raise_error(rv, "env_txn_begin: %s",db_strerror(rv)); + return Qnil; + } + txn->env=eh; + txn->self=t_obj; + rb_ary_push(eh->atxn,t_obj); + + /* Once we get this working, we'll have to track transactions */ + rb_obj_call_init(t_obj,0,NULL); + return t_obj; +} + +VALUE env_txn_checkpoint(VALUE obj, VALUE vkbyte, VALUE vmin, + VALUE vflags) +{ + u_int32_t flags=0; + int rv; + t_envh *eh; + u_int32_t kbyte=0, min=0; + + if ( ! NIL_P(vflags)) + flags=NUM2INT(vflags); + + if ( FIXNUM_P(vkbyte) ) + kbyte=FIX2UINT(vkbyte); + + if ( FIXNUM_P(vmin) ) + min=FIX2UINT(vmin); + + Data_Get_Struct(obj,t_envh,eh); + rv=eh->env->txn_checkpoint(eh->env,kbyte,min,flags); + if ( rv != 0 ) { + raise_error(rv, "env_txn_checkpoint: %s",db_strerror(rv)); + return Qnil; + } + return Qtrue; +} + +VALUE env_txn_stat_active(DB_TXN_ACTIVE *t) +{ + VALUE ao; + + ao=rb_class_new_instance(0,NULL,cTxnStatActive); + + rb_iv_set(ao,"@txnid",INT2FIX(t->txnid)); + rb_iv_set(ao,"@parentid",INT2FIX(t->parentid)); + /* rb_iv_set(ao,"@thread_id",INT2FIX(t->thread_id)); */ + rb_iv_set(ao,"@lsn",rb_ary_new3(2, + INT2FIX(t->lsn.file), + INT2FIX(t->lsn.offset))); + /* XA status is currently excluded */ + return ao; +} + +VALUE env_txn_stat(VALUE obj, VALUE vflags) +{ + u_int32_t flags=0; + int rv; + t_envh *eh; + DB_TXN_STAT *statp; + VALUE s_obj; + VALUE active; + int i; + + if ( ! NIL_P(vflags)) + flags=NUM2INT(vflags); + + Data_Get_Struct(obj,t_envh,eh); + + /* statp will need free() */ + rv=eh->env->txn_stat(eh->env,&statp,flags); + if ( rv != 0 ) { + raise_error(rv, "txn_stat: %s",db_strerror(rv)); + } + + s_obj=rb_class_new_instance(0,NULL,cTxnStat); + rb_iv_set(s_obj,"@st_last_ckp", + rb_ary_new3(2, + INT2FIX(statp->st_last_ckp.file), + INT2FIX(statp->st_last_ckp.offset)) ); + rb_iv_set(s_obj,"@st_time_ckp", + rb_time_new(statp->st_time_ckp,0)); + rb_iv_set(s_obj,"@st_last_txnid", + INT2FIX(statp->st_last_txnid)); + rb_iv_set(s_obj,"@st_maxtxns", + INT2FIX(statp->st_maxtxns)); + rb_iv_set(s_obj,"@st_nactive", + INT2FIX(statp->st_nactive)); + rb_iv_set(s_obj,"@st_maxnactive", + INT2FIX(statp->st_maxnactive)); + rb_iv_set(s_obj,"@st_nbegins", + INT2FIX(statp->st_nbegins)); + rb_iv_set(s_obj,"@st_naborts", + INT2FIX(statp->st_naborts)); + rb_iv_set(s_obj,"@st_ncommits", + INT2FIX(statp->st_ncommits)); + rb_iv_set(s_obj,"@st_nrestores", + INT2FIX(statp->st_nrestores)); + rb_iv_set(s_obj,"@st_regsize", + INT2FIX(statp->st_regsize)); + rb_iv_set(s_obj,"@st_region_wait", + INT2FIX(statp->st_region_wait)); + rb_iv_set(s_obj,"@st_region_nowait", + INT2FIX(statp->st_region_nowait)); + rb_iv_set(s_obj,"@st_txnarray", + active=rb_ary_new2(statp->st_nactive)); + + for (i=0; ist_nactive; i++) { + rb_ary_push(active,env_txn_stat_active(&(statp->st_txnarray[i]))); + } + + free(statp); + return s_obj; +} + +VALUE env_set_timeout(VALUE obj, VALUE vtimeout, VALUE vflags) +{ + t_envh *eh; + u_int32_t flags=0; + db_timeout_t timeout; + int rv; + + if ( ! NIL_P(vflags)) + flags=NUM2INT(vflags); + timeout=FIX2UINT(vtimeout); + + Data_Get_Struct(obj,t_envh,eh); + rv=eh->env->set_timeout(eh->env,timeout,flags); + if ( rv != 0 ) { + raise_error(rv, "env_set_timeout: %s",db_strerror(rv)); + } + + return vtimeout; +} + +VALUE env_get_timeout(VALUE obj, VALUE vflags) +{ + t_envh *eh; + u_int32_t flags=0; + db_timeout_t timeout; + int rv; + + if ( ! NIL_P(vflags)) + flags=NUM2INT(vflags); + + Data_Get_Struct(obj,t_envh,eh); + rv=eh->env->get_timeout(eh->env,&timeout,flags); + if ( rv != 0 ) { + raise_error(rv, "env_get_timeout: %s",db_strerror(rv)); + } + + return INT2FIX(timeout); +} + +VALUE env_set_tx_max(VALUE obj, VALUE vmax) +{ + t_envh *eh; + u_int32_t max; + int rv; + + max=FIX2UINT(vmax); + + Data_Get_Struct(obj,t_envh,eh); + rv=eh->env->set_tx_max(eh->env,max); + if ( rv != 0 ) { + raise_error(rv, "env_set_tx_max: %s",db_strerror(rv)); + } + + return vmax; +} + +VALUE env_get_tx_max(VALUE obj) +{ + t_envh *eh; + u_int32_t max; + int rv; + + Data_Get_Struct(obj,t_envh,eh); + rv=eh->env->get_tx_max(eh->env,&max); + if ( rv != 0 ) { + raise_error(rv, "env_get_tx_max: %s",db_strerror(rv)); + } + + return INT2FIX(max); +} + + +static void txn_finish(t_txnh *txn) +{ + if ( RTEST(ruby_debug) ) + rb_warning("%s/%d %s 0x%x",__FILE__,__LINE__,"txn_finish",txn); + + rb_ary_delete(txn->env->atxn,txn->self); + txn->txn=NULL; + txn->env=NULL; +} + +VALUE txn_commit(VALUE obj, VALUE vflags) +{ + t_txnh *txn=NULL; + u_int32_t flags=0; + int rv; + + if ( ! NIL_P(vflags)) + flags=NUM2INT(vflags); + + Data_Get_Struct(obj,t_txnh,txn); + rv=txn->txn->commit(txn->txn,flags); + txn_finish(txn); + if ( rv != 0 ) { + raise_error(rv, "txn_commit: %s",db_strerror(rv)); + return Qnil; + } + return Qtrue; +} + +VALUE txn_abort(VALUE obj) +{ + t_txnh *txn=NULL; + int rv; + + Data_Get_Struct(obj,t_txnh,txn); + rv=txn->txn->abort(txn->txn); + txn_finish(txn); + if ( rv != 0 ) { + raise_error(rv, "txn_abort: %s",db_strerror(rv)); + return Qnil; + } + return Qtrue; +} + +VALUE txn_discard(VALUE obj) +{ + t_txnh *txn=NULL; + int rv; + + Data_Get_Struct(obj,t_txnh,txn); + rv=txn->txn->discard(txn->txn,NOFLAGS); + txn_finish(txn); + if ( rv != 0 ) { + raise_error(rv, "txn_abort: %s",db_strerror(rv)); + return Qnil; + } + return Qtrue; +} + +VALUE txn_id(VALUE obj) +{ + t_txnh *txn=NULL; + int rv; + + Data_Get_Struct(obj,t_txnh,txn); + if (txn->txn==NULL) + raise_error(0,"txn is closed"); + + rv=txn->txn->id(txn->txn); + return INT2FIX(rv); +} + +VALUE txn_set_timeout(VALUE obj, VALUE vtimeout, VALUE vflags) +{ + t_txnh *txn=NULL; + db_timeout_t timeout; + u_int32_t flags=0; + int rv; + + if ( ! NIL_P(vflags)) + flags=NUM2INT(vflags); + + if ( ! FIXNUM_P(vtimeout) ) + raise_error(0,"timeout must be a fixed integer"); + timeout=FIX2UINT(vtimeout); + + Data_Get_Struct(obj,t_txnh,txn); + rv=txn->txn->set_timeout(txn->txn,timeout,flags); + if ( rv != 0 ) { + raise_error(rv, "txn_set_timeout: %s",db_strerror(rv)); + return Qnil; + } + return Qtrue; +} void Init_bdb2() { - fv_txn=rb_intern("@txn"); fv_call=rb_intern("call"); fv_err_new=rb_intern("new"); fv_err_code=rb_intern("@code"); @@ -778,32 +1348,57 @@ void Init_bdb2() { rb_define_alloc_func(cDb,db_alloc); rb_define_method(cDb,"initialize",db_initialize,0); - rb_define_method(cDb,"txn=",db_txn_eq,1); - rb_define_method(cDb,"put",db_put,3); - rb_define_method(cDb,"get",db_get,3); - rb_define_method(cDb,"del",db_del,2); - rb_define_method(cDb,"cursor",db_cursor,1); - rb_define_method(cDb,"associate",db_associate,3); + rb_define_method(cDb,"put",db_put,4); + rb_define_method(cDb,"get",db_get,4); + rb_define_method(cDb,"pget",db_get,4); + rb_define_method(cDb,"del",db_del,3); + rb_define_method(cDb,"cursor",db_cursor,2); + rb_define_method(cDb,"associate",db_associate,4); rb_define_method(cDb,"flags=",db_flags_set,1); - rb_define_method(cDb,"open",db_open,5); + rb_define_method(cDb,"open",db_open,6); rb_define_method(cDb,"close",db_close,1); rb_define_method(cDb,"[]",db_aget,1); rb_define_method(cDb,"[]=",db_aset,2); rb_define_method(cDb,"join",db_join,2); + rb_define_method(cDb,"get_byteswapped",db_get_byteswapped,0); + rb_define_method(cDb,"get_type",db_get_type,0); +#if DB_VERSION_MINOR > 3 + rb_define_method(cDb,"compact",db_compact,5); +#endif cCursor = rb_define_class_under(cDb,"Cursor",rb_cObject); rb_define_method(cCursor,"get",dbc_get,3); + rb_define_method(cCursor,"pget",dbc_pget,3); rb_define_method(cCursor,"put",dbc_put,3); rb_define_method(cCursor,"close",dbc_close,0); rb_define_method(cCursor,"del",dbc_del,0); rb_define_method(cCursor,"count",dbc_count,0); cEnv = rb_define_class_under(mBdb,"Env",rb_cObject); - rb_define_singleton_method(cEnv,"new",env_new,0); + rb_define_singleton_method(cEnv,"new",env_new,1); rb_define_method(cEnv,"open",env_open,3); rb_define_method(cEnv,"close",env_close,0); rb_define_method(cEnv,"db",env_db,0); rb_define_method(cEnv,"cachesize=",env_set_cachesize,1); rb_define_method(cEnv,"flags_on=",env_set_flags_on,1); rb_define_method(cEnv,"flags_off=",env_set_flags_off,1); + rb_define_method(cEnv,"list_dbs",env_list_dbs,0); + rb_define_method(cEnv,"txn_begin",env_txn_begin,2); + rb_define_method(cEnv,"txn_checkpoint",env_txn_checkpoint,3); + rb_define_method(cEnv,"txn_stat",env_txn_stat,1); + rb_define_method(cEnv,"set_timeout",env_set_timeout,2); + rb_define_method(cEnv,"get_timeout",env_get_timeout,1); + rb_define_method(cEnv,"set_tx_max",env_set_tx_max,1); + rb_define_method(cEnv,"get_tx_max",env_get_tx_max,0); + + cTxnStat = rb_define_class_under(mBdb,"TxnStat",rb_cObject); + cTxnStatActive = + rb_define_class_under(cTxnStat,"Active",rb_cObject); + + cTxn = rb_define_class_under(mBdb,"Txn",rb_cObject); + rb_define_method(cTxn,"commit",txn_commit,1); + rb_define_method(cTxn,"abort",txn_abort,0); + rb_define_method(cTxn,"discard",txn_discard,0); + rb_define_method(cTxn,"tid",txn_id,0); + rb_define_method(cTxn,"set_timeout",txn_set_timeout,2); } diff --git a/bdb.h b/bdb.h index 5e5b3f8..222cb5c 100644 --- a/bdb.h +++ b/bdb.h @@ -22,21 +22,34 @@ #define filename_dup(fpd,fps) \ strncpy(fpd,fps,FNLEN); +typedef struct s_envh { + VALUE self; + DB_ENV *env; + VALUE adb; /* Ruby array holding opened databases */ + VALUE atxn; /* Ruby array holding open transactions */ +} t_envh; + typedef struct s_dbh { - VALUE dbinst; - DB *dbp; + VALUE self; + DB *db; VALUE aproc; + t_envh *env; /* Parent environment, NULL if not opened from one */ + VALUE adbc; /* Ruby array holding opened cursor */ char filename[FNLEN+1]; } t_dbh; typedef struct s_dbch { + VALUE self; DBC *dbc; + t_dbh *db; char filename[FNLEN+1]; } t_dbch; -typedef struct s_envh { - DB_ENV *env; -} t_envh; +typedef struct s_txnh { + VALUE self; + DB_TXN *txn; + t_envh *env; +} t_txnh; #define ci(b,m) \ rb_define_const(b,#m,INT2FIX(m)) @@ -51,4 +64,17 @@ VALUE db_ ## fname ## _eq(VALUE obj, VALUE v) \ return obj; \ } +#define attr_writer(fname) \ + VALUE fname ## _writer(VALUE obj, VALUE v) \ + { \ + rb_ivar_set(obj,fv_ ## fname,v); \ + return obj; \ + } + +#define attr_reader(fname) \ + VALUE fname ## _reader(VALUE obj) \ + { \ + return rb_ivar_get(obj,fv_ ## fname); \ + } + #endif