/*-------
 * Module:			pgapi30.c
 *
 * Description:		This module contains routines related to ODBC 3.0
 *			most of their implementations are temporary
 *			and must be rewritten properly.
 *			2001/07/23	inoue
 *
 * Classes:			n/a
 *
 * API functions:	PGAPI_ColAttribute, PGAPI_GetDiagRec,
			PGAPI_GetConnectAttr, PGAPI_GetStmtAttr,
			PGAPI_SetConnectAttr, PGAPI_SetStmtAttr
 *-------
 */

#ifndef ODBCVER
#define ODBCVER 0x0300
#endif
#include "psqlodbc.h"
#include <stdio.h>
#include <string.h>

#include "environ.h"
#include "connection.h"
#include "statement.h"
#include "descriptor.h"
#include "qresult.h"
#include "pgapifunc.h"

static HSTMT statementHandleFromDescHandle(SQLHDESC, SQLINTEGER *descType); 
/*	SQLError -> SQLDiagRec */
RETCODE		SQL_API
PGAPI_GetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle,
		SQLSMALLINT RecNumber, SQLCHAR *Sqlstate,
		SQLINTEGER *NativeError, SQLCHAR *MessageText,
		SQLSMALLINT BufferLength, SQLSMALLINT *TextLength)
{
	RETCODE		ret;
	static const char *func = "PGAPI_GetDiagRec";

	mylog("%s entering rec=%d", func, RecNumber);
	switch (HandleType)
	{
		case SQL_HANDLE_ENV:
			ret = PGAPI_EnvError(Handle, RecNumber, Sqlstate,
					NativeError, MessageText,
					BufferLength, TextLength, 0);
			break;
		case SQL_HANDLE_DBC:
			ret = PGAPI_ConnectError(Handle, RecNumber, Sqlstate,
					NativeError, MessageText, BufferLength,
					TextLength, 0);
			break;
		case SQL_HANDLE_STMT:
			ret = PGAPI_StmtError(Handle, RecNumber, Sqlstate,
					NativeError, MessageText, BufferLength,
					TextLength, 0);
			break;
		case SQL_HANDLE_DESC:
			ret = PGAPI_StmtError(statementHandleFromDescHandle(Handle, NULL),
					RecNumber, Sqlstate, NativeError,
					MessageText, BufferLength,
					TextLength, 0);
			break;
		default:
			ret = SQL_ERROR;
	}
	mylog("%s exiting %d\n", func, ret);
	return ret;
}

/*
 *	Minimal implementation. 
 *
 */
RETCODE		SQL_API
PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle,
		SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier,
		PTR DiagInfoPtr, SQLSMALLINT BufferLength,
		SQLSMALLINT *StringLengthPtr)
{
	RETCODE		ret = SQL_ERROR, rtn;
	ConnectionClass	*conn;
	SQLHANDLE	stmtHandle;
	StatementClass	*stmt;
	SDWORD		rc;
	SWORD		pcbErrm;
	static const char *func = "PGAPI_GetDiagField";

	mylog("%s entering rec=%d", func, RecNumber);
	switch (HandleType)
	{
		case SQL_HANDLE_ENV:
			switch (DiagIdentifier)
			{
				case SQL_DIAG_CLASS_ORIGIN:
				case SQL_DIAG_SUBCLASS_ORIGIN:
				case SQL_DIAG_CONNECTION_NAME:
				case SQL_DIAG_SERVER_NAME:
					strcpy((char *) DiagInfoPtr, "");
					if (StringLengthPtr)
						*StringLengthPtr = 0;
					ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_MESSAGE_TEXT:
					ret = PGAPI_EnvError(Handle, RecNumber,
                        			NULL, NULL, DiagInfoPtr,
						BufferLength, StringLengthPtr, 0);  
					break;
				case SQL_DIAG_NATIVE:
					ret = PGAPI_EnvError(Handle, RecNumber,
                        			NULL, DiagInfoPtr, NULL,
						0, NULL, 0);  
					if (StringLengthPtr)  
						*StringLengthPtr = sizeof(SQLINTEGER);  
					if (SQL_SUCCESS_WITH_INFO == ret)  
						ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_NUMBER:
					ret = PGAPI_EnvError(Handle, RecNumber,
                        			NULL, NULL, NULL,
						0, NULL, 0);
					if (SQL_SUCCESS == ret ||
					    SQL_SUCCESS_WITH_INFO == ret)
					{
						*((SQLINTEGER *) DiagInfoPtr) = 1;
						if (StringLengthPtr)
							*StringLengthPtr = sizeof(SQLINTEGER);
						ret = SQL_SUCCESS;
					}
					break;
				case SQL_DIAG_SQLSTATE:
					ret = PGAPI_EnvError(Handle, RecNumber,
                        			DiagInfoPtr, NULL, NULL,
						0, NULL, 0);
					if (StringLengthPtr)  
						*StringLengthPtr = 5;  
					if (SQL_SUCCESS_WITH_INFO == ret)  
						ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_RETURNCODE: /* driver manager returns */
					break;
				case SQL_DIAG_CURSOR_ROW_COUNT:
				case SQL_DIAG_ROW_COUNT:
				case SQL_DIAG_DYNAMIC_FUNCTION:
				case SQL_DIAG_DYNAMIC_FUNCTION_CODE:
					/* options for statement type only */
					break;
			}
			break;
		case SQL_HANDLE_DBC:
			conn = (ConnectionClass *) Handle;
			switch (DiagIdentifier)
			{
				case SQL_DIAG_CLASS_ORIGIN:
				case SQL_DIAG_SUBCLASS_ORIGIN:
				case SQL_DIAG_CONNECTION_NAME:
					strcpy((char *) DiagInfoPtr, "");
					if (StringLengthPtr)
						*StringLengthPtr = 0;
					ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_SERVER_NAME:
					strcpy((SQLCHAR *) DiagInfoPtr, CC_get_DSN(conn));
					if (StringLengthPtr)
						*StringLengthPtr = strlen(CC_get_DSN(conn));
					ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_MESSAGE_TEXT:
					ret = PGAPI_ConnectError(Handle, RecNumber,
                        			NULL, NULL, DiagInfoPtr,
						BufferLength, StringLengthPtr, 0);  
					break;
				case SQL_DIAG_NATIVE:
					ret = PGAPI_ConnectError(Handle, RecNumber,
                        			NULL, DiagInfoPtr, NULL,
						0, NULL, 0);  
					if (StringLengthPtr)  
						*StringLengthPtr = sizeof(SQLINTEGER);  
					if (SQL_SUCCESS_WITH_INFO == ret)  
						ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_NUMBER:
					ret = PGAPI_ConnectError(Handle, RecNumber,
                        			NULL, NULL, NULL,
						0, NULL, 0);
					if (SQL_SUCCESS == ret ||
					    SQL_SUCCESS_WITH_INFO == ret)
					{
						*((SQLINTEGER *) DiagInfoPtr) = 1;
						if (StringLengthPtr)
							*StringLengthPtr = sizeof(SQLINTEGER);
						ret = SQL_SUCCESS;
					}
					break;  
				case SQL_DIAG_SQLSTATE:
					ret = PGAPI_ConnectError(Handle, RecNumber,
                        			DiagInfoPtr, NULL, NULL,
						0, NULL, 0);
					if (StringLengthPtr)  
						*StringLengthPtr = 5;  
					if (SQL_SUCCESS_WITH_INFO == ret)  
						ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_RETURNCODE: /* driver manager returns */
					break;
				case SQL_DIAG_CURSOR_ROW_COUNT:
				case SQL_DIAG_ROW_COUNT:
				case SQL_DIAG_DYNAMIC_FUNCTION:
				case SQL_DIAG_DYNAMIC_FUNCTION_CODE:
					/* options for statement type only */
					break;
			}
			break;
		case SQL_HANDLE_STMT:
			conn = (ConnectionClass *) SC_get_conn(((StatementClass *) Handle));
			switch (DiagIdentifier)
			{
				case SQL_DIAG_CLASS_ORIGIN:
				case SQL_DIAG_SUBCLASS_ORIGIN:
				case SQL_DIAG_CONNECTION_NAME:
					strcpy((char *) DiagInfoPtr, "");
					if (StringLengthPtr)
						*StringLengthPtr = 0;
					ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_SERVER_NAME:
					strcpy((SQLCHAR *) DiagInfoPtr, CC_get_DSN(conn));
					if (StringLengthPtr)
						*StringLengthPtr = strlen(CC_get_DSN(conn));
					ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_MESSAGE_TEXT:
					ret = PGAPI_StmtError(Handle, RecNumber,
                        			NULL, NULL, DiagInfoPtr,
						BufferLength, StringLengthPtr, 0);  
					break;
				case SQL_DIAG_NATIVE:
					ret = PGAPI_StmtError(Handle, RecNumber,
                        			NULL, DiagInfoPtr, NULL,
						0, NULL, 0);  
					if (StringLengthPtr)  
						*StringLengthPtr = sizeof(SQLINTEGER);  
					if (SQL_SUCCESS_WITH_INFO == ret)  
						ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_NUMBER:
					*((SQLINTEGER *) DiagInfoPtr) = 0;
					ret = SQL_NO_DATA_FOUND;
					stmt = (StatementClass *) Handle;
					do
					{
						rtn = PGAPI_StmtError(Handle, RecNumber,
                        				NULL, NULL, NULL,
							0, &pcbErrm, 0);
						if (SQL_SUCCESS == rtn ||
					    	    SQL_SUCCESS_WITH_INFO == rtn)
						{
							*((SQLINTEGER *) DiagInfoPtr)++;
							ret = SQL_SUCCESS;
						}
					} while (pcbErrm >= stmt->error_recsize);
					if (StringLengthPtr)
						*StringLengthPtr = sizeof(SQLINTEGER);
					break;
				case SQL_DIAG_SQLSTATE:
					ret = PGAPI_StmtError(Handle, RecNumber,
                        			DiagInfoPtr, NULL, NULL,
						0, NULL, 0);
					if (StringLengthPtr)  
						*StringLengthPtr = 5;
					if (SQL_SUCCESS_WITH_INFO == ret)  
						ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_CURSOR_ROW_COUNT:
					stmt = (StatementClass *) Handle;
					rc = -1;
					if (stmt->status == STMT_FINISHED)
					{
						QResultClass *res = SC_get_Curres(stmt);

						if (res && QR_NumResultCols(res) > 0 && !SC_is_fetchcursor(stmt))
							rc = QR_get_num_total_tuples(res) - res->dl_count;
					} 
					*((SQLINTEGER *) DiagInfoPtr) = rc;
					if (StringLengthPtr)
						*StringLengthPtr = sizeof(SQLINTEGER);
					ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_ROW_COUNT:
					stmt = (StatementClass *) Handle;
					*((SQLINTEGER *) DiagInfoPtr) = stmt->diag_row_count;
					if (StringLengthPtr)
						*StringLengthPtr = sizeof(SQLINTEGER);
					ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_RETURNCODE: /* driver manager returns */
					break;
			}
			break;
		case SQL_HANDLE_DESC:
			stmtHandle = statementHandleFromDescHandle(Handle, NULL); 
			conn = (ConnectionClass *) SC_get_conn(((StatementClass *) stmtHandle)); 
			switch (DiagIdentifier)
			{
				case SQL_DIAG_CLASS_ORIGIN:
				case SQL_DIAG_SUBCLASS_ORIGIN:
				case SQL_DIAG_CONNECTION_NAME:
					strcpy((char *) DiagInfoPtr, "");
					if (StringLengthPtr)
						*StringLengthPtr = 0;
					ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_SERVER_NAME:
					strcpy((SQLCHAR *) DiagInfoPtr, CC_get_DSN(conn));
					if (StringLengthPtr)
						*StringLengthPtr = strlen(CC_get_DSN(conn));
					ret = SQL_SUCCESS;
					break;
				case SQL_DIAG_MESSAGE_TEXT:
				case SQL_DIAG_NATIVE:
				case SQL_DIAG_NUMBER:
				case SQL_DIAG_SQLSTATE:
					ret = PGAPI_GetDiagField(SQL_HANDLE_STMT,
						stmtHandle, RecNumber,
						DiagIdentifier, DiagInfoPtr,
						BufferLength, StringLengthPtr);
					break;
				case SQL_DIAG_RETURNCODE: /* driver manager returns */
					break;
				case SQL_DIAG_CURSOR_ROW_COUNT:
				case SQL_DIAG_ROW_COUNT:
				case SQL_DIAG_DYNAMIC_FUNCTION:
				case SQL_DIAG_DYNAMIC_FUNCTION_CODE:
					/* options for statement type only */
					break;
			}
			break;
		default:
			ret = SQL_ERROR;
	}
	mylog("%s exiting %d\n", func, ret);
	return ret;
}

