Blob Blame History Raw
From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=A1=D0=B0=D0=BC?=
 =?UTF-8?q?=D1=83=D1=81=D0=B5=D0=BD=D0=BA=D0=BE?= <samusenko@msm.ru>
Date: Mon, 25 Jun 2012 17:54:27 +0400
Subject: [PATCH] Removed all async Oracle operations - they didn't work well
 anyway


diff --git a/modules/db_oracle/asynch.c b/modules/db_oracle/asynch.c
deleted file mode 100644
index d1bbe84..0000000
--- a/modules/db_oracle/asynch.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * $Id$
- *
- * Oracle module interface
- *
- * Copyright (C) 2007,2008 TRUNK MOBILE
- *
- * This file is part of opensips, a free SIP server.
- *
- * opensips 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
- *
- * opensips 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-/*
- * History:
- * --------
- */
-
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/time.h>
-#include <oci.h>
-#include "../../dprint.h"
-#include "../../sr_module.h"
-#include "ora_con.h"
-#include "asynch.h"
-
-#define MAX_TIMEOUT_S  10
-#define MIN_TIMEOUT_MS 100
-
-
-/* Default is 3.0 second */
-static struct timeval request_tm = { .tv_sec = 3, .tv_usec = 0 };
-
-/* Default is 0.2 second */
-static struct timeval restore_tm       = { .tv_sec = 0, .tv_usec = 200*1000 };
-static const struct timeval defrest_tm = { .tv_sec = 0, .tv_usec = 200*1000 };
-
-static int synch_mode;
-static int cur_asynch_mode;
-static struct timeval wtm;
-
-
-static __inline__ int is_zero_tm(const struct timeval* tv)
-{
-	return !tv->tv_usec && !tv->tv_sec;
-}
-
-
-/*
- * parse timeout value in syntax: nnn.mmm (sec/ms)
- */
-static int set_tv(unsigned type, const char* val, struct timeval* tv)
-{
-	char *eptr;
-	unsigned long s, ms;
-	double dv;
-
-	if (type != STR_PARAM) {
-		LM_ERR("type of parameter is no STR\n");
-		return -1;
-	}
-
-	if (!val || !*val) {
-		LM_ERR("empty parameter\n");
-		return -1;
-	}
-
-	errno = 0;
-	dv = strtod(val, &eptr);
-
-	if (*eptr) {
-		LM_ERR("invalid parameter string\n");
-		return -2;
-	}
-
-	if (   errno
-	    || dv > (double)MAX_TIMEOUT_S
-	    || (dv && dv < ((double)MIN_TIMEOUT_MS)/1000))
-	{
-		LM_ERR("value must be between 0.%u and %u.0\n",
-			MIN_TIMEOUT_MS, MAX_TIMEOUT_S);
-		return -3;
-	}
-
-	s = (unsigned)dv;
-	dv -= (double)s;
-	ms = (unsigned)(dv * 1000);
-	tv->tv_sec = (time_t)s;
-	tv->tv_usec = (suseconds_t)ms;
-	return 0;
-}
-
-
-/*
- * set operation timeout
- */
-int set_timeout(unsigned type, const char* val)
-{
-	int rc = set_tv(type, val, &request_tm);
-
-	if (!rc) {
-		synch_mode = is_zero_tm(&request_tm);
-		if (!synch_mode && is_zero_tm(&restore_tm))
-			restore_tm = defrest_tm;
-	}
-
-	return rc;
-}
-
-
-/*
- * set (re)connect timeout
- */
-int set_reconnect(unsigned type, const char* val)
-{
-	int rc = set_tv(type, val, &restore_tm);
-
-	if (!synch_mode && is_zero_tm(&restore_tm)) {
-		LM_WARN("in asyncronus mode reconnect time can't be zero. "
-			"Set default value\n");
-		restore_tm = defrest_tm;
-	}
-
-	return rc;
-}
-
-
-static sword change_mode(ora_con_t* con)
-{
-	return OCIAttrSet(con->svchp, OCI_HTYPE_SVCCTX, NULL, 0,
-		OCI_ATTR_NONBLOCKING_MODE, con->errhp);
-}
-
-
-/*
- * start timelimited operation (if work in synch mode return SUCCESS)
- */
-sword begin_timelimit(ora_con_t* con, int connect)
-{
-	struct timeval* tv;
-	sword status;
-
-	if (synch_mode)
-		return OCI_SUCCESS;
-
-	if (connect || cur_asynch_mode) {
-		ub1 mode;
-
-		status = OCIAttrGet(con->svchp, OCI_HTYPE_SVCCTX, &mode, NULL,
-			OCI_ATTR_NONBLOCKING_MODE, con->errhp);
-		if (status != OCI_SUCCESS)
-			return status;
-
-		if (mode) {
-			status = change_mode(con);
-			if (status != OCI_SUCCESS)
-				return status;
-		}
-		cur_asynch_mode = 0;
-	}
-
-	status = change_mode(con);
-	if (status != OCI_SUCCESS && connect >= 0)
-		return status;
-
-	cur_asynch_mode = 1;
-
-	gettimeofday(&wtm, NULL);
-	tv = &request_tm;
-	if (connect)
-		tv = &restore_tm;
-	wtm.tv_sec += tv->tv_sec;
-	wtm.tv_usec += tv->tv_usec;
-	if (wtm.tv_usec >= 1000000) {
-		wtm.tv_usec -= 1000000;
-		++wtm.tv_sec;
-	}
-
-	return OCI_SUCCESS;
-}
-
-
-static sword remap_status(ora_con_t* con, sword status)
-{
-	sword code;
-
-	if (   status == OCI_ERROR
-	    && OCIErrorGet(con->errhp, 1, NULL, &code,
-			NULL, 0, OCI_HTYPE_ERROR) == OCI_SUCCESS
-	    && (code == 3123 /*|| code == 3127*/))
-	{
-		status = OCI_STILL_EXECUTING;
-	}
-	return status;
-}
-
-
-/*
- * check completion of timelimited operation (if work in synch mode return 0)
- */
-int wait_timelimit(ora_con_t* con, sword status)
-{
-	struct timeval cur;
-
-	if (!cur_asynch_mode)
-		return 0;
-
-	if (remap_status(con, status) != OCI_STILL_EXECUTING)
-		return 0;
-
-	gettimeofday(&cur, NULL);
-	return (   cur.tv_sec < wtm.tv_sec
-		|| (cur.tv_sec == wtm.tv_sec && cur.tv_usec < wtm.tv_usec));
-}
-
-
-/*
- * close current timelimited operation and disconnect if timeout occured
- * return true only if work in asynch mode and timeout detect
- */
-int done_timelimit(ora_con_t* con, sword status)
-{
-	int ret = 0;
-
-	if (!cur_asynch_mode)
-		return 0;
-
-	if (remap_status(con, status) == OCI_STILL_EXECUTING) {
-		sword code;
-
-		status = OCIBreak(con->svchp, con->errhp);
-		if (status != OCI_SUCCESS)
-			LM_ERR("driver: %s\n",
-				db_oracle_error(con, status));
-
-		status = OCIReset(con->svchp, con->errhp);
-		if (   status == OCI_ERROR
-		    && OCIErrorGet(con->errhp, 1, NULL, &code,
-			    NULL, 0, OCI_HTYPE_ERROR) == OCI_SUCCESS
-		    && code == 1013)
-		{
-			status = OCI_SUCCESS;
-		}
-		if (status != OCI_SUCCESS)
-			LM_ERR("driver: %s\n",
-				db_oracle_error(con, status));
-		db_oracle_disconnect(con);
-		++ret;
-	} else {
-		status = change_mode(con);
-		if (status != OCI_SUCCESS) {
-			LM_ERR("driver: %s\n", db_oracle_error(con, status));
-			++ret;
-		} else {
-			cur_asynch_mode = 0;
-		}
-	}
-	return ret;
-}
diff --git a/modules/db_oracle/asynch.h b/modules/db_oracle/asynch.h
deleted file mode 100644
index 9694088..0000000
--- a/modules/db_oracle/asynch.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * $Id$
- *
- * Oracle module interface
- *
- * Copyright (C) 2007,2008 TRUNK MOBILE
- *
- * This file is part of opensips, a free SIP server.
- *
- * opensips 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
- *
- * opensips 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * History:
- * --------
- */
-
-#ifndef ASYNCH_H
-#define ASYNCH_H
-
-#include <oci.h>
-#include "ora_con.h"
-
-
-/*
- * parse timeout value for operation in syntax: nnn.mmm (sec/ms)
- */
-int set_timeout(unsigned type, const char* val);
-
-/*
- * parse timeout value for reconnect in syntax: nnn.mmm (sec/ms)
- */
-int set_reconnect(unsigned type, const char* val);
-
-
-/*
- * start timelimited operation (if work in synch mode return SUCCESS)
- */
-sword begin_timelimit(ora_con_t* con, int connect);
-
-/*
- * check completion of timelimited operation (if work in synch mode return 0)
- */
-int wait_timelimit(ora_con_t* con, sword status);
-
-/*
- * close current timelimited operation and disconnect if timeout occured
- * return true only if work in asynch mode and timeout detect
- */
-int done_timelimit(ora_con_t* con, sword status);
-
-#endif /* ASYNCH_H */
diff --git a/modules/db_oracle/db_oracle.c b/modules/db_oracle/db_oracle.c
index 70cfe40..40c7692 100644
--- a/modules/db_oracle/db_oracle.c
+++ b/modules/db_oracle/db_oracle.c
@@ -31,7 +31,7 @@
 #include "../../sr_module.h"
 #include "../../db/db.h"
 #include "dbase.h"
