Initial revision
This commit is contained in:
commit
20a230483c
72
README
Normal file
72
README
Normal file
|
@ -0,0 +1,72 @@
|
|||
This interface is most closely based on the DB4 C api and tries to
|
||||
maintain close interface proximity.
|
||||
|
||||
That API is published by sleepycat at
|
||||
|
||||
http://www.sleepycat.com/docs/api_c/frame.html
|
||||
|
||||
function all arguments that are systematically omitted are leading
|
||||
DB handles and TXN handles. A few calls omit the flags parameter when the
|
||||
documenation indicates that no flag values are used. cursor.close is one.
|
||||
|
||||
the defines generator is imperfect and includes some defines that are not
|
||||
flags. while it could be improved, it is easier to delete the incorrect ones.
|
||||
thus, if you decide to rebuild the defines, you will need to edit the resulting
|
||||
file. this may be necessary if using a different release of DB4 than the one
|
||||
I used.
|
||||
|
||||
I have put all possible caution into ensuring that DB and Ruby cooperate.
|
||||
The memory access was one apsect carefully considered. Since Ruby copies
|
||||
when doing String#new, all key/data retrieval from DB is done with a 0 flag,
|
||||
meaning that DB will be responsible. See the copied news group posting about
|
||||
the effect of that.
|
||||
|
||||
The only other design consideration of consequence was associate. The prior
|
||||
version used a Ruby thread local variable and kept track of the current
|
||||
database in use. I decided to take a simpler approach since Ruby is green
|
||||
threads. A global array stores the VALUE of the Proc for a given association
|
||||
by the file descriptor number of the underlying database. This is looked
|
||||
up when the first layer callback is made. It would have been better considered
|
||||
if DB allowed the passing of a (void *) user data into the alloc that would
|
||||
be supplied during callback. So far this design has not produced any problems.
|
||||
|
||||
|
||||
|
||||
[This is a message from comp.databases.berkeley-db]
|
||||
|
||||
http://groups.google.com/group/comp.databases.berkeley-db/browse_frm/thread/4f70a9999b64ce6a/c06b94692e3cbc41?tvc=1&q=dbt+malloc#c06b94692e3cbc41
|
||||
|
||||
Subject: Some questions about BerkeleyDB
|
||||
|
||||
|
||||
Patrick Schaaf
|
||||
Sep 16 2004, 9:31 am show options
|
||||
Hi Cylin,
|
||||
|
||||
I'm only replying to one point; I'm not so sure about the others.
|
||||
|
||||
>> >4.In http://www.sleepycat.com/docs/api_c/dbt_class.html#DB_DBT_MALLOC
|
||||
>I mean when we query by a key, and get return data( or key).
|
||||
>If we set DB_DBT_MALLOC or not ,what is the difference about
|
||||
>data.data/key.data?
|
||||
|
||||
DB_DBT_MALLOC is only relevant for the DBT in which Berkeley DB
|
||||
gives you back data from the database.
|
||||
Without DB_DBT_MALLOC, i.e. with DBT.flags set to 0, after the successful
|
||||
query call, you will find in data.data a pointer into memory which is
|
||||
under the responsibility of Berkeley DB. It may be (I don't know for sure)
|
||||
a pointer into the memory mapped shared environment. You can copy from
|
||||
there to some place safe, and I think you must NOT assume that the pointer
|
||||
will be valid after another call to a retrieval function.
|
||||
|
||||
On the other hand, when you set (before the retrieval call) that DBT's
|
||||
.flags field to DB_DBT_MALLOC, then the retrieval function of Berkeley DB
|
||||
will automatically call malloc() to get _new_ memory for the retrieved
|
||||
data, and you will find after the retrieval that data.data points
|
||||
to that memory. As it is newly malloc()ed, you can access it for as
|
||||
long as you want. IMPORTANT: it is also your responsibility to
|
||||
use free() when you don't need it any more.
|
||||
|
||||
best regards
|
||||
Patrick
|
||||
|
809
bdb.c
Normal file
809
bdb.c
Normal file
|
@ -0,0 +1,809 @@
|
|||
#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);
|
||||
}
|
49
bdb.h
Normal file
49
bdb.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
|
||||
#ifndef BDB2_H
|
||||
#define BDB2_H
|
||||
|
||||
#include <ruby.h>
|
||||
#include <version.h>
|
||||
#include <db4/db.h>
|
||||
|
||||
|
||||
#define NOTXN NULL
|
||||
|
||||
#define FNLEN 40
|
||||
|
||||
#define filename_copy(fp,fv) \
|
||||
strncpy(fp,RSTRING(fv)->ptr,FNLEN);
|
||||
|
||||
#define filename_dup(fpd,fps) \
|
||||
strncpy(fpd,fps,FNLEN);
|
||||
|
||||
typedef struct s_dbh {
|
||||
VALUE dbinst;
|
||||
DB *dbp;
|
||||
VALUE aproc;
|
||||
char filename[FNLEN+1];
|
||||
} t_dbh;
|
||||
|
||||
typedef struct s_dbch {
|
||||
DBC *dbc;
|
||||
char filename[FNLEN+1];
|
||||
} t_dbch;
|
||||
|
||||
typedef struct s_envh {
|
||||
DB_ENV *env;
|
||||
} t_envh;
|
||||
|
||||
#define ci(b,m) \
|
||||
rb_define_const(b,#m,INT2FIX(m))
|
||||
|
||||
#define cs(b,m) \
|
||||
rb_define_const(b,#m,rb_str_new2(m))
|
||||
|
||||
#define simple_set(fname) \
|
||||
VALUE db_ ## fname ## _eq(VALUE obj, VALUE v) \
|
||||
{ \
|
||||
rb_ivar_set(obj,fv_ ## fname,v); \
|
||||
return obj; \
|
||||
}
|
||||
|
||||
#endif
|
69
extconf.rb
Normal file
69
extconf.rb
Normal file
|
@ -0,0 +1,69 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require 'mkmf'
|
||||
|
||||
mj,mi,rv=RUBY_VERSION.split('.').collect {|s| s.to_i}
|
||||
ri=(((mj*1000)+mi)*1000)+rv
|
||||
if ri < 1008004
|
||||
$stderr.puts("Version 1.8.4 minimum required")
|
||||
exit(3)
|
||||
end
|
||||
|
||||
inc_dir,lib_dir = dir_config('bdb2')
|
||||
|
||||
$stderr.puts("lib_dir=#{lib_dir} inc_dir=#{inc_dir}")
|
||||
|
||||
#case Config::CONFIG["arch"]
|
||||
#when /solaris2/
|
||||
# $DLDFLAGS ||= ""
|
||||
# $DLDFLAGS += " -R#{lib_dir}"
|
||||
#end
|
||||
|
||||
versions=%w(db-4.3 db-4.2)
|
||||
locations=%w(/usr/local/lib /opt/local/lib /usr/local/BerkeleyDB.4.2/lib /usr/local/BerkeleyDB.4.3/lib)
|
||||
until versions.empty?
|
||||
(lib_ok=find_library(this_version=versions.shift,'db_create',*locations)) && break
|
||||
end
|
||||
|
||||
h_locations=%w(/usr/local/include /opt/local/include /usr/local/BerkeleyDB.4.2/include /usr/local/BerkeleyDB.4.3/include)
|
||||
h_headers=%w(db4/db.h db.h)
|
||||
until h_headers.empty?
|
||||
(inc_ok=find_header(this_h=h_headers.shift,*h_locations)) && break
|
||||
end
|
||||
|
||||
# Find db.h, not sure this will work everywhere, gcc is ok
|
||||
src=create_tmpsrc("#include <#{this_h}>")
|
||||
cmd=cpp_command("-M")
|
||||
r=`#{cmd}`
|
||||
header_loc=r.split.collect {|k| k if k =~ %r{^/.*db.h} }.compact[0]
|
||||
message("header is #{header_loc}\n")
|
||||
|
||||
|
||||
inc="#include <#{this_h}>"
|
||||
n=0
|
||||
message("Writing bdb_aux.c (defines), this takes a while")
|
||||
defines=[]
|
||||
File.open(header_loc) {|fd|
|
||||
File.open("bdb_aux._c","w") {|hd|
|
||||
hd.puts("/* This file automatically generated by extconf.rb */\n")
|
||||
fd.each_line {|l|
|
||||
if l =~ %r{^#define\s+(DBC?_\w*)\s+(\"?)} and macro_defined?($1,inc)
|
||||
if $2 == '"'
|
||||
hd.print(%Q{ cs(mBdb,%s);\n}%[$1])
|
||||
else
|
||||
hd.print(%Q{ ci(mBdb,%s);\n}%[$1])
|
||||
end
|
||||
message(".")
|
||||
n+=1
|
||||
end
|
||||
}
|
||||
}
|
||||
}
|
||||
message("\nwrote #{n} defines\n")
|
||||
|
||||
if lib_ok and inc_ok
|
||||
create_makefile('bdb2')
|
||||
else
|
||||
$stderr.puts("cannot create Makefile")
|
||||
end
|
||||
|
15
status.txt
Normal file
15
status.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
9-Feb-2006
|
||||
Done:
|
||||
All common environment, database and cursor functions are in. They are also
|
||||
_fully_ unit tested and are in production under heavy use. There are a
|
||||
few missing, like pget.
|
||||
|
||||
Limitations:
|
||||
If Ruby process exits and there are any open cursors (your program did not
|
||||
close them) the process will SEGV. This is due to dbc_free trying to close
|
||||
the cursor(s), even though the DB may already be closed. This will be fixed
|
||||
by keeping track of open cursors and automatically closing then when
|
||||
db->close is called, either by calling close or during finalization. Other
|
||||
than messing up your BDB environment, the SEGV is larglely harmless (but
|
||||
annoying). It should never happen if your program is written correctly.
|
||||
|
157
test.rb
Executable file
157
test.rb
Executable file
|
@ -0,0 +1,157 @@
|
|||
#!/usr/bin/env ruby
|
||||
require 'bdb2'
|
||||
|
||||
50.times {|n|
|
||||
db=Bdb::Db.new
|
||||
db.open("dbtest.db",nil,Bdb::Db::BTREE,Bdb::DB_CREATE,0)
|
||||
db.put(n.to_s,"ploppy #{n} #{Time.now}",0)
|
||||
db.close(0)
|
||||
}
|
||||
db=Bdb::Db.new
|
||||
db.open("dbtest.db",nil,nil,nil,0)
|
||||
500.times {|n|
|
||||
db.put(n.to_s,"ploppy #{n}",0)
|
||||
}
|
||||
db.close(0)
|
||||
|
||||
db=Bdb::Db.new
|
||||
db.open("dbtest.db",nil,nil,nil,0)
|
||||
dbc=db.cursor(0)
|
||||
puts("cursor is: "+dbc.inspect.to_s)
|
||||
kv=dbc.get(nil,nil,Bdb::DB_FIRST);
|
||||
puts("first data is: " + kv.inspect.to_s)
|
||||
kv=dbc.get(nil,nil,Bdb::DB_LAST);
|
||||
puts("last data is: " + kv.inspect.to_s)
|
||||
kv=dbc.get(nil,nil,Bdb::DB_PREV);
|
||||
puts("prior data is: " + kv.inspect.to_s)
|
||||
dbc.del;
|
||||
begin
|
||||
kv=dbc.get(nil,nil,Bdb::DB_CURRENT);
|
||||
rescue Bdb::DbError => m
|
||||
puts("deleted record is gone from current position:" + m.to_s)
|
||||
puts("code is #{m.code}")
|
||||
end
|
||||
puts("current data is: " +kv.inspect.to_s)
|
||||
dbc.put("elephant","gorilla",Bdb::DB_KEYFIRST)
|
||||
kv=dbc.get(nil,nil,Bdb::DB_CURRENT);
|
||||
puts("current data after put: " +kv.inspect.to_s)
|
||||
kv=dbc.get(nil,nil,Bdb::DB_LAST);
|
||||
puts("last data is: " + kv.inspect.to_s)
|
||||
kv=dbc.get(nil,nil,Bdb::DB_PREV);
|
||||
puts("prior data is: " + kv.inspect.to_s)
|
||||
puts("duplicates here is: " + dbc.count.to_s)
|
||||
dbc.close
|
||||
db.close(0)
|
||||
|
||||
db=Bdb::Db.new
|
||||
db.open("dbtest.db",nil,nil,nil,0)
|
||||
5.times {|n|
|
||||
$stdout.puts(db.get(n.to_s,0))
|
||||
begin
|
||||
db.del(n.to_s,0)
|
||||
rescue
|
||||
end
|
||||
}
|
||||
5.times {|n|
|
||||
v=db.get(n.to_s,0)
|
||||
if v
|
||||
$stdout.puts("For #{n}:" + v)
|
||||
else
|
||||
$stdout.puts("-- not in database #{n}")
|
||||
end
|
||||
}
|
||||
db.close(0)
|
||||
$stderr.puts("All OK!")
|
||||
|
||||
|
||||
File.delete('db1.db') if File.exist?('db1.db')
|
||||
File.delete('db2.db') if File.exist?('db2.db')
|
||||
|
||||
db=Bdb::Db.new
|
||||
db.open("db1.db",nil,Bdb::Db::HASH,Bdb::DB_CREATE,0)
|
||||
|
||||
db2=Bdb::Db.new
|
||||
db2.flags=Bdb::DB_DUPSORT
|
||||
db2.open("db2.db",nil,Bdb::Db::HASH,Bdb::DB_CREATE,0)
|
||||
|
||||
db.associate(db2,0,
|
||||
proc {|sdb,key,data|
|
||||
key.split('-')[0]
|
||||
})
|
||||
|
||||
c=0
|
||||
File.open("skus") {|fd|
|
||||
tlen=fd.stat.size
|
||||
pf=tlen/10
|
||||
pl=0
|
||||
fd.each do |line|
|
||||
c+=1
|
||||
if c%1000==0
|
||||
$stderr.print('.')
|
||||
cp=fd.pos
|
||||
if ( cp/pf > pl )
|
||||
pl=cp/pf
|
||||
$stderr.print(" #{pl*10}% ")
|
||||
end
|
||||
end
|
||||
line.chomp!
|
||||
n=line*50
|
||||
isbn,item=line.split('|')[0..1]
|
||||
sku="%s-%03d"%[isbn,item]
|
||||
db.put(sku,line,0)
|
||||
end
|
||||
}
|
||||
$stderr.print("\ntotal count: #{c}\n")
|
||||
|
||||
db2.close(0)
|
||||
db.close(0)
|
||||
|
||||
$stderr.puts("test environment")
|
||||
|
||||
env=Bdb::Env.new
|
||||
env.cachesize=25*1024*1024;
|
||||
env.open(".",Bdb::DB_INIT_CDB|Bdb::DB_INIT_MPOOL|Bdb::DB_CREATE,0)
|
||||
|
||||
db=env.db
|
||||
db.open("db1.db",nil,Bdb::Db::HASH,Bdb::DB_CREATE,0)
|
||||
|
||||
db2=env.db
|
||||
db2.flags=Bdb::DB_DUPSORT
|
||||
db2.open("db2.db",nil,Bdb::Db::HASH,Bdb::DB_CREATE,0)
|
||||
|
||||
db.associate(db2,0,
|
||||
proc {|sdb,key,data|
|
||||
key.split('-')[0]
|
||||
})
|
||||
c=0
|
||||
File.open("skus") {|fd|
|
||||
tlen=fd.stat.size
|
||||
pf=tlen/10
|
||||
pl=0
|
||||
fd.each do |line|
|
||||
c+=1
|
||||
if c%1000==0
|
||||
$stderr.print('.')
|
||||
cp=fd.pos
|
||||
if ( cp/pf > pl )
|
||||
pl=cp/pf
|
||||
$stderr.print(" #{pl*10}% ")
|
||||
end
|
||||
end
|
||||
line.chomp!
|
||||
n=line*50
|
||||
isbn,item=line.split('|')[0..1]
|
||||
sku="%s-%03d"%[isbn,item]
|
||||
db.put(sku,line,0)
|
||||
end
|
||||
}
|
||||
$stderr.print("\ntotal count: #{c}\n")
|
||||
|
||||
db2.close(0)
|
||||
db.close(0)
|
||||
env.close
|
||||
|
||||
exit
|
||||
|
||||
$stderr.puts(Rusage.get.inspect)
|
||||
$stderr.puts(`ps -up #{$$}`)
|
Loading…
Reference in a new issue