/*	SQLGetConnectOption -> SQLGetconnectAttr */
RETCODE		SQL_API
PGAPI_GetConnectAttr(HDBC ConnectionHandle,
			SQLINTEGER Attribute, PTR Value,
			SQLINTEGER BufferLength, SQLINTEGER *StringLength)
{
	static const char *func = "PGAPI_GetConnectAttr";
	ConnectionClass *conn = (ConnectionClass *) ConnectionHandle;
	RETCODE	ret = SQL_SUCCESS;
	SQLINTEGER	len = 4;

	mylog("PGAPI_GetConnectAttr %d\n", Attribute);
	switch (Attribute)
	{
		case SQL_ATTR_ASYNC_ENABLE:
			*((SQLUINTEGER *) Value) = SQL_ASYNC_ENABLE_OFF;
			break;
		case SQL_ATTR_AUTO_IPD:
			*((SQLUINTEGER *) Value) = SQL_FALSE;
			break;
		case SQL_ATTR_CONNECTION_DEAD:
			*((SQLUINTEGER *) Value) = (conn->status == CONN_NOT_CONNECTED || conn->status == CONN_DOWN);
			break;
		case SQL_ATTR_CONNECTION_TIMEOUT:
			*((SQLUINTEGER *) Value) = 0;
			break;
		case SQL_ATTR_METADATA_ID:
			conn->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
			conn->errormsg = "Unsupported connect attribute (Get)";
			CC_log_error(func, "", conn);
			return SQL_ERROR;
		default:
			ret = PGAPI_GetConnectOption(ConnectionHandle, (UWORD) Attribute, Value);
	}
	if (StringLength)
		*StringLength = len;
	return ret;
}

static SQLHDESC
descHandleFromStatementHandle(HSTMT StatementHandle, SQLINTEGER descType) 
{
	switch (descType)
	{
		case SQL_ATTR_APP_ROW_DESC:		/* 10010 */
			return StatementHandle;	/* this is bogus */
		case SQL_ATTR_APP_PARAM_DESC:	/* 10011 */
			return (HSTMT) ((SQLUINTEGER) StatementHandle + 1) ; /* this is bogus */
		case SQL_ATTR_IMP_ROW_DESC:		/* 10012 */
			return (HSTMT) ((SQLUINTEGER) StatementHandle + 2); /* this is bogus */
		case SQL_ATTR_IMP_PARAM_DESC:	/* 10013 */
			return (HSTMT) ((SQLUINTEGER) StatementHandle + 3); /* this is bogus */
	}
	return (HSTMT) 0;
}
static HSTMT
statementHandleFromDescHandle(SQLHDESC DescHandle, SQLINTEGER *descType) 
{
	SQLUINTEGER res = (SQLUINTEGER) DescHandle % 4;
	if (descType)
	{
		switch (res)
		{
			case 0: *descType = SQL_ATTR_APP_ROW_DESC; /* 10010 */
				break;
			case 1: *descType = SQL_ATTR_APP_PARAM_DESC; /* 10011 */
				break;
			case 2: *descType = SQL_ATTR_IMP_ROW_DESC; /* 10012 */
				break;
			case 3: *descType = SQL_ATTR_IMP_PARAM_DESC; /* 10013 */
				break;
		}
	}
	return (HSTMT) ((SQLUINTEGER) DescHandle - res);
}

