From 228d48ad14b05773fc7b5537f26761ff6e6071bf Mon Sep 17 00:00:00 2001 From: Aleksandar Fabijanic Date: Sat, 22 Sep 2007 01:20:20 +0000 Subject: [PATCH] proper bool support --- Data/ODBC/include/Poco/Data/ODBC/Binder.h | 2 +- .../ODBC/include/Poco/Data/ODBC/Preparation.h | 2 +- Data/ODBC/include/Poco/Data/ODBC/Utility.h | 3 - Data/ODBC/src/Binder.cpp | 2 +- Data/ODBC/src/Extractor.cpp | 2 +- Data/ODBC/src/ODBCColumn.cpp | 2 + Data/ODBC/src/Preparation.cpp | 2 +- Data/ODBC/src/Utility.cpp | 2 - .../ODBC/testsuite/src/ODBCPostgreSQLTest.cpp | 32 ++++ Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h | 4 +- Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp | 24 +++ Data/ODBC/testsuite/src/ODBCSQLServerTest.h | 2 + Data/ODBC/testsuite/src/SQLExecutor.cpp | 62 ++++++- Data/ODBC/testsuite/src/SQLExecutor.h | 1 + Data/include/Poco/Data/Binding.h | 73 +++++++++ Data/include/Poco/Data/Column.h | 152 ++++++++++++++++++ Data/include/Poco/Data/Extraction.h | 9 +- Data/include/Poco/Data/RecordSet.h | 18 +-- Data/include/Poco/Data/Statement.h | 4 +- Data/include/Poco/Data/StatementImpl.h | 4 +- Data/src/RecordSet.cpp | 4 +- Data/src/StatementImpl.cpp | 1 + Data/testsuite/src/DataTest.cpp | 63 ++++++++ Data/testsuite/src/DataTest.h | 1 + 24 files changed, 442 insertions(+), 29 deletions(-) diff --git a/Data/ODBC/include/Poco/Data/ODBC/Binder.h b/Data/ODBC/include/Poco/Data/ODBC/Binder.h index 51841cefc..d678cae1f 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Binder.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Binder.h @@ -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); } diff --git a/Data/ODBC/include/Poco/Data/ODBC/Preparation.h b/Data/ODBC/include/Poco/Data/ODBC/Preparation.h index eeb7148d8..17e4376cd 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Preparation.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Preparation.h @@ -295,7 +295,7 @@ inline void Preparation::prepare(std::size_t pos, Poco::UInt64) inline void Preparation::prepare(std::size_t pos, bool) { - preparePOD(pos, Utility::boolDataType); + preparePOD(pos, SQL_C_BIT); } diff --git a/Data/ODBC/include/Poco/Data/ODBC/Utility.h b/Data/ODBC/include/Poco/Data/ODBC/Utility.h index feff35793..2f8e16b6a 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Utility.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Utility.h @@ -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 diff --git a/Data/ODBC/src/Binder.cpp b/Data/ODBC/src/Binder.cpp index ba31d4b51..e1ba39026 100644 --- a/Data/ODBC/src/Binder.cpp +++ b/Data/ODBC/src/Binder.cpp @@ -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; diff --git a/Data/ODBC/src/Extractor.cpp b/Data/ODBC/src/Extractor.cpp index c01fab7b6..d8f02507f 100644 --- a/Data/ODBC/src/Extractor.cpp +++ b/Data/ODBC/src/Extractor.cpp @@ -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); } diff --git a/Data/ODBC/src/ODBCColumn.cpp b/Data/ODBC/src/ODBCColumn.cpp index de0749bcb..7eee80e80 100644 --- a/Data/ODBC/src/ODBCColumn.cpp +++ b/Data/ODBC/src/ODBCColumn.cpp @@ -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: diff --git a/Data/ODBC/src/Preparation.cpp b/Data/ODBC/src/Preparation.cpp index a02ed7dc4..716f92d29 100644 --- a/Data/ODBC/src/Preparation.cpp +++ b/Data/ODBC/src/Preparation.cpp @@ -125,7 +125,7 @@ void Preparation::prepare(std::size_t pos, const Poco::Any&) return preparePOD(pos, SQL_C_UBIGINT); case MetaColumn::FDT_BOOL: - return preparePOD(pos, Utility::boolDataType); + return preparePOD(pos, SQL_C_BIT); case MetaColumn::FDT_FLOAT: return preparePOD(pos, SQL_C_FLOAT); diff --git a/Data/ODBC/src/Utility.cpp b/Data/ODBC/src/Utility.cpp index ad149c2d3..975434c60 100644 --- a/Data/ODBC/src/Utility.cpp +++ b/Data/ODBC/src/Utility.cpp @@ -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) diff --git a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp index 8cbdf686c..51a263867 100644 --- a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp @@ -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; } diff --git a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h index 15f943307..15a58bff8 100644 --- a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h +++ b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h @@ -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); diff --git a/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp b/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp index 1c9a359a2..2aad2fde6 100644 --- a/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp @@ -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; } diff --git a/Data/ODBC/testsuite/src/ODBCSQLServerTest.h b/Data/ODBC/testsuite/src/ODBCSQLServerTest.h index 295327749..a7546c5a9 100644 --- a/Data/ODBC/testsuite/src/ODBCSQLServerTest.h +++ b/Data/ODBC/testsuite/src/ODBCSQLServerTest.h @@ -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); diff --git a/Data/ODBC/testsuite/src/SQLExecutor.cpp b/Data/ODBC/testsuite/src/SQLExecutor.cpp index d2b496cda..b2aeb00ba 100644 --- a/Data/ODBC/testsuite/src/SQLExecutor.cpp +++ b/Data/ODBC/testsuite/src/SQLExecutor.cpp @@ -2152,7 +2152,7 @@ void SQLExecutor::nulls() void SQLExecutor::rowIterator() { - std::string funct = "internalExtraction()"; + std::string funct = "rowIterator()"; std::vector > v; v.push_back(Tuple(1, 1.5f, "3")); v.push_back(Tuple(2, 2.5f, "4")); @@ -2179,3 +2179,63 @@ void SQLExecutor::rowIterator() std::copy(rset.begin(), rset.end(), std::ostream_iterator(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 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::iterator it = v.begin(); + std::vector::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(0, i) ? 1 : 0; + assert (2 == t); +} diff --git a/Data/ODBC/testsuite/src/SQLExecutor.h b/Data/ODBC/testsuite/src/SQLExecutor.h index 55e70479a..cf5de6f96 100644 --- a/Data/ODBC/testsuite/src/SQLExecutor.h +++ b/Data/ODBC/testsuite/src/SQLExecutor.h @@ -132,6 +132,7 @@ public: void nulls(); void notNulls(const std::string& sqlState = "23502"); void rowIterator(); + void stdVectorBool(); private: Poco::Data::Session* _pSession; diff --git a/Data/include/Poco/Data/Binding.h b/Data/include/Poco/Data/Binding.h index 329a54c63..02e38ae21 100644 --- a/Data/include/Poco/Data/Binding.h +++ b/Data/include/Poco/Data/Binding.h @@ -164,6 +164,79 @@ private: }; +template <> +class Binding >: public AbstractBinding + /// Specialization for std::vector. + /// This specialization is necessary due to the nature of std::vector. + /// For details, see the standard library implementation of std::vector + /// or + /// S. Meyers: "Effective STL" (Copyright Addison-Wesley 2001), + /// Item 18: "Avoid using vector." + /// + /// The workaround employed here is using std::deque as an + /// internal replacement container. + /// + /// IMPORTANT: + /// Only IN binding is supported. +{ +public: + explicit Binding(const std::vector& 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 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::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::bind(pos, *_begin, getBinder(), getDirection()); + ++_begin; + } + + void reset() + { + _begin = _deq.begin(); + _end = _deq.end(); + } + +private: + const std::vector& _val; + std::deque _deq; + std::deque::const_iterator _begin; + std::deque::const_iterator _end; +}; + + template class Binding >: public AbstractBinding /// Specialization for std::list. diff --git a/Data/include/Poco/Data/Column.h b/Data/include/Poco/Data/Column.h index f4ab38f45..fba4964b7 100644 --- a/Data/include/Poco/Data/Column.h +++ b/Data/include/Poco/Data/Column.h @@ -45,6 +45,7 @@ #include "Poco/SharedPtr.h" #include #include +#include namespace Poco { @@ -184,6 +185,157 @@ private: }; +template <> +class Column > + /// The std::vector specialization for the Column class. + /// + /// This specialization is necessary due to the nature of std::vector. + /// For details, see the standard library implementation of vector + /// or + /// S. Meyers: "Effective STL" (Copyright Addison-Wesley 2001), + /// Item 18: "Avoid using vector." + /// + /// The workaround employed here is using deque as an + /// internal "companion" container kept in sync with the vector + /// column data. +{ +public: + typedef std::vector 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 _pData; + mutable std::deque _deque; +}; + + template class Column > /// Column specialization for std::list diff --git a/Data/include/Poco/Data/Extraction.h b/Data/include/Poco/Data/Extraction.h index 9a37324c7..982b3bede 100644 --- a/Data/include/Poco/Data/Extraction.h +++ b/Data/include/Poco/Data/Extraction.h @@ -171,8 +171,13 @@ public: void extract(std::size_t pos) { AbstractExtractor* pExt = getExtractor(); - _rResult.push_back(_default); - TypeHandler::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::extract(pos, tmp, _default, pExt); + _rResult.push_back(tmp); + _nulls.push_back(pExt->isNull(pos)); } diff --git a/Data/include/Poco/Data/RecordSet.h b/Data/include/Poco/Data/RecordSet.h index bc19fbc72..1763a7f9a 100644 --- a/Data/include/Poco/Data/RecordSet.h +++ b/Data/include/Poco/Data/RecordSet.h @@ -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(rExtractions[pos].get()); if (pExtraction) @@ -180,25 +180,25 @@ public: /// Returns the data value at named column, row location. template - 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(name, _currentRow); } template - 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(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); } diff --git a/Data/include/Poco/Data/Statement.h b/Data/include/Poco/Data/Statement.h index 0db062b7f..349b7da05 100644 --- a/Data/include/Poco/Data/Statement.h +++ b/Data/include/Poco/Data/Statement.h @@ -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); } diff --git a/Data/include/Poco/Data/StatementImpl.h b/Data/include/Poco/Data/StatementImpl.h index 5a6985caf..734f8cd48 100644 --- a/Data/include/Poco/Data/StatementImpl.h +++ b/Data/include/Poco/Data/StatementImpl.h @@ -299,7 +299,7 @@ private: addExtract(createExtract >(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 { diff --git a/Data/src/RecordSet.cpp b/Data/src/RecordSet.cpp index 2783090b3..20583a08e 100644 --- a/Data/src/RecordSet.cpp +++ b/Data/src/RecordSet.cpp @@ -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(col, row); case MetaColumn::FDT_INT8: return value(col, row); case MetaColumn::FDT_UINT8: return value(col, row); case MetaColumn::FDT_INT16: return value(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(name, row); case MetaColumn::FDT_INT8: return value(name, row); case MetaColumn::FDT_UINT8: return value(name, row); case MetaColumn::FDT_INT16: return value(name, row); diff --git a/Data/src/StatementImpl.cpp b/Data/src/StatementImpl.cpp index 96d2ab58c..1306ceb27 100644 --- a/Data/src/StatementImpl.cpp +++ b/Data/src/StatementImpl.cpp @@ -289,6 +289,7 @@ void StatementImpl::makeExtractors(Poco::UInt32 count) switch (mc.type()) { case MetaColumn::FDT_BOOL: + addInternalExtract(mc); break; case MetaColumn::FDT_INT8: addInternalExtract(mc); break; case MetaColumn::FDT_UINT8: diff --git a/Data/testsuite/src/DataTest.cpp b/Data/testsuite/src/DataTest.cpp index 7375446cd..75fd994c3 100644 --- a/Data/testsuite/src/DataTest.cpp +++ b/Data/testsuite/src/DataTest.cpp @@ -423,6 +423,68 @@ void DataTest::testColumnVector() } +void DataTest::testColumnVectorBool() +{ + MetaColumn mc(0, "mc", MetaColumn::FDT_BOOL); + + std::vector* pData = new std::vector; + pData->push_back(true); + pData->push_back(false); + pData->push_back(true); + pData->push_back(false); + pData->push_back(true); + + Column 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 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 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 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 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); diff --git a/Data/testsuite/src/DataTest.h b/Data/testsuite/src/DataTest.h index 5b014211d..9057b67c6 100644 --- a/Data/testsuite/src/DataTest.h +++ b/Data/testsuite/src/DataTest.h @@ -55,6 +55,7 @@ public: void testBLOB(); void testBLOBStreams(); void testColumnVector(); + void testColumnVectorBool(); void testColumnDeque(); void testColumnList(); void testRow();