diff options
author | Ian Boros <ian.boros@mongodb.com> | 2020-01-21 23:27:55 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2020-01-21 23:27:55 +0000 |
commit | 48cd578fa9c3ef317666ca475f9ee14c1fe0bc4f (patch) | |
tree | 06d2404c18339a300eb8441bbce195c71f52321e /src | |
parent | 896505a1ee961597277945fe63892d0bbb91f9ad (diff) | |
download | mongo-48cd578fa9c3ef317666ca475f9ee14c1fe0bc4f.tar.gz |
SERVER-45403 Don't recompute wildcard projection exhaustive paths
Diffstat (limited to 'src')
21 files changed, 185 insertions, 115 deletions
diff --git a/src/mongo/db/exec/add_fields_projection_executor.h b/src/mongo/db/exec/add_fields_projection_executor.h index e17ce25e494..00ba580c2c7 100644 --- a/src/mongo/db/exec/add_fields_projection_executor.h +++ b/src/mongo/db/exec/add_fields_projection_executor.h @@ -117,6 +117,10 @@ public: */ Document applyProjection(const Document& inputDoc) const final; + boost::optional<std::set<FieldRef>> extractExhaustivePaths() const { + return boost::none; + } + private: /** * Attempts to parse 'objSpec' as an expression like {$add: [...]}. Adds a computed field to diff --git a/src/mongo/db/exec/exclusion_projection_executor.h b/src/mongo/db/exec/exclusion_projection_executor.h index d8cdab39e1d..cf21bf57126 100644 --- a/src/mongo/db/exec/exclusion_projection_executor.h +++ b/src/mongo/db/exec/exclusion_projection_executor.h @@ -136,6 +136,10 @@ public: return {DocumentSource::GetModPathsReturn::Type::kFiniteSet, std::move(modifiedPaths), {}}; } + boost::optional<std::set<FieldRef>> extractExhaustivePaths() const { + return boost::none; + } + private: // The ExclusionNode tree does most of the execution work once constructed. std::unique_ptr<ExclusionNode> _root; diff --git a/src/mongo/db/exec/inclusion_projection_executor.h b/src/mongo/db/exec/inclusion_projection_executor.h index 2ce8d03623c..b095a423652 100644 --- a/src/mongo/db/exec/inclusion_projection_executor.h +++ b/src/mongo/db/exec/inclusion_projection_executor.h @@ -225,6 +225,21 @@ public: return _root->applyToDocument(inputDoc); } + /** + * Returns the exhaustive set of all paths that will be preserved by this projection, or + * boost::none if the exhaustive set cannot be determined. + */ + boost::optional<std::set<FieldRef>> extractExhaustivePaths() const override { + std::set<FieldRef> exhaustivePaths; + DepsTracker depsTracker; + addDependencies(&depsTracker); + for (auto&& field : depsTracker.fields) { + exhaustivePaths.insert(FieldRef{field}); + } + + return exhaustivePaths; + } + private: // The InclusionNode tree does most of the execution work once constructed. std::unique_ptr<InclusionNode> _root; diff --git a/src/mongo/db/exec/projection_executor.h b/src/mongo/db/exec/projection_executor.h index 7921d4510e8..ca8e0d3990c 100644 --- a/src/mongo/db/exec/projection_executor.h +++ b/src/mongo/db/exec/projection_executor.h @@ -91,6 +91,12 @@ public: _rootReplacementExpression = expr; } + /** + * Returns the exhaustive set of all paths that will be preserved by this projection, or + * boost::none if the exhaustive set cannot be determined. + */ + virtual boost::optional<std::set<FieldRef>> extractExhaustivePaths() const = 0; + protected: ProjectionExecutor(const boost::intrusive_ptr<ExpressionContext>& expCtx, ProjectionPolicies policies) diff --git a/src/mongo/db/exec/projection_executor_utils.cpp b/src/mongo/db/exec/projection_executor_utils.cpp index 0923e2e2fab..1cbc04531c9 100644 --- a/src/mongo/db/exec/projection_executor_utils.cpp +++ b/src/mongo/db/exec/projection_executor_utils.cpp @@ -54,20 +54,6 @@ stdx::unordered_set<std::string> applyProjectionToFields( return out; } -std::set<FieldRef> extractExhaustivePaths(const projection_executor::ProjectionExecutor* executor) { - std::set<FieldRef> exhaustivePaths; - - if (executor->getType() == TransformerInterface::TransformerType::kInclusionProjection) { - DepsTracker depsTracker; - executor->addDependencies(&depsTracker); - for (auto&& field : depsTracker.fields) { - exhaustivePaths.insert(FieldRef{field}); - } - } - - return exhaustivePaths; -} - namespace { /** * Holds various parameters required to apply a $slice projection. Populated from the arguments diff --git a/src/mongo/db/exec/projection_executor_utils.h b/src/mongo/db/exec/projection_executor_utils.h index 338d12d39d3..0b5558bc221 100644 --- a/src/mongo/db/exec/projection_executor_utils.h +++ b/src/mongo/db/exec/projection_executor_utils.h @@ -50,13 +50,6 @@ stdx::unordered_set<std::string> applyProjectionToFields( const stdx::unordered_set<std::string>& fields); /** - * Returns the exhaustive set of all paths that will be preserved by this projection, or an - * empty set if the exhaustive set cannot be determined. An inclusion will always produce an - * exhaustive set; an exclusion will always produce an empty set. - */ -std::set<FieldRef> extractExhaustivePaths(const projection_executor::ProjectionExecutor* executor); - -/** * Applies a positional projection on the first array found in the 'path' on a projection * 'preImage' document. The applied projection is merged with a projection 'postImage' document. * The 'matchExpr' specifies a condition to locate the first matching element in the array and must diff --git a/src/mongo/db/exec/projection_executor_wildcard_access_test.cpp b/src/mongo/db/exec/projection_executor_wildcard_access_test.cpp index 672447b90d5..8edf7432fe4 100644 --- a/src/mongo/db/exec/projection_executor_wildcard_access_test.cpp +++ b/src/mongo/db/exec/projection_executor_wildcard_access_test.cpp @@ -211,11 +211,11 @@ TEST(ProjectionExecutorTests, InclusionFieldPathsWithImplicitIdInclusion) { ASSERT(parsedProject->getType() == TransformerInterface::TransformerType::kInclusionProjection); // Extract the exhaustive set of paths that will be preserved by the projection. - auto exhaustivePaths = projection_executor_utils::extractExhaustivePaths(parsedProject.get()); + auto exhaustivePaths = parsedProject->extractExhaustivePaths(); std::set<FieldRef> expectedPaths = toFieldRefs({"_id", "a.b.c", "d"}); // Verify that the exhaustive set of paths is as expected. - ASSERT(exhaustivePaths == expectedPaths); + ASSERT(*exhaustivePaths == expectedPaths); } TEST(ProjectionExecutorTests, InclusionFieldPathsWithExplicitIdInclusion) { @@ -224,11 +224,11 @@ TEST(ProjectionExecutorTests, InclusionFieldPathsWithExplicitIdInclusion) { ASSERT(parsedProject->getType() == TransformerInterface::TransformerType::kInclusionProjection); // Extract the exhaustive set of paths that will be preserved by the projection. - auto exhaustivePaths = projection_executor_utils::extractExhaustivePaths(parsedProject.get()); + auto exhaustivePaths = parsedProject->extractExhaustivePaths(); std::set<FieldRef> expectedPaths = toFieldRefs({"_id", "a.b.c", "d"}); // Verify that the exhaustive set of paths is as expected. - ASSERT(exhaustivePaths == expectedPaths); + ASSERT(*exhaustivePaths == expectedPaths); } TEST(ProjectionExecutorTests, InclusionFieldPathsWithExplicitIdInclusionIdOnly) { @@ -237,11 +237,11 @@ TEST(ProjectionExecutorTests, InclusionFieldPathsWithExplicitIdInclusionIdOnly) ASSERT(parsedProject->getType() == TransformerInterface::TransformerType::kInclusionProjection); // Extract the exhaustive set of paths that will be preserved by the projection. - auto exhaustivePaths = projection_executor_utils::extractExhaustivePaths(parsedProject.get()); + auto exhaustivePaths = parsedProject->extractExhaustivePaths(); std::set<FieldRef> expectedPaths = toFieldRefs({"_id"}); // Verify that the exhaustive set of paths is as expected. - ASSERT(exhaustivePaths == expectedPaths); + ASSERT(*exhaustivePaths == expectedPaths); } TEST(ProjectionExecutorTests, InclusionFieldPathsWithImplicitIdExclusion) { @@ -250,11 +250,11 @@ TEST(ProjectionExecutorTests, InclusionFieldPathsWithImplicitIdExclusion) { ASSERT(parsedProject->getType() == TransformerInterface::TransformerType::kInclusionProjection); // Extract the exhaustive set of paths that will be preserved by the projection. - auto exhaustivePaths = projection_executor_utils::extractExhaustivePaths(parsedProject.get()); + auto exhaustivePaths = parsedProject->extractExhaustivePaths(); std::set<FieldRef> expectedPaths = toFieldRefs({"a.b.c", "d"}); // Verify that the exhaustive set of paths is as expected. - ASSERT(exhaustivePaths == expectedPaths); + ASSERT(*exhaustivePaths == expectedPaths); } TEST(ProjectionExecutorTests, InclusionFieldPathsWithExplicitIdExclusion) { @@ -263,11 +263,11 @@ TEST(ProjectionExecutorTests, InclusionFieldPathsWithExplicitIdExclusion) { ASSERT(parsedProject->getType() == TransformerInterface::TransformerType::kInclusionProjection); // Extract the exhaustive set of paths that will be preserved by the projection. - auto exhaustivePaths = projection_executor_utils::extractExhaustivePaths(parsedProject.get()); + auto exhaustivePaths = parsedProject->extractExhaustivePaths(); std::set<FieldRef> expectedPaths = toFieldRefs({"a.b.c", "d"}); // Verify that the exhaustive set of paths is as expected. - ASSERT(exhaustivePaths == expectedPaths); + ASSERT(*exhaustivePaths == expectedPaths); } TEST(ProjectionExecutorTests, ExclusionFieldPathsWithImplicitIdInclusion) { @@ -276,10 +276,10 @@ TEST(ProjectionExecutorTests, ExclusionFieldPathsWithImplicitIdInclusion) { ASSERT(parsedProject->getType() == TransformerInterface::TransformerType::kExclusionProjection); // Extract the exhaustive set of paths that will be preserved by the projection. - auto exhaustivePaths = projection_executor_utils::extractExhaustivePaths(parsedProject.get()); + auto exhaustivePaths = parsedProject->extractExhaustivePaths(); - // Verify that the exhaustive set is empty, despite the implicit inclusion of _id. - ASSERT(exhaustivePaths.empty()); + // Verify that the exhaustive set is not available, despite the implicit inclusion of _id. + ASSERT(!exhaustivePaths); } TEST(ProjectionExecutorTests, ExclusionFieldPathsWithExplicitIdInclusion) { @@ -288,10 +288,10 @@ TEST(ProjectionExecutorTests, ExclusionFieldPathsWithExplicitIdInclusion) { ASSERT(parsedProject->getType() == TransformerInterface::TransformerType::kExclusionProjection); // Extract the exhaustive set of paths that will be preserved by the projection. - auto exhaustivePaths = projection_executor_utils::extractExhaustivePaths(parsedProject.get()); + auto exhaustivePaths = parsedProject->extractExhaustivePaths(); - // Verify that the exhaustive set is empty, despite the explicit inclusion of _id. - ASSERT(exhaustivePaths.empty()); + // Verify that the exhaustive set is not available, despite the explicit inclusion of _id. + ASSERT(!exhaustivePaths); } TEST(ProjectionExecutorTests, ExclusionFieldPathsWithImplicitIdExclusion) { @@ -300,10 +300,10 @@ TEST(ProjectionExecutorTests, ExclusionFieldPathsWithImplicitIdExclusion) { ASSERT(parsedProject->getType() == TransformerInterface::TransformerType::kExclusionProjection); // Extract the exhaustive set of paths that will be preserved by the projection. - auto exhaustivePaths = projection_executor_utils::extractExhaustivePaths(parsedProject.get()); + auto exhaustivePaths = parsedProject->extractExhaustivePaths(); // Verify that the exhaustive set is empty. - ASSERT(exhaustivePaths.empty()); + ASSERT(!exhaustivePaths); } TEST(ProjectionExecutorTests, ExclusionFieldPathsWithExplicitIdExclusion) { @@ -312,10 +312,10 @@ TEST(ProjectionExecutorTests, ExclusionFieldPathsWithExplicitIdExclusion) { ASSERT(parsedProject->getType() == TransformerInterface::TransformerType::kExclusionProjection); // Extract the exhaustive set of paths that will be preserved by the projection. - auto exhaustivePaths = projection_executor_utils::extractExhaustivePaths(parsedProject.get()); + auto exhaustivePaths = parsedProject->extractExhaustivePaths(); // Verify that the exhaustive set is empty. - ASSERT(exhaustivePaths.empty()); + ASSERT(!exhaustivePaths); } TEST(ProjectionExecutorTests, ExclusionFieldPathsWithExplicitIdExclusionIdOnly) { @@ -324,10 +324,10 @@ TEST(ProjectionExecutorTests, ExclusionFieldPathsWithExplicitIdExclusionIdOnly) ASSERT(parsedProject->getType() == TransformerInterface::TransformerType::kExclusionProjection); // Extract the exhaustive set of paths that will be preserved by the projection. - auto exhaustivePaths = projection_executor_utils::extractExhaustivePaths(parsedProject.get()); + auto exhaustivePaths = parsedProject->extractExhaustivePaths(); // Verify that the exhaustive set is empty. - ASSERT(exhaustivePaths.empty()); + ASSERT(!exhaustivePaths); } } // namespace diff --git a/src/mongo/db/exec/wildcard_projection.h b/src/mongo/db/exec/wildcard_projection.h new file mode 100644 index 00000000000..38005b5cf2c --- /dev/null +++ b/src/mongo/db/exec/wildcard_projection.h @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2020-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/exec/projection_executor.h" +#include "mongo/db/field_ref.h" + +namespace mongo { + +class WildcardProjection { +public: + WildcardProjection(std::unique_ptr<projection_executor::ProjectionExecutor> projExec) + : _exec(std::move(projExec)), _exhaustivePaths(_exec->extractExhaustivePaths()) { + invariant(_exec); + } + + projection_executor::ProjectionExecutor* exec() const { + return _exec.get(); + } + + const boost::optional<std::set<FieldRef>>& exhaustivePaths() const { + return _exhaustivePaths; + } + +private: + // Guaranteed to be non-null. + std::unique_ptr<projection_executor::ProjectionExecutor> _exec; + // Store this here to avoid having to recompute it repeatedly, which is expensive. + boost::optional<std::set<FieldRef>> _exhaustivePaths; +}; +} // namespace mongo diff --git a/src/mongo/db/index/wildcard_access_method.h b/src/mongo/db/index/wildcard_access_method.h index da03e69c9dc..0d87d6bbb99 100644 --- a/src/mongo/db/index/wildcard_access_method.h +++ b/src/mongo/db/index/wildcard_access_method.h @@ -71,10 +71,10 @@ public: const MultikeyPaths& multikeyPaths) const final; /** - * Returns a pointer to the ProjectionExecutor owned by the underlying WildcardKeyGenerator. + * Returns a pointer to the WildcardProjection owned by the underlying WildcardKeyGenerator. */ - projection_executor::ProjectionExecutor* getProjectionExecutor() const { - return _keyGen.getProjectionExecutor(); + const WildcardProjection* getWildcardProjection() const { + return _keyGen.getWildcardProjection(); } /** diff --git a/src/mongo/db/index/wildcard_key_generator.cpp b/src/mongo/db/index/wildcard_key_generator.cpp index 2766ff5e4fa..831cd776a15 100644 --- a/src/mongo/db/index/wildcard_key_generator.cpp +++ b/src/mongo/db/index/wildcard_key_generator.cpp @@ -65,8 +65,8 @@ void popPathComponent(BSONElement elem, bool enclosingObjIsArray, FieldRef* path constexpr StringData WildcardKeyGenerator::kSubtreeSuffix; -std::unique_ptr<projection_executor::ProjectionExecutor> -WildcardKeyGenerator::createProjectionExecutor(BSONObj keyPattern, BSONObj pathProjection) { +WildcardProjection WildcardKeyGenerator::createProjectionExecutor(BSONObj keyPattern, + BSONObj pathProjection) { // We should never have a key pattern that contains more than a single element. invariant(keyPattern.nFields() == 1); @@ -91,8 +91,8 @@ WildcardKeyGenerator::createProjectionExecutor(BSONObj keyPattern, BSONObj pathP auto expCtx = make_intrusive<ExpressionContext>(nullptr, nullptr); auto policies = ProjectionPolicies::wildcardIndexSpecProjectionPolicies(); auto projection = projection_ast::parse(expCtx, projSpec, policies); - return projection_executor::buildProjectionExecutor( - expCtx, &projection, policies, projection_executor::kDefaultBuilderParams); + return WildcardProjection{projection_executor::buildProjectionExecutor( + expCtx, &projection, policies, projection_executor::kDefaultBuilderParams)}; } WildcardKeyGenerator::WildcardKeyGenerator(BSONObj keyPattern, @@ -100,7 +100,7 @@ WildcardKeyGenerator::WildcardKeyGenerator(BSONObj keyPattern, const CollatorInterface* collator, KeyString::Version keyStringVersion, Ordering ordering) - : _projExec(createProjectionExecutor(keyPattern, pathProjection)), + : _proj(createProjectionExecutor(keyPattern, pathProjection)), _collator(collator), _keyPattern(keyPattern), _keyStringVersion(keyStringVersion), @@ -111,7 +111,7 @@ void WildcardKeyGenerator::generateKeys(BSONObj inputDoc, KeyStringSet* multikeyPaths, boost::optional<RecordId> id) const { FieldRef rootPath; - _traverseWildcard(_projExec->applyTransformation(Document{inputDoc}).toBson(), + _traverseWildcard(_proj.exec()->applyTransformation(Document{inputDoc}).toBson(), false, &rootPath, keys, diff --git a/src/mongo/db/index/wildcard_key_generator.h b/src/mongo/db/index/wildcard_key_generator.h index cf99187e35f..cd403939840 100644 --- a/src/mongo/db/index/wildcard_key_generator.h +++ b/src/mongo/db/index/wildcard_key_generator.h @@ -29,7 +29,7 @@ #pragma once -#include "mongo/db/exec/projection_executor.h" +#include "mongo/db/exec/wildcard_projection.h" #include "mongo/db/field_ref.h" #include "mongo/db/query/collation/collator_interface.h" #include "mongo/db/storage/key_string.h" @@ -51,8 +51,7 @@ public: * internally when generating the keys for the $** index, as defined by the 'keyPattern' and * 'pathProjection' arguments. */ - static std::unique_ptr<projection_executor::ProjectionExecutor> createProjectionExecutor( - BSONObj keyPattern, BSONObj pathProjection); + static WildcardProjection createProjectionExecutor(BSONObj keyPattern, BSONObj pathProjection); WildcardKeyGenerator(BSONObj keyPattern, BSONObj pathProjection, @@ -63,8 +62,8 @@ public: /** * Returns a pointer to the key generator's underlying ProjectionExecutor. */ - projection_executor::ProjectionExecutor* getProjectionExecutor() const { - return _projExec.get(); + const WildcardProjection* getWildcardProjection() const { + return &_proj; } /** @@ -107,7 +106,7 @@ private: KeyStringSet* keys, boost::optional<RecordId> id) const; - std::unique_ptr<projection_executor::ProjectionExecutor> _projExec; + WildcardProjection _proj; const CollatorInterface* _collator; const BSONObj _keyPattern; const KeyString::Version _keyStringVersion; diff --git a/src/mongo/db/query/collection_query_info.cpp b/src/mongo/db/query/collection_query_info.cpp index d65773a230c..9d18d8175d9 100644 --- a/src/mongo/db/query/collection_query_info.cpp +++ b/src/mongo/db/query/collection_query_info.cpp @@ -61,9 +61,9 @@ CoreIndexInfo indexInfoFromIndexCatalogEntry(const IndexCatalogEntry& ice) { auto accessMethod = ice.accessMethod(); invariant(accessMethod); - projection_executor::ProjectionExecutor* projExec = nullptr; + const WildcardProjection* projExec = nullptr; if (desc->getIndexType() == IndexType::INDEX_WILDCARD) - projExec = static_cast<const WildcardAccessMethod*>(accessMethod)->getProjectionExecutor(); + projExec = static_cast<const WildcardAccessMethod*>(accessMethod)->getWildcardProjection(); return {desc->keyPattern(), desc->getIndexType(), @@ -100,17 +100,18 @@ void CollectionQueryInfo::computeIndexKeys(OperationContext* opCtx) { if (descriptor->getAccessMethodName() == IndexNames::WILDCARD) { // Obtain the projection used by the $** index's key generator. const auto* pathProj = - static_cast<const WildcardAccessMethod*>(iam)->getProjectionExecutor(); + static_cast<const WildcardAccessMethod*>(iam)->getWildcardProjection(); // If the projection is an exclusion, then we must check the new document's keys on all // updates, since we do not exhaustively know the set of paths to be indexed. - if (pathProj->getType() == + if (pathProj->exec()->getType() == TransformerInterface::TransformerType::kExclusionProjection) { _indexedPaths.allPathsIndexed(); } else { // If a subtree was specified in the keyPattern, or if an inclusion projection is // present, then we need only index the path(s) preserved by the projection. - for (const auto& path : - projection_executor_utils::extractExhaustivePaths(pathProj)) { + const auto& exhaustivePaths = pathProj->exhaustivePaths(); + invariant(exhaustivePaths); + for (const auto& path : *exhaustivePaths) { _indexedPaths.addPath(path); } } diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp index a8f215d0671..3dcd0e2aa40 100644 --- a/src/mongo/db/query/get_executor.cpp +++ b/src/mongo/db/query/get_executor.cpp @@ -166,18 +166,19 @@ IndexEntry indexEntryFromIndexCatalogEntry(OperationContext* opCtx, const bool isMultikey = desc->isMultikey(); - projection_executor::ProjectionExecutor* projExec = nullptr; + const WildcardProjection* wildcardProjection = nullptr; std::set<FieldRef> multikeyPathSet; if (desc->getIndexType() == IndexType::INDEX_WILDCARD) { - projExec = static_cast<const WildcardAccessMethod*>(accessMethod)->getProjectionExecutor(); + wildcardProjection = + static_cast<const WildcardAccessMethod*>(accessMethod)->getWildcardProjection(); if (isMultikey) { MultikeyMetadataAccessStats mkAccessStats; if (canonicalQuery) { stdx::unordered_set<std::string> fields; QueryPlannerIXSelect::getFields(canonicalQuery->root(), &fields); - const auto projectedFields = - projection_executor_utils::applyProjectionToFields(projExec, fields); + const auto projectedFields = projection_executor_utils::applyProjectionToFields( + wildcardProjection->exec(), fields); multikeyPathSet = accessMethod->getMultikeyPathSet(opCtx, projectedFields, &mkAccessStats); @@ -207,7 +208,7 @@ IndexEntry indexEntryFromIndexCatalogEntry(OperationContext* opCtx, ice.getFilterExpression(), desc->infoObj(), ice.getCollator(), - projExec}; + wildcardProjection}; } /** @@ -1409,7 +1410,8 @@ QueryPlannerParams fillOutPlannerParamsForDistinct(OperationContext* opCtx, } else if (desc->getIndexType() == IndexType::INDEX_WILDCARD && !query.isEmpty()) { // Check whether the $** projection captures the field over which we are distinct-ing. auto* proj = static_cast<const WildcardAccessMethod*>(ice->accessMethod()) - ->getProjectionExecutor(); + ->getWildcardProjection() + ->exec(); if (projection_executor_utils::applyProjectionToOneField(proj, parsedDistinct.getKey())) { plannerParams.indices.push_back( diff --git a/src/mongo/db/query/get_executor_test.cpp b/src/mongo/db/query/get_executor_test.cpp index ec11139b37f..33f22f6ffed 100644 --- a/src/mongo/db/query/get_executor_test.cpp +++ b/src/mongo/db/query/get_executor_test.cpp @@ -39,6 +39,7 @@ #include "mongo/bson/simple_bsonobj_comparator.h" #include "mongo/db/exec/projection_executor.h" #include "mongo/db/exec/projection_executor_builder.h" +#include "mongo/db/exec/wildcard_projection.h" #include "mongo/db/json.h" #include "mongo/db/pipeline/expression_context_for_test.h" #include "mongo/db/query/projection_parser.h" @@ -57,7 +58,7 @@ auto createProjectionExecutor(const BSONObj& spec, const ProjectionPolicies& pol auto projection = projection_ast::parse(expCtx, spec, policies); auto executor = projection_executor::buildProjectionExecutor( expCtx, &projection, policies, projection_executor::kDefaultBuilderParams); - return executor; + return WildcardProjection{std::move(executor)}; } using std::unique_ptr; @@ -150,7 +151,7 @@ IndexEntry buildSimpleIndexEntry(const BSONObj& kp, const std::string& indexName * is neccesary for wildcard indicies. */ IndexEntry buildWildcardIndexEntry(const BSONObj& kp, - projection_executor::ProjectionExecutor* projExec, + const WildcardProjection& wcProj, const std::string& indexName) { return {kp, IndexNames::nameToType(IndexNames::findPluginName(kp)), @@ -163,7 +164,7 @@ IndexEntry buildWildcardIndexEntry(const BSONObj& kp, nullptr, {}, nullptr, - projExec}; + &wcProj}; } // Use of index filters to select compound index over single key index. @@ -219,11 +220,11 @@ TEST(GetExecutorTest, GetAllowedIndicesMatchesMultipleIndexesByKey) { } TEST(GetExecutorTest, GetAllowedWildcardIndicesByKey) { - auto projExec = createProjectionExecutor( + auto wcProj = createProjectionExecutor( fromjson("{_id: 0}"), {ProjectionPolicies::DefaultIdPolicy::kExcludeId, ProjectionPolicies::ArrayRecursionPolicy::kDoNotRecurseNestedArrays}); - testAllowedIndices({buildWildcardIndexEntry(BSON("$**" << 1), projExec.get(), "$**_1"), + testAllowedIndices({buildWildcardIndexEntry(BSON("$**" << 1), wcProj, "$**_1"), buildSimpleIndexEntry(fromjson("{a: 1}"), "a_1"), buildSimpleIndexEntry(fromjson("{a: 1, b: 1}"), "a_1_b_1"), buildSimpleIndexEntry(fromjson("{a: 1, c: 1}"), "a_1_c_1")}, @@ -233,11 +234,11 @@ TEST(GetExecutorTest, GetAllowedWildcardIndicesByKey) { } TEST(GetExecutorTest, GetAllowedWildcardIndicesByName) { - auto projExec = createProjectionExecutor( + auto wcProj = createProjectionExecutor( fromjson("{_id: 0}"), {ProjectionPolicies::DefaultIdPolicy::kExcludeId, ProjectionPolicies::ArrayRecursionPolicy::kDoNotRecurseNestedArrays}); - testAllowedIndices({buildWildcardIndexEntry(BSON("$**" << 1), projExec.get(), "$**_1"), + testAllowedIndices({buildWildcardIndexEntry(BSON("$**" << 1), wcProj, "$**_1"), buildSimpleIndexEntry(fromjson("{a: 1}"), "a_1"), buildSimpleIndexEntry(fromjson("{a: 1, b: 1}"), "a_1_b_1"), buildSimpleIndexEntry(fromjson("{a: 1, c: 1}"), "a_1_c_1")}, @@ -247,11 +248,11 @@ TEST(GetExecutorTest, GetAllowedWildcardIndicesByName) { } TEST(GetExecutorTest, GetAllowedPathSpecifiedWildcardIndicesByKey) { - auto projExec = createProjectionExecutor( + auto wcProj = createProjectionExecutor( fromjson("{_id: 0}"), {ProjectionPolicies::DefaultIdPolicy::kExcludeId, ProjectionPolicies::ArrayRecursionPolicy::kDoNotRecurseNestedArrays}); - testAllowedIndices({buildWildcardIndexEntry(BSON("a.$**" << 1), projExec.get(), "a.$**_1"), + testAllowedIndices({buildWildcardIndexEntry(BSON("a.$**" << 1), wcProj, "a.$**_1"), buildSimpleIndexEntry(fromjson("{a: 1}"), "a_1"), buildSimpleIndexEntry(fromjson("{a: 1, b: 1}"), "a_1_b_1"), buildSimpleIndexEntry(fromjson("{a: 1, c: 1}"), "a_1_c_1")}, @@ -261,11 +262,11 @@ TEST(GetExecutorTest, GetAllowedPathSpecifiedWildcardIndicesByKey) { } TEST(GetExecutorTest, GetAllowedPathSpecifiedWildcardIndicesByName) { - auto projExec = createProjectionExecutor( + auto wcProj = createProjectionExecutor( fromjson("{_id: 0}"), {ProjectionPolicies::DefaultIdPolicy::kExcludeId, ProjectionPolicies::ArrayRecursionPolicy::kDoNotRecurseNestedArrays}); - testAllowedIndices({buildWildcardIndexEntry(BSON("a.$**" << 1), projExec.get(), "a.$**_1"), + testAllowedIndices({buildWildcardIndexEntry(BSON("a.$**" << 1), wcProj, "a.$**_1"), buildSimpleIndexEntry(fromjson("{a: 1}"), "a_1"), buildSimpleIndexEntry(fromjson("{a: 1, b: 1}"), "a_1_b_1"), buildSimpleIndexEntry(fromjson("{a: 1, c: 1}"), "a_1_c_1")}, diff --git a/src/mongo/db/query/index_entry.h b/src/mongo/db/query/index_entry.h index d9eaf0ecdd7..487e8c6853d 100644 --- a/src/mongo/db/query/index_entry.h +++ b/src/mongo/db/query/index_entry.h @@ -42,9 +42,7 @@ namespace mongo { class CollatorInterface; class MatchExpression; -namespace projection_executor { -class ProjectionExecutor; -} +class WildcardProjection; /** * A CoreIndexInfo is a representation of an index in the catalog with parsed information which is @@ -61,16 +59,16 @@ struct CoreIndexInfo { Identifier ident, const MatchExpression* fe = nullptr, const CollatorInterface* ci = nullptr, - projection_executor::ProjectionExecutor* projExec = nullptr) + const WildcardProjection* wildcardProj = nullptr) : identifier(std::move(ident)), keyPattern(kp), filterExpr(fe), type(type), sparse(sp), collator(ci), - wildcardProjection(projExec) { + wildcardProjection(wildcardProj) { // We always expect a projection executor for $** indexes, and none otherwise. - invariant((type == IndexType::INDEX_WILDCARD) == (projExec != nullptr)); + invariant((type == IndexType::INDEX_WILDCARD) == (wildcardProjection != nullptr)); } virtual ~CoreIndexInfo() = default; @@ -138,7 +136,7 @@ struct CoreIndexInfo { // For $** indexes, a pointer to the projection executor owned by the index access method. Null // unless this IndexEntry represents a wildcard index, in which case this is always non-null. - projection_executor::ProjectionExecutor* wildcardProjection = nullptr; + const WildcardProjection* wildcardProjection = nullptr; }; /** @@ -160,8 +158,8 @@ struct IndexEntry : CoreIndexInfo { const MatchExpression* fe, const BSONObj& io, const CollatorInterface* ci, - projection_executor::ProjectionExecutor* projExec) - : CoreIndexInfo(kp, type, sp, std::move(ident), fe, ci, projExec), + const WildcardProjection* wildcardProjection) + : CoreIndexInfo(kp, type, sp, std::move(ident), fe, ci, wildcardProjection), multikey(mk), multikeyPaths(mkp), multikeyPathSet(std::move(multikeyPathSet)), diff --git a/src/mongo/db/query/plan_cache_indexability.cpp b/src/mongo/db/query/plan_cache_indexability.cpp index 96f7697e06c..4846cd85778 100644 --- a/src/mongo/db/query/plan_cache_indexability.cpp +++ b/src/mongo/db/query/plan_cache_indexability.cpp @@ -119,7 +119,7 @@ void PlanCacheIndexabilityState::processWildcardIndex(const CoreIndexInfo& cii) invariant(cii.type == IndexType::INDEX_WILDCARD); _wildcardIndexDiscriminators.emplace_back( - cii.wildcardProjection, cii.identifier.catalogName, cii.filterExpr, cii.collator); + cii.wildcardProjection->exec(), cii.identifier.catalogName, cii.filterExpr, cii.collator); } void PlanCacheIndexabilityState::processIndexCollation(const std::string& indexName, diff --git a/src/mongo/db/query/plan_cache_indexability_test.cpp b/src/mongo/db/query/plan_cache_indexability_test.cpp index eb2978ef6db..471e57bfafb 100644 --- a/src/mongo/db/query/plan_cache_indexability_test.cpp +++ b/src/mongo/db/query/plan_cache_indexability_test.cpp @@ -58,7 +58,8 @@ std::unique_ptr<MatchExpression> parseMatchExpression(const BSONObj& obj, // maintained by the $** index's IndexAccessMethod, and is required because the plan cache will // obtain unowned pointers to it. auto makeWildcardEntry(BSONObj keyPattern, const MatchExpression* filterExpr = nullptr) { - auto projExec = WildcardKeyGenerator::createProjectionExecutor(keyPattern, {}); + auto projExec = std::make_unique<WildcardProjection>( + WildcardKeyGenerator::createProjectionExecutor(keyPattern, {})); return std::make_pair(IndexEntry(keyPattern, IndexNames::nameToType(IndexNames::findPluginName(keyPattern)), false, // multikey diff --git a/src/mongo/db/query/plan_cache_test.cpp b/src/mongo/db/query/plan_cache_test.cpp index ce580870ca9..30c52b82e39 100644 --- a/src/mongo/db/query/plan_cache_test.cpp +++ b/src/mongo/db/query/plan_cache_test.cpp @@ -243,9 +243,9 @@ void assertEquivalent(const char* queryStr, // The latter simulates the ProjectionExecutor which, during normal operation, is owned and // maintained by the $** index's IndexAccessMethod, and is required because the plan cache will // obtain unowned pointers to it. -std::pair<IndexEntry, std::unique_ptr<projection_executor::ProjectionExecutor>> makeWildcardEntry( - BSONObj keyPattern) { - auto projExec = WildcardKeyGenerator::createProjectionExecutor(keyPattern, {}); +std::pair<IndexEntry, std::unique_ptr<WildcardProjection>> makeWildcardEntry(BSONObj keyPattern) { + auto wcProj = std::make_unique<WildcardProjection>( + WildcardKeyGenerator::createProjectionExecutor(keyPattern, {})); return {IndexEntry(keyPattern, IndexNames::nameToType(IndexNames::findPluginName(keyPattern)), false, // multikey @@ -257,22 +257,23 @@ std::pair<IndexEntry, std::unique_ptr<projection_executor::ProjectionExecutor>> nullptr, BSONObj(), nullptr, - projExec.get()), - std::move(projExec)}; + wcProj.get()), + std::move(wcProj)}; } // A version of the above for CoreIndexInfo, used for plan cache update tests. -std::pair<CoreIndexInfo, std::unique_ptr<projection_executor::ProjectionExecutor>> -makeWildcardUpdate(BSONObj keyPattern) { - auto projExec = WildcardKeyGenerator::createProjectionExecutor(keyPattern, {}); +std::pair<CoreIndexInfo, std::unique_ptr<WildcardProjection>> makeWildcardUpdate( + BSONObj keyPattern) { + auto wcProj = std::make_unique<WildcardProjection>( + WildcardKeyGenerator::createProjectionExecutor(keyPattern, {})); return {CoreIndexInfo(keyPattern, IndexNames::nameToType(IndexNames::findPluginName(keyPattern)), false, // sparse IndexEntry::Identifier{"indexName"}, // name nullptr, // filterExpr nullptr, // collation - projExec.get()), // wildcard - std::move(projExec)}; + wcProj.get()), // wildcard + std::move(wcProj)}; } // diff --git a/src/mongo/db/query/planner_ixselect_test.cpp b/src/mongo/db/query/planner_ixselect_test.cpp index 406104b96b0..84ba3f704d0 100644 --- a/src/mongo/db/query/planner_ixselect_test.cpp +++ b/src/mongo/db/query/planner_ixselect_test.cpp @@ -1057,10 +1057,11 @@ auto makeIndexEntry(BSONObj keyPattern, MultikeyPaths multiKeyPaths, std::set<FieldRef> multiKeyPathSet = {}, BSONObj infoObj = BSONObj()) { - auto projExec = (keyPattern.firstElement().fieldNameStringData().endsWith("$**"_sd) - ? WildcardKeyGenerator::createProjectionExecutor( - keyPattern, infoObj.getObjectField("wildcardProjection")) - : nullptr); + + auto wcProj = keyPattern.firstElement().fieldNameStringData().endsWith("$**"_sd) + ? std::make_unique<WildcardProjection>(WildcardKeyGenerator::createProjectionExecutor( + keyPattern, infoObj.getObjectField("wildcardProjection"))) + : std::unique_ptr<WildcardProjection>(nullptr); auto multiKey = !multiKeyPathSet.empty() || std::any_of(multiKeyPaths.cbegin(), multiKeyPaths.cend(), [](const auto& entry) { @@ -1077,8 +1078,8 @@ auto makeIndexEntry(BSONObj keyPattern, nullptr, {}, nullptr, - projExec.get()), - std::move(projExec)); + wcProj.get()), + std::move(wcProj)); } TEST(QueryPlannerIXSelectTest, InternalExprEqCannotUseMultiKeyIndex) { diff --git a/src/mongo/db/query/planner_wildcard_helpers.cpp b/src/mongo/db/query/planner_wildcard_helpers.cpp index edf768c75a3..9681a69cd92 100644 --- a/src/mongo/db/query/planner_wildcard_helpers.cpp +++ b/src/mongo/db/query/planner_wildcard_helpers.cpp @@ -356,12 +356,14 @@ void expandWildcardIndexEntry(const IndexEntry& wildcardIndex, invariant(wildcardIndex.multikeyPaths.empty()); // Obtain the projection executor from the parent wildcard IndexEntry. - auto projExec = wildcardIndex.wildcardProjection; - invariant(projExec); + auto* wildcardProjection = wildcardIndex.wildcardProjection; + invariant(wildcardProjection); const auto projectedFields = - projection_executor_utils::applyProjectionToFields(projExec, fields); - const auto& includedPaths = projection_executor_utils::extractExhaustivePaths(projExec); + projection_executor_utils::applyProjectionToFields(wildcardProjection->exec(), fields); + + std::set<FieldRef> includedPaths = + wildcardProjection->exhaustivePaths().value_or(std::set<FieldRef>{}); out->reserve(out->size() + projectedFields.size()); for (auto&& fieldName : projectedFields) { // Convert string 'fieldName' into a FieldRef, to better facilitate the subsequent checks. diff --git a/src/mongo/db/query/query_planner_wildcard_index_test.cpp b/src/mongo/db/query/query_planner_wildcard_index_test.cpp index 58a217c941e..dde97c3c187 100644 --- a/src/mongo/db/query/query_planner_wildcard_index_test.cpp +++ b/src/mongo/db/query/query_planner_wildcard_index_test.cpp @@ -71,7 +71,7 @@ protected: const bool isMultikey = !multikeyPathSet.empty(); BSONObj infoObj = BSON("wildcardProjection" << wildcardProjection); - _projExec = WildcardKeyGenerator::createProjectionExecutor(keyPattern, wildcardProjection); + _proj = WildcardKeyGenerator::createProjectionExecutor(keyPattern, wildcardProjection); params.indices.push_back({std::move(keyPattern), IndexType::INDEX_WILDCARD, @@ -84,10 +84,10 @@ protected: partialFilterExpr, std::move(infoObj), collator, - _projExec.get()}); + _proj.get_ptr()}); } - std::unique_ptr<projection_executor::ProjectionExecutor> _projExec; + boost::optional<WildcardProjection> _proj; }; // |