Row and RowIterator done and tested (windows and linux)

This commit is contained in:
Aleksandar Fabijanic 2007-06-23 01:22:55 +00:00
parent 08f8448478
commit b2977d3df2
26 changed files with 745 additions and 155 deletions

View File

@ -211,6 +211,12 @@
<File
RelativePath=".\include\Poco\Data\RecordSet.h">
</File>
<File
RelativePath=".\include\Poco\Data\Row.h">
</File>
<File
RelativePath=".\include\Poco\Data\RowIterator.h">
</File>
<File
RelativePath=".\include\Poco\Data\Session.h">
</File>
@ -278,6 +284,12 @@
<File
RelativePath=".\src\RecordSet.cpp">
</File>
<File
RelativePath=".\src\Row.cpp">
</File>
<File
RelativePath=".\src\RowIterator.cpp">
</File>
<File
RelativePath=".\src\Session.cpp">
</File>

View File

@ -12,7 +12,7 @@ objects = AbstractBinder AbstractBinding AbstractExtraction \
AbstractExtractor AbstractPreparation AbstractPrepare \
BLOB BLOBStream DataException Limit MetaColumn \
PooledSessionHolder PooledSessionImpl \
Range RecordSet Session SessionFactory SessionImpl \
Range RecordSet Row RowIterator Session SessionFactory SessionImpl \
Connector SessionPool Statement StatementCreator StatementImpl
target = PocoData

View File

@ -1051,6 +1051,21 @@ void ODBCDB2Test::testStoredFunction()
}
void ODBCDB2Test::testRowIterator()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreateVectorsTable();
_pSession->setFeature("autoBind", bindValues[i]);
_pSession->setFeature("autoExtract", bindValues[i+1]);
_pExecutor->rowIterator();
i += 2;
}
}
void ODBCDB2Test::dropObject(const std::string& type, const std::string& name)
{
try
@ -1305,6 +1320,7 @@ CppUnit::Test* ODBCDB2Test::suite()
CppUnit_addTest(pSuite, ODBCDB2Test, testStoredProcedure);
CppUnit_addTest(pSuite, ODBCDB2Test, testStoredFunction);
CppUnit_addTest(pSuite, ODBCDB2Test, testNull);
CppUnit_addTest(pSuite, ODBCDB2Test, testRowIterator);
return pSuite;
}

View File

@ -123,6 +123,7 @@ public:
void testStoredFunction();
void testNull();
void testRowIterator();
void setUp();
void tearDown();

View File

@ -915,6 +915,21 @@ void ODBCMySQLTest::testStoredFunction()
}
void ODBCMySQLTest::testRowIterator()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreateVectorsTable();
_pSession->setFeature("autoBind", bindValues[i]);
_pSession->setFeature("autoExtract", bindValues[i+1]);
_pExecutor->rowIterator();
i += 2;
}
}
void ODBCMySQLTest::dropObject(const std::string& type, const std::string& name)
{
*_pSession << format("DROP %s IF EXISTS %s", type, name), now;
@ -1152,7 +1167,7 @@ CppUnit::Test* ODBCMySQLTest::suite()
CppUnit_addTest(pSuite, ODBCMySQLTest, testInternalExtraction);
CppUnit_addTest(pSuite, ODBCMySQLTest, testInternalStorageType);
CppUnit_addTest(pSuite, ODBCMySQLTest, testNull);
CppUnit_addTest(pSuite, ODBCMySQLTest, testRowIterator);
return pSuite;
}

View File

@ -125,6 +125,7 @@ public:
void testStoredFunction();
void testNull();
void testRowIterator();
void setUp();
void tearDown();

View File

@ -1068,6 +1068,21 @@ void ODBCOracleTest::testStoredFunction()
}
void ODBCOracleTest::testRowIterator()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreateVectorsTable();
_pSession->setFeature("autoBind", bindValues[i]);
_pSession->setFeature("autoExtract", bindValues[i+1]);
_pExecutor->rowIterator();
i += 2;
}
}
void ODBCOracleTest::dropObject(const std::string& type, const std::string& name)
{
try
@ -1345,6 +1360,7 @@ CppUnit::Test* ODBCOracleTest::suite()
CppUnit_addTest(pSuite, ODBCOracleTest, testInternalExtraction);
CppUnit_addTest(pSuite, ODBCOracleTest, testInternalStorageType);
CppUnit_addTest(pSuite, ODBCOracleTest, testNull);
CppUnit_addTest(pSuite, ODBCOracleTest, testRowIterator);
return pSuite;
}

View File

@ -123,6 +123,7 @@ public:
void testStoredFunction();
void testNull();
void testRowIterator();
void setUp();
void tearDown();

View File

@ -954,6 +954,21 @@ void ODBCPostgreSQLTest::testStoredFunction()
}
void ODBCPostgreSQLTest::testRowIterator()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreateVectorsTable();
_pSession->setFeature("autoBind", bindValues[i]);
_pSession->setFeature("autoExtract", bindValues[i+1]);
_pExecutor->rowIterator();
i += 2;
}
}
void ODBCPostgreSQLTest::configurePLPgSQL()
{
if (!_pSession) fail ("Test not available.");
@ -1263,6 +1278,7 @@ CppUnit::Test* ODBCPostgreSQLTest::suite()
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testInternalStorageType);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testStoredFunction);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testNull);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testRowIterator);
return pSuite;
}

