summaryrefslogtreecommitdiff
path: root/src/mongo/db/exec/sbe/vm/vm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/exec/sbe/vm/vm.cpp')
-rw-r--r--src/mongo/db/exec/sbe/vm/vm.cpp183
1 files changed, 138 insertions, 45 deletions
diff --git a/src/mongo/db/exec/sbe/vm/vm.cpp b/src/mongo/db/exec/sbe/vm/vm.cpp
index 4e533214f97..97276df8c71 100644
--- a/src/mongo/db/exec/sbe/vm/vm.cpp
+++ b/src/mongo/db/exec/sbe/vm/vm.cpp
@@ -5315,88 +5315,181 @@ FastTuple<bool, value::TypeTags, value::Value> ByteCode::builtinSortKeyComponent
}
}
-std::pair<value::TypeTags, value::Value> ByteCode::produceBsonObject(const MakeObjSpec* mos,
+std::pair<value::TypeTags, value::Value> ByteCode::produceBsonObject(const MakeObjSpec* spec,
value::TypeTags rootTag,
value::Value rootVal,
- const size_t startIdx) {
- auto& fieldBehavior = mos->fieldBehavior;
- auto& fieldsAndProjects = mos->fieldsAndProjects;
- auto numFields = mos->numFields;
+ int stackOffset) {
+ auto& fieldNames = spec->fieldNames;
+
+ const bool isInclusion = spec->fieldBehavior == MakeObjSpec::FieldBehavior::keep;
+ const size_t numFields = fieldNames.size();
+ const size_t numKeepOrDrops = spec->numKeepOrDrops;
+ const size_t numComputedFields = numFields - numKeepOrDrops;
+
+ // The "visited" array keeps track of which computed fields have been visited so far so that
+ // later we can append the non-visited computed fields to the end of the object.
+ char* visited = nullptr;
+ char localVisitedArr[64];
+ std::unique_ptr<char[]> allocatedVisitedArr;
+ if (MONGO_unlikely(numComputedFields > 64)) {
+ allocatedVisitedArr = std::make_unique<char[]>(numComputedFields);
+ visited = allocatedVisitedArr.get();
+ } else {
+ visited = &localVisitedArr[0];
+ }
- const bool isInclusion = fieldBehavior == MakeObjSpec::FieldBehavior::keep;
+ memset(visited, 0, numComputedFields);
UniqueBSONObjBuilder bob;
- if (value::isObject(rootTag)) {
- size_t nFieldsIfInclusion = numFields;
+ size_t numFieldsRemaining = numFields;
+ size_t numComputedFieldsRemaining = numComputedFields;
+
+ const size_t numFieldsRemainingThreshold = isInclusion ? 1 : 0;
+ if (value::isObject(rootTag)) {
if (rootTag == value::TypeTags::bsonObject) {
- if (!(nFieldsIfInclusion == 0 && isInclusion)) {
- auto be = value::bitcastTo<const char*>(rootVal);
- const auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
+ auto be = value::bitcastTo<const char*>(rootVal);
+ const auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
- // Skip document length.
- be += 4;
+ // Skip document length.
+ be += 4;
+
+ // Let N = the # of "computed" fields, and let K = (isInclusion && N > 0 ? N-1 : N).
+ //
+ // When we have seen all of the "keepOrDrop" fields and when we have seen K of the
+ // "computed" fields, we can break out of this loop, ignore or copy the remaining
+ // fields from 'rootVal' (depending on whether 'isInclusion' is true or false), and
+ // then finally append the remaining computed field (if there is one) to the output
+ // object.
+ //
+ // (When isInclusion == true and a single "computed" field remains, it's okay to stop
+ // scanning 'rootVal' and append the remaining computed field to the end of the output
+ // object because it will have no observable effect on field order.)
+ if (numFieldsRemaining > numFieldsRemainingThreshold ||
+ numFieldsRemaining != numComputedFieldsRemaining) {
while (be != end - 1) {
auto sv = bson::fieldNameAndLength(be);
auto nextBe = bson::advance(be, sv.size());
+ size_t pos = fieldNames.findPos(sv);
- bool isProjectedOrRestricted;
- size_t pos = fieldsAndProjects.findPos(sv);
if (pos == IndexedStringVector::npos) {
- isProjectedOrRestricted = isInclusion;
- } else if (pos < numFields) {
- isProjectedOrRestricted = !isInclusion;
+ if (!isInclusion) {
+ bob.append(BSONElement(be, sv.size() + 1, nextBe - be));
+ }
+ } else if (pos < numKeepOrDrops) {
+ --numFieldsRemaining;
+
+ if (isInclusion) {
+ bob.append(BSONElement(be, sv.size() + 1, nextBe - be));
+ }
} else {
- isProjectedOrRestricted = true;
- }
+ --numFieldsRemaining;
+ --numComputedFieldsRemaining;
+
+ auto projectIdx = pos - numKeepOrDrops;
+ visited[projectIdx] = 1;
- if (!isProjectedOrRestricted) {
- bob.append(BSONElement(be, sv.size() + 1, nextBe - be));
- --nFieldsIfInclusion;
+ size_t argIdx = stackOffset + projectIdx;
+ auto [_, tag, val] = getFromStack(argIdx);
+ bson::appendValueToBsonObj(bob, fieldNames[pos], tag, val);
}
- if (nFieldsIfInclusion == 0 && isInclusion) {
+ be = nextBe;
+
+ if (numFieldsRemaining <= numFieldsRemainingThreshold &&
+ numFieldsRemaining == numComputedFieldsRemaining) {
break;
}
+ }
+ }
+
+ // If isInclusion == false and 'be' has not reached the end of 'rootVal', copy over
+ // the remaining fields from 'rootVal' to the output object.
+ if (!isInclusion) {
+ while (be != end - 1) {
+ auto sv = bson::fieldNameAndLength(be);
+ auto nextBe = bson::advance(be, sv.size());
+ bob.append(BSONElement(be, sv.size() + 1, nextBe - be));
be = nextBe;
}
}
} else if (rootTag == value::TypeTags::Object) {
- if (!(nFieldsIfInclusion == 0 && isInclusion)) {
- auto objRoot = value::getObjectView(rootVal);
- for (size_t idx = 0; idx < objRoot->size(); ++idx) {
+ auto objRoot = value::getObjectView(rootVal);
+ size_t idx = 0;
+
+ // Let N = number of "computed" fields, and let K = (isInclusion && N > 0 ? N-1 : N).
+ //
+ // When we have seen all of the "keepOrDrop" fields and when we have seen K of the
+ // "computed" fields, we can break out of this loop, ignore or copy the remaining
+ // fields from 'rootVal' (depending on whether 'isInclusion' is true or false), and
+ // then finally append the remaining computed field (if there is one) to the output
+ // object.
+ //
+ // (When isInclusion == true and a single "computed" field remains, it's okay to stop
+ // scanning 'rootVal' and append the remaining computed field to the end of the output
+ // object because it will have no observable effect on field order.)
+ if (numFieldsRemaining > numFieldsRemainingThreshold ||
+ numFieldsRemaining != numComputedFieldsRemaining) {
+ for (; idx < objRoot->size(); ++idx) {
auto sv = StringData(objRoot->field(idx));
+ size_t pos = fieldNames.findPos(sv);
- bool isProjectedOrRestricted;
- size_t pos = fieldsAndProjects.findPos(sv);
if (pos == IndexedStringVector::npos) {
- isProjectedOrRestricted = isInclusion;
- } else if (pos < numFields) {
- isProjectedOrRestricted = !isInclusion;
+ if (!isInclusion) {
+ auto [tag, val] = objRoot->getAt(idx);
+ bson::appendValueToBsonObj(bob, objRoot->field(idx), tag, val);
+ }
+ } else if (pos < numKeepOrDrops) {
+ --numFieldsRemaining;
+
+ if (isInclusion) {
+ auto [tag, val] = objRoot->getAt(idx);
+ bson::appendValueToBsonObj(bob, objRoot->field(idx), tag, val);
+ }
} else {
- isProjectedOrRestricted = true;
- }
+ --numFieldsRemaining;
+ --numComputedFieldsRemaining;
- if (!isProjectedOrRestricted) {
- auto [tag, val] = objRoot->getAt(idx);
- bson::appendValueToBsonObj(bob, objRoot->field(idx), tag, val);
- --nFieldsIfInclusion;
+ auto projectIdx = pos - numKeepOrDrops;
+ visited[projectIdx] = 1;
+
+ size_t argIdx = stackOffset + projectIdx;
+ auto [_, tag, val] = getFromStack(argIdx);
+ bson::appendValueToBsonObj(bob, fieldNames[pos], tag, val);
}
- if (nFieldsIfInclusion == 0 && isInclusion) {
+ if (numFieldsRemaining <= numFieldsRemainingThreshold &&
+ numFieldsRemaining == numComputedFieldsRemaining) {
+ ++idx;
break;
}
}
}
+
+ // If isInclusion == false and 'be' has not reached the end of 'rootVal', copy over
+ // the remaining fields from 'rootVal' to the output object.
+ if (!isInclusion) {
+ for (; idx < objRoot->size(); ++idx) {
+ auto sv = StringData(objRoot->field(idx));
+ auto [fieldTag, fieldVal] = objRoot->getAt(idx);
+ bson::appendValueToBsonObj(bob, sv, fieldTag, fieldVal);
+ }
+ }
}
}
- for (size_t idx = numFields; idx < fieldsAndProjects.size(); ++idx) {
- auto argIdx = startIdx + (idx - numFields);
- auto [_, tag, val] = getFromStack(argIdx);
- bson::appendValueToBsonObj(bob, fieldsAndProjects[idx], tag, val);
+ // Append the remaining computed fields (if any) to the output object.
+ if (numComputedFieldsRemaining > 0) {
+ for (size_t pos = numKeepOrDrops; pos < fieldNames.size(); ++pos) {
+ auto projectIdx = pos - numKeepOrDrops;
+ if (!visited[projectIdx]) {
+ size_t argIdx = stackOffset + projectIdx;
+ auto [_, tag, val] = getFromStack(argIdx);
+ bson::appendValueToBsonObj(bob, fieldNames[pos], tag, val);
+ }
+ }
}
bob.doneFast();
@@ -5418,9 +5511,9 @@ FastTuple<bool, value::TypeTags, value::Value> ByteCode::builtinMakeBsonObj(Arit
auto mos = value::getMakeObjSpecView(mosVal);
- const size_t startIdx = 2;
+ const int stackOffset = 2;
- auto [tag, val] = produceBsonObject(mos, objTag, objVal, startIdx);
+ auto [tag, val] = produceBsonObject(mos, objTag, objVal, stackOffset);
return {true, tag, val};
}