From f8da3990b5437e47b7296a6770a3efd215b326d0 Mon Sep 17 00:00:00 2001
From: Hiroshi Inoue <inoue@tpf.co.jp>
Date: Tue, 2 Apr 2002 10:50:50 +0000
Subject: [PATCH] [HACKERS] Proposed patch for ODBC driver w/ C-a-n-c-e-l    
 From: Bradley McLean <brad@bradm.net>

Patch against 7,2 submitted for comment.

This seems to work just fine; Now, when our users submit a 2 hour
query with four million row sorts by accident, then cancel it 30 seconds
later, it doesn't bog down the server ...
---
 src/interfaces/odbc/connection.c |  71 +++++-
 src/interfaces/odbc/connection.h |  19 ++
 src/interfaces/odbc/descriptor.h |   3 +-
 src/interfaces/odbc/execute.c    |   9 +-
 src/interfaces/odbc/info.c       | 359 +++++++++++++++++++++++++------
 src/interfaces/odbc/info30.c     |  12 +-
 src/interfaces/odbc/misc.c       |  12 ++
 src/interfaces/odbc/misc.h       |   3 +
 src/interfaces/odbc/parse.c      |  56 +++--
 src/interfaces/odbc/psqlodbc.h   |   2 +-
 src/interfaces/odbc/socket.c     |  15 +-
 src/interfaces/odbc/socket.h     |   1 +
 12 files changed, 455 insertions(+), 107 deletions(-)

diff --git a/src/interfaces/odbc/connection.c b/src/interfaces/odbc/connection.c
index d6b622a16d7..7cee671e2b6 100644
--- a/src/interfaces/odbc/connection.c
+++ b/src/interfaces/odbc/connection.c
@@ -19,6 +19,9 @@
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
+#ifndef	WIN32
+#include <errno.h>
+#endif /* WIN32 */
 
 #include "environ.h"
 #include "socket.h"
@@ -289,6 +292,7 @@ CC_Constructor()
 		rv->ms_jet = 0;
 		rv->unicode = 0;
 		rv->result_uncommitted = 0;
+		rv->schema_support = 0;
 #ifdef	MULTIBYTE
 		rv->client_encoding = NULL;
 		rv->server_encoding = NULL;
@@ -882,8 +886,8 @@ another_version_retry:
 					}
 					break;
 				case 'K':		/* Secret key (6.4 protocol) */
-					(void) SOCK_get_int(sock, 4);		/* pid */
-					(void) SOCK_get_int(sock, 4);		/* key */
+					self->be_pid = SOCK_get_int(sock, 4);		/* pid */
+					self->be_key = SOCK_get_int(sock, 4);		/* key */
 
 					break;
 				case 'Z':		/* Backend is ready for new query (6.4) */
@@ -1960,6 +1964,8 @@ CC_lookup_pg_version(ConnectionClass *self)
 		self->pg_version_minor = minor;
 	}
 	self->pg_version_number = (float) atof(szVersion);
+	if (PG_VERSION_GE(self, 7.3))
+		self->schema_support = 1;
 
 	mylog("Got the PostgreSQL version string: '%s'\n", self->pg_version);
 	mylog("Extracted PostgreSQL version number: '%1.1f'\n", self->pg_version_number);
@@ -2019,3 +2025,64 @@ CC_get_max_query_len(const ConnectionClass *conn)
 		value = BLCKSZ;
 	return value;
 }
+
+int
+CC_send_cancel_request(const ConnectionClass *conn)
+{
+#ifdef WIN32
+	int			save_errno = (WSAGetLastError());
+#else
+	int			save_errno = errno;
+#endif
+	int			tmpsock = -1;
+	struct
+	{
+		uint32		packetlen;
+		CancelRequestPacket cp;
+	}			crp;
+
+	/* Check we have an open connection */
+	if (!conn)
+		return FALSE;
+
+	if (conn->sock == NULL )
+	{
+		return FALSE;
+	}
+
+	/*
+	 * We need to open a temporary connection to the postmaster. Use the
+	 * information saved by connectDB to do this with only kernel calls.
+	*/
+	if ((tmpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+	{
+		return FALSE;
+	}
+	if (connect(tmpsock, (struct sockaddr *)&(conn->sock->sadr),
+				sizeof(conn->sock->sadr)) < 0)
+	{
+		return FALSE;
+	}
+
+	/*
+	 * We needn't set nonblocking I/O or NODELAY options here.
+	 */
+	crp.packetlen = htonl((uint32) sizeof(crp));
+	crp.cp.cancelRequestCode = (MsgType) htonl(CANCEL_REQUEST_CODE);
+	crp.cp.backendPID = htonl(conn->be_pid);
+	crp.cp.cancelAuthCode = htonl(conn->be_key);
+
+	if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
+	{
+		return FALSE;
+	}
+
+	/* Sent it, done */
+	closesocket(tmpsock);
+#ifdef WIN32
+	WSASetLastError(save_errno);
+#else
+	errno = save_errno;
+#endif
+	return TRUE;
+}
diff --git a/src/interfaces/odbc/connection.h b/src/interfaces/odbc/connection.h
index 5d01dacd214..849350d14d1 100644
--- a/src/interfaces/odbc/connection.h
+++ b/src/interfaces/odbc/connection.h
@@ -126,6 +126,21 @@ typedef struct _StartupPacket6_2
 	char		tty[PATH_SIZE];
 } StartupPacket6_2;
 
+/* Transferred from pqcomm.h:  */
+
+
+typedef ProtocolVersion MsgType;
+
+#define PG_PROTOCOL(m,n)   (((m) << 16) | (n))
+#define CANCEL_REQUEST_CODE PG_PROTOCOL(1234,5678)
+
+typedef struct CancelRequestPacket
+{
+	/* Note that each field is stored in network byte order! */
+	MsgType		cancelRequestCode;	/* code to identify a cancel request */
+	unsigned int	backendPID;	/* PID of client's backend */
+	unsigned int	cancelAuthCode; /* secret key to authorize cancel */
+} CancelRequestPacket;
 
 /*	Structure to hold all the connection attributes for a specific
 	connection (used for both registry and file, DSN and DRIVER)
@@ -273,11 +288,14 @@ struct ConnectionClass_
 	char		ms_jet;
 	char		unicode;
 	char		result_uncommitted;
+	char		schema_support;
 #ifdef	MULTIBYTE
 	char	   *client_encoding;
 	char	   *server_encoding;
 #endif   /* MULTIBYTE */
 	int	ccsc;
+	int		be_pid;	/* pid returned by backend */
+	int		be_key; /* auth code needed to send cancel */
 };
 
 
@@ -319,6 +337,7 @@ 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);
+int		CC_send_cancel_request(const ConnectionClass *conn);
 void		CC_on_commit(ConnectionClass *conn);
 void		CC_on_abort(ConnectionClass *conn, BOOL set_no_trans);
 void		ProcessRollback(ConnectionClass *conn, BOOL undo);
diff --git a/src/interfaces/odbc/descriptor.h b/src/interfaces/odbc/descriptor.h
index 5186810b217..fe90a2cbd3a 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.2 2002/04/01 03:01:14 inoue Exp $
+ * $Id: descriptor.h,v 1.3 2002/04/02 10:50:44 inoue Exp $
  *
  */
 
@@ -17,6 +17,7 @@
 typedef struct
 {
 	COL_INFO	*col_info; /* cached SQLColumns info for this table */
+	char		schema[MAX_TABLE_LEN + 1];
 	char		name[MAX_TABLE_LEN + 1];
 	char		alias[MAX_TABLE_LEN + 1];
 } TABLE_INFO;
