diff options
-rw-r--r-- | src/mongo/db/cst/SConscript | 13 | ||||
-rw-r--r-- | src/mongo/db/cst/c_node.cpp | 208 | ||||
-rw-r--r-- | src/mongo/db/cst/c_node.h | 81 | ||||
-rw-r--r-- | src/mongo/db/cst/cst_pipeline_translation.cpp | 110 | ||||
-rw-r--r-- | src/mongo/db/cst/cst_pipeline_translation.h | 46 | ||||
-rw-r--r-- | src/mongo/db/cst/cst_pipeline_translation_test.cpp | 170 | ||||
-rw-r--r-- | src/mongo/db/cst/cst_test.cpp | 34 | ||||
-rw-r--r-- | src/mongo/db/cst/key_fieldname.h | 1 | ||||
-rw-r--r-- | src/mongo/db/cst/location_gen.h | 18 | ||||
-rw-r--r-- | src/mongo/db/cst/pipeline_grammar.yy | 7 | ||||
-rw-r--r-- | src/mongo/db/cst/pipeline_parser_gen.cpp | 228 | ||||
-rw-r--r-- | src/mongo/db/cst/pipeline_parser_gen.hpp | 461 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_project.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_project.h | 19 | ||||
-rw-r--r-- | src/mongo/db/query/projection_parser.cpp | 13 | ||||
-rw-r--r-- | src/mongo/db/query/projection_parser.h | 8 |
16 files changed, 1001 insertions, 428 deletions
diff --git a/src/mongo/db/cst/SConscript b/src/mongo/db/cst/SConscript index 52a88dc0406..281631992f4 100644 --- a/src/mongo/db/cst/SConscript +++ b/src/mongo/db/cst/SConscript @@ -8,11 +8,13 @@ env.Library( target='cst', source=[ 'bson_lexer.cpp', + 'cst_pipeline_translation.cpp', 'c_node.cpp', 'pipeline_parser_gen.cpp', ], LIBDEPS=[ "$BUILD_DIR/mongo/base", + '$BUILD_DIR/mongo/db/pipeline/pipeline', '$BUILD_DIR/mongo/db/query/datetime/date_time_support', ] ) @@ -26,3 +28,14 @@ env.CppUnitTest( 'cst', ] ) + +env.CppUnitTest( + target='cst_pipeline_translation_test', + source=[ + 'cst_pipeline_translation_test.cpp', + ], + LIBDEPS=[ + 'cst', + '$BUILD_DIR/mongo/db/query/query_test_service_context', + ] +) diff --git a/src/mongo/db/cst/c_node.cpp b/src/mongo/db/cst/c_node.cpp index 84fb55d04b2..b677797deff 100644 --- a/src/mongo/db/cst/c_node.cpp +++ b/src/mongo/db/cst/c_node.cpp @@ -57,60 +57,33 @@ auto printFieldname(const CNode::Fieldname& fieldname) { fieldname); } -} // namespace - -std::string CNode::toStringHelper(int numTabs) const { +template <typename T> +auto printValue(const T& payload) { return stdx::visit( visit_helper::Overloaded{ - [numTabs](const Children& children) { - if (auto keyFieldname = stdx::get_if<KeyFieldname>(&children[0].first); - keyFieldname != nullptr && *keyFieldname == KeyFieldname::arrayMarker) - return std::accumulate(children.cbegin(), - children.cend(), - tabs(numTabs) + "[\n", - [numTabs](auto&& string, auto&& childpair) { - return string + - childpair.second.toStringHelper(numTabs + 1) + - "\n"; - }) + - tabs(numTabs) + "]"; - else - return std::accumulate(children.cbegin(), - children.cend(), - tabs(numTabs) + "{\n", - [numTabs](auto&& string, auto&& childpair) { - return string + tabs(numTabs) + - printFieldname(childpair.first) + " :\n" + - childpair.second.toStringHelper(numTabs + 1) + - "\n"; - }) + - tabs(numTabs) + "}"; - }, - [numTabs](const KeyValue& value) { - return tabs(numTabs) + "<KeyValue " + + [](const CNode::ArrayChildren& children) { return "<Array>"s; }, + [](const CNode::ObjectChildren& children) { return "<Object>"s; }, + [](const KeyValue& value) { + return "<KeyValue "s + key_value::toString[static_cast<std::underlying_type_t<KeyValue>>(value)] + ">"; }, - [numTabs](const UserDouble& userDouble) { - return tabs(numTabs) + "<UserDouble " + std::to_string(userDouble) + ">"; + [](const UserDouble& userDouble) { + return "<UserDouble "s + std::to_string(userDouble) + ">"; }, - [numTabs](const UserString& userString) { - return tabs(numTabs) + "<UserString " + userString + ">"; - }, - [numTabs](const UserBinary& userBinary) { - return tabs(numTabs) + "<UserBinary " + typeName(userBinary.type) + ", " + + [](const UserString& userString) { return "<UserString "s + userString + ">"; }, + [](const UserBinary& userBinary) { + return "<UserBinary "s + typeName(userBinary.type) + ", " + toHex(userBinary.data, userBinary.length) + ">"; }, - [numTabs](const UserUndefined& userUndefined) { - return tabs(numTabs) + "<UserUndefined>"; - }, - [numTabs](const UserObjectId& userObjectId) { - return tabs(numTabs) + "<UserObjectId " + userObjectId.toString() + ">"; + [](const UserUndefined& userUndefined) { return "<UserUndefined>"s; }, + [](const UserObjectId& userObjectId) { + return "<UserObjectId "s + userObjectId.toString() + ">"; }, - [numTabs](const UserBoolean& userBoolean) { - return tabs(numTabs) + "<UserBoolean " + std::to_string(userBoolean) + ">"; + [](const UserBoolean& userBoolean) { + return "<UserBoolean "s + std::to_string(userBoolean) + ">"; }, - [numTabs](const UserDate& userDate) { - return tabs(numTabs) + "<UserDate " + + [](const UserDate& userDate) { + return "<UserDate "s + [&] { if (auto string = TimeZoneDatabase::utcZone().formatDate( "%Y-%m-%dT%H:%M:%S.%LZ", userDate); @@ -121,40 +94,131 @@ std::string CNode::toStringHelper(int numTabs) const { }() + ">"; }, - [numTabs](const UserNull& userNull) { return tabs(numTabs) + "<UserNull>"; }, - [numTabs](const UserRegex& userRegex) { - return tabs(numTabs) + "<UserRegex " + "/" + userRegex.pattern + "/" + - userRegex.flags + ">"; - }, - [numTabs](const UserDBPointer& userDBPointer) { - return tabs(numTabs) + "<UserDBPointer " + userDBPointer.ns + ", " + - userDBPointer.oid.toString() + ">"; + [](const UserNull& userNull) { return "<UserNull>"s; }, + [](const UserRegex& userRegex) { + return "<UserRegex "s + "/" + userRegex.pattern + "/" + userRegex.flags + ">"; }, - [numTabs](const UserJavascript& userJavascript) { - return tabs(numTabs) + "<UserJavascript " + userJavascript.code + ">"; + [](const UserDBPointer& userDBPointer) { + return "<UserDBPointer "s + userDBPointer.ns + ", " + userDBPointer.oid.toString() + + ">"; }, - [numTabs](const UserSymbol& userSymbol) { - return tabs(numTabs) + "<UserSymbol " + userSymbol.symbol + ">"; + [](const UserJavascript& userJavascript) { + return "<UserJavascript "s + userJavascript.code + ">"; }, - [numTabs](const UserJavascriptWithScope& userJavascriptWithScope) { - return tabs(numTabs) + "<UserJavascriptWithScope " + userJavascriptWithScope.code + - ", "; + [](const UserSymbol& userSymbol) { return "<UserSymbol "s + userSymbol.symbol + ">"; }, + [](const UserJavascriptWithScope& userJavascriptWithScope) { + return "<UserJavascriptWithScope "s + userJavascriptWithScope.code + ", "; userJavascriptWithScope.scope.toString() + ">"; }, - [numTabs](const UserInt& userInt) { - return tabs(numTabs) + "<UserInt " + std::to_string(userInt) + ">"; - }, - [numTabs](const UserTimestamp& userTimestamp) { - return tabs(numTabs) + "<UserTimestamp " + userTimestamp.toString() + ">"; - }, - [numTabs](const UserLong& userLong) { - return tabs(numTabs) + "<UserLong " + std::to_string(userLong) + ">"; + [](const UserInt& userInt) { return "<UserInt "s + std::to_string(userInt) + ">"; }, + [](const UserTimestamp& userTimestamp) { + return "<UserTimestamp "s + userTimestamp.toString() + ">"; }, - [numTabs](const UserDecimal& userDecimal) { - return tabs(numTabs) + "<UserDecimal " + userDecimal.toString() + ">"; + [](const UserLong& userLong) { return "<UserLong "s + std::to_string(userLong) + ">"; }, + [](const UserDecimal& userDecimal) { + return "<UserDecimal "s + userDecimal.toString() + ">"; }, - [numTabs](const UserMinKey& userMinKey) { return tabs(numTabs) + "<UserMinKey>"; }, - [numTabs](const UserMaxKey& userMaxKey) { return tabs(numTabs) + "<UserMaxKey>"; }}, + [](const UserMinKey& userMinKey) { return "<UserMinKey>"s; }, + [](const UserMaxKey& userMaxKey) { return "<UserMaxKey>"s; }}, + payload); +} + +} // namespace + +std::string CNode::toStringHelper(int numTabs) const { + return stdx::visit( + visit_helper::Overloaded{ + [numTabs](const ArrayChildren& children) { + return std::accumulate(children.cbegin(), + children.cend(), + tabs(numTabs) + "[\n", + [numTabs](auto&& string, auto&& child) { + return string + child.toStringHelper(numTabs + 1) + "\n"; + }) + + tabs(numTabs) + "]"; + }, + [numTabs](const ObjectChildren& children) { + return std::accumulate(children.cbegin(), + children.cend(), + tabs(numTabs) + "{\n", + [numTabs](auto&& string, auto&& childpair) { + return string + tabs(numTabs) + + printFieldname(childpair.first) + " :\n" + + childpair.second.toStringHelper(numTabs + 1) + "\n"; + }) + + tabs(numTabs) + "}"; + }, + [this, numTabs](auto&&) { return tabs(numTabs) + printValue(payload); }}, + payload); +} + +std::pair<BSONObj, bool> CNode::toBsonWithArrayIndicator() const { + return stdx::visit( + visit_helper::Overloaded{ + // Build an array which will lose its identity and appear as a BSONObj + [](const ArrayChildren& children) { + return std::pair{ + std::accumulate( + children.cbegin(), + children.cend(), + BSONObj{}, + [fieldCount = 0u](auto&& bson, auto&& child) mutable { + // This is a non-compound field. pull the BSONElement out of it's + // BSONObj shell and add it. + if (auto [childBson, isArray] = child.toBsonWithArrayIndicator(); + childBson.nFields() > 0 && + childBson.firstElementFieldNameStringData() == "") + return bson.addField( + childBson + .replaceFieldNames(BSON(std::to_string(fieldCount++) << "")) + .firstElement()); + // This field is an array. Reconstruct with BSONArray and add it. + else if (isArray) + return bson.addField( + BSON(std::to_string(fieldCount++) << BSONArray{childBson}) + .firstElement()); + // This field is an object. Add it directly. + else + return bson.addField( + BSON(std::to_string(fieldCount++) << childBson).firstElement()); + }), + true}; + }, + // Build an object in a BSONObj. + [](const ObjectChildren& children) { + return std::pair{ + std::accumulate( + children.cbegin(), + children.cend(), + BSONObj{}, + [](auto&& bson, auto&& childPair) { + // This is a non-compound field. pull the BSONElement out of it's + // BSONObj shell and add it. + if (auto [childBson, isArray] = + childPair.second.toBsonWithArrayIndicator(); + childBson.nFields() > 0 && + childBson.firstElementFieldNameStringData() == "") + return bson.addField(childBson + .replaceFieldNames(BSON( + printFieldname(childPair.first) << "")) + .firstElement()); + // This field is an array. Reconstruct with BSONArray and add it. + else if (isArray) + return bson.addField( + BSON(printFieldname(childPair.first) << BSONArray{childBson}) + .firstElement()); + // This field is an object. Add it directly. + else + return bson.addField( + BSON(printFieldname(childPair.first) << childBson) + .firstElement()); + }), + false}; + }, + // Build a non-compound field in a BSONObj shell. + [this](auto&&) { + return std::pair{BSON("" << printValue(payload)), false}; + }}, payload); } diff --git a/src/mongo/db/cst/c_node.h b/src/mongo/db/cst/c_node.h index 773eb49e801..47bbbcab05d 100644 --- a/src/mongo/db/cst/c_node.h +++ b/src/mongo/db/cst/c_node.h @@ -31,11 +31,13 @@ #include "mongo/platform/basic.h" +#include <memory> #include <string> #include <utility> #include <vector> #include "mongo/bson/bsonmisc.h" +#include "mongo/bson/bsonobj.h" #include "mongo/bson/oid.h" #include "mongo/bson/timestamp.h" #include "mongo/db/cst/key_fieldname.h" @@ -70,11 +72,80 @@ struct UserMaxKey {}; struct CNode { static auto noopLeaf() { - return CNode{Children{}}; + return CNode{ObjectChildren{}}; } + /** + * Produce a string formatted with tabs and endlines that describes the CST underneath this + * CNode. + */ auto toString() const { - return toStringHelper(0); + return toStringHelper(0) + "\n"; + } + + /** + * Produce BSON representing this CST. This is for debugging and testing with structured output, + * not for decompiling to the input query. The produced BSON will consist of arrays, objects, + * and descriptive strings only. This version also returns bool that indicates if the returned + * BSON is a BSONArray. + */ + std::pair<BSONObj, bool> toBsonWithArrayIndicator() const; + /** + * Produce BSON representing this CST. This is for debugging and testing with structured output, + * not for decompiling to the input query. The produced BSON will consist of arrays, objects, + * and descriptive strings only. + */ + BSONObj toBson() const { + return toBsonWithArrayIndicator().first; + } + + /* + * Produce the children of this CNode representing an array. Throws a fatal exception if this + * CNode does not represent an array. Const version. + */ + auto& arrayChildren() const { + return stdx::get<ArrayChildren>(payload); + } + /* + * Produce the children of this CNode representing an array. Throws a fatal exception if this + * CNode does not represent an array. Non-const version. + */ + auto& arrayChildren() { + return stdx::get<ArrayChildren>(payload); + } + + /* + * Produce the children of this CNode representing an object. Throws a fatal exception if this + * CNode does not represent an object. Const version. + */ + auto& objectChildren() const { + return stdx::get<ObjectChildren>(payload); + } + /* + * Produce the children of this CNode representing an object. Throws a fatal exception if this + * CNode does not represent an object. Non-const version. + */ + auto& objectChildren() { + return stdx::get<ObjectChildren>(payload); + } + + /* + * Produce the KeyFieldname of the first element of this CNode representing an object. Throws a + * fatal exception if this CNode does not represent an object, if it is an empty object or if + * the first element does not have a KeyFieldname. Const version. + */ + auto& firstKeyFieldname() const { + dassert(objectChildren().size() > 0); + return stdx::get<KeyFieldname>(objectChildren().begin()->first); + } + /* + * Produce the KeyFieldname of the first element of this CNode representing an object. Throws a + * fatal exception if this CNode does not represent an object, if it is an empty object or if + * the first element does not have a KeyFieldname. Non-const version. + */ + auto& firstKeyFieldname() { + dassert(objectChildren().size() > 0); + return stdx::get<KeyFieldname>(objectChildren().begin()->first); } private: @@ -82,8 +153,10 @@ private: public: using Fieldname = stdx::variant<KeyFieldname, UserFieldname>; - using Children = std::vector<std::pair<Fieldname, CNode>>; - stdx::variant<Children, + using ArrayChildren = std::vector<CNode>; + using ObjectChildren = std::vector<std::pair<Fieldname, CNode>>; + stdx::variant<ArrayChildren, + ObjectChildren, KeyValue, UserDouble, UserString, diff --git a/src/mongo/db/cst/cst_pipeline_translation.cpp b/src/mongo/db/cst/cst_pipeline_translation.cpp new file mode 100644 index 00000000000..85602fe5d64 --- /dev/null +++ b/src/mongo/db/cst/cst_pipeline_translation.cpp @@ -0,0 +1,110 @@ +/** + * Copyright (C) 2020-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + + +#include <algorithm> +#include <boost/intrusive_ptr.hpp> +#include <iterator> + +#include "mongo/db/cst/c_node.h" +#include "mongo/db/cst/cst_pipeline_translation.h" +#include "mongo/db/cst/key_fieldname.h" +#include "mongo/db/cst/key_value.h" +#include "mongo/db/exec/inclusion_projection_executor.h" +#include "mongo/db/pipeline/document_source.h" +#include "mongo/db/pipeline/document_source_project.h" +#include "mongo/db/pipeline/expression_context.h" +#include "mongo/db/query/projection.h" +#include "mongo/db/query/projection_ast.h" +#include "mongo/db/query/projection_parser.h" +#include "mongo/util/intrusive_counter.h" + +namespace mongo::cst_pipeline_translation { +namespace { +/** + * Walk a projection CNode and produce a ProjectionASTNode. + */ +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); +} + +/** + * Walk a project stage object CNode and produce a DocumentSourceSingleDocumentTransformation. + */ +auto translateProject(const CNode& cst, const boost::intrusive_ptr<ExpressionContext>& expCtx) { + using namespace projection_ast; + auto root = ProjectionPathASTNode{}; + bool sawId = false; + + for (auto&& [name, child] : cst.objectChildren()) { + if (stdx::get<UserFieldname>(name) == UserFieldname{"_id"}) + sawId = true; + addNodeAtPath(&root, stdx::get<UserFieldname>(name), translateProjection(child, expCtx)); + } + + 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"); +} + +/** + * Walk an aggregation pipeline stage object CNode and produce a DocumentSource. + */ +auto translateSource(const CNode& cst, const boost::intrusive_ptr<ExpressionContext>& expCtx) { + switch (cst.firstKeyFieldname()) { + case KeyFieldname::project: + return translateProject(cst.objectChildren()[0].second, expCtx); + default: + MONGO_UNREACHABLE; + } +} + +} // namespace + +/** + * Walk a pipeline array CNode and produce a Pipeline. + */ +std::unique_ptr<Pipeline, PipelineDeleter> translatePipeline( + const CNode& cst, const boost::intrusive_ptr<ExpressionContext>& expCtx) { + auto sources = Pipeline::SourceContainer{}; + static_cast<void>(std::transform(cst.arrayChildren().begin(), + cst.arrayChildren().end(), + std::back_inserter(sources), + [&](auto&& elem) { return translateSource(elem, expCtx); })); + return Pipeline::create(std::move(sources), expCtx); +} + +} // namespace mongo::cst_pipeline_translation diff --git a/src/mongo/db/cst/cst_pipeline_translation.h b/src/mongo/db/cst/cst_pipeline_translation.h new file mode 100644 index 00000000000..bac68db91da --- /dev/null +++ b/src/mongo/db/cst/cst_pipeline_translation.h @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2020-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include "mongo/platform/basic.h" + +#include <memory> + +#include "mongo/db/pipeline/pipeline.h" + +namespace mongo::cst_pipeline_translation { + +/** + * Walk a pipeline array CNode and produce a Pipeline. + */ +std::unique_ptr<Pipeline, PipelineDeleter> translatePipeline( + const CNode& cst, const boost::intrusive_ptr<ExpressionContext>& expCtx); + +} // namespace mongo::cst_pipeline_translation diff --git a/src/mongo/db/cst/cst_pipeline_translation_test.cpp b/src/mongo/db/cst/cst_pipeline_translation_test.cpp new file mode 100644 index 00000000000..bfcc11ef12d --- /dev/null +++ b/src/mongo/db/cst/cst_pipeline_translation_test.cpp @@ -0,0 +1,170 @@ +/** + * Copyright (C) 2020-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include <boost/intrusive_ptr.hpp> +#include <iostream> +#include <string> + +#include "mongo/bson/bsonobj.h" +#include "mongo/bson/unordered_fields_bsonobj_comparator.h" +#include "mongo/db/cst/c_node.h" +#include "mongo/db/cst/cst_pipeline_translation.h" +#include "mongo/db/cst/key_fieldname.h" +#include "mongo/db/cst/key_value.h" +#include "mongo/db/exec/document_value/document.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/pipeline/document_source_single_document_transformation.h" +#include "mongo/db/pipeline/expression_context_for_test.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { +using namespace std::string_literals; + +TEST(CstTest, TranslatesEmpty) { + auto nss = NamespaceString{"db", "coll"}; + const auto cst = CNode{CNode::ArrayChildren{}}; + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest(nss)); + auto pipeline = cst_pipeline_translation::translatePipeline(cst, expCtx); + 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& sources = pipeline->getSources(); + ASSERT_EQ(1u, sources.size()); + auto iter = sources.begin(); + ASSERT(typeid(DocumentSourceSingleDocumentTransformation) == typeid(**iter)); +} + +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& sources = pipeline->getSources(); + ASSERT_EQ(3u, sources.size()); + auto iter = sources.begin(); + ASSERT(typeid(DocumentSourceSingleDocumentTransformation) == typeid(**iter++)); + ASSERT(typeid(DocumentSourceSingleDocumentTransformation) == typeid(**iter++)); + ASSERT(typeid(DocumentSourceSingleDocumentTransformation) == typeid(**iter)); +} + +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& 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" << true) == + singleDoc.getTransformer().serializeTransformation(boost::none).toBson())); +} + +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); + 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" << true << "b" << true) == + singleDoc.getTransformer().serializeTransformation(boost::none).toBson())); +} + +TEST(CstTest, TranslatesMultipleInclusionProjectionStages) { + auto nss = NamespaceString{"db", "coll"}; + 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}}}}}}}, + }}; + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest(nss)); + auto pipeline = cst_pipeline_translation::translatePipeline(cst, expCtx); + auto& sources = pipeline->getSources(); + ASSERT_EQ(3u, 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" << true) == + singleDoc.getTransformer().serializeTransformation(boost::none).toBson())); + } + { + auto& singleDoc = dynamic_cast<DocumentSourceSingleDocumentTransformation&>(**iter++); + // DocumenSourceSingleDoucmentTransformation reorders fields so we need to be + // insensitive. + ASSERT(UnorderedFieldsBSONObjComparator{}.evaluate( + BSON("_id" << true << "b" << true) == + singleDoc.getTransformer().serializeTransformation(boost::none).toBson())); + } + { + auto& singleDoc = dynamic_cast<DocumentSourceSingleDocumentTransformation&>(**iter); + // DocumenSourceSingleDoucmentTransformation reorders fields so we need to be + // insensitive. + ASSERT(UnorderedFieldsBSONObjComparator{}.evaluate( + BSON("_id" << true << "c" << true) == + singleDoc.getTransformer().serializeTransformation(boost::none).toBson())); + } +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/db/cst/cst_test.cpp b/src/mongo/db/cst/cst_test.cpp index ce4a20f3f81..0d4181c55aa 100644 --- a/src/mongo/db/cst/cst_test.cpp +++ b/src/mongo/db/cst/cst_test.cpp @@ -29,7 +29,6 @@ #include "mongo/platform/basic.h" -#include <iostream> #include <string> #include "mongo/bson/json.h" @@ -38,30 +37,29 @@ #include "mongo/db/cst/key_fieldname.h" #include "mongo/db/cst/key_value.h" #include "mongo/db/cst/pipeline_parser_gen.hpp" -#include "mongo/db/query/util/make_data_structure.h" +#include "mongo/unittest/bson_test_util.h" #include "mongo/unittest/unittest.h" namespace mongo { namespace { -using namespace std::string_literals; TEST(CstTest, BuildsAndPrints) { { - const auto cst = CNode{CNode::Children{ + const auto cst = CNode{CNode::ObjectChildren{ {KeyFieldname::atan2, - CNode{CNode::Children{{KeyFieldname::arrayMarker, CNode{UserDouble{3.0}}}, - {KeyFieldname::arrayMarker, CNode{UserDouble{2.0}}}}}}}}; - ASSERT_EQ("{\natan2 :\n\t[\n\t\t<UserDouble 3.000000>\n\t\t<UserDouble 2.000000>\n\t]\n}"s, - cst.toString()); + CNode{CNode::ArrayChildren{CNode{UserDouble{3.0}}, CNode{UserDouble{2.0}}}}}}}; + ASSERT_BSONOBJ_EQ( + fromjson("{atan2: [\"<UserDouble 3.000000>\", \"<UserDouble 2.000000>\"]}"), + cst.toBson()); } { - const auto cst = CNode{CNode::Children{ + const auto cst = CNode{CNode::ObjectChildren{ {KeyFieldname::project, - CNode{CNode::Children{{UserFieldname{"a"}, CNode{KeyValue::trueKey}}, - {KeyFieldname::id, CNode{KeyValue::falseKey}}}}}}}; - ASSERT_EQ("{\nproject :\n\t{\n\ta :\n\t\t<KeyValue trueKey>\n"s + - "\tid :\n\t\t<KeyValue falseKey>\n\t}\n}", - cst.toString()); + CNode{CNode::ObjectChildren{{UserFieldname{"a"}, CNode{KeyValue::trueKey}}, + {KeyFieldname::id, CNode{KeyValue::falseKey}}}}}}}; + ASSERT_BSONOBJ_EQ( + fromjson("{project : {a: \"<KeyValue trueKey>\", id: \"<KeyValue falseKey>\"}}"), + cst.toBson()); } } @@ -71,8 +69,8 @@ TEST(CstGrammarTest, EmptyPipeline) { BSONLexer lexer(input["pipeline"].Array()); auto parseTree = PipelineParserGen(lexer, &output); ASSERT_EQ(0, parseTree.parse()); - ASSERT_TRUE(stdx::get_if<CNode::Children>(&output.payload)); - ASSERT_EQ(0, stdx::get_if<CNode::Children>(&output.payload)->size()); + ASSERT_TRUE(stdx::get_if<CNode::ArrayChildren>(&output.payload)); + ASSERT_EQ(0, stdx::get_if<CNode::ArrayChildren>(&output.payload)->size()); } TEST(CstGrammarTest, InvalidPipelineSpec) { @@ -109,9 +107,9 @@ TEST(CstGrammarTest, ParsesInternalInhibitOptimization) { BSONLexer lexer(input["pipeline"].Array()); auto parseTree = PipelineParserGen(lexer, &output); ASSERT_EQ(0, parseTree.parse()); - auto stages = stdx::get<CNode::Children>(output.payload); + auto stages = stdx::get<CNode::ArrayChildren>(output.payload); ASSERT_EQ(1, stages.size()); - ASSERT(KeyFieldname::inhibitOptimization == stdx::get<KeyFieldname>(stages[0].first)); + ASSERT(KeyFieldname::inhibitOptimization == stages[0].firstKeyFieldname()); } { CNode output; diff --git a/src/mongo/db/cst/key_fieldname.h b/src/mongo/db/cst/key_fieldname.h index 2ddb692b6b0..edde3699941 100644 --- a/src/mongo/db/cst/key_fieldname.h +++ b/src/mongo/db/cst/key_fieldname.h @@ -34,7 +34,6 @@ #include "mongo/util/printable_enum.h" #define KEYFIELDNAMES(ENUMIFY) \ - ENUMIFY(arrayMarker) \ ENUMIFY(atan2) \ ENUMIFY(id) \ ENUMIFY(project) \ diff --git a/src/mongo/db/cst/location_gen.h b/src/mongo/db/cst/location_gen.h index 7a3a880671c..c1a0c8cac32 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.5.4. +// 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 52 "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 52 "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 3d8bff404d5..9616e113443 100644 --- a/src/mongo/db/cst/pipeline_grammar.yy +++ b/src/mongo/db/cst/pipeline_grammar.yy @@ -118,8 +118,7 @@ // // Semantic values (aka the C++ types produced by the actions). // -%type <CNode> stageList -%type <std::pair<KeyFieldname, CNode>> stage +%nterm <CNode> stageList stage // // Grammar rules @@ -135,14 +134,14 @@ stageList[result]: %empty { } | START_OBJECT stage END_OBJECT stageList[stagesArg] { $result = std::move($stagesArg); - auto& children = stdx::get<CNode::Children>($result.payload); + auto& children = stdx::get<CNode::ArrayChildren>($result.payload); children.emplace_back(std::move($stage)); } ; stage: STAGE_INHIBIT_OPTIMIZATION START_OBJECT END_OBJECT { - $stage = std::pair{KeyFieldname::inhibitOptimization, CNode::noopLeaf()}; + $stage = CNode{CNode::ObjectChildren{std::pair{KeyFieldname::inhibitOptimization, CNode::noopLeaf()}}}; } ; diff --git a/src/mongo/db/cst/pipeline_parser_gen.cpp b/src/mongo/db/cst/pipeline_parser_gen.cpp index d463f5e782f..83f31a004e2 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.5.4. +// A Bison parser, made by GNU Bison 3.6.3. // Skeleton implementation for Bison LALR(1) parsers in C++ @@ -30,15 +30,16 @@ // This special exception was added by the Free Software Foundation in // version 2.2 of Bison. -// Undocumented macros, especially those whose name start with YY_, -// are private implementation details. Do not rely on them. +// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, +// especially those whose name start with YY_ or yy_. They are +// private implementation details that can be changed or removed. #include "pipeline_parser_gen.hpp" // Unqualified %code blocks. -#line 77 "src/mongo/db/cst/pipeline_grammar.yy" +#line 77 "pipeline_grammar.yy" #include "mongo/db/cst/bson_lexer.h" @@ -49,7 +50,7 @@ void PipelineParserGen::error(const PipelineParserGen::location_type& loc, const } } // namespace mongo -#line 57 "src/mongo/db/cst/pipeline_parser_gen.cpp" +#line 58 "pipeline_parser_gen.cpp" #ifndef YY_ @@ -64,6 +65,7 @@ void PipelineParserGen::error(const PipelineParserGen::location_type& loc, const #endif #endif + // Whether we are compiled with exception support. #ifndef YY_EXCEPTIONS #if defined __GNUC__ && !defined __EXCEPTIONS @@ -114,10 +116,10 @@ void PipelineParserGen::error(const PipelineParserGen::location_type& loc, const yy_reduce_print_(Rule); \ } while (false) -#define YY_STACK_PRINT() \ - do { \ - if (yydebug_) \ - yystack_print_(); \ +#define YY_STACK_PRINT() \ + do { \ + if (yydebug_) \ + yy_stack_print_(); \ } while (false) #else // !YYDEBUG @@ -139,10 +141,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 52 "pipeline_grammar.yy" namespace mongo { -#line 149 "src/mongo/db/cst/pipeline_parser_gen.cpp" - +#line 151 "pipeline_parser_gen.cpp" /// Build a parser object. PipelineParserGen::PipelineParserGen(BSONLexer& driver_yyarg, CNode* cst_yyarg) @@ -161,7 +162,7 @@ PipelineParserGen::~PipelineParserGen() {} PipelineParserGen::syntax_error::~syntax_error() YY_NOEXCEPT YY_NOTHROW {} /*---------------. -| Symbol types. | +| symbol kinds. | `---------------*/ @@ -181,19 +182,20 @@ void PipelineParserGen::by_state::move(by_state& that) { PipelineParserGen::by_state::by_state(state_type s) YY_NOEXCEPT : state(s) {} -PipelineParserGen::symbol_number_type PipelineParserGen::by_state::type_get() const YY_NOEXCEPT { +PipelineParserGen::symbol_kind_type PipelineParserGen::by_state::kind() const YY_NOEXCEPT { if (state == empty_state) - return empty_symbol; + return symbol_kind::S_YYEMPTY; else - return yystos_[+state]; + return YY_CAST(symbol_kind_type, yystos_[+state]); } 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.type_get()) { - case 17: // stageList + switch (that.kind()) { + case 16: // stageList + case 17: // stage value.YY_MOVE_OR_COPY<CNode>(YY_MOVE(that.value)); break; @@ -213,10 +215,6 @@ PipelineParserGen::stack_symbol_type::stack_symbol_type(YY_RVREF(stack_symbol_ty value.YY_MOVE_OR_COPY<long long>(YY_MOVE(that.value)); break; - case 18: // stage - value.YY_MOVE_OR_COPY<std::pair<KeyFieldname, CNode>>(YY_MOVE(that.value)); - break; - case 10: // STRING value.YY_MOVE_OR_COPY<std::string>(YY_MOVE(that.value)); break; @@ -233,8 +231,9 @@ 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.type_get()) { - case 17: // stageList + switch (that.kind()) { + case 16: // stageList + case 17: // stage value.move<CNode>(YY_MOVE(that.value)); break; @@ -254,10 +253,6 @@ PipelineParserGen::stack_symbol_type::stack_symbol_type(state_type s, YY_MOVE_RE value.move<long long>(YY_MOVE(that.value)); break; - case 18: // stage - value.move<std::pair<KeyFieldname, CNode>>(YY_MOVE(that.value)); - break; - case 10: // STRING value.move<std::string>(YY_MOVE(that.value)); break; @@ -267,15 +262,16 @@ PipelineParserGen::stack_symbol_type::stack_symbol_type(state_type s, YY_MOVE_RE } // that is emptied. - that.type = empty_symbol; + that.kind_ = symbol_kind::S_YYEMPTY; } #if YY_CPLUSPLUS < 201103L PipelineParserGen::stack_symbol_type& PipelineParserGen::stack_symbol_type::operator=( const stack_symbol_type& that) { state = that.state; - switch (that.type_get()) { - case 17: // stageList + switch (that.kind()) { + case 16: // stageList + case 17: // stage value.copy<CNode>(that.value); break; @@ -295,10 +291,6 @@ PipelineParserGen::stack_symbol_type& PipelineParserGen::stack_symbol_type::oper value.copy<long long>(that.value); break; - case 18: // stage - value.copy<std::pair<KeyFieldname, CNode>>(that.value); - break; - case 10: // STRING value.copy<std::string>(that.value); break; @@ -314,8 +306,9 @@ PipelineParserGen::stack_symbol_type& PipelineParserGen::stack_symbol_type::oper PipelineParserGen::stack_symbol_type& PipelineParserGen::stack_symbol_type::operator=( stack_symbol_type& that) { state = that.state; - switch (that.type_get()) { - case 17: // stageList + switch (that.kind()) { + case 16: // stageList + case 17: // stage value.move<CNode>(that.value); break; @@ -335,10 +328,6 @@ PipelineParserGen::stack_symbol_type& PipelineParserGen::stack_symbol_type::oper value.move<long long>(that.value); break; - case 18: // stage - value.move<std::pair<KeyFieldname, CNode>>(that.value); - break; - case 10: // STRING value.move<std::string>(that.value); break; @@ -365,18 +354,15 @@ template <typename Base> void PipelineParserGen::yy_print_(std::ostream& yyo, const basic_symbol<Base>& yysym) const { std::ostream& yyoutput = yyo; YYUSE(yyoutput); - symbol_number_type yytype = yysym.type_get(); -#if defined __GNUC__ && !defined __clang__ && !defined __ICC && \ - __GNUC__ * 100 + __GNUC_MINOR__ <= 408 - // Avoid a (spurious) G++ 4.8 warning about "array subscript is - // below array bounds". if (yysym.empty()) - std::abort(); -#endif - yyo << (yytype < yyntokens_ ? "token" : "nterm") << ' ' << yytname_[yytype] << " (" - << yysym.location << ": "; - YYUSE(yytype); - yyo << ')'; + yyo << "empty symbol"; + else { + symbol_kind_type yykind = yysym.kind(); + yyo << (yykind < YYNTOKENS ? "token" : "nterm") << ' ' << yysym.name() << " (" + << yysym.location << ": "; + YYUSE(yykind); + yyo << ')'; + } } #endif @@ -419,11 +405,11 @@ void PipelineParserGen::set_debug_level(debug_level_type l) { #endif // YYDEBUG PipelineParserGen::state_type PipelineParserGen::yy_lr_goto_state_(state_type yystate, int yysym) { - int yyr = yypgoto_[yysym - yyntokens_] + yystate; + int yyr = yypgoto_[yysym - YYNTOKENS] + yystate; if (0 <= yyr && yyr <= yylast_ && yycheck_[yyr] == yystate) return yytable_[yyr]; else - return yydefgoto_[yysym - yyntokens_]; + return yydefgoto_[yysym - YYNTOKENS]; } bool PipelineParserGen::yy_pact_value_is_default_(int yyvalue) { @@ -475,6 +461,7 @@ int PipelineParserGen::parse() { `-----------------------------------------------*/ yynewstate: YYCDEBUG << "Entering state " << int(yystack_[0].state) << '\n'; + YY_STACK_PRINT(); // Accept? if (yystack_[0].state == yyfinal_) @@ -494,7 +481,7 @@ int PipelineParserGen::parse() { // Read a lookahead token. if (yyla.empty()) { - YYCDEBUG << "Reading a token: "; + YYCDEBUG << "Reading a token\n"; #if YY_EXCEPTIONS try #endif // YY_EXCEPTIONS @@ -512,10 +499,19 @@ int PipelineParserGen::parse() { } YY_SYMBOL_PRINT("Next token is", yyla); + if (yyla.kind() == symbol_kind::S_YYerror) { + // The scanner already issued an error message, process directly + // to error recovery. But do not keep the error token as + // lookahead, it is too special and may lead us to an endless + // loop in error recovery. */ + yyla.kind_ = symbol_kind::S_YYUNDEF; + goto yyerrlab1; + } + /* If the proper action on seeing token YYLA.TYPE is to reduce or to detect an error, take that action. */ - yyn += yyla.type_get(); - if (yyn < 0 || yylast_ < yyn || yycheck_[yyn] != yyla.type_get()) { + yyn += yyla.kind(); + if (yyn < 0 || yylast_ < yyn || yycheck_[yyn] != yyla.kind()) { goto yydefault; } @@ -559,7 +555,8 @@ int PipelineParserGen::parse() { correct type. The default '$$ = $1' action is NOT applied when using variants. */ switch (yyr1_[yyn]) { - case 17: // stageList + case 16: // stageList + case 17: // stage yylhs.value.emplace<CNode>(); break; @@ -579,10 +576,6 @@ int PipelineParserGen::parse() { yylhs.value.emplace<long long>(); break; - case 18: // stage - yylhs.value.emplace<std::pair<KeyFieldname, CNode>>(); - break; - case 10: // STRING yylhs.value.emplace<std::string>(); break; @@ -607,43 +600,42 @@ int PipelineParserGen::parse() { { switch (yyn) { case 2: -#line 130 "src/mongo/db/cst/pipeline_grammar.yy" +#line 129 "pipeline_grammar.yy" { *cst = std::move(yystack_[1].value.as<CNode>()); } -#line 678 "src/mongo/db/cst/pipeline_parser_gen.cpp" +#line 673 "pipeline_parser_gen.cpp" break; case 3: -#line 135 "src/mongo/db/cst/pipeline_grammar.yy" +#line 134 "pipeline_grammar.yy" { } -#line 684 "src/mongo/db/cst/pipeline_parser_gen.cpp" +#line 679 "pipeline_parser_gen.cpp" break; case 4: -#line 136 "src/mongo/db/cst/pipeline_grammar.yy" +#line 135 "pipeline_grammar.yy" { yylhs.value.as<CNode>() = std::move(yystack_[0].value.as<CNode>()); auto& children = - stdx::get<CNode::Children>(yylhs.value.as<CNode>().payload); - children.emplace_back( - std::move(yystack_[2].value.as<std::pair<KeyFieldname, CNode>>())); + stdx::get<CNode::ArrayChildren>(yylhs.value.as<CNode>().payload); + children.emplace_back(std::move(yystack_[2].value.as<CNode>())); } -#line 694 "src/mongo/db/cst/pipeline_parser_gen.cpp" +#line 689 "pipeline_parser_gen.cpp" break; case 5: -#line 144 "src/mongo/db/cst/pipeline_grammar.yy" +#line 143 "pipeline_grammar.yy" { - yylhs.value.as<std::pair<KeyFieldname, CNode>>() = - std::pair{KeyFieldname::inhibitOptimization, CNode::noopLeaf()}; + yylhs.value.as<CNode>() = CNode{CNode::ObjectChildren{ + std::pair{KeyFieldname::inhibitOptimization, CNode::noopLeaf()}}}; } -#line 702 "src/mongo/db/cst/pipeline_parser_gen.cpp" +#line 697 "pipeline_parser_gen.cpp" break; -#line 706 "src/mongo/db/cst/pipeline_parser_gen.cpp" +#line 701 "pipeline_parser_gen.cpp" default: break; @@ -659,7 +651,6 @@ int PipelineParserGen::parse() { YY_SYMBOL_PRINT("-> $$ =", yylhs); yypop_(yylen); yylen = 0; - YY_STACK_PRINT(); // Shift the result of the reduction. yypush_(YY_NULLPTR, YY_MOVE(yylhs)); @@ -674,7 +665,8 @@ int PipelineParserGen::parse() { // If not already recovering from an error, report this error. if (!yyerrstatus_) { ++yynerrs_; - error(yyla.location, yysyntax_error_(yystack_[0].state, yyla)); + std::string msg = YY_("syntax error"); + error(yyla.location, YY_MOVE(msg)); } @@ -684,7 +676,7 @@ int PipelineParserGen::parse() { error, discard it. */ // Return failure if at end of input. - if (yyla.type_get() == yyeof_) + if (yyla.kind() == symbol_kind::S_YYEOF) YYABORT; else if (!yyla.empty()) { yy_destroy_("Error: discarding", yyla); @@ -709,6 +701,7 @@ int PipelineParserGen::parse() { this YYERROR. */ yypop_(yylen); yylen = 0; + YY_STACK_PRINT(); goto yyerrlab1; @@ -717,28 +710,29 @@ int PipelineParserGen::parse() { `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus_ = 3; // Each real token shifted decrements this. - { - stack_symbol_type error_token; - for (;;) { - yyn = yypact_[+yystack_[0].state]; - if (!yy_pact_value_is_default_(yyn)) { - yyn += yy_error_token_; - if (0 <= yyn && yyn <= yylast_ && yycheck_[yyn] == yy_error_token_) { - yyn = yytable_[yyn]; - if (0 < yyn) - break; - } + // Pop stack until we find a state that shifts the error token. + for (;;) { + yyn = yypact_[+yystack_[0].state]; + if (!yy_pact_value_is_default_(yyn)) { + yyn += symbol_kind::S_YYerror; + if (0 <= yyn && yyn <= yylast_ && yycheck_[yyn] == symbol_kind::S_YYerror) { + yyn = yytable_[yyn]; + if (0 < yyn) + break; } + } - // Pop the current state because it cannot handle the error token. - if (yystack_.size() == 1) - YYABORT; + // Pop the current state because it cannot handle the error token. + if (yystack_.size() == 1) + YYABORT; - yyerror_range[1].location = yystack_[0].location; - yy_destroy_("Error: popping", yystack_[0]); - yypop_(); - YY_STACK_PRINT(); - } + yyerror_range[1].location = yystack_[0].location; + yy_destroy_("Error: popping", yystack_[0]); + yypop_(); + YY_STACK_PRINT(); + } + { + stack_symbol_type error_token; yyerror_range[2].location = yyla.location; YYLLOC_DEFAULT(error_token.location, yyerror_range, 2); @@ -776,6 +770,7 @@ int PipelineParserGen::parse() { /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ yypop_(yylen); + YY_STACK_PRINT(); while (1 < yystack_.size()) { yy_destroy_("Cleanup: popping", yystack_[0]); yypop_(); @@ -804,10 +799,11 @@ void PipelineParserGen::error(const syntax_error& yyexc) { error(yyexc.location, yyexc.what()); } -// Generate an error message. -std::string PipelineParserGen::yysyntax_error_(state_type, const symbol_type&) const { - return YY_("syntax error"); +#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_ = -8; @@ -818,27 +814,27 @@ const signed char PipelineParserGen::yypact_[] = {-7, -2, 2, -6, -4, -8, 3, 1, - const signed char PipelineParserGen::yydefact_[] = {0, 3, 0, 0, 0, 1, 0, 0, 2, 0, 3, 5, 4}; -const signed char PipelineParserGen::yypgoto_[] = {-8, -8, -3, -8}; +const signed char PipelineParserGen::yypgoto_[] = {-8, -3, -8, -8}; -const signed char PipelineParserGen::yydefgoto_[] = {-1, 2, 4, 7}; +const signed char PipelineParserGen::yydefgoto_[] = {-1, 4, 7, 2}; const signed char PipelineParserGen::yytable_[] = {1, 3, 5, 6, 8, 10, 9, 12, 11}; const signed char PipelineParserGen::yycheck_[] = {7, 3, 0, 9, 8, 4, 3, 10, 4}; -const signed char PipelineParserGen::yystos_[] = {0, 7, 16, 3, 17, 0, 9, 18, 8, 3, 4, 4, 17}; +const signed char PipelineParserGen::yystos_[] = {0, 7, 18, 3, 16, 0, 9, 17, 8, 3, 4, 4, 16}; -const signed char PipelineParserGen::yyr1_[] = {0, 15, 16, 17, 17, 18}; +const signed char PipelineParserGen::yyr1_[] = {0, 15, 18, 16, 16, 17}; const signed char PipelineParserGen::yyr2_[] = {0, 2, 3, 0, 4, 3}; #if YYDEBUG // YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. -// First, the terminals, then, starting at \a yyntokens_, nonterminals. +// First, the terminals, then, starting at \a YYNTOKENS, nonterminals. const char* const PipelineParserGen::yytname_[] = {"\"EOF\"", "error", - "$undefined", + "\"invalid token\"", "START_OBJECT", "END_OBJECT", "START_ORDERED_OBJECT", @@ -852,24 +848,24 @@ const char* const PipelineParserGen::yytname_[] = {"\"EOF\"", "NUMBER_DOUBLE", "BOOL", "$accept", - "pipeline", "stageList", "stage", + "pipeline", YY_NULLPTR}; +#endif -const unsigned char PipelineParserGen::yyrline_[] = {0, 130, 130, 135, 136, 144}; +#if YYDEBUG +const unsigned char PipelineParserGen::yyrline_[] = {0, 129, 129, 134, 135, 143}; -// Print the state stack on the debug stream. -void PipelineParserGen::yystack_print_() { +void PipelineParserGen::yy_stack_print_() const { *yycdebug_ << "Stack now"; for (stack_type::const_iterator i = yystack_.begin(), i_end = yystack_.end(); i != i_end; ++i) *yycdebug_ << ' ' << int(i->state); *yycdebug_ << '\n'; } -// Report on the debug stream that the rule \a yyrule is going to be reduced. -void PipelineParserGen::yy_reduce_print_(int yyrule) { +void PipelineParserGen::yy_reduce_print_(int yyrule) const { int yylno = yyrline_[yyrule]; int yynrhs = yyr2_[yyrule]; // Print the symbols being reduced, and their result. @@ -881,8 +877,8 @@ void PipelineParserGen::yy_reduce_print_(int yyrule) { #endif // YYDEBUG -#line 52 "src/mongo/db/cst/pipeline_grammar.yy" +#line 52 "pipeline_grammar.yy" } // namespace mongo -#line 1000 "src/mongo/db/cst/pipeline_parser_gen.cpp" +#line 1003 "pipeline_parser_gen.cpp" -#line 149 "src/mongo/db/cst/pipeline_grammar.yy" +#line 148 "pipeline_grammar.yy" diff --git a/src/mongo/db/cst/pipeline_parser_gen.hpp b/src/mongo/db/cst/pipeline_parser_gen.hpp index bc191a87041..c2f2e24a314 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.5.4. +// A Bison parser, made by GNU Bison 3.6.3. // Skeleton interface for Bison LALR(1) parsers in C++ @@ -32,19 +32,20 @@ /** - ** \file src/mongo/db/cst/pipeline_parser_gen.hpp + ** \file pipeline_parser_gen.hpp ** Define the mongo::parser class. */ // C++ LALR(1) parser skeleton written by Akim Demaille. -// Undocumented macros, especially those whose name start with YY_, -// are private implementation details. Do not rely on them. +// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, +// 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 60 "pipeline_grammar.yy" #include "mongo/db/cst/c_node.h" #include "mongo/db/cst/key_fieldname.h" @@ -60,7 +61,7 @@ class BSONLexer; #pragma warning(disable : 4065) #endif -#line 64 "src/mongo/db/cst/pipeline_parser_gen.hpp" +#line 65 "pipeline_parser_gen.hpp" #include <cassert> #include <cstdlib> // std::abort @@ -189,9 +190,9 @@ class BSONLexer; #define YYDEBUG 0 #endif -#line 52 "src/mongo/db/cst/pipeline_grammar.yy" +#line 52 "pipeline_grammar.yy" namespace mongo { -#line 199 "src/mongo/db/cst/pipeline_parser_gen.hpp" +#line 200 "pipeline_parser_gen.hpp" /// A Bison parser. @@ -218,6 +219,13 @@ public: new (yyas_<T>()) T(YY_MOVE(t)); } +#if 201103L <= YY_CPLUSPLUS + /// Non copyable. + semantic_type(const self_type&) = delete; + /// Non copyable. + self_type& operator=(const self_type&) = delete; +#endif + /// Destruction, allowed only if empty. ~semantic_type() YY_NOEXCEPT { YY_ASSERT(!yytypeid_); @@ -336,9 +344,12 @@ public: } private: - /// Prohibit blind copies. - self_type& operator=(const self_type&); +#if YY_CPLUSPLUS < 201103L + /// Non copyable. semantic_type(const self_type&); + /// Non copyable. + self_type& operator=(const self_type&); +#endif /// Accessor to raw memory as \a T. template <typename T> @@ -357,6 +368,7 @@ public: /// An auxiliary type to compute the largest semantic type. union union_type { // stageList + // stage char dummy1[sizeof(CNode)]; // BOOL @@ -371,11 +383,8 @@ public: // NUMBER_LONG char dummy5[sizeof(long long)]; - // stage - char dummy6[sizeof(std::pair<KeyFieldname, CNode>)]; - // STRING - char dummy7[sizeof(std::string)]; + char dummy6[sizeof(std::string)]; }; /// The size of the largest semantic type. @@ -411,41 +420,73 @@ public: location_type location; }; - /// Tokens. + /// Token kinds. struct token { - enum yytokentype { - END_OF_FILE = 0, - START_OBJECT = 3, - END_OBJECT = 4, - START_ORDERED_OBJECT = 5, - END_ORDERED_OBJECT = 6, - START_ARRAY = 7, - END_ARRAY = 8, - STAGE_INHIBIT_OPTIMIZATION = 9, - STRING = 10, - NUMBER_INT = 11, - NUMBER_LONG = 12, - NUMBER_DOUBLE = 13, - BOOL = 14 + 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_ORDERED_OBJECT = 5, // START_ORDERED_OBJECT + END_ORDERED_OBJECT = 6, // END_ORDERED_OBJECT + START_ARRAY = 7, // START_ARRAY + END_ARRAY = 8, // END_ARRAY + STAGE_INHIBIT_OPTIMIZATION = 9, // STAGE_INHIBIT_OPTIMIZATION + STRING = 10, // STRING + NUMBER_INT = 11, // NUMBER_INT + NUMBER_LONG = 12, // NUMBER_LONG + NUMBER_DOUBLE = 13, // NUMBER_DOUBLE + BOOL = 14 // BOOL }; + /// Backward compatibility alias (Bison 3.6). + typedef token_kind_type yytokentype; }; - /// (External) token type, as returned by yylex. - typedef token::yytokentype token_type; - - /// Symbol type: an internal symbol number. - typedef int symbol_number_type; + /// Token kind, as returned by yylex. + typedef token::yytokentype token_kind_type; + + /// Backward compatibility alias (Bison 3.6). + typedef token_kind_type token_type; + + /// Symbol kinds. + struct symbol_kind { + enum symbol_kind_type { + YYNTOKENS = 15, ///< 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_ORDERED_OBJECT = 5, // START_ORDERED_OBJECT + S_END_ORDERED_OBJECT = 6, // END_ORDERED_OBJECT + S_START_ARRAY = 7, // START_ARRAY + S_END_ARRAY = 8, // END_ARRAY + S_STAGE_INHIBIT_OPTIMIZATION = 9, // STAGE_INHIBIT_OPTIMIZATION + S_STRING = 10, // STRING + S_NUMBER_INT = 11, // NUMBER_INT + S_NUMBER_LONG = 12, // NUMBER_LONG + S_NUMBER_DOUBLE = 13, // NUMBER_DOUBLE + S_BOOL = 14, // BOOL + S_YYACCEPT = 15, // $accept + S_stageList = 16, // stageList + S_stage = 17, // stage + S_pipeline = 18 // pipeline + }; + }; - /// The symbol type number to denote an empty symbol. - enum { empty_symbol = -2 }; + /// (Internal) symbol kind. + typedef symbol_kind::symbol_kind_type symbol_kind_type; - /// Internal symbol number for tokens (subsumed by symbol_number_type). - typedef signed char token_number_type; + /// The number of tokens. + static const symbol_kind_type YYNTOKENS = symbol_kind::YYNTOKENS; /// A complete symbol. /// - /// Expects its Base type to provide access to the symbol type - /// via type_get (). + /// Expects its Base type to provide access to the symbol kind + /// via kind (). /// /// Provide access to semantic value and location. template <typename Base> @@ -458,7 +499,38 @@ public: #if 201103L <= YY_CPLUSPLUS /// Move constructor. - basic_symbol(basic_symbol&& that); + basic_symbol(basic_symbol&& that) + : Base(std::move(that)), value(), location(std::move(that.location)) { + switch (this->kind()) { + case 16: // stageList + case 17: // stage + value.move<CNode>(std::move(that.value)); + break; + + case 14: // BOOL + value.move<bool>(std::move(that.value)); + break; + + case 13: // NUMBER_DOUBLE + value.move<double>(std::move(that.value)); + break; + + case 11: // NUMBER_INT + value.move<int>(std::move(that.value)); + break; + + case 12: // NUMBER_LONG + value.move<long long>(std::move(that.value)); + break; + + case 10: // STRING + value.move<std::string>(std::move(that.value)); + break; + + default: + break; + } + } #endif /// Copy constructor. @@ -507,17 +579,6 @@ public: : Base(t), value(v), location(l) {} #endif #if 201103L <= YY_CPLUSPLUS - basic_symbol(typename Base::kind_type t, - std::pair<KeyFieldname, 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<KeyFieldname, 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 @@ -533,17 +594,18 @@ public: /// Destroy contents, and record that is empty. void clear() { // User destructor. - symbol_number_type yytype = this->type_get(); + symbol_kind_type yykind = this->kind(); basic_symbol<Base>& yysym = *this; (void)yysym; - switch (yytype) { + switch (yykind) { default: break; } - // Type destructor. - switch (yytype) { - case 17: // stageList + // Value type destructor. + switch (yykind) { + case 16: // stageList + case 17: // stage value.template destroy<CNode>(); break; @@ -563,10 +625,6 @@ public: value.template destroy<long long>(); break; - case 18: // stage - value.template destroy<std::pair<KeyFieldname, CNode>>(); - break; - case 10: // STRING value.template destroy<std::string>(); break; @@ -578,6 +636,17 @@ 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; + /// Whether empty. bool empty() const YY_NOEXCEPT; @@ -598,44 +667,49 @@ public: }; /// Type access provider for token (enum) based symbols. - struct by_type { + struct by_kind { /// Default constructor. - by_type(); + by_kind(); #if 201103L <= YY_CPLUSPLUS /// Move constructor. - by_type(by_type&& that); + by_kind(by_kind&& that); #endif /// Copy constructor. - by_type(const by_type& that); + by_kind(const by_kind& that); - /// The symbol type as needed by the constructor. - typedef token_type kind_type; + /// The symbol kind as needed by the constructor. + typedef token_kind_type kind_type; /// Constructor from (external) token numbers. - by_type(kind_type t); + by_kind(kind_type t); /// Record that this symbol is empty. void clear(); - /// Steal the symbol type from \a that. - void move(by_type& that); + /// Steal the symbol kind from \a that. + void move(by_kind& that); /// The (internal) type number (corresponding to \a type). /// \a empty when empty. - symbol_number_type type_get() const YY_NOEXCEPT; + symbol_kind_type kind() const YY_NOEXCEPT; - /// The symbol type. - /// \a empty_symbol when empty. - /// An int, not token_number_type, to be able to store empty_symbol. - int type; + /// Backward compatibility (Bison 3.6). + symbol_kind_type type_get() const YY_NOEXCEPT; + + /// The symbol kind. + /// \a S_YYEMPTY when empty. + symbol_kind_type kind_; }; + /// Backward compatibility for a private implementation detail (Bison 3.6). + typedef by_kind by_type; + /// "External" symbols: returned by the scanner. - struct symbol_type : basic_symbol<by_type> { + struct symbol_type : basic_symbol<by_kind> { /// Superclass. - typedef basic_symbol<by_type> super_type; + typedef basic_symbol<by_kind> super_type; /// Empty symbol. symbol_type() {} @@ -643,17 +717,19 @@ public: /// Constructor for valueless symbols, and symbols from each type. #if 201103L <= YY_CPLUSPLUS symbol_type(int tok, location_type l) : super_type(token_type(tok), std::move(l)) { - YY_ASSERT(tok == token::END_OF_FILE || tok == token::START_OBJECT || - tok == token::END_OBJECT || tok == token::START_ORDERED_OBJECT || - tok == token::END_ORDERED_OBJECT || tok == token::START_ARRAY || - tok == token::END_ARRAY || tok == token::STAGE_INHIBIT_OPTIMIZATION); + YY_ASSERT(tok == token::END_OF_FILE || tok == token::YYerror || tok == token::YYUNDEF || + tok == token::START_OBJECT || tok == token::END_OBJECT || + tok == token::START_ORDERED_OBJECT || tok == token::END_ORDERED_OBJECT || + tok == token::START_ARRAY || tok == token::END_ARRAY || + tok == token::STAGE_INHIBIT_OPTIMIZATION); } #else symbol_type(int tok, const location_type& l) : super_type(token_type(tok), l) { - YY_ASSERT(tok == token::END_OF_FILE || tok == token::START_OBJECT || - tok == token::END_OBJECT || tok == token::START_ORDERED_OBJECT || - tok == token::END_ORDERED_OBJECT || tok == token::START_ARRAY || - tok == token::END_ARRAY || tok == token::STAGE_INHIBIT_OPTIMIZATION); + YY_ASSERT(tok == token::END_OF_FILE || tok == token::YYerror || tok == token::YYUNDEF || + tok == token::START_OBJECT || tok == token::END_OBJECT || + tok == token::START_ORDERED_OBJECT || tok == token::END_ORDERED_OBJECT || + tok == token::START_ARRAY || tok == token::END_ARRAY || + tok == token::STAGE_INHIBIT_OPTIMIZATION); } #endif #if 201103L <= YY_CPLUSPLUS @@ -717,6 +793,13 @@ public: PipelineParserGen(BSONLexer& driver_yyarg, CNode* cst_yyarg); virtual ~PipelineParserGen(); +#if 201103L <= YY_CPLUSPLUS + /// Non copyable. + PipelineParserGen(const PipelineParserGen&) = delete; + /// Non copyable. + PipelineParserGen& operator=(const PipelineParserGen&) = delete; +#endif + /// Parse. An alias for parse (). /// \returns 0 iff parsing succeeded. int operator()(); @@ -747,6 +830,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) { @@ -758,6 +848,24 @@ public: } #endif #if 201103L <= YY_CPLUSPLUS + static symbol_type make_YYerror(location_type l) { + return symbol_type(token::YYerror, std::move(l)); + } +#else + static symbol_type make_YYerror(const location_type& l) { + return symbol_type(token::YYerror, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static symbol_type make_YYUNDEF(location_type l) { + return symbol_type(token::YYUNDEF, std::move(l)); + } +#else + static symbol_type make_YYUNDEF(const location_type& l) { + return symbol_type(token::YYUNDEF, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS static symbol_type make_START_OBJECT(location_type l) { return symbol_type(token::START_OBJECT, std::move(l)); } @@ -868,18 +976,17 @@ public: private: - /// This class is not copyable. +#if YY_CPLUSPLUS < 201103L + /// Non copyable. PipelineParserGen(const PipelineParserGen&); + /// Non copyable. PipelineParserGen& operator=(const PipelineParserGen&); +#endif + /// Stored state numbers (used for stacks). typedef signed char state_type; - /// Generate an error message. - /// \param yystate the state where the error occurred. - /// \param yyla the lookahead token. - virtual std::string yysyntax_error_(state_type yystate, const symbol_type& yyla) const; - /// Compute post-reduction state. /// \param yystate the current state /// \param yysym the nonterminal to push on the stack @@ -896,10 +1003,16 @@ private: static const signed char yypact_ninf_; static const signed char yytable_ninf_; - /// Convert a scanner token number \a t to a symbol number. - /// In theory \a t should be a token_type, but character literals + /// Convert a scanner token kind \a t to a symbol kind. + /// In theory \a t should be a token_kind_type, but character literals /// are valid, yet not members of the token_type enum. - static token_number_type yytranslate_(int t); + static symbol_kind_type yytranslate_(int t); + +#if YYDEBUG || 0 + /// For a symbol, its name in clear. + static const char* const yytname_[]; +#endif // #if YYDEBUG || 0 + // Tables. // YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing @@ -936,22 +1049,19 @@ private: #if YYDEBUG - /// For a symbol, its name in clear. - static const char* const yytname_[]; - // YYRLINE[YYN] -- Source line where rule number YYN was defined. static const unsigned char yyrline_[]; /// Report on the debug stream that the rule \a r is going to be reduced. - virtual void yy_reduce_print_(int r); + virtual void yy_reduce_print_(int r) const; /// Print the state stack on the debug stream. - virtual void yystack_print_(); + virtual void yy_stack_print_() const; /// Debugging level. int yydebug_; /// Debug stream. std::ostream* yycdebug_; - /// \brief Display a symbol type, value and location. + /// \brief Display a symbol kind, value and location. /// \param yyo The output stream. /// \param yysym The symbol. template <typename Base> @@ -971,7 +1081,7 @@ private: /// Default constructor. by_state() YY_NOEXCEPT; - /// The symbol type as needed by the constructor. + /// The symbol kind as needed by the constructor. typedef state_type kind_type; /// Constructor. @@ -983,12 +1093,12 @@ private: /// Record that this symbol is empty. void clear() YY_NOEXCEPT; - /// Steal the symbol type from \a that. + /// Steal the symbol kind from \a that. void move(by_state& that); - /// The (internal) type number (corresponding to \a state). - /// \a empty_symbol when empty. - symbol_number_type type_get() const YY_NOEXCEPT; + /// The symbol kind (corresponding to \a state). + /// \a S_YYEMPTY when empty. + symbol_kind_type kind() const YY_NOEXCEPT; /// The state number used to denote an empty symbol. /// We use the initial state, as it does not have a value. @@ -1025,13 +1135,20 @@ private: class stack { public: // Hide our reversed order. - typedef typename S::reverse_iterator iterator; - typedef typename S::const_reverse_iterator const_iterator; + typedef typename S::iterator iterator; + typedef typename S::const_iterator const_iterator; typedef typename S::size_type size_type; typedef typename std::ptrdiff_t index_type; stack(size_type n = 200) : seq_(n) {} +#if 201103L <= YY_CPLUSPLUS + /// Non copyable. + stack(const stack&) = delete; + /// Non copyable. + stack& operator=(const stack&) = delete; +#endif + /// Random access. /// /// Index 0 returns the topmost element. @@ -1070,18 +1187,14 @@ private: return index_type(seq_.size()); } - std::ptrdiff_t ssize() const YY_NOEXCEPT { - return std::ptrdiff_t(size()); - } - /// Iterator on top of the stack (going downwards). const_iterator begin() const YY_NOEXCEPT { - return seq_.rbegin(); + return seq_.begin(); } /// Bottom of the stack. const_iterator end() const YY_NOEXCEPT { - return seq_.rend(); + return seq_.end(); } /// Present a slice of the top of a stack. @@ -1099,8 +1212,12 @@ private: }; private: +#if YY_CPLUSPLUS < 201103L + /// Non copyable. stack(const stack&); + /// Non copyable. stack& operator=(const stack&); +#endif /// The wrapped container. S seq_; }; @@ -1130,17 +1247,11 @@ private: /// Pop \a n symbols from the stack. void yypop_(int n = 1); - /// Some specific tokens. - static const token_number_type yy_error_token_ = 1; - static const token_number_type yy_undef_token_ = 2; - /// Constants. enum { - yyeof_ = 0, - yylast_ = 8, ///< Last index in yytable_. - yynnts_ = 4, ///< Number of nonterminal symbols. - yyfinal_ = 5, ///< Termination state number. - yyntokens_ = 15 ///< Number of tokens. + yylast_ = 8, ///< Last index in yytable_. + yynnts_ = 4, ///< Number of nonterminal symbols. + yyfinal_ = 5 ///< Termination state number. }; @@ -1149,55 +1260,17 @@ private: CNode* cst; }; -inline PipelineParserGen::token_number_type PipelineParserGen::yytranslate_(int t) { - return static_cast<token_number_type>(t); +inline PipelineParserGen::symbol_kind_type PipelineParserGen::yytranslate_(int t) { + return static_cast<symbol_kind_type>(t); } // basic_symbol. -#if 201103L <= YY_CPLUSPLUS -template <typename Base> -PipelineParserGen::basic_symbol<Base>::basic_symbol(basic_symbol&& that) - : Base(std::move(that)), value(), location(std::move(that.location)) { - switch (this->type_get()) { - case 17: // stageList - value.move<CNode>(std::move(that.value)); - break; - - case 14: // BOOL - value.move<bool>(std::move(that.value)); - break; - - case 13: // NUMBER_DOUBLE - value.move<double>(std::move(that.value)); - break; - - case 11: // NUMBER_INT - value.move<int>(std::move(that.value)); - break; - - case 12: // NUMBER_LONG - value.move<long long>(std::move(that.value)); - break; - - case 18: // stage - value.move<std::pair<KeyFieldname, CNode>>(std::move(that.value)); - break; - - case 10: // STRING - value.move<std::string>(std::move(that.value)); - break; - - default: - break; - } -} -#endif - template <typename Base> PipelineParserGen::basic_symbol<Base>::basic_symbol(const basic_symbol& that) : Base(that), value(), location(that.location) { - switch (this->type_get()) { - case 17: // stageList + switch (this->kind()) { + case 16: // stageList + case 17: // stage value.copy<CNode>(YY_MOVE(that.value)); break; @@ -1217,10 +1290,6 @@ PipelineParserGen::basic_symbol<Base>::basic_symbol(const basic_symbol& that) value.copy<long long>(YY_MOVE(that.value)); break; - case 18: // stage - value.copy<std::pair<KeyFieldname, CNode>>(YY_MOVE(that.value)); - break; - case 10: // STRING value.copy<std::string>(YY_MOVE(that.value)); break; @@ -1232,15 +1301,22 @@ PipelineParserGen::basic_symbol<Base>::basic_symbol(const basic_symbol& that) template <typename Base> +PipelineParserGen::symbol_kind_type PipelineParserGen::basic_symbol<Base>::type_get() const + YY_NOEXCEPT { + return this->kind(); +} + +template <typename Base> bool PipelineParserGen::basic_symbol<Base>::empty() const YY_NOEXCEPT { - return Base::type_get() == empty_symbol; + return this->kind() == symbol_kind::S_YYEMPTY; } template <typename Base> void PipelineParserGen::basic_symbol<Base>::move(basic_symbol& s) { super_type::move(s); - switch (this->type_get()) { - case 17: // stageList + switch (this->kind()) { + case 16: // stageList + case 17: // stage value.move<CNode>(YY_MOVE(s.value)); break; @@ -1260,10 +1336,6 @@ void PipelineParserGen::basic_symbol<Base>::move(basic_symbol& s) { value.move<long long>(YY_MOVE(s.value)); break; - case 18: // stage - value.move<std::pair<KeyFieldname, CNode>>(YY_MOVE(s.value)); - break; - case 10: // STRING value.move<std::string>(YY_MOVE(s.value)); break; @@ -1275,35 +1347,40 @@ void PipelineParserGen::basic_symbol<Base>::move(basic_symbol& s) { location = YY_MOVE(s.location); } -// by_type. -inline PipelineParserGen::by_type::by_type() : type(empty_symbol) {} +// by_kind. +inline PipelineParserGen::by_kind::by_kind() : kind_(symbol_kind::S_YYEMPTY) {} #if 201103L <= YY_CPLUSPLUS -inline PipelineParserGen::by_type::by_type(by_type&& that) : type(that.type) { +inline PipelineParserGen::by_kind::by_kind(by_kind&& that) : kind_(that.kind_) { that.clear(); } #endif -inline PipelineParserGen::by_type::by_type(const by_type& that) : type(that.type) {} +inline PipelineParserGen::by_kind::by_kind(const by_kind& that) : kind_(that.kind_) {} -inline PipelineParserGen::by_type::by_type(token_type t) : type(yytranslate_(t)) {} +inline PipelineParserGen::by_kind::by_kind(token_kind_type t) : kind_(yytranslate_(t)) {} -inline void PipelineParserGen::by_type::clear() { - type = empty_symbol; +inline void PipelineParserGen::by_kind::clear() { + kind_ = symbol_kind::S_YYEMPTY; } -inline void PipelineParserGen::by_type::move(by_type& that) { - type = that.type; +inline void PipelineParserGen::by_kind::move(by_kind& that) { + kind_ = that.kind_; that.clear(); } -inline int PipelineParserGen::by_type::type_get() const YY_NOEXCEPT { - return type; +inline PipelineParserGen::symbol_kind_type PipelineParserGen::by_kind::kind() const YY_NOEXCEPT { + return kind_; +} + +inline PipelineParserGen::symbol_kind_type PipelineParserGen::by_kind::type_get() const + YY_NOEXCEPT { + return this->kind(); } -#line 52 "src/mongo/db/cst/pipeline_grammar.yy" +#line 52 "pipeline_grammar.yy" } // namespace mongo -#line 1569 "src/mongo/db/cst/pipeline_parser_gen.hpp" +#line 1658 "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/document_source_project.cpp b/src/mongo/db/pipeline/document_source_project.cpp index 657350dc656..e5f048a8e3d 100644 --- a/src/mongo/db/pipeline/document_source_project.cpp +++ b/src/mongo/db/pipeline/document_source_project.cpp @@ -37,7 +37,6 @@ #include "mongo/db/exec/projection_executor.h" #include "mongo/db/exec/projection_executor_builder.h" #include "mongo/db/pipeline/lite_parsed_document_source.h" -#include "mongo/db/query/projection_parser.h" namespace mongo { @@ -62,7 +61,9 @@ BSONObj buildExclusionProjectionSpecification(const std::vector<BSONElement>& un } // namespace intrusive_ptr<DocumentSource> DocumentSourceProject::create( - BSONObj projectSpec, const intrusive_ptr<ExpressionContext>& expCtx, StringData specifiedName) { + projection_ast::Projection projection, + const intrusive_ptr<ExpressionContext>& expCtx, + StringData specifiedName) { const bool isIndependentOfAnyCollection = false; intrusive_ptr<DocumentSource> project(new DocumentSourceSingleDocumentTransformation( expCtx, @@ -72,8 +73,6 @@ intrusive_ptr<DocumentSource> DocumentSourceProject::create( // here so we can add the name that was actually specified by the user, be it $project // or an alias. try { - auto policies = ProjectionPolicies::aggregateProjectionPolicies(); - auto projection = projection_ast::parse(expCtx, projectSpec, policies); // We won't optimize the executor on creation, and will do it as part of the // pipeline optimization process when requested via the 'optimize()' method on // 'DocumentSourceSingleDocumentTransformation', so we won't pass the @@ -87,7 +86,10 @@ intrusive_ptr<DocumentSource> DocumentSourceProject::create( projection_executor::kDefaultBuilderParams}; builderParams.reset(projection_executor::kOptimizeExecutor); return projection_executor::buildProjectionExecutor( - expCtx, &projection, policies, builderParams); + expCtx, + &projection, + ProjectionPolicies::aggregateProjectionPolicies(), + builderParams); } catch (DBException& ex) { ex.addContext("Invalid " + specifiedName.toString()); throw; diff --git a/src/mongo/db/pipeline/document_source_project.h b/src/mongo/db/pipeline/document_source_project.h index 43ee90fce0f..13d45282a75 100644 --- a/src/mongo/db/pipeline/document_source_project.h +++ b/src/mongo/db/pipeline/document_source_project.h @@ -30,6 +30,7 @@ #pragma once #include "mongo/db/pipeline/document_source_single_document_transformation.h" +#include "mongo/db/query/projection_parser.h" namespace mongo { @@ -45,12 +46,28 @@ public: static constexpr StringData kAliasNameUnset = "$unset"_sd; /** + * Method to create a $project stage from a Projection AST. + */ + static boost::intrusive_ptr<DocumentSource> create( + projection_ast::Projection projection, + const boost::intrusive_ptr<ExpressionContext>& expCtx, + StringData specifiedName); + + /** * Convenience method to create a $project stage from 'projectSpec'. */ static boost::intrusive_ptr<DocumentSource> create( BSONObj projectSpec, const boost::intrusive_ptr<ExpressionContext>& expCtx, - StringData specifiedName); + StringData specifiedName) try { + return create(projection_ast::parse( + expCtx, projectSpec, ProjectionPolicies::aggregateProjectionPolicies()), + expCtx, + specifiedName); + } catch (DBException& ex) { + ex.addContext("Invalid " + specifiedName.toString()); + throw; + } /** * Parses a $project stage from the user-supplied BSON. diff --git a/src/mongo/db/query/projection_parser.cpp b/src/mongo/db/query/projection_parser.cpp index 54f050b7353..bc0f6944dd4 100644 --- a/src/mongo/db/query/projection_parser.cpp +++ b/src/mongo/db/query/projection_parser.cpp @@ -186,12 +186,6 @@ void addNodeAtPathHelper(ProjectionPathASTNode* root, addNodeAtPathHelper(childPathNode, path, componentIndex + 1, std::move(newChild)); } -void addNodeAtPath(ProjectionPathASTNode* root, - const FieldPath& path, - std::unique_ptr<ASTNode> newChild) { - addNodeAtPathHelper(root, path, 0, std::move(newChild)); -} - bool hasPositionalOperator(StringData path) { return path.endsWith(".$"); } @@ -641,5 +635,12 @@ Projection parse(boost::intrusive_ptr<ExpressionContext> expCtx, ProjectionPolicies policies) { return parse(std::move(expCtx), obj, nullptr, BSONObj(), std::move(policies)); } + +void addNodeAtPath(ProjectionPathASTNode* root, + const FieldPath& path, + std::unique_ptr<ASTNode> newChild) { + addNodeAtPathHelper(root, path, 0, std::move(newChild)); +} + } // namespace projection_ast } // namespace mongo diff --git a/src/mongo/db/query/projection_parser.h b/src/mongo/db/query/projection_parser.h index bee70317f9b..11d36846e72 100644 --- a/src/mongo/db/query/projection_parser.h +++ b/src/mongo/db/query/projection_parser.h @@ -53,5 +53,13 @@ Projection parse(boost::intrusive_ptr<ExpressionContext> expCtx, Projection parse(boost::intrusive_ptr<ExpressionContext> expCtx, const BSONObj& obj, ProjectionPolicies policies); + +/** + * Adds a node to the projection AST rooted at 'root' to the path specified by 'path'. + */ +void addNodeAtPath(ProjectionPathASTNode* root, + const FieldPath& path, + std::unique_ptr<ASTNode> newChild); + } // namespace projection_ast } // namespace mongo |