mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-17 19:25:53 +02:00
null bindings and RecordSet::isNull (SQLite done and tested, ODBC todo)
This commit is contained in:
@@ -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();
|
||||
|
Reference in New Issue
Block a user