diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/query/canonical_query_encoder.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/query/planner_access.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/query/planner_analysis.cpp | 34 | ||||
-rw-r--r-- | src/mongo/db/query/projection.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/query/projection.h | 4 | ||||
-rw-r--r-- | src/mongo/db/query/projection_test.cpp | 27 | ||||
-rw-r--r-- | src/mongo/db/query/query_planner.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder.cpp | 23 |
8 files changed, 59 insertions, 43 deletions
diff --git a/src/mongo/db/query/canonical_query_encoder.cpp b/src/mongo/db/query/canonical_query_encoder.cpp index a6a94915e91..2013c8a635e 100644 --- a/src/mongo/db/query/canonical_query_encoder.cpp +++ b/src/mongo/db/query/canonical_query_encoder.cpp @@ -586,11 +586,11 @@ void encodeKeyForProj(const projection_ast::Projection* proj, StringBuilder* key return; } - std::vector<std::string> requiredFields = proj->getRequiredFields(); + std::set<std::string> requiredFields = proj->getRequiredFields(); // If the only requirement is that $sortKey be included with some value, we just act as if the // entire document is needed. - if (requiredFields.size() == 1 && requiredFields.front() == "$sortKey") { + if (requiredFields.size() == 1 && *requiredFields.begin() == "$sortKey") { return; } @@ -599,7 +599,6 @@ void encodeKeyForProj(const projection_ast::Projection* proj, StringBuilder* key *keyBuilder << kEncodeProjectionSection; // Encode the fields required by the projection in order. - std::sort(requiredFields.begin(), requiredFields.end()); bool isFirst = true; for (auto&& requiredField : requiredFields) { invariant(!requiredField.empty()); diff --git a/src/mongo/db/query/planner_access.cpp b/src/mongo/db/query/planner_access.cpp index 4d3ec560aa5..f283979cc5c 100644 --- a/src/mongo/db/query/planner_access.cpp +++ b/src/mongo/db/query/planner_access.cpp @@ -1163,7 +1163,7 @@ bool isCoveredNullQuery(const CanonicalQuery& query, // Note that it is not possible to project onto dotted paths of _id here, since they may be // null or missing, and the index cannot differentiate between the two cases, so we would // still need a FETCH stage. - return projFields.size() == 1 && projFields[0] == "_id"; + return projFields.size() == 1 && *projFields.begin() == "_id"; } return false; diff --git a/src/mongo/db/query/planner_analysis.cpp b/src/mongo/db/query/planner_analysis.cpp index 79fa06fa65b..9f3460a1cc6 100644 --- a/src/mongo/db/query/planner_analysis.cpp +++ b/src/mongo/db/query/planner_analysis.cpp @@ -33,6 +33,7 @@ #include <set> #include <vector> +#include "mongo/base/simple_string_data_comparator.h" #include "mongo/bson/simple_bsonelement_comparator.h" #include "mongo/db/bson/dotted_path_support.h" #include "mongo/db/index/expression_params.h" @@ -322,9 +323,9 @@ void geoSkipValidationOn(const std::set<StringData>& twoDSphereFields, /** * If any field is missing from the list of fields the projection wants, we are not covered. */ -auto providesAllFields(const vector<std::string>& fields, const QuerySolutionNode& solnRoot) { - for (size_t i = 0; i < fields.size(); ++i) { - if (!solnRoot.hasField(fields[i])) +auto providesAllFields(const std::set<std::string>& fields, const QuerySolutionNode& solnRoot) { + for (auto&& field : fields) { + if (!solnRoot.hasField(field)) return false; } return true; @@ -540,6 +541,20 @@ bool canUseSimpleSort(const QuerySolutionNode& solnRoot, !(plannerParams.options & QueryPlannerParams::PRESERVE_RECORD_ID); } +/** + * Returns true if 'setS' is a non-strict subset of 'setT'. + * + * The types of the sets are permitted to be different to allow checking something with compatible + * but different types e.g. std::set<std::string> and StringDataUnorderedMap. + */ +template <typename SetL, typename SetR> +bool isSubset(const SetL& setL, const SetR& setR) { + return setL.size() <= setR.size() && + std::all_of(setL.begin(), setL.end(), [&setR](auto&& lElem) { + return setR.find(lElem) != setR.end(); + }); +} + void removeProjectSimpleBelowGroupRecursive(QuerySolutionNode* solnRoot) { if (solnRoot == nullptr) { return; @@ -562,19 +577,16 @@ void removeProjectSimpleBelowGroupRecursive(QuerySolutionNode* solnRoot) { // projection, it would have type PROJECTION_DEFAULT. return; } - // Check to see if the projectNode's field set is a super set of the groupNodes. - auto projectNode = static_cast<ProjectionNodeSimple*>(projectNodeCandidate); - auto projectFields = projectNode->proj.getRequiredFields(); - if (!std::any_of(projectFields.begin(), projectFields.end(), [groupNode](auto&& fld) { - return groupNode->requiredFields.contains(fld); - })) { + if (!isSubset(groupNode->requiredFields, + checked_cast<ProjectionNodeSimple*>(projectNodeCandidate) + ->proj.getRequiredFields())) { // The dependency set of the GROUP stage is wider than the projectNode field set. return; - }; + } // Attach the projectNode's child to the groupNode's child. - groupNode->children[0] = std::move(projectNode->children[0]); + groupNode->children[0] = std::move(projectNodeCandidate->children[0]); } else { // Keep traversing the tree in search of a GROUP stage. for (size_t i = 0; i < solnRoot->children.size(); ++i) { diff --git a/src/mongo/db/query/projection.cpp b/src/mongo/db/query/projection.cpp index 73caaa8f312..3c93367ae8c 100644 --- a/src/mongo/db/query/projection.cpp +++ b/src/mongo/db/query/projection.cpp @@ -49,8 +49,8 @@ struct DepsAnalysisData { fieldDependencyTracker.fields.insert(fieldName); } - std::vector<std::string> requiredFields() const { - return {fieldDependencyTracker.fields.begin(), fieldDependencyTracker.fields.end()}; + std::set<std::string> requiredFields() const { + return fieldDependencyTracker.fields; } }; diff --git a/src/mongo/db/query/projection.h b/src/mongo/db/query/projection.h index 80ede2a7a1a..9987dd641db 100644 --- a/src/mongo/db/query/projection.h +++ b/src/mongo/db/query/projection.h @@ -48,7 +48,7 @@ struct ProjectionDependencies { bool hasExpressions = false; // Which fields are necessary to perform the projection, or boost::none if all are required. - boost::optional<std::vector<std::string>> requiredFields; + boost::optional<std::set<std::string>> requiredFields; bool hasDottedPath = false; @@ -94,7 +94,7 @@ public: * Return which fields are required to compute the projection, assuming the entire document is * not needed. */ - const std::vector<std::string>& getRequiredFields() const { + const std::set<std::string>& getRequiredFields() const { invariant(_type == ProjectType::kInclusion); return *_deps.requiredFields; } diff --git a/src/mongo/db/query/projection_test.cpp b/src/mongo/db/query/projection_test.cpp index 35abfb3e92f..1b9bd564104 100644 --- a/src/mongo/db/query/projection_test.cpp +++ b/src/mongo/db/query/projection_test.cpp @@ -117,8 +117,9 @@ TEST(QueryProjectionTest, MakeSingleFieldInclusion) { ASSERT_FALSE(proj.requiresDocument()); const auto& fields = proj.getRequiredFields(); ASSERT_EQUALS(fields.size(), 2U); - ASSERT_EQUALS(fields[0], "_id"); - ASSERT_EQUALS(fields[1], "a"); + auto fieldsIt = fields.begin(); + ASSERT_EQUALS(*fieldsIt++, "_id"); + ASSERT_EQUALS(*fieldsIt++, "a"); } TEST(QueryProjectionTest, MakeSingleFieldInclusionNoId) { @@ -126,7 +127,7 @@ TEST(QueryProjectionTest, MakeSingleFieldInclusionNoId) { ASSERT_FALSE(proj.requiresDocument()); const auto& fields = proj.getRequiredFields(); ASSERT_EQUALS(fields.size(), 1U); - ASSERT_EQUALS(fields[0], "a"); + ASSERT_EQUALS(*fields.begin(), "a"); } TEST(QueryProjectionTest, MakeSingleFieldId) { @@ -134,7 +135,7 @@ TEST(QueryProjectionTest, MakeSingleFieldId) { ASSERT_FALSE(proj.requiresDocument()); const auto& fields = proj.getRequiredFields(); ASSERT_EQUALS(fields.size(), 1U); - ASSERT_EQUALS(fields[0], "_id"); + ASSERT_EQUALS(*fields.begin(), "_id"); } TEST(QueryProjectionTest, MakeSingleFieldNoIdBoolean) { @@ -142,7 +143,7 @@ TEST(QueryProjectionTest, MakeSingleFieldNoIdBoolean) { ASSERT_FALSE(proj.requiresDocument()); const auto& fields = proj.getRequiredFields(); ASSERT_EQUALS(fields.size(), 1U); - ASSERT_EQUALS(fields[0], "a"); + ASSERT_EQUALS(*fields.begin(), "a"); } TEST(QueryProjectionTest, MakeSingleFieldFalseIdBoolean) { @@ -150,7 +151,7 @@ TEST(QueryProjectionTest, MakeSingleFieldFalseIdBoolean) { ASSERT_FALSE(proj.requiresDocument()); const auto& fields = proj.getRequiredFields(); ASSERT_EQUALS(fields.size(), 1U); - ASSERT_EQUALS(fields[0], "a"); + ASSERT_EQUALS(*fields.begin(), "a"); } // @@ -504,7 +505,7 @@ TEST(QueryProjectionTest, ProjectionWithExpressionIsNotSimple) { const auto& fields = proj.getRequiredFields(); ASSERT_EQ(fields.size(), 1); - ASSERT_EQ(fields[0], "_id"); + ASSERT_EQ(*fields.begin(), "_id"); } TEST(QueryProjectionTest, ProjectionWithTopLevelExpressionConstantDoesNotRequireField) { @@ -513,8 +514,9 @@ TEST(QueryProjectionTest, ProjectionWithTopLevelExpressionConstantDoesNotRequire const auto& fields = proj.getRequiredFields(); ASSERT_EQ(fields.size(), 2); - ASSERT_EQ(fields[0], "_id"); - ASSERT_EQ(fields[1], "b"); + auto fieldsIt = fields.begin(); + ASSERT_EQ(*fieldsIt++, "_id"); + ASSERT_EQ(*fieldsIt++, "b"); } TEST(QueryProjectionTest, ProjectionWithROOTNeedsWholeDocument) { @@ -530,8 +532,9 @@ TEST(QueryProjectionTest, ProjectionWithFieldPathExpressionDoesNotNeedWholeDocum const auto& fields = proj.getRequiredFields(); ASSERT_EQ(fields.size(), 2); - ASSERT_EQ(fields[0], "b"); - ASSERT_EQ(fields[1], "c"); + auto fieldsIt = fields.begin(); + ASSERT_EQ(*fieldsIt++, "b"); + ASSERT_EQ(*fieldsIt++, "c"); } TEST(QueryProjectionTest, AssignmentToDottedPathRequiresFirstComponent) { @@ -541,7 +544,7 @@ TEST(QueryProjectionTest, AssignmentToDottedPathRequiresFirstComponent) { const auto& fields = proj.getRequiredFields(); ASSERT_EQ(fields.size(), 1); - ASSERT_EQ(fields[0], "a"); + ASSERT_EQ(*fields.begin(), "a"); } } // namespace diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp index 693ce386cdf..de727ab6190 100644 --- a/src/mongo/db/query/query_planner.cpp +++ b/src/mongo/db/query/query_planner.cpp @@ -180,8 +180,7 @@ std::pair<DepsTracker, DepsTracker> computeDeps(const QueryPlannerParams& params outputDeps.needWholeDocument = true; return {std::move(filterDeps), std::move(outputDeps)}; } - auto projectionFields = query.getProj()->getRequiredFields(); - outputDeps.fields.insert(projectionFields.begin(), projectionFields.end()); + outputDeps.fields = query.getProj()->getRequiredFields(); if (auto sortPattern = query.getSortPattern()) { sortPattern->addDependencies(&outputDeps); } diff --git a/src/mongo/db/query/sbe_stage_builder.cpp b/src/mongo/db/query/sbe_stage_builder.cpp index 1ec091d515e..99384dc11fb 100644 --- a/src/mongo/db/query/sbe_stage_builder.cpp +++ b/src/mongo/db/query/sbe_stage_builder.cpp @@ -1635,16 +1635,19 @@ SlotBasedStageBuilder::buildProjectionSimple(const QuerySolutionNode* root, const auto childResult = outputs.get(kResult); outputs.set(kResult, _slotIdGenerator.generate()); - inputStage = sbe::makeS<sbe::MakeBsonObjStage>(std::move(inputStage), - outputs.get(kResult), - childResult, - sbe::MakeBsonObjStage::FieldBehavior::keep, - pn->proj.getRequiredFields(), - std::vector<std::string>{}, - sbe::value::SlotVector{}, - true, - false, - root->nodeId()); + inputStage = sbe::makeS<sbe::MakeBsonObjStage>( + std::move(inputStage), + outputs.get(kResult), + childResult, + sbe::MakeBsonObjStage::FieldBehavior::keep, + // TODO SERVER-67039 take a set instead of a vector here. + std::vector<std::string>{pn->proj.getRequiredFields().begin(), + pn->proj.getRequiredFields().end()}, + std::vector<std::string>{}, + sbe::value::SlotVector{}, + true, + false, + root->nodeId()); return {std::move(inputStage), std::move(outputs)}; } |