2012-04-29 20:52:25 +02:00
POCO Data Connectors Developer Guide
2012-10-13 12:07:38 +02:00
POCO Data Library
2012-04-29 20:52:25 +02:00
!!!Overview
Developing one's own <*Data Connector*> implementation is rather straight-forward.
Just implement the following interfaces:
* Poco::Data::AbstractBinder
* Poco::Data::AbstractExtractor
* Poco::Data::StatementImpl
* Poco::Data::SessionImpl
* Poco::Data::Connector
* optional: Poco::Data::AbstractPreparation
2022-07-07 11:18:20 +02:00
It is recommended to implement the classes from top to down (ie. start with Binder and Extractor) and to use a
2012-04-29 20:52:25 +02:00
namespace that has <[ Poco::Data ]> as parent, e.g.<[ Poco::Data::SQLite ]>.
!!!AbstractBinder
2022-07-07 11:18:20 +02:00
An <[AbstractBinder]> is a class that maps values to placeholders. It is also responsible to bind primitive C++ data types to database
data types. The constructor of the subclass should receive everything needed to bind variables to
2012-04-29 20:52:25 +02:00
placeholders by position. An example taken from the SQLite implementation would be:
Binder::Binder(sqlite3_stmt* pStmt):
_pStmt(pStmt)
{
}
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
void Binder::bind(std::size_t pos, const Poco::Int32& val)
{
int rc = sqlite3_bind_int(_pStmt, (int)pos, val);
checkReturn(rc);
}
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
void Binder::bind(std::size_t pos, const Poco::Int16& val)
{
Poco::Int32 tmp = val;
bind(pos, tmp);
}
----
SQLite only needs an <*sqlite3_stmt*> as internal state, Int32 is bound via <*sqlite3_bind_int*> and Int16 values are mapped to Int32 values.
!!Complete Interface
All methods are public.
AbstractBinder();
/// Creates the AbstractBinder.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual ~AbstractBinder();
/// Destroys the AbstractBinder.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const Poco::Int8 &val) = 0;
/// Binds an Int8.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const Poco::UInt8 &val) = 0;
/// Binds an UInt8.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const Poco::Int16 &val) = 0;
/// Binds an Int16.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const Poco::UInt16 &val) = 0;
/// Binds an UInt16.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const Poco::Int32 &val) = 0;
/// Binds an Int32.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const Poco::UInt32 &val) = 0;
/// Binds an UInt32.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const Poco::Int64 &val) = 0;
/// Binds an Int64.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const Poco::UInt64 &val) = 0;
/// Binds an UInt64.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const bool &val) = 0;
/// Binds a boolean.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const float &val) = 0;
/// Binds a float.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const double &val) = 0;
/// Binds a double.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const char &val) = 0;
/// Binds a single character.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const char* const &pVal) = 0;
/// Binds a const char ptr.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const std::string& val) = 0;
/// Binds a string.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bind(std::size_t pos, const BLOB& val) = 0;
/// Binds a BLOB.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void reset() = 0;
/// Resets the internal state, called before a rebind
----
!!!AbstractExtractor
An <[AbstractExtractor]> takes a result row and extracts from a given position one single value. It performs the reverse operation to the <[AbstractBinder]>,
ie. it maps database types to primitive C++ types. An <[AbstractExtractor]> also has to handle null values. If it detects a null value, it is not allowed to modify
the incoming value but will simply return false. An example taken from the SQLite implementation:
Extractor::Extractor(sqlite3_stmt* pStmt):
_pStmt(pStmt)
{
}
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
bool Extractor::extract(std::size_t pos, Poco::Int32& val)
{
if (isNull(pos<[
return false;
val = sqlite3_column_int(_pStmt, (int)pos);
return true;
}
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
bool Extractor::extract(std::size_t pos, Poco::Int16& val)
{
if (isNull(pos<[
return false;
val = sqlite3_column_int(_pStmt, (int)pos);
return true;
}
----
!!Complete Interface
All methods are public.
AbstractExtractor();
/// Creates the AbstractExtractor.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual ~AbstractExtractor();
/// Destroys the AbstractExtractor.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, Poco::Int8& val) = 0;
/// Extracts an Int8. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, Poco::UInt8& val) = 0;
/// Extracts an UInt8. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, Poco::Int16& val) = 0;
/// Extracts an Int16. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, Poco::UInt16& val) = 0;
/// Extracts an UInt16. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, Poco::Int32& val) = 0;
/// Extracts an Int32. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, Poco::UInt32& val) = 0;
/// Extracts an UInt32. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, Poco::Int64& val) = 0;
/// Extracts an Int64. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, Poco::UInt64& val) = 0;
/// Extracts an UInt64. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, bool& val) = 0;
/// Extracts a boolean. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, float& val) = 0;
/// Extracts a float. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, double& val) = 0;
/// Extracts a double. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, char& val) = 0;
/// Extracts a single character. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, std::string& val) = 0;
/// Extracts a string. Returns false if null was received.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool extract(std::size_t pos, BLOB& val) = 0;
/// Extracts a BLOB. Returns false if null was received.
----
!!!AbstractPreparation
<[AbstractPreparation]> is an optional interface responsible for preparing an extract. If you need it depends on the <[DataConnector]> you implement. For example, SQLite can do perfectly without it, ODBC instead requires it.
SQLite doesn't need it because it works as follows:
* sendQuery
* getNextResult
* extract single row values from result set
This works because SQLites <*getNextResult*> provides the data as string, i.e. it doesn't need any type information.
The ODBC implementation is different:
* register/prepare for each column an output location
* getNextResult
* extract for each row the value by copying the content of the previously registered output location
<[AbstractPreparation]> is responsible for the first step. A typical prepare implementation will look like that:
void prepare(std::size_t pos, Poco::Int32 val)
{
_myVec[pos] = Poco::Any a(val);
int* i = AnyCast<int>(&_myVec[pos]);
//register int* i for output, Db specific
2022-07-07 11:18:20 +02:00
}
2012-04-29 20:52:25 +02:00
----
Extract now changes to:
bool Extractor::extract(std::size_t pos, Poco::Int16& val)
{
if (isNull(pos))
return false;
val = AnyCast<int>(_myVec[pos]);
return true;
}
----
!!Complete Interface
AbstractPreparation();
/// Creates the AbstractPreparation.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual ~AbstractPreparation();
/// Destroys the AbstractPreparation.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, Poco::Int8) = 0;
/// Prepares an Int8.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, Poco::UInt8) = 0;
/// Prepares an UInt8.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, Poco::Int16) = 0;
/// Prepares an Int16.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, Poco::UInt16) = 0;
/// Prepares an UInt16.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, Poco::Int32) = 0;
/// Prepares an Int32.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, Poco::UInt32) = 0;
/// Prepares an UInt32.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, Poco::Int64) = 0;
/// Prepares an Int64.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, Poco::UInt64) = 0;
/// Prepares an UInt64.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, bool) = 0;
/// Prepares a boolean.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, float) = 0;
/// Prepares a float.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, double) = 0;
/// Prepares a double.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, char) = 0;
/// Prepares a single character.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, const std::string& ) = 0;
/// Prepares a string.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void prepare(std::size_t pos, const BLOB&) = 0;
----
2012-09-26 01:39:33 +02:00
Note that it is recommended to prepare a statement only once in the compileImpl of <[StatementImpl]>. The AbstractPreparator objects (which make use of <[AbstractPreparation]>
2012-04-29 20:52:25 +02:00
can be created by iterating over the Extractor objects of the StatementImpl:
Poco::Data::AbstractExtractingVec::iterator it = extractings().begin();
Poco::Data::AbstractExtractingVec::iterator itEnd = extractings().end();
std::size_t pos = 0; // sqlite starts with pos 0 for results! your DB maybe with 1
for (; it != itEnd; ++it)
{
2012-09-26 01:39:33 +02:00
AbstractPreparator* pPrep = (*it)->createPrepareObject(pPreparation, pos);
2012-04-29 20:52:25 +02:00
_prepareVec.push_back(pPrep);
(*it)->extract(pos);
pos += (*it)->numOfColumnsHandled();
}
----
!!!StatementImpl
A <[StatementImpl]> stores as member a Binder and an Extractor (optional a Preparation object) and is responsible for compiling, binding, fetching single rows from the database and invoking the <*Extracting*> objects.
The interface it has to implement is given as:
public:
StatementImpl();
/// Creates the StatementImpl.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual ~StatementImpl();
/// Destroys the StatementImpl.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
protected:
virtual bool hasNext() = 0;
2022-07-07 11:18:20 +02:00
/// Returns true if a call to next() will return data. Note that the
/// implementation must support several consecutive calls to hasNext
/// without data getting lost, ie. hasNext(); hasNext(); next() must
2012-04-29 20:52:25 +02:00
/// be equal to hasNext(); next();
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void next() = 0;
/// Retrieves the next row from the resultset.
/// Will throw, if the resultset is empty.
/// Expects the statement to be compiled and bound
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual bool canBind() const = 0;
/// Returns if another bind is possible.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void compileImpl() = 0;
/// Compiles the statement, doesn't bind yet.
2022-07-07 11:18:20 +02:00
/// From now on AbstractBinder and AbstractExtractor
2012-04-29 20:52:25 +02:00
/// will be used
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual void bindImpl() = 0;
/// Binds parameters.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual AbstractExtractor& extractor() = 0;
/// Returns the concrete extractor used by the statement.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
virtual AbstractBinder& binder() = 0;
2022-07-07 11:18:20 +02:00
/// Returns the concrete binder used by the statement.
2012-04-29 20:52:25 +02:00
----
The Extracting and Binding objects can be accessed via the calls to the super-class methods <*extractings()*> and <*bindings()*>.
A high-level <*bind*> implementation will look like this:
[...]
Poco::Data::AbstractBindingVec& binds = bindings();
std::size_t pos = 1; // or 0 depending on your database
Poco::Data::AbstractBindingVec::iterator it = binds.begin();
Poco::Data::AbstractBindingVec::iterator itEnd = binds.end();
for (; it != itEnd && (*it)->canBind(); ++it)
{
(*it)->bind(pos);
pos += (*it)->numOfColumnsHandled();
}
----
A high-level <*next*> implementation:
if (!hasNext())
throw Poco::Data::DataException("No data received");
int nCol = countColumnsInResult...;
2022-07-07 11:18:20 +02:00
poco_assert (columnsHandled() == nCol);
2012-04-29 20:52:25 +02:00
Poco::Data::AbstractExtractingVec::iterator it = extractings().begin();
Poco::Data::AbstractExtractingVec::iterator itEnd = extractings().end();
std::size_t pos = 0; // sqlite starts with pos 0 for results! your DB maybe with 1
for (; it != itEnd; ++it)
{
(*it)->extract(pos);
pos += (*it)->numOfColumnsHandled();
}
enableHasNext();
----
A high-level <*hasNext*> implementation:
if (enabledhasNext())
{
checkIfItHasMoreData
cacheResult
disablehasNext()
}
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
return cachedResult;
----
A high-level <*compileImpl*>:
if (compiled)
return;
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
std::string sqlStmt(toString());
if database expects placeholders in different format than ":name", parse and replace them
compile statement;
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
create Binder;
create Extractor;
----
A high-level <*canBind*>:
bool ret = false;
if (!bindings().empty() && validCompiledStatement)
ret = (*bindings().begin())->canBind();
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
return ret;
----
!!!SessionImpl
The purpose of the <[SessionImpl]> is simply to open/close a connection to the database, to act as factory for <[StatementImpl]> objects, and to handle transactions.
The connection is opened in the constructor, and closed in the destructor.
Poco::Data::StatementImpl* createStatementImpl();
/// Returns an SQLite StatementImpl
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
void begin();
/// Starts a transaction
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
void commit();
/// Commits and ends a transaction
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
void rollback();
/// Aborts a transaction
----
!!!Connector
Finally, one needs to implement the <[Connector]>.
Each <[Connector]> should have a public static const string member named <*KEY*> and must have a factory method to <*create*> <[ Poco::AutoPtr ]> objects of type <[SessionImpl]>.
It should also have a static <*addToFactory()*> and a static <*removeFromFactory()*> method:
class My_API Connector: public Poco::Data::Connector
/// Connector instantiates SessionImpl objects.
{
public:
static const std::string KEY;
/// Keyword for creating sessions
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
Connector();
/// Creates the Connector.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
~Connector();
/// Destroys the Connector.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
Poco::AutoPtr < Poco::Data::SessionImpl > createSession(const std::string& connectionString);
/// Creates a SessionImpl object and initializes it with the given connectionString.
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
static void registerConnector();
/// Registers the Connector under the Keyword Connector::KEY at the Poco::Data::SessionFactory
2022-07-07 11:18:20 +02:00
2012-04-29 20:52:25 +02:00
static void unregisterConnector();
/// Unregisters the Connector under the Keyword Connector::KEY at the Poco::Data::SessionFactory
};
----