summaryrefslogtreecommitdiff
path: root/src/mongo/db/query
diff options
context:
space:
mode:
authorSvilen Mihaylov <svilen.mihaylov@mongodb.com>2023-02-10 00:29:20 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-10 03:07:27 +0000
commita6babc2a1789fae2eb6057152fca59b3da2ab778 (patch)
tree88a9ac3199b9db45ecc4deb3f43247366f8c51a8 /src/mongo/db/query
parent53478bd7dce5f14708fb35db41293df1ea79db33 (diff)
downloadmongo-a6babc2a1789fae2eb6057152fca59b3da2ab778.tar.gz
SERVER-73102 [CQF] Simplify explain for evaluation of simple constants
Diffstat (limited to 'src/mongo/db/query')
-rw-r--r--src/mongo/db/query/cost_model/cost_estimator_impl.cpp18
-rw-r--r--src/mongo/db/query/cost_model/cost_model_test.cpp3
-rw-r--r--src/mongo/db/query/optimizer/explain.cpp32
-rw-r--r--src/mongo/db/query/optimizer/interval_simplify_test.cpp21
-rw-r--r--src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp140
-rw-r--r--src/mongo/db/query/optimizer/optimizer_test.cpp18
-rw-r--r--src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp85
-rw-r--r--src/mongo/db/query/optimizer/rewrites/path_optimizer_test.cpp3
-rw-r--r--src/mongo/db/query/optimizer/utils/path_utils.h17
9 files changed, 110 insertions, 227 deletions
diff --git a/src/mongo/db/query/cost_model/cost_estimator_impl.cpp b/src/mongo/db/query/cost_model/cost_estimator_impl.cpp
index 9f797997727..6daf1d972d7 100644
--- a/src/mongo/db/query/cost_model/cost_estimator_impl.cpp
+++ b/src/mongo/db/query/cost_model/cost_estimator_impl.cpp
@@ -30,6 +30,8 @@
#include "mongo/db/query/cost_model/cost_estimator_impl.h"
#include "mongo/db/query/optimizer/defs.h"
+#include "mongo/db/query/optimizer/utils/path_utils.h"
+
namespace mongo::cost_model {
@@ -119,7 +121,7 @@ public:
CostAndCEInternal operator()(const ABT& /*n*/, const FilterNode& node) {
CostAndCEInternal childResult = deriveChild(node.getChild(), 0);
double filterCost = childResult._cost;
- if (!isTrivialExpr<EvalFilter>(node.getFilter())) {
+ if (getTrivialExprPtr<EvalFilter>(node.getFilter()).empty()) {
// Non-trivial filter.
filterCost += _coefficients.getFilterStartupCost() +
_coefficients.getFilterIncrementalCost() * childResult._ce._value;
@@ -130,7 +132,7 @@ public:
CostAndCEInternal operator()(const ABT& /*n*/, const EvaluationNode& node) {
CostAndCEInternal childResult = deriveChild(node.getChild(), 0);
double evalCost = childResult._cost;
- if (!isTrivialExpr<EvalPath>(node.getProjection())) {
+ if (getTrivialExprPtr<EvalPath>(node.getProjection()).empty()) {
// Non-trivial projection.
evalCost += _coefficients.getEvalStartupCost() +
_coefficients.getEvalIncrementalCost() * _cardinalityEstimate._value;
@@ -364,18 +366,6 @@ private:
_nodeCEMap(nodeCEMap),
_coefficients(coefficients) {}
- template <class T>
- static bool isTrivialExpr(const ABT& n) {
- if (n.is<Constant>() || n.is<Variable>()) {
- return true;
- }
- if (const auto* ptr = n.cast<T>(); ptr != nullptr &&
- ptr->getPath().template is<PathIdentity>() && isTrivialExpr<T>(ptr->getInput())) {
- return true;
- }
- return false;
- }
-
static CostAndCEInternal deriveInternal(const Metadata& metadata,
const Memo& memo,
const PhysProps& physProps,
diff --git a/src/mongo/db/query/cost_model/cost_model_test.cpp b/src/mongo/db/query/cost_model/cost_model_test.cpp
index 2c1a19cbfe4..b8fd107fa56 100644
--- a/src/mongo/db/query/cost_model/cost_model_test.cpp
+++ b/src/mongo/db/query/cost_model/cost_model_test.cpp
@@ -169,8 +169,7 @@ TEST(CostModel, IncreaseJoinsCost) {
"| | Collation\n"
"| | Ascending\n"
"| Union [{rid_1}]\n"
- "| Evaluation [{rid_1}]\n"
- "| | Variable [rid_0]\n"
+ "| Evaluation [{rid_1} = Variable [rid_0]]\n"
"| IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index2, interval: "
"{=Const [2]}]\n"
"IndexScan [{'<indexKey> 0': pa, '<rid>': rid_0}, scanDefName: c1, indexDefName: "
diff --git a/src/mongo/db/query/optimizer/explain.cpp b/src/mongo/db/query/optimizer/explain.cpp
index 581ad743da4..806771fc6e2 100644
--- a/src/mongo/db/query/optimizer/explain.cpp
+++ b/src/mongo/db/query/optimizer/explain.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/query/optimizer/cascades/rewriter_rules.h"
#include "mongo/db/query/optimizer/defs.h"
#include "mongo/db/query/optimizer/node.h"
+#include "mongo/db/query/optimizer/utils/path_utils.h"
#include "mongo/util/assert_util.h"
@@ -1223,23 +1224,32 @@ public:
ExplainPrinter projectionResult) {
ExplainPrinter printer("Evaluation");
maybePrintProps(printer, node);
- printer.separator(" [");
- // The bind block (projectionResult) is empty in V1-V2 explains. In the case of the
- // Evaluation node, the bind block may have useful information about the embedded
- // expression, so we make sure to print the projected expression.
if constexpr (version < ExplainVersion::V3) {
+ const ABT& expr = node.getProjection();
+
+ printer.separator(" [");
+ // The bind block (projectionResult) is empty in V1-V2 explains. In the case of the
+ // Evaluation node, the bind block may have useful information about the embedded
+ // expression, so we make sure to print the projected expression.
printProjection(printer, node.getProjectionName());
- }
+ if (const auto ref = getTrivialExprPtr<EvalPath>(expr); !ref.empty()) {
+ ExplainPrinter local = generate(ref);
+ printer.separator(" = ").printSingleLevel(local).separator("]");
- printer.separator("]");
- nodeCEPropsPrint(printer, n, node);
- printer.setChildCount(2);
+ nodeCEPropsPrint(printer, n, node);
+ printer.setChildCount(1, true /*noInline*/);
+ } else {
+ printer.separator("]");
- if constexpr (version < ExplainVersion::V3) {
- auto pathPrinter = generate(node.getProjection());
- printer.print(pathPrinter);
+ nodeCEPropsPrint(printer, n, node);
+ printer.setChildCount(2);
+
+ auto pathPrinter = generate(expr);
+ printer.print(pathPrinter);
+ }
} else if constexpr (version == ExplainVersion::V3) {
+ nodeCEPropsPrint(printer, n, node);
printer.fieldName("projection").print(projectionResult);
} else {
MONGO_UNREACHABLE;
diff --git a/src/mongo/db/query/optimizer/interval_simplify_test.cpp b/src/mongo/db/query/optimizer/interval_simplify_test.cpp
index 92aea9eec42..8bd2acf5a38 100644
--- a/src/mongo/db/query/optimizer/interval_simplify_test.cpp
+++ b/src/mongo/db/query/optimizer/interval_simplify_test.cpp
@@ -120,8 +120,7 @@ TEST_F(IntervalIntersection, SingleFieldIntersection) {
const std::string q4Text = "{$and: [{a0: {$gt:20}}, {a0: {$lt: 20}}]}";
ASSERT_EXPLAIN_V2_AUTO(
"Root [{scan_0}]\n"
- "Evaluation [{scan_0}]\n"
- "| Const [Nothing]\n"
+ "Evaluation [{scan_0} = Const [Nothing]]\n"
"LimitSkip [limit: 0, skip: 0]\n"
"CoScan []\n",
optimizedQueryPlan(q4Text, testIndex));
@@ -146,8 +145,7 @@ TEST_F(IntervalIntersection, SingleFieldIntersection) {
"40}}]}]}";
ASSERT_EXPLAIN_V2_AUTO(
"Root [{scan_0}]\n"
- "Evaluation [{scan_0}]\n"
- "| Const [Nothing]\n"
+ "Evaluation [{scan_0} = Const [Nothing]]\n"
"LimitSkip [limit: 0, skip: 0]\n"
"CoScan []\n",
optimizedQueryPlan(q6Text, testIndex));
@@ -158,8 +156,7 @@ TEST_F(IntervalIntersection, SingleFieldIntersection) {
"42}}]}]}";
ASSERT_EXPLAIN_V2_AUTO(
"Root [{scan_0}]\n"
- "Evaluation [{scan_0}]\n"
- "| Const [Nothing]\n"
+ "Evaluation [{scan_0} = Const [Nothing]]\n"
"LimitSkip [limit: 0, skip: 0]\n"
"CoScan []\n",
optimizedQueryPlan(q7Text, testIndex));
@@ -178,8 +175,7 @@ TEST_F(IntervalIntersection, MultiFieldIntersection) {
"{$and: [{a0: {$gt: 11}}, {a0: {$lt: 14}}, {b0: {$gt: 21}}, {b0: {$lt: 12}}]}";
ASSERT_EXPLAIN_V2_AUTO(
"Root [{scan_0}]\n"
- "Evaluation [{scan_0}]\n"
- "| Const [Nothing]\n"
+ "Evaluation [{scan_0} = Const [Nothing]]\n"
"LimitSkip [limit: 0, skip: 0]\n"
"CoScan []\n",
optimizedQueryPlan(q1Text, testIndex));
@@ -188,8 +184,7 @@ TEST_F(IntervalIntersection, MultiFieldIntersection) {
"{$and: [{a0: {$gt: 14}}, {a0: {$lt: 11}}, {b0: {$gt: 12}}, {b0: {$lt: 21}}]}";
ASSERT_EXPLAIN_V2_AUTO(
"Root [{scan_0}]\n"
- "Evaluation [{scan_0}]\n"
- "| Const [Nothing]\n"
+ "Evaluation [{scan_0} = Const [Nothing]]\n"
"LimitSkip [limit: 0, skip: 0]\n"
"CoScan []\n",
optimizedQueryPlan(q2Text, testIndex));
@@ -198,8 +193,7 @@ TEST_F(IntervalIntersection, MultiFieldIntersection) {
"{$and: [{a0: {$gt: 14}}, {a0: {$lt: 11}}, {b0: {$gt: 21}}, {b0: {$lt: 12}}]}";
ASSERT_EXPLAIN_V2_AUTO(
"Root [{scan_0}]\n"
- "Evaluation [{scan_0}]\n"
- "| Const [Nothing]\n"
+ "Evaluation [{scan_0} = Const [Nothing]]\n"
"LimitSkip [limit: 0, skip: 0]\n"
"CoScan []\n",
optimizedQueryPlan(q3Text, testIndex));
@@ -207,8 +201,7 @@ TEST_F(IntervalIntersection, MultiFieldIntersection) {
const std::string q4Text = "{$and: [{a0: 42}, {b0: {$gt: 21}}, {b0: {$lt: 12}}]}";
ASSERT_EXPLAIN_V2_AUTO(
"Root [{scan_0}]\n"
- "Evaluation [{scan_0}]\n"
- "| Const [Nothing]\n"
+ "Evaluation [{scan_0} = Const [Nothing]]\n"
"LimitSkip [limit: 0, skip: 0]\n"
"CoScan []\n",
optimizedQueryPlan(q4Text, testIndex));
diff --git a/src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp b/src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp
index c61ffab4d4a..ca9349d915b 100644
--- a/src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp
+++ b/src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp
@@ -240,10 +240,7 @@ TEST(LogicalRewriter, FilterProjectRewrite) {
" EvalFilter []\n"
" PathIdentity []\n"
" Variable [P1]\n"
- " Evaluation [{P1}]\n"
- " EvalPath []\n"
- " PathIdentity []\n"
- " Variable [ptest]\n"
+ " Evaluation [{P1} = Variable [ptest]]\n"
" Collation []\n"
" collation: \n"
" ptest: Ascending\n"
@@ -267,10 +264,7 @@ TEST(LogicalRewriter, FilterProjectRewrite) {
" EvalFilter []\n"
" PathIdentity []\n"
" Variable [P1]\n"
- " Evaluation [{P1}]\n"
- " EvalPath []\n"
- " PathIdentity []\n"
- " Variable [ptest]\n"
+ " Evaluation [{P1} = Variable [ptest]]\n"
" Scan [test, {ptest}]\n",
latest);
}
@@ -322,21 +316,12 @@ TEST(LogicalRewriter, FilterProjectComplexRewrite) {
"| EvalFilter []\n"
"| | Variable [p1]\n"
"| PathIdentity []\n"
- "Evaluation [{p1}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
+ "Evaluation [{p1} = Variable [ptest]]\n"
"Collation []\n"
"| | collation: \n"
"| | ptest: Ascending\n"
- "Evaluation [{p3}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
- "Evaluation [{p2}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
+ "Evaluation [{p3} = Variable [ptest]]\n"
+ "Evaluation [{p2} = Variable [ptest]]\n"
"Scan [test, {ptest}]\n",
rootNode);
@@ -366,18 +351,9 @@ TEST(LogicalRewriter, FilterProjectComplexRewrite) {
"| EvalFilter []\n"
"| | Variable [p1]\n"
"| PathIdentity []\n"
- "Evaluation [{p1}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
- "Evaluation [{p3}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
- "Evaluation [{p2}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
+ "Evaluation [{p1} = Variable [ptest]]\n"
+ "Evaluation [{p3} = Variable [ptest]]\n"
+ "Evaluation [{p2} = Variable [ptest]]\n"
"Scan [test, {ptest}]\n",
latest);
}
@@ -420,18 +396,12 @@ TEST(LogicalRewriter, FilterProjectGroupRewrite) {
"| aggregations: \n"
"| [c]\n"
"| Variable [b]\n"
- "Evaluation [{b}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
+ "Evaluation [{b} = Variable [ptest]]\n"
"Filter []\n"
"| EvalFilter []\n"
"| | Variable [a]\n"
"| PathIdentity []\n"
- "Evaluation [{a}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
+ "Evaluation [{a} = Variable [ptest]]\n"
"Scan [test, {ptest}]\n",
latest);
}
@@ -482,14 +452,8 @@ TEST(LogicalRewriter, FilterProjectUnwindRewrite) {
"| | Variable [a]\n"
"| PathIdentity []\n"
"Unwind [{a, a_pid}]\n"
- "Evaluation [{b}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
- "Evaluation [{a}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
+ "Evaluation [{b} = Variable [ptest]]\n"
+ "Evaluation [{a} = Variable [ptest]]\n"
"Scan [test, {ptest}]\n",
latest);
}
@@ -527,10 +491,7 @@ TEST(LogicalRewriter, FilterProjectExchangeRewrite) {
ASSERT_EXPLAIN_V2_AUTO(
"Root [{a, b}]\n"
- "Evaluation [{b}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
+ "Evaluation [{b} = Variable [ptest]]\n"
"Exchange []\n"
"| | distribution: \n"
"| | type: HashPartitioning\n"
@@ -540,10 +501,7 @@ TEST(LogicalRewriter, FilterProjectExchangeRewrite) {
"| EvalFilter []\n"
"| | Variable [a]\n"
"| PathIdentity []\n"
- "Evaluation [{a}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
+ "Evaluation [{a} = Variable [ptest]]\n"
"Scan [test, {ptest}]\n",
latest);
}
@@ -589,14 +547,8 @@ TEST(LogicalRewriter, UnwindCollationRewrite) {
"| | collation: \n"
"| | b: Ascending\n"
"Unwind [{a, a_pid}]\n"
- "Evaluation [{b}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
- "Evaluation [{a}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
+ "Evaluation [{b} = Variable [ptest]]\n"
+ "Evaluation [{a} = Variable [ptest]]\n"
"Scan [test, {ptest}]\n",
latest);
}
@@ -639,15 +591,9 @@ TEST(LogicalRewriter, FilterUnionReorderSingleProjection) {
"| PathCompare [Eq]\n"
"| Const [1]\n"
"Union [{pUnion}]\n"
- "| Evaluation [{pUnion}]\n"
- "| | EvalPath []\n"
- "| | | Variable [ptest2]\n"
- "| | PathIdentity []\n"
+ "| Evaluation [{pUnion} = Variable [ptest2]]\n"
"| Scan [test2, {ptest2}]\n"
- "Evaluation [{pUnion}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest1]\n"
- "| PathIdentity []\n"
+ "Evaluation [{pUnion} = Variable [ptest1]]\n"
"Scan [test1, {ptest1}]\n",
latest);
@@ -669,10 +615,7 @@ TEST(LogicalRewriter, FilterUnionReorderSingleProjection) {
"| | PathTraverse [1]\n"
"| | PathCompare [Eq]\n"
"| | Const [1]\n"
- "| Evaluation [{pUnion}]\n"
- "| | EvalPath []\n"
- "| | | Variable [ptest2]\n"
- "| | PathIdentity []\n"
+ "| Evaluation [{pUnion} = Variable [ptest2]]\n"
"| Scan [test2, {ptest2}]\n"
"Filter []\n"
"| EvalFilter []\n"
@@ -681,10 +624,7 @@ TEST(LogicalRewriter, FilterUnionReorderSingleProjection) {
"| PathTraverse [1]\n"
"| PathCompare [Eq]\n"
"| Const [1]\n"
- "Evaluation [{pUnion}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest1]\n"
- "| PathIdentity []\n"
+ "Evaluation [{pUnion} = Variable [ptest1]]\n"
"Scan [test1, {ptest1}]\n",
latest);
}
@@ -754,23 +694,11 @@ TEST(LogicalRewriter, MultipleFilterUnionReorder) {
"| PathCompare [Eq]\n"
"| Const [1]\n"
"Union [{pUnion1, pUnion2}]\n"
- "| Evaluation [{pUnion2}]\n"
- "| | EvalPath []\n"
- "| | | Variable [ptest2]\n"
- "| | PathIdentity []\n"
- "| Evaluation [{pUnion1}]\n"
- "| | EvalPath []\n"
- "| | | Variable [ptest2]\n"
- "| | PathIdentity []\n"
+ "| Evaluation [{pUnion2} = Variable [ptest2]]\n"
+ "| Evaluation [{pUnion1} = Variable [ptest2]]\n"
"| Scan [test2, {ptest2}]\n"
- "Evaluation [{pUnion2}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest1]\n"
- "| PathIdentity []\n"
- "Evaluation [{pUnion1}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest1]\n"
- "| PathIdentity []\n"
+ "Evaluation [{pUnion2} = Variable [ptest1]]\n"
+ "Evaluation [{pUnion1} = Variable [ptest1]]\n"
"Scan [test1, {ptest1}]\n",
latest);
@@ -792,10 +720,7 @@ TEST(LogicalRewriter, MultipleFilterUnionReorder) {
"| | PathTraverse [1]\n"
"| | PathCompare [Eq]\n"
"| | Const [1]\n"
- "| Evaluation [{pUnion2}]\n"
- "| | EvalPath []\n"
- "| | | Variable [ptest2]\n"
- "| | PathIdentity []\n"
+ "| Evaluation [{pUnion2} = Variable [ptest2]]\n"
"| Filter []\n"
"| | EvalFilter []\n"
"| | | Variable [pUnion1]\n"
@@ -803,10 +728,7 @@ TEST(LogicalRewriter, MultipleFilterUnionReorder) {
"| | PathTraverse [1]\n"
"| | PathCompare [Eq]\n"
"| | Const [1]\n"
- "| Evaluation [{pUnion1}]\n"
- "| | EvalPath []\n"
- "| | | Variable [ptest2]\n"
- "| | PathIdentity []\n"
+ "| Evaluation [{pUnion1} = Variable [ptest2]]\n"
"| Scan [test2, {ptest2}]\n"
"Filter []\n"
"| EvalFilter []\n"
@@ -815,10 +737,7 @@ TEST(LogicalRewriter, MultipleFilterUnionReorder) {
"| PathTraverse [1]\n"
"| PathCompare [Eq]\n"
"| Const [1]\n"
- "Evaluation [{pUnion2}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest1]\n"
- "| PathIdentity []\n"
+ "Evaluation [{pUnion2} = Variable [ptest1]]\n"
"Filter []\n"
"| EvalFilter []\n"
"| | Variable [pUnion1]\n"
@@ -826,10 +745,7 @@ TEST(LogicalRewriter, MultipleFilterUnionReorder) {
"| PathTraverse [1]\n"
"| PathCompare [Eq]\n"
"| Const [1]\n"
- "Evaluation [{pUnion1}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest1]\n"
- "| PathIdentity []\n"
+ "Evaluation [{pUnion1} = Variable [ptest1]]\n"
"Scan [test1, {ptest1}]\n",
latest);
}
diff --git a/src/mongo/db/query/optimizer/optimizer_test.cpp b/src/mongo/db/query/optimizer/optimizer_test.cpp
index 2b2deaca2ce..d6646b332dc 100644
--- a/src/mongo/db/query/optimizer/optimizer_test.cpp
+++ b/src/mongo/db/query/optimizer/optimizer_test.cpp
@@ -351,12 +351,9 @@ TEST(Optimizer, GroupBy) {
" Const [10]\n"
" [a2]\n"
" Const [11]\n"
- " Evaluation [{p3}]\n"
- " Const [3]\n"
- " Evaluation [{p2}]\n"
- " Const [2]\n"
- " Evaluation [{p1}]\n"
- " Const [1]\n"
+ " Evaluation [{p3} = Const [3]]\n"
+ " Evaluation [{p2} = Const [2]]\n"
+ " Evaluation [{p1} = Const [1]]\n"
" Scan [test, {ptest}]\n",
rootNode);
@@ -400,14 +397,11 @@ TEST(Optimizer, Union) {
ASSERT_EXPLAIN_AUTO(
"Root [{B, ptest}]\n"
" Union [{B, ptest}]\n"
- " Evaluation [{B}]\n"
- " Const [3]\n"
+ " Evaluation [{B} = Const [3]]\n"
" Scan [test, {ptest}]\n"
- " Evaluation [{B}]\n"
- " Const [4]\n"
+ " Evaluation [{B} = Const [4]]\n"
" Scan [test, {ptest}]\n"
- " Evaluation [{B}]\n"
- " Const [5]\n"
+ " Evaluation [{B} = Const [5]]\n"
" Evaluation [{ptest}]\n"
" EvalPath []\n"
" PathConstant []\n"
diff --git a/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp b/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp
index 2ae1dbe73c3..ce5b4d8d398 100644
--- a/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp
+++ b/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp
@@ -91,10 +91,7 @@ TEST(PhysRewriter, PhysicalRewriterBasic) {
"| PathGet [a]\n"
"| PathCompare [Eq]\n"
"| Const [1]\n"
- "Evaluation [{p2}]\n"
- "| EvalPath []\n"
- "| | Variable [p1]\n"
- "| PathIdentity []\n"
+ "Evaluation [{p2} = Variable [p1]]\n"
"Filter []\n"
"| EvalFilter []\n"
"| | Variable [p1]\n"
@@ -174,10 +171,7 @@ TEST(PhysRewriter, PhysicalRewriterBasic) {
"| type: Centralized, disableExchanges\n"
"| indexingRequirement: \n"
"| Complete, dedupRID\n"
- "Evaluation [{p2}]\n"
- "| EvalPath []\n"
- "| | Variable [p1]\n"
- "| PathIdentity []\n"
+ "Evaluation [{p2} = Variable [p1]]\n"
"Properties [cost: 0.428487, localCost: 0, adjustedCE: 100]\n"
"| | Logical:\n"
"| | cardinalityEstimate: \n"
@@ -280,14 +274,8 @@ TEST(PhysRewriter, GroupBy) {
"| aggregations: \n"
"| [c]\n"
"| Variable [b]\n"
- "Evaluation [{b}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
- "Evaluation [{a}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
+ "Evaluation [{b} = Variable [ptest]]\n"
+ "Evaluation [{a} = Variable [ptest]]\n"
"PhysicalScan [{'<root>': ptest}, test]\n",
optimized);
}
@@ -341,8 +329,7 @@ TEST(PhysRewriter, GroupBy1) {
"| aggregations: \n"
"| [pb]\n"
"| Variable [pa]\n"
- "Evaluation [{pa}]\n"
- "| Const [null]\n"
+ "Evaluation [{pa} = Const [null]]\n"
"PhysicalScan [{}, test]\n",
optimized);
}
@@ -398,14 +385,8 @@ TEST(PhysRewriter, Unwind) {
"| | Variable [a]\n"
"| PathIdentity []\n"
"Unwind [{a, a_pid}]\n"
- "Evaluation [{b}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
- "Evaluation [{a}]\n"
- "| EvalPath []\n"
- "| | Variable [ptest]\n"
- "| PathIdentity []\n"
+ "Evaluation [{b} = Variable [ptest]]\n"
+ "Evaluation [{a} = Variable [ptest]]\n"
"PhysicalScan [{'<root>': ptest}, test]\n",
optimized);
}
@@ -1222,10 +1203,7 @@ TEST(PhysRewriter, FilterIndexing5) {
"| | Variable [pb]\n"
"| PathCompare [Gt]\n"
"| Const [0]\n"
- "Evaluation [{pb}]\n"
- "| EvalPath []\n"
- "| | Variable [evalTemp_0]\n"
- "| PathIdentity []\n"
+ "Evaluation [{pb} = Variable [evalTemp_0]]\n"
"IndexScan [{'<indexKey> 0': pa, '<indexKey> 1': evalTemp_0}, scanDefName: c1, indexDefNa"
"me: index1, interval: {>Const [0 | maxKey]}]\n",
optimized);
@@ -1841,8 +1819,7 @@ TEST(PhysRewriter, SargableProjectionRenames) {
// projections.
ASSERT_EXPLAIN_V2_AUTO(
"Root [{root}]\n"
- "Evaluation [{pa1}]\n"
- "| Variable [pa]\n"
+ "Evaluation [{pa1} = Variable [pa]]\n"
"Sargable [Complete]\n"
"| | | | requirementsMap: \n"
"| | | | refProjection: root, path: 'PathGet [a] PathIdentity []', boundProje"
@@ -2218,8 +2195,7 @@ TEST(PhysRewriter, EvalIndexing2) {
// Verify collation is subsumed into the index scan.
ASSERT_EXPLAIN_V2_AUTO(
"Root [{pa2}]\n"
- "Evaluation [{pa3}]\n"
- "| Variable [pa1]\n"
+ "Evaluation [{pa3} = Variable [pa1]]\n"
"Evaluation [{pa2}]\n"
"| EvalPath []\n"
"| | Const [0]\n"
@@ -2329,20 +2305,14 @@ TEST(PhysRewriter, MultiKeyIndex) {
"| FunctionCall [$addToSet]\n"
"| Variable [sideId_0]\n"
"Union [{rid_0, sideId_0, unionTemp_0, unionTemp_1}]\n"
- "| Evaluation [{unionTemp_1}]\n"
- "| | Variable [pb]\n"
- "| Evaluation [{unionTemp_0}]\n"
- "| | Const [Nothing]\n"
- "| Evaluation [{sideId_0}]\n"
- "| | Const [1]\n"
+ "| Evaluation [{unionTemp_1} = Variable [pb]]\n"
+ "| Evaluation [{unionTemp_0} = Const [Nothing]]\n"
+ "| Evaluation [{sideId_0} = Const [1]]\n"
"| IndexScan [{'<indexKey> 0': pb, '<rid>': rid_0}, scanDefName: c1, indexDefName: "
"index2, interval: {[Const [maxKey], Const [2])}]\n"
- "Evaluation [{unionTemp_1}]\n"
- "| Const [Nothing]\n"
- "Evaluation [{unionTemp_0}]\n"
- "| Variable [pa]\n"
- "Evaluation [{sideId_0}]\n"
- "| Const [0]\n"
+ "Evaluation [{unionTemp_1} = Const [Nothing]]\n"
+ "Evaluation [{unionTemp_0} = Variable [pa]]\n"
+ "Evaluation [{sideId_0} = Const [0]]\n"
"IndexScan [{'<indexKey> 0': pa, '<rid>': rid_0}, scanDefName: c1, indexDefName: "
"index1, interval: {=Const [1]}]\n",
optimized);
@@ -2367,8 +2337,7 @@ TEST(PhysRewriter, MultiKeyIndex) {
"| | Condition\n"
"| | rid_0 = rid_2\n"
"| Union [{rid_2}]\n"
- "| Evaluation [{rid_2}]\n"
- "| | Variable [rid_0]\n"
+ "| Evaluation [{rid_2} = Variable [rid_0]]\n"
"| IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index2, interval: "
"{[Const [maxKey], Const [2])}, reversed]\n"
"IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {=Const "
@@ -3487,8 +3456,7 @@ TEST(PhysRewriter, ElemMatchIndexNoArrays) {
// If we do not have arrays (index is not multikey) we simplify to unsatisfiable query.
ASSERT_EXPLAIN_V2_AUTO(
"Root [{root}]\n"
- "Evaluation [{root}]\n"
- "| Const [Nothing]\n"
+ "Evaluation [{root} = Const [Nothing]]\n"
"LimitSkip [limit: 0, skip: 0]\n"
"CoScan []\n",
optimized);
@@ -3732,12 +3700,10 @@ TEST(PhysRewriter, NestedElemMatch) {
"| [sides_0]\n"
"| FunctionCall [$addToSet] Variable [sideId_0]\n"
"Union [{rid_0, sideId_0}]\n"
- "| Evaluation [{sideId_0}]\n"
- "| | Const [1]\n"
- "| IndexScan [{'<rid>': rid_0}, scanDefName: coll1, indexDefName: index1, interval: {[C"
- "onst [[]], Const [BinData(0, )])}]\n"
- "Evaluation [{sideId_0}]\n"
- "| Const [0]\n"
+ "| Evaluation [{sideId_0} = Const [1]]\n"
+ "| IndexScan [{'<rid>': rid_0}, scanDefName: coll1, indexDefName: index1, interval: "
+ "{[Const [[]], Const [BinData(0, )])}]\n"
+ "Evaluation [{sideId_0} = Const [0]]\n"
"Filter []\n"
"| EvalFilter []\n"
"| | Variable [evalTemp_3]\n"
@@ -5662,10 +5628,9 @@ TEST(PhysRewriter, PerfOnlyPreds2) {
"| | Collation\n"
"| | Ascending\n"
"| Union [{rid_5}]\n"
- "| Evaluation [{rid_5}]\n"
- "| | Variable [rid_0]\n"
- "| IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index2, interval: {=Cons"
- "t [2]}]\n"
+ "| Evaluation [{rid_5} = Variable [rid_0]]\n"
+ "| IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index2, interval: {=Const "
+ "[2]}]\n"
"IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {=Const [1"
"]}]\n",
optimized);
diff --git a/src/mongo/db/query/optimizer/rewrites/path_optimizer_test.cpp b/src/mongo/db/query/optimizer/rewrites/path_optimizer_test.cpp
index d4414fc08a8..257ec2621d4 100644
--- a/src/mongo/db/query/optimizer/rewrites/path_optimizer_test.cpp
+++ b/src/mongo/db/query/optimizer/rewrites/path_optimizer_test.cpp
@@ -735,8 +735,7 @@ TEST(Path, ProjElim3) {
ASSERT_EXPLAIN_AUTO(
"Root [{p99}]\n"
- " Evaluation [{p99}]\n"
- " Variable [root]\n"
+ " Evaluation [{p99} = Variable [root]]\n"
" Scan [test, {root}]\n",
tree);
}
diff --git a/src/mongo/db/query/optimizer/utils/path_utils.h b/src/mongo/db/query/optimizer/utils/path_utils.h
index c5e33bf6e39..17e9eefe05e 100644
--- a/src/mongo/db/query/optimizer/utils/path_utils.h
+++ b/src/mongo/db/query/optimizer/utils/path_utils.h
@@ -36,6 +36,23 @@
namespace mongo::optimizer {
/**
+ * If the input expression is a constant or a variable, or it is an EvalFilter/Path which has an
+ * identity path and input which itself is constant or variable, then return a pointer to the deepst
+ * simple expression.
+ */
+template <class T>
+ABT::reference_type getTrivialExprPtr(const ABT& n) {
+ if (n.is<Constant>() || n.is<Variable>()) {
+ return n.ref();
+ }
+ if (const auto* ptr = n.cast<T>();
+ ptr != nullptr && ptr->getPath().template is<PathIdentity>()) {
+ return getTrivialExprPtr<T>(ptr->getInput());
+ }
+ return {};
+}
+
+/**
* Returns a vector all paths nested under conjunctions (PathComposeM) in the given path.
* For example, PathComposeM(PathComposeM(Foo, Bar), Baz) returns [Foo, Bar, Baz].
* If the given path is not a conjunction, returns a vector with the given path.