SF [2047672] RecordSet Filtering

This commit is contained in:
Aleksandar Fabijanic 2008-08-12 10:12:56 +00:00
parent d6b81dca0f
commit e409026fba
23 changed files with 986 additions and 86 deletions

View File

@ -333,6 +333,9 @@
<File <File
RelativePath=".\include\Poco\Data\Row.h"> RelativePath=".\include\Poco\Data\Row.h">
</File> </File>
<File
RelativePath=".\include\Poco\Data\RowFilter.h">
</File>
<File <File
RelativePath=".\include\Poco\Data\RowFormatter.h"> RelativePath=".\include\Poco\Data\RowFormatter.h">
</File> </File>
@ -424,6 +427,9 @@
<File <File
RelativePath=".\src\Row.cpp"> RelativePath=".\src\Row.cpp">
</File> </File>
<File
RelativePath=".\src\RowFilter.cpp">
</File>
<File <File
RelativePath=".\src\RowFormatter.cpp"> RelativePath=".\src\RowFormatter.cpp">
</File> </File>

View File

@ -461,6 +461,10 @@
RelativePath=".\include\Poco\Data\Row.h" RelativePath=".\include\Poco\Data\Row.h"
> >
</File> </File>
<File
RelativePath=".\include\Poco\Data\RowFilter.h"
>
</File>
<File <File
RelativePath=".\include\Poco\Data\RowFormatter.h" RelativePath=".\include\Poco\Data\RowFormatter.h"
> >
@ -585,6 +589,10 @@
RelativePath=".\src\Row.cpp" RelativePath=".\src\Row.cpp"
> >
</File> </File>
<File
RelativePath=".\src\RowFilter.cpp"
>
</File>
<File <File
RelativePath=".\src\RowFormatter.cpp" RelativePath=".\src\RowFormatter.cpp"
> >

View File

@ -466,6 +466,10 @@
RelativePath=".\include\Poco\Data\Row.h" RelativePath=".\include\Poco\Data\Row.h"
> >
</File> </File>
<File
RelativePath=".\include\Poco\Data\RowFilter.h"
>
</File>
<File <File
RelativePath=".\include\Poco\Data\RowFormatter.h" RelativePath=".\include\Poco\Data\RowFormatter.h"
> >
@ -590,6 +594,10 @@
RelativePath=".\src\Row.cpp" RelativePath=".\src\Row.cpp"
> >
</File> </File>
<File
RelativePath=".\src\RowFilter.cpp"
>
</File>
<File <File
RelativePath=".\src\RowFormatter.cpp" RelativePath=".\src\RowFormatter.cpp"
> >

View File

@ -8,11 +8,11 @@
include $(POCO_BASE)/build/rules/global include $(POCO_BASE)/build/rules/global
objects = AbstractBinder AbstractBinding AbstractExtraction \ objects = AbstractBinder AbstractBinding AbstractExtraction AbstractExtractor \
AbstractExtractor AbstractPreparation AbstractPrepare ArchiveStrategy \ AbstractPreparation AbstractPrepare ArchiveStrategy AutoTransaction \
Bulk Connector BLOB BLOBStream DataException Date Limit MetaColumn \ Bulk Connector BLOB BLOBStream DataException Date Limit MetaColumn \
PooledSessionHolder PooledSessionImpl Position \ PooledSessionHolder PooledSessionImpl Position \
Range RecordSet Row RowFormatter RowIterator \ Range RecordSet Row RowFilter RowFormatter RowIterator \
SimpleRowFormatter Session SessionFactory SessionImpl \ SimpleRowFormatter Session SessionFactory SessionImpl \
SessionPool SQLChannel Statement StatementCreator StatementImpl Time SessionPool SQLChannel Statement StatementCreator StatementImpl Time

View File

@ -43,6 +43,7 @@
#include "Poco/UnicodeConverter.h" #include "Poco/UnicodeConverter.h"
#include "Poco/Buffer.h" #include "Poco/Buffer.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
#include <cstring>
#ifdef POCO_OS_FAMILY_WINDOWS #ifdef POCO_OS_FAMILY_WINDOWS
#include <windows.h> #include <windows.h>
#endif #endif

View File

