mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-13 10:32:57 +01:00
SF [2047672] RecordSet Filtering
This commit is contained in:
parent
d6b81dca0f
commit
e409026fba
@ -333,6 +333,9 @@
|
||||
<File
|
||||
RelativePath=".\include\Poco\Data\Row.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\Poco\Data\RowFilter.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\Poco\Data\RowFormatter.h">
|
||||
</File>
|
||||
@ -424,6 +427,9 @@
|
||||
<File
|
||||
RelativePath=".\src\Row.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\RowFilter.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\RowFormatter.cpp">
|
||||
</File>
|
||||
|
@ -461,6 +461,10 @@
|
||||
RelativePath=".\include\Poco\Data\Row.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\Poco\Data\RowFilter.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\Poco\Data\RowFormatter.h"
|
||||
>
|
||||
@ -585,6 +589,10 @@
|
||||
RelativePath=".\src\Row.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\RowFilter.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\RowFormatter.cpp"
|
||||
>
|
||||
|
@ -466,6 +466,10 @@
|
||||
RelativePath=".\include\Poco\Data\Row.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\Poco\Data\RowFilter.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\Poco\Data\RowFormatter.h"
|
||||
>
|
||||
@ -590,6 +594,10 @@
|
||||
RelativePath=".\src\Row.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\RowFilter.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\RowFormatter.cpp"
|
||||
>
|
||||
|
@ -8,11 +8,11 @@
|
||||
|
||||
include $(POCO_BASE)/build/rules/global
|
||||
|
||||
objects = AbstractBinder AbstractBinding AbstractExtraction \
|
||||
AbstractExtractor AbstractPreparation AbstractPrepare ArchiveStrategy \
|
||||
objects = AbstractBinder AbstractBinding AbstractExtraction AbstractExtractor \
|
||||
AbstractPreparation AbstractPrepare ArchiveStrategy AutoTransaction \
|
||||
Bulk Connector BLOB BLOBStream DataException Date Limit MetaColumn \
|
||||
PooledSessionHolder PooledSessionImpl Position \
|
||||
Range RecordSet Row RowFormatter RowIterator \
|
||||
Range RecordSet Row RowFilter RowFormatter RowIterator \
|
||||
SimpleRowFormatter Session SessionFactory SessionImpl \
|
||||
SessionPool SQLChannel Statement StatementCreator StatementImpl Time
|
||||
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "Poco/UnicodeConverter.h"
|
||||
#include "Poco/Buffer.h"
|
||||
#include "Poco/Exception.h"
|
||||
#include <cstring>
|
||||
#ifdef POCO_OS_FAMILY_WINDOWS
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
@ -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()
|
||||
{
|
||||
if (!_pSession) fail ("Test not available.");
|
||||
@ -646,6 +661,7 @@ CppUnit::Test* ODBCDB2Test::suite()
|
||||
CppUnit_addTest(pSuite, ODBCDB2Test, testTuple);
|
||||
CppUnit_addTest(pSuite, ODBCDB2Test, testTupleVector);
|
||||
CppUnit_addTest(pSuite, ODBCDB2Test, testInternalExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCDB2Test, testFilter);
|
||||
CppUnit_addTest(pSuite, ODBCDB2Test, testInternalBulkExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCDB2Test, testInternalStorageType);
|
||||
CppUnit_addTest(pSuite, ODBCDB2Test, testStoredProcedure);
|
||||
|
@ -55,6 +55,7 @@ public:
|
||||
void testBareboneODBC();
|
||||
|
||||
void testBLOB();
|
||||
void testFilter();
|
||||
|
||||
void testStoredProcedure();
|
||||
void testStoredProcedureAny();
|
||||
|
@ -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)
|
||||
{
|
||||
*_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, testStoredFunction);
|
||||
CppUnit_addTest(pSuite, ODBCMySQLTest, testInternalExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCMySQLTest, testFilter);
|
||||
//CppUnit_addTest(pSuite, ODBCOracleTest, testInternalBulkExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCMySQLTest, testInternalStorageType);
|
||||
CppUnit_addTest(pSuite, ODBCMySQLTest, testNull);
|
||||
|
@ -65,6 +65,7 @@ public:
|
||||
void testNull();
|
||||
|
||||
void testMultipleResults();
|
||||
void testFilter();
|
||||
|
||||
static CppUnit::Test* suite();
|
||||
|
||||
|
@ -905,6 +905,7 @@ CppUnit::Test* ODBCOracleTest::suite()
|
||||
CppUnit_addTest(pSuite, ODBCOracleTest, testStoredFunction);
|
||||
CppUnit_addTest(pSuite, ODBCOracleTest, testCursorStoredFunction);
|
||||
CppUnit_addTest(pSuite, ODBCOracleTest, testInternalExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCOracleTest, testFilter);
|
||||
CppUnit_addTest(pSuite, ODBCOracleTest, testInternalBulkExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCOracleTest, testInternalStorageType);
|
||||
CppUnit_addTest(pSuite, ODBCOracleTest, testNull);
|
||||
|
@ -629,6 +629,7 @@ CppUnit::Test* ODBCPostgreSQLTest::suite()
|
||||
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTuple);
|
||||
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTupleVector);
|
||||
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testInternalExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testFilter);
|
||||
//On Linux, PostgreSQL driver returns SQL_NEED_DATA on SQLExecute (see ODBCStatementImpl::bindImpl() )
|
||||
//this behavior is not expected and not handled for automatic binding
|
||||
#ifdef POCO_OS_FAMILY_WINDOWS
|
||||
|
@ -757,6 +757,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
|
||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredProcedureDynamicAny);
|
||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testStoredFunction);
|
||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testFilter);
|
||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalBulkExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testInternalStorageType);
|
||||
CppUnit_addTest(pSuite, ODBCSQLServerTest, testNull);
|
||||
|
@ -382,6 +382,7 @@ CppUnit::Test* ODBCSQLiteTest::suite()
|
||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testTuple);
|
||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testTupleVector);
|
||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testInternalExtraction);
|
||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testFilter);
|
||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testInternalStorageType);
|
||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testNull);
|
||||
CppUnit_addTest(pSuite, ODBCSQLiteTest, testRowIterator);
|
||||
|
@ -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()
|
||||
{
|
||||
if (!_pSession) fail ("Test not available.");
|
||||
|
@ -135,6 +135,7 @@ public:
|
||||
virtual void testTupleVector();
|
||||
|
||||
virtual void testInternalExtraction();
|
||||
virtual void testFilter();
|
||||
virtual void testInternalBulkExtraction();
|
||||
virtual void testInternalStorageType();
|
||||
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "Poco/Data/StatementImpl.h"
|
||||
#include "Poco/Data/RecordSet.h"
|
||||
#include "Poco/Data/RowIterator.h"
|
||||
#include "Poco/Data/RowFilter.h"
|
||||
#include "Poco/Data/BulkExtraction.h"
|
||||
#include "Poco/Data/BulkBinding.h"
|
||||
#include "Poco/Data/SQLChannel.h"
|
||||
@ -72,6 +73,8 @@ using Poco::Data::Statement;
|
||||
using Poco::Data::RecordSet;
|
||||
using Poco::Data::Column;
|
||||
using Poco::Data::Row;
|
||||
using Poco::Data::RowFilter;
|
||||
using Poco::Data::RowIterator;
|
||||
using Poco::Data::SQLChannel;
|
||||
using Poco::Data::LimitException;
|
||||
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()
|
||||
{
|
||||
std::string funct = "internalBulkExtraction()";
|
||||
@ -2665,7 +2748,8 @@ void SQLExecutor::notNulls(const std::string& sqlState)
|
||||
{
|
||||
//make sure we're failing for the right reason
|
||||
//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("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; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
|
||||
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
|
||||
|
||||
int i = 1;
|
||||
double f = 1.2;
|
||||
double f = 1.5;
|
||||
std::string s = "123";
|
||||
|
||||
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("r"));
|
||||
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; }
|
||||
catch(ConnectionException& ce){ std::cout << ce.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::copy(rset.begin(), rset.end(), std::ostream_iterator<Row>(osCopy));
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -484,6 +484,10 @@ public:
|
||||
void tupleVector();
|
||||
|
||||
void internalExtraction();
|
||||
void filter(const std::string& query =
|
||||
"SELECT * FROM Vectors ORDER BY int0 ASC",
|
||||
const std::string& intFldName = "int0");
|
||||
|
||||
void internalBulkExtraction();
|
||||
void internalStorageType();
|
||||
void nulls();
|
||||
|
@ -56,7 +56,7 @@ namespace Poco {
|
||||
namespace Data {
|
||||
|
||||
|
||||
class Session;
|
||||
class RowFilter;
|
||||
|
||||
|
||||
class Data_API RecordSet: private Statement
|
||||
@ -112,7 +112,16 @@ public:
|
||||
/// Assignment operator.
|
||||
|
||||
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;
|
||||
/// Returns the number of rows in the recordset.
|
||||
@ -154,9 +163,12 @@ public:
|
||||
/// Rows are lazy-created and cached.
|
||||
|
||||
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.
|
||||
{
|
||||
if (useFilter && isFiltered() && !isAllowed(row))
|
||||
throw InvalidAccessException("Row not allowed");
|
||||
|
||||
switch (storage())
|
||||
{
|
||||
case STORAGE_VECTOR:
|
||||
@ -181,9 +193,12 @@ public:
|
||||
}
|
||||
|
||||
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.
|
||||
{
|
||||
if (useFilter && isFiltered() && !isAllowed(row))
|
||||
throw InvalidAccessException("Row not allowed");
|
||||
|
||||
switch (storage())
|
||||
{
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
/// 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
|
||||
/// if the value is not NULL, or deflt otherwise.
|
||||
{
|
||||
if (isNull(index, _currentRow))
|
||||
return DynamicAny(deflt);
|
||||
else
|
||||
return value(index, _currentRow);
|
||||
}
|
||||
|
||||
ConstIterator& begin() const;
|
||||
/// Returns the const row iterator.
|
||||
@ -315,6 +344,9 @@ public:
|
||||
/// Copies the column names and values to the target output stream.
|
||||
/// Copied strings are formatted by the current RowFormatter.
|
||||
|
||||
bool isFiltered() const;
|
||||
/// Returns true if recordset is filtered.
|
||||
|
||||
private:
|
||||
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;
|
||||
RowIterator* _pBegin;
|
||||
RowIterator* _pEnd;
|
||||
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)
|
||||
{
|
||||
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());
|
||||
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
|
||||
|
||||
|
||||
|
285
Data/include/Poco/Data/RowFilter.h
Normal file
285
Data/include/Poco/Data/RowFilter.h
Normal 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
|
@ -792,7 +792,6 @@ inline void swap(Statement& s1, Statement& s2)
|
||||
}
|
||||
|
||||
|
||||
|
||||
} } // namespace Poco::Data
|
||||
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include "Poco/Data/RecordSet.h"
|
||||
#include "Poco/Data/Session.h"
|
||||
#include "Poco/Data/RowFilter.h"
|
||||
#include "Poco/Data/Date.h"
|
||||
#include "Poco/Data/Time.h"
|
||||
#include "Poco/Data/DataException.h"
|
||||
@ -54,7 +55,8 @@ RecordSet::RecordSet(const Statement& rStatement):
|
||||
Statement(rStatement),
|
||||
_currentRow(0),
|
||||
_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)),
|
||||
_currentRow(0),
|
||||
_pBegin(new RowIterator(this)),
|
||||
_pEnd(new RowIterator(this, true))
|
||||
_pEnd(new RowIterator(this, true)),
|
||||
_pFilter(0)
|
||||
{
|
||||
if (pRowFormatter) setRowFormatter(pRowFormatter);
|
||||
}
|
||||
@ -75,7 +78,8 @@ RecordSet::RecordSet(const RecordSet& other):
|
||||
Statement(other.impl().duplicate()),
|
||||
_currentRow(other._currentRow),
|
||||
_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 _pEnd;
|
||||
if(_pFilter) _pFilter->release();
|
||||
|
||||
RowMap::iterator it = _rowMap.begin();
|
||||
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();
|
||||
|
||||
switch (columnType(col))
|
||||
{
|
||||
case MetaColumn::FDT_BOOL: return value<bool>(col, row);
|
||||
case MetaColumn::FDT_INT8: return value<Int8>(col, row);
|
||||
case MetaColumn::FDT_UINT8: return value<UInt8>(col, row);
|
||||
case MetaColumn::FDT_INT16: return value<Int16>(col, row);
|
||||
case MetaColumn::FDT_UINT16: return value<UInt16>(col, row);
|
||||
case MetaColumn::FDT_INT32: return value<Int32>(col, row);
|
||||
case MetaColumn::FDT_UINT32: return value<UInt32>(col, row);
|
||||
case MetaColumn::FDT_INT64: return value<Int64>(col, row);
|
||||
case MetaColumn::FDT_UINT64: return value<UInt64>(col, row);
|
||||
case MetaColumn::FDT_FLOAT: return value<float>(col, row);
|
||||
case MetaColumn::FDT_DOUBLE: return value<double>(col, row);
|
||||
case MetaColumn::FDT_STRING: return value<std::string>(col, row);
|
||||
case MetaColumn::FDT_BLOB: return value<BLOB>(col, row);
|
||||
case MetaColumn::FDT_DATE: return value<Date>(col, row);
|
||||
case MetaColumn::FDT_TIME: return value<Time>(col, row);
|
||||
case MetaColumn::FDT_BOOL: return value<bool>(col, row, useFilter);
|
||||
case MetaColumn::FDT_INT8: return value<Int8>(col, row, useFilter);
|
||||
case MetaColumn::FDT_UINT8: return value<UInt8>(col, row, useFilter);
|
||||
case MetaColumn::FDT_INT16: return value<Int16>(col, row, useFilter);
|
||||
case MetaColumn::FDT_UINT16: return value<UInt16>(col, row, useFilter);
|
||||
case MetaColumn::FDT_INT32: return value<Int32>(col, row, useFilter);
|
||||
case MetaColumn::FDT_UINT32: return value<UInt32>(col, row, useFilter);
|
||||
case MetaColumn::FDT_INT64: return value<Int64>(col, row, useFilter);
|
||||
case MetaColumn::FDT_UINT64: return value<UInt64>(col, row, useFilter);
|
||||
case MetaColumn::FDT_FLOAT: return value<float>(col, row, useFilter);
|
||||
case MetaColumn::FDT_DOUBLE: return value<double>(col, row, useFilter);
|
||||
case MetaColumn::FDT_STRING: return value<std::string>(col, row, useFilter);
|
||||
case MetaColumn::FDT_BLOB: return value<BLOB>(col, row, useFilter);
|
||||
case MetaColumn::FDT_DATE: return value<Date>(col, row, useFilter);
|
||||
case MetaColumn::FDT_TIME: return value<Time>(col, row, useFilter);
|
||||
case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(col, row);
|
||||
default:
|
||||
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();
|
||||
|
||||
switch (columnType(name))
|
||||
{
|
||||
case MetaColumn::FDT_BOOL: return value<bool>(name, row);
|
||||
case MetaColumn::FDT_INT8: return value<Int8>(name, row);
|
||||
case MetaColumn::FDT_UINT8: return value<UInt8>(name, row);
|
||||
case MetaColumn::FDT_INT16: return value<Int16>(name, row);
|
||||
case MetaColumn::FDT_UINT16: return value<UInt16>(name, row);
|
||||
case MetaColumn::FDT_INT32: return value<Int32>(name, row);
|
||||
case MetaColumn::FDT_UINT32: return value<UInt32>(name, row);
|
||||
case MetaColumn::FDT_INT64: return value<Int64>(name, row);
|
||||
case MetaColumn::FDT_UINT64: return value<UInt64>(name, row);
|
||||
case MetaColumn::FDT_FLOAT: return value<float>(name, row);
|
||||
case MetaColumn::FDT_DOUBLE: return value<double>(name, row);
|
||||
case MetaColumn::FDT_STRING: return value<std::string>(name, row);
|
||||
case MetaColumn::FDT_BLOB: return value<BLOB>(name, row);
|
||||
case MetaColumn::FDT_DATE: return value<Date>(name, row);
|
||||
case MetaColumn::FDT_TIME: return value<Time>(name, row);
|
||||
case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(name, row);
|
||||
case MetaColumn::FDT_BOOL: return value<bool>(name, row, useFilter);
|
||||
case MetaColumn::FDT_INT8: return value<Int8>(name, row, useFilter);
|
||||
case MetaColumn::FDT_UINT8: return value<UInt8>(name, row, useFilter);
|
||||
case MetaColumn::FDT_INT16: return value<Int16>(name, row, useFilter);
|
||||
case MetaColumn::FDT_UINT16: return value<UInt16>(name, row, useFilter);
|
||||
case MetaColumn::FDT_INT32: return value<Int32>(name, row, useFilter);
|
||||
case MetaColumn::FDT_UINT32: return value<UInt32>(name, row, useFilter);
|
||||
case MetaColumn::FDT_INT64: return value<Int64>(name, row, useFilter);
|
||||
case MetaColumn::FDT_UINT64: return value<UInt64>(name, row, useFilter);
|
||||
case MetaColumn::FDT_FLOAT: return value<float>(name, row, useFilter);
|
||||
case MetaColumn::FDT_DOUBLE: return value<double>(name, row, useFilter);
|
||||
case MetaColumn::FDT_STRING: return value<std::string>(name, row, useFilter);
|
||||
case MetaColumn::FDT_BLOB: return value<BLOB>(name, row, useFilter);
|
||||
case MetaColumn::FDT_DATE: return value<Date>(name, row, useFilter);
|
||||
case MetaColumn::FDT_TIME: return value<Time>(name, row, useFilter);
|
||||
case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(name, row, useFilter);
|
||||
default:
|
||||
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()
|
||||
{
|
||||
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;
|
||||
}
|
||||
else return false;
|
||||
@ -196,49 +244,57 @@ bool RecordSet::moveFirst()
|
||||
|
||||
bool RecordSet::moveNext()
|
||||
{
|
||||
if (_currentRow >= rowCount() - 1) return false;
|
||||
++_currentRow;
|
||||
std::size_t currentRow = _currentRow;
|
||||
do
|
||||
{
|
||||
if (currentRow >= totalRowCount() - 1) return false;
|
||||
++currentRow;
|
||||
} while (isFiltered() && !isAllowed(currentRow));
|
||||
|
||||
_currentRow = currentRow;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool RecordSet::movePrevious()
|
||||
{
|
||||
if (0 == _currentRow) return false;
|
||||
--_currentRow;
|
||||
std::size_t currentRow = _currentRow;
|
||||
do
|
||||
{
|
||||
if (currentRow <= 0) return false;
|
||||
--currentRow;
|
||||
} while (isFiltered() && !isAllowed(currentRow));
|
||||
|
||||
_currentRow = currentRow;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
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
|
||||
{
|
||||
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
|
||||
|
230
Data/src/RowFilter.cpp
Normal file
230
Data/src/RowFilter.cpp
Normal 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
|
@ -50,7 +50,7 @@ const int RowIterator::POSITION_END = std::numeric_limits<std::size_t>::max();
|
||||
|
||||
RowIterator::RowIterator(RecordSet* pRecordSet, bool positionEnd):
|
||||
_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)
|
||||
throw RangeException("End of iterator reached.");
|
||||
|
||||
if (_position < _pRecordSet->rowCount() - 1)
|
||||
if (_position < _pRecordSet->totalRowCount() - 1)
|
||||
++_position;
|
||||
else
|
||||
_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)
|
||||
throw RangeException("Beginning of iterator reached.");
|
||||
else if (POSITION_END == _position)
|
||||
_position = _pRecordSet->rowCount() - 1;
|
||||
_position = _pRecordSet->totalRowCount() - 1;
|
||||
else
|
||||
--_position;
|
||||
|
||||
if (_pRecordSet->getFilter() && 0 != _position)
|
||||
{
|
||||
while (!_pRecordSet->isAllowed(_position))
|
||||
{
|
||||
decrement();
|
||||
if (0 == _position) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
else if (pos == _pRecordSet->rowCount())
|
||||
else if (pos == _pRecordSet->totalRowCount())
|
||||
_position = POSITION_END;
|
||||
else
|
||||
throw RangeException("Invalid position argument.");
|
||||
|
Loading…
Reference in New Issue
Block a user