mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-12 10:13:51 +01:00
* 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:
parent
de04b9eac7
commit
5131fe1c15
@ -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.
|
||||
|
||||
|
@ -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();
|
||||
|
@ -138,6 +138,8 @@ public:
|
||||
|
||||
const Diagnostics& diagnostics()
|
||||
{
|
||||
if (SQL_NULL_HANDLE == _handle) return *this;
|
||||
|
||||
DiagnosticFields df;
|
||||
SQLSMALLINT count = 1;
|
||||
SQLSMALLINT messageLength = 0;
|
||||
|
@ -57,7 +57,6 @@ private:
|
||||
const EnvironmentHandle& operator=(const EnvironmentHandle&);
|
||||
|
||||
SQLHENV _henv;
|
||||
bool _isOwner;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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()");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 (...)
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -45,7 +45,7 @@ void Parameter::init()
|
||||
&_decimalDigits,
|
||||
&_isNullable)))
|
||||
{
|
||||
throw StatementException(_rStmt);
|
||||
throw StatementException(_rStmt, "ODBC::Parameter::init():SQLDescribeParam()");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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, ¶meterSize, &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, ¶meterSize, &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");
|
||||
|
@ -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();
|
||||
|
@ -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.");
|
||||
}
|
||||
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,9 +11,10 @@
|
||||
LINKMODE ?= SHARED
|
||||
|
||||
SANITIZEFLAGS ?=
|
||||
#-fsanitize=address
|
||||
#-fsanitize=undefined
|
||||
#-fsanitize=thread
|
||||
# sanitize flags:
|
||||
# -fsanitize=address
|
||||
# -fsanitize=undefined
|
||||
# -fsanitize=thread
|
||||
|
||||
#
|
||||
# Define Tools
|
||||
|
Loading…
Reference in New Issue
Block a user