8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 20:43:02 +01:00

Merge pull request #7083 from FirebirdSQL/cursor-info

ResultSet::getInfo() implementation
This commit is contained in:
Dmitry Yemanov 2022-04-23 09:15:28 +03:00 committed by GitHub
commit 44b76cc2ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 282 additions and 29 deletions

View File

@ -1796,15 +1796,15 @@ interface replaces isc_db_handle:</font></p>
cursor (analogue of isc_dsql_set_cursor_name()). Parameter
cursorFlags is needed to open bidirectional cursor setting it's
value to Istatement::CURSOR_TYPE_SCROLLABLE.</font></p>
<li><p><font size="4" style="font-size: 14pt">I</font><font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">Batch*
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IBatch*
createBatch(StatusType* status, ITransaction* transaction, unsigned
stmtLength, const char* sqlStmt, unsigned dialect, IMessageMetadata*
inMetadata, unsigned</font></font> <font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">parLength,
inMetadata, unsigned parLength,
const unsigned char* par) prepares sqlStmt and creates <a href="#Batch">Batch</a>
interface ready to accept multiple sets of input parameters in
inMetadata format. Leaving inMetadata</font></font> <font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">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.</font></font></p>
passed to createBatch() making it possible to adjust batch behavior.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IEvents*
queEvents(StatusType* status, IEventCallback* callback, unsigned
length, const unsigned char* events) replaces isc_que_events()
@ -1951,7 +1951,6 @@ possible buffer size (one set by TAG_BUFFER_BYTES_SIZE)</font></p>
<a href="#Batch">Batch</a> interface. It contains more or less
(depending upon parameters passed when <a href="#Batch">Batch</a> was
created) detailed information about the results of batch execution.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">{</font></p>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">uint
getSize(StatusType* status) returns the total number of
@ -2377,14 +2376,13 @@ with execution of SQL statements.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getMessageLength(StatusType* status) - returns length of message
buffer (use it to allocate memory for the buffer).</font></p>
<li><p><font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">unsigned
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getAlignment(StatusType* status) returns alignment required for
message buffer.</font></font></p>
<li><p><font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">unsigned</font></font>
<font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">getAlignedLength(StatusType*
status) returns length of message buffer taking into an account
message buffer.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">unsigned
getAlignedLength(StatusType* status) returns length of message buffer taking into an account
alignment requirements (use it to allocate memory for an array of
buffers and navigate through that array).</font></font></p>
buffers and navigate through that array).</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
@ -2426,18 +2424,18 @@ message or construct metadata from the beginning.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IMessageMetadata*
getMetadata(StatusType* status) get <a href="#10. MessageMetadata">MessageMetadata</a>
interface built by this builder.</font></p>
<li><p style="margin-bottom: 0cm"><font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">void
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setField(StatusType* status, uint index, const string field) set
name of a field / column.</font></font></p>
<li><p style="margin-bottom: 0cm"><font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">void
name of a field / column.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setRelation(StatusType* status, uint index, const string relation)
set name of the relation from which the field was selected.</font></font></p>
<li><p style="margin-bottom: 0cm"><font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">void
set name of the relation from which the field was selected.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setOwner(StatusType* status, uint index, const string owner) set
name of that relation owner.</font></font></p>
<li><p style="margin-bottom: 0cm"><font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">void
name of that relation owner.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setAlias(StatusType* status, uint index, const string alias) set
alias name of the field in related statement.</font></font></p>
alias name of the field in related statement.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
@ -2639,6 +2637,11 @@ of isc_stmt_handle. This interface is returned by openCursor() call
in <a href="#Attachment">IAttachment</a> or <a href="#Statement">IStatement</a>.
All fetch calls except fetchNext() work only for bidirectional
(opened with CURSOR_TYPE_SCROLLABLE flag) result set.</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">Items
accepted in getInfo() call:</font></p>
<p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">INF_RECORD_COUNT
number of records stored inside a scrollable cursor, or -1 for a uni-directional cursor.</font></p>
<p style="margin-bottom: 0cm"><br/>
<ol>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">int
fetchNext(StatusType* status, void* message) fetch next record,
@ -2673,6 +2676,10 @@ All fetch calls except fetchNext() work only for bidirectional
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
close(StatusType* status) close result set, releases interface
on success.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
getInfo(StatusType* status, unsigned itemsLength, const unsigned
char* items, unsigned bufferLength, unsigned char* buffer)
retrieve information about result set.</font></p>
</ol>
<p style="margin-bottom: 0cm"><br/>
@ -2733,7 +2740,7 @@ interface replaces (partially) isc_stmt_handle.</font></p>
returning multiple rows of data. Partial analogue of
isc_dsql_execute2() - in and out XSLQDAs replaced with input and
output messages with appropriate buffers.</font></p>
<li><p style="margin-bottom: 0cm"><font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">IResultSet*
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IResultSet*
openCursor(StatusType* status, ITransaction* transaction,
IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata*
outMetadata, unsigned flags) executes SQL statement potentially
@ -2741,8 +2748,8 @@ interface replaces (partially) isc_stmt_handle.</font></p>
interface which should be used to fetch that data. Format of output
data is defined by outMetadata parameter, leaving it NULL default
format may be used. Parameter flags is needed to open bidirectional
cursor setting it's value to Istatement::CURSOR_TYPE_SCROLLABLE.</font></font></p>
<li><p style="margin-bottom: 0cm"><font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">IBatch*
cursor setting it's value to Istatement::CURSOR_TYPE_SCROLLABLE.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">IBatch*
createBatch(StatusType* status, IMessageMetadata* inMetadata, uint
parLength, const uchar* par) creates <a href="#Batch">Batch</a>
interface to SQL statement with input parameters making it possible
@ -2750,10 +2757,10 @@ interface replaces (partially) isc_stmt_handle.</font></p>
of input data is defined by inMetadata parameter, leaving it NULL
makes batch use default format from this interface. Parameters block
may be passed to createBatch() making it possible to adjust batch
behavior.</font></font></p>
<li><p style="margin-bottom: 0cm"><font face="Liberation Serif, serif"><font size="4" style="font-size: 14pt">void
behavior.</font></p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
setCursorName(StatusType* status, const char* name) replaces
isc_dsql_set_cursor_name(). </font></font>
isc_dsql_set_cursor_name().</font>
</p>
<li><p style="margin-bottom: 0cm"><font size="4" style="font-size: 14pt">void
free(StatusType* status) free statement, releases interface on

