diff --git a/Data/ODBC/include/Poco/Data/ODBC/Binder.h b/Data/ODBC/include/Poco/Data/ODBC/Binder.h index 59a4cbdad..d6ee4fb15 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Binder.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Binder.h @@ -165,9 +165,17 @@ private: catch (StatementException&) { } } + bool in = isInBound(); + bool out = isOutBound(); + SQLSMALLINT ioType = SQL_PARAM_TYPE_UNKNOWN; + if (in && out) ioType = SQL_PARAM_INPUT_OUTPUT; + else if(in) ioType = SQL_PARAM_INPUT; + else if(out) ioType = SQL_PARAM_OUTPUT; + else throw Poco::IllegalStateException("Binder not bound (must be [in] OR [out])."); + if (Utility::isError(SQLBindParameter(_rStmt, (SQLUSMALLINT) pos + 1, - SQL_PARAM_INPUT, + ioType, cDataType, sqlDataType, columnSize, diff --git a/Data/ODBC/testsuite/src/ODBCOracleTest.cpp b/Data/ODBC/testsuite/src/ODBCOracleTest.cpp index 5a5be5560..186b36f1b 100644 --- a/Data/ODBC/testsuite/src/ODBCOracleTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCOracleTest.cpp @@ -34,6 +34,7 @@ #include "CppUnit/TestCaller.h" #include "CppUnit/TestSuite.h" #include "Poco/String.h" +#include "Poco/Tuple.h" #include "Poco/Format.h" #include "Poco/Exception.h" #include "Poco/Data/Common.h" @@ -54,6 +55,7 @@ using Poco::Data::ODBC::ConnectionException; using Poco::Data::ODBC::StatementException; using Poco::Data::ODBC::StatementDiagnostics; using Poco::format; +using Poco::Tuple; using Poco::NotFoundException; @@ -827,7 +829,98 @@ void ODBCOracleTest::testInternalExtraction() void ODBCOracleTest::testStoredProcedure() { - //TODO + *_pSession << "CREATE OR REPLACE " + "PROCEDURE storedProcedure(outParam OUT NUMBER) IS " + " BEGIN outParam := -1; " + "END storedProcedure;" , now; + + int i = 0; + *_pSession << "{call storedProcedure(?)}", out(i), now; + assert(-1 == i); + *_pSession << "DROP PROCEDURE storedProcedure;", now; + + *_pSession << "CREATE OR REPLACE " + "PROCEDURE storedProcedure(inParam IN NUMBER, outParam OUT NUMBER) IS " + " BEGIN outParam := inParam*inParam; " + "END storedProcedure;" , now; + + i = 2; + int j = 0; + *_pSession << "{call storedProcedure(?, ?)}", in(i), out(j), now; + assert(4 == j); + *_pSession << "DROP PROCEDURE storedProcedure;", now; + + *_pSession << "CREATE OR REPLACE " + "PROCEDURE storedProcedure(ioParam IN OUT NUMBER) IS " + " BEGIN ioParam := ioParam*ioParam; " + " END storedProcedure;" , now; + + i = 2; + *_pSession << "{call storedProcedure(?)}", io(i), now; + assert(4 == i); + *_pSession << "DROP PROCEDURE storedProcedure;", now; +} + + +void ODBCOracleTest::testStoredFunction() +{ + *_pSession << "CREATE OR REPLACE " + "FUNCTION storedFunction RETURN NUMBER IS " + " BEGIN return(-1); " + " END storedFunction;" , now; + + int i = 0; + *_pSession << "{? = call storedFunction()}", out(i), now; + assert(-1 == i); + *_pSession << "DROP FUNCTION storedFunction;", now; + + *_pSession << "CREATE OR REPLACE " + "FUNCTION storedFunction(inParam IN NUMBER) RETURN NUMBER IS " + " BEGIN RETURN(inParam*inParam); " + " END storedFunction;" , now; + + i = 2; + int result = 0; + *_pSession << "{? = call storedFunction(?)}", out(result), in(i), now; + assert(4 == result); + *_pSession << "DROP FUNCTION storedFunction;", now; + + *_pSession << "CREATE OR REPLACE " + "FUNCTION storedFunction(inParam IN NUMBER, outParam OUT NUMBER) RETURN NUMBER IS " + " BEGIN outParam := inParam*inParam; RETURN(outParam); " + " END storedFunction;" , now; + + i = 2; + int j = 0; + result = 0; + *_pSession << "{? = call storedFunction(?, ?)}", out(result), in(i), out(j), now; + assert(4 == j); + assert(j == result); + *_pSession << "DROP FUNCTION storedFunction;", now; + + *_pSession << "CREATE OR REPLACE " + "FUNCTION storedFunction(param1 IN OUT NUMBER, param2 IN OUT NUMBER) RETURN NUMBER IS " + " temp NUMBER := param1; " + " BEGIN param1 := param2; param2 := temp; RETURN(param1+param2); " + " END storedFunction;" , now; + + i = 1; + j = 2; + result = 0; + *_pSession << "{? = call storedFunction(?, ?)}", out(result), io(i), io(j), now; + assert(1 == j); + assert(2 == i); + assert(3 == result); + + Tuple params(1, 2); + assert(1 == params.get<0>()); + assert(2 == params.get<1>()); + result = 0; + *_pSession << "{? = call storedFunction(?, ?)}", out(result), io(params), now; + assert(1 == params.get<1>()); + assert(2 == params.get<0>()); + assert(3 == result); + *_pSession << "DROP FUNCTION storedFunction;", now; } @@ -1081,6 +1174,7 @@ CppUnit::Test* ODBCOracleTest::suite() CppUnit_addTest(pSuite, ODBCOracleTest, testTuple); CppUnit_addTest(pSuite, ODBCOracleTest, testTupleVector); CppUnit_addTest(pSuite, ODBCOracleTest, testStoredProcedure); + CppUnit_addTest(pSuite, ODBCOracleTest, testStoredFunction); CppUnit_addTest(pSuite, ODBCOracleTest, testInternalExtraction); return pSuite; diff --git a/Data/ODBC/testsuite/src/ODBCOracleTest.h b/Data/ODBC/testsuite/src/ODBCOracleTest.h index 6291a37b7..88f3f1588 100644 --- a/Data/ODBC/testsuite/src/ODBCOracleTest.h +++ b/Data/ODBC/testsuite/src/ODBCOracleTest.h @@ -117,6 +117,7 @@ public: void testInternalExtraction(); void testStoredProcedure(); + void testStoredFunction(); void setUp(); void tearDown(); diff --git a/Data/include/Poco/Data/AbstractBinder.h b/Data/include/Poco/Data/AbstractBinder.h index d74731017..c8b297b4a 100644 --- a/Data/include/Poco/Data/AbstractBinder.h +++ b/Data/include/Poco/Data/AbstractBinder.h @@ -106,9 +106,52 @@ public: virtual void bind(std::size_t pos, const BLOB& val) = 0; /// Binds a BLOB. + + bool isInBound() const; + /// Returns true if binder is in-bound. + + bool isOutBound() const; + /// Returns true if binder is out-bound. + + void setInBound(bool inBound = true); + /// If inBound is true, binder is set to be in-bound. + + void setOutBound(bool outBound = true); + /// If outBound is true, binder is set to be out-bound. + +protected: + bool _inBound; + bool _outBound; }; +/// +/// inlines +/// +inline bool AbstractBinder::isInBound() const +{ + return _inBound; +} + + +inline bool AbstractBinder::isOutBound() const +{ + return _outBound; +} + + +inline void AbstractBinder::setInBound(bool inBound) +{ + _inBound = inBound; +} + + +inline void AbstractBinder::setOutBound(bool outBound) +{ + _outBound = outBound; +} + + } } // namespace Poco::Data diff --git a/Data/include/Poco/Data/AbstractBinding.h b/Data/include/Poco/Data/AbstractBinding.h index e85ebea6f..9f6d1c04e 100644 --- a/Data/include/Poco/Data/AbstractBinding.h +++ b/Data/include/Poco/Data/AbstractBinding.h @@ -41,6 +41,7 @@ #include "Poco/Data/Data.h" +#include "Poco/Data/AbstractBinder.h" #include "Poco/Any.h" #include "Poco/RefCountedObject.h" #include "Poco/AutoPtr.h" @@ -52,9 +53,6 @@ namespace Poco { namespace Data { -class AbstractBinder; - - class Data_API AbstractBinding: public Poco::RefCountedObject /// AbstractBinding connects a value with a placeholder via an AbstractBinder interface. { @@ -92,8 +90,16 @@ public: virtual void reset() = 0; /// Allows a binding to be reused. + void setInBound(bool inBound = true); + /// Sets the binder to be in-bound. + + void setOutBound(bool outBound = true); + /// Sets the binder to be out-bound. + private: AbstractBinder* _pBinder; + bool _inBound; + bool _outBound; }; @@ -106,7 +112,11 @@ typedef std::vector AbstractBindingVec; // inline void AbstractBinding::setBinder(AbstractBinder* pBinder) { + poco_check_ptr (pBinder); _pBinder = pBinder; + _pBinder->setInBound(_inBound); + _pBinder->setOutBound(_outBound); + poco_assert_dbg (_inBound || _outBound); } @@ -116,6 +126,18 @@ inline AbstractBinder* AbstractBinding::getBinder() const } +inline void AbstractBinding::setInBound(bool inBound) +{ + _inBound = inBound; +} + + +inline void AbstractBinding::setOutBound(bool outBound) +{ + _outBound = outBound; +} + + } } // namespace Poco::Data diff --git a/Data/include/Poco/Data/Binding.h b/Data/include/Poco/Data/Binding.h index c1e98c7ae..9af01e207 100644 --- a/Data/include/Poco/Data/Binding.h +++ b/Data/include/Poco/Data/Binding.h @@ -478,7 +478,36 @@ private: template Binding* use(const T& t) /// Convenience function for a more compact Binding creation. { - return new Binding(t); + Binding* pB = new Binding(t); + poco_check_ptr (pB); + return pB; +} + + +template Binding* in(const T& t) + /// Convenience function for a more compact Binding creation. +{ + return use(t); +} + + +template Binding* out(const T& t) + /// Convenience function for a more compact Binding creation. +{ + Binding* pB = use(t); + pB->setInBound(false); + pB->setOutBound(true); + return pB; +} + + +template Binding* io(const T& t) + /// Convenience function for a more compact Binding creation. +{ + Binding* pB = use(t); + pB->setInBound(true); + pB->setOutBound(true); + return pB; } diff --git a/Data/src/AbstractBinder.cpp b/Data/src/AbstractBinder.cpp index 41e5f42ab..068e6e19c 100644 --- a/Data/src/AbstractBinder.cpp +++ b/Data/src/AbstractBinder.cpp @@ -41,7 +41,9 @@ namespace Poco { namespace Data { -AbstractBinder::AbstractBinder() +AbstractBinder::AbstractBinder(): + _inBound(true), + _outBound(false) { } diff --git a/Data/src/AbstractBinding.cpp b/Data/src/AbstractBinding.cpp index b33812f4a..14757f290 100644 --- a/Data/src/AbstractBinding.cpp +++ b/Data/src/AbstractBinding.cpp @@ -42,7 +42,9 @@ namespace Data { AbstractBinding::AbstractBinding(): - _pBinder(0) + _pBinder(0), + _inBound(true), + _outBound(false) { }