proper bool support

This commit is contained in:
Aleksandar Fabijanic
2007-09-22 01:20:20 +00:00
parent 40c0f41fa0
commit 228d48ad14
24 changed files with 442 additions and 29 deletions

View File

@@ -280,7 +280,7 @@ inline void Binder::bind(std::size_t pos, const double& val, Direction dir)
inline void Binder::bind(std::size_t pos, const bool& val, Direction dir)
{
bindImpl(pos, val, Utility::boolDataType, dir);
bindImpl(pos, val, SQL_C_BIT, dir);
}

View File

@@ -295,7 +295,7 @@ inline void Preparation::prepare(std::size_t pos, Poco::UInt64)
inline void Preparation::prepare(std::size_t pos, bool)
{
preparePOD<bool>(pos, Utility::boolDataType);
preparePOD<bool>(pos, SQL_C_BIT);
}

View File

@@ -101,9 +101,6 @@ public:
static void dateTimeSync(SQL_TIMESTAMP_STRUCT& ts, const Poco::DateTime& dt);
/// Transfers data from Poco::DateTime to ODBC SQL_TIMESTAMP_STRUCT.
static const SQLSMALLINT boolDataType;
/// ODBC size for bool data type.
private:
static const TypeInfo _dataTypes;
/// C <==> SQL data type mapping

View File

@@ -224,7 +224,7 @@ void Binder::bind(std::size_t pos, const NullData& val, Direction dir)
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_BOOL: bindNull(pos, SQL_C_BIT); 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;

View File

@@ -358,7 +358,7 @@ bool Extractor::extract(std::size_t pos, Poco::UInt64& val)
bool Extractor::extract(std::size_t pos, bool& val)
{
if (Preparation::DE_MANUAL == _dataExtraction)
return extractManualImpl(pos, val, Utility::boolDataType);
return extractManualImpl(pos, val, SQL_C_BIT);
else
return extractBoundImpl(pos, val);
}

View File

@@ -101,6 +101,8 @@ void ODBCColumn::init()
setNullable(SQL_NULLABLE == _columnDesc.isNullable);
switch(_columnDesc.dataType)
{
case SQL_BIT:
setType(MetaColumn::FDT_BOOL); break;
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:

View File

@@ -125,7 +125,7 @@ void Preparation::prepare(std::size_t pos, const Poco::Any&)
return preparePOD<Poco::UInt64>(pos, SQL_C_UBIGINT);
case MetaColumn::FDT_BOOL:
return preparePOD<bool>(pos, Utility::boolDataType);
return preparePOD<bool>(pos, SQL_C_BIT);
case MetaColumn::FDT_FLOAT:
return preparePOD<float>(pos, SQL_C_FLOAT);

View File

@@ -48,8 +48,6 @@ namespace ODBC {
const TypeInfo Utility::_dataTypes;
const SQLSMALLINT Utility::boolDataType = (sizeof(bool) <= sizeof(char)) ? SQL_C_TINYINT :
(sizeof(bool) == sizeof(short)) ? SQL_C_SHORT : SQL_C_LONG;
Utility::DriverMap& Utility::drivers(Utility::DriverMap& driverMap)

View File

@@ -972,6 +972,28 @@ void ODBCPostgreSQLTest::testRowIterator()
}
void ODBCPostgreSQLTest::testStdVectorBool()
{
// psqlODBC driver returns string for bool fields
// even when field is explicitly cast to boolean,
// so this functionality seems to be untestable with it
#ifdef POCO_ODBC_USE_MAMMOTH_NG
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreateBoolTable();
_pSession->setFeature("autoBind", bindValues[i]);
_pSession->setFeature("autoExtract", bindValues[i+1]);
_pExecutor->stdVectorBool();
i += 2;
}
#endif // POCO_ODBC_USE_MAMMOTH_NG
}
void ODBCPostgreSQLTest::configurePLPgSQL()
{
if (!_pSession) fail ("Test not available.");
@@ -1110,6 +1132,15 @@ void ODBCPostgreSQLTest::recreateNullsTable(const std::string& notNull)
}
void ODBCPostgreSQLTest::recreateBoolTable()
{
dropObject("TABLE", "BoolTest");
try { *_pSession << "CREATE TABLE BoolTest (b BOOLEAN)", now; }
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("recreateBoolTable()"); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("recreateBoolTable()"); }
}
bool ODBCPostgreSQLTest::canConnect(const std::string& driver, const std::string& dsn)
{
Utility::DriverMap::iterator itDrv = _drivers.begin();
@@ -1285,6 +1316,7 @@ CppUnit::Test* ODBCPostgreSQLTest::suite()
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testStoredFunction);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testNull);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testRowIterator);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testStdVectorBool);
return pSuite;
}