@ -166,6 +166,21 @@ void ODBCDB2Test::testBLOB()
} }
void ODBCDB2Test::testFilter()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreateVectorsTable();
_pSession->setFeature("autoBind", bindValue(i));
_pSession->setFeature("autoExtract", bindValue(i+1));
_pExecutor->filter("SELECT * FROM Vectors ORDER BY i0 ASC", "i0");
i += 2;
}
}
void ODBCDB2Test::testStoredProcedure() void ODBCDB2Test::testStoredProcedure()
{ {
if (!_pSession) fail ("Test not available."); if (!_pSession) fail ("Test not available.");
@ -646,6 +661,7 @@ CppUnit::Test* ODBCDB2Test::suite()
CppUnit_addTest(pSuite, ODBCDB2Test, testTuple); CppUnit_addTest(pSuite, ODBCDB2Test, testTuple);
CppUnit_addTest(pSuite, ODBCDB2Test, testTupleVector); CppUnit_addTest(pSuite, ODBCDB2Test, testTupleVector);
CppUnit_addTest(pSuite, ODBCDB2Test, testInternalExtraction); CppUnit_addTest(pSuite, ODBCDB2Test, testInternalExtraction);
CppUnit_addTest(pSuite, ODBCDB2Test, testFilter);
CppUnit_addTest(pSuite, ODBCDB2Test, testInternalBulkExtraction); CppUnit_addTest(pSuite, ODBCDB2Test, testInternalBulkExtraction);
CppUnit_addTest(pSuite, ODBCDB2Test, testInternalStorageType); CppUnit_addTest(pSuite, ODBCDB2Test, testInternalStorageType);
CppUnit_addTest(pSuite, ODBCDB2Test, testStoredProcedure); CppUnit_addTest(pSuite, ODBCDB2Test, testStoredProcedure);

View File

@ -55,6 +55,7 @@ public:
void testBareboneODBC(); void testBareboneODBC();
void testBLOB(); void testBLOB();
void testFilter();
void testStoredProcedure(); void testStoredProcedure();
void testStoredProcedureAny(); void testStoredProcedureAny();

View File

@ -240,6 +240,21 @@ http://bugs.mysql.com/bug.php?id=7445
} }
void ODBCMySQLTest::testFilter()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreateVectorsTable();
_pSession->setFeature("autoBind", bindValue(i));
_pSession->setFeature("autoExtract", bindValue(i+1));
_pExecutor->filter("SELECT * FROM Vectors ORDER BY i0 ASC", "i0");
i += 2;
}
}
void ODBCMySQLTest::dropObject(const std::string& type, const std::string& name) void ODBCMySQLTest::dropObject(const std::string& type, const std::string& name)
{ {
*_pSession << format("DROP %s IF EXISTS %s", type, name), now; *_pSession << format("DROP %s IF EXISTS %s", type, name), now;
@ -466,6 +481,7 @@ CppUnit::Test* ODBCMySQLTest::suite()
CppUnit_addTest(pSuite, ODBCMySQLTest, testStoredProcedure); CppUnit_addTest(pSuite, ODBCMySQLTest, testStoredProcedure);
CppUnit_addTest(pSuite, ODBCMySQLTest, testStoredFunction); CppUnit_addTest(pSuite, ODBCMySQLTest, testStoredFunction);
CppUnit_addTest(pSuite, ODBCMySQLTest, testInternalExtraction); CppUnit_addTest(pSuite, ODBCMySQLTest, testInternalExtraction);
CppUnit_addTest(pSuite, ODBCMySQLTest, testFilter);
//CppUnit_addTest(pSuite, ODBCOracleTest, testInternalBulkExtraction); //CppUnit_addTest(pSuite, ODBCOracleTest, testInternalBulkExtraction);
CppUnit_addTest(pSuite, ODBCMySQLTest, testInternalStorageType); CppUnit_addTest(pSuite, ODBCMySQLTest, testInternalStorageType);
CppUnit_addTest(pSuite, ODBCMySQLTest, testNull); CppUnit_addTest(pSuite, ODBCMySQLTest, testNull);

View File

@ -65,6 +65,7 @@ public:
void testNull(); void testNull();
void testMultipleResults(); void testMultipleResults();
void testFilter();
static CppUnit::Test* suite(); static CppUnit::Test* suite();

View File

@ -905,6 +905,7 @@ CppUnit::Test* ODBCOracleTest::suite()
CppUnit_addTest(pSuite, ODBCOracleTest, testStoredFunction); CppUnit_addTest(pSuite, ODBCOracleTest, testStoredFunction);
CppUnit_addTest(pSuite, ODBCOracleTest, testCursorStoredFunction); CppUnit_addTest(pSuite, ODBCOracleTest, testCursorStoredFunction);
CppUnit_addTest(pSuite, ODBCOracleTest, testInternalExtraction); CppUnit_addTest(pSuite, ODBCOracleTest, testInternalExtraction);
CppUnit_addTest(pSuite, ODBCOracleTest, testFilter);
CppUnit_addTest(pSuite, ODBCOracleTest, testInternalBulkExtraction); CppUnit_addTest(pSuite, ODBCOracleTest, testInternalBulkExtraction);
CppUnit_addTest(pSuite, ODBCOracleTest, testInternalStorageType); CppUnit_addTest(pSuite, ODBCOracleTest, testInternalStorageType);
CppUnit_addTest(pSuite, ODBCOracleTest, testNull); CppUnit_addTest(pSuite, ODBCOracleTest, testNull);

View File

@ -629,6 +629,7 @@ CppUnit::Test* ODBCPostgreSQLTest::suite()
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTuple); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTuple);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTupleVector); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTupleVector);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testInternalExtraction); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testInternalExtraction);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testFilter);
//On Linux, PostgreSQL driver returns SQL_NEED_DATA on SQLExecute (see ODBCStatementImpl::bindImpl() ) //On Linux, PostgreSQL driver returns SQL_NEED_DATA on SQLExecute (see ODBCStatementImpl::bindImpl() )
//this behavior is not expected and not handled for automatic binding //this behavior is not expected and not handled for automatic binding
#ifdef POCO_OS_FAMILY_WINDOWS #ifdef POCO_OS_FAMILY_WINDOWS

View File

@ -757,6 +757,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredProcedureDynamicAny); CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredProcedureDynamicAny);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredFunction); CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredFunction);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalExtraction); CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalExtraction);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testFilter);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalBulkExtraction); CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalBulkExtraction);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalStorageType); CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalStorageType);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testNull); CppUnit_addTest(pSuite, ODBCSQLServerTest, testNull);

View File

@ -382,6 +382,7 @@ CppUnit::Test* ODBCSQLiteTest::suite()
CppUnit_addTest(pSuite, ODBCSQLiteTest, testTuple); CppUnit_addTest(pSuite, ODBCSQLiteTest, testTuple);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testTupleVector); CppUnit_addTest(pSuite, ODBCSQLiteTest, testTupleVector);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testInternalExtraction); CppUnit_addTest(pSuite, ODBCSQLiteTest, testInternalExtraction);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testFilter);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testInternalStorageType); CppUnit_addTest(pSuite, ODBCSQLiteTest, testInternalStorageType);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testNull); CppUnit_addTest(pSuite, ODBCSQLiteTest, testNull);
CppUnit_addTest(pSuite, ODBCSQLiteTest, testRowIterator); CppUnit_addTest(pSuite, ODBCSQLiteTest, testRowIterator);

View File

