123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- /*
- * $Id$
- *
- * Oracle module core 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 <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <time.h>
- #include <oci.h>
- #include "../../mem/mem.h"
- #include "../../dprint.h"
- #include "../../lib/srdb1/db_pool.h"
- #include "../../lib/srdb1/db_ut.h"
- #include "../../lib/srdb1/db_res.h"
- #include "../../lib/srdb1/db_query.h"
- #include "val.h"
- #include "ora_con.h"
- #include "res.h"
- #include "asynch.h"
- #include "dbase.h"
- #define MAX_BIND_HANDLES 128
- char st_buf[STATIC_BUF_LEN];
- /*
- * Make error message. Always return negative value
- */
- int sql_buf_small(void)
- {
- LM_ERR("static buffer too small\n");
- return -11;
- }
- /*
- * Decode error
- */
- static char errbuf[512];
- static const char* db_oracle_errorinfo(ora_con_t* con)
- {
- sword errcd;
- if (OCIErrorGet(con->errhp, 1, NULL, &errcd,
- (OraText*)errbuf, sizeof(errbuf),
- OCI_HTYPE_ERROR) != OCI_SUCCESS) errbuf[0] = '\0';
- else switch (errcd) {
- case 28: /* your session has been killed */
- case 30: /* session ID does not exists */
- case 31: /* session marked for kill */
- case 41: /* active time limit exceeded session terminated */
- case 107: /* failed to connect to oracle listener */
- case 115: /* connection refused; dispatcher table is full */
- case 1033: /* init/shutdown in progress */
- case 1034: /* not available (startup) */
- case 1089: /* server shutdown */
- case 1090: /* shutdown wait after command */
- case 1092: /* oracle instance terminated. Disconnection forced */
- case 1573: /* shutdown instance, no futher change allowed */
- case 2049: /* timeout: distributed transaction waiting lock */
- case 3113: /* EOF on communication channel */
- case 3114: /* not connected */
- case 3135: /* lost connection */
- case 6033: /* connect failed, partner rejected connection */
- case 6034: /* connect failed, partner exited unexpectedly */
- case 6037: /* connect failed, node unrecheable */
- case 6039: /* connect failed */
- case 6042: /* msgrcv failure (DNT) */
- case 6043: /* msgsend failure (DNT) */
- case 6107: /* network server not found */
- case 6108: /* connect to host failed */
- case 6109: /* msgrcv failure (TCP) */
- case 6110: /* msgsend failure (TCP) */
- case 6114: /* SID lookup failure */
- case 6124: /* TCP timeout */
- case 6135: /* connect rejected; server is stopping (TCP) */
- case 6144: /* SID unavaliable (TCP) */
- case 6413: /* connection not open */
- case 12150: /* tns can't send data, probably disconnect */
- case 12152: /* tns unable to send break message */
- case 12153: /* tns not connected */
- case 12161: /* tns internal error */
- case 12170: /* tns connect timeout */
- case 12224: /* tns no listener */
- case 12225: /* tns destination host unrecheable */
- case 12230: /* tns network error */
- case 12525: /* tns (internal) timeout */
- case 12521: /* tns can't resolve db name */
- case 12537: /* tns connection cloed */
- case 12541: /* tns not running */
- case 12543: /* tns destination host unrecheable */
- case 12547: /* tns lost contact */
- case 12560: /* tns protocol(transport) error */
- case 12561: /* tns unknown error */
- case 12608: /* tns send timeount */
- case 12609: /* tns receive timeount */
- LM_ERR("conneciom dropped\n");
- db_oracle_disconnect(con);
- default:
- break;
- }
- return errbuf;
- }
- const char* db_oracle_error(ora_con_t* con, sword status)
- {
- switch (status) {
- case OCI_SUCCESS:
- return "internal (success)";
- case OCI_SUCCESS_WITH_INFO:
- case OCI_ERROR:
- return db_oracle_errorinfo(con);
- case OCI_NEED_DATA:
- return "need data";
- case OCI_NO_DATA:
- return "no data";
- case OCI_INVALID_HANDLE:
- return "invalid handle";
- case OCI_STILL_EXECUTING: // ORA-3123
- return "executing (logic)";
- case OCI_CONTINUE:
- return "continue (library)";
- default:
- snprintf(errbuf, sizeof(errbuf),
- "unknown status %u", status);
- return errbuf;
- }
- }
- /*
- * Initialize database module
- * No function should be called before this
- */
- db1_con_t* db_oracle_init(const str* _url)
- {
- return db_do_init(_url, (void *)db_oracle_new_connection);
- }
- /*
- * Shut down database module
- * No function should be called after this
- */
- void db_oracle_close(db1_con_t* _h)
- {
- db_do_close(_h, db_oracle_free_connection);
- }
- /*
- * Release a result set from memory
- */
- int db_oracle_free_result(db1_con_t* _h, db1_res_t* _r)
- {
- if (!_h || !_r) {
- LM_ERR("invalid parameter value\n");
- return -1;
- }
- if (db_free_result(_r) < 0)
- {
- LM_ERR("failed to free result structure\n");
- return -1;
- }
- return 0;
- }
- /*
- * Send an SQL query to the server
- */
- static int db_oracle_submit_query(const db1_con_t* _h, const str* _s)
- {
- OCIBind* bind[MAX_BIND_HANDLES];
- OCIDate odt[sizeof(bind)/sizeof(bind[0])];
- str tmps;
- sword status;
- int pass;
- ora_con_t* con = CON_ORA(_h);
- query_data_t* pqd = con->pqdata;
- size_t hc = pqd->_n + pqd->_nw;
- OCIStmt *stmthp;
- if (hc >= sizeof(bind)/sizeof(bind[0])) {
- LM_ERR("too many bound. Rebuild with MAX_BIND_HANDLES >= %u\n",
- (unsigned)hc);
- return -1;
- }
-
- if (!pqd->_rs) {
- /*
- * This method is at ~25% faster as set OCI_COMMIT_ON_SUCCESS
- * in StmtExecute
- */
- tmps.len = snprintf(st_buf, sizeof(st_buf),
- "begin %.*s; commit write batch nowait; end;",
- _s->len, _s->s);
- if ((unsigned)tmps.len >= sizeof(st_buf))
- return sql_buf_small();
- tmps.s = st_buf;
- _s = &tmps;
- }
- pass = 1;
- if (!con->connected) {
- status = db_oracle_reconnect(con);
- if (status != OCI_SUCCESS) {
- LM_ERR("can't restore connection: %s\n", db_oracle_error(con, status));
- return -2;
- }
- LM_INFO("connection restored\n");
- --pass;
- }
- repeat:
- stmthp = NULL;
- status = OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&stmthp,
- OCI_HTYPE_STMT, 0, NULL);
- if (status != OCI_SUCCESS)
- goto ora_err;
- status = OCIStmtPrepare(stmthp, con->errhp, (text*)_s->s, _s->len,
- OCI_NTV_SYNTAX, OCI_DEFAULT);
- if (status != OCI_SUCCESS)
- goto ora_err;
- if (hc) {
- bmap_t bmap;
- size_t pos = 1;
- int i;
- memset(bind, 0, hc*sizeof(bind[0]));
- for (i = 0; i < pqd->_n; i++) {
- if (db_oracle_val2bind(&bmap, &pqd->_v[i], &odt[pos]) < 0)
- goto bind_err;
- status = OCIBindByPos(stmthp, &bind[pos], con->errhp,
- pos, bmap.addr, bmap.size, bmap.type,
- NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
- if (status != OCI_SUCCESS)
- goto ora_err;
- ++pos;
- }
- for (i = 0; i < pqd->_nw; i++) {
- if (db_oracle_val2bind(&bmap, &pqd->_w[i], &odt[pos]) < 0) {
- bind_err:
- OCIHandleFree(stmthp, OCI_HTYPE_STMT);
- LM_ERR("can't map values\n");
- return -3;
- }
- status = OCIBindByPos(stmthp, &bind[pos], con->errhp,
- pos, bmap.addr, bmap.size, bmap.type,
- NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
- if (status != OCI_SUCCESS)
- goto ora_err;
- ++pos;
- }
- }
- // timelimited operation
- status = begin_timelimit(con, 0);
- if (status != OCI_SUCCESS) goto ora_err;
- do status = OCIStmtExecute(con->svchp, stmthp, con->errhp,
- !pqd->_rs, 0, NULL, NULL,
- pqd->_rs ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT);
- while (wait_timelimit(con, status));
- if (done_timelimit(con, status)) goto stop_exec;
- switch (status) {
- case OCI_SUCCESS_WITH_INFO:
- LM_WARN("driver: %s\n", db_oracle_errorinfo(con));
- //PASS THRU
- case OCI_SUCCESS:
- if (pqd->_rs)
- *pqd->_rs = stmthp;
- else
- OCIHandleFree(stmthp, OCI_HTYPE_STMT);
- return 0;
- default:
- pass = -pass;
- break;
- }
- ora_err:
- LM_ERR("driver: %s\n", db_oracle_error(con, status));
- stop_exec:
- if (stmthp)
- OCIHandleFree(stmthp, OCI_HTYPE_STMT);
- if (pass == -1 && !con->connected) {
- /* Attemtp to reconnect */
- if (db_oracle_reconnect(con) == OCI_SUCCESS) {
- LM_NOTICE("attempt repeat after reconnect\n");
- pass = 0;
- goto repeat;
- }
- LM_ERR("connection loss\n");
- }
- return -4;
- }
- /*
- * Query table for specified rows
- * _h: structure representing database connection
- * _k: key names
- * _op: operators
- * _v: values of the keys that must match
- * _c: column names to return
- * _n: number of key=values pairs to compare
- * _nc: number of columns to return
- * _o: order by the specified column
- */
- int db_oracle_query(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _op,
- const db_val_t* _v, const db_key_t* _c, int _n, int _nc,
- const db_key_t _o, db1_res_t** _r)
- {
- query_data_t cb;
- OCIStmt* reshp;
- int rc;
- if (!_h || !CON_TABLE(_h) || !_r) {
- LM_ERR("invalid parameter value\n");
- return -1;
- }
- cb._rs = &reshp;
- cb._v = _v;
- cb._n = _n;
- cb._w = NULL;
- cb._nw = 0;
- CON_ORA(_h)->pqdata = &cb;
- CON_ORA(_h)->bindpos = 0;
- rc = db_do_query(_h, _k, _op, _v, _c, _n, _nc, _o, _r,
- db_oracle_val2str, db_oracle_submit_query, db_oracle_store_result);
- CON_ORA(_h)->pqdata = NULL; /* paranoid for next call */
- return rc;
- }
- /*
- * Execute a raw SQL query
- */
- int db_oracle_raw_query(const db1_con_t* _h, const str* _s, db1_res_t** _r)
- {
- query_data_t cb;
- OCIStmt* reshp;
- int len;
- const char *p;
- if (!_h || !_s || !_s->s) {
- badparam:
- LM_ERR("invalid parameter value\n");
- return -1;
- }
- memset(&cb, 0, sizeof(cb));
- p = _s->s;
- len = _s->len;
- while (len && *p == ' ') ++p, --len;
- #define _S_DIFF(p, l, S) (l <= sizeof(S)-1 || strncasecmp(p, S, sizeof(S)-1))
- if (!_S_DIFF(p, len, "select ")) {
- if (!_r) goto badparam;
- cb._rs = &reshp;
- } else {
- if ( _S_DIFF(p, len, "insert ")
- && _S_DIFF(p, len, "delete ")
- && _S_DIFF(p, len, "update "))
- {
- LM_ERR("unknown raw_query: '%.*s'\n", _s->len, _s->s);
- return -2;
- }
- #undef _S_DIFF
- if (_r) goto badparam;
- cb._rs = NULL;
- }
- len = db_do_raw_query(_h, _s, _r, db_oracle_submit_query, db_oracle_store_result);
- CON_ORA(_h)->pqdata = NULL; /* paranoid for next call */
- return len;
- }
- /*
- * Insert a row into specified table
- * _h: structure representing database connection
- * _k: key names
- * _v: values of the keys
- * _n: number of key=value pairs
- */
- int db_oracle_insert(const db1_con_t* _h, const db_key_t* _k, const db_val_t* _v,
- int _n)
- {
- query_data_t cb;
- int rc;
- if (!_h || !CON_TABLE(_h)) {
- LM_ERR("invalid parameter value\n");
- return -1;
- }
- cb._rs = NULL;
- cb._v = _v;
- cb._n = _n;
- cb._w = NULL;
- cb._nw = 0;
- CON_ORA(_h)->pqdata = &cb;
- CON_ORA(_h)->bindpos = 0;
- rc = db_do_insert(_h, _k, _v, _n, db_oracle_val2str, db_oracle_submit_query);
- CON_ORA(_h)->pqdata = NULL; /* paranoid for next call */
- return rc;
- }
- /*
- * Delete a row from the specified table
- * _h: structure representing database connection
- * _k: key names
- * _o: operators
- * _v: values of the keys that must match
- * _n: number of key=value pairs
- */
- int db_oracle_delete(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _o,
- const db_val_t* _v, int _n)
- {
- query_data_t cb;
- int rc;
- if (!_h || !CON_TABLE(_h)) {
- LM_ERR("invalid parameter value\n");
- return -1;
- }
- cb._rs = NULL;
- cb._v = _v;
- cb._n = _n;
- cb._w = NULL;
- cb._nw = 0;
- CON_ORA(_h)->pqdata = &cb;
- CON_ORA(_h)->bindpos = 0;
- rc = db_do_delete(_h, _k, _o, _v, _n, db_oracle_val2str, db_oracle_submit_query);
- CON_ORA(_h)->pqdata = NULL; /* paranoid for next call */
- return rc;
- }
- /*
- * Update some rows in the specified table
- * _h: structure representing database connection
- * _k: key names
- * _o: operators
- * _v: values of the keys that must match
- * _uk: updated columns
- * _uv: updated values of the columns
- * _n: number of key=value pairs
- * _un: number of columns to update
- */
- int db_oracle_update(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _o,
- const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv,
- int _n, int _un)
- {
- query_data_t cb;
- int rc;
-
- if (!_h || !CON_TABLE(_h)) {
- LM_ERR("invalid parameter value\n");
- return -1;
- }
- cb._rs = NULL;
- cb._v = _uv;
- cb._n = _un;
- cb._w = _v;
- cb._nw = _n;
- CON_ORA(_h)->pqdata = &cb;
- CON_ORA(_h)->bindpos = 0;
- rc = db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un,
- db_oracle_val2str, db_oracle_submit_query);
- CON_ORA(_h)->pqdata = NULL; /* paranoid for next call */
- return rc;
- }
- /*
- * Store name of table that will be used by
- * subsequent database functions
- */
- int db_oracle_use_table(db1_con_t* _h, const str* _t)
- {
- return db_use_table(_h, _t);
- }
|