Browse Source

GH #499: Poco::Data::Statement::execute returns wrong value when zero results (ODBC)

Aleksandar Fabijanic 11 years ago
parent
commit
86b81bfcbd

+ 1 - 1
Data/MySQL/include/Poco/Data/MySQL/MySQLStatementImpl.h

@@ -50,7 +50,7 @@ protected:
 	virtual std::size_t columnsReturned() const;
 		/// Returns number of columns returned by query.
 
-	virtual std::size_t affectedRowCount() const;
+	virtual int affectedRowCount() const;
 		/// Returns the number of affected rows.
 		/// Used to find out the number of rows affected by insert, delete or update.
 	

+ 2 - 2
Data/MySQL/include/Poco/Data/MySQL/StatementExecutor.h

@@ -64,7 +64,7 @@ public:
 	bool fetchColumn(std::size_t n, MYSQL_BIND *bind);
 		/// Fetches the column.
 
-	std::size_t getAffectedRowCount() const;
+	int getAffectedRowCount() const;
 		
 	operator MYSQL_STMT* ();
 		/// Cast operator to native handle type.
@@ -78,7 +78,7 @@ private:
 	MYSQL*      _pSessionHandle;
 	MYSQL_STMT* _pHandle;
 	int         _state;
-	std::size_t _affectedRowCount;
+	int         _affectedRowCount;
 	std::string _query;
 };
 

+ 1 - 1
Data/MySQL/src/MySQLStatementImpl.cpp

@@ -42,7 +42,7 @@ std::size_t MySQLStatementImpl::columnsReturned() const
 }
 
 
-std::size_t MySQLStatementImpl::affectedRowCount() const
+int MySQLStatementImpl::affectedRowCount() const
 {
 	return _stmt.getAffectedRowCount();
 }

+ 1 - 1
Data/MySQL/src/StatementExecutor.cpp

@@ -132,7 +132,7 @@ bool StatementExecutor::fetchColumn(std::size_t n, MYSQL_BIND *bind)
 	return (res == 0);
 }
 
-std::size_t StatementExecutor::getAffectedRowCount() const
+int StatementExecutor::getAffectedRowCount() const
 {
 	return _affectedRowCount;
 }

+ 1 - 1
Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h

@@ -56,7 +56,7 @@ protected:
 	std::size_t columnsReturned() const;
 		/// Returns number of columns returned by query.
 
-	std::size_t affectedRowCount() const;
+	int affectedRowCount() const;
 		/// Returns the number of affected rows.
 		/// Used to find out the number of rows affected by insert or update.
 

+ 1 - 1
Data/ODBC/src/ODBCStatementImpl.cpp

@@ -440,7 +440,7 @@ const MetaColumn& ODBCStatementImpl::metaColumn(std::size_t pos) const
 }
 
 
-std::size_t ODBCStatementImpl::affectedRowCount() const
+int ODBCStatementImpl::affectedRowCount() const
 {
 	if (0 == _affectedRowCount)
 	{

+ 1 - 0
Data/ODBC/testsuite/src/ODBCDB2Test.cpp

@@ -596,6 +596,7 @@ CppUnit::Test* ODBCDB2Test::suite()
 		CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCDB2Test");
 
 		CppUnit_addTest(pSuite, ODBCDB2Test, testBareboneODBC);
+		CppUnit_addTest(pSuite, ODBCDB2Test, testZeroRows);
 		CppUnit_addTest(pSuite, ODBCDB2Test, testSimpleAccess);
 		CppUnit_addTest(pSuite, ODBCDB2Test, testComplexType);
 		CppUnit_addTest(pSuite, ODBCDB2Test, testSimpleAccessVector);

+ 1 - 0
Data/ODBC/testsuite/src/ODBCMySQLTest.cpp

@@ -421,6 +421,7 @@ CppUnit::Test* ODBCMySQLTest::suite()
 		CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCMySQLTest");
 
 		CppUnit_addTest(pSuite, ODBCMySQLTest, testBareboneODBC);
+		CppUnit_addTest(pSuite, ODBCMySQLTest, testZeroRows);
 		CppUnit_addTest(pSuite, ODBCMySQLTest, testSimpleAccess);
 		CppUnit_addTest(pSuite, ODBCMySQLTest, testComplexType);
 		CppUnit_addTest(pSuite, ODBCMySQLTest, testSimpleAccessVector);

+ 1 - 0
Data/ODBC/testsuite/src/ODBCOracleTest.cpp

@@ -856,6 +856,7 @@ CppUnit::Test* ODBCOracleTest::suite()
 		CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCOracleTest");
 
 		CppUnit_addTest(pSuite, ODBCOracleTest, testBareboneODBC);
+		CppUnit_addTest(pSuite, ODBCOracleTest, testZeroRows);
 		CppUnit_addTest(pSuite, ODBCOracleTest, testSimpleAccess);
 		CppUnit_addTest(pSuite, ODBCOracleTest, testComplexType);
 		CppUnit_addTest(pSuite, ODBCOracleTest, testComplexTypeTuple);

+ 1 - 0
Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp

@@ -585,6 +585,7 @@ CppUnit::Test* ODBCPostgreSQLTest::suite()
 		CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCPostgreSQLTest");
 
 		CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testBareboneODBC);
+		CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testZeroRows);
 		CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSimpleAccess);
 		CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testComplexType);
 		CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSimpleAccessVector);

