bdb_lib.c

Go to the documentation of this file.
00001 /*
00002  * $Id: bdb_lib.c 4585 2008-08-06 08:20:30Z klaus_darilion $
00003  *
00004  * db_berkeley module, portions of this code were templated using
00005  * the dbtext and postgres modules.
00006 
00007  * Copyright (C) 2007 Cisco Systems
00008  *
00009  * This file is part of SIP-router, a free SIP server.
00010  *
00011  * SIP-router is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version
00015  *
00016  * SIP-router is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License 
00022  * along with this program; if not, write to the Free Software 
00023  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00024  * 
00025  * History:
00026  * --------
00027  * 2007-09-19  genesis (wiquan)
00028  */
00029 
00038 #include <stdio.h>
00039 #include <string.h>
00040 #include <sys/types.h>
00041 #include <dirent.h>
00042 #include <errno.h>
00043 #include "../../ut.h"
00044 #include "../../mem/mem.h"
00045 #include "../../dprint.h"
00046 
00047 #include "bdb_fld.h"
00048 #include "bdb_lib.h"
00049 
00050 static bdb_params_p _bdb_parms = NULL;
00051 
00055 int bdblib_init(bdb_params_p _p) 
00056 {
00057         bdb_params_p dp = NULL;
00058         if (_bdb_parms != NULL)
00059                 return 0;
00060                 
00061                 /*create default parms*/
00062         dp = (bdb_params_p) pkg_malloc( sizeof(bdb_params_t) );
00063         if (dp==NULL) 
00064         {
00065                 ERR("not enough private memory\n");
00066                 return -1;
00067         }
00068                 
00069         if(_p!=NULL)
00070         {
00071                 dp->cache_size  = _p->cache_size;
00072                 dp->auto_reload = _p->auto_reload;
00073                 dp->log_enable  = _p->log_enable;
00074                 dp->journal_roll_interval = _p->journal_roll_interval;
00075         } else {
00076                 dp->cache_size = (4 * 1024 * 1024); //4Mb
00077                 dp->auto_reload = 0;
00078                 dp->log_enable = 0;
00079                 dp->journal_roll_interval = 3600;
00080         }
00081                 
00082         _bdb_parms = dp;
00083         return 0;
00084 }
00085 
00086 
00090 int bdblib_destroy(void)
00091 {
00092         if(_bdb_parms)  pkg_free(_bdb_parms);
00093         return 0;
00094 }
00095 
00096 
00101 int bdblib_close(bdb_db_p _db_p, str *dirpath)
00102 {
00103         int rc;
00104         bdb_tcache_p _tbc;
00105         DB* _db = NULL;
00106         DB_ENV* _env = NULL;
00107         
00108         if (_db_p==NULL || dirpath==NULL)
00109                 return -1;
00110         
00111         rc = 0;
00112         
00113         if (_db_p==NULL)
00114         {       
00115                 DBG("DB not found %.*s \n", dirpath->len, dirpath->s);
00116                 return 1; /*table not found*/
00117         }
00118         
00119         _env = _db_p->dbenv;
00120         _tbc = _db_p->tables;
00121         DBG("ENV %.*s \n", _db_p->name.len, _db_p->name.s);
00122         if(dirpath->len == _db_p->name.len && 
00123                 !strncasecmp(dirpath->s, _db_p->name.s, _db_p->name.len))
00124         {
00125                 //close the whole dbenv
00126                 DBG("ENV %.*s \n", dirpath->len, dirpath->s);
00127                 while(_tbc)
00128                 {
00129                         if(_tbc->dtp)
00130                         {
00131                                 _db = _tbc->dtp->db;
00132                                 if(_db)
00133                                         rc = _db->close(_db, 0);
00134                                 if(rc != 0)
00135                                         ERR("error closing %s\n", _tbc->dtp->name.s);
00136                                 _tbc->dtp->db = NULL;
00137                         }
00138                         _tbc = _tbc->next;
00139                 }
00140                 _env->close(_env, 0);
00141                 _db_p->dbenv = NULL;
00142                 return 0;
00143         }
00144                 
00145         //close a particular db
00146         while(_tbc)
00147         {
00148                 if(_tbc->dtp)
00149                 {
00150                         DBG("checking DB %.*s \n", _tbc->dtp->name.len, _tbc->dtp->name.s);
00151                                 
00152                         if(_tbc->dtp->name.len == dirpath->len && 
00153                                 !strncasecmp(_tbc->dtp->name.s, dirpath->s, dirpath->len ))
00154                         {
00155                                 DBG("DB %.*s \n", dirpath->len, dirpath->s);
00156                                 _db = _tbc->dtp->db;
00157                                 if(_db)
00158                                         rc = _db->close(_db, 0);
00159                                 if(rc != 0)
00160                                         ERR("error closing %s\n", _tbc->dtp->name.s);
00161                                 _tbc->dtp->db = NULL;
00162                                 return 0;
00163                         }
00164                 }
00165                 _tbc = _tbc->next;
00166         }
00167         DBG("DB not found %.*s \n", dirpath->len, dirpath->s);
00168         return 1; /*table not found*/   
00169 }
00170 
00175 int bdblib_reopen(bdb_db_p _db_p, str *dirpath)
00176 {
00177         int rc, flags;
00178         bdb_tcache_p _tbc;
00179         DB* _db = NULL;
00180         DB_ENV* _env = NULL;
00181         rc = flags = 0;
00182         _tbc = NULL;
00183         
00184         if (_db_p==NULL || dirpath==NULL)
00185                 return -1;
00186 
00187         
00188         if (_db_p)
00189         {
00190                 DBG("bdb: DB not found %.*s \n", dirpath->len, dirpath->s);
00191                 return 1; /*table not found*/
00192         }
00193         
00194         _env = _db_p->dbenv;
00195         _tbc = _db_p->tables;
00196                 
00197         if(dirpath->len ==_db_p->name.len && 
00198                 !strncasecmp(dirpath->s, _db_p->name.s, _db_p->name.len))
00199         {
00200                 //open the whole dbenv
00201                 DBG("-- bdblib_reopen ENV %.*s \n", dirpath->len, dirpath->s);
00202                 if(!_db_p->dbenv)
00203                 {
00204                         rc = bdblib_create_dbenv(&_env, dirpath->s);
00205                         _db_p->dbenv = _env;
00206                 }
00207                         
00208                 if(rc!=0) return rc;
00209 
00210                 _env = _db_p->dbenv;
00211                 _tbc = _db_p->tables;
00212 
00213                 while(_tbc)
00214                 {
00215                         if(_tbc->dtp)
00216                         {
00217                                 if(!_tbc->dtp->db)
00218                                 {
00219                                         if ((rc = db_create(&_db, _env, 0)) != 0)
00220                                         {
00221                                                 _env->err(_env, rc, "db_create");
00222                                                 ERR("error in db_create, db error: %s.\n",
00223                                                                 db_strerror(rc));
00224                                                 bdblib_recover(_tbc->dtp, rc);
00225                                         }
00226                                 }
00227                                         
00228                                 if ((rc = _db->open(_db, NULL, dirpath->s, NULL, DB_HASH,
00229                                                                 DB_CREATE, 0664)) != 0)
00230                                 {
00231                                         _db->dbenv->err(_env, rc, "DB->open: %s", dirpath->s);
00232                                         ERR("error in db_open: %s.\n",db_strerror(rc));
00233                                         bdblib_recover(_tbc->dtp, rc);
00234                                 }
00235                                         
00236                                 _tbc->dtp->db = _db;
00237                         }
00238                         _tbc = _tbc->next;
00239                 }
00240                 _env->close(_env, 0);
00241                 return rc;
00242         }
00243                 
00244         // open a particular db
00245         while(_tbc)
00246         {
00247                 if(_tbc->dtp)
00248                 {
00249                         ERR("checking DB %.*s \n", _tbc->dtp->name.len, _tbc->dtp->name.s);
00250                                 
00251                         if(_tbc->dtp->name.len == dirpath->len && 
00252                                 !strncasecmp(_tbc->dtp->name.s, dirpath->s, dirpath->len ))
00253                         {
00254                                 ERR("DB %.*s \n", dirpath->len, dirpath->s);
00255                                 if(!_tbc->dtp->db) 
00256                                 {
00257                                         if ((rc = db_create(&_db, _env, 0)) != 0)
00258                                         {
00259                                                 _env->err(_env, rc, "db_create");
00260                                                 ERR("error in db_create, db error: %s.\n",
00261                                                                 db_strerror(rc));
00262                                                 bdblib_recover(_tbc->dtp, rc);
00263                                         }
00264                                 }
00265                                         
00266                                 if ((rc = _db->open(_db, NULL, dirpath->s, NULL, DB_HASH,
00267                                                                 DB_CREATE, 0664)) != 0)
00268                                 {
00269                                         _db->dbenv->err(_env, rc, "DB->open: %s", dirpath->s);
00270                                         ERR("bdb open: %s.\n",db_strerror(rc));
00271                                         bdblib_recover(_tbc->dtp, rc);
00272                                 }
00273                                 _tbc->dtp->db = _db;
00274                                 return rc;
00275                         }
00276                 }
00277                 _tbc = _tbc->next;
00278         }
00279 
00280         DBG("DB not found %.*s \n", dirpath->len, dirpath->s);
00281         return 1; /*table not found*/
00282 }
00283 
00284 
00288 int bdblib_create_dbenv(DB_ENV **_dbenv, char* _home)
00289 {
00290         DB_ENV *env;
00291         char *progname;
00292         int rc, flags;
00293         
00294         progname = "openser";
00295         
00296         /* Create an environment and initialize it for additional error * reporting. */ 
00297         if ((rc = db_env_create(&env, 0)) != 0) 
00298         {
00299                 ERR("db_env_create failed! bdb error: %s.\n", db_strerror(rc)); 
00300                 return (rc);
00301         }
00302  
00303         env->set_errpfx(env, progname);
00304 
00305         /*  Specify the shared memory buffer pool cachesize */ 
00306         if ((rc = env->set_cachesize(env, 0, _bdb_parms->cache_size, 0)) != 0) 
00307         {
00308                 ERR("dbenv set_cachsize failed! bdb error: %s.\n", db_strerror(rc));
00309                 env->err(env, rc, "set_cachesize"); 
00310                 goto err; 
00311         }
00312 
00313         /* Concurrent Data Store flags */
00314         flags = DB_CREATE |
00315                 DB_INIT_CDB |
00316                 DB_INIT_MPOOL |
00317                 DB_THREAD;
00318         
00319         /* Transaction Data Store flags ; not supported yet */
00320         /*
00321         flags = DB_CREATE |
00322                 DB_RECOVER |
00323                 DB_INIT_LOG | 
00324                 DB_INIT_LOCK |
00325                 DB_INIT_MPOOL |
00326                 DB_THREAD |
00327                 DB_INIT_TXN;
00328         */
00329         
00330         /* Open the environment */ 
00331         if ((rc = env->open(env, _home, flags, 0)) != 0) 
00332         { 
00333                 ERR("dbenv is not initialized! bdb error: %s.\n",db_strerror(rc));
00334                 env->err(env, rc, "environment open: %s", _home); 
00335                 goto err; 
00336         }
00337         
00338         *_dbenv = env;
00339         return (0);
00340 
00341 err: (void)env->close(env, 0);
00342         return (rc);
00343 }
00344 
00345 
00348 bdb_db_p bdblib_get_db(str *dirpath)
00349 {
00350         int rc;
00351         bdb_db_p _db_p=NULL;
00352 
00353         if(dirpath==0 || dirpath->s==NULL || dirpath->s[0]=='\0')
00354                 return NULL;
00355 
00356         if(_bdb_parms==NULL)
00357         {
00358                 ERR("bdb: cache is not initialized! Check if you loaded bdb "
00359                         "before any other module that uses it.\n");
00360                 return NULL;
00361         }
00362 
00363         if(!bdb_is_database(dirpath->s))
00364         {       
00365                 ERR("bdb: database [%.*s] does not exists!\n",
00366                                 dirpath->len , dirpath->s);
00367                 return NULL;
00368         }
00369 
00370         _db_p = (bdb_db_p)pkg_malloc(sizeof(bdb_db_t));
00371         if(!_db_p)
00372         {
00373                 ERR("no private memory for dbenv_t.\n");
00374                 pkg_free(_db_p);
00375                 return NULL;
00376         }
00377 
00378         _db_p->name.s = (char*)pkg_malloc(dirpath->len*sizeof(char));
00379         memcpy(_db_p->name.s, dirpath->s, dirpath->len);
00380         _db_p->name.len = dirpath->len;
00381 
00382         if ((rc = bdblib_create_dbenv(&(_db_p->dbenv), dirpath->s)) != 0)
00383         {
00384                 ERR("bdblib_create_dbenv failed");
00385                 pkg_free(_db_p->name.s);
00386                 pkg_free(_db_p);
00387                 return NULL;
00388         }
00389 
00390         _db_p->tables=NULL;
00391 
00392         return _db_p;
00393 }
00394 
00395 
00400 bdb_tcache_p bdblib_get_table(bdb_db_t *_db, str *_s)
00401 {
00402         bdb_tcache_p _tbc = NULL;
00403         bdb_table_p _tp = NULL;
00404 
00405         if(!_db || !_s || !_s->s || _s->len<=0)
00406                 return NULL;
00407 
00408         if(!_db->dbenv)
00409         {
00410                 return NULL;
00411         }
00412 
00413         _tbc = _db->tables;
00414         while(_tbc)
00415         {
00416                 if(_tbc->dtp)
00417                 {
00418 
00419                         if(_tbc->dtp->name.len == _s->len 
00420                                 && !strncasecmp(_tbc->dtp->name.s, _s->s, _s->len ))
00421                         {
00422                                 return _tbc;
00423                         }
00424                 }
00425                 _tbc = _tbc->next;
00426         }
00427 
00428         _tbc = (bdb_tcache_p)pkg_malloc(sizeof(bdb_tcache_t));
00429         if(!_tbc)
00430                 return NULL;
00431 
00432         _tp = bdblib_create_table(_db, _s);
00433 
00434         if(!_tp)
00435         {
00436                 ERR("failed to create table.\n");
00437                 pkg_free(_tbc);
00438                 return NULL;
00439         }
00440 
00441         _tbc->dtp = _tp;
00442 
00443         if(_db->tables)
00444                 (_db->tables)->prev = _tbc;
00445         
00446         _tbc->next = _db->tables;
00447         _db->tables = _tbc;
00448 
00449         return _tbc;
00450 }
00451 
00452 
00453 void bdblib_log(int op, bdb_db_p _db_p, bdb_table_p _tp, char* _msg, int len)
00454 {
00455         if(!_tp || !len)                return;
00456         if(! _bdb_parms->log_enable)    return;
00457         if (_tp->logflags == JLOG_NONE) return;
00458         
00459         if ((_tp->logflags & op) == op)
00460         {       int op_len=7;
00461                 char buf[MAX_ROW_SIZE + op_len];
00462                 char *c;
00463                 time_t now = time(NULL);
00464                 
00465                 if( _bdb_parms->journal_roll_interval)
00466                 {
00467                         if((_tp->t) && (now - _tp->t) > _bdb_parms->journal_roll_interval)
00468                         {       /*try to roll logfile*/
00469                                 if(bdblib_create_journal(_db_p, _tp))
00470                                 {
00471                                         ERR("Journaling has FAILED !\n");
00472                                         return;
00473                                 }
00474                         }
00475                 }
00476                 
00477                 c = buf;
00478                 switch (op)
00479                 {
00480                 case JLOG_INSERT:
00481                         strncpy(c, "INSERT|", op_len);
00482                         break;
00483                 case JLOG_UPDATE:
00484                         strncpy(c, "UPDATE|", op_len);
00485                         break;
00486                 case JLOG_DELETE:
00487                         strncpy(c, "DELETE|", op_len);
00488                         break;
00489                 }
00490                 
00491                 c += op_len;
00492                 strncpy(c, _msg, len);
00493                 c +=len;
00494                 *c = '\n';
00495                 c++;
00496                 *c = '\0';
00497                 
00498                 if ((_tp->logflags & JLOG_STDOUT) == JLOG_STDOUT)
00499                         puts(buf);
00500                 
00501                 if ((_tp->logflags & JLOG_SYSLOG) == JLOG_SYSLOG)
00502                         syslog(LOG_LOCAL6, "%s", buf);
00503                 
00504                 if(_tp->fp) 
00505                 {
00506                         if(!fputs(buf, _tp->fp) )
00507                                 fflush(_tp->fp);
00508                 }
00509         }
00510 }
00511 
00529 bdb_table_p bdblib_create_table(bdb_db_p _db, str *_s)
00530 {
00531 
00532         int rc,i,flags;
00533         DB *bdb = NULL;
00534         bdb_table_p tp = NULL;
00535         char tblname[MAX_TABLENAME_SIZE]; 
00536 
00537         if(!_db || !_db->dbenv)
00538         {
00539                 ERR("no bdb_db_p or dbenv.\n");
00540                 return NULL;
00541         }
00542 
00543         tp = (bdb_table_p)pkg_malloc(sizeof(bdb_table_t));
00544         if(!tp)
00545         {
00546                 ERR("no private memory for bdb_table_t.\n");
00547                 return NULL;
00548         }
00549 
00550         if ((rc = db_create(&bdb, _db->dbenv, 0)) != 0)
00551         { 
00552                 _db->dbenv->err(_db->dbenv, rc, "database create");
00553                 ERR("error in db_create, bdb error: %s.\n",db_strerror(rc));
00554                 pkg_free(tp);
00555                 return NULL;
00556         }
00557 
00558         memset(tblname, 0, MAX_TABLENAME_SIZE);
00559         strncpy(tblname, _s->s, _s->len);
00560 
00561         flags = DB_THREAD;
00562 
00563         if ((rc = bdb->open(bdb, NULL, tblname, NULL, DB_HASH, flags, 0664)) != 0)
00564         { 
00565                 _db->dbenv->err(_db->dbenv, rc, "DB->open: %s", tblname);
00566                 ERR("bdb open failed: %s.\n",db_strerror(rc));
00567                 pkg_free(tp);
00568                 return NULL;
00569         }
00570 
00571         tp->name.s = (char*)pkg_malloc(_s->len*sizeof(char));
00572         memcpy(tp->name.s, _s->s, _s->len);
00573         tp->name.len = _s->len;
00574         tp->db=bdb;
00575         tp->ncols=0;
00576         tp->nkeys=0;
00577         tp->ro=0;    /*0=ReadWrite ; 1=ReadOnly*/
00578         tp->ino=0;   /*inode*/
00579         tp->logflags=0; /*bitmap; 4=Delete, 2=Update, 1=Insert, 0=None*/
00580         tp->fp=0;
00581         tp->t=0;
00582         
00583         for(i=0;i<MAX_NUM_COLS;i++)
00584                 tp->colp[i] = NULL;
00585 
00586         /*load metadata; seeded\db_loaded when database are created*/
00587         
00588         /*initialize columns with metadata*/
00589         rc = load_metadata_columns(tp);
00590         if(rc!=0)
00591         {
00592                 ERR("FAILED to load METADATA COLS in table: %s.\n", tblname);
00593                 goto error;
00594         }
00595         
00596         /*initialize columns default values from metadata*/
00597         rc = load_metadata_defaults(tp);
00598         if(rc!=0)
00599         {
00600                 ERR("FAILED to load METADATA DEFAULTS in table: %s.\n", tblname);
00601                 goto error;
00602         }
00603         
00604         rc = load_metadata_keys(tp);
00605         if(rc!=0)
00606         {
00607                 ERR("FAILED to load METADATA KEYS in table: %s.\n", tblname);
00608                 /*will have problems later figuring column types*/
00609                 goto error;
00610         }
00611 
00612         /*opened RW by default; Query to set the RO flag */
00613         rc = load_metadata_readonly(tp);
00614         if(rc!=0)
00615         {
00616                 INFO("No METADATA_READONLY in table: %s.\n", tblname);
00617                 /*non-critical; table will default to READWRITE*/
00618         }
00619 
00620         if(tp->ro)
00621         {       
00622                 /*schema defines this table RO readonly*/
00623                 
00624                 if ((rc = bdb->close(bdb,0)) != 0)
00625                 { 
00626                         _db->dbenv->err(_db->dbenv, rc, "DB->close: %s", tblname);
00627                         ERR("bdb close: %s.\n",db_strerror(rc));
00628                         goto error;
00629                 }
00630                 
00631                 bdb = NULL;
00632                 if ((rc = db_create(&bdb, _db->dbenv, 0)) != 0)
00633                 { 
00634                         _db->dbenv->err(_db->dbenv, rc, "database create");
00635                         ERR("error in db_create.\n");
00636                         goto error;
00637                 }
00638                 
00639                 flags = DB_THREAD | DB_RDONLY;
00640                 if ((rc = bdb->open(bdb, NULL, tblname, NULL, DB_HASH, flags, 0664)) != 0)
00641                 { 
00642                         _db->dbenv->err(_db->dbenv, rc, "DB->open: %s", tblname);
00643                         ERR("bdb open: %s.\n",db_strerror(rc));
00644                         goto error;
00645                 }
00646                 tp->db=bdb;
00647         }
00648         
00649         /* set the journaling flags; flags indicate which operations
00650            need to be journalled. (e.g possible to only journal INSERT.)
00651         */
00652         rc = load_metadata_logflags(tp);
00653         if(rc!=0)
00654                 INFO("No METADATA_LOGFLAGS in table: %s.\n", tblname);
00655         
00656         if ((tp->logflags & JLOG_FILE) == JLOG_FILE)
00657                 bdblib_create_journal(_db, tp);
00658         
00659         return tp;
00660         
00661 error:
00662         if(tp) 
00663         {
00664                 pkg_free(tp->name.s);
00665                 pkg_free(tp);
00666         }
00667         return NULL;
00668 }
00669 
00670 int bdblib_create_journal(bdb_db_p _db_p, bdb_table_p _tp)
00671 {
00672         char *s;
00673         char fn[1024];
00674         char d[64];
00675         FILE *fp = NULL;
00676         struct tm *t;
00677         int bl;
00678         time_t tim = time(NULL);
00679         
00680         if(! _db_p || ! _tp) return -1;
00681         if(! _bdb_parms->log_enable) return 0;
00682         /* journal filename ; e.g. '/var/kamailio/db/location-YYYYMMDDhhmmss.jnl' */
00683         s=fn;
00684         strncpy(s, _db_p->name.s, _db_p->name.len);
00685         s+=_db_p->name.len;
00686         
00687         *s = '/';
00688         s++;
00689         
00690         strncpy(s, _tp->name.s, _tp->name.len);
00691         s+=_tp->name.len;
00692         
00693         t = localtime( &tim );
00694         bl=strftime(d,128,"-%Y%m%d%H%M%S.jnl",t);
00695         strncpy(s, d, bl);
00696         s+= bl;
00697         *s = 0;
00698         
00699         if(_tp->fp)
00700         {       /* must be rolling. */
00701                 if( fclose(_tp->fp) )
00702                 {       ERR("Failed to Close Log in table: %.*s .\n", _tp->name.len,
00703                          _tp->name.s);
00704                         return -1;
00705                 }
00706         }
00707         
00708         if( (fp = fopen(fn, "w")) != NULL )
00709         {
00710                 _tp->fp = fp;
00711         }
00712         else
00713         {
00714                 ERR("Failed to Open Log in table: %.*s .\n",_tp->name.len, _tp->name.s);
00715                 return -1;
00716         }
00717         
00718         _tp->t = tim;
00719         return 0;
00720 
00721 }
00722 
00723 int load_metadata_columns(bdb_table_p _tp)
00724 {
00725         int ret,n,len;
00726         char dbuf[MAX_ROW_SIZE];
00727         char *s = NULL;
00728         char cn[64], ct[16];
00729         DB *db = NULL;
00730         DBT key, data;
00731         bdb_col_p col;
00732         ret = n = len = 0;
00733         
00734         if(!_tp || !_tp->db)
00735                 return -1;
00736         
00737         if(_tp->ncols!=0)
00738                 return 0;
00739         
00740         db = _tp->db;
00741         memset(&key, 0, sizeof(DBT));
00742         memset(&data, 0, sizeof(DBT));
00743         memset(dbuf, 0, MAX_ROW_SIZE);
00744 
00745         key.data = METADATA_COLUMNS;
00746         key.size = strlen(METADATA_COLUMNS);
00747 
00748         /*memory for the result*/
00749         data.data = dbuf;
00750         data.ulen = MAX_ROW_SIZE;
00751         data.flags = DB_DBT_USERMEM;
00752         
00753         if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
00754         {
00755                 db->err(db, ret, "load_metadata_columns DB->get failed");
00756                 ERR("FAILED to find METADATA_COLUMNS in DB \n");
00757                 return -1;
00758         }
00759 
00760         /* eg: dbuf = "bdb_table_name(str) bdb_table_version(int)" */
00761         s = strtok(dbuf, " ");
00762         while(s!=NULL && n<MAX_NUM_COLS) 
00763         {
00764                 /* eg: meta[0]=table_name  meta[1]=str */
00765                 sscanf(s,"%20[^(](%10[^)])[^\n]", cn, ct);
00766                 
00767                 /* create column*/
00768                 col = (bdb_col_p) pkg_malloc(sizeof(bdb_col_t));
00769                 if(!col)
00770                 {       ERR("out of private memory \n");
00771                         return -1;
00772                 }
00773                 
00774                 /* set name*/
00775                 len = strlen( cn );
00776                 col->name.s = (char*)pkg_malloc(len * sizeof(char));
00777                 memcpy(col->name.s, cn, len );
00778                 col->name.len = len;
00779                 
00780                 /*set column type*/
00781                 if(strncmp(ct, "str", 3)==0)
00782                 {       col->type = DB_STR;
00783                 }
00784                 else if(strncmp(ct, "int", 3)==0)
00785                 {       col->type = DB_INT;
00786                 }
00787                 else if(strncmp(ct, "double", 6)==0)
00788                 {       col->type = DB_DOUBLE;
00789                 }
00790                 else if(strncmp(ct, "datetime", 8)==0)
00791                 {       col->type = DB_DATETIME;
00792                 }
00793                 else
00794                 {       col->type = DB_STR;
00795                 }
00796                 
00797                 col->flag = 0;
00798                 _tp->colp[n] = col;
00799                 n++;
00800                 _tp->ncols++;
00801                 s=strtok(NULL, " ");
00802         }
00803 
00804         return 0;
00805 }
00806 
00807 int load_metadata_keys(bdb_table_p _tp)
00808 {
00809         int ret,n,ci;
00810         char dbuf[MAX_ROW_SIZE];
00811         char *s = NULL;
00812         DB *db = NULL;
00813         DBT key, data;
00814         ret = n = ci = 0;
00815         
00816         if(!_tp || !_tp->db)
00817                 return -1;
00818         
00819         db = _tp->db;
00820         memset(&key, 0, sizeof(DBT));
00821         memset(&data, 0, sizeof(DBT));
00822         memset(dbuf, 0, MAX_ROW_SIZE);
00823         key.data = METADATA_KEY;
00824         key.size = strlen(METADATA_KEY);
00825         data.data = dbuf;
00826         data.ulen = MAX_ROW_SIZE;
00827         data.flags = DB_DBT_USERMEM;
00828         
00829         if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
00830         {
00831                 db->err(db, ret, "load_metadata_keys DB->get failed");
00832                 ERR("FAILED to find METADATA in table \n");
00833                 return ret;
00834         }
00835         
00836         s = strtok(dbuf, " ");
00837         while(s!=NULL && n< _tp->ncols) 
00838         {       ret = sscanf(s,"%i", &ci);
00839                 if(ret != 1) return -1;
00840                 if( _tp->colp[ci] ) 
00841                 {       _tp->colp[ci]->flag = 1;
00842                         _tp->nkeys++;
00843                 }
00844                 n++;
00845                 s=strtok(NULL, " ");
00846         }
00847 
00848         return 0;
00849 }
00850 
00851 
00852 int load_metadata_readonly(bdb_table_p _tp)
00853 {
00854         int i, ret;
00855         char dbuf[MAX_ROW_SIZE];
00856 
00857         DB *db = NULL;
00858         DBT key, data;
00859         i = 0;
00860         
00861         if(!_tp || !_tp->db)
00862                 return -1;
00863         
00864         db = _tp->db;
00865         memset(&key, 0, sizeof(DBT));
00866         memset(&data, 0, sizeof(DBT));
00867         memset(dbuf, 0, MAX_ROW_SIZE);
00868         key.data = METADATA_READONLY;
00869         key.size = strlen(METADATA_READONLY);
00870         data.data = dbuf;
00871         data.ulen = MAX_ROW_SIZE;
00872         data.flags = DB_DBT_USERMEM;
00873         
00874         if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
00875         {       return ret;
00876         }
00877         
00878         if( 1 == sscanf(dbuf,"%i", &i) )
00879                 _tp->ro=(i>0)?1:0;
00880         
00881         return 0;
00882 }
00883 
00884 int load_metadata_logflags(bdb_table_p _tp)
00885 {
00886         int i, ret;
00887         char dbuf[MAX_ROW_SIZE];
00888 
00889         DB *db = NULL;
00890         DBT key, data;
00891         i = 0;
00892         
00893         if(!_tp || !_tp->db)
00894                 return -1;
00895         
00896         db = _tp->db;
00897         memset(&key, 0, sizeof(DBT));
00898         memset(&data, 0, sizeof(DBT));
00899         memset(dbuf, 0, MAX_ROW_SIZE);
00900         key.data = METADATA_LOGFLAGS;
00901         key.size = strlen(METADATA_LOGFLAGS);
00902         data.data = dbuf;
00903         data.ulen = MAX_ROW_SIZE;
00904         data.flags = DB_DBT_USERMEM;
00905         
00906         if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
00907         {       return ret;
00908         }
00909         
00910         if( 1 == sscanf(dbuf,"%i", &i) )
00911                 _tp->logflags=i;
00912         
00913         return 0;
00914 }
00915 
00916 int load_metadata_defaults(bdb_table_p _tp)
00917 {
00918         int ret,n,len;
00919         char dbuf[MAX_ROW_SIZE];
00920         char *s = NULL;
00921         char cv[64];
00922         DB *db = NULL;
00923         DBT key, data;
00924         bdb_col_p col;
00925         ret = n = len = 0;
00926         
00927         if(!_tp || !_tp->db)
00928                 return -1;
00929         
00930         db = _tp->db;
00931         memset(&key, 0, sizeof(DBT));
00932         memset(&data, 0, sizeof(DBT));
00933         memset(dbuf, 0, MAX_ROW_SIZE);
00934 
00935         key.data = METADATA_DEFAULTS;
00936         key.size = strlen(METADATA_DEFAULTS);
00937 
00938         /*memory for the result*/
00939         data.data = dbuf;
00940         data.ulen = MAX_ROW_SIZE;
00941         data.flags = DB_DBT_USERMEM;
00942         
00943         if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
00944         {
00945                 /*no defaults in DB; make some up.*/
00946                 for(n=0; n<_tp->ncols; n++)
00947                 {
00948                         col = _tp->colp[n];
00949                         if( col ) 
00950                         {       /*set all columns default value to 'NULL' */
00951                                 len = strlen("NULL");
00952                                 col->dv.s = (char*)pkg_malloc(len * sizeof(char));
00953                                 memcpy(col->dv.s, "NULL", len);
00954                                 col->dv.len = len;
00955                         }
00956                 }
00957                 return 0;
00958         }
00959         
00960         /* use the defaults provided*/
00961         s = strtok(dbuf, DELIM);
00962         while(s!=NULL && n< _tp->ncols) 
00963         {       ret = sscanf(s,"%s", cv);
00964                 if(ret != 1) return -1;
00965                 col = _tp->colp[n];
00966                 if( col ) 
00967                 {       /*set column default*/
00968                         len = strlen(s);
00969                         col->dv.s = (char*)pkg_malloc(len * sizeof(char));
00970                         memcpy(col->dv.s, cv, len);
00971                         col->dv.len = len;
00972                 }
00973                 n++;
00974                 s=strtok(NULL, DELIM);
00975         }
00976         
00977         return 0;
00978 }
00979 
00980 inline int bdb_int2str(int _v, char* _s, int* _l)
00981 {
00982         int ret;
00983 
00984         if ((!_s) || (!_l) || (!*_l)) {
00985                 ERR("Invalid parameter value\n");
00986                 return -1;
00987         }
00988 
00989         ret = snprintf(_s, *_l, "%-d", _v);
00990         if (ret < 0 || ret >= *_l) {
00991                 ERR("Error in snprintf\n");
00992                 return -1;
00993         }
00994         *_l = ret;
00995 
00996         return 0;
00997 }
00998 
00999 inline int bdb_double2str(double _v, char* _s, int* _l)
01000 {
01001         int ret;
01002 
01003         if ((!_s) || (!_l) || (!*_l)) {
01004                 ERR("Invalid parameter value\n");
01005                 return -1;
01006         }
01007 
01008         ret = snprintf(_s, *_l, "%-10.2f", _v);
01009         if (ret < 0 || ret >= *_l) {
01010                 ERR("Error in snprintf\n");
01011                 return -1;
01012         }
01013         *_l = ret;
01014 
01015         return 0;
01016 }
01017 
01018 inline int bdb_time2str(time_t _v, char* _s, int* _l)
01019 {
01020         struct tm* t;
01021         int l;
01022 
01023         if ((!_s) || (!_l) || (*_l < 2)) {
01024                 ERR("Invalid parameter value\n");
01025                 return -1;
01026         }
01027 
01028         *_s++ = '\'';
01029 
01030         /* Convert time_t structure to format accepted by the database */
01031         t = localtime(&_v);
01032         l = strftime(_s, *_l -1, "%Y-%m-%d %H:%M:%S", t);
01033 
01034         if (l == 0) {
01035                 ERR("Error during time conversion\n");
01036                 /* the value of _s is now unspecified */
01037                 _s = NULL;
01038                 _l = 0;
01039                 return -1;
01040         }
01041         *_l = l;
01042 
01043         *(_s + l) = '\'';
01044         *_l = l + 2;
01045         return 0;
01046 }
01047 
01048 /*
01049  * Used when converting result from a query
01050  */
01051 int bdb_val2str(db_fld_t *fld, char *sout, int *slen)
01052 {
01053         int l;
01054         db_fld_val_t *val;
01055 
01056         if (fld->flags&DB_NULL) 
01057         {
01058                 *slen = snprintf(sout, *slen, "NULL");
01059                 return 0;
01060         }
01061         
01062         val = &(fld->v);
01063         switch(fld->type)
01064         {
01065                 case DB_INT:
01066                         if (bdb_int2str(val->int4, sout, slen) < 0) {
01067                                 ERR("Error while converting int to string\n");
01068                                 return -2;
01069                         } else {
01070                                 DBG("Converted int to string\n");
01071                                 return 0;
01072                         }
01073                 break;
01074 
01075                 case DB_BITMAP:
01076                         if (bdb_int2str(val->bitmap, sout, slen) < 0) {
01077                                 ERR("Error while converting bitmap to string\n");
01078                                 return -3;
01079                         } else {
01080                                 DBG("Converted bitmap to string\n");
01081                                 return 0;
01082                         }
01083                 break;
01084 
01085                 case DB_DOUBLE:
01086                         if (bdb_double2str(val->dbl, sout, slen) < 0) {
01087                                 ERR("Error while converting double  to string\n");
01088                                 return -3;
01089                         } else {
01090                                 DBG("Converted double to string\n");
01091                                 return 0;
01092                         }
01093                 break;
01094 
01095                 case DB_CSTR:
01096                         l = strlen(val->cstr);
01097                         if (*slen < l ) 
01098                         {
01099                                 ERR("Destination buffer too short for string\n");
01100                                 return -4;
01101                         } else {
01102                                 DBG("Converted string to string\n");
01103                                 strncpy(sout, val->cstr , l);
01104                                 sout[l] = 0;
01105                                 *slen = l;
01106                                 return 0;
01107                         }
01108                 break;
01109 
01110                 case DB_STR:
01111                         l = val->lstr.len;
01112                         if (*slen < l) 
01113                         {
01114                                 ERR("Destination buffer too short for str\n");
01115                                 return -5;
01116                         } else {
01117                                 DBG("Converted str to string\n");
01118                                 strncpy(sout, val->lstr.s , val->lstr.len);
01119                                 *slen = val->lstr.len;
01120                                 return 0;
01121                         }
01122                 break;
01123 
01124                 case DB_DATETIME:
01125                         if (bdb_time2str(val->time, sout, slen) < 0) {
01126                                 ERR("Error while converting time_t to string\n");
01127                                 return -6;
01128                         } else {
01129                                 DBG("Converted time_t to string\n");
01130                                 return 0;
01131                         }
01132                 break;
01133 
01134                 case DB_BLOB:
01135                         l = val->blob.len;
01136                         if (*slen < l) 
01137                         {
01138                                 ERR("Destination buffer too short for blob\n");
01139                                 return -7;
01140                         } else {
01141                                 DBG("Converting BLOB [%s]\n", sout);
01142                                 memcpy(sout, val->blob.s , val->blob.len);
01143                                 *slen = l;
01144                                 return 0;
01145                         }
01146                 break;
01147 
01148                 default:
01149                         DBG("Unknown data type\n");
01150                         return -8;
01151         }
01152 }
01153 
01154 /*creates a composite key _k of length _klen from n values of _v;
01155   provide your own initialized memory for target _k and _klen;
01156   resulting value: _k = "KEY1 | KEY2"
01157   ko = key only
01158 */
01159 
01160 int bdblib_valtochar(bdb_table_p tp, db_fld_t *fld, int fld_count, char *kout,
01161                 int *klen, int ktype)
01162 {
01163         char *p; 
01164         static char sk[MAX_ROW_SIZE]; // subkey(sk) val
01165         char* delim = DELIM;
01166         char* cNULL = "NULL";
01167         int  len, total, sum;
01168         int i, j, k;
01169         bdb_fld_t *f;
01170 
01171         p =  kout;
01172         len = sum = total = 0;
01173         i = j = k = 0;
01174         
01175         if(tp==NULL) return -1;
01176         if(fld==NULL || fld_count<1) return -1;
01177         if(kout==NULL || klen==NULL ) return -1;
01178         if( *klen < 1)    return -1;
01179         
01180         memset(sk, 0, MAX_ROW_SIZE);
01181         total = *klen;
01182         *klen = 0; //sum
01183         
01184         /*
01185           schema has specified keys
01186           verify all schema keys are provided
01187           use 'NULL' for those that are missing.
01188         */
01189         for(i=0; i<tp->ncols; i++)
01190         {       /* i indexes columns in schema order */
01191                 if(ktype)
01192                 {       /* keymode; skip over non-key columns */
01193                         if(tp->colp[i]->flag==0) 
01194                                 continue; 
01195                 }
01196                 
01197                 for(j=0; j<fld_count; j++)
01198                 {
01199                         f = DB_GET_PAYLOAD(fld + j);
01200                         /*
01201                           j indexes the columns provided in _k
01202                           which may be less than the total required by
01203                           the schema. the app does not know the order
01204                           of the columns in our schema!
01205                          */
01206                         k = f->col_pos;
01207                         
01208                         /*
01209                          * k index will remap back to our schema order; like i
01210                          */
01211                         if(i == k)
01212                         {
01213                                 /*
01214                                  KEY was provided; append to buffer;
01215                                  _k[j] contains a key, but its a key that 
01216                                  corresponds to column k of our schema.
01217                                  now we know its a match, and we dont need
01218                                  index k for anything else
01219                                 */
01220                                 len = total - sum;
01221                                 if ( bdb_val2str((fld+j), sk, &len) != 0)
01222                                 {
01223                                         ERR("Destination buffer too short for subval %s\n",sk);
01224                                         return -4;
01225                                 }
01226                                 
01227                                 sum += len;
01228                                 if(sum > total)
01229                                 {
01230                                         ERR("Destination buffer too short for subval %s\n",sk);
01231                                         return -5;
01232                                 }
01233 
01234                                 strncpy(p, sk, len);
01235                                 p += len;
01236                                 *klen = sum;
01237 
01238                                 sum += DELIM_LEN;
01239                                 if(sum > total)
01240                                 {
01241                                         ERR("Destination buffer too short for delim \n");
01242                                         return -5;
01243                                 } 
01244                                 
01245                                 /* append delim */
01246                                 strncpy(p, delim, DELIM_LEN);
01247                                 p += DELIM_LEN;
01248                                 *klen = sum;
01249                                 
01250                                 
01251                                 /* take us out of inner for loop
01252                                    and at the end of the outer loop
01253                                    to look for our next schema key
01254                                 */
01255                                 goto next;
01256                         }
01257                 }
01258 
01259                 /*
01260                  NO KEY provided; use the column default value (dv)
01261                      i.e _tp->colp[i]->dv
01262                 */
01263                 len = tp->colp[i]->dv.len;
01264                 sum += len;
01265                 if(sum > total)
01266                 {
01267                         ERR("Destination buffer too short for subval %s\n",cNULL);
01268                         return -5;
01269                 }
01270                 
01271                 strncpy(p, tp->colp[i]->dv.s, len);
01272                 p += len;
01273                 *klen = sum;
01274                 
01275                 sum += DELIM_LEN;
01276                 if(sum > total)
01277                 {
01278                         ERR("Destination buffer too short for delim \n");
01279                         return -5;
01280                 } 
01281                 
01282                 strncpy(p, delim, DELIM_LEN);
01283                 p += DELIM_LEN;
01284                 *klen = sum;
01285 next:
01286                 continue;
01287         }
01288 
01289         return 0;
01290 }
01291 
01292 
01296 int bdb_db_free(bdb_db_p _dbp)
01297 {
01298         bdb_tcache_p _tbc = NULL, _tbc0=NULL;
01299         if(!_dbp)
01300                 return -1;
01301 
01302         _tbc = _dbp->tables;
01303 
01304         while(_tbc)
01305         {
01306                 _tbc0 = _tbc->next;
01307                 bdb_tcache_free(_tbc);
01308                 _tbc = _tbc0;
01309         }
01310         
01311         if(_dbp->dbenv)
01312                 _dbp->dbenv->close(_dbp->dbenv, 0);
01313         
01314         if(_dbp->name.s)
01315                 pkg_free(_dbp->name.s);
01316         
01317         pkg_free(_dbp);
01318 
01319         return 0;
01320 }
01321 
01322 
01326 int bdb_tcache_free(bdb_tcache_p _tbc)
01327 {
01328         if(!_tbc)
01329                 return -1;
01330         
01331         /*while ??!? */ 
01332         if(_tbc->dtp)
01333                 bdb_table_free(_tbc->dtp);
01334         
01335         pkg_free(_tbc);
01336 
01337         return 0;
01338 }
01339 
01340 
01344 int bdb_table_free(bdb_table_p _tp)
01345 {       int i;
01346         if(!_tp)
01347                 return -1;
01348 
01349         if(_tp->db)
01350                 _tp->db->close(_tp->db, 0);
01351         
01352         if(_tp->fp)
01353                 fclose(_tp->fp);
01354 
01355         if(_tp->name.s)
01356                 pkg_free(_tp->name.s);
01357         
01358         for(i=0;i<_tp->ncols;i++)
01359         {       if(_tp->colp[i])
01360                 {       pkg_free(_tp->colp[i]->name.s);
01361                         pkg_free(_tp->colp[i]->dv.s);
01362                         pkg_free(_tp->colp[i]);
01363                 }
01364         }
01365 
01366         pkg_free(_tp);
01367         return 0;
01368 }
01369 
01370 int bdblib_recover(bdb_table_p _tp, int _rc)
01371 {
01372         switch(_rc)
01373         {
01374                 case DB_LOCK_DEADLOCK:
01375                 ERR("DB_LOCK_DEADLOCK detected !!\n");
01376                 
01377                 case DB_RUNRECOVERY:
01378                 ERR("DB_RUNRECOVERY detected !! \n");
01379                 bdblib_destroy();
01380                 exit(1);
01381                 break;
01382         }
01383         
01384         return 0;
01385 }
01386 
01390 int bdb_is_database(char *dirpath)
01391 {
01392         DIR *dirp = NULL;
01393         
01394         if(dirpath==NULL || dirpath[0]=='\0')
01395                 return 0;
01396         dirp = opendir(dirpath);
01397         if(dirp==NULL)
01398                 return 0;
01399         closedir(dirp);
01400 
01401         return 1;
01402 }
01403 
01404 int bdb_get_colpos(bdb_table_t *tp, char *name)
01405 {
01406         str s;
01407         int i;
01408 
01409         if(tp==NULL || name==NULL)
01410         {
01411                 ERR("bdb: bad parameters\n");
01412                 return -1;
01413         }
01414 
01415         s.s = name;
01416         s.len = strlen(name);
01417         for(i=0; i<tp->ncols; i++) {
01418                 if(tp->colp[i]->name.len == s.len
01419                                 && !strncasecmp(s.s, tp->colp[i]->name.s, s.len))
01420                         return i;
01421         }
01422         return -1;
01423 }
01424 
01425 int bdb_str2time(char *s, time_t *v)
01426 {
01427         struct tm time;
01428 
01429         if ((!s) || (!v)) {
01430                 ERR("bdb:invalid parameter value\n");
01431                 return -1;
01432         }
01433 
01434         memset(&time, '\0', sizeof(struct tm));
01435         //if (strptime(s, "%Y-%m-%d %H:%M:%S", &time) == NULL) {
01436         //      ERR("Error during time conversion\n");
01437         //      return -1;
01438         //}
01439 
01440         time.tm_isdst = -1;
01441         *v = mktime(&time);
01442 
01443         return 0;
01444 }
01445 
01446 int bdb_str2double(char *s, double *v)
01447 {
01448         if ((!s) || (!v)) {
01449                 ERR("Invalid parameter value\n");
01450                 return -1;
01451         }
01452 
01453         *v = atof(s);
01454         return 0;
01455 }
01456 
01457 int bdb_str2int(char *s, int *v)
01458 {
01459         long tmp;
01460 
01461         if (!s || !v) {
01462                 ERR("Invalid parameter value\n");
01463                 return -1;
01464         }
01465 
01466         tmp = strtoul(s, 0, 10);
01467         if ((tmp == ULONG_MAX && errno == ERANGE) || 
01468             (tmp < INT_MIN) || (tmp > UINT_MAX)) {
01469                 ERR("Value out of range\n");
01470                 return -1;
01471         }
01472 
01473         *v = (int)tmp;
01474         return 0;
01475 }