null bindings and RecordSet::isNull (SQLite done and tested, ODBC todo)

This commit is contained in:
Aleksandar Fabijanic
2007-06-19 23:24:16 +00:00
parent 6a5afde278
commit 7fac905b65
21 changed files with 402 additions and 44 deletions

View File

@@ -125,12 +125,15 @@ public:
void bind(std::size_t pos, const std::string& val, Direction dir = PD_IN); void bind(std::size_t pos, const std::string& val, Direction dir = PD_IN);
/// Binds a string. /// Binds a string.
void bind(std::size_t pos, const Poco::Data::BLOB& val, Direction dir = PD_IN); void bind(std::size_t pos, const BLOB& val, Direction dir = PD_IN);
/// Binds a BLOB. /// Binds a BLOB. In-bound only.
void bind(std::size_t pos, const Poco::DateTime& val, Direction dir = PD_IN); void bind(std::size_t pos, const DateTime& val, Direction dir = PD_IN);
/// Binds a DateTime. /// Binds a DateTime.
void bind(std::size_t pos, const NullData& val, Direction dir = PD_IN);
/// Binds a null. In-bound only.
void setDataBinding(ParameterBinding binding); void setDataBinding(ParameterBinding binding);
/// Set data binding type. /// Set data binding type.
@@ -212,6 +215,10 @@ private:
} }
} }
void bindNull(std::size_t pos, SQLSMALLINT cDataType);
/// Utility function for binding null values.
/// For in-bound purposes only.
const StatementHandle& _rStmt; const StatementHandle& _rStmt;
LengthVec _lengthIndicator; LengthVec _lengthIndicator;
ParamMap _inParams; ParamMap _inParams;
@@ -298,6 +305,15 @@ inline void Binder::bind(std::size_t pos, const char& val, Direction dir)
} }
inline void Binder::bind(std::size_t pos, const NullData& val, Direction dir)
{
if (isOutBound(dir) || !isInBound(dir))
throw NotImplementedException("NULL parameter type can only be inbound.");
throw NotImplementedException("TODO");
}
inline void Binder::setDataBinding(Binder::ParameterBinding binding) inline void Binder::setDataBinding(Binder::ParameterBinding binding)
{ {
_paramBinding = binding; _paramBinding = binding;

View File

@@ -127,6 +127,9 @@ public:
Preparation::DataExtraction getDataExtraction() const; Preparation::DataExtraction getDataExtraction() const;
/// Returns data extraction mode. /// Returns data extraction mode.
bool isNull(std::size_t pos);
/// Returns true if the current row value at pos column is null.
private: private:
static const int CHUNK_SIZE = 1024; static const int CHUNK_SIZE = 1024;
/// Amount of data retrieved in one SQLGetData() request when doing manual extract. /// Amount of data retrieved in one SQLGetData() request when doing manual extract.
@@ -200,6 +203,12 @@ inline Preparation::DataExtraction Extractor::getDataExtraction() const
} }
inline bool Extractor::isNull(std::size_t pos)
{
throw NotImplementedException("TODO");
}
} } } // namespace Poco::Data::ODBC } } } // namespace Poco::Data::ODBC

View File

