summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/optimizer/utils/path_utils.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/query/optimizer/utils/path_utils.h')
-rw-r--r--src/mongo/db/query/optimizer/utils/path_utils.h174
1 files changed, 174 insertions, 0 deletions
diff --git a/src/mongo/db/query/optimizer/utils/path_utils.h b/src/mongo/db/query/optimizer/utils/path_utils.h
new file mode 100644
index 00000000000..c5e33bf6e39
--- /dev/null
+++ b/src/mongo/db/query/optimizer/utils/path_utils.h
@@ -0,0 +1,174 @@
+/**
+ * Copyright (C) 2023-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/db/query/optimizer/defs.h"
+#include "mongo/db/query/optimizer/node.h"
+
+
+namespace mongo::optimizer {
+
+/**
+ * Returns a vector all paths nested under conjunctions (PathComposeM) in the given path.
+ * For example, PathComposeM(PathComposeM(Foo, Bar), Baz) returns [Foo, Bar, Baz].
+ * If the given path is not a conjunction, returns a vector with the given path.
+ */
+std::vector<ABT::reference_type> collectComposed(const ABT& n);
+
+/**
+ * Like collectComposed() but bounded by a maximum number of composed paths.
+ * If the given path has more PathComposeM;s than specified by maxDepth, then return a vector
+ * with the given path. Otherwise, returns the result of collectComposed().
+ *
+ * This is useful for preventing the optimizer from unintentionally creating a very deep tree which
+ * causes stack-overflow on a recursive traversal.
+ */
+std::vector<ABT::reference_type> collectComposedBounded(const ABT& n, size_t maxDepth);
+
+/**
+ * De-compose a path and an input to an EvalFilter into sequence of Filter nodes. If we have a path
+ * with a prefix of PathGet's followed by a series of nested PathComposeM, then split into two or
+ * more filter nodes at the composition and retain the prefix for each. The result is a tree of
+ * chained filter nodes. We return an empty result if we have less than "minDepth" sub-tress which
+ * are composed. If "minDepth" = 1, then we are guaranteed to return a result, which will consist of
+ * a single Filter node.
+ *
+ * If the number of compositions exceeds "maxDepth" then we return the a single FilterNode
+ * consisting of an EvalFilter over the original path and input.
+ *
+ * TODO: SERVER-73744. Consolidate usages in a new optimizer phase.
+ */
+constexpr size_t kMaxPathConjunctionDecomposition = 20;
+boost::optional<ABT> decomposeToFilterNodes(const ABT& input,
+ const ABT& path,
+ const ABT& pathInput,
+ size_t minDepth,
+ size_t maxDepth = kMaxPathConjunctionDecomposition);
+
+/**
+ * Returns true if the path represented by 'node' is of the form PathGet "field" PathId
+ */
+bool isSimplePath(const ABT& node);
+
+template <class Element = PathComposeM>
+inline void maybeComposePath(ABT& composition, ABT child) {
+ if (child.is<PathIdentity>()) {
+ return;
+ }
+ if (composition.is<PathIdentity>()) {
+ composition = std::move(child);
+ return;
+ }
+
+ composition = make<Element>(std::move(composition), std::move(child));
+}
+
+/**
+ * Creates a balanced tree of composition elements over the input vector which it modifies in place.
+ * In the end at most one element remains in the vector.
+ */
+template <class Element = PathComposeM>
+inline void maybeComposePaths(ABTVector& paths) {
+ while (paths.size() > 1) {
+ const size_t half = paths.size() / 2;
+ for (size_t i = 0; i < half; i++) {
+ maybeComposePath<Element>(paths.at(i), std::move(paths.back()));
+ paths.pop_back();
+ }
+ }
+}
+
+/**
+ * Appends a path to another path. Performs the append at PathIdentity elements.
+ */
+class PathAppender {
+public:
+ PathAppender(ABT suffix) : _suffix(std::move(suffix)) {}
+
+ void transport(ABT& n, const PathIdentity& node) {
+ n = _suffix;
+ }
+
+ template <typename T, typename... Ts>
+ void transport(ABT& /*n*/, const T& /*node*/, Ts&&...) {
+ // noop
+ }
+
+ /**
+ * Concatenate 'prefix' and 'suffix' by modifying 'prefix' in place.
+ */
+ static void appendInPlace(ABT& prefix, ABT suffix) {
+ PathAppender instance{std::move(suffix)};
+ algebra::transport<true>(prefix, instance);
+ }
+
+ /**
+ * Return the concatenation of 'prefix' and 'suffix'.
+ */
+ [[nodiscard]] static ABT append(ABT prefix, ABT suffix) {
+ appendInPlace(prefix, std::move(suffix));
+ return prefix;
+ }
+
+private:
+ ABT _suffix;
+};
+
+/**
+ * Given a path and a MultikeynessTrie describing the path's input,
+ * removes any Traverse nodes that we know will never encounter an array.
+ *
+ * Returns true if any changes were made to the ABT.
+ */
+bool simplifyTraverseNonArray(ABT& path, const MultikeynessTrie& multikeynessTrie);
+
+/**
+ * Fuses an index path and a query path to determine a residual path to apply over the index
+ * results. Checks if one index path is a prefix of another. Considers only Get, Traverse, and Id.
+ * Return the suffix that doesn't match.
+ */
+struct IndexFusionResult {
+ boost::optional<ABT::reference_type> _suffix;
+ size_t _numTraversesSkipped = 0;
+ size_t _numTraversesFused = 0;
+};
+IndexFusionResult fuseIndexPath(const ABT& node, const ABT& candidatePrefix);
+
+/**
+ * Check if a path contains a Traverse element.
+ */
+bool checkPathContainsTraverse(const ABT& path);
+
+/**
+ * This helper checks to see if we have a PathTraverse + PathId at the end of the path.
+ */
+bool pathEndsInTraverse(const optimizer::ABT& path);
+
+} // namespace mongo::optimizer