8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 15:23:03 +01:00

Implemented #6959: IBatch::getInfo()

This commit is contained in:
AlexPeshkoff 2021-09-10 17:18:48 +03:00
parent a6d36dac0f
commit 33caf198dd
22 changed files with 798 additions and 333 deletions

View File

@ -6,7 +6,7 @@
<meta name="generator" content="LibreOffice 6.4.7.2 (Linux)"/>
<meta name="author" content="alex "/>
<meta name="created" content="2013-05-31T00:00:00.010003100"/>
<meta name="changed" content="2021-08-12T15:06:38.252335410"/>
<meta name="changed" content="2021-09-10T16:41:58.007600045"/>
<meta name="created" content="00:00:00">
<meta name="created" content="00:00:00">
<meta name="created" content="00:00:00">
@ -30,21 +30,19 @@ 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
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><font size="4" style="font-size: 14pt">)</font></strong><strong>
</strong><strong><font size="4" style="font-size: 14pt">but
it's not convenient for direct use from most languages. Therefore
<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++
@ -74,8 +72,7 @@ for it.</font></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 </font><font size="4" style="font-size: 14pt">fictitious
database encryption</font><font size="4" style="font-size: 14pt">)
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/>
@ -101,8 +98,8 @@ too.</font></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>
<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
@ -157,8 +154,7 @@ 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)
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">,
@ -274,8 +270,7 @@ 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>
<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)
@ -319,8 +314,8 @@ may take a look at how to start and commit transaction in examples
<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>
<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>
@ -353,8 +348,8 @@ may take a look at how to start and commit transaction in examples
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Executing
SQL operator with input parameters.</font></h1>
<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
@ -434,21 +429,20 @@ fields:</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>
<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>
<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
@ -513,8 +507,8 @@ 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>
<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
@ -596,10 +590,10 @@ 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>
<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
@ -617,8 +611,8 @@ metadata values like it's done in our samples 03.select.cpp and
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Using
FB_MESSAGE macro for static messages.</font></h1>
<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
@ -667,12 +661,11 @@ 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
</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>
<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,
@ -717,8 +710,7 @@ 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>
<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
@ -911,16 +903,16 @@ meta = batch-&gt;getMetadata(&amp;status);</i></font></p>
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>
<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>
<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,
@ -930,15 +922,15 @@ 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>
<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>
<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
@ -949,19 +941,19 @@ 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><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>
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
@ -969,18 +961,17 @@ for some reason you want to make batch buffers empty not executing it
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>
<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>
<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>
<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/>
@ -1002,27 +993,23 @@ returned in </font><font color="#000000"><font size="4" style="font-size: 14pt">
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><br/>
<br/>
<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>
<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>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
@ -1031,19 +1018,18 @@ it batch may be executed or next portion of messages added to it.</font></p>
<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">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
@ -1053,29 +1039,26 @@ 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><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;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>
<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,
@ -1085,13 +1068,13 @@ 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>
<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
@ -1104,17 +1087,16 @@ 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">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>
<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()
@ -1124,13 +1106,12 @@ 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><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
@ -1161,8 +1142,7 @@ 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
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
@ -1171,50 +1151,47 @@ 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>
<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>
<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>
<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>
<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
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
@ -1235,8 +1212,7 @@ with ISC API. Please use for additional details our sample
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Using
services.</font></h1>
<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
@ -1364,15 +1340,14 @@ plugins.</font></font></p>
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
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
@ -1392,28 +1367,28 @@ 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>
<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
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>
<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/>
@ -1480,15 +1455,15 @@ 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>
</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>
@ -1502,8 +1477,8 @@ 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>
<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
@ -1534,15 +1509,14 @@ config(cnf), refCounter(0), owner(NULL)</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>
<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>
@ -1551,9 +1525,9 @@ NULL.</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>
<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>
@ -1582,8 +1556,8 @@ addRef()</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>
<font size="4" style="font-size: 14pt">Absolutely typical
implementation of reference counted object.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
@ -1605,8 +1579,8 @@ getOwner()</i></font></p>
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>
<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>
@ -1635,8 +1609,7 @@ 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>
<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
@ -1660,12 +1633,12 @@ p;</i></font></p>
</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
<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
@ -1674,8 +1647,8 @@ 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>
<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
@ -1749,8 +1722,7 @@ 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>
<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>
@ -1827,12 +1799,10 @@ interface replaces isc_db_handle:</font></p>
<li><p><font size="4" style="font-size: 14pt">I</font><font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">Batch*
createBatch(StatusType* status, ITransaction* transaction, unsigned
stmtLength, const char* sqlStmt, unsigned dialect, IMessageMetadata*
inMetadata, unsigned</font></font>
<font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">parLength,
inMetadata, unsigned</font></font> <font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">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</font></font>
<font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">NULL
inMetadata format. Leaving inMetadata</font></font> <font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">NULL
makes batch use default format for sqlStmt. Parameters block may be
passed to createBatch() making it possible to adjust batch behavior.</font></font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IEvents*
@ -1861,15 +1831,13 @@ 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,
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,
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.
@ -1932,6 +1900,10 @@ parameters in single statement execution.</font></p>
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>
@ -1960,6 +1932,17 @@ used to store blobs:</font></p>
- 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>
@ -2156,10 +2139,10 @@ sub-entries) in firebird configuration file:</font></p>
</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>
<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)
@ -2794,8 +2777,8 @@ defined by Statement interface:</font></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>
<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>
@ -2820,8 +2803,8 @@ used combinations of flags:</font></p>
</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>
<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>
@ -2832,8 +2815,7 @@ parameters</font></p>
</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>
<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/>
@ -2931,8 +2913,8 @@ moment when given timer should alarm.</font></p>
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>
<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
@ -3066,8 +3048,8 @@ interface various helper methods required here or there.</font></p>
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>
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,
@ -3195,8 +3177,8 @@ builder types:</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>
<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
@ -3262,8 +3244,8 @@ such interface it should be implemented by author of the plugin.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Server
side of authentication plugin.</font></h1>
<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
@ -3350,8 +3332,8 @@ interface is main interface of server side of authentication plugin. </font>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Client
side of authentication plugin.</font></h1>
<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>
@ -3387,8 +3369,7 @@ interface is main interface of client side of authentication plugin.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">User
management plugin.</font></h1>
<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
@ -3481,8 +3462,8 @@ about the user. </font>
</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>
<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
@ -3559,8 +3540,8 @@ interface is main interface of user management plugin.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Database
encryption plugin.</font></h1>
<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.
@ -3651,8 +3632,8 @@ interface is main interface of database crypt plugin.</font></p>
<p style="margin-bottom: 0cm"><br/>
</p>
<h1><font size="4" style="font-size: 14pt">Key
holder for database encryption plugin.</font></h1>
<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
@ -3706,8 +3687,8 @@ interface is main interface of database crypt key holder plugin.</font></p>
<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>
<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