-#include "asynch.h"
+#include "timer.h"
 
 static int oracle_mod_init(void);
 static void destroy(void);
diff --git a/modules/db_oracle/dbase.c b/modules/db_oracle/dbase.c
index d129e11..be2352e 100644
--- a/modules/db_oracle/dbase.c
+++ b/modules/db_oracle/dbase.c
@@ -36,7 +36,7 @@
 #include "val.h"
 #include "ora_con.h"
 #include "res.h"
-#include "asynch.h"
+#include "timer.h"
 #include "dbase.h"
 
 
@@ -212,7 +212,6 @@ static int db_oracle_submit_query(const db_con_t* _h, const str* _s)
 	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;
@@ -223,32 +222,16 @@ static int db_oracle_submit_query(const db_con_t* _h, const str* _s)
 			(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) {
+	if (con->connected != 2) {
 		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);
@@ -291,14 +274,10 @@ bind_err:
 		}
 	}
 
-	// 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;
+	request_timer();
+	status = OCIStmtExecute(con->svchp, stmthp, con->errhp,
+		!pqd->_rs, 0, NULL, NULL, pqd->_rs ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS);
+	timer_stop();
 	switch (status)	{
 	case OCI_SUCCESS_WITH_INFO:
 		LM_WARN("driver: %s\n", db_oracle_errorinfo(con));
@@ -310,8 +289,8 @@ bind_err:
 			OCIHandleFree(stmthp, OCI_HTYPE_STMT);
 		return 0;
 	default:
-	    pass = -pass;
-	    break;
+		LM_ERR("timeout/not connected to oracle\n");
+		break;
 	}
 
 ora_err:
@@ -319,15 +298,13 @@ ora_err:
 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_INFO("reconnecting to oracle...\n");
+	/* Attempt to reconnect */
+	if (db_oracle_reconnect(con) == OCI_SUCCESS)
+		LM_INFO("connection restored\n");
+	else
 		LM_ERR("connection loss\n");
-	}
+
 	return -4;
 }
 
diff --git a/modules/db_oracle/ora_con.c b/modules/db_oracle/ora_con.c
index 73ced27..f03f753 100644
--- a/modules/db_oracle/ora_con.c
+++ b/modules/db_oracle/ora_con.c
@@ -22,9 +22,10 @@
 
 #include <string.h>
 #include <stdio.h>
+#include <errno.h>
 #include "../../mem/mem.h"
 #include "../../dprint.h"
-#include "asynch.h"
+#include "timer.h"
 #include "ora_con.h"
 
 /*************************************************************************/
@@ -105,6 +106,12 @@ bad_param:
 	status = OCIAttrSet(con->svchp, OCI_HTYPE_SVCCTX, con->authp, 0,
                    OCI_ATTR_SESSION, con->errhp);
 	if (status != OCI_SUCCESS) goto connect_err;
+
+        if (init_ora_timer(con) != 0) {
+           LM_ERR("can't start oracle timer thread\n");
+           goto drop_connection;
+        }
+
 	status = db_oracle_reconnect(con);
 	if (status != OCI_SUCCESS) {
 connect_err:
@@ -122,12 +129,11 @@ drop_connection:
 	}
 
 	// timelimited operation