+ 1 - 0
Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp

@@ -737,6 +737,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
 		CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCSQLServerTest");
 
 		CppUnit_addTest(pSuite, ODBCSQLServerTest, testBareboneODBC);
+		CppUnit_addTest(pSuite, ODBCSQLServerTest, testZeroRows);
 		CppUnit_addTest(pSuite, ODBCSQLServerTest, testSimpleAccess);
 		CppUnit_addTest(pSuite, ODBCSQLServerTest, testComplexType);
 		CppUnit_addTest(pSuite, ODBCSQLServerTest, testSimpleAccessVector);

+ 1 - 0
Data/ODBC/testsuite/src/ODBCSQLiteTest.cpp

@@ -325,6 +325,7 @@ CppUnit::Test* ODBCSQLiteTest::suite()
 		CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCSQLiteTest");
 
 		CppUnit_addTest(pSuite, ODBCSQLiteTest, testBareboneODBC);
+		CppUnit_addTest(pSuite, ODBCSQLiteTest, testZeroRows);
 		CppUnit_addTest(pSuite, ODBCSQLiteTest, testSimpleAccess);
 		CppUnit_addTest(pSuite, ODBCSQLiteTest, testComplexType);
 		CppUnit_addTest(pSuite, ODBCSQLiteTest, testSimpleAccessVector);

+ 17 - 0
Data/ODBC/testsuite/src/ODBCTest.cpp

@@ -79,6 +79,23 @@ ODBCTest::~ODBCTest()
 }
 
 
+void ODBCTest::testZeroRows()
+{
+	if (!_pSession) fail ("Test not available.");
+
+	std::string tableName("Person");
+
+	for (int i = 0; i < 8;)
+	{
+		recreatePersonTable();
+		_pSession->setFeature("autoBind", bindValue(i));
+		_pSession->setFeature("autoExtract", bindValue(i+1));
+		_pExecutor->zeroRows();
+		i += 2;
+	}
+}
+
+
 void ODBCTest::testSimpleAccess()
 {
 	if (!_pSession) fail ("Test not available.");

+ 1 - 0
Data/ODBC/testsuite/src/ODBCTest.h

@@ -49,6 +49,7 @@ public:
 
 	virtual void testBareboneODBC() = 0;
 
+	virtual void testZeroRows();
 	virtual void testSimpleAccess();
 	virtual void testComplexType();
 	virtual void testComplexTypeTuple();

+ 7 - 0
Data/ODBC/testsuite/src/SQLExecutor.cpp

@@ -942,6 +942,13 @@ void SQLExecutor::execute(const std::string& sql)
 }
 
 
+void SQLExecutor::zeroRows()
+{
+	Statement stmt = (session() << "SELECT * FROM Person WHERE 0 = 1");
+	assert(0 == stmt.execute());
+}
+
+
 void SQLExecutor::simpleAccess()
 {
 	std::string funct = "simpleAccess()";

+ 1 - 0
Data/ODBC/testsuite/src/SQLExecutor.h

@@ -115,6 +115,7 @@ public:
 		/// (e.g. SQLGetData() restrictions relaxation policy, if any). 
 		/// If these test pass, subsequent tests failures are likely ours.
 
+	void zeroRows();
 	void simpleAccess();
 	void complexType();
 	void complexTypeTuple();

+ 3 - 3
Data/SQLite/include/Poco/Data/SQLite/SQLiteStatementImpl.h

@@ -54,7 +54,7 @@ protected:
 	std::size_t columnsReturned() const;
 		/// Returns number of columns returned by query.
 
-	std::size_t affectedRowCount() const;
+	int affectedRowCount() const;
 		/// Returns the number of affected rows.
 		/// Used to find out the number of rows affected by insert, delete or update.
 		/// All changes are counted, even if they are later undone by a ROLLBACK or ABORT. 
@@ -113,14 +113,14 @@ private:
 	BinderPtr        _pBinder;
 	ExtractorPtr     _pExtractor;
 	MetaColumnVecVec _columns;
-	std::size_t      _affectedRowCount;
+	int              _affectedRowCount;
 	StrPtr           _pLeftover;
 	BindIt           _bindBegin;
 	bool             _canBind;
 	bool             _isExtracted;
 	bool             _canCompile;
 
-	static const std::size_t POCO_SQLITE_INV_ROW_CNT;
+	static const int POCO_SQLITE_INV_ROW_CNT;
 };
 
 

+ 2 - 2
Data/SQLite/src/SQLiteStatementImpl.cpp

