1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
/**
* Tests that a $not-$in-$regex query, which cannot be supported by an index, cannot incorrectly
* hijack the cached plan for an earlier $not-$in query.
*/
(function() {
"use strict";
load('jstests/libs/analyze_plan.js'); // For isCollScan and getPlanCacheKeyFromShape.
const coll = db.plan_cache_not_in_regex;
coll.drop();
// Helper function which obtains the cached plan, if any, for a given query shape.
function getPlanForCacheEntry(query, proj, sort) {
const keyHash = getPlanCacheKeyFromShape(
{query: query, projection: proj, sort: sort, collection: coll, db: db});
const cursor = coll.aggregate([{$planCacheStats: {}}, {$match: {planCacheKey: keyHash}}]);
const entryStats = cursor.toArray();
assert.eq(entryStats.length, 1, `Expected one cached plan, found: ${tojson(entryStats)}`);
return entryStats.shift();
}
// Insert a document containing a field 'a', and create two indexes that can support queries on
// this field. This is to ensure that the plan we choose will be cached, since if only a single
// index is available, the solution will not be cached.
assert.commandWorked(coll.insert({a: "foo"}));
assert.commandWorked(coll.createIndex({a: 1}));
assert.commandWorked(coll.createIndex({a: 1, b: 1}));
// Repeat the test for query, query with projection, and query with projection and sort.
for (let [proj, sort] of [[{}, {}], [{_id: 0, a: 1}, {}], [{_id: 0, a: 1}, {a: 1}]]) {
// Perform a plain $not-$in query on 'a' and confirm that the plan is cached.
const queryShape = {a: {$not: {$in: [32, 33]}}};
assert.eq(1, coll.find(queryShape, proj).sort(sort).itcount());
let cacheEntry = getPlanForCacheEntry(queryShape, proj, sort);
assert(cacheEntry);
// If the cached plan is inactive, perform the same query to activate it.
if (cacheEntry.isActive === false) {
assert.eq(1, coll.find(queryShape, proj).sort(sort).itcount());
cacheEntry = getPlanForCacheEntry(queryShape, proj, sort);
assert(cacheEntry);
assert(cacheEntry.isActive);
}
// Now perform a $not-$in-$regex query, confirm that it obtains the correct results, and
// that it used a COLLSCAN rather than planning from the cache.
const explainOutput =
assert.commandWorked(coll.find({a: {$not: {$in: [34, /bar/]}}}).explain("executionStats"));
assert(isCollscan(coll.getDB(), getWinningPlan(explainOutput.queryPlanner)));
assert.eq(1, explainOutput.executionStats.nReturned);
// Flush the plan cache before the next iteration.
coll.getPlanCache().clear();
}
})();
|