summaryrefslogtreecommitdiff
path: root/src/mongo/db/query
diff options
context:
space:
mode:
authorSvilen Mihaylov <svilen.mihaylov@mongodb.com>2023-02-03 18:44:40 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-03 21:15:53 +0000
commitdd107e8f21b626df74a96db84d0a95cc0db05a75 (patch)
treec026b143c81eca0c84c2893526d9241fbc858dca /src/mongo/db/query
parent56616080c12298229fc6e3cc71ace4c85ac973f4 (diff)
downloadmongo-dd107e8f21b626df74a96db84d0a95cc0db05a75.tar.gz
SERVER-72784 [CQF] Update representation of compound index bounds
Diffstat (limited to 'src/mongo/db/query')
-rw-r--r--src/mongo/db/query/ce/bound_utils.cpp12
-rw-r--r--src/mongo/db/query/optimizer/explain.cpp128
-rw-r--r--src/mongo/db/query/optimizer/index_bounds.cpp92
-rw-r--r--src/mongo/db/query/optimizer/index_bounds.h170
-rw-r--r--src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp47
-rw-r--r--src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp124
-rw-r--r--src/mongo/db/query/optimizer/utils/abt_hash.cpp12
-rw-r--r--src/mongo/db/query/optimizer/utils/interval_utils.cpp27
-rw-r--r--src/mongo/db/query/optimizer/utils/interval_utils.h5
-rw-r--r--src/mongo/db/query/optimizer/utils/utils.cpp16
10 files changed, 380 insertions, 253 deletions
diff --git a/src/mongo/db/query/ce/bound_utils.cpp b/src/mongo/db/query/ce/bound_utils.cpp
index 13961f8e2bb..3374a550c74 100644
--- a/src/mongo/db/query/ce/bound_utils.cpp
+++ b/src/mongo/db/query/ce/bound_utils.cpp
@@ -88,12 +88,12 @@ IntervalRequirement getMinMaxIntervalForType(sbe::value::TypeTags type) {
bool isIntervalSubsetOfType(const IntervalRequirement& interval, sbe::value::TypeTags type) {
// Create a conjunction of the interval and the min-max interval for the type as input for the
// intersection function.
- auto intervals =
- IntervalReqExpr::make<IntervalReqExpr::Disjunction>(IntervalReqExpr::NodeVector{
- IntervalReqExpr::make<IntervalReqExpr::Conjunction>(IntervalReqExpr::NodeVector{
- IntervalReqExpr::make<IntervalReqExpr::Atom>(interval),
- IntervalReqExpr::make<IntervalReqExpr::Atom>(getMinMaxIntervalForType(type))})});
-
+ auto intervals = std::move(*IntervalReqExpr::Builder{}
+ .pushDisj()
+ .pushConj()
+ .atom(interval)
+ .atom(getMinMaxIntervalForType(type))
+ .finish());
return intersectDNFIntervals(intervals, ConstEval::constFold).has_value();
}
diff --git a/src/mongo/db/query/optimizer/explain.cpp b/src/mongo/db/query/optimizer/explain.cpp
index fb9af129873..af36021acbf 100644
--- a/src/mongo/db/query/optimizer/explain.cpp
+++ b/src/mongo/db/query/optimizer/explain.cpp
@@ -893,91 +893,115 @@ public:
return printer;
}
- void printInterval(ExplainPrinter& printer, const IntervalRequirement& interval) {
- const BoundRequirement& lowBound = interval.getLowBound();
- const BoundRequirement& highBound = interval.getHighBound();
+ void printBound(ExplainPrinter& printer, const BoundRequirement& bound) {
+ if constexpr (version < ExplainVersion::V3) {
+ // Since we are printing on a single level, use V1 printer in order to avoid children
+ // being reversed. Also note that we are specifically not printing inclusive flag here.
+ // The inclusion is explained by the caller.
+ ExplainGeneratorTransporter<ExplainVersion::V1> gen;
+ auto boundPrinter = gen.generate(bound.getBound());
+ printer.printSingleLevel(boundPrinter);
+ } else if constexpr (version == ExplainVersion::V3) {
+ printer.fieldName("inclusive").print(bound.isInclusive());
+ {
+ ExplainPrinter boundPrinter = generate(bound.getBound());
+ printer.fieldName("bound").print(boundPrinter);
+ }
+ } else {
+ MONGO_UNREACHABLE;
+ }
+ }
+
+ void printBound(ExplainPrinter& printer, const CompoundBoundRequirement& bound) {
if constexpr (version < ExplainVersion::V3) {
- const auto printBoundFn = [](ExplainPrinter& printer, const ABT& bound) {
- // Since we are printing on a single level, use V1 printer in order to avoid
- // children being reversed.
- ExplainGeneratorTransporter<ExplainVersion::V1> gen;
- auto boundPrinter = gen.generate(bound);
- printer.printSingleLevel(boundPrinter);
- };
+ const bool manyConstants = bound.size() > 1 && bound.isConstant();
+ if (manyConstants) {
+ printer.print("Const [");
+ }
+
+ bool first = true;
+ for (const auto& entry : bound.getBound()) {
+ if (first) {
+ first = false;
+ } else {
+ printer.print(" | ");
+ }
+
+ if (manyConstants) {
+ std::ostringstream os;
+ os << entry.cast<Constant>()->get();
+ printer.print(os.str());
+ } else {
+ ExplainGeneratorTransporter<ExplainVersion::V1> gen;
+ auto boundPrinter = gen.generate(entry);
+ printer.printSingleLevel(boundPrinter);
+ }
+ }
+
+ if (manyConstants) {
+ printer.print("]");
+ }
+ } else if constexpr (version == ExplainVersion::V3) {
+ printer.fieldName("inclusive").print(bound.isInclusive());
+
+ std::vector<ExplainPrinter> printers;
+ for (const auto& entry : bound.getBound()) {
+ printers.push_back(generate(entry));
+ }
+ printer.fieldName("bound").print(printers);
+ } else {
+ MONGO_UNREACHABLE;
+ }
+ }
+
+ template <class T>
+ void printInterval(ExplainPrinter& printer, const T& interval) {
+ const auto& lowBound = interval.getLowBound();
+ const auto& highBound = interval.getHighBound();
+ if constexpr (version < ExplainVersion::V3) {
// Shortened output for half-open, fully open and point intervals.
if (interval.isFullyOpen()) {
printer.print("<fully open>");
} else if (interval.isEquality()) {
printer.print("=");
- printBoundFn(printer, lowBound.getBound());
+ printBound(printer, lowBound);
} else if (lowBound.isMinusInf()) {
printer.print("<");
if (highBound.isInclusive()) {
printer.print("=");
}
- printBoundFn(printer, highBound.getBound());
+ printBound(printer, highBound);
} else if (highBound.isPlusInf()) {
printer.print(">");
if (lowBound.isInclusive()) {
printer.print("=");
}
- printBoundFn(printer, lowBound.getBound());
+ printBound(printer, lowBound);
} else {
// Output for a generic interval.
printer.print(lowBound.isInclusive() ? "[" : "(");
- printBoundFn(printer, lowBound.getBound());
+ printBound(printer, lowBound);
printer.print(", ");
- printBoundFn(printer, highBound.getBound());
+ printBound(printer, highBound);
printer.print(highBound.isInclusive() ? "]" : ")");
}
} else if constexpr (version == ExplainVersion::V3) {
ExplainPrinter lowBoundPrinter;
- lowBoundPrinter.fieldName("inclusive").print(lowBound.isInclusive());
- {
- ExplainPrinter boundPrinter = generate(lowBound.getBound());
- lowBoundPrinter.fieldName("bound").print(boundPrinter);
- }
-
+ printBound(lowBoundPrinter, lowBound);
ExplainPrinter highBoundPrinter;
- highBoundPrinter.fieldName("inclusive").print(highBound.isInclusive());
- {
- ExplainPrinter boundPrinter = generate(highBound.getBound());
- highBoundPrinter.fieldName("bound").print(boundPrinter);
- }
+ printBound(highBoundPrinter, highBound);
- printer.fieldName("lowBound")
+ ExplainPrinter local;
+ local.fieldName("lowBound")
.print(lowBoundPrinter)
.fieldName("highBound")
.print(highBoundPrinter);
- } else {
- MONGO_UNREACHABLE;
- }
- }
-
- void printInterval(ExplainPrinter& printer, const CompoundIntervalRequirement& interval) {
- if constexpr (version < ExplainVersion::V3) {
- bool first = true;
- for (const auto& entry : interval) {
- if (first) {
- first = false;
- } else {
- printer.print(", ");
- }
- printInterval(printer, entry);
- }
- } else if constexpr (version == ExplainVersion::V3) {
- std::vector<ExplainPrinter> printers;
- for (const auto& entry : interval) {
- ExplainPrinter local;
- printInterval(local, entry);
- printers.push_back(std::move(local));
- }
- printer.print(printers);
+ printer.print(local);
} else {
MONGO_UNREACHABLE;
}
@@ -2862,7 +2886,7 @@ std::string ExplainGenerator::explainInterval(const IntervalRequirement& interva
return gen.printInterval(interval);
}
-std::string explainInterval(const CompoundIntervalRequirement& interval) {
+std::string ExplainGenerator::explainInterval(const CompoundIntervalRequirement& interval) {
ExplainGeneratorV2 gen;
return gen.printInterval(interval);
}
diff --git a/src/mongo/db/query/optimizer/index_bounds.cpp b/src/mongo/db/query/optimizer/index_bounds.cpp
index 3135126a57b..f6421d0e0d0 100644
--- a/src/mongo/db/query/optimizer/index_bounds.cpp
+++ b/src/mongo/db/query/optimizer/index_bounds.cpp
@@ -44,19 +44,10 @@ BoundRequirement BoundRequirement::makePlusInf() {
return {true /*inclusive*/, Constant::maxKey()};
}
-BoundRequirement::BoundRequirement(bool inclusive, ABT bound)
- : _inclusive(inclusive), _bound(std::move(bound)) {
+BoundRequirement::BoundRequirement(bool inclusive, ABT bound) : Base(inclusive, std::move(bound)) {
assertExprSort(_bound);
}
-bool BoundRequirement::operator==(const BoundRequirement& other) const {
- return _inclusive == other._inclusive && _bound == other._bound;
-}
-
-bool BoundRequirement::isInclusive() const {
- return _inclusive;
-}
-
bool BoundRequirement::isMinusInf() const {
return _inclusive && _bound == Constant::minKey();
}
@@ -65,50 +56,78 @@ bool BoundRequirement::isPlusInf() const {
return _inclusive && _bound == Constant::maxKey();
}
-const ABT& BoundRequirement::getBound() const {
- return _bound;
-}
-
IntervalRequirement::IntervalRequirement()
: IntervalRequirement(BoundRequirement::makeMinusInf(), BoundRequirement::makePlusInf()) {}
IntervalRequirement::IntervalRequirement(BoundRequirement lowBound, BoundRequirement highBound)
- : _lowBound(std::move(lowBound)), _highBound(std::move(highBound)) {}
-
-bool IntervalRequirement::operator==(const IntervalRequirement& other) const {
- return _lowBound == other._lowBound && _highBound == other._highBound;
-}
+ : Base(std::move(lowBound), std::move(highBound)) {}
bool IntervalRequirement::isFullyOpen() const {
return _lowBound.isMinusInf() && _highBound.isPlusInf();
}
-bool IntervalRequirement::isEquality() const {
- return _lowBound.isInclusive() && _highBound.isInclusive() && _lowBound == _highBound;
+bool IntervalRequirement::isConstant() const {
+ return getLowBound().getBound().is<Constant>() && getHighBound().getBound().is<Constant>();
}
-const BoundRequirement& IntervalRequirement::getLowBound() const {
- return _lowBound;
+bool isIntervalReqFullyOpenDNF(const IntervalReqExpr::Node& n) {
+ if (auto singular = IntervalReqExpr::getSingularDNF(n); singular && singular->isFullyOpen()) {
+ return true;
+ }
+ return false;
}
-BoundRequirement& IntervalRequirement::getLowBound() {
- return _lowBound;
+CompoundBoundRequirement::CompoundBoundRequirement(bool inclusive, ABTVector bound)
+ : Base(inclusive, std::move(bound)) {
+ for (const auto& expr : _bound) {
+ assertExprSort(expr);
+ }
}
-const BoundRequirement& IntervalRequirement::getHighBound() const {
- return _highBound;
+bool CompoundBoundRequirement::isMinusInf() const {
+ return _inclusive && std::all_of(_bound.cbegin(), _bound.cend(), [](const ABT& element) {
+ return element == Constant::minKey();
+ });
}
-BoundRequirement& IntervalRequirement::getHighBound() {
- return _highBound;
+bool CompoundBoundRequirement::isPlusInf() const {
+ return _inclusive && std::all_of(_bound.cbegin(), _bound.cend(), [](const ABT& element) {
+ return element == Constant::maxKey();
+ });
}
-void IntervalRequirement::reverse() {
- std::swap(_lowBound, _highBound);
+bool CompoundBoundRequirement::isConstant() const {
+ return std::all_of(
+ _bound.cbegin(), _bound.cend(), [](const ABT& element) { return element.is<Constant>(); });
}
-bool IntervalRequirement::isConstant() const {
- return getLowBound().getBound().is<Constant>() && getHighBound().getBound().is<Constant>();
+size_t CompoundBoundRequirement::size() const {
+ return _bound.size();
+}
+
+void CompoundBoundRequirement::push_back(BoundRequirement bound) {
+ _inclusive &= bound.isInclusive();
+ _bound.push_back(std::move(bound.getBound()));
+}
+
+CompoundIntervalRequirement::CompoundIntervalRequirement()
+ : CompoundIntervalRequirement({true /*inclusive*/, {}}, {true /*inclusive*/, {}}) {}
+
+CompoundIntervalRequirement::CompoundIntervalRequirement(CompoundBoundRequirement lowBound,
+ CompoundBoundRequirement highBound)
+ : Base(std::move(lowBound), std::move(highBound)) {}
+
+bool CompoundIntervalRequirement::isFullyOpen() const {
+ return _lowBound.isMinusInf() && _highBound.isPlusInf();
+}
+
+size_t CompoundIntervalRequirement::size() const {
+ return _lowBound.size();
+}
+
+void CompoundIntervalRequirement::push_back(IntervalRequirement interval) {
+ _lowBound.push_back(std::move(interval.getLowBound()));
+ _highBound.push_back(std::move(interval.getHighBound()));
}
PartialSchemaKey::PartialSchemaKey(ABT path) : PartialSchemaKey(boost::none, std::move(path)) {}
@@ -126,13 +145,6 @@ bool PartialSchemaKey::operator==(const PartialSchemaKey& other) const {
return _projectionName == other._projectionName && _path == other._path;
}
-bool isIntervalReqFullyOpenDNF(const IntervalReqExpr::Node& n) {
- if (auto singular = IntervalReqExpr::getSingularDNF(n); singular && singular->isFullyOpen()) {
- return true;
- }
- return false;
-}
-
PartialSchemaRequirement::PartialSchemaRequirement(
boost::optional<ProjectionName> boundProjectionName,
IntervalReqExpr::Node intervals,
diff --git a/src/mongo/db/query/optimizer/index_bounds.h b/src/mongo/db/query/optimizer/index_bounds.h
index f26f88b0aff..3ecae65f19f 100644
--- a/src/mongo/db/query/optimizer/index_bounds.h
+++ b/src/mongo/db/query/optimizer/index_bounds.h
@@ -37,49 +37,155 @@
namespace mongo::optimizer {
-class BoundRequirement {
+/**
+ * Generic bound.
+ */
+template <class T>
+class Bound {
+public:
+ Bound(bool inclusive, T bound) : _inclusive(inclusive), _bound(std::move(bound)) {}
+
+ bool operator==(const Bound& other) const {
+ return _inclusive == other._inclusive && _bound == other._bound;
+ }
+
+ bool isInclusive() const {
+ return _inclusive;
+ }
+
+ const T& getBound() const {
+ return _bound;
+ }
+ T& getBound() {
+ return _bound;
+ }
+
+protected:
+ bool _inclusive;
+ T _bound;
+};
+
+/**
+ * Generic interval.
+ */
+template <class T>
+class Interval {
+public:
+ Interval(T lowBound, T highBound)
+ : _lowBound(std::move(lowBound)), _highBound(std::move(highBound)) {}
+
+ bool operator==(const Interval& other) const {
+ return _lowBound == other._lowBound && _highBound == other._highBound;
+ }
+
+ bool isEquality() const {
+ return _lowBound.isInclusive() && _highBound.isInclusive() && _lowBound == _highBound;
+ }
+
+ void reverse() {
+ std::swap(_lowBound, _highBound);
+ }
+
+ const T& getLowBound() const {
+ return _lowBound;
+ }
+ T& getLowBound() {
+ return _lowBound;
+ }
+
+ const T& getHighBound() const {
+ return _highBound;
+ }
+ T& getHighBound() {
+ return _highBound;
+ }
+
+protected:
+ T _lowBound;
+ T _highBound;
+};
+
+/**
+ * Represents a bound in an simple interval (interval over one projection). The bound can be a
+ * constant or an expression (e.g. a formula). This is a logical abstraction.
+ */
+class BoundRequirement : public Bound<ABT> {
+ using Base = Bound<ABT>;
+
public:
static BoundRequirement makeMinusInf();
static BoundRequirement makePlusInf();
BoundRequirement(bool inclusive, ABT bound);
- bool operator==(const BoundRequirement& other) const;
-
- bool isInclusive() const;
-
bool isMinusInf() const;
bool isPlusInf() const;
-
- const ABT& getBound() const;
-
-private:
- bool _inclusive;
- ABT _bound;
};
-class IntervalRequirement {
+/**
+ * Represents a simple interval (interval over one projection). This is a logical abstraction. It
+ * counts low and high bounds which may be inclusive or exclusive.
+ */
+class IntervalRequirement : public Interval<BoundRequirement> {
+ using Base = Interval<BoundRequirement>;
+
public:
IntervalRequirement();
IntervalRequirement(BoundRequirement lowBound, BoundRequirement highBound);
- bool operator==(const IntervalRequirement& other) const;
-
bool isFullyOpen() const;
- bool isEquality() const;
- void reverse();
bool isConstant() const;
+};
- const BoundRequirement& getLowBound() const;
- BoundRequirement& getLowBound();
- const BoundRequirement& getHighBound() const;
- BoundRequirement& getHighBound();
+/**
+ * Represents an expression (consisting of possibly nested unions and intersections) over an
+ * interval.
+ */
+using IntervalReqExpr = BoolExpr<IntervalRequirement>;
+bool isIntervalReqFullyOpenDNF(const IntervalReqExpr::Node& n);
-private:
- BoundRequirement _lowBound;
- BoundRequirement _highBound;
+/**
+ * Represents a bound in a compound interval, which encodes an equality prefix. It consists of a
+ * vector of expressions, which represents an index bound. This is a physical abstraction.
+ */
+class CompoundBoundRequirement : public Bound<ABTVector> {
+ using Base = Bound<ABTVector>;
+
+public:
+ CompoundBoundRequirement(bool inclusive, ABTVector bound);
+
+ bool isMinusInf() const;
+ bool isPlusInf() const;
+ bool isConstant() const;
+
+ size_t size() const;
+
+ // Extend the current compound bound with a simple bound. It is the caller's responsibility to
+ // ensure we confirm to an equality prefix.
+ void push_back(BoundRequirement bound);
};
+/**
+ * An interval of compound keys: each endpoint is a compound key, with one expression per index key.
+ * This is a physical primitive tied to a specific index.
+ */
+class CompoundIntervalRequirement : public Interval<CompoundBoundRequirement> {
+ using Base = Interval<CompoundBoundRequirement>;
+
+public:
+ CompoundIntervalRequirement();
+ CompoundIntervalRequirement(CompoundBoundRequirement lowBound,
+ CompoundBoundRequirement highBound);
+
+ bool isFullyOpen() const;
+
+ size_t size() const;
+ void push_back(IntervalRequirement interval);
+};
+
+// Unions and conjunctions of individual compound intervals.
+using CompoundIntervalReqExpr = BoolExpr<CompoundIntervalRequirement>;
+
struct PartialSchemaKey {
PartialSchemaKey(ABT path);
PartialSchemaKey(ProjectionName projectionName, ABT path);
@@ -97,9 +203,6 @@ struct PartialSchemaKey {
ABT _path;
};
-using IntervalReqExpr = BoolExpr<IntervalRequirement>;
-bool isIntervalReqFullyOpenDNF(const IntervalReqExpr::Node& n);
-
class PartialSchemaRequirement {
public:
PartialSchemaRequirement(boost::optional<ProjectionName> boundProjectionName,
@@ -290,21 +393,6 @@ struct ResidualRequirementWithCE {
};
using ResidualRequirementsWithCE = std::vector<ResidualRequirementWithCE>;
-
-// A sequence of intervals corresponding to a compound bound, with one entry for each index key.
-// This is a physical primitive as it is tied to a specific index. It contains a sequence of simple
-// bounds which form a single equality prefix. As such the first 0+ entries are inclusive
-// (equalities), followed by 0/1 possibly exclusive ranges, followed by 0+ unconstrained entries
-// (MinKey to MaxKey). When the IndexNode is lowered we need to compute a global inclusion/exclusion
-// for the entire compound interval, and we do so by determining if there are ANY exclusive simple
-// low bounds or high bounds. In this case the compound bound is exclusive (on the low or the high
-// side respectively).
-// TODO: SERVER-72784: Update representation of compound index bounds.
-using CompoundIntervalRequirement = std::vector<IntervalRequirement>;
-
-// Unions and conjunctions of individual compound intervals.
-using CompoundIntervalReqExpr = BoolExpr<CompoundIntervalRequirement>;
-
struct EqualityPrefixEntry {
EqualityPrefixEntry(size_t startPos);
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 9288904a1d6..c50787889b1 100644
--- a/src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp
+++ b/src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp
@@ -2070,13 +2070,14 @@ TEST(LogicalRewriter, SargableNodeRIN) {
ASSERT_EQ(1, ci.at(0)._eqPrefixes.size());
// The first index field ("a") is constrained to 1, the remaining fields are not constrained.
- ASSERT_EQ(
+ ASSERT_INTERVAL_AUTO( // NOLINT
"{\n"
" {\n"
- " {=Const [1], <fully open>, <fully open>, <fully open>, <fully open>}\n"
+ " {[Const [1 | minKey | minKey | minKey | minKey], Const [1 | maxKey | maxKey | "
+ "maxKey | maxKey]]}\n"
" }\n"
"}\n",
- ExplainGenerator::explainIntervalExpr(ci.at(0)._eqPrefixes.front()._interval));
+ ci.at(0)._eqPrefixes.front()._interval);
// No correlated projections.
ASSERT_EQ(0, ci.at(0)._correlatedProjNames.getVector().size());
@@ -2098,27 +2099,29 @@ TEST(LogicalRewriter, SargableNodeRIN) {
ASSERT_EQ(2, ci.at(1)._eqPrefixes.size());
// The first index field ("a") is again constrained to 1, and the remaining ones are not.
- ASSERT_EQ(
+ ASSERT_INTERVAL_AUTO( // NOLINT
"{\n"
" {\n"
- " {=Const [1], <fully open>, <fully open>, <fully open>, <fully open>}\n"
+ " {[Const [1 | minKey | minKey | minKey | minKey], Const [1 | maxKey | maxKey | ma"
+ "xKey | maxKey]]}\n"
" }\n"
"}\n",
- ExplainGenerator::explainIntervalExpr(ci.at(1)._eqPrefixes.at(0)._interval));
+ ci.at(1)._eqPrefixes.at(0)._interval);
// Second eq prefix begins at index field 2.
ASSERT_EQ(2, ci.at(1)._eqPrefixes.at(1)._startPos);
// The first two index fields are constrained to variables obtained from the first scan, the
// third one ("c") is bound to "2". The last two fields are unconstrained.
- ASSERT_EQ(
+ ASSERT_INTERVAL_AUTO( // NOLINT
"{\n"
" {\n"
- " {=Variable [evalTemp_26], =Variable [evalTemp_27], =Const [2], <fully open>, "
- "<fully open>}\n"
+ " {[Variable [evalTemp_26] | Variable [evalTemp_27] | Const [2] | Const [minKey] |"
+ " Const [minKey], Variable [evalTemp_26] | Variable [evalTemp_27] | Const [2] | Const [ma"
+ "xKey] | Const [maxKey]]}\n"
" }\n"
"}\n",
- ExplainGenerator::explainIntervalExpr(ci.at(1)._eqPrefixes.at(1)._interval));
+ ci.at(1)._eqPrefixes.at(1)._interval);
// Two correlated projections.
ASSERT_EQ(2, ci.at(1)._correlatedProjNames.getVector().size());
@@ -2138,35 +2141,37 @@ TEST(LogicalRewriter, SargableNodeRIN) {
ASSERT_EQ(4, ci.at(2)._correlatedProjNames.getVector().size());
// The first index field ("a") is again constrained to 1.
- ASSERT_EQ(
+ ASSERT_INTERVAL_AUTO( // NOLINT
"{\n"
" {\n"
- " {=Const [1], <fully open>, <fully open>, <fully open>, <fully open>}\n"
+ " {[Const [1 | minKey | minKey | minKey | minKey], Const [1 | maxKey | maxKey | ma"
+ "xKey | maxKey]]}\n"
" }\n"
"}\n",
- ExplainGenerator::explainIntervalExpr(ci.at(2)._eqPrefixes.at(0)._interval));
+ ci.at(2)._eqPrefixes.at(0)._interval);
// The first two index fields are constrained to variables obtained from the first scan, the
// third one ("c") is bound to "2". The last two fields are unconstrained.
- ASSERT_EQ(
+ ASSERT_INTERVAL_AUTO( // NOLINT
"{\n"
" {\n"
- " {=Variable [evalTemp_29], =Variable [evalTemp_30], =Const [2], <fully open>, "
- "<fully open>}\n"
+ " {[Variable [evalTemp_29] | Variable [evalTemp_30] | Const [2] | Const [minKey] |"
+ " Const [minKey], Variable [evalTemp_29] | Variable [evalTemp_30] | Const [2] | Const [ma"
+ "xKey] | Const [maxKey]]}\n"
" }\n"
"}\n",
- ExplainGenerator::explainIntervalExpr(ci.at(2)._eqPrefixes.at(1)._interval));
+ ci.at(2)._eqPrefixes.at(1)._interval);
// The first 4 index fields are constrained to variables from the second scan, and the last one
// to 4.
- ASSERT_EQ(
+ ASSERT_INTERVAL_AUTO( // NOLINT
"{\n"
" {\n"
- " {=Variable [evalTemp_29], =Variable [evalTemp_30], =Variable [evalTemp_31], "
- "=Variable [evalTemp_32], =Const [3]}\n"
+ " {=Variable [evalTemp_29] | Variable [evalTemp_30] | Variable [evalTemp_31] | Var"
+ "iable [evalTemp_32] | Const [3]}\n"
" }\n"
"}\n",
- ExplainGenerator::explainIntervalExpr(ci.at(2)._eqPrefixes.at(2)._interval));
+ ci.at(2)._eqPrefixes.at(2)._interval);
}
TEST(LogicalRewriter, EmptyArrayIndexBounds) {
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 6d9ad94b866..25f35a160a6 100644
--- a/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp
+++ b/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp
@@ -1084,8 +1084,8 @@ TEST(PhysRewriter, FilterIndexing3) {
"| | pa\n"
"| RefBlock: \n"
"| Variable [pa]\n"
- "IndexScan [{'<indexKey> 0': pa}, scanDefName: c1, indexDefName: index1, interval: {=Cons"
- "t [1], <fully open>}]\n",
+ "IndexScan [{'<indexKey> 0': pa}, scanDefName: c1, indexDefName: index1, interval: {[Const "
+ "[1 | minKey], Const [1 | maxKey]]}]\n",
optimized);
}
@@ -1148,8 +1148,8 @@ TEST(PhysRewriter, FilterIndexing3MultiKey) {
"Unique []\n"
"| projections: \n"
"| rid_0\n"
- "IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {=Const [1"
- "], <fully open>}]\n",
+ "IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {[Const [1"
+ " | minKey], Const [1 | maxKey]]}]\n",
optimized);
}
@@ -1259,7 +1259,7 @@ TEST(PhysRewriter, FilterIndexing4) {
"| Const [1]\n"
"IndexScan [{'<indexKey> 0': pa, '<indexKey> 1': evalTemp_12, '<indexKey> 2': evalTemp_13"
", '<indexKey> 3': evalTemp_14}, scanDefName: c1, indexDefName: index1, interval: {>Const"
- " [1], >Const [maxKey], >Const [maxKey], >Const [maxKey]}]\n",
+ " [1 | maxKey | maxKey | maxKey]}]\n",
optimized);
}
@@ -1342,7 +1342,7 @@ TEST(PhysRewriter, FilterIndexing5) {
"| | Variable [evalTemp_0]\n"
"| PathIdentity []\n"
"IndexScan [{'<indexKey> 0': pa, '<indexKey> 1': evalTemp_0}, scanDefName: c1, indexDefNa"
- "me: index1, interval: {>Const [0], >Const [maxKey]}]\n",
+ "me: index1, interval: {>Const [0 | maxKey]}]\n",
optimized);
}
@@ -1411,7 +1411,7 @@ TEST(PhysRewriter, FilterIndexing6) {
"| Variable [pa]\n"
"| Variable [pb]\n"
"IndexScan [{'<indexKey> 0': pa, '<indexKey> 1': pb}, scanDefName: c1, indexDefName: inde"
- "x1, interval: {=Const [0], >Const [0]}]\n",
+ "x1, interval: {(Const [0 | 0], Const [0 | maxKey]]}]\n",
optimized);
}
@@ -1727,8 +1727,8 @@ TEST(PhysRewriter, FilterIndexingRIN) {
"evalTemp_60}]\n"
"| | | Const [true]\n"
"| | IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: "
- "{=Variable [evalTemp_57], =Variable [evalTemp_58], =Variable [evalTemp_59], =Variable "
- "[evalTemp_60], =Const [3]}]\n"
+ "{=Variable [evalTemp_57] | Variable [evalTemp_58] | Variable [evalTemp_59] | Variable "
+ "[evalTemp_60] | Const [3]}]\n"
"| SpoolProducer [Lazy, id: 0, {evalTemp_59, evalTemp_60}]\n"
"| | | Const [true]\n"
"| Union [{evalTemp_59, evalTemp_60}]\n"
@@ -1739,17 +1739,19 @@ TEST(PhysRewriter, FilterIndexingRIN) {
"| | | | limit: 1\n"
"| | | | skip: 0\n"
"| | | IndexScan [{'<indexKey> 2': evalTemp_59, '<indexKey> 3': evalTemp_60}, "
- "scanDefName: c1, indexDefName: index1, interval: {(Variable [evalTemp_57], Variable "
- "[evalTemp_57]], (Variable [evalTemp_58], Variable [evalTemp_58]], >Variable [rinInner_2], "
- ">Variable [rinInner_3], =Const [maxKey]}]\n"
+ "scanDefName: c1, indexDefName: index1, interval: {(Variable [evalTemp_57] | Variable "
+ "[evalTemp_58] | Variable [rinInner_2] | Variable [rinInner_3] | Const [maxKey], Variable "
+ "[evalTemp_57] | Variable [evalTemp_58] | Const [maxKey] | Const [maxKey] | Const "
+ "[maxKey])}]\n"
"| | SpoolConsumer [Stack, id: 0, {rinInner_2, rinInner_3}]\n"
"| LimitSkip []\n"
"| | limitSkip:\n"
"| | limit: 1\n"
"| | skip: 0\n"
"| IndexScan [{'<indexKey> 2': evalTemp_59, '<indexKey> 3': evalTemp_60}, scanDefName: "
- "c1, indexDefName: index1, interval: {=Variable [evalTemp_57], =Variable [evalTemp_58], "
- ">Const [2], >Const [maxKey], >Const [maxKey]}]\n"
+ "c1, indexDefName: index1, interval: {(Variable [evalTemp_57] | Variable [evalTemp_58] | "
+ "Const [2] | Const [maxKey] | Const [maxKey], Variable [evalTemp_57] | Variable "
+ "[evalTemp_58] | Const [maxKey] | Const [maxKey] | Const [maxKey]]}]\n"
"SpoolProducer [Lazy, id: 1, {evalTemp_57, evalTemp_58}]\n"
"| | Const [true]\n"
"Union [{evalTemp_57, evalTemp_58}]\n"
@@ -1760,16 +1762,16 @@ TEST(PhysRewriter, FilterIndexingRIN) {
"| | | limit: 1\n"
"| | | skip: 0\n"
"| | IndexScan [{'<indexKey> 0': evalTemp_57, '<indexKey> 1': evalTemp_58}, "
- "scanDefName: c1, indexDefName: index1, interval: {>Variable [rinInner_0], >Variable "
- "[rinInner_1], =Const [maxKey], =Const [maxKey], =Const [maxKey]}]\n"
+ "scanDefName: c1, indexDefName: index1, interval: {(Variable [rinInner_0] | Variable "
+ "[rinInner_1] | Const [maxKey] | Const [maxKey] | Const [maxKey], Const [maxKey | maxKey | "
+ "maxKey | maxKey | maxKey])}]\n"
"| SpoolConsumer [Stack, id: 1, {rinInner_0, rinInner_1}]\n"
"LimitSkip []\n"
"| limitSkip:\n"
"| limit: 1\n"
"| skip: 0\n"
"IndexScan [{'<indexKey> 0': evalTemp_57, '<indexKey> 1': evalTemp_58}, scanDefName: c1, "
- "indexDefName: index1, interval: {>Const [1], >Const [maxKey], >Const [maxKey], >Const [m"
- "axKey], >Const [maxKey]}]\n",
+ "indexDefName: index1, interval: {>Const [1 | maxKey | maxKey | maxKey | maxKey]}]\n",
optimized);
}
@@ -1840,7 +1842,7 @@ TEST(PhysRewriter, FilterIndexingRIN1) {
"NestedLoopJoin [joinType: Inner, {pa}]\n"
"| | Const [true]\n"
"| IndexScan [{'<indexKey> 1': pb, '<rid>': rid_0}, scanDefName: c1, indexDefName: "
- "index1, interval: {=Variable [pa], >Const [2]}]\n"
+ "index1, interval: {(Variable [pa] | Const [2], Variable [pa] | Const [maxKey]]}]\n"
"SpoolProducer [Lazy, id: 1, {pa}]\n"
"| | Const [true]\n"
"Union [{pa}]\n"
@@ -1851,14 +1853,14 @@ TEST(PhysRewriter, FilterIndexingRIN1) {
"| | | limit: 1\n"
"| | | skip: 0\n"
"| | IndexScan [{'<indexKey> 0': pa}, scanDefName: c1, indexDefName: index1, interval: "
- "{(Const [1], Variable [rinInner_1]), (Const [maxKey], Const [minKey]]}, reversed]\n"
+ "{(Const [1 | maxKey], Variable [rinInner_1] | Const [minKey])}, reversed]\n"
"| SpoolConsumer [Stack, id: 1, {rinInner_1}]\n"
"LimitSkip []\n"
"| limitSkip:\n"
"| limit: 1\n"
"| skip: 0\n"
"IndexScan [{'<indexKey> 0': pa}, scanDefName: c1, indexDefName: index1, interval: {>Const "
- "[1], >Const [maxKey]}, reversed]\n",
+ "[1 | maxKey]}, reversed]\n",
optimized);
}
@@ -1951,9 +1953,11 @@ TEST(PhysRewriter, FilterIndexingRIN2) {
"| | | Variable [disjunction_3]\n"
"| | Union [{disjunction_3, rid_0}]\n"
"| | | IndexScan [{'<indexKey> 1': disjunction_3, '<rid>': rid_0}, scanDefName: c1, "
- "indexDefName: index1, interval: {=Variable [disjunction_0], [Const [7], Const [8]]}]\n"
+ "indexDefName: index1, interval: {[Variable [disjunction_0] | Const [7], Variable "
+ "[disjunction_0] | Const [8]]}]\n"
"| | IndexScan [{'<indexKey> 1': disjunction_3, '<rid>': rid_0}, scanDefName: c1, "
- "indexDefName: index1, interval: {=Variable [disjunction_0], [Const [5], Const [6]]}]\n"
+ "indexDefName: index1, interval: {[Variable [disjunction_0] | Const [5], Variable "
+ "[disjunction_0] | Const [6]]}]\n"
"| SpoolProducer [Lazy, id: 1, {disjunction_0}]\n"
"| | | Const [true]\n"
"| Union [{disjunction_0}]\n"
@@ -1964,14 +1968,14 @@ TEST(PhysRewriter, FilterIndexingRIN2) {
"| | | | limit: 1\n"
"| | | | skip: 0\n"
"| | | IndexScan [{'<indexKey> 0': disjunction_0}, scanDefName: c1, indexDefName: "
- "index1, interval: {(Variable [rinInner_1], Const [4]], =Const [maxKey]}]\n"
+ "index1, interval: {(Variable [rinInner_1] | Const [maxKey], Const [4 | maxKey])}]\n"
"| | SpoolConsumer [Stack, id: 1, {rinInner_1}]\n"
"| LimitSkip []\n"
"| | limitSkip:\n"
"| | limit: 1\n"
"| | skip: 0\n"
"| IndexScan [{'<indexKey> 0': disjunction_0}, scanDefName: c1, indexDefName: index1, "
- "interval: {[Const [3], Const [4]], <fully open>}]\n"
+ "interval: {[Const [3 | minKey], Const [4 | maxKey]]}]\n"
"NestedLoopJoin [joinType: Inner, {disjunction_0}]\n"
"| | Const [true]\n"
"| GroupBy []\n"
@@ -1984,9 +1988,11 @@ TEST(PhysRewriter, FilterIndexingRIN2) {
"| | Variable [disjunction_2]\n"
"| Union [{disjunction_2, rid_0}]\n"
"| | IndexScan [{'<indexKey> 1': disjunction_2, '<rid>': rid_0}, scanDefName: c1, "
- "indexDefName: index1, interval: {=Variable [disjunction_0], [Const [7], Const [8]]}]\n"
+ "indexDefName: index1, interval: {[Variable [disjunction_0] | Const [7], Variable "
+ "[disjunction_0] | Const [8]]}]\n"
"| IndexScan [{'<indexKey> 1': disjunction_2, '<rid>': rid_0}, scanDefName: c1, "
- "indexDefName: index1, interval: {=Variable [disjunction_0], [Const [5], Const [6]]}]\n"
+ "indexDefName: index1, interval: {[Variable [disjunction_0] | Const [5], Variable "
+ "[disjunction_0] | Const [6]]}]\n"
"SpoolProducer [Lazy, id: 0, {disjunction_0}]\n"
"| | Const [true]\n"
"Union [{disjunction_0}]\n"
@@ -1997,14 +2003,14 @@ TEST(PhysRewriter, FilterIndexingRIN2) {
"| | | limit: 1\n"
"| | | skip: 0\n"
"| | IndexScan [{'<indexKey> 0': disjunction_0}, scanDefName: c1, indexDefName: "
- "index1, interval: {(Variable [rinInner_0], Const [2]], =Const [maxKey]}]\n"
+ "index1, interval: {(Variable [rinInner_0] | Const [maxKey], Const [2 | maxKey])}]\n"
"| SpoolConsumer [Stack, id: 0, {rinInner_0}]\n"
"LimitSkip []\n"
"| limitSkip:\n"
"| limit: 1\n"
"| skip: 0\n"
"IndexScan [{'<indexKey> 0': disjunction_0}, scanDefName: c1, indexDefName: index1, "
- "interval: {[Const [1], Const [2]], <fully open>}]\n",
+ "interval: {[Const [1 | minKey], Const [2 | maxKey]]}]\n",
optimized);
}
@@ -2921,19 +2927,19 @@ TEST(PhysRewriter, CompoundIndex3) {
explainRoot, "child.child.leftChild.rightChild.children.0.child")
.Obj();
ASSERT_BSON_PATH("\"IndexScan\"", explainIndex1, "nodeType");
- ASSERT_BSON_PATH("2", explainIndex1, "interval.0.lowBound.bound.value");
- ASSERT_BSON_PATH("2", explainIndex1, "interval.0.highBound.bound.value");
- ASSERT_BSON_PATH("4", explainIndex1, "interval.1.lowBound.bound.value");
- ASSERT_BSON_PATH("4", explainIndex1, "interval.1.highBound.bound.value");
+ ASSERT_BSON_PATH("2", explainIndex1, "interval.lowBound.bound.0.value");
+ ASSERT_BSON_PATH("2", explainIndex1, "interval.highBound.bound.0.value");
+ ASSERT_BSON_PATH("4", explainIndex1, "interval.lowBound.bound.1.value");
+ ASSERT_BSON_PATH("4", explainIndex1, "interval.highBound.bound.1.value");
const BSONObj& explainIndex2 =
dotted_path_support::extractElementAtPath(explainRoot, "child.child.leftChild.leftChild")
.Obj();
ASSERT_BSON_PATH("\"IndexScan\"", explainIndex2, "nodeType");
- ASSERT_BSON_PATH("1", explainIndex2, "interval.0.lowBound.bound.value");
- ASSERT_BSON_PATH("1", explainIndex2, "interval.0.highBound.bound.value");
- ASSERT_BSON_PATH("3", explainIndex2, "interval.1.lowBound.bound.value");
- ASSERT_BSON_PATH("3", explainIndex2, "interval.1.highBound.bound.value");
+ ASSERT_BSON_PATH("1", explainIndex2, "interval.lowBound.bound.0.value");
+ ASSERT_BSON_PATH("1", explainIndex2, "interval.highBound.bound.0.value");
+ ASSERT_BSON_PATH("3", explainIndex2, "interval.lowBound.bound.1.value");
+ ASSERT_BSON_PATH("3", explainIndex2, "interval.highBound.bound.1.value");
}
TEST(PhysRewriter, CompoundIndex4Negative) {
@@ -3012,8 +3018,8 @@ TEST(PhysRewriter, CompoundIndex4Negative) {
ASSERT_BSON_PATH("\"Seek\"", explainRoot, "child.rightChild.child.child.nodeType");
ASSERT_BSON_PATH("\"IndexScan\"", explainRoot, "child.leftChild.nodeType");
- ASSERT_BSON_PATH("1", explainRoot, "child.leftChild.interval.0.lowBound.bound.value");
- ASSERT_BSON_PATH("1", explainRoot, "child.leftChild.interval.0.highBound.bound.value");
+ ASSERT_BSON_PATH("1", explainRoot, "child.leftChild.interval.lowBound.bound.0.value");
+ ASSERT_BSON_PATH("1", explainRoot, "child.leftChild.interval.highBound.bound.0.value");
}
TEST(PhysRewriter, CompoundIndex5) {
@@ -3079,13 +3085,13 @@ TEST(PhysRewriter, CompoundIndex5) {
"| aggregations: \n"
"Union [{rid_0}]\n"
"| | | IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval"
- ": {=Const [1], =Const [3]}]\n"
+ ": {=Const [1 | 3]}]\n"
"| | IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {="
- "Const [0], =Const [3]}]\n"
+ "Const [0 | 3]}]\n"
"| IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {=Cons"
- "t [1], =Const [2]}]\n"
+ "t [1 | 2]}]\n"
"IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {=Const [0"
- "], =Const [2]}]\n",
+ " | 2]}]\n",
optimized);
}
@@ -3156,14 +3162,14 @@ TEST(PhysRewriter, IndexBoundsIntersect) {
const BSONObj& explainIndexScan =
dotted_path_support::extractElementAtPath(explainRoot, "child.leftChild.child").Obj();
ASSERT_BSON_PATH("\"IndexScan\"", explainIndexScan, "nodeType");
- ASSERT_BSON_PATH("1", explainIndexScan, "interval.0.lowBound.bound.value");
- ASSERT_BSON_PATH("1", explainIndexScan, "interval.0.highBound.bound.value");
+ ASSERT_BSON_PATH("1", explainIndexScan, "interval.lowBound.bound.0.value");
+ ASSERT_BSON_PATH("1", explainIndexScan, "interval.highBound.bound.0.value");
const std::string lowBound = dotted_path_support::extractElementAtPath(
- explainIndexScan, "interval.1.lowBound.bound.value")
+ explainIndexScan, "interval.lowBound.bound.1.value")
.toString(false /*includeFieldName*/);
const std::string highBound = dotted_path_support::extractElementAtPath(
- explainIndexScan, "interval.1.highBound.bound.value")
+ explainIndexScan, "interval.highBound.bound.1.value")
.toString(false /*includeFieldName*/);
ASSERT_TRUE((filterVal == "70" && lowBound == "MinKey" && highBound == "90") ||
(filterVal == "90" && lowBound == "70" && highBound == "MaxKey"));
@@ -3490,7 +3496,7 @@ TEST(PhysRewriter, IndexResidualReq) {
"| PathCompare [Gt]\n"
"| Const [0]\n"
"IndexScan [{'<indexKey> 0': pa, '<indexKey> 1': evalTemp_2}, scanDefName: c1, indexDefNa"
- "me: index1, interval: {>Const [0], >Const [maxKey]}]\n",
+ "me: index1, interval: {>Const [0 | maxKey]}]\n",
phaseManager);
}
@@ -3573,8 +3579,8 @@ TEST(PhysRewriter, IndexResidualReq1) {
"| Seek [ridProjection: rid_0, {'<root>': root}, c1]\n"
"| RefBlock: \n"
"| Variable [rid_0]\n"
- "IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {=Const [0"
- "], =Const [0], =Const [0], <fully open>}]\n",
+ "IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {[Const [0"
+ " | 0 | 0 | minKey], Const [0 | 0 | 0 | maxKey]]}]\n",
optimized);
}
@@ -3647,7 +3653,7 @@ TEST(PhysRewriter, IndexResidualReq2) {
"| PathCompare [Eq]\n"
"| Const [0]\n"
"IndexScan [{'<indexKey> 2': evalTemp_10, '<rid>': rid_0}, scanDefName: c1, indexDefName:"
- " index1, interval: {=Const [0], <fully open>, <fully open>}]\n",
+ " index1, interval: {[Const [0 | minKey | minKey], Const [0 | maxKey | maxKey]]}]\n",
optimized);
}
@@ -3788,8 +3794,8 @@ TEST(PhysRewriter, ElemMatchIndex1) {
"Unique []\n"
"| projections: \n"
"| rid_0\n"
- "IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {=Const [1"
- "], (Const [70], Const [90])}]\n",
+ "IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {(Const [1"
+ " | 70], Const [1 | 90])}]\n",
optimized);
}
@@ -3946,7 +3952,7 @@ TEST(PhysRewriter, ObjectElemMatchResidual) {
"| | Variable [evalTemp_8]\n"
"| PathGet [c] PathTraverse [1] PathCompare [Eq] Const [1]\n"
"IndexScan [{'<indexKey> 1': evalTemp_8, '<rid>': rid_0}, scanDefName: c1, indexDefName: "
- "index1, interval: {<fully open>, <fully open>}]\n",
+ "index1, interval: {<fully open>}]\n",
optimized);
}
@@ -4190,7 +4196,7 @@ TEST(PhysRewriter, PathObj) {
"| projections: \n"
"| rid_0\n"
"IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {[Const [{"
- "}], Const [[]]), <Const [minKey]}]\n",
+ "} | minKey], Const [[] | minKey])}]\n",
optimized);
}
@@ -4282,9 +4288,9 @@ TEST(PhysRewriter, ArrayConstantIndex) {
"| aggregations: \n"
"Union [{rid_0}]\n"
"| IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {=Cons"
- "t [0], =Const [[1, 2, 3]]}]\n"
+ "t [0 | [1, 2, 3]]}]\n"
"IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {=Const [0"
- "], =Const [1]}]\n",
+ " | 1]}]\n",
optimized);
}
@@ -6155,8 +6161,8 @@ TEST(PhysRewriter, PerfOnlyPreds1) {
"| Seek [ridProjection: rid_0, {'a': pa, 'b': evalTemp_3}, c1]\n"
"| RefBlock: \n"
"| Variable [rid_0]\n"
- "IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {=Const [2"
- "], <Const [1]}]\n",
+ "IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index1, interval: {[Const [2"
+ " | minKey], Const [2 | 1])}]\n",
optimized);
}
diff --git a/src/mongo/db/query/optimizer/utils/abt_hash.cpp b/src/mongo/db/query/optimizer/utils/abt_hash.cpp
index 2b9454cfc84..7344f58491d 100644
--- a/src/mongo/db/query/optimizer/utils/abt_hash.cpp
+++ b/src/mongo/db/query/optimizer/utils/abt_hash.cpp
@@ -75,6 +75,13 @@ static void updateBoundHash(size_t& result, const BoundRequirement& bound) {
updateHash(result, ABTHashGenerator::generate(bound.getBound()));
};
+static void updateCompoundBoundHash(size_t& result, const CompoundBoundRequirement& bound) {
+ updateHash(result, std::hash<bool>()(bound.isInclusive()));
+ for (const auto& expr : bound.getBound()) {
+ updateHash(result, ABTHashGenerator::generate(expr));
+ }
+}
+
template <class T>
class IntervalHasher {
public:
@@ -87,9 +94,8 @@ public:
size_t computeHash(const CompoundIntervalRequirement& req) {
size_t result = 19;
- for (const auto& interval : req) {
- updateHash(result, computeHash(interval));
- }
+ updateCompoundBoundHash(result, req.getLowBound());
+ updateCompoundBoundHash(result, req.getHighBound());
return result;
}
diff --git a/src/mongo/db/query/optimizer/utils/interval_utils.cpp b/src/mongo/db/query/optimizer/utils/interval_utils.cpp
index 850dcb42efe..875e3adeec5 100644
--- a/src/mongo/db/query/optimizer/utils/interval_utils.cpp
+++ b/src/mongo/db/query/optimizer/utils/interval_utils.cpp
@@ -667,8 +667,7 @@ bool combineCompoundIntervalsDNF(CompoundIntervalReqExpr::Node& targetIntervals,
targetConjunction.cast<CompoundIntervalReqExpr::Conjunction>()->nodes()) {
const auto& targetInterval =
targetConjunct.cast<CompoundIntervalReqExpr::Atom>()->getExpr();
- if (!targetInterval.empty() && !targetInterval.back().isEquality() &&
- !sourceInterval.isFullyOpen()) {
+ if (!targetInterval.isEquality() && !sourceInterval.isFullyOpen()) {
// We do not have an equality prefix. Reject.
return false;
}
@@ -708,14 +707,11 @@ void padCompoundIntervalsDNF(CompoundIntervalReqExpr::Node& targetIntervals,
targetConjunct.cast<CompoundIntervalReqExpr::Atom>()->getExpr();
IntervalRequirement sourceInterval;
- if (!targetInterval.empty()) {
- const auto& lastInterval = targetInterval.back();
- if (!lastInterval.getLowBound().isInclusive()) {
- sourceInterval.getLowBound() = {false /*inclusive*/, Constant::maxKey()};
- }
- if (!lastInterval.getHighBound().isInclusive()) {
- sourceInterval.getHighBound() = {false /*inclusive*/, Constant::minKey()};
- }
+ if (!targetInterval.getLowBound().isInclusive()) {
+ sourceInterval.getLowBound() = {false /*inclusive*/, Constant::maxKey()};
+ }
+ if (!targetInterval.getHighBound().isInclusive()) {
+ sourceInterval.getHighBound() = {false /*inclusive*/, Constant::minKey()};
}
auto newInterval = targetInterval;
@@ -826,18 +822,9 @@ boost::optional<ABT> coerceIntervalToPathCompareEqMember(const IntervalReqExpr::
return boost::none;
}
-bool areCompoundIntervalsEqualities(const CompoundIntervalRequirement& intervals) {
- for (const auto& interval : intervals) {
- if (!interval.isEquality()) {
- return false;
- }
- }
- return true;
-}
-
bool isSimpleRange(const CompoundIntervalReqExpr::Node& interval) {
if (const auto singularInterval = CompoundIntervalReqExpr::getSingularDNF(interval);
- singularInterval && !areCompoundIntervalsEqualities(*singularInterval)) {
+ singularInterval && !singularInterval->isEquality()) {
return true;
}
return false;
diff --git a/src/mongo/db/query/optimizer/utils/interval_utils.h b/src/mongo/db/query/optimizer/utils/interval_utils.h
index 9bb8afc5903..ccaeb574e0f 100644
--- a/src/mongo/db/query/optimizer/utils/interval_utils.h
+++ b/src/mongo/db/query/optimizer/utils/interval_utils.h
@@ -130,11 +130,6 @@ boost::optional<ABT> coerceIntervalToPathCompareEqMember(const IntervalReqExpr::
/**
- * Returns true if all components of the compound interval are equalities.
- */
-bool areCompoundIntervalsEqualities(const CompoundIntervalRequirement& intervals);
-
-/**
* Returns true if the interval corresponds to a simple range (e.g >10 as opposed to a point
* equality or complex boolean expression of intervals).
*/
diff --git a/src/mongo/db/query/optimizer/utils/utils.cpp b/src/mongo/db/query/optimizer/utils/utils.cpp
index 24ab7aeddd4..eb87415b20d 100644
--- a/src/mongo/db/query/optimizer/utils/utils.cpp
+++ b/src/mongo/db/query/optimizer/utils/utils.cpp
@@ -2248,9 +2248,9 @@ public:
const auto& currentCorrelatedProjNames = params._correlatedProjNames;
// Update interval with current correlations.
for (size_t i = 0; i < startPos; i++) {
- BoundRequirement bound{true /*inclusive*/,
- make<Variable>(currentCorrelatedProjNames.at(i))};
- interval.at(i) = {bound, bound};
+ auto& lowBound = interval.getLowBound().getBound().at(i);
+ lowBound = make<Variable>(currentCorrelatedProjNames.at(i));
+ interval.getHighBound().getBound().at(i) = lowBound;
}
if (_currentEqPrefixIndex + 1 == _eqPrefixes.size()) {
@@ -2276,11 +2276,15 @@ public:
FieldProjectionMap innerFPM;
const auto addInnerBound = [&](BoundRequirement bound) {
- const auto& req = interval.at(innerInterval.size());
+ const size_t size = innerInterval.size();
if (reverse) {
- innerInterval.emplace_back(req.getLowBound(), std::move(bound));
+ BoundRequirement lowBound{false /*inclusive*/,
+ interval.getLowBound().getBound().at(size)};
+ innerInterval.push_back({std::move(lowBound), std::move(bound)});
} else {
- innerInterval.emplace_back(std::move(bound), req.getHighBound());
+ BoundRequirement highBound{false /*inclusive*/,
+ interval.getHighBound().getBound().at(size)};
+ innerInterval.push_back({std::move(bound), std::move(highBound)});
}
};