View File

@ -114,6 +114,53 @@ unsigned putSegment(unsigned char*& ptr, const char* testData)
return align(l + sizeof l, IBatch::BLOB_SEGHDR_ALIGN);
}
// batch info printer - prints what we know about batch
static void printInfo(ThrowStatusWrapper& status, const char* hdr, IBatch* b, IUtil* utl)
{
printf("\n%s\n", hdr);
const unsigned char items[] = {IBatch::INF_BLOB_ALIGNMENT, IBatch::INF_BUFFER_BYTES_SIZE,
IBatch::INF_DATA_BYTES_SIZE, IBatch::INF_BLOBS_BYTES_SIZE};
unsigned char buffer[29];
b->getInfo(&status, sizeof items, items, sizeof buffer, buffer);
IXpbBuilder* pb = utl->getXpbBuilder(&status, IXpbBuilder::INFO_RESPONSE, buffer, sizeof buffer);
for (pb->rewind(&status); !pb->isEof(&status); pb->moveNext(&status))
{
int val = pb->getInt(&status);
const char* text = "Unknown tag";
switch (pb->getTag(&status))
{
case IBatch::INF_BLOB_ALIGNMENT:
text = "Blob alignment";
break;
case IBatch::INF_BUFFER_BYTES_SIZE:
text = "Buffer size";
break;
case IBatch::INF_DATA_BYTES_SIZE:
text = "Messages size";
break;
case IBatch::INF_BLOBS_BYTES_SIZE:
text = "Blobs size";
break;
case isc_info_truncated:
printf(" truncated\n");
// fall down...
case isc_info_end:
pb->dispose();
return;
default:
printf("Unexpected item %d\n", pb->getTag(&status));
pb->dispose();
return;
}
printf("%s = %d\n", text, val);
}
pb->dispose();
}
// BatchCompletionState printer - prints all what we know about completed batch
static void print_cs(ThrowStatusWrapper& status, IBatchCompletionState* cs, IUtil* utl)
@ -270,6 +317,9 @@ int main()
batch->add(&status, 1, project1.getData());
}
// check batch state
printInfo(status, "Info when added many records", batch, utl);
// ... and cancel that records
batch->cancel(&status);
@ -347,6 +397,8 @@ int main()
batch->appendBlobData(&status, strlen(sqlStmt1), sqlStmt1);
batch->add(&status, 1, project2.getData());
printInfo(status, "Info with blob", batch, utl);
// execute it
cs = batch->execute(&status, tra);
print_cs(status, cs, utl);

View File

@ -242,6 +242,7 @@ UCHAR ClumpletReader::getBufferTag() const
case SpbReceiveItems:
case SpbResponse:
case InfoResponse:
case InfoItems:
usage_mistake("buffer is not tagged");
return 0;
case SpbAttach:
@ -313,6 +314,7 @@ ClumpletReader::ClumpletType ClumpletReader::getClumpletType(UCHAR tag) const
}
return StringSpb;
case SpbReceiveItems:
case InfoItems:
return SingleTpb;
case SpbStart:
switch(tag)
@ -704,6 +706,7 @@ void ClumpletReader::rewind()
case SpbReceiveItems:
case SpbResponse:
case InfoResponse:
case InfoItems:
cur_offset = 0;
break;
default:

View File

@ -58,7 +58,8 @@ public:
SpbSendItems,
SpbReceiveItems,
SpbResponse,
InfoResponse
InfoResponse,
InfoItems
};
struct KindList

View File

