#include <WSCdb.h>
#include <WSCdbDrv.h>
#include <WSCdbPgsql.h>
#include <WSClocaleSet.h>

#include "udebug.h"

extern "C" {
void libwspostgres_func();
};
void libwspostgres_func(){
}

//#define BINARY		// binary cursor test use

#ifdef FACTORY_METHOD /* factory method handler..*/
WSCdbDrvDatabase* _WSCdbPgsqlDatabase_create_(){
  WSCdbPgsqlDatabase* inst = new WSCdbPgsqlDatabase();
  inst->setThisPtr(inst);
  return inst;
}
WSCdbDrvRecord* _WSCdbPgsqlRecord_create_(WSCdbDrvDatabase* db){
  return new WSCdbPgsqlRecord((WSCdbPgsqlDatabase*)db->getThisPtr());
}

class _WSCdbPgsqlDatabase_init_ {
  public:
    _WSCdbPgsqlDatabase_init_ (){
       WSCdbDatabase::setCreateHandler((void*)_WSCdbPgsqlDatabase_create_,"PGSQL");
       WSCdbRecord::setCreateHandler((void*)_WSCdbPgsqlRecord_create_,"PGSQL");
    };
};
static _WSCdbPgsqlDatabase_init_ _execute_initialize_;
#endif /* factory method handler..*/

WSCdbPgsqlDatabase::WSCdbPgsqlDatabase() : WSCdbDrvDatabase()
{
	TRACE_U(("WSCdbPgsqlDatabase::WSCdbPgsqlDatabase start"));
	
	_hconn = NULL;
	_open = False;
	_begin = 0;
	_cursor = 0;
	_processid = 0;
	memset(_dbName, '\0', sizeof(_dbName));
	_encoding = -1;
	TRACE_U(("WSCdbPgsqlDatabase::WSCdbPgsqlDatabase end"));
}
WSCdbPgsqlDatabase::~WSCdbPgsqlDatabase()
{
	TRACE_U(("WSCdbPgsqlDatabase::~WSCdbPgsqlDatabase"));

	close();
	
	TRACE_U(("WSCdbPgsqlDatabase::~WSCdbPgsqlDatabase end"));
}

long WSCdbPgsqlDatabase::open(char* host, char* uid, char* pwd, char* dbname, char* port)
{
	TRACE_U(("WSCdbPgsqlDatabase::open start"));

	if(isOpen()) {
		close();
	}
	_open = False;
	
	_hconn = PQsetdbLogin(host, 
						  (port == NULL ? NULL : atoi(port) == 0 ? NULL : port), 
						  NULL, 
						  NULL, 
						  dbname, 
						  (uid == NULL ? NULL : strlen(uid) == 0 ? NULL : uid),
						  (pwd == NULL ? NULL : strlen(pwd) == 0 ? NULL : pwd));
	if(PQstatus(_hconn) == CONNECTION_BAD) {
		getError();
        return WS_ERR;
	}
	_open = True;
	_processid = PQbackendPID(_hconn);
	strcpy(_dbName, dbname);

	// get encoding
	beginTran();
	char buf1[256];
	char buf2[256];
	PGresult* res = NULL;
	sprintf(buf1, "DECLARE MYCURSOR_%d CURSOR FOR ", _processid);
	sprintf(buf2, "%s select encoding from pg_database where datname = '%s'", buf1, _dbName);
	if(sqlExecute(buf2) == WS_NO_ERR) {
		sprintf(buf2, "FETCH ALL IN MYCURSOR_%d", _processid);
		res = PQexec(_hconn, buf2);
		if(!IS_PGSQL_ERROR(res)) {
//			encoding_conv(atoi((char*)PQgetvalue(res, 0, 0)));
			char* val = PQgetvalue(res, 0, 0);
			encoding_conv((val != NULL ? atoi(val) : 0));
		}
		PQclear(res);
		sprintf(buf2, "CLOSE MYCURSOR_%d", _processid);
		res = PQexec(_hconn, buf2);
		PQclear(res);
	}
	abortTran();

	TRACE_U(("WSCdbPgsqlDatabase::open end"));
	return WS_NO_ERR;
}
long WSCdbPgsqlDatabase::close()
{
	TRACE_U(("WSCdbPgsqlDatabase::close start"));
	if(_hconn == NULL) {
		return WS_NO_ERR;
	}
	if(_begin) {
		abortTran();
	}
	PQfinish(_hconn);
	_hconn = NULL;
	_open = False;
	_cursor = 0;
	_processid = 0;
	memset(_dbName, '\0', sizeof(_dbName));
	_encoding = -1;
	TRACE_U(("WSCdbPgsqlDatabase::close end"));
	return WS_NO_ERR;
}
long WSCdbPgsqlDatabase::beginTran()
{
	TRACE_U(("WSCdbPgsqlDatabase::beginTran start"));
	if(!isOpen()) {
		return WS_ERR;
	}
	long ret = WS_NO_ERR;
	PGresult* res = NULL;
	res = PQexec(_hconn, "BEGIN");
	if(IS_PGSQL_ERROR(res)) {
		getError();
		ret = WS_ERR;
	} else {
		_begin = PGSQL_TRAN_FUNCTION;
	}
	PQclear(res);
	TRACE_U(("WSCdbPgsqlDatabase::beginTran end"));
	return ret;
}
long WSCdbPgsqlDatabase::commitTran()
{
	TRACE_U(("WSCdbPgsqlDatabase::commitTran start"));
	if(!isOpen()) {
		return WS_ERR;
	}
	long ret = WS_NO_ERR;
	PGresult* res = NULL;
	res = PQexec(_hconn, "COMMIT");
	if(IS_PGSQL_ERROR(res)) {
		getError();
		ret = WS_ERR;
	} else {
		_begin = False;
	}
	PQclear(res);
	TRACE_U(("WSCdbPgsqlDatabase::commitTran end"));
	return ret;
}
long WSCdbPgsqlDatabase::abortTran()
{
	TRACE_U(("WSCdbPgsqlDatabase::abortTran start"));
	if(!isOpen()) {
		return WS_ERR;
	}
	long ret = WS_NO_ERR;
	PGresult* res = NULL;
	res = PQexec(_hconn, "ROLLBACK");
	if(IS_PGSQL_ERROR(res)) {
		getError();
		ret = WS_ERR;
	} else {
		_begin = False;
	}
	PQclear(res);
	TRACE_U(("WSCdbPgsqlDatabase::abortTran end"));
	return ret;
}
long WSCdbPgsqlDatabase::sqlExecute(const char* stmt)
{
	TRACE_U(("WSCdbPgsqlDatabase::sqlExecute start"));
	if(!isOpen()) {
		return WS_ERR;
	}
	long ret = WS_NO_ERR;
	PGresult* res = NULL;

	TRACE_U(("decode(%s)\n", decode(stmt)));

	res = PQexec(_hconn, decode(stmt));
	if(IS_PGSQL_ERROR(res)) {
		getError();
		ret = WS_ERR;
	}
	PQclear(res);
	TRACE_U(("WSCdbPgsqlDatabase::sqlExecute end"));
	return ret;
}
WSCbool WSCdbPgsqlDatabase::isOpen()
{
	return _open;
}
void WSCdbPgsqlDatabase::getError()
{
	TRACE_U(("WSCdbPgsqlDatabase::getError start"));

	strcpy((char*)_szSqlState, "DB001");
	memset(_szErrorMsg, '\0', sizeof(_szErrorMsg));
	strncpy((char*)_szErrorMsg, PQerrorMessage(_hconn), sizeof(_szErrorMsg)-1);

	TRACE_U(("WSCdbPgsqlDatabase::getError State:%s messgae:%s", _szSqlState, _szErrorMsg));
	TRACE_U(("WSCdbPgsqlDatabase::getError end"));
}