void	Desc_set_error(SQLHDESC hdesc, int errornumber, const char *errormsg)
{
	SQLINTEGER	descType;
	HSTMT	hstmt = statementHandleFromDescHandle(hdesc, &descType);
	StatementClass	*stmt;

	if (!hstmt)
		return;
	stmt = (StatementClass *) hstmt;
	stmt->errornumber = errornumber;
	stmt->errormsg = errormsg; /* should be static */
}

static  void column_bindings_set(ARDFields *opts, int cols, BOOL maxset)
{
	int	i;

	if (cols == opts->allocated)
		return;
	if (cols > opts->allocated)
	{
		extend_column_bindings(opts, cols);
		return;
	}
	if (maxset)	return;

	for (i = opts->allocated; i > cols; i--)
		reset_a_column_binding(opts, i);
	opts->allocated = cols;
	if (0 == cols)
	{
		free(opts->bindings);
		opts->bindings = NULL;
	}
}

static RETCODE SQL_API
ARDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
		SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength)
{
	RETCODE		ret = SQL_SUCCESS;
	PTR		tptr;
	ARDFields	*opts = SC_get_ARD(stmt);
	switch (FieldIdentifier)
	{
		case SQL_DESC_ARRAY_SIZE:
			opts->rowset_size = (SQLUINTEGER) Value;
			break; 
		case SQL_DESC_ARRAY_STATUS_PTR:
			opts->row_operation_ptr = Value;
			break;
		case SQL_DESC_BIND_OFFSET_PTR:
			opts->row_offset_ptr = Value;
			break;
		case SQL_DESC_BIND_TYPE:
			opts->bind_size = (SQLUINTEGER) Value;
			break;

		case SQL_DESC_TYPE:
			column_bindings_set(opts, RecNumber, TRUE);
			reset_a_column_binding(opts, RecNumber);
			opts->bindings[RecNumber - 1].returntype = (Int4) Value;
			break;
		case SQL_DESC_DATETIME_INTERVAL_CODE:
			column_bindings_set(opts, RecNumber, TRUE);
			switch (opts->bindings[RecNumber - 1].returntype)
			{
				case SQL_DATETIME:
				case SQL_C_TYPE_DATE:
				case SQL_C_TYPE_TIME:
				case SQL_C_TYPE_TIMESTAMP:
				switch ((Int4) Value)
				{
					case SQL_CODE_DATE:
						opts->bindings[RecNumber - 1].returntype = SQL_C_TYPE_DATE;
						break;
					case SQL_CODE_TIME:
						opts->bindings[RecNumber - 1].returntype = SQL_C_TYPE_TIME;
						break;
					case SQL_CODE_TIMESTAMP:
						opts->bindings[RecNumber - 1].returntype = SQL_C_TYPE_TIMESTAMP;
						break;
				}
				break;
			}
			break;
		case SQL_DESC_CONCISE_TYPE:
			column_bindings_set(opts, RecNumber, TRUE);
			opts->bindings[RecNumber - 1].returntype = (Int4) Value;
			break;
		case SQL_DESC_DATA_PTR:
			if (!RecNumber)
				opts->bookmark->buffer = Value;
			else
			{
				column_bindings_set(opts, RecNumber, TRUE);
				opts->bindings[RecNumber - 1].buffer = Value;
			}
			break;
		case SQL_DESC_INDICATOR_PTR:
			if (!RecNumber)
				tptr = opts->bookmark->used;
			else
			{
				column_bindings_set(opts, RecNumber, TRUE);
				tptr = opts->bindings[RecNumber - 1].used;
			}
			if (Value != tptr)
			{
				ret = SQL_ERROR;
				stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
				stmt->errormsg = "INDICATOR != OCTET_LENGTH_PTR"; 
			}
			break;
		case SQL_DESC_OCTET_LENGTH_PTR:
			if (!RecNumber)
				opts->bookmark->used = Value;
			else
			{
				column_bindings_set(opts, RecNumber, TRUE);
				opts->bindings[RecNumber - 1].used = Value;
			}
			break;
		case SQL_DESC_COUNT:
			column_bindings_set(opts, (SQLUINTEGER) Value, FALSE);
			break;
		case SQL_DESC_OCTET_LENGTH:
			if (RecNumber)
			{
				column_bindings_set(opts, RecNumber, TRUE);
				opts->bindings[RecNumber - 1].buflen = (Int4) Value;
			}
			break;
		case SQL_DESC_PRECISION:
			if (RecNumber)
			{
				column_bindings_set(opts, RecNumber, TRUE);
				opts->bindings[RecNumber - 1].precision = (Int2) Value;
			}
			break;
		case SQL_DESC_SCALE:
			if (RecNumber)
			{
				column_bindings_set(opts, RecNumber, TRUE);
				opts->bindings[RecNumber - 1].scale = (Int4) Value;
			}
			break;
		case SQL_DESC_ALLOC_TYPE: /* read-only */
		case SQL_DESC_DATETIME_INTERVAL_PRECISION:
		case SQL_DESC_LENGTH:
		case SQL_DESC_NUM_PREC_RADIX:
		default:ret = SQL_ERROR;
			stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
	}
	return ret;
}

static  void parameter_bindings_set(APDFields *opts, int params, BOOL maxset)
{
	int	i;

	if (params == opts->allocated)
		return;
	if (params > opts->allocated)
	{
		extend_parameter_bindings(opts, params);
		return;
	}
	if (maxset)	return;

	for (i = opts->allocated; i > params; i--)
		reset_a_parameter_binding(opts, i);
	opts->allocated = params;
	if (0 == params)
	{
		free(opts->parameters);
		opts->parameters = NULL;
	}
}

