mirror of
https://github.com/pocoproject/poco.git
synced 2025-03-27 08:46:17 +01:00
ODBC null value support and tests (tested win32 all & Linux PostgreSQL)
This commit is contained in:
parent
7fac905b65
commit
31860214d3
@ -171,34 +171,7 @@ private:
|
||||
|
||||
SQLINTEGER colSize = 0;
|
||||
SQLSMALLINT decDigits = 0;
|
||||
try
|
||||
{
|
||||
// This is a proper way to find out about database specific type sizes.
|
||||
// Not all drivers are equally willing to cooperate in this matter, though.
|
||||
// Hence the funky flow control.
|
||||
if (_pTypeInfo)
|
||||
{
|
||||
colSize = _pTypeInfo->getInfo(cDataType, "COLUMN_SIZE");
|
||||
decDigits = _pTypeInfo->getInfo(cDataType, "MINIMUM_SCALE");
|
||||
}
|
||||
else throw NotFoundException();
|
||||
}catch (NotFoundException&)
|
||||
{
|
||||
try
|
||||
{
|
||||
Parameter p(_rStmt, pos);
|
||||
colSize = (SQLINTEGER) p.columnSize();
|
||||
decDigits = (SQLSMALLINT) p.decimalDigits();
|
||||
}catch (StatementException&)
|
||||
{
|
||||
try
|
||||
{
|
||||
ODBCColumn c(_rStmt, pos);
|
||||
colSize = (SQLINTEGER) c.length();
|
||||
decDigits = (SQLSMALLINT) c.precision();
|
||||
}catch (StatementException&) { }
|
||||
}
|
||||
}
|
||||
getColSizeAndPrecision(pos, cDataType, colSize, decDigits);
|
||||
|
||||
if (Utility::isError(SQLBindParameter(_rStmt,
|
||||
(SQLUSMALLINT) pos + 1,
|
||||
@ -215,6 +188,18 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void getColSizeAndPrecision(std::size_t pos,
|
||||
SQLSMALLINT cDataType,
|
||||
SQLINTEGER& colSize,
|
||||
SQLSMALLINT& decDigits);
|
||||
/// Used to retrieve column size and precision.
|
||||
/// Not all drivers cooperate with this inquiry under all circumstances
|
||||
/// This function runs for query and stored procedure parameters (in and
|
||||
/// out-bound). Some drivers, however, do not care about knowing this
|
||||
/// information to start with. For that reason, after all the attempts
|
||||
/// to discover the required values are unsuccesfully exhausted, the values
|
||||
/// are both set to zero and no exception is thrown.
|
||||
|
||||
void bindNull(std::size_t pos, SQLSMALLINT cDataType);
|
||||
/// Utility function for binding null values.
|
||||
/// For in-bound purposes only.
|
||||
@ -257,21 +242,15 @@ inline void Binder::bind(std::size_t pos, const Poco::UInt16& val, Direction dir
|
||||
}
|
||||
|
||||
|
||||
inline void Binder::bind(std::size_t pos, const Poco::UInt32& val, Direction dir)
|
||||
{
|
||||
bindImpl(pos, val, SQL_C_ULONG, dir);
|
||||
}
|
||||
|
||||
|
||||
inline void Binder::bind(std::size_t pos, const Poco::Int32& val, Direction dir)
|
||||
{
|
||||
bindImpl(pos, val, SQL_C_SLONG, dir);
|
||||
}
|
||||
|
||||
|
||||
inline void Binder::bind(std::size_t pos, const Poco::UInt64& val, Direction dir)
|
||||
inline void Binder::bind(std::size_t pos, const Poco::UInt32& val, Direction dir)
|
||||
{
|
||||
bindImpl(pos, val, SQL_C_UBIGINT, dir);
|
||||
bindImpl(pos, val, SQL_C_ULONG, dir);
|
||||
}
|
||||
|
||||
|
||||
@ -281,6 +260,12 @@ inline void Binder::bind(std::size_t pos, const Poco::Int64& val, Direction dir)
|
||||
}
|
||||
|
||||
|
||||
inline void Binder::bind(std::size_t pos, const Poco::UInt64& val, Direction dir)
|
||||
{
|
||||
bindImpl(pos, val, SQL_C_UBIGINT, dir);
|
||||
}
|
||||
|
||||
|
||||
inline void Binder::bind(std::size_t pos, const float& val, Direction dir)
|
||||
{
|
||||
bindImpl(pos, val, SQL_C_FLOAT, dir);
|
||||
@ -305,15 +290,6 @@ inline void Binder::bind(std::size_t pos, const char& val, Direction dir)
|
||||
}
|
||||
|
||||
|
||||
inline void Binder::bind(std::size_t pos, const NullData& val, Direction dir)
|
||||
{
|
||||
if (isOutBound(dir) || !isInBound(dir))
|
||||
throw NotImplementedException("NULL parameter type can only be inbound.");
|
||||
|
||||
throw NotImplementedException("TODO");
|
||||
}
|
||||
|
||||
|
||||
inline void Binder::setDataBinding(Binder::ParameterBinding binding)
|
||||
{
|
||||
_paramBinding = binding;
|
||||
|
@ -130,6 +130,9 @@ public:
|
||||
bool isNull(std::size_t pos);
|
||||
/// Returns true if the current row value at pos column is null.
|
||||
|
||||
void reset();
|
||||
/// Resets the internally cached null value indicators.
|
||||
|
||||
private:
|
||||
static const int CHUNK_SIZE = 1024;
|
||||
/// Amount of data retrieved in one SQLGetData() request when doing manual extract.
|
||||
@ -145,9 +148,15 @@ private:
|
||||
/// (i.e. the bound buffer is large enough to receive
|
||||
/// the returned value)
|
||||
|
||||
void resizeLengths(std::size_t pos);
|
||||
/// Resizes the vector holding extracted data lengths to the
|
||||
/// appropriate size.
|
||||
|
||||
template<typename T>
|
||||
bool extractBoundImpl(std::size_t pos, T& val)
|
||||
{
|
||||
if (isNull(pos)) return false;
|
||||
|
||||
poco_assert (typeid(T) == _rPreparation[pos].type());
|
||||
val = *AnyCast<T>(&_rPreparation[pos]);
|
||||
return true;
|
||||
@ -157,32 +166,42 @@ private:
|
||||
bool extractManualImpl(std::size_t pos, T& val, SQLSMALLINT cType)
|
||||
{
|
||||
SQLRETURN rc = 0;
|
||||
SQLLEN len = 0;
|
||||
T value = (T) 0;
|
||||
|
||||
|
||||
resizeLengths(pos);
|
||||
|
||||
rc = SQLGetData(_rStmt,
|
||||
(SQLUSMALLINT) pos + 1,
|
||||
cType, //C data type
|
||||
&value, //returned value
|
||||
0, //buffer length (ignored)
|
||||
&len); //length indicator
|
||||
|
||||
//for fixed-length data, buffer must be large enough
|
||||
//otherwise, driver may write past the end
|
||||
poco_assert_dbg (len <= sizeof(T));
|
||||
&_lengths[pos]); //length indicator
|
||||
|
||||
if (Utility::isError(rc))
|
||||
throw StatementException(_rStmt, "SQLGetData()");
|
||||
|
||||
if (SQL_NULL_DATA == len) val = (T) 0;
|
||||
else val = value;
|
||||
if (isNullLengthIndicator(_lengths[pos]))
|
||||
return false;
|
||||
else
|
||||
{
|
||||
//for fixed-length data, buffer must be large enough
|
||||
//otherwise, driver may write past the end
|
||||
poco_assert_dbg (_lengths[pos] <= sizeof(T));
|
||||
val = value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isNullLengthIndicator(SQLLEN val) const;
|
||||
/// The reason for this utility wrapper are platforms where
|
||||
/// SQLLEN macro (a.k.a. SQLINTEGER) yields 64-bit value,
|
||||
/// while SQL_NULL_DATA (#define'd as -1 literal) remains 32-bit.
|
||||
|
||||
const StatementHandle& _rStmt;
|
||||
Preparation& _rPreparation;
|
||||
Preparation::DataExtraction _dataExtraction;
|
||||
std::vector<SQLLEN> _lengths;
|
||||
};
|
||||
|
||||
|
||||
@ -203,9 +222,22 @@ inline Preparation::DataExtraction Extractor::getDataExtraction() const
|
||||
}
|
||||
|
||||
|
||||
inline bool Extractor::isNull(std::size_t pos)
|
||||
inline void Extractor::reset()
|
||||
{
|
||||
throw NotImplementedException("TODO");
|
||||
_lengths.clear();
|
||||
}
|
||||
|
||||
|
||||
inline void Extractor::resizeLengths(std::size_t pos)
|
||||
{
|
||||
if (pos >= _lengths.size())
|
||||
_lengths.resize(pos + 1, (SQLLEN) 0);
|
||||
}
|
||||
|
||||
|
||||
inline bool Extractor::isNullLengthIndicator(SQLLEN val) const
|
||||
{
|
||||
return SQL_NULL_DATA == (int) val;
|
||||
}
|
||||
|
||||
|
||||
|
@ -168,10 +168,11 @@ public:
|
||||
/// Returned length for variable length fields is the one
|
||||
/// supported by this implementation, not the underlying DB.
|
||||
|
||||
std::size_t actualDataSize(std::size_t pos) const;
|
||||
int actualDataSize(std::size_t pos) const;
|
||||
/// Returns the returned length. This is usually
|
||||
/// equal to the column size, except for variable length fields
|
||||
/// (BLOB and variable length strings).
|
||||
/// For null values, the return value is -1 (SQL_NO_DATA)
|
||||
|
||||
void setDataExtraction(DataExtraction ext);
|
||||
/// Set data extraction mode.
|
||||
@ -355,7 +356,7 @@ inline std::size_t Preparation::maxDataSize(std::size_t pos) const
|
||||
}
|
||||
|
||||
|
||||
inline std::size_t Preparation::actualDataSize(std::size_t pos) const
|
||||
inline int Preparation::actualDataSize(std::size_t pos) const
|
||||
{
|
||||
poco_assert (pos >= 0 && pos < _pValues.size());
|
||||
poco_assert (_pLengths[pos]);
|
||||
|
@ -190,15 +190,7 @@ void Binder::bind(std::size_t pos, const Poco::DateTime& val, Direction dir)
|
||||
|
||||
SQLINTEGER colSize = 0;
|
||||
SQLSMALLINT decDigits = 0;
|
||||
|
||||
if (_pTypeInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
colSize = _pTypeInfo->getInfo(SQL_TYPE_TIMESTAMP, "COLUMN_SIZE");
|
||||
decDigits = _pTypeInfo->getInfo(SQL_TYPE_TIMESTAMP, "MINIMUM_SCALE");
|
||||
}catch (NotFoundException&) { }
|
||||
}
|
||||
getColSizeAndPrecision(pos, SQL_TYPE_TIMESTAMP, colSize, decDigits);
|
||||
|
||||
if (Utility::isError(SQLBindParameter(_rStmt,
|
||||
(SQLUSMALLINT) pos + 1,
|
||||
@ -216,6 +208,34 @@ void Binder::bind(std::size_t pos, const Poco::DateTime& val, Direction dir)
|
||||
}
|
||||
|
||||
|
||||
void Binder::bind(std::size_t pos, const NullData& val, Direction dir)
|
||||
{
|
||||
if (isOutBound(dir) || !isInBound(dir))
|
||||
throw NotImplementedException("NULL parameter type can only be inbound.");
|
||||
|
||||
switch (val)
|
||||
{
|
||||
case NULL_INT8: bindNull(pos, SQL_C_STINYINT); break;
|
||||
case NULL_UINT8: bindNull(pos, SQL_C_UTINYINT); break;
|
||||
case NULL_INT16: bindNull(pos, SQL_C_SSHORT); break;
|
||||
case NULL_UINT16: bindNull(pos, SQL_C_USHORT); break;
|
||||
case NULL_INT32: bindNull(pos, SQL_C_SLONG); break;
|
||||
case NULL_UINT32: bindNull(pos, SQL_C_ULONG); break;
|
||||
case NULL_INT64: bindNull(pos, SQL_C_SBIGINT); break;
|
||||
case NULL_UINT64: bindNull(pos, SQL_C_UBIGINT); break;
|
||||
case NULL_BOOL: bindNull(pos, Utility::boolDataType); break;
|
||||
case NULL_FLOAT: bindNull(pos, SQL_C_FLOAT); break;
|
||||
case NULL_DOUBLE: bindNull(pos, SQL_C_DOUBLE); break;
|
||||
case NULL_STRING: bindNull(pos, SQL_C_CHAR); break;
|
||||
case NULL_BLOB: bindNull(pos, SQL_C_BINARY); break;
|
||||
case NULL_TIMESTAMP: bindNull(pos, SQL_C_TIMESTAMP); break;
|
||||
|
||||
default:
|
||||
throw DataFormatException("Unsupported data type.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Binder::bindNull(std::size_t pos, SQLSMALLINT cDataType)
|
||||
{
|
||||
_inParams.insert(ParamMap::value_type(0, 0));
|
||||
@ -223,28 +243,11 @@ void Binder::bindNull(std::size_t pos, SQLSMALLINT cDataType)
|
||||
SQLLEN* pLenIn = new SQLLEN;
|
||||
*pLenIn = SQL_NULL_DATA;
|
||||
|
||||
if (PB_AT_EXEC == _paramBinding)
|
||||
*pLenIn = SQL_LEN_DATA_AT_EXEC(SQL_NULL_DATA);
|
||||
|
||||
_lengthIndicator.push_back(pLenIn);
|
||||
|
||||
SQLINTEGER colSize = 0;
|
||||
SQLSMALLINT decDigits = 0;
|
||||
|
||||
try
|
||||
{
|
||||
Parameter p(_rStmt, pos);
|
||||
colSize = (SQLINTEGER) p.columnSize();
|
||||
decDigits = (SQLSMALLINT) p.decimalDigits();
|
||||
}catch (StatementException&)
|
||||
{
|
||||
try
|
||||
{
|
||||
ODBCColumn c(_rStmt, pos);
|
||||
colSize = (SQLINTEGER) c.length();
|
||||
decDigits = (SQLSMALLINT) c.precision();
|
||||
}catch (StatementException&) { }
|
||||
}
|
||||
getColSizeAndPrecision(pos, cDataType, colSize, decDigits);
|
||||
|
||||
if (Utility::isError(SQLBindParameter(_rStmt,
|
||||
(SQLUSMALLINT) pos + 1,
|
||||
@ -308,4 +311,49 @@ void Binder::synchronize()
|
||||
}
|
||||
|
||||
|
||||
void Binder::getColSizeAndPrecision(std::size_t pos,
|
||||
SQLSMALLINT cDataType,
|
||||
SQLINTEGER& colSize,
|
||||
SQLSMALLINT& decDigits)
|
||||
{
|
||||
// Not all drivers are equally willing to cooperate in this matter.
|
||||
// Hence the funky flow control.
|
||||
try
|
||||
{
|
||||
if (_pTypeInfo)
|
||||
{
|
||||
colSize = _pTypeInfo->getInfo(cDataType, "COLUMN_SIZE");
|
||||
decDigits = _pTypeInfo->getInfo(cDataType, "MINIMUM_SCALE");
|
||||
return;
|
||||
}
|
||||
else
|
||||
throw NotFoundException();
|
||||
}catch (NotFoundException&)
|
||||
{
|
||||
try
|
||||
{
|
||||
Parameter p(_rStmt, pos);
|
||||
colSize = (SQLINTEGER) p.columnSize();
|
||||
decDigits = (SQLSMALLINT) p.decimalDigits();
|
||||
return;
|
||||
}catch (StatementException&)
|
||||
{
|
||||
try
|
||||
{
|
||||
ODBCColumn c(_rStmt, pos);
|
||||
colSize = (SQLINTEGER) c.length();
|
||||
decDigits = (SQLSMALLINT) c.precision();
|
||||
return;
|
||||
}catch (StatementException&) { }
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
} } } // namespace Poco::Data::ODBC
|
||||
|
@ -70,6 +70,8 @@ Extractor::~Extractor()
|
||||
template<>
|
||||
bool Extractor::extractBoundImpl<std::string>(std::size_t pos, std::string& val)
|
||||
{
|
||||
if (isNull(pos)) return false;
|
||||
|
||||
std::size_t dataSize = _rPreparation.actualDataSize(pos);
|
||||
SharedPtr<char>& sp = RefAnyCast<SharedPtr<char> >(_rPreparation[pos]);
|
||||
std::size_t len = strlen(sp);
|
||||
@ -83,6 +85,8 @@ bool Extractor::extractBoundImpl<std::string>(std::size_t pos, std::string& val)
|
||||
template<>
|
||||
bool Extractor::extractBoundImpl<Poco::Data::BLOB>(std::size_t pos, Poco::Data::BLOB& val)
|
||||
{
|
||||
if (isNull(pos)) return false;
|
||||
|
||||
std::size_t dataSize = _rPreparation.actualDataSize(pos);
|
||||
checkDataSize(dataSize);
|
||||
SharedPtr<char>& sp = RefAnyCast<SharedPtr<char> >(_rPreparation[pos]);
|
||||
@ -94,6 +98,8 @@ bool Extractor::extractBoundImpl<Poco::Data::BLOB>(std::size_t pos, Poco::Data::
|
||||
template<>
|
||||
bool Extractor::extractBoundImpl<Poco::DateTime>(std::size_t pos, Poco::DateTime& val)
|
||||
{
|
||||
if (isNull(pos)) return false;
|
||||
|
||||
std::size_t dataSize = _rPreparation.actualDataSize(pos);
|
||||
checkDataSize(dataSize);
|
||||
SharedPtr<SQL_TIMESTAMP_STRUCT>& sp = RefAnyCast<SharedPtr<SQL_TIMESTAMP_STRUCT> >(_rPreparation[pos]);
|
||||
@ -109,12 +115,14 @@ bool Extractor::extractManualImpl<std::string>(std::size_t pos, std::string& val
|
||||
std::size_t maxSize = _rPreparation.getMaxFieldSize();
|
||||
std::size_t fetchedSize = 0;
|
||||
std::size_t totalSize = 0;
|
||||
|
||||
|
||||
SQLLEN len;
|
||||
std::auto_ptr<char> apChar(new char[CHUNK_SIZE]);
|
||||
char* pChar = apChar.get();
|
||||
SQLRETURN rc = 0;
|
||||
|
||||
val.clear();
|
||||
resizeLengths(pos);
|
||||
|
||||
do
|
||||
{
|
||||
@ -127,16 +135,21 @@ bool Extractor::extractManualImpl<std::string>(std::size_t pos, std::string& val
|
||||
CHUNK_SIZE, //buffer length
|
||||
&len); //length indicator
|
||||
|
||||
_lengths[pos] += len;
|
||||
|
||||
if (SQL_NO_DATA != rc && Utility::isError(rc))
|
||||
throw StatementException(_rStmt, "SQLGetData()");
|
||||
|
||||
if (SQL_NO_TOTAL == len)//unknown length, throw
|
||||
throw UnknownDataLengthException("Could not determine returned data length.");
|
||||
|
||||
if (SQL_NO_DATA == rc || SQL_NULL_DATA == len || !len)
|
||||
if (isNullLengthIndicator(len))
|
||||
return false;
|
||||
|
||||
if (SQL_NO_DATA == rc || !len)
|
||||
break;
|
||||
|
||||
fetchedSize = len > CHUNK_SIZE ? CHUNK_SIZE : len;
|
||||
fetchedSize = _lengths[pos] > CHUNK_SIZE ? CHUNK_SIZE : _lengths[pos];
|
||||
totalSize += fetchedSize;
|
||||
if (totalSize <= maxSize) val.append(pChar, fetchedSize);
|
||||
else throw DataException(format(FLD_SIZE_EXCEEDED_FMT,
|
||||
@ -162,7 +175,9 @@ bool Extractor::extractManualImpl<Poco::Data::BLOB>(std::size_t pos,
|
||||
std::auto_ptr<char> apChar(new char[CHUNK_SIZE]);
|
||||
char* pChar = apChar.get();
|
||||
SQLRETURN rc = 0;
|
||||
|
||||
val.clear();
|
||||
resizeLengths(pos);
|
||||
|
||||
do
|
||||
{
|
||||
@ -175,19 +190,26 @@ bool Extractor::extractManualImpl<Poco::Data::BLOB>(std::size_t pos,
|
||||
CHUNK_SIZE, //buffer length
|
||||
&len); //length indicator
|
||||
|
||||
_lengths[pos] += len;
|
||||
|
||||
if (SQL_NO_DATA != rc && Utility::isError(rc))
|
||||
throw StatementException(_rStmt, "SQLGetData()");
|
||||
|
||||
if (SQL_NO_TOTAL == len)//unknown length, throw
|
||||
throw UnknownDataLengthException("Could not determine returned data length.");
|
||||
|
||||
if (SQL_NO_DATA == rc || SQL_NULL_DATA == len || !len)
|
||||
if (isNullLengthIndicator(len))
|
||||
return false;
|
||||
|
||||
if (SQL_NO_DATA == rc || !len)
|
||||
break;
|
||||
|
||||
fetchedSize = len > CHUNK_SIZE ? CHUNK_SIZE : len;
|
||||
totalSize += fetchedSize;
|
||||
if (totalSize <= maxSize) val.appendRaw(pChar, fetchedSize);
|
||||
else throw DataException(format(FLD_SIZE_EXCEEDED_FMT,
|
||||
if (totalSize <= maxSize)
|
||||
val.appendRaw(pChar, fetchedSize);
|
||||
else
|
||||
throw DataException(format(FLD_SIZE_EXCEEDED_FMT,
|
||||
fetchedSize,
|
||||
maxSize));
|
||||
|
||||
@ -202,21 +224,21 @@ bool Extractor::extractManualImpl<Poco::DateTime>(std::size_t pos,
|
||||
Poco::DateTime& val,
|
||||
SQLSMALLINT cType)
|
||||
{
|
||||
SQLLEN len = 0;
|
||||
SQL_TIMESTAMP_STRUCT ts;
|
||||
resizeLengths(pos);
|
||||
|
||||
SQLRETURN rc = SQLGetData(_rStmt,
|
||||
(SQLUSMALLINT) pos + 1,
|
||||
cType, //C data type
|
||||
&ts, //returned value
|
||||
sizeof(ts), //buffer length
|
||||
&len); //length indicator
|
||||
&_lengths[pos]); //length indicator
|
||||
|
||||
if (Utility::isError(rc))
|
||||
throw StatementException(_rStmt, "SQLGetData()");
|
||||
|
||||
if (SQL_NULL_DATA == len)
|
||||
val.assign(0,0,0,0,0,0);
|
||||
if (isNullLengthIndicator(_lengths[pos]))
|
||||
return false;
|
||||
else
|
||||
Utility::dateTimeSync(val, ts);
|
||||
|
||||
@ -367,7 +389,7 @@ bool Extractor::extract(std::size_t pos, Poco::Any& val)
|
||||
switch (column.type())
|
||||
{
|
||||
case MetaColumn::FDT_INT8:
|
||||
{ Poco::Int8 i = 0; extract(pos, i); val = i; return true; }
|
||||
{ Poco::Int8 i = 0; extract(pos, i); val = i; return true; }
|
||||
|
||||
case MetaColumn::FDT_UINT8:
|
||||
{ Poco::UInt8 i = 0; extract(pos, i); val = i; return true; }
|
||||
@ -391,13 +413,13 @@ bool Extractor::extract(std::size_t pos, Poco::Any& val)
|
||||
{ Poco::UInt64 i = 0; extract(pos, i); val = i; return true; }
|
||||
|
||||
case MetaColumn::FDT_BOOL:
|
||||
{ bool b; extract(pos, b); val = b; return true; }
|
||||
{ bool b; extract(pos, b); val = b; return true; }
|
||||
|
||||
case MetaColumn::FDT_FLOAT:
|
||||
{ float f; extract(pos, f); val = f; return true; }
|
||||
{ float f; extract(pos, f); val = f; return true; }
|
||||
|
||||
case MetaColumn::FDT_DOUBLE:
|
||||
{ double d; extract(pos, d); val = d; return true; }
|
||||
{ double d; extract(pos, d); val = d; return true; }
|
||||
|
||||
case MetaColumn::FDT_STRING:
|
||||
{ std::string s; extract(pos, s); val = s; return true; }
|
||||
@ -416,6 +438,23 @@ bool Extractor::extract(std::size_t pos, Poco::Any& val)
|
||||
}
|
||||
|
||||
|
||||
bool Extractor::isNull(std::size_t pos)
|
||||
{
|
||||
if (Preparation::DE_MANUAL == _dataExtraction)
|
||||
{
|
||||
try
|
||||
{
|
||||
return isNullLengthIndicator(_lengths.at(pos));
|
||||
}catch (std::out_of_range& ex)
|
||||
{
|
||||
throw RangeException(ex.what());
|
||||
}
|
||||
}
|
||||
else
|
||||
return SQL_NULL_DATA == _rPreparation.actualDataSize(pos);
|
||||
}
|
||||
|
||||
|
||||
void Extractor::checkDataSize(std::size_t size)
|
||||
{
|
||||
std::size_t maxSize = _rPreparation.getMaxFieldSize();
|
||||
|
@ -249,6 +249,7 @@ bool ODBCStatementImpl::hasNext()
|
||||
return _stepCalled = nextRowReady();
|
||||
|
||||
_stepCalled = true;
|
||||
_pExtractor->reset();
|
||||
_nextResponse = SQLFetch(_stmt);
|
||||
|
||||
if (!nextRowReady())
|
||||
|
@ -861,6 +861,32 @@ void ODBCDB2Test::testInternalStorageType()
|
||||
}
|
||||
|
||||
|
||||
void ODBCDB2Test::testNull()
|
||||
{
|
||||
if (!_pSession) fail ("Test not available.");
|
||||
|
||||
// test for NOT NULL violation exception
|
||||
for (int i = 0; i < 8;)
|
||||
{
|
||||
recreateNullsTable("NOT NULL");
|
||||
_pSession->setFeature("autoBind", bindValues[i]);
|
||||
_pSession->setFeature("autoExtract", bindValues[i+1]);
|
||||
_pExecutor->notNulls();
|
||||
i += 2;
|
||||
}
|
||||
|
||||
// test for null insertion
|
||||
for (int i = 0; i < 8;)
|
||||
{
|
||||
recreateNullsTable();
|
||||
_pSession->setFeature("autoBind", bindValues[i]);
|
||||
_pSession->setFeature("autoExtract", bindValues[i+1]);
|
||||
_pExecutor->nulls();
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ODBCDB2Test::testStoredProcedure()
|
||||
{
|
||||
if (!_pSession) fail ("Test not available.");
|
||||
@ -1125,6 +1151,18 @@ void ODBCDB2Test::recreateVectorsTable()
|
||||
}
|
||||
|
||||
|
||||
void ODBCDB2Test::recreateNullsTable(const std::string& notNull)
|
||||
{
|
||||
dropObject("TABLE", "NullTest");
|
||||
try { *_pSession << format("CREATE TABLE NullTest (i INTEGER %s, r FLOAT %s, v VARCHAR(30) %s)",
|
||||
notNull,
|
||||
notNull,
|
||||
notNull), now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("recreateNullsTable()"); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("recreateNullsTable()"); }
|
||||
}
|
||||
|
||||
|
||||
bool ODBCDB2Test::canConnect(const std::string& driver, const std::string& dsn)
|
||||
{
|
||||
Utility::DriverMap::iterator itDrv = _drivers.begin();
|
||||
@ -1266,6 +1304,7 @@ CppUnit::Test* ODBCDB2Test::suite()
|
||||
CppUnit_addTest(pSuite, ODBCDB2Test, testInternalStorageType);
|
||||
CppUnit_addTest(pSuite, ODBCDB2Test, testStoredProcedure);
|
||||
CppUnit_addTest(pSuite, ODBCDB2Test, testStoredFunction);
|
||||
CppUnit_addTest(pSuite, ODBCDB2Test, testNull);
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
@ -122,6 +122,8 @@ public:
|
||||
void testStoredProcedure();
|
||||
void testStoredFunction();
|
||||
|
||||
void testNull();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
||||
@ -137,6 +139,7 @@ private:
|
||||
void recreateFloatsTable();
|
||||
void recreateTuplesTable();
|
||||
void recreateVectorsTable();
|
||||
void recreateNullsTable(const std::string& notNull = "");
|
||||
|
||||
static bool init(const std::string& driver, const std::string& dsn);
|
||||
static bool canConnect(const std::string& driver, const std::string& dsn);
|
||||
|
@ -863,6 +863,32 @@ void ODBCMySQLTest::testInternalStorageType()
|
||||
}
|
||||
|
||||
|
||||
void ODBCMySQLTest::testNull()
|
||||
{
|
||||
if (!_pSession) fail ("Test not available.");
|
||||
|
||||
// test for NOT NULL violation exception
|
||||
for (int i = 0; i < 8;)
|
||||
{
|
||||
recreateNullsTable("NOT NULL");
|
||||
_pSession->setFeature("autoBind", bindValues[i]);
|
||||
_pSession->setFeature("autoExtract", bindValues[i+1]);
|
||||
_pExecutor->notNulls("HYT00");
|
||||
i += 2;
|
||||
}
|
||||
|
||||
// test for null insertion
|
||||
for (int i = 0; i < 8;)
|
||||
{
|
||||
recreateNullsTable();
|
||||
_pSession->setFeature("autoBind", bindValues[i]);
|
||||
_pSession->setFeature("autoExtract", bindValues[i+1]);
|
||||
_pExecutor->nulls();
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ODBCMySQLTest::testStoredProcedure()
|
||||
{
|
||||
//MySQL is currently buggy in this area:
|
||||
@ -970,6 +996,18 @@ void ODBCMySQLTest::recreateVectorsTable()
|
||||
}
|
||||
|
||||
|
||||
void ODBCMySQLTest::recreateNullsTable(const std::string& notNull)
|
||||
{
|
||||
dropObject("TABLE", "NullTest");
|
||||
try { *_pSession << format("CREATE TABLE NullTest (i INTEGER %s, r FLOAT %s, v VARCHAR(30) %s)",
|
||||
notNull,
|
||||
notNull,
|
||||
notNull), now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("recreateNullsTable()"); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("recreateNullsTable()"); }
|
||||
}
|
||||
|
||||
|
||||
bool ODBCMySQLTest::canConnect(const std::string& driver, const std::string& dsn)
|
||||
{
|
||||
Utility::DriverMap::iterator itDrv = _drivers.begin();
|
||||
@ -1113,6 +1151,7 @@ CppUnit::Test* ODBCMySQLTest::suite()
|
||||
CppUnit_addTest(pSuite, ODBCMySQLTest, testStoredFunction);
|
||||
CppUnit_addTest(pSuite, ODBCMySQLTest, testInternalExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCMySQLTest, testInternalStorageType);
|
||||
CppUnit_addTest(pSuite, ODBCMySQLTest, testNull);
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
@ -124,6 +124,8 @@ public:
|
||||
void testStoredProcedure();
|
||||
void testStoredFunction();
|
||||
|
||||
void testNull();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
||||
@ -139,6 +141,7 @@ private:
|
||||
void recreateFloatsTable();
|
||||
void recreateTuplesTable();
|
||||
void recreateVectorsTable();
|
||||
void recreateNullsTable(const std::string& notNull = "");
|
||||
|
||||
static bool init(const std::string& driver, const std::string& dsn);
|
||||
static bool canConnect(const std::string& driver, const std::string& dsn);
|
||||
|
@ -873,6 +873,32 @@ void ODBCOracleTest::testInternalStorageType()
|
||||
}
|
||||
|
||||
|
||||
void ODBCOracleTest::testNull()
|
||||
{
|
||||
if (!_pSession) fail ("Test not available.");
|
||||
|
||||
// test for NOT NULL violation exception
|
||||
for (int i = 0; i < 8;)
|
||||
{
|
||||
recreateNullsTable("NOT NULL");
|
||||
_pSession->setFeature("autoBind", bindValues[i]);
|
||||
_pSession->setFeature("autoExtract", bindValues[i+1]);
|
||||
_pExecutor->notNulls("HY000");
|
||||
i += 2;
|
||||
}
|
||||
|
||||
// test for null insertion
|
||||
for (int i = 0; i < 8;)
|
||||
{
|
||||
recreateNullsTable();
|
||||
_pSession->setFeature("autoBind", bindValues[i]);
|
||||
_pSession->setFeature("autoExtract", bindValues[i+1]);
|
||||
_pExecutor->nulls();
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ODBCOracleTest::testStoredProcedure()
|
||||
{
|
||||
if (!_pSession) fail ("Test not available.");
|
||||
@ -1143,6 +1169,18 @@ void ODBCOracleTest::recreateVectorsTable()
|
||||
}
|
||||
|
||||
|
||||
void ODBCOracleTest::recreateNullsTable(const std::string& notNull)
|
||||
{
|
||||
dropObject("TABLE", "NullTest");
|
||||
try { *_pSession << format("CREATE TABLE NullTest (i INTEGER %s, r NUMBER %s, v VARCHAR(30) %s)",
|
||||
notNull,
|
||||
notNull,
|
||||
notNull), now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("recreateNullsTable()"); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("recreateNullsTable()"); }
|
||||
}
|
||||
|
||||
|
||||
bool ODBCOracleTest::canConnect(const std::string& driver, const std::string& dsn)
|
||||
{
|
||||
Utility::DriverMap::iterator itDrv = _drivers.begin();
|
||||
@ -1306,6 +1344,7 @@ CppUnit::Test* ODBCOracleTest::suite()
|
||||
CppUnit_addTest(pSuite, ODBCOracleTest, testStoredFunction);
|
||||
CppUnit_addTest(pSuite, ODBCOracleTest, testInternalExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCOracleTest, testInternalStorageType);
|
||||
CppUnit_addTest(pSuite, ODBCOracleTest, testNull);
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
@ -122,6 +122,8 @@ public:
|
||||
void testStoredProcedure();
|
||||
void testStoredFunction();
|
||||
|
||||
void testNull();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
||||
@ -137,6 +139,7 @@ private:
|
||||
void recreateFloatsTable();
|
||||
void recreateTuplesTable();
|
||||
void recreateVectorsTable();
|
||||
void recreateNullsTable(const std::string& notNull = "");
|
||||
|
||||
static bool init(const std::string& driver, const std::string& dsn);
|
||||
static bool canConnect(const std::string& driver, const std::string& dsn);
|
||||
|
@ -865,6 +865,32 @@ void ODBCPostgreSQLTest::testInternalStorageType()
|
||||
}
|
||||
|
||||
|
||||
void ODBCPostgreSQLTest::testNull()
|
||||
{
|
||||
if (!_pSession) fail ("Test not available.");
|
||||
|
||||
// test for NOT NULL violation exception
|
||||
for (int i = 0; i < 8;)
|
||||
{
|
||||
recreateNullsTable("NOT NULL");
|
||||
_pSession->setFeature("autoBind", bindValues[i]);
|
||||
_pSession->setFeature("autoExtract", bindValues[i+1]);
|
||||
_pExecutor->notNulls();
|
||||
i += 2;
|
||||
}
|
||||
|
||||
// test for null insertion
|
||||
for (int i = 0; i < 8;)
|
||||
{
|
||||
recreateNullsTable();
|
||||
_pSession->setFeature("autoBind", bindValues[i]);
|
||||
_pSession->setFeature("autoExtract", bindValues[i+1]);
|
||||
_pExecutor->nulls();
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ODBCPostgreSQLTest::testStoredFunction()
|
||||
{
|
||||
configurePLPgSQL();
|
||||
@ -1054,6 +1080,18 @@ void ODBCPostgreSQLTest::recreateVectorsTable()
|
||||
}
|
||||
|
||||
|
||||
void ODBCPostgreSQLTest::recreateNullsTable(const std::string& notNull)
|
||||
{
|
||||
dropObject("TABLE", "NullTest");
|
||||
try { *_pSession << format("CREATE TABLE NullTest (i INTEGER %s, r FLOAT %s, v VARCHAR(30) %s)",
|
||||
notNull,
|
||||
notNull,
|
||||
notNull), now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("recreateNullsTable()"); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("recreateNullsTable()"); }
|
||||
}
|
||||
|
||||
|
||||
bool ODBCPostgreSQLTest::canConnect(const std::string& driver, const std::string& dsn)
|
||||
{
|
||||
Utility::DriverMap::iterator itDrv = _drivers.begin();
|
||||
@ -1090,7 +1128,7 @@ bool ODBCPostgreSQLTest::canConnect(const std::string& driver, const std::string
|
||||
// DSN not found, try connect without it
|
||||
format(_dbConnString, "DRIVER=%s;"
|
||||
"DATABASE=postgres;"
|
||||
"SERVER=localhost;"
|
||||
"SERVER=a-fabijanic;"
|
||||
"PORT=5432;"
|
||||
"UID=postgres;"
|
||||
"PWD=postgres;"
|
||||
@ -1224,6 +1262,7 @@ CppUnit::Test* ODBCPostgreSQLTest::suite()
|
||||
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testInternalExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testInternalStorageType);
|
||||
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testStoredFunction);
|
||||
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testNull);
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
@ -123,6 +123,7 @@ public:
|
||||
void testInternalStorageType();
|
||||
|
||||
void testStoredFunction();
|
||||
void testNull();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
@ -139,6 +140,7 @@ private:
|
||||
void recreateFloatsTable();
|
||||
void recreateTuplesTable();
|
||||
void recreateVectorsTable();
|
||||
void recreateNullsTable(const std::string& notNull="");
|
||||
|
||||
static bool init(const std::string& driver, const std::string& dsn);
|
||||
static bool canConnect(const std::string& driver, const std::string& dsn);
|
||||
|
@ -864,6 +864,32 @@ void ODBCSQLServerTest::testInternalStorageType()
|
||||
}
|
||||
|
||||
|
||||
void ODBCSQLServerTest::testNull()
|
||||
{
|
||||
if (!_pSession) fail ("Test not available.");
|
||||
|
||||
// test for NOT NULL violation exception
|
||||
for (int i = 0; i < 8;)
|
||||
{
|
||||
recreateNullsTable("NOT NULL");
|
||||
_pSession->setFeature("autoBind", bindValues[i]);
|
||||
_pSession->setFeature("autoExtract", bindValues[i+1]);
|
||||
_pExecutor->notNulls("23000");
|
||||
i += 2;
|
||||
}
|
||||
|
||||
// test for null insertion
|
||||
for (int i = 0; i < 8;)
|
||||
{
|
||||
recreateNullsTable();
|
||||
_pSession->setFeature("autoBind", bindValues[i]);
|
||||
_pSession->setFeature("autoExtract", bindValues[i+1]);
|
||||
_pExecutor->nulls();
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ODBCSQLServerTest::testStoredProcedure()
|
||||
{
|
||||
for (int k = 0; k < 8;)
|
||||
@ -1137,6 +1163,18 @@ void ODBCSQLServerTest::recreateVectorsTable()
|
||||
}
|
||||
|
||||
|
||||
void ODBCSQLServerTest::recreateNullsTable(const std::string& notNull)
|
||||
{
|
||||
dropObject("TABLE", "NullTest");
|
||||
try { *_pSession << format("CREATE TABLE NullTest (i INTEGER %s, r FLOAT %s, v VARCHAR(30) %s)",
|
||||
notNull,
|
||||
notNull,
|
||||
notNull), now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("recreateNullsTable()"); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("recreateNullsTable()"); }
|
||||
}
|
||||
|
||||
|
||||
bool ODBCSQLServerTest::canConnect(const std::string& driver, const std::string& dsn)
|
||||
{
|
||||
Utility::DriverMap::iterator itDrv = _drivers.begin();
|
||||
@ -1282,6 +1320,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
|
||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredFunction);
|
||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalStorageType);
|
||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testNull);
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
@ -125,6 +125,8 @@ public:
|
||||
void testInternalExtraction();
|
||||
void testInternalStorageType();
|
||||
|
||||
void testNull();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
||||
@ -141,6 +143,7 @@ private:
|
||||
void recreateTuplesTable();
|
||||
void recreateVectorTable();
|
||||
void recreateVectorsTable();
|
||||
void recreateNullsTable(const std::string& notNull = "");
|
||||
|
||||
static bool init(const std::string& driver, const std::string& dsn);
|
||||
static bool canConnect(const std::string& driver, const std::string& dsn);
|
||||
|
@ -864,6 +864,32 @@ void ODBCSQLiteTest::testInternalStorageType()
|
||||
}
|
||||
|
||||
|
||||
void ODBCSQLiteTest::testNull()
|
||||
{
|
||||
if (!_pSession) fail ("Test not available.");
|
||||
|
||||
// test for NOT NULL violation exception
|
||||
for (int i = 0; i < 8;)
|
||||
{
|
||||
recreateNullsTable("NOT NULL");
|
||||
_pSession->setFeature("autoBind", bindValues[i]);
|
||||
_pSession->setFeature("autoExtract", bindValues[i+1]);
|
||||
_pExecutor->notNulls("HY000");
|
||||
i += 2;
|
||||
}
|
||||
|
||||
// test for null insertion
|
||||
for (int i = 0; i < 8;)
|
||||
{
|
||||
recreateNullsTable();
|
||||
_pSession->setFeature("autoBind", bindValues[i]);
|
||||
_pSession->setFeature("autoExtract", bindValues[i+1]);
|
||||
_pExecutor->nulls();
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ODBCSQLiteTest::dropObject(const std::string& type, const std::string& name)
|
||||
{
|
||||
try
|
||||
@ -964,6 +990,18 @@ void ODBCSQLiteTest::recreateVectorsTable()
|
||||
}
|
||||
|
||||
|
||||
void ODBCSQLiteTest::recreateNullsTable(const std::string& notNull)
|
||||
{
|
||||
dropObject("TABLE", "NullTest");
|
||||
try { *_pSession << format("CREATE TABLE NullTest (i INTEGER %s, r REAL %s, v VARCHAR(30) %s)",
|
||||
notNull,
|
||||
notNull,
|
||||
notNull), now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("recreateNullsTable()"); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("recreateNullsTable()"); }
|
||||
}
|
||||
|
||||
|
||||
bool ODBCSQLiteTest::canConnect(const std::string& driver, const std::string& dsn)
|
||||
{
|
||||
Utility::DriverMap::iterator itDrv = _drivers.begin();
|
||||
@ -1097,6 +1135,7 @@ CppUnit::Test* ODBCSQLiteTest::suite()
|
||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testTupleVector);
|
||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testInternalExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testInternalStorageType);
|
||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testNull);
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
@ -119,6 +119,8 @@ public:
|
||||
void testInternalExtraction();
|
||||
void testInternalStorageType();
|
||||
|
||||
void testNull();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
||||
@ -134,6 +136,7 @@ private:
|
||||
void recreateFloatsTable();
|
||||
void recreateTuplesTable();
|
||||
void recreateVectorsTable();
|
||||
void recreateNullsTable(const std::string& notNull = "");
|
||||
|
||||
static bool init(const std::string& driver, const std::string& dsn);
|
||||
static bool canConnect(const std::string& driver, const std::string& dsn);
|
||||
|
@ -2062,3 +2062,80 @@ void SQLExecutor::internalStorageType()
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
|
||||
}
|
||||
|
||||
|
||||
void SQLExecutor::notNulls(const std::string& sqlState)
|
||||
{
|
||||
try
|
||||
{
|
||||
*_pSession << "INSERT INTO NullTest (i,r,v) VALUES (?,?,?)", use(NULL_INT8), use(NULL_FLOAT), use(NULL_STRING), now;
|
||||
fail ("must fail");
|
||||
}catch (StatementException& se)
|
||||
{
|
||||
//make sure we're failing for the right reason
|
||||
//default sqlState value is "23502", but some drivers report "HY???" codes
|
||||
assert (sqlState == se.diagnostics().sqlState(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SQLExecutor::nulls()
|
||||
{
|
||||
std::string funct = "nulls()";
|
||||
|
||||
try { *_pSession << "INSERT INTO NullTest (i,r,v) VALUES (?,?,?)", use(NULL_INT32), use(NULL_FLOAT), use(NULL_STRING), now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
|
||||
|
||||
RecordSet rs(*_pSession, "SELECT * FROM NullTest");
|
||||
assert (1 == rs.rowCount());
|
||||
rs.moveFirst();
|
||||
assert (rs.isNull("i"));
|
||||
assert (rs["i"] == 0);
|
||||
assert (rs.isNull("r"));
|
||||
assert (rs.isNull("v"));
|
||||
assert (rs["v"] == "");
|
||||
try { *_pSession << "DELETE FROM NullTest", now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
|
||||
|
||||
int i = 1;
|
||||
double f = 1.2;
|
||||
std::string s = "123";
|
||||
|
||||
try { *_pSession << "INSERT INTO NullTest (i, r, v) VALUES (?,?,?)", use(i), use(f), use(s), now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
|
||||
rs = (*_pSession << "SELECT * FROM NullTest", now);
|
||||
assert (1 == rs.rowCount());
|
||||
rs.moveFirst();
|
||||
assert (!rs.isNull("i"));
|
||||
assert (rs["i"] == 1);
|
||||
assert (!rs.isNull("v"));
|
||||
assert (!rs.isNull("r"));
|
||||
assert (rs["v"] == "123");
|
||||
|
||||
try { *_pSession << "UPDATE NullTest SET v = ? WHERE i = ?", use(NULL_STRING), use(i), now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
|
||||
i = 2;
|
||||
f = 3.4;
|
||||
try { *_pSession << "INSERT INTO NullTest (i, r, v) VALUES (?,?,?)", use(i), use(NULL_FLOAT), use(NULL_STRING), now; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
|
||||
rs = (*_pSession << "SELECT i, r, v FROM NullTest ORDER BY i ASC", now);
|
||||
assert (2 == rs.rowCount());
|
||||
rs.moveFirst();
|
||||
assert (!rs.isNull("i"));
|
||||
assert (rs["i"] == 1);
|
||||
assert (!rs.isNull("r"));
|
||||
assert (rs.isNull("v"));
|
||||
assert (rs["v"] == "");
|
||||
|
||||
assert (rs.moveNext());
|
||||
assert (!rs.isNull("i"));
|
||||
assert (rs["i"] == 2);
|
||||
assert (rs.isNull("r"));
|
||||
assert (rs.isNull("v"));
|
||||
assert (rs["v"] == "");
|
||||
}
|
||||
|
@ -129,6 +129,8 @@ public:
|
||||
|
||||
void internalExtraction();
|
||||
void internalStorageType();
|
||||
void nulls();
|
||||
void notNulls(const std::string& sqlState = "23502");
|
||||
|
||||
private:
|
||||
Poco::Data::Session* _pSession;
|
||||
|
@ -63,10 +63,12 @@ enum NullData
|
||||
NULL_UINT32,
|
||||
NULL_INT64,
|
||||
NULL_UINT64,
|
||||
NULL_BOOL,
|
||||
NULL_FLOAT,
|
||||
NULL_DOUBLE,
|
||||
NULL_STRING,
|
||||
NULL_BLOB
|
||||
NULL_BLOB,
|
||||
NULL_TIMESTAMP
|
||||
};
|
||||
|
||||
|
||||
|
@ -95,7 +95,7 @@ public:
|
||||
/// Extracts a value from the param, starting at the given column position.
|
||||
|
||||
virtual void reset() = 0;
|
||||
/// Resets the etxractor so that it can be re-used.
|
||||
/// Resets the extractor so that it can be re-used.
|
||||
|
||||
virtual AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) const = 0;
|
||||
/// Creates a Prepare object for the etxracting object
|
||||
|
Loading…
x
Reference in New Issue
Block a user