@@ -216,6 +216,52 @@ void Binder::bind(std::size_t pos, const Poco::DateTime& val, Direction dir)
} }
void Binder::bindNull(std::size_t pos, SQLSMALLINT cDataType)
{
_inParams.insert(ParamMap::value_type(0, 0));
SQLLEN* pLenIn = new SQLLEN;
*pLenIn = SQL_NULL_DATA;
if (PB_AT_EXEC == _paramBinding)
*pLenIn = SQL_LEN_DATA_AT_EXEC(SQL_NULL_DATA);
_lengthIndicator.push_back(pLenIn);
SQLINTEGER colSize = 0;
SQLSMALLINT decDigits = 0;
try
{
Parameter p(_rStmt, pos);
colSize = (SQLINTEGER) p.columnSize();
decDigits = (SQLSMALLINT) p.decimalDigits();
}catch (StatementException&)
{
try
{
ODBCColumn c(_rStmt, pos);
colSize = (SQLINTEGER) c.length();
decDigits = (SQLSMALLINT) c.precision();
}catch (StatementException&) { }
}
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
SQL_PARAM_INPUT,
cDataType,
Utility::sqlDataType(cDataType),
colSize,
decDigits,
0,
0,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter()");
}
}
std::size_t Binder::parameterSize(SQLPOINTER pAddr) const std::size_t Binder::parameterSize(SQLPOINTER pAddr) const
{ {
ParamMap::const_iterator it = _inParams.find(pAddr); ParamMap::const_iterator it = _inParams.find(pAddr);

View File

@@ -63,57 +63,61 @@ public:
~Binder(); ~Binder();
/// Destroys the Binder. /// Destroys the Binder.
void bind(std::size_t pos, const Poco::Int8 &val, Direction dir); void bind(std::size_t pos, const Poco::Int8 &val, Direction dir = PD_IN);
/// Binds an Int8. /// Binds an Int8.
void bind(std::size_t pos, const Poco::UInt8 &val, Direction dir); void bind(std::size_t pos, const Poco::UInt8 &val, Direction dir = PD_IN);
/// Binds an UInt8. /// Binds an UInt8.
void bind(std::size_t pos, const Poco::Int16 &val, Direction dir); void bind(std::size_t pos, const Poco::Int16 &val, Direction dir = PD_IN);
/// Binds an Int16. /// Binds an Int16.
void bind(std::size_t pos, const Poco::UInt16 &val, Direction dir); void bind(std::size_t pos, const Poco::UInt16 &val, Direction dir = PD_IN);
/// Binds an UInt16. /// Binds an UInt16.
void bind(std::size_t pos, const Poco::Int32 &val, Direction dir); void bind(std::size_t pos, const Poco::Int32 &val, Direction dir = PD_IN);
/// Binds an Int32. /// Binds an Int32.
void bind(std::size_t pos, const Poco::UInt32 &val, Direction dir); void bind(std::size_t pos, const Poco::UInt32 &val, Direction dir = PD_IN);
/// Binds an UInt32. /// Binds an UInt32.
void bind(std::size_t pos, const Poco::Int64 &val, Direction dir); void bind(std::size_t pos, const Poco::Int64 &val, Direction dir = PD_IN);
/// Binds an Int64. /// Binds an Int64.
void bind(std::size_t pos, const Poco::UInt64 &val, Direction dir); void bind(std::size_t pos, const Poco::UInt64 &val, Direction dir = PD_IN);
/// Binds an UInt64. /// Binds an UInt64.
void bind(std::size_t pos, const bool &val, Direction dir); void bind(std::size_t pos, const bool &val, Direction dir = PD_IN);
/// Binds a boolean. /// Binds a boolean.
void bind(std::size_t pos, const float &val, Direction dir); void bind(std::size_t pos, const float &val, Direction dir = PD_IN);
/// Binds a float. /// Binds a float.
void bind(std::size_t pos, const double &val, Direction dir); void bind(std::size_t pos, const double &val, Direction dir = PD_IN);
/// Binds a double. /// Binds a double.
void bind(std::size_t pos, const char &val, Direction dir); void bind(std::size_t pos, const char &val, Direction dir = PD_IN);
/// Binds a single character. /// Binds a single character.
void bind(std::size_t pos, const char* const &pVal, Direction dir); void bind(std::size_t pos, const char* const &pVal, Direction dir = PD_IN);
/// Binds a const char ptr. /// Binds a const char ptr.
void bind(std::size_t pos, const std::string& val, Direction dir); void bind(std::size_t pos, const std::string& val, Direction dir = PD_IN);
/// Binds a string. /// Binds a string.
void bind(std::size_t pos, const Poco::Data::BLOB& val, Direction dir); void bind(std::size_t pos, const Poco::Data::BLOB& val, Direction dir = PD_IN);
/// Binds a BLOB. /// Binds a BLOB.
void bind(std::size_t pos, const DateTime& val, Direction dir); void bind(std::size_t pos, const DateTime& val, Direction dir = PD_IN);
/// Binds a DateTime. /// Binds a DateTime.
void bind(std::size_t pos, const NullData& val, Direction dir = PD_IN);
/// Binds a null.
private: private:
void checkReturn(int rc); void checkReturn(int rc);
/// Checks the SQLite return code and throws an appropriate exception. /// Checks the SQLite return code and throws an appropriate exception
/// if error has occurred.
sqlite3_stmt* _pStmt; sqlite3_stmt* _pStmt;
}; };

View File