View File

@ -21,10 +21,12 @@
*/
#include "firebird.h"
#include "../common/classes/ClumpletWriter.h"
#include "../jrd/tra_proto.h"
#include "../jrd/trace/TraceManager.h"
#include "../jrd/trace/TraceDSQLHelpers.h"
#include "../dsql/dsql_proto.h"
#include "../dsql/DsqlCursor.h"
using namespace Firebird;
@ -210,6 +212,81 @@ int DsqlCursor::fetchRelative(thread_db* tdbb, UCHAR* buffer, SLONG offset)
return fetchFromCache(tdbb, buffer, position);
}
void DsqlCursor::getInfo(thread_db* tdbb,
unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer)
{
if (bufferLength < 7) // isc_info_error + 2-byte length + 4-byte error code
{
if (bufferLength)
*buffer = isc_info_truncated;
return;
}
const bool isScrollable = (m_flags & IStatement::CURSOR_TYPE_SCROLLABLE);
ClumpletWriter response(ClumpletReader::InfoResponse, bufferLength - 1); // isc_info_end
ISC_STATUS errorCode = 0;
bool needLength = false, completed = false;
try
{
ClumpletReader infoItems(ClumpletReader::InfoItems, items, itemsLength);
for (infoItems.rewind(); !errorCode && !infoItems.isEof(); infoItems.moveNext())
{
const auto tag = infoItems.getClumpTag();
switch (tag)
{
case isc_info_end:
break;
case isc_info_length:
needLength = true;
break;
case IResultSet::INF_RECORD_COUNT:
if (isScrollable && !m_eof)
{
cacheInput(tdbb);
fb_assert(m_eof);
}
response.insertInt(tag, isScrollable ? m_cachedCount : -1);
break;
default:
errorCode = isc_infunk;
break;
}
}
completed = infoItems.isEof();
if (needLength && completed)
{
response.rewind();
response.insertInt(isc_info_length, response.getBufferLength() + 1); // isc_info_end
}
}
catch (const Exception&)
{
if (!response.hasOverflow())
throw;
}
if (errorCode)
{
response.clear();
response.insertInt(isc_info_error, (SLONG) errorCode);
}
fb_assert(response.getBufferLength() <= bufferLength);
memcpy(buffer, response.getBuffer(), response.getBufferLength());
buffer += response.getBufferLength();
*buffer = completed ? isc_info_end : isc_info_truncated;
}
int DsqlCursor::fetchFromCache(thread_db* tdbb, UCHAR* buffer, FB_UINT64 position)
{
if (position >= m_cachedCount)

View File

@ -61,6 +61,10 @@ public:
return (m_state == EOS);
}
void getInfo(thread_db* tdbb,
unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer);
private:
int fetchFromCache(thread_db* tdbb, UCHAR* buffer, FB_UINT64 position);
bool cacheInput(thread_db* tdbb, FB_UINT64 position = MAX_UINT64);