@ -36,15 +36,23 @@
namespace Firebird {
ClumpletWriter::ClumpletWriter(Kind k, FB_SIZE_T limit, UCHAR tag) :
ClumpletReader(k, NULL, 0), sizeLimit(limit), kindList(NULL), dynamic_buffer(getPool())
ClumpletWriter::ClumpletWriter(Kind k, FB_SIZE_T limit, UCHAR tag)
: ClumpletReader(k, NULL, 0),
sizeLimit(limit),
kindList(NULL),
dynamic_buffer(getPool()),
flag_overflow(false)
{
initNewBuffer(tag);
rewind();
}
ClumpletWriter::ClumpletWriter(MemoryPool& given_pool, Kind k, FB_SIZE_T limit, UCHAR tag) :
ClumpletReader(given_pool, k, NULL, 0), sizeLimit(limit), kindList(NULL), dynamic_buffer(getPool())
ClumpletWriter::ClumpletWriter(MemoryPool& given_pool, Kind k, FB_SIZE_T limit, UCHAR tag)
: ClumpletReader(given_pool, k, NULL, 0),
sizeLimit(limit),
kindList(NULL),
dynamic_buffer(getPool()),
flag_overflow(false)
{
initNewBuffer(tag);
rewind();
@ -82,46 +90,72 @@ void ClumpletWriter::initNewBuffer(UCHAR tag)
}
ClumpletWriter::ClumpletWriter(Kind k, FB_SIZE_T limit, const UCHAR* buffer, FB_SIZE_T buffLen, UCHAR tag)
: ClumpletReader(k, NULL, 0), sizeLimit(limit), kindList(NULL), dynamic_buffer(getPool())
: ClumpletReader(k, NULL, 0),
sizeLimit(limit),
kindList(NULL),
dynamic_buffer(getPool()),
flag_overflow(false)
{
create(buffer, buffLen, tag);
}
ClumpletWriter::ClumpletWriter(MemoryPool& pool, const KindList* kl, FB_SIZE_T limit,
const UCHAR* buffer, FB_SIZE_T buffLen)
: ClumpletReader(pool, kl, buffer, buffLen), sizeLimit(limit),
kindList(kl), dynamic_buffer(getPool())
: ClumpletReader(pool, kl, buffer, buffLen),
sizeLimit(limit),
kindList(kl),
dynamic_buffer(getPool()),
flag_overflow(false)
{
create(buffer, buffLen, kl->tag);
}
ClumpletWriter::ClumpletWriter(const KindList* kl, FB_SIZE_T limit, const UCHAR* buffer, FB_SIZE_T buffLen)
: ClumpletReader(kl, buffer, buffLen), sizeLimit(limit), kindList(kl), dynamic_buffer(getPool())
: ClumpletReader(kl, buffer, buffLen),
sizeLimit(limit),
kindList(kl),
dynamic_buffer(getPool()),
flag_overflow(false)
{
create(buffer, buffLen, kl->tag);
}
ClumpletWriter::ClumpletWriter(MemoryPool& pool, const KindList* kl, FB_SIZE_T limit)
: ClumpletReader(pool, kl, NULL, 0), sizeLimit(limit),
kindList(kl), dynamic_buffer(getPool())
: ClumpletReader(pool, kl, NULL, 0),
sizeLimit(limit),
kindList(kl),
dynamic_buffer(getPool()),
flag_overflow(false)
{
create(NULL, 0, kl->tag);
}
ClumpletWriter::ClumpletWriter(const KindList* kl, FB_SIZE_T limit)
: ClumpletReader(kl, NULL, 0), sizeLimit(limit), kindList(kl), dynamic_buffer(getPool())
: ClumpletReader(kl, NULL, 0),
sizeLimit(limit),
kindList(kl),
dynamic_buffer(getPool()),
flag_overflow(false)
{
create(NULL, 0, kl->tag);
}
ClumpletWriter::ClumpletWriter(MemoryPool& pool, const ClumpletWriter& from)
: ClumpletReader(pool, from), sizeLimit(from.sizeLimit), kindList(NULL), dynamic_buffer(getPool())
: ClumpletReader(pool, from),
sizeLimit(from.sizeLimit),
kindList(NULL),
dynamic_buffer(getPool()),
flag_overflow(false)
{
create(from.getBuffer(), from.getBufferEnd() - from.getBuffer(), from.isTagged() ? from.getBufferTag() : 0);
}
ClumpletWriter::ClumpletWriter(const ClumpletWriter& from)
: ClumpletReader(from), sizeLimit(from.sizeLimit), kindList(NULL), dynamic_buffer(getPool())
: ClumpletReader(from),
sizeLimit(from.sizeLimit),
kindList(NULL),
dynamic_buffer(getPool()),
flag_overflow(false)
{
create(from.getBuffer(), from.getBufferEnd() - from.getBuffer(), from.isTagged() ? from.getBufferTag() : 0);
}
@ -138,8 +172,11 @@ void ClumpletWriter::create(const UCHAR* buffer, FB_SIZE_T buffLen, UCHAR tag)
}
ClumpletWriter::ClumpletWriter(MemoryPool& given_pool, Kind k, FB_SIZE_T limit,
const UCHAR* buffer, FB_SIZE_T buffLen, UCHAR tag) :
ClumpletReader(given_pool, k, NULL, 0), sizeLimit(limit), dynamic_buffer(getPool())
const UCHAR* buffer, FB_SIZE_T buffLen, UCHAR tag)
: ClumpletReader(given_pool, k, NULL, 0),
sizeLimit(limit),
dynamic_buffer(getPool()),
flag_overflow(false)
{
if (buffer && buffLen) {
dynamic_buffer.push(buffer, buffLen);
@ -194,6 +231,13 @@ void ClumpletWriter::size_overflow()
fatal_exception::raise("Clumplet buffer size limit reached");
}
void ClumpletWriter::size_overflow(bool condition)
{
flag_overflow = condition;
if (condition)
size_overflow();
}
void ClumpletWriter::toVaxInteger(UCHAR* ptr, FB_SIZE_T length, const SINT64 value)
{
fb_assert(ptr && length > 0 && length < 9); // We can't handle numbers bigger than int64.
@ -362,9 +406,7 @@ void ClumpletWriter::insertBytesLengthCheck(UCHAR tag, const void* bytes, const
}
// Check that resulting data doesn't overflow size limit
if (dynamic_buffer.getCount() + length + lenSize + 1 > sizeLimit) {
size_overflow();
}
size_overflow(dynamic_buffer.getCount() + length + lenSize + 1 > sizeLimit);
// Insert the data
const FB_SIZE_T saved_offset = cur_offset;
@ -415,9 +457,7 @@ void ClumpletWriter::insertEndMarker(UCHAR tag)
}
// Check that resulting data doesn't overflow size limit
if (cur_offset + 1 > sizeLimit) {
size_overflow();
}
size_overflow(cur_offset + 1 > sizeLimit);
dynamic_buffer.shrink(cur_offset);
dynamic_buffer.push(tag);

View File

@ -98,10 +98,14 @@ public:
// Returns true if any found
bool deleteWithTag(UCHAR tag);
virtual const UCHAR* getBuffer() const;
const UCHAR* getBuffer() const override;
bool hasOverflow() const
{
return flag_overflow;
}
protected:
virtual const UCHAR* getBufferEnd() const;
const UCHAR* getBufferEnd() const override;
virtual void size_overflow();
void insertBytesLengthCheck(UCHAR tag, const void* bytes, const FB_SIZE_T length);
bool upgradeVersion(); // upgrade clumplet version - obtain newest from kindList
@ -117,14 +121,10 @@ private:
void initNewBuffer(UCHAR tag);
void create(const UCHAR* buffer, FB_SIZE_T buffLen, UCHAR tag);
static void toVaxInteger(UCHAR* ptr, FB_SIZE_T length, const SINT64 value);
void size_overflow(bool condition);
bool flag_overflow;
};
/*
template <>
void ClumpletWriter::insertString(UCHAR tag, const char*& str)
{
insertString(tag, str, strlen(str));
}
*/
} // namespace Firebird