void WSCdbPgsqlDatabase::getErrorMsg(char* szState, char* szMsg)
{
	TRACE_U(("WSCdbPgsqlDatabase::getErrorMsg start"));
	
	strcpy(szState, (char*)_szSqlState);
	strcpy(szMsg, (char*)_szErrorMsg);
	
	TRACE_U(("WSCdbPgsqlDatabase::getErrorMsg end"));
}

void WSCdbPgsqlDatabase::getErrorMsg(char* szMsg,long size)
{
	TRACE_U(("WSCdbPgsqlDatabase::getErrorMsg start"));
	
	char szStat[6];
	char szErrorMsg[PGSQL_MAX_MESSAGE_LENGTH + 1];
	char buf[PGSQL_MAX_MESSAGE_LENGTH + 1+32];
	getErrorMsg(szStat,szErrorMsg);
	sprintf(buf,"status:%s message:%s",szStat,szErrorMsg);
	strncpy(szMsg,buf,size);
	szMsg[size] = '\0';

	TRACE_U(("WSCdbPgsqlDatabase::getErrorMsg end"));
}
int WSCdbPgsqlDatabase::getProcessId()
{
	return 	_processid;
}
WSCulong WSCdbPgsqlDatabase::getCursor()
{
	return ++_cursor;
}
short WSCdbPgsqlDatabase::getbeginTranmode()
{
	return _begin;
}
void WSCdbPgsqlDatabase::setbeginTranmode(short begin)
{
	_begin = begin;
}
char* WSCdbPgsqlDatabase::getDbName()
{
	return _dbName;
}
void WSCdbPgsqlDatabase::setEncode(long code)
{
	_encoding = code;
}
long WSCdbPgsqlDatabase::getEncode()
{
	return _encoding;
}
void WSCdbPgsqlDatabase::encoding_conv(long code)
{
	if(code == EUC_JP) {
		// EUC for Japanese
		_encoding = WS_EN_EUCJP;
	} else
	if(code == EUC_CN) {
		// EUC for Chinese
		_encoding = WS_EN_EUCCN;
	} else 
	if(code == EUC_KR) {
		// EUC for Korean
		_encoding = WS_EN_EUCKR;
	} else
	if(code == UNICODE) {
		// Unicode UTF-8
		_encoding = WS_EN_UTF8;
	} else
	if(code == LATIN1) {
		// ISO-8859 Latin 1 
		_encoding = WS_EN_ISO8859_1;
	} else
	if(code == LATIN2) {
		// ISO-8859 Latin 2
		_encoding = WS_EN_ISO8859_2;
	} else
	if(code == LATIN3) {
		// ISO-8859 Latin 3
		_encoding = WS_EN_ISO8859_3;
	} else
	if(code == LATIN4) {
		// ISO-8859 Latin 4
		_encoding = WS_EN_ISO8859_4;
	} else
	if(code == LATIN5) {
		// ISO-8859 Latin 5
		_encoding = WS_EN_ISO8859_5;
	} else
	if(code == LATIN6) {
		// ISO-8859 Latin 6
		_encoding = WS_EN_ISO8859_6;
	} else
	if(code == LATIN7) {
		// ISO-8859 Latin 7
		_encoding = WS_EN_ISO8859_7;
	} else
	if(code == LATIN8) {
		// ISO-8859 Latin 8
		_encoding = WS_EN_ISO8859_8;
	} else
	if(code == LATIN9) {
		// ISO-8859 Latin 9
		_encoding = WS_EN_ISO8859_9;
	} else
	if(code == KOI8) {
		// KOI8-R/U
		_encoding = WS_EN_KOI8R;
	} else
	if(code == SJIS) {
		// Shift JIS
		_encoding = WS_EN_SJIS;
	} else
	if(code == BIG5) {
		_encoding = WS_EN_BIG5;
	} else {
		_encoding = -1;
	}
	TRACE_U(("encoding_conv code:%d, _encoding:%d\n", code, _encoding));
}
char* WSCdbPgsqlDatabase::decode(const char* buf)
{
	long code = WSGIappLocaleSet()->getDefaultEncoding();
	static WSCstring s;
	s = buf;

	TRACE_U(("decode buf:%s _encoding=%d, decode=%d\n", buf, _encoding, code));

	if(_encoding != code && _encoding != -1) {
	TRACE_U(("decode buf:%s\n", buf));
	TRACE_U(("decode str:%s\n", s.getString(_encoding)));
		return s.getString(_encoding);
	}
	return s.getString();
}