View File

@ -124,6 +124,7 @@ public:
void testStoredFunction();
void testNull();
void testRowIterator();
void setUp();
void tearDown();

View File

@ -1054,6 +1054,21 @@ void ODBCSQLServerTest::testStoredFunction()
}
void ODBCSQLServerTest::testRowIterator()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreateVectorsTable();
_pSession->setFeature("autoBind", bindValues[i]);
_pSession->setFeature("autoExtract", bindValues[i+1]);
_pExecutor->rowIterator();
i += 2;
}
}
void ODBCSQLServerTest::dropObject(const std::string& type, const std::string& name)
{
try
@ -1321,6 +1336,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalExtraction);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalStorageType);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testNull);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testRowIterator);
return pSuite;
}

View File

@ -126,6 +126,7 @@ public:
void testInternalStorageType();
void testNull();
void testRowIterator();
void setUp();
void tearDown();

View File

@ -890,6 +890,21 @@ void ODBCSQLiteTest::testNull()
}
void ODBCSQLiteTest::testRowIterator()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreateVectorsTable();
_pSession->setFeature("autoBind", bindValues[i]);
_pSession->setFeature("autoExtract", bindValues[i+1]);
_pExecutor->rowIterator();
i += 2;
}
}
void ODBCSQLiteTest::dropObject(const std::string& type, const std::string& name)
{
try
@ -1136,6 +1151,7 @@ CppUnit::Test* ODBCSQLiteTest::suite()
CppUnit_addTest(pSuite, ODBCSQLiteTest, testInternalExtraction);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testInternalStorageType);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testNull);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testRowIterator);
return pSuite;
}

View File

@ -120,6 +120,7 @@ public:
void testInternalStorageType();
void testNull();
void testRowIterator();
void setUp();
void tearDown();

View File

@ -42,6 +42,7 @@
#include "Poco/Data/BLOB.h"
#include "Poco/Data/StatementImpl.h"
#include "Poco/Data/RecordSet.h"
#include "Poco/Data/RowIterator.h"
#include "Poco/Data/ODBC/Connector.h"
#include "Poco/Data/ODBC/Utility.h"
#include "Poco/Data/ODBC/Diagnostics.h"
@ -50,6 +51,8 @@
#include "Poco/Data/ODBC/ODBCStatementImpl.h"
#include <sqltypes.h>
#include <iostream>
#include <sstream>
#include <iterator>
using namespace Poco::Data;
@ -2139,3 +2142,34 @@ void SQLExecutor::nulls()
assert (rs.isNull("v"));
assert (rs["v"] == "");
}
void SQLExecutor::rowIterator()
{
std::string funct = "internalExtraction()";
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"));
v.push_back(Tuple<int, double, std::string>(3, 3.5f, "5"));
v.push_back(Tuple<int, double, std::string>(4, 4.5f, "6"));
try { *_pSession << "INSERT INTO Vectors 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); }
RecordSet rset(*_pSession, "SELECT * FROM Vectors");
std::ostringstream osLoop;
RecordSet::Iterator it = rset.begin();
RecordSet::Iterator end = rset.end();
for (int i = 1; it != end; ++it, ++i)
{
assert (it->get(0) == i);
osLoop << *it;
}
assert (!osLoop.str().empty());
std::ostringstream osCopy;
std::copy(rset.begin(), rset.end(), std::ostream_iterator<Row>(osCopy));
assert (osLoop.str() == osCopy.str());
}

View File

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

View File

@ -1669,6 +1669,38 @@ void SQLiteTest::testNull()
}
void SQLiteTest::testRowIterator()
{
Session ses (SessionFactory::instance().create(SQLite::Connector::KEY, "dummy.db"));
ses << "DROP TABLE IF EXISTS Vectors", now;
ses << "CREATE TABLE Vectors (int0 INTEGER, flt0 REAL, str0 VARCHAR)", now;
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"));
v.push_back(Tuple<int, double, std::string>(3, 3.5f, "5"));
v.push_back(Tuple<int, double, std::string>(4, 4.5f, "6"));
ses << "INSERT INTO Vectors VALUES (?,?,?)", use(v), now;
RecordSet rset(ses, "SELECT * FROM Vectors");
std::ostringstream osLoop;
RecordSet::Iterator it = rset.begin();
RecordSet::Iterator end = rset.end();
for (int i = 1; it != end; ++it, ++i)
{
assert (it->get(0) == i);
osLoop << *it;
}
assert (!osLoop.str().empty());
std::ostringstream osCopy;
std::copy(rset.begin(), rset.end(), std::ostream_iterator<Row>(osCopy));
assert (osLoop.str() == osCopy.str());
}
void SQLiteTest::setUp()
{
}
@ -1742,6 +1774,7 @@ CppUnit::Test* SQLiteTest::suite()
CppUnit_addTest(pSuite, SQLiteTest, testInternalExtraction);
CppUnit_addTest(pSuite, SQLiteTest, testPrimaryKeyConstraint);
CppUnit_addTest(pSuite, SQLiteTest, testNull);
CppUnit_addTest(pSuite, SQLiteTest, testRowIterator);
return pSuite;
}