-	status = begin_timelimit(con, 0);
-	if (status != OCI_SUCCESS) goto connect_err;
-	do status = OCIServerVersion(con->svchp, con->errhp, (OraText*)buf,
+	request_timer();
+	status = OCIServerVersion(con->svchp, con->errhp, (OraText*)buf,
 		(ub4)sizeof(buf), OCI_HTYPE_SVCCTX);
-	while (wait_timelimit(con, status));
-	if (done_timelimit(con, status)) goto drop_connection;
+	timer_stop();
+	
 	if (status != OCI_SUCCESS) goto connect_err;
 	LM_INFO("server version is %s\n", buf);
 	return con;
@@ -139,6 +145,7 @@ drop_connection:
  */
 void db_oracle_free_connection(ora_con_t* con)
 {
+	destroy_ora_timer(con);
 	if (!con) return;
 
 	if (con->connected)
@@ -164,20 +171,17 @@ void db_oracle_free_connection(ora_con_t* con)
 void db_oracle_disconnect(ora_con_t* con)
 {
 	sword status;
-
-	switch (con->connected) {
-	default:
-		status = OCISessionEnd(con->svchp, con->errhp, con->authp,
-			OCI_DEFAULT);
-		if (status != OCI_SUCCESS)
-			LM_ERR("driver: %s\n", db_oracle_error(con, status));
-	case 1:
+	if (con->connected > 0) {
+		if (con->connected > 1) {
+			status = OCISessionEnd(con->svchp, con->errhp, con->authp,
+				OCI_DEFAULT);
+			if (status != OCI_SUCCESS)
+				LM_ERR("driver: %s\n", db_oracle_error(con, status));
+		}
 		status = OCIServerDetach(con->srvhp, con->errhp, OCI_DEFAULT);
 		if (status != OCI_SUCCESS)
 			LM_ERR("driver: %s\n", db_oracle_error(con, status));
 		con->connected = 0;
-	case 0:
-		break;
 	}
 }
 
@@ -192,48 +196,19 @@ sword db_oracle_reconnect(ora_con_t* con)
 	if (con->connected)
 		db_oracle_disconnect(con);
 
-	/* timelimited operation, but OCI tcp-network does not support it :( */
+	restore_timer();
 	status = OCIServerAttach(con->srvhp, con->errhp, (OraText*)con->uri,
 		con->uri_len, 0);
+	timer_stop();
 	if (status == OCI_SUCCESS) {
 		++con->connected;
-		/*
-		 * timelimited operation, but OCI has BUG in asynch
-		 * implementation of OCISessionBegin :(.
-		 *
-		 * Next code is 'empiric hack' that work (tested) in v10/v11.
-		 */
-		status = begin_timelimit(con, 1);
-		if (status != OCI_SUCCESS) goto done;
+		request_timer();
 		status = OCISessionBegin(con->svchp, con->errhp, con->authp,
 			OCI_CRED_RDBMS, OCI_DEFAULT);
-		while (wait_timelimit(con, status)) {
-			sword code;
-
-			status = OCIServerVersion(con->svchp, con->errhp, NULL,
-				0, OCI_HTYPE_SVCCTX);
-
-			if (   status != OCI_ERROR
-			    || OCIErrorGet(con->errhp, 1, NULL, &code, NULL, 0,
-				     OCI_HTYPE_ERROR) != OCI_SUCCESS) break;
-			switch (code) {
-			case 24909:	/* other call in progress */
-				status = OCI_STILL_EXECUTING;
-				continue;
-
-			case 3127:	/* no new operation until active ends */
-				status = OCISessionBegin(con->svchp, con->errhp,
-					con->authp, OCI_CRED_RDBMS, OCI_DEFAULT);
-			default:
-				break;
-			}
-			break;
-		}
-		if (done_timelimit(con, status)) goto done;
+		timer_stop();
 
 		if (status == OCI_SUCCESS)
 			++con->connected;
 	}
-done:
 	return status;
 }
diff --git a/modules/db_oracle/ora_con.h b/modules/db_oracle/ora_con.h
index 5835ffe..7267bee 100644
--- a/modules/db_oracle/ora_con.h
+++ b/modules/db_oracle/ora_con.h
@@ -50,7 +50,8 @@ struct ora_con {
 
 	int connected;		/* Authorized session started */
 	int bindpos;		/* Last Bind handle position */
-	
+	pthread_t timer;
+
 	query_data_t* pqdata;	/* Temporary: cb data for submit_query/store_result */
 
 	int  uri_len;
diff --git a/modules/db_oracle/res.c b/modules/db_oracle/res.c
index e9e6233..2c625f8 100644
--- a/modules/db_oracle/res.c
+++ b/modules/db_oracle/res.c
@@ -32,7 +32,7 @@
 #include "../../dprint.h"
 #include "ora_con.h"
 #include "dbase.h"
-#include "asynch.h"
+#include "timer.h"
 #include "res.h"
 
 
@@ -54,6 +54,10 @@ struct dmap {
 };
 typedef struct dmap dmap_t;
 
+typedef struct ora_row {
+   struct db_row hdr;
+   struct ora_row* next;
+} ora_row_t;
 
 /*
  * Get and convert columns from a result. Define handlers and buffers
@@ -362,66 +366,74 @@ static int convert_row(db_res_t* _res, db_row_t* _r, dmap_t* _d)
  */
 static int get_rows(ora_con_t* con, db_res_t* _r, OCIStmt* _c, dmap_t* _d)
 {
-	ub4 rcnt;
+	ub4 i = 0, rcnt = 0;
 	sword status;
 	unsigned n = RES_COL_N(_r);
-
+	// Since OCI have not "mysql_num_rows()", sequentialy fetch all rows into tmp area orow_list.
+	// Then call db_allocate_rows() and copy tmp to db module.
+	ora_row_t *orow_list = NULL, *orow_it, *orow_tail;
+	
 	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_NEXT, 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;
+	
+	request_timer();
+	while ((status = OCIStmtFetch2(_c, con->errhp, 1, OCI_FETCH_NEXT, 0,
+		OCI_DEFAULT)) == OCI_SUCCESS) {
+		timer_stop();
+	
+		orow_it = (ora_row_t*) pkg_malloc(sizeof(ora_row_t) + sizeof(db_val_t)*RES_COL_N(_r));
+		if (orow_it == NULL) {
+			LM_ERR("no private memory left\n");
+			goto stop_load;
+		}
+		memset(orow_it, 0, sizeof(ora_row_t) + sizeof(db_val_t)*RES_COL_N(_r));
+		orow_it->hdr.values = (db_val_t*)(orow_it+1);
+		
+		if (orow_list == NULL)
+			orow_list = orow_it;
+		else
+			orow_tail->next = orow_it;
+		orow_tail = orow_it;
+		
+		if (convert_row(_r, &orow_it->hdr, _d) < 0) {
+			LM_ERR("erroc convert row\n");
+			goto stop_load;
+		}
+		memcpy(_d->len, _d->ilen, sizeof(_d->len[0]) * n);
+		++rcnt;
+		request_timer();
 	}
-
+	timer_stop();
+	if (status != OCI_NO_DATA)
+		goto ora_err;
+	
 	RES_ROW_N(_r) = rcnt;
 	if (db_allocate_rows( _r, rcnt)!=0) {
 		LM_ERR("no private memory left\n");
-		return -1;
+		goto stop_load;
 	}
-
-	while ( 1 ) {
-		if (convert_row(_r, &RES_ROWS(_r)[--rcnt], _d) < 0) {
-			LM_ERR("error 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;
+	
+	for (orow_it = orow_list; orow_it; orow_it = orow_it->next) {
+		memcpy(ROW_VALUES(&RES_ROWS(_r)[i]), ROW_VALUES(&orow_it->hdr), sizeof(db_val_t)*RES_COL_N(_r));
+		ROW_N(&RES_ROWS(_r)[i]) = ROW_N(&orow_it->hdr);
+		++i;
+	}
+	
+	for (orow_it = orow_list; orow_it; orow_it = orow_list) {
+		orow_list = orow_it->next;
+		pkg_free(orow_it);
+		orow_it = NULL;
 	}
+	return 0;
+	
 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 :) */
+	for (orow_it = orow_list; orow_it; orow_it = orow_list) {
+		orow_list = orow_it->next;
+		db_free_row(&orow_it->hdr);
+		pkg_free(orow_it);
+		orow_it = NULL;
+	}
 	return -3;
 }
 
diff --git a/modules/db_oracle/timer.c b/modules/db_oracle/timer.c
new file mode 100644
index 0000000..50484f4
--- /dev/null
+++ b/modules/db_oracle/timer.c
@@ -0,0 +1,131 @@
+#include "timer.h"
+#include "../../dprint.h"
+#include "../../sr_module.h"
+
+#include <signal.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#define MAX_TIMEOUT_S  10
+#define MIN_TIMEOUT_MCS 100000
+
+/* Default is 3.0 second */
+struct itimerval request_tm = {{0,0}, {3,0}};
+// unsigned request_tm;
+
+/* Default is 0.2 second */
+struct itimerval restore_tm = {{0,0}, {0,200000}};
+
+struct itimerval stop_tm = {{0,0}, {0,0}};
+
+/* Simple error handling functions */
+
+#define handle_error_en(en, msg) \
+	do { LM_ERR(msg": %s\n", strerror(en)); return 1; } while (0)
+	
+static ora_con_t *g_con;
+
+static int set_tv(unsigned type, const char* val, struct timeval* tv)
+{
+	char *eptr;
+	struct itimerval tt;
+	unsigned long s, ms;
+	double dv;
+	
+	if (type != STR_PARAM) {
+		LM_ERR("type of parameter is no STR\n");
+		return -1;
+	}
+	
+	if (!val || !*val) {
+		LM_ERR("empty parameter\n");
+		return -1;
+	}
+	
+	errno = 0;
+	dv = strtod(val, &eptr);
+	
+	if (*eptr) {
+		LM_ERR("invalid parameter string\n");
+		return -2;
+	}
+	
+	if (   errno
+		|| dv > (double)MAX_TIMEOUT_S
+		|| (dv < ((double)MIN_TIMEOUT_MCS)/100000))
+	{
+		LM_ERR("value must be between 0.%.6u and %u.0\n",
+		MIN_TIMEOUT_MCS, MAX_TIMEOUT_S);
+		return -3;
+	}
+	
+	s = (unsigned)dv;
+	dv -= (double)s;
+	ms = (unsigned)(dv * 1000000);
+	tv->tv_sec = (time_t)s;
+	tv->tv_usec = (suseconds_t)ms;
+	return 0;
+}
+
+
+/*
+	* set operation timeout
+	*/
+int set_timeout(unsigned type, const char* val)
+{
+	return set_tv(type, val, &request_tm.it_value);
+}
+
+
+/*
+	* set (re)connect timeout
+	*/
+int set_reconnect(unsigned type, const char* val)
+{
+	return set_tv(type, val, &restore_tm.it_value);
+}
+
+void ora_alarm_handle(int sig) {
+	LM_INFO("ora_alarm_handle\n");
+	sword status;
+	status = OCIBreak(g_con->svchp, g_con->errhp);
+	if (status != OCI_SUCCESS) {
+		LM_ERR("OCIBreak: %s\n", db_oracle_error(g_con, status));
+	}
+}
+
+int init_ora_timer(ora_con_t* con) {
+	g_con = con;
+	struct sigaction sa;
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = ora_alarm_handle;
+	if (sigaction(SIGALRM, &sa, NULL) < -1)
+		handle_error_en(errno, "sigaction");
+	
+	return 0;
+}
+
+int destroy_ora_timer()
+{
+	timer_stop();
+}
+
+void request_timer()
+{
+	if (setitimer(ITIMER_REAL, &request_tm, NULL) < 0)
+		LM_ERR("setitimer: %s\n", strerror(errno));
+}
+
+void restore_timer()
+{
+	if (setitimer(ITIMER_REAL, &restore_tm, NULL) < 0)
+		LM_ERR("setitimer: %s\n", strerror(errno));
+}
+
+void timer_stop()
+{
+	if (setitimer(ITIMER_REAL, &stop_tm, NULL) < 0)
+		LM_ERR("setitimer: %s\n", strerror(errno));
+}
+	
\ No newline at end of file
diff --git a/modules/db_oracle/timer.h b/modules/db_oracle/timer.h
new file mode 100644
index 0000000..24aa4a9
--- /dev/null
+++ b/modules/db_oracle/timer.h
@@ -0,0 +1,22 @@
+#ifndef ORA_TIMER_H
+#define ORA_TIMER_H
+
+#include "ora_con.h"
+
+/*
+ * parse timeout value for operation in syntax: nnn.mmm (sec/ms)
+ */
+int set_timeout(unsigned type, const char* val);
+
+/*
+ * parse timeout value for reconnect in syntax: nnn.mmm (sec/ms)
+ */
+int set_reconnect(unsigned type, const char* val);
+
+int init_ora_timer(ora_con_t* con);
+int destroy_ora_timer();
+
+void request_timer();
+void restore_timer();
+void timer_stop();
+#endif // ORA_TIMER_H
\ No newline at end of file