View File

@ -30,7 +30,7 @@
#include "../jrd/exe_proto.h"
#include "../dsql/dsql.h"
#include "../dsql/errd_proto.h"
#include "../common/classes/ClumpletReader.h"
#include "../common/classes/ClumpletWriter.h"
#include "../common/classes/auto.h"
#include "../common/classes/fb_string.h"
#include "../common/utils_proto.h"
@ -966,6 +966,14 @@ ULONG DsqlBatch::DataCache::getSize() const
return m_used + m_cache.getCount();
}
ULONG DsqlBatch::DataCache::getCapacity() const
{
if (!m_cacheCapacity)
return 0;
return m_limit;
}
void DsqlBatch::DataCache::clear()
{
m_cache.clear();
@ -973,3 +981,99 @@ void DsqlBatch::DataCache::clear()
m_space->releaseSpace(0, m_used);
m_used = m_got = m_shift = 0;
}
void DsqlBatch::info(thread_db* tdbb, unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer)
{
// Sanity check
if (bufferLength < 3) // bigger values will be processed by later code OK
{
if (bufferLength-- > 0)
{
*buffer++ = isc_info_truncated;
if (bufferLength-- > 0)
*buffer++ = isc_info_end;
}
return;
}
ClumpletReader it(ClumpletReader::InfoItems, items, itemsLength);
ClumpletWriter out(ClumpletReader::InfoResponse, bufferLength - 1); // place for isc_info_truncated / isc_info_end
enum BufCloseState {BUF_OPEN, BUF_INTERNAL, BUF_END};
BufCloseState closeOut = BUF_OPEN;
try
{
bool flInfoLength = false;
for (it.rewind(); !it.isEof(); it.moveNext())
{
UCHAR item = it.getClumpTag();
if (item == isc_info_end)
break;
switch(item)
{
case IBatch::INF_BUFFER_BYTES_SIZE:
out.insertInt(item, m_messages.getCapacity());
break;
case IBatch::INF_DATA_BYTES_SIZE:
out.insertInt(item, FB_ALIGN(m_messages.getSize(), m_alignment));
break;
case IBatch::INF_BLOBS_BYTES_SIZE:
if (m_blobs.getSize())
out.insertInt(item, m_blobs.getSize());
break;
case IBatch::INF_BLOB_ALIGNMENT:
out.insertInt(item, BLOB_STREAM_ALIGN);
break;
case isc_info_length:
flInfoLength = true;
break;
default:
out.insertInt(isc_info_error, isc_infunk);
break;
}
}
// finalize writer
closeOut = BUF_INTERNAL; // finished adding internal info
out.insertTag(isc_info_end);
closeOut = BUF_END; // alreayd marked with isc_info_end but misses isc_info_length
if (flInfoLength)
{
out.rewind();
out.insertInt(isc_info_length, out.getBufferLength());
}
}
catch(const fatal_exception&)
{
// here it's sooner of all caused by writer overflow but carefully check that
if (out.hasOverflow())
{
memcpy(buffer, out.getBuffer(), out.getBufferLength());
buffer += out.getBufferLength();
switch (closeOut)
{
case BUF_OPEN:
*buffer++ = isc_info_truncated;
if (out.getBufferLength() <= bufferLength - 2)
*buffer++ = isc_info_end;
break;
case BUF_INTERNAL:
// overflow adding isc_info_end, but we actually have 1 reserved byte
*buffer++ = isc_info_end;
break;
case BUF_END:
// ignore isc_info_length
break;
}
return;
}
else
throw;
}
memcpy(buffer, out.getBuffer(), out.getBufferLength());
}

View File

@ -76,6 +76,8 @@ public:
Firebird::IMessageMetadata* getMetadata(thread_db* tdbb);
void cancel(thread_db* tdbb);
void setDefaultBpb(thread_db* tdbb, unsigned parLength, const unsigned char* par);
void info(thread_db* tdbb, unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer);
// Additional flags - start from the maximum one
static const UCHAR FLAG_DEFAULT_SEGMENTED = 31;
@ -121,6 +123,7 @@ private:
ULONG reget(ULONG size, UCHAR** buffer, ULONG alignment);
void remained(ULONG size, ULONG alignment = 0);
ULONG getSize() const;
ULONG getCapacity() const;
void clear();
private:

View File

@ -530,6 +530,11 @@ interface Batch : ReferenceCounted
const uint BLOB_SEGHDR_ALIGN = 2; // Alignment of segment header in the stream
const uchar INF_BUFFER_BYTES_SIZE = 10; // Maximum possible buffer size
const uchar INF_DATA_BYTES_SIZE = 11; // Already added messages size
const uchar INF_BLOBS_BYTES_SIZE = 12; // Already added blobs size
const uchar INF_BLOB_ALIGNMENT = 13; // Duplicate getBlobAlignment
void add(Status status, uint count, const void* inBuffer);
void addBlob(Status status, uint length, const void* inBuffer, ISC_QUAD* blobId, uint parLength, const uchar* par);
void appendBlobData(Status status, uint length, const void* inBuffer);
@ -545,6 +550,9 @@ interface Batch : ReferenceCounted
version: // 4.0.0 => 4.0.1
[notImplementedAction if ::FB_UsedInYValve then defaultAction else call deprecatedClose(status) endif]
void close(Status status);
void getInfo(Status status,
uint itemsLength, const uchar* items,
uint bufferLength, uchar* buffer);
}
interface BatchCompletionState : Disposable
@ -1203,6 +1211,8 @@ interface XpbBuilder : Disposable
const uint SPB_SEND = 7;
const uint SPB_RECEIVE = 8;
const uint SPB_RESPONSE = 9;
const uint INFO_SEND = 10;
const uint INFO_RESPONSE = 11;
// removing data
void clear(Status status);

