///////////////////////////////////////////////////////////////////////////////
//
//	File    : $Id$
//	Subject : IBPP, Service class implementation
//
///////////////////////////////////////////////////////////////////////////////
//
//	(C) Copyright 2000-2006 T.I.P. Group S.A. and the IBPP Team (www.ibpp.org)
//
//	The contents of this file are subject to the IBPP License (the "License");
//	you may not use this file except in compliance with the License.  You may
//	obtain a copy of the License at http://www.ibpp.org or in the 'license.txt'
//	file which must have been distributed along with this file.
//
//	This software, distributed under the License, is distributed on an "AS IS"
//	basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the
//	License for the specific language governing rights and limitations
//	under the License.
//
///////////////////////////////////////////////////////////////////////////////
//
//	COMMENTS
//	* Tabulations should be set every four characters when editing this file.
//
///////////////////////////////////////////////////////////////////////////////

#ifdef _MSC_VER
#pragma warning(disable: 4786 4996)
#ifndef _DEBUG
#pragma warning(disable: 4702)
#endif
#endif

#include "_ibpp.h"

#ifdef HAS_HDRSTOP
#pragma hdrstop
#endif

using namespace ibpp_internals;

//	(((((((( OBJECT INTERFACE IMPLEMENTATION ))))))))

