mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
486 lines
11 KiB
486 lines
11 KiB
/*
|
|
* $Id$
|
|
*
|
|
* Oracle module result related functions
|
|
*
|
|
* Copyright (C) 2007,2008 TRUNK MOBILE
|
|
*
|
|
* This file is part of Kamailio, a free SIP server.
|
|
*
|
|
* Kamailio is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* Kamailio is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <oci.h>
|
|
#include "../../lib/srdb1/db_res.h"
|
|
#include "../../lib/srdb1/db_row.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../dprint.h"
|
|
#include "ora_con.h"
|
|
#include "dbase.h"
|
|
#include "asynch.h"
|
|
#include "res.h"
|
|
|
|
|
|
#define MAX_DEF_HANDLES 64
|
|
|
|
struct dmap {
|
|
OCIDefine* defh[MAX_DEF_HANDLES];
|
|
union {
|
|
dvoid* v;
|
|
double* f;
|
|
int* i;
|
|
char* c;
|
|
OCIDate* o;
|
|
}pv[MAX_DEF_HANDLES];
|
|
dvoid* pval[MAX_DEF_HANDLES];
|
|
ub2 ilen[MAX_DEF_HANDLES];
|
|
sb2 ind[MAX_DEF_HANDLES];
|
|
ub2 len[MAX_DEF_HANDLES];
|
|
};
|
|
typedef struct dmap dmap_t;
|
|
|
|
|
|
/*
|
|
* Get and convert columns from a result. Define handlers and buffers
|
|
*/
|
|
static int get_columns(ora_con_t* con, db1_res_t* _r, OCIStmt* _c, dmap_t* _d)
|
|
{
|
|
OCIParam *param;
|
|
size_t tsz;
|
|
ub4 i, n;
|
|
sword status;
|
|
|
|
status = OCIAttrGet(_c, OCI_HTYPE_STMT, &n, NULL, OCI_ATTR_PARAM_COUNT,
|
|
con->errhp);
|
|
|
|
if (status != OCI_SUCCESS) {
|
|
LM_ERR("driver: %s\n", db_oracle_error(con, status));
|
|
return -1;
|
|
}
|
|
|
|
if (!n) {
|
|
LM_ERR("no columns\n");
|
|
return -2;
|
|
}
|
|
|
|
if (n >= MAX_DEF_HANDLES) {
|
|
LM_ERR("too many res. Rebuild with MAX_DEF_HANDLES >= %u\n", n);
|
|
return -3;
|
|
}
|
|
|
|
if (db_allocate_columns(_r, n) != 0) {
|
|
LM_ERR("could not allocate columns");
|
|
return -4;
|
|
}
|
|
memset(RES_NAMES(_r), 0, sizeof(db_key_t) * n);
|
|
|
|
RES_COL_N(_r) = n;
|
|
|
|
tsz = 0;
|
|
memset(_d->defh, 0, sizeof(_d->defh[0]) * n);
|
|
for (i = 0; i < n; i++) {
|
|
ub4 len;
|
|
ub2 dtype;
|
|
|
|
status = OCIParamGet(_c, OCI_HTYPE_STMT, con->errhp,
|
|
(dvoid**)(dvoid*)¶m, i+1);
|
|
if (status != OCI_SUCCESS) goto ora_err;
|
|
|
|
{
|
|
text* name;
|
|
str* sname;
|
|
status = OCIAttrGet(param, OCI_DTYPE_PARAM,
|
|
(dvoid**)(dvoid*)&name, &len, OCI_ATTR_NAME,
|
|
con->errhp);
|
|
if (status != OCI_SUCCESS) goto ora_err;
|
|
sname = (str*)pkg_malloc(sizeof(str)+len+1);
|
|
if (!sname) {
|
|
db_free_columns(_r);
|
|
LM_ERR("no private memory left\n");
|
|
return -5;
|
|
}
|
|
sname->len = len;
|
|
sname->s = (char*)sname + sizeof(str);
|
|
memcpy(sname->s, name, len);
|
|
sname->s[len] = '\0';
|
|
RES_NAMES(_r)[i] = sname;
|
|
}
|
|
|
|
status = OCIAttrGet(param, OCI_DTYPE_PARAM,
|
|
(dvoid**)(dvoid*)&dtype, NULL, OCI_ATTR_DATA_TYPE,
|
|
con->errhp);
|
|
if (status != OCI_SUCCESS) goto ora_err;
|
|
|
|
switch (dtype) {
|
|
case SQLT_UIN: /* unsigned integer */
|
|
set_bitmap:
|
|
LM_DBG("use DB1_BITMAP type");
|
|
RES_TYPES(_r)[i] = DB1_BITMAP;
|
|
len = sizeof(VAL_BITMAP((db_val_t*)NULL));
|
|
break;
|
|
|
|
case SQLT_INT: /* (ORANET TYPE) integer */
|
|
set_int:
|
|
LM_DBG("use DB1_INT result type");
|
|
RES_TYPES(_r)[i] = DB1_INT;
|
|
len = sizeof(VAL_INT((db_val_t*)NULL));
|
|
break;
|
|
|
|
// case SQLT_LNG: /* long */
|
|
case SQLT_VNU: /* NUM with preceding length byte */
|
|
case SQLT_NUM: /* (ORANET TYPE) oracle numeric */
|
|
len = 0; /* PRECISION is ub1 */
|
|
status = OCIAttrGet(param, OCI_DTYPE_PARAM,
|
|
(dvoid**)(dvoid*)&len, NULL, OCI_ATTR_PRECISION,
|
|
con->errhp);
|
|
if (status != OCI_SUCCESS) goto ora_err;
|
|
if (len <= 11) {
|
|
sb1 sc;
|
|
status = OCIAttrGet(param, OCI_DTYPE_PARAM,
|
|
(dvoid**)(dvoid*)&sc, NULL,
|
|
OCI_ATTR_SCALE, con->errhp);
|
|
if (status != OCI_SUCCESS) goto ora_err;
|
|
if (!sc) {
|
|
dtype = SQLT_INT;
|
|
if (len != 11) goto set_int;
|
|
dtype = SQLT_UIN;
|
|
goto set_bitmap;
|
|
}
|
|
}
|
|
case SQLT_FLT: /* (ORANET TYPE) Floating point number */
|
|
case SQLT_BFLOAT: /* Native Binary float*/
|
|
case SQLT_BDOUBLE: /* NAtive binary double */
|
|
case SQLT_IBFLOAT: /* binary float canonical */
|
|
case SQLT_IBDOUBLE: /* binary double canonical */
|
|
case SQLT_PDN: /* (ORANET TYPE) Packed Decimal Numeric */
|
|
LM_DBG("use DB1_DOUBLE result type");
|
|
RES_TYPES(_r)[i] = DB1_DOUBLE;
|
|
len = sizeof(VAL_DOUBLE((db_val_t*)NULL));
|
|
dtype = SQLT_FLT;
|
|
break;
|
|
|
|
// case SQLT_TIME: /* TIME */
|
|
// case SQLT_TIME_TZ: /* TIME WITH TIME ZONE */
|
|
case SQLT_DATE: /* ANSI Date */
|
|
case SQLT_DAT: /* date in oracle format */
|
|
case SQLT_ODT: /* OCIDate type */
|
|
case SQLT_TIMESTAMP: /* TIMESTAMP */
|
|
case SQLT_TIMESTAMP_TZ: /* TIMESTAMP WITH TIME ZONE */
|
|
case SQLT_TIMESTAMP_LTZ:/* TIMESTAMP WITH LOCAL TZ */
|
|
// case SQLT_INTERVAL_YM: /* INTERVAL YEAR TO MONTH */
|
|
// case SQLT_INTERVAL_DS: /* INTERVAL DAY TO SECOND */
|
|
LM_DBG("use DB1_DATETIME result type");
|
|
RES_TYPES(_r)[i] = DB1_DATETIME;
|
|
len = sizeof(OCIDate);
|
|
dtype = SQLT_ODT;
|
|
break;
|
|
|
|
case SQLT_CLOB: /* character lob */
|
|
case SQLT_BLOB: /* binary lob */
|
|
// case SQLT_BFILEE: /* binary file lob */
|
|
// case SQLT_CFILEE: /* character file lob */
|
|
// case SQLT_BIN: /* binary data(DTYBIN) */
|
|
// case SQLT_LBI: /* long binary */
|
|
LM_DBG("use DB1_BLOB result type");
|
|
RES_TYPES(_r)[i] = DB1_BLOB;
|
|
goto dyn_str;
|
|
|
|
case SQLT_CHR: /* (ORANET TYPE) character string */
|
|
case SQLT_STR: /* zero terminated string */
|
|
case SQLT_VST: /* OCIString type */
|
|
case SQLT_VCS: /* Variable character string */
|
|
case SQLT_AFC: /* Ansi fixed char */
|
|
case SQLT_AVC: /* Ansi Var char */
|
|
// case SQLT_RID: /* rowid */
|
|
LM_DBG("use DB1_STR result type");
|
|
RES_TYPES(_r)[i] = DB1_STR;
|
|
dyn_str:
|
|
dtype = SQLT_CHR;
|
|
len = 0; /* DATA_SIZE is ub2 */
|
|
status = OCIAttrGet(param, OCI_DTYPE_PARAM,
|
|
(dvoid**)(dvoid*)&len, NULL, OCI_ATTR_DATA_SIZE,
|
|
con->errhp);
|
|
if (status != OCI_SUCCESS) goto ora_err;
|
|
if (len >= 4000) {
|
|
LM_DBG("use DB1_BLOB result type");
|
|
RES_TYPES(_r)[i] = DB1_BLOB;
|
|
}
|
|
++len;
|
|
break;
|
|
|
|
default:
|
|
LM_ERR("unsupported datatype %d\n", dtype);
|
|
goto stop_load;
|
|
}
|
|
_d->ilen[i] = (ub2)len;
|
|
_d->pv[i].v = st_buf + tsz;
|
|
tsz += len;
|
|
status = OCIDefineByPos(_c, &_d->defh[i], con->errhp, i+1,
|
|
_d->pv[i].v, len, dtype, &_d->ind[i],
|
|
&_d->len[i], NULL, OCI_DEFAULT);
|
|
if (status != OCI_SUCCESS) goto ora_err;
|
|
}
|
|
|
|
#if STATIC_BUF_LEN < 65536
|
|
#error
|
|
#endif
|
|
if (tsz > 65536) {
|
|
LM_ERR("Row size exceed 65K. IOB's are not supported");
|
|
goto stop_load;
|
|
}
|
|
return 0;
|
|
|
|
ora_err:
|
|
LM_ERR("driver: %s\n", db_oracle_error(con, status));
|
|
stop_load:
|
|
db_free_columns(_r);
|
|
return -6;
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert data fron db format to internal format
|
|
*/
|
|
static int convert_row(db1_res_t* _res, db_row_t* _r, dmap_t* _d)
|
|
{
|
|
unsigned i, n = RES_COL_N(_res);
|
|
|
|
ROW_N(_r) = n;
|
|
ROW_VALUES(_r) = (db_val_t*)pkg_malloc(sizeof(db_val_t) * n);
|
|
if (!ROW_VALUES(_r)) {
|
|
nomem:
|
|
LM_ERR("no private memory left\n");
|
|
return -1;
|
|
}
|
|
memset(ROW_VALUES(_r), 0, sizeof(db_val_t) * n);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
static const str dummy_string = {"", 0};
|
|
|
|
db_val_t* v = &ROW_VALUES(_r)[i];
|
|
db_type_t t = RES_TYPES(_res)[i];
|
|
|
|
if (_d->ind[i] == -1) {
|
|
/* Initialize the string pointers to a dummy empty
|
|
* string so that we do not crash when the NULL flag
|
|
* is set but the module does not check it properly
|
|
*/
|
|
VAL_STRING(v) = dummy_string.s;
|
|
VAL_STR(v) = dummy_string;
|
|
VAL_BLOB(v) = dummy_string;
|
|
VAL_TYPE(v) = t;
|
|
VAL_NULL(v) = 1;
|
|
continue;
|
|
}
|
|
|
|
if (_d->ind[i])
|
|
LM_WARN("truncated value in DB\n");
|
|
|
|
VAL_TYPE(v) = t;
|
|
switch (t) {
|
|
case DB1_INT:
|
|
VAL_INT(v) = *_d->pv[i].i;
|
|
break;
|
|
|
|
case DB1_BIGINT:
|
|
LM_ERR("BIGINT not supported");
|
|
return -1;
|
|
|
|
case DB1_BITMAP:
|
|
VAL_BITMAP(v) = *_d->pv[i].i;
|
|
break;
|
|
|
|
case DB1_DOUBLE:
|
|
VAL_DOUBLE(v) = *_d->pv[i].f;
|
|
break;
|
|
|
|
case DB1_DATETIME:
|
|
{
|
|
struct tm tm;
|
|
memset(&tm, 0, sizeof(tm));
|
|
OCIDateGetTime(_d->pv[i].o, &tm.tm_hour,
|
|
&tm.tm_min, &tm.tm_sec);
|
|
OCIDateGetDate(_d->pv[i].o, &tm.tm_year,
|
|
&tm.tm_mon, &tm.tm_mday);
|
|
if (tm.tm_mon)
|
|
--tm.tm_mon;
|
|
if (tm.tm_year >= 1900)
|
|
tm.tm_year -= 1900;
|
|
VAL_TIME(v) = mktime(&tm);
|
|
}
|
|
break;
|
|
|
|
case DB1_STR:
|
|
case DB1_BLOB:
|
|
case DB1_STRING:
|
|
{
|
|
size_t len = _d->len[i];
|
|
char *pstr = pkg_malloc(len+1);
|
|
if (!pstr) goto nomem;
|
|
memcpy(pstr, _d->pv[i].c, len);
|
|
pstr[len] = '\0';
|
|
VAL_FREE(v) = 1;
|
|
if (t == DB1_STR) {
|
|
VAL_STR(v).s = pstr;
|
|
VAL_STR(v).len = len;
|
|
} else if (t == DB1_BLOB) {
|
|
VAL_BLOB(v).s = pstr;
|
|
VAL_BLOB(v).len = len;
|
|
} else {
|
|
VAL_STRING(v) = pstr;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
LM_ERR("unknown type mapping (%u)\n", t);
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get rows and convert it from oracle to db API representation
|
|
*/
|
|
static int get_rows(ora_con_t* con, db1_res_t* _r, OCIStmt* _c, dmap_t* _d)
|
|
{
|
|
ub4 rcnt;
|
|
sword status;
|
|
unsigned n = RES_COL_N(_r);
|
|
|
|
memcpy(_d->len, _d->ilen, sizeof(_d->len[0]) * n);
|
|
|
|
// timelimited operation
|
|
status = begin_timelimit(con, 0);
|
|
if (status != OCI_SUCCESS) goto ora_err;
|
|
do status = OCIStmtFetch2(_c, con->errhp, 1, OCI_FETCH_LAST, 0,
|
|
OCI_DEFAULT);
|
|
while (wait_timelimit(con, status));
|
|
if (done_timelimit(con, status)) goto stop_load;
|
|
if (status != OCI_SUCCESS) {
|
|
if (status != OCI_NO_DATA)
|
|
goto ora_err;
|
|
|
|
RES_ROW_N(_r) = 0;
|
|
RES_ROWS(_r) = NULL;
|
|
return 0;
|
|
}
|
|
|
|
status = OCIAttrGet(_c, OCI_HTYPE_STMT, &rcnt, NULL,
|
|
OCI_ATTR_CURRENT_POSITION, con->errhp);
|
|
if (status != OCI_SUCCESS) goto ora_err;
|
|
if (!rcnt) {
|
|
LM_ERR("lastpos==0\n");
|
|
goto stop_load;
|
|
}
|
|
|
|
RES_ROW_N(_r) = rcnt;
|
|
RES_ROWS(_r) = (db_row_t*)pkg_malloc(sizeof(db_row_t) * rcnt);
|
|
if (!RES_ROWS(_r)) {
|
|
LM_ERR("no private memory left\n");
|
|
return -1;
|
|
}
|
|
memset(RES_ROWS(_r), 0, sizeof(db_row_t) * rcnt);
|
|
|
|
while ( 1 ) {
|
|
if (convert_row(_r, &RES_ROWS(_r)[--rcnt], _d) < 0) {
|
|
LM_ERR("erroc convert row\n");
|
|
goto stop_load;
|
|
}
|
|
|
|
if (!rcnt)
|
|
return 0;
|
|
|
|
memcpy(_d->len, _d->ilen, sizeof(_d->len[0]) * n);
|
|
// timelimited operation
|
|
status = begin_timelimit(con, 0);
|
|
if (status != OCI_SUCCESS) goto ora_err;
|
|
do status = OCIStmtFetch2(_c, con->errhp, 1, OCI_FETCH_PRIOR, 0,
|
|
OCI_DEFAULT);
|
|
while (wait_timelimit(con, status));
|
|
if (done_timelimit(con, status)) goto stop_load;
|
|
if (status != OCI_SUCCESS) break;
|
|
}
|
|
ora_err:
|
|
LM_ERR("driver: %s\n", db_oracle_error(con, status));
|
|
stop_load:
|
|
db_free_rows(_r);
|
|
RES_ROW_N(_r) = 0; /* TODO: skipped in db_res.c :) */
|
|
return -3;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read database answer and fill the structure
|
|
*/
|
|
int db_oracle_store_result(const db1_con_t* _h, db1_res_t** _r)
|
|
{
|
|
dmap_t dmap;
|
|
int rc;
|
|
db1_res_t* r;
|
|
ora_con_t* con;
|
|
OCIStmt* hs;
|
|
|
|
if (!_h || !_r) {
|
|
badparam:
|
|
LM_ERR("invalid parameter\n");
|
|
return -1;
|
|
}
|
|
|
|
con = CON_ORA(_h);
|
|
{
|
|
query_data_t *pcb = con->pqdata;
|
|
|
|
|
|
if (!pcb || !pcb->_rs)
|
|
goto badparam;
|
|
|
|
hs = *pcb->_rs;
|
|
pcb->_rs = NULL; /* paranoid for next call */
|
|
}
|
|
|
|
rc = -1;
|
|
if (_r) *_r = NULL; /* unification for all errors */
|
|
|
|
r = db_new_result();
|
|
if (!r) {
|
|
LM_ERR("no memory left\n");
|
|
goto done;
|
|
}
|
|
|
|
if (get_columns(con, r, hs, &dmap) < 0) {
|
|
LM_ERR("error while getting column names\n");
|
|
goto done;
|
|
}
|
|
|
|
if (get_rows(con, r, hs, &dmap) < 0) {
|
|
LM_ERR("error while converting rows\n");
|
|
db_free_columns(r);
|
|
goto done;
|
|
}
|
|
|
|
rc = 0;
|
|
*_r = r;
|
|
done:
|
|
OCIHandleFree(hs, OCI_HTYPE_STMT);
|
|
return rc;
|
|
}
|