View File

@ -1999,6 +1999,7 @@ namespace Firebird
void (CLOOP_CARG *setDefaultBpb)(IBatch* self, IStatus* status, unsigned parLength, const unsigned char* par) throw();
void (CLOOP_CARG *deprecatedClose)(IBatch* self, IStatus* status) throw();
void (CLOOP_CARG *close)(IBatch* self, IStatus* status) throw();
void (CLOOP_CARG *getInfo)(IBatch* self, IStatus* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) throw();
};
protected:
@ -2025,6 +2026,10 @@ namespace Firebird
static const unsigned char BLOB_ID_USER = 2;
static const unsigned char BLOB_STREAM = 3;
static const unsigned BLOB_SEGHDR_ALIGN = 2;
static const unsigned char INF_BUFFER_BYTES_SIZE = 10;
static const unsigned char INF_DATA_BYTES_SIZE = 11;
static const unsigned char INF_BLOBS_BYTES_SIZE = 12;
static const unsigned char INF_BLOB_ALIGNMENT = 13;
template <typename StatusType> void add(StatusType* status, unsigned count, const void* inBuffer)
{
@ -2123,6 +2128,19 @@ namespace Firebird
static_cast<VTable*>(this->cloopVTable)->close(this, status);
StatusType::checkException(status);
}
template <typename StatusType> void getInfo(StatusType* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer)
{
if (cloopVTable->version < 4)
{
StatusType::setVersionError(status, "IBatch", cloopVTable->version, 4);
StatusType::checkException(status);
return;
}
StatusType::clearException(status);
static_cast<VTable*>(this->cloopVTable)->getInfo(this, status, itemsLength, items, bufferLength, buffer);
StatusType::checkException(status);
}
};
class IBatchCompletionState : public IDisposable
@ -4755,6 +4773,8 @@ namespace Firebird
static const unsigned SPB_SEND = 7;
static const unsigned SPB_RECEIVE = 8;
static const unsigned SPB_RESPONSE = 9;
static const unsigned INFO_SEND = 10;
static const unsigned INFO_RESPONSE = 11;
template <typename StatusType> void clear(StatusType* status)
{
@ -10173,6 +10193,7 @@ namespace Firebird
this->setDefaultBpb = &Name::cloopsetDefaultBpbDispatcher;
this->deprecatedClose = &Name::cloopdeprecatedCloseDispatcher;
this->close = &Name::cloopcloseDispatcher;
this->getInfo = &Name::cloopgetInfoDispatcher;
}
} vTable;
@ -10350,6 +10371,20 @@ namespace Firebird
}
}
static void CLOOP_CARG cloopgetInfoDispatcher(IBatch* self, IStatus* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) throw()
{
StatusType status2(status);
try
{
static_cast<Name*>(self)->Name::getInfo(&status2, itemsLength, items, bufferLength, buffer);
}
catch (...)
{
StatusType::catchException(&status2);
}
}
static void CLOOP_CARG cloopaddRefDispatcher(IReferenceCounted* self) throw()
{
try
@ -10401,6 +10436,7 @@ namespace Firebird
virtual void setDefaultBpb(StatusType* status, unsigned parLength, const unsigned char* par) = 0;
virtual void deprecatedClose(StatusType* status) = 0;
virtual void close(StatusType* status) = 0;
virtual void getInfo(StatusType* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) = 0;
};
template <typename Name, typename StatusType, typename Base>

View File