View File

@ -111,6 +111,7 @@ public:
void testInternalExtraction();
void testPrimaryKeyConstraint();
void testNull();
void testRowIterator();
void setUp();
void tearDown();

View File

@ -78,6 +78,7 @@ class Data_API RecordSet: private Statement
{
public:
typedef std::map<std::size_t, Row*> RowMap;
typedef RowIterator Iterator;
using Statement::isNull;
@ -132,8 +133,8 @@ public:
}
}
const Row& row(std::size_t pos) const;
/// Returns row at position pos.
Row& row(std::size_t pos);
/// Returns reference to row at position pos.
/// Rows are lazy-created and cached.
template <class T>
@ -278,7 +279,7 @@ private:
std::size_t _currentRow;
RowIterator* _pBegin;
RowIterator* _pEnd;
mutable RowMap _rowMap;
RowMap _rowMap;
};
@ -380,7 +381,7 @@ inline bool RecordSet::isNull(const std::string& name)
inline const RowIterator& RecordSet::end()
{
if (!_pEnd)
_pEnd = new RowIterator(*this);
_pEnd = new RowIterator(*this, true);
return *_pEnd;
}

View File

@ -1,7 +1,7 @@
//
// Row.h
//
// $Id: //poco/Main/Data/include/Poco/Data/Row.h#7 $
// $Id: //poco/Main/Data/include/Poco/Data/Row.h#1 $
//
// Library: Data
// Package: DataCore
@ -42,9 +42,11 @@
#include "Poco/Data/Data.h"
#include "Poco/DynamicAny.h"
#include "Poco/Tuple.h"
#include "Poco/SharedPtr.h"
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
namespace Poco {
@ -55,10 +57,31 @@ class RecordSet;
class Data_API Row
/// Row class.
/// Row class provides a data type for RecordSet iteration purposes.
/// Dereferencing a RowIterator returns Row.
/// Rows are sortable. The sortability is maintained at all times (i.e. there
/// is always at least one column specified as a sorting criteria) .
/// The default and minimal sorting criteria is the first field (position 0).
/// The default sorting criteria can be replaced with any other field by
/// calling replaceSortField() member function.
/// Additional fields can be added to sorting criteria, in which case the
/// field precedence corresponds to addition order (i.e. later added fields
/// have lower sorting precedence).
/// These features make Row suitable for use with standard sorted
/// containers and algorithms. The main constraint is that all the rows from
/// a set that is being sorted must have the same sorting criteria (i.e., the same
/// set of fields must be in sorting criteria in the same order). Since rows don't
/// know about each other, it is the programmer's responsibility to ensure this
/// constraint is satisfied.
/// Field names are a shared pointer to a vector of strings. For efficiency sake,
/// a constructor taking a shared pointer to names vector argument is provided.
/// The stream operator is provided for Row data type as a free-standing function.
{
public:
enum Comparison
typedef std::vector<std::string> NameVec;
typedef SharedPtr<std::vector<std::string> > NameVecPtr;
enum ComparisonType
{
COMPARE_AS_INTEGER,
COMPARE_AS_FLOAT,
@ -70,9 +93,15 @@ public:
Row();
/// Creates the Row.
explicit Row(NameVecPtr pNames);
/// Creates the Row.
~Row();
/// Destroys the Row.
DynamicAny& get(std::size_t col);
/// Returns the reference to data value at column location.
DynamicAny& operator [] (std::size_t col);
/// Returns the reference to data value at column location.
@ -83,31 +112,78 @@ public:
void append(const std::string& name, const T& val)
/// Appends the value to the row.
{
if (!_pNames) _pNames = new NameVec;
DynamicAny da = val;
_values.push_back(da);
_names.push_back(name);
_pNames->push_back(name);
if (1 == _values.size()) addSortField(0);
}
template <typename T>
void set(std::size_t pos, const T& val)
/// Assigns the value to the row.
{
try
{
_values.at(pos) = val;
}catch (std::out_of_range&)
{
throw RangeException("Invalid column number.");
}
}
template <typename T>
void set(const std::string& name, const T& val)
/// Assigns the value to the row.
{
NameVec::iterator it = _pNames->begin();
NameVec::iterator end = _pNames->end();
for (int i = 0; it != end; ++it, ++i)
{
if (*it == name)
return set(i, val);
}
std::ostringstream os;
os << "Column with name " << name << " not found.";
throw NotFoundException(os.str());
}
std::size_t fieldCount() const;
/// Returns the number of fields in this row.
void reset();
/// Resets the row.
/// Resets the row by clearing all field names and values.
void separator(const std::string& sep);
/// Sets the separator.
void sortField(std::size_t pos);
/// Sets the field used for sorting.
void addSortField(std::size_t pos);
/// Adds the field used for sorting.
void sortField(const std::string& name);
/// Sets the field used for sorting.
void addSortField(const std::string& name);
/// Adds the field used for sorting.
const std::string& toStringN() const;
void removeSortField(std::size_t pos);
/// Removes the field used for sorting.
void removeSortField(const std::string& name);
/// Removes the field used for sorting.
void replaceSortField(std::size_t oldPos, std::size_t newPos);
/// Replaces the field used for sorting.
void replaceSortField(const std::string& oldName, const std::string& newName);
/// Replaces the field used for sorting.
void resetSort();
/// Resets the sorting criteria to field 0 only.
const std::string namesToString() const;
/// Converts the row names to string, inserting separator
/// string between fields and end-of-line at the end.
const std::string& toStringV() const;
const std::string valuesToString() const;
/// Converts the row values to string, inserting separator
/// string between fields and end-of-line at the end.
@ -120,21 +196,26 @@ public:
bool operator < (const Row& other) const;
/// Less-then operator.
void comparison(Comparison comp);
/// Sets the type of comparison.
NameVecPtr names();
/// Returns the shared pointer to names vector.
private:
typedef std::vector<DynamicAny> ValueVec;
typedef Tuple<std::size_t, ComparisonType> SortTuple;
typedef std::vector<SortTuple> SortMap;
/// The type for map holding fields used for sorting criteria.
/// Fields are added sequentially and have precedence that
/// corresponds to adding order rather than field's position in the row.
/// That requirement rules out use of std::map due to its sorted nature.
std::size_t getPosition(const std::string& name);
bool isEqualSize(const Row& other) const;
bool isEqualType(const Row& other) const;
std::vector<std::string> _names;
std::vector<DynamicAny> _values;
mutable std::string _strValues;
mutable std::string _strNames;
std::string _separator;
std::size_t _sortField;
Comparison _comparison;
std::string _separator;
NameVecPtr _pNames;
ValueVec _values;
SortMap _sortFields;
};
@ -152,7 +233,7 @@ inline std::size_t Row::fieldCount() const
inline void Row::reset()
{
_names.clear();
_pNames->clear();
_values.clear();
}
@ -163,6 +244,24 @@ inline void Row::separator(const std::string& sep)
}
inline Row::NameVecPtr Row::names()
{
return _pNames;
}
inline DynamicAny& Row::operator [] (std::size_t col)
{
return get(col);
}
inline DynamicAny& Row::operator [] (const std::string& name)
{
return get(getPosition(name));
}
} } // namespace Poco::Data

