summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Evans <jacob.evans@10gen.com>2020-07-07 17:39:11 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-07-13 23:46:06 +0000
commit60e04b716c909497ab91f6bbadf332c36f007446 (patch)
treee8fd6ed5bae78c288d9ef94b6bd6e1939283d6bd
parentc31cac2855eb84c9bc824852773265c8e0b60a4e (diff)
downloadmongo-60e04b716c909497ab91f6bbadf332c36f007446.tar.gz
SERVER-49043 Begin CST -> Pipeline translation
-rw-r--r--src/mongo/db/cst/SConscript13
-rw-r--r--src/mongo/db/cst/c_node.cpp208
-rw-r--r--src/mongo/db/cst/c_node.h81
-rw-r--r--src/mongo/db/cst/cst_pipeline_translation.cpp110
-rw-r--r--src/mongo/db/cst/cst_pipeline_translation.h46
-rw-r--r--src/mongo/db/cst/cst_pipeline_translation_test.cpp170
-rw-r--r--src/mongo/db/cst/cst_test.cpp34
-rw-r--r--src/mongo/db/cst/key_fieldname.h1
-rw-r--r--src/mongo/db/cst/location_gen.h18
-rw-r--r--src/mongo/db/cst/pipeline_grammar.yy7
-rw-r--r--src/mongo/db/cst/pipeline_parser_gen.cpp228
-rw-r--r--src/mongo/db/cst/pipeline_parser_gen.hpp461
-rw-r--r--src/mongo/db/pipeline/document_source_project.cpp12
-rw-r--r--src/mongo/db/pipeline/document_source_project.h19
-rw-r--r--src/mongo/db/query/projection_parser.cpp13
-rw-r--r--src/mongo/db/query/projection_parser.h8
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