123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- /*
- * $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;
- }
|