// // ODBCStatementImpl.cpp // // $Id: //poco/Main/Data/ODBC/src/ODBCStatementImpl.cpp#8 $ // // 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" #include "Poco/Data/ODBC/ConnectionHandle.h" #include "Poco/Data/ODBC/Utility.h" #include "Poco/Data/ODBC/ODBCException.h" #include "Poco/Data/AbstractPrepare.h" #include "Poco/Exception.h" #include #ifdef POCO_OS_FAMILY_WINDOWS #undef max #pragma warning(disable:4312)// 'type cast' : conversion from 'Poco::UInt32' to 'SQLPOINTER' of greater size #endif using Poco::DataFormatException; namespace Poco { namespace Data { namespace ODBC { const std::string ODBCStatementImpl::INVALID_CURSOR_STATE = "24000"; ODBCStatementImpl::ODBCStatementImpl(SessionImpl& rSession): Poco::Data::StatementImpl(rSession), _rConnection(rSession.dbc()), _stmt(rSession.dbc()), _stepCalled(false), _nextResponse(0), _prepared(false) { if (session().getFeature("autoBind")) SQLSetStmtAttr(_stmt, SQL_ATTR_PARAM_BIND_TYPE, (SQLPOINTER) SQL_PARAM_BIND_BY_COLUMN, 0); Poco::UInt32 step = getStep(); SQLSetStmtAttr(_stmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) step, 0); } ODBCStatementImpl::~ODBCStatementImpl() { ColumnPtrVec::iterator it = _columnPtrs.begin(); ColumnPtrVec::iterator itEnd = _columnPtrs.end(); for(; it != itEnd; ++it) delete *it; } void ODBCStatementImpl::compileImpl() { _stepCalled = false; _nextResponse = 0; if (_preparations.size()) PreparationVec().swap(_preparations); addPreparation(); Binder::ParameterBinding bind = session().getFeature("autoBind") ? Binder::PB_IMMEDIATE : Binder::PB_AT_EXEC; TypeInfo* pDT = 0; try { Poco::Any dti = session().getProperty("dataTypeInfo"); pDT = AnyCast(dti); }catch (NotSupportedException&) { } _pBinder = new Binder(_stmt, bind, pDT); // 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); makeInternalExtractors(); doPrepare(); } void ODBCStatementImpl::makeInternalExtractors() { if (hasData() && !extractions().size()) { try { fillColumns(); } catch (DataFormatException&) { if (isStoredProcedure()) return; throw; } makeExtractors(columnsReturned()); fixupExtraction(); } } 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(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()) { Poco::UInt32 curDataSet = currentDataSet(); poco_check_ptr (_preparations[curDataSet]); Extractions& extracts = extractions(); Extractions::iterator it = extracts.begin(); Extractions::iterator itEnd = extracts.end(); for (std::size_t pos = 0; it != itEnd; ++it) { AbstractPrepare* pAP = (*it)->createPrepareObject(_preparations[curDataSet], pos); pAP->prepare(); pos += (*it)->numOfColumnsHandled(); delete pAP; } _prepared = true; } } bool ODBCStatementImpl::canBind() const { if (!bindings().empty()) return (*bindings().begin())->canBind(); return false; } void ODBCStatementImpl::doBind(bool clear, bool reset) { if (clear) this->clear(); Bindings& binds = bindings(); if (!binds.empty()) { Bindings::iterator it = binds.begin(); Bindings::iterator itEnd = binds.end(); for (std::size_t pos = 0; it != itEnd && (*it)->canBind(); ++it) { (*it)->bind(pos); pos += (*it)->numOfColumnsHandled(); } if (reset) { it = binds.begin(); for (; it != itEnd && (*it)->canBind(); ++it) (*it)->reset(); } } } void ODBCStatementImpl::bindImpl() { doBind(); SQLRETURN rc = SQLExecute(_stmt); if (SQL_NEED_DATA == rc) putData(); else checkError(rc, "SQLExecute()"); _pBinder->synchronize(); } void ODBCStatementImpl::putData() { SQLPOINTER pParam = 0; SQLINTEGER dataSize = 0; SQLRETURN rc; while (SQL_NEED_DATA == (rc = SQLParamData(_stmt, &pParam))) { poco_assert_dbg (pParam); dataSize = (SQLINTEGER) _pBinder->parameterSize(pParam); if (Utility::isError(SQLPutData(_stmt, pParam, dataSize))) throw StatementException(_stmt, "SQLPutData()"); } checkError(rc, "SQLParamData()"); } void ODBCStatementImpl::clear() { SQLRETURN rc = SQLCloseCursor(_stmt); _stepCalled = false; 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(); if (_stepCalled) return _stepCalled = nextRowReady(); makeStep(); if (!nextRowReady()) { try { activateNextDataSet(); } catch (InvalidAccessException&) { return false; } try { checkError(SQLMoreResults(_stmt)); } catch (NoDataException&) { return false; } addPreparation(); doPrepare(); fixupExtraction(); makeStep(); } else if (Utility::isError(_nextResponse)) checkError(_nextResponse, "SQLFetch()"); return true; } return false; } void ODBCStatementImpl::makeStep() { _extractors[currentDataSet()]->reset(); _nextResponse = SQLFetch(_stmt); _stepCalled = true; } Poco::UInt32 ODBCStatementImpl::next() { if (nextRowReady()) { Extractions& extracts = extractions(); Extractions::iterator it = extracts.begin(); Extractions::iterator itEnd = extracts.end(); for (std::size_t pos = 0; it != itEnd; ++it) { (*it)->extract(pos); pos += (*it)->numOfColumnsHandled(); } _stepCalled = false; } else { throw StatementException(_stmt, std::string("Iterator Error: trying to access the next value")); } return 1u; } 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]; memset(pNative, 0, retlen); length = retlen; if (Utility::isError(SQLNativeSql(_rConnection, (SQLCHAR*) statement.c_str(), (SQLINTEGER) statement.size(), (SQLCHAR*) pNative, length, &retlen))) { delete [] pNative; throw ConnectionException(_rConnection, "SQLNativeSql()"); } ++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)) { if (rc != SQL_NO_DATA) { 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(); throw StatementException(_stmt, str); } else throw NoDataException(); } } void ODBCStatementImpl::fillColumns() { Poco::UInt32 colCount = columnsReturned(); for (int i = 0; i < colCount; ++i) _columnPtrs.push_back(new ODBCColumn(_stmt, i)); } bool ODBCStatementImpl::isStoredProcedure() const { std::string str = toString(); if (trimInPlace(str).size() < 2) return false; return ('{' == str[0] && '}' == str[str.size()-1]); } } } } // namespace Poco::Data::ODBC