static RETCODE SQL_API
APDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
		SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength)
{
	RETCODE		ret = SQL_SUCCESS;
	APDFields	*opts = SC_get_APD(stmt);
	switch (FieldIdentifier)
	{
		case SQL_DESC_ARRAY_SIZE:
			opts->paramset_size = (SQLUINTEGER) Value;
			break; 
		case SQL_DESC_ARRAY_STATUS_PTR:
			opts->param_operation_ptr = Value;
			break;
		case SQL_DESC_BIND_OFFSET_PTR:
			opts->param_offset_ptr = Value;
			break;
		case SQL_DESC_BIND_TYPE:
			opts->param_bind_type = (SQLUINTEGER) Value;
			break;

		case SQL_DESC_TYPE:
			parameter_bindings_set(opts, RecNumber, TRUE);
			reset_a_parameter_binding(opts, RecNumber);
			opts->parameters[RecNumber - 1].CType = (Int4) Value;
			break;
		case SQL_DESC_DATETIME_INTERVAL_CODE:
			parameter_bindings_set(opts, RecNumber, TRUE);
			switch (opts->parameters[RecNumber - 1].CType)
			{
				case SQL_DATETIME:
				case SQL_C_TYPE_DATE:
				case SQL_C_TYPE_TIME:
				case SQL_C_TYPE_TIMESTAMP:
				switch ((Int4) Value)
				{
					case SQL_CODE_DATE:
						opts->parameters[RecNumber - 1].CType = SQL_C_TYPE_DATE;
						break;
					case SQL_CODE_TIME:
						opts->parameters[RecNumber - 1].CType = SQL_C_TYPE_TIME;
						break;
					case SQL_CODE_TIMESTAMP:
						opts->parameters[RecNumber - 1].CType = SQL_C_TYPE_TIMESTAMP;
						break;
				}
				break;
			}
			break;
		case SQL_DESC_CONCISE_TYPE:
			parameter_bindings_set(opts, RecNumber, TRUE);
			opts->parameters[RecNumber - 1].CType = (Int4) Value;
			break;
		case SQL_DESC_DATA_PTR:
			parameter_bindings_set(opts, RecNumber, TRUE);
			opts->parameters[RecNumber - 1].buffer = Value;
			break;
		case SQL_DESC_INDICATOR_PTR:
			if (opts->allocated < RecNumber ||
			    Value != opts->parameters[RecNumber - 1].used)
			{
				ret = SQL_ERROR;
				stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
				stmt->errormsg = "INDICATOR != OCTET_LENGTH_PTR"; 
			}
			break;
		case SQL_DESC_OCTET_LENGTH:
			parameter_bindings_set(opts, RecNumber, TRUE);
			opts->parameters[RecNumber - 1].buflen = (Int4) Value;
			break;
		case SQL_DESC_OCTET_LENGTH_PTR:
			parameter_bindings_set(opts, RecNumber, TRUE);
			opts->parameters[RecNumber - 1].used = Value;
			break;
		case SQL_DESC_COUNT:
			parameter_bindings_set(opts, (SQLUINTEGER) Value, FALSE);
			break; 
		case SQL_DESC_PRECISION:
			parameter_bindings_set(opts, RecNumber, TRUE);
			opts->parameters[RecNumber - 1].precision = (Int2) Value;
			break;
		case SQL_DESC_SCALE:
			parameter_bindings_set(opts, RecNumber, TRUE);
			opts->parameters[RecNumber - 1].scale = (Int2) Value;
			break;
		case SQL_DESC_ALLOC_TYPE: /* read-only */
		case SQL_DESC_DATETIME_INTERVAL_PRECISION:
		case SQL_DESC_LENGTH:
		case SQL_DESC_NUM_PREC_RADIX:
		default:ret = SQL_ERROR;
			stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
	}
	return ret;
}

static RETCODE SQL_API
IRDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
		SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength)
{
	RETCODE		ret = SQL_SUCCESS;
	IRDFields	*opts = SC_get_IRD(stmt);
	switch (FieldIdentifier)
	{
		case SQL_DESC_ARRAY_STATUS_PTR:
			opts->rowStatusArray = (SQLUSMALLINT *) Value;
			break;
		case SQL_DESC_ROWS_PROCESSED_PTR:
			opts->rowsFetched = (SQLUINTEGER *) Value;
			break;
		case SQL_DESC_ALLOC_TYPE: /* read-only */
		case SQL_DESC_COUNT: /* read-only */
		case SQL_DESC_AUTO_UNIQUE_VALUE: /* read-only */
		case SQL_DESC_BASE_COLUMN_NAME: /* read-only */
		case SQL_DESC_BASE_TABLE_NAME: /* read-only */
		case SQL_DESC_CASE_SENSITIVE: /* read-only */
		case SQL_DESC_CATALOG_NAME: /* read-only */
		case SQL_DESC_CONCISE_TYPE: /* read-only */
		case SQL_DESC_DATETIME_INTERVAL_CODE: /* read-only */
		case SQL_DESC_DATETIME_INTERVAL_PRECISION: /* read-only */
		case SQL_DESC_DISPLAY_SIZE: /* read-only */
		case SQL_DESC_FIXED_PREC_SCALE: /* read-only */
		case SQL_DESC_LABEL: /* read-only */
		case SQL_DESC_LENGTH: /* read-only */
		case SQL_DESC_LITERAL_PREFIX: /* read-only */
		case SQL_DESC_LITERAL_SUFFIX: /* read-only */
		case SQL_DESC_LOCAL_TYPE_NAME: /* read-only */
		case SQL_DESC_NAME: /* read-only */
		case SQL_DESC_NULLABLE: /* read-only */
		case SQL_DESC_NUM_PREC_RADIX: /* read-only */
		case SQL_DESC_OCTET_LENGTH: /* read-only */
		case SQL_DESC_PRECISION: /* read-only */
#if (ODBCVER >= 0x0350)
		case SQL_DESC_ROWVER: /* read-only */
#endif /* ODBCVER */
		case SQL_DESC_SCALE: /* read-only */
		case SQL_DESC_SCHEMA_NAME: /* read-only */
		case SQL_DESC_SEARCHABLE: /* read-only */
		case SQL_DESC_TABLE_NAME: /* read-only */
		case SQL_DESC_TYPE: /* read-only */
		case SQL_DESC_TYPE_NAME: /* read-only */
		case SQL_DESC_UNNAMED: /* read-only */
		case SQL_DESC_UNSIGNED: /* read-only */
		case SQL_DESC_UPDATABLE: /* read-only */
		default:ret = SQL_ERROR;
			stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
	}
	return ret;
}

static RETCODE SQL_API
IPDSetField(StatementClass *stmt, SQLSMALLINT RecNumber,
		SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength)
{
	RETCODE		ret = SQL_SUCCESS;
	IPDFields	*ipdopts = SC_get_IPD(stmt);
	APDFields	*apdopts = SC_get_APD(stmt);

	switch (FieldIdentifier)
	{
		case SQL_DESC_ARRAY_STATUS_PTR:
			ipdopts->param_status_ptr = (SQLUSMALLINT *) Value;
			break;
		case SQL_DESC_ROWS_PROCESSED_PTR:
			ipdopts->param_processed_ptr = (SQLUINTEGER *) Value;
			break;
		case SQL_DESC_UNNAMED: /* only SQL_UNNAMED is allowed */ 
			if (SQL_UNNAMED !=  (SQLUINTEGER) Value)
			{
				ret = SQL_ERROR;
				stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER;
			}
			break;
		case SQL_DESC_TYPE:
			parameter_bindings_set(apdopts, RecNumber, TRUE);
			apdopts->parameters[RecNumber - 1].SQLType = (Int4) Value;
			break;
		case SQL_DESC_DATETIME_INTERVAL_CODE:
			parameter_bindings_set(apdopts, RecNumber, TRUE);
			switch (apdopts->parameters[RecNumber - 1].SQLType)
			{
				case SQL_DATETIME:
				case SQL_TYPE_DATE:
				case SQL_TYPE_TIME:
				case SQL_TYPE_TIMESTAMP:
				switch ((Int4) Value)
				{
					case SQL_CODE_DATE:
						apdopts->parameters[RecNumber - 1].SQLType = SQL_TYPE_DATE;
						break;
					case SQL_CODE_TIME:
						apdopts->parameters[RecNumber - 1].SQLType = SQL_TYPE_TIME;
						break;
					case SQL_CODE_TIMESTAMP:
						apdopts->parameters[RecNumber - 1].SQLType = SQL_TYPE_TIMESTAMP;
						break;
				}
				break;
			}
			break;
		case SQL_DESC_CONCISE_TYPE:
			parameter_bindings_set(apdopts, RecNumber, TRUE);
			apdopts->parameters[RecNumber - 1].SQLType = (Int4) Value;
			break;
		case SQL_DESC_COUNT:
			parameter_bindings_set(apdopts, (SQLUINTEGER) Value, FALSE);
			break; 
		case SQL_DESC_PARAMETER_TYPE:
			apdopts->parameters[RecNumber - 1].paramType = (Int2) Value;
			break;
		case SQL_DESC_SCALE:
			apdopts->parameters[RecNumber - 1].decimal_digits = (Int2) Value;
			break;
		case SQL_DESC_ALLOC_TYPE: /* read-only */ 
		case SQL_DESC_CASE_SENSITIVE: /* read-only */
		case SQL_DESC_DATETIME_INTERVAL_PRECISION:
		case SQL_DESC_FIXED_PREC_SCALE: /* read-only */
		case SQL_DESC_LENGTH:
		case SQL_DESC_LOCAL_TYPE_NAME: /* read-only */
		case SQL_DESC_NAME:
		case SQL_DESC_NULLABLE: /* read-only */
		case SQL_DESC_NUM_PREC_RADIX:
		case SQL_DESC_OCTET_LENGTH:
		case SQL_DESC_PRECISION:
#if (ODBCVER >= 0x0350)
		case SQL_DESC_ROWVER: /* read-only */
#endif /* ODBCVER */
		case SQL_DESC_TYPE_NAME: /* read-only */
		case SQL_DESC_UNSIGNED: /* read-only */
		default:ret = SQL_ERROR;
			stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
	}
	return ret;
}