View File

@@ -45,7 +45,7 @@
// uncomment to use Mammoth ODBCng driver
//#define POCO_ODBC_USE_MAMMOTH_NG
#define POCO_ODBC_USE_MAMMOTH_NG
class ODBCPostgreSQLTest: public CppUnit::TestCase
@@ -131,6 +131,7 @@ public:
void testStoredFunction();
void testNull();
void testRowIterator();
void testStdVectorBool();
void setUp();
void tearDown();
@@ -152,6 +153,7 @@ private:
void recreateTuplesTable();
void recreateVectorsTable();
void recreateNullsTable(const std::string& notNull="");
void recreateBoolTable();
static bool init(const std::string& driver, const std::string& dsn);
static bool canConnect(const std::string& driver, const std::string& dsn);

View File

@@ -1070,6 +1070,21 @@ void ODBCSQLServerTest::testRowIterator()
}
void ODBCSQLServerTest::testStdVectorBool()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreateBoolTable();
_pSession->setFeature("autoBind", bindValues[i]);
_pSession->setFeature("autoExtract", bindValues[i+1]);
_pExecutor->stdVectorBool();
i += 2;
}
}
void ODBCSQLServerTest::dropObject(const std::string& type, const std::string& name)
{
try
@@ -1190,6 +1205,14 @@ void ODBCSQLServerTest::recreateNullsTable(const std::string& notNull)
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("recreateNullsTable()"); }
}
void ODBCSQLServerTest::recreateBoolTable()
{
dropObject("TABLE", "BoolTest");
try { *_pSession << "CREATE TABLE BoolTest (b BIT)", now; }
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("recreateBoolTable()"); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("recreateBoolTable()"); }
}
bool ODBCSQLServerTest::canConnect(const std::string& driver, const std::string& dsn)
{
@@ -1338,6 +1361,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalStorageType);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testNull);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testRowIterator);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStdVectorBool);
return pSuite;
}

View File

@@ -127,6 +127,7 @@ public:
void testNull();
void testRowIterator();
void testStdVectorBool();
void setUp();
void tearDown();
@@ -149,6 +150,7 @@ private:
void recreateVectorTable();
void recreateVectorsTable();
void recreateNullsTable(const std::string& notNull = "");
void recreateBoolTable();
static bool init(const std::string& driver, const std::string& dsn);
static bool canConnect(const std::string& driver, const std::string& dsn);

View File

@@ -2152,7 +2152,7 @@ void SQLExecutor::nulls()
void SQLExecutor::rowIterator()
{
std::string funct = "internalExtraction()";
std::string funct = "rowIterator()";
std::vector<Tuple<int, double, std::string> > v;
v.push_back(Tuple<int, double, std::string>(1, 1.5f, "3"));
v.push_back(Tuple<int, double, std::string>(2, 2.5f, "4"));
@@ -2179,3 +2179,63 @@ void SQLExecutor::rowIterator()
std::copy(rset.begin(), rset.end(), std::ostream_iterator<Row>(osCopy));
assert (osLoop.str() == osCopy.str());
}
void SQLExecutor::stdVectorBool()
{
std::string funct = "stdVectorBool()";
bool b = false;
try { *_pSession << "INSERT INTO BoolTest VALUES (?)", use(b), now; }
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
b = true;
*_pSession << "SELECT * FROM BoolTest", into(b), now;
assert (false == b);
*_pSession << "DELETE FROM BoolTest", now;
b = true;
try { *_pSession << "INSERT INTO BoolTest VALUES (?)", use(b), now; }
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
b = false;
*_pSession << "SELECT * FROM BoolTest", into(b), now;
assert (true == b);
*_pSession << "DELETE FROM BoolTest", now;
std::vector<bool> v;
v.push_back(true);
v.push_back(false);
v.push_back(false);
v.push_back(true);
try { *_pSession << "INSERT INTO BoolTest VALUES (?)", use(v), now; }
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
v.clear();
*_pSession << "SELECT * FROM BoolTest", into(v), now;
assert (4 == v.size());
std::vector<bool>::iterator it = v.begin();
std::vector<bool>::iterator end = v.end();
int t = 0;
for (; it != end; ++it)
t += *it ? 1 : 0;
assert (2 == t);
try { *_pSession << "SELECT * FROM BoolTest WHERE b = ?", out(v), now; fail("must fail"); }
catch (BindingException&) { }
try { *_pSession << "SELECT * FROM BoolTest WHERE b = ?", io(v), now; fail("must fail"); }
catch (BindingException&) { }
RecordSet rset(*_pSession, "SELECT * FROM BoolTest");
t = 0;
for (int i = 0; i < 4; ++i)
t += rset.value<bool>(0, i) ? 1 : 0;
assert (2 == t);
}

