diff options
author | Jacob Evans <jacob.evans@10gen.com> | 2020-07-14 17:52:36 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-07-23 04:03:44 +0000 |
commit | 00d90d78e84cdc16a03a2a5173ce797e3c3745b9 (patch) | |
tree | 5ec55b080cd910dee7308c5e4d8c6eeac5525bc8 | |
parent | 96ae85e1eb307ef6529d277136b054c3dff20ea3 (diff) | |
download | mongo-00d90d78e84cdc16a03a2a5173ce797e3c3745b9.tar.gz |
SERVER-49331 Implement aggExpression rule in grammar
-rw-r--r-- | src/mongo/db/cst/bson_lexer.cpp | 37 | ||||
-rw-r--r-- | src/mongo/db/cst/bson_lexer_test.cpp | 50 | ||||
-rw-r--r-- | src/mongo/db/cst/c_node.cpp | 13 | ||||
-rw-r--r-- | src/mongo/db/cst/c_node.h | 2 | ||||
-rw-r--r-- | src/mongo/db/cst/cst_pipeline_translation.cpp | 196 | ||||
-rw-r--r-- | src/mongo/db/cst/cst_pipeline_translation_test.cpp | 180 | ||||
-rw-r--r-- | src/mongo/db/cst/cst_test.cpp | 50 | ||||
-rw-r--r-- | src/mongo/db/cst/key_fieldname.h | 1 | ||||
-rw-r--r-- | src/mongo/db/cst/key_value.h | 8 | ||||
-rw-r--r-- | src/mongo/db/cst/location_gen.h | 18 | ||||
-rw-r--r-- | src/mongo/db/cst/pipeline_grammar.yy | 254 | ||||
-rw-r--r-- | src/mongo/db/cst/pipeline_parser_gen.cpp | 935 | ||||
-rw-r--r-- | src/mongo/db/cst/pipeline_parser_gen.hpp | 638 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.h | 14 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_trigonometric.cpp | 37 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_trigonometric.h | 38 |
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 |