From 33caf198dd2a31b82d94f15d5dca49c43c3b288a Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Fri, 10 Sep 2021 17:18:48 +0300 Subject: [PATCH] Implemented #6959: IBatch::getInfo() --- doc/Using_OO_API.html | 543 ++++++++++----------- examples/interfaces/11.batch.cpp | 52 ++ src/common/classes/ClumpletReader.cpp | 3 + src/common/classes/ClumpletReader.h | 3 +- src/common/classes/ClumpletWriter.cpp | 82 +++- src/common/classes/ClumpletWriter.h | 18 +- src/dsql/DsqlBatch.cpp | 106 +++- src/dsql/DsqlBatch.h | 3 + src/include/firebird/FirebirdInterface.idl | 10 + src/include/firebird/IdlFbInterfaces.h | 36 ++ src/include/gen/Firebird.pas | 31 ++ src/include/gen/msgs.h | 2 +- src/jrd/EngineInterface.h | 2 + src/jrd/jrd.cpp | 42 ++ src/msgs/messages2.sql | 2 +- src/remote/client/interface.cpp | 156 +++++- src/remote/protocol.cpp | 1 + src/remote/protocol.h | 3 +- src/remote/server/server.cpp | 10 + src/yvalve/YObjects.h | 2 + src/yvalve/utl.cpp | 8 +- src/yvalve/why.cpp | 16 + 22 files changed, 798 insertions(+), 333 deletions(-) diff --git a/doc/Using_OO_API.html b/doc/Using_OO_API.html index 28975e0959..b15ab1adf4 100644 --- a/doc/Using_OO_API.html +++ b/doc/Using_OO_API.html @@ -6,7 +6,7 @@ - + @@ -30,21 +30,19 @@ in some aspects like OLE2 interfaces (some of them have addRef() and release() methods) are non standard and have features, missing in other widely used types of interfaces. First of all Firebird interfaces are language -independent – -that +independentthat means that to define/use them one need not use language specific constructions like class -in -C++, interface may be defined using any language able to call -functions using C calling conventions and having concepts of array -and pointer to procedure/function. Next interfaces are versioned -– i.e. -we support different versions of same interface. Binary layout of -interfaces is designed to support that features very efficient (there -is no need in additional virtual calls like in OLE2/COM with it's -QueryInterface) -but -it's not convenient for direct use from most languages. Therefore +in C++, interface may be +defined using any language able to call functions using C calling +conventions and having concepts of array and pointer to +procedure/function. Next interfaces are versioned +– i.e. we support different +versions of same interface. Binary layout of interfaces is designed +to support that features very efficient (there is no need in +additional virtual calls like in OLE2/COM with it's QueryInterface) +but it's not +convenient for direct use from most languages. Therefore language-specific wrappers should better be designed for different languages making use of API easier. Currently we have wrappers for C++ and Pascal, Java is coming soon. From end-user POV calls from C++ @@ -74,8 +72,7 @@ for it.

Firebird installation package contains a number of live samples of use of OO API – they are in examples/interfaces (database access) and -examples/dbcrypt (plugin performing fictitious -database encryption) +examples/dbcrypt (plugin performing fictitious database encryption) directories. It's supposed that the reader is familiar with ISC API used in Firebird since interbase times.


@@ -101,8 +98,8 @@ too.

Accessing databases.

-

Creating -database and attaching to existing database.

+

Creating database and +attaching to existing database.

