diff --git a/src/interfaces/odbc/bind.c b/src/interfaces/odbc/bind.c
index 397d984ec6c6bea046295fc1f36e27abe445d8e1..e931b8b8d51766aaf5332b2c5d8501cfe2916071 100644
--- a/src/interfaces/odbc/bind.c
+++ b/src/interfaces/odbc/bind.c
@@ -67,8 +67,8 @@ PGAPI_BindParameter(
 	opts->parameters[ipar].paramType = fParamType;
 	opts->parameters[ipar].CType = fCType;
 	opts->parameters[ipar].SQLType = fSqlType;
-	opts->parameters[ipar].precision = cbColDef;
-	opts->parameters[ipar].scale = ibScale;
+	opts->parameters[ipar].column_size = cbColDef;
+	opts->parameters[ipar].decimal_digits = ibScale;
 
 	/*
 	 * If rebinding a parameter that had data-at-exec stuff in it, then
@@ -276,10 +276,10 @@ PGAPI_DescribeParam(
 		*pfSqlType = opts->parameters[ipar].SQLType;
 
 	if (pcbColDef)
-		*pcbColDef = opts->parameters[ipar].precision;
+		*pcbColDef = opts->parameters[ipar].column_size;
 
 	if (pibScale)
-		*pibScale = opts->parameters[ipar].scale;
+		*pibScale = opts->parameters[ipar].decimal_digits;
 
 	if (pfNullable)
 		*pfNullable = pgtype_nullable(stmt, opts->parameters[ipar].paramType);
@@ -458,8 +458,8 @@ reset_a_parameter_binding(APDFields *self, int ipar)
 		self->parameters[ipar].EXEC_buffer = NULL;
 	}
 	self->parameters[ipar].SQLType = 0;
-	self->parameters[ipar].precision = 0;
-	self->parameters[ipar].scale = 0;
+	self->parameters[ipar].column_size = 0;
+	self->parameters[ipar].decimal_digits = 0;
 	self->parameters[ipar].data_at_exec = FALSE;
 	self->parameters[ipar].lobj_oid = 0;
 }
diff --git a/src/interfaces/odbc/bind.h b/src/interfaces/odbc/bind.h
index 16d9f84b6e62817acef49146a8e4c543c22129cb..7eadea92e44c41a42b2bb4440ef7741b720c8258 100644
--- a/src/interfaces/odbc/bind.h
+++ b/src/interfaces/odbc/bind.h
@@ -40,8 +40,8 @@ struct ParameterInfoClass_
 	Int2		paramType;
 	Int2		CType;
 	Int2		SQLType;
-	UInt4		precision;
-	Int2		scale;
+	UInt4		column_size;
+	Int2		decimal_digits;
 	Oid			lobj_oid;
 	Int4	   *EXEC_used;		/* amount of data OR the oid of the large
 								 * object */
diff --git a/src/interfaces/odbc/connection.c b/src/interfaces/odbc/connection.c
index db237b3f952866b79caedca309648bc374bacde4..d6b622a16d736df112b019bad220bcd78d9e9bdd 100644
--- a/src/interfaces/odbc/connection.c
+++ b/src/interfaces/odbc/connection.c
@@ -288,6 +288,7 @@ CC_Constructor()
 		rv->pg_version_minor = 0;
 		rv->ms_jet = 0;
 		rv->unicode = 0;
+		rv->result_uncommitted = 0;
 #ifdef	MULTIBYTE
 		rv->client_encoding = NULL;
 		rv->server_encoding = NULL;
@@ -1110,21 +1111,30 @@ CC_get_error(ConnectionClass *self, int *number, char **message)
 }
 
 
-void	CC_on_commit(ConnectionClass *conn, BOOL set_no_trans)
+void	CC_on_commit(ConnectionClass *conn)
 {
 	if (CC_is_in_trans(conn))
 	{
-		if (set_no_trans)
-			CC_set_no_trans(conn);
+#ifdef	DRIVER_CURSOR_IMPLEMENT
+		if (conn->result_uncommitted)
+			ProcessRollback(conn, FALSE);
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+		CC_set_no_trans(conn);
 	}
+	conn->result_uncommitted = 0;
 }
 void	CC_on_abort(ConnectionClass *conn, BOOL set_no_trans)
 {
 	if (CC_is_in_trans(conn))
 	{
+#ifdef	DRIVER_CURSOR_IMPLEMENT
+		if (conn->result_uncommitted)
+			ProcessRollback(conn, TRUE);
+#endif /* DRIVER_CURSOR_IMPLEMENT */
 		if (set_no_trans)
 			CC_set_no_trans(conn);
 	}
+	conn->result_uncommitted = 0;
 }
 
 /*
@@ -1293,11 +1303,11 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag)
 						}
 					}
 					else if (strnicmp(cmdbuffer, "COMMIT", 6) == 0)
-						CC_on_commit(self, TRUE);
+						CC_on_commit(self);
 					else if (strnicmp(cmdbuffer, "ROLLBACK", 8) == 0)
 						CC_on_abort(self, TRUE);
 					else if (strnicmp(cmdbuffer, "END", 3) == 0)
-						CC_on_commit(self, TRUE);
+						CC_on_commit(self);
 					else if (strnicmp(cmdbuffer, "ABORT", 5) == 0)
 						CC_on_abort(self, TRUE);
 
diff --git a/src/interfaces/odbc/connection.h b/src/interfaces/odbc/connection.h
index 67411edd67a9dc6d4fdadb3abfba3a38c9d8e367..5d01dacd2148f368f40fc60c734d14e6a2fb7da0 100644
--- a/src/interfaces/odbc/connection.h
+++ b/src/interfaces/odbc/connection.h
@@ -272,6 +272,7 @@ struct ConnectionClass_
 	Int2		pg_version_minor;
 	char		ms_jet;
 	char		unicode;
+	char		result_uncommitted;
 #ifdef	MULTIBYTE
 	char	   *client_encoding;
 	char	   *server_encoding;
@@ -318,8 +319,9 @@ void		CC_lookup_pg_version(ConnectionClass *conn);
 void		CC_initialize_pg_version(ConnectionClass *conn);
 void		CC_log_error(const char *func, const char *desc, const ConnectionClass *self);
 int			CC_get_max_query_len(const ConnectionClass *self);
-void		CC_on_commit(ConnectionClass *conn, BOOL set_no_trans);
+void		CC_on_commit(ConnectionClass *conn);
 void		CC_on_abort(ConnectionClass *conn, BOOL set_no_trans);
+void		ProcessRollback(ConnectionClass *conn, BOOL undo);
 
 /* CC_send_query_options */
 #define	CLEAR_RESULT_ON_ABORT	1L