void StatementImpl::Prepare(const std::string& sql)
{
	if (mDatabase == 0)
		throw LogicExceptionImpl("Statement::Prepare", _("An IDatabase must be attached."));
	if (mDatabase->GetHandle() == 0)
		throw LogicExceptionImpl("Statement::Prepare", _("IDatabase must be connected."));
	if (mTransaction == 0)
		throw LogicExceptionImpl("Statement::Prepare", _("An ITransaction must be attached."));
	if (mTransaction->GetHandle() == 0)
		throw LogicExceptionImpl("Statement::Prepare", _("ITransaction must be started."));
	if (sql.empty())
		throw LogicExceptionImpl("Statement::Prepare", _("SQL statement can't be 0."));

	// Saves the SQL sentence, only for reporting reasons in case of errors
	mSql = sql;

	IBS status;

	// Free all resources currently attached to this Statement, then allocate
	// a new statement descriptor.
	Close();
	(*gds.Call()->m_dsql_allocate_statement)(status.Self(), mDatabase->GetHandlePtr(), &mHandle);
	if (status.Errors())
		throw SQLExceptionImpl(status, "Statement::Prepare",
			_("isc_dsql_allocate_statement failed"));

	// Empirical estimate of parameters count and output columns count.
	// This is by far not an exact estimation, which would require parsing the
	// SQL statement. If the SQL statement contains '?' and ',' in string
	// constants, this count will obviously be wrong, but it will be exagerated.
	// It won't hurt. We just try to not have to re-allocate those descriptors later.
	// So we prefer to get them a little bit larger than needed than the other way.
	int16_t inEstimate = 0;
	int16_t outEstimate = 1;
	size_t len = strlen(sql.c_str());
	for (size_t i = 0; i < len; i++)
	{
		if (sql[i] == '?') ++inEstimate;
		if (sql[i] == ',') ++outEstimate;
	}

	/*
	DebugStream()<< "Prepare(\""<< sql<< "\")"<< fds;
	DebugStream()<< _("Estimation: ")<< inEstimate<< _(" IN parameters and ")
			<< outEstimate<< _(" OUT columns")<< fds;
	*/

	// Allocates output descriptor and prepares the statement
	mOutRow = new RowImpl(mDatabase->Dialect(), outEstimate, mDatabase, mTransaction);
	mOutRow->AddRef();

	status.Reset();
	(*gds.Call()->m_dsql_prepare)(status.Self(), mTransaction->GetHandlePtr(),
		&mHandle, (short)sql.length(), const_cast<char*>(sql.c_str()),
			short(mDatabase->Dialect()), mOutRow->Self());
	if (status.Errors())
	{
		Close();
		std::string context = "Statement::Prepare( ";
		context.append(mSql).append(" )");
		throw SQLExceptionImpl(status, context.c_str(),
			_("isc_dsql_prepare failed"));
	}

	// Read what kind of statement was prepared
	status.Reset();
	char itemsReq[] = {isc_info_sql_stmt_type};
	char itemsRes[8];
	(*gds.Call()->m_dsql_sql_info)(status.Self(), &mHandle, 1, itemsReq,
		sizeof(itemsRes), itemsRes);
	if (status.Errors())
	{
		Close();
		throw SQLExceptionImpl(status, "Statement::Prepare",
			_("isc_dsql_sql_info failed"));
	}
	if (itemsRes[0] == isc_info_sql_stmt_type)
	{
		switch (itemsRes[3])
		{
			case isc_info_sql_stmt_select :		mType = IBPP::stSelect; break;
			case isc_info_sql_stmt_insert :		mType = IBPP::stInsert; break;
			case isc_info_sql_stmt_update :		mType = IBPP::stUpdate; break;
			case isc_info_sql_stmt_delete :		mType = IBPP::stDelete; break;
			case isc_info_sql_stmt_ddl :		mType = IBPP::stDDL; break;
			case isc_info_sql_stmt_exec_procedure :	mType = IBPP::stExecProcedure; break;
			case isc_info_sql_stmt_start_trans:	mType = IBPP::stStartTransaction; break;
			case isc_info_sql_stmt_commit:		mType = IBPP::stCommitTransaction; break;
			case isc_info_sql_stmt_rollback:	mType = IBPP::stRollbackTransaction; break;
			case isc_info_sql_stmt_select_for_upd : mType = IBPP::stSelectUpdate; break;
			case isc_info_sql_stmt_set_generator :	mType = IBPP::stSetGenerator; break;
			case isc_info_sql_stmt_savepoint :	mType = IBPP::stSavePoint; break;
			default : mType = IBPP::stUnsupported;
		}
	}
	if (mType == IBPP::stUnknown || mType == IBPP::stUnsupported)
	{
		Close();
		throw LogicExceptionImpl("Statement::Prepare",
			_("Unknown or unsupported statement type"));
	}

	if (mOutRow->Columns() == 0)
	{
		// Get rid of the output descriptor, if it wasn't required (no output)
		mOutRow->Release();
		mOutRow = 0;
		/*
		DebugStream()<< _("Dropped output descriptor which was not required")<< fds;
		*/
	}
	else if (mOutRow->Columns() > mOutRow->AllocatedSize())
	{
		// Resize the output descriptor (which is too small).
		// The statement does not need to be prepared again, though the
		// output columns must be described again.

		/*
		DebugStream()<< _("Resize output descriptor from ")
			<< mOutRow->AllocatedSize()<< _(" to ")<< mOutRow->Columns()<< fds;
		*/

		mOutRow->Resize(mOutRow->Columns());
		status.Reset();
		(*gds.Call()->m_dsql_describe)(status.Self(), &mHandle, 1, mOutRow->Self());
		if (status.Errors())
		{
			Close();
			throw SQLExceptionImpl(status, "Statement::Prepare",
				_("isc_dsql_describe failed"));
		}
	}

	if (inEstimate > 0)
	{
		// Ready an input descriptor
		mInRow = new RowImpl(mDatabase->Dialect(), inEstimate, mDatabase, mTransaction);
		mInRow->AddRef();

		status.Reset();
		(*gds.Call()->m_dsql_describe_bind)(status.Self(), &mHandle, 1, mInRow->Self());
		if (status.Errors())
		{
			Close();
			throw SQLExceptionImpl(status, "Statement::Prepare",
				_("isc_dsql_describe_bind failed"));
		}

		if (mInRow->Columns() == 0)
		{
			// Get rid of the input descriptor, if it wasn't required (no parameters)
			mInRow->Release();
			mInRow = 0;
			/*
			DebugStream()<< _("Dropped input descriptor which was not required")<< fds;
			*/
		}
		else if (mInRow->Columns() > mInRow->AllocatedSize())
		{
			// Resize the input descriptor (which is too small).
			// The statement does not need to be prepared again, though the
			// parameters must be described again.

			/*
			DebugStream()<< _("Resize input descriptor from ")
					<< mInRow->AllocatedSize()<< _(" to ")
					<< mInRow->Columns()<< fds;
			*/

			mInRow->Resize(mInRow->Columns());
			status.Reset();
			(*gds.Call()->m_dsql_describe_bind)(status.Self(), &mHandle, 1, mInRow->Self());
			if (status.Errors())
			{
				Close();
				throw SQLExceptionImpl(status, "Statement::Prepare",
					_("isc_dsql_describe_bind failed"));
			}
		}
	}

	// Allocates variables of the input descriptor
	if (mInRow != 0)
	{
		// Turn on 'can be NULL' on each input parameter
		for (int i = 0; i < mInRow->Columns(); i++)
		{
			XSQLVAR* var = &(mInRow->Self()->sqlvar[i]);
			if (! (var->sqltype & 1)) var->sqltype += short(1);
		}
		mInRow->AllocVariables();
	}

	// Allocates variables of the output descriptor
	if (mOutRow != 0) mOutRow->AllocVariables();
}