@@ -43,6 +43,8 @@
#include "Poco/Data/SQLite/SQLite.h" #include "Poco/Data/SQLite/SQLite.h"
#include "Poco/Data/AbstractExtractor.h" #include "Poco/Data/AbstractExtractor.h"
#include "Poco/Any.h" #include "Poco/Any.h"
#include <vector>
#include <utility>
struct sqlite3_stmt; struct sqlite3_stmt;
@@ -58,6 +60,9 @@ class SQLite_API Extractor: public Poco::Data::AbstractExtractor
/// If NULL is received, the incoming val value is not changed and false is returned /// If NULL is received, the incoming val value is not changed and false is returned
{ {
public: public:
typedef std::vector<std::pair<bool, bool> > NullIndVec;
/// Type for null indicators container.
Extractor(sqlite3_stmt* pStmt); Extractor(sqlite3_stmt* pStmt);
/// Creates the Extractor. /// Creates the Extractor.
@@ -112,14 +117,38 @@ public:
bool extract(std::size_t pos, Poco::Any& val); bool extract(std::size_t pos, Poco::Any& val);
/// Extracts an Any. /// Extracts an Any.
private:
bool isNull(std::size_t pos); bool isNull(std::size_t pos);
/// Returns true if a non null value can be extracted /// Returns true if the current row value at pos column is null.
/// Because of the loss of information about null-ness of the
/// underlying database values due to the nature of SQLite engine,
/// (once null value is converted to default value, SQLite API
/// treats it as non-null), a null indicator container member
/// variable is used to cache the indicators of the underlying nulls
/// thus rendering this function idempotent.
/// The container is a vector of [bool, bool] pairs.
/// The vector index corresponds to the column position, the first
/// bool value in the pair is true if the null indicator has
/// been set and the second bool value in the pair is true if
/// the column is actually null.
void reset();
/// Clears the cached nulls indicator vector.
private:
sqlite3_stmt* _pStmt; sqlite3_stmt* _pStmt;
NullIndVec _nulls;
}; };
///
/// inlines
///
inline void Extractor::reset()
{
_nulls.clear();
}
} } } // namespace Poco::Data::SQLite } } } // namespace Poco::Data::SQLite

View File

