diff --git a/modules/htable/ht_api.c b/modules/htable/ht_api.c index bc20c08c50e..d3c1aa825f4 100644 --- a/modules/htable/ht_api.c +++ b/modules/htable/ht_api.c @@ -251,11 +251,14 @@ ht_t* ht_get_table(str *name) return NULL; } -int ht_add_table(str *name, int autoexp, str *dbtable, int size, int dbmode, - int itype, int_str *ival, int updateexpire, int dmqreplicate) +int ht_add_table(str *name, int autoexp, str *dbtable, str *dbcols, int size, + int dbmode, int itype, int_str *ival, int updateexpire, + int dmqreplicate) { unsigned int htid; ht_t *ht; + int c; + int i; htid = ht_compute_hash(name); @@ -296,6 +299,48 @@ int ht_add_table(str *name, int autoexp, str *dbtable, int size, int dbmode, if(ival!=NULL) ht->initval = *ival; ht->dmqreplicate = dmqreplicate; + + if(dbcols!=NULL && dbcols->s!=NULL && dbcols->len>0) { + ht->scols[0].s = (char*)shm_malloc((1+dbcols->len)*sizeof(char)); + if(ht->scols[0].s==NULL) { + LM_ERR("no more shm memory\n"); + shm_free(ht); + return -1; + } + memset(ht->scols[0].s, 0, (1+dbcols->len)*sizeof(char)); + memcpy(ht->scols[0].s, dbcols->s, dbcols->len); + ht->scols[0].len = dbcols->len; + c = 0; + for(i=0; ilen; i++) { + if(ht->scols[0].s[i]==',') { + ht->scols[c].len = (ht->scols[0].s + i - ht->scols[c].s); + LM_DBG("db table column[%d]='%.*s'\n", c, + ht->scols[c].len, ht->scols[c].s); + c++; + if(c>=HT_MAX_COLS) { + LM_ERR("too many columns %d\n", c); + shm_free(ht->scols[0].s); + shm_free(ht); + return -1; + } + ht->scols[c].s = ht->scols[0].s + i + 1; + ht->scols[c].len = ht->scols[0].s + dbcols->len - ht->scols[c].s; + } + } + LM_DBG("db table column[%d]='%.*s'\n", c, + ht->scols[c].len, ht->scols[c].s); + if(c==0) { + LM_ERR("there must be at least two columns (prefix, value)\n"); + shm_free(ht->scols[0].s); + shm_free(ht); + return -1; + } + ht->ncols = c + 1; + ht->pack[0] = 'l'; + ht->pack[1] = ','; + ht->pack[2] = '*'; + } + ht->next = _ht_root; _ht_root = ht; return 0; @@ -828,6 +873,7 @@ int ht_table_spec(char *spec) keyvalue_t kval; str name; str dbtable = {0, 0}; + str dbcols = {0, 0}; unsigned int autoexpire = 0; unsigned int size = 4; unsigned int dbmode = 0; @@ -863,6 +909,10 @@ int ht_table_spec(char *spec) dbtable = tok; LM_DBG("htable [%.*s] - dbtable [%.*s]\n", name.len, name.s, dbtable.len, dbtable.s); + } else if(pit->name.len==4 && strncmp(pit->name.s, "cols", 4)==0) { + dbcols = tok; + LM_DBG("htable [%.*s] - dbcols [%.*s]\n", name.len, name.s, + dbcols.len, dbcols.s); } else if(pit->name.len==10 && strncmp(pit->name.s, "autoexpire", 10)==0) { if(str2int(&tok, &autoexpire)!=0) goto error; @@ -897,7 +947,7 @@ int ht_table_spec(char *spec) } else { goto error; } } - return ht_add_table(&name, autoexpire, &dbtable, size, dbmode, + return ht_add_table(&name, autoexpire, &dbtable, &dbcols, size, dbmode, itype, &ival, updateexpire, dmqreplicate); error: diff --git a/modules/htable/ht_api.h b/modules/htable/ht_api.h index e91db338a3d..a37e98f3f4a 100644 --- a/modules/htable/ht_api.h +++ b/modules/htable/ht_api.h @@ -53,6 +53,7 @@ typedef struct _ht_entry int rec_lock_level; /* recursive lock count */ } ht_entry_t; +#define HT_MAX_COLS 8 typedef struct _ht { str name; @@ -60,6 +61,9 @@ typedef struct _ht unsigned int htexpire; str dbtable; int dbmode; + int ncols; + str scols[HT_MAX_COLS]; + char pack[4]; int flags; int_str initval; int updateexpire; @@ -76,8 +80,9 @@ typedef struct _ht_pv { pv_elem_t *pve; } ht_pv_t, *ht_pv_p; -int ht_add_table(str *name, int autoexp, str *dbtable, int size, int dbmode, - int itype, int_str *ival, int updateexpire, int dmqreplicate); +int ht_add_table(str *name, int autoexp, str *dbtable, str *dbcols, int size, + int dbmode, int itype, int_str *ival, int updateexpire, + int dmqreplicate); int ht_init_tables(void); int ht_destroy(void); int ht_set_cell(ht_t *ht, str *name, int type, int_str *val, int mode); diff --git a/modules/htable/ht_db.c b/modules/htable/ht_db.c index 4288554b79f..cf38f82a44a 100644 --- a/modules/htable/ht_db.c +++ b/modules/htable/ht_db.c @@ -111,13 +111,68 @@ int ht_db_close_con(void) #define HT_NAME_BUF_SIZE 256 static char ht_name_buf[HT_NAME_BUF_SIZE]; +static int ht_pack_values(ht_t *ht, db1_res_t* db_res, + int row, int cols, str *hvalue) +{ + static char vbuf[4096]; + int c; + int len; + char *p; + str iv; + + len = 0; + for(c=1; c=4096) { + LM_ERR("too large values (need %d)\n", len+c); + return -1; + } + p = vbuf; + for(c=1; cpack[2]; + p++; + } else if(RES_ROWS(db_res)[row].values[c].type == DB1_STRING) { + strcpy(p, RES_ROWS(db_res)[row].values[c].val.string_val); + p += strlen(RES_ROWS(db_res)[row].values[c].val.string_val); + } else if(RES_ROWS(db_res)[row].values[c].type == DB1_STR) { + strncpy(p, RES_ROWS(db_res)[row].values[c].val.str_val.s, + RES_ROWS(db_res)[row].values[c].val.str_val.len); + p += RES_ROWS(db_res)[row].values[c].val.str_val.len; + } else if(RES_ROWS(db_res)[row].values[c].type == DB1_INT) { + iv.s = sint2str(RES_ROWS(db_res)[row].values[c].val.int_val, &iv.len); + strncpy(p, iv.s, iv.len); + p += iv.len; + } + if(c+1pack[1]; + p++; + } + } + hvalue->s = vbuf; + hvalue->len = p - vbuf; + LM_DBG("packed: [%.*s]\n", hvalue->len, hvalue->s); + return 0; +} + /** * load content of a db table in hash table */ int ht_db_load_table(ht_t *ht, str *dbtable, int mode) { - db_key_t db_cols[5] = {&ht_db_name_column, &ht_db_ktype_column, - &ht_db_vtype_column, &ht_db_value_column, &ht_db_expires_column}; + db_key_t db_cols[HT_MAX_COLS]; db_key_t db_ord = &ht_db_name_column; db1_res_t* db_res = NULL; str kname; @@ -135,6 +190,7 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode) int cnt; int now; int ncols; + int c; if(ht_db_con==NULL) { @@ -147,13 +203,25 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode) LM_ERR("failed to use_table\n"); return -1; } + if(ht->ncols>0) { + for(c=0; cncols; c++) { + db_cols[c] = &ht->scols[c]; + } + ncols = c; + } else { + db_cols[0] = &ht_db_name_column; + db_cols[1] = &ht_db_ktype_column; + db_cols[2] = &ht_db_vtype_column; + db_cols[3] = &ht_db_value_column; + db_cols[4] = &ht_db_expires_column; + ncols = 4; + if(ht->htexpire > 0 && ht_db_expires_flag!=0) + ncols = 5; + } LM_DBG("=============== loading hash table [%.*s] from database [%.*s]\n", ht->name.len, ht->name.s, dbtable->len, dbtable->s); cnt = 0; - ncols = 4; - if(ht->htexpire > 0 && ht_db_expires_flag!=0) - ncols = 5; if (DB_CAPABILITY(ht_dbf, DB_CAP_FETCH)) { if(ht_dbf.query(ht_db_con,0,0,0,db_cols,0,ncols,db_ord,0) < 0) @@ -215,161 +283,173 @@ int ht_db_load_table(ht_t *ht, str *dbtable, int mode) } kname.len = strlen(kname.s); - expires.n = 0; - if(ht->htexpire > 0 && ht_db_expires_flag!=0) { - expires.n = RES_ROWS(db_res)[i].values[4].val.int_val; - if (expires.n > 0 && expires.n < now) { - LM_DBG("skipping expired entry [%.*s] (%d)\n", kname.len, - kname.s, expires.n-now); - continue; + if(ht->ncols>0) { + if(ht_pack_values(ht, db_res, i, ncols, &val.s)<0) { + LM_ERR("Error packing values\n"); + goto error; } - } - - cnt++; - switch(RES_ROWS(db_res)[i].values[1].type) - { - case DB1_INT: - ktype = RES_ROWS(db_res)[i].values[1].val.int_val; - break; - case DB1_BIGINT: - ktype = RES_ROWS(db_res)[i].values[1].val.ll_val; - break; - default: - LM_ERR("Wrong db type [%d] for key_type column\n", - RES_ROWS(db_res)[i].values[1].type); - goto error; - } - if(last_ktype==1) - { - if(pname.len>0 - && (pname.len!=kname.len - || strncmp(pname.s, kname.s, pname.len)!=0)) + if(ht_set_cell(ht, &kname, AVP_VAL_STR, &val, mode)) { - /* new key name, last was an array => add its size */ - snprintf(ht_name_buf, HT_NAME_BUF_SIZE, "%.*s%.*s", - pname.len, pname.s, ht_array_size_suffix.len, - ht_array_size_suffix.s); - hname.s = ht_name_buf; - hname.len = strlen(ht_name_buf); - val.n = n; - - if(ht_set_cell(ht, &hname, 0, &val, mode)) - { - LM_ERR("error adding array size to hash table.\n"); - goto error; - } - pname.len = 0; - pname.s = ""; - n = 0; + LM_ERR("error adding to hash table\n"); + goto error; } - } - last_ktype = ktype; - pname = kname; - if(ktype==1) - { - snprintf(ht_name_buf, HT_NAME_BUF_SIZE, "%.*s[%d]", - kname.len, kname.s, n); - hname.s = ht_name_buf; - hname.len = strlen(ht_name_buf); - n++; } else { - hname = kname; - } - switch(RES_ROWS(db_res)[i].values[2].type) - { - case DB1_INT: - vtype = RES_ROWS(db_res)[i].values[2].val.int_val; - break; - case DB1_BIGINT: - vtype = RES_ROWS(db_res)[i].values[2].val.ll_val; - break; - default: - LM_ERR("Wrong db type [%d] for value_type column\n", - RES_ROWS(db_res)[i].values[2].type); - goto error; - } + expires.n = 0; + if(ht->htexpire > 0 && ht_db_expires_flag!=0) { + expires.n = RES_ROWS(db_res)[i].values[4].val.int_val; + if (expires.n > 0 && expires.n < now) { + LM_DBG("skipping expired entry [%.*s] (%d)\n", kname.len, + kname.s, expires.n-now); + continue; + } + } - /* add to hash */ - if(vtype==1) - { - switch(RES_ROWS(db_res)[i].values[3].type) + cnt++; + switch(RES_ROWS(db_res)[i].values[1].type) { - case DB1_STR: - kvalue = RES_ROWS(db_res)[i].values[3].val.str_val; - if(kvalue.s==NULL) { - LM_ERR("null value in row %d\n", i); - goto error; - } - str2sint(&kvalue, &val.n); - break; - case DB1_STRING: - kvalue.s = (char*)(RES_ROWS(db_res)[i].values[3].val.string_val); - if(kvalue.s==NULL) { - LM_ERR("null value in row %d\n", i); - goto error; - } - kvalue.len = strlen(kvalue.s); - str2sint(&kvalue, &val.n); - break; - case DB1_INT: - val.n = RES_ROWS(db_res)[i].values[3].val.int_val; + case DB1_INT: + ktype = RES_ROWS(db_res)[i].values[1].val.int_val; break; case DB1_BIGINT: - val.n = RES_ROWS(db_res)[i].values[3].val.ll_val; + ktype = RES_ROWS(db_res)[i].values[1].val.ll_val; break; default: - LM_ERR("Wrong db type [%d] for key_value column\n", - RES_ROWS(db_res)[i].values[3].type); + LM_ERR("Wrong db type [%d] for key_type column\n", + RES_ROWS(db_res)[i].values[1].type); goto error; } - } else { - switch(RES_ROWS(db_res)[i].values[3].type) + if(last_ktype==1) { - case DB1_STR: - kvalue = RES_ROWS(db_res)[i].values[3].val.str_val; - if(kvalue.s==NULL) { - LM_ERR("null value in row %d\n", i); - goto error; + if(pname.len>0 + && (pname.len!=kname.len + || strncmp(pname.s, kname.s, pname.len)!=0)) + { + /* new key name, last was an array => add its size */ + snprintf(ht_name_buf, HT_NAME_BUF_SIZE, "%.*s%.*s", + pname.len, pname.s, ht_array_size_suffix.len, + ht_array_size_suffix.s); + hname.s = ht_name_buf; + hname.len = strlen(ht_name_buf); + val.n = n; + + if(ht_set_cell(ht, &hname, 0, &val, mode)) + { + LM_ERR("error adding array size to hash table.\n"); + goto error; + } + pname.len = 0; + pname.s = ""; + n = 0; } - val.s = kvalue; - break; - case DB1_STRING: - kvalue.s = (char*)(RES_ROWS(db_res)[i].values[3].val.string_val); - if(kvalue.s==NULL) { - LM_ERR("null value in row %d\n", i); + } + last_ktype = ktype; + pname = kname; + if(ktype==1) + { + snprintf(ht_name_buf, HT_NAME_BUF_SIZE, "%.*s[%d]", + kname.len, kname.s, n); + hname.s = ht_name_buf; + hname.len = strlen(ht_name_buf); + n++; + } else { + hname = kname; + } + switch(RES_ROWS(db_res)[i].values[2].type) + { + case DB1_INT: + vtype = RES_ROWS(db_res)[i].values[2].val.int_val; + break; + case DB1_BIGINT: + vtype = RES_ROWS(db_res)[i].values[2].val.ll_val; + break; + default: + LM_ERR("Wrong db type [%d] for value_type column\n", + RES_ROWS(db_res)[i].values[2].type); goto error; + } + + /* add to hash */ + if(vtype==1) + { + switch(RES_ROWS(db_res)[i].values[3].type) + { + case DB1_STR: + kvalue = RES_ROWS(db_res)[i].values[3].val.str_val; + if(kvalue.s==NULL) { + LM_ERR("null value in row %d\n", i); + goto error; + } + str2sint(&kvalue, &val.n); + break; + case DB1_STRING: + kvalue.s = (char*)(RES_ROWS(db_res)[i].values[3].val.string_val); + if(kvalue.s==NULL) { + LM_ERR("null value in row %d\n", i); + goto error; + } + kvalue.len = strlen(kvalue.s); + str2sint(&kvalue, &val.n); + break; + case DB1_INT: + val.n = RES_ROWS(db_res)[i].values[3].val.int_val; + break; + case DB1_BIGINT: + val.n = RES_ROWS(db_res)[i].values[3].val.ll_val; + break; + default: + LM_ERR("Wrong db type [%d] for key_value column\n", + RES_ROWS(db_res)[i].values[3].type); + goto error; + } + } else { + switch(RES_ROWS(db_res)[i].values[3].type) + { + case DB1_STR: + kvalue = RES_ROWS(db_res)[i].values[3].val.str_val; + if(kvalue.s==NULL) { + LM_ERR("null value in row %d\n", i); + goto error; + } + val.s = kvalue; + break; + case DB1_STRING: + kvalue.s = (char*)(RES_ROWS(db_res)[i].values[3].val.string_val); + if(kvalue.s==NULL) { + LM_ERR("null value in row %d\n", i); + goto error; + } + kvalue.len = strlen(kvalue.s); + val.s = kvalue; + break; + case DB1_INT: + kvalue.s = int2str(RES_ROWS(db_res)[i].values[3].val.int_val, &kvalue.len); + val.s = kvalue; + break; + case DB1_BIGINT: + kvalue.s = int2str(RES_ROWS(db_res)[i].values[3].val.ll_val, &kvalue.len); + val.s = kvalue; + break; + default: + LM_ERR("Wrong db type [%d] for key_value column\n", + RES_ROWS(db_res)[i].values[3].type); + goto error; } - kvalue.len = strlen(kvalue.s); - val.s = kvalue; - break; - case DB1_INT: - kvalue.s = int2str(RES_ROWS(db_res)[i].values[3].val.int_val, &kvalue.len); - val.s = kvalue; - break; - case DB1_BIGINT: - kvalue.s = int2str(RES_ROWS(db_res)[i].values[3].val.ll_val, &kvalue.len); - val.s = kvalue; - break; - default: - LM_ERR("Wrong db type [%d] for key_value column\n", - RES_ROWS(db_res)[i].values[3].type); - goto error; } - } - if(ht_set_cell(ht, &hname, (vtype)?0:AVP_VAL_STR, &val, mode)) - { - LM_ERR("error adding to hash table\n"); - goto error; - } - - /* set expiry */ - if (ht->htexpire > 0 && expires.n > 0) { - expires.n -= now; - if(ht_set_cell_expire(ht, &hname, 0, &expires)) { - LM_ERR("error setting expires to hash entry [%*.s]\n", hname.len, hname.s); + if(ht_set_cell(ht, &hname, (vtype)?0:AVP_VAL_STR, &val, mode)) + { + LM_ERR("error adding to hash table\n"); goto error; } + + /* set expiry */ + if (ht->htexpire > 0 && expires.n > 0) { + expires.n -= now; + if(ht_set_cell_expire(ht, &hname, 0, &expires)) { + LM_ERR("error setting expires to hash entry [%*.s]\n", hname.len, hname.s); + goto error; + } + } } } if (DB_CAPABILITY(ht_dbf, DB_CAP_FETCH)) {