fix(Poco::Data): fixes and improvements #4198 (#4199)

* fix(Poco::Data): fixes and improvements #4198

* chore: remove inadvertently commited garbage file

* fix(SQLite): SQLChannel tests #4198

* fix(Data::SessionPool): Improve Data::SessionPool thread safety #4206
This commit is contained in:
Aleksandar Fabijanic 2023-10-22 12:53:54 +02:00 committed by GitHub
parent de04b9eac7
commit 5131fe1c15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 2462 additions and 781 deletions

View File

@ -407,7 +407,7 @@ private:
decDigits,
(SQLPOINTER) &val, 0, 0)))
{
throw StatementException(_rStmt, "SQLBindParameter()");
throw StatementException(_rStmt, "ODBC::Binder::SQLBindParameter()");
}
}
@ -436,14 +436,14 @@ private:
(SQLUSMALLINT) pos + 1,
SQL_PARAM_INPUT,
SQL_C_BINARY,
SQL_LONGVARBINARY,
Utility::sqlDataType(SQL_C_BINARY),
columnSize,
0,
pVal,
(SQLINTEGER) size,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(LOB)");
throw StatementException(_rStmt, "ODBC::Binder::SQLBindParameter(LOB)");
}
}
@ -476,7 +476,7 @@ private:
0,
&(*_vecLengthIndicator[pos])[0])))
{
throw StatementException(_rStmt, "SQLBindParameter()");
throw StatementException(_rStmt, "ODBC::Binder::SQLBindParameter()");
}
}
@ -537,7 +537,7 @@ private:
0,
&(*_vecLengthIndicator[pos])[0])))
{
throw StatementException(_rStmt, "SQLBindParameter()");
throw StatementException(_rStmt, "ODBC::Binder::SQLBindParameter()");
}
}
@ -577,7 +577,7 @@ private:
if (size == _maxFieldSize)
{
getMinValueSize(*pVal, size);
// accomodate for terminating zero
// accommodate for terminating zero
if (size != _maxFieldSize) ++size;
}
@ -614,14 +614,14 @@ private:
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_CHAR,
SQL_LONGVARCHAR,
Utility::sqlDataType(SQL_C_CHAR),
(SQLUINTEGER) size - 1,
0,
_charPtrs[pos],
(SQLINTEGER) size,
&(*_vecLengthIndicator[pos])[0])))
{
throw StatementException(_rStmt, Poco::format("SQLBindParameter(%s)", typeID));
throw StatementException(_rStmt, "SQLBindParameter(std::vector<std::string>)");
}
}
@ -672,7 +672,7 @@ private:
{
strSize = it->size() * sizeof(UTF16Char);
if (strSize > size)
throw LengthExceededException("SQLBindParameter(std::vector<UTF16String>)");
throw LengthExceededException("ODBC::Binder::bindImplContainerUTF16String:SQLBindParameter(std::vector<UTF16String>)");
std::memcpy(pBuf + offset, it->data(), strSize);
offset += size;
}
@ -681,14 +681,14 @@ private:
(SQLUSMALLINT)pos + 1,
toODBCDirection(dir),
SQL_C_WCHAR,
SQL_WLONGVARCHAR,
Utility::sqlDataType(SQL_C_WCHAR),
(SQLUINTEGER)size - 1,
0,
_utf16CharPtrs[pos],
(SQLINTEGER)size,
&(*_vecLengthIndicator[pos])[0])))
{
throw StatementException(_rStmt, "SQLBindParameter(std::vector<UTF16String>)");
throw StatementException(_rStmt, "ODBC::Binder::bindImplContainerUTF16String:SQLBindParameter(std::vector<UTF16String>)");
}
}
@ -699,14 +699,14 @@ private:
typedef typename LOBType::ValueType CharType;
if (isOutBound(dir) || !isInBound(dir))
throw NotImplementedException("BLOB container parameter type can only be inbound.");
throw NotImplementedException("ODBC::Binder::bindImplContainerLOB():BLOB container parameter type can only be inbound.");
if (PB_IMMEDIATE != _paramBinding)
throw InvalidAccessException("Containers can only be bound immediately.");
throw InvalidAccessException("ODBC::Binder::bindImplContainerLOB():Containers can only be bound immediately.");
std::size_t length = val.size();
if (0 == length)
throw InvalidArgumentException("Empty container not allowed.");
throw InvalidArgumentException("ODBC::Binder::bindImplContainerLOB():Empty container not allowed.");
setParamSetSize(length);
@ -742,7 +742,7 @@ private:
{
blobSize = cIt->size();
if (blobSize > size)
throw LengthExceededException("SQLBindParameter(std::vector<BLOB>)");
throw LengthExceededException("ODBC::Binder::bindImplContainerLOB():SQLBindParameter(std::vector<BLOB>)");
std::memcpy(_charPtrs[pos] + offset, cIt->rawContent(), blobSize * sizeof(CharType));
offset += size;
}
@ -751,14 +751,14 @@ private:
(SQLUSMALLINT) pos + 1,
SQL_PARAM_INPUT,
SQL_C_BINARY,
SQL_LONGVARBINARY,
Utility::sqlDataType(SQL_C_BINARY),
(SQLUINTEGER) size,
0,
_charPtrs[pos],
(SQLINTEGER) size,
&(*_vecLengthIndicator[pos])[0])))
{
throw StatementException(_rStmt, "SQLBindParameter(std::vector<BLOB>)");
throw StatementException(_rStmt, "ODBC::Binder::bindImplContainerLOB():SQLBindParameter(std::vector<BLOB>)");
}
}
@ -766,15 +766,15 @@ private:
void bindImplContainerDate(std::size_t pos, const C& val, Direction dir)
{
if (isOutBound(dir) || !isInBound(dir))
throw NotImplementedException("Date vector parameter type can only be inbound.");
throw NotImplementedException("ODBC::Binder::bindImplContainerDate():Date vector parameter type can only be inbound.");
if (PB_IMMEDIATE != _paramBinding)
throw InvalidAccessException("std::vector can only be bound immediately.");
throw InvalidAccessException("ODBC::Binder::bindImplContainerDate():std::vector can only be bound immediately.");
std::size_t length = val.size();
if (0 == length)
throw InvalidArgumentException("Empty vector not allowed.");
throw InvalidArgumentException("ODBC::Binder::bindImplContainerDate():Empty vector not allowed.");
setParamSetSize(length);
@ -800,14 +800,14 @@ private:
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TYPE_DATE,
SQL_TYPE_DATE,
Utility::sqlDataType(SQL_C_TYPE_DATE),
colSize,
decDigits,
(SQLPOINTER) &(*_dateVecVec[pos])[0],
0,
&(*_vecLengthIndicator[pos])[0])))
{
throw StatementException(_rStmt, "SQLBindParameter(Date[])");
throw StatementException(_rStmt, "ODBC::Binder::bindImplContainerDate():SQLBindParameter(Date[])");
}
}
@ -815,14 +815,14 @@ private:
void bindImplContainerTime(std::size_t pos, const C& val, Direction dir)
{
if (isOutBound(dir) || !isInBound(dir))
throw NotImplementedException("Time container parameter type can only be inbound.");
throw NotImplementedException("ODBC::Binder::bindImplContainerTime():Time container parameter type can only be inbound.");
if (PB_IMMEDIATE != _paramBinding)
throw InvalidAccessException("Containers can only be bound immediately.");
throw InvalidAccessException("ODBC::Binder::bindImplContainerTime():Containers can only be bound immediately.");
std::size_t length = val.size();
if (0 == length)
throw InvalidArgumentException("Empty container not allowed.");
throw InvalidArgumentException("ODBC::Binder::bindImplContainerTime():Empty container not allowed.");
setParamSetSize(val.size());
@ -848,14 +848,14 @@ private:
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TYPE_TIME,
SQL_TYPE_TIME,
Utility::sqlDataType(SQL_C_TYPE_TIME),
colSize,
decDigits,
(SQLPOINTER) &(*_timeVecVec[pos])[0],
0,
&(*_vecLengthIndicator[pos])[0])))
{
throw StatementException(_rStmt, "SQLBindParameter(Time[])");
throw StatementException(_rStmt, "ODBC::Binder::bindImplContainerTime():SQLBindParameter(Time[])");
}
}
@ -863,15 +863,15 @@ private:
void bindImplContainerDateTime(std::size_t pos, const C& val, Direction dir)
{
if (isOutBound(dir) || !isInBound(dir))
throw NotImplementedException("DateTime container parameter type can only be inbound.");
throw NotImplementedException("ODBC::Binder::bindImplContainerDateTime():DateTime container parameter type can only be inbound.");
if (PB_IMMEDIATE != _paramBinding)
throw InvalidAccessException("Containers can only be bound immediately.");
throw InvalidAccessException("ODBC::Binder::bindImplContainerDateTime():Containers can only be bound immediately.");
std::size_t length = val.size();
if (0 == length)
throw InvalidArgumentException("Empty Containers not allowed.");
throw InvalidArgumentException("ODBC::Binder::bindImplContainerDateTime():Empty Containers not allowed.");
setParamSetSize(length);
@ -897,14 +897,14 @@ private:
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TYPE_TIMESTAMP,
SQL_TYPE_TIMESTAMP,
Utility::sqlDataType(SQL_C_TYPE_TIMESTAMP),
colSize,
decDigits,
(SQLPOINTER) &(*_dateTimeVecVec[pos])[0],
0,
&(*_vecLengthIndicator[pos])[0])))
{
throw StatementException(_rStmt, "SQLBindParameter(Time[])");
throw StatementException(_rStmt, "ODBC::Binder::bindImplContainerDateTime():SQLBindParameter(Time[])");
}
}
@ -912,15 +912,15 @@ private:
void bindImplNullContainer(std::size_t pos, const C& val, Direction dir)
{
if (isOutBound(dir) || !isInBound(dir))
throw NotImplementedException("Null container parameter type can only be inbound.");
throw NotImplementedException("ODBC::Binder::bindImplNullContainer():Null container parameter type can only be inbound.");
if (PB_IMMEDIATE != _paramBinding)
throw InvalidAccessException("Container can only be bound immediately.");
throw InvalidAccessException("ODBC::Binder::bindImplNullContainer():Container can only be bound immediately.");
std::size_t length = val.size();
if (0 == length)
throw InvalidArgumentException("Empty container not allowed.");
throw InvalidArgumentException("ODBC::Binder::bindImplNullContainer():Empty container not allowed.");
setParamSetSize(length);
@ -945,7 +945,7 @@ private:
0,
&(*_vecLengthIndicator[pos])[0])))
{
throw StatementException(_rStmt, "SQLBindParameter()");
throw StatementException(_rStmt, "ODBC::Binder::bindImplNullContainer():SQLBindParameter()");
}
}
@ -973,6 +973,9 @@ private:
/// size should be set to some default value prior to calling this
/// function in order to avoid undefined size value.
std::size_t getParamSizeDirect(std::size_t pos, SQLINTEGER& size);
/// A "last ditch" attempt" to obtain parameter size directly from the driver.
void freeMemory();
/// Frees all dynamically allocated memory resources.

View File

@ -36,18 +36,40 @@ class ODBC_API ConnectionHandle
/// ODBC connection handle class
{
public:
ConnectionHandle(EnvironmentHandle* pEnvironment = 0);
ConnectionHandle(const std::string& connectString = "", SQLULEN timeout = 5);
/// Creates the ConnectionHandle.
~ConnectionHandle();
/// Creates the ConnectionHandle.
operator const SQLHDBC& () const;
/// Const conversion operator into reference to native type.
bool connect(const std::string& connectString = "", SQLULEN timeout = 5);
/// Connects the handle to the database.
bool disconnect();
/// Disconnects the handle from database.
bool isConnected() const;
/// Returns true if connected.
void setTimeout(SQLULEN timeout);
/// Sets the connection timeout in seconds.
int getTimeout() const;
/// Returns the connection timeout in seconds.
const SQLHDBC& handle() const;
/// Returns const reference to handle;
const SQLHDBC* pHandle() const;
/// Returns const pointer to handle;
operator const SQLHDBC& () const;
/// Const conversion operator into reference to native type.
operator bool();
/// Returns true if handles are not null.
/// True value is not a guarantee that the connection is valid.
private:
operator SQLHDBC& ();
/// Conversion operator into reference to native type.
@ -55,17 +77,26 @@ private:
SQLHDBC& handle();
/// Returns reference to handle;
void alloc();
/// Allocates the connection handle.
void free();
/// Frees the connection handle.
ConnectionHandle(const ConnectionHandle&);
const ConnectionHandle& operator=(const ConnectionHandle&);
const EnvironmentHandle* _pEnvironment;
SQLHDBC _hdbc;
bool _ownsEnvironment;
const EnvironmentHandle* _pEnvironment = nullptr;
SQLHDBC _hdbc = SQL_NULL_HDBC;
std::string _connectString;
friend class Poco::Data::ODBC::SessionImpl;
};
using Connection = ConnectionHandle;
//
// inlines
//
@ -75,12 +106,24 @@ inline ConnectionHandle::operator const SQLHDBC& () const
}
inline ConnectionHandle::operator bool()
{
return _pEnvironment != nullptr && _hdbc != SQL_NULL_HDBC;
}
inline const SQLHDBC& ConnectionHandle::handle() const
{
return _hdbc;
}
inline const SQLHDBC* ConnectionHandle::pHandle() const
{
return &_hdbc;
}
inline ConnectionHandle::operator SQLHDBC& ()
{
return handle();

View File

@ -138,6 +138,8 @@ public:
const Diagnostics& diagnostics()
{
if (SQL_NULL_HANDLE == _handle) return *this;
DiagnosticFields df;
SQLSMALLINT count = 1;
SQLSMALLINT messageLength = 0;

View File

@ -57,7 +57,6 @@ private:
const EnvironmentHandle& operator=(const EnvironmentHandle&);
SQLHENV _henv;
bool _isOwner;
};

View File

@ -410,6 +410,7 @@ private:
CharType** pc = AnyCast<CharType*>(&(_pPreparator->at(pos)));
poco_assert_dbg (pc);
poco_assert_dbg(*pc);
poco_assert_dbg (_pPreparator->bulkSize() == values.size());
std::size_t colWidth = columnSize(pos);
ItType it = values.begin();
@ -441,6 +442,7 @@ private:
CharType** pc = AnyCast<CharType*>(&(_pPreparator->at(pos)));
poco_assert_dbg (pc);
poco_assert_dbg(*pc);
poco_assert_dbg (_pPreparator->bulkSize() == values.size());
std::size_t colWidth = _pPreparator->maxDataSize(pos);
ItType it = values.begin();
@ -480,7 +482,7 @@ private:
&_lengths[pos]); //length indicator
if (Utility::isError(rc))
throw StatementException(_rStmt, "SQLGetData()");
throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl():SQLGetData()");
if (isNullLengthIndicator(_lengths[pos]))
return false;

View File

@ -94,6 +94,9 @@ protected:
std::string nativeSQL();
/// Returns the SQL string as modified by the driver.
void printErrors(std::ostream& os) const;
/// Print errors, if any.
private:
typedef Poco::Data::AbstractBindingVec Bindings;
typedef Poco::SharedPtr<Binder> BinderPtr;
@ -143,17 +146,26 @@ private:
void fillColumns();
void checkError(SQLRETURN rc, const std::string& msg="");
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;
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;
};

View File

@ -583,7 +583,7 @@ private:
(SQLINTEGER) dataSize,
&_lengths[pos])))
{
throw StatementException(_rStmt, "SQLBindCol()");
throw StatementException(_rStmt, "ODBC::Preparator::prepareFixedSize():SQLBindCol()");
}
}
@ -612,7 +612,7 @@ private:
(SQLINTEGER) dataSize,
&_lenLengths[pos][0])))
{
throw StatementException(_rStmt, "SQLBindCol()");
throw StatementException(_rStmt, "ODBC::Preparator::prepareFixedSize():SQLBindCol()");
}
}
@ -637,7 +637,7 @@ private:
(SQLINTEGER) size*sizeof(T),
&_lengths[pos])))
{
throw StatementException(_rStmt, "SQLBindCol()");
throw StatementException(_rStmt, "ODBC::Preparator::prepareVariableLen():SQLBindCol()");
}
}
@ -664,7 +664,7 @@ private:
(SQLINTEGER) size,
&_lenLengths[pos][0])))
{
throw StatementException(_rStmt, "SQLBindCol()");
throw StatementException(_rStmt, "ODBC::Preparator::prepareCharArray():SQLBindCol()");
}
}

View File

@ -52,6 +52,13 @@ public:
ODBC_TXN_CAPABILITY_TRUE = 1
};
enum CursorUse
{
ODBC_CURSOR_USE_ALWAYS = Poco::Data::SessionImpl::CURSOR_USE_ALWAYS,
ODBC_CURSOR_USE_IF_NEEDED = Poco::Data::SessionImpl::CURSOR_USE_IF_NEEDED,
ODBC_CURSOR_USE_NEVER = Poco::Data::SessionImpl::CURSOR_USE_NEVER
};
SessionImpl(const std::string& connect,
std::size_t loginTimeout,
std::size_t maxFieldSize = ODBC_MAX_FIELD_SIZE,
@ -159,6 +166,15 @@ public:
/// Returns the timeout (in seconds) for queries,
/// or -1 if no timeout has been set.
void setCursorUse(const std::string&, const Poco::Any& value);
/// Sets the use of cursors:
/// - SQL_CUR_USE_ODBC - always
/// - SQL_CUR_USE_IF_NEEDED - if needed
/// - SQL_CUR_USE_DRIVER - never
Poco::Any getCursorUse(const std::string&) const;
/// Returns the use of cursors.
int queryTimeout() const;
/// Returns the timeout (in seconds) for queries,
/// or -1 if no timeout has been set.
@ -195,17 +211,17 @@ private:
/// Sets the transaction isolation level.
/// Called internally from getTransactionIsolation()
std::string _connector;
mutable ConnectionHandle _db;
Poco::Any _maxFieldSize;
bool _autoBind;
bool _autoExtract;
TypeInfo _dataTypes;
mutable char _canTransact;
bool _inTransaction;
int _queryTimeout;
std::string _dbEncoding;
Poco::FastMutex _mutex;
std::string _connector;
mutable ConnectionHandle _db;
Poco::Any _maxFieldSize;
bool _autoBind;
bool _autoExtract;
TypeInfo _dataTypes;
mutable TransactionCapability _canTransact;
std::atomic<bool> _inTransaction;
int _queryTimeout;
std::string _dbEncoding;
Poco::FastMutex _mutex;
};

View File

@ -82,7 +82,7 @@ public:
int sqlDataType(int cDataType) const;
/// Returns SQL data type corresponding to supplied C data type.
void fillTypeInfo(SQLHDBC pHDBC);
void fillTypeInfo(const SQLHDBC* pHDBC);
/// Fills the data type info structure for the database.
DynamicAny getInfo(SQLSMALLINT type, const std::string& param) const;
@ -107,7 +107,7 @@ private:
DataTypeMap _cDataTypes;
DataTypeMap _sqlDataTypes;
TypeInfoVec _typeInfo;
SQLHDBC* _pHDBC;
const SQLHDBC* _pHDBC;
};

View File

