diff --git a/doc/sql.extensions/README.profiler.md b/doc/sql.extensions/README.profiler.md index 8ad4c139bf..3a2c12ce46 100644 --- a/doc/sql.extensions/README.profiler.md +++ b/doc/sql.extensions/README.profiler.md @@ -265,6 +265,7 @@ Below is the list of tables that stores profile data. - `CURSOR_ID` type `INTEGER` - Cursor ID - `RECORD_SOURCE_ID` type `INTEGER` - Record source ID - `PARENT_RECORD_SOURCE_ID` type `INTEGER` - Parent record source ID + - `LEVEL` type `INTEGER` - Indentation level for the record source - `ACCESS_PATH` type `VARCHAR(255) CHARACTER SET UTF8` - Access path for the record source - Primary key: `PROFILE_ID, STATEMENT_ID, CURSOR_ID, RECORD_SOURCE_ID` @@ -419,6 +420,7 @@ select rstat.profile_id, cur.column_num cursor_column_num, rstat.record_source_id, recsrc.parent_record_source_id, + recsrc.level, recsrc.access_path, cast(sum(rstat.open_counter) as bigint) open_counter, min(rstat.open_min_elapsed_time) open_min_elapsed_time, @@ -461,6 +463,7 @@ select rstat.profile_id, cur.column_num, rstat.record_source_id, recsrc.parent_record_source_id, + recsrc.level, recsrc.access_path order by coalesce(sum(rstat.open_total_elapsed_time), 0) + coalesce(sum(rstat.fetch_total_elapsed_time), 0) desc ``` diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index 7991466436..c2419d6635 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -1744,7 +1744,7 @@ interface ProfilerSession : Disposable void defineCursor(int64 statementId, uint cursorId, const string name, uint line, uint column); void defineRecordSource(int64 statementId, uint cursorId, uint recSourceId, - const string accessPath, uint parentRecSourceId); + uint level, const string accessPath, uint parentRecSourceId); void onRequestStart(Status status, int64 statementId, int64 requestId, int64 callerStatementId, int64 callerRequestId, ISC_TIMESTAMP_TZ timestamp); diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index 8524e9c9ed..58e2bdda6d 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -6954,7 +6954,7 @@ namespace Firebird void (CLOOP_CARG *finish)(IProfilerSession* self, IStatus* status, ISC_TIMESTAMP_TZ timestamp) CLOOP_NOEXCEPT; void (CLOOP_CARG *defineStatement)(IProfilerSession* self, IStatus* status, ISC_INT64 statementId, ISC_INT64 parentStatementId, const char* type, const char* packageName, const char* routineName, const char* sqlText) CLOOP_NOEXCEPT; void (CLOOP_CARG *defineCursor)(IProfilerSession* self, ISC_INT64 statementId, unsigned cursorId, const char* name, unsigned line, unsigned column) CLOOP_NOEXCEPT; - void (CLOOP_CARG *defineRecordSource)(IProfilerSession* self, ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, const char* accessPath, unsigned parentRecSourceId) CLOOP_NOEXCEPT; + void (CLOOP_CARG *defineRecordSource)(IProfilerSession* self, ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, unsigned level, const char* accessPath, unsigned parentRecSourceId) CLOOP_NOEXCEPT; void (CLOOP_CARG *onRequestStart)(IProfilerSession* self, IStatus* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_INT64 callerStatementId, ISC_INT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) CLOOP_NOEXCEPT; void (CLOOP_CARG *onRequestFinish)(IProfilerSession* self, IStatus* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp, IProfilerStats* stats) CLOOP_NOEXCEPT; void (CLOOP_CARG *beforePsqlLineColumn)(IProfilerSession* self, ISC_INT64 statementId, ISC_INT64 requestId, unsigned line, unsigned column) CLOOP_NOEXCEPT; @@ -7019,9 +7019,9 @@ namespace Firebird static_cast(this->cloopVTable)->defineCursor(this, statementId, cursorId, name, line, column); } - void defineRecordSource(ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, const char* accessPath, unsigned parentRecSourceId) + void defineRecordSource(ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, unsigned level, const char* accessPath, unsigned parentRecSourceId) { - static_cast(this->cloopVTable)->defineRecordSource(this, statementId, cursorId, recSourceId, accessPath, parentRecSourceId); + static_cast(this->cloopVTable)->defineRecordSource(this, statementId, cursorId, recSourceId, level, accessPath, parentRecSourceId); } template void onRequestStart(StatusType* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_INT64 callerStatementId, ISC_INT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) @@ -20528,11 +20528,11 @@ namespace Firebird } } - static void CLOOP_CARG cloopdefineRecordSourceDispatcher(IProfilerSession* self, ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, const char* accessPath, unsigned parentRecSourceId) CLOOP_NOEXCEPT + static void CLOOP_CARG cloopdefineRecordSourceDispatcher(IProfilerSession* self, ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, unsigned level, const char* accessPath, unsigned parentRecSourceId) CLOOP_NOEXCEPT { try { - static_cast(self)->Name::defineRecordSource(statementId, cursorId, recSourceId, accessPath, parentRecSourceId); + static_cast(self)->Name::defineRecordSource(statementId, cursorId, recSourceId, level, accessPath, parentRecSourceId); } catch (...) { @@ -20672,7 +20672,7 @@ namespace Firebird virtual void finish(StatusType* status, ISC_TIMESTAMP_TZ timestamp) = 0; virtual void defineStatement(StatusType* status, ISC_INT64 statementId, ISC_INT64 parentStatementId, const char* type, const char* packageName, const char* routineName, const char* sqlText) = 0; virtual void defineCursor(ISC_INT64 statementId, unsigned cursorId, const char* name, unsigned line, unsigned column) = 0; - virtual void defineRecordSource(ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, const char* accessPath, unsigned parentRecSourceId) = 0; + virtual void defineRecordSource(ISC_INT64 statementId, unsigned cursorId, unsigned recSourceId, unsigned level, const char* accessPath, unsigned parentRecSourceId) = 0; virtual void onRequestStart(StatusType* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_INT64 callerStatementId, ISC_INT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) = 0; virtual void onRequestFinish(StatusType* status, ISC_INT64 statementId, ISC_INT64 requestId, ISC_TIMESTAMP_TZ timestamp, IProfilerStats* stats) = 0; virtual void beforePsqlLineColumn(ISC_INT64 statementId, ISC_INT64 requestId, unsigned line, unsigned column) = 0; diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 9b2a69ad91..02aa859834 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -727,7 +727,7 @@ type IProfilerSession_finishPtr = procedure(this: IProfilerSession; status: IStatus; timestamp: ISC_TIMESTAMP_TZ); cdecl; IProfilerSession_defineStatementPtr = procedure(this: IProfilerSession; status: IStatus; statementId: Int64; parentStatementId: Int64; type_: PAnsiChar; packageName: PAnsiChar; routineName: PAnsiChar; sqlText: PAnsiChar); cdecl; IProfilerSession_defineCursorPtr = procedure(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; name: PAnsiChar; line: Cardinal; column: Cardinal); cdecl; - IProfilerSession_defineRecordSourcePtr = procedure(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); cdecl; + IProfilerSession_defineRecordSourcePtr = procedure(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; level: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); cdecl; IProfilerSession_onRequestStartPtr = procedure(this: IProfilerSession; status: IStatus; statementId: Int64; requestId: Int64; callerStatementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ); cdecl; IProfilerSession_onRequestFinishPtr = procedure(this: IProfilerSession; status: IStatus; statementId: Int64; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; stats: IProfilerStats); cdecl; IProfilerSession_beforePsqlLineColumnPtr = procedure(this: IProfilerSession; statementId: Int64; requestId: Int64; line: Cardinal; column: Cardinal); cdecl; @@ -3838,7 +3838,7 @@ type procedure finish(status: IStatus; timestamp: ISC_TIMESTAMP_TZ); procedure defineStatement(status: IStatus; statementId: Int64; parentStatementId: Int64; type_: PAnsiChar; packageName: PAnsiChar; routineName: PAnsiChar; sqlText: PAnsiChar); procedure defineCursor(statementId: Int64; cursorId: Cardinal; name: PAnsiChar; line: Cardinal; column: Cardinal); - procedure defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); + procedure defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; level: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); procedure onRequestStart(status: IStatus; statementId: Int64; requestId: Int64; callerStatementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ); procedure onRequestFinish(status: IStatus; statementId: Int64; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; stats: IProfilerStats); procedure beforePsqlLineColumn(statementId: Int64; requestId: Int64; line: Cardinal; column: Cardinal); @@ -3859,7 +3859,7 @@ type procedure finish(status: IStatus; timestamp: ISC_TIMESTAMP_TZ); virtual; abstract; procedure defineStatement(status: IStatus; statementId: Int64; parentStatementId: Int64; type_: PAnsiChar; packageName: PAnsiChar; routineName: PAnsiChar; sqlText: PAnsiChar); virtual; abstract; procedure defineCursor(statementId: Int64; cursorId: Cardinal; name: PAnsiChar; line: Cardinal; column: Cardinal); virtual; abstract; - procedure defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); virtual; abstract; + procedure defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; level: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); virtual; abstract; procedure onRequestStart(status: IStatus; statementId: Int64; requestId: Int64; callerStatementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ); virtual; abstract; procedure onRequestFinish(status: IStatus; statementId: Int64; requestId: Int64; timestamp: ISC_TIMESTAMP_TZ; stats: IProfilerStats); virtual; abstract; procedure beforePsqlLineColumn(statementId: Int64; requestId: Int64; line: Cardinal; column: Cardinal); virtual; abstract; @@ -9428,9 +9428,9 @@ begin ProfilerSessionVTable(vTable).defineCursor(Self, statementId, cursorId, name, line, column); end; -procedure IProfilerSession.defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); +procedure IProfilerSession.defineRecordSource(statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; level: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); begin - ProfilerSessionVTable(vTable).defineRecordSource(Self, statementId, cursorId, recSourceId, accessPath, parentRecSourceId); + ProfilerSessionVTable(vTable).defineRecordSource(Self, statementId, cursorId, recSourceId, level, accessPath, parentRecSourceId); end; procedure IProfilerSession.onRequestStart(status: IStatus; statementId: Int64; requestId: Int64; callerStatementId: Int64; callerRequestId: Int64; timestamp: ISC_TIMESTAMP_TZ); @@ -16541,10 +16541,10 @@ begin end end; -procedure IProfilerSessionImpl_defineRecordSourceDispatcher(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); cdecl; +procedure IProfilerSessionImpl_defineRecordSourceDispatcher(this: IProfilerSession; statementId: Int64; cursorId: Cardinal; recSourceId: Cardinal; level: Cardinal; accessPath: PAnsiChar; parentRecSourceId: Cardinal); cdecl; begin try - IProfilerSessionImpl(this).defineRecordSource(statementId, cursorId, recSourceId, accessPath, parentRecSourceId); + IProfilerSessionImpl(this).defineRecordSource(statementId, cursorId, recSourceId, level, accessPath, parentRecSourceId); except on e: Exception do FbException.catchException(nil, e); end diff --git a/src/jrd/ProfilerManager.cpp b/src/jrd/ProfilerManager.cpp index deda95b8ca..4ab5b372c4 100644 --- a/src/jrd/ProfilerManager.cpp +++ b/src/jrd/ProfilerManager.cpp @@ -475,13 +475,17 @@ void ProfilerManager::prepareCursor(thread_db* tdbb, Request* request, const Sel auto cursorId = select->getCursorId(); - if (profileStatement->definedCursors.exist(cursorId)) - return; + if (!profileStatement->definedCursors.exist(cursorId)) + { + currentSession->pluginSession->defineCursor(profileStatement->id, cursorId, + select->getName().nullStr(), select->getLine(), select->getColumn()); - currentSession->pluginSession->defineCursor(profileStatement->id, cursorId, - select->getName().nullStr(), select->getLine(), select->getColumn()); + profileStatement->definedCursors.add(cursorId); + } - profileStatement->definedCursors.add(cursorId); + const auto recordSource = select->getAccessPath(); + + prepareRecSource(tdbb, request, recordSource); } void ProfilerManager::prepareRecSource(thread_db* tdbb, Request* request, const RecordSource* rsb) @@ -496,35 +500,32 @@ void ProfilerManager::prepareRecSource(thread_db* tdbb, Request* request, const fb_assert(profileStatement->definedCursors.exist(rsb->getCursorId())); - Array> tree; - tree.add({rsb, nullptr}); - - for (unsigned pos = 0; pos < tree.getCount(); ++pos) + struct PlanItem : PermanentStorage { - const auto thisRsb = tree[pos].first; + explicit PlanItem(MemoryPool& p) + : PermanentStorage(p) + { + } - Array children; - thisRsb->getChildren(children); + const RecordSource* recordSource = nullptr; + const RecordSource* parentRecordSource = nullptr; + string accessPath{getPool()}; + unsigned level = 0; + }; - unsigned childPos = pos; + ObjectsArray planItems; + planItems.add().recordSource = rsb; - for (const auto child : children) - tree.insert(++childPos, {child, thisRsb}); - } - - NonPooledMap idSequenceMap; - auto sequencePtr = profileStatement->cursorNextSequence.getOrPut(rsb->getCursorId()); - - for (const auto& pair : tree) + for (unsigned pos = 0; pos < planItems.getCount(); ++pos) { - const auto cursorId = pair.first->getCursorId(); - const auto recSourceId = pair.first->getRecSourceId(); - idSequenceMap.put(recSourceId, ++*sequencePtr); + auto& planItem = planItems[pos]; + const auto thisRsb = planItem.recordSource; - string accessPath; - pair.first->print(tdbb, accessPath, true, 0, false); + string& accessPath = planItem.accessPath; + thisRsb->print(tdbb, accessPath, true, 0, false); constexpr auto INDENT_MARKER = "\n "; + constexpr unsigned INDENT_COUNT = 4; if (accessPath.find(INDENT_MARKER) == 0) { @@ -535,13 +536,44 @@ void ProfilerManager::prepareRecSource(thread_db* tdbb, Request* request, const } while ((pos = accessPath.find(INDENT_MARKER, pos + 1)) != string::npos); } + if (accessPath.hasData() && accessPath[0] == '\n') + accessPath.erase(0, 1); + + Array children; + thisRsb->getChildren(children); + + unsigned level = planItem.level; + + if (const auto lastLinePos = accessPath.find_last_of('\n'); lastLinePos != string::npos) + level += (accessPath.find_first_not_of(' ', lastLinePos + 1) - lastLinePos + 1) / INDENT_COUNT; + + unsigned childPos = pos; + + for (const auto child : children) + { + auto& inserted = planItems.insert(++childPos); + inserted.recordSource = child; + inserted.parentRecordSource = thisRsb; + inserted.level = level + 1; + } + } + + NonPooledMap idSequenceMap; + auto sequencePtr = profileStatement->cursorNextSequence.getOrPut(rsb->getCursorId()); + + for (const auto& planItem : planItems) + { + const auto cursorId = planItem.recordSource->getCursorId(); + const auto recSourceId = planItem.recordSource->getRecSourceId(); + idSequenceMap.put(recSourceId, ++*sequencePtr); + ULONG parentSequence = 0; - if (pair.second) - parentSequence = *idSequenceMap.get(pair.second->getRecSourceId()); + if (planItem.parentRecordSource) + parentSequence = *idSequenceMap.get(planItem.parentRecordSource->getRecSourceId()); currentSession->pluginSession->defineRecordSource(profileStatement->id, cursorId, - *sequencePtr, accessPath.c_str(), parentSequence); + *sequencePtr, planItem.level, planItem.accessPath.c_str(), parentSequence); profileStatement->recSourceSequence.put(recSourceId, *sequencePtr); } diff --git a/src/jrd/ProfilerManager.h b/src/jrd/ProfilerManager.h index 1cb5610f8b..f9ce5e2cb9 100644 --- a/src/jrd/ProfilerManager.h +++ b/src/jrd/ProfilerManager.h @@ -94,8 +94,6 @@ public: { lastTicks = profilerManager->queryTicks(); - profilerManager->prepareRecSource(tdbb, request, recordSource); - if (profilerManager->currentSession->flags & Firebird::IProfilerSession::FLAG_BEFORE_EVENTS) { if (event == Event::OPEN) @@ -191,7 +189,6 @@ public: const Firebird::PathName& pluginName, const Firebird::string& description, const Firebird::string& options); void prepareCursor(thread_db* tdbb, Request* request, const Select* select); - void prepareRecSource(thread_db* tdbb, Request* request, const RecordSource* rsb); void onRequestFinish(Request* request, Stats& stats); void beforePsqlLineColumn(Request* request, ULONG line, ULONG column) @@ -218,11 +215,12 @@ public: if (const auto profileRequestId = getRequest(request, Firebird::IProfilerSession::FLAG_BEFORE_EVENTS)) { const auto profileStatement = getStatement(request); - const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId()); - fb_assert(sequencePtr); - currentSession->pluginSession->beforeRecordSourceOpen( - profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr); + if (const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId())) + { + currentSession->pluginSession->beforeRecordSourceOpen( + profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr); + } } } @@ -231,11 +229,12 @@ public: if (const auto profileRequestId = getRequest(request, Firebird::IProfilerSession::FLAG_AFTER_EVENTS)) { const auto profileStatement = getStatement(request); - const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId()); - fb_assert(sequencePtr); - currentSession->pluginSession->afterRecordSourceOpen( - profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr, &stats); + if (const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId())) + { + currentSession->pluginSession->afterRecordSourceOpen( + profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr, &stats); + } } } @@ -244,11 +243,12 @@ public: if (const auto profileRequestId = getRequest(request, Firebird::IProfilerSession::FLAG_BEFORE_EVENTS)) { const auto profileStatement = getStatement(request); - const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId()); - fb_assert(sequencePtr); - currentSession->pluginSession->beforeRecordSourceGetRecord( - profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr); + if (const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId())) + { + currentSession->pluginSession->beforeRecordSourceGetRecord( + profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr); + } } } @@ -257,11 +257,12 @@ public: if (const auto profileRequestId = getRequest(request, Firebird::IProfilerSession::FLAG_AFTER_EVENTS)) { const auto profileStatement = getStatement(request); - const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId()); - fb_assert(sequencePtr); - currentSession->pluginSession->afterRecordSourceGetRecord( - profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr, &stats); + if (const auto sequencePtr = profileStatement->recSourceSequence.get(rsb->getRecSourceId())) + { + currentSession->pluginSession->afterRecordSourceGetRecord( + profileStatement->id, profileRequestId, rsb->getCursorId(), *sequencePtr, &stats); + } } } @@ -282,6 +283,8 @@ public: } private: + void prepareRecSource(thread_db* tdbb, Request* request, const RecordSource* rsb); + void cancelSession(); void finishSession(thread_db* tdbb, bool flushData); void pauseSession(bool flushData); diff --git a/src/plugins/profiler/Profiler.cpp b/src/plugins/profiler/Profiler.cpp index 41c9e8dac4..faaa9d3285 100644 --- a/src/plugins/profiler/Profiler.cpp +++ b/src/plugins/profiler/Profiler.cpp @@ -106,6 +106,7 @@ struct Cursor struct RecordSource { Nullable parentId; + unsigned level; string accessPath{defaultPool()}; }; @@ -181,7 +182,7 @@ public: void defineCursor(SINT64 statementId, unsigned cursorId, const char* name, unsigned line, unsigned column) override; void defineRecordSource(SINT64 statementId, unsigned cursorId, unsigned recSourceId, - const char* accessPath, unsigned parentRecordSourceId) override; + unsigned level, const char* accessPath, unsigned parentRecordSourceId) override; void onRequestStart(ThrowStatusExceptionWrapper* status, SINT64 statementId, SINT64 requestId, SINT64 callerStatementId, SINT64 callerRequestId, ISC_TIMESTAMP_TZ timestamp) override; @@ -441,8 +442,8 @@ void ProfilerPlugin::flush(ThrowStatusExceptionWrapper* status) constexpr auto recSrcSql = R"""( update or insert into plg$prof_record_sources (profile_id, statement_id, cursor_id, record_source_id, - parent_record_source_id, access_path) - values (?, ?, ?, ?, ?, ?) + parent_record_source_id, level, access_path) + values (?, ?, ?, ?, ?, ?, ?) matching (profile_id, statement_id, cursor_id, record_source_id) )"""; @@ -452,6 +453,7 @@ void ProfilerPlugin::flush(ThrowStatusExceptionWrapper* status) (FB_INTEGER, cursorId) (FB_INTEGER, recordSourceId) (FB_INTEGER, parentRecordSourceId) + (FB_INTEGER, level) (FB_INTL_VARCHAR(MAX_ACCESS_PATH_CHAR_LEN * 4, CS_UTF8), accessPath) ) recSrcMessage(status, MasterInterfacePtr()); recSrcMessage.clear(); @@ -773,6 +775,9 @@ void ProfilerPlugin::flush(ThrowStatusExceptionWrapper* status) recSrcMessage->parentRecordSourceIdNull = !recSrc.parentId.specified; recSrcMessage->parentRecordSourceId = recSrc.parentId.value; + recSrcMessage->levelNull = FB_FALSE; + recSrcMessage->level = recSrc.level; + recSrcMessage->accessPathNull = FB_FALSE; recSrcMessage->accessPath.set(recSrc.accessPath.c_str()); @@ -1041,6 +1046,7 @@ void ProfilerPlugin::createMetadata(ThrowStatusExceptionWrapper* status, RefPtr< cursor_id integer not null, record_source_id integer not null, parent_record_source_id integer, + level integer not null, access_path varchar(255) character set utf8 not null, constraint plg$prof_record_sources_pk primary key (profile_id, statement_id, cursor_id, record_source_id) @@ -1276,6 +1282,7 @@ void ProfilerPlugin::createMetadata(ThrowStatusExceptionWrapper* status, RefPtr< cur.column_num cursor_column_num, rstat.record_source_id, recsrc.parent_record_source_id, + recsrc.level, recsrc.access_path, cast(sum(rstat.open_counter) as bigint) open_counter, min(rstat.open_min_elapsed_time) open_min_elapsed_time, @@ -1318,6 +1325,7 @@ void ProfilerPlugin::createMetadata(ThrowStatusExceptionWrapper* status, RefPtr< cur.column_num, rstat.record_source_id, recsrc.parent_record_source_id, + recsrc.level, recsrc.access_path order by coalesce(sum(rstat.open_total_elapsed_time), 0) + coalesce(sum(rstat.fetch_total_elapsed_time), 0) desc )""", @@ -1440,7 +1448,7 @@ void Session::defineCursor(SINT64 statementId, unsigned cursorId, const char* na } void Session::defineRecordSource(SINT64 statementId, unsigned cursorId, unsigned recSourceId, - const char* accessPath, unsigned parentRecordSourceId) + unsigned level, const char* accessPath, unsigned parentRecordSourceId) { const auto recSource = recordSources.put({{statementId, cursorId}, recSourceId}); fb_assert(recSource); @@ -1448,6 +1456,7 @@ void Session::defineRecordSource(SINT64 statementId, unsigned cursorId, unsigned if (!recSource) return; + recSource->level = level; recSource->accessPath = accessPath; if (unsigned len = recSource->accessPath.length(); len > MAX_ACCESS_PATH_CHAR_LEN)