summaryrefslogtreecommitdiff
path: root/jstests/core/index_filter_catalog_independent.js
blob: f3ea81a662749da2f83d13e3ac8ef27b93a2ac61 (plain)
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/**
 * Test that index filters are applied regardless of catalog changes. Intended to reproduce
 * SERVER-33303.
 *
 * @tags: [
 *   # This test performs queries with index filters set up. Since index filters are local to a
 *   # mongod, and do not replicate, this test must issue all of its commands against the same
 *   # node.
 *   assumes_read_preference_unchanged,
 *   does_not_support_stepdowns,
 * ]
 */
(function() {
    "use strict";

    load("jstests/libs/analyze_plan.js");  // For getPlanStages.

    const collName = "index_filter_catalog_independent";
    const coll = db[collName];
    coll.drop();

    /*
     * Check that there's one index filter on the given query which allows only 'indexes'.
     */
    function assertOneIndexFilter(query, indexes) {
        let res = assert.commandWorked(db.runCommand({planCacheListFilters: collName}));
        assert.eq(res.filters.length, 1);
        assert.eq(res.filters[0].query, query);
        assert.eq(res.filters[0].indexes, indexes);
    }

    function assertIsIxScanOnIndex(winningPlan, keyPattern) {
        const ixScans = getPlanStages(winningPlan, "IXSCAN");
        assert.gt(ixScans.length, 0);
        ixScans.every((ixScan) => assert.eq(ixScan.keyPattern, keyPattern));

        const collScans = getPlanStages(winningPlan, "COLLSCAN");
        assert.eq(collScans.length, 0);
    }

    function checkIndexFilterSet(explain, shouldBeSet) {
        if (explain.queryPlanner.winningPlan.shards) {
            for (let shard of explain.queryPlanner.winningPlan.shards) {
                assert.eq(shard.indexFilterSet, shouldBeSet);
            }
        } else {
            assert.eq(explain.queryPlanner.indexFilterSet, shouldBeSet);
        }
    }

    assert.commandWorked(coll.createIndexes([{x: 1}, {x: 1, y: 1}]));
    assert.commandWorked(
        db.runCommand({planCacheSetFilter: collName, query: {"x": 3}, indexes: [{x: 1, y: 1}]}));
    assertOneIndexFilter({x: 3}, [{x: 1, y: 1}]);

    let explain = assert.commandWorked(coll.find({x: 3}).explain());
    checkIndexFilterSet(explain, true);
    assertIsIxScanOnIndex(explain.queryPlanner.winningPlan, {x: 1, y: 1});

    // Drop an index. The filter should not change.
    assert.commandWorked(coll.dropIndex({x: 1, y: 1}));
    assertOneIndexFilter({x: 3}, [{x: 1, y: 1}]);

    // The {x: 1} index _could_ be used, but should not be considered because of the filter.
    // Since we dropped the {x: 1, y: 1} index, a COLLSCAN must be used.
    explain = coll.find({x: 3}).explain();
    checkIndexFilterSet(explain, true);
    assert(isCollscan(db, explain.queryPlanner.winningPlan));

    // Create another index. This should not change whether the index filter is applied.
    assert.commandWorked(coll.createIndex({x: 1, z: 1}));
    explain = assert.commandWorked(coll.find({x: 3}).explain());
    checkIndexFilterSet(explain, true);
    assert(isCollscan(db, explain.queryPlanner.winningPlan));

    // Changing the catalog and then setting an index filter should not result in duplicate entries.
    assert.commandWorked(coll.createIndex({x: 1, a: 1}));
    assert.commandWorked(
        db.runCommand({planCacheSetFilter: collName, query: {"x": 3}, indexes: [{x: 1, y: 1}]}));
    assertOneIndexFilter({x: 3}, [{x: 1, y: 1}]);

    // Recreate the {x: 1, y: 1} index and be sure that it's still used.
    assert.commandWorked(coll.createIndexes([{x: 1}, {x: 1, y: 1}]));
    assertOneIndexFilter({x: 3}, [{x: 1, y: 1}]);

    explain = assert.commandWorked(coll.find({x: 3}).explain());
    checkIndexFilterSet(explain, true);
    assertIsIxScanOnIndex(explain.queryPlanner.winningPlan, {x: 1, y: 1});
})();