@ -85,7 +85,7 @@ void Binder::freeMemory()
UUIDMap::iterator itUUID = _uuids.begin();
UUIDMap::iterator itUUIDEnd = _uuids.end();
for(; itUUID != itUUIDEnd; ++itUUID) std::free(itUUID->first);
for(; itUUID != itUUIDEnd; ++itUUID) delete [] itUUID->first;
BoolPtrVec::iterator itBool = _boolPtrs.begin();
BoolPtrVec::iterator endBool = _boolPtrs.end();
@ -152,7 +152,7 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir)
}
}
else
throw InvalidArgumentException("Parameter must be [in] OR [out] bound.");
throw InvalidArgumentException("ODBC::Binder::bind(string):Parameter must be [in] OR [out] bound.");
SQLLEN* pLenIn = new SQLLEN(SQL_NTS);
@ -165,14 +165,14 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir)
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_CHAR,
Connector::stringBoundToLongVarChar() ? SQL_LONGVARCHAR : SQL_VARCHAR,
Utility::sqlDataType(SQL_C_CHAR),
(SQLUINTEGER) colSize,
0,
pVal,
(SQLINTEGER) size,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(std::string)");
throw StatementException(_rStmt, "ODBC::Binder::bind(string):SQLBindParameter(std::string)");
}
}
@ -201,7 +201,7 @@ void Binder::bind(std::size_t pos, const UTF16String& val, Direction dir)
_inParams.insert(ParamMap::value_type(pVal, size));
}
else
throw InvalidArgumentException("Parameter must be [in] OR [out] bound.");
throw InvalidArgumentException("ODBC::Binder::bind():Parameter must be [in] OR [out] bound.");
SQLLEN* pLenIn = new SQLLEN(SQL_NTS);
@ -216,14 +216,14 @@ void Binder::bind(std::size_t pos, const UTF16String& val, Direction dir)
(SQLUSMALLINT)pos + 1,
toODBCDirection(dir),
SQL_C_WCHAR,
SQL_WLONGVARCHAR,
Utility::sqlDataType(SQL_C_WCHAR),
(SQLUINTEGER)colSize,
0,
pVal,
(SQLINTEGER)size,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(std::string)");
throw StatementException(_rStmt, "ODBC::Binder::bind(UTF16String):SQLBindParameter(std::string)");
}
}
@ -249,14 +249,14 @@ void Binder::bind(std::size_t pos, const Date& val, Direction dir)
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TYPE_DATE,
SQL_TYPE_DATE,
Utility::sqlDataType(SQL_C_TYPE_DATE),
colSize,
decDigits,
(SQLPOINTER) pDS,
0,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(Date)");
throw StatementException(_rStmt, "ODBC::Binder::bind(Date):SQLBindParameter(Date)");
}
}
@ -282,14 +282,14 @@ void Binder::bind(std::size_t pos, const Time& val, Direction dir)
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TYPE_TIME,
SQL_TYPE_TIME,
Utility::sqlDataType(SQL_C_TYPE_TIME),
colSize,
decDigits,
(SQLPOINTER) pTS,
0,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(Time)");
throw StatementException(_rStmt, "ODBC::Binder::bind(Time):SQLBindParameter(Time)");
}
}
@ -315,14 +315,14 @@ void Binder::bind(std::size_t pos, const Poco::DateTime& val, Direction dir)
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TYPE_TIMESTAMP,
SQL_TYPE_TIMESTAMP,
Utility::sqlDataType(SQL_C_TYPE_TIMESTAMP),
colSize,
decDigits,
(SQLPOINTER) pTS,
0,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(DateTime)");
throw StatementException(_rStmt, "ODBC::Binder::bind(DateTime):SQLBindParameter(DateTime)");
}
}
@ -386,7 +386,7 @@ void Binder::bind(std::size_t pos, const NullData& val, Direction dir)
0,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter()");
throw StatementException(_rStmt, "ODBC::Binder::bind(NullData):SQLBindParameter()");
}
}
@ -510,25 +510,28 @@ void Binder::getColSizeAndPrecision(std::size_t pos,
SQLSMALLINT& decDigits,
std::size_t actualSize)
{
colSize = 0;
decDigits = 0;
// Not all drivers are equally willing to cooperate in this matter.
// Hence the funky flow control.
DynamicAny tmp;
bool found(false);
if (_pTypeInfo)
{
found = _pTypeInfo->tryGetInfo(cDataType, "COLUMN_SIZE", tmp);
if (found) colSize = tmp;
if (found && actualSize > colSize)
DynamicAny tmp;
bool foundSize(false);
bool foundPrec(false);
foundSize = _pTypeInfo->tryGetInfo(cDataType, "COLUMN_SIZE", tmp);
if (foundSize) colSize = tmp;
if (actualSize > colSize)
{
throw LengthExceededException(Poco::format("Error binding column %z size=%z, max size=%ld)",
pos, actualSize, static_cast<long>(colSize)));
}
found = _pTypeInfo->tryGetInfo(cDataType, "MINIMUM_SCALE", tmp);
if (found)
{
decDigits = tmp;
foundPrec = _pTypeInfo->tryGetInfo(cDataType, "MAXIMUM_SCALE", tmp);
if (foundPrec) decDigits = tmp;
if (foundSize && foundPrec)
return;
}
}
try
@ -560,14 +563,30 @@ void Binder::getColSizeAndPrecision(std::size_t pos,
pos, actualSize, static_cast<long>(colSize)));
}
// no success, set to zero and hope for the best
// (most drivers do not require these most of the times anyway)
colSize = 0;
decDigits = 0;
return;
}
std::size_t Binder::getParamSizeDirect(std::size_t pos, SQLINTEGER& size)
{
//On Linux, PostgreSQL driver segfaults on SQLGetDescField, so this is disabled for now
#ifdef POCO_OS_FAMILY_WINDOWS
size = DEFAULT_PARAM_SIZE;
SQLHDESC hIPD = 0;
if (!Utility::isError(SQLGetStmtAttr(_rStmt, SQL_ATTR_IMP_PARAM_DESC, &hIPD, SQL_IS_POINTER, 0)))
{
SQLULEN sz = 0;
if (!Utility::isError(SQLGetDescField(hIPD, (SQLSMALLINT)pos + 1, SQL_DESC_LENGTH, &sz, SQL_IS_UINTEGER, 0)) &&
sz > 0)
{
size = (SQLINTEGER)sz;
}
}
#endif
return static_cast<std::size_t>(size);
}
void Binder::getColumnOrParameterSize(std::size_t pos, SQLINTEGER& size)
{
std::size_t colSize = 0;
@ -585,23 +604,10 @@ void Binder::getColumnOrParameterSize(std::size_t pos, SQLINTEGER& size)
Parameter p(_rStmt, pos);
paramSize = p.columnSize();
}
catch (StatementException&)
{
size = DEFAULT_PARAM_SIZE;
//On Linux, PostgreSQL driver segfaults on SQLGetDescField, so this is disabled for now
#ifdef POCO_OS_FAMILY_WINDOWS
SQLHDESC hIPD = 0;
if (!Utility::isError(SQLGetStmtAttr(_rStmt, SQL_ATTR_IMP_PARAM_DESC, &hIPD, SQL_IS_POINTER, 0)))
{
SQLUINTEGER sz = 0;
if (!Utility::isError(SQLGetDescField(hIPD, (SQLSMALLINT) pos + 1, SQL_DESC_LENGTH, &sz, SQL_IS_UINTEGER, 0)) &&
sz > 0)
{
size = sz;
}
}
#endif
}
catch (StatementException&) {}
if (colSize == 0 && paramSize == 0)
paramSize = getParamSizeDirect(pos, size);
if (colSize > 0 && paramSize > 0)
size = colSize < paramSize ? static_cast<SQLINTEGER>(colSize) : static_cast<SQLINTEGER>(paramSize);
@ -620,7 +626,7 @@ void Binder::setParamSetSize(std::size_t length)
{
if (Utility::isError(Poco::Data::ODBC::SQLSetStmtAttr(_rStmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, SQL_IS_UINTEGER)) ||
Utility::isError(Poco::Data::ODBC::SQLSetStmtAttr(_rStmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) length, SQL_IS_UINTEGER)))
throw StatementException(_rStmt, "SQLSetStmtAttr()");
throw StatementException(_rStmt, "ODBC::Binder::setParamSetSize():SQLSetStmtAttr()");
_paramSetSize = static_cast<SQLINTEGER>(length);
}

View File

@ -24,17 +24,12 @@ namespace Data {
namespace ODBC {
ConnectionHandle::ConnectionHandle(EnvironmentHandle* pEnvironment):
_pEnvironment(pEnvironment ? pEnvironment : new EnvironmentHandle),
ConnectionHandle::ConnectionHandle(const std::string& connectString, SQLULEN timeout): _pEnvironment(nullptr),
_hdbc(SQL_NULL_HDBC),
_ownsEnvironment(pEnvironment ? false : true)
_connectString(connectString)
{
if (Utility::isError(SQLAllocHandle(SQL_HANDLE_DBC,
_pEnvironment->handle(),
&_hdbc)))
{
throw ODBCException("Could not allocate connection handle.");
}
alloc();
setTimeout(timeout);
}
@ -42,13 +37,7 @@ ConnectionHandle::~ConnectionHandle()
{
try
{
SQLDisconnect(_hdbc);
SQLRETURN rc = SQLFreeHandle(SQL_HANDLE_DBC, _hdbc);
if (_ownsEnvironment) delete _pEnvironment;
#if defined(_DEBUG)
if (Utility::isError(rc))
Debugger::enter(Poco::Error::getMessage(Poco::Error::last()), __FILE__, __LINE__);
#endif
disconnect();
}
catch (...)
{
@ -57,4 +46,116 @@ ConnectionHandle::~ConnectionHandle()
}
void ConnectionHandle::alloc()
{
if (_pEnvironment || _hdbc) free();
_pEnvironment = new EnvironmentHandle;
if (Utility::isError(SQLAllocHandle(SQL_HANDLE_DBC, _pEnvironment->handle(), &_hdbc)))
{
delete _pEnvironment;
_pEnvironment = nullptr;
_hdbc = SQL_NULL_HDBC;
throw ODBCException("ODBC: Could not allocate connection handle.");
}
}
void ConnectionHandle::free()
{
if (_hdbc != SQL_NULL_HDBC)
{
SQLFreeHandle(SQL_HANDLE_DBC, _hdbc);
_hdbc = SQL_NULL_HDBC;
}
if (_pEnvironment)
{
delete _pEnvironment;
_pEnvironment = 0;
}
}
bool ConnectionHandle::connect(const std::string& connectString, SQLULEN timeout)
{
if (isConnected())
throw Poco::InvalidAccessException("ODBC: connection already established.");
if (connectString.empty())
throw Poco::InvalidArgumentException("ODBC: connection string is empty.");
if (connectString != _connectString)
_connectString = connectString;
SQLCHAR connectOutput[512] = {0};
SQLSMALLINT result;
if (!_pEnvironment) alloc();
if (Utility::isError(Poco::Data::ODBC::SQLDriverConnect(_hdbc
, NULL
,(SQLCHAR*) _connectString.c_str()
,(SQLSMALLINT) SQL_NTS
, connectOutput
, sizeof(connectOutput)
, &result
, SQL_DRIVER_NOPROMPT)))
{
disconnect();
ConnectionError err(_hdbc);
throw ConnectionFailedException(err.toString());
}
return _hdbc != SQL_NULL_HDBC;;
}
bool ConnectionHandle::disconnect()
{
SQLRETURN rc = 0;
if (isConnected())
rc = SQLDisconnect(_hdbc);
free();
return !Utility::isError(rc);
}
void ConnectionHandle::setTimeout(SQLULEN timeout)
{
if (Utility::isError(SQLSetConnectAttr(_hdbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) timeout, 0)))
{
ConnectionError e(_hdbc);
throw ConnectionFailedException(e.toString());
}
}
int ConnectionHandle::getTimeout() const
{
SQLULEN timeout = 0;
if (Utility::isError(SQLGetConnectAttr(_hdbc, SQL_ATTR_LOGIN_TIMEOUT, &timeout, 0, 0)))
{
ConnectionError e(_hdbc);
throw ConnectionFailedException(e.toString());
}
return static_cast<int>(timeout);
}
bool ConnectionHandle::isConnected() const
{
if (!*this) return false;
SQLULEN value = 0;
if (Utility::isError(Poco::Data::ODBC::SQLGetConnectAttr(_hdbc,
SQL_ATTR_CONNECTION_DEAD,
&value,
0,
0))) return false;
return (SQL_CD_FALSE == value);
}
} } } // namespace Poco::Data::ODBC

View File

@ -26,15 +26,15 @@ namespace ODBC {
EnvironmentHandle::EnvironmentHandle(): _henv(SQL_NULL_HENV)
{
if (Utility::isError(SQLAllocHandle(SQL_HANDLE_ENV,
SQL_NULL_HANDLE,
&_henv)) ||
Utility::isError(SQLSetEnvAttr(_henv,
SQL_ATTR_ODBC_VERSION,
(SQLPOINTER) SQL_OV_ODBC3,
0)))
SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &_henv);
if (Utility::isError(rc))
throw ODBCException("EnvironmentHandle: Could not initialize ODBC environment.");
rc = SQLSetEnvAttr(_henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);
if (Utility::isError(rc))
{
throw ODBCException("Could not initialize environment.");
EnvironmentError err(_henv);
throw ODBCException(err.toString());
}
}
@ -43,10 +43,15 @@ EnvironmentHandle::~EnvironmentHandle()
{
try
{
SQLRETURN rc = SQLFreeHandle(SQL_HANDLE_ENV, _henv);
#if defined(_DEBUG)
SQLRETURN rc = SQLFreeHandle(SQL_HANDLE_ENV, _henv);
if (Utility::isError(rc))
Debugger::enter(Poco::Error::getMessage(Poco::Error::last()), __FILE__, __LINE__);
{
EnvironmentError err(_henv);
Debugger::enter(err.toString(), __FILE__, __LINE__);
}
#else
SQLFreeHandle(SQL_HANDLE_ENV, _henv);
#endif
}
catch (...)

View File

@ -19,6 +19,7 @@
#include "Poco/Data/ODBC/ODBCException.h"
#include "Poco/Data/LOB.h"
#include "Poco/Buffer.h"
#include "Poco/Exception.h"
#include <typeinfo>
@ -300,10 +301,10 @@ bool Extractor::extractManualImpl<std::string>(std::size_t pos, std::string& val
&len); //length indicator
if (SQL_NO_DATA != rc && Utility::isError(rc))
throw StatementException(_rStmt, "SQLGetData()");
throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl(string):SQLGetData()");
if (SQL_NO_TOTAL == len)//unknown length, throw
throw UnknownDataLengthException("Could not determine returned data length.");
throw UnknownDataLengthException("ODBC::Extractor::extractManualImpl(string):Could not determine returned data length.");
if (isNullLengthIndicator(len))
{
@ -355,10 +356,10 @@ bool Extractor::extractManualImpl<UTF16String>(std::size_t pos, UTF16String& val
&len); //length indicator
if (SQL_NO_DATA != rc && Utility::isError(rc))
throw StatementException(_rStmt, "SQLGetData()");
throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl(UTF16String):SQLGetData()");
if (SQL_NO_TOTAL == len)//unknown length, throw
throw UnknownDataLengthException("Could not determine returned data length.");
throw UnknownDataLengthException("ODBC::Extractor::extractManualImpl(UTF16String):Could not determine returned data length.");
if (isNullLengthIndicator(len))
{
@ -414,10 +415,10 @@ bool Extractor::extractManualImpl<Poco::Data::CLOB>(std::size_t pos,
_lengths[pos] += len;
if (SQL_NO_DATA != rc && Utility::isError(rc))
throw StatementException(_rStmt, "SQLGetData()");
throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl(CLOB):SQLGetData()");
if (SQL_NO_TOTAL == len)//unknown length, throw
throw UnknownDataLengthException("Could not determine returned data length.");
throw UnknownDataLengthException("ODBC::Extractor::extractManualImpl(CLOB):Could not determine returned data length.");
if (isNullLengthIndicator(len))
return false;
@ -454,7 +455,7 @@ bool Extractor::extractManualImpl<Poco::Data::Date>(std::size_t pos,
&_lengths[pos]); //length indicator
if (Utility::isError(rc))
throw StatementException(_rStmt, "SQLGetData()");
throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl(Date):SQLGetData()");
if (isNullLengthIndicator(_lengths[pos]))
return false;
@ -481,7 +482,7 @@ bool Extractor::extractManualImpl<Poco::Data::Time>(std::size_t pos,
&_lengths[pos]); //length indicator
if (Utility::isError(rc))
throw StatementException(_rStmt, "SQLGetData()");
throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl(Time):SQLGetData()");
if (isNullLengthIndicator(_lengths[pos]))
return false;
@ -508,7 +509,7 @@ bool Extractor::extractManualImpl<Poco::DateTime>(std::size_t pos,
&_lengths[pos]); //length indicator
if (Utility::isError(rc))
throw StatementException(_rStmt, "SQLGetData()");
throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl(DateTime):SQLGetData()");
if (isNullLengthIndicator(_lengths[pos]))
return false;

View File

@ -53,7 +53,7 @@ void ODBCMetaColumn::getDescription()
&_columnDesc.decimalDigits,
&_columnDesc.isNullable)))
{
throw StatementException(_rStmt);
throw StatementException(_rStmt, "ODBCMetaColumn::getDescription()");
}
}
@ -69,7 +69,7 @@ bool ODBCMetaColumn::isUnsigned() const
0,
&val)))
{
throw StatementException(_rStmt);
throw StatementException(_rStmt, "ODBCMetaColumn::isUnsigned()");
}
return (val == SQL_TRUE);
}
@ -87,7 +87,7 @@ void ODBCMetaColumn::init()
0,
&_dataLength)))
{
throw StatementException(_rStmt);
throw StatementException(_rStmt, "ODBCMetaColumn::init()");
}
setName(std::string((char*) _columnDesc.name));

View File

@ -47,13 +47,19 @@ ODBCStatementImpl::ODBCStatementImpl(SessionImpl& rSession):
_canCompile(true)
{
int queryTimeout = rSession.queryTimeout();
int rc = 0;
if (queryTimeout >= 0)
{
SQLULEN uqt = static_cast<SQLULEN>(queryTimeout);
SQLSetStmtAttr(_stmt,
rc = SQLSetStmtAttr(_stmt,
SQL_ATTR_QUERY_TIMEOUT,
(SQLPOINTER) uqt,
0);
if (Utility::isError(rc))
{
throw ODBC::ConnectionException(_stmt,
Poco::format("SQLSetStmtAttr(SQL_ATTR_QUERY_TIMEOUT, %d)", queryTimeout));
}
}
}
@ -96,7 +102,15 @@ void ODBCStatementImpl::compileImpl()
{
}
std::size_t maxFieldSize = AnyCast<std::size_t>(session().getProperty("maxFieldSize"));
std::size_t maxFieldSize;
try
{
maxFieldSize = AnyCast<std::size_t>(session().getProperty("maxFieldSize"));
}
catch (Poco::BadCastException&)
{
maxFieldSize = AnyCast<int>(session().getProperty("maxFieldSize"));
}
_pBinder = new Binder(_stmt, maxFieldSize, bind, pDT, TextEncoding::find("UTF-8"),
TextEncoding::find(Poco::RefAnyCast<std::string>(session().getProperty("dbEncoding"))));
@ -139,7 +153,15 @@ void ODBCStatementImpl::addPreparator()
Preparator::DataExtraction ext = session().getFeature("autoExtract") ?
Preparator::DE_BOUND : Preparator::DE_MANUAL;
std::size_t maxFieldSize = AnyCast<std::size_t>(session().getProperty("maxFieldSize"));
std::size_t maxFieldSize;
try
{
maxFieldSize = AnyCast<std::size_t>(session().getProperty("maxFieldSize"));
}
catch (Poco::BadCastException&)
{
maxFieldSize = AnyCast<int>(session().getProperty("maxFieldSize"));
}
_preparations.push_back(new Preparator(_stmt, statement, maxFieldSize, ext));
}
@ -216,6 +238,38 @@ void ODBCStatementImpl::doBind()
}
void ODBCStatementImpl::addErrors()
{
SQLSMALLINT i = 0;
SQLSMALLINT len;
SQLRETURN ret;
do
{
_errorInfo.push_back({});
ret = SQLGetDiagRec(SQL_HANDLE_STMT, _stmt, ++i,
_errorInfo.back().state, &_errorInfo.back().native, _errorInfo.back().text,
sizeof(_errorInfo.back().text), &len);
if (!SQL_SUCCEEDED(ret) && _errorInfo.size())
_errorInfo.pop_back();
} while( ret == SQL_SUCCESS );
}
void ODBCStatementImpl::printErrors(std::ostream& os) const
{
if (_errorInfo.size())
{
os << "Errors\n==================";
for (const auto& e : _errorInfo)
{
os << "\nstate: " << e.state << "\nnative: "
<< e.native << "\ntext: " << e.text << '\n';
}
os << "==================\n";
}
}
void ODBCStatementImpl::bindImpl()
{
doBind();
@ -223,15 +277,16 @@ void ODBCStatementImpl::bindImpl()
SQLRETURN rc = SQLExecute(_stmt);
if (SQL_NEED_DATA == rc) putData();
else checkError(rc, "SQLExecute()");
else checkError(rc, "ODBCStatementImpl::bindImpl():SQLExecute()");
_pBinder->synchronize();
}
void ODBCStatementImpl::execDirectImpl(const std::string& query)
{
SQLCHAR * statementText = (SQLCHAR*) query.c_str();
SQLINTEGER textLength = query.size();
SQLINTEGER textLength = static_cast<SQLINTEGER>(query.size());
SQLRETURN rc = SQLExecDirect(_stmt,statementText,textLength);
checkError(rc, "SQLExecute()");
@ -251,13 +306,13 @@ void ODBCStatementImpl::putData()
dataSize = (SQLINTEGER) _pBinder->parameterSize(pParam);
if (Utility::isError(SQLPutData(_stmt, pParam, dataSize)))
throw StatementException(_stmt, "SQLPutData()");
throw StatementException(_stmt, "ODBCStatementImpl::putData():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()");
throw StatementException(_stmt, "ODBCStatementImpl::putData():SQLPutData()");
}
}
@ -267,30 +322,12 @@ void ODBCStatementImpl::putData()
void ODBCStatementImpl::clear()
{
SQLRETURN rc = SQLCloseCursor(_stmt);
_stepCalled = false;
_affectedRowCount = 0;
_errorInfo.clear();
SQLRETURN rc = SQLFreeStmt(_stmt, SQL_CLOSE);
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()");
}
throw StatementException(_stmt, "ODBCStatementImpl::putData():SQLFreeStmt(SQL_CLOSE)");
}
@ -335,6 +372,25 @@ void ODBCStatementImpl::makeStep()
{
_extractors[currentDataSet()]->reset();
_nextResponse = SQLFetch(_stmt);
// workaround for SQL Server drivers 17, 18, ...
// stored procedure calls produce additional data,
// causing SQLFetch error 24000 (invalid cursor state);
// when it happens, SQLMoreResults() is called to
// force SQL_NO_DATA response
if (Utility::isError(_nextResponse))
{
StatementError se(_stmt);
const StatementDiagnostics& sd = se.diagnostics();
for (int i = 0; i < sd.count(); ++i)
{
if (sd.sqlState(i) == INVALID_CURSOR_STATE)
{
_nextResponse = SQLMoreResults(_stmt);
break;
}
}
}
checkError(_nextResponse);
_stepCalled = true;
}
@ -363,7 +419,7 @@ std::size_t ODBCStatementImpl::next()
else
{
throw StatementException(_stmt,
std::string("Next row not available."));
"ODBCStatementImpl::next():Next row not available.");
}
return count;
@ -416,6 +472,7 @@ void ODBCStatementImpl::checkError(SQLRETURN rc, const std::string& msg)
throw StatementException(_stmt, str);
}
else if (SQL_SUCCESS_WITH_INFO == rc) addErrors();
}