void StatementImpl::Plan(std::string& plan)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Plan", _("No statement has been prepared."));
	if (mDatabase == 0)
		throw LogicExceptionImpl("Statement::Plan", _("A Database must be attached."));
	if (mDatabase->GetHandle() == 0)
		throw LogicExceptionImpl("Statement::Plan", _("Database must be connected."));

	IBS status;
	RB result(4096);
	char itemsReq[] = {isc_info_sql_get_plan};

	(*gds.Call()->m_dsql_sql_info)(status.Self(), &mHandle, 1, itemsReq,
								   result.Size(), result.Self());
	if (status.Errors()) throw SQLExceptionImpl(status,
								"Statement::Plan", _("isc_dsql_sql_info failed."));

	result.GetString(isc_info_sql_get_plan, plan);
	if (plan[0] == '\n') plan.erase(0, 1);
}

void StatementImpl::Execute(const std::string& sql)
{
	if (! sql.empty()) Prepare(sql);

	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Execute",
			_("No statement has been prepared."));

	// Check that a value has been set for each input parameter
	if (mInRow != 0 && mInRow->MissingValues())
		throw LogicExceptionImpl("Statement::Execute",
			_("All parameters must be specified."));

	CursorFree();	// Free a previous 'cursor' if any

	IBS status;
	if (mType == IBPP::stSelect)
	{
		// Could return a result set (none, single or multi rows)
		(*gds.Call()->m_dsql_execute)(status.Self(), mTransaction->GetHandlePtr(),
			&mHandle, 1, mInRow == 0 ? 0 : mInRow->Self());
		if (status.Errors())
		{
			//Close();	Commented because Execute error should not free the statement
			std::string context = "Statement::Execute( ";
			context.append(mSql).append(" )");
			throw SQLExceptionImpl(status, context.c_str(),
				_("isc_dsql_execute failed"));
		}
		if (mOutRow != 0) mResultSetAvailable = true;
	}
	else
	{
		// Should return at most a single row
		(*gds.Call()->m_dsql_execute2)(status.Self(), mTransaction->GetHandlePtr(),
			&mHandle, 1, mInRow == 0 ? 0 : mInRow->Self(),
			mOutRow == 0 ? 0 : mOutRow->Self());
		if (status.Errors())
		{
			//Close();	Commented because Execute error should not free the statement
			std::string context = "Statement::Execute( ";
			context.append(mSql).append(" )");
			throw SQLExceptionImpl(status, context.c_str(),
				_("isc_dsql_execute2 failed"));
		}
	}
}

