/*
 * Updates a row in table
 * Limitation: only knows how to update a single row
 *
 * _con: structure representing database connection
 * _k: key names
 * _op: operators
 * _v: values of the keys that must match
 * _uk: update keys; cols that need to be updated
 * _uv: update values; col values that need to be commited
 * _un: number of rows to update
 */
int bdb_update(db_con_t* _con, db_key_t* _k, db_op_t* _op, db_val_t* _v,
               db_key_t* _uk, db_val_t* _uv, int _n, int _un)
{
    str s;
    char *c, *t;
    int ret, i, qcol, len, sum;
    int *lkey=NULL;
    tbl_cache_p _tbc = NULL;
    table_p _tp = NULL;
    char kbuf[MAX_ROW_SIZE];
    char qbuf[MAX_ROW_SIZE];
    char ubuf[MAX_ROW_SIZE];
    DBT key, qdata, udata;
    DB *db;

    sum = ret = i = qcol = len = 0;

    if (!_con || !CON_TABLE(_con) || !_uk || !_uv || _un <= 0)
        return -1;

    s.s = (char*)CON_TABLE(_con);
    s.len = strlen(CON_TABLE(_con));

    _tbc = bdblib_get_table(BDB_CON_CONNECTION(_con), &s);
    if(!_tbc)
    {   LM_ERR("table does not exist\n");
        return -1;
    }

    _tp = _tbc->dtp;
    if(!_tp)
    {   LM_ERR("table not loaded\n");
        return -1;
    }

    db = _tp->db;
    if(!db)
    {   LM_ERR("DB null ptr\n");
        return -1;
    }

#ifdef BDB_EXTRA_DEBUG
    LM_DBG("UPDATE in %.*s\n", _tp->name.len, _tp->name.s);
    if (_op) LM_DBG("DONT-CARE : _op: operators for refining query \n");
#endif

    memset(&key, 0, sizeof(DBT));
    memset(kbuf, 0, MAX_ROW_SIZE);
    memset(&qdata, 0, sizeof(DBT));
    memset(qbuf, 0, MAX_ROW_SIZE);

    qdata.data = qbuf;
    qdata.ulen = MAX_ROW_SIZE;
    qdata.flags = DB_DBT_USERMEM;

    if(_k)
    {   lkey = bdb_get_colmap(_tbc->dtp, _k, _n);
        if(!lkey) return -4;
    }
    else
    {
        LM_ERR("Null keys in update _k=0 \n");
        return -1;
    }

    len = MAX_ROW_SIZE;

    if ( (ret = bdblib_valtochar(_tp, lkey, kbuf, &len, _v, _n, BDB_KEY)) != 0 )
    {   LM_ERR("Error in query key \n");
        goto cleanup;
    }

    if(lkey) pkg_free(lkey);

    key.data = kbuf;
    key.ulen = MAX_ROW_SIZE;
    key.flags = DB_DBT_USERMEM;
    key.size = len;

    /*stage 1: QUERY Berkely DB*/
    if ((ret = db->get(db, NULL, &key, &qdata, 0)) == 0)
    {

#ifdef BDB_EXTRA_DEBUG
        LM_DBG("RESULT\nKEY:  [%.*s]\nDATA: [%.*s]\n"
               , (int)   key.size
               , (char *)key.data
               , (int)   qdata.size
               , (char *)qdata.data);
#endif

    }
    else
    {   goto db_error;
    }

    /* stage 2: UPDATE row with new values */

    /* map the provided keys to those in our schema */
    lkey = bdb_get_colmap(_tbc->dtp, _uk, _un);
    if(!lkey) return -4;

    /* build a new row for update data (udata) */
    memset(&udata, 0, sizeof(DBT));
    memset(ubuf, 0, MAX_ROW_SIZE);

    /* loop over each column of the qbuf and copy it to our new ubuf unless
       its a field that needs to update
    */
    c = strtok(qbuf, DELIM);
    t = ubuf;
    while( c!=NULL)
    {   char* delim = DELIM;
        int k;

        len = strlen(c);
        sum+=len;

        if(sum > MAX_ROW_SIZE)
        {   LM_ERR("value too long for string \n");
            ret = -3;
            goto cleanup;
        }

        for(i=0; i<_un; i++)
        {
            k = lkey[i];
            if (qcol == k)
            {   /* update this col */
                int j = MAX_ROW_SIZE - sum;
                if( bdb_val2str( &_uv[i], t, &j) )
                {   LM_ERR("value too long for string \n");
                    ret = -3;
                    goto cleanup;
                }

                goto next;
            }

        }

        /* copy original column to the new column */
        strncpy(t, c, len);

next:
        t+=len;

        /* append DELIM */
        sum += DELIM_LEN;
        if(sum > MAX_ROW_SIZE)
        {   LM_ERR("value too long for string \n");
            ret = -3;
            goto cleanup;
        }

        strncpy(t, delim, DELIM_LEN);
        t += DELIM_LEN;

        c = strtok(NULL, DELIM);
        qcol++;
    }

    ubuf[sum]  = '0';
    udata.data = ubuf;
    udata.ulen  = MAX_ROW_SIZE;
    udata.flags = DB_DBT_USERMEM;
    udata.size  = sum;

#ifdef BDB_EXTRA_DEBUG
    LM_DBG("MODIFIED Data\nKEY:  [%.*s]\nDATA: [%.*s]\n"
           , (int)   key.size
           , (char *)key.data
           , (int)   udata.size
           , (char *)udata.data);
#endif
    /* stage 3: DELETE old row using key*/
    if ((ret = db->del(db, NULL, &key, 0)) == 0)
    {
#ifdef BDB_EXTRA_DEBUG
        LM_DBG("DELETED ROW\nKEY: %s \n", (char *)key.data);
#endif
    }
    else
    {   goto db_error;
    }

    /* stage 4: INSERT new row with key*/
    if ((ret = db->put(db, NULL, &key, &udata, 0)) == 0)
    {
        bdblib_log(JLOG_UPDATE, _tp, ubuf, sum);
#ifdef BDB_EXTRA_DEBUG
        LM_DBG("INSERT \nKEY:  [%.*s]\nDATA: [%.*s]\n"
               , (int)   key.size
               , (char *)key.data
               , (int)   udata.size
               , (char *)udata.data);
#endif
    }
    else
    {   goto db_error;
    }

#ifdef BDB_EXTRA_DEBUG
    LM_DBG("UPDATE COMPLETE \n");
#endif


cleanup:
    if(lkey)
        pkg_free(lkey);

    return ret;


db_error:

    /*Berkeley DB error handler*/
    switch(ret)
    {

    case DB_NOTFOUND:

#ifdef BDB_EXTRA_DEBUG
        LM_DBG("NO RESULT \n");
#endif
        return -1;

    /* The following are all critical/fatal */
    case DB_LOCK_DEADLOCK:
    /* The operation was selected to resolve a deadlock. */
    case DB_SECONDARY_BAD:
    /* A secondary index references a nonexistent primary key.*/
    case DB_RUNRECOVERY:
    default:
        LM_CRIT("DB->get error: %s.\n", db_strerror(ret));
        bdblib_recover(_tp,ret);
    }

    if(lkey)
        pkg_free(lkey);

    return ret;
}
Beispiel #2
0
int bdblib_valtochar(bdb_table_p tp, db_fld_t *fld, int fld_count, char *kout,
		int *klen, int ktype)
{
	char *p; 
	static char sk[MAX_ROW_SIZE]; // subkey(sk) val
	char* delim = DELIM;
	char* cNULL = "NULL";
	int  len, total, sum;
	int i, j, k;
	bdb_fld_t *f;

	p =  kout;
	len = sum = total = 0;
	i = j = k = 0;
	
	if(tp==NULL) return -1;
	if(fld==NULL || fld_count<1) return -1;
	if(kout==NULL || klen==NULL ) return -1;
	if( *klen < 1)    return -1;
	
	memset(sk, 0, MAX_ROW_SIZE);
	total = *klen;
	*klen = 0; //sum
	
	/*
	  schema has specified keys
	  verify all schema keys are provided
	  use 'NULL' for those that are missing.
	*/
	for(i=0; i<tp->ncols; i++)
	{	/* i indexes columns in schema order */
		if(ktype)
		{	/* keymode; skip over non-key columns */
			if(tp->colp[i]->flag==0) 
				continue; 
		}
		
		for(j=0; j<fld_count; j++)
		{
			f = DB_GET_PAYLOAD(fld + j);
			/*
			  j indexes the columns provided in _k
			  which may be less than the total required by
			  the schema. the app does not know the order
			  of the columns in our schema!
			 */
			k = f->col_pos;
			
			/*
			 * k index will remap back to our schema order; like i
			 */
			if(i == k)
			{
				/*
				 KEY was provided; append to buffer;
				 _k[j] contains a key, but its a key that 
				 corresponds to column k of our schema.
				 now we know its a match, and we dont need
				 index k for anything else
				*/
				len = total - sum;
				if ( bdb_val2str((fld+j), sk, &len) != 0)
				{
					ERR("Destination buffer too short for subval %s\n",sk);
					return -4;
				}
				
				sum += len;
				if(sum > total)
				{
					ERR("Destination buffer too short for subval %s\n",sk);
					return -5;
				}

				strncpy(p, sk, len);
				p += len;
				*klen = sum;

				sum += DELIM_LEN;
				if(sum > total)
				{
					ERR("Destination buffer too short for delim \n");
					return -5;
				} 
				
				/* append delim */
				strncpy(p, delim, DELIM_LEN);
				p += DELIM_LEN;
				*klen = sum;
				
				
				/* take us out of inner for loop
				   and at the end of the outer loop
				   to look for our next schema key
				*/
				goto next;
			}
		}

		/*
		 NO KEY provided; use the column default value (dv)
		     i.e _tp->colp[i]->dv
		*/
		len = tp->colp[i]->dv.len;
		sum += len;
		if(sum > total)
		{
			ERR("Destination buffer too short for subval %s\n",cNULL);
			return -5;
		}
		
		strncpy(p, tp->colp[i]->dv.s, len);
		p += len;
		*klen = sum;
		
		sum += DELIM_LEN;
		if(sum > total)
		{
			ERR("Destination buffer too short for delim \n");
			return -5;
		} 
		
		strncpy(p, delim, DELIM_LEN);
		p += DELIM_LEN;
		*klen = sum;
next:
		continue;
	}

	return 0;
}