8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 18:43:02 +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="generator" content="LibreOffice 6.4.7.2 (Linux)"/>
<meta name="author" content="alex "/> <meta name="author" content="alex "/>
<meta name="created" content="2013-05-31T00:00:00.010003100"/> <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"> <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 release() methods) are non standard and have features, missing in
other widely used types of interfaces. First of all Firebird other widely used types of interfaces. First of all Firebird
interfaces are </font><font size="4" style="font-size: 14pt"><b>language interfaces are </font><font size="4" style="font-size: 14pt"><b>language
independent</b></font> independent</b></font> <font size="4" style="font-size: 14pt">that
<font size="4" style="font-size: 14pt">that
means that to define/use them one need not use language specific 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> constructions like </font><font size="4" style="font-size: 14pt"><i>class</i></font>
<font size="4" style="font-size: 14pt">in <font size="4" style="font-size: 14pt">in C++, interface may be
C++, interface may be defined using any language able to call defined using any language able to call functions using C calling
functions using C calling conventions and having concepts of array conventions and having concepts of array and pointer to
and pointer to procedure/function. Next interfaces are </font><font size="4" style="font-size: 14pt"><b>versioned</b></font> 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. <font size="4" style="font-size: 14pt">i.e. we support different
we support different versions of same interface. Binary layout of versions of same interface. Binary layout of interfaces is designed
interfaces is designed to support that features very efficient (there to support that features very efficient (there is no need in
is no need in additional virtual calls like in OLE2/COM with it's additional virtual calls like in OLE2/COM with it's </font><strong><font size="4" style="font-size: 14pt">QueryInterface)</font></strong><strong>
</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
</strong><strong><font size="4" style="font-size: 14pt">but convenient for direct use from most languages. Therefore
it's not convenient for direct use from most languages. Therefore
language-specific wrappers should better be designed for different language-specific wrappers should better be designed for different
languages making use of API easier. Currently we have wrappers for 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++ 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 <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 installation package contains a number of live samples of use of OO
API they are in examples/interfaces (database access) and API they are in examples/interfaces (database access) and
examples/dbcrypt (plugin performing </font><font size="4" style="font-size: 14pt">fictitious examples/dbcrypt (plugin performing fictitious database encryption)
database encryption</font><font size="4" style="font-size: 14pt">)
directories. It's supposed that the reader is familiar with ISC API directories. It's supposed that the reader is familiar with ISC API
used in Firebird since interbase times.</font></p> used in Firebird since interbase times.</font></p>
<p style="margin-bottom: 0cm"><br/> <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"> <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 <font face="Albany, sans-serif"><font size="5" style="font-size: 18pt">Accessing
databases.</font></font></p> databases.</font></font></p>
<h1><font size="4" style="font-size: 14pt">Creating <h1><font size="4" style="font-size: 14pt">Creating database and
database and attaching to existing database.</font></h1> attaching to existing database.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">First <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 of all we need to get access to <b>IMaster</b> interface. IMaster is
primary Firebird interface, required to access all the rest of 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 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 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 special in-memory data structure, called </font><font size="4" style="font-size: 14pt"><i>database
parameters block</i></font> parameters block</i></font> <font size="4" style="font-size: 14pt">(DPB)
<font size="4" style="font-size: 14pt">(DPB)
is used. Format of it is well defined, and it's possible to build 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 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">, </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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Working <h1><font size="4" style="font-size: 14pt">Working with transactions.</font></h1>
with transactions.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Only <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Only
creating empty databases is definitely not enough to work with RDBMS. 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) 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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Executing <h1><font size="4" style="font-size: 14pt">Executing SQL operator
SQL operator without input parameters and returned rows.</font></h1> without input parameters and returned rows.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">With <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">With
started transaction we are ready to execute our first SQL operators. started transaction we are ready to execute our first SQL operators.
Used for it execute() method in <a href="#Attachment">IAttachment</a> 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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Executing <h1><font size="4" style="font-size: 14pt">Executing SQL operator
SQL operator with input parameters.</font></h1> with input parameters.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">There <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">There
are 2 ways to execute statement with input parameters. Choice of are 2 ways to execute statement with input parameters. Choice of
correct method depends upon do you need to execute it more than once 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, <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>builder-&gt;setLength(&amp;status,
1, 3);</i></font></p> 1, 3);</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">New <font size="4" style="font-size: 14pt">New API is using old constants
API is using old constants for SQL types, smallest bit as earlier for SQL types, smallest bit as earlier stands for nullability. In
stands for nullability. In some case it may also make sense to set some case it may also make sense to set sub-type (for blobs),
sub-type (for blobs), character set (for text fields) or scale (for character set (for text fields) or scale (for numeric fields). And
numeric fields). And finally it's time to get an instance of finally it's time to get an instance of IMessageMetadata:</font></p>
IMessageMetadata:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#MessageMetadata">IMessageMetadata</a>* <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> meta = builder-&gt;getMetadata(&amp;status);</i></font></p>
<p style="margin-bottom: 0cm"><br/> <p style="margin-bottom: 0cm"><br/>
</p> </p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Here <font size="4" style="font-size: 14pt">Here we do not discuss in
we do not discuss in details own implementation of IMessageMetadata. details own implementation of IMessageMetadata. If one cares there is
If one cares there is a sample 05.user_metadata.cpp.</font></p> a sample 05.user_metadata.cpp.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">So <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 finally we have obtained (one or another way) an instance of metadata
description of input parameters. But to work with a message we also 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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Opening <h1><font size="4" style="font-size: 14pt">Opening cursor and
cursor and fetching data from it.</font></h1> fetching data from it.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">The <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 only way to get rows of data, returned by SELECT operator, in OO API
is to use <a href="#ResultSet">IResultSet</a> interface. This 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 <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> 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"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">where <font size="4" style="font-size: 14pt">where n is the number of a
n is the number of a field in a message. That pointer should be field in a message. That pointer should be casted to appropriate
casted to appropriate type, depending upon field type. For example, type, depending upon field type. For example, for a VARCHAR field
for a VARCHAR field cast to struct vary should be used:</font></p> cast to struct vary should be used:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>vary* <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> 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 <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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Using <h1><font size="4" style="font-size: 14pt">Using FB_MESSAGE macro for
FB_MESSAGE macro for static messages.</font></h1> static messages.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Working <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 with data using offsets is rather efficient but requires a lot of
code to be written. In C++ this problem can be solved using 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> of appropriate class, char - with struct <a href="#FbChar">FbChar</a>
and varchar with struct <a href="#FbVarChar">FbVarChar</a>. For and varchar with struct <a href="#FbVarChar">FbVarChar</a>. For
each field preprocessor creates two data members in the message each field preprocessor creates two data members in the message
</font><font size="4" style="font-size: 14pt"><i>name</i></font> </font><font size="4" style="font-size: 14pt"><i>name</i></font> <font size="4" style="font-size: 14pt">for
<font size="4" style="font-size: 14pt">for
field/parameter value and </font><font size="4" style="font-size: 14pt"><i>nameNull</i></font> field/parameter value and </font><font size="4" style="font-size: 14pt"><i>nameNull</i></font>
<font size="4" style="font-size: 14pt">for <font size="4" style="font-size: 14pt">for NULL indicator. Message
NULL indicator. Message constructor has 2 parameters pointer to constructor has 2 parameters pointer to status wrapper and master
status wrapper and master interface:</font></p> interface:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>FB_MESSAGE(Output, <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>FB_MESSAGE(Output,
ThrowStatusWrapper,</i></font></p> ThrowStatusWrapper,</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>(FB_SMALLINT, <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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Working <h1><font size="4" style="font-size: 14pt">Working with blobs.</font></h1>
with blobs.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">For <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">For
blobs in message buffer firebird stores blob identifier an 8-byte blobs in message buffer firebird stores blob identifier an 8-byte
entity which should be aligned on 4-byte boundary. Identifier has 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 if you have passed your own format of messages to the batch you may
simply use it.</font></p> simply use it.</font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">In <font size="4" style="font-size: 14pt">In the former text I suppose
the former text I suppose that some function fillNextMessage(unsigned that some function fillNextMessage(unsigned char* data,
char* data, IMessageMetadata* metadata) is present and can fill IMessageMetadata* metadata) is present and can fill buffer data
buffer data according to passed format metadata. In order according to passed format metadata. In order to work with
to work with messages we need a buffer for a data:</font></p> messages we need a buffer for a data:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>unsigned <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> char* data = new unsigned char[meta-&gt;getMessageLength(&amp;status)];</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Now <font size="4" style="font-size: 14pt">Now we can add some messages
we can add some messages full of data to the batch:</font></p> full of data to the batch:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>fillNextMessage(data, <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>fillNextMessage(data,
meta);</i></font></p> meta);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>batch-&gt;add(&amp;status, <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, <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i>batch-&gt;add(&amp;status,
1, data);</i></font></p> 1, data);</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">An <font size="4" style="font-size: 14pt">An alternative way of working
alternative way of working with messages (using FB_MESSAGE macro) is with messages (using FB_MESSAGE macro) is present in the sample of
present in the sample of using batch interface 11.batch.cpp.</font></p> using batch interface 11.batch.cpp.</font></p>
<p style="margin-bottom: 0cm"><br/> <p style="margin-bottom: 0cm"><br/>
</p> </p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Finally <font size="4" style="font-size: 14pt">Finally batch should be
batch should be executed:</font></p> executed:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt"><i><a href="#BatchCompletionState">IBatchCompletionState</a>* <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> cs = batch-&gt;execute(&amp;status, tra);</i></font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">We <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 than the number of messages passed to the batch if error happened and
an option enabling multiple errors during batch processing was not an option enabling multiple errors during batch processing was not
turned on):</font></p> turned on):</font></p>
<p><font size="4" style="font-size: 14pt"><i>unsigned <p><font size="4" style="font-size: 14pt"><i>unsigned total =
total = cs-&gt;getSize(&amp;status);</i></font></p> cs-&gt;getSize(&amp;status);</i></font></p>
<p><font size="4" style="font-size: 14pt">Now <p><font size="4" style="font-size: 14pt">Now print the state of each
print the state of each message:</font></p> message:</font></p>
<p><font size="4" style="font-size: 14pt"><i>for <p><font size="4" style="font-size: 14pt"><i>for (unsigned p = 0; p &lt;
(unsigned p = 0; p &lt; total; ++p) printf(“Msg %u state %d\n”, total; ++p) printf(“Msg %u state %d\n”, p, cs-&gt;getState(&amp;status,
p, cs-&gt;getState(&amp;status, p));</i></font></p> p));</i></font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">When <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> 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 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 <p><font color="#000000"><font size="4" style="font-size: 14pt">Full
sample of printing contents</font></font><font color="#000000"> sample of printing contents</font></font><font color="#000000"> of
of </font><font color="#000000"><font size="4" style="font-size: 14pt"><a href="#BatchCompletionState">BatchCompletionState</a> </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> 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 <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 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> 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 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"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Like <font size="4" style="font-size: 14pt">Like the rest of our data
the rest of our data access interfaces Batch has special method to access interfaces Batch has special method to close it:</font></p>
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 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"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Standard <font size="4" style="font-size: 14pt">Standard release() call may be
release() call may be used instead if one does not care about errors:</font></p> 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 size="4" style="font-size: 14pt"><i>batch-&gt;release();</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Described <font size="4" style="font-size: 14pt">Described methods help to
methods help to implement all what one needs for JDBC-style prepared implement all what one needs for JDBC-style prepared statement batch
statement batch operations. </font> operations. </font>
</p> </p>
<p style="margin-bottom: 0cm"><br/> <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 An optimal batch size should be found for each particular case but
sooner of all having it &gt;1000 hardly gives you serious performance sooner of all having it &gt;1000 hardly gives you serious performance
increase.</font></font></p> increase.</font></font></p>
<p><br/> <p style="margin-bottom: 0cm"><br/>
<br/>
</p> </p>
<p><font size="4" style="font-size: 14pt">One <p><font size="4" style="font-size: 14pt">One can add more than
can add more than single message in one call to the batch. When doing single message in one call to the batch. When doing it please
it please remember messages should be appropriately aligned for remember messages should be appropriately aligned for this
this feature to work correctly. Required alignment and aligned size feature to work correctly. Required alignment and aligned size of the
of the message should be obtained from <a href="#MessageMetadata">MessageMetadata</a> message should be obtained from <a href="#MessageMetadata">MessageMetadata</a>
interface, for example:</font></p> interface, for example:</font></p>
<p><font size="4" style="font-size: 14pt"><i>unsigned <p><font size="4" style="font-size: 14pt"><i>unsigned aligned =
aligned = meta-&gt;getAlignedLength(&amp;status);</i></font></p> meta-&gt;getAlignedLength(&amp;status);</i></font></p>
<p><font size="4" style="font-size: 14pt">Later <p><font size="4" style="font-size: 14pt">Later that size will be
that size will be useful when allocating an array of messages and useful when allocating an array of messages and working with it:</font></p>
working with it:</font></p> <p><font size="4" style="font-size: 14pt"><i>unsigned char* data =
<p><font size="4" style="font-size: 14pt"><i>unsigned new unsigned char[aligned * N]; // N is desired number of messages</i></font></p>
char* data = new unsigned char[aligned * N]; // N is desired number <p><font size="4" style="font-size: 14pt"><i>for (int n = 0; n &lt;
of messages</i></font></p> N; ++n) fillNextMessage(&amp;data[aligned * n], meta);</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, <p><font size="4" style="font-size: 14pt"><i>batch-&gt;add(&amp;status,
N, data);</i></font></p> N, data);</i></font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">After <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/> <br/>
</p> </p>
<p><font size="4" style="font-size: 14pt">Blobs <p><font size="4" style="font-size: 14pt">Blobs in general are not
in general are not compatible with batches batch is efficient compatible with batches batch is efficient when one needs to pass
when one needs to pass a lot of small data to the server in single a lot of small data to the server in single step, blobs are treated
step, blobs are treated as large objects and therefore in general it as large objects and therefore in general it makes no sense to use
makes no sense to use them in batches. But on practice it often them in batches. But on practice it often happens that blobs are not
happens that blobs are not too big and in this case use of too big and in this case use of traditional blob API (create
traditional blob API (create blob, pass segments to the server, close blob, pass segments to the server, close blob, pass blobs ID in the
blob, pass blobs ID in the message) kills performance, specially when message) kills performance, specially when used over WAN. Therefore
used over WAN. Therefore in firebird batch supports passing blobs to in firebird batch supports passing blobs to server inline, together
server inline, together with other messages. To use that feature with other messages. To use that feature first of all <a href="#Batch_Blob_Policy">blob
first of all <a href="#Batch_Blob_Policy">blob usage policy</a> for a usage policy</a> for a batch to be created should be set (as an
batch to be created should be set (as an option in <a href="#Batch_PB">parameters option in <a href="#Batch_PB">parameters block</a>):</font></p>
block</a>):</font></p>
<p><font size="4" style="font-size: 14pt"><i>pb-&gt;insertInt(&amp;status, <p><font size="4" style="font-size: 14pt"><i>pb-&gt;insertInt(&amp;status,
IBatch::BLOB_IDS, IBatch::BLOB_IDS_ENGINE);</i></font></p> 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 <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> message is described as follows:</font></p>
<p style="font-variant: normal"><font size="4" style="font-size: 14pt"><i>FB_MESSAGE(Msg, <p style="font-variant: normal"><font size="4" style="font-size: 14pt"><i>FB_MESSAGE(Msg,
ThrowStatusWrapper,</i></font></p> ThrowStatusWrapper,</i></font></p>
<p><font size="4" style="font-size: 14pt"><i>(FB_VARCHAR(5), <p><font size="4" style="font-size: 14pt"><i>(FB_VARCHAR(5), id)</i></font></p>
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_VARCHAR(10), <p><font size="4" style="font-size: 14pt"><i>(FB_BLOB, desc)</i></font></p>
name)</i></font></p> <p><font size="4" style="font-size: 14pt"><i>) project(&amp;status,
<p><font size="4" style="font-size: 14pt"><i>(FB_BLOB, master);</i></font></p>
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 <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 that case to send a message containing blob to the server one can do
something like this:</font></p> something like this:</font></p>
<p><font size="4" style="font-size: 14pt"><i>project-&gt;id <p><font size="4" style="font-size: 14pt"><i>project-&gt;id =
= ++idCounter;</i></font></p> ++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>project-&gt;name.set(currentName);</i></font></p>
<p><font size="4" style="font-size: 14pt"><i>batch-&gt;addBlob(&amp;status, <p><font size="4" style="font-size: 14pt"><i>batch-&gt;addBlob(&amp;status,
descriptionSize, descriptionText, &amp;project-&gt;desc);</i></font></p> descriptionSize, descriptionText, &amp;project-&gt;desc);</i></font></p>
<p><font size="4" style="font-size: 14pt"><i>batch-&gt;add(&amp;status, <p><font size="4" style="font-size: 14pt"><i>batch-&gt;add(&amp;status,
1, project.getData());</i></font></p> 1, project.getData());</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">If <font size="4" style="font-size: 14pt">If some blob happened to be
some blob happened to be big enough not to fit into your existing big enough not to fit into your existing buffer you may instead
buffer you may instead reallocating buffer use appendBlobData() reallocating buffer use appendBlobData() method. It appends more data
method. It appends more data to last added blob. </font> to last added blob. </font>
</p> </p>
<p style="font-variant: normal"><font size="4" style="font-size: 14pt"><i>batch-&gt;addBlob(&amp;status, <p style="font-variant: normal"><font size="4" style="font-size: 14pt"><i>batch-&gt;addBlob(&amp;status,
descriptionSize, descriptionText, &amp;project→desc, bpbLength, 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> descriptionText, update descriptionSize and:</font></p>
<p style="font-variant: normal"><font size="4" style="font-size: 14pt"><i>batch-&gt;appendBlobData(&amp;status, <p style="font-variant: normal"><font size="4" style="font-size: 14pt"><i>batch-&gt;appendBlobData(&amp;status,
descriptionSize, descriptionText);</i></font></p> descriptionSize, descriptionText);</i></font></p>
<p><font size="4" style="font-size: 14pt">This <p><font size="4" style="font-size: 14pt">This may be done in a loop
may be done in a loop but take care not to overflow internal batch but take care not to overflow internal batch buffers its size
buffers its size is controlled by <a href="#Batch_PB">BUFFER_BYTES_SIZE</a> is controlled by <a href="#Batch_PB">BUFFER_BYTES_SIZE</a> option
option when creating batch interface but cant exceed 256Mb when creating batch interface but cant exceed 256Mb (default is
(default is 16Mb). If you need to process such big blob (for example 16Mb). If you need to process such big blob (for example on the
on the background of a lot of small one this can explain use of background of a lot of small one this can explain use of batch)
batch) just use standard blob API and <a href="#Batch::registerBlob">registerBlob</a> just use standard blob API and <a href="#Batch::registerBlob">registerBlob</a>
method of <a href="#Batch">Batch</a> interface.</font></p> 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 <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 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 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 are already present in them. In such case use of user-supplied blob
IDs can greatly simplify your code.</font></p> IDs can greatly simplify your code.</font></p>
<p><font size="4" style="font-size: 14pt">Please <p><font size="4" style="font-size: 14pt">Please take into an account
take into an account unlike blobs created using regular unlike blobs created using regular <a href="#Attachment">createBlob</a>()
<a href="#Attachment">createBlob</a>() blobs created by <a href="#Batch">Batch</a> blobs created by <a href="#Batch">Batch</a> interface are by default
interface are by default stream, not segmented. Segmented blobs stream, not segmented. Segmented blobs provide nothing interesting
provide nothing interesting compared with stream one and therefore compared with stream one and therefore not recommended to be used in
not recommended to be used in new development, we support that format new development, we support that format only for backward
only for backward compatibility reasons. If you really need segmented compatibility reasons. If you really need segmented blobs this
blobs this default may be overridden by calling:</font></p> default may be overridden by calling:</font></p>
<p><font size="4" style="font-size: 14pt"><i>batch-&gt;setDefaultBpb(&amp;status,</i></font> <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, <font size="4" style="font-size: 14pt"><i>bpbLength, bpb);</i></font></p>
bpb);</i></font></p>
<p style="font-variant: normal; font-style: normal"><font size="4" style="font-size: 14pt">Certainly <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 passed BPB may contain any other blob creation parameters too. As you
may have already noticed you may also pass BPB directly to addBlob() 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. the blob, following calls to appendBlobData() will add more segments.
Do not forget that segment size is limited to 64Kb 1, an attempt 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> to pass more data in a single call with cause an error.</font></p>
<p><font size="4" style="font-size: 14pt">Next <p><font size="4" style="font-size: 14pt">Next step when working with
step when working with existing blob streams is use of existing blob streams is use of addBlobStream() method. Using it one
addBlobStream() method. Using it one can add more than one blob to can add more than one blob to the batch per single call. Blob stream
the batch per single call. Blob stream is a sequence of blobs, each is a sequence of blobs, each starts with blob header. Header should
starts with blob header. Header should be appropriately aligned - be appropriately aligned - <a href="#Batch">Batch</a> interface
<a href="#Batch">Batch</a> interface provides special call for this provides special call for this purpose:</font></p>
purpose:</font></p>
<p style="font-variant: normal"><font size="4" style="font-size: 14pt"><i>unsigned <p style="font-variant: normal"><font size="4" style="font-size: 14pt"><i>unsigned
alignment = batch-&gt;getBlobAlignment(&amp;status);</i></font></p> 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 <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> 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 <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 method used to work with blobs stands alone from the first three that
pass blob data inline with the rest of batch data </font> pass blob data inline with the rest of batch data </font> <font size="4" style="font-size: 14pt">its
<font size="4" style="font-size: 14pt">its
needed to register in a batch ID of a blob created using standard 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 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 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, <p><font size="4" style="font-size: 14pt"><i>batch-&gt;registerBlob(&amp;status,
&amp;realId, &amp;msg-&gt;desc);</i></font></p> &amp;realId, &amp;msg-&gt;desc);</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">If <font size="4" style="font-size: 14pt">If blob policy makes firebird
blob policy makes firebird engine generate blob IDs this code is engine generate blob IDs this code is enough to correctly register
enough to correctly register existing blob in a batch. In other cases existing blob in a batch. In other cases you will have to assign
you will have to assign correct (from batch POV) ID to msg-&gt;desc.</font></p> correct (from batch POV) ID to msg-&gt;desc.</font></p>
<p style="margin-bottom: 0cm"><br/> <p style="margin-bottom: 0cm"><br/>
</p> </p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Almost <font size="4" style="font-size: 14pt">Almost all mentioned methods
all mentioned methods are used in 11.batch.cpp please use it to are used in 11.batch.cpp please use it to see an alive sample of
see an alive sample of batching in firebird.</font></p> batching in firebird.</font></p>
<p style="margin-bottom: 0cm"><br/> <p style="margin-bottom: 0cm"><br/>
</p> </p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Two <font size="4" style="font-size: 14pt">Two words about access to
words about access to batches from ISC API - one can execute prepared batches from ISC API - one can execute prepared ISC statement in
ISC statement in batch mode. Main support for it is presence of two batch mode. Main support for it is presence of two new API functions,
new API functions, namely fb_get_transaction_interface &amp; namely fb_get_transaction_interface &amp; fb_get_statement_interface,
fb_get_statement_interface, which make it possible to access which make it possible to access appropriate interfaces identical to
appropriate interfaces identical to existing ISC handles. An example existing ISC handles. An example of it is present in
of it is present in 12.batch_isc.cpp.</font></p> 12.batch_isc.cpp.</font></p>
<p style="margin-bottom: 0cm"><br/> <p style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Working <h1><font size="4" style="font-size: 14pt">Working with events.</font></h1>
with events.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Events <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 interface was not completed in FB3, we expect to have something more
interesting in next version. The minimum existing support is as interesting in next version. The minimum existing support is as
follows: <a href="#Attachment">IAttachment</a> contains call follows: <a href="#Attachment">IAttachment</a> contains call
queEvents() which performs almost same functions as isc_que_events() 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 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 ast</i></font> <font size="4" style="font-size: 14pt">and </font><font size="4" style="font-size: 14pt"><i>void*
</font><font size="4" style="font-size: 14pt"><i>void* arg</i></font><font size="4" style="font-size: 14pt">, required to
arg</i></font><font size="4" style="font-size: 14pt">, invoke user code when event happens in firebird engine, callback
required to invoke user code when event happens in firebird engine, interface IEventCallback is used. This is traditional approach which
callback interface IEventCallback is used. This is traditional helps to avoid non-safe casts from void* in user function. Another
approach which helps to avoid non-safe casts from void* in user important difference is that instead event identifier (a kind of
function. Another important difference is that instead event handler) this function returns reference counted interface <a href="#Events">IEvents</a>
identifier (a kind of handler) this function returns reference having method cancel() used when waiting for event should be stopped.
counted interface <a href="#Events">IEvents</a> having method Unlike identifier which is automatically destroyed when event arrives
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 interface can not be automatically destroyed in case when event
is received right before canceling interface call to cancel() would is received right before canceling interface call to cancel() would
cause segfault when interface is already destroyed. Therefore 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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Using <h1><font size="4" style="font-size: 14pt">Using services.</font></h1>
services.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">To <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 begin to use services one should first of all connect to service
manager. This is done using attachServiceManager() method of 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 write a plugin means to implement some interfaces and place your
implementation into dynamic library (.dll in windows or .so in linux) implementation into dynamic library (.dll in windows or .so in linux)
later referenced as </font><font size="4" style="font-size: 14pt"><i>plugin later referenced as </font><font size="4" style="font-size: 14pt"><i>plugin
module</i></font> <font size="4" style="font-size: 14pt">or module</i></font> <font size="4" style="font-size: 14pt">or just
just </font><font size="4" style="font-size: 14pt"><i>module</i></font><font size="4" style="font-size: 14pt">. </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> In most cases single plugin is placed</font> <font size="4" style="font-size: 14pt">into</font>
<font size="4" style="font-size: 14pt">into</font> <font size="4" style="font-size: 14pt">dynamic library but in common
<font size="4" style="font-size: 14pt">dynamic case multiple plugins may coexist in single dynamic library. One of
library but in common case multiple plugins may coexist in single that interfaces <a href="#PluginModule">IPluginModule</a> is
dynamic library. One of that interfaces <a href="#PluginModule">IPluginModule</a> module-wide (as more or less clear from it's name), others are per
is module-wide (as more or less clear from it's name), others are plugin. Also each plugin module should contain special exported
per plugin. Also each plugin module should contain special exported
entrypoint firebird_plugin() which name is defined in include file entrypoint firebird_plugin() which name is defined in include file
firebird/Interfaces.h as FB_PLUGIN_ENTRY_POINT.</font></p> firebird/Interfaces.h as FB_PLUGIN_ENTRY_POINT.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">In <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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Implementation <h1><font size="4" style="font-size: 14pt">Implementation of plugin
of plugin module.</font></h1> module.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Plugins <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 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">. manager</i></font><font size="4" style="font-size: 14pt">. In
In particular plugin manager should be aware what plugin modules were particular plugin manager should be aware what plugin modules were
loaded and must be notified if operating system tries to unload one loaded and must be notified if operating system tries to unload one
of that modules without explicit plugin manager command (this may of that modules without explicit plugin manager command (this may
happen first of all when using embedded access when exit() is 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> 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 <font size="4" style="font-size: 14pt">is unloaded). Primary task of
unloaded). Primary task of IPluginModule interface is that IPluginModule interface is that notification. First of all one must
notification. First of all one must decide - how to detect that decide - how to detect that module is going to be unloaded? When
module is going to be unloaded? When dynamic library is unloaded for dynamic library is unloaded for some reason a lot of OS-dependent
some reason a lot of OS-dependent actions is performed and some of actions is performed and some of that actions may be used to detect
that actions may be used to detect this fact in the program. When this fact in the program. When writing plugins distributed with
writing plugins distributed with firebird we always use invocation of firebird we always use invocation of destructor of global variable.
destructor of global variable. The big “plus” for this method is The big “plus” for this method is that it is OS independent
that it is OS independent (though something like atexit() function (though something like atexit() function maybe also used
maybe also used successfully). But use of destructor makes it successfully). But use of destructor makes it possible to easily
possible to easily concentrate almost everything related with unload concentrate almost everything related with unload detection in single
detection in single class implementing at the same time <a href="#PluginModule">IPluginModule</a> class implementing at the same time <a href="#PluginModule">IPluginModule</a>
interface.</font></p> interface.</font></p>
<p style="margin-bottom: 0cm"><br/> <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 variable, at the same time module is registered in plugin manager by
the call to registerModule() method with own address as a single the call to registerModule() method with own address as a single
parameter. Variable </font><font size="4" style="font-size: 14pt"><i>pluginManager parameter. Variable </font><font size="4" style="font-size: 14pt"><i>pluginManager
</i></font><font size="4" style="font-size: 14pt">not </i></font><font size="4" style="font-size: 14pt">not only stores
only stores pointer to interface, at the same time it serves as a pointer to interface, at the same time it serves as a flag that
flag that module is registered. When destructor of registered module module is registered. When destructor of registered module is invoked
is invoked it notifies plugin manager (yes, this is what for this it notifies plugin manager (yes, this is what for this class exists!)
class exists!) about unexpected unload by the call to about unexpected unload by the call to unregisterModule() passing
unregisterModule() passing pointer to itself. When plugin manager is pointer to itself. When plugin manager is going to unload module in
going to unload module in regular way in first of all calls doClean() regular way in first of all calls doClean() method changing module
method changing module state to unregistered and this avoiding call state to unregistered and this avoiding call to unregisterModule()
to unregisterModule() when OS performs actual unload.</font></p> when OS performs actual unload.</font></p>
<p style="margin-bottom: 0cm"><br/> <p style="margin-bottom: 0cm"><br/>
</p> </p>
@ -1502,8 +1477,8 @@ registerMe() function from FB_PLUGIN_ENTRY_POINT.</font></p>
<p style="margin-bottom: 0cm"><br/> <p style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Core <h1><font size="4" style="font-size: 14pt">Core interface of any
interface of any plugin.</font></h1> plugin.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Let's <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Let's
start implementing plugin itself. The type of main interface depends start implementing plugin itself. The type of main interface depends
upon plugin type (which is obvious), but all of them are based on 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>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 size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Constructor <font size="4" style="font-size: 14pt">Constructor gets as parameter
gets as parameter plugin configuration interface. If you are going to plugin configuration interface. If you are going to have you plugin
have you plugin configured in some way it's good idea to save this configured in some way it's good idea to save this interface in your
interface in your plugin and use it later. This will let you use plugin and use it later. This will let you use common for all
common for all firebird configuration style letting users have firebird configuration style letting users have familiar
familiar configuration and minimize code written. Certainly when configuration and minimize code written. Certainly when saving any
saving any reference counted interface it's better not forget to add reference counted interface it's better not forget to add reference
reference to it. Also set reference counter to 0 and plugin owner to to it. Also set reference counter to 0 and plugin owner to NULL.</font></p>
NULL.</font></p>
<p style="margin-bottom: 0cm"><br/> <p style="margin-bottom: 0cm"><br/>
</p> </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>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 size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Destructor <font size="4" style="font-size: 14pt">Destructor releases config
releases config interface. Pay attention we do not change interface. Pay attention we do not change reference counter of
reference counter of our owner cause it owns us, not we own it.</font></p> our owner cause it owns us, not we own it.</font></p>
<p style="margin-bottom: 0cm"><br/> <p style="margin-bottom: 0cm"><br/>
</p> </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>++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 size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Absolutely <font size="4" style="font-size: 14pt">Absolutely typical
typical implementation of reference counted object.</font></p> implementation of reference counted object.</font></p>
<p style="margin-bottom: 0cm"><br/> <p style="margin-bottom: 0cm"><br/>
</p> </p>
@ -1605,8 +1579,8 @@ getOwner()</i></font></p>
owner;</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 size="4" style="font-size: 14pt"><i>}</i></font></p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">As <font size="4" style="font-size: 14pt">As it was promised
it was promised implementation of IPluginBase is trivial.</font></p> implementation of IPluginBase is trivial.</font></p>
<p style="margin-bottom: 0cm"><br/> <p style="margin-bottom: 0cm"><br/>
</p> </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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Plugin's <h1><font size="4" style="font-size: 14pt">Plugin's factory.</font></h1>
factory.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">One <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>. more interface required for plugin to work is <a href="#PluginFactory">IPluginFactory</a>.
Factory creates instances of plugin and returns them to plugin Factory creates instances of plugin and returns them to plugin
@ -1660,12 +1633,12 @@ p;</i></font></p>
</p> </p>
<p style="margin-bottom: 0cm; font-variant: normal; font-style: normal"> <p style="margin-bottom: 0cm; font-variant: normal; font-style: normal">
<font size="4" style="font-size: 14pt">Here <font size="4" style="font-size: 14pt">Here attention should be payed
attention should be payed to the fact that even in a case when code to the fact that even in a case when code in a function may throw
in a function may throw exceptions (operator new may throw in a case exceptions (operator new may throw in a case when memory exhausted)
when memory exhausted) one need not always manually define try/catch one need not always manually define try/catch block
block implementation of firebird interfaces does this job for implementation of firebird interfaces does this job for you, in
you, in implementation of IPluginFactory it's placed into template implementation of IPluginFactory it's placed into template
IPluginFactoryImpl. Take into an account that default status wrappers IPluginFactoryImpl. Take into an account that default status wrappers
perform meaning-full processing only for FbException. But if you perform meaning-full processing only for FbException. But if you
(that definitely makes sense if you work on some big project) define (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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Plugin <h1><font size="4" style="font-size: 14pt">Plugin module
module initialization entrypoint.</font></h1> initialization entrypoint.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">When <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">When
plugin manager loads plugin module it invokes module initializing plugin manager loads plugin module it invokes module initializing
routine the only exported from plugin function 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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Generic <h1><font size="4" style="font-size: 14pt">Generic interfaces.</font></h1>
interfaces.</font></h1>
<p style="margin-bottom: 0cm"><a name="Attachment"></a><font size="4" style="font-size: 14pt">Attachment <p style="margin-bottom: 0cm"><a name="Attachment"></a><font size="4" style="font-size: 14pt">Attachment
interface replaces isc_db_handle:</font></p> interface replaces isc_db_handle:</font></p>
<ol> <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* <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 createBatch(StatusType* status, ITransaction* transaction, unsigned
stmtLength, const char* sqlStmt, unsigned dialect, IMessageMetadata* stmtLength, const char* sqlStmt, unsigned dialect, IMessageMetadata*
inMetadata, unsigned</font></font> inMetadata, unsigned</font></font> <font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">parLength,
<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> const unsigned char* par) prepares sqlStmt and creates <a href="#Batch">Batch</a>
interface ready to accept multiple sets of input parameters in interface ready to accept multiple sets of input parameters in
inMetadata format. Leaving inMetadata</font></font> inMetadata format. Leaving inMetadata</font></font> <font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">NULL
<font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">NULL
makes batch use default format for sqlStmt. Parameters block may be makes batch use default format for sqlStmt. Parameters block may be
passed to createBatch() making it possible to adjust batch behavior.</font></font></p> 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* <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> parameters in single statement execution.</font></p>
<ol> <ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void <li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
add(StatusType* status, unsigned</font> add(StatusType* status, unsigned</font> <font size="4" style="font-size: 14pt">count,
<font size="4" style="font-size: 14pt">count,
const void* inBuffer) adds count messages from inBuffer to the const void* inBuffer) adds count messages from inBuffer to the
batch. Total size of messages that can be added to the batch is 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> limited by TAG_BUFFER_BYTES_SIZE <a href="#Batch_PB">parameter</a>
of batch creation.</font></p> of batch creation.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void <li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
addBlob(StatusType* status, unsigned</font> addBlob(StatusType* status, unsigned</font> <font size="4" style="font-size: 14pt">length,
<font size="4" style="font-size: 14pt">length,
const void* inBuffer, ISC_QUAD* blobId, unsigned bpbLength, const const void* inBuffer, ISC_QUAD* blobId, unsigned bpbLength, const
unsigned char* bpb) adds single blob having length bytes from unsigned char* bpb) adds single blob having length bytes from
inBuffer to the batch, blob identifier is located at blobId address. 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 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 non-default BPB. Must be called before adding any message or blob to
batch.</font></p> 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> </ol>
<p style="margin-bottom: 0cm"><a name="Batch_PB"></a><font size="4" style="font-size: 14pt">Tag <p style="margin-bottom: 0cm"><a name="Batch_PB"></a><font size="4" style="font-size: 14pt">Tag
for parameters block:</font></p> 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> - 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 <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">BLOB_STREAM
- blobs are added in a stream</font></p> - 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 style="margin-bottom: 0cm"><br/>
</p> </p>
@ -2156,10 +2139,10 @@ sub-entries) in firebird configuration file:</font></p>
</p> </p>
<p style="margin-bottom: 0cm"><a name="DecFloat16"></a><a name="DecFloat34"></a> <p style="margin-bottom: 0cm"><a name="DecFloat16"></a><a name="DecFloat34"></a>
<font size="4" style="font-size: 14pt">DecFloat16 <font size="4" style="font-size: 14pt">DecFloat16 / DecFloat34
/ DecFloat34 interfaces that help to work with DECFLOAT (16 &amp; interfaces that help to work with DECFLOAT (16 &amp; 34 respectively)
34 respectively) datatypes. They have almost same set of methods with datatypes. They have almost same set of methods with FB_DEC16
FB_DEC16 parameter replaced by FB_DEC34 for DecFloat34:</font></p> parameter replaced by FB_DEC34 for DecFloat34:</font></p>
<ol> <ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void <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) 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() <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IAttachment::prepare()
flags:</font></p> flags:</font></p>
<p style="margin-left: 0.96cm; text-indent: -0.02cm; margin-bottom: 0cm; page-break-before: auto; page-break-after: auto"> <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 <font size="4" style="font-size: 14pt">PREPARE_PREFETCH_NONE
constant to pass no flags, 0 value.</font></p> constant to pass no flags, 0 value.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">The <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> following flags may be OR-ed to get desired set of flags:</font></p>
<ol> <ol>
@ -2820,8 +2803,8 @@ used combinations of flags:</font></p>
</p> </p>
<p style="margin-bottom: 0cm"><a name="Values returned by getFlags"></a> <p style="margin-bottom: 0cm"><a name="Values returned by getFlags"></a>
<font size="4" style="font-size: 14pt">Values <font size="4" style="font-size: 14pt">Values returned by getFlags()
returned by getFlags() method: </font> method: </font>
</p> </p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">FLAG_HAS_CURSOR <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> use openCursor() to execute this statement, not execute()</font></p>
@ -2832,8 +2815,7 @@ parameters</font></p>
</p> </p>
<p style="margin-bottom: 0cm"><a name="Flags passed to openCursor"></a> <p style="margin-bottom: 0cm"><a name="Flags passed to openCursor"></a>
<font size="4" style="font-size: 14pt">Flags <font size="4" style="font-size: 14pt">Flags passed to openCursor():</font></p>
passed to openCursor():</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">CURSOR_TYPE_SCROLLABLE <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">CURSOR_TYPE_SCROLLABLE
open bidirectional cursor.</font></p> open bidirectional cursor.</font></p>
<p style="margin-bottom: 0cm"><br/> <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(StatusType* status, ITimer* timer, ISC_UINT64 microSeconds)
start <a href="#Timer">ITimer</a> to alarm after given delay (in 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> microseconds, 10</font><sup><font size="4" style="font-size: 14pt">-6</font></sup>
<font size="4" style="font-size: 14pt">seconds). <font size="4" style="font-size: 14pt">seconds). Timer will be waked
Timer will be waked up only once after this call.</font></p> up only once after this call.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void <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>. stop(StatusType* status, ITimer* timer) stop <a href="#Timer">ITimer</a>.
It's not an error to stop not started timer thus avoiding problems 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, decodeTimeTz(StatusType* status, const ISC_TIME_TZ* timeTz,
unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned*</font></p> unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned*</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">fractions, <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">fractions,
unsigned timeZoneBufferLength, char* timeZoneBuffer) unsigned timeZoneBufferLength, char* timeZoneBuffer) decode time
decode time taking time zone into an account.</font></p> taking time zone into an account.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void <li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
decodeTimeStampTz(StatusType* status, const ISC_TIMESTAMP_TZ* decodeTimeStampTz(StatusType* status, const ISC_TIMESTAMP_TZ*
timeStampTz, unsigned* year, unsigned* month, unsigned* day, timeStampTz, unsigned* year, unsigned* month, unsigned* day,
@ -3195,8 +3177,8 @@ builder types:</font></p>
<p style="margin-bottom: 0cm"><br/> <p style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Plugin, <h1><font size="4" style="font-size: 14pt">Plugin, encrypting data
encrypting data transferred over the wire.</font></h1> transferred over the wire.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Algorithms <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Algorithms
performing encryption of data for different purposes are well known performing encryption of data for different purposes are well known
for many years. The only “little” typical problem remaining is 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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Server <h1><font size="4" style="font-size: 14pt">Server side of
side of authentication plugin.</font></h1> authentication plugin.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Authentication <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Authentication
plugin contains two required parts client and server and may also plugin contains two required parts client and server and may also
contain related third part - user manager. During authentication 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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Client <h1><font size="4" style="font-size: 14pt">Client side of
side of authentication plugin.</font></h1> authentication plugin.</font></h1>
<p style="margin-bottom: 0cm"><a name="ClientBlock"></a><font size="4" style="font-size: 14pt">ClientBlock <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 interface is used by client side of authentication plugin to exchange
data with server.</font></p> 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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">User <h1><font size="4" style="font-size: 14pt">User management plugin.</font></h1>
management plugin.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">This <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">This
plugin is actively related with server side of authentication it plugin is actively related with server side of authentication it
prepares users' list for authentication plugin. Not each prepares users' list for authentication plugin. Not each
@ -3481,8 +3462,8 @@ about the user. </font>
</p> </p>
<p style="margin-bottom: 0cm"><a name="Constants defined by User interface"></a> <p style="margin-bottom: 0cm"><a name="Constants defined by User interface"></a>
<font size="4" style="font-size: 14pt">Constants <font size="4" style="font-size: 14pt">Constants defined by User
defined by User interface valid codes of operation.</font></p> interface valid codes of operation.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">OP_USER_ADD <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">OP_USER_ADD
create user</font></p> create user</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">OP_USER_MODIFY <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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Database <h1><font size="4" style="font-size: 14pt">Database encryption
encryption plugin.</font></h1> plugin.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">An <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">An
ability to encrypt database was present in firebird since interbase ability to encrypt database was present in firebird since interbase
times but appropriate places in the code were commented. 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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Key <h1><font size="4" style="font-size: 14pt">Key holder for database
holder for database encryption plugin.</font></h1> encryption plugin.</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">This <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">This
type of plugin is needed to delineate functionality db crypt type of plugin is needed to delineate functionality db crypt
plugin is dealing with actual encryption, key holder solves questions 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 style="margin-bottom: 0cm"><br/>
</p> </p>
<h1><font size="4" style="font-size: 14pt">Non-interface <h1><font size="4" style="font-size: 14pt">Non-interface objects used
objects used by API (C++ specific header Message.h).</font></h1> by API (C++ specific header Message.h).</font></h1>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Following <p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Following
3 classes are used to represent date, time and timestamp (datetime) 3 classes are used to represent date, time and timestamp (datetime)
when using FB_MESSAGE macro. Members of data structure, representing 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); 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 // BatchCompletionState printer - prints all what we know about completed batch
static void print_cs(ThrowStatusWrapper& status, IBatchCompletionState* cs, IUtil* utl) static void print_cs(ThrowStatusWrapper& status, IBatchCompletionState* cs, IUtil* utl)
@ -270,6 +317,9 @@ int main()
batch->add(&status, 1, project1.getData()); batch->add(&status, 1, project1.getData());
} }
// check batch state
printInfo(status, "Info when added many records", batch, utl);
// ... and cancel that records // ... and cancel that records
batch->cancel(&status); batch->cancel(&status);
@ -347,6 +397,8 @@ int main()
batch->appendBlobData(&status, strlen(sqlStmt1), sqlStmt1); batch->appendBlobData(&status, strlen(sqlStmt1), sqlStmt1);
batch->add(&status, 1, project2.getData()); batch->add(&status, 1, project2.getData());
printInfo(status, "Info with blob", batch, utl);
// execute it // execute it
cs = batch->execute(&status, tra); cs = batch->execute(&status, tra);
print_cs(status, cs, utl); print_cs(status, cs, utl);

