km_db_berkeley.c

Go to the documentation of this file.
00001 /*
00002  * $Id$
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 
00037 #include <stdio.h>
00038 #include <unistd.h>
00039 #include <sys/stat.h>
00040 
00041 
00042 #include "../../str.h"
00043 #include "../../ut.h"
00044 #include "../../mem/mem.h"
00045 
00046 #include "../../sr_module.h"
00047 #include "../../lib/srdb1/db_res.h"
00048 #include "../../lib/srdb1/db.h"
00049 #include "../../lib/srdb1/db_query.h"
00050 #include "km_db_berkeley.h"
00051 #include "km_bdb_lib.h"
00052 #include "km_bdb_res.h"
00053 #include "km_bdb_mi.h"
00054 #include "bdb_mod.h"
00055 #include "bdb_crs_compat.h"
00056 
00057 #ifndef CFG_DIR
00058 #define CFG_DIR "/tmp"
00059 #endif
00060 
00061 #define BDB_ID          "berkeley://"
00062 #define BDB_ID_LEN      (sizeof(BDB_ID)-1)
00063 #define BDB_PATH_LEN    256
00064 
00065 #define BDB_KEY   1
00066 #define BDB_VALUE 0
00067 
00068 /*MODULE_VERSION*/
00069 
00070 int bdb_bind_api(db_func_t *dbb);
00071 
00072 /*
00073  * Exported functions
00074  */
00075 static kam_cmd_export_t cmds[] = {
00076         {"db_bind_api",    (cmd_function)bdb_bind_api,   0, 0, 0, 0},
00077         {0, 0, 0, 0, 0, 0}
00078 };
00079 
00080 
00081 /*
00082  * Exported parameters
00083  */
00084 static param_export_t params[] = {
00085         {"auto_reload",        INT_PARAM, &auto_reload },
00086         {"log_enable",         INT_PARAM, &log_enable  },
00087         {"journal_roll_interval", INT_PARAM, &journal_roll_interval  },
00088         {0, 0, 0}
00089 };
00090 
00091 /*
00092  * Exported MI functions
00093  */
00094 static mi_export_t mi_cmds[] = {
00095         { MI_BDB_RELOAD, mi_bdb_reload, 0, 0, 0 },
00096         { 0, 0, 0, 0, 0}
00097 };
00098 
00099 struct kam_module_exports kam_exports = {       
00100         "db_berkeley",
00101         DEFAULT_DLFLAGS, /* dlopen flags */
00102         cmds,     /* Exported functions */
00103         params,   /* Exported parameters */
00104         0,        /* exported statistics */
00105         mi_cmds,  /* exported MI functions */
00106         0,        /* exported pseudo-variables */
00107         0,        /* extra processes */
00108         km_mod_init, /* module initialization function */
00109         0,        /* response function*/
00110         km_destroy,  /* destroy function */
00111         0         /* per-child init function */
00112 };
00113 
00114 int km_mod_init(void)
00115 {
00116         db_parms_t p;
00117         
00118         if(register_mi_mod(kam_exports.name, mi_cmds)!=0)
00119         {
00120                 LM_ERR("failed to register MI commands\n");
00121                 return -1;
00122         }
00123 
00124         p.auto_reload = auto_reload;
00125         p.log_enable = log_enable;
00126         p.cache_size  = (4 * 1024 * 1024); //4Mb
00127         p.journal_roll_interval = journal_roll_interval;
00128         
00129         if(km_bdblib_init(&p))
00130                 return -1;
00131 
00132         return 0;
00133 }
00134 
00135 void km_destroy(void)
00136 {
00137         km_bdblib_destroy();
00138 }
00139 
00140 int bdb_bind_api(db_func_t *dbb)
00141 {
00142         if(dbb==NULL)
00143                 return -1;
00144 
00145         memset(dbb, 0, sizeof(db_func_t));
00146 
00147         dbb->use_table   = bdb_use_table;
00148         dbb->init        = bdb_init;
00149         dbb->close       = bdb_close;
00150         dbb->query       = (db_query_f)km_bdb_query;
00151         dbb->free_result = bdb_free_query;
00152         dbb->insert      = (db_insert_f)bdb_insert;
00153         dbb->delete      = (db_delete_f)bdb_delete; 
00154         dbb->update      = (db_update_f)bdb_update;
00155 
00156         return 0;
00157 }
00158 
00159 int bdb_use_table(db1_con_t* _h, const str* _t)
00160 {
00161         return db_use_table(_h, _t);
00162 }
00163 
00164 /*
00165  * Initialize database connection
00166  */
00167 db1_con_t* bdb_init(const str* _sqlurl)
00168 {
00169         db1_con_t* _res;
00170         str _s;
00171         char bdb_path[BDB_PATH_LEN];
00172         
00173         if (!_sqlurl || !_sqlurl->s) {
00174                 LM_ERR("invalid parameter value\n");
00175                 return 0;
00176         }
00177         
00178         _s.s = _sqlurl->s;
00179         _s.len = _sqlurl->len;
00180         if(_s.len <= BDB_ID_LEN || strncmp(_s.s, BDB_ID, BDB_ID_LEN)!=0)
00181         {
00182                 LM_ERR("invalid database URL - should be:"
00183                         " <%s[/]path/to/directory>\n", BDB_ID);
00184                 return NULL;
00185         }
00186         _s.s   += BDB_ID_LEN;
00187         _s.len -= BDB_ID_LEN;
00188         
00189         if(_s.s[0]!='/')
00190         {
00191                 if(sizeof(CFG_DIR)+_s.len+2 > BDB_PATH_LEN)
00192                 {
00193                         LM_ERR("path to database is too long\n");
00194                         return NULL;
00195                 }
00196                 strcpy(bdb_path, CFG_DIR);
00197                 bdb_path[sizeof(CFG_DIR)] = '/';
00198                 strncpy(&bdb_path[sizeof(CFG_DIR)+1], _s.s, _s.len);
00199                 _s.len += sizeof(CFG_DIR);
00200                 _s.s = bdb_path;
00201         }
00202         
00203         _res = pkg_malloc(sizeof(db1_con_t)+sizeof(bdb_con_t));
00204         if (!_res)
00205         {
00206                 LM_ERR("No private memory left\n");
00207                 return NULL;
00208         }
00209         memset(_res, 0, sizeof(db1_con_t) + sizeof(bdb_con_t));
00210         _res->tail = (unsigned long)((char*)_res+sizeof(db1_con_t));
00211 
00212         LM_INFO("using database at: %.*s", _s.len, _s.s);
00213         BDB_CON_CONNECTION(_res) = km_bdblib_get_db(&_s);
00214         if (!BDB_CON_CONNECTION(_res))
00215         {
00216                 LM_ERR("cannot get the link to database\n");
00217                 return NULL;
00218         }
00219 
00220     return _res;
00221 }
00222 
00223 
00224 /*
00225  * Close a database connection
00226  */
00227 void bdb_close(db1_con_t* _h)
00228 {
00229         if(BDB_CON_RESULT(_h))
00230                 db_free_result(BDB_CON_RESULT(_h));
00231         pkg_free(_h);
00232 }
00233 
00234 /* 
00235  * n can be the dbenv path or a table name
00236 */
00237 int bdb_reload(char* _n)
00238 {
00239         int rc = 0;
00240 #ifdef BDB_EXTRA_DEBUG
00241         LM_DBG("[bdb_reload] Initiate RELOAD in %s\n", _n);
00242 #endif
00243 
00244         if ((rc = km_bdblib_close(_n)) != 0) 
00245         {       LM_ERR("[bdb_reload] Error while closing db_berkeley DB.\n");
00246                 return rc;
00247         }
00248 
00249         if ((rc = km_bdblib_reopen(_n)) != 0) 
00250         {       LM_ERR("[bdb_reload] Error while reopening db_berkeley DB.\n");
00251                 return rc;
00252         }
00253 
00254 #ifdef BDB_EXTRA_DEBUG
00255         LM_DBG("[bdb_reload] RELOAD successful in %s\n", _n);
00256 #endif
00257 
00258         return rc;
00259 }
00260 
00261 /*
00262  * Attempts to reload a Berkeley database; reloads when the inode changes
00263  */
00264 void bdb_check_reload(db1_con_t* _con)
00265 {
00266         str s;
00267         char* p;
00268         int rc, len;
00269         struct stat st;
00270         database_p db;
00271         char n[MAX_ROW_SIZE];
00272         char t[MAX_TABLENAME_SIZE];
00273         table_p tp = NULL;
00274         tbl_cache_p tbc = NULL;
00275         
00276         p=n;
00277         rc = len = 0;
00278         
00279         /*get dbenv name*/
00280         db = BDB_CON_CONNECTION(_con);
00281         if(!db->dbenv)  return;
00282         s.s = db->name.s;
00283         s.len = db->name.len;
00284         len+=s.len;
00285         
00286         if(len > MAX_ROW_SIZE)
00287         {       LM_ERR("dbenv name too long \n");
00288                 return;
00289         }
00290         
00291         strncpy(p, s.s, s.len);
00292         p+=s.len;
00293         
00294         len++;
00295         if(len > MAX_ROW_SIZE)
00296         {       LM_ERR("dbenv name too long \n");
00297                 return;
00298         }
00299         
00300         /*append slash */
00301         *p = '/';
00302         p++;
00303         
00304         /*get table name*/
00305         s.s = CON_TABLE(_con)->s;
00306         s.len = CON_TABLE(_con)->len;
00307         len+=s.len;
00308         
00309         if((len>MAX_ROW_SIZE) || (s.len > MAX_TABLENAME_SIZE) )
00310         {       LM_ERR("table name too long \n");
00311                 return;
00312         }
00313 
00314         strncpy(t, s.s, s.len);
00315         t[s.len] = 0;
00316         
00317         strncpy(p, s.s, s.len);
00318         p+=s.len;
00319         *p=0;
00320         
00321         if( (tbc = km_bdblib_get_table(db, &s)) == NULL)
00322                 return;
00323         
00324         if( (tp = tbc->dtp) == NULL)
00325                 return;
00326         
00327         LM_DBG("stat file [%.*s]\n", len, n);
00328         rc = stat(n, &st);
00329         if(!rc)
00330         {       if((tp->ino!=0) && (st.st_ino != tp->ino))
00331                         bdb_reload(t); /*file changed on disk*/
00332                 
00333                 tp->ino = st.st_ino;
00334         }
00335 
00336 }
00337 
00338 
00339 /*
00340  * Free all memory allocated by get_result
00341  */
00342 int bdb_free_query(db1_con_t* _h, db1_res_t* _r)
00343 {
00344         if(_r)
00345                 db_free_result(_r);
00346         if(_h)
00347                 BDB_CON_RESULT(_h) = NULL;
00348         return 0;
00349 }
00350 
00351 
00352 /*
00353  * Query table for specified rows
00354  * _con: structure representing database connection
00355  * _k: key names
00356  * _op: operators
00357  * _v: values of the keys that must match
00358  * _c: column names to return
00359  * _n: number of key=values pairs to compare
00360  * _nc: number of columns to return
00361  * _o: order by the specified column
00362  */
00363 int km_bdb_query(db1_con_t* _con, db_key_t* _k, db_op_t* _op, db_val_t* _v, 
00364                         db_key_t* _c, int _n, int _nc, db_key_t _o, db1_res_t** _r)
00365 {
00366         tbl_cache_p _tbc = NULL;
00367         table_p _tp = NULL;
00368         char kbuf[MAX_ROW_SIZE];
00369         char dbuf[MAX_ROW_SIZE];
00370         u_int32_t i, len, ret; 
00371         int klen=MAX_ROW_SIZE;
00372         int *lkey=NULL, *lres=NULL;
00373         DBT key, data;
00374         DB *db;
00375         DBC *dbcp;
00376 
00377         if ((!_con) || (!_r) || !CON_TABLE(_con))
00378         {
00379 #ifdef BDB_EXTRA_DEBUG
00380                 LM_ERR("Invalid parameter value\n");
00381 #endif
00382                 return -1;
00383         }
00384         *_r = NULL;
00385         
00386         /*check if underlying DB file has changed inode */
00387         if(auto_reload)
00388                 bdb_check_reload(_con);
00389 
00390         _tbc = km_bdblib_get_table(BDB_CON_CONNECTION(_con), (str*)CON_TABLE(_con));
00391         if(!_tbc)
00392         {       LM_WARN("table does not exist!\n");
00393                 return -1;
00394         }
00395 
00396         _tp = _tbc->dtp;
00397         if(!_tp)
00398         {       LM_WARN("table not loaded!\n");
00399                 return -1;
00400         }
00401 
00402 #ifdef BDB_EXTRA_DEBUG
00403         LM_DBG("QUERY in %.*s\n", _tp->name.len, _tp->name.s);
00404 
00405         if (_o)  LM_DBG("DONT-CARE : _o: order by the specified column \n");
00406         if (_op) LM_DBG("DONT-CARE : _op: operators for refining query \n");
00407 #endif
00408         
00409         db = _tp->db;
00410         if(!db) return -1;
00411         
00412         memset(&key, 0, sizeof(DBT));
00413         memset(kbuf, 0, MAX_ROW_SIZE);
00414         memset(&data, 0, sizeof(DBT));
00415         memset(dbuf, 0, MAX_ROW_SIZE);
00416         
00417         data.data = dbuf;
00418         data.ulen = MAX_ROW_SIZE;
00419         data.flags = DB_DBT_USERMEM;
00420 
00421         /* if _c is NULL and _nc is zero, you will get all table 
00422            columns in the result
00423         */
00424         if (_c)
00425         {       lres = bdb_get_colmap(_tbc->dtp, _c, _nc);
00426                 if(!lres)
00427                 {       ret = -1;
00428                         goto error;
00429                 }
00430         }
00431         
00432         if(_k)
00433         {       lkey = bdb_get_colmap(_tbc->dtp, _k, _n);
00434                 if(!lkey) 
00435                 {       ret = -1;
00436                         goto error;
00437                 }
00438         }
00439         else
00440         {
00441                 DB_HASH_STAT st;
00442                 memset(&st, 0, sizeof(DB_HASH_STAT));
00443                 i =0 ;
00444 
00445 #ifdef BDB_EXTRA_DEBUG
00446                 LM_DBG("SELECT * FROM %.*s\n", _tp->name.len, _tp->name.s);
00447 #endif
00448 
00449                 /* Acquire a cursor for the database. */
00450                 if ((ret = db->cursor(db, NULL, &dbcp, 0)) != 0) 
00451                 {       LM_ERR("Error creating cursor\n");
00452                         goto error;
00453                 }
00454                 
00455                 /*count the number of records*/
00456                 while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0)
00457                 {       if(!strncasecmp((char*)key.data,"METADATA",8)) 
00458                                 continue;
00459                         i++;
00460                 }
00461                 
00462                 dbcp->CLOSE_CURSOR(dbcp);
00463                 ret=0;
00464                 
00465 #ifdef BDB_EXTRA_DEBUG
00466                 LM_DBG("%i = SELECT COUNT(*) FROM %.*s\n", i, _tp->name.len, _tp->name.s);
00467 #endif
00468 
00469                 *_r = db_new_result();
00470                 if (!*_r) 
00471                 {       LM_ERR("no memory left for result \n");
00472                         ret = -2;
00473                         goto error;
00474                 }
00475                 
00476                 if(i == 0)
00477                 {       
00478                         /*return empty table*/
00479                         RES_ROW_N(*_r) = 0;
00480                         BDB_CON_RESULT(_con) = *_r;
00481                         return 0;
00482                 }
00483                 
00484                 /*allocate N rows in the result*/
00485                 RES_ROW_N(*_r) = i;
00486                 len  = sizeof(db_row_t) * i;
00487                 RES_ROWS(*_r) = (db_row_t*)pkg_malloc( len );
00488                 memset(RES_ROWS(*_r), 0, len);
00489                 
00490                 /*fill in the column part of db1_res_t (metadata) */
00491                 if ((ret = bdb_get_columns(_tbc->dtp, *_r, lres, _nc)) < 0) 
00492                 {       LM_ERR("Error while getting column names\n");
00493                         goto error;
00494                 }
00495                 
00496                 /* Acquire a cursor for the database. */
00497                 if ((ret = db->cursor(db, NULL, &dbcp, 0)) != 0) 
00498                 {       LM_ERR("Error creating cursor\n");
00499                         goto error;
00500                 }
00501 
00502                 /*convert each record into a row in the result*/
00503                 i =0 ;
00504                 while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0)
00505                 {
00506                         if(!strncasecmp((char*)key.data,"METADATA",8)) 
00507                                 continue;
00508                         
00509 #ifdef BDB_EXTRA_DEBUG
00510                 LM_DBG("KEY: [%.*s]\nDATA: [%.*s]\n"
00511                         , (int)   key.size
00512                         , (char *)key.data
00513                         , (int)   data.size
00514                         , (char *)data.data);
00515 #endif
00516 
00517                         /*fill in the row part of db1_res_t */
00518                         if ((ret=bdb_append_row( *_r, dbuf, lres, i)) < 0) 
00519                         {       LM_ERR("Error while converting row\n");
00520                                 goto error;
00521                         }
00522                         i++;
00523                 }
00524                 
00525                 dbcp->CLOSE_CURSOR(dbcp);
00526                 BDB_CON_RESULT(_con) = *_r;
00527                 return 0; 
00528         }
00529 
00530         if ( (ret = km_bdblib_valtochar(_tp, lkey, kbuf, &klen, _v, _n, BDB_KEY)) != 0 ) 
00531         {       LM_ERR("error in query key \n");
00532                 goto error;
00533         }
00534 
00535         key.data = kbuf;
00536         key.ulen = MAX_ROW_SIZE;
00537         key.flags = DB_DBT_USERMEM;
00538         key.size = klen;
00539 
00540         data.data = dbuf;
00541         data.ulen = MAX_ROW_SIZE;
00542         data.flags = DB_DBT_USERMEM;
00543 
00544         /*create an empty db1_res_t which gets returned even if no result*/
00545         *_r = db_new_result();
00546         if (!*_r) 
00547         {       LM_ERR("no memory left for result \n");
00548                 ret = -2;
00549                 goto error;
00550         }
00551         RES_ROW_N(*_r) = 0;
00552         BDB_CON_RESULT(_con) = *_r;
00553 
00554 #ifdef BDB_EXTRA_DEBUG
00555                 LM_DBG("SELECT  KEY: [%.*s]\n"
00556                         , (int)   key.size
00557                         , (char *)key.data );
00558 #endif
00559 
00560         /*query Berkely DB*/
00561         if ((ret = db->get(db, NULL, &key, &data, 0)) == 0) 
00562         {
00563 #ifdef BDB_EXTRA_DEBUG
00564                 LM_DBG("RESULT\nKEY:  [%.*s]\nDATA: [%.*s]\n"
00565                         , (int)   key.size
00566                         , (char *)key.data
00567                         , (int)   data.size
00568                         , (char *)data.data);
00569 #endif
00570 
00571                 /*fill in the col part of db1_res_t */
00572                 if ((ret = bdb_get_columns(_tbc->dtp, *_r, lres, _nc)) < 0) 
00573                 {       LM_ERR("Error while getting column names\n");
00574                         goto error;
00575                 }
00576                 /*fill in the row part of db1_res_t */
00577                 if ((ret=bdb_convert_row( *_r, dbuf, lres)) < 0) 
00578                 {       LM_ERR("Error while converting row\n");
00579                         goto error;
00580                 }
00581                 
00582         }
00583         else
00584         {       
00585                 /*Berkeley DB error handler*/
00586                 switch(ret)
00587                 {
00588                 
00589                 case DB_NOTFOUND:
00590                 
00591 #ifdef BDB_EXTRA_DEBUG
00592                         LM_DBG("NO RESULT for QUERY \n");
00593 #endif
00594                 
00595                         ret=0;
00596                         break;
00597                 /*The following are all critical/fatal */
00598                 case DB_LOCK_DEADLOCK:  
00599                 // The operation was selected to resolve a deadlock. 
00600                 case DB_SECONDARY_BAD:
00601                 // A secondary index references a nonexistent primary key. 
00602                 case DB_RUNRECOVERY:
00603                 default:
00604                         LM_CRIT("DB->get error: %s.\n", db_strerror(ret));
00605                         km_bdblib_recover(_tp,ret);
00606                         goto error;
00607                 }
00608         }
00609         
00610         if(lkey)
00611                 pkg_free(lkey);
00612         if(lres)
00613                 pkg_free(lres);
00614         
00615         return ret;
00616         
00617 error:
00618         if(lkey)
00619                 pkg_free(lkey);
00620         if(lres)
00621                 pkg_free(lres);
00622         if(*_r) 
00623                 db_free_result(*_r);
00624         *_r = NULL;
00625         
00626         return ret;
00627 }
00628 
00629 
00630 
00631 /*
00632  * Raw SQL query
00633  */
00634 int bdb_raw_query(db1_con_t* _h, char* _s, db1_res_t** _r)
00635 {
00636         LM_CRIT("DB RAW QUERY not implemented!\n");
00637         return -1;
00638 }
00639 
00640 /*
00641  * Insert a row into table
00642  */
00643 int bdb_insert(db1_con_t* _h, db_key_t* _k, db_val_t* _v, int _n)
00644 {
00645         tbl_cache_p _tbc = NULL;
00646         table_p _tp = NULL;
00647         char kbuf[MAX_ROW_SIZE];
00648         char dbuf[MAX_ROW_SIZE];
00649         int i, j, ret, klen, dlen;
00650         int *lkey=NULL;
00651         DBT key, data;
00652         DB *db;
00653 
00654         i = j = ret = 0;
00655         klen=MAX_ROW_SIZE;
00656         dlen=MAX_ROW_SIZE;
00657 
00658         if ((!_h) || (!_v) || !CON_TABLE(_h))
00659         {       return -1;
00660         }
00661 
00662         if (!_k)
00663         {
00664 #ifdef BDB_EXTRA_DEBUG
00665         LM_ERR("DB INSERT without KEYs not implemented! \n");
00666 #endif
00667                 return -2;
00668         }
00669 
00670         _tbc = km_bdblib_get_table(BDB_CON_CONNECTION(_h), (str*)CON_TABLE(_h));
00671         if(!_tbc)
00672         {       LM_WARN("table does not exist!\n");
00673                 return -3;
00674         }
00675 
00676         _tp = _tbc->dtp;
00677         if(!_tp)
00678         {       LM_WARN("table not loaded!\n");
00679                 return -4;
00680         }
00681 
00682 #ifdef BDB_EXTRA_DEBUG
00683         LM_DBG("INSERT in %.*s\n", _tp->name.len, _tp->name.s );
00684 #endif
00685         
00686         db = _tp->db;
00687         memset(&key, 0, sizeof(DBT));
00688         memset(kbuf, 0, klen);
00689         
00690         if(_tp->ncols<_n) 
00691         {       LM_WARN("more values than columns!!\n");
00692                 return -5;
00693         }
00694 
00695         lkey = bdb_get_colmap(_tp, _k, _n);
00696         if(!lkey)  return -7;
00697 
00698         /* verify col types provided */
00699         for(i=0; i<_n; i++)
00700         {       j = (lkey)?lkey[i]:i;
00701                 if(bdb_is_neq_type(_tp->colp[j]->type, _v[i].type))
00702                 {
00703                         LM_WARN("incompatible types v[%d] - c[%d]!\n", i, j);
00704                         ret = -8;
00705                         goto error;
00706                 }
00707         }
00708         
00709         /* make the key */
00710         if ( (ret = km_bdblib_valtochar(_tp, lkey, kbuf, &klen, _v, _n, BDB_KEY)) != 0 ) 
00711         {       LM_ERR("Error in km_bdblib_valtochar  \n");
00712                 ret = -9;
00713                 goto error;
00714         }
00715         
00716         key.data = kbuf;
00717         key.ulen = MAX_ROW_SIZE;
00718         key.flags = DB_DBT_USERMEM;
00719         key.size = klen;
00720 
00721         //make the value (row)
00722         memset(&data, 0, sizeof(DBT));
00723         memset(dbuf, 0, MAX_ROW_SIZE);
00724 
00725         if ( (ret = km_bdblib_valtochar(_tp, lkey, dbuf, &dlen, _v, _n, BDB_VALUE)) != 0 ) 
00726         {       LM_ERR("Error in km_bdblib_valtochar \n");
00727                 ret = -9;
00728                 goto error;
00729         }
00730 
00731         data.data = dbuf;
00732         data.ulen = MAX_ROW_SIZE;
00733         data.flags = DB_DBT_USERMEM;
00734         data.size = dlen;
00735 
00736         if ((ret = db->put(db, NULL, &key, &data, 0)) == 0) 
00737         {
00738                 km_bdblib_log(JLOG_INSERT, _tp, dbuf, dlen);
00739 
00740 #ifdef BDB_EXTRA_DEBUG
00741         LM_DBG("INSERT\nKEY:  [%.*s]\nDATA: [%.*s]\n"
00742                 , (int)   key.size
00743                 , (char *)key.data
00744                 , (int)   data.size
00745                 , (char *)data.data);
00746 #endif
00747         }
00748         else
00749         {       /*Berkeley DB error handler*/
00750                 switch(ret)
00751                 {
00752                 /*The following are all critical/fatal */
00753                 case DB_LOCK_DEADLOCK:  
00754                 /* The operation was selected to resolve a deadlock. */ 
00755                 
00756                 case DB_RUNRECOVERY:
00757                 default:
00758                         LM_CRIT("DB->put error: %s.\n", db_strerror(ret));
00759                         km_bdblib_recover(_tp, ret);
00760                         goto error;
00761                 }
00762         }
00763         
00764 error:
00765         if(lkey)
00766                 pkg_free(lkey);
00767         
00768         return ret;
00769 
00770 }
00771 
00772 /*
00773  * Delete a row from table
00774  *
00775  * To delete ALL rows:
00776  *   do Not specify any keys, or values, and _n <=0
00777  *
00778  */
00779 int bdb_delete(db1_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n)
00780 {
00781         tbl_cache_p _tbc = NULL;
00782         table_p _tp = NULL;
00783         char kbuf[MAX_ROW_SIZE];
00784         int i, j, ret, klen;
00785         int *lkey=NULL;
00786         DBT key;
00787         DB *db;
00788         DBC *dbcp;
00789 
00790         i = j = ret = 0;
00791         klen=MAX_ROW_SIZE;
00792 
00793         if (_op)
00794                 return ( _bdb_delete_cursor(_h, _k, _op, _v, _n) );
00795 
00796         if ((!_h) || !CON_TABLE(_h))
00797                 return -1;
00798 
00799         _tbc = km_bdblib_get_table(BDB_CON_CONNECTION(_h), (str*)CON_TABLE(_h));
00800         if(!_tbc)
00801         {       LM_WARN("table does not exist!\n");
00802                 return -3;
00803         }
00804 
00805         _tp = _tbc->dtp;
00806         if(!_tp)
00807         {       LM_WARN("table not loaded!\n");
00808                 return -4;
00809         }
00810 
00811 #ifdef BDB_EXTRA_DEBUG
00812                 LM_DBG("DELETE in %.*s\n", _tp->name.len, _tp->name.s );
00813 #endif
00814 
00815         db = _tp->db;
00816         memset(&key, 0, sizeof(DBT));
00817         memset(kbuf, 0, klen);
00818 
00819         if(!_k || !_v || _n<=0)
00820         {
00821                 /* Acquire a cursor for the database. */
00822                 if ((ret = db->cursor(db, NULL, &dbcp, DB_WRITECURSOR) ) != 0) 
00823                 {       LM_ERR("Error creating cursor\n");
00824                         goto error;
00825                 }
00826                 
00827                 while ((ret = dbcp->c_get(dbcp, &key, NULL, DB_NEXT)) == 0)
00828                 {
00829                         if(!strncasecmp((char*)key.data,"METADATA",8)) 
00830                                 continue;
00831 #ifdef BDB_EXTRA_DEBUG
00832                         LM_DBG("KEY: [%.*s]\n"
00833                                 , (int)   key.size
00834                                 , (char *)key.data);
00835 #endif
00836                         ret = dbcp->c_del(dbcp, 0);
00837                 }
00838                 
00839                 dbcp->CLOSE_CURSOR(dbcp);
00840                 return 0;
00841         }
00842 
00843         lkey = bdb_get_colmap(_tp, _k, _n);
00844         if(!lkey)  return -5;
00845 
00846         /* make the key */
00847         if ( (ret = km_bdblib_valtochar(_tp, lkey, kbuf, &klen, _v, _n, BDB_KEY)) != 0 ) 
00848         {       LM_ERR("Error in bdblib_makekey\n");
00849                 ret = -6;
00850                 goto error;
00851         }
00852 
00853         key.data = kbuf;
00854         key.ulen = MAX_ROW_SIZE;
00855         key.flags = DB_DBT_USERMEM;
00856         key.size = klen;
00857 
00858         if ((ret = db->del(db, NULL, &key, 0)) == 0)
00859         {
00860                 km_bdblib_log(JLOG_DELETE, _tp, kbuf, klen);
00861 
00862 #ifdef BDB_EXTRA_DEBUG
00863                 LM_DBG("DELETED ROW \n KEY: %s \n", (char *)key.data);
00864 #endif
00865         }
00866         else
00867         {       /*Berkeley DB error handler*/
00868                 switch(ret){
00869                         
00870                 case DB_NOTFOUND:
00871                         ret = 0;
00872                         break;
00873                         
00874                 /*The following are all critical/fatal */
00875                 case DB_LOCK_DEADLOCK:  
00876                 /* The operation was selected to resolve a deadlock. */ 
00877                 case DB_SECONDARY_BAD:
00878                 /* A secondary index references a nonexistent primary key. */
00879                 case DB_RUNRECOVERY:
00880                 default:
00881                         LM_CRIT("DB->del error: %s.\n"
00882                                 , db_strerror(ret));
00883                         km_bdblib_recover(_tp, ret);
00884                         goto error;
00885                 }
00886         }
00887 
00888         ret = 0;
00889         
00890 error:
00891         if(lkey)
00892                 pkg_free(lkey);
00893         
00894         return ret;
00895 
00896 }
00897 
00898 /*
00899 _bdb_delete_cursor -- called from bdb_delete when the query involves operators 
00900   other than equal '='. Adds support for queries like this:
00901         DELETE from SomeTable WHERE _k[0] < _v[0]
00902   In this case, the keys _k are not the actually schema keys, so we need to 
00903   iterate via cursor to perform this operation.
00904 */
00905 int _bdb_delete_cursor(db1_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n)
00906 {
00907         tbl_cache_p _tbc = NULL;
00908         table_p _tp = NULL;
00909         db1_res_t* _r   = NULL;
00910         char kbuf[MAX_ROW_SIZE];
00911         char dbuf[MAX_ROW_SIZE];
00912         int i, ret, klen=MAX_ROW_SIZE;
00913         DBT key, data;
00914         DB *db;
00915         DBC *dbcp;
00916         int *lkey=NULL;
00917         
00918         i = ret = 0;
00919         
00920         if ((!_h) || !CON_TABLE(_h))
00921                 return -1;
00922 
00923         _tbc = km_bdblib_get_table(BDB_CON_CONNECTION(_h), (str*)CON_TABLE(_h));
00924         if(!_tbc)
00925         {       LM_WARN("table does not exist!\n");
00926                 return -3;
00927         }
00928 
00929         _tp = _tbc->dtp;
00930         if(!_tp)
00931         {       LM_WARN("table not loaded!\n");
00932                 return -4;
00933         }
00934         
00935 #ifdef BDB_EXTRA_DEBUG
00936         LM_DBG("DELETE by cursor in %.*s\n", _tp->name.len, _tp->name.s );
00937 #endif
00938 
00939         if(_k)
00940         {       lkey = bdb_get_colmap(_tp, _k, _n);
00941                 if(!lkey) 
00942                 {       ret = -1;
00943                         goto error;
00944                 }
00945         }
00946         
00947         /* create an empty db1_res_t which gets returned even if no result */
00948         _r = db_new_result();
00949         if (!_r) 
00950         {       LM_ERR("no memory for result \n");
00951         }
00952         
00953         RES_ROW_N(_r) = 0;
00954         
00955         /* fill in the col part of db1_res_t */
00956         if ((ret = bdb_get_columns(_tp, _r, 0, 0)) != 0) 
00957         {       LM_ERR("Error while getting column names\n");
00958                 goto error;
00959         }
00960         
00961         db = _tp->db;
00962         memset(&key, 0, sizeof(DBT));
00963         memset(kbuf, 0, klen);
00964         memset(&data, 0, sizeof(DBT));
00965         memset(dbuf, 0, MAX_ROW_SIZE);
00966         
00967         data.data = dbuf;
00968         data.ulen = MAX_ROW_SIZE;
00969         data.flags = DB_DBT_USERMEM;
00970         
00971         /* Acquire a cursor for the database. */
00972         if ((ret = db->cursor(db, NULL, &dbcp, DB_WRITECURSOR)) != 0) 
00973         {       LM_ERR("Error creating cursor\n");
00974         }
00975         
00976         while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0)
00977         {
00978                 if(!strncasecmp((char*)key.data,"METADATA",8))
00979                         continue;
00980                 
00981                 /*fill in the row part of db1_res_t */
00982                 if ((ret=bdb_convert_row( _r, dbuf, 0)) < 0) 
00983                 {       LM_ERR("Error while converting row\n");
00984                         goto error;
00985                 }
00986                 
00987                 if(bdb_row_match(_k, _op, _v, _n, _r, lkey ))
00988                 {
00989 
00990 #ifdef BDB_EXTRA_DEBUG
00991                         LM_DBG("DELETE ROW by KEY:  [%.*s]\n", (int) key.size, 
00992                                 (char *)key.data);
00993 #endif
00994 
00995                         if((ret = dbcp->c_del(dbcp, 0)) != 0)
00996                         {       
00997                                 /* Berkeley DB error handler */
00998                                 LM_CRIT("DB->get error: %s.\n", db_strerror(ret));
00999                                 km_bdblib_recover(_tp,ret);
01000                         }
01001                         
01002                 }
01003                 
01004                 memset(dbuf, 0, MAX_ROW_SIZE);
01005                 db_free_rows( _r);
01006         }
01007         ret = 0;
01008         
01009 error:
01010         if(dbcp)
01011                 dbcp->CLOSE_CURSOR(dbcp);
01012         if(_r)
01013                 db_free_result(_r);
01014         if(lkey)
01015                 pkg_free(lkey);
01016         
01017         return ret;
01018 }
01019 
01020 /*
01021  * Updates a row in table
01022  * Limitation: only knows how to update a single row
01023  *
01024  * _con: structure representing database connection
01025  * _k: key names
01026  * _op: operators
01027  * _v: values of the keys that must match
01028  * _uk: update keys; cols that need to be updated 
01029  * _uv: update values; col values that need to be commited
01030  * _un: number of rows to update
01031  */
01032 int bdb_update(db1_con_t* _con, db_key_t* _k, db_op_t* _op, db_val_t* _v,
01033               db_key_t* _uk, db_val_t* _uv, int _n, int _un)
01034 {
01035         char *c, *t;
01036         int ret, i, qcol, len, sum;
01037         int *lkey=NULL;
01038         tbl_cache_p _tbc = NULL;
01039         table_p _tp = NULL;
01040         char kbuf[MAX_ROW_SIZE];
01041         char qbuf[MAX_ROW_SIZE];
01042         char ubuf[MAX_ROW_SIZE];
01043         DBT key, qdata, udata;
01044         DB *db;
01045         
01046         sum = ret = i = qcol = len = 0;
01047         
01048         if (!_con || !CON_TABLE(_con) || !_uk || !_uv || _un <= 0)
01049                 return -1;
01050 
01051         _tbc = km_bdblib_get_table(BDB_CON_CONNECTION(_con), (str*)CON_TABLE(_con));
01052         if(!_tbc)
01053         {       LM_ERR("table does not exist\n");
01054                 return -1;
01055         }
01056 
01057         _tp = _tbc->dtp;
01058         if(!_tp)
01059         {       LM_ERR("table not loaded\n");
01060                 return -1;
01061         }
01062         
01063         db = _tp->db;
01064         if(!db)
01065         {       LM_ERR("DB null ptr\n");
01066                 return -1;
01067         }
01068         
01069 #ifdef BDB_EXTRA_DEBUG
01070         LM_DBG("UPDATE in %.*s\n", _tp->name.len, _tp->name.s);
01071         if (_op) LM_DBG("DONT-CARE : _op: operators for refining query \n");
01072 #endif
01073         
01074         memset(&key, 0, sizeof(DBT));
01075         memset(kbuf, 0, MAX_ROW_SIZE);
01076         memset(&qdata, 0, sizeof(DBT));
01077         memset(qbuf, 0, MAX_ROW_SIZE);
01078         
01079         qdata.data = qbuf;
01080         qdata.ulen = MAX_ROW_SIZE;
01081         qdata.flags = DB_DBT_USERMEM;
01082         
01083         if(_k)
01084         {       lkey = bdb_get_colmap(_tbc->dtp, _k, _n);
01085                 if(!lkey) return -4;
01086         }
01087         else
01088         {
01089                 LM_ERR("Null keys in update _k=0 \n");
01090                 return -1;
01091         }
01092         
01093         len = MAX_ROW_SIZE;
01094         
01095         if ( (ret = km_bdblib_valtochar(_tp, lkey, kbuf, &len, _v, _n, BDB_KEY)) != 0 ) 
01096         {       LM_ERR("Error in query key \n");
01097                 goto cleanup;
01098         }
01099         
01100         if(lkey) pkg_free(lkey);
01101         
01102         key.data = kbuf;
01103         key.ulen = MAX_ROW_SIZE;
01104         key.flags = DB_DBT_USERMEM;
01105         key.size = len;
01106         
01107         /*stage 1: QUERY Berkely DB*/
01108         if ((ret = db->get(db, NULL, &key, &qdata, 0)) == 0) 
01109         {
01110 
01111 #ifdef BDB_EXTRA_DEBUG
01112                 LM_DBG("RESULT\nKEY:  [%.*s]\nDATA: [%.*s]\n"
01113                         , (int)   key.size
01114                         , (char *)key.data
01115                         , (int)   qdata.size
01116                         , (char *)qdata.data);
01117 #endif
01118 
01119         }
01120         else
01121         {       goto db_error;
01122         }
01123         
01124         /* stage 2: UPDATE row with new values */
01125         
01126         /* map the provided keys to those in our schema */ 
01127         lkey = bdb_get_colmap(_tbc->dtp, _uk, _un);
01128         if(!lkey) return -4;
01129         
01130         /* build a new row for update data (udata) */
01131         memset(&udata, 0, sizeof(DBT));
01132         memset(ubuf, 0, MAX_ROW_SIZE);
01133         
01134         /* loop over each column of the qbuf and copy it to our new ubuf unless
01135            its a field that needs to update
01136         */
01137         c = strtok(qbuf, DELIM);
01138         t = ubuf;
01139         while( c!=NULL)
01140         {       char* delim = DELIM;
01141                 int k;
01142                 
01143                 len = strlen(c);
01144                 sum+=len;
01145                 
01146                 if(sum > MAX_ROW_SIZE)
01147                 {       LM_ERR("value too long for string \n");
01148                         ret = -3;
01149                         goto cleanup;
01150                 }
01151                 
01152                 for(i=0;i<_un;i++)
01153                 {
01154                         k = lkey[i];
01155                         if (qcol == k)
01156                         {       /* update this col */
01157                                 int j = MAX_ROW_SIZE - sum;
01158                                 if( km_bdb_val2str( &_uv[i], t, &j) )
01159                                 {       LM_ERR("value too long for string \n");
01160                                         ret = -3;
01161                                         goto cleanup;
01162                                 }
01163 
01164                                 goto next;
01165                         }
01166                         
01167                 }
01168                 
01169                 /* copy original column to the new column */
01170                 strncpy(t, c, len);
01171 
01172 next:
01173                 t+=len;
01174                 
01175                 /* append DELIM */
01176                 sum += DELIM_LEN;
01177                 if(sum > MAX_ROW_SIZE)
01178                 {       LM_ERR("value too long for string \n");
01179                         ret = -3;
01180                         goto cleanup;
01181                 }
01182                 
01183                 strncpy(t, delim, DELIM_LEN);
01184                 t += DELIM_LEN;
01185                 
01186                 c = strtok(NULL, DELIM);
01187                 qcol++;
01188         }
01189         
01190         ubuf[sum]  = '0';
01191         udata.data = ubuf;
01192         udata.ulen  = MAX_ROW_SIZE;
01193         udata.flags = DB_DBT_USERMEM;
01194         udata.size  = sum;
01195 
01196 #ifdef BDB_EXTRA_DEBUG
01197         LM_DBG("MODIFIED Data\nKEY:  [%.*s]\nDATA: [%.*s]\n"
01198                 , (int)   key.size
01199                 , (char *)key.data
01200                 , (int)   udata.size
01201                 , (char *)udata.data);
01202 #endif
01203         /* stage 3: DELETE old row using key*/
01204         if ((ret = db->del(db, NULL, &key, 0)) == 0)
01205         {
01206 #ifdef BDB_EXTRA_DEBUG
01207                 LM_DBG("DELETED ROW\nKEY: %s \n", (char *)key.data);
01208 #endif
01209         }
01210         else
01211         {       goto db_error;
01212         }
01213         
01214         /* stage 4: INSERT new row with key*/
01215         if ((ret = db->put(db, NULL, &key, &udata, 0)) == 0) 
01216         {
01217                 km_bdblib_log(JLOG_UPDATE, _tp, ubuf, sum);
01218 #ifdef BDB_EXTRA_DEBUG
01219         LM_DBG("INSERT \nKEY:  [%.*s]\nDATA: [%.*s]\n"
01220                 , (int)   key.size
01221                 , (char *)key.data
01222                 , (int)   udata.size
01223                 , (char *)udata.data);
01224 #endif
01225         }
01226         else
01227         {       goto db_error;
01228         }
01229 
01230 #ifdef BDB_EXTRA_DEBUG
01231         LM_DBG("UPDATE COMPLETE \n");
01232 #endif
01233 
01234 
01235 cleanup:
01236         if(lkey)
01237                 pkg_free(lkey);
01238         
01239         return ret;
01240 
01241 
01242 db_error:
01243 
01244         /*Berkeley DB error handler*/
01245         switch(ret)
01246         {
01247         
01248         case DB_NOTFOUND:
01249         
01250 #ifdef BDB_EXTRA_DEBUG
01251                 LM_DBG("NO RESULT \n");
01252 #endif
01253                 return -1;
01254         
01255         /* The following are all critical/fatal */
01256         case DB_LOCK_DEADLOCK:  
01257         /* The operation was selected to resolve a deadlock. */
01258         case DB_SECONDARY_BAD:
01259         /* A secondary index references a nonexistent primary key.*/ 
01260         case DB_RUNRECOVERY:
01261         default:
01262                 LM_CRIT("DB->get error: %s.\n", db_strerror(ret));
01263                 km_bdblib_recover(_tp,ret);
01264         }
01265         
01266         if(lkey)
01267                 pkg_free(lkey);
01268         
01269         return ret;
01270 }