diff options
author | Svilen Mihaylov <svilen.mihaylov@mongodb.com> | 2023-02-10 00:29:20 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-02-10 03:07:27 +0000 |
commit | a6babc2a1789fae2eb6057152fca59b3da2ab778 (patch) | |
tree | 88a9ac3199b9db45ecc4deb3f43247366f8c51a8 /src/mongo/db/query | |
parent | 53478bd7dce5f14708fb35db41293df1ea79db33 (diff) | |
download | mongo-a6babc2a1789fae2eb6057152fca59b3da2ab778.tar.gz |
SERVER-73102 [CQF] Simplify explain for evaluation of simple constants
Diffstat (limited to 'src/mongo/db/query')
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. |