@@ -32,7 +32,7 @@ namespace Data {
 namespace SQLite {
 
 
-const std::size_t SQLiteStatementImpl::POCO_SQLITE_INV_ROW_CNT = std::numeric_limits<std::size_t>::max();
+const int SQLiteStatementImpl::POCO_SQLITE_INV_ROW_CNT = std::numeric_limits<std::size_t>::max();
 
 
 SQLiteStatementImpl::SQLiteStatementImpl(Poco::Data::SessionImpl& rSession, sqlite3* pDB):
@@ -302,7 +302,7 @@ const MetaColumn& SQLiteStatementImpl::metaColumn(std::size_t pos) const
 }
 
 
-std::size_t SQLiteStatementImpl::affectedRowCount() const
+int SQLiteStatementImpl::affectedRowCount() const
 {
 	if (_affectedRowCount != POCO_SQLITE_INV_ROW_CNT) return _affectedRowCount;
 	return _pStmt == 0 || sqlite3_stmt_readonly(_pStmt) ? 0 : sqlite3_changes(_pDB);

+ 11 - 0
Data/SQLite/testsuite/src/SQLiteTest.cpp

@@ -295,6 +295,16 @@ void SQLiteTest::testBinding()
 }
 
 
+void SQLiteTest::testZeroRows()
+{
+	Session tmp (Poco::Data::SQLite::Connector::KEY, "dummy.db");
+	tmp << "DROP TABLE IF EXISTS ZeroTest", now;
+	tmp << "CREATE TABLE IF NOT EXISTS ZeroTest (zt INTEGER(3))", now;
+	Statement stmt = (tmp << "SELECT * FROM ZeroTest");
+	assert(0 == stmt.execute());
+}
+
+
 void SQLiteTest::testSimpleAccess()
 {
 	Session tmp (Poco::Data::SQLite::Connector::KEY, "dummy.db");
@@ -3361,6 +3371,7 @@ CppUnit::Test* SQLiteTest::suite()
 	CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("SQLiteTest");
 
 	CppUnit_addTest(pSuite, SQLiteTest, testBinding);
+	CppUnit_addTest(pSuite, SQLiteTest, testZeroRows);
 	CppUnit_addTest(pSuite, SQLiteTest, testSimpleAccess);
 	CppUnit_addTest(pSuite, SQLiteTest, testInMemory);
 	CppUnit_addTest(pSuite, SQLiteTest, testNullCharPointer);

+ 1 - 0
Data/SQLite/testsuite/src/SQLiteTest.h

@@ -35,6 +35,7 @@ public:
 	~SQLiteTest();
 
 	void testBinding();
+	void testZeroRows();
 	void testSimpleAccess();
 	void testInMemory();
 	void testNullCharPointer();

+ 1 - 1
Data/include/Poco/Data/Statement.h

@@ -416,7 +416,7 @@ private:
 	Mutex               _mutex;
 	AsyncExecMethodPtr  _pAsyncExec;
 	std::vector<Any>    _arguments;
-	RowFormatter::Ptr     _pRowFormatter;
+	RowFormatter::Ptr   _pRowFormatter;
 	mutable std::string _stmtString;
 };
 

+ 5 - 1
Data/include/Poco/Data/StatementImpl.h

@@ -161,9 +161,13 @@ protected:
 	virtual std::size_t columnsReturned() const = 0;
 		/// Returns number of columns returned by query. 
 
-	virtual std::size_t affectedRowCount() const = 0;
+	virtual int affectedRowCount() const = 0;
 		/// Returns the number of affected rows.
 		/// Used to find out the number of rows affected by insert, delete or update.
+		/// 
+		/// Some back-ends may return a negative number in certain circumstances (e.g.
+		/// some ODBC drivers when this function is called after a select statement
+		/// execution).
 
 	virtual const MetaColumn& metaColumn(std::size_t pos) const = 0;
 		/// Returns column meta data.

+ 16 - 2
Data/src/StatementImpl.cpp

@@ -144,7 +144,14 @@ std::size_t StatementImpl::executeWithLimit()
 	else 
 		_state = ST_PAUSED;
 
-	return count ? count : affectedRowCount();
+	int affectedRows = affectedRowCount();
+	if (count == 0)
+	{
+		if (affectedRows > 0)
+			return affectedRows;
+	}
+
+	return count;
 }
 
 
@@ -159,7 +166,14 @@ std::size_t StatementImpl::executeWithoutLimit()
 		while (hasNext()) count += next();
 	} while (canBind());
 
-	return count ? count : affectedRowCount();
+	int affectedRows = affectedRowCount();
+	if (count == 0)
+	{
+		if (affectedRows > 0)
+			return affectedRows;
+	}
+
+	return count;
 }
 
 

+ 2 - 2
Data/testsuite/src/TestStatementImpl.h

@@ -46,7 +46,7 @@ protected:
 	std::size_t columnsReturned() const;
 		/// Returns number of columns returned by query. 
 	
-	std::size_t affectedRowCount() const;
+	int affectedRowCount() const;
 		/// Returns the number of affected rows.
 		/// Used to find out the number of rows affected by insert or update.
 
@@ -101,7 +101,7 @@ inline AbstractBinding::BinderPtr TestStatementImpl::binder()
 }
 
 
-inline std::size_t TestStatementImpl::affectedRowCount() const
+inline int TestStatementImpl::affectedRowCount() const
 {
 	return 0;
 }