mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-08 21:10:02 +02:00
null bindings and RecordSet::isNull (SQLite done and tested, ODBC todo)
This commit is contained in:
@@ -125,12 +125,15 @@ public:
|
||||
void bind(std::size_t pos, const std::string& val, Direction dir = PD_IN);
|
||||
/// Binds a string.
|
||||
|
||||
void bind(std::size_t pos, const Poco::Data::BLOB& val, Direction dir = PD_IN);
|
||||
/// Binds a BLOB.
|
||||
void bind(std::size_t pos, const BLOB& val, Direction dir = PD_IN);
|
||||
/// 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.
|
||||
|
||||
void bind(std::size_t pos, const NullData& val, Direction dir = PD_IN);
|
||||
/// Binds a null. In-bound only.
|
||||
|
||||
void setDataBinding(ParameterBinding binding);
|
||||
/// 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;
|
||||
LengthVec _lengthIndicator;
|
||||
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)
|
||||
{
|
||||
_paramBinding = binding;
|
||||
|
@@ -127,6 +127,9 @@ public:
|
||||
Preparation::DataExtraction getDataExtraction() const;
|
||||
/// Returns data extraction mode.
|
||||
|
||||
bool isNull(std::size_t pos);
|
||||
/// Returns true if the current row value at pos column is null.
|
||||
|
||||
private:
|
||||
static const int CHUNK_SIZE = 1024;
|
||||
/// 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
|
||||
|
||||
|
||||
|
@@ -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
|
||||
{
|
||||
ParamMap::const_iterator it = _inParams.find(pAddr);
|
||||
|
@@ -63,57 +63,61 @@ public:
|
||||
~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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
void bind(std::size_t pos, const NullData& val, Direction dir = PD_IN);
|
||||
/// Binds a null.
|
||||
|
||||
private:
|
||||
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;
|
||||
};
|
||||
|
@@ -43,6 +43,8 @@
|
||||
#include "Poco/Data/SQLite/SQLite.h"
|
||||
#include "Poco/Data/AbstractExtractor.h"
|
||||
#include "Poco/Any.h"
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
typedef std::vector<std::pair<bool, bool> > NullIndVec;
|
||||
/// Type for null indicators container.
|
||||
|
||||
Extractor(sqlite3_stmt* pStmt);
|
||||
/// Creates the Extractor.
|
||||
|
||||
@@ -112,14 +117,38 @@ public:
|
||||
bool extract(std::size_t pos, Poco::Any& val);
|
||||
/// Extracts an Any.
|
||||
|
||||
private:
|
||||
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;
|
||||
NullIndVec _nulls;
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// inlines
|
||||
///
|
||||
inline void Extractor::reset()
|
||||
{
|
||||
_nulls.clear();
|
||||
}
|
||||
|
||||
|
||||
} } } // namespace Poco::Data::SQLite
|
||||
|
||||
|
||||
|
@@ -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)
|
||||
{
|
||||
if (rc != SQLITE_OK)
|
||||
|
@@ -313,7 +313,16 @@ bool Extractor::extract(std::size_t pos, Poco::Any& val)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -185,6 +185,8 @@ bool SQLiteStatementImpl::hasNext()
|
||||
|
||||
_stepCalled = true;
|
||||
_nextResponse = sqlite3_step(_pStmt);
|
||||
_pExtractor->reset();//to clear the cached null indicators
|
||||
|
||||
if (_nextResponse != SQLITE_ROW && _nextResponse != SQLITE_OK && _nextResponse != SQLITE_DONE)
|
||||
{
|
||||
Utility::throwException(_nextResponse);
|
||||
|
@@ -38,13 +38,12 @@
|
||||
#include "Poco/Data/Statement.h"
|
||||
#include "Poco/Data/RecordSet.h"
|
||||
#include "Poco/Data/SQLite/Connector.h"
|
||||
#include "Poco/Data/SQLite/SQLiteException.h"
|
||||
#include "Poco/Data/TypeHandler.h"
|
||||
#include "Poco/Tuple.h"
|
||||
#include "Poco/Any.h"
|
||||
#include "Poco/Exception.h"
|
||||
#include <iostream>
|
||||
#include "Poco/File.h"
|
||||
#include "Poco/Stopwatch.h"
|
||||
|
||||
|
||||
using namespace Poco::Data;
|
||||
@@ -54,7 +53,7 @@ using Poco::AnyCast;
|
||||
using Poco::InvalidAccessException;
|
||||
using Poco::RangeException;
|
||||
using Poco::BadCastException;
|
||||
|
||||
using Poco::Data::SQLite::InvalidSQLStatementException;
|
||||
|
||||
struct Person
|
||||
{
|
||||
@@ -187,6 +186,9 @@ void SQLiteTest::testSimpleAccess()
|
||||
assert (lastName == result);
|
||||
tmp << "SELECT Age FROM PERSON", into(count), now;
|
||||
assert (count == age);
|
||||
tmp << "UPDATE PERSON SET Age = -1", now;
|
||||
tmp << "SELECT Age FROM PERSON", into(age), now;
|
||||
assert (-1 == age);
|
||||
tmp.close();
|
||||
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()
|
||||
{
|
||||
}
|
||||
@@ -1682,6 +1746,7 @@ CppUnit::Test* SQLiteTest::suite()
|
||||
CppUnit_addTest(pSuite, SQLiteTest, testTupleVector1);
|
||||
CppUnit_addTest(pSuite, SQLiteTest, testInternalExtraction);
|
||||
CppUnit_addTest(pSuite, SQLiteTest, testPrimaryKeyConstraint);
|
||||
CppUnit_addTest(pSuite, SQLiteTest, testNull);
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
@@ -110,6 +110,7 @@ public:
|
||||
|
||||
void testInternalExtraction();
|
||||
void testPrimaryKeyConstraint();
|
||||
void testNull();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
@@ -46,15 +46,32 @@
|
||||
|
||||
namespace Poco {
|
||||
|
||||
|
||||
class DateTime;
|
||||
|
||||
|
||||
namespace Data {
|
||||
|
||||
|
||||
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
|
||||
/// 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;
|
||||
/// Binds a DateTime.
|
||||
|
||||
virtual void bind(std::size_t pos, const NullData& val, Direction dir) = 0;
|
||||
/// Binds a null.
|
||||
|
||||
static bool isOutBound(Direction dir);
|
||||
/// Returns true if direction is out bound;
|
||||
|
||||
|
@@ -106,6 +106,14 @@ public:
|
||||
Poco::UInt32 getLimit() const;
|
||||
/// 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:
|
||||
AbstractExtractor* _pExtractor;
|
||||
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
|
||||
|
||||
|
||||
|
@@ -111,9 +111,25 @@ public:
|
||||
|
||||
virtual bool extract(std::size_t pos, DateTime& val) = 0;
|
||||
/// 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
|
||||
|
||||
|
||||
|
@@ -93,11 +93,18 @@ public:
|
||||
return 1u;
|
||||
}
|
||||
|
||||
bool isNull(std::size_t row = 0) const
|
||||
{
|
||||
return _null;
|
||||
}
|
||||
|
||||
void extract(std::size_t pos)
|
||||
{
|
||||
if (_extracted) throw ExtractException("value already extracted");
|
||||
_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()
|
||||
@@ -114,6 +121,7 @@ private:
|
||||
T& _rResult;
|
||||
T _default; // copy the default
|
||||
bool _extracted;
|
||||
bool _null;
|
||||
};
|
||||
|
||||
|
||||
@@ -149,10 +157,23 @@ public:
|
||||
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)
|
||||
{
|
||||
AbstractExtractor* pExt = getExtractor();
|
||||
_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()
|
||||
@@ -174,6 +195,7 @@ protected:
|
||||
private:
|
||||
std::vector<T>& _rResult;
|
||||
T _default; // copy the default
|
||||
std::deque<bool> _nulls;
|
||||
};
|
||||
|
||||
|
||||
@@ -209,10 +231,23 @@ public:
|
||||
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)
|
||||
{
|
||||
AbstractExtractor* pExt = getExtractor();
|
||||
_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()
|
||||
@@ -234,6 +269,7 @@ protected:
|
||||
private:
|
||||
std::list<T>& _rResult;
|
||||
T _default; // copy the default
|
||||
std::deque<bool> _nulls;
|
||||
};
|
||||
|
||||
|
||||
@@ -269,10 +305,23 @@ public:
|
||||
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)
|
||||
{
|
||||
AbstractExtractor* pExt = getExtractor();
|
||||
_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()
|
||||
@@ -294,6 +343,7 @@ protected:
|
||||
private:
|
||||
std::deque<T>& _rResult;
|
||||
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
|
||||
{
|
||||
return *_pColumn;
|
||||
|
@@ -53,6 +53,9 @@ namespace Poco {
|
||||
namespace Data {
|
||||
|
||||
|
||||
class Session;
|
||||
|
||||
|
||||
class Data_API RecordSet: private Statement
|
||||
/// RecordSet provides access to data returned from a query.
|
||||
/// 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.
|
||||
{
|
||||
public:
|
||||
using Statement::isNull;
|
||||
|
||||
explicit RecordSet(const Statement& rStatement);
|
||||
/// Creates the RecordSet.
|
||||
|
||||
explicit RecordSet(Session& rSession, const std::string& query);
|
||||
/// Creates the RecordSet.
|
||||
|
||||
~RecordSet();
|
||||
/// Destroys the RecordSet.
|
||||
|
||||
@@ -223,6 +231,9 @@ public:
|
||||
/// Returns column precision for the column with specified name.
|
||||
/// 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:
|
||||
RecordSet();
|
||||
|
||||
@@ -273,7 +284,7 @@ inline std::size_t RecordSet::columnCount() const
|
||||
|
||||
inline Statement& RecordSet::operator = (const Statement& stmt)
|
||||
{
|
||||
_currentRow = 1;
|
||||
_currentRow = 0;
|
||||
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
|
||||
|
||||
|
||||
|
@@ -168,6 +168,9 @@ protected:
|
||||
const MetaColumn& metaColumn(const std::string& name) const;
|
||||
/// 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:
|
||||
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
|
||||
|
||||
|
||||
|
@@ -299,6 +299,9 @@ private:
|
||||
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& 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
|
||||
|
||||
|
||||
|
@@ -35,6 +35,7 @@
|
||||
|
||||
|
||||
#include "Poco/Data/RecordSet.h"
|
||||
#include "Poco/Data/Session.h"
|
||||
|
||||
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -96,10 +96,13 @@ public:
|
||||
void bind(std::size_t pos, const std::string& val, Direction dir = PD_IN);
|
||||
/// 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.
|
||||
|
||||
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.
|
||||
|
||||
void reset();
|
||||
|
@@ -99,6 +99,9 @@ public:
|
||||
bool extract(std::size_t pos, Poco::DateTime& val);
|
||||
/// Extracts a DateTime.
|
||||
|
||||
bool isNull(std::size_t pos);
|
||||
/// Returns true if the current row value at pos column is null.
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
@@ -108,6 +111,12 @@ inline void Extractor::reset()
|
||||
}
|
||||
|
||||
|
||||
inline bool Extractor::isNull(std::size_t pos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
} } } // namespace Poco::Data::Test
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user