@ -919,6 +919,21 @@ void ODBCTest::testInternalExtraction()
} }
void ODBCTest::testFilter()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreateVectorsTable();
_pSession->setFeature("autoBind", bindValue(i));
_pSession->setFeature("autoExtract", bindValue(i+1));
_pExecutor->filter();
i += 2;
}
}
void ODBCTest::testInternalBulkExtraction() void ODBCTest::testInternalBulkExtraction()
{ {
if (!_pSession) fail ("Test not available."); if (!_pSession) fail ("Test not available.");

View File

@ -135,6 +135,7 @@ public:
virtual void testTupleVector(); virtual void testTupleVector();
virtual void testInternalExtraction(); virtual void testInternalExtraction();
virtual void testFilter();
virtual void testInternalBulkExtraction(); virtual void testInternalBulkExtraction();
virtual void testInternalStorageType(); virtual void testInternalStorageType();

View File

@ -51,6 +51,7 @@
#include "Poco/Data/StatementImpl.h" #include "Poco/Data/StatementImpl.h"
#include "Poco/Data/RecordSet.h" #include "Poco/Data/RecordSet.h"
#include "Poco/Data/RowIterator.h" #include "Poco/Data/RowIterator.h"
#include "Poco/Data/RowFilter.h"
#include "Poco/Data/BulkExtraction.h" #include "Poco/Data/BulkExtraction.h"
#include "Poco/Data/BulkBinding.h" #include "Poco/Data/BulkBinding.h"
#include "Poco/Data/SQLChannel.h" #include "Poco/Data/SQLChannel.h"
@ -72,6 +73,8 @@ using Poco::Data::Statement;
using Poco::Data::RecordSet; using Poco::Data::RecordSet;
using Poco::Data::Column; using Poco::Data::Column;
using Poco::Data::Row; using Poco::Data::Row;
using Poco::Data::RowFilter;
using Poco::Data::RowIterator;
using Poco::Data::SQLChannel; using Poco::Data::SQLChannel;
using Poco::Data::LimitException; using Poco::Data::LimitException;
using Poco::Data::BindingException; using Poco::Data::BindingException;
@ -2526,6 +2529,86 @@ void SQLExecutor::internalExtraction()
} }
void SQLExecutor::filter(const std::string& query, const std::string& intFldName)
{
std::string funct = "filter()";
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 { session() << "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); }
try
{
Statement stmt = (session() << query, now);
RecordSet rset(stmt);
assert (rset.totalRowCount() == 4);
RowFilter::Ptr pRF = new RowFilter(&rset);
assert (pRF->isEmpty());
pRF->add(intFldName, RowFilter::VALUE_EQUAL, 1);
assert (!pRF->isEmpty());
DynamicAny da;
try
{
da = rset.value(0, 1);
fail ("must fail");
} catch (InvalidAccessException&)
{
da = rset.value(0, 1, false);
assert (2 == da);
da = rset.value(0, 0);
assert (1 == da);
}
assert (rset.rowCount() == 1);
assert (rset.moveFirst());
assert (1 == rset[intFldName]);
assert (!rset.moveNext());
pRF->add("flt0", RowFilter::VALUE_LESS_THAN_OR_EQUAL, 3.5f);
assert (rset.rowCount() == 3);
assert (rset.moveNext());
assert (2.5 == rset["flt0"]);
assert (rset.moveNext());
assert (3.5 == rset["flt0"]);
assert (!rset.moveNext());
pRF->add("str0", RowFilter::VALUE_EQUAL, 6);
assert (rset.rowCount() == 4);
assert (rset.moveLast());
assert ("6" == rset["str0"]);
pRF->remove("flt0");
assert (rset.rowCount() == 2);
assert (rset.moveFirst());
assert ("3" == rset["str0"]);
assert (rset.moveNext());
assert ("6" == rset["str0"]);
pRF->remove(intFldName);
pRF->remove("str0");
assert (pRF->isEmpty());
pRF->add("str0", "!=", 3);
assert (rset.rowCount() == 3);
RowFilter::Ptr pRF1 = new RowFilter(pRF, RowFilter::OP_AND);
pRF1->add(intFldName, "==", 2);
assert (rset.rowCount() == 1);
pRF1->add(intFldName, "<", 2);
assert (rset.rowCount() == 1);
pRF1->add(intFldName, ">", 3);
assert (rset.rowCount() == 2);
pRF->removeFilter(pRF1);
pRF->remove("str0");
assert (pRF->isEmpty());
assert (rset.rowCount() == 4);
}
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
}
void SQLExecutor::internalBulkExtraction() void SQLExecutor::internalBulkExtraction()
{ {
std::string funct = "internalBulkExtraction()"; std::string funct = "internalBulkExtraction()";
@ -2665,7 +2748,8 @@ void SQLExecutor::notNulls(const std::string& sqlState)
{ {
//make sure we're failing for the right reason //make sure we're failing for the right reason
//default sqlState value is "23502"; some drivers report "HY???" codes //default sqlState value is "23502"; some drivers report "HY???" codes
assert (sqlState == se.diagnostics().sqlState(0)); if (se.diagnostics().fields().size())
assert (sqlState == se.diagnostics().sqlState(0));
} }
} }
@ -2686,12 +2770,18 @@ void SQLExecutor::nulls()
assert (rs.isNull("r")); assert (rs.isNull("r"));
assert (rs.isNull("v")); assert (rs.isNull("v"));
assert (rs["v"] != ""); assert (rs["v"] != "");
assert (rs.nvl<int>("i") == 0);
assert (rs.nvl("i", -1) == -1);
assert (rs.nvl<double>("r") == double());
assert (rs.nvl("r", -1.5) == -1.5);
assert (rs.nvl<std::string>("v") == "");
assert (rs.nvl("v", "123") == "123");
try { session() << "DELETE FROM NullTest", now; } try { session() << "DELETE FROM NullTest", now; }
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
int i = 1; int i = 1;
double f = 1.2; double f = 1.5;
std::string s = "123"; std::string s = "123";
try { session() << "INSERT INTO NullTest (i, r, v) VALUES (?,?,?)", use(i), use(f), use(s), now; } try { session() << "INSERT INTO NullTest (i, r, v) VALUES (?,?,?)", use(i), use(f), use(s), now; }
@ -2705,7 +2795,12 @@ void SQLExecutor::nulls()
assert (!rs.isNull("v")); assert (!rs.isNull("v"));
assert (!rs.isNull("r")); assert (!rs.isNull("r"));
assert (rs["v"] == "123"); assert (rs["v"] == "123");
assert (rs.nvl<int>("i") == 1);
assert (rs.nvl("i", -1) == 1);
assert (rs.nvl<double>("r") == 1.5);
assert (rs.nvl("r", -1.5) == 1.5);
assert (rs.nvl<std::string>("v") == "123");
assert (rs.nvl("v", "456") == "123");
try { session() << "UPDATE NullTest SET v = ? WHERE i = ?", use(null), use(i), now; } try { session() << "UPDATE NullTest SET v = ? WHERE i = ?", use(null), use(i), now; }
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
@ -2803,6 +2898,19 @@ void SQLExecutor::rowIterator()
std::ostringstream osCopy; std::ostringstream osCopy;
std::copy(rset.begin(), rset.end(), std::ostream_iterator<Row>(osCopy)); std::copy(rset.begin(), rset.end(), std::ostream_iterator<Row>(osCopy));
assert (osLoop.str() == osCopy.str()); assert (osLoop.str() == osCopy.str());
RowFilter::Ptr pRF = new RowFilter(&rset);
assert (pRF->isEmpty());
pRF->add("str0", RowFilter::VALUE_EQUAL, 1);
assert (!pRF->isEmpty());
it = rset.begin();
end = rset.end();
for (int i = 1; it != end; ++it, ++i)
{
assert (it->get(0) == i);
assert (1 == i);
}
} }

View File

@ -484,6 +484,10 @@ public:
void tupleVector(); void tupleVector();
void internalExtraction(); void internalExtraction();
void filter(const std::string& query =
"SELECT * FROM Vectors ORDER BY int0 ASC",
const std::string& intFldName = "int0");
void internalBulkExtraction(); void internalBulkExtraction();
void internalStorageType(); void internalStorageType();
void nulls(); void nulls();

View File

