2007-05-12 16:41:03 +02:00
|
|
|
//
|
|
|
|
// ODBCStatementImpl.cpp
|
|
|
|
//
|
2007-05-16 13:23:29 +02:00
|
|
|
// $Id: //poco/Main/Data/ODBC/src/ODBCStatementImpl.cpp#3 $
|
2007-05-12 16:41:03 +02:00
|
|
|
//
|
|
|
|
// Library: ODBC
|
|
|
|
// Package: ODBC
|
|
|
|
// Module: ODBCStatementImpl
|
|
|
|
//
|
|
|
|
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
|
|
|
|
// and Contributors.
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person or organization
|
|
|
|
// obtaining a copy of the software and accompanying documentation covered by
|
|
|
|
// this license (the "Software") to use, reproduce, display, distribute,
|
|
|
|
// execute, and transmit the Software, and to prepare derivative works of the
|
|
|
|
// Software, and to permit third-parties to whom the Software is furnished to
|
|
|
|
// do so, all subject to the following:
|
|
|
|
//
|
|
|
|
// The copyright notices in the Software and this entire statement, including
|
|
|
|
// the above license grant, this restriction and the following disclaimer,
|
|
|
|
// must be included in all copies of the Software, in whole or in part, and
|
|
|
|
// all derivative works of the Software, unless such copies or derivative
|
|
|
|
// works are solely in the form of machine-executable object code generated by
|
|
|
|
// a source language processor.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
|
|
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
|
|
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
|
|
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
#include "Poco/Data/ODBC/ODBCStatementImpl.h"
|
2007-05-31 01:20:47 +02:00
|
|
|
#include "Poco/Data/ODBC/ConnectionHandle.h"
|
2007-05-12 16:41:03 +02:00
|
|
|
#include "Poco/Data/ODBC/Utility.h"
|
|
|
|
#include "Poco/Data/ODBC/ODBCException.h"
|
|
|
|
#include "Poco/Data/AbstractPrepare.h"
|
|
|
|
#include <sql.h>
|
|
|
|
|
|
|
|
|
|
|
|
namespace Poco {
|
|
|
|
namespace Data {
|
|
|
|
namespace ODBC {
|
|
|
|
|
|
|
|
|
|
|
|
const std::string ODBCStatementImpl::INVALID_CURSOR_STATE = "24000";
|
|
|
|
|
|
|
|
|
|
|
|
ODBCStatementImpl::ODBCStatementImpl(SessionImpl& rSession):
|
2007-05-31 01:20:47 +02:00
|
|
|
Poco::Data::StatementImpl(rSession),
|
|
|
|
_rConnection(rSession.dbc()),
|
|
|
|
_stmt(rSession.dbc()),
|
2007-05-12 16:41:03 +02:00
|
|
|
_stepCalled(false),
|
|
|
|
_nextResponse(0)
|
|
|
|
{
|
|
|
|
checkError(SQLSetStmtAttr(_stmt,
|
|
|
|
SQL_CURSOR_TYPE,
|
|
|
|
(SQLPOINTER) SQL_CURSOR_FORWARD_ONLY,
|
|
|
|
0),
|
|
|
|
"SQLSetStmtAttr(SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY)");
|
|
|
|
|
2007-05-31 01:20:47 +02:00
|
|
|
if (session().getFeature("autoBind"))
|
2007-05-12 16:41:03 +02:00
|
|
|
{
|
|
|
|
SQLSetStmtAttr(_stmt,
|
|
|
|
SQL_ATTR_PARAM_BIND_TYPE,
|
|
|
|
(SQLPOINTER) SQL_PARAM_BIND_BY_COLUMN,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SQLSetStmtAttr(_stmt,
|
|
|
|
SQL_ATTR_ROW_ARRAY_SIZE,
|
|
|
|
(SQLPOINTER) 1,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ODBCStatementImpl::~ODBCStatementImpl()
|
|
|
|
{
|
|
|
|
ColumnPtrVec::iterator it = _columnPtrs.begin();
|
|
|
|
ColumnPtrVec::iterator itEnd = _columnPtrs.end();
|
|
|
|
for(; it != itEnd; ++it) delete *it;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ODBCStatementImpl::compileImpl()
|
|
|
|
{
|
2007-06-06 03:26:58 +02:00
|
|
|
_stepCalled = false;
|
2007-05-12 16:41:03 +02:00
|
|
|
_nextResponse = 0;
|
|
|
|
|
|
|
|
std::string statement(toString());
|
|
|
|
if (statement.empty())
|
|
|
|
throw ODBCException("Empty statements are illegal");
|
|
|
|
|
2007-05-31 01:20:47 +02:00
|
|
|
Preparation::DataExtraction ext = session().getFeature("autoExtract") ?
|
2007-05-12 16:41:03 +02:00
|
|
|
Preparation::DE_BOUND : Preparation::DE_MANUAL;
|
|
|
|
|
2007-05-31 01:20:47 +02:00
|
|
|
std::size_t maxFieldSize = AnyCast<std::size_t>(session().getProperty("maxFieldSize"));
|
2007-05-12 16:41:03 +02:00
|
|
|
_pPreparation = new Preparation(_stmt,
|
|
|
|
statement,
|
|
|
|
maxFieldSize,
|
|
|
|
ext);
|
|
|
|
|
2007-05-31 01:20:47 +02:00
|
|
|
Binder::ParameterBinding bind = session().getFeature("autoBind") ?
|
2007-05-12 16:41:03 +02:00
|
|
|
Binder::PB_IMMEDIATE : Binder::PB_AT_EXEC;
|
|
|
|
|
2007-06-02 03:51:38 +02:00
|
|
|
TypeInfo* pDT = 0;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Poco::Any dti = session().getProperty("dataTypeInfo");
|
|
|
|
pDT = AnyCast<TypeInfo*>(dti);
|
|
|
|
}catch (NotSupportedException&) { }
|
|
|
|
|
|
|
|
_pBinder = new Binder(_stmt, bind, pDT);
|
2007-05-12 16:41:03 +02:00
|
|
|
_pExtractor = new Extractor(_stmt, *_pPreparation);
|
|
|
|
|
2007-06-06 03:26:58 +02:00
|
|
|
// This is a hack to conform to some ODBC drivers behavior (e.g. MS SQLServer) with
|
|
|
|
// stored procedure calls: driver refuses to report the number of columns, unless all
|
|
|
|
// parameters for the stored procedure are bound. Since number of columns is essential
|
|
|
|
// information for the internal extraction creation, in order to allow for querying it,
|
|
|
|
// these calls must occur before.
|
|
|
|
fixupBinding(); doBind(false, true);
|
|
|
|
|
2007-05-12 16:41:03 +02:00
|
|
|
bool dataAvailable = hasData();
|
|
|
|
if (dataAvailable && !extractions().size())
|
|
|
|
{
|
|
|
|
fillColumns();
|
|
|
|
makeExtractors(columnsReturned());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Preparation::DE_BOUND == ext && dataAvailable)
|
|
|
|
{
|
|
|
|
Extractions& extracts = extractions();
|
|
|
|
Extractions::iterator it = extracts.begin();
|
|
|
|
Extractions::iterator itEnd = extracts.end();
|
2007-05-23 03:07:39 +02:00
|
|
|
for (std::size_t pos = 0; it != itEnd; ++it)
|
2007-05-12 16:41:03 +02:00
|
|
|
{
|
|
|
|
AbstractPrepare* pAP = (*it)->createPrepareObject(_pPreparation, pos);
|
|
|
|
pAP->prepare();
|
|
|
|
pos += (*it)->numOfColumnsHandled();
|
|
|
|
delete pAP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ODBCStatementImpl::canBind() const
|
|
|
|
{
|
|
|
|
if (!bindings().empty())
|
|
|
|
return (*bindings().begin())->canBind();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-06-06 03:26:58 +02:00
|
|
|
void ODBCStatementImpl::doBind(bool clear, bool reset)
|
2007-05-12 16:41:03 +02:00
|
|
|
{
|
2007-06-06 03:26:58 +02:00
|
|
|
if (clear) this->clear();
|
2007-05-12 16:41:03 +02:00
|
|
|
Bindings& binds = bindings();
|
|
|
|
if (!binds.empty())
|
|
|
|
{
|
|
|
|
Bindings::iterator it = binds.begin();
|
|
|
|
Bindings::iterator itEnd = binds.end();
|
2007-05-23 03:07:39 +02:00
|
|
|
for (std::size_t pos = 0; it != itEnd && (*it)->canBind(); ++it)
|
2007-05-12 16:41:03 +02:00
|
|
|
{
|
|
|
|
(*it)->bind(pos);
|
|
|
|
pos += (*it)->numOfColumnsHandled();
|
|
|
|
}
|
2007-06-06 03:26:58 +02:00
|
|
|
|
|
|
|
if (reset)
|
|
|
|
{
|
|
|
|
it = binds.begin();
|
|
|
|
for (; it != itEnd && (*it)->canBind(); ++it)
|
|
|
|
(*it)->reset();
|
|
|
|
}
|
2007-05-12 16:41:03 +02:00
|
|
|
}
|
2007-06-06 03:26:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ODBCStatementImpl::bindImpl()
|
|
|
|
{
|
|
|
|
doBind();
|
2007-05-12 16:41:03 +02:00
|
|
|
|
|
|
|
SQLRETURN rc = SQLExecute(_stmt);
|
|
|
|
|
|
|
|
if (SQL_NEED_DATA == rc) putData();
|
|
|
|
else checkError(rc, "SQLExecute()");
|
2007-06-01 00:40:27 +02:00
|
|
|
|
2007-06-07 00:43:10 +02:00
|
|
|
_pBinder->synchronize();
|
2007-05-12 16:41:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ODBCStatementImpl::putData()
|
|
|
|
{
|
|
|
|
SQLPOINTER pParam = 0;
|
2007-06-08 03:33:56 +02:00
|
|
|
SQLINTEGER dataSize = 0;
|
2007-05-12 16:41:03 +02:00
|
|
|
SQLRETURN rc = SQLParamData(_stmt, &pParam);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
poco_assert_dbg (pParam);
|
2007-06-08 03:33:56 +02:00
|
|
|
dataSize = (SQLINTEGER) _pBinder->parameterSize(pParam);
|
2007-05-12 16:41:03 +02:00
|
|
|
|
|
|
|
if (Utility::isError(SQLPutData(_stmt, pParam, dataSize)))
|
|
|
|
throw StatementException(_stmt, "SQLPutData()");
|
2007-06-08 03:33:56 +02:00
|
|
|
|
2007-05-12 16:41:03 +02:00
|
|
|
}while (SQL_NEED_DATA == (rc = SQLParamData(_stmt, &pParam)));
|
|
|
|
|
|
|
|
checkError(rc, "SQLParamData()");
|
2007-06-07 00:43:10 +02:00
|
|
|
}
|
2007-06-06 03:26:58 +02:00
|
|
|
|
2007-06-07 00:43:10 +02:00
|
|
|
|
2007-05-12 16:41:03 +02:00
|
|
|
void ODBCStatementImpl::clear()
|
|
|
|
{
|
|
|
|
SQLRETURN rc = SQLCloseCursor(_stmt);
|
|
|
|
if (Utility::isError(rc))
|
|
|
|
{
|
|
|
|
StatementError err(_stmt);
|
|
|
|
bool ignoreError = false;
|
|
|
|
|
|
|
|
const StatementDiagnostics& diagnostics = err.diagnostics();
|
|
|
|
//ignore "Invalid cursor state" error
|
|
|
|
//(returned by 3.x drivers when cursor is not opened)
|
|
|
|
for (int i = 0; i < diagnostics.count(); ++i)
|
|
|
|
{
|
|
|
|
if (ignoreError =
|
|
|
|
(INVALID_CURSOR_STATE == std::string(diagnostics.sqlState(i))))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ignoreError)
|
|
|
|
throw StatementException(_stmt, "SQLCloseCursor()");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ODBCStatementImpl::hasNext()
|
|
|
|
{
|
|
|
|
if (hasData())
|
|
|
|
{
|
|
|
|
if (_stepCalled)
|
|
|
|
return _stepCalled = nextRowReady();
|
|
|
|
|
|
|
|
_stepCalled = true;
|
|
|
|
_nextResponse = SQLFetch(_stmt);
|
|
|
|
|
|
|
|
if (!nextRowReady())
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
if (Utility::isError(_nextResponse))
|
|
|
|
checkError(_nextResponse, "SQLFetch()");
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ODBCStatementImpl::next()
|
|
|
|
{
|
|
|
|
if (nextRowReady())
|
|
|
|
{
|
|
|
|
poco_assert (columnsExtracted() == _pPreparation->columns());
|
|
|
|
|
|
|
|
Extractions& extracts = extractions();
|
|
|
|
Extractions::iterator it = extracts.begin();
|
|
|
|
Extractions::iterator itEnd = extracts.end();
|
2007-05-23 03:07:39 +02:00
|
|
|
for (std::size_t pos = 0; it != itEnd; ++it)
|
2007-05-12 16:41:03 +02:00
|
|
|
{
|
|
|
|
(*it)->extract(pos);
|
|
|
|
pos += (*it)->numOfColumnsHandled();
|
|
|
|
}
|
|
|
|
_stepCalled = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw StatementException(_stmt,
|
|
|
|
std::string("Iterator Error: trying to access the next value"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::string ODBCStatementImpl::nativeSQL()
|
|
|
|
{
|
|
|
|
std::string statement = toString();
|
|
|
|
|
|
|
|
//Hopefully, double the original statement length is enough.
|
|
|
|
//If it is not, the total available length is indicated in the retlen parameter,
|
|
|
|
//which is in turn used to resize the buffer and request the native SQL again.
|
|
|
|
SQLINTEGER length = (SQLINTEGER) statement.size() * 2;
|
|
|
|
|
|
|
|
char* pNative = 0;
|
|
|
|
SQLINTEGER retlen = length;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
delete [] pNative;
|
|
|
|
pNative = new char[retlen];
|
|
|
|
memset(pNative, 0, retlen);
|
|
|
|
length = retlen;
|
2007-05-31 01:20:47 +02:00
|
|
|
if (Utility::isError(SQLNativeSql(_rConnection,
|
2007-05-12 16:41:03 +02:00
|
|
|
(POCO_SQLCHAR*) statement.c_str(),
|
|
|
|
(SQLINTEGER) statement.size(),
|
|
|
|
(POCO_SQLCHAR*) pNative,
|
|
|
|
length,
|
|
|
|
&retlen)))
|
|
|
|
{
|
|
|
|
delete [] pNative;
|
2007-05-31 01:20:47 +02:00
|
|
|
throw ConnectionException(_rConnection, "SQLNativeSql()");
|
2007-05-12 16:41:03 +02:00
|
|
|
}
|
|
|
|
++retlen;//accomodate for terminating '\0'
|
|
|
|
}while (retlen > length);
|
|
|
|
|
|
|
|
std::string sql(pNative);
|
|
|
|
delete [] pNative;
|
|
|
|
return sql;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ODBCStatementImpl::checkError(SQLRETURN rc, const std::string& msg)
|
|
|
|
{
|
|
|
|
if (Utility::isError(rc))
|
|
|
|
{
|
2007-06-08 03:33:56 +02:00
|
|
|
std::ostringstream os;
|
|
|
|
os << std::endl << "Requested SQL statement: " << toString() << std::endl;
|
|
|
|
os << "Native SQL statement: " << nativeSQL() << std::endl;
|
|
|
|
std::string str(msg); str += os.str();
|
2007-05-12 16:41:03 +02:00
|
|
|
|
|
|
|
throw StatementException(_stmt, str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ODBCStatementImpl::fillColumns()
|
|
|
|
{
|
|
|
|
Poco::UInt32 colCount = columnsReturned();
|
|
|
|
|
2007-05-16 06:17:03 +02:00
|
|
|
for (int i = 0; i < colCount; ++i)
|
2007-05-12 16:41:03 +02:00
|
|
|
_columnPtrs.push_back(new ODBCColumn(_stmt, i));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} } } // namespace Poco::Data::ODBC
|