View File

@ -433,6 +433,9 @@ version: // 3.0 => 4.0
interface ResultSet : ReferenceCounted
{
// Info items
const uchar INF_RECORD_COUNT = 10; // Number of records in the result set
[notImplemented(Status::RESULT_ERROR)] int fetchNext(Status status, void* message);
[notImplemented(Status::RESULT_ERROR)] int fetchPrior(Status status, void* message);
[notImplemented(Status::RESULT_ERROR)] int fetchFirst(Status status, void* message);
@ -452,6 +455,11 @@ interface ResultSet : ReferenceCounted
version: // 3.0.7 => 3.0.8, 4.0.0 => 4.0.1
[notImplementedAction if ::FB_UsedInYValve then defaultAction else call deprecatedClose(status) endif]
void close(Status status);
version: // 4.0.1 => 5.0
void getInfo(Status status,
uint itemsLength, const uchar* items,
uint bufferLength, uchar* buffer);
}
interface Statement : ReferenceCounted

View File

@ -1668,6 +1668,7 @@ namespace Firebird
void (CLOOP_CARG *deprecatedClose)(IResultSet* self, IStatus* status) throw();
void (CLOOP_CARG *setDelayedOutputFormat)(IResultSet* self, IStatus* status, IMessageMetadata* format) throw();
void (CLOOP_CARG *close)(IResultSet* self, IStatus* status) throw();
void (CLOOP_CARG *getInfo)(IResultSet* self, IStatus* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) throw();
};
protected:
@ -1681,7 +1682,9 @@ namespace Firebird
}
public:
static const unsigned VERSION = 4;
static const unsigned VERSION = 5;
static const unsigned char INF_RECORD_COUNT = 10;
template <typename StatusType> int fetchNext(StatusType* status, void* message)
{
@ -1786,6 +1789,19 @@ namespace Firebird
static_cast<VTable*>(this->cloopVTable)->close(this, status);
StatusType::checkException(status);
}
template <typename StatusType> void getInfo(StatusType* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer)
{
if (cloopVTable->version < 5)
{
StatusType::setVersionError(status, "IResultSet", cloopVTable->version, 5);
StatusType::checkException(status);
return;
}
StatusType::clearException(status);
static_cast<VTable*>(this->cloopVTable)->getInfo(this, status, itemsLength, items, bufferLength, buffer);
StatusType::checkException(status);
}
};
class IStatement : public IReferenceCounted
@ -9620,6 +9636,7 @@ namespace Firebird
this->deprecatedClose = &Name::cloopdeprecatedCloseDispatcher;
this->setDelayedOutputFormat = &Name::cloopsetDelayedOutputFormatDispatcher;
this->close = &Name::cloopcloseDispatcher;
this->getInfo = &Name::cloopgetInfoDispatcher;
}
} vTable;
@ -9803,6 +9820,20 @@ namespace Firebird
}
}
static void CLOOP_CARG cloopgetInfoDispatcher(IResultSet* self, IStatus* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) throw()
{
StatusType status2(status);
try
{
static_cast<Name*>(self)->Name::getInfo(&status2, itemsLength, items, bufferLength, buffer);
}
catch (...)
{
StatusType::catchException(&status2);
}
}
static void CLOOP_CARG cloopaddRefDispatcher(IReferenceCounted* self) throw()
{
try
@ -9854,6 +9885,7 @@ namespace Firebird
virtual void deprecatedClose(StatusType* status) = 0;
virtual void setDelayedOutputFormat(StatusType* status, IMessageMetadata* format) = 0;
virtual void close(StatusType* status) = 0;
virtual void getInfo(StatusType* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) = 0;
};
template <typename Name, typename StatusType, typename Base>

View File