/////
WSCdbPgsqlRecord::WSCdbPgsqlRecord(WSCdbPgsqlDatabase* pDatabase) : WSCdbDrvRecord(pDatabase)
{
	TRACE_U(("WSCdbPgsqlRecord::WSCdbPgsqlRecord start"));

	Cleanup(pDatabase);

	TRACE_U(("WSCdbPgsqlRecord::WSCdbPgsqlRecord end"));
}
void WSCdbPgsqlRecord::Cleanup(WSCdbPgsqlDatabase* pDatabase)
{
	_pDB = pDatabase;
	_res_stmt = NULL;
	_open = False;
	_Cols = 0;
	_fd = NULL;
	_access_mode = PGSQL_ACCESS_MODE_READ;
	_Cursor = WSCDB_SQL_CURSOR_STATIC;
	_access = WSCDB_SQL_CONCUR_LOCK;
	memset(_szSqlState, '\0', sizeof(_szSqlState));
	memset(_szErrorMsg, '\0', sizeof(_szErrorMsg));
	memset(_CurrsorName, '\0', sizeof(_CurrsorName));
	_RowCount = 0;
	_FetchRowPos = 0;
	_encoding = -1;
	_oiduse = 0;
}

WSCdbPgsqlRecord::~WSCdbPgsqlRecord()
{
	TRACE_U(("WSCdbPgsqlRecord::~WSCdbPgsqlRecord start"));

	close();

	_pDB = NULL;

	TRACE_U(("WSCdbPgsqlRecord::~WSCdbPgsqlRecord end"));
}
long WSCdbPgsqlRecord::open(const char* stmt)
{
	return open(stmt, WSCDB_SQL_CURSOR_STATIC, WSCDB_SQL_CONCUR_LOCK);
}
long WSCdbPgsqlRecord::open(const char* stmt, short eCousor, short eAccess)
{
	TRACE_U(("WSCdbPgsqlRecord::open start"));

	if(_pDB == NULL) {
		return WS_ERR;
	}
	if(_pDB->_hconn == NULL) {
		return WS_ERR;
	}
	if(isOpen()) {
		close();
	}
	_open = False;
	_Cursor = eCousor;
	_access = eAccess;
	_RowCount = 0;
	_FetchRowPos = 0xffffffff;
	_encoding = _pDB->getEncode();
	memset(_tableName, '\0', sizeof(_tableName));

	long ret = WS_NO_ERR;
	WSCbool	select = False;
	PGresult* res = NULL;
	char*	buf;
	char*	sql;
	char	oid[256];
	char	buf1[80];
	char	buf2[256];
	strncpy(buf1, stmt, 6);
	buf1[6] = '\0';

	if(_access != WSCDB_SQL_CONCUR_READ_ONLY
		&& WSGFstricmp(buf1, "select") == 0) {
		select = True;
		if(!_pDB->getbeginTranmode()) {
			if(_pDB->beginTran() == WS_ERR) {
				return WS_ERR;
			}
			_pDB->setbeginTranmode(PGSQL_TRAN_RECORD_FUNCTION);
		}
		sprintf(_CurrsorName, "WSdbCursor%d%d", _pDB->getProcessId(), _pDB->getCursor());
#ifndef BINARY
		sprintf(buf1, "DECLARE %s CURSOR FOR ", _CurrsorName);
#else
		sprintf(buf1, "DECLARE %s BINARY CURSOR FOR ", _CurrsorName);
#endif	
		sql = (char*)&stmt[6];
		getTableName((char*)stmt, _tableName, sizeof(_tableName));

		sprintf(buf2, "%s select count(a.attname) from "
			          "pg_class c, pg_attribute a "
					  "where c.relname = '%s' "
					  "and a.attrelid = c.oid and attname='oid'", buf1, _tableName);
		// oid item check
		_oiduse = 0;
//		oid[0] = '\0';
		strcpy(oid, "select ");
		res = PQexec(_pDB->_hconn, buf2);
		TRACE_U(("WSCdbPgsqlRecord::open oid %s", buf2));
		if(!IS_PGSQL_ERROR(res)) {
			PQclear(res);
			sprintf(buf2, "FETCH ALL IN %s", _CurrsorName);
			res = PQexec(_pDB->_hconn, buf2);
			if(atoi((char*)PQgetvalue(res, 0, 0)) > 0) {
				_oiduse = 1;
//				sprintf(oid, "select oid as oid_%s,", _CurrsorName);
				sprintf(oid, "select %s.oid as oid_%s,", _tableName, _CurrsorName);
				TRACE_U(("WSCdbPgsqlRecord::open oid use"));
			}
			PQclear(res);
			sprintf(buf2, "CLOSE %s", _CurrsorName);
			res = PQexec(_pDB->_hconn, buf2);
		}
		PQclear(res);

	} else {
		_CurrsorName[0] = '\0';
		buf1[0] = '\0';
		sql = (char*)stmt;
		oid[0] = '\0';
		_oiduse = 0;
	}
	buf = new char[strlen(stmt) + strlen(buf1) + sizeof(oid) + 1];

	sprintf(buf, "%s%s%s", buf1, oid, sql);
	res = PQexec(_pDB->_hconn, buf);
	if(IS_PGSQL_ERROR(res)) {
		getError();
		ret = WS_ERR;
	}
	PQclear(res);
	
	if(select && ret == WS_NO_ERR) {
		sprintf(buf, "FETCH ALL IN %s", _CurrsorName);
		_res_stmt = PQexec(_pDB->_hconn, buf);
		if(IS_PGSQL_ERROR(res)) {
			getError();
			ret = WS_ERR;
		} else {
			_RowCount = PQntuples(_res_stmt);
		}
	}
	delete[] buf;

	if(BindCols() != WS_NO_ERR) {
		return WS_ERR;
	}
	_open = True;

//	delete[] buf;

	moveNext();

	TRACE_U(("WSCdbPgsqlRecord::open end"));
	return WS_NO_ERR;
}
long WSCdbPgsqlRecord::close()
{
	TRACE_U(("WSCdbPgsqlRecord::close start"));

	if(_res_stmt) {
		PQclear(_res_stmt);
		PGresult* res = NULL;
		char stmt[128];
		sprintf(stmt, "CLOSE %s", _CurrsorName);
		res = PQexec(_pDB->_hconn, stmt);
		PQclear(res);
	}
	if(_pDB->getbeginTranmode() == PGSQL_TRAN_RECORD_FUNCTION) {
		_pDB->abortTran();
	}
	_res_stmt = NULL;
	ReleaseCols();
	_open = False;
	_RowCount = 0;
	_FetchRowPos = 0;
	_encoding = -1;
	_oiduse = 0;

	TRACE_U(("WSCdbPgsqlRecord::close end"));
	return WS_NO_ERR;
}
WSCbool WSCdbPgsqlRecord::isEOF()
{
	return (_FetchRowPos < _RowCount ? False : True);
}
WSCbool WSCdbPgsqlRecord::isOpen()
{
	return _open;
}
long WSCdbPgsqlRecord::getColValue(long index, WSCvariant* var)
{
//	if(index < 0 || index >= (_Cols-1)) {
	if(index < 0 || index >= (_Cols-_oiduse)) {
		return WS_ERR;
	}
	getValue(index, var);
	return WS_NO_ERR;
}
long WSCdbPgsqlRecord::getColValue(const char* name, WSCvariant* var)
{
	return getColValue(getColIndex(name), var);
}
long WSCdbPgsqlRecord::getColStringValue(long index, char* var)
{
//	if(index < 0 || index >= (_Cols-1)) {
	if(index < 0 || index >= (_Cols-_oiduse)) {
		return WS_ERR;
	}
	strcpy(var, (char*)_fd[index+_oiduse].buff);
	return WS_NO_ERR;
}
long WSCdbPgsqlRecord::getColStringValue(const char* name, char* var)
{
	return getColStringValue(getColIndex(name), var);
}
long WSCdbPgsqlRecord::setColValue(long index, WSCvariant var)
{
//	if(index < 0 || index >= (_Cols-1)) {
	if(index < 0 || index >= (_Cols-_oiduse)) {
		return WS_ERR;
	}
	if(_access_mode == PGSQL_ACCESS_MODE_READ) {
		return WS_ERR;
	}
	setValue(index, &var);
	return WS_NO_ERR;
}
long WSCdbPgsqlRecord::setColValue(const char* name, WSCvariant var)
{
	return setColValue(getColIndex(name), var);
}
long WSCdbPgsqlRecord::edit()
{
//	if(isEOF() || _access == WSCDB_SQL_CONCUR_READ_ONLY) {
	if(isEOF() || _access == WSCDB_SQL_CONCUR_READ_ONLY || _oiduse == 0) {
		return WS_ERR;
	}
	_access_mode = PGSQL_ACCESS_MODE_EDIT;

	return WS_NO_ERR;
}
long WSCdbPgsqlRecord::addnew()
{
//	if(!isOpen() || _access == WSCDB_SQL_CONCUR_READ_ONLY) {
	if(!isOpen() || _access == WSCDB_SQL_CONCUR_READ_ONLY || _oiduse == 0) {
		return WS_ERR;
	}
	for(int i = 0; i < _Cols; i++) {
		memset(_fd[i].buff, '\0', _fd[i].buffLen);
	}
	_access_mode = PGSQL_ACCESS_MODE_ADDNEW;
	return WS_NO_ERR;
}
long WSCdbPgsqlRecord::update()
{
	long ret = WS_ERR;
	if(_access_mode == PGSQL_ACCESS_MODE_ADDNEW) {
		ret = _pDB->sqlExecute(makeINSERT());
	} else
	if(_access_mode == PGSQL_ACCESS_MODE_EDIT) {
		ret = _pDB->sqlExecute(makeUPDATE());
	}
	_access_mode = PGSQL_ACCESS_MODE_READ;
	return ret;
}
long WSCdbPgsqlRecord::deleterow()
{
//	if(isEOF() || _access == WSCDB_SQL_CONCUR_READ_ONLY) {
	if(isEOF() || _access == WSCDB_SQL_CONCUR_READ_ONLY || _oiduse == 0) {
		return WS_ERR;
	}
	_access_mode = PGSQL_ACCESS_MODE_READ;
	return _pDB->sqlExecute(makeDELETE());
}
long WSCdbPgsqlRecord::moveNext()
{
	_access_mode = PGSQL_ACCESS_MODE_READ;

	if(_FetchRowPos != 0xffffffff) {
		if(isEOF()) {
			return WS_ERR;
		}
	}
	_FetchRowPos++;
	if(isEOF()) {
		return WS_ERR;
	}
	return setRowPos(_FetchRowPos);
}
long WSCdbPgsqlRecord::movePrevious()
{
	if(!isOpen() || _FetchRowPos == 0) {
		return WS_ERR;
	}
	_access_mode = PGSQL_ACCESS_MODE_READ;
	return setRowPos((_FetchRowPos > 0 ? --_FetchRowPos : 0));
}
long WSCdbPgsqlRecord::moveFirst()
{
	if(!isOpen() || _FetchRowPos == 0) {
		return WS_ERR;
	}
	_access_mode = PGSQL_ACCESS_MODE_READ;
	return setRowPos(0);
}
long WSCdbPgsqlRecord::moveLast()
{
	if(isEOF()) {
		return WS_ERR;
	}
	_access_mode = PGSQL_ACCESS_MODE_READ;
	return setRowPos(_RowCount-1);
}
long WSCdbPgsqlRecord::setRowPos(WSCulong pos)
{
	_FetchRowPos = pos;
	for(int i = 0; i < _Cols; i++) {

#ifndef BINARY
//		strcpy((char*)_fd[i].buff, PQgetvalue(_res_stmt, _FetchRowPos, i));
		encode(PQgetvalue(_res_stmt, _FetchRowPos, i), (char*)_fd[i].buff);
#else
		memcpy(_fd[i].buff, 
			   PQgetvalue(_res_stmt, _FetchRowPos, i), 
			   _fd[i].buffLen-1);
		((char*)_fd[i].buff)[_fd[i].buffLen] = '\0'; 
#endif

	}
	return WS_NO_ERR;
}
long WSCdbPgsqlRecord::getColName(long index, char* name)
{
	if(!isOpen()) {
		return -1;
	}
//	if(index < 0 || index >= (_Cols-1)) {
	if(index < 0 || index >= (_Cols-_oiduse)) {
		return WS_ERR;
	}
//	strcpy(name, (char*)_fd[index+1].colName);
	strcpy(name, (char*)_fd[index+_oiduse].colName);
	return WS_NO_ERR;
}
long WSCdbPgsqlRecord::getColIndex(const char* name)
{
	if(!isOpen()) {
		return -1;
	}
//	for(int i = 1; i < _Cols; i++) {
	for(int i = _oiduse; i < _Cols; i++) {
		if(WSGFstricmp((char*)_fd[i].colName, (char*)name) == 0) {
			return (i-_oiduse);
		}
	}
	return -1;
}
long WSCdbPgsqlRecord::getColType(long index)
{
	if(!isOpen()) {
		return -1;
	}
//	if(index < 0 || index >= (_Cols-1)) {
	if(index < 0 || index >= (_Cols-_oiduse)) {
		return -1;
	}
//	return _fd[index+1].colType;
//	return _fd[index+_oiduse].colType;
	return getColTypeConv(index+_oiduse);
}
long WSCdbPgsqlRecord::getColType(const char* name)
{
	return getColType(getColIndex(name));
}
long WSCdbPgsqlRecord::getColLength(long index)
{
	if(!isOpen()) {
		return -1;
	}
//	if(index < 0 || index >= (_Cols-1)) {
	if(index < 0 || index >= (_Cols-_oiduse)) {
		return -1;
	}
//	return _fd[index+1].buffLen-1;
	return _fd[index+_oiduse].buffLen-1;
}
long WSCdbPgsqlRecord::getColLength(const char* name)
{
	return getColType(getColIndex(name));
}