First of all we need to get access to IMaster interface. IMaster is primary Firebird interface, required to access all the rest of @@ -157,8 +154,7 @@ will have to modify a call too often to add new parameters, and number of them will be very big no matter of the fact that typically one needs to pass not too much of them. Therefore to pass additional parameters special in-memory data structure, called database -parameters block -(DPB) +parameters block (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 IXpbBuilder, @@ -274,8 +270,7 @@ appropriate samples when reading this document.


-

Working -with transactions.

+

Working with transactions.

Only creating empty databases is definitely not enough to work with RDBMS. We want to be able to create various objects (like tables and so on) @@ -319,8 +314,8 @@ may take a look at how to start and commit transaction in examples


-

Executing -SQL operator without input parameters and returned rows.

+

Executing SQL operator +without input parameters and returned rows.

With started transaction we are ready to execute our first SQL operators. Used for it execute() method in IAttachment @@ -353,8 +348,8 @@ may take a look at how to start and commit transaction in examples


-

Executing -SQL operator with input parameters.

+

Executing SQL operator +with input parameters.

There are 2 ways to execute statement with input parameters. Choice of correct method depends upon do you need to execute it more than once @@ -434,21 +429,20 @@ fields:

builder->setLength(&status, 1, 3);

-New -API is using old constants for SQL types, smallest bit as earlier -stands for nullability. In some case it may also make sense to set -sub-type (for blobs), character set (for text fields) or scale (for -numeric fields). And finally it's time to get an instance of -IMessageMetadata:

+New API is using old constants +for SQL types, smallest bit as earlier stands for nullability. In +some case it may also make sense to set sub-type (for blobs), +character set (for text fields) or scale (for numeric fields). And +finally it's time to get an instance of IMessageMetadata:

IMessageMetadata* meta = builder->getMetadata(&status);


-Here -we do not discuss in details own implementation of IMessageMetadata. -If one cares there is a sample 05.user_metadata.cpp.

+Here we do not discuss in +details own implementation of IMessageMetadata. If one cares there is +a sample 05.user_metadata.cpp.

So finally we have obtained (one or another way) an instance of metadata description of input parameters. But to work with a message we also @@ -513,8 +507,8 @@ exception may be caught by C++ program.


-

Opening -cursor and fetching data from it.

+

Opening cursor and +fetching data from it.

The only way to get rows of data, returned by SELECT operator, in OO API is to use IResultSet interface. This @@ -596,10 +590,10 @@ particular field field's offset should be used:

unsigned char* field_N_ptr = buffer + meta->getOffset(&status, n);

-where -n is the number of a field in a message. That pointer should be -casted to appropriate type, depending upon field type. For example, -for a VARCHAR field cast to struct vary should be used:

+where n is the number of a +field in a message. That pointer should be casted to appropriate +type, depending upon field type. For example, for a VARCHAR field +cast to struct vary should be used:

vary* v_ptr = (vary*) (buffer + meta->getOffset(&status, n));

Now @@ -617,8 +611,8 @@ metadata values like it's done in our samples 03.select.cpp and


-

Using -FB_MESSAGE macro for static messages.

+

Using FB_MESSAGE macro for +static messages.

Working with data using offsets is rather efficient but requires a lot of code to be written. In C++ this problem can be solved using @@ -667,12 +661,11 @@ class FbTimestamp, containing two public data members date and time of appropriate class, char - with struct FbChar and varchar – with struct FbVarChar. For each field preprocessor creates two data members in the message – -name -for +name for field/parameter value and nameNull -for -NULL indicator. Message constructor has 2 parameters – pointer to -status wrapper and master interface:

+for NULL indicator. Message +constructor has 2 parameters – pointer to status wrapper and master +interface:

FB_MESSAGE(Output, ThrowStatusWrapper,

(FB_SMALLINT, @@ -717,8 +710,7 @@ sample 06.fb_message.cpp.


-

Working -with blobs.

+

Working with blobs.

For blobs in message buffer firebird stores blob identifier – an 8-byte entity which should be aligned on 4-byte boundary. Identifier has @@ -911,16 +903,16 @@ meta = batch->getMetadata(&status);

if you have passed your own format of messages to the batch you may simply use it.

-In -the former text I suppose that some function fillNextMessage(unsigned -char* data, IMessageMetadata* metadata) is present and can fill -buffer ‘data’ according to passed format ‘metadata’. In order -to work with messages we need a buffer for a data:

+In the former text I suppose +that some function fillNextMessage(unsigned char* data, +IMessageMetadata* metadata) is present and can fill buffer ‘data’ +according to passed format ‘metadata’. In order to work with +messages we need a buffer for a data:

unsigned char* data = new unsigned char[meta->getMessageLength(&status)];

-Now -we can add some messages full of data to the batch:

+Now we can add some messages +full of data to the batch:

fillNextMessage(data, meta);

batch->add(&status, @@ -930,15 +922,15 @@ meta);

batch->add(&status, 1, data);

-An -alternative way of working with messages (using FB_MESSAGE macro) is -present in the sample of using batch interface 11.batch.cpp.

+An alternative way of working +with messages (using FB_MESSAGE macro) is present in the sample of +using batch interface 11.batch.cpp.


-Finally -batch should be executed:

+Finally batch should be +executed:

IBatchCompletionState* cs = batch->execute(&status, tra);

We @@ -949,19 +941,19 @@ Determine total number of messages processed by batch (it can be less than the number of messages passed to the batch if error happened and an option enabling multiple errors during batch processing was not turned on):

-

unsigned -total = cs->getSize(&status);

-

Now -print the state of each message:

-

for -(unsigned p = 0; p < total; ++p) printf(“Msg %u state %d\n”, -p, cs->getState(&status, p));

+

unsigned total = +cs->getSize(&status);

+

Now print the state of each +message:

+

for (unsigned p = 0; p < +total; ++p) printf(“Msg %u state %d\n”, p, cs->getState(&status, +p));

When finished analyzing completion state don’t forget to dispose it:

cs->dispose();

Full -sample of printing contents -of BatchCompletionState +sample of printing contents of +BatchCompletionState is in print_cs() function in sample 11.batch.cpp.

If for some reason you want to make batch buffers empty not executing it @@ -969,18 +961,17 @@ for some reason you want to make batch buffers empty not executing it method:

batch->cancel(&status);

-Like -the rest of our data access interfaces Batch has special method to -close it:

+Like the rest of our data +access interfaces Batch has special method to close it:

batch->close(&status);

-Standard -release() call may be used instead if one does not care about errors:

+Standard release() call may be +used instead if one does not care about errors:

batch->release();

-Described -methods help to implement all what one needs for JDBC-style prepared -statement batch operations. +Described methods help to +implement all what one needs for JDBC-style prepared statement batch +operations.


@@ -1002,27 +993,23 @@ returned in
An optimal batch size should be found for each particular case but sooner of all having it >1000 hardly gives you serious performance increase.

-


-
+


-

One -can add more than single message in one call to the batch. When doing -it please remember – messages should be appropriately aligned for -this feature to work correctly. Required alignment and aligned size -of the message should be obtained from MessageMetadata +

One can add more than +single message in one call to the batch. When doing it please +remember – messages should be appropriately aligned for this +feature to work correctly. Required alignment and aligned size of the +message should be obtained from MessageMetadata interface, for example:

-

unsigned -aligned = meta->getAlignedLength(&status);

-

Later -that size will be useful when allocating an array of messages and -working with it:

-

unsigned -char* data = new unsigned char[aligned * N]; // N is desired number -of messages

-

for -(int n = 0; n < N; ++n) fillNextMessage(&data[aligned * n], -meta);

+

unsigned aligned = +meta->getAlignedLength(&status);

+

Later that size will be +useful when allocating an array of messages and working with it:

+

unsigned char* data = +new unsigned char[aligned * N]; // N is desired number of messages

+

for (int n = 0; n < +N; ++n) fillNextMessage(&data[aligned * n], meta);

batch->add(&status, N, data);

After @@ -1031,19 +1018,18 @@ it batch may be executed or next portion of messages added to it.


-

Blobs -in general are not compatible with batches – batch is efficient -when one needs to pass a lot of small data to the server in single -step, blobs are treated as large objects and therefore in general it -makes no sense to use them in batches. But on practice it often -happens that blobs are not too big – and in this case use of -traditional blob API (create blob, pass segments to the server, close -blob, pass blobs ID in the message) kills performance, specially when -used over WAN. Therefore in firebird batch supports passing blobs to -server inline, together with other messages. To use that feature -first of all blob usage policy for a -batch to be created should be set (as an option in parameters -block):

+

Blobs in general are not +compatible with batches – batch is efficient when one needs to pass +a lot of small data to the server in single step, blobs are treated +as large objects and therefore in general it makes no sense to use +them in batches. But on practice it often happens that blobs are not +too big – and in this case use of traditional blob API (create +blob, pass segments to the server, close blob, pass blobs ID in the +message) kills performance, specially when used over WAN. Therefore +in firebird batch supports passing blobs to server inline, together +with other messages. To use that feature first of all blob +usage policy for a batch to be created should be set (as an +option in parameters block):

pb->insertInt(&status, IBatch::BLOB_IDS, IBatch::BLOB_IDS_ENGINE);

In @@ -1053,29 +1039,26 @@ that’s the simplest and rather common usage. Imagine that the message is described as follows:

FB_MESSAGE(Msg, ThrowStatusWrapper,

-

(FB_VARCHAR(5), -id)

-

(FB_VARCHAR(10), -name)

-

(FB_BLOB, -desc)

-

) -project(&status, master);

+

(FB_VARCHAR(5), id)

+

(FB_VARCHAR(10), name)

+

(FB_BLOB, desc)

+

) project(&status, +master);

In that case to send a message containing blob to the server one can do something like this:

-

project->id -= ++idCounter;

+

project->id = +++idCounter;

project->name.set(currentName);

batch->addBlob(&status, descriptionSize, descriptionText, &project->desc);

batch->add(&status, 1, project.getData());

-If -some blob happened to be big enough not to fit into your existing -buffer you may instead reallocating buffer use appendBlobData() -method. It appends more data to last added blob. +If some blob happened to be +big enough not to fit into your existing buffer you may instead +reallocating buffer use appendBlobData() method. It appends more data +to last added blob.

batch->addBlob(&status, descriptionSize, descriptionText, &project→desc, bpbLength, @@ -1085,13 +1068,13 @@ adding first part of blob get next portion of data into descriptionText, update descriptionSize and:

batch->appendBlobData(&status, descriptionSize, descriptionText);

-

This -may be done in a loop but take care not to overflow internal batch -buffers – it’s size is controlled by BUFFER_BYTES_SIZE -option when creating batch interface but can’t exceed 256Mb -(default is 16Mb). If you need to process such big blob (for example -on the background of a lot of small one – this can explain use of -batch) just use standard blob API and registerBlob +

This may be done in a loop +but take care not to overflow internal batch buffers – it’s size +is controlled by BUFFER_BYTES_SIZE option +when creating batch interface but can’t exceed 256Mb (default is +16Mb). If you need to process such big blob (for example on the +background of a lot of small one – this can explain use of batch) +just use standard blob API and registerBlob method of Batch interface.

One more possible choice of blob policy is BLOB_IDS_USER. Usage at the @@ -1104,17 +1087,16 @@ imagine a case when you get blobs and other data in relatively independent streams (blocks in a file for example) and some good IDs are already present in them. In such case use of user-supplied blob IDs can greatly simplify your code.

-

Please -take into an account – unlike blobs created using regular -createBlob() blobs created by Batch -interface are by default stream, not segmented. Segmented blobs -provide nothing interesting compared with stream one and therefore -not recommended to be used in new development, we support that format -only for backward compatibility reasons. If you really need segmented -blobs this default may be overridden by calling:

+

Please take into an account +– unlike blobs created using regular createBlob() +blobs created by Batch interface are by default +stream, not segmented. Segmented blobs provide nothing interesting +compared with stream one and therefore not recommended to be used in +new development, we support that format only for backward +compatibility reasons. If you really need segmented blobs this +default may be overridden by calling:

batch->setDefaultBpb(&status, -bpbLength, -bpb);

+bpbLength, bpb);

Certainly passed BPB may contain any other blob creation parameters too. As you may have already noticed you may also pass BPB directly to addBlob() @@ -1124,13 +1106,12 @@ to segmented blobs – call to addBlob() will add first segment to the blob, following calls to appendBlobData() will add more segments. Do not forget that segment size is limited to 64Kb – 1, an attempt to pass more data in a single call with cause an error.

-

Next -step when working with existing blob streams is use of -addBlobStream() method. Using it one can add more than one blob to -the batch per single call. Blob stream is a sequence of blobs, each -starts with blob header. Header should be appropriately aligned - -Batch interface provides special call for this -purpose:

+

Next step when working with +existing blob streams is use of addBlobStream() method. Using it one +can add more than one blob to the batch per single call. Blob stream +is a sequence of blobs, each starts with blob header. Header should +be appropriately aligned - Batch interface +provides special call for this purpose:

unsigned alignment = batch->getBlobAlignment(&status);

It’s @@ -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.

Last method used to work with blobs stands alone from the first three that -pass blob data inline with the rest of batch data – -it’s +pass blob data inline with the rest of batch data it’s needed to register in a batch ID of a blob created using standard blob API. This may be unavoidable if one needs to pass to a batch really big blob. Do not use ID of such blob in batch directly – @@ -1171,50 +1151,47 @@ do:

batch->registerBlob(&status, &realId, &msg->desc);

-If -blob policy makes firebird engine generate blob IDs this code is -enough to correctly register existing blob in a batch. In other cases -you will have to assign correct (from batch POV) ID to msg->desc.

+If blob policy makes firebird +engine generate blob IDs this code is enough to correctly register +existing blob in a batch. In other cases you will have to assign +correct (from batch POV) ID to msg->desc.


-Almost -all mentioned methods are used in 11.batch.cpp – please use it to -see an alive sample of batching in firebird.

+Almost all mentioned methods +are used in 11.batch.cpp – please use it to see an alive sample of +batching in firebird.


-Two -words about access to batches from ISC API - one can execute prepared -ISC statement in batch mode. Main support for it is presence of two -new API functions, namely fb_get_transaction_interface & -fb_get_statement_interface, which make it possible to access -appropriate interfaces identical to existing ISC handles. An example -of it is present in 12.batch_isc.cpp.

+Two words about access to +batches from ISC API - one can execute prepared ISC statement in +batch mode. Main support for it is presence of two new API functions, +namely fb_get_transaction_interface & fb_get_statement_interface, +which make it possible to access appropriate interfaces identical to +existing ISC handles. An example of it is present in +12.batch_isc.cpp.


-

Working -with events.

+

Working with events.

Events interface was not completed in FB3, we expect to have something more interesting in next version. The minimum existing support is as follows: IAttachment contains call queEvents() which performs almost same functions as isc_que_events() call. Instead the pair of parameters FPTR_EVENT_CALLBACK -ast and -void* -arg, -required to invoke user code when event happens in firebird engine, -callback interface IEventCallback is used. This is traditional -approach which helps to avoid non-safe casts from void* in user -function. Another important difference is that instead event -identifier (a kind of handler) this function returns reference -counted interface IEvents having method -cancel() used when waiting for event should be stopped. Unlike -identifier which is automatically destroyed when event arrives +ast and void* +arg, required to +invoke user code when event happens in firebird engine, callback +interface IEventCallback is used. This is traditional approach which +helps to avoid non-safe casts from void* in user function. Another +important difference is that instead event identifier (a kind of +handler) this function returns reference counted interface IEvents +having method cancel() used when waiting for event should be stopped. +Unlike identifier which is automatically destroyed when event arrives interface can not be automatically destroyed – in case when event is received right before canceling interface call to cancel() would cause segfault when interface is already destroyed. Therefore @@ -1235,8 +1212,7 @@ with ISC API. Please use for additional details our sample


-

Using -services.

+

Using services.

To begin to use services one should first of all connect to service manager. This is done using attachServiceManager() method of @@ -1364,15 +1340,14 @@ plugins.

write a plugin means to implement some interfaces and place your implementation into dynamic library (.dll in windows or .so in linux) later referenced as
plugin -module or -just module. -In most cases single plugin is placed -into -dynamic -library but in common case multiple plugins may coexist in single -dynamic library. One of that interfaces – IPluginModule -– is module-wide (as more or less clear from it's name), others are -per plugin. Also each plugin module should contain special exported +module or just +module. +In most cases single plugin is placed into +dynamic library but in common +case multiple plugins may coexist in single dynamic library. One of +that interfaces – IPluginModule – is +module-wide (as more or less clear from it's name), others are per +plugin. Also each plugin module should contain special exported entrypoint firebird_plugin() which name is defined in include file firebird/Interfaces.h as FB_PLUGIN_ENTRY_POINT.

In @@ -1392,28 +1367,28 @@ sample yourself and learn it when reading later.


-

Implementation -of plugin module.

+

Implementation of plugin +module.

Plugins actively interact with special firebird component called plugin -manager. -In particular plugin manager should be aware what plugin modules were +manager. In +particular plugin manager should be aware what plugin modules were loaded and must be notified if operating system tries to unload one of that modules without explicit plugin manager command (this may happen first of all when using embedded access – when exit() is called in a program or main firebird library fbclient -is -unloaded). Primary task of IPluginModule interface is that -notification. First of all one must decide - how to detect that -module is going to be unloaded? When dynamic library is unloaded for -some reason a lot of OS-dependent actions is performed and some of -that actions may be used to detect this fact in the program. When -writing plugins distributed with firebird we always use invocation of -destructor of global variable. The big “plus” for this method is -that it is OS independent (though something like atexit() function -maybe also used successfully). But use of destructor makes it -possible to easily concentrate almost everything related with unload -detection in single class implementing at the same time IPluginModule +is unloaded). Primary task of +IPluginModule interface is that notification. First of all one must +decide - how to detect that module is going to be unloaded? When +dynamic library is unloaded for some reason a lot of OS-dependent +actions is performed and some of that actions may be used to detect +this fact in the program. When writing plugins distributed with +firebird we always use invocation of destructor of global variable. +The big “plus” for this method is that it is OS independent +(though something like atexit() function maybe also used +successfully). But use of destructor makes it possible to easily +concentrate almost everything related with unload detection in single +class implementing at the same time IPluginModule interface.


@@ -1480,15 +1455,15 @@ It's passed to registerModule() function and saved in private variable, at the same time module is registered in plugin manager by the call to registerModule() method with own address as a single parameter. Variable
pluginManager -not -only stores pointer to interface, at the same time it serves as a -flag that module is registered. When destructor of registered module -is invoked it notifies plugin manager (yes, this is what for this -class exists!) about unexpected unload by the call to -unregisterModule() passing pointer to itself. When plugin manager is -going to unload module in regular way in first of all calls doClean() -method changing module state to unregistered and this avoiding call -to unregisterModule() when OS performs actual unload.

+
not only stores +pointer to interface, at the same time it serves as a flag that +module is registered. When destructor of registered module is invoked +it notifies plugin manager (yes, this is what for this class exists!) +about unexpected unload by the call to unregisterModule() passing +pointer to itself. When plugin manager is going to unload module in +regular way in first of all calls doClean() method changing module +state to unregistered and this avoiding call to unregisterModule() +when OS performs actual unload.


@@ -1502,8 +1477,8 @@ registerMe() function from FB_PLUGIN_ENTRY_POINT.


-

Core -interface of any plugin.

+

Core interface of any +plugin.

Let's start implementing plugin itself. The type of main interface depends upon plugin type (which is obvious), but all of them are based on @@ -1534,15 +1509,14 @@ config(cnf), refCounter(0), owner(NULL)

config->addRef();

}