@ -339,6 +339,7 @@ type
IResultSet_deprecatedClosePtr = procedure(this: IResultSet; status: IStatus); cdecl;
IResultSet_setDelayedOutputFormatPtr = procedure(this: IResultSet; status: IStatus; format: IMessageMetadata); cdecl;
IResultSet_closePtr = procedure(this: IResultSet; status: IStatus); cdecl;
IResultSet_getInfoPtr = procedure(this: IResultSet; status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr); cdecl;
IStatement_getInfoPtr = procedure(this: IStatement; status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr); cdecl;
IStatement_getTypePtr = function(this: IStatement; status: IStatus): Cardinal; cdecl;
IStatement_getPlanPtr = function(this: IStatement; status: IStatus; detailed: Boolean): PAnsiChar; cdecl;
@ -1425,10 +1426,12 @@ type
deprecatedClose: IResultSet_deprecatedClosePtr;
setDelayedOutputFormat: IResultSet_setDelayedOutputFormatPtr;
close: IResultSet_closePtr;
getInfo: IResultSet_getInfoPtr;
end;
IResultSet = class(IReferenceCounted)
const VERSION = 4;
const VERSION = 5;
const INF_RECORD_COUNT = Byte(10);
function fetchNext(status: IStatus; message: Pointer): Integer;
function fetchPrior(status: IStatus; message: Pointer): Integer;
@ -1442,6 +1445,7 @@ type
procedure deprecatedClose(status: IStatus);
procedure setDelayedOutputFormat(status: IStatus; format: IMessageMetadata);
procedure close(status: IStatus);
procedure getInfo(status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr);
end;
IResultSetImpl = class(IResultSet)
@ -1461,6 +1465,7 @@ type
procedure deprecatedClose(status: IStatus); virtual; abstract;
procedure setDelayedOutputFormat(status: IStatus; format: IMessageMetadata); virtual; abstract;
procedure close(status: IStatus); virtual; abstract;
procedure getInfo(status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr); virtual; abstract;
end;
StatementVTable = class(ReferenceCountedVTable)
@ -6501,6 +6506,17 @@ begin
FbException.checkException(status);
end;
procedure IResultSet.getInfo(status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr);
begin
if (vTable.version < 5) then begin
FbException.setVersionError(status, 'IResultSet', vTable.version, 5);
end
else begin
ResultSetVTable(vTable).getInfo(Self, status, itemsLength, items, bufferLength, buffer);
end;
FbException.checkException(status);
end;
procedure IStatement.getInfo(status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr);
begin
StatementVTable(vTable).getInfo(Self, status, itemsLength, items, bufferLength, buffer);
@ -10435,6 +10451,15 @@ begin
end
end;
procedure IResultSetImpl_getInfoDispatcher(this: IResultSet; status: IStatus; itemsLength: Cardinal; items: BytePtr; bufferLength: Cardinal; buffer: BytePtr); cdecl;
begin
try
IResultSetImpl(this).getInfo(status, itemsLength, items, bufferLength, buffer);
except
on e: Exception do FbException.catchException(status, e);
end
end;
var
IResultSetImpl_vTable: ResultSetVTable;
@ -15620,7 +15645,7 @@ initialization
IMetadataBuilderImpl_vTable.setAlias := @IMetadataBuilderImpl_setAliasDispatcher;
IResultSetImpl_vTable := ResultSetVTable.create;
IResultSetImpl_vTable.version := 4;
IResultSetImpl_vTable.version := 5;
IResultSetImpl_vTable.addRef := @IResultSetImpl_addRefDispatcher;
IResultSetImpl_vTable.release := @IResultSetImpl_releaseDispatcher;
IResultSetImpl_vTable.fetchNext := @IResultSetImpl_fetchNextDispatcher;
@ -15635,6 +15660,7 @@ initialization
IResultSetImpl_vTable.deprecatedClose := @IResultSetImpl_deprecatedCloseDispatcher;
IResultSetImpl_vTable.setDelayedOutputFormat := @IResultSetImpl_setDelayedOutputFormatDispatcher;
IResultSetImpl_vTable.close := @IResultSetImpl_closeDispatcher;
IResultSetImpl_vTable.getInfo := @IResultSetImpl_getInfoDispatcher;
IStatementImpl_vTable := StatementVTable.create;
IStatementImpl_vTable.version := 5;

View File

@ -169,6 +169,9 @@ public:
void close(Firebird::CheckStatusWrapper* status) override;
void deprecatedClose(Firebird::CheckStatusWrapper* status) override;
void setDelayedOutputFormat(Firebird::CheckStatusWrapper* status, Firebird::IMessageMetadata* format) override;
void getInfo(Firebird::CheckStatusWrapper* status,
unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer) override;
public:
JResultSet(DsqlCursor* handle, JStatement* aStatement);

View File