diff --git a/src/interfaces/odbc/convert.c b/src/interfaces/odbc/convert.c
index c0a2706ef6116763577986c40d5d0dc3d02e8ebb..e4232137fef7dd292c2cdee3ec182b7f92abe69b 100644
--- a/src/interfaces/odbc/convert.c
+++ b/src/interfaces/odbc/convert.c
@@ -265,6 +265,16 @@ stime2timestamp(const SIMPLE_TIME *st, char *str, BOOL bZone, BOOL precision)
 	int			i;
 
 	precstr[0] = '\0';
+	if (st->infinity > 0)
+	{
+		strcpy(str, "Infinity");
+		return TRUE;
+	}
+	else if (st->infinity < 0)
+	{
+		strcpy(str, "-Infinity");
+		return TRUE;
+	}
 	if (precision && st->fr)
 	{
 		sprintf(precstr, ".%09d", st->fr);
@@ -447,6 +457,27 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2
 		case PG_TYPE_DATETIME:
 		case PG_TYPE_TIMESTAMP:
 			st.fr = 0;
+			st.infinity = 0;
+			if (strnicmp(value, "infinity", 8) == 0)
+			{
+				st.infinity = 1;
+				st.m = 12;
+				st.d = 31;
+				st.y = 9999;
+				st.hh = 24;
+				st.mm = 0;
+				st.ss = 0;
+			}
+			if (strnicmp(value, "-infinity", 9) == 0)
+			{
+				st.infinity = -1;
+				st.m = 0;
+				st.d = 0;
+				st.y = 0;
+				st.hh = 0;
+				st.mm = 0;
+				st.ss = 0;
+			}
 			if (strnicmp(value, "invalid", 7) != 0)
 			{
 				BOOL		bZone = (field_type != PG_TYPE_TIMESTAMP_NO_TMZONE && PG_VERSION_GE(SC_get_conn(stmt), 7.2));
@@ -2495,7 +2526,7 @@ convert_money(const char *s, char *sout, size_t soutmax)
  *	It does not zero out SIMPLE_TIME in case it is desired to initialize it with a value
  */
 char
-parse_datetime(char *buf, SIMPLE_TIME *st)
+parse_datetime(const char *buf, SIMPLE_TIME *st)
 {
 	int			y,
 				m,
diff --git a/src/interfaces/odbc/convert.h b/src/interfaces/odbc/convert.h
index 2d5f01859f04dd87f253549a7a4f77f371d28151..1ca0b1bb0c94e251271eaccf2b4554b0ffc4ebf2 100644
--- a/src/interfaces/odbc/convert.h
+++ b/src/interfaces/odbc/convert.h
@@ -25,6 +25,7 @@
 
 typedef struct
 {
+	int		infinity;
 	int			m;
 	int			d;
 	int			y;
@@ -42,7 +43,7 @@ int			copy_statement_with_parameters(StatementClass *stmt);
 int		convert_escape(const char *value, StatementClass *stmt,
 			int *npos, int *stsize, const char **val_resume);
 BOOL		convert_money(const char *s, char *sout, size_t soutmax);
-char		parse_datetime(char *buf, SIMPLE_TIME *st);
+char		parse_datetime(const char *buf, SIMPLE_TIME *st);
 int			convert_linefeeds(const char *s, char *dst, size_t max, BOOL convlf, BOOL *changed);
 int			convert_special_chars(const char *si, char *dst, int used, BOOL convlf,int ccsc);
 
diff --git a/src/interfaces/odbc/descriptor.h b/src/interfaces/odbc/descriptor.h
index 58036914ada6f94cf119f099a5b8e7a77eb072c6..5186810b21799e3b97c63e0a44b04b79cbb6d08c 100644
--- a/src/interfaces/odbc/descriptor.h
+++ b/src/interfaces/odbc/descriptor.h
@@ -5,7 +5,7 @@
  *
  * Comments:		See "notice.txt" for copyright and license information.
  *
- * $Id: descriptor.h,v 1.1 2002/03/28 08:08:02 inoue Exp $
+ * $Id: descriptor.h,v 1.2 2002/04/01 03:01:14 inoue Exp $
  *
  */
 
@@ -92,5 +92,8 @@ void	IRDFields_free(IRDFields *self);
 void	IPDFields_free(IPDFields *self);
 void	ARD_unbind_cols(ARDFields *self, BOOL freeall);
 void	APD_free_params(APDFields *self, char option);
+#if (ODBCVER >= 0x0300)
+void	Desc_set_error(SQLHDESC hdesc, int errornumber, const char * errormsg);
+#endif /* ODBCVER */
 
 #endif
diff --git a/src/interfaces/odbc/odbcapi30.c b/src/interfaces/odbc/odbcapi30.c
index 0e9b63a304961eb27c78b9aadb522fa931a30040..8bcb1cab146bfd49929c38cb56a3b191e9d5edaf 100644
--- a/src/interfaces/odbc/odbcapi30.c
+++ b/src/interfaces/odbc/odbcapi30.c
@@ -235,7 +235,8 @@ SQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle,
 				SQLSMALLINT *StringLength)
 {
 	mylog("[[SQLGetDiagField]] Handle=(%u,%x) Rec=%d Id=%d\n", HandleType, Handle, RecNumber, DiagIdentifier);
-	return SQL_ERROR;
+	return PGAPI_GetDiagField(HandleType, Handle, RecNumber, DiagIdentifier,
+				DiagInfo, BufferLength, StringLength);
 }
 
 /*	SQLError -> SQLDiagRec */
diff --git a/src/interfaces/odbc/odbcapi30w.c b/src/interfaces/odbc/odbcapi30w.c
index 0273f97b6b95376917d9ad1bbb7088c3e7f93604..ac0f1d9931fe361332929a3c06290a9e5b69ca7a 100644
--- a/src/interfaces/odbc/odbcapi30w.c
+++ b/src/interfaces/odbc/odbcapi30w.c
@@ -89,10 +89,25 @@ SQLSetDescFieldW(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber,
 	mylog("[SQLSetDescFieldW]");
 	if (BufferLength > 0)
 	{
-		uval = ucs2_to_utf8(Value, BufferLength / 2, &vallen);
-		val_alloced = TRUE;
+		switch (FieldIdentifier)
+		{
+			case SQL_DESC_BASE_COLUMN_NAME:
+			case SQL_DESC_BASE_TABLE_NAME:
+			case SQL_DESC_CATALOG_NAME:
+			case SQL_DESC_LABEL:
+			case SQL_DESC_LITERAL_PREFIX:
+			case SQL_DESC_LITERAL_SUFFIX:
+			case SQL_DESC_LOCAL_TYPE_NAME:
+			case SQL_DESC_NAME:
+			case SQL_DESC_SCHEMA_NAME:
+			case SQL_DESC_TABLE_NAME:
+			case SQL_DESC_TYPE_NAME:
+				uval = ucs2_to_utf8(Value, BufferLength / 2, &vallen);
+				val_alloced = TRUE;
+			break;
+		}
 	}
-	else
+	if (!val_alloced)
 	{
 		uval = Value;
 		vallen = BufferLength;
@@ -109,11 +124,49 @@ SQLGetDescFieldW(SQLHDESC hdesc, SQLSMALLINT iRecord, SQLSMALLINT iField,
     				SQLINTEGER *pcbValue)
 {
 	RETCODE	ret;
-        char    *qstr = NULL, *mtxt = NULL;
+	BOOL	alloced = FALSE;
+	SQLINTEGER	blen, bMax, *pcbV;
+        char    *rgbV = NULL;
 
 	mylog("[SQLGetDescFieldW]");
-	ret = PGAPI_GetDescField(hdesc, iRecord, iField, rgbValue,
-				cbValueMax, pcbValue);
+	switch (iField)
+	{
+		case SQL_DESC_BASE_COLUMN_NAME:
+		case SQL_DESC_BASE_TABLE_NAME:
+		case SQL_DESC_CATALOG_NAME:
+		case SQL_DESC_LABEL:
+		case SQL_DESC_LITERAL_PREFIX:
+		case SQL_DESC_LITERAL_SUFFIX:
+		case SQL_DESC_LOCAL_TYPE_NAME:
+		case SQL_DESC_NAME:
+		case SQL_DESC_SCHEMA_NAME:
+		case SQL_DESC_TABLE_NAME:
+		case SQL_DESC_TYPE_NAME:
+			alloced = TRUE;
+			bMax = cbValueMax * 3 / 2;
+			rgbV = malloc(bMax + 1);
+			pcbV = &blen;
+                	break;
+		default:
+			rgbV = rgbValue;
+			bMax = cbValueMax;
+			pcbV = pcbValue;
+			break;
+	}
+	ret = PGAPI_GetDescField(hdesc, iRecord, iField, rgbV, bMax, pcbV);
+	if (alloced)
+	{
+		blen = utf8_to_ucs2(rgbV, blen, (SQLWCHAR *) rgbValue, cbValueMax / 2);
+		if (SQL_SUCCESS == ret && blen * 2 > cbValueMax)
+		{
+			ret = SQL_SUCCESS_WITH_INFO;
+			Desc_set_error(hdesc, STMT_TRUNCATED, "The buffer was too small for the rgbDesc.");
+		}
+		if (pcbValue)
+			*pcbValue = blen * 2;
+		free(rgbV);
+	}
+
 	return ret;
 }
 
@@ -171,6 +224,9 @@ RETCODE SQL_API SQLColAttributeW(
     SQLINTEGER 		  *pfDesc)
 {
 	RETCODE	ret;
+	BOOL	alloced = FALSE;
+	SQLSMALLINT	*rgbL, blen, bMax;
+        char    *rgbD = NULL;
 
 	mylog("[SQLColAttributeW]");
 	switch (fDescType)
@@ -187,11 +243,35 @@ RETCODE SQL_API SQLColAttributeW(
 		case SQL_DESC_TABLE_NAME:
 		case SQL_DESC_TYPE_NAME:
 		case SQL_COLUMN_NAME:
+			alloced = TRUE;
+			bMax = cbDescMax * 3 / 2;
+			rgbD = malloc(bMax + 1);
+			rgbL = &blen;
                 	break;
+		default:
+			rgbD = rgbDesc;
+			bMax = cbDescMax;
+			rgbL = pcbDesc;
+			break;
 	}
 
-	ret = PGAPI_ColAttributes(hstmt, icol, fDescType, rgbDesc,
-		cbDescMax, pcbDesc, pfDesc);
+	ret = PGAPI_ColAttributes(hstmt, icol, fDescType, rgbD,
+		bMax, rgbL, pfDesc);
+	if (alloced)
+	{
+		blen = utf8_to_ucs2(rgbD, blen, (SQLWCHAR *) rgbDesc, cbDescMax / 2);
+		if (SQL_SUCCESS == ret && blen * 2 > cbDescMax)
+		{
+			StatementClass	*stmt = (StatementClass *) hstmt;
+
+			ret = SQL_SUCCESS_WITH_INFO;
+			stmt->errornumber = STMT_TRUNCATED;
+			stmt->errormsg = "The buffer was too small for the rgbDesc.";
+		}
+		if (pcbDesc)
+			*pcbDesc = blen * 2;
+		free(rgbD);
+	}
 
 	return ret;
 }
diff --git a/src/interfaces/odbc/parse.c b/src/interfaces/odbc/parse.c
index 3549ff2ef7a0beae70b79b0c8fd9b28e70799c09..d86e62f85bb0af7cdc40320683638b4d998746ef 100644
--- a/src/interfaces/odbc/parse.c
+++ b/src/interfaces/odbc/parse.c
@@ -304,7 +304,8 @@ parse_statement(StatementClass *stmt)
 				in_on = FALSE,
 				in_from = FALSE,
 				in_where = FALSE,
-				in_table = FALSE;
+				in_table = FALSE,
+				out_table = TRUE;
 	char		in_field = FALSE,
 				in_expr = FALSE,
 				in_func = FALSE,
@@ -610,12 +611,21 @@ parse_statement(StatementClass *stmt)
 
 		if (in_from)
 		{
-			if (!in_table)
+			if (token[0] == ';')
 			{
-				if (!token[0])
+				in_from = FALSE;
+				break;
+			}
+			switch (token[0])
+			{
+				case '\0':
 					continue;
-				if (token[0] == ';')
-					break;
+				case ',':
+					out_table = TRUE; 
+					continue;
+			}
+			if (out_table && !in_table) /* new table */
+			{
 
 				if (!(stmt->ntab % TAB_INCR))
 				{
@@ -660,22 +670,47 @@ parse_statement(StatementClass *stmt)
 				mylog("got table = '%s'\n", ti[stmt->ntab]->name);
 
 				if (delim == ',')
+				{
+					out_table = TRUE;
 					mylog("more than 1 tables\n");
+				}
 				else
+				{
+					out_table = FALSE;
 					in_table = TRUE;
+				}
 				stmt->ntab++;
 				continue;
 			}
 
-			if (token[0] == ';')
-				break;
-			if (stricmp(token, "as"))
+			if (!dquote && stricmp(token, "JOIN") == 0)
+			{
+				in_table = FALSE;
+				out_table = TRUE;
+				continue;
+			}
+			if (in_table && stricmp(token, "as"))
 			{
+				if (!dquote)
+				{
+					if (stricmp(token, "LEFT") == 0 ||
+					    stricmp(token, "RIGHT") == 0 ||
+					    stricmp(token, "OUTER") == 0 ||
+					    stricmp(token, "FULL") == 0 ||
+					    stricmp(token, "ON") == 0)
+					{
+						in_table = FALSE;
+						continue;
+					}
+				}
 				strcpy(ti[stmt->ntab - 1]->alias, token);
 				mylog("alias for table '%s' is '%s'\n", ti[stmt->ntab - 1]->name, ti[stmt->ntab - 1]->alias);
 				in_table = FALSE;
 				if (delim == ',')
+				{
+					out_table = TRUE;
 					mylog("more than 1 tables\n");
+				}
 			}
 		} /* in_from */
 	}
diff --git a/src/interfaces/odbc/pgapi30.c b/src/interfaces/odbc/pgapi30.c
index a67cc434fab5c1dca776dbbbed57540429ee4929..e73773931565aa784db8702d240c5d10c7c65536 100644
--- a/src/interfaces/odbc/pgapi30.c
+++ b/src/interfaces/odbc/pgapi30.c
@@ -27,7 +27,7 @@
 #include "descriptor.h"
 #include "pgapifunc.h"
 
-static HSTMT statementHandleFromDescHandle(HSTMT, SQLINTEGER *descType); 
+static HSTMT statementHandleFromDescHandle(SQLHDESC, SQLINTEGER *descType); 
 /*	SQLError -> SQLDiagRec */
 RETCODE		SQL_API
 PGAPI_GetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle,
@@ -75,7 +75,7 @@ PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle,
 		PTR DiagInfoPtr, SQLSMALLINT BufferLength,
 		SQLSMALLINT *StringLengthPtr)
 {
-	RETCODE		ret = SQL_SUCCESS;
+	RETCODE		ret = SQL_ERROR;
 	static const char *func = "PGAPI_GetDiagField";
 
 	mylog("%s entering rec=%d", func, RecNumber);
@@ -122,6 +122,7 @@ PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle,
 				case SQL_DIAG_NUMBER:
 				case SQL_DIAG_RETURNCODE:
 				case SQL_DIAG_SERVER_NAME:
+					break;
 				case SQL_DIAG_SQLSTATE:
 					break;
 			}
@@ -154,7 +155,12 @@ PGAPI_GetConnectAttr(HDBC ConnectionHandle,
 			*((SQLUINTEGER *) Value) = SQL_FALSE;
 			break;
 		case SQL_ATTR_CONNECTION_DEAD:
-			*((SQLUINTEGER *) Value) = SQL_CD_FALSE;
+			if (CC_is_in_trans(conn))
+				*((SQLUINTEGER *) Value) = SQL_CD_FALSE;
+			else if (conn->num_stmts > 0)
+				*((SQLUINTEGER *) Value) = SQL_CD_FALSE;
+			else
+				*((SQLUINTEGER *) Value) = SQL_CD_FALSE;
 			break;
 		case SQL_ATTR_CONNECTION_TIMEOUT:
 			*((SQLUINTEGER *) Value) = 0;
@@ -172,7 +178,7 @@ PGAPI_GetConnectAttr(HDBC ConnectionHandle,
 	return ret;
 }
 
-static HSTMT
+static SQLHDESC
 descHandleFromStatementHandle(HSTMT StatementHandle, SQLINTEGER descType) 
 {
 	switch (descType)
@@ -189,7 +195,7 @@ descHandleFromStatementHandle(HSTMT StatementHandle, SQLINTEGER descType)
 	return (HSTMT) 0;
 }
 static HSTMT
-statementHandleFromDescHandle(HSTMT DescHandle, SQLINTEGER *descType) 
+statementHandleFromDescHandle(SQLHDESC DescHandle, SQLINTEGER *descType) 
 {
 	SQLUINTEGER res = (SQLUINTEGER) DescHandle % 4;
 	if (descType)
@@ -209,6 +215,19 @@ statementHandleFromDescHandle(HSTMT DescHandle, SQLINTEGER *descType)
 	return (HSTMT) ((SQLUINTEGER) DescHandle - res);
 }
 
+void	Desc_set_error(SQLHDESC hdesc, int errornumber, const char *errormsg)
+{
+	SQLINTEGER	descType;
+	HSTMT	hstmt = statementHandleFromDescHandle(hdesc, &descType);
+	StatementClass	*stmt;
+
+	if (!hstmt)
+		return;
+	stmt = (StatementClass *) hstmt;
+	stmt->errornumber = errornumber;
+	stmt->errormsg = errormsg; /* should be static */
+}
+
 static  void column_bindings_set(ARDFields *opts, int cols, BOOL maxset)
 {
 	int	i;
@@ -568,7 +587,7 @@ IPDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 			apdopts->parameters[RecNumber - 1].paramType = (Int2) Value;
 			break;
 		case SQL_DESC_SCALE:
-			apdopts->parameters[RecNumber - 1].scale = (Int2) Value;
+			apdopts->parameters[RecNumber - 1].decimal_digits = (Int2) Value;
 			break;
 		case SQL_DESC_ALLOC_TYPE: /* read-only */ 
 		case SQL_DESC_CASE_SENSITIVE: /* read-only */
@@ -599,7 +618,7 @@ ARDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 		SQLINTEGER *StringLength)
 {
 	RETCODE		ret = SQL_SUCCESS;
-	SQLINTEGER	len, ival;
+	SQLINTEGER	len, ival, rettype = 0;
 	PTR		ptr = NULL;
 	const ARDFields	*opts = SC_get_ARD(stmt);
 
@@ -610,9 +629,11 @@ ARDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 			ival = opts->rowset_size;
 			break; 
 		case SQL_DESC_ARRAY_STATUS_PTR:
+			rettype = SQL_IS_POINTER;
 			ptr = opts->row_operation_ptr;
 			break;
 		case SQL_DESC_BIND_OFFSET_PTR:
+			rettype = SQL_IS_POINTER;
 			ptr = opts->row_offset_ptr;
 			break;
 		case SQL_DESC_BIND_TYPE:
@@ -651,6 +672,7 @@ ARDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 			ival = opts->bindings[RecNumber - 1].returntype;
 			break;
 		case SQL_DESC_DATA_PTR:
+			rettype = SQL_IS_POINTER;
 			if (!RecNumber)
 				ptr = opts->bookmark->buffer;
 			else
@@ -659,6 +681,7 @@ ARDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 			}
 			break;
 		case SQL_DESC_INDICATOR_PTR:
+			rettype = SQL_IS_POINTER;
 			if (!RecNumber)
 				ptr = opts->bookmark->used;
 			else
@@ -667,6 +690,7 @@ ARDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 			}
 			break;
 		case SQL_DESC_OCTET_LENGTH_PTR:
+			rettype = SQL_IS_POINTER;
 			if (!RecNumber)
 				ptr = opts->bookmark->used;
 			else
@@ -694,25 +718,13 @@ ARDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 		default:ret = SQL_ERROR;
 			stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
 	}
-	switch (BufferLength)
+	switch (rettype)
 	{
 		case 0:
 		case SQL_IS_INTEGER:
 			len = 4;
 			*((SQLINTEGER *) Value) = ival;
 			break;
-		case SQL_IS_UINTEGER:
-			len = 4;
-			*((UInt4 *) Value) = ival;
-			break;
-		case SQL_IS_SMALLINT:
-			len = 2;
-			*((SQLSMALLINT *) Value) = (SQLSMALLINT) ival;
-			break;
-		case SQL_IS_USMALLINT:
-			len = 2;
-			*((SQLUSMALLINT *) Value) = (SQLUSMALLINT) ival;
-			break;
 		case SQL_IS_POINTER:
 			len = 4;
 			*((void **) Value) = ptr;
@@ -730,7 +742,7 @@ APDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 		SQLINTEGER *StringLength)
 {
 	RETCODE		ret = SQL_SUCCESS;
-	SQLINTEGER	ival = 0, len;
+	SQLINTEGER	ival = 0, len, rettype = 0;
 	PTR		ptr = NULL;
 	const APDFields	*opts = SC_get_APD(stmt);
 
@@ -738,12 +750,15 @@ APDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 	switch (FieldIdentifier)
 	{
 		case SQL_DESC_ARRAY_SIZE:
+			rettype = SQL_IS_POINTER;
 			ival = opts->paramset_size;
 			break; 
 		case SQL_DESC_ARRAY_STATUS_PTR:
+			rettype = SQL_IS_POINTER;
 			ptr = opts->param_operation_ptr;
 			break;
 		case SQL_DESC_BIND_OFFSET_PTR:
+			rettype = SQL_IS_POINTER;
 			ptr = opts->param_offset_ptr;
 			break;
 		case SQL_DESC_BIND_TYPE:
@@ -783,15 +798,18 @@ APDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 			ival = opts->parameters[RecNumber - 1].CType;
 			break;
 		case SQL_DESC_DATA_PTR:
+			rettype = SQL_IS_POINTER;
 			ptr = opts->parameters[RecNumber - 1].buffer;
 			break;
 		case SQL_DESC_INDICATOR_PTR:
+			rettype = SQL_IS_POINTER;
 			ptr = opts->parameters[RecNumber - 1].used;
 			break;
 		case SQL_DESC_OCTET_LENGTH:
 			ival = opts->parameters[RecNumber - 1].buflen;
 			break;
 		case SQL_DESC_OCTET_LENGTH_PTR:
+			rettype = SQL_IS_POINTER;
 			ptr = opts->parameters[RecNumber - 1].used;
 			break;
 		case SQL_DESC_COUNT:
@@ -800,33 +818,21 @@ APDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 		case SQL_DESC_ALLOC_TYPE: /* read-only */
 			ival = SQL_DESC_ALLOC_AUTO;
 			break;
+		case SQL_DESC_PRECISION:
+		case SQL_DESC_SCALE:
 		case SQL_DESC_DATETIME_INTERVAL_PRECISION:
 		case SQL_DESC_LENGTH:
 		case SQL_DESC_NUM_PREC_RADIX:
-		case SQL_DESC_PRECISION:
-		case SQL_DESC_SCALE:
 		default:ret = SQL_ERROR;
 			stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
 	}
-	switch (BufferLength)
+	switch (rettype)
 	{
 		case 0:
 		case SQL_IS_INTEGER:
 			len = 4;
 			*((Int4 *) Value) = ival;
 			break;
-		case SQL_IS_UINTEGER:
-			len = 4;
-			*((UInt4 *) Value) = ival;
-			break;
-		case SQL_IS_SMALLINT:
-			len = 2;
-			*((SQLSMALLINT *) Value) = (SQLSMALLINT) ival;
-			break;
-		case SQL_IS_USMALLINT:
-			len = 2;
-			*((SQLUSMALLINT *) Value) = (SQLUSMALLINT) ival;
-			break;
 		case SQL_IS_POINTER:
 			len = 4;
 			*((void **) Value) = ptr;
@@ -844,36 +850,33 @@ IRDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 		SQLINTEGER *StringLength)
 {
 	RETCODE		ret = SQL_SUCCESS;
-	SQLINTEGER	ival = 0, len;
+	SQLINTEGER	ival = 0, len, rettype = 0;
 	PTR		ptr = NULL;
+	BOOL		bCallColAtt = FALSE;
 	const IRDFields	*opts = SC_get_IRD(stmt);
 
 	switch (FieldIdentifier)
 	{
 		case SQL_DESC_ARRAY_STATUS_PTR:
+			rettype = SQL_IS_POINTER;
 			ptr = opts->rowStatusArray;
 			break;
 		case SQL_DESC_ROWS_PROCESSED_PTR:
+			rettype = SQL_IS_POINTER;
 			ptr = opts->rowsFetched;
 			break;
 		case SQL_DESC_ALLOC_TYPE: /* read-only */
+			ival = SQL_DESC_ALLOC_AUTO;
+			break;
 		case SQL_DESC_COUNT: /* read-only */
 		case SQL_DESC_AUTO_UNIQUE_VALUE: /* read-only */
-		case SQL_DESC_BASE_COLUMN_NAME: /* read-only */
-		case SQL_DESC_BASE_TABLE_NAME: /* read-only */
 		case SQL_DESC_CASE_SENSITIVE: /* read-only */
-		case SQL_DESC_CATALOG_NAME: /* read-only */
 		case SQL_DESC_CONCISE_TYPE: /* read-only */
 		case SQL_DESC_DATETIME_INTERVAL_CODE: /* read-only */
 		case SQL_DESC_DATETIME_INTERVAL_PRECISION: /* read-only */
 		case SQL_DESC_DISPLAY_SIZE: /* read-only */
 		case SQL_DESC_FIXED_PREC_SCALE: /* read-only */
-		case SQL_DESC_LABEL: /* read-only */
 		case SQL_DESC_LENGTH: /* read-only */
-		case SQL_DESC_LITERAL_PREFIX: /* read-only */
-		case SQL_DESC_LITERAL_SUFFIX: /* read-only */
-		case SQL_DESC_LOCAL_TYPE_NAME: /* read-only */
-		case SQL_DESC_NAME: /* read-only */
 		case SQL_DESC_NULLABLE: /* read-only */
 		case SQL_DESC_NUM_PREC_RADIX: /* read-only */
 		case SQL_DESC_OCTET_LENGTH: /* read-only */
@@ -882,18 +885,40 @@ IRDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 		case SQL_DESC_ROWVER: /* read-only */
 #endif /* ODBCVER */
 		case SQL_DESC_SCALE: /* read-only */
-		case SQL_DESC_SCHEMA_NAME: /* read-only */
 		case SQL_DESC_SEARCHABLE: /* read-only */
-		case SQL_DESC_TABLE_NAME: /* read-only */
 		case SQL_DESC_TYPE: /* read-only */
-		case SQL_DESC_TYPE_NAME: /* read-only */
 		case SQL_DESC_UNNAMED: /* read-only */
 		case SQL_DESC_UNSIGNED: /* read-only */
 		case SQL_DESC_UPDATABLE: /* read-only */
+			bCallColAtt = TRUE;
+			break;
+		case SQL_DESC_BASE_COLUMN_NAME: /* read-only */
+		case SQL_DESC_BASE_TABLE_NAME: /* read-only */
+		case SQL_DESC_CATALOG_NAME: /* read-only */
+		case SQL_DESC_LABEL: /* read-only */
+		case SQL_DESC_LITERAL_PREFIX: /* read-only */
+		case SQL_DESC_LITERAL_SUFFIX: /* read-only */
+		case SQL_DESC_LOCAL_TYPE_NAME: /* read-only */
+		case SQL_DESC_NAME: /* read-only */
+		case SQL_DESC_SCHEMA_NAME: /* read-only */
+		case SQL_DESC_TABLE_NAME: /* read-only */
+		case SQL_DESC_TYPE_NAME: /* read-only */
+			rettype = SQL_NTS;
+			bCallColAtt = TRUE;
+			break; 
 		default:ret = SQL_ERROR;
 			stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
 	}
-	switch (BufferLength)
+	if (bCallColAtt)
+	{
+		SQLSMALLINT	pcbL;
+
+		ret = PGAPI_ColAttributes(stmt, RecNumber,
+			FieldIdentifier, Value, (SQLSMALLINT) BufferLength,
+				&pcbL, &ival);
+		len = pcbL;
+	} 
+	switch (rettype)
 	{
 		case 0:
 		case SQL_IS_INTEGER:
@@ -904,14 +929,6 @@ IRDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 			len = 4;
 			*((UInt4 *) Value) = ival;
 			break;
-		case SQL_IS_SMALLINT:
-			len = 2;
-			*((SQLSMALLINT *) Value) = (SQLSMALLINT) ival;
-			break;
-		case SQL_IS_USMALLINT:
-			len = 2;
-			*((SQLUSMALLINT *) Value) = (SQLUSMALLINT) ival;
-			break;
 		case SQL_IS_POINTER:
 			len = 4;
 			*((void **) Value) = ptr;
@@ -929,7 +946,7 @@ IPDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 		SQLINTEGER *StringLength)
 {
 	RETCODE		ret = SQL_SUCCESS;
-	SQLINTEGER	ival = 0, len;
+	SQLINTEGER	ival = 0, len, rettype = 0;
 	PTR		ptr = NULL;
 	const IPDFields	*ipdopts = SC_get_IPD(stmt);
 	const APDFields	*apdopts = SC_get_APD(stmt);
@@ -937,9 +954,11 @@ IPDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 	switch (FieldIdentifier)
 	{
 		case SQL_DESC_ARRAY_STATUS_PTR:
+			rettype = SQL_IS_POINTER;
 			ptr = ipdopts->param_status_ptr;
 			break;
 		case SQL_DESC_ROWS_PROCESSED_PTR:
+			rettype = SQL_IS_POINTER;
 			ptr = ipdopts->param_processed_ptr;
 			break;
 		case SQL_DESC_UNNAMED: /* only SQL_UNNAMED is allowed */
@@ -981,8 +1000,24 @@ IPDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 		case SQL_DESC_PARAMETER_TYPE:
 			ival = apdopts->parameters[RecNumber - 1].paramType;
 			break;
+		case SQL_DESC_PRECISION:
+			switch (apdopts->parameters[RecNumber - 1].CType)
+			{
+				case SQL_C_TYPE_DATE:
+				case SQL_C_TYPE_TIME:
+				case SQL_C_TYPE_TIMESTAMP:
+				case SQL_DATETIME:
+					ival = apdopts->parameters[RecNumber - 1].decimal_digits;
+					break;
+			}
+			break;
 		case SQL_DESC_SCALE:
-			ival = apdopts->parameters[RecNumber - 1].scale ;
+			switch (apdopts->parameters[RecNumber - 1].CType)
+			{
+				case SQL_C_NUMERIC:
+					ival = apdopts->parameters[RecNumber - 1].decimal_digits;
+					break;
+			}
 			break;
 		case SQL_DESC_ALLOC_TYPE: /* read-only */
 			ival = SQL_DESC_ALLOC_AUTO;
@@ -996,7 +1031,6 @@ IPDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 		case SQL_DESC_NULLABLE: /* read-only */
 		case SQL_DESC_NUM_PREC_RADIX:
 		case SQL_DESC_OCTET_LENGTH:
-		case SQL_DESC_PRECISION:
 #if (ODBCVER >= 0x0350)
 		case SQL_DESC_ROWVER: /* read-only */
 #endif /* ODBCVER */
@@ -1005,25 +1039,13 @@ IPDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
 		default:ret = SQL_ERROR;
 			stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
 	}
-	switch (BufferLength)
+	switch (rettype)
 	{
 		case 0:
 		case SQL_IS_INTEGER:
 			len = 4;
 			*((Int4 *) Value) = ival;
 			break;
-		case SQL_IS_UINTEGER:
-			len = 4;
-			*((UInt4 *) Value) = ival;
-			break;
-		case SQL_IS_SMALLINT:
-			len = 2;
-			*((SQLSMALLINT *) Value) = (SQLSMALLINT) ival;
-			break;
-		case SQL_IS_USMALLINT:
-			len = 2;
-			*((SQLUSMALLINT *) Value) = (SQLUSMALLINT) ival;
-			break;
 		case SQL_IS_POINTER:
 			len = 4;
 			*((void **)Value) = ptr;
diff --git a/src/interfaces/odbc/pgapifunc.h b/src/interfaces/odbc/pgapifunc.h
index a906a311debce5020c3f0b5117e3cd1942a6596c..f4c740bca84c60748434c3077776de63543c36e6 100644
--- a/src/interfaces/odbc/pgapifunc.h
+++ b/src/interfaces/odbc/pgapifunc.h
@@ -265,6 +265,10 @@ RETCODE SQL_API PGAPI_GetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle,
 		SQLSMALLINT RecNumber, SQLCHAR *Sqlstate,
 		SQLINTEGER *NativeError, SQLCHAR *MessageText,
 		SQLSMALLINT BufferLength, SQLSMALLINT *TextLength);
+RETCODE SQL_API PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle,
+		SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier,
+		PTR DiagInfoPtr, SQLSMALLINT BufferLength,
+		SQLSMALLINT *StringLengthPtr);
 RETCODE SQL_API PGAPI_GetConnectAttr(HDBC ConnectionHandle,
 			SQLINTEGER Attribute, PTR Value,
 			SQLINTEGER BufferLength, SQLINTEGER *StringLength);
diff --git a/src/interfaces/odbc/psqlodbc.h b/src/interfaces/odbc/psqlodbc.h
index a0d4be134b1435d411d2564690f8c316f6d9d1a6..839c6e4876cde49792d2592304e12c8a4e5c0ef5 100644
--- a/src/interfaces/odbc/psqlodbc.h
+++ b/src/interfaces/odbc/psqlodbc.h
@@ -5,7 +5,7 @@
  *
  * Comments:		See "notice.txt" for copyright and license information.
  *
- * $Id: psqlodbc.h,v 1.61 2002/03/28 08:08:06 inoue Exp $
+ * $Id: psqlodbc.h,v 1.62 2002/04/01 03:01:15 inoue Exp $
  *
  */
 
diff --git a/src/interfaces/odbc/qresult.c b/src/interfaces/odbc/qresult.c
index 923448abeba844a9dea9140fff1817f69c559dcc..2817a7f316a3e2fc10e0dc746c0afa205207cff9 100644
--- a/src/interfaces/odbc/qresult.c
+++ b/src/interfaces/odbc/qresult.c
@@ -123,6 +123,7 @@ QR_Constructor()
 		rv->rowset_size = 1;
 		rv->haskeyset = 0;
 		rv->keyset = NULL;
+		rv->rb_alloc = 0;
 		rv->rb_count = 0;
 		rv->rollback = NULL;
 	}
@@ -233,6 +234,7 @@ QR_free_memory(QResultClass *self)
 	if (self->rollback)
 	{
 		free(self->rollback);
+		self->rb_alloc = 0;
 		self->rb_count = 0;
 		self->rollback = NULL;
 	}
diff --git a/src/interfaces/odbc/qresult.h b/src/interfaces/odbc/qresult.h
index a7291c43a6f325a6f0e9440fd91b48e63b7236ec..1bf1ce93b742f24d03f4b92fa6c791ee63948bf7 100644
--- a/src/interfaces/odbc/qresult.h
+++ b/src/interfaces/odbc/qresult.h
@@ -74,7 +74,8 @@ struct QResultClass_
 	char		aborted;		/* was aborted? */
 	char		haskeyset;		/* this result contains keyset ? */
 	KeySet		*keyset;
-	UInt4		rb_count;	/* count of rollback info */	
+	UInt2		rb_alloc;	/* count of allocated rollback info */	
+	UInt2		rb_count;	/* count of rollback info */	
 	Rollback	*rollback;	
 };
 
diff --git a/src/interfaces/odbc/results.c b/src/interfaces/odbc/results.c
index 6d20bbaad593b483f523ae133f7fc75019f880dd..517d5a234d1c99b8b9c91eadbd96e7087fdcb77e 100644
--- a/src/interfaces/odbc/results.c
+++ b/src/interfaces/odbc/results.c
@@ -491,7 +491,7 @@ PGAPI_ColAttributes(
 			return SQL_SUCCESS;
 		}
 
-		if (stmt->parse_status != STMT_PARSE_FATAL && irdflds->fi && irdflds->fi[col_idx])
+		if (stmt->parse_status != STMT_PARSE_FATAL && irdflds->fi)
 		{
 			if (col_idx >= cols)
 			{
@@ -500,9 +500,12 @@ PGAPI_ColAttributes(
 				SC_log_error(func, "", stmt);
 				return SQL_ERROR;
 			}
-			field_type = irdflds->fi[col_idx]->type;
-			if (field_type > 0)
-				parse_ok = TRUE;
+			if (irdflds->fi[col_idx])
+			{
+				field_type = irdflds->fi[col_idx]->type;
+				if (field_type > 0)
+					parse_ok = TRUE;
+			}
 		}
 	}
 
@@ -756,14 +759,6 @@ inolog("COLUMN_TYPE=%d\n", value);
 
 		if (rgbDesc)
 		{
-#ifdef	UNICODE_SUPPORT
-			if (conn->unicode)
-			{
-				len = utf8_to_ucs2(p, len, (SQLWCHAR *) rgbDesc, cbDescMax / 2);
-				len *= 2;
-			}
-			else
-#endif /* UNICODE_SUPPORT */
 			strncpy_null((char *) rgbDesc, p, (size_t) cbDescMax);
 
 			if (len >= cbDescMax)
@@ -1343,8 +1338,6 @@ PGAPI_ExtendedFetch(
 #endif   /* DRIVER_CURSOR_IMPLEMENT */
 			else
 				*(rgfRowStatus + i) = SQL_ROW_SUCCESS;
-if (rgfRowStatus[i] != SQL_ROW_SUCCESS)
-inolog("rgfRowStatus[%d]=%d\n", i, rgfRowStatus[i]);
 		}
 	}
 
@@ -1426,6 +1419,121 @@ static void KeySetSet(const TupleField *tuple, int num_fields, KeySet *keyset)
 	sscanf(tuple[num_fields - 1].value, "%u", &keyset->oid);
 }
 
+static void AddRollback(ConnectionClass *conn, QResultClass *res, int index, const KeySet *keyset)
+{
+	Rollback *rollback;
+
+	if (!res->rollback)
+	{
+		res->rb_count = 0;
+		res->rb_alloc = 10;
+		rollback = res->rollback = malloc(sizeof(Rollback) * res->rb_alloc);
+	}
+	else
+	{
+		if (res->rb_count >= res->rb_alloc)
+		{
+			res->rb_alloc *= 2; 
+			if (rollback = realloc(res->rollback, sizeof(Rollback) * res->rb_alloc), !rollback)
+			{
+				res->rb_alloc = res->rb_count = 0;
+				return;
+			}
+			res->rollback = rollback; 
+		}
+		rollback = res->rollback + res->rb_count;
+	}
+	rollback->index = index;
+	if (keyset)
+	{
+		rollback->blocknum = keyset[index].blocknum;
+		rollback->offset = keyset[index].offset;
+	}
+	else
+	{
+		rollback->offset = 0;
+		rollback->blocknum = 0;
+	}
+
+	conn->result_uncommitted = 1;
+	res->rb_count++;	
+}
+
+static void DiscardRollback(QResultClass *res)
+{
+	int	i, index;
+	UWORD	status;
+	Rollback *rollback;
+	KeySet	*keyset;
+
+	if (0 == res->rb_count || NULL == res->rollback)
+		return;
+	rollback = res->rollback;
+	keyset = res->keyset;
+	for (i = 0; i < res->rb_count; i++)
+	{
+		index = rollback[i].index;
+		status = keyset[index].status;
+		keyset[index].status &= ~(CURS_SELF_DELETING | CURS_SELF_UPDATING | CURS_SELF_ADDING);
+		keyset[index].status |= ((status & (CURS_SELF_DELETING | CURS_SELF_UPDATING | CURS_SELF_ADDING)) << 3);
+	}
+	free(rollback);
+	res->rollback = NULL;
+	res->rb_count = res->rb_alloc = 0;
+}
+
+static void UndoRollback(QResultClass *res)
+{
+	int	i, index;
+	UWORD	status;
+	Rollback *rollback;
+	KeySet	*keyset;
+
+	if (0 == res->rb_count || NULL == res->rollback)
+		return;
+	rollback = res->rollback;
+	keyset = res->keyset;
+	for (i = res->rb_count - 1; i >= 0; i--)
+	{
+		index = rollback[i].index;
+		status = keyset[index].status;
+		if ((status & CURS_SELF_ADDING) != 0)
+		{
+			if (index < res->fcount)
+				res->fcount = index;
+		}
+		else
+		{
+			keyset[index].status &= ~(CURS_SELF_DELETING | CURS_SELF_UPDATING | CURS_SELF_ADDING | KEYSET_INFO_PUBLIC);
+			keyset[index].blocknum = rollback[i].blocknum;
+			keyset[index].offset = rollback[i].offset;
+		}
+	}
+	free(rollback);
+	res->rollback = NULL;
+	res->rb_count = res->rb_alloc = 0;
+}
+
+void	ProcessRollback(ConnectionClass *conn, BOOL undo) 
+{
+	int	i;
+	StatementClass	*stmt;
+	QResultClass	*res;
+
+	for (i = 0; i < conn->num_stmts; i++)
+	{
+		if (stmt = conn->stmts[i], !stmt)
+			continue;
+		for (res = SC_get_Result(stmt); res; res = res->next)
+		{
+			if (undo)
+				UndoRollback(res);
+			else
+				DiscardRollback(res);
+		}
+	}
+}
+
 #define	LATEST_TUPLE_LOAD	1L
 #define	USE_INSERTED_TID	(1L << 1)
 static QResultClass *
@@ -1534,7 +1642,8 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UDWORD global_ridx, UWORD *count
 			ret = SQL_SUCCESS_WITH_INFO;
 			if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
 			{
-				res->keyset[global_ridx].oid = 0;
+				res->keyset[global_ridx].blocknum = 0;
+				res->keyset[global_ridx].offset = 0;
 				res->keyset[global_ridx].status |= SQL_ROW_DELETED;
 			}
 		}
@@ -1670,6 +1779,7 @@ SC_pos_update(StatementClass *stmt,
 				num_cols,
 				upd_cols;
 	QResultClass *res;
+	ConnectionClass	*conn = SC_get_conn(stmt);
 	ARDFields	*opts = SC_get_ARD(stmt);
 	IRDFields	*irdflds = SC_get_IRD(stmt);
 	BindInfoClass *bindings = opts->bindings;
@@ -1735,7 +1845,7 @@ SC_pos_update(StatementClass *stmt,
 		sprintf(updstr, "%s where ctid = '(%u, %u)' and oid = %u", updstr,
 				blocknum, pgoffset, oid);
 		mylog("updstr=%s\n", updstr);
-		if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS)
+		if (PGAPI_AllocStmt(conn, &hstmt) != SQL_SUCCESS)
 			return SQL_ERROR;
 		qstmt = (StatementClass *) hstmt;
 		apdopts = SC_get_APD(qstmt);
@@ -1788,8 +1898,11 @@ SC_pos_update(StatementClass *stmt,
 	}
 	if (SQL_SUCCESS == ret && res->keyset)
 	{
-		if (CC_is_in_trans(SC_get_conn(stmt)))
+		if (CC_is_in_trans(conn))
+		{
+			AddRollback(conn, res, global_ridx, res->keyset);
 			res->keyset[global_ridx].status |= (SQL_ROW_UPDATED  | CURS_SELF_UPDATING);
+		}
 		else
 			res->keyset[global_ridx].status |= (SQL_ROW_UPDATED  | CURS_SELF_UPDATED);
 	}
@@ -1815,12 +1928,12 @@ SC_pos_delete(StatementClass *stmt,
 {
 	UWORD		offset;
 	QResultClass *res, *qres;
+	ConnectionClass	*conn = SC_get_conn(stmt);
 	ARDFields	*opts = SC_get_ARD(stmt);
 	IRDFields	*irdflds = SC_get_IRD(stmt);
 	BindInfoClass *bindings = opts->bindings;
 	char		dltstr[4096];
 	RETCODE		ret;
-	/*const char	   *oidval;*/
 	UInt4		oid, blocknum;
 
 	mylog("POS DELETE ti=%x\n", stmt->ti);
@@ -1844,7 +1957,7 @@ SC_pos_delete(StatementClass *stmt,
 			stmt->ti[0]->name, blocknum, offset, oid);
 
 	mylog("dltstr=%s\n", dltstr);
-	qres = CC_send_query(SC_get_conn(stmt), dltstr, NULL, CLEAR_RESULT_ON_ABORT);
+	qres = CC_send_query(conn, dltstr, NULL, CLEAR_RESULT_ON_ABORT);
 	ret = SQL_SUCCESS;
 	if (qres && QR_command_successful(qres))
 	{
@@ -1881,8 +1994,11 @@ SC_pos_delete(StatementClass *stmt,
 		QR_Destructor(qres);
 	if (SQL_SUCCESS == ret && res->keyset)
 	{
-		if (CC_is_in_trans(SC_get_conn(stmt)))
+		if (CC_is_in_trans(conn))
+		{
+			AddRollback(conn, res, global_ridx, res->keyset);
 			res->keyset[global_ridx].status |= (SQL_ROW_DELETED | CURS_SELF_DELETING);
+		}
 		else
 			res->keyset[global_ridx].status |= (SQL_ROW_DELETED | CURS_SELF_DELETED);
 	}
@@ -1988,7 +2104,7 @@ SC_pos_add(StatementClass *stmt,
 	num_cols = irdflds->nfields;
 	conn = SC_get_conn(stmt);
 	sprintf(addstr, "insert into \"%s\" (", stmt->ti[0]->name);
-	if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS)
+	if (PGAPI_AllocStmt(conn, &hstmt) != SQL_SUCCESS)
 		return SQL_ERROR;
 	if (opts->row_offset_ptr)
 		offset = *opts->row_offset_ptr;
@@ -2068,10 +2184,15 @@ SC_pos_add(StatementClass *stmt,
 	PGAPI_FreeStmt(hstmt, SQL_DROP);
 	if (SQL_SUCCESS == ret && res->keyset)
 	{
+		int	global_ridx = res->fcount - 1;
 		if (CC_is_in_trans(conn))
-			res->keyset[res->fcount - 1].status |= (SQL_ROW_ADDED | CURS_SELF_ADDING);
+		{
+
+			AddRollback(conn, res, global_ridx, NULL);
+			res->keyset[global_ridx].status |= (SQL_ROW_ADDED | CURS_SELF_ADDING);
+		}
 		else
-			res->keyset[res->fcount - 1].status |= (SQL_ROW_ADDED | CURS_SELF_ADDED);
+			res->keyset[global_ridx].status |= (SQL_ROW_ADDED | CURS_SELF_ADDED);
 	}
 #if (ODBCVER >= 0x0300)
 	if (irdflds->rowStatusArray)
diff --git a/src/interfaces/odbc/statement.c b/src/interfaces/odbc/statement.c
index f48a1724219da2edac54a16ebe8d69618966e154..6bdac480872929beb87e9c2185d4b06c43415187 100644
--- a/src/interfaces/odbc/statement.c
+++ b/src/interfaces/odbc/statement.c
@@ -503,8 +503,8 @@ SC_recycle_statement(StatementClass *self)
 		int	i;
 
 		for (i = 0; i < self->ntab; i++)
-			if (self->ti)
-				free(self->ti);
+			if (self->ti[i])
+				free(self->ti[i]);
 		self->ti = NULL;
 		self->ntab = 0;
 	}