@@ -110,6 +110,12 @@ void Binder::bind(std::size_t pos, const DateTime& val, Direction dir)
} }
void Binder::bind(std::size_t pos, const NullData&, Direction)
{
sqlite3_bind_null(_pStmt, pos);
}
void Binder::checkReturn(int rc) void Binder::checkReturn(int rc)
{ {
if (rc != SQLITE_OK) if (rc != SQLITE_OK)

View File

@@ -313,7 +313,16 @@ bool Extractor::extract(std::size_t pos, Poco::Any& val)
bool Extractor::isNull(std::size_t pos) bool Extractor::isNull(std::size_t pos)
{ {
return sqlite3_column_type(_pStmt, (int) pos) == SQLITE_NULL; if (pos >= _nulls.size())
_nulls.resize(pos + 1);
if (!_nulls[pos].first)
{
_nulls[pos].first = true;
_nulls[pos].second = (SQLITE_NULL == sqlite3_column_type(_pStmt, pos));
}
return _nulls[pos].second;
} }

View File

@@ -185,6 +185,8 @@ bool SQLiteStatementImpl::hasNext()
_stepCalled = true; _stepCalled = true;
_nextResponse = sqlite3_step(_pStmt); _nextResponse = sqlite3_step(_pStmt);
_pExtractor->reset();//to clear the cached null indicators
if (_nextResponse != SQLITE_ROW && _nextResponse != SQLITE_OK && _nextResponse != SQLITE_DONE) if (_nextResponse != SQLITE_ROW && _nextResponse != SQLITE_OK && _nextResponse != SQLITE_DONE)
{ {
Utility::throwException(_nextResponse); Utility::throwException(_nextResponse);

View File

@@ -38,13 +38,12 @@
#include "Poco/Data/Statement.h" #include "Poco/Data/Statement.h"
#include "Poco/Data/RecordSet.h" #include "Poco/Data/RecordSet.h"
#include "Poco/Data/SQLite/Connector.h" #include "Poco/Data/SQLite/Connector.h"
#include "Poco/Data/SQLite/SQLiteException.h"
#include "Poco/Data/TypeHandler.h" #include "Poco/Data/TypeHandler.h"
#include "Poco/Tuple.h" #include "Poco/Tuple.h"
#include "Poco/Any.h" #include "Poco/Any.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
#include <iostream> #include <iostream>
#include "Poco/File.h"
#include "Poco/Stopwatch.h"
using namespace Poco::Data; using namespace Poco::Data;
@@ -54,7 +53,7 @@ using Poco::AnyCast;
using Poco::InvalidAccessException; using Poco::InvalidAccessException;
using Poco::RangeException; using Poco::RangeException;
using Poco::BadCastException; using Poco::BadCastException;
using Poco::Data::SQLite::InvalidSQLStatementException;
struct Person struct Person
{ {
@@ -187,6 +186,9 @@ void SQLiteTest::testSimpleAccess()
assert (lastName == result); assert (lastName == result);
tmp << "SELECT Age FROM PERSON", into(count), now; tmp << "SELECT Age FROM PERSON", into(count), now;
assert (count == age); assert (count == age);
tmp << "UPDATE PERSON SET Age = -1", now;
tmp << "SELECT Age FROM PERSON", into(age), now;
assert (-1 == age);
tmp.close(); tmp.close();
assert (!tmp.isConnected()); assert (!tmp.isConnected());
} }
@@ -1610,6 +1612,68 @@ void SQLiteTest::testPrimaryKeyConstraint()
} }
void SQLiteTest::testNull()
{
Session ses (SessionFactory::instance().create(SQLite::Connector::KEY, "dummy.db"));
ses << "DROP TABLE IF EXISTS NullTest", now;
ses << "CREATE TABLE NullTest (i INTEGER NOT NULL)", now;
try
{
ses << "INSERT INTO NullTest VALUES(:i)", use(null), now;
fail ("must fail");
}catch (InvalidSQLStatementException&) { }
ses << "DROP TABLE IF EXISTS NullTest", now;
ses << "CREATE TABLE NullTest (i INTEGER, r REAL, v VARCHAR)", now;
ses << "INSERT INTO NullTest VALUES(:i, :r, :v)", use(null), use(null), use(null), now;
RecordSet rs(ses, "SELECT * FROM NullTest");
rs.moveFirst();
assert (rs.isNull("i"));
assert (rs["i"] == 0);
assert (rs.isNull("r"));
assert (rs.isNull("v"));
assert (rs["v"] == "");
ses << "DROP TABLE IF EXISTS NullTest", now;
ses << "CREATE TABLE NullTest (i INTEGER, r REAL, v VARCHAR)", now;
int i = 1;
double f = 1.2;
std::string s = "123";
ses << "INSERT INTO NullTest (i, r, v) VALUES (:i, :r, :v)", use(i), use(f), use(s), now;
rs = (ses << "SELECT * FROM NullTest", now);
rs.moveFirst();
assert (!rs.isNull("i"));
assert (rs["i"] == 1);
assert (!rs.isNull("v"));
assert (!rs.isNull("r"));
assert (rs["v"] == "123");
ses << "UPDATE NullTest SET v = :n WHERE i == :i", use(null), use(i), now;
i = 2;
f = 3.4;
ses << "INSERT INTO NullTest (i, r, v) VALUES (:i, :r, :v)", use(i), use(null), use(null), now;
rs = (ses << "SELECT i, r, v FROM NullTest ORDER BY i ASC", now);
rs.moveFirst();
assert (!rs.isNull("i"));
assert (rs["i"] == 1);
assert (!rs.isNull("r"));
assert (rs.isNull("v"));
assert (rs["v"] == "");
assert (rs.moveNext());
assert (!rs.isNull("i"));
assert (rs["i"] == 2);
assert (rs.isNull("r"));
assert (rs.isNull("v"));
assert (rs["v"] == "");
}
void SQLiteTest::setUp() void SQLiteTest::setUp()
{ {
} }
@@ -1682,6 +1746,7 @@ CppUnit::Test* SQLiteTest::suite()
CppUnit_addTest(pSuite, SQLiteTest, testTupleVector1); CppUnit_addTest(pSuite, SQLiteTest, testTupleVector1);
CppUnit_addTest(pSuite, SQLiteTest, testInternalExtraction); CppUnit_addTest(pSuite, SQLiteTest, testInternalExtraction);
CppUnit_addTest(pSuite, SQLiteTest, testPrimaryKeyConstraint); CppUnit_addTest(pSuite, SQLiteTest, testPrimaryKeyConstraint);
CppUnit_addTest(pSuite, SQLiteTest, testNull);
return pSuite; return pSuite;
} }

View File