View File

@@ -132,6 +132,7 @@ public:
void nulls();
void notNulls(const std::string& sqlState = "23502");
void rowIterator();
void stdVectorBool();
private:
Poco::Data::Session* _pSession;

View File

@@ -164,6 +164,79 @@ private:
};
template <>
class Binding<std::vector<bool> >: public AbstractBinding
/// Specialization for std::vector<bool>.
/// This specialization is necessary due to the nature of std::vector<bool>.
/// For details, see the standard library implementation of std::vector<bool>
/// or
/// S. Meyers: "Effective STL" (Copyright Addison-Wesley 2001),
/// Item 18: "Avoid using vector<bool>."
///
/// The workaround employed here is using std::deque<bool> as an
/// internal replacement container.
///
/// IMPORTANT:
/// Only IN binding is supported.
{
public:
explicit Binding(const std::vector<bool>& val, const std::string& name = "", Direction direction = PD_IN):
AbstractBinding(name, direction),
_val(val),
_deq(_val.begin(), _val.end()),
_begin(_deq.begin()),
_end(_deq.end())
/// Creates the Binding.
{
if (PD_IN != direction)
throw BindingException("Only IN direction is legal for std:vector<bool> binding.");
if (numOfRowsHandled() == 0)
throw BindingException("It is illegal to bind to an empty data collection");
}
~Binding()
/// Destroys the Binding.
{
}
std::size_t numOfColumnsHandled() const
{
return TypeHandler<bool>::size();
}
std::size_t numOfRowsHandled() const
{
return _val.size();
}
bool canBind() const
{
return _begin != _end;
}
void bind(std::size_t pos)
{
poco_assert_dbg(getBinder() != 0);
poco_assert_dbg(canBind());
TypeHandler<bool>::bind(pos, *_begin, getBinder(), getDirection());
++_begin;
}
void reset()
{
_begin = _deq.begin();
_end = _deq.end();
}
private:
const std::vector<bool>& _val;
std::deque<bool> _deq;
std::deque<bool>::const_iterator _begin;
std::deque<bool>::const_iterator _end;
};
template <class T>
class Binding<std::list<T> >: public AbstractBinding
/// Specialization for std::list.

View File

@@ -45,6 +45,7 @@
#include "Poco/SharedPtr.h"
#include <vector>
#include <list>
#include <deque>
namespace Poco {
@@ -184,6 +185,157 @@ private:
};
template <>
class Column<bool, std::vector<bool> >
/// The std::vector<bool> specialization for the Column class.
///
/// This specialization is necessary due to the nature of std::vector<bool>.
/// For details, see the standard library implementation of vector<bool>
/// or
/// S. Meyers: "Effective STL" (Copyright Addison-Wesley 2001),
/// Item 18: "Avoid using vector<bool>."
///
/// The workaround employed here is using deque<bool> as an
/// internal "companion" container kept in sync with the vector<bool>
/// column data.
{
public:
typedef std::vector<bool> Container;
typedef Container::const_iterator Iterator;
typedef Container::const_reverse_iterator RIterator;
typedef Container::size_type Size;
Column(const MetaColumn& metaColumn, Container* pData):
_metaColumn(metaColumn),
_pData(pData)
/// Creates the Column.
{
poco_check_ptr (_pData);
_deque.assign(_pData->begin(), _pData->end());
}
Column(const Column& col):
_metaColumn(col._metaColumn),
_pData(col._pData)
/// Creates the Column.
{
_deque.assign(_pData->begin(), _pData->end());
}
~Column()
/// Destroys the Column.
{
}
Column& operator = (const Column& col)
/// Assignment operator.
{
Column tmp(col);
swap(tmp);
return *this;
}
void swap(Column& other)
/// Swaps the column with another one.
{
std::swap(_metaColumn, other._metaColumn);
std::swap(_pData, other._pData);
std::swap(_deque, other._deque);
}
Container& data()
/// Returns reference to contained data.
{
return *_pData;
}
const bool& value(std::size_t row) const
/// Returns the field value in specified row.
{
if (_deque.size() < _pData->size())
_deque.resize(_pData->size());
try
{
return _deque.at(row) = _pData->at(row);
}
catch (std::out_of_range& ex)
{
throw RangeException(ex.what());
}
}
const bool& operator [] (std::size_t row) const
/// Returns the field value in specified row.
{
return value(row);
}
Size rowCount() const
/// Returns number of rows.
{
return _pData->size();
}
void reset()
/// Clears and shrinks the storage.
{
Container().swap(*_pData);
_deque.clear();
}
const std::string& name() const
/// Returns column name.
{
return _metaColumn.name();
}
std::size_t length() const
/// Returns column maximum length.
{
return _metaColumn.length();
}
std::size_t precision() const
/// Returns column precision.
/// Valid for floating point fields only (zero for other data types).
{
return _metaColumn.precision();
}
std::size_t position() const
/// Returns column position.
{
return _metaColumn.position();
}
MetaColumn::ColumnDataType type() const
/// Returns column type.
{
return _metaColumn.type();
}
Iterator begin() const
/// Returns iterator pointing to the beginning of data storage vector.
{
return _pData->begin();
}
Iterator end() const
/// Returns iterator pointing to the end of data storage vector.
{
return _pData->end();
}
private:
Column();
MetaColumn _metaColumn;
Poco::SharedPtr<Container> _pData;
mutable std::deque<bool> _deque;
};
template <class T>
class Column<T, std::list<T> >
/// Column specialization for std::list

