diff options
Diffstat (limited to 'src/mongo/db/query')
-rw-r--r-- | src/mongo/db/query/canonical_query.h | 5 | ||||
-rw-r--r-- | src/mongo/db/query/get_executor.cpp | 19 | ||||
-rw-r--r-- | src/mongo/db/query/projection.cpp | 38 | ||||
-rw-r--r-- | src/mongo/db/query/projection.h | 19 | ||||
-rw-r--r-- | src/mongo/db/query/projection_ast.h | 45 | ||||
-rw-r--r-- | src/mongo/db/query/projection_ast_path_tracking_visitor.h | 56 | ||||
-rw-r--r-- | src/mongo/db/query/projection_ast_util.cpp | 47 | ||||
-rw-r--r-- | src/mongo/db/query/projection_ast_util.h | 2 | ||||
-rw-r--r-- | src/mongo/db/query/projection_ast_visitor.h | 29 | ||||
-rw-r--r-- | src/mongo/db/query/projection_ast_walker.h | 9 | ||||
-rw-r--r-- | src/mongo/db/query/projection_parser.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/query/projection_test.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/query/query_planner_test_lib.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/query/query_solution.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/query/stage_builder.cpp | 18 |
15 files changed, 178 insertions, 139 deletions
diff --git a/src/mongo/db/query/canonical_query.h b/src/mongo/db/query/canonical_query.h index 39ed4e927f5..317544c4944 100644 --- a/src/mongo/db/query/canonical_query.h +++ b/src/mongo/db/query/canonical_query.h @@ -128,6 +128,11 @@ public: const projection_ast::Projection* getProj() const { return _proj.get_ptr(); } + + projection_ast::Projection* getProj() { + return _proj.get_ptr(); + } + const CollatorInterface* getCollator() const { return _collator.get(); } diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp index 29cc73c097a..dec282568dc 100644 --- a/src/mongo/db/query/get_executor.cpp +++ b/src/mongo/db/query/get_executor.cpp @@ -417,15 +417,14 @@ StatusWith<PrepareExecutionResult> prepareExecution(OperationContext* opCtx, // Stuff the right data into the params depending on what proj impl we use. if (!canonicalQuery->getProj()->isSimple()) { root = std::make_unique<ProjectionStageDefault>( - opCtx, - canonicalQuery->getProj()->getProjObj(), + canonicalQuery->getExpCtx(), + canonicalQuery->getQueryRequest().getProj(), + canonicalQuery->getProj(), ws, - std::move(root), - *canonicalQuery->root(), - canonicalQuery->getCollator()); + std::move(root)); } else { root = std::make_unique<ProjectionStageSimple>( - opCtx, canonicalQuery->getProj()->getProjObj(), ws, std::move(root)); + opCtx, canonicalQuery->getQueryRequest().getProj(), ws, std::move(root)); } } @@ -683,12 +682,8 @@ StatusWith<unique_ptr<PlanStage>> applyProjection(OperationContext* opCtx, "Cannot use a $meta sortKey projection in findAndModify commands."}; } - return {std::make_unique<ProjectionStageDefault>(opCtx, - projObj, - ws, - std::unique_ptr<PlanStage>(root.release()), - *cq->root(), - cq->getCollator())}; + return {std::make_unique<ProjectionStageDefault>( + cq->getExpCtx(), projObj, &proj, ws, std::unique_ptr<PlanStage>(root.release()))}; } } // namespace diff --git a/src/mongo/db/query/projection.cpp b/src/mongo/db/query/projection.cpp index d2a4ee89264..cd6f83091b2 100644 --- a/src/mongo/db/query/projection.cpp +++ b/src/mongo/db/query/projection.cpp @@ -58,32 +58,32 @@ struct DepsAnalysisData { * Does "broad" analysis on the projection, about whether the entire document, or details from the * match expression are needed and so on. */ -class ProjectionAnalysisVisitor final : public ProjectionASTVisitor { +class ProjectionAnalysisVisitor final : public ProjectionASTConstVisitor { public: ProjectionAnalysisVisitor(ProjectionDependencies* deps) : _deps(deps) { invariant(_deps); } - void visit(ProjectionPathASTNode* node) final { + void visit(const ProjectionPathASTNode* node) final { if (node->parent()) { _deps->hasDottedPath = true; } } - void visit(ProjectionPositionalASTNode* node) final { + void visit(const ProjectionPositionalASTNode* node) final { _deps->requiresMatchDetails = true; _deps->requiresDocument = true; } - void visit(ProjectionSliceASTNode* node) final { + void visit(const ProjectionSliceASTNode* node) final { _deps->requiresDocument = true; } - void visit(ProjectionElemMatchASTNode* node) final { + void visit(const ProjectionElemMatchASTNode* node) final { _deps->requiresDocument = true; } - void visit(ExpressionASTNode* node) final { + void visit(const ExpressionASTNode* node) final { const Expression* expr = node->expressionRaw(); const ExpressionMeta* meta = dynamic_cast<const ExpressionMeta*>(expr); @@ -94,8 +94,8 @@ public: } } - void visit(BooleanConstantASTNode* node) final {} - void visit(MatchExpressionASTNode* node) final {} + void visit(const BooleanConstantASTNode* node) final {} + void visit(const MatchExpressionASTNode* node) final {} private: ProjectionDependencies* _deps; @@ -108,33 +108,33 @@ private: * 'PathTrackingWalker' which will help to maintain the current path via * 'PathTrackingVisitorContext'. */ -class DepsAnalysisVisitor final : public ProjectionASTVisitor { +class DepsAnalysisVisitor final : public ProjectionASTConstVisitor { public: DepsAnalysisVisitor(PathTrackingVisitorContext<DepsAnalysisData>* context) : _context{context} { invariant(_context); } - void visit(MatchExpressionASTNode* node) final { + void visit(const MatchExpressionASTNode* node) final { node->matchExpression()->addDependencies(&_context->data().fieldDependencyTracker); } - void visit(ProjectionPositionalASTNode* node) final { + void visit(const ProjectionPositionalASTNode* node) final { // Positional projection on a.b.c.$ may actually modify a, a.b, a.b.c, etc. // Treat the top-level field as a dependency. addTopLevelPathAsDependency(); } - void visit(ProjectionSliceASTNode* node) final { + void visit(const ProjectionSliceASTNode* node) final { // find() $slice on a.b.c may modify a, a.b, and a.b.c if they're all arrays. // Treat the top-level field as a dependency. addTopLevelPathAsDependency(); } - void visit(ProjectionElemMatchASTNode* node) final { + void visit(const ProjectionElemMatchASTNode* node) final { addFullPathAsDependency(); } - void visit(ExpressionASTNode* node) final { + void visit(const ExpressionASTNode* node) final { // The output of an expression on a dotted path depends on whether that field is an array. invariant(node->parent()); if (!node->parent()->isRoot()) { @@ -144,14 +144,14 @@ public: node->expression()->addDependencies(&_context->data().fieldDependencyTracker); } - void visit(BooleanConstantASTNode* node) final { + void visit(const BooleanConstantASTNode* node) final { // For inclusions, we depend on the field. if (node->value()) { addFullPathAsDependency(); } } - void visit(ProjectionPathASTNode* node) final {} + void visit(const ProjectionPathASTNode* node) final {} private: void addTopLevelPathAsDependency() { @@ -169,7 +169,7 @@ private: PathTrackingVisitorContext<DepsAnalysisData>* _context; }; -auto analyzeProjection(ProjectionPathASTNode* root, ProjectType type) { +auto analyzeProjection(const ProjectionPathASTNode* root, ProjectType type) { ProjectionDependencies deps; PathTrackingVisitorContext<DepsAnalysisData> context; DepsAnalysisVisitor depsAnalysisVisitor{&context}; @@ -194,8 +194,8 @@ auto analyzeProjection(ProjectionPathASTNode* root, ProjectType type) { } // namespace -Projection::Projection(ProjectionPathASTNode root, ProjectType type, const BSONObj& bson) - : _root(std::move(root)), _type(type), _deps(analyzeProjection(&_root, type)), _bson(bson) {} +Projection::Projection(ProjectionPathASTNode root, ProjectType type) + : _root(std::move(root)), _type(type), _deps(analyzeProjection(&_root, type)) {} namespace { diff --git a/src/mongo/db/query/projection.h b/src/mongo/db/query/projection.h index 91afe2b931f..486f9ed9abf 100644 --- a/src/mongo/db/query/projection.h +++ b/src/mongo/db/query/projection.h @@ -59,7 +59,11 @@ struct ProjectionDependencies { enum class ProjectType { kInclusion, kExclusion }; class Projection { public: - Projection(ProjectionPathASTNode root, ProjectType type, const BSONObj& bson); + Projection(ProjectionPathASTNode root, ProjectType type); + + const ProjectionPathASTNode* root() const { + return &_root; + } ProjectionPathASTNode* root() { return &_root; @@ -105,15 +109,6 @@ public: bool isFieldRetainedExactly(StringData path); /** - * TODO SERVER-42423: Delete this method and _bson. - * - * This method is deprecated and new call sites should not be added. - */ - BSONObj getProjObj() const { - return _bson; - } - - /** * A projection is considered "simple" if it doesn't require the full document, operates only * on top-level fields, has no positional projection, and doesn't require the sort key. */ @@ -125,11 +120,7 @@ public: private: ProjectionPathASTNode _root; ProjectType _type; - ProjectionDependencies _deps; - - // Do NOT add new usages of this. - BSONObj _bson; }; } // namespace projection_ast diff --git a/src/mongo/db/query/projection_ast.h b/src/mongo/db/query/projection_ast.h index 2c136ba0a00..8a279fbbf8c 100644 --- a/src/mongo/db/query/projection_ast.h +++ b/src/mongo/db/query/projection_ast.h @@ -67,7 +67,8 @@ public: virtual std::unique_ptr<ASTNode> clone() const = 0; - virtual void acceptVisitor(ProjectionASTVisitor* visitor) = 0; + virtual void acceptVisitor(ProjectionASTMutableVisitor* visitor) = 0; + virtual void acceptVisitor(ProjectionASTConstVisitor* visitor) const = 0; const ASTNodeVector& children() const { return _children; @@ -103,7 +104,11 @@ public: return std::make_unique<MatchExpressionASTNode>(*this); } - void acceptVisitor(ProjectionASTVisitor* visitor) override { + void acceptVisitor(ProjectionASTMutableVisitor* visitor) override { + visitor->visit(this); + } + + void acceptVisitor(ProjectionASTConstVisitor* visitor) const override { visitor->visit(this); } @@ -124,7 +129,11 @@ public: invariant(_children.size() == _fieldNames.size()); } - void acceptVisitor(ProjectionASTVisitor* visitor) override { + void acceptVisitor(ProjectionASTMutableVisitor* visitor) override { + visitor->visit(this); + } + + void acceptVisitor(ProjectionASTConstVisitor* visitor) const override { visitor->visit(this); } @@ -163,7 +172,11 @@ public: addChildToInternalVector(std::move(child)); } - void acceptVisitor(ProjectionASTVisitor* visitor) override { + void acceptVisitor(ProjectionASTMutableVisitor* visitor) override { + visitor->visit(this); + } + + void acceptVisitor(ProjectionASTConstVisitor* visitor) const override { visitor->visit(this); } @@ -176,7 +189,11 @@ class ProjectionSliceASTNode final : public ASTNode { public: ProjectionSliceASTNode(boost::optional<int> skip, int limit) : _skip(skip), _limit(limit) {} - void acceptVisitor(ProjectionASTVisitor* visitor) override { + void acceptVisitor(ProjectionASTMutableVisitor* visitor) override { + visitor->visit(this); + } + + void acceptVisitor(ProjectionASTConstVisitor* visitor) const override { visitor->visit(this); } @@ -204,7 +221,11 @@ public: addChildToInternalVector(std::move(child)); } - void acceptVisitor(ProjectionASTVisitor* visitor) override { + void acceptVisitor(ProjectionASTMutableVisitor* visitor) override { + visitor->visit(this); + } + + void acceptVisitor(ProjectionASTConstVisitor* visitor) const override { visitor->visit(this); } @@ -228,7 +249,11 @@ public: _expr = clonedExpr; } - void acceptVisitor(ProjectionASTVisitor* visitor) override { + void acceptVisitor(ProjectionASTMutableVisitor* visitor) override { + visitor->visit(this); + } + + void acceptVisitor(ProjectionASTConstVisitor* visitor) const override { visitor->visit(this); } @@ -252,7 +277,11 @@ class BooleanConstantASTNode final : public ASTNode { public: BooleanConstantASTNode(bool val) : _val(val) {} - void acceptVisitor(ProjectionASTVisitor* visitor) override { + void acceptVisitor(ProjectionASTMutableVisitor* visitor) override { + visitor->visit(this); + } + + void acceptVisitor(ProjectionASTConstVisitor* visitor) const override { visitor->visit(this); } diff --git a/src/mongo/db/query/projection_ast_path_tracking_visitor.h b/src/mongo/db/query/projection_ast_path_tracking_visitor.h index 2108233ac8e..affc3732dd6 100644 --- a/src/mongo/db/query/projection_ast_path_tracking_visitor.h +++ b/src/mongo/db/query/projection_ast_path_tracking_visitor.h @@ -106,14 +106,14 @@ namespace { * This is intended to be used with the 'ProjectionPathTrackingWalker' only to correctly maintain * the state about the current path being visited. */ -template <class UserData = PathTrackingDummyDefaultType> -class PathTrackingPreVisitor final : public ProjectionASTVisitor { +template <class UserData = PathTrackingDummyDefaultType, bool IsConst = true> +class PathTrackingPreVisitor final : public ProjectionASTVisitor<IsConst> { public: PathTrackingPreVisitor(PathTrackingVisitorContext<UserData>* context) : _context{context} { invariant(_context); } - void visit(ProjectionPathASTNode* node) final { + void visit(MaybeConstPtr<IsConst, ProjectionPathASTNode> node) final { if (node->parent()) { _context->setBasePath(_context->fullPath()); _context->popFrontFieldName(); @@ -122,12 +122,12 @@ public: _context->pushFieldNames({node->fieldNames().begin(), node->fieldNames().end()}); } - void visit(MatchExpressionASTNode* node) final {} - void visit(ProjectionPositionalASTNode* node) final {} - void visit(ProjectionSliceASTNode* node) final {} - void visit(ProjectionElemMatchASTNode* node) final {} - void visit(ExpressionASTNode* node) final {} - void visit(BooleanConstantASTNode* node) final {} + void visit(MaybeConstPtr<IsConst, MatchExpressionASTNode> node) final {} + void visit(MaybeConstPtr<IsConst, ProjectionPositionalASTNode> node) final {} + void visit(MaybeConstPtr<IsConst, ProjectionSliceASTNode> node) final {} + void visit(MaybeConstPtr<IsConst, ProjectionElemMatchASTNode> node) final {} + void visit(MaybeConstPtr<IsConst, ExpressionASTNode> node) final {} + void visit(MaybeConstPtr<IsConst, BooleanConstantASTNode> node) final {} private: PathTrackingVisitorContext<UserData>* _context; @@ -139,14 +139,14 @@ private: * This is intended to be used with the 'PathTrackingWalker' only to correctly maintain the state * about the current path being visited. */ -template <class UserData = PathTrackingDummyDefaultType> -class PathTrackingPostVisitor final : public ProjectionASTVisitor { +template <class UserData = PathTrackingDummyDefaultType, bool IsConst = true> +class PathTrackingPostVisitor final : public ProjectionASTVisitor<IsConst> { public: PathTrackingPostVisitor(PathTrackingVisitorContext<UserData>* context) : _context{context} { invariant(_context); } - void visit(projection_ast::ProjectionPathASTNode* node) final { + void visit(MaybeConstPtr<IsConst, ProjectionPathASTNode> node) final { _context->popFieldNames(); if (_context->basePath()) { @@ -161,27 +161,27 @@ public: } } - void visit(ProjectionPositionalASTNode* node) final { + void visit(MaybeConstPtr<IsConst, ProjectionPositionalASTNode> node) final { _context->popFrontFieldName(); } - void visit(ProjectionSliceASTNode* node) final { + void visit(MaybeConstPtr<IsConst, ProjectionSliceASTNode> node) final { _context->popFrontFieldName(); } - void visit(ProjectionElemMatchASTNode* node) final { + void visit(MaybeConstPtr<IsConst, ProjectionElemMatchASTNode> node) final { _context->popFrontFieldName(); } - void visit(ExpressionASTNode* node) final { + void visit(MaybeConstPtr<IsConst, ExpressionASTNode> node) final { _context->popFrontFieldName(); } - void visit(BooleanConstantASTNode* node) final { + void visit(MaybeConstPtr<IsConst, BooleanConstantASTNode> node) final { _context->popFrontFieldName(); } - void visit(MatchExpressionASTNode* node) final {} + void visit(MaybeConstPtr<IsConst, MatchExpressionASTNode> node) final {} private: PathTrackingVisitorContext<UserData>* _context; @@ -197,12 +197,12 @@ private: * The visitors specified in the 'preVisitors' and 'postVisitors' parameters will be visited in * the same order as they were added to the vector. */ -template <class UserData = PathTrackingDummyDefaultType> +template <class UserData = PathTrackingDummyDefaultType, bool IsConst = true> class PathTrackingWalker final { public: PathTrackingWalker(PathTrackingVisitorContext<UserData>* context, - std::vector<ProjectionASTVisitor*> preVisitors, - std::vector<ProjectionASTVisitor*> postVisitors) + std::vector<ProjectionASTVisitor<IsConst>*> preVisitors, + std::vector<ProjectionASTVisitor<IsConst>*> postVisitors) : _pathTrackingPreVisitor{context}, _pathTrackingPostVisitor{context}, _preVisitors{std::move(preVisitors)}, @@ -211,25 +211,25 @@ public: _postVisitors.push_back(&_pathTrackingPostVisitor); } - void preVisit(projection_ast::ASTNode* node) { + void preVisit(MaybeConstPtr<IsConst, projection_ast::ASTNode> node) { for (auto visitor : _preVisitors) { node->acceptVisitor(visitor); } } - void postVisit(projection_ast::ASTNode* node) { + void postVisit(MaybeConstPtr<IsConst, projection_ast::ASTNode> node) { for (auto visitor : _postVisitors) { node->acceptVisitor(visitor); } } - void inVisit(long count, projection_ast::ASTNode* node) {} + void inVisit(long count, MaybeConstPtr<IsConst, ASTNode> node) {} private: - PathTrackingPreVisitor<UserData> _pathTrackingPreVisitor; - PathTrackingPostVisitor<UserData> _pathTrackingPostVisitor; - std::vector<ProjectionASTVisitor*> _preVisitors; - std::vector<ProjectionASTVisitor*> _postVisitors; + PathTrackingPreVisitor<UserData, IsConst> _pathTrackingPreVisitor; + PathTrackingPostVisitor<UserData, IsConst> _pathTrackingPostVisitor; + std::vector<ProjectionASTVisitor<IsConst>*> _preVisitors; + std::vector<ProjectionASTVisitor<IsConst>*> _postVisitors; }; } // namespace projection_ast } // namespace mongo diff --git a/src/mongo/db/query/projection_ast_util.cpp b/src/mongo/db/query/projection_ast_util.cpp index f2b4c2aa1ee..1d31ddef614 100644 --- a/src/mongo/db/query/projection_ast_util.cpp +++ b/src/mongo/db/query/projection_ast_util.cpp @@ -44,16 +44,17 @@ struct BSONVisitorContext { } }; -class BSONPreVisitor : public ProjectionASTVisitor { +class BSONPreVisitor : public ProjectionASTConstVisitor { public: BSONPreVisitor(BSONVisitorContext* context) : _context(context) {} - virtual void visit(MatchExpressionASTNode* node) { + virtual void visit(const MatchExpressionASTNode* node) { static_cast<const MatchExpressionASTNode*>(node)->matchExpression()->serialize( &_context->builder()); _context->fieldNames.top().pop_front(); } - virtual void visit(ProjectionPathASTNode* node) { + + virtual void visit(const ProjectionPathASTNode* node) { if (!node->parent()) { // No root of the tree, thus this node has no field name. _context->builders.push(BSONObjBuilder()); @@ -65,7 +66,8 @@ public: _context->fieldNames.push( std::list<std::string>(node->fieldNames().begin(), node->fieldNames().end())); } - virtual void visit(ProjectionPositionalASTNode* node) { + + virtual void visit(const ProjectionPositionalASTNode* node) { // ProjectionPositional always has the original query's match expression node as its // child. Serialize as: {"positional.projection.field.$": <original match expression>}. _context->builders.push(_context->builder().subobjStart(getFieldName() + ".$")); @@ -74,7 +76,8 @@ public: // been put on the stack (just like every other node), and will pop it. _context->fieldNames.push({"<dummy>"}); } - virtual void visit(ProjectionSliceASTNode* node) { + + virtual void visit(const ProjectionSliceASTNode* node) { BSONObjBuilder sub(_context->builder().subobjStart(getFieldName())); if (node->skip()) { sub.appendArray("$slice", BSON_ARRAY(*node->skip() << node->limit())); @@ -82,13 +85,16 @@ public: sub.appendNumber("$slice", node->limit()); } } - virtual void visit(ProjectionElemMatchASTNode* node) { + + virtual void visit(const ProjectionElemMatchASTNode* node) { // Defer to the child, match expression node. } - virtual void visit(ExpressionASTNode* node) { + + virtual void visit(const ExpressionASTNode* node) { node->expression()->serialize(false).addToBsonObj(&_context->builder(), getFieldName()); } - virtual void visit(BooleanConstantASTNode* node) { + + virtual void visit(const BooleanConstantASTNode* node) { _context->builders.top().append(getFieldName(), node->value()); } @@ -104,12 +110,11 @@ private: BSONVisitorContext* _context; }; -class BSONPostVisitor : public ProjectionASTVisitor { +class BSONPostVisitor : public ProjectionASTConstVisitor { public: BSONPostVisitor(BSONVisitorContext* context) : _context(context) {} - virtual void visit(MatchExpressionASTNode* node) {} - virtual void visit(ProjectionPathASTNode* node) { + virtual void visit(const ProjectionPathASTNode* node) { // Don't pop the top builder. if (node->parent()) { // Pop the BSONObjBuilder that was added in the pre visitor. @@ -120,15 +125,17 @@ public: invariant(_context->fieldNames.top().empty()); _context->fieldNames.pop(); } - virtual void visit(ProjectionPositionalASTNode* node) { + + virtual void visit(const ProjectionPositionalASTNode* node) { _context->builders.pop(); _context->fieldNames.pop(); } - virtual void visit(ProjectionSliceASTNode* node) {} - virtual void visit(ProjectionElemMatchASTNode* node) {} - virtual void visit(ExpressionASTNode* node) {} - virtual void visit(BooleanConstantASTNode* node) {} + virtual void visit(const MatchExpressionASTNode* node) {} + virtual void visit(const ProjectionSliceASTNode* node) {} + virtual void visit(const ProjectionElemMatchASTNode* node) {} + virtual void visit(const ExpressionASTNode* node) {} + virtual void visit(const BooleanConstantASTNode* node) {} private: BSONVisitorContext* _context; @@ -138,15 +145,15 @@ class BSONWalker { public: BSONWalker() : _preVisitor(&_context), _postVisitor(&_context) {} - void preVisit(ASTNode* node) { + void preVisit(const ASTNode* node) { node->acceptVisitor(&_preVisitor); } - void postVisit(ASTNode* node) { + void postVisit(const ASTNode* node) { node->acceptVisitor(&_postVisitor); } - void inVisit(long count, ASTNode* node) { + void inVisit(long count, const ASTNode* node) { // No op. } @@ -165,7 +172,7 @@ private: }; } // namespace -BSONObj astToDebugBSON(ASTNode* root) { +BSONObj astToDebugBSON(const ASTNode* root) { BSONWalker walker; projection_ast_walker::walk(&walker, root); diff --git a/src/mongo/db/query/projection_ast_util.h b/src/mongo/db/query/projection_ast_util.h index 231f037b48c..af89254a9a1 100644 --- a/src/mongo/db/query/projection_ast_util.h +++ b/src/mongo/db/query/projection_ast_util.h @@ -36,6 +36,6 @@ namespace projection_ast { /** * This is intended to be used for debug output, not for serialization. */ -BSONObj astToDebugBSON(ASTNode* root); +BSONObj astToDebugBSON(const ASTNode* root); } // namespace projection_ast } // namespace mongo diff --git a/src/mongo/db/query/projection_ast_visitor.h b/src/mongo/db/query/projection_ast_visitor.h index 39caedac3d8..81cc07bf932 100644 --- a/src/mongo/db/query/projection_ast_visitor.h +++ b/src/mongo/db/query/projection_ast_visitor.h @@ -31,7 +31,6 @@ namespace mongo { namespace projection_ast { - class MatchExpressionASTNode; class ProjectionPathASTNode; class ProjectionPositionalASTNode; @@ -41,20 +40,34 @@ class ExpressionASTNode; class BooleanConstantASTNode; /** + * A template type which resolves to 'const T*' if 'IsConst' argument is 'true', and to 'T*' + * otherwise. + */ +template <bool IsConst, typename T> +using MaybeConstPtr = typename std::conditional<IsConst, const T*, T*>::type; + +/** * Visitor pattern for ProjectionAST. * * This code is not responsible for traversing the AST, only for performing the double-dispatch. + * + * If the visitor doesn't intend to modify the AST, then the template argument 'IsConst' should be + * set to 'true'. In this case all 'visit()' methods will take a const pointer to a visiting node. */ +template <bool IsConst = false> class ProjectionASTVisitor { public: virtual ~ProjectionASTVisitor() = default; - virtual void visit(MatchExpressionASTNode* node) = 0; - virtual void visit(ProjectionPathASTNode* node) = 0; - virtual void visit(ProjectionPositionalASTNode* node) = 0; - virtual void visit(ProjectionSliceASTNode* node) = 0; - virtual void visit(ProjectionElemMatchASTNode* node) = 0; - virtual void visit(ExpressionASTNode* node) = 0; - virtual void visit(BooleanConstantASTNode* node) = 0; + virtual void visit(MaybeConstPtr<IsConst, MatchExpressionASTNode> node) = 0; + virtual void visit(MaybeConstPtr<IsConst, ProjectionPathASTNode> node) = 0; + virtual void visit(MaybeConstPtr<IsConst, ProjectionPositionalASTNode> node) = 0; + virtual void visit(MaybeConstPtr<IsConst, ProjectionSliceASTNode> node) = 0; + virtual void visit(MaybeConstPtr<IsConst, ProjectionElemMatchASTNode> node) = 0; + virtual void visit(MaybeConstPtr<IsConst, ExpressionASTNode> node) = 0; + virtual void visit(MaybeConstPtr<IsConst, BooleanConstantASTNode> node) = 0; }; + +using ProjectionASTMutableVisitor = ProjectionASTVisitor<false>; +using ProjectionASTConstVisitor = ProjectionASTVisitor<true>; } // namespace projection_ast } // namespace mongo diff --git a/src/mongo/db/query/projection_ast_walker.h b/src/mongo/db/query/projection_ast_walker.h index 5d697efee7a..64674dbd3d0 100644 --- a/src/mongo/db/query/projection_ast_walker.h +++ b/src/mongo/db/query/projection_ast_walker.h @@ -44,9 +44,12 @@ namespace mongo::projection_ast_walker { * * walker.postVisit() once after walking to each child. * Each of the ASTNode's child ASTNode is recursively walked and the same three methods are * called for it. + * + * If the caller doesn't intend to modify the AST, then the template argument 'IsConst' should be + * set to 'true'. In this case the 'node' pointer will be qualified with 'const'. */ -template <typename Walker> -void walk(Walker* walker, projection_ast::ASTNode* node) { +template <typename Walker, bool IsConst = true> +void walk(Walker* walker, projection_ast::MaybeConstPtr<IsConst, projection_ast::ASTNode> node) { if (node) { walker->preVisit(node); @@ -55,7 +58,7 @@ void walk(Walker* walker, projection_ast::ASTNode* node) { if (count) walker->inVisit(count, node); ++count; - walk(walker, child.get()); + walk<Walker, IsConst>(walker, child.get()); } walker->postVisit(node); diff --git a/src/mongo/db/query/projection_parser.cpp b/src/mongo/db/query/projection_parser.cpp index a64d2e7dbd8..9642b2456d2 100644 --- a/src/mongo/db/query/projection_parser.cpp +++ b/src/mongo/db/query/projection_parser.cpp @@ -347,7 +347,7 @@ Projection parse(boost::intrusive_ptr<ExpressionContext> expCtx, addNodeAtPath(&root, "_id", std::make_unique<BooleanConstantASTNode>(true)); } - return Projection{std::move(root), *type, obj}; + return Projection{std::move(root), *type}; } Projection parse(boost::intrusive_ptr<ExpressionContext> expCtx, diff --git a/src/mongo/db/query/projection_test.cpp b/src/mongo/db/query/projection_test.cpp index c0044173cd4..c4180584f2f 100644 --- a/src/mongo/db/query/projection_test.cpp +++ b/src/mongo/db/query/projection_test.cpp @@ -229,9 +229,7 @@ TEST(QueryProjectionTest, SortKeyMetaProjectionInExclusionProjection) { // A projection with just a $meta projection defaults to an exclusion projection. auto proj = createProjection("{}", "{foo: {$meta: 'sortKey'}}"); - ASSERT_BSONOBJ_EQ(proj.getProjObj(), fromjson("{foo: {$meta: 'sortKey'}}")); ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]); - ASSERT_FALSE(proj.requiresMatchDetails()); ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]); ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearPoint]); @@ -241,10 +239,7 @@ TEST(QueryProjectionTest, SortKeyMetaProjectionInExclusionProjection) { TEST(QueryProjectionTest, SortKeyMetaProjectionInExclusionProjectionWithOtherFields) { auto proj = createProjection("{}", "{a: 0, foo: {$meta: 'sortKey'}}"); - ASSERT_BSONOBJ_EQ(proj.getProjObj(), fromjson("{a: 0, foo: {$meta: 'sortKey'}}")); ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]); - - ASSERT_FALSE(proj.requiresMatchDetails()); ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]); ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearPoint]); ASSERT_TRUE(proj.requiresDocument()); @@ -253,9 +248,7 @@ TEST(QueryProjectionTest, SortKeyMetaProjectionInExclusionProjectionWithOtherFie TEST(QueryProjectionTest, SortKeyMetaProjectionInInclusionProjection) { auto proj = createProjection("{}", "{a: 1, foo: {$meta: 'sortKey'}}"); - ASSERT_BSONOBJ_EQ(proj.getProjObj(), fromjson("{a: 1, foo: {$meta: 'sortKey'}}")); ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]); - ASSERT_FALSE(proj.requiresMatchDetails()); ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]); ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearPoint]); @@ -265,9 +258,7 @@ TEST(QueryProjectionTest, SortKeyMetaProjectionInInclusionProjection) { TEST(QueryProjectionTest, SortKeyMetaProjectionDoesNotRequireDocument) { auto proj = createProjection("{}", "{a: 1, foo: {$meta: 'sortKey'}, _id: 0}"); - ASSERT_BSONOBJ_EQ(proj.getProjObj(), fromjson("{a: 1, foo: {$meta: 'sortKey'}, _id: 0}")); ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]); - ASSERT_FALSE(proj.requiresDocument()); ASSERT_FALSE(proj.requiresMatchDetails()); ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]); @@ -277,11 +268,8 @@ TEST(QueryProjectionTest, SortKeyMetaProjectionDoesNotRequireDocument) { TEST(QueryProjectionTest, SortKeyMetaAndSlice) { auto proj = createProjection("{}", "{a: 1, foo: {$meta: 'sortKey'}, _id: 0, b: {$slice: 1}}"); - ASSERT_BSONOBJ_EQ(proj.getProjObj(), - fromjson("{a: 1, foo: {$meta: 'sortKey'}, _id: 0, b: {$slice: 1}}")); ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]); ASSERT_TRUE(proj.requiresDocument()); - ASSERT_FALSE(proj.requiresMatchDetails()); ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]); ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearPoint]); @@ -291,11 +279,8 @@ TEST(QueryProjectionTest, SortKeyMetaAndElemMatch) { auto proj = createProjection("{}", "{a: 1, foo: {$meta: 'sortKey'}, _id: 0, b: {$elemMatch: {a: 1}}}"); - ASSERT_BSONOBJ_EQ(proj.getProjObj(), - fromjson("{a: 1, foo: {$meta: 'sortKey'}, _id: 0, b: {$elemMatch: {a: 1}}}")); ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]); ASSERT_TRUE(proj.requiresDocument()); - ASSERT_FALSE(proj.requiresMatchDetails()); ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]); ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearPoint]); diff --git a/src/mongo/db/query/query_planner_test_lib.cpp b/src/mongo/db/query/query_planner_test_lib.cpp index 23287d59225..a08283aa6de 100644 --- a/src/mongo/db/query/query_planner_test_lib.cpp +++ b/src/mongo/db/query/query_planner_test_lib.cpp @@ -43,6 +43,8 @@ #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/pipeline/expression_context_for_test.h" #include "mongo/db/query/collation/collator_factory_mock.h" +#include "mongo/db/query/projection_ast_util.h" +#include "mongo/db/query/projection_parser.h" #include "mongo/db/query/query_planner.h" #include "mongo/db/query/query_solution.h" #include "mongo/unittest/unittest.h" @@ -617,7 +619,13 @@ bool QueryPlannerTestLib::solutionMatches(const BSONObj& testSoln, return false; } - return SimpleBSONObjComparator::kInstance.evaluate(spec.Obj() == pn->proj.getProjObj()) && + // Create an empty/dummy expression context without access to the operation context and + // collator. This should be sufficient to parse a projection. + auto expCtx = make_intrusive<ExpressionContext>(nullptr, nullptr); + auto projection = projection_ast::parse(expCtx, spec.Obj(), {}); + auto specProjObj = projection_ast::astToDebugBSON(projection.root()); + auto solnProjObj = projection_ast::astToDebugBSON(pn->proj.root()); + return SimpleBSONObjComparator::kInstance.evaluate(specProjObj == solnProjObj) && solutionMatches(child.Obj(), pn->children[0], relaxBoundsCheck); } else if (STAGE_SORT == trueSoln->getType()) { const SortNode* sn = static_cast<const SortNode*>(trueSoln); diff --git a/src/mongo/db/query/query_solution.cpp b/src/mongo/db/query/query_solution.cpp index e09c8c82bd0..245621eb388 100644 --- a/src/mongo/db/query/query_solution.cpp +++ b/src/mongo/db/query/query_solution.cpp @@ -42,6 +42,7 @@ #include "mongo/db/query/index_bounds_builder.h" #include "mongo/db/query/planner_analysis.h" #include "mongo/db/query/planner_wildcard_helpers.h" +#include "mongo/db/query/projection_ast_util.h" #include "mongo/db/query/query_planner_common.h" namespace mongo { @@ -878,7 +879,7 @@ void ProjectionNode::appendToString(str::stream* ss, int indent) const { addIndent(ss, indent); *ss << "PROJ\n"; addIndent(ss, indent + 1); - *ss << "proj = " << proj.getProjObj().toString() << '\n'; + *ss << "proj = " << projection_ast::astToDebugBSON(proj.root()).toString() << '\n'; addIndent(ss, indent + 1); *ss << "type = " << projectionImplementationTypeToString() << '\n'; addCommon(ss, indent); diff --git a/src/mongo/db/query/stage_builder.cpp b/src/mongo/db/query/stage_builder.cpp index b70ca49a5a3..a3cc6aee999 100644 --- a/src/mongo/db/query/stage_builder.cpp +++ b/src/mongo/db/query/stage_builder.cpp @@ -145,24 +145,26 @@ std::unique_ptr<PlanStage> buildStages(OperationContext* opCtx, case STAGE_PROJECTION_DEFAULT: { auto pn = static_cast<const ProjectionNodeDefault*>(root); auto childStage = buildStages(opCtx, collection, cq, qsol, pn->children[0], ws); - return std::make_unique<ProjectionStageDefault>(opCtx, - pn->proj.getProjObj(), + return std::make_unique<ProjectionStageDefault>(cq.getExpCtx(), + cq.getQueryRequest().getProj(), + cq.getProj(), ws, - std::move(childStage), - pn->fullExpression, - cq.getCollator()); + std::move(childStage)); } case STAGE_PROJECTION_COVERED: { auto pn = static_cast<const ProjectionNodeCovered*>(root); auto childStage = buildStages(opCtx, collection, cq, qsol, pn->children[0], ws); - return std::make_unique<ProjectionStageCovered>( - opCtx, pn->proj.getProjObj(), ws, std::move(childStage), pn->coveredKeyObj); + return std::make_unique<ProjectionStageCovered>(opCtx, + cq.getQueryRequest().getProj(), + ws, + std::move(childStage), + pn->coveredKeyObj); } case STAGE_PROJECTION_SIMPLE: { auto pn = static_cast<const ProjectionNodeSimple*>(root); auto childStage = buildStages(opCtx, collection, cq, qsol, pn->children[0], ws); return std::make_unique<ProjectionStageSimple>( - opCtx, pn->proj.getProjObj(), ws, std::move(childStage)); + opCtx, cq.getQueryRequest().getProj(), ws, std::move(childStage)); } case STAGE_LIMIT: { const LimitNode* ln = static_cast<const LimitNode*>(root); |