mirror of
https://github.com/pocoproject/poco.git
synced 2025-04-25 09:25:57 +02: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,
|
decDigits,
|
||||||
(SQLPOINTER) &val, 0, 0)))
|
(SQLPOINTER) &val, 0, 0)))
|
||||||
{
|
{
|
||||||
throw StatementException(_rStmt, "SQLBindParameter()");
|
throw StatementException(_rStmt, "ODBC::Binder::SQLBindParameter()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,14 +436,14 @@ private:
|
|||||||
(SQLUSMALLINT) pos + 1,
|
(SQLUSMALLINT) pos + 1,
|
||||||
SQL_PARAM_INPUT,
|
SQL_PARAM_INPUT,
|
||||||
SQL_C_BINARY,
|
SQL_C_BINARY,
|
||||||
SQL_LONGVARBINARY,
|
Utility::sqlDataType(SQL_C_BINARY),
|
||||||
columnSize,
|
columnSize,
|
||||||
0,
|
0,
|
||||||
pVal,
|
pVal,
|
||||||
(SQLINTEGER) size,
|
(SQLINTEGER) size,
|
||||||
_lengthIndicator.back())))
|
_lengthIndicator.back())))
|
||||||
{
|
{
|
||||||
throw StatementException(_rStmt, "SQLBindParameter(LOB)");
|
throw StatementException(_rStmt, "ODBC::Binder::SQLBindParameter(LOB)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,7 +476,7 @@ private:
|
|||||||
0,
|
0,
|
||||||
&(*_vecLengthIndicator[pos])[0])))
|
&(*_vecLengthIndicator[pos])[0])))
|
||||||
{
|
{
|
||||||
throw StatementException(_rStmt, "SQLBindParameter()");
|
throw StatementException(_rStmt, "ODBC::Binder::SQLBindParameter()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,7 +537,7 @@ private:
|
|||||||
0,
|
0,
|
||||||
&(*_vecLengthIndicator[pos])[0])))
|
&(*_vecLengthIndicator[pos])[0])))
|
||||||
{
|
{
|
||||||
throw StatementException(_rStmt, "SQLBindParameter()");
|
throw StatementException(_rStmt, "ODBC::Binder::SQLBindParameter()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -577,7 +577,7 @@ private:
|
|||||||
if (size == _maxFieldSize)
|
if (size == _maxFieldSize)
|
||||||
{
|
{
|
||||||
getMinValueSize(*pVal, size);
|
getMinValueSize(*pVal, size);
|
||||||
// accomodate for terminating zero
|
// accommodate for terminating zero
|
||||||
if (size != _maxFieldSize) ++size;
|
if (size != _maxFieldSize) ++size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,14 +614,14 @@ private:
|
|||||||
(SQLUSMALLINT) pos + 1,
|
(SQLUSMALLINT) pos + 1,
|
||||||
toODBCDirection(dir),
|
toODBCDirection(dir),
|
||||||
SQL_C_CHAR,
|
SQL_C_CHAR,
|
||||||
SQL_LONGVARCHAR,
|
Utility::sqlDataType(SQL_C_CHAR),
|
||||||
(SQLUINTEGER) size - 1,
|
(SQLUINTEGER) size - 1,
|
||||||
0,
|
0,
|
||||||
_charPtrs[pos],
|
_charPtrs[pos],
|
||||||
(SQLINTEGER) size,
|
(SQLINTEGER) size,
|
||||||
&(*_vecLengthIndicator[pos])[0])))
|
&(*_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);
|
strSize = it->size() * sizeof(UTF16Char);
|
||||||
if (strSize > size)
|
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);
|
std::memcpy(pBuf + offset, it->data(), strSize);
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
@ -681,14 +681,14 @@ private:
|
|||||||
(SQLUSMALLINT)pos + 1,
|
(SQLUSMALLINT)pos + 1,
|
||||||
toODBCDirection(dir),
|
toODBCDirection(dir),
|
||||||
SQL_C_WCHAR,
|
SQL_C_WCHAR,
|
||||||
SQL_WLONGVARCHAR,
|
Utility::sqlDataType(SQL_C_WCHAR),
|
||||||
(SQLUINTEGER)size - 1,
|
(SQLUINTEGER)size - 1,
|
||||||
0,
|
0,
|
||||||
_utf16CharPtrs[pos],
|
_utf16CharPtrs[pos],
|
||||||
(SQLINTEGER)size,
|
(SQLINTEGER)size,
|
||||||
&(*_vecLengthIndicator[pos])[0])))
|
&(*_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;
|
typedef typename LOBType::ValueType CharType;
|
||||||
|
|
||||||
if (isOutBound(dir) || !isInBound(dir))
|
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)
|
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();
|
std::size_t length = val.size();
|
||||||
if (0 == length)
|
if (0 == length)
|
||||||
throw InvalidArgumentException("Empty container not allowed.");
|
throw InvalidArgumentException("ODBC::Binder::bindImplContainerLOB():Empty container not allowed.");
|
||||||
|
|
||||||
setParamSetSize(length);
|
setParamSetSize(length);
|
||||||
|
|
||||||
@ -742,7 +742,7 @@ private:
|
|||||||
{
|
{
|
||||||
blobSize = cIt->size();
|
blobSize = cIt->size();
|
||||||
if (blobSize > 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));
|
std::memcpy(_charPtrs[pos] + offset, cIt->rawContent(), blobSize * sizeof(CharType));
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
@ -751,14 +751,14 @@ private:
|
|||||||
(SQLUSMALLINT) pos + 1,
|
(SQLUSMALLINT) pos + 1,
|
||||||
SQL_PARAM_INPUT,
|
SQL_PARAM_INPUT,
|
||||||
SQL_C_BINARY,
|
SQL_C_BINARY,
|
||||||
SQL_LONGVARBINARY,
|
Utility::sqlDataType(SQL_C_BINARY),
|
||||||
(SQLUINTEGER) size,
|
(SQLUINTEGER) size,
|
||||||
0,
|
0,
|
||||||
_charPtrs[pos],
|
_charPtrs[pos],
|
||||||
(SQLINTEGER) size,
|
(SQLINTEGER) size,
|
||||||
&(*_vecLengthIndicator[pos])[0])))
|
&(*_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)
|
void bindImplContainerDate(std::size_t pos, const C& val, Direction dir)
|
||||||
{
|
{
|
||||||
if (isOutBound(dir) || !isInBound(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)
|
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();
|
std::size_t length = val.size();
|
||||||
|
|
||||||
if (0 == length)
|
if (0 == length)
|
||||||
throw InvalidArgumentException("Empty vector not allowed.");
|
throw InvalidArgumentException("ODBC::Binder::bindImplContainerDate():Empty vector not allowed.");
|
||||||
|
|
||||||
setParamSetSize(length);
|
setParamSetSize(length);
|
||||||
|
|
||||||
@ -800,14 +800,14 @@ private:
|
|||||||
(SQLUSMALLINT) pos + 1,
|
(SQLUSMALLINT) pos + 1,
|
||||||
toODBCDirection(dir),
|
toODBCDirection(dir),
|
||||||
SQL_C_TYPE_DATE,
|
SQL_C_TYPE_DATE,
|
||||||
SQL_TYPE_DATE,
|
Utility::sqlDataType(SQL_C_TYPE_DATE),
|
||||||
colSize,
|
colSize,
|
||||||
decDigits,
|
decDigits,
|
||||||
(SQLPOINTER) &(*_dateVecVec[pos])[0],
|
(SQLPOINTER) &(*_dateVecVec[pos])[0],
|
||||||
0,
|
0,
|
||||||
&(*_vecLengthIndicator[pos])[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)
|
void bindImplContainerTime(std::size_t pos, const C& val, Direction dir)
|
||||||
{
|
{
|
||||||
if (isOutBound(dir) || !isInBound(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)
|
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();
|
std::size_t length = val.size();
|
||||||
if (0 == length)
|
if (0 == length)
|
||||||
throw InvalidArgumentException("Empty container not allowed.");
|
throw InvalidArgumentException("ODBC::Binder::bindImplContainerTime():Empty container not allowed.");
|
||||||
|
|
||||||
setParamSetSize(val.size());
|
setParamSetSize(val.size());
|
||||||
|
|
||||||
@ -848,14 +848,14 @@ private:
|
|||||||
(SQLUSMALLINT) pos + 1,
|
(SQLUSMALLINT) pos + 1,
|
||||||
toODBCDirection(dir),
|
toODBCDirection(dir),
|
||||||
SQL_C_TYPE_TIME,
|
SQL_C_TYPE_TIME,
|
||||||
SQL_TYPE_TIME,
|
Utility::sqlDataType(SQL_C_TYPE_TIME),
|
||||||
colSize,
|
colSize,
|
||||||
decDigits,
|
decDigits,
|
||||||
(SQLPOINTER) &(*_timeVecVec[pos])[0],
|
(SQLPOINTER) &(*_timeVecVec[pos])[0],
|
||||||
0,
|
0,
|
||||||
&(*_vecLengthIndicator[pos])[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)
|
void bindImplContainerDateTime(std::size_t pos, const C& val, Direction dir)
|
||||||
{
|
{
|
||||||
if (isOutBound(dir) || !isInBound(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)
|
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();
|
std::size_t length = val.size();
|
||||||
|
|
||||||
if (0 == length)
|
if (0 == length)
|
||||||
throw InvalidArgumentException("Empty Containers not allowed.");
|
throw InvalidArgumentException("ODBC::Binder::bindImplContainerDateTime():Empty Containers not allowed.");
|
||||||
|
|
||||||
setParamSetSize(length);
|
setParamSetSize(length);
|
||||||
|
|
||||||
@ -897,14 +897,14 @@ private:
|
|||||||
(SQLUSMALLINT) pos + 1,
|
(SQLUSMALLINT) pos + 1,
|
||||||
toODBCDirection(dir),
|
toODBCDirection(dir),
|
||||||
SQL_C_TYPE_TIMESTAMP,
|
SQL_C_TYPE_TIMESTAMP,
|
||||||
SQL_TYPE_TIMESTAMP,
|
Utility::sqlDataType(SQL_C_TYPE_TIMESTAMP),
|
||||||
colSize,
|
colSize,
|
||||||
decDigits,
|
decDigits,
|
||||||
(SQLPOINTER) &(*_dateTimeVecVec[pos])[0],
|
(SQLPOINTER) &(*_dateTimeVecVec[pos])[0],
|
||||||
0,
|
0,
|
||||||
&(*_vecLengthIndicator[pos])[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)
|
void bindImplNullContainer(std::size_t pos, const C& val, Direction dir)
|
||||||
{
|
{
|
||||||
if (isOutBound(dir) || !isInBound(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)
|
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();
|
std::size_t length = val.size();
|
||||||
|
|
||||||
if (0 == length)
|
if (0 == length)
|
||||||
throw InvalidArgumentException("Empty container not allowed.");
|
throw InvalidArgumentException("ODBC::Binder::bindImplNullContainer():Empty container not allowed.");
|
||||||
|
|
||||||
setParamSetSize(length);
|
setParamSetSize(length);
|
||||||
|
|
||||||
@ -945,7 +945,7 @@ private:
|
|||||||
0,
|
0,
|
||||||
&(*_vecLengthIndicator[pos])[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
|
/// size should be set to some default value prior to calling this
|
||||||
/// function in order to avoid undefined size value.
|
/// 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();
|
void freeMemory();
|
||||||
/// Frees all dynamically allocated memory resources.
|
/// Frees all dynamically allocated memory resources.
|
||||||
|
|
||||||
|
@ -36,18 +36,40 @@ class ODBC_API ConnectionHandle
|
|||||||
/// ODBC connection handle class
|
/// ODBC connection handle class
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ConnectionHandle(EnvironmentHandle* pEnvironment = 0);
|
ConnectionHandle(const std::string& connectString = "", SQLULEN timeout = 5);
|
||||||
/// Creates the ConnectionHandle.
|
/// Creates the ConnectionHandle.
|
||||||
|
|
||||||
~ConnectionHandle();
|
~ConnectionHandle();
|
||||||
/// Creates the ConnectionHandle.
|
/// Creates the ConnectionHandle.
|
||||||
|
|
||||||
operator const SQLHDBC& () const;
|
bool connect(const std::string& connectString = "", SQLULEN timeout = 5);
|
||||||
/// Const conversion operator into reference to native type.
|
/// 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;
|
const SQLHDBC& handle() const;
|
||||||
/// Returns const reference to handle;
|
/// 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:
|
private:
|
||||||
operator SQLHDBC& ();
|
operator SQLHDBC& ();
|
||||||
/// Conversion operator into reference to native type.
|
/// Conversion operator into reference to native type.
|
||||||
@ -55,17 +77,26 @@ private:
|
|||||||
SQLHDBC& handle();
|
SQLHDBC& handle();
|
||||||
/// Returns reference to handle;
|
/// Returns reference to handle;
|
||||||
|
|
||||||
|
void alloc();
|
||||||
|
/// Allocates the connection handle.
|
||||||
|
|
||||||
|
void free();
|
||||||
|
/// Frees the connection handle.
|
||||||
|
|
||||||
ConnectionHandle(const ConnectionHandle&);
|
ConnectionHandle(const ConnectionHandle&);
|
||||||
const ConnectionHandle& operator=(const ConnectionHandle&);
|
const ConnectionHandle& operator=(const ConnectionHandle&);
|
||||||
|
|
||||||
const EnvironmentHandle* _pEnvironment;
|
const EnvironmentHandle* _pEnvironment = nullptr;
|
||||||
SQLHDBC _hdbc;
|
SQLHDBC _hdbc = SQL_NULL_HDBC;
|
||||||
bool _ownsEnvironment;
|
std::string _connectString;
|
||||||
|
|
||||||
friend class Poco::Data::ODBC::SessionImpl;
|
friend class Poco::Data::ODBC::SessionImpl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
using Connection = ConnectionHandle;
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// inlines
|
// 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
|
inline const SQLHDBC& ConnectionHandle::handle() const
|
||||||
{
|
{
|
||||||
return _hdbc;
|
return _hdbc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline const SQLHDBC* ConnectionHandle::pHandle() const
|
||||||
|
{
|
||||||
|
return &_hdbc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inline ConnectionHandle::operator SQLHDBC& ()
|
inline ConnectionHandle::operator SQLHDBC& ()
|
||||||
{
|
{
|
||||||
return handle();
|
return handle();
|
||||||
|
@ -138,6 +138,8 @@ public:
|
|||||||
|
|
||||||
const Diagnostics& diagnostics()
|
const Diagnostics& diagnostics()
|
||||||
{
|
{
|
||||||
|
if (SQL_NULL_HANDLE == _handle) return *this;
|
||||||
|
|
||||||
DiagnosticFields df;
|
DiagnosticFields df;
|
||||||
SQLSMALLINT count = 1;
|
SQLSMALLINT count = 1;
|
||||||
SQLSMALLINT messageLength = 0;
|
SQLSMALLINT messageLength = 0;
|
||||||
|
@ -57,7 +57,6 @@ private:
|
|||||||
const EnvironmentHandle& operator=(const EnvironmentHandle&);
|
const EnvironmentHandle& operator=(const EnvironmentHandle&);
|
||||||
|
|
||||||
SQLHENV _henv;
|
SQLHENV _henv;
|
||||||
bool _isOwner;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -410,6 +410,7 @@ private:
|
|||||||
|
|
||||||
CharType** pc = AnyCast<CharType*>(&(_pPreparator->at(pos)));
|
CharType** pc = AnyCast<CharType*>(&(_pPreparator->at(pos)));
|
||||||
poco_assert_dbg (pc);
|
poco_assert_dbg (pc);
|
||||||
|
poco_assert_dbg(*pc);
|
||||||
poco_assert_dbg (_pPreparator->bulkSize() == values.size());
|
poco_assert_dbg (_pPreparator->bulkSize() == values.size());
|
||||||
std::size_t colWidth = columnSize(pos);
|
std::size_t colWidth = columnSize(pos);
|
||||||
ItType it = values.begin();
|
ItType it = values.begin();
|
||||||
@ -441,6 +442,7 @@ private:
|
|||||||
|
|
||||||
CharType** pc = AnyCast<CharType*>(&(_pPreparator->at(pos)));
|
CharType** pc = AnyCast<CharType*>(&(_pPreparator->at(pos)));
|
||||||
poco_assert_dbg (pc);
|
poco_assert_dbg (pc);
|
||||||
|
poco_assert_dbg(*pc);
|
||||||
poco_assert_dbg (_pPreparator->bulkSize() == values.size());
|
poco_assert_dbg (_pPreparator->bulkSize() == values.size());
|
||||||
std::size_t colWidth = _pPreparator->maxDataSize(pos);
|
std::size_t colWidth = _pPreparator->maxDataSize(pos);
|
||||||
ItType it = values.begin();
|
ItType it = values.begin();
|
||||||
@ -480,7 +482,7 @@ private:
|
|||||||
&_lengths[pos]); //length indicator
|
&_lengths[pos]); //length indicator
|
||||||
|
|
||||||
if (Utility::isError(rc))
|
if (Utility::isError(rc))
|
||||||
throw StatementException(_rStmt, "SQLGetData()");
|
throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl():SQLGetData()");
|
||||||
|
|
||||||
if (isNullLengthIndicator(_lengths[pos]))
|
if (isNullLengthIndicator(_lengths[pos]))
|
||||||
return false;
|
return false;
|
||||||
|
@ -94,6 +94,9 @@ protected:
|
|||||||
std::string nativeSQL();
|
std::string nativeSQL();
|
||||||
/// Returns the SQL string as modified by the driver.
|
/// Returns the SQL string as modified by the driver.
|
||||||
|
|
||||||
|
void printErrors(std::ostream& os) const;
|
||||||
|
/// Print errors, if any.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef Poco::Data::AbstractBindingVec Bindings;
|
typedef Poco::Data::AbstractBindingVec Bindings;
|
||||||
typedef Poco::SharedPtr<Binder> BinderPtr;
|
typedef Poco::SharedPtr<Binder> BinderPtr;
|
||||||
@ -143,6 +146,14 @@ private:
|
|||||||
void fillColumns();
|
void fillColumns();
|
||||||
void checkError(SQLRETURN rc, const std::string& msg="");
|
void checkError(SQLRETURN rc, const std::string& msg="");
|
||||||
|
|
||||||
|
struct ERROR_INFO
|
||||||
|
{
|
||||||
|
SQLCHAR state[8];
|
||||||
|
SQLINTEGER native;
|
||||||
|
SQLCHAR text[256];
|
||||||
|
};
|
||||||
|
void addErrors();
|
||||||
|
|
||||||
const SQLHDBC& _rConnection;
|
const SQLHDBC& _rConnection;
|
||||||
const StatementHandle _stmt;
|
const StatementHandle _stmt;
|
||||||
PreparatorVec _preparations;
|
PreparatorVec _preparations;
|
||||||
@ -154,6 +165,7 @@ private:
|
|||||||
bool _prepared;
|
bool _prepared;
|
||||||
mutable std::size_t _affectedRowCount;
|
mutable std::size_t _affectedRowCount;
|
||||||
bool _canCompile;
|
bool _canCompile;
|
||||||
|
std::vector<ERROR_INFO> _errorInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -583,7 +583,7 @@ private:
|
|||||||
(SQLINTEGER) dataSize,
|
(SQLINTEGER) dataSize,
|
||||||
&_lengths[pos])))
|
&_lengths[pos])))
|
||||||
{
|
{
|
||||||
throw StatementException(_rStmt, "SQLBindCol()");
|
throw StatementException(_rStmt, "ODBC::Preparator::prepareFixedSize():SQLBindCol()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,7 +612,7 @@ private:
|
|||||||
(SQLINTEGER) dataSize,
|
(SQLINTEGER) dataSize,
|
||||||
&_lenLengths[pos][0])))
|
&_lenLengths[pos][0])))
|
||||||
{
|
{
|
||||||
throw StatementException(_rStmt, "SQLBindCol()");
|
throw StatementException(_rStmt, "ODBC::Preparator::prepareFixedSize():SQLBindCol()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,7 +637,7 @@ private:
|
|||||||
(SQLINTEGER) size*sizeof(T),
|
(SQLINTEGER) size*sizeof(T),
|
||||||
&_lengths[pos])))
|
&_lengths[pos])))
|
||||||
{
|
{
|
||||||
throw StatementException(_rStmt, "SQLBindCol()");
|
throw StatementException(_rStmt, "ODBC::Preparator::prepareVariableLen():SQLBindCol()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,7 +664,7 @@ private:
|
|||||||
(SQLINTEGER) size,
|
(SQLINTEGER) size,
|
||||||
&_lenLengths[pos][0])))
|
&_lenLengths[pos][0])))
|
||||||
{
|
{
|
||||||
throw StatementException(_rStmt, "SQLBindCol()");
|
throw StatementException(_rStmt, "ODBC::Preparator::prepareCharArray():SQLBindCol()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +52,13 @@ public:
|
|||||||
ODBC_TXN_CAPABILITY_TRUE = 1
|
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,
|
SessionImpl(const std::string& connect,
|
||||||
std::size_t loginTimeout,
|
std::size_t loginTimeout,
|
||||||
std::size_t maxFieldSize = ODBC_MAX_FIELD_SIZE,
|
std::size_t maxFieldSize = ODBC_MAX_FIELD_SIZE,
|
||||||
@ -159,6 +166,15 @@ public:
|
|||||||
/// Returns the timeout (in seconds) for queries,
|
/// Returns the timeout (in seconds) for queries,
|
||||||
/// or -1 if no timeout has been set.
|
/// 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;
|
int queryTimeout() const;
|
||||||
/// Returns the timeout (in seconds) for queries,
|
/// Returns the timeout (in seconds) for queries,
|
||||||
/// or -1 if no timeout has been set.
|
/// or -1 if no timeout has been set.
|
||||||
@ -201,8 +217,8 @@ private:
|
|||||||
bool _autoBind;
|
bool _autoBind;
|
||||||
bool _autoExtract;
|
bool _autoExtract;
|
||||||
TypeInfo _dataTypes;
|
TypeInfo _dataTypes;
|
||||||
mutable char _canTransact;
|
mutable TransactionCapability _canTransact;
|
||||||
bool _inTransaction;
|
std::atomic<bool> _inTransaction;
|
||||||
int _queryTimeout;
|
int _queryTimeout;
|
||||||
std::string _dbEncoding;
|
std::string _dbEncoding;
|
||||||
Poco::FastMutex _mutex;
|
Poco::FastMutex _mutex;
|
||||||
|
@ -82,7 +82,7 @@ public:
|
|||||||
int sqlDataType(int cDataType) const;
|
int sqlDataType(int cDataType) const;
|
||||||
/// Returns SQL data type corresponding to supplied C data type.
|
/// 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.
|
/// Fills the data type info structure for the database.
|
||||||
|
|
||||||
DynamicAny getInfo(SQLSMALLINT type, const std::string& param) const;
|
DynamicAny getInfo(SQLSMALLINT type, const std::string& param) const;
|
||||||
@ -107,7 +107,7 @@ private:
|
|||||||
DataTypeMap _cDataTypes;
|
DataTypeMap _cDataTypes;
|
||||||
DataTypeMap _sqlDataTypes;
|
DataTypeMap _sqlDataTypes;
|
||||||
TypeInfoVec _typeInfo;
|
TypeInfoVec _typeInfo;
|
||||||
SQLHDBC* _pHDBC;
|
const SQLHDBC* _pHDBC;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ void Binder::freeMemory()
|
|||||||
|
|
||||||
UUIDMap::iterator itUUID = _uuids.begin();
|
UUIDMap::iterator itUUID = _uuids.begin();
|
||||||
UUIDMap::iterator itUUIDEnd = _uuids.end();
|
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 itBool = _boolPtrs.begin();
|
||||||
BoolPtrVec::iterator endBool = _boolPtrs.end();
|
BoolPtrVec::iterator endBool = _boolPtrs.end();
|
||||||
@ -152,7 +152,7 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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);
|
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,
|
(SQLUSMALLINT) pos + 1,
|
||||||
toODBCDirection(dir),
|
toODBCDirection(dir),
|
||||||
SQL_C_CHAR,
|
SQL_C_CHAR,
|
||||||
Connector::stringBoundToLongVarChar() ? SQL_LONGVARCHAR : SQL_VARCHAR,
|
Utility::sqlDataType(SQL_C_CHAR),
|
||||||
(SQLUINTEGER) colSize,
|
(SQLUINTEGER) colSize,
|
||||||
0,
|
0,
|
||||||
pVal,
|
pVal,
|
||||||
(SQLINTEGER) size,
|
(SQLINTEGER) size,
|
||||||
_lengthIndicator.back())))
|
_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));
|
_inParams.insert(ParamMap::value_type(pVal, size));
|
||||||
}
|
}
|
||||||
else
|
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);
|
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,
|
(SQLUSMALLINT)pos + 1,
|
||||||
toODBCDirection(dir),
|
toODBCDirection(dir),
|
||||||
SQL_C_WCHAR,
|
SQL_C_WCHAR,
|
||||||
SQL_WLONGVARCHAR,
|
Utility::sqlDataType(SQL_C_WCHAR),
|
||||||
(SQLUINTEGER)colSize,
|
(SQLUINTEGER)colSize,
|
||||||
0,
|
0,
|
||||||
pVal,
|
pVal,
|
||||||
(SQLINTEGER)size,
|
(SQLINTEGER)size,
|
||||||
_lengthIndicator.back())))
|
_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,
|
(SQLUSMALLINT) pos + 1,
|
||||||
toODBCDirection(dir),
|
toODBCDirection(dir),
|
||||||
SQL_C_TYPE_DATE,
|
SQL_C_TYPE_DATE,
|
||||||
SQL_TYPE_DATE,
|
Utility::sqlDataType(SQL_C_TYPE_DATE),
|
||||||
colSize,
|
colSize,
|
||||||
decDigits,
|
decDigits,
|
||||||
(SQLPOINTER) pDS,
|
(SQLPOINTER) pDS,
|
||||||
0,
|
0,
|
||||||
_lengthIndicator.back())))
|
_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,
|
(SQLUSMALLINT) pos + 1,
|
||||||
toODBCDirection(dir),
|
toODBCDirection(dir),
|
||||||
SQL_C_TYPE_TIME,
|
SQL_C_TYPE_TIME,
|
||||||
SQL_TYPE_TIME,
|
Utility::sqlDataType(SQL_C_TYPE_TIME),
|
||||||
colSize,
|
colSize,
|
||||||
decDigits,
|
decDigits,
|
||||||
(SQLPOINTER) pTS,
|
(SQLPOINTER) pTS,
|
||||||
0,
|
0,
|
||||||
_lengthIndicator.back())))
|
_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,
|
(SQLUSMALLINT) pos + 1,
|
||||||
toODBCDirection(dir),
|
toODBCDirection(dir),
|
||||||
SQL_C_TYPE_TIMESTAMP,
|
SQL_C_TYPE_TIMESTAMP,
|
||||||
SQL_TYPE_TIMESTAMP,
|
Utility::sqlDataType(SQL_C_TYPE_TIMESTAMP),
|
||||||
colSize,
|
colSize,
|
||||||
decDigits,
|
decDigits,
|
||||||
(SQLPOINTER) pTS,
|
(SQLPOINTER) pTS,
|
||||||
0,
|
0,
|
||||||
_lengthIndicator.back())))
|
_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,
|
0,
|
||||||
_lengthIndicator.back())))
|
_lengthIndicator.back())))
|
||||||
{
|
{
|
||||||
throw StatementException(_rStmt, "SQLBindParameter()");
|
throw StatementException(_rStmt, "ODBC::Binder::bind(NullData):SQLBindParameter()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,26 +510,29 @@ void Binder::getColSizeAndPrecision(std::size_t pos,
|
|||||||
SQLSMALLINT& decDigits,
|
SQLSMALLINT& decDigits,
|
||||||
std::size_t actualSize)
|
std::size_t actualSize)
|
||||||
{
|
{
|
||||||
|
colSize = 0;
|
||||||
|
decDigits = 0;
|
||||||
|
|
||||||
// Not all drivers are equally willing to cooperate in this matter.
|
// Not all drivers are equally willing to cooperate in this matter.
|
||||||
// Hence the funky flow control.
|
// Hence the funky flow control.
|
||||||
DynamicAny tmp;
|
|
||||||
bool found(false);
|
|
||||||
if (_pTypeInfo)
|
if (_pTypeInfo)
|
||||||
{
|
{
|
||||||
found = _pTypeInfo->tryGetInfo(cDataType, "COLUMN_SIZE", tmp);
|
DynamicAny tmp;
|
||||||
if (found) colSize = tmp;
|
bool foundSize(false);
|
||||||
if (found && actualSize > colSize)
|
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)",
|
throw LengthExceededException(Poco::format("Error binding column %z size=%z, max size=%ld)",
|
||||||
pos, actualSize, static_cast<long>(colSize)));
|
pos, actualSize, static_cast<long>(colSize)));
|
||||||
}
|
}
|
||||||
found = _pTypeInfo->tryGetInfo(cDataType, "MINIMUM_SCALE", tmp);
|
foundPrec = _pTypeInfo->tryGetInfo(cDataType, "MAXIMUM_SCALE", tmp);
|
||||||
if (found)
|
if (foundPrec) decDigits = tmp;
|
||||||
{
|
|
||||||
decDigits = tmp;
|
if (foundSize && foundPrec)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -560,14 +563,30 @@ void Binder::getColSizeAndPrecision(std::size_t pos,
|
|||||||
pos, actualSize, static_cast<long>(colSize)));
|
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;
|
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)
|
void Binder::getColumnOrParameterSize(std::size_t pos, SQLINTEGER& size)
|
||||||
{
|
{
|
||||||
std::size_t colSize = 0;
|
std::size_t colSize = 0;
|
||||||
@ -585,23 +604,10 @@ void Binder::getColumnOrParameterSize(std::size_t pos, SQLINTEGER& size)
|
|||||||
Parameter p(_rStmt, pos);
|
Parameter p(_rStmt, pos);
|
||||||
paramSize = p.columnSize();
|
paramSize = p.columnSize();
|
||||||
}
|
}
|
||||||
catch (StatementException&)
|
catch (StatementException&) {}
|
||||||
{
|
|
||||||
size = DEFAULT_PARAM_SIZE;
|
if (colSize == 0 && paramSize == 0)
|
||||||
//On Linux, PostgreSQL driver segfaults on SQLGetDescField, so this is disabled for now
|
paramSize = getParamSizeDirect(pos, size);
|
||||||
#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
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colSize > 0 && paramSize > 0)
|
if (colSize > 0 && paramSize > 0)
|
||||||
size = colSize < paramSize ? static_cast<SQLINTEGER>(colSize) : static_cast<SQLINTEGER>(paramSize);
|
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)) ||
|
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)))
|
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);
|
_paramSetSize = static_cast<SQLINTEGER>(length);
|
||||||
}
|
}
|
||||||
|
@ -24,17 +24,12 @@ namespace Data {
|
|||||||
namespace ODBC {
|
namespace ODBC {
|
||||||
|
|
||||||
|
|
||||||
ConnectionHandle::ConnectionHandle(EnvironmentHandle* pEnvironment):
|
ConnectionHandle::ConnectionHandle(const std::string& connectString, SQLULEN timeout): _pEnvironment(nullptr),
|
||||||
_pEnvironment(pEnvironment ? pEnvironment : new EnvironmentHandle),
|
|
||||||
_hdbc(SQL_NULL_HDBC),
|
_hdbc(SQL_NULL_HDBC),
|
||||||
_ownsEnvironment(pEnvironment ? false : true)
|
_connectString(connectString)
|
||||||
{
|
{
|
||||||
if (Utility::isError(SQLAllocHandle(SQL_HANDLE_DBC,
|
alloc();
|
||||||
_pEnvironment->handle(),
|
setTimeout(timeout);
|
||||||
&_hdbc)))
|
|
||||||
{
|
|
||||||
throw ODBCException("Could not allocate connection handle.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -42,13 +37,7 @@ ConnectionHandle::~ConnectionHandle()
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SQLDisconnect(_hdbc);
|
disconnect();
|
||||||
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
|
|
||||||
}
|
}
|
||||||
catch (...)
|
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
|
} } } // namespace Poco::Data::ODBC
|
||||||
|
@ -26,15 +26,15 @@ namespace ODBC {
|
|||||||
|
|
||||||
EnvironmentHandle::EnvironmentHandle(): _henv(SQL_NULL_HENV)
|
EnvironmentHandle::EnvironmentHandle(): _henv(SQL_NULL_HENV)
|
||||||
{
|
{
|
||||||
if (Utility::isError(SQLAllocHandle(SQL_HANDLE_ENV,
|
SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &_henv);
|
||||||
SQL_NULL_HANDLE,
|
if (Utility::isError(rc))
|
||||||
&_henv)) ||
|
throw ODBCException("EnvironmentHandle: Could not initialize ODBC environment.");
|
||||||
Utility::isError(SQLSetEnvAttr(_henv,
|
|
||||||
SQL_ATTR_ODBC_VERSION,
|
rc = SQLSetEnvAttr(_henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);
|
||||||
(SQLPOINTER) SQL_OV_ODBC3,
|
if (Utility::isError(rc))
|
||||||
0)))
|
|
||||||
{
|
{
|
||||||
throw ODBCException("Could not initialize environment.");
|
EnvironmentError err(_henv);
|
||||||
|
throw ODBCException(err.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,10 +43,15 @@ EnvironmentHandle::~EnvironmentHandle()
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SQLRETURN rc = SQLFreeHandle(SQL_HANDLE_ENV, _henv);
|
|
||||||
#if defined(_DEBUG)
|
#if defined(_DEBUG)
|
||||||
|
SQLRETURN rc = SQLFreeHandle(SQL_HANDLE_ENV, _henv);
|
||||||
if (Utility::isError(rc))
|
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
|
#endif
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "Poco/Data/ODBC/ODBCException.h"
|
#include "Poco/Data/ODBC/ODBCException.h"
|
||||||
#include "Poco/Data/LOB.h"
|
#include "Poco/Data/LOB.h"
|
||||||
#include "Poco/Buffer.h"
|
#include "Poco/Buffer.h"
|
||||||
|
#include "Poco/Exception.h"
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
|
|
||||||
|
|
||||||
@ -300,10 +301,10 @@ bool Extractor::extractManualImpl<std::string>(std::size_t pos, std::string& val
|
|||||||
&len); //length indicator
|
&len); //length indicator
|
||||||
|
|
||||||
if (SQL_NO_DATA != rc && Utility::isError(rc))
|
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
|
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))
|
if (isNullLengthIndicator(len))
|
||||||
{
|
{
|
||||||
@ -355,10 +356,10 @@ bool Extractor::extractManualImpl<UTF16String>(std::size_t pos, UTF16String& val
|
|||||||
&len); //length indicator
|
&len); //length indicator
|
||||||
|
|
||||||
if (SQL_NO_DATA != rc && Utility::isError(rc))
|
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
|
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))
|
if (isNullLengthIndicator(len))
|
||||||
{
|
{
|
||||||
@ -414,10 +415,10 @@ bool Extractor::extractManualImpl<Poco::Data::CLOB>(std::size_t pos,
|
|||||||
_lengths[pos] += len;
|
_lengths[pos] += len;
|
||||||
|
|
||||||
if (SQL_NO_DATA != rc && Utility::isError(rc))
|
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
|
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))
|
if (isNullLengthIndicator(len))
|
||||||
return false;
|
return false;
|
||||||
@ -454,7 +455,7 @@ bool Extractor::extractManualImpl<Poco::Data::Date>(std::size_t pos,
|
|||||||
&_lengths[pos]); //length indicator
|
&_lengths[pos]); //length indicator
|
||||||
|
|
||||||
if (Utility::isError(rc))
|
if (Utility::isError(rc))
|
||||||
throw StatementException(_rStmt, "SQLGetData()");
|
throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl(Date):SQLGetData()");
|
||||||
|
|
||||||
if (isNullLengthIndicator(_lengths[pos]))
|
if (isNullLengthIndicator(_lengths[pos]))
|
||||||
return false;
|
return false;
|
||||||
@ -481,7 +482,7 @@ bool Extractor::extractManualImpl<Poco::Data::Time>(std::size_t pos,
|
|||||||
&_lengths[pos]); //length indicator
|
&_lengths[pos]); //length indicator
|
||||||
|
|
||||||
if (Utility::isError(rc))
|
if (Utility::isError(rc))
|
||||||
throw StatementException(_rStmt, "SQLGetData()");
|
throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl(Time):SQLGetData()");
|
||||||
|
|
||||||
if (isNullLengthIndicator(_lengths[pos]))
|
if (isNullLengthIndicator(_lengths[pos]))
|
||||||
return false;
|
return false;
|
||||||
@ -508,7 +509,7 @@ bool Extractor::extractManualImpl<Poco::DateTime>(std::size_t pos,
|
|||||||
&_lengths[pos]); //length indicator
|
&_lengths[pos]); //length indicator
|
||||||
|
|
||||||
if (Utility::isError(rc))
|
if (Utility::isError(rc))
|
||||||
throw StatementException(_rStmt, "SQLGetData()");
|
throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl(DateTime):SQLGetData()");
|
||||||
|
|
||||||
if (isNullLengthIndicator(_lengths[pos]))
|
if (isNullLengthIndicator(_lengths[pos]))
|
||||||
return false;
|
return false;
|
||||||
|
@ -53,7 +53,7 @@ void ODBCMetaColumn::getDescription()
|
|||||||
&_columnDesc.decimalDigits,
|
&_columnDesc.decimalDigits,
|
||||||
&_columnDesc.isNullable)))
|
&_columnDesc.isNullable)))
|
||||||
{
|
{
|
||||||
throw StatementException(_rStmt);
|
throw StatementException(_rStmt, "ODBCMetaColumn::getDescription()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ bool ODBCMetaColumn::isUnsigned() const
|
|||||||
0,
|
0,
|
||||||
&val)))
|
&val)))
|
||||||
{
|
{
|
||||||
throw StatementException(_rStmt);
|
throw StatementException(_rStmt, "ODBCMetaColumn::isUnsigned()");
|
||||||
}
|
}
|
||||||
return (val == SQL_TRUE);
|
return (val == SQL_TRUE);
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ void ODBCMetaColumn::init()
|
|||||||
0,
|
0,
|
||||||
&_dataLength)))
|
&_dataLength)))
|
||||||
{
|
{
|
||||||
throw StatementException(_rStmt);
|
throw StatementException(_rStmt, "ODBCMetaColumn::init()");
|
||||||
}
|
}
|
||||||
|
|
||||||
setName(std::string((char*) _columnDesc.name));
|
setName(std::string((char*) _columnDesc.name));
|
||||||
|
@ -47,13 +47,19 @@ ODBCStatementImpl::ODBCStatementImpl(SessionImpl& rSession):
|
|||||||
_canCompile(true)
|
_canCompile(true)
|
||||||
{
|
{
|
||||||
int queryTimeout = rSession.queryTimeout();
|
int queryTimeout = rSession.queryTimeout();
|
||||||
|
int rc = 0;
|
||||||
if (queryTimeout >= 0)
|
if (queryTimeout >= 0)
|
||||||
{
|
{
|
||||||
SQLULEN uqt = static_cast<SQLULEN>(queryTimeout);
|
SQLULEN uqt = static_cast<SQLULEN>(queryTimeout);
|
||||||
SQLSetStmtAttr(_stmt,
|
rc = SQLSetStmtAttr(_stmt,
|
||||||
SQL_ATTR_QUERY_TIMEOUT,
|
SQL_ATTR_QUERY_TIMEOUT,
|
||||||
(SQLPOINTER) uqt,
|
(SQLPOINTER) uqt,
|
||||||
0);
|
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"),
|
_pBinder = new Binder(_stmt, maxFieldSize, bind, pDT, TextEncoding::find("UTF-8"),
|
||||||
TextEncoding::find(Poco::RefAnyCast<std::string>(session().getProperty("dbEncoding"))));
|
TextEncoding::find(Poco::RefAnyCast<std::string>(session().getProperty("dbEncoding"))));
|
||||||
@ -139,7 +153,15 @@ void ODBCStatementImpl::addPreparator()
|
|||||||
Preparator::DataExtraction ext = session().getFeature("autoExtract") ?
|
Preparator::DataExtraction ext = session().getFeature("autoExtract") ?
|
||||||
Preparator::DE_BOUND : Preparator::DE_MANUAL;
|
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));
|
_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()
|
void ODBCStatementImpl::bindImpl()
|
||||||
{
|
{
|
||||||
doBind();
|
doBind();
|
||||||
@ -223,15 +277,16 @@ void ODBCStatementImpl::bindImpl()
|
|||||||
SQLRETURN rc = SQLExecute(_stmt);
|
SQLRETURN rc = SQLExecute(_stmt);
|
||||||
|
|
||||||
if (SQL_NEED_DATA == rc) putData();
|
if (SQL_NEED_DATA == rc) putData();
|
||||||
else checkError(rc, "SQLExecute()");
|
else checkError(rc, "ODBCStatementImpl::bindImpl():SQLExecute()");
|
||||||
|
|
||||||
_pBinder->synchronize();
|
_pBinder->synchronize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ODBCStatementImpl::execDirectImpl(const std::string& query)
|
void ODBCStatementImpl::execDirectImpl(const std::string& query)
|
||||||
{
|
{
|
||||||
SQLCHAR * statementText = (SQLCHAR*) query.c_str();
|
SQLCHAR * statementText = (SQLCHAR*) query.c_str();
|
||||||
SQLINTEGER textLength = query.size();
|
SQLINTEGER textLength = static_cast<SQLINTEGER>(query.size());
|
||||||
SQLRETURN rc = SQLExecDirect(_stmt,statementText,textLength);
|
SQLRETURN rc = SQLExecDirect(_stmt,statementText,textLength);
|
||||||
|
|
||||||
checkError(rc, "SQLExecute()");
|
checkError(rc, "SQLExecute()");
|
||||||
@ -251,13 +306,13 @@ void ODBCStatementImpl::putData()
|
|||||||
dataSize = (SQLINTEGER) _pBinder->parameterSize(pParam);
|
dataSize = (SQLINTEGER) _pBinder->parameterSize(pParam);
|
||||||
|
|
||||||
if (Utility::isError(SQLPutData(_stmt, pParam, dataSize)))
|
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
|
else // if pParam is null pointer, do a dummy call
|
||||||
{
|
{
|
||||||
char dummy = 0;
|
char dummy = 0;
|
||||||
if (Utility::isError(SQLPutData(_stmt, &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()
|
void ODBCStatementImpl::clear()
|
||||||
{
|
{
|
||||||
SQLRETURN rc = SQLCloseCursor(_stmt);
|
|
||||||
_stepCalled = false;
|
_stepCalled = false;
|
||||||
_affectedRowCount = 0;
|
_affectedRowCount = 0;
|
||||||
|
_errorInfo.clear();
|
||||||
|
SQLRETURN rc = SQLFreeStmt(_stmt, SQL_CLOSE);
|
||||||
if (Utility::isError(rc))
|
if (Utility::isError(rc))
|
||||||
{
|
throw StatementException(_stmt, "ODBCStatementImpl::putData():SQLFreeStmt(SQL_CLOSE)");
|
||||||
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()");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -335,6 +372,25 @@ void ODBCStatementImpl::makeStep()
|
|||||||
{
|
{
|
||||||
_extractors[currentDataSet()]->reset();
|
_extractors[currentDataSet()]->reset();
|
||||||
_nextResponse = SQLFetch(_stmt);
|
_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);
|
checkError(_nextResponse);
|
||||||
_stepCalled = true;
|
_stepCalled = true;
|
||||||
}
|
}
|
||||||
@ -363,7 +419,7 @@ std::size_t ODBCStatementImpl::next()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw StatementException(_stmt,
|
throw StatementException(_stmt,
|
||||||
std::string("Next row not available."));
|
"ODBCStatementImpl::next():Next row not available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
@ -416,6 +472,7 @@ void ODBCStatementImpl::checkError(SQLRETURN rc, const std::string& msg)
|
|||||||
|
|
||||||
throw StatementException(_stmt, str);
|
throw StatementException(_stmt, str);
|
||||||
}
|
}
|
||||||
|
else if (SQL_SUCCESS_WITH_INFO == rc) addErrors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ void Parameter::init()
|
|||||||
&_decimalDigits,
|
&_decimalDigits,
|
||||||
&_isNullable)))
|
&_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();
|
SQLCHAR* pStr = (SQLCHAR*) statement.c_str();
|
||||||
if (Utility::isError(Poco::Data::ODBC::SQLPrepare(_rStmt, pStr, (SQLINTEGER) statement.length())))
|
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!)
|
// accomodate for terminating zero (non-bulk only!)
|
||||||
MetaColumn::ColumnDataType type = mc.type();
|
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&) { }
|
catch (StatementException&) { }
|
||||||
|
|
||||||
@ -201,7 +202,7 @@ void Preparator::prepareBoolArray(std::size_t pos, SQLSMALLINT valueType, std::s
|
|||||||
(SQLINTEGER) sizeof(bool),
|
(SQLINTEGER) sizeof(bool),
|
||||||
&_lenLengths[pos][0])))
|
&_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")
|
_dbEncoding("UTF-8")
|
||||||
{
|
{
|
||||||
setFeature("bulk", true);
|
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();
|
open();
|
||||||
setProperty("handle", _db.handle());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -63,8 +65,10 @@ SessionImpl::SessionImpl(const std::string& connect,
|
|||||||
_dbEncoding("UTF-8")
|
_dbEncoding("UTF-8")
|
||||||
{
|
{
|
||||||
setFeature("bulk", true);
|
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();
|
open();
|
||||||
setProperty("handle", _db.handle());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -104,38 +108,16 @@ void SessionImpl::open(const std::string& connect)
|
|||||||
setConnectionString(connect);
|
setConnectionString(connect);
|
||||||
}
|
}
|
||||||
|
|
||||||
poco_assert_dbg (!connectionString().empty());
|
if (connectionString().empty())
|
||||||
|
throw InvalidArgumentException("SessionImpl::open(): Connection string empty");
|
||||||
|
|
||||||
SQLULEN tout = static_cast<SQLULEN>(getLoginTimeout());
|
SQLULEN tout = static_cast<SQLULEN>(getLoginTimeout());
|
||||||
if (Utility::isError(SQLSetConnectAttr(_db, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) tout, 0)))
|
|
||||||
{
|
|
||||||
if (Utility::isError(SQLGetConnectAttr(_db, SQL_ATTR_LOGIN_TIMEOUT, &tout, 0, 0)) ||
|
|
||||||
getLoginTimeout() != tout)
|
|
||||||
{
|
|
||||||
ConnectionError e(_db);
|
|
||||||
throw ConnectionFailedException(e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLCHAR connectOutput[512] = {0};
|
if (_db.connect(connectionString(), tout))
|
||||||
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);
|
setProperty("handle", _db.handle());
|
||||||
std::string errStr = err.toString();
|
|
||||||
close();
|
|
||||||
throw ConnectionFailedException(errStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
_dataTypes.fillTypeInfo(_db);
|
_dataTypes.fillTypeInfo(_db.pHandle());
|
||||||
addProperty("dataTypeInfo",
|
addProperty("dataTypeInfo",
|
||||||
&SessionImpl::setDataTypeInfo,
|
&SessionImpl::setDataTypeInfo,
|
||||||
&SessionImpl::dataTypeInfo);
|
&SessionImpl::dataTypeInfo);
|
||||||
@ -167,6 +149,10 @@ void SessionImpl::open(const std::string& connect)
|
|||||||
Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_QUIET_MODE, 0, 0);
|
Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_QUIET_MODE, 0, 0);
|
||||||
|
|
||||||
if (!canTransact()) autoCommit("", true);
|
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
|
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,
|
SQL_ATTR_AUTOCOMMIT,
|
||||||
val ? (SQLPOINTER) SQL_AUTOCOMMIT_ON :
|
val ? (SQLPOINTER) SQL_AUTOCOMMIT_ON :
|
||||||
(SQLPOINTER) SQL_AUTOCOMMIT_OFF,
|
(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
|
bool SessionImpl::isAutoCommit(const std::string&) const
|
||||||
{
|
{
|
||||||
|
if (!_db) return false;
|
||||||
|
|
||||||
SQLULEN value = 0;
|
SQLULEN value = 0;
|
||||||
|
|
||||||
checkError(Poco::Data::ODBC::SQLGetConnectAttr(_db,
|
checkError(Poco::Data::ODBC::SQLGetConnectAttr(_db,
|
||||||
@ -353,7 +388,7 @@ bool SessionImpl::isAutoCommit(const std::string&) const
|
|||||||
|
|
||||||
bool SessionImpl::isTransaction() const
|
bool SessionImpl::isTransaction() const
|
||||||
{
|
{
|
||||||
if (!canTransact()) return false;
|
if (!_db || !canTransact()) return false;
|
||||||
|
|
||||||
SQLULEN value = 0;
|
SQLULEN value = 0;
|
||||||
checkError(Poco::Data::ODBC::SQLGetConnectAttr(_db,
|
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();
|
fillCTypes();
|
||||||
fillSQLTypes();
|
fillSQLTypes();
|
||||||
if (_pHDBC) fillTypeInfo(*_pHDBC);
|
if (_pHDBC) fillTypeInfo(_pHDBC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -62,7 +62,8 @@ void TypeInfo::fillCTypes()
|
|||||||
|
|
||||||
void TypeInfo::fillSQLTypes()
|
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_BIT, SQL_BIT));
|
||||||
_sqlDataTypes.insert(ValueType(SQL_C_TINYINT, SQL_TINYINT));
|
_sqlDataTypes.insert(ValueType(SQL_C_TINYINT, SQL_TINYINT));
|
||||||
_sqlDataTypes.insert(ValueType(SQL_C_STINYINT, 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)
|
if (_typeInfo.empty() && _pHDBC)
|
||||||
{
|
{
|
||||||
@ -98,7 +99,7 @@ void TypeInfo::fillTypeInfo(SQLHDBC pHDBC)
|
|||||||
|
|
||||||
rc = SQLAllocHandle(SQL_HANDLE_STMT, *_pHDBC, &hstmt);
|
rc = SQLAllocHandle(SQL_HANDLE_STMT, *_pHDBC, &hstmt);
|
||||||
if (!SQL_SUCCEEDED(rc))
|
if (!SQL_SUCCEEDED(rc))
|
||||||
throw StatementException(hstmt, "SQLGetData()");
|
throw StatementException(hstmt, "ODBC::Preparator::fillTypeInfo():SQLGetData()");
|
||||||
|
|
||||||
rc = SQLGetTypeInfo(hstmt, SQL_ALL_TYPES);
|
rc = SQLGetTypeInfo(hstmt, SQL_ALL_TYPES);
|
||||||
if (SQL_SUCCEEDED(rc))
|
if (SQL_SUCCEEDED(rc))
|
||||||
|
@ -594,6 +594,9 @@ CppUnit::Test* ODBCDB2Test::suite()
|
|||||||
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCDB2Test");
|
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCDB2Test");
|
||||||
|
|
||||||
CppUnit_addTest(pSuite, ODBCDB2Test, testBareboneODBC);
|
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, testZeroRows);
|
||||||
CppUnit_addTest(pSuite, ODBCDB2Test, testSimpleAccess);
|
CppUnit_addTest(pSuite, ODBCDB2Test, testSimpleAccess);
|
||||||
CppUnit_addTest(pSuite, ODBCDB2Test, testComplexType);
|
CppUnit_addTest(pSuite, ODBCDB2Test, testComplexType);
|
||||||
|
@ -421,6 +421,9 @@ CppUnit::Test* ODBCMySQLTest::suite()
|
|||||||
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCMySQLTest");
|
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCMySQLTest");
|
||||||
|
|
||||||
CppUnit_addTest(pSuite, ODBCMySQLTest, testBareboneODBC);
|
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, testZeroRows);
|
||||||
CppUnit_addTest(pSuite, ODBCMySQLTest, testSimpleAccess);
|
CppUnit_addTest(pSuite, ODBCMySQLTest, testSimpleAccess);
|
||||||
CppUnit_addTest(pSuite, ODBCMySQLTest, testComplexType);
|
CppUnit_addTest(pSuite, ODBCMySQLTest, testComplexType);
|
||||||
|
@ -854,6 +854,9 @@ CppUnit::Test* ODBCOracleTest::suite()
|
|||||||
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCOracleTest");
|
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCOracleTest");
|
||||||
|
|
||||||
CppUnit_addTest(pSuite, ODBCOracleTest, testBareboneODBC);
|
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, testZeroRows);
|
||||||
CppUnit_addTest(pSuite, ODBCOracleTest, testSimpleAccess);
|
CppUnit_addTest(pSuite, ODBCOracleTest, testSimpleAccess);
|
||||||
CppUnit_addTest(pSuite, ODBCOracleTest, testComplexType);
|
CppUnit_addTest(pSuite, ODBCOracleTest, testComplexType);
|
||||||
|
@ -583,6 +583,9 @@ CppUnit::Test* ODBCPostgreSQLTest::suite()
|
|||||||
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCPostgreSQLTest");
|
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCPostgreSQLTest");
|
||||||
|
|
||||||
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testBareboneODBC);
|
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, testZeroRows);
|
||||||
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSimpleAccess);
|
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSimpleAccess);
|
||||||
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testComplexType);
|
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testComplexType);
|
||||||
|
@ -47,39 +47,26 @@ using Poco::DateTime;
|
|||||||
// uncomment to use native SQL driver
|
// uncomment to use native SQL driver
|
||||||
//#define POCO_ODBC_USE_SQL_NATIVE
|
//#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)
|
// (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
|
#ifdef POCO_ODBC_USE_SQL_NATIVE
|
||||||
#define MS_SQL_SERVER_ODBC_DRIVER "SQL Server Native Client 10.0"
|
#define MS_SQL_SERVER_ODBC_DRIVER "SQL Server Native Client 10.0"
|
||||||
#else
|
#else
|
||||||
#define MS_SQL_SERVER_ODBC_DRIVER "SQL Server"
|
#define MS_SQL_SERVER_ODBC_DRIVER "ODBC Driver 18 for SQL Server"
|
||||||
#endif
|
#endif
|
||||||
#pragma message ("Using " MS_SQL_SERVER_ODBC_DRIVER " driver")
|
#pragma message ("Using " MS_SQL_SERVER_ODBC_DRIVER " driver")
|
||||||
#else
|
#else
|
||||||
#define MS_SQL_SERVER_ODBC_DRIVER "FreeTDS"
|
#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)
|
#if defined(POCO_OS_FAMILY_WINDOWS)
|
||||||
#pragma message ("Using " MS_SQL_SERVER_ODBC_DRIVER " driver, version " FREE_TDS_VERSION)
|
#pragma message ("Using " MS_SQL_SERVER_ODBC_DRIVER " driver, version " FREE_TDS_VERSION)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MS_SQL_SERVER_DSN "PocoDataSQLServerTest"
|
#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_PORT "1433"
|
||||||
#define MS_SQL_SERVER_DB "poco"
|
#define MS_SQL_SERVER_DB "poco"
|
||||||
#define MS_SQL_SERVER_UID "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 ";"
|
"DATABASE=" MS_SQL_SERVER_DB ";"
|
||||||
"SERVER=" MS_SQL_SERVER_SERVER ";"
|
"SERVER=" MS_SQL_SERVER_SERVER ";"
|
||||||
"PORT=" MS_SQL_SERVER_PORT ";"
|
"PORT=" MS_SQL_SERVER_PORT ";"
|
||||||
|
"Encrypt=no"
|
||||||
#ifdef FREE_TDS_VERSION
|
#ifdef FREE_TDS_VERSION
|
||||||
"TDS_Version=" FREE_TDS_VERSION ";"
|
"TDS_Version=" FREE_TDS_VERSION ";"
|
||||||
#endif
|
#endif
|
||||||
@ -120,7 +108,7 @@ ODBCSQLServerTest::~ODBCSQLServerTest()
|
|||||||
|
|
||||||
void ODBCSQLServerTest::testBareboneODBC()
|
void ODBCSQLServerTest::testBareboneODBC()
|
||||||
{
|
{
|
||||||
std::string tableCreateString = "CREATE TABLE Test "
|
std::string createString = "CREATE TABLE Test "
|
||||||
"(First VARCHAR(30),"
|
"(First VARCHAR(30),"
|
||||||
"Second VARCHAR(30),"
|
"Second VARCHAR(30),"
|
||||||
"Third VARBINARY(30),"
|
"Third VARBINARY(30),"
|
||||||
@ -128,24 +116,47 @@ void ODBCSQLServerTest::testBareboneODBC()
|
|||||||
"Fifth FLOAT,"
|
"Fifth FLOAT,"
|
||||||
"Sixth DATETIME)";
|
"Sixth DATETIME)";
|
||||||
|
|
||||||
executor().bareboneODBCTest(dbConnString(), tableCreateString,
|
executor().bareboneODBCTest(dbConnString(), createString,
|
||||||
SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_MANUAL, true, "CONVERT(VARBINARY(30),?)");
|
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),?)");
|
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),?)");
|
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),?)");
|
SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND, true, "CONVERT(VARBINARY(30),?)");
|
||||||
|
|
||||||
tableCreateString = "CREATE TABLE Test "
|
createString = "CREATE TABLE Test "
|
||||||
"(First VARCHAR(30),"
|
"(First VARCHAR(30),"
|
||||||
"Second INTEGER,"
|
"Second INTEGER,"
|
||||||
"Third FLOAT)";
|
"Third FLOAT)";
|
||||||
|
|
||||||
executor().bareboneODBCMultiResultTest(dbConnString(), tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_MANUAL);
|
executor().bareboneODBCMultiResultTest(dbConnString(), createString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_MANUAL);
|
||||||
executor().bareboneODBCMultiResultTest(dbConnString(), tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND);
|
executor().bareboneODBCMultiResultTest(dbConnString(), createString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND);
|
||||||
executor().bareboneODBCMultiResultTest(dbConnString(), tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL);
|
executor().bareboneODBCMultiResultTest(dbConnString(), createString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL);
|
||||||
executor().bareboneODBCMultiResultTest(dbConnString(), tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND);
|
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()
|
void ODBCSQLServerTest::testTempTable()
|
||||||
@ -161,13 +172,14 @@ void ODBCSQLServerTest::testTempTable()
|
|||||||
std::vector<testParam> testParams;
|
std::vector<testParam> testParams;
|
||||||
session() << ("select * from #test;"), into(testParams), now;
|
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(1, testParams.front().get<0>());
|
||||||
assertEquals(2, testParams.front().get<1>());
|
assertEquals(2, testParams.front().get<1>());
|
||||||
assertEquals(3, testParams.front().get<2>());
|
assertEquals(3, testParams.front().get<2>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ODBCSQLServerTest::testBLOB()
|
void ODBCSQLServerTest::testBLOB()
|
||||||
{
|
{
|
||||||
const std::size_t maxFldSize = 250000;
|
const std::size_t maxFldSize = 250000;
|
||||||
@ -262,27 +274,29 @@ void ODBCSQLServerTest::testBulk()
|
|||||||
|
|
||||||
void ODBCSQLServerTest::testStoredProcedure()
|
void ODBCSQLServerTest::testStoredProcedure()
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
for (int k = 0; k < 8;)
|
for (int k = 0; k < 8;)
|
||||||
{
|
{
|
||||||
session().setFeature("autoBind", bindValue(k));
|
session().setFeature("autoBind", bindValue(k));
|
||||||
session().setFeature("autoExtract", bindValue(k+1));
|
session().setFeature("autoExtract", bindValue(k + 1));
|
||||||
|
|
||||||
dropObject("PROCEDURE", "storedProcedure");
|
dropObject("PROCEDURE", "storedProcedure");
|
||||||
|
|
||||||
session() << "CREATE PROCEDURE storedProcedure(@outParam int OUTPUT) AS "
|
session() << "CREATE PROCEDURE storedProcedure(@outParam int OUTPUT) AS "
|
||||||
"BEGIN "
|
"BEGIN "
|
||||||
"SET @outParam = -1; "
|
" SET @outParam = -1; "
|
||||||
"END;"
|
"END;"
|
||||||
, now;
|
, now;
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
session() << "{call storedProcedure(?)}", out(i), now;
|
session() << "{call storedProcedure(?)}", out(i), now;
|
||||||
assertTrue (-1 == i);
|
assertTrue (-1 == i);
|
||||||
dropObject("PROCEDURE", "storedProcedure");
|
|
||||||
|
|
||||||
|
dropObject("PROCEDURE", "storedProcedure");
|
||||||
session() << "CREATE PROCEDURE storedProcedure(@inParam int, @outParam int OUTPUT) AS "
|
session() << "CREATE PROCEDURE storedProcedure(@inParam int, @outParam int OUTPUT) AS "
|
||||||
"BEGIN "
|
"BEGIN "
|
||||||
"SET @outParam = @inParam*@inParam; "
|
" SET @outParam = @inParam*@inParam; "
|
||||||
"END;"
|
"END;"
|
||||||
, now;
|
, now;
|
||||||
|
|
||||||
@ -290,11 +304,11 @@ void ODBCSQLServerTest::testStoredProcedure()
|
|||||||
int j = 0;
|
int j = 0;
|
||||||
session() << "{call storedProcedure(?, ?)}", in(i), out(j), now;
|
session() << "{call storedProcedure(?, ?)}", in(i), out(j), now;
|
||||||
assertTrue (4 == j);
|
assertTrue (4 == j);
|
||||||
dropObject("PROCEDURE", "storedProcedure");
|
|
||||||
|
|
||||||
|
dropObject("PROCEDURE", "storedProcedure");
|
||||||
session() << "CREATE PROCEDURE storedProcedure(@ioParam int OUTPUT) AS "
|
session() << "CREATE PROCEDURE storedProcedure(@ioParam int OUTPUT) AS "
|
||||||
"BEGIN "
|
"BEGIN "
|
||||||
"SET @ioParam = @ioParam*@ioParam; "
|
" SET @ioParam = @ioParam*@ioParam; "
|
||||||
"END;"
|
"END;"
|
||||||
, now;
|
, now;
|
||||||
|
|
||||||
@ -313,34 +327,31 @@ void ODBCSQLServerTest::testStoredProcedure()
|
|||||||
assertTrue (19 == dt.day());
|
assertTrue (19 == dt.day());
|
||||||
dropObject("PROCEDURE", "storedProcedure");
|
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().setFeature("autoBind", true);
|
||||||
session() << "CREATE PROCEDURE storedProcedure(@inParam VARCHAR(MAX), @outParam VARCHAR(MAX) OUTPUT) AS "
|
session() << "CREATE PROCEDURE storedProcedure(@inParam VARCHAR(MAX), @outParam VARCHAR(MAX) OUTPUT) AS "
|
||||||
"BEGIN "
|
"BEGIN "
|
||||||
"SET @outParam = @inParam; "
|
" SET @outParam = @inParam; "
|
||||||
"END;"
|
"END;"
|
||||||
, now;
|
, now;
|
||||||
|
|
||||||
std::string inParam = "123";
|
std::string inParam = "123";
|
||||||
std::string outParam;
|
std::string outParam(4, 0);
|
||||||
try{
|
|
||||||
session() << "{call storedProcedure(?, ?)}", in(inParam), out(outParam), now;
|
session() << "{call storedProcedure(?, ?)}", in(inParam), out(outParam), now;
|
||||||
}catch(StatementException& ex){std::cout << ex.toString();}
|
assertTrue(outParam == inParam);
|
||||||
assertTrue (outParam == inParam);
|
|
||||||
|
k += 2;
|
||||||
|
}
|
||||||
dropObject("PROCEDURE", "storedProcedure");
|
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()
|
void ODBCSQLServerTest::testCursorStoredProcedure()
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
for (int k = 0; k < 8;)
|
for (int k = 0; k < 8;)
|
||||||
{
|
{
|
||||||
session().setFeature("autoBind", bindValue(k));
|
session().setFeature("autoBind", bindValue(k));
|
||||||
@ -380,16 +391,58 @@ void ODBCSQLServerTest::testCursorStoredProcedure()
|
|||||||
assertTrue (rs["Address"] == "Springfield");
|
assertTrue (rs["Address"] == "Springfield");
|
||||||
assertTrue (rs["Age"] == 12);
|
assertTrue (rs["Age"] == 12);
|
||||||
|
|
||||||
dropObject("TABLE", "Person");
|
|
||||||
dropObject("PROCEDURE", "storedCursorProcedure");
|
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;
|
k += 2;
|
||||||
}
|
}
|
||||||
|
dropObject("PROCEDURE", "storedCursorProcedure");
|
||||||
|
}
|
||||||
|
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()
|
void ODBCSQLServerTest::testStoredProcedureAny()
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
for (int k = 0; k < 8;)
|
for (int k = 0; k < 8;)
|
||||||
{
|
{
|
||||||
session().setFeature("autoBind", bindValue(k));
|
session().setFeature("autoBind", bindValue(k));
|
||||||
@ -398,6 +451,7 @@ void ODBCSQLServerTest::testStoredProcedureAny()
|
|||||||
Any i = 2;
|
Any i = 2;
|
||||||
Any j = 0;
|
Any j = 0;
|
||||||
|
|
||||||
|
dropObject("PROCEDURE", "storedProcedure");
|
||||||
session() << "CREATE PROCEDURE storedProcedure(@inParam int, @outParam int OUTPUT) AS "
|
session() << "CREATE PROCEDURE storedProcedure(@inParam int, @outParam int OUTPUT) AS "
|
||||||
"BEGIN "
|
"BEGIN "
|
||||||
"SET @outParam = @inParam*@inParam; "
|
"SET @outParam = @inParam*@inParam; "
|
||||||
@ -406,8 +460,8 @@ void ODBCSQLServerTest::testStoredProcedureAny()
|
|||||||
|
|
||||||
session() << "{call storedProcedure(?, ?)}", in(i), out(j), now;
|
session() << "{call storedProcedure(?, ?)}", in(i), out(j), now;
|
||||||
assertTrue (4 == AnyCast<int>(j));
|
assertTrue (4 == AnyCast<int>(j));
|
||||||
session() << "DROP PROCEDURE storedProcedure;", now;
|
|
||||||
|
|
||||||
|
dropObject("PROCEDURE", "storedProcedure");
|
||||||
session() << "CREATE PROCEDURE storedProcedure(@ioParam int OUTPUT) AS "
|
session() << "CREATE PROCEDURE storedProcedure(@ioParam int OUTPUT) AS "
|
||||||
"BEGIN "
|
"BEGIN "
|
||||||
"SET @ioParam = @ioParam*@ioParam; "
|
"SET @ioParam = @ioParam*@ioParam; "
|
||||||
@ -421,11 +475,17 @@ void ODBCSQLServerTest::testStoredProcedureAny()
|
|||||||
|
|
||||||
k += 2;
|
k += 2;
|
||||||
}
|
}
|
||||||
|
dropObject("PROCEDURE", "storedProcedure");
|
||||||
|
}
|
||||||
|
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()
|
void ODBCSQLServerTest::testStoredProcedureDynamicAny()
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
for (int k = 0; k < 8;)
|
for (int k = 0; k < 8;)
|
||||||
{
|
{
|
||||||
session().setFeature("autoBind", bindValue(k));
|
session().setFeature("autoBind", bindValue(k));
|
||||||
@ -433,6 +493,7 @@ void ODBCSQLServerTest::testStoredProcedureDynamicAny()
|
|||||||
DynamicAny i = 2;
|
DynamicAny i = 2;
|
||||||
DynamicAny j = 0;
|
DynamicAny j = 0;
|
||||||
|
|
||||||
|
dropObject("PROCEDURE", "storedProcedure");
|
||||||
session() << "CREATE PROCEDURE storedProcedure(@inParam int, @outParam int OUTPUT) AS "
|
session() << "CREATE PROCEDURE storedProcedure(@inParam int, @outParam int OUTPUT) AS "
|
||||||
"BEGIN "
|
"BEGIN "
|
||||||
"SET @outParam = @inParam*@inParam; "
|
"SET @outParam = @inParam*@inParam; "
|
||||||
@ -441,8 +502,8 @@ void ODBCSQLServerTest::testStoredProcedureDynamicAny()
|
|||||||
|
|
||||||
session() << "{call storedProcedure(?, ?)}", in(i), out(j), now;
|
session() << "{call storedProcedure(?, ?)}", in(i), out(j), now;
|
||||||
assertTrue (4 == j);
|
assertTrue (4 == j);
|
||||||
session() << "DROP PROCEDURE storedProcedure;", now;
|
|
||||||
|
|
||||||
|
dropObject("PROCEDURE", "storedProcedure");
|
||||||
session() << "CREATE PROCEDURE storedProcedure(@ioParam int OUTPUT) AS "
|
session() << "CREATE PROCEDURE storedProcedure(@ioParam int OUTPUT) AS "
|
||||||
"BEGIN "
|
"BEGIN "
|
||||||
"SET @ioParam = @ioParam*@ioParam; "
|
"SET @ioParam = @ioParam*@ioParam; "
|
||||||
@ -452,22 +513,27 @@ void ODBCSQLServerTest::testStoredProcedureDynamicAny()
|
|||||||
i = 2;
|
i = 2;
|
||||||
session() << "{call storedProcedure(?)}", io(i), now;
|
session() << "{call storedProcedure(?)}", io(i), now;
|
||||||
assertTrue (4 == i);
|
assertTrue (4 == i);
|
||||||
dropObject("PROCEDURE", "storedProcedure");
|
|
||||||
|
|
||||||
k += 2;
|
k += 2;
|
||||||
}
|
}
|
||||||
|
dropObject("PROCEDURE", "storedProcedure");
|
||||||
|
}
|
||||||
|
catch (ConnectionException& ce) { std::cout << ce.toString() << std::endl; fail("testStoredProcedureDynamicAny()"); }
|
||||||
|
catch (StatementException& se) { std::cout << se.toString() << std::endl; fail("testStoredProcedureDynamicAny()"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ODBCSQLServerTest::testStoredFunction()
|
void ODBCSQLServerTest::testStoredProcedureReturn()
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
for (int k = 0; k < 8;)
|
for (int k = 0; k < 8;)
|
||||||
{
|
{
|
||||||
session().setFeature("autoBind", bindValue(k));
|
session().setFeature("autoBind", bindValue(k));
|
||||||
session().setFeature("autoExtract", bindValue(k+1));
|
session().setFeature("autoExtract", bindValue(k+1));
|
||||||
|
|
||||||
dropObject("PROCEDURE", "storedFunction");
|
dropObject("PROCEDURE", "storedProcedureReturn");
|
||||||
session() << "CREATE PROCEDURE storedFunction AS "
|
session() << "CREATE PROCEDURE storedProcedureReturn AS "
|
||||||
"BEGIN "
|
"BEGIN "
|
||||||
"DECLARE @retVal int;"
|
"DECLARE @retVal int;"
|
||||||
"SET @retVal = -1;"
|
"SET @retVal = -1;"
|
||||||
@ -476,12 +542,11 @@ void ODBCSQLServerTest::testStoredFunction()
|
|||||||
, now;
|
, now;
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
session() << "{? = call storedFunction}", out(i), now;
|
session() << "{? = call storedProcedureReturn}", out(i), now;
|
||||||
assertTrue (-1 == i);
|
assertTrue (-1 == i);
|
||||||
dropObject("PROCEDURE", "storedFunction");
|
|
||||||
|
|
||||||
|
dropObject("PROCEDURE", "storedProcedureReturn");
|
||||||
session() << "CREATE PROCEDURE storedFunction(@inParam int) AS "
|
session() << "CREATE PROCEDURE storedProcedureReturn(@inParam int) AS "
|
||||||
"BEGIN "
|
"BEGIN "
|
||||||
"RETURN @inParam*@inParam;"
|
"RETURN @inParam*@inParam;"
|
||||||
"END;"
|
"END;"
|
||||||
@ -489,12 +554,11 @@ void ODBCSQLServerTest::testStoredFunction()
|
|||||||
|
|
||||||
i = 2;
|
i = 2;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
session() << "{? = call storedFunction(?)}", out(result), in(i), now;
|
session() << "{? = call storedProcedureReturn(?)}", out(result), in(i), now;
|
||||||
assertTrue (4 == result);
|
assertTrue (4 == result);
|
||||||
dropObject("PROCEDURE", "storedFunction");
|
|
||||||
|
|
||||||
|
dropObject("PROCEDURE", "storedProcedureReturn");
|
||||||
session() << "CREATE PROCEDURE storedFunction(@inParam int, @outParam int OUTPUT) AS "
|
session() << "CREATE PROCEDURE storedProcedureReturn(@inParam int, @outParam int OUTPUT) AS "
|
||||||
"BEGIN "
|
"BEGIN "
|
||||||
"SET @outParam = @inParam*@inParam;"
|
"SET @outParam = @inParam*@inParam;"
|
||||||
"RETURN @outParam;"
|
"RETURN @outParam;"
|
||||||
@ -504,13 +568,12 @@ void ODBCSQLServerTest::testStoredFunction()
|
|||||||
i = 2;
|
i = 2;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
result = 0;
|
result = 0;
|
||||||
session() << "{? = call storedFunction(?, ?)}", out(result), in(i), out(j), now;
|
session() << "{? = call storedProcedureReturn(?, ?)}", out(result), in(i), out(j), now;
|
||||||
assertTrue (4 == j);
|
assertTrue (4 == j);
|
||||||
assertTrue (j == result);
|
assertTrue (j == result);
|
||||||
dropObject("PROCEDURE", "storedFunction");
|
|
||||||
|
|
||||||
|
dropObject("PROCEDURE", "storedProcedureReturn");
|
||||||
session() << "CREATE PROCEDURE storedFunction(@param1 int OUTPUT,@param2 int OUTPUT) AS "
|
session() << "CREATE PROCEDURE storedProcedureReturn(@param1 int OUTPUT,@param2 int OUTPUT) AS "
|
||||||
"BEGIN "
|
"BEGIN "
|
||||||
"DECLARE @temp int; "
|
"DECLARE @temp int; "
|
||||||
"SET @temp = @param1;"
|
"SET @temp = @param1;"
|
||||||
@ -523,7 +586,7 @@ void ODBCSQLServerTest::testStoredFunction()
|
|||||||
i = 1;
|
i = 1;
|
||||||
j = 2;
|
j = 2;
|
||||||
result = 0;
|
result = 0;
|
||||||
session() << "{? = call storedFunction(?, ?)}", out(result), io(i), io(j), now;
|
session() << "{? = call storedProcedureReturn(?, ?)}", out(result), io(i), io(j), now;
|
||||||
assertTrue (1 == j);
|
assertTrue (1 == j);
|
||||||
assertTrue (2 == i);
|
assertTrue (2 == i);
|
||||||
assertTrue (3 == result);
|
assertTrue (3 == result);
|
||||||
@ -532,15 +595,65 @@ void ODBCSQLServerTest::testStoredFunction()
|
|||||||
assertTrue (1 == params.get<0>());
|
assertTrue (1 == params.get<0>());
|
||||||
assertTrue (2 == params.get<1>());
|
assertTrue (2 == params.get<1>());
|
||||||
result = 0;
|
result = 0;
|
||||||
session() << "{? = call storedFunction(?, ?)}", out(result), io(params), now;
|
session() << "{? = call storedProcedureReturn(?, ?)}", out(result), io(params), now;
|
||||||
assertTrue (1 == params.get<1>());
|
assertTrue (1 == params.get<1>());
|
||||||
assertTrue (2 == params.get<0>());
|
assertTrue (2 == params.get<0>());
|
||||||
assertTrue (3 == result);
|
assertTrue (3 == result);
|
||||||
|
|
||||||
dropObject("PROCEDURE", "storedFunction");
|
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()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (int k = 0; k < 8;)
|
||||||
|
{
|
||||||
|
session().setFeature("autoBind", bindValue(k));
|
||||||
|
session().setFeature("autoExtract", bindValue(k + 1));
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
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)
|
catch (StatementException& ex)
|
||||||
{
|
{
|
||||||
bool ignoreError = false;
|
|
||||||
const StatementDiagnostics::FieldVec& flds = ex.diagnostics().fields();
|
const StatementDiagnostics::FieldVec& flds = ex.diagnostics().fields();
|
||||||
StatementDiagnostics::Iterator it = flds.begin();
|
StatementDiagnostics::Iterator it = flds.begin();
|
||||||
for (; it != flds.end(); ++it)
|
for (; it != flds.end(); ++it)
|
||||||
{
|
{
|
||||||
if (3701 == it->_nativeError)//(table does not exist)
|
if (3701 == it->_nativeError) // (does not exist)
|
||||||
{
|
return;
|
||||||
ignoreError = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignoreError) throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,13 +834,13 @@ void ODBCSQLServerTest::recreateLogTable()
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string sql = "CREATE TABLE %s "
|
std::string sql = "CREATE TABLE %s "
|
||||||
"(Source VARCHAR(max),"
|
"(Source VARCHAR(256),"
|
||||||
"Name VARCHAR(max),"
|
"Name VARCHAR(256),"
|
||||||
"ProcessId INTEGER,"
|
"ProcessId INTEGER,"
|
||||||
"Thread VARCHAR(max), "
|
"Thread VARCHAR(256), "
|
||||||
"ThreadId INTEGER,"
|
"ThreadId INTEGER,"
|
||||||
"Priority INTEGER,"
|
"Priority INTEGER,"
|
||||||
"Text VARCHAR(max),"
|
"Text VARCHAR(1024),"
|
||||||
"DateTime DATETIME)";
|
"DateTime DATETIME)";
|
||||||
|
|
||||||
session() << sql, "T_POCO_LOG", now;
|
session() << sql, "T_POCO_LOG", now;
|
||||||
@ -779,6 +888,9 @@ CppUnit::Test* ODBCSQLServerTest::suite()
|
|||||||
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCSQLServerTest");
|
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCSQLServerTest");
|
||||||
|
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testBareboneODBC);
|
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, testZeroRows);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testSimpleAccess);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testSimpleAccess);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testComplexType);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testComplexType);
|
||||||
@ -828,6 +940,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
|
|||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testBLOB);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testBLOB);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testBLOBContainer);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testBLOBContainer);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testBLOBStmt);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testBLOBStmt);
|
||||||
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testRecordSet);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testDateTime);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testDateTime);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testFloat);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testFloat);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testDouble);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testDouble);
|
||||||
@ -838,6 +951,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
|
|||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testCursorStoredProcedure);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testCursorStoredProcedure);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredProcedureAny);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredProcedureAny);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredProcedureDynamicAny);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredProcedureDynamicAny);
|
||||||
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredProcedureReturn);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredFunction);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredFunction);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalExtraction);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalExtraction);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testFilter);
|
CppUnit_addTest(pSuite, ODBCSQLServerTest, testFilter);
|
||||||
|
@ -26,13 +26,14 @@ class ODBCSQLServerTest: public ODBCTest
|
|||||||
/// SQLServer ODBC test class
|
/// SQLServer ODBC test class
|
||||||
/// Tested:
|
/// Tested:
|
||||||
///
|
///
|
||||||
/// Driver | DB | OS
|
/// Driver name | Driver version | DB version | OS
|
||||||
/// --------------------+-----------------------------------+------------------------------------------
|
/// ------------------------------------+-------------------+-----------------------+------------------------------------------
|
||||||
/// 2000.86.1830.00 | SQL Server Express 9.0.2047 | MS Windows XP Professional x64 v.2003/SP1
|
/// SQL Server Express 9.0.2047 | 2000.86.1830.00 | | 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
|
/// SQL Server Express 9.0.2047 | 2005.90.2047.00 | | 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
|
/// 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:
|
public:
|
||||||
ODBCSQLServerTest(const std::string& name);
|
ODBCSQLServerTest(const std::string& name);
|
||||||
@ -51,6 +52,7 @@ public:
|
|||||||
void testStoredProcedureAny();
|
void testStoredProcedureAny();
|
||||||
void testStoredProcedureDynamicAny();
|
void testStoredProcedureDynamicAny();
|
||||||
|
|
||||||
|
void testStoredProcedureReturn();
|
||||||
void testStoredFunction();
|
void testStoredFunction();
|
||||||
|
|
||||||
static CppUnit::Test* suite();
|
static CppUnit::Test* suite();
|
||||||
|
@ -323,6 +323,9 @@ CppUnit::Test* ODBCSQLiteTest::suite()
|
|||||||
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCSQLiteTest");
|
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCSQLiteTest");
|
||||||
|
|
||||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testBareboneODBC);
|
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, testZeroRows);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testSimpleAccess);
|
CppUnit_addTest(pSuite, ODBCSQLiteTest, testSimpleAccess);
|
||||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testComplexType);
|
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()
|
void ODBCTest::testZeroRows()
|
||||||
{
|
{
|
||||||
if (!_pSession) fail ("Test not available.");
|
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()
|
void ODBCTest::testDateTime()
|
||||||
{
|
{
|
||||||
if (!_pSession) fail ("Test not available.");
|
if (!_pSession) fail ("Test not available.");
|
||||||
@ -1368,7 +1401,7 @@ ODBCTest::SessionPtr ODBCTest::init(const std::string& driver,
|
|||||||
|
|
||||||
try
|
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);
|
SessionPtr ptr = new Session(Poco::Data::ODBC::Connector::KEY, dbConnString, 5);
|
||||||
if (!dbEncoding.empty())
|
if (!dbEncoding.empty())
|
||||||
ptr->setProperty("dbEncoding", dbEncoding);
|
ptr->setProperty("dbEncoding", dbEncoding);
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#include "SQLExecutor.h"
|
#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
|
class ODBCTest: public CppUnit::TestCase
|
||||||
@ -47,6 +47,10 @@ public:
|
|||||||
|
|
||||||
virtual void testBareboneODBC() = 0;
|
virtual void testBareboneODBC() = 0;
|
||||||
|
|
||||||
|
virtual void testConnection();
|
||||||
|
virtual void testSession();
|
||||||
|
virtual void testSessionPool();
|
||||||
|
|
||||||
virtual void testZeroRows();
|
virtual void testZeroRows();
|
||||||
virtual void testSimpleAccess();
|
virtual void testSimpleAccess();
|
||||||
virtual void testComplexType();
|
virtual void testComplexType();
|
||||||
@ -107,6 +111,8 @@ public:
|
|||||||
virtual void testBLOBContainer();
|
virtual void testBLOBContainer();
|
||||||
virtual void testBLOBStmt();
|
virtual void testBLOBStmt();
|
||||||
|
|
||||||
|
virtual void testRecordSet();
|
||||||
|
|
||||||
virtual void testDateTime();
|
virtual void testDateTime();
|
||||||
virtual void testDate();
|
virtual void testDate();
|
||||||
virtual void testTime();
|
virtual void testTime();
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
#include "Poco/Data/Date.h"
|
#include "Poco/Data/Date.h"
|
||||||
#include "Poco/Data/Time.h"
|
#include "Poco/Data/Time.h"
|
||||||
#include "Poco/Data/LOB.h"
|
#include "Poco/Data/LOB.h"
|
||||||
|
#include "Poco/Data/Session.h"
|
||||||
|
#include "Poco/Data/SessionPool.h"
|
||||||
#include "Poco/Data/StatementImpl.h"
|
#include "Poco/Data/StatementImpl.h"
|
||||||
#include "Poco/Data/RecordSet.h"
|
#include "Poco/Data/RecordSet.h"
|
||||||
#include "Poco/Data/RowIterator.h"
|
#include "Poco/Data/RowIterator.h"
|
||||||
@ -54,6 +56,7 @@
|
|||||||
|
|
||||||
using namespace Poco::Data::Keywords;
|
using namespace Poco::Data::Keywords;
|
||||||
using Poco::Data::Session;
|
using Poco::Data::Session;
|
||||||
|
using Poco::Data::SessionPool;
|
||||||
using Poco::Data::Statement;
|
using Poco::Data::Statement;
|
||||||
using Poco::Data::RecordSet;
|
using Poco::Data::RecordSet;
|
||||||
using Poco::Data::Column;
|
using Poco::Data::Column;
|
||||||
@ -73,6 +76,7 @@ using Poco::Data::ODBC::Preparator;
|
|||||||
using Poco::Data::ODBC::ConnectionException;
|
using Poco::Data::ODBC::ConnectionException;
|
||||||
using Poco::Data::ODBC::StatementException;
|
using Poco::Data::ODBC::StatementException;
|
||||||
using Poco::Data::ODBC::DataTruncatedException;
|
using Poco::Data::ODBC::DataTruncatedException;
|
||||||
|
using Poco::Data::ODBC::ConnectionError;
|
||||||
using Poco::Data::ODBC::StatementError;
|
using Poco::Data::ODBC::StatementError;
|
||||||
using Poco::format;
|
using Poco::format;
|
||||||
using Poco::Tuple;
|
using Poco::Tuple;
|
||||||
@ -354,13 +358,13 @@ void SQLExecutor::bareboneODBCTest(const std::string& dbConnString,
|
|||||||
assertTrue (SQL_SUCCEEDED(rc) || SQL_NO_DATA == rc);
|
assertTrue (SQL_SUCCEEDED(rc) || SQL_NO_DATA == rc);
|
||||||
|
|
||||||
SQLULEN dateTimeColSize = 0;
|
SQLULEN dateTimeColSize = 0;
|
||||||
SQLSMALLINT dateTimeDecDigits = 0;
|
SQLSMALLINT dateTimeDecDigits = -1;
|
||||||
if (SQL_SUCCEEDED(rc))
|
if (SQL_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
SQLLEN ind = 0;
|
SQLLEN ind = 0;
|
||||||
rc = SQLGetData(hstmt, 3, SQL_C_SLONG, &dateTimeColSize, sizeof(SQLINTEGER), &ind);
|
rc = SQLGetData(hstmt, 3, SQL_C_SLONG, &dateTimeColSize, sizeof(SQLINTEGER), &ind);
|
||||||
poco_odbc_check_stmt (rc, hstmt);
|
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);
|
poco_odbc_check_stmt (rc, hstmt);
|
||||||
|
|
||||||
assertTrue (sizeof(SQL_TIMESTAMP_STRUCT) <= dateTimeColSize);
|
assertTrue (sizeof(SQL_TIMESTAMP_STRUCT) <= dateTimeColSize);
|
||||||
@ -484,18 +488,26 @@ void SQLExecutor::bareboneODBCTest(const std::string& dbConnString,
|
|||||||
0);
|
0);
|
||||||
poco_odbc_check_stmt (rc, hstmt);
|
poco_odbc_check_stmt (rc, hstmt);
|
||||||
|
|
||||||
|
if (dateTimeColSize == 0 || dateTimeDecDigits == -1)
|
||||||
|
{
|
||||||
SQLSMALLINT dataType = 0;
|
SQLSMALLINT dataType = 0;
|
||||||
SQLULEN parameterSize = 0;
|
SQLULEN parameterSize = 0;
|
||||||
SQLSMALLINT decimalDigits = 0;
|
SQLSMALLINT decimalDigits = -1;
|
||||||
SQLSMALLINT nullable = 0;
|
SQLSMALLINT nullable = 0;
|
||||||
rc = SQLDescribeParam(hstmt, 6, &dataType, ¶meterSize, &decimalDigits, &nullable);
|
rc = SQLDescribeParam(hstmt, 6, &dataType, ¶meterSize, &decimalDigits, &nullable);
|
||||||
if (SQL_SUCCEEDED(rc))
|
if (SQL_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
if (parameterSize)
|
if (parameterSize != 0 && dateTimeColSize == 0)
|
||||||
dateTimeColSize = parameterSize;
|
dateTimeColSize = parameterSize;
|
||||||
if (decimalDigits)
|
if (decimalDigits != -1 && dateTimeDecDigits == -1)
|
||||||
dateTimeDecDigits = decimalDigits;
|
dateTimeDecDigits = decimalDigits;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << '[' << name() << ']' << " Warning: could not get SQL_TYPE_TIMESTAMP "
|
||||||
|
"parameter description." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
std::cerr << '[' << name() << ']' << " Warning: could not get SQL_TYPE_TIMESTAMP parameter description." << std::endl;
|
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)
|
void SQLExecutor::execute(const std::string& sql)
|
||||||
{
|
{
|
||||||
try { session() << sql, now; }
|
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()
|
void SQLExecutor::zeroRows()
|
||||||
{
|
{
|
||||||
Statement stmt = (session() << "SELECT * FROM Person WHERE 0 = 1");
|
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()
|
void SQLExecutor::dateTime()
|
||||||
{
|
{
|
||||||
std::string funct = "dateTime()";
|
std::string funct = "dateTime()";
|
||||||
@ -3457,22 +4062,35 @@ void SQLExecutor::sqlChannel(const std::string& connect)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
AutoPtr<SQLChannel> pChannel = new SQLChannel(Poco::Data::ODBC::Connector::KEY, connect, "TestSQLChannel");
|
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");
|
pChannel->setProperty("keep", "2 seconds");
|
||||||
|
|
||||||
Message msgInf("InformationSource", "a Informational async message", Message::PRIO_INFORMATION);
|
Message msgInf("InformationSource", "a Informational async message", Message::PRIO_INFORMATION);
|
||||||
pChannel->log(msgInf);
|
pChannel->log(msgInf);
|
||||||
|
while (pChannel->logged() != 1) Thread::sleep(10);
|
||||||
|
|
||||||
Message msgWarn("WarningSource", "b Warning async message", Message::PRIO_WARNING);
|
Message msgWarn("WarningSource", "b Warning async message", Message::PRIO_WARNING);
|
||||||
pChannel->log(msgWarn);
|
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);
|
Message msgInfS("InformationSource", "c Informational sync message", Message::PRIO_INFORMATION);
|
||||||
pChannel->log(msgInfS);
|
pChannel->log(msgInfS);
|
||||||
|
while (pChannel->logged() != 3) Thread::sleep(10);
|
||||||
Message msgWarnS("WarningSource", "d Warning sync message", Message::PRIO_WARNING);
|
Message msgWarnS("WarningSource", "d Warning sync message", Message::PRIO_WARNING);
|
||||||
pChannel->log(msgWarnS);
|
pChannel->log(msgWarnS);
|
||||||
|
while (pChannel->logged() != 4) Thread::sleep(10);
|
||||||
|
|
||||||
RecordSet rs(session(), "SELECT * FROM T_POCO_LOG ORDER by Text");
|
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 ("InformationSource" == rs["Source"]);
|
||||||
assertTrue ("a Informational async message" == rs["Text"]);
|
assertTrue ("a Informational async message" == rs["Text"]);
|
||||||
rs.moveNext();
|
rs.moveNext();
|
||||||
@ -3485,12 +4103,14 @@ void SQLExecutor::sqlChannel(const std::string& connect)
|
|||||||
assertTrue ("WarningSource" == rs["Source"]);
|
assertTrue ("WarningSource" == rs["Source"]);
|
||||||
assertTrue ("d Warning sync message" == rs["Text"]);
|
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);
|
Message msgInfA("InformationSource", "e Informational sync message", Message::PRIO_INFORMATION);
|
||||||
pChannel->log(msgInfA);
|
pChannel->log(msgInfA);
|
||||||
|
while (pChannel->logged() != 5) Thread::sleep(10);
|
||||||
Message msgWarnA("WarningSource", "f Warning sync message", Message::PRIO_WARNING);
|
Message msgWarnA("WarningSource", "f Warning sync message", Message::PRIO_WARNING);
|
||||||
pChannel->log(msgWarnA);
|
pChannel->log(msgWarnA);
|
||||||
|
while (pChannel->logged() != 6) Thread::sleep(10);
|
||||||
|
|
||||||
RecordSet rs1(session(), "SELECT * FROM T_POCO_LOG_ARCHIVE");
|
RecordSet rs1(session(), "SELECT * FROM T_POCO_LOG_ARCHIVE");
|
||||||
assertTrue (4 == rs1.rowCount());
|
assertTrue (4 == rs1.rowCount());
|
||||||
@ -3504,7 +4124,6 @@ void SQLExecutor::sqlChannel(const std::string& connect)
|
|||||||
rs2.moveNext();
|
rs2.moveNext();
|
||||||
assertTrue ("WarningSource" == rs2["Source"]);
|
assertTrue ("WarningSource" == rs2["Source"]);
|
||||||
assertTrue ("f Warning sync message" == rs2["Text"]);
|
assertTrue ("f Warning sync message" == rs2["Text"]);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("sqlChannel()"); }
|
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("sqlChannel()"); }
|
||||||
catch(StatementException& se){ std::cout << se.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
|
try
|
||||||
{
|
{
|
||||||
Logger& root = Logger::root();
|
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.setLevel(Message::PRIO_INFORMATION);
|
||||||
|
|
||||||
root.information("a Informational message");
|
root.information("a Informational message");
|
||||||
root.warning("b Warning message");
|
root.warning("b Warning message");
|
||||||
root.debug("Debug 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");
|
RecordSet rs(session(), "SELECT * FROM T_POCO_LOG ORDER by Text");
|
||||||
assertTrue (2 == rs.rowCount());
|
assertTrue (2 == rs.rowCount());
|
||||||
assertTrue ("TestSQLChannel" == rs["Source"]);
|
assertTrue ("TestSQLChannel" == rs["Source"]);
|
||||||
@ -3531,7 +4159,6 @@ void SQLExecutor::sqlLogger(const std::string& connect)
|
|||||||
rs.moveNext();
|
rs.moveNext();
|
||||||
assertTrue ("TestSQLChannel" == rs["Source"]);
|
assertTrue ("TestSQLChannel" == rs["Source"]);
|
||||||
assertTrue ("b Warning message" == rs["Text"]);
|
assertTrue ("b Warning message" == rs["Text"]);
|
||||||
root.setChannel(nullptr);
|
|
||||||
}
|
}
|
||||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("sqlLogger()"); }
|
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("sqlLogger()"); }
|
||||||
catch(StatementException& se){ std::cout << se.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()
|
void SQLExecutor::transactor()
|
||||||
{
|
{
|
||||||
std::string funct = "transaction()";
|
std::string funct = "transactor()";
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
bool autoCommit = session().getFeature("autoCommit");
|
bool autoCommit = session().getFeature("autoCommit");
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
if (!SQL_SUCCEEDED(r)) \
|
if (!SQL_SUCCEEDED(r)) \
|
||||||
{ \
|
{ \
|
||||||
Poco::Data::ODBC::StatementException se(h); \
|
Poco::Data::ODBC::StatementException se(h); \
|
||||||
std::cout << se.toString() << std::endl; \
|
std::cout << se.displayText() << std::endl; \
|
||||||
} \
|
} \
|
||||||
assert (SQL_SUCCEEDED(r))
|
assert (SQL_SUCCEEDED(r))
|
||||||
|
|
||||||
@ -57,7 +57,7 @@
|
|||||||
if (!SQL_SUCCEEDED(r)) \
|
if (!SQL_SUCCEEDED(r)) \
|
||||||
{ \
|
{ \
|
||||||
Poco::Data::ODBC::DescriptorException de(h); \
|
Poco::Data::ODBC::DescriptorException de(h); \
|
||||||
std::cout << de.toString() << std::endl; \
|
std::cout << de.displayText() << std::endl; \
|
||||||
} \
|
} \
|
||||||
assert (SQL_SUCCEEDED(r))
|
assert (SQL_SUCCEEDED(r))
|
||||||
|
|
||||||
@ -106,13 +106,24 @@ public:
|
|||||||
SQLExecutor::DataExtraction extractMode,
|
SQLExecutor::DataExtraction extractMode,
|
||||||
const std::string& insert = MULTI_INSERT,
|
const std::string& insert = MULTI_INSERT,
|
||||||
const std::string& select = MULTI_SELECT);
|
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).
|
/// (i.e. calls are not "wrapped" in PocoData framework structures).
|
||||||
/// The purpose of the functions is to verify that a driver behaves
|
/// The purpose of the functions is to verify that a driver behaves
|
||||||
/// correctly as well as to determine its capabilities
|
/// correctly as well as to determine its capabilities
|
||||||
/// (e.g. SQLGetData() restrictions relaxation policy, if any).
|
/// (e.g. SQLGetData() restrictions relaxation policy, if any).
|
||||||
/// If these test pass, subsequent tests failures are likely ours.
|
/// 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 zeroRows();
|
||||||
void simpleAccess();
|
void simpleAccess();
|
||||||
void complexType();
|
void complexType();
|
||||||
@ -463,6 +474,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void blobStmt();
|
void blobStmt();
|
||||||
|
void recordSet();
|
||||||
|
|
||||||
void dateTime();
|
void dateTime();
|
||||||
void date();
|
void date();
|
||||||
|
@ -257,11 +257,6 @@ Poco::Any SessionImpl::getTransactionType(const std::string& prop) const
|
|||||||
|
|
||||||
void SessionImpl::autoCommit(const std::string&, bool)
|
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("TIMESTAMP", MetaColumn::FDT_TIMESTAMP));
|
||||||
_types.insert(TypeMap::value_type("UUID", MetaColumn::FDT_UUID));
|
_types.insert(TypeMap::value_type("UUID", MetaColumn::FDT_UUID));
|
||||||
_types.insert(TypeMap::value_type("GUID", 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::Dynamic::Var;
|
||||||
using Poco::Data::SQLite::Utility;
|
using Poco::Data::SQLite::Utility;
|
||||||
using Poco::delegate;
|
using Poco::delegate;
|
||||||
|
using Poco::Stopwatch;
|
||||||
|
|
||||||
|
|
||||||
class Person
|
class Person
|
||||||
@ -1408,7 +1409,7 @@ void SQLiteTest::testNonexistingDB()
|
|||||||
Session tmp (Poco::Data::SQLite::Connector::KEY, "foo/bar/nonexisting.db", 1);
|
Session tmp (Poco::Data::SQLite::Connector::KEY, "foo/bar/nonexisting.db", 1);
|
||||||
fail("non-existing DB must throw");
|
fail("non-existing DB must throw");
|
||||||
}
|
}
|
||||||
catch(ConnectionFailedException& ex)
|
catch(ConnectionFailedException&)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2474,53 +2475,68 @@ void SQLiteTest::testSQLChannel()
|
|||||||
"DateTime DATE)", now;
|
"DateTime DATE)", now;
|
||||||
|
|
||||||
AutoPtr<SQLChannel> pChannel = new SQLChannel(Poco::Data::SQLite::Connector::KEY, "dummy.db", "TestSQLChannel");
|
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");
|
pChannel->setProperty("keep", "2 seconds");
|
||||||
|
|
||||||
Message msgInf("InformationSource", "a Informational async message", Message::PRIO_INFORMATION);
|
Message msgInf("InformationSource", "a Informational async message", Message::PRIO_INFORMATION);
|
||||||
pChannel->log(msgInf);
|
pChannel->log(msgInf);
|
||||||
|
while (pChannel->logged() != 1) Thread::sleep(100);
|
||||||
|
|
||||||
Message msgWarn("WarningSource", "b Warning async message", Message::PRIO_WARNING);
|
Message msgWarn("WarningSource", "b Warning async message", Message::PRIO_WARNING);
|
||||||
pChannel->log(msgWarn);
|
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);
|
Message msgInfS("InformationSource", "c Informational sync message", Message::PRIO_INFORMATION);
|
||||||
pChannel->log(msgInfS);
|
pChannel->log(msgInfS);
|
||||||
|
while (pChannel->logged() != 3) Thread::sleep(10);
|
||||||
Message msgWarnS("WarningSource", "d Warning sync message", Message::PRIO_WARNING);
|
Message msgWarnS("WarningSource", "d Warning sync message", Message::PRIO_WARNING);
|
||||||
pChannel->log(msgWarnS);
|
pChannel->log(msgWarnS);
|
||||||
|
while (pChannel->logged() != 4) Thread::sleep(10);
|
||||||
|
|
||||||
RecordSet rs(tmp, "SELECT * FROM T_POCO_LOG ORDER by Text");
|
RecordSet rs(tmp, "SELECT * FROM T_POCO_LOG ORDER by Text");
|
||||||
assertTrue (4 == rs.rowCount());
|
size_t rc = rs.rowCount();
|
||||||
assertTrue ("InformationSource" == rs["Source"]);
|
assertTrue(4 == rc);
|
||||||
assertTrue ("a Informational async message" == rs["Text"]);
|
assertTrue("InformationSource" == rs["Source"]);
|
||||||
|
assertTrue("a Informational async message" == rs["Text"]);
|
||||||
rs.moveNext();
|
rs.moveNext();
|
||||||
assertTrue ("WarningSource" == rs["Source"]);
|
assertTrue("WarningSource" == rs["Source"]);
|
||||||
assertTrue ("b Warning async message" == rs["Text"]);
|
assertTrue("b Warning async message" == rs["Text"]);
|
||||||
rs.moveNext();
|
rs.moveNext();
|
||||||
assertTrue ("InformationSource" == rs["Source"]);
|
assertTrue("InformationSource" == rs["Source"]);
|
||||||
assertTrue ("c Informational sync message" == rs["Text"]);
|
assertTrue("c Informational sync message" == rs["Text"]);
|
||||||
rs.moveNext();
|
rs.moveNext();
|
||||||
assertTrue ("WarningSource" == rs["Source"]);
|
assertTrue("WarningSource" == rs["Source"]);
|
||||||
assertTrue ("d Warning sync message" == rs["Text"]);
|
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);
|
Message msgInfA("InformationSource", "e Informational sync message", Message::PRIO_INFORMATION);
|
||||||
pChannel->log(msgInfA);
|
pChannel->log(msgInfA);
|
||||||
|
while (pChannel->logged() != 5) Thread::sleep(10);
|
||||||
Message msgWarnA("WarningSource", "f Warning sync message", Message::PRIO_WARNING);
|
Message msgWarnA("WarningSource", "f Warning sync message", Message::PRIO_WARNING);
|
||||||
pChannel->log(msgWarnA);
|
pChannel->log(msgWarnA);
|
||||||
|
while (pChannel->logged() != 6) Thread::sleep(10);
|
||||||
|
|
||||||
RecordSet rs1(tmp, "SELECT * FROM T_POCO_LOG_ARCHIVE");
|
RecordSet rs1(tmp, "SELECT * FROM T_POCO_LOG_ARCHIVE");
|
||||||
assertTrue (4 == rs1.rowCount());
|
assertTrue(4 == rs1.rowCount());
|
||||||
|
|
||||||
pChannel->setProperty("keep", "");
|
pChannel->setProperty("keep", "");
|
||||||
assertTrue ("forever" == pChannel->getProperty("keep"));
|
assertTrue("forever" == pChannel->getProperty("keep"));
|
||||||
RecordSet rs2(tmp, "SELECT * FROM T_POCO_LOG ORDER by Text");
|
RecordSet rs2(tmp, "SELECT * FROM T_POCO_LOG ORDER by Text");
|
||||||
assertTrue (2 == rs2.rowCount());
|
assertTrue(2 == rs2.rowCount());
|
||||||
assertTrue ("InformationSource" == rs2["Source"]);
|
assertTrue("InformationSource" == rs2["Source"]);
|
||||||
assertTrue ("e Informational sync message" == rs2["Text"]);
|
assertTrue("e Informational sync message" == rs2["Text"]);
|
||||||
rs2.moveNext();
|
rs2.moveNext();
|
||||||
assertTrue ("WarningSource" == rs2["Source"]);
|
assertTrue("WarningSource" == rs2["Source"]);
|
||||||
assertTrue ("f Warning sync message" == rs2["Text"]);
|
assertTrue("f Warning sync message" == rs2["Text"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2537,25 +2553,30 @@ void SQLiteTest::testSQLLogger()
|
|||||||
"Text VARCHAR,"
|
"Text VARCHAR,"
|
||||||
"DateTime DATE)", now;
|
"DateTime DATE)", now;
|
||||||
|
|
||||||
{
|
|
||||||
AutoPtr<SQLChannel> pChannel = new SQLChannel(Poco::Data::SQLite::Connector::KEY, "dummy.db", "TestSQLChannel");
|
|
||||||
Logger& root = Logger::root();
|
Logger& root = Logger::root();
|
||||||
root.setChannel(pChannel);
|
AutoPtr<SQLChannel> pSQLChannel = new SQLChannel(Poco::Data::SQLite::Connector::KEY, "dummy.db", "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.setLevel(Message::PRIO_INFORMATION);
|
||||||
|
|
||||||
root.information("Informational message");
|
root.information("a Informational message");
|
||||||
root.warning("Warning message");
|
root.warning("b Warning message");
|
||||||
root.debug("Debug message");
|
root.debug("Debug message");
|
||||||
}
|
|
||||||
|
|
||||||
Thread::sleep(100);
|
while (pSQLChannel->logged() != 2) Thread::sleep(100);
|
||||||
RecordSet rs(tmp, "SELECT * FROM T_POCO_LOG ORDER by DateTime");
|
RecordSet rs(tmp, "SELECT * FROM T_POCO_LOG ORDER by Text");
|
||||||
assertTrue (2 == rs.rowCount());
|
assertTrue(2 == rs.rowCount());
|
||||||
assertTrue ("TestSQLChannel" == rs["Source"]);
|
assertTrue("TestSQLChannel" == rs["Source"]);
|
||||||
assertTrue ("Informational message" == rs["Text"]);
|
assertTrue("a Informational message" == rs["Text"]);
|
||||||
rs.moveNext();
|
rs.moveNext();
|
||||||
assertTrue ("TestSQLChannel" == rs["Source"]);
|
assertTrue("TestSQLChannel" == rs["Source"]);
|
||||||
assertTrue ("Warning message" == rs["Text"]);
|
assertTrue("b Warning message" == rs["Text"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3130,12 +3151,6 @@ void SQLiteTest::testSessionTransaction()
|
|||||||
Session local (Poco::Data::SQLite::Connector::KEY, "dummy.db");
|
Session local (Poco::Data::SQLite::Connector::KEY, "dummy.db");
|
||||||
assertTrue (local.isConnected());
|
assertTrue (local.isConnected());
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
local.setFeature("autoCommit", true);
|
|
||||||
fail ("Setting SQLite auto-commit explicitly must fail!");
|
|
||||||
}
|
|
||||||
catch (NotImplementedException&) { }
|
|
||||||
assertTrue (local.getFeature("autoCommit"));
|
assertTrue (local.getFeature("autoCommit"));
|
||||||
|
|
||||||
std::string funct = "transaction()";
|
std::string funct = "transaction()";
|
||||||
@ -3235,12 +3250,16 @@ void SQLiteTest::testTransaction()
|
|||||||
std::string tableName("Person");
|
std::string tableName("Person");
|
||||||
lastNames.push_back("LN1");
|
lastNames.push_back("LN1");
|
||||||
lastNames.push_back("LN2");
|
lastNames.push_back("LN2");
|
||||||
|
lastNames.push_back("LN3");
|
||||||
firstNames.push_back("FN1");
|
firstNames.push_back("FN1");
|
||||||
firstNames.push_back("FN2");
|
firstNames.push_back("FN2");
|
||||||
|
firstNames.push_back("FN3");
|
||||||
addresses.push_back("ADDR1");
|
addresses.push_back("ADDR1");
|
||||||
addresses.push_back("ADDR2");
|
addresses.push_back("ADDR2");
|
||||||
|
addresses.push_back("ADDR3");
|
||||||
ages.push_back(1);
|
ages.push_back(1);
|
||||||
ages.push_back(2);
|
ages.push_back(2);
|
||||||
|
ages.push_back(3);
|
||||||
int count = 0, locCount = 0;
|
int count = 0, locCount = 0;
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
@ -3258,7 +3277,7 @@ void SQLiteTest::testTransaction()
|
|||||||
assertTrue (trans.isActive());
|
assertTrue (trans.isActive());
|
||||||
|
|
||||||
session << "SELECT COUNT(*) FROM Person", into(count), now;
|
session << "SELECT COUNT(*) FROM Person", into(count), now;
|
||||||
assertTrue (2 == count);
|
assertTrue (3 == count);
|
||||||
assertTrue (session.isTransaction());
|
assertTrue (session.isTransaction());
|
||||||
assertTrue (trans.isActive());
|
assertTrue (trans.isActive());
|
||||||
// no explicit commit, so transaction RAII must roll back here
|
// 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;
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
assertTrue (2 == count);
|
assertTrue (3 == count);
|
||||||
local << "SELECT count(*) FROM Person", into(count), now;
|
local << "SELECT count(*) FROM Person", into(count), now;
|
||||||
assertTrue (2 == count);
|
assertTrue (3 == count);
|
||||||
|
|
||||||
session << "DELETE FROM Person", now;
|
session << "DELETE FROM Person", now;
|
||||||
|
|
||||||
@ -3314,7 +3333,8 @@ void SQLiteTest::testTransaction()
|
|||||||
session << "SELECT count(*) FROM Person", into(count), now;
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
assertTrue (0 == count);
|
assertTrue (0 == count);
|
||||||
|
|
||||||
trans.execute(sql);
|
bool status = trans.execute(sql);
|
||||||
|
assertTrue (status);
|
||||||
|
|
||||||
Statement stmt3 = (local << "SELECT COUNT(*) FROM Person", into(locCount), now);
|
Statement stmt3 = (local << "SELECT COUNT(*) FROM Person", into(locCount), now);
|
||||||
assertTrue (2 == locCount);
|
assertTrue (2 == locCount);
|
||||||
@ -3322,6 +3342,21 @@ void SQLiteTest::testTransaction()
|
|||||||
session << "SELECT count(*) FROM Person", into(count), now;
|
session << "SELECT count(*) FROM Person", into(count), now;
|
||||||
assertTrue (2 == count);
|
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();
|
session.close();
|
||||||
assertTrue (!session.isConnected());
|
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)
|
void setFeature(const std::string& name, bool state)
|
||||||
/// Looks a feature up in the features map
|
/// Looks a feature up in the features map
|
||||||
/// and calls the feature's setter, if there is one.
|
/// and calls the feature's setter, if there is one.
|
||||||
@ -145,6 +155,16 @@ public:
|
|||||||
else throw NotSupportedException(name);
|
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)
|
void setProperty(const std::string& name, const Poco::Any& value)
|
||||||
/// Looks a property up in the properties map
|
/// Looks a property up in the properties map
|
||||||
/// and calls the property's setter, if there is one.
|
/// and calls the property's setter, if there is one.
|
||||||
|
@ -180,7 +180,8 @@ public:
|
|||||||
ArchiveByAgeStrategy(const std::string& connector,
|
ArchiveByAgeStrategy(const std::string& connector,
|
||||||
const std::string& connect,
|
const std::string& connect,
|
||||||
const std::string& sourceTable,
|
const std::string& sourceTable,
|
||||||
const std::string& destinationTable = DEFAULT_ARCHIVE_DESTINATION);
|
const std::string& destinationTable = DEFAULT_ARCHIVE_DESTINATION,
|
||||||
|
const std::string& age = "");
|
||||||
|
|
||||||
~ArchiveByAgeStrategy();
|
~ArchiveByAgeStrategy();
|
||||||
|
|
||||||
|
@ -62,8 +62,10 @@ public:
|
|||||||
bool hasTransactionIsolation(Poco::UInt32) const;
|
bool hasTransactionIsolation(Poco::UInt32) const;
|
||||||
bool isTransactionIsolation(Poco::UInt32) const;
|
bool isTransactionIsolation(Poco::UInt32) const;
|
||||||
const std::string& connectorName() const;
|
const std::string& connectorName() const;
|
||||||
|
bool hasFeature(const std::string& name);
|
||||||
void setFeature(const std::string& name, bool state);
|
void setFeature(const std::string& name, bool state);
|
||||||
bool getFeature(const std::string& name);
|
bool getFeature(const std::string& name);
|
||||||
|
bool hasProperty(const std::string& name);
|
||||||
void setProperty(const std::string& name, const Poco::Any& value);
|
void setProperty(const std::string& name, const Poco::Any& value);
|
||||||
Poco::Any getProperty(const std::string& name);
|
Poco::Any getProperty(const std::string& name);
|
||||||
|
|
||||||
|
@ -50,7 +50,8 @@ public:
|
|||||||
void prepare()
|
void prepare()
|
||||||
/// Prepares data.
|
/// Prepares data.
|
||||||
{
|
{
|
||||||
TypeHandler<T>::prepare(_pos, _val, preparation());
|
auto pPrep = preparation();
|
||||||
|
TypeHandler<T>::prepare(_pos, _val, pPrep);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -133,6 +133,9 @@ public:
|
|||||||
/// for large recordsets, so it should be used judiciously.
|
/// for large recordsets, so it should be used judiciously.
|
||||||
/// Use totalRowCount() to obtain the total number of rows.
|
/// 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;
|
std::size_t extractedRowCount() const;
|
||||||
/// Returns the number of rows extracted during the last statement
|
/// Returns the number of rows extracted during the last statement
|
||||||
/// execution.
|
/// execution.
|
||||||
|
@ -22,18 +22,24 @@
|
|||||||
#include "Poco/Data/Connector.h"
|
#include "Poco/Data/Connector.h"
|
||||||
#include "Poco/Data/Session.h"
|
#include "Poco/Data/Session.h"
|
||||||
#include "Poco/Data/Statement.h"
|
#include "Poco/Data/Statement.h"
|
||||||
|
#include "Poco/Logger.h"
|
||||||
#include "Poco/Data/ArchiveStrategy.h"
|
#include "Poco/Data/ArchiveStrategy.h"
|
||||||
#include "Poco/Channel.h"
|
#include "Poco/Channel.h"
|
||||||
|
#include "Poco/FileChannel.h"
|
||||||
#include "Poco/Message.h"
|
#include "Poco/Message.h"
|
||||||
#include "Poco/AutoPtr.h"
|
#include "Poco/AutoPtr.h"
|
||||||
#include "Poco/String.h"
|
#include "Poco/String.h"
|
||||||
|
#include "Poco/NotificationQueue.h"
|
||||||
|
#include "Poco/Thread.h"
|
||||||
|
#include "Poco/Mutex.h"
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
|
||||||
namespace Poco {
|
namespace Poco {
|
||||||
namespace Data {
|
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.
|
/// This Channel implements logging to a SQL database.
|
||||||
/// The channel is dependent on the schema. The DDL for
|
/// The channel is dependent on the schema. The DDL for
|
||||||
/// table creation (subject to target DDL dialect dependent
|
/// 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.
|
/// a risk of long blocking periods in case of remote server communication delays.
|
||||||
{
|
{
|
||||||
public:
|
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();
|
SQLChannel();
|
||||||
/// Creates SQLChannel.
|
/// Creates SQLChannel.
|
||||||
|
|
||||||
SQLChannel(const std::string& connector,
|
SQLChannel(const std::string& connector,
|
||||||
const std::string& connect,
|
const std::string& connect,
|
||||||
const std::string& name = "-");
|
const std::string& name = "-",
|
||||||
/// Creates a SQLChannel with the given connector, connect string, timeout, table and 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.
|
/// The connector must be already registered.
|
||||||
|
|
||||||
void open();
|
void open();
|
||||||
/// Opens the SQLChannel.
|
/// Opens the SQLChannel.
|
||||||
|
/// Returns true if succesful.
|
||||||
|
|
||||||
void close();
|
void close(int ms = 0);
|
||||||
/// Closes the SQLChannel.
|
/// 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);
|
void log(const Message& msg);
|
||||||
/// Writes the log message to the database.
|
/// Writes the log message to the database.
|
||||||
|
|
||||||
@ -108,6 +145,8 @@ public:
|
|||||||
/// the target, the previous operation must have been either completed
|
/// the target, the previous operation must have been either completed
|
||||||
/// or timed out (see timeout and throw properties for details on
|
/// or timed out (see timeout and throw properties for details on
|
||||||
/// how abnormal conditos are handled).
|
/// 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.
|
/// * timeout: Timeout (ms) to wait for previous log operation completion.
|
||||||
/// Values "0" and "" mean no timeout. Only valid when logging
|
/// 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.
|
/// Setting this property to false may result in log entries being lost.
|
||||||
/// True values are (case insensitive) "true", "t", "yes", "y".
|
/// True values are (case insensitive) "true", "t", "yes", "y".
|
||||||
/// Anything else yields false.
|
/// 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;
|
std::string getProperty(const std::string& name) const;
|
||||||
/// Returns the value of the property with the given name.
|
/// 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
|
/// Waits for the completion of the previous operation and returns
|
||||||
/// the result. If chanel is in synchronous mode, returns 0 immediately.
|
/// the result. If chanel is in synchronous mode, returns 0 immediately.
|
||||||
|
|
||||||
|
size_t logged() const;
|
||||||
|
/// Returns the number of logged entries.
|
||||||
|
|
||||||
static void registerChannel();
|
static void registerChannel();
|
||||||
/// Registers the channel with the global LoggingFactory.
|
/// Registers the channel with the global LoggingFactory.
|
||||||
|
|
||||||
@ -136,56 +196,84 @@ public:
|
|||||||
static const std::string PROP_MAX_AGE;
|
static const std::string PROP_MAX_AGE;
|
||||||
static const std::string PROP_ASYNC;
|
static const std::string PROP_ASYNC;
|
||||||
static const std::string PROP_TIMEOUT;
|
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_THROW;
|
||||||
|
static const std::string PROP_FILE;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~SQLChannel();
|
~SQLChannel();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using SessionPtr = Poco::SharedPtr<Session>;
|
static const std::string SQL_INSERT_STMT;
|
||||||
using StatementPtr = Poco::SharedPtr<Statement>;
|
|
||||||
using Priority = Poco::Message::Priority;
|
|
||||||
using StrategyPtr = Poco::SharedPtr<ArchiveStrategy>;
|
|
||||||
|
|
||||||
void initLogStatement();
|
typedef Poco::SharedPtr<Session> SessionPtr;
|
||||||
/// Initiallizes the log statement.
|
typedef Poco::SharedPtr<Statement> StatementPtr;
|
||||||
|
typedef Poco::Message::Priority Priority;
|
||||||
|
typedef Poco::SharedPtr<ArchiveStrategy> StrategyPtr;
|
||||||
|
|
||||||
void initArchiveStatements();
|
void reconnect();
|
||||||
/// Initiallizes the archive statement.
|
/// Closes and opens the DB connection.
|
||||||
|
|
||||||
void logAsync(const Message& msg);
|
bool processOne(int minBatch = 0);
|
||||||
/// Waits for previous operation completion and
|
/// Processes one message.
|
||||||
/// calls logSync(). If the previous operation times out,
|
/// If the number of acummulated messages is greater
|
||||||
/// and _throw is true, TimeoutException is thrown, oterwise
|
/// than minBatch, sends logs to the destination.
|
||||||
/// the timeout is ignored and log entry is lost.
|
/// Returns true if log entry was processed.
|
||||||
|
|
||||||
void logSync(const Message& msg);
|
size_t execSQL();
|
||||||
/// Inserts the message in the target database.
|
/// Executes the log statement.
|
||||||
|
|
||||||
|
size_t logSync();
|
||||||
|
/// Inserts entries into the target database.
|
||||||
|
|
||||||
bool isTrue(const std::string& value) const;
|
bool isTrue(const std::string& value) const;
|
||||||
/// Returns true is value is "true", "t", "yes" or "y".
|
/// Returns true is value is "true", "t", "yes" or "y".
|
||||||
/// Case insensitive.
|
/// Case insensitive.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
mutable Poco::FastMutex _mutex;
|
||||||
|
|
||||||
std::string _connector;
|
std::string _connector;
|
||||||
std::string _connect;
|
std::string _connect;
|
||||||
SessionPtr _pSession;
|
SessionPtr _pSession;
|
||||||
StatementPtr _pLogStatement;
|
std::string _sql;
|
||||||
std::string _name;
|
std::string _name;
|
||||||
std::string _table;
|
std::string _table;
|
||||||
|
bool _tableChanged;
|
||||||
int _timeout;
|
int _timeout;
|
||||||
bool _throw;
|
std::atomic<int> _minBatch;
|
||||||
bool _async;
|
int _maxBatch;
|
||||||
|
bool _bulk;
|
||||||
// members for log entry cache (needed for async mode)
|
std::atomic<bool> _throw;
|
||||||
std::string _source;
|
|
||||||
long _pid;
|
|
||||||
std::string _thread;
|
|
||||||
long _tid;
|
|
||||||
int _priority;
|
|
||||||
std::string _text;
|
|
||||||
DateTime _dateTime;
|
|
||||||
|
|
||||||
|
// 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;
|
StrategyPtr _pArchiveStrategy;
|
||||||
|
std::string _file;
|
||||||
|
AutoPtr<FileChannel> _pFileChannel;
|
||||||
|
Poco::Logger& _logger = Poco::Logger::get("SQLChannel");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -193,14 +281,6 @@ private:
|
|||||||
// inlines
|
// 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
|
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
|
} } // namespace Poco::Data
|
||||||
|
|
||||||
|
|
||||||
|
@ -273,6 +273,9 @@ public:
|
|||||||
/// Utility function that teturns the URI formatted from supplied
|
/// Utility function that teturns the URI formatted from supplied
|
||||||
/// arguments as "connector:///connectionString".
|
/// 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);
|
void setFeature(const std::string& name, bool state);
|
||||||
/// Set the state of a feature.
|
/// Set the state of a feature.
|
||||||
///
|
///
|
||||||
@ -291,6 +294,9 @@ public:
|
|||||||
/// Throws a NotSupportedException if the requested feature is
|
/// Throws a NotSupportedException if the requested feature is
|
||||||
/// not supported by the underlying implementation.
|
/// 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);
|
void setProperty(const std::string& name, const Poco::Any& value);
|
||||||
/// Set the value of a property.
|
/// 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)
|
inline void Session::setFeature(const std::string& name, bool state)
|
||||||
{
|
{
|
||||||
_pImpl->setFeature(name, 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)
|
inline void Session::setProperty(const std::string& name, const Poco::Any& value)
|
||||||
{
|
{
|
||||||
_pImpl->setProperty(name, value);
|
_pImpl->setProperty(name, value);
|
||||||
|
@ -53,6 +53,11 @@ public:
|
|||||||
static const std::size_t CONNECTION_TIMEOUT_DEFAULT = CONNECTION_TIMEOUT_INFINITE;
|
static const std::size_t CONNECTION_TIMEOUT_DEFAULT = CONNECTION_TIMEOUT_INFINITE;
|
||||||
/// Default connection/login timeout in seconds.
|
/// 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,
|
SessionImpl(const std::string& connectionString,
|
||||||
std::size_t timeout = LOGIN_TIMEOUT_DEFAULT);
|
std::size_t timeout = LOGIN_TIMEOUT_DEFAULT);
|
||||||
/// Creates the SessionImpl.
|
/// Creates the SessionImpl.
|
||||||
@ -141,6 +146,9 @@ public:
|
|||||||
std::string uri() const;
|
std::string uri() const;
|
||||||
/// Returns the URI for this session.
|
/// 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;
|
virtual void setFeature(const std::string& name, bool state) = 0;
|
||||||
/// Set the state of a feature.
|
/// Set the state of a feature.
|
||||||
///
|
///
|
||||||
@ -159,6 +167,9 @@ public:
|
|||||||
/// Throws a NotSupportedException if the requested feature is
|
/// Throws a NotSupportedException if the requested feature is
|
||||||
/// not supported by the underlying implementation.
|
/// 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;
|
virtual void setProperty(const std::string& name, const Poco::Any& value) = 0;
|
||||||
/// Set the value of a property.
|
/// Set the value of a property.
|
||||||
///
|
///
|
||||||
|
@ -193,17 +193,17 @@ private:
|
|||||||
|
|
||||||
std::string _connector;
|
std::string _connector;
|
||||||
std::string _connectionString;
|
std::string _connectionString;
|
||||||
int _minSessions;
|
std::atomic<int> _minSessions;
|
||||||
int _maxSessions;
|
std::atomic<int> _maxSessions;
|
||||||
int _idleTime;
|
std::atomic<int> _idleTime;
|
||||||
int _connTimeout;
|
std::atomic<int> _connTimeout;
|
||||||
int _nSessions;
|
std::atomic<int> _nSessions;
|
||||||
SessionList _idleSessions;
|
SessionList _idleSessions;
|
||||||
SessionList _activeSessions;
|
SessionList _activeSessions;
|
||||||
Poco::Timer _janitorTimer;
|
Poco::Timer _janitorTimer;
|
||||||
FeatureMap _featureMap;
|
FeatureMap _featureMap;
|
||||||
PropertyMap _propertyMap;
|
PropertyMap _propertyMap;
|
||||||
bool _shutdown;
|
std::atomic<bool> _shutdown;
|
||||||
AddPropertyMap _addPropertyMap;
|
AddPropertyMap _addPropertyMap;
|
||||||
AddFeatureMap _addFeatureMap;
|
AddFeatureMap _addFeatureMap;
|
||||||
mutable
|
mutable
|
||||||
|
@ -369,6 +369,10 @@ public:
|
|||||||
/// Returns the number of rows extracted so far for the data set.
|
/// Returns the number of rows extracted so far for the data set.
|
||||||
/// Default value indicates current data set (if any).
|
/// 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;
|
std::size_t extractionCount() const;
|
||||||
/// Returns the number of extraction storage buffers associated
|
/// Returns the number of extraction storage buffers associated
|
||||||
/// with the current data set.
|
/// 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
|
inline std::size_t Statement::extractionCount() const
|
||||||
{
|
{
|
||||||
return _pImpl->extractionCount();
|
return _pImpl->extractionCount();
|
||||||
|
@ -39,6 +39,9 @@ class Data_API Transaction
|
|||||||
public:
|
public:
|
||||||
Transaction(Poco::Data::Session& session, Poco::Logger* pLogger = 0);
|
Transaction(Poco::Data::Session& session, Poco::Logger* pLogger = 0);
|
||||||
/// Creates the Transaction and starts it, using the given database session and logger.
|
/// 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);
|
Transaction(Poco::Data::Session& session, bool start);
|
||||||
/// Creates the Transaction, using the given database session.
|
/// Creates the Transaction, using the given database session.
|
||||||
@ -107,10 +110,18 @@ public:
|
|||||||
/// Passing true value for commit disables rollback during destruction
|
/// Passing true value for commit disables rollback during destruction
|
||||||
/// of this Transaction object.
|
/// 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
|
/// Executes all the SQL statements supplied in the vector and, after the last
|
||||||
/// one is sucesfully executed, commits the transaction.
|
/// one is sucesfully executed, commits the transaction and returns true.
|
||||||
/// If an error occurs during execution, transaction is rolled back.
|
/// 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
|
/// Passing true value for commit disables rollback during destruction
|
||||||
/// of this Transaction object.
|
/// of this Transaction object.
|
||||||
|
|
||||||
@ -148,7 +159,8 @@ private:
|
|||||||
/// Otherwise does nothing.
|
/// Otherwise does nothing.
|
||||||
|
|
||||||
Session _rSession;
|
Session _rSession;
|
||||||
Logger* _pLogger;
|
bool _autoCommit = false;
|
||||||
|
Logger* _pLogger = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,10 +64,12 @@ void ArchiveStrategy::open()
|
|||||||
ArchiveByAgeStrategy::ArchiveByAgeStrategy(const std::string& connector,
|
ArchiveByAgeStrategy::ArchiveByAgeStrategy(const std::string& connector,
|
||||||
const std::string& connect,
|
const std::string& connect,
|
||||||
const std::string& sourceTable,
|
const std::string& sourceTable,
|
||||||
const std::string& destinationTable):
|
const std::string& destinationTable,
|
||||||
|
const std::string& age):
|
||||||
ArchiveStrategy(connector, connect, sourceTable, destinationTable)
|
ArchiveStrategy(connector, connect, sourceTable, destinationTable)
|
||||||
{
|
{
|
||||||
initStatements();
|
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)
|
void PooledSessionImpl::setFeature(const std::string& name, bool state)
|
||||||
{
|
{
|
||||||
access()->setFeature(name, 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)
|
void PooledSessionImpl::setProperty(const std::string& name, const Poco::Any& value)
|
||||||
{
|
{
|
||||||
access()->setProperty(name, value);
|
access()->setProperty(name, value);
|
||||||
|
@ -244,7 +244,8 @@ Row& RecordSet::row(std::size_t pos)
|
|||||||
|
|
||||||
std::size_t RecordSet::rowCount() const
|
std::size_t RecordSet::rowCount() const
|
||||||
{
|
{
|
||||||
poco_assert (extractions().size());
|
if (extractions().size() == 0) return 0;
|
||||||
|
|
||||||
std::size_t rc = subTotalRowCount();
|
std::size_t rc = subTotalRowCount();
|
||||||
if (!isFiltered()) return rc;
|
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
|
bool RecordSet::isAllowed(std::size_t row) const
|
||||||
{
|
{
|
||||||
if (!isFiltered()) return true;
|
if (!isFiltered()) return true;
|
||||||
|
@ -14,12 +14,18 @@
|
|||||||
|
|
||||||
#include "Poco/Data/SQLChannel.h"
|
#include "Poco/Data/SQLChannel.h"
|
||||||
#include "Poco/Data/SessionFactory.h"
|
#include "Poco/Data/SessionFactory.h"
|
||||||
|
#include "Poco/Data/BulkBinding.h"
|
||||||
#include "Poco/DateTime.h"
|
#include "Poco/DateTime.h"
|
||||||
|
#include "Poco/DateTimeFormatter.h"
|
||||||
|
#include "Poco/DateTimeFormat.h"
|
||||||
#include "Poco/LoggingFactory.h"
|
#include "Poco/LoggingFactory.h"
|
||||||
#include "Poco/Instantiator.h"
|
#include "Poco/Instantiator.h"
|
||||||
#include "Poco/NumberParser.h"
|
#include "Poco/NumberParser.h"
|
||||||
#include "Poco/NumberFormatter.h"
|
#include "Poco/NumberFormatter.h"
|
||||||
|
#include "Poco/Stopwatch.h"
|
||||||
#include "Poco/Format.h"
|
#include "Poco/Format.h"
|
||||||
|
#include "Poco/File.h"
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
namespace Poco {
|
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_MAX_AGE("keep");
|
||||||
const std::string SQLChannel::PROP_ASYNC("async");
|
const std::string SQLChannel::PROP_ASYNC("async");
|
||||||
const std::string SQLChannel::PROP_TIMEOUT("timeout");
|
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_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():
|
SQLChannel::SQLChannel():
|
||||||
_name("-"),
|
_name("-"),
|
||||||
_table("T_POCO_LOG"),
|
_table("T_POCO_LOG"),
|
||||||
|
_tableChanged(true),
|
||||||
_timeout(1000),
|
_timeout(1000),
|
||||||
_throw(true),
|
_minBatch(DEFAULT_MIN_BATCH_SIZE),
|
||||||
_async(true),
|
_maxBatch(DEFAULT_MAX_BATCH_SIZE),
|
||||||
|
_bulk(true),
|
||||||
|
_throw(false),
|
||||||
_pid(),
|
_pid(),
|
||||||
_tid(),
|
_tid(),
|
||||||
_priority()
|
_priority(),
|
||||||
|
_reconnect(false),
|
||||||
|
_running(false),
|
||||||
|
_stop(false),
|
||||||
|
_logged(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SQLChannel::SQLChannel(const std::string& connector,
|
SQLChannel::SQLChannel(const std::string& connector,
|
||||||
const std::string& connect,
|
const std::string& connect,
|
||||||
const std::string& name):
|
const std::string& name,
|
||||||
|
const std::string& table,
|
||||||
|
int timeout,
|
||||||
|
int minBatch,
|
||||||
|
int maxBatch) :
|
||||||
_connector(connector),
|
_connector(connector),
|
||||||
_connect(connect),
|
_connect(connect),
|
||||||
_name(name),
|
_name(name),
|
||||||
_table("T_POCO_LOG"),
|
_table(table),
|
||||||
_timeout(1000),
|
_tableChanged(true),
|
||||||
_throw(true),
|
_timeout(timeout),
|
||||||
_async(true),
|
_minBatch(minBatch),
|
||||||
|
_maxBatch(maxBatch),
|
||||||
|
_bulk(false),
|
||||||
|
_throw(false),
|
||||||
_pid(),
|
_pid(),
|
||||||
_tid(),
|
_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
|
try
|
||||||
{
|
{
|
||||||
close();
|
stop();
|
||||||
|
close(_timeout);
|
||||||
|
wait();
|
||||||
|
if (_pFileChannel)
|
||||||
|
_pFileChannel->close();
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
@ -84,70 +122,177 @@ SQLChannel::~SQLChannel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SQLChannel::open()
|
std::string SQLChannel::maskPwd()
|
||||||
{
|
{
|
||||||
if (_connector.empty() || _connect.empty())
|
std::string displayConnect = _connect;
|
||||||
throw IllegalStateException("Connector and connect string must be non-empty.");
|
Poco::istring is1(displayConnect.c_str());
|
||||||
|
Poco::istring is2("pwd=");
|
||||||
_pSession = new Session(_connector, _connect);
|
std::size_t pos1 = Poco::isubstr(is1, is2);
|
||||||
initLogStatement();
|
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)
|
void SQLChannel::log(const Message& msg)
|
||||||
{
|
{
|
||||||
if (_async) logAsync(msg);
|
_logQueue.enqueueNotification(new LogNotification(msg));
|
||||||
else logSync(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
|
try
|
||||||
{
|
{
|
||||||
_pLogStatement->execute();
|
return execSQL();
|
||||||
}
|
}
|
||||||
catch (Exception&)
|
catch (Exception&)
|
||||||
{
|
{
|
||||||
if (_throw) throw;
|
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)
|
void SQLChannel::setProperty(const std::string& name, const std::string& value)
|
||||||
{
|
{
|
||||||
|
Poco::FastMutex::ScopedLock l(_mutex);
|
||||||
|
|
||||||
if (name == PROP_NAME)
|
if (name == PROP_NAME)
|
||||||
{
|
{
|
||||||
_name = value;
|
_name = value;
|
||||||
@ -156,17 +301,19 @@ void SQLChannel::setProperty(const std::string& name, const std::string& value)
|
|||||||
else if (name == PROP_CONNECTOR)
|
else if (name == PROP_CONNECTOR)
|
||||||
{
|
{
|
||||||
_connector = value;
|
_connector = value;
|
||||||
close(); open();
|
reconnect();
|
||||||
}
|
}
|
||||||
else if (name == PROP_CONNECT)
|
else if (name == PROP_CONNECT)
|
||||||
{
|
{
|
||||||
_connect = value;
|
_connect = value;
|
||||||
close(); open();
|
reconnect();
|
||||||
}
|
}
|
||||||
else if (name == PROP_TABLE)
|
else if (name == PROP_TABLE)
|
||||||
{
|
{
|
||||||
_table = value;
|
_table = value;
|
||||||
initLogStatement();
|
if (_pArchiveStrategy)
|
||||||
|
_pArchiveStrategy->setSource(value);
|
||||||
|
_tableChanged = true;
|
||||||
}
|
}
|
||||||
else if (name == PROP_ARCHIVE_TABLE)
|
else if (name == PROP_ARCHIVE_TABLE)
|
||||||
{
|
{
|
||||||
@ -180,7 +327,9 @@ void SQLChannel::setProperty(const std::string& name, const std::string& value)
|
|||||||
}
|
}
|
||||||
else
|
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)
|
else if (name == PROP_MAX_AGE)
|
||||||
@ -195,15 +344,14 @@ void SQLChannel::setProperty(const std::string& name, const std::string& value)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ArchiveByAgeStrategy* p = new ArchiveByAgeStrategy(_connector, _connect, _table);
|
std::string destination = ArchiveByAgeStrategy::DEFAULT_ARCHIVE_DESTINATION;
|
||||||
p->setThreshold(value);
|
if (_pArchiveStrategy) destination = _pArchiveStrategy->getDestination();
|
||||||
_pArchiveStrategy = p;
|
_pArchiveStrategy = new ArchiveByAgeStrategy(_connector, _connect, _table, destination, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (name == PROP_ASYNC)
|
else if (name == PROP_ASYNC)
|
||||||
{
|
{
|
||||||
_async = isTrue(value);
|
// no-op
|
||||||
initLogStatement();
|
|
||||||
}
|
}
|
||||||
else if (name == PROP_TIMEOUT)
|
else if (name == PROP_TIMEOUT)
|
||||||
{
|
{
|
||||||
@ -212,10 +360,32 @@ void SQLChannel::setProperty(const std::string& name, const std::string& value)
|
|||||||
else
|
else
|
||||||
_timeout = NumberParser::parse(value);
|
_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)
|
else if (name == PROP_THROW)
|
||||||
{
|
{
|
||||||
_throw = isTrue(value);
|
_throw = isTrue(value);
|
||||||
}
|
}
|
||||||
|
else if (name == PROP_FILE)
|
||||||
|
{
|
||||||
|
_file = value;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Channel::setProperty(name, value);
|
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
|
std::string SQLChannel::getProperty(const std::string& name) const
|
||||||
{
|
{
|
||||||
|
Poco::FastMutex::ScopedLock l(_mutex);
|
||||||
|
|
||||||
if (name == PROP_NAME)
|
if (name == PROP_NAME)
|
||||||
{
|
{
|
||||||
if (_name != "-") return _name;
|
if (_name != "-") return _name;
|
||||||
@ -254,11 +426,28 @@ std::string SQLChannel::getProperty(const std::string& name) const
|
|||||||
{
|
{
|
||||||
return NumberFormatter::format(_timeout);
|
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)
|
else if (name == PROP_THROW)
|
||||||
{
|
{
|
||||||
if (_throw) return "true";
|
if (_throw) return "true";
|
||||||
else return "false";
|
else return "false";
|
||||||
}
|
}
|
||||||
|
else if (name == PROP_FILE)
|
||||||
|
{
|
||||||
|
return _file;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Channel::getProperty(name);
|
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::size_t n = 0;
|
||||||
|
|
||||||
|
if (!pFileChannel) pFileChannel = new FileChannel(fileName);
|
||||||
|
if (pFileChannel)
|
||||||
|
{
|
||||||
std::string sql;
|
std::string sql;
|
||||||
Poco::format(sql, "INSERT INTO %s VALUES (?,?,?,?,?,?,?,?)", _table);
|
Poco::format(sql, SQL_INSERT_STMT, _table, std::string());
|
||||||
*_pLogStatement << sql,
|
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(_source),
|
||||||
use(_name),
|
use(names),
|
||||||
use(_pid),
|
use(_pid),
|
||||||
use(_thread),
|
use(_thread),
|
||||||
use(_tid),
|
use(_tid),
|
||||||
use(_priority),
|
use(_priority),
|
||||||
use(_text),
|
use(_text),
|
||||||
use(_dateTime);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
if (_async) _pLogStatement->setAsync();
|
|
||||||
|
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()
|
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();
|
purgeDeadSessions();
|
||||||
|
|
||||||
if (_idleSessions.empty())
|
if (_idleSessions.empty())
|
||||||
@ -95,7 +95,6 @@ Session SessionPool::get()
|
|||||||
|
|
||||||
void SessionPool::purgeDeadSessions()
|
void SessionPool::purgeDeadSessions()
|
||||||
{
|
{
|
||||||
Poco::Mutex::ScopedLock lock(_mutex);
|
|
||||||
if (_shutdown) return;
|
if (_shutdown) return;
|
||||||
|
|
||||||
SessionList::iterator it = _idleSessions.begin();
|
SessionList::iterator it = _idleSessions.begin();
|
||||||
@ -139,9 +138,9 @@ int SessionPool::connTimeout() const
|
|||||||
|
|
||||||
int SessionPool::dead()
|
int SessionPool::dead()
|
||||||
{
|
{
|
||||||
Poco::Mutex::ScopedLock lock(_mutex);
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
|
Poco::Mutex::ScopedLock lock(_mutex);
|
||||||
SessionList::iterator it = _activeSessions.begin();
|
SessionList::iterator it = _activeSessions.begin();
|
||||||
SessionList::iterator itEnd = _activeSessions.end();
|
SessionList::iterator itEnd = _activeSessions.end();
|
||||||
for (; it != itEnd; ++it)
|
for (; it != itEnd; ++it)
|
||||||
@ -156,7 +155,6 @@ int SessionPool::dead()
|
|||||||
|
|
||||||
int SessionPool::allocated() const
|
int SessionPool::allocated() const
|
||||||
{
|
{
|
||||||
Poco::Mutex::ScopedLock lock(_mutex);
|
|
||||||
return _nSessions;
|
return _nSessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,21 +168,24 @@ int SessionPool::available() const
|
|||||||
|
|
||||||
void SessionPool::setFeature(const std::string& name, bool state)
|
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 (_shutdown) throw InvalidAccessException("Session pool has been shut down.");
|
||||||
|
|
||||||
if (_nSessions > 0)
|
if (_nSessions > 0)
|
||||||
throw InvalidAccessException("Features can not be set after the first session was created.");
|
throw InvalidAccessException("Features can not be set after the first session was created.");
|
||||||
|
|
||||||
|
Poco::Mutex::ScopedLock lock(_mutex);
|
||||||
_featureMap.insert(FeatureMap::ValueType(name, state));
|
_featureMap.insert(FeatureMap::ValueType(name, state));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SessionPool::getFeature(const std::string& name)
|
bool SessionPool::getFeature(const std::string& name)
|
||||||
{
|
{
|
||||||
FeatureMap::ConstIterator it = _featureMap.find(name);
|
|
||||||
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);
|
||||||
|
FeatureMap::ConstIterator it = _featureMap.find(name);
|
||||||
|
|
||||||
if (_featureMap.end() == it)
|
if (_featureMap.end() == it)
|
||||||
throw NotFoundException("Feature not found:" + name);
|
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)
|
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 (_shutdown) throw InvalidAccessException("Session pool has been shut down.");
|
||||||
|
|
||||||
if (_nSessions > 0)
|
if (_nSessions > 0)
|
||||||
throw InvalidAccessException("Properties can not be set after first session was created.");
|
throw InvalidAccessException("Properties can not be set after first session was created.");
|
||||||
|
|
||||||
|
Poco::Mutex::ScopedLock lock(_mutex);
|
||||||
_propertyMap.insert(PropertyMap::ValueType(name, value));
|
_propertyMap.insert(PropertyMap::ValueType(name, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Poco::Any SessionPool::getProperty(const std::string& name)
|
Poco::Any SessionPool::getProperty(const std::string& name)
|
||||||
{
|
{
|
||||||
|
Poco::Mutex::ScopedLock lock(_mutex);
|
||||||
PropertyMap::ConstIterator it = _propertyMap.find(name);
|
PropertyMap::ConstIterator it = _propertyMap.find(name);
|
||||||
|
|
||||||
if (_propertyMap.end() == it)
|
if (_propertyMap.end() == it)
|
||||||
@ -234,9 +236,9 @@ void SessionPool::customizeSession(Session&)
|
|||||||
|
|
||||||
void SessionPool::putBack(PooledSessionHolderPtr pHolder)
|
void SessionPool::putBack(PooledSessionHolderPtr pHolder)
|
||||||
{
|
{
|
||||||
Poco::Mutex::ScopedLock lock(_mutex);
|
|
||||||
if (_shutdown) return;
|
if (_shutdown) return;
|
||||||
|
|
||||||
|
Poco::Mutex::ScopedLock lock(_mutex);
|
||||||
SessionList::iterator it = std::find(_activeSessions.begin(), _activeSessions.end(), pHolder);
|
SessionList::iterator it = std::find(_activeSessions.begin(), _activeSessions.end(), pHolder);
|
||||||
if (it != _activeSessions.end())
|
if (it != _activeSessions.end())
|
||||||
{
|
{
|
||||||
@ -287,9 +289,9 @@ void SessionPool::putBack(PooledSessionHolderPtr pHolder)
|
|||||||
|
|
||||||
void SessionPool::onJanitorTimer(Poco::Timer&)
|
void SessionPool::onJanitorTimer(Poco::Timer&)
|
||||||
{
|
{
|
||||||
Poco::Mutex::ScopedLock lock(_mutex);
|
|
||||||
if (_shutdown) return;
|
if (_shutdown) return;
|
||||||
|
|
||||||
|
Poco::Mutex::ScopedLock lock(_mutex);
|
||||||
SessionList::iterator it = _idleSessions.begin();
|
SessionList::iterator it = _idleSessions.begin();
|
||||||
while (_nSessions > _minSessions && it != _idleSessions.end())
|
while (_nSessions > _minSessions && it != _idleSessions.end())
|
||||||
{
|
{
|
||||||
@ -312,15 +314,9 @@ void SessionPool::onJanitorTimer(Poco::Timer&)
|
|||||||
|
|
||||||
void SessionPool::shutdown()
|
void SessionPool::shutdown()
|
||||||
{
|
{
|
||||||
{
|
if (_shutdown.exchange(true)) return;
|
||||||
Poco::Mutex::ScopedLock lock(_mutex);
|
|
||||||
if (_shutdown) return;
|
|
||||||
_shutdown = true;
|
_shutdown = true;
|
||||||
}
|
|
||||||
|
|
||||||
_janitorTimer.stop();
|
_janitorTimer.stop();
|
||||||
|
|
||||||
Poco::Mutex::ScopedLock lock(_mutex);
|
|
||||||
closeAll(_idleSessions);
|
closeAll(_idleSessions);
|
||||||
closeAll(_activeSessions);
|
closeAll(_activeSessions);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ namespace Data {
|
|||||||
|
|
||||||
Transaction::Transaction(Poco::Data::Session& rSession, Poco::Logger* pLogger):
|
Transaction::Transaction(Poco::Data::Session& rSession, Poco::Logger* pLogger):
|
||||||
_rSession(rSession),
|
_rSession(rSession),
|
||||||
|
_autoCommit(_rSession.hasFeature("autoCommit") ? _rSession.getFeature("autoCommit") : false),
|
||||||
_pLogger(pLogger)
|
_pLogger(pLogger)
|
||||||
{
|
{
|
||||||
begin();
|
begin();
|
||||||
@ -30,6 +31,7 @@ Transaction::Transaction(Poco::Data::Session& rSession, Poco::Logger* pLogger):
|
|||||||
|
|
||||||
Transaction::Transaction(Poco::Data::Session& rSession, bool start):
|
Transaction::Transaction(Poco::Data::Session& rSession, bool start):
|
||||||
_rSession(rSession),
|
_rSession(rSession),
|
||||||
|
_autoCommit(_rSession.hasFeature("autoCommit") ? _rSession.getFeature("autoCommit") : false),
|
||||||
_pLogger(0)
|
_pLogger(0)
|
||||||
{
|
{
|
||||||
if (start) begin();
|
if (start) begin();
|
||||||
@ -71,7 +73,11 @@ Transaction::~Transaction()
|
|||||||
void Transaction::begin()
|
void Transaction::begin()
|
||||||
{
|
{
|
||||||
if (!_rSession.isTransaction())
|
if (!_rSession.isTransaction())
|
||||||
|
{
|
||||||
|
if (_autoCommit)
|
||||||
|
_rSession.setFeature("autoCommit", false);
|
||||||
_rSession.begin();
|
_rSession.begin();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
throw InvalidAccessException("Transaction in progress.");
|
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
|
try
|
||||||
{
|
{
|
||||||
std::vector<std::string>::const_iterator it = sql.begin();
|
std::vector<std::string>::const_iterator it = sql.begin();
|
||||||
std::vector<std::string>::const_iterator end = sql.end();
|
std::vector<std::string>::const_iterator end = sql.end();
|
||||||
for (; it != end; ++it) execute(*it, it + 1 == end ? true : false);
|
for (; it != end; ++it) execute(*it, it + 1 == end ? true : false);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception& ex)
|
catch (Exception& ex)
|
||||||
{
|
{
|
||||||
if (_pLogger) _pLogger->log(ex);
|
if (_pLogger) _pLogger->log(ex);
|
||||||
|
if(info) *info = ex.displayText();
|
||||||
}
|
}
|
||||||
|
|
||||||
rollback();
|
rollback();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -109,6 +122,8 @@ void Transaction::commit()
|
|||||||
_pLogger->debug("Committing transaction.");
|
_pLogger->debug("Committing transaction.");
|
||||||
|
|
||||||
_rSession.commit();
|
_rSession.commit();
|
||||||
|
if (_autoCommit)
|
||||||
|
_rSession.setFeature("autoCommit", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -118,6 +133,8 @@ void Transaction::rollback()
|
|||||||
_pLogger->debug("Rolling back transaction.");
|
_pLogger->debug("Rolling back transaction.");
|
||||||
|
|
||||||
_rSession.rollback();
|
_rSession.rollback();
|
||||||
|
if (_autoCommit)
|
||||||
|
_rSession.setFeature("autoCommit", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,9 +11,10 @@
|
|||||||
LINKMODE ?= SHARED
|
LINKMODE ?= SHARED
|
||||||
|
|
||||||
SANITIZEFLAGS ?=
|
SANITIZEFLAGS ?=
|
||||||
#-fsanitize=address
|
# sanitize flags:
|
||||||
#-fsanitize=undefined
|
# -fsanitize=address
|
||||||
#-fsanitize=thread
|
# -fsanitize=undefined
|
||||||
|
# -fsanitize=thread
|
||||||
|
|
||||||
#
|
#
|
||||||
# Define Tools
|
# Define Tools
|
||||||
|
Loading…
x
Reference in New Issue
Block a user