View File

@ -45,7 +45,7 @@ void Parameter::init()
&_decimalDigits,
&_isNullable)))
{
throw StatementException(_rStmt);
throw StatementException(_rStmt, "ODBC::Parameter::init():SQLDescribeParam()");
}
}

View File

@ -35,7 +35,7 @@ Preparator::Preparator(const StatementHandle& rStmt,
{
SQLCHAR* pStr = (SQLCHAR*) statement.c_str();
if (Utility::isError(Poco::Data::ODBC::SQLPrepare(_rStmt, pStr, (SQLINTEGER) statement.length())))
throw StatementException(_rStmt);
throw StatementException(_rStmt, "ODBC::Preparator():SQLPrepare()");
}
@ -158,7 +158,8 @@ std::size_t Preparator::maxDataSize(std::size_t pos) const
// accomodate for terminating zero (non-bulk only!)
MetaColumn::ColumnDataType type = mc.type();
if (sz && !isBulk() && ((ODBCMetaColumn::FDT_WSTRING == type) || (ODBCMetaColumn::FDT_STRING == type))) ++sz;
if (sz && !isBulk() && ((ODBCMetaColumn::FDT_WSTRING == type) || (ODBCMetaColumn::FDT_STRING == type)))
++sz;
}
catch (StatementException&) { }
@ -201,7 +202,7 @@ void Preparator::prepareBoolArray(std::size_t pos, SQLSMALLINT valueType, std::s
(SQLINTEGER) sizeof(bool),
&_lenLengths[pos][0])))
{
throw StatementException(_rStmt, "SQLBindCol()");
throw StatementException(_rStmt, "ODBC::Preparator::prepareBoolArray():SQLBindCol()");
}
}

View File

@ -43,8 +43,10 @@ SessionImpl::SessionImpl(const std::string& connect,
_dbEncoding("UTF-8")
{
setFeature("bulk", true);
// this option is obsolete; here only to support older drivers, should be changed to ODBC_CURSOR_USE_NEVER
// https://github.com/MicrosoftDocs/sql-docs/blob/live/docs/odbc/reference/appendixes/using-the-odbc-cursor-library.md
setCursorUse("", ODBC_CURSOR_USE_IF_NEEDED);
open();
setProperty("handle", _db.handle());
}
@ -63,8 +65,10 @@ SessionImpl::SessionImpl(const std::string& connect,
_dbEncoding("UTF-8")
{
setFeature("bulk", true);
// this option is obsolete; here only to support older drivers, should be changed to ODBC_CURSOR_USE_NEVER
// https://github.com/MicrosoftDocs/sql-docs/blob/live/docs/odbc/reference/appendixes/using-the-odbc-cursor-library.md
setCursorUse("", ODBC_CURSOR_USE_IF_NEEDED);
open();
setProperty("handle", _db.handle());
}
@ -104,69 +108,51 @@ void SessionImpl::open(const std::string& connect)
setConnectionString(connect);
}
poco_assert_dbg (!connectionString().empty());
if (connectionString().empty())
throw InvalidArgumentException("SessionImpl::open(): Connection string empty");
SQLULEN tout = static_cast<SQLULEN>(getLoginTimeout());
if (Utility::isError(SQLSetConnectAttr(_db, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) tout, 0)))
if (_db.connect(connectionString(), tout))
{
if (Utility::isError(SQLGetConnectAttr(_db, SQL_ATTR_LOGIN_TIMEOUT, &tout, 0, 0)) ||
getLoginTimeout() != tout)
{
ConnectionError e(_db);
throw ConnectionFailedException(e.toString());
}
setProperty("handle", _db.handle());
_dataTypes.fillTypeInfo(_db.pHandle());
addProperty("dataTypeInfo",
&SessionImpl::setDataTypeInfo,
&SessionImpl::dataTypeInfo);
addFeature("autoCommit",
&SessionImpl::autoCommit,
&SessionImpl::isAutoCommit);
addFeature("autoBind",
&SessionImpl::autoBind,
&SessionImpl::isAutoBind);
addFeature("autoExtract",
&SessionImpl::autoExtract,
&SessionImpl::isAutoExtract);
addProperty("maxFieldSize",
&SessionImpl::setMaxFieldSize,
&SessionImpl::getMaxFieldSize);
addProperty("queryTimeout",
&SessionImpl::setQueryTimeout,
&SessionImpl::getQueryTimeout);
addProperty("dbEncoding",
&SessionImpl::setDBEncoding,
&SessionImpl::getDBEncoding);
Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_QUIET_MODE, 0, 0);
if (!canTransact()) autoCommit("", true);
}
SQLCHAR connectOutput[512] = {0};
SQLSMALLINT result;
if (Utility::isError(Poco::Data::ODBC::SQLDriverConnect(_db
, NULL
,(SQLCHAR*) connectionString().c_str()
,(SQLSMALLINT) SQL_NTS
, connectOutput
, sizeof(connectOutput)
, &result
, SQL_DRIVER_NOPROMPT)))
{
ConnectionError err(_db);
std::string errStr = err.toString();
close();
throw ConnectionFailedException(errStr);
}
_dataTypes.fillTypeInfo(_db);
addProperty("dataTypeInfo",
&SessionImpl::setDataTypeInfo,
&SessionImpl::dataTypeInfo);
addFeature("autoCommit",
&SessionImpl::autoCommit,
&SessionImpl::isAutoCommit);
addFeature("autoBind",
&SessionImpl::autoBind,
&SessionImpl::isAutoBind);
addFeature("autoExtract",
&SessionImpl::autoExtract,
&SessionImpl::isAutoExtract);
addProperty("maxFieldSize",
&SessionImpl::setMaxFieldSize,
&SessionImpl::getMaxFieldSize);
addProperty("queryTimeout",
&SessionImpl::setQueryTimeout,
&SessionImpl::getQueryTimeout);
addProperty("dbEncoding",
&SessionImpl::setDBEncoding,
&SessionImpl::getDBEncoding);
Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_QUIET_MODE, 0, 0);
if (!canTransact()) autoCommit("", true);
else
throw ConnectionException(SQL_NULL_HDBC,
Poco::format("Connection to '%s' failed.", connectionString()));
}
@ -180,15 +166,62 @@ void SessionImpl::setDBEncoding(const std::string&, const Poco::Any& value)
bool SessionImpl::isConnected() const
{
SQLULEN value = 0;
return _db.isConnected();
}
if (Utility::isError(Poco::Data::ODBC::SQLGetConnectAttr(_db,
SQL_ATTR_CONNECTION_DEAD,
&value,
0,
0))) return false;
return (SQL_CD_FALSE == value);
inline void SessionImpl::setCursorUse(const std::string&, const Poco::Any& value)
{
#if POCO_OS == POCO_OS_WINDOWS_NT
#pragma warning (disable : 4995) // ignore marked as deprecated
#endif
int cursorUse = static_cast<int>(Poco::AnyCast<CursorUse>(value));
int rc = 0;
switch (cursorUse)
{
case ODBC_CURSOR_USE_ALWAYS:
rc = Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_ODBC_CURSORS, (SQLPOINTER)SQL_CUR_USE_ODBC, SQL_IS_INTEGER);
break;
case ODBC_CURSOR_USE_IF_NEEDED:
rc = Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_ODBC_CURSORS, (SQLPOINTER)SQL_CUR_USE_IF_NEEDED, SQL_IS_INTEGER);
break;
case ODBC_CURSOR_USE_NEVER:
rc = Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_ODBC_CURSORS, (SQLPOINTER)SQL_CUR_USE_DRIVER, SQL_IS_INTEGER);
break;
default:
throw Poco::InvalidArgumentException(Poco::format("SessionImpl::setCursorUse(%d)", cursorUse));
}
#if POCO_OS == POCO_OS_WINDOWS_NT
#pragma warning (default : 4995)
#endif
if (Utility::isError(rc))
{
throw Poco::Data::ODBC::HandleException<SQLHDBC, SQL_HANDLE_DBC>(_db, Poco::format("SessionImpl::setCursorUse(%d)", cursorUse));
}
}
inline Poco::Any SessionImpl::getCursorUse(const std::string&) const
{
#if POCO_OS == POCO_OS_WINDOWS_NT
#pragma warning (disable : 4995) // ignore marked as deprecated
#endif
SQLUINTEGER curUse = 0;
Poco::Data::ODBC::SQLGetConnectAttr(_db, SQL_ATTR_ODBC_CURSORS, &curUse, SQL_IS_UINTEGER, 0);
switch (curUse)
{
case SQL_CUR_USE_ODBC:
return ODBC_CURSOR_USE_ALWAYS;
case SQL_CUR_USE_IF_NEEDED:
return ODBC_CURSOR_USE_IF_NEEDED;
case SQL_CUR_USE_DRIVER:
return ODBC_CURSOR_USE_NEVER;
default: break;
}
throw Poco::InvalidArgumentException(Poco::format("SessionImpl::getCursorUse(%u)", curUse));
#if POCO_OS == POCO_OS_WINDOWS_NT
#pragma warning (default : 4995)
#endif
}
@ -333,12 +366,14 @@ void SessionImpl::autoCommit(const std::string&, bool val)
SQL_ATTR_AUTOCOMMIT,
val ? (SQLPOINTER) SQL_AUTOCOMMIT_ON :
(SQLPOINTER) SQL_AUTOCOMMIT_OFF,
SQL_IS_UINTEGER), "Failed to set automatic commit.");
SQL_IS_UINTEGER), "Failed to set autocommit.");
}
bool SessionImpl::isAutoCommit(const std::string&) const
{
if (!_db) return false;
SQLULEN value = 0;
checkError(Poco::Data::ODBC::SQLGetConnectAttr(_db,
@ -353,7 +388,7 @@ bool SessionImpl::isAutoCommit(const std::string&) const
bool SessionImpl::isTransaction() const
{
if (!canTransact()) return false;
if (!_db || !canTransact()) return false;
SQLULEN value = 0;
checkError(Poco::Data::ODBC::SQLGetConnectAttr(_db,
@ -419,7 +454,8 @@ void SessionImpl::close()
{
}
SQLDisconnect(_db);
_db.disconnect();
setProperty("handle", SQL_NULL_HDBC);
}

View File

@ -27,7 +27,7 @@ TypeInfo::TypeInfo(SQLHDBC* pHDBC): _pHDBC(pHDBC)
{
fillCTypes();
fillSQLTypes();
if (_pHDBC) fillTypeInfo(*_pHDBC);
if (_pHDBC) fillTypeInfo(_pHDBC);
}
@ -62,7 +62,8 @@ void TypeInfo::fillCTypes()
void TypeInfo::fillSQLTypes()
{
_sqlDataTypes.insert(ValueType(SQL_C_CHAR, SQL_LONGVARCHAR));
_sqlDataTypes.insert(ValueType(SQL_C_CHAR, SQL_VARCHAR));
_sqlDataTypes.insert(ValueType(SQL_C_WCHAR, SQL_WVARCHAR));
_sqlDataTypes.insert(ValueType(SQL_C_BIT, SQL_BIT));
_sqlDataTypes.insert(ValueType(SQL_C_TINYINT, SQL_TINYINT));
_sqlDataTypes.insert(ValueType(SQL_C_STINYINT, SQL_TINYINT));
@ -84,9 +85,9 @@ void TypeInfo::fillSQLTypes()
}
void TypeInfo::fillTypeInfo(SQLHDBC pHDBC)
void TypeInfo::fillTypeInfo(const SQLHDBC* pHDBC)
{
_pHDBC = &pHDBC;
_pHDBC = pHDBC;
if (_typeInfo.empty() && _pHDBC)
{
@ -98,7 +99,7 @@ void TypeInfo::fillTypeInfo(SQLHDBC pHDBC)
rc = SQLAllocHandle(SQL_HANDLE_STMT, *_pHDBC, &hstmt);
if (!SQL_SUCCEEDED(rc))
throw StatementException(hstmt, "SQLGetData()");
throw StatementException(hstmt, "ODBC::Preparator::fillTypeInfo():SQLGetData()");
rc = SQLGetTypeInfo(hstmt, SQL_ALL_TYPES);
if (SQL_SUCCEEDED(rc))

View File

@ -594,6 +594,9 @@ CppUnit::Test* ODBCDB2Test::suite()
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCDB2Test");
CppUnit_addTest(pSuite, ODBCDB2Test, testBareboneODBC);
CppUnit_addTest(pSuite, ODBCDB2Test, testConnection);
CppUnit_addTest(pSuite, ODBCDB2Test, testSession);
CppUnit_addTest(pSuite, ODBCDB2Test, testSessionPool);
CppUnit_addTest(pSuite, ODBCDB2Test, testZeroRows);
CppUnit_addTest(pSuite, ODBCDB2Test, testSimpleAccess);
CppUnit_addTest(pSuite, ODBCDB2Test, testComplexType);

View File

@ -421,6 +421,9 @@ CppUnit::Test* ODBCMySQLTest::suite()
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCMySQLTest");
CppUnit_addTest(pSuite, ODBCMySQLTest, testBareboneODBC);
CppUnit_addTest(pSuite, ODBCMySQLTest, testConnection);
CppUnit_addTest(pSuite, ODBCMySQLTest, testSession);
CppUnit_addTest(pSuite, ODBCMySQLTest, testSessionPool);
CppUnit_addTest(pSuite, ODBCMySQLTest, testZeroRows);
CppUnit_addTest(pSuite, ODBCMySQLTest, testSimpleAccess);
CppUnit_addTest(pSuite, ODBCMySQLTest, testComplexType);

View File

@ -854,6 +854,9 @@ CppUnit::Test* ODBCOracleTest::suite()
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCOracleTest");
CppUnit_addTest(pSuite, ODBCOracleTest, testBareboneODBC);
CppUnit_addTest(pSuite, ODBCOracleTest, testConnection);
CppUnit_addTest(pSuite, ODBCOracleTest, testSession);
CppUnit_addTest(pSuite, ODBCOracleTest, testSessionPool);
CppUnit_addTest(pSuite, ODBCOracleTest, testZeroRows);
CppUnit_addTest(pSuite, ODBCOracleTest, testSimpleAccess);
CppUnit_addTest(pSuite, ODBCOracleTest, testComplexType);

View File

@ -583,6 +583,9 @@ CppUnit::Test* ODBCPostgreSQLTest::suite()
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCPostgreSQLTest");
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testBareboneODBC);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testConnection);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSession);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSessionPool);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testZeroRows);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSimpleAccess);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testComplexType);

View File