View File

@@ -171,8 +171,13 @@ public:
void extract(std::size_t pos)
{
AbstractExtractor* pExt = getExtractor();
_rResult.push_back(_default);
TypeHandler<T>::extract(pos, _rResult.back(), _default, pExt);
// for vector specialization, a temporary must be used to
// allow for extraction of bool values
T tmp = _default;
TypeHandler<T>::extract(pos, tmp, _default, pExt);
_rResult.push_back(tmp);
_nulls.push_back(pExt->isNull(pos));
}

View File

@@ -118,7 +118,7 @@ public:
std::size_t s = rExtractions.size();
if (0 == s || pos >= s)
throw RangeException(format("Invalid column index: %z", pos));
ExtractionVecPtr pExtraction = dynamic_cast<ExtractionVecPtr>(rExtractions[pos].get());
if (pExtraction)
@@ -180,25 +180,25 @@ public:
/// Returns the data value at named column, row location.
template <typename C>
DynamicAny nvl(const std::string& name, const C& deflt)
const C& nvl(const std::string& name, const C& deflt) const
/// Returns the value in the named column of the current row
/// if the value is not NULL, or deflt otherwise.
{
if (isNull(name))
return DynamicAny(deflt);
return deflt;
else
return value(name);
return value<C>(name, _currentRow);
}
template <typename C>
DynamicAny nvl(std::size_t index, const C& deflt)
const C& nvl(std::size_t index, const C& deflt) const
/// Returns the value in the given column of the current row
/// if the value is not NULL, or deflt otherwise.
{
if (isNull(index))
return DynamicAny(deflt);
return deflt;
else
return value(index);
return value<C>(index, _currentRow);
}
const RowIterator& begin();
@@ -267,7 +267,7 @@ public:
/// Returns column precision for the column with specified name.
/// Valid for floating point fields only (zero for other data types).
bool isNull(const std::string& name);
bool isNull(const std::string& name) const;
/// Returns true if column value of the current row is null.
private:
@@ -394,7 +394,7 @@ inline std::size_t RecordSet::columnPrecision(const std::string& name)const
}
inline bool RecordSet::isNull(const std::string& name)
inline bool RecordSet::isNull(const std::string& name) const
{
return isNull(metaColumn(name).position(), _currentRow);
}

View File

@@ -168,7 +168,7 @@ protected:
const MetaColumn& metaColumn(const std::string& name) const;
/// Returns the type for the column with specified name.
bool isNull(std::size_t col, std::size_t row);
bool isNull(std::size_t col, std::size_t row) const;
/// Returns true if the current row value at column pos is null.
private:
@@ -272,7 +272,7 @@ inline Statement::Storage Statement::storage() const
}
inline bool Statement::isNull(std::size_t col, std::size_t row)
inline bool Statement::isNull(std::size_t col, std::size_t row) const
{
return _ptr->isNull(col, row);
}

