/// 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:
/// 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:
Note that it is recommended to prepare a statement only once in the compileImpl of <[StatementImpl]>. The AbstractPreparator objects (which make use of <[AbstractPreparation]>
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.
virtual ~StatementImpl();
/// Destroys the StatementImpl.
protected:
virtual bool hasNext() = 0;
/// 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
/// be equal to hasNext(); next();
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
virtual bool canBind() const = 0;
/// Returns if another bind is possible.
virtual void compileImpl() = 0;
/// Compiles the statement, doesn't bind yet.
/// From now on AbstractBinder and AbstractExtractor
/// will be used
virtual void bindImpl() = 0;
/// Binds parameters.
virtual AbstractExtractor& extractor() = 0;
/// Returns the concrete extractor used by the statement.
virtual AbstractBinder& binder() = 0;
/// Returns the concrete binder used by the statement.
----
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:
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()
}
return cachedResult;
----
A high-level <*compileImpl*>:
if (compiled)
return;
std::string sqlStmt(toString());
if database expects placeholders in different format than ":name", parse and replace them
compile statement;
create Binder;
create Extractor;
----
A high-level <*canBind*>:
bool ret = false;
if (!bindings().empty() && validCompiledStatement)
ret = (*bindings().begin())->canBind();
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
void begin();
/// Starts a transaction
void commit();
/// Commits and ends a transaction
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