summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/query/optimizer')
-rw-r--r--src/mongo/db/query/optimizer/SConscript13
-rw-r--r--src/mongo/db/query/optimizer/cascades/cost_derivation.cpp446
-rw-r--r--src/mongo/db/query/optimizer/cascades/cost_derivation.h50
-rw-r--r--src/mongo/db/query/optimizer/cascades/implementers.cpp34
-rw-r--r--src/mongo/db/query/optimizer/cascades/logical_props_derivation.cpp5
-rw-r--r--src/mongo/db/query/optimizer/cascades/logical_rewriter.cpp42
-rw-r--r--src/mongo/db/query/optimizer/cascades/memo_defs.h4
-rw-r--r--src/mongo/db/query/optimizer/explain.cpp28
-rw-r--r--src/mongo/db/query/optimizer/explain.h5
-rw-r--r--src/mongo/db/query/optimizer/interval_intersection_test.cpp12
-rw-r--r--src/mongo/db/query/optimizer/logical_rewriter_optimizer_test.cpp120
-rw-r--r--src/mongo/db/query/optimizer/node.cpp22
-rw-r--r--src/mongo/db/query/optimizer/node.h13
-rw-r--r--src/mongo/db/query/optimizer/opt_phase_manager.cpp17
-rw-r--r--src/mongo/db/query/optimizer/opt_phase_manager.h5
-rw-r--r--src/mongo/db/query/optimizer/optimizer_failure_test.cpp48
-rw-r--r--src/mongo/db/query/optimizer/optimizer_test.cpp44
-rw-r--r--src/mongo/db/query/optimizer/physical_rewriter_optimizer_test.cpp245
-rw-r--r--src/mongo/db/query/optimizer/props.cpp15
-rw-r--r--src/mongo/db/query/optimizer/props.h7
-rw-r--r--src/mongo/db/query/optimizer/utils/abt_hash.cpp2
-rw-r--r--src/mongo/db/query/optimizer/utils/unit_test_pipeline_utils.cpp12
-rw-r--r--src/mongo/db/query/optimizer/utils/unit_test_utils.cpp206
-rw-r--r--src/mongo/db/query/optimizer/utils/unit_test_utils.h64
-rw-r--r--src/mongo/db/query/optimizer/utils/utils.cpp11
-rw-r--r--src/mongo/db/query/optimizer/utils/utils.h1
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