View File

@@ -299,7 +299,7 @@ private:
addExtract(createExtract<T, std::deque<T> >(mc));
}
bool isNull(std::size_t col, std::size_t row);
bool isNull(std::size_t col, std::size_t row) const;
/// Returns true if the value in [col, row] is null.
StatementImpl(const StatementImpl& stmt);
@@ -406,7 +406,7 @@ inline bool StatementImpl::isStoredProcedure() const
}
inline bool StatementImpl::isNull(std::size_t col, std::size_t row)
inline bool StatementImpl::isNull(std::size_t col, std::size_t row) const
{
try
{

View File

@@ -80,7 +80,7 @@ DynamicAny RecordSet::value(std::size_t col, std::size_t row) const
{
switch (columnType(col))
{
case MetaColumn::FDT_BOOL:
case MetaColumn::FDT_BOOL: return value<bool>(col, row);
case MetaColumn::FDT_INT8: return value<Int8>(col, row);
case MetaColumn::FDT_UINT8: return value<UInt8>(col, row);
case MetaColumn::FDT_INT16: return value<Int16>(col, row);
@@ -104,7 +104,7 @@ DynamicAny RecordSet::value(const std::string& name, std::size_t row) const
{
switch (columnType(name))
{
case MetaColumn::FDT_BOOL:
case MetaColumn::FDT_BOOL: return value<bool>(name, row);
case MetaColumn::FDT_INT8: return value<Int8>(name, row);
case MetaColumn::FDT_UINT8: return value<UInt8>(name, row);
case MetaColumn::FDT_INT16: return value<Int16>(name, row);

View File

@@ -289,6 +289,7 @@ void StatementImpl::makeExtractors(Poco::UInt32 count)
switch (mc.type())
{
case MetaColumn::FDT_BOOL:
addInternalExtract<bool>(mc); break;
case MetaColumn::FDT_INT8:
addInternalExtract<Int8>(mc); break;
case MetaColumn::FDT_UINT8:

View File

@@ -423,6 +423,68 @@ void DataTest::testColumnVector()
}
void DataTest::testColumnVectorBool()
{
MetaColumn mc(0, "mc", MetaColumn::FDT_BOOL);
std::vector<bool>* pData = new std::vector<bool>;
pData->push_back(true);
pData->push_back(false);
pData->push_back(true);
pData->push_back(false);
pData->push_back(true);
Column<bool> c(mc, pData);
assert (c.rowCount() == 5);
assert (c[0] == true);
assert (c[1] == false);
assert (c[2] == true);
assert (c[3] == false);
assert (c[4] == true);
assert (c.type() == MetaColumn::FDT_BOOL);
try
{
bool b = c[100];
fail ("must fail");
}
catch (RangeException&) { }
Column<bool> c1 = c;
assert (c1.rowCount() == 5);
assert (c1[0] == true);
assert (c1[1] == false);
assert (c1[2] == true);
assert (c1[3] == false);
assert (c1[4] == true);
Column<bool> c2(c1);
assert (c2.rowCount() == 5);
assert (c2[0] == true);
assert (c2[1] == false);
assert (c2[2] == true);
assert (c2[3] == false);
assert (c2[4] == true);
std::vector<bool> vi;
vi.assign(c.begin(), c.end());
assert (vi.size() == 5);
assert (vi[0] == true);
assert (vi[1] == false);
assert (vi[2] == true);
assert (vi[3] == false);
assert (vi[4] == true);
c.reset();
assert (c.rowCount() == 0);
assert (c1.rowCount() == 0);
assert (c2.rowCount() == 0);
}
void DataTest::testColumnDeque()
{
typedef std::deque<int> ContainerType;
@@ -824,6 +886,7 @@ CppUnit::Test* DataTest::suite()
CppUnit_addTest(pSuite, DataTest, testBLOB);
CppUnit_addTest(pSuite, DataTest, testBLOBStreams);
CppUnit_addTest(pSuite, DataTest, testColumnVector);
CppUnit_addTest(pSuite, DataTest, testColumnVectorBool);
CppUnit_addTest(pSuite, DataTest, testColumnDeque);
CppUnit_addTest(pSuite, DataTest, testColumnList);
CppUnit_addTest(pSuite, DataTest, testRow);

View File

@@ -55,6 +55,7 @@ public:
void testBLOB();
void testBLOBStreams();
void testColumnVector();
void testColumnVectorBool();
void testColumnDeque();
void testColumnList();
void testRow();