long WSCdbPgsqlRecord::getRowCount()
{
	return (isOpen() ? _RowCount : 0);
}
long WSCdbPgsqlRecord::getColCount()
{
	return _Cols - _oiduse;
}
long WSCdbPgsqlRecord::BindCols()
{
	TRACE_U(("WSCdbPgsqlRecord::BindCols start"));

	_Cols = PQnfields(_res_stmt);
//	if(_Cols < 2) {
	if(_Cols < 1) {
		TRACE_U(("WSCdbPgsqlRecord::BindCols PQnfields() Error"));
		getError();
		return WS_ERR;
	}
	_fd = new _PgsqlCol[_Cols];
	memset(_fd, '\0', sizeof(_PgsqlCol) * _Cols);

    for(int i = 0; i < _Cols; i++ ) {
		strncpy((char*)_fd[i].colName, PQfname(_res_stmt, i), sizeof(_fd[i].colName)-1);
		_fd[i].colType = PQftype(_res_stmt, i);
		_fd[i].buffLen = getBufflen(_fd[i].colType);
        _fd[i].buff = new char[_fd[i].buffLen];
		memset(_fd[i].buff, '\0', _fd[i].buffLen);
	}

	TRACE_U(("WSCdbPgsqlRecord::BindCols end"));
	return WS_NO_ERR;
}
void WSCdbPgsqlRecord::ReleaseCols()
{
	TRACE_U(("WSCdbPgsqlRecord::ReleaseCols start"));
	
	if(_fd == NULL) {
		return;
	}
    for(int i = 0; i < _Cols; i++ ) {
        delete[] (char*)(_fd[i].buff);
    }
    delete[] _fd;

	_fd = NULL;

	TRACE_U(("WSCdbPgsqlRecord::ReleaseCols end"));
}