static RETCODE SQL_API
ARDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
		SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength,
		SQLINTEGER *StringLength)
{
	RETCODE		ret = SQL_SUCCESS;
	SQLINTEGER	len, ival, rettype = 0;
	PTR		ptr = NULL;
	const ARDFields	*opts = SC_get_ARD(stmt);

	len = 4;
	switch (FieldIdentifier)
	{
		case SQL_DESC_ARRAY_SIZE:
			ival = opts->rowset_size;
			break; 
		case SQL_DESC_ARRAY_STATUS_PTR:
			rettype = SQL_IS_POINTER;
			ptr = opts->row_operation_ptr;
			break;
		case SQL_DESC_BIND_OFFSET_PTR:
			rettype = SQL_IS_POINTER;
			ptr = opts->row_offset_ptr;
			break;
		case SQL_DESC_BIND_TYPE:
			ival = opts->bind_size;
			break;
		case SQL_DESC_TYPE:
			switch (opts->bindings[RecNumber - 1].returntype)
			{
				case SQL_C_TYPE_DATE:
				case SQL_C_TYPE_TIME:
				case SQL_C_TYPE_TIMESTAMP:
					ival = SQL_DATETIME;
					break;
				default:
					ival = opts->bindings[RecNumber - 1].returntype;
			}
			break;
		case SQL_DESC_DATETIME_INTERVAL_CODE:
			switch (opts->bindings[RecNumber - 1].returntype)
			{
				case SQL_C_TYPE_DATE:
					ival = SQL_CODE_DATE;
					break;
				case SQL_C_TYPE_TIME:
					ival = SQL_CODE_TIME;
					break;
				case SQL_C_TYPE_TIMESTAMP:
					ival = SQL_CODE_TIMESTAMP;
					break;
				default:
					ival = 0;
					break;
			}
			break;
		case SQL_DESC_CONCISE_TYPE:
			ival = opts->bindings[RecNumber - 1].returntype;
			break;
		case SQL_DESC_DATA_PTR:
			rettype = SQL_IS_POINTER;
			if (!RecNumber)
				ptr = opts->bookmark->buffer;
			else
			{
				ptr = opts->bindings[RecNumber - 1].buffer;
			}
			break;
		case SQL_DESC_INDICATOR_PTR:
			rettype = SQL_IS_POINTER;
			if (!RecNumber)
				ptr = opts->bookmark->used;
			else
			{
				ptr = opts->bindings[RecNumber - 1].used;
			}
			break;
		case SQL_DESC_OCTET_LENGTH_PTR:
			rettype = SQL_IS_POINTER;
			if (!RecNumber)
				ptr = opts->bookmark->used;
			else
			{
				ptr = opts->bindings[RecNumber - 1].used;
			}
			break;
		case SQL_DESC_COUNT:
			ival = opts->allocated;
			break;
		case SQL_DESC_OCTET_LENGTH:
			if (RecNumber)
			{
				ival = opts->bindings[RecNumber - 1].buflen;
			}
			break;
		case SQL_DESC_ALLOC_TYPE: /* read-only */
			ival = SQL_DESC_ALLOC_AUTO;
			break;
		case SQL_DESC_PRECISION:
			if (RecNumber)
			{
				ival = opts->bindings[RecNumber - 1].precision;
			}
			break;
		case SQL_DESC_SCALE:
			if (RecNumber)
			{
				ival = opts->bindings[RecNumber - 1].scale;
			}
			break;
		case SQL_DESC_NUM_PREC_RADIX:
			ival = 10;
			break;
		case SQL_DESC_DATETIME_INTERVAL_PRECISION:
		case SQL_DESC_LENGTH:
		default:ret = SQL_ERROR;
			stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
	}
	switch (rettype)
	{
		case 0:
		case SQL_IS_INTEGER:
			len = 4;
			*((SQLINTEGER *) Value) = ival;
			break;
		case SQL_IS_POINTER:
			len = 4;
			*((void **) Value) = ptr;
			break;
	}
			
	if (StringLength)
		*StringLength = len;
	return ret;
}

static RETCODE SQL_API
APDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
		SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength,
		SQLINTEGER *StringLength)
{
	RETCODE		ret = SQL_SUCCESS;
	SQLINTEGER	ival = 0, len, rettype = 0;
	PTR		ptr = NULL;
	const APDFields	*opts = SC_get_APD(stmt);

	len = 4;
	switch (FieldIdentifier)
	{
		case SQL_DESC_ARRAY_SIZE:
			rettype = SQL_IS_POINTER;
			ival = opts->paramset_size;
			break; 
		case SQL_DESC_ARRAY_STATUS_PTR:
			rettype = SQL_IS_POINTER;
			ptr = opts->param_operation_ptr;
			break;
		case SQL_DESC_BIND_OFFSET_PTR:
			rettype = SQL_IS_POINTER;
			ptr = opts->param_offset_ptr;
			break;
		case SQL_DESC_BIND_TYPE:
			ival = opts->param_bind_type;
			break;

		case SQL_DESC_TYPE:
			switch (opts->parameters[RecNumber - 1].CType)
			{
				case SQL_C_TYPE_DATE:
				case SQL_C_TYPE_TIME:
				case SQL_C_TYPE_TIMESTAMP:
					ival = SQL_DATETIME;
					break;
				default:
					ival = opts->parameters[RecNumber - 1].CType;
			}
			break;
		case SQL_DESC_DATETIME_INTERVAL_CODE:
			switch (opts->parameters[RecNumber - 1].CType)
			{
				case SQL_C_TYPE_DATE:
					ival = SQL_CODE_DATE;
					break;
				case SQL_C_TYPE_TIME:
					ival = SQL_CODE_TIME;
					break;
				case SQL_C_TYPE_TIMESTAMP:
					ival = SQL_CODE_TIMESTAMP;
					break;
				default:
					ival = 0;
					break;
			}
			break;
		case SQL_DESC_CONCISE_TYPE:
			ival = opts->parameters[RecNumber - 1].CType;
			break;
		case SQL_DESC_DATA_PTR:
			rettype = SQL_IS_POINTER;
			ptr = opts->parameters[RecNumber - 1].buffer;
			break;
		case SQL_DESC_INDICATOR_PTR:
			rettype = SQL_IS_POINTER;
			ptr = opts->parameters[RecNumber - 1].used;
			break;
		case SQL_DESC_OCTET_LENGTH:
			ival = opts->parameters[RecNumber - 1].buflen;
			break;
		case SQL_DESC_OCTET_LENGTH_PTR:
			rettype = SQL_IS_POINTER;
			ptr = opts->parameters[RecNumber - 1].used;
			break;
		case SQL_DESC_COUNT:
			ival = opts->allocated;
			break; 
		case SQL_DESC_ALLOC_TYPE: /* read-only */
			ival = SQL_DESC_ALLOC_AUTO;
			break;
		case SQL_DESC_NUM_PREC_RADIX:
			ival = 10;
			break;
		case SQL_DESC_PRECISION:
			ival = opts->parameters[RecNumber - 1].precision;
			break;
		case SQL_DESC_SCALE:
			ival = opts->parameters[RecNumber - 1].scale;
			break;
		case SQL_DESC_DATETIME_INTERVAL_PRECISION:
		case SQL_DESC_LENGTH:
		default:ret = SQL_ERROR;
			stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
	}
	switch (rettype)
	{
		case 0:
		case SQL_IS_INTEGER:
			len = 4;
			*((Int4 *) Value) = ival;
			break;
		case SQL_IS_POINTER:
			len = 4;
			*((void **) Value) = ptr;
			break;
	}
			
	if (StringLength)
		*StringLength = len;
	return ret;
}

