/* $Header:   V:\vcs\dams50\damgenrc\indy\dgensta.cpv   1.2   08 Mar 2011 15:41:54   GASHFORD  $ */
/*
	OMNIS Studio - Generic DAM statement object
	Copyright (C) TigerLogic Corporataion 2010

FILENAME:	dgensta.cpp

DESCRIPTION:	Implements the Generic DAM statement functionality

DETAILS:	
	
*/
/* Changes
Date			Edit		Fault				Description
04-Mar-26	gra1733						Added dConvert(pParamNum)
*/

#include "dgenobj.hi"
#include "dgensta.hi"

//Statement method parameters
ECOparam statementParamMethod1[] = 
{
	5100, fftInteger, 0, 0
};

//Statement properties
ECOproperty	statementProps[] =
{
	STAT_OBJ_PROPS,
	cGenStatementProp1,		cGenStatementProp1,			fftConstant,			0, 0, preDatesubF, preDatesubL,
	cGenStatementProp2,		cGenStatementProp2,			fftNumber,				0, 0, 0, 0
};

//Statement methods
ECOmethodEvent statementFuncs[] = 
{
	STAT_OBJ_FUNCS,
	cGenStatementMethod1,	cGenStatementMethod1, fftBoolean, 1, statementParamMethod1, 0, 0,
};

//	##################################################################
//	PURPOSE:	Derived statement object constructor
//						(Parent session object supplied)
//	RETURN:		None
//	DETAIL:		Sets initial values for properties and other attributes
tqfGenericStatementObj::tqfGenericStatementObj()
{
	mStatement = NULL;
	
	if (getSession()) getSession()->debugMsg(3,"Statement object created\n"); 

	mProperty1 = preDatesubDate1980;
	mProperty2 = 3.14159265;
}

//	##################################################################
//	PURPOSE:	Statement object destructor
//	RETURN:		None
//	DETAIL:
tqfGenericStatementObj::~tqfGenericStatementObj()
{
	dClearStatement();
	dDropStatement();
}

