diff options
author | Svilen Mihaylov <svilen.mihaylov@mongodb.com> | 2022-07-11 22:02:36 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-07-11 23:21:54 +0000 |
commit | b16dde9cc2dd00827a3939c9c56fad755f2cd00c (patch) | |
tree | 0523fee49453c18a393f09fab05cf508ea871f45 /src/mongo/db/query/optimizer | |
parent | d4f322da12a6a52b1f32c39187f04b961f8cf206 (diff) | |
download | mongo-b16dde9cc2dd00827a3939c9c56fad755f2cd00c.tar.gz |
SERVER-67782 Fix handling of bounds with MaxKey and MinKey
Diffstat (limited to 'src/mongo/db/query/optimizer')
9 files changed, 165 insertions, 111 deletions
diff --git a/src/mongo/db/query/optimizer/explain.cpp b/src/mongo/db/query/optimizer/explain.cpp index fedbb01fda9..abf0b2a1daf 100644 --- a/src/mongo/db/query/optimizer/explain.cpp +++ b/src/mongo/db/query/optimizer/explain.cpp @@ -712,37 +712,23 @@ public: }; printer.print(lowBound.isInclusive() ? "[" : "("); - if (lowBound.isInfinite()) { - printer.print("-inf"); - } else { - printBoundFn(printer, lowBound.getBound()); - } + printBoundFn(printer, lowBound.getBound()); printer.print(", "); - if (highBound.isInfinite()) { - printer.print("+inf"); - } else { - printBoundFn(printer, highBound.getBound()); - } + printBoundFn(printer, highBound.getBound()); printer.print(highBound.isInclusive() ? "]" : ")"); } else if constexpr (version == ExplainVersion::V3) { ExplainPrinter lowBoundPrinter; - lowBoundPrinter.fieldName("inclusive") - .print(lowBound.isInclusive()) - .fieldName("infinite") - .print(lowBound.isInfinite()); - if (!lowBound.isInfinite()) { + lowBoundPrinter.fieldName("inclusive").print(lowBound.isInclusive()); + { ExplainPrinter boundPrinter = generate(lowBound.getBound()); lowBoundPrinter.fieldName("bound").print(boundPrinter); } ExplainPrinter highBoundPrinter; - highBoundPrinter.fieldName("inclusive") - .print(highBound.isInclusive()) - .fieldName("infinite") - .print(highBound.isInfinite()); - if (!highBound.isInfinite()) { + highBoundPrinter.fieldName("inclusive").print(highBound.isInclusive()); + { ExplainPrinter boundPrinter = generate(highBound.getBound()); highBoundPrinter.fieldName("bound").print(boundPrinter); } diff --git a/src/mongo/db/query/optimizer/index_bounds.cpp b/src/mongo/db/query/optimizer/index_bounds.cpp index 4ceaf663a94..d2a41dee4d5 100644 --- a/src/mongo/db/query/optimizer/index_bounds.cpp +++ b/src/mongo/db/query/optimizer/index_bounds.cpp @@ -34,13 +34,17 @@ namespace mongo::optimizer { -BoundRequirement::BoundRequirement() : _inclusive(false), _bound() {} +BoundRequirement BoundRequirement::makeMinusInf() { + return {true /*inclusive*/, Constant::minKey()}; +} -BoundRequirement::BoundRequirement(bool inclusive, boost::optional<ABT> bound) - : _inclusive(inclusive), _bound(std::move(bound)) { - uassert(6624077, "Infinite bound cannot be inclusive", !inclusive || !isInfinite()); +BoundRequirement BoundRequirement::makePlusInf() { + return {true /*inclusive*/, Constant::maxKey()}; } +BoundRequirement::BoundRequirement(bool inclusive, ABT bound) + : _inclusive(inclusive), _bound(std::move(bound)) {} + bool BoundRequirement::operator==(const BoundRequirement& other) const { return _inclusive == other._inclusive && _bound == other._bound; } @@ -49,19 +53,21 @@ bool BoundRequirement::isInclusive() const { return _inclusive; } -void BoundRequirement::setInclusive(bool value) { - _inclusive = value; +bool BoundRequirement::isMinusInf() const { + return _inclusive && _bound == Constant::minKey(); } -bool BoundRequirement::isInfinite() const { - return !_bound.has_value(); +bool BoundRequirement::isPlusInf() const { + return _inclusive && _bound == Constant::maxKey(); } const ABT& BoundRequirement::getBound() const { - uassert(6624078, "Cannot retrieve infinite bound", !isInfinite()); - return _bound.get(); + return _bound; } +IntervalRequirement::IntervalRequirement() + : IntervalRequirement(BoundRequirement::makeMinusInf(), BoundRequirement::makePlusInf()) {} + IntervalRequirement::IntervalRequirement(BoundRequirement lowBound, BoundRequirement highBound) : _lowBound(std::move(lowBound)), _highBound(std::move(highBound)) {} @@ -70,7 +76,7 @@ bool IntervalRequirement::operator==(const IntervalRequirement& other) const { } bool IntervalRequirement::isFullyOpen() const { - return _lowBound.isInfinite() && _highBound.isInfinite(); + return _lowBound.isMinusInf() && _highBound.isPlusInf(); } bool IntervalRequirement::isEquality() const { diff --git a/src/mongo/db/query/optimizer/index_bounds.h b/src/mongo/db/query/optimizer/index_bounds.h index 7acdeec5767..3222b698545 100644 --- a/src/mongo/db/query/optimizer/index_bounds.h +++ b/src/mongo/db/query/optimizer/index_bounds.h @@ -38,31 +38,28 @@ namespace mongo::optimizer { class BoundRequirement { public: - static BoundRequirement makeInfinite() { - return BoundRequirement(false, boost::none); - } + static BoundRequirement makeMinusInf(); + static BoundRequirement makePlusInf(); - BoundRequirement(); - BoundRequirement(bool inclusive, boost::optional<ABT> bound); + BoundRequirement(bool inclusive, ABT bound); bool operator==(const BoundRequirement& other) const; bool isInclusive() const; - void setInclusive(bool value); - bool isInfinite() const; + bool isMinusInf() const; + bool isPlusInf() const; + const ABT& getBound() const; private: bool _inclusive; - - // If we do not have a bound ABT, the bound is considered infinite. - boost::optional<ABT> _bound; + ABT _bound; }; class IntervalRequirement { public: - IntervalRequirement() = default; + IntervalRequirement(); IntervalRequirement(BoundRequirement lowBound, BoundRequirement highBound); bool operator==(const IntervalRequirement& other) const; diff --git a/src/mongo/db/query/optimizer/interval_intersection_test.cpp b/src/mongo/db/query/optimizer/interval_intersection_test.cpp index 7593e199846..a965e2600e2 100644 --- a/src/mongo/db/query/optimizer/interval_intersection_test.cpp +++ b/src/mongo/db/query/optimizer/interval_intersection_test.cpp @@ -383,10 +383,10 @@ TEST(IntervalIntersection, VariableIntervals) { IntervalReqExpr::make<IntervalReqExpr::Conjunction>(IntervalReqExpr::NodeVector{ IntervalReqExpr::make<IntervalReqExpr::Atom>(IntervalRequirement{ BoundRequirement(true /*inclusive*/, make<Variable>("v1")), - BoundRequirement::makeInfinite()}), + BoundRequirement::makePlusInf()}), IntervalReqExpr::make<IntervalReqExpr::Atom>(IntervalRequirement{ BoundRequirement(false /*inclusive*/, make<Variable>("v2")), - BoundRequirement::makeInfinite()})})}); + BoundRequirement::makePlusInf()})})}); auto result = intersectDNFIntervals(interval); ASSERT_TRUE(result); @@ -401,7 +401,7 @@ TEST(IntervalIntersection, VariableIntervals) { " U \n" " {\n" " {(If [] BinaryOp [Gte] Variable [v1] Variable [v2] Variable [v1] Variable " - "[v2], +inf)}\n" + "[v2], Const [maxKey]]}\n" " }\n" "}\n", ExplainGenerator::explainIntervalExpr(*result)); 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 7090365c044..a1468c7e8e7 100644 --- a/src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp +++ b/src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp @@ -1257,7 +1257,7 @@ TEST(LogicalRewriter, UnionPreservesCommonLogicalProps) { " | Sargable [Complete]\n" " | | | | | requirementsMap: \n" " | | | | | refProjection: ptest1, path: 'PathGet [a] " - "PathIdentity []', boundProjection: a, intervals: {{{(-inf, +inf)}}}\n" + "PathIdentity []', boundProjection: a, intervals: {{{[Const [minKey], Const [maxKey]]}}}\n" " | | | | candidateIndexes: \n" " | | | BindBlock:\n" " | | | [a]\n" @@ -1317,7 +1317,7 @@ TEST(LogicalRewriter, UnionPreservesCommonLogicalProps) { " | Sargable [Complete]\n" " | | | | | requirementsMap: \n" " | | | | | refProjection: ptest2, path: 'PathGet [a] " - "PathIdentity []', boundProjection: a, intervals: {{{(-inf, +inf)}}}\n" + "PathIdentity []', boundProjection: a, intervals: {{{[Const [minKey], Const [maxKey]]}}}\n" " | | | | candidateIndexes: \n" " | | | BindBlock:\n" " | | | [a]\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 87e09f87977..6f0a2654909 100644 --- a/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp +++ b/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp @@ -1107,7 +1107,7 @@ TEST(PhysRewriter, FilterIndexing3) { "| RefBlock: \n" "| Variable [pa]\n" "IndexScan [{'<indexKey> 0': pa}, scanDefName: c1, indexDefName: index1, interval: {[Const " - "[1], Const [1]], (-inf, +inf)}]\n" + "[1], Const [1]], [Const [minKey], Const [maxKey]]}]\n" " BindBlock:\n" " [pa]\n" " Source []\n", @@ -1176,7 +1176,7 @@ TEST(PhysRewriter, FilterIndexing3MultiKey) { "| projections: \n" "| rid_0\n" "IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {[Const " - "[1], Const [1]], (-inf, +inf)}]\n" + "[1], Const [1]], [Const [minKey], Const [maxKey]]}]\n" " BindBlock:\n" " [rid_0]\n" " Source []\n", @@ -1275,8 +1275,9 @@ TEST(PhysRewriter, FilterIndexing4) { "| PathCompare [Lt]\n" "| Const [1]\n" "IndexScan [{'<indexKey> 0': pa, '<indexKey> 1': evalTemp_6, '<indexKey> 2': evalTemp_7, " - "'<indexKey> 3': evalTemp_8}, scanDefName: c1, indexDefName: index1, interval: {(-inf, " - "Const [1]), (-inf, +inf), (-inf, +inf), (-inf, +inf)}]\n" + "'<indexKey> 3': evalTemp_8}, scanDefName: c1, indexDefName: index1, interval: {[Const " + "[minKey], Const [1]), [Const [minKey], Const [maxKey]], [Const [minKey], Const [maxKey]], " + "[Const [minKey], Const [maxKey]]}]\n" " BindBlock:\n" " [evalTemp_6]\n" " Source []\n" @@ -1363,7 +1364,7 @@ TEST(PhysRewriter, FilterIndexing5) { "| PathCompare [Gt]\n" "| Const [0]\n" "IndexScan [{'<indexKey> 0': pa, '<indexKey> 1': pb}, scanDefName: c1, indexDefName: " - "index1, interval: {(Const [0], +inf), (-inf, +inf)}]\n" + "index1, interval: {(Const [0], Const [maxKey]], [Const [minKey], Const [maxKey]]}]\n" " BindBlock:\n" " [pa]\n" " Source []\n" @@ -1436,7 +1437,7 @@ TEST(PhysRewriter, FilterIndexing6) { "| Variable [pa]\n" "| Variable [pb]\n" "IndexScan [{'<indexKey> 0': pa, '<indexKey> 1': pb}, scanDefName: c1, indexDefName: " - "index1, interval: {[Const [0], Const [0]], (Const [0], +inf)}]\n" + "index1, interval: {[Const [0], Const [0]], (Const [0], Const [maxKey]]}]\n" " BindBlock:\n" " [pa]\n" " Source []\n" @@ -1586,7 +1587,8 @@ TEST(PhysRewriter, FilterIndexingVariable) { ASSERT_EQ(4, phaseManager.getMemo().getStats()._physPlanExplorationCount); // Observe unioning of two index scans with complex expressions for bounds. This encodes: - // (max(param_0, param_1), +inf) U [param_0 > param_1 ? MaxKey : param_1, max(param_0, param_1)] + // (max(param_0, param_1), Const [maxKey]] U [param_0 > param_1 ? MaxKey : param_1, max(param_0, + // param_1)] ASSERT_EXPLAIN_V2( "Root []\n" "| | projections: \n" @@ -1616,8 +1618,8 @@ TEST(PhysRewriter, FilterIndexingVariable) { "| | Source []\n" "| IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {(If [] " "BinaryOp [Gte] FunctionCall [getQueryParam] Const [0] FunctionCall [getQueryParam] Const " - "[1] FunctionCall [getQueryParam] Const [0] FunctionCall [getQueryParam] Const [1], " - "+inf)}]\n" + "[1] FunctionCall [getQueryParam] Const [0] FunctionCall [getQueryParam] Const [1], Const " + "[maxKey]]}]\n" "| BindBlock:\n" "| [rid_0]\n" "| Source []\n" @@ -1632,6 +1634,81 @@ TEST(PhysRewriter, FilterIndexingVariable) { optimized); } +TEST(PhysRewriter, FilterIndexingMaxKey) { + using namespace properties; + + ABT scanNode = make<ScanNode>("root", "c1"); + + ABT filterNode1 = make<FilterNode>( + make<EvalFilter>( + make<PathGet>( + "a", + make<PathTraverse>( + make<PathComposeM>(make<PathCompare>(Operations::Gt, Constant::int64(1)), + make<PathCompare>(Operations::Lte, Constant::maxKey())), + PathTraverse::kSingleLevel)), + make<Variable>("root")), + std::move(scanNode)); + + ABT filterNode2 = make<FilterNode>( + make<EvalFilter>( + make<PathGet>( + "b", + make<PathTraverse>( + make<PathComposeM>(make<PathCompare>(Operations::Gt, Constant::int64(2)), + make<PathCompare>(Operations::Lt, Constant::maxKey())), + PathTraverse::kSingleLevel)), + make<Variable>("root")), + std::move(filterNode1)); + + ABT rootNode = + make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode2)); + + PrefixId prefixId; + OptPhaseManager phaseManager( + {OptPhaseManager::OptPhase::MemoSubstitutionPhase, + OptPhaseManager::OptPhase::MemoExplorationPhase, + OptPhaseManager::OptPhase::MemoImplementationPhase}, + prefixId, + {{{"c1", ScanDefinition{{}, {}}}}}, + {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); + + ABT optimized = rootNode; + ASSERT_TRUE(phaseManager.optimize(optimized)); + + // Observe redundant predicate a <= MaxKey is removed. + ASSERT_EXPLAIN_V2( + "Root []\n" + "| | projections: \n" + "| | root\n" + "| RefBlock: \n" + "| Variable [root]\n" + "Filter []\n" + "| EvalFilter []\n" + "| | Variable [evalTemp_1]\n" + "| PathTraverse [1]\n" + "| PathComposeM []\n" + "| | PathCompare [Lt]\n" + "| | Const [maxKey]\n" + "| PathCompare [Gt]\n" + "| Const [2]\n" + "Filter []\n" + "| EvalFilter []\n" + "| | Variable [evalTemp_0]\n" + "| PathTraverse [1]\n" + "| PathCompare [Gt]\n" + "| Const [1]\n" + "PhysicalScan [{'<root>': root, 'a': evalTemp_0, 'b': evalTemp_1}, c1]\n" + " BindBlock:\n" + " [evalTemp_0]\n" + " Source []\n" + " [evalTemp_1]\n" + " Source []\n" + " [root]\n" + " Source []\n", + optimized); +} + TEST(PhysRewriter, FilterReorder) { using namespace properties; PrefixId prefixId; @@ -1768,9 +1845,8 @@ TEST(PhysRewriter, CoveredScan) { "| | pa\n" "| RefBlock: \n" "| Variable [pa]\n" - "IndexScan [{'<indexKey> 0': pa}, scanDefName: c1, indexDefName: index1, interval: " - "{(-inf, " - "+inf)}]\n" + "IndexScan [{'<indexKey> 0': pa}, scanDefName: c1, indexDefName: index1, interval: {[Const " + "[minKey], Const [maxKey]]}]\n" " BindBlock:\n" " [pa]\n" " Source []\n", @@ -1824,7 +1900,7 @@ TEST(PhysRewriter, EvalIndexing) { "| RefBlock: \n" "| Variable [pa]\n" "IndexScan [{'<indexKey> 0': pa}, scanDefName: c1, indexDefName: index1, " - "interval: {(Const [1], +inf)}]\n" + "interval: {(Const [1], Const [maxKey]]}]\n" " BindBlock:\n" " [pa]\n" " Source []\n", @@ -1862,7 +1938,7 @@ TEST(PhysRewriter, EvalIndexing) { "| RefBlock: \n" "| Variable [pa]\n" "IndexScan [{'<indexKey> 0': pa}, scanDefName: c1, indexDefName: index1, " - "interval: {(Const [1], +inf)}]\n" + "interval: {(Const [1], Const [maxKey]]}]\n" " BindBlock:\n" " [pa]\n" " Source []\n", @@ -1999,7 +2075,7 @@ TEST(PhysRewriter, EvalIndexing2) { "| PathConstant []\n" "| Variable [pa1]\n" "IndexScan [{'<indexKey> 0': pa1}, scanDefName: c1, indexDefName: index1, interval: " - "{(-inf, +inf)}]\n" + "{[Const [minKey], Const [maxKey]]}]\n" " BindBlock:\n" " [pa1]\n" " Source []\n", @@ -2130,9 +2206,8 @@ TEST(PhysRewriter, MultiKeyIndex) { "| | BindBlock:\n" "| | [sideId_0]\n" "| | Const [1]\n" - "| IndexScan [{'<indexKey> 0': pb, '<rid>': rid_0}, scanDefName: c1, " - "indexDefName: " - "index2, interval: {(Const [2], +inf)}]\n" + "| IndexScan [{'<indexKey> 0': pb, '<rid>': rid_0}, scanDefName: c1, indexDefName: " + "index2, interval: {(Const [2], Const [maxKey]]}]\n" "| BindBlock:\n" "| [pb]\n" "| Source []\n" @@ -2200,7 +2275,7 @@ TEST(PhysRewriter, MultiKeyIndex) { "| | [rid_2]\n" "| | Variable [rid_0]\n" "| IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index2, interval: " - "{(Const [2], +inf)}, reversed]\n" + "{(Const [2], Const [maxKey]]}, reversed]\n" "| BindBlock:\n" "| [rid_0]\n" "| Source []\n" @@ -2654,7 +2729,8 @@ TEST(PhysRewriter, IndexBoundsIntersect) { "| | [rid_0]\n" "| | Source []\n" "| IndexScan [{'<indexKey> 1': disjunction_0, '<rid>': rid_0}, scanDefName: c1, " - "indexDefName: index1, interval: {[Const [100], Const [100]], (-inf, +inf)}]\n" + "indexDefName: index1, interval: {[Const [100], Const [100]], [Const [minKey], Const " + "[maxKey]]}]\n" "| BindBlock:\n" "| [disjunction_0]\n" "| Source []\n" @@ -2690,7 +2766,8 @@ TEST(PhysRewriter, IndexBoundsIntersect) { "| | [sideId_0]\n" "| | Const [1]\n" "| IndexScan [{'<indexKey> 1': conjunction_0, '<rid>': rid_0}, scanDefName: c1, " - "indexDefName: index1, interval: {(-inf, Const [90]), (-inf, +inf)}]\n" + "indexDefName: index1, interval: {[Const [minKey], Const [90]), [Const [minKey], Const " + "[maxKey]]}]\n" "| BindBlock:\n" "| [conjunction_0]\n" "| Source []\n" @@ -2701,7 +2778,8 @@ TEST(PhysRewriter, IndexBoundsIntersect) { "| [sideId_0]\n" "| Const [0]\n" "IndexScan [{'<indexKey> 1': conjunction_0, '<rid>': rid_0}, scanDefName: c1, " - "indexDefName: index1, interval: {(Const [70], +inf), (-inf, +inf)}]\n" + "indexDefName: index1, interval: {(Const [70], Const [maxKey]], [Const [minKey], Const " + "[maxKey]]}]\n" " BindBlock:\n" " [conjunction_0]\n" " Source []\n" @@ -2938,9 +3016,8 @@ TEST(PhysRewriter, IndexBoundsIntersect3) { "| | BindBlock:\n" "| | [sideId_0]\n" "| | Const [1]\n" - "| IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: " - "{(-inf, " - "Const [90])}]\n" + "| IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {[Const " + "[minKey], Const [90])}]\n" "| BindBlock:\n" "| [rid_0]\n" "| Source []\n" @@ -2949,7 +3026,7 @@ TEST(PhysRewriter, IndexBoundsIntersect3) { "| [sideId_0]\n" "| Const [0]\n" "IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {(Const " - "[70], +inf)}]\n" + "[70], Const [maxKey]]}]\n" " BindBlock:\n" " [rid_0]\n" " Source []\n", @@ -3140,7 +3217,8 @@ TEST(PhysRewriter, IndexResidualReq) { "| PathCompare [Gt]\n" "| Const [0]\n" "IndexScan [{'<indexKey> 0': pa, '<indexKey> 1': evalTemp_1}, scanDefName: c1, " - "indexDefName: index1, interval: {(Const [0], +inf), (-inf, +inf)}]\n" + "indexDefName: index1, interval: {(Const [0], Const [maxKey]], [Const [minKey], Const " + "[maxKey]]}]\n" " BindBlock:\n" " [evalTemp_1]\n" " Source []\n" @@ -3230,7 +3308,8 @@ TEST(PhysRewriter, IndexResidualReq1) { "| RefBlock: \n" "| Variable [rid_0]\n" "IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {[Const " - "[0], Const [0]], [Const [0], Const [0]], [Const [0], Const [0]], (-inf, +inf)}]\n" + "[0], Const [0]], [Const [0], Const [0]], [Const [0], Const [0]], [Const [minKey], Const " + "[maxKey]]}]\n" " BindBlock:\n" " [rid_0]\n" " Source []\n", @@ -3307,9 +3386,9 @@ TEST(PhysRewriter, IndexResidualReq2) { "| | Variable [evalTemp_4]\n" "| PathCompare [Eq]\n" "| Const [0]\n" - "IndexScan [{'<indexKey> 2': evalTemp_4, '<rid>': rid_0}, scanDefName: c1, " - "indexDefName: " - "index1, interval: {[Const [0], Const [0]], (-inf, +inf), (-inf, +inf)}]\n" + "IndexScan [{'<indexKey> 2': evalTemp_4, '<rid>': rid_0}, scanDefName: c1, indexDefName: " + "index1, interval: {[Const [0], Const [0]], [Const [minKey], Const [maxKey]], [Const " + "[minKey], Const [maxKey]]}]\n" " BindBlock:\n" " [evalTemp_4]\n" " Source []\n" @@ -3879,7 +3958,7 @@ TEST(PhysRewriter, IndexPartitioning) { "| | type: RoundRobin\n" "| RefBlock: \n" "IndexScan [{'<indexKey> 0': pa, '<rid>': rid_0}, scanDefName: c1, indexDefName: index1, " - "interval: {(Const [0], +inf)}]\n" + "interval: {(Const [0], Const [maxKey]]}]\n" " BindBlock:\n" " [pa]\n" " Source []\n" @@ -4869,7 +4948,7 @@ TEST(PhysRewriter, JoinRewrite1) { "BinaryJoin [joinType: Inner, {p1, p2}]\n" "| | Const [true]\n" "| IndexScan [{}, scanDefName: test2, indexDefName: index1, interval: {(If [] BinaryOp " - "[Gte] Variable [p1] Variable [p2] Variable [p1] Variable [p2], +inf)}]\n" + "[Gte] Variable [p1] Variable [p2] Variable [p1] Variable [p2], Const [maxKey]]}]\n" "| BindBlock:\n" "PhysicalScan [{'a1': p1, 'a2': p2}, test1]\n" " BindBlock:\n" diff --git a/src/mongo/db/query/optimizer/utils/abt_hash.cpp b/src/mongo/db/query/optimizer/utils/abt_hash.cpp index 04a4362c375..acdc9f34bca 100644 --- a/src/mongo/db/query/optimizer/utils/abt_hash.cpp +++ b/src/mongo/db/query/optimizer/utils/abt_hash.cpp @@ -72,9 +72,7 @@ static size_t computeDistributionHash(const properties::DistributionRequirement& static void updateBoundHash(size_t& result, const BoundRequirement& bound) { updateHash(result, std::hash<bool>()(bound.isInclusive())); - if (!bound.isInfinite()) { - updateHash(result, ABTHashGenerator::generate(bound.getBound())); - } + updateHash(result, ABTHashGenerator::generate(bound.getBound())); }; template <class T> diff --git a/src/mongo/db/query/optimizer/utils/interval_utils.cpp b/src/mongo/db/query/optimizer/utils/interval_utils.cpp index 5a0f11e3299..0b4053107a1 100644 --- a/src/mongo/db/query/optimizer/utils/interval_utils.cpp +++ b/src/mongo/db/query/optimizer/utils/interval_utils.cpp @@ -110,14 +110,10 @@ std::vector<IntervalRequirement> intersectIntervals(const IntervalRequirement& i return {i1}; } - const ABT low1 = - i1.getLowBound().isInfinite() ? Constant::minKey() : i1.getLowBound().getBound(); - const ABT high1 = - i1.getHighBound().isInfinite() ? Constant::maxKey() : i1.getHighBound().getBound(); - const ABT low2 = - i2.getLowBound().isInfinite() ? Constant::minKey() : i2.getLowBound().getBound(); - const ABT high2 = - i2.getHighBound().isInfinite() ? Constant::maxKey() : i2.getHighBound().getBound(); + const ABT& low1 = i1.getLowBound().getBound(); + const ABT& high1 = i1.getHighBound().getBound(); + const ABT& low2 = i2.getLowBound().getBound(); + const ABT& high2 = i2.getHighBound().getBound(); const auto foldFn = [](ABT expr) { // Performs constant folding. @@ -151,12 +147,8 @@ std::vector<IntervalRequirement> intersectIntervals(const IntervalRequirement& i // We form a "main" result interval which is closed on any side with "agreement" between the two // intervals. For example [low1, high1] ^ [low2, high2) -> [max(low1, low2), min(high1, high2)) - BoundRequirement lowBoundMain = (maxLow == Constant::minKey()) - ? BoundRequirement::makeInfinite() - : BoundRequirement{low1Inc && low2Inc, maxLow}; - BoundRequirement highBoundMain = (minHigh == Constant::maxKey()) - ? BoundRequirement::makeInfinite() - : BoundRequirement{high1Inc && high2Inc, minHigh}; + BoundRequirement lowBoundMain(low1Inc && low2Inc, maxLow); + BoundRequirement highBoundMain(high1Inc && high2Inc, minHigh); const bool boundsEqual = foldFn(make<BinaryOp>(Operations::Eq, maxLow, minHigh)) == Constant::boolean(true); @@ -180,7 +172,7 @@ std::vector<IntervalRequirement> intersectIntervals(const IntervalRequirement& i // (max(low1, low2), min(high1, high2)). Then we add an extra closed interval for each side with // disagreement. For example for the lower sides we add: [low2 >= low1 ? MaxKey : low1, // min(max(low1, low2), min(high1, high2)] This is a closed interval which would reduce to - // [max(low1, low2), max(low1, low2)] if low1 < low2. If low2 >= low1 the interval reduces to an + // [max(low1, low2), max(low1, low2)] if low2 < low1. If low2 >= low1 the interval reduces to an // empty one [MaxKey, min(max(low1, low2), min(high1, high2)] which will return no results from // an index scan. We do not know that in general if we do not have constants (we cannot fold). // @@ -199,7 +191,7 @@ std::vector<IntervalRequirement> intersectIntervals(const IntervalRequirement& i if (comparison == Constant::boolean(true)) { if (interval.isEquality()) { // We can determine the two bounds are equal. - bound.setInclusive(true); + bound = {true /*inclusive*/, bound.getBound()}; } else { result.push_back(std::move(interval)); } diff --git a/src/mongo/db/query/optimizer/utils/utils.cpp b/src/mongo/db/query/optimizer/utils/utils.cpp index f594c135032..ff9170c5157 100644 --- a/src/mongo/db/query/optimizer/utils/utils.cpp +++ b/src/mongo/db/query/optimizer/utils/utils.cpp @@ -636,28 +636,25 @@ public: if (!inputResult) { return {}; } - if (!inputResult->_bound.has_value() || !inputResult->_reqMap.empty()) { + if (!inputResult->_bound || !inputResult->_reqMap.empty()) { return {}; } - const auto& bound = inputResult->_bound; - bool lowBoundInclusive = false; - boost::optional<ABT> lowBound; - bool highBoundInclusive = false; - boost::optional<ABT> highBound; + const auto& bound = *inputResult->_bound; + bool lowBoundInclusive = true; + ABT lowBound = Constant::minKey(); + bool highBoundInclusive = true; + ABT highBound = Constant::maxKey(); const Operations op = pathCompare.op(); switch (op) { case Operations::Eq: - lowBoundInclusive = true; lowBound = bound; - highBoundInclusive = true; highBound = bound; break; case Operations::Lt: case Operations::Lte: - lowBoundInclusive = false; highBoundInclusive = op == Operations::Lte; highBound = bound; break; @@ -666,7 +663,6 @@ public: case Operations::Gte: lowBoundInclusive = op == Operations::Gte; lowBound = bound; - highBoundInclusive = false; break; default: @@ -1165,13 +1161,13 @@ public: } ABT result = make<PathIdentity>(); - if (!lowBound.isInfinite()) { + if (!lowBound.isMinusInf()) { maybeComposePath( result, make<PathCompare>(lowBound.isInclusive() ? Operations::Gte : Operations::Gt, lowBound.getBound())); } - if (!highBound.isInfinite()) { + if (!highBound.isPlusInf()) { maybeComposePath( result, make<PathCompare>(highBound.isInclusive() ? Operations::Lte : Operations::Lt, |