static RETCODE SQL_API
IRDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
		SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength,
		SQLINTEGER *StringLength)
{
	RETCODE		ret = SQL_SUCCESS;
	SQLINTEGER	ival = 0, len, rettype = 0;
	PTR		ptr = NULL;
	BOOL		bCallColAtt = FALSE;
	const IRDFields	*opts = SC_get_IRD(stmt);

	switch (FieldIdentifier)
	{
		case SQL_DESC_ARRAY_STATUS_PTR:
			rettype = SQL_IS_POINTER;
			ptr = opts->rowStatusArray;
			break;
		case SQL_DESC_ROWS_PROCESSED_PTR:
			rettype = SQL_IS_POINTER;
			ptr = opts->rowsFetched;
			break;
		case SQL_DESC_ALLOC_TYPE: /* read-only */
			ival = SQL_DESC_ALLOC_AUTO;
			break;
		case SQL_DESC_COUNT: /* read-only */
		case SQL_DESC_AUTO_UNIQUE_VALUE: /* read-only */
		case SQL_DESC_CASE_SENSITIVE: /* read-only */
		case SQL_DESC_CONCISE_TYPE: /* read-only */
		case SQL_DESC_DATETIME_INTERVAL_CODE: /* read-only */
		case SQL_DESC_DATETIME_INTERVAL_PRECISION: /* read-only */
		case SQL_DESC_DISPLAY_SIZE: /* read-only */
		case SQL_DESC_FIXED_PREC_SCALE: /* read-only */
		case SQL_DESC_LENGTH: /* read-only */
		case SQL_DESC_NULLABLE: /* read-only */
		case SQL_DESC_NUM_PREC_RADIX: /* read-only */
		case SQL_DESC_OCTET_LENGTH: /* read-only */
		case SQL_DESC_PRECISION: /* read-only */
#if (ODBCVER >= 0x0350)
		case SQL_DESC_ROWVER: /* read-only */
#endif /* ODBCVER */
		case SQL_DESC_SCALE: /* read-only */
		case SQL_DESC_SEARCHABLE: /* read-only */
		case SQL_DESC_TYPE: /* read-only */
		case SQL_DESC_UNNAMED: /* read-only */
		case SQL_DESC_UNSIGNED: /* read-only */
		case SQL_DESC_UPDATABLE: /* read-only */
			bCallColAtt = TRUE;
			break;
		case SQL_DESC_BASE_COLUMN_NAME: /* read-only */
		case SQL_DESC_BASE_TABLE_NAME: /* read-only */
		case SQL_DESC_CATALOG_NAME: /* read-only */
		case SQL_DESC_LABEL: /* read-only */
		case SQL_DESC_LITERAL_PREFIX: /* read-only */
		case SQL_DESC_LITERAL_SUFFIX: /* read-only */
		case SQL_DESC_LOCAL_TYPE_NAME: /* read-only */
		case SQL_DESC_NAME: /* read-only */
		case SQL_DESC_SCHEMA_NAME: /* read-only */
		case SQL_DESC_TABLE_NAME: /* read-only */
		case SQL_DESC_TYPE_NAME: /* read-only */
			rettype = SQL_NTS;
			bCallColAtt = TRUE;
			break; 
		default:ret = SQL_ERROR;
			stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
	}
	if (bCallColAtt)
	{
		SQLSMALLINT	pcbL;

		ret = PGAPI_ColAttributes(stmt, RecNumber,
			FieldIdentifier, Value, (SQLSMALLINT) BufferLength,
				&pcbL, &ival);
		len = pcbL;
	} 
	switch (rettype)
	{
		case 0:
		case SQL_IS_INTEGER:
			len = 4;
			*((Int4 *) Value) = ival;
			break;
		case SQL_IS_UINTEGER:
			len = 4;
			*((UInt4 *) Value) = ival;
			break;
		case SQL_IS_POINTER:
			len = 4;
			*((void **) Value) = ptr;
			break;
	}
			
	if (StringLength)
		*StringLength = len;
	return ret;
}

static RETCODE SQL_API
IPDGetField(StatementClass *stmt, SQLSMALLINT RecNumber,
		SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength,
		SQLINTEGER *StringLength)
{
	RETCODE		ret = SQL_SUCCESS;
	SQLINTEGER	ival = 0, len, rettype = 0;
	PTR		ptr = NULL;
	const IPDFields	*ipdopts = SC_get_IPD(stmt);
	const APDFields	*apdopts = SC_get_APD(stmt);

	switch (FieldIdentifier)
	{
		case SQL_DESC_ARRAY_STATUS_PTR:
			rettype = SQL_IS_POINTER;
			ptr = ipdopts->param_status_ptr;
			break;
		case SQL_DESC_ROWS_PROCESSED_PTR:
			rettype = SQL_IS_POINTER;
			ptr = ipdopts->param_processed_ptr;
			break;
		case SQL_DESC_UNNAMED: /* only SQL_UNNAMED is allowed */
			ival = SQL_UNNAMED;
			break;
		case SQL_DESC_TYPE:
			switch (apdopts->parameters[RecNumber - 1].SQLType)
			{
				case SQL_TYPE_DATE:
				case SQL_TYPE_TIME:
				case SQL_TYPE_TIMESTAMP:
					ival = SQL_DATETIME;
					break;
				default:
					ival = apdopts->parameters[RecNumber - 1].SQLType;
			}
			break;
		case SQL_DESC_DATETIME_INTERVAL_CODE:
			switch (apdopts->parameters[RecNumber - 1].SQLType)
			{
				case SQL_TYPE_DATE:
					ival = SQL_CODE_DATE;
				case SQL_TYPE_TIME:
					ival = SQL_CODE_TIME;
					break;
				case SQL_TYPE_TIMESTAMP:
					ival = SQL_CODE_TIMESTAMP;
					break;
				default:
					ival = 0;
			}
			break;
		case SQL_DESC_CONCISE_TYPE:
			ival = apdopts->parameters[RecNumber - 1].SQLType;
			break;
		case SQL_DESC_COUNT:
			ival = apdopts->allocated;
			break; 
		case SQL_DESC_PARAMETER_TYPE:
			ival = apdopts->parameters[RecNumber - 1].paramType;
			break;
		case SQL_DESC_PRECISION:
			switch (apdopts->parameters[RecNumber - 1].CType)
			{
				case SQL_C_TYPE_DATE:
				case SQL_C_TYPE_TIME:
				case SQL_C_TYPE_TIMESTAMP:
				case SQL_DATETIME:
					ival = apdopts->parameters[RecNumber - 1].decimal_digits;
					break;
			}
			break;
		case SQL_DESC_SCALE:
			switch (apdopts->parameters[RecNumber - 1].CType)
			{
				case SQL_C_NUMERIC:
					ival = apdopts->parameters[RecNumber - 1].decimal_digits;
					break;
			}
			break;
		case SQL_DESC_ALLOC_TYPE: /* read-only */
			ival = SQL_DESC_ALLOC_AUTO;
			break; 
		case SQL_DESC_CASE_SENSITIVE: /* read-only */
		case SQL_DESC_DATETIME_INTERVAL_PRECISION:
		case SQL_DESC_FIXED_PREC_SCALE: /* read-only */
		case SQL_DESC_LENGTH:
		case SQL_DESC_LOCAL_TYPE_NAME: /* read-only */
		case SQL_DESC_NAME:
		case SQL_DESC_NULLABLE: /* read-only */
		case SQL_DESC_NUM_PREC_RADIX:
		case SQL_DESC_OCTET_LENGTH:
#if (ODBCVER >= 0x0350)
		case SQL_DESC_ROWVER: /* read-only */
#endif /* ODBCVER */
		case SQL_DESC_TYPE_NAME: /* read-only */
		case SQL_DESC_UNSIGNED: /* read-only */
		default:ret = SQL_ERROR;
			stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; 
	}
	switch (rettype)
	{
		case 0:
		case SQL_IS_INTEGER:
			len = 4;
			*((Int4 *) Value) = ival;
			break;
		case SQL_IS_POINTER:
			len = 4;
			*((void **)Value) = ptr;
			break;
	}
			
	if (StringLength)
		*StringLength = len;
	return ret;
}

