summaryrefslogtreecommitdiff
path: root/src/mongo/db/pipeline/expression.cpp
diff options
context:
space:
mode:
authorTed Tuckman <ted.tuckman@mongodb.com>2023-02-17 14:26:17 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-17 16:46:55 +0000
commit5d2a08e7736eadfe1510955d4f990030c6a0196a (patch)
tree32920c7054dc7ddf1dbda80dcdfc4fa8966feb1f /src/mongo/db/pipeline/expression.cpp
parent56b8b371ceca0f51d8e030885e3b6ee63603d64d (diff)
downloadmongo-5d2a08e7736eadfe1510955d4f990030c6a0196a.tar.gz
SERVER-73320 Implement literal and field name redaction for ExpressionGetField, ExpressionSetField, ExpressionObject, and ExpressionArray
Diffstat (limited to 'src/mongo/db/pipeline/expression.cpp')
-rw-r--r--src/mongo/db/pipeline/expression.cpp123
1 files changed, 90 insertions, 33 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 8981a1c6731..386ec63f53e 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -640,6 +640,9 @@ Value ExpressionArray::evaluate(const Document& root, Variables* variables) cons
}
Value ExpressionArray::serialize(SerializationOptions options) const {
+ if (options.replacementForLiteralArgs && selfAndChildrenAreConstant()) {
+ return serializeConstant(Value(options.replacementForLiteralArgs.get()));
+ }
vector<Value> expressions;
expressions.reserve(_children.size());
for (auto&& expr : _children) {
@@ -666,6 +669,15 @@ intrusive_ptr<Expression> ExpressionArray::optimize() {
return this;
}
+bool ExpressionArray::selfAndChildrenAreConstant() const {
+ for (auto&& exprPointer : _children) {
+ if (!exprPointer->selfAndChildrenAreConstant()) {
+ return false;
+ }
+ }
+ return true;
+}
+
const char* ExpressionArray::getOpName() const {
// This should never be called, but is needed to inherit from ExpressionNary.
return "$array";
@@ -1213,7 +1225,7 @@ Value ExpressionConstant::evaluate(const Document& root, Variables* variables) c
Value ExpressionConstant::serialize(SerializationOptions options) const {
if (options.replacementForLiteralArgs) {
- return Value(DOC("$const" << *options.replacementForLiteralArgs));
+ return serializeConstant(Value(options.replacementForLiteralArgs.get()));
}
return serializeConstant(_value);
}
@@ -2368,10 +2380,27 @@ Value ExpressionObject::evaluate(const Document& root, Variables* variables) con
return outputDoc.freezeToValue();
}
+bool ExpressionObject::selfAndChildrenAreConstant() const {
+ for (auto&& [_, exprPointer] : _expressions) {
+ if (!exprPointer->selfAndChildrenAreConstant()) {
+ return false;
+ }
+ }
+ return true;
+}
+
Value ExpressionObject::serialize(SerializationOptions options) const {
+ if (options.replacementForLiteralArgs && selfAndChildrenAreConstant()) {
+ return serializeConstant(Value(options.replacementForLiteralArgs.get()));
+ }
MutableDocument outputDoc;
for (auto&& pair : _expressions) {
- outputDoc.addField(pair.first, pair.second->serialize(options));
+ if (options.redactFieldNames) {
+ outputDoc.addField(options.redactFieldNamesStrategy(pair.first),
+ pair.second->serialize(options));
+ } else {
+ outputDoc.addField(pair.first, pair.second->serialize(options));
+ }
}
return outputDoc.freezeToValue();
}
@@ -2534,32 +2563,40 @@ Value ExpressionFieldPath::evaluate(const Document& root, Variables* variables)
}
}
-Value ExpressionFieldPath::serialize(SerializationOptions options) const {
- auto&& [prefix, path] = [&]() {
- if (_fieldPath.getFieldName(0) == "CURRENT" && _fieldPath.getPathLength() > 1) {
- // use short form for "$$CURRENT.foo" but not just "$$CURRENT"
- return std::make_pair(std::string("$"), _fieldPath.tail());
- } else {
- return std::make_pair(std::string("$$"), _fieldPath);
+namespace {
+// Shared among expressions that need to serialize dotted paths and redact the path components.
+auto getPrefixAndPath(FieldPath path) {
+ if (path.getFieldName(0) == "CURRENT" && path.getPathLength() > 1) {
+ // use short form for "$$CURRENT.foo" but not just "$$CURRENT"
+ return std::make_pair(std::string("$"), path.tail());
+ } else {
+ return std::make_pair(std::string("$$"), path);
+ }
+}
+std::string hashFieldPath(SerializationOptions options, std::string prefix, FieldPath path) {
+ std::stringstream redacted;
+ redacted << prefix;
+ size_t startPos = 0;
+ // Check if our prefix indicates this path begins with a system variable.
+ if (prefix.length() == 2) {
+ // Don't redact a variable reference.
+ redacted << path.getFieldName(0);
+ ++startPos;
+ }
+ for (size_t i = startPos; i < path.getPathLength(); ++i) {
+ if (i > 0) {
+ redacted << ".";
}
- }();
+ redacted << options.redactFieldNamesStrategy(path.getFieldName(i));
+ }
+ return redacted.str();
+}
+} // namespace
+
+Value ExpressionFieldPath::serialize(SerializationOptions options) const {
+ auto [prefix, path] = getPrefixAndPath(_fieldPath);
if (options.redactFieldNames) {
- std::stringstream redacted;
- redacted << prefix;
- size_t startPos = 0;
- // Check if our prefix indicates this path begins with a system variable.
- if (prefix.length() == 2) {
- // Don't redact a variable reference.
- redacted << path.getFieldName(0);
- ++startPos;
- }
- for (size_t i = startPos; i < path.getPathLength(); ++i) {
- if (i > 0) {
- redacted << ".";
- }
- redacted << options.redactFieldNamesStrategy(path.getFieldName(i));
- }
- return Value(redacted.str());
+ return Value(hashFieldPath(options, prefix, path));
} else {
return Value(prefix + path.fullPath());
}
@@ -7974,9 +8011,19 @@ intrusive_ptr<Expression> ExpressionGetField::optimize() {
}
Value ExpressionGetField::serialize(SerializationOptions options) const {
- return Value(Document{{"$getField"_sd,
- Document{{"field"_sd, _children[_kField]->serialize(options)},
- {"input"_sd, _children[_kInput]->serialize(options)}}}});
+ MutableDocument argDoc;
+ if (options.redactFieldNames) {
+ // The parser guarantees that the '_children[_kField]' expression evaluates to a constant
+ // string.
+ auto strPath =
+ static_cast<ExpressionConstant*>(_children[_kField].get())->getValue().getString();
+ argDoc.addField("field"_sd, Value(hashFieldPath(options, {""}, FieldPath(strPath))));
+ } else {
+ argDoc.addField("field"_sd, _children[_kField]->serialize(options));
+ }
+ argDoc.addField("input"_sd, _children[_kInput]->serialize(options));
+
+ return Value(Document{{"$getField"_sd, argDoc.freezeToValue()}});
}
/* -------------------------- ExpressionSetField ------------------------------ */
@@ -8087,10 +8134,20 @@ intrusive_ptr<Expression> ExpressionSetField::optimize() {
}
Value ExpressionSetField::serialize(SerializationOptions options) const {
- return Value(Document{{"$setField"_sd,
- Document{{"field"_sd, _children[_kField]->serialize(options)},
- {"input"_sd, _children[_kInput]->serialize(options)},
- {"value"_sd, _children[_kValue]->serialize(options)}}}});
+ MutableDocument argDoc;
+ if (options.redactFieldNames) {
+ // The parser guarantees that the '_children[_kField]' expression evaluates to a constant
+ // string.
+ auto strPath =
+ static_cast<ExpressionConstant*>(_children[_kField].get())->getValue().getString();
+ argDoc.addField("field"_sd, Value(hashFieldPath(options, {""}, FieldPath(strPath))));
+ } else {
+ argDoc.addField("field"_sd, _children[_kField]->serialize(options));
+ }
+ argDoc.addField("input"_sd, _children[_kInput]->serialize(options));
+ argDoc.addField("value"_sd, _children[_kValue]->serialize(options));
+
+ return Value(Document{{"$setField"_sd, argDoc.freezeToValue()}});
}
/* ------------------------- ExpressionTsSecond ----------------------------- */