@ -47,39 +47,26 @@ using Poco::DateTime;
// uncomment to use native SQL driver
//#define POCO_ODBC_USE_SQL_NATIVE
// FreeTDS version selection guide (from http://www.freetds.org/userguide/choosingtdsprotocol.htm)
// FreeTDS version selection guide: http://www.freetds.org/userguide/choosingtdsprotocol.htm
// (see #define FREE_TDS_VERSION below)
// Product TDS Version Comment
// ---------------------------------------------------+------------+------------------------------------------------------------
// Sybase before System 10, Microsoft SQL Server 6.x 4.2 Still works with all products, subject to its limitations.
// Sybase System 10 and above 5.0 Still the most current protocol used by Sybase.
// Sybase System SQL Anywhere 5.0 only Originally Watcom SQL Server, a completely separate codebase.
// Our best information is that SQL Anywhere first supported TDS
// in version 5.5.03 using the OpenServer Gateway (OSG), and native
// TDS 5.0 support arrived with version 6.0.
// Microsoft SQL Server 7.0 7.0 Includes support for the extended datatypes in SQL Server 7.0
// (such as char/varchar fields of more than 255 characters), and
// support for Unicode.
// Microsoft SQL Server 2000 8.0 Include support for bigint (64 bit integers), variant and collation
// on all fields. variant is not supported; collation is not widely used.
#if defined(POCO_OS_FAMILY_WINDOWS) && !defined(FORCE_FREE_TDS)
#if !defined(FORCE_FREE_TDS)
#ifdef POCO_ODBC_USE_SQL_NATIVE
#define MS_SQL_SERVER_ODBC_DRIVER "SQL Server Native Client 10.0"
#else
#define MS_SQL_SERVER_ODBC_DRIVER "SQL Server"
#define MS_SQL_SERVER_ODBC_DRIVER "ODBC Driver 18 for SQL Server"
#endif
#pragma message ("Using " MS_SQL_SERVER_ODBC_DRIVER " driver")
#else
#define MS_SQL_SERVER_ODBC_DRIVER "FreeTDS"
#define FREE_TDS_VERSION "8.0"
#define FREE_TDS_VERSION "7.4"
#if defined(POCO_OS_FAMILY_WINDOWS)
#pragma message ("Using " MS_SQL_SERVER_ODBC_DRIVER " driver, version " FREE_TDS_VERSION)
#endif
#endif
#define MS_SQL_SERVER_DSN "PocoDataSQLServerTest"
#define MS_SQL_SERVER_SERVER POCO_ODBC_TEST_DATABASE_SERVER "\\SQLEXPRESS"
#define MS_SQL_SERVER_SERVER POCO_ODBC_TEST_DATABASE_SERVER
#define MS_SQL_SERVER_PORT "1433"
#define MS_SQL_SERVER_DB "poco"
#define MS_SQL_SERVER_UID "poco"
@ -101,6 +88,7 @@ std::string ODBCSQLServerTest::_connectString = "DRIVER=" MS_SQL_SERVER_ODBC_DRI
"DATABASE=" MS_SQL_SERVER_DB ";"
"SERVER=" MS_SQL_SERVER_SERVER ";"
"PORT=" MS_SQL_SERVER_PORT ";"
"Encrypt=no"
#ifdef FREE_TDS_VERSION
"TDS_Version=" FREE_TDS_VERSION ";"
#endif
@ -120,7 +108,7 @@ ODBCSQLServerTest::~ODBCSQLServerTest()
void ODBCSQLServerTest::testBareboneODBC()
{
std::string tableCreateString = "CREATE TABLE Test "
std::string createString = "CREATE TABLE Test "
"(First VARCHAR(30),"
"Second VARCHAR(30),"
"Third VARBINARY(30),"
@ -128,24 +116,47 @@ void ODBCSQLServerTest::testBareboneODBC()
"Fifth FLOAT,"
"Sixth DATETIME)";
executor().bareboneODBCTest(dbConnString(), tableCreateString,
executor().bareboneODBCTest(dbConnString(), createString,
SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_MANUAL, true, "CONVERT(VARBINARY(30),?)");
executor().bareboneODBCTest(dbConnString(), tableCreateString,
executor().bareboneODBCTest(dbConnString(), createString,
SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND, true, "CONVERT(VARBINARY(30),?)");
executor().bareboneODBCTest(dbConnString(), tableCreateString,
executor().bareboneODBCTest(dbConnString(), createString,
SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL, true, "CONVERT(VARBINARY(30),?)");
executor().bareboneODBCTest(dbConnString(), tableCreateString,
executor().bareboneODBCTest(dbConnString(), createString,
SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND, true, "CONVERT(VARBINARY(30),?)");
tableCreateString = "CREATE TABLE Test "
createString = "CREATE TABLE Test "
"(First VARCHAR(30),"
"Second INTEGER,"
"Third FLOAT)";
executor().bareboneODBCMultiResultTest(dbConnString(), tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_MANUAL);
executor().bareboneODBCMultiResultTest(dbConnString(), tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND);
executor().bareboneODBCMultiResultTest(dbConnString(), tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL);
executor().bareboneODBCMultiResultTest(dbConnString(), tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND);
executor().bareboneODBCMultiResultTest(dbConnString(), createString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_MANUAL);
executor().bareboneODBCMultiResultTest(dbConnString(), createString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND);
executor().bareboneODBCMultiResultTest(dbConnString(), createString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL);
executor().bareboneODBCMultiResultTest(dbConnString(), createString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND);
dropObject("PROCEDURE", "TestStoredProcedure");
createString = "CREATE PROCEDURE TestStoredProcedure(@inParam VARCHAR(MAX), @outParam VARCHAR(MAX) OUTPUT) AS "
"BEGIN "
" DECLARE @retVal int;"
" SET @outParam = @inParam; "
" SET @retVal = @outParam;"
" RETURN @retVal;"
"END;";
std::string execString = "{? = CALL TestStoredProcedure(?, ?)}";
executor().bareboneODBCStoredFuncTest(dbConnString(), createString, execString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_MANUAL);
executor().bareboneODBCStoredFuncTest(dbConnString(), createString, execString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND);
// data at exec fails for the SNAC driver - for some reason, SQLParamData() never reports the return parameter
// newer drivers work fine
if (std::string(MS_SQL_SERVER_ODBC_DRIVER) != "SQL Server")
{
executor().bareboneODBCStoredFuncTest(dbConnString(), createString, execString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL);
executor().bareboneODBCStoredFuncTest(dbConnString(), createString, execString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND);
}
else
std::cout << "Parameter at exec binding tests disabled for " << MS_SQL_SERVER_ODBC_DRIVER << std::endl;
}
void ODBCSQLServerTest::testTempTable()
@ -161,13 +172,14 @@ void ODBCSQLServerTest::testTempTable()
std::vector<testParam> testParams;
session() << ("select * from #test;"), into(testParams), now;
assertEquals(1, testParams.size());
assertEquals(1, static_cast<long>(testParams.size()));
assertEquals(1, testParams.front().get<0>());
assertEquals(2, testParams.front().get<1>());
assertEquals(3, testParams.front().get<2>());
}
void ODBCSQLServerTest::testBLOB()
{
const std::size_t maxFldSize = 250000;
@ -262,285 +274,386 @@ void ODBCSQLServerTest::testBulk()
void ODBCSQLServerTest::testStoredProcedure()
{
for (int k = 0; k < 8;)
try
{
session().setFeature("autoBind", bindValue(k));
session().setFeature("autoExtract", bindValue(k+1));
for (int k = 0; k < 8;)
{
session().setFeature("autoBind", bindValue(k));
session().setFeature("autoExtract", bindValue(k + 1));
dropObject("PROCEDURE", "storedProcedure");
session() << "CREATE PROCEDURE storedProcedure(@outParam int OUTPUT) AS "
"BEGIN "
" SET @outParam = -1; "
"END;"
, now;
int i = 0;
session() << "{call storedProcedure(?)}", out(i), now;
assertTrue (-1 == i);
dropObject("PROCEDURE", "storedProcedure");
session() << "CREATE PROCEDURE storedProcedure(@inParam int, @outParam int OUTPUT) AS "
"BEGIN "
" SET @outParam = @inParam*@inParam; "
"END;"
, now;
i = 2;
int j = 0;
session() << "{call storedProcedure(?, ?)}", in(i), out(j), now;
assertTrue (4 == j);
dropObject("PROCEDURE", "storedProcedure");
session() << "CREATE PROCEDURE storedProcedure(@ioParam int OUTPUT) AS "
"BEGIN "
" SET @ioParam = @ioParam*@ioParam; "
"END;"
, now;
i = 2;
session() << "{call storedProcedure(?)}", io(i), now;
assertTrue (4 == i);
dropObject("PROCEDURE", "storedProcedure");
session() << "CREATE PROCEDURE storedProcedure(@ioParam DATETIME OUTPUT) AS "
"BEGIN "
" SET @ioParam = @ioParam + 1; "
"END;" , now;
DateTime dt(1965, 6, 18, 5, 35, 1);
session() << "{call storedProcedure(?)}", io(dt), now;
assertTrue (19 == dt.day());
dropObject("PROCEDURE", "storedProcedure");
session().setFeature("autoBind", true);
session() << "CREATE PROCEDURE storedProcedure(@inParam VARCHAR(MAX), @outParam VARCHAR(MAX) OUTPUT) AS "
"BEGIN "
" SET @outParam = @inParam; "
"END;"
, now;
std::string inParam = "123";
std::string outParam(4, 0);
session() << "{call storedProcedure(?, ?)}", in(inParam), out(outParam), now;
assertTrue(outParam == inParam);
k += 2;
}
dropObject("PROCEDURE", "storedProcedure");
session() << "CREATE PROCEDURE storedProcedure(@outParam int OUTPUT) AS "
"BEGIN "
"SET @outParam = -1; "
"END;"
, now;
int i = 0;
session() << "{call storedProcedure(?)}", out(i), now;
assertTrue (-1 == i);
dropObject("PROCEDURE", "storedProcedure");
session() << "CREATE PROCEDURE storedProcedure(@inParam int, @outParam int OUTPUT) AS "
"BEGIN "
"SET @outParam = @inParam*@inParam; "
"END;"
, now;
i = 2;
int j = 0;
session() << "{call storedProcedure(?, ?)}", in(i), out(j), now;
assertTrue (4 == j);
dropObject("PROCEDURE", "storedProcedure");
session() << "CREATE PROCEDURE storedProcedure(@ioParam int OUTPUT) AS "
"BEGIN "
"SET @ioParam = @ioParam*@ioParam; "
"END;"
, now;
i = 2;
session() << "{call storedProcedure(?)}", io(i), now;
assertTrue (4 == i);
dropObject("PROCEDURE", "storedProcedure");
session() << "CREATE PROCEDURE storedProcedure(@ioParam DATETIME OUTPUT) AS "
"BEGIN "
" SET @ioParam = @ioParam + 1; "
"END;" , now;
DateTime dt(1965, 6, 18, 5, 35, 1);
session() << "{call storedProcedure(?)}", io(dt), now;
assertTrue (19 == dt.day());
dropObject("PROCEDURE", "storedProcedure");
k += 2;
}
/*TODO - currently fails with following error:
[Microsoft][ODBC SQL Server Driver][SQL Server]Invalid parameter
2 (''): Data type 0x23 is a deprecated large object, or LOB, but is marked as output parameter.
Deprecated types are not supported as output parameters. Use current large object types instead.
session().setFeature("autoBind", true);
session() << "CREATE PROCEDURE storedProcedure(@inParam VARCHAR(MAX), @outParam VARCHAR(MAX) OUTPUT) AS "
"BEGIN "
"SET @outParam = @inParam; "
"END;"
, now;
std::string inParam = "123";
std::string outParam;
try{
session() << "{call storedProcedure(?, ?)}", in(inParam), out(outParam), now;
}catch(StatementException& ex){std::cout << ex.toString();}
assertTrue (outParam == inParam);
dropObject("PROCEDURE", "storedProcedure");
*/
catch (ConnectionException& ce) { std::cout << ce.toString() << std::endl; fail("testStoredProcedure()"); }
catch (StatementException& se) { std::cout << se.toString() << std::endl; fail("testStoredProcedure()"); }
}
void ODBCSQLServerTest::testCursorStoredProcedure()
{
for (int k = 0; k < 8;)
try
{
session().setFeature("autoBind", bindValue(k));
session().setFeature("autoExtract", bindValue(k+1));
for (int k = 0; k < 8;)
{
session().setFeature("autoBind", bindValue(k));
session().setFeature("autoExtract", bindValue(k+1));
recreatePersonTable();
typedef Tuple<std::string, std::string, std::string, int> Person;
std::vector<Person> people;
people.push_back(Person("Simpson", "Homer", "Springfield", 42));
people.push_back(Person("Simpson", "Bart", "Springfield", 12));
people.push_back(Person("Simpson", "Lisa", "Springfield", 10));
session() << "INSERT INTO Person VALUES (?, ?, ?, ?)", use(people), now;
recreatePersonTable();
typedef Tuple<std::string, std::string, std::string, int> Person;
std::vector<Person> people;
people.push_back(Person("Simpson", "Homer", "Springfield", 42));
people.push_back(Person("Simpson", "Bart", "Springfield", 12));
people.push_back(Person("Simpson", "Lisa", "Springfield", 10));
session() << "INSERT INTO Person VALUES (?, ?, ?, ?)", use(people), now;
dropObject("PROCEDURE", "storedCursorProcedure");
session() << "CREATE PROCEDURE storedCursorProcedure(@ageLimit int) AS "
"BEGIN "
" SELECT * "
" FROM Person "
" WHERE Age < @ageLimit "
" ORDER BY Age DESC; "
"END;"
, now;
people.clear();
int age = 13;
session() << "{call storedCursorProcedure(?)}", in(age), into(people), now;
assertTrue (2 == people.size());
assertTrue (Person("Simpson", "Bart", "Springfield", 12) == people[0]);
assertTrue (Person("Simpson", "Lisa", "Springfield", 10) == people[1]);
Statement stmt = ((session() << "{call storedCursorProcedure(?)}", in(age), now));
RecordSet rs(stmt);
assertTrue (rs["LastName"] == "Simpson");
assertTrue (rs["FirstName"] == "Bart");
assertTrue (rs["Address"] == "Springfield");
assertTrue (rs["Age"] == 12);
dropObject("PROCEDURE", "storedCursorProcedure");
// procedure that suppresses row counts and recordsets
session() << "CREATE PROCEDURE storedCursorProcedure(@outStr varchar(64) OUTPUT) AS "
"BEGIN "
" SET NOCOUNT ON;"
" DECLARE @PersonTable TABLE (LastName VARCHAR(30), FirstName VARCHAR(30), Address VARCHAR(30), Age INTEGER); "
" INSERT INTO @PersonTable SELECT * FROM Person; "
" UPDATE Person SET FirstName = 'Dart' WHERE FirstName = 'Bart';"
" SELECT @outStr = FirstName FROM Person WHERE Age = 12;"
" RETURN -1;"
"END;"
, now;
std::string outStr(64, 0);
int ret = 0;
session() << "{? = call storedCursorProcedure(?)}", out(ret), out(outStr), now;
assertTrue(ret == -1);
assertTrue(outStr == "Dart");
dropObject("PROCEDURE", "storedCursorProcedure");
// procedure that suppresses row counts and recordsets
session() << "CREATE PROCEDURE storedCursorProcedure(@name varchar(30)) AS "
"BEGIN "
" SET NOCOUNT ON;"
" DECLARE @count int; "
" SELECT @count = count(*) FROM Person WHERE FirstName = @name;"
" RETURN @count;"
"END;"
, now;
std::string name = "Dart";
ret = 0;
session() << "{? = call storedCursorProcedure(?)}", out(ret), in(name), now;
assertTrue(ret == 1);
dropObject("TABLE", "Person");
k += 2;
}
dropObject("PROCEDURE", "storedCursorProcedure");
session() << "CREATE PROCEDURE storedCursorProcedure(@ageLimit int) AS "
"BEGIN "
" SELECT * "
" FROM Person "
" WHERE Age < @ageLimit "
" ORDER BY Age DESC; "
"END;"
, now;
people.clear();
int age = 13;
session() << "{call storedCursorProcedure(?)}", in(age), into(people), now;
assertTrue (2 == people.size());
assertTrue (Person("Simpson", "Bart", "Springfield", 12) == people[0]);
assertTrue (Person("Simpson", "Lisa", "Springfield", 10) == people[1]);
Statement stmt = ((session() << "{call storedCursorProcedure(?)}", in(age), now));
RecordSet rs(stmt);
assertTrue (rs["LastName"] == "Simpson");
assertTrue (rs["FirstName"] == "Bart");
assertTrue (rs["Address"] == "Springfield");
assertTrue (rs["Age"] == 12);
dropObject("TABLE", "Person");
dropObject("PROCEDURE", "storedCursorProcedure");
k += 2;
}
catch (ConnectionException& ce) { std::cout << ce.toString() << std::endl; fail("testCursorStoredProcedure()"); }
catch (StatementException& se) { std::cout << se.toString() << std::endl; fail("testCursorStoredProcedure()"); }
}
void ODBCSQLServerTest::testStoredProcedureAny()
{
for (int k = 0; k < 8;)
try
{
session().setFeature("autoBind", bindValue(k));
session().setFeature("autoExtract", bindValue(k+1));
for (int k = 0; k < 8;)
{
session().setFeature("autoBind", bindValue(k));
session().setFeature("autoExtract", bindValue(k+1));
Any i = 2;
Any j = 0;
Any i = 2;
Any j = 0;
session() << "CREATE PROCEDURE storedProcedure(@inParam int, @outParam int OUTPUT) AS "
"BEGIN "
"SET @outParam = @inParam*@inParam; "
"END;"
, now;
dropObject("PROCEDURE", "storedProcedure");
session() << "CREATE PROCEDURE storedProcedure(@inParam int, @outParam int OUTPUT) AS "
"BEGIN "
"SET @outParam = @inParam*@inParam; "
"END;"
, now;
session() << "{call storedProcedure(?, ?)}", in(i), out(j), now;
assertTrue (4 == AnyCast<int>(j));
session() << "DROP PROCEDURE storedProcedure;", now;
session() << "{call storedProcedure(?, ?)}", in(i), out(j), now;
assertTrue (4 == AnyCast<int>(j));
session() << "CREATE PROCEDURE storedProcedure(@ioParam int OUTPUT) AS "
"BEGIN "
"SET @ioParam = @ioParam*@ioParam; "
"END;"
, now;
dropObject("PROCEDURE", "storedProcedure");
session() << "CREATE PROCEDURE storedProcedure(@ioParam int OUTPUT) AS "
"BEGIN "
"SET @ioParam = @ioParam*@ioParam; "
"END;"
, now;
i = 2;
session() << "{call storedProcedure(?)}", io(i), now;
assertTrue (4 == AnyCast<int>(i));
i = 2;
session() << "{call storedProcedure(?)}", io(i), now;
assertTrue (4 == AnyCast<int>(i));
dropObject("PROCEDURE", "storedProcedure");
k += 2;
}
dropObject("PROCEDURE", "storedProcedure");
k += 2;
}
catch (ConnectionException& ce) { std::cout << ce.toString() << std::endl; fail("testStoredProcedureAny()"); }
catch (StatementException& se) { std::cout << se.toString() << std::endl; fail("testStoredProcedureAny()"); }
}
void ODBCSQLServerTest::testStoredProcedureDynamicAny()
{
for (int k = 0; k < 8;)
try
{
session().setFeature("autoBind", bindValue(k));
for (int k = 0; k < 8;)
{
session().setFeature("autoBind", bindValue(k));
DynamicAny i = 2;
DynamicAny j = 0;
DynamicAny i = 2;
DynamicAny j = 0;
session() << "CREATE PROCEDURE storedProcedure(@inParam int, @outParam int OUTPUT) AS "
"BEGIN "
"SET @outParam = @inParam*@inParam; "
"END;"
, now;
dropObject("PROCEDURE", "storedProcedure");
session() << "CREATE PROCEDURE storedProcedure(@inParam int, @outParam int OUTPUT) AS "
"BEGIN "
"SET @outParam = @inParam*@inParam; "
"END;"
, now;
session() << "{call storedProcedure(?, ?)}", in(i), out(j), now;
assertTrue (4 == j);
session() << "DROP PROCEDURE storedProcedure;", now;
session() << "{call storedProcedure(?, ?)}", in(i), out(j), now;
assertTrue (4 == j);
session() << "CREATE PROCEDURE storedProcedure(@ioParam int OUTPUT) AS "
"BEGIN "
"SET @ioParam = @ioParam*@ioParam; "
"END;"
, now;
dropObject("PROCEDURE", "storedProcedure");
session() << "CREATE PROCEDURE storedProcedure(@ioParam int OUTPUT) AS "
"BEGIN "
"SET @ioParam = @ioParam*@ioParam; "
"END;"
, now;
i = 2;
session() << "{call storedProcedure(?)}", io(i), now;
assertTrue (4 == i);
i = 2;
session() << "{call storedProcedure(?)}", io(i), now;
assertTrue (4 == i);
k += 2;
}
dropObject("PROCEDURE", "storedProcedure");
k += 2;
}
catch (ConnectionException& ce) { std::cout << ce.toString() << std::endl; fail("testStoredProcedureDynamicAny()"); }
catch (StatementException& se) { std::cout << se.toString() << std::endl; fail("testStoredProcedureDynamicAny()"); }
}
void ODBCSQLServerTest::testStoredProcedureReturn()
{
try
{
for (int k = 0; k < 8;)
{
session().setFeature("autoBind", bindValue(k));
session().setFeature("autoExtract", bindValue(k+1));
dropObject("PROCEDURE", "storedProcedureReturn");
session() << "CREATE PROCEDURE storedProcedureReturn AS "
"BEGIN "
"DECLARE @retVal int;"
"SET @retVal = -1;"
"RETURN @retVal;"
"END;"
, now;
int i = 0;
session() << "{? = call storedProcedureReturn}", out(i), now;
assertTrue (-1 == i);
dropObject("PROCEDURE", "storedProcedureReturn");
session() << "CREATE PROCEDURE storedProcedureReturn(@inParam int) AS "
"BEGIN "
"RETURN @inParam*@inParam;"
"END;"
, now;
i = 2;
int result = 0;
session() << "{? = call storedProcedureReturn(?)}", out(result), in(i), now;
assertTrue (4 == result);
dropObject("PROCEDURE", "storedProcedureReturn");
session() << "CREATE PROCEDURE storedProcedureReturn(@inParam int, @outParam int OUTPUT) AS "
"BEGIN "
"SET @outParam = @inParam*@inParam;"
"RETURN @outParam;"
"END"
, now;
i = 2;
int j = 0;
result = 0;
session() << "{? = call storedProcedureReturn(?, ?)}", out(result), in(i), out(j), now;
assertTrue (4 == j);
assertTrue (j == result);
dropObject("PROCEDURE", "storedProcedureReturn");
session() << "CREATE PROCEDURE storedProcedureReturn(@param1 int OUTPUT,@param2 int OUTPUT) AS "
"BEGIN "
"DECLARE @temp int; "
"SET @temp = @param1;"
"SET @param1 = @param2;"
"SET @param2 = @temp;"
"RETURN @param1 + @param2; "
"END"
, now;
i = 1;
j = 2;
result = 0;
session() << "{? = call storedProcedureReturn(?, ?)}", out(result), io(i), io(j), now;
assertTrue (1 == j);
assertTrue (2 == i);
assertTrue (3 == result);
Tuple<int, int> params(1, 2);
assertTrue (1 == params.get<0>());
assertTrue (2 == params.get<1>());
result = 0;
session() << "{? = call storedProcedureReturn(?, ?)}", out(result), io(params), now;
assertTrue (1 == params.get<1>());
assertTrue (2 == params.get<0>());
assertTrue (3 == result);
k += 2;
}
dropObject("PROCEDURE", "storedProcedureReturn");
}
catch (ConnectionException& ce) { std::cout << ce.toString() << std::endl; fail("testStoredProcedureReturn()"); }
catch (StatementException& se) { std::cout << se.toString() << std::endl; fail("testStoredProcedureReturn()"); }
}
void ODBCSQLServerTest::testStoredFunction()
{
for (int k = 0; k < 8;)
try
{
session().setFeature("autoBind", bindValue(k));
session().setFeature("autoExtract", bindValue(k+1));
for (int k = 0; k < 8;)
{
session().setFeature("autoBind", bindValue(k));
session().setFeature("autoExtract", bindValue(k + 1));
dropObject("PROCEDURE", "storedFunction");
session() << "CREATE PROCEDURE storedFunction AS "
"BEGIN "
"DECLARE @retVal int;"
"SET @retVal = -1;"
"RETURN @retVal;"
"END;"
, now;
dropObject("FUNCTION", "storedFunction");
session() << "CREATE FUNCTION storedFunction() "
" RETURNS int AS "
"BEGIN "
" DECLARE @retVal int;"
" SET @retVal = -1;"
" RETURN @retVal;"
"END;"
, now;
int i = 0;
session() << "{? = call storedFunction}", out(i), now;
assertTrue (-1 == i);
dropObject("PROCEDURE", "storedFunction");
int i = 0;
session() << "{? = call dbo.storedFunction}", out(i), now;
assertTrue(-1 == i);
dropObject("FUNCTION", "storedFunction");
session() << "CREATE FUNCTION storedFunction(@inParam int) "
"RETURNS int AS "
"BEGIN "
" RETURN @inParam*@inParam;"
"END;"
, now;
session() << "CREATE PROCEDURE storedFunction(@inParam int) AS "
"BEGIN "
"RETURN @inParam*@inParam;"
"END;"
, now;
i = 2;
int result = 0;
session() << "{? = call dbo.storedFunction(?)}", out(result), in(i), now;
assertTrue(4 == result);
result = 0;
session() << "SELECT dbo.storedFunction(?)", into(result), in(i), now;
assertTrue(4 == result);
i = 2;
int result = 0;
session() << "{? = call storedFunction(?)}", out(result), in(i), now;
assertTrue (4 == result);
dropObject("PROCEDURE", "storedFunction");
session() << "CREATE PROCEDURE storedFunction(@inParam int, @outParam int OUTPUT) AS "
"BEGIN "
"SET @outParam = @inParam*@inParam;"
"RETURN @outParam;"
"END"
, now;
i = 2;
int j = 0;
result = 0;
session() << "{? = call storedFunction(?, ?)}", out(result), in(i), out(j), now;
assertTrue (4 == j);
assertTrue (j == result);
dropObject("PROCEDURE", "storedFunction");
session() << "CREATE PROCEDURE storedFunction(@param1 int OUTPUT,@param2 int OUTPUT) AS "
"BEGIN "
"DECLARE @temp int; "
"SET @temp = @param1;"
"SET @param1 = @param2;"
"SET @param2 = @temp;"
"RETURN @param1 + @param2; "
"END"
, now;
i = 1;
j = 2;
result = 0;
session() << "{? = call storedFunction(?, ?)}", out(result), io(i), io(j), now;
assertTrue (1 == j);
assertTrue (2 == i);
assertTrue (3 == result);
Tuple<int, int> params(1, 2);
assertTrue (1 == params.get<0>());
assertTrue (2 == params.get<1>());
result = 0;
session() << "{? = call storedFunction(?, ?)}", out(result), io(params), now;
assertTrue (1 == params.get<1>());
assertTrue (2 == params.get<0>());
assertTrue (3 == result);
dropObject("PROCEDURE", "storedFunction");
k += 2;
k += 2;
}
dropObject("FUNCTION", "storedFunction");
}
catch (ConnectionException& ce) { std::cout << ce.toString() << std::endl; fail("testStoredFunction()"); }
catch (StatementException& se) { std::cout << se.toString() << std::endl; fail("testStoredFunction()"); }
}
@ -552,19 +665,15 @@ void ODBCSQLServerTest::dropObject(const std::string& type, const std::string& n
}
catch (StatementException& ex)
{
bool ignoreError = false;
const StatementDiagnostics::FieldVec& flds = ex.diagnostics().fields();
StatementDiagnostics::Iterator it = flds.begin();
for (; it != flds.end(); ++it)
{
if (3701 == it->_nativeError)//(table does not exist)
{
ignoreError = true;
break;
}
if (3701 == it->_nativeError) // (does not exist)
return;
}
if (!ignoreError) throw;
throw;
}
}
@ -725,13 +834,13 @@ void ODBCSQLServerTest::recreateLogTable()
try
{
std::string sql = "CREATE TABLE %s "
"(Source VARCHAR(max),"
"Name VARCHAR(max),"
"(Source VARCHAR(256),"
"Name VARCHAR(256),"
"ProcessId INTEGER,"
"Thread VARCHAR(max), "
"Thread VARCHAR(256), "
"ThreadId INTEGER,"
"Priority INTEGER,"
"Text VARCHAR(max),"
"Text VARCHAR(1024),"
"DateTime DATETIME)";
session() << sql, "T_POCO_LOG", now;
@ -779,6 +888,9 @@ CppUnit::Test* ODBCSQLServerTest::suite()
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCSQLServerTest");
CppUnit_addTest(pSuite, ODBCSQLServerTest, testBareboneODBC);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testConnection);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testSession);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testSessionPool);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testZeroRows);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testSimpleAccess);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testComplexType);
@ -828,6 +940,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
CppUnit_addTest(pSuite, ODBCSQLServerTest, testBLOB);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testBLOBContainer);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testBLOBStmt);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testRecordSet);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testDateTime);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testFloat);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testDouble);
@ -838,6 +951,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
CppUnit_addTest(pSuite, ODBCSQLServerTest, testCursorStoredProcedure);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredProcedureAny);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredProcedureDynamicAny);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredProcedureReturn);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredFunction);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalExtraction);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testFilter);