void WSCdbPgsqlRecord::getValue(int index, WSCvariant* var)
{
	TRACE_U(("WSCdbPgsqlRecord::getValue start"));
	
//	index++;
	index += _oiduse;

	TRACE_U(("WSCdbPgsqlRecord::getValue _fd[index].colType:%d", _fd[index].colType));

	if(_fd[index].colType == PG_TYPE_BPCHAR) {
		// SQL_CHAR unsigned char
		*var = (char*)_fd[index].buff;
	} else
	if(_fd[index].colType == PG_TYPE_NUMERIC) {
		// SQL_NUMERIC, SQL_DECIMAL
		*var = (char*)_fd[index].buff;
	} else
	if(_fd[index].colType == PG_TYPE_INT4) {
		// SQL_INTEGER long int
		*var = (long)atol((char*)_fd[index].buff);
	} else
	if(_fd[index].colType == PG_TYPE_INT2) {
		// SQL_SMALLINT short int
		*var = (short)atoi((char*)_fd[index].buff);
	} else
	if(_fd[index].colType == PG_TYPE_FLOAT8) {
		// SQL_FLOAT, SQL_DOUBLE float
		*var = (double)atof((char*)_fd[index].buff);
	} else
	if(_fd[index].colType == PG_TYPE_FLOAT4) {
		// SQL_REAL REAL
		*var = (float)atof((char*)_fd[index].buff);
	} else
	if(_fd[index].colType == PG_TYPE_DATE) {
		// SQL_TYPE_DATE
		*var = (char*)_fd[index].buff;
	} else
	if(_fd[index].colType == PG_TYPE_TIME) {
		// SQL_TYPE_TIME
		*var = (char*)_fd[index].buff;
	} else
	if(_fd[index].colType == PG_TYPE_DATETIME) {
		// SQL_TYPE_TIMESTAMP
		*var = (char*)_fd[index].buff;
	} else {
		*var = (char*)_fd[index].buff;
	}

	TRACE_U(("WSCdbPgsqlRecord::getValue end"));
}
void WSCdbPgsqlRecord::setValue(int index, WSCvariant* var)
{
	TRACE_U(("WSCdbPgsqlRecord::setValue start"));
	
//	index++;
	index += _oiduse;

	TRACE_U(("WSCdbPgsqlRecord::setValue _fd[index].colType:%d", _fd[index].colType));

	int len = (int)strlen((char*)var);

	if(_fd[index].colType == PG_TYPE_BPCHAR) {
		// SQL_CHAR unsigned char
		strncpy((char*)_fd[index].buff, (char*)var, _fd[index].buffLen-1);
		((char*)_fd[index].buff)[(len < _fd[index].buffLen ? len : _fd[index].buffLen-1)] = '\0';
	} else
	if(_fd[index].colType == PG_TYPE_NUMERIC) {
		// SQL_NUMERIC, SQL_DECIMAL
		strncpy((char*)_fd[index].buff, (char*)var, _fd[index].buffLen-1);
		((char*)_fd[index].buff)[(len < _fd[index].buffLen ? len : _fd[index].buffLen-1)] = '\0';
	} else
	if(_fd[index].colType == PG_TYPE_INT4) {
		// SQL_INTEGER long int
		sprintf((char*)_fd[index].buff, "%d", var->getLong());
	} else
	if(_fd[index].colType == PG_TYPE_INT2) {
		// SQL_SMALLINT short int
		sprintf((char*)_fd[index].buff, "%d", var->getShort());
	} else
	if(_fd[index].colType == PG_TYPE_FLOAT8) {
		// SQL_FLOAT, SQL_DOUBLE float
		sprintf((char*)_fd[index].buff, "%f", var->getDouble());
	} else
	if(_fd[index].colType == PG_TYPE_FLOAT4) {
		// SQL_REAL REAL
		sprintf((char*)_fd[index].buff, "%f", var->getFloat());
	} else
	if(_fd[index].colType == PG_TYPE_DATE) {
		// SQL_TYPE_DATE
		strncpy((char*)_fd[index].buff, (char*)var, _fd[index].buffLen-1);
		((char*)_fd[index].buff)[(len < _fd[index].buffLen ? len : _fd[index].buffLen-1)] = '\0';
	} else
	if(_fd[index].colType == PG_TYPE_TIME) {
		// SQL_TYPE_TIME
		strncpy((char*)_fd[index].buff, (char*)var, _fd[index].buffLen-1);
		((char*)_fd[index].buff)[(len < _fd[index].buffLen ? len : _fd[index].buffLen-1)] = '\0';
	} else
	if(_fd[index].colType == PG_TYPE_DATETIME) {
		// SQL_TYPE_TIMESTAMP
		strncpy((char*)_fd[index].buff, (char*)var, _fd[index].buffLen-1);
		((char*)_fd[index].buff)[(len < _fd[index].buffLen ? len : _fd[index].buffLen-1)] = '\0';
	} else {
		strncpy((char*)_fd[index].buff, (char*)var, _fd[index].buffLen-1);
		((char*)_fd[index].buff)[(len < _fd[index].buffLen ? len : _fd[index].buffLen-1)] = '\0';
	}

	TRACE_U(("WSCdbPgsqlRecord::setValue end"));
}

