summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdityavardhan Agrawal <adi.agrawal@mongodb.com>2023-05-11 13:54:16 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-05-11 14:34:01 +0000
commitbb7468fd0c2f7f2e3bdd4561d2c906f9ebb007ab (patch)
treee722d195cdbf05cd41131c869555d9548b2e10b5
parentdc864de3212e83b2d2a7553b6d9a975c523305e6 (diff)
downloadmongo-bb7468fd0c2f7f2e3bdd4561d2c906f9ebb007ab.tar.gz
SERVER-76944: Correctly return bool in the prepSubNodes function in plan_enumerator.cpp
-rw-r--r--jstests/noPassthrough/no_query_plan_unindexed_child_with_text.js54
-rw-r--r--src/mongo/db/query/plan_enumerator.cpp9
-rw-r--r--src/mongo/db/query/plan_enumerator.h3
3 files changed, 62 insertions, 4 deletions
diff --git a/jstests/noPassthrough/no_query_plan_unindexed_child_with_text.js b/jstests/noPassthrough/no_query_plan_unindexed_child_with_text.js
new file mode 100644
index 00000000000..4e298a3a9a0
--- /dev/null
+++ b/jstests/noPassthrough/no_query_plan_unindexed_child_with_text.js
@@ -0,0 +1,54 @@
+/**
+ * Tests that query planning fails when an $or has a text child along with an unindexed child.
+ *
+ * @tags: [
+ * requires_fcv_71,
+ * ]
+ */
+(function() {
+"use strict";
+
+const conn = MongoRunner.runMongod();
+assert.neq(null, conn, "mongod was unable to start up");
+
+const db = conn.getDB("test");
+const coll = db.getCollection(jsTestName());
+coll.drop();
+
+assert.commandWorked(coll.insert({x: 1}));
+
+assert.commandWorked(coll.createIndex({"$**": "text"}));
+
+assert.commandWorked(coll.createIndex({"indexed": 1}));
+
+const pipeline = [
+ {
+ $match: {
+ $and: [{
+ $and: [
+ {"indexed": {$eq: 1}},
+ {
+ $or: [
+ {$text: {$search: "abcd"}},
+ {"unindexed": {$eq: 1}},
+ ]
+ },
+ ]
+ }]
+ }
+ },
+];
+
+assert.throwsWithCode(function() {
+ coll.aggregate(pipeline);
+}, ErrorCodes.NoQueryExecutionPlans);
+
+assert.commandWorked(
+ db.adminCommand({configureFailPoint: "disableMatchExpressionOptimization", mode: "alwaysOn"}));
+
+assert.throwsWithCode(function() {
+ coll.aggregate(pipeline);
+}, ErrorCodes.NoQueryExecutionPlans);
+
+MongoRunner.stopMongod(conn);
+})();
diff --git a/src/mongo/db/query/plan_enumerator.cpp b/src/mongo/db/query/plan_enumerator.cpp
index 4db9ca8b693..ac79474cbb8 100644
--- a/src/mongo/db/query/plan_enumerator.cpp
+++ b/src/mongo/db/query/plan_enumerator.cpp
@@ -1331,7 +1331,6 @@ void PlanEnumerator::getIndexedPreds(MatchExpression* node,
}
bool PlanEnumerator::prepSubNodes(MatchExpression* node,
-
PrepMemoContext context,
vector<MemoID>* subnodesOut,
vector<MemoID>* mandatorySubnodes) {
@@ -1366,9 +1365,13 @@ bool PlanEnumerator::prepSubNodes(MatchExpression* node,
childContext.elemMatchExpr = child;
childContext.outsidePreds = context.outsidePreds;
markTraversedThroughElemMatchObj(&childContext);
- prepSubNodes(child, childContext, subnodesOut, mandatorySubnodes);
+ if (!prepSubNodes(child, childContext, subnodesOut, mandatorySubnodes)) {
+ return false;
+ }
} else if (MatchExpression::AND == child->matchType()) {
- prepSubNodes(child, context, subnodesOut, mandatorySubnodes);
+ if (!prepSubNodes(child, context, subnodesOut, mandatorySubnodes)) {
+ return false;
+ }
}
}
return true;
diff --git a/src/mongo/db/query/plan_enumerator.h b/src/mongo/db/query/plan_enumerator.h
index 9eabd5b09b9..db7206a608e 100644
--- a/src/mongo/db/query/plan_enumerator.h
+++ b/src/mongo/db/query/plan_enumerator.h
@@ -338,7 +338,8 @@ private:
* Recursively traverse 'node', with OR nodes as the base case. The OR nodes are not
* explored--instead we call prepMemo() on the OR subnode, and add its assignment to the output.
* Subnodes are "mandatory" if they *must* use an index (TEXT and GEO).
- * Returns a boolean indicating whether all mandatory subnodes can be indexed.
+ * Returns a boolean indicating whether all mandatory subnodes can be indexed and returns false
+ * to stop enumerating alternatives for an indexed OR.
*/
bool prepSubNodes(MatchExpression* node,
PrepMemoContext context,