diff options
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/query/cqf_command_utils.h | 4 | ||||
-rw-r--r-- | src/mongo/db/query/cqf_get_executor.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/explain.cpp | 74 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/explain.h | 7 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/optimizer_test.cpp | 30 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/utils/unit_test_utils.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/utils/unit_test_utils.h | 4 | ||||
-rw-r--r-- | src/mongo/db/query/query_knobs.idl | 8 |
8 files changed, 123 insertions, 10 deletions
diff --git a/src/mongo/db/query/cqf_command_utils.h b/src/mongo/db/query/cqf_command_utils.h index 4ceb333d364..5529afdc126 100644 --- a/src/mongo/db/query/cqf_command_utils.h +++ b/src/mongo/db/query/cqf_command_utils.h @@ -30,9 +30,9 @@ #pragma once #include "mongo/db/catalog/collection.h" +#include "mongo/db/query/query_knobs_gen.h" #define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kQuery -constexpr bool kMongoOptimizerStdCoutDebugOutput = false; namespace mongo { @@ -57,7 +57,7 @@ void coutPrint(const std::string& msg, const logv2::detail::NamedArg<Args>&... a #define OPTIMIZER_DEBUG_LOG(ID, DLEVEL, FMTSTR_MESSAGE, ...) \ LOGV2_DEBUG(ID, DLEVEL, FMTSTR_MESSAGE, ##__VA_ARGS__); \ - if (kMongoOptimizerStdCoutDebugOutput) \ + if (internalCascadesOptimizerStdCoutDebugOutput.load()) \ ::mongo::coutPrint(FMTSTR_MESSAGE, __VA_ARGS__); /** diff --git a/src/mongo/db/query/cqf_get_executor.cpp b/src/mongo/db/query/cqf_get_executor.cpp index 8e5daaec376..799d214caae 100644 --- a/src/mongo/db/query/cqf_get_executor.cpp +++ b/src/mongo/db/query/cqf_get_executor.cpp @@ -274,7 +274,7 @@ static std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> optimizeAndCreateExe } { - const std::string explain = ExplainGenerator::explainV2( + const std::string explain = ExplainGenerator::explainV2Compact( make<MemoPhysicalDelegatorNode>(phaseManager.getPhysicalNodeId()), true /*displayPhysicalProperties*/, &phaseManager.getMemo()); @@ -520,7 +520,7 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp } OPTIMIZER_DEBUG_LOG( - 6264803, 5, "Translated ABT", "explain"_attr = ExplainGenerator::explainV2(abt)); + 6264803, 5, "Translated ABT", "explain"_attr = ExplainGenerator::explainV2Compact(abt)); const int64_t numRecords = collectionExists ? collection->numRecords(opCtx) : -1; diff --git a/src/mongo/db/query/optimizer/explain.cpp b/src/mongo/db/query/optimizer/explain.cpp index 206b7bf0d8b..cc4e7cfab69 100644 --- a/src/mongo/db/query/optimizer/explain.cpp +++ b/src/mongo/db/query/optimizer/explain.cpp @@ -43,7 +43,7 @@ BSONObj ABTPrinter::explainBSON() const { _abtTree, true /*displayProperties*/, nullptr /*Memo*/, _nodeToPropsMap); } -enum class ExplainVersion { V1, V2, V3, Vmax }; +enum class ExplainVersion { V1, V2, V2Compact, V3, Vmax }; bool constexpr operator<(const ExplainVersion v1, const ExplainVersion v2) { return static_cast<int>(v1) < static_cast<int>(v2); @@ -72,6 +72,18 @@ struct CommandStruct { using CommandVector = std::vector<CommandStruct>; +/** + * Helper class for building indented, multiline strings. + * + * The main operations it supports are: + * - Print a single value, of any type that supports '<<' to std::ostream. + * - Indent/unindent, and add newlines. + * - Print another ExplainPrinterImpl, preserving its 2D layout. + * + * Being able to print another whole printer makes it easy to build these 2D strings + * bottom-up, without passing around a std::ostream. It also allows displaying + * child elements in a different order than they were visited. + */ template <const ExplainVersion version = kDefaultExplainVersion> class ExplainPrinterImpl { public: @@ -81,6 +93,7 @@ public: _osDirty(false), _indentCount(0), _childrenRemaining(0), + _inlineNextChild(false), _cmdInsertPos(-1) {} ~ExplainPrinterImpl() { @@ -101,6 +114,7 @@ public: _osDirty(other._osDirty), _indentCount(other._indentCount), _childrenRemaining(other._childrenRemaining), + _inlineNextChild(other._inlineNextChild), _cmdInsertPos(other._cmdInsertPos) {} template <class T> @@ -140,12 +154,20 @@ public: } ExplainPrinterImpl& setChildCount(const size_t childCount) { - if (version > ExplainVersion::V1) { + if (version == ExplainVersion::V1) { + return *this; + } + + if (version == ExplainVersion::V2Compact && childCount == 1) { + _inlineNextChild = true; _childrenRemaining = childCount; - indent(""); - for (int i = 0; i < _childrenRemaining - 1; i++) { - indent("|"); - } + return *this; + } + + _childrenRemaining = childCount; + indent(""); + for (int i = 0; i < _childrenRemaining - 1; i++) { + indent("|"); } return *this; } @@ -205,6 +227,10 @@ public: return os.str(); } + /** + * Ends the current line, if there is one. Repeated calls do not create + * blank lines. + */ void newLine() { if (!_osDirty) { return; @@ -251,8 +277,25 @@ private: _os << element._str; } } + } else if (_inlineNextChild) { + _inlineNextChild = false; + // Print 'other' without starting a new line. + // Embed its first line into our current one, and keep the rest of its commands. + bool first = true; + for (const CommandStruct& element : other.getCommands()) { + if (first && element._type == CommandType::AddLine) { + _os << singleLevelSpacer << element._str; + } else { + newLine(); + _cmd.push_back(element); + } + first = false; + } } else { newLine(); + // If 'hadChildrenRemaining' then 'other' represents a child of 'this', which means + // there was a prior call to setChildCount() that added indentation for it. + // If '! hadChildrenRemaining' then create indentation for it now. if (!hadChildrenRemaining) { indent(); } @@ -281,11 +324,22 @@ private: _cmd.emplace_back(CommandType::Unindent, ""); } + // Holds completed lines, and indent/unIndent commands. + // When '_cmdInsertPos' is nonnegative, some of these lines and commands belong + // after the currently-being-built line. CommandVector _cmd; + // Holds the incomplete line currently being built. Once complete this will become the last + // line, unless '_cmdInsertPos' is nonnegative. std::ostringstream _os; + // True means we have an incomplete line in '_os'. + // Once the line is completed with newLine(), this flag is false until + // we begin building a new one with print(). bool _osDirty; int _indentCount; int _childrenRemaining; + bool _inlineNextChild; + // When nonnegative, indicates the insertion point where completed lines + // should be added to '_cmd'. -1 means completed lines will be added at the end. int _cmdInsertPos; }; @@ -2305,6 +2359,14 @@ std::string ExplainGenerator::explainV2(const ABT& node, return gen.generate(node).str(); } +std::string ExplainGenerator::explainV2Compact(const ABT& node, + const bool displayProperties, + const cascades::Memo* memo, + const NodeToGroupPropsMap& nodeMap) { + ExplainGeneratorTransporter<ExplainVersion::V2Compact> gen(displayProperties, memo, nodeMap); + return gen.generate(node).str(); +} + std::string ExplainGenerator::explainNode(const ABT& node) { if (node.empty()) { return "Empty\n"; diff --git a/src/mongo/db/query/optimizer/explain.h b/src/mongo/db/query/optimizer/explain.h index 5cc0f29fafe..dfc8cdda77d 100644 --- a/src/mongo/db/query/optimizer/explain.h +++ b/src/mongo/db/query/optimizer/explain.h @@ -75,6 +75,13 @@ public: const cascades::Memo* memo = nullptr, const NodeToGroupPropsMap& nodeMap = {}); + // Optionally display logical and physical properties using the memo. + // whenever memo delegators are printed. + static std::string explainV2Compact(const ABT& node, + bool displayProperties = false, + const cascades::Memo* memo = nullptr, + const NodeToGroupPropsMap& nodeMap = {}); + static std::string explainNode(const ABT& node); static std::pair<sbe::value::TypeTags, sbe::value::Value> explainBSON( diff --git a/src/mongo/db/query/optimizer/optimizer_test.cpp b/src/mongo/db/query/optimizer/optimizer_test.cpp index 5e1534fe936..d3ec1ca24a5 100644 --- a/src/mongo/db/query/optimizer/optimizer_test.cpp +++ b/src/mongo/db/query/optimizer/optimizer_test.cpp @@ -698,5 +698,35 @@ TEST(Properties, Basic) { } } +TEST(Explain, ExplainV2Compact) { + ABT pathNode = + make<PathGet>("a", + make<PathTraverse>( + make<PathComposeM>( + make<PathCompare>(Operations::Gte, + make<UnaryOp>(Operations::Neg, Constant::int64(2))), + make<PathCompare>(Operations::Lt, Constant::int64(7))), + 1)); + ABT scanNode = make<ScanNode>("x1", "test"); + ABT evalNode = make<EvaluationNode>( + "x2", make<EvalPath>(pathNode, make<Variable>("a")), std::move(scanNode)); + + ASSERT_EXPLAIN_V2Compact( + "Evaluation []\n" + "| BindBlock:\n" + "| [x2]\n" + "| EvalPath []\n" + "| | Variable [a]\n" + "| PathGet [a] PathTraverse [1] PathComposeM []\n" + "| | PathCompare [Lt] Const [7]\n" + "| PathCompare [Gte] UnaryOp [Neg] Const [2]\n" + "Scan [test]\n" + " BindBlock:\n" + " [x1]\n" + " Source []\n", + evalNode); +} + + } // namespace } // namespace mongo::optimizer diff --git a/src/mongo/db/query/optimizer/utils/unit_test_utils.cpp b/src/mongo/db/query/optimizer/utils/unit_test_utils.cpp index 01882a4ff22..1bd82ce80f5 100644 --- a/src/mongo/db/query/optimizer/utils/unit_test_utils.cpp +++ b/src/mongo/db/query/optimizer/utils/unit_test_utils.cpp @@ -45,12 +45,14 @@ void maybePrintABT(const ABT& abt) { // Always print using the supported versions to make sure we don't crash. const std::string strV1 = ExplainGenerator::explain(abt); const std::string strV2 = ExplainGenerator::explainV2(abt); + const std::string strV2Compact = ExplainGenerator::explainV2Compact(abt); auto [tag, val] = ExplainGenerator::explainBSON(abt); sbe::value::ValueGuard vg(tag, val); if constexpr (kDebugAsserts) { std::cout << "V1: " << strV1 << "\n"; std::cout << "V2: " << strV2 << "\n"; + std::cout << "V2Compact: " << strV2Compact << "\n"; std::cout << "BSON: " << ExplainGenerator::printBSON(tag, val) << "\n"; } } diff --git a/src/mongo/db/query/optimizer/utils/unit_test_utils.h b/src/mongo/db/query/optimizer/utils/unit_test_utils.h index 24a32017416..f25df1a11b0 100644 --- a/src/mongo/db/query/optimizer/utils/unit_test_utils.h +++ b/src/mongo/db/query/optimizer/utils/unit_test_utils.h @@ -49,6 +49,10 @@ void maybePrintABT(const ABT& abt); maybePrintABT(abt); \ ASSERT_EQ(expected, ExplainGenerator::explainV2(abt)) +#define ASSERT_EXPLAIN_V2Compact(expected, abt) \ + maybePrintABT(abt); \ + ASSERT_EQ(expected, ExplainGenerator::explainV2Compact(abt)) + #define ASSERT_EXPLAIN_BSON(expected, abt) \ maybePrintABT(abt); \ ASSERT_EQ(expected, ExplainGenerator::explainBSON(abt)) diff --git a/src/mongo/db/query/query_knobs.idl b/src/mongo/db/query/query_knobs.idl index 3edf0e05cc9..efbe81f8501 100644 --- a/src/mongo/db/query/query_knobs.idl +++ b/src/mongo/db/query/query_knobs.idl @@ -790,6 +790,14 @@ server_parameters: cpp_vartype: AtomicWord<bool> default: false + internalCascadesOptimizerStdCoutDebugOutput: + description: "Enables verbose, non-JSON, debug output for Cascades optimizer." + set_at: [ startup, runtime ] + cpp_varname: "internalCascadesOptimizerStdCoutDebugOutput" + cpp_vartype: AtomicWord<bool> + default: false + test_only: true + internalQueryFrameworkControl: description: "Knob to control the optimizer/execution engine to use." set_at: [ startup, runtime ] |