From 01e322652b14c2aeac1737f29f8bcf9e18d1060e Mon Sep 17 00:00:00 2001
From: Hiroshi Inoue <inoue@tpf.co.jp>
Date: Thu, 14 Mar 2002 05:42:04 +0000
Subject: [PATCH] 1) Internal improvements to handle updatable cursors(1st
 cut). 2) Fix a bug in SQLColAttribute().

---
 src/interfaces/odbc/connection.c        |  22 +-
 src/interfaces/odbc/connection.h        |   5 +-
 src/interfaces/odbc/convert.c           |  24 ++-
 src/interfaces/odbc/environ.c           |   3 +
 src/interfaces/odbc/execute.c           |  21 +-
 src/interfaces/odbc/info.c              |  28 +--
 src/interfaces/odbc/info30.c            |  31 +--
 src/interfaces/odbc/multibyte.c         |   2 +-
 src/interfaces/odbc/odbcapi30.c         | 168 +--------------
 src/interfaces/odbc/options.c           |  85 +++-----
 src/interfaces/odbc/parse.c             |  38 +++-
 src/interfaces/odbc/psqlodbc.h          |   3 +-
 src/interfaces/odbc/psqlodbc_api30.def  |   2 +-
 src/interfaces/odbc/psqlodbc_api30w.def |   2 +-
 src/interfaces/odbc/qresult.c           |  32 ++-
 src/interfaces/odbc/qresult.h           |   4 +
 src/interfaces/odbc/results.c           | 259 +++++++++++++++++-------
 src/interfaces/odbc/statement.c         |  38 ++--
 src/interfaces/odbc/statement.h         |   6 +
 src/interfaces/odbc/tuple.h             |  13 ++
 20 files changed, 414 insertions(+), 372 deletions(-)