View File

@ -26,13 +26,14 @@ class ODBCSQLServerTest: public ODBCTest
/// SQLServer ODBC test class
/// Tested:
///
/// Driver | DB | OS
/// --------------------+-----------------------------------+------------------------------------------
/// 2000.86.1830.00 | SQL Server Express 9.0.2047 | MS Windows XP Professional x64 v.2003/SP1
/// 2005.90.2047.00 | SQL Server Express 9.0.2047 | MS Windows XP Professional x64 v.2003/SP1
/// 2009.100.1600.01 | SQL Server Express 10.50.1600.1 | MS Windows XP Professional x64 v.2003/SP1
///
/// Driver name | Driver version | DB version | OS
/// ------------------------------------+-------------------+-----------------------+------------------------------------------
/// SQL Server Express 9.0.2047 | 2000.86.1830.00 | | MS Windows XP Professional x64 v.2003/SP1
/// SQL Server Express 9.0.2047 | 2005.90.2047.00 | | MS Windows XP Professional x64 v.2003/SP1
/// SQL Server Express 10.50.1600.1 | 2009.100.1600.01 | | MS Windows XP Professional x64 v.2003/SP1
/// SQL Server | 10.00.22621.1992 | 16.0.1000.6 (64-bit) | Windows 11
/// ODBC Driver 17 for SQL Server | 2017.1710.03.01 | 16.0.1000.6 (64-bit) | Windows 11
/// ODBC Driver 18 for SQL Server | 2018.183.01.01 | 16.0.1000.6 (64-bit) | Windows 11
{
public:
ODBCSQLServerTest(const std::string& name);
@ -51,6 +52,7 @@ public:
void testStoredProcedureAny();
void testStoredProcedureDynamicAny();
void testStoredProcedureReturn();
void testStoredFunction();
static CppUnit::Test* suite();

View File

@ -323,6 +323,9 @@ CppUnit::Test* ODBCSQLiteTest::suite()
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCSQLiteTest");
CppUnit_addTest(pSuite, ODBCSQLiteTest, testBareboneODBC);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testConnection);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testSession);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testSessionPool);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testZeroRows);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testSimpleAccess);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testComplexType);

View File

@ -77,6 +77,24 @@ ODBCTest::~ODBCTest()
}
void ODBCTest::testConnection()
{
_pExecutor->connection(_rConnectString);
}
void ODBCTest::testSession()
{
_pExecutor->session(_rConnectString, 5);
}
void ODBCTest::testSessionPool()
{
_pExecutor->sessionPool(_rConnectString, 1, 4, 3, 5);
}
void ODBCTest::testZeroRows()
{
if (!_pSession) fail ("Test not available.");
@ -842,6 +860,21 @@ void ODBCTest::testBLOBStmt()
}
void ODBCTest::testRecordSet()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreatePersonDateTimeTable();
_pSession->setFeature("autoBind", bindValue(i));
_pSession->setFeature("autoExtract", bindValue(i+1));
_pExecutor->recordSet();
i += 2;
}
}
void ODBCTest::testDateTime()
{
if (!_pSession) fail ("Test not available.");
@ -1368,7 +1401,7 @@ ODBCTest::SessionPtr ODBCTest::init(const std::string& driver,
try
{
std::cout << "Conecting to [" << dbConnString << ']' << std::endl;
std::cout << "Connecting to [" << dbConnString << ']' << std::endl;
SessionPtr ptr = new Session(Poco::Data::ODBC::Connector::KEY, dbConnString, 5);
if (!dbEncoding.empty())
ptr->setProperty("dbEncoding", dbEncoding);

View File

@ -23,7 +23,7 @@
#include "SQLExecutor.h"
#define POCO_ODBC_TEST_DATABASE_SERVER "localhost"
#define POCO_ODBC_TEST_DATABASE_SERVER "10.211.55.5"//"localhost"
class ODBCTest: public CppUnit::TestCase
@ -47,6 +47,10 @@ public:
virtual void testBareboneODBC() = 0;
virtual void testConnection();
virtual void testSession();
virtual void testSessionPool();
virtual void testZeroRows();
virtual void testSimpleAccess();
virtual void testComplexType();
@ -107,6 +111,8 @@ public:
virtual void testBLOBContainer();
virtual void testBLOBStmt();
virtual void testRecordSet();
virtual void testDateTime();
virtual void testDate();
virtual void testTime();

View File

@ -29,6 +29,8 @@
#include "Poco/Data/Date.h"
#include "Poco/Data/Time.h"
#include "Poco/Data/LOB.h"
#include "Poco/Data/Session.h"
#include "Poco/Data/SessionPool.h"
#include "Poco/Data/StatementImpl.h"
#include "Poco/Data/RecordSet.h"
#include "Poco/Data/RowIterator.h"
@ -54,6 +56,7 @@
using namespace Poco::Data::Keywords;
using Poco::Data::Session;
using Poco::Data::SessionPool;
using Poco::Data::Statement;
using Poco::Data::RecordSet;
using Poco::Data::Column;
@ -73,6 +76,7 @@ using Poco::Data::ODBC::Preparator;
using Poco::Data::ODBC::ConnectionException;
using Poco::Data::ODBC::StatementException;
using Poco::Data::ODBC::DataTruncatedException;
using Poco::Data::ODBC::ConnectionError;
using Poco::Data::ODBC::StatementError;
using Poco::format;
using Poco::Tuple;
@ -354,13 +358,13 @@ void SQLExecutor::bareboneODBCTest(const std::string& dbConnString,
assertTrue (SQL_SUCCEEDED(rc) || SQL_NO_DATA == rc);
SQLULEN dateTimeColSize = 0;
SQLSMALLINT dateTimeDecDigits = 0;
SQLSMALLINT dateTimeDecDigits = -1;
if (SQL_SUCCEEDED(rc))
{
SQLLEN ind = 0;
rc = SQLGetData(hstmt, 3, SQL_C_SLONG, &dateTimeColSize, sizeof(SQLINTEGER), &ind);
poco_odbc_check_stmt (rc, hstmt);
rc = SQLGetData(hstmt, 14, SQL_C_SSHORT, &dateTimeDecDigits, sizeof(SQLSMALLINT), &ind);
rc = SQLGetData(hstmt, 15, SQL_C_SSHORT, &dateTimeDecDigits, sizeof(SQLSMALLINT), &ind);
poco_odbc_check_stmt (rc, hstmt);
assertTrue (sizeof(SQL_TIMESTAMP_STRUCT) <= dateTimeColSize);
@ -484,17 +488,25 @@ void SQLExecutor::bareboneODBCTest(const std::string& dbConnString,
0);
poco_odbc_check_stmt (rc, hstmt);
SQLSMALLINT dataType = 0;
SQLULEN parameterSize = 0;
SQLSMALLINT decimalDigits = 0;
SQLSMALLINT nullable = 0;
rc = SQLDescribeParam(hstmt, 6, &dataType, &parameterSize, &decimalDigits, &nullable);
if (SQL_SUCCEEDED(rc))
if (dateTimeColSize == 0 || dateTimeDecDigits == -1)
{
if (parameterSize)
dateTimeColSize = parameterSize;
if (decimalDigits)
dateTimeDecDigits = decimalDigits;
SQLSMALLINT dataType = 0;
SQLULEN parameterSize = 0;
SQLSMALLINT decimalDigits = -1;
SQLSMALLINT nullable = 0;
rc = SQLDescribeParam(hstmt, 6, &dataType, &parameterSize, &decimalDigits, &nullable);
if (SQL_SUCCEEDED(rc))
{
if (parameterSize != 0 && dateTimeColSize == 0)
dateTimeColSize = parameterSize;
if (decimalDigits != -1 && dateTimeDecDigits == -1)
dateTimeDecDigits = decimalDigits;
}
else
{
std::cerr << '[' << name() << ']' << " Warning: could not get SQL_TYPE_TIMESTAMP "
"parameter description." << std::endl;
}
}
else
std::cerr << '[' << name() << ']' << " Warning: could not get SQL_TYPE_TIMESTAMP parameter description." << std::endl;
@ -939,6 +951,191 @@ void SQLExecutor::bareboneODBCMultiResultTest(const std::string& dbConnString,
}
void SQLExecutor::bareboneODBCStoredFuncTest(const std::string& dbConnString,
const std::string& procCreateString,
const std::string& procExecuteString,
SQLExecutor::DataBinding bindMode,
SQLExecutor::DataExtraction extractMode)
{
SQLRETURN rc;
SQLHENV henv = SQL_NULL_HENV;
SQLHDBC hdbc = SQL_NULL_HDBC;
SQLHSTMT hstmt = SQL_NULL_HSTMT;
// Environment begin
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
poco_odbc_check_env(rc, henv);
rc = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
poco_odbc_check_env(rc, henv);
// Connection begin
rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
poco_odbc_check_dbc(rc, hdbc);
SQLCHAR connectOutput[1024] = { 0 };
SQLSMALLINT result;
rc = SQLDriverConnect(hdbc
, NULL
, (SQLCHAR*)dbConnString.c_str()
, (SQLSMALLINT)SQL_NTS
, connectOutput
, sizeof(connectOutput)
, &result
, SQL_DRIVER_NOPROMPT);
poco_odbc_check_dbc(rc, hdbc);
// retrieve datetime type information for this DBMS
rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
poco_odbc_check_stmt(rc, hstmt);
rc = SQLGetTypeInfo(hstmt, SQL_TYPE_TIMESTAMP);
poco_odbc_check_stmt(rc, hstmt);
rc = SQLFetch(hstmt);
assertTrue(SQL_SUCCEEDED(rc) || SQL_NO_DATA == rc);
SQLULEN dateTimeColSize = 0;
SQLSMALLINT dateTimeDecDigits = -1;
if (SQL_SUCCEEDED(rc))
{
SQLLEN ind = 0;
rc = SQLGetData(hstmt, 3, SQL_C_SLONG, &dateTimeColSize, sizeof(SQLINTEGER), &ind);
poco_odbc_check_stmt(rc, hstmt);
rc = SQLGetData(hstmt, 15, SQL_C_SSHORT, &dateTimeDecDigits, sizeof(SQLSMALLINT), &ind);
poco_odbc_check_stmt(rc, hstmt);
assertTrue(sizeof(SQL_TIMESTAMP_STRUCT) <= dateTimeColSize);
}
else if (SQL_NO_DATA == rc)
std::cerr << '[' << name() << ']' << " Warning: no SQL_TYPE_TIMESTAMP data type info returned by driver." << std::endl;
rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
poco_odbc_check_stmt(rc, hstmt);
// Statement begin
rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
poco_odbc_check_stmt(rc, hstmt);
std::string sql = "DROP PROCEDURE TestStoredProcedure";
SQLCHAR* pStr = (SQLCHAR*)sql.c_str();
SQLExecDirect(hstmt, pStr, (SQLINTEGER)sql.length());
//no return code check - ignore drop errors
// create stored prcedure and go
sql = procCreateString;
pStr = (SQLCHAR*)sql.c_str();
rc = SQLPrepare(hstmt, pStr, (SQLINTEGER)sql.length());
poco_odbc_check_stmt(rc, hstmt);
rc = SQLExecute(hstmt);
poco_odbc_check_stmt(rc, hstmt);
char inParam[10] = "1234";
char outParam[10] = "";
char retVal[10] = "";
sql = procExecuteString;
pStr = (SQLCHAR*)sql.c_str();
rc = SQLPrepare(hstmt, pStr, (SQLINTEGER)sql.length());
poco_odbc_check_stmt(rc, hstmt);
SQLLEN li[3] = { SQL_NTS, SQL_NTS, SQL_NTS };
SQLINTEGER size = (SQLINTEGER)strlen(inParam);
if (SQLExecutor::PB_AT_EXEC == bindMode)
li[0] = SQL_LEN_DATA_AT_EXEC(size);
rc = SQLBindParameter(hstmt,
(SQLUSMALLINT)1,
SQL_PARAM_OUTPUT,
SQL_C_CHAR,
SQL_VARCHAR,
(SQLUINTEGER)sizeof(retVal),
0,
(SQLPOINTER)retVal,
sizeof(retVal),
&li[0]);
poco_odbc_check_stmt(rc, hstmt);
if (SQLExecutor::PB_AT_EXEC == bindMode)
li[1] = SQL_LEN_DATA_AT_EXEC(size);
rc = SQLBindParameter(hstmt,
(SQLUSMALLINT)2,
SQL_PARAM_INPUT,
SQL_C_CHAR,
SQL_VARCHAR,
(SQLUINTEGER)sizeof(inParam),
0,
(SQLPOINTER)inParam,
sizeof(inParam),
&li[1]);
poco_odbc_check_stmt(rc, hstmt);
if (SQLExecutor::PB_AT_EXEC == bindMode)
li[2] = SQL_LEN_DATA_AT_EXEC(size);
rc = SQLBindParameter(hstmt,
(SQLUSMALLINT)3,
SQL_PARAM_OUTPUT,
SQL_C_CHAR,
SQL_VARCHAR,
(SQLUINTEGER)sizeof(outParam),
0,
(SQLPOINTER)outParam,
sizeof(outParam),
&li[2]);
poco_odbc_check_stmt(rc, hstmt);
rc = SQLExecute(hstmt);
if (rc && SQL_NEED_DATA != rc)
{
std::cout << "rc=" << rc << ", SQL_NEED_DATA=" << SQL_NEED_DATA << '\n';
poco_odbc_check_stmt(rc, hstmt);
}
assertTrue(SQL_NEED_DATA == rc || SQL_SUCCEEDED(rc));
if (SQL_NEED_DATA == rc)
{
SQLPOINTER pParam = 0;
while (SQL_NEED_DATA == (rc = SQLParamData(hstmt, &pParam)))
{
if ((pParam != (SQLPOINTER)retVal) &&
(pParam != (SQLPOINTER)inParam) &&
(pParam != (SQLPOINTER)outParam))
{
fail("Parameter mismatch.");
}
assertTrue(0 != (SQLINTEGER)size);
rc = SQLPutData(hstmt, pParam, (SQLINTEGER)size);
poco_odbc_check_stmt(rc, hstmt);
}
}
poco_odbc_check_stmt(rc, hstmt);
assertTrue(std::string(outParam) == std::string(inParam));
assertTrue(std::string(retVal) == std::string(inParam));
sql = "DROP PROCEDURE TestStoredProcedure";
pStr = (SQLCHAR*)sql.c_str();
rc = SQLExecDirect(hstmt, pStr, (SQLINTEGER)sql.length());
poco_odbc_check_stmt(rc, hstmt);
rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
poco_odbc_check_stmt(rc, hstmt);
// Connection end
rc = SQLDisconnect(hdbc);
poco_odbc_check_dbc(rc, hdbc);
rc = SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
poco_odbc_check_dbc(rc, hdbc);
// Environment end
rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
poco_odbc_check_env(rc, henv);
}
void SQLExecutor::execute(const std::string& sql)
{
try { session() << sql, now; }
@ -947,6 +1144,314 @@ void SQLExecutor::execute(const std::string& sql)
}
void SQLExecutor::connection(const std::string& connectString)
{
Poco::Data::ODBC::Connection c;
assertFalse (c.isConnected());
assertTrue (c.connect(connectString, 5));
assertTrue (c.isConnected());
assertTrue (c.getTimeout() == 5);
c.setTimeout(6);
assertTrue (c.getTimeout() == 6);
assertTrue (c.disconnect());
assertFalse (c.isConnected());
assertTrue (c.connect(connectString));
assertTrue (c.isConnected());
try
{
c.connect();
fail ("Connection attempt must fail when already connected");
}
catch(const Poco::InvalidAccessException&){}
assertTrue (c.disconnect());
assertFalse (c.isConnected());
try
{
c.connect();
fail ("Connection attempt with empty string must fail");
}
catch(const Poco::InvalidArgumentException&){}
}
void SQLExecutor::session(const std::string& connectString, int timeout)
{
Poco::Data::Session s(Poco::Data::ODBC::Connector::KEY, connectString, timeout);
assertTrue (s.isConnected());
s.close();
assertTrue (!s.isConnected());
s.open();
assertTrue (s.isConnected());
s.reconnect();
assertTrue (s.isConnected());
s.close();
assertTrue (!s.isConnected());
s.reconnect();
assertTrue (s.isConnected());
Poco::Any any = s.getProperty("handle");
assertTrue (typeid(SQLHDBC) == any.type());
SQLHDBC hdbc = Poco::AnyCast<SQLHDBC>(any);
assertTrue (SQL_NULL_HDBC != hdbc);
SQLRETURN rc = SQLDisconnect(hdbc);
assertTrue (!Utility::isError(rc));
assertTrue (!s.isConnected());
s.open();
assertTrue (s.isConnected());
hdbc = Poco::AnyCast<SQLHDBC>(any);
rc = SQLDisconnect(hdbc);
assertTrue (!Utility::isError(rc));
assertTrue (!s.isConnected());
s.reconnect();
assertTrue (s.isConnected());
s.close();
assertTrue (!s.isConnected());
s.reconnect();
assertTrue (s.isConnected());
s.reconnect();
assertTrue (s.isConnected());
}
void SQLExecutor::sessionPool(const std::string& connectString, int minSessions, int maxSessions, int idleTime, int timeout)
{
assertTrue (minSessions <= maxSessions);
SessionPool sp("ODBC", connectString, minSessions, maxSessions, idleTime, timeout);
assertEqual (0, sp.allocated());
assertEqual (maxSessions, sp.available());
Session s1 = sp.get();
assertEqual (minSessions, sp.allocated());
assertEqual (maxSessions-1, sp.available());
s1 = sp.get();
assertEqual (2, sp.allocated());
assertEqual (maxSessions-1, sp.available());
{
Session s2 = sp.get();
assertEqual (2, sp.allocated());
assertEqual (maxSessions-2, sp.available());
}
assertEqual (2, sp.allocated());
assertEqual (maxSessions-1, sp.available());
Thread::sleep(idleTime + 500);
assertEqual (maxSessions-minSessions, sp.available());
try
{
sp.setFeature("autoBind", true);
fail("SessionPool must throw on setFeature after the first session was created.");
}
catch(const Poco::InvalidAccessException&) {}
try
{
sp.setProperty("handle", SQL_NULL_HDBC);
fail("SessionPool must throw on setProperty after the first session was created.");
}
catch(const Poco::InvalidAccessException&) {}
std::vector<Session> sessions;
for (int i = 0; i < maxSessions-minSessions; ++i)
{
sessions.push_back(sp.get());
}
try
{
Session s = sp.get();
fail("SessionPool must throw when no sesions available.");
}
catch(const Poco::Data::SessionPoolExhaustedException&) {}
sp.shutdown();
try
{
Session s = sp.get();
fail("SessionPool that was shut down must throw on get.");
}
catch(const Poco::InvalidAccessException&) {}
{
SessionPool pool("ODBC", connectString, 1, 4, 2, 10);
pool.setFeature("f1", true);
assertTrue (pool.getFeature("f1"));
try { pool.getFeature("g1"); fail ("must fail"); }
catch ( Poco::NotFoundException& ) { }
pool.setProperty("p1", 1);
assertTrue (1 == Poco::AnyCast<int>(pool.getProperty("p1")));
try { pool.getProperty("r1"); fail ("must fail"); }
catch ( Poco::NotFoundException& ) { }
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 0);
assertTrue (pool.idle() == 0);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 4);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
Session s1(pool.get());
assertTrue (s1.getFeature("f1"));
assertTrue (1 == Poco::AnyCast<int>(s1.getProperty("p1")));
try { pool.setFeature("f1", true); fail ("must fail"); }
catch ( Poco::InvalidAccessException& ) { }
try { pool.setProperty("p1", 1); fail ("must fail"); }
catch ( Poco::InvalidAccessException& ) { }
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 1);
assertTrue (pool.idle() == 0);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 3);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
Session s2(pool.get("f1", false));
assertTrue (!s2.getFeature("f1"));
assertTrue (1 == Poco::AnyCast<int>(s2.getProperty("p1")));
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 2);
assertTrue (pool.idle() == 0);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 2);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
{
Session s3(pool.get("p1", 2));
assertTrue (s3.getFeature("f1"));
assertTrue (2 == Poco::AnyCast<int>(s3.getProperty("p1")));
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 3);
assertTrue (pool.idle() == 0);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 1);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
}
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 3);
assertTrue (pool.idle() == 1);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 2);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
Session s4(pool.get());
assertTrue (s4.getFeature("f1"));
assertTrue (1 == Poco::AnyCast<int>(s4.getProperty("p1")));
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 3);
assertTrue (pool.idle() == 0);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 1);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
Session s5(pool.get());
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 4);
assertTrue (pool.idle() == 0);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 0);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
try
{
Session s6(pool.get());
fail("pool exhausted - must throw");
}
catch (Poco::Data::SessionPoolExhaustedException&) { }
s5.close();
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 4);
assertTrue (pool.idle() == 1);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 1);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
try
{
s5 << "DROP TABLE IF EXISTS Test", now;
fail("session unusable - must throw");
}
catch (Poco::Data::SessionUnavailableException&) { }
s4.close();
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 4);
assertTrue (pool.idle() == 2);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 2);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
Thread::sleep(5000); // time to clean up idle sessions
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 2);
assertTrue (pool.idle() == 0);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 2);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
Session s6(pool.get());
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 3);
assertTrue (pool.idle() == 0);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 1);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
s6.setFeature("connected", false);
assertTrue (pool.dead() == 1);
s6.close();
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 2);
assertTrue (pool.idle() == 0);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 2);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
assertTrue (pool.isActive());
pool.shutdown();
assertTrue (!pool.isActive());
try
{
Session s7(pool.get());
fail("pool shut down - must throw");
}
catch (InvalidAccessException&) { }
assertTrue (pool.capacity() == 4);
assertTrue (pool.allocated() == 0);
assertTrue (pool.idle() == 0);
assertTrue (pool.connTimeout() == 10);
assertTrue (pool.available() == 0);
assertTrue (pool.dead() == 0);
assertTrue (pool.allocated() == pool.used() + pool.idle());
}
}
void SQLExecutor::zeroRows()
{
Statement stmt = (session() << "SELECT * FROM Person WHERE 0 = 1");
@ -2470,6 +2975,106 @@ void SQLExecutor::blobStmt()
}
void SQLExecutor::recordSet()
{
std::string funct = "dateTime()";
std::string lastName("lastname");
std::string firstName("firstname");
std::string address("Address");
DateTime born(1965, 6, 18, 5, 35, 1);
DateTime born2(1991, 6, 18, 14, 35, 1);
try
{
{
Statement stmt = (session() << "SELECT COUNT(*) AS row_count FROM Person", now);
RecordSet rset(stmt);
assertTrue (rset.rowCount() == 1);
assertTrue (rset["row_count"].convert<int>() == 0);
}
{
Statement stmt = (session() <<
"INSERT INTO Person VALUES (?,?,?,?)",
use(lastName), use(firstName), use(address), use(born), now);
RecordSet rset(stmt);
assertTrue (rset.rowCount() == 0);
assertTrue (rset.affectedRowCount() == 1);
}
{
Statement stmt = (session() << "SELECT COUNT(*) AS row_count FROM Person", now);
RecordSet rset(stmt);
assertTrue (rset.rowCount() == 1);
assertTrue (rset["row_count"].convert<int>() == 1);
}
{
Statement stmt = (session() << "SELECT Born FROM Person", now);
RecordSet rset(stmt);
assertTrue (rset.rowCount() == 1);
assertTrue (rset["Born"].convert<DateTime>() == born);
}
{
Statement stmt = (session() <<
"DELETE FROM Person WHERE born = ?", use(born), now);
RecordSet rset(stmt);
assertTrue (rset.rowCount() == 0);
assertTrue (rset.affectedRowCount() == 1);
}
{
Statement stmt = (session() <<
"INSERT INTO Person VALUES (?,?,?,?)",
use(lastName), use(firstName), use(address), use(born), now);
RecordSet rset(stmt);
assertTrue (rset.rowCount() == 0);
assertTrue (rset.affectedRowCount() == 1);
}
{
Statement stmt = (session() <<
"INSERT INTO Person VALUES (?,?,?,?)",
use(lastName), use(firstName), use(address), use(born2), now);
RecordSet rset(stmt);
assertTrue (rset.rowCount() == 0);
assertTrue (rset.affectedRowCount() == 1);
}
{
Statement stmt = (session() << "SELECT COUNT(*) AS row_count FROM Person", now);
RecordSet rset(stmt);
assertTrue (rset.rowCount() == 1);
assertTrue (rset["row_count"].convert<int>() == 2);
}
{
Statement stmt = (session() << "SELECT Born FROM Person ORDER BY Born DESC", now);
RecordSet rset(stmt);
assertTrue (rset.rowCount() == 2);
assertTrue (rset["Born"].convert<DateTime>() == born2);
rset.moveNext();
assertTrue (rset["Born"].convert<DateTime>() == born);
rset.moveFirst();
assertTrue (rset["Born"].convert<DateTime>() == born2);
rset.moveLast();
assertTrue (rset["Born"].convert<DateTime>() == born);
}
{
Statement stmt = (session() << "DELETE FROM Person", now);
RecordSet rset(stmt);
assertTrue (rset.rowCount() == 0);
assertTrue (rset.affectedRowCount() == 2);
}
}
catch (ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail(funct); }
catch (StatementException& se){ std::cout << se.toString() << std::endl; fail(funct); }
}
void SQLExecutor::dateTime()
{
std::string funct = "dateTime()";
@ -3457,22 +4062,35 @@ void SQLExecutor::sqlChannel(const std::string& connect)
try
{
AutoPtr<SQLChannel> pChannel = new SQLChannel(Poco::Data::ODBC::Connector::KEY, connect, "TestSQLChannel");
Stopwatch sw; sw.start();
while (!pChannel->isRunning())
{
Thread::sleep(10);
if (sw.elapsedSeconds() > 3)
fail ("SQLExecutor::sqlLogger(): SQLChannel timed out");
}
pChannel->setProperty("bulk", "true");
pChannel->setProperty("keep", "2 seconds");
Message msgInf("InformationSource", "a Informational async message", Message::PRIO_INFORMATION);
pChannel->log(msgInf);
while (pChannel->logged() != 1) Thread::sleep(10);
Message msgWarn("WarningSource", "b Warning async message", Message::PRIO_WARNING);
pChannel->log(msgWarn);
pChannel->wait();
while (pChannel->logged() != 2) Thread::sleep(10);
pChannel->setProperty("async", "false");
Message msgInfS("InformationSource", "c Informational sync message", Message::PRIO_INFORMATION);
pChannel->log(msgInfS);
while (pChannel->logged() != 3) Thread::sleep(10);
Message msgWarnS("WarningSource", "d Warning sync message", Message::PRIO_WARNING);
pChannel->log(msgWarnS);
while (pChannel->logged() != 4) Thread::sleep(10);
RecordSet rs(session(), "SELECT * FROM T_POCO_LOG ORDER by Text");
assertTrue (4 == rs.rowCount());
size_t rc = rs.rowCount();
assertTrue (4 == rc);
assertTrue ("InformationSource" == rs["Source"]);
assertTrue ("a Informational async message" == rs["Text"]);
rs.moveNext();
@ -3485,12 +4103,14 @@ void SQLExecutor::sqlChannel(const std::string& connect)
assertTrue ("WarningSource" == rs["Source"]);
assertTrue ("d Warning sync message" == rs["Text"]);
Thread::sleep(3000);
Thread::sleep(3000); // give it time to archive
Message msgInfA("InformationSource", "e Informational sync message", Message::PRIO_INFORMATION);
pChannel->log(msgInfA);
while (pChannel->logged() != 5) Thread::sleep(10);
Message msgWarnA("WarningSource", "f Warning sync message", Message::PRIO_WARNING);
pChannel->log(msgWarnA);
while (pChannel->logged() != 6) Thread::sleep(10);
RecordSet rs1(session(), "SELECT * FROM T_POCO_LOG_ARCHIVE");
assertTrue (4 == rs1.rowCount());
@ -3504,7 +4124,6 @@ void SQLExecutor::sqlChannel(const std::string& connect)
rs2.moveNext();
assertTrue ("WarningSource" == rs2["Source"]);
assertTrue ("f Warning sync message" == rs2["Text"]);
}
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("sqlChannel()"); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("sqlChannel()"); }
@ -3516,14 +4135,23 @@ void SQLExecutor::sqlLogger(const std::string& connect)
try
{
Logger& root = Logger::root();
root.setChannel(new SQLChannel(Poco::Data::ODBC::Connector::KEY, connect, "TestSQLChannel"));
SQLChannel* pSQLChannel = new SQLChannel(Poco::Data::ODBC::Connector::KEY, connect, "TestSQLChannel");
Stopwatch sw; sw.start();
while (!pSQLChannel->isRunning())
{
Thread::sleep(10);
if (sw.elapsedSeconds() > 3)
fail ("SQLExecutor::sqlLogger(): SQLChannel timed out");
}
root.setChannel(pSQLChannel);
root.setLevel(Message::PRIO_INFORMATION);
root.information("a Informational message");
root.warning("b Warning message");
root.debug("Debug message");
Thread::sleep(100);
while (pSQLChannel->logged() != 2) Thread::sleep(100);
RecordSet rs(session(), "SELECT * FROM T_POCO_LOG ORDER by Text");
assertTrue (2 == rs.rowCount());
assertTrue ("TestSQLChannel" == rs["Source"]);
@ -3531,7 +4159,6 @@ void SQLExecutor::sqlLogger(const std::string& connect)
rs.moveNext();
assertTrue ("TestSQLChannel" == rs["Source"]);
assertTrue ("b Warning message" == rs["Text"]);
root.setChannel(nullptr);
}
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("sqlLogger()"); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("sqlLogger()"); }
@ -3852,7 +4479,7 @@ struct TestRollbackTransactor
void SQLExecutor::transactor()
{
std::string funct = "transaction()";
std::string funct = "transactor()";
int count = 0;
bool autoCommit = session().getFeature("autoCommit");

View File

@ -48,7 +48,7 @@
if (!SQL_SUCCEEDED(r)) \
{ \
Poco::Data::ODBC::StatementException se(h); \
std::cout << se.toString() << std::endl; \
std::cout << se.displayText() << std::endl; \
} \
assert (SQL_SUCCEEDED(r))
@ -57,7 +57,7 @@
if (!SQL_SUCCEEDED(r)) \
{ \
Poco::Data::ODBC::DescriptorException de(h); \
std::cout << de.toString() << std::endl; \
std::cout << de.displayText() << std::endl; \
} \
assert (SQL_SUCCEEDED(r))
@ -106,13 +106,24 @@ public:
SQLExecutor::DataExtraction extractMode,
const std::string& insert = MULTI_INSERT,
const std::string& select = MULTI_SELECT);
/// The above two functions use "bare bone" ODBC API calls
void bareboneODBCStoredFuncTest(const std::string& dbConnString,
const std::string& tableCreateString,
const std::string& procExecuteString,
SQLExecutor::DataBinding bindMode,
SQLExecutor::DataExtraction extractMode);
/// The above three functions use "bare bone" ODBC API calls
/// (i.e. calls are not "wrapped" in PocoData framework structures).
/// The purpose of the functions is to verify that a driver behaves
/// correctly as well as to determine its capabilities
/// (e.g. SQLGetData() restrictions relaxation policy, if any).
/// If these test pass, subsequent tests failures are likely ours.
void connection(const std::string& connectString);
void session(const std::string& connectString, int timeout);
void sessionPool(const std::string& connectString,
int minSessions, int maxSessions, int idleTime, int timeout);
void zeroRows();
void simpleAccess();
void complexType();
@ -463,6 +474,7 @@ public:
}
void blobStmt();
void recordSet();
void dateTime();
void date();