diff --git a/src/interfaces/odbc/execute.c b/src/interfaces/odbc/execute.c
index 11893321f84..14ed086e83a 100644
--- a/src/interfaces/odbc/execute.c
+++ b/src/interfaces/odbc/execute.c
@@ -573,6 +573,7 @@ PGAPI_Cancel(
 {
 	static char *func = "PGAPI_Cancel";
 	StatementClass *stmt = (StatementClass *) hstmt;
+	ConnectionClass *conn;
 	RETCODE		result;
 	ConnInfo   *ci;
 
@@ -589,7 +590,8 @@ PGAPI_Cancel(
 		SC_log_error(func, "", NULL);
 		return SQL_INVALID_HANDLE;
 	}
-	ci = &(SC_get_conn(stmt)->connInfo);
+	conn = SC_get_conn(stmt);
+	ci = &(conn->connInfo);
 
 	/*
 	 * Not in the middle of SQLParamData/SQLPutData so cancel like a
@@ -597,6 +599,11 @@ PGAPI_Cancel(
 	 */
 	if (stmt->data_at_exec < 0)
 	{
+		/*
+		 * Tell the Backend that we're cancelling this request
+		 */
+		if (stmt->status == STMT_EXECUTING)
+			CC_send_cancel_request(conn);
 		/*
 		 * MAJOR HACK for Windows to reset the driver manager's cursor
 		 * state: Because of what seems like a bug in the Odbc driver
diff --git a/src/interfaces/odbc/info.c b/src/interfaces/odbc/info.c
index a2aba6cf1ef..39fdce1f32e 100644
--- a/src/interfaces/odbc/info.c
+++ b/src/interfaces/odbc/info.c
@@ -1169,7 +1169,7 @@ PGAPI_Tables(
 				systable;
 	int			i;
 
-	mylog("%s: entering...stmt=%u\n", func, stmt);
+	mylog("%s: entering...stmt=%u scnm=%x len=%d\n", func, stmt, szTableOwner, cbTableOwner);
 
 	if (!stmt)
 	{
@@ -1196,7 +1196,13 @@ PGAPI_Tables(
 	/*
 	 * Create the query to find out the tables
 	 */
-	if (PG_VERSION_GE(conn, 7.1))
+	if (conn->schema_support)
+	{
+		/* view is represented by its relkind since 7.1 */
+		strcpy(tables_query, "select relname, nspname, relkind from pg_class, pg_namespace");
+		strcat(tables_query, " where relkind in ('r', 'v')");
+	}
+	else if (PG_VERSION_GE(conn, 7.1))
 	{
 		/* view is represented by its relkind since 7.1 */
 		strcpy(tables_query, "select relname, usename, relkind from pg_class, pg_user");
@@ -1208,7 +1214,10 @@ PGAPI_Tables(
 		strcat(tables_query, " where relkind = 'r'");
 	}
 
-	my_strcat(tables_query, " and usename like '%.*s'", szTableOwner, cbTableOwner);
+	if (conn->schema_support)
+		schema_strcat(tables_query, " and nspname like '%.*s'", szTableOwner, cbTableOwner, szTableName, cbTableName);
+	else
+		my_strcat(tables_query, " and usename like '%.*s'", szTableOwner, cbTableOwner);
 	my_strcat(tables_query, " and relname like '%.*s'", szTableName, cbTableName);
 
 	/* Parse the extra systable prefix	*/
@@ -1278,8 +1287,10 @@ PGAPI_Tables(
 		/* filter out large objects in older versions */
 		strcat(tables_query, " and relname !~ '^xinv[0-9]+'");
 
-	strcat(tables_query, " and usesysid = relowner");
-	strcat(tables_query, " order by relname");
+	if (conn->schema_support)
+		strcat(tables_query, " and pg_namespace.oid = relnamespace order by nspname, relname");
+	else
+		strcat(tables_query, " and usesysid = relowner order by relname");
 
 	result = PGAPI_ExecDirect(htbl_stmt, tables_query, strlen(tables_query));
 	if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
@@ -1404,7 +1415,6 @@ PGAPI_Tables(
 			row = (TupleNode *) malloc(sizeof(TupleNode) + (5 - 1) *sizeof(TupleField));
 
 			/*set_tuplefield_string(&row->tuple[0], "");*/
-			/*set_tuplefield_string(&row->tuple[0], "cat0");*/
 			set_tuplefield_null(&row->tuple[0]);
 
 			/*
@@ -1418,8 +1428,11 @@ PGAPI_Tables(
 
 			mylog("%s: table_name = '%s'\n", func, table_name);
 
-			/* set_tuplefield_string(&row->tuple[1], ""); */
-			set_tuplefield_null(&row->tuple[1]);
+			if (conn->schema_support)
+				set_tuplefield_string(&row->tuple[1], GET_SCHEMA_NAME(table_owner));
+			else
+				/* set_tuplefield_string(&row->tuple[1], ""); */
+				set_tuplefield_null(&row->tuple[1]);
 			set_tuplefield_string(&row->tuple[2], table_name);
 			set_tuplefield_string(&row->tuple[3], systable ? "SYSTEM TABLE" : (view ? "VIEW" : "TABLE"));
 			/*set_tuplefield_string(&row->tuple[4], "");*/
@@ -1561,7 +1574,7 @@ PGAPI_Columns(
 	ConnectionClass *conn;
 
 
-	mylog("%s: entering...stmt=%u\n", func, stmt);
+	mylog("%s: entering...stmt=%u scnm=%x len=%d\n", func, stmt, szTableOwner, cbTableOwner);
 
 	if (!stmt)
 	{
@@ -1579,7 +1592,15 @@ PGAPI_Columns(
 	 * Create the query to find out the columns (Note: pre 6.3 did not
 	 * have the atttypmod field)
 	 */
-	sprintf(columns_query, "select u.usename, c.relname, a.attname, a.atttypid"
+	if (conn->schema_support)
+		sprintf(columns_query, "select u.nspname, c.relname, a.attname, a.atttypid"
+	   ", t.typname, a.attnum, a.attlen, %s, a.attnotnull, c.relhasrules"
+			" from pg_namespace u, pg_class c, pg_attribute a, pg_type t"
+			" where u.oid = c.relnamespace"
+	  " and c.oid= a.attrelid and a.atttypid = t.oid and (a.attnum > 0)",
+			"a.atttypmod");
+	else
+		sprintf(columns_query, "select u.usename, c.relname, a.attname, a.atttypid"
 	   ", t.typname, a.attnum, a.attlen, %s, a.attnotnull, c.relhasrules"
 			" from pg_user u, pg_class c, pg_attribute a, pg_type t"
 			" where u.usesysid = c.relowner"
@@ -1589,7 +1610,10 @@ PGAPI_Columns(
 	if ((flag & PODBC_NOT_SEARCH_PATTERN) != 0) 
 	{
 		my_strcat(columns_query, " and c.relname = '%.*s'", szTableName, cbTableName);
-		my_strcat(columns_query, " and u.usename = '%.*s'", szTableOwner, cbTableOwner);
+		if (conn->schema_support)
+			schema_strcat(columns_query, " and u.nspname = '%.*s'", szTableOwner, cbTableOwner, szTableName, cbTableName);
+		else
+			my_strcat(columns_query, " and u.usename = '%.*s'", szTableOwner, cbTableOwner);
 		my_strcat(columns_query, " and a.attname = '%.*s'", szColumnName, cbColumnName);
 	}
 	else
@@ -1599,7 +1623,10 @@ PGAPI_Columns(
 
 		escTbnamelen = reallyEscapeCatalogEscapes(szTableName, cbTableName, esc_table_name, sizeof(esc_table_name), conn->ccsc);
 		my_strcat(columns_query, " and c.relname like '%.*s'", esc_table_name, escTbnamelen);
-		my_strcat(columns_query, " and u.usename like '%.*s'", szTableOwner, cbTableOwner);
+		if (conn->schema_support)
+			schema_strcat(columns_query, " and u.nspname like '%.*s'", szTableOwner, cbTableOwner, szTableName, cbTableName);
+		else
+			my_strcat(columns_query, " and u.usename like '%.*s'", szTableOwner, cbTableOwner);
 		my_strcat(columns_query, " and a.attname like '%.*s'", szColumnName, cbColumnName);
 	}
 
@@ -1607,7 +1634,10 @@ PGAPI_Columns(
 	 * give the output in the order the columns were defined when the
 	 * table was created
 	 */
-	strcat(columns_query, " order by attnum");
+	if (conn->schema_support)
+		strcat(columns_query, " order by u.nspname, c.relname, attnum");
+	else
+		strcat(columns_query, " order by c.relname, attnum");
 
 	result = PGAPI_AllocStmt(stmt->hdbc, &hcol_stmt);
 	if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
@@ -1815,8 +1845,10 @@ PGAPI_Columns(
 
 			set_tuplefield_string(&row->tuple[0], "");
 			/* see note in SQLTables() */
-			/* set_tuplefield_string(&row->tuple[1], table_owner); */
-			set_tuplefield_string(&row->tuple[1], "");
+			if (conn->schema_support)
+				set_tuplefield_string(&row->tuple[1], GET_SCHEMA_NAME(table_owner));
+			else
+				set_tuplefield_string(&row->tuple[1], "");
 			set_tuplefield_string(&row->tuple[2], table_name);
 			set_tuplefield_string(&row->tuple[3], "oid");
 			sqltype = pgtype_to_concise_type(stmt, the_type);
@@ -1854,8 +1886,10 @@ PGAPI_Columns(
 
 		set_tuplefield_string(&row->tuple[0], "");
 		/* see note in SQLTables() */
-		/* set_tuplefield_string(&row->tuple[1], table_owner); */
-		set_tuplefield_string(&row->tuple[1], "");
+		if (conn->schema_support)
+			set_tuplefield_string(&row->tuple[1], GET_SCHEMA_NAME(table_owner));
+		else
+			set_tuplefield_string(&row->tuple[1], "");
 		set_tuplefield_string(&row->tuple[2], table_name);
 		set_tuplefield_string(&row->tuple[3], field_name);
 		sqltype = pgtype_to_concise_type(stmt, field_type);
@@ -1980,7 +2014,10 @@ PGAPI_Columns(
 								   (result_cols - 1) *sizeof(TupleField));
 
 		set_tuplefield_string(&row->tuple[0], "");
-		set_tuplefield_string(&row->tuple[1], "");
+		if (conn->schema_support)
+			set_tuplefield_string(&row->tuple[1], GET_SCHEMA_NAME(table_owner));
+		else
+			set_tuplefield_string(&row->tuple[1], "");
 		set_tuplefield_string(&row->tuple[2], table_name);
 		set_tuplefield_string(&row->tuple[3], "xmin");
 		sqltype = pgtype_to_concise_type(stmt, the_type);
@@ -2040,6 +2077,7 @@ PGAPI_SpecialColumns(
 	static char *func = "PGAPI_SpecialColumns";
 	TupleNode  *row;
 	StatementClass *stmt = (StatementClass *) hstmt;
+	ConnectionClass *conn;
 	QResultClass	*res;
 	ConnInfo   *ci;
 	HSTMT		hcol_stmt;
@@ -2048,28 +2086,37 @@ PGAPI_SpecialColumns(
 	RETCODE		result;
 	char		relhasrules[MAX_INFO_STRING];
 
-	mylog("%s: entering...stmt=%u\n", func, stmt);
+	mylog("%s: entering...stmt=%u scnm=%x len=%d\n", func, stmt, szTableOwner, cbTableOwner);
 
 	if (!stmt)
 	{
 		SC_log_error(func, "", NULL);
 		return SQL_INVALID_HANDLE;
 	}
-	ci = &(SC_get_conn(stmt)->connInfo);
+	conn = SC_get_conn(stmt);
+	ci = &(conn->connInfo);
 
 	stmt->manual_result = TRUE;
 
 	/*
 	 * Create the query to find out if this is a view or not...
 	 */
-	sprintf(columns_query, "select c.relhasrules "
+	if (conn->schema_support)
+		sprintf(columns_query, "select c.relhasrules "
+			"from pg_namespace u, pg_class c where "
+			"u.oid = c.relnamespace");
+	else
+		sprintf(columns_query, "select c.relhasrules "
 			"from pg_user u, pg_class c where "
 			"u.usesysid = c.relowner");
 
 	/* TableName cannot contain a string search pattern */
 	my_strcat(columns_query, " and c.relname = '%.*s'", szTableName, cbTableName);
 	/* SchemaName cannot contain a string search pattern */
-	my_strcat(columns_query, " and u.usename = '%.*s'", szTableOwner, cbTableOwner);
+	if (conn->schema_support)
+		schema_strcat(columns_query, " and u.nspname = '%.*s'", szTableOwner, cbTableOwner, szTableName, cbTableName);
+	else
+		my_strcat(columns_query, " and u.usename = '%.*s'", szTableOwner, cbTableOwner);
 
 
 	result = PGAPI_AllocStmt(stmt->hdbc, &hcol_stmt);
@@ -2188,11 +2235,12 @@ PGAPI_Statistics(
 {
 	static char *func = "PGAPI_Statistics";
 	StatementClass *stmt = (StatementClass *) hstmt;
+	ConnectionClass *conn;
 	QResultClass	*res;
 	char		index_query[INFO_INQUIRY_LEN];
 	HSTMT		hindx_stmt;
 	RETCODE		result;
-	char	   *table_name;
+	char	   *table_name, *schema_name = NULL;
 	char		index_name[MAX_INFO_STRING];
 	short		fields_vector[16];
 	char		isunique[10],
@@ -2206,7 +2254,8 @@ PGAPI_Statistics(
 	StatementClass *col_stmt,
 			   *indx_stmt;
 	char		column_name[MAX_INFO_STRING],
-				relhasrules[MAX_INFO_STRING];
+			table_qualifier[MAX_INFO_STRING],
+				relhasrules[10];
 	char	  **column_names = 0;
 	SQLINTEGER	column_name_len;
 	int			total_columns = 0;
@@ -2214,7 +2263,7 @@ PGAPI_Statistics(
 	ConnInfo   *ci;
 	char		buf[256];
 
-	mylog("%s: entering...stmt=%u\n", func, stmt);
+	mylog("%s: entering...stmt=%u scnm=%x len=%d\n", func, stmt, szTableOwner, cbTableOwner);
 
 	if (!stmt)
 	{
@@ -2225,7 +2274,8 @@ PGAPI_Statistics(
 	stmt->manual_result = TRUE;
 	stmt->errormsg_created = TRUE;
 
-	ci = &(SC_get_conn(stmt)->connInfo);
+	conn = SC_get_conn(stmt);
+	ci = &(conn->connInfo);
 
 	if (res = QR_Constructor(), !res)
 	{
@@ -2272,6 +2322,9 @@ PGAPI_Statistics(
 		SC_log_error(func, "", stmt);
 		return SQL_ERROR;
 	}
+	table_qualifier[0] = '\0';
+	if (conn->schema_support)
+		schema_strcat(table_qualifier, "%.*s", szTableOwner, cbTableOwner, szTableName, cbTableName);
 
 	/*
 	 * we need to get a list of the field names first, so we can return
@@ -2295,8 +2348,8 @@ PGAPI_Statistics(
 	/* 
 	 * table_name parameter cannot contain a string search pattern. 
 	 */
-	result = PGAPI_Columns(hcol_stmt, "", 0, "", 0,
-						   table_name, (SWORD) strlen(table_name), "", 0, PODBC_NOT_SEARCH_PATTERN);
+	result = PGAPI_Columns(hcol_stmt, "", 0, table_qualifier, SQL_NTS,
+						   table_name, SQL_NTS, "", 0, PODBC_NOT_SEARCH_PATTERN);
 	col_stmt->internal = FALSE;
 
 	if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
@@ -2308,7 +2361,7 @@ PGAPI_Statistics(
 		goto SEEYA;
 	}
 	result = PGAPI_BindCol(hcol_stmt, 4, SQL_C_CHAR,
-						 column_name, MAX_INFO_STRING, &column_name_len);
+						 column_name, sizeof(column_name), &column_name_len);
 	if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
 	{
 		stmt->errormsg = col_stmt->errormsg;
@@ -2359,16 +2412,32 @@ PGAPI_Statistics(
 	}
 	indx_stmt = (StatementClass *) hindx_stmt;
 
-	sprintf(index_query, "select c.relname, i.indkey, i.indisunique"
+	if (conn->schema_support)
+		sprintf(index_query, "select c.relname, i.indkey, i.indisunique"
+			", i.indisclustered, a.amname, c.relhasrules, n.nspname"
+			" from pg_index i, pg_class c, pg_class d, pg_am a, pg_namespace n"
+			" where d.relname = '%s'"
+			" and n.nspname = '%s'"
+			" and n.oid = d.relnamespace"
+			" and d.oid = i.indrelid"
+			" and i.indexrelid = c.oid"
+			" and c.relam = a.oid order by"
+			,table_name, table_qualifier);
+	else
+		sprintf(index_query, "select c.relname, i.indkey, i.indisunique"
 			", i.indisclustered, a.amname, c.relhasrules"
 			" from pg_index i, pg_class c, pg_class d, pg_am a"
 			" where d.relname = '%s'"
 			" and d.oid = i.indrelid"
 			" and i.indexrelid = c.oid"
-			" and c.relam = a.oid"
+			" and c.relam = a.oid order by"
 			,table_name);
 	if (PG_VERSION_GT(SC_get_conn(stmt), 6.4))
-		strcat(index_query, " order by i.indisprimary desc");
+		strcat(index_query, " i.indisprimary desc,");
+	if (conn->schema_support)
+		strcat(index_query, " i.indisunique, n.nspname, c.relname");
+	else
+		strcat(index_query, " i.indisunique, c.relname");
 
 	result = PGAPI_ExecDirect(hindx_stmt, index_query, strlen(index_query));
 	if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
@@ -2447,7 +2516,7 @@ PGAPI_Statistics(
 	}
 
 	result = PGAPI_BindCol(hindx_stmt, 6, SQL_C_CHAR,
-						   relhasrules, MAX_INFO_STRING, NULL);
+					relhasrules, sizeof(relhasrules), NULL);
 	if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
 	{
 		stmt->errormsg = indx_stmt->errormsg;
@@ -2465,7 +2534,7 @@ PGAPI_Statistics(
 		/* no table qualifier */
 		set_tuplefield_string(&row->tuple[0], "");
 		/* don't set the table owner, else Access tries to use it */
-		set_tuplefield_string(&row->tuple[1], "");
+		set_tuplefield_string(&row->tuple[1], GET_SCHEMA_NAME(table_qualifier));
 		set_tuplefield_string(&row->tuple[2], table_name);
 
 		/* non-unique index? */
@@ -2509,7 +2578,7 @@ PGAPI_Statistics(
 				/* no table qualifier */
 				set_tuplefield_string(&row->tuple[0], "");
 				/* don't set the table owner, else Access tries to use it */
-				set_tuplefield_string(&row->tuple[1], "");
+				set_tuplefield_string(&row->tuple[1], GET_SCHEMA_NAME(table_qualifier));
 				set_tuplefield_string(&row->tuple[2], table_name);
 
 				/* non-unique index? */
@@ -2654,13 +2723,13 @@ PGAPI_PrimaryKeys(
 	char		tables_query[INFO_INQUIRY_LEN];
 	char		attname[MAX_INFO_STRING];
 	SDWORD		attname_len;
-	char		pktab[MAX_TABLE_LEN + 1];
+	char		pktab[MAX_TABLE_LEN + 1], pkscm[MAX_TABLE_LEN + 1];
 	Int2		result_cols;
 	int			qno,
 				qstart,
 				qend;
 
-	mylog("%s: entering...stmt=%u\n", func, stmt);
+	mylog("%s: entering...stmt=%u scnm=%x len=%d\n", func, stmt, szTableOwner, cbTableOwner);
 
 	if (!stmt)
 	{
@@ -2708,6 +2777,7 @@ PGAPI_PrimaryKeys(
 	}
 	tbl_stmt = (StatementClass *) htbl_stmt;
 
+	conn = SC_get_conn(stmt);
 	pktab[0] = '\0';
 	make_string(szTableName, cbTableName, pktab);
 	if (pktab[0] == '\0')
@@ -2718,6 +2788,9 @@ PGAPI_PrimaryKeys(
 		PGAPI_FreeStmt(htbl_stmt, SQL_DROP);
 		return SQL_ERROR;
 	}
+	pkscm[0] = '\0';
+	if (conn->schema_support)
+		schema_strcat(pkscm, "%.*s", szTableOwner, cbTableOwner, szTableName, cbTableName);
 
 	result = PGAPI_BindCol(htbl_stmt, 1, SQL_C_CHAR,
 						   attname, MAX_INFO_STRING, &attname_len);
@@ -2730,7 +2803,6 @@ PGAPI_PrimaryKeys(
 		return SQL_ERROR;
 	}
 
-	conn = SC_get_conn(stmt);
 	if (PG_VERSION_LE(conn, 6.4))
 		qstart = 2;
 	else
@@ -2747,7 +2819,20 @@ PGAPI_PrimaryKeys(
 				 * possible index columns. Courtesy of Tom Lane - thomas
 				 * 2000-03-21
 				 */
-				sprintf(tables_query, "select ta.attname, ia.attnum"
+				if (conn->schema_support)
+					sprintf(tables_query, "select ta.attname, ia.attnum"
+						" from pg_attribute ta, pg_attribute ia, pg_class c, pg_index i, pg_namespace n"
+						" where c.relname = '%s'"
+						" AND n.nspname = '%s'"
+						" AND c.oid = i.indrelid"
+						" AND n.oid = c.relnamespace"
+						" AND i.indisprimary = 't'"
+						" AND ia.attrelid = i.indexrelid"
+						" AND ta.attrelid = i.indrelid"
+						" AND ta.attnum = i.indkey[ia.attnum-1]"
+						" order by ia.attnum", pktab, pkscm);
+				else
+					sprintf(tables_query, "select ta.attname, ia.attnum"
 						" from pg_attribute ta, pg_attribute ia, pg_class c, pg_index i"
 						" where c.relname = '%s'"
 						" AND c.oid = i.indrelid"
@@ -2762,7 +2847,19 @@ PGAPI_PrimaryKeys(
 				/*
 				 * Simplified query to search old fashoned primary key
 				 */
-				sprintf(tables_query, "select ta.attname, ia.attnum"
+				if (conn->schema_support)
+					sprintf(tables_query, "select ta.attname, ia.attnum"
+						" from pg_attribute ta, pg_attribute ia, pg_class c, pg_index i, pg_namespace n"
+						" where c.relname = '%s_pkey'"
+						" AND n.nspname = '%s'"
+						" AND c.oid = i.indexrelid"
+						" AND n.oid = c.relnamespace"
+						" AND ia.attrelid = i.indexrelid"
+						" AND ta.attrelid = i.indrelid"
+						" AND ta.attnum = i.indkey[ia.attnum-1]"
+						" order by ia.attnum", pktab, pkscm);
+				else
+					sprintf(tables_query, "select ta.attname, ia.attnum"
 						" from pg_attribute ta, pg_attribute ia, pg_class c, pg_index i"
 						" where c.relname = '%s_pkey'"
 						" AND c.oid = i.indexrelid"
@@ -2801,7 +2898,7 @@ PGAPI_PrimaryKeys(
 		 * valid according to the ODBC SQL grammar, but Postgres won't
 		 * support it.)
 		 */
-		set_tuplefield_string(&row->tuple[1], "");
+		set_tuplefield_string(&row->tuple[1], GET_SCHEMA_NAME(pkscm));
 		set_tuplefield_string(&row->tuple[2], pktab);
 		set_tuplefield_string(&row->tuple[3], attname);
 		set_tuplefield_int2(&row->tuple[4], (Int2) (++seq));
@@ -2859,7 +2956,7 @@ isMultibyte(const unsigned char *str)
 	return FALSE;
 }
 static char *
-getClientTableName(ConnectionClass *conn, char *serverTableName, BOOL *nameAlloced)
+getClientTableName(ConnectionClass *conn, const char *serverSchemaName, char *serverTableName, BOOL *nameAlloced)
 {
 	char		query[1024],
 				saveoid[24],
@@ -2886,7 +2983,10 @@ getClientTableName(ConnectionClass *conn, char *serverTableName, BOOL *nameAlloc
 	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 (conn->schema_support)
+			sprintf(query, "select OID from pg_class where relname = '%s' and pg_namespace.oid = relnamespace and pg_namespace.nspname = '%s'", serverTableName, serverSchemaName);
+		else
+			sprintf(query, "select OID from pg_class where relname = '%s'", serverTableName);
 		if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res)
 		{
 			if (QR_get_num_tuples(res) > 0)
@@ -2922,7 +3022,7 @@ getClientTableName(ConnectionClass *conn, char *serverTableName, BOOL *nameAlloc
 	return ret;
 }
 static char *
-getClientColumnName(ConnectionClass *conn, const char *serverTableName, char *serverColumnName, BOOL *nameAlloced)
+getClientColumnName(ConnectionClass *conn, const char * serverSchemaName, const char *serverTableName, char *serverColumnName, BOOL *nameAlloced)
 {
 	char		query[1024],
 				saveattrelid[24],
@@ -2950,7 +3050,12 @@ getClientColumnName(ConnectionClass *conn, const char *serverTableName, char *se
 	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 "
+		if (conn->schema_support)
+			sprintf(query, "select attrelid, attnum from pg_class, pg_attribute "
+				"where relname = '%s' and attrelid = pg_class.oid "
+				"and attname = '%s' and pg_namespace.oid = relnamespace and pg_namespace.nspname = '%s'", serverTableName, serverColumnName, serverSchemaName);
+		else
+			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, CLEAR_RESULT_ON_ABORT), res)
@@ -3025,6 +3130,7 @@ PGAPI_ForeignKeys(
 				del_rule[MAX_TABLE_LEN];
 	char		pk_table_needed[MAX_TABLE_LEN + 1];
 	char		fk_table_needed[MAX_TABLE_LEN + 1];
+	char		schema_needed[MAX_TABLE_LEN + 1];
 	char	   *pkey_ptr,
 			   *pkey_text,
 			   *fkey_ptr,
@@ -3034,12 +3140,12 @@ PGAPI_ForeignKeys(
 			   *fk_table,
 			   *fkt_text;
 
+	ConnectionClass *conn;
 #ifdef	MULTIBYTE
 	BOOL		pkey_alloced,
 				fkey_alloced,
 				pkt_alloced,
 				fkt_alloced;
-	ConnectionClass *conn;
 #endif   /* MULTIBYTE */
 	int			i,
 				j,
@@ -3081,7 +3187,11 @@ PGAPI_ForeignKeys(
 	 * a statement is actually executed, so we'll have to do this
 	 * ourselves.
 	 */
+#if (ODBCVER >= 0x0300)
+	result_cols = 15;
+#else
 	result_cols = 14;
+#endif /* ODBCVER */
 	extend_column_bindings(SC_get_ARD(stmt), result_cols);
 
 	/* set the field names */
@@ -3129,14 +3239,15 @@ PGAPI_ForeignKeys(
 
 	pk_table_needed[0] = '\0';
 	fk_table_needed[0] = '\0';
+	schema_needed[0] = '\0';
 
 	make_string(szPkTableName, cbPkTableName, pk_table_needed);
 	make_string(szFkTableName, cbFkTableName, fk_table_needed);
 
+	conn = SC_get_conn(stmt);
 #ifdef	MULTIBYTE
 	pkey_text = fkey_text = pkt_text = fkt_text = NULL;
 	pkey_alloced = fkey_alloced = pkt_alloced = fkt_alloced = FALSE;
-	conn = SC_get_conn(stmt);
 #endif   /* MULTIBYTE */
 
 	/*
@@ -3146,7 +3257,42 @@ PGAPI_ForeignKeys(
 	if (fk_table_needed[0] != '\0')
 	{
 		mylog("%s: entering Foreign Key Case #2", func);
-		sprintf(tables_query, "SELECT	pt.tgargs, "
+		if (conn->schema_support)
+		{
+			schema_strcat(schema_needed, "%.*s", szFkTableOwner, cbFkTableOwner, szFkTableName, cbFkTableName); 	
+			sprintf(tables_query, "SELECT	pt.tgargs, "
+				"		pt.tgnargs, "
+				"		pt.tgdeferrable, "
+				"		pt.tginitdeferred, "
+				"		pg_proc.proname, "
+				"		pg_proc_1.proname "
+				"FROM	pg_class pc, "
+				"		pg_proc pg_proc, "
+				"		pg_proc pg_proc_1, "
+				"		pg_trigger pg_trigger, "
+				"		pg_trigger pg_trigger_1, "
+				"		pg_proc pp, "
+				"		pg_trigger pt "
+				"WHERE	pt.tgrelid = pc.oid "
+				"AND pp.oid = pt.tgfoid "
+				"AND pg_trigger.tgconstrrelid = pc.oid "
+				"AND pg_proc.oid = pg_trigger.tgfoid "
+				"AND pg_trigger_1.tgfoid = pg_proc_1.oid "
+				"AND pg_trigger_1.tgconstrrelid = pc.oid "
+				"AND ((pc.relname='%s') "
+				"AND (pg_namespace.oid = pc.relnamespace) "
+				"AND (pg_namespace.nspname = '%s') "
+				"AND (pp.proname LIKE '%%ins') "
+				"AND (pg_proc.proname LIKE '%%upd') "
+				"AND (pg_proc_1.proname LIKE '%%del') "
+				"AND (pg_trigger.tgrelid=pt.tgconstrrelid) "
+				"AND (pg_trigger.tgconstrname=pt.tgconstrname) "
+				"AND (pg_trigger_1.tgrelid=pt.tgconstrrelid) "
+				"AND (pg_trigger_1.tgconstrname=pt.tgconstrname))",
+				fk_table_needed, schema_needed);
+		}
+		else
+			sprintf(tables_query, "SELECT	pt.tgargs, "
 				"		pt.tgnargs, "
 				"		pt.tgdeferrable, "
 				"		pt.tginitdeferred, "
@@ -3300,7 +3446,7 @@ PGAPI_ForeignKeys(
 
 #ifdef	MULTIBYTE
 			fk_table = trig_args + strlen(trig_args) + 1;
-			pkt_text = getClientTableName(conn, pk_table, &pkt_alloced);
+			pkt_text = getClientTableName(conn, schema_needed, pk_table, &pkt_alloced);
 #else
 			pkt_text = pk_table;
 #endif   /* MULTIBYTE */
@@ -3315,7 +3461,7 @@ PGAPI_ForeignKeys(
 				}
 			}
 
-			keyresult = PGAPI_PrimaryKeys(hpkey_stmt, NULL, 0, NULL, 0, pkt_text, SQL_NTS);
+			keyresult = PGAPI_PrimaryKeys(hpkey_stmt, NULL, 0, schema_needed, SQL_NTS, pkt_text, SQL_NTS);
 			if (keyresult != SQL_SUCCESS)
 			{
 				stmt->errornumber = STMT_NO_MEMORY_ERROR;
@@ -3341,7 +3487,7 @@ PGAPI_ForeignKeys(
 					break;
 				}
 #ifdef	MULTIBYTE
-				pkey_text = getClientColumnName(conn, pk_table, pkey_ptr, &pkey_alloced);
+				pkey_text = getClientColumnName(conn, schema_needed, pk_table, pkey_ptr, &pkey_alloced);
 #else
 				pkey_text = pkey_ptr;
 #endif   /* MULTIBYTE */
@@ -3409,21 +3555,21 @@ PGAPI_ForeignKeys(
 				row = (TupleNode *) malloc(sizeof(TupleNode) + (result_cols - 1) *sizeof(TupleField));
 
 #ifdef	MULTIBYTE
-				pkey_text = getClientColumnName(conn, pk_table, pkey_ptr, &pkey_alloced);
-				fkey_text = getClientColumnName(conn, fk_table, fkey_ptr, &fkey_alloced);
+				pkey_text = getClientColumnName(conn, schema_needed, pk_table, pkey_ptr, &pkey_alloced);
+				fkey_text = getClientColumnName(conn, schema_needed, fk_table, fkey_ptr, &fkey_alloced);
 #else
 				pkey_text = pkey_ptr;
 				fkey_text = fkey_ptr;
 #endif   /* MULTIBYTE */
 				mylog("%s: pk_table = '%s', pkey_ptr = '%s'\n", func, pkt_text, pkey_text);
 				set_tuplefield_null(&row->tuple[0]);
-				set_tuplefield_string(&row->tuple[1], "");
+				set_tuplefield_string(&row->tuple[1], GET_SCHEMA_NAME(schema_needed));
 				set_tuplefield_string(&row->tuple[2], pkt_text);
 				set_tuplefield_string(&row->tuple[3], pkey_text);
 
 				mylog("%s: fk_table_needed = '%s', fkey_ptr = '%s'\n", func, fk_table_needed, fkey_text);
 				set_tuplefield_null(&row->tuple[4]);
-				set_tuplefield_string(&row->tuple[5], "");
+				set_tuplefield_string(&row->tuple[5], GET_SCHEMA_NAME(schema_needed));
 				set_tuplefield_string(&row->tuple[6], fk_table_needed);
 				set_tuplefield_string(&row->tuple[7], fkey_text);
 
@@ -3472,7 +3618,43 @@ PGAPI_ForeignKeys(
 	 */
 	else if (pk_table_needed[0] != '\0')
 	{
-		sprintf(tables_query, "SELECT	pg_trigger.tgargs, "
+		if (conn->schema_support)
+		{
+			schema_strcat(schema_needed, "%.*s", szPkTableOwner, cbPkTableOwner, szPkTableName, cbPkTableName); 	
+			sprintf(tables_query, "SELECT	pg_trigger.tgargs, "
+				"		pg_trigger.tgnargs, "
+				"		pg_trigger.tgdeferrable, "
+				"		pg_trigger.tginitdeferred, "
+				"		pg_proc.proname, "
+				"		pg_proc_1.proname "
+				"FROM	pg_class pg_class, "
+				"		pg_class pg_class_1, "
+				"		pg_class pg_class_2, "
+				"		pg_proc pg_proc, "
+				"		pg_proc pg_proc_1, "
+				"		pg_trigger pg_trigger, "
+				"		pg_trigger pg_trigger_1, "
+				"		pg_trigger pg_trigger_2 "
+				"WHERE	pg_trigger.tgconstrrelid = pg_class.oid "
+				"	AND pg_trigger.tgrelid = pg_class_1.oid "
+				"	AND pg_trigger_1.tgfoid = pg_proc_1.oid "
+				"	AND pg_trigger_1.tgconstrrelid = pg_class_1.oid "
+				"	AND pg_trigger_2.tgconstrrelid = pg_class_2.oid "
+				"	AND pg_trigger_2.tgfoid = pg_proc.oid "
+				"	AND pg_class_2.oid = pg_trigger.tgrelid "
+				"	AND ("
+				"		 (pg_class.relname='%s') "
+				"	AND  (pg_namespace.oid = pg_class.relnamespace) "
+				"	AND  (pg_namespace.nspname = '%s') "
+				"	AND  (pg_proc.proname Like '%%upd') "
+				"	AND  (pg_proc_1.proname Like '%%del')"
+				"	AND	 (pg_trigger_1.tgrelid = pg_trigger.tgconstrrelid) "
+				"	AND	 (pg_trigger_2.tgrelid = pg_trigger.tgconstrrelid) "
+				"		)",
+				pk_table_needed, schema_needed);
+		}
+		else
+			sprintf(tables_query, "SELECT	pg_trigger.tgargs, "
 				"		pg_trigger.tgnargs, "
 				"		pg_trigger.tgdeferrable, "
 				"		pg_trigger.tginitdeferred, "
@@ -3641,7 +3823,7 @@ PGAPI_ForeignKeys(
 			fk_table += strlen(fk_table) + 1;
 #ifdef	MULTIBYTE
 			pk_table = fk_table + strlen(fk_table) + 1;
-			fkt_text = getClientTableName(conn, fk_table, &fkt_alloced);
+			fkt_text = getClientTableName(conn, schema_needed, fk_table, &fkt_alloced);
 #else
 			fkt_text = fk_table;
 #endif   /* MULTIBYTE */
@@ -3654,8 +3836,8 @@ PGAPI_ForeignKeys(
 			for (k = 0; k < num_keys; k++)
 			{
 #ifdef	MULTIBYTE
-				pkey_text = getClientColumnName(conn, pk_table, pkey_ptr, &pkey_alloced);
-				fkey_text = getClientColumnName(conn, fk_table, fkey_ptr, &fkey_alloced);
+				pkey_text = getClientColumnName(conn, schema_needed, pk_table, pkey_ptr, &pkey_alloced);
+				fkey_text = getClientColumnName(conn, schema_needed, fk_table, fkey_ptr, &fkey_alloced);
 #else
 				pkey_text = pkey_ptr;
 				fkey_text = fkey_ptr;
@@ -3666,13 +3848,13 @@ PGAPI_ForeignKeys(
 
 				mylog("pk_table_needed = '%s', pkey_ptr = '%s'\n", pk_table_needed, pkey_text);
 				set_tuplefield_null(&row->tuple[0]);
-				set_tuplefield_string(&row->tuple[1], "");
+				set_tuplefield_string(&row->tuple[1], GET_SCHEMA_NAME(schema_needed));
 				set_tuplefield_string(&row->tuple[2], pk_table_needed);
 				set_tuplefield_string(&row->tuple[3], pkey_text);
 
 				mylog("fk_table = '%s', fkey_ptr = '%s'\n", fkt_text, fkey_text);
 				set_tuplefield_null(&row->tuple[4]);
-				set_tuplefield_string(&row->tuple[5], "");
+				set_tuplefield_string(&row->tuple[5], GET_SCHEMA_NAME(schema_needed));
 				set_tuplefield_string(&row->tuple[6], fkt_text);
 				set_tuplefield_string(&row->tuple[7], fkey_text);
 
@@ -3688,7 +3870,7 @@ PGAPI_ForeignKeys(
 				set_tuplefield_string(&row->tuple[13], trig_args);
 
 #if (ODBCVER >= 0x0300)
-				mylog("defer_type = '%s'", defer_type);
+				mylog(" defer_type = %d\n", defer_type);
 				set_tuplefield_int2(&row->tuple[14], defer_type);
 #endif   /* ODBCVER >= 0x0300 */
 
@@ -3783,7 +3965,7 @@ PGAPI_Procedures(
 	char		proc_query[INFO_INQUIRY_LEN];
 	QResultClass *res;
 
-	mylog("%s: entering...\n", func);
+	mylog("%s: entering... scnm=%x len=%d\n", func, szProcOwner, cbProcOwner);
 
 	if (PG_VERSION_LT(conn, 6.5))
 	{
@@ -3798,12 +3980,26 @@ PGAPI_Procedures(
 	/*
 	 * The following seems the simplest implementation
 	 */
-	strcpy(proc_query, "select '' as " "PROCEDURE_CAT" ", '' as " "PROCEDURE_SCHEM" ","
+	if (conn->schema_support)
+		strcpy(proc_query, "select '' as " "PROCEDURE_CAT" ", case when nspname = 'PUBLIC' then ''::text else nspname end as " "PROCEDURE_SCHEM" ","
 		" proname as " "PROCEDURE_NAME" ", '' as " "NUM_INPUT_PARAMS" ","
 		   " '' as " "NUM_OUTPUT_PARAMS" ", '' as " "NUM_RESULT_SETS" ","
 		   " '' as " "REMARKS" ","
-		   " 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);
+		   " case when prorettype = 0 then 1::int2 else 2::int2 end as " "PROCEDURE_TYPE" " from pg_namespace, pg_proc where");
+	else
+		strcpy(proc_query, "select '' as " "PROCEDURE_CAT" ", '' as " "PROCEDURE_SCHEM" ","
+		" proname as " "PROCEDURE_NAME" ", '' as " "NUM_INPUT_PARAMS" ","
+		   " '' as " "NUM_OUTPUT_PARAMS" ", '' as " "NUM_RESULT_SETS" ","
+		   " '' as " "REMARKS" ","
+		   " case when prorettype = 0 then 1::int2 else 2::int2 end as " "PROCEDURE_TYPE" " from pg_proc where");
+	if (conn->schema_support)
+	{
+		strcat(proc_query, " pg_proc.namespace = pg_namespace.oid");
+		schema_strcat(proc_query, " and nspname like '%.*s'", szProcOwner, cbProcOwner, szProcName, cbProcName);
+		my_strcat(proc_query, " and proname like '%.*s'", szProcName, cbProcName);
+	}
+	else
+		my_strcat(proc_query, " proname like '%.*s'", szProcName, cbProcName);
 
 	if (res = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), !res)
 	{
@@ -3895,9 +4091,9 @@ PGAPI_TablePrivileges(
 	int		tablecount, usercount, i, j, k;
 	BOOL		grpauth, sys, su;
 	char		(*useracl)[ACLMAX], *acl, *user, *delim, *auth;
-	char		*reln, *owner, *priv;
+	char		*reln, *owner, *priv, *schnm;
 
-	mylog("%s: entering...\n", func);
+	mylog("%s: entering... scnm=%x len-%d\n", func, szTableOwner, cbTableOwner);
 	if (!SC_recycle_statement(stmt))
 		return SQL_ERROR;
 
@@ -3930,17 +4126,33 @@ PGAPI_TablePrivileges(
 	stmt->currTuple = -1;
 	stmt->rowset_start = -1;
 	stmt->current_col = -1;
-	strncpy_null(proc_query, "select relname, usename, relacl from pg_class , pg_user where", sizeof(proc_query)); 
-	if ((flag & PODBC_NOT_SEARCH_PATTERN) != 0) 
+	if (conn->schema_support)
+		strncpy_null(proc_query, "select relname, usename, relacl, nspname from pg_namespace, pg_class , pg_user where", sizeof(proc_query));
+	else 
+		strncpy_null(proc_query, "select relname, usename, relacl from pg_class , pg_user where", sizeof(proc_query)); 
+	if ((flag & PODBC_NOT_SEARCH_PATTERN) != 0)
+	{
+		if (conn->schema_support)
+		{ 
+			schema_strcat(proc_query, " nspname = '%.*s' and", szTableOwner, cbTableOwner, szTableName, cbTableName);
+		}
 		my_strcat(proc_query, " relname = '%.*s' and", szTableName, cbTableName);
+	}
 	else
 	{
 		char	esc_table_name[MAX_TABLE_LEN * 2];
 		int	escTbnamelen;
 
+		if (conn->schema_support)
+		{
+			escTbnamelen = reallyEscapeCatalogEscapes(szTableOwner, cbTableOwner, esc_table_name, sizeof(esc_table_name), conn->ccsc);
+			schema_strcat(proc_query, " nspname like '%.*s' and", esc_table_name, escTbnamelen, szTableName, cbTableName);
+		}
 		escTbnamelen = reallyEscapeCatalogEscapes(szTableName, cbTableName, esc_table_name, sizeof(esc_table_name), conn->ccsc);
 		my_strcat(proc_query, " relname like '%.*s' and", esc_table_name, escTbnamelen);
 	}
+	if (conn->schema_support)
+		strcat(proc_query, " pg_namespace.oid = relnamespace and"); 
 	strcat(proc_query, " pg_user.usesysid = relowner"); 
 	if (res = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), !res)
 	{
@@ -4029,6 +4241,8 @@ mylog("guid=%s\n", uid);
 		}
 		reln = QR_get_value_backend_row(res, i, 0);
 		owner = QR_get_value_backend_row(res, i, 1);
+		if (conn->schema_support)
+			schnm = QR_get_value_backend_row(res, i, 3);
 		/* The owner has all privileges */
 		useracl_upd(useracl, allures, owner, ALL_PRIVILIGES);
 		for (j = 0; j < usercount; j++)
@@ -4051,7 +4265,10 @@ mylog("guid=%s\n", uid);
 				}
 				row = (TupleNode *) malloc(sizeof(TupleNode) + (7 - 1) *sizeof(TupleField));
 				set_tuplefield_string(&row->tuple[0], "");
-				set_tuplefield_string(&row->tuple[1], "");
+				if (conn->schema_support)
+					set_tuplefield_string(&row->tuple[1], GET_SCHEMA_NAME(schnm));
+				else
+					set_tuplefield_string(&row->tuple[1], "");
 				set_tuplefield_string(&row->tuple[2], reln);
 				if (su || sys)
 					set_tuplefield_string(&row->tuple[3], "_SYSTEM");
diff --git a/src/interfaces/odbc/info30.c b/src/interfaces/odbc/info30.c
index c1c2c63d066..f92c2ab6ff7 100644
--- a/src/interfaces/odbc/info30.c
+++ b/src/interfaces/odbc/info30.c
@@ -171,10 +171,10 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue,
 			break;
 		case SQL_CREATE_SCHEMA:
 			len = 4;
-			if (PG_VERSION_LE(conn, 7.2))
-				value = 0;
+			if (conn->schema_support)
+				value = SQL_CS_CREATE_SCHEMA | SQL_CS_AUTHORIZATION;
 			else
-				value = SQL_CS_CREATE_SCHEMA | SQL_CS_AUTHORIZATION; /* hopefully */
+				value = 0;
 			break;
 		case SQL_CREATE_TABLE:
 			len = 4;
@@ -218,10 +218,10 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue,
 			break;
 		case SQL_DROP_SCHEMA:
 			len = 4;
-			if (PG_VERSION_LE(conn, 7.2))
-				value = 0;
+			if (conn->schema_support)
+				value = SQL_DS_DROP_SCHEMA | SQL_DS_RESTRICT | SQL_DS_CASCADE;
 			else
-				value = SQL_DS_DROP_SCHEMA | SQL_DS_RESTRICT | SQL_DS_CASCADE; /* hopefully */
+				value = 0;
 			break;
 		case SQL_DROP_TABLE:
 			len = 4;
diff --git a/src/interfaces/odbc/misc.c b/src/interfaces/odbc/misc.c
index a7bcaca8d97..df999941759 100644
--- a/src/interfaces/odbc/misc.c
+++ b/src/interfaces/odbc/misc.c
@@ -279,6 +279,18 @@ my_strcat(char *buf, const char *fmt, const char *s, int len)
 	return NULL;
 }
 
+char *
+schema_strcat(char *buf, const char *fmt, const char *s, int len, const char *tbname, int tbnmlen)
+{
+	if (!s || 0 == len)
+	{
+		if (tbname && (tbnmlen > 0 || tbnmlen == SQL_NTS))
+			return my_strcat(buf, fmt, "public", 6);
+		return NULL;
+	}
+	return my_strcat(buf, fmt, s, len);
+}
+
 
 void
 remove_newlines(char *string)
diff --git a/src/interfaces/odbc/misc.h b/src/interfaces/odbc/misc.h
index cba7f2bfede..fe946eebfa1 100644
--- a/src/interfaces/odbc/misc.h
+++ b/src/interfaces/odbc/misc.h
@@ -89,6 +89,9 @@ char	   *strncpy_null(char *dst, const char *src, int len);
 char	   *trim(char *string);
 char	   *make_string(const char *s, int len, char *buf);
 char	   *my_strcat(char *buf, const char *fmt, const char *s, int len);
+char	   *schema_strcat(char *buf, const char *fmt, const char *s, int len,
+			const char *, int);
+#define	GET_SCHEMA_NAME(nspname) 	(stricmp(nspname, "public") ? nspname : "")
 
 /* defines for return value of my_strcpy */
 #define STRCPY_SUCCESS		1
diff --git a/src/interfaces/odbc/parse.c b/src/interfaces/odbc/parse.c
index d86e62f85bb..cb77fddd836 100644
--- a/src/interfaces/odbc/parse.c
+++ b/src/interfaces/odbc/parse.c
@@ -543,15 +543,21 @@ parse_statement(StatementClass *stmt)
 			 */
 			if (in_dot)
 			{
-				irdflds->nfields--;
-				strcpy(fi[irdflds->nfields]->dot, fi[irdflds->nfields]->name);
-				strcpy(fi[irdflds->nfields]->name, token);
-				irdflds->nfields++;
-				in_dot = FALSE;
+				int	ifld = irdflds->nfields - 1;
+
+				if (fi[ifld]->dot[0])
+				{
+					strcat(fi[ifld]->dot, ".");
+					strcat(fi[ifld]->dot, fi[ifld]->name);
+				}
+				else
+					strcpy(fi[ifld]->dot, fi[ifld]->name);
+				strcpy(fi[ifld]->name, token);
 
 				if (delim == ',')
 				{
 					mylog("in_dot: got comma\n");
+					in_dot = FALSE;
 					in_field = FALSE;
 				}
 				continue;
@@ -575,6 +581,7 @@ parse_statement(StatementClass *stmt)
 			/* Function */
 			if (token[0] == '(')
 			{
+				in_dot = FALSE;
 				in_func = TRUE;
 				blevel = 1;
 				fi[irdflds->nfields - 1]->func = TRUE;
@@ -594,6 +601,7 @@ parse_statement(StatementClass *stmt)
 				continue;
 			}
 
+			in_dot = FALSE;
 			if (!stricmp(token, "as"))
 			{
 				in_as = TRUE;
@@ -644,6 +652,7 @@ parse_statement(StatementClass *stmt)
 					return FALSE;
 				}
 
+				ti[stmt->ntab]->schema[0] = '\0';
 				ti[stmt->ntab]->alias[0] = '\0';
 
 				strcpy(ti[stmt->ntab]->name, token);
@@ -680,6 +689,7 @@ parse_statement(StatementClass *stmt)
 					in_table = TRUE;
 				}
 				stmt->ntab++;
+				in_dot = FALSE;
 				continue;
 			}
 
@@ -689,9 +699,21 @@ parse_statement(StatementClass *stmt)
 				out_table = TRUE;
 				continue;
 			}
-			if (in_table && stricmp(token, "as"))
+			if (in_table)
 			{
-				if (!dquote)
+				if (in_dot)
+				{
+					strcpy(ti[stmt->ntab - 1]->schema, ti[stmt->ntab - 1]->name);
+					strcpy(ti[stmt->ntab - 1]->name, token);
+					in_dot = FALSE;
+					continue;
+				}
+				if (strcmp(token, ".") == 0)
+				{
+					in_dot = TRUE;
+					continue;
+				}
+				if (!dquote && stricmp(token, "as"))
 				{
 					if (stricmp(token, "LEFT") == 0 ||
 					    stricmp(token, "RIGHT") == 0 ||
@@ -702,14 +724,14 @@ parse_statement(StatementClass *stmt)
 						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");
+					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 */
@@ -823,8 +845,8 @@ parse_statement(StatementClass *stmt)
 			col_stmt = (StatementClass *) hcol_stmt;
 			col_stmt->internal = TRUE;
 
-			result = PGAPI_Columns(hcol_stmt, "", 0, "", 0,
-						ti[i]->name, (SWORD) strlen(ti[i]->name), "", 0, PODBC_NOT_SEARCH_PATTERN);
+			result = PGAPI_Columns(hcol_stmt, "", 0, ti[i]->schema,
+					 SQL_NTS, ti[i]->name, SQL_NTS, "", 0, PODBC_NOT_SEARCH_PATTERN);
 
 			mylog("        Past PG_Columns\n");
 			if (result == SQL_SUCCESS)
diff --git a/src/interfaces/odbc/psqlodbc.h b/src/interfaces/odbc/psqlodbc.h
index 839c6e4876c..f9bb1581d99 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.62 2002/04/01 03:01:15 inoue Exp $
+ * $Id: psqlodbc.h,v 1.63 2002/04/02 10:50:49 inoue Exp $
  *
  */
 
diff --git a/src/interfaces/odbc/socket.c b/src/interfaces/odbc/socket.c
index 031a6779cba..7f563592747 100644
--- a/src/interfaces/odbc/socket.c
+++ b/src/interfaces/odbc/socket.c
@@ -107,7 +107,6 @@ char
 SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname)
 {
 	struct hostent *host;
-	struct sockaddr_in sadr;
 	unsigned long iaddr;
 
 	if (self->socket != -1)
@@ -117,7 +116,7 @@ SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname)
 		return 0;
 	}
 
-	memset((char *) &sadr, 0, sizeof(sadr));
+	memset((char *) &(self->sadr), 0, sizeof(self->sadr));
 
 	/*
 	 * If it is a valid IP address, use it. Otherwise use hostname lookup.
@@ -132,13 +131,13 @@ SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname)
 			self->errormsg = "Could not resolve hostname.";
 			return 0;
 		}
-		memcpy(&(sadr.sin_addr), host->h_addr, host->h_length);
+		memcpy(&(self->sadr.sin_addr), host->h_addr, host->h_length);
 	}
 	else
-		memcpy(&(sadr.sin_addr), (struct in_addr *) & iaddr, sizeof(iaddr));
+		memcpy(&(self->sadr.sin_addr), (struct in_addr *) & iaddr, sizeof(iaddr));
 
-	sadr.sin_family = AF_INET;
-	sadr.sin_port = htons(port);
+	self->sadr.sin_family = AF_INET;
+	self->sadr.sin_port = htons(port);
 
 	self->socket = socket(AF_INET, SOCK_STREAM, 0);
 	if (self->socket == -1)
@@ -148,8 +147,8 @@ SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname)
 		return 0;
 	}
 
-	if (connect(self->socket, (struct sockaddr *) & (sadr),
-				sizeof(sadr)) < 0)
+	if (connect(self->socket, (struct sockaddr *) & (self->sadr),
+				sizeof(self->sadr)) < 0)
 	{
 		self->errornumber = SOCKET_COULD_NOT_CONNECT;
 		self->errormsg = "Could not connect to remote socket.";
diff --git a/src/interfaces/odbc/socket.h b/src/interfaces/odbc/socket.h
index c49d9fe88d8..2337eb9a087 100644
--- a/src/interfaces/odbc/socket.h
+++ b/src/interfaces/odbc/socket.h
@@ -61,6 +61,7 @@ struct SocketClass_
 
 	char	   *errormsg;
 	int			errornumber;
+	struct sockaddr_in	sadr; /* Used for handling connections for cancel */
 
 	char		reverse;		/* used to handle Postgres 6.2 protocol
 								 * (reverse byte order) */
-- 
GitLab