@ -366,6 +366,7 @@ type
IBatch_setDefaultBpbPtr = procedure(this: IBatch; status: IStatus; parLength: Cardinal; par: BytePtr); cdecl;
IBatch_deprecatedClosePtr = procedure(this: IBatch; status: IStatus); cdecl;
IBatch_closePtr = procedure(this: IBatch; status: IStatus); cdecl;
IBatch_getInfoPtr = procedure(this: IBatch; status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr); cdecl;
IBatchCompletionState_getSizePtr = function(this: IBatchCompletionState; status: IStatus): Cardinal; cdecl;
IBatchCompletionState_getStatePtr = function(this: IBatchCompletionState; status: IStatus; pos: Cardinal): Integer; cdecl;
IBatchCompletionState_findErrorPtr = function(this: IBatchCompletionState; status: IStatus; pos: Cardinal): Cardinal; cdecl;
@ -1548,6 +1549,7 @@ type
setDefaultBpb: IBatch_setDefaultBpbPtr;
deprecatedClose: IBatch_deprecatedClosePtr;
close: IBatch_closePtr;
getInfo: IBatch_getInfoPtr;
end;
IBatch = class(IReferenceCounted)
@ -1563,6 +1565,10 @@ type
const BLOB_ID_USER = Byte(2);
const BLOB_STREAM = Byte(3);
const BLOB_SEGHDR_ALIGN = Cardinal(2);
const INF_BUFFER_BYTES_SIZE = Byte(10);
const INF_DATA_BYTES_SIZE = Byte(11);
const INF_BLOBS_BYTES_SIZE = Byte(12);
const INF_BLOB_ALIGNMENT = Byte(13);
procedure add(status: IStatus; count: Cardinal; inBuffer: Pointer);
procedure addBlob(status: IStatus; length: Cardinal; inBuffer: Pointer; blobId: ISC_QUADPtr; parLength: Cardinal; par: BytePtr);
@ -1576,6 +1582,7 @@ type
procedure setDefaultBpb(status: IStatus; parLength: Cardinal; par: BytePtr);
procedure deprecatedClose(status: IStatus);
procedure close(status: IStatus);
procedure getInfo(status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr);
end;
IBatchImpl = class(IBatch)
@ -1595,6 +1602,7 @@ type
procedure setDefaultBpb(status: IStatus; parLength: Cardinal; par: BytePtr); virtual; abstract;
procedure deprecatedClose(status: IStatus); virtual; abstract;
procedure close(status: IStatus); virtual; abstract;
procedure getInfo(status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr); virtual; abstract;
end;
BatchCompletionStateVTable = class(DisposableVTable)
@ -2798,6 +2806,8 @@ type
const SPB_SEND = Cardinal(7);
const SPB_RECEIVE = Cardinal(8);
const SPB_RESPONSE = Cardinal(9);
const INFO_SEND = Cardinal(10);
const INFO_RESPONSE = Cardinal(11);
procedure clear(status: IStatus);
procedure removeCurrent(status: IStatus);
@ -6685,6 +6695,17 @@ begin
FbException.checkException(status);
end;
procedure IBatch.getInfo(status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr);
begin
if (vTable.version < 4) then begin
FbException.setVersionError(status, 'IBatch', vTable.version, 4);
end
else begin
BatchVTable(vTable).getInfo(Self, status, itemsLength, items, bufferLength, buffer);
end;
FbException.checkException(status);
end;
function IBatchCompletionState.getSize(status: IStatus): Cardinal;
begin
Result := BatchCompletionStateVTable(vTable).getSize(Self, status);
@ -10704,6 +10725,15 @@ begin
end
end;
procedure IBatchImpl_getInfoDispatcher(this: IBatch; status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr); cdecl;
begin
try
IBatchImpl(this).getInfo(status, itemsLength, items, bufferLength, buffer);
except
on e: Exception do FbException.catchException(status, e);
end
end;
var
IBatchImpl_vTable: BatchVTable;
@ -15637,6 +15667,7 @@ initialization
IBatchImpl_vTable.setDefaultBpb := @IBatchImpl_setDefaultBpbDispatcher;
IBatchImpl_vTable.deprecatedClose := @IBatchImpl_deprecatedCloseDispatcher;
IBatchImpl_vTable.close := @IBatchImpl_closeDispatcher;
IBatchImpl_vTable.getInfo := @IBatchImpl_getInfoDispatcher;
IBatchCompletionStateImpl_vTable := BatchCompletionStateVTable.create;
IBatchCompletionStateImpl_vTable.version := 3;

View File

@ -882,7 +882,7 @@ Data source : @4"}, /* eds_statement */
{335545175, "Segment size (@1) should not exceed 65535 (64K - 1) when using segmented blob"}, /* big_segment */
{335545176, "Invalid blob policy in the batch for @1() call"}, /* batch_policy */
{335545177, "Can't change default BPB after adding any data to batch"}, /* batch_defbpb */
{335545178, "Unexpected info buffer structure querying for default blob alignment"}, /* batch_align */
{335545178, "Unexpected info buffer structure querying for server batch parameters"}, /* batch_align */
{335545179, "Duplicated segment @1 in multisegment connect block parameter"}, /* multi_segment_dup */
{335545180, "Plugin not supported by network protocol"}, /* non_plugin_protocol */
{335545181, "Error parsing message format"}, /* message_format */

View File

@ -209,6 +209,8 @@ public:
void setDefaultBpb(Firebird::CheckStatusWrapper* status, unsigned parLength, const unsigned char* par) override;
void close(Firebird::CheckStatusWrapper* status) override;
void deprecatedClose(Firebird::CheckStatusWrapper* status) override;
void getInfo(Firebird::CheckStatusWrapper* status, unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer) override;
public:
JBatch(DsqlBatch* handle, JStatement* aStatement, Firebird::IMessageMetadata* aMetadata);

View File

@ -6330,6 +6330,48 @@ void JBatch::cancel(CheckStatusWrapper* status)
}
void JBatch::getInfo(CheckStatusWrapper* user_status,
unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer)
{
/**************************************
*
* g d s _ $ b l o b _ i n f o
*
**************************************
*
* Functional description
* Provide information on blob object.
*
**************************************/
try
{
EngineContextHolder tdbb(user_status, this, FB_FUNCTION);
check_database(tdbb);
try
{
DsqlBatch* b = getHandle();
b->info(tdbb, itemsLength, items, bufferLength, buffer);
}
catch (const Exception& ex)
{
transliterateException(tdbb, ex, user_status, "JBatch::getInfo");
return;
}
}
catch (const Exception& ex)
{
ex.stuffException(user_status);
return;
}
successful_completion(user_status);
}
JReplicator::JReplicator(Applier* appl, StableAttachmentPart* sa)
: applier(appl), sAtt(sa)
{ }

View File

@ -965,7 +965,7 @@ Data source : @4', NULL, NULL)
('big_segment', NULL, NULL, NULL, 0, 855, NULL, 'Segment size (@1) should not exceed 65535 (64K - 1) when using segmented blob', NULL, NULL);
('batch_policy', NULL, NULL, NULL, 0, 856, NULL, 'Invalid blob policy in the batch for @1() call', NULL, NULL);
('batch_defbpb', NULL, NULL, NULL, 0, 857, NULL, 'Can''t change default BPB after adding any data to batch', NULL, NULL);
('batch_align', NULL, 'interface.cpp', NULL, 0, 858, NULL, 'Unexpected info buffer structure querying for default blob alignment', NULL, NULL);
('batch_align', NULL, 'interface.cpp', NULL, 0, 858, NULL, 'Unexpected info buffer structure querying for server batch parameters', NULL, NULL);
('multi_segment_dup', 'getMultiPartConnectParameter', 'server.cpp', NULL, 0, 859, NULL, 'Duplicated segment @1 in multisegment connect block parameter', NULL, NULL);
('non_plugin_protocol', NULL, 'server.cpp', NULL, 0, 860, NULL, 'Plugin not supported by network protocol', NULL, NULL);
('message_format', NULL, 'server.cpp', NULL, 0, 861, NULL, 'Error parsing message format', NULL, NULL);

