8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 00:03:02 +01:00
firebird-mirror/doc/Using_OO_API.html

3835 lines
212 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title></title>
<meta name="generator" content="LibreOffice 7.1.5.2 (Linux)"/>
<meta name="author" content="alex "/>
<meta name="created" content="2013-05-31T00:00:00.010003100"/>
<meta name="changed" content="2022-06-24T17:47:03.455745039"/>
<meta name="created" content="00:00:00">
<meta name="created" content="00:00:00">
<meta name="created" content="00:00:00">
<meta name="created" content="00:00:00">
<meta name="created" content="00:00:00">
<meta name="created" content="00:00:00">
<meta name="created" content="00:00:00">
<meta name="created" content="00:00:00">
<style type="text/css">
@page { margin: 2.01cm }
h1 { color: #000000 }
p { margin-bottom: 0.2cm; color: #000000 }
</style>
</head>
<body lang="en-US" text="#000000" dir="ltr"><p style="margin-top: 0.43cm; margin-bottom: 0.51cm; page-break-after: avoid">
<font face="Albany, sans-serif"><font size="5" style="font-size: 18pt">Firebird
interfaces.</font></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Firebird's
OO API is based on use of interfaces. That interfaces, though looking
in some aspects like OLE2 interfaces (some of them have addRef() and
release() methods) are non standard and have features, missing in
other widely used types of interfaces. First of all Firebird
interfaces are </font><font size="4" style="font-size: 14pt"><b>language
independent</b></font> <font size="4" style="font-size: 14pt">that
means that to define/use them one need not use language specific
constructions like </font><font size="4" style="font-size: 14pt"><i>class</i></font>
<font size="4" style="font-size: 14pt">in C++, interface may be
defined using any language able to call functions using C calling
conventions and having concepts of array and pointer to
procedure/function. Next interfaces are </font><font size="4" style="font-size: 14pt"><b>versioned</b></font>
<font size="4" style="font-size: 14pt">i.e. we support different
versions of same interface. Binary layout of interfaces is designed
to support that features very efficient (there is no need in
additional virtual calls like in OLE2/COM with it's </font><strong><font size="4" style="font-size: 14pt">QueryInterface)</font></strong><strong>
</strong><strong><font size="4" style="font-size: 14pt">but it's not
convenient for direct use from most languages. Therefore
language-specific wrappers should better be designed for different
languages making use of API easier. Currently we have wrappers for
C++ and Pascal, Java is coming soon. From end-user POV calls from C++
and Pascal have absolutely no difference, though some additional
language-specific features present in C++ (like ability to turn off
automatic status check after API calls) are missing in Pascal.</font></strong></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm; font-weight: normal"><font size="4" style="font-size: 14pt">Typically
database API is used to access data stored in database. Firebird OO
API certainly performs this task but in addition it supports writing
your own </font><font size="4" style="font-size: 14pt"><b>plugins</b></font>
<font size="4" style="font-size: 14pt">modules, making it
possible to enhance Firebird capabilities according to your needs.
Therefore this document contains 2 big parts accessing databases
and writing plugins. Certainly some interfaces (like status vector)
are used in both parts of API, they will be discussed in data access
part and freely referenced later when discussing plugins. Therefore
even if you plan to write some plugin you should better start with
the first part of this document. Moreover a lot of plugins need to
access databases themselves and data access API is typically needed
for it.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="result_box"></a><font size="4" style="font-size: 14pt">Firebird
installation package contains a number of live samples of use of OO
API they are in examples/interfaces (database access) and
examples/dbcrypt (plugin performing fictitious database encryption)
directories. It's supposed that the reader is familiar with ISC API
used in Firebird since interbase times.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">This
document does not pretend to be a full Firebird 3 documentation
it just describes new object oriented API, and a reader should be
familiar with main Firebird concepts, knowledge about ISC API is also
much welcome. For example when describing how to work with
services there is no explanation what is service and why is it
needed, only description of how to obtain <a href="#Service">IService</a>
interface and how to use it. Also pay attention that samples of code
do not use a lot of powerful features of C++. Not used reference
counted pointers, not used other RAII holders, not used templates
(except one present in firebird public headers), etc. Primary goal is
to make this text usable not only to C++ people because our API is
oriented to support not only C++ but other, more simple languages
too.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-top: 0.43cm; margin-bottom: 0.51cm; page-break-after: avoid">
<font face="Albany, sans-serif"><font size="5" style="font-size: 18pt">Accessing
databases.</font></font></p>
<h1><font size="4" style="font-size: 14pt">Creating database and
attaching to existing database.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">First
of all we need to get access to <b>IMaster</b> interface. IMaster is
primary Firebird interface, required to access all the rest of
interfaces. Therefore there is a special way of accessing it the
only one needed to use OO API plain function called
fb_get_master_interface(). This function has no parameters and always
succeeds. There is one and only one instance of IMaster per Firebird
client library, therefore one need not care about releasing memory,
used by master interface. A simplest way to access it from your
program is to have appropriate global or static variable:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>static
<a href="#Master">IMaster</a>* master = fb_get_master_interface();</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">For
a lot of methods, used in Firebird API, first parameter is <b>IStatus</b>
interface. It's a logical replacement of ISC_STATUS_ARRAY, but works
separately with errors and warnings (not mixing them in same array),
can contain unlimited number of errors inside and (this will be
important if you plan to implement IStatus yourself) always keeps
strings, referenced by it, inside interface. Typically you need at
least one instance of IStatus to call other methods. You obtain it
from IMaster:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#Status">IStatus</a>*
st = master-&gt;getStatus();</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">If
method getStatus() fails for some reason (OOM for example) it returns
NULL obviously we can't use generic error reporting method which
is based on use of IStatus here.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Now
we are going to deal with first interface, directly related to
database calls. This is <a href="#Provider">IProvider</a>
interface called this way cause it's exactly that interface that must
be implemented by any provider in Firebird. Firebird client library
also has it's own implementation of IProvider, which must be used to
start any database activity. To obtain it we call IMaster's method:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>IProvider*
prov = master-&gt;getDispatcher();</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">When
attaching to existing database or moreover creating new one it's
often necessary to pass a lot of additional parameters
(logon/password, page size for new database, etc.) to API call.
Having separate language-level parameters is close to unreal we
will have to modify a call too often to add new parameters, and
number of them will be very big no matter of the fact that typically
one needs to pass not too much of them. Therefore to pass additional
parameters special in-memory data structure, called </font><font size="4" style="font-size: 14pt"><i>database
parameters block</i></font> <font size="4" style="font-size: 14pt">(DPB)
is used. Format of it is well defined, and it's possible to build DPB
byte after byte. But it's much easier to use special interface
</font><a href="#XpbBuilder"><font size="4" style="font-size: 14pt"><b>IXpbBuilder</b></font></a><font size="4" style="font-size: 14pt">,
which simplifies creation of various parameters blocks. To obtain an
instance of IXpbBuilder you must know one more generic-use interface
of firebird API </font><a href="#Util"><font size="4" style="font-size: 14pt"><b>IUtil</b></font></a><font size="4" style="font-size: 14pt">.
It's a kind of placeholder for the calls that do not fit well in
other places. So we do</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>IUtil*
utl = master-&gt;getUtilInterface();</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>IXpbBuilder*
dpb = utl-&gt;getXpbBuilder(&amp;status, IXpbBuilder::DPB, NULL, 0);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">This
creates empty parameters' block builder of DPB type. Now adding
required parameter to it is trivial:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>dpb-&gt;insertInt(&amp;status,
isc_dpb_page_size, 4 * 1024);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">will
make firebird to create new database with pagesize equal to 4Kb and
meaning of</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>dpb-&gt;insertString(&amp;status,
isc_dpb_user_name, “sysdba”);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>dpb-&gt;insertString(&amp;status,
isc_dpb_password, “masterkey”);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">is
(I hope) obvious.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Status Wrapper"></a><font size="4" style="font-size: 14pt"><u>The
following is C++ specific: </u>We are almost ready to call
createDatabase() method of IProvider, but before it a few words about
concept of <b>Status Wrapper</b> should be said. Status wrapper is
not an interface, it's very thin envelope for IStatus interface. It
helps to customize behavior of C++ API (change a way how errors,
returned in IStatus interface, are processed). For the first time we
recommend use of <b>ThrowStatusWrapper</b>, which raises C++
exception each time an error is returned in IStatus.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>ThrowStatusWrapper
status(st);</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Now
we may create new empty database:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#Attachment">IAttachment</a>*
att = prov-&gt;createDatabase(&amp;status, &quot;fbtests.fdb&quot;,
dpb-&gt;getBufferLength(&amp;status), dpb-&gt;getBuffer(&amp;status));</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>printf(&quot;Database
fbtests.fdb created\n&quot;);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Pay
attention that we do not check status after the call to
createDatabase(), because in case of error C++ or Pascal exception
will be raised (therefore it's very good idea to have
try/catch/except syntax in your program). We also use two new
functions from IXpbBuilder getBufferLength() and getBuffer(),
which extract data from interface in native parameters block format.
As you can see there is no need to check explicitly for status of
functions, returning intermediate results.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Detaching
from just created database is trivial:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>att-&gt;detach(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Now
it remains to enclose all operators into <i>try</i> block and write a
handler in catch block. When using ThrowStatusWrapper you should
always catch defined in C++ API exception class FbException, in
Pascal you must also work with class FbException. Exception handler
block in simplest case may look this way:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>catch
(const FbException&amp; error)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>char
buf[256];</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>utl-&gt;formatStatus(buf,
sizeof(buf), error.getStatus());</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>fprintf(stderr,
&quot;%s\n&quot;, buf);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Pay
attention that here we use one more function from <a href="#Util">IUtil</a>
formatStatus(). It returns in buffer text, describing an error
(warning), stored in IStatus parameter.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">To
attach to existing database just use attachDatabase() method of
IProvider instead createDatabase(). All parameters are the same for
both methods.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>att
= prov-&gt;attachDatabase(&amp;status, &quot;fbtests.fdb&quot;, 0,
NULL);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">This
sample is using no additional DPB parameters. Take into account that
without logon/password any remote connection will fail if no trusted
authorization plugin is configured. Certainly login info may be also
provided in environment (in ISC_USER and ISC_PASSWORD variables) like
it was before.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Our
examples contain complete samples, dedicated except others to
creating databases 01.create.cpp and 01.create.pas. When samples
are present it will be very useful to build and try to run
appropriate samples when reading this document.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Working with transactions.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Only
creating empty databases is definitely not enough to work with RDBMS.
We want to be able to create various objects (like tables and so on)
in database and insert data in that tables. Any operation within
database is performed by firebird under transaction control.
Therefore first of all we must learn to start transaction. Here we do
not discuss distributed transactions (supported by <a href="#Dtc">IDtc</a>
interface) to avoid unneeded to most users overcomplication. Starting
of non-distributed transaction is very simple and done via attachment
interface:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#Transaction">ITransaction</a>*
tra = att-&gt;startTransaction(&amp;status, 0, NULL);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">In
this sample default transaction parameters are used no TPB is
passed to startTransaction() method. If you need non-default
parameters you may create appropriate <a href="#XpbBuilder">IXpbBuilder</a>,
add required items to it:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#XpbBuilder">IXpbBuilder</a>*
tpb = utl-&gt;getXpbBuilder(&amp;status, IXpbBuilder::TPB, NULL, 0);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>tpb-&gt;insertTag(&amp;status,
isc_tpb_read_committed);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">and
pass resulting TPB to startTransaction():</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>ITransaction*
tra = att-&gt;startTransaction(&amp;status,
tpb-&gt;getBufferLength(&amp;status), tpb-&gt;getBuffer(&amp;status));</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Transaction
interface is used as a parameter in a lot of other API calls but
itself it does not perform any actions except commit/rollback
transaction, may be retaining:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>tra-&gt;commit(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">You
may take a look at how to start and commit transaction in examples
01.create.cpp and 01.create.pas.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Executing SQL operator
without input parameters and returned rows.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">With
started transaction we are ready to execute our first SQL operators.
Used for it execute() method in <a href="#Attachment">IAttachment</a>
is rather universal and may be also used to execute SQL operators
with input and output parameters (which is typical for EXECUTE
PROCEDURE statement), but right now we will use the simple most form
of it. Both DDL and DML operators can be executed:</font></p>
<p style="margin-bottom: 0cm; font-weight: normal"><font size="4" style="font-size: 14pt"><i>att-&gt;execute(&amp;status,
tra, 0, &quot;create table dates_table (d1 date)&quot;,
SQL_DIALECT_V6, NULL, NULL, NULL, NULL);</i></font></p>
<p style="margin-bottom: 0cm; font-weight: normal"><font size="4" style="font-size: 14pt"><i>tra-&gt;commitRetaining(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm; font-weight: normal"><font size="4" style="font-size: 14pt"><i>att-&gt;execute(&amp;status,
tra, 0, &quot;insert into dates_table values (CURRENT_DATE)&quot;,
SQL_DIALECT_V6, NULL, NULL, NULL, NULL);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">As
you can see transaction interface is a required parameter for
execute() method (must be NULL only if you execute START TRANSACTION
statement). Next follows length of SQL operator (may be zero causing
use of C rules to determine string length), text of operator and SQL
dialect that should be used for it. The following for NULLs stand for
metadata descriptions and buffers of input parameters and output
data. Complete description of this method is provided in <a href="#Attachment">IAttachment</a>
interface.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">You
may take a look at how to start and commit transaction in examples
01.create.cpp and 01.create.pas.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Executing SQL operator
with input parameters.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">There
are 2 ways to execute statement with input parameters. Choice of
correct method depends upon do you need to execute it more than once
and do you know in advance format of parameters. When that format is
known and statement is needed to be run only once single call to
IAttachment::execute() may be used. In other cases SQL statement
should be prepared first and after it executed, may be many times
with different parameters.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">To
prepare SQL statement for execution use prepare() method of
<a href="#Attachment">IAttachment</a> interface:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#Statement">IStatement</a>*
stmt = att-&gt;prepare(&amp;status, tra, 0, “UPDATE department SET
budget = ? * budget + budget WHERE dept_no = ?”,</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>SQL_DIALECT_V6,
IStatement::PREPARE_PREFETCH_METADATA);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">If
you are not going to use parameters description from firebird (i.e.
you can provide that information yourself) please use
IStatement::PREPARE_PREFETCH_NONE instead PREPARE_PREFETCH_METADATA
this will save client/server traffic and server resources a bit.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">In
ISC API XSQLDA is used to describe format of statement parameters.
New API does not use XSQLDA instead interface IMessageMetadata is
used. A set of input parameters (and also a row fetched from cursor)
is described in firebird API in same way and later called message.
<a href="#MessageMetadata">IMessageMetadata</a> is passed as a
parameter to the methods performing message exchange between your
program and database engine. There are many ways to have an instance
of IMessageMetadata one can:</font></p>
<ul>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">get
it from <a href="#Statement">IStatement</a>,</font></p>
</ul>
<ul>
<li value="1"><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">build
using <a href="#MetadataBuilder">IMetadataBuilder</a> interface,</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">have
your own implementation of this interface.</font></p>
</ul>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Getting
metadata from prepared statement is very simple method
getInputMetadata() return interface describing input message (i.e.
statement parameters), interface returned by getOutputMetadata()
describes output message (i.e. row in selected data or values
returned by procedure). In our case we can:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#MessageMetadata">IMessageMetadata</a>*
meta = stmt-&gt;getInputMetadata(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Or
we can build message metadata ourself. First of all we need builder
interface for it:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#MetadataBuilder">IMetadataBuilder</a>*
builder = master-&gt;getMetadataBuilder(&amp;status, 2);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Second
parameter is expected number of fields in the message, it can be
changed later, i.e. that's just an optimization.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Now
it's necessary to set individual fields characteristics in the
builder. An absolute minimum is field types and length for string
fields:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>builder-&gt;setType(&amp;status,
0, SQL_DOUBLE + 1);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>builder-&gt;setType(&amp;status,
1, SQL_TEXT + 1);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>builder-&gt;setLength(&amp;status,
1, 3);</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">New API is using old constants
for SQL types, smallest bit as earlier stands for nullability. In
some case it may also make sense to set sub-type (for blobs),
character set (for text fields) or scale (for numeric fields). And
finally it's time to get an instance of IMessageMetadata:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#MessageMetadata">IMessageMetadata</a>*
meta = builder-&gt;getMetadata(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Here we do not discuss in
details own implementation of IMessageMetadata. If one cares there is
a sample 05.user_metadata.cpp.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">So
finally we have obtained (one or another way) an instance of metadata
description of input parameters. But to work with a message we also
need buffer for it. Buffer size is one of main message metadata
characteristics and it's returned by method in IMessageMetadata:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>char*
buffer = new char[meta-&gt;getMessageLength(&amp;status)];</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">To
deal with individual values inside buffer offset to them should be
taken into an account. IMessageMetadata is aware of offsets for all
values in a message, using it we can create pointers to them:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>double*
percent_inc = (double*) &amp;buffer[meta-&gt;getOffset(&amp;status,
0)];</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>char*
dept_no = &amp;buffer[meta-&gt;getOffset(&amp;status, 1)];</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Also
let's do not forget to set to NOT NULL null flags:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>short*
flag = (short*)&amp;buffer[meta-&gt;getNullOffset(&amp;status, 0)];</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>*flag
= 0;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>flag
= (short*) &amp;buffer[meta-&gt;getNullOffset(&amp;status, 1)];</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>*flag
= 0;</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">After
finishing with offsets we are ready to execute statement with some
parameters values:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>getInputValues(dept_no,
percent_inc);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">and
may execute prepared statement:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>stmt-&gt;execute(&amp;status,
tra, meta, buffer, NULL, NULL);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Two
more NULLs in the end of parameters stand for output message and is
used typically for EXECUTE PROCEDURE statement.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">If
you do not need to get metadata from statement and plan to execute it
only once you may choose a simpler way use method execute() in
<a href="#Attachment">IAttachment</a> interface:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>att-&gt;execute(&amp;status,
tra, 0, &quot;UPDATE department SET budget = ? * budget + budget
WHERE dept_no = ?&quot;, SQL_DIALECT_V6, meta, buffer, NULL, NULL);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">In
that case you do not need to use <a href="#Statement">IStatement</a>
at all.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">An
example how to execute UPDATE with parameters is present in
02.update.cpp, you will also see how raised in trigger/procedure
exception may be caught by C++ program.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Opening cursor and
fetching data from it.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">The
only way to get rows of data, returned by SELECT operator, in OO API
is to use <a href="#ResultSet">IResultSet</a> interface. This
interface is returned by openCursor() method in both IAttachment and
IStatement. openCursor() is in most aspects alike execute() and a
choice how to open cursor (using prepared statement or directly from
attachment interface) is the same. In the samples 03.select.cpp and
04.print_table.cpp both methods are used. Let's pay attention at one
specific openCursor() feature compared with execute() - one does not
pass buffer for output message into openCursor(), it will be passed
later when data is fetched from cursor. This makes it possible to
open cursor with unknown format of output message (NULL is passed
instead output metadata). Firebird is using in this case default
message format which may be requested from IResultSet interface:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>const
char* sql = &quot;select * from ...&quot;; // some select statement</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#ResultSet">IResultSet</a>*
curs = att-&gt;openCursor(&amp;status, tra, 0, sql, SQL_DIALECT_V6,
NULL, NULL, NULL, NULL, 0);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#MessageMetadata">IMessageMetadata</a>*
meta = curs-&gt;getMetadata(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Later
this metadata may be used to allocate buffer for data and parse
fetched rows.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">As
an alternative one can first prepare statement, get metadata from
prepared statement and after it open cursor. This is preferred way if
cursor is likely to be opened &gt;1 times.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><a href="#Statement">IStatement</a>*
stmt = att-&gt;prepare(&amp;status, tra, 0, sql, SQL_DIALECT_V6,
Istatement::PREPARE_PREFETCH_METADATA);</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><a href="#MessageMetadata">IMessageMetadata</a>*
meta = stmt-&gt;getOutputMetadata(&amp;status);</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><a href="#ResultSet">IResultSet</a>*
curs = stmt-&gt;openCursor(&amp;status, tra, NULL, NULL, NULL, 0);</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">We
have obtained (one or another way) an instance of metadata
description of output fields (a row in a set). To work with a message
we also need buffer for it:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>unsigned
char* buffer = new unsigned char[meta-&gt;getMessageLength(&amp;status)];</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IResultSet
has a lot of various fetch methods but when cursor is not opened with
SCROLL option only fetchNext() works, i.e. one can navigate only
forward record by record. In addition to errors and warnings in
status fetchNext() returns completion code, which may have values
RESULT_OK (when buffer is filled with values for next row) or
RESULT_NO_DATA (when no more rows left in cursor). RESULT_NO_DATA is
not error state, it's normal state after completion of the method,
but we know that data in cursor is over. If status wrapper, not
throwing exception in case of error return is used, one more value
RESULT_ERROR may be returned, that means no data in buffer and
error vector in status. Method fetchNext() is usually called in a
cycle:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>while
(curs-&gt;fetchNext(&amp;status, buffer) == IStatus::RESULT_OK)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>//
row processing</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">What
is done during row processing depends upon your needs. To access
particular field field's offset should be used:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>unsigned
char* field_N_ptr = buffer + meta-&gt;getOffset(&amp;status, n);</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">where n is the number of a
field in a message. That pointer should be casted to appropriate
type, depending upon field type. For example, for a VARCHAR field
cast to struct vary should be used:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>vary*
v_ptr = (vary*) (buffer + meta-&gt;getOffset(&amp;status, n));</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Now
we may print the value of a field:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>printf(“field
%s value is %*.*s\n”, meta-&gt;getField(&amp;status, n),
v_ptr-&gt;vary_length, v_ptr-&gt;vary_length, v_ptr-&gt;vary_string);</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">If
you need maximum performance it will be good idea to cache needed
metadata values like it's done in our samples 03.select.cpp and
04.print_table.cpp.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Using FB_MESSAGE macro for
static messages.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Working
with data using offsets is rather efficient but requires a lot of
code to be written. In C++ this problem can be solved using
templates, but even compared with them the most convenient way to
work with the message is to represent it in native (for given
language) way structure in C/C++, record in Pascal, etc.
Certainly this works only if format of a message is known in advance.
To help building such structures in C++ firebird contains special
macro FB_MESSAGE.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_MESSAGE
has 3 arguments message (struct) name, type of status wrapper and
list of fields. Usage of first and second is obvious, list of fields
contains pairs (field_type, field_name), where field_type is one of
the following:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_BIGINT</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_BLOB</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_BOOLEAN</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_CHAR(len)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_DATE</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_DECFIXED(scale)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_DECFLOAT16</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_DECFLOAT34</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_DOUBLE</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_FLOAT</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_INTEGER</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_INTL_CHAR(len,
charSet)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_INTL_VARCHAR(len,
charSet)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_SCALED_BIGINT(scale)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_SCALED_INTEGER(scale)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_SCALED_SMALLINT(scale)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_SMALLINT</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_TIME</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_TIMESTAMP</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_VARCHAR(len)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">In
generated by preprocessor structure integer and float types are
matched with appropriate C types, date and time with classes
<a href="#FbDate">FbDate</a> and <a href="#FbTime">FbTime</a> (all
mentioned here classes are in namespace Firebird), timestamp with
class FbTimestamp, containing two public data members date and time
of appropriate class, char - with struct <a href="#FbChar">FbChar</a>
and varchar with struct <a href="#FbVarChar">FbVarChar</a>. For
each field preprocessor creates two data members in the message
</font><font size="4" style="font-size: 14pt"><i>name</i></font> <font size="4" style="font-size: 14pt">for
field/parameter value and </font><font size="4" style="font-size: 14pt"><i>nameNull</i></font>
<font size="4" style="font-size: 14pt">for NULL indicator. Message
constructor has 2 parameters pointer to status wrapper and master
interface:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>FB_MESSAGE(Output,
ThrowStatusWrapper,</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>(FB_SMALLINT,
relationId)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>(FB_CHAR(31),
relationName)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>(FB_VARCHAR(100),
description)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>)
output(&amp;status, master);</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">For
static messages use of FB_MESSAGE is sooner of all the best choice
they can be at the same time easily passed to execute, openCursor and
fetch methods:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>rs
= att-&gt;openCursor(&amp;status, tra, 0, sqlText,</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>SQL_DIALECT_V6,
NULL, NULL, output.getMetadata(), NULL, 0);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">and
used to work with values of individual fields:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>while
(rs-&gt;fetchNext(&amp;status, output.getData()) ==
IStatus::RESULT_OK)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>printf(&quot;%4d
%31.31s %*.*s\n&quot;, output-&gt;relationId,
output-&gt;relationName.str,</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>output-&gt;descriptionNull
? 0 : output-&gt;description.length,</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>output-&gt;descriptionNull
? 0 : output-&gt;description.length, output-&gt;description.str);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">An
example of using macro FB_MESSAGE to work with messages is in the
sample 06.fb_message.cpp.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Working with blobs.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">For
blobs in message buffer firebird stores blob identifier an 8-byte
entity which should be aligned on 4-byte boundary. Identifier has
ISC_QUAD type. Interface <a href="#Attachment">IAttachment</a> has 2
methods to work with blobs openBlob() and createBlob(), both
returning interface <a href="#Blob">IBlob</a> and having same set of
parameters, but performing somewhat contrary actions: openBlob()
takes blob identifier from the message and prepares blob for reading
but createBlob() creates new blob, puts it's identifier into message
and prepares blob for writing. </font>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">To
work with blobs one must first of all include blob identifier into
the message. If you get metadata from firebird engine field of
appropriate type will be already present. You just use it's offset
(assuming variable blobFieldNumber contains number of blob field)
(and appropriate null offset to check for nulls or set null flag) to
obtain pointer into the message buffer:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>ISC_QUAD*
blobPtr = (ISC_QUAD*) &amp;buffer[metadata-&gt;getOffset(&amp;status,
blobFieldNumber)];</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>ISC_SHORT*
blobNullPtr = (ISC_SHORT*) &amp;buffer[metadata-&gt;getNullOffset(&amp;status,
blobFieldNumber)];</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">If
you use static messages and FB_MESSAGE macro blob field is declared
as having FB_BLOB type:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>FB_MESSAGE(Msg,
ThrowStatusWrapper,</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>(FB_BLOB,
b)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>)
message(&amp;status, master);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>ISC_QUAD*
blobPtr = &amp;message-&gt;b;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>ISC_SHORT*
blobNullPtr = &amp;message-&gt;bNull;</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">To
create new blob invoke createBlob() method:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#Blob">IBlob</a>*
blob = att-&gt;createBlob(status, tra, blobPtr, 0, NULL);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Last
two parameters are required only if you want to use blob filters or
use stream blob, that's out of scope here.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Blob
interface is ready to accept data into blob. Use putSegment() method
to send data to engine:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>void*
segmentData;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>unsigned
segmentLength;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>while
(userFunctionProvidingBlobData(&amp;segmentData, &amp;segmentLength))</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>blob-&gt;putSegment(&amp;status,
segmentLength, segmentData);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">After
sending some data to blob do not forget to close blob interface:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>blob-&gt;close(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Make
sure that null flag is not set (not required if you nullified all
message buffer before creating blob):</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>*blobNullPtr
= 0;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">and
message, containing blob, may be used in insert or update statement.
After execution of that statement new blob will be stored in
database.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">To
read a blob begin with getting containing it's identifier message
from firebird engine. This may be done using fetch() or execute()
methods. After it use openBlob() attachment's method:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#Blob">IBlob</a>*
blob = att-&gt;openBlob(status, tra, blobPtr, 0, NULL);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Blob
interface is ready to provide blob data. Use getSegment() method to
receive data from engine:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>char
buffer[BUFSIZE];</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>unsigned
actualLength;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>for(;;)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>switch
(blob-&gt;getSegment(&amp;status, sizeof(buffer), buffer,
&amp;actualLength))</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>case
IStatus::RESULT_OK:</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>userFunctionAcceptingBlobData(buffer,
actualLength, true);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>continue;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>case
IStatus::RESULT_SEGMENT:</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>userFunctionAcceptingBlobData(buffer,
actualLength, false);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>continue;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>default:</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>break;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Last
parameter in userFunctionAcceptingBlobData() is a flag that end of
segment is reached when getSegment() returns RESULT_SEGMENT
completion code that function is notified (by passing false as last
parameter) that segment was not read completely and continuation is
expected at next call.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">After
finishing with blob do not forget to close it:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>blob-&gt;close(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><a name="Modifying data in a batch"></a><font size="4" style="font-size: 14pt">Modifying
data in a batch.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Since
version 4 firebird supports batch execution of statements with input
parameters that means sending more than single set of parameters
when executing statement. <a href="#Batch">Batch</a> interface is
designed (first of all) in order to satisfy JDBC requirements for
prepared statements batch processing but has some serious
differences:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">-
like all operations with data in firebird its oriented on
messages, not single field;</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">-
as an important extension out batch interface supports inline use of
blobs (specially efficient when working with small blobs);</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">-
execute() method returns not plain array of integers but special
<a href="#BatchCompletionState">BatchCompletionState</a> interface
which can (depending upon batch creation parameters) contain both
update records info and in addition to error flag detailed status
vectors for messages that caused execution errors.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><a href="#Batch">Batch</a>
(exactly like <a href="#ResultSet">ResultSet</a>) may be created in 2
ways using <a href="#Statement">Statement</a> or <a href="#Attachment">Attachment</a>
interface, in both cases createBatch() method of appropriate
interface is called. In second case text of SQL statement to be
executed in a batch is passed directly to createBatch(). Tuning of
batch operation is performed using <a href="#Batch_PB">Batch
parameters block</a> which has format more or less similar to DPB v.2
tag in the beginning (IBatch::CURRENT_VERSION) followed by the
set of wide clumplets: 1-byte tag, 4-byte length, length-byte value.
Possible tags are <a href="#Batch_PB">described</a> in batch
interface. The simplest (and recommended) way to create parameters
block for batch creation is to use appropriate <a href="#XpbBuilder">XpbBuilder</a>
interface:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>IXpbBuilder*
pb = utl-&gt;getXpbBuilder(&amp;status, IXpbBuilder::BATCH, NULL, 0);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>pb-&gt;insertInt(&amp;status,
IBatch::RECORD_COUNTS, 1);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Use
of such parameters block directs batch to account number of updated
records on per-message basis.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">To
create batch interface with desired parameters pass parameters block
to createBatch() call:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>IBatch*
batch = att-&gt;createBatch(&amp;status, tra, 0, sqlStmtText,
SQL_DIALECT_V6, NULL,</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>pb-&gt;getBufferLength(&amp;status),
pb-&gt;getBuffer(&amp;status));</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">In
this sample batch interface is created with default format of
messages cause NULL is passed instead input metadata format.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">In
order to proceed with created batch interface we need to know format
of messages in it. It can be obtained using getMetadata() method:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>IMessageMetadata*
meta = batch-&gt;getMetadata(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Certainly
if you have passed your own format of messages to the batch you may
simply use it.</font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">In the former text I suppose
that some function fillNextMessage(unsigned char* data,
IMessageMetadata* metadata) is present and can fill buffer data
according to passed format metadata. In order to work with
messages we need a buffer for a data:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>unsigned
char* data = new unsigned char[meta-&gt;getMessageLength(&amp;status)];</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Now we can add some messages
full of data to the batch:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>fillNextMessage(data,
meta);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>batch-&gt;add(&amp;status,
1, data);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>fillNextMessage(data,
meta);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>batch-&gt;add(&amp;status,
1, data);</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">An alternative way of working
with messages (using FB_MESSAGE macro) is present in the sample of
using batch interface 11.batch.cpp.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Finally batch should be
executed:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#BatchCompletionState">IBatchCompletionState</a>*
cs = batch-&gt;execute(&amp;status, tra);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">We
requested accounting of the number of modified (inserted, updated or
deleted) records per message. To print it we must use
<a href="#BatchCompletionState">BatchCompletionState</a> interface.
Determine total number of messages processed by batch (it can be less
than the number of messages passed to the batch if error happened and
an option enabling multiple errors during batch processing was not
turned on):</font></p>
<p><font size="4" style="font-size: 14pt"><i>unsigned total =
cs-&gt;getSize(&amp;status);</i></font></p>
<p><font size="4" style="font-size: 14pt">Now print the state of each
message:</font></p>
<p><font size="4" style="font-size: 14pt"><i>for (unsigned p = 0; p &lt;
total; ++p) printf(“Msg %u state %d\n”, p, cs-&gt;getState(&amp;status,
p));</i></font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">When
finished analyzing completion state dont forget to dispose it:</font></p>
<p><font size="4" style="font-size: 14pt"><i>cs-&gt;dispose();</i></font></p>
<p><font color="#000000"><font size="4" style="font-size: 14pt">Full
sample of printing contents</font></font><font color="#000000"> of
</font><font color="#000000"><font size="4" style="font-size: 14pt"><a href="#BatchCompletionState">BatchCompletionState</a>
is in print_cs() function in sample 11.batch.cpp.</font></font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">If
for some reason you want to make batch buffers empty not executing it
(i.e. prepare for new portion of messages to process) use cancel()
method:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>batch-&gt;cancel(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Like the rest of our data
access interfaces Batch has special method to close it:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>batch-&gt;close(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Standard release() call may be
used instead if one does not care about errors:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>batch-&gt;release();</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Described methods help to
implement all what one needs for JDBC-style prepared statement batch
operations. </font>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Notice:
JDBC does not recommend to use too large batches, for example “<a href="https://docs.oracle.com/cd/E11882_01/java.112/e16548/oraperf.htm#JJDBC28753">Oracle
recommends you to keep the batch sizes in the general range of 50 to
100</a>”. Firebird supports bigger batches but it anyway has to
limit maximum batch size see <a href="#TAG_BUFFER_BYTES_SIZE">TAG_BUFFER_BYTES_SIZE</a>.
If total size of messages exceeds that limit isc_batch_too_big error
is returned. Pay attention that due to deep buffering of batch data
when sending it over the wire you will not get this error at once but
only when buffers are flashed from client to server. This may happen
in both add() and execute() methods execute() performs final
flash. If you still wish to execute the batch with the messages that
fit in a buffer you may do it (when error was returned by execute()
just repeat it). An actual number of processed messages will be
returned in </font><font color="#000000"><font size="4" style="font-size: 14pt"><a href="#BatchCompletionState">BatchCompletionState</a>.
An optimal batch size should be found for each particular case but
sooner of all having it &gt;1000 hardly gives you serious performance
increase.</font></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p><font size="4" style="font-size: 14pt">One can add more than
single message in one call to the batch. When doing it please
remember messages should be appropriately aligned for this
feature to work correctly. Required alignment and aligned size of the
message should be obtained from <a href="#MessageMetadata">MessageMetadata</a>
interface, for example:</font></p>
<p><font size="4" style="font-size: 14pt"><i>unsigned aligned =
meta-&gt;getAlignedLength(&amp;status);</i></font></p>
<p><font size="4" style="font-size: 14pt">Later that size will be
useful when allocating an array of messages and working with it:</font></p>
<p><font size="4" style="font-size: 14pt"><i>unsigned char* data =
new unsigned char[aligned * N]; // N is desired number of messages</i></font></p>
<p><font size="4" style="font-size: 14pt"><i>for (int n = 0; n &lt;
N; ++n) fillNextMessage(&amp;data[aligned * n], meta);</i></font></p>
<p><font size="4" style="font-size: 14pt"><i>batch-&gt;add(&amp;status,
N, data);</i></font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">After
it batch may be executed or next portion of messages added to it.</font></p>
<p><br/>
<br/>
</p>
<p><font size="4" style="font-size: 14pt">Blobs in general are not
compatible with batches batch is efficient when one needs to pass
a lot of small data to the server in single step, blobs are treated
as large objects and therefore in general it makes no sense to use
them in batches. But on practice it often happens that blobs are not
too big and in this case use of traditional blob API (create
blob, pass segments to the server, close blob, pass blobs ID in the
message) kills performance, specially when used over WAN. Therefore
in firebird batch supports passing blobs to server inline, together
with other messages. To use that feature first of all <a href="#Batch_Blob_Policy">blob
usage policy</a> for a batch to be created should be set (as an
option in <a href="#Batch_PB">parameters block</a>):</font></p>
<p><font size="4" style="font-size: 14pt"><i>pb-&gt;insertInt(&amp;status,
IBatch::BLOB_IDS, IBatch::BLOB_IDS_ENGINE);</i></font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">In
this example temporal blob IDs needed to keep a link between blob and
a message where its used will be generated by firebird engine
thats the simplest and rather common usage. Imagine that the
message is described as follows:</font></p>
<p style="font-variant: normal"><font size="4" style="font-size: 14pt"><i>FB_MESSAGE(Msg,
ThrowStatusWrapper,</i></font></p>
<p><font size="4" style="font-size: 14pt"><i>(FB_VARCHAR(5), id)</i></font></p>
<p><font size="4" style="font-size: 14pt"><i>(FB_VARCHAR(10), name)</i></font></p>
<p><font size="4" style="font-size: 14pt"><i>(FB_BLOB, desc)</i></font></p>
<p><font size="4" style="font-size: 14pt"><i>) project(&amp;status,
master);</i></font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">In
that case to send a message containing blob to the server one can do
something like this:</font></p>
<p><font size="4" style="font-size: 14pt"><i>project-&gt;id =
++idCounter;</i></font></p>
<p><font size="4" style="font-size: 14pt"><i>project-&gt;name.set(currentName);</i></font></p>
<p><font size="4" style="font-size: 14pt"><i>batch-&gt;addBlob(&amp;status,
descriptionSize, descriptionText, &amp;project-&gt;desc);</i></font></p>
<p><font size="4" style="font-size: 14pt"><i>batch-&gt;add(&amp;status,
1, project.getData());</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">If some blob happened to be
big enough not to fit into your existing buffer you may instead
reallocating buffer use appendBlobData() method. It appends more data
to last added blob. </font>
</p>
<p style="font-variant: normal"><font size="4" style="font-size: 14pt"><i>batch-&gt;addBlob(&amp;status,
descriptionSize, descriptionText, &amp;project→desc, bpbLength,
bpb);</i></font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">After
adding first part of blob get next portion of data into
descriptionText, update descriptionSize and:</font></p>
<p style="font-variant: normal"><font size="4" style="font-size: 14pt"><i>batch-&gt;appendBlobData(&amp;status,
descriptionSize, descriptionText);</i></font></p>
<p><font size="4" style="font-size: 14pt">This may be done in a loop
but take care not to overflow internal batch buffers its size
is controlled by <a href="#Batch_PB">BUFFER_BYTES_SIZE</a> option
when creating batch interface but cant exceed 256Mb (default is
16Mb). If you need to process such big blob (for example on the
background of a lot of small one this can explain use of batch)
just use standard blob API and <a href="#Batch::registerBlob">registerBlob</a>
method of <a href="#Batch">Batch</a> interface.</font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">One
more possible choice of blob policy is BLOB_IDS_USER. Usage at the
first look does not change much before calling addBlob() correct
and unique per batch execution ID should be placed to the memory
referenced by last parameter. Certainly same ID should be passed in
the data message to the blob. Taking into an account that generation
of blob IDs by engine is very fast such policy may seem useless but
imagine a case when you get blobs and other data in relatively
independent streams (blocks in a file for example) and some good IDs
are already present in them. In such case use of user-supplied blob
IDs can greatly simplify your code.</font></p>
<p><font size="4" style="font-size: 14pt">Please take into an account
unlike blobs created using regular <a href="#Attachment">createBlob</a>()
blobs created by <a href="#Batch">Batch</a> interface are by default
stream, not segmented. Segmented blobs provide nothing interesting
compared with stream one and therefore not recommended to be used in
new development, we support that format only for backward
compatibility reasons. If you really need segmented blobs this
default may be overridden by calling:</font></p>
<p><font size="4" style="font-size: 14pt"><i>batch-&gt;setDefaultBpb(&amp;status,</i></font>
<font size="4" style="font-size: 14pt"><i>bpbLength, bpb);</i></font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">Certainly
passed BPB may contain any other blob creation parameters too. As you
may have already noticed you may also pass BPB directly to addBlob()
but if most of blobs you are going to add have same non-default
format use of setDefaultBpb() is slightly more efficient. Returning
to segmented blobs call to addBlob() will add first segment to
the blob, following calls to appendBlobData() will add more segments.
Do not forget that segment size is limited to 64Kb 1, an attempt
to pass more data in a single call with cause an error.</font></p>
<p><font size="4" style="font-size: 14pt">Next step when working with
existing blob streams is use of addBlobStream() method. Using it one
can add more than one blob to the batch per single call. Blob stream
is a sequence of blobs, each starts with blob header. Header should
be appropriately aligned - <a href="#Batch">Batch</a> interface
provides special call for this purpose:</font></p>
<p style="font-variant: normal"><font size="4" style="font-size: 14pt"><i>unsigned
alignment = batch-&gt;getBlobAlignment(&amp;status);</i></font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">Its
supposed that all components of blob stream in a batch should be
aligned at least at alignment boundary, including size of stream
portions passed to addBlobStream() which should be a multiple of this
alignment. Header contains 3 fields 8-byte blob ID (must be
non-zero), 4-byte total blob size and 4 byte BPB size. Total blob
size includes BPB inside, i.e. one will always find next blob in the
stream in blob-size bytes after the header (taking into account the
alignment). BPB (if present, i.e. if BPB size is not zero) is placed
right after the header. After BPB blob data goes, its format
depends upon blob type stream or segmented. In case of stream
blob its a plain sequence of bytes having size blob-size
BPB-size. With segmented blob things are a bit more compicated: blob
data is a set of segments where each segment has the following format
2-bytes size of segment (this should be aligned at
IBatch::BLOB_SEGHDR_ALIGN boundary) followed by stored in this 2
bytes number of bytes.</font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">When
big blob is added to the stream its size is not always known in
advance. In order not to have too big buffer for that blob (remember,
size should be provided in blob header, before blob data) blob
continuation record may be used. In blob header you leave blob size
at a value known when creating that header and add continuation
record that has format absolutely same as blob header, but here blob
ID must be zero and BPB size must always be zero too. Typically you
will want to have one continuation record per addBlobStream() call.</font></p>
<p><a name="Batch::registerBlob"></a><font size="4" style="font-size: 14pt">Last
method used to work with blobs stands alone from the first three that
pass blob data inline with the rest of batch data </font> <font size="4" style="font-size: 14pt">its
needed to register in a batch ID of a blob created using standard
blob API. This may be unavoidable if one needs to pass to a batch
really big blob. Do not use ID of such blob in batch directly
that will cause invalid blob ID error during batch execution. Instead
do:</font></p>
<p><font size="4" style="font-size: 14pt"><i>batch-&gt;registerBlob(&amp;status,
&amp;realId, &amp;msg-&gt;desc);</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">If blob policy makes firebird
engine generate blob IDs this code is enough to correctly register
existing blob in a batch. In other cases you will have to assign
correct (from batch POV) ID to msg-&gt;desc.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Almost all mentioned methods
are used in 11.batch.cpp please use it to see an alive sample of
batching in firebird.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Two words about access to
batches from ISC API - one can execute prepared ISC statement in
batch mode. Main support for it is presence of two new API functions,
namely fb_get_transaction_interface &amp; fb_get_statement_interface,
which make it possible to access appropriate interfaces identical to
existing ISC handles. An example of it is present in
12.batch_isc.cpp.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Working with events.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Events
interface was not completed in FB3, we expect to have something more
interesting in next version. The minimum existing support is as
follows: <a href="#Attachment">IAttachment</a> contains call
queEvents() which performs almost same functions as isc_que_events()
call. Instead the pair of parameters </font><font size="4" style="font-size: 14pt"><i>FPTR_EVENT_CALLBACK
ast</i></font> <font size="4" style="font-size: 14pt">and </font><font size="4" style="font-size: 14pt"><i>void*
arg</i></font><font size="4" style="font-size: 14pt">, required to
invoke user code when event happens in firebird engine, callback
interface IEventCallback is used. This is traditional approach which
helps to avoid non-safe casts from void* in user function. Another
important difference is that instead event identifier (a kind of
handler) this function returns reference counted interface <a href="#Events">IEvents</a>
having method cancel() used when waiting for event should be stopped.
Unlike identifier which is automatically destroyed when event arrives
interface can not be automatically destroyed in case when event
is received right before canceling interface call to cancel() would
cause segfault when interface is already destroyed. Therefore
interface <a href="#Events">IEvents</a> must be explicitly released
after receiving an event. This may be done for example right before
queuing for an event next time:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>events-&gt;release();</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>events
= NULL;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>events
= attachment-&gt;queEvents(&amp;status, this, eveLen, eveBuffer);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Setting
interface pointer to NULL is useful in case of exception during
queEvents. In other aspects events handling did not change compared
with ISC API. Please use for additional details our sample
08.events.cpp. </font>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Using services.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">To
begin to use services one should first of all connect to service
manager. This is done using attachServiceManager() method of
<a href="#Provider">IProvider</a>. This method returns <a href="#Service">IService</a>
interface which is used later to talk to service. To prepare SPB to
attach to service manager one can use IXpbBuilder:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>IXpbBuilder*
spb1 = utl-&gt;getXpbBuilder(&amp;status, IXpbBuilder::SPB_ATTACH,
NULL, 0);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>spb1-&gt;insertString(&amp;status,
isc_spb_user_name, “sysdba”);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>spb1-&gt;insertString(&amp;status,
isc_spb_password, “masterkey”);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">and
proceed with attach:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><a href="#Service">IService</a>*
svc = prov-&gt;attachServiceManager(&amp;status, “service_mgr”,
spb1-&gt;getBufferLength(&amp;status), spb1-&gt;getBuffer(&amp;status));</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Using
IService one can perform both available for services actions
start services and query various information about started utilities
and server in general. When querying information one limitation takes
place formats of parameter blocks, used by query() method, in
Firebird 3 are not supported by IXpbBuilder. Support will be probably
added in later versions, in Firebird 3 you will have to build and
analyze that blocks manually. Format of that blocks matches old
format (used in ISC API) one to one. </font>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">To
start service one should first of all create appropriate SPB:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IXpbBuilder*
spb2 = utl-&gt;getXpbBuilder(&amp;status, IXpbBuilder::SPB_START,
NULL, 0);</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">and
add required items to it. For example, to print encryption statistics
for database employee the following should be placed into SPB:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">spb2-&gt;insertTag(&amp;status,
isc_action_svc_db_stats);</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">spb2-&gt;insertString(&amp;status,
isc_spb_dbname, &quot;employee&quot;);</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">spb2-&gt;insertInt(&amp;status,
isc_spb_options, isc_spb_sts_encryption);</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">After
it service can be started using start() method of <a href="#Service">IService</a>
interface:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">svc-&gt;start(&amp;status,
spb2-&gt;getBufferLength(&amp;status), spb2-&gt;getBuffer(&amp;status));</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Many
started services (including mentioned here gstat) return text
information during execution. To display it one should query started
service anout that information line by line. This is done by calling
query() method of <a href="#Service">IService</a> interface with
appropriate send and receive blocks of parameters. Send block may
contain various helper information (like timeout when querying
service) or information to be passed to stdin of service utility or
may be empty in the simplest case. Receive block must contain list of
tags you want to receive from service. For most of utilities this is
single isc_info_svc_line:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>const
unsigned char receiveItems1[] = {isc_info_svc_line};</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">To
query information one also needs a buffer for that information:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>unsigned
char results[1024];</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">After
that preliminary steps we are ready to query service in a loop (each
line returned in a single call to query()):</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>do</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>svc-&gt;query(&amp;status,
0, NULL, sizeof(receiveItems1), receiveItems1, sizeof(results),
results);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}
while (printInfo(results, sizeof(results)));</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">In
this example we suppose that printInfo() function returns TRUE as
long as service returns results block containing next output line
(i.e. till end of data stream from service). Format of results block
varies from service to service, and some services like gsec produce
historical formats that are not trivial for parse but this is out
of our scope here. A minimum working sample of printInfo() is present
in example 09.service.cpp.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Same
query method is used to retrieve information about server but in this
case query function is not invoked in a loop, i.e. buffer must be big
enough to fit all information at once. This is not too hard
typically such calls do not return much data. As in previous case
begin with receive block placing required items in it in our
example it's isc_info_svc_server_version:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
unsigned char receiveItems2[] = {isc_info_svc_server_version};</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Existing
from previous call results buffer may be reused. No loop is needed
here:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>svc-&gt;query(&amp;status,
0, NULL, sizeof(receiveItems2), receiveItems2, sizeof(results),
results);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>printInfo(results,
sizeof(results));</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">After
finishing with services tasks do not forget to close an interface:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>svc-&gt;detach(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">IService interface also
contains method cancel(). Its needed to cancel wait in a query()
and is used internally by network server. Current implementation is
restricted IService::cancel() is not supported by remote
redirector, only engine provider supports it. That means one can use
it only in embedded connections.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-top: 0.43cm; margin-bottom: 0.51cm; page-break-after: avoid">
<font face="Albany, sans-serif"><font size="5" style="font-size: 18pt">Writing
plugins.</font></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">To
write a plugin means to implement some interfaces and place your
implementation into dynamic library (.dll in windows or .so in linux)
later referenced as </font><font size="4" style="font-size: 14pt"><i>plugin
module</i></font> <font size="4" style="font-size: 14pt">or just
</font><font size="4" style="font-size: 14pt"><i>module</i></font><font size="4" style="font-size: 14pt">.
In most cases single plugin is placed</font> <font size="4" style="font-size: 14pt">into</font>
<font size="4" style="font-size: 14pt">dynamic library but in common
case multiple plugins may coexist in single dynamic library. One of
that interfaces <a href="#PluginModule">IPluginModule</a> is
module-wide (as more or less clear from it's name), others are per
plugin. Also each plugin module should contain special exported
entrypoint firebird_plugin() which name is defined in include file
firebird/Interfaces.h as FB_PLUGIN_ENTRY_POINT.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">In
previous part of this text we were mostly describing how to use
existing interfaces, here main attention will be paid to implementing
interfaces yourself. Certainly to do it one can and should use
already existing interfaces both generic needed for accessing
firebird databases (already described) and some more interfaces
specifically designed for plugins.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Following
text is actively using example of database encryption plugin
examples/dbcrypt/DbCrypt.cpp. It will be good idea to compile this
sample yourself and learn it when reading later.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Implementation of plugin
module.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Plugins
actively interact with special firebird component called </font><font size="4" style="font-size: 14pt"><i>plugin
manager</i></font><font size="4" style="font-size: 14pt">. In
particular plugin manager should be aware what plugin modules were
loaded and must be notified if operating system tries to unload one
of that modules without explicit plugin manager command (this may
happen first of all when using embedded access when exit() is
called in a program or main firebird library </font><font size="4" style="font-size: 14pt"><i>fbclient</i></font>
<font size="4" style="font-size: 14pt">is unloaded). Primary task of
IPluginModule interface is that notification. First of all one must
decide - how to detect that module is going to be unloaded? When
dynamic library is unloaded for some reason a lot of OS-dependent
actions is performed and some of that actions may be used to detect
this fact in the program. When writing plugins distributed with
firebird we always use invocation of destructor of global variable.
The big “plus” for this method is that it is OS independent
(though something like atexit() function maybe also used
successfully). But use of destructor makes it possible to easily
concentrate almost everything related with unload detection in single
class implementing at the same time <a href="#PluginModule">IPluginModule</a>
interface.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Minimum
implementation looks as follows:</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>class
PluginModule : public IPluginModuleImpl&lt;PluginModule,
CheckStatusWrapper&gt;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>private:</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>IPluginManager*
pluginManager;</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>public:</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>PluginModule()</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>:
pluginManager(NULL)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{
}</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>~PluginModule()</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>if
(pluginManager)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>pluginManager-&gt;unregisterModule(this);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>doClean();</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>void
registerMe(IPluginManager* m)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>pluginManager
= m;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>pluginManager-&gt;registerModule(this);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>void
doClean()</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>pluginManager
= NULL;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>};</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">The
only data member is plugin manager interface <a href="#PluginManager">IPluginManager</a>.
It's passed to registerModule() function and saved in private
variable, at the same time module is registered in plugin manager by
the call to registerModule() method with own address as a single
parameter. Variable </font><font size="4" style="font-size: 14pt"><i>pluginManager
</i></font><font size="4" style="font-size: 14pt">not only stores
pointer to interface, at the same time it serves as a flag that
module is registered. When destructor of registered module is invoked
it notifies plugin manager (yes, this is what for this class exists!)
about unexpected unload by the call to unregisterModule() passing
pointer to itself. When plugin manager is going to unload module in
regular way in first of all calls doClean() method changing module
state to unregistered and this avoiding call to unregisterModule()
when OS performs actual unload.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Implementing
plugin's interface IPluginModule we met with first interface required
to implement plugins <a href="#PluginManager">IPluginManager</a>.
It will be actively used later, the rest of internals of this class
will hardly be required to you after copying it to your program. Just
don't forget to declare global variable of this type and call
registerMe() function from FB_PLUGIN_ENTRY_POINT.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Core interface of any
plugin.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Let's
start implementing plugin itself. The type of main interface depends
upon plugin type (which is obvious), but all of them are based on
common reference counted interface IPluginBase which performs common
for all plugins (and very simple) tasks. Each plugin has some (also
reference counted) object which <i>owns</i> this plugin. In order to
perform smart plugin lifetime management any plugin must be able to
store that owner information and report it to plugin manager on
request. That means that each plugin must implement two trivial
methods setOwner() and getOwner() contained in IPluginBase interface.
Type-dependent methods are certainly more interesting - they are
discussed in interfaces description part.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Let's
take a look at typical part of any plugin implementation (here I
specially use non-existent type SomePlugin):</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>class
MyPlugin : public ISomePluginImpl&lt;MyPlugin, CheckStatusWrapper&gt;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>public:</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>explicit
MyPlugin(<a href="#PluginConfig">IPluginConfig</a>* cnf) noexcept</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>:
config(cnf), refCounter(0), owner(NULL)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>config-&gt;addRef();</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Constructor gets as parameter
plugin configuration interface. If you are going to have you plugin
configured in some way it's good idea to save this interface in your
plugin and use it later. This will let you use common for all
firebird configuration style letting users have familiar
configuration and minimize code written. Certainly when saving any
reference counted interface it's better not forget to add reference
to it. Also set reference counter to 0 and plugin owner to NULL.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>~MyPlugin()</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>config-&gt;release();</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Destructor releases config
interface. Pay attention we do not change reference counter of
our owner cause it owns us, not we own it.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>//
IRefCounted implementation</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>int
release()</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>if
(--refCounter == 0)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>delete
this;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>return
0;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>return
1;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>void
addRef()</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>++refCounter;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Absolutely typical
implementation of reference counted object.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>//
IPluginBase implementation</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>void
setOwner(IReferenceCounted* o)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>owner
= o;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>IReferenceCounted*
getOwner()</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>return
owner;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">As it was promised
implementation of IPluginBase is trivial.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>//
ISomePlugin implementation</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>//
… here go various methods required for particular plugin type</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>private:</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>IPluginConfig*
config;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>std::atomic_int
refCounter;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>IReferenceCounted*
owner;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>};</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">With
this sample formal part of main plugin interface implementation is
over. After adding type-specific methods (and writing probably a
lo-o-o-ot of code to make them useful) interface is ready.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Plugin's factory.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">One
more interface required for plugin to work is <a href="#PluginFactory">IPluginFactory</a>.
Factory creates instances of plugin and returns them to plugin
manager. Factory typically looks this way:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>class
Factory : public IPluginFactoryImpl&lt;Factory, CheckStatusWrapper&gt;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>public:</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>IPluginBase*
createPlugin(CheckStatusWrapper* status, IPluginConfig*
factoryParameter)</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>{</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>MyPlugin*
p = new MyPlugin(factoryParameter);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>p-&gt;addRef();</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>return
p;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>};</i></font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Here attention should be payed
to the fact that even in a case when code in a function may throw
exceptions (operator new may throw in a case when memory exhausted)
one need not always manually define try/catch block
implementation of firebird interfaces does this job for you, in
implementation of IPluginFactory it's placed into template
IPluginFactoryImpl. Take into an account that default status wrappers
perform meaning-full processing only for FbException. But if you
(that definitely makes sense if you work on some big project) define
your own wrapper you can handle any type of C++ exception and pass
useful information about it from your plugin.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Plugin module
initialization entrypoint.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">When
plugin manager loads plugin module it invokes module initializing
routine the only exported from plugin function
FB_PLUGIN_ENTRY_POINT. To wrote it's code one will need two global
variables plugin module and plugin factory. In our case that is:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>PluginModule
module;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>Factory
factory;</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">If
you module contains more than one plugin you will need a factory per
each plugin.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">For
FB_PLUGIN_ENTRY_POINT we should not forget that it should be exported
from plugin module, and it requires taking into an account some OS
specifics. We do it using macro FB_DLL_EXPORT defined in
examples/interfaces/ifaceExamples.h. If you are sure you write plugin
only for some specific OS you can make this place a bit simpler. In
minimum case the function should register module and all factories in
plugin manager:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">extern
&quot;C&quot; FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster*
master)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">{</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IPluginManager*
pluginManager = master-&gt;getPluginManager();</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">module.registerMe(pluginManager);</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">pluginManager-&gt;registerPluginFactory(IPluginManager::TYPE_DB_CRYPT,
&quot;fbSampleDbCrypt&quot;, &amp;factory);</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">}</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">First
of all we call written by us not long ago PluginModule::registerMe()
function which will saves IPluginManager for future use and registers
our plugin module. Next time to register factory (or factories in
case of multiple plugins per module) takes place. We must pass
correct plugin type (valid types are enumerated in interface
IPluginManager) and a name under which plugin will be registered. In
simple case it should match with the name of dynamic library with
plugin module. Following last rule will help you avoid configuring
your plugin manually in plugins.conf.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Pay
attention - unlike applications plugins should not use
fb_get_master_interface() to obtain IMaster. Instance, passed to
FB_PLUGIN_ENTRY_POINT, should be used instead. If you ever need
master interface in your plugin take care about saving it in this
function.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-top: 0.43cm; margin-bottom: 0.51cm; page-break-after: avoid">
<font face="Albany, sans-serif"><font size="5" style="font-size: 18pt">Interfaces:
from A to Z</font></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">In
this glossary we do not list interfaces that are not actively used
(like IRequest, needed first of all to support legacy ISC API
requests). Same reference may be made for a number of methods (like
compileRequest() in IAttachment). For interfaces / methods, having
direct analogue in old API, that analogue is provided.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Generic interfaces.</font></h1>
<p style="margin-bottom: 0cm"><a name="Attachment"></a><font size="4" style="font-size: 14pt">Attachment
interface replaces isc_db_handle:</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
getInfo(StatusType* status, unsigned itemsLength, const unsigned
char* items, unsigned bufferLength, unsigned char* buffer)
replaces isc_database_info().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ITransaction*
startTransaction(StatusType* status, unsigned tpbLength, const
unsigned char* tpb) partially replaces isc_start_multiple(), to
start &gt;1 transaction <a href="#Dtc">distributed transactions
coordinator</a> should be used, also possible to <a href="#Transaction">join</a>
2 transactions into single distributed transaction.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ITransaction*
reconnectTransaction(StatusType* status, unsigned length, const
unsigned char* id) makes it possible to connect to a transaction
in limbo. Id parameter contains transaction number in network format
of given length.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IRequest*
compileRequest(StatusType* status, unsigned blrLength, const
unsigned char* blr) support of ISC API.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
transactRequest(StatusType* status, ITransaction* transaction,
unsigned blrLength, const unsigned char* blr, unsigned inMsgLength,
const unsigned char* inMsg, unsigned outMsgLength, unsigned char*
outMsg) support of ISC API.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IBlob*
createBlob(StatusType* status, ITransaction* transaction, ISC_QUAD*
id, unsigned bpbLength, const unsigned char* bpb) creates new
blob, stores it's identifier in id, replaces isc_create_blob2().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IBlob*
openBlob(StatusType* status, ITransaction* transaction, ISC_QUAD*
id, unsigned bpbLength, const unsigned char* bpb) opens existing
blob, replaces isc_open_blob2().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
getSlice(StatusType* status, ITransaction* transaction, ISC_QUAD*
id, unsigned sdlLength, const unsigned char* sdl, unsigned
paramLength, const unsigned char* param, int sliceLength, unsigned
char* slice) - support of ISC API.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
putSlice(StatusType* status, ITransaction* transaction, ISC_QUAD*
id, unsigned sdlLength, const unsigned char* sdl, unsigned
paramLength, const unsigned char* param, int sliceLength, unsigned
char* slice) - support of ISC API.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
executeDyn(StatusType* status, ITransaction* transaction, unsigned
length, const unsigned char* dyn) - support of ISC API.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IStatement*
prepare(StatusType* status, ITransaction* tra, unsigned stmtLength,
const char* sqlStmt, unsigned dialect, unsigned flags) replaces
isc_dsql_prepare(). Additional parameter flags makes it possible to
control what information will be preloaded from engine at once (i.e.
in single network packet for remote operation).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ITransaction*
execute(StatusType* status, ITransaction* transaction, unsigned
stmtLength, const char* sqlStmt, unsigned dialect, IMessageMetadata*
inMetadata, void* inBuffer, IMessageMetadata* outMetadata, void*
outBuffer) executes any SQL statement except returning multiple
rows of data. Partial analogue of isc_dsql_execute2() - in and out
XSLQDAs replaced with input and output messages with appropriate
buffers.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IResultSet*
openCursor(StatusType* status, ITransaction* transaction, unsigned
stmtLength, const char* sqlStmt, unsigned dialect, IMessageMetadata*
inMetadata, void* inBuffer, IMessageMetadata* outMetadata, const
char* cursorName, unsigned cursorFlags) executes SQL statement
potentially returning multiple rows of data. Returns <a href="#ResultSet">ResultSet</a>
interface which should be used to fetch that data. Format of output
data is defined by outMetadata parameter, leaving it NULL default
format may be used. Parameter cursorName specifies name of opened
cursor (analogue of isc_dsql_set_cursor_name()). Parameter
cursorFlags is needed to open bidirectional cursor setting it's
value to Istatement::CURSOR_TYPE_SCROLLABLE.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IBatch*
createBatch(StatusType* status, ITransaction* transaction, unsigned
stmtLength, const char* sqlStmt, unsigned dialect, IMessageMetadata*
inMetadata, unsigned parLength, const unsigned char* par)
prepares sqlStmt and creates <a href="#Batch">Batch</a> interface
ready to accept multiple sets of input parameters in inMetadata
format. Leaving inMetadata NULL makes batch use default format for
sqlStmt. Parameters block may be passed to createBatch() making it
possible to adjust batch behavior.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IEvents*
queEvents(StatusType* status, IEventCallback* callback, unsigned
length, const unsigned char* events) replaces isc_que_events()
call. Instead callback function with void* parameter callback
interface is used.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
cancelOperation(StatusType* status, int option) replaces
fb_cancel_operation().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
ping(StatusType* status) check connection status. If test fails
the only operation possible with attachment is to close it.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
detach(StatusType* status) replaces isc_detach_database(). On
success releases interface.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
dropDatabase(StatusType* status) - replaces isc_drop_database(). On
success releases interface.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Batch"></a><font size="4" style="font-size: 14pt">Batch
interface makes it possible to process multiple sets of
parameters in single statement execution.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
add(StatusType* status, unsigned</font> <font size="4" style="font-size: 14pt">count,
const void* inBuffer) adds count messages from inBuffer to the
batch. Total size of messages that can be added to the batch is
limited by TAG_BUFFER_BYTES_SIZE <a href="#Batch_PB">parameter</a>
of batch creation.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
addBlob(StatusType* status, unsigned</font> <font size="4" style="font-size: 14pt">length,
const void* inBuffer, ISC_QUAD* blobId, unsigned bpbLength, const
unsigned char* bpb) adds single blob having length bytes from
inBuffer to the batch, blob identifier is located at blobId address.
If blob should be created with non-default parameters BPB may be
passed (format matches one used in <a href="#Attachment">Attachment</a>::createBlob).Total
size of inline blobs that can be added to the batch (including
optional BPBs, blob headers, segment sizes and taking into an
accoount alignment) is limited by TAG_BUFFER_BYTES_SIZE <a href="#Batch_PB">parameter</a>
of batch creation (affects all blob-oriented methods except
registerBlob()). </font>
</p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
appendBlobData(StatusType* status, unsigned length, const void*
inBuffer) extend last added blob: append length bytes taken from
inBuffer address to it.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
addBlobStream(StatusType* status, unsigned length, const void*
inBuffer) adds blob data (this can be multiple objects or part
of single blob) to the batch. Header of each blob in the stream is
aligned at getBlobAlignment() boundary and contains 3 fields: first
- 8-bytes blob identifier (in ISC_QUAD format), second - 4-bytes
length of blob, third 4-bytes length of BPB. Blob header should
not cross boundaries of buffer in this function call. BPB data is
placed right after header, blob data goes next. Length of blob
includes BPB (if it present). All data may be distributed between
multiple addBlobStream() calls. Blob data in turn may be structured
in case of segmented blob, see chapter “<a href="#Modifying data in a batch">Modifying
data in a batch</a>”.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
registerBlob(StatusType* status, const ISC_QUAD* existingBlob,
ISC_QUAD* blobId) makes it possible to use in batch blobs added
using standard <a href="#Blob">Blob</a> interface. This function
contains 2 ISC_QUAD* parameters, its important not to mix them
second parameter (existingBlob) is a pointer to blob identifier,
already added out of batch scope, third (blobId) points to blob
identifier that will be placed in a message in this batch.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><a href="#BatchCompletionState">IBatchCompletionState</a>*
execute(StatusType* status, ITransaction* transaction) execute
batch with parameters passed to it in the messages. If parameter
MULTIERROR is not set in <a href="#Batch_PB">parameters block</a>
when creating the batch execution will be stopped after first error,
in MULTIERROR mode an unlimited number of errors can happen, after
an error execution is continued from the next message. This function
returns BatchCompletionState interface that contains all requested
nformation about the results of batch execution.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
cancel(StatusType* status) clear messages and blobs buffers,
return batch to a state it had right after creation. Notice
being reference counted interface batch does not contain any special
function to close it, please use release() for this purposes. </font>
</p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getBlobAlignment(StatusType* status) returns required alignment
for the data placed into the buffer of addBlobStream().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IMessageMetadata*
getMetadata(StatusType* status) return format of metadata used
in batchs messages.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setDefaultBpb(StatusType* status, unsigned parLength, const unsigned
char* par) sets BPB which will be used for all blobs missing
non-default BPB. Must be called before adding any message or blob to
batch.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
getInfo(StatusType* status, unsigned itemsLength, const unsigned
char* items, unsigned bufferLength, unsigned char* buffer)
requests information about batch.</font></p>
</ol>
<p style="margin-bottom: 0cm"><a name="Batch_PB"></a><font size="4" style="font-size: 14pt">Tag
for parameters block:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">VERSION1</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Tags
for clumplets in parameters block:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TAG_MULTIERROR
(0/1) can have &gt;1 message with errors</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TAG_RECORD_COUNTS
(0/1) - per-message modified records accounting</font></p>
<p style="margin-bottom: 0cm"><a name="TAG_BUFFER_BYTES_SIZE"></a><font size="4" style="font-size: 14pt">TAG_BUFFER_BYTES_SIZE
(integer) - maximum possible buffer size on server (default 16Mb,
maximum 256Mb)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TAG_BLOB_POLICY
- <a href="#Batch_Blob_Policy">policy</a> used to store blobs</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TAG_DETAILED_ERRORS
(integer) - how many vectors with detailed error info are stored in
completion state (default 64, maximum 256)</font></p>
<p style="margin-bottom: 0cm"><a name="Batch_Blob_Policy"></a><font size="4" style="font-size: 14pt">Policies
used to store blobs:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">BLOB_NONE
inline blobs can't be used (registerBlob() works anyway)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">BLOB_ID_ENGINE
- blobs are added one by one, IDs are generated by firebird engine</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">BLOB_ID_USER
- blobs are added one by one, IDs are generated by user</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">BLOB_STREAM
- blobs are added in a stream</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Items
accepted in getInfo() call:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">INF_BUFFER_BYTES_SIZE
actual </font><font color="#000000"><font size="4" style="font-size: 14pt">m</font></font><font size="4" style="font-size: 14pt">aximum
possible buffer size (one set by TAG_BUFFER_BYTES_SIZE)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">INF_DATA_BYTES_SIZE
- already added messages size</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">INF_BLOBS_BYTES_SIZE
- already added blobs size</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">INF_BLOB_ALIGNMENT
- required alignment for the BLOB data (duplicates getBlobAlignment)</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="BatchCompletionState"></a><font size="4" style="font-size: 14pt">BatchCompletionState
disposable interface, always returned by execute() method of
<a href="#Batch">Batch</a> interface. It contains more or less
(depending upon parameters passed when <a href="#Batch">Batch</a> was
created) detailed information about the results of batch execution.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">uint
getSize(StatusType* status) returns the total number of
processed messages.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
getState(StatusType* status, uint pos) returns the result of
execution of message number pos. On any error with the message
this is EXECUTE_FAILED constant, value returned on success depends
upon presence of RECORD_COUNTS <a href="#Batch_PB">parameter</a> of
batch creation. When it present and has non-zero value number of
records inserted, updated or deleted during particular message
processing is returned, else SUCCESS_NO_INFO constant is returned.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">uint
findError(StatusType* status, uint pos) finds next (starting
with pos) message which processing caused an error. When such
message is missing NO_MORE_ERRORS constant is returned. Number of
status vectors, returned in this interface, is limited by the value
of DETAILED_ERRORS <a href="#Batch_PB">parameter</a> of batch
creation.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
getStatus(StatusType* status, IStatus* to, uint pos) returns
detailed information (full status vector) about an error that took
place when processing pos message. In order to distinguish
between errors (in <a href="#Batch">Batch</a>::execute() or in
<a href="#BatchCompletionState">BatchCompletionState</a>::getStatus())
that status is returned in separate to parameter unlike errors
in this call that are placed into status parameter.</font></p>
</ol>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Special
values returned by getState():</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">EXECUTE_FAILED
- error happened when processing this message</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">SUCCESS_NO_INFO
- record update info was not collected</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Special
value returned by findError():</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">NO_MORE_ERRORS
no more messages with errors in this batch</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Blob"></a><font size="4" style="font-size: 14pt">Blob
interface replaces isc_blob_handle:</font></p>
<ol>
<li value="1"><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
getInfo(StatusType* status, unsigned itemsLength, const unsigned
char* items, unsigned bufferLength, unsigned char* buffer)
replaces isc_blob_info().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
getSegment(StatusType* status, unsigned bufferLength, void* buffer,
unsigned* segmentLength) replaces isc_get_segment(). Unlike it
never returns isc_segstr_eof and isc_segment errors (that are
actually not errors), instead returns <a href="#Completion codes">completion
codes</a> IStatus::RESULT_NO_DATA and IStatus::RESULT_SEGMENT,
normal return is IStatus::RESULT_OK.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
putSegment(StatusType* status, unsigned length, const void* buffer)
replaces isc_put_segment().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
cancel(StatusType* status) replaces isc_cancel_blob(). On
success releases interface.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
close(StatusType* status) replaces isc_close_blob(). On success
releases interface.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
seek(StatusType* status, int mode, int offset) replaces
isc_seek_blob().</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Config"></a><font size="4" style="font-size: 14pt">Config
interface generic configuration file interface:</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IConfigEntry*
find(StatusType* status, const char* name) find entry by name.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IConfigEntry*
findValue(StatusType* status, const char* name, const char* value)
find entry by name and value.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IConfigEntry*
findPos(StatusType* status, const char* name, unsigned pos) find
entry by name and position. If configuration file contains lines:</font></p>
</ol>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Db=DBA</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Db=DBB</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Db=DBC</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">call
to findPos(status, “Db”, 2) will return entry with value DBB.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="ConfigManager"></a><font size="4" style="font-size: 14pt">ConfigManager
interface generic interface to access various configuration
objects:</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getDirectory(unsigned code) returns location of
appropriate directory in current firebird instance. See codes for
this call a <a href="#Directory codes">few lines later</a>.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IFirebirdConf*
getFirebirdConf() - returns interface to access default
configuration values (from firebird.conf).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IFirebirdConf*
getDatabaseConf(const char* dbName) - returns interface to access
db-specific configuration (takes into an account firebird.conf and
appropriate part of databases.conf).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IConfig*
getPluginConfig(const char* configuredPlugin) returns interface
to access named plugin configuration.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getInstallDirectory() - returns directory where firebird is
installed.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getRootDirectory() - returns root directory of current
instance, in single-instance case usually matches install directory.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getDefaultSecurityDb() - returns default (i.e. not taking into
an account configuration files) pathname of security database, used
first of all internally to enable correct access to security
database on multi-provider server with zero configuration.</font></p>
</ol>
<p style="margin-bottom: 0cm"><a name="Directory codes"></a><font size="4" style="font-size: 14pt">Directory
codes:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_BIN
bin (utilities like isql, gbak, gstat)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_SBIN
sbin (fbguard and firebird server)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_CONF
configuration files (firebird.conf, databases.conf, plugins.conf)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_LIB
lib (fbclient, ib_util)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_INC
include (ibase.h, firebird/Interfaces.h)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_DOC
- documentation</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_UDF
UDF (ib_udf, fbudf)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_SAMPLE
- samples</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_SAMPLEDB
samples database (employee.fdb)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_INTL
international libraries (fbintl)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_MISC
miscellaneous files (like uninstall manifest and something else)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_SECDB
where security database is stored (securityN.fdb)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_MSG
where messages file is stored (firebird.msg)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_LOG
where log file is stored (firebird.log)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_GUARD
where guardian lock is stored (fb_guard)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DIR_PLUGINS
plugins directory ([lib]Engine13.{dll|so})</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="ConfigEntry"></a><font size="4" style="font-size: 14pt">ConfigEntry
interface represents an entry (Key = Values with probably
sub-entries) in firebird configuration file:</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getName() - returns key name.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getValue() - returns value as character string.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ISC_INT64
getIntValue() - treats value as integer and returns it.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_BOOLEAN
getBoolValue() - treats value as boolean and returns it.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IConfig*
getSubConfig(StatusType* status) treats sub-entries as separate
configuration file and returns Config interface for it.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="DecFloat16"></a><a name="DecFloat34"></a>
<font size="4" style="font-size: 14pt">DecFloat16 / DecFloat34
interfaces that help to work with DECFLOAT (16 &amp; 34 respectively)
datatypes. They have almost same set of methods with FB_DEC16
parameter replaced by FB_DEC34 for DecFloat34:</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
toBcd(const FB_DEC16* from, int* sign, unsigned char* bcd, int* exp)
convert decimal float value to BCD.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
toString(StatusType* status, const FB_DEC16* from, unsigned
bufferLength, char* buffer) convert decimal float value to
string.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
fromBcd(int sign, const unsigned char* bcd, int exp, FB_DEC16* to)
make decimal float value from BCD.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
fromString(StatusType* status, const char* from, FB_DEC16* to)
make decimal float value from string.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Dtc"></a><font size="4" style="font-size: 14pt"><font color="#000000">Dtc
</font>interface distributed transactions coordinator. Used to
start distributed (working with 2 or more attachments) transaction.
Unlike pre-FB3 approach where distributed transaction must be started
in this way from the most beginning FB3's distributed transactions
coordinator makes it also possible to join already started
transactions into single distributed transaction.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ITransaction*
join(StatusType* status, ITransaction* one, ITransaction* two)
joins 2 independent transactions into distributed transaction. On
success both transactions passed to join() are released and pointers
to them should not be used any more.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IDtcStart*
startBuilder(StatusType* status) returns <a href="#DtcStart">DtcStart</a>
interface.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="DtcStart"></a><font size="4" style="font-size: 14pt">DtcStart
interface replaces array of struct TEB (passed to
isc_start_multiple() in ISC API). This interface accumulates
attachments (and probably appropriate TPBs) for which dustributed
transaction should be started.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
addAttachment(StatusType* status, IAttachment* att) adds
attachment, transaction for it will be started with default TPB.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
addWithTpb(StatusType* status, IAttachment* att, unsigned length,
const unsigned char* tpb) - adds attachment and TPB which will be
used to start transaction for this attachment.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ITransaction*
start(StatusType* status) start distributed transaction for
accumulated attachments. On successful return DtcStart interface is
disposed automatically.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="EventCallback"></a><font size="4" style="font-size: 14pt">EventCallback
interface replaces callback function used in isc_que_events()
call. Should be implemented by user to monitor events with
IAttachment::queEvents() method.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
eventCallbackFunction(unsigned length, const unsigned char* events)
is called each time event happens.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Events"></a><font size="4" style="font-size: 14pt">Events
interface replaces event identifier when working with events
monitoring.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
cancel(StatusType* status) - cancels events monitoring started by
IAttachment::queEvents().</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FirebirdConf
interface access to main firebird configuration. Used for both
default configuration, set by firebird.conf, and per-database
configuration, adjusted by databases.conf. In order to speed up
access to configuration values calls accessing actual values use
integer key instead symbolic parameter name. Key is stable during
server run (i.e. plugin can get it once and than use to get
configuration value for different databases). </font>
</p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getKey(const char* name) get key for parameter name. ~0 (all
bits are 1) is returned in a case when there is no such parameter.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ISC_INT64
asInteger(unsigned key) return value of integer parameter.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* asString(unsigned key) - return value of string parameter.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_BOOLEAN
asBoolean(unsigned key) - return value of boolean parameter.
Standard abbreviations (1/true/t/yes/y) are treated as “true”,
all other cases false.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getVersion(StatusType* status) get version of configuration
manager associated with this interface. Different versions of
configuration manager may coexist on same server for example when
old DB engine is in use on modern server. Pay attention keys
(see getKey()) from different versions do not match and when used
inappropriately will always return 0/nullptr/false.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Int128"></a><font size="4" style="font-size: 14pt">Int128
interfaces that help to work with 128-bit integers (used as base type
for numeric and decimal with precision &gt; 18).</font></p>
<ol>
<li value="1"><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
toString(StatusType* status, const FB_I128* from, int scale,
unsigned bufferLength, char* buffer) convert 128-bit integer
value to string taking scale into an account.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
fromString(StatusType* status, int scale, const char* from, FB_I128*
to) make 128-bit integer value from string taking scale into an
account.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Master"></a><font size="4" style="font-size: 14pt">Master
interface main interface from which start all operations with
firebird API.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IStatus*
getStatus() - get instance of <a href="#Status">Status</a>
interface.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IProvider*
getDispatcher() - get instance of <a href="#Provider">Provider</a>
interface, implemented by yValve (main provider instance).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IPluginManager*
getPluginManager() - get instance of <a href="#PluginManager">PluginManager</a>
interface.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ITimerControl*
getTimerControl() - get instance of <a href="#TimerControl">TimerControl</a>
interface.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IDtc*
getDtc() - get instance of <a href="#Dtc">Dtc</a> interface.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IUtil*
getUtilInterface() - get instance of <a href="#Util">Util</a>
interface.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IConfigManager*
getConfigManager() - get instance of <a href="#ConfigManager">ConfigManager</a>
interface.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="MessageMetadata"></a><font size="4" style="font-size: 14pt">MessageMetadata
interface partial analogue of XSQLDA (does not contain message
data, only message format info is present). Used in a calls related
with execution of SQL statements.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getCount(StatusType* status) returns number of fields/parameters
in a message. In all calls, containing index parameter, it's value
should be: 0 &lt;= index &lt; getCount().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getField(StatusType* status, unsigned index) returns field
name.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getRelation(StatusType* status, unsigned index) returns
relation name (from which given field is selected).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getOwner(StatusType* status, unsigned index) - returns
relation's owner name.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getAlias(StatusType* status, unsigned index) - returns field
alias.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getType(StatusType* status, unsigned index) - returns field SQL
type.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_BOOLEAN
isNullable(StatusType* status, unsigned index) - returns true if
field is nullable.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
getSubType(StatusType* status, unsigned index) - returns blof field
subtype (0 binary, 1 text, etc.).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getLength(StatusType* status, unsigned index) - returns maximum
field length.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
getScale(StatusType* status, unsigned index) - returns scale factor
for numeric field.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getCharSet(StatusType* status, unsigned index) - returns character
set for character field and text blob.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getOffset(StatusType* status, unsigned index) - returns offset of
field data in message buffer (use it to access data in message
buffer).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getNullOffset(StatusType* status, unsigned index) - returns offset
of null indicator for a field in message buffer.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IMetadataBuilder*
getBuilder(StatusType* status) - returns <a href="#MessageMetadata">MetadataBuilder</a>
interface initialized with this message metadata.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getMessageLength(StatusType* status) - returns length of message
buffer (use it to allocate memory for the buffer).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getAlignment(StatusType* status) returns alignment required for
message buffer.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getAlignedLength(StatusType* status) returns length of message
buffer taking into an account alignment requirements (use it to
allocate memory for an array of buffers and navigate through that
array).</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="MetadataBuilder"></a><font size="4" style="font-size: 14pt">MetadataBuilder
interface makes it possible to coerce datatypes in existing
message or construct metadata from the beginning.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setType(StatusType* status, unsigned index, unsigned type) set
SQL type of a field.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setSubType(StatusType* status, unsigned index, int subType) set
blof field subtype.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setLength(StatusType* status, unsigned index, unsigned length)
set maximum length of character field.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setCharSet(StatusType* status, unsigned index, unsigned charSet)
set character set for character field and text blob.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setScale(StatusType* status, unsigned index, unsigned scale) set
scale factor for numeric field </font>
</p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
truncate(StatusType* status, unsigned count) truncate message to
contain not more than count fields.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
moveNameToIndex(StatusType* status, const char* name, unsigned
index) reorganize fields in a message move field “name”
to given position.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
remove(StatusType* status, unsigned index) remove field.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
addField(StatusType* status) add field.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IMessageMetadata*
getMetadata(StatusType* status) get <a href="#10. MessageMetadata">MessageMetadata</a>
interface built by this builder.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setField(StatusType* status, uint index, const string field) set
name of a field / column.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setRelation(StatusType* status, uint index, const string relation)
set name of the relation from which the field was selected.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setOwner(StatusType* status, uint index, const string owner) set
name of that relation owner.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setAlias(StatusType* status, uint index, const string alias) set
alias name of the field in related statement.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="OffsetsCallback"></a><font size="4" style="font-size: 14pt">OffsetsCallback
interface:</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">setOffset(StatusType*
status, unsigned index, unsigned offset, unsigned nullOffset)
notifies that offsets for field/parameter number “index” should
be set to passed values. Should be implemented by user when
implementing <a href="#MessageMetadata">MessageMetadata</a>
interface and using <a href="#Util">Util</a>::setOffsets().</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="PluginConfig"></a><font size="4" style="font-size: 14pt">PluginConfig
interface passed to plugin's factory when plugin instance (with
particular configuration) to be created.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getConfigFileName() - recommended file name where
configuration for plugin is expected to be stored.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IConfig*
getDefaultConfig(StatusType* status) plugin configuration loaded
with default rules.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IFirebirdConf*
getFirebirdConf(StatusType* status) master firebird
configuration taking into an account per-database settings for a
database with which will work new instance of plugin.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setReleaseDelay(StatusType* status, ISC_UINT64 microSeconds)
used by plugin to configure recommended delay during which plugin
module will not be unloaded by plugin manager after release of last
plugin instance from that module.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="PluginFactory"></a><font size="4" style="font-size: 14pt">PluginFactory
interface should be implemented by plugin author when writing
plugin.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IPluginBase*
createPlugin(StatusType* status, IPluginConfig* factoryParameter)
creates new instance of plugin with passed recommended
configuration.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="PluginManager"></a><font size="4" style="font-size: 14pt">PluginManager
interface API of plugin manager.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
registerPluginFactory(unsigned pluginType, const char* defaultName,
IPluginFactory* factory) registers named factory of plugins of
given type.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
registerModule(IPluginModule* cleanup) registers plugin module.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
unregisterModule(IPluginModule* cleanup) unregisters plugin
module (in case of unexpected unload by OS).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IPluginSet*
getPlugins(StatusType* status, unsigned pluginType, const char*
namesList, IFirebirdConf* firebirdConf) returns PluginSet
interface providing access to list of plugins of given type. Names
of plugins to be included are taken from namesList, when missing
(NULL) from configuration setting for given pluginType. If
firebirdConf parameter is specified it is used for all configuration
purporses (including getting list of plugins and passing to
<a href="#PluginFactory">PluginFactory</a>::createPlugin() method),
if missing (NULL) default configuration (from firebird.conf) is
used.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IConfig*
getConfig(StatusType* status, const char* filename) returns
Config interface for given configuration file name. Can be used by
plugins to access configuration files with standard format but
non-default name.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
releasePlugin(IPluginBase* plugin) release given plugin. Should
be used for plugins instead simple release() due to need to perform
additional actions with plugin owner before actual release.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Constants
defined by PluginManager interface (plugin types):</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TYPE_PROVIDER</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TYPE_AUTH_SERVER</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TYPE_AUTH_CLIENT</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TYPE_AUTH_USER_MANAGEMENT</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TYPE_EXTERNAL_ENGINE</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TYPE_TRACE</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TYPE_WIRE_CRYPT</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TYPE_DB_CRYPT</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TYPE_KEY_HOLDER</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="PluginModule"></a><font size="4" style="font-size: 14pt">PluginModule
interface represents plugin module (dynamic library). Should be
implemented by plugin author in each plugin module (one instance per
module).</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
doClean() - called by plugin manager before normal unload of plugin
module.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">PluginSet
interface represents set of plugins of given type. Typically used
by internal firebird code but recommended for use in plugins loading
other plugins.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getName() - get name of current plugin in a set.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getModuleName() - get name of a module of current plugin in a
set (in simple case matches plugin name).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IPluginBase*
getPlugin(StatusType* status) get an instance of current plugin,
returned interface should be casted to main interface of plugin of
requested in <a href="#PluginManager">PluginManager</a>::getPlugins()
type. Returns NULL if set does not contain any more plugins.
Reference counter of plugin, returned by this function, is
incremented on return do not forget to use releasePlugin()
method of <a href="#PluginManager">PluginManager</a> for plugins
returned by this method.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
next(StatusType* status) make set to switch to next plugin from
the list.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
set(StatusType* status, const char* list) reset interface: make
it work with list of plugins provided by list parameter. Type of
plugins remains unchanged.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Provider"></a><font size="4" style="font-size: 14pt">Provider
interface main interface to start database / service access.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IAttachment*
attachDatabase(StatusType* status, const char* fileName, unsigned
dpbLength, const unsigned char* dpb) replaces
isc_attach_database().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IAttachment*
createDatabase(StatusType* status, const char* fileName, unsigned
dpbLength, const unsigned char* dpb) replaces
isc_create_database().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IService*
attachServiceManager(StatusType* status, const char* service,
unsigned spbLength, const unsigned char* spb) replaces
isc_service_attach().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
shutdown(StatusType* status, unsigned timeout, const int reason)
replaces fb_shutdown().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setDbCryptCallback(StatusType* status, ICryptKeyCallback*
cryptCallback) sets database encryption callback interface that
will be used for following database and service attachments. See …
for details.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="ResultSet"></a><font size="4" style="font-size: 14pt">ResultSet
interface replaces (with extended functionality) some functions
of isc_stmt_handle. This interface is returned by openCursor() call
in <a href="#Attachment">IAttachment</a> or <a href="#Statement">IStatement</a>.
All fetch calls except fetchNext() work only for bidirectional
(opened with CURSOR_TYPE_SCROLLABLE flag) result set.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Items
accepted in getInfo() call:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">INF_RECORD_COUNT
number of records stored inside a scrollable cursor, or -1 for a
uni-directional cursor.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
fetchNext(StatusType* status, void* message) fetch next record,
replaces isc_dsql_fetch(). This method (and other fetch methods)
returns <a href="#Completion codes">completion code</a>
Status::RESULT_NO_DATA when EOF is reached, Status::RESULT_OK on
success.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
fetchPrior(StatusType* status, void* message) fetch previous
record.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
fetchFirst(StatusType* status, void* message) fetch first
record.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
fetchLast(StatusType* status, void* message) fetch last record.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
fetchAbsolute(StatusType* status, int position, void* message)
fetch record by it's absolute position in result set.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
fetchRelative(StatusType* status, int offset, void* message)
fetch record by position relative to current.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_BOOLEAN
isEof(StatusType* status) check for EOF.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_BOOLEAN
isBof(StatusType* status) check for BOF.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IMessageMetadata*
getMetadata(StatusType* status) get metadata for messages in
result set, specially useful when result set is opened by
<a href="#Attachment">IAttachment</a>::openCursor() call with NULL
output metadata format parameter (this is the only way to obtain
message format in this case).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
close(StatusType* status) close result set, releases interface
on success.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
getInfo(StatusType* status, unsigned itemsLength, const unsigned
char* items, unsigned bufferLength, unsigned char* buffer)
retrieve information about result set.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Service"></a><font size="4" style="font-size: 14pt">Service
interface replaces isc_svc_handle.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
detach(StatusType* status) close attachment to services manager,
on success releases interface. Replaces isc_service_detach().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
query(StatusType* status, unsigned sendLength, const unsigned char*
sendItems, unsigned receiveLength, const unsigned char*
receiveItems, unsigned bufferLength, unsigned char* buffer) send
and request information to/from service, with different receiveItems
may be used for both running services and to obtain various
server-wide information. Replaces isc_service_query().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
start(StatusType* status, unsigned spbLength, const unsigned char*
spb) start utility in services manager. Replaces
isc_service_start().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
cancel(StatusType* status) cancel wait of current query() call.
Supported only for embedded connections.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Statement"></a><font size="4" style="font-size: 14pt">Statement
interface replaces (partially) isc_stmt_handle.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
getInfo(StatusType* status, unsigned itemsLength, const unsigned
char* items, unsigned bufferLength, unsigned char* buffer)
replaces isc_dsql_sql_info().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getType(StatusType* status) statement type, currently can be
found only in firebird sources in dsql/dsql.h.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getPlan(StatusType* status, FB_BOOLEAN detailed) returns
statement execution plan.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ISC_UINT64
getAffectedRecords(StatusType* status) returns number of records
affected by statement.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IMessageMetadata*
getInputMetadata(StatusType* status) returns parameters
metadata.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IMessageMetadata*
getOutputMetadata(StatusType* status) returns output values
metadata.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ITransaction*
execute(StatusType* status, ITransaction* transaction,
IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata*
outMetadata, void* outBuffer) executes any SQL statement except
returning multiple rows of data. Partial analogue of
isc_dsql_execute2() - in and out XSLQDAs replaced with input and
output messages with appropriate buffers.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IResultSet*
openCursor(StatusType* status, ITransaction* transaction,
IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata*
outMetadata, unsigned flags) executes SQL statement potentially
returning multiple rows of data. Returns <a href="#ResultSet">ResultSet</a>
interface which should be used to fetch that data. Format of output
data is defined by outMetadata parameter, leaving it NULL default
format may be used. Parameter flags is needed to open bidirectional
cursor setting it's value to Istatement::CURSOR_TYPE_SCROLLABLE.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IBatch*
createBatch(StatusType* status, IMessageMetadata* inMetadata, uint
parLength, const uchar* par) creates <a href="#Batch">Batch</a>
interface to SQL statement with input parameters making it possible
to execute that statement with multiple sets of parameters. Format
of input data is defined by inMetadata parameter, leaving it NULL
makes batch use default format from this interface. Parameters block
may be passed to createBatch() making it possible to adjust batch
behavior.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setCursorName(StatusType* status, const char* name) replaces
isc_dsql_set_cursor_name().</font>
</p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
free(StatusType* status) free statement, releases interface on
success.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getFlags(StatusType* status) returns <a href="#Values returned by getFlags">flags</a>
describing how this statement should be executed, simplified
replacement of getType() method.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Constants
defined by Statement interface:</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IAttachment::prepare()
flags:</font></p>
<p style="margin-left: 0.96cm; text-indent: -0.02cm; margin-bottom: 0cm; page-break-before: auto; page-break-after: auto">
<font size="4" style="font-size: 14pt">PREPARE_PREFETCH_NONE
constant to pass no flags, 0 value.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">The
following flags may be OR-ed to get desired set of flags:</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">PREPARE_PREFETCH_TYPE</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">PREPARE_PREFETCH_INPUT_PARAMETERS</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">PREPARE_PREFETCH_OUTPUT_PARAMETERS</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">PREPARE_PREFETCH_LEGACY_PLAN</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">PREPARE_PREFETCH_DETAILED_PLAN</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">PREPARE_PREFETCH_AFFECTED_RECORDS</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">PREPARE_PREFETCH_FLAGS
(flags returned by getFlags() method)</font></p>
</ol>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Frequently
used combinations of flags:</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">PREPARE_PREFETCH_METADATA</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">PREPARE_PREFETCH_ALL
</font>
</p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Values returned by getFlags"></a>
<font size="4" style="font-size: 14pt">Values returned by getFlags()
method: </font>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FLAG_HAS_CURSOR
use openCursor() to execute this statement, not execute()</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FLAG_REPEAT_EXECUTE
when prepared statement may be executed many times with different
parameters</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Flags passed to openCursor"></a>
<font size="4" style="font-size: 14pt">Flags passed to openCursor():</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">CURSOR_TYPE_SCROLLABLE
open bidirectional cursor.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Status"></a><font size="4" style="font-size: 14pt">Status
interface replaces ISC_STATUS_ARRAY. Functionality is extended
Status has separate access to errors and warnings vectors, can hold
vectors of unlimited length, itself stores strings used in vectors
avoiding need in circular strings buffer. In C++ Status is always
used under status wrapper, C++ API provides two different <a href="#Status Wrapper">wrappers</a>
having different behavior when error is returned from API call.
Interface is on purpose minimized (methods like convert it to text
are moved to <a href="#Util">Util</a> interface) in order to simplify
it's implementation by users when needed.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
init() - cleanup interface, set it to initial state.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getState() - get current state of interface, returns <a href="#returned by getState">state
flags</a>, may be OR-ed.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setErrors2(unsigned length, const intptr_t* value) set contents
of errors vector with length explicitly specified in a call.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setWarnings2(unsigned length, const intptr_t* value) set
contents of warnings vector with length explicitly specified in a
call.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setErrors(const intptr_t* value) set contents of errors vector,
length is defined by value context.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setWarnings(const intptr_t* value) set contents of warnings
vector, length is defined by value context.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
intptr_t* getErrors() - get errors vector.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
intptr_t* getWarnings() - get warnings vector.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IStatus*
clone() - create clone of current interface.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Constants
defined by Status interface:</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="returned by getState"></a><font size="4" style="font-size: 14pt">Flags
set in the value, returned by getState() method:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">STATE_WARNINGS</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">STATE_ERRORS</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Completion codes"></a><font size="4" style="font-size: 14pt">Completion
codes:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">RESULT_ERROR</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">RESULT_OK</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">RESULT_NO_DATA</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">RESULT_SEGMENT</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Timer"></a><font size="4" style="font-size: 14pt">Timer
interface user timer. Callback interface which should be
implemented by user to use firebird timer.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
handler() - method is called when timer rings (or when server is
shutting down).</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="TimerControl"></a><font size="4" style="font-size: 14pt">TimerControl
interface very simple and not too precise implementation of
timer. Arrived here because existing timers are very much OS
dependent and may be used in programs that require to be portable and
do not need really high precision timer. Particularly execution of
given timer may be delayed if another one has not completed at the
moment when given timer should alarm.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
start(StatusType* status, ITimer* timer, ISC_UINT64 microSeconds)
start <a href="#Timer">ITimer</a> to alarm after given delay (in
microseconds, 10</font><sup><font size="4" style="font-size: 14pt">-6</font></sup>
<font size="4" style="font-size: 14pt">seconds). Timer will be waked
up only once after this call.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
stop(StatusType* status, ITimer* timer) stop <a href="#Timer">ITimer</a>.
It's not an error to stop not started timer thus avoiding problems
with races between stop() and timer alarm.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Transaction"></a><font size="4" style="font-size: 14pt">Transaction
interface replaces isc_tr_handle.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
getInfo(StatusType* status, unsigned itemsLength, const unsigned
char* items, unsigned bufferLength, unsigned char* buffer)
replaces isc_transaction_info().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
prepare(StatusType* status, unsigned msgLength, const unsigned char*
message) replaces isc_prepare_transaction2(), with zero
msgLength behaves like isc_prepare_transaction() automatically
generating appropriate message.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
commit(StatusType* status) replaces isc_commit_transaction().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
commitRetaining(StatusType* status) replaces
isc_commit_retaining().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
rollback(StatusType* status) replaces
isc_rollback_transaction().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
rollbackRetaining(StatusType* status) replaces
isc_rollback_retaining().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
disconnect(StatusType* status) replaces
fb_disconnect_transaction().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ITransaction*
join(StatusType* status, ITransaction* transaction) joins
current transaction and passed as parameter transaction into single
distributed transaction (using <a href="#Dtc">Dtc</a>). On success
both current transaction and passed as parameter transaction are
released and should not be used any more.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ITransaction*
validate(StatusType* status, IAttachment* attachment) this
method is used to support distributed transactions coordinator.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ITransaction*
enterDtc(StatusType* status) this method is used to support
distributed transactions coordinator.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="VersionCallback"></a><font size="4" style="font-size: 14pt">VersionCallback
interface callback for <a href="#Util">Util</a>::getFbVersion().</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
callback(StatusType* status, const char* text) called by
firebird engine for each line in multiline version report. Makes it
possible to print that lines one by one, place them into message box
in any GUI, etc.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Util"></a><font size="4" style="font-size: 14pt">Util
interface various helper methods required here or there.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
getFbVersion(StatusType* status, IAttachment* att, IVersionCallback*
callback) produce long and beautiful report about firebird
version used. It may be seen in ISQL when invoked with -Z switch.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
loadBlob(StatusType* status, ISC_QUAD* blobId, IAttachment* att,
ITransaction* tra, const char* file, FB_BOOLEAN txt) load blob
from file.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
dumpBlob(StatusType* status, ISC_QUAD* blobId, IAttachment* att,
ITransaction* tra, const char* file, FB_BOOLEAN txt) save blob
to file.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
getPerfCounters(StatusType* status, IAttachment* att, const char*
countersSet, ISC_INT64* counters) get statistics for given
attachment.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IAttachment*
executeCreateDatabase(StatusType* status, unsigned stmtLength, const
char* creatDBstatement, unsigned dialect, FB_BOOLEAN*
stmtIsCreateDb) execute “CREATE DATABASE ...” statement
ISC trick with NULL statement handle does not work with interfaces.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
decodeDate(ISC_DATE date, unsigned* year, unsigned* month, unsigned*
day) replaces isc_decode_sql_date().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
decodeTime(ISC_TIME time, unsigned* hours, unsigned* minutes,
unsigned* seconds, unsigned* fractions) replaces
isc_decode_sql_time().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ISC_DATE
encodeDate(unsigned year, unsigned month, unsigned day) replaces
isc_encode_sql_date().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ISC_TIME
encodeTime(unsigned hours, unsigned minutes, unsigned seconds,
unsigned fractions) replaces isc_encode_sql_time().</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
formatStatus(char* buffer, unsigned bufferSize, IStatus* status)
replaces fb_interpret(). Size of buffer, passed into this method,
should not be less than 50 bytes.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getClientVersion() returns integer, containing major version in
byte 0 and minor version in byte 1.</font></p>
<li><p style="margin-bottom: 0cm"><font color="#000000"><font size="4" style="font-size: 14pt">IXpbBuilder*
getXpbBuilder(StatusType* status, unsigned kind, const unsigned
char* buf, unsigned len) returns <a href="#XpbBuilder">XpbBuilder</a>
interface. Valid <a href="#Valid builder types">kinds</a> are
enumerated in <a href="#XpbBuilder">XpbBuilder</a>.</font></font></p>
<li><p style="margin-bottom: 0cm"><font color="#000000"><font size="4" style="font-size: 14pt">unsigned
setOffsets(StatusType* status, IMessageMetadata* metadata,
IOffsetsCallback* callback) sets valid offsets in
<a href="#MessageMetadata">MessageMetadata</a>. Performs calls to
callback in <a href="#OffsetsCallback">OffsetsCallback</a> for each
field/parameter.</font></font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IDecFloat16*
getDecFloat16(StatusType* status) access <a href="#DecFloat16">DecFloat16</a>
interface.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IDecFloat34*
getDecFloat34(StatusType* status) access <a href="#DecFloat34">DecFloat34</a>
interface.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
decodeTimeTz(StatusType* status, const ISC_TIME_TZ* timeTz,
unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned*</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">fractions,
unsigned timeZoneBufferLength, char* timeZoneBuffer) decode time
taking time zone into an account.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
decodeTimeStampTz(StatusType* status, const ISC_TIMESTAMP_TZ*
timeStampTz, unsigned* year, unsigned* month, unsigned* day,
unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned*
fractions, unsigned timeZoneBufferLength, char* timeZoneBuffer) -
decode timestamp taking time zone into an account.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
encodeTimeTz(StatusType* status, ISC_TIME_TZ* timeTz, unsigned
hours, unsigned minutes, unsigned seconds, unsigned fractions, const
char* timeZone) encode time taking time zone into an account.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
encodeTimeStampTz(StatusType* status, ISC_TIMESTAMP_TZ* timeStampTz,
unsigned year, unsigned month, unsigned day, unsigned hours,
unsigned minutes, unsigned seconds, unsigned fractions, const char*
timeZone) encode timestamp taking time zone into an account.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IInt128*
getInt128(Status status) access <a href="#Int128">Int128</a>
interface.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
decodeTimeTzEx(Status status, const ISC_TIME_TZ_EX* timeTz, uint*
hours, uint* minutes, uint* seconds, uint* fractions, uint
timeZoneBufferLength, string timeZoneBuffer) decode time taking
extended time zone into an account.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
decodeTimeStampTzEx(Status status, const ISC_TIMESTAMP_TZ_EX*
timeStampTz, uint* year, uint* month, uint* day, uint* hours, uint*
minutes, uint* seconds, uint* fractions, uint timeZoneBufferLength,
string timeZoneBuffer) decode timestamp taking extended time
zone into an account.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="XpbBuilder"></a><font size="4" style="font-size: 14pt">XpbBuilder
methods:</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
clear(StatusType* status) reset builder to empty state.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
removeCurrent(StatusType* status) removes current clumplet.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
insertInt(StatusType* status, unsigned char tag, int value)
inserts a clumplet with value representing integer in network
format.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
insertBigInt(StatusType* status, unsigned char tag, ISC_INT64 value)
inserts a clumplet with value representing integer in network
format.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
insertBytes(StatusType* status, unsigned char tag, const void*
bytes, unsigned length) - inserts a clumplet with value containing
passed bytes.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
insertTag(StatusType* status, unsigned char tag) inserts a
clumplet without a value.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_BOOLEAN
isEof(StatusType* status) checks that there is no current
clumplet.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
moveNext(StatusType* status) moves to next clumplet.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
rewind(StatusType* status) moves to first clumplet.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_BOOLEAN
findFirst(StatusType* status, unsigned char tag) finds first
clumplet with given tag.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_BOOLEAN
findNext(StatusType* status) finds next clumplet with given tag.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
char getTag(StatusType* status) returns tag for current
clumplet.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getLength(StatusType* status) returns length of current clumplet
value.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
getInt(StatusType* status) returns value of current clumplet as
integer.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ISC_INT64
getBigInt(StatusType* status) returns value of current clumplet
as 64-bit integer.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getString(StatusType* status) returns value of current
clumplet as pointer to zero-terminated string (pointer is valid till
next call to this method).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
unsigned char* getBytes(StatusType* status) returns value of
current clumplet as pointer to unsigned char.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getBufferLength(StatusType* status) returns length of parameters
block.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
unsigned char* getBuffer(StatusType* status) returns pointer to
parameters block.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Constants
defined by XpbBuilder interface:</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Valid builder types"></a><font size="4" style="font-size: 14pt">Valid
builder types:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">BATCH
(IBatch parameters block)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">BPB
(BLOB parameters block)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DPB
(database attachment parameters block)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">SPB_ATTACH
(service attachment parameters block)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">SPB_START
(start service parameters)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">SPB_SEND
(send items in <a href="#Service">IService</a>::query())</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">SPB_RECEIVE
(receive items in <a href="#Service">IService</a>::query())</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">SPB_RESPONSE
(response from <a href="#Service">IService</a>::query())</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">TPB
(transaction parameters block)</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Plugin, encrypting data
transferred over the wire.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Algorithms
performing encryption of data for different purposes are well known
for many years. The only “little” typical problem remaining is
where to get the top secret key to be used by that algorithm. Luckily
for network traffic encryption there is one good solution unique
encryption key should be generated by authentication plugin. At least
default SRP plugin can produce such a key. And that key is resistant
to attacks, including man-in-the-middle. Therefore was chosen a
method of providing keys for wire crypt plugin: get it from
authentication plugin. (In case when used authentication plugin can
not provide a key a pseudo-plugin may be added in AuthClient and
AuthServer lists to produce keys, something like two asymmetric
private/public pairs.) </font>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">CryptKey
interface is used to store a key provided by authentication plugin
and pass it to wire crypt plugin. This interface should be used as
follows when server or client authentication plugin is ready to
provide a key it asks <a href="#ServerBlock">ServerBlock</a> or
<a href="#ClientBlock">ClientBlock</a> to produce new CryptKey
interface and stores a key in it. Appropriate for <a href="#WireCryptPlugin">WireCryptPlugin</a>
type of key will be selected by firebird and passed to that
interface.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setSymmetric(StatusType* status, const char* type, unsigned
keyLength, const void* key) make it store symmetric key of given
type.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setAsymmetric(StatusType* status, const char* type, unsigned
encryptKeyLength, const void* encryptKey, unsigned decryptKeyLength,
const void* decryptKey) make it store pair of asymmetric keys of
given type.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
void* getEncryptKey(unsigned* length) get a key for encryption.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
void* getDecryptKey(unsigned* length) get a key for decryption
(in case of symmetric key produces same result as getEncryptKey()).</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="WireCryptPlugin"></a><font size="4" style="font-size: 14pt">WireCryptPlugin
interface is main interface of network crypt plugin. Like any other
such interface it should be implemented by author of the plugin.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getKnownTypes(StatusType* status) returns
whitespace/tab/comma separated list of acceptable keys.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setKey(StatusType* status, ICryptKey* key) plugin should use a
key passed to it by this call.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
encrypt(StatusType* status, unsigned length, const void* from, void*
to) encrypts a packet to be sent over the wire.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
decrypt(StatusType* status, unsigned length, const void* from, void*
to) decrypts a packet received from network.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Server side of
authentication plugin.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Authentication
plugin contains two required parts client and server and may also
contain related third part - user manager. During authentication
process firebird client invokes client plugin and sends generated by
it data to server, next server invokes server plugin and sends
generated by it data to client. This process repeats as long as both
plugins return AUTH_MORE_DATA code. AUTH_SUCCESS returned at server
side means successful authentication, AUTH_FAILED at any side
immediate abort of iterative process and failure reported to client,
AUTH_CONTINUE means that next plugin from the list of configured
authentication plugins should be tried.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">There
is no dedicated sample of authentication plugins but in firebird
sources in directory src/auth one can find AuthDbg plugin using which
one can learn on trivial example (no complex calculations like in Srp
and no calls to crazy WinAPI functions like in AuthSspi) how client
and server sides perform authentication handshake. </font>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Auth
interface does not contain methods, only some constants defining
codes return from authenticate() method of <a href="#Client">Client</a>
and <a href="#Server">Server</a>.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">AUTH_FAILED</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">AUTH_SUCCESS</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">AUTH_MORE_DATA</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">AUTH_CONTINUE</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Writer"></a><font size="4" style="font-size: 14pt">Writer
interface writes authentication parameters block.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
reset() - clear target block.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
add(StatusType* status, const char* name) add login name.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setType(StatusType* status, const char* value) set type of added
login (user, group, etc).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setDb(StatusType* status, const char* value) set security
database in which authentication was done.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="ServerBlock"></a><font size="4" style="font-size: 14pt">ServerBlock
interface is used by server side of authentication plugin to exchange
data with client.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getLogin() - get login name passed from client.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
unsigned char* getData(unsigned* length) get authentication data
passed from client.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
putData(StatusType* status, unsigned length, const void* data)
pass authentication data to client.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ICryptKey*
newKey(StatusType* status) create new wire crypt key and add it
to the list of available for wire crypt plugins.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Server"></a><font size="4" style="font-size: 14pt">Server
interface is main interface of server side of authentication plugin. </font>
</p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
authenticate(StatusType* status, IServerBlock* sBlock, IWriter*
writerInterface) perform single authentication step. Data
exchange with client is performed using sBlock interface. When some
authentication item is produced it should be added to authentication
block using writerInterface. Possible return values are defined in
<a href="#Auth">Auth</a> interface.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Client side of
authentication plugin.</font></h1>
<p style="margin-bottom: 0cm"><a name="ClientBlock"></a><font size="4" style="font-size: 14pt">ClientBlock
interface is used by client side of authentication plugin to exchange
data with server.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getLogin() - get login name if it is present in DPB.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getPassword() - get password if it is present in DPB.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
unsigned char* getData(unsigned* length) get authentication data
passed from server.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
putData(StatusType* status, unsigned length, const void* data)
pass authentication data to server.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ICryptKey*
newKey(StatusType* status) - create new wire crypt key and add it to
the list of available for wire crypt plugins.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Client"></a><font size="4" style="font-size: 14pt">Client
interface is main interface of client side of authentication plugin.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
authenticate(StatusType* status, IClientBlock* cBlock)1. perform
single authentication step. Data exchange with server is performed
using cBlock interface. Possible return values are defined in Auth
interface. AUTH_SUCCESS is treated by client side as AUTH_MORE_DATA
(i.e. client sends generated data to server and waits for an answer
from it).</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">User management plugin.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">This
plugin is actively related with server side of authentication it
prepares users' list for authentication plugin. Not each
authentication plugin requires user manager some may access list
of users created using non-firebird software (AuthSspi for example).
Record describing user consists of a number of fields and operation
which should be performed like add user, modify user, list user(s),
etc. Plugin must interpret commands received in <a href="#User">User</a>
interface.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="UserField"></a><font size="4" style="font-size: 14pt">UserField
interface is not used as standalone interface, it's base for
CharUserField and IntUserField. </font>
</p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
entered() - returns non-zero if a value for a field was entered
(assigned).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
specified() - return non-zero if NULL value was assigned to the
field.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setEntered(StatusType* status, int newValue) sets entered flag
to 0/non-zero value for a field. There is no method to assign NULL
for a field cause it's never needed. NULLs if used are assigned by
the code implementing interfaces and therefore having full access to
internals of them.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="CharUserField"></a><font size="4" style="font-size: 14pt">CharUserField
interface:</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* get() - get field's value as C-string (\0 terminated).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
set(StatusType* status, const char* newValue) assigns value to
the field. Sets entered flag to true.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="IntUserField"></a><font size="4" style="font-size: 14pt">IntUserField
interface:</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
get() - get field's value.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
set(StatusType* status, int newValue) assigns value to the
field. Sets entered flag to true.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="User"></a><font size="4" style="font-size: 14pt">User
interface is a list of methods accessing fields included into record
about the user. </font>
</p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
operation() - code of operation (see <a href="#Constants defined by User interface">list</a>
below).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ICharUserField*
userName() - login name.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ICharUserField*
password() - password. Always empty when listing users.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ICharUserField*
firstName() - this and 2 next are components of full user name.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ICharUserField*
lastName()</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ICharUserField*
middleName()</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ICharUserField*
comment() - comment (from SQL operator COMMENT ON USER IS …).</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ICharUserField*
attributes() - tags in a form tag1=val1, tag2=val2, …, tagN=valN.
Val may be empty than means that tag will be deleted.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IIntUserField*
active() - changes ACTIVE/INACTIVE setting for user.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IIntUserField*
admin() - sets/drops admin rights for the user.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
clear(StatusType* status) sets all fields to not entered and not
specified.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Constants defined by User interface"></a>
<font size="4" style="font-size: 14pt">Constants defined by User
interface valid codes of operation.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">OP_USER_ADD
create user</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">OP_USER_MODIFY
alter user</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">OP_USER_DELETE
drop user</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">OP_USER_DISPLAY
show user</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">OP_USER_SET_MAP
turn on mapping of windows admins to role rdb$admin</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">OP_USER_DROP_MAP
turn off mapping of windows admins to role rdb$admin</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="ListUsers"></a><font size="4" style="font-size: 14pt">ListUsers
interface is callback used by authentication plugin when list users
operation is requested. Plugin fills <a href="#User">User</a>
interface for all items in list of users one by one and for each user
calls list() method of this interface.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
list(StatusType* status, IUser* user) callback function.
Implementation can do what it wants with received data. For example,
it may put data from user parameter into output stream of listing
service or place into special tables from SEC$ group.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="LogonInfo"></a><font size="4" style="font-size: 14pt">LogonInfo
interface contains data passed to user mamngement plugin to attach to
security database with valid credentials. Pres</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* name() - returns current attachment's login name.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* role() - returns current attachment's active role.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* networkProtocol() - returns current attachment's network
protocol. Currently not used by plugins.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* remoteAddress() - returns current attachment's remote address.
Currently not used by plugins.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
unsigned char* authBlock(unsigned* length) returns current
attachment's authentication block. When not NULL overrides login
name.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="Management"></a><font size="4" style="font-size: 14pt">Management
interface is main interface of user management plugin.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
start(StatusType* status, ILogonInfo* logonInfo) starts plugin,
if needed it attaches to security database to manage may be (it's
plugin-dependent solution use it or not) using credentials from
logonInfo. </font>
</p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
execute(StatusType* status, IUser* user, IListUsers* callback)
executes a command provided by operation() method of user parameter.
If needed callback interface will be used. Parameter callback may
have NULL value for commands not requiring users' listing.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
commit(StatusType* status) commits changes done by calls to
execute() method.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
rollback(StatusType* status) rollbacks changes done by calls to
execute() method.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Database encryption
plugin.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">An
ability to encrypt database was present in firebird since interbase
times but appropriate places in the code were commented.
Implementation was suspicious crypt key was always sent from the
client in DPB, no attempt was made to hide it from the world and no
way was suggested to encrypt existing database. FB3 solves most of
the problems except probably the worst one how to manage crypt
keys. We suggest a kind of solution but it requires efforts in
plugins, i.e. there is no beautiful way to work with keys like it is
for wire crypt plugins.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Before
starting with own db crypt plugin one should take into an account the
following. We see two main usages of database encryption first,
it may be needed to avoid data loss if database server is physically
stolen, and second, it may be used to protect data in database which
is sailed together with special application accessing that data.
Requirements for this usages are quite different. In first case we
may trust database server that it is not modified to steal keys
passed to security plugin i.e. we expect that key will not be
sent to inappropriate server. In second case server may be modified
in some way to steal keys (if they are passed from application to
plugin via server code) or even data (as the last resort to dump
blocks from the cache where they are in non-encrypted form).
Therefore your plugin should make sure that it's running with not
modified firebird binaries and your application before sending a key
to plugin should make sure it's really required plugin, may be asking
a kind of digital signature from it. Making sure that network line is
encrypted (parsing output of <a href="#Util">Util</a>::getFbVersion())
or using some own encryption of a key is also very good idea in case
when network access to the server is used. All this job should be
done in plugin (and application working with it) i.e. database block
encryption algorithm by itself may happen to be easiest part of db
crypt plugin, specially when some standard library is used for it.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="CryptKeyCallback"></a><font size="4" style="font-size: 14pt">CryptKeyCallback
interface should be provided by a side sending crypt key to db crypt
plugin or key holder plugin.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
callback(unsigned dataLength, const void* data, unsigned
bufferLength, void* buffer) when performing callback information
is passed in both directions. The source of a key receives
dataLength bytes of data and may send up to bufferLength bytes into
buffer returning actual number of bytes placed into buffer.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">
void dispose() - invoked when interface is not needed any more. Helps
to avoid memory leaks in statefull inrefaces.
</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">
unsigned afterAttach(StatusType* status, const char* dbName, const IStatus* attStatus) -
invoked after attach on client system. NULL in attStatus means attach was successful
but afterAttach() is anyway invoked in order to let plugin perform required cleanup.
The following values may be returned by this function:
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">NO_RETRY -
do not repeat attempts to attach to database.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">DO_RETRY -
retry attach (ignored when function was called without attStatus).</font></p>
</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="DbCryptInfo"></a><font size="4" style="font-size: 14pt">DbCryptInfo
interface is passed to DbCryptPlugin by engine. Plugin may save this
interface and use when needed to obtain additional informatio about
database.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">const
char* getDatabaseFullPath(StatusType* status) returns full
(including path) name of primary database file.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="DbCryptPlugin"></a><font size="4" style="font-size: 14pt">DbCryptPlugin
interface is main interface of database crypt plugin.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setKey(StatusType* status, unsigned length, IKeyHolderPlugin**
sources, const char* keyName) is used to provide to db crypt
plugin information about encryption key. Firebird never passes keys
for this type of plugin directly. Instead array of <a href="#KeyHolderPlugin">KeyHolderPlugins</a>
of given length is passed to crypt plugin which must get from one of
it <a href="#CryptKeyCallback">CryptKeyCallback</a> interface and
get a key using it. Parameter keyName is a name of a key like it was
entered in “ALTER DATABASE ENCRYPT …” operator.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
encrypt(StatusType* status, unsigned length, const void* from, void*
to) encrypts data before writing block to database file.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
decrypt(StatusType* status, unsigned length, const void* from, void*
to) decrypts data after reading block from database file.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setInfo(StatusType* status, IDbCryptInfo* info) in this method
crypt plugin typically saves informational interface for future use.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Key holder for database
encryption plugin.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">This
type of plugin is needed to delineate functionality db crypt
plugin is dealing with actual encryption, key holder solves questions
related with providing it a key in secure way. Plugin may be received
from application or loaded in some other way (up to using flash
device inserted into server at firebird startup time).</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="KeyHolderPlugin"></a><font size="4" style="font-size: 14pt">KeyHolderPlugin
interface is main interface of database crypt key holder plugin.</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
keyCallback(StatusType* status, ICryptKeyCallback* callback) is
used to pass attachment's <a href="#CryptKeyCallback">CryptKeyCallback</a>
interface (if provided by user with <a href="#Provider">Provider</a>::setDbCryptCallback()
call). This call is always performed at database attach moment, and
some holders may reject attachment if satisfactory key was not
provided. Return value of 0 means that key holder can not provide a
key to crypt plugin, non-zero can.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ICryptKeyCallback*
keyHandle(StatusType* status, const char* keyName) is intended
to be called by <a href="#DbCryptPlugin">DbCryptPlugin</a> directly
to obtain callback interface for named key from key holder. This
makes it possible for open source firebird code to never touch
actual keys avoiding ability to steal a key changing firebird code.
After getting <a href="#CryptKeyCallback">CryptKeyCallback</a>
interface crypt plugin starts data exchange using it. Key holder can
(for example) check digital signature of crypt plugin before sending
a key to it in order to avoid use of modified crypt plugin able to
steal secret key.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FB_BOOLEAN
useOnlyOwnKeys(StatusType* status) informs firebird engine
whether a key, provided by key holder, can be used in other
attachments. Makes sense only for SuperServer only it can share
database crypt keys between attachments. Returning FB_TRUE from this
method enforces firebird to make sure that this particular key
holder (and therefore in turn attachment related to it) provides
correct crypt key for any other attachment to this database.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ICryptKeyCallback*
chainHandle(StatusType* status) support of a chain of key
holders. In some cases key has to pass through more than single key
holder before it reaches db crypt plugin. This is needed (for
example) to support execute statement in encrypted database. This is
just a sample chains are also used in some other cases. Callback
interface, returned by this method, may differ from one returned by
keyHandle() function (see above). Typically is should be able to
duplicate one-to-one keys, received by KeyHolderPlugin when
keyCallback() function is invoked.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Non-interface objects used
by API (C++ specific header Message.h).</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Following
3 classes are used to represent date, time and timestamp (datetime)
when using FB_MESSAGE macro. Members of data structure, representing
static message, correspondint to fields of types FB_DATE / FB_TIME /
FB_TIMESTAMP will have a type of one of this classes. Knowing methods
/ members (which are enough self-descriptive to avoid describing them
here) of this classes is needed to access date and time fields in
static messages.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="FbDate"></a><font size="4" style="font-size: 14pt">class
FbDate methods:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
decode(IUtil* util, unsigned* year, unsigned* month, unsigned* day)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getYear(IUtil* util) </font>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getMonth(IUtil* util)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getDay(IUtil* util)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
encode(IUtil* util, unsigned year, unsigned month, unsigned day)</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><a name="FbTime"></a><font size="4" style="font-size: 14pt">class
FbTime methods:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
decode(IUtil* util, unsigned* hours, unsigned* minutes, unsigned*
seconds, unsigned* fractions)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getHours(IUtil* util)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getMinutes(IUtil* util)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getSeconds(IUtil* util)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getFractions(IUtil* util)</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
encode(IUtil* util, unsigned hours, unsigned minutes, unsigned
seconds, unsigned fractions)</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">class
FbTimestamp members:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FbDate
date;</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FbTime
time;</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Following
two templates are used in static messages to represent CHAR(N) and
VARCHAR(N) fields. </font>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">template
&lt;unsigned N&gt;</font></p>
<p style="margin-bottom: 0cm"><a name="FbChar"></a><font size="4" style="font-size: 14pt">struct
FbChar</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">{</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">char
str[N];</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">};</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">template
&lt;unsigned N&gt;</font></p>
<p style="margin-bottom: 0cm"><a name="FbVarChar"></a><font size="4" style="font-size: 14pt">struct
FbVarChar</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">{</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">ISC_USHORT
length;</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">char
str[N];</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
set(const char* s);</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">};</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<p style="margin-bottom: 0cm"><br/>
</p>
<hr/>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">This
document is currently missing 2 types of plugins ExternalEngine
and Trace. Information about them will be made available in next
release of it.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
</body>
</html>