summaryrefslogtreecommitdiff
path: root/src/mongo/db/exec/inclusion_projection_executor.cpp
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-14 14:26:38 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-14 14:26:38 -0300
commit294bc6ecabf14c09c9bc8644704921dcf97cb44e (patch)
tree279b1e0bab53901a1647ac63c1c724f0f789a663 /src/mongo/db/exec/inclusion_projection_executor.cpp
parent70be7c27a251621187a1de533462ae2bb1e3bd39 (diff)
parent1e917fd798aa25b7066d4b414b51184f13d5a092 (diff)
Update upstream source from tag 'upstream/6.0.10'debian/6.0.10-1
Update to upstream version '6.0.10' with Debian dir 2d176fa254eee97b139f712fec5709641335a8c3
Diffstat (limited to 'src/mongo/db/exec/inclusion_projection_executor.cpp')
-rw-r--r--src/mongo/db/exec/inclusion_projection_executor.cpp105
1 files changed, 74 insertions, 31 deletions
diff --git a/src/mongo/db/exec/inclusion_projection_executor.cpp b/src/mongo/db/exec/inclusion_projection_executor.cpp
index d7384be8109..c2a759be998 100644
--- a/src/mongo/db/exec/inclusion_projection_executor.cpp
+++ b/src/mongo/db/exec/inclusion_projection_executor.cpp
@@ -99,6 +99,50 @@ boost::intrusive_ptr<Expression> substituteInExpr(boost::intrusive_ptr<Expressio
}
return ex;
};
+
+/**
+ * Returns a vector of top-level dependencies where each index i in the vector corresponds to the
+ * dependencies from the ith expression according to 'orderToProcess'.
+ */
+std::vector<OrderedPathSet> getTopLevelDeps(
+ const std::vector<std::string>& orderToProcess,
+ const StringMap<boost::intrusive_ptr<Expression>>& expressions,
+ const StringMap<std::unique_ptr<ProjectionNode>>& children) {
+ std::vector<OrderedPathSet> topLevelDeps;
+ for (const auto& field : orderToProcess) {
+ DepsTracker deps;
+ if (auto exprIt = expressions.find(field); exprIt != expressions.end()) {
+ exprIt->second->addDependencies(&deps);
+ } else {
+ // Each expression in orderToProcess should either be in expressions or children.
+ auto childIt = children.find(field);
+ tassert(6657000, "Unable to calculate dependencies", childIt != children.end());
+ childIt->second->reportDependencies(&deps);
+ }
+
+ OrderedPathSet ops{deps.fields.begin(), deps.fields.end()};
+ topLevelDeps.push_back(
+ DepsTracker::simplifyDependencies(ops, DepsTracker::TruncateToRootLevel::yes));
+ }
+ return topLevelDeps;
+}
+
+/**
+ * Returns whether or not there is an expression in the projection which depends on 'field' other
+ * than the expression which computes 'field'. For example, given field "a" and projection
+ * {a: "$b", c: {$sum: ["$a", 5]}}, return true. Given field "a" and projection
+ * {a: {$sum: ["$a", 5]}, c: "$b"}, return false. 'field' should be a top level path.
+ */
+bool computedExprDependsOnField(const std::vector<OrderedPathSet>& topLevelDeps,
+ const std::string& field,
+ const size_t fieldIndex) {
+ for (size_t i = 0; i < topLevelDeps.size(); i++) {
+ if (i != fieldIndex && topLevelDeps[i].count(field) > 0) {
+ return true;
+ }
+ }
+ return false;
+}
} // namespace
std::pair<BSONObj, bool> InclusionNode::extractComputedProjectionsInProject(
@@ -109,8 +153,8 @@ std::pair<BSONObj, bool> InclusionNode::extractComputedProjectionsInProject(
return {BSONObj{}, false};
}
- DepsTracker allDeps;
- reportDependencies(&allDeps);
+ std::vector<OrderedPathSet> topLevelDeps =
+ getTopLevelDeps(_orderToProcessAdditionsAndChildren, _expressions, _children);
// Auxiliary vector with extracted computed projections: <name, expression, replacement
// strategy>. If the replacement strategy flag is true, the expression is replaced with a
@@ -118,19 +162,15 @@ std::pair<BSONObj, bool> InclusionNode::extractComputedProjectionsInProject(
std::vector<std::tuple<StringData, boost::intrusive_ptr<Expression>, bool>>
addFieldsExpressions;
bool replaceWithProjField = true;
- for (auto&& field : _orderToProcessAdditionsAndChildren) {
+ for (size_t i = 0; i < _orderToProcessAdditionsAndChildren.size(); i++) {
+ auto&& field = _orderToProcessAdditionsAndChildren[i];
+
if (reservedNames.count(field) > 0) {
// Do not pushdown computed projection with reserved name.
replaceWithProjField = false;
continue;
}
- if (allDeps.fields.count(field) > 0) {
- // Do not extract a computed projection if its name is the same as a dependent field. If
- // the extracted $addFields were to be placed before this projection, the dependency
- // with the common name would be shadowed by the computed projection.
- replaceWithProjField = false;
- continue;
- }
+
auto expressionIt = _expressions.find(field);
if (expressionIt == _expressions.end()) {
// After seeing the first dotted path expression we need to replace computed
@@ -138,13 +178,17 @@ std::pair<BSONObj, bool> InclusionNode::extractComputedProjectionsInProject(
replaceWithProjField = false;
continue;
}
- DepsTracker deps;
- expressionIt->second->addDependencies(&deps);
- auto topLevelFieldNames =
- deps.toProjectionWithoutMetadata(DepsTracker::TruncateToRootLevel::yes)
- .getFieldNames<std::set<std::string>>();
- topLevelFieldNames.erase("_id");
+ // Do not extract a computed projection if it is computing a value that other fields in the
+ // same projection depend on. If the extracted $addFields were to be placed before this
+ // projection, the dependency with the common name would be shadowed by the computed
+ // projection.
+ if (computedExprDependsOnField(topLevelDeps, field, i)) {
+ replaceWithProjField = false;
+ continue;
+ }
+
+ const auto& topLevelFieldNames = topLevelDeps[i];
if (topLevelFieldNames.size() == 1 && topLevelFieldNames.count(oldName.toString()) == 1) {
// Substitute newName for oldName in the expression.
StringMap<std::string> renames;
@@ -197,35 +241,34 @@ std::pair<BSONObj, bool> InclusionNode::extractComputedProjectionsInAddFields(
return {BSONObj{}, false};
}
- DepsTracker allDeps;
- reportDependencies(&allDeps);
+ std::vector<OrderedPathSet> topLevelDeps =
+ getTopLevelDeps(_orderToProcessAdditionsAndChildren, _expressions, _children);
// Auxiliary vector with extracted computed projections: <name, expression>.
// To preserve the original fields order, only projections at the beginning of the
// _orderToProcessAdditionsAndChildren list can be extracted for pushdown.
std::vector<std::pair<StringData, boost::intrusive_ptr<Expression>>> addFieldsExpressions;
- for (auto&& field : _orderToProcessAdditionsAndChildren) {
+ for (size_t i = 0; i < _orderToProcessAdditionsAndChildren.size(); i++) {
+ auto&& field = _orderToProcessAdditionsAndChildren[i];
// Do not extract for pushdown computed projection with reserved name.
if (reservedNames.count(field) > 0) {
break;
}
- if (allDeps.fields.count(field) > 0) {
- // Do not extract a computed projection if its name is the same as a dependent field. If
- // the extracted $addFields were to be placed before this $addFields, the dependency
- // with the common name would be shadowed by the computed projection.
- break;
- }
+
auto expressionIt = _expressions.find(field);
if (expressionIt == _expressions.end()) {
break;
}
- DepsTracker deps;
- expressionIt->second->addDependencies(&deps);
- auto topLevelFieldNames =
- deps.toProjectionWithoutMetadata(DepsTracker::TruncateToRootLevel::yes)
- .getFieldNames<std::set<std::string>>();
- topLevelFieldNames.erase("_id");
+ // Do not extract a computed projection if it is computing a value that other fields in the
+ // same projection depend on. If the extracted $addFields were to be placed before this
+ // projection, the dependency with the common name would be shadowed by the computed
+ // projection.
+ if (computedExprDependsOnField(topLevelDeps, field, i)) {
+ break;
+ }
+
+ auto& topLevelFieldNames = topLevelDeps[i];
if (topLevelFieldNames.size() == 1 && topLevelFieldNames.count(oldName.toString()) == 1) {
// Substitute newName for oldName in the expression.
StringMap<std::string> renames;