diff options
Diffstat (limited to 'src/mongo/db/query/optimizer')
26 files changed, 581 insertions, 890 deletions
diff --git a/src/mongo/db/query/optimizer/SConscript b/src/mongo/db/query/optimizer/SConscript index a128b8eecc1..70ce6442e3a 100644 --- a/src/mongo/db/query/optimizer/SConscript +++ b/src/mongo/db/query/optimizer/SConscript @@ -91,17 +91,6 @@ env.Library( ], ) -# Default costing module. -env.Library( - target="optimizer_default_costing", - source=[ - "cascades/cost_derivation.cpp", - ], - LIBDEPS=[ - "optimizer_memo", - ], -) - # Main optimizer target. env.Library( target="optimizer", @@ -112,7 +101,6 @@ env.Library( LIBDEPS=[ "optimizer_cascades", "optimizer_default_ce", - "optimizer_default_costing", "optimizer_rewrites", ], ) @@ -126,6 +114,7 @@ env.Library( LIBDEPS=[ # We do not depend on the entire pipeline target. "$BUILD_DIR/mongo/db/pipeline/abt_utils", + "$BUILD_DIR/mongo/db/query/cost_model/query_cost_model", "$BUILD_DIR/mongo/db/query/optimizer/optimizer", "$BUILD_DIR/mongo/unittest/unittest", ], diff --git a/src/mongo/db/query/optimizer/cascades/cost_derivation.cpp b/src/mongo/db/query/optimizer/cascades/cost_derivation.cpp deleted file mode 100644 index f58380b50b3..00000000000 --- a/src/mongo/db/query/optimizer/cascades/cost_derivation.cpp +++ /dev/null @@ -1,446 +0,0 @@ -/** - * Copyright (C) 2022-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#include "mongo/db/query/optimizer/cascades/cost_derivation.h" -#include "mongo/db/query/optimizer/defs.h" - -namespace mongo::optimizer::cascades { - -using namespace properties; - -struct CostAndCEInternal { - CostAndCEInternal(double cost, CEType ce) : _cost(cost), _ce(ce) { - uassert(8423334, "Invalid cost.", !std::isnan(cost) && cost >= 0.0); - uassert(8423332, "Invalid cardinality", std::isfinite(ce) && ce >= 0.0); - } - double _cost; - CEType _ce; -}; - -class CostDerivation { - // These cost should reflect estimated aggregated execution time in milliseconds. - static constexpr double ms = 1.0e-3; - - // Startup cost of an operator. This is the minimal cost of an operator since it is - // present even if it doesn't process any input. - // TODO: calibrate the cost individually for each operator - static constexpr double kStartupCost = 0.000001; - - // TODO: collection scan should depend on the width of the doc. - // TODO: the actual measured cost is (0.4 * ms), however we increase it here because currently - // it is not possible to estimate the cost of a collection scan vs a full index scan. - static constexpr double kScanIncrementalCost = 0.6 * ms; - - // TODO: cost(N fields) ~ (0.55 + 0.025 * N) - static constexpr double kIndexScanIncrementalCost = 0.5 * ms; - - // TODO: cost(N fields) ~ 0.7 + 0.19 * N - static constexpr double kSeekCost = 2.0 * ms; - - // TODO: take the expression into account. - // cost(N conditions) = 0.2 + N * ??? - static constexpr double kFilterIncrementalCost = 0.2 * ms; - // TODO: the cost of projection depends on number of fields: cost(N fields) ~ 0.1 + 0.2 * N - static constexpr double kEvalIncrementalCost = 2.0 * ms; - - // TODO: cost(N fields) ~ 0.04 + 0.03*(N^2) - static constexpr double kGroupByIncrementalCost = 0.07 * ms; - static constexpr double kUnwindIncrementalCost = 0.03 * ms; // TODO: not yet calibrated - // TODO: not yet calibrated, should be at least as expensive as a filter - static constexpr double kBinaryJoinIncrementalCost = 0.2 * ms; - static constexpr double kHashJoinIncrementalCost = 0.05 * ms; // TODO: not yet calibrated - static constexpr double kMergeJoinIncrementalCost = 0.02 * ms; // TODO: not yet calibrated - - static constexpr double kUniqueIncrementalCost = 0.7 * ms; - - // TODO: implement collation cost that depends on number and size of sorted fields - // Based on a mix of int and str(64) fields: - // 1 sort field: sort_cost(N) = 1.0/10 * N * log(N) - // 5 sort fields: sort_cost(N) = 2.5/10 * N * log(N) - // 10 sort fields: sort_cost(N) = 3.0/10 * N * log(N) - // field_cost_coeff(F) ~ 0.75 + 0.2 * F - static constexpr double kCollationIncrementalCost = 2.5 * ms; // 5 fields avg - static constexpr double kCollationWithLimitIncrementalCost = - 1.0 * ms; // TODO: not yet calibrated - - static constexpr double kUnionIncrementalCost = 0.02 * ms; - - static constexpr double kExchangeIncrementalCost = 0.1 * ms; // TODO: not yet calibrated - -public: - CostAndCEInternal operator()(const ABT& /*n*/, const PhysicalScanNode& /*node*/) { - // Default estimate for scan. - const double collectionScanCost = - kStartupCost + kScanIncrementalCost * _cardinalityEstimate; - return {collectionScanCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const CoScanNode& /*node*/) { - // Assumed to be free. - return {kStartupCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const IndexScanNode& node) { - const double indexScanCost = - kStartupCost + kIndexScanIncrementalCost * _cardinalityEstimate; - return {indexScanCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const SeekNode& /*node*/) { - // SeekNode should deliver one result via cardinality estimate override. - // TODO: consider using node.getProjectionMap()._fieldProjections.size() to make the cost - // dependent on the size of the projection - const double seekCost = kStartupCost + kSeekCost * _cardinalityEstimate; - return {seekCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const MemoLogicalDelegatorNode& node) { - const LogicalProps& childLogicalProps = _memo.getLogicalProps(node.getGroupId()); - // Notice that unlike all physical nodes, this logical node takes it cardinality directly - // from the memo group logical property, ignoring _cardinalityEstimate. - CEType baseCE = getPropertyConst<CardinalityEstimate>(childLogicalProps).getEstimate(); - - if (hasProperty<IndexingRequirement>(_physProps)) { - const auto& indexingReq = getPropertyConst<IndexingRequirement>(_physProps); - if (indexingReq.getIndexReqTarget() == IndexReqTarget::Seek) { - // If we are performing a seek, normalize against the scan group cardinality. - const GroupIdType scanGroupId = - getPropertyConst<IndexingAvailability>(childLogicalProps).getScanGroupId(); - if (scanGroupId == node.getGroupId()) { - baseCE = 1.0; - } else { - const CEType scanGroupCE = - getPropertyConst<CardinalityEstimate>(_memo.getLogicalProps(scanGroupId)) - .getEstimate(); - if (scanGroupCE > 0.0) { - baseCE /= scanGroupCE; - } - } - } - } - - return {0.0, getAdjustedCE(baseCE, _physProps)}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const MemoPhysicalDelegatorNode& /*node*/) { - uasserted(6624040, "Should not be costing physical delegator nodes."); - } - - CostAndCEInternal operator()(const ABT& /*n*/, const FilterNode& node) { - CostAndCEInternal childResult = deriveChild(node.getChild(), 0); - double filterCost = childResult._cost; - if (!isTrivialExpr<EvalFilter>(node.getFilter())) { - // Non-trivial filter. - filterCost += kStartupCost + kFilterIncrementalCost * childResult._ce; - } - return {filterCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const EvaluationNode& node) { - CostAndCEInternal childResult = deriveChild(node.getChild(), 0); - double evalCost = childResult._cost; - if (!isTrivialExpr<EvalPath>(node.getProjection())) { - // Non-trivial projection. - evalCost += kStartupCost + kEvalIncrementalCost * _cardinalityEstimate; - } - return {evalCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const BinaryJoinNode& node) { - CostAndCEInternal leftChildResult = deriveChild(node.getLeftChild(), 0); - CostAndCEInternal rightChildResult = deriveChild(node.getRightChild(), 1); - const double joinCost = kStartupCost + - kBinaryJoinIncrementalCost * (leftChildResult._ce + rightChildResult._ce) + - leftChildResult._cost + rightChildResult._cost; - return {joinCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const HashJoinNode& node) { - CostAndCEInternal leftChildResult = deriveChild(node.getLeftChild(), 0); - CostAndCEInternal rightChildResult = deriveChild(node.getRightChild(), 1); - - // TODO: distinguish build side and probe side. - const double hashJoinCost = kStartupCost + - kHashJoinIncrementalCost * (leftChildResult._ce + rightChildResult._ce) + - leftChildResult._cost + rightChildResult._cost; - return {hashJoinCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const MergeJoinNode& node) { - CostAndCEInternal leftChildResult = deriveChild(node.getLeftChild(), 0); - CostAndCEInternal rightChildResult = deriveChild(node.getRightChild(), 1); - - const double mergeJoinCost = kStartupCost + - kMergeJoinIncrementalCost * (leftChildResult._ce + rightChildResult._ce) + - leftChildResult._cost + rightChildResult._cost; - - return {mergeJoinCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const UnionNode& node) { - const ABTVector& children = node.nodes(); - // UnionNode with one child is optimized away before lowering, therefore - // its cost is the cost of its child. - if (children.size() == 1) { - CostAndCEInternal childResult = deriveChild(children[0], 0); - return {childResult._cost, _cardinalityEstimate}; - } - - double totalCost = kStartupCost; - // The cost is the sum of the costs of its children and the cost to union each child. - for (size_t childIdx = 0; childIdx < children.size(); childIdx++) { - CostAndCEInternal childResult = deriveChild(children[childIdx], childIdx); - const double childCost = - childResult._cost + (childIdx > 0 ? kUnionIncrementalCost * childResult._ce : 0); - totalCost += childCost; - } - return {totalCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const GroupByNode& node) { - CostAndCEInternal childResult = deriveChild(node.getChild(), 0); - double groupByCost = kStartupCost; - - // TODO: for now pretend global group by is free. - if (node.getType() == GroupNodeType::Global) { - groupByCost += childResult._cost; - } else { - // TODO: consider RepetitionEstimate since this is a stateful operation. - groupByCost += kGroupByIncrementalCost * childResult._ce + childResult._cost; - } - return {groupByCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const UnwindNode& node) { - CostAndCEInternal childResult = deriveChild(node.getChild(), 0); - // Unwind probably depends mostly on its output size. - const double unwindCost = kUnwindIncrementalCost * _cardinalityEstimate + childResult._cost; - return {unwindCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const UniqueNode& node) { - CostAndCEInternal childResult = deriveChild(node.getChild(), 0); - const double uniqueCost = - kStartupCost + kUniqueIncrementalCost * childResult._ce + childResult._cost; - return {uniqueCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const CollationNode& node) { - CostAndCEInternal childResult = deriveChild(node.getChild(), 0); - // TODO: consider RepetitionEstimate since this is a stateful operation. - - double logFactor = childResult._ce; - double incrConst = kCollationIncrementalCost; - if (hasProperty<LimitSkipRequirement>(_physProps)) { - if (auto limit = getPropertyConst<LimitSkipRequirement>(_physProps).getAbsoluteLimit(); - limit < logFactor) { - logFactor = limit; - incrConst = kCollationWithLimitIncrementalCost; - } - } - - // Notice that log2(x) < 0 for any x < 1, and log2(1) = 0. Generally it makes sense that - // there is no cost to sort 1 document, so the only cost left is the startup cost. - const double sortCost = kStartupCost + childResult._cost + - ((logFactor <= 1.0) - ? 0.0 - // TODO: The cost formula below is based on 1 field, mix of int and str. Instead we - // have to take into account the number and size of sorted fields. - : incrConst * childResult._ce * std::log2(logFactor)); - return {sortCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const LimitSkipNode& node) { - // Assumed to be free. - CostAndCEInternal childResult = deriveChild(node.getChild(), 0); - const double limitCost = kStartupCost + childResult._cost; - return {limitCost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const ExchangeNode& node) { - CostAndCEInternal childResult = deriveChild(node.getChild(), 0); - double localCost = kStartupCost + kExchangeIncrementalCost * _cardinalityEstimate; - - switch (node.getProperty().getDistributionAndProjections()._type) { - case DistributionType::Replicated: - localCost *= 2.0; - break; - - case DistributionType::HashPartitioning: - case DistributionType::RangePartitioning: - localCost *= 1.1; - break; - - default: - break; - } - - return {localCost + childResult._cost, _cardinalityEstimate}; - } - - CostAndCEInternal operator()(const ABT& /*n*/, const RootNode& node) { - return deriveChild(node.getChild(), 0); - } - - /** - * Other ABT types. - */ - template <typename T, typename... Ts> - CostAndCEInternal operator()(const ABT& /*n*/, const T& /*node*/, Ts&&...) { - static_assert(!canBePhysicalNode<T>(), "Physical node must implement its cost derivation."); - return {0.0, 0.0}; - } - - static CostAndCEInternal derive(const Metadata& metadata, - const Memo& memo, - const PhysProps& physProps, - const ABT::reference_type physNodeRef, - const ChildPropsType& childProps, - const NodeCEMap& nodeCEMap) { - CostAndCEInternal result = - deriveInternal(metadata, memo, physProps, physNodeRef, childProps, nodeCEMap); - - switch (getPropertyConst<DistributionRequirement>(physProps) - .getDistributionAndProjections() - ._type) { - case DistributionType::Centralized: - case DistributionType::Replicated: - break; - - case DistributionType::RoundRobin: - case DistributionType::HashPartitioning: - case DistributionType::RangePartitioning: - case DistributionType::UnknownPartitioning: - result._cost /= metadata._numberOfPartitions; - break; - - default: - MONGO_UNREACHABLE; - } - - return result; - } - -private: - CostDerivation(const Metadata& metadata, - const Memo& memo, - const CEType ce, - const PhysProps& physProps, - const ChildPropsType& childProps, - const NodeCEMap& nodeCEMap) - : _metadata(metadata), - _memo(memo), - _physProps(physProps), - _cardinalityEstimate(getAdjustedCE(ce, _physProps)), - _childProps(childProps), - _nodeCEMap(nodeCEMap) {} - - template <class T> - static bool isTrivialExpr(const ABT& n) { - if (n.is<Constant>() || n.is<Variable>()) { - return true; - } - if (const auto* ptr = n.cast<T>(); ptr != nullptr && - ptr->getPath().template is<PathIdentity>() && isTrivialExpr<T>(ptr->getInput())) { - return true; - } - return false; - } - - static CostAndCEInternal deriveInternal(const Metadata& metadata, - const Memo& memo, - const PhysProps& physProps, - const ABT::reference_type physNodeRef, - const ChildPropsType& childProps, - const NodeCEMap& nodeCEMap) { - auto it = nodeCEMap.find(physNodeRef.cast<Node>()); - bool found = (it != nodeCEMap.cend()); - uassert(8423330, - "Only MemoLogicalDelegatorNode can be missing from nodeCEMap.", - found || physNodeRef.is<MemoLogicalDelegatorNode>()); - const CEType ce = (found ? it->second : 0.0); - - CostDerivation instance(metadata, memo, ce, physProps, childProps, nodeCEMap); - CostAndCEInternal costCEestimates = physNodeRef.visit(instance); - return costCEestimates; - } - - CostAndCEInternal deriveChild(const ABT& child, const size_t childIndex) { - PhysProps physProps = _childProps.empty() ? _physProps : _childProps.at(childIndex).second; - return deriveInternal(_metadata, _memo, physProps, child.ref(), {}, _nodeCEMap); - } - - static CEType getAdjustedCE(CEType baseCE, const PhysProps& physProps) { - CEType result = baseCE; - - // First: correct for un-enforced limit. - if (hasProperty<LimitSkipRequirement>(physProps)) { - const auto limit = getPropertyConst<LimitSkipRequirement>(physProps).getAbsoluteLimit(); - if (result > limit) { - result = limit; - } - } - - // Second: correct for enforced limit. - if (hasProperty<LimitEstimate>(physProps)) { - const auto limit = getPropertyConst<LimitEstimate>(physProps).getEstimate(); - if (result > limit) { - result = limit; - } - } - - // Third: correct for repetition. - if (hasProperty<RepetitionEstimate>(physProps)) { - result *= getPropertyConst<RepetitionEstimate>(physProps).getEstimate(); - } - - return result; - } - - // We don't own this. - const Metadata& _metadata; - const Memo& _memo; - const PhysProps& _physProps; - const CEType _cardinalityEstimate; - const ChildPropsType& _childProps; - const NodeCEMap& _nodeCEMap; -}; - -CostAndCE DefaultCosting::deriveCost(const Metadata& metadata, - const Memo& memo, - const PhysProps& physProps, - const ABT::reference_type physNodeRef, - const ChildPropsType& childProps, - const NodeCEMap& nodeCEMap) const { - const CostAndCEInternal result = - CostDerivation::derive(metadata, memo, physProps, physNodeRef, childProps, nodeCEMap); - return {CostType::fromDouble(result._cost), result._ce}; -} - -} // namespace mongo::optimizer::cascades diff --git a/src/mongo/db/query/optimizer/cascades/cost_derivation.h b/src/mongo/db/query/optimizer/cascades/cost_derivation.h deleted file mode 100644 index b71a020316e..00000000000 --- a/src/mongo/db/query/optimizer/cascades/cost_derivation.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (C) 2022-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include "mongo/db/query/optimizer/cascades/interfaces.h" -#include "mongo/db/query/optimizer/cascades/memo.h" - -namespace mongo::optimizer::cascades { - -/** - * Default costing for physical nodes with logical delegator (not-yet-optimized) inputs. - */ -class DefaultCosting : public CostingInterface { -public: - CostAndCE deriveCost(const Metadata& metadata, - const Memo& memo, - const properties::PhysProps& physProps, - ABT::reference_type physNodeRef, - const ChildPropsType& childProps, - const NodeCEMap& nodeCEMap) const override final; -}; - -} // namespace mongo::optimizer::cascades 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/cascades/memo_defs.h b/src/mongo/db/query/optimizer/cascades/memo_defs.h index b667df2816b..f0e476ab819 100644 --- a/src/mongo/db/query/optimizer/cascades/memo_defs.h +++ b/src/mongo/db/query/optimizer/cascades/memo_defs.h @@ -59,6 +59,10 @@ public: OrderPreservingABTSet(const OrderPreservingABTSet&) = delete; OrderPreservingABTSet(OrderPreservingABTSet&&) = default; + OrderPreservingABTSet& operator=(const OrderPreservingABTSet&) = delete; + OrderPreservingABTSet& operator=(OrderPreservingABTSet&&) = default; + + ABT::reference_type at(size_t index) const; std::pair<size_t, bool> emplace_back(ABT node); std::pair<size_t, bool> find(ABT::reference_type node) const; diff --git a/src/mongo/db/query/optimizer/explain.cpp b/src/mongo/db/query/optimizer/explain.cpp index ba5aecdbd73..5e9df5df3bb 100644 --- a/src/mongo/db/query/optimizer/explain.cpp +++ b/src/mongo/db/query/optimizer/explain.cpp @@ -496,7 +496,7 @@ private: void addValue(sbe::value::TypeTags tag, sbe::value::Value val, const bool append = false) { if (!_initialized) { _initialized = true; - _canAppend = !_nextFieldName.empty(); + _canAppend = _nextFieldName.has_value(); if (_canAppend) { std::tie(_tag, _val) = sbe::value::makeNewObject(); } else { @@ -512,7 +512,7 @@ private: } if (append) { - uassert(6624073, "Field name is not empty", _nextFieldName.empty()); + uassert(6624073, "Field name is not set", !_nextFieldName.has_value()); uassert(6624349, "Other printer does not contain Object", tag == sbe::value::TypeTags::Object); @@ -523,19 +523,19 @@ private: addField(obj->field(i), fieldTag, fieldVal); } } else { - addField(_nextFieldName, tag, val); - _nextFieldName.clear(); + tassert(6751700, "Missing field name to serialize", _nextFieldName); + addField(*_nextFieldName, tag, val); + _nextFieldName = boost::none; } } void addField(const std::string& fieldName, sbe::value::TypeTags tag, sbe::value::Value val) { - uassert(6624074, "Field name is empty", !fieldName.empty()); uassert(6624075, "Duplicate field name", _fieldNameSet.insert(fieldName).second); sbe::value::getObjectView(_val)->push_back(fieldName, tag, val); } void reset() { - _nextFieldName.clear(); + _nextFieldName = boost::none; _initialized = false; _canAppend = false; _tag = sbe::value::TypeTags::Nothing; @@ -543,7 +543,8 @@ private: _fieldNameSet.clear(); } - std::string _nextFieldName; + // Cannot assume empty means non-existent, so use optional<>. + boost::optional<std::string> _nextFieldName; bool _initialized; bool _canAppend; sbe::value::TypeTags _tag; @@ -1276,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); @@ -1766,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()) { @@ -2263,7 +2263,7 @@ public: ExplainPrinter printer("PathGet"); printer.separator(" [") .fieldName("path", ExplainVersion::V3) - .print(path.name()) + .print(path.name().empty() ? "<empty>" : path.name()) .separator("]") .setChildCount(1) .fieldName("input", ExplainVersion::V3) @@ -2542,8 +2542,12 @@ static void printBSONstr(PrinterType& printer, } } -std::string ExplainGenerator::printBSON(const sbe::value::TypeTags tag, - const sbe::value::Value val) { +std::string ExplainGenerator::explainBSONStr(const ABT& node, + bool displayProperties, + const cascades::MemoExplainInterface* memoInterface, + const NodeToGroupPropsMap& nodeMap) { + const auto [tag, val] = explainBSON(node, displayProperties, memoInterface, nodeMap); + sbe::value::ValueGuard vg(tag, val); ExplainPrinterImpl<ExplainVersion::V2> printer; printBSONstr(printer, tag, val); return printer.str(); diff --git a/src/mongo/db/query/optimizer/explain.h b/src/mongo/db/query/optimizer/explain.h index ad62dd54126..19e9221f8dd 100644 --- a/src/mongo/db/query/optimizer/explain.h +++ b/src/mongo/db/query/optimizer/explain.h @@ -94,7 +94,10 @@ public: const cascades::MemoExplainInterface* memoInterface = nullptr, const NodeToGroupPropsMap& nodeMap = {}); - static std::string printBSON(sbe::value::TypeTags tag, sbe::value::Value val); + static std::string explainBSONStr(const ABT& node, + bool displayProperties = false, + const cascades::MemoExplainInterface* memoInterface = nullptr, + const NodeToGroupPropsMap& nodeMap = {}); static std::string explainLogicalProps(const std::string& description, const properties::LogicalProps& props); diff --git a/src/mongo/db/query/optimizer/interval_intersection_test.cpp b/src/mongo/db/query/optimizer/interval_intersection_test.cpp index 119a1b98fea..cc8127d49b7 100644 --- a/src/mongo/db/query/optimizer/interval_intersection_test.cpp +++ b/src/mongo/db/query/optimizer/interval_intersection_test.cpp @@ -56,12 +56,12 @@ std::string optimizedQueryPlan(const std::string& query, ABT translated = translatePipeline(metadata, "[{$match: " + query + "}]", scanDefName, prefixId); - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase, - OptPhase::MemoExplorationPhase, - OptPhase::MemoImplementationPhase}, - prefixId, - metadata, - DebugInfo::kDefaultForTests); + auto phaseManager = makePhaseManager({OptPhase::MemoSubstitutionPhase, + OptPhase::MemoExplorationPhase, + OptPhase::MemoImplementationPhase}, + prefixId, + metadata, + DebugInfo::kDefaultForTests); ABT optimized = translated; phaseManager.getHints()._disableScan = true; 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 2434e811de1..03980eb5db4 100644 --- a/src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp +++ b/src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp @@ -74,10 +74,10 @@ TEST(LogicalRewriter, RootNodeMerge) { " Source []\n", rootNode); - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase}, - prefixId, - {{{"test", createScanDef({}, {})}}}, - DebugInfo::kDefaultForTests); + auto phaseManager = makePhaseManager({OptPhase::MemoSubstitutionPhase}, + prefixId, + {{{"test", createScanDef({}, {})}}}, + DebugInfo::kDefaultForTests); ABT rewritten = std::move(rootNode); phaseManager.optimize(rewritten); @@ -288,10 +288,10 @@ TEST(LogicalRewriter, FilterProjectRewrite) { " Source []\n", rootNode); - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase}, - prefixId, - {{{"test", createScanDef({}, {})}}}, - DebugInfo::kDefaultForTests); + auto phaseManager = makePhaseManager({OptPhase::MemoSubstitutionPhase}, + prefixId, + {{{"test", createScanDef({}, {})}}}, + DebugInfo::kDefaultForTests); ABT latest = std::move(rootNode); phaseManager.optimize(latest); @@ -399,10 +399,10 @@ TEST(LogicalRewriter, FilterProjectComplexRewrite) { " Source []\n", rootNode); - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase}, - prefixId, - {{{"test", createScanDef({}, {})}}}, - DebugInfo::kDefaultForTests); + auto phaseManager = makePhaseManager({OptPhase::MemoSubstitutionPhase}, + prefixId, + {{{"test", createScanDef({}, {})}}}, + DebugInfo::kDefaultForTests); ABT latest = std::move(rootNode); phaseManager.optimize(latest); @@ -477,10 +477,10 @@ TEST(LogicalRewriter, FilterProjectGroupRewrite) { ABT rootNode = make<RootNode>(properties::ProjectionRequirement{ProjectionNameVector{"c"}}, std::move(filterANode)); - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase}, - prefixId, - {{{"test", createScanDef({}, {})}}}, - DebugInfo::kDefaultForTests); + auto phaseManager = makePhaseManager({OptPhase::MemoSubstitutionPhase}, + prefixId, + {{{"test", createScanDef({}, {})}}}, + DebugInfo::kDefaultForTests); ABT latest = std::move(rootNode); phaseManager.optimize(latest); @@ -547,10 +547,10 @@ TEST(LogicalRewriter, FilterProjectUnwindRewrite) { ABT rootNode = make<RootNode>(properties::ProjectionRequirement{ProjectionNameVector{"a", "b"}}, std::move(filterBNode)); - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase}, - prefixId, - {{{"test", createScanDef({}, {})}}}, - DebugInfo::kDefaultForTests); + auto phaseManager = makePhaseManager({OptPhase::MemoSubstitutionPhase}, + prefixId, + {{{"test", createScanDef({}, {})}}}, + DebugInfo::kDefaultForTests); ABT latest = std::move(rootNode); phaseManager.optimize(latest); @@ -618,10 +618,10 @@ TEST(LogicalRewriter, FilterProjectExchangeRewrite) { ABT rootNode = make<RootNode>(properties::ProjectionRequirement{ProjectionNameVector{"a", "b"}}, std::move(filterANode)); - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase}, - prefixId, - {{{"test", createScanDef({}, {})}}}, - DebugInfo::kDefaultForTests); + auto phaseManager = makePhaseManager({OptPhase::MemoSubstitutionPhase}, + prefixId, + {{{"test", createScanDef({}, {})}}}, + DebugInfo::kDefaultForTests); ABT latest = std::move(rootNode); phaseManager.optimize(latest); @@ -690,10 +690,10 @@ TEST(LogicalRewriter, UnwindCollationRewrite) { ABT rootNode = make<RootNode>(properties::ProjectionRequirement{ProjectionNameVector{"a", "b"}}, std::move(unwindNode)); - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase}, - prefixId, - {{{"test", createScanDef({}, {})}}}, - DebugInfo::kDefaultForTests); + auto phaseManager = makePhaseManager({OptPhase::MemoSubstitutionPhase}, + prefixId, + {{{"test", createScanDef({}, {})}}}, + DebugInfo::kDefaultForTests); ABT latest = std::move(rootNode); phaseManager.optimize(latest); @@ -802,11 +802,11 @@ TEST(LogicalRewriter, FilterUnionReorderSingleProjection) { " Source []\n", latest); - OptPhaseManager phaseManager( - {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase}, - prefixId, - {{{"test1", createScanDef({}, {})}, {"test2", createScanDef({}, {})}}}, - DebugInfo::kDefaultForTests); + auto phaseManager = + makePhaseManager({OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase}, + prefixId, + {{{"test1", createScanDef({}, {})}, {"test2", createScanDef({}, {})}}}, + DebugInfo::kDefaultForTests); phaseManager.optimize(latest); ASSERT_EXPLAIN_V2( @@ -966,11 +966,11 @@ TEST(LogicalRewriter, MultipleFilterUnionReorder) { " Source []\n", latest); - OptPhaseManager phaseManager( - {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase}, - prefixId, - {{{"test1", createScanDef({}, {})}, {"test2", createScanDef({}, {})}}}, - DebugInfo::kDefaultForTests); + auto phaseManager = + makePhaseManager({OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase}, + prefixId, + {{{"test1", createScanDef({}, {})}, {"test2", createScanDef({}, {})}}}, + DebugInfo::kDefaultForTests); phaseManager.optimize(latest); ASSERT_EXPLAIN_V2( @@ -1070,12 +1070,12 @@ TEST(LogicalRewriter, FilterUnionUnionPushdown) { ABT rootNode = make<RootNode>(properties::ProjectionRequirement{ProjectionNameVector{"ptest"}}, std::move(filter)); - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase}, - prefixId, - {{{"test1", createScanDef({}, {})}, - {"test2", createScanDef({}, {})}, - {"test3", createScanDef({}, {})}}}, - DebugInfo::kDefaultForTests); + auto phaseManager = makePhaseManager({OptPhase::MemoSubstitutionPhase}, + prefixId, + {{{"test1", createScanDef({}, {})}, + {"test2", createScanDef({}, {})}, + {"test3", createScanDef({}, {})}}}, + DebugInfo::kDefaultForTests); ABT latest = std::move(rootNode); ASSERT_EXPLAIN_V2( @@ -1216,10 +1216,11 @@ TEST(LogicalRewriter, UnionPreservesCommonLogicalProps) { // Run the reordering rewrite such that the scan produces a hash partition. PrefixId prefixId; - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase}, - prefixId, - metadata, - DebugInfo::kDefaultForTests); + auto phaseManager = + makePhaseManager({OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase}, + prefixId, + metadata, + DebugInfo::kDefaultForTests); ABT optimized = rootNode; phaseManager.optimize(optimized); @@ -1432,10 +1433,11 @@ TEST(LogicalRewriter, SargableCE) { PrefixId prefixId; ABT rootNode = sargableCETestSetup(); - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase}, - prefixId, - {{{"test", createScanDef({}, {})}}}, - DebugInfo::kDefaultForTests); + auto phaseManager = + makePhaseManager({OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase}, + prefixId, + {{{"test", createScanDef({}, {})}}}, + DebugInfo::kDefaultForTests); ABT latest = std::move(rootNode); phaseManager.optimize(latest); @@ -1474,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" @@ -1508,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" @@ -1540,10 +1544,10 @@ TEST(LogicalRewriter, RemoveNoopFilter) { ABT rootNode = make<RootNode>(properties::ProjectionRequirement{ProjectionNameVector{"ptest"}}, std::move(filterANode)); - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase}, - prefixId, - {{{"test", createScanDef({}, {})}}}, - DebugInfo::kDefaultForTests); + auto phaseManager = makePhaseManager({OptPhase::MemoSubstitutionPhase}, + prefixId, + {{{"test", createScanDef({}, {})}}}, + DebugInfo::kDefaultForTests); ABT latest = std::move(rootNode); phaseManager.optimize(latest); 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/opt_phase_manager.cpp b/src/mongo/db/query/optimizer/opt_phase_manager.cpp index 4c4fd0deed3..a00e30aa499 100644 --- a/src/mongo/db/query/optimizer/opt_phase_manager.cpp +++ b/src/mongo/db/query/optimizer/opt_phase_manager.cpp @@ -30,7 +30,6 @@ #include "mongo/db/query/optimizer/opt_phase_manager.h" #include "mongo/db/query/optimizer/cascades/ce_heuristic.h" -#include "mongo/db/query/optimizer/cascades/cost_derivation.h" #include "mongo/db/query/optimizer/cascades/logical_props_derivation.h" #include "mongo/db/query/optimizer/rewrites/const_eval.h" #include "mongo/db/query/optimizer/rewrites/path.h" @@ -49,22 +48,6 @@ OptPhaseManager::PhaseSet OptPhaseManager::_allRewrites = {OptPhase::ConstEvalPr OptPhaseManager::OptPhaseManager(OptPhaseManager::PhaseSet phaseSet, PrefixId& prefixId, - Metadata metadata, - DebugInfo debugInfo, - QueryHints queryHints) - : OptPhaseManager(std::move(phaseSet), - prefixId, - false /*requireRID*/, - std::move(metadata), - std::make_unique<HeuristicCE>(), - std::make_unique<DefaultCosting>(), - {} /*pathToInterval*/, - ConstEval::constFold, - std::move(debugInfo), - std::move(queryHints)) {} - -OptPhaseManager::OptPhaseManager(OptPhaseManager::PhaseSet phaseSet, - PrefixId& prefixId, const bool requireRID, Metadata metadata, std::unique_ptr<CEInterface> ceDerivation, diff --git a/src/mongo/db/query/optimizer/opt_phase_manager.h b/src/mongo/db/query/optimizer/opt_phase_manager.h index 0a94ffb9761..81a849a3461 100644 --- a/src/mongo/db/query/optimizer/opt_phase_manager.h +++ b/src/mongo/db/query/optimizer/opt_phase_manager.h @@ -77,11 +77,6 @@ public: OptPhaseManager(PhaseSet phaseSet, PrefixId& prefixId, - Metadata metadata, - DebugInfo debugInfo, - QueryHints queryHints = {}); - OptPhaseManager(PhaseSet phaseSet, - PrefixId& prefixId, bool requireRID, Metadata metadata, std::unique_ptr<CEInterface> ceDerivation, diff --git a/src/mongo/db/query/optimizer/optimizer_failure_test.cpp b/src/mongo/db/query/optimizer/optimizer_failure_test.cpp index bf4715548aa..6ab998dcf13 100644 --- a/src/mongo/db/query/optimizer/optimizer_failure_test.cpp +++ b/src/mongo/db/query/optimizer/optimizer_failure_test.cpp @@ -28,7 +28,6 @@ */ #include "mongo/db/query/optimizer/cascades/ce_heuristic.h" -#include "mongo/db/query/optimizer/cascades/cost_derivation.h" #include "mongo/db/query/optimizer/metadata_factory.h" #include "mongo/db/query/optimizer/node.h" #include "mongo/db/query/optimizer/opt_phase_manager.h" @@ -53,11 +52,11 @@ DEATH_TEST_REGEX(Optimizer, HitIterationLimitInrunStructuralPhases, "Tripwire as ABT evalNode = make<EvaluationNode>("evalProj1", Constant::int64(5), std::move(scanNode)); - OptPhaseManager phaseManager( - {OptPhase::PathFuse, OptPhase::ConstEvalPre}, - prefixId, - {{{"test1", createScanDef({}, {})}, {"test2", createScanDef({}, {})}}}, - DebugInfo(true, DebugInfo::kDefaultDebugLevelForTests, 0)); + auto phaseManager = + makePhaseManager({OptPhase::PathFuse, OptPhase::ConstEvalPre}, + prefixId, + {{{"test1", createScanDef({}, {})}, {"test2", createScanDef({}, {})}}}, + DebugInfo(true, DebugInfo::kDefaultDebugLevelForTests, 0)); ASSERT_THROWS_CODE(phaseManager.optimize(evalNode), DBException, 6808700); } @@ -81,10 +80,10 @@ DEATH_TEST_REGEX(Optimizer, ABT rootNode = make<RootNode>(properties::ProjectionRequirement{{}}, std::move(filterNode)); - OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase}, - prefixId, - {{{"test", createScanDef({}, {})}}}, - DebugInfo(true, DebugInfo::kDefaultDebugLevelForTests, 0)); + auto phaseManager = makePhaseManager({OptPhase::MemoSubstitutionPhase}, + prefixId, + {{{"test", createScanDef({}, {})}}}, + DebugInfo(true, DebugInfo::kDefaultDebugLevelForTests, 0)); ASSERT_THROWS_CODE(phaseManager.optimize(rootNode), DBException, 6808702); } @@ -108,10 +107,10 @@ DEATH_TEST_REGEX(Optimizer, ABT rootNode = make<RootNode>(properties::ProjectionRequirement{{}}, std::move(filterNode)); - OptPhaseManager phaseManager({OptPhase::MemoExplorationPhase}, - prefixId, - {{{"test", createScanDef({}, {})}}}, - DebugInfo(true, DebugInfo::kDefaultDebugLevelForTests, 0)); + auto phaseManager = makePhaseManager({OptPhase::MemoExplorationPhase}, + prefixId, + {{{"test", createScanDef({}, {})}}}, + DebugInfo(true, DebugInfo::kDefaultDebugLevelForTests, 0)); ASSERT_THROWS_CODE(phaseManager.optimize(rootNode), DBException, 6808702); } @@ -133,10 +132,10 @@ DEATH_TEST_REGEX(Optimizer, BadGroupID, "Tripwire assertion.*6808704") { ABT rootNode = make<RootNode>(properties::ProjectionRequirement{{}}, std::move(filterNode)); - OptPhaseManager phaseManager({OptPhase::MemoImplementationPhase}, - prefixId, - {{{"test", createScanDef({}, {})}}}, - DebugInfo(true, DebugInfo::kDefaultDebugLevelForTests, 0)); + auto phaseManager = makePhaseManager({OptPhase::MemoImplementationPhase}, + prefixId, + {{{"test", createScanDef({}, {})}}}, + DebugInfo(true, DebugInfo::kDefaultDebugLevelForTests, 0)); ASSERT_THROWS_CODE(phaseManager.optimize(rootNode), DBException, 6808704); } @@ -161,13 +160,13 @@ DEATH_TEST_REGEX(Optimizer, EnvHasFreeVariables, "Tripwire assertion.*6808711") ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"p3"}}, std::move(filter2Node)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, {{{"test", createScanDef({}, {})}}}, - {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); + DebugInfo(true, DebugInfo::kDefaultDebugLevelForTests, DebugInfo::kIterationLimitForTests)); ASSERT_THROWS_CODE(phaseManager.optimize(rootNode), DBException, 6808711); } @@ -206,12 +205,11 @@ DEATH_TEST_REGEX(Optimizer, FailedToRetrieveRID, "Tripwire assertion.*6808705") ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pc"}}, std::move(groupByNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManagerRequireRID( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - true /*requireRID*/, {{{"c1", createScanDef( {}, @@ -224,11 +222,7 @@ DEATH_TEST_REGEX(Optimizer, FailedToRetrieveRID, "Tripwire assertion.*6808705") ConstEval::constFold, {DistributionType::HashPartitioning, makeSeq(makeNonMultikeyIndexPath("b"))})}}, 5 /*numberOfPartitions*/}, - std::make_unique<HeuristicCE>(), - std::make_unique<DefaultCosting>(), - {} /*pathToInterval*/, - ConstEval::constFold, - {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); + DebugInfo(true, DebugInfo::kDefaultDebugLevelForTests, DebugInfo::kIterationLimitForTests)); ASSERT_THROWS_CODE(phaseManager.optimize(rootNode), DBException, 6808705); } diff --git a/src/mongo/db/query/optimizer/optimizer_test.cpp b/src/mongo/db/query/optimizer/optimizer_test.cpp index d52b84470a1..423a6a399af 100644 --- a/src/mongo/db/query/optimizer/optimizer_test.cpp +++ b/src/mongo/db/query/optimizer/optimizer_test.cpp @@ -29,18 +29,45 @@ #include "mongo/db/query/optimizer/explain.h" #include "mongo/db/query/optimizer/node.h" -#include "mongo/db/query/optimizer/opt_phase_manager.h" #include "mongo/db/query/optimizer/reference_tracker.h" #include "mongo/db/query/optimizer/rewrites/const_eval.h" #include "mongo/db/query/optimizer/syntax/syntax.h" -#include "mongo/db/query/optimizer/syntax/syntax_fwd_declare.h" #include "mongo/db/query/optimizer/utils/unit_test_utils.h" #include "mongo/db/query/optimizer/utils/utils.h" #include "mongo/unittest/unittest.h" + namespace mongo::optimizer { namespace { +TEST(Optimizer, AutoUpdateExplain) { + ABT tree = make<BinaryOp>(Operations::Add, + Constant::int64(1), + make<Variable>("very very very very very very very very very very " + "very very long variable name with \"quotes\"")); + + /** + * To exercise the auto-updating behavior: + * 1. Change the flag "kAutoUpdateOnFailure" to "true". + * 2. Induce a failure: change something in the expected output. + * 3. Recompile and run the test binary as normal. + * 4. Observe after the run the test file is updated with the correct output. + */ + ASSERT_EXPLAIN_V2_AUTO( // NOLINT (test auto-update) + "BinaryOp [Add]\n" + "| Variable [very very very very very very very very very very very very long variable " + "name with \"quotes\"]\n" + "Const [1]\n", + tree); + + // Test for short constant. It should not be inlined. The nolint comment on the string constant + // itself is auto-generated. + ABT tree1 = make<Variable>("short name"); + ASSERT_EXPLAIN_V2_AUTO( // NOLINT (test auto-update) + "Variable [short name]\n", // NOLINT (test auto-update) + tree1); +} + Constant* constEval(ABT& tree) { auto env = VariableEnvironment::build(tree); ConstEval evaluator{env}; @@ -746,13 +773,14 @@ TEST(Explain, ExplainV2Compact) { TEST(Explain, ExplainBsonForConstant) { ABT cNode = Constant::int64(3); - auto [tag, val] = ExplainGenerator::explainBSON(cNode); - sbe::value::ValueGuard vg(tag, val); - ASSERT_EQ( - "{\n nodeType: \"Const\", \n" + + ASSERT_EXPLAIN_BSON( + "{\n" + " nodeType: \"Const\", \n" " tag: \"NumberInt64\", \n" - " value: 3\n}\n", - ExplainGenerator::printBSON(tag, val)); + " value: 3\n" + "}\n", + cNode); } } // namespace 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 1079e45acfa..789bcdeea81 100644 --- a/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp +++ b/src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp @@ -30,7 +30,6 @@ #include "mongo/db/pipeline/abt/utils.h" #include "mongo/db/query/optimizer/cascades/ce_heuristic.h" #include "mongo/db/query/optimizer/cascades/ce_hinted.h" -#include "mongo/db/query/optimizer/cascades/cost_derivation.h" #include "mongo/db/query/optimizer/cascades/rewriter_rules.h" #include "mongo/db/query/optimizer/explain.h" #include "mongo/db/query/optimizer/metadata_factory.h" @@ -66,7 +65,7 @@ TEST(PhysRewriter, PhysicalRewriterBasic) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"p2"}}, std::move(filter2Node)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -121,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" @@ -147,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" @@ -271,7 +270,7 @@ TEST(PhysRewriter, GroupBy) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"c"}}, std::move(filterANode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -341,7 +340,7 @@ TEST(PhysRewriter, GroupBy1) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pb"}}, std::move(groupByNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -413,7 +412,7 @@ TEST(PhysRewriter, Unwind) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"a", "b"}}, std::move(filterBNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -491,7 +490,7 @@ TEST(PhysRewriter, DuplicateFilter) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode2)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -552,7 +551,7 @@ TEST(PhysRewriter, FilterCollation) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pb"}}, std::move(limitSkipNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -608,7 +607,7 @@ TEST(PhysRewriter, EvalCollation) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa"}}, std::move(collationNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -663,7 +662,7 @@ TEST(PhysRewriter, FilterEvalCollation) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(collationNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -719,7 +718,7 @@ TEST(PhysRewriter, FilterIndexing) { { PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase}, prefixId, {{{"c1", @@ -739,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" @@ -762,7 +761,7 @@ TEST(PhysRewriter, FilterIndexing) { { PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -804,7 +803,7 @@ TEST(PhysRewriter, FilterIndexing) { { PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -868,7 +867,7 @@ TEST(PhysRewriter, FilterIndexing1) { make<RootNode>(ProjectionRequirement{ProjectionNameVector{"p1"}}, std::move(filterNode)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -933,7 +932,7 @@ TEST(PhysRewriter, FilterIndexing2) { make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -1013,7 +1012,7 @@ TEST(PhysRewriter, FilterIndexing2NonSarg) { make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode2)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -1131,7 +1130,7 @@ TEST(PhysRewriter, FilterIndexing3) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -1186,7 +1185,7 @@ TEST(PhysRewriter, FilterIndexing3MultiKey) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -1280,7 +1279,7 @@ TEST(PhysRewriter, FilterIndexing4) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa"}}, std::move(filterDNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -1388,7 +1387,7 @@ TEST(PhysRewriter, FilterIndexing5) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa", "pb"}}, std::move(collationNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -1478,7 +1477,7 @@ TEST(PhysRewriter, FilterIndexing6) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa", "pb"}}, std::move(collationNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -1541,7 +1540,7 @@ TEST(PhysRewriter, FilterIndexingStress) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(result)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -1641,7 +1640,7 @@ TEST(PhysRewriter, FilterIndexingVariable) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -1737,7 +1736,7 @@ TEST(PhysRewriter, FilterIndexingMaxKey) { make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode2)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -1805,7 +1804,7 @@ TEST(PhysRewriter, SargableProjectionRenames) { make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(evalNode2)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase}, prefixId, {{{"c1", createScanDef({}, {})}}}, @@ -1867,7 +1866,7 @@ TEST(PhysRewriter, SargableAcquireProjection) { make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(evalNode)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase}, prefixId, {{{"c1", createScanDef({}, {})}}}, @@ -1933,17 +1932,13 @@ TEST(PhysRewriter, FilterReorder) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(result)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef({}, {})}}}, std::make_unique<HintedCE>(std::move(hints)), - std::make_unique<DefaultCosting>(), - {} /*pathToInterval*/, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = std::move(rootNode); @@ -2028,21 +2023,17 @@ TEST(PhysRewriter, CoveredScan) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef( {}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending, false /*isMultiKey*/)}})}}}, std::make_unique<HintedCE>(std::move(hints)), - std::make_unique<DefaultCosting>(), - {} /*pathToInterval*/, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = std::move(rootNode); @@ -2100,7 +2091,7 @@ TEST(PhysRewriter, EvalIndexing) { make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa"}}, std::move(collationNode)); { - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -2133,7 +2124,7 @@ TEST(PhysRewriter, EvalIndexing) { { // Index and collation node have incompatible ops. - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -2192,7 +2183,7 @@ TEST(PhysRewriter, EvalIndexing1) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(collationNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -2262,7 +2253,7 @@ TEST(PhysRewriter, EvalIndexing2) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa2"}}, std::move(collationNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::ConstEvalPre, OptPhase::PathFuse, OptPhase::MemoSubstitutionPhase, @@ -2347,12 +2338,11 @@ TEST(PhysRewriter, MultiKeyIndex) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(collationNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef( {}, @@ -2360,9 +2350,6 @@ TEST(PhysRewriter, MultiKeyIndex) { {"index2", makeIndexDefinition("b", CollationOp::Descending, false /*isMultiKey*/)}})}}}, std::make_unique<HintedCE>(std::move(hints)), - std::make_unique<DefaultCosting>(), - {} /*pathToInterval*/, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); { @@ -2571,7 +2558,7 @@ TEST(PhysRewriter, CompoundIndex1) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterDNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -2656,7 +2643,7 @@ TEST(PhysRewriter, CompoundIndex2) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(collationNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -2743,7 +2730,7 @@ TEST(PhysRewriter, CompoundIndex3) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(collationNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -2830,12 +2817,11 @@ TEST(PhysRewriter, CompoundIndex4Negative) { make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterBNode)); // Create the following indexes: {a:1, c:1, {name: 'index1'}}, and {b:1, d:1, {name: 'index2'}} - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef( {}, @@ -2848,9 +2834,6 @@ TEST(PhysRewriter, CompoundIndex4Negative) { {makeNonMultikeyIndexPath("d"), CollationOp::Ascending}}, false /*isMultiKey*/}}})}}}, std::make_unique<HintedCE>(std::move(hints)), - std::make_unique<DefaultCosting>(), - {} /*pathToInterval*/, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = rootNode; @@ -2905,7 +2888,7 @@ TEST(PhysRewriter, IndexBoundsIntersect) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode2)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -2980,7 +2963,7 @@ TEST(PhysRewriter, IndexBoundsIntersect1) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(collationNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -3048,7 +3031,7 @@ TEST(PhysRewriter, IndexBoundsIntersect2) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -3122,7 +3105,7 @@ TEST(PhysRewriter, IndexBoundsIntersect3) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -3201,7 +3184,7 @@ TEST(PhysRewriter, IndexResidualReq) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa"}}, std::move(collationNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -3229,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" @@ -3257,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" @@ -3321,7 +3304,7 @@ TEST(PhysRewriter, IndexResidualReq1) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(collationNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -3404,7 +3387,7 @@ TEST(PhysRewriter, IndexResidualReq2) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterBNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -3483,18 +3466,13 @@ TEST(PhysRewriter, ElemMatchIndex) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef({}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending)}})}}}, - std::make_unique<HeuristicCE>(), - std::make_unique<DefaultCosting>(), - defaultConvertPathToInterval, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = rootNode; @@ -3567,22 +3545,17 @@ TEST(PhysRewriter, ElemMatchIndex1) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode2)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef({}, {{"index1", makeCompositeIndexDefinition( {{"b", CollationOp::Ascending, true /*isMultiKey*/}, {"a", CollationOp::Ascending, true /*isMultiKey*/}})}})}}}, - std::make_unique<HeuristicCE>(), - std::make_unique<DefaultCosting>(), - defaultConvertPathToInterval, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = rootNode; @@ -3650,21 +3623,16 @@ TEST(PhysRewriter, ElemMatchIndexNoArrays) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef( {}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending, false /*multiKey*/)}})}}}, - std::make_unique<HeuristicCE>(), - std::make_unique<DefaultCosting>(), - defaultConvertPathToInterval, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = rootNode; @@ -3720,22 +3688,17 @@ TEST(PhysRewriter, ObjectElemMatchResidual) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef({}, {{"index1", makeCompositeIndexDefinition( {{"b", CollationOp::Ascending, true /*isMultiKey*/}, {"a", CollationOp::Ascending, true /*isMultiKey*/}})}})}}}, - std::make_unique<HeuristicCE>(), - std::make_unique<DefaultCosting>(), - defaultConvertPathToInterval, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = rootNode; @@ -3840,12 +3803,11 @@ TEST(PhysRewriter, ObjectElemMatchBounds) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef( {}, @@ -3855,10 +3817,6 @@ TEST(PhysRewriter, ObjectElemMatchBounds) { true /*isMultiKey*/}}} )}}}, - std::make_unique<HeuristicCE>(), - std::make_unique<DefaultCosting>(), - defaultConvertPathToInterval, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = rootNode; @@ -3928,21 +3886,16 @@ TEST(PhysRewriter, NestedElemMatch) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"coll1", createScanDef( {}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending, true /*isMultiKey*/)}})}}}, - std::make_unique<HeuristicCE>(), - std::make_unique<DefaultCosting>(), - defaultConvertPathToInterval, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = rootNode; @@ -4049,12 +4002,11 @@ TEST(PhysRewriter, PathObj) { make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); PrefixId prefixId; - OptPhaseManager phaseManager{ + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef({}, {{"index1", @@ -4062,13 +4014,8 @@ TEST(PhysRewriter, PathObj) { {{"a", CollationOp::Ascending, false /*isMultiKey*/}, {"b", CollationOp::Ascending, true /*isMultiKey*/}})}})}}}, std::make_unique<HintedCE>(std::move(hints)), - std::make_unique<DefaultCosting>(), - // The path-to-interval callback is important for this test. - // We want to confirm PathObj becomes an interval. - defaultConvertPathToInterval, - ConstEval::constFold, DebugInfo{true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}, - {} /*hints*/}; + {} /*hints*/); ABT optimized = rootNode; phaseManager.optimize(optimized); @@ -4139,7 +4086,7 @@ TEST(PhysRewriter, ArrayConstantIndex) { ABT rootNode = make<RootNode>(properties::ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode2)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -4244,7 +4191,7 @@ TEST(PhysRewriter, ArrayConstantNoIndex) { ABT rootNode = make<RootNode>(properties::ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode2)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -4306,7 +4253,7 @@ TEST(PhysRewriter, ParallelScan) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -4368,7 +4315,7 @@ TEST(PhysRewriter, HashPartitioning) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pc"}}, std::move(groupByNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -4452,12 +4399,11 @@ TEST(PhysRewriter, IndexPartitioning) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pc"}}, std::move(groupByNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef( {}, @@ -4471,9 +4417,6 @@ TEST(PhysRewriter, IndexPartitioning) { {DistributionType::HashPartitioning, makeSeq(makeNonMultikeyIndexPath("b"))})}}, 5 /*numberOfPartitions*/}, std::make_unique<HintedCE>(std::move(hints)), - std::make_unique<DefaultCosting>(), - {} /*pathToInterval*/, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = rootNode; @@ -4573,12 +4516,11 @@ TEST(PhysRewriter, IndexPartitioning1) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pc"}}, std::move(groupByNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef( {}, @@ -4598,9 +4540,6 @@ TEST(PhysRewriter, IndexPartitioning1) { {DistributionType::HashPartitioning, makeSeq(makeNonMultikeyIndexPath("c"))})}}, 5 /*numberOfPartitions*/}, std::make_unique<HintedCE>(std::move(hints)), - std::make_unique<DefaultCosting>(), - {} /*pathToInterval*/, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = rootNode; @@ -4653,7 +4592,7 @@ TEST(PhysRewriter, LocalGlobalAgg) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa", "pc"}}, std::move(groupByNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -4731,7 +4670,7 @@ TEST(PhysRewriter, LocalGlobalAgg1) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pc"}}, std::move(groupByNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -4786,7 +4725,7 @@ TEST(PhysRewriter, LocalLimitSkip) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(limitSkipNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -4920,7 +4859,7 @@ TEST(PhysRewriter, CollationLimit) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(limitSkipNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -5061,7 +5000,7 @@ TEST(PhysRewriter, PartialIndex1) { ASSERT_TRUE(conversionResult); ASSERT_FALSE(conversionResult->_retainPredicate); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -5142,7 +5081,7 @@ TEST(PhysRewriter, PartialIndex2) { ASSERT_TRUE(conversionResult); ASSERT_FALSE(conversionResult->_retainPredicate); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -5222,7 +5161,7 @@ TEST(PhysRewriter, PartialIndexReject) { ASSERT_TRUE(conversionResult); ASSERT_FALSE(conversionResult->_retainPredicate); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -5287,17 +5226,12 @@ TEST(PhysRewriter, RequireRID) { ABT rootNode = make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManagerRequireRID( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - true /*requireRID*/, {{{"c1", createScanDef({}, {})}}}, - std::make_unique<HeuristicCE>(), - std::make_unique<DefaultCosting>(), - {} /*pathToInterval*/, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = rootNode; @@ -5342,17 +5276,12 @@ TEST(PhysRewriter, RequireRID1) { std::move(filterNode)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManagerRequireRID( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - true /*requireRID*/, {{{"c1", createScanDef({}, {})}}}, - std::make_unique<HeuristicCE>(), - std::make_unique<DefaultCosting>(), - {} /*pathToInterval*/, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = rootNode; @@ -5410,7 +5339,7 @@ TEST(PhysRewriter, UnionRewrite) { std::move(unionNode)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -5479,7 +5408,7 @@ TEST(PhysRewriter, JoinRewrite) { std::move(joinNode)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -5554,7 +5483,7 @@ TEST(PhysRewriter, JoinRewrite1) { std::move(joinNode)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -5609,7 +5538,7 @@ TEST(PhysRewriter, RootInterval) { make<RootNode>(ProjectionRequirement{ProjectionNameVector{"root"}}, std::move(filterNode)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -5663,7 +5592,7 @@ TEST(PhysRewriter, EqMemberSargable) { { PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase}, prefixId, {{{"c1", @@ -5711,7 +5640,7 @@ TEST(PhysRewriter, EqMemberSargable) { { PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -5811,7 +5740,7 @@ TEST(PhysRewriter, IndexSubfieldCovered) { std::move(filterNode3)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, @@ -5895,12 +5824,11 @@ TEST(PhysRewriter, PerfOnlyPreds1) { make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa"}}, std::move(filterNode2)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef( {}, @@ -5909,9 +5837,6 @@ TEST(PhysRewriter, PerfOnlyPreds1) { {"a", CollationOp::Ascending, false /*isMultiKey*/}}, false /*isMultiKey*/)}})}}}, std::make_unique<HintedCE>(std::move(hints)), - std::make_unique<DefaultCosting>(), - {} /*pathToInterval*/, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); ABT optimized = rootNode; @@ -5988,12 +5913,11 @@ TEST(PhysRewriter, PerfOnlyPreds2) { make<RootNode>(ProjectionRequirement{ProjectionNameVector{"pa"}}, std::move(filterNode2)); PrefixId prefixId; - OptPhaseManager phaseManager( + auto phaseManager = makePhaseManager( {OptPhase::MemoSubstitutionPhase, OptPhase::MemoExplorationPhase, OptPhase::MemoImplementationPhase}, prefixId, - false /*requireRID*/, {{{"c1", createScanDef( {}, @@ -6001,15 +5925,12 @@ TEST(PhysRewriter, PerfOnlyPreds2) { {"index2", makeIndexDefinition("b", CollationOp::Ascending, false /*isMultiKey*/)}})}}}, std::make_unique<HintedCE>(std::move(hints)), - std::make_unique<DefaultCosting>(), - {} /*pathToInterval*/, - ConstEval::constFold, {true /*debugMode*/, 2 /*debugLevel*/, DebugInfo::kIterationLimitForTests}); 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( @@ -6042,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/unit_test_pipeline_utils.cpp b/src/mongo/db/query/optimizer/utils/unit_test_pipeline_utils.cpp index 446d975c16d..8c807453232 100644 --- a/src/mongo/db/query/optimizer/utils/unit_test_pipeline_utils.cpp +++ b/src/mongo/db/query/optimizer/utils/unit_test_pipeline_utils.cpp @@ -32,9 +32,9 @@ #include "mongo/db/pipeline/abt/document_source_visitor.h" #include "mongo/db/pipeline/expression_context_for_test.h" #include "mongo/db/query/optimizer/cascades/ce_heuristic.h" -#include "mongo/db/query/optimizer/cascades/cost_derivation.h" #include "mongo/db/query/optimizer/explain.h" #include "mongo/db/query/optimizer/rewrites/const_eval.h" +#include "mongo/db/query/optimizer/utils/unit_test_utils.h" #include "mongo/unittest/temp_dir.h" @@ -212,15 +212,7 @@ ABT optimizeABT(ABT abt, bool phaseManagerDisableScan) { PrefixId prefixId; - OptPhaseManager phaseManager(phaseSet, - prefixId, - false, - metadata, - std::make_unique<HeuristicCE>(), - std::make_unique<DefaultCosting>(), - pathToInterval, - ConstEval::constFold, - DebugInfo::kDefaultForTests); + auto phaseManager = makePhaseManager(phaseSet, prefixId, metadata, DebugInfo::kDefaultForTests); if (phaseManagerDisableScan) { phaseManager.getHints()._disableScan = true; } diff --git a/src/mongo/db/query/optimizer/utils/unit_test_utils.cpp b/src/mongo/db/query/optimizer/utils/unit_test_utils.cpp index 5ad7c6615c5..538a4b1f036 100644 --- a/src/mongo/db/query/optimizer/utils/unit_test_utils.cpp +++ b/src/mongo/db/query/optimizer/utils/unit_test_utils.cpp @@ -29,30 +29,172 @@ #include "mongo/db/query/optimizer/utils/unit_test_utils.h" +#include <fstream> + +#include "mongo/db/pipeline/abt/utils.h" +#include "mongo/db/query/cost_model/cost_estimator.h" +#include "mongo/db/query/cost_model/cost_model_manager.h" +#include "mongo/db/query/optimizer/cascades/ce_heuristic.h" #include "mongo/db/query/optimizer/explain.h" #include "mongo/db/query/optimizer/metadata.h" #include "mongo/db/query/optimizer/node.h" -#include "mongo/unittest/unittest.h" +#include "mongo/db/query/optimizer/rewrites/const_eval.h" +#include "mongo/util/str_escape.h" namespace mongo::optimizer { static constexpr bool kDebugAsserts = false; +// DO NOT COMMIT WITH "TRUE". +static constexpr bool kAutoUpdateOnFailure = false; +static constexpr const char* kTempFileSuffix = ".tmp.txt"; + +// Map from file name to a list of updates. We keep track of how many lines are added or deleted at +// a particular line of a source file. +using LineDeltaVector = std::vector<std::pair<uint64_t, int64_t>>; +std::map<std::string, LineDeltaVector> gLineDeltaMap; + + void maybePrintABT(const ABT& abt) { // Always print using the supported versions to make sure we don't crash. const std::string strV1 = ExplainGenerator::explain(abt); const std::string strV2 = ExplainGenerator::explainV2(abt); const std::string strV2Compact = ExplainGenerator::explainV2Compact(abt); - auto [tag, val] = ExplainGenerator::explainBSON(abt); - sbe::value::ValueGuard vg(tag, val); + const std::string strBSON = ExplainGenerator::explainBSONStr(abt); if constexpr (kDebugAsserts) { std::cout << "V1: " << strV1 << "\n"; std::cout << "V2: " << strV2 << "\n"; std::cout << "V2Compact: " << strV2Compact << "\n"; - std::cout << "BSON: " << ExplainGenerator::printBSON(tag, val) << "\n"; + std::cout << "BSON: " << strBSON << "\n"; + } +} + +static std::vector<std::string> formatStr(const std::string& str) { + std::vector<std::string> replacementLines; + std::istringstream lineInput(str); + + // Account for maximum line length after linting. We need to indent, add quotes, etc. + static constexpr size_t kEscapedLength = 88; + + std::string line; + while (std::getline(lineInput, line)) { + // Read the string line by line and format it to match the test file's expected format. We + // have an initial indentation, followed by quotes and the escaped string itself. + + std::string escaped = mongo::str::escapeForJSON(line); + for (;;) { + // If the line is estimated to exceed the maximum length allowed by the linter, break it + // up and make sure to insert '\n' only at the end of the last segment. + const bool breakupLine = escaped.size() > kEscapedLength; + + std::ostringstream os; + os << " \"" << escaped.substr(0, kEscapedLength); + if (!breakupLine) { + os << "\\n"; + } + os << "\"\n"; + replacementLines.push_back(os.str()); + + if (breakupLine) { + escaped = escaped.substr(kEscapedLength); + } else { + break; + } + } + } + + if (!replacementLines.empty() && !replacementLines.back().empty()) { + // Account for the fact that we need an extra comma after the string constant in the macro. + auto& lastLine = replacementLines.back(); + lastLine.insert(lastLine.size() - 1, ","); + + if (replacementLines.size() == 1) { + // For single lines, add a 'nolint' comment to prevent the linter from inlining the + // single line with the macro itself. + lastLine.insert(lastLine.size() - 1, " // NOLINT (test auto-update)"); + } + } + + return replacementLines; +} + +bool handleAutoUpdate(const std::string& expected, + const std::string& actual, + const std::string& fileName, + const size_t lineNumber) { + if (expected == actual) { + return true; + } + if constexpr (!kAutoUpdateOnFailure) { + std::cout << "Auto-updating is disabled.\n"; + return false; + } + + const auto expectedFormatted = formatStr(expected); + const auto actualFormatted = formatStr(actual); + + std::cout << "Updating expected result in file '" << fileName << "', line: " << lineNumber + << ".\n"; + std::cout << "Replacement:\n"; + for (const auto& line : actualFormatted) { + std::cout << line; + } + + // Compute the total number of lines added or removed before the current macro line. + auto& lineDeltas = gLineDeltaMap.emplace(fileName, LineDeltaVector{}).first->second; + int64_t totalDelta = 0; + for (const auto& [line, delta] : lineDeltas) { + if (line < lineNumber) { + totalDelta += delta; + } } + + const size_t replacementEndLine = lineNumber + totalDelta; + // Treat an empty string as needing one line. Adjust for line delta. + const size_t replacementStartLine = + replacementEndLine - (expectedFormatted.empty() ? 1 : expectedFormatted.size()); + + const std::string tempFileName = fileName + kTempFileSuffix; + std::string line; + size_t lineIndex = 0; + + try { + std::ifstream in; + in.open(fileName); + std::ofstream out; + out.open(tempFileName); + + // Generate a new test file, updated with the replacement string. + while (std::getline(in, line)) { + lineIndex++; + + if (lineIndex < replacementStartLine || lineIndex >= replacementEndLine) { + out << line << "\n"; + } else if (lineIndex == replacementStartLine) { + for (const auto& line1 : actualFormatted) { + out << line1; + } + } + } + + out.close(); + in.close(); + + std::rename(tempFileName.c_str(), fileName.c_str()); + } catch (const std::exception& ex) { + // Print and re-throw exception. + std::cout << "Caught an exception while manipulating files: " << ex.what(); + throw ex; + } + + // Add the current delta. + lineDeltas.emplace_back( + lineNumber, static_cast<int64_t>(actualFormatted.size()) - expectedFormatted.size()); + + // Do not assert in order to allow multiple tests to be updated. + return true; } ABT makeIndexPath(FieldPathType fieldPath, bool isMultiKey) { @@ -96,4 +238,60 @@ IndexDefinition makeCompositeIndexDefinition(std::vector<TestIndexField> indexFi return IndexDefinition{std::move(idxCollSpec), isMultiKey}; } +std::unique_ptr<CostingInterface> makeCosting() { + return std::make_unique<cost_model::CostEstimator>( + cost_model::CostModelManager().getDefaultCoefficients()); +} + +OptPhaseManager makePhaseManager(OptPhaseManager::PhaseSet phaseSet, + PrefixId& prefixId, + Metadata metadata, + DebugInfo debugInfo, + QueryHints queryHints) { + return OptPhaseManager{std::move(phaseSet), + prefixId, + false /*requireRID*/, + std::move(metadata), + std::make_unique<HeuristicCE>(), + makeCosting(), + defaultConvertPathToInterval, + ConstEval::constFold, + std::move(debugInfo), + std::move(queryHints)}; +} + +OptPhaseManager makePhaseManager(OptPhaseManager::PhaseSet phaseSet, + PrefixId& prefixId, + Metadata metadata, + std::unique_ptr<CEInterface> ceDerivation, + DebugInfo debugInfo, + QueryHints queryHints) { + return OptPhaseManager{std::move(phaseSet), + prefixId, + false /*requireRID*/, + std::move(metadata), + std::move(ceDerivation), + makeCosting(), + defaultConvertPathToInterval, + ConstEval::constFold, + std::move(debugInfo), + std::move(queryHints)}; +} + +OptPhaseManager makePhaseManagerRequireRID(OptPhaseManager::PhaseSet phaseSet, + PrefixId& prefixId, + Metadata metadata, + DebugInfo debugInfo, + QueryHints queryHints) { + return OptPhaseManager{std::move(phaseSet), + prefixId, + true /*requireRID*/, + std::move(metadata), + std::make_unique<HeuristicCE>(), + makeCosting(), + defaultConvertPathToInterval, + ConstEval::constFold, + std::move(debugInfo), + std::move(queryHints)}; +} } // namespace mongo::optimizer diff --git a/src/mongo/db/query/optimizer/utils/unit_test_utils.h b/src/mongo/db/query/optimizer/utils/unit_test_utils.h index b0ad4704b84..e358dc13495 100644 --- a/src/mongo/db/query/optimizer/utils/unit_test_utils.h +++ b/src/mongo/db/query/optimizer/utils/unit_test_utils.h @@ -31,6 +31,7 @@ #include "mongo/db/bson/dotted_path_support.h" #include "mongo/db/query/optimizer/defs.h" +#include "mongo/db/query/optimizer/opt_phase_manager.h" #include "mongo/db/query/optimizer/utils/utils.h" @@ -38,6 +39,11 @@ namespace mongo::optimizer { void maybePrintABT(const ABT& abt); +bool handleAutoUpdate(const std::string& expected, + const std::string& actual, + const std::string& fileName, + size_t lineNumber); + #define ASSERT_EXPLAIN(expected, abt) \ maybePrintABT(abt); \ ASSERT_EQ(expected, ExplainGenerator::explain(abt)) @@ -46,13 +52,36 @@ void maybePrintABT(const ABT& abt); maybePrintABT(abt); \ ASSERT_EQ(expected, ExplainGenerator::explainV2(abt)) +/** + * Auto update result back in the source file if the assert fails. + * The expected result must be a multi-line string in the following form: + * + * ASSERT_EXPLAIN_V2_AUTO( // NOLINT + * "BinaryOp [Add]\n" + * "| Const [2]\n" + * "Const [1]\n", + * tree); + * + * Limitations: + * 1. There should not be any comments or other formatting inside the multi-line string + * constant other than 'NOLINT'. If we have a single-line constant, the auto-updating will + * generate a 'NOLINT' at the end of the line. + * 2. The expression which we are explaining ('tree' in the example above) must fit on a single + * line. The macro should be indented by 4 spaces. + * + * TODO: SERVER-71004: Extend the usability of the auto-update macro. + */ +#define ASSERT_EXPLAIN_V2_AUTO(expected, abt) \ + maybePrintABT(abt); \ + ASSERT(handleAutoUpdate(expected, ExplainGenerator::explainV2(abt), __FILE__, __LINE__)) + #define ASSERT_EXPLAIN_V2Compact(expected, abt) \ maybePrintABT(abt); \ ASSERT_EQ(expected, ExplainGenerator::explainV2Compact(abt)) #define ASSERT_EXPLAIN_BSON(expected, abt) \ maybePrintABT(abt); \ - ASSERT_EQ(expected, ExplainGenerator::explainBSON(abt)) + ASSERT_EQ(expected, ExplainGenerator::explainBSONStr(abt)) #define ASSERT_EXPLAIN_PROPS_V2(expected, phaseManager) \ ASSERT_EQ(expected, \ @@ -93,4 +122,37 @@ IndexDefinition makeIndexDefinition(FieldNameType fieldName, IndexDefinition makeCompositeIndexDefinition(std::vector<TestIndexField> indexFields, bool isMultiKey = true); +/** + * A convenience factory function to create costing. + */ +std::unique_ptr<CostingInterface> makeCosting(); + +/** + * A convenience factory function to create OptPhaseManager for unit tests. + */ +OptPhaseManager makePhaseManager(OptPhaseManager::PhaseSet phaseSet, + PrefixId& prefixId, + Metadata metadata, + DebugInfo debugInfo, + QueryHints queryHints = {}); + +/** + * A convenience factory function to create OptPhaseManager for unit tests with CE hints. + */ +OptPhaseManager makePhaseManager(OptPhaseManager::PhaseSet phaseSet, + PrefixId& prefixId, + Metadata metadata, + std::unique_ptr<CEInterface> ceDerivation, + DebugInfo debugInfo, + QueryHints queryHints = {}); + +/** + * A convenience factory function to create OptPhaseManager for unit tests which requires RID. + */ +OptPhaseManager makePhaseManagerRequireRID(OptPhaseManager::PhaseSet phaseSet, + PrefixId& prefixId, + Metadata metadata, + DebugInfo debugInfo, + QueryHints queryHints = {}); + } // namespace mongo::optimizer 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 |