-Constructor -gets as parameter plugin configuration interface. If you are going to -have you plugin configured in some way it's good idea to save this -interface in your plugin and use it later. This will let you use -common for all firebird configuration style letting users have -familiar configuration and minimize code written. Certainly when -saving any reference counted interface it's better not forget to add -reference to it. Also set reference counter to 0 and plugin owner to -NULL.

+Constructor gets as parameter +plugin configuration interface. If you are going to have you plugin +configured in some way it's good idea to save this interface in your +plugin and use it later. This will let you use common for all +firebird configuration style letting users have familiar +configuration and minimize code written. Certainly when saving any +reference counted interface it's better not forget to add reference +to it. Also set reference counter to 0 and plugin owner to NULL.


@@ -1551,9 +1525,9 @@ NULL.

config->release();

}

-Destructor -releases config interface. Pay attention – we do not change -reference counter of our owner cause it owns us, not we own it.

+Destructor releases config +interface. Pay attention – we do not change reference counter of +our owner cause it owns us, not we own it.


@@ -1582,8 +1556,8 @@ addRef()

++refCounter;

}

-Absolutely -typical implementation of reference counted object.

+Absolutely typical +implementation of reference counted object.


@@ -1605,8 +1579,8 @@ getOwner()

owner;

}

-As -it was promised implementation of IPluginBase is trivial.

+As it was promised +implementation of IPluginBase is trivial.


@@ -1635,8 +1609,7 @@ lo-o-o-ot of code to make them useful) interface is ready.


-

Plugin's -factory.

+

Plugin's factory.

One more interface required for plugin to work is IPluginFactory. Factory creates instances of plugin and returns them to plugin @@ -1660,12 +1633,12 @@ p;

-Here -attention should be payed to the fact that even in a case when code -in a function may throw exceptions (operator new may throw in a case -when memory exhausted) one need not always manually define try/catch -block – implementation of firebird interfaces does this job for -you, in implementation of IPluginFactory it's placed into template +Here attention should be payed +to the fact that even in a case when code in a function may throw +exceptions (operator new may throw in a case when memory exhausted) +one need not always manually define try/catch block – +implementation of firebird interfaces does this job for you, in +implementation of IPluginFactory it's placed into template IPluginFactoryImpl. Take into an account that default status wrappers perform meaning-full processing only for FbException. But if you (that definitely makes sense if you work on some big project) define @@ -1674,8 +1647,8 @@ useful information about it from your plugin.


-

Plugin -module initialization entrypoint.

+

Plugin module +initialization entrypoint.

When plugin manager loads plugin module it invokes module initializing routine – the only exported from plugin function @@ -1749,8 +1722,7 @@ direct analogue in old API, that analogue is provided.


-

Generic -interfaces.

+

Generic interfaces.