void StatementImpl::CursorExecute(const std::string& cursor, const std::string& sql)
{
	if (cursor.empty())
		throw LogicExceptionImpl("Statement::CursorExecute", _("Cursor name can't be 0."));

	if (! sql.empty()) Prepare(sql);

	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::CursorExecute", _("No statement has been prepared."));
	if (mType != IBPP::stSelectUpdate)
		throw LogicExceptionImpl("Statement::CursorExecute", _("Statement must be a SELECT FOR UPDATE."));
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::CursorExecute", _("Statement would return no rows."));

	// Check that a value has been set for each input parameter
	if (mInRow != 0 && mInRow->MissingValues())
		throw LogicExceptionImpl("Statement::CursorExecute",
			_("All parameters must be specified."));

	CursorFree();	// Free a previous 'cursor' if any

	IBS status;
	(*gds.Call()->m_dsql_execute)(status.Self(), mTransaction->GetHandlePtr(),
		&mHandle, 1, mInRow == 0 ? 0 : mInRow->Self());
	if (status.Errors())
	{
		//Close();	Commented because Execute error should not free the statement
		std::string context = "Statement::CursorExecute( ";
		context.append(mSql).append(" )");
		throw SQLExceptionImpl(status, context.c_str(),
			_("isc_dsql_execute failed"));
	}

	status.Reset();
	(*gds.Call()->m_dsql_set_cursor_name)(status.Self(), &mHandle, const_cast<char*>(cursor.c_str()), 0);
	if (status.Errors())
	{
		//Close();	Commented because Execute error should not free the statement
		throw SQLExceptionImpl(status, "Statement::CursorExecute",
			_("isc_dsql_set_cursor_name failed"));
	}

	mResultSetAvailable = true;
	mCursorOpened = true;
}

void StatementImpl::ExecuteImmediate(const std::string& sql)
{
	if (mDatabase == 0)
		throw LogicExceptionImpl("Statement::ExecuteImmediate", _("An IDatabase must be attached."));
	if (mDatabase->GetHandle() == 0)
		throw LogicExceptionImpl("Statement::ExecuteImmediate", _("IDatabase must be connected."));
	if (mTransaction == 0)
		throw LogicExceptionImpl("Statement::ExecuteImmediate", _("An ITransaction must be attached."));
	if (mTransaction->GetHandle() == 0)
		throw LogicExceptionImpl("Statement::ExecuteImmediate", _("ITransaction must be started."));
	if (sql.empty())
		throw LogicExceptionImpl("Statement::ExecuteImmediate", _("SQL statement can't be 0."));

	IBS status;
	Close();
    (*gds.Call()->m_dsql_execute_immediate)(status.Self(), mDatabase->GetHandlePtr(),
    	mTransaction->GetHandlePtr(), 0, const_cast<char*>(sql.c_str()),
    		short(mDatabase->Dialect()), 0);
    if (status.Errors())
	{
		std::string context = "Statement::ExecuteImmediate( ";
		context.append(sql).append(" )");
		throw SQLExceptionImpl(status, context.c_str(),
			_("isc_dsql_execute_immediate failed"));
	}
}

int StatementImpl::AffectedRows()
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::AffectedRows", _("No statement has been prepared."));
	if (mDatabase == 0)
		throw LogicExceptionImpl("Statement::AffectedRows", _("A Database must be attached."));
	if (mDatabase->GetHandle() == 0)
		throw LogicExceptionImpl("Statement::AffectedRows", _("Database must be connected."));

	int count(0);
	IBS status;
	RB result;
	char itemsReq[] = {isc_info_sql_records};

	(*gds.Call()->m_dsql_sql_info)(status.Self(), &mHandle, 1, itemsReq,
		result.Size(), result.Self());
	if (status.Errors()) throw SQLExceptionImpl(status,
			"Statement::AffectedRows", _("isc_dsql_sql_info failed."));

	// Cover the INSERT or UPDATE case
	if (mType == IBPP::stInsert || mType == IBPP::stUpdate)
	{
		count += result.GetValue(isc_info_sql_records, isc_info_req_insert_count);
		count += result.GetValue(isc_info_sql_records, isc_info_req_update_count);
	}
	else if (mType == IBPP::stDelete)
			count = result.GetValue(isc_info_sql_records, isc_info_req_delete_count);
	else if (mType == IBPP::stSelect)
			count = result.GetValue(isc_info_sql_records, isc_info_req_select_count);
	else	count = 0;	// Returns zero count for unknown cases

	return count;
}

