diff options
author | Matt Boros <matt.boros@mongodb.com> | 2022-10-31 20:56:19 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-10-31 22:17:01 +0000 |
commit | 201497d405225041f1806d25dfed475f3ec3f7ae (patch) | |
tree | 9338ef7e07b24fdac88784e777f1d86848eeedf9 /src | |
parent | 54aa1224e35847da94648f1e9f520af6c1606b99 (diff) | |
download | mongo-201497d405225041f1806d25dfed475f3ec3f7ae.tar.gz |
SERVER-70696 Determine presence of left and right intervals as a logical property
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/query/optimizer/cascades/implementers.cpp | 34 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/cascades/logical_props_derivation.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp | 42 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/explain.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/node.cpp | 22 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/node.h | 13 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp | 18 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/props.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/props.h | 7 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/utils/abt_hash.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/utils/utils.cpp | 11 | ||||
-rw-r--r-- | src/mongo/db/query/optimizer/utils/utils.h | 1 |
13 files changed, 95 insertions, 84 deletions
diff --git a/src/mongo/db/query/optimizer/cascades/implementers.cpp b/src/mongo/db/query/optimizer/cascades/implementers.cpp index 6039ac3cb43..835045a2611 100644 --- a/src/mongo/db/query/optimizer/cascades/implementers.cpp +++ b/src/mongo/db/query/optimizer/cascades/implementers.cpp @@ -189,8 +189,7 @@ public: if (node.getArraySize() == 0) { nodeCEMap.emplace(physNode.cast<Node>(), 0.0); - physNode = - make<LimitSkipNode>(properties::LimitSkipRequirement{0, 0}, std::move(physNode)); + physNode = make<LimitSkipNode>(LimitSkipRequirement{0, 0}, std::move(physNode)); nodeCEMap.emplace(physNode.cast<Node>(), 0.0); for (const ProjectionName& boundProjName : node.binder().names()) { @@ -208,8 +207,7 @@ public: } else { nodeCEMap.emplace(physNode.cast<Node>(), 1.0); - physNode = - make<LimitSkipNode>(properties::LimitSkipRequirement{1, 0}, std::move(physNode)); + physNode = make<LimitSkipNode>(LimitSkipRequirement{1, 0}, std::move(physNode)); nodeCEMap.emplace(physNode.cast<Node>(), 1.0); const ProjectionName valueScanProj = _prefixId.getNextId("valueScan"); @@ -710,7 +708,21 @@ public: return; } const bool isIndex = indexReqTarget == IndexReqTarget::Index; - if (isIndex && (!node.hasLeftIntervals() || !node.hasRightIntervals())) { + + const GroupIdType leftGroupId = + node.getLeftChild().cast<MemoLogicalDelegatorNode>()->getGroupId(); + const GroupIdType rightGroupId = + node.getRightChild().cast<MemoLogicalDelegatorNode>()->getGroupId(); + + const LogicalProps& leftLogicalProps = _memo.getLogicalProps(leftGroupId); + const LogicalProps& rightLogicalProps = _memo.getLogicalProps(rightGroupId); + + const bool hasProperIntervalLeft = + getPropertyConst<IndexingAvailability>(leftLogicalProps).hasProperInterval(); + const bool hasProperIntervalRight = + getPropertyConst<IndexingAvailability>(rightLogicalProps).hasProperInterval(); + + if (isIndex && (!hasProperIntervalLeft || !hasProperIntervalRight)) { // We need to have proper intervals on both sides. return; } @@ -729,14 +741,6 @@ public: } } - const GroupIdType leftGroupId = - node.getLeftChild().cast<MemoLogicalDelegatorNode>()->getGroupId(); - const GroupIdType rightGroupId = - node.getRightChild().cast<MemoLogicalDelegatorNode>()->getGroupId(); - - const LogicalProps& leftLogicalProps = _memo.getLogicalProps(leftGroupId); - const LogicalProps& rightLogicalProps = _memo.getLogicalProps(rightGroupId); - const CEType intersectedCE = getPropertyConst<CardinalityEstimate>(_logicalProps).getEstimate(); const CEType leftCE = getPropertyConst<CardinalityEstimate>(leftLogicalProps).getEstimate(); @@ -1660,9 +1664,9 @@ void addImplementers(const Metadata& metadata, const QueryHints& hints, const RIDProjectionsMap& ridProjections, PrefixId& prefixId, - const properties::PhysProps& physProps, + const PhysProps& physProps, PhysQueueAndImplPos& queue, - const properties::LogicalProps& logicalProps, + const LogicalProps& logicalProps, const OrderPreservingABTSet& logicalNodes, const PathToIntervalFn& pathToInterval) { ImplementationVisitor visitor(metadata, diff --git a/src/mongo/db/query/optimizer/cascades/logical_props_derivation.cpp b/src/mongo/db/query/optimizer/cascades/logical_props_derivation.cpp index c2ef5529e79..29d4ccc22ec 100644 --- a/src/mongo/db/query/optimizer/cascades/logical_props_derivation.cpp +++ b/src/mongo/db/query/optimizer/cascades/logical_props_derivation.cpp @@ -222,8 +222,7 @@ public: indexingAvailability.setEqPredsOnly(computeEqPredsOnly(node.getReqMap())); } - auto& satisfiedPartialIndexes = - getProperty<IndexingAvailability>(result).getSatisfiedPartialIndexes(); + auto& satisfiedPartialIndexes = indexingAvailability.getSatisfiedPartialIndexes(); for (const auto& [indexDefName, indexDef] : scanDef.getIndexDefs()) { if (!indexDef.getPartialReqMap().empty()) { auto intersection = node.getReqMap(); @@ -237,6 +236,8 @@ public: } } + indexingAvailability.setHasProperInterval(hasProperIntervals(node.getReqMap())); + return maybeUpdateNodePropsMap(node, std::move(result)); } diff --git a/src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp b/src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp index 421d03f73a1..0c9a923b2ed 100644 --- a/src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp +++ b/src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp @@ -941,8 +941,6 @@ struct SplitRequirementsResult { PartialSchemaRequirements _rightReqs; bool _hasFieldCoverage = true; - bool _hasLeftIntervals = false; - bool _hasRightIntervals = false; }; /** @@ -981,7 +979,6 @@ static SplitRequirementsResult splitRequirements( size_t index = 0; for (const auto& [key, req] : reqMap) { - const bool fullyOpenInterval = isFullyOpen.at(index); if (((1ull << index) & mask) != 0) { bool addedToLeft = false; @@ -1005,7 +1002,7 @@ static SplitRequirementsResult splitRequirements( // We cannot return index values if our interval can possibly contain Null. Instead, // we remove the output binding for the left side, and return the value from the // right (seek) side. - if (!fullyOpenInterval) { + if (!isFullyOpen.at(index)) { addRequirement( leftReqs, key, boost::none /*boundProjectionName*/, req.getIntervals()); addedToLeft = true; @@ -1018,9 +1015,6 @@ static SplitRequirementsResult splitRequirements( } if (addedToLeft) { - if (!fullyOpenInterval) { - result._hasLeftIntervals = true; - } if (indexFieldPrefixMapForScanDef) { if (auto pathPtr = key._path.cast<PathGet>(); pathPtr != nullptr && indexFieldPrefixMapForScanDef->count(pathPtr->name()) == 0) { @@ -1033,9 +1027,6 @@ static SplitRequirementsResult splitRequirements( } } else if (isIndex || !req.getIsPerfOnly()) { addRequirement(rightReqs, key, req.getBoundProjectionName(), req.getIntervals()); - if (!fullyOpenInterval) { - result._hasRightIntervals = true; - } } index++; } @@ -1141,10 +1132,13 @@ struct ExploreConvert<SargableNode> { continue; } - if (isIndex && (!splitResult._hasLeftIntervals || !splitResult._hasRightIntervals)) { - // Reject. Must have at least one proper interval on either side. + // Reject. Must have at least one proper interval on either side. + if (isIndex && + (!hasProperIntervals(splitResult._leftReqs) || + !hasProperIntervals(splitResult._rightReqs))) { continue; } + if (!splitResult._hasFieldCoverage) { // Reject rewrite. No suitable indexes. continue; @@ -1196,11 +1190,8 @@ struct ExploreConvert<SargableNode> { isIndex ? IndexReqTarget::Index : IndexReqTarget::Seek, scanDelegator); - ABT newRoot = make<RIDIntersectNode>(scanProjectionName, - splitResult._hasLeftIntervals, - splitResult._hasRightIntervals, - std::move(leftChild), - std::move(rightChild)); + ABT newRoot = make<RIDIntersectNode>( + scanProjectionName, std::move(leftChild), std::move(rightChild)); const auto& result = ctx.addNode(newRoot, false /*substitute*/); for (const MemoLogicalNodeId nodeId : result.second) { @@ -1290,14 +1281,27 @@ void reorderAgainstRIDIntersectNode(ABT::reference_type aboveNode, } const RIDIntersectNode& node = *belowNode.cast<RIDIntersectNode>(); - if (node.hasLeftIntervals() && hasLeftRef) { + const GroupIdType groupIdLeft = + node.getLeftChild().cast<MemoLogicalDelegatorNode>()->getGroupId(); + const bool hasProperIntervalLeft = + properties::getPropertyConst<properties::IndexingAvailability>( + ctx.getMemo().getLogicalProps(groupIdLeft)) + .hasProperInterval(); + if (hasProperIntervalLeft && hasLeftRef) { defaultReorder<AboveNode, RIDIntersectNode, DefaultChildAccessor, LeftChildAccessor, false /*substitute*/>(aboveNode, belowNode, ctx); } - if (node.hasRightIntervals() && hasRightRef) { + + const GroupIdType groupIdRight = + node.getRightChild().cast<MemoLogicalDelegatorNode>()->getGroupId(); + const bool hasProperIntervalRight = + properties::getPropertyConst<properties::IndexingAvailability>( + ctx.getMemo().getLogicalProps(groupIdRight)) + .hasProperInterval(); + if (hasProperIntervalRight && hasRightRef) { defaultReorder<AboveNode, RIDIntersectNode, DefaultChildAccessor, diff --git a/src/mongo/db/query/optimizer/explain.cpp b/src/mongo/db/query/optimizer/explain.cpp index 5c49f3ad74f..b1bb27568d5 100644 --- a/src/mongo/db/query/optimizer/explain.cpp +++ b/src/mongo/db/query/optimizer/explain.cpp @@ -1277,8 +1277,6 @@ public: printer.separator(" [") .fieldName("scanProjectionName", ExplainVersion::V3) .print(node.getScanProjectionName()); - printBooleanFlag(printer, "hasLeftIntervals", node.hasLeftIntervals()); - printBooleanFlag(printer, "hasRightIntervals", node.hasRightIntervals()); printer.separator("]"); nodeCEPropsPrint(printer, n, node); @@ -1767,6 +1765,7 @@ public: .fieldName("scanDefName") .print(prop.getScanDefName()); printBooleanFlag(printer, "eqPredsOnly", prop.getEqPredsOnly()); + printBooleanFlag(printer, "hasProperInterval", prop.hasProperInterval()); printer.separator("]"); if (!prop.getSatisfiedPartialIndexes().empty()) { 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 056a048658b..03980eb5db4 100644 --- a/src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp +++ b/src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp @@ -1476,7 +1476,8 @@ TEST(LogicalRewriter, SargableCE) { " | | projections: \n" " | | ptest\n" " | | indexingAvailability: \n" - " | | [groupId: 0, scanProjection: ptest, scanDefName: test, eqPredsOnly]\n" + " | | [groupId: 0, scanProjection: ptest, scanDefName: test, eqPredsOnly, " + "hasProperInterval]\n" " | | collectionAvailability: \n" " | | test\n" " | | distributionAvailability: \n" @@ -1510,7 +1511,8 @@ TEST(LogicalRewriter, SargableCE) { " | | projections: \n" " | | ptest\n" " | | indexingAvailability: \n" - " | | [groupId: 0, scanProjection: ptest, scanDefName: test, eqPredsOnly]\n" + " | | [groupId: 0, scanProjection: ptest, scanDefName: test, eqPredsOnly, " + "hasProperInterval]\n" " | | collectionAvailability: \n" " | | test\n" " | | distributionAvailability: \n" diff --git a/src/mongo/db/query/optimizer/node.cpp b/src/mongo/db/query/optimizer/node.cpp index d9b0086fb1e..1eed39980b1 100644 --- a/src/mongo/db/query/optimizer/node.cpp +++ b/src/mongo/db/query/optimizer/node.cpp @@ -290,15 +290,9 @@ bool EvaluationNode::operator==(const EvaluationNode& other) const { getChild() == other.getChild(); } -RIDIntersectNode::RIDIntersectNode(ProjectionName scanProjectionName, - const bool hasLeftIntervals, - const bool hasRightIntervals, - ABT leftChild, - ABT rightChild) +RIDIntersectNode::RIDIntersectNode(ProjectionName scanProjectionName, ABT leftChild, ABT rightChild) : Base(std::move(leftChild), std::move(rightChild)), - _scanProjectionName(std::move(scanProjectionName)), - _hasLeftIntervals(hasLeftIntervals), - _hasRightIntervals(hasRightIntervals) { + _scanProjectionName(std::move(scanProjectionName)) { assertNodeSort(getLeftChild()); assertNodeSort(getRightChild()); } @@ -321,23 +315,13 @@ ABT& RIDIntersectNode::getRightChild() { bool RIDIntersectNode::operator==(const RIDIntersectNode& other) const { return _scanProjectionName == other._scanProjectionName && - _hasLeftIntervals == other._hasLeftIntervals && - _hasRightIntervals == other._hasRightIntervals && getLeftChild() == other.getLeftChild() && - getRightChild() == other.getRightChild(); + getLeftChild() == other.getLeftChild() && getRightChild() == other.getRightChild(); } const ProjectionName& RIDIntersectNode::getScanProjectionName() const { return _scanProjectionName; } -bool RIDIntersectNode::hasLeftIntervals() const { - return _hasLeftIntervals; -} - -bool RIDIntersectNode::hasRightIntervals() const { - return _hasRightIntervals; -} - static ProjectionNameVector createSargableBindings(const PartialSchemaRequirements& reqMap) { ProjectionNameVector result; for (const auto& entry : reqMap) { diff --git a/src/mongo/db/query/optimizer/node.h b/src/mongo/db/query/optimizer/node.h index 155f1d69f29..880d2fd5480 100644 --- a/src/mongo/db/query/optimizer/node.h +++ b/src/mongo/db/query/optimizer/node.h @@ -389,11 +389,7 @@ class RIDIntersectNode final : public Operator<2>, public ExclusivelyLogicalNode using Base = Operator<2>; public: - RIDIntersectNode(ProjectionName scanProjectionName, - bool hasLeftIntervals, - bool hasRightIntervals, - ABT leftChild, - ABT rightChild); + RIDIntersectNode(ProjectionName scanProjectionName, ABT leftChild, ABT rightChild); bool operator==(const RIDIntersectNode& other) const; @@ -405,15 +401,8 @@ public: const ProjectionName& getScanProjectionName() const; - bool hasLeftIntervals() const; - bool hasRightIntervals() const; - private: const ProjectionName _scanProjectionName; - - // If true left and right children have at least one proper interval (not fully open). - const bool _hasLeftIntervals; - const bool _hasRightIntervals; }; /** 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 0d21c897dd8..789bcdeea81 100644 --- a/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp +++ b/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp @@ -120,7 +120,7 @@ TEST(PhysRewriter, PhysicalRewriterBasic) { "| | p1\n" "| | p2\n" "| | indexingAvailability: \n" - "| | [groupId: 0, scanProjection: p1, scanDefName: test]\n" + "| | [groupId: 0, scanProjection: p1, scanDefName: test, hasProperInterval]\n" "| | collectionAvailability: \n" "| | test\n" "| | distributionAvailability: \n" @@ -146,7 +146,7 @@ TEST(PhysRewriter, PhysicalRewriterBasic) { "| | p1\n" "| | p2\n" "| | indexingAvailability: \n" - "| | [groupId: 0, scanProjection: p1, scanDefName: test]\n" + "| | [groupId: 0, scanProjection: p1, scanDefName: test, hasProperInterval]\n" "| | collectionAvailability: \n" "| | test\n" "| | distributionAvailability: \n" @@ -738,7 +738,7 @@ TEST(PhysRewriter, FilterIndexing) { "| | root\n" "| RefBlock: \n" "| Variable [root]\n" - "RIDIntersect [root, hasLeftIntervals]\n" + "RIDIntersect [root]\n" "| Scan [c1]\n" "| BindBlock:\n" "| [root]\n" @@ -3212,7 +3212,7 @@ TEST(PhysRewriter, IndexResidualReq) { "| | pa\n" "| | root\n" "| | indexingAvailability: \n" - "| | [groupId: 0, scanProjection: root, scanDefName: c1]\n" + "| | [groupId: 0, scanProjection: root, scanDefName: c1, hasProperInterval]\n" "| | collectionAvailability: \n" "| | c1\n" "| | distributionAvailability: \n" @@ -3240,7 +3240,7 @@ TEST(PhysRewriter, IndexResidualReq) { "| | pa\n" "| | root\n" "| | indexingAvailability: \n" - "| | [groupId: 0, scanProjection: root, scanDefName: c1]\n" + "| | [groupId: 0, scanProjection: root, scanDefName: c1, hasProperInterval]\n" "| | collectionAvailability: \n" "| | c1\n" "| | distributionAvailability: \n" @@ -5930,7 +5930,7 @@ TEST(PhysRewriter, PerfOnlyPreds2) { ABT optimized = rootNode; phaseManager.getHints()._disableYieldingTolerantPlans = false; phaseManager.optimize(optimized); - ASSERT_BETWEEN(10, 15, phaseManager.getMemo().getStats()._physPlanExplorationCount); + ASSERT_BETWEEN(10, 17, phaseManager.getMemo().getStats()._physPlanExplorationCount); // Demonstrate an intersection plan, with predicates repeated on the Seek side. ASSERT_EXPLAIN_V2Compact( @@ -5963,16 +5963,16 @@ TEST(PhysRewriter, PerfOnlyPreds2) { "| Variable [rid_0]\n" "MergeJoin []\n" "| | | Condition\n" - "| | | rid_0 = rid_3\n" + "| | | rid_0 = rid_5\n" "| | Collation\n" "| | Ascending\n" "| Union []\n" "| | BindBlock:\n" - "| | [rid_3]\n" + "| | [rid_5]\n" "| | Source []\n" "| Evaluation []\n" "| | BindBlock:\n" - "| | [rid_3]\n" + "| | [rid_5]\n" "| | Variable [rid_0]\n" "| IndexScan [{'<rid>': rid_0}, scanDefName: c1, indexDefName: index2, interval: {[Const " "[2], Const [2]]}]\n" diff --git a/src/mongo/db/query/optimizer/props.cpp b/src/mongo/db/query/optimizer/props.cpp index 854882292be..c0079a78ed2 100644 --- a/src/mongo/db/query/optimizer/props.cpp +++ b/src/mongo/db/query/optimizer/props.cpp @@ -282,17 +282,20 @@ IndexingAvailability::IndexingAvailability(GroupIdType scanGroupId, ProjectionName scanProjection, std::string scanDefName, const bool eqPredsOnly, + const bool hasProperInterval, opt::unordered_set<std::string> satisfiedPartialIndexes) : _scanGroupId(scanGroupId), _scanProjection(std::move(scanProjection)), _scanDefName(std::move(scanDefName)), _eqPredsOnly(eqPredsOnly), - _satisfiedPartialIndexes(std::move(satisfiedPartialIndexes)) {} + _satisfiedPartialIndexes(std::move(satisfiedPartialIndexes)), + _hasProperInterval(hasProperInterval) {} bool IndexingAvailability::operator==(const IndexingAvailability& other) const { return _scanGroupId == other._scanGroupId && _scanProjection == other._scanProjection && _scanDefName == other._scanDefName && _eqPredsOnly == other._eqPredsOnly && - _satisfiedPartialIndexes == other._satisfiedPartialIndexes; + _satisfiedPartialIndexes == other._satisfiedPartialIndexes && + _hasProperInterval == other._hasProperInterval; } GroupIdType IndexingAvailability::getScanGroupId() const { @@ -327,6 +330,14 @@ void IndexingAvailability::setEqPredsOnly(const bool value) { _eqPredsOnly = value; } +bool IndexingAvailability::hasProperInterval() const { + return _hasProperInterval; +} + +void IndexingAvailability::setHasProperInterval(const bool hasProperInterval) { + _hasProperInterval = hasProperInterval; +} + CollectionAvailability::CollectionAvailability(opt::unordered_set<std::string> scanDefSet) : _scanDefSet(std::move(scanDefSet)) {} diff --git a/src/mongo/db/query/optimizer/props.h b/src/mongo/db/query/optimizer/props.h index adfb0f82847..e7ac16f227d 100644 --- a/src/mongo/db/query/optimizer/props.h +++ b/src/mongo/db/query/optimizer/props.h @@ -399,6 +399,7 @@ public: ProjectionName scanProjection, std::string scanDefName, bool eqPredsOnly, + bool hasProperInterval, opt::unordered_set<std::string> satisfiedPartialIndexes); bool operator==(const IndexingAvailability& other) const; @@ -415,6 +416,9 @@ public: bool getEqPredsOnly() const; void setEqPredsOnly(bool value); + bool hasProperInterval() const; + void setHasProperInterval(bool hasProperInterval); + private: GroupIdType _scanGroupId; const ProjectionName _scanProjection; @@ -427,6 +431,9 @@ private: // Set of indexes with partial indexes whose partial filters are satisfied for the current // group. opt::unordered_set<std::string> _satisfiedPartialIndexes; + + // True if there is at least one proper interval in a sargable node in this group. + bool _hasProperInterval; }; diff --git a/src/mongo/db/query/optimizer/utils/abt_hash.cpp b/src/mongo/db/query/optimizer/utils/abt_hash.cpp index 90585939882..b9f8d5a2517 100644 --- a/src/mongo/db/query/optimizer/utils/abt_hash.cpp +++ b/src/mongo/db/query/optimizer/utils/abt_hash.cpp @@ -195,8 +195,6 @@ public: size_t rightChildResult) { // Specifically always including children. return computeHashSeq<45>(std::hash<ProjectionName>()(node.getScanProjectionName()), - std::hash<bool>()(node.hasLeftIntervals()), - std::hash<bool>()(node.hasRightIntervals()), leftChildResult, rightChildResult); } diff --git a/src/mongo/db/query/optimizer/utils/utils.cpp b/src/mongo/db/query/optimizer/utils/utils.cpp index 6c2dac94c96..a676e28179b 100644 --- a/src/mongo/db/query/optimizer/utils/utils.cpp +++ b/src/mongo/db/query/optimizer/utils/utils.cpp @@ -102,6 +102,7 @@ properties::LogicalProps createInitialScanProps(const ProjectionName& projection projectionName, scanDefName, true /*eqPredsOnly*/, + false /*hasProperInterval*/, {} /*satisfiedPartialIndexes*/), properties::CollectionAvailability({scanDefName}), properties::DistributionAvailability(std::move(distributions))); @@ -1928,4 +1929,14 @@ bool pathEndsInTraverse(const optimizer::ABT& path) { return optimizer::algebra::transport<false>(path, t); } +bool hasProperIntervals(const PartialSchemaRequirements& reqMap) { + // Compute if this node has any proper (not fully open) intervals. + for (const auto& [key, req] : reqMap) { + if (!isIntervalReqFullyOpenDNF(req.getIntervals())) { + return true; + } + } + return false; +} + } // namespace mongo::optimizer diff --git a/src/mongo/db/query/optimizer/utils/utils.h b/src/mongo/db/query/optimizer/utils/utils.h index e4a2b2367a6..48589995a14 100644 --- a/src/mongo/db/query/optimizer/utils/utils.h +++ b/src/mongo/db/query/optimizer/utils/utils.h @@ -347,4 +347,5 @@ ABT lowerIntervals(PrefixId& prefixId, */ bool pathEndsInTraverse(const optimizer::ABT& path); +bool hasProperIntervals(const PartialSchemaRequirements& reqMap); } // namespace mongo::optimizer |