summaryrefslogtreecommitdiff
path: root/src/mongo/db/pipeline
diff options
context:
space:
mode:
authorTed Tuckman <ted.tuckman@mongodb.com>2020-08-19 10:04:10 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-08-26 12:52:51 +0000
commit199df5ec6c6b8c0fc8e74dfad8a074547b79d3f2 (patch)
treecd86a8fb93148a0bcc221313681c58e52031661d /src/mongo/db/pipeline
parent4dc5ad1c019849f2084bb9d30419d303679e6afe (diff)
downloadmongo-199df5ec6c6b8c0fc8e74dfad8a074547b79d3f2.tar.gz
SERVER-49927 Add $FieldPath to grammar
Diffstat (limited to 'src/mongo/db/pipeline')
-rw-r--r--src/mongo/db/pipeline/SConscript10
-rw-r--r--src/mongo/db/pipeline/document_source_graph_lookup_test.cpp224
-rw-r--r--src/mongo/db/pipeline/document_source_group.cpp2
-rw-r--r--src/mongo/db/pipeline/document_source_lookup.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_merge.cpp3
-rw-r--r--src/mongo/db/pipeline/expression.cpp25
-rw-r--r--src/mongo/db/pipeline/expression.h12
-rw-r--r--src/mongo/db/pipeline/expression_field_path_test.cpp56
-rw-r--r--src/mongo/db/pipeline/expression_nary_test.cpp6
-rw-r--r--src/mongo/db/pipeline/expression_object_test.cpp32
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp10
-rw-r--r--src/mongo/db/pipeline/variable_validation.cpp82
-rw-r--r--src/mongo/db/pipeline/variable_validation.h40
-rw-r--r--src/mongo/db/pipeline/variables.cpp53
-rw-r--r--src/mongo/db/pipeline/variables.h7
15 files changed, 342 insertions, 223 deletions
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index 31e47662a07..887fb93486b 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -55,6 +55,15 @@ env.Library(
)
env.Library(
+ target='variable_validation',
+ source=[
+ "variable_validation.cpp",
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ ]
+)
+env.Library(
target='expression_context',
source=[
'expression.cpp',
@@ -80,6 +89,7 @@ env.Library(
'$BUILD_DIR/mongo/util/summation',
'aggregation_request',
'dependencies',
+ 'variable_validation',
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/db/vector_clock',
diff --git a/src/mongo/db/pipeline/document_source_graph_lookup_test.cpp b/src/mongo/db/pipeline/document_source_graph_lookup_test.cpp
index 5d4fe741a6c..c59ed9c2ea4 100644
--- a/src/mongo/db/pipeline/document_source_graph_lookup_test.cpp
+++ b/src/mongo/db/pipeline/document_source_graph_lookup_test.cpp
@@ -87,17 +87,17 @@ TEST_F(DocumentSourceGraphLookUpTest,
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
- auto graphLookupStage =
- DocumentSourceGraphLookUp::create(expCtx,
- fromNs,
- "results",
- "from",
- "to",
- ExpressionFieldPath::create(expCtx.get(), "_id"),
- boost::none,
- boost::none,
- boost::none,
- boost::none);
+ auto graphLookupStage = DocumentSourceGraphLookUp::create(
+ expCtx,
+ fromNs,
+ "results",
+ "from",
+ "to",
+ ExpressionFieldPath::deprecatedCreate(expCtx.get(), "_id"),
+ boost::none,
+ boost::none,
+ boost::none,
+ boost::none);
graphLookupStage->setSource(inputMock.get());
ASSERT_THROWS_CODE(graphLookupStage->getNext(), AssertionException, 40271);
}
@@ -116,17 +116,17 @@ TEST_F(DocumentSourceGraphLookUpTest,
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
- auto graphLookupStage =
- DocumentSourceGraphLookUp::create(expCtx,
- fromNs,
- "results",
- "from",
- "to",
- ExpressionFieldPath::create(expCtx.get(), "_id"),
- boost::none,
- boost::none,
- boost::none,
- boost::none);
+ auto graphLookupStage = DocumentSourceGraphLookUp::create(
+ expCtx,
+ fromNs,
+ "results",
+ "from",
+ "to",
+ ExpressionFieldPath::deprecatedCreate(expCtx.get(), "_id"),
+ boost::none,
+ boost::none,
+ boost::none,
+ boost::none);
graphLookupStage->setSource(inputMock.get());
ASSERT_THROWS_CODE(graphLookupStage->getNext(), AssertionException, 40271);
@@ -146,17 +146,17 @@ TEST_F(DocumentSourceGraphLookUpTest,
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
auto unwindStage = DocumentSourceUnwind::create(expCtx, "results", false, boost::none);
- auto graphLookupStage =
- DocumentSourceGraphLookUp::create(expCtx,
- fromNs,
- "results",
- "from",
- "to",
- ExpressionFieldPath::create(expCtx.get(), "_id"),
- boost::none,
- boost::none,
- boost::none,
- unwindStage);
+ auto graphLookupStage = DocumentSourceGraphLookUp::create(
+ expCtx,
+ fromNs,
+ "results",
+ "from",
+ "to",
+ ExpressionFieldPath::deprecatedCreate(expCtx.get(), "_id"),
+ boost::none,
+ boost::none,
+ boost::none,
+ unwindStage);
graphLookupStage->setSource(inputMock.get());
ASSERT_THROWS_CODE(graphLookupStage->getNext(), AssertionException, 40271);
@@ -189,17 +189,17 @@ TEST_F(DocumentSourceGraphLookUpTest,
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
- auto graphLookupStage =
- DocumentSourceGraphLookUp::create(expCtx,
- fromNs,
- "results",
- "from",
- "to",
- ExpressionFieldPath::create(expCtx.get(), "_id"),
- boost::none,
- boost::none,
- boost::none,
- boost::none);
+ auto graphLookupStage = DocumentSourceGraphLookUp::create(
+ expCtx,
+ fromNs,
+ "results",
+ "from",
+ "to",
+ ExpressionFieldPath::deprecatedCreate(expCtx.get(), "_id"),
+ boost::none,
+ boost::none,
+ boost::none,
+ boost::none);
graphLookupStage->setSource(inputMock.get());
graphLookupStage->setSource(inputMock.get());
@@ -253,17 +253,17 @@ TEST_F(DocumentSourceGraphLookUpTest, ShouldPropagatePauses) {
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
- auto graphLookupStage =
- DocumentSourceGraphLookUp::create(expCtx,
- fromNs,
- "results",
- "from",
- "to",
- ExpressionFieldPath::create(expCtx.get(), "startPoint"),
- boost::none,
- boost::none,
- boost::none,
- boost::none);
+ auto graphLookupStage = DocumentSourceGraphLookUp::create(
+ expCtx,
+ fromNs,
+ "results",
+ "from",
+ "to",
+ ExpressionFieldPath::deprecatedCreate(expCtx.get(), "startPoint"),
+ boost::none,
+ boost::none,
+ boost::none,
+ boost::none);
graphLookupStage->setSource(inputMock.get());
@@ -329,17 +329,17 @@ TEST_F(DocumentSourceGraphLookUpTest, ShouldPropagatePausesWhileUnwinding) {
auto unwindStage = DocumentSourceUnwind::create(
expCtx, "results", preserveNullAndEmptyArrays, includeArrayIndex);
- auto graphLookupStage =
- DocumentSourceGraphLookUp::create(expCtx,
- fromNs,
- "results",
- "from",
- "to",
- ExpressionFieldPath::create(expCtx.get(), "startPoint"),
- boost::none,
- boost::none,
- boost::none,
- unwindStage);
+ auto graphLookupStage = DocumentSourceGraphLookUp::create(
+ expCtx,
+ fromNs,
+ "results",
+ "from",
+ "to",
+ ExpressionFieldPath::deprecatedCreate(expCtx.get(), "startPoint"),
+ boost::none,
+ boost::none,
+ boost::none,
+ unwindStage);
graphLookupStage->setSource(inputMock.get());
@@ -388,17 +388,17 @@ TEST_F(DocumentSourceGraphLookUpTest, GraphLookupShouldReportAsFieldIsModified)
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface =
std::make_shared<MockMongoInterface>(std::deque<DocumentSource::GetNextResult>{});
- auto graphLookupStage =
- DocumentSourceGraphLookUp::create(expCtx,
- fromNs,
- "results",
- "from",
- "to",
- ExpressionFieldPath::create(expCtx.get(), "startPoint"),
- boost::none,
- boost::none,
- boost::none,
- boost::none);
+ auto graphLookupStage = DocumentSourceGraphLookUp::create(
+ expCtx,
+ fromNs,
+ "results",
+ "from",
+ "to",
+ ExpressionFieldPath::deprecatedCreate(expCtx.get(), "startPoint"),
+ boost::none,
+ boost::none,
+ boost::none,
+ boost::none);
auto modifiedPaths = graphLookupStage->getModifiedPaths();
ASSERT(modifiedPaths.type == DocumentSource::GetModPathsReturn::Type::kFiniteSet);
@@ -415,17 +415,17 @@ TEST_F(DocumentSourceGraphLookUpTest, GraphLookupShouldReportFieldsModifiedByAbs
std::make_shared<MockMongoInterface>(std::deque<DocumentSource::GetNextResult>{});
auto unwindStage =
DocumentSourceUnwind::create(expCtx, "results", false, std::string("arrIndex"));
- auto graphLookupStage =
- DocumentSourceGraphLookUp::create(expCtx,
- fromNs,
- "results",
- "from",
- "to",
- ExpressionFieldPath::create(expCtx.get(), "startPoint"),
- boost::none,
- boost::none,
- boost::none,
- unwindStage);
+ auto graphLookupStage = DocumentSourceGraphLookUp::create(
+ expCtx,
+ fromNs,
+ "results",
+ "from",
+ "to",
+ ExpressionFieldPath::deprecatedCreate(expCtx.get(), "startPoint"),
+ boost::none,
+ boost::none,
+ boost::none,
+ unwindStage);
auto modifiedPaths = graphLookupStage->getModifiedPaths();
ASSERT(modifiedPaths.type == DocumentSource::GetModPathsReturn::Type::kFiniteSet);
@@ -455,8 +455,8 @@ TEST_F(DocumentSourceGraphLookUpTest, GraphLookupWithComparisonExpressionForStar
"to",
ExpressionCompare::create(expCtx.get(),
ExpressionCompare::GT,
- ExpressionFieldPath::create(expCtx.get(), "a"),
- ExpressionFieldPath::create(expCtx.get(), "b")),
+ ExpressionFieldPath::deprecatedCreate(expCtx.get(), "a"),
+ ExpressionFieldPath::deprecatedCreate(expCtx.get(), "b")),
boost::none,
boost::none,
boost::none,
@@ -510,17 +510,17 @@ TEST_F(DocumentSourceGraphLookUpTest, ShouldExpandArraysAtEndOfConnectFromField)
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
- auto graphLookupStage =
- DocumentSourceGraphLookUp::create(expCtx,
- fromNs,
- "results",
- "to",
- "_id",
- ExpressionFieldPath::create(expCtx.get(), "startVal"),
- boost::none,
- boost::none,
- boost::none,
- boost::none);
+ auto graphLookupStage = DocumentSourceGraphLookUp::create(
+ expCtx,
+ fromNs,
+ "results",
+ "to",
+ "_id",
+ ExpressionFieldPath::deprecatedCreate(expCtx.get(), "startVal"),
+ boost::none,
+ boost::none,
+ boost::none,
+ boost::none);
graphLookupStage->setSource(inputMock.get());
graphLookupStage->setSource(inputMock.get());
@@ -583,17 +583,17 @@ TEST_F(DocumentSourceGraphLookUpTest, ShouldNotExpandArraysWithinArraysAtEndOfCo
expCtx->setResolvedNamespaces(StringMap<ExpressionContext::ResolvedNamespace>{
{fromNs.coll().toString(), {fromNs, std::vector<BSONObj>()}}});
expCtx->mongoProcessInterface = std::make_shared<MockMongoInterface>(std::move(fromContents));
- auto graphLookupStage =
- DocumentSourceGraphLookUp::create(expCtx,
- fromNs,
- "results",
- "connectedTo",
- "coordinate",
- ExpressionFieldPath::create(expCtx.get(), "startVal"),
- boost::none,
- boost::none,
- boost::none,
- boost::none);
+ auto graphLookupStage = DocumentSourceGraphLookUp::create(
+ expCtx,
+ fromNs,
+ "results",
+ "connectedTo",
+ "coordinate",
+ ExpressionFieldPath::deprecatedCreate(expCtx.get(), "startVal"),
+ boost::none,
+ boost::none,
+ boost::none,
+ boost::none);
graphLookupStage->setSource(inputMock.get());
graphLookupStage->setSource(inputMock.get());
diff --git a/src/mongo/db/pipeline/document_source_group.cpp b/src/mongo/db/pipeline/document_source_group.cpp
index cf31fd10c6e..d53ca4fb3cf 100644
--- a/src/mongo/db/pipeline/document_source_group.cpp
+++ b/src/mongo/db/pipeline/document_source_group.cpp
@@ -826,7 +826,7 @@ DocumentSourceGroup::rewriteGroupAsTransformOnFirstDocument() const {
// The _id field can be specified either as a fieldpath (ex. _id: "$a") or as a singleton
// object (ex. _id: {v: "$a"}).
if (_idFieldNames.empty()) {
- idField = ExpressionFieldPath::create(pExpCtx.get(), groupId);
+ idField = ExpressionFieldPath::deprecatedCreate(pExpCtx.get(), groupId);
} else {
invariant(_idFieldNames.size() == 1);
idField = ExpressionObject::create(pExpCtx.get(),
diff --git a/src/mongo/db/pipeline/document_source_lookup.cpp b/src/mongo/db/pipeline/document_source_lookup.cpp
index 25fe9668ce0..94a4071d4ad 100644
--- a/src/mongo/db/pipeline/document_source_lookup.cpp
+++ b/src/mongo/db/pipeline/document_source_lookup.cpp
@@ -42,6 +42,7 @@
#include "mongo/db/pipeline/document_source_merge_gen.h"
#include "mongo/db/pipeline/expression.h"
#include "mongo/db/pipeline/expression_context.h"
+#include "mongo/db/pipeline/variable_validation.h"
#include "mongo/db/query/query_knobs_gen.h"
#include "mongo/platform/overflow_arithmetic.h"
#include "mongo/util/fail_point.h"
@@ -158,7 +159,7 @@ DocumentSourceLookUp::DocumentSourceLookUp(NamespaceString fromNs,
for (auto&& varElem : letVariables) {
const auto varName = varElem.fieldNameStringData();
- Variables::validateNameForUserWrite(varName);
+ variableValidation::validateNameForUserWrite(varName);
_letVariables.emplace_back(
varName.toString(),
diff --git a/src/mongo/db/pipeline/document_source_merge.cpp b/src/mongo/db/pipeline/document_source_merge.cpp
index 2dd399d6fce..2b8223d8753 100644
--- a/src/mongo/db/pipeline/document_source_merge.cpp
+++ b/src/mongo/db/pipeline/document_source_merge.cpp
@@ -40,6 +40,7 @@
#include "mongo/db/curop_failpoint_helpers.h"
#include "mongo/db/ops/write_ops.h"
#include "mongo/db/pipeline/document_path_support.h"
+#include "mongo/db/pipeline/variable_validation.h"
#include "mongo/logv2/log.h"
namespace mongo {
@@ -370,7 +371,7 @@ DocumentSourceMerge::DocumentSourceMerge(NamespaceString outputNs,
for (auto&& varElem : *letVariables) {
const auto varName = varElem.fieldNameStringData();
- Variables::validateNameForUserWrite(varName);
+ variableValidation::validateNameForUserWrite(varName);
_letVariables->emplace(
varName.toString(),
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 71d24d13528..c6c0823eee7 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -45,6 +45,7 @@
#include "mongo/db/hasher.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/pipeline/expression_context.h"
+#include "mongo/db/pipeline/variable_validation.h"
#include "mongo/db/query/datetime/date_time_support.h"
#include "mongo/platform/bits.h"
#include "mongo/platform/decimal128.h"
@@ -2045,8 +2046,8 @@ Expression::ComputedPaths ExpressionObject::getComputedPaths(const std::string&
/* --------------------- ExpressionFieldPath --------------------------- */
// this is the old deprecated version only used by tests not using variables
-intrusive_ptr<ExpressionFieldPath> ExpressionFieldPath::create(ExpressionContext* const expCtx,
- const string& fieldPath) {
+intrusive_ptr<ExpressionFieldPath> ExpressionFieldPath::deprecatedCreate(
+ ExpressionContext* const expCtx, const string& fieldPath) {
return new ExpressionFieldPath(expCtx, "CURRENT." + fieldPath, Variables::kRootId);
}
@@ -2066,7 +2067,7 @@ intrusive_ptr<ExpressionFieldPath> ExpressionFieldPath::parse(ExpressionContext*
const StringData rawSD = raw;
const StringData fieldPath = rawSD.substr(2); // strip off $$
const StringData varName = fieldPath.substr(0, fieldPath.find('.'));
- Variables::validateNameForUserRead(varName);
+ variableValidation::validateNameForUserRead(varName);
auto varId = vps.getVariable(varName);
return new ExpressionFieldPath(expCtx, fieldPath.toString(), varId);
} else {
@@ -2076,6 +2077,18 @@ intrusive_ptr<ExpressionFieldPath> ExpressionFieldPath::parse(ExpressionContext*
}
}
+intrusive_ptr<ExpressionFieldPath> ExpressionFieldPath::createPathFromString(
+ ExpressionContext* const expCtx, const string& raw, const VariablesParseState& vps) {
+ return new ExpressionFieldPath(expCtx, "CURRENT." + raw, vps.getVariable("CURRENT"));
+}
+intrusive_ptr<ExpressionFieldPath> ExpressionFieldPath::createVarFromString(
+ ExpressionContext* const expCtx, const string& raw, const VariablesParseState& vps) {
+ const auto rawSD = StringData{raw};
+ const StringData varName = rawSD.substr(0, rawSD.find('.'));
+ auto varId = vps.getVariable(varName);
+ return new ExpressionFieldPath(expCtx, raw, varId);
+}
+
ExpressionFieldPath::ExpressionFieldPath(ExpressionContext* const expCtx,
const string& theFieldPath,
Variables::Id variable)
@@ -2249,7 +2262,7 @@ intrusive_ptr<Expression> ExpressionFilter::parse(ExpressionContext* const expCt
// If "as" is not specified, then use "this" by default.
auto varName = asElem.eoo() ? "this" : asElem.str();
- Variables::validateNameForUserWrite(varName);
+ variableValidation::validateNameForUserWrite(varName);
Variables::Id varId = vpsSub.defineVariable(varName);
// Parse "cond", has access to "as" variable.
@@ -2379,7 +2392,7 @@ intrusive_ptr<Expression> ExpressionLet::parse(ExpressionContext* const expCtx,
std::vector<Variables::Id> orderedVariableIds;
for (auto&& varElem : varsObj) {
const string varName = varElem.fieldName();
- Variables::validateNameForUserWrite(varName);
+ variableValidation::validateNameForUserWrite(varName);
Variables::Id id = vpsSub.defineVariable(varName);
orderedVariableIds.push_back(id);
@@ -2491,7 +2504,7 @@ intrusive_ptr<Expression> ExpressionMap::parse(ExpressionContext* const expCtx,
// If "as" is not specified, then use "this" by default.
auto varName = asElem.eoo() ? "this" : asElem.str();
- Variables::validateNameForUserWrite(varName);
+ variableValidation::validateNameForUserWrite(varName);
Variables::Id varId = vpsSub.defineVariable(varName);
// parse "in"
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index a21f4c8bd39..fc6f16c0eb8 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -1384,13 +1384,19 @@ public:
indicator
@returns the newly created field path expression
*/
- static boost::intrusive_ptr<ExpressionFieldPath> create(ExpressionContext* const expCtx,
- const std::string& fieldPath);
+ static boost::intrusive_ptr<ExpressionFieldPath> deprecatedCreate(
+ ExpressionContext* const expCtx, const std::string& fieldPath);
- /// Like create(), but works with the raw std::string from the user with the "$" prefixes.
+ // Parse from the raw std::string from the user with the "$" prefixes.
static boost::intrusive_ptr<ExpressionFieldPath> parse(ExpressionContext* const expCtx,
const std::string& raw,
const VariablesParseState& vps);
+ // Create from a non-prefixed string. Assumes path not variable.
+ static boost::intrusive_ptr<ExpressionFieldPath> createPathFromString(
+ ExpressionContext* const expCtx, const std::string& raw, const VariablesParseState& vps);
+ // Create from a non-prefixed string. Assumes variable not path.
+ static boost::intrusive_ptr<ExpressionFieldPath> createVarFromString(
+ ExpressionContext* const expCtx, const std::string& raw, const VariablesParseState& vps);
/**
* Returns true if this expression logically represents the path 'dottedPath'. For example, if
diff --git a/src/mongo/db/pipeline/expression_field_path_test.cpp b/src/mongo/db/pipeline/expression_field_path_test.cpp
index 14064134b9d..9fa73620dfe 100644
--- a/src/mongo/db/pipeline/expression_field_path_test.cpp
+++ b/src/mongo/db/pipeline/expression_field_path_test.cpp
@@ -68,7 +68,7 @@ class Invalid {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- ASSERT_THROWS(ExpressionFieldPath::create(&expCtx, ""), AssertionException);
+ ASSERT_THROWS(ExpressionFieldPath::deprecatedCreate(&expCtx, ""), AssertionException);
}
};
@@ -104,7 +104,7 @@ TEST(FieldPath, RemoveOptimizesToMissingValue) {
TEST(FieldPath, NoOptimizationOnNormalPath) {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a");
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::deprecatedCreate(&expCtx, "a");
// An attempt to optimize returns the Expression itself.
ASSERT_EQUALS(expression, expression->optimize());
}
@@ -205,7 +205,8 @@ class Dependencies {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
DepsTracker dependencies;
expression->addDependencies(&dependencies);
ASSERT_EQUALS(1U, dependencies.fields.size());
@@ -220,7 +221,7 @@ class Missing {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a");
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::deprecatedCreate(&expCtx, "a");
ASSERT_BSONOBJ_BINARY_EQ(fromjson("{}"),
toBson(expression->evaluate({}, &expCtx.variables)));
}
@@ -231,7 +232,7 @@ class Present {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a");
+ intrusive_ptr<Expression> expression = ExpressionFieldPath::deprecatedCreate(&expCtx, "a");
ASSERT_BSONOBJ_BINARY_EQ(
fromjson("{'':123}"),
toBson(expression->evaluate(fromBson(BSON("a" << 123)), &expCtx.variables)));
@@ -243,7 +244,8 @@ class NestedBelowNull {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
ASSERT_BSONOBJ_BINARY_EQ(
fromjson("{}"),
toBson(expression->evaluate(fromBson(fromjson("{a:null}")), &expCtx.variables)));
@@ -255,7 +257,8 @@ class NestedBelowUndefined {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
ASSERT_BSONOBJ_BINARY_EQ(
fromjson("{}"),
toBson(expression->evaluate(fromBson(fromjson("{a:undefined}")), &expCtx.variables)));
@@ -267,7 +270,8 @@ class NestedBelowMissing {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
ASSERT_BSONOBJ_BINARY_EQ(
fromjson("{}"),
toBson(expression->evaluate(fromBson(fromjson("{z:1}")), &expCtx.variables)));
@@ -279,7 +283,8 @@ class NestedBelowInt {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
ASSERT_BSONOBJ_BINARY_EQ(
fromjson("{}"),
toBson(expression->evaluate(fromBson(BSON("a" << 2)), &expCtx.variables)));
@@ -291,7 +296,8 @@ class NestedValue {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
ASSERT_BSONOBJ_BINARY_EQ(BSON("" << 55),
toBson(expression->evaluate(fromBson(BSON("a" << BSON("b" << 55))),
&expCtx.variables)));
@@ -303,7 +309,8 @@ class NestedBelowEmptyObject {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
ASSERT_BSONOBJ_BINARY_EQ(
fromjson("{}"),
toBson(expression->evaluate(fromBson(BSON("a" << BSONObj())), &expCtx.variables)));
@@ -315,7 +322,8 @@ class NestedBelowEmptyArray {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
ASSERT_BSONOBJ_BINARY_EQ(
BSON("" << BSONArray()),
toBson(expression->evaluate(fromBson(BSON("a" << BSONArray())), &expCtx.variables)));
@@ -327,7 +335,8 @@ class NestedBelowArrayWithNull {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
ASSERT_BSONOBJ_BINARY_EQ(
fromjson("{'':[]}"),
toBson(expression->evaluate(fromBson(fromjson("{a:[null]}")), &expCtx.variables)));
@@ -339,7 +348,8 @@ class NestedBelowArrayWithUndefined {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
ASSERT_BSONOBJ_BINARY_EQ(
fromjson("{'':[]}"),
toBson(expression->evaluate(fromBson(fromjson("{a:[undefined]}")), &expCtx.variables)));
@@ -351,7 +361,8 @@ class NestedBelowArrayWithInt {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
ASSERT_BSONOBJ_BINARY_EQ(
fromjson("{'':[]}"),
toBson(expression->evaluate(fromBson(fromjson("{a:[1]}")), &expCtx.variables)));
@@ -363,7 +374,8 @@ class NestedWithinArray {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
ASSERT_BSONOBJ_BINARY_EQ(
fromjson("{'':[9]}"),
toBson(expression->evaluate(fromBson(fromjson("{a:[{b:9}]}")), &expCtx.variables)));
@@ -375,7 +387,8 @@ class MultipleArrayValues {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
ASSERT_BSONOBJ_BINARY_EQ(
fromjson("{'':[9,20]}"),
toBson(expression->evaluate(
@@ -389,7 +402,8 @@ class ExpandNestedArrays {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b.c");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b.c");
ASSERT_BSONOBJ_BINARY_EQ(
fromjson("{'':[[1,2],3,[4],[[5]],[6,7]]}"),
toBson(expression->evaluate(fromBson(fromjson("{a:[{b:[{c:1},{c:2}]},"
@@ -406,7 +420,8 @@ class AddToBsonObj {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b.c");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b.c");
ASSERT_BSONOBJ_BINARY_EQ(BSON("foo"
<< "$a.b.c"),
BSON("foo" << expression->serialize(false)));
@@ -418,7 +433,8 @@ class AddToBsonArray {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression = ExpressionFieldPath::create(&expCtx, "a.b.c");
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b.c");
BSONArrayBuilder bab;
bab << expression->serialize(false);
ASSERT_BSONOBJ_BINARY_EQ(BSON_ARRAY("$a.b.c"), bab.arr());
diff --git a/src/mongo/db/pipeline/expression_nary_test.cpp b/src/mongo/db/pipeline/expression_nary_test.cpp
index 3b709c4c7ae..3d284235335 100644
--- a/src/mongo/db/pipeline/expression_nary_test.cpp
+++ b/src/mongo/db/pipeline/expression_nary_test.cpp
@@ -208,7 +208,8 @@ TEST_F(ExpressionNaryTest, AddedConstantOperandIsSerialized) {
}
TEST_F(ExpressionNaryTest, AddedFieldPathOperandIsSerialized) {
- _notAssociativeNorCommutative->addOperand(ExpressionFieldPath::create(&expCtx, "ab.c"));
+ _notAssociativeNorCommutative->addOperand(
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "ab.c"));
assertContents(_notAssociativeNorCommutative, BSON_ARRAY("$ab.c"));
}
@@ -222,7 +223,8 @@ TEST_F(ExpressionNaryTest, ValidateConstantExpressionDependency) {
}
TEST_F(ExpressionNaryTest, ValidateFieldPathExpressionDependency) {
- _notAssociativeNorCommutative->addOperand(ExpressionFieldPath::create(&expCtx, "ab.c"));
+ _notAssociativeNorCommutative->addOperand(
+ ExpressionFieldPath::deprecatedCreate(&expCtx, "ab.c"));
assertDependencies(_notAssociativeNorCommutative, BSON_ARRAY("ab.c"));
}
diff --git a/src/mongo/db/pipeline/expression_object_test.cpp b/src/mongo/db/pipeline/expression_object_test.cpp
index 5b339bf911b..7246e9569e3 100644
--- a/src/mongo/db/pipeline/expression_object_test.cpp
+++ b/src/mongo/db/pipeline/expression_object_test.cpp
@@ -211,10 +211,11 @@ TEST(ExpressionObjectEvaluate, ShouldEvaluateEachField) {
TEST(ExpressionObjectEvaluate, OrderOfFieldsInOutputShouldMatchOrderInSpecification) {
auto expCtx = ExpressionContextForTest{};
- auto object = ExpressionObject::create(&expCtx,
- {{"a", ExpressionFieldPath::create(&expCtx, "a")},
- {"b", ExpressionFieldPath::create(&expCtx, "b")},
- {"c", ExpressionFieldPath::create(&expCtx, "c")}});
+ auto object =
+ ExpressionObject::create(&expCtx,
+ {{"a", ExpressionFieldPath::deprecatedCreate(&expCtx, "a")},
+ {"b", ExpressionFieldPath::deprecatedCreate(&expCtx, "b")},
+ {"c", ExpressionFieldPath::deprecatedCreate(&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}},
@@ -223,10 +224,10 @@ TEST(ExpressionObjectEvaluate, OrderOfFieldsInOutputShouldMatchOrderInSpecificat
TEST(ExpressionObjectEvaluate, ShouldRemoveFieldsThatHaveMissingValues) {
auto expCtx = ExpressionContextForTest{};
- auto object =
- ExpressionObject::create(&expCtx,
- {{"a", ExpressionFieldPath::create(&expCtx, "a.b")},
- {"b", ExpressionFieldPath::create(&expCtx, "missing")}});
+ auto object = ExpressionObject::create(
+ &expCtx,
+ {{"a", ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b")},
+ {"b", ExpressionFieldPath::deprecatedCreate(&expCtx, "missing")}});
ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document(), &(expCtx.variables)));
ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document{{"a", 1}}, &(expCtx.variables)));
}
@@ -236,9 +237,10 @@ TEST(ExpressionObjectEvaluate, ShouldEvaluateFieldsWithinNestedObject) {
auto object = ExpressionObject::create(
&expCtx,
{{"a",
- ExpressionObject::create(&expCtx,
- {{"b", ExpressionConstant::create(&expCtx, Value{1})},
- {"c", ExpressionFieldPath::create(&expCtx, "_id")}})}});
+ ExpressionObject::create(
+ &expCtx,
+ {{"b", ExpressionConstant::create(&expCtx, Value{1})},
+ {"c", ExpressionFieldPath::deprecatedCreate(&expCtx, "_id")}})}});
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}}}}),
@@ -247,8 +249,8 @@ TEST(ExpressionObjectEvaluate, ShouldEvaluateFieldsWithinNestedObject) {
TEST(ExpressionObjectEvaluate, ShouldEvaluateToEmptyDocumentIfAllFieldsAreMissing) {
auto expCtx = ExpressionContextForTest{};
- auto object =
- ExpressionObject::create(&expCtx, {{"a", ExpressionFieldPath::create(&expCtx, "missing")}});
+ auto object = ExpressionObject::create(
+ &expCtx, {{"a", ExpressionFieldPath::deprecatedCreate(&expCtx, "missing")}});
ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document(), &(expCtx.variables)));
auto objectWithNestedObject = ExpressionObject::create(&expCtx, {{"nested", object}});
@@ -271,8 +273,8 @@ TEST(ExpressionObjectDependencies, ConstantValuesShouldNotBeAddedToDependencies)
TEST(ExpressionObjectDependencies, FieldPathsShouldBeAddedToDependencies) {
auto expCtx = ExpressionContextForTest{};
- auto object =
- ExpressionObject::create(&expCtx, {{"x", ExpressionFieldPath::create(&expCtx, "c.d")}});
+ auto object = ExpressionObject::create(
+ &expCtx, {{"x", ExpressionFieldPath::deprecatedCreate(&expCtx, "c.d")}});
DepsTracker deps;
object->addDependencies(&deps);
ASSERT_EQ(deps.fields.size(), 1UL);
diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp
index 5288fe96c00..e1eeb44d3d2 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -548,7 +548,7 @@ class Dependencies {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> nested = ExpressionFieldPath::create(&expCtx, "a.b");
+ intrusive_ptr<Expression> nested = ExpressionFieldPath::deprecatedCreate(&expCtx, "a.b");
intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(&expCtx, nested);
DepsTracker dependencies;
expression->addDependencies(&dependencies);
@@ -564,8 +564,8 @@ class AddToBsonObj {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression =
- ExpressionCoerceToBool::create(&expCtx, ExpressionFieldPath::create(&expCtx, "foo"));
+ intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(
+ &expCtx, ExpressionFieldPath::deprecatedCreate(&expCtx, "foo"));
// serialized as $and because CoerceToBool isn't an ExpressionNary
ASSERT_BSONOBJ_BINARY_EQ(fromjson("{field:{$and:['$foo']}}"), toBsonObj(expression));
@@ -582,8 +582,8 @@ class AddToBsonArray {
public:
void run() {
auto expCtx = ExpressionContextForTest{};
- intrusive_ptr<Expression> expression =
- ExpressionCoerceToBool::create(&expCtx, ExpressionFieldPath::create(&expCtx, "foo"));
+ intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create(
+ &expCtx, ExpressionFieldPath::deprecatedCreate(&expCtx, "foo"));
// serialized as $and because CoerceToBool isn't an ExpressionNary
ASSERT_BSONOBJ_BINARY_EQ(BSON_ARRAY(fromjson("{$and:['$foo']}")), toBsonArray(expression));
diff --git a/src/mongo/db/pipeline/variable_validation.cpp b/src/mongo/db/pipeline/variable_validation.cpp
new file mode 100644
index 00000000000..a0b29b2f59e
--- /dev/null
+++ b/src/mongo/db/pipeline/variable_validation.cpp
@@ -0,0 +1,82 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/base/error_codes.h"
+#include "mongo/util/str.h"
+
+namespace mongo::variableValidation {
+void validateName(StringData varName,
+ std::function<bool(char)> prefixPred,
+ std::function<bool(char)> suffixPred,
+ int prefixLen) {
+ uassert(16866, "empty variable names are not allowed", !varName.empty());
+ for (int i = 0; i < prefixLen; ++i)
+ if (!prefixPred(varName[i]))
+ uasserted(16867,
+ str::stream()
+ << "'" << varName
+ << "' starts with an invalid character for a user variable name");
+
+ for (size_t i = prefixLen; i < varName.size(); i++)
+ if (!suffixPred(varName[i]))
+ uasserted(16868,
+ str::stream() << "'" << varName << "' contains an invalid character "
+ << "for a variable name: '" << varName[i] << "'");
+}
+
+void validateNameForUserWrite(StringData varName) {
+ // System variables users allowed to write to (currently just one)
+ if (varName == "CURRENT") {
+ return;
+ }
+ validateName(varName,
+ [](char ch) -> bool {
+ return (ch >= 'a' && ch <= 'z') || (ch & '\x80'); // non-ascii
+ },
+ [](char ch) -> bool {
+ return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
+ (ch >= '0' && ch <= '9') || (ch == '_') || (ch & '\x80'); // non-ascii
+ },
+ 1);
+}
+
+void validateNameForUserRead(StringData varName) {
+ validateName(varName,
+ [](char ch) -> bool {
+ return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
+ (ch & '\x80'); // non-ascii
+ },
+ [](char ch) -> bool {
+ return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
+ (ch >= '0' && ch <= '9') || (ch == '_') || (ch & '\x80'); // non-ascii
+ },
+ 1);
+}
+
+} // namespace mongo::variableValidation
diff --git a/src/mongo/db/pipeline/variable_validation.h b/src/mongo/db/pipeline/variable_validation.h
new file mode 100644
index 00000000000..a11b1df49cd
--- /dev/null
+++ b/src/mongo/db/pipeline/variable_validation.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+
+namespace mongo::variableValidation {
+void validateNameForUserWrite(StringData varName);
+void validateNameForUserRead(StringData varName);
+void validateName(StringData varName,
+ std::function<bool(char)> prefixPred,
+ std::function<bool(char)> suffixPred,
+ int prefixLen);
+} // namespace mongo::variableValidation
diff --git a/src/mongo/db/pipeline/variables.cpp b/src/mongo/db/pipeline/variables.cpp
index 485569152d1..28d8e135271 100644
--- a/src/mongo/db/pipeline/variables.cpp
+++ b/src/mongo/db/pipeline/variables.cpp
@@ -32,6 +32,7 @@
#include "mongo/db/client.h"
#include "mongo/db/logical_clock.h"
#include "mongo/db/pipeline/expression.h"
+#include "mongo/db/pipeline/variable_validation.h"
#include "mongo/platform/basic.h"
#include "mongo/platform/random.h"
#include "mongo/util/str.h"
@@ -88,54 +89,6 @@ const std::map<StringData, std::function<void(const Value&)>> Variables::kSystem
value.getType() == BSONType::Bool);
}}};
-void Variables::validateName(StringData varName,
- std::function<bool(char)> prefixPred,
- std::function<bool(char)> suffixPred,
- int prefixLen) {
- uassert(16866, "empty variable names are not allowed", !varName.empty());
- for (int i = 0; i < prefixLen; ++i)
- if (!prefixPred(varName[i]))
- uasserted(16867,
- str::stream()
- << "'" << varName
- << "' starts with an invalid character for a user variable name");
-
- for (size_t i = prefixLen; i < varName.size(); i++)
- if (!suffixPred(varName[i]))
- uasserted(16868,
- str::stream() << "'" << varName << "' contains an invalid character "
- << "for a variable name: '" << varName[i] << "'");
-}
-
-void Variables::validateNameForUserWrite(StringData varName) {
- // System variables users allowed to write to (currently just one)
- if (varName == "CURRENT") {
- return;
- }
- validateName(varName,
- [](char ch) -> bool {
- return (ch >= 'a' && ch <= 'z') || (ch & '\x80'); // non-ascii
- },
- [](char ch) -> bool {
- return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
- (ch >= '0' && ch <= '9') || (ch == '_') || (ch & '\x80'); // non-ascii
- },
- 1);
-}
-
-void Variables::validateNameForUserRead(StringData varName) {
- validateName(varName,
- [](char ch) -> bool {
- return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
- (ch & '\x80'); // non-ascii
- },
- [](char ch) -> bool {
- return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
- (ch >= '0' && ch <= '9') || (ch == '_') || (ch & '\x80'); // non-ascii
- },
- 1);
-}
-
void Variables::setValue(Id id, const Value& value, bool isConstant) {
uassert(17199, "can't use Variables::setValue to set a reserved builtin variable", id >= 0);
@@ -236,7 +189,7 @@ void Variables::setDefaultRuntimeConstants(OperationContext* opCtx) {
void Variables::seedVariablesWithLetParameters(ExpressionContext* const expCtx,
const BSONObj letParams) {
for (auto&& elem : letParams) {
- Variables::validateNameForUserWrite(elem.fieldName());
+ variableValidation::validateNameForUserWrite(elem.fieldName());
auto expr = Expression::parseOperand(expCtx, elem, expCtx->variablesParseState);
uassert(4890500,
@@ -287,7 +240,7 @@ void Variables::copyToExpCtx(const VariablesParseState& vps, ExpressionContext*
}
Variables::Id VariablesParseState::defineVariable(StringData name) {
- // Caller should have validated before hand by using Variables::validateNameForUserWrite.
+ // Caller should have validated before hand by using variableValidationvalidateNameForUserWrite.
massert(17275,
"Can't redefine a non-user-writable variable",
Variables::kBuiltinVarNameToId.find(name) == Variables::kBuiltinVarNameToId.end());
diff --git a/src/mongo/db/pipeline/variables.h b/src/mongo/db/pipeline/variables.h
index 6cf5256a640..b200d04e115 100644
--- a/src/mongo/db/pipeline/variables.h
+++ b/src/mongo/db/pipeline/variables.h
@@ -73,8 +73,6 @@ public:
Variables() = default;
- static void validateNameForUserWrite(StringData varName);
- static void validateNameForUserRead(StringData varName);
static bool isUserDefinedVariable(Variables::Id id) {
return id >= 0;
}
@@ -199,11 +197,6 @@ private:
void setValue(Id id, const Value& value, bool isConstant);
- static void validateName(StringData varName,
- std::function<bool(char)> prefixPred,
- std::function<bool(char)> suffixPred,
- int prefixLen);
-
static auto getBuiltinVariableName(Variables::Id variable) {
for (auto& [name, id] : kBuiltinVarNameToId) {
if (variable == id) {