//	##################################################################
//	PURPOSE:	Allocate a statement handle
//	RETURN:		Boolean (qtrue for success)
//	DETAIL:
qbool tqfGenericStatementObj::allocStatement()
{
	qbool rtnStatus = qfalse;

	if (mStatement == NULL)//Done once for each statement only
	{
		//Allocate a statement handle here
		tqfGenericDAMObj *session = (tqfGenericDAMObj *)getSession();
		mErrorInfo.setConnectionHandle(session->mConnection);
	}
	rtnStatus = qtrue;
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Prepare statement text
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:
qbool tqfGenericStatementObj::dPrepare(qchar *pSQLText, qlong pSQLTextLen)
{ 
	qbool rtnStatus = qfalse;
	tqfGenericDAMObj *session = (tqfGenericDAMObj *)getSession();
	void *connection = session->mConnection;

	getSession()->debugMsg(3, "Prepare: %s\n", (char *)pSQLText);

	do {
		if (connection == NULL) break;
		if (pSQLText == NULL) break;
		if (allocStatement() == qfalse) break;

		unicodeText uSQLText(pSQLText, pSQLTextLen, kSessionEncodingUtf32);
		const char* query = (const char*)uSQLText.convToEncoding(getSession()->getApiEncoding()); GDIignore(query);
		//Add implentation-specific code to prepare the statement here

		rtnStatus = qtrue;
	} while (0);

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Execute statement
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:
qbool tqfGenericStatementObj::dExecute()
{
	tqfGenericDAMObj *session = (tqfGenericDAMObj *)getSession();
	void *connection = session->mConnection;
	qbool rtnStatus = qfalse;

	getSession()->debugMsg(3, "Executing statement\n");

	do {
		if (connection == NULL) break;

		//Implementation executes the prepared statement here
		rtnStatus = qtrue;
	} while (0);

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Directly executes a SQL statement
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		
qbool	tqfGenericStatementObj::dExecDirect(qchar *pSQLText, qlong pSQLTextLen)
{			//EXAMPLE IMPLEMENTATION
	tqfGenericDAMObj *session = (tqfGenericDAMObj *)getSession();
	void *connection = session->mConnection;
	qbool rtnStatus = qfalse;

	getSession()->debugMsg(3, "ExecDirect: %s\n", (char *)pSQLText);

	do {
		if (connection == NULL) break;
		if (pSQLText == NULL) break;
		if (allocStatement() == qfalse) break;
		
		addBindMarkers(pSQLText, pSQLTextLen);
		unicodeText uCommand(pSQLText, pSQLTextLen, kSessionEncodingUtf32);
		char* command = (char*)uCommand.convToEncoding(getSession()->getApiEncoding()); GDIignore(command);

		//Implementation directly-executes the SQL statement here
		rtnStatus = qtrue;
	} while (0);
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Establish number of rows & columns affected by the last
//						statement to be executed
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		Sets column count, row count and rows affected
qbool tqfGenericStatementObj::dSetResults()
{
	qbool rtnStatus = qfalse;

	do {
		mColumnCount = 0;
		mRowCount = 0;
		mRowsAffected = 0;

		rtnStatus = qtrue;
	} while (0);

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Provide a description of a result column
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:
qbool tqfGenericStatementObj::dDescribeParam(qshort pParamNumber, DAMParamPtr pParam)
{
	qbool rtnStatus = qfalse;

	do {
		char *colName = (char *)"";
		int colType = 0; //server column type id
		int colSize = 0; //size of database column
		int precision = 0; //precision or size limit
		int scale = 0;	//numeric scale

		//Implementation interrogates the specified column of the result-set here

		getSession()->debugMsg(3, "Describing parameter %d: Name=%b, Size=%d\n", pParamNumber, colName, colSize);

		server2Param(pParam, pParamNumber, colType, colSize, precision, scale); //convert from server type to DAM parameter

		unicodeText uColName(colName);
		getSession()->charMapIn((qbyte *)uColName.dataPtr(), uColName.charLength(), pParam->getEncoding()); 
		str255 colNameStr((qshort)uColName.charLength(), uColName.dataPtr());
		pParam->setName(colNameStr);

	} while(0); 
	rtnStatus = qtrue;

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Bind a result column to a parameter buffer
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		Required if the server supports result column binding
qbool tqfGenericStatementObj::dBindColumn(qshort pParamNum, DAMParamPtr pParam)
{
	getSession()->debugMsg(3, "dBindColumn called\n");
	//Implement server result set column binding here
	return qtrue;
}

//	##################################################################
//	PURPOSE:	Fetch a batch of rows from the result set
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:
eFetchStatus tqfGenericStatementObj::dFetch()
{
	eFetchStatus rtnStatus = kFetchOk;
	str255 stmtName; this->getName(stmtName);
	getSession()->debugMsg(3, "%s: dFetch called\n", stmtName.cString());
	
	do {
		//Implement a fetch from the server's pending result set here
		rtnStatus = kFetchFinished;
	} while (0);
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Returns a boolean which indicates if the current row in 
//						the result set returned by the server can be added to the 
//						result set returned to the OMNIS code.
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		Used mainly for meta-data calls, this may be required where 
//						the information in the result set needs coercion before it 
//						can be returned to Omnis
qbool	tqfGenericStatementObj::dCanAddRow()
{
	qbool rtnStatus = qtrue;
	getSession()->debugMsg(3, "dCanAddRow() (returning %x)\n", rtnStatus);
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Returns qtrue if there is another result set pending
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		Where SQL statements generate more than one result set, this
//						function informs the base class that another result set is
//						available. It is called after dFetch() returns kFetchFinished
qbool tqfGenericStatementObj::dMoreResults()
{
	qbool rtnStatus = qfalse;
	getSession()->debugMsg(3, "dMoreResults() (returning %x)\n", rtnStatus);
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Provides conversion from the server's description of a parameter
//						to the DAM interface's parameter structure
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		DAMParam is modified based on the supplied colType, size and
//						other available info
void tqfGenericStatementObj::server2Param(DAMParamPtr pParam, qshort pParamNumber, int colType, int colSize, int precision, int scale)
{										//EXAMPLE IMPLEMENTATION										 
	ffttype bufferType = fftNone;
	qshort bufferSubType = 0;
	eCType omCType = omChar;
	

	if(colSize > MAX_CHUNK_SIZE)	
		colSize = MAX_CHUNK_SIZE;

	tqfGenericDAMObj *session = (tqfGenericDAMObj *)getSession();
	session->mapImplToOmnis(colType, precision, scale, colSize, bufferType, bufferSubType, omCType);

	if (bufferType == fftDate)
		pParam->setSendType(kConvert); //Implementation needs to convert dates into fldvals

	pParam->setBufferType(bufferType);
	pParam->setBufferSubType(bufferSubType);
	pParam->setFieldLen(colSize);	 
	pParam->setCType(omCType);
	pParam->setServerType((qlong)colType); 	
}

//	##################################################################
//	PURPOSE:	Implementation provides conversion of the supplied data
//						into the format expected by the Omnis result set
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		Following a fetch, pParam contains a description of the data
//						and the data will be in the parameter's paramBuffer
qbool tqfGenericStatementObj::dConvertParam(DAMParamPtr pParam, qlong pBatchRow, qshort pParamNum) //gra1733
{		//EXAMPLE IMPLEMENTATION ONLY
	qbool rtnStatus = qtrue;
	getSession()->debugMsg(3, "Converting parameter: %s\n", (pParam->getName()->cString()));

	int colType = pParam->getServerType(); GDIignore(&colType);
	qchar *buffer = (qchar *)pParam->getParamBuffer();	
	qlong bufferLen = (qlong) *(pParam->getParamLenBuffer());
	unicodeText uBuffer(buffer, QCHARLEN(bufferLen), kSessionEncodingUtf32);

	if (pParam->getBufferType()==fftDate) //(Example implementation provided)
	{	
		//tokenize the date string
		char *date = (char *)uBuffer.convToEncoding(kSessionEncodingAnsi); //convert to non-Unicode
		char *tokens[16]; int tokNum = 0;
		tokens[tokNum] = strtok(date,"-/. :");
		do {
			tokens[++tokNum] = strtok(NULL, "-/. :+"); 
		} while ((tokens[tokNum] != NULL) && (tokNum<=15));

		//re-cast the param buffer as a datestamptype (easily big enough)
		datestamptype *datetime = (datestamptype *)buffer;

		//all datetimes start off as the default date
		datestamptype defaultDate = getSession()->getDefaultDate();
		datetime->mYear = defaultDate.mYear; 
		datetime->mMonth = defaultDate.mMonth;
		datetime->mDay = defaultDate.mDay;
		datetime->mHour = 0;
		datetime->mMin = 0;
		datetime->mSec = 0;
		datetime->mHun = 0;
		datetime->mDateOk = qtrue; 
		datetime->mTimeOk = qtrue; 
		datetime->mSecOk = qtrue; 
		datetime->mHunOk = qfalse;

		//The implementation copies and converts tokens into the required datestamp fields here
	}

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Clear the statement object and reset it ready to be re-used
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		Note: This does not drop the statement
qbool tqfGenericStatementObj::dClearStatement()
{
	qbool rtnStatus = qtrue;
	dClearParams(&mInputParams); 

	//Implementation performs any processing necessary free server resources here.
	//Ensure that any resources for prepared (but non-executed) statements are also freed.

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Drop the statement object, freeing any permanent resources
//						allocated to it.
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		This is called by the base class's drop() method, indicating
//						that the object is about to be destructed.
qbool tqfGenericStatementObj::dDropStatement()
{
	qbool rtnStatus = qtrue;
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Return the number of methods defined in the derived statement object
//	RETURN:		Method count
//	DETAIL:		
qshort tqfGenericStatementObj::methodCount()
{
	return sizeof(statementFuncs)/sizeof(statementFuncs[0]);
}

//	##################################################################
//	PURPOSE:	Return the number of properties defined in the derived statement object
//	RETURN:		Property count
//	DETAIL:		
qshort tqfGenericStatementObj::propCount()
{
	return sizeof(statementProps)/sizeof(statementProps[0]);
}

//	##################################################################
//	PURPOSE:	Execute the DAM-specific code necessary to generate a $tables result set
//	RETURN:		Boolean status (qtrue for success)
//	DETAIL:		If successful, this call should generate a pending result set
qbool tqfGenericStatementObj::dTables(qlong pTableType, str255 &pOwner)
{
	qbool rtnStatus = qfalse;
	getSession()->debugMsg(3, "Querying tables\n");
	mTablesTableType = pTableType; //cache for use later

	do {
		//Implement code to obtain a list of server tables/views here
		//Custom SQL may be executed via dPrepare() and dExecute() if required
	} while (0);

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Execute the DAM-specific code necessary to generate a $columns result set
//	RETURN:		Boolean status (qtrue for success)
//	DETAIL:		If successful, this call should generate a pending result set
qbool tqfGenericStatementObj::dColumns(str255 &pTableName)	
{
	qbool rtnStatus = qfalse;
	getSession()->debugMsg(3, "Querying column info for %s\n", pTableName.cString());

	do {
		//Implement code to obtain a list of table column information
		//Custom SQL may be executed via dPrepare() and dExecute() if required
	} while (0);

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Execute the DAM-specific code necessary to generate a $indexes result set
//	RETURN:		Boolean status (qtrue for success)
//	DETAIL:		If successful, this call should generate a pending result set
//						pTableName format is "[schema.]tablename"
qbool tqfGenericStatementObj::dIndexes(str255 &pTableName, qlong pIndexType)
{
	qbool rtnStatus = qfalse;
	getSession()->debugMsg(3,"Querying indexes for %s\n",(char *)pTableName.cString()); 

	do {
		//Implement code to obtain a list of indexes for the specified table
		//Custom SQL may be executed via dPrepare() and dExecute() if required
	} while (0);

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Return the table owner from the current row in the $tables result set
//	RETURN:		pColVal = owner
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'owner' column of the result set
void tqfGenericStatementObj::dTablesGetOwner(EXTfldval &pColVal) 
{
	DAMParamPtr param = mOutputParams.getParam(TABLES_OWNER_NAME);
		
	qbyte *buffer = (qbyte *)param->getParamBuffer();
	qlong nameLen = (qlong)*param->getParamLenBuffer(); 
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Return the table name from the current row in the $tables result set
//	RETURN:		pColVal = name
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'table name' column of the result set
void tqfGenericStatementObj::dTablesGetTableName(EXTfldval &pColVal) 
{
	DAMParamPtr param = mOutputParams.getParam(TABLES_TABLE_NAME);
		
	qbyte *buffer = param->getParamBuffer(); 
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Return the table type from the current row in the $tables result set
//	RETURN:		pColVal = type
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'table type' column of the result set
void tqfGenericStatementObj::dTablesGetTableType(EXTfldval &pColVal) 
{
	preconst tableTypeVal = (mTablesTableType == kStatementServerView)? preStatementServerView : preStatementServerTable;
	pColVal.setConstant(tableTypeVal);
}

//	##################################################################
//	PURPOSE:	Return the table description from the current row in the $tables result set
//	RETURN:		pColVal = description
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'description' column of the result set
void tqfGenericStatementObj::dTablesGetDescription(EXTfldval &pColVal) 
{
	pColVal.setEmpty(fftCharacter,dpDefault);
}

//	##################################################################
//	PURPOSE:	Return the table damInfoRow for the current row in the $tables result set
//	RETURN:		pColVal = row variable
//	DETAIL:		This is called following a fetch and allows the implementation to
//						return any additional information via the 'damInfoRow' column of the result set
void tqfGenericStatementObj::dTablesGetDamInfo(EXTfldval &pColVal) 
{					// EXAMPLE IMPLEMENTATION
	EXTqlist *infoRow = new EXTqlist(listVlen);	//pColVal will deallocate this
	infoRow->clear(listVlen);	
	do {
		str15 colName;											
		RESloadString(gInstLib, 2135, colName); //add three columns to the list
		infoRow->addCol(1,fftCharacter, dpFcharacter, 16, NULL, &colName); //hasindexes
		RESloadString(gInstLib, 2136, colName);
		infoRow->addCol(2,fftCharacter, dpFcharacter, 16, NULL, &colName);	//hasrules
		RESloadString(gInstLib, 2137, colName);
		infoRow->addCol(3,fftCharacter, dpFcharacter, 16, NULL, &colName);	//hastriggers

		infoRow->insertRow();								//add one row to the list
		EXTfldval columnData;								//set column values...
		DAMParamPtr param = mOutputParams.getParam(TABLES_HAS_INDEXES);
		char *buffer = (char *)param->getParamBuffer();
		if (buffer[0] == 1)
			columnData.setBool(preBoolTrue); 
		else 
			columnData.setBool(preBoolFalse); 
		infoRow->putColVal(1,1,columnData);
			
		param = mOutputParams.getParam(TABLES_HAS_RULES);
		buffer = (char *)param->getParamBuffer();
		if (buffer[0] == 1)
			columnData.setBool(preBoolTrue); 
		else 
			columnData.setBool(preBoolFalse); 
		infoRow->putColVal(1,2,columnData);

		param = mOutputParams.getParam(TABLES_HAS_TRIGGERS);
		buffer = (char *)param->getParamBuffer();
		if (buffer[0] == 1)
			columnData.setBool(preBoolTrue); 
		else 
			columnData.setBool(preBoolFalse); 
		infoRow->putColVal(1,3,columnData);
	} while (0);

	pColVal.setList(infoRow, qtrue);	//transfer ownership of infoRow to pColVal
	delete infoRow;										//delete local copy
}

//	##################################################################
//	PURPOSE:	Return the column database or catalog from the current row in the $columns result set
//	RETURN:		pColVal = database or catalog name
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'database' column of the result set
void tqfGenericStatementObj::dColumnsGetDatabaseOrCatalog(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(COLUMNS_DB_NAME);
		
	qbyte *buffer = (qbyte *)param->getParamBuffer(); 
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Return the column owner from the current row in the $columns result set
//	RETURN:		pColVal = owner
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'owner' column of the result set
void tqfGenericStatementObj::dColumnsGetOwner(EXTfldval &pColVal) 
{
	DAMParamPtr param = mOutputParams.getParam(COLUMNS_OWNER_NAME);
		
	qbyte *buffer = (qbyte *)param->getParamBuffer(); 
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Return the column name from the current row in the $columns result set
//	RETURN:		pColVal = Column name
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'column name' column of the result set
void tqfGenericStatementObj::dColumnsGetColumnName(EXTfldval &pColVal) 
{
	DAMParamPtr param = mOutputParams.getParam(COLUMNS_COLUMN_NAME);
		
	qbyte *buffer = param->getParamBuffer(); 
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Return the Omnis data type text from the current row in the $columns result set
//	RETURN:		pColVal = OMNIS data type
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'Omnis data type' column of the result set
void tqfGenericStatementObj::dColumnsGetOmnisDataTypeText(EXTfldval &pColVal) 
{			//EXAMPLE IMPLEMENTATION
	do {
		DAMParamPtr param = mOutputParams.getParam(COLUMNS_DATA_TYPE);
		int serverType = *((int *)param->getParamBuffer());

		param = mOutputParams.getParam(COLUMNS_COL_PREC);
		mCurColPrecision = *((int *)param->getParamBuffer());

		param = mOutputParams.getParam(COLUMNS_COL_SCALE);
		int colScale = *((int *)param->getParamBuffer());

		param = mOutputParams.getParam(COLUMNS_COL_SIZE);
		int colSize = *((int *)param->getParamBuffer());

		eCType omCType;
		tqfGenericDAMObj *session = (tqfGenericDAMObj *)getSession();
		session->mapImplToOmnis(serverType, mCurColPrecision, colScale, colSize, mCurColType, mCurColSubType, omCType); 

		if (mCurColType == fftCharacter)
			mCurColPrecision = colSize; //store for later use

		//Use a callback to get the Omnis data type string
		str255 typeText;
		DAMgetDataTypeText(mCurColType, mCurColSubType, colSize, &typeText); 
		pColVal.setChar(typeText);

	} while (0);
}

//	##################################################################
//	PURPOSE:	Return the Omnis data type numeric value (ffttype)
//	RETURN:		pColVal = Omnis data type
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'Omnis ffttype' column of the result set
void tqfGenericStatementObj::dColumnsGetOmnisDataType(EXTfldval &pColVal)
{
	pColVal.setLong(mCurColType); //stored from earlier
}

//	##################################################################
//	PURPOSE:	Return the Omnis data sub-type numeric value (fdp)
//	RETURN:		pColVal = Omnis data type
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'Omnis ffttype' column of the result set
void tqfGenericStatementObj::dColumnsGetOmnisDataSubType(EXTfldval &pColVal)
{
	pColVal.setLong(mCurColSubType); //stored from earlier
}

//	##################################################################
//	PURPOSE:	Return the SQL data type from the current row in the $columns result set
//	RETURN:		pColVal = SQL data type
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'Data type text' column of the result set
void tqfGenericStatementObj::dColumnsGetSqlDataType(EXTfldval &pColVal) 
{
	DAMParamPtr param = mOutputParams.getParam(COLUMNS_TYPE_NAME);
		
	qbyte* buffer = (qbyte*)param->getParamBuffer(); GDIignore(buffer);
	qlong nameLen = (qlong)*param->getParamLenBuffer(); GDIignore(&nameLen);
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Return the column length from the current row in the $columns result set
//	RETURN:		pColVal = length or precision
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'length' column of the result set
void tqfGenericStatementObj::dColumnsGetLength(EXTfldval &pColVal) 
{
	if (mCurColType == fftCharacter)
		pColVal.setLong(mCurColPrecision); //(set for character columns only)
	else
		pColVal.setLong(0);
}

//	##################################################################
//	PURPOSE:	Return the column scale from the current row in the $columns result set
//	RETURN:		pColVal = scale
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'scale' column of the result set
void tqfGenericStatementObj::dColumnsGetScale(EXTfldval &pColVal) 
{
	pColVal.setLong(mCurColScale); //stored from earlier
}

//	##################################################################
//	PURPOSE:	Return the null state from the current row in the $columns result set
//	RETURN:		pColVal = null state
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'nullable' column of the result set
void tqfGenericStatementObj::dColumnsGetNull(EXTfldval &pColVal) 
{
	DAMParamPtr param = mOutputParams.getParam(COLUMNS_NULLABLE);
	qbyte *buffer = (qbyte *)param->getParamBuffer();
	if (buffer[0] == 1)
		pColVal.setBool(preBoolTrue); 
	else 
		pColVal.setBool(preBoolFalse); 
}

//	##################################################################
//	PURPOSE:	Return the index state from the current row in the $columns result set
//	RETURN:		pColVal = index state
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'is indexed' column of the result set
void tqfGenericStatementObj::dColumnsGetIndex(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(COLUMNS_IS_INDEX);
	qbyte *buffer = (qbyte *)param->getParamBuffer();
	if (buffer[0] == 1)
		pColVal.setBool(preBoolTrue); 
	else 
		pColVal.setBool(preBoolFalse); 
}

//	##################################################################
//	PURPOSE:	Return the primary key state for the current row of the $columns result set
//	RETURN:		pColVal = primary key state
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'primary key' column of the result set
void tqfGenericStatementObj::dColumnsGetPrimaryKey(EXTfldval &pColVal) 
{
	DAMParamPtr param = mOutputParams.getParam(COLUMNS_PRIM_KEY);
	qbyte *buffer = (qbyte *)param->getParamBuffer();
	if (buffer[0] == 1)
		pColVal.setBool(preBoolTrue); 
	else 
		pColVal.setBool(preBoolFalse); 
}

//	##################################################################
//	PURPOSE:	Return the column description from the current row in the $columns result set
//	RETURN:		pColVal = description
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'description' column of the result set
void tqfGenericStatementObj::dColumnsGetDescription(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(COLUMNS_DESC);
		
	qbyte *buffer = param->getParamBuffer(); 
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Return extra information about the $columns column
//	RETURN:		pColVal = row value
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'damInfoRow' column of the result set
void tqfGenericStatementObj::dColumnsGetDamInfo(EXTfldval &pColVal)
{	//SEE dTablesGetDamInfo FOR AN EXAMPLE
	pColVal.setEmpty(fftList,dpDefault);
}

//	##################################################################
//	PURPOSE:	Return the database or catalog name from the current row in the $indexes result set
//	RETURN:		pColVal = database or catalog name
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'database' column of the result set
void tqfGenericStatementObj::dIndexesGetDatabaseOrCatalog(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(INDEXES_DB_NAME);
		
	qbyte *buffer = param->getParamBuffer(); 
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Return the owner from the current row in the $indexes result set
//	RETURN:		pColVal = Owner
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'owner' column of the result set
void tqfGenericStatementObj::dIndexesGetOwner(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(INDEXES_OWNER);
	qbyte *buffer = param->getParamBuffer(); 
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Return the column name from the current row in the $indexes result set
//	RETURN:		pColVal = Column name
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'column name' column of the result set
void tqfGenericStatementObj::dIndexesGetColumnName(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(INDEXES_COL_NAME);
	qbyte *buffer = param->getParamBuffer(); 
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Return the index name from the current row in the $indexes result set
//	RETURN:		pColVal = length or precision
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'index name' column of the result set
void tqfGenericStatementObj::dIndexesGetIndexName(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(INDEXES_IND_NAME);
	qbyte *buffer = param->getParamBuffer(); 
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi);
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Return the unique status from the current row in the $indexes result set
//	RETURN:		pColVal = unique status
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'unique' column of the result set
void tqfGenericStatementObj::dIndexesGetUnique(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(INDEXES_UNIQUE);
	qbyte *buffer = (qbyte *)param->getParamBuffer();
	if (buffer[0] == 1)
		pColVal.setBool(preBoolTrue); 
	else 
		pColVal.setBool(preBoolFalse); 
}

//	##################################################################
//	PURPOSE:	Return the column position from the current row in the $indexes result set
//	RETURN:		pColVal = column position
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'column position' column of the result set
void tqfGenericStatementObj::dIndexesGetColumnPosition(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(INDEXES_COL_POS);
	qlong *longVal = (qlong*)param->getParamBuffer();
	pColVal.setLong(*longVal); 
}

//	##################################################################
//	PURPOSE:	Return the DAMInfoRow column for $indexes
//	RETURN:		void
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'damInfoRow' column of the result set
void tqfGenericStatementObj::dIndexesGetDamInfo(EXTfldval &pColVal)
{	//SEE dTablesGetDamInfo FOR AN EXAMPLE
	pColVal.setEmpty(fftList,dpDefault);
}

//	##################################################################
//	PURPOSE:	Get text suitable for describing the SQL type of a result column of a meta-data method
//
//	RETURN:		void
//
//	DETAIL:		This only needs to support fftCharacter, fftInteger and fftBoolean
void tqfGenericStatementObj::dMetaDataGetSqlDataTypeText(ffttype pType, str255 &pSqlDataTypeText)
{
	qlong resourceNumber = 0;
	switch (pType)
	{
		case fftCharacter:	resourceNumber = 2110; break;
		case fftInteger:		resourceNumber = 2111; break;
		case fftBoolean:		resourceNumber = 2112; break;
	}
	if (!resourceNumber) pSqlDataTypeText[0] = 0;
	else RESloadString(gInstLib, resourceNumber, pSqlDataTypeText);
}

//	##################################################################
//	PURPOSE:	Get field length and nullable info for a $results current row
//	RETURN:		void		
//	DETAIL:		Called by the base class during a call to $results(),
//						dResults is passed each column of the pending result set. 
//						This allows the implementation to modify values if required.
//						dResults() is called before the DAMgetDataTypeText() callback.
qbool	tqfGenericStatementObj::dResults(DAMParamPtr pParam)
{
	qbool rtnStatus = qtrue;
	str255 columnName = *pParam->getName();
	getSession()->debugMsg(3, "dResults called (%s)\n", columnName.cString());
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Get SQL data type text, column length and scale for $results current row
//	RETURN:		pSqlDataTypeText and pColumnLength (optional)		
//	DETAIL:		Called by the base class during a call to $results() after the
//						DAMgetDataTypeText() callback. This function obtains or otherwise
//						computes the SQL data type corresponding to the supplied parameter.
//						The implementation also has final say over the parameter's attributes
//						before the row is added to the results list.
void tqfGenericStatementObj::dGetColumnInfoForResults(DAMParamPtr pParam, str255 &pSqlDataTypeText, qlong &pColumnLength) 
{
	getSession()->debugMsg(3, "dGetColumnInfoForResults called\n");
	pSqlDataTypeText = str255(QTEXT("unknown"));
}

//	##################################################################
//	PURPOSE:	Close statement
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		Frees any server resources associated with the executed statement.
//						Re-execution of the statement should be possible following this call.
qbool tqfGenericStatementObj::dCloseStatement()
{
	qbool rtnStatus = qtrue;
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Set up input parameters at prepare time
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		pInputParams contain initial descriptions of each input bind
//						parameter based on the Omnis bind variables.
//						dProcessPrepareParams() processes each parameter, determining matching SQL
//						type, if and when parameters can be bound and whether conversion is needed.
qbool tqfGenericStatementObj::dProcessPrepareParams(DAMDataPtr pInputParams)
{
	qbool rtnStatus = qfalse;
	getSession()->debugMsg(3, "dProcessPrepareParams called\n");

	do {
		qshort param, numParams = pInputParams->numParams();
		for (param=0; param<numParams; param++)
		{
			DAMParamPtr currParam = pInputParams->getParam(param + 1); GDIignore(currParam);
		}
		rtnStatus = qtrue;
	} while (0);

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Convert or set an input parameter value at execute time
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		Called for any input parameters with a sendType==kConvert.
//						NOT called if the bindType==kBindOmnis or if *paramLenBuffer==DAM_PARAM_IS_NULL.
//						pInputParam has its buffers set with the length & value of the input bind variable at this point.
//						The implementation should use this function to perform conversion
//						of buffered length & value into the format expected by the server.
//						Note: If bindType==kBindPrepare, you cannot clear and reallocate buffers here because
//						the param buffer and bufferLen addresses have aleady been sent to the server.
qbool tqfGenericStatementObj::dSetInputBufferValue(DAMParamPtr pInputParam)
{
	qbool rtnStatus = qtrue;
	getSession()->debugMsg(3, "dSetInputBufferValue called\n");
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Modify input parameters at execute time
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		pInputParams now have their buffers set with the values of the input
//						bind variables. The implementation can use this function to modify
//						values, affect chunking and/or binding of parameters before they are bound
//						to the server (dBindParameter). 
//						Note: Only parameters with a bindType==kBindExecute will be bound passed this point,
//						others will be ignored.
qbool tqfGenericStatementObj::dProcessExecuteParams(DAMDataPtr pInputParams)
{
	getSession()->debugMsg(3, "dProcessExecuteParams called\n");
	return qtrue;
}

//	##################################################################
//	PURPOSE:	Derived support for deferred inlining
//	RETURN:		qtrue/qfalse
//	DETAIL:		This function should perform deferred inlining of bind variable values
//						for any parameters with sendType==kDeferInline.
//						Values to be inlined (especially character & binary values) may need to be escaped 
//						as per the server's syntax requirements. 
qbool tqfGenericStatementObj::dProcessInlines(DAMDataPtr pInputParams)
{
	getSession()->debugMsg(3, "dProcessInlines called\n");
	return qfalse;
}

//	##################################################################
//	PURPOSE:	This allows the DAM to free up any parameter resources it owns, e.g in mDamStorage
//	RETURN:		qtrue/qfalse
//	DETAIL:		The implementation should free any additional resources for each parameter
//						that may have been allocated. Each parameter should remain in a re-usable state
//						following this call. (dropParms() is used to free parameters).
void tqfGenericStatementObj::dClearParams(DAMDataPtr pDAMData)
{
	qshort paramCount, nParams = pDAMData->numParams();
	getSession()->debugMsg(3,"dClearParams called\n"); 

	for (paramCount = 0; paramCount < nParams; paramCount++)
	{
	}
}

//	##################################################################
//	PURPOSE:	Bind a single parameter 
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		The base class calls dBindParameter() and prepare time for any parameters with
//						bindType==kBindPrepare and at execute time where bindType==kBindExecute.
//						The implementation should use this call to bind the parameter's
//						length and value buffers to the server.
qbool tqfGenericStatementObj::dBindParameter(DAMParamPtr currParam, qshort parmNumber)
{
	qbool rtnStatus = qtrue;
	qlong currBufferLen = (qlong) *(currParam->getParamLenBuffer());
	EXTfldval *fval = currParam->getOmnisRef();
	qbool isNull = ((currBufferLen == DAM_PARAM_IS_NULL) || (fval->isNull() == qtrue));  GDIignore(&isNull);

	getSession()->debugMsg(3,"dBindParameter: Binding parameter %x\n",parmNumber); 
	//Implement parameter binding here

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Send the contents of a single input parameter 
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		Called following execution for chunked columns only. (Not called for inlined parameters)
//						The implementation should send the data for the parameter in chunks as defined by the size of
//						the parameter's data buffer. readNextChunk() is used to obtain each successive chunk
//						from the parameter's chunk handle.
qbool	tqfGenericStatementObj::dSendParameter(DAMParamPtr pParam, qshort pParamNum)
{
	qbool rtnStatus = qtrue;
	eChunkState chunkState = kChunkOk;
	qlong chunkLength = pParam->getBufferLen();
	getSession()->debugMsg(3,"dSendParameter called\n"); 

	while (chunkState == kChunkOk)
	{
		 chunkState = pParam->readNextChunk(chunkLength); //also returns the availale chunkLength
		 //if(chunkState != kChunkFailed) Implementation sends a chunk of data to the server here 
	}

	if(chunkState == kChunkFailed)
		rtnStatus = qfalse;

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Set up return columns for output binding/chunking 
//	RETURN:		Status - qtrue/qfalse
//	DETAIL:		After each column in the result set has been described, dProcessOutputColumns()
//						allows the implementation to modify the output parameters containing the 
//						column descriptions, for example: turning off binding and turning on chunking instead.
qbool	tqfGenericStatementObj::dProcessOutputColumns(DAMDataPtr pOutputParams)
{
	qbool rtnStatus = qtrue;
	qshort i;
	DAMParamPtr currParam;
	getSession()->debugMsg(3,"dProcessOutputColumns called\n"); 

	for (i=1; i<= pOutputParams->numParams(); i++)
	{
		currParam = pOutputParams->getParam(i);
	}
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Get (at most) a chunk of data into the parameter's data buffer
//	RETURN:		Chunk status
//	DETAIL:		Called by the base class in order to obtain the next chunk of data for a chunked
//						output parameter/result set column. The implementation should place the data
//						for the next chunk into the parameter's data buffer and assign the buffer length accordingly.
eChunkState	tqfGenericStatementObj::dGetNextChunk(DAMParamPtr pParam, qshort pParamNum)
{
	eChunkState chunkState = kChunkFinished;
	getSession()->debugMsg(3,"dGetNextChunk called\n"); 
	//Implement chunking of result set column data here
	//The base class will handle character mapping of non-Unicode character data upon receipt of each chunk
	return chunkState;
}

//	##################################################################
//	PURPOSE:	Clear statement error
//	RETURN:		Status = qtrue/qfalse		
//	DETAIL:		
void tqfGenericStatementObj::dClearNativeError()
{
	mErrorInfo.clearError();
}

//	##################################################################
//	PURPOSE:	Obtain the native error code and native error text for the last error
//	RETURN:		Error state (kErrorFailed,kErrorOk,kErrorPending)					
//	DETAIL:		If an error is already pending, it is extracted from mErrorInfo and returned
//						via errorCode & pErrorText. Otherwise mErrorInfo uses it's 'connection handle'
//						to obtain error information from the server.
eErrorState tqfGenericStatementObj::dGetNativeError(qlong &errorCode, EXTfldval &pErrorText)
{
	eErrorState errorState = kErrorOk;
	getSession()->debugMsg(3,"dGetNativeError called\n"); 

	if(mErrorInfo.getErrorPending() == qtrue)	
	{	
		errorCode = mErrorInfo.getErrorCode();	//Get the error code
		mErrorInfo.getErrorText(pErrorText);		//Get the error text
		errorState = mErrorInfo.setError();			//Check for/cache enother error	
	}
	else																			
	{
		errorState = mErrorInfo.setError();			//Check for an error
		errorCode = mErrorInfo.getErrorCode();	//Get the error code
		mErrorInfo.getErrorText(pErrorText);		//Get the error text
		if(errorState == kErrorPending)
			errorState = mErrorInfo.setError();		//Check for/cache enother error	 
	}
	return errorState;
}
	
//	##################################################################
//	PURPOSE:	Handles requests for derived method calls
//	RETURN:		State qtrue/qfalse indicating whether dMethodCall handled the request
//	DETAIL:		The funcId previously registered via ECM_GETMETHODNAME is used
//						to identify the method to be called. If the method has a return value, it is
//						assigned to rtnVal and hasRtnVal should be set to qtrue
qbool	tqfGenericStatementObj::dMethodCall(EXTCompInfo* pEci, EXTfldval &rtnVal, qbool &hasRtnVal)
{
	qlong funcId = ECOgetId(pEci);
	qbool rtnStatus = qtrue, funcStatus = qfalse;
	hasRtnVal = qfalse; 

	getSession()->debugMsg(3,"MethodCall with funcId %x\n",funcId);
	
	switch (funcId)
	{
		case cGenStatementMethod1: 
		{
			funcStatus = doMethod1(pEci); //call method implementation here
			break;
		}
		default:
		{ //An invalid method id was passed
			rtnStatus = qfalse;
		}
	}
	
	hasRtnVal = qtrue;
	rtnVal.setLong(funcStatus);
	return rtnStatus;
}	

//	##################################################################
//	PURPOSE:	Derived statement object property support
//	RETURN:		State qtrue/qfalse 
//	DETAIL:		This function returns qtrue if the specified property is assignable 
//						from Omnis, qfalse otherwise.
qbool	tqfGenericStatementObj::dPropertyCanAssign(WPARAM wParam, LPARAM lParam, EXTCompInfo* eci)
{
	qbool rtnStatus = qfalse;
	switch(ECOgetId(eci))
	{			
		case cGenStatementProp1:		
		case cGenStatementProp2:	
		{
			rtnStatus = qtrue;
			break;
		}
		default:			break;
	}
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Derived statement object property support
//	RETURN:		State qtrue/qfalse 
//	DETAIL:		Called when Omnis wishes to set one of the derived statement object's properties
qbool	tqfGenericStatementObj::dSetProperty(WPARAM wParam, LPARAM lParam, EXTCompInfo* eci)
{
	qbool rtnStatus = qfalse;
	mErrorInfo.clearError();													
	// get the externals parameter
	EXTParamInfo* param = ECOfindParamNum( eci, 1 );
	
	if ( param )
	{                             
		// get the fldval from the parameter.
		EXTfldval fval( (qfldval)param->mData );
		switch(ECOgetId(eci))
		{
			case cGenStatementProp1:
			{
				rtnStatus = qtrue;
				mProperty1 = fval.getConstant(preDatesubF,preDatesubL);	
				break;
			}
			case cGenStatementProp2:
			{
				qshort prec;
				fval.getNum(mProperty2,prec,&rtnStatus);	
				break;
			}	
			default:
			{
				rtnStatus = qfalse;
			}	
		}
	}
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Derived statement object property support
//	RETURN:		State qtrue/qfalse 
//	DETAIL:		Called when Omnis requires the value of a derived statement object property
qbool	tqfGenericStatementObj::dGetProperty(WPARAM wParam, LPARAM lParam, EXTCompInfo* eci, EXTfldval &pPropVal)
{
	qbool rtnStatus = qtrue;
	switch(ECOgetId(eci))
	{
		case cGenStatementProp1:
		{
			pPropVal.setConstant(mProperty1); 
			break;
		}
		case cGenStatementProp2:
		{
			pPropVal.setNum(mProperty2);
			break;
		}
		default:
		{
			rtnStatus = qfalse;
		}
	}
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Issue a call to the server to list the remote procedures
//	RETURN:		Status = qtrue/qfalse						
//	DETAIL:		If successful, this call should generate a pending result set
qbool tqfGenericStatementObj::dRpcProcedures(str255 &pOwner)
{
	qbool rtnStatus = qfalse;
	getSession()->debugMsg(3,"dRpcProcedures called\n"); 

	do {
		dClearStatement(); 
		if (allocStatement() == qfalse) break;
		//Implement code to obtain a list of server procedures/functions here
		//Custom SQL may be executed via dPrepare() and dExecute() if required.
		mResultsPending = qfalse;
	} while (0);

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Issue a call to the server to list the parameters of a remote procedure
//	RETURN:		Status = qtrue/qfalse						
//	DETAIL:		If successful, this call should generate a pending result set
qbool tqfGenericStatementObj::dRpcParameters(str255 &pProcedureName)
{
	qbool rtnStatus = qfalse; 
	getSession()->debugMsg(3,"dRpcParameters called\n"); 

	do {
		if (pProcedureName.length() == 0)	break;
		dClearStatement(); 
		if (allocStatement() == qfalse) break;
		//Implement code to obtain a list of RPC parameters here
		//Custom SQL may be executed via dPrepare() and dExecute() if required.
		mResultsPending = qfalse;
	} while (0);

	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Get the database or catalog name from the current row of the $rpcprocedures() result set			
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'database' column of the result set
void tqfGenericStatementObj::dRpcProceduresGetDatabaseOrCatalog(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(PROCEDURES_NAMESPACE);
	qbyte *buffer = (qbyte *)param->getParamBuffer();
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Get the owner name from the current row of the rpc procedures result set			
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'owner' column of the result set
void tqfGenericStatementObj::dRpcProceduresGetOwner(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(PROCEDURES_OWNER);
	qbyte *buffer = (qbyte *)param->getParamBuffer();
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Get the procedure name from the current row of the rpc procedures result set
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'procedure name' column of the result set
void tqfGenericStatementObj::dRpcProceduresGetProcedureName(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(PROCEDURES_NAME);
	qbyte *buffer = (qbyte *)param->getParamBuffer();
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Get the extra information about the procedure 
//	DETAIL:		This is called following a fetch and allows the implementation to
//						returns any additional information about the procedure via a row variable
void tqfGenericStatementObj::dRpcProceduresGetDamInfo(EXTfldval &pColVal)
{	//EXAMPLE IMPLEMENTATION ONLY
	str80 colName;	EXTfldval columnData;
	EXTqlist *infoRow = new EXTqlist(listVlen);	
	infoRow->clear(listVlen);										//Clear list definition

	RESloadString(gInstLib, 2150, colName);			//Add three columns
	infoRow->addCol(1,fftCharacter, dpFcharacter, 16, NULL, &colName);
	RESloadString(gInstLib, 2151, colName);
	infoRow->addCol(2,fftBoolean, dpFcharacter, 3, NULL, &colName); 
	RESloadString(gInstLib, 2152, colName);
	infoRow->addCol(3,fftBoolean, dpFcharacter, 3, NULL, &colName);

	qlong rowNumber = infoRow->insertRow();		GDIignore(&rowNumber); 	//Add one row to the list variable

	DAMParamPtr param = mOutputParams.getParam(PROCEDURES_LANG);
	qbyte* buffer = (qbyte*)param->getParamBuffer();  GDIignore(buffer);
	qlong nameLen = (qlong)*param->getParamLenBuffer(); GDIignore(&nameLen);
	param->setFldVal(columnData, mBatchRow);
	infoRow->putColVal(1,1,columnData);

	param = mOutputParams.getParam(PROCEDURES_ISAGG); 
	qbool isAgg = *(qbool *)param->getParamBuffer();
	columnData.setBool(isAgg + 1);
	infoRow->putColVal(1,2,columnData);

	param = mOutputParams.getParam(PROCEDURES_SECDEF); 
	qbool secDef = *(qbool *)param->getParamBuffer();
	columnData.setBool(secDef + 1);
	infoRow->putColVal(1,3,columnData);

	pColVal.setList(infoRow, qtrue);	//transfer ownership of infoRow to pColVal
	delete infoRow;										//Delete local copy
}

//	##################################################################
//	PURPOSE:	Get the database or catalog name from the current row of the rpc parameters result set		
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'database' column of the result set
void tqfGenericStatementObj::dRpcParametersGetDatabaseOrCatalog(EXTfldval &pColVal)
{ 
	DAMParamPtr param = mOutputParams.getParam(PARAMETERS_DATABASE);
	qbyte *buffer = (qbyte *)param->getParamBuffer();
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Get the owner name from the current row of the rpc parameters result set
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'owner' column of the result set			
void tqfGenericStatementObj::dRpcParametersGetOwner(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(PARAMETERS_OWNER);
	qbyte *buffer = (qbyte *)param->getParamBuffer();
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Get the parameter name from the current row of the rpc parameters result set
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'name' column of the result set	
void tqfGenericStatementObj::dRpcParametersGetParameterName(EXTfldval &pColVal)
{
	DAMParamPtr param = mOutputParams.getParam(PARAMETERS_NAME);
	qbyte *buffer = (qbyte *)param->getParamBuffer();
	qlong nameLen = (qlong)*param->getParamLenBuffer();
	getSession()->charMapIn(buffer, nameLen, kSessionEncodingAnsi); 
	param->setFldVal(pColVal, mBatchRow);
}

//	##################################################################
//	PURPOSE:	Get the OMNIS data type text from the current row of the rpc parameters result set
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'OmnisDataType' column of the result set.
//						Called before the other dRpcParametersGetX functions so is able to cache results for the others
void tqfGenericStatementObj::dRpcParametersGetOmnisDataTypeText(EXTfldval &pColVal)
{	//EXAMPLE IMPLEMENTATION
	DAMParamPtr param = mOutputParams.getParam(PARAMETERS_DATA_TYPE);
	mCurColServerType = *((int *)param->getParamBuffer()); //store for later

	param = mOutputParams.getParam(PARAMETERS_LENGTH);
	mCurColPrecision = *((qlong *)param->getParamBuffer()); //store for later

	param = mOutputParams.getParam(PARAMETERS_SCALE);
	mCurColServerType = *((qshort *)param->getParamBuffer()); //store for later

	eCType omCType; int colSize; //not used
	//Use an Omnis callback to derive the Omnis data type text from the column attributes
	tqfGenericDAMObj *session = (tqfGenericDAMObj *)getSession();
	session->mapImplToOmnis(mCurColServerType, mCurColPrecision, mCurColScale, colSize, mCurColType, mCurColSubType, omCType); 
	if (mCurColType != fftCharacter)
		mCurColPrecision = 0;

	str255 typeText;
	DAMgetDataTypeText(mCurColType, mCurColSubType, mCurColPrecision, &typeText);
	pColVal.setChar(typeText);
}

//	##################################################################
//	PURPOSE:	Get the SQL data type from the current row of the rpc parameters result set
//
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'length' column of the result set
void tqfGenericStatementObj::dRpcParametersGetSqlDataType(EXTfldval &pColVal)
{
	unicodeText uDataType;
	//Implementation derives the SQL data type text from mCurColServerType (id)
	pColVal.setChar(uDataType.dataPtr(), uDataType.charLength());
}

//	##################################################################
//	PURPOSE:	Get the parameter length from the current row of the rpc parameters result set
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'length' column of the result set
void tqfGenericStatementObj::dRpcParametersGetLength(EXTfldval &pColVal)
{
	pColVal.setLong(mCurColPrecision); //Return the value stored previously
}

//	##################################################################
//	PURPOSE:	Get the parameter scale from the current row of the rpc parameters result set
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'scale' column of the result set
void tqfGenericStatementObj::dRpcParametersGetScale(EXTfldval &pColVal)
{
	pColVal.setLong(mCurColScale); //Return the value stored previously
}

//	##################################################################
//	PURPOSE:	Get the parameter pass type from the current row of the rpc parameters result set
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'passtype' column of the result set
void tqfGenericStatementObj::dRpcParametersGetPassType(EXTfldval &pColVal)
{	//EXAMPLE IMPLEMENTATION
	DAMParamPtr param = mOutputParams.getParam(PARAMETERS_PASS_TYPE);
	qbyte *buffer = (qbyte *)param->getParamBuffer();
	if (buffer[0] == 'r')
		pColVal.setConstant(preParameterReturnValue); 
	else if (buffer[0] == 'i')
		pColVal.setConstant(preParameterInput);
	else if (buffer[0] == 'b')
		pColVal.setConstant(preParameterInputOutput);
	else if (buffer[0] == 'o')
		pColVal.setConstant(preParameterOutput);
	else 
		pColVal.setEmpty(fftCharacter, dpDefault);
}

//	##################################################################
//	PURPOSE:	Get the parameter Main OMNIS type from the current row of the rpc parameters result set
//	DETAIL:		This is called following a fetch and allows the implementation to
//						explicitly set the contents of the 'Omnis type' column of the result set
void tqfGenericStatementObj::dRpcParametersGetOmnisDataType(EXTfldval &pColVal)
{
	pColVal.setLong(mCurColType); //Return the value stored previously
}

//	##################################################################
//	PURPOSE:	Get the parameter OMNIS subtype from the current row of the rpc parameters result set
//	DETAIL:		
void tqfGenericStatementObj::dRpcParametersGetOmnisDataSubType(EXTfldval &pColVal)
{
	pColVal.setLong(mCurColSubType); //Return the value stored previously
}

//	##################################################################
//	PURPOSE:	Get extra parameter info
//	DETAIL:		
void tqfGenericStatementObj::dRpcParametersGetDamInfo(EXTfldval &pColVal)	
{
	pColVal.setEmpty(fftList,dpDefault); //Return an empty row
}

//	##################################################################
//	PURPOSE:	Build the statement to execute the RPC at the server
//	DETAIL:		Called by dRpc()
void tqfGenericStatementObj::buildRpcCall(DAMrpcDefn *pRpcDefn, EXTfldval &pRpcCall)
{		//EXAMPLE IMPLEMENTATION
	qbool hasReturnValue = qbool(pRpcDefn->returnValueIndex() >= 0); // Is there a return value?
	
	unicodeText uRpcCall;
	if (hasReturnValue) 	
		uRpcCall.concat((void *)"{?=call "); // Initial parameter marker is for return value
	else 
		uRpcCall.concat((void *)"{call "); 
		
	pRpcCall.setChar(uRpcCall.dataPtr(), uRpcCall.charLength()); 

	qshort rpcNameLen;
	qchar *rpcName = pRpcDefn->getName(rpcNameLen);
	pRpcCall.concat(rpcName, rpcNameLen);
	
	qshort paramCount = pRpcDefn->paramCount();
	if (hasReturnValue) --paramCount;
	if (!paramCount)
	{
		pRpcCall.concat('}');
	}
	else
	{
		pRpcCall.concat('(');
		for (qshort i = 1; i <= paramCount; ++i)
		{
			unicodeText uEndStr; 
			if (i == paramCount) 
				uEndStr.concat((void *)"?)}"); 
			else 
				uEndStr.concat((void *)"?,"); 
			pRpcCall.concat(uEndStr.dataPtr(), uEndStr.charLength());
		}
	}
}

//	##################################################################
//	PURPOSE:	Call a remote procedure
//	RETURN:		Status (qtrue/qfalse)
//	DETAIL:		This function should call a remote procedure defined by pRpcDefn
qbool tqfGenericStatementObj::dRpc(DAMrpcDefn *pRpcDefn, EXTCompInfo* pEci)
{		//EXAMPLE IMPLEMENTATION
	eDAMError errorCode = kDAMNoError;
	qbool rtnStatus = qfalse;
	getSession()->debugMsg(3,"dRpc called\n"); 
	do
	{
		// Build the text for the RPC call.  This requires parameter markers for the return value, and the parameters.
		EXTfldval rpcCall;
		buildRpcCall(pRpcDefn, rpcCall);
		// Now, bind the RPC parameters.  These are all bound using mInputParams, even the RPC output parameters, since
		// mOutputParams is used for the result set.
		// Convert the RPC definition to a bind variables array structure.
		// In our case, the first bind variable definition must be for the return value of the procedure.
		rpcDefnToBindVars(pRpcDefn, pEci);

		// Set up the bind variables
		if (!setupInputVars(kBindExecute)) break;						// setupInputVars sets the error code
		if (!dProcessPrepareParams(&mInputParams))
		{
			errorCode = kStatementRpcPrepareParamsError;
			break;
		}
		qret bindStatus = DAMgetBindVariableValues(pEci, mBindVars);
		if (bindStatus != e_ok)
		{
			errorCode = kStatementRpcGetParamValuesError;
			break;
		}
		if (!setBufferValues(qtrue)) break;						// setBufferValues sets the error code
		if (!bindInputBuffers(kBindExecute)) break;		// bindInputBuffers sets the error code

		// Now call the procedure and store the output parameters in the caller's parameters
		qHandle	textHandle = rpcCall.getHandle(qfalse);
		qHandleTextPtr textHp(textHandle, 0);
		if (!dExecDirect(*textHp, textHp.charLen()) || !sendInputValues() || !storeRpcOutputParameters(pRpcDefn, pEci))
		{
			errorCode = kStatementExecDirectFailed;
			if ((getSession() != NULL) && (kTranAutomatic == getSession()->getTranMode())) getSession()->rollback();
			break;
		}
		mSQLText = rpcCall; //store the SQL text in the object's $sqltext property
		rtnStatus = qtrue;
	} while (0);
	if (!rtnStatus && errorCode != kDAMNoError) setError(errorCode);
	return rtnStatus;
}

//	##################################################################
//	PURPOSE:	Allows implementation to augment the supplied parameter index 
//	RETURN:		The new parameter index					
//	DETAIL:		This can be used where the parameter order expected by the procedure
//						differs from the order in which they were supplied.
//						Called from the base class rpcDefnToBindVars() method
qshort tqfGenericStatementObj::dRpcBindIndex(DAMrpcDefn *pRpcDefn, qshort pParamIndex)
{
	getSession()->debugMsg(3,"dRpcBindIndex called\n"); 
	return(pParamIndex);
}

//	##################################################################
//	PURPOSE:	Substitute sequence numbers for bind markers	
//	DETAIL:		Note that the example implementation relies on there being 
//						4 x dollar characters to represent each bind variable.
//						This function adds sequence numbers to each marker
void tqfGenericStatementObj::addBindMarkers(qchar *pSQLText, qlong pSQLTextLen)
{
	qlong charNum = 0, bindNum = 1;
	do {
		if ((pSQLText[charNum] == '$') && (pSQLText[charNum+1] == '$') && (pSQLText[charNum+2] == '$') && (pSQLText[charNum+3] == '$'))
		{
			str15 bindNumStr; qlongToString(bindNum++, bindNumStr);
			qshort digit = 1, bindLen = bindNumStr.length(); 
			for (digit=1; digit<4; digit++)
				pSQLText[charNum+digit] = (digit<=bindLen)? bindNumStr[digit] : ' ';
			charNum++; charNum++; charNum++;
		}
		charNum++;
	} while (charNum < pSQLTextLen);
}

//	##################################################################
//	PURPOSE:	Example implementation for a custom method				
//	DETAIL:	
qbool	tqfGenericStatementObj::doMethod1(EXTCompInfo* pEci)
{
	qbool rtnStatus = qtrue;
	getSession()->debugMsg(3,"doMethod1 called\n"); 
	return rtnStatus;
}
// End of file
