summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/planner_access.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/query/planner_access.cpp')
-rw-r--r--src/mongo/db/query/planner_access.cpp112
1 files changed, 75 insertions, 37 deletions
diff --git a/src/mongo/db/query/planner_access.cpp b/src/mongo/db/query/planner_access.cpp
index 2e8e77c44b5..de294ad661a 100644
--- a/src/mongo/db/query/planner_access.cpp
+++ b/src/mongo/db/query/planner_access.cpp
@@ -45,14 +45,21 @@
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_array.h"
#include "mongo/db/matcher/expression_geo.h"
+#include "mongo/db/matcher/expression_internal_expr_comparison.h"
+#include "mongo/db/matcher/expression_leaf.h"
#include "mongo/db/matcher/expression_text.h"
+#include "mongo/db/matcher/expression_tree.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/query/index_bounds.h"
#include "mongo/db/query/index_bounds_builder.h"
#include "mongo/db/query/index_tag.h"
#include "mongo/db/query/indexability.h"
+#include "mongo/db/query/parsed_find_command.h"
#include "mongo/db/query/planner_wildcard_helpers.h"
#include "mongo/db/query/query_knobs_gen.h"
#include "mongo/db/query/query_planner.h"
#include "mongo/db/query/query_planner_common.h"
+#include "mongo/db/query/record_id_range.h"
#include "mongo/db/record_id_helpers.h"
#include "mongo/logv2/log.h"
#include "mongo/util/transitional_tools_do_not_use/vector_spooling.h"
@@ -269,7 +276,8 @@ bool compatibleCollator(const QueryPlannerParams& params,
void handleRIDRangeMinMax(const CanonicalQuery& query,
CollectionScanNode* collScan,
const QueryPlannerParams& params,
- const CollatorInterface* collator) {
+ const CollatorInterface* collator,
+ RecordIdRange& recordRange) {
BSONObj minObj = query.getFindCommandRequest().getMin();
BSONObj maxObj = query.getFindCommandRequest().getMax();
if (minObj.isEmpty() && maxObj.isEmpty()) {
@@ -289,16 +297,16 @@ void handleRIDRangeMinMax(const CanonicalQuery& query,
if (!maxObj.isEmpty() && compatibleCollator(params, collator, maxObj.firstElement())) {
// max() is exclusive.
// Assumes clustered collection scans are only supported with the forward direction.
- collScan->boundInclusion =
- CollectionScanParams::ScanBoundInclusion::kIncludeStartRecordOnly;
- setLowestRecord(collScan->maxRecord,
- IndexBoundsBuilder::objFromElement(maxObj.firstElement(), collator));
+ recordRange.maybeNarrowMax(
+ IndexBoundsBuilder::objFromElement(maxObj.firstElement(), collator),
+ false /* NOT inclusive*/);
}
if (!minObj.isEmpty() && compatibleCollator(params, collator, minObj.firstElement())) {
// The min() is inclusive as are bounded collection scans by default.
- setHighestRecord(collScan->minRecord,
- IndexBoundsBuilder::objFromElement(minObj.firstElement(), collator));
+ recordRange.maybeNarrowMin(
+ IndexBoundsBuilder::objFromElement(minObj.firstElement(), collator),
+ true /* inclusive*/);
}
}
@@ -323,7 +331,8 @@ void handleRIDRangeMinMax(const CanonicalQuery& query,
[[nodiscard]] bool handleRIDRangeScan(const MatchExpression* conjunct,
CollectionScanNode* collScan,
const QueryPlannerParams& params,
- const CollatorInterface* collator) {
+ const CollatorInterface* collator,
+ RecordIdRange& recordRange) {
invariant(params.clusteredInfo);
if (conjunct == nullptr) {
@@ -334,7 +343,8 @@ void handleRIDRangeMinMax(const CanonicalQuery& query,
if (andMatchPtr != nullptr) {
bool atLeastOneConjunctCompatibleCollation = false;
for (size_t index = 0; index < andMatchPtr->numChildren(); index++) {
- if (handleRIDRangeScan(andMatchPtr->getChild(index), collScan, params, collator)) {
+ if (handleRIDRangeScan(
+ andMatchPtr->getChild(index), collScan, params, collator, recordRange)) {
atLeastOneConjunctCompatibleCollation = true;
}
}
@@ -378,31 +388,35 @@ void handleRIDRangeMinMax(const CanonicalQuery& query,
}
}
+ // {min,max}RecordId will bound the range of ids scanned to the highest and lowest present
+ // in the InMatchExpression, but the filter is still required to filter to _exactly_ the
+ // requested matches.
+
// Finally, tighten the collscan bounds with the min/max bounds for the $in.
- if (minBound) {
- setHighestRecord(collScan->minRecord, *minBound);
- }
- if (maxBound) {
- setLowestRecord(collScan->maxRecord, *maxBound);
- }
+ recordRange.intersectRange(minBound, maxBound);
return allEltsCollationCompatible;
}
- auto match = dynamic_cast<const ComparisonMatchExpression*>(conjunct);
+ auto match = dynamic_cast<const ComparisonMatchExpressionBase*>(conjunct);
if (match == nullptr) {
return false; // Not a comparison match expression.
}
const auto& element = match->getData();
- // Set coarse min/max bounds based on type in case we can't set tight bounds.
- BSONObjBuilder minb;
- minb.appendMinForType("", element.type());
- setHighestRecord(collScan->minRecord, minb.obj());
+ if (!ComparisonMatchExpressionBase::isInternalExprComparison(match->matchType())) {
+ // Internal comparisons e.g., $_internalExprGt do _not_ carry type bracketing
+ // semantics (consistent with `$expr{$gt:[a,b]}`).
+ // For other comparisons which _do_ perform type bracketing, the RecordId bounds
+ // may be tightened here.
+ BSONObjBuilder minb;
+ minb.appendMinForType("", element.type());
+ recordRange.maybeNarrowMin(minb.obj(), true /* inclusive */);
- BSONObjBuilder maxb;
- maxb.appendMaxForType("", element.type());
- setLowestRecord(collScan->maxRecord, maxb.obj());
+ BSONObjBuilder maxb;
+ maxb.appendMaxForType("", element.type());
+ recordRange.maybeNarrowMax(maxb.obj(), true /* inclusive */);
+ }
bool compatible = compatibleCollator(params, collator, element);
if (!compatible) {
@@ -410,19 +424,33 @@ void handleRIDRangeMinMax(const CanonicalQuery& query,
}
// Even if the collations don't match at this point, it's fine,
- // because the bounds exclude values that use it
- const auto collated = IndexBoundsBuilder::objFromElement(element, collator);
- if (dynamic_cast<const EqualityMatchExpression*>(match)) {
- setHighestRecord(collScan->minRecord, collated);
- setLowestRecord(collScan->maxRecord, collated);
- } else if (dynamic_cast<const LTMatchExpression*>(match) ||
- dynamic_cast<const LTEMatchExpression*>(match)) {
- setLowestRecord(collScan->maxRecord, collated);
- } else if (dynamic_cast<const GTMatchExpression*>(match) ||
- dynamic_cast<const GTEMatchExpression*>(match)) {
- setHighestRecord(collScan->minRecord, collated);
+ // because the bounds exclude values that use it.
+ const BSONObj collated = IndexBoundsBuilder::objFromElement(element, collator);
+ using MType = MatchExpression::MatchType;
+ switch (match->matchType()) {
+ case MType::EQ:
+ case MType::INTERNAL_EXPR_EQ:
+ recordRange.maybeNarrowMin(collated, true /* inclusive */);
+ recordRange.maybeNarrowMax(collated, true /* inclusive */);
+ break;
+ case MType::LT:
+ case MType::INTERNAL_EXPR_LT:
+ recordRange.maybeNarrowMax(collated, false /* EXclusive */);
+ break;
+ case MType::LTE:
+ case MType::INTERNAL_EXPR_LTE:
+ recordRange.maybeNarrowMax(collated, true /* inclusive */);
+ break;
+ case MType::GT:
+ case MType::INTERNAL_EXPR_GT:
+ recordRange.maybeNarrowMin(collated, false /* EXclusive */);
+ break;
+ case MType::GTE:
+ case MType::INTERNAL_EXPR_GTE:
+ recordRange.maybeNarrowMin(collated, true /* inclusive */);
+ break;
+ default:;
}
-
return true;
}
@@ -528,11 +556,21 @@ std::unique_ptr<QuerySolutionNode> QueryPlannerAccess::makeCollectionScan(
// query is guaranteed to exclude values of the cluster key which are affected by collation.
// If so, then even if the query and collection collations differ, the collation difference
// won't affect the query results. In that case, we can say hasCompatibleCollation is true.
+
+ RecordIdRange recordRange;
+ // min/max records may have been set if oplog or change collection.
+ recordRange.intersectRange(csn->minRecord, csn->maxRecord);
bool compatibleCollation =
- handleRIDRangeScan(csn->filter.get(), csn.get(), params, queryCollator);
+ handleRIDRangeScan(csn->filter.get(), csn.get(), params, queryCollator, recordRange);
csn->hasCompatibleCollation |= compatibleCollation;
- handleRIDRangeMinMax(query, csn.get(), params, queryCollator);
+ handleRIDRangeMinMax(query, csn.get(), params, queryCollator, recordRange);
+
+ csn->minRecord = recordRange.getMin();
+ csn->maxRecord = recordRange.getMax();
+
+ csn->boundInclusion = CollectionScanParams::makeInclusion(recordRange.isMinInclusive(),
+ recordRange.isMaxInclusive());
}
return csn;