810 lines
18 KiB
C
810 lines
18 KiB
C
|
#include <bdb.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#define LMEMFLAG 0
|
||
|
|
||
|
#ifdef HAVE_STDARG_PROTOTYPES
|
||
|
#include <stdarg.h>
|
||
|
#define va_init_list(a,b) va_start(a,b)
|
||
|
#else
|
||
|
#include <varargs.h>
|
||
|
#define va_init_list(a,b) va_start(a)
|
||
|
#endif
|
||
|
|
||
|
VALUE mBdb; /* Top level module */
|
||
|
VALUE cDb; /* DBT class */
|
||
|
VALUE cEnv; /* Environment class */
|
||
|
VALUE cTxn; /* Transaction class */
|
||
|
VALUE cCursor; /* Cursors */
|
||
|
VALUE eDbError;
|
||
|
|
||
|
static ID fv_txn, fv_call, fv_err_new,fv_err_code,fv_err_msg;
|
||
|
|
||
|
static void
|
||
|
#ifdef HAVE_STDARG_PROTOTYPES
|
||
|
raise_error(int code, const char *fmt, ...)
|
||
|
#else
|
||
|
raise_error(code,fmt,va_alist)
|
||
|
int code;
|
||
|
const char *fmt;
|
||
|
va_dcl
|
||
|
#endif
|
||
|
{
|
||
|
va_list args;
|
||
|
char buf[1024];
|
||
|
VALUE exc;
|
||
|
VALUE argv[2];
|
||
|
|
||
|
va_init_list(args,fmt);
|
||
|
vsnprintf(buf,1024,fmt,args);
|
||
|
va_end(args);
|
||
|
|
||
|
argv[0]=rb_str_new2(buf);
|
||
|
argv[1]=INT2NUM(code);
|
||
|
|
||
|
exc=rb_class_new_instance(2,argv,eDbError);
|
||
|
rb_exc_raise(exc);
|
||
|
}
|
||
|
|
||
|
VALUE err_initialize(VALUE obj, VALUE message, VALUE code)
|
||
|
{
|
||
|
VALUE args[1];
|
||
|
args[0]=message;
|
||
|
rb_call_super(1,args);
|
||
|
rb_ivar_set(obj,fv_err_code,code);
|
||
|
}
|
||
|
VALUE err_code(VALUE obj)
|
||
|
{
|
||
|
return rb_ivar_get(obj,fv_err_code);
|
||
|
}
|
||
|
|
||
|
static void db_free(void *p)
|
||
|
{
|
||
|
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->aproc ) {
|
||
|
rb_gc_unregister_address(&(dbh->aproc));
|
||
|
}
|
||
|
free(p);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void db_mark(void *p)
|
||
|
{
|
||
|
t_dbh *dbh;
|
||
|
if ( dbh->aproc )
|
||
|
rb_gc_mark(dbh->aproc);
|
||
|
}
|
||
|
static void dbc_free(void *p)
|
||
|
{
|
||
|
t_dbch *dbch;
|
||
|
dbch=(t_dbch *)p;
|
||
|
|
||
|
#ifdef DEBUG_DB
|
||
|
if ( RTEST(ruby_debug) )
|
||
|
fprintf(stderr,"%s/%d %s 0x%x\n",__FILE__,__LINE__,
|
||
|
"dbc_free cleanup!",p);
|
||
|
#endif
|
||
|
|
||
|
if ( dbch ) {
|
||
|
if ( dbch->dbc ) {
|
||
|
dbch->dbc->c_close(dbch->dbc);
|
||
|
if ( RTEST(ruby_debug) )
|
||
|
fprintf(stderr,"%s/%d %s 0x%x %s\n",__FILE__,__LINE__,
|
||
|
"dbc_free cursor was still open!",p,dbch->filename);
|
||
|
}
|
||
|
free(p);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VALUE
|
||
|
db_alloc(VALUE klass)
|
||
|
{
|
||
|
return Data_Wrap_Struct(klass,0,db_free,0);
|
||
|
}
|
||
|
|
||
|
VALUE db_init_aux(VALUE obj,DB_ENV *env)
|
||
|
{
|
||
|
DB *db;
|
||
|
t_dbh *dbh;
|
||
|
int rv;
|
||
|
|
||
|
rv = db_create(&db,env,0);
|
||
|
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);
|
||
|
#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;
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
VALUE db_initialize(VALUE obj)
|
||
|
{
|
||
|
return db_init_aux(obj,NULL);
|
||
|
}
|
||
|
|
||
|
VALUE db_open(VALUE obj, VALUE vdisk_file, VALUE vlogical_db,
|
||
|
VALUE vdbtype, VALUE vflags, VALUE vmode)
|
||
|
{
|
||
|
t_dbh *dbh;
|
||
|
int rv;
|
||
|
VALUE vtxn;
|
||
|
DB_TXN *txn=NULL;
|
||
|
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 */
|
||
|
|
||
|
if ( TYPE(vlogical_db)==T_STRING && RSTRING(vlogical_db)->len > 0 )
|
||
|
logical_db=StringValueCStr(vlogical_db);
|
||
|
|
||
|
if ( FIXNUM_P(vdbtype) ) {
|
||
|
dbtype=NUM2INT(vdbtype);
|
||
|
if ( dbtype < DB_BTREE || dbtype > DB_UNKNOWN ) {
|
||
|
raise_error(0,"db_open Bad access type: %d",dbtype);
|
||
|
return Qnil;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( TYPE(vdisk_file)!=T_STRING || RSTRING(vdisk_file)->len < 1 ) {
|
||
|
raise_error(0,"db_open Bad disk file name");
|
||
|
return Qnil;
|
||
|
}
|
||
|
|
||
|
if ( ! NIL_P(vmode) )
|
||
|
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 (rv != 0) {
|
||
|
raise_error(rv,"db_open failure: %s",db_strerror(rv));
|
||
|
}
|
||
|
filename_copy(dbh->filename,vdisk_file)
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
VALUE db_flags_set(VALUE obj, VALUE vflags)
|
||
|
{
|
||
|
t_dbh *dbh;
|
||
|
int rv;
|
||
|
u_int32_t flags;
|
||
|
|
||
|
flags=NUM2INT(vflags);
|
||
|
Data_Get_Struct(obj,t_dbh,dbh);
|
||
|
rv = dbh->dbp->set_flags(dbh->dbp,flags);
|
||
|
if ( rv != 0 ) {
|
||
|
raise_error(rv, "db_flag_set failure: %s",db_strerror(rv));
|
||
|
}
|
||
|
return vflags;
|
||
|
}
|
||
|
|
||
|
VALUE db_close(VALUE obj, VALUE vflags)
|
||
|
{
|
||
|
t_dbh *dbh;
|
||
|
int rv;
|
||
|
u_int32_t flags;
|
||
|
|
||
|
flags=NUM2INT(vflags);
|
||
|
Data_Get_Struct(obj,t_dbh,dbh);
|
||
|
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);
|
||
|
if ( rv != 0 ) {
|
||
|
raise_error(rv, "db_close failure: %s",db_strerror(rv));
|
||
|
}
|
||
|
dbh->dbp=NULL;
|
||
|
dbh->aproc=(VALUE)NULL;
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
VALUE db_put(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags)
|
||
|
{
|
||
|
t_dbh *dbh;
|
||
|
int rv;
|
||
|
u_int32_t flags=0;
|
||
|
DBT key,data;
|
||
|
|
||
|
memset(&key,0,sizeof(DBT));
|
||
|
memset(&data,0,sizeof(DBT));
|
||
|
|
||
|
if ( ! NIL_P(vflags) )
|
||
|
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;
|
||
|
|
||
|
StringValue(vdata);
|
||
|
data.data = RSTRING(vdata)->ptr;
|
||
|
data.size = RSTRING(vdata)->len;
|
||
|
data.flags = LMEMFLAG;
|
||
|
|
||
|
rv = dbh->dbp->put(dbh->dbp,NULL,&key,&data,flags);
|
||
|
/*
|
||
|
if (rv == DB_KEYEXIST)
|
||
|
return Qnil;
|
||
|
*/
|
||
|
if (rv != 0) {
|
||
|
raise_error(rv, "db_put fails: %s",db_strerror(rv));
|
||
|
}
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
VALUE db_get(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags)
|
||
|
{
|
||
|
t_dbh *dbh;
|
||
|
int rv;
|
||
|
u_int32_t flags=0;
|
||
|
DBT key,data;
|
||
|
VALUE str;
|
||
|
|
||
|
memset(&key,0,sizeof(DBT));
|
||
|
memset(&data,0,sizeof(DBT));
|
||
|
|
||
|
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->dbp->get(dbh->dbp,NULL,&key,&data,flags);
|
||
|
if ( rv == 0 ) {
|
||
|
return rb_str_new(data.data,data.size);
|
||
|
} else if (rv == DB_NOTFOUND) {
|
||
|
return Qnil;
|
||
|
} else {
|
||
|
raise_error(rv, "db_get failure: %s",db_strerror(rv));
|
||
|
}
|
||
|
return Qnil;
|
||
|
}
|
||
|
|
||
|
VALUE db_aget(VALUE obj, VALUE vkey)
|
||
|
{
|
||
|
return db_get(obj,vkey,Qnil,Qnil);
|
||
|
}
|
||
|
VALUE db_aset(VALUE obj, VALUE vkey, VALUE vdata)
|
||
|
{
|
||
|
return db_put(obj,vkey,vdata,Qnil);
|
||
|
}
|
||
|
VALUE db_join(VALUE obj, VALUE vacurs, VALUE vflags)
|
||
|
{
|
||
|
t_dbh *dbh;
|
||
|
t_dbch *dbch;
|
||
|
int flags;
|
||
|
DBC **curs;
|
||
|
int i,rv;
|
||
|
VALUE jcurs;
|
||
|
|
||
|
flags=NUM2INT(vflags);
|
||
|
Data_Get_Struct(obj,t_dbh,dbh);
|
||
|
|
||
|
curs = ALLOCA_N(DBC *,RARRAY(vacurs)->len);
|
||
|
for (i=0; i<RARRAY(vacurs)->len; i++) {
|
||
|
Data_Get_Struct(RARRAY(vacurs)->ptr[i],t_dbch,dbch);
|
||
|
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);
|
||
|
if (rv) {
|
||
|
raise_error(rv, "db_join: %s",db_strerror(rv));
|
||
|
}
|
||
|
|
||
|
rb_obj_call_init(jcurs,0,NULL);
|
||
|
return jcurs;
|
||
|
}
|
||
|
|
||
|
VALUE db_del(VALUE obj, VALUE vkey, VALUE vflags)
|
||
|
{
|
||
|
t_dbh *dbh;
|
||
|
int rv;
|
||
|
u_int32_t flags;
|
||
|
DBT key;
|
||
|
VALUE str;
|
||
|
|
||
|
memset(&key,0,sizeof(DBT));
|
||
|
|
||
|
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;
|
||
|
|
||
|
rv = dbh->dbp->del(dbh->dbp,NOTXN,&key,flags);
|
||
|
if ( rv == DB_NOTFOUND ) {
|
||
|
return Qnil;
|
||
|
} else if (rv != 0) {
|
||
|
raise_error(rv, "db_del failure: %s",db_strerror(rv));
|
||
|
}
|
||
|
return Qtrue;
|
||
|
}
|
||
|
|
||
|
t_dbh *dbassoc[OPEN_MAX];
|
||
|
VALUE
|
||
|
assoc_callback2(VALUE *args)
|
||
|
{
|
||
|
rb_funcall(args[0],fv_call,3,args[1],args[2],args[3]);
|
||
|
}
|
||
|
|
||
|
int assoc_callback(DB *secdb,const DBT* key, const DBT* data, DBT* result)
|
||
|
{
|
||
|
t_dbh *dbh;
|
||
|
VALUE proc;
|
||
|
int fdp,status;
|
||
|
VALUE retv;
|
||
|
VALUE args[4];
|
||
|
|
||
|
memset(result,0,sizeof(DBT));
|
||
|
secdb->fd(secdb,&fdp);
|
||
|
dbh=dbassoc[fdp];
|
||
|
|
||
|
args[0]=dbh->aproc;
|
||
|
args[1]=dbh->dbinst;
|
||
|
args[2]=rb_str_new(key->data,key->size);
|
||
|
args[3]=rb_str_new(data->data,data->size);
|
||
|
|
||
|
retv=rb_protect((VALUE(*)_((VALUE)))assoc_callback2,(VALUE)args,&status);
|
||
|
|
||
|
if (status) return 99999;
|
||
|
if ( NIL_P(retv) )
|
||
|
return DB_DONOTINDEX;
|
||
|
|
||
|
if ( TYPE(retv) != T_STRING )
|
||
|
rb_warning("return from assoc callback not a string!");
|
||
|
|
||
|
StringValue(retv);
|
||
|
result->data=RSTRING(retv)->ptr;
|
||
|
result->size=RSTRING(retv)->len;
|
||
|
result->flags=LMEMFLAG;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
VALUE db_associate(VALUE obj, VALUE osecdb, VALUE vflags, VALUE cb_proc)
|
||
|
{
|
||
|
t_dbh *sdbh,*pdbh;
|
||
|
int rv;
|
||
|
u_int32_t flags,flagsp,flagss;
|
||
|
int fdp;
|
||
|
|
||
|
flags=NUM2INT(vflags);
|
||
|
Data_Get_Struct(obj,t_dbh,pdbh);
|
||
|
Data_Get_Struct(osecdb,t_dbh,sdbh);
|
||
|
|
||
|
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);
|
||
|
if ( flagsp & DB_RDONLY & flagss ) {
|
||
|
rv=pdbh->dbp->associate(pdbh->dbp,NOTXN,sdbh->dbp,NULL,flags);
|
||
|
if (rv)
|
||
|
raise_error(rv,"db_associate: %s",db_strerror(rv));
|
||
|
|
||
|
} else {
|
||
|
raise_error(0,"db_associate empty associate only available when both DBs opened with DB_RDONLY");
|
||
|
}
|
||
|
} else if ( rb_obj_is_instance_of(cb_proc,rb_cProc) != Qtrue ) {
|
||
|
raise_error(0, "db_associate proc required");
|
||
|
}
|
||
|
|
||
|
sdbh->dbp->fd(sdbh->dbp,&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));
|
||
|
dbassoc[fdp]=sdbh;
|
||
|
rv=pdbh->dbp->associate(pdbh->dbp,NOTXN,sdbh->dbp,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]);
|
||
|
#endif
|
||
|
if (rv != 0) {
|
||
|
raise_error(rv, "db_associate failure: %s",db_strerror(rv));
|
||
|
}
|
||
|
return Qtrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
VALUE db_cursor(VALUE obj, VALUE vflags)
|
||
|
{
|
||
|
t_dbh *dbh;
|
||
|
int rv;
|
||
|
u_int32_t flags;
|
||
|
DBC *dbc;
|
||
|
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);
|
||
|
|
||
|
rv=dbh->dbp->cursor(dbh->dbp,NOTXN,&(dbch->dbc),flags);
|
||
|
if (rv)
|
||
|
raise_error(rv,"db_cursor: %s",db_strerror(rv));
|
||
|
|
||
|
filename_dup(dbch->filename,dbh->filename);
|
||
|
rb_obj_call_init(c_obj,0,NULL);
|
||
|
return c_obj;
|
||
|
}
|
||
|
|
||
|
VALUE dbc_close(VALUE obj)
|
||
|
{
|
||
|
t_dbch *dbch;
|
||
|
int rv;
|
||
|
Data_Get_Struct(obj,t_dbch,dbch);
|
||
|
if ( dbch->dbc ) {
|
||
|
rv=dbch->dbc->c_close(dbch->dbc);
|
||
|
if (rv)
|
||
|
raise_error(rv,"dbc_close: %s",db_strerror(rv));
|
||
|
|
||
|
dbch->dbc=NULL;
|
||
|
}
|
||
|
return Qnil;
|
||
|
}
|
||
|
|
||
|
VALUE dbc_get(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags)
|
||
|
{
|
||
|
t_dbch *dbch;
|
||
|
u_int32_t flags;
|
||
|
DBT key,data;
|
||
|
VALUE rar;
|
||
|
int rv;
|
||
|
|
||
|
flags=NUM2INT(vflags);
|
||
|
Data_Get_Struct(obj,t_dbch,dbch);
|
||
|
|
||
|
memset(&key,0,sizeof(DBT));
|
||
|
memset(&data,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_get(dbch->dbc,&key,&data,flags);
|
||
|
if ( rv == 0 ) {
|
||
|
rar = rb_ary_new3(2,rb_str_new(key.data,key.size),
|
||
|
rb_str_new(data.data,data.size));
|
||
|
return rar;
|
||
|
} else if (rv == DB_NOTFOUND) {
|
||
|
return Qnil;
|
||
|
} else {
|
||
|
raise_error(rv, "dbc_get %s",db_strerror(rv));
|
||
|
}
|
||
|
return Qnil;
|
||
|
}
|
||
|
VALUE dbc_put(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags)
|
||
|
{
|
||
|
t_dbch *dbch;
|
||
|
u_int32_t flags;
|
||
|
DBT key,data;
|
||
|
int rv;
|
||
|
|
||
|
if ( NIL_P(vdata) )
|
||
|
raise_error(0,"data element is required for put");
|
||
|
|
||
|
flags=NUM2INT(vflags);
|
||
|
Data_Get_Struct(obj,t_dbch,dbch);
|
||
|
|
||
|
memset(&key,0,sizeof(DBT));
|
||
|
memset(&data,0,sizeof(DBT));
|
||
|
|
||
|
if ( ! NIL_P(vkey) ) {
|
||
|
StringValue(vkey);
|
||
|
key.data = RSTRING(vkey)->ptr;
|
||
|
key.size = RSTRING(vkey)->len;
|
||
|
key.flags = LMEMFLAG;
|
||
|
}
|
||
|
|
||
|
StringValue(vdata);
|
||
|
data.data = RSTRING(vdata)->ptr;
|
||
|
data.size = RSTRING(vdata)->len;
|
||
|
data.flags = LMEMFLAG;
|
||
|
|
||
|
rv = dbch->dbc->c_put(dbch->dbc,&key,&data,flags);
|
||
|
if (rv != 0)
|
||
|
raise_error(rv,"dbc_put failure: %s",db_strerror(rv));
|
||
|
|
||
|
return vdata;
|
||
|
}
|
||
|
|
||
|
VALUE dbc_del(VALUE obj)
|
||
|
{
|
||
|
t_dbch *dbch;
|
||
|
int rv;
|
||
|
|
||
|
Data_Get_Struct(obj,t_dbch,dbch);
|
||
|
rv = dbch->dbc->c_del(dbch->dbc,0);
|
||
|
if (rv == DB_KEYEMPTY)
|
||
|
return Qnil;
|
||
|
else if (rv != 0) {
|
||
|
raise_error(rv, "dbc_del failure: %s",db_strerror(rv));
|
||
|
}
|
||
|
return Qtrue;
|
||
|
}
|
||
|
|
||
|
VALUE dbc_count(VALUE obj)
|
||
|
{
|
||
|
t_dbch *dbch;
|
||
|
int rv;
|
||
|
db_recno_t count;
|
||
|
|
||
|
Data_Get_Struct(obj,t_dbch,dbch);
|
||
|
rv = dbch->dbc->c_count(dbch->dbc,&count,0);
|
||
|
if (rv != 0)
|
||
|
raise_error(rv, "db_count failure: %s",db_strerror(rv));
|
||
|
|
||
|
return INT2NUM(count);
|
||
|
}
|
||
|
|
||
|
static void env_free(void *p)
|
||
|
{
|
||
|
t_envh *eh;
|
||
|
eh=(t_envh *)p;
|
||
|
|
||
|
#ifdef DEBUG_DB
|
||
|
if ( RTEST(ruby_debug) )
|
||
|
fprintf(stderr,"%s/%d %s 0x%x\n",__FILE__,__LINE__,"env_free cleanup!",p);
|
||
|
#endif
|
||
|
|
||
|
if ( eh ) {
|
||
|
if ( eh->env ) {
|
||
|
eh->env->close(eh->env,0);
|
||
|
}
|
||
|
free(p);
|
||
|
}
|
||
|
}
|
||
|
VALUE env_new(VALUE class)
|
||
|
{
|
||
|
t_envh *eh;
|
||
|
int rv;
|
||
|
VALUE obj;
|
||
|
|
||
|
obj=Data_Make_Struct(class,t_envh,NULL,env_free,eh);
|
||
|
rv=db_env_create(&(eh->env),0);
|
||
|
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);
|
||
|
rb_obj_call_init(obj,0,NULL);
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
VALUE env_open(VALUE obj, VALUE vhome, VALUE vflags, VALUE vmode)
|
||
|
{
|
||
|
t_envh *eh;
|
||
|
int rv;
|
||
|
u_int32_t flags=0;
|
||
|
int mode=0;
|
||
|
|
||
|
if ( ! NIL_P(vflags) )
|
||
|
flags=NUM2INT(vflags);
|
||
|
if ( ! NIL_P(vmode) )
|
||
|
mode=NUM2INT(vmode);
|
||
|
Data_Get_Struct(obj,t_envh,eh);
|
||
|
rv = eh->env->open(eh->env,StringValueCStr(vhome),flags,mode);
|
||
|
if (rv != 0) {
|
||
|
raise_error(rv, "env_open failure: %s",db_strerror(rv));
|
||
|
}
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
VALUE env_close(VALUE obj)
|
||
|
{
|
||
|
t_envh *eh;
|
||
|
int rv;
|
||
|
|
||
|
Data_Get_Struct(obj,t_envh,eh);
|
||
|
|
||
|
if ( RTEST(ruby_debug) )
|
||
|
rb_warning("%s/%d %s 0x%x",__FILE__,__LINE__,"env_close!",eh);
|
||
|
|
||
|
rv = eh->env->close(eh->env,0);
|
||
|
if ( rv != 0 ) {
|
||
|
raise_error(rv, "env_close failure: %s",db_strerror(rv));
|
||
|
return Qnil;
|
||
|
}
|
||
|
eh->env=NULL;
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
VALUE env_db(VALUE obj)
|
||
|
{
|
||
|
t_envh *eh;
|
||
|
VALUE dbo;
|
||
|
|
||
|
Data_Get_Struct(obj,t_envh,eh);
|
||
|
dbo = Data_Wrap_Struct(cDb,0,db_free,0);
|
||
|
|
||
|
return db_init_aux(dbo,eh->env);
|
||
|
}
|
||
|
|
||
|
VALUE env_set_cachesize(VALUE obj, VALUE size)
|
||
|
{
|
||
|
t_envh *eh;
|
||
|
unsigned long long ln;
|
||
|
u_int32_t bytes=0,gbytes=0;
|
||
|
int rv;
|
||
|
Data_Get_Struct(obj,t_envh,eh);
|
||
|
|
||
|
if ( TYPE(size) == T_BIGNUM ) {
|
||
|
ln = rb_big2ull(size);
|
||
|
bytes = (u_int32_t) ln;
|
||
|
gbytes = (u_int32_t) (ln >> sizeof(u_int32_t));
|
||
|
} else if (FIXNUM_P(size) ) {
|
||
|
bytes=NUM2INT(size);
|
||
|
} else {
|
||
|
raise_error(0,"set_cachesize requires number");
|
||
|
return Qnil;
|
||
|
}
|
||
|
|
||
|
rb_warning("setting cache size %d %d %d",gbytes,bytes,1);
|
||
|
rv=eh->env->set_cachesize(eh->env,gbytes,bytes,1);
|
||
|
if ( rv != 0 ) {
|
||
|
raise_error(rv, "set_cachesize failure: %s",db_strerror(rv));
|
||
|
return Qnil;
|
||
|
}
|
||
|
|
||
|
return Qtrue;
|
||
|
}
|
||
|
|
||
|
VALUE env_set_flags(VALUE obj, VALUE vflags, int onoff)
|
||
|
{
|
||
|
t_envh *eh;
|
||
|
int rv;
|
||
|
u_int32_t flags;
|
||
|
|
||
|
Data_Get_Struct(obj,t_envh,eh);
|
||
|
if ( ! NIL_P(vflags) ) {
|
||
|
flags=NUM2INT(vflags);
|
||
|
|
||
|
rv=eh->env->set_flags(eh->env,flags,onoff);
|
||
|
|
||
|
if ( rv != 0 ) {
|
||
|
raise_error(rv, "set_flags failure: %s",db_strerror(rv));
|
||
|
return Qnil;
|
||
|
}
|
||
|
}
|
||
|
return Qtrue;
|
||
|
}
|
||
|
|
||
|
VALUE env_set_flags_on(VALUE obj, VALUE vflags)
|
||
|
{
|
||
|
return env_set_flags(obj,vflags,1);
|
||
|
}
|
||
|
VALUE env_set_flags_off(VALUE obj, VALUE vflags)
|
||
|
{
|
||
|
return env_set_flags(obj,vflags,0);
|
||
|
}
|
||
|
|
||
|
simple_set(txn);
|
||
|
|
||
|
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");
|
||
|
fv_err_msg=rb_intern("@message");
|
||
|
|
||
|
mBdb = rb_define_module("Bdb");
|
||
|
|
||
|
#include "bdb_aux._c"
|
||
|
|
||
|
cDb = rb_define_class_under(mBdb,"Db", rb_cObject);
|
||
|
eDbError = rb_define_class_under(mBdb,"DbError",rb_eStandardError);
|
||
|
rb_define_method(eDbError,"initialize",err_initialize,2);
|
||
|
rb_define_method(eDbError,"code",err_code,0);
|
||
|
|
||
|
rb_define_const(cDb,"BTREE",INT2FIX((DBTYPE)(DB_BTREE)));
|
||
|
rb_define_const(cDb,"HASH",INT2FIX((DBTYPE)(DB_HASH)));
|
||
|
rb_define_const(cDb,"RECNO",INT2FIX((DBTYPE)(DB_RECNO)));
|
||
|
rb_define_const(cDb,"QUEUE",INT2FIX((DBTYPE)(DB_QUEUE)));
|
||
|
rb_define_const(cDb,"UNKNOWN",INT2FIX((DBTYPE)(DB_UNKNOWN)));
|
||
|
|
||
|
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,"flags=",db_flags_set,1);
|
||
|
rb_define_method(cDb,"open",db_open,5);
|
||
|
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);
|
||
|
|
||
|
cCursor = rb_define_class_under(cDb,"Cursor",rb_cObject);
|
||
|
rb_define_method(cCursor,"get",dbc_get,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_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);
|
||
|
}
|