summaryrefslogtreecommitdiff
path: root/jstests/sharding/invalid_system_views_sharded_collection.js
blob: ae97b49d0ab06b5cd8c371e4d5010d022d6ada7c (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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
 * Tests that invalid view definitions in system.views do not impact valid commands on sharded
 * collections.
 */

(function() {
"use strict";

function runTest(st, badViewDefinition) {
    const mongos = st.s;
    const config = mongos.getDB("config");
    const db = mongos.getDB("invalid_system_views");
    assert.commandWorked(db.dropDatabase());

    assert.commandWorked(config.adminCommand({enableSharding: db.getName()}));
    st.ensurePrimaryShard(db.getName(), st.shard0.shardName);

    // Create sharded and unsharded collections, then insert an invalid view into system.views.
    const viewsCollection = db.getCollection("coll");
    const staticCollection = db.getCollection("staticCollection");
    assert.commandWorked(
        config.adminCommand({shardCollection: viewsCollection.getFullName(), key: {a: 1}}));
    assert.commandWorked(
        config.adminCommand({shardCollection: staticCollection.getFullName(), key: {a: 1}}));

    assert.commandWorked(viewsCollection.createIndex({x: 1}));

    const unshardedColl = db.getCollection("unshardedColl");
    assert.commandWorked(unshardedColl.insert({b: "boo"}));

    // applyOps is not available on mongos, so we use it to insert into the system.views collection
    // directly on each shard.
    [st.shard0, st.shard1].forEach(shard => {
        assert.commandWorked(shard.getDB(db.getName()).createCollection("system.views"));
        assert.commandWorked(
            shard.adminCommand(
                {applyOps: [{op: "i", ns: db.getName() + ".system.views", o: badViewDefinition}]}),
            "failed to insert " + tojson(badViewDefinition));
    });

    // Test that a command involving views properly fails with a views-specific error code.
    assert.commandFailedWithCode(
        db.runCommand({listCollections: 1}),
        ErrorCodes.InvalidViewDefinition,
        "listCollections should have failed in the presence of an invalid view");

    // Helper function to create a message to use if an assertion fails.
    function makeErrorMessage(msg) {
        return msg +
            " should work on a valid, existing collection, despite the presence of bad views" +
            " in system.views";
    }

    assert.commandWorked(viewsCollection.insert({y: "baz", a: 5}), makeErrorMessage("insert"));

    assert.commandWorked(viewsCollection.update({y: "baz"}, {$set: {y: "qux"}}),
                         makeErrorMessage("update"));

    assert.commandWorked(viewsCollection.remove({y: "baz"}), makeErrorMessage("remove"));

    assert.commandWorked(
        db.runCommand(
            {findAndModify: viewsCollection.getName(), query: {x: 1, a: 1}, update: {x: 2}}),
        makeErrorMessage("findAndModify with update"));

    assert.commandWorked(
        db.runCommand(
            {findAndModify: viewsCollection.getName(), query: {x: 2, a: 1}, remove: true}),
        makeErrorMessage("findAndModify with remove"));

    const lookup = {
        $lookup:
            {from: unshardedColl.getName(), localField: "_id", foreignField: "_id", as: "match"}
    };
    assert.commandWorked(
        db.runCommand({aggregate: viewsCollection.getName(), pipeline: [lookup], cursor: {}}),
        makeErrorMessage("aggregate with $lookup"));

    const graphLookup = {
            $graphLookup: {
                from: unshardedColl.getName(),
                startWith: "$_id",
                connectFromField: "_id",
                connectToField: "_id",
                as: "match"
            }
        };
    assert.commandWorked(
        db.runCommand({aggregate: viewsCollection.getName(), pipeline: [graphLookup], cursor: {}}),
        makeErrorMessage("aggregate with $graphLookup"));

    assert.commandWorked(db.runCommand({dropIndexes: viewsCollection.getName(), index: "x_1"}),
                         makeErrorMessage("dropIndexes"));

    assert.commandWorked(viewsCollection.createIndex({x: 1}), makeErrorMessage("createIndexes"));

    assert.commandWorked(
        db.runCommand({collMod: viewsCollection.getName(), validator: {x: {$type: "string"}}}),
        makeErrorMessage("collMod"));

    assert.commandWorked(db.runCommand({drop: viewsCollection.getName()}),
                         makeErrorMessage("drop"));
    assert.commandWorked(db.runCommand({drop: staticCollection.getName()}),
                         makeErrorMessage("drop"));

    // An invalid view in the view catalog should not interfere with attempting to run operations on
    // nonexistent collections.
    assert.commandWorked(db.runCommand({drop: staticCollection.getName()}),
                         makeErrorMessage("drop"));

    assert.commandWorked(db.runCommand({drop: unshardedColl.getName()}), makeErrorMessage("drop"));

    // Drop the offending view so that the validate hook succeeds.
    assert(db.system.views.drop());
}

const st = new ShardingTest({name: "views_sharded", shards: 2, other: {enableBalancer: false}});

runTest(st, {_id: "invalid_system_views.badViewStringPipeline", viewOn: "coll", pipeline: "bad"});
runTest(st, {_id: "invalid_system_views.badViewEmptyObjectPipeline", viewOn: "coll", pipeline: {}});
runTest(st, {_id: "invalid_system_views.badViewNumericalPipeline", viewOn: "coll", pipeline: 7});
runTest(
    st,
    {_id: "invalid_system_views.badViewArrayWithIntegerPipeline", viewOn: "coll", pipeline: [1]});
runTest(st, {
    _id: "invalid_system_views.badViewArrayWithEmptyArrayPipeline",
    viewOn: "coll",
    pipeline: [[]]
});
runTest(st, {_id: 7, viewOn: "coll", pipeline: []});
runTest(st, {_id: "invalid_system_views.embedded\0null", viewOn: "coll", pipeline: []});
runTest(st, {_id: "invalidNotFullyQualifiedNs", viewOn: "coll", pipeline: []});
runTest(st, {_id: "invalid_system_views.missingViewOnField", pipeline: []});

st.stop();
}());