@@ -110,6 +110,7 @@ public:
void testInternalExtraction(); void testInternalExtraction();
void testPrimaryKeyConstraint(); void testPrimaryKeyConstraint();
void testNull();
void setUp(); void setUp();
void tearDown(); void tearDown();

View File

@@ -46,15 +46,32 @@
namespace Poco { namespace Poco {
class DateTime; class DateTime;
namespace Data { namespace Data {
class BLOB; class BLOB;
enum NullData
{
NULL_GENERIC = 0,
NULL_INT8,
NULL_UINT8,
NULL_INT16,
NULL_UINT16,
NULL_INT32,
NULL_UINT32,
NULL_INT64,
NULL_UINT64,
NULL_FLOAT,
NULL_DOUBLE,
NULL_STRING,
NULL_BLOB
};
static const NullData null = NULL_GENERIC;
class Data_API AbstractBinder class Data_API AbstractBinder
/// Interface for Binding data types to placeholders. The default placeholder format /// Interface for Binding data types to placeholders. The default placeholder format
@@ -123,6 +140,9 @@ public:
virtual void bind(std::size_t pos, const DateTime& val, Direction dir) = 0; virtual void bind(std::size_t pos, const DateTime& val, Direction dir) = 0;
/// Binds a DateTime. /// Binds a DateTime.
virtual void bind(std::size_t pos, const NullData& val, Direction dir) = 0;
/// Binds a null.
static bool isOutBound(Direction dir); static bool isOutBound(Direction dir);
/// Returns true if direction is out bound; /// Returns true if direction is out bound;

View File

@@ -106,6 +106,14 @@ public:
Poco::UInt32 getLimit() const; Poco::UInt32 getLimit() const;
/// Gets the limit. /// Gets the limit.
virtual bool isNull(std::size_t row) const;
/// In implementations, this function returns true if value at row is null,
/// false otherwise.
/// Normal behavior is to replace nulls with default values.
/// However, extraction implementations may remember the underlying database
/// null values and be able to later provide information about them.
/// Here, this function throws NotImplementedException.
private: private:
AbstractExtractor* _pExtractor; AbstractExtractor* _pExtractor;
Poco::UInt32 _limit; Poco::UInt32 _limit;
@@ -143,6 +151,12 @@ inline Poco::UInt32 AbstractExtraction::getLimit() const
} }
inline bool AbstractExtraction::isNull(std::size_t row) const
{
throw NotImplementedException("Check for null values not implemented.");
}
} } // namespace Poco::Data } } // namespace Poco::Data

View File

@@ -111,9 +111,25 @@ public:
virtual bool extract(std::size_t pos, DateTime& val) = 0; virtual bool extract(std::size_t pos, DateTime& val) = 0;
/// Extracts a DateTime. Returns false if null was received. /// Extracts a DateTime. Returns false if null was received.
virtual bool isNull(std::size_t pos) = 0;
/// Returns true if the current row value at pos column is null.
virtual void reset();
/// Resets any information internally cached by the extractor.
/// (e.g. info about underlying DB null values)
}; };
///
/// inlines
///
inline void AbstractExtractor::reset()
{
//default no-op
}
} } // namespace Poco::Data } } // namespace Poco::Data

View File