View File

@ -349,11 +349,14 @@ public:
Firebird::IMessageMetadata* getMetadata(Firebird::CheckStatusWrapper* status) override;
void close(Firebird::CheckStatusWrapper* status) override;
void deprecatedClose(Firebird::CheckStatusWrapper* status) override;
void getInfo(CheckStatusWrapper* status,
unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer) override;
private:
void freeClientData(CheckStatusWrapper* status, bool force = false);
void releaseStatement();
void setBlobAlignment();
void setServerInfo();
void cleanup()
{
@ -411,7 +414,7 @@ private:
// working with blob stream buffer
void newBlob()
{
setBlobAlignment();
setServerInfo();
alignBlobBuffer(blobAlign);
fb_assert(blobStream - blobStreamBuffer <= blobBufferSize);
@ -513,7 +516,7 @@ private:
{
if (blobPolicy != BLOB_NONE)
{
setBlobAlignment();
setServerInfo();
alignBlobBuffer(blobAlign);
ULONG size = blobStream - blobStreamBuffer;
if (size)
@ -530,6 +533,7 @@ private:
}
batchActive = false;
blobCount = messageCount = 0;
}
void sendBlobPacket(unsigned size, const UCHAR* ptr, bool flash);
@ -549,6 +553,8 @@ private:
UCHAR blobPolicy;
bool segmented, defSegmented, batchActive;
ULONG messageCount, blobCount, serverSize;
public:
bool tmpStatement;
};
@ -2368,7 +2374,8 @@ Batch::Batch(Statement* s, IMessageMetadata* inFmt, unsigned parLength, const un
: messageStream(0), blobStream(nullptr), sizePointer(nullptr),
messageSize(0), alignedSize(0), blobBufferSize(0), messageBufferSize(0), flags(0),
stmt(s), format(inFmt), blobAlign(0), blobPolicy(BLOB_NONE),
segmented(false), defSegmented(false), batchActive(false), tmpStatement(false)
segmented(false), defSegmented(false), batchActive(false),
messageCount(0), blobCount(0), serverSize(0), tmpStatement(false)
{
LocalStatus ls;
CheckStatusWrapper st(&ls);
@ -2484,6 +2491,7 @@ void Batch::sendMessagePacket(unsigned count, const UCHAR* ptr, bool flash)
statement->rsr_batch_size = alignedSize;
sendDeferredPacket(port, packet, flash);
messageCount += count;
}
@ -2616,7 +2624,7 @@ void Batch::sendBlobPacket(unsigned size, const UCHAR* ptr, bool flash)
Rdb* rdb = statement->rsr_rdb;
rem_port* port = rdb->rdb_port;
setBlobAlignment();
setServerInfo();
fb_assert(!(size % blobAlign));
PACKET* packet = &rdb->rdb_packet;
@ -2627,6 +2635,8 @@ void Batch::sendBlobPacket(unsigned size, const UCHAR* ptr, bool flash)
batch->p_batch_blob_data.cstr_length = size;
sendDeferredPacket(port, packet, flash);
blobCount += size;
}
@ -2698,7 +2708,7 @@ unsigned Batch::getBlobAlignment(CheckStatusWrapper* status)
{
try
{
setBlobAlignment();
setServerInfo();
}
catch (const Exception& ex)
{
@ -2709,7 +2719,7 @@ unsigned Batch::getBlobAlignment(CheckStatusWrapper* status)
}
void Batch::setBlobAlignment()
void Batch::setServerInfo()
{
if (blobAlign)
return;
@ -2727,21 +2737,68 @@ void Batch::setBlobAlignment()
rem_port* port = rdb->rdb_port;
RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
// Perform info call to server
LocalStatus ls;
CheckStatusWrapper s(&ls);
UCHAR item = isc_info_sql_stmt_blob_align;
UCHAR buffer[16];
info(&s, rdb, op_info_sql, statement->rsr_id, 0,
1, &item, 0, 0, sizeof(buffer), buffer);
if (port->port_protocol < PROTOCOL_VERSION17)
{
UCHAR item = isc_info_sql_stmt_blob_align;
UCHAR buffer[16];
info(&s, rdb, op_info_sql, statement->rsr_id, 0,
1, &item, 0, 0, sizeof(buffer), buffer);
check(&s);
// Extract from buffer
if (buffer[0] != item)
Arg::Gds(isc_batch_align).raise();
int len = gds__vax_integer(&buffer[1], 2);
statement->rsr_batch_stream.alignment = blobAlign = gds__vax_integer(&buffer[3], len);
if (!blobAlign)
Arg::Gds(isc_batch_align).raise();
return;
}
// Perform info call to server
UCHAR items[] = {IBatch::INF_BLOB_ALIGNMENT, IBatch::INF_BUFFER_BYTES_SIZE};
UCHAR buffer[32];
info(&s, rdb, op_info_batch, statement->rsr_id, 0,
sizeof(items), items, 0, 0, sizeof(buffer), buffer);
check(&s);
// Extract from buffer
if (buffer[0] != item)
Arg::Gds(isc_batch_align).raise();
ClumpletReader out(ClumpletReader::InfoResponse, buffer, sizeof(buffer));
for (out.rewind(); !out.isEof(); out.moveNext())
{
UCHAR item = out.getClumpTag();
if (item == isc_info_end)
break;
int len = gds__vax_integer(&buffer[1], 2);
statement->rsr_batch_stream.alignment = blobAlign = gds__vax_integer(&buffer[3], len);
switch(item)
{
case IBatch::INF_BLOB_ALIGNMENT:
statement->rsr_batch_stream.alignment = blobAlign = out.getInt();
break;
case IBatch::INF_BUFFER_BYTES_SIZE:
serverSize = out.getInt();
break;
case isc_info_error:
(Arg::Gds(isc_batch_align) << Arg::Gds(out.getInt())).raise();
case isc_info_truncated:
(Arg::Gds(isc_batch_align) << Arg::Gds(isc_random) << "truncated").raise();
default:
{
string msg;
msg.printf("Wrong info item %u", item);
(Arg::Gds(isc_batch_align) << Arg::Gds(isc_random) << msg).raise();
}
}
}
if (! (blobAlign && serverSize))
Arg::Gds(isc_batch_align).raise();
}
@ -2960,6 +3017,73 @@ void Batch::close(CheckStatusWrapper* status)
}
void Batch::getInfo(CheckStatusWrapper* status, unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer)
{
try
{
ClumpletReader it(ClumpletReader::InfoItems, items, itemsLength);
ClumpletWriter out(ClumpletReader::InfoResponse, bufferLength - 1); // place for isc_info_end / isc_info_truncated
for (it.rewind(); !it.isEof(); it.moveNext())
{
UCHAR item = it.getClumpTag();
if (item == isc_info_end)
break;
try
{
switch(item)
{
case IBatch::INF_BUFFER_BYTES_SIZE:
setServerInfo();
if (serverSize)
out.insertInt(item, serverSize);
break;
case IBatch::INF_DATA_BYTES_SIZE:
out.insertInt(item, (messageCount + messageStream) * alignedSize);
break;
case IBatch::INF_BLOBS_BYTES_SIZE:
if (blobStream)
out.insertInt(item, blobCount + (blobStream - blobStreamBuffer));
break;
case IBatch::INF_BLOB_ALIGNMENT:
setServerInfo();
out.insertInt(item, blobAlign);
break;
default:
out.insertInt(isc_info_error, isc_infunk);
break;
}
}
catch(const fatal_exception&)
{
// here it's sooner of all caused by writer overflow but anyway check that
if (out.hasOverflow())
{
memcpy(buffer, out.getBuffer(), out.getBufferLength());
buffer += out.getBufferLength();
*buffer++ = isc_info_truncated;
if (out.getBufferLength() <= bufferLength - 2)
*buffer++ = isc_info_end;
return;
}
else
throw;
}
}
memcpy(buffer, out.getBuffer(), out.getBufferLength());
buffer += out.getBufferLength();
*buffer++ = isc_info_end;
}
catch (const Exception& ex)
{
ex.stuffException(status);
}
}
void Batch::releaseStatement()
{
if (tmpStatement)

View File

@ -504,6 +504,7 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p)
case op_info_transaction:
case op_service_info:
case op_info_sql:
case op_info_batch:
info = &p->p_info;
MAP(xdr_short, reinterpret_cast<SSHORT&>(info->p_info_object));
MAP(xdr_short, reinterpret_cast<SSHORT&>(info->p_info_incarnation));

View File

@ -94,7 +94,7 @@ const USHORT PROTOCOL_VERSION16 = (FB_PROTOCOL_FLAG | 16);
const USHORT PROTOCOL_STMT_TOUT = PROTOCOL_VERSION16;
// Protocol 17:
// - supports op_batch_sync
// - supports op_batch_sync, op_info_batch
const USHORT PROTOCOL_VERSION17 = (FB_PROTOCOL_FLAG | 17);
@ -295,6 +295,7 @@ enum P_OP
op_batch_cancel = 109,
op_batch_sync = 110,
op_info_batch = 111,
op_max
};