View File

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

View File

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

View File

@ -36,15 +36,23 @@
namespace Firebird { namespace Firebird {
ClumpletWriter::ClumpletWriter(Kind k, FB_SIZE_T limit, UCHAR tag) : ClumpletWriter::ClumpletWriter(Kind k, FB_SIZE_T limit, 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)
{ {
initNewBuffer(tag); initNewBuffer(tag);
rewind(); rewind();
} }
ClumpletWriter::ClumpletWriter(MemoryPool& given_pool, Kind k, FB_SIZE_T limit, UCHAR tag) : 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()) : ClumpletReader(given_pool, k, NULL, 0),
sizeLimit(limit),
kindList(NULL),
dynamic_buffer(getPool()),
flag_overflow(false)
{ {
initNewBuffer(tag); initNewBuffer(tag);
rewind(); 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) 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); create(buffer, buffLen, tag);
} }
ClumpletWriter::ClumpletWriter(MemoryPool& pool, const KindList* kl, FB_SIZE_T limit, ClumpletWriter::ClumpletWriter(MemoryPool& pool, const KindList* kl, FB_SIZE_T limit,
const UCHAR* buffer, FB_SIZE_T buffLen) const UCHAR* buffer, FB_SIZE_T buffLen)
: ClumpletReader(pool, kl, buffer, buffLen), sizeLimit(limit), : ClumpletReader(pool, kl, buffer, buffLen),
kindList(kl), dynamic_buffer(getPool()) sizeLimit(limit),
kindList(kl),
dynamic_buffer(getPool()),
flag_overflow(false)
{ {
create(buffer, buffLen, kl->tag); create(buffer, buffLen, kl->tag);
} }
ClumpletWriter::ClumpletWriter(const KindList* kl, FB_SIZE_T limit, const UCHAR* buffer, FB_SIZE_T buffLen) 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); create(buffer, buffLen, kl->tag);
} }
ClumpletWriter::ClumpletWriter(MemoryPool& pool, const KindList* kl, FB_SIZE_T limit) ClumpletWriter::ClumpletWriter(MemoryPool& pool, const KindList* kl, FB_SIZE_T limit)
: ClumpletReader(pool, kl, NULL, 0), sizeLimit(limit), : ClumpletReader(pool, kl, NULL, 0),
kindList(kl), dynamic_buffer(getPool()) sizeLimit(limit),
kindList(kl),
dynamic_buffer(getPool()),
flag_overflow(false)
{ {
create(NULL, 0, kl->tag); create(NULL, 0, kl->tag);
} }
ClumpletWriter::ClumpletWriter(const KindList* kl, FB_SIZE_T limit) 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); create(NULL, 0, kl->tag);
} }
ClumpletWriter::ClumpletWriter(MemoryPool& pool, const ClumpletWriter& from) 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); create(from.getBuffer(), from.getBufferEnd() - from.getBuffer(), from.isTagged() ? from.getBufferTag() : 0);
} }
ClumpletWriter::ClumpletWriter(const ClumpletWriter& from) 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); 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, ClumpletWriter::ClumpletWriter(MemoryPool& given_pool, Kind k, FB_SIZE_T limit,
const UCHAR* buffer, FB_SIZE_T buffLen, UCHAR tag) : const UCHAR* buffer, FB_SIZE_T buffLen, UCHAR tag)
ClumpletReader(given_pool, k, NULL, 0), sizeLimit(limit), dynamic_buffer(getPool()) : ClumpletReader(given_pool, k, NULL, 0),
sizeLimit(limit),
dynamic_buffer(getPool()),
flag_overflow(false)
{ {
if (buffer && buffLen) { if (buffer && buffLen) {
dynamic_buffer.push(buffer, buffLen); dynamic_buffer.push(buffer, buffLen);
@ -194,6 +231,13 @@ void ClumpletWriter::size_overflow()
fatal_exception::raise("Clumplet buffer size limit reached"); 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) 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. 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 // Check that resulting data doesn't overflow size limit
if (dynamic_buffer.getCount() + length + lenSize + 1 > sizeLimit) { size_overflow(dynamic_buffer.getCount() + length + lenSize + 1 > sizeLimit);
size_overflow();
}
// Insert the data // Insert the data
const FB_SIZE_T saved_offset = cur_offset; 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 // Check that resulting data doesn't overflow size limit
if (cur_offset + 1 > sizeLimit) { size_overflow(cur_offset + 1 > sizeLimit);
size_overflow();
}
dynamic_buffer.shrink(cur_offset); dynamic_buffer.shrink(cur_offset);
dynamic_buffer.push(tag); dynamic_buffer.push(tag);

View File

@ -98,10 +98,14 @@ public:
// Returns true if any found // Returns true if any found
bool deleteWithTag(UCHAR tag); bool deleteWithTag(UCHAR tag);
virtual const UCHAR* getBuffer() const; const UCHAR* getBuffer() const override;
bool hasOverflow() const
{
return flag_overflow;
}
protected: protected:
virtual const UCHAR* getBufferEnd() const; const UCHAR* getBufferEnd() const override;
virtual void size_overflow(); virtual void size_overflow();
void insertBytesLengthCheck(UCHAR tag, const void* bytes, const FB_SIZE_T length); void insertBytesLengthCheck(UCHAR tag, const void* bytes, const FB_SIZE_T length);
bool upgradeVersion(); // upgrade clumplet version - obtain newest from kindList bool upgradeVersion(); // upgrade clumplet version - obtain newest from kindList
@ -117,14 +121,10 @@ private:
void initNewBuffer(UCHAR tag); void initNewBuffer(UCHAR tag);
void create(const UCHAR* buffer, FB_SIZE_T buffLen, 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); 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 } // namespace Firebird

View File

@ -30,7 +30,7 @@
#include "../jrd/exe_proto.h" #include "../jrd/exe_proto.h"
#include "../dsql/dsql.h" #include "../dsql/dsql.h"
#include "../dsql/errd_proto.h" #include "../dsql/errd_proto.h"
#include "../common/classes/ClumpletReader.h" #include "../common/classes/ClumpletWriter.h"
#include "../common/classes/auto.h" #include "../common/classes/auto.h"
#include "../common/classes/fb_string.h" #include "../common/classes/fb_string.h"
#include "../common/utils_proto.h" #include "../common/utils_proto.h"
@ -966,6 +966,14 @@ ULONG DsqlBatch::DataCache::getSize() const
return m_used + m_cache.getCount(); return m_used + m_cache.getCount();
} }
ULONG DsqlBatch::DataCache::getCapacity() const
{
if (!m_cacheCapacity)
return 0;
return m_limit;
}
void DsqlBatch::DataCache::clear() void DsqlBatch::DataCache::clear()
{ {
m_cache.clear(); m_cache.clear();
@ -973,3 +981,99 @@ void DsqlBatch::DataCache::clear()
m_space->releaseSpace(0, m_used); m_space->releaseSpace(0, m_used);
m_used = m_got = m_shift = 0; 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); Firebird::IMessageMetadata* getMetadata(thread_db* tdbb);
void cancel(thread_db* tdbb); void cancel(thread_db* tdbb);
void setDefaultBpb(thread_db* tdbb, unsigned parLength, const unsigned char* par); 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 // Additional flags - start from the maximum one
static const UCHAR FLAG_DEFAULT_SEGMENTED = 31; static const UCHAR FLAG_DEFAULT_SEGMENTED = 31;
@ -121,6 +123,7 @@ private:
ULONG reget(ULONG size, UCHAR** buffer, ULONG alignment); ULONG reget(ULONG size, UCHAR** buffer, ULONG alignment);
void remained(ULONG size, ULONG alignment = 0); void remained(ULONG size, ULONG alignment = 0);
ULONG getSize() const; ULONG getSize() const;
ULONG getCapacity() const;
void clear(); void clear();
private: private:

View File

@ -530,6 +530,11 @@ interface Batch : ReferenceCounted
const uint BLOB_SEGHDR_ALIGN = 2; // Alignment of segment header in the stream 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 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 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); void appendBlobData(Status status, uint length, const void* inBuffer);
@ -545,6 +550,9 @@ interface Batch : ReferenceCounted
version: // 4.0.0 => 4.0.1 version: // 4.0.0 => 4.0.1
[notImplementedAction if ::FB_UsedInYValve then defaultAction else call deprecatedClose(status) endif] [notImplementedAction if ::FB_UsedInYValve then defaultAction else call deprecatedClose(status) endif]
void close(Status status); void close(Status status);
void getInfo(Status status,
uint itemsLength, const uchar* items,
uint bufferLength, uchar* buffer);
} }
interface BatchCompletionState : Disposable interface BatchCompletionState : Disposable
@ -1203,6 +1211,8 @@ interface XpbBuilder : Disposable
const uint SPB_SEND = 7; const uint SPB_SEND = 7;
const uint SPB_RECEIVE = 8; const uint SPB_RECEIVE = 8;
const uint SPB_RESPONSE = 9; const uint SPB_RESPONSE = 9;
const uint INFO_SEND = 10;
const uint INFO_RESPONSE = 11;
// removing data // removing data
void clear(Status status); 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 *setDefaultBpb)(IBatch* self, IStatus* status, unsigned parLength, const unsigned char* par) throw();
void (CLOOP_CARG *deprecatedClose)(IBatch* self, IStatus* status) throw(); void (CLOOP_CARG *deprecatedClose)(IBatch* self, IStatus* status) throw();
void (CLOOP_CARG *close)(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: protected:
@ -2025,6 +2026,10 @@ namespace Firebird
static const unsigned char BLOB_ID_USER = 2; static const unsigned char BLOB_ID_USER = 2;
static const unsigned char BLOB_STREAM = 3; static const unsigned char BLOB_STREAM = 3;
static const unsigned BLOB_SEGHDR_ALIGN = 2; 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) 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); static_cast<VTable*>(this->cloopVTable)->close(this, status);
StatusType::checkException(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 class IBatchCompletionState : public IDisposable
@ -4755,6 +4773,8 @@ namespace Firebird
static const unsigned SPB_SEND = 7; static const unsigned SPB_SEND = 7;
static const unsigned SPB_RECEIVE = 8; static const unsigned SPB_RECEIVE = 8;
static const unsigned SPB_RESPONSE = 9; 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) template <typename StatusType> void clear(StatusType* status)
{ {
@ -10173,6 +10193,7 @@ namespace Firebird
this->setDefaultBpb = &Name::cloopsetDefaultBpbDispatcher; this->setDefaultBpb = &Name::cloopsetDefaultBpbDispatcher;
this->deprecatedClose = &Name::cloopdeprecatedCloseDispatcher; this->deprecatedClose = &Name::cloopdeprecatedCloseDispatcher;
this->close = &Name::cloopcloseDispatcher; this->close = &Name::cloopcloseDispatcher;
this->getInfo = &Name::cloopgetInfoDispatcher;
} }
} vTable; } 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() static void CLOOP_CARG cloopaddRefDispatcher(IReferenceCounted* self) throw()
{ {
try try
@ -10401,6 +10436,7 @@ namespace Firebird
virtual void setDefaultBpb(StatusType* status, unsigned parLength, const unsigned char* par) = 0; virtual void setDefaultBpb(StatusType* status, unsigned parLength, const unsigned char* par) = 0;
virtual void deprecatedClose(StatusType* status) = 0; virtual void deprecatedClose(StatusType* status) = 0;
virtual void close(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> 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_setDefaultBpbPtr = procedure(this: IBatch; status: IStatus; parLength: Cardinal; par: BytePtr); cdecl;
IBatch_deprecatedClosePtr = procedure(this: IBatch; status: IStatus); cdecl; IBatch_deprecatedClosePtr = procedure(this: IBatch; status: IStatus); cdecl;
IBatch_closePtr = 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_getSizePtr = function(this: IBatchCompletionState; status: IStatus): Cardinal; cdecl;
IBatchCompletionState_getStatePtr = function(this: IBatchCompletionState; status: IStatus; pos: Cardinal): Integer; cdecl; IBatchCompletionState_getStatePtr = function(this: IBatchCompletionState; status: IStatus; pos: Cardinal): Integer; cdecl;
IBatchCompletionState_findErrorPtr = function(this: IBatchCompletionState; status: IStatus; pos: Cardinal): Cardinal; cdecl; IBatchCompletionState_findErrorPtr = function(this: IBatchCompletionState; status: IStatus; pos: Cardinal): Cardinal; cdecl;
@ -1548,6 +1549,7 @@ type
setDefaultBpb: IBatch_setDefaultBpbPtr; setDefaultBpb: IBatch_setDefaultBpbPtr;
deprecatedClose: IBatch_deprecatedClosePtr; deprecatedClose: IBatch_deprecatedClosePtr;
close: IBatch_closePtr; close: IBatch_closePtr;
getInfo: IBatch_getInfoPtr;
end; end;
IBatch = class(IReferenceCounted) IBatch = class(IReferenceCounted)
@ -1563,6 +1565,10 @@ type
const BLOB_ID_USER = Byte(2); const BLOB_ID_USER = Byte(2);
const BLOB_STREAM = Byte(3); const BLOB_STREAM = Byte(3);
const BLOB_SEGHDR_ALIGN = Cardinal(2); 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 add(status: IStatus; count: Cardinal; inBuffer: Pointer);
procedure addBlob(status: IStatus; length: Cardinal; inBuffer: Pointer; blobId: ISC_QUADPtr; parLength: Cardinal; par: BytePtr); 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 setDefaultBpb(status: IStatus; parLength: Cardinal; par: BytePtr);
procedure deprecatedClose(status: IStatus); procedure deprecatedClose(status: IStatus);
procedure close(status: IStatus); procedure close(status: IStatus);
procedure getInfo(status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr);
end; end;
IBatchImpl = class(IBatch) IBatchImpl = class(IBatch)
@ -1595,6 +1602,7 @@ type
procedure setDefaultBpb(status: IStatus; parLength: Cardinal; par: BytePtr); virtual; abstract; procedure setDefaultBpb(status: IStatus; parLength: Cardinal; par: BytePtr); virtual; abstract;
procedure deprecatedClose(status: IStatus); virtual; abstract; procedure deprecatedClose(status: IStatus); virtual; abstract;
procedure close(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; end;
BatchCompletionStateVTable = class(DisposableVTable) BatchCompletionStateVTable = class(DisposableVTable)
@ -2798,6 +2806,8 @@ type
const SPB_SEND = Cardinal(7); const SPB_SEND = Cardinal(7);
const SPB_RECEIVE = Cardinal(8); const SPB_RECEIVE = Cardinal(8);
const SPB_RESPONSE = Cardinal(9); const SPB_RESPONSE = Cardinal(9);
const INFO_SEND = Cardinal(10);
const INFO_RESPONSE = Cardinal(11);
procedure clear(status: IStatus); procedure clear(status: IStatus);
procedure removeCurrent(status: IStatus); procedure removeCurrent(status: IStatus);
@ -6685,6 +6695,17 @@ begin
FbException.checkException(status); FbException.checkException(status);
end; 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; function IBatchCompletionState.getSize(status: IStatus): Cardinal;
begin begin
Result := BatchCompletionStateVTable(vTable).getSize(Self, status); Result := BatchCompletionStateVTable(vTable).getSize(Self, status);
@ -10704,6 +10725,15 @@ begin
end end
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 var
IBatchImpl_vTable: BatchVTable; IBatchImpl_vTable: BatchVTable;
@ -15637,6 +15667,7 @@ initialization
IBatchImpl_vTable.setDefaultBpb := @IBatchImpl_setDefaultBpbDispatcher; IBatchImpl_vTable.setDefaultBpb := @IBatchImpl_setDefaultBpbDispatcher;
IBatchImpl_vTable.deprecatedClose := @IBatchImpl_deprecatedCloseDispatcher; IBatchImpl_vTable.deprecatedClose := @IBatchImpl_deprecatedCloseDispatcher;
IBatchImpl_vTable.close := @IBatchImpl_closeDispatcher; IBatchImpl_vTable.close := @IBatchImpl_closeDispatcher;
IBatchImpl_vTable.getInfo := @IBatchImpl_getInfoDispatcher;
IBatchCompletionStateImpl_vTable := BatchCompletionStateVTable.create; IBatchCompletionStateImpl_vTable := BatchCompletionStateVTable.create;
IBatchCompletionStateImpl_vTable.version := 3; 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 */ {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 */ {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 */ {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 */ {335545179, "Duplicated segment @1 in multisegment connect block parameter"}, /* multi_segment_dup */
{335545180, "Plugin not supported by network protocol"}, /* non_plugin_protocol */ {335545180, "Plugin not supported by network protocol"}, /* non_plugin_protocol */
{335545181, "Error parsing message format"}, /* message_format */ {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 setDefaultBpb(Firebird::CheckStatusWrapper* status, unsigned parLength, const unsigned char* par) override;
void close(Firebird::CheckStatusWrapper* status) override; void close(Firebird::CheckStatusWrapper* status) override;
void deprecatedClose(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: public:
JBatch(DsqlBatch* handle, JStatement* aStatement, Firebird::IMessageMetadata* aMetadata); 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) JReplicator::JReplicator(Applier* appl, StableAttachmentPart* sa)
: applier(appl), sAtt(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); ('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_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_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); ('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); ('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); ('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; Firebird::IMessageMetadata* getMetadata(Firebird::CheckStatusWrapper* status) override;
void close(Firebird::CheckStatusWrapper* status) override; void close(Firebird::CheckStatusWrapper* status) override;
void deprecatedClose(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: private:
void freeClientData(CheckStatusWrapper* status, bool force = false); void freeClientData(CheckStatusWrapper* status, bool force = false);
void releaseStatement(); void releaseStatement();
void setBlobAlignment(); void setServerInfo();
void cleanup() void cleanup()
{ {
@ -411,7 +414,7 @@ private:
// working with blob stream buffer // working with blob stream buffer
void newBlob() void newBlob()
{ {
setBlobAlignment(); setServerInfo();
alignBlobBuffer(blobAlign); alignBlobBuffer(blobAlign);
fb_assert(blobStream - blobStreamBuffer <= blobBufferSize); fb_assert(blobStream - blobStreamBuffer <= blobBufferSize);
@ -513,7 +516,7 @@ private:
{ {
if (blobPolicy != BLOB_NONE) if (blobPolicy != BLOB_NONE)
{ {
setBlobAlignment(); setServerInfo();
alignBlobBuffer(blobAlign); alignBlobBuffer(blobAlign);
ULONG size = blobStream - blobStreamBuffer; ULONG size = blobStream - blobStreamBuffer;
if (size) if (size)
@ -530,6 +533,7 @@ private:
} }
batchActive = false; batchActive = false;
blobCount = messageCount = 0;
} }
void sendBlobPacket(unsigned size, const UCHAR* ptr, bool flash); void sendBlobPacket(unsigned size, const UCHAR* ptr, bool flash);
@ -549,6 +553,8 @@ private:
UCHAR blobPolicy; UCHAR blobPolicy;
bool segmented, defSegmented, batchActive; bool segmented, defSegmented, batchActive;
ULONG messageCount, blobCount, serverSize;
public: public:
bool tmpStatement; bool tmpStatement;
}; };
@ -2368,7 +2374,8 @@ Batch::Batch(Statement* s, IMessageMetadata* inFmt, unsigned parLength, const un
: messageStream(0), blobStream(nullptr), sizePointer(nullptr), : messageStream(0), blobStream(nullptr), sizePointer(nullptr),
messageSize(0), alignedSize(0), blobBufferSize(0), messageBufferSize(0), flags(0), messageSize(0), alignedSize(0), blobBufferSize(0), messageBufferSize(0), flags(0),
stmt(s), format(inFmt), blobAlign(0), blobPolicy(BLOB_NONE), 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; LocalStatus ls;
CheckStatusWrapper st(&ls); CheckStatusWrapper st(&ls);
@ -2484,6 +2491,7 @@ void Batch::sendMessagePacket(unsigned count, const UCHAR* ptr, bool flash)
statement->rsr_batch_size = alignedSize; statement->rsr_batch_size = alignedSize;
sendDeferredPacket(port, packet, flash); 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; Rdb* rdb = statement->rsr_rdb;
rem_port* port = rdb->rdb_port; rem_port* port = rdb->rdb_port;
setBlobAlignment(); setServerInfo();
fb_assert(!(size % blobAlign)); fb_assert(!(size % blobAlign));
PACKET* packet = &rdb->rdb_packet; 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; batch->p_batch_blob_data.cstr_length = size;
sendDeferredPacket(port, packet, flash); sendDeferredPacket(port, packet, flash);
blobCount += size;
} }
@ -2698,7 +2708,7 @@ unsigned Batch::getBlobAlignment(CheckStatusWrapper* status)
{ {
try try
{ {
setBlobAlignment(); setServerInfo();
} }
catch (const Exception& ex) catch (const Exception& ex)
{ {
@ -2709,7 +2719,7 @@ unsigned Batch::getBlobAlignment(CheckStatusWrapper* status)
} }
void Batch::setBlobAlignment() void Batch::setServerInfo()
{ {
if (blobAlign) if (blobAlign)
return; return;
@ -2727,21 +2737,68 @@ void Batch::setBlobAlignment()
rem_port* port = rdb->rdb_port; rem_port* port = rdb->rdb_port;
RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION); RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
// Perform info call to server
LocalStatus ls; LocalStatus ls;
CheckStatusWrapper s(&ls); CheckStatusWrapper s(&ls);
UCHAR item = isc_info_sql_stmt_blob_align;
UCHAR buffer[16]; if (port->port_protocol < PROTOCOL_VERSION17)
info(&s, rdb, op_info_sql, statement->rsr_id, 0, {
1, &item, 0, 0, sizeof(buffer), buffer); 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); check(&s);
// Extract from buffer // Extract from buffer
if (buffer[0] != item) ClumpletReader out(ClumpletReader::InfoResponse, buffer, sizeof(buffer));
Arg::Gds(isc_batch_align).raise(); 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); switch(item)
statement->rsr_batch_stream.alignment = blobAlign = gds__vax_integer(&buffer[3], len); {
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() void Batch::releaseStatement()
{ {
if (tmpStatement) if (tmpStatement)

View File

@ -504,6 +504,7 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p)
case op_info_transaction: case op_info_transaction:
case op_service_info: case op_service_info:
case op_info_sql: case op_info_sql:
case op_info_batch:
info = &p->p_info; info = &p->p_info;
MAP(xdr_short, reinterpret_cast<SSHORT&>(info->p_info_object)); MAP(xdr_short, reinterpret_cast<SSHORT&>(info->p_info_object));
MAP(xdr_short, reinterpret_cast<SSHORT&>(info->p_info_incarnation)); 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; const USHORT PROTOCOL_STMT_TOUT = PROTOCOL_VERSION16;
// Protocol 17: // Protocol 17:
// - supports op_batch_sync // - supports op_batch_sync, op_info_batch
const USHORT PROTOCOL_VERSION17 = (FB_PROTOCOL_FLAG | 17); const USHORT PROTOCOL_VERSION17 = (FB_PROTOCOL_FLAG | 17);
@ -295,6 +295,7 @@ enum P_OP
op_batch_cancel = 109, op_batch_cancel = 109,
op_batch_sync = 110, op_batch_sync = 110,
op_info_batch = 111,
op_max 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, statement->rsr_iface->getInfo(&status_vector, info_len, info_buffer,
buffer_length, buffer); buffer_length, buffer);
break; 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. // 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_info_transaction:
case op_service_info: case op_service_info:
case op_info_sql: case op_info_sql:
case op_info_batch:
port->info(op, &receive->p_info, sendL); port->info(op, &receive->p_info, sendL);
break; break;

View File

@ -397,6 +397,8 @@ public:
void setDefaultBpb(Firebird::CheckStatusWrapper* status, unsigned parLength, const unsigned char* par); void setDefaultBpb(Firebird::CheckStatusWrapper* status, unsigned parLength, const unsigned char* par);
void close(Firebird::CheckStatusWrapper* status); void close(Firebird::CheckStatusWrapper* status);
void deprecatedClose(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: public:
AtomicAttPtr attachment; AtomicAttPtr attachment;

View File

@ -916,8 +916,14 @@ public:
case SPB_RESPONSE: case SPB_RESPONSE:
k = ClumpletReader::SpbResponse; k = ClumpletReader::SpbResponse;
break; break;
case INFO_SEND:
k = ClumpletReader::InfoItems;
break;
case INFO_RESPONSE:
k = ClumpletReader::InfoResponse;
break;
default: 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; 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);
}
}
//------------------------------------- //-------------------------------------