@ -56,7 +56,7 @@ namespace Poco {
namespace Data { namespace Data {
class Session; class RowFilter;
class Data_API RecordSet: private Statement class Data_API RecordSet: private Statement
@ -112,7 +112,16 @@ public:
/// Assignment operator. /// Assignment operator.
std::size_t rowCount() const; std::size_t rowCount() const;
/// Returns the number of rows in the recordset. /// Returns the number of rows in the RecordSet.
/// The number of rows reported is dependent on filtering.
/// Due to the need for filter conditions checking,
/// this function may suffer significant performance penalty
/// for large recordsets, so it should be used judiciously.
/// Use totalRowCount() to obtain the total number of rows.
std::size_t totalRowCount() const;
/// Returns the total number of rows in the RecordSet.
/// The number of rows reported is independent of filtering.
std::size_t columnCount() const; std::size_t columnCount() const;
/// Returns the number of rows in the recordset. /// Returns the number of rows in the recordset.
@ -154,9 +163,12 @@ public:
/// Rows are lazy-created and cached. /// Rows are lazy-created and cached.
template <class T> template <class T>
const T& value(std::size_t col, std::size_t row) const const T& value(std::size_t col, std::size_t row, bool useFilter = true) const
/// Returns the reference to data value at [col, row] location. /// Returns the reference to data value at [col, row] location.
{ {
if (useFilter && isFiltered() && !isAllowed(row))
throw InvalidAccessException("Row not allowed");
switch (storage()) switch (storage())
{ {
case STORAGE_VECTOR: case STORAGE_VECTOR:
@ -181,9 +193,12 @@ public:
} }
template <class T> template <class T>
const T& value(const std::string& name, std::size_t row) const const T& value(const std::string& name, std::size_t row, bool useFilter = true) const
/// Returns the reference to data value at named column, row location. /// Returns the reference to data value at named column, row location.
{ {
if (useFilter && isFiltered() && !isAllowed(row))
throw InvalidAccessException("Row not allowed");
switch (storage()) switch (storage())
{ {
case STORAGE_VECTOR: case STORAGE_VECTOR:
@ -207,19 +222,33 @@ public:
} }
} }
DynamicAny value(std::size_t col, std::size_t row) const; DynamicAny value(std::size_t col, std::size_t row, bool checkFiltering = true) const;
/// Returns the data value at column, row location. /// Returns the data value at column, row location.
DynamicAny value(const std::string& name, std::size_t row) const; DynamicAny value(const std::string& name, std::size_t row, bool checkFiltering = true) const;
/// Returns the data value at named column, row location. /// Returns the data value at named column, row location.
DynamicAny nvl(const std::string& name, const DynamicAny& deflt) const; template <typename T>
DynamicAny nvl(const std::string& name, const T& deflt = T()) const
/// Returns the value in the named column of the current row /// Returns the value in the named column of the current row
/// if the value is not NULL, or deflt otherwise. /// if the value is not NULL, or deflt otherwise.
{
DynamicAny nvl(std::size_t index, const DynamicAny& deflt) const; if (isNull(name))
return DynamicAny(deflt);
else
return value(name, _currentRow);
}
template <typename T>
DynamicAny nvl(std::size_t index, const T& deflt = T()) const
/// Returns the value in the given column of the current row /// Returns the value in the given column of the current row
/// if the value is not NULL, or deflt otherwise. /// if the value is not NULL, or deflt otherwise.
{
if (isNull(index, _currentRow))
return DynamicAny(deflt);
else
return value(index, _currentRow);
}
ConstIterator& begin() const; ConstIterator& begin() const;
/// Returns the const row iterator. /// Returns the const row iterator.
@ -315,6 +344,9 @@ public:
/// Copies the column names and values to the target output stream. /// Copies the column names and values to the target output stream.
/// Copied strings are formatted by the current RowFormatter. /// Copied strings are formatted by the current RowFormatter.
bool isFiltered() const;
/// Returns true if recordset is filtered.
private: private:
RecordSet(); RecordSet();
@ -384,10 +416,25 @@ private:
} }
} }
bool isAllowed(std::size_t row) const;
/// Returns true if the specified row is allowed by the
/// currently active filter.
void filter(RowFilter* pFilter);
/// Sets the filter for the RecordSet.
RowFilter* getFilter();
/// Returns the filter associated with the RecordSet.
std::size_t _currentRow; std::size_t _currentRow;
RowIterator* _pBegin; RowIterator* _pBegin;
RowIterator* _pEnd; RowIterator* _pEnd;
RowMap _rowMap; RowMap _rowMap;
RowFilter* _pFilter;
friend class RowIterator;
friend class RowFilter;
}; };
@ -397,11 +444,11 @@ private:
inline Data_API std::ostream& operator << (std::ostream &os, const RecordSet& rs) inline Data_API std::ostream& operator << (std::ostream &os, const RecordSet& rs)
{ {
return rs.copy(os); return rs.copy(os);
} }
inline std::size_t RecordSet::rowCount() const inline std::size_t RecordSet::totalRowCount() const
{ {
poco_assert (extractions().size()); poco_assert (extractions().size());
return extractions()[0].get()->numOfRowsHandled(); return extractions()[0].get()->numOfRowsHandled();
@ -524,6 +571,42 @@ inline std::ostream& RecordSet::copyNames(std::ostream& os) const
} }
inline RowFilter* RecordSet::getFilter()
{
return _pFilter;
}
/* TODO
namespace Keywords {
inline const std::string& select(const std::string& str)
{
return str;
}
inline const RecordSet& from(const RecordSet& rs)
{
return rs;
}
inline RecordSet from(const Statement& stmt)
{
return RecordSet(stmt);
}
inline const std::string& where(const std::string& str)
{
return str;
}
} // namespace Keywords
*/
} } // namespace Poco::Data } } // namespace Poco::Data

View File