bool StatementImpl::Fetch()
{
	if (! mResultSetAvailable)
		throw LogicExceptionImpl("Statement::Fetch",
			_("No statement has been executed or no result set available."));

	IBS status;
	ISC_STATUS code = (*gds.Call()->m_dsql_fetch)(status.Self(), &mHandle, 1, mOutRow->Self());
	if (code == 100)	// This special code means "no more rows"
	{
		mResultSetAvailable = false;
		// Oddly enough, fetching rows up to the last one seems to open
		// an 'implicit' cursor that needs to be closed.
		mCursorOpened = true;
		CursorFree();	// Free the explicit or implicit cursor/result-set
		return false;
	}
	if (status.Errors())
	{
		Close();
		throw SQLExceptionImpl(status, "Statement::Fetch",
			_("isc_dsql_fetch failed."));
	}

    // Close the 'implicit' cursor to allow for forther Execute() calls
    // on the prepared statement without fetching up to the last row
	mCursorOpened = true;
	return true;
}

bool StatementImpl::Fetch(IBPP::Row& row)
{
	if (! mResultSetAvailable)
		throw LogicExceptionImpl("Statement::Fetch(row)",
			_("No statement has been executed or no result set available."));

	RowImpl* rowimpl = new RowImpl(*mOutRow);
	row = rowimpl;

	IBS status;
	ISC_STATUS code = (*gds.Call()->m_dsql_fetch)(status.Self(), &mHandle, 1,
					rowimpl->Self());
	if (code == 100)	// This special code means "no more rows"
	{
		mResultSetAvailable = false;
		// Oddly enough, fetching rows up to the last one seems to open
		// an 'implicit' cursor that needs to be closed.
		mCursorOpened = true;
		CursorFree();	// Free the explicit or implicit cursor/result-set
		row.clear();
		return false;
	}
	if (status.Errors())
	{
		Close();
		row.clear();
		throw SQLExceptionImpl(status, "Statement::Fetch(row)",
			_("isc_dsql_fetch failed."));
	}

	return true;
}

void StatementImpl::Close()
{
	// Free all statement resources.
	// Used before preparing a new statement or from destructor.

	if (mInRow != 0) { mInRow->Release(); mInRow = 0; }
	if (mOutRow != 0) { mOutRow->Release(); mOutRow = 0; }

	mResultSetAvailable = false;
	mCursorOpened = false;
	mType = IBPP::stUnknown;

	if (mHandle != 0)
	{
		IBS status;
		(*gds.Call()->m_dsql_free_statement)(status.Self(), &mHandle, DSQL_drop);
		mHandle = 0;
		if (status.Errors())
			throw SQLExceptionImpl(status, "Statement::Close(DSQL_drop)",
				_("isc_dsql_free_statement failed."));
	}
}

void StatementImpl::SetNull(int param)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::SetNull", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::SetNull", _("The statement does not take parameters."));

	mInRow->SetNull(param);
}

void StatementImpl::Set(int param, bool value)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[bool]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[bool]", _("The statement does not take parameters."));

	mInRow->Set(param, value);
}

void StatementImpl::Set(int param, const char* cstring)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[char*]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[char*]", _("The statement does not take parameters."));

	mInRow->Set(param, cstring);
}

void StatementImpl::Set(int param, const void* bindata, int len)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[void*]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[void*]", _("The statement does not take parameters."));

	mInRow->Set(param, bindata, len);
}

void StatementImpl::Set(int param, const std::string& s)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[string]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[string]", _("The statement does not take parameters."));

	mInRow->Set(param, s);
}

void StatementImpl::Set(int param, int16_t value)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[int16_t]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[int16_t]", _("The statement does not take parameters."));

	mInRow->Set(param, value);
}

void StatementImpl::Set(int param, int32_t value)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[int32_t]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[int32_t]", _("The statement does not take parameters."));

	mInRow->Set(param, value);
}

void StatementImpl::Set(int param, int64_t value)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[int64_t]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[int64_t]", _("The statement does not take parameters."));

	mInRow->Set(param, value);
}

void StatementImpl::Set(int param, float value)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[float]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[float]", _("The statement does not take parameters."));

	mInRow->Set(param, value);
}

void StatementImpl::Set(int param, double value)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[double]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[double]", _("The statement does not take parameters."));

	mInRow->Set(param, value);
}

void StatementImpl::Set(int param, const IBPP::Timestamp& value)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[Timestamp]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[Timestamp]", _("The statement does not take parameters."));

	mInRow->Set(param, value);
}

void StatementImpl::Set(int param, const IBPP::Date& value)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[Date]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[Date]", _("The statement does not take parameters."));

	mInRow->Set(param, value);
}