View File

@ -4431,6 +4431,15 @@ void rem_port::info(P_OP op, P_INFO* stuff, PACKET* sendL)
statement->rsr_iface->getInfo(&status_vector, info_len, info_buffer,
buffer_length, buffer);
break;
case op_info_batch:
getHandle(statement, stuff->p_info_object);
statement->checkIface();
statement->checkBatch();
statement->rsr_batch->getInfo(&status_vector, info_len, info_buffer,
buffer_length, buffer);
break;
}
// Send a response that includes the segment.
@ -4911,6 +4920,7 @@ static bool process_packet(rem_port* port, PACKET* sendL, PACKET* receive, rem_p
case op_info_transaction:
case op_service_info:
case op_info_sql:
case op_info_batch:
port->info(op, &receive->p_info, sendL);
break;

View File

@ -397,6 +397,8 @@ public:
void setDefaultBpb(Firebird::CheckStatusWrapper* status, unsigned parLength, const unsigned char* par);
void close(Firebird::CheckStatusWrapper* status);
void deprecatedClose(Firebird::CheckStatusWrapper* status);
void getInfo(Firebird::CheckStatusWrapper* status, unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer);
public:
AtomicAttPtr attachment;

View File

@ -916,8 +916,14 @@ public:
case SPB_RESPONSE:
k = ClumpletReader::SpbResponse;
break;
case INFO_SEND:
k = ClumpletReader::InfoItems;
break;
case INFO_RESPONSE:
k = ClumpletReader::InfoResponse;
break;
default:
fatal_exception::raiseFmt("Wrong parameters block kind %d, should be from %d to %d", kind, DPB, SPB_RESPONSE);
fatal_exception::raiseFmt("Wrong parameters block kind %d, should be from %d to %d", kind, DPB, INFO_RESPONSE);
break;
}

View File

@ -5084,6 +5084,22 @@ void YBatch::deprecatedClose(CheckStatusWrapper* status)
}
void YBatch::getInfo(CheckStatusWrapper* status, unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer)
{
try
{
YEntry<YBatch> entry(status, this);
entry.next()->getInfo(status, itemsLength, items, bufferLength, buffer);
}
catch (const Exception& e)
{
e.stuffException(status);
}
}
//-------------------------------------