From 94e1b8b7a338d3486cfa154677fc29db3b30b7ca Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Tue, 21 Dec 2021 11:02:58 +0300 Subject: [PATCH] Initial implementation of ResultSet::getInfo() --- doc/Using_OO_API.html | 59 +++++++++-------- src/dsql/DsqlCursor.cpp | 77 ++++++++++++++++++++++ src/dsql/DsqlCursor.h | 4 ++ src/include/firebird/FirebirdInterface.idl | 8 +++ src/include/firebird/IdlFbInterfaces.h | 34 +++++++++- src/include/gen/Firebird.pas | 30 ++++++++- src/jrd/EngineInterface.h | 3 + src/jrd/jrd.cpp | 28 ++++++++ src/remote/client/interface.cpp | 35 ++++++++++ src/remote/protocol.cpp | 1 + src/remote/protocol.h | 1 + src/remote/server/server.cpp | 15 ++++- src/yvalve/YObjects.h | 3 + src/yvalve/why.cpp | 15 +++++ 14 files changed, 283 insertions(+), 30 deletions(-) diff --git a/doc/Using_OO_API.html b/doc/Using_OO_API.html index b15ab1adf4..ba269991f9 100644 --- a/doc/Using_OO_API.html +++ b/doc/Using_OO_API.html @@ -1796,15 +1796,15 @@ interface – replaces isc_db_handle:

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.

-
  • IBatch* +

  • 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.

    + passed to createBatch() making it possible to adjust batch behavior.

  • 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)

    Batch interface. It contains more or less (depending upon parameters passed when Batch was created) detailed information about the results of batch execution.

    -

    {

    1. uint getSize(StatusType* status) – returns the total number of @@ -2377,14 +2376,13 @@ with execution of SQL statements.

    2. unsigned getMessageLength(StatusType* status) - returns length of message buffer (use it to allocate memory for the buffer).

      -
    3. unsigned +

    4. unsigned getAlignment(StatusType* status) – returns alignment required for - message buffer.

      -
    5. unsigned - getAlignedLength(StatusType* - status) – returns length of message buffer taking into an account + message buffer.

      +
    6. 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).

      + buffers and navigate through that array).


    @@ -2426,18 +2424,18 @@ message or construct metadata from the beginning.

  • IMessageMetadata* getMetadata(StatusType* status) – get MessageMetadata interface built by this builder.

    -
  • void +

  • void setField(StatusType* status, uint index, const string field) – set - name of a field / column.

    -
  • void + name of a field / column.

    +
  • void setRelation(StatusType* status, uint index, const string relation) – - set name of the relation from which the field was selected.

    -
  • void + set name of the relation from which the field was selected.

    +
  • void setOwner(StatusType* status, uint index, const string owner) – set - name of that relation owner.

    -
  • void + name of that relation owner.

    +
  • void setAlias(StatusType* status, uint index, const string alias) – set - alias name of the field in related statement.

    + alias name of the field in related statement.


    @@ -2639,6 +2637,11 @@ of isc_stmt_handle. This interface is returned by openCursor() call in IAttachment or IStatement. All fetch calls except fetchNext() work only for bidirectional (opened with CURSOR_TYPE_SCROLLABLE flag) result set.

    +

    Items +accepted in getInfo() call:

    +

    INF_RECORD_COUNT +– number of records stored inside a scrollable cursor, or -1 for a uni-directional cursor.

    +


    1. int fetchNext(StatusType* status, void* message) – fetch next record, @@ -2673,6 +2676,10 @@ All fetch calls except fetchNext() work only for bidirectional

    2. void close(StatusType* status) – close result set, releases interface on success.

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


    @@ -2733,7 +2740,7 @@ interface – replaces (partially) isc_stmt_handle.

    returning multiple rows of data. Partial analogue of isc_dsql_execute2() - in and out XSLQDAs replaced with input and output messages with appropriate buffers.

    -
  • IResultSet* +

  • 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.

    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.

    -
  • IBatch* + cursor setting it's value to Istatement::CURSOR_TYPE_SCROLLABLE.

    +
  • IBatch* createBatch(StatusType* status, IMessageMetadata* inMetadata, uint parLength, const uchar* par) – creates Batch interface to SQL statement with input parameters making it possible @@ -2750,10 +2757,10 @@ interface – replaces (partially) isc_stmt_handle.

    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.

    -
  • void + behavior.

    +
  • void setCursorName(StatusType* status, const char* name) – replaces - isc_dsql_set_cursor_name(). + isc_dsql_set_cursor_name().

  • void free(StatusType* status) – free statement, releases interface on @@ -3793,4 +3800,4 @@ release of it.

    - \ No newline at end of file + diff --git a/src/dsql/DsqlCursor.cpp b/src/dsql/DsqlCursor.cpp index 564a476459..ada2b4973b 100644 --- a/src/dsql/DsqlCursor.cpp +++ b/src/dsql/DsqlCursor.cpp @@ -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) diff --git a/src/dsql/DsqlCursor.h b/src/dsql/DsqlCursor.h index 864a8b3817..05bc28694c 100644 --- a/src/dsql/DsqlCursor.h +++ b/src/dsql/DsqlCursor.h @@ -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); diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index e966bb6baf..365d6ed6e7 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -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 diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index 4677f84434..517de22d2c 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -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 int fetchNext(StatusType* status, void* message) { @@ -1786,6 +1789,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 < 5) + { + StatusType::setVersionError(status, "IResultSet", cloopVTable->version, 5); + StatusType::checkException(status); + return; + } + StatusType::clearException(status); + static_cast(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(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 diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 9b770f67b2..0cd751cc9b 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -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) @@ -6498,6 +6503,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); @@ -10432,6 +10448,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; @@ -15617,7 +15642,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; @@ -15632,6 +15657,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; diff --git a/src/jrd/EngineInterface.h b/src/jrd/EngineInterface.h index 943bf4fae8..0e8a60511b 100644 --- a/src/jrd/EngineInterface.h +++ b/src/jrd/EngineInterface.h @@ -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); diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index dab848ce40..77eeabb577 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -5460,6 +5460,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); diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index dca515ecf5..ba4aec4d46 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -298,6 +298,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) @@ -5070,6 +5073,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) { /************************************** diff --git a/src/remote/protocol.cpp b/src/remote/protocol.cpp index 890472c45e..f2f05067fb 100644 --- a/src/remote/protocol.cpp +++ b/src/remote/protocol.cpp @@ -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(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 862f896db0..cdc3985e6f 100644 --- a/src/remote/protocol.h +++ b/src/remote/protocol.h @@ -311,6 +311,7 @@ enum P_OP op_info_batch = 111, op_fetch_scroll = 112, + op_info_cursor = 113, op_max }; diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index 4873772dab..4f4f4d6885 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -4592,7 +4592,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); @@ -4606,6 +4606,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. @@ -5087,6 +5099,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; diff --git a/src/yvalve/YObjects.h b/src/yvalve/YObjects.h index eef9fe9023..79f2382e15 100644 --- a/src/yvalve/YObjects.h +++ b/src/yvalve/YObjects.h @@ -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; diff --git a/src/yvalve/why.cpp b/src/yvalve/why.cpp index e816b6b29a..03f49579ba 100644 --- a/src/yvalve/why.cpp +++ b/src/yvalve/why.cpp @@ -4881,6 +4881,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 entry(status, this); + + entry.next()->getInfo(status, itemsLength, items, bufferLength, buffer); + } + catch (const Exception& e) + { + e.stuffException(status); + } +} void YResultSet::close(CheckStatusWrapper* status) {