void StatementImpl::Set(int param, const IBPP::Time& value)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[Time]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[Time]", _("The statement does not take parameters."));

	mInRow->Set(param, value);
}

void StatementImpl::Set(int param, const IBPP::Blob& blob)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[Blob]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[Blob]", _("The statement does not take parameters."));

	mInRow->Set(param, blob);
}

void StatementImpl::Set(int param, const IBPP::Array& array)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[Array]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[Array]", _("The statement does not take parameters."));

	mInRow->Set(param, array);
}

void StatementImpl::Set(int param, const IBPP::DBKey& key)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[DBKey]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[DBKey]", _("The statement does not take parameters."));

	mInRow->Set(param, key);
}

/*
void StatementImpl::Set(int param, const IBPP::Value& value)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Set[Value]", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Set[Value]", _("The statement does not take parameters."));

	mInRow->Set(param, value);
}
*/

bool StatementImpl::IsNull(int column)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::IsNull", _("The row is not initialized."));

	return mOutRow->IsNull(column);
}

bool StatementImpl::Get(int column, bool* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));
	if (retvalue == 0)
		throw LogicExceptionImpl("Statement::Get", _("Null pointer detected"));

	return mOutRow->Get(column, *retvalue);
}

bool StatementImpl::Get(int column, bool& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, retvalue);
}

bool StatementImpl::Get(int column, char* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, retvalue);
}

bool StatementImpl::Get(int column, void* bindata, int& userlen)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, bindata, userlen);
}

bool StatementImpl::Get(int column, std::string& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, retvalue);
}

bool StatementImpl::Get(int column, int16_t* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));
	if (retvalue == 0)
		throw LogicExceptionImpl("Statement::Get", _("Null pointer detected"));

	return mOutRow->Get(column, *retvalue);
}

bool StatementImpl::Get(int column, int16_t& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, retvalue);
}

bool StatementImpl::Get(int column, int32_t* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));
	if (retvalue == 0)
		throw LogicExceptionImpl("Statement::Get", _("Null pointer detected"));

	return mOutRow->Get(column, *retvalue);
}

bool StatementImpl::Get(int column, int32_t& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, retvalue);
}

bool StatementImpl::Get(int column, int64_t* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));
	if (retvalue == 0)
		throw LogicExceptionImpl("Statement::Get", _("Null pointer detected"));

	return mOutRow->Get(column, *retvalue);
}

bool StatementImpl::Get(int column, int64_t& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, retvalue);
}

bool StatementImpl::Get(int column, float* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));
	if (retvalue == 0)
		throw LogicExceptionImpl("Statement::Get", _("Null pointer detected"));

	return mOutRow->Get(column, *retvalue);
}

bool StatementImpl::Get(int column, float& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, retvalue);
}

bool StatementImpl::Get(int column, double* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));
	if (retvalue == 0)
		throw LogicExceptionImpl("Statement::Get", _("Null pointer detected"));

	return mOutRow->Get(column, *retvalue);
}

bool StatementImpl::Get(int column, double& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, retvalue);
}

bool StatementImpl::Get(int column, IBPP::Timestamp& timestamp)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, timestamp);
}

bool StatementImpl::Get(int column, IBPP::Date& date)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, date);
}

bool StatementImpl::Get(int column, IBPP::Time& time)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, time);
}

bool StatementImpl::Get(int column, IBPP::Blob& blob)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, blob);
}

bool StatementImpl::Get(int column, IBPP::DBKey& key)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, key);
}

bool StatementImpl::Get(int column, IBPP::Array& array)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column, array);
}

/*
const IBPP::Value StatementImpl::Get(int column)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(column);
}
*/

bool StatementImpl::IsNull(const std::string& name)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::IsNull", _("The row is not initialized."));

	return mOutRow->IsNull(name);
}

bool StatementImpl::Get(const std::string& name, bool* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));
	if (retvalue == 0)
		throw LogicExceptionImpl("Statement::Get", _("Null pointer detected"));

	return mOutRow->Get(name, *retvalue);
}

bool StatementImpl::Get(const std::string& name, bool& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue);
}

bool StatementImpl::Get(const std::string& name, char* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get[char*]", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue);
}

