summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/query/cqf_command_utils.h4
-rw-r--r--src/mongo/db/query/cqf_get_executor.cpp4
-rw-r--r--src/mongo/db/query/optimizer/explain.cpp74
-rw-r--r--src/mongo/db/query/optimizer/explain.h7
-rw-r--r--src/mongo/db/query/optimizer/optimizer_test.cpp30
-rw-r--r--src/mongo/db/query/optimizer/utils/unit_test_utils.cpp2
-rw-r--r--src/mongo/db/query/optimizer/utils/unit_test_utils.h4
-rw-r--r--src/mongo/db/query/query_knobs.idl8
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 ]