View File

@ -257,11 +257,6 @@ Poco::Any SessionImpl::getTransactionType(const std::string& prop) const
void SessionImpl::autoCommit(const std::string&, bool)
{
// The problem here is to decide whether to call commit or rollback
// when autocommit is set to true. Hence, it is best not to implement
// this explicit call and only implicitly support autocommit setting.
throw NotImplementedException(
"SQLite autocommit is implicit with begin/commit/rollback.");
}

View File

@ -138,6 +138,7 @@ Utility::Utility()
_types.insert(TypeMap::value_type("TIMESTAMP", MetaColumn::FDT_TIMESTAMP));
_types.insert(TypeMap::value_type("UUID", MetaColumn::FDT_UUID));
_types.insert(TypeMap::value_type("GUID", MetaColumn::FDT_UUID));
_types.insert(TypeMap::value_type("JSON", MetaColumn::FDT_JSON));
}
}

View File

@ -89,6 +89,7 @@ using Poco::Int64;
using Poco::Dynamic::Var;
using Poco::Data::SQLite::Utility;
using Poco::delegate;
using Poco::Stopwatch;
class Person
@ -1408,7 +1409,7 @@ void SQLiteTest::testNonexistingDB()
Session tmp (Poco::Data::SQLite::Connector::KEY, "foo/bar/nonexisting.db", 1);
fail("non-existing DB must throw");
}
catch(ConnectionFailedException& ex)
catch(ConnectionFailedException&)
{
return;
}
@ -2474,53 +2475,68 @@ void SQLiteTest::testSQLChannel()
"DateTime DATE)", now;
AutoPtr<SQLChannel> pChannel = new SQLChannel(Poco::Data::SQLite::Connector::KEY, "dummy.db", "TestSQLChannel");
Stopwatch sw; sw.start();
while (!pChannel->isRunning())
{
Thread::sleep(10);
if (sw.elapsedSeconds() > 3)
fail ("SQLExecutor::sqlLogger(): SQLChannel timed out");
}
// bulk binding mode is not suported by SQLite, but SQLChannel should handle it internally
pChannel->setProperty("bulk", "true");
pChannel->setProperty("keep", "2 seconds");
Message msgInf("InformationSource", "a Informational async message", Message::PRIO_INFORMATION);
pChannel->log(msgInf);
while (pChannel->logged() != 1) Thread::sleep(100);
Message msgWarn("WarningSource", "b Warning async message", Message::PRIO_WARNING);
pChannel->log(msgWarn);
pChannel->wait();
while (pChannel->logged() != 2) Thread::sleep(10);
pChannel->setProperty("async", "false");
Message msgInfS("InformationSource", "c Informational sync message", Message::PRIO_INFORMATION);
pChannel->log(msgInfS);
while (pChannel->logged() != 3) Thread::sleep(10);
Message msgWarnS("WarningSource", "d Warning sync message", Message::PRIO_WARNING);
pChannel->log(msgWarnS);
while (pChannel->logged() != 4) Thread::sleep(10);
RecordSet rs(tmp, "SELECT * FROM T_POCO_LOG ORDER by Text");
assertTrue (4 == rs.rowCount());
assertTrue ("InformationSource" == rs["Source"]);
assertTrue ("a Informational async message" == rs["Text"]);
size_t rc = rs.rowCount();
assertTrue(4 == rc);
assertTrue("InformationSource" == rs["Source"]);
assertTrue("a Informational async message" == rs["Text"]);
rs.moveNext();
assertTrue ("WarningSource" == rs["Source"]);
assertTrue ("b Warning async message" == rs["Text"]);
assertTrue("WarningSource" == rs["Source"]);
assertTrue("b Warning async message" == rs["Text"]);
rs.moveNext();
assertTrue ("InformationSource" == rs["Source"]);
assertTrue ("c Informational sync message" == rs["Text"]);
assertTrue("InformationSource" == rs["Source"]);
assertTrue("c Informational sync message" == rs["Text"]);
rs.moveNext();
assertTrue ("WarningSource" == rs["Source"]);
assertTrue ("d Warning sync message" == rs["Text"]);
assertTrue("WarningSource" == rs["Source"]);
assertTrue("d Warning sync message" == rs["Text"]);
Thread::sleep(3000);
Thread::sleep(3000); // give it time to archive
Message msgInfA("InformationSource", "e Informational sync message", Message::PRIO_INFORMATION);
pChannel->log(msgInfA);
while (pChannel->logged() != 5) Thread::sleep(10);
Message msgWarnA("WarningSource", "f Warning sync message", Message::PRIO_WARNING);
pChannel->log(msgWarnA);
while (pChannel->logged() != 6) Thread::sleep(10);
RecordSet rs1(tmp, "SELECT * FROM T_POCO_LOG_ARCHIVE");
assertTrue (4 == rs1.rowCount());
assertTrue(4 == rs1.rowCount());
pChannel->setProperty("keep", "");
assertTrue ("forever" == pChannel->getProperty("keep"));
assertTrue("forever" == pChannel->getProperty("keep"));
RecordSet rs2(tmp, "SELECT * FROM T_POCO_LOG ORDER by Text");
assertTrue (2 == rs2.rowCount());
assertTrue ("InformationSource" == rs2["Source"]);
assertTrue ("e Informational sync message" == rs2["Text"]);
assertTrue(2 == rs2.rowCount());
assertTrue("InformationSource" == rs2["Source"]);
assertTrue("e Informational sync message" == rs2["Text"]);
rs2.moveNext();
assertTrue ("WarningSource" == rs2["Source"]);
assertTrue ("f Warning sync message" == rs2["Text"]);
assertTrue("WarningSource" == rs2["Source"]);
assertTrue("f Warning sync message" == rs2["Text"]);
}
@ -2537,25 +2553,30 @@ void SQLiteTest::testSQLLogger()
"Text VARCHAR,"
"DateTime DATE)", now;
Logger& root = Logger::root();
AutoPtr<SQLChannel> pSQLChannel = new SQLChannel(Poco::Data::SQLite::Connector::KEY, "dummy.db", "TestSQLChannel");
Stopwatch sw; sw.start();
while (!pSQLChannel->isRunning())
{
AutoPtr<SQLChannel> pChannel = new SQLChannel(Poco::Data::SQLite::Connector::KEY, "dummy.db", "TestSQLChannel");
Logger& root = Logger::root();
root.setChannel(pChannel);
root.setLevel(Message::PRIO_INFORMATION);
root.information("Informational message");
root.warning("Warning message");
root.debug("Debug message");
Thread::sleep(10);
if (sw.elapsedSeconds() > 3)
fail ("SQLExecutor::sqlLogger(): SQLChannel timed out");
}
root.setChannel(pSQLChannel);
root.setLevel(Message::PRIO_INFORMATION);
Thread::sleep(100);
RecordSet rs(tmp, "SELECT * FROM T_POCO_LOG ORDER by DateTime");
assertTrue (2 == rs.rowCount());
assertTrue ("TestSQLChannel" == rs["Source"]);
assertTrue ("Informational message" == rs["Text"]);
root.information("a Informational message");
root.warning("b Warning message");
root.debug("Debug message");
while (pSQLChannel->logged() != 2) Thread::sleep(100);
RecordSet rs(tmp, "SELECT * FROM T_POCO_LOG ORDER by Text");
assertTrue(2 == rs.rowCount());
assertTrue("TestSQLChannel" == rs["Source"]);
assertTrue("a Informational message" == rs["Text"]);
rs.moveNext();
assertTrue ("TestSQLChannel" == rs["Source"]);
assertTrue ("Warning message" == rs["Text"]);
assertTrue("TestSQLChannel" == rs["Source"]);
assertTrue("b Warning message" == rs["Text"]);
}
@ -3130,12 +3151,6 @@ void SQLiteTest::testSessionTransaction()
Session local (Poco::Data::SQLite::Connector::KEY, "dummy.db");
assertTrue (local.isConnected());
try
{
local.setFeature("autoCommit", true);
fail ("Setting SQLite auto-commit explicitly must fail!");
}
catch (NotImplementedException&) { }
assertTrue (local.getFeature("autoCommit"));
std::string funct = "transaction()";
@ -3235,12 +3250,16 @@ void SQLiteTest::testTransaction()
std::string tableName("Person");
lastNames.push_back("LN1");
lastNames.push_back("LN2");
lastNames.push_back("LN3");
firstNames.push_back("FN1");
firstNames.push_back("FN2");
firstNames.push_back("FN3");
addresses.push_back("ADDR1");
addresses.push_back("ADDR2");
addresses.push_back("ADDR3");
ages.push_back(1);
ages.push_back(2);
ages.push_back(3);
int count = 0, locCount = 0;
std::string result;
@ -3258,7 +3277,7 @@ void SQLiteTest::testTransaction()
assertTrue (trans.isActive());
session << "SELECT COUNT(*) FROM Person", into(count), now;
assertTrue (2 == count);
assertTrue (3 == count);
assertTrue (session.isTransaction());
assertTrue (trans.isActive());
// no explicit commit, so transaction RAII must roll back here
@ -3285,9 +3304,9 @@ void SQLiteTest::testTransaction()
}
session << "SELECT count(*) FROM Person", into(count), now;
assertTrue (2 == count);
assertTrue (3 == count);
local << "SELECT count(*) FROM Person", into(count), now;
assertTrue (2 == count);
assertTrue (3 == count);
session << "DELETE FROM Person", now;
@ -3314,7 +3333,8 @@ void SQLiteTest::testTransaction()
session << "SELECT count(*) FROM Person", into(count), now;
assertTrue (0 == count);
trans.execute(sql);
bool status = trans.execute(sql);
assertTrue (status);
Statement stmt3 = (local << "SELECT COUNT(*) FROM Person", into(locCount), now);
assertTrue (2 == locCount);
@ -3322,6 +3342,21 @@ void SQLiteTest::testTransaction()
session << "SELECT count(*) FROM Person", into(count), now;
assertTrue (2 == count);
session << "DELETE FROM Person", now;
std::string sql3 = format("INSERT INTO Pers VALUES ('%s','%s','%s',%d)", lastNames[2], firstNames[2], addresses[2], ages[2]);
// Table name is misspelled, should cause transaction rollback
sql.push_back(sql3);
std::string info;
status = trans.execute(sql, &info);
assertFalse (status);
assertEqual (info, "Invalid SQL statement: no such table: Pers: no such table: Pers");
session << "SELECT count(*) FROM Person", into(count), now;
assertTrue (0 == count);
session.close();
assertTrue (!session.isConnected());

