//
// ODBCStatementImpl.h
//
// Library: Data/ODBC
// Package: ODBC
// Module:  ODBCStatementImpl
//
// Definition of the ODBCStatementImpl class.
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#ifndef Data_ODBC_ODBCStatementImpl_INCLUDED
#define Data_ODBC_ODBCStatementImpl_INCLUDED


#include "Poco/Data/ODBC/ODBC.h"
#include "Poco/Data/ODBC/SessionImpl.h"
#include "Poco/Data/ODBC/Binder.h"
#include "Poco/Data/ODBC/Extractor.h"
#include "Poco/Data/ODBC/Preparator.h"
#include "Poco/Data/ODBC/ODBCMetaColumn.h"
#include "Poco/Data/StatementImpl.h"
#include "Poco/SharedPtr.h"

#ifdef POCO_OS_FAMILY_WINDOWS
#include <windows.h>
#endif

#include <sqltypes.h>


namespace Poco {
namespace Data {
namespace ODBC {


class ODBC_API ODBCStatementImpl: public Poco::Data::StatementImpl
	/// Implements statement functionality needed for ODBC
{
public:
	ODBCStatementImpl(SessionImpl& rSession);
		/// Creates the ODBCStatementImpl.

	~ODBCStatementImpl() override;
		/// Destroys the ODBCStatementImpl.

	std::string nativeSQL();
		/// Returns the SQL string as modified by the driver.

protected:
	std::size_t columnsReturned() const override;
		/// Returns number of columns returned by query.

	int affectedRowCount() const override;
		/// Returns the number of affected rows.
		/// Used to find out the number of rows affected by insert or update.

	const MetaColumn& metaColumn(std::size_t pos) const override;
		/// Returns column meta data.

	bool hasNext() override;
		/// Returns true if a call to next() will return data.

	std::size_t next() override;
		/// Retrieves the next row or set of rows from the resultset.
		/// Returns the number of rows retrieved.
		/// Will throw, if the resultset is empty.

	bool canBind() const override;
		/// Returns true if a valid statement is set and we can bind.

	bool canCompile() const override;
		/// Returns true if another compile is possible.

	void compileImpl() override;
		/// Compiles the statement, doesn't bind yet.
		/// Does nothing if the statement has already been compiled.

	void bindImpl() override;
		/// Binds all parameters and executes the statement.

	AbstractExtraction::ExtractorPtr extractor() override;
		/// Returns the concrete extractor used by the statement.

	AbstractBinding::BinderPtr binder() override;
		/// Returns the concrete binder used by the statement.

	void execDirectImpl(const std::string& query) override;
		/// Execute query directly impl

	void printErrors(std::ostream& os) const;
		/// Print errors, if any.

private:
	typedef Poco::Data::AbstractBindingVec    Bindings;
	typedef Poco::SharedPtr<Binder>           BinderPtr;
	typedef Poco::Data::AbstractExtractionVec Extractions;
	typedef Poco::SharedPtr<Preparator>       PreparatorPtr;
	typedef std::vector<PreparatorPtr>        PreparatorVec;
	typedef Poco::SharedPtr<Extractor>        ExtractorPtr;
	typedef std::vector<ExtractorPtr>         ExtractorVec;
	typedef std::vector<ODBCMetaColumn*>      ColumnPtrVec;
	typedef std::vector<ColumnPtrVec>         ColumnPtrVecVec;

	static const std::string INVALID_CURSOR_STATE;

	void clear();
		/// Closes the cursor and resets indicator variables.

	void doBind();
		/// Binds parameters.

	void makeInternalExtractors();
		/// Creates internal extractors if none were supplied from the user.

	bool isStoredProcedure() const override;
		/// Returns true if SQL is a stored procedure call.

	void doPrepare();
		/// Prepares placeholders for data returned by statement.
		/// It is called during statement compilation for SQL statements
		/// returning data. For stored procedures returning datasets,
		/// it is called upon the first check for data availability
		/// (see hasNext() function).

	bool hasData() const;
		/// Returns true if statement returns data.

	void makeStep();
		/// Fetches the next row of data.

	bool nextRowReady() const;
		/// Returns true if there is a row fetched but not yet extracted.

	void putData();
		/// Called whenever SQLExecute returns SQL_NEED_DATA. This is expected
		/// behavior for PB_AT_EXEC binding mode.

	void addPreparator();
	void fillColumns();
	void checkError(SQLRETURN rc, const std::string& msg="");

	struct ERROR_INFO
	{
		SQLCHAR state[8];
		SQLINTEGER native;
		SQLCHAR text[256];
	};
	void addErrors();

	const SQLHDBC&          _rConnection;
	const StatementHandle   _stmt;
	PreparatorVec           _preparations;
	BinderPtr               _pBinder;
	ExtractorVec            _extractors;
	bool                    _stepCalled;
	int                     _nextResponse;
	ColumnPtrVecVec         _columnPtrs;
	bool                    _prepared;
	mutable std::size_t     _affectedRowCount;
	bool                    _canCompile;
	std::vector<ERROR_INFO> _errorInfo;
};


//
// inlines
//
inline AbstractExtraction::ExtractorPtr ODBCStatementImpl::extractor()
{
	poco_assert_dbg (currentDataSet() < _extractors.size());
	poco_assert_dbg (_extractors[currentDataSet()]);
	return _extractors[currentDataSet()];
}


inline AbstractBinding::BinderPtr ODBCStatementImpl::binder()
{
	poco_assert_dbg (!_pBinder.isNull());
	return _pBinder;
}


inline std::size_t ODBCStatementImpl::columnsReturned() const
{
	poco_assert_dbg (currentDataSet() < _preparations.size());
	poco_assert_dbg (_preparations[currentDataSet()]);
	return static_cast<std::size_t>(_preparations[currentDataSet()]->columns());
}


inline bool ODBCStatementImpl::hasData() const
{
	return (columnsReturned() > 0);
}


inline bool ODBCStatementImpl::nextRowReady() const
{
	return (!Utility::isError(_nextResponse));
}


inline bool ODBCStatementImpl::canCompile() const
{
	return _canCompile;
}


} } } // namespace Poco::Data::ODBC


#endif // Data_ODBC_ODBCStatementImpl_INCLUDED
