From fa829b7f7baf83c6fcc360a218e1b1577b2fc4e1 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 11 Aug 2023 14:16:47 +0300 Subject: [PATCH 1/2] Recursive plan output for selectable stored procedures --- builds/install/misc/firebird.conf | 13 +++ src/common/config/config.h | 7 +- src/jrd/ProfilerManager.cpp | 4 +- src/jrd/recsrc/AggregatedStream.cpp | 8 +- src/jrd/recsrc/BitmapTableScan.cpp | 5 +- src/jrd/recsrc/BufferedStream.cpp | 8 +- src/jrd/recsrc/ConditionalStream.cpp | 14 +-- src/jrd/recsrc/Cursor.cpp | 20 +++-- src/jrd/recsrc/Cursor.h | 8 +- src/jrd/recsrc/ExternalTableScan.cpp | 5 +- src/jrd/recsrc/FilteredStream.cpp | 8 +- src/jrd/recsrc/FirstRowsStream.cpp | 8 +- src/jrd/recsrc/FullOuterJoin.cpp | 14 +-- src/jrd/recsrc/FullTableScan.cpp | 4 +- src/jrd/recsrc/HashJoin.cpp | 14 +-- src/jrd/recsrc/IndexTableScan.cpp | 4 +- src/jrd/recsrc/LocalTableStream.cpp | 4 +- src/jrd/recsrc/LockedStream.cpp | 8 +- src/jrd/recsrc/MergeJoin.cpp | 10 +-- src/jrd/recsrc/NestedLoopJoin.cpp | 10 +-- src/jrd/recsrc/ProcedureScan.cpp | 30 ++++++- src/jrd/recsrc/RecordSource.h | 129 +++++++++++++++++---------- src/jrd/recsrc/RecursiveStream.cpp | 14 +-- src/jrd/recsrc/SingularStream.cpp | 8 +- src/jrd/recsrc/SkipRowsStream.cpp | 8 +- src/jrd/recsrc/SortedStream.cpp | 11 ++- src/jrd/recsrc/Union.cpp | 10 +-- src/jrd/recsrc/VirtualTableScan.cpp | 4 +- src/jrd/recsrc/WindowedStream.cpp | 27 +++--- 29 files changed, 246 insertions(+), 171 deletions(-) diff --git a/builds/install/misc/firebird.conf b/builds/install/misc/firebird.conf index c6e8b9f08e3..1c38cdf47bf 100644 --- a/builds/install/misc/firebird.conf +++ b/builds/install/misc/firebird.conf @@ -749,6 +749,19 @@ #ReadConsistency = 1 +# ---------------------------- +# Define maximum recursion depth of the plan output. +# +# When set to non-zero, allows to recurse into selectable procedures and +# include their nested plans into the plan of the outer query. +# +# Per-database configurable. +# +# Type: integer +# +#PlanRecursionLimit = 0 + + # ---------------------------- # The engine provides a number of new datatypes unknown to legacy clients. # To simplify use of old applications set this parameter to the Firebird version diff --git a/src/common/config/config.h b/src/common/config/config.h index d9ccd105057..6d557677cf9 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -192,6 +192,7 @@ enum ConfigKey KEY_MAX_STATEMENT_CACHE_SIZE, KEY_PARALLEL_WORKERS, KEY_MAX_PARALLEL_WORKERS, + KEY_PLAN_RECURSION_LIMIT, MAX_CONFIG_KEY // keep it last }; @@ -310,7 +311,8 @@ constexpr ConfigEntry entries[MAX_CONFIG_KEY] = {TYPE_STRING, "TempTableDirectory", false, ""}, {TYPE_INTEGER, "MaxStatementCacheSize", false, 2 * 1048576}, // bytes {TYPE_INTEGER, "ParallelWorkers", true, 1}, - {TYPE_INTEGER, "MaxParallelWorkers", true, 1} + {TYPE_INTEGER, "MaxParallelWorkers", true, 1}, + {TYPE_INTEGER, "PlanRecursionLimit", false, 0} }; @@ -638,6 +640,9 @@ class Config : public RefCounted, public GlobalStorage CONFIG_GET_GLOBAL_INT(getParallelWorkers, KEY_PARALLEL_WORKERS); CONFIG_GET_GLOBAL_INT(getMaxParallelWorkers, KEY_MAX_PARALLEL_WORKERS); + + CONFIG_GET_PER_DB_INT(getPlanRecursionLimit, KEY_PLAN_RECURSION_LIMIT); + }; // Implementation of interface to access master configuration file diff --git a/src/jrd/ProfilerManager.cpp b/src/jrd/ProfilerManager.cpp index 20bc786ca00..7799876c6fb 100644 --- a/src/jrd/ProfilerManager.cpp +++ b/src/jrd/ProfilerManager.cpp @@ -520,7 +520,9 @@ void ProfilerManager::prepareRecSource(thread_db* tdbb, Request* request, const const auto thisRsb = planItem.recordSource; string& accessPath = planItem.accessPath; - thisRsb->print(tdbb, accessPath, true, 0, false); + + PlanPrintContext context(tdbb->getDatabase(), accessPath, true, false); + thisRsb->print(tdbb, context, 0); constexpr auto INDENT_MARKER = "\n "; constexpr unsigned INDENT_COUNT = 4; diff --git a/src/jrd/recsrc/AggregatedStream.cpp b/src/jrd/recsrc/AggregatedStream.cpp index 4cfd13a3748..a1549f446a9 100644 --- a/src/jrd/recsrc/AggregatedStream.cpp +++ b/src/jrd/recsrc/AggregatedStream.cpp @@ -380,16 +380,16 @@ void AggregatedStream::getChildren(Array& children) const children.add(m_next); } -void AggregatedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void AggregatedStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Aggregate"; printOptInfo(plan); } - if (recurse) - m_next->print(tdbb, plan, detailed, level, recurse); + if (plan.goDeeper()) + m_next->print(tdbb, plan, level); } bool AggregatedStream::internalGetRecord(thread_db* tdbb) const diff --git a/src/jrd/recsrc/BitmapTableScan.cpp b/src/jrd/recsrc/BitmapTableScan.cpp index acbf1191750..74ac4e68d93 100644 --- a/src/jrd/recsrc/BitmapTableScan.cpp +++ b/src/jrd/recsrc/BitmapTableScan.cpp @@ -126,10 +126,9 @@ void BitmapTableScan::getChildren(Array& children) const { } -void BitmapTableScan::print(thread_db* tdbb, string& plan, - bool detailed, unsigned level, bool recurse) const +void BitmapTableScan::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID"; diff --git a/src/jrd/recsrc/BufferedStream.cpp b/src/jrd/recsrc/BufferedStream.cpp index 9711567199b..b1a7011ffd1 100644 --- a/src/jrd/recsrc/BufferedStream.cpp +++ b/src/jrd/recsrc/BufferedStream.cpp @@ -319,9 +319,9 @@ void BufferedStream::getChildren(Array& children) const children.add(m_next); } -void BufferedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void BufferedStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { string extras; extras.printf(" (record length: %" ULONGFORMAT")", m_format->fmt_length); @@ -330,8 +330,8 @@ void BufferedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigne printOptInfo(plan); } - if (recurse) - m_next->print(tdbb, plan, detailed, level, recurse); + if (plan.goDeeper()) + m_next->print(tdbb, plan, level); } void BufferedStream::markRecursive() diff --git a/src/jrd/recsrc/ConditionalStream.cpp b/src/jrd/recsrc/ConditionalStream.cpp index 0af771986ab..24eeb1375cb 100644 --- a/src/jrd/recsrc/ConditionalStream.cpp +++ b/src/jrd/recsrc/ConditionalStream.cpp @@ -120,17 +120,17 @@ void ConditionalStream::getChildren(Array& children) const children.add(m_second); } -void ConditionalStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void ConditionalStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Condition"; printOptInfo(plan); - if (recurse) + if (plan.goDeeper()) { - m_first->print(tdbb, plan, true, level, recurse); - m_second->print(tdbb, plan, true, level, recurse); + m_first->print(tdbb, plan, level); + m_second->print(tdbb, plan, level); } } else @@ -138,11 +138,11 @@ void ConditionalStream::print(thread_db* tdbb, string& plan, bool detailed, unsi if (!level) plan += "("; - m_first->print(tdbb, plan, false, level + 1, recurse); + m_first->print(tdbb, plan, level + 1); plan += ", "; - m_second->print(tdbb, plan, false, level + 1, recurse); + m_second->print(tdbb, plan, level + 1); if (!level) plan += ")"; diff --git a/src/jrd/recsrc/Cursor.cpp b/src/jrd/recsrc/Cursor.cpp index 4523889751e..f00619b1815 100644 --- a/src/jrd/recsrc/Cursor.cpp +++ b/src/jrd/recsrc/Cursor.cpp @@ -107,26 +107,28 @@ void Select::initializeInvariants(Request* request) const } } -void Select::print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level, bool recurse) const +void Select::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + plan += level ? RecordSource::printIndent(level) : "\n"; + + if (plan.isDetailed()) { if (m_rse->isSubQuery()) { - plan += "\nSub-query"; + plan += "Sub-query"; if (m_rse->isInvariant()) plan += " (invariant)"; } else if (m_cursorName.hasData()) { - plan += "\nCursor \"" + string(m_cursorName) + "\""; + plan += "Cursor \"" + string(m_cursorName) + "\""; if (m_rse->isScrollable()) plan += " (scrollable)"; } else - plan += "\nSelect Expression"; + plan += "Select Expression"; if (m_line || m_column) { @@ -137,18 +139,18 @@ void Select::print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsig } else { - if (m_line || m_column) + if (!level && (m_line || m_column)) { string pos; pos.printf("\n-- line %u, column %u", m_line, m_column); plan += pos; } - plan += "\nPLAN "; + plan += "PLAN "; } - if (recurse) - m_root->print(tdbb, plan, detailed, level, true); + if (plan.goDeeper()) + m_root->print(tdbb, plan, level); } // --------------------- diff --git a/src/jrd/recsrc/Cursor.h b/src/jrd/recsrc/Cursor.h index 68d3d142613..c230bb97beb 100644 --- a/src/jrd/recsrc/Cursor.h +++ b/src/jrd/recsrc/Cursor.h @@ -25,12 +25,12 @@ #include "../common/classes/array.h" #include "../jrd/MetaName.h" +#include "../jrd/recsrc/RecordSource.h" namespace Jrd { class thread_db; class CompilerScratch; - class RecordSource; class RseNode; // Select class (common base for sub-queries and cursors) @@ -70,11 +70,11 @@ namespace Jrd void printPlan(thread_db* tdbb, Firebird::string& plan, bool detailed) const { - print(tdbb, plan, detailed, 0, true); + PlanPrintContext context(tdbb->getDatabase(), plan, detailed, true); + print(tdbb, context, 0); } - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void getChildren(Firebird::Array& children) const override { diff --git a/src/jrd/recsrc/ExternalTableScan.cpp b/src/jrd/recsrc/ExternalTableScan.cpp index 4137a7a5e0c..dced7697c54 100644 --- a/src/jrd/recsrc/ExternalTableScan.cpp +++ b/src/jrd/recsrc/ExternalTableScan.cpp @@ -119,10 +119,9 @@ void ExternalTableScan::getChildren(Array& children) const { } -void ExternalTableScan::print(thread_db* tdbb, string& plan, - bool detailed, unsigned level, bool recurse) const +void ExternalTableScan::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan"; diff --git a/src/jrd/recsrc/FilteredStream.cpp b/src/jrd/recsrc/FilteredStream.cpp index aaa98aa8fc2..128b510b918 100644 --- a/src/jrd/recsrc/FilteredStream.cpp +++ b/src/jrd/recsrc/FilteredStream.cpp @@ -121,9 +121,9 @@ void FilteredStream::getChildren(Array& children) const children.add(m_next); } -void FilteredStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void FilteredStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Filter"; @@ -133,8 +133,8 @@ void FilteredStream::print(thread_db* tdbb, string& plan, bool detailed, unsigne printOptInfo(plan); } - if (recurse) - m_next->print(tdbb, plan, detailed, level, recurse); + if (plan.goDeeper()) + m_next->print(tdbb, plan, level); } void FilteredStream::markRecursive() diff --git a/src/jrd/recsrc/FirstRowsStream.cpp b/src/jrd/recsrc/FirstRowsStream.cpp index 19df50092f0..2554543e1ab 100644 --- a/src/jrd/recsrc/FirstRowsStream.cpp +++ b/src/jrd/recsrc/FirstRowsStream.cpp @@ -125,16 +125,16 @@ void FirstRowsStream::getChildren(Array& children) const children.add(m_next); } -void FirstRowsStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void FirstRowsStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "First N Records"; printOptInfo(plan); } - if (recurse) - m_next->print(tdbb, plan, detailed, level, recurse); + if (plan.goDeeper()) + m_next->print(tdbb, plan, level); } void FirstRowsStream::markRecursive() diff --git a/src/jrd/recsrc/FullOuterJoin.cpp b/src/jrd/recsrc/FullOuterJoin.cpp index ae87b72db20..15c666ffb37 100644 --- a/src/jrd/recsrc/FullOuterJoin.cpp +++ b/src/jrd/recsrc/FullOuterJoin.cpp @@ -118,25 +118,25 @@ void FullOuterJoin::getChildren(Array& children) const children.add(m_arg2); } -void FullOuterJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void FullOuterJoin::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Full Outer Join"; - if (recurse) + if (plan.goDeeper()) { - m_arg1->print(tdbb, plan, true, level, recurse); - m_arg2->print(tdbb, plan, true, level, recurse); + m_arg1->print(tdbb, plan, level); + m_arg2->print(tdbb, plan, level); } } else { level++; plan += "JOIN ("; - m_arg1->print(tdbb, plan, false, level, recurse); + m_arg1->print(tdbb, plan, level); plan += ", "; - m_arg2->print(tdbb, plan, false, level, recurse); + m_arg2->print(tdbb, plan, level); plan += ")"; } } diff --git a/src/jrd/recsrc/FullTableScan.cpp b/src/jrd/recsrc/FullTableScan.cpp index 3cdc94f450d..4da2e178d95 100644 --- a/src/jrd/recsrc/FullTableScan.cpp +++ b/src/jrd/recsrc/FullTableScan.cpp @@ -166,9 +166,9 @@ void FullTableScan::getChildren(Array& children) const { } -void FullTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void FullTableScan::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { auto lowerBounds = 0, upperBounds = 0; for (const auto range : m_dbkeyRanges) diff --git a/src/jrd/recsrc/HashJoin.cpp b/src/jrd/recsrc/HashJoin.cpp index e3d21b608b2..35ff943b2b7 100644 --- a/src/jrd/recsrc/HashJoin.cpp +++ b/src/jrd/recsrc/HashJoin.cpp @@ -462,33 +462,33 @@ void HashJoin::getChildren(Array& children) const children.add(m_args[i].source); } -void HashJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void HashJoin::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Hash Join (inner)"; printOptInfo(plan); - if (recurse) + if (plan.goDeeper()) { - m_leader.source->print(tdbb, plan, true, level, recurse); + m_leader.source->print(tdbb, plan, level); for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) - m_args[i].source->print(tdbb, plan, true, level, recurse); + m_args[i].source->print(tdbb, plan, level); } } else { level++; plan += "HASH ("; - m_leader.source->print(tdbb, plan, false, level, recurse); + m_leader.source->print(tdbb, plan, level); plan += ", "; for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) { if (i) plan += ", "; - m_args[i].source->print(tdbb, plan, false, level, recurse); + m_args[i].source->print(tdbb, plan, level); } plan += ")"; } diff --git a/src/jrd/recsrc/IndexTableScan.cpp b/src/jrd/recsrc/IndexTableScan.cpp index c11a66c9381..d4688319722 100644 --- a/src/jrd/recsrc/IndexTableScan.cpp +++ b/src/jrd/recsrc/IndexTableScan.cpp @@ -304,9 +304,9 @@ void IndexTableScan::getChildren(Array& children) const { } -void IndexTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void IndexTableScan::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID"; diff --git a/src/jrd/recsrc/LocalTableStream.cpp b/src/jrd/recsrc/LocalTableStream.cpp index 86e2df624ea..aed9064b731 100644 --- a/src/jrd/recsrc/LocalTableStream.cpp +++ b/src/jrd/recsrc/LocalTableStream.cpp @@ -113,11 +113,11 @@ WriteLockResult LocalTableStream::lockRecord(thread_db* tdbb, bool skipLocked) c status_exception::raise(Arg::Gds(isc_record_lock_not_supp)); } -void LocalTableStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void LocalTableStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { //// TODO: Use Local Table name/alias. - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Local Table Full Scan"; printOptInfo(plan); diff --git a/src/jrd/recsrc/LockedStream.cpp b/src/jrd/recsrc/LockedStream.cpp index e392c93021d..bab9bf75e32 100644 --- a/src/jrd/recsrc/LockedStream.cpp +++ b/src/jrd/recsrc/LockedStream.cpp @@ -116,16 +116,16 @@ void LockedStream::getChildren(Array& children) const children.add(m_next); } -void LockedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void LockedStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Write Lock"; printOptInfo(plan); } - if (recurse) - m_next->print(tdbb, plan, detailed, level, recurse); + if (plan.goDeeper()) + m_next->print(tdbb, plan, level); } void LockedStream::markRecursive() diff --git a/src/jrd/recsrc/MergeJoin.cpp b/src/jrd/recsrc/MergeJoin.cpp index ddafb39ebfa..773c83dae87 100644 --- a/src/jrd/recsrc/MergeJoin.cpp +++ b/src/jrd/recsrc/MergeJoin.cpp @@ -351,17 +351,17 @@ void MergeJoin::getChildren(Array& children) const children.add(m_args[i]); } -void MergeJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void MergeJoin::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Merge Join (inner)"; printOptInfo(plan); - if (recurse) + if (plan.goDeeper()) { for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) - m_args[i]->print(tdbb, plan, true, level, recurse); + m_args[i]->print(tdbb, plan, level); } } else @@ -373,7 +373,7 @@ void MergeJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned lev if (i) plan += ", "; - m_args[i]->print(tdbb, plan, false, level, recurse); + m_args[i]->print(tdbb, plan, level); } plan += ")"; } diff --git a/src/jrd/recsrc/NestedLoopJoin.cpp b/src/jrd/recsrc/NestedLoopJoin.cpp index b267c5b3662..35a93194d1f 100644 --- a/src/jrd/recsrc/NestedLoopJoin.cpp +++ b/src/jrd/recsrc/NestedLoopJoin.cpp @@ -214,11 +214,11 @@ void NestedLoopJoin::getChildren(Array& children) const children.add(m_args[i]); } -void NestedLoopJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void NestedLoopJoin::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { if (m_args.hasData()) { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Nested Loop Join "; @@ -246,10 +246,10 @@ void NestedLoopJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigne printOptInfo(plan); - if (recurse) + if (plan.goDeeper()) { for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) - m_args[i]->print(tdbb, plan, true, level, recurse); + m_args[i]->print(tdbb, plan, level); } } else @@ -261,7 +261,7 @@ void NestedLoopJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigne if (i) plan += ", "; - m_args[i]->print(tdbb, plan, false, level, recurse); + m_args[i]->print(tdbb, plan, level); } plan += ")"; } diff --git a/src/jrd/recsrc/ProcedureScan.cpp b/src/jrd/recsrc/ProcedureScan.cpp index fce5434a956..0cd852a0344 100644 --- a/src/jrd/recsrc/ProcedureScan.cpp +++ b/src/jrd/recsrc/ProcedureScan.cpp @@ -30,6 +30,7 @@ #include "../jrd/trace/TraceManager.h" #include "../jrd/trace/TraceJrdHelpers.h" #include "../jrd/optimizer/Optimizer.h" +#include "../jrd/recsrc/Cursor.h" #include "RecordSource.h" @@ -253,13 +254,38 @@ void ProcedureScan::getChildren(Array& children) const { } -void ProcedureScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void ProcedureScan::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + const auto statement = m_procedure->getStatement(); + + if (plan.isDetailed()) { plan += printIndent(++level) + "Procedure " + printName(tdbb, m_procedure->getName().toString(), m_alias) + " Scan"; printOptInfo(plan); + + if (statement && statement->fors.hasData() && + plan.goDeeper() && plan.mayRecurse(statement)) + { + PlanPrintContext nested(plan, statement); + ++level; + + for (const auto select : statement->fors) + select->print(tdbb, nested, level); + } + } + else if (statement && statement->fors.hasData() && + plan.goDeeper() && plan.mayRecurse(statement)) + { + PlanPrintContext nested(plan, statement); + ++level; + + for (const auto select : statement->fors) + { + plan += "("; + select->print(tdbb, nested, level); + plan += ")"; + } } else { diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index 2fc03e9a620..50e3f9b0e84 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -53,6 +53,59 @@ namespace Jrd enum JoinType { INNER_JOIN, OUTER_JOIN, SEMI_JOIN, ANTI_JOIN }; + // Plan print helper class + class PlanPrintContext : public Firebird::AutoStorage + { + public: + PlanPrintContext(const Database* dbb, Firebird::string& plan, + bool detailed, bool recurse) + : m_plan(plan), m_detailed(detailed), m_recurse(recurse), + m_depth(0), m_limit(dbb->dbb_config->getPlanRecursionLimit()), + m_statements(getPool()) + {} + + PlanPrintContext(const PlanPrintContext& other, const Statement* statement) + : m_plan(other.m_plan), m_detailed(other.m_detailed), m_recurse(other.m_recurse), + m_depth(other.m_depth + 1), m_limit(other.m_limit), + m_statements(getPool(), other.m_statements) + { + m_statements.add(statement); + } + + bool isDetailed() const + { + return m_detailed; + } + + bool goDeeper() const + { + return m_recurse; + } + + bool mayRecurse(const Statement* statement) const + { + return (m_depth < m_limit) && !m_statements.exist(statement); + } + + operator Firebird::string&() + { + return m_plan; + } + + void operator +=(const Firebird::string& plan) + { + m_plan += plan; + } + + private: + Firebird::string& m_plan; + const bool m_detailed; + const bool m_recurse; + const unsigned m_depth; + const unsigned m_limit; + Firebird::HalfStaticArray m_statements; + }; + // Common base for record sources, sub-queries and cursors. class AccessPath { @@ -71,8 +124,7 @@ namespace Jrd return m_recSourceId; } - virtual void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const = 0; + virtual void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const = 0; virtual void getChildren(Firebird::Array& children) const = 0; @@ -115,6 +167,8 @@ namespace Jrd bool getRecord(thread_db* tdbb) const; + static Firebird::string printIndent(unsigned level); + protected: // Generic impure block struct Impure @@ -134,7 +188,6 @@ namespace Jrd static Firebird::string printName(thread_db* tdbb, const Firebird::string& name, const Firebird::string& alias); - static Firebird::string printIndent(unsigned level); static void printInversion(thread_db* tdbb, const InversionNode* inversion, Firebird::string& plan, bool detailed, unsigned level, bool navigation = false); @@ -193,8 +246,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; protected: void internalOpen(thread_db* tdbb) const override; @@ -222,8 +274,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; protected: void internalOpen(thread_db* tdbb) const override; @@ -265,8 +316,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void setInversion(InversionNode* inversion, BoolExprNode* condition) { @@ -318,8 +368,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; protected: void internalOpen(thread_db* tdbb) const override; @@ -343,8 +392,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; protected: void internalOpen(thread_db* tdbb) const override; @@ -379,8 +427,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; protected: void internalOpen(thread_db* tdbb) const override; @@ -412,8 +459,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -444,8 +490,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -479,8 +524,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -519,8 +563,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -555,8 +598,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -676,8 +718,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -901,7 +942,7 @@ namespace Jrd public: void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; protected: bool internalGetRecord(thread_db* tdbb) const override; @@ -972,7 +1013,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void findUsedStreams(StreamList& streams, bool expandAll = false) const override; void nullRecords(thread_db* tdbb) const override; @@ -1014,8 +1055,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -1082,8 +1122,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -1126,8 +1165,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -1159,8 +1197,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -1213,8 +1250,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -1279,8 +1315,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -1315,8 +1350,7 @@ namespace Jrd bool refetchRecord(thread_db* tdbb) const override; WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; protected: void internalOpen(thread_db* tdbb) const override; @@ -1345,8 +1379,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -1390,8 +1423,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -1432,8 +1464,7 @@ namespace Jrd void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, - bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; diff --git a/src/jrd/recsrc/RecursiveStream.cpp b/src/jrd/recsrc/RecursiveStream.cpp index d40f3b3e24a..eb089035abe 100644 --- a/src/jrd/recsrc/RecursiveStream.cpp +++ b/src/jrd/recsrc/RecursiveStream.cpp @@ -244,17 +244,17 @@ void RecursiveStream::getChildren(Array& children) const children.add(m_inner); } -void RecursiveStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void RecursiveStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Recursion"; printOptInfo(plan); - if (recurse) + if (plan.goDeeper()) { - m_root->print(tdbb, plan, true, level, recurse); - m_inner->print(tdbb, plan, true, level, recurse); + m_root->print(tdbb, plan, level); + m_inner->print(tdbb, plan, level); } } else @@ -262,11 +262,11 @@ void RecursiveStream::print(thread_db* tdbb, string& plan, bool detailed, unsign if (!level) plan += "("; - m_root->print(tdbb, plan, false, level + 1, recurse); + m_root->print(tdbb, plan, level + 1); plan += ", "; - m_inner->print(tdbb, plan, false, level + 1, recurse); + m_inner->print(tdbb, plan, level + 1); if (!level) plan += ")"; diff --git a/src/jrd/recsrc/SingularStream.cpp b/src/jrd/recsrc/SingularStream.cpp index 99c0218e1c7..ca50ed97081 100644 --- a/src/jrd/recsrc/SingularStream.cpp +++ b/src/jrd/recsrc/SingularStream.cpp @@ -151,16 +151,16 @@ void SingularStream::getChildren(Array& children) const children.add(m_next); } -void SingularStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void SingularStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Singularity Check"; printOptInfo(plan); } - if (recurse) - m_next->print(tdbb, plan, detailed, level, recurse); + if (plan.goDeeper()) + m_next->print(tdbb, plan, level); } void SingularStream::markRecursive() diff --git a/src/jrd/recsrc/SkipRowsStream.cpp b/src/jrd/recsrc/SkipRowsStream.cpp index 297db0b7fb6..686e8eef788 100644 --- a/src/jrd/recsrc/SkipRowsStream.cpp +++ b/src/jrd/recsrc/SkipRowsStream.cpp @@ -121,16 +121,16 @@ void SkipRowsStream::getChildren(Array& children) const children.add(m_next); } -void SkipRowsStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void SkipRowsStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Skip N Records"; printOptInfo(plan); } - if (recurse) - m_next->print(tdbb, plan, detailed, level, recurse); + if (plan.goDeeper()) + m_next->print(tdbb, plan, level); } void SkipRowsStream::markRecursive() diff --git a/src/jrd/recsrc/SortedStream.cpp b/src/jrd/recsrc/SortedStream.cpp index fc5df6e9980..8f05c1fc9bc 100644 --- a/src/jrd/recsrc/SortedStream.cpp +++ b/src/jrd/recsrc/SortedStream.cpp @@ -126,10 +126,9 @@ void SortedStream::getChildren(Array& children) const children.add(m_next); } -void SortedStream::print(thread_db* tdbb, string& plan, - bool detailed, unsigned level, bool recurse) const +void SortedStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { string extras; extras.printf(" (record length: %" ULONGFORMAT", key length: %" ULONGFORMAT")", @@ -142,14 +141,14 @@ void SortedStream::print(thread_db* tdbb, string& plan, ((m_map->flags & FLAG_PROJECT) ? "Unique Sort" : "Sort") + extras; printOptInfo(plan); - if (recurse) - m_next->print(tdbb, plan, true, level, recurse); + if (plan.goDeeper()) + m_next->print(tdbb, plan, level); } else { level++; plan += "SORT ("; - m_next->print(tdbb, plan, false, level, recurse); + m_next->print(tdbb, plan, level); plan += ")"; } } diff --git a/src/jrd/recsrc/Union.cpp b/src/jrd/recsrc/Union.cpp index 804133f2eb1..845119c6cd2 100644 --- a/src/jrd/recsrc/Union.cpp +++ b/src/jrd/recsrc/Union.cpp @@ -168,17 +168,17 @@ void Union::getChildren(Array& children) const children.add(m_args[i]); } -void Union::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void Union::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + (m_args.getCount() == 1 ? "Materialize" : "Union"); printOptInfo(plan); - if (recurse) + if (plan.goDeeper()) { for (FB_SIZE_T i = 0; i < m_args.getCount(); i++) - m_args[i]->print(tdbb, plan, true, level, recurse); + m_args[i]->print(tdbb, plan, level); } } else @@ -191,7 +191,7 @@ void Union::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, if (i) plan += ", "; - m_args[i]->print(tdbb, plan, false, level + 1, recurse); + m_args[i]->print(tdbb, plan, level + 1); } if (!level) diff --git a/src/jrd/recsrc/VirtualTableScan.cpp b/src/jrd/recsrc/VirtualTableScan.cpp index 82dad5f80d1..f4058ce7f50 100644 --- a/src/jrd/recsrc/VirtualTableScan.cpp +++ b/src/jrd/recsrc/VirtualTableScan.cpp @@ -113,9 +113,9 @@ void VirtualTableScan::getChildren(Array& children) const { } -void VirtualTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void VirtualTableScan::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan"; diff --git a/src/jrd/recsrc/WindowedStream.cpp b/src/jrd/recsrc/WindowedStream.cpp index ce795f76bc3..97e1aff82d7 100644 --- a/src/jrd/recsrc/WindowedStream.cpp +++ b/src/jrd/recsrc/WindowedStream.cpp @@ -61,7 +61,7 @@ namespace void getChildren(Firebird::Array& children) const override; - void print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level, bool recurse) const override; + void print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const override; void markRecursive() override; void invalidateRecords(Request* request) const override; @@ -152,16 +152,16 @@ namespace children.add(m_next); } - void BufferedStreamWindow::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const + void BufferedStreamWindow::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Window Buffer"; printOptInfo(plan); } - if (recurse) - m_next->print(tdbb, plan, detailed, level, recurse); + if (plan.goDeeper()) + m_next->print(tdbb, plan, level); } void BufferedStreamWindow::markRecursive() @@ -409,16 +409,16 @@ void WindowedStream::getChildren(Array& children) const children.add(m_joinedStream); } -void WindowedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const +void WindowedStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Window"; printOptInfo(plan); } - if (recurse) - m_joinedStream->print(tdbb, plan, detailed, level, recurse); + if (plan.goDeeper()) + m_joinedStream->print(tdbb, plan, level); } void WindowedStream::markRecursive() @@ -904,17 +904,16 @@ void WindowedStream::WindowStream::getChildren(Array& child children.add(m_next); } -void WindowedStream::WindowStream::print(thread_db* tdbb, string& plan, bool detailed, - unsigned level, bool recurse) const +void WindowedStream::WindowStream::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - if (detailed) + if (plan.isDetailed()) { plan += printIndent(++level) + "Window Partition"; printOptInfo(plan); } - if (recurse) - m_next->print(tdbb, plan, detailed, level, recurse); + if (plan.goDeeper()) + m_next->print(tdbb, plan, level); } void WindowedStream::WindowStream::findUsedStreams(StreamList& streams, bool expandAll) const From 21addada2e2f59fbed8a3f106c6b763783729e47 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Mon, 14 Aug 2023 10:31:01 +0300 Subject: [PATCH 2/2] Fixed embedded legacy plans --- src/jrd/recsrc/Cursor.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/jrd/recsrc/Cursor.cpp b/src/jrd/recsrc/Cursor.cpp index f00619b1815..594223904ef 100644 --- a/src/jrd/recsrc/Cursor.cpp +++ b/src/jrd/recsrc/Cursor.cpp @@ -109,10 +109,10 @@ void Select::initializeInvariants(Request* request) const void Select::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) const { - plan += level ? RecordSource::printIndent(level) : "\n"; - if (plan.isDetailed()) { + plan += level ? RecordSource::printIndent(level) : "\n"; + if (m_rse->isSubQuery()) { plan += "Sub-query"; @@ -137,16 +137,16 @@ void Select::print(thread_db* tdbb, PlanPrintContext& plan, unsigned level) cons plan += pos; } } - else + else if (!level) { - if (!level && (m_line || m_column)) + if (m_line || m_column) { string pos; pos.printf("\n-- line %u, column %u", m_line, m_column); plan += pos; } - plan += "PLAN "; + plan += "\nPLAN "; } if (plan.goDeeper())