@ -0,0 +1,285 @@
//
// RowFilter.h
//
// $Id: //poco/Main/Data/include/Poco/Data/RowFilter.h#1 $
//
// Library: Data
// Package: DataCore
// Module: RowFilter
//
// Definition of the RowFilter class.
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
#ifndef Data_RowFilter_INCLUDED
#define Data_RowFilter_INCLUDED
#include "Poco/Data/Data.h"
#include "Poco/Data/RecordSet.h"
#include "Poco/DynamicAny.h"
#include "Poco/Tuple.h"
#include "Poco/String.h"
#include "Poco/RefCountedObject.h"
#include "Poco/AutoPtr.h"
#include <map>
#include <list>
#include <utility>
namespace Poco {
namespace Data {
class Data_API RowFilter: public RefCountedObject
/// RowFilter class provides row filtering functionality.
/// A filter contains a set of criteria (field name, value and
/// logical operation) for row filtering.
/// Additionally, a row filter contains a map of pointers to other
/// filters with related logical operations between filters.
/// RowFilter is typically added to recordset in order to filter
/// its content. Since the recordset own iteration is dependent upon
/// filtering, whenever the filtering criteria is changed,
/// the filter automatically notifies all associated recordsets
/// by rewinding them to the first position.
{
public:
enum Comparison
{
VALUE_LESS_THAN,
VALUE_LESS_THAN_OR_EQUAL,
VALUE_EQUAL,
VALUE_GREATER_THAN,
VALUE_GREATER_THAN_OR_EQUAL,
VALUE_NOT_EQUAL,
VALUE_IS_NULL
};
enum LogicOperator
{
OP_AND,
OP_OR,
OP_NOT
};
typedef bool (*CompT)(const DynamicAny&, const DynamicAny&);
typedef AutoPtr<RowFilter> Ptr;
typedef std::map<std::string, Comparison> Comparisons;
typedef Tuple<DynamicAny, Comparison, LogicOperator> ComparisonEntry;
typedef std::multimap<std::string, ComparisonEntry> ComparisonMap;
typedef std::map<AutoPtr<RowFilter>, LogicOperator> FilterMap;
RowFilter(RecordSet* pRecordSet);
/// Creates the top-level RowFilter and associates it with the recordset.
RowFilter(Ptr pParent, LogicOperator op = OP_OR);
/// Creates child RowFilter and associates it with the parent filter.
~RowFilter();
/// Destroys the RowFilter.
void addFilter(const Ptr& pFilter, LogicOperator comparison);
/// Appends another filter to this one.
void removeFilter(const Ptr& pFilter);
/// Removes filter from this filter.
template <typename T>
void add(const std::string& name, Comparison comparison, const T& value, LogicOperator op = OP_OR)
/// Adds value to the filter.
{
if (_pRecordSet) _pRecordSet->moveFirst();
_comparisonMap.insert(ComparisonMap::value_type(toUpper(name),
ComparisonEntry(value, comparison, op)));
}
template <typename T>
void add(const std::string& name, const std::string& comp, const T& value, LogicOperator op = OP_OR)
/// Adds value to the filter.
{
add(name, getComparison(comp), value, op);
}
template <typename T>
void addAnd(const std::string& name, const std::string& comp, const T& value)
/// Adds logically AND-ed value to the filter.
{
add(name, getComparison(comp), value, OP_AND);
}
template <typename T>
void addOr(const std::string& name, const std::string& comp, const T& value)
/// Adds logically OR-ed value to the filter.
{
add(name, getComparison(comp), value, OP_OR);
}
int remove(const std::string& name);
/// Removes named comparisons from the filter.
/// All comparisons with specified name are removed.
/// Returns the number of comparisons removed.
void toggleNot();
/// Togless the NOT operator for this filter;
bool isNot() const;
/// Returns true if filter is NOT-ed, false otherwise.
bool isEmpty() const;
/// Returns true if there is not filtering criteria specified.
bool isAllowed(std::size_t row) const;//const std::string& name, const DynamicAny& val) const;
/// Returns true if name and value are allowed.
bool exists(const std::string& name) const;
/// Returns true if name is known to this row filter.
private:
RowFilter();
RowFilter(const RowFilter&);
RowFilter& operator=(const RowFilter&);
void init();
static bool equal(const DynamicAny& p1, const DynamicAny& p2);
static bool notEqual(const DynamicAny& p1, const DynamicAny& p2);
static bool less(const DynamicAny& p1, const DynamicAny& p2);
static bool greater(const DynamicAny& p1, const DynamicAny& p2);
static bool lessOrEqual(const DynamicAny& p1, const DynamicAny& p2);
static bool greaterOrEqual(const DynamicAny& p1, const DynamicAny& p2);
static bool logicalAnd(const DynamicAny& p1, const DynamicAny& p2);
static bool logicalOr(const DynamicAny& p1, const DynamicAny& p2);
static bool isNull(const DynamicAny& p1, const DynamicAny&);
static void doCompare(DynamicAny& ret,
DynamicAny& val,
CompT comp,
const ComparisonEntry& ce);
RecordSet& recordSet() const;
Comparison getComparison(const std::string& comp) const;
Comparisons _comparisons;
ComparisonMap _comparisonMap;
mutable RecordSet* _pRecordSet;
Ptr _pParent;
FilterMap _filterMap;
bool _not;
friend class RecordSet;
};
///
/// inlines
///
inline bool RowFilter::isEmpty() const
{
return _comparisonMap.size() == 0;
}
inline bool RowFilter::exists(const std::string& name) const
{
return _comparisonMap.find(name) != _comparisonMap.end();
}
inline void RowFilter::toggleNot()
{
_not = !_not;
}
inline bool RowFilter::isNot() const
{
return _not;
}
inline bool RowFilter::equal(const DynamicAny& p1, const DynamicAny& p2)
{
return p1 == p2;
}
inline bool RowFilter::notEqual(const DynamicAny& p1, const DynamicAny& p2)
{
return p1 != p2;
}
inline bool RowFilter::less(const DynamicAny& p1, const DynamicAny& p2)
{
return p1 < p2;
}
inline bool RowFilter::greater(const DynamicAny& p1, const DynamicAny& p2)
{
return p1 > p2;
}
inline bool RowFilter::lessOrEqual(const DynamicAny& p1, const DynamicAny& p2)
{
return p1 <= p2;
}
inline bool RowFilter::greaterOrEqual(const DynamicAny& p1, const DynamicAny& p2)
{
return p1 >= p2;
}
inline bool RowFilter::logicalAnd(const DynamicAny& p1, const DynamicAny& p2)
{
return p1 && p2;
}
inline bool RowFilter::logicalOr(const DynamicAny& p1, const DynamicAny& p2)
{
return p1 || p2;
}
inline bool RowFilter::isNull(const DynamicAny& p1, const DynamicAny&)
{
return p1.isEmpty();
}
} } // namespace Poco::Data
#endif // Data_RowFilter_INCLUDED

View File

@ -792,7 +792,6 @@ inline void swap(Statement& s1, Statement& s2)
} }
} } // namespace Poco::Data } } // namespace Poco::Data

View File