View File

@ -115,6 +115,16 @@ public:
{
}
bool hasFeature(const std::string& name)
/// Looks a feature up in the features map
/// and returns true if there is one.
{
auto it = _features.find(name);
return it != _features.end() &&
it->second.getter &&
it->second.setter;
}
void setFeature(const std::string& name, bool state)
/// Looks a feature up in the features map
/// and calls the feature's setter, if there is one.
@ -145,6 +155,16 @@ public:
else throw NotSupportedException(name);
}
bool hasProperty(const std::string& name)
/// Looks a property up in the properties map
/// and returns true if there is one.
{
auto it = _properties.find(name);
return it != _properties.end() &&
it->second.getter &&
it->second.setter;
}
void setProperty(const std::string& name, const Poco::Any& value)
/// Looks a property up in the properties map
/// and calls the property's setter, if there is one.

View File

@ -180,7 +180,8 @@ public:
ArchiveByAgeStrategy(const std::string& connector,
const std::string& connect,
const std::string& sourceTable,
const std::string& destinationTable = DEFAULT_ARCHIVE_DESTINATION);
const std::string& destinationTable = DEFAULT_ARCHIVE_DESTINATION,
const std::string& age = "");
~ArchiveByAgeStrategy();

View File

@ -62,8 +62,10 @@ public:
bool hasTransactionIsolation(Poco::UInt32) const;
bool isTransactionIsolation(Poco::UInt32) const;
const std::string& connectorName() const;
bool hasFeature(const std::string& name);
void setFeature(const std::string& name, bool state);
bool getFeature(const std::string& name);
bool hasProperty(const std::string& name);
void setProperty(const std::string& name, const Poco::Any& value);
Poco::Any getProperty(const std::string& name);

View File

@ -50,7 +50,8 @@ public:
void prepare()
/// Prepares data.
{
TypeHandler<T>::prepare(_pos, _val, preparation());
auto pPrep = preparation();
TypeHandler<T>::prepare(_pos, _val, pPrep);
}
private:

View File

@ -133,6 +133,9 @@ public:
/// for large recordsets, so it should be used judiciously.
/// Use totalRowCount() to obtain the total number of rows.
std::size_t affectedRowCount() const;
/// Returns the number of rows affected by the statement execution.
std::size_t extractedRowCount() const;
/// Returns the number of rows extracted during the last statement
/// execution.

View File

@ -22,18 +22,24 @@
#include "Poco/Data/Connector.h"
#include "Poco/Data/Session.h"
#include "Poco/Data/Statement.h"
#include "Poco/Logger.h"
#include "Poco/Data/ArchiveStrategy.h"
#include "Poco/Channel.h"
#include "Poco/FileChannel.h"
#include "Poco/Message.h"
#include "Poco/AutoPtr.h"
#include "Poco/String.h"
#include "Poco/NotificationQueue.h"
#include "Poco/Thread.h"
#include "Poco/Mutex.h"
#include <atomic>
namespace Poco {
namespace Data {
class Data_API SQLChannel: public Poco::Channel
class Data_API SQLChannel: public Poco::Channel, Poco::Runnable
/// This Channel implements logging to a SQL database.
/// The channel is dependent on the schema. The DDL for
/// table creation (subject to target DDL dialect dependent
@ -61,23 +67,54 @@ class Data_API SQLChannel: public Poco::Channel
/// a risk of long blocking periods in case of remote server communication delays.
{
public:
using Ptr = Poco::AutoPtr<SQLChannel>;
class LogNotification : public Poco::Notification
{
public:
using Ptr = Poco::AutoPtr<LogNotification>;
LogNotification(const Poco::Message& message) :
_message(message)
{
}
const Poco::Message& message() const
{
return _message;
}
private:
Poco::Message _message;
};
static const int DEFAULT_MIN_BATCH_SIZE = 1;
static const int DEFAULT_MAX_BATCH_SIZE = 1000;
SQLChannel();
/// Creates SQLChannel.
SQLChannel(const std::string& connector,
const std::string& connect,
const std::string& name = "-");
/// Creates a SQLChannel with the given connector, connect string, timeout, table and name.
const std::string& name = "-",
const std::string& table = "T_POCO_LOG",
int timeout = 1000,
int minBatch = DEFAULT_MIN_BATCH_SIZE,
int maxBatch = DEFAULT_MAX_BATCH_SIZE);
/// Creates an SQLChannel with the given connector, connect string, timeout, table and name.
/// The connector must be already registered.
void open();
/// Opens the SQLChannel.
/// Returns true if succesful.
void close();
void close(int ms = 0);
/// Closes the SQLChannel.
void run();
/// Dequeues and sends the logs to the DB.
bool isRunning() const;
/// Returns true if the logging thread is running.
void log(const Message& msg);
/// Writes the log message to the database.
@ -108,6 +145,8 @@ public:
/// the target, the previous operation must have been either completed
/// or timed out (see timeout and throw properties for details on
/// how abnormal conditos are handled).
/// This property is deprecated and has no effect - all logging
/// is asynchronous since the 1.13.0. release.
///
/// * timeout: Timeout (ms) to wait for previous log operation completion.
/// Values "0" and "" mean no timeout. Only valid when logging
@ -117,14 +156,35 @@ public:
/// Setting this property to false may result in log entries being lost.
/// True values are (case insensitive) "true", "t", "yes", "y".
/// Anything else yields false.
///
/// * minBatch: Minimal number of log entries to accumulate before actually sending
/// logs to the destination.
/// Defaults to 1 entry (for compatibility with older versions);
/// can't be zero or larger than `maxBatch`.
///
/// * maxBatch: Maximum number of log entries to accumulate. When the log queue
/// reaches this size, log entries are silently discarded.
/// Defaults to 100, can't be zero or larger than 1000.
///
/// * bulk: Do bulk execute (on most DBMS systems, this can speed up things
/// drastically).
///
/// * file Destination file name for the backup FileChannel, used when DB
/// connection is not present to log not executed SQL statements.
std::string getProperty(const std::string& name) const;
/// Returns the value of the property with the given name.
std::size_t wait();
void stop();
/// Stops and joins the logging thread..
std::size_t wait(int ms = 1000);
/// Waits for the completion of the previous operation and returns
/// the result. If chanel is in synchronous mode, returns 0 immediately.
size_t logged() const;
/// Returns the number of logged entries.
static void registerChannel();
/// Registers the channel with the global LoggingFactory.
@ -136,56 +196,84 @@ public:
static const std::string PROP_MAX_AGE;
static const std::string PROP_ASYNC;
static const std::string PROP_TIMEOUT;
static const std::string PROP_MIN_BATCH;
static const std::string PROP_MAX_BATCH;
static const std::string PROP_BULK;
static const std::string PROP_THROW;
static const std::string PROP_FILE;
protected:
~SQLChannel();
private:
using SessionPtr = Poco::SharedPtr<Session>;
using StatementPtr = Poco::SharedPtr<Statement>;
using Priority = Poco::Message::Priority;
using StrategyPtr = Poco::SharedPtr<ArchiveStrategy>;
static const std::string SQL_INSERT_STMT;
void initLogStatement();
/// Initiallizes the log statement.
typedef Poco::SharedPtr<Session> SessionPtr;
typedef Poco::SharedPtr<Statement> StatementPtr;
typedef Poco::Message::Priority Priority;
typedef Poco::SharedPtr<ArchiveStrategy> StrategyPtr;
void initArchiveStatements();
/// Initiallizes the archive statement.
void reconnect();
/// Closes and opens the DB connection.
void logAsync(const Message& msg);
/// Waits for previous operation completion and
/// calls logSync(). If the previous operation times out,
/// and _throw is true, TimeoutException is thrown, oterwise
/// the timeout is ignored and log entry is lost.
bool processOne(int minBatch = 0);
/// Processes one message.
/// If the number of acummulated messages is greater
/// than minBatch, sends logs to the destination.
/// Returns true if log entry was processed.
void logSync(const Message& msg);
/// Inserts the message in the target database.
size_t execSQL();
/// Executes the log statement.
size_t logSync();
/// Inserts entries into the target database.
bool isTrue(const std::string& value) const;
/// Returns true is value is "true", "t", "yes" or "y".
/// Case insensitive.
std::string _connector;
std::string _connect;
SessionPtr _pSession;
StatementPtr _pLogStatement;
std::string _name;
std::string _table;
int _timeout;
bool _throw;
bool _async;
size_t logTofile(AutoPtr<FileChannel>& pFileChannel, const std::string& fileName, bool clear = false);
/// Logs cached entries to a file. Called in case DB insertions fail.
// members for log entry cache (needed for async mode)
std::string _source;
long _pid;
std::string _thread;
long _tid;
int _priority;
std::string _text;
DateTime _dateTime;
std::string maskPwd();
/// Masks the password in the connection
/// string, if detected. This is not a
/// bullet-proof method; if not succesful,
/// empty string is returned.
StrategyPtr _pArchiveStrategy;
mutable Poco::FastMutex _mutex;
std::string _connector;
std::string _connect;
SessionPtr _pSession;
std::string _sql;
std::string _name;
std::string _table;
bool _tableChanged;
int _timeout;
std::atomic<int> _minBatch;
int _maxBatch;
bool _bulk;
std::atomic<bool> _throw;
// members for log entry cache
std::vector<std::string> _source;
std::vector<long> _pid;
std::vector<std::string> _thread;
std::vector<long> _tid;
std::vector<int> _priority;
std::vector<std::string> _text;
std::vector<DateTime> _dateTime;
Poco::NotificationQueue _logQueue;
std::unique_ptr<Poco::Thread> _pDBThread;
std::atomic<bool> _reconnect;
std::atomic<bool> _running;
std::atomic<bool> _stop;
std::atomic<size_t> _logged;
StrategyPtr _pArchiveStrategy;
std::string _file;
AutoPtr<FileChannel> _pFileChannel;
Poco::Logger& _logger = Poco::Logger::get("SQLChannel");
};
@ -193,14 +281,6 @@ private:
// inlines
//
inline std::size_t SQLChannel::wait()
{
if (_async && _pLogStatement)
return _pLogStatement->wait(_timeout);
return 0;
}
inline bool SQLChannel::isTrue(const std::string& value) const
{
@ -211,6 +291,18 @@ inline bool SQLChannel::isTrue(const std::string& value) const
}
inline bool SQLChannel::isRunning() const
{
return _running;
}
inline size_t SQLChannel::logged() const
{
return _logged;
}
} } // namespace Poco::Data

View File

@ -273,6 +273,9 @@ public:
/// Utility function that teturns the URI formatted from supplied
/// arguments as "connector:///connectionString".
bool hasFeature(const std::string& name);
/// Returns true if session has the named feature.
void setFeature(const std::string& name, bool state);
/// Set the state of a feature.
///
@ -291,6 +294,9 @@ public:
/// Throws a NotSupportedException if the requested feature is
/// not supported by the underlying implementation.
bool hasProperty(const std::string& name);
/// Returns true if session has the named property.
void setProperty(const std::string& name, const Poco::Any& value);
/// Set the value of a property.
///
@ -456,6 +462,12 @@ inline std::string Session::uri() const
}
inline bool Session::hasFeature(const std::string& name)
{
return _pImpl->hasFeature(name);
}
inline void Session::setFeature(const std::string& name, bool state)
{
_pImpl->setFeature(name, state);
@ -468,6 +480,11 @@ inline bool Session::getFeature(const std::string& name) const
}
inline bool Session::hasProperty(const std::string& name)
{
return _pImpl->hasProperty(name);
}
inline void Session::setProperty(const std::string& name, const Poco::Any& value)
{
_pImpl->setProperty(name, value);

View File

@ -53,6 +53,11 @@ public:
static const std::size_t CONNECTION_TIMEOUT_DEFAULT = CONNECTION_TIMEOUT_INFINITE;
/// Default connection/login timeout in seconds.
// ODBC only, otherwise no-op
static const int CURSOR_USE_ALWAYS = 0;
static const int CURSOR_USE_IF_NEEDED = 1;
static const int CURSOR_USE_NEVER = 2;
SessionImpl(const std::string& connectionString,
std::size_t timeout = LOGIN_TIMEOUT_DEFAULT);
/// Creates the SessionImpl.
@ -141,6 +146,9 @@ public:
std::string uri() const;
/// Returns the URI for this session.
virtual bool hasFeature(const std::string& name) = 0;
/// Returns true if session has the named feature.
virtual void setFeature(const std::string& name, bool state) = 0;
/// Set the state of a feature.
///
@ -159,6 +167,9 @@ public:
/// Throws a NotSupportedException if the requested feature is
/// not supported by the underlying implementation.
virtual bool hasProperty(const std::string& name) = 0;
/// Returns true if session has the named feature.
virtual void setProperty(const std::string& name, const Poco::Any& value) = 0;
/// Set the value of a property.
///

View File

@ -191,21 +191,21 @@ private:
void closeAll(SessionList& sessionList);
std::string _connector;
std::string _connectionString;
int _minSessions;
int _maxSessions;
int _idleTime;
int _connTimeout;
int _nSessions;
SessionList _idleSessions;
SessionList _activeSessions;
Poco::Timer _janitorTimer;
FeatureMap _featureMap;
PropertyMap _propertyMap;
bool _shutdown;
AddPropertyMap _addPropertyMap;
AddFeatureMap _addFeatureMap;
std::string _connector;
std::string _connectionString;
std::atomic<int> _minSessions;
std::atomic<int> _maxSessions;
std::atomic<int> _idleTime;
std::atomic<int> _connTimeout;
std::atomic<int> _nSessions;
SessionList _idleSessions;
SessionList _activeSessions;
Poco::Timer _janitorTimer;
FeatureMap _featureMap;
PropertyMap _propertyMap;
std::atomic<bool> _shutdown;
AddPropertyMap _addPropertyMap;
AddFeatureMap _addFeatureMap;
mutable
Poco::Mutex _mutex;

View File

@ -369,6 +369,10 @@ public:
/// Returns the number of rows extracted so far for the data set.
/// Default value indicates current data set (if any).
std::size_t affectedRowCount() const;
/// Returns the number of affected rows.
/// Used to find out the number of rows affected by insert, delete or update.
std::size_t extractionCount() const;
/// Returns the number of extraction storage buffers associated
/// with the current data set.
@ -709,6 +713,12 @@ inline void Statement::setStorage(const std::string& storage)
}
inline std::size_t Statement::affectedRowCount() const
{
return static_cast<std::size_t>(_pImpl->affectedRowCount());
}
inline std::size_t Statement::extractionCount() const
{
return _pImpl->extractionCount();

View File

@ -39,6 +39,9 @@ class Data_API Transaction
public:
Transaction(Poco::Data::Session& session, Poco::Logger* pLogger = 0);
/// Creates the Transaction and starts it, using the given database session and logger.
/// If `session` is in autocommit mode, it is switched to manual commit mode
/// for the duration of the transaction and reverted back to the original mode
/// after transaction completes.
Transaction(Poco::Data::Session& session, bool start);
/// Creates the Transaction, using the given database session.
@ -107,10 +110,18 @@ public:
/// Passing true value for commit disables rollback during destruction
/// of this Transaction object.
void execute(const std::vector<std::string>& sql);
bool execute(const std::vector<std::string>& sql);
/// Executes all the SQL statements supplied in the vector and, after the last
/// one is sucesfully executed, commits the transaction.
/// If an error occurs during execution, transaction is rolled back.
/// one is sucesfully executed, commits the transaction and returns true.
/// If an error occurs during execution, transaction is rolled back and false is returned.
/// Passing true value for commit disables rollback during destruction
/// of this Transaction object.
bool execute(const std::vector<std::string>& sql, std::string* info);
/// Executes all the SQL statements supplied in the vector and, after the last
/// one is sucesfully executed, commits the transaction and returns true.
/// If an error occurs during execution, transaction is rolled back false is returned
/// and info pointer is assigned to a std::string containing the error message.
/// Passing true value for commit disables rollback during destruction
/// of this Transaction object.
@ -148,7 +159,8 @@ private:
/// Otherwise does nothing.
Session _rSession;
Logger* _pLogger;
bool _autoCommit = false;
Logger* _pLogger = nullptr;
};

View File

@ -64,10 +64,12 @@ void ArchiveStrategy::open()
ArchiveByAgeStrategy::ArchiveByAgeStrategy(const std::string& connector,
const std::string& connect,
const std::string& sourceTable,
const std::string& destinationTable):
const std::string& destinationTable,
const std::string& age):
ArchiveStrategy(connector, connect, sourceTable, destinationTable)
{
initStatements();
if (!age.empty()) setThreshold(age);
}

View File