bool StatementImpl::Get(const std::string& name, void* retvalue, int& count)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get[void*,int]", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue, count);
}

bool StatementImpl::Get(const std::string& name, std::string& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::GetString", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue);
}

bool StatementImpl::Get(const std::string& name, int16_t* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));
	if (retvalue == 0)
		throw LogicExceptionImpl("Statement::Get", _("Null pointer detected"));

	return mOutRow->Get(name, *retvalue);
}

bool StatementImpl::Get(const std::string& name, int16_t& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue);
}

bool StatementImpl::Get(const std::string& name, int32_t* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));
	if (retvalue == 0)
		throw LogicExceptionImpl("Statement::Get", _("Null pointer detected"));

	return mOutRow->Get(name, *retvalue);
}

bool StatementImpl::Get(const std::string& name, int32_t& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue);
}

bool StatementImpl::Get(const std::string& name, int64_t* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));
	if (retvalue == 0)
		throw LogicExceptionImpl("Statement::Get", _("Null pointer detected"));

	return mOutRow->Get(name, *retvalue);
}

bool StatementImpl::Get(const std::string& name, int64_t& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue);
}

bool StatementImpl::Get(const std::string& name, float* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));
	if (retvalue == 0)
		throw LogicExceptionImpl("Statement::Get", _("Null pointer detected"));

	return mOutRow->Get(name, *retvalue);
}

bool StatementImpl::Get(const std::string& name, float& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue);
}

bool StatementImpl::Get(const std::string& name, double* retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));
	if (retvalue == 0)
		throw LogicExceptionImpl("Statement::Get", _("Null pointer detected"));

	return mOutRow->Get(name, *retvalue);
}

bool StatementImpl::Get(const std::string& name, double& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue);
}

bool StatementImpl::Get(const std::string& name, IBPP::Timestamp& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue);
}

bool StatementImpl::Get(const std::string& name, IBPP::Date& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue);
}

bool StatementImpl::Get(const std::string& name, IBPP::Time& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue);
}

bool StatementImpl::Get(const std::string&name, IBPP::Blob& retblob)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name, retblob);
}

bool StatementImpl::Get(const std::string& name, IBPP::DBKey& retvalue)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name, retvalue);
}

bool StatementImpl::Get(const std::string& name, IBPP::Array& retarray)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name, retarray);
}

/*
const IBPP::Value StatementImpl::Get(const std::string& name)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Get", _("The row is not initialized."));

	return mOutRow->Get(name);
}
*/

int StatementImpl::Columns()
{
	if (mOutRow == 0)
		return 0;

	return mOutRow->Columns();
}

int StatementImpl::ColumnNum(const std::string& name)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::ColumnNum", _("The row is not initialized."));

	return mOutRow->ColumnNum(name);
}

const char* StatementImpl::ColumnName(int varnum)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Columns", _("The row is not initialized."));

	return mOutRow->ColumnName(varnum);
}

const char* StatementImpl::ColumnAlias(int varnum)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Columns", _("The row is not initialized."));

	return mOutRow->ColumnAlias(varnum);
}

const char* StatementImpl::ColumnTable(int varnum)
{
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::Columns", _("The row is not initialized."));

	return mOutRow->ColumnTable(varnum);
}

IBPP::SDT StatementImpl::ColumnType(int varnum)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::ColumnType", _("No statement has been prepared."));
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::ColumnType", _("The statement does not return results."));

    return mOutRow->ColumnType(varnum);
}

int StatementImpl::ColumnSubtype(int varnum)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::ColumnSubtype", _("No statement has been prepared."));
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::ColumnSubtype", _("The statement does not return results."));

    return mOutRow->ColumnSubtype(varnum);
}

int StatementImpl::ColumnSize(int varnum)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::ColumnSize", _("No statement has been prepared."));
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::ColumnSize", _("The row is not initialized."));

	return mOutRow->ColumnSize(varnum);
}

int StatementImpl::ColumnScale(int varnum)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::ColumnScale", _("No statement has been prepared."));
	if (mOutRow == 0)
		throw LogicExceptionImpl("Statement::ColumnScale", _("The row is not initialized."));

	return mOutRow->ColumnScale(varnum);
}