View File

@ -1,7 +1,7 @@
//
// RowIterator.h
//
// $Id: //poco/Main/Data/include/Poco/Data/RowIterator.h#7 $
// $Id: //poco/Main/Data/include/Poco/Data/RowIterator.h#1 $
//
// Library: Data
// Package: DataCore
@ -43,6 +43,7 @@
#include "Poco/Data/Data.h"
#include "Poco/Data/Row.h"
#include "Poco/DynamicAny.h"
#include <iterator>
namespace Poco {
@ -53,10 +54,16 @@ class RecordSet;
class Data_API RowIterator
/// RowIterator class is an interface to a row of RecordSet data.
/// RowIterator class.
{
public:
RowIterator(const RecordSet& recordSet, bool isEmpty = true);
typedef std::bidirectional_iterator_tag iterator_category;
typedef Row value_type;
typedef std::ptrdiff_t difference_type;
typedef Row* pointer;
typedef Row& reference;
RowIterator(RecordSet& recordSet, bool positionEnd = false);
/// Creates the RowIterator and positions it at the beginning.
~RowIterator();
@ -68,24 +75,23 @@ public:
bool operator != (const RowIterator& other);
/// Inequality operator.
const Row& operator * () const;
/// Returns const reference to the current row.
Row& operator * () const;
/// Returns reference to the current row.
const Row& operator ++ ();
/// Advances by one position and returns const reference
/// to the current row.
Row* operator -> () const;
/// Returns pointer to the current row.
const Row& operator ++ (int);
/// Advances by one position and returns const reference
/// to the previous current row.
std::size_t operator ++ ();
/// Advances by one position and returns current position.
const Row& operator -- ();
/// Goes back by one position and returns const reference
/// to the current row.
std::size_t operator ++ (int);
/// Advances by one position and returns previous current position.
const Row& operator -- (int);
/// Goes back by one position and returns const reference
/// to the previous current row.
std::size_t operator -- ();
/// Goes back by one position and returns current position.
std::size_t operator -- (int);
/// Goes back by one position and returns previouscurrent position.
private:
RowIterator();
@ -95,8 +101,8 @@ private:
static const int POSITION_END;
RecordSet& _recordSet;
std::size_t _position;
const RecordSet& _recordSet;
};

View File

@ -121,13 +121,13 @@ DynamicAny RecordSet::value(const std::string& name, std::size_t row) const
const RowIterator& RecordSet::begin()
{
if (!_pBegin)
_pBegin = new RowIterator(*this, 0 == extractions().size());
_pBegin = new RowIterator(*this);
return *_pBegin;
}
const Row& RecordSet::row(std::size_t pos) const
Row& RecordSet::row(std::size_t pos)
{
if (pos > rowCount() - 1)
throw RangeException("Invalid recordset row requested.");
@ -136,12 +136,23 @@ const Row& RecordSet::row(std::size_t pos) const
Row* pRow = 0;
if (it == _rowMap.end())
{
pRow = new Row;
for (std::size_t i = 0; i < columnCount(); ++i)
pRow->append(metaColumn(static_cast<UInt32>(pos)).name(), value(i, pos));
if (_rowMap.size())//reuse first row column names to save some memory
{
pRow = new Row(_rowMap.begin()->second->names());
for (std::size_t i = 0; i < columnCount(); ++i)
pRow->set(i, value(i, pos));
}
else
{
pRow = new Row;
for (std::size_t i = 0; i < columnCount(); ++i)
pRow->append(metaColumn(static_cast<UInt32>(pos)).name(), value(i, pos));
}
_rowMap.insert(RowMap::value_type(pos, pRow));
}
else
pRow = it->second;
poco_check_ptr (pRow);
return *pRow;

View File

@ -1,7 +1,7 @@
//
// Row.cpp
//
// $Id: //poco/Main/Data/src/Row.cpp#2 $
// $Id: //poco/Main/Data/src/Row.cpp#1 $
//
// Library: Data
// Package: DataCore
@ -52,60 +52,74 @@ const std::string Row::EOL = "\n";
std::ostream& operator << (std::ostream &os, const Row& row)
{
os << row.toStringV();
os << row.valuesToString();
return os;
}
Row::Row():
_separator("\t"),
_sortField(0),
_comparison(COMPARE_AS_STRING)
Row::Row(): _separator("\t"), _pNames(0)
{
}
Row::Row(NameVecPtr pNames): _separator("\t"), _pNames(pNames)
{
if (!_pNames)
throw NullPointerException();
_values.resize(_pNames->size());
addSortField(0);
}
Row::~Row()
{
}
DynamicAny& Row::operator [] (std::size_t col)
DynamicAny& Row::get(std::size_t col)
{
try
{
return _values.at(col);
}catch (std::range_error& re)
}catch (std::out_of_range& re)
{
throw RangeException(re.what());
}
}
DynamicAny& Row::operator [] (const std::string& name)
{
std::size_t col = getPosition(name);
return (*this)[col];
}
std::size_t Row::getPosition(const std::string& name)
{
std::vector<std::string>::const_iterator it = _names.begin();
std::vector<std::string>::const_iterator end = _names.end();
if (!_pNames)
throw NullPointerException();
NameVec::const_iterator it = _pNames->begin();
NameVec::const_iterator end = _pNames->end();
std::size_t col = 0;
for (; it != end; ++it, ++col)
if (name == *it) break;
if (it == end)
throw NotFoundException(name);
return col;
}
void Row::sortField(std::size_t pos)
void Row::addSortField(std::size_t pos)
{
poco_assert (pos <= _values.size());
_sortField = pos;
SortMap::iterator it = _sortFields.begin();
SortMap::iterator end = _sortFields.end();
for (; it != end; ++it)
{
if (it->get<0>() == pos)
throw InvalidAccessException("Field already in comparison set.");
}
ComparisonType ct;
if ((_values[pos].type() == typeid(Poco::Int8)) ||
(_values[pos].type() == typeid(Poco::UInt8)) ||
(_values[pos].type() == typeid(Poco::Int16)) ||
@ -116,24 +130,103 @@ void Row::sortField(std::size_t pos)
(_values[pos].type() == typeid(Poco::UInt64)) ||
(_values[pos].type() == typeid(bool)))
{
comparison(COMPARE_AS_INTEGER);
ct = COMPARE_AS_INTEGER;
}
else if ((_values[pos].type() == typeid(float)) ||
(_values[pos].type() == typeid(double)))
{
comparison(COMPARE_AS_FLOAT);
ct = COMPARE_AS_FLOAT;
}
else
{
comparison(COMPARE_AS_STRING);
ct = COMPARE_AS_STRING;
}
_sortFields.push_back(SortTuple(pos, ct));
}
void Row::sortField(const std::string& name)
void Row::addSortField(const std::string& name)
{
sortField(getPosition(name));
addSortField(getPosition(name));
}
void Row::removeSortField(std::size_t pos)
{
SortMap::iterator it = _sortFields.begin();
SortMap::iterator end = _sortFields.end();
for (; it != end; ++it)
{
if (it->get<0>() == pos)
{
_sortFields.erase(it);
return;
}
}
}
void Row::removeSortField(const std::string& name)
{
removeSortField(getPosition(name));
}
void Row::replaceSortField(std::size_t oldPos, std::size_t newPos)
{
poco_assert (oldPos <= _values.size());
poco_assert (newPos <= _values.size());
ComparisonType ct;
if ((_values[newPos].type() == typeid(Poco::Int8)) ||
(_values[newPos].type() == typeid(Poco::UInt8)) ||
(_values[newPos].type() == typeid(Poco::Int16)) ||
(_values[newPos].type() == typeid(Poco::UInt16)) ||
(_values[newPos].type() == typeid(Poco::Int32)) ||
(_values[newPos].type() == typeid(Poco::UInt32)) ||
(_values[newPos].type() == typeid(Poco::Int64)) ||
(_values[newPos].type() == typeid(Poco::UInt64)) ||
(_values[newPos].type() == typeid(bool)))
{
ct = COMPARE_AS_INTEGER;
}
else if ((_values[newPos].type() == typeid(float)) ||
(_values[newPos].type() == typeid(double)))
{
ct = COMPARE_AS_FLOAT;
}
else
{
ct = COMPARE_AS_STRING;
}
SortMap::iterator it = _sortFields.begin();
SortMap::iterator end = _sortFields.end();
for (; it != end; ++it)
{
if (it->get<0>() == oldPos)
{
*it = SortTuple(newPos, ct);
return;
}
}
throw NotFoundException("Field not found");
}
void Row::replaceSortField(const std::string& oldName, const std::string& newName)
{
replaceSortField(getPosition(oldName), getPosition(newName));
}
void Row::resetSort()
{
_sortFields.clear();
if (_values.size()) addSortField(0);
}
@ -157,12 +250,6 @@ bool Row::isEqualType(const Row& other) const
}
void Row::comparison(Comparison comp)
{
_comparison = comp;
}
bool Row::operator == (const Row& other) const
{
if (!isEqualSize(other)) return false;
@ -188,57 +275,80 @@ bool Row::operator != (const Row& other) const
bool Row::operator < (const Row& other) const
{
if (_sortField != other._sortField)
if (_sortFields != other._sortFields)
throw InvalidAccessException("Rows compared have different sorting criteria.");
switch (_comparison)
SortMap::const_iterator it = _sortFields.begin();
SortMap::const_iterator end = _sortFields.end();
for (; it != end; ++it)
{
case COMPARE_AS_INTEGER:
return (_values[_sortField].convert<Poco::Int64>() <
other._values[other._sortField].convert<Poco::Int64>());
switch (it->get<1>())
{
case COMPARE_AS_INTEGER:
if (_values[it->get<0>()].convert<Poco::Int64>() <
other._values[it->get<0>()].convert<Poco::Int64>())
return true;
else if (_values[it->get<0>()].convert<Poco::Int64>() !=
other._values[it->get<0>()].convert<Poco::Int64>())
return false;
break;
case COMPARE_AS_FLOAT:
return (_values[_sortField].convert<double>() <
other._values[other._sortField].convert<double>());
case COMPARE_AS_FLOAT:
if (_values[it->get<0>()].convert<double>() <
other._values[it->get<0>()].convert<double>())
return true;
else if (_values[it->get<0>()].convert<double>() <
other._values[it->get<0>()].convert<double>())
return false;
break;
case COMPARE_AS_STRING:
return (_values[_sortField].convert<std::string>() <
other._values[other._sortField].convert<std::string>());
case COMPARE_AS_STRING:
if (_values[it->get<0>()].convert<std::string>() <
other._values[it->get<0>()].convert<std::string>())
return true;
else if (_values[it->get<0>()].convert<std::string>() <
other._values[it->get<0>()].convert<std::string>())
return false;
break;
}
}
throw IllegalStateException("Unknown comparison mode.");
return false;
}
const std::string& Row::toStringV() const
const std::string Row::valuesToString() const
{
_strValues.clear();
std::vector<DynamicAny>::const_iterator it = _values.begin();
std::vector<DynamicAny>::const_iterator end = _values.end();
std::string strValues;
ValueVec::const_iterator it = _values.begin();
ValueVec::const_iterator end = _values.end();
for (; it != end; ++it)
{
_strValues.append(it->convert<std::string>());
_strValues.append(_separator);
strValues.append(it->convert<std::string>());
strValues.append(_separator);
}
_strValues.replace(_strValues.find_last_of(_separator), _separator.length(), EOL);
strValues.replace(strValues.find_last_of(_separator), _separator.length(), EOL);
return _strValues;
return strValues;
}
const std::string& Row::toStringN() const
const std::string Row::namesToString() const
{
_strNames.clear();
std::vector<std::string>::const_iterator it = _names.begin();
std::vector<std::string>::const_iterator end = _names.end();
if (!_pNames)
throw NullPointerException();
std::string strNames;
NameVec::const_iterator it = _pNames->begin();
NameVec::const_iterator end = _pNames->end();
for (; it != end; ++it)
{
_strNames.append(*it);
_strNames.append(_separator);
strNames.append(*it);
strNames.append(_separator);
}
_strNames.replace(_strNames.find_last_of(_separator), _separator.length(), EOL);
strNames.replace(strNames.find_last_of(_separator), _separator.length(), EOL);
return _strNames;
return strNames;
}

View File

@ -1,7 +1,7 @@
//
// RowIterator.cpp
//
// $Id: //poco/Main/Data/src/RowIterator.cpp#2 $
// $Id: //poco/Main/Data/src/RowIterator.cpp#1 $
//
// Library: Data
// Package: DataCore
@ -48,9 +48,9 @@ namespace Data {
const int RowIterator::POSITION_END = std::numeric_limits<std::size_t>::max();
RowIterator::RowIterator(const RecordSet& recordSet, bool isEmpty):
_position(isEmpty ? POSITION_END : 0),
_recordSet(recordSet)
RowIterator::RowIterator(RecordSet& recordSet, bool positionEnd):
_recordSet(recordSet),
_position((0 == recordSet.rowCount()) || positionEnd ? POSITION_END : 0)
{
}
@ -75,43 +75,59 @@ void RowIterator::increment()
void RowIterator::decrement()
{
if (0 == _position)
throw RangeException("End of iterator reached.");
--_position;
throw RangeException("Beginning of iterator reached.");
else if (POSITION_END == _position)
_position = _recordSet.rowCount() - 1;
else
--_position;
}
const Row& RowIterator::operator * () const
Row& RowIterator::operator * () const
{
if (POSITION_END == _position)
throw InvalidAccessException("End of iterator reached.");
return _recordSet.row(_position);
}
const Row& RowIterator::operator ++ ()
Row* RowIterator::operator -> () const
{
if (POSITION_END == _position)
throw InvalidAccessException("End of iterator reached.");
return &_recordSet.row(_position);
}
std::size_t RowIterator::operator ++ ()
{
increment();
return _recordSet.row(_position);
return _position;
}
const Row& RowIterator::operator ++ (int)
std::size_t RowIterator::operator ++ (int)
{
std::size_t oldPos = _position;
increment();
return _recordSet.row(_position - 1);
return oldPos;
}
const Row& RowIterator::operator -- ()
std::size_t RowIterator::operator -- ()
{
decrement();
return _recordSet.row(_position);
return _position;
}
const Row& RowIterator::operator -- (int)
std::size_t RowIterator::operator -- (int)
{
std::size_t oldPos = _position;
decrement();
return _recordSet.row(_position + 1);
return oldPos;
}

View File

@ -38,7 +38,6 @@
#include "Poco/Data/BLOBStream.h"
#include "Poco/Data/MetaColumn.h"
#include "Poco/Data/Column.h"
#include "Poco/Data/Row.h"
#include "Connector.h"
#include "Poco/BinaryReader.h"
#include "Poco/BinaryWriter.h"
@ -46,7 +45,7 @@
#include "Poco/Exception.h"
#include <cstring>
#include <sstream>
#include <map>
#include <set>
using namespace Poco::Data;
@ -59,6 +58,7 @@ using Poco::Int64;
using Poco::UInt64;
using Poco::InvalidAccessException;
using Poco::RangeException;
using Poco::NotFoundException;
DataTest::DataTest(const std::string& name): CppUnit::TestCase(name)
@ -576,13 +576,36 @@ void DataTest::testColumnList()
void DataTest::testRow()
{
Row row;
row.append("field0", 0);
row.append("field1", 1);
row.append("field2", 2);
row.append("field3", 3);
row.append("field4", 4);
assert (row["field0"] == 0);
assert (row["field1"] == 1);
assert (row["field2"] == 2);
assert (row["field3"] == 3);
assert (row["field4"] == 4);
assert (row[0] == 0);
assert (row[1] == 1);
assert (row[2] == 2);
assert (row[3] == 3);
assert (row[4] == 4);
try
{
int i = row[5];
fail ("must fail");
}catch (RangeException&) {}
try
{
int i = row["a bad name"];
fail ("must fail");
}catch (NotFoundException&) {}
assert (5 == row.fieldCount());
assert (row[0] == 0);
assert (row["field0"] == 0);
@ -595,12 +618,12 @@ void DataTest::testRow()
assert (row[4] == 4);
assert (row["field4"] == 4);
assert (row.toStringN() == std::string("field0\tfield1\tfield2\tfield3\tfield4") + Row::EOL);
assert (row.namesToString() == std::string("field0\tfield1\tfield2\tfield3\tfield4") + Row::EOL);
std::ostringstream os;
os << row;
assert (os.str() == std::string("0\t1\t2\t3\t4") + Row::EOL);
row.separator(",");
assert (row.toStringN() == std::string("field0,field1,field2,field3,field4") + Row::EOL);
assert (row.namesToString() == std::string("field0,field1,field2,field3,field4") + Row::EOL);
os.str("");
os << row;
assert (os.str() == std::string("0,1,2,3,4") + Row::EOL);
@ -615,30 +638,6 @@ void DataTest::testRow()
assert (row != row2);
std::map<Row, int> rowMap;
rowMap.insert(std::map<Row, int>::value_type(row2, 0));
rowMap.insert(std::map<Row, int>::value_type(row, 1));
std::map<Row, int>::iterator it = rowMap.begin();
assert (row == it->first);
++it;
assert (row2 == it->first);
rowMap.clear();
row.sortField("field4");
rowMap.insert(std::map<Row, int>::value_type(row, 0));
try
{
rowMap.insert(std::map<Row, int>::value_type(row2, 1));
fail ("must fail");
}catch (InvalidAccessException&) {}
row2.sortField("field4");
rowMap.insert(std::map<Row, int>::value_type(row2, 1));
it = rowMap.begin();
assert (row2 == it->first);
++it;
assert (row == it->first);
Row row3;
row3.append("field0", 0);
@ -648,6 +647,160 @@ void DataTest::testRow()
row3.append("field4", 4);
assert (row3 == row);
assert (!(row < row3 | row3 < row));
Row row4(row3.names());
try
{
row4.set("badfieldname", 0);
fail ("must fail");
}catch (NotFoundException&) {}
row4.set("field0", 0);
row4.set("field1", 1);
row4.set("field2", 2);
row4.set("field3", 3);
row4.set("field4", 4);
assert (row3 == row4);
try
{
row4.set(5, 0);
fail ("must fail");
}catch (RangeException&) {}
row4.set("field0", 1);
assert (row3 != row4);
assert (row3 < row4);
}
void DataTest::testRowSort()
{
Row row1;
row1.append("0", 0);
row1.append("1", 1);
row1.append("2", 2);
row1.append("3", 3);
row1.append("4", 4);
Row row2;
row2.append("0", 0);
row2.append("1", 1);
row2.append("2", 2);
row2.append("3", 3);
row2.append("4", 4);
std::multiset<Row> rowSet1;
rowSet1.insert(row1);
rowSet1.insert(row2);
std::multiset<Row>::iterator it1 = rowSet1.begin();
assert (row1 == *it1);
++it1;
assert (row2 == *it1);
Row row3;
row3.append("0", 1);
row3.append("1", 1);
row3.append("2", 2);
row3.append("3", 3);
row3.append("4", 4);
Row row4;
row4.append("0", 0);
row4.append("1", 1);
row4.append("2", 2);
row4.append("3", 3);
row4.append("4", 4);
std::set<Row> rowSet2;
rowSet2.insert(row4);
rowSet2.insert(row3);
std::set<Row>::iterator it2 = rowSet2.begin();
assert (row4 == *it2);
++it2;
assert (row3 == *it2);
Row row5;
row5.append("0", 2);
row5.append("1", 2);
row5.append("2", 0);
row5.append("3", 3);
row5.append("4", 4);
row5.addSortField("1");
Row row6;
row6.append("0", 1);
row6.append("1", 0);
row6.append("2", 1);
row6.append("3", 3);
row6.append("4", 4);
row6.addSortField("1");
Row row7;
row7.append("0", 0);
row7.append("1", 1);
row7.append("2", 2);
row7.append("3", 3);
row7.append("4", 4);
std::set<Row> rowSet3;
rowSet3.insert(row5);
rowSet3.insert(row6);
try
{
rowSet3.insert(row7);//has no same sort criteria
fail ("must fail");
} catch (InvalidAccessException&) {}
row7.addSortField("1");
testRowStrictWeak(row7, row6, row5);
rowSet3.insert(row7);
std::set<Row>::iterator it3 = rowSet3.begin();
assert (row7 == *it3);
++it3;
assert (row6 == *it3);
++it3;
assert (row5 == *it3);
row5.replaceSortField("0", "2");
row6.replaceSortField("0", "2");
row7.replaceSortField("0", "2");
rowSet3.clear();
rowSet3.insert(row7);
rowSet3.insert(row6);
rowSet3.insert(row5);
it3 = rowSet3.begin();
assert (row5 == *it3);
++it3;
assert (row6 == *it3);
++it3;
assert (row7 == *it3);
row5.resetSort();
row6.resetSort();
row7.resetSort();
rowSet3.clear();
rowSet3.insert(row5);
rowSet3.insert(row6);
rowSet3.insert(row7);
it3 = rowSet3.begin();
assert (row7 == *it3);
++it3;
assert (row6 == *it3);
++it3;
assert (row5 == *it3);
}
void DataTest::testRowStrictWeak(const Row& row1, const Row& row2, const Row& row3)
{
assert (row1 < row2 && !(row2 < row1)); // antisymmetric
assert (row1 < row2 && row2 < row3 && row1 < row3); // transitive
assert (!(row1 < row1)); // irreflexive
}
@ -674,6 +827,7 @@ CppUnit::Test* DataTest::suite()
CppUnit_addTest(pSuite, DataTest, testColumnDeque);
CppUnit_addTest(pSuite, DataTest, testColumnList);
CppUnit_addTest(pSuite, DataTest, testRow);
CppUnit_addTest(pSuite, DataTest, testRowSort);
return pSuite;
}

View File

@ -39,6 +39,7 @@
#include "Poco/Data/Data.h"
#include "Poco/BinaryReader.h"
#include "Poco/BinaryWriter.h"
#include "Poco/Data/Row.h"
#include "CppUnit/TestCase.h"
@ -57,6 +58,7 @@ public:
void testColumnDeque();
void testColumnList();
void testRow();
void testRowSort();
void setUp();
void tearDown();
@ -64,6 +66,15 @@ public:
static CppUnit::Test* suite();
private:
void testRowStrictWeak(const Poco::Data::Row& row1,
const Poco::Data::Row& row2,
const Poco::Data::Row& row3);
/// Strict weak ordering requirement for sorted containers
/// as described in Josuttis "The Standard C++ Library"
/// chapter 6.5. pg. 176.
/// For this to pass, the following condition must be satisifed:
/// row1 < row2 < row3
void writeToBLOB(Poco::BinaryWriter& writer);
void readFromBLOB(Poco::BinaryReader& reader);
};