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.
519 lines
12 KiB
519 lines
12 KiB
/*
|
|
* $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);
|
|
}
|