@@ -93,11 +93,18 @@ public:
return 1u; return 1u;
} }
bool isNull(std::size_t row = 0) const
{
return _null;
}
void extract(std::size_t pos) void extract(std::size_t pos)
{ {
if (_extracted) throw ExtractException("value already extracted"); if (_extracted) throw ExtractException("value already extracted");
_extracted = true; _extracted = true;
TypeHandler<T>::extract(pos, _rResult, _default, getExtractor()); AbstractExtractor* pExt = getExtractor();
TypeHandler<T>::extract(pos, _rResult, _default, pExt);
_null = pExt->isNull(pos);
} }
void reset() void reset()
@@ -114,6 +121,7 @@ private:
T& _rResult; T& _rResult;
T _default; // copy the default T _default; // copy the default
bool _extracted; bool _extracted;
bool _null;
}; };
@@ -149,10 +157,23 @@ public:
return getLimit(); return getLimit();
} }
bool isNull(std::size_t row) const
{
try
{
return _nulls.at(row);
}catch (std::out_of_range& ex)
{
throw RangeException(ex.what());
}
}
void extract(std::size_t pos) void extract(std::size_t pos)
{ {
AbstractExtractor* pExt = getExtractor();
_rResult.push_back(_default); _rResult.push_back(_default);
TypeHandler<T>::extract(pos, _rResult.back(), _default, getExtractor()); TypeHandler<T>::extract(pos, _rResult.back(), _default, pExt);
_nulls.push_back(pExt->isNull(pos));
} }
virtual void reset() virtual void reset()
@@ -172,8 +193,9 @@ protected:
} }
private: private:
std::vector<T>& _rResult; std::vector<T>& _rResult;
T _default; // copy the default T _default; // copy the default
std::deque<bool> _nulls;
}; };
@@ -209,10 +231,23 @@ public:
return getLimit(); return getLimit();
} }
bool isNull(std::size_t row) const
{
try
{
return _nulls.at(row);
}catch (std::out_of_range& ex)
{
throw RangeException(ex.what());
}
}
void extract(std::size_t pos) void extract(std::size_t pos)
{ {
AbstractExtractor* pExt = getExtractor();
_rResult.push_back(_default); _rResult.push_back(_default);
TypeHandler<T>::extract(pos, _rResult.back(), _default, getExtractor()); TypeHandler<T>::extract(pos, _rResult.back(), _default, pExt);
_nulls.push_back(pExt->isNull(pos));
} }
virtual void reset() virtual void reset()
@@ -232,8 +267,9 @@ protected:
} }
private: private:
std::list<T>& _rResult; std::list<T>& _rResult;
T _default; // copy the default T _default; // copy the default
std::deque<bool> _nulls;
}; };
@@ -269,10 +305,23 @@ public:
return getLimit(); return getLimit();
} }
bool isNull(std::size_t row) const
{
try
{
return _nulls.at(row);
}catch (std::out_of_range& ex)
{
throw RangeException(ex.what());
}
}
void extract(std::size_t pos) void extract(std::size_t pos)
{ {
AbstractExtractor* pExt = getExtractor();
_rResult.push_back(_default); _rResult.push_back(_default);
TypeHandler<T>::extract(pos, _rResult.back(), _default, getExtractor()); TypeHandler<T>::extract(pos, _rResult.back(), _default, pExt);
_nulls.push_back(pExt->isNull(pos));
} }
virtual void reset() virtual void reset()
@@ -292,8 +341,9 @@ protected:
} }
private: private:
std::deque<T>& _rResult; std::deque<T>& _rResult;
T _default; // copy the default T _default; // copy the default
std::deque<bool> _nulls;
}; };
@@ -341,6 +391,11 @@ public:
} }
} }
bool isNull(std::size_t row) const
{
return Extraction<C>::isNull(row);
}
const Column<T,C>& column() const const Column<T,C>& column() const
{ {
return *_pColumn; return *_pColumn;

View File

@@ -53,6 +53,9 @@ namespace Poco {
namespace Data { namespace Data {
class Session;
class Data_API RecordSet: private Statement class Data_API RecordSet: private Statement
/// RecordSet provides access to data returned from a query. /// RecordSet provides access to data returned from a query.
/// Data access indexes (row and column) are 0-based, as usual in C++. /// Data access indexes (row and column) are 0-based, as usual in C++.
@@ -73,9 +76,14 @@ class Data_API RecordSet: private Statement
/// a limit for the Statement. /// a limit for the Statement.
{ {
public: public:
using Statement::isNull;
explicit RecordSet(const Statement& rStatement); explicit RecordSet(const Statement& rStatement);
/// Creates the RecordSet. /// Creates the RecordSet.
explicit RecordSet(Session& rSession, const std::string& query);
/// Creates the RecordSet.
~RecordSet(); ~RecordSet();
/// Destroys the RecordSet. /// Destroys the RecordSet.
@@ -223,6 +231,9 @@ public:
/// Returns column precision for the column with specified name. /// Returns column precision for the column with specified name.
/// Valid for floating point fields only (zero for other data types). /// Valid for floating point fields only (zero for other data types).
bool isNull(const std::string& name);
/// Returns true if column value of the current row is null.
private: private:
RecordSet(); RecordSet();
@@ -273,7 +284,7 @@ inline std::size_t RecordSet::columnCount() const
inline Statement& RecordSet::operator = (const Statement& stmt) inline Statement& RecordSet::operator = (const Statement& stmt)
{ {
_currentRow = 1; _currentRow = 0;
return Statement::operator = (stmt); return Statement::operator = (stmt);
} }
@@ -344,6 +355,12 @@ inline std::size_t RecordSet::columnPrecision(const std::string& name)const
} }
inline bool RecordSet::isNull(const std::string& name)
{
return isNull(metaColumn(name).position(), _currentRow);
}
} } // namespace Poco::Data } } // namespace Poco::Data

View File

@@ -168,6 +168,9 @@ protected:
const MetaColumn& metaColumn(const std::string& name) const; const MetaColumn& metaColumn(const std::string& name) const;
/// Returns the type for the column with specified name. /// Returns the type for the column with specified name.
bool isNull(std::size_t col, std::size_t row);
/// Returns true if the current row value at column pos is null.
private: private:
typedef Poco::SharedPtr<StatementImpl> StatementImplPtr; typedef Poco::SharedPtr<StatementImpl> StatementImplPtr;
@@ -269,6 +272,12 @@ inline Statement::Storage Statement::storage() const
} }
inline bool Statement::isNull(std::size_t col, std::size_t row)
{
return _ptr->isNull(col, row);
}
} } // namespace Poco::Data } } // namespace Poco::Data