diff --git a/src/interfaces/odbc/connection.c b/src/interfaces/odbc/connection.c
index d176d925d4a..898b41ec289 100644
--- a/src/interfaces/odbc/connection.c
+++ b/src/interfaces/odbc/connection.c
@@ -374,7 +374,7 @@ CC_begin(ConnectionClass *self)
 	char	ret = TRUE;
 	if (!CC_is_in_trans(self))
 	{
-		QResultClass *res = CC_send_query(self, "BEGIN", NULL, TRUE);
+		QResultClass *res = CC_send_query(self, "BEGIN", NULL, CLEAR_RESULT_ON_ABORT);
 		mylog("CC_begin:  sending BEGIN!\n");
 
 		if (res != NULL)
@@ -401,7 +401,7 @@ CC_commit(ConnectionClass *self)
 	char	ret = FALSE;
 	if (CC_is_in_trans(self))
 	{
-		QResultClass *res = CC_send_query(self, "COMMIT", NULL, TRUE);
+		QResultClass *res = CC_send_query(self, "COMMIT", NULL, CLEAR_RESULT_ON_ABORT);
 		mylog("CC_commit:  sending COMMIT!\n");
 
 		CC_set_no_trans(self);
@@ -427,7 +427,7 @@ CC_abort(ConnectionClass *self)
 {
 	if (CC_is_in_trans(self))
 	{
-		QResultClass *res = CC_send_query(self, "ROLLBACK", NULL, TRUE);
+		QResultClass *res = CC_send_query(self, "ROLLBACK", NULL, CLEAR_RESULT_ON_ABORT);
 		mylog("CC_abort:  sending ABORT!\n");
 
 		CC_set_no_trans(self);
@@ -919,7 +919,7 @@ another_version_retry:
 	 */
 	mylog("sending an empty query...\n");
 
-	res = CC_send_query(self, " ", NULL, TRUE);
+	res = CC_send_query(self, " ", NULL, CLEAR_RESULT_ON_ABORT);
 	if (res == NULL || QR_get_status(res) != PGRES_EMPTY_QUERY)
 	{
 		mylog("got no result from the empty query.  (probably database does not exist)\n");
@@ -956,7 +956,7 @@ another_version_retry:
 	 *	Multibyte handling is available ?
 	 */
 #ifdef MULTIBYTE
-	if (PG_VERSION_GE(self, 7.0))
+	if (PG_VERSION_GE(self, 6.4))
 	{
 		CC_lookup_characterset(self);
 		if (self->errornumber != 0)
@@ -977,7 +977,7 @@ another_version_retry:
 				if (self->client_encoding)
 					free(self->client_encoding);
 				self->client_encoding = NULL;
-				if (res = CC_send_query(self, "set client_encoding to 'UTF8'", NULL, TRUE), res)
+				if (res = CC_send_query(self, "set client_encoding to 'UTF8'", NULL, CLEAR_RESULT_ON_ABORT), res)
 				{
 					self->client_encoding = strdup("UNICODE");
 					QR_Destructor(res);
@@ -991,7 +991,7 @@ another_version_retry:
 	else if (self->unicode)
 	{
 		self->errornumber = CONN_NOT_IMPLEMENTED_ERROR;
-		self->errormsg = "Unicode isn't supported before 7.0";
+		self->errormsg = "Unicode isn't supported before 6.4";
 		return 0;
 	}
 #endif /* UNICODE_SUPPORT */
@@ -1128,12 +1128,14 @@ CC_get_error(ConnectionClass *self, int *number, char **message)
  *	'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements.
  */
 QResultClass *
-CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, BOOL clear_result_on_abort)
+CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag)
 {
 	QResultClass *result_in = NULL,
 			   *cmdres = NULL,
 			   *retres = NULL,
 			   *res = NULL;
+	BOOL	clear_result_on_abort = ((flag & CLEAR_RESULT_ON_ABORT) != 0),
+		create_keyset = ((flag & CREATE_KEYSET) != 0);
 	char		swallow,
 			   *wq;
 	int			id;
@@ -1376,6 +1378,8 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, BOOL clear_resu
 				if (query_completed)
 				{
 					res->next = QR_Constructor();
+					if (create_keyset)
+						QR_set_haskeyset(res->next);
 					mylog("send_query: 'T' no result_in: res = %u\n", res->next);
 					if (!res->next)
 					{
@@ -1392,6 +1396,8 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, BOOL clear_resu
 				}
 				if (!used_passed_result_object)
 				{
+					if (create_keyset)
+						QR_set_haskeyset(res);
 					if (!QR_fetch_tuples(res, self, qi ? qi->cursor : NULL))
 					{
 						self->errornumber = CONNECTION_COULD_NOT_RECEIVE;
diff --git a/src/interfaces/odbc/connection.h b/src/interfaces/odbc/connection.h
index 408d11836c5..ee19d27dcb7 100644
--- a/src/interfaces/odbc/connection.h
+++ b/src/interfaces/odbc/connection.h
@@ -305,7 +305,7 @@ char		CC_connect(ConnectionClass *self, char do_password);
 char		CC_add_statement(ConnectionClass *self, StatementClass *stmt);
 char		CC_remove_statement(ConnectionClass *self, StatementClass *stmt);
 char		CC_get_error(ConnectionClass *self, int *number, char **message);
-QResultClass *CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, BOOL);
+QResultClass *CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag);
 void		CC_clear_error(ConnectionClass *self);
 char	   *CC_create_errormsg(ConnectionClass *self);
 int			CC_send_function(ConnectionClass *conn, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *argv, int nargs);
@@ -316,4 +316,7 @@ 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);
 
+/* CC_send_query_options */
+#define	CLEAR_RESULT_ON_ABORT	1L
+#define	CREATE_KEYSET		(1L << 1) /* create keyset for updatable curosrs */
 #endif
diff --git a/src/interfaces/odbc/convert.c b/src/interfaces/odbc/convert.c
index 6ba6587f640..c0bf5cf2219 100644
--- a/src/interfaces/odbc/convert.c
+++ b/src/interfaces/odbc/convert.c
@@ -1291,6 +1291,7 @@ copy_statement_with_parameters(StatementClass *stmt)
 #ifdef	DRIVER_CURSOR_IMPLEMENT
 	BOOL		search_from_pos = FALSE;
 #endif   /* DRIVER_CURSOR_IMPLEMENT */
+	Int4	from_pos = -1, where_pos = -1;
 
 	if (ci->disallow_premature)
 		prepare_dummy_cursor = stmt->pre_executing;
@@ -1326,7 +1327,11 @@ copy_statement_with_parameters(StatementClass *stmt)
 		else if (!stmt->ti || stmt->ntab != 1)
 			stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
 		else
-			search_from_pos = TRUE;
+		{
+			/** search_from_pos = TRUE; **/
+			from_pos = stmt->from_pos;
+			where_pos = stmt->where_pos;
+		}
 	}
 #endif   /* DRIVER_CURSOR_IMPLEMENT */
 
@@ -1366,9 +1371,18 @@ copy_statement_with_parameters(StatementClass *stmt)
 #ifdef MULTIBYTE
 	make_encoded_str(&encstr, conn, old_statement);
 #endif
-
 	for (opos = 0; opos < oldstmtlen; opos++)
 	{
+		if (from_pos == (Int4) opos)
+		{
+			CVT_APPEND_STR(", CTID, OID ");
+		}
+		else if (where_pos == (Int4) opos)
+		{
+			stmt->load_statement = malloc(npos + 1);
+			memcpy(stmt->load_statement, new_statement, npos);
+			stmt->load_statement[npos] = '\0';
+		}
 #ifdef MULTIBYTE
 		oldchar = encoded_byte_check(&encstr, opos);
 		if (ENCODE_STATUS(encstr) != 0)
@@ -2033,6 +2047,12 @@ copy_statement_with_parameters(StatementClass *stmt)
 #ifdef	DRIVER_CURSOR_IMPLEMENT
 	if (search_from_pos)
 		stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
+	if (!stmt->load_statement && from_pos >=0)
+	{
+		stmt->load_statement = malloc(npos + 1);
+		memcpy(stmt->load_statement, new_statement, npos);
+		stmt->load_statement[npos] = '\0';
+	}
 #endif   /* DRIVER_CURSOR_IMPLEMENT */
 	if (prepare_dummy_cursor && SC_is_pre_executable(stmt))
 	{
diff --git a/src/interfaces/odbc/environ.c b/src/interfaces/odbc/environ.c
index 69718a19149..78737fbb332 100644
--- a/src/interfaces/odbc/environ.c
+++ b/src/interfaces/odbc/environ.c
@@ -245,6 +245,9 @@ PGAPI_StmtError(	HSTMT hstmt,
 			case STMT_INVALID_CURSOR_STATE_ERROR:
 				strcpy(szSqlState, "24000");
 				break;
+			case STMT_ERROR_IN_ROW:
+				strcpy(szSqlState, "01S01");
+				break;
 			case STMT_OPTION_VALUE_CHANGED:
 				strcpy(szSqlState, "01S02");
 				break;
diff --git a/src/interfaces/odbc/execute.c b/src/interfaces/odbc/execute.c
index ae095e9691e..8f05024f454 100644
--- a/src/interfaces/odbc/execute.c
+++ b/src/interfaces/odbc/execute.c
@@ -94,6 +94,12 @@ PGAPI_Prepare(HSTMT hstmt,
 
 	if (self->statement)
 		free(self->statement);
+	if (self->stmt_with_params)
+		free(self->stmt_with_params);
+	self->stmt_with_params = NULL;
+	if (self->load_statement)
+		free(self->load_statement);
+	self->load_statement = NULL;
 
 	self->statement = make_string(szSqlStr, cbSqlStr, NULL);
 	if (!self->statement)
@@ -141,6 +147,12 @@ PGAPI_ExecDirect(
 
 	if (stmt->statement)
 		free(stmt->statement);
+	if (stmt->stmt_with_params)
+		free(stmt->stmt_with_params);
+	stmt->stmt_with_params = NULL;
+	if (stmt->load_statement)
+		free(stmt->load_statement);
+	stmt->load_statement = NULL;
 
 	/*
 	 * keep a copy of the un-parametrized statement, in case they try to
@@ -421,7 +433,7 @@ next_param_row:
 		BOOL		in_trans = CC_is_in_trans(conn);
 		BOOL		issued_begin = FALSE,
 					begin_included = FALSE;
-		QResultClass *res;
+		QResultClass *res, *curres;
 
 		if (strnicmp(stmt->stmt_with_params, "BEGIN;", 6) == 0)
 			begin_included = TRUE;
@@ -436,7 +448,7 @@ next_param_row:
 		}
 		/* we are now in a transaction */
 		CC_set_in_trans(conn);
-		res = CC_send_query(conn, stmt->stmt_with_params, NULL, TRUE);
+		res = CC_send_query(conn, stmt->stmt_with_params, NULL, CLEAR_RESULT_ON_ABORT);
 		if (!res)
 		{
 			CC_abort(conn);
@@ -445,6 +457,9 @@ next_param_row:
 			return SQL_ERROR;
 		}
 		SC_set_Result(stmt, res);
+		for (curres = res; !curres->num_fields; curres = curres->next)
+			;
+		SC_set_Curres(stmt, curres);
 		if (CC_is_in_autocommit(conn))
 		{
 			if (issued_begin)
@@ -518,7 +533,7 @@ PGAPI_Transact(
 	{
 		mylog("PGAPI_Transact: sending on conn %d '%s'\n", conn, stmt_string);
 
-		res = CC_send_query(conn, stmt_string, NULL, TRUE);
+		res = CC_send_query(conn, stmt_string, NULL, CLEAR_RESULT_ON_ABORT);
 		CC_set_no_trans(conn);
 
 		if (!res)
diff --git a/src/interfaces/odbc/info.c b/src/interfaces/odbc/info.c
index 10a900207c6..4d929803311 100644
--- a/src/interfaces/odbc/info.c
+++ b/src/interfaces/odbc/info.c
@@ -2858,7 +2858,7 @@ getClientTableName(ConnectionClass *conn, char *serverTableName, BOOL *nameAlloc
 		return ret;
 	if (!conn->server_encoding)
 	{
-		if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, TRUE), res)
+		if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, CLEAR_RESULT_ON_ABORT), res)
 		{
 			if (QR_get_num_tuples(res) > 0)
 				conn->server_encoding = strdup(QR_get_value_backend_row(res, 0, 0));
@@ -2868,11 +2868,11 @@ getClientTableName(ConnectionClass *conn, char *serverTableName, BOOL *nameAlloc
 	if (!conn->server_encoding)
 		return ret;
 	sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->server_encoding);
-	bError = (CC_send_query(conn, query, NULL, TRUE) == NULL);
+	bError = (CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT) == NULL);
 	if (!bError && continueExec)
 	{
 		sprintf(query, "select OID from pg_class where relname = '%s'", serverTableName);
-		if (res = CC_send_query(conn, query, NULL, TRUE), res)
+		if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res)
 		{
 			if (QR_get_num_tuples(res) > 0)
 				strcpy(saveoid, QR_get_value_backend_row(res, 0, 0));
@@ -2891,11 +2891,11 @@ getClientTableName(ConnectionClass *conn, char *serverTableName, BOOL *nameAlloc
 	}
 	/* restore the client encoding */
 	sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->client_encoding);
-	bError = (CC_send_query(conn, query, NULL, TRUE) == NULL);
+	bError = (CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT) == NULL);
 	if (bError || !continueExec)
 		return ret;
 	sprintf(query, "select relname from pg_class where OID = %s", saveoid);
-	if (res = CC_send_query(conn, query, NULL, TRUE), res)
+	if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res)
 	{
 		if (QR_get_num_tuples(res) > 0)
 		{
@@ -2922,7 +2922,7 @@ getClientColumnName(ConnectionClass *conn, const char *serverTableName, char *se
 		return ret;
 	if (!conn->server_encoding)
 	{
-		if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, TRUE), res)
+		if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, CLEAR_RESULT_ON_ABORT), res)
 		{
 			if (QR_get_num_tuples(res) > 0)
 				conn->server_encoding = strdup(QR_get_value_backend_row(res, 0, 0));
@@ -2932,13 +2932,13 @@ getClientColumnName(ConnectionClass *conn, const char *serverTableName, char *se
 	if (!conn->server_encoding)
 		return ret;
 	sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->server_encoding);
-	bError = (CC_send_query(conn, query, NULL, TRUE) == NULL);
+	bError = (CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT) == NULL);
 	if (!bError && continueExec)
 	{
 		sprintf(query, "select attrelid, attnum from pg_class, pg_attribute "
 				"where relname = '%s' and attrelid = pg_class.oid "
 				"and attname = '%s'", serverTableName, serverColumnName);
-		if (res = CC_send_query(conn, query, NULL, TRUE), res)
+		if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res)
 		{
 			if (QR_get_num_tuples(res) > 0)
 			{
@@ -2960,11 +2960,11 @@ getClientColumnName(ConnectionClass *conn, const char *serverTableName, char *se
 	}
 	/* restore the cleint encoding */
 	sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->client_encoding);
-	bError = (CC_send_query(conn, query, NULL, TRUE) == NULL);
+	bError = (CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT) == NULL);
 	if (bError || !continueExec)
 		return ret;
 	sprintf(query, "select attname from pg_attribute where attrelid = %s and attnum = %s", saveattrelid, saveattnum);
-	if (res = CC_send_query(conn, query, NULL, TRUE), res)
+	if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res)
 	{
 		if (QR_get_num_tuples(res) > 0)
 		{
@@ -3790,7 +3790,7 @@ PGAPI_Procedures(
 		   " case when prorettype = 0 then 1::int2 else 2::int2 end as " "PROCEDURE_TYPE" " from pg_proc");
 	my_strcat(proc_query, " where proname like '%.*s'", szProcName, cbProcName);
 
-	if (res = CC_send_query(conn, proc_query, NULL, TRUE), !res)
+	if (res = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), !res)
 	{
 		stmt->errornumber = STMT_EXEC_ERROR;
 		stmt->errormsg = "PGAPI_Procedures query error";
@@ -3927,7 +3927,7 @@ PGAPI_TablePrivileges(
 		my_strcat(proc_query, " relname like '%.*s' and", esc_table_name, escTbnamelen);
 	}
 	strcat(proc_query, " pg_user.usesysid = relowner"); 
-	if (res = CC_send_query(conn, proc_query, NULL, TRUE), !res)
+	if (res = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), !res)
 	{
 		stmt->errornumber = STMT_EXEC_ERROR;
 		stmt->errormsg = "PGAPI_TablePrivileges query error";
@@ -3935,7 +3935,7 @@ PGAPI_TablePrivileges(
 	}
 	strncpy_null(proc_query, "select usename, usesysid, usesuper from pg_user", sizeof(proc_query));
 	tablecount = QR_get_num_tuples(res);
-	if (allures = CC_send_query(conn, proc_query, NULL, TRUE), !allures)
+	if (allures = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), !allures)
 	{
 		QR_Destructor(res);
 		stmt->errornumber = STMT_EXEC_ERROR;
@@ -3983,7 +3983,7 @@ PGAPI_TablePrivileges(
 				char	*grolist, *uid, *delm;
 
 				snprintf(proc_query, sizeof(proc_query) - 1, "select grolist from pg_group where groname = '%s'", user);
-				if (gres = CC_send_query(conn, proc_query, NULL, TRUE))
+				if (gres = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT))
 				{
 					grolist = QR_get_value_backend_row(gres, 0, 0);
 					if (grolist && grolist[0] == '{')
diff --git a/src/interfaces/odbc/info30.c b/src/interfaces/odbc/info30.c
index b606f0dc622..414110b8938 100644
--- a/src/interfaces/odbc/info30.c
+++ b/src/interfaces/odbc/info30.c
@@ -47,14 +47,15 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue,
 			break;
 		case SQL_KEYSET_CURSOR_ATTRIBUTES1:
 			len = 4;
-			value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE
+			value = 0;
+			if (ci->updatable_cursors || ci->drivers.lie)
+				value |= (SQL_CA1_NEXT | SQL_CA1_ABSOLUTE
 				| SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK
 				| SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION
 				| SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE
-				| SQL_CA1_POS_REFRESH;
+				| SQL_CA1_POS_REFRESH);
 			if (ci->drivers.lie)
-				value |=
-				( SQL_CA1_BULK_ADD
+				value |= (SQL_CA1_BULK_ADD
 				| SQL_CA1_BULK_UPDATE_BY_BOOKMARK
 				| SQL_CA1_BULK_DELETE_BY_BOOKMARK
 				| SQL_CA1_BULK_FETCH_BY_BOOKMARK
@@ -68,13 +69,14 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue,
 			break;
 		case SQL_KEYSET_CURSOR_ATTRIBUTES2:
 			len = 4;
-			value = SQL_CA2_OPT_ROWVER_CONCURRENCY
+			value = 0;
+			if (ci->updatable_cursors || ci->drivers.lie)
+				value |= (SQL_CA2_OPT_ROWVER_CONCURRENCY
 				| SQL_CA2_SENSITIVITY_ADDITIONS
 				| SQL_CA2_SENSITIVITY_DELETIONS
-				| SQL_CA2_SENSITIVITY_UPDATES;
+				| SQL_CA2_SENSITIVITY_UPDATES);
 			if (ci->drivers.lie)
-				value |=
-				( SQL_CA2_READ_ONLY_CONCURRENCY
+				value |= (SQL_CA2_READ_ONLY_CONCURRENCY
 				| SQL_CA2_LOCK_CONCURRENCY
 				| SQL_CA2_OPT_VALUES_CONCURRENCY
 				| SQL_CA2_MAX_ROWS_SELECT
@@ -95,16 +97,19 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue,
 			len = 4;
 			value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE
 				| SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK
-				| SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION
-				| SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE
-				| SQL_CA1_POS_REFRESH;
+				| SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION;
+			if (ci->updatable_cursors)
+				value |= (SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE
+				| SQL_CA1_POS_REFRESH);
 			break;
 		case SQL_STATIC_CURSOR_ATTRIBUTES2:
 			len = 4;
-			value = SQL_CA2_OPT_ROWVER_CONCURRENCY
+			value = 0;
+			if (ci->updatable_cursors)
+				value |= (SQL_CA2_OPT_ROWVER_CONCURRENCY
 				| SQL_CA2_SENSITIVITY_ADDITIONS
 				| SQL_CA2_SENSITIVITY_DELETIONS
-				| SQL_CA2_SENSITIVITY_UPDATES;
+				| SQL_CA2_SENSITIVITY_UPDATES);
 			break;
 
 		case SQL_ODBC_INTERFACE_CONFORMANCE:
diff --git a/src/interfaces/odbc/multibyte.c b/src/interfaces/odbc/multibyte.c
index 11d35369ddf..2458abbe69b 100644
--- a/src/interfaces/odbc/multibyte.c
+++ b/src/interfaces/odbc/multibyte.c
@@ -300,7 +300,7 @@ CC_lookup_cs_new(ConnectionClass *self)
 	char		*encstr = NULL;
 	QResultClass	*res;
 
-	res = CC_send_query(self, "select pg_client_encoding()", NULL, TRUE);
+	res = CC_send_query(self, "select pg_client_encoding()", NULL, CLEAR_RESULT_ON_ABORT);
 	if (res)
 	{
 		char 	*enc = QR_get_value_backend_row(res, 0, 0);
diff --git a/src/interfaces/odbc/odbcapi30.c b/src/interfaces/odbc/odbcapi30.c
index 6d4ad1f2877..c4efdd56e52 100644
--- a/src/interfaces/odbc/odbcapi30.c
+++ b/src/interfaces/odbc/odbcapi30.c
@@ -419,177 +419,17 @@ SQLSetConnectAttr(HDBC ConnectionHandle,
 	return PGAPI_SetConnectOption(ConnectionHandle, (UWORD) Attribute, (UDWORD) Value);
 }
 
-static RETCODE SQL_API
-ARDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
-		SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength)
-{
-	RETCODE		ret = SQL_SUCCESS;
-	PTR		tptr;
-	switch (FieldIdentifier)
-	{
-		case SQL_DESC_ARRAY_SIZE:
-			stmt->options.rowset_size = (SQLUINTEGER) Value;
-			break; 
-		case SQL_DESC_ARRAY_STATUS_PTR:
-			stmt->options.row_operation_ptr = Value;
-			break;
-		case SQL_DESC_BIND_OFFSET_PTR:
-			stmt->options.row_offset_ptr = Value;
-			break;
-		case SQL_DESC_BIND_TYPE:
-			stmt->options.bind_size = (SQLUINTEGER) Value;
-			break;
-
-		case SQL_DESC_DATA_PTR:
-			if (!RecNumber)
-				stmt->bookmark.buffer = Value;
-			else
-				stmt->bindings[RecNumber - 1].buffer = Value;
-			break;
-		case SQL_DESC_INDICATOR_PTR:
-			if (!RecNumber)
-				tptr = stmt->bookmark.used;
-			else
-				tptr = stmt->bindings[RecNumber - 1].used;
-			if (Value != tptr)
-			{
-				ret = SQL_ERROR;
-				stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; 
-				stmt->errormsg = "INDICATOR != OCTET_LENGTH_PTR"; 
-			}
-			break;
-		case SQL_DESC_OCTET_LENGTH_PTR:
-			if (!RecNumber)
-				stmt->bookmark.used = Value;
-			else
-				stmt->bindings[RecNumber - 1].used = Value;
-			break;
-		default:ret = SQL_ERROR;
-			stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; 
-			stmt->errormsg = "not implemedted yet"; 
-	}
-	return ret;
-}
-
-static RETCODE SQL_API
-APDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
-		SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength)
-{
-	RETCODE		ret = SQL_SUCCESS;
-	switch (FieldIdentifier)
-	{
-		case SQL_DESC_ARRAY_SIZE:
-			stmt->options.paramset_size = (SQLUINTEGER) Value;
-			break; 
-		case SQL_DESC_ARRAY_STATUS_PTR:
-			stmt->options.param_operation_ptr = Value;
-			break;
-		case SQL_DESC_BIND_OFFSET_PTR:
-			stmt->options.param_offset_ptr = Value;
-			break;
-		case SQL_DESC_BIND_TYPE:
-			stmt->options.param_bind_type = (SQLUINTEGER) Value;
-			break;
-
-		case SQL_DESC_DATA_PTR:
-			if (stmt->parameters_allocated < RecNumber)
-				PGAPI_BindParameter(stmt, RecNumber, 0, 0, 0, 0, 0, 0, 0, 0);
-			stmt->parameters[RecNumber - 1].buffer = Value;
-			break;
-		case SQL_DESC_INDICATOR_PTR:
-			if (stmt->parameters_allocated < RecNumber ||
-			    Value != stmt->parameters[RecNumber - 1].used)
-			{
-				ret = SQL_ERROR;
-				stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; 
-				stmt->errormsg = "INDICATOR != OCTET_LENGTH_PTR"; 
-			}
-			break;
-		case SQL_DESC_OCTET_LENGTH_PTR:
-			if (stmt->parameters_allocated < RecNumber)
-				PGAPI_BindParameter(stmt, RecNumber, 0, 0, 0, 0, 0, 0, 0, 0);
-			stmt->parameters[RecNumber - 1].used = Value;
-			break;
-		default:ret = SQL_ERROR;
-			stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; 
-	}
-	return ret;
-}
-
-static RETCODE SQL_API
-IRDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
-		SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength)
-{
-	RETCODE		ret = SQL_SUCCESS;
-	switch (FieldIdentifier)
-	{
-		case SQL_DESC_ARRAY_STATUS_PTR:
-			stmt->options.rowStatusArray = (SQLUSMALLINT *) Value;
-			break;
-		case SQL_DESC_ROWS_PROCESSED_PTR:
-			stmt->options.rowsFetched = (SQLUINTEGER *) Value;
-			break;
-		default:ret = SQL_ERROR;
-			stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; 
-	}
-	return ret;
-}
-
-static RETCODE SQL_API
-IPDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
-		SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength)
-{
-	RETCODE		ret = SQL_SUCCESS;
-	switch (FieldIdentifier)
-	{
-		case SQL_DESC_ARRAY_STATUS_PTR:
-			stmt->options.param_status_ptr = (SQLUSMALLINT *) Value;
-			break;
-		case SQL_DESC_ROWS_PROCESSED_PTR:
-			stmt->options.param_processed_ptr = (SQLUINTEGER *) Value;
-			break;
-		default:ret = SQL_ERROR;
-			stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; 
-	}
-	return ret;
-}
-
 /*	new function */
 RETCODE		SQL_API
 SQLSetDescField(SQLHDESC DescriptorHandle,
 				SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier,
 				PTR Value, SQLINTEGER BufferLength)
 {
-	RETCODE		ret = SQL_SUCCESS;
-	HSTMT		hstmt;
-	SQLUINTEGER	descType;
-	StatementClass *stmt;
-	static const char *func = "SQLSetDescField";
+	RETCODE		ret;
 
 	mylog("[[SQLSetDescField]] h=%u rec=%d field=%d val=%x\n", DescriptorHandle, RecNumber, FieldIdentifier, Value);
-	hstmt = statementHandleFromDescHandle(DescriptorHandle, &descType);
-	mylog("stmt=%x type=%d\n", hstmt, descType);
-	stmt = (StatementClass *) hstmt;
-	switch (descType)
-	{
-		case SQL_ATTR_APP_ROW_DESC:
-			ret = ARDSetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength);
-			break;
-		case SQL_ATTR_APP_PARAM_DESC:
-			ret = APDSetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength);
-			break;
-		case SQL_ATTR_IMP_ROW_DESC:
-			ret = IRDSetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength);
-			break;
-		case SQL_ATTR_IMP_PARAM_DESC:
-			ret = IPDSetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength);
-			break;
-		default:ret = SQL_ERROR;
-			stmt->errornumber = STMT_INTERNAL_ERROR; 
-			stmt->errormsg = "Error not implemented";
-	}
-	if (ret == SQL_ERROR)
-		SC_log_error(func, "", stmt);
+	ret = PGAPI_SetDescField(DescriptorHandle, RecNumber, FieldIdentifier,
+				Value, BufferLength);
 	return ret;
 }
 
@@ -602,7 +442,7 @@ SQLSetDescRec(SQLHDESC DescriptorHandle,
 			  PTR Data, SQLINTEGER *StringLength,
 			  SQLINTEGER *Indicator)
 {
-	const char *func = "SQLSetDescField";
+	const char *func = "SQLSetDescRec";
 
 	mylog("[[SQLSetDescRec]]\n");
 	mylog("Error not implemented\n");
diff --git a/src/interfaces/odbc/options.c b/src/interfaces/odbc/options.c
index c8e07700c83..ea11fae76be 100644
--- a/src/interfaces/odbc/options.c
+++ b/src/interfaces/odbc/options.c
@@ -39,6 +39,7 @@ set_statement_option(ConnectionClass *conn,
 	static char *func = "set_statement_option";
 	char		changed = FALSE;
 	ConnInfo   *ci = NULL;
+	UDWORD		setval;
 
 	if (conn)
 		ci = &(conn->connInfo);
@@ -63,22 +64,21 @@ set_statement_option(ConnectionClass *conn,
 			 * positioned update isn't supported so cursor concurrency is
 			 * read-only
 			 */
-			mylog("SetStmtOption(): SQL_CONCURRENCY = %d\n", vParam);
-			if (ci->drivers.lie || vParam == SQL_CONCUR_READ_ONLY || vParam == SQL_CONCUR_ROWVER)
-			{
-				if (conn)
-					conn->stmtOptions.scroll_concurrency = vParam;
-				if (stmt)
-					stmt->options.scroll_concurrency = vParam;
-			}
-			else
-			{
-				if (conn)
-					conn->stmtOptions.scroll_concurrency = SQL_CONCUR_ROWVER;
-				if (stmt)
-					stmt->options.scroll_concurrency = SQL_CONCUR_ROWVER;
+			mylog("SetStmtOption(): SQL_CONCURRENCY = %d ", vParam);
+			setval = SQL_CONCUR_READ_ONLY;
+			if (SQL_CONCUR_READ_ONLY == vParam)
+				;
+			if (ci->drivers.lie)
+				setval = vParam;
+			else if (ci->updatable_cursors)
+				setval = SQL_CONCUR_ROWVER;
+			if (conn)
+				conn->stmtOptions.scroll_concurrency = setval;
+			else if (stmt)
+				stmt->options.scroll_concurrency = setval;
+			if (setval != vParam)
 				changed = TRUE;
-			}
+			mylog("-> %d\n", setval);
 			break;
 
 		case SQL_CURSOR_TYPE:
@@ -87,47 +87,24 @@ set_statement_option(ConnectionClass *conn,
 			 * if declare/fetch, then type can only be forward. otherwise,
 			 * it can only be forward or static.
 			 */
-			mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d\n", vParam);
-
+			mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d ", vParam);
+			setval = SQL_CURSOR_FORWARD_ONLY;
 			if (ci->drivers.lie)
-			{
-				if (conn)
-					conn->stmtOptions.cursor_type = vParam;
-				if (stmt)
-					stmt->options.cursor_type = vParam;
-			}
-			else
-			{
-				if (ci->drivers.use_declarefetch)
-				{
-					if (conn)
-						conn->stmtOptions.cursor_type = SQL_CURSOR_FORWARD_ONLY;
-					if (stmt)
-						stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY;
+				setval = vParam;
+			else if (ci->drivers.use_declarefetch)
+				;
+			else if (SQL_CURSOR_STATIC == vParam)
+				setval = vParam;
+			/** else if (SQL_CURSOR_KEYSET_DRIVEN == vParam && ci->updatable)
+				setval = vParam; **/
 
-					if (vParam != SQL_CURSOR_FORWARD_ONLY)
-						changed = TRUE;
-				}
-				else
-				{
-					if (vParam == SQL_CURSOR_FORWARD_ONLY || vParam == SQL_CURSOR_STATIC)
-					{
-						if (conn)
-							conn->stmtOptions.cursor_type = vParam;		/* valid type */
-						if (stmt)
-							stmt->options.cursor_type = vParam; /* valid type */
-					}
-					else
-					{
-						if (conn)
-							conn->stmtOptions.cursor_type = SQL_CURSOR_STATIC;
-						if (stmt)
-							stmt->options.cursor_type = SQL_CURSOR_STATIC;
-
-						changed = TRUE;
-					}
-				}
-			}
+			if (conn)
+				conn->stmtOptions.cursor_type = setval;
+			else if (stmt)
+				stmt->options.cursor_type = setval;
+			if (setval != vParam)
+				changed = TRUE;
+			mylog("-> %d\n", setval);
 			break;
 
 		case SQL_KEYSET_SIZE:	/* ignored, but saved and returned	*/
diff --git a/src/interfaces/odbc/parse.c b/src/interfaces/odbc/parse.c
index 55bb08bd93b..8a7e6d7415c 100644
--- a/src/interfaces/odbc/parse.c
+++ b/src/interfaces/odbc/parse.c
@@ -297,7 +297,6 @@ parse_statement(StatementClass *stmt)
 				in_distinct = FALSE,
 				in_on = FALSE,
 				in_from = FALSE,
-				from_found = FALSE,
 				in_where = FALSE,
 				in_table = FALSE;
 	char		in_field = FALSE,
@@ -309,7 +308,6 @@ parse_statement(StatementClass *stmt)
 				i,
 				k = 0,
 				n,
-				first_where = 0,
 				blevel = 0;
 	FIELD_INFO **fi;
 	TABLE_INFO **ti;
@@ -318,6 +316,7 @@ parse_statement(StatementClass *stmt)
 	HSTMT		hcol_stmt;
 	StatementClass *col_stmt;
 	RETCODE		result;
+	BOOL		updatable = TRUE;
 
 	mylog("%s: entering...\n", func);
 
@@ -327,6 +326,8 @@ parse_statement(StatementClass *stmt)
 
 	stmt->nfld = 0;
 	stmt->ntab = 0;
+	stmt->from_pos = -1;
+	stmt->where_pos = -1;
 
 #ifdef MULTIBYTE
 	while (pptr = ptr, (ptr = getNextToken(conn->ccsc, pptr, token, sizeof(token), &delim, &quote, &dquote, &numeric)) != NULL)
@@ -343,6 +344,7 @@ parse_statement(StatementClass *stmt)
 			if (!stricmp(token, "distinct"))
 			{
 				in_distinct = TRUE;
+				updatable = FALSE;
 
 				mylog("DISTINCT\n");
 				continue;
@@ -359,11 +361,11 @@ parse_statement(StatementClass *stmt)
 			{
 				in_select = FALSE;
 				in_from = TRUE;
-				if (!from_found &&
+				if (stmt->from_pos < 0 &&
 					(!strnicmp(pptr, "from", 4)))
 				{
 					mylog("First ");
-					from_found = TRUE;
+					stmt->from_pos = pptr - stmt->statement;
 				}
 
 				mylog("FROM\n");
@@ -384,9 +386,13 @@ parse_statement(StatementClass *stmt)
 				in_from = FALSE;
 				in_where = TRUE;
 
-				if (!first_where &&
-					(!stricmp(token, "where")))
-					first_where = ptr - stmt->statement;
+				if (!stricmp(token, "where"))
+				{
+					if (stmt->where_pos < 0)
+						stmt->where_pos = pptr - stmt->statement;
+				}
+				else if (stricmp(token, "order"))
+					updatable = FALSE;
 
 				mylog("WHERE...\n");
 				break;
@@ -733,6 +739,10 @@ parse_statement(StatementClass *stmt)
 	 */
 
 	/* Call SQLColumns for each table and store the result */
+	if (stmt->ntab > 1)
+		updatable = FALSE;
+	else if (stmt->from_pos < 0)
+		updatable = FALSE;
 	for (i = 0; i < stmt->ntab; i++)
 	{
 		/* See if already got it */
@@ -828,9 +838,11 @@ parse_statement(StatementClass *stmt)
 	 */
 	for (i = 0; i < stmt->nfld;)
 	{
+		fi[i]->updatable = updatable;
 		/* Dont worry about functions or quotes */
 		if (fi[i]->func || fi[i]->quote || fi[i]->numeric)
 		{
+			fi[i]->updatable = FALSE;
 			i++;
 			continue;
 		}
@@ -928,6 +940,7 @@ parse_statement(StatementClass *stmt)
 					mylog("about to copy at %d\n", n + i);
 
 					getColInfo(the_ti->col_info, fi[n + i], n);
+					fi[n + i]->updatable = updatable;
 
 					mylog("done copying\n");
 				}
@@ -945,24 +958,29 @@ parse_statement(StatementClass *stmt)
 		else if (fi[i]->ti)
 		{
 			if (!searchColInfo(fi[i]->ti->col_info, fi[i]))
+			{
 				parse = FALSE;
-
+				fi[i]->updatable = FALSE;
+			}
 			i++;
 		}
 
 		/* Don't know the table -- search all tables in "from" list */
 		else
 		{
-			parse = FALSE;
 			for (k = 0; k < stmt->ntab; k++)
 			{
 				if (searchColInfo(ti[k]->col_info, fi[i]))
 				{
 					fi[i]->ti = ti[k];	/* now know the table */
-					parse = TRUE;
 					break;
 				}
 			}
+			if (k >= stmt->ntab)
+			{
+				parse = FALSE;
+				fi[i]->updatable = FALSE;
+			}
 			i++;
 		}
 	}
diff --git a/src/interfaces/odbc/psqlodbc.h b/src/interfaces/odbc/psqlodbc.h
index 8fba1abd30d..958a3b6b102 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.59 2002/03/11 10:25:57 inoue Exp $
+ * $Id: psqlodbc.h,v 1.60 2002/03/14 05:42:03 inoue Exp $
  *
  */
 
@@ -166,6 +166,7 @@ typedef struct TupleListClass_ TupleListClass;
 typedef struct EnvironmentClass_ EnvironmentClass;
 typedef struct TupleNode_ TupleNode;
 typedef struct TupleField_ TupleField;
+typedef struct KeySet_ KeySet;
 
 typedef struct col_info COL_INFO;
 typedef struct lo_arg LO_ARG;
diff --git a/src/interfaces/odbc/psqlodbc_api30.def b/src/interfaces/odbc/psqlodbc_api30.def
index bafd2e6d575..a2031c7aba6 100755
--- a/src/interfaces/odbc/psqlodbc_api30.def
+++ b/src/interfaces/odbc/psqlodbc_api30.def
@@ -5,7 +5,7 @@ SQLAllocEnv @2
 SQLAllocStmt @3
 SQLBindCol @4
 SQLCancel @5
-SQLColAttributes @6
+; SQLColAttributes @6 */
 SQLConnect @7
 SQLDescribeCol @8
 SQLDisconnect @9
diff --git a/src/interfaces/odbc/psqlodbc_api30w.def b/src/interfaces/odbc/psqlodbc_api30w.def
index 71ea830cb43..ef7cdfdf33d 100644
--- a/src/interfaces/odbc/psqlodbc_api30w.def
+++ b/src/interfaces/odbc/psqlodbc_api30w.def
@@ -5,7 +5,7 @@ SQLAllocEnv @2
 SQLAllocStmt @3
 SQLBindCol @4
 SQLCancel @5
-SQLColAttributes @6
+; SQLColAttributes @6
 SQLConnect @7
 SQLDescribeCol @8
 SQLDisconnect @9
diff --git a/src/interfaces/odbc/qresult.c b/src/interfaces/odbc/qresult.c
index 2e462bf5d5b..d4445ec1ef0 100644
--- a/src/interfaces/odbc/qresult.c
+++ b/src/interfaces/odbc/qresult.c
@@ -121,6 +121,8 @@ QR_Constructor()
 
 		rv->cache_size = 0;
 		rv->rowset_size = 1;
+		rv->haskeyset = 0;
+		rv->keyset = NULL;
 	}
 
 	mylog("exit QR_Constructor\n");
@@ -221,6 +223,11 @@ QR_free_memory(QResultClass *self)
 		free(self->backend_tuples);
 		self->backend_tuples = NULL;
 	}
+	if (self->keyset)
+	{
+		free(self->keyset);
+		self->keyset = NULL;
+	}
 
 	self->fcount = 0;
 
@@ -296,6 +303,8 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
 		mylog("MALLOC: tuple_size = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size);
 		self->count_allocated = 0;
 		self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * tuple_size);
+		if (self->haskeyset)
+			self->keyset = (KeySet *) calloc(sizeof(KeySet), tuple_size);
 		if (!self->backend_tuples)
 		{
 			self->status = PGRES_FATAL_ERROR;
@@ -347,7 +356,7 @@ QR_close(QResultClass *self)
 		sprintf(buf, "close %s", self->cursor);
 		mylog("QResult: closing cursor: '%s'\n", buf);
 
-		res = CC_send_query(self->conn, buf, NULL, TRUE);
+		res = CC_send_query(self->conn, buf, NULL, CLEAR_RESULT_ON_ABORT);
 
 		self->inTuples = FALSE;
 		self->currTuple = -1;
@@ -482,6 +491,8 @@ QR_next_tuple(QResultClass *self)
 					QR_set_message(self, "Out of memory while reading tuples.");
 					return FALSE;
 				}
+				if (self->haskeyset)
+					self->keyset = (KeySet *) realloc(self->keyset, sizeof(KeySet) * self->cache_size); 
 				self->count_allocated = self->cache_size;
 			}
 			sprintf(fetch, "fetch %d in %s", fetch_size, self->cursor);
@@ -492,7 +503,7 @@ QR_next_tuple(QResultClass *self)
 			qi.row_size = self->cache_size;
 			qi.result_in = self;
 			qi.cursor = NULL;
-			res = CC_send_query(self->conn, fetch, &qi, TRUE);
+			res = CC_send_query(self->conn, fetch, &qi, CLEAR_RESULT_ON_ABORT);
 			if (res == NULL)
 			{
 				self->status = PGRES_FATAL_ERROR;
@@ -552,6 +563,8 @@ QR_next_tuple(QResultClass *self)
 						QR_set_message(self, "Out of memory while reading tuples.");
 						return FALSE;
 					}
+					if (self->haskeyset)
+						self->keyset = (KeySet *) realloc(self->keyset, sizeof(KeySet) * tuple_size);
 					self->count_allocated = tuple_size;
 				}
 
@@ -626,6 +639,7 @@ QR_read_tuple(QResultClass *self, char binary)
 {
 	Int2		field_lf;
 	TupleField *this_tuplefield;
+	KeySet	*this_keyset = NULL;
 	char		bmp,
 				bitmap[MAX_FIELDS];		/* Max. len of the bitmap */
 	Int2		bitmaplen;		/* len of the bitmap in bytes */
@@ -639,6 +653,11 @@ QR_read_tuple(QResultClass *self, char binary)
 
 	/* set the current row to read the fields into */
 	this_tuplefield = self->backend_tuples + (self->fcount * num_fields);
+	if (self->haskeyset)
+	{
+		this_keyset = self->keyset + self->fcount;
+		this_keyset->status = 0;
+	}
 
 	bitmaplen = (Int2) num_fields / BYTELEN;
 	if ((num_fields % BYTELEN) > 0)
@@ -709,6 +728,15 @@ QR_read_tuple(QResultClass *self, char binary)
 		else
 			bmp <<= 1;
 	}
+	if (this_keyset)
+	{
+		if (this_tuplefield[num_fields - 2].value)
+			sscanf(this_tuplefield[num_fields - 2].value, "(%u,%hu)",
+				&this_keyset->blocknum, &this_keyset->offset);
+		if (this_tuplefield[num_fields - 1].value)
+			sscanf(this_tuplefield[num_fields - 1].value, "%u",
+				&this_keyset->oid);
+	}
 	self->currTuple++;
 	return TRUE;
 }
diff --git a/src/interfaces/odbc/qresult.h b/src/interfaces/odbc/qresult.h
index b304fd5d351..dbb6f46901e 100644
--- a/src/interfaces/odbc/qresult.h
+++ b/src/interfaces/odbc/qresult.h
@@ -72,6 +72,9 @@ struct QResultClass_
 	char		inTuples;		/* is a fetch of rows from the backend in
 								 * progress? */
 	char		aborted;		/* was aborted? */
+	char		haskeyset;		/* this result contains keyset ? */
+	KeySet		*keyset;	
+	
 };
 
 #define QR_get_fields(self)					(self->fields)
@@ -102,6 +105,7 @@ struct QResultClass_
 #define QR_set_status(self, condition)		( self->status = condition )
 #define QR_set_message(self, message_)		( self->message = message_)
 #define QR_set_aborted(self, aborted_)		( self->aborted = aborted_)
+#define QR_set_haskeyset(self)		(self->haskeyset = TRUE)
 
 #define QR_get_message(self)				(self->message)
 #define QR_get_command(self)				(self->command)
diff --git a/src/interfaces/odbc/results.c b/src/interfaces/odbc/results.c
index 13f223b7205..af7c8b30f8b 100644
--- a/src/interfaces/odbc/results.c
+++ b/src/interfaces/odbc/results.c
@@ -160,8 +160,7 @@ PGAPI_NumResultCols(
 
 		*pccol = QR_NumResultCols(result);
 		/* updatable cursors */
-		if (ci->updatable_cursors &&
-			stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
+		if (result->keyset)
 			*pccol -= 2;
 	}
 
@@ -433,7 +432,7 @@ PGAPI_ColAttributes(
 	 */
 
 #if (ODBCVER >= 0x0300)
-	if (0 == icol) /* bookmark column */
+	if (0 == icol && SQL_DESC_COUNT != fDescType) /* bookmark column */
 	{
 		switch (fDescType)
 		{
@@ -473,7 +472,11 @@ PGAPI_ColAttributes(
 		 * Column Count is a special case.	The Column number is ignored
 		 * in this case.
 		 */
+#if (ODBCVER >= 0x0300)
+		if (fDescType == SQL_DESC_COUNT)
+#else
 		if (fDescType == SQL_COLUMN_COUNT)
+#endif /* ODBCVER */
 		{
 			if (pfDesc)
 				*pfDesc = cols;
@@ -539,6 +542,8 @@ PGAPI_ColAttributes(
 		}
 
 		field_type = QR_get_field_type(SC_get_Curres(stmt), col_idx);
+		if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[col_idx])
+			fi = stmt->fi[col_idx];
 	}
 
 	mylog("colAttr: col %d field_type = %d\n", col_idx, field_type);
@@ -549,6 +554,7 @@ PGAPI_ColAttributes(
 			value = pgtype_auto_increment(stmt, field_type);
 			if (value == -1)	/* non-numeric becomes FALSE (ODBC Doc) */
 				value = FALSE;
+inolog("AUTO_INCREMENT=%d\n", value);
 
 			break;
 
@@ -581,9 +587,8 @@ PGAPI_ColAttributes(
 
 #if (ODBCVER >= 0x0300)
 		case SQL_DESC_NAME:
-#else
-		case SQL_COLUMN_NAME:
 #endif /* ODBCVER */
+		case SQL_COLUMN_NAME:
 			p = fi ? (fi->alias[0] ? fi->alias : fi->name) : QR_get_fieldname(SC_get_Curres(stmt), col_idx);
 
 			mylog("PGAPI_ColAttr: COLUMN_NAME = '%s'\n", p);
@@ -597,14 +602,15 @@ PGAPI_ColAttributes(
 
 		case SQL_COLUMN_MONEY: /* == SQL_DESC_FIXED_PREC_SCALE */
 			value = pgtype_money(stmt, field_type);
+inolog("COLUMN_MONEY=%d\n", value);
 			break;
 
 #if (ODBCVER >= 0x0300)
 		case SQL_DESC_NULLABLE:
-#else
-		case SQL_COLUMN_NULLABLE:
 #endif /* ODBCVER */
+		case SQL_COLUMN_NULLABLE:
 			value = fi ? fi->nullable : pgtype_nullable(stmt, field_type);
+inolog("COLUMN_NULLABLE=%d\n", value);
 			break;
 
 		case SQL_COLUMN_OWNER_NAME: /* == SQL_DESC_SCHEMA_NAME */
@@ -623,6 +629,7 @@ PGAPI_ColAttributes(
 
 		case SQL_COLUMN_SCALE:
 			value = pgtype_scale(stmt, field_type, col_idx);
+inolog("COLUMN_SCALE=%d\n", value);
 			break;
 
 		case SQL_COLUMN_SEARCHABLE: /* SQL_DESC_SEARCHABLE */
@@ -637,6 +644,7 @@ PGAPI_ColAttributes(
 
 		case SQL_COLUMN_TYPE: /* == SQL_DESC_CONCISE_TYPE */
 			value = pgtype_to_sqltype(stmt, field_type);
+inolog("COLUMN_TYPE=%d\n", value);
 			break;
 
 		case SQL_COLUMN_TYPE_NAME: /* == SQL_DESC_TYPE_NAME */
@@ -658,7 +666,7 @@ PGAPI_ColAttributes(
 			 * if (field_type == PG_TYPE_OID) pfDesc = SQL_ATTR_READONLY;
 			 * else
 			 */
-			value = SQL_ATTR_WRITE;
+			value = fi ? (fi->updatable ? SQL_ATTR_WRITE : SQL_ATTR_READONLY) : SQL_ATTR_READWRITE_UNKNOWN;
 
 			mylog("PGAPI_ColAttr: UPDATEABLE = %d\n", value);
 			break;
@@ -1292,9 +1300,17 @@ PGAPI_ExtendedFetch(
 			if (result == SQL_ERROR)
 				*(rgfRowStatus + i) = SQL_ROW_ERROR;
 #ifdef	DRIVER_CURSOR_IMPLEMENT
-			/* this should be refined */
-			else if (result > 10 && result < 20)
-				*(rgfRowStatus + i) = result - 10;
+			else if (res->keyset)
+			{
+				UWORD	pstatus = res->keyset[stmt->currTuple].status & KEYSET_INFO_PUBLIC;
+				if (pstatus != 0)
+				{
+					rgfRowStatus[i] = pstatus;
+					res->keyset[stmt->currTuple].status &= (~KEYSET_INFO_PUBLIC);
+				}
+				else
+					rgfRowStatus[i] = SQL_ROW_SUCCESS;
+			}
 #endif   /* DRIVER_CURSOR_IMPLEMENT */
 			else
 				*(rgfRowStatus + i) = SQL_ROW_SUCCESS;
@@ -1347,7 +1363,7 @@ PGAPI_MoreResults(
 
 	mylog("%s: entering...\n", func);
 	if (stmt && (res = SC_get_Curres(stmt)))
-		SC_get_Curres(stmt) = res->next;
+		SC_set_Curres(stmt, res->next);
 	if (SC_get_Curres(stmt))
 		return SQL_SUCCESS; 
 	return SQL_NO_DATA_FOUND;
@@ -1358,28 +1374,61 @@ PGAPI_MoreResults(
 /*
  *	Stuff for updatable cursors.
  */
+static const char *getOidValue(const QResultClass *res, int index)
+{
+	return QR_get_value_backend_row(res, index, QR_NumResultCols(res) - 1);
+}
+static UInt4	getOid(const QResultClass *res, int index)
+{
+	return res->keyset[index].oid;
+}
+static const char *getTidValue(const QResultClass *res, int index)
+{
+	return QR_get_value_backend_row(res, index, QR_NumResultCols(res) - 2);
+}
+static void getTid(const QResultClass *res, int index, UInt4 *blocknum, UInt2 *offset)
+{
+	*blocknum = res->keyset[index].blocknum;
+	*offset = res->keyset[index].offset;
+}
+static void KeySetSet(const QResultClass *res, int index)
+{
+	int		num_fields = res->num_fields;
+	TupleField	*tuple = res->backend_tuples + num_fields * index;
+	KeySet		*keyset = res->keyset + index;
+
+	sscanf(tuple[num_fields - 2].value, "(%u,%hu)",
+			&keyset->blocknum, &keyset->offset);
+	sscanf(tuple[num_fields - 1].value, "%u", &keyset->oid);
+}
+
 static QResultClass *
 positioned_load(StatementClass *stmt, BOOL latest, int res_cols, UInt4 oid, const char *tidval)
 {
-	int			i;
 	QResultClass *qres;
-	char		selstr[4096];
+	char	*selstr;
+	UInt4	len;
 
-	sprintf(selstr, "select");
-	for (i = 0; i < res_cols; i++)
-		sprintf(selstr, "%s \"%s\",", selstr, stmt->fi[i]->name);
-	sprintf(selstr, "%s CTID, OID from \"%s\" where", selstr, stmt->ti[0]->name);
+	len = strlen(stmt->load_statement);
 	if (tidval)
 	{
+		len += 100;
+		selstr = malloc(len);
 		if (latest)
-			sprintf(selstr, "%s ctid = currtid2('%s', '%s') and",
-					selstr, stmt->ti[0]->name, tidval);
-		else
-			sprintf(selstr, "%s ctid = '%s' and", selstr, tidval);
+			sprintf(selstr, "%s where ctid = currtid2('%s', '%s') and oid  = %u", stmt->load_statement, stmt->ti[0]->name, tidval, oid);
+		else 
+			sprintf(selstr, "%s where ctid = '%s' and oid = %u", stmt->load_statement, tidval, oid); 
 	}
-	sprintf(selstr, "%s oid = %u", selstr, oid),
-		mylog("selstr=%s\n", selstr);
-	qres = CC_send_query(SC_get_conn(stmt), selstr, NULL, TRUE);
+	else
+	{
+		len += 20;
+		selstr = malloc(len);
+		sprintf(selstr, "%s where oid = %u", stmt->load_statement, oid);
+	} 
+
+	mylog("selstr=%s\n", selstr);
+	qres = CC_send_query(SC_get_conn(stmt), selstr, NULL, CLEAR_RESULT_ON_ABORT);
+free(selstr);
 	return qres;
 }
 
@@ -1388,14 +1437,12 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count)
 {
 	int			i,
 				res_cols;
-	UWORD		rcnt,
-				global_ridx;
-	UInt4		oid;
+	UWORD		rcnt, global_ridx, offset;
+	UInt4		oid, blocknum;
 	QResultClass *res,
 			   *qres;
 	RETCODE		ret = SQL_ERROR;
-	char	   *tidval,
-			   *oidval;
+	char		tidval[32];
 
 	mylog("positioned load fi=%x ti=%x\n", stmt->fi, stmt->ti);
 	rcnt = 0;
@@ -1412,15 +1459,18 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count)
 	}
 	global_ridx = irow + stmt->rowset_start;
 	res_cols = QR_NumResultCols(res);
-	if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
+	if (!(oid = getOid(res, global_ridx)))
+		return SQL_SUCCESS_WITH_INFO;
+	getTid(res, global_ridx, &blocknum, &offset);
+	sprintf(tidval, "(%u, %u)", blocknum, offset);
+	/*if (!(oidval = getOidValue(res, global_ridx)))
 		return SQL_SUCCESS_WITH_INFO;
 	sscanf(oidval, "%u", &oid);
-	tidval = QR_get_value_backend_row(res, global_ridx, res_cols - 2);
+	tidval = getTidValue(res, global_ridx);*/
 	res_cols -= 2;
 	if (qres = positioned_load(stmt, TRUE, res_cols, oid, tidval), qres)
 	{
-		TupleField *tupleo,
-				   *tuplen;
+		TupleField *tupleo, *tuplen;
 
 		rcnt = QR_get_num_tuples(qres);
 		tupleo = res->backend_tuples + res->num_fields * global_ridx;
@@ -1437,6 +1487,13 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count)
 				tupleo[i].value = tuplen[i].value;
 				tuplen[i].value = NULL;
 			}
+			if (res->keyset)
+			{
+				if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type &&
+					strcmp(tupleo[res->num_fields - 2].value, tidval))
+					res->keyset[global_ridx].status |= SQL_ROW_UPDATED;
+				KeySetSet(res, global_ridx);
+			}
 			ret = SQL_SUCCESS;
 		}
 		else
@@ -1450,6 +1507,7 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count)
 					free(tupleo[res_cols + 1].value);
 				tupleo[res_cols + 1].value = NULL;
 				tupleo[res_cols + 1].len = 0;
+				res->keyset[global_ridx].status |= SQL_ROW_DELETED;
 			}
 		}
 		QR_Destructor(qres);
@@ -1481,8 +1539,7 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval)
 	}
 	if (qres = positioned_load(stmt, TRUE, QR_NumResultCols(res) - 2, oid, tidval), qres)
 	{
-		TupleField *tupleo,
-				   *tuplen;
+		TupleField *tupleo, *tuplen;
 		int			count = QR_get_num_tuples(qres);
 
 		QR_set_position(qres, 0);
@@ -1507,6 +1564,8 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval)
 					QR_Destructor(qres);
 					return SQL_ERROR;
 				}
+				if (res->haskeyset)
+					res->keyset = (KeySet *) realloc(res->keyset, sizeof(KeySet) * tuple_size);	
 				res->count_allocated = tuple_size;
 			}
 			tupleo = res->backend_tuples + res->num_fields * res->fcount;
@@ -1517,6 +1576,7 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval)
 				tupleo[i].value = tuplen[i].value;
 				tuplen[i].value = NULL;
 			}
+			KeySetSet(res, res->fcount);
 			res->fcount++;
 			ret = SQL_SUCCESS;
 		}
@@ -1533,12 +1593,12 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval)
 }
 
 static RETCODE SQL_API
-irow_update(RETCODE ret, StatementClass *stmt, UWORD irow)
+irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt, UWORD irow)
 {
 	if (ret != SQL_ERROR)
 	{
 		int			updcnt;
-		const char *cmdstr = QR_get_command(SC_get_Curres(stmt));
+		const char *cmdstr = QR_get_command(SC_get_Curres(ustmt));
 
 		if (cmdstr &&
 			sscanf(cmdstr, "UPDATE %d", &updcnt) == 1)
@@ -1580,9 +1640,8 @@ SC_pos_update(StatementClass *stmt,
 	BindInfoClass *bindings = stmt->bindings;
 	char		updstr[4096];
 	RETCODE		ret;
-	char	   *tidval,
-			   *oidval;
-	UInt4	offset;
+	UInt4	oid, offset, blocknum;
+	UInt2	pgoffset;
 	Int4	*used;
 
 	mylog("POS UPDATE %d+%d fi=%x ti=%x\n", irow, SC_get_Curres(stmt)->base, stmt->fi, stmt->ti);
@@ -1597,12 +1656,14 @@ SC_pos_update(StatementClass *stmt,
 	}
 	global_ridx = irow + stmt->rowset_start;
 	res_cols = QR_NumResultCols(res);
-	if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
+	/*if (!(oidval = getOidValue(res, global_ridx)))*/
+	if (!(oid = getOid(res, global_ridx)))
 	{
 		stmt->errormsg = "The row is already deleted";
 		return SQL_ERROR;
 	}
-	tidval = QR_get_value_backend_row(res, global_ridx, res_cols - 2);
+	/*tidval = getTidValue(res, global_ridx);*/
+	getTid(res, global_ridx, &blocknum, &pgoffset);
 
 	sprintf(updstr, "update \"%s\" set", stmt->ti[0]->name);
 	num_cols = stmt->nfld;
@@ -1635,8 +1696,10 @@ SC_pos_update(StatementClass *stmt,
 		int			res_cols = QR_NumResultCols(res);
 		StatementClass *qstmt;
 
-		sprintf(updstr, "%s where ctid = '%s' and oid = %s", updstr,
-				tidval, oidval);
+		/*sprintf(updstr, "%s where ctid = '%s' and oid = %s", updstr,
+				tidval, oidval);*/
+		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)
 			return SQL_ERROR;
@@ -1676,11 +1739,13 @@ SC_pos_update(StatementClass *stmt,
 			stmt->errormsg = "SetPos with data_at_exec not yet supported";
 			ret = SQL_ERROR;
 		}
-		ret = irow_update(ret, qstmt, irow);
+		ret = irow_update(ret, stmt, qstmt, irow);
 		PGAPI_FreeStmt(hstmt, SQL_DROP);
 	}
 	else
 		ret = SQL_SUCCESS_WITH_INFO;
+	if (SQL_SUCCESS == ret && res->keyset)
+		res->keyset[global_ridx].status |= (SQL_ROW_UPDATED | DRV_SELF_UPDATED);
 #if (ODBCVER >= 0x0300)
 	if (stmt->options.rowStatusArray)
 	{
@@ -1689,9 +1754,8 @@ SC_pos_update(StatementClass *stmt,
 			case SQL_SUCCESS:
 				stmt->options.rowStatusArray[irow] = SQL_ROW_UPDATED;
 				break;
-			case SQL_SUCCESS_WITH_INFO:
-				stmt->options.rowStatusArray[irow] = SQL_ROW_SUCCESS_WITH_INFO;
-				break;
+			default:
+				stmt->options.rowStatusArray[irow] = ret;
 		}
 	}
 #endif /* ODBCVER */
@@ -1703,13 +1767,13 @@ SC_pos_delete(StatementClass *stmt,
 			  UWORD irow)
 {
 	int			res_cols;
-	UWORD		global_ridx;
-	QResultClass *res,
-			   *qres;
+	UWORD		global_ridx, offset;
+	QResultClass *res, *qres;
 	BindInfoClass *bindings = stmt->bindings;
 	char		dltstr[4096];
 	RETCODE		ret;
-	char	   *oidval;
+	/*const char	   *oidval;*/
+	UInt4		oid, blocknum;
 
 	mylog("POS DELETE fi=%x ti=%x\n", stmt->fi, stmt->ti);
 	if (!(res = SC_get_Curres(stmt)))
@@ -1723,18 +1787,20 @@ SC_pos_delete(StatementClass *stmt,
 	}
 	res_cols = QR_NumResultCols(res);
 	global_ridx = irow + stmt->rowset_start;
-	if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
+	/* if (!(oidval = getOidValue(res, global_ridx)))*/
+	if (!(oid = getOid(res, global_ridx)))
 	{
 		stmt->errormsg = "The row is already deleted";
 		return SQL_ERROR;
 	}
-	sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s",
-			stmt->ti[0]->name,
-	   QR_get_value_backend_row(SC_get_Curres(stmt), global_ridx, res_cols - 2),
-			oidval);
+	getTid(res, global_ridx, &blocknum, &offset);
+	/*sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s",*/
+	sprintf(dltstr, "delete from \"%s\" where ctid = '(%u, %u)' and oid = %u",
+			stmt->ti[0]->name, blocknum, offset, oid);
 
 	mylog("dltstr=%s\n", dltstr);
-	qres = CC_send_query(SC_get_conn(stmt), dltstr, NULL, TRUE);
+	qres = CC_send_query(SC_get_conn(stmt), dltstr, NULL, CLEAR_RESULT_ON_ABORT);
+	ret = SQL_SUCCESS;
 	if (qres && QR_command_successful(qres))
 	{
 		int			dltcnt;
@@ -1769,6 +1835,8 @@ SC_pos_delete(StatementClass *stmt,
 	}
 	if (qres)
 		QR_Destructor(qres);
+	if (SQL_SUCCESS == ret && res->keyset)
+		res->keyset[global_ridx].status |= (SQL_ROW_DELETED | DRV_SELF_DELETED);
 #if (ODBCVER >= 0x0300)
 	if (stmt->options.rowStatusArray)
 	{
@@ -1777,9 +1845,8 @@ SC_pos_delete(StatementClass *stmt,
 			case SQL_SUCCESS:
 				stmt->options.rowStatusArray[irow] = SQL_ROW_DELETED;
 				break;
-			case SQL_SUCCESS_WITH_INFO:
-				stmt->options.rowStatusArray[irow] = SQL_ROW_SUCCESS_WITH_INFO;
-				break;
+			default:
+				stmt->options.rowStatusArray[irow] = ret;
 		}
 	}
 #endif /* ODBCVER */
@@ -1787,13 +1854,13 @@ SC_pos_delete(StatementClass *stmt,
 }
 
 static RETCODE SQL_API
-irow_insert(RETCODE ret, StatementClass *stmt, int addpos)
+irow_insert(RETCODE ret, StatementClass *stmt, StatementClass *istmt, int addpos)
 {
 	if (ret != SQL_ERROR)
 	{
 		int			addcnt;
 		UInt4		oid;
-		const char *cmdstr = QR_get_command(SC_get_Curres(stmt));
+		const char *cmdstr = QR_get_command(SC_get_Curres(istmt));
 
 		if (cmdstr &&
 			sscanf(cmdstr, "INSERT %u %d", &oid, &addcnt) == 2 &&
@@ -1802,12 +1869,14 @@ irow_insert(RETCODE ret, StatementClass *stmt, int addpos)
 			SC_pos_newload(stmt, oid, NULL);
 			if (stmt->bookmark.buffer)
 			{
-				char		buf[32];
+				char	buf[32];
+				UInt4	offset = stmt->options.row_offset_ptr ? *stmt->options.row_offset_ptr : 0;
 
-				sprintf(buf, "%ld", addpos);
+				sprintf(buf, "%ld", addpos + 1);
 				copy_and_convert_field(stmt, 0, buf,
-				 SQL_C_ULONG, stmt->bookmark.buffer,
-				 0, stmt->bookmark.used);
+                         		SQL_C_ULONG, stmt->bookmark.buffer + offset,
+					0, stmt->bookmark.used ? stmt->bookmark.used
+					+ (offset >> 2) : NULL);
 			}
 		}
 		else
@@ -1882,6 +1951,7 @@ SC_pos_add(StatementClass *stmt,
 	}
 	if (add_cols > 0)
 	{
+		int	brow_save;
 
 		sprintf(addstr, "%s) values (", addstr);
 		for (i = 0; i < add_cols; i++)
@@ -1907,11 +1977,16 @@ SC_pos_add(StatementClass *stmt,
 			stmt->errormsg = "SetPos with data_at_exec not yet supported";
 			ret = SQL_ERROR;
 		}
-		ret = irow_insert(ret, qstmt, res->fcount);
+		brow_save = stmt->bind_row; 
+		stmt->bind_row = irow; 
+		ret = irow_insert(ret, stmt, qstmt, res->fcount);
+		stmt->bind_row = brow_save; 
 	}
 	else
 		ret = SQL_SUCCESS_WITH_INFO;
 	PGAPI_FreeStmt(hstmt, SQL_DROP);
+	if (SQL_SUCCESS == ret && res->keyset)
+		res->keyset[res->fcount - 1].status |= DRV_SELF_ADDED;
 #if (ODBCVER >= 0x0300)
 	if (stmt->options.rowStatusArray)
 	{
@@ -1920,9 +1995,8 @@ SC_pos_add(StatementClass *stmt,
 			case SQL_SUCCESS:
 				stmt->options.rowStatusArray[irow] = SQL_ROW_ADDED;
 				break;
-			case SQL_SUCCESS_WITH_INFO:
-				stmt->options.rowStatusArray[irow] = SQL_ROW_SUCCESS_WITH_INFO;
-				break;
+			default:
+				stmt->options.rowStatusArray[irow] = ret;
 		}
 	}
 #endif /* ODBCVER */
@@ -1947,6 +2021,7 @@ PGAPI_SetPos(
 			 UWORD fLock)
 {
 	static char *func = "PGAPI_SetPos";
+	RETCODE	ret;
 	StatementClass *stmt = (StatementClass *) hstmt;
 	QResultClass *res;
 	int			num_cols,
@@ -1960,7 +2035,7 @@ PGAPI_SetPos(
 	}
 
 #ifdef	DRIVER_CURSOR_IMPLEMENT
-	mylog("SetPos fOption=%d irow=%d lock=%d currt=%d\n", fOption, irow, fLock, stmt->currTuple);
+	mylog("%s fOption=%d irow=%d lock=%d currt=%d\n", func, fOption, irow, fLock, stmt->currTuple);
 	if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
 		;
 	else
@@ -1982,12 +2057,40 @@ PGAPI_SetPos(
 	}
 	num_cols = QR_NumResultCols(res);
 
-	if (irow == 0)
+	if (irow == 0) /* bulk operation */
 	{
-		stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
-		stmt->errormsg = "Driver does not support Bulk operations.";
-		SC_log_error(func, "", stmt);
-		return SQL_ERROR;
+		int	processed;
+
+		if (SQL_POSITION == fOption)
+		{
+			stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
+			stmt->errormsg = "Bulk Fresh operations not allowed.";
+			SC_log_error(func, "", stmt);
+			return SQL_ERROR;
+		}
+		ret = SQL_SUCCESS;
+		for (i = 0, processed = 0; i < stmt->options.rowset_size; i++)
+		{
+#if (ODBCVER >= 0x0300)
+			if (!stmt->options.row_operation_ptr || stmt->options.row_operation_ptr[i] == SQL_ROW_PROCEED)
+			{
+#endif /* ODBCVER */
+				if (ret = PGAPI_SetPos(hstmt, (UWORD) (i + 1), fOption, fLock), SQL_ERROR == ret)
+					break;
+				processed++;
+#if (ODBCVER >= 0x0300)
+			}
+#endif /* ODBCVER */
+		}
+		if (processed > 0 && SQL_ERROR == ret)
+		{
+			processed++;
+			ret = SQL_SUCCESS_WITH_INFO;
+			stmt->errornumber = STMT_ERROR_IN_ROW;
+		}
+		if (stmt->options.rowsFetched)
+			*stmt->options.rowsFetched = processed;
+		return ret; 
 	}
 
 	if (irow > stmt->last_fetch_count)
diff --git a/src/interfaces/odbc/statement.c b/src/interfaces/odbc/statement.c
index 4bb884dbf8f..b33ccc2cc95 100644
--- a/src/interfaces/odbc/statement.c
+++ b/src/interfaces/odbc/statement.c
@@ -242,6 +242,7 @@ SC_Constructor(void)
 
 		rv->statement = NULL;
 		rv->stmt_with_params = NULL;
+		rv->load_statement = NULL;
 		rv->stmt_size_limit = -1;
 		rv->statement_type = STMT_TYPE_UNKNOWN;
 
@@ -318,6 +319,8 @@ SC_Destructor(StatementClass *self)
 		free(self->stmt_with_params);
 		self->stmt_with_params = NULL;
 	}
+	if (self->load_statement)
+		free(self->load_statement);
 
 	SC_free_params(self, STMT_FREE_PARAMS_ALL);
 
@@ -548,6 +551,12 @@ SC_recycle_statement(StatementClass *self)
 	 * SQLParamData/SQLPutData is called.
 	 */
 	SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);
+	if (self->stmt_with_params)
+		free(self->stmt_with_params);
+	self->stmt_with_params = NULL;
+	if (self->load_statement)
+		free(self->load_statement);
+	self->load_statement = NULL;
 
 	return TRUE;
 }
@@ -635,12 +644,16 @@ SC_create_errormsg(StatementClass *self)
 	QResultClass *res = SC_get_Curres(self);
 	ConnectionClass *conn = self->hdbc;
 	int			pos;
+	BOOL			detailmsg = FALSE;
 	static char msg[4096];
 
 	msg[0] = '\0';
 
 	if (res && res->message)
+	{
 		strcpy(msg, res->message);
+		detailmsg = TRUE;
+	}
 	else if (self->errormsg)
 		strcpy(msg, self->errormsg);
 
@@ -660,10 +673,10 @@ SC_create_errormsg(StatementClass *self)
 	{
 		SocketClass *sock = conn->sock;
 
-		if (conn->errormsg && conn->errormsg[0] != '\0')
+		if (!detailmsg && conn->errormsg && conn->errormsg[0] != '\0')
 		{
 			pos = strlen(msg);
-			/*sprintf(&msg[pos], ";\n%s", conn->errormsg);*/
+			sprintf(&msg[pos], ";\n%s", conn->errormsg);
 		}
 
 		if (sock && sock->errormsg && sock->errormsg[0] != '\0')
@@ -722,9 +735,6 @@ SC_fetch(StatementClass *self)
 	int			retval,
 				result;
 
-#ifdef	DRIVER_CURSOR_IMPLEMENT
-	int			updret;
-#endif   /* DRIVER_CURSOR_IMPLEMENT */
 	Int2		num_cols,
 				lf;
 	Oid			type;
@@ -799,20 +809,13 @@ SC_fetch(StatementClass *self)
 	}
 
 #ifdef	DRIVER_CURSOR_IMPLEMENT
-	updret = 0;
 	if (self->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
 	{
-		if (!QR_get_value_backend_row(res, self->currTuple, num_cols - 1))
-			updret = SQL_ROW_DELETED;
 		num_cols -= 2;
 	}
 #endif   /* DRIVER_CURSOR_IMPLEMENT */
 	if (self->options.retrieve_data == SQL_RD_OFF)		/* data isn't required */
-#ifdef	DRIVER_CURSOR_IMPLEMENT
-		return updret ? updret + 10 : SQL_SUCCESS;
-#else
 		return SQL_SUCCESS;
-#endif   /* DRIVER_CURSOR_IMPLEMENT */
 	for (lf = 0; lf < num_cols; lf++)
 	{
 		mylog("fetch: cols=%d, lf=%d, self = %u, self->bindings = %u, buffer[] = %u\n", num_cols, lf, self, self->bindings, self->bindings[lf].buffer);
@@ -893,10 +896,6 @@ SC_fetch(StatementClass *self)
 		}
 	}
 
-#ifdef	DRIVER_CURSOR_IMPLEMENT
-	if (updret)
-		result = updret + 10;
-#endif   /* DRIVER_CURSOR_IMPLEMENT */
 	return result;
 }
 
@@ -955,11 +954,12 @@ SC_execute(StatementClass *self)
 	if (self->statement_type == STMT_TYPE_SELECT)
 	{
 		char		fetch[128];
+		UDWORD	qflag = (SQL_CONCUR_ROWVER == self->options.scroll_concurrency ? CREATE_KEYSET : 0); 
 
 		mylog("       Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name);
 
 		/* send the declare/select */
-		res = CC_send_query(conn, self->stmt_with_params, NULL, FALSE);
+		res = CC_send_query(conn, self->stmt_with_params, NULL, qflag);
 		if (SC_is_fetchcursor(self) && res != NULL &&
 			QR_command_successful(res))
 		{
@@ -982,7 +982,7 @@ SC_execute(StatementClass *self)
 			 */
 			sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name);
 
-			res = CC_send_query(conn, fetch, &qi, FALSE);
+			res = CC_send_query(conn, fetch, &qi, qflag);
 		}
 		mylog("     done sending the query:\n");
 	}
@@ -990,7 +990,7 @@ SC_execute(StatementClass *self)
 	{
 		/* not a SELECT statement so don't use a cursor */
 		mylog("      it's NOT a select statement: stmt=%u\n", self);
-		res = CC_send_query(conn, self->stmt_with_params, NULL, FALSE);
+		res = CC_send_query(conn, self->stmt_with_params, NULL, 0);
 
 		/*
 		 * We shouldn't send COMMIT. Postgres backend does the autocommit
diff --git a/src/interfaces/odbc/statement.h b/src/interfaces/odbc/statement.h
index cf104aa7d13..74583fbb2da 100644
--- a/src/interfaces/odbc/statement.h
+++ b/src/interfaces/odbc/statement.h
@@ -76,6 +76,7 @@ typedef enum
 #define STMT_BAD_ERROR							27
 #define STMT_INVALID_OPTION_IDENTIFIER					28
 #define STMT_RETURN_NULL_WITHOUT_INDICATOR				29
+#define STMT_ERROR_IN_ROW						30
 
 /* statement types */
 enum
@@ -135,6 +136,7 @@ typedef struct
 	char		quote;
 	char		dquote;
 	char		numeric;
+	char		updatable;
 	char		dot[MAX_TABLE_LEN + 1];
 	char		name[MAX_COLUMN_LEN + 1];
 	char		alias[MAX_COLUMN_LEN + 1];
@@ -219,11 +221,15 @@ struct StatementClass_
 	char		miscinfo;
 	SWORD		errorpos;
 	SWORD		error_recsize;
+	char		*load_statement; /* to (re)load updatable individual rows */
+	Int4		from_pos;	
+	Int4		where_pos;
 };
 
 #define SC_get_conn(a)	  (a->hdbc)
 #define SC_set_Result(a, b)  (a->result = a->curres = b)
 #define SC_get_Result(a)  (a->result)
+#define SC_set_Curres(a, b)  (a->curres = b)
 #define SC_get_Curres(a)  (a->curres)
 
 /*	options for SC_free_params() */
diff --git a/src/interfaces/odbc/tuple.h b/src/interfaces/odbc/tuple.h
index fdc1a5f9eaa..388f9fa0210 100644
--- a/src/interfaces/odbc/tuple.h
+++ b/src/interfaces/odbc/tuple.h
@@ -30,6 +30,19 @@ struct TupleNode_
 	TupleField	tuple[1];
 };
 
+/*	keyset(TID + OID) info */
+struct KeySet_
+{
+	UWORD	status;
+	UWORD	offset;
+	UDWORD	blocknum;
+	UDWORD	oid;
+};
+#define	KEYSET_INFO_PUBLIC	0x0f
+#define	DRV_SELF_ADDED		(1L << 4)
+#define	DRV_SELF_DELETED	(1L << 5)
+#define	DRV_SELF_UPDATED	(1L << 6)
+
 /*	These macros are wrappers for the corresponding set_tuplefield functions
 	but these handle automatic NULL determination and call set_tuplefield_null()
 	if appropriate for the datatype (used by SQLGetTypeInfo).
-- 
GitLab