@ -36,6 +36,7 @@
#include "Poco/Data/RecordSet.h" #include "Poco/Data/RecordSet.h"
#include "Poco/Data/Session.h" #include "Poco/Data/Session.h"
#include "Poco/Data/RowFilter.h"
#include "Poco/Data/Date.h" #include "Poco/Data/Date.h"
#include "Poco/Data/Time.h" #include "Poco/Data/Time.h"
#include "Poco/Data/DataException.h" #include "Poco/Data/DataException.h"
@ -54,7 +55,8 @@ RecordSet::RecordSet(const Statement& rStatement):
Statement(rStatement), Statement(rStatement),
_currentRow(0), _currentRow(0),
_pBegin(new RowIterator(this)), _pBegin(new RowIterator(this)),
_pEnd(new RowIterator(this, true)) _pEnd(new RowIterator(this, true)),
_pFilter(0)
{ {
} }
@ -65,7 +67,8 @@ RecordSet::RecordSet(Session& rSession,
Statement((rSession << query, now)), Statement((rSession << query, now)),
_currentRow(0), _currentRow(0),
_pBegin(new RowIterator(this)), _pBegin(new RowIterator(this)),
_pEnd(new RowIterator(this, true)) _pEnd(new RowIterator(this, true)),
_pFilter(0)
{ {
if (pRowFormatter) setRowFormatter(pRowFormatter); if (pRowFormatter) setRowFormatter(pRowFormatter);
} }
@ -75,7 +78,8 @@ RecordSet::RecordSet(const RecordSet& other):
Statement(other.impl().duplicate()), Statement(other.impl().duplicate()),
_currentRow(other._currentRow), _currentRow(other._currentRow),
_pBegin(new RowIterator(this)), _pBegin(new RowIterator(this)),
_pEnd(new RowIterator(this, true)) _pEnd(new RowIterator(this, true)),
_pFilter(other._pFilter)
{ {
} }
@ -84,34 +88,38 @@ RecordSet::~RecordSet()
{ {
delete _pBegin; delete _pBegin;
delete _pEnd; delete _pEnd;
if(_pFilter) _pFilter->release();
RowMap::iterator it = _rowMap.begin(); RowMap::iterator it = _rowMap.begin();
RowMap::iterator end = _rowMap.end(); RowMap::iterator end = _rowMap.end();
for (; it != end; ++it) delete it->second; for (; it != end; ++it) delete it->second;
} }
DynamicAny RecordSet::value(std::size_t col, std::size_t row) const DynamicAny RecordSet::value(std::size_t col, std::size_t row, bool useFilter) const
{ {
if (useFilter && isFiltered() && !isAllowed(row))
throw InvalidAccessException("Row not allowed");
if (isNull(col, row)) return DynamicAny(); if (isNull(col, row)) return DynamicAny();
switch (columnType(col)) switch (columnType(col))
{ {
case MetaColumn::FDT_BOOL: return value<bool>(col, row); case MetaColumn::FDT_BOOL: return value<bool>(col, row, useFilter);
case MetaColumn::FDT_INT8: return value<Int8>(col, row); case MetaColumn::FDT_INT8: return value<Int8>(col, row, useFilter);
case MetaColumn::FDT_UINT8: return value<UInt8>(col, row); case MetaColumn::FDT_UINT8: return value<UInt8>(col, row, useFilter);
case MetaColumn::FDT_INT16: return value<Int16>(col, row); case MetaColumn::FDT_INT16: return value<Int16>(col, row, useFilter);
case MetaColumn::FDT_UINT16: return value<UInt16>(col, row); case MetaColumn::FDT_UINT16: return value<UInt16>(col, row, useFilter);
case MetaColumn::FDT_INT32: return value<Int32>(col, row); case MetaColumn::FDT_INT32: return value<Int32>(col, row, useFilter);
case MetaColumn::FDT_UINT32: return value<UInt32>(col, row); case MetaColumn::FDT_UINT32: return value<UInt32>(col, row, useFilter);
case MetaColumn::FDT_INT64: return value<Int64>(col, row); case MetaColumn::FDT_INT64: return value<Int64>(col, row, useFilter);
case MetaColumn::FDT_UINT64: return value<UInt64>(col, row); case MetaColumn::FDT_UINT64: return value<UInt64>(col, row, useFilter);
case MetaColumn::FDT_FLOAT: return value<float>(col, row); case MetaColumn::FDT_FLOAT: return value<float>(col, row, useFilter);
case MetaColumn::FDT_DOUBLE: return value<double>(col, row); case MetaColumn::FDT_DOUBLE: return value<double>(col, row, useFilter);
case MetaColumn::FDT_STRING: return value<std::string>(col, row); case MetaColumn::FDT_STRING: return value<std::string>(col, row, useFilter);
case MetaColumn::FDT_BLOB: return value<BLOB>(col, row); case MetaColumn::FDT_BLOB: return value<BLOB>(col, row, useFilter);
case MetaColumn::FDT_DATE: return value<Date>(col, row); case MetaColumn::FDT_DATE: return value<Date>(col, row, useFilter);
case MetaColumn::FDT_TIME: return value<Time>(col, row); case MetaColumn::FDT_TIME: return value<Time>(col, row, useFilter);
case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(col, row); case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(col, row);
default: default:
throw UnknownTypeException("Data type not supported."); throw UnknownTypeException("Data type not supported.");
@ -119,28 +127,31 @@ DynamicAny RecordSet::value(std::size_t col, std::size_t row) const
} }
DynamicAny RecordSet::value(const std::string& name, std::size_t row) const DynamicAny RecordSet::value(const std::string& name, std::size_t row, bool useFilter) const
{ {
if (useFilter && isFiltered() && !isAllowed(row))
throw InvalidAccessException("Row not allowed");
if (isNull(metaColumn(name).position(), row)) return DynamicAny(); if (isNull(metaColumn(name).position(), row)) return DynamicAny();
switch (columnType(name)) switch (columnType(name))
{ {
case MetaColumn::FDT_BOOL: return value<bool>(name, row); case MetaColumn::FDT_BOOL: return value<bool>(name, row, useFilter);
case MetaColumn::FDT_INT8: return value<Int8>(name, row); case MetaColumn::FDT_INT8: return value<Int8>(name, row, useFilter);
case MetaColumn::FDT_UINT8: return value<UInt8>(name, row); case MetaColumn::FDT_UINT8: return value<UInt8>(name, row, useFilter);
case MetaColumn::FDT_INT16: return value<Int16>(name, row); case MetaColumn::FDT_INT16: return value<Int16>(name, row, useFilter);
case MetaColumn::FDT_UINT16: return value<UInt16>(name, row); case MetaColumn::FDT_UINT16: return value<UInt16>(name, row, useFilter);
case MetaColumn::FDT_INT32: return value<Int32>(name, row); case MetaColumn::FDT_INT32: return value<Int32>(name, row, useFilter);
case MetaColumn::FDT_UINT32: return value<UInt32>(name, row); case MetaColumn::FDT_UINT32: return value<UInt32>(name, row, useFilter);
case MetaColumn::FDT_INT64: return value<Int64>(name, row); case MetaColumn::FDT_INT64: return value<Int64>(name, row, useFilter);
case MetaColumn::FDT_UINT64: return value<UInt64>(name, row); case MetaColumn::FDT_UINT64: return value<UInt64>(name, row, useFilter);
case MetaColumn::FDT_FLOAT: return value<float>(name, row); case MetaColumn::FDT_FLOAT: return value<float>(name, row, useFilter);
case MetaColumn::FDT_DOUBLE: return value<double>(name, row); case MetaColumn::FDT_DOUBLE: return value<double>(name, row, useFilter);
case MetaColumn::FDT_STRING: return value<std::string>(name, row); case MetaColumn::FDT_STRING: return value<std::string>(name, row, useFilter);
case MetaColumn::FDT_BLOB: return value<BLOB>(name, row); case MetaColumn::FDT_BLOB: return value<BLOB>(name, row, useFilter);
case MetaColumn::FDT_DATE: return value<Date>(name, row); case MetaColumn::FDT_DATE: return value<Date>(name, row, useFilter);
case MetaColumn::FDT_TIME: return value<Time>(name, row); case MetaColumn::FDT_TIME: return value<Time>(name, row, useFilter);
case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(name, row); case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(name, row, useFilter);
default: default:
throw UnknownTypeException("Data type not supported."); throw UnknownTypeException("Data type not supported.");
} }
@ -183,11 +194,48 @@ Row& RecordSet::row(std::size_t pos)
} }
std::size_t RecordSet::rowCount() const
{
poco_assert (extractions().size());
std::size_t rc = totalRowCount();
if (!isFiltered()) return rc;
std::size_t counter = 0;
for (int row = 0; row < rc; ++row)
{
if (isAllowed(row)) ++counter;
}
return counter;
}
bool RecordSet::isAllowed(std::size_t row) const
{
if (!isFiltered()) return true;
return _pFilter->isAllowed(row);
}
bool RecordSet::moveFirst() bool RecordSet::moveFirst()
{ {
if (rowCount() > 0) if (totalRowCount() > 0)
{ {
_currentRow = 0; if (!isFiltered())
{
_currentRow = 0;
return true;
}
std::size_t currentRow = _currentRow;
currentRow = 0;
while (!isAllowed(currentRow))
{
if (currentRow >= totalRowCount() - 1) return false;
++currentRow;
}
_currentRow = currentRow;
return true; return true;
} }
else return false; else return false;
@ -196,49 +244,57 @@ bool RecordSet::moveFirst()
bool RecordSet::moveNext() bool RecordSet::moveNext()
{ {
if (_currentRow >= rowCount() - 1) return false; std::size_t currentRow = _currentRow;
++_currentRow; do
{
if (currentRow >= totalRowCount() - 1) return false;
++currentRow;
} while (isFiltered() && !isAllowed(currentRow));
_currentRow = currentRow;
return true; return true;
} }
bool RecordSet::movePrevious() bool RecordSet::movePrevious()
{ {
if (0 == _currentRow) return false; std::size_t currentRow = _currentRow;
--_currentRow; do
{
if (currentRow <= 0) return false;
--currentRow;
} while (isFiltered() && !isAllowed(currentRow));
_currentRow = currentRow;
return true; return true;
} }
bool RecordSet::moveLast() bool RecordSet::moveLast()
{ {
if (rowCount() > 0) if (totalRowCount() > 0)
{ {
_currentRow = rowCount() - 1; std::size_t currentRow = _currentRow;
currentRow = totalRowCount() - 1;
if (!isFiltered())
{
_currentRow = currentRow;
return true;
}
while (!isAllowed(currentRow))
{
if (currentRow <= 0) return false;
--currentRow;
}
_currentRow = currentRow;
return true; return true;
} }
else return false; else return false;
} }
DynamicAny RecordSet::nvl(const std::string& name, const DynamicAny& deflt) const
{
if (isNull(name))
return deflt;
else
return value(name, _currentRow);
}
DynamicAny RecordSet::nvl(std::size_t index, const DynamicAny& deflt) const
{
if (isNull(index, _currentRow))
return deflt;
else
return value(index, _currentRow);
}
std::ostream& RecordSet::copyValues(std::ostream& os, std::size_t offset, std::size_t length) const std::ostream& RecordSet::copyValues(std::ostream& os, std::size_t offset, std::size_t length) const
{ {
if (length == RowIterator::POSITION_END) if (length == RowIterator::POSITION_END)
@ -265,4 +321,18 @@ std::ostream& RecordSet::copy(std::ostream& os) const
} }
void RecordSet::filter(RowFilter* pFilter)
{
if (_pFilter) _pFilter->release();
_pFilter = pFilter;
if(_pFilter) _pFilter->duplicate();
}
bool RecordSet::isFiltered() const
{
return _pFilter && !_pFilter->isEmpty();
}
} } // namespace Poco::Data } } // namespace Poco::Data

230
Data/src/RowFilter.cpp Normal file
View File

@ -0,0 +1,230 @@
//
// RowFilter.cpp
//
// $Id: //poco/Main/Data/src/RowFilter.cpp#1 $
//
// Library: Data
// Package: DataCore
// Module: RowFilter
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
#include "Poco/Data/RowFilter.h"
#include "Poco/Data/RecordSet.h"
#include "Poco/String.h"
#include "Poco/Exception.h"
#include <functional>
namespace Poco {
namespace Data {
RowFilter::RowFilter(RecordSet* pRecordSet): _pRecordSet(pRecordSet), _not(false)
{
poco_check_ptr(pRecordSet);
init();
_pRecordSet->filter(this);
}
RowFilter::RowFilter(Ptr pParent, LogicOperator op): _pRecordSet(0),
_pParent(pParent),
_not(false)
{
poco_check_ptr(_pParent.get());
init();
_pParent->addFilter(this, op);
}
void RowFilter::init()
{
_comparisons.insert(Comparisons::value_type("<", VALUE_LESS_THAN));
_comparisons.insert(Comparisons::value_type("<=", VALUE_LESS_THAN_OR_EQUAL));
_comparisons.insert(Comparisons::value_type("=", VALUE_EQUAL));
_comparisons.insert(Comparisons::value_type("==", VALUE_EQUAL));
_comparisons.insert(Comparisons::value_type(">", VALUE_GREATER_THAN));
_comparisons.insert(Comparisons::value_type(">=", VALUE_GREATER_THAN_OR_EQUAL));
_comparisons.insert(Comparisons::value_type("<>", VALUE_NOT_EQUAL));
_comparisons.insert(Comparisons::value_type("!=", VALUE_NOT_EQUAL));
_comparisons.insert(Comparisons::value_type("IS NULL", VALUE_IS_NULL));
duplicate();
}
RowFilter::~RowFilter()
{
release();
if (_pRecordSet) _pRecordSet->filter(0);
if (_pParent.get()) _pParent->removeFilter(this);
}
bool RowFilter::isAllowed(std::size_t row) const
{
DynamicAny retVal;
const RecordSet& rs = recordSet();
std::size_t columns = rs.columnCount();
ComparisonMap::const_iterator it = _comparisonMap.begin();
ComparisonMap::const_iterator end = _comparisonMap.end();
for (; it != end; ++it)
{
for (std::size_t col = 0; col < columns; ++col)
{
const std::string name = toUpper(rs.metaColumn(static_cast<UInt32>(col)).name());
if (_comparisonMap.find(name) == _comparisonMap.end()) continue;
DynamicAny ret;
CompT compOp = 0;
DynamicAny val = rs.value(col, row, false);
switch (it->second.get<1>())
{
case VALUE_LESS_THAN:
compOp = less; break;
case VALUE_LESS_THAN_OR_EQUAL:
compOp = lessOrEqual; break;
case VALUE_EQUAL:
compOp = equal; break;
case VALUE_GREATER_THAN:
compOp = greater; break;
case VALUE_GREATER_THAN_OR_EQUAL:
compOp = greaterOrEqual; break;
case VALUE_NOT_EQUAL:
compOp = notEqual; break;
case VALUE_IS_NULL:
compOp = isNull; break;
default:
throw IllegalStateException("Unsupported comparison criteria.");
}
doCompare(ret, val, compOp, it->second);
if (retVal.isEmpty()) retVal = ret;
else retVal = retVal || ret;
}
}
// iterate through children
FilterMap::const_iterator fIt = _filterMap.begin();
FilterMap::const_iterator fEnd = _filterMap.end();
for (; fIt != fEnd; ++fIt)
{
if (OP_OR == fIt->second)
{
if (retVal.isEmpty())
retVal = fIt->first->isAllowed(row);
else
retVal = retVal || fIt->first->isAllowed(row);
}
else if (OP_AND == fIt->second)
{
if (retVal.isEmpty())
retVal = fIt->first->isAllowed(row);
else
retVal = retVal && fIt->first->isAllowed(row);
}
else
throw IllegalStateException("Unknown logical operation.");
}
if (retVal.isEmpty()) retVal = true; // no filtering found
return (!_not) && retVal.extract<bool>();
}
int RowFilter::remove(const std::string& name)
{
poco_check_ptr (_pRecordSet);
_pRecordSet->moveFirst();
return static_cast<int>(_comparisonMap.erase(toUpper(name)));
}
RowFilter::Comparison RowFilter::getComparison(const std::string& comp) const
{
Comparisons::const_iterator it = _comparisons.find(toUpper(comp));
if (it == _comparisons.end())
throw NotFoundException("Comparison not found", comp);
return it->second;
}
void RowFilter::addFilter(const Ptr& pFilter, LogicOperator comparison)
{
poco_check_ptr (_pRecordSet);
pFilter->_pRecordSet = _pRecordSet;
_pRecordSet->moveFirst();
_filterMap.insert(FilterMap::value_type(pFilter, comparison));
}
void RowFilter::removeFilter(const Ptr& pFilter)
{
poco_check_ptr (_pRecordSet);
pFilter->_pRecordSet = 0;
_pRecordSet->moveFirst();
_filterMap.erase(pFilter);
}
void RowFilter::doCompare(DynamicAny& ret,
DynamicAny& val,
CompT comp,
const ComparisonEntry& ce)
{
if (ret.isEmpty()) ret = comp(val, ce.get<0>());
else
{
if (ce.get<2>() == OP_OR)
ret = ret || comp(val, ce.get<0>());
else if (ce.get<2>() == OP_AND)
ret = ret && comp(val, ce.get<0>());
else
throw IllegalStateException("Unknown logical operation.");
}
}
RecordSet& RowFilter::recordSet() const
{
if (!_pRecordSet)
{
Ptr pParent = _pParent;
while (pParent && !_pRecordSet)
_pRecordSet = pParent->_pRecordSet;
}
poco_check_ptr (_pRecordSet);
return *_pRecordSet;
}
} } // namespace Poco::Data

View File

@ -50,7 +50,7 @@ const int RowIterator::POSITION_END = std::numeric_limits<std::size_t>::max();
RowIterator::RowIterator(RecordSet* pRecordSet, bool positionEnd): RowIterator::RowIterator(RecordSet* pRecordSet, bool positionEnd):
_pRecordSet(pRecordSet), _pRecordSet(pRecordSet),
_position((0 == pRecordSet->rowCount()) || positionEnd ? POSITION_END : 0) _position((0 == pRecordSet->totalRowCount()) || positionEnd ? POSITION_END : 0)
{ {
} }
@ -89,10 +89,19 @@ void RowIterator::increment() const
if (POSITION_END == _position) if (POSITION_END == _position)
throw RangeException("End of iterator reached."); throw RangeException("End of iterator reached.");
if (_position < _pRecordSet->rowCount() - 1) if (_position < _pRecordSet->totalRowCount() - 1)
++_position; ++_position;
else else
_position = POSITION_END; _position = POSITION_END;
if (_pRecordSet->getFilter() && POSITION_END != _position)
{
while (!_pRecordSet->isAllowed(_position))
{
increment();
if (POSITION_END == _position) break;
}
}
} }
@ -101,17 +110,52 @@ void RowIterator::decrement() const
if (0 == _position) if (0 == _position)
throw RangeException("Beginning of iterator reached."); throw RangeException("Beginning of iterator reached.");
else if (POSITION_END == _position) else if (POSITION_END == _position)
_position = _pRecordSet->rowCount() - 1; _position = _pRecordSet->totalRowCount() - 1;
else else
--_position; --_position;
if (_pRecordSet->getFilter() && 0 != _position)
{
while (!_pRecordSet->isAllowed(_position))
{
decrement();
if (0 == _position) break;
}
}
} }
void RowIterator::setPosition(std::size_t pos) const void RowIterator::setPosition(std::size_t pos) const
{ {
if (pos < _pRecordSet->rowCount()) if (_position == pos) return;
if (_pRecordSet->getFilter())
{
std::size_t start = _position;
if (_position > pos)
{
std::size_t end = _position - pos;
for (; start > end; --start)
{
if (pos) --pos;
else throw RangeException("Invalid position argument.");
}
}
else
{
std::size_t end = pos - _position;
for (; start < end; ++start)
{
if (_pRecordSet->totalRowCount() != pos) ++pos;
else throw RangeException("Invalid position argument.");
}
}
}
if (pos < _pRecordSet->totalRowCount())
_position = pos; _position = pos;
else if (pos == _pRecordSet->rowCount()) else if (pos == _pRecordSet->totalRowCount())
_position = POSITION_END; _position = POSITION_END;
else else
throw RangeException("Invalid position argument."); throw RangeException("Invalid position argument.");