void WSCdbPgsqlRecord::getError()
{
	TRACE_U(("WSCdbPgsqlRecord::getError start"));

	strcpy((char*)_szSqlState, "REC01");
	memset(_szErrorMsg, '\0', sizeof(_szErrorMsg));
	strncpy((char*)_szErrorMsg, PQerrorMessage(_pDB->_hconn), sizeof(_szErrorMsg)-1);

	TRACE_U(("WSCdbPgsqlRecord::getError State:%s messgae:%s", _szSqlState, _szErrorMsg));
	TRACE_U(("WSCdbPgsqlRecord::getError end"));
}

void WSCdbPgsqlRecord::getErrorMsg(char* szState, char* szMsg)
{
	TRACE_U(("WSCdbPgsqlRecord::getErrorMsg start"));

	strcpy(szState, (char*)_szSqlState);
	strcpy(szMsg, (char*)_szErrorMsg);

	TRACE_U(("WSCdbPgsqlRecord::getErrorMsg start"));
}
char* WSCdbPgsqlRecord::makeINSERT()
{
	static WSCstring str;
	str = "insert into ";
	str << _tableName;
	str << "(";
	int i;
	for(i = 1; i < _Cols; i++) {
		str << (char*)_fd[i].colName;
		if(i < _Cols-1) {
			str << ",";
		}
	}
	str << ") values(";
	for(i = 1; i < _Cols; i++) {
		str << getSqlString(i);
		if(i < _Cols-1) {
			str << ",";
		}
	}
	str << ")";
	TRACE_U(("makeINSERT:%s", str.getString()));
	return str.getString();
}
char* WSCdbPgsqlRecord::makeUPDATE()
{
	static WSCstring str;

	str = "update ";
	str << _tableName;
	str << " set ";
	for(int i = 1; i < _Cols; i++) {
		str << (char*)_fd[i].colName;
		str << "=";
		str << getSqlString(i);
		if(i < _Cols-1) {
			str << ",";
		}
	}
	str << " where oid = ";
#ifndef BINARY
	str << (char*)_fd[0].buff;
#else
	str << *((long*)_fd[0].buff);
#endif
	TRACE_U(("makeUPDATE:%s", str.getString()));
	return str.getString();
}
char*  WSCdbPgsqlRecord::makeDELETE()
{
	static WSCstring str;

	str = "delete from ";
	str << _tableName;
	str << " where oid = ";
#ifndef BINARY
	str << (char*)_fd[0].buff;
#else
	str << *((long*)_fd[0].buff);
#endif
	TRACE_U(("makeDELETE:%s", str.getString()));
	return str.getString();
}