/*	SQLGetStmtOption -> SQLGetStmtAttr */
RETCODE		SQL_API
PGAPI_GetStmtAttr(HSTMT StatementHandle,
		SQLINTEGER Attribute, PTR Value,
		SQLINTEGER BufferLength, SQLINTEGER *StringLength)
{
	static char *func = "PGAPI_GetStmtAttr";
	StatementClass *stmt = (StatementClass *) StatementHandle;
	RETCODE		ret = SQL_SUCCESS;
	int			len = 0;

	mylog("%s Handle=%u %d\n", func, StatementHandle, Attribute);
	switch (Attribute)
	{
		case SQL_ATTR_FETCH_BOOKMARK_PTR:		/* 16 */
			*((void **) Value) = stmt->options.bookmark_ptr;
			len = 4;
			break;
		case SQL_ATTR_PARAM_BIND_OFFSET_PTR:	/* 17 */
			*((SQLUINTEGER **) Value) = SC_get_APD(stmt)->param_offset_ptr;
			len = 4;
			break;
		case SQL_ATTR_PARAM_BIND_TYPE:	/* 18 */
			*((SQLUINTEGER *) Value) = SC_get_APD(stmt)->param_bind_type;
			len = 4;
			break;
		case SQL_ATTR_PARAM_OPERATION_PTR:		/* 19 */
			*((SQLUSMALLINT **) Value) = SC_get_APD(stmt)->param_operation_ptr;
			len = 4;
			break;
		case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */
			*((SQLUSMALLINT **) Value) = SC_get_IPD(stmt)->param_status_ptr;
			len = 4;
			break;
		case SQL_ATTR_PARAMS_PROCESSED_PTR:		/* 21 */
			*((SQLUINTEGER **) Value) = SC_get_IPD(stmt)->param_processed_ptr;
			len = 4;
			break;
		case SQL_ATTR_PARAMSET_SIZE:	/* 22 */
			*((SQLUINTEGER *) Value) = SC_get_APD(stmt)->paramset_size;
			len = 4;
			break;
		case SQL_ATTR_ROW_BIND_OFFSET_PTR:		/* 23 */
			*((SQLUINTEGER **) Value) = SC_get_ARD(stmt)->row_offset_ptr;
			len = 4;
			break;
		case SQL_ATTR_ROW_OPERATION_PTR:		/* 24 */
			*((SQLUSMALLINT **) Value) = SC_get_ARD(stmt)->row_operation_ptr;
			len = 4;
			break;
		case SQL_ATTR_ROW_STATUS_PTR:	/* 25 */
			*((SQLUSMALLINT **) Value) = SC_get_IRD(stmt)->rowStatusArray;
			len = 4;
			break;
		case SQL_ATTR_ROWS_FETCHED_PTR: /* 26 */
			*((SQLUINTEGER **) Value) = SC_get_IRD(stmt)->rowsFetched;
			len = 4;
			break;
		case SQL_ATTR_ROW_ARRAY_SIZE:	/* 27 */
			*((SQLUINTEGER *) Value) = SC_get_ARD(stmt)->rowset_size;
			len = 4;
			break;
		case SQL_ATTR_APP_ROW_DESC:		/* 10010 */
		case SQL_ATTR_APP_PARAM_DESC:	/* 10011 */
		case SQL_ATTR_IMP_ROW_DESC:		/* 10012 */
		case SQL_ATTR_IMP_PARAM_DESC:	/* 10013 */
			len = 4;
			*((HSTMT *) Value) = descHandleFromStatementHandle(StatementHandle, Attribute); 
			break;
		case SQL_ATTR_AUTO_IPD:	/* 10001 */
			/* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */

		case SQL_ATTR_CURSOR_SCROLLABLE:		/* -1 */
		case SQL_ATTR_CURSOR_SENSITIVITY:		/* -2 */
		case SQL_ATTR_ENABLE_AUTO_IPD:	/* 15 */
		case SQL_ATTR_METADATA_ID:		/* 10014 */

			/*
			 * case SQL_ATTR_PREDICATE_PTR: case
			 * SQL_ATTR_PREDICATE_OCTET_LENGTH_PTR:
			 */
			stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
			stmt->errormsg = "Unsupported statement option (Get)";
			SC_log_error(func, "", stmt);
			return SQL_ERROR;
		default:
			len = 4;
			ret = PGAPI_GetStmtOption(StatementHandle, (UWORD) Attribute, Value);
	}
	if (ret == SQL_SUCCESS && StringLength)
		*StringLength = len;
	return ret;
}

/*	SQLSetConnectOption -> SQLSetConnectAttr */
RETCODE		SQL_API
PGAPI_SetConnectAttr(HDBC ConnectionHandle,
			SQLINTEGER Attribute, PTR Value,
			SQLINTEGER StringLength)
{
	ConnectionClass *conn = (ConnectionClass *) ConnectionHandle;
	RETCODE	ret = SQL_SUCCESS;

	mylog("PGAPI_SetConnectAttr %d\n", Attribute);
	switch (Attribute)
	{
		case SQL_ATTR_ASYNC_ENABLE:
		case SQL_ATTR_AUTO_IPD:
		case SQL_ATTR_CONNECTION_DEAD:
		case SQL_ATTR_CONNECTION_TIMEOUT:
		case SQL_ATTR_METADATA_ID:
			conn->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
			conn->errormsg = "Unsupported connect attribute (Set)";
			return SQL_ERROR;
		default:
			ret = PGAPI_SetConnectOption(ConnectionHandle, (UWORD) Attribute, (UDWORD) Value);
	}
	return ret;
}

/*	new function */
RETCODE		SQL_API
PGAPI_GetDescField(SQLHDESC DescriptorHandle,
			SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier,
			PTR Value, SQLINTEGER BufferLength,
			SQLINTEGER *StringLength)
{
	RETCODE		ret = SQL_SUCCESS;
	HSTMT		hstmt;
	SQLUINTEGER	descType;
	StatementClass *stmt;
	static const char *func = "PGAPI_GetDescField";

	mylog("%s h=%u rec=%d field=%d blen=%d\n", func, DescriptorHandle, RecNumber, FieldIdentifier, BufferLength);
	hstmt = statementHandleFromDescHandle(DescriptorHandle, &descType);
	mylog("stmt=%x type=%d\n", hstmt, descType);
	stmt = (StatementClass *) hstmt;
	switch (descType)
	{
		case SQL_ATTR_APP_ROW_DESC:
			ret = ARDGetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength, StringLength);
			break;
		case SQL_ATTR_APP_PARAM_DESC:
			ret = APDGetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength, StringLength);
			break;
		case SQL_ATTR_IMP_ROW_DESC:
			ret = IRDGetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength, StringLength);
			break;
		case SQL_ATTR_IMP_PARAM_DESC:
			ret = IPDGetField(stmt, RecNumber, FieldIdentifier, Value, BufferLength, StringLength);
			break;
		default:ret = SQL_ERROR;
			stmt->errornumber = STMT_INTERNAL_ERROR; 
			stmt->errormsg = "Error not implemented";
	}
	if (ret == SQL_ERROR)
	{
		if (!stmt->errormsg && stmt->errornumber == STMT_INVALID_DESCRIPTOR_IDENTIFIER) 
			stmt->errormsg = "can't SQLGetDescField for this descriptor identifier"; 
		SC_log_error(func, "", stmt);
	}
	return ret;
}