@ -5471,6 +5471,34 @@ IMessageMetadata* JResultSet::getMetadata(CheckStatusWrapper* user_status)
}
void JResultSet::getInfo(CheckStatusWrapper* user_status,
unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer)
{
try
{
EngineContextHolder tdbb(user_status, this, FB_FUNCTION);
check_database(tdbb);
try
{
cursor->getInfo(tdbb, itemsLength, items, bufferLength, buffer);
}
catch (const Exception& ex)
{
transliterateException(tdbb, ex, user_status, "JResultSet::getInfo");
return;
}
}
catch (const Exception& ex)
{
ex.stuffException(user_status);
return;
}
successful_completion(user_status);
}
void JResultSet::deprecatedClose(CheckStatusWrapper* user_status)
{
freeEngineData(user_status);

View File

@ -292,6 +292,9 @@ public:
void close(CheckStatusWrapper* status) override;
void deprecatedClose(CheckStatusWrapper* status) override;
void setDelayedOutputFormat(CheckStatusWrapper* status, IMessageMetadata* format) override;
void getInfo(CheckStatusWrapper* status,
unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer) override;
ResultSet(Statement* s, IMessageMetadata* outFmt, unsigned f)
: stmt(s), flags(f), tmpStatement(false), delayedFormat(outFmt == DELAYED_OUT_FORMAT)
@ -5062,6 +5065,38 @@ IMessageMetadata* ResultSet::getMetadata(CheckStatusWrapper* status)
return outputFormat;
}
void ResultSet::getInfo(CheckStatusWrapper* status,
unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer)
{
try
{
reset(status);
// Check and validate handles, etc.
if (!stmt)
Arg::Gds(isc_dsql_cursor_err).raise();
const auto statement = stmt->getStatement();
CHECK_HANDLE(statement, isc_bad_req_handle);
const auto rdb = statement->rsr_rdb;
const auto port = rdb->rdb_port;
RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION);
if (port->port_protocol < PROTOCOL_FETCH_SCROLL)
unsupported();
info(status, rdb, op_info_cursor, statement->rsr_id, 0,
itemsLength, items, 0, 0, bufferLength, buffer);
}
catch (const Exception& ex)
{
ex.stuffException(status);
}
}
void ResultSet::freeClientData(CheckStatusWrapper* status, bool force)
{
/**************************************

View File

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

View File

@ -316,6 +316,7 @@ enum P_OP
op_info_batch = 111,
op_fetch_scroll = 112,
op_info_cursor = 113,
op_max
};

View File

@ -4594,7 +4594,7 @@ void rem_port::info(P_OP op, P_INFO* stuff, PACKET* sendL)
case op_info_sql:
getHandle(statement, stuff->p_info_object);
statement->checkIface(isc_info_unprepared_stmt);
statement->checkIface();
statement->rsr_iface->getInfo(&status_vector, info_len, info_buffer,
buffer_length, buffer);
@ -4608,6 +4608,18 @@ void rem_port::info(P_OP op, P_INFO* stuff, PACKET* sendL)
statement->rsr_batch->getInfo(&status_vector, info_len, info_buffer,
buffer_length, buffer);
break;
case op_info_cursor:
getHandle(statement, stuff->p_info_object);
statement->checkIface();
statement->checkCursor();
statement->rsr_cursor->getInfo(&status_vector, info_len, info_buffer,
buffer_length, buffer);
break;
default:
fb_assert(false);
}
// Send a response that includes the segment.
@ -5089,6 +5101,7 @@ static bool process_packet(rem_port* port, PACKET* sendL, PACKET* receive, rem_p
case op_service_info:
case op_info_sql:
case op_info_batch:
case op_info_cursor:
port->info(op, &receive->p_info, sendL);
break;

View File

@ -366,6 +366,9 @@ public:
void close(Firebird::CheckStatusWrapper* status);
void deprecatedClose(Firebird::CheckStatusWrapper* status);
void setDelayedOutputFormat(Firebird::CheckStatusWrapper* status, Firebird::IMessageMetadata* format);
void getInfo(Firebird::CheckStatusWrapper* status,
unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer);
public:
AtomicAttPtr attachment;

View File

@ -4884,6 +4884,21 @@ IMessageMetadata* YResultSet::getMetadata(CheckStatusWrapper* status)
return NULL;
}
void YResultSet::getInfo(CheckStatusWrapper* status,
unsigned int itemsLength, const unsigned char* items,
unsigned int bufferLength, unsigned char* buffer)
{
try
{
YEntry<YResultSet> entry(status, this);
entry.next()->getInfo(status, itemsLength, items, bufferLength, buffer);
}
catch (const Exception& e)
{
e.stuffException(status);
}
}
void YResultSet::close(CheckStatusWrapper* status)
{