From 6e380b6b13eb243375ddd2a5965770152075af22 Mon Sep 17 00:00:00 2001 From: Aleksandar Fabijanic Date: Sat, 29 Sep 2007 18:40:43 +0000 Subject: [PATCH] Various feature additions and fixes: - asynchronous execution for Data::Statement - ActiveMethod copy and assignment - added Data components to $POCO_BASE/components - SQLite 64-bit integer default - SessionPool timer seconds to milliseconds - ODBC fix for subsequent calls to execute() - std::deque (instead of std::vector) as default container --- Data/ODBC/src/Binder.cpp | 22 ++- Data/ODBC/src/ODBCStatementImpl.cpp | 1 + Data/ODBC/testsuite/src/ODBCDB2Test.cpp | 16 ++ Data/ODBC/testsuite/src/ODBCDB2Test.h | 2 + Data/ODBC/testsuite/src/ODBCMySQLTest.cpp | 17 ++ Data/ODBC/testsuite/src/ODBCMySQLTest.h | 2 + Data/ODBC/testsuite/src/ODBCOracleTest.cpp | 16 ++ Data/ODBC/testsuite/src/ODBCOracleTest.h | 2 + .../ODBC/testsuite/src/ODBCPostgreSQLTest.cpp | 16 ++ Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h | 2 + Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp | 16 ++ Data/ODBC/testsuite/src/ODBCSQLServerTest.h | 2 + Data/ODBC/testsuite/src/ODBCSQLiteTest.cpp | 16 ++ Data/ODBC/testsuite/src/ODBCSQLiteTest.h | 2 + Data/ODBC/testsuite/src/SQLExecutor.cpp | 45 ++++- Data/ODBC/testsuite/src/SQLExecutor.h | 2 + Data/SQLite/src/Utility.cpp | 2 +- Data/SQLite/testsuite/src/SQLiteTest.cpp | 101 ++++++---- Data/SQLite/testsuite/src/SQLiteTest.h | 2 +- Data/include/Poco/Data/AbstractSessionImpl.h | 4 +- Data/include/Poco/Data/Column.h | 2 +- Data/include/Poco/Data/Extraction.h | 2 +- Data/include/Poco/Data/RecordSet.h | 4 +- Data/include/Poco/Data/SessionPool.h | 6 +- Data/include/Poco/Data/Statement.h | 175 ++++++++++++++++-- Data/include/Poco/Data/StatementImpl.h | 20 +- Data/src/SessionPool.cpp | 2 +- Data/src/Statement.cpp | 85 +++++++-- Data/src/StatementImpl.cpp | 16 +- Data/testsuite/src/DataTest.cpp | 14 +- Foundation/include/Poco/ActiveMethod.h | 42 ++++- Foundation/testsuite/src/ActiveMethodTest.cpp | 54 +++++- Foundation/testsuite/src/ActiveMethodTest.h | 1 + components | 3 + 34 files changed, 598 insertions(+), 116 deletions(-) diff --git a/Data/ODBC/src/Binder.cpp b/Data/ODBC/src/Binder.cpp index e1ba39026..7ec926848 100644 --- a/Data/ODBC/src/Binder.cpp +++ b/Data/ODBC/src/Binder.cpp @@ -300,15 +300,21 @@ SQLSMALLINT Binder::toODBCDirection(Direction dir) const void Binder::synchronize() { - TimestampMap::iterator itTS = _timestamps.begin(); - TimestampMap::iterator itTSEnd = _timestamps.end(); - for(; itTS != itTSEnd; ++itTS) - Utility::dateTimeSync(*itTS->second, *itTS->first); + if (_timestamps.size()) + { + TimestampMap::iterator itTS = _timestamps.begin(); + TimestampMap::iterator itTSEnd = _timestamps.end(); + for(; itTS != itTSEnd; ++itTS) + Utility::dateTimeSync(*itTS->second, *itTS->first); + } - StringMap::iterator itStr = _strings.begin(); - StringMap::iterator itStrEnd = _strings.end(); - for(; itStr != itStrEnd; ++itStr) - itStr->second->assign(itStr->first, strlen(itStr->first)); + if (_strings.size()) + { + StringMap::iterator itStr = _strings.begin(); + StringMap::iterator itStrEnd = _strings.end(); + for(; itStr != itStrEnd; ++itStr) + itStr->second->assign(itStr->first, strlen(itStr->first)); + } } diff --git a/Data/ODBC/src/ODBCStatementImpl.cpp b/Data/ODBC/src/ODBCStatementImpl.cpp index 7e51c4f4d..c953a53d5 100644 --- a/Data/ODBC/src/ODBCStatementImpl.cpp +++ b/Data/ODBC/src/ODBCStatementImpl.cpp @@ -212,6 +212,7 @@ void ODBCStatementImpl::putData() void ODBCStatementImpl::clear() { SQLRETURN rc = SQLCloseCursor(_stmt); + _stepCalled = false; if (Utility::isError(rc)) { StatementError err(_stmt); diff --git a/Data/ODBC/testsuite/src/ODBCDB2Test.cpp b/Data/ODBC/testsuite/src/ODBCDB2Test.cpp index 4f86539e8..f9009230b 100644 --- a/Data/ODBC/testsuite/src/ODBCDB2Test.cpp +++ b/Data/ODBC/testsuite/src/ODBCDB2Test.cpp @@ -1067,6 +1067,21 @@ void ODBCDB2Test::testRowIterator() } +void ODBCDB2Test::testAsync() +{ + if (!_pSession) fail ("Test not available."); + + for (int i = 0; i < 8;) + { + recreateIntsTable(); + _pSession->setFeature("autoBind", bindValues[i]); + _pSession->setFeature("autoExtract", bindValues[i+1]); + _pExecutor->asynchronous(); + i += 2; + } +} + + void ODBCDB2Test::dropObject(const std::string& type, const std::string& name) { try @@ -1322,6 +1337,7 @@ CppUnit::Test* ODBCDB2Test::suite() CppUnit_addTest(pSuite, ODBCDB2Test, testStoredFunction); CppUnit_addTest(pSuite, ODBCDB2Test, testNull); CppUnit_addTest(pSuite, ODBCDB2Test, testRowIterator); + CppUnit_addTest(pSuite, ODBCDB2Test, testAsync); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCDB2Test.h b/Data/ODBC/testsuite/src/ODBCDB2Test.h index 1e6f2117b..fed542333 100644 --- a/Data/ODBC/testsuite/src/ODBCDB2Test.h +++ b/Data/ODBC/testsuite/src/ODBCDB2Test.h @@ -125,6 +125,8 @@ public: void testNull(); void testRowIterator(); + void testAsync(); + void setUp(); void tearDown(); diff --git a/Data/ODBC/testsuite/src/ODBCMySQLTest.cpp b/Data/ODBC/testsuite/src/ODBCMySQLTest.cpp index a7283fd32..84432249d 100644 --- a/Data/ODBC/testsuite/src/ODBCMySQLTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCMySQLTest.cpp @@ -931,6 +931,21 @@ void ODBCMySQLTest::testRowIterator() } +void ODBCMySQLTest::testAsync() +{ + if (!_pSession) fail ("Test not available."); + + for (int i = 0; i < 8;) + { + recreateIntsTable(); + _pSession->setFeature("autoBind", bindValues[i]); + _pSession->setFeature("autoExtract", bindValues[i+1]); + _pExecutor->asynchronous(); + i += 2; + } +} + + void ODBCMySQLTest::dropObject(const std::string& type, const std::string& name) { *_pSession << format("DROP %s IF EXISTS %s", type, name), now; @@ -1169,6 +1184,8 @@ CppUnit::Test* ODBCMySQLTest::suite() CppUnit_addTest(pSuite, ODBCMySQLTest, testInternalStorageType); CppUnit_addTest(pSuite, ODBCMySQLTest, testNull); CppUnit_addTest(pSuite, ODBCMySQLTest, testRowIterator); + CppUnit_addTest(pSuite, ODBCMySQLTest, testAsync); + return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCMySQLTest.h b/Data/ODBC/testsuite/src/ODBCMySQLTest.h index b96209a25..bc5d3d9b1 100644 --- a/Data/ODBC/testsuite/src/ODBCMySQLTest.h +++ b/Data/ODBC/testsuite/src/ODBCMySQLTest.h @@ -127,6 +127,8 @@ public: void testNull(); void testRowIterator(); + void testAsync(); + void setUp(); void tearDown(); diff --git a/Data/ODBC/testsuite/src/ODBCOracleTest.cpp b/Data/ODBC/testsuite/src/ODBCOracleTest.cpp index 9627be67a..b0c89e409 100644 --- a/Data/ODBC/testsuite/src/ODBCOracleTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCOracleTest.cpp @@ -1082,6 +1082,21 @@ void ODBCOracleTest::testRowIterator() } +void ODBCOracleTest::testAsync() +{ + if (!_pSession) fail ("Test not available."); + + for (int i = 0; i < 8;) + { + recreateIntsTable(); + _pSession->setFeature("autoBind", bindValues[i]); + _pSession->setFeature("autoExtract", bindValues[i+1]); + _pExecutor->asynchronous(); + i += 2; + } +} + + void ODBCOracleTest::dropObject(const std::string& type, const std::string& name) { try @@ -1369,6 +1384,7 @@ CppUnit::Test* ODBCOracleTest::suite() CppUnit_addTest(pSuite, ODBCOracleTest, testInternalStorageType); CppUnit_addTest(pSuite, ODBCOracleTest, testNull); CppUnit_addTest(pSuite, ODBCOracleTest, testRowIterator); + CppUnit_addTest(pSuite, ODBCOracleTest, testAsync); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCOracleTest.h b/Data/ODBC/testsuite/src/ODBCOracleTest.h index 9090c2e7a..d98df5e98 100644 --- a/Data/ODBC/testsuite/src/ODBCOracleTest.h +++ b/Data/ODBC/testsuite/src/ODBCOracleTest.h @@ -125,6 +125,8 @@ public: void testNull(); void testRowIterator(); + void testAsync(); + void setUp(); void tearDown(); diff --git a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp index 51a263867..f5328151d 100644 --- a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp @@ -994,6 +994,21 @@ void ODBCPostgreSQLTest::testStdVectorBool() } +void ODBCPostgreSQLTest::testAsync() +{ + if (!_pSession) fail ("Test not available."); + + for (int i = 0; i < 8;) + { + recreateIntsTable(); + _pSession->setFeature("autoBind", bindValues[i]); + _pSession->setFeature("autoExtract", bindValues[i+1]); + _pExecutor->asynchronous(); + i += 2; + } +} + + void ODBCPostgreSQLTest::configurePLPgSQL() { if (!_pSession) fail ("Test not available."); @@ -1317,6 +1332,7 @@ CppUnit::Test* ODBCPostgreSQLTest::suite() CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testNull); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testRowIterator); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testStdVectorBool); + CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testAsync); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h index 15a58bff8..b68fb984f 100644 --- a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h +++ b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h @@ -133,6 +133,8 @@ public: void testRowIterator(); void testStdVectorBool(); + void testAsync(); + void setUp(); void tearDown(); diff --git a/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp b/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp index 2aad2fde6..01656b1f8 100644 --- a/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp @@ -1085,6 +1085,21 @@ void ODBCSQLServerTest::testStdVectorBool() } +void ODBCSQLServerTest::testAsync() +{ + if (!_pSession) fail ("Test not available."); + + for (int i = 0; i < 8;) + { + recreateIntsTable(); + _pSession->setFeature("autoBind", bindValues[i]); + _pSession->setFeature("autoExtract", bindValues[i+1]); + _pExecutor->asynchronous(); + i += 2; + } +} + + void ODBCSQLServerTest::dropObject(const std::string& type, const std::string& name) { try @@ -1362,6 +1377,7 @@ CppUnit::Test* ODBCSQLServerTest::suite() CppUnit_addTest(pSuite, ODBCSQLServerTest, testNull); CppUnit_addTest(pSuite, ODBCSQLServerTest, testRowIterator); CppUnit_addTest(pSuite, ODBCSQLServerTest, testStdVectorBool); + CppUnit_addTest(pSuite, ODBCSQLServerTest, testAsync); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCSQLServerTest.h b/Data/ODBC/testsuite/src/ODBCSQLServerTest.h index a7546c5a9..cca98b008 100644 --- a/Data/ODBC/testsuite/src/ODBCSQLServerTest.h +++ b/Data/ODBC/testsuite/src/ODBCSQLServerTest.h @@ -129,6 +129,8 @@ public: void testRowIterator(); void testStdVectorBool(); + void testAsync(); + void setUp(); void tearDown(); diff --git a/Data/ODBC/testsuite/src/ODBCSQLiteTest.cpp b/Data/ODBC/testsuite/src/ODBCSQLiteTest.cpp index 97572176c..5ea1637e4 100644 --- a/Data/ODBC/testsuite/src/ODBCSQLiteTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCSQLiteTest.cpp @@ -906,6 +906,21 @@ void ODBCSQLiteTest::testRowIterator() } +void ODBCSQLiteTest::testAsync() +{ + if (!_pSession) fail ("Test not available."); + + for (int i = 0; i < 8;) + { + recreateIntsTable(); + _pSession->setFeature("autoBind", bindValues[i]); + _pSession->setFeature("autoExtract", bindValues[i+1]); + _pExecutor->asynchronous(); + i += 2; + } +} + + void ODBCSQLiteTest::dropObject(const std::string& type, const std::string& name) { try @@ -1153,6 +1168,7 @@ CppUnit::Test* ODBCSQLiteTest::suite() CppUnit_addTest(pSuite, ODBCSQLiteTest, testInternalStorageType); CppUnit_addTest(pSuite, ODBCSQLiteTest, testNull); CppUnit_addTest(pSuite, ODBCSQLiteTest, testRowIterator); + CppUnit_addTest(pSuite, ODBCSQLiteTest, testAsync); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCSQLiteTest.h b/Data/ODBC/testsuite/src/ODBCSQLiteTest.h index 9c6ace94d..5305a85fb 100644 --- a/Data/ODBC/testsuite/src/ODBCSQLiteTest.h +++ b/Data/ODBC/testsuite/src/ODBCSQLiteTest.h @@ -122,6 +122,8 @@ public: void testNull(); void testRowIterator(); + void testAsync(); + void setUp(); void tearDown(); diff --git a/Data/ODBC/testsuite/src/SQLExecutor.cpp b/Data/ODBC/testsuite/src/SQLExecutor.cpp index b2aeb00ba..c594b384f 100644 --- a/Data/ODBC/testsuite/src/SQLExecutor.cpp +++ b/Data/ODBC/testsuite/src/SQLExecutor.cpp @@ -1952,7 +1952,7 @@ void SQLExecutor::internalExtraction() i = rset.value("str0", 2); assert (5 == i); - const Column& col = rset.column >(0); + const Column& col = rset.column >(0); Column::Iterator it = col.begin(); Column::Iterator end = col.end(); for (int i = 1; it != end; ++it, ++i) @@ -2239,3 +2239,46 @@ void SQLExecutor::stdVectorBool() t += rset.value(0, i) ? 1 : 0; assert (2 == t); } + + +void SQLExecutor::asynchronous() +{ + Session tmp = *_pSession; + + int rowCount = 500; + std::vector data(rowCount); + Statement stmt = (tmp << "INSERT INTO Strings VALUES(?)", use(data)); + Statement::Result result = stmt.executeAsync(); + assert (!stmt.isAsync()); + result.wait(); + + stmt = tmp << "SELECT * FROM Strings", into(data), async, now; + assert (stmt.isAsync()); + stmt.wait(); + assert (stmt.execute() == 0); + + try { + result = stmt.executeAsync(); + fail ("must fail"); + } catch (InvalidAccessException&) + { + assert (stmt.isAsync()); + stmt.wait(); + result = stmt.executeAsync(); + } + + assert (stmt.wait() == rowCount); + assert (result.data() == rowCount); + stmt.setAsync(false); + assert (!stmt.isAsync()); + assert (stmt.execute() == rowCount); + + stmt = tmp << "SELECT * FROM Strings", into(data), sync, now; + assert (!stmt.isAsync()); + assert (stmt.wait() == 0); + assert (stmt.execute() == rowCount); + result = stmt.executeAsync(); + assert (!stmt.isAsync()); + result.wait(); + assert (result.data() == rowCount); +} diff --git a/Data/ODBC/testsuite/src/SQLExecutor.h b/Data/ODBC/testsuite/src/SQLExecutor.h index cf5de6f96..ff71647dd 100644 --- a/Data/ODBC/testsuite/src/SQLExecutor.h +++ b/Data/ODBC/testsuite/src/SQLExecutor.h @@ -134,6 +134,8 @@ public: void rowIterator(); void stdVectorBool(); + void asynchronous(); + private: Poco::Data::Session* _pSession; }; diff --git a/Data/SQLite/src/Utility.cpp b/Data/SQLite/src/Utility.cpp index 0670f552f..84f4b4491 100644 --- a/Data/SQLite/src/Utility.cpp +++ b/Data/SQLite/src/Utility.cpp @@ -64,7 +64,7 @@ MetaColumn::ColumnDataType Utility::getColumnType(sqlite3_stmt* pStmt, std::size Poco::toUpperInPlace(sqliteType); if (sqliteType.npos != sqliteType.find("INT")) - return MetaColumn::FDT_INT32; + return MetaColumn::FDT_INT64; else if (sqliteType.empty() || sqliteType.npos != sqliteType.find("CHAR") || sqliteType.npos != sqliteType.find("CLOB") || diff --git a/Data/SQLite/testsuite/src/SQLiteTest.cpp b/Data/SQLite/testsuite/src/SQLiteTest.cpp index 6fad5382c..99dd085b1 100644 --- a/Data/SQLite/testsuite/src/SQLiteTest.cpp +++ b/Data/SQLite/testsuite/src/SQLiteTest.cpp @@ -54,6 +54,8 @@ using Poco::InvalidAccessException; using Poco::RangeException; using Poco::BadCastException; using Poco::Data::SQLite::InvalidSQLStatementException; +using Poco::Int64; + struct Person { @@ -496,13 +498,16 @@ void SQLiteTest::testLimitPrepare() Statement stmt = (tmp << "SELECT * FROM Strings", into(retData), limit(50)); assert (retData.size() == 0); assert (!stmt.done()); - stmt.execute(); + Poco::UInt32 rows = stmt.execute(); + assert (50 == rows); assert (!stmt.done()); assert (retData.size() == 50); - stmt.execute(); + rows = stmt.execute(); + assert (50 == rows); assert (stmt.done()); assert (retData.size() == 100); - stmt.execute(); // will restart execution! + rows = stmt.execute(); // will restart execution! + assert (50 == rows); assert (!stmt.done()); assert (retData.size() == 150); for (int x = 0; x < 150; ++x) @@ -1030,28 +1035,6 @@ void SQLiteTest::testBLOB() } -void SQLiteTest::testBLOBStmt() -{ - // the following test will fail becuase we use a temporary object as parameter to use - /* - Session tmp (SessionFactory::instance().create(SQLite::Connector::KEY, "dummy.db")); - tmp << "DROP TABLE IF EXISTS Person", now; - tmp << "CREATE TABLE IF NOT EXISTS Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Image BLOB)", now; - BLOB img("0123456789", 10); - int count = 0; - Statement ins = (tmp << "INSERT INTO PERSON VALUES(:ln, :fn, :ad, :img)", use("lastname"), use("firstname"), use("Address"), use(BLOB("0123456789", 10))); - ins.execute(); - tmp << "SELECT COUNT(*) FROM PERSON", into(count), now; - assert (count == 1); - BLOB res; - poco_assert (res.size() == 0); - Statement stmt = (tmp << "SELECT Image FROM Person WHERE LastName == :ln", use("lastname"), into(res)); - stmt.execute(); - poco_assert (res == img); - */ -} - - void SQLiteTest::testTuple10() { Session tmp (SessionFactory::instance().create(SQLite::Connector::KEY, "dummy.db")); @@ -1537,10 +1520,13 @@ void SQLiteTest::testInternalExtraction() assert (3 == rset2.columnCount()); assert (4 == rset2.rowCount()); - int a = rset.value(0,2); + Int64 a = rset.value(0,2); assert (3 == a); - int b = rset2.value("InT0",2); + int c = rset2.value(0); + assert (1 == c); + + Int64 b = rset2.value("InT0",2); assert (3 == b); double d = rset.value(1,0); @@ -1549,18 +1535,18 @@ void SQLiteTest::testInternalExtraction() std::string s = rset.value(2,1); assert ("4" == s); - typedef std::vector IntVec; + typedef std::deque IntDeq; - const Column& col = rset.column(0); + const Column& col = rset.column(0); assert (col[0] == 1); - try { rset.column(100); fail ("must fail"); } + try { rset.column(100); fail ("must fail"); } catch (RangeException&) { } - const Column& col1 = rset.column(0); + const Column& col1 = rset.column(0); assert ("int0" == col1.name()); - Column::Iterator it = col1.begin(); - Column::Iterator itEnd = col1.end(); + Column::Iterator it = col1.begin(); + Column::Iterator itEnd = col1.end(); int counter = 1; for (; it != itEnd; ++it, ++counter) assert (counter == *it); @@ -1572,7 +1558,7 @@ void SQLiteTest::testInternalExtraction() stmt = (tmp << "DELETE FROM Vectors", now); rset = stmt; - try { rset.column(0); fail ("must fail"); } + try { rset.column(0); fail ("must fail"); } catch (RangeException&) { } } @@ -1701,6 +1687,51 @@ void SQLiteTest::testRowIterator() } +void SQLiteTest::testAsync() +{ + Session tmp (SessionFactory::instance().create(SQLite::Connector::KEY, "dummy.db")); + tmp << "DROP TABLE IF EXISTS Strings", now; + tmp << "CREATE TABLE IF NOT EXISTS Strings (str INTEGER(10))", now; + + int rowCount = 500; + std::vector data(rowCount); + Statement stmt = (tmp << "INSERT INTO Strings VALUES(:str)", use(data)); + Statement::Result result = stmt.executeAsync(); + assert (!stmt.isAsync()); + result.wait(); + + stmt = tmp << "SELECT * FROM Strings", into(data), async, now; + assert (stmt.isAsync()); + stmt.wait(); + + assert (stmt.execute() == 0); + assert (stmt.isAsync()); + try { + result = stmt.executeAsync(); + fail ("must fail"); + } catch (InvalidAccessException&) + { + stmt.wait(); + result = stmt.executeAsync(); + } + + assert (stmt.wait() == rowCount); + assert (result.data() == rowCount); + stmt.setAsync(false); + assert (!stmt.isAsync()); + assert (stmt.execute() == rowCount); + + stmt = tmp << "SELECT * FROM Strings", into(data), sync, now; + assert (!stmt.isAsync()); + assert (stmt.wait() == 0); + assert (stmt.execute() == rowCount); + result = stmt.executeAsync(); + assert (!stmt.isAsync()); + result.wait(); + assert (result.data() == rowCount); +} + + void SQLiteTest::setUp() { } @@ -1750,7 +1781,6 @@ CppUnit::Test* SQLiteTest::suite() CppUnit_addTest(pSuite, SQLiteTest, testSingleSelect); CppUnit_addTest(pSuite, SQLiteTest, testEmptyDB); CppUnit_addTest(pSuite, SQLiteTest, testBLOB); - CppUnit_addTest(pSuite, SQLiteTest, testBLOBStmt); CppUnit_addTest(pSuite, SQLiteTest, testTuple10); CppUnit_addTest(pSuite, SQLiteTest, testTupleVector10); CppUnit_addTest(pSuite, SQLiteTest, testTuple9); @@ -1775,6 +1805,7 @@ CppUnit::Test* SQLiteTest::suite() CppUnit_addTest(pSuite, SQLiteTest, testPrimaryKeyConstraint); CppUnit_addTest(pSuite, SQLiteTest, testNull); CppUnit_addTest(pSuite, SQLiteTest, testRowIterator); + CppUnit_addTest(pSuite, SQLiteTest, testAsync); return pSuite; } diff --git a/Data/SQLite/testsuite/src/SQLiteTest.h b/Data/SQLite/testsuite/src/SQLiteTest.h index c24189b03..d1fde24ca 100644 --- a/Data/SQLite/testsuite/src/SQLiteTest.h +++ b/Data/SQLite/testsuite/src/SQLiteTest.h @@ -85,7 +85,6 @@ public: void testEmptyDB(); void testBLOB(); - void testBLOBStmt(); void testTuple1(); void testTupleVector1(); @@ -112,6 +111,7 @@ public: void testPrimaryKeyConstraint(); void testNull(); void testRowIterator(); + void testAsync(); void setUp(); void tearDown(); diff --git a/Data/include/Poco/Data/AbstractSessionImpl.h b/Data/include/Poco/Data/AbstractSessionImpl.h index db04586d4..c1718101d 100644 --- a/Data/include/Poco/Data/AbstractSessionImpl.h +++ b/Data/include/Poco/Data/AbstractSessionImpl.h @@ -75,7 +75,7 @@ public: AbstractSessionImpl() /// Creates the AbstractSessionImpl. /// Adds "storage" property and sets the default internal storage container - /// type to std::vector. + /// type to std::deque. /// The storage is created by statements automatically whenever a query /// returning results is executed but external storage is provided by the user. /// Storage type can be reconfigured at runtime both globally (for the @@ -86,7 +86,7 @@ public: &AbstractSessionImpl::setStorage, &AbstractSessionImpl::getStorage); - setProperty("storage", std::string("vector")); + setProperty("storage", std::string("deque")); } ~AbstractSessionImpl() diff --git a/Data/include/Poco/Data/Column.h b/Data/include/Poco/Data/Column.h index fba4964b7..e7c5c848f 100644 --- a/Data/include/Poco/Data/Column.h +++ b/Data/include/Poco/Data/Column.h @@ -52,7 +52,7 @@ namespace Poco { namespace Data { -template > +template > class Column /// Column class is column data container. /// Data (a pointer to container) is assigned to the class diff --git a/Data/include/Poco/Data/Extraction.h b/Data/include/Poco/Data/Extraction.h index 982b3bede..bc9780fee 100644 --- a/Data/include/Poco/Data/Extraction.h +++ b/Data/include/Poco/Data/Extraction.h @@ -352,7 +352,7 @@ private: }; -template > +template > class InternalExtraction: public Extraction /// Container Data Type specialization extension for extraction of values from a query result set. /// diff --git a/Data/include/Poco/Data/RecordSet.h b/Data/include/Poco/Data/RecordSet.h index 1763a7f9a..4451a2acd 100644 --- a/Data/include/Poco/Data/RecordSet.h +++ b/Data/include/Poco/Data/RecordSet.h @@ -144,11 +144,11 @@ public: switch (storage()) { case STORAGE_VECTOR: - case STORAGE_UNKNOWN: return column >(col).value(row); case STORAGE_LIST: return column >(col).value(row); case STORAGE_DEQUE: + case STORAGE_UNKNOWN: return column >(col).value(row); default: throw IllegalStateException("Invalid storage setting."); @@ -162,11 +162,11 @@ public: switch (storage()) { case STORAGE_VECTOR: - case STORAGE_UNKNOWN: return column >(name).value(row); case STORAGE_LIST: return column >(name).value(row); case STORAGE_DEQUE: + case STORAGE_UNKNOWN: return column >(name).value(row); default: throw IllegalStateException("Invalid storage setting."); diff --git a/Data/include/Poco/Data/SessionPool.h b/Data/include/Poco/Data/SessionPool.h index 633a1be93..f17716ecf 100644 --- a/Data/include/Poco/Data/SessionPool.h +++ b/Data/include/Poco/Data/SessionPool.h @@ -87,7 +87,11 @@ class Data_API SessionPool /// ... { public: - SessionPool(const std::string& sessionKey, const std::string& connectionString, int minSessions = 1, int maxSessions = 32, int idleTime = 60); + SessionPool(const std::string& sessionKey, + const std::string& connectionString, + int minSessions = 1, + int maxSessions = 32, + int idleTime = 60); /// Creates the SessionPool for sessions with the given sessionKey /// and connectionString. /// diff --git a/Data/include/Poco/Data/Statement.h b/Data/include/Poco/Data/Statement.h index c89c22dfa..231e9e781 100644 --- a/Data/include/Poco/Data/Statement.h +++ b/Data/include/Poco/Data/Statement.h @@ -44,6 +44,8 @@ #include "Poco/Data/StatementImpl.h" #include "Poco/Data/Range.h" #include "Poco/SharedPtr.h" +#include "Poco/ActiveMethod.h" +#include "Poco/ActiveResult.h" namespace Poco { @@ -60,15 +62,67 @@ class Data_API Statement /// A Statement is used to execute SQL statements. /// It does not contain code of its own. /// Its main purpose is to forward calls to the concrete StatementImpl stored inside. + /// Statement execution can be synchronous or asynchronous. + /// Synchronous ececution is achieved through execute() call, while asynchronous is + /// achieved through executeAsync() method call. + /// An asynchronously executing statement should not be copied during the execution. + /// Copying is not prohibited, however the benefits of the asynchronous call shall + /// be lost for that particular call since the synchronizing call shall internally be + /// called in the copy constructor. + /// + /// For example, in the following case, although the execution is asyncronous, the + /// synchronization part happens in the copy constructor, so the asynchronous nature + /// of the statement is lost for the end user: + /// + /// Statement stmt = (session << "SELECT * FROM Table", async, now); + /// + /// while in this case it is preserved: + /// + /// Statement stmt = session << "SELECT * FROM Table", async, now; + /// + /// There are two ways to preserve the asynchronous nature of a statement: + /// + /// 1) Call executeAsync() method directly: + /// + /// Statement stmt = session << "SELECT * FROM Table"; // no execution yet + /// stmt.executeAsync(); // asynchronous execution + /// // do something else ... + /// stmt.wait(); // synchronize + /// + /// 2) Ensure asynchronous execution through careful syntax constructs: + /// + /// Statement stmt(session); + /// stmt = session << "SELECT * FROM Table", async, now; + /// // do something else ... + /// stmt.wait(); // synchronize + /// + /// Note: + /// + /// Once set as asynchronous through 'async' manipulator, statement remains + /// asynchronous for all subsequent execution calls, both execute() and executeAsync(). + /// However, calling executAsync() on a synchronous statement shall execute + /// asynchronously but without altering the underlying statement's synchronous nature. + /// + /// Once asyncronous, a statement can be reverted back to synchronous state in two ways: + /// + /// 1) By calling setAsync(false) + /// 2) By means of 'sync' or 'reset' manipulators + /// + /// See individual functions documentation for more details. + /// { public: typedef void (*Manipulator)(Statement&); + typedef Poco::UInt32 ResultType; + typedef ActiveResult Result; + typedef SharedPtr ResultPtr; + typedef ActiveMethod AsyncExecMethod; enum Storage { + STORAGE_DEQUE = StatementImpl::STORAGE_DEQUE_IMPL, STORAGE_VECTOR = StatementImpl::STORAGE_VECTOR_IMPL, STORAGE_LIST = StatementImpl::STORAGE_LIST_IMPL, - STORAGE_DEQUE = StatementImpl::STORAGE_DEQUE_IMPL, STORAGE_UNKNOWN = StatementImpl::STORAGE_UNKNOWN_IMPL }; @@ -93,7 +147,10 @@ public: /// Destroys the Statement. Statement(const Statement& stmt); - /// Copy constructor + /// Copy constructor. + /// If the statement has been executed asynchronously and has not been + /// synchronized prior to copy operation (i.e. is copied while executing), + /// this constructor shall synchronize it. Statement& operator = (const Statement& stmt); /// Assignment operator. @@ -103,7 +160,7 @@ public: template Statement& operator << (const T& t) - /// Concatenates the send data to a string version of the SQL statement. + /// Concatenates data with the SQL statement string. { _ptr->add(t); return *this; @@ -121,7 +178,7 @@ public: Statement& operator , (const Limit& extrLimit); /// Sets a limit on the maximum number of rows a select is allowed to return. /// - /// Set per default to Limit::LIMIT_UNLIMITED which disables the limit. + /// Set per default to zero to Limit::LIMIT_UNLIMITED, which disables the limit. Statement& operator , (const Range& extrRange); /// Sets a an etxraction Range on the maximum number of rows a select is allowed to return. @@ -131,13 +188,43 @@ public: std::string toString() const; /// Creates a string from the accumulated SQL statement - Poco::UInt32 execute(); - /// Executes the whole statement. Stops when either a limit is hit or the whole statement was executed. - /// Returns the number of rows extracted from the Database. + ResultType execute(); + /// Executes the statement synchronously or asynchronously. + /// Stops when either a limit is hit or the whole statement was executed. + /// Returns the number of rows extracted from the database. + /// If isAsync() returns true, the statement is executed asynchronously + /// and the return value from this function is zero. + /// The number of extracted rows from the query can be obtained by calling + /// wait(). + + const Result& executeAsync(); + /// Executes the statement asynchronously. + /// Stops when either a limit is hit or the whole statement was executed. + /// Returns immediately. For statements returning data, the number of rows extracted is + /// available by calling wait() method on either the returned value or the statement itself. + /// When executed on otherwise synchronous statement, this method does not alter the + /// statement's synchronous nature. + + void setAsync(bool async); + /// Sets the asynchronous flag. If this flag is true, executeAsync() is called + /// from the now() manipulator. This setting does not affect the statement's + /// capability to be executed synchronously by directly calling execute(). + + bool isAsync() const; + /// Returns true if statement was marked for asynchronous execution. + + Statement::ResultType wait(long milliseconds = WAIT_FOREVER); + /// Waits for the execution completion for asynchronous statements or + /// returns immediately for synchronous ones. The return value for + /// asynchronous statement is the execution result (i.e. number of + /// rows retrieved). For synchronous statements, the return value is zero. + + bool initialized(); + /// Returns true if the statement was initialized (i.e. not executed yet). bool done(); - /// Returns if the statement was completely executed or if a previously set limit stopped it - /// and there is more work to do. When no limit is set, it will always - after calling execute() - return true. + /// Returns true if the statement was completely executed or false if a range limit stopped it + /// and there is more work to do. When no limit is set, it will always return true after calling execute(). Statement& reset(Session& session); /// Resets the Statement so that it can be filled with a new SQL command. @@ -174,25 +261,59 @@ protected: private: typedef Poco::SharedPtr StatementImplPtr; - StatementImplPtr _ptr; + static const int WAIT_FOREVER = -1; + + StatementImplPtr _ptr; + + // asynchronous execution related members + bool _isAsync; + mutable ResultPtr _pResult; + FastMutex _mutex; + AsyncExecMethod _asyncExec; }; // // Manipulators // + void Data_API now(Statement& statement); + /// Enforces immediate execution of the statement. + /// If _isAsync flag has been set, execution is invoked asynchronously. -void Data_API vector(Statement& statement); -void Data_API list(Statement& statement); +void Data_API sync(Statement& statement); + /// Sets the _isAsync flag to false, signalling synchronous execution. + /// Synchronous execution is default, so specifying this manipulator + /// only makes sense if async() was called for the statement before. + + +void Data_API async(Statement& statement); + /// Sets the _isAsync flag to true, signalling asynchronous execution. + void Data_API deque(Statement& statement); + /// Sets the internal storage to std::deque. + /// std::deque is default storage, so specifying this manipulator + /// only makes sense if list() or deque() were called for the statement before. + + +void Data_API vector(Statement& statement); + /// Sets the internal storage to std::vector. + + +void Data_API list(Statement& statement); + /// Sets the internal storage to std::list. + + +void Data_API reset(Statement& statement); + /// Sets all internal settings to their respective default values. // // inlines // + inline Statement& Statement::operator , (Manipulator manip) { manip(*this); @@ -271,12 +392,42 @@ inline Statement::Storage Statement::storage() const } +inline bool Statement::canModifyStorage() +{ + return (0 == extractionCount()) && (initialized() || done()); +} + + +inline bool Statement::initialized() +{ + return _ptr->getState() == StatementImpl::ST_INITIALIZED; +} + + +inline bool Statement::done() +{ + return _ptr->getState() == StatementImpl::ST_DONE; +} + + inline bool Statement::isNull(std::size_t col, std::size_t row) const { return _ptr->isNull(col, row); } +inline void Statement::setAsync(bool async) +{ + _isAsync = async; +} + + +inline bool Statement::isAsync() const +{ + return _isAsync; +} + + } } // namespace Poco::Data diff --git a/Data/include/Poco/Data/StatementImpl.h b/Data/include/Poco/Data/StatementImpl.h index 734f8cd48..1c0f88fc1 100644 --- a/Data/include/Poco/Data/StatementImpl.h +++ b/Data/include/Poco/Data/StatementImpl.h @@ -79,15 +79,15 @@ public: enum Storage { + STORAGE_DEQUE_IMPL, STORAGE_VECTOR_IMPL, STORAGE_LIST_IMPL, - STORAGE_DEQUE_IMPL, STORAGE_UNKNOWN_IMPL }; + static const std::string DEQUE; static const std::string VECTOR; static const std::string LIST; - static const std::string DEQUE; static const std::string UNKNOWN; StatementImpl(SessionImpl& rSession); @@ -97,7 +97,7 @@ public: /// Destroys the StatementImpl. template void add(const T& t) - /// Appends SQl statement (fragments). + /// Appends SQL statement (fragments). { _ostr << t; } @@ -272,31 +272,31 @@ private: /// overrides the session setting for storage, otherwise the /// session setting is used. /// If neither this statement nor the session have the storage - /// type set, std::vector is the default container type used. + /// type set, std::deque is the default container type used. { std::string storage; switch (_storage) { + case STORAGE_DEQUE_IMPL: + storage = DEQUE; break; case STORAGE_VECTOR_IMPL: storage = VECTOR; break; case STORAGE_LIST_IMPL: storage = LIST; break; - case STORAGE_DEQUE_IMPL: - storage = DEQUE; break; case STORAGE_UNKNOWN_IMPL: storage = AnyCast(session().getProperty("storage")); break; } - if (storage.empty()) storage = VECTOR; + if (storage.empty()) storage = DEQUE; - if (0 == icompare(VECTOR, storage)) + if (0 == icompare(DEQUE, storage)) + addExtract(createExtract >(mc)); + else if (0 == icompare(VECTOR, storage)) addExtract(createExtract >(mc)); else if (0 == icompare(LIST, storage)) addExtract(createExtract >(mc)); - else if (0 == icompare(DEQUE, storage)) - addExtract(createExtract >(mc)); } bool isNull(std::size_t col, std::size_t row) const; diff --git a/Data/src/SessionPool.cpp b/Data/src/SessionPool.cpp index 7e1d20d16..21c4d80ea 100644 --- a/Data/src/SessionPool.cpp +++ b/Data/src/SessionPool.cpp @@ -51,7 +51,7 @@ SessionPool::SessionPool(const std::string& sessionKey, const std::string& conne _maxSessions(maxSessions), _idleTime(idleTime), _nSessions(0), - _janitorTimer(idleTime, idleTime/4) + _janitorTimer(1000*idleTime, 1000*idleTime/4) { Poco::TimerCallback callback(*this, &SessionPool::onJanitorTimer); _janitorTimer.start(callback); diff --git a/Data/src/Statement.cpp b/Data/src/Statement.cpp index 890426344..b72f1f7b7 100644 --- a/Data/src/Statement.cpp +++ b/Data/src/Statement.cpp @@ -40,6 +40,7 @@ #include "Poco/Data/Session.h" #include "Poco/Any.h" #include "Poco/Tuple.h" +#include "Poco/ActiveMethod.h" #include @@ -48,21 +49,30 @@ namespace Data { Statement::Statement(StatementImpl* pImpl): - _ptr(pImpl) + _ptr(pImpl), + _isAsync(false), + _asyncExec(_ptr, &StatementImpl::execute) { poco_check_ptr (pImpl); } -Statement::Statement(Session& session) +Statement::Statement(Session& session): + _asyncExec(_ptr, &StatementImpl::execute) { reset(session); } Statement::Statement(const Statement& stmt): - _ptr(stmt._ptr) + _ptr(stmt._ptr), + _isAsync(stmt._isAsync), + _pResult(stmt._pResult), + _asyncExec(_ptr, &StatementImpl::execute) { + // if executing asynchronously, wait + if (stmt._pResult) + stmt._pResult->wait(); } @@ -82,31 +92,56 @@ Statement& Statement::operator = (const Statement& stmt) void Statement::swap(Statement& other) { std::swap(_ptr, other._ptr); + std::swap(_isAsync, other._isAsync); + std::swap(_asyncExec, other._asyncExec); + std::swap(_pResult, other._pResult); } -Poco::UInt32 Statement::execute() +Statement::ResultType Statement::execute() { - if (done()) + if (!isAsync()) { - _ptr->reset(); + if (done()) _ptr->reset(); + return _ptr->execute(); + } + else + { + executeAsync(); + return 0; } - - return _ptr->execute(); } -bool Statement::done() +const Statement::Result& Statement::executeAsync() { - return _ptr->getState() == StatementImpl::ST_DONE; + FastMutex::ScopedLock lock(_mutex); + bool isDone = done(); + if (initialized() || isDone) + { + if (isDone) _ptr->reset(); + _pResult = new Result(_asyncExec()); + poco_check_ptr (_pResult); + return *_pResult; + } + else + throw InvalidAccessException("Statement still executing."); } -bool Statement::canModifyStorage() +Statement::ResultType Statement::wait(long milliseconds) { - return 0 == extractionCount() && - (_ptr->getState() == StatementImpl::ST_INITIALIZED || - _ptr->getState() == StatementImpl::ST_RESET); + if (!_pResult) return 0; + + if (WAIT_FOREVER != milliseconds) + _pResult->wait(milliseconds); + else + _pResult->wait(); + + if (_pResult->exception()) + throw *_pResult->exception(); + + return _pResult->data(); } @@ -142,6 +177,18 @@ void now(Statement& statement) } +void sync(Statement& statement) +{ + statement.setAsync(false); +} + + +void async(Statement& statement) +{ + statement.setAsync(true); +} + + void vector(Statement& statement) { if (!statement.canModifyStorage()) @@ -169,4 +216,14 @@ void deque(Statement& statement) } +void reset(Statement& statement) +{ + if (!statement.canModifyStorage()) + throw InvalidAccessException("Storage not modifiable."); + + statement.setStorage("deque"); + statement.setAsync(false); +} + + } } // namespace Poco::Data diff --git a/Data/src/StatementImpl.cpp b/Data/src/StatementImpl.cpp index 1306ceb27..11d1e66d8 100644 --- a/Data/src/StatementImpl.cpp +++ b/Data/src/StatementImpl.cpp @@ -99,9 +99,7 @@ Poco::UInt32 StatementImpl::execute() Poco::UInt32 StatementImpl::executeWithLimit() { poco_assert (_state != ST_DONE); - compile(); - Poco::UInt32 count = 0; do { @@ -111,8 +109,7 @@ Poco::UInt32 StatementImpl::executeWithLimit() next(); ++count; } - } - while (canBind()); + } while (canBind()); if (!canBind() && (!hasNext() || _extrLimit.value() == 0)) _state = ST_DONE; @@ -126,9 +123,7 @@ Poco::UInt32 StatementImpl::executeWithLimit() Poco::UInt32 StatementImpl::executeWithoutLimit() { poco_assert (_state != ST_DONE); - compile(); - Poco::UInt32 count = 0; do { @@ -138,8 +133,7 @@ Poco::UInt32 StatementImpl::executeWithoutLimit() next(); ++count; } - } - while (canBind()); + } while (canBind()); _state = ST_DONE; return count; @@ -268,12 +262,12 @@ void StatementImpl::resetExtraction() void StatementImpl::setStorage(const std::string& storage) { - if (0 == icompare(VECTOR, storage)) + if (0 == icompare(DEQUE, storage)) + _storage = STORAGE_DEQUE_IMPL; + else if (0 == icompare(VECTOR, storage)) _storage = STORAGE_VECTOR_IMPL; else if (0 == icompare(LIST, storage)) _storage = STORAGE_LIST_IMPL; - else if (0 == icompare(DEQUE, storage)) - _storage = STORAGE_DEQUE_IMPL; else if (0 == icompare(UNKNOWN, storage)) _storage = STORAGE_UNKNOWN_IMPL; else diff --git a/Data/testsuite/src/DataTest.cpp b/Data/testsuite/src/DataTest.cpp index 75fd994c3..3bab609c8 100644 --- a/Data/testsuite/src/DataTest.cpp +++ b/Data/testsuite/src/DataTest.cpp @@ -368,7 +368,7 @@ void DataTest::testColumnVector() pData->push_back(4); pData->push_back(5); - Column c(mc, pData); + Column > c(mc, pData); assert (c.rowCount() == 5); assert (c[0] == 1); @@ -389,7 +389,7 @@ void DataTest::testColumnVector() } catch (RangeException&) { } - Column c1 = c; + Column > c1 = c; assert (c1.rowCount() == 5); assert (c1[0] == 1); @@ -398,7 +398,7 @@ void DataTest::testColumnVector() assert (c1[3] == 4); assert (c1[4] == 5); - Column c2(c1); + Column > c2(c1); assert (c2.rowCount() == 5); assert (c2[0] == 1); @@ -434,7 +434,7 @@ void DataTest::testColumnVectorBool() pData->push_back(false); pData->push_back(true); - Column c(mc, pData); + Column > c(mc, pData); assert (c.rowCount() == 5); assert (c[0] == true); @@ -451,7 +451,7 @@ void DataTest::testColumnVectorBool() } catch (RangeException&) { } - Column c1 = c; + Column > c1 = c; assert (c1.rowCount() == 5); assert (c1[0] == true); @@ -460,7 +460,7 @@ void DataTest::testColumnVectorBool() assert (c1[3] == false); assert (c1[4] == true); - Column c2(c1); + Column > c2(c1); assert (c2.rowCount() == 5); assert (c2[0] == true); @@ -488,7 +488,7 @@ void DataTest::testColumnVectorBool() void DataTest::testColumnDeque() { typedef std::deque ContainerType; - typedef Column ColumnType; + typedef Column ColumnType; MetaColumn mc(0, "mc", MetaColumn::FDT_DOUBLE, 2, 3, true); diff --git a/Foundation/include/Poco/ActiveMethod.h b/Foundation/include/Poco/ActiveMethod.h index f389bb20d..3beac6954 100644 --- a/Foundation/include/Poco/ActiveMethod.h +++ b/Foundation/include/Poco/ActiveMethod.h @@ -115,10 +115,27 @@ public: return result; } + ActiveMethod(const ActiveMethod& other): + _pOwner(other._pOwner), + _method(other._method) + { + } + + ActiveMethod& operator = (const ActiveMethod& other) + { + ActiveMethod tmp(other); + swap(tmp); + return *this; + } + + void swap(ActiveMethod& other) + { + std::swap(_pOwner, other._pOwner); + std::swap(_method, other._method); + } + private: ActiveMethod(); - ActiveMethod(const ActiveMethod&); - ActiveMethod& operator = (const ActiveMethod&); OwnerType* _pOwner; Callback _method; @@ -190,10 +207,27 @@ public: return result; } + ActiveMethod(const ActiveMethod& other): + _pOwner(other._pOwner), + _method(other._method) + { + } + + ActiveMethod& operator = (const ActiveMethod& other) + { + ActiveMethod tmp(other); + swap(tmp); + return *this; + } + + void swap(ActiveMethod& other) + { + std::swap(_pOwner, other._pOwner); + std::swap(_method, other._method); + } + private: ActiveMethod(); - ActiveMethod(const ActiveMethod&); - ActiveMethod& operator = (const ActiveMethod&); OwnerType* _pOwner; Callback _method; diff --git a/Foundation/testsuite/src/ActiveMethodTest.cpp b/Foundation/testsuite/src/ActiveMethodTest.cpp index fcc5c823d..e640b872f 100644 --- a/Foundation/testsuite/src/ActiveMethodTest.cpp +++ b/Foundation/testsuite/src/ActiveMethodTest.cpp @@ -51,6 +51,11 @@ namespace class ActiveObject { public: + typedef ActiveMethod IntIntType; + typedef ActiveMethod VoidIntType; + typedef ActiveMethod VoidVoidType; + typedef ActiveMethod IntVoidType; + ActiveObject(): testMethod(this, &ActiveObject::testMethodImpl), testVoid(this,&ActiveObject::testVoidOutImpl), @@ -63,13 +68,13 @@ namespace { } - ActiveMethod testMethod; + IntIntType testMethod; - ActiveMethod testVoid; + VoidIntType testVoid; - ActiveMethod testVoidInOut; + VoidVoidType testVoidInOut; - ActiveMethod testVoidIn; + IntVoidType testVoidIn; void cont() { @@ -130,6 +135,46 @@ void ActiveMethodTest::testWait() } +void ActiveMethodTest::testCopy() +{ + ActiveObject activeObj; + + ActiveObject::IntIntType ii = activeObj.testMethod; + ActiveResult rii = ii(123); + assert (!rii.available()); + activeObj.cont(); + rii.wait(); + assert (rii.available()); + assert (rii.data() == 123); + assert (!rii.failed()); + + ActiveObject::VoidIntType vi = activeObj.testVoid; + ActiveResult rvi = vi(123); + assert (!rvi.available()); + activeObj.cont(); + rvi.wait(); + assert (rvi.available()); + assert (!rvi.failed()); + + ActiveObject::VoidVoidType vv = activeObj.testVoidInOut; + ActiveResult rvv = vv(); + assert (!rvv.available()); + activeObj.cont(); + rvv.wait(); + assert (rvv.available()); + assert (!rvv.failed()); + + ActiveObject::IntVoidType iv = activeObj.testVoidIn; + ActiveResult riv = iv(); + assert (!riv.available()); + activeObj.cont(); + riv.wait(); + assert (riv.available()); + assert (riv.data() == 123); + assert (!riv.failed()); +} + + void ActiveMethodTest::testWaitInterval() { ActiveObject activeObj; @@ -226,6 +271,7 @@ CppUnit::Test* ActiveMethodTest::suite() CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ActiveMethodTest"); CppUnit_addTest(pSuite, ActiveMethodTest, testWait); + CppUnit_addTest(pSuite, ActiveMethodTest, testCopy); CppUnit_addTest(pSuite, ActiveMethodTest, testWaitInterval); CppUnit_addTest(pSuite, ActiveMethodTest, testTryWait); CppUnit_addTest(pSuite, ActiveMethodTest, testFailure); diff --git a/Foundation/testsuite/src/ActiveMethodTest.h b/Foundation/testsuite/src/ActiveMethodTest.h index 670375c1a..9b453be65 100644 --- a/Foundation/testsuite/src/ActiveMethodTest.h +++ b/Foundation/testsuite/src/ActiveMethodTest.h @@ -47,6 +47,7 @@ public: ~ActiveMethodTest(); void testWait(); + void testCopy(); void testWaitInterval(); void testTryWait(); void testFailure(); diff --git a/components b/components index 9695fba7c..267ee0bf2 100644 --- a/components +++ b/components @@ -3,4 +3,7 @@ XML Util Net NetSSL_OpenSSL +Data +Data/SQLite +Data/ODBC CppUnit