/*	new function */
RETCODE		SQL_API
PGAPI_SetDescField(SQLHDESC DescriptorHandle,
			SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier,
			PTR Value, SQLINTEGER BufferLength)
{
	RETCODE		ret = SQL_SUCCESS;
	HSTMT		hstmt;
	SQLUINTEGER	descType;
	StatementClass *stmt;
	static const char *func = "PGAPI_SetDescField";

	mylog("%s h=%u rec=%d field=%d val=%x,%d\n", func, DescriptorHandle, RecNumber, FieldIdentifier, Value, BufferLength);
	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)
	{
		if (!stmt->errormsg && stmt->errornumber == STMT_INVALID_DESCRIPTOR_IDENTIFIER) 
			stmt->errormsg = "can't SQLSetDescField for this descriptor identifier"; 
		SC_log_error(func, "", stmt);
	}
	return ret;
}

/*	SQLSet(Param/Scroll/Stmt)Option -> SQLSetStmtAttr */
RETCODE		SQL_API
PGAPI_SetStmtAttr(HSTMT StatementHandle,
		SQLINTEGER Attribute, PTR Value,
		SQLINTEGER StringLength)
{
	static char *func = "PGAPI_SetStmtAttr";
	StatementClass *stmt = (StatementClass *) StatementHandle;

	mylog("%s Handle=%u %d,%u\n", func, StatementHandle, Attribute, Value);
	switch (Attribute)
	{
		case SQL_ATTR_CURSOR_SCROLLABLE:		/* -1 */
		case SQL_ATTR_CURSOR_SENSITIVITY:		/* -2 */

		case SQL_ATTR_ENABLE_AUTO_IPD:	/* 15 */

		case SQL_ATTR_APP_ROW_DESC:		/* 10010 */
		case SQL_ATTR_APP_PARAM_DESC:	/* 10011 */
		case SQL_ATTR_AUTO_IPD:	/* 10001 */
		/* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */
		case SQL_ATTR_IMP_ROW_DESC:	/* 10012 (read-only) */
		case SQL_ATTR_IMP_PARAM_DESC:	/* 10013 (read-only) */
		case SQL_ATTR_METADATA_ID:		/* 10014 */

			/*
			 * case SQL_ATTR_PREDICATE_PTR: case
			 * SQL_ATTR_PREDICATE_OCTET_LENGTH_PTR:
			 */
			stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
			stmt->errormsg = "Unsupported statement option (Set)";
			SC_log_error(func, "", stmt);
			return SQL_ERROR;

		case SQL_ATTR_FETCH_BOOKMARK_PTR:		/* 16 */
			stmt->options.bookmark_ptr = Value;
			break;
		case SQL_ATTR_PARAM_BIND_OFFSET_PTR:	/* 17 */
			SC_get_APD(stmt)->param_offset_ptr = (SQLUINTEGER *) Value;
			break;
		case SQL_ATTR_PARAM_BIND_TYPE:	/* 18 */
			SC_get_APD(stmt)->param_bind_type = (SQLUINTEGER) Value;
			break;
		case SQL_ATTR_PARAM_OPERATION_PTR:		/* 19 */
			SC_get_APD(stmt)->param_operation_ptr = Value;
			break;
		case SQL_ATTR_PARAM_STATUS_PTR:			/* 20 */
			SC_get_IPD(stmt)->param_status_ptr = (SQLUSMALLINT *) Value;
			break;
		case SQL_ATTR_PARAMS_PROCESSED_PTR:		/* 21 */
			SC_get_IPD(stmt)->param_processed_ptr = (SQLUINTEGER *) Value;
			break;
		case SQL_ATTR_PARAMSET_SIZE:	/* 22 */
			SC_get_APD(stmt)->paramset_size = (SQLUINTEGER) Value;
			break;
		case SQL_ATTR_ROW_BIND_OFFSET_PTR:		/* 23 */
			SC_get_ARD(stmt)->row_offset_ptr = (SQLUINTEGER *) Value;
			break;
		case SQL_ATTR_ROW_OPERATION_PTR:		/* 24 */
			SC_get_ARD(stmt)->row_operation_ptr = Value;
			break;
		case SQL_ATTR_ROW_STATUS_PTR:	/* 25 */
			SC_get_IRD(stmt)->rowStatusArray = (SQLUSMALLINT *) Value;
			break;
		case SQL_ATTR_ROWS_FETCHED_PTR: /* 26 */
			SC_get_IRD(stmt)->rowsFetched = (SQLUINTEGER *) Value;
			break;
		case SQL_ATTR_ROW_ARRAY_SIZE:	/* 27 */
			SC_get_ARD(stmt)->rowset_size = (SQLUINTEGER) Value;
			break;
		default:
			return PGAPI_SetStmtOption(StatementHandle, (UWORD) Attribute, (UDWORD) Value);
	}
	return SQL_SUCCESS;
}

#ifdef DRIVER_CURSOR_IMPLEMENT
RETCODE	SQL_API
PGAPI_BulkOperations(HSTMT hstmt, SQLSMALLINT operation)
{
	static char	*func = "PGAPI_BulkOperations";
	StatementClass	*stmt = (StatementClass *) hstmt;
	ARDFields	*opts = SC_get_ARD(stmt);
	RETCODE		ret;
	UInt4		offset, bind_size = opts->bind_size, *bmark = NULL;
	int		i, processed;
	ConnectionClass	*conn;
	BOOL		auto_commit_needed = FALSE;
	QResultClass	*res;

	mylog("%s operation = %d\n", func, operation);
	SC_clear_error(stmt);
	offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
	
	if (SQL_FETCH_BY_BOOKMARK != operation)
	{
		conn = SC_get_conn(stmt);
		if (auto_commit_needed = CC_is_in_autocommit(conn), auto_commit_needed)
			PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
	}
	if (SQL_ADD != operation)
	{
		if (bmark = (UInt4 *) opts->bookmark->buffer, !bmark)
		{
			stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER;
			stmt->errormsg = "bookmark isn't specified";
			return SQL_ERROR;
		}
		bmark += (offset >> 4);
	}
	for (i = 0, processed = 0; i < opts->rowset_size; i++)
	{
		/* Note opts->row_operation_ptr is ignored */
		switch (operation)
		{
			case SQL_ADD:
				ret = SC_pos_add(stmt, (UWORD) i);
				break;
			case SQL_UPDATE_BY_BOOKMARK:
				ret = SC_pos_update(stmt, (UWORD) i, *bmark);
				break;
			case SQL_DELETE_BY_BOOKMARK:
				ret = SC_pos_delete(stmt, (UWORD) i, *bmark);
				break;
			case SQL_FETCH_BY_BOOKMARK:
				ret = SC_pos_refresh(stmt, (UWORD) i, *bmark);
				break;
		}
		processed++;
		if (SQL_ERROR == ret)
			break;
		if (SQL_ADD != operation)
		{
			if (bind_size > 0)
				bmark += (bind_size >> 2);
			else
				bmark++;
		} 
	}
	if (auto_commit_needed)
		PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON);
	if (SC_get_IRD(stmt)->rowsFetched)
		*(SC_get_IRD(stmt)->rowsFetched) = processed;

	if (res = SC_get_Curres(stmt), res)
		res->recent_processed_row_count = stmt->diag_row_count = processed;
	return ret;
}	
#endif /* DRIVER_CURSOR_IMPLEMENT */