View File

@@ -299,6 +299,9 @@ private:
addExtract(createExtract<T, std::deque<T> >(mc)); addExtract(createExtract<T, std::deque<T> >(mc));
} }
bool isNull(std::size_t col, std::size_t row);
/// Returns true if the value in [col, row] is null.
StatementImpl(const StatementImpl& stmt); StatementImpl(const StatementImpl& stmt);
StatementImpl& operator = (const StatementImpl& stmt); StatementImpl& operator = (const StatementImpl& stmt);
@@ -403,6 +406,18 @@ inline bool StatementImpl::isStoredProcedure() const
} }
inline bool StatementImpl::isNull(std::size_t col, std::size_t row)
{
try
{
return extractions().at(col)->isNull(row);
}catch (std::out_of_range& ex)
{
throw RangeException(ex.what());
}
}
} } // namespace Poco::Data } } // namespace Poco::Data

View File

@@ -35,6 +35,7 @@
#include "Poco/Data/RecordSet.h" #include "Poco/Data/RecordSet.h"
#include "Poco/Data/Session.h"
namespace Poco { namespace Poco {
@@ -48,6 +49,13 @@ RecordSet::RecordSet(const Statement& rStatement):
} }
RecordSet::RecordSet(Session& rSession, const std::string& query):
Statement((rSession << query, now)),
_currentRow(0)
{
}
RecordSet::~RecordSet() RecordSet::~RecordSet()
{ {
} }

View File

@@ -120,12 +120,17 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir)
} }
void Binder::bind(std::size_t pos, const Poco::Data::BLOB& val, Direction dir) void Binder::bind(std::size_t pos, const BLOB& val, Direction dir)
{ {
} }
void Binder::bind(std::size_t pos, const Poco::DateTime& val, Direction dir) void Binder::bind(std::size_t pos, const DateTime& val, Direction dir)
{
}
void Binder::bind(std::size_t pos, const NullData& val, Direction dir)
{ {
} }

View File

@@ -96,10 +96,13 @@ public:
void bind(std::size_t pos, const std::string& val, Direction dir = PD_IN); void bind(std::size_t pos, const std::string& val, Direction dir = PD_IN);
/// Binds a string. /// Binds a string.
void bind(std::size_t pos, const Poco::Data::BLOB& val, Direction dir = PD_IN); void bind(std::size_t pos, const BLOB& val, Direction dir = PD_IN);
/// Binds a BLOB. /// Binds a BLOB.
void bind(std::size_t pos, const Poco::DateTime& val, Direction dir = PD_IN); void bind(std::size_t pos, const DateTime& val, Direction dir = PD_IN);
/// Binds a DateTime.
void bind(std::size_t pos, const NullData& val, Direction dir = PD_IN);
/// Binds a DateTime. /// Binds a DateTime.
void reset(); void reset();

View File

@@ -99,6 +99,9 @@ public:
bool extract(std::size_t pos, Poco::DateTime& val); bool extract(std::size_t pos, Poco::DateTime& val);
/// Extracts a DateTime. /// Extracts a DateTime.
bool isNull(std::size_t pos);
/// Returns true if the current row value at pos column is null.
void reset(); void reset();
}; };
@@ -108,6 +111,12 @@ inline void Extractor::reset()
} }
inline bool Extractor::isNull(std::size_t pos)
{
return false;
}
} } } // namespace Poco::Data::Test } } } // namespace Poco::Data::Test