summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Evans <jacob.evans@10gen.com>2020-07-14 17:52:36 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-07-23 04:03:44 +0000
commit00d90d78e84cdc16a03a2a5173ce797e3c3745b9 (patch)
tree5ec55b080cd910dee7308c5e4d8c6eeac5525bc8
parent96ae85e1eb307ef6529d277136b054c3dff20ea3 (diff)
downloadmongo-00d90d78e84cdc16a03a2a5173ce797e3c3745b9.tar.gz
SERVER-49331 Implement aggExpression rule in grammar
-rw-r--r--src/mongo/db/cst/bson_lexer.cpp37
-rw-r--r--src/mongo/db/cst/bson_lexer_test.cpp50
-rw-r--r--src/mongo/db/cst/c_node.cpp13
-rw-r--r--src/mongo/db/cst/c_node.h2
-rw-r--r--src/mongo/db/cst/cst_pipeline_translation.cpp196
-rw-r--r--src/mongo/db/cst/cst_pipeline_translation_test.cpp180
-rw-r--r--src/mongo/db/cst/cst_test.cpp50
-rw-r--r--src/mongo/db/cst/key_fieldname.h1
-rw-r--r--src/mongo/db/cst/key_value.h8
-rw-r--r--src/mongo/db/cst/location_gen.h18
-rw-r--r--src/mongo/db/cst/pipeline_grammar.yy254
-rw-r--r--src/mongo/db/cst/pipeline_parser_gen.cpp935
-rw-r--r--src/mongo/db/cst/pipeline_parser_gen.hpp638
-rw-r--r--src/mongo/db/pipeline/expression.h14
-rw-r--r--src/mongo/db/pipeline/expression_trigonometric.cpp37
-rw-r--r--src/mongo/db/pipeline/expression_trigonometric.h38
16 files changed, 1993 insertions, 478 deletions
diff --git a/src/mongo/db/cst/bson_lexer.cpp b/src/mongo/db/cst/bson_lexer.cpp
index 047f8ef4c95..7516bef39c4 100644
--- a/src/mongo/db/cst/bson_lexer.cpp
+++ b/src/mongo/db/cst/bson_lexer.cpp
@@ -40,12 +40,16 @@ namespace {
// Mapping of reserved keywords to BSON token. Any key which is not included in this map is assumed
// to be a user field name and is treated as a terminal by the parser.
const StringMap<PipelineParserGen::token_type> reservedKeyLookup = {
+ {"_id", PipelineParserGen::token::ID},
{"$_internalInhibitOptimization", PipelineParserGen::token::STAGE_INHIBIT_OPTIMIZATION},
{"$unionWith", PipelineParserGen::token::STAGE_UNION_WITH},
- {"coll", PipelineParserGen::token::COLL_ARG},
- {"pipeline", PipelineParserGen::token::PIPELINE_ARG},
+ {"$project", PipelineParserGen::token::STAGE_PROJECT},
{"$skip", PipelineParserGen::token::STAGE_SKIP},
{"$limit", PipelineParserGen::token::STAGE_LIMIT},
+ {"coll", PipelineParserGen::token::COLL_ARG},
+ {"pipeline", PipelineParserGen::token::PIPELINE_ARG},
+ {"$add", PipelineParserGen::token::ADD},
+ {"$atan2", PipelineParserGen::token::ATAN2},
};
bool isCompound(PipelineParserGen::symbol_type token) {
return token.type_get() == static_cast<int>(PipelineParserGen::token::START_OBJECT) ||
@@ -129,8 +133,8 @@ void BSONLexer::tokenize(BSONElement elem, bool includeFieldName) {
// Place the token expected by the parser if this is a reserved keyword.
_tokens.emplace_back(it->second, getNextLoc());
} else {
- // If we don't care about the keyword, the fieldname is treated as a normal string.
- _tokens.emplace_back(PipelineParserGen::make_STRING(elem.fieldName(), getNextLoc()));
+ // If we don't care about the keyword, then it's treated as a generic fieldname.
+ _tokens.emplace_back(PipelineParserGen::make_FIELDNAME(elem.fieldName(), getNextLoc()));
}
}
@@ -154,19 +158,30 @@ void BSONLexer::tokenize(BSONElement elem, bool includeFieldName) {
_tokens.emplace_back(PipelineParserGen::make_STRING(elem.String(), getNextLoc()));
break;
case NumberLong:
- _tokens.emplace_back(
- PipelineParserGen::make_NUMBER_LONG(elem.numberLong(), getNextLoc()));
+ if (elem.numberLong() == 0ll)
+ _tokens.emplace_back(PipelineParserGen::token::LONG_ZERO, getNextLoc());
+ else
+ _tokens.emplace_back(
+ PipelineParserGen::make_LONG_NON_ZERO(elem.numberLong(), getNextLoc()));
break;
case NumberInt:
- _tokens.emplace_back(
- PipelineParserGen::make_NUMBER_INT(elem.numberInt(), getNextLoc()));
+ if (elem.numberInt() == 0)
+ _tokens.emplace_back(PipelineParserGen::token::INT_ZERO, getNextLoc());
+ else
+ _tokens.emplace_back(
+ PipelineParserGen::make_INT_NON_ZERO(elem.numberInt(), getNextLoc()));
break;
case NumberDouble:
- _tokens.emplace_back(
- PipelineParserGen::make_NUMBER_DOUBLE(elem.numberDouble(), getNextLoc()));
+ if (elem.numberDouble() == 0.0)
+ _tokens.emplace_back(PipelineParserGen::token::DOUBLE_ZERO, getNextLoc());
+ else
+ _tokens.emplace_back(
+ PipelineParserGen::make_DOUBLE_NON_ZERO(elem.numberDouble(), getNextLoc()));
break;
case Bool:
- _tokens.emplace_back(PipelineParserGen::make_BOOL(elem.boolean(), getNextLoc()));
+ _tokens.emplace_back(elem.boolean() ? PipelineParserGen::token::TRUE
+ : PipelineParserGen::token::FALSE,
+ getNextLoc());
break;
default:
MONGO_UNREACHABLE;
diff --git a/src/mongo/db/cst/bson_lexer_test.cpp b/src/mongo/db/cst/bson_lexer_test.cpp
index 726fdb0125c..6a5eb631e68 100644
--- a/src/mongo/db/cst/bson_lexer_test.cpp
+++ b/src/mongo/db/cst/bson_lexer_test.cpp
@@ -54,9 +54,9 @@ TEST(BSONLexerTest, TokenizesOpaqueUserObjects) {
assertTokensMatch(lexer,
{PipelineParserGen::token::START_ARRAY,
PipelineParserGen::token::START_OBJECT,
- PipelineParserGen::token::STRING,
- PipelineParserGen::token::NUMBER_INT,
- PipelineParserGen::token::STRING,
+ PipelineParserGen::token::FIELDNAME,
+ PipelineParserGen::token::INT_NON_ZERO,
+ PipelineParserGen::token::FIELDNAME,
PipelineParserGen::token::STRING,
PipelineParserGen::token::END_OBJECT,
PipelineParserGen::token::END_ARRAY});
@@ -81,7 +81,7 @@ TEST(BSONLexerTest, TokenizesReservedKeywordsAtAnyDepth) {
assertTokensMatch(lexer,
{PipelineParserGen::token::START_ARRAY,
PipelineParserGen::token::START_OBJECT,
- PipelineParserGen::token::STRING,
+ PipelineParserGen::token::FIELDNAME,
PipelineParserGen::token::START_OBJECT,
PipelineParserGen::token::STAGE_INHIBIT_OPTIMIZATION,
PipelineParserGen::token::START_OBJECT,
@@ -92,7 +92,7 @@ TEST(BSONLexerTest, TokenizesReservedKeywordsAtAnyDepth) {
}
TEST(BSONLexerTest, MidRuleActionToSortNestedObject) {
- auto input = fromjson("{pipeline: [{pipeline: 1, coll: 'test'}]}");
+ auto input = fromjson("{pipeline: [{pipeline: 1.0, coll: 'test'}]}");
BSONLexer lexer(input["pipeline"].Array());
// Iterate until the first object.
ASSERT_EQ(lexer.getNext().type_get(), PipelineParserGen::token::START_ARRAY);
@@ -104,7 +104,7 @@ TEST(BSONLexerTest, MidRuleActionToSortNestedObject) {
PipelineParserGen::token::COLL_ARG,
PipelineParserGen::token::STRING,
PipelineParserGen::token::PIPELINE_ARG,
- PipelineParserGen::token::NUMBER_INT,
+ PipelineParserGen::token::DOUBLE_NON_ZERO,
PipelineParserGen::token::END_OBJECT,
PipelineParserGen::token::END_ARRAY};
assertTokensMatch(lexer, expected);
@@ -113,7 +113,7 @@ TEST(BSONLexerTest, MidRuleActionToSortNestedObject) {
TEST(BSONLexerTest, MidRuleActionToSortDoesNotSortNestedObjects) {
auto input = fromjson(
- "{pipeline: [{$unionWith: {pipeline: [{$unionWith: 'inner', a: 1}], coll: 'outer'}}]}");
+ "{pipeline: [{$unionWith: {pipeline: [{$unionWith: 'inner', a: 1.0}], coll: 'outer'}}]}");
BSONLexer lexer(input["pipeline"].Array());
// Iterate until we reach the $unionWith object.
ASSERT_EQ(lexer.getNext().type_get(), PipelineParserGen::token::START_ARRAY);
@@ -130,8 +130,8 @@ TEST(BSONLexerTest, MidRuleActionToSortDoesNotSortNestedObjects) {
// The nested pipeline does *not* get sorted, meaning '$unionWith' stays before 'a'.
PipelineParserGen::token::STAGE_UNION_WITH,
PipelineParserGen::token::STRING, // $unionWith: 'inner'
- PipelineParserGen::token::STRING,
- PipelineParserGen::token::NUMBER_INT, // a: 1
+ PipelineParserGen::token::FIELDNAME,
+ PipelineParserGen::token::DOUBLE_NON_ZERO, // a: 1.0
PipelineParserGen::token::END_OBJECT,
PipelineParserGen::token::END_ARRAY,
PipelineParserGen::token::END_OBJECT,
@@ -143,8 +143,8 @@ TEST(BSONLexerTest, MidRuleActionToSortDoesNotSortNestedObjects) {
TEST(BSONLexerTest, MultipleNestedObjectsAreReorderedCorrectly) {
auto input = fromjson(
- "{pipeline: [{$unionWith: {pipeline: [{$unionWith: 'inner', a: 1}], coll: [{$unionWith: "
- "'innerB', a: 2}]}}]}");
+ "{pipeline: [{$unionWith: {pipeline: [{$unionWith: 'inner', a: 1.0}], coll: [{$unionWith: "
+ "'innerB', a: 2.0}]}}]}");
BSONLexer lexer(input["pipeline"].Array());
// Iterate until we reach the $unionWith object.
ASSERT_EQ(lexer.getNext().type_get(), PipelineParserGen::token::START_ARRAY);
@@ -158,9 +158,9 @@ TEST(BSONLexerTest, MultipleNestedObjectsAreReorderedCorrectly) {
PipelineParserGen::token::START_OBJECT,
// The nested pipeline does *not* get sorted, meaning '$unionWith' stays before 'a'.
PipelineParserGen::token::STAGE_UNION_WITH,
- PipelineParserGen::token::STRING, // innerb
- PipelineParserGen::token::STRING, // a
- PipelineParserGen::token::NUMBER_INT, // a: 2
+ PipelineParserGen::token::STRING, // innerb
+ PipelineParserGen::token::FIELDNAME, // a
+ PipelineParserGen::token::DOUBLE_NON_ZERO, // a: 2.0
PipelineParserGen::token::END_OBJECT,
PipelineParserGen::token::END_ARRAY,
// Coll nested object ends here.
@@ -169,9 +169,9 @@ TEST(BSONLexerTest, MultipleNestedObjectsAreReorderedCorrectly) {
PipelineParserGen::token::START_OBJECT,
// The nested pipeline does *not* get sorted, meaning '$unionWith' stays before 'a'.
PipelineParserGen::token::STAGE_UNION_WITH,
- PipelineParserGen::token::STRING, // $unionWith: 'inner'
- PipelineParserGen::token::STRING, // a
- PipelineParserGen::token::NUMBER_INT, // a: 1
+ PipelineParserGen::token::STRING, // $unionWith: 'inner'
+ PipelineParserGen::token::FIELDNAME, // a
+ PipelineParserGen::token::DOUBLE_NON_ZERO, // a: 1.0
PipelineParserGen::token::END_OBJECT,
PipelineParserGen::token::END_ARRAY,
PipelineParserGen::token::END_OBJECT,
@@ -182,8 +182,8 @@ TEST(BSONLexerTest, MultipleNestedObjectsAreReorderedCorrectly) {
}
TEST(BSONLexerTest, MultiLevelBSONDoesntSortChildren) {
auto input = fromjson(
- "{pipeline: [{$unionWith: {pipeline: [{$unionWith: {'nested': 1, 'apple': 1}, a: 1}], "
- "coll: 'outer'}}]}");
+ "{pipeline: [{$unionWith: {pipeline: [{$unionWith: {'nested': 1.0, 'apple': 1.0}, a: 1.0}],"
+ " coll: 'outer'}}]}");
BSONLexer lexer(input["pipeline"].Array());
// Iterate until we reach the $unionWith object.
ASSERT_EQ(lexer.getNext().type_get(), PipelineParserGen::token::START_ARRAY);
@@ -201,14 +201,14 @@ TEST(BSONLexerTest, MultiLevelBSONDoesntSortChildren) {
PipelineParserGen::token::STAGE_UNION_WITH,
// Second nested object
PipelineParserGen::token::START_OBJECT,
- PipelineParserGen::token::STRING, // nested: 1
- PipelineParserGen::token::NUMBER_INT,
- PipelineParserGen::token::STRING, // apple: 1
- PipelineParserGen::token::NUMBER_INT,
+ PipelineParserGen::token::FIELDNAME, // nested: 1.0
+ PipelineParserGen::token::DOUBLE_NON_ZERO,
+ PipelineParserGen::token::FIELDNAME, // apple: 1.0
+ PipelineParserGen::token::DOUBLE_NON_ZERO,
PipelineParserGen::token::END_OBJECT,
// End second nested object
- PipelineParserGen::token::STRING,
- PipelineParserGen::token::NUMBER_INT, // a: 1
+ PipelineParserGen::token::FIELDNAME,
+ PipelineParserGen::token::DOUBLE_NON_ZERO, // a: 1.0
PipelineParserGen::token::END_OBJECT,
// End first nested object
PipelineParserGen::token::END_ARRAY,
diff --git a/src/mongo/db/cst/c_node.cpp b/src/mongo/db/cst/c_node.cpp
index 35093d90ec8..f84507760fa 100644
--- a/src/mongo/db/cst/c_node.cpp
+++ b/src/mongo/db/cst/c_node.cpp
@@ -57,6 +57,16 @@ auto printFieldname(const CNode::Fieldname& fieldname) {
fieldname);
}
+auto printNonZeroKey(const NonZeroKey& nonZeroKey) {
+ return stdx::visit(
+ visit_helper::Overloaded{
+ [](const int& keyInt) { return "int "s + std::to_string(keyInt); },
+ [](const long long& keyLong) { return "long "s + std::to_string(keyLong); },
+ [](const double& keyDouble) { return "double "s + std::to_string(keyDouble); },
+ [](const Decimal128& keyDecimal) { return "decimal "s + keyDecimal.toString(); }},
+ nonZeroKey);
+}
+
template <typename T>
auto printValue(const T& payload) {
return stdx::visit(
@@ -67,6 +77,9 @@ auto printValue(const T& payload) {
return "<KeyValue "s +
key_value::toString[static_cast<std::underlying_type_t<KeyValue>>(value)] + ">";
},
+ [](const NonZeroKey& nonZeroKey) {
+ return "<NonZeroKey of type "s + printNonZeroKey(nonZeroKey) + ">";
+ },
[](const UserDouble& userDouble) {
return "<UserDouble "s + std::to_string(userDouble) + ">";
},
diff --git a/src/mongo/db/cst/c_node.h b/src/mongo/db/cst/c_node.h
index 47bbbcab05d..5d1fcafefd3 100644
--- a/src/mongo/db/cst/c_node.h
+++ b/src/mongo/db/cst/c_node.h
@@ -49,6 +49,7 @@
namespace mongo {
using UserFieldname = std::string;
+using NonZeroKey = stdx::variant<int, long long, double, Decimal128>;
// These are the non-compound types from bsonspec.org.
using UserDouble = double;
using UserString = std::string;
@@ -158,6 +159,7 @@ public:
stdx::variant<ArrayChildren,
ObjectChildren,
KeyValue,
+ NonZeroKey,
UserDouble,
UserString,
UserBinary,
diff --git a/src/mongo/db/cst/cst_pipeline_translation.cpp b/src/mongo/db/cst/cst_pipeline_translation.cpp
index 5afcf8e70fd..4a6eda1625f 100644
--- a/src/mongo/db/cst/cst_pipeline_translation.cpp
+++ b/src/mongo/db/cst/cst_pipeline_translation.cpp
@@ -30,8 +30,10 @@
#include <algorithm>
#include <boost/intrusive_ptr.hpp>
+#include <boost/optional.hpp>
#include <iterator>
+#include "mongo/bson/bsonmisc.h"
#include "mongo/db/cst/c_node.h"
#include "mongo/db/cst/cst_pipeline_translation.h"
#include "mongo/db/cst/key_fieldname.h"
@@ -41,7 +43,9 @@
#include "mongo/db/pipeline/document_source_limit.h"
#include "mongo/db/pipeline/document_source_project.h"
#include "mongo/db/pipeline/document_source_skip.h"
+#include "mongo/db/pipeline/expression.h"
#include "mongo/db/pipeline/expression_context.h"
+#include "mongo/db/pipeline/expression_trigonometric.h"
#include "mongo/db/query/projection.h"
#include "mongo/db/query/projection_ast.h"
#include "mongo/db/query/projection_parser.h"
@@ -50,13 +54,144 @@
namespace mongo::cst_pipeline_translation {
namespace {
+boost::intrusive_ptr<Expression> translateExpression(
+ const CNode& cst, const boost::intrusive_ptr<ExpressionContext>& expCtx);
+
+/**
+ * Walk a literal array payload and produce an ExpressionArray.
+ */
+auto translateLiteralArray(const CNode::ArrayChildren& array,
+ const boost::intrusive_ptr<ExpressionContext>& expCtx) {
+ auto expressions = std::vector<boost::intrusive_ptr<Expression>>{};
+ static_cast<void>(std::transform(
+ array.begin(), array.end(), std::back_inserter(expressions), [&](auto&& elem) {
+ return translateExpression(elem, expCtx);
+ }));
+ return ExpressionArray::create(expCtx.get(), std::move(expressions));
+}
+
+/**
+ * Walk a literal object payload and produce an ExpressionObject.
+ */
+auto translateLiteralObject(const CNode::ObjectChildren& object,
+ const boost::intrusive_ptr<ExpressionContext>& expCtx) {
+ auto fields = std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>>{};
+ static_cast<void>(
+ std::transform(object.begin(), object.end(), std::back_inserter(fields), [&](auto&& field) {
+ return std::pair{std::string{stdx::get<UserFieldname>(field.first)},
+ translateExpression(field.second, expCtx)};
+ }));
+ return ExpressionObject::create(expCtx.get(), std::move(fields));
+}
+
+/**
+ * Walk an agg function/operator object payload and produce an Expression.
+ */
+boost::intrusive_ptr<Expression> translateFunctionObject(
+ const CNode::ObjectChildren& object, const boost::intrusive_ptr<ExpressionContext>& expCtx) {
+ auto expressions = std::vector<boost::intrusive_ptr<Expression>>{};
+ // This assumes the Expression is in array-form.
+ auto&& array = object[0].second.arrayChildren();
+ static_cast<void>(std::transform(
+ array.begin(), array.end(), std::back_inserter(expressions), [&](auto&& elem) {
+ return translateExpression(elem, expCtx);
+ }));
+ switch (stdx::get<KeyFieldname>(object[0].first)) {
+ case KeyFieldname::add:
+ return make_intrusive<ExpressionAdd>(expCtx.get(), std::move(expressions));
+ case KeyFieldname::atan2:
+ return make_intrusive<ExpressionArcTangent2>(expCtx.get(), std::move(expressions));
+ default:
+ MONGO_UNREACHABLE;
+ }
+}
+
+/**
+ * Walk a literal leaf CNode and produce an agg Value.
+ */
+auto translateLiteralLeaf(const CNode& cst, const boost::intrusive_ptr<ExpressionContext>& expCtx) {
+ return stdx::visit(
+ visit_helper::Overloaded{
+ // These are illegal since they're non-leaf.
+ [](const CNode::ArrayChildren&) -> Value { MONGO_UNREACHABLE; },
+ [](const CNode::ObjectChildren&) -> Value { MONGO_UNREACHABLE; },
+ // These are illegal since they're non-literal.
+ [](const KeyValue&) -> Value { MONGO_UNREACHABLE; },
+ [](const NonZeroKey&) -> Value { MONGO_UNREACHABLE; },
+ // These payloads require a special translation to DocumentValue parlance.
+ [](const UserUndefined&) { return Value{BSONUndefined}; },
+ [](const UserNull&) { return Value{BSONNULL}; },
+ [](const UserMinKey&) { return Value{MINKEY}; },
+ [](const UserMaxKey&) { return Value{MAXKEY}; },
+ // The rest convert directly.
+ [](auto&& payload) { return Value{payload}; }},
+ cst.payload);
+}
+
+/**
+ * Walk an expression CNode and produce an agg Expression.
+ */
+boost::intrusive_ptr<Expression> translateExpression(
+ const CNode& cst, const boost::intrusive_ptr<ExpressionContext>& expCtx) {
+ return stdx::visit(
+ visit_helper::Overloaded{
+ // When we're not inside an agg operator/function, this is a non-leaf literal.
+ [&](const CNode::ArrayChildren& array) -> boost::intrusive_ptr<Expression> {
+ return translateLiteralArray(array, expCtx);
+ },
+ // This is either a literal object or an agg operator/function.
+ [&](const CNode::ObjectChildren& object) -> boost::intrusive_ptr<Expression> {
+ if (!object.empty() && stdx::holds_alternative<KeyFieldname>(object[0].first))
+ return translateFunctionObject(object, expCtx);
+ else
+ return translateLiteralObject(object, expCtx);
+ },
+ // If a key occurs outside a particular agg operator/function, it was misplaced.
+ [](const KeyValue&) -> boost::intrusive_ptr<Expression> { MONGO_UNREACHABLE; },
+ [](const NonZeroKey&) -> boost::intrusive_ptr<Expression> { MONGO_UNREACHABLE; },
+ // Everything else is a literal leaf.
+ [&](auto &&) -> boost::intrusive_ptr<Expression> {
+ return ExpressionConstant::create(expCtx.get(), translateLiteralLeaf(cst, expCtx));
+ }},
+ cst.payload);
+}
+
/**
- * Walk a projection CNode and produce a ProjectionASTNode.
+ * Walk a projection CNode and produce a ProjectionASTNode. Also returns whether this was an
+ * inclusion (or expressive projection) or an exclusion projection.
*/
auto translateProjection(const CNode& cst, const boost::intrusive_ptr<ExpressionContext>& expCtx) {
using namespace projection_ast;
- // TODO SERVER-48834: Support more than inclusion projection.
- return std::make_unique<BooleanConstantASTNode>(true);
+ // Returns whether a KeyValue indicates inclusion or exclusion.
+ auto isInclusionKeyValue = [](auto&& keyValue) {
+ switch (stdx::get<KeyValue>(keyValue)) {
+ case KeyValue::trueKey:
+ return true;
+ case KeyValue::intZeroKey:
+ case KeyValue::longZeroKey:
+ case KeyValue::doubleZeroKey:
+ case KeyValue::decimalZeroKey:
+ case KeyValue::falseKey:
+ return false;
+ default:
+ MONGO_UNREACHABLE;
+ }
+ };
+
+ if (stdx::holds_alternative<NonZeroKey>(cst.payload) ||
+ (stdx::holds_alternative<KeyValue>(cst.payload) && isInclusionKeyValue(cst.payload)))
+ // This is an inclusion Key.
+ return std::pair{std::unique_ptr<ASTNode>{std::make_unique<BooleanConstantASTNode>(true)},
+ true};
+ else if (stdx::holds_alternative<KeyValue>(cst.payload) && !isInclusionKeyValue(cst.payload))
+ // This is an exclusion Key.
+ return std::pair{std::unique_ptr<ASTNode>{std::make_unique<BooleanConstantASTNode>(false)},
+ false};
+ else
+ // This is an arbitrary expression to produce a computed field (this counts as inclusion).
+ return std::pair{std::unique_ptr<ASTNode>{
+ std::make_unique<ExpressionASTNode>(translateExpression(cst, expCtx))},
+ true};
}
/**
@@ -66,21 +201,56 @@ auto translateProject(const CNode& cst, const boost::intrusive_ptr<ExpressionCon
using namespace projection_ast;
auto root = ProjectionPathASTNode{};
bool sawId = false;
+ bool removeId = false;
+ boost::optional<bool> inclusion;
for (auto&& [name, child] : cst.objectChildren()) {
- if (stdx::get<UserFieldname>(name) == UserFieldname{"_id"})
+ // Turn the CNode into a projection AST node.
+ auto&& [projection, wasInclusion] = translateProjection(child, expCtx);
+ // If we see a key fieldname, make sure it's _id.
+ if (auto keyFieldname = stdx::get_if<KeyFieldname>(&name);
+ keyFieldname && *keyFieldname == KeyFieldname::id) {
+ // Keep track of whether we've ever seen _id at all.
sawId = true;
- addNodeAtPath(&root, stdx::get<UserFieldname>(name), translateProjection(child, expCtx));
+ // Keep track of whether we will need to remove the _id field to get around an exclusion
+ // projection bug in the case where it was manually included.
+ removeId = wasInclusion;
+ // Add node to the projection AST.
+ addNodeAtPath(&root, "_id", std::move(projection));
+ } else {
+ // This conditional changes the status of 'inclusion' to indicate whether we're in an
+ // inclusion or exclusion projection.
+ // TODO SERVER-48810: Improve error message with BSON locations.
+ inclusion = !inclusion ? wasInclusion
+ : *inclusion == wasInclusion ? wasInclusion : []() -> bool {
+ uasserted(4933100,
+ "$project must include only exclusion "
+ "projection or only inclusion projection");
+ }();
+ // Add node to the projection AST.
+ addNodeAtPath(&root, stdx::get<UserFieldname>(name), std::move(projection));
+ }
}
- if (!sawId)
- addNodeAtPath(&root, "_id", std::make_unique<BooleanConstantASTNode>(true));
-
- return DocumentSourceProject::create(
- // TODO SERVER-48834: Support more than inclusion projection.
- Projection{root, ProjectType::kInclusion},
- expCtx,
- "$project");
+ // If we saw any non-_id exclusions, this is an exclusion projection.
+ if (inclusion && !*inclusion) {
+ // If we saw an inclusion _id for an exclusion projection, we must manually remove it or
+ // projection AST will turn it into an _id exclusion due to a bug.
+ // TODO Fix the bug or organize this code to circumvent projection AST.
+ if (removeId)
+ static_cast<void>(root.removeChild("_id"));
+ return DocumentSourceProject::create(
+ Projection{root, ProjectType::kExclusion}, expCtx, "$project");
+ // If we saw any non-_id inclusions or computed fields, this is an inclusion projectioion.
+ // Also if inclusion was not determinted, it is the default.
+ } else {
+ // If we didn't see _id we need to add it in manually for inclusion or projection AST
+ // will incorrectly assume we want it gone.
+ if (!sawId)
+ addNodeAtPath(&root, "_id", std::make_unique<BooleanConstantASTNode>(true));
+ return DocumentSourceProject::create(
+ Projection{root, ProjectType::kInclusion}, expCtx, "$project");
+ }
}
/**
diff --git a/src/mongo/db/cst/cst_pipeline_translation_test.cpp b/src/mongo/db/cst/cst_pipeline_translation_test.cpp
index 6a7760b1bab..269b0ae92fc 100644
--- a/src/mongo/db/cst/cst_pipeline_translation_test.cpp
+++ b/src/mongo/db/cst/cst_pipeline_translation_test.cpp
@@ -30,7 +30,6 @@
#include "mongo/platform/basic.h"
#include <boost/intrusive_ptr.hpp>
-#include <iostream>
#include <string>
#include "mongo/bson/bsonobj.h"
@@ -51,21 +50,22 @@ namespace mongo {
namespace {
using namespace std::string_literals;
-TEST(CstTest, TranslatesEmpty) {
+auto getExpCtx() {
auto nss = NamespaceString{"db", "coll"};
+ return boost::intrusive_ptr<ExpressionContextForTest>{new ExpressionContextForTest(nss)};
+}
+
+TEST(CstTest, TranslatesEmpty) {
const auto cst = CNode{CNode::ArrayChildren{}};
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest(nss));
- auto pipeline = cst_pipeline_translation::translatePipeline(cst, expCtx);
+ auto pipeline = cst_pipeline_translation::translatePipeline(cst, getExpCtx());
auto& sources = pipeline->getSources();
ASSERT_EQ(0u, sources.size());
}
TEST(CstTest, TranslatesEmptyProject) {
- auto nss = NamespaceString{"db", "coll"};
const auto cst = CNode{CNode::ArrayChildren{
CNode{CNode::ObjectChildren{{KeyFieldname::project, CNode{CNode::ObjectChildren{}}}}}}};
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest(nss));
- auto pipeline = cst_pipeline_translation::translatePipeline(cst, expCtx);
+ auto pipeline = cst_pipeline_translation::translatePipeline(cst, getExpCtx());
auto& sources = pipeline->getSources();
ASSERT_EQ(1u, sources.size());
auto iter = sources.begin();
@@ -73,13 +73,11 @@ TEST(CstTest, TranslatesEmptyProject) {
}
TEST(CstTest, TranslatesEmptyProjects) {
- auto nss = NamespaceString{"db", "coll"};
const auto cst = CNode{CNode::ArrayChildren{
CNode{CNode::ObjectChildren{{KeyFieldname::project, CNode{CNode::ObjectChildren{}}}}},
CNode{CNode::ObjectChildren{{KeyFieldname::project, CNode{CNode::ObjectChildren{}}}}},
CNode{CNode::ObjectChildren{{KeyFieldname::project, CNode{CNode::ObjectChildren{}}}}}}};
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest(nss));
- auto pipeline = cst_pipeline_translation::translatePipeline(cst, expCtx);
+ auto pipeline = cst_pipeline_translation::translatePipeline(cst, getExpCtx());
auto& sources = pipeline->getSources();
ASSERT_EQ(3u, sources.size());
auto iter = sources.begin();
@@ -89,12 +87,10 @@ TEST(CstTest, TranslatesEmptyProjects) {
}
TEST(CstTest, TranslatesOneFieldInclusionProjectionStage) {
- auto nss = NamespaceString{"db", "coll"};
const auto cst = CNode{CNode::ArrayChildren{CNode{CNode::ObjectChildren{
{KeyFieldname::project,
CNode{CNode::ObjectChildren{{UserFieldname{"a"}, CNode{KeyValue::trueKey}}}}}}}}};
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest(nss));
- auto pipeline = cst_pipeline_translation::translatePipeline(cst, expCtx);
+ auto pipeline = cst_pipeline_translation::translatePipeline(cst, getExpCtx());
auto& sources = pipeline->getSources();
ASSERT_EQ(1u, sources.size());
auto iter = sources.begin();
@@ -106,14 +102,12 @@ TEST(CstTest, TranslatesOneFieldInclusionProjectionStage) {
}
TEST(CstTest, TranslatesMultifieldInclusionProjection) {
- auto nss = NamespaceString{"db", "coll"};
const auto cst = CNode{CNode::ArrayChildren{CNode{CNode::ObjectChildren{
{KeyFieldname::project,
- CNode{CNode::ObjectChildren{{UserFieldname{"_id"}, CNode{KeyValue::trueKey}},
- {UserFieldname{"a"}, CNode{KeyValue::trueKey}},
- {UserFieldname{"b"}, CNode{KeyValue::trueKey}}}}}}}}};
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest(nss));
- auto pipeline = cst_pipeline_translation::translatePipeline(cst, expCtx);
+ CNode{CNode::ObjectChildren{{KeyFieldname::id, CNode{KeyValue::trueKey}},
+ {UserFieldname{"a"}, CNode{NonZeroKey{7}}},
+ {UserFieldname{"b"}, CNode{NonZeroKey{-99999999999ll}}}}}}}}}};
+ auto pipeline = cst_pipeline_translation::translatePipeline(cst, getExpCtx());
auto& sources = pipeline->getSources();
ASSERT_EQ(1u, sources.size());
auto iter = sources.begin();
@@ -124,21 +118,144 @@ TEST(CstTest, TranslatesMultifieldInclusionProjection) {
singleDoc.getTransformer().serializeTransformation(boost::none).toBson()));
}
-TEST(CstTest, TranslatesMultipleInclusionProjectionStages) {
- auto nss = NamespaceString{"db", "coll"};
+TEST(CstTest, TranslatesOneFieldExclusionProjectionStage) {
+ const auto cst = CNode{CNode::ArrayChildren{CNode{CNode::ObjectChildren{
+ {KeyFieldname::project,
+ CNode{CNode::ObjectChildren{{UserFieldname{"a"}, CNode{KeyValue::falseKey}}}}}}}}};
+ auto pipeline = cst_pipeline_translation::translatePipeline(cst, getExpCtx());
+ auto& sources = pipeline->getSources();
+ ASSERT_EQ(1u, sources.size());
+ auto iter = sources.begin();
+ auto& singleDoc = dynamic_cast<DocumentSourceSingleDocumentTransformation&>(**iter);
+ // DocumenSourceSingleDoucmentTransformation reorders fields so we need to be insensitive.
+ ASSERT(UnorderedFieldsBSONObjComparator{}.evaluate(
+ BSON("a" << false) ==
+ singleDoc.getTransformer().serializeTransformation(boost::none).toBson()));
+}
+
+TEST(CstTest, TranslatesMultifieldExclusionProjection) {
+ const auto cst = CNode{CNode::ArrayChildren{CNode{CNode::ObjectChildren{
+ {KeyFieldname::project,
+ CNode{CNode::ObjectChildren{{KeyFieldname::id, CNode{KeyValue::falseKey}},
+ {UserFieldname{"a"}, CNode{KeyValue::doubleZeroKey}},
+ {UserFieldname{"b"}, CNode{KeyValue::decimalZeroKey}}}}}}}}};
+ auto pipeline = cst_pipeline_translation::translatePipeline(cst, getExpCtx());
+ auto& sources = pipeline->getSources();
+ ASSERT_EQ(1u, sources.size());
+ auto iter = sources.begin();
+ auto& singleDoc = dynamic_cast<DocumentSourceSingleDocumentTransformation&>(**iter);
+ // DocumenSourceSingleDoucmentTransformation reorders fields so we need to be insensitive.
+ ASSERT(UnorderedFieldsBSONObjComparator{}.evaluate(
+ BSON("_id" << false << "a" << false << "b" << false) ==
+ singleDoc.getTransformer().serializeTransformation(boost::none).toBson()));
+}
+
+TEST(CstTest, FailsToTranslateInclusionExclusionMixedProjectionStage) {
+ const auto cst = CNode{CNode::ArrayChildren{CNode{CNode::ObjectChildren{
+ {KeyFieldname::project,
+ CNode{CNode::ObjectChildren{{UserFieldname{"a"}, CNode{KeyValue::trueKey}},
+ {UserFieldname{"b"}, CNode{KeyValue::falseKey}}}}}}}}};
+ ASSERT_THROWS_CODE(
+ cst_pipeline_translation::translatePipeline(cst, getExpCtx()), DBException, 4933100);
+}
+
+TEST(CstTest, TranslatesComputedProjection) {
+ const auto cst = CNode{CNode::ArrayChildren{CNode{CNode::ObjectChildren{
+ {KeyFieldname::project,
+ CNode{CNode::ObjectChildren{
+ {UserFieldname{"a"},
+ CNode{CNode::ObjectChildren{
+ {KeyFieldname::atan2,
+ CNode{CNode::ArrayChildren{CNode{UserInt{1}}, CNode{UserInt{0}}}}}}}},
+ {UserFieldname{"b"},
+ CNode{
+ CNode::ObjectChildren{{KeyFieldname::add,
+ CNode{CNode::ArrayChildren{CNode{UserInt{1}},
+ CNode{UserInt{2}},
+ CNode{UserInt{3}},
+ CNode{UserInt{4}}}}}}}}}}}}}}};
+ auto pipeline = cst_pipeline_translation::translatePipeline(cst, getExpCtx());
+ auto& sources = pipeline->getSources();
+ ASSERT_EQ(1u, sources.size());
+ auto iter = sources.begin();
+ auto& singleDoc = dynamic_cast<DocumentSourceSingleDocumentTransformation&>(**iter);
+ // DocumenSourceSingleDoucmentTransformation reorders fields so we need to be insensitive.
+ ASSERT(UnorderedFieldsBSONObjComparator{}.evaluate(
+ BSON("_id" << true << "a"
+ << BSON("$atan2" << BSON_ARRAY(BSON("$const" << 1) << BSON("$const" << 0)))
+ << "b"
+ << BSON("$add" << BSON_ARRAY(BSON("$const" << 1)
+ << BSON("$const" << 2) << BSON("$const" << 3)
+ << BSON("$const" << 4)))) ==
+ singleDoc.getTransformer().serializeTransformation(boost::none).toBson()));
+}
+
+TEST(CstTest, FailsToTranslateComputedExclusionMixedProjectionStage) {
+ const auto cst = CNode{CNode::ArrayChildren{CNode{CNode::ObjectChildren{
+ {KeyFieldname::project,
+ CNode{CNode::ObjectChildren{
+ {UserFieldname{"a"},
+ CNode{CNode::ObjectChildren{
+ {KeyFieldname::atan2,
+ CNode{CNode::ArrayChildren{CNode{UserDouble{1.0}}, CNode{UserDecimal{0.0}}}}}}}},
+ {UserFieldname{"b"}, CNode{KeyValue::falseKey}}}}}}}}};
+ ASSERT_THROWS_CODE(
+ cst_pipeline_translation::translatePipeline(cst, getExpCtx()), DBException, 4933100);
+}
+
+TEST(CstTest, TranslatesComputedInclusionMixedProjectionStage) {
+ const auto cst = CNode{CNode::ArrayChildren{CNode{CNode::ObjectChildren{
+ {KeyFieldname::project,
+ CNode{CNode::ObjectChildren{
+ {UserFieldname{"a"},
+ CNode{CNode::ObjectChildren{
+ {KeyFieldname::add,
+ CNode{CNode::ArrayChildren{CNode{UserLong{0ll}}, CNode{UserInt{1}}}}}}}},
+ {UserFieldname{"b"}, CNode{NonZeroKey{Decimal128{590.095}}}}}}}}}}};
+ auto pipeline = cst_pipeline_translation::translatePipeline(cst, getExpCtx());
+ auto& sources = pipeline->getSources();
+ ASSERT_EQ(1u, sources.size());
+ auto iter = sources.begin();
+ auto& singleDoc = dynamic_cast<DocumentSourceSingleDocumentTransformation&>(**iter);
+ // DocumenSourceSingleDoucmentTransformation reorders fields so we need to be insensitive.
+ ASSERT(UnorderedFieldsBSONObjComparator{}.evaluate(
+ BSON("_id" << true << "a"
+ << BSON("$add" << BSON_ARRAY(BSON("$const" << 0ll) << BSON("$const" << 1)))
+ << "b" << true) ==
+ singleDoc.getTransformer().serializeTransformation(boost::none).toBson()));
+}
+
+TEST(CstTest, TranslatesMultipleProjectionStages) {
+ // [
+ // { $project: { a: true },
+ // { $project: { b: false },
+ // { $project: { c: { $add: [
+ // { $const: 2.2 },
+ // { $atan2: [ { $const: 1 }, { $const: 0 } ] },
+ // { $const: 3 } ] } } }
+ // ]
const auto cst = CNode{CNode::ArrayChildren{
CNode{CNode::ObjectChildren{
{KeyFieldname::project,
CNode{CNode::ObjectChildren{{UserFieldname{"a"}, CNode{KeyValue::trueKey}}}}}}},
CNode{CNode::ObjectChildren{
{KeyFieldname::project,
- CNode{CNode::ObjectChildren{{UserFieldname{"b"}, CNode{KeyValue::trueKey}}}}}}},
- CNode{CNode::ObjectChildren{
- {KeyFieldname::project,
- CNode{CNode::ObjectChildren{{UserFieldname{"c"}, CNode{KeyValue::trueKey}}}}}}},
+ CNode{CNode::ObjectChildren{{UserFieldname{"b"}, CNode{KeyValue::falseKey}}}}}}},
+ CNode{
+ CNode::ObjectChildren{{KeyFieldname::project,
+ CNode{CNode::ObjectChildren{
+ {UserFieldname{"c"},
+ CNode{CNode::ObjectChildren{
+ {KeyFieldname::add,
+ CNode{CNode::ArrayChildren{
+ CNode{UserDouble{2.2}},
+ CNode{CNode::ObjectChildren{
+ {KeyFieldname::atan2,
+ CNode{CNode::ArrayChildren{
+ CNode{UserInt{1}}, CNode{UserInt{0}}}}}}},
+ CNode{UserLong{3ll}}}}}}}}}}}}},
}};
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest(nss));
- auto pipeline = cst_pipeline_translation::translatePipeline(cst, expCtx);
+ auto pipeline = cst_pipeline_translation::translatePipeline(cst, getExpCtx());
auto& sources = pipeline->getSources();
ASSERT_EQ(3u, sources.size());
auto iter = sources.begin();
@@ -155,7 +272,7 @@ TEST(CstTest, TranslatesMultipleInclusionProjectionStages) {
// DocumenSourceSingleDoucmentTransformation reorders fields so we need to be
// insensitive.
ASSERT(UnorderedFieldsBSONObjComparator{}.evaluate(
- BSON("_id" << true << "b" << true) ==
+ BSON("b" << false) ==
singleDoc.getTransformer().serializeTransformation(boost::none).toBson()));
}
{
@@ -163,7 +280,12 @@ TEST(CstTest, TranslatesMultipleInclusionProjectionStages) {
// DocumenSourceSingleDoucmentTransformation reorders fields so we need to be
// insensitive.
ASSERT(UnorderedFieldsBSONObjComparator{}.evaluate(
- BSON("_id" << true << "c" << true) ==
+ BSON("_id" << true << "c"
+ << BSON("$add"
+ << BSON_ARRAY(BSON("$const" << 2.2)
+ << BSON("$atan2" << BSON_ARRAY(BSON("$const" << 1)
+ << BSON("$const" << 0)))
+ << BSON("$const" << 3ll)))) ==
singleDoc.getTransformer().serializeTransformation(boost::none).toBson()));
}
}
diff --git a/src/mongo/db/cst/cst_test.cpp b/src/mongo/db/cst/cst_test.cpp
index 4ed9b889f15..94e3ddfe6c4 100644
--- a/src/mongo/db/cst/cst_test.cpp
+++ b/src/mongo/db/cst/cst_test.cpp
@@ -241,6 +241,7 @@ TEST(CstGrammarTest, InvalidParseLimitString) {
auto parseTree = PipelineParserGen(lexer, &output);
ASSERT_THROWS_CODE(parseTree.parse(), AssertionException, ErrorCodes::FailedToParse);
}
+
TEST(CstGrammarTest, InvalidParseLimitObject) {
CNode output;
auto input = fromjson("{pipeline: [{$limit: {}}]}");
@@ -257,5 +258,54 @@ TEST(CstGrammarTest, InvalidParseLimitArray) {
ASSERT_THROWS_CODE(parseTree.parse(), AssertionException, ErrorCodes::FailedToParse);
}
+TEST(CstGrammarTest, ParsesProject) {
+ {
+ CNode output;
+ auto input =
+ fromjson("{pipeline: [{$project: {a: 1.0, b: NumberInt(1), _id: NumberLong(1)}}]}");
+ BSONLexer lexer(input["pipeline"].Array());
+ auto parseTree = PipelineParserGen(lexer, &output);
+ ASSERT_EQ(0, parseTree.parse());
+ auto stages = stdx::get<CNode::ArrayChildren>(output.payload);
+ ASSERT_EQ(1, stages.size());
+ ASSERT(KeyFieldname::project == stages[0].firstKeyFieldname());
+ ASSERT_EQ(stages[0].toBson().toString(),
+ "{ project: { a: \"<NonZeroKey of type double 1.000000>\", b: \"<NonZeroKey of "
+ "type int 1>\", id: \"<NonZeroKey of type long 1>\" } }");
+ }
+ {
+ CNode output;
+ auto input =
+ fromjson("{pipeline: [{$project: {a: 0.0, b: NumberInt(0), c: NumberLong(0)}}]}");
+ BSONLexer lexer(input["pipeline"].Array());
+ auto parseTree = PipelineParserGen(lexer, &output);
+ ASSERT_EQ(0, parseTree.parse());
+ auto stages = stdx::get<CNode::ArrayChildren>(output.payload);
+ ASSERT_EQ(1, stages.size());
+ ASSERT(KeyFieldname::project == stages[0].firstKeyFieldname());
+ ASSERT_EQ(stages[0].toBson().toString(),
+ "{ project: { a: \"<KeyValue doubleZeroKey>\", b: \"<KeyValue intZeroKey>\", "
+ "c: \"<KeyValue longZeroKey>\" } }");
+ }
+ {
+ CNode output;
+ auto input = fromjson(
+ "{pipeline: [{$project: {_id: 9.10, a: {$add: [4, 5, {$add: [6, 7, 8]}]}, b: "
+ "{$atan2: "
+ "[1.0, {$add: [2, -3]}]}}}]}");
+ BSONLexer lexer(input["pipeline"].Array());
+ auto parseTree = PipelineParserGen(lexer, &output);
+ ASSERT_EQ(0, parseTree.parse());
+ auto stages = stdx::get<CNode::ArrayChildren>(output.payload);
+ ASSERT_EQ(1, stages.size());
+ ASSERT(KeyFieldname::project == stages[0].firstKeyFieldname());
+ ASSERT_EQ(stages[0].toBson().toString(),
+ "{ project: { id: \"<NonZeroKey of type double 9.100000>\", a: { add: [ "
+ "\"<UserInt 4>\", \"<UserInt 5>\", { add: [ \"<UserInt 6>\", \"<UserInt 7>\", "
+ "\"<UserInt 8>\" ] } ] }, b: { atan2: [ \"<UserDouble 1.000000>\", { add: [ "
+ "\"<UserInt 2>\", \"<UserInt -3>\" ] } ] } } }");
+ }
+}
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/cst/key_fieldname.h b/src/mongo/db/cst/key_fieldname.h
index 5659ed15846..14b1ebcbc9b 100644
--- a/src/mongo/db/cst/key_fieldname.h
+++ b/src/mongo/db/cst/key_fieldname.h
@@ -34,6 +34,7 @@
#include "mongo/util/printable_enum.h"
#define KEYFIELDNAMES(ENUMIFY) \
+ ENUMIFY(add) \
ENUMIFY(atan2) \
ENUMIFY(id) \
ENUMIFY(project) \
diff --git a/src/mongo/db/cst/key_value.h b/src/mongo/db/cst/key_value.h
index 897c2b73652..fbd9d6f2a89 100644
--- a/src/mongo/db/cst/key_value.h
+++ b/src/mongo/db/cst/key_value.h
@@ -33,8 +33,12 @@
#include "mongo/util/printable_enum.h"
-#define KEYVALUES(ENUMIFY) \
- ENUMIFY(trueKey) \
+#define KEYVALUES(ENUMIFY) \
+ ENUMIFY(intZeroKey) \
+ ENUMIFY(longZeroKey) \
+ ENUMIFY(doubleZeroKey) \
+ ENUMIFY(decimalZeroKey) \
+ ENUMIFY(trueKey) \
ENUMIFY(falseKey)
MAKE_PRINTABLE_ENUM(KeyValue, KEYVALUES);
diff --git a/src/mongo/db/cst/location_gen.h b/src/mongo/db/cst/location_gen.h
index 3849efc00c0..c6549fdadbf 100644
--- a/src/mongo/db/cst/location_gen.h
+++ b/src/mongo/db/cst/location_gen.h
@@ -1,4 +1,4 @@
-// A Bison parser, made by GNU Bison 3.6.
+// A Bison parser, made by GNU Bison 3.6.3.
// Locations for Bison parsers in C++
@@ -31,12 +31,12 @@
// version 2.2 of Bison.
/**
- ** \file src/mongo/db/cst/location_gen.h
+ ** \file location_gen.h
** Define the mongo::location class.
*/
-#ifndef YY_YY_SRC_MONGO_DB_CST_LOCATION_GEN_H_INCLUDED
-#define YY_YY_SRC_MONGO_DB_CST_LOCATION_GEN_H_INCLUDED
+#ifndef YY_YY_LOCATION_GEN_H_INCLUDED
+#define YY_YY_LOCATION_GEN_H_INCLUDED
#include <iostream>
#include <string>
@@ -53,9 +53,9 @@
#endif
#endif
-#line 52 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 58 "pipeline_grammar.yy"
namespace mongo {
-#line 59 "src/mongo/db/cst/location_gen.h"
+#line 59 "location_gen.h"
/// A point in a source file.
class position {
@@ -260,8 +260,8 @@ std::basic_ostream<YYChar>& operator<<(std::basic_ostream<YYChar>& ostr, const l
return ostr;
}
-#line 52 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 58 "pipeline_grammar.yy"
} // namespace mongo
-#line 333 "src/mongo/db/cst/location_gen.h"
+#line 333 "location_gen.h"
-#endif // !YY_YY_SRC_MONGO_DB_CST_LOCATION_GEN_H_INCLUDED
+#endif // !YY_YY_LOCATION_GEN_H_INCLUDED
diff --git a/src/mongo/db/cst/pipeline_grammar.yy b/src/mongo/db/cst/pipeline_grammar.yy
index 824628e3a33..42bbae2ed9b 100644
--- a/src/mongo/db/cst/pipeline_grammar.yy
+++ b/src/mongo/db/cst/pipeline_grammar.yy
@@ -48,6 +48,12 @@
// of std::variant structure. This allows symbol declaration with '%type <C++ type> symbol'.
%define api.value.type variant
+// Every $foo becomes a std::move(*pull_foo_from_stack*). This makes the syntax cleaner and prevents
+// accidental copies but each $foo must be used only once per production rule! Move foo into an auto
+// variable first if multiple copies are needed. Manually writing std::move($foo) is harmless but
+// should be avoided since it's redundant.
+%define api.value.automove
+
%define parse.assert
%define api.namespace {mongo}
%define api.parser.class {PipelineParserGen}
@@ -76,6 +82,7 @@
// Cpp only.
%code {
#include "mongo/db/cst/bson_lexer.h"
+ #include "mongo/platform/decimal128.h"
namespace mongo {
// Mandatory error function.
@@ -105,29 +112,50 @@
START_ARRAY
END_ARRAY
+ ID
+
+ // Special literals
+ INT_ZERO
+ LONG_ZERO
+ DOUBLE_ZERO
+ DECIMAL_ZERO
+ TRUE
+ FALSE
+
// Reserve pipeline stage names.
STAGE_INHIBIT_OPTIMIZATION
STAGE_UNION_WITH
STAGE_SKIP
STAGE_LIMIT
+ STAGE_PROJECT
// $unionWith arguments.
COLL_ARG
PIPELINE_ARG
+ // Expressions
+ ADD
+ ATAN2
+
END_OF_FILE 0 "EOF"
;
+%token <std::string> FIELDNAME
%token <std::string> STRING
-%token <int> NUMBER_INT
-%token <long long> NUMBER_LONG
-%token <double> NUMBER_DOUBLE
-%token <bool> BOOL
+%token <int> INT_NON_ZERO
+%token <long long> LONG_NON_ZERO
+%token <double> DOUBLE_NON_ZERO
+%token <Decimal128> DECIMAL_NON_ZERO
//
// Semantic values (aka the C++ types produced by the actions).
//
%nterm <CNode> stageList stage inhibitOptimization unionWith num skip limit
+%nterm <CNode> project projectFields projection
+%nterm <CNode> compoundExpression expression maths add atan2 string int long double bool value
+%nterm <CNode::Fieldname> projectionFieldname
+%nterm <std::pair<CNode::Fieldname, CNode>> projectField
+%nterm <std::vector<CNode>> expressions
//
// Grammar rules
@@ -135,14 +163,16 @@
%%
// Entry point to pipeline parsing.
-pipeline: START_ARRAY stageList END_ARRAY {
- *cst = std::move($stageList);
-};
+pipeline:
+ START_ARRAY stageList END_ARRAY {
+ *cst = $stageList;
+ }
+;
-stageList[result]:
+stageList:
%empty { }
| START_OBJECT stage END_OBJECT stageList[stagesArg] {
- $result = CNode{CNode::ArrayChildren{$stage}};
+ $$ = CNode{CNode::ArrayChildren{$stage}};
}
;
@@ -152,36 +182,27 @@ stageList[result]:
START_ORDERED_OBJECT: { lexer.sortObjTokens(); } START_OBJECT;
stage:
- inhibitOptimization | unionWith | skip | limit
+ inhibitOptimization | unionWith | skip | limit | project
;
inhibitOptimization:
STAGE_INHIBIT_OPTIMIZATION START_OBJECT END_OBJECT {
- $inhibitOptimization =
-CNode{CNode::ObjectChildren{std::pair{KeyFieldname::inhibitOptimization, CNode::noopLeaf()}}};
- };
+ $$ = CNode{CNode::ObjectChildren{std::pair{KeyFieldname::inhibitOptimization, CNode::noopLeaf()}}};
+ }
+;
unionWith:
- STAGE_UNION_WITH START_ORDERED_OBJECT COLL_ARG STRING PIPELINE_ARG NUMBER_DOUBLE END_OBJECT {
- auto coll = CNode{UserString($STRING)};
- auto pipeline = CNode{UserDouble($NUMBER_DOUBLE)};
- $unionWith = CNode{CNode::ObjectChildren{std::pair{KeyFieldname::unionWith,
+ STAGE_UNION_WITH START_ORDERED_OBJECT COLL_ARG string PIPELINE_ARG double END_OBJECT {
+ auto pipeline = $double;
+ $$ = CNode{CNode::ObjectChildren{std::pair{KeyFieldname::unionWith,
CNode{CNode::ObjectChildren{
- {KeyFieldname::collArg, std::move(coll)},
+ {KeyFieldname::collArg, $string},
{KeyFieldname::pipelineArg, std::move(pipeline)}
}}}}};
};
num:
- NUMBER_INT {
- $num = CNode{UserInt($NUMBER_INT)};
- }
- | NUMBER_LONG {
- $num = CNode{UserLong($NUMBER_LONG)};
- }
- | NUMBER_DOUBLE {
- $num = CNode{UserDouble($NUMBER_DOUBLE)};
- }
+ int | long | double
;
skip:
@@ -194,4 +215,183 @@ limit:
$limit = CNode{CNode::ObjectChildren{std::pair{KeyFieldname::limit, $num}}};
};
+project:
+ STAGE_PROJECT START_OBJECT projectFields END_OBJECT {
+ $$ = CNode{CNode::ObjectChildren{std::pair{KeyFieldname::project, $projectFields}}};
+ }
+;
+
+projectFields:
+ %empty {
+ $$ = CNode::noopLeaf();
+ }
+ | projectFields[projectArg] projectField {
+ $$ = $projectArg;
+ $$.objectChildren().emplace_back($projectField);
+ }
+;
+
+projectField:
+ ID projection {
+ $$ = {KeyFieldname::id, $projection};
+ }
+ | projectionFieldname projection {
+ $$ = {$projectionFieldname, $projection};
+ }
+;
+
+projection:
+ string
+ | INT_NON_ZERO {
+ $$ = CNode{NonZeroKey{$1}};
+ }
+ | INT_ZERO {
+ $$ = CNode{KeyValue::intZeroKey};
+ }
+ | LONG_NON_ZERO {
+ $$ = CNode{NonZeroKey{$1}};
+ }
+ | LONG_ZERO {
+ $$ = CNode{KeyValue::longZeroKey};
+ }
+ | DOUBLE_NON_ZERO {
+ $$ = CNode{NonZeroKey{$1}};
+ }
+ | DOUBLE_ZERO {
+ $$ = CNode{KeyValue::doubleZeroKey};
+ }
+ | DECIMAL_NON_ZERO {
+ $$ = CNode{NonZeroKey{$1}};
+ }
+ | DECIMAL_ZERO {
+ $$ = CNode{KeyValue::decimalZeroKey};
+ }
+ | TRUE {
+ $$ = CNode{KeyValue::trueKey};
+ }
+ | FALSE {
+ $$ = CNode{KeyValue::falseKey};
+ }
+ | compoundExpression
+;
+
+projectionFieldname:
+ FIELDNAME {
+ $$ = UserFieldname{$1};
+ }
+ // Here we need to list all key Fieldnames so they can be converted back to string in contexts
+ // where they're not special. It's laborious but this is the perennial Bison way.
+ | STAGE_INHIBIT_OPTIMIZATION {
+ $$ = UserFieldname{"$_internalInhibitOptimization"};
+ }
+ | STAGE_UNION_WITH {
+ $$ = UserFieldname{"$unionWith"};
+ }
+ | STAGE_PROJECT {
+ $$ = UserFieldname{"$project"};
+ }
+ | COLL_ARG {
+ $$ = UserFieldname{"coll"};
+ }
+ | PIPELINE_ARG {
+ $$ = UserFieldname{"pipeline"};
+ }
+ | ADD {
+ $$ = UserFieldname{"$add"};
+ }
+ | ATAN2 {
+ $$ = UserFieldname{"$atan2"};
+ }
+;
+
+string:
+ STRING {
+ $$ = CNode{UserString{$1}};
+ }
+;
+
+int:
+ INT_NON_ZERO {
+ $$ = CNode{UserInt{$1}};
+ }
+ | INT_ZERO {
+ $$ = CNode{UserLong{0}};
+ }
+;
+
+long:
+ LONG_NON_ZERO {
+ $$ = CNode{UserLong{$1}};
+ }
+ | LONG_ZERO {
+ $$ = CNode{UserLong{0ll}};
+ }
+;
+
+double:
+ DOUBLE_NON_ZERO {
+ $$ = CNode{UserDouble{$1}};
+ }
+ | DOUBLE_ZERO {
+ $$ = CNode{UserDouble{0.0}};
+ }
+;
+
+bool:
+ TRUE {
+ $$ = CNode{UserBoolean{true}};
+ }
+ | FALSE {
+ $$ = CNode{UserBoolean{false}};
+ }
+;
+
+value:
+ string
+ | int
+ | long
+ | double
+ | bool
+;
+
+// Zero or more expressions. Specify mandatory expressions in a rule using the 'expression'
+// nonterminal and append this non-terminal if an unbounded number of additional optional
+// expressions are allowed.
+expressions:
+ %empty { }
+ | expression expressions[expressionArg] {
+ $$ = $expressionArg;
+ $$.emplace_back($expression);
+ }
+
+expression:
+ value
+ | compoundExpression
+;
+
+compoundExpression:
+ maths
+;
+
+maths:
+ add
+ | atan2
+;
+add:
+ START_OBJECT ADD START_ARRAY expression[expr1] expression[expr2] expressions END_ARRAY END_OBJECT {
+ $$ = CNode{CNode::ObjectChildren{{KeyFieldname::add,
+ CNode{CNode::ArrayChildren{$expr1, $expr2}}}}};
+ auto&& others = $expressions;
+ auto&& array = $$.objectChildren()[0].second.arrayChildren();
+ array.insert(array.end(), others.begin(), others.end());
+ }
+;
+
+atan2:
+ START_OBJECT ATAN2 START_ARRAY expression[expr1] expression[expr2] END_ARRAY END_OBJECT {
+ $$ = CNode{CNode::ObjectChildren{{KeyFieldname::atan2,
+ CNode{CNode::ArrayChildren{$expr1, $expr2}}}}};
+ }
+;
+
%%
diff --git a/src/mongo/db/cst/pipeline_parser_gen.cpp b/src/mongo/db/cst/pipeline_parser_gen.cpp
index 1767319051f..3c66acbaf7a 100644
--- a/src/mongo/db/cst/pipeline_parser_gen.cpp
+++ b/src/mongo/db/cst/pipeline_parser_gen.cpp
@@ -1,4 +1,4 @@
-// A Bison parser, made by GNU Bison 3.6.
+// A Bison parser, made by GNU Bison 3.6.3.
// Skeleton implementation for Bison LALR(1) parsers in C++
@@ -39,9 +39,10 @@
// Unqualified %code blocks.
-#line 77 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 83 "pipeline_grammar.yy"
#include "mongo/db/cst/bson_lexer.h"
+#include "mongo/platform/decimal128.h"
namespace mongo {
// Mandatory error function.
@@ -53,7 +54,7 @@ void PipelineParserGen::error(const PipelineParserGen::location_type& loc, const
}
} // namespace mongo
-#line 62 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 63 "pipeline_parser_gen.cpp"
#ifndef YY_
@@ -144,16 +145,9 @@ void PipelineParserGen::error(const PipelineParserGen::location_type& loc, const
#define YYERROR goto yyerrorlab
#define YYRECOVERING() (!!yyerrstatus_)
-#line 52 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 58 "pipeline_grammar.yy"
namespace mongo {
-#line 155 "src/mongo/db/cst/pipeline_parser_gen.cpp"
-
-#if YYDEBUG || 0
-const char* PipelineParserGen::symbol_name(symbol_kind_type yysymbol) {
- return yytname_[yysymbol];
-}
-#endif // #if YYDEBUG || 0
-
+#line 156 "pipeline_parser_gen.cpp"
/// Build a parser object.
PipelineParserGen::PipelineParserGen(BSONLexer& lexer_yyarg, CNode* cst_yyarg)
@@ -204,36 +198,63 @@ PipelineParserGen::stack_symbol_type::stack_symbol_type() {}
PipelineParserGen::stack_symbol_type::stack_symbol_type(YY_RVREF(stack_symbol_type) that)
: super_type(YY_MOVE(that.state), YY_MOVE(that.location)) {
switch (that.kind()) {
- case 19: // stageList
- case 20: // stage
- case 21: // inhibitOptimization
- case 22: // unionWith
- case 23: // num
- case 24: // skip
- case 25: // limit
+ case 30: // stageList
+ case 31: // stage
+ case 32: // inhibitOptimization
+ case 33: // unionWith
+ case 34: // num
+ case 35: // skip
+ case 36: // limit
+ case 37: // project
+ case 38: // projectFields
+ case 39: // projection
+ case 40: // compoundExpression
+ case 41: // expression
+ case 42: // maths
+ case 43: // add
+ case 44: // atan2
+ case 45: // string
+ case 46: // int
+ case 47: // long
+ case 48: // double
+ case 49: // bool
+ case 50: // value
value.YY_MOVE_OR_COPY<CNode>(YY_MOVE(that.value));
break;
- case 17: // BOOL
- value.YY_MOVE_OR_COPY<bool>(YY_MOVE(that.value));
+ case 51: // projectionFieldname
+ value.YY_MOVE_OR_COPY<CNode::Fieldname>(YY_MOVE(that.value));
+ break;
+
+ case 28: // DECIMAL_NON_ZERO
+ value.YY_MOVE_OR_COPY<Decimal128>(YY_MOVE(that.value));
break;
- case 16: // NUMBER_DOUBLE
+ case 27: // DOUBLE_NON_ZERO
value.YY_MOVE_OR_COPY<double>(YY_MOVE(that.value));
break;
- case 14: // NUMBER_INT
+ case 25: // INT_NON_ZERO
value.YY_MOVE_OR_COPY<int>(YY_MOVE(that.value));
break;
- case 15: // NUMBER_LONG
+ case 26: // LONG_NON_ZERO
value.YY_MOVE_OR_COPY<long long>(YY_MOVE(that.value));
break;
- case 13: // STRING
+ case 52: // projectField
+ value.YY_MOVE_OR_COPY<std::pair<CNode::Fieldname, CNode>>(YY_MOVE(that.value));
+ break;
+
+ case 23: // FIELDNAME
+ case 24: // STRING
value.YY_MOVE_OR_COPY<std::string>(YY_MOVE(that.value));
break;
+ case 53: // expressions
+ value.YY_MOVE_OR_COPY<std::vector<CNode>>(YY_MOVE(that.value));
+ break;
+
default:
break;
}
@@ -247,36 +268,63 @@ PipelineParserGen::stack_symbol_type::stack_symbol_type(YY_RVREF(stack_symbol_ty
PipelineParserGen::stack_symbol_type::stack_symbol_type(state_type s, YY_MOVE_REF(symbol_type) that)
: super_type(s, YY_MOVE(that.location)) {
switch (that.kind()) {
- case 19: // stageList
- case 20: // stage
- case 21: // inhibitOptimization
- case 22: // unionWith
- case 23: // num
- case 24: // skip
- case 25: // limit
+ case 30: // stageList
+ case 31: // stage
+ case 32: // inhibitOptimization
+ case 33: // unionWith
+ case 34: // num
+ case 35: // skip
+ case 36: // limit
+ case 37: // project
+ case 38: // projectFields
+ case 39: // projection
+ case 40: // compoundExpression
+ case 41: // expression
+ case 42: // maths
+ case 43: // add
+ case 44: // atan2
+ case 45: // string
+ case 46: // int
+ case 47: // long
+ case 48: // double
+ case 49: // bool
+ case 50: // value
value.move<CNode>(YY_MOVE(that.value));
break;
- case 17: // BOOL
- value.move<bool>(YY_MOVE(that.value));
+ case 51: // projectionFieldname
+ value.move<CNode::Fieldname>(YY_MOVE(that.value));
break;
- case 16: // NUMBER_DOUBLE
+ case 28: // DECIMAL_NON_ZERO
+ value.move<Decimal128>(YY_MOVE(that.value));
+ break;
+
+ case 27: // DOUBLE_NON_ZERO
value.move<double>(YY_MOVE(that.value));
break;
- case 14: // NUMBER_INT
+ case 25: // INT_NON_ZERO
value.move<int>(YY_MOVE(that.value));
break;
- case 15: // NUMBER_LONG
+ case 26: // LONG_NON_ZERO
value.move<long long>(YY_MOVE(that.value));
break;
- case 13: // STRING
+ case 52: // projectField
+ value.move<std::pair<CNode::Fieldname, CNode>>(YY_MOVE(that.value));
+ break;
+
+ case 23: // FIELDNAME
+ case 24: // STRING
value.move<std::string>(YY_MOVE(that.value));
break;
+ case 53: // expressions
+ value.move<std::vector<CNode>>(YY_MOVE(that.value));
+ break;
+
default:
break;
}
@@ -290,36 +338,63 @@ PipelineParserGen::stack_symbol_type& PipelineParserGen::stack_symbol_type::oper
const stack_symbol_type& that) {
state = that.state;
switch (that.kind()) {
- case 19: // stageList
- case 20: // stage
- case 21: // inhibitOptimization
- case 22: // unionWith
- case 23: // num
- case 24: // skip
- case 25: // limit
+ case 30: // stageList
+ case 31: // stage
+ case 32: // inhibitOptimization
+ case 33: // unionWith
+ case 34: // num
+ case 35: // skip
+ case 36: // limit
+ case 37: // project
+ case 38: // projectFields
+ case 39: // projection
+ case 40: // compoundExpression
+ case 41: // expression
+ case 42: // maths
+ case 43: // add
+ case 44: // atan2
+ case 45: // string
+ case 46: // int
+ case 47: // long
+ case 48: // double
+ case 49: // bool
+ case 50: // value
value.copy<CNode>(that.value);
break;
- case 17: // BOOL
- value.copy<bool>(that.value);
+ case 51: // projectionFieldname
+ value.copy<CNode::Fieldname>(that.value);
+ break;
+
+ case 28: // DECIMAL_NON_ZERO
+ value.copy<Decimal128>(that.value);
break;
- case 16: // NUMBER_DOUBLE
+ case 27: // DOUBLE_NON_ZERO
value.copy<double>(that.value);
break;
- case 14: // NUMBER_INT
+ case 25: // INT_NON_ZERO
value.copy<int>(that.value);
break;
- case 15: // NUMBER_LONG
+ case 26: // LONG_NON_ZERO
value.copy<long long>(that.value);
break;
- case 13: // STRING
+ case 52: // projectField
+ value.copy<std::pair<CNode::Fieldname, CNode>>(that.value);
+ break;
+
+ case 23: // FIELDNAME
+ case 24: // STRING
value.copy<std::string>(that.value);
break;
+ case 53: // expressions
+ value.copy<std::vector<CNode>>(that.value);
+ break;
+
default:
break;
}
@@ -332,36 +407,63 @@ PipelineParserGen::stack_symbol_type& PipelineParserGen::stack_symbol_type::oper
stack_symbol_type& that) {
state = that.state;
switch (that.kind()) {
- case 19: // stageList
- case 20: // stage
- case 21: // inhibitOptimization
- case 22: // unionWith
- case 23: // num
- case 24: // skip
- case 25: // limit
+ case 30: // stageList
+ case 31: // stage
+ case 32: // inhibitOptimization
+ case 33: // unionWith
+ case 34: // num
+ case 35: // skip
+ case 36: // limit
+ case 37: // project
+ case 38: // projectFields
+ case 39: // projection
+ case 40: // compoundExpression
+ case 41: // expression
+ case 42: // maths
+ case 43: // add
+ case 44: // atan2
+ case 45: // string
+ case 46: // int
+ case 47: // long
+ case 48: // double
+ case 49: // bool
+ case 50: // value
value.move<CNode>(that.value);
break;
- case 17: // BOOL
- value.move<bool>(that.value);
+ case 51: // projectionFieldname
+ value.move<CNode::Fieldname>(that.value);
break;
- case 16: // NUMBER_DOUBLE
+ case 28: // DECIMAL_NON_ZERO
+ value.move<Decimal128>(that.value);
+ break;
+
+ case 27: // DOUBLE_NON_ZERO
value.move<double>(that.value);
break;
- case 14: // NUMBER_INT
+ case 25: // INT_NON_ZERO
value.move<int>(that.value);
break;
- case 15: // NUMBER_LONG
+ case 26: // LONG_NON_ZERO
value.move<long long>(that.value);
break;
- case 13: // STRING
+ case 52: // projectField
+ value.move<std::pair<CNode::Fieldname, CNode>>(that.value);
+ break;
+
+ case 23: // FIELDNAME
+ case 24: // STRING
value.move<std::string>(that.value);
break;
+ case 53: // expressions
+ value.move<std::vector<CNode>>(that.value);
+ break;
+
default:
break;
}
@@ -388,7 +490,7 @@ void PipelineParserGen::yy_print_(std::ostream& yyo, const basic_symbol<Base>& y
yyo << "empty symbol";
else {
symbol_kind_type yykind = yysym.kind();
- yyo << (yykind < YYNTOKENS ? "token" : "nterm") << ' ' << symbol_name(yykind) << " ("
+ yyo << (yykind < YYNTOKENS ? "token" : "nterm") << ' ' << yysym.name() << " ("
<< yysym.location << ": ";
YYUSE(yykind);
yyo << ')';
@@ -585,36 +687,63 @@ int PipelineParserGen::parse() {
correct type. The default '$$ = $1' action is NOT applied
when using variants. */
switch (yyr1_[yyn]) {
- case 19: // stageList
- case 20: // stage
- case 21: // inhibitOptimization
- case 22: // unionWith
- case 23: // num
- case 24: // skip
- case 25: // limit
+ case 30: // stageList
+ case 31: // stage
+ case 32: // inhibitOptimization
+ case 33: // unionWith
+ case 34: // num
+ case 35: // skip
+ case 36: // limit
+ case 37: // project
+ case 38: // projectFields
+ case 39: // projection
+ case 40: // compoundExpression
+ case 41: // expression
+ case 42: // maths
+ case 43: // add
+ case 44: // atan2
+ case 45: // string
+ case 46: // int
+ case 47: // long
+ case 48: // double
+ case 49: // bool
+ case 50: // value
yylhs.value.emplace<CNode>();
break;
- case 17: // BOOL
- yylhs.value.emplace<bool>();
+ case 51: // projectionFieldname
+ yylhs.value.emplace<CNode::Fieldname>();
+ break;
+
+ case 28: // DECIMAL_NON_ZERO
+ yylhs.value.emplace<Decimal128>();
break;
- case 16: // NUMBER_DOUBLE
+ case 27: // DOUBLE_NON_ZERO
yylhs.value.emplace<double>();
break;
- case 14: // NUMBER_INT
+ case 25: // INT_NON_ZERO
yylhs.value.emplace<int>();
break;
- case 15: // NUMBER_LONG
+ case 26: // LONG_NON_ZERO
yylhs.value.emplace<long long>();
break;
- case 13: // STRING
+ case 52: // projectField
+ yylhs.value.emplace<std::pair<CNode::Fieldname, CNode>>();
+ break;
+
+ case 23: // FIELDNAME
+ case 24: // STRING
yylhs.value.emplace<std::string>();
break;
+ case 53: // expressions
+ yylhs.value.emplace<std::vector<CNode>>();
+ break;
+
default:
break;
}
@@ -635,137 +764,555 @@ int PipelineParserGen::parse() {
{
switch (yyn) {
case 2:
-#line 138 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 167 "pipeline_grammar.yy"
{
- *cst = std::move(yystack_[1].value.as<CNode>());
+ *cst = YY_MOVE(yystack_[1].value.as<CNode>());
}
-#line 711 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 838 "pipeline_parser_gen.cpp"
break;
case 3:
-#line 143 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 173 "pipeline_grammar.yy"
{
}
-#line 717 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 844 "pipeline_parser_gen.cpp"
break;
case 4:
-#line 144 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 174 "pipeline_grammar.yy"
{
yylhs.value.as<CNode>() =
- CNode{CNode::ArrayChildren{yystack_[2].value.as<CNode>()}};
+ CNode{CNode::ArrayChildren{YY_MOVE(yystack_[2].value.as<CNode>())}};
}
-#line 725 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 852 "pipeline_parser_gen.cpp"
break;
case 5:
-#line 152 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 182 "pipeline_grammar.yy"
{
lexer.sortObjTokens();
}
-#line 731 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 858 "pipeline_parser_gen.cpp"
break;
case 7:
-#line 155 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 185 "pipeline_grammar.yy"
{
- yylhs.value.as<CNode>() = yystack_[0].value.as<CNode>();
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
}
-#line 737 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 864 "pipeline_parser_gen.cpp"
break;
case 8:
-#line 155 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 185 "pipeline_grammar.yy"
{
- yylhs.value.as<CNode>() = yystack_[0].value.as<CNode>();
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
}
-#line 743 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 870 "pipeline_parser_gen.cpp"
break;
case 9:
-#line 155 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 185 "pipeline_grammar.yy"
{
- yylhs.value.as<CNode>() = yystack_[0].value.as<CNode>();
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
}
-#line 749 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 876 "pipeline_parser_gen.cpp"
break;
case 10:
-#line 155 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 185 "pipeline_grammar.yy"
{
- yylhs.value.as<CNode>() = yystack_[0].value.as<CNode>();
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
}
-#line 755 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 882 "pipeline_parser_gen.cpp"
break;
case 11:
-#line 159 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 185 "pipeline_grammar.yy"
{
- yylhs.value.as<CNode>() = CNode{CNode::ObjectChildren{
- std::pair{KeyFieldname::inhibitOptimization, CNode::noopLeaf()}}};
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
}
-#line 764 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 888 "pipeline_parser_gen.cpp"
break;
case 12:
-#line 165 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 189 "pipeline_grammar.yy"
{
- auto coll = CNode{UserString(yystack_[3].value.as<std::string>())};
- auto pipeline = CNode{UserDouble(yystack_[1].value.as<double>())};
yylhs.value.as<CNode>() = CNode{CNode::ObjectChildren{
- std::pair{KeyFieldname::unionWith,
- CNode{CNode::ObjectChildren{
- {KeyFieldname::collArg, std::move(coll)},
- {KeyFieldname::pipelineArg, std::move(pipeline)}}}}}};
+ std::pair{KeyFieldname::inhibitOptimization, CNode::noopLeaf()}}};
}
-#line 778 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 896 "pipeline_parser_gen.cpp"
break;
case 13:
-#line 176 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 195 "pipeline_grammar.yy"
{
- yylhs.value.as<CNode>() = CNode{UserInt(yystack_[0].value.as<int>())};
+ auto pipeline = YY_MOVE(yystack_[1].value.as<CNode>());
+ yylhs.value.as<CNode>() = CNode{CNode::ObjectChildren{std::pair{
+ KeyFieldname::unionWith,
+ CNode{CNode::ObjectChildren{
+ {KeyFieldname::collArg, YY_MOVE(yystack_[3].value.as<CNode>())},
+ {KeyFieldname::pipelineArg, std::move(pipeline)}}}}}};
}
-#line 786 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 909 "pipeline_parser_gen.cpp"
break;
case 14:
-#line 179 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 205 "pipeline_grammar.yy"
{
- yylhs.value.as<CNode>() =
- CNode{UserLong(yystack_[0].value.as<long long>())};
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
}
-#line 794 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 915 "pipeline_parser_gen.cpp"
break;
case 15:
-#line 182 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 205 "pipeline_grammar.yy"
{
- yylhs.value.as<CNode>() = CNode{UserDouble(yystack_[0].value.as<double>())};
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
}
-#line 802 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 921 "pipeline_parser_gen.cpp"
break;
case 16:
-#line 188 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 205 "pipeline_grammar.yy"
{
- yylhs.value.as<CNode>() = CNode{CNode::ObjectChildren{
- std::pair{KeyFieldname::skip, yystack_[0].value.as<CNode>()}}};
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
}
-#line 810 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 927 "pipeline_parser_gen.cpp"
break;
case 17:
-#line 193 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 209 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{CNode::ObjectChildren{
+ std::pair{KeyFieldname::skip, YY_MOVE(yystack_[0].value.as<CNode>())}}};
+ }
+#line 935 "pipeline_parser_gen.cpp"
+ break;
+
+ case 18:
+#line 214 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{CNode::ObjectChildren{std::pair{
+ KeyFieldname::limit, YY_MOVE(yystack_[0].value.as<CNode>())}}};
+ }
+#line 943 "pipeline_parser_gen.cpp"
+ break;
+
+ case 19:
+#line 219 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{CNode::ObjectChildren{std::pair{
+ KeyFieldname::project, YY_MOVE(yystack_[1].value.as<CNode>())}}};
+ }
+#line 951 "pipeline_parser_gen.cpp"
+ break;
+
+ case 20:
+#line 225 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode::noopLeaf();
+ }
+#line 959 "pipeline_parser_gen.cpp"
+ break;
+
+ case 21:
+#line 228 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[1].value.as<CNode>());
+ yylhs.value.as<CNode>().objectChildren().emplace_back(
+ YY_MOVE(yystack_[0].value.as<std::pair<CNode::Fieldname, CNode>>()));
+ }
+#line 968 "pipeline_parser_gen.cpp"
+ break;
+
+ case 22:
+#line 235 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<std::pair<CNode::Fieldname, CNode>>() = {
+ KeyFieldname::id, YY_MOVE(yystack_[0].value.as<CNode>())};
+ }
+#line 976 "pipeline_parser_gen.cpp"
+ break;
+
+ case 23:
+#line 238 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<std::pair<CNode::Fieldname, CNode>>() = {
+ YY_MOVE(yystack_[1].value.as<CNode::Fieldname>()),
+ YY_MOVE(yystack_[0].value.as<CNode>())};
+ }
+#line 984 "pipeline_parser_gen.cpp"
+ break;
+
+ case 24:
+#line 244 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
+ }
+#line 990 "pipeline_parser_gen.cpp"
+ break;
+
+ case 25:
+#line 245 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() =
+ CNode{NonZeroKey{YY_MOVE(yystack_[0].value.as<int>())}};
+ }
+#line 998 "pipeline_parser_gen.cpp"
+ break;
+
+ case 26:
+#line 248 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{KeyValue::intZeroKey};
+ }
+#line 1006 "pipeline_parser_gen.cpp"
+ break;
+
+ case 27:
+#line 251 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() =
+ CNode{NonZeroKey{YY_MOVE(yystack_[0].value.as<long long>())}};
+ }
+#line 1014 "pipeline_parser_gen.cpp"
+ break;
+
+ case 28:
+#line 254 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{KeyValue::longZeroKey};
+ }
+#line 1022 "pipeline_parser_gen.cpp"
+ break;
+
+ case 29:
+#line 257 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() =
+ CNode{NonZeroKey{YY_MOVE(yystack_[0].value.as<double>())}};
+ }
+#line 1030 "pipeline_parser_gen.cpp"
+ break;
+
+ case 30:
+#line 260 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{KeyValue::doubleZeroKey};
+ }
+#line 1038 "pipeline_parser_gen.cpp"
+ break;
+
+ case 31:
+#line 263 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() =
+ CNode{NonZeroKey{YY_MOVE(yystack_[0].value.as<Decimal128>())}};
+ }
+#line 1046 "pipeline_parser_gen.cpp"
+ break;
+
+ case 32:
+#line 266 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{KeyValue::decimalZeroKey};
+ }
+#line 1054 "pipeline_parser_gen.cpp"
+ break;
+
+ case 33:
+#line 269 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{KeyValue::trueKey};
+ }
+#line 1062 "pipeline_parser_gen.cpp"
+ break;
+
+ case 34:
+#line 272 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{KeyValue::falseKey};
+ }
+#line 1070 "pipeline_parser_gen.cpp"
+ break;
+
+ case 35:
+#line 275 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
+ }
+#line 1076 "pipeline_parser_gen.cpp"
+ break;
+
+ case 36:
+#line 279 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode::Fieldname>() =
+ UserFieldname{YY_MOVE(yystack_[0].value.as<std::string>())};
+ }
+#line 1084 "pipeline_parser_gen.cpp"
+ break;
+
+ case 37:
+#line 284 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode::Fieldname>() =
+ UserFieldname{"$_internalInhibitOptimization"};
+ }
+#line 1092 "pipeline_parser_gen.cpp"
+ break;
+
+ case 38:
+#line 287 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode::Fieldname>() = UserFieldname{"$unionWith"};
+ }
+#line 1100 "pipeline_parser_gen.cpp"
+ break;
+
+ case 39:
+#line 290 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode::Fieldname>() = UserFieldname{"$project"};
+ }
+#line 1108 "pipeline_parser_gen.cpp"
+ break;
+
+ case 40:
+#line 293 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode::Fieldname>() = UserFieldname{"coll"};
+ }
+#line 1116 "pipeline_parser_gen.cpp"
+ break;
+
+ case 41:
+#line 296 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode::Fieldname>() = UserFieldname{"pipeline"};
+ }
+#line 1124 "pipeline_parser_gen.cpp"
+ break;
+
+ case 42:
+#line 299 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode::Fieldname>() = UserFieldname{"$add"};
+ }
+#line 1132 "pipeline_parser_gen.cpp"
+ break;
+
+ case 43:
+#line 302 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode::Fieldname>() = UserFieldname{"$atan2"};
+ }
+#line 1140 "pipeline_parser_gen.cpp"
+ break;
+
+ case 44:
+#line 308 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() =
+ CNode{UserString{YY_MOVE(yystack_[0].value.as<std::string>())}};
+ }
+#line 1148 "pipeline_parser_gen.cpp"
+ break;
+
+ case 45:
+#line 314 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() =
+ CNode{UserInt{YY_MOVE(yystack_[0].value.as<int>())}};
+ }
+#line 1156 "pipeline_parser_gen.cpp"
+ break;
+
+ case 46:
+#line 317 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{UserLong{0}};
+ }
+#line 1164 "pipeline_parser_gen.cpp"
+ break;
+
+ case 47:
+#line 323 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() =
+ CNode{UserLong{YY_MOVE(yystack_[0].value.as<long long>())}};
+ }
+#line 1172 "pipeline_parser_gen.cpp"
+ break;
+
+ case 48:
+#line 326 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{UserLong{0ll}};
+ }
+#line 1180 "pipeline_parser_gen.cpp"
+ break;
+
+ case 49:
+#line 332 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() =
+ CNode{UserDouble{YY_MOVE(yystack_[0].value.as<double>())}};
+ }
+#line 1188 "pipeline_parser_gen.cpp"
+ break;
+
+ case 50:
+#line 335 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{UserDouble{0.0}};
+ }
+#line 1196 "pipeline_parser_gen.cpp"
+ break;
+
+ case 51:
+#line 341 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{UserBoolean{true}};
+ }
+#line 1204 "pipeline_parser_gen.cpp"
+ break;
+
+ case 52:
+#line 344 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{UserBoolean{false}};
+ }
+#line 1212 "pipeline_parser_gen.cpp"
+ break;
+
+ case 53:
+#line 350 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
+ }
+#line 1218 "pipeline_parser_gen.cpp"
+ break;
+
+ case 54:
+#line 351 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
+ }
+#line 1224 "pipeline_parser_gen.cpp"
+ break;
+
+ case 55:
+#line 352 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
+ }
+#line 1230 "pipeline_parser_gen.cpp"
+ break;
+
+ case 56:
+#line 353 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
+ }
+#line 1236 "pipeline_parser_gen.cpp"
+ break;
+
+ case 57:
+#line 354 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
+ }
+#line 1242 "pipeline_parser_gen.cpp"
+ break;
+
+ case 58:
+#line 361 "pipeline_grammar.yy"
+ {
+ }
+#line 1248 "pipeline_parser_gen.cpp"
+ break;
+
+ case 59:
+#line 362 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<std::vector<CNode>>() =
+ YY_MOVE(yystack_[0].value.as<std::vector<CNode>>());
+ yylhs.value.as<std::vector<CNode>>().emplace_back(
+ YY_MOVE(yystack_[1].value.as<CNode>()));
+ }
+#line 1257 "pipeline_parser_gen.cpp"
+ break;
+
+ case 60:
+#line 368 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
+ }
+#line 1263 "pipeline_parser_gen.cpp"
+ break;
+
+ case 61:
+#line 369 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
+ }
+#line 1269 "pipeline_parser_gen.cpp"
+ break;
+
+ case 62:
+#line 373 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
+ }
+#line 1275 "pipeline_parser_gen.cpp"
+ break;
+
+ case 63:
+#line 377 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
+ }
+#line 1281 "pipeline_parser_gen.cpp"
+ break;
+
+ case 64:
+#line 378 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = YY_MOVE(yystack_[0].value.as<CNode>());
+ }
+#line 1287 "pipeline_parser_gen.cpp"
+ break;
+
+ case 65:
+#line 381 "pipeline_grammar.yy"
+ {
+ yylhs.value.as<CNode>() = CNode{CNode::ObjectChildren{
+ {KeyFieldname::add,
+ CNode{CNode::ArrayChildren{YY_MOVE(yystack_[4].value.as<CNode>()),
+ YY_MOVE(yystack_[3].value.as<CNode>())}}}}};
+ auto&& others = YY_MOVE(yystack_[2].value.as<std::vector<CNode>>());
+ auto&& array =
+ yylhs.value.as<CNode>().objectChildren()[0].second.arrayChildren();
+ array.insert(array.end(), others.begin(), others.end());
+ }
+#line 1299 "pipeline_parser_gen.cpp"
+ break;
+
+ case 66:
+#line 391 "pipeline_grammar.yy"
{
yylhs.value.as<CNode>() = CNode{CNode::ObjectChildren{
- std::pair{KeyFieldname::limit, yystack_[0].value.as<CNode>()}}};
+ {KeyFieldname::atan2,
+ CNode{CNode::ArrayChildren{YY_MOVE(yystack_[3].value.as<CNode>()),
+ YY_MOVE(yystack_[2].value.as<CNode>())}}}}};
}
-#line 818 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 1308 "pipeline_parser_gen.cpp"
break;
-#line 822 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 1312 "pipeline_parser_gen.cpp"
default:
break;
@@ -929,39 +1476,68 @@ void PipelineParserGen::error(const syntax_error& yyexc) {
error(yyexc.location, yyexc.what());
}
+#if YYDEBUG || 0
+const char* PipelineParserGen::symbol_name(symbol_kind_type yysymbol) {
+ return yytname_[yysymbol];
+}
+#endif // #if YYDEBUG || 0
+
-const signed char PipelineParserGen::yypact_ninf_ = -11;
+const signed char PipelineParserGen::yypact_ninf_ = -37;
const signed char PipelineParserGen::yytable_ninf_ = -1;
const signed char PipelineParserGen::yypact_[] = {
- 2, 5, 9, -7, 4, -11, 8, -11, -10, -10, 10, -11, -11, -11, -11, -11, 11,
- 1, 13, -11, -11, -11, -11, -11, 5, -11, 0, -11, -11, 6, 3, 16, -11};
-
-const signed char PipelineParserGen::yydefact_[] = {0, 3, 0, 0, 0, 1, 0, 5, 0, 0, 0,
- 7, 8, 9, 10, 2, 0, 0, 0, 13, 14, 15,
- 16, 17, 3, 11, 0, 6, 4, 0, 0, 0, 12};
-
-const signed char PipelineParserGen::yypgoto_[] = {
- -11, -3, -11, -11, -11, 14, -11, -11, -11, -11, -11};
-
-const signed char PipelineParserGen::yydefgoto_[] = {-1, 4, 10, 11, 12, 22, 13, 14, 2, 17, 18};
-
-const signed char PipelineParserGen::yytable_[] = {6, 7, 8, 9, 19, 20, 21, 1, 3, 5, 15, 16,
- 26, 29, 24, 25, 27, 0, 30, 31, 32, 28, 0, 23};
-
-const signed char PipelineParserGen::yycheck_[] = {7, 8, 9, 10, 14, 15, 16, 5, 3, 0, 6, 3,
- 11, 13, 4, 4, 3, -1, 12, 16, 4, 24, -1, 9};
-
-const signed char PipelineParserGen::yystos_[] = {0, 5, 26, 3, 19, 0, 7, 8, 9, 10, 20,
- 21, 22, 24, 25, 6, 3, 27, 28, 14, 15, 16,
- 23, 23, 4, 4, 11, 3, 19, 13, 12, 16, 4};
+ 9, 13, 19, 55, 23, -37, 28, -37, -5, -5, 33, 38, -37, -37, -37, -37, -37,
+ -37, 40, 46, 44, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, 13,
+ -37, 42, -37, 39, -37, -37, 54, -37, -1, -37, -37, -37, -37, -37, -37, -37, -37,
+ -1, -37, 3, -4, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
+ -37, -37, -37, -37, 63, 71, 72, -37, 25, 25, -37, -37, -37, 25, -37, -37, -37,
+ -37, -37, -37, 25, 25, 73, 25, 74, 77, -37, 78, -37, -37};
+
+const signed char PipelineParserGen::yydefact_[] = {
+ 0, 3, 0, 0, 0, 1, 0, 5, 0, 0, 0, 0, 7, 8, 9, 10, 11, 2, 0, 0,
+ 0, 46, 48, 50, 45, 47, 49, 17, 14, 15, 16, 18, 20, 3, 12, 0, 6, 0, 4, 44,
+ 0, 19, 0, 37, 38, 39, 40, 41, 42, 43, 36, 0, 21, 0, 0, 26, 28, 30, 32, 33,
+ 34, 25, 27, 29, 31, 22, 35, 62, 63, 64, 24, 23, 0, 0, 0, 13, 0, 0, 51, 52,
+ 61, 0, 53, 54, 55, 56, 57, 60, 0, 58, 0, 58, 0, 0, 59, 0, 66, 65};
+
+const signed char PipelineParserGen::yypgoto_[] = {-37, 45, -37, -37, -37, 75, -37, -37, -37, -37,
+ 34, -36, -13, -37, -37, -37, -3, 32, 47, -8,
+ -37, -37, -37, -37, -2, -37, -37, -37};
+
+const signed char PipelineParserGen::yydefgoto_[] = {-1, 4, 11, 12, 13, 27, 14, 15, 16, 37,
+ 65, 80, 91, 67, 68, 69, 82, 83, 84, 85,
+ 86, 87, 51, 52, 92, 2, 19, 20};
+
+const signed char PipelineParserGen::yytable_[] = {
+ 30, 30, 54, 21, 22, 23, 66, 55, 56, 57, 58, 59, 60, 23, 1, 66, 3, 73, 74, 5, 24, 25, 26,
+ 39, 61, 62, 63, 64, 54, 17, 26, 18, 40, 21, 22, 23, 32, 78, 79, 70, 28, 28, 33, 41, 34, 72,
+ 42, 36, 70, 39, 24, 25, 26, 43, 44, 29, 29, 45, 46, 47, 48, 49, 50, 81, 88, 35, 39, 75, 89,
+ 6, 7, 8, 9, 10, 53, 90, 76, 77, 38, 93, 95, 96, 97, 0, 31, 71, 0, 0, 0, 94};
+
+const signed char PipelineParserGen::yycheck_[] = {
+ 8, 9, 3, 8, 9, 10, 42, 8, 9, 10, 11, 12, 13, 10, 5, 51, 3, 21, 22, 0, 25, 26, 27,
+ 24, 25, 26, 27, 28, 3, 6, 27, 3, 35, 8, 9, 10, 3, 12, 13, 42, 8, 9, 4, 4, 4, 53,
+ 7, 3, 51, 24, 25, 26, 27, 14, 15, 8, 9, 18, 19, 20, 21, 22, 23, 76, 77, 19, 24, 4, 81,
+ 14, 15, 16, 17, 18, 20, 88, 5, 5, 33, 6, 6, 4, 4, -1, 9, 51, -1, -1, -1, 91};
+
+const signed char PipelineParserGen::yystos_[] = {
+ 0, 5, 54, 3, 30, 0, 14, 15, 16, 17, 18, 31, 32, 33, 35, 36, 37, 6, 3, 55,
+ 56, 8, 9, 10, 25, 26, 27, 34, 46, 47, 48, 34, 3, 4, 4, 19, 3, 38, 30, 24,
+ 45, 4, 7, 14, 15, 18, 19, 20, 21, 22, 23, 51, 52, 20, 3, 8, 9, 10, 11, 12,
+ 13, 25, 26, 27, 28, 39, 40, 42, 43, 44, 45, 39, 48, 21, 22, 4, 5, 5, 12, 13,
+ 40, 41, 45, 46, 47, 48, 49, 50, 41, 41, 41, 41, 53, 6, 53, 6, 4, 4};
const signed char PipelineParserGen::yyr1_[] = {
- 0, 18, 26, 19, 19, 28, 27, 20, 20, 20, 20, 21, 22, 23, 23, 23, 24, 25};
+ 0, 29, 54, 30, 30, 56, 55, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 35, 36, 37, 38, 38, 52,
+ 52, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 51, 51, 51, 51, 51, 51, 51, 51, 45, 46,
+ 46, 47, 47, 48, 48, 49, 49, 50, 50, 50, 50, 50, 53, 53, 41, 41, 40, 42, 42, 43, 44};
-const signed char PipelineParserGen::yyr2_[] = {
- 0, 2, 3, 0, 4, 0, 2, 1, 1, 1, 1, 3, 7, 1, 1, 1, 2, 2};
+const signed char PipelineParserGen::yyr2_[] = {0, 2, 3, 0, 4, 0, 2, 1, 1, 1, 1, 1, 3, 7, 1, 1, 1,
+ 2, 2, 4, 0, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 8, 7};
#if YYDEBUG
@@ -974,17 +1550,28 @@ const char* const PipelineParserGen::yytname_[] = {"\"EOF\"",
"END_OBJECT",
"START_ARRAY",
"END_ARRAY",
+ "ID",
+ "INT_ZERO",
+ "LONG_ZERO",
+ "DOUBLE_ZERO",
+ "DECIMAL_ZERO",
+ "TRUE",
+ "FALSE",
"STAGE_INHIBIT_OPTIMIZATION",
"STAGE_UNION_WITH",
"STAGE_SKIP",
"STAGE_LIMIT",
+ "STAGE_PROJECT",
"COLL_ARG",
"PIPELINE_ARG",
+ "ADD",
+ "ATAN2",
+ "FIELDNAME",
"STRING",
- "NUMBER_INT",
- "NUMBER_LONG",
- "NUMBER_DOUBLE",
- "BOOL",
+ "INT_NON_ZERO",
+ "LONG_NON_ZERO",
+ "DOUBLE_NON_ZERO",
+ "DECIMAL_NON_ZERO",
"$accept",
"stageList",
"stage",
@@ -993,6 +1580,23 @@ const char* const PipelineParserGen::yytname_[] = {"\"EOF\"",
"num",
"skip",
"limit",
+ "project",
+ "projectFields",
+ "projection",
+ "compoundExpression",
+ "expression",
+ "maths",
+ "add",
+ "atan2",
+ "string",
+ "int",
+ "long",
+ "double",
+ "bool",
+ "value",
+ "projectionFieldname",
+ "projectField",
+ "expressions",
"pipeline",
"START_ORDERED_OBJECT",
"$@1",
@@ -1001,8 +1605,11 @@ const char* const PipelineParserGen::yytname_[] = {"\"EOF\"",
#if YYDEBUG
-const unsigned char PipelineParserGen::yyrline_[] = {
- 0, 138, 138, 143, 144, 152, 152, 155, 155, 155, 155, 159, 165, 176, 179, 182, 188, 193};
+const short PipelineParserGen::yyrline_[] = {
+ 0, 167, 167, 173, 174, 182, 182, 185, 185, 185, 185, 185, 189, 195, 205, 205, 205,
+ 209, 214, 219, 225, 228, 235, 238, 244, 245, 248, 251, 254, 257, 260, 263, 266, 269,
+ 272, 275, 279, 284, 287, 290, 293, 296, 299, 302, 308, 314, 317, 323, 326, 332, 335,
+ 341, 344, 350, 351, 352, 353, 354, 361, 362, 368, 369, 373, 377, 378, 381, 391};
void PipelineParserGen::yy_stack_print_() const {
*yycdebug_ << "Stack now";
@@ -1023,8 +1630,8 @@ void PipelineParserGen::yy_reduce_print_(int yyrule) const {
#endif // YYDEBUG
-#line 52 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 58 "pipeline_grammar.yy"
} // namespace mongo
-#line 1131 "src/mongo/db/cst/pipeline_parser_gen.cpp"
+#line 1683 "pipeline_parser_gen.cpp"
-#line 197 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 397 "pipeline_grammar.yy"
diff --git a/src/mongo/db/cst/pipeline_parser_gen.hpp b/src/mongo/db/cst/pipeline_parser_gen.hpp
index c742e600bca..695413404b6 100644
--- a/src/mongo/db/cst/pipeline_parser_gen.hpp
+++ b/src/mongo/db/cst/pipeline_parser_gen.hpp
@@ -1,4 +1,4 @@
-// A Bison parser, made by GNU Bison 3.6.
+// A Bison parser, made by GNU Bison 3.6.3.
// Skeleton interface for Bison LALR(1) parsers in C++
@@ -32,7 +32,7 @@
/**
- ** \file src/mongo/db/cst/pipeline_parser_gen.hpp
+ ** \file pipeline_parser_gen.hpp
** Define the mongo::parser class.
*/
@@ -42,10 +42,10 @@
// especially those whose name start with YY_ or yy_. They are
// private implementation details that can be changed or removed.
-#ifndef YY_YY_SRC_MONGO_DB_CST_PIPELINE_PARSER_GEN_HPP_INCLUDED
-#define YY_YY_SRC_MONGO_DB_CST_PIPELINE_PARSER_GEN_HPP_INCLUDED
+#ifndef YY_YY_PIPELINE_PARSER_GEN_HPP_INCLUDED
+#define YY_YY_PIPELINE_PARSER_GEN_HPP_INCLUDED
// "%code requires" blocks.
-#line 60 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 66 "pipeline_grammar.yy"
#include "mongo/db/cst/c_node.h"
#include "mongo/db/cst/key_fieldname.h"
@@ -61,7 +61,7 @@ class BSONLexer;
#pragma warning(disable : 4065)
#endif
-#line 65 "src/mongo/db/cst/pipeline_parser_gen.hpp"
+#line 65 "pipeline_parser_gen.hpp"
#include <cassert>
#include <cstdlib> // std::abort
@@ -190,9 +190,9 @@ class BSONLexer;
#define YYDEBUG 0
#endif
-#line 52 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 58 "pipeline_grammar.yy"
namespace mongo {
-#line 200 "src/mongo/db/cst/pipeline_parser_gen.hpp"
+#line 200 "pipeline_parser_gen.hpp"
/// A Bison parser.
@@ -374,22 +374,46 @@ public:
// num
// skip
// limit
+ // project
+ // projectFields
+ // projection
+ // compoundExpression
+ // expression
+ // maths
+ // add
+ // atan2
+ // string
+ // int
+ // long
+ // double
+ // bool
+ // value
char dummy1[sizeof(CNode)];
- // BOOL
- char dummy2[sizeof(bool)];
+ // projectionFieldname
+ char dummy2[sizeof(CNode::Fieldname)];
- // NUMBER_DOUBLE
- char dummy3[sizeof(double)];
+ // DECIMAL_NON_ZERO
+ char dummy3[sizeof(Decimal128)];
- // NUMBER_INT
- char dummy4[sizeof(int)];
+ // DOUBLE_NON_ZERO
+ char dummy4[sizeof(double)];
- // NUMBER_LONG
- char dummy5[sizeof(long long)];
+ // INT_NON_ZERO
+ char dummy5[sizeof(int)];
+ // LONG_NON_ZERO
+ char dummy6[sizeof(long long)];
+
+ // projectField
+ char dummy7[sizeof(std::pair<CNode::Fieldname, CNode>)];
+
+ // FIELDNAME
// STRING
- char dummy6[sizeof(std::string)];
+ char dummy8[sizeof(std::string)];
+
+ // expressions
+ char dummy9[sizeof(std::vector<CNode>)];
};
/// The size of the largest semantic type.
@@ -429,24 +453,35 @@ public:
struct token {
enum token_kind_type {
YYEMPTY = -2,
- END_OF_FILE = 0, // "EOF"
- YYerror = 1, // error
- YYUNDEF = 2, // "invalid token"
- START_OBJECT = 3, // START_OBJECT
- END_OBJECT = 4, // END_OBJECT
- START_ARRAY = 5, // START_ARRAY
- END_ARRAY = 6, // END_ARRAY
- STAGE_INHIBIT_OPTIMIZATION = 7, // STAGE_INHIBIT_OPTIMIZATION
- STAGE_UNION_WITH = 8, // STAGE_UNION_WITH
- STAGE_SKIP = 9, // STAGE_SKIP
- STAGE_LIMIT = 10, // STAGE_LIMIT
- COLL_ARG = 11, // COLL_ARG
- PIPELINE_ARG = 12, // PIPELINE_ARG
- STRING = 13, // STRING
- NUMBER_INT = 14, // NUMBER_INT
- NUMBER_LONG = 15, // NUMBER_LONG
- NUMBER_DOUBLE = 16, // NUMBER_DOUBLE
- BOOL = 17 // BOOL
+ END_OF_FILE = 0, // "EOF"
+ YYerror = 1, // error
+ YYUNDEF = 2, // "invalid token"
+ START_OBJECT = 3, // START_OBJECT
+ END_OBJECT = 4, // END_OBJECT
+ START_ARRAY = 5, // START_ARRAY
+ END_ARRAY = 6, // END_ARRAY
+ ID = 7, // ID
+ INT_ZERO = 8, // INT_ZERO
+ LONG_ZERO = 9, // LONG_ZERO
+ DOUBLE_ZERO = 10, // DOUBLE_ZERO
+ DECIMAL_ZERO = 11, // DECIMAL_ZERO
+ TRUE = 12, // TRUE
+ FALSE = 13, // FALSE
+ STAGE_INHIBIT_OPTIMIZATION = 14, // STAGE_INHIBIT_OPTIMIZATION
+ STAGE_UNION_WITH = 15, // STAGE_UNION_WITH
+ STAGE_SKIP = 16, // STAGE_SKIP
+ STAGE_LIMIT = 17, // STAGE_LIMIT
+ STAGE_PROJECT = 18, // STAGE_PROJECT
+ COLL_ARG = 19, // COLL_ARG
+ PIPELINE_ARG = 20, // PIPELINE_ARG
+ ADD = 21, // ADD
+ ATAN2 = 22, // ATAN2
+ FIELDNAME = 23, // FIELDNAME
+ STRING = 24, // STRING
+ INT_NON_ZERO = 25, // INT_NON_ZERO
+ LONG_NON_ZERO = 26, // LONG_NON_ZERO
+ DOUBLE_NON_ZERO = 27, // DOUBLE_NON_ZERO
+ DECIMAL_NON_ZERO = 28 // DECIMAL_NON_ZERO
};
/// Backward compatibility alias (Bison 3.6).
typedef token_kind_type yytokentype;
@@ -461,37 +496,65 @@ public:
/// Symbol kinds.
struct symbol_kind {
enum symbol_kind_type {
- YYNTOKENS = 18, ///< Number of tokens.
+ YYNTOKENS = 29, ///< Number of tokens.
S_YYEMPTY = -2,
- S_YYEOF = 0, // "EOF"
- S_YYerror = 1, // error
- S_YYUNDEF = 2, // "invalid token"
- S_START_OBJECT = 3, // START_OBJECT
- S_END_OBJECT = 4, // END_OBJECT
- S_START_ARRAY = 5, // START_ARRAY
- S_END_ARRAY = 6, // END_ARRAY
- S_STAGE_INHIBIT_OPTIMIZATION = 7, // STAGE_INHIBIT_OPTIMIZATION
- S_STAGE_UNION_WITH = 8, // STAGE_UNION_WITH
- S_STAGE_SKIP = 9, // STAGE_SKIP
- S_STAGE_LIMIT = 10, // STAGE_LIMIT
- S_COLL_ARG = 11, // COLL_ARG
- S_PIPELINE_ARG = 12, // PIPELINE_ARG
- S_STRING = 13, // STRING
- S_NUMBER_INT = 14, // NUMBER_INT
- S_NUMBER_LONG = 15, // NUMBER_LONG
- S_NUMBER_DOUBLE = 16, // NUMBER_DOUBLE
- S_BOOL = 17, // BOOL
- S_YYACCEPT = 18, // $accept
- S_stageList = 19, // stageList
- S_stage = 20, // stage
- S_inhibitOptimization = 21, // inhibitOptimization
- S_unionWith = 22, // unionWith
- S_num = 23, // num
- S_skip = 24, // skip
- S_limit = 25, // limit
- S_pipeline = 26, // pipeline
- S_START_ORDERED_OBJECT = 27, // START_ORDERED_OBJECT
- S_28_1 = 28 // $@1
+ S_YYEOF = 0, // "EOF"
+ S_YYerror = 1, // error
+ S_YYUNDEF = 2, // "invalid token"
+ S_START_OBJECT = 3, // START_OBJECT
+ S_END_OBJECT = 4, // END_OBJECT
+ S_START_ARRAY = 5, // START_ARRAY
+ S_END_ARRAY = 6, // END_ARRAY
+ S_ID = 7, // ID
+ S_INT_ZERO = 8, // INT_ZERO
+ S_LONG_ZERO = 9, // LONG_ZERO
+ S_DOUBLE_ZERO = 10, // DOUBLE_ZERO
+ S_DECIMAL_ZERO = 11, // DECIMAL_ZERO
+ S_TRUE = 12, // TRUE
+ S_FALSE = 13, // FALSE
+ S_STAGE_INHIBIT_OPTIMIZATION = 14, // STAGE_INHIBIT_OPTIMIZATION
+ S_STAGE_UNION_WITH = 15, // STAGE_UNION_WITH
+ S_STAGE_SKIP = 16, // STAGE_SKIP
+ S_STAGE_LIMIT = 17, // STAGE_LIMIT
+ S_STAGE_PROJECT = 18, // STAGE_PROJECT
+ S_COLL_ARG = 19, // COLL_ARG
+ S_PIPELINE_ARG = 20, // PIPELINE_ARG
+ S_ADD = 21, // ADD
+ S_ATAN2 = 22, // ATAN2
+ S_FIELDNAME = 23, // FIELDNAME
+ S_STRING = 24, // STRING
+ S_INT_NON_ZERO = 25, // INT_NON_ZERO
+ S_LONG_NON_ZERO = 26, // LONG_NON_ZERO
+ S_DOUBLE_NON_ZERO = 27, // DOUBLE_NON_ZERO
+ S_DECIMAL_NON_ZERO = 28, // DECIMAL_NON_ZERO
+ S_YYACCEPT = 29, // $accept
+ S_stageList = 30, // stageList
+ S_stage = 31, // stage
+ S_inhibitOptimization = 32, // inhibitOptimization
+ S_unionWith = 33, // unionWith
+ S_num = 34, // num
+ S_skip = 35, // skip
+ S_limit = 36, // limit
+ S_project = 37, // project
+ S_projectFields = 38, // projectFields
+ S_projection = 39, // projection
+ S_compoundExpression = 40, // compoundExpression
+ S_expression = 41, // expression
+ S_maths = 42, // maths
+ S_add = 43, // add
+ S_atan2 = 44, // atan2
+ S_string = 45, // string
+ S_int = 46, // int
+ S_long = 47, // long
+ S_double = 48, // double
+ S_bool = 49, // bool
+ S_value = 50, // value
+ S_projectionFieldname = 51, // projectionFieldname
+ S_projectField = 52, // projectField
+ S_expressions = 53, // expressions
+ S_pipeline = 54, // pipeline
+ S_START_ORDERED_OBJECT = 55, // START_ORDERED_OBJECT
+ S_56_1 = 56 // $@1
};
};
@@ -520,36 +583,63 @@ public:
basic_symbol(basic_symbol&& that)
: Base(std::move(that)), value(), location(std::move(that.location)) {
switch (this->kind()) {
- case 19: // stageList
- case 20: // stage
- case 21: // inhibitOptimization
- case 22: // unionWith
- case 23: // num
- case 24: // skip
- case 25: // limit
+ case 30: // stageList
+ case 31: // stage
+ case 32: // inhibitOptimization
+ case 33: // unionWith
+ case 34: // num
+ case 35: // skip
+ case 36: // limit
+ case 37: // project
+ case 38: // projectFields
+ case 39: // projection
+ case 40: // compoundExpression
+ case 41: // expression
+ case 42: // maths
+ case 43: // add
+ case 44: // atan2
+ case 45: // string
+ case 46: // int
+ case 47: // long
+ case 48: // double
+ case 49: // bool
+ case 50: // value
value.move<CNode>(std::move(that.value));
break;
- case 17: // BOOL
- value.move<bool>(std::move(that.value));
+ case 51: // projectionFieldname
+ value.move<CNode::Fieldname>(std::move(that.value));
+ break;
+
+ case 28: // DECIMAL_NON_ZERO
+ value.move<Decimal128>(std::move(that.value));
break;
- case 16: // NUMBER_DOUBLE
+ case 27: // DOUBLE_NON_ZERO
value.move<double>(std::move(that.value));
break;
- case 14: // NUMBER_INT
+ case 25: // INT_NON_ZERO
value.move<int>(std::move(that.value));
break;
- case 15: // NUMBER_LONG
+ case 26: // LONG_NON_ZERO
value.move<long long>(std::move(that.value));
break;
- case 13: // STRING
+ case 52: // projectField
+ value.move<std::pair<CNode::Fieldname, CNode>>(std::move(that.value));
+ break;
+
+ case 23: // FIELDNAME
+ case 24: // STRING
value.move<std::string>(std::move(that.value));
break;
+ case 53: // expressions
+ value.move<std::vector<CNode>>(std::move(that.value));
+ break;
+
default:
break;
}
@@ -574,10 +664,17 @@ public:
: Base(t), value(v), location(l) {}
#endif
#if 201103L <= YY_CPLUSPLUS
- basic_symbol(typename Base::kind_type t, bool&& v, location_type&& l)
+ basic_symbol(typename Base::kind_type t, CNode::Fieldname&& v, location_type&& l)
: Base(t), value(std::move(v)), location(std::move(l)) {}
#else
- basic_symbol(typename Base::kind_type t, const bool& v, const location_type& l)
+ basic_symbol(typename Base::kind_type t, const CNode::Fieldname& v, const location_type& l)
+ : Base(t), value(v), location(l) {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol(typename Base::kind_type t, Decimal128&& v, location_type&& l)
+ : Base(t), value(std::move(v)), location(std::move(l)) {}
+#else
+ basic_symbol(typename Base::kind_type t, const Decimal128& v, const location_type& l)
: Base(t), value(v), location(l) {}
#endif
#if 201103L <= YY_CPLUSPLUS
@@ -602,12 +699,32 @@ public:
: Base(t), value(v), location(l) {}
#endif
#if 201103L <= YY_CPLUSPLUS
+ basic_symbol(typename Base::kind_type t,
+ std::pair<CNode::Fieldname, CNode>&& v,
+ location_type&& l)
+ : Base(t), value(std::move(v)), location(std::move(l)) {}
+#else
+ basic_symbol(typename Base::kind_type t,
+ const std::pair<CNode::Fieldname, CNode>& v,
+ const location_type& l)
+ : Base(t), value(v), location(l) {}
+#endif
+#if 201103L <= YY_CPLUSPLUS
basic_symbol(typename Base::kind_type t, std::string&& v, location_type&& l)
: Base(t), value(std::move(v)), location(std::move(l)) {}
#else
basic_symbol(typename Base::kind_type t, const std::string& v, const location_type& l)
: Base(t), value(v), location(l) {}
#endif
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol(typename Base::kind_type t, std::vector<CNode>&& v, location_type&& l)
+ : Base(t), value(std::move(v)), location(std::move(l)) {}
+#else
+ basic_symbol(typename Base::kind_type t,
+ const std::vector<CNode>& v,
+ const location_type& l)
+ : Base(t), value(v), location(l) {}
+#endif
/// Destroy the symbol.
~basic_symbol() {
@@ -627,36 +744,63 @@ public:
// Value type destructor.
switch (yykind) {
- case 19: // stageList
- case 20: // stage
- case 21: // inhibitOptimization
- case 22: // unionWith
- case 23: // num
- case 24: // skip
- case 25: // limit
+ case 30: // stageList
+ case 31: // stage
+ case 32: // inhibitOptimization
+ case 33: // unionWith
+ case 34: // num
+ case 35: // skip
+ case 36: // limit
+ case 37: // project
+ case 38: // projectFields
+ case 39: // projection
+ case 40: // compoundExpression
+ case 41: // expression
+ case 42: // maths
+ case 43: // add
+ case 44: // atan2
+ case 45: // string
+ case 46: // int
+ case 47: // long
+ case 48: // double
+ case 49: // bool
+ case 50: // value
value.template destroy<CNode>();
break;
- case 17: // BOOL
- value.template destroy<bool>();
+ case 51: // projectionFieldname
+ value.template destroy<CNode::Fieldname>();
+ break;
+
+ case 28: // DECIMAL_NON_ZERO
+ value.template destroy<Decimal128>();
break;
- case 16: // NUMBER_DOUBLE
+ case 27: // DOUBLE_NON_ZERO
value.template destroy<double>();
break;
- case 14: // NUMBER_INT
+ case 25: // INT_NON_ZERO
value.template destroy<int>();
break;
- case 15: // NUMBER_LONG
+ case 26: // LONG_NON_ZERO
value.template destroy<long long>();
break;
- case 13: // STRING
+ case 52: // projectField
+ value.template destroy<std::pair<CNode::Fieldname, CNode>>();
+ break;
+
+ case 23: // FIELDNAME
+ case 24: // STRING
value.template destroy<std::string>();
break;
+ case 53: // expressions
+ value.template destroy<std::vector<CNode>>();
+ break;
+
default:
break;
}
@@ -664,6 +808,14 @@ public:
Base::clear();
}
+#if YYDEBUG || 0
+ /// The user-facing name of this symbol.
+ const char* name() const YY_NOEXCEPT {
+ return PipelineParserGen::symbol_name(this->kind());
+ }
+#endif // #if YYDEBUG || 0
+
+
/// Backward compatibility (Bison 3.6).
symbol_kind_type type_get() const YY_NOEXCEPT;
@@ -739,74 +891,82 @@ public:
symbol_type(int tok, location_type l) : super_type(token_type(tok), std::move(l)) {
YY_ASSERT(tok == token::END_OF_FILE || tok == token::YYerror || tok == token::YYUNDEF ||
tok == token::START_OBJECT || tok == token::END_OBJECT ||
- tok == token::START_ARRAY || tok == token::END_ARRAY ||
+ tok == token::START_ARRAY || tok == token::END_ARRAY || tok == token::ID ||
+ tok == token::INT_ZERO || tok == token::LONG_ZERO ||
+ tok == token::DOUBLE_ZERO || tok == token::DECIMAL_ZERO ||
+ tok == token::TRUE || tok == token::FALSE ||
tok == token::STAGE_INHIBIT_OPTIMIZATION || tok == token::STAGE_UNION_WITH ||
tok == token::STAGE_SKIP || tok == token::STAGE_LIMIT ||
- tok == token::COLL_ARG || tok == token::PIPELINE_ARG);
+ tok == token::STAGE_PROJECT || tok == token::COLL_ARG ||
+ tok == token::PIPELINE_ARG || tok == token::ADD || tok == token::ATAN2);
}
#else
symbol_type(int tok, const location_type& l) : super_type(token_type(tok), l) {
YY_ASSERT(tok == token::END_OF_FILE || tok == token::YYerror || tok == token::YYUNDEF ||
tok == token::START_OBJECT || tok == token::END_OBJECT ||
- tok == token::START_ARRAY || tok == token::END_ARRAY ||
+ tok == token::START_ARRAY || tok == token::END_ARRAY || tok == token::ID ||
+ tok == token::INT_ZERO || tok == token::LONG_ZERO ||
+ tok == token::DOUBLE_ZERO || tok == token::DECIMAL_ZERO ||
+ tok == token::TRUE || tok == token::FALSE ||
tok == token::STAGE_INHIBIT_OPTIMIZATION || tok == token::STAGE_UNION_WITH ||
tok == token::STAGE_SKIP || tok == token::STAGE_LIMIT ||
- tok == token::COLL_ARG || tok == token::PIPELINE_ARG);
+ tok == token::STAGE_PROJECT || tok == token::COLL_ARG ||
+ tok == token::PIPELINE_ARG || tok == token::ADD || tok == token::ATAN2);
}
#endif
#if 201103L <= YY_CPLUSPLUS
- symbol_type(int tok, bool v, location_type l)
+ symbol_type(int tok, Decimal128 v, location_type l)
: super_type(token_type(tok), std::move(v), std::move(l)) {
- YY_ASSERT(tok == token::BOOL);
+ YY_ASSERT(tok == token::DECIMAL_NON_ZERO);
}
#else
- symbol_type(int tok, const bool& v, const location_type& l)
+ symbol_type(int tok, const Decimal128& v, const location_type& l)
: super_type(token_type(tok), v, l) {
- YY_ASSERT(tok == token::BOOL);
+ YY_ASSERT(tok == token::DECIMAL_NON_ZERO);
}
#endif
#if 201103L <= YY_CPLUSPLUS
symbol_type(int tok, double v, location_type l)
: super_type(token_type(tok), std::move(v), std::move(l)) {
- YY_ASSERT(tok == token::NUMBER_DOUBLE);
+ YY_ASSERT(tok == token::DOUBLE_NON_ZERO);
}
#else
symbol_type(int tok, const double& v, const location_type& l)
: super_type(token_type(tok), v, l) {
- YY_ASSERT(tok == token::NUMBER_DOUBLE);
+ YY_ASSERT(tok == token::DOUBLE_NON_ZERO);
}
#endif
#if 201103L <= YY_CPLUSPLUS
symbol_type(int tok, int v, location_type l)
: super_type(token_type(tok), std::move(v), std::move(l)) {
- YY_ASSERT(tok == token::NUMBER_INT);
+ YY_ASSERT(tok == token::INT_NON_ZERO);
}
#else
symbol_type(int tok, const int& v, const location_type& l)
: super_type(token_type(tok), v, l) {
- YY_ASSERT(tok == token::NUMBER_INT);
+ YY_ASSERT(tok == token::INT_NON_ZERO);
}
#endif
#if 201103L <= YY_CPLUSPLUS
symbol_type(int tok, long long v, location_type l)
: super_type(token_type(tok), std::move(v), std::move(l)) {
- YY_ASSERT(tok == token::NUMBER_LONG);
+ YY_ASSERT(tok == token::LONG_NON_ZERO);
}
#else
symbol_type(int tok, const long long& v, const location_type& l)
: super_type(token_type(tok), v, l) {
- YY_ASSERT(tok == token::NUMBER_LONG);
+ YY_ASSERT(tok == token::LONG_NON_ZERO);
}
#endif
#if 201103L <= YY_CPLUSPLUS
symbol_type(int tok, std::string v, location_type l)
: super_type(token_type(tok), std::move(v), std::move(l)) {
- YY_ASSERT(tok == token::STRING);
+ YY_ASSERT(tok == token::FIELDNAME || tok == token::STRING);
}
#else
symbol_type(int tok, const std::string& v, const location_type& l)
: super_type(token_type(tok), v, l) {
- YY_ASSERT(tok == token::STRING);
+ YY_ASSERT(tok == token::FIELDNAME || tok == token::STRING);
}
#endif
};
@@ -852,6 +1012,13 @@ public:
/// Report a syntax error.
void error(const syntax_error& err);
+#if YYDEBUG || 0
+ /// The user-facing name of the symbol whose (internal) number is
+ /// YYSYMBOL. No bounds checking.
+ static const char* symbol_name(symbol_kind_type yysymbol);
+#endif // #if YYDEBUG || 0
+
+
// Implementation of make_symbol for each symbol type.
#if 201103L <= YY_CPLUSPLUS
static symbol_type make_END_OF_FILE(location_type l) {
@@ -917,6 +1084,69 @@ public:
}
#endif
#if 201103L <= YY_CPLUSPLUS
+ static symbol_type make_ID(location_type l) {
+ return symbol_type(token::ID, std::move(l));
+ }
+#else
+ static symbol_type make_ID(const location_type& l) {
+ return symbol_type(token::ID, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static symbol_type make_INT_ZERO(location_type l) {
+ return symbol_type(token::INT_ZERO, std::move(l));
+ }
+#else
+ static symbol_type make_INT_ZERO(const location_type& l) {
+ return symbol_type(token::INT_ZERO, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static symbol_type make_LONG_ZERO(location_type l) {
+ return symbol_type(token::LONG_ZERO, std::move(l));
+ }
+#else
+ static symbol_type make_LONG_ZERO(const location_type& l) {
+ return symbol_type(token::LONG_ZERO, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static symbol_type make_DOUBLE_ZERO(location_type l) {
+ return symbol_type(token::DOUBLE_ZERO, std::move(l));
+ }
+#else
+ static symbol_type make_DOUBLE_ZERO(const location_type& l) {
+ return symbol_type(token::DOUBLE_ZERO, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static symbol_type make_DECIMAL_ZERO(location_type l) {
+ return symbol_type(token::DECIMAL_ZERO, std::move(l));
+ }
+#else
+ static symbol_type make_DECIMAL_ZERO(const location_type& l) {
+ return symbol_type(token::DECIMAL_ZERO, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static symbol_type make_TRUE(location_type l) {
+ return symbol_type(token::TRUE, std::move(l));
+ }
+#else
+ static symbol_type make_TRUE(const location_type& l) {
+ return symbol_type(token::TRUE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static symbol_type make_FALSE(location_type l) {
+ return symbol_type(token::FALSE, std::move(l));
+ }
+#else
+ static symbol_type make_FALSE(const location_type& l) {
+ return symbol_type(token::FALSE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
static symbol_type make_STAGE_INHIBIT_OPTIMIZATION(location_type l) {
return symbol_type(token::STAGE_INHIBIT_OPTIMIZATION, std::move(l));
}
@@ -953,6 +1183,15 @@ public:
}
#endif
#if 201103L <= YY_CPLUSPLUS
+ static symbol_type make_STAGE_PROJECT(location_type l) {
+ return symbol_type(token::STAGE_PROJECT, std::move(l));
+ }
+#else
+ static symbol_type make_STAGE_PROJECT(const location_type& l) {
+ return symbol_type(token::STAGE_PROJECT, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
static symbol_type make_COLL_ARG(location_type l) {
return symbol_type(token::COLL_ARG, std::move(l));
}
@@ -971,6 +1210,33 @@ public:
}
#endif
#if 201103L <= YY_CPLUSPLUS
+ static symbol_type make_ADD(location_type l) {
+ return symbol_type(token::ADD, std::move(l));
+ }
+#else
+ static symbol_type make_ADD(const location_type& l) {
+ return symbol_type(token::ADD, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static symbol_type make_ATAN2(location_type l) {
+ return symbol_type(token::ATAN2, std::move(l));
+ }
+#else
+ static symbol_type make_ATAN2(const location_type& l) {
+ return symbol_type(token::ATAN2, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static symbol_type make_FIELDNAME(std::string v, location_type l) {
+ return symbol_type(token::FIELDNAME, std::move(v), std::move(l));
+ }
+#else
+ static symbol_type make_FIELDNAME(const std::string& v, const location_type& l) {
+ return symbol_type(token::FIELDNAME, v, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
static symbol_type make_STRING(std::string v, location_type l) {
return symbol_type(token::STRING, std::move(v), std::move(l));
}
@@ -980,39 +1246,39 @@ public:
}
#endif
#if 201103L <= YY_CPLUSPLUS
- static symbol_type make_NUMBER_INT(int v, location_type l) {
- return symbol_type(token::NUMBER_INT, std::move(v), std::move(l));
+ static symbol_type make_INT_NON_ZERO(int v, location_type l) {
+ return symbol_type(token::INT_NON_ZERO, std::move(v), std::move(l));
}
#else
- static symbol_type make_NUMBER_INT(const int& v, const location_type& l) {
- return symbol_type(token::NUMBER_INT, v, l);
+ static symbol_type make_INT_NON_ZERO(const int& v, const location_type& l) {
+ return symbol_type(token::INT_NON_ZERO, v, l);
}
#endif
#if 201103L <= YY_CPLUSPLUS
- static symbol_type make_NUMBER_LONG(long long v, location_type l) {
- return symbol_type(token::NUMBER_LONG, std::move(v), std::move(l));
+ static symbol_type make_LONG_NON_ZERO(long long v, location_type l) {
+ return symbol_type(token::LONG_NON_ZERO, std::move(v), std::move(l));
}
#else
- static symbol_type make_NUMBER_LONG(const long long& v, const location_type& l) {
- return symbol_type(token::NUMBER_LONG, v, l);
+ static symbol_type make_LONG_NON_ZERO(const long long& v, const location_type& l) {
+ return symbol_type(token::LONG_NON_ZERO, v, l);
}
#endif
#if 201103L <= YY_CPLUSPLUS
- static symbol_type make_NUMBER_DOUBLE(double v, location_type l) {
- return symbol_type(token::NUMBER_DOUBLE, std::move(v), std::move(l));
+ static symbol_type make_DOUBLE_NON_ZERO(double v, location_type l) {
+ return symbol_type(token::DOUBLE_NON_ZERO, std::move(v), std::move(l));
}
#else
- static symbol_type make_NUMBER_DOUBLE(const double& v, const location_type& l) {
- return symbol_type(token::NUMBER_DOUBLE, v, l);
+ static symbol_type make_DOUBLE_NON_ZERO(const double& v, const location_type& l) {
+ return symbol_type(token::DOUBLE_NON_ZERO, v, l);
}
#endif
#if 201103L <= YY_CPLUSPLUS
- static symbol_type make_BOOL(bool v, location_type l) {
- return symbol_type(token::BOOL, std::move(v), std::move(l));
+ static symbol_type make_DECIMAL_NON_ZERO(Decimal128 v, location_type l) {
+ return symbol_type(token::DECIMAL_NON_ZERO, std::move(v), std::move(l));
}
#else
- static symbol_type make_BOOL(const bool& v, const location_type& l) {
- return symbol_type(token::BOOL, v, l);
+ static symbol_type make_DECIMAL_NON_ZERO(const Decimal128& v, const location_type& l) {
+ return symbol_type(token::DECIMAL_NON_ZERO, v, l);
}
#endif
@@ -1051,10 +1317,6 @@ private:
static symbol_kind_type yytranslate_(int t);
#if YYDEBUG || 0
- /// The user-facing name of the symbol whose (internal) number is
- /// YYSYMBOL. No bounds checking.
- static const char* symbol_name(symbol_kind_type yysymbol);
-
/// For a symbol, its name in clear.
static const char* const yytname_[];
#endif // #if YYDEBUG || 0
@@ -1096,7 +1358,7 @@ private:
#if YYDEBUG
// YYRLINE[YYN] -- Source line where rule number YYN was defined.
- static const unsigned char yyrline_[];
+ static const short yyrline_[];
/// Report on the debug stream that the rule \a r is going to be reduced.
virtual void yy_reduce_print_(int r) const;
/// Print the state stack on the debug stream.
@@ -1295,8 +1557,8 @@ private:
/// Constants.
enum {
- yylast_ = 23, ///< Last index in yytable_.
- yynnts_ = 11, ///< Number of nonterminal symbols.
+ yylast_ = 89, ///< Last index in yytable_.
+ yynnts_ = 28, ///< Number of nonterminal symbols.
yyfinal_ = 5 ///< Termination state number.
};
@@ -1315,36 +1577,63 @@ template <typename Base>
PipelineParserGen::basic_symbol<Base>::basic_symbol(const basic_symbol& that)
: Base(that), value(), location(that.location) {
switch (this->kind()) {
- case 19: // stageList
- case 20: // stage
- case 21: // inhibitOptimization
- case 22: // unionWith
- case 23: // num
- case 24: // skip
- case 25: // limit
+ case 30: // stageList
+ case 31: // stage
+ case 32: // inhibitOptimization
+ case 33: // unionWith
+ case 34: // num
+ case 35: // skip
+ case 36: // limit
+ case 37: // project
+ case 38: // projectFields
+ case 39: // projection
+ case 40: // compoundExpression
+ case 41: // expression
+ case 42: // maths
+ case 43: // add
+ case 44: // atan2
+ case 45: // string
+ case 46: // int
+ case 47: // long
+ case 48: // double
+ case 49: // bool
+ case 50: // value
value.copy<CNode>(YY_MOVE(that.value));
break;
- case 17: // BOOL
- value.copy<bool>(YY_MOVE(that.value));
+ case 51: // projectionFieldname
+ value.copy<CNode::Fieldname>(YY_MOVE(that.value));
+ break;
+
+ case 28: // DECIMAL_NON_ZERO
+ value.copy<Decimal128>(YY_MOVE(that.value));
break;
- case 16: // NUMBER_DOUBLE
+ case 27: // DOUBLE_NON_ZERO
value.copy<double>(YY_MOVE(that.value));
break;
- case 14: // NUMBER_INT
+ case 25: // INT_NON_ZERO
value.copy<int>(YY_MOVE(that.value));
break;
- case 15: // NUMBER_LONG
+ case 26: // LONG_NON_ZERO
value.copy<long long>(YY_MOVE(that.value));
break;
- case 13: // STRING
+ case 52: // projectField
+ value.copy<std::pair<CNode::Fieldname, CNode>>(YY_MOVE(that.value));
+ break;
+
+ case 23: // FIELDNAME
+ case 24: // STRING
value.copy<std::string>(YY_MOVE(that.value));
break;
+ case 53: // expressions
+ value.copy<std::vector<CNode>>(YY_MOVE(that.value));
+ break;
+
default:
break;
}
@@ -1366,36 +1655,63 @@ template <typename Base>
void PipelineParserGen::basic_symbol<Base>::move(basic_symbol& s) {
super_type::move(s);
switch (this->kind()) {
- case 19: // stageList
- case 20: // stage
- case 21: // inhibitOptimization
- case 22: // unionWith
- case 23: // num
- case 24: // skip
- case 25: // limit
+ case 30: // stageList
+ case 31: // stage
+ case 32: // inhibitOptimization
+ case 33: // unionWith
+ case 34: // num
+ case 35: // skip
+ case 36: // limit
+ case 37: // project
+ case 38: // projectFields
+ case 39: // projection
+ case 40: // compoundExpression
+ case 41: // expression
+ case 42: // maths
+ case 43: // add
+ case 44: // atan2
+ case 45: // string
+ case 46: // int
+ case 47: // long
+ case 48: // double
+ case 49: // bool
+ case 50: // value
value.move<CNode>(YY_MOVE(s.value));
break;
- case 17: // BOOL
- value.move<bool>(YY_MOVE(s.value));
+ case 51: // projectionFieldname
+ value.move<CNode::Fieldname>(YY_MOVE(s.value));
+ break;
+
+ case 28: // DECIMAL_NON_ZERO
+ value.move<Decimal128>(YY_MOVE(s.value));
break;
- case 16: // NUMBER_DOUBLE
+ case 27: // DOUBLE_NON_ZERO
value.move<double>(YY_MOVE(s.value));
break;
- case 14: // NUMBER_INT
+ case 25: // INT_NON_ZERO
value.move<int>(YY_MOVE(s.value));
break;
- case 15: // NUMBER_LONG
+ case 26: // LONG_NON_ZERO
value.move<long long>(YY_MOVE(s.value));
break;
- case 13: // STRING
+ case 52: // projectField
+ value.move<std::pair<CNode::Fieldname, CNode>>(YY_MOVE(s.value));
+ break;
+
+ case 23: // FIELDNAME
+ case 24: // STRING
value.move<std::string>(YY_MOVE(s.value));
break;
+ case 53: // expressions
+ value.move<std::vector<CNode>>(YY_MOVE(s.value));
+ break;
+
default:
break;
}
@@ -1434,9 +1750,9 @@ inline PipelineParserGen::symbol_kind_type PipelineParserGen::by_kind::type_get(
return this->kind();
}
-#line 52 "src/mongo/db/cst/pipeline_grammar.yy"
+#line 58 "pipeline_grammar.yy"
} // namespace mongo
-#line 1728 "src/mongo/db/cst/pipeline_parser_gen.hpp"
+#line 2116 "pipeline_parser_gen.hpp"
-#endif // !YY_YY_SRC_MONGO_DB_CST_PIPELINE_PARSER_GEN_HPP_INCLUDED
+#endif // !YY_YY_PIPELINE_PARSER_GEN_HPP_INCLUDED
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index 4ce4b2c8a01..0618833d080 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -325,6 +325,8 @@ public:
protected:
explicit ExpressionNary(ExpressionContext* const expCtx) : Expression(expCtx) {}
+ ExpressionNary(ExpressionContext* const expCtx, ExpressionVector&& children)
+ : Expression(expCtx, std::move(children)) {}
void _doAddDependencies(DepsTracker* deps) const override;
};
@@ -345,6 +347,8 @@ public:
protected:
explicit ExpressionNaryBase(ExpressionContext* const expCtx) : ExpressionNary(expCtx) {}
+ ExpressionNaryBase(ExpressionContext* const expCtx, ExpressionVector&& children)
+ : ExpressionNary(expCtx, std::move(children)) {}
};
/// Inherit from this class if your expression takes a variable number of arguments.
@@ -353,6 +357,8 @@ class ExpressionVariadic : public ExpressionNaryBase<SubClass> {
public:
explicit ExpressionVariadic(ExpressionContext* const expCtx)
: ExpressionNaryBase<SubClass>(expCtx) {}
+ ExpressionVariadic(ExpressionContext* const expCtx, Expression::ExpressionVector&& children)
+ : ExpressionNaryBase<SubClass>(expCtx, std::move(children)) {}
};
/**
@@ -380,6 +386,8 @@ class ExpressionFixedArity : public ExpressionNaryBase<SubClass> {
public:
explicit ExpressionFixedArity(ExpressionContext* const expCtx)
: ExpressionNaryBase<SubClass>(expCtx) {}
+ ExpressionFixedArity(ExpressionContext* const expCtx, Expression::ExpressionVector&& children)
+ : ExpressionNaryBase<SubClass>(expCtx, std::move(children)) {}
void validateArguments(const Expression::ExpressionVector& args) const override {
uassert(16020,
@@ -480,6 +488,9 @@ class ExpressionTwoNumericArgs : public ExpressionFixedArity<SubClass, 2> {
public:
explicit ExpressionTwoNumericArgs(ExpressionContext* const expCtx)
: ExpressionFixedArity<SubClass, 2>(expCtx) {}
+ ExpressionTwoNumericArgs(ExpressionContext* const expCtx,
+ Expression::ExpressionVector&& children)
+ : ExpressionFixedArity<SubClass, 2>(expCtx, std::move(children)) {}
virtual ~ExpressionTwoNumericArgs() = default;
@@ -746,6 +757,9 @@ public:
explicit ExpressionAdd(ExpressionContext* const expCtx)
: ExpressionVariadic<ExpressionAdd>(expCtx) {}
+ ExpressionAdd(ExpressionContext* const expCtx, ExpressionVector&& children)
+ : ExpressionVariadic<ExpressionAdd>(expCtx, std::move(children)) {}
+
Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const final;
diff --git a/src/mongo/db/pipeline/expression_trigonometric.cpp b/src/mongo/db/pipeline/expression_trigonometric.cpp
index 0ee9eeb84b1..96369b5109c 100644
--- a/src/mongo/db/pipeline/expression_trigonometric.cpp
+++ b/src/mongo/db/pipeline/expression_trigonometric.cpp
@@ -139,43 +139,6 @@ CREATE_TRIGONOMETRIC_CLASS(HyperbolicTangent, tanh);
#undef CREATE_TRIGONOMETRIC_CLASS
-
-/* ----------------------- ExpressionArcTangent2 ---------------------------- */
-
-class ExpressionArcTangent2 final : public ExpressionTwoNumericArgs<ExpressionArcTangent2> {
-public:
- explicit ExpressionArcTangent2(ExpressionContext* const expCtx)
- : ExpressionTwoNumericArgs(expCtx) {}
-
- Value evaluateNumericArgs(const Value& numericArg1, const Value& numericArg2) const final {
- auto totalType = BSONType::NumberDouble;
- // If the type of either argument is NumberDecimal, we promote to Decimal128.
- if (numericArg1.getType() == BSONType::NumberDecimal ||
- numericArg2.getType() == BSONType::NumberDecimal) {
- totalType = BSONType::NumberDecimal;
- }
- switch (totalType) {
- case BSONType::NumberDecimal: {
- auto dec = numericArg1.coerceToDecimal();
- return Value(dec.atan2(numericArg2.coerceToDecimal()));
- }
- case BSONType::NumberDouble: {
- return Value(
- std::atan2(numericArg1.coerceToDouble(), numericArg2.coerceToDouble()));
- }
- default:
- MONGO_UNREACHABLE;
- }
- }
-
- const char* getOpName() const final {
- return "$atan2";
- }
-
- void acceptVisitor(ExpressionVisitor* visitor) final {
- return visitor->visit(this);
- }
-};
REGISTER_EXPRESSION(atan2, ExpressionArcTangent2::parse);
diff --git a/src/mongo/db/pipeline/expression_trigonometric.h b/src/mongo/db/pipeline/expression_trigonometric.h
index 1c4236e7ccc..b015f97da78 100644
--- a/src/mongo/db/pipeline/expression_trigonometric.h
+++ b/src/mongo/db/pipeline/expression_trigonometric.h
@@ -230,4 +230,42 @@ public:
*/
virtual const char* getOpName() const = 0;
};
+
+class ExpressionArcTangent2 final : public ExpressionTwoNumericArgs<ExpressionArcTangent2> {
+public:
+ explicit ExpressionArcTangent2(ExpressionContext* const expCtx)
+ : ExpressionTwoNumericArgs(expCtx) {}
+
+ ExpressionArcTangent2(ExpressionContext* const expCtx, ExpressionVector&& children)
+ : ExpressionTwoNumericArgs(expCtx, std::move(children)) {}
+
+ Value evaluateNumericArgs(const Value& numericArg1, const Value& numericArg2) const final {
+ auto totalType = BSONType::NumberDouble;
+ // If the type of either argument is NumberDecimal, we promote to Decimal128.
+ if (numericArg1.getType() == BSONType::NumberDecimal ||
+ numericArg2.getType() == BSONType::NumberDecimal) {
+ totalType = BSONType::NumberDecimal;
+ }
+ switch (totalType) {
+ case BSONType::NumberDecimal: {
+ auto dec = numericArg1.coerceToDecimal();
+ return Value(dec.atan2(numericArg2.coerceToDecimal()));
+ }
+ case BSONType::NumberDouble: {
+ return Value(
+ std::atan2(numericArg1.coerceToDouble(), numericArg2.coerceToDouble()));
+ }
+ default:
+ MONGO_UNREACHABLE;
+ }
+ }
+
+ const char* getOpName() const final {
+ return "$atan2";
+ }
+
+ void acceptVisitor(ExpressionVisitor* visitor) final {
+ return visitor->visit(this);
+ }
+};
} // namespace mongo