int StatementImpl::Parameters()
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::Parameters", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::Parameters", _("The statement uses no parameters."));

	return mInRow->Columns();
}

IBPP::SDT StatementImpl::ParameterType(int varnum)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::ParameterType", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::ParameterType", _("The statement uses no parameters."));

    return mInRow->ColumnType(varnum);
}

int StatementImpl::ParameterSubtype(int varnum)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::ParameterSubtype", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::ParameterSubtype", _("The statement uses no parameters."));

    return mInRow->ColumnSubtype(varnum);
}

int StatementImpl::ParameterSize(int varnum)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::ParameterSize", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::ParameterSize", _("The statement uses no parameters."));

	return mInRow->ColumnSize(varnum);
}

int StatementImpl::ParameterScale(int varnum)
{
	if (mHandle == 0)
		throw LogicExceptionImpl("Statement::ParameterScale", _("No statement has been prepared."));
	if (mInRow == 0)
		throw LogicExceptionImpl("Statement::ParameterScale", _("The statement uses no parameters."));

	return mInRow->ColumnScale(varnum);
}

IBPP::Database StatementImpl::DatabasePtr() const
{
	return mDatabase;
}

IBPP::Transaction StatementImpl::TransactionPtr() const
{
	return mTransaction;
}

IBPP::IStatement* StatementImpl::AddRef()
{
	ASSERTION(mRefCount >= 0);
	++mRefCount;

	return this;
}

void StatementImpl::Release()
{
	// Release cannot throw, except in DEBUG builds on assertion
	ASSERTION(mRefCount >= 0);
	--mRefCount;
	try { if (mRefCount <= 0) delete this; }
		catch (...) { }
}

//	(((((((( OBJECT INTERNAL METHODS ))))))))

void StatementImpl::AttachDatabaseImpl(DatabaseImpl* database)
{
	if (database == 0)
		throw LogicExceptionImpl("Statement::AttachDatabase",
			_("Can't attach a 0 IDatabase object."));

	if (mDatabase != 0) mDatabase->DetachStatementImpl(this);
	mDatabase = database;
	mDatabase->AttachStatementImpl(this);
}

void StatementImpl::DetachDatabaseImpl()
{
	if (mDatabase == 0) return;

	Close();
	mDatabase->DetachStatementImpl(this);
	mDatabase = 0;
}

void StatementImpl::AttachTransactionImpl(TransactionImpl* transaction)
{
	if (transaction == 0)
		throw LogicExceptionImpl("Statement::AttachTransaction",
			_("Can't attach a 0 ITransaction object."));

	if (mTransaction != 0) mTransaction->DetachStatementImpl(this);
	mTransaction = transaction;
	mTransaction->AttachStatementImpl(this);
}

void StatementImpl::DetachTransactionImpl()
{
	if (mTransaction == 0) return;

	Close();
	mTransaction->DetachStatementImpl(this);
	mTransaction = 0;
}

void StatementImpl::CursorFree()
{
	if (mCursorOpened)
	{
		mCursorOpened = false;
		if (mHandle != 0)
		{
			IBS status;
			(*gds.Call()->m_dsql_free_statement)(status.Self(), &mHandle, DSQL_close);
			if (status.Errors())
				throw SQLExceptionImpl(status, "StatementImpl::CursorFree(DSQL_close)",
					_("isc_dsql_free_statement failed."));
		}
	}
}

StatementImpl::StatementImpl(DatabaseImpl* database, TransactionImpl* transaction)
	: mRefCount(0), mHandle(0), mDatabase(0), mTransaction(0),
	mInRow(0), mOutRow(0),
	mResultSetAvailable(false), mCursorOpened(false), mType(IBPP::stUnknown)
{
	AttachDatabaseImpl(database);
	if (transaction != 0) AttachTransactionImpl(transaction);
}

StatementImpl::~StatementImpl()
{
	try { Close(); }
		catch (...) { }
	try { if (mTransaction != 0) mTransaction->DetachStatementImpl(this); }
		catch (...) { }
	try { if (mDatabase != 0) mDatabase->DetachStatementImpl(this); }
		catch (...) { }
}

//
//	EOF
//
