///////////////////////////////////////////////////////////////////////////////
//                                                         
// cgwrap.cc
// ---------
// Cego C wrapper API
// 
// Design and Implementation by Bjoern Lemke               
//                                                         
// (C)opyright 2009-2025 Bjoern Lemke
//
// Description: This is a wrapper module which implements a plain C client interface to the
//              cego database system
//
// Status: CLEAN
//
// 29.06.2024, extensions and stabilization added
//
///////////////////////////////////////////////////////////////////////////////

#include "cgwrap.h"

#include <lfcbase/SetT.h>
#include <lfcbase/Tokenizer.h>
#include <lfcbase/Net.h>
#include <lfcbase/NetHandler.h>
#include <lfcbase/Datetime.h>

#include "CegoDbHandler.h"
#include "CegoDefs.h"
#include "CegoModule.h"

#include <string.h>
#include <stdlib.h>

#define NETMSG_BUFLEN 8192
#define MAXERRMSGLEN 100

#define MODNAME "cgwrap"

char cgerrmsg[MAXERRMSGLEN];
unsigned long modId;

static int setCGValue(CGVal* cgval, const CegoFieldValue& fv);
static void updateModLog(CegoModule* pModule);

static SetT<Chain> modLogSet;

extern "C" {

    void cego_modlog(char* module, unsigned level)
    {
	Chain modEntry = *module;

	if ( level == CG_LOG_ERROR )
	    modEntry += Chain(":ERROR");
	else if ( level == CG_LOG_NOTICE )
	    modEntry += Chain(":NOTICE");
	else if ( level == CG_LOG_DEBUG )
	    modEntry += Chain(":DEBUG");

	modLogSet.Insert(modEntry);
    }

    CGDB* cego_connect(char *servername, 
		       unsigned portno,
		       char *prot,
		       char *tsname, 
		       char *user, 
		       char *pass, 
		       char* logfile)
    {
	CGDB* cgdb = new CGDB;

	if ( servername == 0 )
	{
	    Chain msg = Chain("Server name undefined");
	    strcpy(cgerrmsg, (char*)msg); 
	    return NULL;
	}
	
	if ( logfile == 0 )
	    cgdb->pModule = new CegoModule();
	else
	    cgdb->pModule = new CegoModule(logfile, Chain());

	updateModLog(cgdb->pModule);

	Net n ( NETMSG_BUFLEN, NETMNG_SIZEBUFLEN, NETMNG_MAXSENDLEN );
	
	Chain serverName( servername );

	CegoDbHandler::ProtocolType protType;

	if ( prot == 0 )
	{
	    protType = CegoDbHandler::FASTSERIAL;
	}
	else if ( strcmp(prot, "fastserial") == 0 )
	{
	    protType = CegoDbHandler::FASTSERIAL;
	}
	else if ( strcmp(prot, "serial") == 0 )
	{
	    protType = CegoDbHandler::SERIAL;
	}
	else if ( strcmp(prot, "xml") == 0)
	{
	    protType = CegoDbHandler::XML;
	}
	else
	{
	    Chain msg = Chain("Invalid protocol ") + Chain(prot);
	    strcpy(cgerrmsg, (char*)msg); 
	    return NULL;
	}

	try
	{    
	    cgdb->pNet = n.connect(serverName, portno);
	    cgdb->host = serverName;
	    cgdb->port = portno;
	}
	catch ( Exception e )
	{
	    Chain msg = Chain("Cannot connect to server ") + serverName + Chain(" on port ") + Chain(portno);
	    strcpy(cgerrmsg, (char*)msg); 
	    return NULL;
	}
		
	cgdb->pDB = new CegoDbHandler(cgdb->pNet, protType, cgdb->pModule);
	
	Chain tableSet(tsname);
	Chain dbUser(user);
	Chain dbPWD(pass);
	
	CegoDbHandler::ResultType res = cgdb->pDB->requestSession(tableSet, dbUser, dbPWD);
	if ( res != CegoDbHandler::DB_OK )
	{
	    Chain msg = cgdb->pDB->getMsg();
	    strcpy(cgerrmsg, (char*)msg); 
	    return NULL;
	}
	
	return cgdb;
    }

    int cego_disconnect (CGDB* cgdb)
    {
	if ( cgdb )
	{
	    cgdb->pModule->log(modId, Logger::DEBUG, Chain("Disconnecting ..."));
	    
	    cgdb->pDB->closeSession();
	    delete cgdb->pDB;
	    delete cgdb->pNet;
	    delete cgdb->pModule;
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid connection handle");
	return -1;
    }

    char* cego_server(CGDB* cgdb)
    {
	if ( cgdb )
	    return (char*)cgdb->host;
	strcpy(cgerrmsg, "Invalid connection handle");
	return NULL;
    }

    int cego_port(CGDB* cgdb)
    {
	if ( cgdb )
	    return cgdb->port;
	strcpy(cgerrmsg, "Invalid connection handle");
	return -1;
    }

    char* cego_protocol(CGDB* cgdb)
    {
	if ( cgdb )
	    return (char*)cgdb->pDB->getProtocol();
	strcpy(cgerrmsg, "Invalid connection handle");
	return NULL;
    }
    
    char* cego_dbname(CGDB* cgdb)
    {
	if (cgdb )
	    return (char*)cgdb->pDB->getDbName();
	strcpy(cgerrmsg, "Invalid connection handle");
	return NULL;
    }
    
    char* cego_version(CGDB* cgdb)
    {
	if (cgdb )
	    return (char*)cgdb->pDB->getDbVersion();
	strcpy(cgerrmsg, "Invalid connection handle");
	return NULL;
    }

    char* cego_tableset(CGDB* cgdb)
    {
	if (cgdb )
	    return (char*)cgdb->pDB->getTableSet();
	strcpy(cgerrmsg, "Invalid connection handle");
	return NULL;
    }

    char* cego_user(CGDB* cgdb)
    {
	if (cgdb )
	    return (char*)cgdb->pDB->getUser();
	strcpy(cgerrmsg, "Invalid connection handle");
	return NULL;
    }
    
    CGFetch* cego_allocate_fetch()
    {
	return new CGFetch();
    }

    int cego_free_fetch(CGFetch *cgfetch)
    {
	if ( cgfetch )
	{
	    if ( cgfetch->pSchema )
		delete cgfetch->pSchema;
	    delete cgfetch;
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid fetch handle");
	return -1;
    }

    CGVal* cego_allocate_value(unsigned type, unsigned len, void *val)      
    {	
	CGVal *pVal = (CGVal*)malloc(sizeof(CGVal));
	pVal->type = type;
	pVal->len = len;
	
	switch ( type )
	{
	case CG_INT:
	{
	    int *pI = (int*)malloc(sizeof(int));
	    *pI = *((int*)val);
	    pVal->val = pI;
	    break;
	}
	case CG_VARCHAR:
	case CG_DECIMAL:
	case CG_BIGINT:	    
	{
	    char *s = (char*)malloc(len);
	    strcpy(s, (char*)val);
	    pVal->val = s;
	    break;
	}
	case CG_LONG:
	{
	    long *pL = (long*)malloc(sizeof(long));
	    *pL = *((long*)val);
	    pVal->val = pL;
	    break;
	}
	case CG_BOOL:
	case CG_TINYINT:
	{
	    char *pB = (char*)malloc(sizeof(char));
	    *pB = *((char*)val);
	    pVal->val = pB;
	    break;
	}
	case CG_DATETIME:
	{
	    long long *pL = (long long*)malloc(sizeof(long long));
	    *pL = *((long long*)val);
	    pVal->val = pL;
	    break;
	}
	case CG_FLOAT:
	{
	    float *pF = (float*)malloc(sizeof(float));
	    *pF = *((float*)val);
	    pVal->val = pF;
	    break;
	}
	case CG_DOUBLE:
	{
	    double *pD = (double*)malloc(sizeof(double));
	    *pD = *((double*)val);
	    pVal->val = pD;
	    break;
	}
	case CG_SMALLINT:
	{
	    short *pS = (short*)malloc(sizeof(short));
	    *pS = *((short*)val);
	    pVal->val = pS;
	    break;
	}
	case CG_CLOB:
	case CG_BLOB:
	{	    
	    PageIdType *pB = (PageIdType*)malloc(sizeof(PageIdType));
	    *pB = *((PageIdType*)val);
	    pVal->val = pB;
	    break;
	}
	case CG_NULL:
	{
	    pVal->val = 0;
	    break;	   
	}
	}     
	return pVal;
    }
    
    int cego_free_value(CGVal *pVal)
    {
	if ( pVal )
	{
	    if ( pVal->val )
		free(pVal->val);
	    free(pVal);
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid value handle");
	return -1;	
    }
    
    int cego_num_col(CGFetch* cgfetch)
    {
	if ( cgfetch )
	{
	    if ( cgfetch->isActive )
		return cgfetch->pSchema->Size();
	}
	strcpy(cgerrmsg, "Invalid fetch handle");
	return -1;
    }

    int cego_getpos(CGFetch* cgfetch, char* attr)
    {
	if ( cgfetch )
	{
	    unsigned pos = 0;
	    CegoField *pF = cgfetch->pSchema->First();
	    while ( pF )
	    {
		if ( pF->getAttrName() == Chain(attr) )
		    return pos;
		pF = cgfetch->pSchema->Next();
		pos++;
	    }
	}
	strcpy(cgerrmsg, "Invalid fetch handle");
	return -1;
    }

    char* cego_getcolname(CGFetch* cgfetch, unsigned pos)
    {
	if ( cgfetch )
	{
	    if ( cgfetch->pSchema->Size() < pos )
		return NULL;
	    
	    return (*(cgfetch->pSchema))[pos].getAttrName();
	}
	strcpy(cgerrmsg, "Invalid fetch handle");
	return NULL;
    }

    int cego_getcollen(CGFetch* cgfetch, unsigned pos)
    {
	if ( cgfetch )
	{
	    if ( cgfetch->pSchema->Size() < pos )
		return -1;
	    
	    return (*(cgfetch->pSchema))[pos].getLength();
	}
	strcpy(cgerrmsg, "Invalid fetch handle");
	return -1;
    }

    int cego_getcoltype(CGFetch* cgfetch, unsigned pos)
    {
	if ( cgfetch )
	{
	    if ( cgfetch->pSchema->Size() < pos )
		return -1;
	    
	    switch ( (*(cgfetch->pSchema))[pos].getType() )
	    {
	    case INT_TYPE:
		return CG_INT;
	    case LONG_TYPE:
		return CG_LONG;
	    case VARCHAR_TYPE:
		return CG_VARCHAR;
	    case BOOL_TYPE:
		return CG_BOOL;
	    case DATETIME_TYPE:
		return CG_DATETIME;
	    case BIGINT_TYPE:
		return CG_BIGINT;
	    case FLOAT_TYPE:
		return CG_FLOAT;
	    case DOUBLE_TYPE:
		return CG_DOUBLE;
	    case DECIMAL_TYPE:
		return CG_DECIMAL;
	    case SMALLINT_TYPE:
		return CG_SMALLINT;
	    case TINYINT_TYPE:
		return CG_TINYINT;
	    case BLOB_TYPE:
		return CG_BLOB;
	    case CLOB_TYPE:
		return CG_CLOB;
	    case NULL_TYPE:
		return CG_NULL;
	    default:
	    {
		strcpy(cgerrmsg, "Unknown type");
		return -1;
	    }
	    }
	}
	strcpy(cgerrmsg, "Invalid fetch handle");
	return -1;
    }

    CGStmt* cego_prepare (char *stmt)
    {
	if ( stmt )
	{
	    CGStmt *pStmt = new CGStmt;
	    pStmt->pParamList = 0;
	    
	    pStmt->numParams=0;
	    Chain c = Chain(stmt);
	    if ( c.cutTrailing(" ").subChain(1,1) == Chain("?") )
		pStmt->numParams++;
	    
	    Tokenizer tok(Chain(stmt), "?");
	    pStmt->pStmtChunks = new ListT<Chain>;
	    
	    Chain token;
	    
	    tok.nextToken(token);
	    pStmt->pStmtChunks->Insert(token);
	    
	    while ( tok.nextToken(token) == true) 	
	    {
		pStmt->pStmtChunks->Insert(token);
		pStmt->numParams++;
	    }
	    
	    if ( pStmt->numParams > 0 )
		pStmt->pParamList = new ListT<CegoBindValue>;
	    
	    pStmt->isOut = false;
	    return pStmt;
	}
	strcpy(cgerrmsg, "Invalid statement");
	return NULL;
    }
	
    int cego_bind_in (CGStmt * cgstmt, CGVal* cgval, unsigned pos)
    {
	if ( cgstmt && cgval )
	{
	    CegoBindValue bval;
	    bval.cgval = cgval;
	    bval.pos = pos;
	    bval.type = CegoBindValue::BINDIN;
	    CegoBindValue *pBV = cgstmt->pParamList->Find(CegoBindValue(pos));
	    if ( pBV )
	    {
		if ( pBV->cgval )
		    free ( pBV->cgval );
		cgstmt->pParamList->Remove(CegoBindValue(pos));
	    }
	    
	    cgstmt->pParamList->Insert(bval);
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid statement or value handle");
	return -1;
    }
    
    int cego_bind_out (CGStmt * cgstmt, CGVal* cgval, unsigned pos)
    {
	if ( cgstmt && cgval )
	{
	    CegoBindValue bval;
	    bval.cgval = cgval;
	    bval.pos = pos;
	    bval.type = CegoBindValue::BINDOUT;
	    cgstmt->pParamList->Insert(bval);
	    cgstmt->isOut = true;
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid statement or value handle");
	return -1;
    }
	
    int cego_bind_inout (CGStmt * cgstmt, CGVal* cgval, unsigned pos)
    {
	if ( cgstmt && cgval )
	{
	    CegoBindValue bval;
	    bval.cgval = cgval;
	    bval.pos = pos;
	    bval.type = CegoBindValue::BINDINOUT;
	    cgstmt->pParamList->Insert(bval);
	    cgstmt->isOut = true;
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid statement or value handle");
	return -1;
    }
        
    int cego_execute (CGDB *cgdb, CGStmt* cgstmt, CGFetch* cgfetch)
    {

	if ( cgdb && cgstmt )
	{
	    cgdb->pModule->log(modId, Logger::DEBUG, Chain("Executing stmt"));
	    
	    Chain stmtString;
	    if ( cgstmt->numParams == 0 )
	    {
		stmtString = *(cgstmt->pStmtChunks->First());
	    }
	    else
	    {	
		unsigned pos=1;
		
		Chain* pChunk = cgstmt->pStmtChunks->First();
		
		if ( cgstmt->numParams == cgstmt->pStmtChunks->Size() )
		{
		    stmtString = Chain(":p") + Chain(pos) + *pChunk;   
		    pos++;
		}
		else
		{
		    stmtString = *pChunk;
		}
			
		pChunk = cgstmt->pStmtChunks->Next();
		while ( pChunk )
		{    
		    CegoBindValue* pParam = cgstmt->pParamList->Find( CegoBindValue(pos) );
		    
		    if ( pParam )
		    {
			if ( pParam->type == CegoBindValue::BINDIN || pParam->type == CegoBindValue::BINDINOUT )
			{
			    Chain val;
			    
			    switch ( pParam->cgval->type )
			    {
			    case CG_VARCHAR:
			    {
				val = Chain("'") + Chain((char*)pParam->cgval->val) + Chain("'");
				break;
			    }
			    case CG_INT:
			    {
				val = Chain(*(int*)pParam->cgval->val);
				break;
			    }   
			    case CG_LONG:
			    {
				val = Chain(*(long*)pParam->cgval->val);
				break;
			    }
			    case CG_BOOL:
			    {
				if ( *(char*)pParam->cgval->val == 0 )			      
				    val = Chain("false");
				else
				    val = Chain("true");
				break;
			    }
			    case CG_DATETIME:
			    {
				long long ll;
				memcpy(&ll, pParam->cgval->val, sizeof(long long));
				if ( ll == 0 )
				{
				    val = Chain("sysdate");
				}
				else
				{
				    Datetime dt ( ll );
				    val = Chain("scandate('") + DEFAULTDATETIMEFORMAT1 + Chain("','") + dt.asChain("%d.%m.%Y %H:%M:%S") + Chain("')");
				}
				break;
			    }
			    case CG_BIGINT:
			    {
				val = Chain("(bigint)") + Chain((char*)pParam->cgval->val);
				break;
			    }
			    case CG_FLOAT:
			    {
				val = Chain(*(float*)pParam->cgval->val);
				break;
			    }
			    case CG_DOUBLE:
			    {
				val = Chain("(double)") + Chain(*(double*)pParam->cgval->val);
				break;
			    }
			    case CG_DECIMAL:
			    {
				val = Chain("(decimal)") + Chain((char*)pParam->cgval->val);
				break;
			    }
			    case CG_SMALLINT:
			    {
				short s = *(short*)pParam->cgval->val;
				val = Chain((int)s);
				break;
			    }
			    case CG_TINYINT:
			    {
				char c = *(char*)pParam->cgval->val;
				val = Chain((int)c);
				break;
			    }
			    case CG_BLOB:
			    {
				PageIdType pageId = *(PageIdType*)(pParam->cgval->val);			    
				val = Chain("blob [") + Chain(pageId) + Chain("]");
				break;
			    }
			    case CG_CLOB:
			    {
				PageIdType pageId = *(PageIdType*)(pParam->cgval->val);			    
				val = Chain("clob [") + Chain(pageId) + Chain("]");
				break;
			    }
			    case CG_NULL:
			    {
				val = Chain("null");
				break;
			    }
			    }	    
			    stmtString += val + *pChunk; 
			}
			else
			{
			    stmtString += Chain(":p") + Chain(pos) + *pChunk;
			}
			pChunk = cgstmt->pStmtChunks->Next();
		    }
		    else
		    {
			Chain msg = Chain("Missing parameter at position ") + Chain(pos);
			strcpy(cgerrmsg, (char*)msg);
			return -1;		   
		    }
		    pos++;
		}
	    }
	    
	    // cout << "Executing stmt <" << stmtString << endl;
	    
	    if (  cego_query(cgdb, (char*)stmtString, cgfetch) != 0 )
	    {
		cgdb->pModule->log(modId, Logger::LOGERR, Chain("Query failed"));	
		return -1;	    
	    }
	    cgdb->pModule->log(modId, Logger::DEBUG, Chain("Query ok"));	
	    
	    if ( cgstmt->isOut )
	    {
		ListT<CegoProcVar> outParamList;
		CegoFieldValue retValue;
		
		cgdb->pDB->getProcResult(outParamList, retValue);
		
		CegoBindValue* pParam = cgstmt->pParamList->First();
		while ( pParam )
		{
		    if ( pParam->type == CegoBindValue::BINDOUT || pParam->type == CegoBindValue::BINDINOUT )
		    {		
			CegoProcVar* pVar = outParamList.Find(CegoProcVar(Chain("p") + Chain(pParam->pos)));
			if ( pVar )
			{
			    setCGValue ( pParam->cgval, pVar->getValue()); 
			}
			else if ( pParam->pos == 1 )
			{
			    setCGValue ( pParam->cgval, retValue); 
			}		    
		    }
		    pParam = cgstmt->pParamList->Next();
		}
	    }
	    cgdb->pModule->log(modId, Logger::DEBUG, Chain("Query returns 0"));	
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid connection or statement handle");
	return -1;
    }

    int cego_free_stmt(CGStmt* cgstmt)
    {
	if ( cgstmt )
	{
	    if ( cgstmt->pParamList )
	    {
		CegoBindValue *pBindValue = cgstmt->pParamList->First();
		while ( pBindValue )
		{
		    if ( pBindValue->cgval )
		    {
			cego_free_value(pBindValue->cgval);
			pBindValue->cgval = 0;
		    }
		    pBindValue = cgstmt->pParamList->Next();
		}
		delete cgstmt->pParamList;
	    }
	    if ( cgstmt->pStmtChunks )
		delete cgstmt->pStmtChunks;
	    delete cgstmt;
	    
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid statement handle");
	return -1;
    }
    
    int cego_query (CGDB *cgdb, const char *stmt, CGFetch* cgfetch)
    {
	if ( cgdb && stmt )
	{
	    CegoDbHandler::ResultType res = cgdb->pDB->reqQueryOp(Chain(stmt));
	    
	    if ( res == CegoDbHandler::DB_ERROR )
	    {
		if ( cgfetch )
		    cgfetch->isActive = false;

		Chain msg = cgdb->pDB->getMsg();
		strcpy(cgerrmsg, (char*)msg);
		return -1;
	    }
	    else if ( res == CegoDbHandler::DB_OK )
	    {
		if ( cgfetch )
		    cgfetch->isActive = false;
		return 0;
	    }
	    else if ( res == CegoDbHandler::DB_INFO )
	    {
		if ( cgfetch )
		    cgfetch->isActive = false;
		return 0;
	    }
	    else if ( res == CegoDbHandler::DB_FIN )
	    {
		if ( cgfetch )
		    cgfetch->isActive = false;
		return 0;
	    }
	    else if ( res == CegoDbHandler::DB_DATA )
	    {
		if ( cgfetch != NULL )
		{
		    cgfetch->pSchema = new ListT<CegoField>;
		    cgfetch->cgdb = cgdb;
		    cgfetch->isActive = true;
		    *(cgfetch->pSchema) = cgdb->pDB->getSchema();
		    
		    return 0;
		}
		else
		{
		    strcpy(cgerrmsg, "Invalid fetch handle");
		    return -1;
		}
	    }
	    else
	    {
		strcpy(cgerrmsg, "Invalid result from server");
		return -1;
	    }	    
	}
	
	strcpy(cgerrmsg, "Invalid connection or statement handle");
	return -1;
    }

    int cego_isfetchable (CGFetch* cgfetch)
    {
	if ( cgfetch )
	{
	    if ( cgfetch->isActive )
		return 1;
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid fetch handle");
	return -1;	
    }
    
    unsigned long cego_getaffected(CGDB *cgdb)
    {
	if ( cgdb )
	    return cgdb->pDB->getAffected();
	strcpy(cgerrmsg, "Invalid connection handle");
	return -1;	
    }
    
    int cego_fetch (CGFetch *cgfetch, CGVal cgval[], unsigned numCol)
    {
	if ( cgfetch )
	{
	    if ( cgfetch->isActive == false )
		return 0;
	    
	    CegoDbHandler::ResultType res;
	    
	    ListT<CegoFieldValue> fvl;
	    if ( ( res = cgfetch->cgdb->pDB->receiveTableData(*(cgfetch->pSchema), fvl) ) == CegoDbHandler::DB_DATA )
	    {
		unsigned pos=0;
		CegoFieldValue *pFV = fvl.First();
		while ( pFV )
		{
		    if ( pos >= numCol )
		    {
			strcpy(cgerrmsg, "Column number exceeded");
			return -1;
		    }
		    setCGValue(&cgval[pos], *pFV);
		    pFV = fvl.Next();
		    pos++;
		}
		fvl.Empty();
		return pos;
	    }
	    else
	    {	    
		cgfetch->isActive = false;
		return 0;
	    }
	}
	strcpy(cgerrmsg, "Invalid fetch handle");
	return -1;	
    }

    int cego_abort (CGFetch *cgfetch)
    {
	if ( cgfetch )
	{
	    // if no more active, we can return
	    if ( cgfetch->isActive == false )
		return 0;
	    
	    cgfetch->cgdb->pDB->abortQuery();
	    cgfetch->isActive = false;
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid fetch handle");
	return -1;
    }

    int cego_reset (CGFetch *cgfetch)
    {
	if ( cgfetch )
	{
	    if ( cgfetch->isActive == false )
	    {
		strcpy(cgerrmsg, "No active fetch handle");
	    	return -1;
	    }
	    cgfetch->cgdb->pDB->resetQuery();
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid fetch handle");
	return -1;
    }

    int cego_putblob (CGDB *cgdb, CGBlob* cgblob)
    {
	if ( cgdb && cgblob )
	{
	    CegoBlob blob;
	    blob.allocate(cgblob->len);
	    blob.reset();
	    blob.putChunk(cgblob->buf, cgblob->len);
	    
	    if ( cgdb->pDB->putBlob(blob) != CegoDbHandler::DB_OK)
	    {
		Chain msg = cgdb->pDB->getMsg();
		strcpy(cgerrmsg, (char*)msg);		
		return -1;
	    }
	    cgblob->pageId = blob.getPageId();
	    
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid connection or blob handle");
	return -1;	
    }

    int cego_getblob (CGDB *cgdb, CGBlob* cgblob)
    {
	if ( cgdb && cgblob )
	{
	    CegoBlob blob(cgblob->pageId);
	    if ( cgdb->pDB->getBlob(blob) != CegoDbHandler::DB_OK)
	    {
		Chain msg = cgdb->pDB->getMsg();
		strcpy(cgerrmsg, (char*)msg);
		return -1;
	    }
	    
	    if ( cgblob->len < (unsigned)blob.getSize() )
	    {
		cgblob->buf = (unsigned char*)realloc( cgblob->buf, blob.getSize());
		cgblob->len = blob.getSize();
	    }
	    blob.reset();
	    blob.nextChunk(blob.getSize());
	    memcpy( cgblob->buf, blob.getChunkPtr(), blob.getSize());
	    
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid connection or blob handle");
	return -1;	
    }

    int cego_delblob (CGDB *cgdb, CGBlob* cgblob)
    {
	if ( cgdb && cgblob )
	{
	    CegoBlob blob(cgblob->pageId);
	    if ( cgdb->pDB->delBlob(blob) != CegoDbHandler::DB_OK)
	    {
		Chain msg = cgdb->pDB->getMsg();
		strcpy(cgerrmsg, (char*)msg);
		return -1;
	    }
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid connection or blob handle");
	return -1;
    }

    int cego_putclob (CGDB *cgdb, CGClob* cgclob)
    {
	if ( cgdb && cgclob )
	{
	    CegoClob clob;
	    clob.allocate(cgclob->len);
	    clob.reset();
	    clob.putChunk(cgclob->buf, cgclob->len);
	    if ( cgdb->pDB->putClob(clob) != CegoDbHandler::DB_OK)
	    {
		Chain msg = cgdb->pDB->getMsg();
		strcpy(cgerrmsg, (char*)msg);
		return -1;
	    }
	    cgclob->pageId = clob.getPageId();	    
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid connection or clob handle");
	return -1;
    }
	
    int cego_getclob (CGDB *cgdb, CGClob* cgclob)
    {
	if ( cgdb && cgclob )
	{
	    CegoClob clob(cgclob->pageId);
	    if ( cgdb->pDB->getClob(clob) != CegoDbHandler::DB_OK)
	    {
		Chain msg = cgdb->pDB->getMsg();
		strcpy(cgerrmsg, (char*)msg);		
		return -1;
	    }
	    if ( cgclob->len < (unsigned)clob.getSize() )
	    {
		cgclob->buf = (char*)realloc( cgclob->buf, clob.getSize());
		cgclob->len = clob.getSize();
	    }
	    clob.reset();
	    clob.nextChunk(clob.getSize());
	    memcpy( cgclob->buf, clob.getChunkPtr(), clob.getSize());    
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid connection or clob handle");
	return -1;
    }

    int cego_delclob (CGDB *cgdb, CGClob* cgclob)
    {
	if ( cgdb && cgclob )
	{
	    CegoClob clob(cgclob->pageId);
	    if ( cgdb->pDB->delClob(clob) != CegoDbHandler::DB_OK)
	    {
		Chain msg = cgdb->pDB->getMsg();
		strcpy(cgerrmsg, (char*)msg);		
		return -1;
	    }
	    return 0;
	}
	strcpy(cgerrmsg, "Invalid connection or clob handle");
	return -1;
    }    

    char* cego_error()
    {
	return cgerrmsg;
    }
}

/* private functions */

static int setCGValue(CGVal* cgval, const CegoFieldValue& fv)
{
    if ( cgval->val )
	free ( cgval->val );

    switch ( fv.getType() )
    {
    case VARCHAR_TYPE:
    {
	cgval->type = CG_VARCHAR;
	Chain val = fv.valAsChain();
	cgval->val = new char[val.length()];
	memcpy((char*)cgval->val, (char*)val, val.length());
	cgval->len=val.length();
	return 0;
    }
    case INT_TYPE:
    {
	cgval->type = CG_INT;
	cgval->val = new int;
	memcpy((int*)cgval->val, (int*)fv.getValue(), sizeof(int));
	cgval->len=sizeof(int);
	return 0;
    }   
    case LONG_TYPE:
    {
	cgval->type = CG_LONG;
	cgval->val = new long;
	memcpy((long*)cgval->val, (long*)fv.getValue(), sizeof(long long));
	cgval->len=sizeof(long long);
	return 0;
    }   
    case BOOL_TYPE:
    {
	cgval->type = CG_BOOL;
	cgval->val = new char;
	memcpy((char*)cgval->val, (char*)fv.getValue(), sizeof(char));
	cgval->len=sizeof(char);
	return 0;
    }   
    case DATETIME_TYPE:
    {
	cgval->type = CG_DATETIME;
	cgval->val = new long long;
	memcpy((long long*)cgval->val, (long long*)fv.getValue(), sizeof(int));
	cgval->len=sizeof(long long);
	return 0;
    }   
    case FLOAT_TYPE:
    {
	cgval->type = CG_FLOAT;
	cgval->val = new float;
	memcpy((float*)cgval->val, (float*)fv.getValue(), sizeof(float));
	cgval->len=sizeof(float);
	return 0;
    }   
    case DOUBLE_TYPE:
    {
	cgval->type = CG_DOUBLE;
	cgval->val = new double;
	memcpy((double*)cgval->val, (double*)fv.getValue(), sizeof(double));
	cgval->len=sizeof(double);
	return 0;
    }   
    case BIGINT_TYPE:
    {
	cgval->type = CG_BIGINT;
	Chain val = fv.valAsChain();
	cgval->val = new char[val.length()];
	memcpy((char*)cgval->val, (char*)val, val.length());
	cgval->len=val.length();
	return 0;
    }	
    case DECIMAL_TYPE:
    {
	cgval->type = CG_DECIMAL;
	Chain val = fv.valAsChain();
	cgval->val = new char[val.length()];
	memcpy((char*)cgval->val, (char*)val, val.length());
	cgval->len=val.length();
	return 0;
    }
    case SMALLINT_TYPE:
    {
	cgval->type = CG_SMALLINT;
	cgval->val = new short;
	memcpy((short*)cgval->val, (short*)fv.getValue(), sizeof(short));
	cgval->len=sizeof(short);
	return 0;
    }   
    case TINYINT_TYPE:
    {
	cgval->type = CG_TINYINT;
	cgval->val = new char;
	memcpy((char*)cgval->val, (char*)fv.getValue(), sizeof(char));
	cgval->len=sizeof(char);
	return 0;
    }   
    case BLOB_TYPE:
    {
	cgval->type = CG_BLOB;
	cgval->val = malloc ( 2 * sizeof(int));
	memcpy((int*)cgval->val, (int*)fv.getValue(), 2 * sizeof(int));
	cgval->len= 2 * sizeof(int);
	return 0;	
    }
    case CLOB_TYPE:
    {
	cgval->type = CG_CLOB;
	cgval->val = malloc ( 2 * sizeof(int));
	memcpy((int*)cgval->val, (int*)fv.getValue(), 2 * sizeof(int));
	cgval->len= 2 * sizeof(int);
	return 0;	
    }
    case NULL_TYPE:
    {
	cgval->val = NULL;
	cgval->len=0;
	return 0;
    } 
    default:   
    // case PAGEID_TYPE:
    {
	// not supported
	return -1;
    }
    }
}

static void updateModLog(CegoModule* pModule)
{
    Chain *pMod = modLogSet.First();
    while ( pMod )
    {
	Tokenizer tok(*pMod, ":");

	Chain module;
	Chain levelString;
	
	tok.nextToken(module);
	tok.nextToken(levelString);

	Logger::LogLevel level = Logger::NONE;
	
	if ( levelString == Chain("NOTICE") )
	    level = Logger::NOTICE;
	else if ( levelString == Chain("ERROR") )
	    level = Logger::LOGERR;
	else if ( levelString == Chain("DEBUG") )
	    level = Logger::DEBUG;
		
	if ( module == Chain("ALL") )
	{
	    int mapSize = pModule->getMapSize();
	    for ( int i=0; i<mapSize; i++)
	    {	       
		pModule->logModule(i, pModule->getModName(i), level);
	    }
	}
	else
	{
	    modId = pModule->getModId(module);
	    pModule->logModule(modId, module, level);
	}

	pMod = modLogSet.Next();
    }
}
