summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKatherine Wu <katherine.wu@mongodb.com>2021-06-10 13:49:19 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-06-14 21:38:33 +0000
commit19572bcc17bf07c391a2248e0dd52bc08d207fcd (patch)
tree61dbdf1ac44a2b3be8657b29b2709ebc738faabb
parent431106074ec36920816a002296f10d44fdf9d971 (diff)
downloadmongo-19572bcc17bf07c391a2248e0dd52bc08d207fcd.tar.gz
SERVER-57403 Serialize 'let' variables by wrapping with $literal
-rw-r--r--jstests/core/command_let_variables.js7
-rw-r--r--jstests/noPassthroughWithMongod/command_let_variables_merge_only.js33
-rw-r--r--src/mongo/db/pipeline/variables.cpp4
3 files changed, 41 insertions, 3 deletions
diff --git a/jstests/core/command_let_variables.js b/jstests/core/command_let_variables.js
index 208a0801530..6a3de698291 100644
--- a/jstests/core/command_let_variables.js
+++ b/jstests/core/command_let_variables.js
@@ -536,4 +536,11 @@ assert.between(0, result, 1);
const deduped = [...new Set(values)];
assert.eq(1, deduped.length, `Expected all identical values: ${deduped}`);
}
+
+// Test that expressions wrapped with $literal are serialized correctly when run in sharded cluster
+// environments.
+result = coll.aggregate([{$match: {$expr: {$eq: ["$_id", 2]}}}, {$project: {a: "$$b"}}],
+ {let : {b: {$literal: "$notAFieldPath"}}})
+ .toArray();
+assert.eq(result, [{_id: 2, a: "$notAFieldPath"}]);
}());
diff --git a/jstests/noPassthroughWithMongod/command_let_variables_merge_only.js b/jstests/noPassthroughWithMongod/command_let_variables_merge_only.js
index b6d5ceddbbf..ab913e01888 100644
--- a/jstests/noPassthroughWithMongod/command_let_variables_merge_only.js
+++ b/jstests/noPassthroughWithMongod/command_let_variables_merge_only.js
@@ -1,7 +1,7 @@
// Tests that the aggregate command can use command-level let variables with $merge. Note $merge
// tests must be run in a noPassthrough suite so the other operators are exercised in
// jstests/core/command_let_variables.js.
-// @tags: [assumes_against_mongod_not_mongos, requires_fcv46]
+// @tags: [requires_fcv46]
(function() {
"use strict";
@@ -125,4 +125,35 @@ assert.commandWorked(db.runCommand({
let : {variable: "OUTER"}
}));
assert.eq(targetColl.aggregate({$match: {$expr: {$eq: ["$var", "INNER"]}}}).toArray().length, 1);
+
+// Test that expressions wrapped with $literal are serialized correctly in combination with
+// pipelines containing $merge.
+prepMergeTargetColl();
+assert.commandWorked(db.runCommand({
+ aggregate: coll.getName(),
+ pipeline: [{$project: {var : "$$variable"}}, {$merge: {into: targetColl.getName()}}],
+ let : {variable: {$literal: "$notAFieldPath"}},
+ cursor: {}
+}));
+assert.eq(targetColl.aggregate({$match: {$expr: {$eq: ["$var", {$literal: "$notAFieldPath"}]}}})
+ .toArray()
+ .length,
+ 4);
+
+prepMergeTargetColl();
+assert.commandWorked(db.runCommand({
+ aggregate: coll.getName(),
+ pipeline: [{
+ $merge: {
+ into: targetColl.getName(),
+ let : {variable: {$literal: "$notAFieldPath"}},
+ whenMatched: [{$addFields: {"var": "$$variable"}}]
+ }
+ }],
+ cursor: {},
+}));
+assert.eq(targetColl.aggregate({$match: {$expr: {$eq: ["$var", {$literal: "$notAFieldPath"}]}}})
+ .toArray()
+ .length,
+ 1);
}());
diff --git a/src/mongo/db/pipeline/variables.cpp b/src/mongo/db/pipeline/variables.cpp
index 8cabf0f3c52..6adc0e280c1 100644
--- a/src/mongo/db/pipeline/variables.cpp
+++ b/src/mongo/db/pipeline/variables.cpp
@@ -350,7 +350,7 @@ BSONObj VariablesParseState::serialize(const Variables& vars) const {
auto bob = BSONObjBuilder{};
for (auto&& [var_name, id] : _variables)
if (vars.hasValue(id))
- bob << var_name << vars.getValue(id);
+ bob << var_name << Value(DOC("$literal" << vars.getValue(id)));
// System variables have to be added separately since the variable IDs are reserved and not
// allocated like normal variables, and so not present in '_variables'.
@@ -363,7 +363,7 @@ std::pair<LegacyRuntimeConstants, BSONObj> VariablesParseState::transitionalComp
auto bob = BSONObjBuilder{};
for (auto&& [var_name, id] : _variables)
if (vars.hasValue(id))
- bob << var_name << vars.getValue(id);
+ bob << var_name << Value(DOC("$literal" << vars.getValue(id)));
return {vars.transitionalExtractRuntimeConstants(), bob.obj()};
}