void WSCdbPgsqlRecord::getTableName(char* p, char* tbl, int size)
{
	char buf[7];
	buf[6] = '\0';
	*tbl = '\0';
	for(; *p != '\0'; p++) {
		if(*p != ' ') {
			continue;
		}
		strncpy(buf, p, 6);
		if(WSGFstricmp(buf, " FROM ") == 0) {
			int j = 0;
			char* s1 = p + 6;
			for(j = 0; (*s1 != '\0') &&  j < size; s1++) {
				if(*s1 == ' ') {
					if(tbl[0] != '\0') {
						tbl[j] = '\0';
						break;
					}
//				} else {
				} else if(*s1 != '(' && *s1 != ')') {
					tbl[j++] = *s1;
				}
			}
			break;
		}
	}
}
int WSCdbPgsqlRecord::getBufflen(int type)
{
	if(type == PG_TYPE_BPCHAR) {
		return 32767;
	} else
	if(type == PG_TYPE_NUMERIC) {
		// SQL_NUMERIC, SQL_DECIMAL
		return 11 + 1;
	} else 
	if(type == PG_TYPE_INT4) {
		// SQL_INTEGER long int
		return 11 + 1;
	} else
	if(type == PG_TYPE_INT2) {
		// SQL_SMALLINT short int
		return 6 + 1;
	} else
	if(type == PG_TYPE_FLOAT8) {
		// SQL_FLOAT, SQL_DOUBLE float
		return 22 + 1;
	} else
	if(type == PG_TYPE_FLOAT4) {
		// SQL_REAL REAL
		return 13 + 1;
	} else
	if(type == PG_TYPE_DATE) {
		// SQL_TYPE_DATE
		return 10 + 1;
	} else
	if(type == PG_TYPE_TIME) {
		// SQL_TYPE_TIME
		return 23 + 1;
	} else
	if(type == PG_TYPE_DATETIME) {
		// SQL_TYPE_TIMESTAMP:
		return 23 + 1;
	} else 
	if(type == PG_TYPE_OID) {
		return 10 + 1;
	}
	return 32767;
}
char* WSCdbPgsqlRecord::getSqlString(int i)
{
	static WSCstring str;
	str = "";
	
	if(_fd[i].colType == PG_TYPE_INT2) {
#ifndef BINARY
		str << (char*)_fd[i].buff;
#else
		str << *((short*)_fd[i].buff);
#endif
	} else
	if(_fd[i].colType == PG_TYPE_INT4) {
#ifndef BINARY
		str << (char*)_fd[i].buff;
#else
		str << *((long*)_fd[i].buff);
#endif
	} else
	if(_fd[i].colType == PG_TYPE_FLOAT4) {
#ifndef BINARY
		str << (char*)_fd[i].buff;
#else
		str << *((float*)_fd[i].buff);
#endif
	} else
	if(_fd[i].colType == PG_TYPE_FLOAT8) {
#ifndef BINARY
		str << (char*)_fd[i].buff;
#else
		str << *((double*)_fd[i].buff);
#endif
	} else 
	if(_fd[i].colType == PG_TYPE_NUMERIC){
		str << (char*)_fd[i].buff;
	} else {
		str << "'" << decode((char*)_fd[i].buff) << "'";
	}
	return str.getString();
}
void WSCdbPgsqlRecord::encode(char* buf, char* str)
{
	long encode = WSGIappLocaleSet()->getDefaultEncoding();
	if(_encoding != encode && _encoding != -1) {
		WSCstring s(buf,  _encoding);
		strcpy(str, s.getString(encode));
	TRACE_U(("encode buf:%s\n", buf));
	TRACE_U(("encode str:%s\n", str));
	} else {
		strcpy(str, buf);
	}
}
char* WSCdbPgsqlRecord::decode(char* buf)
{
	long decode = WSGIappLocaleSet()->getDefaultEncoding();
	static WSCstring s;
	s = buf;
	if(_encoding != decode && _encoding != -1) {
	TRACE_U(("decode buf:%s\n", buf));
	TRACE_U(("decode str:%s\n", s.getString(_encoding)));
		return s.getString(_encoding);
	}
	return s.getString();
}
long WSCdbPgsqlRecord::getColTypeConv(long index)
{
	if(_fd[index].colType == PG_TYPE_BPCHAR) {
		return WSCDB_FIELD_TYPE_CHAR;
	} else
	if(_fd[index].colType == PG_TYPE_NUMERIC) {
		// SQL_NUMERIC, SQL_DECIMAL
		return WSCDB_FIELD_TYPE_NUMERIC;
	} else 
	if(_fd[index].colType == PG_TYPE_INT4) {
		// SQL_INTEGER long int
		return WSCDB_FIELD_TYPE_INTEGER;
	} else
	if(_fd[index].colType == PG_TYPE_INT2) {
		// SQL_SMALLINT short int
		return WSCDB_FIELD_TYPE_SMALLINT;
	} else
	if(_fd[index].colType == PG_TYPE_FLOAT8) {
		// SQL_FLOAT, SQL_DOUBLE float
		return WSCDB_FIELD_TYPE_DOUBLE;
	} else
	if(_fd[index].colType == PG_TYPE_FLOAT4) {
		// SQL_REAL REAL
		return WSCDB_FIELD_TYPE_REAL;
	} else
	if(_fd[index].colType == PG_TYPE_DATE) {
		// SQL_TYPE_DATE
		return WSCDB_FIELD_TYPE_DATE;
	} else
	if(_fd[index].colType == PG_TYPE_TIME) {
		// SQL_TYPE_TIME
		return WSCDB_FIELD_TYPE_TIME;
	} else
	if(_fd[index].colType == PG_TYPE_DATETIME) {
		// SQL_TYPE_TIMESTAMP:
		return WSCDB_FIELD_TYPE_TIMESTAMP;
	}
	return WSCDB_FIELD_TYPE_NONE;
}
WSCstring WSCdbPgsqlRecord::getErrorMessage()
{
  WSCstring ret;
  char stat[10];
  char buf[PGSQL_MAX_MESSAGE_LENGTH+1];
  getErrorMsg(stat,buf);
  char buf2[PGSQL_MAX_MESSAGE_LENGTH+1+32];
  sprintf(buf2,"state: %s ,message: %s",stat,buf);
  ret.setString(buf2);
  return ret;
}
