poco/Data/ODBC/src/ODBCStatementImpl.cpp

469 lines
11 KiB
C++
Raw Normal View History

2007-05-12 16:41:03 +02:00
//
// ODBCStatementImpl.cpp
//
// $Id: //poco/Main/Data/ODBC/src/ODBCStatementImpl.cpp#8 $
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 "Poco/Exception.h"
2007-05-12 16:41:03 +02:00
2007-11-11 00:21:28 +01:00
#ifdef POCO_OS_FAMILY_WINDOWS
#pragma warning(disable:4312)// 'type cast' : conversion from 'Poco::UInt32' to 'SQLPOINTER' of greater size
#endif
2007-05-12 16:41:03 +02:00
using Poco::DataFormatException;
2007-05-12 16:41:03 +02:00
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),
_prepared(false),
2008-01-21 01:56:52 +01:00
_affectedRowCount(0),
2008-01-22 03:05:04 +01:00
_canCompile(true)
2007-05-12 16:41:03 +02:00
{
}
ODBCStatementImpl::~ODBCStatementImpl()
{
2008-01-21 01:56:52 +01:00
ColumnPtrVecVec::iterator it = _columnPtrs.begin();
ColumnPtrVecVec::iterator end = _columnPtrs.end();
for(; it != end; ++it)
{
ColumnPtrVec::iterator itC = it->begin();
ColumnPtrVec::iterator endC = it->end();
for(; itC != endC; ++itC) delete *itC;
}
2007-05-12 16:41:03 +02:00
}
2008-01-22 03:05:04 +01:00
void ODBCStatementImpl::compileImpl()
2007-05-12 16:41:03 +02:00
{
2008-01-22 03:05:04 +01:00
if (!_canCompile) return;
2008-01-21 01:56:52 +01:00
2007-06-06 03:26:58 +02:00
_stepCalled = false;
2007-05-12 16:41:03 +02:00
_nextResponse = 0;
if (_preparations.size())
PreparationVec().swap(_preparations);
2007-05-12 16:41:03 +02:00
addPreparation();
2007-05-12 16:41:03 +02:00
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&) { }
2007-12-16 18:44:46 +01:00
std::size_t maxFieldSize = AnyCast<std::size_t>(session().getProperty("maxFieldSize"));
_pBinder = new Binder(_stmt, maxFieldSize, bind, pDT);
2007-06-19 04:13:30 +02:00
makeInternalExtractors();
doPrepare();
2008-01-21 01:56:52 +01:00
2008-01-22 03:05:04 +01:00
_canCompile = false;
}
void ODBCStatementImpl::makeInternalExtractors()
{
if (hasData() && !extractions().size())
2007-05-12 16:41:03 +02:00
{
try
{
fillColumns();
} catch (DataFormatException&)
{
if (isStoredProcedure()) return;
throw;
}
2007-05-12 16:41:03 +02:00
makeExtractors(columnsReturned());
fixupExtraction();
2007-05-12 16:41:03 +02:00
}
}
2007-05-12 16:41:03 +02:00
void ODBCStatementImpl::addPreparation()
{
if (0 == _preparations.size())
{
std::string statement(toString());
if (statement.empty())
throw ODBCException("Empty statements are illegal");
Preparation::DataExtraction ext = session().getFeature("autoExtract") ?
Preparation::DE_BOUND : Preparation::DE_MANUAL;
std::size_t maxFieldSize = AnyCast<std::size_t>(session().getProperty("maxFieldSize"));
_preparations.push_back(new Preparation(_stmt, statement, maxFieldSize, ext));
}
else
_preparations.push_back(new Preparation(*_preparations[0]));
_extractors.push_back(new Extractor(_stmt, *_preparations.back()));
}
void ODBCStatementImpl::doPrepare()
{
if (session().getFeature("autoExtract") && hasData())
2007-05-12 16:41:03 +02:00
{
Poco::UInt32 curDataSet = currentDataSet();
poco_check_ptr (_preparations[curDataSet]);
2007-05-12 16:41:03 +02:00
Extractions& extracts = extractions();
Extractions::iterator it = extracts.begin();
Extractions::iterator itEnd = extracts.end();
2007-12-16 18:44:46 +01:00
if (it != itEnd && (*it)->isBulk())
{
Poco::UInt32 limit = getExtractionLimit();
if (limit == Limit::LIMIT_UNLIMITED)
throw InvalidArgumentException("Bulk operation not allowed without limit.");
2008-05-30 21:00:11 +02:00
checkError(Poco::Data::ODBC::SQLSetStmtAttr(_stmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) limit, 0),
2007-12-16 18:44:46 +01:00
"SQLSetStmtAttr(SQL_ATTR_ROW_ARRAY_SIZE)");
}
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(_preparations[curDataSet], pos);
2007-05-12 16:41:03 +02:00
pAP->prepare();
pos += (*it)->numOfColumnsHandled();
delete pAP;
}
_prepared = true;
2007-05-12 16:41:03 +02:00
}
}
bool ODBCStatementImpl::canBind() const
{
if (!bindings().empty())
return (*bindings().begin())->canBind();
return false;
}
void ODBCStatementImpl::doBind()
2007-05-12 16:41:03 +02:00
{
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-06-06 03:26:58 +02:00
if (it != itEnd && 0 == _affectedRowCount)
_affectedRowCount = static_cast<Poco::UInt32>((*it)->numOfRowsHandled());
2007-12-16 18:44:46 +01:00
for (std::size_t pos = 0; it != itEnd && (*it)->canBind(); ++it)
{
(*it)->bind(pos);
pos += (*it)->numOfColumnsHandled();
2007-06-06 03:26:58 +02:00
}
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
_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;
SQLRETURN rc;
2007-05-12 16:41:03 +02:00
while (SQL_NEED_DATA == (rc = SQLParamData(_stmt, &pParam)))
2007-05-12 16:41:03 +02:00
{
if (pParam)
{
dataSize = (SQLINTEGER) _pBinder->parameterSize(pParam);
2007-05-12 16:41:03 +02:00
if (Utility::isError(SQLPutData(_stmt, pParam, dataSize)))
throw StatementException(_stmt, "SQLPutData()");
}
else // if pParam is null pointer, do a dummy call
{
char dummy = 0;
if (Utility::isError(SQLPutData(_stmt, &dummy, 0)))
throw StatementException(_stmt, "SQLPutData()");
}
}
2007-05-12 16:41:03 +02:00
checkError(rc, "SQLParamData()");
}
2007-06-06 03:26:58 +02:00
2007-05-12 16:41:03 +02:00
void ODBCStatementImpl::clear()
{
SQLRETURN rc = SQLCloseCursor(_stmt);
_stepCalled = false;
_affectedRowCount = 0;
2007-05-12 16:41:03 +02:00
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 (!extractions().size())
makeInternalExtractors();
if (!_prepared) doPrepare();
2007-05-12 16:41:03 +02:00
if (_stepCalled)
return _stepCalled = nextRowReady();
makeStep();
2007-05-12 16:41:03 +02:00
if (!nextRowReady())
{
2008-01-21 01:56:52 +01:00
if (hasMoreDataSets()) activateNextDataSet();
else return false;
2007-12-16 18:44:46 +01:00
if (SQL_NO_DATA == SQLMoreResults(_stmt))
return false;
addPreparation();
doPrepare();
fixupExtraction();
makeStep();
}
else if (Utility::isError(_nextResponse))
2007-05-12 16:41:03 +02:00
checkError(_nextResponse, "SQLFetch()");
return true;
}
return false;
}
void ODBCStatementImpl::makeStep()
{
_extractors[currentDataSet()]->reset();
_nextResponse = SQLFetch(_stmt);
2008-02-07 21:14:40 +01:00
checkError(_nextResponse);
_stepCalled = true;
}
2007-11-11 00:21:28 +01:00
Poco::UInt32 ODBCStatementImpl::next()
2007-05-12 16:41:03 +02:00
{
2007-12-16 18:44:46 +01:00
std::size_t count = 0;
2007-05-12 16:41:03 +02:00
if (nextRowReady())
{
Extractions& extracts = extractions();
Extractions::iterator it = extracts.begin();
Extractions::iterator itEnd = extracts.end();
2007-12-16 18:44:46 +01:00
std::size_t prevCount = 0;
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
{
2007-12-16 18:44:46 +01:00
count = (*it)->extract(pos);
if (prevCount && count != prevCount)
throw IllegalStateException("Different extraction counts");
prevCount = count;
2007-05-12 16:41:03 +02:00
pos += (*it)->numOfColumnsHandled();
}
_stepCalled = false;
}
else
{
throw StatementException(_stmt,
std::string("Iterator Error: trying to access the next value"));
}
2007-11-11 00:21:28 +01:00
2007-12-16 18:44:46 +01:00
return static_cast<Poco::UInt32>(count);
return 0;
2007-05-12 16:41:03 +02:00
}
std::string ODBCStatementImpl::nativeSQL()
{
std::string statement = toString();
SQLINTEGER length = (SQLINTEGER) statement.size() * 2;
char* pNative = 0;
SQLINTEGER retlen = length;
do
{
delete [] pNative;
pNative = new char[retlen];
2008-05-30 21:00:11 +02:00
std::memset(pNative, 0, retlen);
2007-05-12 16:41:03 +02:00
length = retlen;
2007-05-31 01:20:47 +02:00
if (Utility::isError(SQLNativeSql(_rConnection,
(SQLCHAR*) statement.c_str(),
2007-05-12 16:41:03 +02:00
(SQLINTEGER) statement.size(),
(SQLCHAR*) pNative,
2007-05-12 16:41:03 +02:00
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)
{
2008-02-07 21:14:40 +01:00
if (SQL_NO_DATA == rc) return;
2007-05-12 16:41:03 +02:00
if (Utility::isError(rc))
{
2008-02-07 21:14:40 +01: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();
2008-02-07 21:14:40 +01:00
throw StatementException(_stmt, str);
2007-05-12 16:41:03 +02:00
}
}
void ODBCStatementImpl::fillColumns()
{
Poco::UInt32 colCount = columnsReturned();
2008-01-21 01:56:52 +01:00
Poco::UInt32 curDataSet = currentDataSet();
if (curDataSet >= _columnPtrs.size())
_columnPtrs.resize(curDataSet + 1);
2007-05-12 16:41:03 +02:00
for (int i = 0; i < colCount; ++i)
2008-01-21 01:56:52 +01:00
_columnPtrs[curDataSet].push_back(new ODBCMetaColumn(_stmt, i));
2007-05-12 16:41:03 +02:00
}
bool ODBCStatementImpl::isStoredProcedure() const
{
std::string str = toString();
if (trimInPlace(str).size() < 2) return false;
return ('{' == str[0] && '}' == str[str.size()-1]);
}
2007-12-16 18:44:46 +01:00
const MetaColumn& ODBCStatementImpl::metaColumn(Poco::UInt32 pos) const
{
2008-01-21 01:56:52 +01:00
Poco::UInt32 curDataSet = currentDataSet();
poco_assert_dbg (curDataSet < _columnPtrs.size());
std::size_t sz = _columnPtrs[curDataSet].size();
2007-12-16 18:44:46 +01:00
if (0 == sz || pos > sz - 1)
throw InvalidAccessException(format("Invalid column number: %u", pos));
2008-01-21 01:56:52 +01:00
return *_columnPtrs[curDataSet][pos];
2007-12-16 18:44:46 +01:00
}
Poco::UInt32 ODBCStatementImpl::affectedRowCount() const
{
if (0 == _affectedRowCount)
{
SQLLEN rows;
if (!Utility::isError(SQLRowCount(_stmt, &rows)))
_affectedRowCount = static_cast<Poco::UInt32>(rows);
}
return _affectedRowCount;
}
2007-05-12 16:41:03 +02:00
} } } // namespace Poco::Data::ODBC