Attachment interface – replaces isc_db_handle:

    @@ -1827,12 +1799,10 @@ interface – replaces isc_db_handle:

  1. IBatch* createBatch(StatusType* status, ITransaction* transaction, unsigned stmtLength, const char* sqlStmt, unsigned dialect, IMessageMetadata* - inMetadata, unsigned - parLength, + inMetadata, unsigned parLength, const unsigned char* par) – prepares sqlStmt and creates Batch interface ready to accept multiple sets of input parameters in - inMetadata format. Leaving inMetadata - NULL + inMetadata format. Leaving inMetadata NULL makes batch use default format for sqlStmt. Parameters block may be passed to createBatch() making it possible to adjust batch behavior.

  2. IEvents* @@ -1861,15 +1831,13 @@ interface – makes it possible to process multiple sets of parameters in single statement execution.

    1. void - add(StatusType* status, unsigned - count, + add(StatusType* status, unsigned count, const void* inBuffer) – adds count messages from inBuffer to the batch. Total size of messages that can be added to the batch is limited by TAG_BUFFER_BYTES_SIZE parameter of batch creation.

    2. void - addBlob(StatusType* status, unsigned - length, + addBlob(StatusType* status, unsigned length, const void* inBuffer, ISC_QUAD* blobId, unsigned bpbLength, const unsigned char* bpb) – adds single blob having length bytes from inBuffer to the batch, blob identifier is located at blobId address. @@ -1932,6 +1900,10 @@ parameters in single statement execution.

      char* par) – sets BPB which will be used for all blobs missing non-default BPB. Must be called before adding any message or blob to batch.

      +
    3. void + getInfo(StatusType* status, unsigned itemsLength, const unsigned + char* items, unsigned bufferLength, unsigned char* buffer) – + requests information about batch.

    Tag for parameters block:

    @@ -1960,6 +1932,17 @@ used to store blobs:

    - blobs are added one by one, IDs are generated by user

    BLOB_STREAM - blobs are added in a stream

    +

    Items +accepted in getInfo() call:

    +

    INF_BUFFER_BYTES_SIZE +– actual maximum +possible buffer size (one set by TAG_BUFFER_BYTES_SIZE)

    +

    INF_DATA_BYTES_SIZE +- already added messages size

    +

    INF_BLOBS_BYTES_SIZE +- already added blobs size

    +

    INF_BLOB_ALIGNMENT +- required alignment for the BLOB data (duplicates getBlobAlignment)


    @@ -2156,10 +2139,10 @@ sub-entries) in firebird configuration file:

    -DecFloat16 -/ DecFloat34 – interfaces that help to work with DECFLOAT (16 & -34 respectively) datatypes. They have almost same set of methods with -FB_DEC16 parameter replaced by FB_DEC34 for DecFloat34:

    +DecFloat16 / DecFloat34 – +interfaces that help to work with DECFLOAT (16 & 34 respectively) +datatypes. They have almost same set of methods with FB_DEC16 +parameter replaced by FB_DEC34 for DecFloat34:

    1. void toBcd(const FB_DEC16* from, int* sign, unsigned char* bcd, int* exp) @@ -2794,8 +2777,8 @@ defined by Statement interface:

      IAttachment::prepare() flags:

      -PREPARE_PREFETCH_NONE -– constant to pass no flags, 0 value.

      +PREPARE_PREFETCH_NONE – +constant to pass no flags, 0 value.

      The following flags may be OR-ed to get desired set of flags:

        @@ -2820,8 +2803,8 @@ used combinations of flags:

        -Values -returned by getFlags() method: +Values returned by getFlags() +method:

        FLAG_HAS_CURSOR – use openCursor() to execute this statement, not execute()

        @@ -2832,8 +2815,7 @@ parameters

        -Flags -passed to openCursor():

        +Flags passed to openCursor():

        CURSOR_TYPE_SCROLLABLE – open bidirectional cursor.


        @@ -2931,8 +2913,8 @@ moment when given timer should alarm.

        start(StatusType* status, ITimer* timer, ISC_UINT64 microSeconds) – start ITimer to alarm after given delay (in microseconds, 10-6 - seconds). - Timer will be waked up only once after this call.

        + seconds). Timer will be waked + up only once after this call.

      1. void stop(StatusType* status, ITimer* timer) – stop ITimer. 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.

        decodeTimeTz(StatusType* status, const ISC_TIME_TZ* timeTz, unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned*

        fractions, - unsigned timeZoneBufferLength, char* timeZoneBuffer) - – decode time taking time zone into an account.

        + unsigned timeZoneBufferLength, char* timeZoneBuffer) – decode time + taking time zone into an account.

      2. void decodeTimeStampTz(StatusType* status, const ISC_TIMESTAMP_TZ* timeStampTz, unsigned* year, unsigned* month, unsigned* day, @@ -3195,8 +3177,8 @@ builder types:


        -

        Plugin, -encrypting data transferred over the wire.

        +

        Plugin, encrypting data +transferred over the wire.

        Algorithms performing encryption of data for different purposes are well known for many years. The only “little” typical problem remaining is @@ -3262,8 +3244,8 @@ such interface it should be implemented by author of the plugin.


        -

        Server -side of authentication plugin.

        +

        Server side of +authentication plugin.

        Authentication plugin contains two required parts – client and server and may also contain related third part - user manager. During authentication @@ -3350,8 +3332,8 @@ interface is main interface of server side of authentication plugin.


        -

        Client -side of authentication plugin.

        +

        Client side of +authentication plugin.

        ClientBlock interface is used by client side of authentication plugin to exchange data with server.

        @@ -3387,8 +3369,7 @@ interface is main interface of client side of authentication plugin.


        -

        User -management plugin.

        +

        User management plugin.

        This plugin is actively related with server side of authentication – it prepares users' list for authentication plugin. Not each @@ -3481,8 +3462,8 @@ about the user.

        -Constants -defined by User interface – valid codes of operation.

        +Constants defined by User +interface – valid codes of operation.

        OP_USER_ADD – create user

        OP_USER_MODIFY @@ -3559,8 +3540,8 @@ interface is main interface of user management plugin.


        -

        Database -encryption plugin.

        +

        Database encryption +plugin.

        An ability to encrypt database was present in firebird since interbase times but appropriate places in the code were commented. @@ -3651,8 +3632,8 @@ interface is main interface of database crypt plugin.


        -

        Key -holder for database encryption plugin.

        +

        Key holder for database +encryption plugin.

        This type of plugin is needed to delineate functionality – db crypt plugin is dealing with actual encryption, key holder solves questions @@ -3706,8 +3687,8 @@ interface is main interface of database crypt key holder plugin.


        -

        Non-interface -objects used by API (C++ specific header Message.h).

        +

        Non-interface objects used +by API (C++ specific header Message.h).

        Following 3 classes are used to represent date, time and timestamp (datetime) when using FB_MESSAGE macro. Members of data structure, representing diff --git a/examples/interfaces/11.batch.cpp b/examples/interfaces/11.batch.cpp index 4daa4f8553..8518037683 100644 --- a/examples/interfaces/11.batch.cpp +++ b/examples/interfaces/11.batch.cpp @@ -114,6 +114,53 @@ unsigned putSegment(unsigned char*& ptr, const char* testData) return align(l + sizeof l, IBatch::BLOB_SEGHDR_ALIGN); } +// batch info printer - prints what we know about batch + +static void printInfo(ThrowStatusWrapper& status, const char* hdr, IBatch* b, IUtil* utl) +{ + printf("\n%s\n", hdr); + + const unsigned char items[] = {IBatch::INF_BLOB_ALIGNMENT, IBatch::INF_BUFFER_BYTES_SIZE, + IBatch::INF_DATA_BYTES_SIZE, IBatch::INF_BLOBS_BYTES_SIZE}; + unsigned char buffer[29]; + b->getInfo(&status, sizeof items, items, sizeof buffer, buffer); + + IXpbBuilder* pb = utl->getXpbBuilder(&status, IXpbBuilder::INFO_RESPONSE, buffer, sizeof buffer); + for (pb->rewind(&status); !pb->isEof(&status); pb->moveNext(&status)) + { + int val = pb->getInt(&status); + const char* text = "Unknown tag"; + switch (pb->getTag(&status)) + { + case IBatch::INF_BLOB_ALIGNMENT: + text = "Blob alignment"; + break; + case IBatch::INF_BUFFER_BYTES_SIZE: + text = "Buffer size"; + break; + case IBatch::INF_DATA_BYTES_SIZE: + text = "Messages size"; + break; + case IBatch::INF_BLOBS_BYTES_SIZE: + text = "Blobs size"; + break; + case isc_info_truncated: + printf(" truncated\n"); + // fall down... + case isc_info_end: + pb->dispose(); + return; + default: + printf("Unexpected item %d\n", pb->getTag(&status)); + pb->dispose(); + return; + } + + printf("%s = %d\n", text, val); + } + pb->dispose(); +} + // BatchCompletionState printer - prints all what we know about completed batch static void print_cs(ThrowStatusWrapper& status, IBatchCompletionState* cs, IUtil* utl) @@ -270,6 +317,9 @@ int main() batch->add(&status, 1, project1.getData()); } + // check batch state + printInfo(status, "Info when added many records", batch, utl); + // ... and cancel that records batch->cancel(&status); @@ -347,6 +397,8 @@ int main() batch->appendBlobData(&status, strlen(sqlStmt1), sqlStmt1); batch->add(&status, 1, project2.getData()); + printInfo(status, "Info with blob", batch, utl); + // execute it cs = batch->execute(&status, tra); print_cs(status, cs, utl); diff --git a/src/common/classes/ClumpletReader.cpp b/src/common/classes/ClumpletReader.cpp index 50385bd137..6fba979970 100644 --- a/src/common/classes/ClumpletReader.cpp +++ b/src/common/classes/ClumpletReader.cpp @@ -242,6 +242,7 @@ UCHAR ClumpletReader::getBufferTag() const case SpbReceiveItems: case SpbResponse: case InfoResponse: + case InfoItems: usage_mistake("buffer is not tagged"); return 0; case SpbAttach: @@ -313,6 +314,7 @@ ClumpletReader::ClumpletType ClumpletReader::getClumpletType(UCHAR tag) const } return StringSpb; case SpbReceiveItems: + case InfoItems: return SingleTpb; case SpbStart: switch(tag) @@ -704,6 +706,7 @@ void ClumpletReader::rewind() case SpbReceiveItems: case SpbResponse: case InfoResponse: + case InfoItems: cur_offset = 0; break; default: diff --git a/src/common/classes/ClumpletReader.h b/src/common/classes/ClumpletReader.h index b886163111..7ceddb00ef 100644 --- a/src/common/classes/ClumpletReader.h +++ b/src/common/classes/ClumpletReader.h @@ -58,7 +58,8 @@ public: SpbSendItems, SpbReceiveItems, SpbResponse, - InfoResponse + InfoResponse, + InfoItems }; struct KindList diff --git a/src/common/classes/ClumpletWriter.cpp b/src/common/classes/ClumpletWriter.cpp index 091c988486..7e2a185abe 100644 --- a/src/common/classes/ClumpletWriter.cpp +++ b/src/common/classes/ClumpletWriter.cpp @@ -36,15 +36,23 @@ namespace Firebird { -ClumpletWriter::ClumpletWriter(Kind k, FB_SIZE_T limit, UCHAR tag) : - ClumpletReader(k, NULL, 0), sizeLimit(limit), kindList(NULL), dynamic_buffer(getPool()) +ClumpletWriter::ClumpletWriter(Kind k, FB_SIZE_T limit, UCHAR tag) + : ClumpletReader(k, NULL, 0), + sizeLimit(limit), + kindList(NULL), + dynamic_buffer(getPool()), + flag_overflow(false) { initNewBuffer(tag); rewind(); } -ClumpletWriter::ClumpletWriter(MemoryPool& given_pool, Kind k, FB_SIZE_T limit, UCHAR tag) : - ClumpletReader(given_pool, k, NULL, 0), sizeLimit(limit), kindList(NULL), dynamic_buffer(getPool()) +ClumpletWriter::ClumpletWriter(MemoryPool& given_pool, Kind k, FB_SIZE_T limit, UCHAR tag) + : ClumpletReader(given_pool, k, NULL, 0), + sizeLimit(limit), + kindList(NULL), + dynamic_buffer(getPool()), + flag_overflow(false) { initNewBuffer(tag); rewind(); @@ -82,46 +90,72 @@ void ClumpletWriter::initNewBuffer(UCHAR tag) } ClumpletWriter::ClumpletWriter(Kind k, FB_SIZE_T limit, const UCHAR* buffer, FB_SIZE_T buffLen, UCHAR tag) - : ClumpletReader(k, NULL, 0), sizeLimit(limit), kindList(NULL), dynamic_buffer(getPool()) + : ClumpletReader(k, NULL, 0), + sizeLimit(limit), + kindList(NULL), + dynamic_buffer(getPool()), + flag_overflow(false) { create(buffer, buffLen, tag); } ClumpletWriter::ClumpletWriter(MemoryPool& pool, const KindList* kl, FB_SIZE_T limit, const UCHAR* buffer, FB_SIZE_T buffLen) - : ClumpletReader(pool, kl, buffer, buffLen), sizeLimit(limit), - kindList(kl), dynamic_buffer(getPool()) + : ClumpletReader(pool, kl, buffer, buffLen), + sizeLimit(limit), + kindList(kl), + dynamic_buffer(getPool()), + flag_overflow(false) { create(buffer, buffLen, kl->tag); } ClumpletWriter::ClumpletWriter(const KindList* kl, FB_SIZE_T limit, const UCHAR* buffer, FB_SIZE_T buffLen) - : ClumpletReader(kl, buffer, buffLen), sizeLimit(limit), kindList(kl), dynamic_buffer(getPool()) + : ClumpletReader(kl, buffer, buffLen), + sizeLimit(limit), + kindList(kl), + dynamic_buffer(getPool()), + flag_overflow(false) { create(buffer, buffLen, kl->tag); } ClumpletWriter::ClumpletWriter(MemoryPool& pool, const KindList* kl, FB_SIZE_T limit) - : ClumpletReader(pool, kl, NULL, 0), sizeLimit(limit), - kindList(kl), dynamic_buffer(getPool()) + : ClumpletReader(pool, kl, NULL, 0), + sizeLimit(limit), + kindList(kl), + dynamic_buffer(getPool()), + flag_overflow(false) { create(NULL, 0, kl->tag); } ClumpletWriter::ClumpletWriter(const KindList* kl, FB_SIZE_T limit) - : ClumpletReader(kl, NULL, 0), sizeLimit(limit), kindList(kl), dynamic_buffer(getPool()) + : ClumpletReader(kl, NULL, 0), + sizeLimit(limit), + kindList(kl), + dynamic_buffer(getPool()), + flag_overflow(false) { create(NULL, 0, kl->tag); } ClumpletWriter::ClumpletWriter(MemoryPool& pool, const ClumpletWriter& from) - : ClumpletReader(pool, from), sizeLimit(from.sizeLimit), kindList(NULL), dynamic_buffer(getPool()) + : ClumpletReader(pool, from), + sizeLimit(from.sizeLimit), + kindList(NULL), + dynamic_buffer(getPool()), + flag_overflow(false) { create(from.getBuffer(), from.getBufferEnd() - from.getBuffer(), from.isTagged() ? from.getBufferTag() : 0); } ClumpletWriter::ClumpletWriter(const ClumpletWriter& from) - : ClumpletReader(from), sizeLimit(from.sizeLimit), kindList(NULL), dynamic_buffer(getPool()) + : ClumpletReader(from), + sizeLimit(from.sizeLimit), + kindList(NULL), + dynamic_buffer(getPool()), + flag_overflow(false) { create(from.getBuffer(), from.getBufferEnd() - from.getBuffer(), from.isTagged() ? from.getBufferTag() : 0); } @@ -138,8 +172,11 @@ void ClumpletWriter::create(const UCHAR* buffer, FB_SIZE_T buffLen, UCHAR tag) } ClumpletWriter::ClumpletWriter(MemoryPool& given_pool, Kind k, FB_SIZE_T limit, - const UCHAR* buffer, FB_SIZE_T buffLen, UCHAR tag) : - ClumpletReader(given_pool, k, NULL, 0), sizeLimit(limit), dynamic_buffer(getPool()) + const UCHAR* buffer, FB_SIZE_T buffLen, UCHAR tag) + : ClumpletReader(given_pool, k, NULL, 0), + sizeLimit(limit), + dynamic_buffer(getPool()), + flag_overflow(false) { if (buffer && buffLen) { dynamic_buffer.push(buffer, buffLen); @@ -194,6 +231,13 @@ void ClumpletWriter::size_overflow() fatal_exception::raise("Clumplet buffer size limit reached"); } +void ClumpletWriter::size_overflow(bool condition) +{ + flag_overflow = condition; + if (condition) + size_overflow(); +} + void ClumpletWriter::toVaxInteger(UCHAR* ptr, FB_SIZE_T length, const SINT64 value) { fb_assert(ptr && length > 0 && length < 9); // We can't handle numbers bigger than int64. @@ -362,9 +406,7 @@ void ClumpletWriter::insertBytesLengthCheck(UCHAR tag, const void* bytes, const } // Check that resulting data doesn't overflow size limit - if (dynamic_buffer.getCount() + length + lenSize + 1 > sizeLimit) { - size_overflow(); - } + size_overflow(dynamic_buffer.getCount() + length + lenSize + 1 > sizeLimit); // Insert the data const FB_SIZE_T saved_offset = cur_offset; @@ -415,9 +457,7 @@ void ClumpletWriter::insertEndMarker(UCHAR tag) } // Check that resulting data doesn't overflow size limit - if (cur_offset + 1 > sizeLimit) { - size_overflow(); - } + size_overflow(cur_offset + 1 > sizeLimit); dynamic_buffer.shrink(cur_offset); dynamic_buffer.push(tag); diff --git a/src/common/classes/ClumpletWriter.h b/src/common/classes/ClumpletWriter.h index cd33b21bba..ddda851ad7 100644 --- a/src/common/classes/ClumpletWriter.h +++ b/src/common/classes/ClumpletWriter.h @@ -98,10 +98,14 @@ public: // Returns true if any found bool deleteWithTag(UCHAR tag); - virtual const UCHAR* getBuffer() const; + const UCHAR* getBuffer() const override; + bool hasOverflow() const + { + return flag_overflow; + } protected: - virtual const UCHAR* getBufferEnd() const; + const UCHAR* getBufferEnd() const override; virtual void size_overflow(); void insertBytesLengthCheck(UCHAR tag, const void* bytes, const FB_SIZE_T length); bool upgradeVersion(); // upgrade clumplet version - obtain newest from kindList @@ -117,14 +121,10 @@ private: void initNewBuffer(UCHAR tag); void create(const UCHAR* buffer, FB_SIZE_T buffLen, UCHAR tag); static void toVaxInteger(UCHAR* ptr, FB_SIZE_T length, const SINT64 value); + + void size_overflow(bool condition); + bool flag_overflow; }; -/* -template <> -void ClumpletWriter::insertString(UCHAR tag, const char*& str) -{ - insertString(tag, str, strlen(str)); -} -*/ } // namespace Firebird diff --git a/src/dsql/DsqlBatch.cpp b/src/dsql/DsqlBatch.cpp index 2368d58c6c..2273b125d3 100644 --- a/src/dsql/DsqlBatch.cpp +++ b/src/dsql/DsqlBatch.cpp @@ -30,7 +30,7 @@ #include "../jrd/exe_proto.h" #include "../dsql/dsql.h" #include "../dsql/errd_proto.h" -#include "../common/classes/ClumpletReader.h" +#include "../common/classes/ClumpletWriter.h" #include "../common/classes/auto.h" #include "../common/classes/fb_string.h" #include "../common/utils_proto.h" @@ -966,6 +966,14 @@ ULONG DsqlBatch::DataCache::getSize() const return m_used + m_cache.getCount(); } +ULONG DsqlBatch::DataCache::getCapacity() const +{ + if (!m_cacheCapacity) + return 0; + + return m_limit; +} + void DsqlBatch::DataCache::clear() { m_cache.clear(); @@ -973,3 +981,99 @@ void DsqlBatch::DataCache::clear() m_space->releaseSpace(0, m_used); m_used = m_got = m_shift = 0; } + +void DsqlBatch::info(thread_db* tdbb, unsigned int itemsLength, const unsigned char* items, + unsigned int bufferLength, unsigned char* buffer) +{ + // Sanity check + if (bufferLength < 3) // bigger values will be processed by later code OK + { + if (bufferLength-- > 0) + { + *buffer++ = isc_info_truncated; + if (bufferLength-- > 0) + *buffer++ = isc_info_end; + } + return; + } + + ClumpletReader it(ClumpletReader::InfoItems, items, itemsLength); + ClumpletWriter out(ClumpletReader::InfoResponse, bufferLength - 1); // place for isc_info_truncated / isc_info_end + enum BufCloseState {BUF_OPEN, BUF_INTERNAL, BUF_END}; + BufCloseState closeOut = BUF_OPEN; + + try + { + bool flInfoLength = false; + + for (it.rewind(); !it.isEof(); it.moveNext()) + { + UCHAR item = it.getClumpTag(); + if (item == isc_info_end) + break; + + switch(item) + { + case IBatch::INF_BUFFER_BYTES_SIZE: + out.insertInt(item, m_messages.getCapacity()); + break; + case IBatch::INF_DATA_BYTES_SIZE: + out.insertInt(item, FB_ALIGN(m_messages.getSize(), m_alignment)); + break; + case IBatch::INF_BLOBS_BYTES_SIZE: + if (m_blobs.getSize()) + out.insertInt(item, m_blobs.getSize()); + break; + case IBatch::INF_BLOB_ALIGNMENT: + out.insertInt(item, BLOB_STREAM_ALIGN); + break; + case isc_info_length: + flInfoLength = true; + break; + default: + out.insertInt(isc_info_error, isc_infunk); + break; + } + } + + // finalize writer + closeOut = BUF_INTERNAL; // finished adding internal info + out.insertTag(isc_info_end); + closeOut = BUF_END; // alreayd marked with isc_info_end but misses isc_info_length + if (flInfoLength) + { + out.rewind(); + out.insertInt(isc_info_length, out.getBufferLength()); + } + } + catch(const fatal_exception&) + { + // here it's sooner of all caused by writer overflow but carefully check that + if (out.hasOverflow()) + { + memcpy(buffer, out.getBuffer(), out.getBufferLength()); + buffer += out.getBufferLength(); + switch (closeOut) + { + case BUF_OPEN: + *buffer++ = isc_info_truncated; + if (out.getBufferLength() <= bufferLength - 2) + *buffer++ = isc_info_end; + break; + case BUF_INTERNAL: + // overflow adding isc_info_end, but we actually have 1 reserved byte + *buffer++ = isc_info_end; + break; + case BUF_END: + // ignore isc_info_length + break; + } + return; + } + else + throw; + } + + memcpy(buffer, out.getBuffer(), out.getBufferLength()); +} + diff --git a/src/dsql/DsqlBatch.h b/src/dsql/DsqlBatch.h index aafc207dde..187fc34e4b 100644 --- a/src/dsql/DsqlBatch.h +++ b/src/dsql/DsqlBatch.h @@ -76,6 +76,8 @@ public: Firebird::IMessageMetadata* getMetadata(thread_db* tdbb); void cancel(thread_db* tdbb); void setDefaultBpb(thread_db* tdbb, unsigned parLength, const unsigned char* par); + void info(thread_db* tdbb, unsigned int itemsLength, const unsigned char* items, + unsigned int bufferLength, unsigned char* buffer); // Additional flags - start from the maximum one static const UCHAR FLAG_DEFAULT_SEGMENTED = 31; @@ -121,6 +123,7 @@ private: ULONG reget(ULONG size, UCHAR** buffer, ULONG alignment); void remained(ULONG size, ULONG alignment = 0); ULONG getSize() const; + ULONG getCapacity() const; void clear(); private: diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index 31c06a3705..5f362601d2 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -530,6 +530,11 @@ interface Batch : ReferenceCounted const uint BLOB_SEGHDR_ALIGN = 2; // Alignment of segment header in the stream + const uchar INF_BUFFER_BYTES_SIZE = 10; // Maximum possible buffer size + const uchar INF_DATA_BYTES_SIZE = 11; // Already added messages size + const uchar INF_BLOBS_BYTES_SIZE = 12; // Already added blobs size + const uchar INF_BLOB_ALIGNMENT = 13; // Duplicate getBlobAlignment + void add(Status status, uint count, const void* inBuffer); void addBlob(Status status, uint length, const void* inBuffer, ISC_QUAD* blobId, uint parLength, const uchar* par); void appendBlobData(Status status, uint length, const void* inBuffer); @@ -545,6 +550,9 @@ interface Batch : ReferenceCounted version: // 4.0.0 => 4.0.1 [notImplementedAction if ::FB_UsedInYValve then defaultAction else call deprecatedClose(status) endif] void close(Status status); + void getInfo(Status status, + uint itemsLength, const uchar* items, + uint bufferLength, uchar* buffer); } interface BatchCompletionState : Disposable @@ -1203,6 +1211,8 @@ interface XpbBuilder : Disposable const uint SPB_SEND = 7; const uint SPB_RECEIVE = 8; const uint SPB_RESPONSE = 9; + const uint INFO_SEND = 10; + const uint INFO_RESPONSE = 11; // removing data void clear(Status status); diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index e97cda329a..cec3798bfe 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -1999,6 +1999,7 @@ namespace Firebird void (CLOOP_CARG *setDefaultBpb)(IBatch* self, IStatus* status, unsigned parLength, const unsigned char* par) throw(); void (CLOOP_CARG *deprecatedClose)(IBatch* self, IStatus* status) throw(); void (CLOOP_CARG *close)(IBatch* self, IStatus* status) throw(); + void (CLOOP_CARG *getInfo)(IBatch* self, IStatus* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) throw(); }; protected: @@ -2025,6 +2026,10 @@ namespace Firebird static const unsigned char BLOB_ID_USER = 2; static const unsigned char BLOB_STREAM = 3; static const unsigned BLOB_SEGHDR_ALIGN = 2; + static const unsigned char INF_BUFFER_BYTES_SIZE = 10; + static const unsigned char INF_DATA_BYTES_SIZE = 11; + static const unsigned char INF_BLOBS_BYTES_SIZE = 12; + static const unsigned char INF_BLOB_ALIGNMENT = 13; template void add(StatusType* status, unsigned count, const void* inBuffer) { @@ -2123,6 +2128,19 @@ namespace Firebird static_cast(this->cloopVTable)->close(this, status); StatusType::checkException(status); } + + template 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(this->cloopVTable)->getInfo(this, status, itemsLength, items, bufferLength, buffer); + StatusType::checkException(status); + } }; class IBatchCompletionState : public IDisposable @@ -4755,6 +4773,8 @@ namespace Firebird static const unsigned SPB_SEND = 7; static const unsigned SPB_RECEIVE = 8; static const unsigned SPB_RESPONSE = 9; + static const unsigned INFO_SEND = 10; + static const unsigned INFO_RESPONSE = 11; template void clear(StatusType* status) { @@ -10173,6 +10193,7 @@ namespace Firebird this->setDefaultBpb = &Name::cloopsetDefaultBpbDispatcher; this->deprecatedClose = &Name::cloopdeprecatedCloseDispatcher; this->close = &Name::cloopcloseDispatcher; + this->getInfo = &Name::cloopgetInfoDispatcher; } } vTable; @@ -10350,6 +10371,20 @@ namespace Firebird } } + static void CLOOP_CARG cloopgetInfoDispatcher(IBatch* self, IStatus* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) throw() + { + StatusType status2(status); + + try + { + static_cast(self)->Name::getInfo(&status2, itemsLength, items, bufferLength, buffer); + } + catch (...) + { + StatusType::catchException(&status2); + } + } + static void CLOOP_CARG cloopaddRefDispatcher(IReferenceCounted* self) throw() { try @@ -10401,6 +10436,7 @@ namespace Firebird virtual void setDefaultBpb(StatusType* status, unsigned parLength, const unsigned char* par) = 0; virtual void deprecatedClose(StatusType* status) = 0; virtual void close(StatusType* status) = 0; + virtual void getInfo(StatusType* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) = 0; }; template diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 100739fe39..e93f210087 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -366,6 +366,7 @@ type IBatch_setDefaultBpbPtr = procedure(this: IBatch; status: IStatus; parLength: Cardinal; par: BytePtr); cdecl; IBatch_deprecatedClosePtr = procedure(this: IBatch; status: IStatus); cdecl; IBatch_closePtr = procedure(this: IBatch; status: IStatus); cdecl; + IBatch_getInfoPtr = procedure(this: IBatch; status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr); cdecl; IBatchCompletionState_getSizePtr = function(this: IBatchCompletionState; status: IStatus): Cardinal; cdecl; IBatchCompletionState_getStatePtr = function(this: IBatchCompletionState; status: IStatus; pos: Cardinal): Integer; cdecl; IBatchCompletionState_findErrorPtr = function(this: IBatchCompletionState; status: IStatus; pos: Cardinal): Cardinal; cdecl; @@ -1548,6 +1549,7 @@ type setDefaultBpb: IBatch_setDefaultBpbPtr; deprecatedClose: IBatch_deprecatedClosePtr; close: IBatch_closePtr; + getInfo: IBatch_getInfoPtr; end; IBatch = class(IReferenceCounted) @@ -1563,6 +1565,10 @@ type const BLOB_ID_USER = Byte(2); const BLOB_STREAM = Byte(3); const BLOB_SEGHDR_ALIGN = Cardinal(2); + const INF_BUFFER_BYTES_SIZE = Byte(10); + const INF_DATA_BYTES_SIZE = Byte(11); + const INF_BLOBS_BYTES_SIZE = Byte(12); + const INF_BLOB_ALIGNMENT = Byte(13); procedure add(status: IStatus; count: Cardinal; inBuffer: Pointer); procedure addBlob(status: IStatus; length: Cardinal; inBuffer: Pointer; blobId: ISC_QUADPtr; parLength: Cardinal; par: BytePtr); @@ -1576,6 +1582,7 @@ type procedure setDefaultBpb(status: IStatus; parLength: Cardinal; par: BytePtr); procedure deprecatedClose(status: IStatus); procedure close(status: IStatus); + procedure getInfo(status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr); end; IBatchImpl = class(IBatch) @@ -1595,6 +1602,7 @@ type procedure setDefaultBpb(status: IStatus; parLength: Cardinal; par: BytePtr); virtual; abstract; procedure deprecatedClose(status: IStatus); virtual; abstract; procedure close(status: IStatus); virtual; abstract; + procedure getInfo(status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr); virtual; abstract; end; BatchCompletionStateVTable = class(DisposableVTable) @@ -2798,6 +2806,8 @@ type const SPB_SEND = Cardinal(7); const SPB_RECEIVE = Cardinal(8); const SPB_RESPONSE = Cardinal(9); + const INFO_SEND = Cardinal(10); + const INFO_RESPONSE = Cardinal(11); procedure clear(status: IStatus); procedure removeCurrent(status: IStatus); @@ -6685,6 +6695,17 @@ begin FbException.checkException(status); end; +procedure IBatch.getInfo(status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr); +begin + if (vTable.version < 4) then begin + FbException.setVersionError(status, 'IBatch', vTable.version, 4); + end + else begin + BatchVTable(vTable).getInfo(Self, status, itemsLength, items, bufferLength, buffer); + end; + FbException.checkException(status); +end; + function IBatchCompletionState.getSize(status: IStatus): Cardinal; begin Result := BatchCompletionStateVTable(vTable).getSize(Self, status); @@ -10704,6 +10725,15 @@ begin end end; +procedure IBatchImpl_getInfoDispatcher(this: IBatch; status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr); cdecl; +begin + try + IBatchImpl(this).getInfo(status, itemsLength, items, bufferLength, buffer); + except + on e: Exception do FbException.catchException(status, e); + end +end; + var IBatchImpl_vTable: BatchVTable; @@ -15637,6 +15667,7 @@ initialization IBatchImpl_vTable.setDefaultBpb := @IBatchImpl_setDefaultBpbDispatcher; IBatchImpl_vTable.deprecatedClose := @IBatchImpl_deprecatedCloseDispatcher; IBatchImpl_vTable.close := @IBatchImpl_closeDispatcher; + IBatchImpl_vTable.getInfo := @IBatchImpl_getInfoDispatcher; IBatchCompletionStateImpl_vTable := BatchCompletionStateVTable.create; IBatchCompletionStateImpl_vTable.version := 3; diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index 6fe682d9fc..7e183b8c34 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -882,7 +882,7 @@ Data source : @4"}, /* eds_statement */ {335545175, "Segment size (@1) should not exceed 65535 (64K - 1) when using segmented blob"}, /* big_segment */ {335545176, "Invalid blob policy in the batch for @1() call"}, /* batch_policy */ {335545177, "Can't change default BPB after adding any data to batch"}, /* batch_defbpb */ - {335545178, "Unexpected info buffer structure querying for default blob alignment"}, /* batch_align */ + {335545178, "Unexpected info buffer structure querying for server batch parameters"}, /* batch_align */ {335545179, "Duplicated segment @1 in multisegment connect block parameter"}, /* multi_segment_dup */ {335545180, "Plugin not supported by network protocol"}, /* non_plugin_protocol */ {335545181, "Error parsing message format"}, /* message_format */ diff --git a/src/jrd/EngineInterface.h b/src/jrd/EngineInterface.h index 96b2098c9b..14ae33186f 100644 --- a/src/jrd/EngineInterface.h +++ b/src/jrd/EngineInterface.h @@ -209,6 +209,8 @@ public: void setDefaultBpb(Firebird::CheckStatusWrapper* status, unsigned parLength, const unsigned char* par) override; void close(Firebird::CheckStatusWrapper* status) override; void deprecatedClose(Firebird::CheckStatusWrapper* status) override; + void getInfo(Firebird::CheckStatusWrapper* status, unsigned int itemsLength, const unsigned char* items, + unsigned int bufferLength, unsigned char* buffer) override; public: JBatch(DsqlBatch* handle, JStatement* aStatement, Firebird::IMessageMetadata* aMetadata); diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 901abe08e4..8026322a89 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -6330,6 +6330,48 @@ void JBatch::cancel(CheckStatusWrapper* status) } +void JBatch::getInfo(CheckStatusWrapper* user_status, + unsigned int itemsLength, const unsigned char* items, + unsigned int bufferLength, unsigned char* buffer) +{ +/************************************** + * + * g d s _ $ b l o b _ i n f o + * + ************************************** + * + * Functional description + * Provide information on blob object. + * + **************************************/ + try + { + EngineContextHolder tdbb(user_status, this, FB_FUNCTION); + check_database(tdbb); + + try + { + DsqlBatch* b = getHandle(); + b->info(tdbb, itemsLength, items, bufferLength, buffer); + } + catch (const Exception& ex) + { + transliterateException(tdbb, ex, user_status, "JBatch::getInfo"); + return; + } + } + catch (const Exception& ex) + { + ex.stuffException(user_status); + return; + } + + successful_completion(user_status); +} + + + + JReplicator::JReplicator(Applier* appl, StableAttachmentPart* sa) : applier(appl), sAtt(sa) { } diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index d4f145d39f..f2aabfa8c8 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -965,7 +965,7 @@ Data source : @4', NULL, NULL) ('big_segment', NULL, NULL, NULL, 0, 855, NULL, 'Segment size (@1) should not exceed 65535 (64K - 1) when using segmented blob', NULL, NULL); ('batch_policy', NULL, NULL, NULL, 0, 856, NULL, 'Invalid blob policy in the batch for @1() call', NULL, NULL); ('batch_defbpb', NULL, NULL, NULL, 0, 857, NULL, 'Can''t change default BPB after adding any data to batch', NULL, NULL); -('batch_align', NULL, 'interface.cpp', NULL, 0, 858, NULL, 'Unexpected info buffer structure querying for default blob alignment', NULL, NULL); +('batch_align', NULL, 'interface.cpp', NULL, 0, 858, NULL, 'Unexpected info buffer structure querying for server batch parameters', NULL, NULL); ('multi_segment_dup', 'getMultiPartConnectParameter', 'server.cpp', NULL, 0, 859, NULL, 'Duplicated segment @1 in multisegment connect block parameter', NULL, NULL); ('non_plugin_protocol', NULL, 'server.cpp', NULL, 0, 860, NULL, 'Plugin not supported by network protocol', NULL, NULL); ('message_format', NULL, 'server.cpp', NULL, 0, 861, NULL, 'Error parsing message format', NULL, NULL); diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index 6714918dc8..dc08a8678f 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -349,11 +349,14 @@ public: Firebird::IMessageMetadata* getMetadata(Firebird::CheckStatusWrapper* status) override; void close(Firebird::CheckStatusWrapper* status) override; void deprecatedClose(Firebird::CheckStatusWrapper* status) override; + void getInfo(CheckStatusWrapper* status, + unsigned int itemsLength, const unsigned char* items, + unsigned int bufferLength, unsigned char* buffer) override; private: void freeClientData(CheckStatusWrapper* status, bool force = false); void releaseStatement(); - void setBlobAlignment(); + void setServerInfo(); void cleanup() { @@ -411,7 +414,7 @@ private: // working with blob stream buffer void newBlob() { - setBlobAlignment(); + setServerInfo(); alignBlobBuffer(blobAlign); fb_assert(blobStream - blobStreamBuffer <= blobBufferSize); @@ -513,7 +516,7 @@ private: { if (blobPolicy != BLOB_NONE) { - setBlobAlignment(); + setServerInfo(); alignBlobBuffer(blobAlign); ULONG size = blobStream - blobStreamBuffer; if (size) @@ -530,6 +533,7 @@ private: } batchActive = false; + blobCount = messageCount = 0; } void sendBlobPacket(unsigned size, const UCHAR* ptr, bool flash); @@ -549,6 +553,8 @@ private: UCHAR blobPolicy; bool segmented, defSegmented, batchActive; + ULONG messageCount, blobCount, serverSize; + public: bool tmpStatement; }; @@ -2368,7 +2374,8 @@ Batch::Batch(Statement* s, IMessageMetadata* inFmt, unsigned parLength, const un : messageStream(0), blobStream(nullptr), sizePointer(nullptr), messageSize(0), alignedSize(0), blobBufferSize(0), messageBufferSize(0), flags(0), stmt(s), format(inFmt), blobAlign(0), blobPolicy(BLOB_NONE), - segmented(false), defSegmented(false), batchActive(false), tmpStatement(false) + segmented(false), defSegmented(false), batchActive(false), + messageCount(0), blobCount(0), serverSize(0), tmpStatement(false) { LocalStatus ls; CheckStatusWrapper st(&ls); @@ -2484,6 +2491,7 @@ void Batch::sendMessagePacket(unsigned count, const UCHAR* ptr, bool flash) statement->rsr_batch_size = alignedSize; sendDeferredPacket(port, packet, flash); + messageCount += count; } @@ -2616,7 +2624,7 @@ void Batch::sendBlobPacket(unsigned size, const UCHAR* ptr, bool flash) Rdb* rdb = statement->rsr_rdb; rem_port* port = rdb->rdb_port; - setBlobAlignment(); + setServerInfo(); fb_assert(!(size % blobAlign)); PACKET* packet = &rdb->rdb_packet; @@ -2627,6 +2635,8 @@ void Batch::sendBlobPacket(unsigned size, const UCHAR* ptr, bool flash) batch->p_batch_blob_data.cstr_length = size; sendDeferredPacket(port, packet, flash); + + blobCount += size; } @@ -2698,7 +2708,7 @@ unsigned Batch::getBlobAlignment(CheckStatusWrapper* status) { try { - setBlobAlignment(); + setServerInfo(); } catch (const Exception& ex) { @@ -2709,7 +2719,7 @@ unsigned Batch::getBlobAlignment(CheckStatusWrapper* status) } -void Batch::setBlobAlignment() +void Batch::setServerInfo() { if (blobAlign) return; @@ -2727,21 +2737,68 @@ void Batch::setBlobAlignment() rem_port* port = rdb->rdb_port; RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION); - // Perform info call to server LocalStatus ls; CheckStatusWrapper s(&ls); - UCHAR item = isc_info_sql_stmt_blob_align; - UCHAR buffer[16]; - info(&s, rdb, op_info_sql, statement->rsr_id, 0, - 1, &item, 0, 0, sizeof(buffer), buffer); + + if (port->port_protocol < PROTOCOL_VERSION17) + { + UCHAR item = isc_info_sql_stmt_blob_align; + UCHAR buffer[16]; + info(&s, rdb, op_info_sql, statement->rsr_id, 0, + 1, &item, 0, 0, sizeof(buffer), buffer); + check(&s); + + // Extract from buffer + if (buffer[0] != item) + Arg::Gds(isc_batch_align).raise(); + + int len = gds__vax_integer(&buffer[1], 2); + statement->rsr_batch_stream.alignment = blobAlign = gds__vax_integer(&buffer[3], len); + + if (!blobAlign) + Arg::Gds(isc_batch_align).raise(); + + return; + } + + // Perform info call to server + UCHAR items[] = {IBatch::INF_BLOB_ALIGNMENT, IBatch::INF_BUFFER_BYTES_SIZE}; + UCHAR buffer[32]; + info(&s, rdb, op_info_batch, statement->rsr_id, 0, + sizeof(items), items, 0, 0, sizeof(buffer), buffer); check(&s); // Extract from buffer - if (buffer[0] != item) - Arg::Gds(isc_batch_align).raise(); + ClumpletReader out(ClumpletReader::InfoResponse, buffer, sizeof(buffer)); + for (out.rewind(); !out.isEof(); out.moveNext()) + { + UCHAR item = out.getClumpTag(); + if (item == isc_info_end) + break; - int len = gds__vax_integer(&buffer[1], 2); - statement->rsr_batch_stream.alignment = blobAlign = gds__vax_integer(&buffer[3], len); + switch(item) + { + case IBatch::INF_BLOB_ALIGNMENT: + statement->rsr_batch_stream.alignment = blobAlign = out.getInt(); + break; + case IBatch::INF_BUFFER_BYTES_SIZE: + serverSize = out.getInt(); + break; + case isc_info_error: + (Arg::Gds(isc_batch_align) << Arg::Gds(out.getInt())).raise(); + case isc_info_truncated: + (Arg::Gds(isc_batch_align) << Arg::Gds(isc_random) << "truncated").raise(); + default: + { + string msg; + msg.printf("Wrong info item %u", item); + (Arg::Gds(isc_batch_align) << Arg::Gds(isc_random) << msg).raise(); + } + } + } + + if (! (blobAlign && serverSize)) + Arg::Gds(isc_batch_align).raise(); } @@ -2960,6 +3017,73 @@ void Batch::close(CheckStatusWrapper* status) } +void Batch::getInfo(CheckStatusWrapper* status, unsigned int itemsLength, const unsigned char* items, + unsigned int bufferLength, unsigned char* buffer) +{ + try + { + ClumpletReader it(ClumpletReader::InfoItems, items, itemsLength); + ClumpletWriter out(ClumpletReader::InfoResponse, bufferLength - 1); // place for isc_info_end / isc_info_truncated + + for (it.rewind(); !it.isEof(); it.moveNext()) + { + UCHAR item = it.getClumpTag(); + if (item == isc_info_end) + break; + + try + { + switch(item) + { + case IBatch::INF_BUFFER_BYTES_SIZE: + setServerInfo(); + if (serverSize) + out.insertInt(item, serverSize); + break; + case IBatch::INF_DATA_BYTES_SIZE: + out.insertInt(item, (messageCount + messageStream) * alignedSize); + break; + case IBatch::INF_BLOBS_BYTES_SIZE: + if (blobStream) + out.insertInt(item, blobCount + (blobStream - blobStreamBuffer)); + break; + case IBatch::INF_BLOB_ALIGNMENT: + setServerInfo(); + out.insertInt(item, blobAlign); + break; + default: + out.insertInt(isc_info_error, isc_infunk); + break; + } + } + catch(const fatal_exception&) + { + // here it's sooner of all caused by writer overflow but anyway check that + if (out.hasOverflow()) + { + memcpy(buffer, out.getBuffer(), out.getBufferLength()); + buffer += out.getBufferLength(); + *buffer++ = isc_info_truncated; + if (out.getBufferLength() <= bufferLength - 2) + *buffer++ = isc_info_end; + return; + } + else + throw; + } + } + + memcpy(buffer, out.getBuffer(), out.getBufferLength()); + buffer += out.getBufferLength(); + *buffer++ = isc_info_end; + } + catch (const Exception& ex) + { + ex.stuffException(status); + } +} + + void Batch::releaseStatement() { if (tmpStatement) diff --git a/src/remote/protocol.cpp b/src/remote/protocol.cpp index 3d23237023..97fae903c0 100644 --- a/src/remote/protocol.cpp +++ b/src/remote/protocol.cpp @@ -504,6 +504,7 @@ bool_t xdr_protocol(RemoteXdr* xdrs, PACKET* p) case op_info_transaction: case op_service_info: case op_info_sql: + case op_info_batch: info = &p->p_info; MAP(xdr_short, reinterpret_cast(info->p_info_object)); MAP(xdr_short, reinterpret_cast(info->p_info_incarnation)); diff --git a/src/remote/protocol.h b/src/remote/protocol.h index e897306aa7..9d44191b73 100644 --- a/src/remote/protocol.h +++ b/src/remote/protocol.h @@ -94,7 +94,7 @@ const USHORT PROTOCOL_VERSION16 = (FB_PROTOCOL_FLAG | 16); const USHORT PROTOCOL_STMT_TOUT = PROTOCOL_VERSION16; // Protocol 17: -// - supports op_batch_sync +// - supports op_batch_sync, op_info_batch const USHORT PROTOCOL_VERSION17 = (FB_PROTOCOL_FLAG | 17); @@ -295,6 +295,7 @@ enum P_OP op_batch_cancel = 109, op_batch_sync = 110, + op_info_batch = 111, op_max }; diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index 3d3f2a8905..098797dff3 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -4431,6 +4431,15 @@ void rem_port::info(P_OP op, P_INFO* stuff, PACKET* sendL) statement->rsr_iface->getInfo(&status_vector, info_len, info_buffer, buffer_length, buffer); break; + + case op_info_batch: + getHandle(statement, stuff->p_info_object); + statement->checkIface(); + statement->checkBatch(); + + statement->rsr_batch->getInfo(&status_vector, info_len, info_buffer, + buffer_length, buffer); + break; } // Send a response that includes the segment. @@ -4911,6 +4920,7 @@ static bool process_packet(rem_port* port, PACKET* sendL, PACKET* receive, rem_p case op_info_transaction: case op_service_info: case op_info_sql: + case op_info_batch: port->info(op, &receive->p_info, sendL); break; diff --git a/src/yvalve/YObjects.h b/src/yvalve/YObjects.h index 31e1a467a3..efddb0f942 100644 --- a/src/yvalve/YObjects.h +++ b/src/yvalve/YObjects.h @@ -397,6 +397,8 @@ public: void setDefaultBpb(Firebird::CheckStatusWrapper* status, unsigned parLength, const unsigned char* par); void close(Firebird::CheckStatusWrapper* status); void deprecatedClose(Firebird::CheckStatusWrapper* status); + void getInfo(Firebird::CheckStatusWrapper* status, unsigned int itemsLength, const unsigned char* items, + unsigned int bufferLength, unsigned char* buffer); public: AtomicAttPtr attachment; diff --git a/src/yvalve/utl.cpp b/src/yvalve/utl.cpp index 3f09935829..0a12ae1915 100644 --- a/src/yvalve/utl.cpp +++ b/src/yvalve/utl.cpp @@ -916,8 +916,14 @@ public: case SPB_RESPONSE: k = ClumpletReader::SpbResponse; break; + case INFO_SEND: + k = ClumpletReader::InfoItems; + break; + case INFO_RESPONSE: + k = ClumpletReader::InfoResponse; + break; default: - fatal_exception::raiseFmt("Wrong parameters block kind %d, should be from %d to %d", kind, DPB, SPB_RESPONSE); + fatal_exception::raiseFmt("Wrong parameters block kind %d, should be from %d to %d", kind, DPB, INFO_RESPONSE); break; } diff --git a/src/yvalve/why.cpp b/src/yvalve/why.cpp index f720597479..bb83cdaefc 100644 --- a/src/yvalve/why.cpp +++ b/src/yvalve/why.cpp @@ -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 entry(status, this); + + entry.next()->getInfo(status, itemsLength, items, bufferLength, buffer); + } + catch (const Exception& e) + { + e.stuffException(status); + } +} + + //-------------------------------------