summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/optimizer
diff options
context:
space:
mode:
authorSvilen Mihaylov <svilen.mihaylov@mongodb.com>2022-07-11 22:02:36 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-07-11 23:21:54 +0000
commitb16dde9cc2dd00827a3939c9c56fad755f2cd00c (patch)
tree0523fee49453c18a393f09fab05cf508ea871f45 /src/mongo/db/query/optimizer
parentd4f322da12a6a52b1f32c39187f04b961f8cf206 (diff)
downloadmongo-b16dde9cc2dd00827a3939c9c56fad755f2cd00c.tar.gz
SERVER-67782 Fix handling of bounds with MaxKey and MinKey
Diffstat (limited to 'src/mongo/db/query/optimizer')
-rw-r--r--src/mongo/db/query/optimizer/explain.cpp26
-rw-r--r--src/mongo/db/query/optimizer/index_bounds.cpp28
-rw-r--r--src/mongo/db/query/optimizer/index_bounds.h19
-rw-r--r--src/mongo/db/query/optimizer/interval_intersection_test.cpp6
-rw-r--r--src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp4
-rw-r--r--src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp145
-rw-r--r--src/mongo/db/query/optimizer/utils/abt_hash.cpp4
-rw-r--r--src/mongo/db/query/optimizer/utils/interval_utils.cpp24
-rw-r--r--src/mongo/db/query/optimizer/utils/utils.cpp20
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,