summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArun Banala <arun.banala@mongodb.com>2019-06-12 11:25:59 +0100
committerArun Banala <arun.banala@mongodb.com>2019-06-13 19:17:14 +0100
commit1e02fdb576d8167562587cd18c5c7132e9675639 (patch)
tree33cd8a039e66203c6e139e42fe77717d8dc7a1bd
parent6329d3f5cbfcea79ec9d49a7e0123890b7fd040e (diff)
downloadmongo-1e02fdb576d8167562587cd18c5c7132e9675639.tar.gz
SERVER-41065 Make agg evaluate() thread safe by passing 'Variables' as a parameter
(cherry picked from commit ab9d3aaad1cb9ad42063c1291ea07e321260a3d1)
-rw-r--r--buildscripts/resmokeconfig/suites/aggregation_sharded_collections_passthrough.yml3
-rw-r--r--jstests/aggregation/bugs/server9840.js99
-rw-r--r--jstests/aggregation/expressions/let.js132
-rw-r--r--jstests/aggregation/sources/graphLookup/variables.js41
-rw-r--r--jstests/concurrency/fsm_all_sharded_with_stepdowns.js1
-rw-r--r--jstests/concurrency/fsm_all_sharded_with_stepdowns_and_balancer.js1
-rw-r--r--jstests/concurrency/fsm_workloads/schema_validator_with_expr_variables.js77
-rw-r--r--src/mongo/db/matcher/expression_expr.cpp7
-rw-r--r--src/mongo/db/pipeline/document_source_bucket_auto.cpp5
-rw-r--r--src/mongo/db/pipeline/document_source_graph_lookup.cpp2
-rw-r--r--src/mongo/db/pipeline/document_source_group.cpp14
-rw-r--r--src/mongo/db/pipeline/document_source_lookup.cpp2
-rw-r--r--src/mongo/db/pipeline/document_source_redact.cpp2
-rw-r--r--src/mongo/db/pipeline/document_source_replace_root.cpp2
-rw-r--r--src/mongo/db/pipeline/document_source_sort.cpp2
-rw-r--r--src/mongo/db/pipeline/expression.cpp422
-rw-r--r--src/mongo/db/pipeline/expression.h159
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp255
-rw-r--r--src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp6
-rw-r--r--src/mongo/db/pipeline/parsed_inclusion_projection.cpp5
20 files changed, 738 insertions, 499 deletions
diff --git a/buildscripts/resmokeconfig/suites/aggregation_sharded_collections_passthrough.yml b/buildscripts/resmokeconfig/suites/aggregation_sharded_collections_passthrough.yml
index 85b7a90db67..3a0969386a4 100644
--- a/buildscripts/resmokeconfig/suites/aggregation_sharded_collections_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/aggregation_sharded_collections_passthrough.yml
@@ -34,6 +34,9 @@ selector:
# Uses the profiler, which is not supported through mongos.
- jstests/aggregation/sources/lookup/profile_lookup.js
+ # TODO SERVER-32309: Enable once $lookup with pipeline supports sharded foreign collections.
+ - jstests/aggregation/sources/graphLookup/variables.js
+
exclude_with_any_tags:
# Tests tagged with the following will fail because they assume collections are not sharded.
- assumes_no_implicit_collection_creation_after_drop
diff --git a/jstests/aggregation/bugs/server9840.js b/jstests/aggregation/bugs/server9840.js
deleted file mode 100644
index 4316730370a..00000000000
--- a/jstests/aggregation/bugs/server9840.js
+++ /dev/null
@@ -1,99 +0,0 @@
-// SERVER-9840 variables in expressions and $let
-
-load('jstests/aggregation/extras/utils.js');
-var t = db.server9840;
-t.drop();
-
-function test(expression, expected) {
- t.drop();
- t.insert({zero: 0, one: 1, two: 2, three: 3, nested: {four: 4}});
-
- // Test in projection:
- var result = t.aggregate({$project: {_id: 0, res: expression}}).toArray();
- assert.eq(result, [{res: expected}]);
-
- // Test in group:
- var result = t.aggregate({$group: {_id: 0, res: {$sum: expression}}}).toArray();
- assert.eq(result, [{_id: 0, res: expected}]);
-}
-
-// basics
-test('$two', 2);
-test('$$CURRENT.two', 2);
-test('$$ROOT.two', 2);
-
-// using sub expressions
-test({$add: ['$two', '$$CURRENT.three']}, 5);
-test({$add: ['$$CURRENT.two', '$$ROOT.nested.four']}, 6);
-
-// $let simple
-test({$let: {vars: {a: 10}, in : '$$a'}}, 10);
-test({$let: {vars: {a: '$zero'}, in : '$$a'}}, 0);
-test({$let: {vars: {a: {$add: ['$one', '$two']}, b: 10}, in : {$multiply: ['$$a', '$$b']}}}, 30);
-
-// $let changing CURRENT
-test({$let: {vars: {CURRENT: '$$ROOT.nested'}, in : {$multiply: ['$four', '$$ROOT.two']}}}, 8);
-test({
- $let: {
- vars: {CURRENT: '$$CURRENT.nested'}, // using original value of CURRENT
- in : {$multiply: ['$four', '$$ROOT.two']}
- }
-},
- 8);
-test({
- $let: {
- vars: {CURRENT: '$nested'}, // same as last
- in : {$multiply: ['$four', '$$ROOT.two']}
- }
-},
- 8);
-test({
- $let: {
- vars: {CURRENT: {$const: {ten: 10}}}, // "artificial" object
- in : {$multiply: ['$ten', '$$ROOT.two']}
- }
-},
- 20);
-test({
- $let: {
- vars: {CURRENT: '$three'}, // sets current to the number 3 (not an object)
- in : {$multiply: ['$$CURRENT', '$$ROOT.two']}
- }
-},
- 6);
-
-// swapping with $let (ensures there is no ordering dependency in vars)
-test({
- $let: {
- vars: {x: 6, y: 10},
- in : {
- $let: {
- vars: {x: '$$y', y: '$$x'}, // now {x:10, y:6}
- in : {$subtract: ['$$x', '$$y']}
- }
- }
- }
-}, // not commutative!
- 4); // 10-6 not 6-10 or 6-6
-
-// unicode is allowed
-test({$let: {vars: {'日本語': 10}, in : '$$日本語'}}, 10); // Japanese for "Japanese language"
-
-// Can use ROOT and CURRENT directly with no subfield (SERVER-5916)
-t.drop();
-t.insert({_id: 'obj'});
-assert.eq(t.aggregate({$project: {_id: 0, obj: '$$ROOT'}}).toArray(), [{obj: {_id: 'obj'}}]);
-assert.eq(t.aggregate({$project: {_id: 0, obj: '$$CURRENT'}}).toArray(), [{obj: {_id: 'obj'}}]);
-assert.eq(t.aggregate({$group: {_id: 0, objs: {$push: '$$ROOT'}}}).toArray(),
- [{_id: 0, objs: [{_id: 'obj'}]}]);
-assert.eq(t.aggregate({$group: {_id: 0, objs: {$push: '$$CURRENT'}}}).toArray(),
- [{_id: 0, objs: [{_id: 'obj'}]}]);
-
-// check name validity checks
-assertErrorCode(t, {$project: {a: {$let: {vars: {ROOT: 1}, in : '$$ROOT'}}}}, 16867);
-assertErrorCode(t, {$project: {a: {$let: {vars: {FOO: 1}, in : '$$FOO'}}}}, 16867);
-assertErrorCode(t, {$project: {a: {$let: {vars: {_underbar: 1}, in : '$$FOO'}}}}, 16867);
-assertErrorCode(t, {$project: {a: {$let: {vars: {'a.b': 1}, in : '$$FOO'}}}}, 16868);
-assertErrorCode(t, {$project: {a: {$let: {vars: {'a b': 1}, in : '$$FOO'}}}}, 16868);
-assertErrorCode(t, {$project: {a: '$$_underbar'}}, 16870);
-assertErrorCode(t, {$project: {a: '$$with spaces'}}, 16871);
diff --git a/jstests/aggregation/expressions/let.js b/jstests/aggregation/expressions/let.js
new file mode 100644
index 00000000000..b4cfb7d3188
--- /dev/null
+++ b/jstests/aggregation/expressions/let.js
@@ -0,0 +1,132 @@
+/**
+ * Basic integration tests for the $let expression.
+ */
+(function() {
+ "use strict";
+
+ load("jstests/aggregation/extras/utils.js"); // For assertErrorCode.
+
+ let coll = db.agg_expr_let;
+ coll.drop();
+ assert.writeOK(coll.insert({zero: 0, one: 1, two: 2, three: 3, nested: {four: 4}}));
+
+ function testExpr(expression, output) {
+ const res = coll.aggregate([{$project: {output: expression}}]).toArray();
+ assert.eq(res.length, 1, tojson(res));
+ assert.eq(res[0].output, output, tojson(res));
+
+ // Test in group:
+ const result = coll.aggregate({$group: {_id: 0, res: {$sum: expression}}}).toArray();
+ assert.eq(result, [{_id: 0, res: output}]);
+ }
+
+ // Basic tests.
+ testExpr('$two', 2);
+ testExpr('$$CURRENT.two', 2);
+ testExpr('$$ROOT.two', 2);
+
+ // Using sub expressions.
+ testExpr({$add: ['$two', '$$CURRENT.three']}, 5);
+ testExpr({$add: ['$$CURRENT.two', '$$ROOT.nested.four']}, 6);
+
+ // Verify that the variables defined in $let work.
+ testExpr({$let: {vars: {a: 10}, in : '$$a'}}, 10);
+ testExpr({$let: {vars: {a: '$zero'}, in : '$$a'}}, 0);
+ testExpr({$let: {vars: {a: {$add: ['$one', '$two']}, b: 10}, in : {$multiply: ['$$a', '$$b']}}},
+ 30);
+
+ // Verify that the outer level variable works in inner level $let.
+ testExpr({
+ $let: {
+ vars: {var1: 1},
+ in : {$let: {vars: {var2: "$$var1"}, in : {$sum: ["$$var1", "$$var2"]}}}
+ }
+ },
+ 2);
+
+ // Verify that the outer level variables get overwritten by inner level variables.
+ testExpr({
+ $let: {
+ vars: {var1: "$one"},
+ in : {$let: {vars: {var2: "$$var1", var1: 3}, in : {$sum: ["$$var2", "$$var1"]}}}
+ }
+ },
+ 4);
+
+ // $let changing CURRENT
+ testExpr({$let: {vars: {CURRENT: '$$ROOT.nested'}, in : {$multiply: ['$four', '$$ROOT.two']}}},
+ 8);
+ testExpr({
+ $let: {
+ vars: {CURRENT: '$$CURRENT.nested'}, // using original value of CURRENT
+ in : {$multiply: ['$four', '$$ROOT.two']}
+ }
+ },
+ 8);
+ testExpr({
+ $let: {
+ vars: {CURRENT: '$nested'}, // same as last
+ in : {$multiply: ['$four', '$$ROOT.two']}
+ }
+ },
+ 8);
+ testExpr({
+ $let: {
+ vars: {CURRENT: {$const: {ten: 10}}}, // "artificial" object
+ in : {$multiply: ['$ten', '$$ROOT.two']}
+ }
+ },
+ 20);
+ testExpr({
+ $let: {
+ vars: {CURRENT: '$three'}, // sets current to the number 3 (not an object)
+ in : {$multiply: ['$$CURRENT', '$$ROOT.two']}
+ }
+ },
+ 6);
+
+ // Swapping with $let (ensures there is no ordering dependency in vars).
+ testExpr({
+ $let: {
+ vars: {x: 6, y: 10},
+ in : {
+ $let: {
+ vars: {x: '$$y', y: '$$x'}, // now {x:10, y:6}
+ in : {$subtract: ['$$x', '$$y']}
+ }
+ }
+ }
+ }, // Not commutative!
+ 4); // 10-6 not 6-10 or 6-6
+
+ // Unicode is allowed.
+ testExpr({$let: {vars: {'日本語': 10}, in : '$$日本語'}},
+ 10); // Japanese for "Japanese language".
+
+ // Can use ROOT and CURRENT directly with no subfield (SERVER-5916).
+ coll.drop();
+ coll.insert({_id: 'obj'});
+ assert.eq(coll.aggregate({$project: {_id: 0, obj: '$$ROOT'}}).toArray(), [{obj: {_id: 'obj'}}]);
+ assert.eq(coll.aggregate({$project: {_id: 0, obj: '$$CURRENT'}}).toArray(),
+ [{obj: {_id: 'obj'}}]);
+ assert.eq(coll.aggregate({$group: {_id: 0, objs: {$push: '$$ROOT'}}}).toArray(),
+ [{_id: 0, objs: [{_id: 'obj'}]}]);
+ assert.eq(coll.aggregate({$group: {_id: 0, objs: {$push: '$$CURRENT'}}}).toArray(),
+ [{_id: 0, objs: [{_id: 'obj'}]}]);
+
+ // Check name validity checks.
+ assertErrorCode(coll, {$project: {a: {$let: {vars: {ROOT: 1}, in : '$$ROOT'}}}}, 16867);
+ assertErrorCode(coll, {$project: {a: {$let: {vars: {FOO: 1}, in : '$$FOO'}}}}, 16867);
+ assertErrorCode(coll, {$project: {a: {$let: {vars: {_underbar: 1}, in : '$$FOO'}}}}, 16867);
+ assertErrorCode(coll, {$project: {a: {$let: {vars: {'a.b': 1}, in : '$$FOO'}}}}, 16868);
+ assertErrorCode(coll, {$project: {a: {$let: {vars: {'a b': 1}, in : '$$FOO'}}}}, 16868);
+ assertErrorCode(coll, {$project: {a: '$$_underbar'}}, 16870);
+ assertErrorCode(coll, {$project: {a: '$$with spaces'}}, 16871);
+
+ // Verify that variables defined in '$let' cannot be used to initialize other variables.
+ assertErrorCode(
+ coll,
+ [{$project: {output: {$let: {vars: {var1: "$one", var2: "$$var1"}, in : "$$var1"}}}}],
+ 17276);
+
+}());
diff --git a/jstests/aggregation/sources/graphLookup/variables.js b/jstests/aggregation/sources/graphLookup/variables.js
new file mode 100644
index 00000000000..87e2c8b3975
--- /dev/null
+++ b/jstests/aggregation/sources/graphLookup/variables.js
@@ -0,0 +1,41 @@
+/**
+ * Tests to verify that $graphLookup can use the variables defined in an outer scope.
+ */
+(function() {
+ "use strict";
+
+ let local = db.graph_lookup_var_local;
+ let foreign = db.graph_lookup_var_foreign;
+ local.drop();
+ foreign.drop();
+
+ foreign.insert({from: "b", to: "a", _id: 0});
+ local.insert({});
+
+ const basicGraphLookup = {
+ $graphLookup: {
+ from: "graph_lookup_var_foreign",
+ startWith: "$$var1",
+ connectFromField: "from",
+ connectToField: "to",
+ as: "resultsFromGraphLookup"
+ }
+ };
+
+ const lookup = {
+ $lookup: {
+ from: "graph_lookup_var_local",
+ let : {var1: "a"},
+ pipeline: [basicGraphLookup],
+ as: "resultsFromLookup"
+ }
+ };
+
+ // Verify that $graphLookup can use the variable 'var1' which is defined in parent $lookup.
+ let res = local.aggregate([lookup]).toArray();
+ assert.eq(res.length, 1);
+ assert.eq(res[0].resultsFromLookup.length, 1);
+ assert.eq(res[0].resultsFromLookup[0].resultsFromGraphLookup.length, 1);
+ assert.eq(res[0].resultsFromLookup[0].resultsFromGraphLookup[0], {_id: 0, from: "b", to: "a"});
+
+})();
diff --git a/jstests/concurrency/fsm_all_sharded_with_stepdowns.js b/jstests/concurrency/fsm_all_sharded_with_stepdowns.js
index af52a52123a..eb9a8a95727 100644
--- a/jstests/concurrency/fsm_all_sharded_with_stepdowns.js
+++ b/jstests/concurrency/fsm_all_sharded_with_stepdowns.js
@@ -133,6 +133,7 @@ var blacklist = [
// Use non retryable writes.
'remove_and_bulk_insert.js',
+ 'schema_validator_with_expr_variables.js',
'update_and_bulk_insert.js',
'update_check_index.js',
'update_multifield_isolated_multiupdate.js',
diff --git a/jstests/concurrency/fsm_all_sharded_with_stepdowns_and_balancer.js b/jstests/concurrency/fsm_all_sharded_with_stepdowns_and_balancer.js
index c67d0c85cce..25e7c25bfcc 100644
--- a/jstests/concurrency/fsm_all_sharded_with_stepdowns_and_balancer.js
+++ b/jstests/concurrency/fsm_all_sharded_with_stepdowns_and_balancer.js
@@ -139,6 +139,7 @@ var blacklist = [
// Use non retryable writes.
'remove_and_bulk_insert.js',
+ 'schema_validator_with_expr_variables.js',
'update_and_bulk_insert.js',
'update_check_index.js',
'update_multifield_isolated_multiupdate.js',
diff --git a/jstests/concurrency/fsm_workloads/schema_validator_with_expr_variables.js b/jstests/concurrency/fsm_workloads/schema_validator_with_expr_variables.js
new file mode 100644
index 00000000000..da39afdcf32
--- /dev/null
+++ b/jstests/concurrency/fsm_workloads/schema_validator_with_expr_variables.js
@@ -0,0 +1,77 @@
+/**
+ * Test to verify that the schema validator works correctly in a multi-threaded environment, when
+ * $expr uses expressions which mutate variable values while executing ($let, $map etc).
+ * @tags: [requires_non_retryable_writes]
+ */
+
+"use strict";
+
+var $config = (function() {
+ function setup(db, collName) {
+ for (let i = 0; i < 200; ++i) {
+ assertAlways.writeOK(
+ db[collName].insert({_id: i, a: i, one: 1, counter: 0, array: [0, i]}));
+ }
+
+ // Add a validator which checks that field 'a' has value 5 and sum of the elements in field
+ // 'array' is 5. The expression is purposefully complex so that it can create a stress on
+ // expressions with variables.
+ assertAlways.commandWorked(db.runCommand({
+ collMod: collName,
+ validator: {
+ $expr: {
+ $and: [
+ {
+ $eq: [
+ 5,
+ {
+ $let: {
+ vars: {item: {$multiply: ["$a", "$one"]}},
+ in : {$multiply: ["$$item", "$one"]}
+ }
+ }
+ ]
+ },
+ {
+ $eq: [
+ 5,
+ {
+ $sum: {
+ $map:
+ {"input": "$array", "as": "item", "in": "$$item"}
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }));
+ }
+
+ const states = {
+ applyValidator: function(db, collName) {
+ assertAlways.writeOK(db[collName].update({_id: 5}, {$inc: {counter: 1}}));
+ assertAlways.writeError(
+ db[collName].update({_id: 4}, {$set: {a: 4}, $inc: {counter: 1}}));
+
+ // Update all the documents in the collection.
+ assertAlways.writeOK(db[collName].update(
+ {}, {$set: {a: 5, array: [2, 3]}, $inc: {counter: 1}}, {multi: true}));
+
+ // Validation fails when elements of 'array' doesn't add up to 5.
+ assertAlways.writeError(db[collName].update({_id: 4}, {$set: {a: 5, array: [2, 2]}}));
+ }
+ };
+
+ let transitions = {applyValidator: {applyValidator: 1}};
+
+ return {
+ threadCount: 50,
+ iterations: 100,
+ states: states,
+ startState: "applyValidator",
+ transitions: transitions,
+ setup: setup
+ };
+})();
diff --git a/src/mongo/db/matcher/expression_expr.cpp b/src/mongo/db/matcher/expression_expr.cpp
index 9ba0567ac99..c4921cfb440 100644
--- a/src/mongo/db/matcher/expression_expr.cpp
+++ b/src/mongo/db/matcher/expression_expr.cpp
@@ -49,7 +49,12 @@ bool ExprMatchExpression::matches(const MatchableDocument* doc, MatchDetails* de
}
Document document(doc->toBSON());
- auto value = _expression->evaluate(document);
+
+ // 'Variables' is not thread safe, and ExprMatchExpression may be used in a validator which
+ // processes documents from multiple threads simultaneously. Hence we make a copy of the
+ // 'Variables' object per-caller.
+ Variables variables = _expCtx->variables;
+ auto value = _expression->evaluate(document, &variables);
return value.coerceToBool();
}
diff --git a/src/mongo/db/pipeline/document_source_bucket_auto.cpp b/src/mongo/db/pipeline/document_source_bucket_auto.cpp
index 9308328e7c6..1d347909881 100644
--- a/src/mongo/db/pipeline/document_source_bucket_auto.cpp
+++ b/src/mongo/db/pipeline/document_source_bucket_auto.cpp
@@ -158,7 +158,7 @@ Value DocumentSourceBucketAuto::extractKey(const Document& doc) {
return Value(BSONNULL);
}
- Value key = _groupByExpression->evaluate(doc);
+ Value key = _groupByExpression->evaluate(doc, &pExpCtx->variables);
if (_granularityRounder) {
uassert(40258,
@@ -191,7 +191,8 @@ void DocumentSourceBucketAuto::addDocumentToBucket(const pair<Value, Document>&
const size_t numAccumulators = _accumulatedFields.size();
for (size_t k = 0; k < numAccumulators; k++) {
- bucket._accums[k]->process(_accumulatedFields[k].expression->evaluate(entry.second), false);
+ bucket._accums[k]->process(
+ _accumulatedFields[k].expression->evaluate(entry.second, &pExpCtx->variables), false);
}
}
diff --git a/src/mongo/db/pipeline/document_source_graph_lookup.cpp b/src/mongo/db/pipeline/document_source_graph_lookup.cpp
index cac5e520df2..7b1d338f620 100644
--- a/src/mongo/db/pipeline/document_source_graph_lookup.cpp
+++ b/src/mongo/db/pipeline/document_source_graph_lookup.cpp
@@ -335,7 +335,7 @@ void DocumentSourceGraphLookUp::performSearch() {
// Make sure _input is set before calling performSearch().
invariant(_input);
- Value startingValue = _startWith->evaluate(*_input);
+ Value startingValue = _startWith->evaluate(*_input, &pExpCtx->variables);
// If _startWith evaluates to an array, treat each value as a separate starting point.
if (startingValue.isArray()) {
diff --git a/src/mongo/db/pipeline/document_source_group.cpp b/src/mongo/db/pipeline/document_source_group.cpp
index ac83f74d998..c2bb0d1f98d 100644
--- a/src/mongo/db/pipeline/document_source_group.cpp
+++ b/src/mongo/db/pipeline/document_source_group.cpp
@@ -164,7 +164,10 @@ DocumentSource::GetNextResult DocumentSourceGroup::getNextStreaming() {
// Add to the current accumulator(s).
for (size_t i = 0; i < _currentAccumulators.size(); i++) {
_currentAccumulators[i]->process(
- _accumulatedFields[i].expression->evaluate(*_firstDocOfNextGroup), _doingMerge);
+ _accumulatedFields[i].expression->evaluate(
+ *_firstDocOfNextGroup,
+ &(_accumulatedFields[i].expression->getExpressionContext()->variables)),
+ _doingMerge);
}
// Retrieve the next document.
@@ -555,8 +558,9 @@ DocumentSource::GetNextResult DocumentSourceGroup::initialize() {
dassert(numAccumulators == group.size());
for (size_t i = 0; i < numAccumulators; i++) {
- group[i]->process(_accumulatedFields[i].expression->evaluate(rootDocument),
- _doingMerge);
+ group[i]->process(
+ _accumulatedFields[i].expression->evaluate(rootDocument, &pExpCtx->variables),
+ _doingMerge);
_memoryUsageBytes += group[i]->memUsageForSorter();
}
@@ -814,7 +818,7 @@ BSONObjSet DocumentSourceGroup::getOutputSorts() {
Value DocumentSourceGroup::computeId(const Document& root) {
// If only one expression, return result directly
if (_idExpressions.size() == 1) {
- Value retValue = _idExpressions[0]->evaluate(root);
+ Value retValue = _idExpressions[0]->evaluate(root, &pExpCtx->variables);
return retValue.missing() ? Value(BSONNULL) : std::move(retValue);
}
@@ -822,7 +826,7 @@ Value DocumentSourceGroup::computeId(const Document& root) {
vector<Value> vals;
vals.reserve(_idExpressions.size());
for (size_t i = 0; i < _idExpressions.size(); i++) {
- vals.push_back(_idExpressions[i]->evaluate(root));
+ vals.push_back(_idExpressions[i]->evaluate(root, &pExpCtx->variables));
}
return Value(std::move(vals));
}
diff --git a/src/mongo/db/pipeline/document_source_lookup.cpp b/src/mongo/db/pipeline/document_source_lookup.cpp
index 04ca69ab9bc..444d61649ce 100644
--- a/src/mongo/db/pipeline/document_source_lookup.cpp
+++ b/src/mongo/db/pipeline/document_source_lookup.cpp
@@ -603,7 +603,7 @@ void DocumentSourceLookUp::resolveLetVariables(const Document& localDoc, Variabl
invariant(variables);
for (auto& letVar : _letVariables) {
- auto value = letVar.expression->evaluate(localDoc);
+ auto value = letVar.expression->evaluate(localDoc, &pExpCtx->variables);
variables->setConstantValue(letVar.id, value);
}
}
diff --git a/src/mongo/db/pipeline/document_source_redact.cpp b/src/mongo/db/pipeline/document_source_redact.cpp
index c887c59f5cc..41c05b025ea 100644
--- a/src/mongo/db/pipeline/document_source_redact.cpp
+++ b/src/mongo/db/pipeline/document_source_redact.cpp
@@ -132,7 +132,7 @@ Value DocumentSourceRedact::redactValue(const Value& in, const Document& root) {
boost::optional<Document> DocumentSourceRedact::redactObject(const Document& root) {
auto& variables = pExpCtx->variables;
- const Value expressionResult = _expression->evaluate(root);
+ const Value expressionResult = _expression->evaluate(root, &variables);
ValueComparator simpleValueCmp;
if (simpleValueCmp.evaluate(expressionResult == keepVal)) {
diff --git a/src/mongo/db/pipeline/document_source_replace_root.cpp b/src/mongo/db/pipeline/document_source_replace_root.cpp
index af2f46982d7..540c66c89a2 100644
--- a/src/mongo/db/pipeline/document_source_replace_root.cpp
+++ b/src/mongo/db/pipeline/document_source_replace_root.cpp
@@ -60,7 +60,7 @@ public:
Document applyTransformation(const Document& input) final {
// Extract subdocument in the form of a Value.
- Value newRoot = _newRoot->evaluate(input);
+ Value newRoot = _newRoot->evaluate(input, &_expCtx->variables);
// The newRoot expression, if it exists, must evaluate to an object.
uassert(40228,
diff --git a/src/mongo/db/pipeline/document_source_sort.cpp b/src/mongo/db/pipeline/document_source_sort.cpp
index 4f3821633c3..9dac5528de1 100644
--- a/src/mongo/db/pipeline/document_source_sort.cpp
+++ b/src/mongo/db/pipeline/document_source_sort.cpp
@@ -462,7 +462,7 @@ StatusWith<Value> DocumentSourceSort::extractKeyPart(const Document& doc,
plainKey = key.getValue();
} else {
invariant(patternPart.expression);
- plainKey = patternPart.expression->evaluate(doc);
+ plainKey = patternPart.expression->evaluate(doc, &pExpCtx->variables);
}
return getCollationComparisonKey(plainKey);
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index c800b1957ca..8acc6cb9c6a 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -241,7 +241,7 @@ const char* ExpressionAbs::getOpName() const {
/* ------------------------- ExpressionAdd ----------------------------- */
-Value ExpressionAdd::evaluate(const Document& root) const {
+Value ExpressionAdd::evaluate(const Document& root, Variables* variables) const {
// We'll try to return the narrowest possible result value while avoiding overflow, loss
// of precision due to intermediate rounding or implicit use of decimal types. To do that,
// compute a compensated sum for non-decimal values and a separate decimal sum for decimal
@@ -253,7 +253,7 @@ Value ExpressionAdd::evaluate(const Document& root) const {
const size_t n = vpOperand.size();
for (size_t i = 0; i < n; ++i) {
- Value val = vpOperand[i]->evaluate(root);
+ Value val = vpOperand[i]->evaluate(root, variables);
switch (val.getType()) {
case NumberDecimal:
@@ -323,8 +323,8 @@ const char* ExpressionAdd::getOpName() const {
/* ------------------------- ExpressionAllElementsTrue -------------------------- */
-Value ExpressionAllElementsTrue::evaluate(const Document& root) const {
- const Value arr = vpOperand[0]->evaluate(root);
+Value ExpressionAllElementsTrue::evaluate(const Document& root, Variables* variables) const {
+ const Value arr = vpOperand[0]->evaluate(root, variables);
uassert(17040,
str::stream() << getOpName() << "'s argument must be an array, but is "
<< typeName(arr.getType()),
@@ -401,10 +401,10 @@ intrusive_ptr<Expression> ExpressionAnd::optimize() {
return pE;
}
-Value ExpressionAnd::evaluate(const Document& root) const {
+Value ExpressionAnd::evaluate(const Document& root, Variables* variables) const {
const size_t n = vpOperand.size();
for (size_t i = 0; i < n; ++i) {
- Value pValue(vpOperand[i]->evaluate(root));
+ Value pValue(vpOperand[i]->evaluate(root, variables));
if (!pValue.coerceToBool())
return Value(false);
}
@@ -419,8 +419,8 @@ const char* ExpressionAnd::getOpName() const {
/* ------------------------- ExpressionAnyElementTrue -------------------------- */
-Value ExpressionAnyElementTrue::evaluate(const Document& root) const {
- const Value arr = vpOperand[0]->evaluate(root);
+Value ExpressionAnyElementTrue::evaluate(const Document& root, Variables* variables) const {
+ const Value arr = vpOperand[0]->evaluate(root, variables);
uassert(17041,
str::stream() << getOpName() << "'s argument must be an array, but is "
<< typeName(arr.getType()),
@@ -441,11 +441,11 @@ const char* ExpressionAnyElementTrue::getOpName() const {
/* ---------------------- ExpressionArray --------------------------- */
-Value ExpressionArray::evaluate(const Document& root) const {
+Value ExpressionArray::evaluate(const Document& root, Variables* variables) const {
vector<Value> values;
values.reserve(vpOperand.size());
for (auto&& expr : vpOperand) {
- Value elemVal = expr->evaluate(root);
+ Value elemVal = expr->evaluate(root, variables);
values.push_back(elemVal.missing() ? Value(BSONNULL) : std::move(elemVal));
}
return Value(std::move(values));
@@ -467,9 +467,9 @@ const char* ExpressionArray::getOpName() const {
/* ------------------------- ExpressionArrayElemAt -------------------------- */
-Value ExpressionArrayElemAt::evaluate(const Document& root) const {
- const Value array = vpOperand[0]->evaluate(root);
- const Value indexArg = vpOperand[1]->evaluate(root);
+Value ExpressionArrayElemAt::evaluate(const Document& root, Variables* variables) const {
+ const Value array = vpOperand[0]->evaluate(root, variables);
+ const Value indexArg = vpOperand[1]->evaluate(root, variables);
if (array.nullish() || indexArg.nullish()) {
return Value(BSONNULL);
@@ -509,8 +509,9 @@ const char* ExpressionArrayElemAt::getOpName() const {
/* ------------------------- ExpressionObjectToArray -------------------------- */
-Value ExpressionObjectToArray::evaluate(const Document& root) const {
- const Value targetVal = vpOperand[0]->evaluate(root);
+
+Value ExpressionObjectToArray::evaluate(const Document& root, Variables* variables) const {
+ const Value targetVal = vpOperand[0]->evaluate(root, variables);
if (targetVal.nullish()) {
return Value(BSONNULL);
@@ -541,8 +542,9 @@ const char* ExpressionObjectToArray::getOpName() const {
}
/* ------------------------- ExpressionArrayToObject -------------------------- */
-Value ExpressionArrayToObject::evaluate(const Document& root) const {
- const Value input = vpOperand[0]->evaluate(root);
+
+Value ExpressionArrayToObject::evaluate(const Document& root, Variables* variables) const {
+ const Value input = vpOperand[0]->evaluate(root, variables);
if (input.nullish()) {
return Value(BSONNULL);
}
@@ -690,8 +692,8 @@ void ExpressionCoerceToBool::_doAddDependencies(DepsTracker* deps) const {
pExpression->addDependencies(deps);
}
-Value ExpressionCoerceToBool::evaluate(const Document& root) const {
- Value pResult(pExpression->evaluate(root));
+Value ExpressionCoerceToBool::evaluate(const Document& root, Variables* variables) const {
+ Value pResult(pExpression->evaluate(root, variables));
bool b = pResult.coerceToBool();
if (b)
return Value(true);
@@ -792,9 +794,10 @@ static const CmpLookup cmpLookup[7] = {
};
}
-Value ExpressionCompare::evaluate(const Document& root) const {
- Value pLeft(vpOperand[0]->evaluate(root));
- Value pRight(vpOperand[1]->evaluate(root));
+
+Value ExpressionCompare::evaluate(const Document& root, Variables* variables) const {
+ Value pLeft(vpOperand[0]->evaluate(root, variables));
+ Value pRight(vpOperand[1]->evaluate(root, variables));
int cmp = getExpressionContext()->getValueComparator().compare(pLeft, pRight);
@@ -820,12 +823,12 @@ const char* ExpressionCompare::getOpName() const {
/* ------------------------- ExpressionConcat ----------------------------- */
-Value ExpressionConcat::evaluate(const Document& root) const {
+Value ExpressionConcat::evaluate(const Document& root, Variables* variables) const {
const size_t n = vpOperand.size();
StringBuilder result;
for (size_t i = 0; i < n; ++i) {
- Value val = vpOperand[i]->evaluate(root);
+ Value val = vpOperand[i]->evaluate(root, variables);
if (val.nullish())
return Value(BSONNULL);
@@ -846,12 +849,12 @@ const char* ExpressionConcat::getOpName() const {
/* ------------------------- ExpressionConcatArrays ----------------------------- */
-Value ExpressionConcatArrays::evaluate(const Document& root) const {
+Value ExpressionConcatArrays::evaluate(const Document& root, Variables* variables) const {
const size_t n = vpOperand.size();
vector<Value> values;
for (size_t i = 0; i < n; ++i) {
- Value val = vpOperand[i]->evaluate(root);
+ Value val = vpOperand[i]->evaluate(root, variables);
if (val.nullish()) {
return Value(BSONNULL);
}
@@ -874,10 +877,10 @@ const char* ExpressionConcatArrays::getOpName() const {
/* ----------------------- ExpressionCond ------------------------------ */
-Value ExpressionCond::evaluate(const Document& root) const {
- Value pCond(vpOperand[0]->evaluate(root));
+Value ExpressionCond::evaluate(const Document& root, Variables* variables) const {
+ Value pCond(vpOperand[0]->evaluate(root, variables));
int idx = pCond.coerceToBool() ? 1 : 2;
- return vpOperand[idx]->evaluate(root);
+ return vpOperand[idx]->evaluate(root, variables);
}
intrusive_ptr<Expression> ExpressionCond::parse(
@@ -948,7 +951,7 @@ void ExpressionConstant::_doAddDependencies(DepsTracker* deps) const {
/* nothing to do */
}
-Value ExpressionConstant::evaluate(const Document& root) const {
+Value ExpressionConstant::evaluate(const Document& root, Variables* variables) const {
return _value;
}
@@ -970,14 +973,15 @@ namespace {
boost::optional<TimeZone> makeTimeZone(const TimeZoneDatabase* tzdb,
const Document& root,
- const Expression* timeZone) {
+ const Expression* timeZone,
+ Variables* variables) {
invariant(tzdb);
if (!timeZone) {
return mongo::TimeZoneDatabase::utcZone();
}
- auto timeZoneId = timeZone->evaluate(root);
+ auto timeZoneId = timeZone->evaluate(root, variables);
if (timeZoneId.nullish()) {
return boost::none;
@@ -1148,8 +1152,10 @@ intrusive_ptr<Expression> ExpressionDateFromParts::optimize() {
_isoWeek,
_isoDayOfWeek,
_timeZone})) {
+
// Everything is a constant, so we can turn into a constant.
- return ExpressionConstant::create(getExpressionContext(), evaluate(Document{}));
+ return ExpressionConstant::create(
+ getExpressionContext(), evaluate(Document{}, &(getExpressionContext()->variables)));
}
return this;
@@ -1191,13 +1197,14 @@ bool ExpressionDateFromParts::evaluateNumberWithinRange(const Document& root,
int defaultValue,
int minValue,
int maxValue,
- int* returnValue) const {
+ int* returnValue,
+ Variables* variables) const {
if (!field) {
*returnValue = defaultValue;
return true;
}
- auto fieldValue = field->evaluate(root);
+ auto fieldValue = field->evaluate(root, variables);
if (fieldValue.nullish()) {
return false;
@@ -1224,18 +1231,21 @@ bool ExpressionDateFromParts::evaluateNumberWithinRange(const Document& root,
return true;
}
-Value ExpressionDateFromParts::evaluate(const Document& root) const {
+Value ExpressionDateFromParts::evaluate(const Document& root, Variables* variables) const {
int hour, minute, second, millisecond;
- if (!evaluateNumberWithinRange(root, _hour.get(), "hour"_sd, 0, 0, 24, &hour) ||
- !evaluateNumberWithinRange(root, _minute.get(), "minute"_sd, 0, 0, 59, &minute) ||
- !evaluateNumberWithinRange(root, _second.get(), "second"_sd, 0, 0, 59, &second) ||
+ if (!evaluateNumberWithinRange(root, _hour.get(), "hour"_sd, 0, 0, 24, &hour, variables) ||
+ !evaluateNumberWithinRange(
+ root, _minute.get(), "minute"_sd, 0, 0, 59, &minute, variables) ||
+ !evaluateNumberWithinRange(
+ root, _second.get(), "second"_sd, 0, 0, 59, &second, variables) ||
!evaluateNumberWithinRange(
- root, _millisecond.get(), "millisecond"_sd, 0, 0, 999, &millisecond)) {
+ root, _millisecond.get(), "millisecond"_sd, 0, 0, 999, &millisecond, variables)) {
return Value(BSONNULL);
}
- auto timeZone = makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get());
+ auto timeZone =
+ makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get(), variables);
if (!timeZone) {
return Value(BSONNULL);
@@ -1244,9 +1254,11 @@ Value ExpressionDateFromParts::evaluate(const Document& root) const {
if (_year) {
int year, month, day;
- if (!evaluateNumberWithinRange(root, _year.get(), "year"_sd, 1970, 0, 9999, &year) ||
- !evaluateNumberWithinRange(root, _month.get(), "month"_sd, 1, 1, 12, &month) ||
- !evaluateNumberWithinRange(root, _day.get(), "day"_sd, 1, 1, 31, &day)) {
+ if (!evaluateNumberWithinRange(
+ root, _year.get(), "year"_sd, 1970, 0, 9999, &year, variables) ||
+ !evaluateNumberWithinRange(
+ root, _month.get(), "month"_sd, 1, 1, 12, &month, variables) ||
+ !evaluateNumberWithinRange(root, _day.get(), "day"_sd, 1, 1, 31, &day, variables)) {
return Value(BSONNULL);
}
@@ -1257,11 +1269,18 @@ Value ExpressionDateFromParts::evaluate(const Document& root) const {
if (_isoWeekYear) {
int isoWeekYear, isoWeek, isoDayOfWeek;
- if (!evaluateNumberWithinRange(
- root, _isoWeekYear.get(), "isoWeekYear"_sd, 1970, 0, 9999, &isoWeekYear) ||
- !evaluateNumberWithinRange(root, _isoWeek.get(), "isoWeek"_sd, 1, 1, 53, &isoWeek) ||
+ if (!evaluateNumberWithinRange(root,
+ _isoWeekYear.get(),
+ "isoWeekYear"_sd,
+ 1970,
+ 0,
+ 9999,
+ &isoWeekYear,
+ variables) ||
!evaluateNumberWithinRange(
- root, _isoDayOfWeek.get(), "isoDayOfWeek"_sd, 1, 1, 7, &isoDayOfWeek)) {
+ root, _isoWeek.get(), "isoWeek"_sd, 1, 1, 53, &isoWeek, variables) ||
+ !evaluateNumberWithinRange(
+ root, _isoDayOfWeek.get(), "isoDayOfWeek"_sd, 1, 1, 7, &isoDayOfWeek, variables)) {
return Value(BSONNULL);
}
@@ -1361,7 +1380,8 @@ intrusive_ptr<Expression> ExpressionDateFromString::optimize() {
if (ExpressionConstant::allNullOrConstant({_dateString, _timeZone})) {
// Everything is a constant, so we can turn into a constant.
- return ExpressionConstant::create(getExpressionContext(), evaluate(Document{}));
+ return ExpressionConstant::create(
+ getExpressionContext(), evaluate(Document{}, &(getExpressionContext()->variables)));
}
return this;
}
@@ -1373,10 +1393,11 @@ Value ExpressionDateFromString::serialize(bool explain) const {
{"timezone", _timeZone ? _timeZone->serialize(explain) : Value()}}}});
}
-Value ExpressionDateFromString::evaluate(const Document& root) const {
- const Value dateString = _dateString->evaluate(root);
+Value ExpressionDateFromString::evaluate(const Document& root, Variables* variables) const {
+ const Value dateString = _dateString->evaluate(root, variables);
- auto timeZone = makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get());
+ auto timeZone =
+ makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get(), variables);
if (!timeZone || dateString.nullish()) {
return Value(BSONNULL);
@@ -1462,7 +1483,8 @@ intrusive_ptr<Expression> ExpressionDateToParts::optimize() {
if (ExpressionConstant::allNullOrConstant({_date, _iso8601, _timeZone})) {
// Everything is a constant, so we can turn into a constant.
- return ExpressionConstant::create(getExpressionContext(), evaluate(Document{}));
+ return ExpressionConstant::create(
+ getExpressionContext(), evaluate(Document{}, &(getExpressionContext()->variables)));
}
return this;
@@ -1476,12 +1498,13 @@ Value ExpressionDateToParts::serialize(bool explain) const {
{"iso8601", _iso8601 ? _iso8601->serialize(explain) : Value()}}}});
}
-boost::optional<int> ExpressionDateToParts::evaluateIso8601Flag(const Document& root) const {
+boost::optional<int> ExpressionDateToParts::evaluateIso8601Flag(const Document& root,
+ Variables* variables) const {
if (!_iso8601) {
return false;
}
- auto iso8601Output = _iso8601->evaluate(root);
+ auto iso8601Output = _iso8601->evaluate(root, variables);
if (iso8601Output.nullish()) {
return boost::none;
@@ -1495,15 +1518,16 @@ boost::optional<int> ExpressionDateToParts::evaluateIso8601Flag(const Document&
return iso8601Output.getBool();
}
-Value ExpressionDateToParts::evaluate(const Document& root) const {
- const Value date = _date->evaluate(root);
+Value ExpressionDateToParts::evaluate(const Document& root, Variables* variables) const {
+ const Value date = _date->evaluate(root, variables);
- auto timeZone = makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get());
+ auto timeZone =
+ makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get(), variables);
if (!timeZone) {
return Value(BSONNULL);
}
- auto iso8601 = evaluateIso8601Flag(root);
+ auto iso8601 = evaluateIso8601Flag(root, variables);
if (!iso8601) {
return Value(BSONNULL);
}
@@ -1608,7 +1632,8 @@ intrusive_ptr<Expression> ExpressionDateToString::optimize() {
if (ExpressionConstant::allNullOrConstant({_date, _timeZone})) {
// Everything is a constant, so we can turn into a constant.
- return ExpressionConstant::create(getExpressionContext(), evaluate(Document{}));
+ return ExpressionConstant::create(
+ getExpressionContext(), evaluate(Document{}, &(getExpressionContext()->variables)));
}
return this;
@@ -1622,10 +1647,11 @@ Value ExpressionDateToString::serialize(bool explain) const {
{"timezone", _timeZone ? _timeZone->serialize(explain) : Value()}}}});
}
-Value ExpressionDateToString::evaluate(const Document& root) const {
- const Value date = _date->evaluate(root);
+Value ExpressionDateToString::evaluate(const Document& root, Variables* variables) const {
+ const Value date = _date->evaluate(root, variables);
- auto timeZone = makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get());
+ auto timeZone =
+ makeTimeZone(getExpressionContext()->timeZoneDatabase, root, _timeZone.get(), variables);
if (!timeZone) {
return Value(BSONNULL);
}
@@ -1646,9 +1672,9 @@ void ExpressionDateToString::_doAddDependencies(DepsTracker* deps) const {
/* ----------------------- ExpressionDivide ---------------------------- */
-Value ExpressionDivide::evaluate(const Document& root) const {
- Value lhs = vpOperand[0]->evaluate(root);
- Value rhs = vpOperand[1]->evaluate(root);
+Value ExpressionDivide::evaluate(const Document& root, Variables* variables) const {
+ Value lhs = vpOperand[0]->evaluate(root, variables);
+ Value rhs = vpOperand[1]->evaluate(root, variables);
auto assertNonZero = [](bool nonZero) { uassert(16608, "can't $divide by zero", nonZero); };
@@ -1747,10 +1773,10 @@ void ExpressionObject::_doAddDependencies(DepsTracker* deps) const {
}
}
-Value ExpressionObject::evaluate(const Document& root) const {
+Value ExpressionObject::evaluate(const Document& root, Variables* variables) const {
MutableDocument outputDoc;
for (auto&& pair : _expressions) {
- outputDoc.addField(pair.first, pair.second->evaluate(root));
+ outputDoc.addField(pair.first, pair.second->evaluate(root, variables));
}
return outputDoc.freezeToValue();
}
@@ -1826,7 +1852,8 @@ intrusive_ptr<Expression> ExpressionFieldPath::optimize() {
}
if (getExpressionContext()->variables.hasConstantValue(_variable)) {
- return ExpressionConstant::create(getExpressionContext(), evaluate(Document()));
+ return ExpressionConstant::create(
+ getExpressionContext(), evaluate(Document(), &(getExpressionContext()->variables)));
}
return intrusive_ptr<Expression>(this);
@@ -1883,17 +1910,16 @@ Value ExpressionFieldPath::evaluatePath(size_t index, const Document& input) con
}
}
-Value ExpressionFieldPath::evaluate(const Document& root) const {
- auto& vars = getExpressionContext()->variables;
+Value ExpressionFieldPath::evaluate(const Document& root, Variables* variables) const {
if (_fieldPath.getPathLength() == 1) // get the whole variable
- return vars.getValue(_variable, root);
+ return variables->getValue(_variable, root);
if (_variable == Variables::kRootId) {
// ROOT is always a document so use optimized code path
return evaluatePath(1, root);
}
- Value var = vars.getValue(_variable, root);
+ Value var = variables->getValue(_variable, root);
switch (var.getType()) {
case Object:
return evaluatePath(1, var.getDocument());
@@ -2012,9 +2038,9 @@ Value ExpressionFilter::serialize(bool explain) const {
<< _filter->serialize(explain))));
}
-Value ExpressionFilter::evaluate(const Document& root) const {
+Value ExpressionFilter::evaluate(const Document& root, Variables* variables) const {
// We are guaranteed at parse time that this isn't using our _varId.
- const Value inputVal = _input->evaluate(root);
+ const Value inputVal = _input->evaluate(root, variables);
if (inputVal.nullish())
return Value(BSONNULL);
@@ -2029,11 +2055,10 @@ Value ExpressionFilter::evaluate(const Document& root) const {
return inputVal;
vector<Value> output;
- auto& vars = getExpressionContext()->variables;
for (const auto& elem : input) {
- vars.setValue(_varId, elem);
+ variables->setValue(_varId, elem);
- if (_filter->evaluate(root).coerceToBool()) {
+ if (_filter->evaluate(root, variables).coerceToBool()) {
output.push_back(std::move(elem));
}
}
@@ -2145,15 +2170,14 @@ Value ExpressionLet::serialize(bool explain) const {
DOC("$let" << DOC("vars" << vars.freeze() << "in" << _subExpression->serialize(explain))));
}
-Value ExpressionLet::evaluate(const Document& root) const {
+Value ExpressionLet::evaluate(const Document& root, Variables* variables) const {
for (const auto& item : _variables) {
// It is guaranteed at parse-time that these expressions don't use the variable ids we
// are setting
- getExpressionContext()->variables.setValue(item.first,
- item.second.expression->evaluate(root));
+ variables->setValue(item.first, item.second.expression->evaluate(root, variables));
}
- return _subExpression->evaluate(root);
+ return _subExpression->evaluate(root, variables);
}
void ExpressionLet::_doAddDependencies(DepsTracker* deps) const {
@@ -2237,9 +2261,9 @@ Value ExpressionMap::serialize(bool explain) const {
<< _each->serialize(explain))));
}
-Value ExpressionMap::evaluate(const Document& root) const {
+Value ExpressionMap::evaluate(const Document& root, Variables* variables) const {
// guaranteed at parse time that this isn't using our _varId
- const Value inputVal = _input->evaluate(root);
+ const Value inputVal = _input->evaluate(root, variables);
if (inputVal.nullish())
return Value(BSONNULL);
@@ -2255,9 +2279,9 @@ Value ExpressionMap::evaluate(const Document& root) const {
vector<Value> output;
output.reserve(input.size());
for (size_t i = 0; i < input.size(); i++) {
- getExpressionContext()->variables.setValue(_varId, input[i]);
+ variables->setValue(_varId, input[i]);
- Value toInsert = _each->evaluate(root);
+ Value toInsert = _each->evaluate(root, variables);
if (toInsert.missing())
toInsert = Value(BSONNULL); // can't insert missing values into array
@@ -2334,7 +2358,7 @@ Value ExpressionMeta::serialize(bool explain) const {
MONGO_UNREACHABLE;
}
-Value ExpressionMeta::evaluate(const Document& root) const {
+Value ExpressionMeta::evaluate(const Document& root, Variables* variables) const {
switch (_metaType) {
case MetaType::TEXT_SCORE:
return root.hasTextScore() ? Value(root.getTextScore()) : Value();
@@ -2352,9 +2376,9 @@ void ExpressionMeta::_doAddDependencies(DepsTracker* deps) const {
/* ----------------------- ExpressionMod ---------------------------- */
-Value ExpressionMod::evaluate(const Document& root) const {
- Value lhs = vpOperand[0]->evaluate(root);
- Value rhs = vpOperand[1]->evaluate(root);
+Value ExpressionMod::evaluate(const Document& root, Variables* variables) const {
+ Value lhs = vpOperand[0]->evaluate(root, variables);
+ Value rhs = vpOperand[1]->evaluate(root, variables);
BSONType leftType = lhs.getType();
BSONType rightType = rhs.getType();
@@ -2409,7 +2433,7 @@ const char* ExpressionMod::getOpName() const {
/* ------------------------- ExpressionMultiply ----------------------------- */
-Value ExpressionMultiply::evaluate(const Document& root) const {
+Value ExpressionMultiply::evaluate(const Document& root, Variables* variables) const {
/*
We'll try to return the narrowest possible result value. To do that
without creating intermediate Values, do the arithmetic for double
@@ -2424,7 +2448,7 @@ Value ExpressionMultiply::evaluate(const Document& root) const {
const size_t n = vpOperand.size();
for (size_t i = 0; i < n; ++i) {
- Value val = vpOperand[i]->evaluate(root);
+ Value val = vpOperand[i]->evaluate(root, variables);
if (val.numeric()) {
BSONType oldProductType = productType;
@@ -2472,12 +2496,12 @@ const char* ExpressionMultiply::getOpName() const {
/* ----------------------- ExpressionIfNull ---------------------------- */
-Value ExpressionIfNull::evaluate(const Document& root) const {
- Value pLeft(vpOperand[0]->evaluate(root));
+Value ExpressionIfNull::evaluate(const Document& root, Variables* variables) const {
+ Value pLeft(vpOperand[0]->evaluate(root, variables));
if (!pLeft.nullish())
return pLeft;
- Value pRight(vpOperand[1]->evaluate(root));
+ Value pRight(vpOperand[1]->evaluate(root, variables));
return pRight;
}
@@ -2488,9 +2512,9 @@ const char* ExpressionIfNull::getOpName() const {
/* ----------------------- ExpressionIn ---------------------------- */
-Value ExpressionIn::evaluate(const Document& root) const {
- Value argument(vpOperand[0]->evaluate(root));
- Value arrayOfValues(vpOperand[1]->evaluate(root));
+Value ExpressionIn::evaluate(const Document& root, Variables* variables) const {
+ Value argument(vpOperand[0]->evaluate(root, variables));
+ Value arrayOfValues(vpOperand[1]->evaluate(root, variables));
uassert(40081,
str::stream() << "$in requires an array as a second argument, found: "
@@ -2532,8 +2556,8 @@ void uassertIfNotIntegralAndNonNegative(Value val,
} // namespace
-Value ExpressionIndexOfArray::evaluate(const Document& root) const {
- Value arrayArg = vpOperand[0]->evaluate(root);
+Value ExpressionIndexOfArray::evaluate(const Document& root, Variables* variables) const {
+ Value arrayArg = vpOperand[0]->evaluate(root, variables);
if (arrayArg.nullish()) {
return Value(BSONNULL);
@@ -2546,18 +2570,18 @@ Value ExpressionIndexOfArray::evaluate(const Document& root) const {
std::vector<Value> array = arrayArg.getArray();
- Value searchItem = vpOperand[1]->evaluate(root);
+ Value searchItem = vpOperand[1]->evaluate(root, variables);
size_t startIndex = 0;
if (vpOperand.size() > 2) {
- Value startIndexArg = vpOperand[2]->evaluate(root);
+ Value startIndexArg = vpOperand[2]->evaluate(root, variables);
uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index");
startIndex = static_cast<size_t>(startIndexArg.coerceToInt());
}
size_t endIndex = array.size();
if (vpOperand.size() > 3) {
- Value endIndexArg = vpOperand[3]->evaluate(root);
+ Value endIndexArg = vpOperand[3]->evaluate(root, variables);
uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index");
// Don't let 'endIndex' exceed the length of the array.
endIndex = std::min(array.size(), static_cast<size_t>(endIndexArg.coerceToInt()));
@@ -2590,8 +2614,8 @@ bool stringHasTokenAtIndex(size_t index, const std::string& input, const std::st
} // namespace
-Value ExpressionIndexOfBytes::evaluate(const Document& root) const {
- Value stringArg = vpOperand[0]->evaluate(root);
+Value ExpressionIndexOfBytes::evaluate(const Document& root, Variables* variables) const {
+ Value stringArg = vpOperand[0]->evaluate(root, variables);
if (stringArg.nullish()) {
return Value(BSONNULL);
@@ -2603,7 +2627,7 @@ Value ExpressionIndexOfBytes::evaluate(const Document& root) const {
stringArg.getType() == String);
const std::string& input = stringArg.getString();
- Value tokenArg = vpOperand[1]->evaluate(root);
+ Value tokenArg = vpOperand[1]->evaluate(root, variables);
uassert(40092,
str::stream() << "$indexOfBytes requires a string as the second argument, found: "
<< typeName(tokenArg.getType()),
@@ -2612,14 +2636,14 @@ Value ExpressionIndexOfBytes::evaluate(const Document& root) const {
size_t startIndex = 0;
if (vpOperand.size() > 2) {
- Value startIndexArg = vpOperand[2]->evaluate(root);
+ Value startIndexArg = vpOperand[2]->evaluate(root, variables);
uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index");
startIndex = static_cast<size_t>(startIndexArg.coerceToInt());
}
size_t endIndex = input.size();
if (vpOperand.size() > 3) {
- Value endIndexArg = vpOperand[3]->evaluate(root);
+ Value endIndexArg = vpOperand[3]->evaluate(root, variables);
uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index");
// Don't let 'endIndex' exceed the length of the string.
endIndex = std::min(input.size(), static_cast<size_t>(endIndexArg.coerceToInt()));
@@ -2644,8 +2668,8 @@ const char* ExpressionIndexOfBytes::getOpName() const {
/* ----------------------- ExpressionIndexOfCP --------------------- */
-Value ExpressionIndexOfCP::evaluate(const Document& root) const {
- Value stringArg = vpOperand[0]->evaluate(root);
+Value ExpressionIndexOfCP::evaluate(const Document& root, Variables* variables) const {
+ Value stringArg = vpOperand[0]->evaluate(root, variables);
if (stringArg.nullish()) {
return Value(BSONNULL);
@@ -2657,7 +2681,7 @@ Value ExpressionIndexOfCP::evaluate(const Document& root) const {
stringArg.getType() == String);
const std::string& input = stringArg.getString();
- Value tokenArg = vpOperand[1]->evaluate(root);
+ Value tokenArg = vpOperand[1]->evaluate(root, variables);
uassert(40094,
str::stream() << "$indexOfCP requires a string as the second argument, found: "
<< typeName(tokenArg.getType()),
@@ -2666,7 +2690,7 @@ Value ExpressionIndexOfCP::evaluate(const Document& root) const {
size_t startCodePointIndex = 0;
if (vpOperand.size() > 2) {
- Value startIndexArg = vpOperand[2]->evaluate(root);
+ Value startIndexArg = vpOperand[2]->evaluate(root, variables);
uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index");
startCodePointIndex = static_cast<size_t>(startIndexArg.coerceToInt());
}
@@ -2689,7 +2713,7 @@ Value ExpressionIndexOfCP::evaluate(const Document& root) const {
size_t endCodePointIndex = codePointLength;
if (vpOperand.size() > 3) {
- Value endIndexArg = vpOperand[3]->evaluate(root);
+ Value endIndexArg = vpOperand[3]->evaluate(root, variables);
uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index");
// Don't let 'endCodePointIndex' exceed the number of code points in the string.
@@ -2746,9 +2770,9 @@ const char* ExpressionLn::getOpName() const {
/* ----------------------- ExpressionLog ---------------------------- */
-Value ExpressionLog::evaluate(const Document& root) const {
- Value argVal = vpOperand[0]->evaluate(root);
- Value baseVal = vpOperand[1]->evaluate(root);
+Value ExpressionLog::evaluate(const Document& root, Variables* variables) const {
+ Value argVal = vpOperand[0]->evaluate(root, variables);
+ Value baseVal = vpOperand[1]->evaluate(root, variables);
if (argVal.nullish() || baseVal.nullish())
return Value(BSONNULL);
@@ -2840,8 +2864,8 @@ intrusive_ptr<Expression> ExpressionNary::optimize() {
// If all the operands are constant expressions, collapse the expression into one constant
// expression.
if (constOperandCount == vpOperand.size()) {
- return intrusive_ptr<Expression>(
- ExpressionConstant::create(getExpressionContext(), evaluate(Document())));
+ return intrusive_ptr<Expression>(ExpressionConstant::create(
+ getExpressionContext(), evaluate(Document(), &(getExpressionContext()->variables))));
}
// If the expression is associative, we can collapse all the consecutive constant operands into
@@ -2884,8 +2908,9 @@ intrusive_ptr<Expression> ExpressionNary::optimize() {
if (constExpressions.size() > 1) {
ExpressionVector vpOperandSave = std::move(vpOperand);
vpOperand = std::move(constExpressions);
- optimizedOperands.emplace_back(
- ExpressionConstant::create(getExpressionContext(), evaluate(Document())));
+ optimizedOperands.emplace_back(ExpressionConstant::create(
+ getExpressionContext(),
+ evaluate(Document(), &getExpressionContext()->variables)));
vpOperand = std::move(vpOperandSave);
} else {
optimizedOperands.insert(
@@ -2899,8 +2924,9 @@ intrusive_ptr<Expression> ExpressionNary::optimize() {
if (constExpressions.size() > 1) {
vpOperand = std::move(constExpressions);
- optimizedOperands.emplace_back(
- ExpressionConstant::create(getExpressionContext(), evaluate(Document())));
+ optimizedOperands.emplace_back(ExpressionConstant::create(
+ getExpressionContext(),
+ evaluate(Document(), &(getExpressionContext()->variables))));
} else {
optimizedOperands.insert(
optimizedOperands.end(), constExpressions.begin(), constExpressions.end());
@@ -2933,8 +2959,8 @@ Value ExpressionNary::serialize(bool explain) const {
/* ------------------------- ExpressionNot ----------------------------- */
-Value ExpressionNot::evaluate(const Document& root) const {
- Value pOp(vpOperand[0]->evaluate(root));
+Value ExpressionNot::evaluate(const Document& root, Variables* variables) const {
+ Value pOp(vpOperand[0]->evaluate(root, variables));
bool b = pOp.coerceToBool();
return Value(!b);
@@ -2947,10 +2973,10 @@ const char* ExpressionNot::getOpName() const {
/* -------------------------- ExpressionOr ----------------------------- */
-Value ExpressionOr::evaluate(const Document& root) const {
+Value ExpressionOr::evaluate(const Document& root, Variables* variables) const {
const size_t n = vpOperand.size();
for (size_t i = 0; i < n; ++i) {
- Value pValue(vpOperand[i]->evaluate(root));
+ Value pValue(vpOperand[i]->evaluate(root, variables));
if (pValue.coerceToBool())
return Value(true);
}
@@ -3120,9 +3146,9 @@ intrusive_ptr<Expression> ExpressionPow::create(
return expr;
}
-Value ExpressionPow::evaluate(const Document& root) const {
- Value baseVal = vpOperand[0]->evaluate(root);
- Value expVal = vpOperand[1]->evaluate(root);
+Value ExpressionPow::evaluate(const Document& root, Variables* variables) const {
+ Value baseVal = vpOperand[0]->evaluate(root, variables);
+ Value expVal = vpOperand[1]->evaluate(root, variables);
if (baseVal.nullish() || expVal.nullish())
return Value(BSONNULL);
@@ -3238,9 +3264,9 @@ const char* ExpressionPow::getOpName() const {
/* ------------------------- ExpressionRange ------------------------------ */
-Value ExpressionRange::evaluate(const Document& root) const {
- Value startVal(vpOperand[0]->evaluate(root));
- Value endVal(vpOperand[1]->evaluate(root));
+Value ExpressionRange::evaluate(const Document& root, Variables* variables) const {
+ Value startVal(vpOperand[0]->evaluate(root, variables));
+ Value endVal(vpOperand[1]->evaluate(root, variables));
uassert(34443,
str::stream() << "$range requires a numeric starting value, found value of type: "
@@ -3267,7 +3293,7 @@ Value ExpressionRange::evaluate(const Document& root) const {
int step = 1;
if (vpOperand.size() == 3) {
// A step was specified by the user.
- Value stepVal(vpOperand[2]->evaluate(root));
+ Value stepVal(vpOperand[2]->evaluate(root, variables));
uassert(34447,
str::stream() << "$range requires a numeric step value, found value of type:"
@@ -3338,8 +3364,8 @@ intrusive_ptr<Expression> ExpressionReduce::parse(
return reduce;
}
-Value ExpressionReduce::evaluate(const Document& root) const {
- Value inputVal = _input->evaluate(root);
+Value ExpressionReduce::evaluate(const Document& root, Variables* variables) const {
+ Value inputVal = _input->evaluate(root, variables);
if (inputVal.nullish()) {
return Value(BSONNULL);
@@ -3350,14 +3376,13 @@ Value ExpressionReduce::evaluate(const Document& root) const {
<< inputVal.toString(),
inputVal.isArray());
- Value accumulatedValue = _initial->evaluate(root);
- auto& vars = getExpressionContext()->variables;
+ Value accumulatedValue = _initial->evaluate(root, variables);
for (auto&& elem : inputVal.getArray()) {
- vars.setValue(_thisVar, elem);
- vars.setValue(_valueVar, accumulatedValue);
+ variables->setValue(_thisVar, elem);
+ variables->setValue(_valueVar, accumulatedValue);
- accumulatedValue = _in->evaluate(root);
+ accumulatedValue = _in->evaluate(root, variables);
}
return accumulatedValue;
@@ -3385,8 +3410,8 @@ Value ExpressionReduce::serialize(bool explain) const {
/* ------------------------ ExpressionReverseArray ------------------------ */
-Value ExpressionReverseArray::evaluate(const Document& root) const {
- Value input(vpOperand[0]->evaluate(root));
+Value ExpressionReverseArray::evaluate(const Document& root, Variables* variables) const {
+ Value input(vpOperand[0]->evaluate(root, variables));
if (input.nullish()) {
return Value(BSONNULL);
@@ -3422,9 +3447,9 @@ ValueSet arrayToSet(const Value& val, const ValueComparator& valueComparator) {
/* ----------------------- ExpressionSetDifference ---------------------------- */
-Value ExpressionSetDifference::evaluate(const Document& root) const {
- const Value lhs = vpOperand[0]->evaluate(root);
- const Value rhs = vpOperand[1]->evaluate(root);
+Value ExpressionSetDifference::evaluate(const Document& root, Variables* variables) const {
+ const Value lhs = vpOperand[0]->evaluate(root, variables);
+ const Value rhs = vpOperand[1]->evaluate(root, variables);
if (lhs.nullish() || rhs.nullish()) {
return Value(BSONNULL);
@@ -3468,13 +3493,13 @@ void ExpressionSetEquals::validateArguments(const ExpressionVector& args) const
args.size() >= 2);
}
-Value ExpressionSetEquals::evaluate(const Document& root) const {
+Value ExpressionSetEquals::evaluate(const Document& root, Variables* variables) const {
const size_t n = vpOperand.size();
const auto& valueComparator = getExpressionContext()->getValueComparator();
ValueSet lhs = valueComparator.makeOrderedValueSet();
for (size_t i = 0; i < n; i++) {
- const Value nextEntry = vpOperand[i]->evaluate(root);
+ const Value nextEntry = vpOperand[i]->evaluate(root, variables);
uassert(17044,
str::stream() << "All operands of $setEquals must be arrays. One "
<< "argument is of type: "
@@ -3505,12 +3530,12 @@ const char* ExpressionSetEquals::getOpName() const {
/* ----------------------- ExpressionSetIntersection ---------------------------- */
-Value ExpressionSetIntersection::evaluate(const Document& root) const {
+Value ExpressionSetIntersection::evaluate(const Document& root, Variables* variables) const {
const size_t n = vpOperand.size();
const auto& valueComparator = getExpressionContext()->getValueComparator();
ValueSet currentIntersection = valueComparator.makeOrderedValueSet();
for (size_t i = 0; i < n; i++) {
- const Value nextEntry = vpOperand[i]->evaluate(root);
+ const Value nextEntry = vpOperand[i]->evaluate(root, variables);
if (nextEntry.nullish()) {
return Value(BSONNULL);
}
@@ -3566,9 +3591,9 @@ Value setIsSubsetHelper(const vector<Value>& lhs, const ValueSet& rhs) {
}
}
-Value ExpressionSetIsSubset::evaluate(const Document& root) const {
- const Value lhs = vpOperand[0]->evaluate(root);
- const Value rhs = vpOperand[1]->evaluate(root);
+Value ExpressionSetIsSubset::evaluate(const Document& root, Variables* variables) const {
+ const Value lhs = vpOperand[0]->evaluate(root, variables);
+ const Value rhs = vpOperand[1]->evaluate(root, variables);
uassert(17046,
str::stream() << "both operands of $setIsSubset must be arrays. First "
@@ -3601,8 +3626,8 @@ public:
vpOperand = operands;
}
- virtual Value evaluate(const Document& root) const {
- const Value lhs = vpOperand[0]->evaluate(root);
+ virtual Value evaluate(const Document& root, Variables* variables) const {
+ const Value lhs = vpOperand[0]->evaluate(root, variables);
uassert(17310,
str::stream() << "both operands of $setIsSubset must be arrays. First "
@@ -3649,11 +3674,11 @@ const char* ExpressionSetIsSubset::getOpName() const {
/* ----------------------- ExpressionSetUnion ---------------------------- */
-Value ExpressionSetUnion::evaluate(const Document& root) const {
+Value ExpressionSetUnion::evaluate(const Document& root, Variables* variables) const {
ValueSet unionedSet = getExpressionContext()->getValueComparator().makeOrderedValueSet();
const size_t n = vpOperand.size();
for (size_t i = 0; i < n; i++) {
- const Value newEntries = vpOperand[i]->evaluate(root);
+ const Value newEntries = vpOperand[i]->evaluate(root, variables);
if (newEntries.nullish()) {
return Value(BSONNULL);
}
@@ -3675,8 +3700,8 @@ const char* ExpressionSetUnion::getOpName() const {
/* ----------------------- ExpressionIsArray ---------------------------- */
-Value ExpressionIsArray::evaluate(const Document& root) const {
- Value argument = vpOperand[0]->evaluate(root);
+Value ExpressionIsArray::evaluate(const Document& root, Variables* variables) const {
+ Value argument = vpOperand[0]->evaluate(root, variables);
return Value(argument.isArray());
}
@@ -3687,12 +3712,12 @@ const char* ExpressionIsArray::getOpName() const {
/* ----------------------- ExpressionSlice ---------------------------- */
-Value ExpressionSlice::evaluate(const Document& root) const {
+Value ExpressionSlice::evaluate(const Document& root, Variables* variables) const {
const size_t n = vpOperand.size();
- Value arrayVal = vpOperand[0]->evaluate(root);
+ Value arrayVal = vpOperand[0]->evaluate(root, variables);
// Could be either a start index or the length from 0.
- Value arg2 = vpOperand[1]->evaluate(root);
+ Value arg2 = vpOperand[1]->evaluate(root, variables);
if (arrayVal.nullish() || arg2.nullish()) {
return Value(BSONNULL);
@@ -3743,7 +3768,7 @@ Value ExpressionSlice::evaluate(const Document& root) const {
start = std::min(array.size(), size_t(startInt));
}
- Value countVal = vpOperand[2]->evaluate(root);
+ Value countVal = vpOperand[2]->evaluate(root, variables);
if (countVal.nullish()) {
return Value(BSONNULL);
@@ -3778,8 +3803,8 @@ const char* ExpressionSlice::getOpName() const {
/* ----------------------- ExpressionSize ---------------------------- */
-Value ExpressionSize::evaluate(const Document& root) const {
- Value array = vpOperand[0]->evaluate(root);
+Value ExpressionSize::evaluate(const Document& root, Variables* variables) const {
+ Value array = vpOperand[0]->evaluate(root, variables);
uassert(17124,
str::stream() << "The argument to $size must be an array, but was of type: "
@@ -3795,9 +3820,9 @@ const char* ExpressionSize::getOpName() const {
/* ----------------------- ExpressionSplit --------------------------- */
-Value ExpressionSplit::evaluate(const Document& root) const {
- Value inputArg = vpOperand[0]->evaluate(root);
- Value separatorArg = vpOperand[1]->evaluate(root);
+Value ExpressionSplit::evaluate(const Document& root, Variables* variables) const {
+ Value inputArg = vpOperand[0]->evaluate(root, variables);
+ Value separatorArg = vpOperand[1]->evaluate(root, variables);
if (inputArg.nullish() || separatorArg.nullish()) {
return Value(BSONNULL);
@@ -3873,9 +3898,9 @@ const char* ExpressionSqrt::getOpName() const {
/* ----------------------- ExpressionStrcasecmp ---------------------------- */
-Value ExpressionStrcasecmp::evaluate(const Document& root) const {
- Value pString1(vpOperand[0]->evaluate(root));
- Value pString2(vpOperand[1]->evaluate(root));
+Value ExpressionStrcasecmp::evaluate(const Document& root, Variables* variables) const {
+ Value pString1(vpOperand[0]->evaluate(root, variables));
+ Value pString2(vpOperand[1]->evaluate(root, variables));
/* boost::iequals returns a bool not an int so strings must actually be allocated */
string str1 = boost::to_upper_copy(pString1.coerceToString());
@@ -3897,10 +3922,10 @@ const char* ExpressionStrcasecmp::getOpName() const {
/* ----------------------- ExpressionSubstrBytes ---------------------------- */
-Value ExpressionSubstrBytes::evaluate(const Document& root) const {
- Value pString(vpOperand[0]->evaluate(root));
- Value pLower(vpOperand[1]->evaluate(root));
- Value pLength(vpOperand[2]->evaluate(root));
+Value ExpressionSubstrBytes::evaluate(const Document& root, Variables* variables) const {
+ Value pString(vpOperand[0]->evaluate(root, variables));
+ Value pLower(vpOperand[1]->evaluate(root, variables));
+ Value pLength(vpOperand[2]->evaluate(root, variables));
string str = pString.coerceToString();
uassert(16034,
@@ -3950,10 +3975,10 @@ const char* ExpressionSubstrBytes::getOpName() const {
/* ----------------------- ExpressionSubstrCP ---------------------------- */
-Value ExpressionSubstrCP::evaluate(const Document& root) const {
- Value inputVal(vpOperand[0]->evaluate(root));
- Value lowerVal(vpOperand[1]->evaluate(root));
- Value lengthVal(vpOperand[2]->evaluate(root));
+Value ExpressionSubstrCP::evaluate(const Document& root, Variables* variables) const {
+ Value inputVal(vpOperand[0]->evaluate(root, variables));
+ Value lowerVal(vpOperand[1]->evaluate(root, variables));
+ Value lengthVal(vpOperand[2]->evaluate(root, variables));
std::string str = inputVal.coerceToString();
uassert(34450,
@@ -4025,8 +4050,8 @@ const char* ExpressionSubstrCP::getOpName() const {
/* ----------------------- ExpressionStrLenBytes ------------------------- */
-Value ExpressionStrLenBytes::evaluate(const Document& root) const {
- Value str(vpOperand[0]->evaluate(root));
+Value ExpressionStrLenBytes::evaluate(const Document& root, Variables* variables) const {
+ Value str(vpOperand[0]->evaluate(root, variables));
uassert(34473,
str::stream() << "$strLenBytes requires a string argument, found: "
@@ -4048,8 +4073,8 @@ const char* ExpressionStrLenBytes::getOpName() const {
/* ----------------------- ExpressionStrLenCP ------------------------- */
-Value ExpressionStrLenCP::evaluate(const Document& root) const {
- Value val(vpOperand[0]->evaluate(root));
+Value ExpressionStrLenCP::evaluate(const Document& root, Variables* variables) const {
+ Value val(vpOperand[0]->evaluate(root, variables));
uassert(34471,
str::stream() << "$strLenCP requires a string argument, found: "
@@ -4073,9 +4098,9 @@ const char* ExpressionStrLenCP::getOpName() const {
/* ----------------------- ExpressionSubtract ---------------------------- */
-Value ExpressionSubtract::evaluate(const Document& root) const {
- Value lhs = vpOperand[0]->evaluate(root);
- Value rhs = vpOperand[1]->evaluate(root);
+Value ExpressionSubtract::evaluate(const Document& root, Variables* variables) const {
+ Value lhs = vpOperand[0]->evaluate(root, variables);
+ Value rhs = vpOperand[1]->evaluate(root, variables);
BSONType diffType = Value::getWidestNumeric(rhs.getType(), lhs.getType());
@@ -4123,12 +4148,12 @@ const char* ExpressionSubtract::getOpName() const {
REGISTER_EXPRESSION(switch, ExpressionSwitch::parse);
-Value ExpressionSwitch::evaluate(const Document& root) const {
+Value ExpressionSwitch::evaluate(const Document& root, Variables* variables) const {
for (auto&& branch : _branches) {
- Value caseExpression(branch.first->evaluate(root));
+ Value caseExpression(branch.first->evaluate(root, variables));
if (caseExpression.coerceToBool()) {
- return branch.second->evaluate(root);
+ return branch.second->evaluate(root, variables);
}
}
@@ -4136,7 +4161,7 @@ Value ExpressionSwitch::evaluate(const Document& root) const {
"$switch could not find a matching branch for an input, and no default was specified.",
_default);
- return _default->evaluate(root);
+ return _default->evaluate(root, variables);
}
boost::intrusive_ptr<Expression> ExpressionSwitch::parse(
@@ -4250,8 +4275,8 @@ Value ExpressionSwitch::serialize(bool explain) const {
/* ------------------------- ExpressionToLower ----------------------------- */
-Value ExpressionToLower::evaluate(const Document& root) const {
- Value pString(vpOperand[0]->evaluate(root));
+Value ExpressionToLower::evaluate(const Document& root, Variables* variables) const {
+ Value pString(vpOperand[0]->evaluate(root, variables));
string str = pString.coerceToString();
boost::to_lower(str);
return Value(str);
@@ -4264,8 +4289,8 @@ const char* ExpressionToLower::getOpName() const {
/* ------------------------- ExpressionToUpper -------------------------- */
-Value ExpressionToUpper::evaluate(const Document& root) const {
- Value pString(vpOperand[0]->evaluate(root));
+Value ExpressionToUpper::evaluate(const Document& root, Variables* variables) const {
+ Value pString(vpOperand[0]->evaluate(root, variables));
string str(pString.coerceToString());
boost::to_upper(str);
return Value(str);
@@ -4280,6 +4305,7 @@ const char* ExpressionToUpper::getOpName() const {
Value ExpressionTrunc::evaluateNumericArg(const Value& numericArg) const {
// There's no point in truncating integers or longs, it will have no effect.
+
switch (numericArg.getType()) {
case NumberDecimal:
return Value(numericArg.getDecimal().quantize(Decimal128::kNormalizedZero,
@@ -4298,8 +4324,8 @@ const char* ExpressionTrunc::getOpName() const {
/* ------------------------- ExpressionType ----------------------------- */
-Value ExpressionType::evaluate(const Document& root) const {
- Value val(vpOperand[0]->evaluate(root));
+Value ExpressionType::evaluate(const Document& root, Variables* variables) const {
+ Value val(vpOperand[0]->evaluate(root, variables));
return Value(StringData(typeName(val.getType())));
}
@@ -4363,7 +4389,7 @@ intrusive_ptr<Expression> ExpressionZip::parse(
return std::move(newZip);
}
-Value ExpressionZip::evaluate(const Document& root) const {
+Value ExpressionZip::evaluate(const Document& root, Variables* variables) const {
// Evaluate input values.
vector<vector<Value>> inputValues;
inputValues.reserve(_inputs.size());
@@ -4371,7 +4397,7 @@ Value ExpressionZip::evaluate(const Document& root) const {
size_t minArraySize = 0;
size_t maxArraySize = 0;
for (size_t i = 0; i < _inputs.size(); i++) {
- Value evalExpr = _inputs[i]->evaluate(root);
+ Value evalExpr = _inputs[i].get()->evaluate(root, variables);
if (evalExpr.nullish()) {
return Value(BSONNULL);
}
@@ -4400,7 +4426,7 @@ Value ExpressionZip::evaluate(const Document& root) const {
// If we need default values, evaluate each expression.
if (minArraySize != maxArraySize) {
for (size_t i = 0; i < _defaults.size(); i++) {
- evaluatedDefaults[i] = _defaults[i]->evaluate(root);
+ evaluatedDefaults[i] = _defaults[i].get()->evaluate(root, variables);
}
}
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index b1b0cfae250..f7f932cc135 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -126,9 +126,12 @@ public:
virtual Value serialize(bool explain) const = 0;
/**
- * Evaluate expression with respect to the Document given by 'root', and return the result.
+ * Evaluate the expression with respect to the Document given by 'root' and the Variables given
+ * by 'variables'. It is an error to supply a Variables argument whose built-in variables (like
+ * $$NOW) are not set. This method is thread-safe, so long as the 'variables' passed in here is
+ * not shared between threads.
*/
- virtual Value evaluate(const Document& root) const = 0;
+ virtual Value evaluate(const Document& root, Variables* variables) const = 0;
/**
* Returns information about the paths computed by this expression. This only needs to be
@@ -212,6 +215,10 @@ public:
*/
static void registerExpression(std::string key, Parser parser);
+ const boost::intrusive_ptr<ExpressionContext>& getExpressionContext() const {
+ return _expCtx;
+ }
+
protected:
Expression(const boost::intrusive_ptr<ExpressionContext>& expCtx) : _expCtx(expCtx) {
auto varIds = _expCtx->variablesParseState.getDefinedVariableIDs();
@@ -222,10 +229,6 @@ protected:
typedef std::vector<boost::intrusive_ptr<Expression>> ExpressionVector;
- const boost::intrusive_ptr<ExpressionContext>& getExpressionContext() const {
- return _expCtx;
- }
-
virtual void _doAddDependencies(DepsTracker* deps) const = 0;
private:
@@ -364,13 +367,13 @@ public:
explicit ExpressionFromAccumulator(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionFromAccumulator<Accumulator>>(expCtx) {}
- Value evaluate(const Document& root) const final {
+ Value evaluate(const Document& root, Variables* variables) const final {
Accumulator accum(this->getExpressionContext());
const size_t n = this->vpOperand.size();
// If a single array arg is given, loop through it passing each member to the accumulator.
// If a single, non-array arg is given, pass it directly to the accumulator.
if (n == 1) {
- Value singleVal = this->vpOperand[0]->evaluate(root);
+ Value singleVal = this->vpOperand[0]->evaluate(root, variables);
if (singleVal.getType() == Array) {
for (const Value& val : singleVal.getArray()) {
accum.process(val, false);
@@ -381,7 +384,7 @@ public:
} else {
// If multiple arguments are given, pass all arguments to the accumulator.
for (auto&& argument : this->vpOperand) {
- accum.process(argument->evaluate(root), false);
+ accum.process(argument->evaluate(root, variables), false);
}
}
return accum.getValue(false);
@@ -416,8 +419,8 @@ public:
virtual ~ExpressionSingleNumericArg() {}
- Value evaluate(const Document& root) const final {
- Value arg = this->vpOperand[0]->evaluate(root);
+ Value evaluate(const Document& root, Variables* variables) const final {
+ Value arg = this->vpOperand[0]->evaluate(root, variables);
if (arg.nullish())
return Value(BSONNULL);
@@ -438,7 +441,7 @@ public:
class ExpressionConstant final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
Value serialize(bool explain) const final;
const char* getOpName() const;
@@ -498,8 +501,8 @@ class DateExpressionAcceptingTimeZone : public Expression {
public:
virtual ~DateExpressionAcceptingTimeZone() {}
- Value evaluate(const Document& root) const final {
- auto dateVal = _date->evaluate(root);
+ Value evaluate(const Document& root, Variables* variables) const final {
+ auto dateVal = _date->evaluate(root, variables);
if (dateVal.nullish()) {
return Value(BSONNULL);
}
@@ -508,7 +511,7 @@ public:
if (!_timeZone) {
return evaluateDate(date, TimeZoneDatabase::utcZone());
}
- auto timeZoneId = _timeZone->evaluate(root);
+ auto timeZoneId = _timeZone->evaluate(root, variables);
if (timeZoneId.nullish()) {
return Value(BSONNULL);
}
@@ -546,7 +549,8 @@ public:
}
if (ExpressionConstant::allNullOrConstant({_date, _timeZone})) {
// Everything is a constant, so we can turn into a constant.
- return ExpressionConstant::create(getExpressionContext(), evaluate(Document{}));
+ return ExpressionConstant::create(
+ getExpressionContext(), evaluate(Document{}, &(getExpressionContext()->variables)));
}
return this;
}
@@ -650,7 +654,7 @@ public:
explicit ExpressionAdd(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionAdd>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -668,7 +672,7 @@ public:
explicit ExpressionAllElementsTrue(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionAllElementsTrue, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -679,7 +683,7 @@ public:
: ExpressionVariadic<ExpressionAnd>(expCtx) {}
boost::intrusive_ptr<Expression> optimize() final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -697,7 +701,7 @@ public:
explicit ExpressionAnyElementTrue(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionAnyElementTrue, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -707,7 +711,7 @@ public:
explicit ExpressionArray(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionArray>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
Value serialize(bool explain) const final;
const char* getOpName() const final;
};
@@ -718,7 +722,7 @@ public:
explicit ExpressionArrayElemAt(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionArrayElemAt, 2>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -727,7 +731,7 @@ public:
explicit ExpressionObjectToArray(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionObjectToArray, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -736,7 +740,7 @@ public:
explicit ExpressionArrayToObject(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionArrayToObject, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -753,7 +757,7 @@ public:
class ExpressionCoerceToBool final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
Value serialize(bool explain) const final;
static boost::intrusive_ptr<ExpressionCoerceToBool> create(
@@ -790,7 +794,7 @@ public:
ExpressionCompare(const boost::intrusive_ptr<ExpressionContext>& expCtx, CmpOp cmpOp)
: ExpressionFixedArity<ExpressionCompare, 2>(expCtx), cmpOp(cmpOp) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
CmpOp getOp() const {
@@ -819,7 +823,7 @@ public:
explicit ExpressionConcat(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionConcat>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -833,7 +837,7 @@ public:
explicit ExpressionConcatArrays(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionConcatArrays>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -846,7 +850,7 @@ class ExpressionCond final : public ExpressionFixedArity<ExpressionCond, 3> {
public:
explicit ExpressionCond(const boost::intrusive_ptr<ExpressionContext>& expCtx) : Base(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
static boost::intrusive_ptr<Expression> parse(
@@ -862,7 +866,7 @@ class ExpressionDateFromString final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value serialize(bool explain) const final;
- Value evaluate(const Document&) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -885,7 +889,7 @@ class ExpressionDateFromParts final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value serialize(bool explain) const final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -919,7 +923,8 @@ private:
int defaultValue,
int minValue,
int maxValue,
- int* returnValue) const;
+ int* returnValue,
+ Variables* variables) const;
boost::intrusive_ptr<Expression> _year;
boost::intrusive_ptr<Expression> _month;
@@ -938,7 +943,7 @@ class ExpressionDateToParts final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value serialize(bool explain) const final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -957,7 +962,7 @@ private:
boost::intrusive_ptr<Expression> timeZone,
boost::intrusive_ptr<Expression> iso8601);
- boost::optional<int> evaluateIso8601Flag(const Document& root) const;
+ boost::optional<int> evaluateIso8601Flag(const Document& root, Variables* variables) const;
boost::intrusive_ptr<Expression> _date;
boost::intrusive_ptr<Expression> _timeZone;
@@ -968,7 +973,7 @@ class ExpressionDateToString final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value serialize(bool explain) const final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -1027,7 +1032,7 @@ public:
explicit ExpressionDivide(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionDivide, 2>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1049,7 +1054,7 @@ public:
}
boost::intrusive_ptr<Expression> optimize() final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
Value serialize(bool explain) const final;
/*
@@ -1116,7 +1121,7 @@ class ExpressionFilter final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value serialize(bool explain) const final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -1170,7 +1175,7 @@ public:
explicit ExpressionIfNull(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionIfNull, 2>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1180,7 +1185,7 @@ public:
explicit ExpressionIn(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionIn, 2>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1190,7 +1195,7 @@ public:
explicit ExpressionIndexOfArray(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionRangedArity<ExpressionIndexOfArray, 2, 4>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1200,7 +1205,7 @@ public:
explicit ExpressionIndexOfBytes(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionRangedArity<ExpressionIndexOfBytes, 2, 4>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1213,7 +1218,7 @@ public:
explicit ExpressionIndexOfCP(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionRangedArity<ExpressionIndexOfCP, 2, 4>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1222,7 +1227,7 @@ class ExpressionLet final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value serialize(bool explain) const final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -1266,7 +1271,7 @@ public:
explicit ExpressionLog(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionLog, 2>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1283,7 +1288,7 @@ class ExpressionMap final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value serialize(bool explain) const final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -1313,7 +1318,7 @@ private:
class ExpressionMeta final : public Expression {
public:
Value serialize(bool explain) const final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -1361,7 +1366,7 @@ public:
explicit ExpressionMod(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionMod, 2>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1371,7 +1376,7 @@ public:
explicit ExpressionMultiply(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionMultiply>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -1400,7 +1405,7 @@ public:
explicit ExpressionNot(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionNot, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1416,7 +1421,7 @@ public:
class ExpressionObject final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
Value serialize(bool explain) const final;
static boost::intrusive_ptr<ExpressionObject> create(
@@ -1462,7 +1467,7 @@ public:
: ExpressionVariadic<ExpressionOr>(expCtx) {}
boost::intrusive_ptr<Expression> optimize() final;
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -1483,7 +1488,7 @@ public:
const boost::intrusive_ptr<ExpressionContext>& expCtx, Value base, Value exp);
private:
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1493,7 +1498,7 @@ public:
explicit ExpressionRange(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionRangedArity<ExpressionRange, 2, 3>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1503,7 +1508,7 @@ public:
explicit ExpressionReduce(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: Expression(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
boost::intrusive_ptr<Expression> optimize() final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -1540,7 +1545,7 @@ public:
explicit ExpressionSetDifference(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSetDifference, 2>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1550,7 +1555,7 @@ public:
explicit ExpressionSetEquals(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionSetEquals>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
void validateArguments(const ExpressionVector& args) const final;
};
@@ -1561,7 +1566,7 @@ public:
explicit ExpressionSetIntersection(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionSetIntersection>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -1581,7 +1586,7 @@ public:
: ExpressionFixedArity<ExpressionSetIsSubset, 2>(expCtx) {}
boost::intrusive_ptr<Expression> optimize() override;
- Value evaluate(const Document& root) const override;
+ Value evaluate(const Document& root, Variables* variables) const override;
const char* getOpName() const final;
private:
@@ -1594,7 +1599,7 @@ public:
explicit ExpressionSetUnion(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionVariadic<ExpressionSetUnion>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
bool isAssociative() const final {
@@ -1612,7 +1617,7 @@ public:
explicit ExpressionSize(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSize, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1622,7 +1627,7 @@ public:
explicit ExpressionReverseArray(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionReverseArray, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1632,7 +1637,7 @@ public:
explicit ExpressionSlice(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionRangedArity<ExpressionSlice, 2, 3>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1642,17 +1647,16 @@ public:
explicit ExpressionIsArray(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionIsArray, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
-
class ExpressionSplit final : public ExpressionFixedArity<ExpressionSplit, 2> {
public:
explicit ExpressionSplit(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSplit, 2>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1672,7 +1676,7 @@ public:
explicit ExpressionStrcasecmp(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionStrcasecmp, 2>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1682,7 +1686,7 @@ public:
explicit ExpressionSubstrBytes(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSubstrBytes, 3>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const;
};
@@ -1692,7 +1696,7 @@ public:
explicit ExpressionSubstrCP(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSubstrCP, 3>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1702,7 +1706,7 @@ public:
explicit ExpressionStrLenBytes(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionStrLenBytes, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1712,7 +1716,7 @@ public:
explicit ExpressionStrLenCP(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionStrLenCP, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1722,7 +1726,7 @@ public:
explicit ExpressionSubtract(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionSubtract, 2>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1732,7 +1736,7 @@ public:
explicit ExpressionSwitch(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: Expression(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
boost::intrusive_ptr<Expression> optimize() final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -1757,7 +1761,7 @@ public:
explicit ExpressionToLower(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionToLower, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1767,11 +1771,10 @@ public:
explicit ExpressionToUpper(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionToUpper, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
-
class ExpressionTrunc final : public ExpressionSingleNumericArg<ExpressionTrunc> {
public:
explicit ExpressionTrunc(const boost::intrusive_ptr<ExpressionContext>& expCtx)
@@ -1787,7 +1790,7 @@ public:
explicit ExpressionType(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: ExpressionFixedArity<ExpressionType, 1>(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
};
@@ -1853,7 +1856,7 @@ public:
explicit ExpressionZip(const boost::intrusive_ptr<ExpressionContext>& expCtx)
: Expression(expCtx) {}
- Value evaluate(const Document& root) const final;
+ Value evaluate(const Document& root, Variables* variables) const final;
boost::intrusive_ptr<Expression> optimize() final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp
index 532e298c1c1..e18d7d2f5cf 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -66,7 +66,7 @@ static Value evaluateExpression(const string& expressionName,
VariablesParseState vps = expCtx->variablesParseState;
const BSONObj obj = BSON(expressionName << ImplicitValue::convertToValue(operands));
auto expression = Expression::parseExpression(expCtx, obj, vps);
- Value result = expression->evaluate(Document());
+ Value result = expression->evaluate({}, &expCtx->variables);
return result;
}
@@ -163,8 +163,9 @@ class ExpressionNaryTestOneArg : public ExpressionBaseTest {
public:
virtual void assertEvaluates(Value input, Value output) {
addOperand(_expr, input);
- ASSERT_VALUE_EQ(output, _expr->evaluate(Document()));
- ASSERT_EQUALS(output.getType(), _expr->evaluate(Document()).getType());
+ ASSERT_VALUE_EQ(output, _expr->evaluate({}, &_expr->getExpressionContext()->variables));
+ ASSERT_EQUALS(output.getType(),
+ _expr->evaluate({}, &_expr->getExpressionContext()->variables).getType());
}
intrusive_ptr<ExpressionNary> _expr;
@@ -175,13 +176,13 @@ public:
/** A dummy child of ExpressionNary used for testing. */
class Testable : public ExpressionNary {
public:
- virtual Value evaluate(const Document& root) const {
+ virtual Value evaluate(const Document& root, Variables* variables) const {
// Just put all the values in a list.
// By default, this is not associative/commutative so the results will change if
// instantiated as commutative or associative and operations are reordered.
vector<Value> values;
for (ExpressionVector::const_iterator i = vpOperand.begin(); i != vpOperand.end(); ++i) {
- values.push_back((*i)->evaluate(root));
+ values.push_back((*i)->evaluate(root, variables));
}
return Value(values);
}
@@ -1075,7 +1076,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<ExpressionNary> expression = new ExpressionAdd(expCtx);
populateOperands(expression);
- ASSERT_BSONOBJ_EQ(expectedResult(), toBson(expression->evaluate(Document())));
+ ASSERT_BSONOBJ_EQ(expectedResult(), toBson(expression->evaluate({}, &expCtx->variables)));
}
protected:
@@ -1091,7 +1092,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<ExpressionNary> expression = new ExpressionAdd(expCtx);
expression->addOperand(ExpressionConstant::create(expCtx, Value(2)));
- ASSERT_BSONOBJ_EQ(BSON("" << 2), toBson(expression->evaluate(Document())));
+ ASSERT_BSONOBJ_EQ(BSON("" << 2), toBson(expression->evaluate({}, &expCtx->variables)));
}
};
@@ -1110,7 +1111,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<ExpressionNary> expression = new ExpressionAdd(expCtx);
expression->addOperand(ExpressionConstant::create(expCtx, Value("a"_sd)));
- ASSERT_THROWS(expression->evaluate(Document()), AssertionException);
+ ASSERT_THROWS(expression->evaluate({}, &expCtx->variables), AssertionException);
}
};
@@ -1121,7 +1122,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<ExpressionNary> expression = new ExpressionAdd(expCtx);
expression->addOperand(ExpressionConstant::create(expCtx, Value(true)));
- ASSERT_THROWS(expression->evaluate(Document()), AssertionException);
+ ASSERT_THROWS(expression->evaluate({}, &expCtx->variables), AssertionException);
}
};
@@ -1360,11 +1361,13 @@ public:
VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
- ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()),
- toBson(expression->evaluate(fromBson(BSON("a" << 1)))));
+ ASSERT_BSONOBJ_EQ(
+ BSON("" << expectedResult()),
+ toBson(expression->evaluate(fromBson(BSON("a" << 1)), &expCtx->variables)));
intrusive_ptr<Expression> optimized = expression->optimize();
- ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()),
- toBson(optimized->evaluate(fromBson(BSON("a" << 1)))));
+ ASSERT_BSONOBJ_EQ(
+ BSON("" << expectedResult()),
+ toBson(optimized->evaluate(fromBson(BSON("a" << 1)), &expCtx->variables)));
}
protected:
@@ -1655,7 +1658,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> nested = ExpressionConstant::create(expCtx, Value(5));
intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(expCtx, nested);
- ASSERT(expression->evaluate(Document()).getBool());
+ ASSERT(expression->evaluate({}, &expCtx->variables).getBool());
}
};
@@ -1666,7 +1669,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> nested = ExpressionConstant::create(expCtx, Value(0));
intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(expCtx, nested);
- ASSERT(!expression->evaluate(Document()).getBool());
+ ASSERT(!expression->evaluate({}, &expCtx->variables).getBool());
}
};
@@ -1774,10 +1777,10 @@ public:
// Check expression spec round trip.
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
// Check evaluation result.
- ASSERT_BSONOBJ_EQ(expectedResult(), toBson(expression->evaluate(Document())));
+ ASSERT_BSONOBJ_EQ(expectedResult(), toBson(expression->evaluate({}, &expCtx->variables)));
// Check that the result is the same after optimizing.
intrusive_ptr<Expression> optimized = expression->optimize();
- ASSERT_BSONOBJ_EQ(expectedResult(), toBson(optimized->evaluate(Document())));
+ ASSERT_BSONOBJ_EQ(expectedResult(), toBson(optimized->evaluate({}, &expCtx->variables)));
}
protected:
@@ -2014,7 +2017,7 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
- ASSERT_VALUE_EQ(expression->evaluate(Document()), Value(true));
+ ASSERT_VALUE_EQ(expression->evaluate({}, &expCtx->variables), Value(true));
}
};
@@ -2147,7 +2150,7 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value(5));
- assertBinaryEqual(BSON("" << 5), toBson(expression->evaluate(Document())));
+ assertBinaryEqual(BSON("" << 5), toBson(expression->evaluate({}, &expCtx->variables)));
}
};
@@ -2163,7 +2166,7 @@ public:
intrusive_ptr<Expression> expression = ExpressionConstant::parse(expCtx, specElement, vps);
assertBinaryEqual(BSON(""
<< "foo"),
- toBson(expression->evaluate(Document())));
+ toBson(expression->evaluate({}, &expCtx->variables)));
}
};
@@ -2229,7 +2232,9 @@ private:
TEST(ExpressionConstantTest, ConstantOfValueMissingRemovesField) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionConstant::create(expCtx, Value());
- assertBinaryEqual(BSONObj(), toBson(expression->evaluate(Document{{"foo", Value("bar"_sd)}})));
+ assertBinaryEqual(
+ BSONObj(),
+ toBson(expression->evaluate(Document{{"foo", Value("bar"_sd)}}, &expCtx->variables)));
}
TEST(ExpressionConstantTest, ConstantOfValueMissingSerializesToRemoveSystemVar) {
@@ -2332,11 +2337,11 @@ TEST(ExpressionPowTest, ThrowsWhenBaseZeroAndExpNegative) {
VariablesParseState vps = expCtx->variablesParseState;
const auto expr = Expression::parseExpression(expCtx, BSON("$pow" << BSON_ARRAY(0 << -5)), vps);
- ASSERT_THROWS([&] { expr->evaluate(Document()); }(), AssertionException);
+ ASSERT_THROWS([&] { expr->evaluate({}, &expCtx->variables); }(), AssertionException);
const auto exprWithLong =
Expression::parseExpression(expCtx, BSON("$pow" << BSON_ARRAY(0LL << -5LL)), vps);
- ASSERT_THROWS([&] { expr->evaluate(Document()); }(), AssertionException);
+ ASSERT_THROWS([&] { expr->evaluate({}, &expCtx->variables); }(), AssertionException);
}
TEST(ExpressionPowTest, LargeExponentValuesWithBaseOfOne) {
@@ -2434,7 +2439,9 @@ TEST(FieldPath, RemoveOptimizesToMissingValue) {
auto optimizedExpr = expression->optimize();
- ASSERT_VALUE_EQ(Value(), optimizedExpr->evaluate(Document(BSON("x" << BSON("y" << 123)))));
+ ASSERT_VALUE_EQ(
+ Value(),
+ optimizedExpr->evaluate(Document(BSON("x" << BSON("y" << 123))), &expCtx->variables));
}
TEST(FieldPath, NoOptimizationOnNormalPath) {
@@ -2556,7 +2563,7 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a");
- assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate(Document())));
+ assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate({}, &expCtx->variables)));
}
};
@@ -2566,8 +2573,9 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a");
- assertBinaryEqual(fromjson("{'':123}"),
- toBson(expression->evaluate(fromBson(BSON("a" << 123)))));
+ assertBinaryEqual(
+ fromjson("{'':123}"),
+ toBson(expression->evaluate(fromBson(BSON("a" << 123)), &expCtx->variables)));
}
};
@@ -2577,8 +2585,9 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
- assertBinaryEqual(fromjson("{}"),
- toBson(expression->evaluate(fromBson(fromjson("{a:null}")))));
+ assertBinaryEqual(
+ fromjson("{}"),
+ toBson(expression->evaluate(fromBson(fromjson("{a:null}")), &expCtx->variables)));
}
};
@@ -2588,8 +2597,9 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
- assertBinaryEqual(fromjson("{}"),
- toBson(expression->evaluate(fromBson(fromjson("{a:undefined}")))));
+ assertBinaryEqual(
+ fromjson("{}"),
+ toBson(expression->evaluate(fromBson(fromjson("{a:undefined}")), &expCtx->variables)));
}
};
@@ -2599,8 +2609,9 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
- assertBinaryEqual(fromjson("{}"),
- toBson(expression->evaluate(fromBson(fromjson("{z:1}")))));
+ assertBinaryEqual(
+ fromjson("{}"),
+ toBson(expression->evaluate(fromBson(fromjson("{z:1}")), &expCtx->variables)));
}
};
@@ -2610,7 +2621,9 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
- assertBinaryEqual(fromjson("{}"), toBson(expression->evaluate(fromBson(BSON("a" << 2)))));
+ assertBinaryEqual(
+ fromjson("{}"),
+ toBson(expression->evaluate(fromBson(BSON("a" << 2)), &expCtx->variables)));
}
};
@@ -2621,7 +2634,8 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(BSON("" << 55),
- toBson(expression->evaluate(fromBson(BSON("a" << BSON("b" << 55))))));
+ toBson(expression->evaluate(fromBson(BSON("a" << BSON("b" << 55))),
+ &expCtx->variables)));
}
};
@@ -2631,8 +2645,9 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
- assertBinaryEqual(fromjson("{}"),
- toBson(expression->evaluate(fromBson(BSON("a" << BSONObj())))));
+ assertBinaryEqual(
+ fromjson("{}"),
+ toBson(expression->evaluate(fromBson(BSON("a" << BSONObj())), &expCtx->variables)));
}
};
@@ -2642,8 +2657,9 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
- assertBinaryEqual(BSON("" << BSONArray()),
- toBson(expression->evaluate(fromBson(BSON("a" << BSONArray())))));
+ assertBinaryEqual(
+ BSON("" << BSONArray()),
+ toBson(expression->evaluate(fromBson(BSON("a" << BSONArray())), &expCtx->variables)));
}
};
@@ -2653,8 +2669,9 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
- assertBinaryEqual(fromjson("{'':[]}"),
- toBson(expression->evaluate(fromBson(fromjson("{a:[null]}")))));
+ assertBinaryEqual(
+ fromjson("{'':[]}"),
+ toBson(expression->evaluate(fromBson(fromjson("{a:[null]}")), &expCtx->variables)));
}
};
@@ -2665,7 +2682,8 @@ public:
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(fromjson("{'':[]}"),
- toBson(expression->evaluate(fromBson(fromjson("{a:[undefined]}")))));
+ toBson(expression->evaluate(fromBson(fromjson("{a:[undefined]}")),
+ &expCtx->variables)));
}
};
@@ -2675,8 +2693,9 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
- assertBinaryEqual(fromjson("{'':[]}"),
- toBson(expression->evaluate(fromBson(fromjson("{a:[1]}")))));
+ assertBinaryEqual(
+ fromjson("{'':[]}"),
+ toBson(expression->evaluate(fromBson(fromjson("{a:[1]}")), &expCtx->variables)));
}
};
@@ -2686,8 +2705,9 @@ public:
void run() {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
- assertBinaryEqual(fromjson("{'':[9]}"),
- toBson(expression->evaluate(fromBson(fromjson("{a:[{b:9}]}")))));
+ assertBinaryEqual(
+ fromjson("{'':[9]}"),
+ toBson(expression->evaluate(fromBson(fromjson("{a:[{b:9}]}")), &expCtx->variables)));
}
};
@@ -2699,7 +2719,8 @@ public:
intrusive_ptr<Expression> expression = ExpressionFieldPath::create(expCtx, "a.b");
assertBinaryEqual(fromjson("{'':[9,20]}"),
toBson(expression->evaluate(
- fromBson(fromjson("{a:[{b:9},null,undefined,{g:4},{b:20},{}]}")))));
+ fromBson(fromjson("{a:[{b:9},null,undefined,{g:4},{b:20},{}]}")),
+ &expCtx->variables)));
}
};
@@ -2714,7 +2735,8 @@ public:
"{b:{c:3}},"
"{b:[{c:4}]},"
"{b:[{c:[5]}]},"
- "{b:{c:[6,7]}}]}")))));
+ "{b:{c:[6,7]}}]}")),
+ &expCtx->variables)));
}
};
@@ -2884,20 +2906,24 @@ TEST(ParseObject, ShouldRejectExpressionAsTheSecondField) {
TEST(ExpressionObjectEvaluate, EmptyObjectShouldEvaluateToEmptyDocument) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+
auto object = ExpressionObject::create(expCtx, {});
- ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document()));
- ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document{{"a", 1}}));
- ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document{{"_id", "ID"_sd}}));
+ ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document(), &(expCtx->variables)));
+ ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document{{"a", 1}}, &(expCtx->variables)));
+ ASSERT_VALUE_EQ(Value(Document()),
+ object->evaluate(Document{{"_id", "ID"_sd}}, &(expCtx->variables)));
}
TEST(ExpressionObjectEvaluate, ShouldEvaluateEachField) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
auto object =
ExpressionObject::create(expCtx, {{"a", makeConstant(1)}, {"b", makeConstant(5)}});
- ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}), object->evaluate(Document()));
- ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}), object->evaluate(Document{{"a", 1}}));
ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}),
- object->evaluate(Document{{"_id", "ID"_sd}}));
+ object->evaluate(Document(), &(expCtx->variables)));
+ ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}),
+ object->evaluate(Document{{"a", 1}}, &(expCtx->variables)));
+ ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}),
+ object->evaluate(Document{{"_id", "ID"_sd}}, &(expCtx->variables)));
}
TEST(ExpressionObjectEvaluate, OrderOfFieldsInOutputShouldMatchOrderInSpecification) {
@@ -2908,7 +2934,8 @@ TEST(ExpressionObjectEvaluate, OrderOfFieldsInOutputShouldMatchOrderInSpecificat
{"c", ExpressionFieldPath::create(expCtx, "c")}});
ASSERT_VALUE_EQ(
Value(Document{{"a", "A"_sd}, {"b", "B"_sd}, {"c", "C"_sd}}),
- object->evaluate(Document{{"c", "C"_sd}, {"a", "A"_sd}, {"b", "B"_sd}, {"_id", "ID"_sd}}));
+ object->evaluate(Document{{"c", "C"_sd}, {"a", "A"_sd}, {"b", "B"_sd}, {"_id", "ID"_sd}},
+ &(expCtx->variables)));
}
TEST(ExpressionObjectEvaluate, ShouldRemoveFieldsThatHaveMissingValues) {
@@ -2916,8 +2943,8 @@ TEST(ExpressionObjectEvaluate, ShouldRemoveFieldsThatHaveMissingValues) {
auto object = ExpressionObject::create(expCtx,
{{"a", ExpressionFieldPath::create(expCtx, "a.b")},
{"b", ExpressionFieldPath::create(expCtx, "missing")}});
- ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document()));
- ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document{{"a", 1}}));
+ ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document(), &(expCtx->variables)));
+ ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document{{"a", 1}}, &(expCtx->variables)));
}
TEST(ExpressionObjectEvaluate, ShouldEvaluateFieldsWithinNestedObject) {
@@ -2928,20 +2955,21 @@ TEST(ExpressionObjectEvaluate, ShouldEvaluateFieldsWithinNestedObject) {
ExpressionObject::create(
expCtx,
{{"b", makeConstant(1)}, {"c", ExpressionFieldPath::create(expCtx, "_id")}})}});
- ASSERT_VALUE_EQ(Value(Document{{"a", Document{{"b", 1}}}}), object->evaluate(Document()));
+ ASSERT_VALUE_EQ(Value(Document{{"a", Document{{"b", 1}}}}),
+ object->evaluate(Document(), &(expCtx->variables)));
ASSERT_VALUE_EQ(Value(Document{{"a", Document{{"b", 1}, {"c", "ID"_sd}}}}),
- object->evaluate(Document{{"_id", "ID"_sd}}));
+ object->evaluate(Document{{"_id", "ID"_sd}}, &(expCtx->variables)));
}
TEST(ExpressionObjectEvaluate, ShouldEvaluateToEmptyDocumentIfAllFieldsAreMissing) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
auto object =
ExpressionObject::create(expCtx, {{"a", ExpressionFieldPath::create(expCtx, "missing")}});
- ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document()));
+ ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document(), &(expCtx->variables)));
auto objectWithNestedObject = ExpressionObject::create(expCtx, {{"nested", object}});
ASSERT_VALUE_EQ(Value(Document{{"nested", Document{}}}),
- objectWithNestedObject->evaluate(Document()));
+ objectWithNestedObject->evaluate(Document(), &(expCtx->variables)));
}
//
@@ -3050,7 +3078,7 @@ TEST(ExpressionObjectOptimizations, OptimizingAnObjectShouldOptimizeSubExpressio
auto expConstant =
dynamic_cast<ExpressionConstant*>(optimizedObject->getChildExpressions()[0].second.get());
ASSERT_TRUE(expConstant);
- ASSERT_VALUE_EQ(expConstant->evaluate(Document()), Value(3));
+ ASSERT_VALUE_EQ(expConstant->evaluate(Document(), &(expCtx->variables)), Value(3));
};
} // namespace Object
@@ -3067,11 +3095,13 @@ public:
VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
- ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()),
- toBson(expression->evaluate(fromBson(BSON("a" << 1)))));
+ ASSERT_BSONOBJ_EQ(
+ BSON("" << expectedResult()),
+ toBson(expression->evaluate(fromBson(BSON("a" << 1)), &expCtx->variables)));
intrusive_ptr<Expression> optimized = expression->optimize();
- ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()),
- toBson(optimized->evaluate(fromBson(BSON("a" << 1)))));
+ ASSERT_BSONOBJ_EQ(
+ BSON("" << expectedResult()),
+ toBson(optimized->evaluate(fromBson(BSON("a" << 1)), &expCtx->variables)));
}
protected:
@@ -3579,7 +3609,7 @@ public:
VariablesParseState vps = expCtx->variablesParseState;
const intrusive_ptr<Expression> expr =
Expression::parseExpression(expCtx, obj, vps);
- Value result = expr->evaluate(Document());
+ Value result = expr->evaluate({}, &expCtx->variables);
if (result.getType() == Array) {
result = sortSet(result);
}
@@ -3606,7 +3636,7 @@ public:
// same
const intrusive_ptr<Expression> expr =
Expression::parseExpression(expCtx, obj, vps);
- expr->evaluate(Document());
+ expr->evaluate(Document(), &expCtx->variables);
},
AssertionException);
}
@@ -3888,7 +3918,8 @@ private:
VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec), expressionToBson(expression));
- ASSERT_BSONOBJ_EQ(BSON("" << expectedResult), toBson(expression->evaluate(Document())));
+ ASSERT_BSONOBJ_EQ(BSON("" << expectedResult),
+ toBson(expression->evaluate({}, &expCtx->variables)));
}
};
@@ -4014,7 +4045,8 @@ public:
VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
- ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), toBson(expression->evaluate(Document())));
+ ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()),
+ toBson(expression->evaluate({}, &expCtx->variables)));
}
protected:
@@ -4120,7 +4152,7 @@ TEST(ExpressionSubstrCPTest, DoesThrowWithBadContinuationByte) {
const auto continuationByte = "\x80\x00"_sd;
const auto expr = Expression::parseExpression(
expCtx, BSON("$substrCP" << BSON_ARRAY(continuationByte << 0 << 1)), vps);
- ASSERT_THROWS({ expr->evaluate(Document()); }, AssertionException);
+ ASSERT_THROWS({ expr->evaluate(Document(), &expCtx->variables); }, AssertionException);
}
TEST(ExpressionSubstrCPTest, DoesThrowWithInvalidLeadingByte) {
@@ -4130,7 +4162,7 @@ TEST(ExpressionSubstrCPTest, DoesThrowWithInvalidLeadingByte) {
const auto leadingByte = "\xFF\x00"_sd;
const auto expr = Expression::parseExpression(
expCtx, BSON("$substrCP" << BSON_ARRAY(leadingByte << 0 << 1)), vps);
- ASSERT_THROWS({ expr->evaluate(Document()); }, AssertionException);
+ ASSERT_THROWS({ expr->evaluate(Document(), &expCtx->variables); }, AssertionException);
}
TEST(ExpressionSubstrCPTest, WithStandardValue) {
@@ -4404,7 +4436,8 @@ public:
VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
- ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), toBson(expression->evaluate(Document())));
+ ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()),
+ toBson(expression->evaluate({}, &expCtx->variables)));
}
protected:
@@ -4461,7 +4494,8 @@ public:
VariablesParseState vps = expCtx->variablesParseState;
intrusive_ptr<Expression> expression = Expression::parseOperand(expCtx, specElement, vps);
ASSERT_BSONOBJ_EQ(constify(spec()), expressionToBson(expression));
- ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()), toBson(expression->evaluate(Document())));
+ ASSERT_BSONOBJ_EQ(BSON("" << expectedResult()),
+ toBson(expression->evaluate({}, &expCtx->variables)));
}
protected:
@@ -4523,7 +4557,7 @@ public:
VariablesParseState vps = expCtx->variablesParseState;
const intrusive_ptr<Expression> expr =
Expression::parseExpression(expCtx, obj, vps);
- const Value result = expr->evaluate(Document());
+ const Value result = expr->evaluate({}, &expCtx->variables);
if (ValueComparator().evaluate(result != expected)) {
string errMsg = str::stream()
<< "for expression " << field.first.toString() << " with argument "
@@ -4547,7 +4581,7 @@ public:
// same
const intrusive_ptr<Expression> expr =
Expression::parseExpression(expCtx, obj, vps);
- expr->evaluate(Document());
+ expr->evaluate(Document(), &expCtx->variables);
},
AssertionException);
}
@@ -5073,14 +5107,16 @@ TEST_F(DateExpressionTest, RejectsArraysWithinObjectSpecification) {
// It will parse as an ExpressionArray, and fail at runtime.
auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
auto contextDoc = Document{{"_id", 0}};
- ASSERT_THROWS_CODE(dateExp->evaluate(contextDoc), AssertionException, 16006);
+ ASSERT_THROWS_CODE(
+ dateExp->evaluate(contextDoc, &expCtx->variables), AssertionException, 16006);
// Test that it rejects an array for the timezone option.
spec =
BSON(expName << BSON("date" << Date_t{} << "timezone" << BSON_ARRAY("Europe/London")));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
contextDoc = Document{{"_id", 0}};
- ASSERT_THROWS_CODE(dateExp->evaluate(contextDoc), AssertionException, 40533);
+ ASSERT_THROWS_CODE(
+ dateExp->evaluate(contextDoc, &expCtx->variables), AssertionException, 40533);
}
}
@@ -5090,7 +5126,8 @@ TEST_F(DateExpressionTest, RejectsTypesThatCannotCoerceToDate) {
BSONObj spec = BSON(expName << "$stringField");
auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
auto contextDoc = Document{{"stringField", "string"_sd}};
- ASSERT_THROWS_CODE(dateExp->evaluate(contextDoc), AssertionException, 16006);
+ ASSERT_THROWS_CODE(
+ dateExp->evaluate(contextDoc, &expCtx->variables), AssertionException, 16006);
}
}
@@ -5100,7 +5137,7 @@ TEST_F(DateExpressionTest, AcceptsObjectIds) {
BSONObj spec = BSON(expName << "$oid");
auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
auto contextDoc = Document{{"oid", OID::gen()}};
- dateExp->evaluate(contextDoc); // Should not throw.
+ dateExp->evaluate(contextDoc, &expCtx->variables); // Should not throw.
}
}
@@ -5110,7 +5147,7 @@ TEST_F(DateExpressionTest, AcceptsTimestamps) {
BSONObj spec = BSON(expName << "$ts");
auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
auto contextDoc = Document{{"ts", Timestamp{Date_t{}}}};
- dateExp->evaluate(contextDoc); // Should not throw.
+ dateExp->evaluate(contextDoc, &expCtx->variables); // Should not throw.
}
}
@@ -5121,7 +5158,8 @@ TEST_F(DateExpressionTest, RejectsNonStringTimezone) {
<< "$intField"));
auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
auto contextDoc = Document{{"intField", 4}};
- ASSERT_THROWS_CODE(dateExp->evaluate(contextDoc), AssertionException, 40533);
+ ASSERT_THROWS_CODE(
+ dateExp->evaluate(contextDoc, &expCtx->variables), AssertionException, 40533);
}
}
@@ -5132,7 +5170,8 @@ TEST_F(DateExpressionTest, RejectsUnrecognizedTimeZoneSpecification) {
<< "UNRECOGNIZED!"));
auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
auto contextDoc = Document{{"_id", 0}};
- ASSERT_THROWS_CODE(dateExp->evaluate(contextDoc), AssertionException, 40485);
+ ASSERT_THROWS_CODE(
+ dateExp->evaluate(contextDoc, &expCtx->variables), AssertionException, 40485);
}
}
@@ -5217,7 +5256,7 @@ TEST_F(DateExpressionTest, DoesRespectTimeZone) {
<< "America/New_York"));
auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
auto contextDoc = Document{{"_id", 0}};
- dateExp->evaluate(contextDoc); // Should not throw.
+ dateExp->evaluate(contextDoc, &expCtx->variables); // Should not throw.
}
// Make sure the time zone is used during evaluation.
@@ -5225,14 +5264,14 @@ TEST_F(DateExpressionTest, DoesRespectTimeZone) {
auto specWithoutTimezone = BSON("$hour" << BSON("date" << date));
auto hourWithoutTimezone =
Expression::parseExpression(expCtx, specWithoutTimezone, expCtx->variablesParseState)
- ->evaluate({});
+ ->evaluate({}, &expCtx->variables);
ASSERT_VALUE_EQ(hourWithoutTimezone, Value(19));
auto specWithTimezone = BSON("$hour" << BSON("date" << date << "timezone"
<< "America/New_York"));
auto hourWithTimezone =
Expression::parseExpression(expCtx, specWithTimezone, expCtx->variablesParseState)
- ->evaluate({});
+ ->evaluate({}, &expCtx->variables);
ASSERT_VALUE_EQ(hourWithTimezone, Value(15));
}
@@ -5247,30 +5286,30 @@ TEST_F(DateExpressionTest, DoesResultInNullIfGivenNullishInput) {
auto spec = BSON(expName << BSON("date"
<< "$missing"));
auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc));
+ ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables));
spec = BSON(expName << BSON("date" << BSONNULL));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc));
+ ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables));
spec = BSON(expName << BSON("date" << BSONUndefined));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc));
+ ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables));
// Test that the expression results in null if the date is present but the timezone is
// nullish.
spec = BSON(expName << BSON("date" << Date_t{} << "timezone"
<< "$missing"));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc));
+ ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables));
spec = BSON(expName << BSON("date" << Date_t{} << "timezone" << BSONNULL));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc));
+ ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables));
spec = BSON(expName << BSON("date" << Date_t{} << "timezone" << BSONUndefined));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc));
+ ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables));
// Test that the expression results in null if the date and timezone both nullish.
spec = BSON(expName << BSON("date"
@@ -5278,7 +5317,7 @@ TEST_F(DateExpressionTest, DoesResultInNullIfGivenNullishInput) {
<< "timezone"
<< BSONUndefined));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc));
+ ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables));
// Test that the expression results in null if the date is nullish and timezone is present.
spec = BSON(expName << BSON("date"
@@ -5286,7 +5325,7 @@ TEST_F(DateExpressionTest, DoesResultInNullIfGivenNullishInput) {
<< "timezone"
<< "Europe/London"));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc));
+ ASSERT_VALUE_EQ(Value(BSONNULL), dateExp->evaluate(contextDoc, &expCtx->variables));
}
}
@@ -5421,7 +5460,7 @@ TEST_F(ExpressionDateFromStringTest, OptimizesToConstantIfAllInputsAreConstant)
ASSERT(dynamic_cast<ExpressionConstant*>(dateExp->optimize().get()));
Date_t dateVal = Date_t::fromMillisSinceEpoch(1499173797000);
- ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}));
+ ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}, &expCtx->variables));
// Test that it becomes a constant with the dateString and timezone being a constant.
spec = BSON("$dateFromString" << BSON("dateString"
@@ -5432,7 +5471,7 @@ TEST_F(ExpressionDateFromStringTest, OptimizesToConstantIfAllInputsAreConstant)
ASSERT(dynamic_cast<ExpressionConstant*>(dateExp->optimize().get()));
dateVal = Date_t::fromMillisSinceEpoch(1499170197000);
- ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}));
+ ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}, &expCtx->variables));
// Test that it does *not* become a constant if dateString is not a constant.
spec = BSON("$dateFromString" << BSON("dateString"
@@ -5455,7 +5494,7 @@ TEST_F(ExpressionDateFromStringTest, RejectsUnparsableString) {
auto spec = BSON("$dateFromString" << BSON("dateString"
<< "60.Monday1770/06:59"));
auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40553);
+ ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40553);
}
TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInString) {
@@ -5464,12 +5503,12 @@ TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInString) {
auto spec = BSON("$dateFromString" << BSON("dateString"
<< "2017-07-13T10:02:57 Europe/London"));
auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40553);
+ ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40553);
spec = BSON("$dateFromString" << BSON("dateString"
<< "July 4, 2017 Europe/London"));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40553);
+ ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40553);
}
TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInStringAndArgument) {
@@ -5481,7 +5520,7 @@ TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInStringAndArgument) {
<< "timezone"
<< "Europe/London"));
auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40551);
+ ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40551);
// Test with timezone abbreviation and timezone
spec = BSON("$dateFromString" << BSON("dateString"
@@ -5489,7 +5528,7 @@ TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInStringAndArgument) {
<< "timezone"
<< "Europe/London"));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40551);
+ ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40551);
// Test with GMT offset and timezone
spec = BSON("$dateFromString" << BSON("dateString"
@@ -5497,7 +5536,7 @@ TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInStringAndArgument) {
<< "timezone"
<< "Europe/London"));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40554);
+ ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40554);
// Test with GMT offset and GMT timezone
spec = BSON("$dateFromString" << BSON("dateString"
@@ -5505,7 +5544,7 @@ TEST_F(ExpressionDateFromStringTest, RejectsTimeZoneInStringAndArgument) {
<< "timezone"
<< "GMT"));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
- ASSERT_THROWS_CODE(dateExp->evaluate({}), AssertionException, 40554);
+ ASSERT_THROWS_CODE(dateExp->evaluate({}, &expCtx->variables), AssertionException, 40554);
}
TEST_F(ExpressionDateFromStringTest, ReadWithUTCOffset) {
@@ -5517,7 +5556,7 @@ TEST_F(ExpressionDateFromStringTest, ReadWithUTCOffset) {
<< "-01:00"));
auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
auto dateVal = Date_t::fromMillisSinceEpoch(1501242472912);
- ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}));
+ ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}, &expCtx->variables));
spec = BSON("$dateFromString" << BSON("dateString"
<< "2017-07-28T10:47:52.912"
@@ -5525,7 +5564,7 @@ TEST_F(ExpressionDateFromStringTest, ReadWithUTCOffset) {
<< "+01:00"));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
dateVal = Date_t::fromMillisSinceEpoch(1501235272912);
- ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}));
+ ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}, &expCtx->variables));
spec = BSON("$dateFromString" << BSON("dateString"
<< "2017-07-28T10:47:52.912"
@@ -5533,7 +5572,7 @@ TEST_F(ExpressionDateFromStringTest, ReadWithUTCOffset) {
<< "+0445"));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
dateVal = Date_t::fromMillisSinceEpoch(1501221772912);
- ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}));
+ ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}, &expCtx->variables));
spec = BSON("$dateFromString" << BSON("dateString"
<< "2017-07-28T10:47:52.912"
@@ -5541,7 +5580,7 @@ TEST_F(ExpressionDateFromStringTest, ReadWithUTCOffset) {
<< "+10:45"));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
dateVal = Date_t::fromMillisSinceEpoch(1501200172912);
- ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}));
+ ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}, &expCtx->variables));
spec = BSON("$dateFromString" << BSON("dateString"
<< "1945-07-28T10:47:52.912"
@@ -5549,7 +5588,7 @@ TEST_F(ExpressionDateFromStringTest, ReadWithUTCOffset) {
<< "-08:00"));
dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState);
dateVal = Date_t::fromMillisSinceEpoch(-770879527088);
- ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}));
+ ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{}, &expCtx->variables));
}
} // namespace ExpressionDateFromStringTest
diff --git a/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp b/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp
index 134af30120d..e6e26c367cd 100644
--- a/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp
+++ b/src/mongo/db/pipeline/granularity_rounder_powers_of_two.cpp
@@ -82,7 +82,8 @@ Value GranularityRounderPowersOfTwo::roundUp(Value value) {
exp = Value(63 - countLeadingZeros64(number) + 1);
}
- return ExpressionPow::create(getExpCtx(), Value(2), exp)->evaluate(Document());
+ return ExpressionPow::create(getExpCtx(), Value(2), exp)
+ ->evaluate(Document(), &getExpCtx()->variables);
}
Value GranularityRounderPowersOfTwo::roundDown(Value value) {
@@ -114,7 +115,8 @@ Value GranularityRounderPowersOfTwo::roundDown(Value value) {
}
}
- return ExpressionPow::create(getExpCtx(), Value(2), exp)->evaluate(Document());
+ return ExpressionPow::create(getExpCtx(), Value(2), exp)
+ ->evaluate(Document(), &getExpCtx()->variables);
}
string GranularityRounderPowersOfTwo::getName() {
diff --git a/src/mongo/db/pipeline/parsed_inclusion_projection.cpp b/src/mongo/db/pipeline/parsed_inclusion_projection.cpp
index d9c8c19373c..cc288fae9ea 100644
--- a/src/mongo/db/pipeline/parsed_inclusion_projection.cpp
+++ b/src/mongo/db/pipeline/parsed_inclusion_projection.cpp
@@ -152,7 +152,10 @@ void InclusionNode::addComputedFields(MutableDocument* outputDoc, const Document
} else {
auto expressionIt = _expressions.find(field);
invariant(expressionIt != _expressions.end());
- outputDoc->setField(field, expressionIt->second->evaluate(root));
+ outputDoc->setField(
+ field,
+ expressionIt->second->evaluate(
+ root, &(expressionIt->second->getExpressionContext()->variables)));
}
}
}