@ -166,6 +166,12 @@ const std::string& PooledSessionImpl::connectorName() const
}
bool PooledSessionImpl::hasFeature(const std::string& name)
{
return access()->hasFeature(name);
}
void PooledSessionImpl::setFeature(const std::string& name, bool state)
{
access()->setFeature(name, state);
@ -178,6 +184,12 @@ bool PooledSessionImpl::getFeature(const std::string& name)
}
bool PooledSessionImpl::hasProperty(const std::string& name)
{
return access()->hasProperty(name);
}
void PooledSessionImpl::setProperty(const std::string& name, const Poco::Any& value)
{
access()->setProperty(name, value);

View File

@ -244,7 +244,8 @@ Row& RecordSet::row(std::size_t pos)
std::size_t RecordSet::rowCount() const
{
poco_assert (extractions().size());
if (extractions().size() == 0) return 0;
std::size_t rc = subTotalRowCount();
if (!isFiltered()) return rc;
@ -258,6 +259,12 @@ std::size_t RecordSet::rowCount() const
}
std::size_t RecordSet::affectedRowCount() const
{
return Statement::affectedRowCount();
}
bool RecordSet::isAllowed(std::size_t row) const
{
if (!isFiltered()) return true;

View File

@ -14,12 +14,18 @@
#include "Poco/Data/SQLChannel.h"
#include "Poco/Data/SessionFactory.h"
#include "Poco/Data/BulkBinding.h"
#include "Poco/DateTime.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/LoggingFactory.h"
#include "Poco/Instantiator.h"
#include "Poco/NumberParser.h"
#include "Poco/NumberFormatter.h"
#include "Poco/Stopwatch.h"
#include "Poco/Format.h"
#include "Poco/File.h"
#include <fstream>
namespace Poco {
@ -37,37 +43,65 @@ const std::string SQLChannel::PROP_ARCHIVE_TABLE("archive");
const std::string SQLChannel::PROP_MAX_AGE("keep");
const std::string SQLChannel::PROP_ASYNC("async");
const std::string SQLChannel::PROP_TIMEOUT("timeout");
const std::string SQLChannel::PROP_MIN_BATCH("minBatch");
const std::string SQLChannel::PROP_MAX_BATCH("maxBatch");
const std::string SQLChannel::PROP_BULK("bulk");
const std::string SQLChannel::PROP_THROW("throw");
const std::string SQLChannel::PROP_FILE("file");
const std::string SQLChannel::SQL_INSERT_STMT = "INSERT INTO %s " \
"(Source, Name, ProcessId, Thread, ThreadId, Priority, Text, DateTime)" \
" VALUES %s";
SQLChannel::SQLChannel():
_name("-"),
_table("T_POCO_LOG"),
_tableChanged(true),
_timeout(1000),
_throw(true),
_async(true),
_minBatch(DEFAULT_MIN_BATCH_SIZE),
_maxBatch(DEFAULT_MAX_BATCH_SIZE),
_bulk(true),
_throw(false),
_pid(),
_tid(),
_priority()
_priority(),
_reconnect(false),
_running(false),
_stop(false),
_logged(0)
{
}
SQLChannel::SQLChannel(const std::string& connector,
const std::string& connect,
const std::string& name):
const std::string& name,
const std::string& table,
int timeout,
int minBatch,
int maxBatch) :
_connector(connector),
_connect(connect),
_name(name),
_table("T_POCO_LOG"),
_timeout(1000),
_throw(true),
_async(true),
_table(table),
_tableChanged(true),
_timeout(timeout),
_minBatch(minBatch),
_maxBatch(maxBatch),
_bulk(false),
_throw(false),
_pid(),
_tid(),
_priority()
_priority(),
_pDBThread(new Thread),
_reconnect(true),
_running(false),
_stop(false),
_logged(0)
{
open();
_pDBThread->start(*this);
}
@ -75,7 +109,11 @@ SQLChannel::~SQLChannel()
{
try
{
close();
stop();
close(_timeout);
wait();
if (_pFileChannel)
_pFileChannel->close();
}
catch (...)
{
@ -84,70 +122,177 @@ SQLChannel::~SQLChannel()
}
void SQLChannel::open()
std::string SQLChannel::maskPwd()
{
if (_connector.empty() || _connect.empty())
throw IllegalStateException("Connector and connect string must be non-empty.");
_pSession = new Session(_connector, _connect);
initLogStatement();
std::string displayConnect = _connect;
Poco::istring is1(displayConnect.c_str());
Poco::istring is2("pwd=");
std::size_t pos1 = Poco::isubstr(is1, is2);
if (pos1 == istring::npos)
{
is2 = "password=";
pos1 = Poco::isubstr(is1, is2);
}
if (pos1 != istring::npos)
{
pos1 += is2.length();
std::size_t pos2 = displayConnect.find(';', pos1);
if (pos2 != std::string::npos)
{
std::string toReplace = displayConnect.substr(pos1, pos2-pos1);
Poco::replaceInPlace(displayConnect, toReplace, std::string("***"));
}
else displayConnect.clear();
}
return displayConnect;
}
void SQLChannel::close()
void SQLChannel::open()
{
wait();
if (!_connector.empty() && !_connect.empty())
{
try
{
_pSession = new Session(_connector, _connect, _timeout / 1000);
if (_pSession->hasProperty("maxFieldSize")) _pSession->setProperty("maxFieldSize", 8192);
if (_pSession->hasProperty("autoBind")) _pSession->setFeature("autoBind", true);
_logger.information("Connected to %s: %s", _connector, maskPwd());
return;
}
catch (DataException& ex)
{
_logger.error(ex.displayText());
}
}
_pSession = nullptr;
return;
}
void SQLChannel::close(int ms)
{
wait(ms);
_pSession = nullptr;
}
void SQLChannel::log(const Message& msg)
{
if (_async) logAsync(msg);
else logSync(msg);
_logQueue.enqueueNotification(new LogNotification(msg));
}
void SQLChannel::logAsync(const Message& msg)
size_t SQLChannel::logSync()
{
poco_check_ptr (_pLogStatement);
if (0 == wait() && !_pLogStatement->done() && !_pLogStatement->initialized())
{
if (_throw)
throw TimeoutException("Timed out waiting for previous statement completion");
else return;
}
if (!_pSession || !_pSession->isConnected()) open();
logSync(msg);
}
void SQLChannel::logSync(const Message& msg)
{
if (_pArchiveStrategy) _pArchiveStrategy->archive();
_source = msg.getSource();
_pid = msg.getPid();
_thread = msg.getThread();
_tid = msg.getTid();
_priority = msg.getPriority();
_text = msg.getText();
_dateTime = msg.getTime();
if (_source.empty()) _source = _name;
try
{
_pLogStatement->execute();
return execSQL();
}
catch (Exception&)
{
if (_throw) throw;
}
return 0;
}
bool SQLChannel::processOne(int minBatch)
{
bool ret = false;
if (_logQueue.size())
{
Notification::Ptr pN = _logQueue.dequeueNotification();
LogNotification::Ptr pLN = pN.cast<LogNotification>();
if (pLN)
{
const Message& msg = pLN->message();
_source.push_back(msg.getSource());
if (_source.back().empty()) _source.back() = _name;
Poco::replaceInPlace(_source.back(), "'", "''");
_pid.push_back(msg.getPid());
_thread.push_back(msg.getThread());
Poco::replaceInPlace(_thread.back(), "'", "''");
_tid.push_back(msg.getTid());
_priority.push_back(msg.getPriority());
_text.push_back(msg.getText());
Poco::replaceInPlace(_text.back(), "'", "''");
_dateTime.push_back(msg.getTime());
}
ret = true;
}
if (_source.size() >= _minBatch) logSync();
return ret;
}
void SQLChannel::run()
{
long sleepTime = 100; // milliseconds
while (!_stop)
{
try
{
if (_reconnect)
{
close(_timeout);
open();
_reconnect = _pSession.isNull();
if (_reconnect && sleepTime < 12800)
sleepTime *= 2;
}
processOne(_minBatch);
sleepTime = 100;
}
catch (Poco::Exception& ex)
{
_logger.error(ex.displayText());
}
catch (std::exception& ex)
{
_logger.error(ex.what());
}
catch (...)
{
_logger.error("SQLChannel::run(): unknown exception");
}
_running = true;
Thread::sleep(100);
}
_running = false;
}
void SQLChannel::stop()
{
if (_pDBThread)
{
_reconnect = false;
_stop = true;
_pDBThread->join();
while (_logQueue.size())
processOne();
}
}
void SQLChannel::reconnect()
{
if (!_pDBThread)
{
_pDBThread.reset(new Thread);
_pDBThread->start(*this);
}
_reconnect = true;
}
void SQLChannel::setProperty(const std::string& name, const std::string& value)
{
Poco::FastMutex::ScopedLock l(_mutex);
if (name == PROP_NAME)
{
_name = value;
@ -156,17 +301,19 @@ void SQLChannel::setProperty(const std::string& name, const std::string& value)
else if (name == PROP_CONNECTOR)
{
_connector = value;
close(); open();
reconnect();
}
else if (name == PROP_CONNECT)
{
_connect = value;
close(); open();
reconnect();
}
else if (name == PROP_TABLE)
{
_table = value;
initLogStatement();
if (_pArchiveStrategy)
_pArchiveStrategy->setSource(value);
_tableChanged = true;
}
else if (name == PROP_ARCHIVE_TABLE)
{
@ -180,7 +327,9 @@ void SQLChannel::setProperty(const std::string& name, const std::string& value)
}
else
{
_pArchiveStrategy = new ArchiveByAgeStrategy(_connector, _connect, _table, value);
std::string threshold;
if (_pArchiveStrategy) threshold = _pArchiveStrategy->getThreshold();
_pArchiveStrategy = new ArchiveByAgeStrategy(_connector, _connect, _table, value, threshold);
}
}
else if (name == PROP_MAX_AGE)
@ -195,15 +344,14 @@ void SQLChannel::setProperty(const std::string& name, const std::string& value)
}
else
{
ArchiveByAgeStrategy* p = new ArchiveByAgeStrategy(_connector, _connect, _table);
p->setThreshold(value);
_pArchiveStrategy = p;
std::string destination = ArchiveByAgeStrategy::DEFAULT_ARCHIVE_DESTINATION;
if (_pArchiveStrategy) destination = _pArchiveStrategy->getDestination();
_pArchiveStrategy = new ArchiveByAgeStrategy(_connector, _connect, _table, destination, value);
}
}
else if (name == PROP_ASYNC)
{
_async = isTrue(value);
initLogStatement();
// no-op
}
else if (name == PROP_TIMEOUT)
{
@ -212,10 +360,32 @@ void SQLChannel::setProperty(const std::string& name, const std::string& value)
else
_timeout = NumberParser::parse(value);
}
else if (name == PROP_MIN_BATCH)
{
int minBatch = NumberParser::parse(value);
if (!minBatch)
throw Poco::InvalidArgumentException(Poco::format("SQLChannel::setProperty(%s,%s)", name, value));
_minBatch = minBatch;
}
else if (name == PROP_MAX_BATCH)
{
int maxBatch = NumberParser::parse(value);
if (!maxBatch)
throw Poco::InvalidArgumentException(Poco::format("SQLChannel::setProperty(%s,%s)", name, value));
_maxBatch = maxBatch;
}
else if (name == PROP_BULK)
{
_bulk = isTrue(value);
}
else if (name == PROP_THROW)
{
_throw = isTrue(value);
}
else if (name == PROP_FILE)
{
_file = value;
}
else
{
Channel::setProperty(name, value);
@ -225,6 +395,8 @@ void SQLChannel::setProperty(const std::string& name, const std::string& value)
std::string SQLChannel::getProperty(const std::string& name) const
{
Poco::FastMutex::ScopedLock l(_mutex);
if (name == PROP_NAME)
{
if (_name != "-") return _name;
@ -254,11 +426,28 @@ std::string SQLChannel::getProperty(const std::string& name) const
{
return NumberFormatter::format(_timeout);
}
else if (name == PROP_MIN_BATCH)
{
return std::to_string(_minBatch);
}
else if (name == PROP_MAX_BATCH)
{
return std::to_string(_maxBatch);
}
else if (name == PROP_BULK)
{
if (_bulk) return "true";
else return "false";
}
else if (name == PROP_THROW)
{
if (_throw) return "true";
else return "false";
}
else if (name == PROP_FILE)
{
return _file;
}
else
{
return Channel::getProperty(name);
@ -266,23 +455,186 @@ std::string SQLChannel::getProperty(const std::string& name) const
}
void SQLChannel::initLogStatement()
size_t SQLChannel::logTofile(AutoPtr<FileChannel>& pFileChannel, const std::string& fileName, bool clear)
{
_pLogStatement = new Statement(*_pSession);
static std::vector<std::string> names;
if (names.size() != _source.size())
names.resize(_source.size(), Poco::replace(_name, "'", "''"));
std::string sql;
Poco::format(sql, "INSERT INTO %s VALUES (?,?,?,?,?,?,?,?)", _table);
*_pLogStatement << sql,
use(_source),
use(_name),
use(_pid),
use(_thread),
use(_tid),
use(_priority),
use(_text),
use(_dateTime);
std::size_t n = 0;
if (_async) _pLogStatement->setAsync();
if (!pFileChannel) pFileChannel = new FileChannel(fileName);
if (pFileChannel)
{
std::string sql;
Poco::format(sql, SQL_INSERT_STMT, _table, std::string());
std::stringstream os;
os << sql << '\n';
auto it = _source.begin();
auto end = _source.end();
int idx = 0, batch = 0;
for (; it != end; ++idx)
{
std::string dt = Poco::DateTimeFormatter::format(_dateTime[idx], "%Y-%m-%d %H:%M:%S.%i");
os << "('" << *it << "','" <<
names[idx] << "'," <<
_pid[idx] << ",'" <<
_thread[idx] << "'," <<
_tid[idx] << ',' <<
_priority[idx] << ",'" <<
_text[idx] << "','" <<
dt << "')";
if (++batch == _maxBatch)
{
os << ";\n";
Message msg(_source[0], os.str(), Message::PRIO_ERROR);
pFileChannel->log(msg);
os.str(""); sql.clear();
Poco::format(sql, SQL_INSERT_STMT, _table, std::string());
batch = 0;
}
if (++it == end)
{
os << ";\n";
break;
}
os << ",\n";
}
Message msg(_source[0], os.str(), Message::PRIO_ERROR);
pFileChannel->log(msg);
n = _source.size();
if (clear && n)
{
_source.clear();
_pid.clear();
_thread.clear();
_tid.clear();
_priority.clear();
_text.clear();
_dateTime.clear();
}
}
return n;
}
size_t SQLChannel::execSQL()
{
static std::vector<std::string> names;
if (names.size() != _source.size())
names.resize(_source.size(), Poco::replace(_name, "'", "''"));
static std::string placeholders = "(?,?,?,?,?,?,?,?)";
Poco::FastMutex::ScopedLock l(_mutex);
if (_tableChanged)
{
Poco::format(_sql, SQL_INSERT_STMT, _table, placeholders);
_tableChanged = false;
}
if (!_pSession || !_pSession->isConnected()) open();
if (_pArchiveStrategy) _pArchiveStrategy->archive();
size_t n = 0;
if (_pSession)
{
try
{
if (_bulk)
{
try
{
(*_pSession) << _sql,
use(_source, bulk),
use(names, bulk),
use(_pid, bulk),
use(_thread, bulk),
use(_tid, bulk),
use(_priority, bulk),
use(_text, bulk),
use(_dateTime, bulk), now;
}
// most likely bulk mode not supported,
// log and try again
catch (Poco::InvalidAccessException& ex)
{
_logger.log(ex);
(*_pSession) << _sql,
use(_source),
use(names),
use(_pid),
use(_thread),
use(_tid),
use(_priority),
use(_text),
use(_dateTime), now;
_bulk = false;
}
}
else
{
(*_pSession) << _sql,
use(_source),
use(names),
use(_pid),
use(_thread),
use(_tid),
use(_priority),
use(_text),
use(_dateTime), now;
}
n = _source.size();
}
catch (Poco::Exception& ex)
{
_logger.error(ex.displayText());
if (!_file.empty())
n = logTofile(_pFileChannel, _file);
close(_timeout);
_reconnect = true;
}
catch (std::exception& ex)
{
_logger.error(ex.what());
if (!_file.empty())
n = logTofile(_pFileChannel, _file);
close(_timeout);
_reconnect = true;
}
}
else
{
if (!_file.empty())
n = logTofile(_pFileChannel, _file);
}
if (n)
{
_logged += n;
_source.clear();
_pid.clear();
_thread.clear();
_tid.clear();
_priority.clear();
_text.clear();
_dateTime.clear();
}
return n;
}
std::size_t SQLChannel::wait(int ms)
{
Stopwatch sw;
sw.start();
int processed = _logQueue.size();
while (_logQueue.size())
{
Thread::sleep(10);
if (ms && sw.elapsed() * 1000 > ms)
break;
}
return processed - _logQueue.size();
}

View File

@ -64,9 +64,9 @@ Session SessionPool::get(const std::string& name, bool value)
Session SessionPool::get()
{
Poco::Mutex::ScopedLock lock(_mutex);
if (_shutdown) throw InvalidAccessException("Session pool has been shut down.");
if (_shutdown) throw InvalidAccessException("Session pool has been shut down.");
Poco::Mutex::ScopedLock lock(_mutex);
purgeDeadSessions();
if (_idleSessions.empty())
@ -95,7 +95,6 @@ Session SessionPool::get()
void SessionPool::purgeDeadSessions()
{
Poco::Mutex::ScopedLock lock(_mutex);
if (_shutdown) return;
SessionList::iterator it = _idleSessions.begin();
@ -139,9 +138,9 @@ int SessionPool::connTimeout() const
int SessionPool::dead()
{
Poco::Mutex::ScopedLock lock(_mutex);
int count = 0;
Poco::Mutex::ScopedLock lock(_mutex);
SessionList::iterator it = _activeSessions.begin();
SessionList::iterator itEnd = _activeSessions.end();
for (; it != itEnd; ++it)
@ -156,7 +155,6 @@ int SessionPool::dead()
int SessionPool::allocated() const
{
Poco::Mutex::ScopedLock lock(_mutex);
return _nSessions;
}
@ -170,21 +168,24 @@ int SessionPool::available() const
void SessionPool::setFeature(const std::string& name, bool state)
{
Poco::Mutex::ScopedLock lock(_mutex);
if (_shutdown) throw InvalidAccessException("Session pool has been shut down.");
if (_nSessions > 0)
throw InvalidAccessException("Features can not be set after the first session was created.");
Poco::Mutex::ScopedLock lock(_mutex);
_featureMap.insert(FeatureMap::ValueType(name, state));
}
bool SessionPool::getFeature(const std::string& name)
{
FeatureMap::ConstIterator it = _featureMap.find(name);
if (_shutdown) throw InvalidAccessException("Session pool has been shut down.");
Poco::Mutex::ScopedLock lock(_mutex);
FeatureMap::ConstIterator it = _featureMap.find(name);
if (_featureMap.end() == it)
throw NotFoundException("Feature not found:" + name);
@ -194,18 +195,19 @@ bool SessionPool::getFeature(const std::string& name)
void SessionPool::setProperty(const std::string& name, const Poco::Any& value)
{
Poco::Mutex::ScopedLock lock(_mutex);
if (_shutdown) throw InvalidAccessException("Session pool has been shut down.");
if (_nSessions > 0)
throw InvalidAccessException("Properties can not be set after first session was created.");
Poco::Mutex::ScopedLock lock(_mutex);
_propertyMap.insert(PropertyMap::ValueType(name, value));
}
Poco::Any SessionPool::getProperty(const std::string& name)
{
Poco::Mutex::ScopedLock lock(_mutex);
PropertyMap::ConstIterator it = _propertyMap.find(name);
if (_propertyMap.end() == it)
@ -234,9 +236,9 @@ void SessionPool::customizeSession(Session&)
void SessionPool::putBack(PooledSessionHolderPtr pHolder)
{
Poco::Mutex::ScopedLock lock(_mutex);
if (_shutdown) return;
Poco::Mutex::ScopedLock lock(_mutex);
SessionList::iterator it = std::find(_activeSessions.begin(), _activeSessions.end(), pHolder);
if (it != _activeSessions.end())
{
@ -287,9 +289,9 @@ void SessionPool::putBack(PooledSessionHolderPtr pHolder)
void SessionPool::onJanitorTimer(Poco::Timer&)
{
Poco::Mutex::ScopedLock lock(_mutex);
if (_shutdown) return;
Poco::Mutex::ScopedLock lock(_mutex);
SessionList::iterator it = _idleSessions.begin();
while (_nSessions > _minSessions && it != _idleSessions.end())
{
@ -312,15 +314,9 @@ void SessionPool::onJanitorTimer(Poco::Timer&)
void SessionPool::shutdown()
{
{
Poco::Mutex::ScopedLock lock(_mutex);
if (_shutdown) return;
_shutdown = true;
}
if (_shutdown.exchange(true)) return;
_shutdown = true;
_janitorTimer.stop();
Poco::Mutex::ScopedLock lock(_mutex);
closeAll(_idleSessions);
closeAll(_activeSessions);
}
@ -344,4 +340,4 @@ void SessionPool::closeAll(SessionList& sessionList)
}
} } // namespace Poco::Data
} } // namespace Poco::Data

View File

@ -22,6 +22,7 @@ namespace Data {
Transaction::Transaction(Poco::Data::Session& rSession, Poco::Logger* pLogger):
_rSession(rSession),
_autoCommit(_rSession.hasFeature("autoCommit") ? _rSession.getFeature("autoCommit") : false),
_pLogger(pLogger)
{
begin();
@ -30,6 +31,7 @@ Transaction::Transaction(Poco::Data::Session& rSession, Poco::Logger* pLogger):
Transaction::Transaction(Poco::Data::Session& rSession, bool start):
_rSession(rSession),
_autoCommit(_rSession.hasFeature("autoCommit") ? _rSession.getFeature("autoCommit") : false),
_pLogger(0)
{
if (start) begin();
@ -71,7 +73,11 @@ Transaction::~Transaction()
void Transaction::begin()
{
if (!_rSession.isTransaction())
{
if (_autoCommit)
_rSession.setFeature("autoCommit", false);
_rSession.begin();
}
else
throw InvalidAccessException("Transaction in progress.");
}
@ -85,21 +91,28 @@ void Transaction::execute(const std::string& sql, bool doCommit)
}
void Transaction::execute(const std::vector<std::string>& sql)
bool Transaction::execute(const std::vector<std::string>& sql)
{
return execute(sql, nullptr);
}
bool Transaction::execute(const std::vector<std::string>& sql, std::string* info)
{
try
{
std::vector<std::string>::const_iterator it = sql.begin();
std::vector<std::string>::const_iterator end = sql.end();
for (; it != end; ++it) execute(*it, it + 1 == end ? true : false);
return;
return true;
}
catch (Exception& ex)
{
if (_pLogger) _pLogger->log(ex);
if(info) *info = ex.displayText();
}
rollback();
return false;
}
@ -109,6 +122,8 @@ void Transaction::commit()
_pLogger->debug("Committing transaction.");
_rSession.commit();
if (_autoCommit)
_rSession.setFeature("autoCommit", true);
}
@ -118,6 +133,8 @@ void Transaction::rollback()
_pLogger->debug("Rolling back transaction.");
_rSession.rollback();
if (_autoCommit)
_rSession.setFeature("autoCommit", true);
}

View File

@ -11,9 +11,10 @@
LINKMODE ?= SHARED
SANITIZEFLAGS ?=
#-fsanitize=address
#-fsanitize=undefined
#-fsanitize=thread
# sanitize flags:
# -fsanitize=address
# -fsanitize=undefined
# -fsanitize=thread
#
# Define Tools